[gtk/wip/baedert/resize] Defer resize invalidation to layout phase
- From: Timm Bäder <baedert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/wip/baedert/resize] Defer resize invalidation to layout phase
- Date: Sun, 7 Jul 2019 13:59:27 +0000 (UTC)
commit 7444773a00476e63feb94cf53aacb409dd0ab3d3
Author: Timm Bäder <mail baedert org>
Date: Sat Jul 6 07:15:18 2019 +0200
Defer resize invalidation to layout phase
When calling queue_resize()/queue_allocate(), just record a
per-GtkWindow list of widgets that want to be resized.
gtk/gtkcontainer.c | 167 +++++++++++++++++++++++++++++++++++--------------
gtk/gtkwidget.c | 125 +++++++++++++++++++++++++++++++++---
gtk/gtkwidgetprivate.h | 3 +
gtk/gtkwindow.c | 87 ++++++++++++++++++++++++++
gtk/gtkwindowprivate.h | 17 +++++
5 files changed, 343 insertions(+), 56 deletions(-)
---
diff --git a/gtk/gtkcontainer.c b/gtk/gtkcontainer.c
index bbc6968bdf..cc0c3c892b 100644
--- a/gtk/gtkcontainer.c
+++ b/gtk/gtkcontainer.c
@@ -36,7 +36,7 @@
#include "gtkstylecontextprivate.h"
#include "gtktypebuiltins.h"
#include "gtkwidgetprivate.h"
-//#include "gtkwindowprivate.h"
+#include "gtkdebug.h"
#include "a11y/gtkcontaineraccessibleprivate.h"
@@ -85,6 +85,7 @@
* See more about implementing custom widgets at https://wiki.gnome.org/HowDoI/CustomWidgets
*/
+#define N_ALLOCATE_ITERATIONS 2
struct _GtkContainerPrivate
{
@@ -333,62 +334,135 @@ gtk_container_remove (GtkContainer *container,
g_object_unref (container);
}
-static gboolean
-gtk_container_needs_idle_sizer (GtkContainer *container)
+static void
+gtk_container_idle_sizer (GdkFrameClock *clock,
+ GtkContainer *container)
{
GtkContainerPrivate *priv = gtk_container_get_instance_private (container);
+ GtkWidget *widget = GTK_WIDGET (container);
+ GtkWindow *window;
+ GPtrArray *resize_widgets;
+ GPtrArray *allocate_widgets;
+ int allocate_iteration;
+ guint i;
+
+ if (!GTK_IS_WINDOW (container))
+ return;
- if (priv->restyle_pending)
- return TRUE;
- return gtk_widget_needs_allocate (GTK_WIDGET (container));
-}
+ g_message ("========== %s ========", __FUNCTION__);
-static void
-gtk_container_idle_sizer (GdkFrameClock *clock,
- GtkContainer *container)
-{
- GtkContainerPrivate *priv = gtk_container_get_instance_private (container);
+ window = GTK_WINDOW (container);
+ resize_widgets = gtk_window_get_resize_widgets (window);
+ allocate_widgets = gtk_window_get_allocate_widgets (window);
- /* 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 (priv->restyle_pending)
+ for (allocate_iteration = 0; allocate_iteration < N_ALLOCATE_ITERATIONS; allocate_iteration ++)
{
- priv->restyle_pending = FALSE;
- gtk_css_node_validate (gtk_widget_get_css_node (GTK_WIDGET (container)));
- }
+ const gboolean last_iteration = (allocate_iteration == N_ALLOCATE_ITERATIONS - 1);
+ g_message ("Iteration %d", allocate_iteration);
- /* 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 (GTK_WIDGET (container)))
- {
- if (GTK_IS_WINDOW (container))
- gtk_window_check_resize (GTK_WINDOW (container));
- else
- g_warning ("gtk_container_idle_sizer() called on a non-window");
- }
+ if (last_iteration)
+ gtk_window_set_last_allocate_iteration (window, TRUE);
- if (!gtk_container_needs_idle_sizer (container))
- {
- gtk_container_stop_idle_sizer (container);
+ /* Actually mutate the widget tree */
+ for (i = 0; i < resize_widgets->len; i ++)
+ {
+ GtkWidget *w = g_ptr_array_index (resize_widgets, i);
+ old_gtk_widget_queue_resize (w);
+ }
+
+ for (i = 0; i < allocate_widgets->len; i ++)
+ {
+ GtkWidget *w = g_ptr_array_index (allocate_widgets, i);
+ old_gtk_widget_queue_allocate (w);
+ }
+
+ /* Clear for now */
+ g_ptr_array_remove_range (resize_widgets, 0, resize_widgets->len);
+ g_ptr_array_remove_range (allocate_widgets, 0, allocate_widgets->len);
+
+ /* 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 (priv->restyle_pending)
+ {
+ priv->restyle_pending = FALSE;
+ gtk_css_node_validate (gtk_widget_get_css_node (GTK_WIDGET (container)));
+ }
+
+ /* Actually do the allocate */
+ gtk_window_check_resize (GTK_WINDOW (container));
+
+ if (resize_widgets->len == 0 &&
+ allocate_widgets->len == 0 &&
+ priv->restyle_pending == 0)
+ {
+ gtk_container_stop_idle_sizer (container);
+
+ /* We don't need a second iteration */
+ break;
+ }
+ else if (!last_iteration)
+ {
+ gdk_frame_clock_request_phase (clock,
+ GDK_FRAME_CLOCK_PHASE_LAYOUT);
+
+ g_message ("Second iteration caused by...");
+ for (i = 0; i < resize_widgets->len; i ++)
+ {
+ GtkWidget *w = g_ptr_array_index (resize_widgets, i);
+ g_message ("Resize:: %s %s %p",
+ G_OBJECT_TYPE_NAME (w),
+ gtk_css_node_get_name (gtk_widget_get_css_node (w)),
+ w);
+ }
+
+ for (i = 0; i < allocate_widgets->len; i ++)
+ {
+ GtkWidget *w = g_ptr_array_index (allocate_widgets, i);
+ g_message ("Allocate: %s %s %p",
+ G_OBJECT_TYPE_NAME (w),
+ gtk_css_node_get_name (gtk_widget_get_css_node (w)),
+ w);
+ }
+
+
+ }
}
- else
+
+ if (1 || GTK_DISPLAY_DEBUG_CHECK (_gtk_widget_get_display (widget), GEOMETRY))
{
- gdk_frame_clock_request_phase (clock,
- GDK_FRAME_CLOCK_PHASE_LAYOUT);
+ for (i = 0; i < resize_widgets->len; i ++)
+ {
+ GtkWidget *w = g_ptr_array_index (resize_widgets, i);
+ g_warning (" Resize after second iteration: %s %s %p",
+ G_OBJECT_TYPE_NAME (w),
+ gtk_css_node_get_name (gtk_widget_get_css_node (w)),
+ w);
+ }
+
+ for (i = 0; i < allocate_widgets->len; i ++)
+ {
+ GtkWidget *w = g_ptr_array_index (allocate_widgets, i);
+ g_warning (" Allocate after second iteration: %s %s %p",
+ G_OBJECT_TYPE_NAME (w),
+ gtk_css_node_get_name (gtk_widget_get_css_node (w)),
+ w);
+ }
}
+
+ /* Ignore everything after the last iteration */
+ g_ptr_array_remove_range (resize_widgets, 0, resize_widgets->len);
+ g_ptr_array_remove_range (allocate_widgets, 0, allocate_widgets->len);
+ gtk_container_stop_idle_sizer (container);
+ gtk_window_set_last_allocate_iteration (window, FALSE);
}
void
@@ -400,9 +474,6 @@ gtk_container_start_idle_sizer (GtkContainer *container)
if (priv->resize_handler != 0)
return;
- if (!gtk_container_needs_idle_sizer (container))
- return;
-
clock = gtk_widget_get_frame_clock (GTK_WIDGET (container));
if (clock == NULL)
return;
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index 42ba750375..d8f71e1bf9 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -859,6 +859,59 @@ gtk_widget_real_grab_notify (GtkWidget *widget,
}
}
+
+static void
+gtk_widget_dequeue_resize (GtkWidget *widget)
+{
+ GtkRoot *root;
+ GtkWindow *window;
+
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+
+ root = gtk_widget_get_root (widget);
+
+ if (!root)
+ return;
+
+ /* TODO: This check should ultimately go away I guess. */
+ if (!GTK_IS_WINDOW (root))
+ {
+ g_warning ("%s: Not in a window...", __FUNCTION__);
+ return;
+ }
+
+ window = GTK_WINDOW (root);
+ gtk_window_remove_resize_widget (window, widget);
+}
+
+
+static void
+gtk_widget_dequeue_allocate (GtkWidget *widget)
+{
+ GtkRoot *root;
+ GtkWindow *window;
+
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+
+ root = gtk_widget_get_root (widget);
+
+ if (!root)
+ return;
+
+ /* TODO: This check should ultimately go away I guess. */
+ if (!GTK_IS_WINDOW (root))
+ {
+ g_warning ("%s: Not in a window...", __FUNCTION__);
+ return;
+ }
+
+ window = GTK_WINDOW (root);
+ gtk_window_remove_allocate_widget (window, widget);
+}
+
+
+
+
static void
gtk_widget_real_root (GtkWidget *widget)
{
@@ -868,6 +921,10 @@ gtk_widget_real_root (GtkWidget *widget)
static void
gtk_widget_real_unroot (GtkWidget *widget)
{
+
+ gtk_widget_dequeue_resize (widget);
+ gtk_widget_dequeue_allocate (widget);
+
gtk_widget_forall (widget, (GtkCallback) gtk_widget_unroot, NULL);
}
@@ -2921,6 +2978,9 @@ gtk_widget_unroot (GtkWidget *widget)
g_assert (priv->root);
g_assert (!priv->realized);
+ gtk_widget_dequeue_resize (widget);
+ gtk_widget_dequeue_allocate (widget);
+
surface_transform_data = priv->surface_transform_data;
if (surface_transform_data &&
surface_transform_data->tracked_parent)
@@ -4076,12 +4136,25 @@ gtk_widget_set_alloc_needed (GtkWidget *widget);
void
gtk_widget_queue_allocate (GtkWidget *widget)
{
+ GtkRoot *root;
+ GtkWindow *window;
+
g_return_if_fail (GTK_IS_WIDGET (widget));
- if (_gtk_widget_get_realized (widget))
- gtk_widget_queue_draw (widget);
+ root = gtk_widget_get_root (widget);
- gtk_widget_set_alloc_needed (widget);
+ if (!root)
+ return;
+
+ /* TODO: This check should ultimately go away I guess. */
+ if (!GTK_IS_WINDOW (root))
+ {
+ g_warning ("%s: Not in a window...", __FUNCTION__);
+ return;
+ }
+
+ window = GTK_WINDOW (root);
+ gtk_window_add_allocate_widget (window, widget);
}
static inline gboolean
@@ -4133,6 +4206,28 @@ gtk_widget_queue_resize_internal (GtkWidget *widget)
}
}
+void
+old_gtk_widget_queue_resize (GtkWidget *widget)
+{
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+
+ if (_gtk_widget_get_realized (widget))
+ gtk_widget_queue_draw (widget);
+
+ gtk_widget_queue_resize_internal (widget);
+}
+
+void
+old_gtk_widget_queue_allocate (GtkWidget *widget)
+{
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+
+ if (_gtk_widget_get_realized (widget))
+ gtk_widget_queue_draw (widget);
+
+ gtk_widget_set_alloc_needed (widget);
+}
+
/**
* gtk_widget_queue_resize:
* @widget: a #GtkWidget
@@ -4151,12 +4246,25 @@ gtk_widget_queue_resize_internal (GtkWidget *widget)
void
gtk_widget_queue_resize (GtkWidget *widget)
{
+ GtkRoot *root;
+ GtkWindow *window;
+
g_return_if_fail (GTK_IS_WIDGET (widget));
- if (_gtk_widget_get_realized (widget))
- gtk_widget_queue_draw (widget);
+ root = gtk_widget_get_root (widget);
- gtk_widget_queue_resize_internal (widget);
+ if (!root)
+ return;
+
+ /* TODO: This check should ultimately go away I guess. */
+ if (!GTK_IS_WINDOW (root))
+ {
+ g_warning ("%s: Not in a window...", __FUNCTION__);
+ return;
+ }
+
+ window = GTK_WINDOW (root);
+ gtk_window_add_resize_widget (window, widget);
}
/**
@@ -4171,7 +4279,8 @@ gtk_widget_queue_resize_no_redraw (GtkWidget *widget)
{
g_return_if_fail (GTK_IS_WIDGET (widget));
- gtk_widget_queue_resize_internal (widget);
+ /* TODO: Remove this entire function */
+ gtk_widget_queue_resize (widget);
}
/**
@@ -11498,7 +11607,7 @@ gtk_widget_set_alloc_needed (GtkWidget *widget)
if (GTK_IS_ROOT (widget))
{
- gtk_container_start_idle_sizer (GTK_CONTAINER (widget));
+ /*gtk_container_start_idle_sizer (GTK_CONTAINER (widget));*/
break;
}
diff --git a/gtk/gtkwidgetprivate.h b/gtk/gtkwidgetprivate.h
index 26df307cc2..4ecc160996 100644
--- a/gtk/gtkwidgetprivate.h
+++ b/gtk/gtkwidgetprivate.h
@@ -367,6 +367,9 @@ guint gtk_widget_add_surface_transform_changed_callback (GtkWidget
void gtk_widget_remove_surface_transform_changed_callback (GtkWidget *widget,
guint id);
+void old_gtk_widget_queue_resize (GtkWidget *widget);
+void old_gtk_widget_queue_allocate (GtkWidget *widget);
+
/* inline getters */
diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c
index dab50be990..2be4ac06bc 100644
--- a/gtk/gtkwindow.c
+++ b/gtk/gtkwindow.c
@@ -266,6 +266,8 @@ typedef struct
guint hide_on_close : 1;
guint in_emit_close_request : 1;
+ guint last_allocate_iteration : 1;
+
GdkSurfaceTypeHint type_hint;
GtkGesture *click_gesture;
@@ -283,6 +285,10 @@ typedef struct
GList *foci;
GtkConstraintSolver *constraint_solver;
+
+ GPtrArray *resize_widgets;
+ GPtrArray *allocate_widgets;
+
} GtkWindowPrivate;
#ifdef GDK_WINDOWING_X11
@@ -1836,6 +1842,9 @@ gtk_window_init (GtkWindow *window)
priv->has_user_ref_count = TRUE;
gtk_window_update_debugging ();
+ priv->resize_widgets = g_ptr_array_new ();
+ priv->allocate_widgets = g_ptr_array_new ();
+
#ifdef GDK_WINDOWING_X11
g_signal_connect (gtk_settings_get_for_display (priv->display),
"notify::gtk-application-prefer-dark-theme",
@@ -4720,6 +4729,9 @@ gtk_window_finalize (GObject *object)
priv->keys_changed_handler = 0;
}
+ g_ptr_array_free (priv->resize_widgets, TRUE);
+ g_ptr_array_free (priv->allocate_widgets, TRUE);
+
g_signal_handlers_disconnect_by_func (gdk_display_get_default_seat (priv->display),
device_removed_cb,
window);
@@ -9521,3 +9533,78 @@ gtk_window_maybe_update_cursor (GtkWindow *window,
break;
}
}
+
+void
+gtk_window_add_resize_widget (GtkWindow *window,
+ GtkWidget *widget)
+{
+ GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
+
+ if (g_ptr_array_find (priv->resize_widgets, widget, NULL))
+ return;
+
+ if (priv->last_allocate_iteration)
+ g_critical ("Resize of %s %p caused N+1 allocate iterations.", G_OBJECT_TYPE_NAME (widget), widget);
+
+ g_ptr_array_add (priv->resize_widgets, widget);
+ gtk_container_start_idle_sizer (GTK_CONTAINER (window));
+}
+
+void
+gtk_window_remove_resize_widget (GtkWindow *window,
+ GtkWidget *widget)
+{
+ GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
+
+ g_ptr_array_remove_fast (priv->resize_widgets, widget);
+}
+
+void
+gtk_window_add_allocate_widget (GtkWindow *window,
+ GtkWidget *widget)
+{
+ GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
+
+ if (g_ptr_array_find (priv->allocate_widgets, widget, NULL))
+ return;
+
+ if (priv->last_allocate_iteration)
+ g_critical ("Allocate of %s %p caused N+1 allocate iterations.", G_OBJECT_TYPE_NAME (widget), widget);
+
+ g_ptr_array_add (priv->allocate_widgets, widget);
+ gtk_container_start_idle_sizer (GTK_CONTAINER (window));
+}
+
+void
+gtk_window_remove_allocate_widget (GtkWindow *window,
+ GtkWidget *widget)
+{
+ GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
+
+ g_ptr_array_remove_fast (priv->allocate_widgets, widget);
+}
+
+GPtrArray *
+gtk_window_get_resize_widgets (GtkWindow *window)
+{
+ GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
+
+ return priv->resize_widgets;
+}
+
+GPtrArray *
+gtk_window_get_allocate_widgets (GtkWindow *window)
+{
+ GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
+
+ return priv->allocate_widgets;
+}
+
+void
+gtk_window_set_last_allocate_iteration (GtkWindow *window,
+ gboolean is_last)
+{
+ GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
+
+ priv->last_allocate_iteration = is_last;
+}
diff --git a/gtk/gtkwindowprivate.h b/gtk/gtkwindowprivate.h
index 941fe9c8f1..e3161b3e42 100644
--- a/gtk/gtkwindowprivate.h
+++ b/gtk/gtkwindowprivate.h
@@ -164,6 +164,23 @@ GtkWidget * gtk_window_pick_popover (GtkWindow *window,
double y,
GtkPickFlags flags);
+GPtrArray * gtk_window_get_resize_widgets (GtkWindow *window);
+void gtk_window_add_resize_widget (GtkWindow *window,
+ GtkWidget *widget);
+void gtk_window_remove_resize_widget (GtkWindow *window,
+ GtkWidget *widget);
+
+GPtrArray * gtk_window_get_allocate_widgets (GtkWindow *window);
+void gtk_window_add_allocate_widget (GtkWindow *window,
+ GtkWidget *widget);
+void gtk_window_remove_allocate_widget (GtkWindow *window,
+ GtkWidget *widget);
+
+void gtk_window_set_last_allocate_iteration (GtkWindow *window,
+ gboolean is_last);
+
+
+
G_END_DECLS
#endif /* __GTK_WINDOW_PRIVATE_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]