[gtk: 42/88] gtk: Allocate everything from GtkNativeClass::layout




commit 8a599b2582422f0b6921d85a33466f560d3073a6
Author: Jonas Ã…dahl <jadahl gmail com>
Date:   Wed Dec 2 11:59:23 2020 +0100

    gtk: Allocate everything from GtkNativeClass::layout
    
    This changes allocation of the widget trees to happen as a side effect
    to the GdkSurface::layout signal, which first passes the GtkNative
    instance where it is then forwarded to the implementations of the
    GtkNative interface.
    
    The implementations of GtkNative are the ones doing the actual
    gtk_widget_allocate(), and they do so in their GtkNativeClass::layout
    function.

 gtk/gtkdragicon.c      |  24 +--
 gtk/gtknative.c        | 121 ++++++++++++++
 gtk/gtknative.h        |   5 +
 gtk/gtknativeprivate.h |   6 +
 gtk/gtkpopover.c       |  38 +++--
 gtk/gtkroot.c          | 144 +----------------
 gtk/gtktexthandle.c    |  23 ++-
 gtk/gtktooltipwindow.c |  24 ++-
 gtk/gtkwidget.c        |   5 +-
 gtk/gtkwindow.c        | 417 +++++++++++--------------------------------------
 10 files changed, 302 insertions(+), 505 deletions(-)
---
diff --git a/gtk/gtkdragicon.c b/gtk/gtkdragicon.c
index dfcb029cc9..0a39c39ccf 100644
--- a/gtk/gtkdragicon.c
+++ b/gtk/gtkdragicon.c
@@ -158,6 +158,14 @@ gtk_drag_icon_native_check_resize (GtkNative *native)
     gtk_drag_icon_move_resize (icon);
 }
 
+static void
+gtk_drag_icon_native_layout (GtkNative *native,
+                             int        width,
+                             int        height)
+{
+  gtk_widget_allocate (GTK_WIDGET (native), width, height, -1, NULL);
+}
+
 static void
 gtk_drag_icon_native_init (GtkNativeInterface *iface)
 {
@@ -165,15 +173,7 @@ gtk_drag_icon_native_init (GtkNativeInterface *iface)
   iface->get_renderer = gtk_drag_icon_native_get_renderer;
   iface->get_surface_transform = gtk_drag_icon_native_get_surface_transform;
   iface->check_resize = gtk_drag_icon_native_check_resize;
-}
-
-static void
-surface_layout (GdkSurface *surface,
-                int         width,
-                int         height,
-                GtkWidget  *widget)
-{
-  gtk_widget_allocate (widget, width, height, -1, NULL);
+  iface->layout = gtk_drag_icon_native_layout;
 }
 
 static gboolean
@@ -194,12 +194,13 @@ gtk_drag_icon_realize (GtkWidget *widget)
 
   gdk_surface_set_widget (icon->surface, widget);
 
-  g_signal_connect (icon->surface, "layout", G_CALLBACK (surface_layout), widget);
   g_signal_connect (icon->surface, "render", G_CALLBACK (surface_render), widget);
 
   GTK_WIDGET_CLASS (gtk_drag_icon_parent_class)->realize (widget);
 
   icon->renderer = gsk_renderer_new_for_surface (icon->surface);
+
+  gtk_native_realize (GTK_NATIVE (icon));
 }
 
 static void
