[gtk/overlay-layout] overlay: Use a layout manager



commit 6e4cbed83e2f5b9ebe7ec8c6bcac5e6fb39c20a0
Author: Matthias Clasen <mclasen redhat com>
Date:   Wed Mar 27 23:47:32 2019 -0400

    overlay: Use a layout manager
    
    This lets us get rid of the child properties,
    by converting them to layout properties.

 gtk/gtkoverlay.c              | 373 +++--------------------------------
 gtk/gtkoverlay.h              |   1 -
 gtk/gtkoverlaylayout.c        | 448 ++++++++++++++++++++++++++++++++++++++++++
 gtk/gtkoverlaylayoutprivate.h |  57 ++++++
 gtk/meson.build               |   1 +
 5 files changed, 537 insertions(+), 343 deletions(-)
---
diff --git a/gtk/gtkoverlay.c b/gtk/gtkoverlay.c
index 0b3c8932c3..5195f9ac0a 100644
--- a/gtk/gtkoverlay.c
+++ b/gtk/gtkoverlay.c
@@ -22,6 +22,7 @@
 
 #include "gtkoverlay.h"
 
+#include "gtkoverlaylayoutprivate.h"
 #include "gtkbuildable.h"
 #include "gtkintl.h"
 #include "gtkmarshalers.h"
@@ -61,104 +62,25 @@
  * whose alignments cause them to be positioned at an edge get the style classes
  * “.left”, “.right”, “.top”, and/or “.bottom” according to their position.
  */
-typedef struct _GtkOverlayChild GtkOverlayChild;
-
-struct _GtkOverlayChild
-{
-  guint measure : 1;
-  guint clip_overlay : 1;
-};
 
 enum {
   GET_CHILD_POSITION,
   LAST_SIGNAL
 };
 
-enum
-{
-  CHILD_PROP_0,
-  CHILD_PROP_PASS_THROUGH,
-  CHILD_PROP_MEASURE,
-  CHILD_PROP_CLIP_OVERLAY
-};
-
 static guint signals[LAST_SIGNAL] = { 0 };
-static GQuark child_data_quark = 0;
 
 static void gtk_overlay_buildable_init (GtkBuildableIface *iface);
 
+typedef struct {
+  GtkLayoutManager *layout;
+} GtkOverlayPrivate;
+
 G_DEFINE_TYPE_WITH_CODE (GtkOverlay, gtk_overlay, GTK_TYPE_BIN,
+                         G_ADD_PRIVATE (GtkOverlay)
                          G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
                                                 gtk_overlay_buildable_init))
 
-static void
-gtk_overlay_set_overlay_child (GtkWidget       *widget,
-                               GtkOverlayChild *child_data)
-{
-  g_object_set_qdata_full (G_OBJECT (widget), child_data_quark, child_data, g_free);
-}
-
-static GtkOverlayChild *
-gtk_overlay_get_overlay_child (GtkWidget *widget)
-{
-  return (GtkOverlayChild *) g_object_get_qdata (G_OBJECT (widget), child_data_quark);
-}
-
-static void
-gtk_overlay_measure (GtkWidget      *widget,
-                     GtkOrientation  orientation,
-                     int             for_size,
-                     int            *minimum,
-                     int            *natural,
-                     int            *minimum_baseline,
-                     int            *natural_baseline)
-{
-  GtkWidget *child;
-
-  for (child = gtk_widget_get_first_child (widget);
-       child != NULL;
-       child = gtk_widget_get_next_sibling (child))
-    {
-      GtkOverlayChild *child_data = gtk_overlay_get_overlay_child (child);
-
-      if (child_data == NULL || child_data->measure)
-        {
-          int child_min, child_nat, child_min_baseline, child_nat_baseline;
-
-          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);
-          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);
-        }
-    }
-}
-
-static void
-gtk_overlay_compute_child_allocation (GtkOverlay      *overlay,
-                                      GtkWidget       *widget,
-                                      GtkOverlayChild *child,
-                                      GtkAllocation   *widget_allocation)
-{
-  GtkAllocation allocation;
-  gboolean result;
-
-  g_signal_emit (overlay, signals[GET_CHILD_POSITION],
-                 0, widget, &allocation, &result);
-
-  widget_allocation->x = allocation.x;
-  widget_allocation->y = allocation.y;
-  widget_allocation->width = allocation.width;
-  widget_allocation->height = allocation.height;
-}
-
 static GtkAlign
 effective_align (GtkAlign         align,
                  GtkTextDirection direction)
@@ -177,110 +99,6 @@ effective_align (GtkAlign         align,
     }
 }
 
