]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkcontainer.c
stylecontext: Do invalidation on first resize container
[~andy/gtk] / gtk / gtkcontainer.c
index 962afdeb9d1ee7e05f8dcf343df79fea14db4668..ce16fe4df42e224471754b0886415b08b200e474 100644 (file)
@@ -42,6 +42,7 @@
 #include "gtkmain.h"
 #include "gtkmarshalers.h"
 #include "gtksizerequest.h"
+#include "gtksizerequestcacheprivate.h"
 #include "gtkwidgetprivate.h"
 #include "gtkwindow.h"
 #include "gtkassistant.h"
@@ -235,10 +236,15 @@ struct _GtkContainerPrivate
 {
   GtkWidget *focus_child;
 
+  guint resize_handler;
+  GdkFrameClock *resize_clock;
+
   guint border_width : 16;
 
   guint has_focus_chain    : 1;
   guint reallocate_redraws : 1;
+  guint resize_pending     : 1;
+  guint restyle_pending    : 1;
   guint resize_mode        : 2;
   guint request_mode       : 2;
 };
@@ -341,7 +347,6 @@ static const gchar           vadjustment_key[] = "gtk-vadjustment";
 static guint                 vadjustment_key_id = 0;
 static const gchar           hadjustment_key[] = "gtk-hadjustment";
 static guint                 hadjustment_key_id = 0;
-static GSList               *container_resize_queue = NULL;
 static guint                 container_signals[LAST_SIGNAL] = { 0 };
 static GtkWidgetClass       *parent_class = NULL;
 extern GParamSpecPool       *_gtk_widget_child_property_pool;
@@ -1351,9 +1356,12 @@ gtk_container_destroy (GtkWidget *widget)
   GtkContainer *container = GTK_CONTAINER (widget);
   GtkContainerPrivate *priv = container->priv;
 
-  if (_gtk_widget_get_resize_pending (GTK_WIDGET (container)))
+  if (priv->resize_pending)
     _gtk_container_dequeue_resize_handler (container);
 
+  if (priv->restyle_pending)
+    priv->restyle_pending = FALSE;
+
   if (priv->focus_child)
     {
       g_object_unref (priv->focus_child);
@@ -1542,10 +1550,9 @@ void
 _gtk_container_dequeue_resize_handler (GtkContainer *container)
 {
   g_return_if_fail (GTK_IS_CONTAINER (container));
-  g_return_if_fail (_gtk_widget_get_resize_pending (GTK_WIDGET (container)));
+  g_return_if_fail (container->priv->resize_pending);
 
-  container_resize_queue = g_slist_remove (container_resize_queue, container);
-  _gtk_widget_set_resize_pending (GTK_WIDGET (container), FALSE);
+  container->priv->resize_pending = FALSE;
 }
 
 /**
@@ -1621,11 +1628,36 @@ gtk_container_set_reallocate_redraws (GtkContainer *container,
   container->priv->reallocate_redraws = needs_redraws ? TRUE : FALSE;
 }
 
-static gboolean
-gtk_container_idle_sizer (gpointer data)
+static void
+gtk_container_idle_sizer (GdkFrameClock *clock,
+                         GtkContainer  *container)
 {
-  GSList *slist;
-  gint64 current_time;
+  /* 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 style_updated 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 (container->priv->restyle_pending)
+    {
+      GtkBitmask *empty;
+      gint64 current_time;
+
+      empty = _gtk_bitmask_new ();
+      current_time = g_get_monotonic_time ();
+
+      container->priv->restyle_pending = FALSE;
+      _gtk_style_context_validate (gtk_widget_get_style_context (GTK_WIDGET (container)),
+                                   current_time,
+                                   0,
+                                   empty);
+
+      _gtk_bitmask_free (empty);
+    }
 
   /* we may be invoked with a container_resize_queue of NULL, because
    * queue_resize could have been adding an extra idle function while
@@ -1633,41 +1665,56 @@ gtk_container_idle_sizer (gpointer data)
    * than trying to explicitely work around them with some extra flags,
    * since it doesn't cause any actual harm.
    */
-
-  /* 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 size_allocate functions often change styles and so could
-   * cause infinite loops in this function.
-   */
-  current_time = g_get_monotonic_time ();
-  for (slist = container_resize_queue; slist; slist = slist->next)
+  if (container->priv->resize_pending)
     {
-      _gtk_style_context_validate (gtk_widget_get_style_context (slist->data),
-                                   current_time,
-                                   0);
+      container->priv->resize_pending = FALSE;
+      gtk_container_check_resize (container);
     }
 
-  while (container_resize_queue)
+  if (!container->priv->restyle_pending && !container->priv->resize_pending)
+    {
+      _gtk_container_stop_idle_sizer (container);
+    }
+  else
     {
-      GtkWidget *widget;
+      gdk_frame_clock_request_phase (clock,
+                                     GDK_FRAME_CLOCK_PHASE_LAYOUT);
+    }
+}
 
