[gtk+] parasite: Make flashing work better



commit 8f962381945ee9ef1a22ac6ce2de76a6671040fd
Author: Matthias Clasen <mclasen redhat com>
Date:   Sat May 3 16:43:04 2014 -0400

    parasite: Make flashing work better
    
    The positioning of the highlight window was not reliable; instead
    just use a after-handler for the draw signal, in the same way that
    drag highlights are drawn by GTK+ itself.
    
    And copy the code for grabbing a widget via pointer from testgtk;
    that code is known to work.

 modules/other/parasite/inspect-button.c |  404 ++++++++++++++++++++-----------
 modules/other/parasite/parasite.h       |    6 +-
 modules/other/parasite/prop-list.c      |   11 +-
 modules/other/parasite/prop-list.h      |    2 +-
 modules/other/parasite/window.c         |    4 +-
 5 files changed, 280 insertions(+), 147 deletions(-)
---
diff --git a/modules/other/parasite/inspect-button.c b/modules/other/parasite/inspect-button.c
index b875ef6..225f4c2 100644
--- a/modules/other/parasite/inspect-button.c
+++ b/modules/other/parasite/inspect-button.c
@@ -20,223 +20,351 @@
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  * THE SOFTWARE.
  */
-
 #include "parasite.h"
 #include "widget-tree.h"
 
+typedef struct
+{
+  gint x;
+  gint y;
+  gboolean found;
+  gboolean first;
+  GtkWidget *res_widget;
+} FindWidgetData;
+
 static void
