[gtk/wip/layout-manager: 8/19] Hook GtkLayoutManager into GtkWidget
- From: Emmanuele Bassi <ebassi src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/wip/layout-manager: 8/19] Hook GtkLayoutManager into GtkWidget
- Date: Mon, 25 Mar 2019 23:18:41 +0000 (UTC)
commit 469cd03ff4d244f25d2e79164db9cf247ec9e0f9
Author: Emmanuele Bassi <ebassi gnome org>
Date: Wed Dec 12 17:20:28 2018 +0000
Hook GtkLayoutManager into GtkWidget
We delegate the size request mode, the measuring, and the allocation of
a widget through a GtkLayoutManager instance, if one has been attached
to the widget; otherwise, we fall back to the widget's own implementation.
docs/reference/gtk/gtk4-sections.txt | 2 +
gtk/gtklayoutmanager.c | 104 +++++++++++++++++++++-------
gtk/gtklayoutmanager.h | 6 +-
gtk/gtksizerequest.c | 128 +++++++++++++++++++++++++----------
gtk/gtktypes.h | 1 +
gtk/gtkwidget.c | 79 ++++++++++++++++++---
gtk/gtkwidget.h | 6 ++
gtk/gtkwidgetprivate.h | 3 +
8 files changed, 260 insertions(+), 69 deletions(-)
---
diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt
index 8ce5bb1d82..1179eeaa2f 100644
--- a/docs/reference/gtk/gtk4-sections.txt
+++ b/docs/reference/gtk/gtk4-sections.txt
@@ -4590,6 +4590,8 @@ gtk_widget_get_first_child
gtk_widget_get_last_child
gtk_widget_insert_before
gtk_widget_insert_after
+gtk_widget_set_layout_manager
+gtk_widget_get_layout_manager
<SUBSECTION>
gtk_widget_get_path
diff --git a/gtk/gtklayoutmanager.c b/gtk/gtklayoutmanager.c
index bb23c5a598..7a5f2f08d5 100644
--- a/gtk/gtklayoutmanager.c
+++ b/gtk/gtklayoutmanager.c
@@ -22,12 +22,23 @@
* @Title: GtkLayoutManager
* @Short_description: Base class for layout manager
*
- * ...
+ * Layout managers are delegate classes that handle the preferred size
+ * and the allocation of a container widget.
+ *
+ * You typically subclass #GtkLayoutManager if you want to implement a
+ * layout policy for the children of a widget, without necessarily
+ * implementing the @GtkWidgetClass.measure() and @GtkWidgetClass.size_allocate()
+ * virtual functions directly.
+ *
+ * Each #GtkWidget can only have a #GtkLayoutManager instance associated to it
+ * at any given time; it is possible, though, to replace the layout manager
+ * instance using gtk_widget_set_layout_manager().
*/
#include "config.h"
-#include "gtklayoutmanager.h"
+#include "gtklayoutmanagerprivate.h"
+#include "gtkwidget.h"
#ifdef G_ENABLE_DEBUG
#define LAYOUT_MANAGER_WARN_NOT_IMPLEMENTED(m,method) G_STMT_START { \
@@ -108,7 +119,7 @@ gtk_layout_manager_init (GtkLayoutManager *self)
* @layout_manager: a #GtkLayoutManager
* @widget: (nullable): a #GtkWidget
*
- * ...
+ * Sets a back pointer from @widget to @layout_manager.
*/
void
gtk_layout_manager_set_widget (GtkLayoutManager *layout_manager,
@@ -116,22 +127,44 @@ gtk_layout_manager_set_widget (GtkLayoutManager *layout_manager,
{
GtkLayoutManagerPrivate *priv = gtk_layout_manager_get_instance_private (layout_manager);
+ if (widget != NULL && priv->widget != NULL)
+ {
+ g_critical ("The layout manager %p of type %s is already in use "
+ "by widget %p '%s', and cannot be used by widget %p '%s'",
+ layout_manager, G_OBJECT_TYPE_NAME (layout_manager),
+ priv->widget, gtk_widget_get_name (priv->widget),
+ widget, gtk_widget_get_name (widget));
+ return;
+ }
+
priv->widget = widget;
}
/**
* gtk_layout_manager_measure:
- * @manager:
- * @widget:
- * @orientation:
- * @for_size:
- * @minimum: (out):
- * @natural: (out):
- * @minimum_baseline: (out):
- * @natural_baseline: (out):
+ * @manager: a #GtkLayoutManager
+ * @widget: the #GtkWidget using @manager
+ * @orientation: the orientation to measure
+ * @for_size: Size for the opposite of @orientation; for instance, if
+ * the @orientation is %GTK_ORIENTATION_HORIZONTAL, this is the height
+ * of the widget; if the @orientation is %GTK_ORIENTATION_VERTICAL, this
+ * is the width of the widget. This allows to measure the height for the
+ * given width, and the width for the given height. Use -1 if the size
+ * is not known
+ * @minimum: (out) (optional): the minimum size for the given size and
+ * orientation
+ * @natural: (out) (optional): the natural, or preferred size for the
+ * given size and orientation
+ * @minimum_baseline: (out) (optional): the baseline position for the
+ * minimum size
+ * @natural_baseline: (out) (optional): the baseline position for the
+ * natural size
*
- * ...
+ * Measures the size of the @widget using @manager, for the
+ * given @orientation and size.
*
+ * See [GtkWidget's geometry management section][geometry-management] for
+ * more details.
*/
void
gtk_layout_manager_measure (GtkLayoutManager *manager,
@@ -158,13 +191,15 @@ gtk_layout_manager_measure (GtkLayoutManager *manager,
/**
* gtk_layout_manager_allocate:
- * @manager:
- * @widget:
- * @width:
- * @height:
- * @baseline:
+ * @manager: a #GtkLayoutManager
+ * @widget: the #GtkWidget using @manager
+ * @width: the new width of the @widget
+ * @height: the new height of the @widget
+ * @baseline: the baseline position of the @widget
*
- * ...
+ * This function assigns the given @width, @height, and @baseline to
+ * a @widget, and computes the position and sizes of the children of
+ * the @widget using the layout management policy of @manager.
*/
void
gtk_layout_manager_allocate (GtkLayoutManager *manager,
@@ -185,12 +220,12 @@ gtk_layout_manager_allocate (GtkLayoutManager *manager,
/**
* gtk_layout_manager_get_request_mode:
- * @manager:
- * @widget:
+ * @manager: a #GtkLayoutManager
+ * @widget: the #GtkWidget using @manager
*
- * ...
+ * Retrieves the request mode of @manager.
*
- * Returns: ...
+ * Returns: a #GtkSizeRequestMode
*/
GtkSizeRequestMode
gtk_layout_manager_get_request_mode (GtkLayoutManager *manager,
@@ -206,17 +241,40 @@ gtk_layout_manager_get_request_mode (GtkLayoutManager *manager,
return klass->get_request_mode (manager, widget);
}
+/**
+ * gtk_layout_manager_get_widget:
+ * @manager: a #GtkLayoutManager
+ *
+ * Retrieves the #GtkWidget using the given #GtkLayoutManager.
+ *
+ * Returns: (transfer none) (nullable): a #GtkWidget
+ */
+GtkWidget *
+gtk_layout_manager_get_widget (GtkLayoutManager *manager)
+{
+ GtkLayoutManagerPrivate *priv = gtk_layout_manager_get_instance_private (manager);
+
+ g_return_val_if_fail (GTK_IS_LAYOUT_MANAGER (manager), NULL);
+
+ return priv->widget;
+}
+
/**
* gtk_layout_manager_layout_changed:
* @manager: a #GtkLayoutManager
*
- * ...
+ * Queues a resize on the #GtkWidget using @manager, if any.
+ *
+ * This function should be called by subclasses of #GtkLayoutManager in
+ * response to changes to their layout management policies.
*/
void
gtk_layout_manager_layout_changed (GtkLayoutManager *manager)
{
GtkLayoutManagerPrivate *priv = gtk_layout_manager_get_instance_private (manager);
+ g_return_if_fail (GTK_IS_LAYOUT_MANAGER (manager));
+
if (priv->widget != NULL)
gtk_widget_queue_resize (priv->widget);
}
diff --git a/gtk/gtklayoutmanager.h b/gtk/gtklayoutmanager.h
index e849e300a2..4d05132c4d 100644
--- a/gtk/gtklayoutmanager.h
+++ b/gtk/gtklayoutmanager.h
@@ -18,7 +18,8 @@
*/
#pragma once
-#include <gtk/gtkcontainer.h>
+#include <gtk/gtktypes.h>
+#include <gtk/gtkwidget.h>
G_BEGIN_DECLS
@@ -88,6 +89,9 @@ GDK_AVAILABLE_IN_ALL
GtkSizeRequestMode gtk_layout_manager_get_request_mode (GtkLayoutManager *manager,
GtkWidget *widget);
+GDK_AVAILABLE_IN_ALL
+GtkWidget * gtk_layout_manager_get_widget (GtkLayoutManager *manager);
+
GDK_AVAILABLE_IN_ALL
void gtk_layout_manager_layout_changed (GtkLayoutManager *manager);
diff --git a/gtk/gtksizerequest.c b/gtk/gtksizerequest.c
index 197d2413ea..d9b24ad8e8 100644
--- a/gtk/gtksizerequest.c
+++ b/gtk/gtksizerequest.c
@@ -31,6 +31,7 @@
#include "gtkwidgetprivate.h"
#include "gtkcssnodeprivate.h"
#include "gtkcssnumbervalueprivate.h"
+#include "gtklayoutmanagerprivate.h"
#ifdef G_ENABLE_CONSISTENCY_CHECKS
@@ -195,45 +196,94 @@ gtk_widget_query_size_for_orientation (GtkWidget *widget,
css_min_for_size = get_number_ceil (style, GTK_CSS_PROPERTY_MIN_WIDTH);
}
- if (for_size < 0)
+ GtkLayoutManager *layout_manager = gtk_widget_get_layout_manager (widget);
+
+ if (layout_manager != NULL)
{
- push_recursion_check (widget, orientation);
- widget_class->measure (widget, orientation, -1,
- &reported_min_size, &reported_nat_size,
- &min_baseline, &nat_baseline);
- pop_recursion_check (widget, orientation);
+ if (for_size < 0)
+ {
+ push_recursion_check (widget, orientation);
+ gtk_layout_manager_measure (layout_manager, widget,
+ orientation, -1,
+ &reported_min_size, &reported_nat_size,
+ &min_baseline, &nat_baseline);
+ pop_recursion_check (widget, orientation);
+ }
+ else
+ {
+ int adjusted_for_size;
+ int minimum_for_size = 0;
+ int natural_for_size = 0;
+ int dummy = 0;
+
+ /* Pull the minimum for_size from the cache as it's needed to adjust
+ * the proposed 'for_size' */
+ gtk_layout_manager_measure (layout_manager, widget,
+ OPPOSITE_ORIENTATION (orientation), -1,
+ &minimum_for_size, &natural_for_size,
+ NULL, NULL);
+
+ if (for_size < MAX (minimum_for_size, css_min_for_size))
+ for_size = MAX (minimum_for_size, css_min_for_size);
+
+ adjusted_for_size = for_size;
+ gtk_widget_adjust_size_allocation (widget, OPPOSITE_ORIENTATION (orientation),
+ &for_size, &natural_for_size,
+ &dummy, &adjusted_for_size);
+ adjusted_for_size -= css_extra_for_size;
+ if (adjusted_for_size < 0)
+ adjusted_for_size = MAX (minimum_for_size, css_min_for_size);
+
+ push_recursion_check (widget, orientation);
+ gtk_layout_manager_measure (layout_manager, widget,
+ orientation,
+ adjusted_for_size,
+ &reported_min_size, &reported_nat_size,
+ &min_baseline, &nat_baseline);
+ pop_recursion_check (widget, orientation);
+ }
}
else
{
- int adjusted_for_size;
- int minimum_for_size = 0;
- int natural_for_size = 0;
- int dummy = 0;
-
- /* Pull the minimum for_size from the cache as it's needed to adjust
- * the proposed 'for_size' */
- gtk_widget_measure (widget, OPPOSITE_ORIENTATION (orientation), -1,
- &minimum_for_size, &natural_for_size, NULL, NULL);
-
- /* TODO: Warn if the given for_size is too small? */
- if (for_size < MAX (minimum_for_size, css_min_for_size))
- for_size = MAX (minimum_for_size, css_min_for_size);
-
- adjusted_for_size = for_size;
- gtk_widget_adjust_size_allocation (widget, OPPOSITE_ORIENTATION (orientation),
- &for_size, &natural_for_size,
- &dummy, &adjusted_for_size);
-
- adjusted_for_size -= css_extra_for_size;
-
- push_recursion_check (widget, orientation);
- widget_class->measure (widget,
- orientation,
- adjusted_for_size,
- &reported_min_size, &reported_nat_size,
- &min_baseline, &nat_baseline);
- pop_recursion_check (widget, orientation);
-
+ if (for_size < 0)
+ {
+ push_recursion_check (widget, orientation);
+ widget_class->measure (widget, orientation, -1,
+ &reported_min_size, &reported_nat_size,
+ &min_baseline, &nat_baseline);
+ pop_recursion_check (widget, orientation);
+ }
+ else
+ {
+ int adjusted_for_size;
+ int minimum_for_size = 0;
+ int natural_for_size = 0;
+ int dummy = 0;
+
+ /* Pull the minimum for_size from the cache as it's needed to adjust
+ * the proposed 'for_size' */
+ gtk_widget_measure (widget, OPPOSITE_ORIENTATION (orientation), -1,
+ &minimum_for_size, &natural_for_size, NULL, NULL);
+
+ /* TODO: Warn if the given for_size is too small? */
+ if (for_size < MAX (minimum_for_size, css_min_for_size))
+ for_size = MAX (minimum_for_size, css_min_for_size);
+
+ adjusted_for_size = for_size;
+ gtk_widget_adjust_size_allocation (widget, OPPOSITE_ORIENTATION (orientation),
+ &for_size, &natural_for_size,
+ &dummy, &adjusted_for_size);
+
+ adjusted_for_size -= css_extra_for_size;
+
+ push_recursion_check (widget, orientation);
+ widget_class->measure (widget,
+ orientation,
+ adjusted_for_size,
+ &reported_min_size, &reported_nat_size,
+ &min_baseline, &nat_baseline);
+ pop_recursion_check (widget, orientation);
+ }
}
min_size = MAX (0, MAX (reported_min_size, css_min_size)) + css_extra_size;
@@ -512,7 +562,13 @@ gtk_widget_get_request_mode (GtkWidget *widget)
if (G_UNLIKELY (!cache->request_mode_valid))
{
- cache->request_mode = GTK_WIDGET_GET_CLASS (widget)->get_request_mode (widget);
+ GtkLayoutManager *layout_manager = gtk_widget_get_layout_manager (widget);
+
+ if (layout_manager != NULL)
+ cache->request_mode = gtk_layout_manager_get_request_mode (layout_manager, widget);
+ else
+ cache->request_mode = GTK_WIDGET_GET_CLASS (widget)->get_request_mode (widget);
+
cache->request_mode_valid = TRUE;
}
diff --git a/gtk/gtktypes.h b/gtk/gtktypes.h
index 5d1218f189..8a48c1c4c8 100644
--- a/gtk/gtktypes.h
+++ b/gtk/gtktypes.h
@@ -38,6 +38,7 @@ typedef struct _GtkBuilder GtkBuilder;
typedef struct _GtkClipboard GtkClipboard;
typedef struct _GtkEventController GtkEventController;
typedef struct _GtkGesture GtkGesture;
+typedef struct _GtkLayoutManager GtkLayoutManager;
typedef struct _GtkRequisition GtkRequisition;
typedef struct _GtkRoot GtkRoot;
typedef struct _GtkSelectionData GtkSelectionData;
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index d676af17f4..5200006897 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -48,6 +48,7 @@
#include "gtkgesturesingle.h"
#include "gtkgestureswipe.h"
#include "gtkintl.h"
+#include "gtklayoutmanagerprivate.h"
#include "gtkmain.h"
#include "gtkmarshalers.h"
#include "gtkmenu.h"
@@ -4346,16 +4347,26 @@ gtk_widget_allocate (GtkWidget *widget,
priv->height = adjusted.height;
priv->baseline = baseline;
- if (g_signal_has_handler_pending (widget, widget_signals[SIZE_ALLOCATE], 0, FALSE))
- g_signal_emit (widget, widget_signals[SIZE_ALLOCATE], 0,
- priv->width,
- priv->height,
- baseline);
+ if (priv->layout_manager != NULL)
+ {
+ gtk_layout_manager_allocate (priv->layout_manager, widget,
+ priv->width,
+ priv->priv->height,
+ baseline);
+ }
else
- GTK_WIDGET_GET_CLASS (widget)->size_allocate (widget,
- priv->width,
- priv->height,
- baseline);
+ {
+ if (g_signal_has_handler_pending (widget, widget_signals[SIZE_ALLOCATE], 0, FALSE))
+ g_signal_emit (widget, widget_signals[SIZE_ALLOCATE], 0,
+ priv->width,
+ priv->height,
+ baseline);
+ else
+ GTK_WIDGET_GET_CLASS (widget)->size_allocate (widget,
+ priv->width,
+ priv->height,
+ baseline);
+ }
/* Size allocation is god... after consulting god, no further requests or allocations are needed */
#ifdef G_ENABLE_DEBUG
@@ -8041,6 +8052,9 @@ gtk_widget_dispose (GObject *object)
while (priv->paintables)
gtk_widget_paintable_set_widget (priv->paintables->data, NULL);
+ gtk_widget_set_layout_manager (widget, NULL);
+ g_clear_object (&priv->layout_manager);
+
priv->visible = FALSE;
if (_gtk_widget_get_realized (widget))
gtk_widget_unrealize (widget);
@@ -13361,3 +13375,50 @@ gtk_widget_get_height (GtkWidget *widget)
return priv->height;
}
+
+/**
+ * gtk_widget_set_layout_manager:
+ * @widget: a #GtkWidget
+ * @layout_manager: (nullable) (transfer full): a #GtkLayoutManager
+ *
+ * Sets the layout manager delegate instance that provides an implementation
+ * for measuring and allocating the children of @widget.
+ *
+ * The @widget acquires a reference to the given @layout_manager.
+ */
+void
+gtk_widget_set_layout_manager (GtkWidget *widget,
+ GtkLayoutManager *layout_manager)
+{
+ GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget);
+
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+ g_return_if_fail (layout_manager == NULL || GTK_IS_LAYOUT_MANAGER (layout_manager));
+ g_return_if_fail (layout_manager == NULL || gtk_layout_manager_get_widget (layout_manager) == NULL);
+
+ if (g_set_object (&priv->layout_manager, layout_manager))
+ {
+ if (priv->layout_manager != NULL)
+ gtk_layout_manager_set_widget (priv->layout_manager, widget);
+
+ gtk_widget_queue_resize (widget);
+ }
+}
+
+/**
+ * gtk_widget_get_layout_manager:
+ * @widget: a #GtkWidget
+ *
+ * Retrieves the layout manager set using gtk_widget_set_layout_manager().
+ *
+ * Returns: (transfer none) (nullable): a #GtkLayoutManager
+ */
+GtkLayoutManager *
+gtk_widget_get_layout_manager (GtkWidget *widget)
+{
+ GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget);
+
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
+
+ return priv->layout_manager;
+}
diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h
index 97848c698e..c561724827 100644
--- a/gtk/gtkwidget.h
+++ b/gtk/gtkwidget.h
@@ -424,6 +424,12 @@ void gtk_widget_get_preferred_size (GtkWidget *w
GtkRequisition *minimum_size,
GtkRequisition *natural_size);
+GDK_AVAILABLE_IN_ALL
+void gtk_widget_set_layout_manager (GtkWidget *widget,
+ GtkLayoutManager *layout_manager);
+GDK_AVAILABLE_IN_ALL
+GtkLayoutManager * gtk_widget_get_layout_manager (GtkWidget *widget);
+
GDK_AVAILABLE_IN_ALL
void gtk_widget_add_accelerator (GtkWidget *widget,
const gchar *accel_signal,
diff --git a/gtk/gtkwidgetprivate.h b/gtk/gtkwidgetprivate.h
index 9ede6b8b79..03c9f95923 100644
--- a/gtk/gtkwidgetprivate.h
+++ b/gtk/gtkwidgetprivate.h
@@ -160,6 +160,9 @@ struct _GtkWidgetPrivate
/* The render node we draw or %NULL if not yet created.*/
GskRenderNode *render_node;
+ /* The layout manager, or %NULL */
+ GtkLayoutManager *layout_manager;
+
GSList *paintables;
/* The widget's surface or its parent surface if it does
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]