Compare commits

...

2 Commits

Author SHA1 Message Date
Timm Bäder
1aec8a0672 Add size allocate widget test 2018-03-26 11:59:05 +02:00
Timm Bäder
73747dafe4 widget: Defer resizing invalidation to the layout phase 2018-03-26 11:55:56 +02:00
9 changed files with 425 additions and 25 deletions

View File

@@ -413,7 +413,6 @@ gdk_frame_clock_paint_idle (void *data)
case GDK_FRAME_CLOCK_PHASE_LAYOUT:
if (priv->freeze_count == 0)
{
int iter;
#ifdef G_ENABLE_DEBUG
if (GDK_DEBUG_CHECK (FRAMES))
{
@@ -424,20 +423,8 @@ gdk_frame_clock_paint_idle (void *data)
#endif /* G_ENABLE_DEBUG */
priv->phase = GDK_FRAME_CLOCK_PHASE_LAYOUT;
/* We loop in the layout phase, because we don't want to progress
* into the paint phase with invalid size allocations. This may
* happen in some situation like races between user window
* resizes and natural size changes.
*/
iter = 0;
while ((priv->requested & GDK_FRAME_CLOCK_PHASE_LAYOUT) &&
priv->freeze_count == 0 && iter++ < 4)
{
priv->requested &= ~GDK_FRAME_CLOCK_PHASE_LAYOUT;
_gdk_frame_clock_emit_layout (clock);
}
if (iter == 5)
g_warning ("gdk-frame-clock: layout continuously requested, giving up after 4 tries");
priv->requested &= ~GDK_FRAME_CLOCK_PHASE_LAYOUT;
_gdk_frame_clock_emit_layout (clock);
}
/* fallthrough */
case GDK_FRAME_CLOCK_PHASE_PAINT:

View File

@@ -43,6 +43,7 @@
#include "gtkwindow.h"
#include "a11y/gtkcontaineraccessibleprivate.h"
#include "gtkwindowprivate.h"
#include <gobject/gobjectnotifyqueue.c>
#include <gobject/gvaluecollector.h>
@@ -59,6 +60,9 @@
GTK_IS_SHORTCUTS_SECTION (x) || \
GTK_IS_SHORTCUTS_WINDOW (x))
#define N_RESIZE_ITERATIONS 2
/**
* SECTION:gtkcontainer
* @Short_description: Base class for widgets which contain other widgets
@@ -159,6 +163,7 @@ struct _GtkContainerPrivate
guint has_focus_chain : 1;
guint restyle_pending : 1;
guint last_size_allocate : 1;
};
enum {
@@ -1556,11 +1561,54 @@ gtk_container_needs_idle_sizer (GtkContainer *container)
return gtk_widget_needs_allocate (GTK_WIDGET (container));
}
gboolean
gtk_container_doing_last_size_allocate (GtkContainer *container)
{
GtkContainerPrivate *priv = gtk_container_get_instance_private (container);
return priv->last_size_allocate;
}
static void
apply_resize_widgets (GtkWindow *window)
{
GPtrArray *resize_widgets = gtk_window_get_resize_widgets (window);
GPtrArray *allocate_widgets = gtk_window_get_allocate_widgets (window);
guint i, p;
g_message ("Resize widgets : %u", resize_widgets->len);
for (i = 0, p = resize_widgets->len; i < p; i ++)
{
GtkWidget *widget = g_ptr_array_index (resize_widgets, i);
g_message (" - %s %p", G_OBJECT_TYPE_NAME (widget), widget);
_gtk_widget_queue_resize (widget);
g_object_unref (G_OBJECT (widget));
}
g_message ("Allocate widgets: %u", allocate_widgets->len);
for (i = 0, p = allocate_widgets->len; i < p; i ++)
{
GtkWidget *widget = g_ptr_array_index (allocate_widgets, i);
g_message (" - %s %p", G_OBJECT_TYPE_NAME (widget), widget);
_gtk_widget_queue_allocate (widget);
g_object_unref (G_OBJECT (widget));
}
g_ptr_array_remove_range (resize_widgets, 0, resize_widgets->len);
g_ptr_array_remove_range (allocate_widgets, 0, allocate_widgets->len);
}
static void
gtk_container_idle_sizer (GdkFrameClock *clock,
GtkContainer *container)
{
GtkContainerPrivate *priv = gtk_container_get_instance_private (container);
guint resize_iteration;
static int k;
g_message ("####### %d Idle Sizer for %s", ++k, G_OBJECT_TYPE_NAME (container));
/* We validate the style contexts in a single loop before even trying
* to handle resizes instead of doing validations inline.
@@ -1584,9 +1632,52 @@ gtk_container_idle_sizer (GdkFrameClock *clock,
* 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)))
for (resize_iteration = 0; resize_iteration < N_RESIZE_ITERATIONS; resize_iteration ++)
{
gtk_container_check_resize (container);
const gboolean last_iteration = (resize_iteration == N_RESIZE_ITERATIONS - 1);
if (GTK_IS_WINDOW (container))
apply_resize_widgets (GTK_WINDOW (container));
if (last_iteration)
priv->last_size_allocate = TRUE;
if (gtk_widget_needs_allocate (GTK_WIDGET (container)))
gtk_container_check_resize (container);
if (last_iteration)
priv->last_size_allocate = FALSE;
}
if (GTK_IS_WINDOW (container))
{
GPtrArray *resize_widgets = gtk_window_get_resize_widgets (GTK_WINDOW (container));
GPtrArray *allocate_widgets = gtk_window_get_allocate_widgets (GTK_WINDOW (container));
/* Explicitly drop all resize/allocate widgets still present after the above loop */
if (resize_widgets->len > 0 || allocate_widgets->len > 0)
{
g_warning ("AFTER size-allocate No. %d:", N_RESIZE_ITERATIONS);
guint i, p;
g_message ("Resize widgets : %u", resize_widgets->len);
for (i = 0, p = resize_widgets->len; i < p; i ++)
{
GtkWidget *widget = g_ptr_array_index (resize_widgets, i);
g_message (" - %s %p", G_OBJECT_TYPE_NAME (widget), widget);
}
g_message ("Allocate widgets: %u", allocate_widgets->len);
for (i = 0, p = allocate_widgets->len; i < p; i ++)
{
GtkWidget *widget = g_ptr_array_index (allocate_widgets, i);
g_message (" - %s %p", G_OBJECT_TYPE_NAME (widget), widget);
}
/* Ignore every queue_resize and queue_allocate after the second iteration. */
g_ptr_array_remove_range (resize_widgets, 0, resize_widgets->len);
g_ptr_array_remove_range (allocate_widgets, 0, allocate_widgets->len);
}
}
if (!gtk_container_needs_idle_sizer (container))
@@ -1642,7 +1733,6 @@ gtk_container_queue_resize_handler (GtkContainer *container)
widget = GTK_WIDGET (container);
if (_gtk_widget_get_visible (widget) &&
gtk_widget_needs_allocate (widget) &&
_gtk_widget_is_toplevel (widget))
{
gtk_container_start_idle_sizer (container);

View File

@@ -33,6 +33,7 @@ void _gtk_container_stop_idle_sizer (GtkContainer *container);
void _gtk_container_maybe_start_idle_sizer (GtkContainer *container);
void gtk_container_set_focus_child (GtkContainer *container,
GtkWidget *child);
gboolean gtk_container_doing_last_size_allocate (GtkContainer *container);
G_END_DECLS

View File

@@ -3225,6 +3225,8 @@ gtk_widget_unparent (GtkWidget *widget)
priv->clip.width,
priv->clip.height);
gtk_widget_dequeue_resize (widget);
if (priv->visible && _gtk_widget_get_visible (priv->parent))
gtk_widget_queue_resize (priv->parent);
@@ -3411,8 +3413,6 @@ gtk_widget_show (GtkWidget *widget)
parent = _gtk_widget_get_parent (widget);
if (parent)
{
gtk_widget_queue_resize (parent);
/* see comment in set_parent() for why this should and can be
* conditional
*/
@@ -3428,6 +3428,7 @@ gtk_widget_show (GtkWidget *widget)
g_object_notify_by_pspec (G_OBJECT (widget), widget_props[PROP_VISIBLE]);
gtk_widget_pop_verify_invariants (widget);
gtk_widget_queue_resize (widget);
g_object_unref (widget);
}
}
@@ -3487,9 +3488,9 @@ gtk_widget_hide (GtkWidget *widget)
parent = gtk_widget_get_parent (widget);
if (parent)
gtk_widget_queue_resize (parent);
gtk_widget_queue_resize (parent);
gtk_widget_queue_allocate (widget);
gtk_widget_dequeue_resize (widget);
gtk_widget_pop_verify_invariants (widget);
g_object_unref (widget);
@@ -4066,10 +4067,16 @@ 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);
GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
if (GTK_IS_WINDOW (toplevel))
{
gtk_window_add_allocate_widget (GTK_WINDOW (toplevel), widget);
gtk_container_queue_resize_handler (GTK_CONTAINER (toplevel));
}
}
static inline gboolean
@@ -4140,9 +4147,45 @@ gtk_widget_queue_resize (GtkWidget *widget)
if (_gtk_widget_get_realized (widget))
gtk_widget_queue_draw (widget);
GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
if (GTK_IS_WINDOW (toplevel))
{
if (gtk_container_doing_last_size_allocate (GTK_CONTAINER (toplevel)))
{
g_warning ("%s in last size-allocate iteration!", __FUNCTION__);
}
gtk_window_add_resize_widget (GTK_WINDOW (toplevel), widget);
gtk_container_queue_resize_handler (GTK_CONTAINER (toplevel));
}
}
void
gtk_widget_dequeue_resize (GtkWidget *widget)
{
GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
if (GTK_IS_WINDOW (toplevel))
{
gtk_window_dequeue_resize_for (GTK_WINDOW (toplevel), widget);
}
}
void
_gtk_widget_queue_resize (GtkWidget *widget)
{
g_return_if_fail (GTK_IS_WIDGET (widget));
gtk_widget_queue_resize_internal (widget);
}
void
_gtk_widget_queue_allocate (GtkWidget *widget)
{
gtk_widget_set_alloc_needed (widget);
}
/**
* gtk_widget_queue_resize_no_redraw:
* @widget: a #GtkWidget
@@ -4155,7 +4198,11 @@ gtk_widget_queue_resize_no_redraw (GtkWidget *widget)
{
g_return_if_fail (GTK_IS_WIDGET (widget));
gtk_widget_queue_resize_internal (widget);
GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
if (GTK_IS_WINDOW (toplevel))
{
gtk_window_add_resize_widget (GTK_WINDOW (toplevel), widget);
}
}
/**
@@ -7337,7 +7384,7 @@ gtk_widget_reposition_after (GtkWidget *widget,
_gtk_widget_get_mapped (priv->parent))
gtk_widget_map (widget);
gtk_widget_queue_resize (priv->parent);
gtk_widget_queue_resize (widget);
}
/* child may cause parent's expand to change, if the child is

View File

@@ -333,10 +333,14 @@ void gtk_widget_init_legacy_controller (GtkWidget *widget);
void gtk_widget_get_origin_relative_to_parent (GtkWidget *widget,
int *origin_x,
int *origin_y);
void gtk_widget_dequeue_resize (GtkWidget *widget);
void _gtk_widget_queue_resize (GtkWidget *widget);
void _gtk_widget_queue_allocate (GtkWidget *widget);
/* inline getters */
static inline GtkWidget *