@@ -207,6 +208,8 @@ gtk_drag_icon_unrealize (GtkWidget *widget)
 {
   GtkDragIcon *icon = GTK_DRAG_ICON (widget);
 
+  gtk_native_unrealize (GTK_NATIVE (icon));
+
   GTK_WIDGET_CLASS (gtk_drag_icon_parent_class)->unrealize (widget);
 
   gsk_renderer_unrealize (icon->renderer);
@@ -214,7 +217,6 @@ gtk_drag_icon_unrealize (GtkWidget *widget)
 
   if (icon->surface)
     {
-      g_signal_handlers_disconnect_by_func (icon->surface, surface_layout, widget);
       g_signal_handlers_disconnect_by_func (icon->surface, surface_render, widget);
       gdk_surface_set_widget (icon->surface, NULL);
     }
diff --git a/gtk/gtknative.c b/gtk/gtknative.c
index 328d0c61fd..45ad317cbf 100644
--- a/gtk/gtknative.c
+++ b/gtk/gtknative.c
@@ -24,6 +24,15 @@
 #include "gdk/gdk-private.h"
 #include "gtkprivate.h"
 #include "gtkintl.h"
+#include "gtkcssnodeprivate.h"
+
+typedef struct _GtkNativePrivate
+{
+  gulong update_handler_id;
+  gulong layout_handler_id;
+} GtkNativePrivate;
+
+static GQuark quark_gtk_native_private;
 
 /**
  * SECTION:gtknative
@@ -59,12 +68,108 @@ gtk_native_default_check_resize (GtkNative *self)
 {
 }
 
+static void
+gtk_native_default_layout (GtkNative *self,
+                           int        width,
+                           int        height)
+{
+}
+
 static void
 gtk_native_default_init (GtkNativeInterface *iface)
 {
   iface->get_renderer = gtk_native_default_get_renderer;
   iface->get_surface_transform = gtk_native_default_get_surface_transform;
   iface->check_resize = gtk_native_default_check_resize;
+  iface->layout = gtk_native_default_layout;
+
+  quark_gtk_native_private = g_quark_from_static_string ("gtk-native-private");
+}
+
+static void
+frame_clock_update_cb (GdkFrameClock *clock,
+                       GtkNative     *native)
+{
+  if (GTK_IS_ROOT (native))
+    gtk_css_node_validate (gtk_widget_get_css_node (GTK_WIDGET (native)));
+}
+
+static void
+gtk_native_layout (GtkNative *self,
+                   int        width,
+                   int        height)
+{
+  return GTK_NATIVE_GET_IFACE (self)->layout (self, width, height);
+}
+
+static void
+surface_layout_cb (GdkSurface *surface,
+                   int         width,
+                   int         height,
+                   GtkNative  *native)
+{
+  gtk_native_layout (native, width, height);
+
+  if (gtk_widget_needs_allocate (GTK_WIDGET (native)))
+    gtk_native_queue_relayout (native);
+}
+
+static void
+verify_priv_unrealized (gpointer user_data)
+{
+  GtkNativePrivate *priv = user_data;
+
+  g_warn_if_fail (priv->update_handler_id == 0);
+  g_warn_if_fail (priv->layout_handler_id == 0);
+
+  g_free (priv);
+}
+
+void
+gtk_native_realize (GtkNative *self)
+{
+  GdkSurface *surface;
+  GdkFrameClock *clock;
+  GtkNativePrivate *priv;
+
+  g_return_if_fail (g_object_get_qdata (G_OBJECT (self),
+                                        quark_gtk_native_private) == NULL);
+
+  surface = gtk_native_get_surface (self);
+  clock = gdk_surface_get_frame_clock (surface);
+  g_return_if_fail (clock != NULL);
+
+  priv = g_new0 (GtkNativePrivate, 1);
+  priv->update_handler_id = g_signal_connect_after (clock, "update",
+                                              G_CALLBACK (frame_clock_update_cb),
+                                              self);
+  priv->layout_handler_id = g_signal_connect (surface, "layout",
+                                              G_CALLBACK (surface_layout_cb),
+                                              self);
+  g_object_set_qdata_full (G_OBJECT (self),
+                           quark_gtk_native_private,
+                           priv,
+                           verify_priv_unrealized);
+}
+
+void
+gtk_native_unrealize (GtkNative *self)
+{
+  GtkNativePrivate *priv;
+  GdkSurface *surface;
+  GdkFrameClock *clock;
+
+  priv = g_object_get_qdata (G_OBJECT (self), quark_gtk_native_private);
+  g_return_if_fail (priv != NULL);
+
+  surface = gtk_native_get_surface (self);
+  clock = gdk_surface_get_frame_clock (surface);
+  g_return_if_fail (clock != NULL);
+
+  g_clear_signal_handler (&priv->update_handler_id, clock);
+  g_clear_signal_handler (&priv->layout_handler_id, surface);
+
+  g_object_set_qdata (G_OBJECT (self), quark_gtk_native_private, NULL);
 }
 
 /**
@@ -157,3 +262,19 @@ gtk_native_get_for_surface (GdkSurface *surface)
 
   return NULL;
 }
+
+void
+gtk_native_queue_relayout (GtkNative *self)
+{
+  GtkWidget *widget = GTK_WIDGET (self);
+  GdkSurface *surface;
+  GdkFrameClock *clock;
+
+  surface = gtk_widget_get_surface (widget);
+  clock = gtk_widget_get_frame_clock (widget);
+  if (clock == NULL)
+    return;
+
+  gdk_frame_clock_request_phase (clock, GDK_FRAME_CLOCK_PHASE_UPDATE);
+  gdk_surface_request_layout (surface);
+}
diff --git a/gtk/gtknative.h b/gtk/gtknative.h
index c9372a60e9..ce2ffcb068 100644
--- a/gtk/gtknative.h
+++ b/gtk/gtknative.h
@@ -34,6 +34,11 @@ G_BEGIN_DECLS
 GDK_AVAILABLE_IN_ALL
 G_DECLARE_INTERFACE (GtkNative, gtk_native, GTK, NATIVE, GtkWidget)
 
+GDK_AVAILABLE_IN_ALL
+void        gtk_native_realize         (GtkNative *self);
+
+GDK_AVAILABLE_IN_ALL
+void        gtk_native_unrealize       (GtkNative *self);
 
 GDK_AVAILABLE_IN_ALL
 GtkNative * gtk_native_get_for_surface (GdkSurface *surface);
diff --git a/gtk/gtknativeprivate.h b/gtk/gtknativeprivate.h
index 6b603361ff..1016631661 100644
--- a/gtk/gtknativeprivate.h
+++ b/gtk/gtknativeprivate.h
@@ -24,8 +24,14 @@ struct _GtkNativeInterface
                                            double       *y);
 
   void          (* check_resize)          (GtkNative    *self);
+
+  void          (* layout)                (GtkNative    *self,
+                                           int           width,
+                                           int           height);
 };
 
+void    gtk_native_queue_relayout         (GtkNative    *native);
+
 G_END_DECLS
 
 #endif /* __GTK_NATIVE_PRIVATE_H__ */
diff --git a/gtk/gtkpopover.c b/gtk/gtkpopover.c
index 252797599c..bb2a171864 100644
--- a/gtk/gtkpopover.c
+++ b/gtk/gtkpopover.c
@@ -589,6 +589,23 @@ gtk_popover_native_check_resize (GtkNative *native)
     present_popup (popover);
 }
 
+static void
+gtk_popover_native_layout (GtkNative *native,
+                           int        width,
+                           int        height)
+{
+  GtkPopover *popover = GTK_POPOVER (native);
+  GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover);
+  GtkWidget *widget = GTK_WIDGET (popover);
+
+  update_popover_layout (popover, gdk_popup_layout_ref (priv->layout));
+
+  if (gtk_widget_needs_allocate (widget))
+    gtk_widget_allocate (widget, width, height, -1, NULL);
+  else
+    gtk_widget_ensure_allocate (widget);
+}
+
 static gboolean
 gtk_popover_has_mnemonic_modifier_pressed (GtkPopover *popover)
 {
@@ -766,20 +783,6 @@ popup_layout_changed (GdkSurface *surface,
   update_popover_layout (popover, gdk_popup_layout_ref (priv->layout));
 }
 
-static void
-surface_layout (GdkSurface *surface,
-                int         width,
-                int         height,
-                GtkWidget  *widget)
-{
-  GtkPopover *popover = GTK_POPOVER (widget);
-  GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover);
-
-  update_popover_layout (popover, gdk_popup_layout_ref (priv->layout));
-  if (_gtk_widget_get_alloc_needed (widget))
-    gtk_widget_allocate (widget, width, height, -1, NULL);
-}
-
 static void
 gtk_popover_activate_default (GtkPopover *popover)
 {
@@ -900,11 +903,12 @@ gtk_popover_realize (GtkWidget *widget)
   g_signal_connect (priv->surface, "render", G_CALLBACK (surface_render), widget);
   g_signal_connect (priv->surface, "event", G_CALLBACK (surface_event), widget);
   g_signal_connect (priv->surface, "popup-layout-changed", G_CALLBACK (popup_layout_changed), widget);
-  g_signal_connect (priv->surface, "layout", G_CALLBACK (surface_layout), widget);
 
   GTK_WIDGET_CLASS (gtk_popover_parent_class)->realize (widget);
 
   priv->renderer = gsk_renderer_new_for_surface (priv->surface);
+
+  gtk_native_realize (GTK_NATIVE (popover));
 }
 
 static void
