[gtk/wip/layout-manager: 8/19] Hook GtkLayoutManager into GtkWidget



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]