widget: Fix drawing invalidation with windowed widgets

E.g. popovers. Find the parent of the given widget with the window and
invalidate the given region in that window.
This commit is contained in:
Timm Bäder 2017-06-21 12:15:47 +02:00 committed by Matthias Clasen
parent bc807789ba
commit 2cbaa998db
3 changed files with 123 additions and 65 deletions

View File

@ -4912,76 +4912,37 @@ static void
gtk_widget_real_queue_draw_region (GtkWidget *widget,
const cairo_region_t *region)
{
GtkWidget *toplevel;
toplevel = gtk_widget_get_toplevel (widget);
if (!GTK_IS_WINDOW (toplevel))
return;
GTK_WIDGET_GET_CLASS (toplevel)->queue_draw_region (toplevel, region);
g_assert (_gtk_widget_get_has_window (widget));
gdk_window_invalidate_region (_gtk_widget_get_window (widget), region, TRUE);
}
/**
* gtk_widget_queue_draw_region:
* @widget: a #GtkWidget
* @region: region to draw
*
* Invalidates the area of @widget defined by @region by notifying
* the parent via its GtkWidgetClass::queue_draw_child() function.
* Once the main loop becomes idle (after the current batch of
* events has been processed, roughly), the window will receive
* expose events for the union of all regions that have been
* invalidated.
*
* Normally you would only use this function in widget
* implementations. You might also use it to schedule a redraw of a
* #GtkDrawingArea or some portion thereof.
*
* Since: 3.0
**/
/*
* Returns the values you're supposed to pass to gdk_window_move_resize
* for a windowed widget.
*/
void
gtk_widget_queue_draw_region (GtkWidget *widget,
const cairo_region_t *region)
gtk_widget_get_window_allocation (GtkWidget *widget,
GtkAllocation *allocation)
{
g_return_if_fail (GTK_IS_WIDGET (widget));
if (cairo_region_is_empty (region))
return;
/* Just return if the widget isn't mapped */
if (!_gtk_widget_get_mapped (widget))
return;
if (!GTK_IS_WINDOW (widget))
{
GtkWidget *toplevel;
GtkWidget *parent;
GtkAllocation alloc;
int x, y;
cairo_region_t *region2;
toplevel = gtk_widget_get_toplevel (widget);
if (!GTK_IS_WINDOW (toplevel))
return;
g_assert (gtk_widget_get_has_window (widget));
gtk_widget_get_allocation (widget, &alloc);
/* Don't consider the parent == widget case here. */
parent = _gtk_widget_get_parent (widget);
while (parent && !_gtk_widget_get_has_window (parent))
parent = _gtk_widget_get_parent (parent);
gtk_widget_translate_coordinates (gtk_widget_get_parent (widget),
toplevel,
g_assert (GTK_IS_WINDOW (parent) || GTK_IS_POPOVER (parent));
gtk_widget_get_own_allocation (widget, &alloc);
gtk_widget_translate_coordinates (widget,
parent,
alloc.x, alloc.y,
&x, &y);
region2 = cairo_region_copy (region);
cairo_region_translate (region2, x - alloc.x, y- alloc.y);
WIDGET_CLASS (widget)->queue_draw_region (toplevel, region2);
cairo_region_destroy (region2);
}
else
{
WIDGET_CLASS (widget)->queue_draw_region (widget, region);
}
&alloc.x, &alloc.y);
*allocation = alloc;
}
/**
@ -5300,6 +5261,94 @@ get_box_padding (GtkCssStyle *style,
border->right = get_number (style, GTK_CSS_PROPERTY_PADDING_RIGHT);
}
/**
* gtk_widget_queue_draw_region:
* @widget: a #GtkWidget
* @region: region to draw
*
* Invalidates the area of @widget defined by @region by notifying
* the parent via its GtkWidgetClass::queue_draw_child() function.
* Once the main loop becomes idle (after the current batch of
* events has been processed, roughly), the window will receive
* expose events for the union of all regions that have been
* invalidated.
*
* Normally you would only use this function in widget
* implementations. You might also use it to schedule a redraw of a
* #GtkDrawingArea or some portion thereof.
*
* Since: 3.0
**/
void
gtk_widget_queue_draw_region (GtkWidget *widget,
const cairo_region_t *region)
{
GtkWidget *parent;
cairo_region_t *region2;
GtkAllocation alloc;
int x, y;
GtkCssStyle *parent_style;
GtkBorder border, padding;
g_return_if_fail (GTK_IS_WIDGET (widget));
if (cairo_region_is_empty (region))
return;
/* Just return if the widget isn't mapped */
if (!_gtk_widget_get_mapped (widget))
return;
if (!_gtk_widget_get_parent (widget))
{
g_assert (_gtk_widget_get_has_window (widget));
region2 = cairo_region_copy (region);
parent = widget;
goto invalidate;
}
/* Look for the parent with a window and invalidate @region in there. */
parent = widget;
while (parent != NULL && !gtk_widget_get_has_window (parent))
parent = _gtk_widget_get_parent (parent);
g_assert (parent != NULL);
/* @region's coordinates are originally relative to @widget's origin */
gtk_widget_get_allocation (widget, &alloc);
if (widget != parent)
gtk_widget_translate_coordinates (_gtk_widget_get_parent (widget),
parent,
0, 0,
&x, &y);
else
x = y = 0;
/* At this point, x and y are relative to the windowed parent's origin,
* but the window of the parent spans over its entire allocation, so we need
* to account for border and padding manually. The values returned from
* gtk_widget_get_window_allocation, which should've been used to size and position
* @parent's window, do not include widget margins nor css margins.
*/
parent_style = gtk_css_node_get_style (parent->priv->cssnode);
get_box_border (parent_style, &border);
get_box_padding (parent_style, &padding);
x += border.left + padding.left;
y += border.top + padding.top;
region2 = cairo_region_copy (region);
cairo_region_translate (region2, x, y);
invalidate:
WIDGET_CLASS (widget)->queue_draw_region (parent, region2);
cairo_region_destroy (region2);
}
/**
* gtk_widget_size_allocate_with_baseline:
* @widget: a #GtkWidget

View File

@ -330,6 +330,10 @@ void gtk_widget_get_content_size (GtkWidget *wi
int *width,
int *height);
void gtk_widget_get_window_allocation (GtkWidget *widget,
GtkAllocation *allocation);
GtkWidget * gtk_widget_common_ancestor (GtkWidget *widget_a,
GtkWidget *widget_b);

View File

@ -760,14 +760,19 @@ gtk_window_pick (GtkWidget *widget,
!gtk_widget_is_drawable (popover->widget))
continue;
popover_get_rect (popover, window, &rect);
gtk_widget_get_outer_allocation (popover->widget, &rect);
if (gdk_rectangle_contains_point (&rect, x, y))
{
if (x_out && y_out)
{
*x_out = x - rect.x;
*y_out = y - rect.y;
int dest_x, dest_y;
gtk_widget_translate_coordinates (widget, popover->widget,
x, y,
&dest_x, &dest_y);
*x_out = dest_x;
*y_out = dest_y;
}
return popover->widget;
@ -7827,7 +7832,7 @@ gtk_window_queue_draw_region (GtkWidget *widget,
{
gtk_debug_updates_add (widget, region);
gdk_window_invalidate_region (_gtk_widget_get_window (widget), region, TRUE);
GTK_WIDGET_CLASS (gtk_window_parent_class)->queue_draw_region (widget, region);
}
/**