@@ -913,6 +917,8 @@ gtk_popover_unrealize (GtkWidget *widget)
   GtkPopover *popover = GTK_POPOVER (widget);
   GtkPopoverPrivate *priv = gtk_popover_get_instance_private (popover);
 
+  gtk_native_unrealize (GTK_NATIVE (popover));
+
   GTK_WIDGET_CLASS (gtk_popover_parent_class)->unrealize (widget);
 
   gsk_renderer_unrealize (priv->renderer);
@@ -923,7 +929,6 @@ gtk_popover_unrealize (GtkWidget *widget)
   g_signal_handlers_disconnect_by_func (priv->surface, surface_render, widget);
   g_signal_handlers_disconnect_by_func (priv->surface, surface_event, widget);
   g_signal_handlers_disconnect_by_func (priv->surface, popup_layout_changed, widget);
-  g_signal_handlers_disconnect_by_func (priv->surface, surface_layout, widget);
   gdk_surface_set_widget (priv->surface, NULL);
   gdk_surface_destroy (priv->surface);
   g_clear_object (&priv->surface);
@@ -1859,6 +1864,7 @@ gtk_popover_native_interface_init (GtkNativeInterface *iface)
   iface->get_renderer = gtk_popover_native_get_renderer;
   iface->get_surface_transform = gtk_popover_native_get_surface_transform;
   iface->check_resize = gtk_popover_native_check_resize;
+  iface->layout = gtk_popover_native_layout;
 }
 
 static GtkBuildableIface *parent_buildable_iface;
diff --git a/gtk/gtkroot.c b/gtk/gtkroot.c
index 6bdf1b74a1..16e1bef28c 100644
--- a/gtk/gtkroot.c
+++ b/gtk/gtkroot.c
@@ -21,6 +21,7 @@
 
 #include "gtkrootprivate.h"
 #include "gtknative.h"
+#include "gtknativeprivate.h"
 #include "gtkcssnodeprivate.h"
 #include "gtkwidgetprivate.h"
 #include "gdk/gdk-private.h"
@@ -43,10 +44,6 @@
  * The obvious example of a #GtkRoot is #GtkWindow.
  */
 
-static GQuark quark_restyle_pending;
-static GQuark quark_layout_handler;
-static GQuark quark_after_update_handler;
-
 G_DEFINE_INTERFACE_WITH_CODE (GtkRoot, gtk_root, GTK_TYPE_WIDGET,
                               g_type_interface_add_prerequisite (g_define_type_id, GTK_TYPE_NATIVE))
 
@@ -82,10 +79,6 @@ gtk_root_default_init (GtkRootInterface *iface)
   iface->get_constraint_solver = gtk_root_default_get_constraint_solver;
   iface->get_focus = gtk_root_default_get_focus;
   iface->set_focus = gtk_root_default_set_focus;
