diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 14:36:24 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 14:36:24 +0000 |
commit | 9b6d8e63db85c30007b463e91f91a791969fa83f (patch) | |
tree | 0899af51d73c1bf986f73ae39a03c4436083018a /subprojects/libhandy/src/hdy-preferences-window.c | |
parent | Initial commit. (diff) | |
download | gnome-control-center-upstream.tar.xz gnome-control-center-upstream.zip |
Adding upstream version 1:3.38.4.upstream/1%3.38.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'subprojects/libhandy/src/hdy-preferences-window.c')
-rw-r--r-- | subprojects/libhandy/src/hdy-preferences-window.c | 721 |
1 files changed, 721 insertions, 0 deletions
diff --git a/subprojects/libhandy/src/hdy-preferences-window.c b/subprojects/libhandy/src/hdy-preferences-window.c new file mode 100644 index 0000000..3618d58 --- /dev/null +++ b/subprojects/libhandy/src/hdy-preferences-window.c @@ -0,0 +1,721 @@ +/* + * Copyright (C) 2019 Purism SPC + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" +#include <glib/gi18n-lib.h> + +#include "hdy-preferences-window.h" + +#include "hdy-animation.h" +#include "hdy-action-row.h" +#include "hdy-deck.h" +#include "hdy-preferences-group-private.h" +#include "hdy-preferences-page-private.h" +#include "hdy-view-switcher.h" +#include "hdy-view-switcher-bar.h" +#include "hdy-view-switcher-title.h" + +/** + * SECTION:hdy-preferences-window + * @short_description: A window to present an application's preferences. + * @Title: HdyPreferencesWindow + * + * The #HdyPreferencesWindow widget presents an application's preferences + * gathered into pages and groups. The preferences are searchable by the user. + * + * Since: 0.0.10 + */ + +typedef struct +{ + HdyDeck *subpages_deck; + GtkWidget *preferences; + GtkStack *content_stack; + GtkStack *pages_stack; + GtkToggleButton *search_button; + GtkSearchEntry *search_entry; + GtkListBox *search_results; + GtkStack *search_stack; + GtkStack *title_stack; + HdyViewSwitcherBar *view_switcher_bar; + HdyViewSwitcherTitle *view_switcher_title; + + gboolean search_enabled; + gboolean can_swipe_back; + gint n_last_search_results; + GtkWidget *subpage; +} HdyPreferencesWindowPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (HdyPreferencesWindow, hdy_preferences_window, HDY_TYPE_WINDOW) + +enum { + PROP_0, + PROP_SEARCH_ENABLED, + PROP_CAN_SWIPE_BACK, + LAST_PROP, +}; + +static GParamSpec *props[LAST_PROP]; + +static gboolean +filter_search_results (HdyActionRow *row, + HdyPreferencesWindow *self) +{ + HdyPreferencesWindowPrivate *priv = hdy_preferences_window_get_instance_private (self); + g_autofree gchar *text = g_utf8_casefold (gtk_entry_get_text (GTK_ENTRY (priv->search_entry)), -1); + g_autofree gchar *title = g_utf8_casefold (hdy_preferences_row_get_title (HDY_PREFERENCES_ROW (row)), -1); + g_autofree gchar *subtitle = NULL; + + /* The CSS engine works in such a way that invisible children are treated as + * visible widgets, which breaks the expectations of the .preferences style + * class when filtering a row, leading to straight corners when the first row + * or last row are filtered out. + * + * This works around it by explicitly toggling the row's visibility, while + * keeping GtkListBox's filtering logic. + * + * See https://gitlab.gnome.org/GNOME/libhandy/-/merge_requests/424 + */ + + if (strstr (title, text)) { + priv->n_last_search_results++; + gtk_widget_show (GTK_WIDGET (row)); + + return TRUE; + } + + subtitle = g_utf8_casefold (hdy_action_row_get_subtitle (row), -1); + + if (!!strstr (subtitle, text)) { + priv->n_last_search_results++; + gtk_widget_show (GTK_WIDGET (row)); + + return TRUE; + } + + gtk_widget_hide (GTK_WIDGET (row)); + + return FALSE; +} + +static GtkWidget * +new_search_row_for_preference (HdyPreferencesRow *row, + HdyPreferencesWindow *self) +{ + HdyPreferencesWindowPrivate *priv = hdy_preferences_window_get_instance_private (self); + HdyActionRow *widget; + HdyPreferencesGroup *group; + HdyPreferencesPage *page; + const gchar *group_title, *page_title; + GtkWidget *parent; + + g_assert (HDY_IS_PREFERENCES_ROW (row)); + + widget = HDY_ACTION_ROW (hdy_action_row_new ()); + gtk_list_box_row_set_activatable (GTK_LIST_BOX_ROW (widget), TRUE); + g_object_bind_property (row, "title", widget, "title", G_BINDING_SYNC_CREATE); + g_object_bind_property (row, "use-underline", widget, "use-underline", G_BINDING_SYNC_CREATE); + + for (parent = gtk_widget_get_parent (GTK_WIDGET (row)); + parent != NULL && !HDY_IS_PREFERENCES_GROUP (parent); + parent = gtk_widget_get_parent (parent)); + group = parent != NULL ? HDY_PREFERENCES_GROUP (parent) : NULL; + group_title = group != NULL ? hdy_preferences_group_get_title (group) : NULL; + if (g_strcmp0 (group_title, "") == 0) + group_title = NULL; + + for (parent = gtk_widget_get_parent (GTK_WIDGET (group)); + parent != NULL && !HDY_IS_PREFERENCES_PAGE (parent); + parent = gtk_widget_get_parent (parent)); + page = parent != NULL ? HDY_PREFERENCES_PAGE (parent) : NULL; + page_title = page != NULL ? hdy_preferences_page_get_title (page) : NULL; + if (g_strcmp0 (page_title, "") == 0) + page_title = NULL; + + if (group_title && !hdy_view_switcher_title_get_title_visible (priv->view_switcher_title)) + hdy_action_row_set_subtitle (widget, group_title); + if (group_title) { + g_autofree gchar *subtitle = g_strdup_printf ("%s → %s", page_title != NULL ? page_title : _("Untitled page"), group_title); + hdy_action_row_set_subtitle (widget, subtitle); + } else if (page_title) + hdy_action_row_set_subtitle (widget, page_title); + + gtk_widget_show (GTK_WIDGET (widget)); + + g_object_set_data (G_OBJECT (widget), "page", page); + g_object_set_data (G_OBJECT (widget), "row", row); + + return GTK_WIDGET (widget); +} + +static void +update_search_results (HdyPreferencesWindow *self) +{ + HdyPreferencesWindowPrivate *priv = hdy_preferences_window_get_instance_private (self); + g_autoptr (GListStore) model; + + model = g_list_store_new (HDY_TYPE_PREFERENCES_ROW); + gtk_container_foreach (GTK_CONTAINER (priv->pages_stack), (GtkCallback) hdy_preferences_page_add_preferences_to_model, model); + gtk_container_foreach (GTK_CONTAINER (priv->search_results), (GtkCallback) gtk_widget_destroy, NULL); + for (guint i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (model)); i++) + gtk_container_add (GTK_CONTAINER (priv->search_results), + new_search_row_for_preference ((HdyPreferencesRow *) g_list_model_get_item (G_LIST_MODEL (model), i), self)); +} + +static void +search_result_activated_cb (HdyPreferencesWindow *self, + HdyActionRow *widget) +{ + HdyPreferencesWindowPrivate *priv = hdy_preferences_window_get_instance_private (self); + HdyPreferencesPage *page; + HdyPreferencesRow *row; + GtkAdjustment *adjustment; + GtkAllocation allocation; + gint y = 0; + + gtk_toggle_button_set_active (priv->search_button, FALSE); + page = HDY_PREFERENCES_PAGE (g_object_get_data (G_OBJECT (widget), "page")); + row = HDY_PREFERENCES_ROW (g_object_get_data (G_OBJECT (widget), "row")); + + g_assert (page != NULL); + g_assert (row != NULL); + + adjustment = hdy_preferences_page_get_vadjustment (page); + + g_assert (adjustment != NULL); + + gtk_stack_set_visible_child (priv->pages_stack, GTK_WIDGET (page)); + gtk_widget_set_can_focus (GTK_WIDGET (row), TRUE); + gtk_widget_grab_focus (GTK_WIDGET (row)); + + if (!gtk_widget_translate_coordinates (GTK_WIDGET (row), GTK_WIDGET (page), 0, 0, NULL, &y)) + return; + + gtk_container_set_focus_child (GTK_CONTAINER (page), GTK_WIDGET (row)); + y += gtk_adjustment_get_value (adjustment); + gtk_widget_get_allocation (GTK_WIDGET (row), &allocation); + gtk_adjustment_clamp_page (adjustment, y, y + allocation.height); +} + +static gboolean +key_press_event_cb (GtkWidget *sender, + GdkEvent *event, + HdyPreferencesWindow *self) +{ + HdyPreferencesWindowPrivate *priv = hdy_preferences_window_get_instance_private (self); + GdkModifierType default_modifiers = gtk_accelerator_get_default_mod_mask (); + guint keyval; + GdkModifierType state; + + if (priv->subpage) + return GDK_EVENT_PROPAGATE; + + gdk_event_get_keyval (event, &keyval); + gdk_event_get_state (event, &state); + + if (priv->search_enabled && + (keyval == GDK_KEY_f || keyval == GDK_KEY_F) && + (state & default_modifiers) == GDK_CONTROL_MASK) { + gtk_toggle_button_set_active (priv->search_button, TRUE); + + return GDK_EVENT_STOP; + } + + if (priv->search_enabled && + gtk_search_entry_handle_event (priv->search_entry, event)) { + gtk_toggle_button_set_active (priv->search_button, TRUE); + + return GDK_EVENT_STOP; + } + + return GDK_EVENT_PROPAGATE; +} + +static void +try_remove_subpages (HdyPreferencesWindow *self) +{ + HdyPreferencesWindowPrivate *priv = hdy_preferences_window_get_instance_private (self); + + if (hdy_deck_get_transition_running (priv->subpages_deck)) + return; + + if (hdy_deck_get_visible_child (priv->subpages_deck) == priv->preferences) + priv->subpage = NULL; + + for (GList *child = gtk_container_get_children (GTK_CONTAINER (priv->subpages_deck)); + child; + child = child->next) + if (child->data != priv->preferences && child->data != priv->subpage) + gtk_container_remove (GTK_CONTAINER (priv->subpages_deck), child->data); +} + +static void +subpages_deck_transition_running_cb (HdyPreferencesWindow *self) +{ + try_remove_subpages (self); +} + +static void +subpages_deck_visible_child_cb (HdyPreferencesWindow *self) +{ + try_remove_subpages (self); +} + +static void +header_bar_size_allocate_cb (HdyPreferencesWindow *self, + GdkRectangle *allocation) +{ + HdyPreferencesWindowPrivate *priv = hdy_preferences_window_get_instance_private (self); + + hdy_view_switcher_title_set_view_switcher_enabled (priv->view_switcher_title, allocation->width > 360); +} + +static void +title_stack_notify_transition_running_cb (HdyPreferencesWindow *self) +{ + HdyPreferencesWindowPrivate *priv = hdy_preferences_window_get_instance_private (self); + + if (gtk_stack_get_transition_running (priv->title_stack) || + gtk_stack_get_visible_child (priv->title_stack) != GTK_WIDGET (priv->view_switcher_title)) + return; + + gtk_entry_set_text (GTK_ENTRY (priv->search_entry), ""); +} + +static void +title_stack_notify_visible_child_cb (HdyPreferencesWindow *self) +{ + HdyPreferencesWindowPrivate *priv = hdy_preferences_window_get_instance_private (self); + + if (hdy_get_enable_animations (GTK_WIDGET (priv->title_stack)) || + gtk_stack_get_visible_child (priv->title_stack) != GTK_WIDGET (priv->view_switcher_title)) + return; + + gtk_entry_set_text (GTK_ENTRY (priv->search_entry), ""); +} + + +static void +search_button_notify_active_cb (HdyPreferencesWindow *self) +{ + HdyPreferencesWindowPrivate *priv = hdy_preferences_window_get_instance_private (self); + + if (gtk_toggle_button_get_active (priv->search_button)) { + update_search_results (self); + gtk_stack_set_visible_child_name (priv->title_stack, "search"); + gtk_stack_set_visible_child_name (priv->content_stack, "search"); + gtk_entry_grab_focus_without_selecting (GTK_ENTRY (priv->search_entry)); + /* Grabbing without selecting puts the cursor at the start of the buffer, so + * for "type to search" to work we must move the cursor at the end. We can't + * use GTK_MOVEMENT_BUFFER_ENDS because it causes a sound to be played. + */ + g_signal_emit_by_name (priv->search_entry, "move-cursor", + GTK_MOVEMENT_LOGICAL_POSITIONS, G_MAXINT, FALSE, NULL); + } else { + gtk_stack_set_visible_child_name (priv->title_stack, "pages"); + gtk_stack_set_visible_child_name (priv->content_stack, "pages"); + } +} + +static void +search_changed_cb (HdyPreferencesWindow *self) +{ + HdyPreferencesWindowPrivate *priv = hdy_preferences_window_get_instance_private (self); + + priv->n_last_search_results = 0; + gtk_list_box_invalidate_filter (priv->search_results); + gtk_stack_set_visible_child_name (priv->search_stack, + priv->n_last_search_results > 0 ? "results" : "no-results"); +} + +static void +on_page_icon_name_changed (HdyPreferencesPage *page, + GParamSpec *pspec, + HdyPreferencesWindow *self) +{ + HdyPreferencesWindowPrivate *priv = hdy_preferences_window_get_instance_private (self); + + gtk_container_child_set (GTK_CONTAINER (priv->pages_stack), GTK_WIDGET (page), + "icon-name", hdy_preferences_page_get_icon_name (page), + NULL); +} + +static void +stop_search_cb (HdyPreferencesWindow *self) +{ + HdyPreferencesWindowPrivate *priv = hdy_preferences_window_get_instance_private (self); + + gtk_toggle_button_set_active (priv->search_button, FALSE); +} + +static void +on_page_title_changed (HdyPreferencesPage *page, + GParamSpec *pspec, + HdyPreferencesWindow *self) +{ + HdyPreferencesWindowPrivate *priv = hdy_preferences_window_get_instance_private (self); + + gtk_container_child_set (GTK_CONTAINER (priv->pages_stack), GTK_WIDGET (page), + "title", hdy_preferences_page_get_title (page), + NULL); +} + +static void +hdy_preferences_window_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + HdyPreferencesWindow *self = HDY_PREFERENCES_WINDOW (object); + + switch (prop_id) { + case PROP_SEARCH_ENABLED: + g_value_set_boolean (value, hdy_preferences_window_get_search_enabled (self)); + break; + case PROP_CAN_SWIPE_BACK: + g_value_set_boolean (value, hdy_preferences_window_get_can_swipe_back (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +hdy_preferences_window_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + HdyPreferencesWindow *self = HDY_PREFERENCES_WINDOW (object); + + switch (prop_id) { + case PROP_SEARCH_ENABLED: + hdy_preferences_window_set_search_enabled (self, g_value_get_boolean (value)); + break; + case PROP_CAN_SWIPE_BACK: + hdy_preferences_window_set_can_swipe_back (self, g_value_get_boolean (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +hdy_preferences_window_add (GtkContainer *container, + GtkWidget *child) +{ + HdyPreferencesWindow *self = HDY_PREFERENCES_WINDOW (container); + HdyPreferencesWindowPrivate *priv = hdy_preferences_window_get_instance_private (self); + + if (priv->content_stack == NULL) + GTK_CONTAINER_CLASS (hdy_preferences_window_parent_class)->add (container, child); + else if (HDY_IS_PREFERENCES_PAGE (child)) { + gtk_container_add (GTK_CONTAINER (priv->pages_stack), child); + on_page_icon_name_changed (HDY_PREFERENCES_PAGE (child), NULL, self); + on_page_title_changed (HDY_PREFERENCES_PAGE (child), NULL, self); + g_signal_connect (child, "notify::icon-name", + G_CALLBACK (on_page_icon_name_changed), self); + g_signal_connect (child, "notify::title", + G_CALLBACK (on_page_title_changed), self); + } else + g_warning ("Can't add children of type %s to %s", + G_OBJECT_TYPE_NAME (child), + G_OBJECT_TYPE_NAME (container)); +} + +static void +hdy_preferences_window_remove (GtkContainer *container, + GtkWidget *child) +{ + HdyPreferencesWindow *self = HDY_PREFERENCES_WINDOW (container); + HdyPreferencesWindowPrivate *priv = hdy_preferences_window_get_instance_private (self); + + if (child == GTK_WIDGET (priv->content_stack)) + GTK_CONTAINER_CLASS (hdy_preferences_window_parent_class)->remove (container, child); + else + gtk_container_remove (GTK_CONTAINER (priv->pages_stack), child); +} + +static void +hdy_preferences_window_forall (GtkContainer *container, + gboolean include_internals, + GtkCallback callback, + gpointer callback_data) +{ + HdyPreferencesWindow *self = HDY_PREFERENCES_WINDOW (container); + HdyPreferencesWindowPrivate *priv = hdy_preferences_window_get_instance_private (self); + + if (include_internals) + GTK_CONTAINER_CLASS (hdy_preferences_window_parent_class)->forall (container, + include_internals, + callback, + callback_data); + else if (priv->pages_stack) + gtk_container_foreach (GTK_CONTAINER (priv->pages_stack), callback, callback_data); +} + +static void +hdy_preferences_window_class_init (HdyPreferencesWindowClass *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_window_get_property; + object_class->set_property = hdy_preferences_window_set_property; + + container_class->add = hdy_preferences_window_add; + container_class->remove = hdy_preferences_window_remove; + container_class->forall = hdy_preferences_window_forall; + + /** + * HdyPreferencesWindow:search-enabled: + * + * Whether search is enabled. + * + * Since: 1.0 + */ + props[PROP_SEARCH_ENABLED] = + g_param_spec_boolean ("search-enabled", + _("Search enabled"), + _("Whether search is enabled"), + TRUE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + + /** + * HdyPreferencesWindow:can-swipe-back: + * + * Whether or not the window allows closing the subpage via a swipe gesture. + * + * Since: 1.0 + */ + props[PROP_CAN_SWIPE_BACK] = + g_param_spec_boolean ("can-swipe-back", + _("Can swipe back"), + _("Whether or not swipe gesture can be used to switch from a subpage to the preferences"), + FALSE, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY); + + g_object_class_install_properties (object_class, LAST_PROP, props); + + gtk_widget_class_set_template_from_resource (widget_class, + "/sm/puri/handy/ui/hdy-preferences-window.ui"); + gtk_widget_class_bind_template_child_private (widget_class, HdyPreferencesWindow, subpages_deck); + gtk_widget_class_bind_template_child_private (widget_class, HdyPreferencesWindow, preferences); + gtk_widget_class_bind_template_child_private (widget_class, HdyPreferencesWindow, content_stack); + gtk_widget_class_bind_template_child_private (widget_class, HdyPreferencesWindow, pages_stack); + gtk_widget_class_bind_template_child_private (widget_class, HdyPreferencesWindow, search_button); + gtk_widget_class_bind_template_child_private (widget_class, HdyPreferencesWindow, search_entry); + gtk_widget_class_bind_template_child_private (widget_class, HdyPreferencesWindow, search_results); + gtk_widget_class_bind_template_child_private (widget_class, HdyPreferencesWindow, search_stack); + gtk_widget_class_bind_template_child_private (widget_class, HdyPreferencesWindow, title_stack); + gtk_widget_class_bind_template_child_private (widget_class, HdyPreferencesWindow, view_switcher_bar); + gtk_widget_class_bind_template_child_private (widget_class, HdyPreferencesWindow, view_switcher_title); + gtk_widget_class_bind_template_callback (widget_class, subpages_deck_transition_running_cb); + gtk_widget_class_bind_template_callback (widget_class, subpages_deck_visible_child_cb); + gtk_widget_class_bind_template_callback (widget_class, header_bar_size_allocate_cb); + gtk_widget_class_bind_template_callback (widget_class, title_stack_notify_transition_running_cb); + gtk_widget_class_bind_template_callback (widget_class, title_stack_notify_visible_child_cb); + gtk_widget_class_bind_template_callback (widget_class, key_press_event_cb); + gtk_widget_class_bind_template_callback (widget_class, search_button_notify_active_cb); + gtk_widget_class_bind_template_callback (widget_class, search_changed_cb); + gtk_widget_class_bind_template_callback (widget_class, search_result_activated_cb); + gtk_widget_class_bind_template_callback (widget_class, stop_search_cb); +} + +static void +hdy_preferences_window_init (HdyPreferencesWindow *self) +{ + HdyPreferencesWindowPrivate *priv = hdy_preferences_window_get_instance_private (self); + + priv->search_enabled = TRUE; + + gtk_widget_init_template (GTK_WIDGET (self)); + + gtk_list_box_set_filter_func (priv->search_results, (GtkListBoxFilterFunc) filter_search_results, self, NULL); +} + +/** + * hdy_preferences_window_new: + * + * Creates a new #HdyPreferencesWindow. + * + * Returns: a new #HdyPreferencesWindow + * + * Since: 0.0.10 + */ +GtkWidget * +hdy_preferences_window_new (void) +{ + return g_object_new (HDY_TYPE_PREFERENCES_WINDOW, NULL); +} + +/** + * hdy_preferences_window_get_search_enabled: + * @self: a #HdyPreferencesWindow + * + * Gets whether search is enabled for @self. + * + * Returns: whether search is enabled for @self. + * + * Since: 1.0 + */ +gboolean +hdy_preferences_window_get_search_enabled (HdyPreferencesWindow *self) +{ + HdyPreferencesWindowPrivate *priv; + + g_return_val_if_fail (HDY_IS_PREFERENCES_WINDOW (self), FALSE); + + priv = hdy_preferences_window_get_instance_private (self); + + return priv->search_enabled; +} + +/** + * hdy_preferences_window_set_search_enabled: + * @self: a #HdyPreferencesWindow + * @search_enabled: %TRUE to enable search, %FALSE to disable it + * + * Sets whether search is enabled for @self. + * + * Since: 1.0 + */ +void +hdy_preferences_window_set_search_enabled (HdyPreferencesWindow *self, + gboolean search_enabled) +{ + HdyPreferencesWindowPrivate *priv; + + g_return_if_fail (HDY_IS_PREFERENCES_WINDOW (self)); + + priv = hdy_preferences_window_get_instance_private (self); + + search_enabled = !!search_enabled; + + if (priv->search_enabled == search_enabled) + return; + + priv->search_enabled = search_enabled; + gtk_widget_set_visible (GTK_WIDGET (priv->search_button), search_enabled); + if (!search_enabled) + gtk_toggle_button_set_active (priv->search_button, FALSE); + + g_object_notify_by_pspec (G_OBJECT (self), props[PROP_SEARCH_ENABLED]); +} + +/** + * hdy_preferences_window_set_can_swipe_back: + * @self: a #HdyPreferencesWindow + * @can_swipe_back: the new value + * + * Sets whether or not @self allows switching from a subpage to the preferences + * via a swipe gesture. + * + * Since: 1.0 + */ +void +hdy_preferences_window_set_can_swipe_back (HdyPreferencesWindow *self, + gboolean can_swipe_back) +{ + HdyPreferencesWindowPrivate *priv; + + g_return_if_fail (HDY_IS_PREFERENCES_WINDOW (self)); + + priv = hdy_preferences_window_get_instance_private (self); + + can_swipe_back = !!can_swipe_back; + + if (priv->can_swipe_back == can_swipe_back) + return; + + priv->can_swipe_back = can_swipe_back; + + g_object_notify_by_pspec (G_OBJECT (self), props[PROP_CAN_SWIPE_BACK]); +} + +/** + * hdy_preferences_window_get_can_swipe_back + * @self: a #HdyPreferencesWindow + * + * Returns whether or not @self allows switching from a subpage to the + * preferences via a swipe gesture. + * + * Returns: %TRUE if back swipe is enabled. + * + * Since: 1.0 + */ +gboolean +hdy_preferences_window_get_can_swipe_back (HdyPreferencesWindow *self) +{ + HdyPreferencesWindowPrivate *priv; + + g_return_val_if_fail (HDY_IS_PREFERENCES_WINDOW (self), FALSE); + + priv = hdy_preferences_window_get_instance_private (self); + + return priv->can_swipe_back; +} + +/** + * hdy_preferences_window_present_subpage: + * @self: a #HdyPreferencesWindow + * @subpage: the subpage + * + * Sets @subpage as the window's subpage and present it. + * The transition can be cancelled by the user, in which case visible child will + * change back to the previously visible child. + * + * Since: 1.0 + */ +void +hdy_preferences_window_present_subpage (HdyPreferencesWindow *self, + GtkWidget *subpage) +{ + HdyPreferencesWindowPrivate *priv; + + g_return_if_fail (HDY_IS_PREFERENCES_WINDOW (self)); + g_return_if_fail (GTK_IS_WIDGET (subpage)); + + priv = hdy_preferences_window_get_instance_private (self); + + if (priv->subpage == subpage) + return; + + priv->subpage = subpage; + + /* The check below avoids a warning when re-entering a subpage during the + * transition between the that subpage to the preferences. + */ + if (gtk_widget_get_parent (subpage) != GTK_WIDGET (priv->subpages_deck)) + gtk_container_add (GTK_CONTAINER (priv->subpages_deck), subpage); + + hdy_deck_set_visible_child (priv->subpages_deck, subpage); +} + +/** + * hdy_preferences_window_close_subpage: + * @self: a #HdyPreferencesWindow + * + * Closes the current subpage to return back to the preferences, if there is no + * presented subpage, this does nothing. + * + * Since: 1.0 + */ +void +hdy_preferences_window_close_subpage (HdyPreferencesWindow *self) +{ + HdyPreferencesWindowPrivate *priv; + + g_return_if_fail (HDY_IS_PREFERENCES_WINDOW (self)); + + priv = hdy_preferences_window_get_instance_private (self); + + if (priv->subpage == NULL) + return; + + hdy_deck_set_visible_child (priv->subpages_deck, priv->preferences); +} |