1725 lines
54 KiB
Diff
1725 lines
54 KiB
Diff
From: Adrien Plazas <kekun.plazas@laposte.net>
|
|
Date: Thu, 10 Sep 2020 10:46:42 +0200
|
|
Subject: Add GtkHdySqueezer
|
|
|
|
This is imported from HdySqueezer from libhandy 1.0.2.
|
|
---
|
|
gtk/gtkprivate.h | 1 +
|
|
gtk/hdy-cairo-private.h | 21 +
|
|
gtk/hdy-squeezer-private.h | 63 ++
|
|
gtk/hdy-squeezer.c | 1573 ++++++++++++++++++++++++++++++++++++++++++++
|
|
gtk/meson.build | 3 +
|
|
5 files changed, 1661 insertions(+)
|
|
create mode 100644 gtk/hdy-cairo-private.h
|
|
create mode 100644 gtk/hdy-squeezer-private.h
|
|
create mode 100644 gtk/hdy-squeezer.c
|
|
|
|
diff --git a/gtk/gtkprivate.h b/gtk/gtkprivate.h
|
|
index 473bb99..68f0f8d 100644
|
|
--- a/gtk/gtkprivate.h
|
|
+++ b/gtk/gtkprivate.h
|
|
@@ -30,6 +30,7 @@
|
|
|
|
#include "gtkcsstypesprivate.h"
|
|
#include "gtktexthandleprivate.h"
|
|
+#include "hdy-squeezer-private.h"
|
|
#include "hdy-view-switcher-private.h"
|
|
|
|
G_BEGIN_DECLS
|
|
diff --git a/gtk/hdy-cairo-private.h b/gtk/hdy-cairo-private.h
|
|
new file mode 100644
|
|
index 0000000..4226ec8
|
|
--- /dev/null
|
|
+++ b/gtk/hdy-cairo-private.h
|
|
@@ -0,0 +1,21 @@
|
|
+/*
|
|
+ * Copyright (C) 2020 Purism SPC
|
|
+ *
|
|
+ * SPDX-License-Identifier: LGPL-2.1+
|
|
+ */
|
|
+
|
|
+#pragma once
|
|
+
|
|
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
|
+#error "Only <gtk/gtk.h> can be included directly."
|
|
+#endif
|
|
+
|
|
+#include <glib.h>
|
|
+#include <cairo/cairo.h>
|
|
+
|
|
+G_BEGIN_DECLS
|
|
+
|
|
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (cairo_t, cairo_destroy)
|
|
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (cairo_surface_t, cairo_surface_destroy)
|
|
+
|
|
+G_END_DECLS
|
|
diff --git a/gtk/hdy-squeezer-private.h b/gtk/hdy-squeezer-private.h
|
|
new file mode 100644
|
|
index 0000000..9224230
|
|
--- /dev/null
|
|
+++ b/gtk/hdy-squeezer-private.h
|
|
@@ -0,0 +1,63 @@
|
|
+/*
|
|
+ * Copyright (C) 2019 Purism SPC
|
|
+ *
|
|
+ * SPDX-License-Identifier: LGPL-2.1+
|
|
+ */
|
|
+
|
|
+#pragma once
|
|
+
|
|
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
|
+#error "Only <gtk/gtk.h> can be included directly."
|
|
+#endif
|
|
+
|
|
+#include "gtkcontainer.h"
|
|
+#include "gtkenums.h"
|
|
+
|
|
+G_BEGIN_DECLS
|
|
+
|
|
+#define GTK_TYPE_HDY_SQUEEZER (gtk_hdy_squeezer_get_type ())
|
|
+
|
|
+G_DECLARE_FINAL_TYPE (GtkHdySqueezer, gtk_hdy_squeezer, GTK, HDY_SQUEEZER, GtkContainer)
|
|
+
|
|
+typedef enum {
|
|
+ GTK_HDY_SQUEEZER_TRANSITION_TYPE_NONE,
|
|
+ GTK_HDY_SQUEEZER_TRANSITION_TYPE_CROSSFADE,
|
|
+} GtkHdySqueezerTransitionType;
|
|
+
|
|
+GtkWidget *gtk_hdy_squeezer_new (void);
|
|
+
|
|
+gboolean gtk_hdy_squeezer_get_homogeneous (GtkHdySqueezer *self);
|
|
+void gtk_hdy_squeezer_set_homogeneous (GtkHdySqueezer *self,
|
|
+ gboolean homogeneous);
|
|
+
|
|
+guint gtk_hdy_squeezer_get_transition_duration (GtkHdySqueezer *self);
|
|
+void gtk_hdy_squeezer_set_transition_duration (GtkHdySqueezer *self,
|
|
+ guint duration);
|
|
+
|
|
+GtkHdySqueezerTransitionType gtk_hdy_squeezer_get_transition_type (GtkHdySqueezer *self);
|
|
+void gtk_hdy_squeezer_set_transition_type (GtkHdySqueezer *self,
|
|
+ GtkHdySqueezerTransitionType transition);
|
|
+
|
|
+gboolean gtk_hdy_squeezer_get_transition_running (GtkHdySqueezer *self);
|
|
+
|
|
+gboolean gtk_hdy_squeezer_get_interpolate_size (GtkHdySqueezer *self);
|
|
+void gtk_hdy_squeezer_set_interpolate_size (GtkHdySqueezer *self,
|
|
+ gboolean interpolate_size);
|
|
+
|
|
+GtkWidget *gtk_hdy_squeezer_get_visible_child (GtkHdySqueezer *self);
|
|
+
|
|
+gboolean gtk_hdy_squeezer_get_child_enabled (GtkHdySqueezer *self,
|
|
+ GtkWidget *child);
|
|
+void gtk_hdy_squeezer_set_child_enabled (GtkHdySqueezer *self,
|
|
+ GtkWidget *child,
|
|
+ gboolean enabled);
|
|
+
|
|
+gfloat gtk_hdy_squeezer_get_xalign (GtkHdySqueezer *self);
|
|
+void gtk_hdy_squeezer_set_xalign (GtkHdySqueezer *self,
|
|
+ gfloat xalign);
|
|
+
|
|
+gfloat gtk_hdy_squeezer_get_yalign (GtkHdySqueezer *self);
|
|
+void gtk_hdy_squeezer_set_yalign (GtkHdySqueezer *self,
|
|
+ gfloat yalign);
|
|
+
|
|
+G_END_DECLS
|
|
diff --git a/gtk/hdy-squeezer.c b/gtk/hdy-squeezer.c
|
|
new file mode 100644
|
|
index 0000000..b36db93
|
|
--- /dev/null
|
|
+++ b/gtk/hdy-squeezer.c
|
|
@@ -0,0 +1,1573 @@
|
|
+/*
|
|
+ * Copyright (C) 2013 Red Hat, Inc.
|
|
+ * Copyright (C) 2019 Purism SPC
|
|
+ *
|
|
+ * Author: Alexander Larsson <alexl@redhat.com>
|
|
+ * Author: Adrien Plazas <adrien.plazas@puri.sm>
|
|
+ *
|
|
+ * SPDX-License-Identifier: LGPL-2.1+
|
|
+ */
|
|
+
|
|
+/*
|
|
+ * Forked from the GTK+ 3.24.2 GtkStack widget initially written by Alexander
|
|
+ * Larsson, and heavily modified for libhandy by Adrien Plazas on behalf of
|
|
+ * Purism SPC 2019.
|
|
+ */
|
|
+
|
|
+#include "config.h"
|
|
+#include <glib/gi18n-lib.h>
|
|
+
|
|
+#include "hdy-squeezer-private.h"
|
|
+
|
|
+#include "gtkorientable.h"
|
|
+#include "gtkprivatetypebuiltins.h"
|
|
+#include "gtkprogresstrackerprivate.h"
|
|
+#include "gtkrender.h"
|
|
+#include "gtkstylecontext.h"
|
|
+#include "gtktypebuiltins.h"
|
|
+#include "gtkwindow.h"
|
|
+#include "hdy-animation-private.h"
|
|
+#include "hdy-cairo-private.h"
|
|
+#include "hdy-css-private.h"
|
|
+
|
|
+/**
|
|
+ * SECTION:hdy-squeezer
|
|
+ * @short_description: A best fit container.
|
|
+ * @Title: GtkHdySqueezer
|
|
+ *
|
|
+ * The GtkHdySqueezer widget is a container which only shows the first of its
|
|
+ * children that fits in the available size. It is convenient to offer different
|
|
+ * widgets to represent the same data with different levels of detail, making
|
|
+ * the widget seem to squeeze itself to fit in the available space.
|
|
+ *
|
|
+ * Transitions between children can be animated as fades. This can be controlled
|
|
+ * with gtk_hdy_squeezer_set_transition_type().
|
|
+ *
|
|
+ * # CSS nodes
|
|
+ *
|
|
+ * #GtkHdySqueezer has a single CSS node with name squeezer.
|
|
+ */
|
|
+
|
|
+enum {
|
|
+ PROP_0,
|
|
+ PROP_HOMOGENEOUS,
|
|
+ PROP_VISIBLE_CHILD,
|
|
+ PROP_TRANSITION_DURATION,
|
|
+ PROP_TRANSITION_TYPE,
|
|
+ PROP_TRANSITION_RUNNING,
|
|
+ PROP_INTERPOLATE_SIZE,
|
|
+ PROP_XALIGN,
|
|
+ PROP_YALIGN,
|
|
+
|
|
+ /* Overridden properties */
|
|
+ PROP_ORIENTATION,
|
|
+
|
|
+ LAST_PROP = PROP_YALIGN + 1,
|
|
+};
|
|
+
|
|
+enum {
|
|
+ CHILD_PROP_0,
|
|
+ CHILD_PROP_ENABLED,
|
|
+
|
|
+ LAST_CHILD_PROP,
|
|
+};
|
|
+
|
|
+typedef struct {
|
|
+ GtkWidget *widget;
|
|
+ gboolean enabled;
|
|
+ GtkWidget *last_focus;
|
|
+} GtkHdySqueezerChildInfo;
|
|
+
|
|
+struct _GtkHdySqueezer
|
|
+{
|
|
+ GtkContainer parent_instance;
|
|
+
|
|
+ GList *children;
|
|
+
|
|
+ GdkWindow* bin_window;
|
|
+ GdkWindow* view_window;
|
|
+
|
|
+ GtkHdySqueezerChildInfo *visible_child;
|
|
+
|
|
+ gboolean homogeneous;
|
|
+
|
|
+ GtkHdySqueezerTransitionType transition_type;
|
|
+ guint transition_duration;
|
|
+
|
|
+ GtkHdySqueezerChildInfo *last_visible_child;
|
|
+ cairo_surface_t *last_visible_surface;
|
|
+ GtkAllocation last_visible_surface_allocation;
|
|
+ guint tick_id;
|
|
+ GtkProgressTracker tracker;
|
|
+ gboolean first_frame_skipped;
|
|
+
|
|
+ gint last_visible_widget_width;
|
|
+ gint last_visible_widget_height;
|
|
+
|
|
+ GtkHdySqueezerTransitionType active_transition_type;
|
|
+
|
|
+ gboolean interpolate_size;
|
|
+
|
|
+ gfloat xalign;
|
|
+ gfloat yalign;
|
|
+
|
|
+ GtkOrientation orientation;
|
|
+};
|
|
+
|
|
+static GParamSpec *props[LAST_PROP];
|
|
+static GParamSpec *child_props[LAST_CHILD_PROP];
|
|
+
|
|
+G_DEFINE_TYPE_WITH_CODE (GtkHdySqueezer, gtk_hdy_squeezer, GTK_TYPE_CONTAINER,
|
|
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL))
|
|
+
|
|
+static GtkOrientation
|
|
+get_orientation (GtkHdySqueezer *self)
|
|
+{
|
|
+ return self->orientation;
|
|
+}
|
|
+
|
|
+static void
|
|
+set_orientation (GtkHdySqueezer *self,
|
|
+ GtkOrientation orientation)
|
|
+{
|
|
+ if (self->orientation == orientation)
|
|
+ return;
|
|
+
|
|
+ self->orientation = orientation;
|
|
+ gtk_widget_queue_resize (GTK_WIDGET (self));
|
|
+ g_object_notify (G_OBJECT (self), "orientation");
|
|
+}
|
|
+
|
|
+static GtkHdySqueezerChildInfo *
|
|
+find_child_info_for_widget (GtkHdySqueezer *self,
|
|
+ GtkWidget *child)
|
|
+{
|
|
+ GtkHdySqueezerChildInfo *info;
|
|
+ GList *l;
|
|
+
|
|
+ for (l = self->children; l != NULL; l = l->next) {
|
|
+ info = l->data;
|
|
+ if (info->widget == child)
|
|
+ return info;
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static void
|
|
+gtk_hdy_squeezer_progress_updated (GtkHdySqueezer *self)
|
|
+{
|
|
+ gtk_widget_queue_draw (GTK_WIDGET (self));
|
|
+
|
|
+ if (!self->homogeneous)
|
|
+ gtk_widget_queue_resize (GTK_WIDGET (self));
|
|
+
|
|
+ if (gtk_progress_tracker_get_state (&self->tracker) == GTK_PROGRESS_STATE_AFTER) {
|
|
+ if (self->last_visible_surface != NULL) {
|
|
+ cairo_surface_destroy (self->last_visible_surface);
|
|
+ self->last_visible_surface = NULL;
|
|
+ }
|
|
+
|
|
+ if (self->last_visible_child != NULL) {
|
|
+ gtk_widget_set_child_visible (self->last_visible_child->widget, FALSE);
|
|
+ self->last_visible_child = NULL;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+gtk_hdy_squeezer_transition_cb (GtkWidget *widget,
|
|
+ GdkFrameClock *frame_clock,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ GtkHdySqueezer *self = GTK_HDY_SQUEEZER (widget);
|
|
+
|
|
+ if (self->first_frame_skipped) {
|
|
+ gtk_progress_tracker_advance_frame (&self->tracker,
|
|
+ gdk_frame_clock_get_frame_time (frame_clock));
|
|
+ } else {
|
|
+ self->first_frame_skipped = TRUE;
|
|
+ }
|
|
+
|
|
+ /* Finish the animation early if the widget isn't mapped anymore. */
|
|
+ if (!gtk_widget_get_mapped (widget))
|
|
+ gtk_progress_tracker_finish (&self->tracker);
|
|
+
|
|
+ gtk_hdy_squeezer_progress_updated (GTK_HDY_SQUEEZER (widget));
|
|
+
|
|
+ if (gtk_progress_tracker_get_state (&self->tracker) == GTK_PROGRESS_STATE_AFTER) {
|
|
+ self->tick_id = 0;
|
|
+ g_object_notify_by_pspec (G_OBJECT (self), props[PROP_TRANSITION_RUNNING]);
|
|
+
|
|
+ return FALSE;
|
|
+ }
|
|
+
|
|
+ return TRUE;
|
|
+}
|
|
+
|
|
+static void
|
|
+gtk_hdy_squeezer_schedule_ticks (GtkHdySqueezer *self)
|
|
+{
|
|
+ if (self->tick_id == 0) {
|
|
+ self->tick_id =
|
|
+ gtk_widget_add_tick_callback (GTK_WIDGET (self), gtk_hdy_squeezer_transition_cb, self, NULL);
|
|
+ g_object_notify_by_pspec (G_OBJECT (self), props[PROP_TRANSITION_RUNNING]);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+gtk_hdy_squeezer_unschedule_ticks (GtkHdySqueezer *self)
|
|
+{
|
|
+ if (self->tick_id != 0) {
|
|
+ gtk_widget_remove_tick_callback (GTK_WIDGET (self), self->tick_id);
|
|
+ self->tick_id = 0;
|
|
+ g_object_notify_by_pspec (G_OBJECT (self), props[PROP_TRANSITION_RUNNING]);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+gtk_hdy_squeezer_start_transition (GtkHdySqueezer *self,
|
|
+ GtkHdySqueezerTransitionType transition_type,
|
|
+ guint transition_duration)
|
|
+{
|
|
+ GtkWidget *widget = GTK_WIDGET (self);
|
|
+
|
|
+ if (gtk_widget_get_mapped (widget) &&
|
|
+ gtk_hdy_get_enable_animations (widget) &&
|
|
+ transition_type != GTK_HDY_SQUEEZER_TRANSITION_TYPE_NONE &&
|
|
+ transition_duration != 0 &&
|
|
+ self->last_visible_child != NULL) {
|
|
+ self->active_transition_type = transition_type;
|
|
+ self->first_frame_skipped = FALSE;
|
|
+ gtk_hdy_squeezer_schedule_ticks (self);
|
|
+ gtk_progress_tracker_start (&self->tracker,
|
|
+ self->transition_duration * 1000,
|
|
+ 0,
|
|
+ 1.0);
|
|
+ } else {
|
|
+ gtk_hdy_squeezer_unschedule_ticks (self);
|
|
+ self->active_transition_type = GTK_HDY_SQUEEZER_TRANSITION_TYPE_NONE;
|
|
+ gtk_progress_tracker_finish (&self->tracker);
|
|
+ }
|
|
+
|
|
+ gtk_hdy_squeezer_progress_updated (GTK_HDY_SQUEEZER (widget));
|
|
+}
|
|
+
|
|
+static void
|
|
+set_visible_child (GtkHdySqueezer *self,
|
|
+ GtkHdySqueezerChildInfo *child_info,
|
|
+ GtkHdySqueezerTransitionType transition_type,
|
|
+ guint transition_duration)
|
|
+{
|
|
+ GtkHdySqueezerChildInfo *info;
|
|
+ GtkWidget *widget = GTK_WIDGET (self);
|
|
+ GList *l;
|
|
+ GtkWidget *toplevel;
|
|
+ GtkWidget *focus;
|
|
+ gboolean contains_focus = FALSE;
|
|
+
|
|
+ /* If we are being destroyed, do not bother with transitions and
|
|
+ * notifications.
|
|
+ */
|
|
+ if (gtk_widget_in_destruction (widget))
|
|
+ return;
|
|
+
|
|
+ /* If none, pick the first visible. */
|
|
+ if (child_info == NULL) {
|
|
+ for (l = self->children; l != NULL; l = l->next) {
|
|
+ info = l->data;
|
|
+ if (gtk_widget_get_visible (info->widget)) {
|
|
+ child_info = info;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (child_info == self->visible_child)
|
|
+ return;
|
|
+
|
|
+ toplevel = gtk_widget_get_toplevel (widget);
|
|
+ if (GTK_IS_WINDOW (toplevel)) {
|
|
+ focus = gtk_window_get_focus (GTK_WINDOW (toplevel));
|
|
+ if (focus &&
|
|
+ self->visible_child &&
|
|
+ self->visible_child->widget &&
|
|
+ gtk_widget_is_ancestor (focus, self->visible_child->widget)) {
|
|
+ contains_focus = TRUE;
|
|
+
|
|
+ if (self->visible_child->last_focus)
|
|
+ g_object_remove_weak_pointer (G_OBJECT (self->visible_child->last_focus),
|
|
+ (gpointer *)&self->visible_child->last_focus);
|
|
+ self->visible_child->last_focus = focus;
|
|
+ g_object_add_weak_pointer (G_OBJECT (self->visible_child->last_focus),
|
|
+ (gpointer *)&self->visible_child->last_focus);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (self->last_visible_child != NULL)
|
|
+ gtk_widget_set_child_visible (self->last_visible_child->widget, FALSE);
|
|
+ self->last_visible_child = NULL;
|
|
+
|
|
+ if (self->last_visible_surface != NULL)
|
|
+ cairo_surface_destroy (self->last_visible_surface);
|
|
+ self->last_visible_surface = NULL;
|
|
+
|
|
+ if (self->visible_child && self->visible_child->widget) {
|
|
+ if (gtk_widget_is_visible (widget)) {
|
|
+ GtkAllocation allocation;
|
|
+
|
|
+ self->last_visible_child = self->visible_child;
|
|
+ gtk_widget_get_allocated_size (self->last_visible_child->widget, &allocation, NULL);
|
|
+ self->last_visible_widget_width = allocation.width;
|
|
+ self->last_visible_widget_height = allocation.height;
|
|
+ } else {
|
|
+ gtk_widget_set_child_visible (self->visible_child->widget, FALSE);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ self->visible_child = child_info;
|
|
+
|
|
+ if (child_info) {
|
|
+ gtk_widget_set_child_visible (child_info->widget, TRUE);
|
|
+
|
|
+ if (contains_focus) {
|
|
+ if (child_info->last_focus)
|
|
+ gtk_widget_grab_focus (child_info->last_focus);
|
|
+ else
|
|
+ gtk_widget_child_focus (child_info->widget, GTK_DIR_TAB_FORWARD);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (self->homogeneous)
|
|
+ gtk_widget_queue_allocate (widget);
|
|
+ else
|
|
+ gtk_widget_queue_resize (widget);
|
|
+
|
|
+ g_object_notify_by_pspec (G_OBJECT (self), props[PROP_VISIBLE_CHILD]);
|
|
+
|
|
+ gtk_hdy_squeezer_start_transition (self, transition_type, transition_duration);
|
|
+}
|
|
+
|
|
+static void
|
|
+stack_child_visibility_notify_cb (GObject *obj,
|
|
+ GParamSpec *pspec,
|
|
+ gpointer user_data)
|
|
+{
|
|
+ GtkHdySqueezer *self = GTK_HDY_SQUEEZER (user_data);
|
|
+ GtkWidget *child = GTK_WIDGET (obj);
|
|
+ GtkHdySqueezerChildInfo *child_info;
|
|
+
|
|
+ child_info = find_child_info_for_widget (self, child);
|
|
+
|
|
+ if (self->visible_child == NULL &&
|
|
+ gtk_widget_get_visible (child))
|
|
+ set_visible_child (self, child_info, self->transition_type, self->transition_duration);
|
|
+ else if (self->visible_child == child_info &&
|
|
+ !gtk_widget_get_visible (child))
|
|
+ set_visible_child (self, NULL, self->transition_type, self->transition_duration);
|
|
+
|
|
+ if (child_info == self->last_visible_child) {
|
|
+ gtk_widget_set_child_visible (self->last_visible_child->widget, FALSE);
|
|
+ self->last_visible_child = NULL;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+gtk_hdy_squeezer_add (GtkContainer *container,
|
|
+ GtkWidget *child)
|
|
+{
|
|
+ GtkHdySqueezer *self = GTK_HDY_SQUEEZER (container);
|
|
+ GtkHdySqueezerChildInfo *child_info;
|
|
+
|
|
+ g_return_if_fail (child != NULL);
|
|
+
|
|
+ child_info = g_slice_new (GtkHdySqueezerChildInfo);
|
|
+ child_info->widget = child;
|
|
+ child_info->enabled = TRUE;
|
|
+ child_info->last_focus = NULL;
|
|
+
|
|
+ self->children = g_list_append (self->children, child_info);
|
|
+
|
|
+ gtk_widget_set_child_visible (child, FALSE);
|
|
+ gtk_widget_set_parent_window (child, self->bin_window);
|
|
+ gtk_widget_set_parent (child, GTK_WIDGET (self));
|
|
+
|
|
+ if (self->bin_window != NULL) {
|
|
+ gdk_window_set_events (self->bin_window,
|
|
+ gdk_window_get_events (self->bin_window) |
|
|
+ gtk_widget_get_events (child));
|
|
+ }
|
|
+
|
|
+ g_signal_connect (child, "notify::visible",
|
|
+ G_CALLBACK (stack_child_visibility_notify_cb), self);
|
|
+
|
|
+ if (self->visible_child == NULL &&
|
|
+ gtk_widget_get_visible (child))
|
|
+ set_visible_child (self, child_info, self->transition_type, self->transition_duration);
|
|
+
|
|
+ if (self->visible_child == child_info)
|
|
+ gtk_widget_queue_resize (GTK_WIDGET (self));
|
|
+}
|
|
+
|
|
+static void
|
|
+gtk_hdy_squeezer_remove (GtkContainer *container,
|
|
+ GtkWidget *child)
|
|
+{
|
|
+ GtkHdySqueezer *self = GTK_HDY_SQUEEZER (container);
|
|
+ GtkHdySqueezerChildInfo *child_info;
|
|
+ gboolean was_visible;
|
|
+
|
|
+ child_info = find_child_info_for_widget (self, child);
|
|
+ if (child_info == NULL)
|
|
+ return;
|
|
+
|
|
+ self->children = g_list_remove (self->children, child_info);
|
|
+
|
|
+ g_signal_handlers_disconnect_by_func (child,
|
|
+ stack_child_visibility_notify_cb,
|
|
+ self);
|
|
+
|
|
+ was_visible = gtk_widget_get_visible (child);
|
|
+
|
|
+ child_info->widget = NULL;
|
|
+
|
|
+ if (self->visible_child == child_info)
|
|
+ set_visible_child (self, NULL, self->transition_type, self->transition_duration);
|
|
+
|
|
+ if (self->last_visible_child == child_info)
|
|
+ self->last_visible_child = NULL;
|
|
+
|
|
+ gtk_widget_unparent (child);
|
|
+
|
|
+ if (child_info->last_focus)
|
|
+ g_object_remove_weak_pointer (G_OBJECT (child_info->last_focus),
|
|
+ (gpointer *)&child_info->last_focus);
|
|
+
|
|
+ g_slice_free (GtkHdySqueezerChildInfo, child_info);
|
|
+
|
|
+ if (self->homogeneous && was_visible)
|
|
+ gtk_widget_queue_resize (GTK_WIDGET (self));
|
|
+}
|
|
+
|
|
+static void
|
|
+gtk_hdy_squeezer_get_property (GObject *object,
|
|
+ guint property_id,
|
|
+ GValue *value,
|
|
+ GParamSpec *pspec)
|
|
+{
|
|
+ GtkHdySqueezer *self = GTK_HDY_SQUEEZER (object);
|
|
+
|
|
+ switch (property_id) {
|
|
+ case PROP_HOMOGENEOUS:
|
|
+ g_value_set_boolean (value, gtk_hdy_squeezer_get_homogeneous (self));
|
|
+ break;
|
|
+ case PROP_VISIBLE_CHILD:
|
|
+ g_value_set_object (value, gtk_hdy_squeezer_get_visible_child (self));
|
|
+ break;
|
|
+ case PROP_TRANSITION_DURATION:
|
|
+ g_value_set_uint (value, gtk_hdy_squeezer_get_transition_duration (self));
|
|
+ break;
|
|
+ case PROP_TRANSITION_TYPE:
|
|
+ g_value_set_enum (value, gtk_hdy_squeezer_get_transition_type (self));
|
|
+ break;
|
|
+ case PROP_TRANSITION_RUNNING:
|
|
+ g_value_set_boolean (value, gtk_hdy_squeezer_get_transition_running (self));
|
|
+ break;
|
|
+ case PROP_INTERPOLATE_SIZE:
|
|
+ g_value_set_boolean (value, gtk_hdy_squeezer_get_interpolate_size (self));
|
|
+ break;
|
|
+ case PROP_XALIGN:
|
|
+ g_value_set_float (value, gtk_hdy_squeezer_get_xalign (self));
|
|
+ break;
|
|
+ case PROP_YALIGN:
|
|
+ g_value_set_float (value, gtk_hdy_squeezer_get_yalign (self));
|
|
+ break;
|
|
+ case PROP_ORIENTATION:
|
|
+ g_value_set_enum (value, get_orientation (self));
|
|
+ break;
|
|
+ default:
|
|
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+gtk_hdy_squeezer_set_property (GObject *object,
|
|
+ guint property_id,
|
|
+ const GValue *value,
|
|
+ GParamSpec *pspec)
|
|
+{
|
|
+ GtkHdySqueezer *self = GTK_HDY_SQUEEZER (object);
|
|
+
|
|
+ switch (property_id) {
|
|
+ case PROP_HOMOGENEOUS:
|
|
+ gtk_hdy_squeezer_set_homogeneous (self, g_value_get_boolean (value));
|
|
+ break;
|
|
+ case PROP_TRANSITION_DURATION:
|
|
+ gtk_hdy_squeezer_set_transition_duration (self, g_value_get_uint (value));
|
|
+ break;
|
|
+ case PROP_TRANSITION_TYPE:
|
|
+ gtk_hdy_squeezer_set_transition_type (self, g_value_get_enum (value));
|
|
+ break;
|
|
+ case PROP_INTERPOLATE_SIZE:
|
|
+ gtk_hdy_squeezer_set_interpolate_size (self, g_value_get_boolean (value));
|
|
+ break;
|
|
+ case PROP_XALIGN:
|
|
+ gtk_hdy_squeezer_set_xalign (self, g_value_get_float (value));
|
|
+ break;
|
|
+ case PROP_YALIGN:
|
|
+ gtk_hdy_squeezer_set_yalign (self, g_value_get_float (value));
|
|
+ break;
|
|
+ case PROP_ORIENTATION:
|
|
+ set_orientation (self, g_value_get_enum (value));
|
|
+ break;
|
|
+ default:
|
|
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+gtk_hdy_squeezer_realize (GtkWidget *widget)
|
|
+{
|
|
+ GtkHdySqueezer *self = GTK_HDY_SQUEEZER (widget);
|
|
+ GtkAllocation allocation;
|
|
+ GdkWindowAttr attributes = { 0 };
|
|
+ GdkWindowAttributesType attributes_mask;
|
|
+ GtkHdySqueezerChildInfo *info;
|
|
+ GList *l;
|
|
+
|
|
+ gtk_widget_set_realized (widget, TRUE);
|
|
+ gtk_widget_set_window (widget, g_object_ref (gtk_widget_get_parent_window (widget)));
|
|
+
|
|
+ gtk_widget_get_allocation (widget, &allocation);
|
|
+
|
|
+ attributes.x = allocation.x;
|
|
+ attributes.y = allocation.y;
|
|
+ attributes.width = allocation.width;
|
|
+ attributes.height = allocation.height;
|
|
+ attributes.window_type = GDK_WINDOW_CHILD;
|
|
+ attributes.wclass = GDK_INPUT_OUTPUT;
|
|
+ attributes.visual = gtk_widget_get_visual (widget);
|
|
+ attributes.event_mask =
|
|
+ gtk_widget_get_events (widget);
|
|
+ attributes_mask = (GDK_WA_X | GDK_WA_Y) | GDK_WA_VISUAL;
|
|
+
|
|
+ self->view_window =
|
|
+ gdk_window_new (gtk_widget_get_window (GTK_WIDGET (self)),
|
|
+ &attributes, attributes_mask);
|
|
+ gtk_widget_register_window (widget, self->view_window);
|
|
+
|
|
+ attributes.x = 0;
|
|
+ attributes.y = 0;
|
|
+ attributes.width = allocation.width;
|
|
+ attributes.height = allocation.height;
|
|
+
|
|
+ for (l = self->children; l != NULL; l = l->next) {
|
|
+ info = l->data;
|
|
+ attributes.event_mask |= gtk_widget_get_events (info->widget);
|
|
+ }
|
|
+
|
|
+ self->bin_window =
|
|
+ gdk_window_new (self->view_window, &attributes, attributes_mask);
|
|
+ gtk_widget_register_window (widget, self->bin_window);
|
|
+
|
|
+ for (l = self->children; l != NULL; l = l->next) {
|
|
+ info = l->data;
|
|
+
|
|
+ gtk_widget_set_parent_window (info->widget, self->bin_window);
|
|
+ }
|
|
+
|
|
+ gdk_window_show (self->bin_window);
|
|
+}
|
|
+
|
|
+static void
|
|
+gtk_hdy_squeezer_unrealize (GtkWidget *widget)
|
|
+{
|
|
+ GtkHdySqueezer *self = GTK_HDY_SQUEEZER (widget);
|
|
+
|
|
+ gtk_widget_unregister_window (widget, self->bin_window);
|
|
+ gdk_window_destroy (self->bin_window);
|
|
+ self->bin_window = NULL;
|
|
+ gtk_widget_unregister_window (widget, self->view_window);
|
|
+ gdk_window_destroy (self->view_window);
|
|
+ self->view_window = NULL;
|
|
+
|
|
+ GTK_WIDGET_CLASS (gtk_hdy_squeezer_parent_class)->unrealize (widget);
|
|
+}
|
|
+
|
|
+static void
|
|
+gtk_hdy_squeezer_map (GtkWidget *widget)
|
|
+{
|
|
+ GtkHdySqueezer *self = GTK_HDY_SQUEEZER (widget);
|
|
+
|
|
+ GTK_WIDGET_CLASS (gtk_hdy_squeezer_parent_class)->map (widget);
|
|
+
|
|
+ gdk_window_show (self->view_window);
|
|
+}
|
|
+
|
|
+static void
|
|
+gtk_hdy_squeezer_unmap (GtkWidget *widget)
|
|
+{
|
|
+ GtkHdySqueezer *self = GTK_HDY_SQUEEZER (widget);
|
|
+
|
|
+ gdk_window_hide (self->view_window);
|
|
+
|
|
+ GTK_WIDGET_CLASS (gtk_hdy_squeezer_parent_class)->unmap (widget);
|
|
+}
|
|
+
|
|
+static void
|
|
+gtk_hdy_squeezer_forall (GtkContainer *container,
|
|
+ gboolean include_internals,
|
|
+ GtkCallback callback,
|
|
+ gpointer callback_data)
|
|
+{
|
|
+ GtkHdySqueezer *self = GTK_HDY_SQUEEZER (container);
|
|
+ GtkHdySqueezerChildInfo *child_info;
|
|
+ GList *l;
|
|
+
|
|
+ l = self->children;
|
|
+ while (l) {
|
|
+ child_info = l->data;
|
|
+ l = l->next;
|
|
+
|
|
+ (* callback) (child_info->widget, callback_data);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+gtk_hdy_squeezer_compute_expand (GtkWidget *widget,
|
|
+ gboolean *hexpand_p,
|
|
+ gboolean *vexpand_p)
|
|
+{
|
|
+ GtkHdySqueezer *self = GTK_HDY_SQUEEZER (widget);
|
|
+ gboolean hexpand, vexpand;
|
|
+ GtkHdySqueezerChildInfo *child_info;
|
|
+ GtkWidget *child;
|
|
+ GList *l;
|
|
+
|
|
+ hexpand = FALSE;
|
|
+ vexpand = FALSE;
|
|
+ for (l = self->children; l != NULL; l = l->next) {
|
|
+ child_info = l->data;
|
|
+ child = child_info->widget;
|
|
+
|
|
+ if (!hexpand &&
|
|
+ gtk_widget_compute_expand (child, GTK_ORIENTATION_HORIZONTAL))
|
|
+ hexpand = TRUE;
|
|
+
|
|
+ if (!vexpand &&
|
|
+ gtk_widget_compute_expand (child, GTK_ORIENTATION_VERTICAL))
|
|
+ vexpand = TRUE;
|
|
+
|
|
+ if (hexpand && vexpand)
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ *hexpand_p = hexpand;
|
|
+ *vexpand_p = vexpand;
|
|
+}
|
|
+
|
|
+static void
|
|
+gtk_hdy_squeezer_draw_crossfade (GtkWidget *widget,
|
|
+ cairo_t *cr)
|
|
+{
|
|
+ GtkHdySqueezer *self = GTK_HDY_SQUEEZER (widget);
|
|
+ gdouble progress = gtk_progress_tracker_get_progress (&self->tracker, FALSE);
|
|
+
|
|
+ cairo_push_group (cr);
|
|
+ gtk_container_propagate_draw (GTK_CONTAINER (self),
|
|
+ self->visible_child->widget,
|
|
+ cr);
|
|
+ cairo_save (cr);
|
|
+
|
|
+ /* Multiply alpha by progress. */
|
|
+ cairo_set_source_rgba (cr, 1, 1, 1, progress);
|
|
+ cairo_set_operator (cr, CAIRO_OPERATOR_DEST_IN);
|
|
+ cairo_paint (cr);
|
|
+
|
|
+ if (self->last_visible_surface != NULL) {
|
|
+ gint width_diff = gtk_widget_get_allocated_width (widget) - self->last_visible_surface_allocation.width;
|
|
+ gint height_diff = gtk_widget_get_allocated_height (widget) - self->last_visible_surface_allocation.height;
|
|
+
|
|
+ cairo_set_source_surface (cr, self->last_visible_surface,
|
|
+ width_diff * self->xalign,
|
|
+ height_diff * self->yalign);
|
|
+ cairo_set_operator (cr, CAIRO_OPERATOR_ADD);
|
|
+ cairo_paint_with_alpha (cr, MAX (1.0 - progress, 0));
|
|
+ }
|
|
+
|
|
+ cairo_restore (cr);
|
|
+
|
|
+ cairo_pop_group_to_source (cr);
|
|
+ cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
|
|
+ cairo_paint (cr);
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+gtk_hdy_squeezer_draw (GtkWidget *widget,
|
|
+ cairo_t *cr)
|
|
+{
|
|
+ GtkHdySqueezer *self = GTK_HDY_SQUEEZER (widget);
|
|
+
|
|
+ if (gtk_cairo_should_draw_window (cr, self->view_window)) {
|
|
+ GtkStyleContext *context;
|
|
+
|
|
+ context = gtk_widget_get_style_context (widget);
|
|
+ gtk_render_background (context,
|
|
+ cr,
|
|
+ 0, 0,
|
|
+ gtk_widget_get_allocated_width (widget),
|
|
+ gtk_widget_get_allocated_height (widget));
|
|
+ }
|
|
+
|
|
+ if (self->visible_child) {
|
|
+ if (gtk_progress_tracker_get_state (&self->tracker) != GTK_PROGRESS_STATE_AFTER) {
|
|
+ if (self->last_visible_surface == NULL &&
|
|
+ self->last_visible_child != NULL) {
|
|
+ g_autoptr (cairo_t) pattern_cr = NULL;
|
|
+
|
|
+ gtk_widget_get_allocation (self->last_visible_child->widget,
|
|
+ &self->last_visible_surface_allocation);
|
|
+ self->last_visible_surface =
|
|
+ gdk_window_create_similar_surface (gtk_widget_get_window (widget),
|
|
+ CAIRO_CONTENT_COLOR_ALPHA,
|
|
+ self->last_visible_surface_allocation.width,
|
|
+ self->last_visible_surface_allocation.height);
|
|
+ pattern_cr = cairo_create (self->last_visible_surface);
|
|
+ /* We don't use propagate_draw here, because we don't want to apply the
|
|
+ * bin_window offset.
|
|
+ */
|
|
+ gtk_widget_draw (self->last_visible_child->widget, pattern_cr);
|
|
+ }
|
|
+
|
|
+ cairo_rectangle (cr,
|
|
+ 0, 0,
|
|
+ gtk_widget_get_allocated_width (widget),
|
|
+ gtk_widget_get_allocated_height (widget));
|
|
+ cairo_clip (cr);
|
|
+
|
|
+ switch (self->active_transition_type) {
|
|
+ case GTK_HDY_SQUEEZER_TRANSITION_TYPE_CROSSFADE:
|
|
+ if (gtk_cairo_should_draw_window (cr, self->bin_window))
|
|
+ gtk_hdy_squeezer_draw_crossfade (widget, cr);
|
|
+ break;
|
|
+ case GTK_HDY_SQUEEZER_TRANSITION_TYPE_NONE:
|
|
+ default:
|
|
+ g_assert_not_reached ();
|
|
+ }
|
|
+
|
|
+ } else if (gtk_cairo_should_draw_window (cr, self->bin_window))
|
|
+ gtk_container_propagate_draw (GTK_CONTAINER (self),
|
|
+ self->visible_child->widget,
|
|
+ cr);
|
|
+ }
|
|
+
|
|
+ return FALSE;
|
|
+}
|
|
+
|
|
+static void
|
|
+gtk_hdy_squeezer_size_allocate (GtkWidget *widget,
|
|
+ GtkAllocation *allocation)
|
|
+{
|
|
+ GtkHdySqueezer *self = GTK_HDY_SQUEEZER (widget);
|
|
+ GtkHdySqueezerChildInfo *child_info = NULL;
|
|
+ GtkWidget *child = NULL;
|
|
+ gint child_min;
|
|
+ GList *l;
|
|
+ GtkAllocation child_allocation;
|
|
+
|
|
+ gtk_hdy_css_size_allocate (widget, allocation);
|
|
+
|
|
+ gtk_widget_set_allocation (widget, allocation);
|
|
+
|
|
+ for (l = self->children; l != NULL; l = l->next) {
|
|
+ child_info = l->data;
|
|
+ child = child_info->widget;
|
|
+
|
|
+ if (!gtk_widget_get_visible (child))
|
|
+ continue;
|
|
+
|
|
+ if (!child_info->enabled)
|
|
+ continue;
|
|
+
|
|
+ if (self->orientation == GTK_ORIENTATION_VERTICAL) {
|
|
+ if (gtk_widget_get_request_mode (child) != GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH)
|
|
+ gtk_widget_get_preferred_height (child, &child_min, NULL);
|
|
+ else
|
|
+ gtk_widget_get_preferred_height_for_width (child, allocation->width, &child_min, NULL);
|
|
+
|
|
+ if (child_min <= allocation->height)
|
|
+ break;
|
|
+ } else {
|
|
+ if (gtk_widget_get_request_mode (child) != GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT)
|
|
+ gtk_widget_get_preferred_width (child, &child_min, NULL);
|
|
+ else
|
|
+ gtk_widget_get_preferred_width_for_height (child, allocation->height, &child_min, NULL);
|
|
+
|
|
+ if (child_min <= allocation->width)
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ set_visible_child (self, child_info,
|
|
+ self->transition_type,
|
|
+ self->transition_duration);
|
|
+
|
|
+ child_allocation.x = 0;
|
|
+ child_allocation.y = 0;
|
|
+
|
|
+ if (gtk_widget_get_realized (widget)) {
|
|
+ gdk_window_move_resize (self->view_window,
|
|
+ allocation->x, allocation->y,
|
|
+ allocation->width, allocation->height);
|
|
+ gdk_window_move_resize (self->bin_window,
|
|
+ 0, 0,
|
|
+ allocation->width, allocation->height);
|
|
+ }
|
|
+
|
|
+ if (self->last_visible_child != NULL) {
|
|
+ int min, nat;
|
|
+ gtk_widget_get_preferred_width (self->last_visible_child->widget, &min, &nat);
|
|
+ child_allocation.width = MAX (min, allocation->width);
|
|
+ gtk_widget_get_preferred_height_for_width (self->last_visible_child->widget,
|
|
+ child_allocation.width,
|
|
+ &min, &nat);
|
|
+ child_allocation.height = MAX (min, allocation->height);
|
|
+
|
|
+ gtk_widget_size_allocate (self->last_visible_child->widget, &child_allocation);
|
|
+ }
|
|
+
|
|
+ child_allocation.width = allocation->width;
|
|
+ child_allocation.height = allocation->height;
|
|
+
|
|
+ if (self->visible_child) {
|
|
+ int min, nat;
|
|
+ GtkAlign valign;
|
|
+
|
|
+ gtk_widget_get_preferred_height_for_width (self->visible_child->widget,
|
|
+ allocation->width,
|
|
+ &min, &nat);
|
|
+ if (self->interpolate_size) {
|
|
+ valign = gtk_widget_get_valign (self->visible_child->widget);
|
|
+ child_allocation.height = MAX (nat, allocation->height);
|
|
+ if (valign == GTK_ALIGN_END &&
|
|
+ child_allocation.height > allocation->height)
|
|
+ child_allocation.y -= nat - allocation->height;
|
|
+ else if (valign == GTK_ALIGN_CENTER &&
|
|
+ child_allocation.height > allocation->height)
|
|
+ child_allocation.y -= (nat - allocation->height) / 2;
|
|
+ }
|
|
+
|
|
+ gtk_widget_size_allocate (self->visible_child->widget, &child_allocation);
|
|
+ }
|
|
+}
|
|
+
|
|
+/* This private method is prefixed by the class name because it will be a
|
|
+ * virtual method in GTK 4.
|
|
+ */
|
|
+static void
|
|
+gtk_hdy_squeezer_measure (GtkWidget *widget,
|
|
+ GtkOrientation orientation,
|
|
+ int for_size,
|
|
+ int *minimum,
|
|
+ int *natural,
|
|
+ int *minimum_baseline,
|
|
+ int *natural_baseline)
|
|
+{
|
|
+ GtkHdySqueezer *self = GTK_HDY_SQUEEZER (widget);
|
|
+ GtkHdySqueezerChildInfo *child_info;
|
|
+ GtkWidget *child;
|
|
+ gint child_min, child_nat;
|
|
+ GList *l;
|
|
+
|
|
+ *minimum = 0;
|
|
+ *natural = 0;
|
|
+
|
|
+ for (l = self->children; l != NULL; l = l->next) {
|
|
+ child_info = l->data;
|
|
+ child = child_info->widget;
|
|
+
|
|
+ if (self->orientation != orientation && !self->homogeneous &&
|
|
+ self->visible_child != child_info)
|
|
+ continue;
|
|
+
|
|
+ if (!gtk_widget_get_visible (child))
|
|
+ continue;
|
|
+
|
|
+ /* Disabled children are taken into account when measuring the widget, to
|
|
+ * keep its size request and allocation consistent. This avoids the
|
|
+ * appearant size and position of a child to changes suddenly when a larger
|
|
+ * child gets enabled/disabled.
|
|
+ */
|
|
+
|
|
+ if (orientation == GTK_ORIENTATION_VERTICAL) {
|
|
+ if (for_size < 0)
|
|
+ gtk_widget_get_preferred_height (child, &child_min, &child_nat);
|
|
+ else
|
|
+ gtk_widget_get_preferred_height_for_width (child, for_size, &child_min, &child_nat);
|
|
+ } else {
|
|
+ if (for_size < 0)
|
|
+ gtk_widget_get_preferred_width (child, &child_min, &child_nat);
|
|
+ else
|
|
+ gtk_widget_get_preferred_width_for_height (child, for_size, &child_min, &child_nat);
|
|
+ }
|
|
+
|
|
+ if (self->orientation == orientation)
|
|
+ *minimum = *minimum == 0 ? child_min : MIN (*minimum, child_min);
|
|
+ else
|
|
+ *minimum = MAX (*minimum, child_min);
|
|
+ *natural = MAX (*natural, child_nat);
|
|
+ }
|
|
+
|
|
+ if (self->orientation != orientation && !self->homogeneous &&
|
|
+ self->interpolate_size &&
|
|
+ self->last_visible_child != NULL) {
|
|
+ gdouble t = gtk_progress_tracker_get_ease_out_cubic (&self->tracker, FALSE);
|
|
+ if (orientation == GTK_ORIENTATION_VERTICAL) {
|
|
+ *minimum = gtk_hdy_lerp (self->last_visible_widget_height, *minimum, t);
|
|
+ *natural = gtk_hdy_lerp (self->last_visible_widget_height, *natural, t);
|
|
+ } else {
|
|
+ *minimum = gtk_hdy_lerp (self->last_visible_widget_width, *minimum, t);
|
|
+ *natural = gtk_hdy_lerp (self->last_visible_widget_width, *natural, t);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ gtk_hdy_css_measure (widget, orientation, minimum, natural);
|
|
+}
|
|
+
|
|
+static void
|
|
+gtk_hdy_squeezer_get_preferred_width (GtkWidget *widget,
|
|
+ gint *minimum,
|
|
+ gint *natural)
|
|
+{
|
|
+ gtk_hdy_squeezer_measure (widget, GTK_ORIENTATION_HORIZONTAL, -1,
|
|
+ minimum, natural, NULL, NULL);
|
|
+}
|
|
+
|
|
+static void
|
|
+gtk_hdy_squeezer_get_preferred_width_for_height (GtkWidget *widget,
|
|
+ gint height,
|
|
+ gint *minimum,
|
|
+ gint *natural)
|
|
+{
|
|
+ gtk_hdy_squeezer_measure (widget, GTK_ORIENTATION_HORIZONTAL, height,
|
|
+ minimum, natural, NULL, NULL);
|
|
+}
|
|
+
|
|
+static void
|
|
+gtk_hdy_squeezer_get_preferred_height (GtkWidget *widget,
|
|
+ gint *minimum,
|
|
+ gint *natural)
|
|
+{
|
|
+ gtk_hdy_squeezer_measure (widget, GTK_ORIENTATION_VERTICAL, -1,
|
|
+ minimum, natural, NULL, NULL);
|
|
+}
|
|
+
|
|
+static void
|
|
+gtk_hdy_squeezer_get_preferred_height_for_width (GtkWidget *widget,
|
|
+ gint width,
|
|
+ gint *minimum,
|
|
+ gint *natural)
|
|
+{
|
|
+ gtk_hdy_squeezer_measure (widget, GTK_ORIENTATION_VERTICAL, width,
|
|
+ minimum, natural, NULL, NULL);
|
|
+}
|
|
+
|
|
+static void
|
|
+gtk_hdy_squeezer_get_child_property (GtkContainer *container,
|
|
+ GtkWidget *widget,
|
|
+ guint property_id,
|
|
+ GValue *value,
|
|
+ GParamSpec *pspec)
|
|
+{
|
|
+ GtkHdySqueezer *self = GTK_HDY_SQUEEZER (container);
|
|
+ GtkHdySqueezerChildInfo *child_info;
|
|
+
|
|
+ child_info = find_child_info_for_widget (self, widget);
|
|
+ if (child_info == NULL) {
|
|
+ g_param_value_set_default (pspec, value);
|
|
+
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ switch (property_id) {
|
|
+ case CHILD_PROP_ENABLED:
|
|
+ g_value_set_boolean (value, gtk_hdy_squeezer_get_child_enabled (self, widget));
|
|
+ break;
|
|
+ default:
|
|
+ GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+gtk_hdy_squeezer_set_child_property (GtkContainer *container,
|
|
+ GtkWidget *widget,
|
|
+ guint property_id,
|
|
+ const GValue *value,
|
|
+ GParamSpec *pspec)
|
|
+{
|
|
+ GtkHdySqueezer *self = GTK_HDY_SQUEEZER (container);
|
|
+ GtkHdySqueezerChildInfo *child_info;
|
|
+
|
|
+ child_info = find_child_info_for_widget (self, widget);
|
|
+ if (child_info == NULL)
|
|
+ return;
|
|
+
|
|
+ switch (property_id) {
|
|
+ case CHILD_PROP_ENABLED:
|
|
+ gtk_hdy_squeezer_set_child_enabled (self, widget, g_value_get_boolean (value));
|
|
+ break;
|
|
+ default:
|
|
+ GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+gtk_hdy_squeezer_dispose (GObject *object)
|
|
+{
|
|
+ GtkHdySqueezer *self = GTK_HDY_SQUEEZER (object);
|
|
+
|
|
+ self->visible_child = NULL;
|
|
+
|
|
+ G_OBJECT_CLASS (gtk_hdy_squeezer_parent_class)->dispose (object);
|
|
+}
|
|
+
|
|
+static void
|
|
+gtk_hdy_squeezer_finalize (GObject *object)
|
|
+{
|
|
+ GtkHdySqueezer *self = GTK_HDY_SQUEEZER (object);
|
|
+
|
|
+ gtk_hdy_squeezer_unschedule_ticks (self);
|
|
+
|
|
+ if (self->last_visible_surface != NULL)
|
|
+ cairo_surface_destroy (self->last_visible_surface);
|
|
+
|
|
+ G_OBJECT_CLASS (gtk_hdy_squeezer_parent_class)->finalize (object);
|
|
+}
|
|
+
|
|
+static void
|
|
+gtk_hdy_squeezer_class_init (GtkHdySqueezerClass *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 = gtk_hdy_squeezer_get_property;
|
|
+ object_class->set_property = gtk_hdy_squeezer_set_property;
|
|
+ object_class->dispose = gtk_hdy_squeezer_dispose;
|
|
+ object_class->finalize = gtk_hdy_squeezer_finalize;
|
|
+
|
|
+ widget_class->size_allocate = gtk_hdy_squeezer_size_allocate;
|
|
+ widget_class->draw = gtk_hdy_squeezer_draw;
|
|
+ widget_class->realize = gtk_hdy_squeezer_realize;
|
|
+ widget_class->unrealize = gtk_hdy_squeezer_unrealize;
|
|
+ widget_class->map = gtk_hdy_squeezer_map;
|
|
+ widget_class->unmap = gtk_hdy_squeezer_unmap;
|
|
+ widget_class->get_preferred_height = gtk_hdy_squeezer_get_preferred_height;
|
|
+ widget_class->get_preferred_height_for_width = gtk_hdy_squeezer_get_preferred_height_for_width;
|
|
+ widget_class->get_preferred_width = gtk_hdy_squeezer_get_preferred_width;
|
|
+ widget_class->get_preferred_width_for_height = gtk_hdy_squeezer_get_preferred_width_for_height;
|
|
+ widget_class->compute_expand = gtk_hdy_squeezer_compute_expand;
|
|
+
|
|
+ container_class->add = gtk_hdy_squeezer_add;
|
|
+ container_class->remove = gtk_hdy_squeezer_remove;
|
|
+ container_class->forall = gtk_hdy_squeezer_forall;
|
|
+ container_class->set_child_property = gtk_hdy_squeezer_set_child_property;
|
|
+ container_class->get_child_property = gtk_hdy_squeezer_get_child_property;
|
|
+ gtk_container_class_handle_border_width (container_class);
|
|
+
|
|
+ g_object_class_override_property (object_class,
|
|
+ PROP_ORIENTATION,
|
|
+ "orientation");
|
|
+
|
|
+ props[PROP_HOMOGENEOUS] =
|
|
+ g_param_spec_boolean ("homogeneous",
|
|
+ _("Homogeneous"),
|
|
+ _("Homogeneous sizing"),
|
|
+ FALSE,
|
|
+ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
|
+
|
|
+ props[PROP_VISIBLE_CHILD] =
|
|
+ g_param_spec_object ("visible-child",
|
|
+ _("Visible child"),
|
|
+ _("The widget currently visible in the squeezer"),
|
|
+ GTK_TYPE_WIDGET,
|
|
+ G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY);
|
|
+
|
|
+ props[PROP_TRANSITION_DURATION] =
|
|
+ g_param_spec_uint ("transition-duration",
|
|
+ _("Transition duration"),
|
|
+ _("The animation duration, in milliseconds"),
|
|
+ 0, G_MAXUINT, 200,
|
|
+ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
|
+
|
|
+ props[PROP_TRANSITION_TYPE] =
|
|
+ g_param_spec_enum ("transition-type",
|
|
+ _("Transition type"),
|
|
+ _("The type of animation used to transition"),
|
|
+ GTK_TYPE_HDY_SQUEEZER_TRANSITION_TYPE,
|
|
+ GTK_HDY_SQUEEZER_TRANSITION_TYPE_NONE,
|
|
+ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
|
+
|
|
+ props[PROP_TRANSITION_RUNNING] =
|
|
+ g_param_spec_boolean ("transition-running",
|
|
+ _("Transition running"),
|
|
+ _("Whether or not the 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);
|
|
+
|
|
+ /**
|
|
+ * GtkHdySqueezer:xalign:
|
|
+ *
|
|
+ * The xalign property determines the horizontal alignment of the children
|
|
+ * inside the squeezer's size allocation.
|
|
+ * Compare this to #GtkWidget:halign, which determines how the squeezer's size
|
|
+ * allocation is positioned in the space available for the squeezer.
|
|
+ * The range goes from 0 (start) to 1 (end).
|
|
+ *
|
|
+ * This will affect the position of children too wide to fit in the squeezer
|
|
+ * as they are fading out.
|
|
+ *
|
|
+ * Since: 1.0
|
|
+ */
|
|
+ props[PROP_XALIGN] =
|
|
+ g_param_spec_float ("xalign",
|
|
+ _("X align"),
|
|
+ _("The horizontal alignment, from 0 (start) to 1 (end)"),
|
|
+ 0.0, 1.0,
|
|
+ 0.5,
|
|
+ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
|
+
|
|
+ /**
|
|
+ * GtkHdySqueezer:yalign:
|
|
+ *
|
|
+ * The yalign property determines the vertical alignment of the children inside
|
|
+ * the squeezer's size allocation.
|
|
+ * Compare this to #GtkWidget:valign, which determines how the squeezer's size
|
|
+ * allocation is positioned in the space available for the squeezer.
|
|
+ * The range goes from 0 (top) to 1 (bottom).
|
|
+ *
|
|
+ * This will affect the position of children too tall to fit in the squeezer
|
|
+ * as they are fading out.
|
|
+ *
|
|
+ * Since: 1.0
|
|
+ */
|
|
+ props[PROP_YALIGN] =
|
|
+ g_param_spec_float ("yalign",
|
|
+ _("Y align"),
|
|
+ _("The vertical alignment, from 0 (top) to 1 (bottom)"),
|
|
+ 0.0, 1.0,
|
|
+ 0.5,
|
|
+ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
|
+
|
|
+ g_object_class_install_properties (object_class, LAST_PROP, props);
|
|
+
|
|
+ child_props[CHILD_PROP_ENABLED] =
|
|
+ g_param_spec_boolean ("enabled",
|
|
+ _("Enabled"),
|
|
+ _("Whether the child can be picked or should be ignored when looking for the child fitting the available size best"),
|
|
+ TRUE,
|
|
+ G_PARAM_READWRITE);
|
|
+
|
|
+ gtk_container_class_install_child_properties (container_class, LAST_CHILD_PROP, child_props);
|
|
+
|
|
+ gtk_widget_class_set_css_name (widget_class, "squeezer");
|
|
+}
|
|
+
|
|
+static void
|
|
+gtk_hdy_squeezer_init (GtkHdySqueezer *self)
|
|
+{
|
|
+
|
|
+ gtk_widget_set_has_window (GTK_WIDGET (self), FALSE);
|
|
+
|
|
+ self->homogeneous = TRUE;
|
|
+ self->transition_duration = 200;
|
|
+ self->transition_type = GTK_HDY_SQUEEZER_TRANSITION_TYPE_NONE;
|
|
+ self->xalign = 0.5;
|
|
+ self->yalign = 0.5;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * gtk_hdy_squeezer_new:
|
|
+ *
|
|
+ * Creates a new #GtkHdySqueezer container.
|
|
+ *
|
|
+ * Returns: a new #GtkHdySqueezer
|
|
+ */
|
|
+GtkWidget *
|
|
+gtk_hdy_squeezer_new (void)
|
|
+{
|
|
+ return g_object_new (GTK_TYPE_HDY_SQUEEZER, NULL);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * gtk_hdy_squeezer_get_homogeneous:
|
|
+ * @self: a #GtkHdySqueezer
|
|
+ *
|
|
+ * Gets whether @self is homogeneous.
|
|
+ *
|
|
+ * See gtk_hdy_squeezer_set_homogeneous().
|
|
+ *
|
|
+ * Returns: %TRUE if @self is homogeneous, %FALSE is not
|
|
+ *
|
|
+ * Since: 0.0.10
|
|
+ */
|
|
+gboolean
|
|
+gtk_hdy_squeezer_get_homogeneous (GtkHdySqueezer *self)
|
|
+{
|
|
+ g_return_val_if_fail (GTK_IS_HDY_SQUEEZER (self), FALSE);
|
|
+
|
|
+ return self->homogeneous;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * gtk_hdy_squeezer_set_homogeneous:
|
|
+ * @self: a #GtkHdySqueezer
|
|
+ * @homogeneous: %TRUE to make @self homogeneous
|
|
+ *
|
|
+ * Sets @self to be homogeneous or not. If it is homogeneous, @self will request
|
|
+ * the same size for all its children for its opposite orientation, e.g. if
|
|
+ * @self is oriented horizontally and is homogeneous, it will request the same
|
|
+ * height for all its children. If it isn't, @self may change size when a
|
|
+ * different child becomes visible.
|
|
+ *
|
|
+ * Since: 0.0.10
|
|
+ */
|
|
+void
|
|
+gtk_hdy_squeezer_set_homogeneous (GtkHdySqueezer *self,
|
|
+ gboolean homogeneous)
|
|
+{
|
|
+ g_return_if_fail (GTK_IS_HDY_SQUEEZER (self));
|
|
+
|
|
+ homogeneous = !!homogeneous;
|
|
+
|
|
+ if (self->homogeneous == homogeneous)
|
|
+ return;
|
|
+
|
|
+ self->homogeneous = homogeneous;
|
|
+
|
|
+ if (gtk_widget_get_visible (GTK_WIDGET(self)))
|
|
+ gtk_widget_queue_resize (GTK_WIDGET (self));
|
|
+
|
|
+ g_object_notify_by_pspec (G_OBJECT (self), props[PROP_HOMOGENEOUS]);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * gtk_hdy_squeezer_get_transition_duration:
|
|
+ * @self: a #GtkHdySqueezer
|
|
+ *
|
|
+ * Gets the amount of time (in milliseconds) that transitions between children
|
|
+ * in @self will take.
|
|
+ *
|
|
+ * Returns: the transition duration
|
|
+ */
|
|
+guint
|
|
+gtk_hdy_squeezer_get_transition_duration (GtkHdySqueezer *self)
|
|
+{
|
|
+ g_return_val_if_fail (GTK_IS_HDY_SQUEEZER (self), 0);
|
|
+
|
|
+ return self->transition_duration;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * gtk_hdy_squeezer_set_transition_duration:
|
|
+ * @self: a #GtkHdySqueezer
|
|
+ * @duration: the new duration, in milliseconds
|
|
+ *
|
|
+ * Sets the duration that transitions between children in @self will take.
|
|
+ */
|
|
+void
|
|
+gtk_hdy_squeezer_set_transition_duration (GtkHdySqueezer *self,
|
|
+ guint duration)
|
|
+{
|
|
+ g_return_if_fail (GTK_IS_HDY_SQUEEZER (self));
|
|
+
|
|
+ if (self->transition_duration == duration)
|
|
+ return;
|
|
+
|
|
+ self->transition_duration = duration;
|
|
+ g_object_notify_by_pspec (G_OBJECT (self), props[PROP_TRANSITION_DURATION]);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * gtk_hdy_squeezer_get_transition_type:
|
|
+ * @self: a #GtkHdySqueezer
|
|
+ *
|
|
+ * Gets the type of animation that will be used for transitions between children
|
|
+ * in @self.
|
|
+ *
|
|
+ * Returns: the current transition type of @self
|
|
+ */
|
|
+GtkHdySqueezerTransitionType
|
|
+gtk_hdy_squeezer_get_transition_type (GtkHdySqueezer *self)
|
|
+{
|
|
+ g_return_val_if_fail (GTK_IS_HDY_SQUEEZER (self), GTK_HDY_SQUEEZER_TRANSITION_TYPE_NONE);
|
|
+
|
|
+ return self->transition_type;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * gtk_hdy_squeezer_set_transition_type:
|
|
+ * @self: a #GtkHdySqueezer
|
|
+ * @transition: the new transition type
|
|
+ *
|
|
+ * Sets the type of animation that will be used for transitions between children
|
|
+ * in @self. Available types include various kinds of fades and slides.
|
|
+ *
|
|
+ * The transition type can be changed without problems at runtime, so it is
|
|
+ * possible to change the animation based on the child that is about to become
|
|
+ * current.
|
|
+ */
|
|
+void
|
|
+gtk_hdy_squeezer_set_transition_type (GtkHdySqueezer *self,
|
|
+ GtkHdySqueezerTransitionType transition)
|
|
+{
|
|
+ g_return_if_fail (GTK_IS_HDY_SQUEEZER (self));
|
|
+
|
|
+ if (self->transition_type == transition)
|
|
+ return;
|
|
+
|
|
+ self->transition_type = transition;
|
|
+ g_object_notify_by_pspec (G_OBJECT (self), props[PROP_TRANSITION_TYPE]);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * gtk_hdy_squeezer_get_transition_running:
|
|
+ * @self: a #GtkHdySqueezer
|
|
+ *
|
|
+ * Gets whether @self is currently in a transition from one child to another.
|
|
+ *
|
|
+ * Returns: %TRUE if the transition is currently running, %FALSE otherwise.
|
|
+ */
|
|
+gboolean
|
|
+gtk_hdy_squeezer_get_transition_running (GtkHdySqueezer *self)
|
|
+{
|
|
+ g_return_val_if_fail (GTK_IS_HDY_SQUEEZER (self), FALSE);
|
|
+
|
|
+ return (self->tick_id != 0);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * gtk_hdy_squeezer_get_interpolate_size:
|
|
+ * @self: A #GtkHdySqueezer
|
|
+ *
|
|
+ * Gets whether @self should interpolate its size on visible child change.
|
|
+ *
|
|
+ * See gtk_hdy_squeezer_set_interpolate_size().
|
|
+ *
|
|
+ * Returns: %TRUE if @self interpolates its size on visible child change, %FALSE if not
|
|
+ *
|
|
+ * Since: 0.0.10
|
|
+ */
|
|
+gboolean
|
|
+gtk_hdy_squeezer_get_interpolate_size (GtkHdySqueezer *self)
|
|
+{
|
|
+ g_return_val_if_fail (GTK_IS_HDY_SQUEEZER (self), FALSE);
|
|
+
|
|
+ return self->interpolate_size;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * gtk_hdy_squeezer_set_interpolate_size:
|
|
+ * @self: A #GtkHdySqueezer
|
|
+ * @interpolate_size: %TRUE to interpolate the size
|
|
+ *
|
|
+ * Sets whether or not @self will interpolate the size of its opposing
|
|
+ * orientation when changing the visible child. If %TRUE, @self will interpolate
|
|
+ * its size between the one of the previous visible child and the one of the new
|
|
+ * visible child, according to the set transition duration and the orientation,
|
|
+ * e.g. if @self is horizontal, it will interpolate the its height.
|
|
+ *
|
|
+ * Since: 0.0.10
|
|
+ */
|
|
+void
|
|
+gtk_hdy_squeezer_set_interpolate_size (GtkHdySqueezer *self,
|
|
+ gboolean interpolate_size)
|
|
+{
|
|
+ g_return_if_fail (GTK_IS_HDY_SQUEEZER (self));
|
|
+
|
|
+ interpolate_size = !!interpolate_size;
|
|
+
|
|
+ if (self->interpolate_size == interpolate_size)
|
|
+ return;
|
|
+
|
|
+ self->interpolate_size = interpolate_size;
|
|
+ g_object_notify_by_pspec (G_OBJECT (self), props[PROP_INTERPOLATE_SIZE]);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * gtk_hdy_squeezer_get_visible_child:
|
|
+ * @self: a #GtkHdySqueezer
|
|
+ *
|
|
+ * Gets the currently visible child of @self, or %NULL if there are no visible
|
|
+ * children.
|
|
+ *
|
|
+ * Returns: (transfer none) (nullable): the visible child of the #GtkHdySqueezer
|
|
+ */
|
|
+GtkWidget *
|
|
+gtk_hdy_squeezer_get_visible_child (GtkHdySqueezer *self)
|
|
+{
|
|
+ g_return_val_if_fail (GTK_IS_HDY_SQUEEZER (self), NULL);
|
|
+
|
|
+ return self->visible_child ? self->visible_child->widget : NULL;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * gtk_hdy_squeezer_get_child_enabled:
|
|
+ * @self: a #GtkHdySqueezer
|
|
+ * @child: a child of @self
|
|
+ *
|
|
+ * Gets whether @child is enabled.
|
|
+ *
|
|
+ * See gtk_hdy_squeezer_set_child_enabled().
|
|
+ *
|
|
+ * Returns: %TRUE if @child is enabled, %FALSE otherwise.
|
|
+ */
|
|
+gboolean
|
|
+gtk_hdy_squeezer_get_child_enabled (GtkHdySqueezer *self,
|
|
+ GtkWidget *child)
|
|
+{
|
|
+ GtkHdySqueezerChildInfo *child_info;
|
|
+
|
|
+ g_return_val_if_fail (GTK_IS_HDY_SQUEEZER (self), FALSE);
|
|
+ g_return_val_if_fail (GTK_IS_WIDGET (child), FALSE);
|
|
+
|
|
+ child_info = find_child_info_for_widget (self, child);
|
|
+
|
|
+ g_return_val_if_fail (child_info != NULL, FALSE);
|
|
+
|
|
+ return child_info->enabled;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * gtk_hdy_squeezer_set_child_enabled:
|
|
+ * @self: a #GtkHdySqueezer
|
|
+ * @child: a child of @self
|
|
+ * @enabled: %TRUE to enable the child, %FALSE to disable it
|
|
+ *
|
|
+ * Make @self enable or disable @child. If a child is disabled, it will be
|
|
+ * ignored when looking for the child fitting the available size best. This
|
|
+ * allows to programmatically and prematurely hide a child of @self even if it
|
|
+ * fits in the available space.
|
|
+ *
|
|
+ * This can be used e.g. to ensure a certain child is hidden below a certain
|
|
+ * window width, or any other constraint you find suitable.
|
|
+ */
|
|
+void
|
|
+gtk_hdy_squeezer_set_child_enabled (GtkHdySqueezer *self,
|
|
+ GtkWidget *child,
|
|
+ gboolean enabled)
|
|
+{
|
|
+ GtkHdySqueezerChildInfo *child_info;
|
|
+
|
|
+ g_return_if_fail (GTK_IS_HDY_SQUEEZER (self));
|
|
+ g_return_if_fail (GTK_IS_WIDGET (child));
|
|
+
|
|
+ child_info = find_child_info_for_widget (self, child);
|
|
+
|
|
+ g_return_if_fail (child_info != NULL);
|
|
+
|
|
+ enabled = !!enabled;
|
|
+
|
|
+ if (child_info->enabled == enabled)
|
|
+ return;
|
|
+
|
|
+ child_info->enabled = enabled;
|
|
+ gtk_widget_queue_resize (GTK_WIDGET (self));
|
|
+}
|
|
+
|
|
+/**
|
|
+ * gtk_hdy_squeezer_get_xalign:
|
|
+ * @self: a #GtkHdySqueezer
|
|
+ *
|
|
+ * Gets the #GtkHdySqueezer:xalign property for @self.
|
|
+ *
|
|
+ * Returns: the xalign property
|
|
+ *
|
|
+ * Since: 1.0
|
|
+ */
|
|
+gfloat
|
|
+gtk_hdy_squeezer_get_xalign (GtkHdySqueezer *self)
|
|
+{
|
|
+ g_return_val_if_fail (GTK_IS_HDY_SQUEEZER (self), 0.5);
|
|
+
|
|
+ return self->xalign;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * gtk_hdy_squeezer_set_xalign:
|
|
+ * @self: a #GtkHdySqueezer
|
|
+ * @xalign: the new xalign value, between 0 and 1
|
|
+ *
|
|
+ * Sets the #GtkHdySqueezer:xalign property for @self.
|
|
+ *
|
|
+ * Since: 1.0
|
|
+ */
|
|
+void
|
|
+gtk_hdy_squeezer_set_xalign (GtkHdySqueezer *self,
|
|
+ gfloat xalign)
|
|
+{
|
|
+ g_return_if_fail (GTK_IS_HDY_SQUEEZER (self));
|
|
+
|
|
+ xalign = CLAMP (xalign, 0.0, 1.0);
|
|
+
|
|
+ if (self->xalign == xalign)
|
|
+ return;
|
|
+
|
|
+ self->xalign = xalign;
|
|
+ gtk_widget_queue_draw (GTK_WIDGET (self));
|
|
+ g_object_notify_by_pspec (G_OBJECT (self), props[PROP_XALIGN]);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * gtk_hdy_squeezer_get_yalign:
|
|
+ * @self: a #GtkHdySqueezer
|
|
+ *
|
|
+ * Gets the #GtkHdySqueezer:yalign property for @self.
|
|
+ *
|
|
+ * Returns: the yalign property
|
|
+ *
|
|
+ * Since: 1.0
|
|
+ */
|
|
+gfloat
|
|
+gtk_hdy_squeezer_get_yalign (GtkHdySqueezer *self)
|
|
+{
|
|
+ g_return_val_if_fail (GTK_IS_HDY_SQUEEZER (self), 0.5);
|
|
+
|
|
+ return self->yalign;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * gtk_hdy_squeezer_set_yalign:
|
|
+ * @self: a #GtkHdySqueezer
|
|
+ * @yalign: the new yalign value, between 0 and 1
|
|
+ *
|
|
+ * Sets the #GtkHdySqueezer:yalign property for @self.
|
|
+ *
|
|
+ * Since: 1.0
|
|
+ */
|
|
+void
|
|
+gtk_hdy_squeezer_set_yalign (GtkHdySqueezer *self,
|
|
+ gfloat yalign)
|
|
+{
|
|
+ g_return_if_fail (GTK_IS_HDY_SQUEEZER (self));
|
|
+
|
|
+ yalign = CLAMP (yalign, 0.0, 1.0);
|
|
+
|
|
+ if (self->yalign == yalign)
|
|
+ return;
|
|
+
|
|
+ self->yalign = yalign;
|
|
+ gtk_widget_queue_draw (GTK_WIDGET (self));
|
|
+ g_object_notify_by_pspec (G_OBJECT (self), props[PROP_YALIGN]);
|
|
+}
|
|
diff --git a/gtk/meson.build b/gtk/meson.build
|
|
index 9fe7c4d..80ae1bf 100644
|
|
--- a/gtk/meson.build
|
|
+++ b/gtk/meson.build
|
|
@@ -385,6 +385,7 @@ gtk_sources = files(
|
|
'gdkpixbufutils.c',
|
|
'hdy-animation.c',
|
|
'hdy-css.c',
|
|
+ 'hdy-squeezer.c',
|
|
'hdy-view-switcher-bar.c',
|
|
'hdy-view-switcher-button.c',
|
|
'hdy-view-switcher.c',
|
|
@@ -396,7 +397,9 @@ gtk_private_type_headers = files(
|
|
'gtkcsstypesprivate.h',
|
|
'gtktexthandleprivate.h',
|
|
'hdy-animation-private.h',
|
|
+ 'hdy-cairo-private.h',
|
|
'hdy-css-private.h',
|
|
+ 'hdy-squeezer-private.h',
|
|
'hdy-view-switcher-bar-private.h',
|
|
'hdy-view-switcher-button-private.h',
|
|
'hdy-view-switcher-private.h',
|