[gtk/wip/layout-manager] Changes after review



commit 25b87fc37eeab14e2132f5e91521dd8e847453d3
Author: Emmanuele Bassi <ebassi gnome org>
Date:   Wed Mar 20 15:37:15 2019 +0000

    Changes after review

 gtk/gtk.h                    |   1 +
 gtk/gtkbinlayout.c           |  49 +++++++++-------
 gtk/gtkbinlayout.h           |  14 ++++-
 gtk/gtkboxlayout.c           |  72 ++++++++++++------------
 gtk/gtkcustomlayout.c        | 130 +++++++++++++++++++++++++++++++++++++++++++
 gtk/gtkcustomlayout.h        |  77 +++++++++++++++++++++++++
 gtk/gtklayoutmanager.c       |  71 +++++++++++++++--------
 gtk/gtklayoutmanager.h       |  13 +++--
 gtk/gtklegacylayout.c        | 129 ------------------------------------------
 gtk/gtklegacylayoutprivate.h |  33 -----------
 gtk/gtksizerequest.c         |   2 +-
 gtk/gtkswitch.c              |   5 +-
 gtk/gtkwidget.c              |  20 +++----
 gtk/meson.build              |   3 +-
 14 files changed, 355 insertions(+), 264 deletions(-)
---
diff --git a/gtk/gtk.h b/gtk/gtk.h
index 27a29e951f..6d1ca27b5f 100644
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -82,6 +82,7 @@
 #include <gtk/gtkcontainer.h>
 #include <gtk/gtkcssprovider.h>
 #include <gtk/gtkcsssection.h>
+#include <gtk/gtkcustomlayout.h>
 #include <gtk/gtkdebug.h>
 #include <gtk/gtkdialog.h>
 #include <gtk/gtkdnd.h>
diff --git a/gtk/gtkbinlayout.c b/gtk/gtkbinlayout.c
index d5cc211e7e..cbc3510892 100644
--- a/gtk/gtkbinlayout.c
+++ b/gtk/gtkbinlayout.c
@@ -1,8 +1,18 @@
 /* gtkbinlayout.c: Layout manager for bin-like widgets
- *
  * Copyright 2019  GNOME Foundation
  *
- * SPDX-License-Identifier: LGPL 2.1+
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  */
 
 /**
@@ -43,23 +53,29 @@ gtk_bin_layout_measure (GtkLayoutManager *layout_manager,
 {
   GtkWidget *child;
 
-  child = _gtk_widget_get_first_child (widget);
-  while (child != NULL)
+  for (child = _gtk_widget_get_first_child (widget);
+       child != NULL;
+       child = _gtk_widget_get_next_sibling (child))
     {
-      GtkWidget *next = _gtk_widget_get_next_sibling (child);
-
       if (gtk_widget_get_visible (child))
         {
           int child_min = 0;
           int child_nat = 0;
+          int child_min_baseline = -1;
+          int child_nat_baseline = -1;
 
-          gtk_widget_measure (child, orientation, for_size, &child_min, &child_nat, NULL, NULL);
+          gtk_widget_measure (child, orientation, for_size,
+                              &child_min, &child_nat,
+                              &child_min_baseline, &child_nat_baseline);
 
           *minimum = MAX (*minimum, child_min);
           *natural = MAX (*natural, child_nat);
-        }
 
-      child = next;
+          if (child_min_baseline > -1)
+            *minimum_baseline = MAX (*minimum_baseline, child_min_baseline);
+          if (child_nat_baseline > -1)
+            *natural_baseline = MAX (*natural_baseline, child_nat_baseline);
+        }
     }
 }
 
@@ -72,19 +88,12 @@ gtk_bin_layout_allocate (GtkLayoutManager *layout_manager,
 {
   GtkWidget *child;
 
-  child = _gtk_widget_get_first_child (widget);
-  while (child != NULL)
+  for (child = _gtk_widget_get_first_child (widget);
+       child != NULL;
+       child = _gtk_widget_get_next_sibling (child))
     {
-      GtkWidget *next = _gtk_widget_get_next_sibling (child);
-
       if (child && gtk_widget_get_visible (child))
-        gtk_widget_size_allocate (child,
-                                  &(GtkAllocation) {
-                                    0, 0,
-                                    width, height
-                                  }, baseline);
-
-      child = next;
+        gtk_widget_allocate (child, width, height, baseline, NULL);
     }
 }
 static void
diff --git a/gtk/gtkbinlayout.h b/gtk/gtkbinlayout.h
index b19581f2eb..400bafc733 100644
--- a/gtk/gtkbinlayout.h
+++ b/gtk/gtkbinlayout.h
@@ -1,8 +1,18 @@
 /* gtkbinlayout.h: Layout manager for bin-like widgets
- *
  * Copyright 2019  GNOME Foundation
  *
- * SPDX-License-Identifier: LGPL 2.1+
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  */
 #pragma once
 
