# HG changeset patch
# Node ID 8000992fb2c2
# Parent  3d069c64056d
diff --git a/gplugin-gtk4/data/gplugin-gtk.gresource.xml b/gplugin-gtk4/data/gplugin-gtk.gresource.xml
--- a/gplugin-gtk4/data/gplugin-gtk.gresource.xml
+++ b/gplugin-gtk4/data/gplugin-gtk.gresource.xml
@@ -3,6 +3,7 @@
   <gresource prefix="/org/imfreedom/keep/gplugin/gplugin-gtk/">
     <file compressed="true" preprocess="xml-stripblanks">plugin-page.ui</file>
     <file compressed="true" preprocess="xml-stripblanks">plugin-row.ui</file>
+    <file compressed="true" preprocess="xml-stripblanks">plugin-settings-list.ui</file>
     <file compressed="true" preprocess="xml-stripblanks">view.ui</file>
   </gresource>
 </gresources>
diff --git a/gplugin-gtk4/data/plugin-page.ui b/gplugin-gtk4/data/plugin-page.ui
--- a/gplugin-gtk4/data/plugin-page.ui
+++ b/gplugin-gtk4/data/plugin-page.ui
@@ -432,6 +432,10 @@
         </property>
       </object>
     </child>
+    <child>
+      <object class="GPluginGtkPluginSettingsList" id="settings">
+      </object>
+    </child>
   </template>
   <object class="GtkSizeGroup">
     <property name="mode">horizontal</property>
diff --git a/gplugin-gtk4/data/plugin-settings-list.ui b/gplugin-gtk4/data/plugin-settings-list.ui
new file mode 100644
--- /dev/null
+++ b/gplugin-gtk4/data/plugin-settings-list.ui
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <requires lib="gtk" version="4.0"/>
+  <template class="GPluginGtkPluginSettingsList" parent="GtkBox">
+    <property name="orientation">vertical</property>
+    <property name="spacing">6</property>
+    <child>
+      <object class="GtkLabel">
+        <property name="css-classes">title-2</property>
+        <property name="label" translatable="1">Settings</property>
+        <property name="xalign">0</property>
+      </object>
+    </child>
+    <child>
+      <object class="GtkFrame">
+        <child>
+          <object class="GtkListBox" id="list_box">
+            <property name="css-classes">rich-list</property>
+            <property name="selection-mode">none</property>
+            <property name="show-separators">1</property>
+            <child type="placeholder">
+              <object class="GtkLabel">
+                <property name="css-classes">large-title</property>
+                <property name="label" translatable="1">No settings available</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>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/gplugin-gtk4/gplugin-gtk-plugin-page.c b/gplugin-gtk4/gplugin-gtk-plugin-page.c
--- a/gplugin-gtk4/gplugin-gtk-plugin-page.c
+++ b/gplugin-gtk4/gplugin-gtk-plugin-page.c
@@ -21,6 +21,7 @@
 
 #include <gplugin-gtk-plugin-closures.h>
 #include <gplugin-gtk-plugin-page.h>
+#include <gplugin-gtk-plugin-settings-list.h>
 
 #define G_SETTINGS_ENABLE_BACKEND
 #include <gio/gsettingsbackend.h>
@@ -42,6 +43,8 @@ struct _GPluginGtkPluginPage {
 
 	GPluginPlugin *plugin;
 	GSettingsBackend *backend;
+
+	GPluginGtkPluginSettingsList *settings;
 };
 
 /******************************************************************************
@@ -66,6 +69,36 @@ static guint signals[N_SIGNALS] = {
 };
 
 /******************************************************************************
+ * Helpers
+ *****************************************************************************/
+static void
+gplugin_gtk_plugin_page_update_settings_list(GPluginGtkPluginPage *page)
+{
+	GSettings *settings = NULL;
+
+	if(GPLUGIN_IS_PLUGIN(page->plugin)) {
+		GPluginPluginInfo *info = NULL;
+
+		info = gplugin_plugin_get_info(page->plugin);
+		if(GPLUGIN_IS_PLUGIN_INFO(info)) {
+			const gchar *schema = NULL;
+
+			schema = gplugin_plugin_info_get_settings_schema(info);
+			if(schema != NULL) {
+				if(page->backend != NULL) {
+					settings =
+						g_settings_new_with_backend(schema, page->backend);
+				} else {
+					settings = g_settings_new(schema);
+				}
+			}
+		}
+	}
+
+	gplugin_gtk_plugin_settings_list_set_settings(page->settings, settings);
+}
+
+/******************************************************************************
  * Callbacks
  *****************************************************************************/
 static gboolean
