GtkScrolledWindow: Allow scrolling without bars

Add a new policy, GTK_POLICY_EXTERNAL, which hides the scrollbar,
but does not force the scrolled windows size to be determined by
its child. This can be used to keep two scrolled windows in sync,
while sharing a single scrollbar.

https://bugzilla.gnome.org/show_bug.cgi?id=730730
This commit is contained in:
Matthias Clasen
2014-10-04 23:15:04 -04:00
parent 3f4bd447f8
commit 94b680c2cd
2 changed files with 63 additions and 38 deletions

View File

@ -569,6 +569,22 @@ gtk_scrolled_window_class_init (GtkScrolledWindowClass *class)
gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_SCROLLED_WINDOW_ACCESSIBLE); gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_SCROLLED_WINDOW_ACCESSIBLE);
} }
static gboolean
may_hscroll (GtkScrolledWindow *scrolled_window)
{
GtkScrolledWindowPrivate *priv = scrolled_window->priv;
return priv->hscrollbar_visible || priv->hscrollbar_policy == GTK_POLICY_EXTERNAL;
}
static gboolean
may_vscroll (GtkScrolledWindow *scrolled_window)
{
GtkScrolledWindowPrivate *priv = scrolled_window->priv;
return priv->vscrollbar_visible || priv->vscrollbar_policy == GTK_POLICY_EXTERNAL;
}
static void static void
scrolled_window_drag_begin_cb (GtkScrolledWindow *scrolled_window, scrolled_window_drag_begin_cb (GtkScrolledWindow *scrolled_window,
gdouble start_x, gdouble start_x,
@ -589,9 +605,8 @@ scrolled_window_drag_begin_cb (GtkScrolledWindow *scrolled_window,
event = gtk_gesture_get_last_event (gesture, sequence); event = gtk_gesture_get_last_event (gesture, sequence);
event_widget = gtk_get_event_widget ((GdkEvent *) event); event_widget = gtk_get_event_widget ((GdkEvent *) event);
if (event_widget == priv->vscrollbar || if (event_widget == priv->vscrollbar || event_widget == priv->hscrollbar ||
event_widget == priv->hscrollbar || (!may_hscroll (scrolled_window) && !may_vscroll (scrolled_window)))
(!priv->hscrollbar_visible && !priv->vscrollbar_visible))
state = GTK_EVENT_SEQUENCE_DENIED; state = GTK_EVENT_SEQUENCE_DENIED;
else if (priv->capture_button_press) else if (priv->capture_button_press)
state = GTK_EVENT_SEQUENCE_CLAIMED; state = GTK_EVENT_SEQUENCE_CLAIMED;
@ -671,7 +686,7 @@ scrolled_window_drag_update_cb (GtkScrolledWindow *scrolled_window,
} }
hadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar)); hadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
if (hadjustment && priv->hscrollbar_visible) if (hadjustment && may_hscroll (scrolled_window))
{ {
dx = priv->drag_start_x - offset_x; dx = priv->drag_start_x - offset_x;
_gtk_scrolled_window_set_adjustment_value (scrolled_window, hadjustment, _gtk_scrolled_window_set_adjustment_value (scrolled_window, hadjustment,
@ -679,7 +694,7 @@ scrolled_window_drag_update_cb (GtkScrolledWindow *scrolled_window,
} }
vadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar)); vadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar));
if (vadjustment && priv->vscrollbar_visible) if (vadjustment && may_vscroll (scrolled_window))
{ {
dy = priv->drag_start_y - offset_y; dy = priv->drag_start_y - offset_y;
_gtk_scrolled_window_set_adjustment_value (scrolled_window, vadjustment, _gtk_scrolled_window_set_adjustment_value (scrolled_window, vadjustment,
@ -712,10 +727,10 @@ scrolled_window_swipe_cb (GtkScrolledWindow *scrolled_window,
priv->x_velocity = -x_velocity; priv->x_velocity = -x_velocity;
priv->y_velocity = -y_velocity; priv->y_velocity = -y_velocity;
/* Zero out vector components without a visible scrollbar */ /* Zero out vector components for which we don't scroll */
if (!priv->hscrollbar_visible) if (!may_hscroll (scrolled_window))
priv->x_velocity = 0; priv->x_velocity = 0;
if (!priv->vscrollbar_visible) if (!may_vscroll (scrolled_window))
priv->y_velocity = 0; priv->y_velocity = 0;
if (priv->x_velocity != 0 || priv->y_velocity != 0 || overshoot) if (priv->x_velocity != 0 || priv->y_velocity != 0 || overshoot)
@ -765,12 +780,12 @@ gtk_scrolled_window_check_attach_pan_gesture (GtkScrolledWindow *sw)
GtkScrolledWindowPrivate *priv = sw->priv; GtkScrolledWindowPrivate *priv = sw->priv;
if (priv->kinetic_scrolling && if (priv->kinetic_scrolling &&
((priv->hscrollbar_visible && !priv->vscrollbar_visible) || ((may_hscroll (sw) && !may_vscroll (sw)) ||
(!priv->hscrollbar_visible && priv->vscrollbar_visible))) (!may_hscroll (sw) && may_vscroll (sw))))
{ {
GtkOrientation orientation; GtkOrientation orientation;
if (priv->hscrollbar_visible) if (may_hscroll (sw))
orientation = GTK_ORIENTATION_HORIZONTAL; orientation = GTK_ORIENTATION_HORIZONTAL;
else else
orientation = GTK_ORIENTATION_VERTICAL; orientation = GTK_ORIENTATION_VERTICAL;
@ -1697,7 +1712,7 @@ gtk_scrolled_window_draw (GtkWidget *widget,
gtk_widget_get_allocated_width (widget), gtk_widget_get_allocated_width (widget),
gtk_widget_get_allocated_height (widget)); gtk_widget_get_allocated_height (widget));
if (priv->hscrollbar_visible && if (priv->hscrollbar_visible &&
priv->vscrollbar_visible) priv->vscrollbar_visible)
gtk_scrolled_window_draw_scrollbars_junction (scrolled_window, cr); gtk_scrolled_window_draw_scrollbars_junction (scrolled_window, cr);
@ -1817,10 +1832,6 @@ gtk_scrolled_window_scroll_child (GtkScrolledWindow *scrolled_window,
return FALSE; return FALSE;
} }
if ((horizontal && !priv->hscrollbar_visible) ||
(!horizontal && !priv->vscrollbar_visible))
return FALSE;
if (horizontal) if (horizontal)
adjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar)); adjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
else else
@ -2075,11 +2086,14 @@ gtk_scrolled_window_size_allocate (GtkWidget *widget,
if (priv->hscrollbar_policy == GTK_POLICY_ALWAYS) if (priv->hscrollbar_policy == GTK_POLICY_ALWAYS)
priv->hscrollbar_visible = TRUE; priv->hscrollbar_visible = TRUE;
else if (priv->hscrollbar_policy == GTK_POLICY_NEVER) else if (priv->hscrollbar_policy == GTK_POLICY_NEVER ||
priv->hscrollbar_policy == GTK_POLICY_EXTERNAL)
priv->hscrollbar_visible = FALSE; priv->hscrollbar_visible = FALSE;
if (priv->vscrollbar_policy == GTK_POLICY_ALWAYS) if (priv->vscrollbar_policy == GTK_POLICY_ALWAYS)
priv->vscrollbar_visible = TRUE; priv->vscrollbar_visible = TRUE;
else if (priv->vscrollbar_policy == GTK_POLICY_NEVER) else if (priv->vscrollbar_policy == GTK_POLICY_NEVER ||
priv->vscrollbar_policy == GTK_POLICY_EXTERNAL)
priv->vscrollbar_visible = FALSE; priv->vscrollbar_visible = FALSE;
child = gtk_bin_get_child (bin); child = gtk_bin_get_child (bin);
@ -2145,21 +2159,21 @@ gtk_scrolled_window_size_allocate (GtkWidget *widget,
} }
else /* priv->hscrollbar_policy != GTK_POLICY_AUTOMATIC */ else /* priv->hscrollbar_policy != GTK_POLICY_AUTOMATIC */
{ {
priv->hscrollbar_visible = priv->hscrollbar_policy != GTK_POLICY_NEVER; priv->hscrollbar_visible = priv->hscrollbar_policy < GTK_POLICY_NEVER;
priv->vscrollbar_visible = child_scroll_height > allocation->height - priv->vscrollbar_visible = child_scroll_height > allocation->height -
(priv->hscrollbar_visible ? sb_height + sb_spacing : 0); (priv->hscrollbar_visible ? sb_height + sb_spacing : 0);
} }
} }
else /* priv->vscrollbar_policy != GTK_POLICY_AUTOMATIC */ else /* priv->vscrollbar_policy != GTK_POLICY_AUTOMATIC */
{ {
priv->vscrollbar_visible = priv->vscrollbar_policy != GTK_POLICY_NEVER; priv->vscrollbar_visible = priv->vscrollbar_policy < GTK_POLICY_NEVER;
if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC) if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
priv->hscrollbar_visible = priv->hscrollbar_visible =
child_scroll_width > allocation->width - child_scroll_width > allocation->width -
(priv->vscrollbar_visible ? 0 : sb_width + sb_spacing); (priv->vscrollbar_visible ? 0 : sb_width + sb_spacing);
else else
priv->hscrollbar_visible = priv->hscrollbar_policy != GTK_POLICY_NEVER; priv->hscrollbar_visible = priv->hscrollbar_policy < GTK_POLICY_NEVER;
} }
} }
else /* GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT */ else /* GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT */
@ -2206,21 +2220,21 @@ gtk_scrolled_window_size_allocate (GtkWidget *widget,
} }
else /* priv->vscrollbar_policy != GTK_POLICY_AUTOMATIC */ else /* priv->vscrollbar_policy != GTK_POLICY_AUTOMATIC */
{ {
priv->vscrollbar_visible = priv->vscrollbar_policy != GTK_POLICY_NEVER; priv->vscrollbar_visible = priv->vscrollbar_policy < GTK_POLICY_NEVER;
priv->hscrollbar_visible = child_scroll_width > allocation->width - priv->hscrollbar_visible = child_scroll_width > allocation->width -
(priv->vscrollbar_visible ? sb_width + sb_spacing : 0); (priv->vscrollbar_visible ? sb_width + sb_spacing : 0);
} }
} }
else /* priv->hscrollbar_policy != GTK_POLICY_AUTOMATIC */ else /* priv->hscrollbar_policy != GTK_POLICY_AUTOMATIC */
{ {
priv->hscrollbar_visible = priv->hscrollbar_policy != GTK_POLICY_NEVER; priv->hscrollbar_visible = priv->hscrollbar_policy < GTK_POLICY_NEVER;
if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC) if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
priv->vscrollbar_visible = priv->vscrollbar_visible =
child_scroll_height > allocation->height - child_scroll_height > allocation->height -
(priv->hscrollbar_visible ? 0 : sb_height + sb_spacing); (priv->hscrollbar_visible ? 0 : sb_height + sb_spacing);
else else
priv->vscrollbar_visible = priv->vscrollbar_policy != GTK_POLICY_NEVER; priv->vscrollbar_visible = priv->vscrollbar_policy < GTK_POLICY_NEVER;
} }
} }
@ -2240,12 +2254,12 @@ gtk_scrolled_window_size_allocate (GtkWidget *widget,
* if they logically did not change since the last configuration * if they logically did not change since the last configuration
*/ */
if (priv->hscrollbar) if (priv->hscrollbar)
gtk_scrolled_window_adjustment_changed gtk_scrolled_window_adjustment_changed
(gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar)), scrolled_window); (gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar)), scrolled_window);
if (priv->vscrollbar) if (priv->vscrollbar)
gtk_scrolled_window_adjustment_changed gtk_scrolled_window_adjustment_changed
(gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar)), scrolled_window); (gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar)), scrolled_window);
/* If, after the first iteration, the hscrollbar and the /* If, after the first iteration, the hscrollbar and the
* vscrollbar flip visiblity... or if one of the scrollbars flip * vscrollbar flip visiblity... or if one of the scrollbars flip
@ -2374,7 +2388,7 @@ gtk_scrolled_window_scroll_event (GtkWidget *widget,
if (gdk_event_get_scroll_deltas ((GdkEvent *) event, &delta_x, &delta_y)) if (gdk_event_get_scroll_deltas ((GdkEvent *) event, &delta_x, &delta_y))
{ {
if (delta_x != 0.0 && if (delta_x != 0.0 &&
gtk_widget_get_visible (priv->hscrollbar)) may_hscroll (scrolled_window))
{ {
GtkAdjustment *adj; GtkAdjustment *adj;
gdouble new_value; gdouble new_value;
@ -2401,7 +2415,7 @@ gtk_scrolled_window_scroll_event (GtkWidget *widget,
} }
if (delta_y != 0.0 && if (delta_y != 0.0 &&
gtk_widget_get_visible (priv->vscrollbar)) may_vscroll (scrolled_window))
{ {
GtkAdjustment *adj; GtkAdjustment *adj;
gdouble new_value; gdouble new_value;
@ -2430,13 +2444,20 @@ gtk_scrolled_window_scroll_event (GtkWidget *widget,
else else
{ {
GtkWidget *range; GtkWidget *range;
gboolean may_scroll;
if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN) if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
range = priv->vscrollbar; {
range = priv->vscrollbar;
may_scroll = may_vscroll (scrolled_window);
}
else else
range = priv->hscrollbar; {
range = priv->hscrollbar;
may_scroll = may_hscroll (scrolled_window);
}
if (range && gtk_widget_get_visible (range)) if (range && may_scroll)
{ {
GtkAdjustment *adj = gtk_range_get_adjustment (GTK_RANGE (range)); GtkAdjustment *adj = gtk_range_get_adjustment (GTK_RANGE (range));
gdouble new_value; gdouble new_value;
@ -2587,7 +2608,7 @@ gtk_scrolled_window_start_deceleration (GtkScrolledWindow *scrolled_window)
data->scrolled_window = scrolled_window; data->scrolled_window = scrolled_window;
data->last_deceleration_time = gdk_frame_clock_get_frame_time (frame_clock); data->last_deceleration_time = gdk_frame_clock_get_frame_time (frame_clock);
if (priv->hscrollbar_visible) if (may_hscroll (scrolled_window))
{ {
gdouble lower,upper; gdouble lower,upper;
GtkAdjustment *hadjustment; GtkAdjustment *hadjustment;
@ -2606,7 +2627,7 @@ gtk_scrolled_window_start_deceleration (GtkScrolledWindow *scrolled_window)
priv->x_velocity); priv->x_velocity);
} }
if (priv->vscrollbar_visible) if (may_vscroll (scrolled_window))
{ {
gdouble lower,upper; gdouble lower,upper;
GtkAdjustment *vadjustment; GtkAdjustment *vadjustment;
@ -2938,7 +2959,7 @@ gtk_scrolled_window_get_preferred_size (GtkWidget *widget,
natural_req.width = MAX (natural_req.width, min_content_width); natural_req.width = MAX (natural_req.width, min_content_width);
extra_width = -1; extra_width = -1;
} }
else else if (priv->vscrollbar_policy < GTK_POLICY_NEVER)
{ {
minimum_req.width += vscrollbar_requisition.width; minimum_req.width += vscrollbar_requisition.width;
natural_req.width += vscrollbar_requisition.width; natural_req.width += vscrollbar_requisition.width;
@ -2966,7 +2987,7 @@ gtk_scrolled_window_get_preferred_size (GtkWidget *widget,
natural_req.height = MAX (natural_req.height, min_content_height); natural_req.height = MAX (natural_req.height, min_content_height);
extra_height = -1; extra_height = -1;
} }
else else if (priv->vscrollbar_policy < GTK_POLICY_NEVER)
{ {
minimum_req.height += vscrollbar_requisition.height; minimum_req.height += vscrollbar_requisition.height;
natural_req.height += vscrollbar_requisition.height; natural_req.height += vscrollbar_requisition.height;

View File

@ -126,6 +126,9 @@ typedef enum
* For example, when all of a #GtkTreeView can not be seen. * For example, when all of a #GtkTreeView can not be seen.
* @GTK_POLICY_NEVER: The scrollbar should never appear. In this mode the * @GTK_POLICY_NEVER: The scrollbar should never appear. In this mode the
* content determines the size. * content determines the size.
* @GTK_POLICY_EXTERNAL: Don't show a scrollbar, but don't force the
* size to follow the content. This can be used e.g. to make multiple
* scrolled windows share a scrollbar. Since: 3.16
* *
* Determines how the size should be computed to achieve the one of the * Determines how the size should be computed to achieve the one of the
* visibility mode for the scrollbars. * visibility mode for the scrollbars.
@ -134,7 +137,8 @@ typedef enum
{ {
GTK_POLICY_ALWAYS, GTK_POLICY_ALWAYS,
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC,
GTK_POLICY_NEVER GTK_POLICY_NEVER,
GTK_POLICY_EXTERNAL
} GtkPolicyType; } GtkPolicyType;