[gtk+/wip/baedert/drawing] widget: Fix drawing invalidation with windowed widgets



commit f18abda585e8894df521d86813ef33ec636c242a
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 558712f..e4fe1ba 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -4916,76 +4916,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;
 }
 
 /**
@@ -5305,6 +5266,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]