@@ -285,6 +318,11 @@ gplugin_gtk_plugin_page_class_init(GPlug
 		widget_class,
 		"/org/imfreedom/keep/gplugin/gplugin-gtk/plugin-page.ui");
 
+	gtk_widget_class_bind_template_child(
+		widget_class,
+		GPluginGtkPluginPage,
+		settings);
+
 	gtk_widget_class_bind_template_callback(
 		widget_class,
 		gplugin_gtk_lookup_plugin_name);
@@ -353,6 +391,8 @@ gplugin_gtk_plugin_page_set_plugin(
 	g_return_if_fail(GPLUGIN_GTK_IS_PLUGIN_PAGE(page));
 
 	if(g_set_object(&page->plugin, plugin)) {
+		gplugin_gtk_plugin_page_update_settings_list(page);
+
 		g_object_notify_by_pspec(G_OBJECT(page), properties[PROP_PLUGIN]);
 	}
 }
@@ -399,6 +439,8 @@ gplugin_gtk_plugin_page_set_settings_bac
 	g_return_if_fail(G_IS_SETTINGS_BACKEND(backend) || backend == NULL);
 
 	if(g_set_object(&page->backend, backend)) {
+		gplugin_gtk_plugin_page_update_settings_list(page);
+
 		g_object_notify_by_pspec(
 			G_OBJECT(page),
 			properties[PROP_SETTINGS_BACKEND]);
diff --git a/gplugin-gtk4/gplugin-gtk-plugin-settings-list.h b/gplugin-gtk4/gplugin-gtk-plugin-settings-list.h
new file mode 100644
--- /dev/null
+++ b/gplugin-gtk4/gplugin-gtk-plugin-settings-list.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2022 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_SETTINGS_LIST_H
+#define GPLUGIN_GTK_PLUGIN_SETTINGS_LIST_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <gtk/gtk.h>
+
+#include <gplugin.h>
+
+G_BEGIN_DECLS
+
+#define GPLUGIN_GTK_TYPE_PLUGIN_SETTINGS_LIST \
+	(gplugin_gtk_plugin_settings_list_get_type())
+G_DECLARE_FINAL_TYPE(
+	GPluginGtkPluginSettingsList,
+	gplugin_gtk_plugin_settings_list,
+	GPLUGIN_GTK,
+	PLUGIN_SETTINGS_LIST,
+	GtkBox)
+
+GtkWidget *gplugin_gtk_plugin_settings_list_new(void);
+
+void gplugin_gtk_plugin_settings_list_set_settings(
+	GPluginGtkPluginSettingsList *list,
+	GSettings *settings);
+GSettings *gplugin_gtk_plugin_settings_list_get_settings(
+	GPluginGtkPluginSettingsList *list);
+
+G_END_DECLS
+
+#endif /* GPLUGIN_GTK_PLUGIN_SETTINGS_LIST_H */
diff --git a/gplugin-gtk4/gplugin-gtk-plugin-settings-list.c b/gplugin-gtk4/gplugin-gtk-plugin-settings-list.c
new file mode 100644
--- /dev/null
+++ b/gplugin-gtk4/gplugin-gtk-plugin-settings-list.c
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2022 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-settings-list.h>
+
+/**
+ * GPluginGtkPluginSettingslist:
+ *
+ * A [class@Gtk.ListBox] widget that displays all the settings from a plugin.
+ *
+ * Since: 0.40.0
+ */
+
+/******************************************************************************
+ * Structs
+ *****************************************************************************/
+struct _GPluginGtkPluginSettingsList {
+	GtkBox parent;
+
+	GSettings *settings;
+	GtkListBox *list_box;
+	GList *rows;
+};
+
+/******************************************************************************
+ * Enums
+ *****************************************************************************/
+enum {
+	PROP_ZERO,
+	PROP_SETTINGS,
+	N_PROPERTIES,
+};
+static GParamSpec *properties[N_PROPERTIES] = {
+	NULL,
+};
+
+/******************************************************************************
+ * Helpers
+ *****************************************************************************/
+static GtkWidget *
+gplugin_gtk_plugin_settings_row_new_for_key(
+	GSettings *settings,
+	const gchar *name,
+	GSettingsSchemaKey *key)
+{
+	/* TODO: Make real widgets. */
+	GtkWidget *widget = NULL;
+	GtkWidget *label = NULL;
+	GtkWidget *entry = NULL;
+	GVariant *value = NULL;
+	char *value_str = NULL;
+
+	widget = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12);
+
+	label = gtk_label_new(g_settings_schema_key_get_name(key));
+	gtk_label_set_xalign(GTK_LABEL(label), 0);
+	gtk_box_append(GTK_BOX(widget), label);
+
+	value = g_settings_get_value(settings, name);
+	value_str = g_variant_print(value, TRUE);
+	g_variant_unref(value);
+
+	entry = gtk_entry_new();
+	gtk_editable_set_text(GTK_EDITABLE(entry), value_str);
+	gtk_editable_set_editable(GTK_EDITABLE(entry), FALSE);
+	gtk_widget_set_hexpand(entry, TRUE);
+	gtk_box_append(GTK_BOX(widget), entry);
+
+	g_free(value_str);
+
+	return widget;
+}
+
+static void
+gplugin_gtk_plugin_settings_list_refresh(
+	GPluginGtkPluginSettingsList *list,
+	GSettings *settings)
+{
+	GSettingsSchema *schema = NULL;
+	gchar **names = NULL;
+
+	while(list->rows) {
+		gtk_list_box_remove(list->list_box, list->rows->data);
+		list->rows = g_list_delete_link(list->rows, list->rows);
+	}
+
+	if(settings == NULL) {
+		return;
+	}
+
+	g_object_get(G_OBJECT(settings), "settings-schema", &schema, NULL);
+	names = g_settings_schema_list_keys(schema);
+
+	for(gint i = 0; names[i] != NULL; i++) {
+		GSettingsSchemaKey *key = NULL;
+		GtkWidget *row = NULL;
+		GtkWidget *widget = NULL;
+
+		key = g_settings_schema_get_key(schema, names[i]);
+		widget = gplugin_gtk_plugin_settings_row_new_for_key(
+			settings,
+			names[i],
+			key);
+
+		row = gtk_list_box_row_new();
+		gtk_list_box_row_set_child(GTK_LIST_BOX_ROW(row), widget);
+		gtk_widget_set_focusable(row, FALSE);
+		gtk_list_box_append(list->list_box, row);
+
+		list->rows = g_list_append(list->rows, row);
+
+		g_settings_schema_key_unref(key);
+	}
+
+	g_strfreev(names);
+	g_settings_schema_unref(schema);
+}
+
+/******************************************************************************
+ * GObject Implementation
+ *****************************************************************************/
+G_DEFINE_TYPE(
+	GPluginGtkPluginSettingsList,
+	gplugin_gtk_plugin_settings_list,
+	GTK_TYPE_BOX)
+
+static void
+gplugin_gtk_plugin_settings_list_set_property(
+	GObject *obj,
+	guint prop_id,
+	const GValue *value,
+	GParamSpec *pspec)
+{
+	GPluginGtkPluginSettingsList *list = GPLUGIN_GTK_PLUGIN_SETTINGS_LIST(obj);
+
+	switch(prop_id) {
+		case PROP_SETTINGS:
+			gplugin_gtk_plugin_settings_list_set_settings(
+				list,
+				g_value_get_object(value));
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec);
+			break;
+	}
+}
+
+static void
+gplugin_gtk_plugin_settings_list_get_property(
+	GObject *obj,
+	guint prop_id,
+	GValue *value,
+	GParamSpec *pspec)
+{
+	GPluginGtkPluginSettingsList *list = GPLUGIN_GTK_PLUGIN_SETTINGS_LIST(obj);
+
+	switch(prop_id) {
+		case PROP_SETTINGS:
+			g_value_set_object(
+				value,
+				gplugin_gtk_plugin_settings_list_get_settings(list));
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec);
+			break;
+	}
+}
+
+static void
+gplugin_gtk_plugin_settings_list_finalize(GObject *obj)
+{
+	GPluginGtkPluginSettingsList *list = GPLUGIN_GTK_PLUGIN_SETTINGS_LIST(obj);
+
+	g_clear_object(&list->settings);
+	g_clear_pointer(&list->rows, g_list_free);
+
+	G_OBJECT_CLASS(gplugin_gtk_plugin_settings_list_parent_class)
+		->finalize(obj);
+}
+
+static void
+gplugin_gtk_plugin_settings_list_class_init(
+	GPluginGtkPluginSettingsListClass *klass)
+{
+	GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
+
+	obj_class->get_property = gplugin_gtk_plugin_settings_list_get_property;
+	obj_class->set_property = gplugin_gtk_plugin_settings_list_set_property;
+	obj_class->finalize = gplugin_gtk_plugin_settings_list_finalize;
+
+	gtk_widget_class_set_template_from_resource(
+		widget_class,
+		"/org/imfreedom/keep/gplugin/gplugin-gtk/plugin-settings-list.ui");
+
+	gtk_widget_class_bind_template_child(
+		widget_class,
+		GPluginGtkPluginSettingsList,
+		list_box);
+
+	/* properties */
+
+	/**
+	 * GPluginGtkPluginSettingsList:settings:
+	 *
+	 * The [class@Gio.Settings] to display.
+	 */
+	properties[PROP_SETTINGS] = g_param_spec_object(
+		"settings",
+		"settings",
+		"The settings to display",
+		G_TYPE_SETTINGS,
+		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
+}
+
+static void
+gplugin_gtk_plugin_settings_list_init(GPluginGtkPluginSettingsList *self)
+{
+	gtk_widget_init_template(GTK_WIDGET(self));
+}
+
+/******************************************************************************
+ * API
+ *****************************************************************************/
+
+/**
+ * gplugin_gtk_plugin_settings_list_new:
+ *
+ * Creates a new [class@GPluginGtk4.PluginSettingsList].
+ *
+ * Returns: (transfer full): The new list.
+ *
+ * Since: 0.40.0
+ */
+GtkWidget *
+gplugin_gtk_plugin_settings_list_new(void)
+{
+	return g_object_new(GPLUGIN_GTK_TYPE_PLUGIN_SETTINGS_LIST, NULL);
+}
+
+/**
+ * gplugin_gtk_plugin_settings_list_set_settings:
+ * @list: The instance.
+ * @settings: (transfer none) (nullable): The plugin settings to display.
+ *
+ * This function will set which plugin settings to display.
+ *
+ * Since: 0.40.0
+ */
+void
+gplugin_gtk_plugin_settings_list_set_settings(
+	GPluginGtkPluginSettingsList *list,
+	GSettings *settings)
+{
+	g_return_if_fail(GPLUGIN_GTK_IS_PLUGIN_SETTINGS_LIST(list));
+	g_return_if_fail(G_IS_SETTINGS(settings) || settings == NULL);
+
+	if(g_set_object(&list->settings, settings)) {
+		gplugin_gtk_plugin_settings_list_refresh(list, settings);
+
+		g_object_notify(G_OBJECT(list), "settings");
+	}
+}
+
+/**
+ * gplugin_gtk_plugin_settings_list_get_settings:
+ * @list: The instance.
+ *
+ * Returns the plugin settings that are being displayed.
+ *
+ * Returns: (transfer none): The settings being displayed.
+ *
+ * Since: 0.40.0
+ */
+GSettings *
+gplugin_gtk_plugin_settings_list_get_settings(
+	GPluginGtkPluginSettingsList *list)
+{
+	g_return_val_if_fail(GPLUGIN_GTK_IS_PLUGIN_SETTINGS_LIST(list), NULL);
+
+	return list->settings;
+}
diff --git a/gplugin-gtk4/meson.build b/gplugin-gtk4/meson.build
--- a/gplugin-gtk4/meson.build
+++ b/gplugin-gtk4/meson.build
@@ -12,12 +12,14 @@ GPLUGIN_GTK4_LIBRARY_VERSION = '0.1.0'
 GPLUGIN_GTK4_SOURCES = [
 	'gplugin-gtk-plugin-page.c',
 	'gplugin-gtk-plugin-row.c',
+	'gplugin-gtk-plugin-settings-list.c',
 	'gplugin-gtk-view.c',
 ]
 
 GPLUGIN_GTK4_HEADERS = [
 	'gplugin-gtk-plugin-page.h',
 	'gplugin-gtk-plugin-row.h',
+	'gplugin-gtk-plugin-settings-list.h',
 	'gplugin-gtk-view.h',
 ]
 
diff --git a/po/POTFILES b/po/POTFILES
--- a/po/POTFILES
+++ b/po/POTFILES
@@ -1,8 +1,10 @@
 gplugin-gtk4/data/plugin-page.ui
 gplugin-gtk4/data/plugin-row.ui
+gplugin-gtk4/data/plugin-settings-list.ui
 gplugin-gtk4/data/view.ui
 gplugin-gtk4/gplugin-gtk-plugin-page.c
 gplugin-gtk4/gplugin-gtk-plugin-row.c
+gplugin-gtk4/gplugin-gtk-plugin-settings-list.c
 gplugin-gtk4/gplugin-gtk-view.c
 gplugin-gtk4-viewer/data/window.ui
 gplugin-gtk4-viewer/gplugin-gtk-viewer-window.c
