[gtk+/wip/baedert/drawing: 276/348] widget: Fix drawing invalidation with windowed widgets
- From: Timm Bäder <baedert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+/wip/baedert/drawing: 276/348] widget: Fix drawing invalidation with windowed widgets
- Date: Sat, 8 Jul 2017 10:54:01 +0000 (UTC)
commit 6e34bcfbd5e66ea3221a2625fe337b30df37b959
Author: Timm Bäder <mail baedert org>
Date: Wed Jun 21 12:15:47 2017 +0200
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.
gtk/gtkwidget.c | 171 +++++++++++++++++++++++++++++++-----------------
gtk/gtkwidgetprivate.h | 4 +
gtk/gtkwindow.c | 13 +++-
3 files changed, 123 insertions(+), 65 deletions(-)
---
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index 364eb04..55849fb 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));
-
- 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;
- GtkAllocation alloc;
- int x, y;
- cairo_region_t *region2;
+ GtkWidget *parent;
+ GtkAllocation alloc;
- 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,
- alloc.x, alloc.y,
- &x, &y);
+ g_assert (GTK_IS_WINDOW (parent) || GTK_IS_POPOVER (parent));
- 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;
}
/**
@@ -5301,6 +5262,94 @@ get_box_padding (GtkCssStyle *style,
}
/**
+ * 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
* @allocation: position and size to be allocated to @widget
diff --git a/gtk/gtkwidgetprivate.h b/gtk/gtkwidgetprivate.h
index 863405f..8f0a96b 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 adb8165..c0198cf 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;
@@ -7824,7 +7829,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);
}
/**
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]