diff --git a/gtk/gtkboxlayout.c b/gtk/gtkboxlayout.c
index 99167163b3..0b88e39fcf 100644
--- a/gtk/gtkboxlayout.c
+++ b/gtk/gtkboxlayout.c
@@ -9,7 +9,7 @@
  *
  * This library is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the GNU
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the GNU
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
@@ -303,7 +303,7 @@ gtk_box_layout_compute_opposite_size (GtkBoxLayout *self,
        child = _gtk_widget_get_next_sibling (child))
     {
       if (_gtk_widget_get_visible (child))
-       {
+        {
           gtk_widget_measure (child,
                               self->orientation,
                               -1,
@@ -311,8 +311,8 @@ gtk_box_layout_compute_opposite_size (GtkBoxLayout *self,
                               NULL, NULL);
 
           children_minimum_size += sizes[i].minimum_size;
-         i += 1;
-       }
+          i += 1;
+        }
     }
 
   if (self->homogeneous)
@@ -335,10 +335,10 @@ gtk_box_layout_compute_opposite_size (GtkBoxLayout *self,
        * and is available for expanding children.
        */
       if (nexpand_children > 0)
-       {
+        {
           size_given_to_child = extra_space / nexpand_children;
           n_extra_widgets = extra_space % nexpand_children;
-       }
+        }
       else
         {
           size_given_to_child = 0;
@@ -410,22 +410,22 @@ gtk_box_layout_compute_opposite_size (GtkBoxLayout *self,
       computed_minimum = MAX (computed_minimum, computed_minimum_below + computed_minimum_above);
       computed_natural = MAX (computed_natural, computed_natural_below + computed_natural_above);
       switch (self->baseline_position)
-       {
-       case GTK_BASELINE_POSITION_TOP:
-         computed_minimum_baseline = computed_minimum_above;
-         computed_natural_baseline = computed_natural_above;
-         break;
-       case GTK_BASELINE_POSITION_CENTER:
-         computed_minimum_baseline = computed_minimum_above + MAX((computed_minimum - 
(computed_minimum_above + computed_minimum_below)) / 2, 0);
-         computed_natural_baseline = computed_natural_above + MAX((computed_natural - 
(computed_natural_above + computed_natural_below)) / 2, 0);
-         break;
-       case GTK_BASELINE_POSITION_BOTTOM:
-         computed_minimum_baseline = computed_minimum - computed_minimum_below;
-         computed_natural_baseline = computed_natural - computed_natural_below;
-         break;
+        {
+        case GTK_BASELINE_POSITION_TOP:
+          computed_minimum_baseline = computed_minimum_above;
+          computed_natural_baseline = computed_natural_above;
+          break;
+        case GTK_BASELINE_POSITION_CENTER:
+          computed_minimum_baseline = computed_minimum_above + MAX((computed_minimum - 
(computed_minimum_above + computed_minimum_below)) / 2, 0);
+          computed_natural_baseline = computed_natural_above + MAX((computed_natural - 
(computed_natural_above + computed_natural_below)) / 2, 0);
+          break;
+        case GTK_BASELINE_POSITION_BOTTOM:
+          computed_minimum_baseline = computed_minimum - computed_minimum_below;
+          computed_natural_baseline = computed_natural - computed_natural_below;
+          break;
         default:
           break;
-       }
+        }
     }
 
   if (minimum != NULL)
@@ -515,7 +515,7 @@ gtk_box_layout_allocate (GtkLayoutManager *layout_manager,
        child = _gtk_widget_get_next_sibling (child))
     {
       if (!_gtk_widget_get_visible (child))
-       continue;
+        continue;
 
       gtk_widget_measure (child,
                           self->orientation,
@@ -550,10 +550,10 @@ gtk_box_layout_allocate (GtkLayoutManager *layout_manager,
        * and is available for expanding children.
        */
       if (nexpand_children > 0)
-       {
+        {
           size_given_to_child = extra_space / nexpand_children;
           n_extra_widgets = extra_space % nexpand_children;
-       }
+        }
       else
         {
           size_given_to_child = 0;
@@ -634,22 +634,22 @@ gtk_box_layout_allocate (GtkLayoutManager *layout_manager,
   if (baseline == -1 && have_baseline)
     {
       /* TODO: This is purely based on the minimum baseline, when things fit we should
-        use the natural one? */
+         use the natural one? */
 
       switch (self->baseline_position)
-       {
-       case GTK_BASELINE_POSITION_TOP:
-         baseline = minimum_above;
-         break;
-       case GTK_BASELINE_POSITION_CENTER:
-         baseline = minimum_above + (height - (minimum_above + minimum_below)) / 2;
-         break;
-       case GTK_BASELINE_POSITION_BOTTOM:
-         baseline = height - minimum_below;
-         break;
+        {
+        case GTK_BASELINE_POSITION_TOP:
+          baseline = minimum_above;
+          break;
+        case GTK_BASELINE_POSITION_CENTER:
+          baseline = minimum_above + (height - (minimum_above + minimum_below)) / 2;
+          break;
+        case GTK_BASELINE_POSITION_BOTTOM:
+          baseline = height - minimum_below;
+          break;
         default:
           break;
-       }
+        }
     }
 
   /* Allocate child positions. */
@@ -846,7 +846,7 @@ gtk_box_layout_get_spacing (GtkBoxLayout *box_layout)
  */
 void
 gtk_box_layout_set_baseline_position (GtkBoxLayout        *box_layout,
-                                     GtkBaselinePosition  position)
+                                      GtkBaselinePosition  position)
 {
   g_return_if_fail (GTK_IS_BOX_LAYOUT (box_layout));
 
diff --git a/gtk/gtkcustomlayout.c b/gtk/gtkcustomlayout.c
new file mode 100644
index 0000000000..d16f603e95
--- /dev/null
+++ b/gtk/gtkcustomlayout.c
@@ -0,0 +1,130 @@
+/**
+ * SECTION:gtkcustomlayout
+ * @Title: GtkCustomLayout
+ * @Short_description: A convenience layout manager
+ *
+ * #GtkCustomLayout is a convenience type meant to be used as a transition
+ * mechanism between #GtkContainers implementing a layout policy, and
+ * #GtkLayoutManager classes.
+ *
+ * A #GtkCustomLayout uses closures matching to the old #GtkWidget virtual
+ * functions for size negotiation, to ease the porting towards the
+ * corresponding #GtkLayoutManager virtual functions.
+ */
+
+#include "config.h"
+
+#include "gtkcustomlayout.h"
+
+struct _GtkCustomLayout
+{
+  GtkLayoutManager parent_instance;
+
+  GtkCustomRequestModeFunc request_mode_func;
+  GtkCustomMeasureFunc measure_func;
+  GtkCustomAllocateFunc allocate_func;
+};
+
+G_DEFINE_TYPE (GtkCustomLayout, gtk_custom_layout, GTK_TYPE_LAYOUT_MANAGER)
+
+static GtkSizeRequestMode
+gtk_custom_layout_get_request_mode (GtkLayoutManager *manager,
+                                    GtkWidget        *widget)
+{
+  GtkCustomLayout *self = GTK_CUSTOM_LAYOUT (manager);
+
+  if (self->request_mode_func != NULL)
+    return self->request_mode_func (widget);
+
+  return GTK_SIZE_REQUEST_CONSTANT_SIZE;
+}
+
+static void
+gtk_custom_layout_measure (GtkLayoutManager *manager,
+                           GtkWidget        *widget,
+                           GtkOrientation    orientation,
+                           int               for_size,
+                           int              *minimum,
+                           int              *natural,
+                           int              *minimum_baseline,
+                           int              *natural_baseline)
+{
+  GtkCustomLayout *self = GTK_CUSTOM_LAYOUT (manager);
+  int min = 0, nat = 0;
+  int min_baseline = -1, nat_baseline = -1;
+
+  self->measure_func (widget, orientation, for_size,
+                      &min, &nat,
+                      &min_baseline, &nat_baseline);
+
+  if (minimum != NULL)
+    *minimum = min;
+  if (natural != NULL)
+    *natural = nat;
+
+  if (minimum_baseline != NULL)
+    *minimum_baseline = min_baseline;
+  if (natural_baseline != NULL)
+    *natural_baseline = nat_baseline;
+}
+
+static void
+gtk_custom_layout_allocate (GtkLayoutManager *manager,
+                            GtkWidget        *widget,
+                            int               width,
+                            int               height,
+                            int               baseline)
+{
+  GtkCustomLayout *self = GTK_CUSTOM_LAYOUT (manager);
+
+  self->allocate_func (widget, width, height, baseline);
+}
+
+static void
+gtk_custom_layout_class_init (GtkCustomLayoutClass *klass)
+{
+  GtkLayoutManagerClass *layout_class = GTK_LAYOUT_MANAGER_CLASS (klass);
+
+  layout_class->get_request_mode = gtk_custom_layout_get_request_mode;
+  layout_class->measure = gtk_custom_layout_measure;
+  layout_class->allocate = gtk_custom_layout_allocate;
+}
+
+static void
+gtk_custom_layout_init (GtkCustomLayout *self)
+{
+}
+
+/**
+ * gtk_custom_layout_new:
+ * @request_mode: (nullable): a function to retrieve
+ *   the #GtkSizeRequestMode of the widget using the layout; the
+ *   default request mode is %GTK_SIZE_REQUEST_CONSTANT_SIZE
+ * @measure: a function to measure the widget using the layout manager
+ * @allocate:  a function to allocate the children of the widget using
+ *   the layout manager
+ *
+ * Creates a new legacy layout manager.
+ *
+ * Legacy layout managers map to the old #GtkWidget size negotiation
+ * virtual functions, and are meant to be used during the transition
+ * from layout containers to layout manager delegates.
+ *
+ * Returns: (transfer full): the newly created #GtkCustomLayout
+ */
+GtkLayoutManager *
+gtk_custom_layout_new (GtkCustomRequestModeFunc request_mode,
+                       GtkCustomMeasureFunc measure,
+                       GtkCustomAllocateFunc allocate)
+{
+  GtkCustomLayout *self = g_object_new (GTK_TYPE_CUSTOM_LAYOUT, NULL);
+
+  g_return_val_if_fail (measure != NULL, NULL);
+  g_return_val_if_fail (allocate != NULL, NULL);
+
+  self->request_mode_func = request_mode;
+  self->measure_func = measure;
+  self->allocate_func = allocate;
+
+  return GTK_LAYOUT_MANAGER (self);
+}
diff --git a/gtk/gtkcustomlayout.h b/gtk/gtkcustomlayout.h
new file mode 100644
index 0000000000..21beb04853
--- /dev/null
+++ b/gtk/gtkcustomlayout.h
@@ -0,0 +1,77 @@
+/* gtkcustomlayout.h: Simple layout manager
+ * Copyright 2019  GNOME Foundation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+
+#include <gtk/gtklayoutmanager.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_CUSTOM_LAYOUT (gtk_custom_layout_get_type ())
+
+/**
+ * GtkCustomRequestModeFunc:
+ * @widget: the widget to be queried
+ *
+ * Queries a widget for its preferred size request mode.
+ *
+ * Returns: the size request mode
+ */
+typedef GtkSizeRequestMode (* GtkCustomRequestModeFunc) (GtkWidget *widget);
+
+/**
+ * GtkCustomMeasureFunc:
+ * @widget: the widget to be measured
+ * @orientation: the direction to be measured
+ * @for_size: the size to be measured for
+ * @minimum: (out): the measured minimum size of the widget
+ * @natural: (out): the measured natural size of the widget
+ * @minimum_baseline: (out): the measured minimum baseline of the widget
+ * @natural_baseline: (out): the measured natural baseline of the widget
+ *
+ * A function to be used by #GtkCustomLayout to measure a widget.
+ */
+typedef void (* GtkCustomMeasureFunc) (GtkWidget      *widget,
+                                       GtkOrientation  orientation,
+                                       int             for_size,
+                                       int            *minimum,
+                                       int            *natural,
+                                       int            *minimum_baseline,
+                                       int            *natural_baseline);
+
+/**
+ * GtkCustomAllocateFunc:
+ * @widget: the widget to allocate
+ * @width: the new width of the widget
+ * @height: the new height of the widget
+ * @baseline: the new baseline of the widget, or -1
+ *
+ * A function to be used by #GtkCustomLayout to allocate a widget.
+ */
+typedef void (* GtkCustomAllocateFunc) (GtkWidget    *widget,
+                                        int           width,
+                                        int           height,
+                                        int           baseline);
+
+GDK_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (GtkCustomLayout, gtk_custom_layout, GTK, CUSTOM_LAYOUT, GtkLayoutManager)
+
+GDK_AVAILABLE_IN_ALL
+GtkLayoutManager *      gtk_custom_layout_new   (GtkCustomRequestModeFunc request_mode,
+                                                 GtkCustomMeasureFunc     measure,
+                                                 GtkCustomAllocateFunc    allocate);
+
+G_END_DECLS
diff --git a/gtk/gtklayoutmanager.c b/gtk/gtklayoutmanager.c
index 02abe0d5c7..9ac3d986b7 100644
--- a/gtk/gtklayoutmanager.c
+++ b/gtk/gtklayoutmanager.c
@@ -26,9 +26,8 @@
  * 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.
+ * layout policy for the children of a widget, or if you want to determine
+ * the size of a widget depending on its contents.
  *
  * Each #GtkWidget can only have a #GtkLayoutManager instance associated to it
  * at any given time; it is possible, though, to replace the layout manager
@@ -54,6 +53,9 @@
 
 typedef struct {
   GtkWidget *widget;
+
+  /* HashTable<Widget, LayoutChild> */
+  GHashTable *layout_children;
 } GtkLayoutManagerPrivate;
 
 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GtkLayoutManager, gtk_layout_manager, G_TYPE_OBJECT)
@@ -200,7 +202,7 @@ gtk_layout_manager_measure (GtkLayoutManager *manager,
  * @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
+ * @baseline: the baseline position of the @widget, or -1
  *
  * This function assigns the given @width, @height, and @baseline to
  * a @widget, and computes the position and sizes of the children of
@@ -217,6 +219,7 @@ gtk_layout_manager_allocate (GtkLayoutManager *manager,
 
   g_return_if_fail (GTK_IS_LAYOUT_MANAGER (manager));
   g_return_if_fail (GTK_IS_WIDGET (widget));
+  g_return_if_fail (baseline >= -1);
 
   klass = GTK_LAYOUT_MANAGER_GET_CLASS (manager);
 
@@ -226,24 +229,22 @@ gtk_layout_manager_allocate (GtkLayoutManager *manager,
 /**
  * gtk_layout_manager_get_request_mode:
  * @manager: a #GtkLayoutManager
- * @widget: the #GtkWidget using @manager
  *
  * Retrieves the request mode of @manager.
  *
  * Returns: a #GtkSizeRequestMode
  */
 GtkSizeRequestMode
-gtk_layout_manager_get_request_mode (GtkLayoutManager *manager,
-                                     GtkWidget        *widget)
+gtk_layout_manager_get_request_mode (GtkLayoutManager *manager)
 {
+  GtkLayoutManagerPrivate *priv = gtk_layout_manager_get_instance_private (manager);
   GtkLayoutManagerClass *klass;
 
   g_return_val_if_fail (GTK_IS_LAYOUT_MANAGER (manager), GTK_SIZE_REQUEST_CONSTANT_SIZE);
-  g_return_val_if_fail (GTK_IS_WIDGET (widget), GTK_SIZE_REQUEST_CONSTANT_SIZE);
 
   klass = GTK_LAYOUT_MANAGER_GET_CLASS (manager);
 
-  return klass->get_request_mode (manager, widget);
+  return klass->get_request_mode (manager, priv->widget);
 }
 
 /**
@@ -284,32 +285,51 @@ gtk_layout_manager_layout_changed (GtkLayoutManager *manager)
     gtk_widget_queue_resize (priv->widget);
 }
 
+static void
+remove_layout_child (GtkWidget        *widget,
+                     GtkWidget        *old_parent,
+                     GtkLayoutManager *self)
+{
+  GtkLayoutManagerPrivate *priv = gtk_layout_manager_get_instance_private (self);
+
+  if (priv->layout_children != NULL)
+    {
+      g_hash_table_remove (priv->layout_children, widget);
+      if (g_hash_table_size (priv->layout_children) == 0)
+        g_clear_pointer (&priv->layout_children, g_hash_table_unref);
+    }
+
+  g_signal_handlers_disconnect_by_func (widget, remove_layout_child, self);
+}
+
 /**
  * gtk_layout_manager_get_layout_child:
  * @manager: a #GtkLayoutManager
- * @widget: a #GtkWidget
+ * @child: a #GtkWidget
  *
  * Retrieves a #GtkLayoutChild instance for the #GtkLayoutManager, creating
- * one if necessary
+ * one if necessary.
+ *
+ * The @child widget must be a child of the widget using @manager.
  *
  * The #GtkLayoutChild instance is owned by the #GtkLayoutManager, and is
- * guaranteed to exist as long as @widget is a child of the #GtkWidget using
+ * guaranteed to exist as long as @child is a child of the #GtkWidget using
  * the given #GtkLayoutManager.
  *
  * Returns: (transfer none): a #GtkLayoutChild
  */
 GtkLayoutChild *
 gtk_layout_manager_get_layout_child (GtkLayoutManager *manager,
-                                     GtkWidget        *widget)
+                                     GtkWidget        *child)
 {
   GtkLayoutManagerPrivate *priv = gtk_layout_manager_get_instance_private (manager);
   GtkLayoutChild *res;
   GtkWidget *parent;
 
   g_return_val_if_fail (GTK_IS_LAYOUT_MANAGER (manager), NULL);
-  g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
+  g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
 
-  parent = gtk_widget_get_parent (widget);
+  parent = gtk_widget_get_parent (child);
   g_return_val_if_fail (parent != NULL, NULL);
 
   if (priv->widget != parent)
@@ -317,7 +337,7 @@ gtk_layout_manager_get_layout_child (GtkLayoutManager *manager,
       g_critical ("The parent %s %p of the widget %s %p does not "
                   "use the given layout manager of type %s %p",
                   gtk_widget_get_name (parent), parent,
-                  gtk_widget_get_name (widget), widget,
+                  gtk_widget_get_name (child), child,
                   G_OBJECT_TYPE_NAME (manager), manager);
       return NULL;
     }
@@ -330,10 +350,14 @@ gtk_layout_manager_get_layout_child (GtkLayoutManager *manager,
       return NULL;
     }
 
-  /* We store the LayoutChild into the Widget, so that the LayoutChild
-   * instance goes away once the Widget goes away
-   */
-  res = g_object_get_qdata (G_OBJECT (widget), quark_layout_child);
+  if (priv->layout_children == NULL)
+    {
+      priv->layout_children = g_hash_table_new_full (NULL, NULL,
+                                                     NULL,
+                                                     (GDestroyNotify) g_object_unref);
+    }
+
+  res = g_hash_table_lookup (priv->layout_children, child);
   if (res != NULL)
     {
       /* If the LayoutChild instance is stale, and refers to another
@@ -345,13 +369,12 @@ gtk_layout_manager_get_layout_child (GtkLayoutManager *manager,
         return res;
     }
 
-  res = GTK_LAYOUT_MANAGER_GET_CLASS (manager)->create_layout_child (manager, widget);
+  res = GTK_LAYOUT_MANAGER_GET_CLASS (manager)->create_layout_child (manager, parent, child);
   g_assert (res != NULL);
   g_assert (g_type_is_a (G_OBJECT_TYPE (res), GTK_TYPE_LAYOUT_CHILD));
 
-  g_object_set_qdata_full (G_OBJECT (widget), quark_layout_child,
-                           res,
-                           g_object_unref);
+  g_hash_table_insert (priv->layout_children, child, res);
+  g_signal_connect (child, "parent-set", G_CALLBACK (remove_layout_child), manager);
 
   return res;
 }
diff --git a/gtk/gtklayoutmanager.h b/gtk/gtklayoutmanager.h
index 02f2173a36..53f4931c69 100644
--- a/gtk/gtklayoutmanager.h
+++ b/gtk/gtklayoutmanager.h
@@ -1,5 +1,5 @@
 /* gtklayoutmanager.h: Layout manager base class
- * Copyright 2018  The GNOME Foundation
+ * Copyright 2019  The GNOME Foundation
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -18,6 +18,7 @@
  */
 #pragma once
 
+#include <gsk/gsk.h>
 #include <gtk/gtktypes.h>
 #include <gtk/gtkwidget.h>
 #include <gtk/gtklayoutchild.h>
@@ -38,6 +39,8 @@ G_DECLARE_DERIVABLE_TYPE (GtkLayoutManager, gtk_layout_manager, GTK, LAYOUT_MANA
  *   sizes of the widget using the layout manager for a given orientation
  * @allocate: a virtual function, used to allocate the size of the widget
  *   using the layout manager
+ * @create_layout_child: a virtual function, used to create a #GtkLayoutChild
+ *   meta object for the layout properties
  *
  * The `GtkLayoutManagerClass` structure contains only private data, and
  * should only be accessed through the provided API, or when subclassing
@@ -68,7 +71,8 @@ struct _GtkLayoutManagerClass
                                               int               baseline);
 
   GtkLayoutChild *   (* create_layout_child) (GtkLayoutManager *manager,
-                                              GtkWidget        *widget);
+                                              GtkWidget        *widget,
+                                              GtkWidget        *for_child);
 
   /*< private >*/
   gpointer _padding[16];
@@ -90,8 +94,7 @@ void                    gtk_layout_manager_allocate             (GtkLayoutManage
                                                                  int               height,
                                                                  int               baseline);
 GDK_AVAILABLE_IN_ALL
-GtkSizeRequestMode      gtk_layout_manager_get_request_mode     (GtkLayoutManager *manager,
-                                                                 GtkWidget        *widget);
+GtkSizeRequestMode      gtk_layout_manager_get_request_mode     (GtkLayoutManager *manager);
 
 GDK_AVAILABLE_IN_ALL
 GtkWidget *             gtk_layout_manager_get_widget           (GtkLayoutManager *manager);
@@ -101,6 +104,6 @@ void                    gtk_layout_manager_layout_changed       (GtkLayoutManage
 
 GDK_AVAILABLE_IN_ALL
 GtkLayoutChild *        gtk_layout_manager_get_layout_child     (GtkLayoutManager *manager,
-                                                                 GtkWidget        *widget);
+                                                                 GtkWidget        *child);
 
 G_END_DECLS
diff --git a/gtk/gtksizerequest.c b/gtk/gtksizerequest.c
index cd25debcae..3039a5a239 100644
--- a/gtk/gtksizerequest.c
+++ b/gtk/gtksizerequest.c
@@ -558,7 +558,7 @@ gtk_widget_get_request_mode (GtkWidget *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);
+        cache->request_mode = gtk_layout_manager_get_request_mode (layout_manager);
       else
         cache->request_mode = GTK_WIDGET_GET_CLASS (widget)->get_request_mode (widget);
 
diff --git a/gtk/gtkswitch.c b/gtk/gtkswitch.c
index 420927f13e..56a077abd5 100644
--- a/gtk/gtkswitch.c
+++ b/gtk/gtkswitch.c
@@ -61,7 +61,7 @@
 #include "gtkgizmoprivate.h"
 #include "gtkintl.h"
 #include "gtkimage.h"
-#include "gtklegacylayoutprivate.h"
+#include "gtkcustomlayout.h"
 #include "gtkmarshalers.h"
 #include "gtkprivate.h"
 #include "gtkprogresstrackerprivate.h"
@@ -641,11 +641,10 @@ gtk_switch_init (GtkSwitch *self)
   gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (gesture));
   priv->pan_gesture = gesture;
 
-  layout = gtk_legacy_layout_new (NULL,
+  layout = gtk_custom_layout_new (NULL,
                                   gtk_switch_measure,
                                   gtk_switch_allocate);
   gtk_widget_set_layout_manager (GTK_WIDGET (self), layout);
-  g_object_unref (layout);
 
   priv->on_image = gtk_image_new_from_icon_name ("switch-on-symbolic");
   gtk_widget_set_parent (priv->on_image, GTK_WIDGET (self));
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index 3b32da8041..a7f24b3132 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -8101,7 +8101,8 @@ gtk_widget_dispose (GObject *object)
   while (priv->paintables)
     gtk_widget_paintable_set_widget (priv->paintables->data, NULL);
 
-  gtk_widget_set_layout_manager (widget, NULL);
+  if (priv->layout_manager != NULL)
+    gtk_layout_manager_set_widget (priv->layout_manager, NULL);
   g_clear_object (&priv->layout_manager);
 
   priv->visible = FALSE;
@@ -13619,8 +13620,6 @@ gtk_widget_get_height (GtkWidget *widget)
  *
  * 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,
@@ -13632,15 +13631,16 @@ gtk_widget_set_layout_manager (GtkWidget        *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);
+  if (priv->layout_manager == layout_manager)
+    return;
 
-      gtk_widget_queue_resize (widget);
+  priv->layout_manager = layout_manager;
+  if (priv->layout_manager != NULL)
+    gtk_layout_manager_set_widget (priv->layout_manager, widget);
 
-      g_object_notify_by_pspec (G_OBJECT (widget), widget_props[PROP_LAYOUT_MANAGER]);
-    }
+  gtk_widget_queue_resize (widget);
+
+  g_object_notify_by_pspec (G_OBJECT (widget), widget_props[PROP_LAYOUT_MANAGER]);
 }
 
 /**
diff --git a/gtk/meson.build b/gtk/meson.build
index 43f38c0e99..5a880737ba 100644
--- a/gtk/meson.build
+++ b/gtk/meson.build
@@ -100,6 +100,7 @@ gtk_private_sources = files([
   'gtkcssvalue.c',
   'gtkcsswidgetnode.c',
   'gtkcsswin32sizevalue.c',
+  'gtkcustomlayout.c',
   'gtkfilechooserembed.c',
   'gtkfilechooserentry.c',
   'gtkfilechoosererrorstack.c',
@@ -116,7 +117,6 @@ gtk_private_sources = files([
   'gtkiconhelper.c',
   'gtkkineticscrolling.c',
   'gtkkeyhash.c',
-  'gtklegacylayout.c',
   'gtkmagnifier.c',
   'gtkmenusectionbox.c',
   'gtkmenutracker.c',
@@ -462,6 +462,7 @@ gtk_public_headers = files([
   'gtkcontainer.h',
   'gtkcssprovider.h',
   'gtkcsssection.h',
+  'gtkcustomlayout.h',
   'gtkdebug.h',
   'gtkdialog.h',
   'gtkdnd.h',


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]