-static void
-gtk_overlay_child_update_style_classes (GtkOverlay *overlay,
-                                        GtkWidget *child,
-                                        GtkAllocation *child_allocation)
-{
-  int width, height;
-  GtkAlign valign, halign;
-  gboolean is_left, is_right, is_top, is_bottom;
-  gboolean has_left, has_right, has_top, has_bottom;
-  GtkStyleContext *context;
-
-  context = gtk_widget_get_style_context (child);
-  has_left = gtk_style_context_has_class (context, GTK_STYLE_CLASS_LEFT);
-  has_right = gtk_style_context_has_class (context, GTK_STYLE_CLASS_RIGHT);
-  has_top = gtk_style_context_has_class (context, GTK_STYLE_CLASS_TOP);
-  has_bottom = gtk_style_context_has_class (context, GTK_STYLE_CLASS_BOTTOM);
-
-  is_left = is_right = is_top = is_bottom = FALSE;
-
-  width = gtk_widget_get_width (GTK_WIDGET (overlay));
-  height = gtk_widget_get_height (GTK_WIDGET (overlay));
-
-  halign = effective_align (gtk_widget_get_halign (child),
-                            gtk_widget_get_direction (child));
-
-  if (halign == GTK_ALIGN_START)
-    is_left = (child_allocation->x == 0);
-  else if (halign == GTK_ALIGN_END)
-    is_right = (child_allocation->x + child_allocation->width == width);
-
-  valign = gtk_widget_get_valign (child);
-
-  if (valign == GTK_ALIGN_START)
-    is_top = (child_allocation->y == 0);
-  else if (valign == GTK_ALIGN_END)
-    is_bottom = (child_allocation->y + child_allocation->height == height);
-
-  if (has_left && !is_left)
-    gtk_style_context_remove_class (context, GTK_STYLE_CLASS_LEFT);
-  else if (!has_left && is_left)
-    gtk_style_context_add_class (context, GTK_STYLE_CLASS_LEFT);
-
-  if (has_right && !is_right)
-    gtk_style_context_remove_class (context, GTK_STYLE_CLASS_RIGHT);
-  else if (!has_right && is_right)
-    gtk_style_context_add_class (context, GTK_STYLE_CLASS_RIGHT);
-
-  if (has_top && !is_top)
-    gtk_style_context_remove_class (context, GTK_STYLE_CLASS_TOP);
-  else if (!has_top && is_top)
-    gtk_style_context_add_class (context, GTK_STYLE_CLASS_TOP);
-
-  if (has_bottom && !is_bottom)
-    gtk_style_context_remove_class (context, GTK_STYLE_CLASS_BOTTOM);
-  else if (!has_bottom && is_bottom)
-    gtk_style_context_add_class (context, GTK_STYLE_CLASS_BOTTOM);
-}
-
-static void
-gtk_overlay_child_allocate (GtkOverlay      *overlay,
-                            GtkWidget       *widget,
-                            GtkOverlayChild *child)
-{
-  GtkAllocation child_allocation;
-
-  if (!gtk_widget_get_visible (widget))
-    return;
-
-  gtk_overlay_compute_child_allocation (overlay, widget, child, &child_allocation);
-
-  gtk_overlay_child_update_style_classes (overlay, widget, &child_allocation);
-  gtk_widget_size_allocate (widget, &child_allocation, -1);
-}
-
-static void
-gtk_overlay_size_allocate (GtkWidget *widget,
-                           int        width,
-                           int        height,
-                           int        baseline)
-{
-  GtkOverlay *overlay = GTK_OVERLAY (widget);
-  GtkWidget *child;
-  GtkWidget *main_widget;
-
-  main_widget = gtk_bin_get_child (GTK_BIN (overlay));
-  if (main_widget && gtk_widget_get_visible (main_widget))
-    gtk_widget_size_allocate (main_widget,
-                              &(GtkAllocation) {
-                                0, 0,
-                                width, height
-                              }, -1);
-
-  for (child = gtk_widget_get_first_child (widget);
-       child != NULL;
-       child = gtk_widget_get_next_sibling (child))
-    {
-      if (child != main_widget)
-        {
-          GtkOverlayChild *child_data = gtk_overlay_get_overlay_child (child);
-          gtk_overlay_child_allocate (overlay, child, child_data);
-        }
-    }
-}
-
 static gboolean
 gtk_overlay_get_child_position (GtkOverlay    *overlay,
                                 GtkWidget     *widget,
@@ -355,7 +173,8 @@ gtk_overlay_add (GtkContainer *container,
   /* We only get into this path if we have no child
    * (since GtkOverlay is a GtkBin) and the only child
    * we can add through gtk_container_add is the main child,
-   * which we want to keep the lowest in the hierarchy. */
+   * which we want to keep the lowest in the hierarchy.
+   */
   gtk_widget_insert_after (widget, GTK_WIDGET (container), NULL);
   _gtk_bin_set_child (GTK_BIN (container), widget);
 }
@@ -394,106 +213,6 @@ gtk_overlay_forall (GtkContainer *overlay,
     }
 }
 
