summaryrefslogtreecommitdiffstats
path: root/subprojects/libhandy/src/hdy-view-switcher-button.c
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects/libhandy/src/hdy-view-switcher-button.c')
-rw-r--r--subprojects/libhandy/src/hdy-view-switcher-button.c536
1 files changed, 536 insertions, 0 deletions
diff --git a/subprojects/libhandy/src/hdy-view-switcher-button.c b/subprojects/libhandy/src/hdy-view-switcher-button.c
new file mode 100644
index 0000000..20b57b0
--- /dev/null
+++ b/subprojects/libhandy/src/hdy-view-switcher-button.c
@@ -0,0 +1,536 @@
+/*
+ * 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-view-switcher-button-private.h"
+
+/**
+ * PRIVATE:hdy-view-switcher-button
+ * @short_description: Button used in #HdyViewSwitcher.
+ * @title: HdyViewSwitcherButton
+ * @See_also: #HdyViewSwitcher
+ * @stability: Private
+ *
+ * #HdyViewSwitcherButton represents an application's view. It is designed to be
+ * used exclusively internally by #HdyViewSwitcher.
+ *
+ * Since: 0.0.10
+ */
+
+enum {
+ PROP_0,
+ PROP_ICON_SIZE,
+ PROP_ICON_NAME,
+ PROP_NEEDS_ATTENTION,
+
+ /* Overridden properties */
+ PROP_LABEL,
+ PROP_ORIENTATION,
+
+ LAST_PROP = PROP_NEEDS_ATTENTION + 1,
+};
+
+struct _HdyViewSwitcherButton
+{
+ GtkRadioButton parent_instance;
+
+ GtkBox *horizontal_box;
+ GtkImage *horizontal_image;
+ GtkLabel *horizontal_label_active;
+ GtkLabel *horizontal_label_inactive;
+ GtkStack *horizontal_label_stack;
+ GtkStack *stack;
+ GtkBox *vertical_box;
+ GtkImage *vertical_image;
+ GtkLabel *vertical_label_active;
+ GtkLabel *vertical_label_inactive;
+ GtkStack *vertical_label_stack;
+
+ gchar *icon_name;
+ GtkIconSize icon_size;
+ gchar *label;
+ GtkOrientation orientation;
+};
+
+static GParamSpec *props[LAST_PROP];
+
+G_DEFINE_TYPE_WITH_CODE (HdyViewSwitcherButton, hdy_view_switcher_button, GTK_TYPE_RADIO_BUTTON,
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL))
+
+static void
+on_active_changed (HdyViewSwitcherButton *self)
+{
+ g_return_if_fail (HDY_IS_VIEW_SWITCHER_BUTTON (self));
+
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self))) {
+ gtk_stack_set_visible_child (self->horizontal_label_stack, GTK_WIDGET (self->horizontal_label_active));
+ gtk_stack_set_visible_child (self->vertical_label_stack, GTK_WIDGET (self->vertical_label_active));
+ } else {
+ gtk_stack_set_visible_child (self->horizontal_label_stack, GTK_WIDGET (self->horizontal_label_inactive));
+ gtk_stack_set_visible_child (self->vertical_label_stack, GTK_WIDGET (self->vertical_label_inactive));
+ }
+}
+
+static GtkOrientation
+get_orientation (HdyViewSwitcherButton *self)
+{
+ g_return_val_if_fail (HDY_IS_VIEW_SWITCHER_BUTTON (self), GTK_ORIENTATION_HORIZONTAL);
+
+ return self->orientation;
+}
+
+static void
+set_orientation (HdyViewSwitcherButton *self,
+ GtkOrientation orientation)
+{
+ g_return_if_fail (HDY_IS_VIEW_SWITCHER_BUTTON (self));
+
+ if (self->orientation == orientation)
+ return;
+
+ self->orientation = orientation;
+
+ gtk_stack_set_visible_child (self->stack,
+ GTK_WIDGET (self->orientation == GTK_ORIENTATION_VERTICAL ?
+ self->vertical_box :
+ self->horizontal_box));
+}
+
+static void
+hdy_view_switcher_button_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ HdyViewSwitcherButton *self = HDY_VIEW_SWITCHER_BUTTON (object);
+
+ switch (prop_id) {
+ case PROP_ICON_NAME:
+ g_value_set_string (value, hdy_view_switcher_button_get_icon_name (self));
+ break;
+ case PROP_ICON_SIZE:
+ g_value_set_int (value, hdy_view_switcher_button_get_icon_size (self));
+ break;
+ case PROP_NEEDS_ATTENTION:
+ g_value_set_boolean (value, hdy_view_switcher_button_get_needs_attention (self));
+ break;
+ case PROP_LABEL:
+ g_value_set_string (value, hdy_view_switcher_button_get_label (self));
+ break;
+ case PROP_ORIENTATION:
+ g_value_set_enum (value, get_orientation (self));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+hdy_view_switcher_button_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ HdyViewSwitcherButton *self = HDY_VIEW_SWITCHER_BUTTON (object);
+
+ switch (prop_id) {
+ case PROP_ICON_NAME:
+ hdy_view_switcher_button_set_icon_name (self, g_value_get_string (value));
+ break;
+ case PROP_ICON_SIZE:
+ hdy_view_switcher_button_set_icon_size (self, g_value_get_int (value));
+ break;
+ case PROP_NEEDS_ATTENTION:
+ hdy_view_switcher_button_set_needs_attention (self, g_value_get_boolean (value));
+ break;
+ case PROP_LABEL:
+ hdy_view_switcher_button_set_label (self, g_value_get_string (value));
+ break;
+ case PROP_ORIENTATION:
+ set_orientation (self, g_value_get_enum (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+hdy_view_switcher_button_finalize (GObject *object)
+{
+ HdyViewSwitcherButton *self = HDY_VIEW_SWITCHER_BUTTON (object);
+
+ g_free (self->icon_name);
+ g_free (self->label);
+
+ G_OBJECT_CLASS (hdy_view_switcher_button_parent_class)->finalize (object);
+}
+
+static void
+hdy_view_switcher_button_class_init (HdyViewSwitcherButtonClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->get_property = hdy_view_switcher_button_get_property;
+ object_class->set_property = hdy_view_switcher_button_set_property;
+ object_class->finalize = hdy_view_switcher_button_finalize;
+
+ g_object_class_override_property (object_class,
+ PROP_LABEL,
+ "label");
+
+ g_object_class_override_property (object_class,
+ PROP_ORIENTATION,
+ "orientation");
+
+ /**
+ * HdyViewSwitcherButton:icon-name:
+ *
+ * The icon name representing the view, or %NULL for no icon.
+ *
+ * Since: 0.0.10
+ */
+ props[PROP_ICON_NAME] =
+ g_param_spec_string ("icon-name",
+ _("Icon Name"),
+ _("Icon name for image"),
+ "text-x-generic-symbolic",
+ G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE);
+
+ /**
+ * HdyViewSwitcherButton:icon-size:
+ *
+ * The icon size.
+ *
+ * Since: 0.0.10
+ */
+ props[PROP_ICON_SIZE] =
+ g_param_spec_int ("icon-size",
+ _("Icon Size"),
+ _("Symbolic size to use for named icon"),
+ 0, G_MAXINT, GTK_ICON_SIZE_BUTTON,
+ G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE);
+
+ /**
+ * HdyViewSwitcherButton:needs-attention:
+ *
+ * Sets a flag specifying whether the view requires the user attention. This
+ * is used by the HdyViewSwitcher to change the appearance of the
+ * corresponding button when a view needs attention and it is not the current
+ * one.
+ *
+ * Since: 0.0.10
+ */
+ props[PROP_NEEDS_ATTENTION] =
+ g_param_spec_boolean ("needs-attention",
+ _("Needs attention"),
+ _("Hint the view needs attention"),
+ FALSE,
+ G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE);
+
+ g_object_class_install_properties (object_class, LAST_PROP, props);
+
+ /* We probably should set the class's CSS name to "viewswitcherbutton"
+ * here, but it doesn't work because GtkCheckButton hardcodes it to "button"
+ * on instantiation, and the functions required to override it are private.
+ * In the meantime, we can use the "viewswitcher > button" CSS selector as
+ * a fairly safe fallback.
+ */
+
+ gtk_widget_class_set_template_from_resource (widget_class,
+ "/sm/puri/handy/ui/hdy-view-switcher-button.ui");
+ gtk_widget_class_bind_template_child (widget_class, HdyViewSwitcherButton, horizontal_box);
+ gtk_widget_class_bind_template_child (widget_class, HdyViewSwitcherButton, horizontal_image);
+ gtk_widget_class_bind_template_child (widget_class, HdyViewSwitcherButton, horizontal_label_active);
+ gtk_widget_class_bind_template_child (widget_class, HdyViewSwitcherButton, horizontal_label_inactive);
+ gtk_widget_class_bind_template_child (widget_class, HdyViewSwitcherButton, horizontal_label_stack);
+ gtk_widget_class_bind_template_child (widget_class, HdyViewSwitcherButton, stack);
+ gtk_widget_class_bind_template_child (widget_class, HdyViewSwitcherButton, vertical_box);
+ gtk_widget_class_bind_template_child (widget_class, HdyViewSwitcherButton, vertical_image);
+ gtk_widget_class_bind_template_child (widget_class, HdyViewSwitcherButton, vertical_label_active);
+ gtk_widget_class_bind_template_child (widget_class, HdyViewSwitcherButton, vertical_label_inactive);
+ gtk_widget_class_bind_template_child (widget_class, HdyViewSwitcherButton, vertical_label_stack);
+ gtk_widget_class_bind_template_callback (widget_class, on_active_changed);
+}
+
+static void
+hdy_view_switcher_button_init (HdyViewSwitcherButton *self)
+{
+ self->icon_size = GTK_ICON_SIZE_BUTTON;
+
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ gtk_stack_set_visible_child (GTK_STACK (self->stack), GTK_WIDGET (self->horizontal_box));
+
+ gtk_widget_set_focus_on_click (GTK_WIDGET (self), FALSE);
+ /* Make the button look like a regular button and not a radio button. */
+ gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (self), FALSE);
+
+ on_active_changed (self);
+}
+
+/**
+ * hdy_view_switcher_button_new:
+ *
+ * Creates a new #HdyViewSwitcherButton widget.
+ *
+ * Returns: a new #HdyViewSwitcherButton
+ *
+ * Since: 0.0.10
+ */
+GtkWidget *
+hdy_view_switcher_button_new (void)
+{
+ return g_object_new (HDY_TYPE_VIEW_SWITCHER_BUTTON, NULL);
+}
+
+/**
+ * hdy_view_switcher_button_get_icon_name:
+ * @self: a #HdyViewSwitcherButton
+ *
+ * Gets the icon name representing the view, or %NULL is no icon is set.
+ *
+ * Returns: (transfer none) (nullable): the icon name, or %NULL
+ *
+ * Since: 0.0.10
+ **/
+const gchar *
+hdy_view_switcher_button_get_icon_name (HdyViewSwitcherButton *self)
+{
+ g_return_val_if_fail (HDY_IS_VIEW_SWITCHER_BUTTON (self), NULL);
+
+ return self->icon_name;
+}
+
+/**
+ * hdy_view_switcher_button_set_icon_name:
+ * @self: a #HdyViewSwitcherButton
+ * @icon_name: (nullable): an icon name or %NULL
+ *
+ * Sets the icon name representing the view, or %NULL to disable the icon.
+ *
+ * Since: 0.0.10
+ **/
+void
+hdy_view_switcher_button_set_icon_name (HdyViewSwitcherButton *self,
+ const gchar *icon_name)
+{
+ g_return_if_fail (HDY_IS_VIEW_SWITCHER_BUTTON (self));
+
+ if (!g_strcmp0 (self->icon_name, icon_name))
+ return;
+
+ g_free (self->icon_name);
+ self->icon_name = g_strdup (icon_name);
+
+ g_object_notify_by_pspec (G_OBJECT (self), props[PROP_ICON_NAME]);
+}
+
+/**
+ * hdy_view_switcher_button_get_icon_size:
+ * @self: a #HdyViewSwitcherButton
+ *
+ * Gets the icon size used by @self.
+ *
+ * Returns: the icon size used by @self
+ *
+ * Since: 0.0.10
+ **/
+GtkIconSize
+hdy_view_switcher_button_get_icon_size (HdyViewSwitcherButton *self)
+{
+ g_return_val_if_fail (HDY_IS_VIEW_SWITCHER_BUTTON (self), GTK_ICON_SIZE_INVALID);
+
+ return self->icon_size;
+}
+
+/**
+ * hdy_view_switcher_button_set_icon_size:
+ * @self: a #HdyViewSwitcherButton
+ * @icon_size: the new icon size
+ *
+ * Sets the icon size used by @self.
+ *
+ * Since: 0.0.10
+ */
+void
+hdy_view_switcher_button_set_icon_size (HdyViewSwitcherButton *self,
+ GtkIconSize icon_size)
+{
+ g_return_if_fail (HDY_IS_VIEW_SWITCHER_BUTTON (self));
+
+ if (self->icon_size == icon_size)
+ return;
+
+ self->icon_size = icon_size;
+
+ g_object_notify_by_pspec (G_OBJECT (self), props[PROP_ICON_SIZE]);
+}
+
+/**
+ * hdy_view_switcher_button_get_needs_attention:
+ * @self: a #HdyViewSwitcherButton
+ *
+ * Gets whether the view represented by @self requires the user attention.
+ *
+ * Returns: %TRUE if the view represented by @self requires the user attention, %FALSE otherwise
+ *
+ * Since: 0.0.10
+ **/
+gboolean
+hdy_view_switcher_button_get_needs_attention (HdyViewSwitcherButton *self)
+{
+ GtkStyleContext *context;
+
+ g_return_val_if_fail (HDY_IS_VIEW_SWITCHER_BUTTON (self), FALSE);
+
+ context = gtk_widget_get_style_context (GTK_WIDGET (self));
+
+ return gtk_style_context_has_class (context, GTK_STYLE_CLASS_NEEDS_ATTENTION);
+}
+
+/**
+ * hdy_view_switcher_button_set_needs_attention:
+ * @self: a #HdyViewSwitcherButton
+ * @needs_attention: the new icon size
+ *
+ * Sets whether the view represented by @self requires the user attention.
+ *
+ * Since: 0.0.10
+ */
+void
+hdy_view_switcher_button_set_needs_attention (HdyViewSwitcherButton *self,
+ gboolean needs_attention)
+{
+ GtkStyleContext *context;
+
+ g_return_if_fail (HDY_IS_VIEW_SWITCHER_BUTTON (self));
+
+ needs_attention = !!needs_attention;
+
+ context = gtk_widget_get_style_context (GTK_WIDGET (self));
+ if (gtk_style_context_has_class (context, GTK_STYLE_CLASS_NEEDS_ATTENTION) == needs_attention)
+ return;
+
+ if (needs_attention)
+ gtk_style_context_add_class (context, GTK_STYLE_CLASS_NEEDS_ATTENTION);
+ else
+ gtk_style_context_remove_class (context, GTK_STYLE_CLASS_NEEDS_ATTENTION);
+
+ g_object_notify_by_pspec (G_OBJECT (self), props[PROP_NEEDS_ATTENTION]);
+}
+
+/**
+ * hdy_view_switcher_button_get_label:
+ * @self: a #HdyViewSwitcherButton
+ *
+ * Gets the label representing the view.
+ *
+ * Returns: (transfer none) (nullable): the label, or %NULL
+ *
+ * Since: 0.0.10
+ **/
+const gchar *
+hdy_view_switcher_button_get_label (HdyViewSwitcherButton *self)
+{
+ g_return_val_if_fail (HDY_IS_VIEW_SWITCHER_BUTTON (self), NULL);
+
+ return self->label;
+}
+
+/**
+ * hdy_view_switcher_button_set_label:
+ * @self: a #HdyViewSwitcherButton
+ * @label: (nullable): a label or %NULL
+ *
+ * Sets the label representing the view.
+ *
+ * Since: 0.0.10
+ **/
+void
+hdy_view_switcher_button_set_label (HdyViewSwitcherButton *self,
+ const gchar *label)
+{
+ g_return_if_fail (HDY_IS_VIEW_SWITCHER_BUTTON (self));
+
+ if (!g_strcmp0 (self->label, label))
+ return;
+
+ g_free (self->label);
+ self->label = g_strdup (label);
+
+ g_object_notify (G_OBJECT (self), "label");
+}
+
+/**
+ * hdy_view_switcher_button_set_narrow_ellipsize:
+ * @self: a #HdyViewSwitcherButton
+ * @mode: a #PangoEllipsizeMode
+ *
+ * Set the mode used to ellipsize the text in narrow mode if there is not
+ * enough space to render the entire string.
+ *
+ * Since: 0.0.10
+ **/
+void
+hdy_view_switcher_button_set_narrow_ellipsize (HdyViewSwitcherButton *self,
+ PangoEllipsizeMode mode)
+{
+ g_return_if_fail (HDY_IS_VIEW_SWITCHER_BUTTON (self));
+ g_return_if_fail (mode >= PANGO_ELLIPSIZE_NONE && mode <= PANGO_ELLIPSIZE_END);
+
+ gtk_label_set_ellipsize (self->vertical_label_active, mode);
+ gtk_label_set_ellipsize (self->vertical_label_inactive, mode);
+}
+
+/**
+ * hdy_view_switcher_button_get_size:
+ * @self: a #HdyViewSwitcherButton
+ * @h_min_width: (out) (nullable): the minimum width when horizontal
+ * @h_nat_width: (out) (nullable): the natural width when horizontal
+ * @v_min_width: (out) (nullable): the minimum width when vertical
+ * @v_nat_width: (out) (nullable): the natural width when vertical
+ *
+ * Measure the size requests in both horizontal and vertical modes.
+ *
+ * Since: 0.0.10
+ */
+void
+hdy_view_switcher_button_get_size (HdyViewSwitcherButton *self,
+ gint *h_min_width,
+ gint *h_nat_width,
+ gint *v_min_width,
+ gint *v_nat_width)
+{
+ GtkStyleContext *context;
+ GtkStateFlags state;
+ GtkBorder border;
+
+ /* gtk_widget_get_preferred_width() doesn't accept both its out parameters to
+ * be NULL, so we must have guards.
+ */
+ if (h_min_width != NULL || h_nat_width != NULL)
+ gtk_widget_get_preferred_width (GTK_WIDGET (self->horizontal_box), h_min_width, h_nat_width);
+ if (v_min_width != NULL || v_nat_width != NULL)
+ gtk_widget_get_preferred_width (GTK_WIDGET (self->vertical_box), v_min_width, v_nat_width);
+
+ context = gtk_widget_get_style_context (GTK_WIDGET (self));
+ state = gtk_style_context_get_state (context);
+ gtk_style_context_get_border (context, state, &border);
+ if (h_min_width != NULL)
+ *h_min_width += border.left + border.right;
+ if (h_nat_width != NULL)
+ *h_nat_width += border.left + border.right;
+ if (v_min_width != NULL)
+ *v_min_width += border.left + border.right;
+ if (v_nat_width != NULL)
+ *v_nat_width += border.left + border.right;
+}