-on_inspect_widget(GtkWidget *grab_window,
-                  GdkEventButton *event,
-                  ParasiteWindow *parasite)
+find_widget (GtkWidget      *widget,
+             FindWidgetData *data)
 {
-  gdk_device_ungrab (gtk_get_current_event_device (), event->time);
-  gtk_widget_hide(parasite->highlight_window);
+  GtkAllocation new_allocation;
+  gint x_offset = 0;
+  gint y_offset = 0;
 
-  if (parasite->selected_window != NULL)
-    {
-      GtkWidget *toplevel = NULL;
-      GtkWidget *widget = NULL;
+  gtk_widget_get_allocation (widget, &new_allocation);
 
-      gdk_window_get_user_data(
-            gdk_window_get_toplevel(parasite->selected_window),
-            (gpointer*)&toplevel);
+  if (data->found || !gtk_widget_get_mapped (widget))
+    return;
 
-      gdk_window_get_user_data (parasite->selected_window, (gpointer*)&widget);
+  /* Note that in the following code, we only count the
+   * position as being inside a WINDOW widget if it is inside
+   * widget->window; points that are outside of widget->window
+   * but within the allocation are not counted. This is consistent
+   * with the way we highlight drag targets.
+   */
+  if (gtk_widget_get_has_window (widget))
+    {
+      new_allocation.x = 0;
+      new_allocation.y = 0;
+    }
 
-      if (toplevel)
+  if (gtk_widget_get_parent (widget) && !data->first)
+    {
+      GdkWindow *window = gtk_widget_get_window (widget);
+      while (window != gtk_widget_get_window (gtk_widget_get_parent (widget)))
         {
-          parasite_widget_tree_scan (PARASITE_WIDGET_TREE (parasite->widget_tree),
-                                     toplevel);
+          gint tx, ty, twidth, theight;
+
+          twidth = gdk_window_get_width (window);
+          theight = gdk_window_get_height (window);
+
+          if (new_allocation.x < 0)
+            {
+              new_allocation.width += new_allocation.x;
+              new_allocation.x = 0;
+            }
+          if (new_allocation.y < 0)
+            {
+              new_allocation.height += new_allocation.y;
+              new_allocation.y = 0;
+            }
+          if (new_allocation.x + new_allocation.width > twidth)
+            new_allocation.width = twidth - new_allocation.x;
+          if (new_allocation.y + new_allocation.height > theight)
+            new_allocation.height = theight - new_allocation.y;
+
+          gdk_window_get_position (window, &tx, &ty);
+          new_allocation.x += tx;
+          x_offset += tx;
+          new_allocation.y += ty;
+          y_offset += ty;
+
+          window = gdk_window_get_parent (window);
         }
+    }
 
-      if (widget)
+  if ((data->x >= new_allocation.x) && (data->y >= new_allocation.y) &&
+      (data->x < new_allocation.x + new_allocation.width) &&
+      (data->y < new_allocation.y + new_allocation.height))
+    {
+      /* First, check if the drag is in a valid drop site in
+       * one of our children 
+       */
+      if (GTK_IS_CONTAINER (widget))
         {
-          parasite_widget_tree_select_object (PARASITE_WIDGET_TREE (parasite->widget_tree),
-                                              G_OBJECT (widget));
+          FindWidgetData new_data = *data;
+
+          new_data.x -= x_offset;
+          new_data.y -= y_offset;
+          new_data.found = FALSE;
+          new_data.first = FALSE;
+
+          gtk_container_forall (GTK_CONTAINER (widget),
+                                (GtkCallback)find_widget,
+                                &new_data);
+
+          data->found = new_data.found;
+          if (data->found)
+            data->res_widget = new_data.res_widget;
+        }
+
+      /* If not, and this widget is registered as a drop site, check to
+       * emit "drag_motion" to check if we are actually in
+       * a drop site.
+       */
+      if (!data->found)
+        {
+          data->found = TRUE;
+          data->res_widget = widget;
         }
     }
 }
 
-static void
-on_highlight_window_show (GtkWidget *window, ParasiteWindow *parasite)
+static GtkWidget *
+find_widget_at_pointer (GdkDevice *device)
 {
-  if (gtk_widget_is_composited (parasite->window))
+  GtkWidget *widget = NULL;
+  GdkWindow *pointer_window;
+  gint x, y;
+  FindWidgetData data;
+
+  pointer_window = gdk_device_get_window_at_position (device, NULL, NULL);
+
+  if (pointer_window)
     {
-      gtk_widget_set_opacity (parasite->highlight_window, 0.2);
+      gpointer widget_ptr;
+
+      gdk_window_get_user_data (pointer_window, &widget_ptr);
+      widget = widget_ptr;
     }
-  else
+
+  if (widget)
     {
-      /*
-       * TODO: Do something different when there's no compositing manager.
-       *         Draw a border or something.
-       */
+      gdk_window_get_device_position (gtk_widget_get_window (widget),
+                                      device, &x, &y, NULL);
+
+      data.x = x;
+      data.y = y;
+      data.found = FALSE;
+      data.first = TRUE;
+
+      find_widget (widget, &data);
+      if (data.found)
+        return data.res_widget;
+
+      return widget;
     }
+
+  return NULL;
 }
 
+static gboolean draw_flash (GtkWidget      *widget,
+                            cairo_t        *cr,
+                            ParasiteWindow *parasite);
+
 static void
-ensure_highlight_window (ParasiteWindow *parasite)
+clear_flash (ParasiteWindow *parasite)
 {
-  GdkRGBA color;
+  if (parasite->flash_widget)
+    {
+      gtk_widget_queue_draw (parasite->flash_widget);
+      g_signal_handlers_disconnect_by_func (parasite->flash_widget, draw_flash, parasite);
+      parasite->flash_widget = NULL;
+    }
+}
 
-  if (parasite->highlight_window != NULL)
+static void
+start_flash (ParasiteWindow *parasite,
+             GtkWidget      *widget)
+{
+  parasite->flash_count = 1;
+  parasite->flash_widget = widget;
+  g_signal_connect_after (widget, "draw", G_CALLBACK (draw_flash), parasite);
+  gtk_widget_queue_draw (widget);
+}
+
+static void
+on_inspect_widget (GtkWidget      *button,
+                   GdkEvent       *event,
+                   ParasiteWindow *parasite)
+{
+  GtkWidget *widget;
+
+  clear_flash (parasite);
+
+  widget = find_widget_at_pointer (gdk_event_get_device (event));
+
+  if (widget == NULL)
     return;
 
-  color.red = 0;
-  color.green = 0;
-  color.blue = 65535;
-  color.alpha = 1;
+  parasite->selected_widget = widget;
 
-  parasite->highlight_window = gtk_window_new (GTK_WINDOW_POPUP);
-  gtk_widget_override_background_color (parasite->highlight_window,
-                                        GTK_STATE_NORMAL,
-                                        &color);
+  parasite_widget_tree_scan (PARASITE_WIDGET_TREE (parasite->widget_tree),
+                             gtk_widget_get_toplevel (widget));
 
-  g_signal_connect (parasite->highlight_window, "show", G_CALLBACK (on_highlight_window_show), parasite);
+  parasite_widget_tree_select_object (PARASITE_WIDGET_TREE (parasite->widget_tree),
+                                      G_OBJECT (widget));
 }
 
 static void
-on_highlight_widget(GtkWidget *grab_window,
-                    GdkEventMotion *event,
-                    ParasiteWindow *parasite)
+on_highlight_widget (GtkWidget      *button,
+                     GdkEvent       *event,
+                     ParasiteWindow *parasite)
 {
-  GdkWindow *selected_window;
-  gint x, y, width, height;
-
-  ensure_highlight_window (parasite);
+  GtkWidget *widget;
 
-  gtk_widget_hide (parasite->highlight_window);
+  widget = find_widget_at_pointer (gdk_event_get_device (event));
 
-  selected_window = gdk_device_get_window_at_position (gtk_get_current_event_device (),
-                                                       NULL,
-                                                       NULL);
-  if (selected_window == NULL)
+  if (widget == NULL)
     {
       /* This window isn't in-process. Ignore it. */
-      parasite->selected_window = NULL;
       return;
     }
 
-  if (gdk_window_get_toplevel(selected_window) == gtk_widget_get_window(parasite->window))
+  if (gtk_widget_get_toplevel (widget) == parasite->window)
     {
-       /* Don't hilight things in the parasite window */
-        parasite->selected_window = NULL;
-        return;
+      /* Don't hilight things in the parasite window */
+      return;
     }
 
-  parasite->selected_window = selected_window;
+  if (parasite->flash_widget == widget)
+    {
+      /* Already selected */
+      return;
+    }
 
-  gdk_window_get_origin (selected_window, &x, &y);
-  width = gdk_window_get_width (selected_window);
-  height = gdk_window_get_height (selected_window);
+  clear_flash (parasite);
+  start_flash (parasite, widget);
+}
 
-  gtk_window_move (GTK_WINDOW (parasite->highlight_window), x, y);
-  gtk_window_resize (GTK_WINDOW (parasite->highlight_window), width, height);
-  gtk_widget_show (parasite->highlight_window);
+static gboolean
+property_query_event (GtkWidget *widget,
+                      GdkEvent  *event,
+                      gpointer   data)
+{
+  if (event->type == GDK_BUTTON_RELEASE)
+    {
+      g_signal_handlers_disconnect_by_func (widget, property_query_event, data);
+      gtk_grab_remove (widget);
+      gdk_device_ungrab (gdk_event_get_device (event), GDK_CURRENT_TIME);
+      on_inspect_widget (widget, event, data);
+    }
+  else if (event->type == GDK_MOTION_NOTIFY)
+    {
+      on_highlight_widget (widget, event, data);
+    }
+
+  return FALSE;
 }
 
 static void
-on_inspect_button_release (GtkWidget *button,
-                           GdkEventButton *event,
-                           ParasiteWindow *parasite)
+on_inspect (GtkWidget      *button,
+            ParasiteWindow *parasite)
 {
+  GdkDisplay *display;
+  GdkDevice *device;
   GdkCursor *cursor;
-  GdkEventMask events;
 
-  events = GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK;
-
-  if (parasite->grab_window == NULL)
-    {
-      parasite->grab_window = gtk_window_new (GTK_WINDOW_POPUP);
-      gtk_widget_show(parasite->grab_window);
-      gtk_window_resize (GTK_WINDOW (parasite->grab_window), 1, 1);
-      gtk_window_move (GTK_WINDOW (parasite->grab_window), -100, -100);
-      gtk_widget_add_events (parasite->grab_window, events);
-
-      g_signal_connect (parasite->grab_window, "button_release_event", G_CALLBACK (on_inspect_widget), 
parasite);
-      g_signal_connect (parasite->grab_window, "motion_notify_event", G_CALLBACK(on_highlight_widget), 
parasite);
-    }
-
-  cursor = gdk_cursor_new_for_display (gtk_widget_get_display (button), GDK_CROSSHAIR);
-  gdk_device_grab (gtk_get_current_event_device (),
-                   gtk_widget_get_window (parasite->grab_window),
-                   GDK_OWNERSHIP_WINDOW,
-                   FALSE,
-                   events,
-                   cursor,
-                   event->time);
+  g_signal_connect (button, "event",
+                    G_CALLBACK (property_query_event), parasite);
+
+  display = gtk_widget_get_display (button);
+  cursor = gdk_cursor_new_for_display (display, GDK_CROSSHAIR);
+  device = gdk_device_manager_get_client_pointer (gdk_display_get_device_manager (display));
+  gdk_device_grab (device,
+                   gtk_widget_get_window (GTK_WIDGET (button)),
+                   GDK_OWNERSHIP_NONE, TRUE,
+                   GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK,
+                   cursor, GDK_CURRENT_TIME);
   g_object_unref (cursor);
+  gtk_grab_add (GTK_WIDGET (button));
 }
 
-
 GtkWidget *
-gtkparasite_inspect_button_new(ParasiteWindow *parasite)
+gtkparasite_inspect_button_new (ParasiteWindow *parasite)
 {
-    GtkWidget *button;
+  GtkWidget *button;
 
-    button = gtk_button_new_from_icon_name ("find", GTK_ICON_SIZE_BUTTON);
-    gtk_widget_set_tooltip_text (button, "Inspect");
-    g_signal_connect(G_OBJECT(button), "button_release_event",
-                     G_CALLBACK(on_inspect_button_release), parasite);
+  button = gtk_button_new_with_label ("Inspect");
+  g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (on_inspect), parasite);
 
-    return button;
+  return button;
 }
 
 static gboolean