-
-  quark_restyle_pending = g_quark_from_static_string ("gtk-root-restyle-pending");
-  quark_layout_handler = g_quark_from_static_string ("gtk-root-layout-handler");
-  quark_after_update_handler = g_quark_from_static_string ("gtk-root-after-update-handler");
 }
 
 /**
@@ -163,152 +156,19 @@ gtk_root_get_focus (GtkRoot *self)
   return GTK_ROOT_GET_IFACE (self)->get_focus (self);
 }
 
-static gboolean
-gtk_root_needs_layout (GtkRoot *self)
-{
-  if (g_object_get_qdata (G_OBJECT (self), quark_restyle_pending))
-    return TRUE;
-
-  return gtk_widget_needs_allocate (GTK_WIDGET (self));
-}
-
-static void
-gtk_root_after_update_cb (GdkFrameClock *clock,
-                          GtkRoot       *self)
-{
-  GtkWidget *widget = GTK_WIDGET (self);
-
-  /* We validate the style contexts in a single loop before even trying
-   * to handle resizes instead of doing validations inline.
-   * This is mostly necessary for compatibility reasons with old code,
-   * because both css_changed and size_allocate functions often change
-   * styles and so could cause infinite loops in this function.
-   *
-   * It's important to note that even an invalid style context returns
-   * sane values. So the result of an invalid style context will never be
-   * a program crash, but only a wrong layout or rendering.
-   */
-  if (g_object_get_qdata (G_OBJECT (self), quark_restyle_pending))
-    {
-      gtk_css_node_validate (gtk_widget_get_css_node (widget));
-    }
-}
-
-static void
-gtk_root_layout_cb (GdkSurface *surface,
-                    int         width,
-                    int         height,
-                    GtkRoot    *self)
-{
-  GtkWidget *widget = GTK_WIDGET (self);
-
-  g_object_set_qdata (G_OBJECT (self), quark_restyle_pending, NULL);
-
-  /* we may be invoked with a container_resize_queue of NULL, because
-   * queue_resize could have been adding an extra idle function while
-   * the queue still got processed. we better just ignore such case
-   * than trying to explicitly work around them with some extra flags,
-   * since it doesn't cause any actual harm.
-   */
-  if (gtk_widget_needs_allocate (widget))
-    {
-      gtk_native_check_resize (GTK_NATIVE (self));
-      if (GTK_IS_WINDOW (widget))
-        {
-          GdkSeat *seat;
-
-          seat = gdk_display_get_default_seat (gtk_widget_get_display (widget));
-          if (seat)
-            {
-              GdkDevice *device;
-              GtkWidget *focus;
-
-              device = gdk_seat_get_pointer (seat);
-              focus = gtk_window_lookup_pointer_focus_widget (GTK_WINDOW (widget), device, NULL);
-              if (focus)
-                gdk_surface_request_motion (gtk_native_get_surface (gtk_widget_get_native (focus)));
-            }
-        }
-    }
-
-  if (gtk_root_needs_layout (self))
-    {
-      gdk_frame_clock_request_phase (gdk_surface_get_frame_clock (surface),
-                                     GDK_FRAME_CLOCK_PHASE_UPDATE);
-      gdk_surface_request_layout (surface);
-    }
-}
-
 void
 gtk_root_start_layout (GtkRoot *self)
 {
-  GdkSurface *surface;
-  GdkFrameClock *clock;
-
-  if (!gtk_root_needs_layout (self))
-    return;
-
-  surface = gtk_widget_get_surface (GTK_WIDGET (self));
-  clock = gtk_widget_get_frame_clock (GTK_WIDGET (self));
-  if (clock == NULL)
-    return;
-
-  if (!g_object_get_qdata (G_OBJECT (self), quark_layout_handler))
-    {
-      guint layout_handler;
-      guint after_update_handler;
-
-      after_update_handler =
-        g_signal_connect_after (clock, "update",
-                                G_CALLBACK (gtk_root_after_update_cb), self);
-      g_object_set_qdata (G_OBJECT (self), quark_after_update_handler,
-                          GINT_TO_POINTER (after_update_handler));
-
-      layout_handler =
-        g_signal_connect (surface, "layout",
-                          G_CALLBACK (gtk_root_layout_cb), self);
-      g_object_set_qdata (G_OBJECT (self), quark_layout_handler,
-                          GINT_TO_POINTER (layout_handler));
-    }
-
-  gdk_frame_clock_request_phase (clock, GDK_FRAME_CLOCK_PHASE_UPDATE);
-  gdk_surface_request_layout (surface);
+  gtk_native_queue_relayout (GTK_NATIVE (self));
 }
 
 void
 gtk_root_stop_layout (GtkRoot *self)
 {
-  GdkSurface *surface;
-  GdkFrameClock *clock;
-  guint layout_handler;
-  guint after_update_handler;
-
-  layout_handler =
-    GPOINTER_TO_INT (g_object_get_qdata (G_OBJECT (self),
-                                         quark_layout_handler));
-  after_update_handler =
-    GPOINTER_TO_INT (g_object_get_qdata (G_OBJECT (self),
-                                         quark_after_update_handler));
-
-  if (layout_handler == 0)
-    return;
-
-  surface = gtk_widget_get_surface (GTK_WIDGET (self));
-  clock = gtk_widget_get_frame_clock (GTK_WIDGET (self));
-  g_signal_handler_disconnect (surface, layout_handler);
-  g_signal_handler_disconnect (clock, after_update_handler);
-  g_object_set_qdata (G_OBJECT (self), quark_layout_handler, NULL);
-  g_object_set_qdata (G_OBJECT (self), quark_after_update_handler, NULL);
 }
 
 void
 gtk_root_queue_restyle (GtkRoot *self)
 {
-  if (g_object_get_qdata (G_OBJECT (self), quark_restyle_pending))
-    return;
-
-  g_object_set_qdata (G_OBJECT (self), quark_restyle_pending, GINT_TO_POINTER (1));
-
   gtk_root_start_layout (self);
 }
-
diff --git a/gtk/gtktexthandle.c b/gtk/gtktexthandle.c
index 47cceafa0e..6d80068088 100644
--- a/gtk/gtktexthandle.c
+++ b/gtk/gtktexthandle.c
@@ -162,11 +162,6 @@ gtk_text_handle_present_surface (GtkTextHandle *handle)
                      MAX (req.height, 1),
                      layout);
   gdk_popup_layout_unref (layout);
-
-  gtk_widget_allocate (widget,
-                       gdk_surface_get_width (handle->surface),
-                       gdk_surface_get_height (handle->surface),
-                       -1, NULL);
 }
 
 static void
@@ -181,6 +176,19 @@ gtk_text_handle_native_check_resize (GtkNative *native)
     gtk_text_handle_present_surface (handle);
 }
 
+static void
+gtk_text_handle_native_layout (GtkNative *native,
+                               int        width,
+                               int        height)
+{
+  GtkWidget *widget = GTK_WIDGET (native);
+
+  if (_gtk_widget_get_alloc_needed (widget))
+    gtk_widget_allocate (widget, width, height, -1, NULL);
+  else
+    gtk_widget_ensure_allocate (widget);
+}
+
 static void
 gtk_text_handle_native_interface_init (GtkNativeInterface *iface)
 {
@@ -188,6 +196,7 @@ gtk_text_handle_native_interface_init (GtkNativeInterface *iface)
   iface->get_renderer = gtk_text_handle_native_get_renderer;
   iface->get_surface_transform = gtk_text_handle_native_get_surface_transform;
   iface->check_resize = gtk_text_handle_native_check_resize;
+  iface->layout = gtk_text_handle_native_layout;
 }
 
 static gboolean
@@ -240,6 +249,8 @@ gtk_text_handle_realize (GtkWidget *widget)
   GTK_WIDGET_CLASS (gtk_text_handle_parent_class)->realize (widget);
 
   handle->renderer = gsk_renderer_new_for_surface (handle->surface);
+
+  gtk_native_realize (GTK_NATIVE (handle));
 }
 
 static void