View File

@@ -204,6 +204,9 @@ struct _GtkWindowPrivate
GdkModifierType mnemonic_modifier;
GPtrArray *resize_widgets;
GPtrArray *allocate_widgets;
gchar *startup_id;
gchar *title;
gchar *wm_role;
@@ -1889,6 +1892,9 @@ gtk_window_init (GtkWindow *window)
priv = window->priv;
gtk_widget_set_has_surface (widget, TRUE);
priv->resize_widgets = g_ptr_array_new ();
priv->allocate_widgets = g_ptr_array_new ();
_gtk_widget_set_is_toplevel (widget, TRUE);
_gtk_widget_set_anchored (widget, TRUE);
@@ -11449,3 +11455,71 @@ 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);
g_ptr_array_add (priv->resize_widgets, g_object_ref (G_OBJECT (widget)));
}
void
gtk_window_add_allocate_widget (GtkWindow *window,
GtkWidget *widget)
{
GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
g_ptr_array_add (priv->allocate_widgets, g_object_ref (G_OBJECT (widget)));
}
void
gtk_window_dequeue_resize_for (GtkWindow *window,
GtkWidget *widget)
{
GtkWindowPrivate *priv = gtk_window_get_instance_private (window);
guint i;
for (i = 0; i < priv->resize_widgets->len; i ++)
{
GtkWidget *w = g_ptr_array_index (priv->resize_widgets, i);
if (w == widget)
{
g_ptr_array_remove_index (priv->resize_widgets, i);
i --;
}
}
for (i = 0; i < priv->allocate_widgets->len; i ++)
{
GtkWidget *w = g_ptr_array_index (priv->allocate_widgets, i);
if (w == widget)
{
g_ptr_array_remove_index (priv->allocate_widgets, i);
i --;
}
}
if (priv->resize_widgets->len == 0 &&
priv->allocate_widgets->len == 0)
_gtk_container_stop_idle_sizer (GTK_CONTAINER (window));
}
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;
}

