summaryrefslogtreecommitdiffstats
path: root/subprojects/libhandy/src/hdy-leaflet.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--subprojects/libhandy/src/hdy-leaflet.c1209
1 files changed, 1209 insertions, 0 deletions
diff --git a/subprojects/libhandy/src/hdy-leaflet.c b/subprojects/libhandy/src/hdy-leaflet.c
new file mode 100644
index 0000000..8c1ba2a
--- /dev/null
+++ b/subprojects/libhandy/src/hdy-leaflet.c
@@ -0,0 +1,1209 @@
+/*
+ * Copyright (C) 2018 Purism SPC
+ * Copyright (C) 2019 Alexander Mikhaylenko <exalm7659@gmail.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+
+#include "config.h"
+#include <glib/gi18n-lib.h>
+
+#include "hdy-leaflet.h"
+#include "hdy-stackable-box-private.h"
+#include "hdy-swipeable.h"
+
+/**
+ * SECTION:hdy-leaflet
+ * @short_description: An adaptive container acting like a box or a stack.
+ * @Title: HdyLeaflet
+ *
+ * The #HdyLeaflet widget can display its children like a #GtkBox does or
+ * like a #GtkStack does, adapting to size changes by switching between
+ * the two modes.
+ *
+ * When there is enough space the children are displayed side by side, otherwise
+ * only one is displayed and the leaflet is said to be “folded”.
+ * The threshold is dictated by the preferred minimum sizes of the children.
+ * When a leaflet is folded, the children can be navigated using swipe gestures.
+ *
+ * The “over” and “under” stack the children one on top of the other, while the
+ * “slide” transition puts the children side by side. While navigating to a
+ * child on the side or below can be performed by swiping the current child
+ * away, navigating to an upper child requires dragging it from the edge where
+ * it resides. This doesn't affect non-dragging swipes.
+ *
+ * The “over” and “under” transitions can draw their shadow on top of the
+ * window's transparent areas, like the rounded corners. This is a side-effect
+ * of allowing shadows to be drawn on top of OpenGL areas. It can be mitigated
+ * by using #HdyWindow or #HdyApplicationWindow as they will crop anything drawn
+ * beyond the rounded corners.
+ *
+ * # CSS nodes
+ *
+ * #HdyLeaflet has a single CSS node with name leaflet. The node will get the
+ * style classes .folded when it is folded, .unfolded when it's not, or none if
+ * it didn't compute its fold yet.
+ */
+
+/**
+ * HdyLeafletTransitionType:
+ * @HDY_LEAFLET_TRANSITION_TYPE_OVER: Cover the old page or uncover the new page, sliding from or towards the end according to orientation, text direction and children order
+ * @HDY_LEAFLET_TRANSITION_TYPE_UNDER: Uncover the new page or cover the old page, sliding from or towards the start according to orientation, text direction and children order
+ * @HDY_LEAFLET_TRANSITION_TYPE_SLIDE: Slide from left, right, up or down according to the orientation, text direction and the children order
+ *
+ * This enumeration value describes the possible transitions between modes and
+ * children in a #HdyLeaflet widget.
+ *
+ * New values may be added to this enumeration over time.
+ *
+ * Since: 0.0.12
+ */
+
+enum {
+ PROP_0,
+ PROP_FOLDED,
+ PROP_HHOMOGENEOUS_FOLDED,
+ PROP_VHOMOGENEOUS_FOLDED,
+ PROP_HHOMOGENEOUS_UNFOLDED,
+ PROP_VHOMOGENEOUS_UNFOLDED,
+ PROP_VISIBLE_CHILD,
+ PROP_VISIBLE_CHILD_NAME,
+ PROP_TRANSITION_TYPE,
+ PROP_MODE_TRANSITION_DURATION,
+ PROP_CHILD_TRANSITION_DURATION,
+ PROP_CHILD_TRANSITION_RUNNING,
+ PROP_INTERPOLATE_SIZE,
+ PROP_CAN_SWIPE_BACK,
+ PROP_CAN_SWIPE_FORWARD,
+
+ /* orientable */
+ PROP_ORIENTATION,
+ LAST_PROP = PROP_ORIENTATION,
+};
+
+enum {
+ CHILD_PROP_0,
+ CHILD_PROP_NAME,
+ CHILD_PROP_NAVIGATABLE,
+ LAST_CHILD_PROP,
+};
+
+typedef struct
+{
+ HdyStackableBox *box;
+} HdyLeafletPrivate;
+
+static GParamSpec *props[LAST_PROP];
+static GParamSpec *child_props[LAST_CHILD_PROP];
+
+static void hdy_leaflet_swipeable_init (HdySwipeableInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (HdyLeaflet, hdy_leaflet, GTK_TYPE_CONTAINER,
+ G_ADD_PRIVATE (HdyLeaflet)
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL)
+ G_IMPLEMENT_INTERFACE (HDY_TYPE_SWIPEABLE, hdy_leaflet_swipeable_init))
+
+#define HDY_GET_HELPER(obj) (((HdyLeafletPrivate *) hdy_leaflet_get_instance_private (HDY_LEAFLET (obj)))->box)
+
+/**
+ * hdy_leaflet_get_folded:
+ * @self: a #HdyLeaflet
+ *
+ * Gets whether @self is folded.
+ *
+ * Returns: whether @self is folded.
+ */
+gboolean
+hdy_leaflet_get_folded (HdyLeaflet *self)
+{
+ g_return_val_if_fail (HDY_IS_LEAFLET (self), FALSE);
+
+ return hdy_stackable_box_get_folded (HDY_GET_HELPER (self));
+}
+
+/**
+ * hdy_leaflet_set_homogeneous:
+ * @self: a #HdyLeaflet
+ * @folded: the fold
+ * @orientation: the orientation
+ * @homogeneous: %TRUE to make @self homogeneous
+ *
+ * Sets the #HdyLeaflet to be homogeneous or not for the given fold and orientation.
+ * If it is homogeneous, the #HdyLeaflet will request the same
+ * width or height for all its children depending on the orientation.
+ * If it isn't and it is folded, the leaflet may change width or height
+ * when a different child becomes visible.
+ */
+void
+hdy_leaflet_set_homogeneous (HdyLeaflet *self,
+ gboolean folded,
+ GtkOrientation orientation,
+ gboolean homogeneous)
+{
+ g_return_if_fail (HDY_IS_LEAFLET (self));
+
+ hdy_stackable_box_set_homogeneous (HDY_GET_HELPER (self), folded, orientation, homogeneous);
+}
+
+/**
+ * hdy_leaflet_get_homogeneous:
+ * @self: a #HdyLeaflet
+ * @folded: the fold
+ * @orientation: the orientation
+ *
+ * Gets whether @self is homogeneous for the given fold and orientation.
+ * See hdy_leaflet_set_homogeneous().
+ *
+ * Returns: whether @self is homogeneous for the given fold and orientation.
+ */
+gboolean
+hdy_leaflet_get_homogeneous (HdyLeaflet *self,
+ gboolean folded,
+ GtkOrientation orientation)
+{
+ g_return_val_if_fail (HDY_IS_LEAFLET (self), FALSE);
+
+ return hdy_stackable_box_get_homogeneous (HDY_GET_HELPER (self), folded, orientation);
+}
+
+/**
+ * hdy_leaflet_get_transition_type:
+ * @self: a #HdyLeaflet
+ *
+ * Gets the type of animation that will be used
+ * for transitions between modes and children in @self.
+ *
+ * Returns: the current transition type of @self
+ *
+ * Since: 0.0.12
+ */
+HdyLeafletTransitionType
+hdy_leaflet_get_transition_type (HdyLeaflet *self)
+{
+ HdyStackableBoxTransitionType type;
+
+ g_return_val_if_fail (HDY_IS_LEAFLET (self), HDY_LEAFLET_TRANSITION_TYPE_OVER);
+
+ type = hdy_stackable_box_get_transition_type (HDY_GET_HELPER (self));
+
+ switch (type) {
+ case HDY_STACKABLE_BOX_TRANSITION_TYPE_OVER:
+ return HDY_LEAFLET_TRANSITION_TYPE_OVER;
+
+ case HDY_STACKABLE_BOX_TRANSITION_TYPE_UNDER:
+ return HDY_LEAFLET_TRANSITION_TYPE_UNDER;
+
+ case HDY_STACKABLE_BOX_TRANSITION_TYPE_SLIDE:
+ return HDY_LEAFLET_TRANSITION_TYPE_SLIDE;
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+/**
+ * hdy_leaflet_set_transition_type:
+ * @self: a #HdyLeaflet
+ * @transition: the new transition type
+ *
+ * Sets the type of animation that will be used for transitions between modes
+ * and children in @self.
+ *
+ * The transition type can be changed without problems at runtime, so it is
+ * possible to change the animation based on the mode or child that is about to
+ * become current.
+ *
+ * Since: 0.0.12
+ */
+void
+hdy_leaflet_set_transition_type (HdyLeaflet *self,
+ HdyLeafletTransitionType transition)
+{
+ HdyStackableBoxTransitionType type;
+
+ g_return_if_fail (HDY_IS_LEAFLET (self));
+ g_return_if_fail (transition <= HDY_LEAFLET_TRANSITION_TYPE_SLIDE);
+
+ switch (transition) {
+ case HDY_LEAFLET_TRANSITION_TYPE_OVER:
+ type = HDY_STACKABLE_BOX_TRANSITION_TYPE_OVER;
+ break;
+
+ case HDY_LEAFLET_TRANSITION_TYPE_UNDER:
+ type = HDY_STACKABLE_BOX_TRANSITION_TYPE_UNDER;
+ break;
+
+ case HDY_LEAFLET_TRANSITION_TYPE_SLIDE:
+ type = HDY_STACKABLE_BOX_TRANSITION_TYPE_SLIDE;
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
+
+ hdy_stackable_box_set_transition_type (HDY_GET_HELPER (self), type);
+}
+
+/**
+ * hdy_leaflet_get_mode_transition_duration:
+ * @self: a #HdyLeaflet
+ *
+ * Returns the amount of time (in milliseconds) that
+ * transitions between modes in @self will take.
+ *
+ * Returns: the mode transition duration
+ */
+guint
+hdy_leaflet_get_mode_transition_duration (HdyLeaflet *self)
+{
+ g_return_val_if_fail (HDY_IS_LEAFLET (self), 0);
+
+ return hdy_stackable_box_get_mode_transition_duration (HDY_GET_HELPER (self));
+}
+
+/**
+ * hdy_leaflet_set_mode_transition_duration:
+ * @self: a #HdyLeaflet
+ * @duration: the new duration, in milliseconds
+ *
+ * Sets the duration that transitions between modes in @self
+ * will take.
+ */
+void
+hdy_leaflet_set_mode_transition_duration (HdyLeaflet *self,
+ guint duration)
+{
+ g_return_if_fail (HDY_IS_LEAFLET (self));
+
+ hdy_stackable_box_set_mode_transition_duration (HDY_GET_HELPER (self), duration);
+}
+
+/**
+ * hdy_leaflet_get_child_transition_duration:
+ * @self: a #HdyLeaflet
+ *
+ * Returns the amount of time (in milliseconds) that
+ * transitions between children in @self will take.
+ *
+ * Returns: the child transition duration
+ */
+guint
+hdy_leaflet_get_child_transition_duration (HdyLeaflet *self)
+{
+ g_return_val_if_fail (HDY_IS_LEAFLET (self), 0);
+
+ return hdy_stackable_box_get_child_transition_duration (HDY_GET_HELPER (self));
+}
+
+/**
+ * hdy_leaflet_set_child_transition_duration:
+ * @self: a #HdyLeaflet
+ * @duration: the new duration, in milliseconds
+ *
+ * Sets the duration that transitions between children in @self
+ * will take.
+ */
+void
+hdy_leaflet_set_child_transition_duration (HdyLeaflet *self,
+ guint duration)
+{
+ g_return_if_fail (HDY_IS_LEAFLET (self));
+
+ hdy_stackable_box_set_child_transition_duration (HDY_GET_HELPER (self), duration);
+}
+
+/**
+ * hdy_leaflet_get_visible_child:
+ * @self: a #HdyLeaflet
+ *
+ * Gets the visible child widget.
+ *
+ * Returns: (transfer none): the visible child widget
+ */
+GtkWidget *
+hdy_leaflet_get_visible_child (HdyLeaflet *self)
+{
+ g_return_val_if_fail (HDY_IS_LEAFLET (self), NULL);
+
+ return hdy_stackable_box_get_visible_child (HDY_GET_HELPER (self));
+}
+
+/**
+ * hdy_leaflet_set_visible_child:
+ * @self: a #HdyLeaflet
+ * @visible_child: the new child
+ *
+ * Makes @visible_child visible using a transition determined by
+ * HdyLeaflet:transition-type and HdyLeaflet:child-transition-duration. The
+ * transition can be cancelled by the user, in which case visible child will
+ * change back to the previously visible child.
+ */
+void
+hdy_leaflet_set_visible_child (HdyLeaflet *self,
+ GtkWidget *visible_child)
+{
+ g_return_if_fail (HDY_IS_LEAFLET (self));
+
+ hdy_stackable_box_set_visible_child (HDY_GET_HELPER (self), visible_child);
+}
+
+/**
+ * hdy_leaflet_get_visible_child_name:
+ * @self: a #HdyLeaflet
+ *
+ * Gets the name of the currently visible child widget.
+ *
+ * Returns: (transfer none): the name of the visible child
+ */
+const gchar *
+hdy_leaflet_get_visible_child_name (HdyLeaflet *self)
+{
+ g_return_val_if_fail (HDY_IS_LEAFLET (self), NULL);
+
+ return hdy_stackable_box_get_visible_child_name (HDY_GET_HELPER (self));
+}
+
+/**
+ * hdy_leaflet_set_visible_child_name:
+ * @self: a #HdyLeaflet
+ * @name: the name of a child
+ *
+ * Makes the child with the name @name visible.
+ *
+ * See hdy_leaflet_set_visible_child() for more details.
+ */
+void
+hdy_leaflet_set_visible_child_name (HdyLeaflet *self,
+ const gchar *name)
+{
+ g_return_if_fail (HDY_IS_LEAFLET (self));
+
+ hdy_stackable_box_set_visible_child_name (HDY_GET_HELPER (self), name);
+}
+
+/**
+ * hdy_leaflet_get_child_transition_running:
+ * @self: a #HdyLeaflet
+ *
+ * Returns whether @self is currently in a transition from one page to
+ * another.
+ *
+ * Returns: %TRUE if the transition is currently running, %FALSE otherwise.
+ */
+gboolean
+hdy_leaflet_get_child_transition_running (HdyLeaflet *self)
+{
+ g_return_val_if_fail (HDY_IS_LEAFLET (self), FALSE);
+
+ return hdy_stackable_box_get_child_transition_running (HDY_GET_HELPER (self));
+}
+
+/**
+ * hdy_leaflet_set_interpolate_size:
+ * @self: a #HdyLeaflet
+ * @interpolate_size: the new value
+ *
+ * Sets whether or not @self will interpolate its size when
+ * changing the visible child. If the #HdyLeaflet:interpolate-size
+ * property is set to %TRUE, @self will interpolate its size between
+ * the current one and the one it'll take after changing the
+ * visible child, according to the set transition duration.
+ */
+void
+hdy_leaflet_set_interpolate_size (HdyLeaflet *self,
+ gboolean interpolate_size)
+{
+ g_return_if_fail (HDY_IS_LEAFLET (self));
+
+ hdy_stackable_box_set_interpolate_size (HDY_GET_HELPER (self), interpolate_size);
+}
+
+/**
+ * hdy_leaflet_get_interpolate_size:
+ * @self: a #HdyLeaflet
+ *
+ * Returns whether the #HdyLeaflet is set up to interpolate between
+ * the sizes of children on page switch.
+ *
+ * Returns: %TRUE if child sizes are interpolated
+ */
+gboolean
+hdy_leaflet_get_interpolate_size (HdyLeaflet *self)
+{
+ g_return_val_if_fail (HDY_IS_LEAFLET (self), FALSE);
+
+ return hdy_stackable_box_get_interpolate_size (HDY_GET_HELPER (self));
+}
+
+/**
+ * hdy_leaflet_set_can_swipe_back:
+ * @self: a #HdyLeaflet
+ * @can_swipe_back: the new value
+ *
+ * Sets whether or not @self allows switching to the previous child that has
+ * 'navigatable' child property set to %TRUE via a swipe gesture
+ *
+ * Since: 0.0.12
+ */
+void
+hdy_leaflet_set_can_swipe_back (HdyLeaflet *self,
+ gboolean can_swipe_back)
+{
+ g_return_if_fail (HDY_IS_LEAFLET (self));
+
+ hdy_stackable_box_set_can_swipe_back (HDY_GET_HELPER (self), can_swipe_back);
+}
+
+/**
+ * hdy_leaflet_get_can_swipe_back
+ * @self: a #HdyLeaflet
+ *
+ * Returns whether the #HdyLeaflet allows swiping to the previous child.
+ *
+ * Returns: %TRUE if back swipe is enabled.
+ *
+ * Since: 0.0.12
+ */
+gboolean
+hdy_leaflet_get_can_swipe_back (HdyLeaflet *self)
+{
+ g_return_val_if_fail (HDY_IS_LEAFLET (self), FALSE);
+
+ return hdy_stackable_box_get_can_swipe_back (HDY_GET_HELPER (self));
+}
+
+/**
+ * hdy_leaflet_set_can_swipe_forward:
+ * @self: a #HdyLeaflet
+ * @can_swipe_forward: the new value
+ *
+ * Sets whether or not @self allows switching to the next child that has
+ * 'navigatable' child property set to %TRUE via a swipe gesture.
+ *
+ * Since: 0.0.12
+ */
+void
+hdy_leaflet_set_can_swipe_forward (HdyLeaflet *self,
+ gboolean can_swipe_forward)
+{
+ g_return_if_fail (HDY_IS_LEAFLET (self));
+
+ hdy_stackable_box_set_can_swipe_forward (HDY_GET_HELPER (self), can_swipe_forward);
+}
+
+/**
+ * hdy_leaflet_get_can_swipe_forward
+ * @self: a #HdyLeaflet
+ *
+ * Returns whether the #HdyLeaflet allows swiping to the next child.
+ *
+ * Returns: %TRUE if forward swipe is enabled.
+ *
+ * Since: 0.0.12
+ */
+gboolean
+hdy_leaflet_get_can_swipe_forward (HdyLeaflet *self)
+{
+ g_return_val_if_fail (HDY_IS_LEAFLET (self), FALSE);
+
+ return hdy_stackable_box_get_can_swipe_forward (HDY_GET_HELPER (self));
+}
+
+/**
+ * hdy_leaflet_get_adjacent_child
+ * @self: a #HdyLeaflet
+ * @direction: the direction
+ *
+ * Gets the previous or next child that doesn't have 'navigatable' child
+ * property set to %FALSE, or %NULL if it doesn't exist. This will be the same
+ * widget hdy_leaflet_navigate() will navigate to.
+ *
+ * Returns: (nullable) (transfer none): the previous or next child, or
+ * %NULL if it doesn't exist.
+ *
+ * Since: 1.0
+ */
+GtkWidget *
+hdy_leaflet_get_adjacent_child (HdyLeaflet *self,
+ HdyNavigationDirection direction)
+{
+ g_return_val_if_fail (HDY_IS_LEAFLET (self), NULL);
+
+ return hdy_stackable_box_get_adjacent_child (HDY_GET_HELPER (self), direction);
+}
+
+/**
+ * hdy_leaflet_navigate
+ * @self: a #HdyLeaflet
+ * @direction: the direction
+ *
+ * Switches to the previous or next child that doesn't have 'navigatable' child
+ * property set to %FALSE, similar to performing a swipe gesture to go in
+ * @direction.
+ *
+ * Returns: %TRUE if visible child was changed, %FALSE otherwise.
+ *
+ * Since: 1.0
+ */
+gboolean
+hdy_leaflet_navigate (HdyLeaflet *self,
+ HdyNavigationDirection direction)
+{
+ g_return_val_if_fail (HDY_IS_LEAFLET (self), FALSE);
+
+ return hdy_stackable_box_navigate (HDY_GET_HELPER (self), direction);
+}
+
+/**
+ * hdy_leaflet_get_child_by_name:
+ * @self: a #HdyLeaflet
+ * @name: the name of the child to find
+ *
+ * Finds the child of @self with the name given as the argument. Returns %NULL
+ * if there is no child with this name.
+ *
+ * Returns: (transfer none) (nullable): the requested child of @self
+ *
+ * Since: 1.0
+ */
+GtkWidget *
+hdy_leaflet_get_child_by_name (HdyLeaflet *self,
+ const gchar *name)
+{
+ g_return_val_if_fail (HDY_IS_LEAFLET (self), NULL);
+
+ return hdy_stackable_box_get_child_by_name (HDY_GET_HELPER (self), name);
+}
+
+/* This private method is prefixed by the call name because it will be a virtual
+ * method in GTK 4.
+ */
+static void
+hdy_leaflet_measure (GtkWidget *widget,
+ GtkOrientation orientation,
+ int for_size,
+ int *minimum,
+ int *natural,
+ int *minimum_baseline,
+ int *natural_baseline)
+{
+ hdy_stackable_box_measure (HDY_GET_HELPER (widget),
+ orientation, for_size,
+ minimum, natural,
+ minimum_baseline, natural_baseline);
+}
+
+static void
+hdy_leaflet_get_preferred_width (GtkWidget *widget,
+ gint *minimum_width,
+ gint *natural_width)
+{
+ hdy_leaflet_measure (widget, GTK_ORIENTATION_HORIZONTAL, -1,
+ minimum_width, natural_width, NULL, NULL);
+}
+
+static void
+hdy_leaflet_get_preferred_height (GtkWidget *widget,
+ gint *minimum_height,
+ gint *natural_height)
+{
+ hdy_leaflet_measure (widget, GTK_ORIENTATION_VERTICAL, -1,
+ minimum_height, natural_height, NULL, NULL);
+}
+
+static void
+hdy_leaflet_get_preferred_width_for_height (GtkWidget *widget,
+ gint height,
+ gint *minimum_width,
+ gint *natural_width)
+{
+ hdy_leaflet_measure (widget, GTK_ORIENTATION_HORIZONTAL, height,
+ minimum_width, natural_width, NULL, NULL);
+}
+
+static void
+hdy_leaflet_get_preferred_height_for_width (GtkWidget *widget,
+ gint width,
+ gint *minimum_height,
+ gint *natural_height)
+{
+ hdy_leaflet_measure (widget, GTK_ORIENTATION_VERTICAL, width,
+ minimum_height, natural_height, NULL, NULL);
+}
+
+static void
+hdy_leaflet_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ hdy_stackable_box_size_allocate (HDY_GET_HELPER (widget), allocation);
+}
+
+static gboolean
+hdy_leaflet_draw (GtkWidget *widget,
+ cairo_t *cr)
+{
+ return hdy_stackable_box_draw (HDY_GET_HELPER (widget), cr);
+}
+
+static void
+hdy_leaflet_direction_changed (GtkWidget *widget,
+ GtkTextDirection previous_direction)
+{
+ hdy_stackable_box_direction_changed (HDY_GET_HELPER (widget), previous_direction);
+}
+
+static void
+hdy_leaflet_add (GtkContainer *container,
+ GtkWidget *widget)
+{
+ hdy_stackable_box_add (HDY_GET_HELPER (container), widget);
+}
+
+static void
+hdy_leaflet_remove (GtkContainer *container,
+ GtkWidget *widget)
+{
+ hdy_stackable_box_remove (HDY_GET_HELPER (container), widget);
+}
+
+static void
+hdy_leaflet_forall (GtkContainer *container,
+ gboolean include_internals,
+ GtkCallback callback,
+ gpointer callback_data)
+{
+ hdy_stackable_box_forall (HDY_GET_HELPER (container), include_internals, callback, callback_data);
+}
+
+static void
+hdy_leaflet_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ HdyLeaflet *self = HDY_LEAFLET (object);
+
+ switch (prop_id) {
+ case PROP_FOLDED:
+ g_value_set_boolean (value, hdy_leaflet_get_folded (self));
+ break;
+ case PROP_HHOMOGENEOUS_FOLDED:
+ g_value_set_boolean (value, hdy_leaflet_get_homogeneous (self, TRUE, GTK_ORIENTATION_HORIZONTAL));
+ break;
+ case PROP_VHOMOGENEOUS_FOLDED:
+ g_value_set_boolean (value, hdy_leaflet_get_homogeneous (self, TRUE, GTK_ORIENTATION_VERTICAL));
+ break;
+ case PROP_HHOMOGENEOUS_UNFOLDED:
+ g_value_set_boolean (value, hdy_leaflet_get_homogeneous (self, FALSE, GTK_ORIENTATION_HORIZONTAL));
+ break;
+ case PROP_VHOMOGENEOUS_UNFOLDED:
+ g_value_set_boolean (value, hdy_leaflet_get_homogeneous (self, FALSE, GTK_ORIENTATION_VERTICAL));
+ break;
+ case PROP_VISIBLE_CHILD:
+ g_value_set_object (value, hdy_leaflet_get_visible_child (self));
+ break;
+ case PROP_VISIBLE_CHILD_NAME:
+ g_value_set_string (value, hdy_leaflet_get_visible_child_name (self));
+ break;
+ case PROP_TRANSITION_TYPE:
+ g_value_set_enum (value, hdy_leaflet_get_transition_type (self));
+ break;
+ case PROP_MODE_TRANSITION_DURATION:
+ g_value_set_uint (value, hdy_leaflet_get_mode_transition_duration (self));
+ break;
+ case PROP_CHILD_TRANSITION_DURATION:
+ g_value_set_uint (value, hdy_leaflet_get_child_transition_duration (self));
+ break;
+ case PROP_CHILD_TRANSITION_RUNNING:
+ g_value_set_boolean (value, hdy_leaflet_get_child_transition_running (self));
+ break;
+ case PROP_INTERPOLATE_SIZE:
+ g_value_set_boolean (value, hdy_leaflet_get_interpolate_size (self));
+ break;
+ case PROP_CAN_SWIPE_BACK:
+ g_value_set_boolean (value, hdy_leaflet_get_can_swipe_back (self));
+ break;
+ case PROP_CAN_SWIPE_FORWARD:
+ g_value_set_boolean (value, hdy_leaflet_get_can_swipe_forward (self));
+ break;
+ case PROP_ORIENTATION:
+ g_value_set_enum (value, hdy_stackable_box_get_orientation (HDY_GET_HELPER (self)));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+hdy_leaflet_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ HdyLeaflet *self = HDY_LEAFLET (object);
+
+ switch (prop_id) {
+ case PROP_HHOMOGENEOUS_FOLDED:
+ hdy_leaflet_set_homogeneous (self, TRUE, GTK_ORIENTATION_HORIZONTAL, g_value_get_boolean (value));
+ break;
+ case PROP_VHOMOGENEOUS_FOLDED:
+ hdy_leaflet_set_homogeneous (self, TRUE, GTK_ORIENTATION_VERTICAL, g_value_get_boolean (value));
+ break;
+ case PROP_HHOMOGENEOUS_UNFOLDED:
+ hdy_leaflet_set_homogeneous (self, FALSE, GTK_ORIENTATION_HORIZONTAL, g_value_get_boolean (value));
+ break;
+ case PROP_VHOMOGENEOUS_UNFOLDED:
+ hdy_leaflet_set_homogeneous (self, FALSE, GTK_ORIENTATION_VERTICAL, g_value_get_boolean (value));
+ break;
+ case PROP_VISIBLE_CHILD:
+ hdy_leaflet_set_visible_child (self, g_value_get_object (value));
+ break;
+ case PROP_VISIBLE_CHILD_NAME:
+ hdy_leaflet_set_visible_child_name (self, g_value_get_string (value));
+ break;
+ case PROP_TRANSITION_TYPE:
+ hdy_leaflet_set_transition_type (self, g_value_get_enum (value));
+ break;
+ case PROP_MODE_TRANSITION_DURATION:
+ hdy_leaflet_set_mode_transition_duration (self, g_value_get_uint (value));
+ break;
+ case PROP_CHILD_TRANSITION_DURATION:
+ hdy_leaflet_set_child_transition_duration (self, g_value_get_uint (value));
+ break;
+ case PROP_INTERPOLATE_SIZE:
+ hdy_leaflet_set_interpolate_size (self, g_value_get_boolean (value));
+ break;
+ case PROP_CAN_SWIPE_BACK:
+ hdy_leaflet_set_can_swipe_back (self, g_value_get_boolean (value));
+ break;
+ case PROP_CAN_SWIPE_FORWARD:
+ hdy_leaflet_set_can_swipe_forward (self, g_value_get_boolean (value));
+ break;
+ case PROP_ORIENTATION:
+ hdy_stackable_box_set_orientation (HDY_GET_HELPER (self), g_value_get_enum (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+hdy_leaflet_finalize (GObject *object)
+{
+ HdyLeaflet *self = HDY_LEAFLET (object);
+ HdyLeafletPrivate *priv = hdy_leaflet_get_instance_private (self);
+
+ g_clear_object (&priv->box);
+
+ G_OBJECT_CLASS (hdy_leaflet_parent_class)->finalize (object);
+}
+
+static void
+hdy_leaflet_get_child_property (GtkContainer *container,
+ GtkWidget *widget,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case CHILD_PROP_NAME:
+ g_value_set_string (value, hdy_stackable_box_get_child_name (HDY_GET_HELPER (container), widget));
+ break;
+
+ case CHILD_PROP_NAVIGATABLE:
+ g_value_set_boolean (value, hdy_stackable_box_get_child_navigatable (HDY_GET_HELPER (container), widget));
+ break;
+
+ default:
+ GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
+ break;
+ }
+}
+
+static void
+hdy_leaflet_set_child_property (GtkContainer *container,
+ GtkWidget *widget,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case CHILD_PROP_NAME:
+ hdy_stackable_box_set_child_name (HDY_GET_HELPER (container), widget, g_value_get_string (value));
+ gtk_container_child_notify_by_pspec (container, widget, pspec);
+ break;
+
+ case CHILD_PROP_NAVIGATABLE:
+ hdy_stackable_box_set_child_navigatable (HDY_GET_HELPER (container), widget, g_value_get_boolean (value));
+ gtk_container_child_notify_by_pspec (container, widget, pspec);
+ break;
+
+ default:
+ GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
+ break;
+ }
+}
+
+static void
+hdy_leaflet_realize (GtkWidget *widget)
+{
+ hdy_stackable_box_realize (HDY_GET_HELPER (widget));
+}
+
+static void
+hdy_leaflet_unrealize (GtkWidget *widget)
+{
+ hdy_stackable_box_unrealize (HDY_GET_HELPER (widget));
+}
+
+static void
+hdy_leaflet_map (GtkWidget *widget)
+{
+ hdy_stackable_box_map (HDY_GET_HELPER (widget));
+}
+
+static void
+hdy_leaflet_unmap (GtkWidget *widget)
+{
+ hdy_stackable_box_unmap (HDY_GET_HELPER (widget));
+}
+
+static void
+hdy_leaflet_switch_child (HdySwipeable *swipeable,
+ guint index,
+ gint64 duration)
+{
+ hdy_stackable_box_switch_child (HDY_GET_HELPER (swipeable), index, duration);
+}
+
+static HdySwipeTracker *
+hdy_leaflet_get_swipe_tracker (HdySwipeable *swipeable)
+{
+ return hdy_stackable_box_get_swipe_tracker (HDY_GET_HELPER (swipeable));
+}
+
+static gdouble
+hdy_leaflet_get_distance (HdySwipeable *swipeable)
+{
+ return hdy_stackable_box_get_distance (HDY_GET_HELPER (swipeable));
+}
+
+static gdouble *
+hdy_leaflet_get_snap_points (HdySwipeable *swipeable,
+ gint *n_snap_points)
+{
+ return hdy_stackable_box_get_snap_points (HDY_GET_HELPER (swipeable), n_snap_points);
+}
+
+static gdouble
+hdy_leaflet_get_progress (HdySwipeable *swipeable)
+{
+ return hdy_stackable_box_get_progress (HDY_GET_HELPER (swipeable));
+}
+
+static gdouble
+hdy_leaflet_get_cancel_progress (HdySwipeable *swipeable)
+{
+ return hdy_stackable_box_get_cancel_progress (HDY_GET_HELPER (swipeable));
+}
+
+static void
+hdy_leaflet_get_swipe_area (HdySwipeable *swipeable,
+ HdyNavigationDirection navigation_direction,
+ gboolean is_drag,
+ GdkRectangle *rect)
+{
+ hdy_stackable_box_get_swipe_area (HDY_GET_HELPER (swipeable), navigation_direction, is_drag, rect);
+}
+
+static void
+hdy_leaflet_class_init (HdyLeafletClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = (GtkWidgetClass*) klass;
+ GtkContainerClass *container_class = (GtkContainerClass*) klass;
+
+ object_class->get_property = hdy_leaflet_get_property;
+ object_class->set_property = hdy_leaflet_set_property;
+ object_class->finalize = hdy_leaflet_finalize;
+
+ widget_class->realize = hdy_leaflet_realize;
+ widget_class->unrealize = hdy_leaflet_unrealize;
+ widget_class->map = hdy_leaflet_map;
+ widget_class->unmap = hdy_leaflet_unmap;
+ widget_class->get_preferred_width = hdy_leaflet_get_preferred_width;
+ widget_class->get_preferred_height = hdy_leaflet_get_preferred_height;
+ widget_class->get_preferred_width_for_height = hdy_leaflet_get_preferred_width_for_height;
+ widget_class->get_preferred_height_for_width = hdy_leaflet_get_preferred_height_for_width;
+ widget_class->size_allocate = hdy_leaflet_size_allocate;
+ widget_class->draw = hdy_leaflet_draw;
+ widget_class->direction_changed = hdy_leaflet_direction_changed;
+
+ container_class->add = hdy_leaflet_add;
+ container_class->remove = hdy_leaflet_remove;
+ container_class->forall = hdy_leaflet_forall;
+ container_class->set_child_property = hdy_leaflet_set_child_property;
+ container_class->get_child_property = hdy_leaflet_get_child_property;
+ gtk_container_class_handle_border_width (container_class);
+
+ g_object_class_override_property (object_class,
+ PROP_ORIENTATION,
+ "orientation");
+
+ /**
+ * HdyLeaflet:folded:
+ *
+ * %TRUE if the leaflet is folded.
+ *
+ * The leaflet will be folded if the size allocated to it is smaller than the
+ * sum of the natural size of its children, it will be unfolded otherwise.
+ */
+ props[PROP_FOLDED] =
+ g_param_spec_boolean ("folded",
+ _("Folded"),
+ _("Whether the widget is folded"),
+ FALSE,
+ G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * HdyLeaflet:hhomogeneous_folded:
+ *
+ * %TRUE if the leaflet allocates the same width for all children when folded.
+ */
+ props[PROP_HHOMOGENEOUS_FOLDED] =
+ g_param_spec_boolean ("hhomogeneous-folded",
+ _("Horizontally homogeneous folded"),
+ _("Horizontally homogeneous sizing when the leaflet is folded"),
+ TRUE,
+ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * HdyLeaflet:vhomogeneous_folded:
+ *
+ * %TRUE if the leaflet allocates the same height for all children when folded.
+ */
+ props[PROP_VHOMOGENEOUS_FOLDED] =
+ g_param_spec_boolean ("vhomogeneous-folded",
+ _("Vertically homogeneous folded"),
+ _("Vertically homogeneous sizing when the leaflet is folded"),
+ TRUE,
+ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * HdyLeaflet:hhomogeneous_unfolded:
+ *
+ * %TRUE if the leaflet allocates the same width for all children when unfolded.
+ */
+ props[PROP_HHOMOGENEOUS_UNFOLDED] =
+ g_param_spec_boolean ("hhomogeneous-unfolded",
+ _("Box horizontally homogeneous"),
+ _("Horizontally homogeneous sizing when the leaflet is unfolded"),
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * HdyLeaflet:vhomogeneous_unfolded:
+ *
+ * %TRUE if the leaflet allocates the same height for all children when unfolded.
+ */
+ props[PROP_VHOMOGENEOUS_UNFOLDED] =
+ g_param_spec_boolean ("vhomogeneous-unfolded",
+ _("Box vertically homogeneous"),
+ _("Vertically homogeneous sizing when the leaflet is unfolded"),
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ props[PROP_VISIBLE_CHILD] =
+ g_param_spec_object ("visible-child",
+ _("Visible child"),
+ _("The widget currently visible when the leaflet is folded"),
+ GTK_TYPE_WIDGET,
+ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ props[PROP_VISIBLE_CHILD_NAME] =
+ g_param_spec_string ("visible-child-name",
+ _("Name of visible child"),
+ _("The name of the widget currently visible when the children are stacked"),
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * HdyLeaflet:transition-type:
+ *
+ * The type of animation that will be used for transitions between modes and
+ * children.
+ *
+ * The transition type can be changed without problems at runtime, so it is
+ * possible to change the animation based on the mode or child that is about
+ * to become current.
+ *
+ * Since: 0.0.12
+ */
+ props[PROP_TRANSITION_TYPE] =
+ g_param_spec_enum ("transition-type",
+ _("Transition type"),
+ _("The type of animation used to transition between modes and children"),
+ HDY_TYPE_LEAFLET_TRANSITION_TYPE, HDY_LEAFLET_TRANSITION_TYPE_OVER,
+ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ props[PROP_MODE_TRANSITION_DURATION] =
+ g_param_spec_uint ("mode-transition-duration",
+ _("Mode transition duration"),
+ _("The mode transition animation duration, in milliseconds"),
+ 0, G_MAXUINT, 250,
+ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ props[PROP_CHILD_TRANSITION_DURATION] =
+ g_param_spec_uint ("child-transition-duration",
+ _("Child transition duration"),
+ _("The child transition animation duration, in milliseconds"),
+ 0, G_MAXUINT, 200,
+ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ props[PROP_CHILD_TRANSITION_RUNNING] =
+ g_param_spec_boolean ("child-transition-running",
+ _("Child transition running"),
+ _("Whether or not the child transition is currently running"),
+ FALSE,
+ G_PARAM_READABLE);
+
+ props[PROP_INTERPOLATE_SIZE] =
+ g_param_spec_boolean ("interpolate-size",
+ _("Interpolate size"),
+ _("Whether or not the size should smoothly change when changing between differently sized children"),
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * HdyLeaflet:can-swipe-back:
+ *
+ * Whether or not the leaflet allows switching to the previous child that has
+ * 'navigatable' child property set to %TRUE via a swipe gesture.
+ *
+ * Since: 0.0.12
+ */
+ 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 to the previous child"),
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * HdyLeaflet:can-swipe-forward:
+ *
+ * Whether or not the leaflet allows switching to the next child that has
+ * 'navigatable' child property set to %TRUE via a swipe gesture.
+ *
+ * Since: 0.0.12
+ */
+ props[PROP_CAN_SWIPE_FORWARD] =
+ g_param_spec_boolean ("can-swipe-forward",
+ _("Can swipe forward"),
+ _("Whether or not swipe gesture can be used to switch to the next child"),
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+
+ g_object_class_install_properties (object_class, LAST_PROP, props);
+
+ child_props[CHILD_PROP_NAME] =
+ g_param_spec_string ("name",
+ _("Name"),
+ _("The name of the child page"),
+ NULL,
+ G_PARAM_READWRITE);
+
+ /**
+ * HdyLeaflet:navigatable:
+ *
+ * Whether the child can be navigated to when folded.
+ * If %FALSE, the child will be ignored by hdy_leaflet_get_adjacent_child(),
+ * hdy_leaflet_navigate(), and swipe gestures.
+ *
+ * This can be used used to prevent switching to widgets like separators.
+ *
+ * Since: 1.0
+ */
+ child_props[CHILD_PROP_NAVIGATABLE] =
+ g_param_spec_boolean ("navigatable",
+ _("Navigatable"),
+ _("Whether the child can be navigated to"),
+ TRUE,
+ G_PARAM_READWRITE);
+
+ gtk_container_class_install_child_properties (container_class, LAST_CHILD_PROP, child_props);
+
+ gtk_widget_class_set_accessible_role (widget_class, ATK_ROLE_PANEL);
+ gtk_widget_class_set_css_name (widget_class, "leaflet");
+}
+
+GtkWidget *
+hdy_leaflet_new (void)
+{
+ return g_object_new (HDY_TYPE_LEAFLET, NULL);
+}
+
+#define NOTIFY(func, prop) \
+static void \
+func (HdyLeaflet *self) { \
+ g_object_notify_by_pspec (G_OBJECT (self), props[prop]); \
+}
+
+NOTIFY (notify_folded_cb, PROP_FOLDED);
+NOTIFY (notify_hhomogeneous_folded_cb, PROP_HHOMOGENEOUS_FOLDED);
+NOTIFY (notify_vhomogeneous_folded_cb, PROP_VHOMOGENEOUS_FOLDED);
+NOTIFY (notify_hhomogeneous_unfolded_cb, PROP_HHOMOGENEOUS_UNFOLDED);
+NOTIFY (notify_vhomogeneous_unfolded_cb, PROP_VHOMOGENEOUS_UNFOLDED);
+NOTIFY (notify_visible_child_cb, PROP_VISIBLE_CHILD);
+NOTIFY (notify_visible_child_name_cb, PROP_VISIBLE_CHILD_NAME);
+NOTIFY (notify_transition_type_cb, PROP_TRANSITION_TYPE);
+NOTIFY (notify_mode_transition_duration_cb, PROP_MODE_TRANSITION_DURATION);
+NOTIFY (notify_child_transition_duration_cb, PROP_CHILD_TRANSITION_DURATION);
+NOTIFY (notify_child_transition_running_cb, PROP_CHILD_TRANSITION_RUNNING);
+NOTIFY (notify_interpolate_size_cb, PROP_INTERPOLATE_SIZE);
+NOTIFY (notify_can_swipe_back_cb, PROP_CAN_SWIPE_BACK);
+NOTIFY (notify_can_swipe_forward_cb, PROP_CAN_SWIPE_FORWARD);
+
+static void
+notify_orientation_cb (HdyLeaflet *self)
+{
+ g_object_notify (G_OBJECT (self), "orientation");
+}
+
+static void
+hdy_leaflet_init (HdyLeaflet *self)
+{
+ HdyLeafletPrivate *priv = hdy_leaflet_get_instance_private (self);
+
+ priv->box = hdy_stackable_box_new (GTK_CONTAINER (self),
+ GTK_CONTAINER_CLASS (hdy_leaflet_parent_class),
+ TRUE);
+
+ g_signal_connect_object (priv->box, "notify::folded", G_CALLBACK (notify_folded_cb), self, G_CONNECT_SWAPPED);
+ g_signal_connect_object (priv->box, "notify::hhomogeneous-folded", G_CALLBACK (notify_hhomogeneous_folded_cb), self, G_CONNECT_SWAPPED);
+ g_signal_connect_object (priv->box, "notify::vhomogeneous-folded", G_CALLBACK (notify_vhomogeneous_folded_cb), self, G_CONNECT_SWAPPED);
+ g_signal_connect_object (priv->box, "notify::hhomogeneous-unfolded", G_CALLBACK (notify_hhomogeneous_unfolded_cb), self, G_CONNECT_SWAPPED);
+ g_signal_connect_object (priv->box, "notify::vhomogeneous-unfolded", G_CALLBACK (notify_vhomogeneous_unfolded_cb), self, G_CONNECT_SWAPPED);
+ g_signal_connect_object (priv->box, "notify::visible-child", G_CALLBACK (notify_visible_child_cb), self, G_CONNECT_SWAPPED);
+ g_signal_connect_object (priv->box, "notify::visible-child-name", G_CALLBACK (notify_visible_child_name_cb), self, G_CONNECT_SWAPPED);
+ g_signal_connect_object (priv->box, "notify::transition-type", G_CALLBACK (notify_transition_type_cb), self, G_CONNECT_SWAPPED);
+ g_signal_connect_object (priv->box, "notify::mode-transition-duration", G_CALLBACK (notify_mode_transition_duration_cb), self, G_CONNECT_SWAPPED);
+ g_signal_connect_object (priv->box, "notify::child-transition-duration", G_CALLBACK (notify_child_transition_duration_cb), self, G_CONNECT_SWAPPED);
+ g_signal_connect_object (priv->box, "notify::child-transition-running", G_CALLBACK (notify_child_transition_running_cb), self, G_CONNECT_SWAPPED);
+ g_signal_connect_object (priv->box, "notify::interpolate-size", G_CALLBACK (notify_interpolate_size_cb), self, G_CONNECT_SWAPPED);
+ g_signal_connect_object (priv->box, "notify::can-swipe-back", G_CALLBACK (notify_can_swipe_back_cb), self, G_CONNECT_SWAPPED);
+ g_signal_connect_object (priv->box, "notify::can-swipe-forward", G_CALLBACK (notify_can_swipe_forward_cb), self, G_CONNECT_SWAPPED);
+ g_signal_connect_object (priv->box, "notify::orientation", G_CALLBACK (notify_orientation_cb), self, G_CONNECT_SWAPPED);
+}
+
+static void
+hdy_leaflet_swipeable_init (HdySwipeableInterface *iface)
+{
+ iface->switch_child = hdy_leaflet_switch_child;
+ iface->get_swipe_tracker = hdy_leaflet_get_swipe_tracker;
+ iface->get_distance = hdy_leaflet_get_distance;
+ iface->get_snap_points = hdy_leaflet_get_snap_points;
+ iface->get_progress = hdy_leaflet_get_progress;
+ iface->get_cancel_progress = hdy_leaflet_get_cancel_progress;
+ iface->get_swipe_area = hdy_leaflet_get_swipe_area;
+}