-
-static void
-gtk_overlay_set_child_property (GtkContainer *container,
-                               GtkWidget    *child,
-                               guint         property_id,
-                               const GValue *value,
-                               GParamSpec   *pspec)
-{
-  GtkOverlay *overlay = GTK_OVERLAY (container);
-  GtkOverlayChild *child_info;
-  GtkWidget *main_widget;
-
-  main_widget = gtk_bin_get_child (GTK_BIN (overlay));
-  if (child == main_widget)
-    child_info = NULL;
-  else
-    {
-      child_info = gtk_overlay_get_overlay_child (child);
-      if (child_info == NULL)
-       {
-         GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
-         return;
-       }
-    }
-
-  switch (property_id)
-    {
-    case CHILD_PROP_MEASURE:
-      if (child_info)
-       {
-         if (g_value_get_boolean (value) != child_info->measure)
-           {
-             child_info->measure = g_value_get_boolean (value);
-             gtk_container_child_notify (container, child, "measure");
-              gtk_widget_queue_resize (GTK_WIDGET (overlay));
-           }
-       }
-      break;
-    case CHILD_PROP_CLIP_OVERLAY:
-      if (child_info)
-       {
-         if (g_value_get_boolean (value) != child_info->clip_overlay)
-           {
-             child_info->clip_overlay = g_value_get_boolean (value);
-             gtk_container_child_notify (container, child, "clip-overlay");
-              gtk_widget_queue_resize (GTK_WIDGET (overlay));
-           }
-       }
-      break;
-
-    default:
-      GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
-      break;
-    }
-}
-
-static void
-gtk_overlay_get_child_property (GtkContainer *container,
-                               GtkWidget    *child,
-                               guint         property_id,
-                               GValue       *value,
-                               GParamSpec   *pspec)
-{
-  GtkOverlay *overlay = GTK_OVERLAY (container);
-  GtkOverlayChild *child_info;
-  GtkWidget *main_widget;
-
-  main_widget = gtk_bin_get_child (GTK_BIN (overlay));
-  if (child == main_widget)
-    child_info = NULL;
-  else
-    {
-      child_info = gtk_overlay_get_overlay_child (child);
-      if (child_info == NULL)
-       {
-         GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
-         return;
-       }
-    }
-
-  switch (property_id)
-    {
-    case CHILD_PROP_MEASURE:
-      if (child_info)
-       g_value_set_boolean (value, child_info->measure);
-      else
-       g_value_set_boolean (value, TRUE);
-      break;
-    case CHILD_PROP_CLIP_OVERLAY:
-      if (child_info)
-       g_value_set_boolean (value, child_info->clip_overlay);
-      else
-       g_value_set_boolean (value, FALSE);
-      break;
-    default:
-      GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
-      break;
-    }
-}
-
 static void
 gtk_overlay_snapshot_child (GtkWidget   *overlay,
                             GtkWidget   *child,
@@ -540,43 +259,14 @@ gtk_overlay_class_init (GtkOverlayClass *klass)
   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
   GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
 
-  widget_class->measure = gtk_overlay_measure;
-  widget_class->size_allocate = gtk_overlay_size_allocate;
   widget_class->snapshot = gtk_overlay_snapshot;
 
   container_class->add = gtk_overlay_add;
   container_class->remove = gtk_overlay_remove;
   container_class->forall = gtk_overlay_forall;
-  container_class->set_child_property = gtk_overlay_set_child_property;
-  container_class->get_child_property = gtk_overlay_get_child_property;
 
   klass->get_child_position = gtk_overlay_get_child_position;
 
-  /**
-   * GtkOverlay:measure:
-   *
-   * Include this child in determining the child request.
-   *
-   * The main child will always be measured.
-   */
-  gtk_container_class_install_child_property (container_class, CHILD_PROP_MEASURE,
-      g_param_spec_boolean ("measure", P_("Measure"), P_("Include in size measurement"),
-                            FALSE,
-                            GTK_PARAM_READWRITE));
-
-  /**
-   * GtkOverlay:clip-overlay:
-   *
-   * Clip the overlay child widget so as to fit the parent
-   */
-  gtk_container_class_install_child_property (container_class, CHILD_PROP_CLIP_OVERLAY,
-                                              g_param_spec_boolean ("clip-overlay",
-                                                                    P_("Clip Overlay"),
-                                                                    P_("Clip the overlay child widget so as 
to fit the parent"),
-                                                                    FALSE,
-                                                                    GTK_PARAM_READWRITE));
-
-
   /**
    * GtkOverlay::get-child-position:
    * @overlay: the #GtkOverlay
@@ -611,15 +301,18 @@ gtk_overlay_class_init (GtkOverlayClass *klass)
                   GTK_TYPE_WIDGET,
                   GDK_TYPE_RECTANGLE | G_SIGNAL_TYPE_STATIC_SCOPE);
 
-  child_data_quark = g_quark_from_static_string ("gtk-overlay-child-data");
-
   gtk_widget_class_set_css_name (widget_class, I_("overlay"));
 }
 
 static void
 gtk_overlay_init (GtkOverlay *overlay)
 {
+  GtkOverlayPrivate *priv = gtk_overlay_get_instance_private (overlay);
+
   gtk_widget_set_has_surface (GTK_WIDGET (overlay), FALSE);
+
+  priv->layout = gtk_overlay_layout_new ();
+  gtk_widget_set_layout_manager (GTK_WIDGET (overlay), priv->layout);
 }
 
 static GtkBuildableIface *parent_buildable_iface;
@@ -687,15 +380,10 @@ void
 gtk_overlay_add_overlay (GtkOverlay *overlay,
                          GtkWidget  *widget)
 {
-  GtkOverlayChild *child;
-
   g_return_if_fail (GTK_IS_OVERLAY (overlay));
   g_return_if_fail (GTK_IS_WIDGET (widget));
 
-  child = g_new0 (GtkOverlayChild, 1);
-
   gtk_widget_insert_before (widget, GTK_WIDGET (overlay), NULL);
-  gtk_overlay_set_overlay_child (widget, child);
 }
 
 /**
@@ -715,12 +403,14 @@ gtk_overlay_set_measure_overlay (GtkOverlay *overlay,
                                 GtkWidget  *widget,
                                 gboolean    measure)
 {
+  GtkOverlayPrivate *priv = gtk_overlay_get_instance_private (overlay);
+  GtkOverlayLayoutChild *child;
+
   g_return_if_fail (GTK_IS_OVERLAY (overlay));
   g_return_if_fail (GTK_IS_WIDGET (widget));
 
-  gtk_container_child_set (GTK_CONTAINER (overlay), widget,
-                          "measure", measure,
-                          NULL);
+  child = GTK_OVERLAY_LAYOUT_CHILD (gtk_layout_manager_get_layout_child (priv->layout, widget));
+  gtk_overlay_layout_child_set_measure (child, measure);
 }
 
 /**
@@ -737,16 +427,14 @@ gboolean
 gtk_overlay_get_measure_overlay (GtkOverlay *overlay,
                                 GtkWidget  *widget)
 {
-  gboolean measure;
+  GtkOverlayPrivate *priv = gtk_overlay_get_instance_private (overlay);
+  GtkOverlayLayoutChild *child;
 
   g_return_val_if_fail (GTK_IS_OVERLAY (overlay), FALSE);
   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
 
-  gtk_container_child_get (GTK_CONTAINER (overlay), widget,
-                          "measure", &measure,
-                          NULL);
-
-  return measure;
+  child = GTK_OVERLAY_LAYOUT_CHILD (gtk_layout_manager_get_layout_child (priv->layout, widget));
+  return gtk_overlay_layout_child_get_measure (child);
 }
 
 /**
@@ -763,12 +451,14 @@ gtk_overlay_set_clip_overlay (GtkOverlay *overlay,
                               GtkWidget  *widget,
                               gboolean    clip_overlay)
 {
+  GtkOverlayPrivate *priv = gtk_overlay_get_instance_private (overlay);
+  GtkOverlayLayoutChild *child;
+
   g_return_if_fail (GTK_IS_OVERLAY (overlay));
   g_return_if_fail (GTK_IS_WIDGET (widget));
 
-  gtk_container_child_set (GTK_CONTAINER (overlay), widget,
-                          "clip-overlay", clip_overlay,
-                          NULL);
+  child = GTK_OVERLAY_LAYOUT_CHILD (gtk_layout_manager_get_layout_child (priv->layout, widget));
+  gtk_overlay_layout_child_set_clip_overlay (child, clip_overlay);
 }
 
 /**
@@ -785,14 +475,13 @@ gboolean
 gtk_overlay_get_clip_overlay (GtkOverlay *overlay,
                               GtkWidget  *widget)
 {
-  gboolean clip_overlay;
+  GtkOverlayPrivate *priv = gtk_overlay_get_instance_private (overlay);
+  GtkOverlayLayoutChild *child;
 
   g_return_val_if_fail (GTK_IS_OVERLAY (overlay), FALSE);
   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
 
-  gtk_container_child_get (GTK_CONTAINER (overlay), widget,
-                          "clip-overlay", &clip_overlay,
-                          NULL);
+  child = GTK_OVERLAY_LAYOUT_CHILD (gtk_layout_manager_get_layout_child (priv->layout, widget));
 
-  return clip_overlay;
+  return gtk_overlay_layout_child_get_clip_overlay (child);
 }
diff --git a/gtk/gtkoverlay.h b/gtk/gtkoverlay.h
index b0ff60d4ad..a4ac9480dd 100644
--- a/gtk/gtkoverlay.h
+++ b/gtk/gtkoverlay.h
@@ -38,7 +38,6 @@ G_BEGIN_DECLS
 
 typedef struct _GtkOverlay         GtkOverlay;
 typedef struct _GtkOverlayClass    GtkOverlayClass;
-typedef struct _GtkOverlayPrivate  GtkOverlayPrivate;
 
 struct _GtkOverlay
 {
diff --git a/gtk/gtkoverlaylayout.c b/gtk/gtkoverlaylayout.c
new file mode 100644
index 0000000000..4fa4f12dd8
--- /dev/null
+++ b/gtk/gtkoverlaylayout.c
@@ -0,0 +1,448 @@
+/* gtkoverlaylayout.c: Overlay layout manager
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * Copyright 2019 Red Hat, Inc.
+ *
+ * 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 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/>.
+ */
+
+#include "config.h"
+
+#include "gtkoverlaylayoutprivate.h"
+
+#include "gtkintl.h"
+#include "gtklayoutchild.h"
+#include "gtkoverlay.h"
+#include "gtkprivate.h"
+#include "gtkstylecontext.h"
+#include "gtkwidgetprivate.h"
+
+#include <graphene-gobject.h>
+
+struct _GtkOverlayLayout
+{
+  GtkLayoutManager parent_instance;
+};
+
+struct _GtkOverlayLayoutChild
+{
+  GtkLayoutChild parent_instance;
+
+  guint measure : 1;
+  guint clip_overlay : 1;
+};
+
+enum
+{
+  PROP_MEASURE = 1,
+  PROP_CLIP_OVERLAY,
+
+  N_CHILD_PROPERTIES
+};
+
+static GParamSpec *child_props[N_CHILD_PROPERTIES];
+
+G_DEFINE_TYPE (GtkOverlayLayoutChild, gtk_overlay_layout_child, GTK_TYPE_LAYOUT_CHILD)
+
+static void
+gtk_overlay_layout_child_set_property (GObject      *gobject,
+                                       guint         prop_id,
+                                       const GValue *value,
+                                       GParamSpec   *pspec)
+{
+  GtkOverlayLayoutChild *self = GTK_OVERLAY_LAYOUT_CHILD (gobject);
+
+  switch (prop_id)
+    {
+    case PROP_MEASURE:
+      gtk_overlay_layout_child_set_measure (self, g_value_get_boolean (value));
+      break;
+
+    case PROP_CLIP_OVERLAY:
+      gtk_overlay_layout_child_set_clip_overlay (self, g_value_get_boolean (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+gtk_overlay_layout_child_get_property (GObject    *gobject,
+                                       guint       prop_id,
+                                       GValue     *value,
+                                       GParamSpec *pspec)
+{
+  GtkOverlayLayoutChild *self = GTK_OVERLAY_LAYOUT_CHILD (gobject);
+
+  switch (prop_id)
+    {
+    case PROP_MEASURE:
+      g_value_set_boolean (value, self->measure);
+      break;
+
+    case PROP_CLIP_OVERLAY:
+      g_value_set_boolean (value, self->clip_overlay);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+gtk_overlay_layout_child_finalize (GObject *gobject)
+{
+  //GtkOverlayLayoutChild *self = GTK_OVERLAY_LAYOUT_CHILD (gobject);
+
+  G_OBJECT_CLASS (gtk_overlay_layout_child_parent_class)->finalize (gobject);
+}
+
+static void
+gtk_overlay_layout_child_class_init (GtkOverlayLayoutChildClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->set_property = gtk_overlay_layout_child_set_property;
+  gobject_class->get_property = gtk_overlay_layout_child_get_property;
+  gobject_class->finalize = gtk_overlay_layout_child_finalize;
+
+  child_props[PROP_MEASURE] =
+    g_param_spec_boolean ("measure",
+                          P_("Measure"),
+                          P_("Include in size measurement"),
+                          FALSE,
+                          GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+  child_props[PROP_CLIP_OVERLAY] =
+    g_param_spec_boolean ("clip-overlay",
+                          P_("Clip Overlay"),
+                          P_("Clip the overlay child widget so as to fit the parent"),
+                          FALSE,
+                          GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+
+  g_object_class_install_properties (gobject_class, N_CHILD_PROPERTIES, child_props);
+}
+
+static void
+gtk_overlay_layout_child_init (GtkOverlayLayoutChild *self)
+{
+}
+
+/**
+ * gtk_overlay_layout_child_set_measure:
+ * @child: a #GtkOverlayLayoutChild
+ * @measure: whether to measure this child
+ *
+ * Sets whether to measure this child.
+ */
+void
+gtk_overlay_layout_child_set_measure (GtkOverlayLayoutChild *child,
+                                      gboolean               measure)
+{
+  GtkLayoutManager *layout;
+
+  g_return_if_fail (GTK_IS_OVERLAY_LAYOUT_CHILD (child));
+
+  if (child->measure == measure)
+    return;
+
+  child->measure = measure;
+
+  layout = gtk_layout_child_get_layout_manager (GTK_LAYOUT_CHILD (child));
+  gtk_layout_manager_layout_changed (layout);
+
+  g_object_notify_by_pspec (G_OBJECT (child), child_props[PROP_MEASURE]);
+}
+
+/**
+ * gtk_overlay_layout_child_get_measure:
+ * @child: a #GtkOverlayLayoutChild
+ *
+ * Retrieves whether the child is measured.
+ *
+ * Returns: (transfer none) (nullable): whether the child is measured
+ */
+gboolean
+gtk_overlay_layout_child_get_measure (GtkOverlayLayoutChild *child)
+{
+  g_return_val_if_fail (GTK_IS_OVERLAY_LAYOUT_CHILD (child), FALSE);
+
+  return child->measure;
+}
+
+/**
+ * gtk_overlay_layout_child_set_clip_overlay:
+ * @child: a #GtkOverlayLayoutChild
+ * @clip_overlay: whether to clip this child
+ *
+ * Sets whether to clip this child.
+ */
+void
+gtk_overlay_layout_child_set_clip_overlay (GtkOverlayLayoutChild *child,
+                                           gboolean               clip_overlay)
+{
+  GtkLayoutManager *layout;
+
+  g_return_if_fail (GTK_IS_OVERLAY_LAYOUT_CHILD (child));
+
+  if (child->clip_overlay == clip_overlay)
+    return;
+
+  child->clip_overlay = clip_overlay;
+
+  layout = gtk_layout_child_get_layout_manager (GTK_LAYOUT_CHILD (child));
+  gtk_layout_manager_layout_changed (layout);
+
+  g_object_notify_by_pspec (G_OBJECT (child), child_props[PROP_CLIP_OVERLAY]);
+}
+
+/**
+ * gtk_overlay_layout_child_get_clip_overlay:
+ * @child: a #GtkOverlayLayoutChild
+ *
+ * Retrieves whether the child is clipped.
+ *
+ * Returns: (transfer none) (nullable): whether the child is clipped
+ */
+gboolean
+gtk_overlay_layout_child_get_clip_overlay (GtkOverlayLayoutChild *child)
+{
+  g_return_val_if_fail (GTK_IS_OVERLAY_LAYOUT_CHILD (child), FALSE);
+
+  return child->clip_overlay;
+}
+
+G_DEFINE_TYPE (GtkOverlayLayout, gtk_overlay_layout, GTK_TYPE_LAYOUT_MANAGER)
+
+static void
+gtk_overlay_layout_measure (GtkLayoutManager *layout_manager,
+                            GtkWidget        *widget,
+                            GtkOrientation    orientation,
+                            int               for_size,
+                            int              *minimum,
+                            int              *natural,
+                            int              *minimum_baseline,
+                            int              *natural_baseline)
+{
+  GtkOverlayLayoutChild *child_info;
+  GtkWidget *child;
+  int min, nat;
+  GtkWidget *main_widget;
+
+  main_widget = gtk_bin_get_child (GTK_BIN (widget));
+
+  min = 0;
+  nat = 0;
+
+  for (child = _gtk_widget_get_first_child (widget);
+       child != NULL;
+       child = _gtk_widget_get_next_sibling (child))
+    {
+      child_info = GTK_OVERLAY_LAYOUT_CHILD (gtk_layout_manager_get_layout_child (layout_manager, child));
+
+      if (child == main_widget || child_info->measure)
+        {
+          int child_min, child_nat, child_min_baseline, child_nat_baseline;
+
+          gtk_widget_measure (child,
+                              orientation,
+                              for_size,
+                              &child_min, &child_nat,
+                              &child_min_baseline, &child_nat_baseline);
+
+          min = MAX (min, child_min);
+          nat = MAX (nat, child_nat);
+        }
+    }
+
+  if (minimum != NULL)
+    *minimum = min;
+  if (natural != NULL)
+    *natural = nat;
+}
+
+static void
+gtk_overlay_compute_child_allocation (GtkOverlay            *overlay,
+                                      GtkWidget             *widget,
+                                      GtkOverlayLayoutChild *child,
+                                      GtkAllocation         *widget_allocation)
+{
+  GtkAllocation allocation;
+  gboolean result;
+
+  g_signal_emit_by_name (overlay, "get-child-position",
+                         widget, &allocation, &result);
+
+  widget_allocation->x = allocation.x;
+  widget_allocation->y = allocation.y;
+  widget_allocation->width = allocation.width;
+  widget_allocation->height = allocation.height;
+}
+
+static GtkAlign
+effective_align (GtkAlign         align,
+                 GtkTextDirection direction)
+{
+  switch (align)
+    {
+    case GTK_ALIGN_START:
+      return direction == GTK_TEXT_DIR_RTL ? GTK_ALIGN_END : GTK_ALIGN_START;
+    case GTK_ALIGN_END:
+      return direction == GTK_TEXT_DIR_RTL ? GTK_ALIGN_START : GTK_ALIGN_END;
+    case GTK_ALIGN_FILL:
+    case GTK_ALIGN_CENTER:
+    case GTK_ALIGN_BASELINE:
+    default:
+      return align;
+    }
+}
+
+static void
+gtk_overlay_child_update_style_classes (GtkOverlay *overlay,
+                                        GtkWidget *child,
+                                        GtkAllocation *child_allocation)
+{
+  int width, height;
+  GtkAlign valign, halign;
+  gboolean is_left, is_right, is_top, is_bottom;
+  gboolean has_left, has_right, has_top, has_bottom;
+  GtkStyleContext *context;
+
+  context = gtk_widget_get_style_context (child);
+  has_left = gtk_style_context_has_class (context, GTK_STYLE_CLASS_LEFT);
+  has_right = gtk_style_context_has_class (context, GTK_STYLE_CLASS_RIGHT);
+  has_top = gtk_style_context_has_class (context, GTK_STYLE_CLASS_TOP);
+  has_bottom = gtk_style_context_has_class (context, GTK_STYLE_CLASS_BOTTOM);
+
+  is_left = is_right = is_top = is_bottom = FALSE;
+
+  width = gtk_widget_get_width (GTK_WIDGET (overlay));
+  height = gtk_widget_get_height (GTK_WIDGET (overlay));
+
+  halign = effective_align (gtk_widget_get_halign (child),
+                            gtk_widget_get_direction (child));
+
+  if (halign == GTK_ALIGN_START)
+    is_left = (child_allocation->x == 0);
+  else if (halign == GTK_ALIGN_END)
+    is_right = (child_allocation->x + child_allocation->width == width);
+
+  valign = gtk_widget_get_valign (child);
+
+  if (valign == GTK_ALIGN_START)
+    is_top = (child_allocation->y == 0);
+  else if (valign == GTK_ALIGN_END)
+    is_bottom = (child_allocation->y + child_allocation->height == height);
+
+  if (has_left && !is_left)
+    gtk_style_context_remove_class (context, GTK_STYLE_CLASS_LEFT);
+  else if (!has_left && is_left)
+    gtk_style_context_add_class (context, GTK_STYLE_CLASS_LEFT);
+
+  if (has_right && !is_right)
+    gtk_style_context_remove_class (context, GTK_STYLE_CLASS_RIGHT);
+  else if (!has_right && is_right)
+    gtk_style_context_add_class (context, GTK_STYLE_CLASS_RIGHT);
+
+  if (has_top && !is_top)
+    gtk_style_context_remove_class (context, GTK_STYLE_CLASS_TOP);
+  else if (!has_top && is_top)
+    gtk_style_context_add_class (context, GTK_STYLE_CLASS_TOP);
+
+  if (has_bottom && !is_bottom)
+    gtk_style_context_remove_class (context, GTK_STYLE_CLASS_BOTTOM);
+  else if (!has_bottom && is_bottom)
+    gtk_style_context_add_class (context, GTK_STYLE_CLASS_BOTTOM);
+}
+
+static void
+gtk_overlay_child_allocate (GtkOverlay            *overlay,
+                            GtkWidget             *widget,
+                            GtkOverlayLayoutChild *child)
+{
+  GtkAllocation child_allocation;
+
+  if (!gtk_widget_get_visible (widget))
+    return;
+
+  gtk_overlay_compute_child_allocation (overlay, widget, child, &child_allocation);
+
+  gtk_overlay_child_update_style_classes (overlay, widget, &child_allocation);
+  gtk_widget_size_allocate (widget, &child_allocation, -1);
+}
+
+static void
+gtk_overlay_layout_allocate (GtkLayoutManager *layout_manager,
+                             GtkWidget        *widget,
+                             int               width,
+                             int               height,
+                             int               baseline)
+{
+  GtkWidget *child;
+  GtkWidget *main_widget;
+
+  main_widget = gtk_bin_get_child (GTK_BIN (widget));
+  if (main_widget && gtk_widget_get_visible (main_widget))
+    gtk_widget_size_allocate (main_widget,
+                              &(GtkAllocation) { 0, 0, width, height },
+                              -1);
+
+  for (child = _gtk_widget_get_first_child (widget);
+       child != NULL;
+       child = _gtk_widget_get_next_sibling (child))
+    {
+      if (child != main_widget)
+        {
+          GtkOverlayLayoutChild *child_data;
+          child_data = GTK_OVERLAY_LAYOUT_CHILD (gtk_layout_manager_get_layout_child (layout_manager, 
child));
+
+          gtk_overlay_child_allocate (GTK_OVERLAY (widget), child, child_data);
+        }
+    }
+}
+
+static GtkLayoutChild *
+gtk_overlay_layout_create_layout_child (GtkLayoutManager *manager,
+                                        GtkWidget        *widget,
+                                        GtkWidget        *for_child)
+{
+  return g_object_new (GTK_TYPE_OVERLAY_LAYOUT_CHILD,
+                       "layout-manager", manager,
+                       "child-widget", for_child,
+                       NULL);
+}
+
+static void
+gtk_overlay_layout_class_init (GtkOverlayLayoutClass *klass)
+{
+  GtkLayoutManagerClass *layout_class = GTK_LAYOUT_MANAGER_CLASS (klass);
+
+  layout_class->measure = gtk_overlay_layout_measure;
+  layout_class->allocate = gtk_overlay_layout_allocate;
+  layout_class->create_layout_child = gtk_overlay_layout_create_layout_child;
+}
+
+static void
+gtk_overlay_layout_init (GtkOverlayLayout *self)
+{
+}
+
+GtkLayoutManager *
+gtk_overlay_layout_new (void)
+{
+  return g_object_new (GTK_TYPE_OVERLAY_LAYOUT, NULL);
+}
diff --git a/gtk/gtkoverlaylayoutprivate.h b/gtk/gtkoverlaylayoutprivate.h
new file mode 100644
index 0000000000..1e5406015a
--- /dev/null
+++ b/gtk/gtkoverlaylayoutprivate.h
@@ -0,0 +1,57 @@
+/* gtkoverlaylayout.h: Overlay layout manager
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * Copyright 2019 Red Hat, Inc.
+ *
+ * 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 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_OVERLAY_LAYOUT (gtk_overlay_layout_get_type ())
+#define GTK_TYPE_OVERLAY_LAYOUT_CHILD (gtk_overlay_layout_child_get_type ())
+
+/* GtkOverlayLayout */
+
+GDK_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (GtkOverlayLayout, gtk_overlay_layout, GTK, OVERLAY_LAYOUT, GtkLayoutManager)
+
+GDK_AVAILABLE_IN_ALL
+GtkLayoutManager *      gtk_overlay_layout_new    (void);
+
+/* GtkOverlayLayoutChild */
+
+GDK_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (GtkOverlayLayoutChild, gtk_overlay_layout_child, GTK, OVERLAY_LAYOUT_CHILD, 
GtkLayoutChild)
+
+GDK_AVAILABLE_IN_ALL
+void            gtk_overlay_layout_child_set_measure (GtkOverlayLayoutChild *child,
+                                                      gboolean               measure);
+
+GDK_AVAILABLE_IN_ALL
+gboolean        gtk_overlay_layout_child_get_measure (GtkOverlayLayoutChild *child);
+
+GDK_AVAILABLE_IN_ALL
+void            gtk_overlay_layout_child_set_clip_overlay (GtkOverlayLayoutChild *child,
+                                                           gboolean               clip_overlay);
+
+GDK_AVAILABLE_IN_ALL
+gboolean        gtk_overlay_layout_child_get_clip_overlay (GtkOverlayLayoutChild *child);
+
+G_END_DECLS
diff --git a/gtk/meson.build b/gtk/meson.build
index dbdbbb213a..0acc012217 100644
--- a/gtk/meson.build
+++ b/gtk/meson.build
@@ -289,6 +289,7 @@ gtk_public_sources = files([
   'gtknotebook.c',
   'gtkorientable.c',
   'gtkoverlay.c',
+  'gtkoverlaylayout.c',
   'gtkpadcontroller.c',
   'gtkpagesetup.c',
   'gtkpaned.c',


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