Compare commits

...

5 Commits

Author SHA1 Message Date
Carlos Garnacho
880fcf199a scrolledwindow: Enable "hover" mode in scrollbars after a timeout
This way transient mouse movements over scrollbars don't cause flickering,
and more spaced movements have it enabled shortly.
2014-12-01 14:54:14 +01:00
Carlos Garnacho
ebfc5814f0 scrolledwindow: improve v/hscrollbar proximity detection
Only horizontal coordinates are checked for the vertical scrollbar, and
vice versa.
2014-12-01 14:52:42 +01:00
Carlos Garnacho
69c0de7938 scrolledwindow: fade out scrollbars on leave notify 2014-12-01 14:51:38 +01:00
Carlos Garnacho
59482c41df scrolledwindow: make it a windowed widget again
This is so we can detect crossing events on the scrolledwindow widget,
which can be useful for toggling certain behaviors or not depending on
whether the mouse is on the widget.
2014-12-01 14:50:40 +01:00
Carlos Garnacho
479935f441 scrolledwindow: Show scrollbars upon captured motion events
On mice, scrollbars are always triggered temporarily. On proximity to an
scrollbar, it will be switched to full-width.
2014-11-29 17:00:33 +01:00

View File

@@ -154,7 +154,7 @@
#define ANIMATION_DURATION 200
/* Overlay scrollbars */
#define INDICATOR_FADE_OUT_DELAY 1000
#define INDICATOR_FADE_OUT_DELAY 2000
#define INDICATOR_FADE_OUT_DURATION 1000
#define INDICATOR_FADE_OUT_TIME 500
@@ -174,6 +174,7 @@ typedef struct
gint64 start_time;
gint64 end_time;
guint tick_id;
guint over_timeout_id;
} Indicator;
struct _GtkScrolledWindowPrivate
@@ -339,6 +340,10 @@ static void remove_indicator (GtkScrolledWindow *sw,
static void indicator_stop_fade (Indicator *indicator);
static gboolean maybe_hide_indicator (gpointer data);
static void indicator_start_fade (Indicator *indicator,
gdouble pos);
static void indicator_set_over (Indicator *indicator,
gboolean over);
static guint signals[LAST_SIGNAL] = {0};
@@ -377,6 +382,20 @@ add_tab_bindings (GtkBindingSet *binding_set,
GTK_TYPE_DIRECTION_TYPE, direction);
}
static gboolean
gtk_scrolled_window_leave_notify (GtkWidget *widget,
GdkEventCrossing *event)
{
GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW (widget)->priv;
indicator_set_over (&priv->hindicator, FALSE);
indicator_start_fade (&priv->hindicator, 0.0);
indicator_set_over (&priv->vindicator, FALSE);
indicator_start_fade (&priv->vindicator, 0.0);
return GDK_EVENT_PROPAGATE;
}
static void
gtk_scrolled_window_class_init (GtkScrolledWindowClass *class)
{
@@ -405,6 +424,7 @@ gtk_scrolled_window_class_init (GtkScrolledWindowClass *class)
widget_class->grab_notify = gtk_scrolled_window_grab_notify;
widget_class->realize = gtk_scrolled_window_realize;
widget_class->unrealize = gtk_scrolled_window_unrealize;
widget_class->leave_notify_event = gtk_scrolled_window_leave_notify;
container_class->add = gtk_scrolled_window_add;
container_class->remove = gtk_scrolled_window_remove;
@@ -906,6 +926,163 @@ gtk_scrolled_window_check_attach_pan_gesture (GtkScrolledWindow *sw)
gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (priv->pan_gesture), phase);
}
static void
indicator_set_over (Indicator *indicator,
gboolean over)
{
GtkStyleContext *context;
if (indicator->over_timeout_id)
{
g_source_remove (indicator->over_timeout_id);
indicator->over_timeout_id = 0;
}
if (indicator->over == over)
return;
context = gtk_widget_get_style_context (indicator->scrollbar);
indicator->over = over;
if (indicator->over)
gtk_style_context_add_class (context, "hovering");
else
gtk_style_context_remove_class (context, "hovering");
gtk_widget_queue_resize (indicator->scrollbar);
}
static gboolean
event_close_to_indicator (GtkScrolledWindow *sw,
Indicator *indicator,
GdkEvent *event)
{
GtkAllocation alloc, indicator_alloc;
GtkScrolledWindowPrivate *priv;
GtkWidget *event_widget;
gint win_x, win_y;
gdouble x, y;
priv = sw->priv;
event_widget = gtk_get_event_widget (event);
gdk_event_get_coords (event, &x, &y);
gtk_widget_get_allocation (GTK_WIDGET (sw), &alloc);
gtk_widget_get_allocation (indicator->scrollbar, &indicator_alloc);
gdk_window_get_position (indicator->window, &win_x, &win_y);
if (event->any.window == indicator->window ||
event_widget == indicator->scrollbar)
{
gint xcoord = x, ycoord = y;
gtk_widget_translate_coordinates (indicator->scrollbar,
GTK_WIDGET (sw),
xcoord, ycoord,
&xcoord, &ycoord);
x = xcoord;
y = ycoord;
}
if ((indicator == &priv->hindicator &&
y >= win_y - 50 &&
y < win_y + indicator_alloc.height + 50) ||
(indicator == &priv->vindicator &&
x >= win_x - 50 &&
x < win_x + indicator_alloc.width + 50))
return TRUE;
return FALSE;
}
static gboolean
enable_over_timeout_cb (gpointer user_data)
{
Indicator *indicator = user_data;
indicator_set_over (indicator, TRUE);
return G_SOURCE_REMOVE;
}
static gboolean
check_update_scrollbar_proximity (GtkScrolledWindow *sw,
Indicator *indicator,
GdkEvent *event)
{
gboolean indicator_close;
indicator_close = event_close_to_indicator (sw, indicator, event);
if (indicator->over_timeout_id)
{
g_source_remove (indicator->over_timeout_id);
indicator->over_timeout_id = 0;
}
if (indicator_close)
indicator->over_timeout_id = gdk_threads_add_timeout (30, enable_over_timeout_cb, indicator);
else
indicator_set_over (indicator, FALSE);
return indicator_close;
}
static gboolean
captured_event_cb (GtkWidget *widget,
GdkEvent *event)
{
GtkScrolledWindowPrivate *priv;
GtkScrolledWindow *sw;
GdkInputSource input_source;
GdkDevice *source_device;
gboolean indicator_close;
if (event->type != GDK_MOTION_NOTIFY &&
event->type != GDK_LEAVE_NOTIFY)
return GDK_EVENT_PROPAGATE;
sw = GTK_SCROLLED_WINDOW (widget);
priv = sw->priv;
source_device = gdk_event_get_source_device (event);
input_source = gdk_device_get_source (source_device);
if (input_source != GDK_SOURCE_MOUSE &&
input_source != GDK_SOURCE_TOUCHPAD)
return GDK_EVENT_PROPAGATE;
if (event->type == GDK_MOTION_NOTIFY)
{
indicator_start_fade (&priv->hindicator, 1.0);
indicator_start_fade (&priv->vindicator, 1.0);
/* Check whether we're hovering close to the vertical scrollbar */
indicator_close = check_update_scrollbar_proximity (sw, &priv->vindicator,
event);
if (!indicator_close)
{
/* Otherwise check the vertical scrollbar */
check_update_scrollbar_proximity (sw, &priv->hindicator, event);
}
else
indicator_set_over (&priv->hindicator, FALSE);
}
else if (event->type == GDK_LEAVE_NOTIFY &&
event->crossing.mode == GDK_CROSSING_UNGRAB)
{
GtkWidget *scrollbar;
scrollbar = gtk_get_event_widget (event);
if (scrollbar == priv->hindicator.scrollbar)
check_update_scrollbar_proximity (sw, &priv->hindicator, event);
else if (scrollbar == priv->vindicator.scrollbar)
check_update_scrollbar_proximity (sw, &priv->vindicator, event);
}
return GDK_EVENT_PROPAGATE;
}
static void
gtk_scrolled_window_init (GtkScrolledWindow *scrolled_window)
{
@@ -915,7 +1092,7 @@ gtk_scrolled_window_init (GtkScrolledWindow *scrolled_window)
scrolled_window->priv = priv =
gtk_scrolled_window_get_instance_private (scrolled_window);
gtk_widget_set_has_window (widget, FALSE);
gtk_widget_set_has_window (widget, TRUE);
gtk_widget_set_can_focus (widget, TRUE);
/* Instantiated by gtk_scrolled_window_set_[hv]adjustment
@@ -968,6 +1145,8 @@ gtk_scrolled_window_init (GtkScrolledWindow *scrolled_window)
gtk_scrolled_window_set_kinetic_scrolling (scrolled_window, TRUE);
gtk_scrolled_window_set_capture_button_press (scrolled_window, TRUE);
_gtk_widget_set_captured_event_handler (widget, captured_event_cb);
}
/**
@@ -2168,8 +2347,8 @@ gtk_scrolled_window_allocate_child (GtkScrolledWindow *swindow,
gtk_scrolled_window_relative_allocation (widget, relative_allocation);
child_allocation.x = relative_allocation->x + allocation.x;
child_allocation.y = relative_allocation->y + allocation.y;
child_allocation.x = relative_allocation->x;
child_allocation.y = relative_allocation->y;
child_allocation.width = relative_allocation->width;
child_allocation.height = relative_allocation->height;
@@ -2198,6 +2377,13 @@ gtk_scrolled_window_size_allocate (GtkWidget *widget,
bin = GTK_BIN (scrolled_window);
priv = scrolled_window->priv;
if (gtk_widget_get_realized (widget))
{
gdk_window_move_resize (gtk_widget_get_window (widget),
allocation->x, allocation->y,
allocation->width, allocation->height);
}
/* Get possible scrollbar dimensions */
sb_spacing = _gtk_scrolled_window_get_scrollbar_spacing (scrolled_window);
gtk_widget_get_preferred_height (priv->hscrollbar, &sb_height, NULL);
@@ -2445,8 +2631,6 @@ gtk_scrolled_window_size_allocate (GtkWidget *widget,
child_allocation.width = relative_allocation.width;
child_allocation.height = sb_height;
child_allocation.x += allocation->x;
child_allocation.y += allocation->y;
if (priv->shadow_type != GTK_SHADOW_NONE)
{
@@ -2502,8 +2686,6 @@ gtk_scrolled_window_size_allocate (GtkWidget *widget,
child_allocation.y = relative_allocation.y;
child_allocation.width = sb_width;
child_allocation.height = relative_allocation.height;
child_allocation.x += allocation->x;
child_allocation.y += allocation->y;
if (priv->shadow_type != GTK_SHADOW_NONE)
{
@@ -3470,6 +3652,9 @@ indicator_start_fade (Indicator *indicator,
"gtk-enable-animations", &animations_enabled,
NULL);
if (target != 0.0)
indicator->last_scroll_time = g_get_monotonic_time ();
if (gtk_widget_get_mapped (indicator->scrollbar) && animations_enabled)
{
indicator->source_pos = indicator->current_pos;
@@ -3516,37 +3701,6 @@ indicator_value_changed (GtkAdjustment *adjustment,
indicator_start_fade (indicator, 1.0);
}
static gboolean
indicator_enter_notify (GtkWidget *scrollbar,
GdkEventCrossing *event,
Indicator *indicator)
{
GtkStyleContext *context;
context = gtk_widget_get_style_context (scrollbar);
gtk_style_context_add_class (context, "hovering");
gtk_widget_queue_resize (scrollbar);
indicator->over = TRUE;
indicator_start_fade (indicator, 1.0);
return G_SOURCE_CONTINUE;
}
static gboolean
indicator_leave_notify (GtkWidget *scrollbar,
GdkEventCrossing *event,
Indicator *indicator)
{
GtkStyleContext *context;
context = gtk_widget_get_style_context (scrollbar);
gtk_style_context_remove_class (context, "hovering");
gtk_widget_queue_resize (scrollbar);
indicator->over = FALSE;
return G_SOURCE_CONTINUE;
}
static void
indicator_style_changed (GtkStyleContext *context,
Indicator *indicator)
@@ -3583,10 +3737,6 @@ setup_indicator (GtkScrolledWindow *scrolled_window,
gtk_style_context_add_class (context, "overlay-indicator");
g_signal_connect (context, "changed",
G_CALLBACK (indicator_style_changed), indicator);
g_signal_connect (scrollbar, "enter-notify-event",
G_CALLBACK (indicator_enter_notify), indicator);
g_signal_connect (scrollbar, "leave-notify-event",
G_CALLBACK (indicator_leave_notify), indicator);
g_signal_connect (adjustment, "value-changed",
G_CALLBACK (indicator_value_changed), indicator);
@@ -3614,8 +3764,6 @@ remove_indicator (GtkScrolledWindow *scrolled_window,
gtk_style_context_remove_class (context, "overlay-indicator");
g_signal_handlers_disconnect_by_func (context, indicator_style_changed, indicator);
g_signal_handlers_disconnect_by_func (scrollbar, indicator_enter_notify, indicator);
g_signal_handlers_disconnect_by_func (scrollbar, indicator_leave_notify, indicator);
g_signal_handlers_disconnect_by_func (adjustment, indicator_value_changed, indicator);
indicator->enabled = FALSE;
@@ -3625,6 +3773,12 @@ remove_indicator (GtkScrolledWindow *scrolled_window,
indicator->conceil_timer = 0;
}
if (indicator->over_timeout_id)
{
g_source_remove (indicator->over_timeout_id);
indicator->over_timeout_id = 0;
}
if (indicator->tick_id)
{
gtk_widget_remove_tick_callback (scrollbar, indicator->tick_id);
@@ -3747,8 +3901,31 @@ gtk_scrolled_window_realize (GtkWidget *widget)
GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
GtkScrolledWindowPrivate *priv = scrolled_window->priv;
GdkDeviceManager *dm;
GdkWindow *window;
GtkAllocation allocation;
GdkWindowAttr attributes;
gint attributes_mask;
GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->realize (widget);
gtk_widget_get_allocation (widget, &allocation);
attributes.window_type = GDK_WINDOW_CHILD;
attributes.wclass = GDK_INPUT_OUTPUT;
attributes.width = allocation.width;
attributes.height = allocation.height;
attributes.x = allocation.x;
attributes.y = allocation.y;
attributes.visual = gtk_widget_get_visual (widget);
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK |
GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK;
window = gdk_window_new (gtk_widget_get_parent_window (widget),
&attributes, attributes_mask);
gtk_widget_set_window (widget, window);
gtk_widget_register_window (widget, window);
gtk_widget_set_realized (widget, TRUE);
priv->hindicator.window = create_indicator_window (scrolled_window, priv->hscrollbar);
priv->vindicator.window = create_indicator_window (scrolled_window, priv->vscrollbar);