diff options
Diffstat (limited to 'subprojects/libhandy/src/hdy-view-switcher-bar.c')
-rw-r--r-- | subprojects/libhandy/src/hdy-view-switcher-bar.c | 392 |
1 files changed, 392 insertions, 0 deletions
diff --git a/subprojects/libhandy/src/hdy-view-switcher-bar.c b/subprojects/libhandy/src/hdy-view-switcher-bar.c new file mode 100644 index 0000000..111d3e6 --- /dev/null +++ b/subprojects/libhandy/src/hdy-view-switcher-bar.c @@ -0,0 +1,392 @@ +/* + * Copyright (C) 2019 Zander Brown <zbrown@gnome.org> + * Copyright (C) 2019 Purism SPC + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" +#include <glib/gi18n-lib.h> + +#include "hdy-enums.h" +#include "hdy-view-switcher-bar.h" + +/** + * SECTION:hdy-view-switcher-bar + * @short_description: A view switcher action bar. + * @title: HdyViewSwitcherBar + * @See_also: #HdyViewSwitcher, #HdyViewSwitcherTitle + * + * An action bar letting you switch between multiple views offered by a + * #GtkStack, via an #HdyViewSwitcher. It is designed to be put at the bottom of + * a window and to be revealed only on really narrow windows e.g. on mobile + * phones. It can't be revealed if there are less than two pages. + * + * You can conveniently bind the #HdyViewSwitcherBar:reveal property to + * #HdyViewSwitcherTitle:title-visible to automatically reveal the view switcher + * bar when the title label is displayed in place of the view switcher. + * + * An example of the UI definition for a common use case: + * |[ + * <object class="GtkWindow"/> + * <child type="titlebar"> + * <object class="HdyHeaderBar"> + * <property name="centering-policy">strict</property> + * <child type="title"> + * <object class="HdyViewSwitcherTitle" + * id="view_switcher_title"> + * <property name="stack">stack</property> + * </object> + * </child> + * </object> + * </child> + * <child> + * <object class="GtkBox"> + * <child> + * <object class="GtkStack" id="stack"/> + * </child> + * <child> + * <object class="HdyViewSwitcherBar"> + * <property name="stack">stack</property> + * <property name="reveal" + * bind-source="view_switcher_title" + * bind-property="title-visible" + * bind-flags="sync-create"/> + * </object> + * </child> + * </object> + * </child> + * </object> + * ]| + * + * # CSS nodes + * + * #HdyViewSwitcherBar has a single CSS node with name viewswitcherbar. + * + * Since: 0.0.10 + */ + +enum { + PROP_0, + PROP_POLICY, + PROP_STACK, + PROP_REVEAL, + LAST_PROP, +}; + +struct _HdyViewSwitcherBar +{ + GtkBin parent_instance; + + GtkActionBar *action_bar; + GtkRevealer *revealer; + HdyViewSwitcher *view_switcher; + + HdyViewSwitcherPolicy policy; + gboolean reveal; +}; + +static GParamSpec *props[LAST_PROP]; + +G_DEFINE_TYPE (HdyViewSwitcherBar, hdy_view_switcher_bar, GTK_TYPE_BIN) + +static void +count_children_cb (GtkWidget *widget, + gint *count) +{ + (*count)++; +} + +static void +update_bar_revealed (HdyViewSwitcherBar *self) { + GtkStack *stack = hdy_view_switcher_get_stack (self->view_switcher); + gint count = 0; + + if (self->reveal && stack) + gtk_container_foreach (GTK_CONTAINER (stack), (GtkCallback) count_children_cb, &count); + + gtk_revealer_set_reveal_child (self->revealer, count > 1); +} + +static void +hdy_view_switcher_bar_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + HdyViewSwitcherBar *self = HDY_VIEW_SWITCHER_BAR (object); + + switch (prop_id) { + case PROP_POLICY: + g_value_set_enum (value, hdy_view_switcher_bar_get_policy (self)); + break; + case PROP_STACK: + g_value_set_object (value, hdy_view_switcher_bar_get_stack (self)); + break; + case PROP_REVEAL: + g_value_set_boolean (value, hdy_view_switcher_bar_get_reveal (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +hdy_view_switcher_bar_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + HdyViewSwitcherBar *self = HDY_VIEW_SWITCHER_BAR (object); + + switch (prop_id) { + case PROP_POLICY: + hdy_view_switcher_bar_set_policy (self, g_value_get_enum (value)); + break; + case PROP_STACK: + hdy_view_switcher_bar_set_stack (self, g_value_get_object (value)); + break; + case PROP_REVEAL: + hdy_view_switcher_bar_set_reveal (self, g_value_get_boolean (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +hdy_view_switcher_bar_class_init (HdyViewSwitcherBarClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->get_property = hdy_view_switcher_bar_get_property; + object_class->set_property = hdy_view_switcher_bar_set_property; + + /** + * HdyViewSwitcherBar:policy: + * + * The #HdyViewSwitcherPolicy the #HdyViewSwitcher should use to determine + * which mode to use. + * + * Since: 0.0.10 + */ + props[PROP_POLICY] = + g_param_spec_enum ("policy", + _("Policy"), + _("The policy to determine the mode to use"), + HDY_TYPE_VIEW_SWITCHER_POLICY, HDY_VIEW_SWITCHER_POLICY_NARROW, + G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + /** + * HdyViewSwitcherBar:stack: + * + * The #GtkStack the #HdyViewSwitcher controls. + * + * Since: 0.0.10 + */ + props[PROP_STACK] = + g_param_spec_object ("stack", + _("Stack"), + _("Stack"), + GTK_TYPE_STACK, + G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + /** + * HdyViewSwitcherBar:reveal: + * + * Whether the bar should be revealed or hidden. + * + * Since: 0.0.10 + */ + props[PROP_REVEAL] = + g_param_spec_boolean ("reveal", + _("Reveal"), + _("Whether the view switcher is revealed"), + FALSE, + G_PARAM_EXPLICIT_NOTIFY | 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, "viewswitcherbar"); + + gtk_widget_class_set_template_from_resource (widget_class, + "/sm/puri/handy/ui/hdy-view-switcher-bar.ui"); + gtk_widget_class_bind_template_child (widget_class, HdyViewSwitcherBar, action_bar); + gtk_widget_class_bind_template_child (widget_class, HdyViewSwitcherBar, view_switcher); +} + +static void +hdy_view_switcher_bar_init (HdyViewSwitcherBar *self) +{ + /* This must be initialized before the template so the embedded view switcher + * can pick up the correct default value. + */ + self->policy = HDY_VIEW_SWITCHER_POLICY_NARROW; + + gtk_widget_init_template (GTK_WIDGET (self)); + + self->revealer = GTK_REVEALER (gtk_bin_get_child (GTK_BIN (self->action_bar))); + update_bar_revealed (self); + gtk_revealer_set_transition_type (self->revealer, GTK_REVEALER_TRANSITION_TYPE_SLIDE_UP); +} + +/** + * hdy_view_switcher_bar_new: + * + * Creates a new #HdyViewSwitcherBar widget. + * + * Returns: a new #HdyViewSwitcherBar + * + * Since: 0.0.10 + */ +GtkWidget * +hdy_view_switcher_bar_new (void) +{ + return g_object_new (HDY_TYPE_VIEW_SWITCHER_BAR, NULL); +} + +/** + * hdy_view_switcher_bar_get_policy: + * @self: a #HdyViewSwitcherBar + * + * Gets the policy of @self. + * + * Returns: the policy of @self + * + * Since: 0.0.10 + */ +HdyViewSwitcherPolicy +hdy_view_switcher_bar_get_policy (HdyViewSwitcherBar *self) +{ + g_return_val_if_fail (HDY_IS_VIEW_SWITCHER_BAR (self), HDY_VIEW_SWITCHER_POLICY_NARROW); + + return self->policy; +} + +/** + * hdy_view_switcher_bar_set_policy: + * @self: a #HdyViewSwitcherBar + * @policy: the new policy + * + * Sets the policy of @self. + * + * Since: 0.0.10 + */ +void +hdy_view_switcher_bar_set_policy (HdyViewSwitcherBar *self, + HdyViewSwitcherPolicy policy) +{ + g_return_if_fail (HDY_IS_VIEW_SWITCHER_BAR (self)); + + if (self->policy == policy) + return; + + self->policy = policy; + + g_object_notify_by_pspec (G_OBJECT (self), props[PROP_POLICY]); + + gtk_widget_queue_resize (GTK_WIDGET (self)); +} + +/** + * hdy_view_switcher_bar_get_stack: + * @self: a #HdyViewSwitcherBar + * + * Get the #GtkStack being controlled by the #HdyViewSwitcher. + * + * Returns: (nullable) (transfer none): the #GtkStack, or %NULL if none has been set + * + * Since: 0.0.10 + */ +GtkStack * +hdy_view_switcher_bar_get_stack (HdyViewSwitcherBar *self) +{ + g_return_val_if_fail (HDY_IS_VIEW_SWITCHER_BAR (self), NULL); + + return hdy_view_switcher_get_stack (self->view_switcher); +} + +/** + * hdy_view_switcher_bar_set_stack: + * @self: a #HdyViewSwitcherBar + * @stack: (nullable): a #GtkStack + * + * Sets the #GtkStack to control. + * + * Since: 0.0.10 + */ +void +hdy_view_switcher_bar_set_stack (HdyViewSwitcherBar *self, + GtkStack *stack) +{ + GtkStack *previous_stack; + + g_return_if_fail (HDY_IS_VIEW_SWITCHER_BAR (self)); + g_return_if_fail (stack == NULL || GTK_IS_STACK (stack)); + + previous_stack = hdy_view_switcher_get_stack (self->view_switcher); + + if (previous_stack == stack) + return; + + if (previous_stack) + g_signal_handlers_disconnect_by_func (previous_stack, G_CALLBACK (update_bar_revealed), self); + + hdy_view_switcher_set_stack (self->view_switcher, stack); + + if (stack) { + g_signal_connect_swapped (stack, "add", G_CALLBACK (update_bar_revealed), self); + g_signal_connect_swapped (stack, "remove", G_CALLBACK (update_bar_revealed), self); + } + + update_bar_revealed (self); + + g_object_notify_by_pspec (G_OBJECT (self), props[PROP_STACK]); +} + +/** + * hdy_view_switcher_bar_get_reveal: + * @self: a #HdyViewSwitcherBar + * + * Gets whether @self should be revealed or not. + * + * Returns: %TRUE if @self is revealed, %FALSE if not. + * + * Since: 0.0.10 + */ +gboolean +hdy_view_switcher_bar_get_reveal (HdyViewSwitcherBar *self) +{ + g_return_val_if_fail (HDY_IS_VIEW_SWITCHER_BAR (self), FALSE); + + return self->reveal; +} + +/** + * hdy_view_switcher_bar_set_reveal: + * @self: a #HdyViewSwitcherBar + * @reveal: %TRUE to reveal @self + * + * Sets whether @self should be revealed or not. + * + * Since: 0.0.10 + */ +void +hdy_view_switcher_bar_set_reveal (HdyViewSwitcherBar *self, + gboolean reveal) +{ + g_return_if_fail (HDY_IS_VIEW_SWITCHER_BAR (self)); + + reveal = !!reveal; + + if (self->reveal == reveal) + return; + + self->reveal = reveal; + update_bar_revealed (self); + + g_object_notify_by_pspec (G_OBJECT (self), props[PROP_REVEAL]); +} |