@@ -247,6 +258,8 @@ gtk_text_handle_unrealize (GtkWidget *widget)
 {
   GtkTextHandle *handle = GTK_TEXT_HANDLE (widget);
 
+  gtk_native_unrealize (GTK_NATIVE (handle));
+
   GTK_WIDGET_CLASS (gtk_text_handle_parent_class)->unrealize (widget);
 
   gsk_renderer_unrealize (handle->renderer);
diff --git a/gtk/gtktooltipwindow.c b/gtk/gtktooltipwindow.c
index 95b4333a9f..c4c5b64ecd 100644
--- a/gtk/gtktooltipwindow.c
+++ b/gtk/gtktooltipwindow.c
@@ -152,14 +152,23 @@ gtk_tooltip_window_native_check_resize (GtkNative *native)
   else if (gtk_widget_get_visible (widget))
     {
       gtk_tooltip_window_relayout (window);
-      if (window->surface)
-        gtk_widget_allocate (GTK_WIDGET (window),
-                             gdk_surface_get_width (window->surface),
-                             gdk_surface_get_height (window->surface),
-                             -1, NULL);
     }
 }
 
+static void
+gtk_tooltip_window_native_layout (GtkNative *native,
+                                  int        width,
+                                  int        height)
+{
+  GtkWidget *widget = GTK_WIDGET (native);
+
+  if (gtk_widget_needs_allocate (widget))
+    gtk_widget_allocate (widget, width, height, -1, NULL);
+  else
+    gtk_widget_ensure_allocate (widget);
+
+}
+
 static void
 gtk_tooltip_window_native_init (GtkNativeInterface *iface)
 {
@@ -167,6 +176,7 @@ gtk_tooltip_window_native_init (GtkNativeInterface *iface)
   iface->get_renderer = gtk_tooltip_window_native_get_renderer;
   iface->get_surface_transform = gtk_tooltip_window_native_get_surface_transform;
   iface->check_resize = gtk_tooltip_window_native_check_resize;
+  iface->layout = gtk_tooltip_window_native_layout;
 }
 
 static void
@@ -223,6 +233,8 @@ gtk_tooltip_window_realize (GtkWidget *widget)
   GTK_WIDGET_CLASS (gtk_tooltip_window_parent_class)->realize (widget);
 
   window->renderer = gsk_renderer_new_for_surface (window->surface);
+
+  gtk_native_realize (GTK_NATIVE (window));
 }
 
 static void
@@ -230,6 +242,8 @@ gtk_tooltip_window_unrealize (GtkWidget *widget)
 {
   GtkTooltipWindow *window = GTK_TOOLTIP_WINDOW (widget);
 
+  gtk_native_unrealize (GTK_NATIVE (window));
+
   GTK_WIDGET_CLASS (gtk_tooltip_window_parent_class)->unrealize (widget);
 
   gsk_renderer_unrealize (window->renderer);
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index 6d1e3c865d..56a9afb295 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -56,6 +56,7 @@
 #include "gtkrenderbackgroundprivate.h"
 #include "gtkrenderborderprivate.h"
 #include "gtkrootprivate.h"
+#include "gtknativeprivate.h"
 #include "gtkscrollable.h"
 #include "gtksettingsprivate.h"
 #include "gtkshortcut.h"
@@ -10223,9 +10224,11 @@ gtk_widget_set_alloc_needed (GtkWidget *widget)
       if (!priv->visible)
         break;
 
+      if (GTK_IS_NATIVE (widget))
+        gtk_native_queue_relayout (GTK_NATIVE (widget));
+
       if (!priv->parent && GTK_IS_ROOT (widget))
         {
-          gtk_root_start_layout (GTK_ROOT (widget));
           break;
         }
 
diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c
index 4660dd40ce..90864cea57 100644
--- a/gtk/gtkwindow.c
+++ b/gtk/gtkwindow.c
@@ -202,7 +202,6 @@ typedef struct
   guint    need_default_size         : 1;
 
   guint    builder_visible           : 1;
-  guint    configure_notify_received : 1;
   guint    decorated                 : 1;
   guint    deletable                 : 1;
   guint    destroy_with_parent       : 1;
@@ -236,6 +235,9 @@ typedef struct
   GtkConstraintSolver *constraint_solver;
   GdkToplevelLayout *layout;
 
+  int surface_width;
+  int surface_height;
+
   GdkCursor *resize_cursor;
 } GtkWindowPrivate;
 
