# HG changeset patch
# Node ID ff7a551d0304
# Parent ec766bf5a520
diff --git a/gplugin-gtk4/gplugin-gtk-plugin-settings-list.c b/gplugin-gtk4/gplugin-gtk-plugin-settings-list.c
--- a/gplugin-gtk4/gplugin-gtk-plugin-settings-list.c
+++ b/gplugin-gtk4/gplugin-gtk-plugin-settings-list.c
@@ -15,6 +15,8 @@
* License along with this library; if not, see .
*/
+#include
+
#include
#include
@@ -51,39 +53,419 @@ static GParamSpec *properties[N_PROPERTI
};
/******************************************************************************
+ * Callbacks
+ *****************************************************************************/
+static gboolean
+gplugin_gtk_plugin_settings_row_byte_to_double(
+ GValue *value,
+ GVariant *variant,
+ G_GNUC_UNUSED gpointer data)
+{
+ g_value_set_double(value, (gdouble)g_variant_get_byte(variant));
+ return TRUE;
+}
+
+static GVariant *
+gplugin_gtk_plugin_settings_row_double_to_byte(
+ const GValue *value,
+ G_GNUC_UNUSED const GVariantType *expected_type,
+ G_GNUC_UNUSED gpointer data)
+{
+ return g_variant_new_byte((char)g_value_get_double(value));
+}
+
+static gboolean
+gplugin_gtk_plugin_settings_row_enum_to_selected(
+ GValue *value,
+ GVariant *variant,
+ gpointer data)
+{
+ GListModel *model = data;
+ const char *string = NULL;
+ guint n_items;
+
+ string = g_variant_get_string(variant, NULL);
+ n_items = g_list_model_get_n_items(model);
+ for(guint index = 0; index < n_items; index++) {
+ GtkStringObject *obj = g_list_model_get_item(model, index);
+
+ if(g_str_equal(string, gtk_string_object_get_string(obj))) {
+ g_value_set_uint(value, index);
+ g_object_unref(obj);
+ return TRUE;
+ }
+
+ g_object_unref(obj);
+ }
+
+ return FALSE;
+}
+
+static GVariant *
+gplugin_gtk_plugin_settings_row_selected_to_enum(
+ const GValue *value,
+ G_GNUC_UNUSED const GVariantType *expected_type,
+ gpointer data)
+{
+ GListModel *model = data;
+ GtkStringObject *obj = NULL;
+ GVariant *result = NULL;
+
+ obj = g_list_model_get_item(model, g_value_get_uint(value));
+ result = g_variant_new_string(gtk_string_object_get_string(obj));
+ g_object_unref(obj);
+
+ return result;
+}
+
+/******************************************************************************
* Helpers
*****************************************************************************/
static GtkWidget *
+gplugin_gtk_plugin_settings_row_new_for_enum(
+ GSettings *settings,
+ const char *name,
+ GVariant *range_value)
+{
+ GtkWidget *dropdown = NULL;
+ const gchar **allowed_values = NULL;
+ GListModel *model = NULL;
+
+ allowed_values = g_variant_get_strv(range_value, NULL);
+ dropdown = gtk_drop_down_new_from_strings(allowed_values);
+ g_free(allowed_values);
+
+ model = gtk_drop_down_get_model(GTK_DROP_DOWN(dropdown));
+ g_settings_bind_with_mapping(
+ settings,
+ name,
+ dropdown,
+ "selected",
+ G_SETTINGS_BIND_DEFAULT,
+ gplugin_gtk_plugin_settings_row_enum_to_selected,
+ gplugin_gtk_plugin_settings_row_selected_to_enum,
+ g_object_ref(model),
+ g_object_unref);
+
+ return dropdown;
+}
+
+static GtkWidget *
+gplugin_gtk_plugin_settings_row_new_for_flags(
+ G_GNUC_UNUSED GSettings *settings,
+ G_GNUC_UNUSED const char *name,
+ G_GNUC_UNUSED GVariant *range_value)
+{
+ GtkWidget *dropdown = NULL;
+
+ /* TODO: Implement this. */
+ dropdown = gtk_label_new("Unimplemented flags setting");
+
+ return dropdown;
+}
+
+static GtkWidget *
+gplugin_gtk_plugin_settings_row_new_for_range(
+ GSettings *settings,
+ const char *name,
+ GVariant *range_value)
+{
+ GtkWidget *spin = NULL;
+ const char *type = NULL;
+ gdouble min, max, step;
+
+ /* By default, for integers, pick a step of 1, which in GtkSpinButton
+ * limits it to displaying only integers. */
+ step = 1.0;
+
+ /* The range value is a tuple with a pair of the type of the expected value
+ * (e.g., "(ii)" for int16 or "(dd)" for double), so look at second
+ * character. */
+ type = g_variant_get_type_string(range_value);
+ switch(type[1]) {
+ case 'y': {
+ guint8 mini, maxi;
+ g_variant_get_child(range_value, 0, "y", &mini);
+ g_variant_get_child(range_value, 1, "y", &maxi);
+ min = (gdouble)mini;
+ max = (gdouble)maxi;
+ } break;
+
+ case 'n': {
+ gint16 mini, maxi;
+ g_variant_get_child(range_value, 0, "n", &mini);
+ g_variant_get_child(range_value, 1, "n", &maxi);
+ min = (gdouble)mini;
+ max = (gdouble)maxi;
+ } break;
+
+ case 'q': {
+ guint16 mini, maxi;
+ g_variant_get_child(range_value, 0, "q", &mini);
+ g_variant_get_child(range_value, 1, "q", &maxi);
+ min = (gdouble)mini;
+ max = (gdouble)maxi;
+ } break;
+
+ case 'i': {
+ gint32 mini, maxi;
+ g_variant_get_child(range_value, 0, "i", &mini);
+ g_variant_get_child(range_value, 1, "i", &maxi);
+ min = (gdouble)mini;
+ max = (gdouble)maxi;
+ } break;
+
+ case 'u': {
+ guint32 mini, maxi;
+ g_variant_get_child(range_value, 0, "u", &mini);
+ g_variant_get_child(range_value, 1, "u", &maxi);
+ min = (gdouble)mini;
+ max = (gdouble)maxi;
+ } break;
+
+ case 'x': {
+ gint64 mini, maxi;
+ g_variant_get_child(range_value, 0, "x", &mini);
+ g_variant_get_child(range_value, 1, "x", &maxi);
+ min = (gdouble)mini;
+ max = (gdouble)maxi;
+ } break;
+
+ case 't': {
+ guint64 mini, maxi;
+ g_variant_get_child(range_value, 0, "t", &mini);
+ g_variant_get_child(range_value, 1, "t", &maxi);
+ min = (gdouble)mini;
+ max = (gdouble)maxi;
+ } break;
+
+ case 'd':
+ /* For doubles, arbitrarily pick a step of 1% of the range. */
+ g_variant_get_child(range_value, 0, "d", &min);
+ g_variant_get_child(range_value, 1, "d", &max);
+ step = (max - min) / 100;
+ break;
+
+ default:
+ g_warning("Unknown range type: %s", type);
+ min = 0.0;
+ max = 1.0;
+ break;
+ }
+
+ spin = gtk_spin_button_new_with_range(min, max, step);
+ if(type[1] == 'y') {
+ /* For some reason, Gio's default bindings understand
+ * double-to-int conversions for any bit size other than
+ * bytes, so manually set up mappings for bytes... */
+ g_settings_bind_with_mapping(
+ settings,
+ name,
+ spin,
+ "value",
+ G_SETTINGS_BIND_DEFAULT,
+ gplugin_gtk_plugin_settings_row_byte_to_double,
+ gplugin_gtk_plugin_settings_row_double_to_byte,
+ NULL,
+ NULL);
+ } else {
+ /* ... and use default binding everywhere else. */
+ g_settings_bind(settings, name, spin, "value", G_SETTINGS_BIND_DEFAULT);
+ }
+
+ return spin;
+}
+
+static GtkWidget *
+gplugin_gtk_plugin_settings_row_new_for_type(
+ GSettings *settings,
+ const char *name,
+ GVariant *range_value)
+{
+ GtkWidget *entry = NULL;
+ char type;
+ gdouble min, max;
+
+ /* The range value is an array with type of the expected value (e.g., "ab"
+ * for boolean or "as" for string), so look at second character. */
+ type = g_variant_get_type_string(range_value)[1];
+
+ switch(type) {
+ /* Boolean */
+ case 'b':
+ entry = gtk_switch_new();
+ gtk_widget_set_halign(entry, GTK_ALIGN_END);
+ gtk_widget_set_valign(entry, GTK_ALIGN_CENTER);
+
+ g_settings_bind(
+ settings,
+ name,
+ entry,
+ "active",
+ G_SETTINGS_BIND_DEFAULT);
+ break;
+
+ /* Integral numbers */
+ case 'y':
+ case 'n':
+ case 'q':
+ case 'i':
+ case 'u':
+ case 'x':
+ case 't':
+ /* Set range for the spin button. */
+ switch(type) {
+ case 'y':
+ min = 0.0;
+ max = G_MAXUINT8;
+ break;
+ case 'n':
+ min = G_MININT16;
+ max = G_MAXINT16;
+ break;
+ case 'q':
+ min = 0.0;
+ max = G_MAXUINT16;
+ break;
+ case 'i':
+ min = G_MININT32;
+ max = G_MAXINT32;
+ break;
+ case 'u':
+ min = 0.0;
+ max = G_MAXUINT32;
+ break;
+ case 'x':
+ min = G_MININT64;
+ max = (gdouble)G_MAXINT64;
+ break;
+ case 't':
+ min = 0.0;
+ max = (gdouble)G_MAXUINT64;
+ break;
+ }
+
+ entry = gtk_spin_button_new_with_range(min, max, 1.0);
+ if(type == 'y') {
+ /* For some reason, Gio's default bindings understand
+ * double-to-int conversions for any bit size other than
+ * bytes, so manually set up mappings for bytes... */
+ g_settings_bind_with_mapping(
+ settings,
+ name,
+ entry,
+ "value",
+ G_SETTINGS_BIND_DEFAULT,
+ gplugin_gtk_plugin_settings_row_byte_to_double,
+ gplugin_gtk_plugin_settings_row_double_to_byte,
+ NULL,
+ NULL);
+ } else {
+ /* ... and use default binding everywhere else. */
+ g_settings_bind(
+ settings,
+ name,
+ entry,
+ "value",
+ G_SETTINGS_BIND_DEFAULT);
+ }
+
+ break;
+
+ /* Double numbers */
+ case 'd':
+ entry = gtk_spin_button_new(NULL, 1.0, 5);
+ g_settings_bind(
+ settings,
+ name,
+ entry,
+ "value",
+ G_SETTINGS_BIND_DEFAULT);
+ break;
+
+ /* Strings */
+ case 's':
+ entry = gtk_entry_new();
+ g_settings_bind(
+ settings,
+ name,
+ gtk_entry_get_buffer(GTK_ENTRY(entry)),
+ "text",
+ G_SETTINGS_BIND_DEFAULT);
+ break;
+
+ default:
+ entry = gtk_label_new(_("Unknown setting type"));
+ break;
+ }
+
+ return entry;
+}
+
+static GtkWidget *
gplugin_gtk_plugin_settings_row_new_for_key(
GSettings *settings,
const gchar *name,
- GSettingsSchemaKey *key)
+ GSettingsSchemaKey *key,
+ GtkSizeGroup *sg)
{
- /* TODO: Make real widgets. */
GtkWidget *widget = NULL;
GtkWidget *label = NULL;
GtkWidget *entry = NULL;
- GVariant *value = NULL;
- char *value_str = NULL;
+ const char *summary = NULL;
+ GVariant *range = NULL;
+ gchar *range_type = NULL;
+ GVariant *range_value = NULL;
widget = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12);
- label = gtk_label_new(g_settings_schema_key_get_name(key));
+ summary = g_settings_schema_key_get_summary(key);
+ label = gtk_label_new((summary != NULL) ? summary : name);
gtk_label_set_xalign(GTK_LABEL(label), 0);
gtk_box_append(GTK_BOX(widget), label);
+ gtk_size_group_add_widget(sg, label);
- value = g_settings_get_value(settings, name);
- value_str = g_variant_print(value, TRUE);
- g_variant_unref(value);
+ gtk_widget_set_tooltip_text(
+ widget,
+ g_settings_schema_key_get_description(key));
+
+ range = g_settings_schema_key_get_range(key);
+ g_variant_get_child(range, 0, "s", &range_type);
+ g_variant_get_child(range, 1, "v", &range_value);
- entry = gtk_entry_new();
- gtk_editable_set_text(GTK_EDITABLE(entry), value_str);
- gtk_editable_set_editable(GTK_EDITABLE(entry), FALSE);
+ if(g_str_equal(range_type, "enum")) {
+ entry = gplugin_gtk_plugin_settings_row_new_for_enum(
+ settings,
+ name,
+ range_value);
+ } else if(g_str_equal(range_type, "flags")) {
+ entry = gplugin_gtk_plugin_settings_row_new_for_flags(
+ settings,
+ name,
+ range_value);
+ } else if(g_str_equal(range_type, "range")) {
+ entry = gplugin_gtk_plugin_settings_row_new_for_range(
+ settings,
+ name,
+ range_value);
+ } else if(g_str_equal(range_type, "type")) {
+ entry = gplugin_gtk_plugin_settings_row_new_for_type(
+ settings,
+ name,
+ range_value);
+ } else {
+ /* No other documented types for g_settings_schema_key_get_range. */
+ g_warn_if_reached();
+ entry = gtk_label_new(_("Unknown setting type"));
+ }
+
+ g_variant_unref(range_value);
+ g_free(range_type);
+ g_variant_unref(range);
+
gtk_widget_set_hexpand(entry, TRUE);
gtk_box_append(GTK_BOX(widget), entry);
- g_free(value_str);
-
return widget;
}
@@ -94,6 +476,7 @@ gplugin_gtk_plugin_settings_list_refresh
{
GSettingsSchema *schema = NULL;
gchar **names = NULL;
+ GtkSizeGroup *sg = NULL;
while(list->rows) {
gtk_list_box_remove(list->list_box, list->rows->data);
@@ -106,6 +489,7 @@ gplugin_gtk_plugin_settings_list_refresh
g_object_get(G_OBJECT(settings), "settings-schema", &schema, NULL);
names = g_settings_schema_list_keys(schema);
+ sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
for(gint i = 0; names[i] != NULL; i++) {
GSettingsSchemaKey *key = NULL;
@@ -116,7 +500,8 @@ gplugin_gtk_plugin_settings_list_refresh
widget = gplugin_gtk_plugin_settings_row_new_for_key(
settings,
names[i],
- key);
+ key,
+ sg);
row = gtk_list_box_row_new();
gtk_list_box_row_set_child(GTK_LIST_BOX_ROW(row), widget);