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>
- <object class="GtkScrolledWindow">
- <property name="hscrollbar_policy">never</property>
- <object class="GtkListBox">
- <child type="placeholder">
- <object class="GtkLabel">
- <property name="label" translatable="yes">No plugins found</property>
+ <object class="GPluginGtkView" id="view"/> --- 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;
@@ -39,6 +43,11 @@
"/org/imfreedom/keep/gplugin/gplugin/viewer/window.ui");
+ gtk_widget_class_bind_template_child( + GPluginGtkViewerWindow, gtk_widget_class_add_binding_action(
@@ -52,3 +61,12 @@
gtk_widget_init_template(GTK_WIDGET(self));
+/****************************************************************************** + *****************************************************************************/ +gplugin_gtk_viewer_window_get_view(GPluginGtkViewerWindow *window) --- 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 <gplugin-gtk.h> #define GPLUGIN_GTK_VIEWER_TYPE_WINDOW (gplugin_gtk_viewer_window_get_type())
@@ -30,6 +32,9 @@
+GPluginGtkView *gplugin_gtk_viewer_window_get_view( + GPluginGtkViewerWindow *window); #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));
+ GPluginGtkView *view = NULL; GPLUGIN_GTK_VIEWER_TYPE_WINDOW,
+ 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"?>
- <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> --- /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"?> + <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> + <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> + <object class="GtkBox" id="header"> + <property name="vexpand">1</property> + <property name="can-focus">0</property> + <property name="spacing">12</property> + <object class="GtkSwitch" id="enable"> + <property name="valign">center</property> + <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> + <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> + <class name="heading"/> + <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> + <class name="subtitle"/> + <object class="GtkLabel" id="version"> + <property name="can-focus">0</property> + <property name="halign">end</property> + <property name="label">1337.0.0</property> + <class name="dim-label"/> + <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 class="GtkButton" id="config"> + <property name="receives-default">1</property> + <property name="valign">center</property> + <property name="child">image1</property> + <class name="circular"/> + <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> + <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 class="GtkListBox"> + <property name="can-focus">0</property> + <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> + <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 class="GtkBox" id="authors_box"> + <property name="orientation">vertical</property> + <object class="GtkLabel"> + <property name="can-focus">0</property> + <property name="label">An Author <author@example.com></property> + <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> + <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 class="GtkLabel" id="website"> + <property name="can-focus">0</property> + <property name="label"><a href="https://pidgin.im">https://pidgin.im</a></property> + <property name="use-markup">1</property> + <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> + <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 class="GtkBox" id="dependencies_box"> + <property name="orientation">vertical</property> + <object class="GtkLabel"> + <property name="can-focus">0</property> + <property name="label">(none)</property> + <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> + <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 class="GtkLabel" id="error"> + <property name="can-focus">0</property> + <property name="label">(none)</property> + <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> + <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 class="GtkLabel" id="id"> + <property name="can-focus">0</property> + <property name="label">gplugin/example-plugin</property> + <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> + <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 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 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> + <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 class="GtkLabel" id="abi_version"> + <property name="can-focus">0</property> + <property name="label">DEADC0DE</property> + <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> + <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 class="GtkLabel" id="loader"> + <property name="can-focus">0</property> + <property name="label">GPluginExampleLoader</property> + <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> + <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 class="GtkLabel" id="internal"> + <property name="can-focus">0</property> + <property name="label">Maybe</property> + <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> + <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 class="GtkLabel" id="load_on_query"> + <property name="can-focus">0</property> + <property name="label">Maybe</property> + <class name="content"/> + <class name="expander"/> + <object class="GtkSizeGroup"> + <property name="mode">horizontal</property> + <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"/> --- /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"?> + <requires lib="gtk" version="4.0"/> + <template class="GPluginGtkView" parent="GtkBox"> + <property name="orientation">vertical</property> + <object class="GtkSearchBar"> + <property name="key-capture-widget">list_box</property> + <property name="show-close-button">1</property> + <object class="GtkSearchEntry" id="search_entry"> + <object class="GtkScrolledWindow"> + <property name="hscrollbar_policy">never</property> + <property name="vexpand">1</property> + <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> + <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> + <class name="large-title"/> --- /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-gtk-plugin-row.h> + * A widget that displays a [class@GPlugin.Plugin] in a user friendly way, + * intended to be placed in a [class@Gtk.ListBox]. +/****************************************************************************** + *****************************************************************************/ +struct _GPluginGtkPluginRow { + GtkWidget *description; + GtkWidget *authors_box; + GtkWidget *dependencies_box; + GtkWidget *abi_version; + GtkWidget *load_on_query; +/****************************************************************************** + *****************************************************************************/ +static GParamSpec *properties[N_PROPERTIES] = { +/****************************************************************************** + *****************************************************************************/ +_gplugin_gtk_plugin_row_refresh(GPluginGtkPluginRow *row) + GtkWidget *widget = NULL; + gchar *name = NULL, *version = NULL, *website = NULL; + gchar *summary = NULL, *description = NULL, *id = NULL, *abi_version = 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)) != + 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)); + "abi_version", &abi_version_uint, + "description", &description, + "dependencies", &dependencies, + /* Finagle the website. */ + gchar *markup = g_markup_printf_escaped( + "<a href=\"%s\">%s</a>", + /* 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. */ + gchar *basename = g_path_get_basename(filename); + name = g_strdup_printf(_("Unnamed Plugin: %s"), 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); + (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(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); + 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); + widget = gtk_label_new(_("(none)")); + gtk_box_append(GTK_BOX(row->authors_box), widget); + /* Set the dependencies. */ + 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); + widget = gtk_label_new(_("(none)")); + gtk_box_append(GTK_BOX(row->dependencies_box), widget); + g_strfreev(dependencies); +/****************************************************************************** + *****************************************************************************/ +gplugin_gtk_plugin_row_state_cb( + G_GNUC_UNUSED GObject *obj, + G_GNUC_UNUSED GParamSpec *pspec, + _gplugin_gtk_plugin_row_refresh(GPLUGIN_GTK_PLUGIN_ROW(data)); +/****************************************************************************** + * GObject Implementation + *****************************************************************************/ + gplugin_gtk_plugin_row, +gplugin_gtk_plugin_row_set_property( + GPluginGtkPluginRow *row = GPLUGIN_GTK_PLUGIN_ROW(obj); + gplugin_gtk_plugin_row_set_plugin(row, g_value_get_object(value)); + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec); +gplugin_gtk_plugin_row_get_property( + GPluginGtkPluginRow *row = GPLUGIN_GTK_PLUGIN_ROW(obj); + g_value_set_object(value, gplugin_gtk_plugin_row_get_plugin(row)); + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec); +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); +gplugin_gtk_plugin_row_init(GPluginGtkPluginRow *row) + gtk_widget_init_template(GTK_WIDGET(row)); +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[PROP_PLUGIN] = g_param_spec_object( + "The GPluginPlugin whose info should be displayed", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS); + gtk_widget_class_set_template_from_resource( + "/org/imfreedom/keep/gplugin/gplugin-gtk/plugin-row.ui"); + gtk_widget_class_bind_template_child( + gtk_widget_class_bind_template_child( + gtk_widget_class_bind_template_child( + gtk_widget_class_bind_template_child( + gtk_widget_class_bind_template_child( + gtk_widget_class_bind_template_child( + gtk_widget_class_bind_template_child( + gtk_widget_class_bind_template_child( + gtk_widget_class_bind_template_child( + gtk_widget_class_bind_template_child( + gtk_widget_class_bind_template_child(widget_class, GPluginGtkPluginRow, id); + gtk_widget_class_bind_template_child( + gtk_widget_class_bind_template_child( + gtk_widget_class_bind_template_child( + gtk_widget_class_bind_template_child( + gtk_widget_class_bind_template_child( +/****************************************************************************** + *****************************************************************************/ + * 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. +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. +gplugin_gtk_plugin_row_set_plugin( + GPluginGtkPluginRow *row, + 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); + 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 + row->signal_id = g_signal_connect( + G_CALLBACK(gplugin_gtk_plugin_row_state_cb), + * 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. +gplugin_gtk_plugin_row_get_plugin(GPluginGtkPluginRow *row) + g_return_val_if_fail(GPLUGIN_GTK_IS_PLUGIN_ROW(row), NULL); --- /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" +#ifndef GPLUGIN_GTK_PLUGIN_ROW_H +#define GPLUGIN_GTK_PLUGIN_ROW_H +#include <glib-object.h> +#define GPLUGIN_GTK_TYPE_PLUGIN_ROW (gplugin_gtk_plugin_row_get_type()) + gplugin_gtk_plugin_row, +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); +#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-gtk-plugin-row.h> +#include <gplugin-gtk-view.h> + * A [class@Gtk.ListBox] widget that displays all the plugins and some basic + * information about them. +/****************************************************************************** + *****************************************************************************/ +struct _GPluginGtkView { + GtkWidget *search_entry; + GPluginManager *manager; + gboolean show_internal; +/****************************************************************************** + *****************************************************************************/ +static GParamSpec *properties[N_PROPERTIES] = { +/****************************************************************************** + *****************************************************************************/ +gplugin_gtk_view_add_plugins_to_list( + G_GNUC_UNUSED const gchar *id, + 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) +gplugin_gtk_view_set_property( + GPluginGtkView *view = GPLUGIN_GTK_VIEW(obj); + gplugin_gtk_view_set_manager(view, g_value_get_object(value)); + case PROP_SHOW_INTERNAL: + gplugin_gtk_view_set_show_internal( + g_value_get_boolean(value)); + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec); +gplugin_gtk_view_get_property( + GPluginGtkView *view = GPLUGIN_GTK_VIEW(obj); + g_value_set_object(value, gplugin_gtk_view_get_manager(view)); + case PROP_SHOW_INTERNAL: + gplugin_gtk_view_get_show_internal(view)); + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec); +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); +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( + "/org/imfreedom/keep/gplugin/gplugin-gtk/view.ui"); + gtk_widget_class_bind_template_child( + gtk_widget_class_bind_template_child( + * GPluginGtkView:manager: + * The plugin manager to display. + properties[PROP_MANAGER] = g_param_spec_object( + "The plugin manager to display", + 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( + "Whether or not to show internal plugins", + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_properties(obj_class, N_PROPERTIES, properties); +gplugin_gtk_view_init(GPluginGtkView *self) + gtk_widget_init_template(GTK_WIDGET(self)); +/****************************************************************************** + *****************************************************************************/ + * gplugin_gtk_view_new: + * Creates a new [class@GPluginGtk4.View]. + * Returns: (transfer full): The new view. +gplugin_gtk_view_new(void) + 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. +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( + gplugin_gtk_view_add_plugins_to_list, + 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. +gplugin_gtk_view_get_manager(GPluginGtkView *view) + g_return_val_if_fail(GPLUGIN_GTK_IS_VIEW(view), FALSE); + * 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 +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. +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" +#ifndef GPLUGIN_GTK_VIEW_H +#define GPLUGIN_GTK_VIEW_H +#include <glib-object.h> +#define GPLUGIN_GTK_TYPE_VIEW (gplugin_gtk_view_get_type()) +GtkWidget *gplugin_gtk_view_new(void); +void gplugin_gtk_view_set_manager( + GPluginManager *manager); +GPluginManager *gplugin_gtk_view_get_manager(GPluginGtkView *view); +void gplugin_gtk_view_set_show_internal( + gboolean show_internal); +gboolean gplugin_gtk_view_get_show_internal(GPluginGtkView *view); +#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-gtk-plugin-row.c', + 'gplugin-gtk-plugin-row.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