View File

@@ -164,6 +164,18 @@ void gtk_window_maybe_update_cursor (GtkWindow *window,
GtkWidget *widget,
GdkDevice *device);
void gtk_window_add_resize_widget (GtkWindow *window,
GtkWidget *widget);
void gtk_window_add_allocate_widget (GtkWindow *window,
GtkWidget *widget);
void gtk_window_dequeue_resize_for (GtkWindow *window,
GtkWidget *widget);
GPtrArray *gtk_window_get_resize_widgets (GtkWindow *window);
GPtrArray *gtk_window_get_allocate_widgets (GtkWindow *window);
G_END_DECLS
#endif /* __GTK_WINDOW_PRIVATE_H__ */

View File

@@ -130,6 +130,7 @@ gtk_tests = [
['testoutsetshadowdrawing'],
['testblur'],
['testtexture'],
['testsizeallocate'],
]
if os_linux

184
tests/testsizeallocate.c Normal file
View File

@@ -0,0 +1,184 @@
#include <gtk/gtk.h>
const char *css =
"resizewidget {"
" background-color: yellow;"
"}"
"resizewidget button:first-child {"
" border-top-left-radius: 50%;"
"}"
"resizewidget button:last-child {"
" border-bottom-right-radius: 50%;"
"}";
typedef struct _GtkResizeWidget GtkResizeWidget;
typedef struct _GtkResizeWidgetClass GtkResizeWidgetClass;
#define GTK_TYPE_RESIZE_WIDGET (gtk_resize_widget_get_type ())
#define GTK_RESIZE_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, GTK_TYPE_RESIZE_WIDGET, GtkResizeWidget))
#define GTK_RESIZE_WIDGET_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST(cls, GTK_TYPE_RESIZE_WIDGET, GtkResizeWidgetClass))
#define GTK_IS_RESIZE_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, GTK_TYPE_RESIZE_WIDGET))
#define GTK_IS_RESIZE_WIDGET_CLASS(cls) (G_TYPE_CHECK_CLASS_TYPE(cls, GTK_TYPE_RESIZE_WIDGET))
#define GTK_RESIZE_WIDGET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS(obj, GTK_TYPE_RESIZE_WIDGET, GtkResizeWidgetClass))
struct _GtkResizeWidget
{
GtkWidget parent_instance;
GtkWidget *buttons[2];
};
struct _GtkResizeWidgetClass
{
GtkWidgetClass parent_class;
};
GType gtk_resize_widget_get_type (void) G_GNUC_CONST;
G_DEFINE_TYPE(GtkResizeWidget, gtk_resize_widget, GTK_TYPE_WIDGET)
static void
gtk_resize_widget_measure (GtkWidget *widget,
GtkOrientation orientation,
int for_size,
int *minimum,
int *natural,
int *minimum_baseline,
int *natural_baseline)
{
GtkResizeWidget *self = GTK_RESIZE_WIDGET (widget);
int i;
/* Just add everything up */
*minimum = 0;
*natural = 0;
for (i = 0; i < 2; i ++)
{
int m, n;
gtk_widget_measure (self->buttons[i], orientation, for_size,
&m, &n, minimum_baseline, natural_baseline);
/* Ignore minimum size. You know why. */
if (i == 0)
*minimum += m;
*natural += n;
}
}
static void
gtk_resize_widget_size_allocate (GtkWidget *widget,
const GtkAllocation *allocation,
int baseline,
GtkAllocation *out_clip)
{
static int k;
GtkResizeWidget *self = GTK_RESIZE_WIDGET (widget);
GtkAllocation child_alloc;
GtkAllocation clip;
int m, n;
int remaining_width = allocation->width;
g_message ("%s: %i", __FUNCTION__, k++);
gtk_widget_measure (self->buttons[0], GTK_ORIENTATION_HORIZONTAL, -1,
&m, &n, NULL, NULL);
child_alloc.x = 0;
child_alloc.y = 0;
child_alloc.width = m;
child_alloc.height = allocation->height;
gtk_widget_size_allocate (self->buttons[0], &child_alloc, -1, &clip);
remaining_width -= child_alloc.width;
gtk_widget_measure (self->buttons[1], GTK_ORIENTATION_HORIZONTAL, -1,
&m, &n, NULL, NULL);
if (remaining_width < m)
{
gtk_widget_hide (self->buttons[1]);
}
else
{
gtk_widget_show (self->buttons[1]);
gtk_widget_measure (self->buttons[1], GTK_ORIENTATION_HORIZONTAL, -1,
&m, &n, NULL, NULL);
/* Still? */
if (remaining_width > m)
{
child_alloc.x += child_alloc.width;
child_alloc.width = m;
gtk_widget_size_allocate (self->buttons[1], &child_alloc, -1, &clip);
}
else
{
/* Shame. */
gtk_widget_hide (self->buttons[1]);
}
}
}
static void
gtk_resize_widget_init (GtkResizeWidget *self)
{
gtk_widget_set_has_surface (GTK_WIDGET (self), FALSE);
self->buttons[0] = gtk_button_new_with_label ("First");
self->buttons[1] = gtk_button_new_with_label ("Second");
gtk_widget_set_parent (self->buttons[0], GTK_WIDGET (self));
gtk_widget_set_parent (self->buttons[1], GTK_WIDGET (self));
}
static void
gtk_resize_widget_class_init (GtkResizeWidgetClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
widget_class->measure = gtk_resize_widget_measure;
widget_class->size_allocate = gtk_resize_widget_size_allocate;
gtk_widget_class_set_css_name (widget_class, "resizewidget");
}
int
main()
{
GtkWidget *window;
GtkWidget *widget;
GtkCssProvider *provider;
gtk_init ();
provider = gtk_css_provider_new ();
gtk_css_provider_load_from_data (provider, css, -1);
gtk_style_context_add_provider_for_display (gdk_display_get_default (),
GTK_STYLE_PROVIDER (provider),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
widget = g_object_new (GTK_TYPE_RESIZE_WIDGET, NULL);
gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
gtk_container_add (GTK_CONTAINER (window), widget);
g_signal_connect (window, "close-request", G_CALLBACK (gtk_main_quit), NULL);
gtk_widget_show (window);
gtk_main ();
}