diff options
author | Timm Bäder <mail@baedert.org> | 2019-07-06 07:15:18 +0200 |
---|---|---|
committer | Timm Bäder <mail@baedert.org> | 2019-07-07 15:58:42 +0200 |
commit | 7444773a00476e63feb94cf53aacb409dd0ab3d3 (patch) | |
tree | f1f6a4d24baeb8e2c22223506be4071030890ac2 | |
parent | 96b782c026fe026dda0b2c61dcb7cb50f4aa828c (diff) | |
download | gtk+-wip/baedert/resize.tar.gz |
Defer resize invalidation to layout phasewip/baedert/resize
When calling queue_resize()/queue_allocate(), just record a
per-GtkWindow list of widgets that want to be resized.
-rw-r--r-- | gtk/gtkcontainer.c | 167 | ||||
-rw-r--r-- | gtk/gtkwidget.c | 125 | ||||
-rw-r--r-- | gtk/gtkwidgetprivate.h | 3 | ||||
-rw-r--r-- | gtk/gtkwindow.c | 87 | ||||
-rw-r--r-- | 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__ */ |