-on_flash_timeout(ParasiteWindow *parasite)
+draw_flash (GtkWidget *widget,
+            cairo_t   *cr,
+            ParasiteWindow *parasite)
 {
-    parasite->flash_count++;
+  GtkAllocation alloc;
 
-    if (parasite->flash_count == 8)
+  if (parasite->flash_count % 2 == 0)
+    return FALSE;
+
+  if (GTK_IS_WINDOW (widget))
     {
-        parasite->flash_cnx = 0;
-        return FALSE;
+      /* We don't want to draw the drag highlight around the
+       * CSD window decorations
+       */
+      gtk_widget_get_allocation (gtk_bin_get_child (GTK_BIN (widget)), &alloc);
     }
-
-    if (parasite->flash_count % 2 == 0)
+  else
     {
-        if (gtk_widget_get_visible(parasite->highlight_window))
-            gtk_widget_hide(parasite->highlight_window);
-        else
-            gtk_widget_show(parasite->highlight_window);
+      alloc.x = 0;
+      alloc.y = 0;
+      alloc.width = gtk_widget_get_allocated_width (widget);
+      alloc.height = gtk_widget_get_allocated_height (widget);
     }
 
-    return TRUE;
+  cairo_set_source_rgba (cr, 0.0, 0.0, 1.0, 0.2);
+  cairo_rectangle (cr,
+                   alloc.x + 0.5, alloc.y + 0.5,
+                   alloc.width - 1, alloc.height - 1);
+  cairo_fill (cr);
+
+  return FALSE;
 }
 
