diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index 364eb046f8..55849fb28c 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -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)); + GtkWidget *parent; + GtkAllocation alloc; - if (cairo_region_is_empty (region)) - return; + g_assert (gtk_widget_get_has_window (widget)); - /* Just return if the widget isn't mapped */ - if (!_gtk_widget_get_mapped (widget)) - return; + /* 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); - if (!GTK_IS_WINDOW (widget)) - { - GtkWidget *toplevel; - GtkAllocation alloc; - int x, y; - cairo_region_t *region2; + g_assert (GTK_IS_WINDOW (parent) || GTK_IS_POPOVER (parent)); - toplevel = gtk_widget_get_toplevel (widget); - if (!GTK_IS_WINDOW (toplevel)) - return; - - gtk_widget_get_allocation (widget, &alloc); - - gtk_widget_translate_coordinates (gtk_widget_get_parent (widget), - toplevel, - 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); - } + gtk_widget_get_own_allocation (widget, &alloc); + gtk_widget_translate_coordinates (widget, + parent, + alloc.x, alloc.y, + &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 diff --git a/gtk/gtkwidgetprivate.h b/gtk/gtkwidgetprivate.h index 863405f489..8f0a96b5ae 100644 --- a/gtk/gtkwidgetprivate.h +++ b/gtk/gtkwidgetprivate.h @@ -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); diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c index 055910971b..71283398be 100644 --- a/gtk/gtkwindow.c +++ b/gtk/gtkwindow.c @@ -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); } /**