From 9a21ff3cd23d7a4e0feccf3aaa001fa40505e1b3 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 13 Mar 2016 00:11:52 -0500 Subject: [PATCH] stack switcher: Support switching during DND GtkNotebook will switch pages if you hover over a tab during DND. The same makes sense in GtkStackSwitcher, so implement it here. --- gtk/gtkstackswitcher.c | 110 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/gtk/gtkstackswitcher.c b/gtk/gtkstackswitcher.c index 6d4fe1cdea..952ff2a624 100644 --- a/gtk/gtkstackswitcher.c +++ b/gtk/gtkstackswitcher.c @@ -21,6 +21,7 @@ #include "gtkstackswitcher.h" #include "gtkradiobutton.h" #include "gtklabel.h" +#include "gtkdnd.h" #include "gtkorientable.h" #include "gtkprivate.h" #include "gtkintl.h" @@ -53,6 +54,8 @@ * stack pages. */ +#define TIMEOUT_EXPAND 500 + typedef struct _GtkStackSwitcherPrivate GtkStackSwitcherPrivate; struct _GtkStackSwitcherPrivate { @@ -60,6 +63,8 @@ struct _GtkStackSwitcherPrivate GHashTable *buttons; gint icon_size; gboolean in_child_changed; + GtkWidget *switch_button; + guint switch_timer; }; enum { @@ -76,6 +81,8 @@ gtk_stack_switcher_init (GtkStackSwitcher *switcher) GtkStyleContext *context; GtkStackSwitcherPrivate *priv; + gtk_widget_set_has_window (GTK_WIDGET (switcher), FALSE); + priv = gtk_stack_switcher_get_instance_private (switcher); priv->icon_size = GTK_ICON_SIZE_MENU; @@ -87,6 +94,9 @@ gtk_stack_switcher_init (GtkStackSwitcher *switcher) gtk_style_context_add_class (context, GTK_STYLE_CLASS_LINKED); gtk_orientable_set_orientation (GTK_ORIENTABLE (switcher), GTK_ORIENTATION_HORIZONTAL); + + gtk_drag_dest_set (GTK_WIDGET (switcher), 0, NULL, 0, 0); + gtk_drag_dest_set_track_motion (GTK_WIDGET (switcher), TRUE); } static void @@ -241,6 +251,102 @@ on_needs_attention_updated (GtkWidget *widget, update_button (self, widget, button); } +static void +remove_switch_timer (GtkStackSwitcher *self) +{ + GtkStackSwitcherPrivate *priv; + + priv = gtk_stack_switcher_get_instance_private (self); + + if (priv->switch_timer) + { + g_source_remove (priv->switch_timer); + priv->switch_timer = 0; + } +} + +static gboolean +gtk_stack_switcher_switch_timeout (gpointer data) +{ + GtkStackSwitcher *self = data; + GtkStackSwitcherPrivate *priv; + GtkWidget *button; + + priv = gtk_stack_switcher_get_instance_private (self); + + priv->switch_timer = 0; + + button = priv->switch_button; + priv->switch_button = NULL; + + if (button) + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE); + + return G_SOURCE_REMOVE; +} + +static gboolean +gtk_stack_switcher_drag_motion (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + guint time) +{ + GtkStackSwitcher *self = GTK_STACK_SWITCHER (widget); + GtkStackSwitcherPrivate *priv; + GtkAllocation allocation; + GtkWidget *button; + GHashTableIter iter; + gpointer value; + gboolean retval = FALSE; + + gtk_widget_get_allocation (widget, &allocation); + + priv = gtk_stack_switcher_get_instance_private (self); + + x += allocation.x; + y += allocation.y; + + button = NULL; + g_hash_table_iter_init (&iter, priv->buttons); + while (g_hash_table_iter_next (&iter, NULL, &value)) + { + gtk_widget_get_allocation (GTK_WIDGET (value), &allocation); + if (x >= allocation.x && x <= allocation.x + allocation.width && + y >= allocation.y && y <= allocation.y + allocation.height) + { + button = GTK_WIDGET (value); + retval = TRUE; + break; + } + } + + if (button != priv->switch_button) + remove_switch_timer (self); + + priv->switch_button = button; + + if (button && !priv->switch_timer) + { + priv->switch_timer = gdk_threads_add_timeout (TIMEOUT_EXPAND, + gtk_stack_switcher_switch_timeout, + self); + g_source_set_name_by_id (priv->switch_timer, "[gtk+] gtk_stack_switcher_switch_timeout"); + } + + return retval; +} + +static void +gtk_stack_switcher_drag_leave (GtkWidget *widget, + GdkDragContext *context, + guint time) +{ + GtkStackSwitcher *self = GTK_STACK_SWITCHER (widget); + + remove_switch_timer (self); +} + static void add_child (GtkWidget *widget, GtkStackSwitcher *self) @@ -252,6 +358,7 @@ add_child (GtkWidget *widget, priv = gtk_stack_switcher_get_instance_private (self); button = gtk_radio_button_new (NULL); + gtk_widget_set_focus_on_click (button, FALSE); gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (button), FALSE); @@ -530,6 +637,7 @@ gtk_stack_switcher_dispose (GObject *object) { GtkStackSwitcher *switcher = GTK_STACK_SWITCHER (object); + remove_switch_timer (switcher); gtk_stack_switcher_set_stack (switcher, NULL); G_OBJECT_CLASS (gtk_stack_switcher_parent_class)->dispose (object); @@ -559,6 +667,8 @@ gtk_stack_switcher_class_init (GtkStackSwitcherClass *class) object_class->dispose = gtk_stack_switcher_dispose; object_class->finalize = gtk_stack_switcher_finalize; + widget_class->drag_motion = gtk_stack_switcher_drag_motion; + widget_class->drag_leave = gtk_stack_switcher_drag_leave; /** * GtkStackSwitcher:icon-size: *