diff options
Diffstat (limited to 'subprojects/libhandy/src/hdy-preferences-group.c')
-rw-r--r-- | subprojects/libhandy/src/hdy-preferences-group.c | 449 |
1 files changed, 449 insertions, 0 deletions
diff --git a/subprojects/libhandy/src/hdy-preferences-group.c b/subprojects/libhandy/src/hdy-preferences-group.c new file mode 100644 index 0000000..8000627 --- /dev/null +++ b/subprojects/libhandy/src/hdy-preferences-group.c @@ -0,0 +1,449 @@ +/* + * Copyright (C) 2019 Purism SPC + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" +#include <glib/gi18n-lib.h> + +#include "hdy-preferences-group-private.h" + +#include "hdy-preferences-row.h" + +/** + * SECTION:hdy-preferences-group + * @short_description: A group gathering preferences rows. + * @Title: HdyPreferencesGroup + * + * A #HdyPreferencesGroup represents a group or tightly related preferences, + * which in turn are represented by HdyPreferencesRow. + * + * To summarize the role of the preferences it gathers, a group can have both a + * title and a description. The title will be used by #HdyPreferencesWindow to + * let the user look for a preference. + * + * # CSS nodes + * + * #HdyPreferencesGroup has a single CSS node with name preferencesgroup. + * + * Since: 0.0.10 + */ + +typedef struct +{ + GtkBox *box; + GtkLabel *description; + GtkListBox *listbox; + GtkBox *listbox_box; + GtkLabel *title; +} HdyPreferencesGroupPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (HdyPreferencesGroup, hdy_preferences_group, GTK_TYPE_BIN) + +enum { + PROP_0, + PROP_DESCRIPTION, + PROP_TITLE, + LAST_PROP, +}; + +static GParamSpec *props[LAST_PROP]; + +static void +update_title_visibility (HdyPreferencesGroup *self) +{ + HdyPreferencesGroupPrivate *priv = hdy_preferences_group_get_instance_private (self); + + /* Show the listbox only if it has children to avoid having showing the + * listbox as an empty frame, parasiting the look of non-GtkListBoxRow + * children. + */ + gtk_widget_set_visible (GTK_WIDGET (priv->title), + gtk_label_get_text (priv->title) != NULL && + g_strcmp0 (gtk_label_get_text (priv->title), "") != 0); +} + +static void +update_description_visibility (HdyPreferencesGroup *self) +{ + HdyPreferencesGroupPrivate *priv = hdy_preferences_group_get_instance_private (self); + + gtk_widget_set_visible (GTK_WIDGET (priv->description), + gtk_label_get_text (priv->description) != NULL && + g_strcmp0 (gtk_label_get_text (priv->description), "") != 0); +} + +static void +update_listbox_visibility (HdyPreferencesGroup *self) +{ + HdyPreferencesGroupPrivate *priv = hdy_preferences_group_get_instance_private (self); + g_autoptr(GList) children = NULL; + + /* We must wait until listob has been built and added. */ + if (priv->listbox == NULL) + return; + + children = gtk_container_get_children (GTK_CONTAINER (priv->listbox)); + + gtk_widget_set_visible (GTK_WIDGET (priv->listbox), children != NULL); +} + +typedef struct { + HdyPreferencesGroup *group; + GtkCallback callback; + gpointer callback_data; +} ForallData; + +static void +for_non_internal_child (GtkWidget *widget, + gpointer callback_data) +{ + ForallData *data = callback_data; + HdyPreferencesGroupPrivate *priv = hdy_preferences_group_get_instance_private (data->group); + + if (widget != (GtkWidget *) priv->listbox) + data->callback (widget, data->callback_data); +} + +static void +hdy_preferences_group_forall (GtkContainer *container, + gboolean include_internals, + GtkCallback callback, + gpointer callback_data) +{ + HdyPreferencesGroup *self = HDY_PREFERENCES_GROUP (container); + HdyPreferencesGroupPrivate *priv = hdy_preferences_group_get_instance_private (self); + ForallData data; + + if (include_internals) { + GTK_CONTAINER_CLASS (hdy_preferences_group_parent_class)->forall (GTK_CONTAINER (self), include_internals, callback, callback_data); + + return; + } + + data.group = self; + data.callback = callback; + data.callback_data = callback_data; + + if (priv->listbox) + GTK_CONTAINER_GET_CLASS (priv->listbox)->forall (GTK_CONTAINER (priv->listbox), include_internals, callback, callback_data); + if (priv->listbox_box) + GTK_CONTAINER_GET_CLASS (priv->listbox_box)->forall (GTK_CONTAINER (priv->listbox_box), include_internals, for_non_internal_child, &data); +} + +static void +hdy_preferences_group_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + HdyPreferencesGroup *self = HDY_PREFERENCES_GROUP (object); + + switch (prop_id) { + case PROP_DESCRIPTION: + g_value_set_string (value, hdy_preferences_group_get_description (self)); + break; + case PROP_TITLE: + g_value_set_string (value, hdy_preferences_group_get_title (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +hdy_preferences_group_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + HdyPreferencesGroup *self = HDY_PREFERENCES_GROUP (object); + + switch (prop_id) { + case PROP_DESCRIPTION: + hdy_preferences_group_set_description (self, g_value_get_string (value)); + break; + case PROP_TITLE: + hdy_preferences_group_set_title (self, g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +hdy_preferences_group_dispose (GObject *object) +{ + HdyPreferencesGroup *self = HDY_PREFERENCES_GROUP (object); + HdyPreferencesGroupPrivate *priv = hdy_preferences_group_get_instance_private (self); + + /* + * Since we overload forall(), the inherited destroy() won't work as normal. + * Remove internal widgets ourself. + */ + g_clear_pointer ((GtkWidget **) &priv->description, gtk_widget_destroy); + g_clear_pointer ((GtkWidget **) &priv->listbox, gtk_widget_destroy); + g_clear_pointer ((GtkWidget **) &priv->listbox_box, gtk_widget_destroy); + g_clear_pointer ((GtkWidget **) &priv->title, gtk_widget_destroy); + + G_OBJECT_CLASS (hdy_preferences_group_parent_class)->dispose (object); +} + +static void +hdy_preferences_group_add (GtkContainer *container, + GtkWidget *child) +{ + HdyPreferencesGroup *self = HDY_PREFERENCES_GROUP (container); + HdyPreferencesGroupPrivate *priv = hdy_preferences_group_get_instance_private (self); + + if (priv->title == NULL || priv->description == NULL || priv->listbox_box == NULL) { + GTK_CONTAINER_CLASS (hdy_preferences_group_parent_class)->add (container, child); + + return; + } + + if (HDY_IS_PREFERENCES_ROW (child)) + gtk_container_add (GTK_CONTAINER (priv->listbox), child); + else + gtk_container_add (GTK_CONTAINER (priv->listbox_box), child); +} + +static void +hdy_preferences_group_remove (GtkContainer *container, + GtkWidget *child) +{ + HdyPreferencesGroup *self = HDY_PREFERENCES_GROUP (container); + HdyPreferencesGroupPrivate *priv = hdy_preferences_group_get_instance_private (self); + + if (child == GTK_WIDGET (priv->box)) + GTK_CONTAINER_CLASS (hdy_preferences_group_parent_class)->remove (container, child); + else if (HDY_IS_PREFERENCES_ROW (child)) + gtk_container_remove (GTK_CONTAINER (priv->listbox), child); + else if (child != GTK_WIDGET (priv->listbox)) + gtk_container_remove (GTK_CONTAINER (priv->listbox_box), child); +} + +static void +hdy_preferences_group_class_init (HdyPreferencesGroupClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass); + + object_class->get_property = hdy_preferences_group_get_property; + object_class->set_property = hdy_preferences_group_set_property; + object_class->dispose = hdy_preferences_group_dispose; + + container_class->add = hdy_preferences_group_add; + container_class->remove = hdy_preferences_group_remove; + container_class->forall = hdy_preferences_group_forall; + + /** + * HdyPreferencesGroup:description: + * + * The description for this group of preferences. + * + * Since: 0.0.10 + */ + props[PROP_DESCRIPTION] = + g_param_spec_string ("description", + _("Description"), + _("Description"), + "", + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + /** + * HdyPreferencesGroup:title: + * + * The title for this group of preferences. + * + * Since: 0.0.10 + */ + props[PROP_TITLE] = + g_param_spec_string ("title", + _("Title"), + _("Title"), + "", + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, LAST_PROP, props); + + gtk_widget_class_set_css_name (widget_class, "preferencesgroup"); + gtk_widget_class_set_template_from_resource (widget_class, + "/sm/puri/handy/ui/hdy-preferences-group.ui"); + gtk_widget_class_bind_template_child_private (widget_class, HdyPreferencesGroup, box); + gtk_widget_class_bind_template_child_private (widget_class, HdyPreferencesGroup, description); + gtk_widget_class_bind_template_child_private (widget_class, HdyPreferencesGroup, listbox); + gtk_widget_class_bind_template_child_private (widget_class, HdyPreferencesGroup, listbox_box); + gtk_widget_class_bind_template_child_private (widget_class, HdyPreferencesGroup, title); + gtk_widget_class_bind_template_callback (widget_class, update_listbox_visibility); +} + +static void +hdy_preferences_group_init (HdyPreferencesGroup *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); + + update_description_visibility (self); + update_title_visibility (self); + update_listbox_visibility (self); +} + +/** + * hdy_preferences_group_new: + * + * Creates a new #HdyPreferencesGroup. + * + * Returns: a new #HdyPreferencesGroup + * + * Since: 0.0.10 + */ +GtkWidget * +hdy_preferences_group_new (void) +{ + return g_object_new (HDY_TYPE_PREFERENCES_GROUP, NULL); +} + +/** + * hdy_preferences_group_get_title: + * @self: a #HdyPreferencesGroup + * + * Gets the title of @self. + * + * Returns: the title of @self. + * + * Since: 0.0.10 + */ +const gchar * +hdy_preferences_group_get_title (HdyPreferencesGroup *self) +{ + HdyPreferencesGroupPrivate *priv; + + g_return_val_if_fail (HDY_IS_PREFERENCES_GROUP (self), NULL); + + priv = hdy_preferences_group_get_instance_private (self); + + return gtk_label_get_text (priv->title); +} + +/** + * hdy_preferences_group_set_title: + * @self: a #HdyPreferencesGroup + * @title: the title + * + * Sets the title for @self. + * + * Since: 0.0.10 + */ +void +hdy_preferences_group_set_title (HdyPreferencesGroup *self, + const gchar *title) +{ + HdyPreferencesGroupPrivate *priv; + + g_return_if_fail (HDY_IS_PREFERENCES_GROUP (self)); + + priv = hdy_preferences_group_get_instance_private (self); + + if (g_strcmp0 (gtk_label_get_label (priv->title), title) == 0) + return; + + gtk_label_set_label (priv->title, title); + update_title_visibility (self); + + g_object_notify_by_pspec (G_OBJECT (self), props[PROP_TITLE]); +} + +/** + * hdy_preferences_group_get_description: + * @self: a #HdyPreferencesGroup + * + * + * Returns: the description of @self. + * + * Since: 0.0.10 + */ +const gchar * +hdy_preferences_group_get_description (HdyPreferencesGroup *self) +{ + HdyPreferencesGroupPrivate *priv; + + g_return_val_if_fail (HDY_IS_PREFERENCES_GROUP (self), NULL); + + priv = hdy_preferences_group_get_instance_private (self); + + return gtk_label_get_text (priv->description); +} + +/** + * hdy_preferences_group_set_description: + * @self: a #HdyPreferencesGroup + * @description: the description + * + * Sets the description for @self. + * + * Since: 0.0.10 + */ +void +hdy_preferences_group_set_description (HdyPreferencesGroup *self, + const gchar *description) +{ + HdyPreferencesGroupPrivate *priv; + + g_return_if_fail (HDY_IS_PREFERENCES_GROUP (self)); + + priv = hdy_preferences_group_get_instance_private (self); + + if (g_strcmp0 (gtk_label_get_label (priv->description), description) == 0) + return; + + gtk_label_set_label (priv->description, description); + update_description_visibility (self); + + g_object_notify_by_pspec (G_OBJECT (self), props[PROP_DESCRIPTION]); +} + +static void +add_preferences_to_model (HdyPreferencesRow *row, + GListStore *model) +{ + const gchar *title; + + g_assert (HDY_IS_PREFERENCES_ROW (row)); + g_assert (G_IS_LIST_STORE (model)); + + if (!gtk_widget_get_visible (GTK_WIDGET (row))) + return; + + title = hdy_preferences_row_get_title (row); + + if (!title || !*title) + return; + + g_list_store_append (model, row); +} + +/** + * hdy_preferences_group_add_preferences_to_model: (skip) + * @self: a #HdyPreferencesGroup + * @model: the model + * + * Add preferences from @self to the model. + * + * Since: 0.0.10 + */ +void +hdy_preferences_group_add_preferences_to_model (HdyPreferencesGroup *self, + GListStore *model) +{ + HdyPreferencesGroupPrivate *priv = hdy_preferences_group_get_instance_private (self); + + g_return_if_fail (HDY_IS_PREFERENCES_GROUP (self)); + g_return_if_fail (G_IS_LIST_STORE (model)); + + if (!gtk_widget_get_visible (GTK_WIDGET (self))) + return; + + gtk_container_foreach (GTK_CONTAINER (priv->listbox), (GtkCallback) add_preferences_to_model, model); +} |