-void
-gtkparasite_flash_widget(ParasiteWindow *parasite, GtkWidget *widget)
+static gboolean
+on_flash_timeout (ParasiteWindow *parasite)
 {
-    gint x, y, width, height;
-    GdkWindow *parent_window;
+  gtk_widget_queue_draw (parasite->flash_widget);
 
-    if (!gtk_widget_get_visible(widget) || !gtk_widget_get_mapped(widget))
-        return;
+  parasite->flash_count++;
 
-    ensure_highlight_window(parasite);
+  if (parasite->flash_count == 6)
+    {
+      g_signal_handlers_disconnect_by_func (parasite->flash_widget, draw_flash, parasite);
+      parasite->flash_widget = NULL;
+      parasite->flash_cnx = 0;
 
-    parent_window = gtk_widget_get_parent_window(widget);
-    if (parent_window != NULL) {
-       GtkAllocation alloc;
-       gtk_widget_get_allocation(widget, &alloc);
-       
-        gdk_window_get_origin(parent_window, &x, &y);
-        x += alloc.x;
-        y += alloc.y;
+      return G_SOURCE_REMOVE;
+    }
 
-        width = alloc.width;
-        height = alloc.height;
+  return G_SOURCE_CONTINUE;
+}
 
-        gtk_window_move(GTK_WINDOW(parasite->highlight_window), x, y);
-        gtk_window_resize(GTK_WINDOW(parasite->highlight_window), width, height);
-        gtk_widget_show(parasite->highlight_window);
+void
+gtkparasite_flash_widget (ParasiteWindow *parasite,
+                          GtkWidget      *widget)
+{
+  if (parasite->flash_cnx != 0)
+    return;
 
-        if (parasite->flash_cnx != 0)
-            g_source_remove(parasite->flash_cnx);
+  if (!gtk_widget_get_visible (widget) || !gtk_widget_get_mapped (widget))
+    return;
 
-        parasite->flash_count = 0;
-        parasite->flash_cnx = g_timeout_add(150, (GSourceFunc)on_flash_timeout,
-                                            parasite);
-    }
+  start_flash (parasite, widget);
+  parasite->flash_cnx = g_timeout_add (150, (GSourceFunc) on_flash_timeout, parasite);
 }
 