@@ -385,11 +387,6 @@ static void gtk_window_transient_parent_unrealized (GtkWidget  *parent,
 static GtkWindowGeometryInfo* gtk_window_get_geometry_info         (GtkWindow    *window,
                                                                     gboolean      create);
 
-static void     gtk_window_move_resize               (GtkWindow    *window);
-static gboolean gtk_window_compare_hints             (GdkGeometry  *geometry_a,
-                                                      guint         flags_a,
-                                                      GdkGeometry  *geometry_b,
-                                                      guint         flags_b);
 static void     gtk_window_update_fixed_size         (GtkWindow    *window,
                                                       GdkGeometry  *new_geometry,
                                                       int           new_width,
@@ -487,6 +484,14 @@ static void             gtk_window_native_interface_init  (GtkNativeInterface  *
 static void ensure_state_flag_backdrop (GtkWidget *widget);
 static void unset_titlebar (GtkWindow *window);
 
+#define INCLUDE_CSD_SIZE 1
+#define EXCLUDE_CSD_SIZE -1
+
+static void
+gtk_window_update_csd_size (GtkWindow *window,
+                            int       *width,
+                            int       *height,
+                            int        apply);
 
 G_DEFINE_TYPE_WITH_CODE (GtkWindow, gtk_window, GTK_TYPE_WIDGET,
                          G_ADD_PRIVATE (GtkWindow)
@@ -1460,7 +1465,6 @@ gtk_window_init (GtkWindow *window)
   priv->focus_widget = NULL;
   priv->default_widget = NULL;
   priv->resizable = TRUE;
-  priv->configure_notify_received = FALSE;
   priv->need_default_size = TRUE;
   priv->modal = FALSE;
   priv->decorated = TRUE;
@@ -1877,11 +1881,59 @@ gtk_window_native_check_resize (GtkNative *native)
   if (!_gtk_widget_get_alloc_needed (widget))
     gtk_widget_ensure_allocate (widget);
   else if (gtk_widget_get_visible (widget))
-    gtk_window_move_resize (GTK_WINDOW (native));
+    gtk_window_present_toplevel (GTK_WINDOW (native));
 
   gdk_profiler_end_mark (before, "size allocation", "");
 }
 
+static void
+gtk_window_native_layout (GtkNative *native,
+                          int        width,
+                          int        height)
+{
+  GtkWindow *window = GTK_WINDOW (native);
+  GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
+  GtkWidget *widget = GTK_WIDGET (native);
+  GdkSeat *seat;
+
+  if (priv->surface_width != width || priv->surface_height != height)
+    {
+      surface_size_changed (widget, width, height);
+      priv->surface_width = width;
+      priv->surface_height = height;
+    }
+
+  seat = gdk_display_get_default_seat (gtk_widget_get_display (widget));
+  if (seat)
+    {
+      GdkDevice *device;
+      GtkWidget *focus;
+
+      device = gdk_seat_get_pointer (seat);
+      focus = gtk_window_lookup_pointer_focus_widget (GTK_WINDOW (widget),
+                                                      device, NULL);
+      if (focus)
+        {
+          GdkSurface *focus_surface =
+            gtk_native_get_surface (gtk_widget_get_native (focus));
+
+          gdk_surface_request_motion (focus_surface);
+        }
+    }
+
+  if (gtk_widget_needs_allocate (widget))
+    {
+      gtk_window_update_csd_size (window,
+                                  &width, &height,
+                                  EXCLUDE_CSD_SIZE);
+      gtk_widget_allocate (widget, width, height, -1, NULL);
+    }
+  else
+    {
+      gtk_widget_ensure_allocate (widget);
+    }
+}
+
 static void
 gtk_window_root_interface_init (GtkRootInterface *iface)
 {
@@ -1898,6 +1950,7 @@ gtk_window_native_interface_init (GtkNativeInterface *iface)
   iface->get_renderer = gtk_window_native_get_renderer;
   iface->get_surface_transform = gtk_window_native_get_surface_transform;
   iface->check_resize = gtk_window_native_check_resize;
+  iface->layout = gtk_window_native_layout;
 }
 
 /**
@@ -3286,9 +3339,6 @@ gtk_window_get_default_icon_name (void)
   return default_icon_name;
 }
 
-#define INCLUDE_CSD_SIZE 1
-#define EXCLUDE_CSD_SIZE -1
-
 static void
 gtk_window_update_csd_size (GtkWindow *window,
                             int       *width,
@@ -3821,8 +3871,6 @@ gtk_window_unmap (GtkWidget *widget)
   GTK_WIDGET_CLASS (gtk_window_parent_class)->unmap (widget);
   gdk_surface_hide (priv->surface);
 
-  priv->configure_notify_received = FALSE;
-
   state = gdk_toplevel_get_state (GDK_TOPLEVEL (priv->surface));
   priv->minimize_initially = (state & GDK_TOPLEVEL_STATE_MINIMIZED) != 0;
   priv->maximize_initially = (state & GDK_TOPLEVEL_STATE_MAXIMIZED) != 0;
@@ -4205,6 +4253,10 @@ toplevel_compute_size (GdkToplevel     *toplevel,
       width = MAX (nat_width, remembered_width);
       height = MAX (nat_height, remembered_height);
 
+      gtk_window_update_csd_size (window,
+                                  &width, &height,
+                                  INCLUDE_CSD_SIZE);
+
       /* Override with default size */
       if (info)
         {
@@ -4222,6 +4274,19 @@ toplevel_compute_size (GdkToplevel     *toplevel,
           if (info->default_height > 0)
             height = default_height_csd;
         }
+
+      info = gtk_window_get_geometry_info (window, TRUE);
+      info->last.configure_request.width = width;
+      info->last.configure_request.height = height;
+    }
+  else if (!priv->resizable)
+    {
+      width = nat_width;
+      height = nat_height;
+
+      gtk_window_update_csd_size (window,
+                                  &width, &height,
+                                  INCLUDE_CSD_SIZE);
     }
   else
     {
@@ -4235,6 +4300,7 @@ toplevel_compute_size (GdkToplevel     *toplevel,
       gtk_window_get_remembered_size (window, &width, &height);
     }
 
+  get_shadow_width (window, &shadow);
 
   /* Don't ever request zero width or height, it's not supported by
      gdk. The size allocation code will round it to 1 anyway but if
@@ -4243,21 +4309,25 @@ toplevel_compute_size (GdkToplevel     *toplevel,
   width = MAX (width, 1);
   height = MAX (height, 1);
 
-  gdk_toplevel_size_set_size (size, width, height);
 
-  get_shadow_width (window, &shadow);
-
-  min_width = MIN (min_width + shadow.left + shadow.right, width);
-  min_height = MIN (min_height + shadow.top + shadow.bottom, height);
+  gtk_window_update_csd_size (window,
+                              &min_width, &min_height,
+                              INCLUDE_CSD_SIZE);
 
   gdk_toplevel_size_set_min_size (size, min_width, min_height);
 
+  gdk_toplevel_size_set_size (size,
+                              MAX (min_width, width),
+                              MAX (min_height, height));
+
   if (priv->use_client_shadow)
     {
       gdk_toplevel_size_set_margin (size,
                                     shadow.left, shadow.right,
                                     shadow.top, shadow.bottom);
     }
+
+  gtk_widget_ensure_resize (widget);
 }
 
 static void
@@ -4357,6 +4427,8 @@ gtk_window_realize (GtkWidget *widget)
   gtk_window_realize_icon (window);
 
   check_scale_changed (window);
+
+  gtk_native_realize (GTK_NATIVE (window));
 }
 
 static void
@@ -4367,6 +4439,8 @@ gtk_window_unrealize (GtkWidget *widget)
   GtkWindowGeometryInfo *info;
   GdkSurface *surface;
 
+  gtk_native_unrealize (GTK_NATIVE (window));
+
   /* On unrealize, we reset the size of the window such
    * that we will re-apply the default sizing stuff
    * next time we show the window.
@@ -4654,16 +4728,6 @@ surface_size_changed (GtkWidget *widget,
       info->last.configure_request.height = height;
     }
 
-  /*
-   * If we do need to resize, we do that by:
-   *   - setting configure_notify_received to TRUE
-   *     for use in gtk_window_move_resize()
-   *   - queueing a resize, leading to invocation of
-   *     gtk_window_move_resize() in an idle handler
-   *
-   */
-  priv->configure_notify_received = TRUE;
-
   gtk_widget_queue_allocate (widget);
 }
 
@@ -5258,303 +5322,6 @@ gtk_window_compute_configure_request (GtkWindow    *window,
     *flags = new_flags;
 }
 
-static void
-gtk_window_move_resize (GtkWindow *window)
-{
-  /* Overview:
-   *
-   * First we determine whether any information has changed that would
-   * cause us to revise our last configure request.  If we would send
-   * a different configure request from last time, then
-   * configure_request_size_changed = TRUE or
-   * configure_request_pos_changed = TRUE. configure_request_size_changed
-   * may be true due to new hints, a gtk_window_resize(), or whatever.
-   * configure_request_pos_changed may be true due to gtk_window_set_position()
-   * or gtk_window_move().
-   *
-   * If the configure request has changed, we send off a new one.  To
-   * ensure GTK invariants are maintained (resize queue does what it
-   * should), we go ahead and size_allocate the requested size in this
-   * function.
-   *
-   * If the configure request has not changed, we don't ever resend
-   * it, because it could mean fighting the user or window manager.
-   *
-   *   To prepare the configure request, we come up with a base size/pos:
-   *      - the one from gtk_window_move()/gtk_window_resize()
-   *      - else default_width, default_height if we haven't ever
-   *        been mapped
-   *      - else the size request if we haven't ever been mapped,
-   *        as a substitute default size
-   *      - else the current size of the window, as received from
-   *        configure notifies (i.e. the current allocation)
-   */
-  GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
-  GtkWidget *widget;
-  GtkWindowGeometryInfo *info;
-  GdkGeometry new_geometry;
-  guint new_flags;
-  GdkRectangle new_request;
-  gboolean configure_request_size_changed;
-  gboolean configure_request_pos_changed;
-  gboolean hints_changed; /* do we need to send these again */
-  GtkWindowLastGeometryInfo saved_last_info;
-  int current_width, current_height;
-
-  widget = GTK_WIDGET (window);
-
-  info = gtk_window_get_geometry_info (window, TRUE);
-
-  configure_request_size_changed = FALSE;
-  configure_request_pos_changed = FALSE;
-  hints_changed = FALSE;
-
-  gtk_window_compute_configure_request (window, &new_request,
-                                        &new_geometry, &new_flags);
-
-  if (!(new_flags & GDK_HINT_MIN_SIZE))
-    new_geometry.min_width = new_geometry.min_height = 1;
-
-  g_clear_pointer (&priv->layout, gdk_toplevel_layout_unref);
-  priv->layout = gtk_window_compute_layout (window);
-
-  /* This check implies the invariant that we never set info->last
-   * without setting the hints and sending off a configure request.
-   *
-   * If we change info->last without sending the request, we may
-   * miss a request.
-   */
-  if (info->last.configure_request.x != new_request.x ||
-      info->last.configure_request.y != new_request.y)
-    configure_request_pos_changed = TRUE;
-
-  if ((info->last.configure_request.width != new_request.width ||
-       info->last.configure_request.height != new_request.height))
-    configure_request_size_changed = TRUE;
-
-  if (!gtk_window_compare_hints (&info->last.geometry, info->last.flags,
-                                 &new_geometry, new_flags))
-    hints_changed = TRUE;
-
-#if 0
-    {
-      GtkAllocation alloc;
-
-      gtk_widget_get_allocation (widget, &alloc);
-
-      g_message ("--- %s ---\n"
-                "last  : %d,%d\t%d x %d\n"
-                "this  : %d,%d\t%d x %d\n"
-                "alloc : %d,%d\t%d x %d\n"
-                "size_changed: %d pos_changed: %d hints_changed: %d\n"
-                "configure_notify_received: %d\n"
-                "position_constraints_changed: %d",
-                priv->title ? priv->title : "(no title)",
-                info->last.configure_request.x,
-                info->last.configure_request.y,
-                info->last.configure_request.width,
-                info->last.configure_request.height,
-                new_request.x,
-                new_request.y,
-                new_request.width,
-                new_request.height,
-                alloc.x,
-                alloc.y,
-                alloc.width,
-                alloc.height,
-                configure_request_size_changed,
-                configure_request_pos_changed,
-                hints_changed,
-                priv->configure_notify_received,
-                info->position_constraints_changed);
-    }
-#endif
-  
-  saved_last_info = info->last;
-  info->last.geometry = new_geometry;
-  info->last.flags = new_flags;
-  info->last.configure_request = new_request;
-  /* need to set PPosition so the WM will look at our position,
-   * but we don't want to count PPosition coming and going as a hints
-   * change for future iterations. So we saved info->last prior to
-   * this.
-   */
-
-  current_width = gdk_surface_get_width (priv->surface);
-  current_height = gdk_surface_get_height (priv->surface);
-
-  /* handle resizing/moving and widget tree allocation
-   */
-  if (priv->configure_notify_received)
-    {
-      GtkAllocation allocation;
-      GtkBorder shadow;
-      int min;
-
-      /* If we have received a configure event since
-       * the last time in this function, we need to
-       * accept our new size and size_allocate child widgets.
-       * (see gtk_window_configure_event() for more details).
-       *
-       * 1 or more configure notifies may have been received.
-       * Also, configure_notify_received will only be TRUE
-       * if all expected configure notifies have been received
-       * (one per configure request), as an optimization.
-       *
-       */
-      priv->configure_notify_received = FALSE;
-
-      get_shadow_width (window, &shadow);
-
-      allocation.x = shadow.left;
-      allocation.y = shadow.top;
-
-      gtk_widget_measure (widget, GTK_ORIENTATION_HORIZONTAL, -1,
-                          &min, NULL, NULL, NULL);
-      allocation.width = MAX (min, current_width - shadow.left - shadow.right);
-      gtk_widget_measure (widget, GTK_ORIENTATION_VERTICAL, allocation.width,
-                          &min, NULL, NULL, NULL);
-      allocation.height = MAX (min, current_height - shadow.top - shadow.bottom);
-
-      gtk_widget_size_allocate (widget, &allocation, -1);
-
-      /* If the configure request changed, it means that
-       * we either:
-       *   1) coincidentally changed hints or widget properties
-       *      impacting the configure request before getting
-       *      a configure notify, or
-       *   2) some broken widget is changing its size request
-       *      during size allocation, resulting in
-       *      a false appearance of changed configure request.
-       *
-       * For 1), we could just go ahead and ask for the
-       * new size right now, but doing that for 2)
-       * might well be fighting the user (and can even
-       * trigger a loop). Since we really don't want to
-       * do that, we requeue a resize in hopes that
-       * by the time it gets handled, the child has seen
-       * the light and is willing to go along with the
-       * new size. (this happens for the zvt widget, since
-       * the size_allocate() above will have stored the
-       * requisition corresponding to the new size in the
-       * zvt widget)
-       *
-       * This doesn't buy us anything for 1), but it shouldn't
-       * hurt us too badly, since it is what would have
-       * happened if we had gotten the configure event before
-       * the new size had been set.
-       */
-
-      if (configure_request_size_changed ||
-          configure_request_pos_changed)
-        {
-          /* Don't change the recorded last info after all, because we
-           * haven't actually updated to the new info yet - we decided
-           * to postpone our configure request until later.
-           */
-          info->last = saved_last_info;
-          g_clear_pointer (&priv->layout, gdk_toplevel_layout_unref);
-          gtk_widget_queue_resize (widget);
-        }
-
-      return; /* Bail out, we didn't really process the move/resize */
-    }
-  else if ((configure_request_size_changed || hints_changed || configure_request_pos_changed) &&
-           (current_width != new_request.width || current_height != new_request.height))
-    {
-      /* We are in one of the following situations:
-       * A. configure_request_size_changed
-       *    our requisition has changed and we need a different window size,
-       *    so we request it from the window manager.
-       * B. !configure_request_size_changed && hints_changed
-       *    the window manager rejects our size, but we have just changed the
-       *    window manager hints, so there's a chance our request will
-       *    be honoured this time, so we try again.
-       *
-       * However, if the new requisition is the same as the current allocation,
-       * we don't request it again, since we won't get a ConfigureNotify back from
-       * the window manager unless it decides to change our requisition. If
-       * we don't get the ConfigureNotify back, the resize queue will never be run.
-       */
-
-      /* for GTK_RESIZE_QUEUE toplevels, we are now awaiting a new
-       * configure event in response to our resizing request.
-       * the configure event will cause a new resize with
-       * ->configure_notify_received=TRUE.
-       * until then, we want to
-       * - discard expose events
-       * - coalesce resizes for our children
-       * - defer any window resizes until the configure event arrived
-       * to achieve this, we queue a resize for the window, but remove its
-       * resizing handler, so resizing will not be handled from the next
-       * idle handler but when the configure event arrives.
-       *
-       * FIXME: we should also dequeue the pending redraws here, since
-       * we handle those ourselves upon ->configure_notify_received==TRUE.
-       */
-
-      /* Now send the configure request */
-      if (configure_request_pos_changed)
-        g_warning ("configure request position changed. This should not happen. Ignoring the position");
-
-      if (_gtk_widget_get_mapped (widget))
-        gdk_toplevel_present (GDK_TOPLEVEL (priv->surface), priv->layout);
-    }
-  else
-    {
-      GtkAllocation allocation;
-      GtkBorder shadow;
-      int min_width, min_height;
-
-      get_shadow_width (window, &shadow);
-
-      allocation.x = shadow.left;
-      allocation.y = shadow.top;
-
-      /* Handle any position changes.
-       */
-      if (configure_request_pos_changed)
-        g_warning ("configure request position changed. This should not happen. Ignoring the position");
-
-      /* Our configure request didn't change size, but maybe some of
-       * our child widgets have. Run a size allocate with our current
-       * size to make sure that we re-layout our child widgets. */
-
-      gtk_widget_measure (widget, GTK_ORIENTATION_HORIZONTAL, current_height - shadow.top - shadow.bottom,
-                          &min_width, NULL, NULL, NULL);
-      allocation.width = MAX (current_width - shadow.left - shadow.right, min_width);
-
-      gtk_widget_measure (widget, GTK_ORIENTATION_VERTICAL, allocation.width,
-                          &min_height, NULL, NULL, NULL);
-      allocation.height = MAX (current_height - shadow.top - shadow.bottom, min_height);
-      gtk_widget_size_allocate (widget, &allocation, -1);
-    }
-}
-
-/* Compare two sets of Geometry hints for equality.
- */
-static gboolean
-gtk_window_compare_hints (GdkGeometry *geometry_a,
-                         guint        flags_a,
-                         GdkGeometry *geometry_b,
-                         guint        flags_b)
-{
-  if (flags_a != flags_b)
-    return FALSE;
-  
-  if ((flags_a & GDK_HINT_MIN_SIZE) &&
-      (geometry_a->min_width != geometry_b->min_width ||
-       geometry_a->min_height != geometry_b->min_height))
-    return FALSE;
-
-  if ((flags_a & GDK_HINT_MAX_SIZE) &&
-      (geometry_a->max_width != geometry_b->max_width ||
-       geometry_a->max_height != geometry_b->max_height))
-    return FALSE;
-
-  return TRUE;
-}
-
 /* For non-resizable windows, make sure the given width/height fits
  * in the geometry constrains and update the geometry hints to match
  * the given width/height if not.



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