From 8f962381945ee9ef1a22ac6ce2de76a6671040fd Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 3 May 2014 16:43:04 -0400 Subject: [PATCH] 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 | 470 +++++++++++++++--------- 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, 313 insertions(+), 180 deletions(-) diff --git a/modules/other/parasite/inspect-button.c b/modules/other/parasite/inspect-button.c index b875ef6646..225f4c2df5 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" -static void -on_inspect_widget(GtkWidget *grab_window, - GdkEventButton *event, - ParasiteWindow *parasite) +typedef struct { - gdk_device_ungrab (gtk_get_current_event_device (), event->time); - gtk_widget_hide(parasite->highlight_window); - - if (parasite->selected_window != NULL) - { - GtkWidget *toplevel = NULL; - GtkWidget *widget = NULL; - - gdk_window_get_user_data( - gdk_window_get_toplevel(parasite->selected_window), - (gpointer*)&toplevel); - - gdk_window_get_user_data (parasite->selected_window, (gpointer*)&widget); - - if (toplevel) - { - parasite_widget_tree_scan (PARASITE_WIDGET_TREE (parasite->widget_tree), - toplevel); - } - - if (widget) - { - parasite_widget_tree_select_object (PARASITE_WIDGET_TREE (parasite->widget_tree), - G_OBJECT (widget)); - } - } -} + gint x; + gint y; + gboolean found; + gboolean first; + GtkWidget *res_widget; +} FindWidgetData; static void -on_highlight_window_show (GtkWidget *window, ParasiteWindow *parasite) +find_widget (GtkWidget *widget, + FindWidgetData *data) { - if (gtk_widget_is_composited (parasite->window)) - { - gtk_widget_set_opacity (parasite->highlight_window, 0.2); - } - else - { - /* - * TODO: Do something different when there's no compositing manager. - * Draw a border or something. - */ - } -} + GtkAllocation new_allocation; + gint x_offset = 0; + gint y_offset = 0; -static void -ensure_highlight_window (ParasiteWindow *parasite) -{ - GdkRGBA color; + gtk_widget_get_allocation (widget, &new_allocation); - if (parasite->highlight_window != NULL) + if (data->found || !gtk_widget_get_mapped (widget)) return; - color.red = 0; - color.green = 0; - color.blue = 65535; - color.alpha = 1; + /* 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; + } - parasite->highlight_window = gtk_window_new (GTK_WINDOW_POPUP); - gtk_widget_override_background_color (parasite->highlight_window, - GTK_STATE_NORMAL, - &color); + 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))) + { + gint tx, ty, twidth, theight; - g_signal_connect (parasite->highlight_window, "show", G_CALLBACK (on_highlight_window_show), parasite); + 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 ((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)) + { + 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 GtkWidget * +find_widget_at_pointer (GdkDevice *device) +{ + 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) + { + gpointer widget_ptr; + + gdk_window_get_user_data (pointer_window, &widget_ptr); + widget = widget_ptr; + } + + if (widget) + { + 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 +clear_flash (ParasiteWindow *parasite) +{ + 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; + } } static void -on_highlight_widget(GtkWidget *grab_window, - GdkEventMotion *event, - ParasiteWindow *parasite) +start_flash (ParasiteWindow *parasite, + GtkWidget *widget) { - GdkWindow *selected_window; - gint x, y, width, height; + parasite->flash_count = 1; + parasite->flash_widget = widget; + g_signal_connect_after (widget, "draw", G_CALLBACK (draw_flash), parasite); + gtk_widget_queue_draw (widget); +} - ensure_highlight_window (parasite); +static void +on_inspect_widget (GtkWidget *button, + GdkEvent *event, + ParasiteWindow *parasite) +{ + GtkWidget *widget; - gtk_widget_hide (parasite->highlight_window); + clear_flash (parasite); - selected_window = gdk_device_get_window_at_position (gtk_get_current_event_device (), - NULL, - NULL); - if (selected_window == NULL) + widget = find_widget_at_pointer (gdk_event_get_device (event)); + + if (widget == NULL) + return; + + parasite->selected_widget = widget; + + parasite_widget_tree_scan (PARASITE_WIDGET_TREE (parasite->widget_tree), + gtk_widget_get_toplevel (widget)); + + parasite_widget_tree_select_object (PARASITE_WIDGET_TREE (parasite->widget_tree), + G_OBJECT (widget)); +} + +static void +on_highlight_widget (GtkWidget *button, + GdkEvent *event, + ParasiteWindow *parasite) +{ + GtkWidget *widget; + + widget = find_widget_at_pointer (gdk_event_get_device (event)); + + 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; - - gdk_window_get_origin (selected_window, &x, &y); - width = gdk_window_get_width (selected_window); - height = gdk_window_get_height (selected_window); - - 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 void -on_inspect_button_release (GtkWidget *button, - GdkEventButton *event, - ParasiteWindow *parasite) -{ - GdkCursor *cursor; - GdkEventMask events; - - events = GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK; - - if (parasite->grab_window == NULL) + if (parasite->flash_widget == widget) { - 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); + /* Already selected */ + return; } - 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_object_unref (cursor); -} - - -GtkWidget * -gtkparasite_inspect_button_new(ParasiteWindow *parasite) -{ - 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); - - return button; + clear_flash (parasite); + start_flash (parasite, widget); } static gboolean -on_flash_timeout(ParasiteWindow *parasite) +property_query_event (GtkWidget *widget, + GdkEvent *event, + gpointer data) { - parasite->flash_count++; - - if (parasite->flash_count == 8) + if (event->type == GDK_BUTTON_RELEASE) { - parasite->flash_cnx = 0; - return FALSE; + 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); } - if (parasite->flash_count % 2 == 0) + return FALSE; +} + +static void +on_inspect (GtkWidget *button, + ParasiteWindow *parasite) +{ + GdkDisplay *display; + GdkDevice *device; + GdkCursor *cursor; + + 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) +{ + GtkWidget *button; + + button = gtk_button_new_with_label ("Inspect"); + g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (on_inspect), parasite); + + return button; +} + +static gboolean +draw_flash (GtkWidget *widget, + cairo_t *cr, + ParasiteWindow *parasite) +{ + GtkAllocation alloc; + + if (parasite->flash_count % 2 == 0) + return FALSE; + + if (GTK_IS_WINDOW (widget)) { - if (gtk_widget_get_visible(parasite->highlight_window)) - gtk_widget_hide(parasite->highlight_window); - else - gtk_widget_show(parasite->highlight_window); + /* 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); + } + else + { + 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; +} + +static gboolean +on_flash_timeout (ParasiteWindow *parasite) +{ + gtk_widget_queue_draw (parasite->flash_widget); + + parasite->flash_count++; + + 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; + + return G_SOURCE_REMOVE; + } + + return G_SOURCE_CONTINUE; } void -gtkparasite_flash_widget(ParasiteWindow *parasite, GtkWidget *widget) +gtkparasite_flash_widget (ParasiteWindow *parasite, + GtkWidget *widget) { - gint x, y, width, height; - GdkWindow *parent_window; + if (parasite->flash_cnx != 0) + return; - if (!gtk_widget_get_visible(widget) || !gtk_widget_get_mapped(widget)) - return; + if (!gtk_widget_get_visible (widget) || !gtk_widget_get_mapped (widget)) + return; - ensure_highlight_window(parasite); - - 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; - - width = alloc.width; - height = alloc.height; - - 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); - - if (parasite->flash_cnx != 0) - g_source_remove(parasite->flash_cnx); - - 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 cc69bf2870..140ad4ea6b 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 8d042f14ce..b5370d8e79 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 efc75b5750..23439882f7 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 fd7dc870e8..8edcc4ba1d 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);