gplugin/gplugin

Implement a plugin viewer in GTK4

2021-12-06, Elliott Sales de Andrade
ac9388f9dc4b
Parents bb3a9a7a52ba
Children 124b09203cba
Implement a plugin viewer in GTK4
--- a/gplugin-gtk4-viewer/data/window.ui Thu Nov 11 22:49:31 2021 -0600
+++ b/gplugin-gtk4-viewer/data/window.ui Mon Dec 06 22:37:08 2021 -0600
@@ -7,18 +7,7 @@
<property name="default-width">600</property>
<property name="default-height">400</property>
<child>
- <object class="GtkScrolledWindow">
- <property name="hscrollbar_policy">never</property>
- <child>
- <object class="GtkListBox">
- <child type="placeholder">
- <object class="GtkLabel">
- <property name="label" translatable="yes">No plugins found</property>
- </object>
- </child>
- </object>
- </child>
- </object>
+ <object class="GPluginGtkView" id="view"/>
</child>
</template>
</interface>
--- a/gplugin-gtk4-viewer/gplugin-gtk-viewer-window.c Thu Nov 11 22:49:31 2021 -0600
+++ b/gplugin-gtk4-viewer/gplugin-gtk-viewer-window.c Mon Dec 06 22:37:08 2021 -0600
@@ -15,10 +15,14 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <gplugin-gtk.h>
+
#include "gplugin-gtk-viewer-window.h"
struct _GPluginGtkViewerWindow {
GtkApplicationWindow parent;
+
+ GPluginGtkView *view;
};
G_DEFINE_TYPE(
@@ -39,6 +43,11 @@
widget_class,
"/org/imfreedom/keep/gplugin/gplugin/viewer/window.ui");
+ gtk_widget_class_bind_template_child(
+ widget_class,
+ GPluginGtkViewerWindow,
+ view);
+
gtk_widget_class_add_binding_action(
widget_class,
GDK_KEY_w,
@@ -52,3 +61,12 @@
{
gtk_widget_init_template(GTK_WIDGET(self));
}
+
+/******************************************************************************
+ * Public API
+ *****************************************************************************/
+GPluginGtkView *
+gplugin_gtk_viewer_window_get_view(GPluginGtkViewerWindow *window)
+{
+ return window->view;
+}
--- a/gplugin-gtk4-viewer/gplugin-gtk-viewer-window.h Thu Nov 11 22:49:31 2021 -0600
+++ b/gplugin-gtk4-viewer/gplugin-gtk-viewer-window.h Mon Dec 06 22:37:08 2021 -0600
@@ -20,6 +20,8 @@
#include <gtk/gtk.h>
+#include <gplugin-gtk.h>
+
G_BEGIN_DECLS
#define GPLUGIN_GTK_VIEWER_TYPE_WINDOW (gplugin_gtk_viewer_window_get_type())
@@ -30,6 +32,9 @@
WINDOW,
GtkApplicationWindow)
+GPluginGtkView *gplugin_gtk_viewer_window_get_view(
+ GPluginGtkViewerWindow *window);
+
G_END_DECLS
#endif /* GPLUGIN_GTK_VIEWER_WINDOW_H */
--- a/gplugin-gtk4-viewer/gplugin-gtk-viewer.c Thu Nov 11 22:49:31 2021 -0600
+++ b/gplugin-gtk4-viewer/gplugin-gtk-viewer.c Mon Dec 06 22:37:08 2021 -0600
@@ -79,12 +79,19 @@
window = gtk_application_get_active_window(GTK_APPLICATION(app));
if(window == NULL) {
+ GPluginGtkView *view = NULL;
+
/* clang-format off */
window = g_object_new(
GPLUGIN_GTK_VIEWER_TYPE_WINDOW,
"application", app,
NULL);
/* clang-format on */
+
+ view = gplugin_gtk_viewer_window_get_view(
+ GPLUGIN_GTK_VIEWER_WINDOW(window));
+ gplugin_gtk_view_set_manager(view, gplugin_manager_get_default());
+ gplugin_gtk_view_set_show_internal(view, show_internal);
}
gtk_window_present(window);
--- a/gplugin-gtk4/data/gplugin-gtk.gresource.xml Thu Nov 11 22:49:31 2021 -0600
+++ b/gplugin-gtk4/data/gplugin-gtk.gresource.xml Mon Dec 06 22:37:08 2021 -0600
@@ -1,5 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
- <gresource prefix="/org/imfreedom/keep/gplugin/gplugin-gtk4/">
+ <gresource prefix="/org/imfreedom/keep/gplugin/gplugin-gtk/">
+ <file compressed="true">plugin-row.ui</file>
+ <file compressed="true">view.ui</file>
</gresource>
</gresources>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/gplugin-gtk4/data/plugin-row.ui Mon Dec 06 22:37:08 2021 -0600
@@ -0,0 +1,461 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk" version="4.0"/>
+ <object class="GtkImage" id="image1">
+ <property name="can-focus">0</property>
+ <property name="margin-start">6</property>
+ <property name="margin-end">6</property>
+ <property name="margin-top">6</property>
+ <property name="margin-bottom">6</property>
+ <property name="icon-name">preferences-system-symbolic</property>
+ </object>
+ <template class="GPluginGtkPluginRow" parent="GtkListBoxRow">
+ <property name="child">
+ <object class="GtkBox">
+ <property name="can-focus">0</property>
+ <property name="margin-start">12</property>
+ <property name="margin-end">12</property>
+ <property name="margin-top">12</property>
+ <property name="margin-bottom">12</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkBox" id="header">
+ <property name="vexpand">1</property>
+ <property name="can-focus">0</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkSwitch" id="enable">
+ <property name="valign">center</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="title_box">
+ <property name="hexpand">1</property>
+ <property name="can-focus">0</property>
+ <property name="valign">center</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel" id="title">
+ <property name="accessible-role">row-header</property>
+ <property name="hexpand">1</property>
+ <property name="can-focus">0</property>
+ <property name="label">Plugin Name</property>
+ <property name="wrap">1</property>
+ <property name="wrap-mode">word-char</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <style>
+ <class name="heading"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="summary">
+ <property name="can-focus">0</property>
+ <property name="halign">start</property>
+ <property name="label">The plugin short summary</property>
+ <property name="wrap">1</property>
+ <property name="wrap-mode">word-char</property>
+ <property name="ellipsize">end</property>
+ <property name="xalign">0</property>
+ <style>
+ <class name="subtitle"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="version">
+ <property name="can-focus">0</property>
+ <property name="halign">end</property>
+ <property name="label">1337.0.0</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkExpander" id="expander">
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+ <property name="margin-start">12</property>
+ <property name="margin-end">12</property>
+ <property name="margin-top">12</property>
+ <property name="margin-bottom">12</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="config">
+ <property name="receives-default">1</property>
+ <property name="valign">center</property>
+ <property name="child">image1</property>
+ <style>
+ <class name="circular"/>
+ </style>
+ </object>
+ </child>
+ <style>
+ <class name="header"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkRevealer" id="revealer">
+ <property name="can-focus">0</property>
+ <property name="reveal-child" bind-source="expander" bind-property="expanded" bind-flags="sync-create"/>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="can-focus">0</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="description">
+ <property name="can-focus">0</property>
+ <property name="margin-start">12</property>
+ <property name="margin-end">12</property>
+ <property name="margin-top">12</property>
+ <property name="margin-bottom">12</property>
+ <property name="label">A longer description of what this plugin does.</property>
+ <property name="wrap">1</property>
+ <property name="xalign">0</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBox">
+ <property name="can-focus">0</property>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="activatable">0</property>
+ <property name="selectable">0</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="can-focus">0</property>
+ <property name="margin-start">12</property>
+ <property name="margin-end">12</property>
+ <property name="margin-top">12</property>
+ <property name="margin-bottom">12</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="authors_label">
+ <property name="can-focus">0</property>
+ <property name="label" translatable="yes">Authors</property>
+ <property name="xalign">0</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="authors_box">
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="can-focus">0</property>
+ <property name="label">An Author &lt;author@example.com&gt;</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="activatable">0</property>
+ <property name="selectable">0</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="can-focus">0</property>
+ <property name="margin-start">12</property>
+ <property name="margin-end">12</property>
+ <property name="margin-top">12</property>
+ <property name="margin-bottom">12</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="website_label">
+ <property name="can-focus">0</property>
+ <property name="label" translatable="yes">Website</property>
+ <property name="xalign">0</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="website">
+ <property name="can-focus">0</property>
+ <property name="label">&lt;a href=&quot;https://pidgin.im&quot;&gt;https://pidgin.im&lt;/a&gt;</property>
+ <property name="use-markup">1</property>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="activatable">0</property>
+ <property name="selectable">0</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="can-focus">0</property>
+ <property name="margin-start">12</property>
+ <property name="margin-end">12</property>
+ <property name="margin-top">12</property>
+ <property name="margin-bottom">12</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="dependencies_label">
+ <property name="can-focus">0</property>
+ <property name="label" translatable="yes">Dependencies</property>
+ <property name="xalign">0</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="dependencies_box">
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="can-focus">0</property>
+ <property name="label">(none)</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="activatable">0</property>
+ <property name="selectable">0</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="can-focus">0</property>
+ <property name="margin-start">12</property>
+ <property name="margin-end">12</property>
+ <property name="margin-top">12</property>
+ <property name="margin-bottom">12</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="error_label">
+ <property name="can-focus">0</property>
+ <property name="label" translatable="yes">Error</property>
+ <property name="xalign">0</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="error">
+ <property name="can-focus">0</property>
+ <property name="label">(none)</property>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="activatable">0</property>
+ <property name="selectable">0</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="can-focus">0</property>
+ <property name="margin-start">12</property>
+ <property name="margin-end">12</property>
+ <property name="margin-top">12</property>
+ <property name="margin-bottom">12</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="id_label">
+ <property name="can-focus">0</property>
+ <property name="label" translatable="yes">ID</property>
+ <property name="xalign">0</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="id">
+ <property name="can-focus">0</property>
+ <property name="label">gplugin/example-plugin</property>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="activatable">0</property>
+ <property name="selectable">0</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="can-focus">0</property>
+ <property name="margin-start">12</property>
+ <property name="margin-end">12</property>
+ <property name="margin-top">12</property>
+ <property name="margin-bottom">12</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="filename_label">
+ <property name="can-focus">0</property>
+ <property name="label" translatable="yes">Filename</property>
+ <property name="xalign">0</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="filename">
+ <property name="can-focus">0</property>
+ <property name="label">/the/full/path/to/plugin.so</property>
+ <property name="ellipsize">start</property>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="activatable">0</property>
+ <property name="selectable">0</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="can-focus">0</property>
+ <property name="margin-start">12</property>
+ <property name="margin-end">12</property>
+ <property name="margin-top">12</property>
+ <property name="margin-bottom">12</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="abi_version_label">
+ <property name="can-focus">0</property>
+ <property name="label" translatable="yes">ABI Version</property>
+ <property name="xalign">0</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="abi_version">
+ <property name="can-focus">0</property>
+ <property name="label">DEADC0DE</property>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="activatable">0</property>
+ <property name="selectable">0</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="can-focus">0</property>
+ <property name="margin-start">12</property>
+ <property name="margin-end">12</property>
+ <property name="margin-top">12</property>
+ <property name="margin-bottom">12</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="loader_label">
+ <property name="can-focus">0</property>
+ <property name="label" translatable="yes">Loader</property>
+ <property name="xalign">0</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="loader">
+ <property name="can-focus">0</property>
+ <property name="label">GPluginExampleLoader</property>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="activatable">0</property>
+ <property name="selectable">0</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="can-focus">0</property>
+ <property name="margin-start">12</property>
+ <property name="margin-end">12</property>
+ <property name="margin-top">12</property>
+ <property name="margin-bottom">12</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="internal_label">
+ <property name="can-focus">0</property>
+ <property name="label" translatable="yes">Internal</property>
+ <property name="xalign">0</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="internal">
+ <property name="can-focus">0</property>
+ <property name="label">Maybe</property>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="activatable">0</property>
+ <property name="selectable">0</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="can-focus">0</property>
+ <property name="margin-start">12</property>
+ <property name="margin-end">12</property>
+ <property name="margin-top">12</property>
+ <property name="margin-bottom">12</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="load_on_query_label">
+ <property name="can-focus">0</property>
+ <property name="label" translatable="yes">Load on Query</property>
+ <property name="xalign">0</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="load_on_query">
+ <property name="can-focus">0</property>
+ <property name="label">Maybe</property>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <style>
+ <class name="nested"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </property>
+ <style>
+ <class name="content"/>
+ <class name="expander"/>
+ </style>
+ </template>
+ <object class="GtkSizeGroup">
+ <property name="mode">horizontal</property>
+ <widgets>
+ <widget name="authors_label"/>
+ <widget name="website_label"/>
+ <widget name="dependencies_label"/>
+ <widget name="error_label"/>
+ <widget name="id_label"/>
+ <widget name="filename_label"/>
+ <widget name="abi_version_label"/>
+ <widget name="loader_label"/>
+ <widget name="internal_label"/>
+ <widget name="load_on_query_label"/>
+ </widgets>
+ </object>
+</interface>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/gplugin-gtk4/data/view.ui Mon Dec 06 22:37:08 2021 -0600
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk" version="4.0"/>
+ <template class="GPluginGtkView" parent="GtkBox">
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkSearchBar">
+ <property name="key-capture-widget">list_box</property>
+ <property name="show-close-button">1</property>
+ <child>
+ <object class="GtkSearchEntry" id="search_entry">
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="hscrollbar_policy">never</property>
+ <property name="vexpand">1</property>
+ <child>
+ <object class="GtkFrame">
+ <property name="margin-bottom">24</property>
+ <property name="margin-end">24</property>
+ <property name="margin-start">24</property>
+ <property name="margin-top">24</property>
+ <child>
+ <object class="GtkListBox" id="list_box">
+ <property name="selection-mode">none</property>
+ <property name="show-separators">1</property>
+ <child type="placeholder">
+ <object class="GtkLabel">
+ <property name="label" translatable="yes">No plugins found</property>
+ <property name="margin-bottom">24</property>
+ <property name="margin-end">24</property>
+ <property name="margin-start">24</property>
+ <property name="margin-top">24</property>
+ <style>
+ <class name="large-title"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/gplugin-gtk4/gplugin-gtk-plugin-row.c Mon Dec 06 22:37:08 2021 -0600
@@ -0,0 +1,459 @@
+/*
+ * Copyright (C) 2021 Elliott Sales de Andrade <quantum.analyst@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n-lib.h>
+
+#include <gplugin.h>
+
+#include <gplugin-gtk-plugin-row.h>
+
+/**
+ * GPluginGtkPluginRow:
+ *
+ * A widget that displays a [class@GPlugin.Plugin] in a user friendly way,
+ * intended to be placed in a [class@Gtk.ListBox].
+ */
+
+/******************************************************************************
+ * Structs
+ *****************************************************************************/
+struct _GPluginGtkPluginRow {
+ GtkListBoxRow parent;
+
+ GPluginPlugin *plugin;
+ gulong signal_id;
+
+ /* Header */
+ GtkWidget *enable;
+ GtkWidget *title;
+ GtkWidget *summary;
+ GtkWidget *version;
+ GtkWidget *config;
+
+ /* Details */
+ GtkWidget *description;
+ GtkWidget *authors_box;
+ GtkWidget *website;
+ GtkWidget *dependencies_box;
+ GtkWidget *error;
+ GtkWidget *id;
+ GtkWidget *filename;
+ GtkWidget *abi_version;
+ GtkWidget *loader;
+ GtkWidget *internal;
+ GtkWidget *load_on_query;
+};
+
+/******************************************************************************
+ * Enums
+ *****************************************************************************/
+enum {
+ PROP_ZERO,
+ PROP_PLUGIN,
+ N_PROPERTIES,
+};
+static GParamSpec *properties[N_PROPERTIES] = {
+ NULL,
+};
+
+/******************************************************************************
+ * Helpers
+ *****************************************************************************/
+static void
+_gplugin_gtk_plugin_row_refresh(GPluginGtkPluginRow *row)
+{
+ GtkWidget *widget = NULL;
+ GError *error = NULL;
+ gchar *name = NULL, *version = NULL, *website = NULL;
+ gchar *summary = NULL, *description = NULL, *id = NULL, *abi_version = NULL;
+ gchar *loader = NULL;
+ gchar **authors = NULL;
+ gchar **dependencies = NULL;
+ guint32 abi_version_uint;
+ gboolean loq = FALSE, internal = FALSE;
+ const gchar *filename = NULL;
+
+ /* Remove all the children from the authors box. */
+ while((widget = gtk_widget_get_first_child(row->authors_box)) != NULL) {
+ gtk_box_remove(GTK_BOX(row->authors_box), widget);
+ }
+
+ /* Remove all the children from the dependencies box. */
+ while((widget = gtk_widget_get_first_child(row->dependencies_box)) !=
+ NULL) {
+ gtk_box_remove(GTK_BOX(row->dependencies_box), widget);
+ }
+
+ /* Now get the info if we can. */
+ if(GPLUGIN_IS_PLUGIN(row->plugin)) {
+ GPluginPluginInfo *plugin_info = gplugin_plugin_get_info(row->plugin);
+ GPluginLoader *plugin_loader = gplugin_plugin_get_loader(row->plugin);
+
+ filename = gplugin_plugin_get_filename(row->plugin);
+ error = gplugin_plugin_get_error(row->plugin);
+
+ if(GPLUGIN_IS_LOADER(plugin_loader)) {
+ loader = g_strdup(G_OBJECT_TYPE_NAME(plugin_loader));
+ }
+
+ /* clang-format off */
+ g_object_get(
+ G_OBJECT(plugin_info),
+ "abi_version", &abi_version_uint,
+ "authors", &authors,
+ "summary", &summary,
+ "description", &description,
+ "dependencies", &dependencies,
+ "id", &id,
+ "internal", &internal,
+ "load-on-query", &loq,
+ "name", &name,
+ "version", &version,
+ "website", &website,
+ NULL);
+ /* clang-format on */
+
+ /* Finagle the website. */
+ if(website) {
+ gchar *markup = g_markup_printf_escaped(
+ "<a href=\"%s\">%s</a>",
+ website,
+ website);
+ g_free(website);
+ website = markup;
+ }
+
+ /* Finagle the abi_version. */
+ abi_version = g_strdup_printf("%08x", abi_version_uint);
+
+ g_clear_object(&plugin_loader);
+ g_clear_object(&plugin_info);
+ }
+
+ /* Add a default name if unavailable. */
+ if(name == NULL) {
+ gchar *basename = g_path_get_basename(filename);
+ name = g_strdup_printf(_("Unnamed Plugin: %s"), basename);
+ g_free(basename);
+ }
+
+ gtk_label_set_text(GTK_LABEL(row->title), name);
+ gtk_label_set_text(GTK_LABEL(row->version), version);
+ gtk_label_set_markup(GTK_LABEL(row->website), website);
+ gtk_label_set_text(GTK_LABEL(row->summary), summary);
+ gtk_label_set_text(GTK_LABEL(row->description), description);
+ gtk_widget_set_visible(row->description, description != NULL);
+ gtk_label_set_text(GTK_LABEL(row->id), id);
+ gtk_label_set_text(
+ GTK_LABEL(row->error),
+ (error != NULL) ? error->message : "(none)");
+ gtk_label_set_text(GTK_LABEL(row->filename), filename);
+ gtk_label_set_text(GTK_LABEL(row->abi_version), abi_version);
+ gtk_label_set_text(
+ GTK_LABEL(row->loader),
+ (loader != NULL) ? loader : _("Unknown"));
+ gtk_label_set_text(GTK_LABEL(row->internal), internal ? _("Yes") : _("No"));
+ gtk_label_set_text(GTK_LABEL(row->load_on_query), loq ? _("Yes") : _("No"));
+ /* FIXME: Set this correctly when plugins get proper configuration. */
+ gtk_widget_set_sensitive(row->config, FALSE);
+
+ /* Set the authors. */
+ if(authors) {
+ gint i = 0;
+
+ for(i = 0; authors[i]; i++) {
+ widget = gtk_label_new(authors[i]);
+ gtk_widget_set_halign(widget, GTK_ALIGN_START);
+ gtk_widget_set_valign(widget, GTK_ALIGN_START);
+ gtk_box_append(GTK_BOX(row->authors_box), widget);
+ }
+ } else {
+ widget = gtk_label_new(_("(none)"));
+ gtk_box_append(GTK_BOX(row->authors_box), widget);
+ }
+
+ /* Set the dependencies. */
+ if(dependencies) {
+ gint i = 0;
+
+ for(i = 0; dependencies[i]; i++) {
+ widget = gtk_label_new(dependencies[i]);
+ gtk_widget_set_halign(widget, GTK_ALIGN_START);
+ gtk_widget_set_valign(widget, GTK_ALIGN_START);
+ gtk_box_append(GTK_BOX(row->dependencies_box), widget);
+ }
+ } else {
+ widget = gtk_label_new(_("(none)"));
+ gtk_box_append(GTK_BOX(row->dependencies_box), widget);
+ }
+
+ g_clear_error(&error);
+ g_free(loader);
+ g_free(abi_version);
+ g_strfreev(authors);
+ g_free(summary);
+ g_free(description);
+ g_strfreev(dependencies);
+ g_free(id);
+ g_free(name);
+ g_free(version);
+ g_free(website);
+}
+
+/******************************************************************************
+ * Callbacks
+ *****************************************************************************/
+static void
+gplugin_gtk_plugin_row_state_cb(
+ G_GNUC_UNUSED GObject *obj,
+ G_GNUC_UNUSED GParamSpec *pspec,
+ gpointer data)
+{
+ _gplugin_gtk_plugin_row_refresh(GPLUGIN_GTK_PLUGIN_ROW(data));
+}
+
+/******************************************************************************
+ * GObject Implementation
+ *****************************************************************************/
+G_DEFINE_TYPE(
+ GPluginGtkPluginRow,
+ gplugin_gtk_plugin_row,
+ GTK_TYPE_LIST_BOX_ROW)
+
+static void
+gplugin_gtk_plugin_row_set_property(
+ GObject *obj,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GPluginGtkPluginRow *row = GPLUGIN_GTK_PLUGIN_ROW(obj);
+
+ switch(prop_id) {
+ case PROP_PLUGIN:
+ gplugin_gtk_plugin_row_set_plugin(row, g_value_get_object(value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gplugin_gtk_plugin_row_get_property(
+ GObject *obj,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GPluginGtkPluginRow *row = GPLUGIN_GTK_PLUGIN_ROW(obj);
+
+ switch(prop_id) {
+ case PROP_PLUGIN:
+ g_value_set_object(value, gplugin_gtk_plugin_row_get_plugin(row));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gplugin_gtk_plugin_row_finalize(GObject *obj)
+{
+ GPluginGtkPluginRow *row = GPLUGIN_GTK_PLUGIN_ROW(obj);
+
+ if(row->signal_id != 0 && GPLUGIN_IS_PLUGIN(row->plugin)) {
+ g_signal_handler_disconnect(G_OBJECT(row->plugin), row->signal_id);
+ }
+
+ g_clear_object(&row->plugin);
+
+ G_OBJECT_CLASS(gplugin_gtk_plugin_row_parent_class)->finalize(obj);
+}
+
+static void
+gplugin_gtk_plugin_row_init(GPluginGtkPluginRow *row)
+{
+ gtk_widget_init_template(GTK_WIDGET(row));
+
+ row->signal_id = 0;
+}
+
+static void
+gplugin_gtk_plugin_row_class_init(GPluginGtkPluginRowClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+
+ obj_class->get_property = gplugin_gtk_plugin_row_get_property;
+ obj_class->set_property = gplugin_gtk_plugin_row_set_property;
+ obj_class->finalize = gplugin_gtk_plugin_row_finalize;
+
+ /* properties */
+ properties[PROP_PLUGIN] = g_param_spec_object(
+ "plugin",
+ "plugin",
+ "The GPluginPlugin whose info should be displayed",
+ G_TYPE_OBJECT,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
+
+ /* template stuff */
+ gtk_widget_class_set_template_from_resource(
+ widget_class,
+ "/org/imfreedom/keep/gplugin/gplugin-gtk/plugin-row.ui");
+
+ /* Header */
+ gtk_widget_class_bind_template_child(
+ widget_class,
+ GPluginGtkPluginRow,
+ enable);
+ gtk_widget_class_bind_template_child(
+ widget_class,
+ GPluginGtkPluginRow,
+ title);
+ gtk_widget_class_bind_template_child(
+ widget_class,
+ GPluginGtkPluginRow,
+ summary);
+ gtk_widget_class_bind_template_child(
+ widget_class,
+ GPluginGtkPluginRow,
+ version);
+ gtk_widget_class_bind_template_child(
+ widget_class,
+ GPluginGtkPluginRow,
+ config);
+
+ /* Details */
+ gtk_widget_class_bind_template_child(
+ widget_class,
+ GPluginGtkPluginRow,
+ description);
+ gtk_widget_class_bind_template_child(
+ widget_class,
+ GPluginGtkPluginRow,
+ authors_box);
+ gtk_widget_class_bind_template_child(
+ widget_class,
+ GPluginGtkPluginRow,
+ website);
+ gtk_widget_class_bind_template_child(
+ widget_class,
+ GPluginGtkPluginRow,
+ dependencies_box);
+ gtk_widget_class_bind_template_child(
+ widget_class,
+ GPluginGtkPluginRow,
+ error);
+ gtk_widget_class_bind_template_child(widget_class, GPluginGtkPluginRow, id);
+ gtk_widget_class_bind_template_child(
+ widget_class,
+ GPluginGtkPluginRow,
+ filename);
+ gtk_widget_class_bind_template_child(
+ widget_class,
+ GPluginGtkPluginRow,
+ abi_version);
+ gtk_widget_class_bind_template_child(
+ widget_class,
+ GPluginGtkPluginRow,
+ loader);
+ gtk_widget_class_bind_template_child(
+ widget_class,
+ GPluginGtkPluginRow,
+ internal);
+ gtk_widget_class_bind_template_child(
+ widget_class,
+ GPluginGtkPluginRow,
+ load_on_query);
+}
+
+/******************************************************************************
+ * Public API
+ *****************************************************************************/
+
+/**
+ * gplugin_gtk_plugin_row_new:
+ *
+ * Create a new [class@GPluginGtk4.View] which can be used to display info
+ * about a [class@GPlugin.Plugin].
+ *
+ * Returns: (transfer full): The new widget.
+ */
+GtkWidget *
+gplugin_gtk_plugin_row_new(void)
+{
+ return g_object_new(GPLUGIN_GTK_TYPE_PLUGIN_ROW, NULL);
+}
+
+/**
+ * gplugin_gtk_plugin_row_set_plugin:
+ * @row: The plugin row instance.
+ * @plugin: The plugin instance.
+ *
+ * Sets the [class@GPlugin.Plugin] that should be displayed.
+ *
+ * A @plugin value of %NULL will clear the widget.
+ */
+void
+gplugin_gtk_plugin_row_set_plugin(
+ GPluginGtkPluginRow *row,
+ GPluginPlugin *plugin)
+{
+ g_return_if_fail(GPLUGIN_GTK_IS_PLUGIN_ROW(row));
+
+ if(row->signal_id != 0 && GPLUGIN_IS_PLUGIN(row->plugin)) {
+ g_signal_handler_disconnect(row->plugin, row->signal_id);
+ row->signal_id = 0;
+ }
+
+ if(g_set_object(&row->plugin, plugin)) {
+ _gplugin_gtk_plugin_row_refresh(row);
+
+ if(GPLUGIN_IS_PLUGIN(plugin)) {
+ /* Connect a signal to refresh when the plugin's state changes. We
+ * can't use g_signal_connect_object because the plugin object never
+ * gets destroyed, as the manager and the loader both keep a
+ * reference to it and the GPluginGtkPluginRow widget is reused for
+ * all plugins so that all means that we just have to manage the
+ * callback ourselves.
+ */
+ row->signal_id = g_signal_connect(
+ G_OBJECT(plugin),
+ "notify::state",
+ G_CALLBACK(gplugin_gtk_plugin_row_state_cb),
+ row);
+ }
+ }
+}
+
+/**
+ * gplugin_gtk_plugin_row_get_plugin:
+ * @row: The plugin row instance.
+ *
+ * Returns the [class@GPlugin.Plugin] that's being displayed.
+ *
+ * Returns: (transfer none): The plugin that's being displayed.
+ */
+GPluginPlugin *
+gplugin_gtk_plugin_row_get_plugin(GPluginGtkPluginRow *row)
+{
+ g_return_val_if_fail(GPLUGIN_GTK_IS_PLUGIN_ROW(row), NULL);
+
+ return row->plugin;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/gplugin-gtk4/gplugin-gtk-plugin-row.h Mon Dec 06 22:37:08 2021 -0600
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2021 Elliott Sales de Andrade <quantum.analyst@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined(GPLUGIN_GTK_GLOBAL_HEADER_INSIDE) && \
+ !defined(GPLUGIN_GTK_COMPILATION)
+#error "only <gplugin-gtk.h> may be included directly"
+#endif
+
+#ifndef GPLUGIN_GTK_PLUGIN_ROW_H
+#define GPLUGIN_GTK_PLUGIN_ROW_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <gtk/gtk.h>
+
+#include <gplugin.h>
+
+G_BEGIN_DECLS
+
+#define GPLUGIN_GTK_TYPE_PLUGIN_ROW (gplugin_gtk_plugin_row_get_type())
+G_DECLARE_FINAL_TYPE(
+ GPluginGtkPluginRow,
+ gplugin_gtk_plugin_row,
+ GPLUGIN_GTK,
+ PLUGIN_ROW,
+ GtkListBoxRow)
+
+GtkWidget *gplugin_gtk_plugin_row_new(void);
+
+void gplugin_gtk_plugin_row_set_plugin(
+ GPluginGtkPluginRow *row,
+ GPluginPlugin *plugin);
+GPluginPlugin *gplugin_gtk_plugin_row_get_plugin(GPluginGtkPluginRow *row);
+
+G_END_DECLS
+
+#endif /* GPLUGIN_GTK_PLUGIN_ROW_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/gplugin-gtk4/gplugin-gtk-view.c Mon Dec 06 22:37:08 2021 -0600
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2021 Elliott Sales de Andrade <quantum.analyst@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <gplugin.h>
+
+#include <gplugin-gtk-plugin-row.h>
+#include <gplugin-gtk-view.h>
+
+/**
+ * GPluginGtkView:
+ *
+ * A [class@Gtk.ListBox] widget that displays all the plugins and some basic
+ * information about them.
+ */
+
+/******************************************************************************
+ * Structs
+ *****************************************************************************/
+struct _GPluginGtkView {
+ GtkBox parent;
+
+ GtkWidget *list_box;
+ GtkWidget *search_entry;
+
+ GPluginManager *manager;
+ gboolean show_internal;
+};
+
+/******************************************************************************
+ * Enums
+ *****************************************************************************/
+enum {
+ PROP_ZERO,
+ PROP_MANAGER,
+ PROP_SHOW_INTERNAL,
+ N_PROPERTIES,
+};
+static GParamSpec *properties[N_PROPERTIES] = {
+ NULL,
+};
+
+/******************************************************************************
+ * Helpers
+ *****************************************************************************/
+static void
+gplugin_gtk_view_add_plugins_to_list(
+ G_GNUC_UNUSED const gchar *id,
+ GSList *plugins,
+ gpointer data)
+{
+ GPluginGtkView *view = GPLUGIN_GTK_VIEW(data);
+
+ for(; plugins; plugins = plugins->next) {
+ GtkWidget *row = gplugin_gtk_plugin_row_new();
+ gplugin_gtk_plugin_row_set_plugin(
+ GPLUGIN_GTK_PLUGIN_ROW(row),
+ GPLUGIN_PLUGIN(plugins->data));
+ gtk_list_box_append(GTK_LIST_BOX(view->list_box), row);
+ }
+}
+
+/******************************************************************************
+ * GObject Implementation
+ *****************************************************************************/
+G_DEFINE_TYPE(GPluginGtkView, gplugin_gtk_view, GTK_TYPE_BOX)
+
+static void
+gplugin_gtk_view_set_property(
+ GObject *obj,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GPluginGtkView *view = GPLUGIN_GTK_VIEW(obj);
+
+ switch(prop_id) {
+ case PROP_MANAGER:
+ gplugin_gtk_view_set_manager(view, g_value_get_object(value));
+ break;
+ case PROP_SHOW_INTERNAL:
+ gplugin_gtk_view_set_show_internal(
+ view,
+ g_value_get_boolean(value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gplugin_gtk_view_get_property(
+ GObject *obj,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GPluginGtkView *view = GPLUGIN_GTK_VIEW(obj);
+
+ switch(prop_id) {
+ case PROP_MANAGER:
+ g_value_set_object(value, gplugin_gtk_view_get_manager(view));
+ break;
+ case PROP_SHOW_INTERNAL:
+ g_value_set_boolean(
+ value,
+ gplugin_gtk_view_get_show_internal(view));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gplugin_gtk_view_finalize(GObject *obj)
+{
+ GPluginGtkView *view = GPLUGIN_GTK_VIEW(obj);
+
+ g_clear_object(&view->manager);
+
+ G_OBJECT_CLASS(gplugin_gtk_view_parent_class)->finalize(obj);
+}
+
+static void
+gplugin_gtk_view_class_init(GPluginGtkViewClass *klass)
+{
+ GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
+
+ obj_class->get_property = gplugin_gtk_view_get_property;
+ obj_class->set_property = gplugin_gtk_view_set_property;
+ obj_class->finalize = gplugin_gtk_view_finalize;
+
+ gtk_widget_class_set_template_from_resource(
+ widget_class,
+ "/org/imfreedom/keep/gplugin/gplugin-gtk/view.ui");
+
+ gtk_widget_class_bind_template_child(
+ widget_class,
+ GPluginGtkView,
+ list_box);
+ gtk_widget_class_bind_template_child(
+ widget_class,
+ GPluginGtkView,
+ search_entry);
+
+ /* properties */
+
+ /**
+ * GPluginGtkView:manager:
+ *
+ * The plugin manager to display.
+ */
+ properties[PROP_MANAGER] = g_param_spec_object(
+ "manager",
+ "manager",
+ "The plugin manager to display",
+ GPLUGIN_TYPE_MANAGER,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ /**
+ * GPluginGtkView:show-internal:
+ *
+ * Whether or not to show internal plugins.
+ */
+ properties[PROP_SHOW_INTERNAL] = g_param_spec_boolean(
+ "show-internal",
+ "show-internal",
+ "Whether or not to show internal plugins",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
+}
+
+static void
+gplugin_gtk_view_init(GPluginGtkView *self)
+{
+ gtk_widget_init_template(GTK_WIDGET(self));
+}
+
+/******************************************************************************
+ * API
+ *****************************************************************************/
+
+/**
+ * gplugin_gtk_view_new:
+ *
+ * Creates a new [class@GPluginGtk4.View].
+ *
+ * Returns: (transfer full): The new view.
+ */
+GtkWidget *
+gplugin_gtk_view_new(void)
+{
+ GObject *ret = NULL;
+
+ /* clang-format off */
+ ret = g_object_new(
+ GPLUGIN_GTK_TYPE_VIEW,
+ NULL);
+ /* clang-format on */
+
+ return GTK_WIDGET(ret);
+}
+
+/**
+ * gplugin_gtk_view_set_manager:
+ * @view: The GTK view instance.
+ * @manager: The plugin manager to display.
+ *
+ * This function will set which plugin manager to display.
+ */
+void
+gplugin_gtk_view_set_manager(GPluginGtkView *view, GPluginManager *manager)
+{
+ g_return_if_fail(GPLUGIN_GTK_IS_VIEW(view));
+ g_return_if_fail(GPLUGIN_IS_MANAGER(manager));
+
+ if(g_set_object(&view->manager, manager)) {
+ GtkListBox *box = GTK_LIST_BOX(view->list_box);
+ GtkWidget *child = NULL;
+
+ while((child = gtk_widget_get_first_child(view->list_box)) != NULL) {
+ gtk_list_box_remove(box, child);
+ }
+
+ gplugin_manager_foreach(
+ manager,
+ gplugin_gtk_view_add_plugins_to_list,
+ view);
+
+ g_object_notify(G_OBJECT(view), "manager");
+ }
+}
+
+/**
+ * gplugin_gtk_view_get_manager:
+ * @view: The GTK view instance.
+ *
+ * Returns the plugin manager that is being displayed.
+ *
+ * Returns: (transfer none): The plugin manager to display.
+ */
+GPluginManager *
+gplugin_gtk_view_get_manager(GPluginGtkView *view)
+{
+ g_return_val_if_fail(GPLUGIN_GTK_IS_VIEW(view), FALSE);
+
+ return view->manager;
+}
+
+/**
+ * gplugin_gtk_view_set_show_internal:
+ * @view: The GTK view instance.
+ * @show_internal: Whether or not to show internal plugins.
+ *
+ * This function will toggle whether or not the widget will show internal
+ * plugins.
+ */
+void
+gplugin_gtk_view_set_show_internal(GPluginGtkView *view, gboolean show_internal)
+{
+ g_return_if_fail(GPLUGIN_GTK_IS_VIEW(view));
+
+ view->show_internal = show_internal;
+
+ g_object_notify(G_OBJECT(view), "show-internal");
+}
+
+/**
+ * gplugin_gtk_view_get_show_internal:
+ * @view: The GTK view instance.
+ *
+ * Returns whether or not @view is showing internal plugins.
+ */
+gboolean
+gplugin_gtk_view_get_show_internal(GPluginGtkView *view)
+{
+ g_return_val_if_fail(GPLUGIN_GTK_IS_VIEW(view), FALSE);
+
+ return view->show_internal;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/gplugin-gtk4/gplugin-gtk-view.h Mon Dec 06 22:37:08 2021 -0600
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2021 Elliott Sales de Andrade <quantum.analyst@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined(GPLUGIN_GTK_GLOBAL_HEADER_INSIDE) && \
+ !defined(GPLUGIN_GTK_COMPILATION)
+#error "only <gplugin-gtk.h> may be included directly"
+#endif
+
+#ifndef GPLUGIN_GTK_VIEW_H
+#define GPLUGIN_GTK_VIEW_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <gtk/gtk.h>
+
+#include <gplugin.h>
+
+G_BEGIN_DECLS
+
+#define GPLUGIN_GTK_TYPE_VIEW (gplugin_gtk_view_get_type())
+G_DECLARE_FINAL_TYPE(
+ GPluginGtkView,
+ gplugin_gtk_view,
+ GPLUGIN_GTK,
+ VIEW,
+ GtkBox)
+
+GtkWidget *gplugin_gtk_view_new(void);
+
+void gplugin_gtk_view_set_manager(
+ GPluginGtkView *view,
+ GPluginManager *manager);
+GPluginManager *gplugin_gtk_view_get_manager(GPluginGtkView *view);
+
+void gplugin_gtk_view_set_show_internal(
+ GPluginGtkView *view,
+ gboolean show_internal);
+gboolean gplugin_gtk_view_get_show_internal(GPluginGtkView *view);
+
+G_END_DECLS
+
+#endif /* GPLUGIN_GTK_VIEW_H */
--- a/gplugin-gtk4/meson.build Thu Nov 11 22:49:31 2021 -0600
+++ b/gplugin-gtk4/meson.build Mon Dec 06 22:37:08 2021 -0600
@@ -9,9 +9,13 @@
GPLUGIN_GTK4_LIBRARY_VERSION = '0.1.0'
GPLUGIN_GTK4_SOURCES = [
+ 'gplugin-gtk-plugin-row.c',
+ 'gplugin-gtk-view.c',
]
GPLUGIN_GTK4_HEADERS = [
+ 'gplugin-gtk-plugin-row.h',
+ 'gplugin-gtk-view.h',
]
GPLUGIN_GTK4_PRIVATE_SOURCES = [
--- a/po/POTFILES Thu Nov 11 22:49:31 2021 -0600
+++ b/po/POTFILES Mon Dec 06 22:37:08 2021 -0600
@@ -5,6 +5,10 @@
gplugin-gtk3/gplugin-gtk-plugin-info.c
gplugin-gtk3/gplugin-gtk-store.c
gplugin-gtk3/gplugin-gtk-view.c
+gplugin-gtk4/data/plugin-row.ui
+gplugin-gtk4/data/view.ui
+gplugin-gtk4/gplugin-gtk-plugin-row.c
+gplugin-gtk4/gplugin-gtk-view.c
gplugin-gtk4-viewer/data/window.ui
gplugin-gtk4-viewer/gplugin-gtk-viewer-window.c
gplugin-gtk4-viewer/gplugin-gtk-viewer.c