-// vim: set et sw=4 ts=4:
+/* vim: set et sw=2 ts=2: */
diff --git a/modules/other/parasite/parasite.h b/modules/other/parasite/parasite.h
index cc69bf2..140ad4e 100644
--- a/modules/other/parasite/parasite.h
+++ b/modules/other/parasite/parasite.h
@@ -43,12 +43,10 @@ typedef struct
     GtkWidget *widget_css_editor;
     GtkWidget *oh;
 
-    GtkWidget *grab_window;
-    GtkWidget *highlight_window;
-
     GtkWidget *widget_popup;
 
-    GdkWindow *selected_window;
+    GtkWidget *selected_widget;
+    GtkWidget *flash_widget;
 
     int flash_count;
     int flash_cnx;
diff --git a/modules/other/parasite/prop-list.c b/modules/other/parasite/prop-list.c
index 8d042f1..b5370d8 100644
--- a/modules/other/parasite/prop-list.c
+++ b/modules/other/parasite/prop-list.c
@@ -338,7 +338,7 @@ parasite_proplist_new (GtkWidget *widget_tree,
                          NULL);
 }
 
-void
+gboolean
 parasite_proplist_set_object (ParasitePropList* pl, GObject *object)
 {
   GtkTreeIter iter;
@@ -347,6 +347,9 @@ parasite_proplist_set_object (ParasitePropList* pl, GObject *object)
   guint i;
   GList *l;
 
+  if (pl->priv->object == object)
+    return FALSE;
+
   pl->priv->object = object;
 
   for (l = pl->priv->signal_cnxs; l != NULL; l = l->next)
@@ -369,11 +372,11 @@ parasite_proplist_set_object (ParasitePropList* pl, GObject *object)
       GtkWidget *parent;
 
       if (!GTK_IS_WIDGET (object))
-        return;
+        return TRUE;
 
       parent = gtk_widget_get_parent (GTK_WIDGET (object));
       if (!parent)
-        return;
+        return TRUE;
 
       props = gtk_container_class_list_child_properties (G_OBJECT_GET_CLASS (parent), &num_properties);
     }
@@ -409,6 +412,8 @@ parasite_proplist_set_object (ParasitePropList* pl, GObject *object)
 
         g_free (signal_name);
     }
+
+  return TRUE;
 }
 
 
diff --git a/modules/other/parasite/prop-list.h b/modules/other/parasite/prop-list.h
index efc75b5..2343988 100644
--- a/modules/other/parasite/prop-list.h
+++ b/modules/other/parasite/prop-list.h
@@ -51,7 +51,7 @@ G_BEGIN_DECLS
 GType      parasite_proplist_get_type   (void);
 GtkWidget *parasite_proplist_new        (GtkWidget *widget_tree,
                                          gboolean   child_properties);
-void       parasite_proplist_set_object (ParasitePropList *proplist,
+gboolean   parasite_proplist_set_object (ParasitePropList *proplist,
                                          GObject          *object);
 
 G_END_DECLS
diff --git a/modules/other/parasite/window.c b/modules/other/parasite/window.c
index fd7dc87..8edcc4b 100644
--- a/modules/other/parasite/window.c
+++ b/modules/other/parasite/window.c
@@ -42,7 +42,9 @@ on_widget_tree_selection_changed (ParasiteWidgetTree *widget_tree,
 
   if (selected != NULL)
     {
-      parasite_proplist_set_object (PARASITE_PROPLIST (parasite->prop_list), selected);
+      if (!parasite_proplist_set_object (PARASITE_PROPLIST (parasite->prop_list), selected))
+        return;
+
       parasite_proplist_set_object (PARASITE_PROPLIST (parasite->child_prop_list), selected);
       parasite_objecthierarchy_set_object (PARASITE_OBJECTHIERARCHY (parasite->oh), selected);
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]