-      slist = container_resize_queue;
-      container_resize_queue = slist->next;
-      widget = slist->data;
-      g_slist_free_1 (slist);
+static void
+gtk_container_start_idle_sizer (GtkContainer *container)
+{
+  GdkFrameClock *clock;
 
-      _gtk_widget_set_resize_pending (widget, FALSE);
-      gtk_container_check_resize (GTK_CONTAINER (widget));
-    }
+  if (container->priv->resize_handler != 0)
+    return;
 
-  gdk_window_process_all_updates ();
+  clock = gtk_widget_get_frame_clock (GTK_WIDGET (container));
+  if (clock == NULL)
+    return;
 
-  return FALSE;
+  container->priv->resize_clock = clock;
+  container->priv->resize_handler = g_signal_connect (clock, "layout",
+                                                     G_CALLBACK (gtk_container_idle_sizer), container);
+  gdk_frame_clock_request_phase (clock,
+                                 GDK_FRAME_CLOCK_PHASE_LAYOUT);
 }
 
 void
-_gtk_container_queue_resize_handler (GtkContainer *container)
+_gtk_container_stop_idle_sizer (GtkContainer *container)
+{
+  if (container->priv->resize_handler == 0)
+    return;
+
+  g_signal_handler_disconnect (container->priv->resize_clock,
+                               container->priv->resize_handler);
+  container->priv->resize_handler = 0;
+  container->priv->resize_clock = NULL;
+}
+
+static void
+gtk_container_queue_resize_handler (GtkContainer *container)
 {
   GtkWidget *widget;
 
@@ -1682,22 +1729,16 @@ _gtk_container_queue_resize_handler (GtkContainer *container)
       switch (container->priv->resize_mode)
         {
         case GTK_RESIZE_QUEUE:
-          if (!_gtk_widget_get_resize_pending (widget))
+          if (!container->priv->resize_pending)
             {
-              _gtk_widget_set_resize_pending (widget, TRUE);
-              if (container_resize_queue == NULL)
-                gdk_threads_add_idle_full (GTK_PRIORITY_RESIZE,
-                                           gtk_container_idle_sizer,
-                                           NULL, NULL);
-              container_resize_queue = g_slist_prepend (container_resize_queue, container);
+              container->priv->resize_pending = TRUE;
+              gtk_container_start_idle_sizer (container);
             }
           break;
 
         case GTK_RESIZE_IMMEDIATE:
-          _gtk_style_context_validate (gtk_widget_get_style_context (widget),
-                                       g_get_monotonic_time (),
-                                       0);
           gtk_container_check_resize (container);
+          break;
 
         case GTK_RESIZE_PARENT:
         default:
@@ -1720,8 +1761,7 @@ _gtk_container_queue_resize_internal (GtkContainer *container,
   do
     {
       _gtk_widget_set_alloc_needed (widget, TRUE);
-      _gtk_widget_set_width_request_needed (widget, TRUE);
-      _gtk_widget_set_height_request_needed (widget, TRUE);
+      _gtk_size_request_cache_clear (_gtk_widget_peek_request_cache (widget));
 
       if (GTK_IS_RESIZE_CONTAINER (widget))
         break;
@@ -1731,7 +1771,23 @@ _gtk_container_queue_resize_internal (GtkContainer *container,
   while (widget);
 
   if (widget && !invalidate_only)
-    _gtk_container_queue_resize_handler (GTK_CONTAINER (widget));
+    gtk_container_queue_resize_handler (GTK_CONTAINER (widget));
+}
+
+void
+_gtk_container_queue_restyle (GtkContainer *container)
+{
+  GtkContainerPrivate *priv;
+
+  g_return_if_fail (GTK_CONTAINER (container));
+
+  priv = container->priv;
+
+  if (priv->restyle_pending)
+    return;
+
+  gtk_container_start_idle_sizer (container);
+  priv->restyle_pending = TRUE;
 }
 
 /**
@@ -1764,6 +1820,13 @@ _gtk_container_resize_invalidate (GtkContainer *container)
   _gtk_container_queue_resize_internal (container, TRUE);
 }
 
+void
+_gtk_container_maybe_start_idle_sizer (GtkContainer *container)
+{
+  if (container->priv->restyle_pending || container->priv->resize_pending)
+    gtk_container_start_idle_sizer (container);
+}
+
 void
 gtk_container_check_resize (GtkContainer *container)
 {
@@ -1867,32 +1930,11 @@ gtk_container_adjust_size_allocation (GtkWidget         *widget,
 
   container = GTK_CONTAINER (widget);
 
-  if (!GTK_CONTAINER_GET_CLASS (widget)->_handle_border_width)
+  if (GTK_CONTAINER_GET_CLASS (widget)->_handle_border_width)
     {
-      parent_class->adjust_size_allocation (widget, orientation,
-                                            minimum_size, natural_size, allocated_pos,
-                                            allocated_size);
-      return;
-    }
-
-  border_width = container->priv->border_width;
-
-  *allocated_size -= border_width * 2;
-
-  /* If we get a pathological too-small allocation to hold
-   * even the border width, leave all allocation to the actual
-   * widget, and leave x,y unchanged. (GtkWidget's min size is
-   * 1x1 if you're wondering why <1 and not <0)
-   *
-   * As long as we have space, set x,y properly.
-   */
+      border_width = container->priv->border_width;
 
-  if (*allocated_size < 1)
-    {
-      *allocated_size += border_width * 2;
-    }
-  else
-    {
+      *allocated_size -= border_width * 2;
       *allocated_pos += border_width;
       *minimum_size -= border_width * 2;
       *natural_size -= border_width * 2;
@@ -1938,27 +1980,17 @@ count_request_modes (GtkWidget        *widget,
 static GtkSizeRequestMode 
 gtk_container_get_request_mode (GtkWidget *widget)
 {
-  GtkContainer        *container = GTK_CONTAINER (widget);
-  GtkContainerPrivate *priv      = container->priv;
-
-  /* Recalculate the request mode of the children by majority
-   * vote whenever the internal content changes */
-  if (_gtk_widget_get_width_request_needed (widget) ||
-      _gtk_widget_get_height_request_needed (widget))
-    {
-      RequestModeCount count = { 0, 0 };
-
-      gtk_container_forall (container, (GtkCallback)count_request_modes, &count);
+  GtkContainer *container = GTK_CONTAINER (widget);
+  RequestModeCount count = { 0, 0 };
 
-      if (!count.hfw && !count.wfh)
-       priv->request_mode = GTK_SIZE_REQUEST_CONSTANT_SIZE;
-      else
-       priv->request_mode = count.wfh > count.hfw ? 
-         GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT :
-         GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
-    }
+  gtk_container_forall (container, (GtkCallback)count_request_modes, &count);
 
-  return priv->request_mode;
+  if (!count.hfw && !count.wfh)
+    return GTK_SIZE_REQUEST_CONSTANT_SIZE;
+  else
+    return count.wfh > count.hfw ? 
+        GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT :
+       GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
 }
 
 /**