1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 /*
19 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
20 * file for a list of people on the GTK+ Team. See the ChangeLog
21 * files for a list of changes. These files are distributed with
22 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
23 */
24
25 #include "config.h"
26
27 #include "gtkscrolledwindow.h"
28
29 #include "gtkadjustment.h"
30 #include "gtkadjustmentprivate.h"
31 #include "gtkbindings.h"
32 #include "gtkcsscustomgadgetprivate.h"
33 #include "gtkdnd.h"
34 #include "gtkintl.h"
35 #include "gtkmain.h"
36 #include "gtkmarshalers.h"
37 #include "gtkprivate.h"
38 #include "gtkscrollable.h"
39 #include "gtkscrollbar.h"
40 #include "gtkrangeprivate.h"
41 #include "gtktypebuiltins.h"
42 #include "gtkviewport.h"
43 #include "gtkwidgetprivate.h"
44 #include "gtkwindow.h"
45 #include "gtkkineticscrolling.h"
46 #include "a11y/gtkscrolledwindowaccessible.h"
47 #include "gtkstylecontextprivate.h"
48 #include "gtkprogresstrackerprivate.h"
49 #include "gtksettingsprivate.h"
50
51 #include "fallback-c89.c"
52
53 /**
54 * SECTION:gtkscrolledwindow
55 * @Short_description: Adds scrollbars to its child widget
56 * @Title: GtkScrolledWindow
57 * @See_also: #GtkScrollable, #GtkViewport, #GtkAdjustment
58 *
59 * GtkScrolledWindow is a container that accepts a single child widget, makes
60 * that child scrollable using either internally added scrollbars or externally
61 * associated adjustments, and optionally draws a frame around the child.
62 *
63 * Widgets with native scrolling support, i.e. those whose classes implement the
64 * #GtkScrollable interface, are added directly. For other types of widget, the
65 * class #GtkViewport acts as an adaptor, giving scrollability to other widgets.
66 * GtkScrolledWindow’s implementation of gtk_container_add() intelligently
67 * accounts for whether or not the added child is a #GtkScrollable. If it isn’t,
68 * #GtkScrolledWindow wraps the child in a #GtkViewport and adds that for you.
69 * Therefore, you can just add any child widget and not worry about the details.
70 *
71 * If gtk_container_add() has added a #GtkViewport for you, you can remove
72 * both your added child widget from the #GtkViewport, and the #GtkViewport
73 * from the GtkScrolledWindow, like this:
74 *
75 * |[<!-- language="C" -->
76 * GtkWidget *scrolled_window = gtk_scrolled_window_new (NULL, NULL);
77 * GtkWidget *child_widget = gtk_button_new ();
78 *
79 * // GtkButton is not a GtkScrollable, so GtkScrolledWindow will automatically
80 * // add a GtkViewport.
81 * gtk_container_add (GTK_CONTAINER (scrolled_window),
82 * child_widget);
83 *
84 * // Either of these will result in child_widget being unparented:
85 * gtk_container_remove (GTK_CONTAINER (scrolled_window),
86 * child_widget);
87 * // or
88 * gtk_container_remove (GTK_CONTAINER (scrolled_window),
89 * gtk_bin_get_child (GTK_BIN (scrolled_window)));
90 * ]|
91 *
92 * Unless #GtkScrolledWindow:policy is GTK_POLICY_NEVER or GTK_POLICY_EXTERNAL,
93 * GtkScrolledWindow adds internal #GtkScrollbar widgets around its child. The
94 * scroll position of the child, and if applicable the scrollbars, is controlled
95 * by the #GtkScrolledWindow:hadjustment and #GtkScrolledWindow:vadjustment
96 * that are associated with the GtkScrolledWindow. See the docs on #GtkScrollbar
97 * for the details, but note that the “step_increment” and “page_increment”
98 * fields are only effective if the policy causes scrollbars to be present.
99 *
100 * If a GtkScrolledWindow doesn’t behave quite as you would like, or
101 * doesn’t have exactly the right layout, it’s very possible to set up
102 * your own scrolling with #GtkScrollbar and for example a #GtkGrid.
103 *
104 * # Touch support
105 *
106 * GtkScrolledWindow has built-in support for touch devices. When a
107 * touchscreen is used, swiping will move the scrolled window, and will
108 * expose 'kinetic' behavior. This can be turned off with the
109 * #GtkScrolledWindow:kinetic-scrolling property if it is undesired.
110 *
111 * GtkScrolledWindow also displays visual 'overshoot' indication when
112 * the content is pulled beyond the end, and this situation can be
113 * captured with the #GtkScrolledWindow::edge-overshot signal.
114 *
115 * If no mouse device is present, the scrollbars will overlayed as
116 * narrow, auto-hiding indicators over the content. If traditional
117 * scrollbars are desired although no mouse is present, this behaviour
118 * can be turned off with the #GtkScrolledWindow:overlay-scrolling
119 * property.
120 *
121 * # CSS nodes
122 *
123 * GtkScrolledWindow has a main CSS node with name scrolledwindow.
124 *
125 * It uses subnodes with names overshoot and undershoot to
126 * draw the overflow and underflow indications. These nodes get
127 * the .left, .right, .top or .bottom style class added depending
128 * on where the indication is drawn.
129 *
130 * GtkScrolledWindow also sets the positional style classes (.left,
131 * .right, .top, .bottom) and style classes related to overlay
132 * scrolling (.overlay-indicator, .dragging, .hovering) on its scrollbars.
133 *
134 * If both scrollbars are visible, the area where they meet is drawn
135 * with a subnode named junction.
136 */
137
138
139 /* scrolled window policy and size requisition handling:
140 *
141 * gtk size requisition works as follows:
142 * a widget upon size-request reports the width and height that it finds
143 * to be best suited to display its contents, including children.
144 * the width and/or height reported from a widget upon size requisition
145 * may be overidden by the user by specifying a width and/or height
146 * other than 0 through gtk_widget_set_size_request().
147 *
148 * a scrolled window needs (for implementing all three policy types) to
149 * request its width and height based on two different rationales.
150 * 1) the user wants the scrolled window to just fit into the space
151 * that it gets allocated for a specifc dimension.
152 * 1.1) this does not apply if the user specified a concrete value
153 * value for that specific dimension by either specifying usize for the
154 * scrolled window or for its child.
155 * 2) the user wants the scrolled window to take as much space up as
156 * is desired by the child for a specifc dimension (i.e. POLICY_NEVER).
157 *
158 * also, kinda obvious:
159 * 3) a user would certainly not have choosen a scrolled window as a container
160 * for the child, if the resulting allocation takes up more space than the
161 * child would have allocated without the scrolled window.
162 *
163 * conclusions:
164 * A) from 1) follows: the scrolled window shouldn’t request more space for a
165 * specifc dimension than is required at minimum.
166 * B) from 1.1) follows: the requisition may be overidden by usize of the scrolled
167 * window (done automatically) or by usize of the child (needs to be checked).
168 * C) from 2) follows: for POLICY_NEVER, the scrolled window simply reports the
169 * child’s dimension.
170 * D) from 3) follows: the scrolled window child’s minimum width and minimum height
171 * under A) at least correspond to the space taken up by its scrollbars.
172 */
173
174 #define DEFAULT_SCROLLBAR_SPACING 3
175 #define TOUCH_BYPASS_CAPTURED_THRESHOLD 30
176
177 /* Kinetic scrolling */
178 #define MAX_OVERSHOOT_DISTANCE 100
179 #define DECELERATION_FRICTION 4
180 #define OVERSHOOT_FRICTION 20
181 #define SCROLL_CAPTURE_THRESHOLD_MS 150
182 #define VELOCITY_ACCUMULATION_FLOOR 0.33
183 #define VELOCITY_ACCUMULATION_CEIL 1.0
184 #define VELOCITY_ACCUMULATION_MAX 6.0
185
186 /* Animated scrolling */
187 #define ANIMATION_DURATION 200
188
189 /* Overlay scrollbars */
190 #define INDICATOR_FADE_OUT_DELAY 2000
191 #define INDICATOR_FADE_OUT_DURATION 1000
192 #define INDICATOR_FADE_OUT_TIME 500
193 #define INDICATOR_CLOSE_DISTANCE 5
194 #define INDICATOR_FAR_DISTANCE 10
195
196 /* Scrolled off indication */
197 #define UNDERSHOOT_SIZE 40
198
199 typedef struct
200 {
201 GtkWidget *scrollbar;
202 GdkWindow *window;
203 gboolean over; /* either mouse over, or while dragging */
204 gint64 last_scroll_time;
205 guint conceil_timer;
206
207 gdouble current_pos;
208 gdouble source_pos;
209 gdouble target_pos;
210 GtkProgressTracker tracker;
211 guint tick_id;
212 guint over_timeout_id;
213 } Indicator;
214
215 typedef struct
216 {
217 gdouble dx;
218 gdouble dy;
219 guint32 evtime;
220 } ScrollHistoryElem;
221
222 struct _GtkScrolledWindowPrivate
223 {
224 GtkWidget *hscrollbar;
225 GtkWidget *vscrollbar;
226
227 GtkCssGadget *gadget;
228 GtkCssNode *overshoot_node[4];
229 GtkCssNode *undershoot_node[4];
230
231 Indicator hindicator;
232 Indicator vindicator;
233
234 GtkCornerType window_placement;
235 guint16 shadow_type;
236
237 guint hscrollbar_policy : 2;
238 guint vscrollbar_policy : 2;
239 guint hscrollbar_visible : 1;
240 guint vscrollbar_visible : 1;
241 guint focus_out : 1; /* used by ::move-focus-out implementation */
242 guint overlay_scrolling : 1;
243 guint use_indicators : 1;
244 guint auto_added_viewport : 1;
245 guint propagate_natural_width : 1;
246 guint propagate_natural_height : 1;
247
248 gint min_content_width;
249 gint min_content_height;
250 gint max_content_width;
251 gint max_content_height;
252
253 guint scroll_events_overshoot_id;
254
255 /* Kinetic scrolling */
256 GtkGesture *long_press_gesture;
257 GtkGesture *swipe_gesture;
258 GtkKineticScrolling *hscrolling;
259 GtkKineticScrolling *vscrolling;
260 gint64 last_deceleration_time;
261
262 GArray *scroll_history;
263 GdkDevice *scroll_device;
264 GdkWindow *scroll_window;
265 GdkCursor *scroll_cursor;
266
267 /* These two gestures are mutually exclusive */
268 GtkGesture *drag_gesture;
269 GtkGesture *pan_gesture;
270
271 gdouble drag_start_x;
272 gdouble drag_start_y;
273
274 GdkDevice *drag_device;
275 guint kinetic_scrolling : 1;
276 guint capture_button_press : 1;
277 guint in_drag : 1;
278
279 guint deceleration_id;
280
281 gdouble x_velocity;
282 gdouble y_velocity;
283
284 gdouble unclamped_hadj_value;
285 gdouble unclamped_vadj_value;
286 };
287
288 enum {
289 PROP_0,
290 PROP_HADJUSTMENT,
291 PROP_VADJUSTMENT,
292 PROP_HSCROLLBAR_POLICY,
293 PROP_VSCROLLBAR_POLICY,
294 PROP_WINDOW_PLACEMENT,
295 PROP_WINDOW_PLACEMENT_SET,
296 PROP_SHADOW_TYPE,
297 PROP_MIN_CONTENT_WIDTH,
298 PROP_MIN_CONTENT_HEIGHT,
299 PROP_KINETIC_SCROLLING,
300 PROP_OVERLAY_SCROLLING,
301 PROP_MAX_CONTENT_WIDTH,
302 PROP_MAX_CONTENT_HEIGHT,
303 PROP_PROPAGATE_NATURAL_WIDTH,
304 PROP_PROPAGATE_NATURAL_HEIGHT,
305 NUM_PROPERTIES
306 };
307
308 /* Signals */
309 enum
310 {
311 SCROLL_CHILD,
312 MOVE_FOCUS_OUT,
313 EDGE_OVERSHOT,
314 EDGE_REACHED,
315 LAST_SIGNAL
316 };
317
318 static void gtk_scrolled_window_set_property (GObject *object,
319 guint prop_id,
320 const GValue *value,
321 GParamSpec *pspec);
322 static void gtk_scrolled_window_get_property (GObject *object,
323 guint prop_id,
324 GValue *value,
325 GParamSpec *pspec);
326 static void gtk_scrolled_window_finalize (GObject *object);
327
328 static void gtk_scrolled_window_destroy (GtkWidget *widget);
329 static gboolean gtk_scrolled_window_draw (GtkWidget *widget,
330 cairo_t *cr);
331 static void gtk_scrolled_window_size_allocate (GtkWidget *widget,
332 GtkAllocation *allocation);
333 static gboolean gtk_scrolled_window_scroll_event (GtkWidget *widget,
334 GdkEventScroll *event);
335 static gboolean gtk_scrolled_window_focus (GtkWidget *widget,
336 GtkDirectionType direction);
337 static void gtk_scrolled_window_add (GtkContainer *container,
338 GtkWidget *widget);
339 static void gtk_scrolled_window_remove (GtkContainer *container,
340 GtkWidget *widget);
341 static void gtk_scrolled_window_forall (GtkContainer *container,
342 gboolean include_internals,
343 GtkCallback callback,
344 gpointer callback_data);
345 static gboolean gtk_scrolled_window_scroll_child (GtkScrolledWindow *scrolled_window,
346 GtkScrollType scroll,
347 gboolean horizontal);
348 static void gtk_scrolled_window_move_focus_out (GtkScrolledWindow *scrolled_window,
349 GtkDirectionType direction_type);
350
351 static void gtk_scrolled_window_relative_allocation(GtkWidget *widget,
352 GtkAllocation *allocation);
353 static void gtk_scrolled_window_inner_allocation (GtkWidget *widget,
354 GtkAllocation *rect);
355 static void gtk_scrolled_window_allocate_scrollbar (GtkScrolledWindow *scrolled_window,
356 GtkWidget *scrollbar,
357 GtkAllocation *allocation);
358 static void gtk_scrolled_window_allocate_child (GtkScrolledWindow *swindow,
359 GtkAllocation *relative_allocation);
360 static void gtk_scrolled_window_adjustment_changed (GtkAdjustment *adjustment,
361 gpointer data);
362 static void gtk_scrolled_window_adjustment_value_changed (GtkAdjustment *adjustment,
363 gpointer data);
364 static gboolean gtk_widget_should_animate (GtkWidget *widget);
365
366 static void gtk_scrolled_window_get_preferred_width (GtkWidget *widget,
367 gint *minimum_size,
368 gint *natural_size);
369 static void gtk_scrolled_window_get_preferred_height (GtkWidget *widget,
370 gint *minimum_size,
371 gint *natural_size);
372 static void gtk_scrolled_window_get_preferred_height_for_width (GtkWidget *layout,
373 gint width,
374 gint *minimum_height,
375 gint *natural_height);
376 static void gtk_scrolled_window_get_preferred_width_for_height (GtkWidget *layout,
377 gint width,
378 gint *minimum_height,
379 gint *natural_height);
380
381 static void gtk_scrolled_window_map (GtkWidget *widget);
382 static void gtk_scrolled_window_unmap (GtkWidget *widget);
383 static void gtk_scrolled_window_realize (GtkWidget *widget);
384 static void gtk_scrolled_window_unrealize (GtkWidget *widget);
385
386 static void gtk_scrolled_window_grab_notify (GtkWidget *widget,
387 gboolean was_grabbed);
388
389 static void _gtk_scrolled_window_set_adjustment_value (GtkScrolledWindow *scrolled_window,
390 GtkAdjustment *adjustment,
391 gdouble value);
392
393 static void gtk_scrolled_window_cancel_deceleration (GtkScrolledWindow *scrolled_window);
394
395 static gboolean _gtk_scrolled_window_get_overshoot (GtkScrolledWindow *scrolled_window,
396 gint *overshoot_x,
397 gint *overshoot_y);
398
399 static void gtk_scrolled_window_start_deceleration (GtkScrolledWindow *scrolled_window);
400 static gint _gtk_scrolled_window_get_scrollbar_spacing (GtkScrolledWindow *scrolled_window);
401
402 static void gtk_scrolled_window_update_use_indicators (GtkScrolledWindow *scrolled_window);
403 static void remove_indicator (GtkScrolledWindow *sw,
404 Indicator *indicator);
405 static void indicator_stop_fade (Indicator *indicator);
406 static gboolean maybe_hide_indicator (gpointer data);
407
408 static void indicator_start_fade (Indicator *indicator,
409 gdouble pos);
410 static void indicator_set_over (Indicator *indicator,
411 gboolean over);
412 static void uninstall_scroll_cursor (GtkScrolledWindow *scrolled_window);
413
414
415 static guint signals[LAST_SIGNAL] = {0};
416 static GParamSpec *properties[NUM_PROPERTIES];
417
G_DEFINE_TYPE_WITH_PRIVATE(GtkScrolledWindow,gtk_scrolled_window,GTK_TYPE_BIN)418 G_DEFINE_TYPE_WITH_PRIVATE (GtkScrolledWindow, gtk_scrolled_window, GTK_TYPE_BIN)
419
420 static void
421 add_scroll_binding (GtkBindingSet *binding_set,
422 guint keyval,
423 GdkModifierType mask,
424 GtkScrollType scroll,
425 gboolean horizontal)
426 {
427 guint keypad_keyval = keyval - GDK_KEY_Left + GDK_KEY_KP_Left;
428
429 gtk_binding_entry_add_signal (binding_set, keyval, mask,
430 "scroll-child", 2,
431 GTK_TYPE_SCROLL_TYPE, scroll,
432 G_TYPE_BOOLEAN, horizontal);
433 gtk_binding_entry_add_signal (binding_set, keypad_keyval, mask,
434 "scroll-child", 2,
435 GTK_TYPE_SCROLL_TYPE, scroll,
436 G_TYPE_BOOLEAN, horizontal);
437 }
438
439 static void
add_tab_bindings(GtkBindingSet * binding_set,GdkModifierType modifiers,GtkDirectionType direction)440 add_tab_bindings (GtkBindingSet *binding_set,
441 GdkModifierType modifiers,
442 GtkDirectionType direction)
443 {
444 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Tab, modifiers,
445 "move-focus-out", 1,
446 GTK_TYPE_DIRECTION_TYPE, direction);
447 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Tab, modifiers,
448 "move-focus-out", 1,
449 GTK_TYPE_DIRECTION_TYPE, direction);
450 }
451
452 static gboolean
gtk_scrolled_window_leave_notify(GtkWidget * widget,GdkEventCrossing * event)453 gtk_scrolled_window_leave_notify (GtkWidget *widget,
454 GdkEventCrossing *event)
455 {
456 GtkScrolledWindowPrivate *priv = GTK_SCROLLED_WINDOW (widget)->priv;
457
458 if (priv->use_indicators && event->detail != GDK_NOTIFY_INFERIOR)
459 {
460 indicator_set_over (&priv->hindicator, FALSE);
461 indicator_set_over (&priv->vindicator, FALSE);
462 }
463
464 return GDK_EVENT_PROPAGATE;
465 }
466
467 static void
update_scrollbar_positions(GtkScrolledWindow * scrolled_window)468 update_scrollbar_positions (GtkScrolledWindow *scrolled_window)
469 {
470 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
471 GtkStyleContext *context;
472 gboolean is_rtl;
473
474 if (priv->hscrollbar != NULL)
475 {
476 context = gtk_widget_get_style_context (priv->hscrollbar);
477 if (priv->window_placement == GTK_CORNER_TOP_LEFT ||
478 priv->window_placement == GTK_CORNER_TOP_RIGHT)
479 {
480 gtk_style_context_add_class (context, GTK_STYLE_CLASS_BOTTOM);
481 gtk_style_context_remove_class (context, GTK_STYLE_CLASS_TOP);
482 }
483 else
484 {
485 gtk_style_context_remove_class (context, GTK_STYLE_CLASS_BOTTOM);
486 gtk_style_context_add_class (context, GTK_STYLE_CLASS_TOP);
487 }
488 }
489
490 if (priv->vscrollbar != NULL)
491 {
492 context = gtk_widget_get_style_context (priv->vscrollbar);
493 is_rtl = gtk_widget_get_direction (GTK_WIDGET (scrolled_window)) == GTK_TEXT_DIR_RTL;
494 if ((is_rtl &&
495 (priv->window_placement == GTK_CORNER_TOP_RIGHT ||
496 priv->window_placement == GTK_CORNER_BOTTOM_RIGHT)) ||
497 (!is_rtl &&
498 (priv->window_placement == GTK_CORNER_TOP_LEFT ||
499 priv->window_placement == GTK_CORNER_BOTTOM_LEFT)))
500 {
501 gtk_style_context_add_class (context, GTK_STYLE_CLASS_RIGHT);
502 gtk_style_context_remove_class (context, GTK_STYLE_CLASS_LEFT);
503 }
504 else
505 {
506 gtk_style_context_remove_class (context, GTK_STYLE_CLASS_RIGHT);
507 gtk_style_context_add_class (context, GTK_STYLE_CLASS_LEFT);
508 }
509 }
510 }
511
512 static void
gtk_scrolled_window_direction_changed(GtkWidget * widget,GtkTextDirection previous_dir)513 gtk_scrolled_window_direction_changed (GtkWidget *widget,
514 GtkTextDirection previous_dir)
515 {
516 GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
517
518 update_scrollbar_positions (scrolled_window);
519
520 GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->direction_changed (widget, previous_dir);
521 }
522
523 static void
gtk_scrolled_window_class_init(GtkScrolledWindowClass * class)524 gtk_scrolled_window_class_init (GtkScrolledWindowClass *class)
525 {
526 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
527 GtkWidgetClass *widget_class;
528 GtkContainerClass *container_class;
529 GtkBindingSet *binding_set;
530
531 widget_class = (GtkWidgetClass*) class;
532 container_class = (GtkContainerClass*) class;
533
534 gobject_class->set_property = gtk_scrolled_window_set_property;
535 gobject_class->get_property = gtk_scrolled_window_get_property;
536 gobject_class->finalize = gtk_scrolled_window_finalize;
537
538 widget_class->destroy = gtk_scrolled_window_destroy;
539 widget_class->draw = gtk_scrolled_window_draw;
540 widget_class->size_allocate = gtk_scrolled_window_size_allocate;
541 widget_class->scroll_event = gtk_scrolled_window_scroll_event;
542 widget_class->focus = gtk_scrolled_window_focus;
543 widget_class->get_preferred_width = gtk_scrolled_window_get_preferred_width;
544 widget_class->get_preferred_height = gtk_scrolled_window_get_preferred_height;
545 widget_class->get_preferred_height_for_width = gtk_scrolled_window_get_preferred_height_for_width;
546 widget_class->get_preferred_width_for_height = gtk_scrolled_window_get_preferred_width_for_height;
547 widget_class->map = gtk_scrolled_window_map;
548 widget_class->unmap = gtk_scrolled_window_unmap;
549 widget_class->grab_notify = gtk_scrolled_window_grab_notify;
550 widget_class->realize = gtk_scrolled_window_realize;
551 widget_class->unrealize = gtk_scrolled_window_unrealize;
552 widget_class->leave_notify_event = gtk_scrolled_window_leave_notify;
553 widget_class->direction_changed = gtk_scrolled_window_direction_changed;
554
555 container_class->add = gtk_scrolled_window_add;
556 container_class->remove = gtk_scrolled_window_remove;
557 container_class->forall = gtk_scrolled_window_forall;
558 gtk_container_class_handle_border_width (container_class);
559
560 class->scrollbar_spacing = -1;
561
562 class->scroll_child = gtk_scrolled_window_scroll_child;
563 class->move_focus_out = gtk_scrolled_window_move_focus_out;
564
565 properties[PROP_HADJUSTMENT] =
566 g_param_spec_object ("hadjustment",
567 P_("Horizontal Adjustment"),
568 P_("The GtkAdjustment for the horizontal position"),
569 GTK_TYPE_ADJUSTMENT,
570 GTK_PARAM_READWRITE|G_PARAM_CONSTRUCT);
571
572 properties[PROP_VADJUSTMENT] =
573 g_param_spec_object ("vadjustment",
574 P_("Vertical Adjustment"),
575 P_("The GtkAdjustment for the vertical position"),
576 GTK_TYPE_ADJUSTMENT,
577 GTK_PARAM_READWRITE|G_PARAM_CONSTRUCT);
578
579 properties[PROP_HSCROLLBAR_POLICY] =
580 g_param_spec_enum ("hscrollbar-policy",
581 P_("Horizontal Scrollbar Policy"),
582 P_("When the horizontal scrollbar is displayed"),
583 GTK_TYPE_POLICY_TYPE,
584 GTK_POLICY_AUTOMATIC,
585 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
586
587 properties[PROP_VSCROLLBAR_POLICY] =
588 g_param_spec_enum ("vscrollbar-policy",
589 P_("Vertical Scrollbar Policy"),
590 P_("When the vertical scrollbar is displayed"),
591 GTK_TYPE_POLICY_TYPE,
592 GTK_POLICY_AUTOMATIC,
593 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
594
595 properties[PROP_WINDOW_PLACEMENT] =
596 g_param_spec_enum ("window-placement",
597 P_("Window Placement"),
598 P_("Where the contents are located with respect to the scrollbars."),
599 GTK_TYPE_CORNER_TYPE,
600 GTK_CORNER_TOP_LEFT,
601 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
602
603 /**
604 * GtkScrolledWindow:window-placement-set:
605 *
606 * Whether "window-placement" should be used to determine the location
607 * of the contents with respect to the scrollbars.
608 *
609 * Since: 2.10
610 *
611 * Deprecated: 3.10: This value is ignored and
612 * #GtkScrolledWindow:window-placement value is always honored.
613 */
614 properties[PROP_WINDOW_PLACEMENT_SET] =
615 g_param_spec_boolean ("window-placement-set",
616 P_("Window Placement Set"),
617 P_("Whether \"window-placement\" should be used to determine the location of the contents with respect to the scrollbars."),
618 TRUE,
619 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
620
621 properties[PROP_SHADOW_TYPE] =
622 g_param_spec_enum ("shadow-type",
623 P_("Shadow Type"),
624 P_("Style of bevel around the contents"),
625 GTK_TYPE_SHADOW_TYPE,
626 GTK_SHADOW_NONE,
627 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
628
629 /**
630 * GtkScrolledWindow:scrollbars-within-bevel:
631 *
632 * Whether to place scrollbars within the scrolled window's bevel.
633 *
634 * Since: 2.12
635 *
636 * Deprecated: 3.20: the value of this style property is ignored.
637 */
638 gtk_widget_class_install_style_property (widget_class,
639 g_param_spec_boolean ("scrollbars-within-bevel",
640 P_("Scrollbars within bevel"),
641 P_("Place scrollbars within the scrolled window's bevel"),
642 FALSE,
643 GTK_PARAM_READABLE|G_PARAM_DEPRECATED));
644
645 gtk_widget_class_install_style_property (widget_class,
646 g_param_spec_int ("scrollbar-spacing",
647 P_("Scrollbar spacing"),
648 P_("Number of pixels between the scrollbars and the scrolled window"),
649 0,
650 G_MAXINT,
651 DEFAULT_SCROLLBAR_SPACING,
652 GTK_PARAM_READABLE));
653
654 /**
655 * GtkScrolledWindow:min-content-width:
656 *
657 * The minimum content width of @scrolled_window, or -1 if not set.
658 *
659 * Since: 3.0
660 */
661 properties[PROP_MIN_CONTENT_WIDTH] =
662 g_param_spec_int ("min-content-width",
663 P_("Minimum Content Width"),
664 P_("The minimum width that the scrolled window will allocate to its content"),
665 -1, G_MAXINT, -1,
666 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
667
668 /**
669 * GtkScrolledWindow:min-content-height:
670 *
671 * The minimum content height of @scrolled_window, or -1 if not set.
672 *
673 * Since: 3.0
674 */
675 properties[PROP_MIN_CONTENT_HEIGHT] =
676 g_param_spec_int ("min-content-height",
677 P_("Minimum Content Height"),
678 P_("The minimum height that the scrolled window will allocate to its content"),
679 -1, G_MAXINT, -1,
680 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
681
682 /**
683 * GtkScrolledWindow:kinetic-scrolling:
684 *
685 * Whether kinetic scrolling is enabled or not. Kinetic scrolling
686 * only applies to devices with source %GDK_SOURCE_TOUCHSCREEN.
687 *
688 * Since: 3.4
689 */
690 properties[PROP_KINETIC_SCROLLING] =
691 g_param_spec_boolean ("kinetic-scrolling",
692 P_("Kinetic Scrolling"),
693 P_("Kinetic scrolling mode."),
694 TRUE,
695 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
696
697 /**
698 * GtkScrolledWindow:overlay-scrolling:
699 *
700 * Whether overlay scrolling is enabled or not. If it is, the
701 * scrollbars are only added as traditional widgets when a mouse
702 * is present. Otherwise, they are overlayed on top of the content,
703 * as narrow indicators.
704 *
705 * Note that overlay scrolling can also be globally disabled, with
706 * the #GtkSettings::gtk-overlay-scrolling setting.
707 *
708 * Since: 3.16
709 */
710 properties[PROP_OVERLAY_SCROLLING] =
711 g_param_spec_boolean ("overlay-scrolling",
712 P_("Overlay Scrolling"),
713 P_("Overlay scrolling mode"),
714 TRUE,
715 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
716
717 /**
718 * GtkScrolledWindow:max-content-width:
719 *
720 * The maximum content width of @scrolled_window, or -1 if not set.
721 *
722 * Since: 3.22
723 */
724 properties[PROP_MAX_CONTENT_WIDTH] =
725 g_param_spec_int ("max-content-width",
726 P_("Maximum Content Width"),
727 P_("The maximum width that the scrolled window will allocate to its content"),
728 -1, G_MAXINT, -1,
729 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
730
731 /**
732 * GtkScrolledWindow:max-content-height:
733 *
734 * The maximum content height of @scrolled_window, or -1 if not set.
735 *
736 * Since: 3.22
737 */
738 properties[PROP_MAX_CONTENT_HEIGHT] =
739 g_param_spec_int ("max-content-height",
740 P_("Maximum Content Height"),
741 P_("The maximum height that the scrolled window will allocate to its content"),
742 -1, G_MAXINT, -1,
743 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
744
745 /**
746 * GtkScrolledWindow:propagate-natural-width:
747 *
748 * Whether the natural width of the child should be calculated and propagated
749 * through the scrolled window’s requested natural width.
750 *
751 * This is useful in cases where an attempt should be made to allocate exactly
752 * enough space for the natural size of the child.
753 *
754 * Since: 3.22
755 */
756 properties[PROP_PROPAGATE_NATURAL_WIDTH] =
757 g_param_spec_boolean ("propagate-natural-width",
758 P_("Propagate Natural Width"),
759 P_("Propagate Natural Width"),
760 FALSE,
761 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
762
763 /**
764 * GtkScrolledWindow:propagate-natural-height:
765 *
766 * Whether the natural height of the child should be calculated and propagated
767 * through the scrolled window’s requested natural height.
768 *
769 * This is useful in cases where an attempt should be made to allocate exactly
770 * enough space for the natural size of the child.
771 *
772 * Since: 3.22
773 */
774 properties[PROP_PROPAGATE_NATURAL_HEIGHT] =
775 g_param_spec_boolean ("propagate-natural-height",
776 P_("Propagate Natural Height"),
777 P_("Propagate Natural Height"),
778 FALSE,
779 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
780
781 g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
782
783 /**
784 * GtkScrolledWindow::scroll-child:
785 * @scrolled_window: a #GtkScrolledWindow
786 * @scroll: a #GtkScrollType describing how much to scroll
787 * @horizontal: whether the keybinding scrolls the child
788 * horizontally or not
789 *
790 * The ::scroll-child signal is a
791 * [keybinding signal][GtkBindingSignal]
792 * which gets emitted when a keybinding that scrolls is pressed.
793 * The horizontal or vertical adjustment is updated which triggers a
794 * signal that the scrolled window’s child may listen to and scroll itself.
795 */
796 signals[SCROLL_CHILD] =
797 g_signal_new (I_("scroll-child"),
798 G_TYPE_FROM_CLASS (gobject_class),
799 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
800 G_STRUCT_OFFSET (GtkScrolledWindowClass, scroll_child),
801 NULL, NULL,
802 _gtk_marshal_BOOLEAN__ENUM_BOOLEAN,
803 G_TYPE_BOOLEAN, 2,
804 GTK_TYPE_SCROLL_TYPE,
805 G_TYPE_BOOLEAN);
806
807 /**
808 * GtkScrolledWindow::move-focus-out:
809 * @scrolled_window: a #GtkScrolledWindow
810 * @direction_type: either %GTK_DIR_TAB_FORWARD or
811 * %GTK_DIR_TAB_BACKWARD
812 *
813 * The ::move-focus-out signal is a
814 * [keybinding signal][GtkBindingSignal] which gets
815 * emitted when focus is moved away from the scrolled window by a
816 * keybinding. The #GtkWidget::move-focus signal is emitted with
817 * @direction_type on this scrolled window’s toplevel parent in the
818 * container hierarchy. The default bindings for this signal are
819 * `Ctrl + Tab` to move forward and `Ctrl + Shift + Tab` to move backward.
820 */
821 signals[MOVE_FOCUS_OUT] =
822 g_signal_new (I_("move-focus-out"),
823 G_TYPE_FROM_CLASS (gobject_class),
824 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
825 G_STRUCT_OFFSET (GtkScrolledWindowClass, move_focus_out),
826 NULL, NULL,
827 NULL,
828 G_TYPE_NONE, 1,
829 GTK_TYPE_DIRECTION_TYPE);
830
831 /**
832 * GtkScrolledWindow::edge-overshot:
833 * @scrolled_window: a #GtkScrolledWindow
834 * @pos: edge side that was hit
835 *
836 * The ::edge-overshot signal is emitted whenever user initiated scrolling
837 * makes the scrolled window firmly surpass (i.e. with some edge resistance)
838 * the lower or upper limits defined by the adjustment in that orientation.
839 *
840 * A similar behavior without edge resistance is provided by the
841 * #GtkScrolledWindow::edge-reached signal.
842 *
843 * Note: The @pos argument is LTR/RTL aware, so callers should be aware too
844 * if intending to provide behavior on horizontal edges.
845 *
846 * Since: 3.16
847 */
848 signals[EDGE_OVERSHOT] =
849 g_signal_new (I_("edge-overshot"),
850 G_TYPE_FROM_CLASS (gobject_class),
851 G_SIGNAL_RUN_LAST, 0,
852 NULL, NULL, NULL,
853 G_TYPE_NONE, 1, GTK_TYPE_POSITION_TYPE);
854
855 /**
856 * GtkScrolledWindow::edge-reached:
857 * @scrolled_window: a #GtkScrolledWindow
858 * @pos: edge side that was reached
859 *
860 * The ::edge-reached signal is emitted whenever user-initiated scrolling
861 * makes the scrolled window exactly reach the lower or upper limits
862 * defined by the adjustment in that orientation.
863 *
864 * A similar behavior with edge resistance is provided by the
865 * #GtkScrolledWindow::edge-overshot signal.
866 *
867 * Note: The @pos argument is LTR/RTL aware, so callers should be aware too
868 * if intending to provide behavior on horizontal edges.
869 *
870 * Since: 3.16
871 */
872 signals[EDGE_REACHED] =
873 g_signal_new (I_("edge-reached"),
874 G_TYPE_FROM_CLASS (gobject_class),
875 G_SIGNAL_RUN_LAST, 0,
876 NULL, NULL, NULL,
877 G_TYPE_NONE, 1, GTK_TYPE_POSITION_TYPE);
878
879 binding_set = gtk_binding_set_by_class (class);
880
881 add_scroll_binding (binding_set, GDK_KEY_Left, GDK_CONTROL_MASK, GTK_SCROLL_STEP_BACKWARD, TRUE);
882 add_scroll_binding (binding_set, GDK_KEY_Right, GDK_CONTROL_MASK, GTK_SCROLL_STEP_FORWARD, TRUE);
883 add_scroll_binding (binding_set, GDK_KEY_Up, GDK_CONTROL_MASK, GTK_SCROLL_STEP_BACKWARD, FALSE);
884 add_scroll_binding (binding_set, GDK_KEY_Down, GDK_CONTROL_MASK, GTK_SCROLL_STEP_FORWARD, FALSE);
885
886 add_scroll_binding (binding_set, GDK_KEY_Page_Up, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_BACKWARD, TRUE);
887 add_scroll_binding (binding_set, GDK_KEY_Page_Down, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_FORWARD, TRUE);
888 add_scroll_binding (binding_set, GDK_KEY_Page_Up, 0, GTK_SCROLL_PAGE_BACKWARD, FALSE);
889 add_scroll_binding (binding_set, GDK_KEY_Page_Down, 0, GTK_SCROLL_PAGE_FORWARD, FALSE);
890
891 add_scroll_binding (binding_set, GDK_KEY_Home, GDK_CONTROL_MASK, GTK_SCROLL_START, TRUE);
892 add_scroll_binding (binding_set, GDK_KEY_End, GDK_CONTROL_MASK, GTK_SCROLL_END, TRUE);
893 add_scroll_binding (binding_set, GDK_KEY_Home, 0, GTK_SCROLL_START, FALSE);
894 add_scroll_binding (binding_set, GDK_KEY_End, 0, GTK_SCROLL_END, FALSE);
895
896 add_tab_bindings (binding_set, GDK_CONTROL_MASK, GTK_DIR_TAB_FORWARD);
897 add_tab_bindings (binding_set, GDK_CONTROL_MASK | GDK_SHIFT_MASK, GTK_DIR_TAB_BACKWARD);
898
899 gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_SCROLLED_WINDOW_ACCESSIBLE);
900 gtk_widget_class_set_css_name (widget_class, "scrolledwindow");
901 }
902
903 static gboolean
may_hscroll(GtkScrolledWindow * scrolled_window)904 may_hscroll (GtkScrolledWindow *scrolled_window)
905 {
906 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
907
908 return priv->hscrollbar_visible || priv->hscrollbar_policy == GTK_POLICY_EXTERNAL;
909 }
910
911 static gboolean
may_vscroll(GtkScrolledWindow * scrolled_window)912 may_vscroll (GtkScrolledWindow *scrolled_window)
913 {
914 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
915
916 return priv->vscrollbar_visible || priv->vscrollbar_policy == GTK_POLICY_EXTERNAL;
917 }
918
919 static inline gboolean
policy_may_be_visible(GtkPolicyType policy)920 policy_may_be_visible (GtkPolicyType policy)
921 {
922 return policy == GTK_POLICY_ALWAYS || policy == GTK_POLICY_AUTOMATIC;
923 }
924
925 static void
scrolled_window_drag_begin_cb(GtkScrolledWindow * scrolled_window,gdouble start_x,gdouble start_y,GtkGesture * gesture)926 scrolled_window_drag_begin_cb (GtkScrolledWindow *scrolled_window,
927 gdouble start_x,
928 gdouble start_y,
929 GtkGesture *gesture)
930 {
931 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
932 GtkEventSequenceState state;
933 GdkEventSequence *sequence;
934 GtkWidget *event_widget;
935 const GdkEvent *event;
936
937 priv->in_drag = FALSE;
938 priv->drag_start_x = priv->unclamped_hadj_value;
939 priv->drag_start_y = priv->unclamped_vadj_value;
940 gtk_scrolled_window_cancel_deceleration (scrolled_window);
941 sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
942 event = gtk_gesture_get_last_event (gesture, sequence);
943 event_widget = gtk_get_event_widget ((GdkEvent *) event);
944
945 if (event_widget == priv->vscrollbar || event_widget == priv->hscrollbar ||
946 (!may_hscroll (scrolled_window) && !may_vscroll (scrolled_window)))
947 state = GTK_EVENT_SEQUENCE_DENIED;
948 else if (priv->capture_button_press)
949 state = GTK_EVENT_SEQUENCE_CLAIMED;
950 else
951 return;
952
953 gtk_gesture_set_sequence_state (gesture, sequence, state);
954 }
955
956 static void
gtk_scrolled_window_invalidate_overshoot(GtkScrolledWindow * scrolled_window)957 gtk_scrolled_window_invalidate_overshoot (GtkScrolledWindow *scrolled_window)
958 {
959 GtkAllocation child_allocation;
960 gint overshoot_x, overshoot_y;
961 GdkRectangle rect;
962
963 if (!_gtk_scrolled_window_get_overshoot (scrolled_window, &overshoot_x, &overshoot_y))
964 return;
965
966 gtk_scrolled_window_relative_allocation (GTK_WIDGET (scrolled_window),
967 &child_allocation);
968 if (overshoot_x != 0)
969 {
970 if (overshoot_x < 0)
971 rect.x = child_allocation.x;
972 else
973 rect.x = child_allocation.x + child_allocation.width - MAX_OVERSHOOT_DISTANCE;
974
975 rect.y = child_allocation.y;
976 rect.width = MAX_OVERSHOOT_DISTANCE;
977 rect.height = child_allocation.height;
978
979 gdk_window_invalidate_rect (gtk_widget_get_window (GTK_WIDGET (scrolled_window)),
980 &rect, TRUE);
981 }
982
983 if (overshoot_y != 0)
984 {
985 if (overshoot_y < 0)
986 rect.y = child_allocation.y;
987 else
988 rect.y = child_allocation.y + child_allocation.height - MAX_OVERSHOOT_DISTANCE;
989
990 rect.x = child_allocation.x;
991 rect.width = child_allocation.width;
992 rect.height = MAX_OVERSHOOT_DISTANCE;
993
994 gdk_window_invalidate_rect (gtk_widget_get_window (GTK_WIDGET (scrolled_window)),
995 &rect, TRUE);
996 }
997 }
998
999 static void
scrolled_window_drag_update_cb(GtkScrolledWindow * scrolled_window,gdouble offset_x,gdouble offset_y,GtkGesture * gesture)1000 scrolled_window_drag_update_cb (GtkScrolledWindow *scrolled_window,
1001 gdouble offset_x,
1002 gdouble offset_y,
1003 GtkGesture *gesture)
1004 {
1005 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
1006 GtkAdjustment *hadjustment;
1007 GtkAdjustment *vadjustment;
1008 gdouble dx, dy;
1009
1010 gtk_scrolled_window_invalidate_overshoot (scrolled_window);
1011
1012 if (!priv->capture_button_press)
1013 {
1014 GdkEventSequence *sequence;
1015
1016 sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
1017 gtk_gesture_set_sequence_state (gesture, sequence,
1018 GTK_EVENT_SEQUENCE_CLAIMED);
1019 }
1020
1021 hadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
1022 if (hadjustment && may_hscroll (scrolled_window))
1023 {
1024 dx = priv->drag_start_x - offset_x;
1025 _gtk_scrolled_window_set_adjustment_value (scrolled_window,
1026 hadjustment, dx);
1027 }
1028
1029 vadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar));
1030 if (vadjustment && may_vscroll (scrolled_window))
1031 {
1032 dy = priv->drag_start_y - offset_y;
1033 _gtk_scrolled_window_set_adjustment_value (scrolled_window,
1034 vadjustment, dy);
1035 }
1036
1037 gtk_scrolled_window_invalidate_overshoot (scrolled_window);
1038 }
1039
1040 static void
scrolled_window_drag_end_cb(GtkScrolledWindow * scrolled_window,GdkEventSequence * sequence,GtkGesture * gesture)1041 scrolled_window_drag_end_cb (GtkScrolledWindow *scrolled_window,
1042 GdkEventSequence *sequence,
1043 GtkGesture *gesture)
1044 {
1045 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
1046
1047 if (!priv->in_drag || !gtk_gesture_handles_sequence (gesture, sequence))
1048 gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_DENIED);
1049 }
1050
1051 static void
gtk_scrolled_window_decelerate(GtkScrolledWindow * scrolled_window,gdouble x_velocity,gdouble y_velocity)1052 gtk_scrolled_window_decelerate (GtkScrolledWindow *scrolled_window,
1053 gdouble x_velocity,
1054 gdouble y_velocity)
1055 {
1056 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
1057 gboolean overshoot;
1058
1059 overshoot = _gtk_scrolled_window_get_overshoot (scrolled_window, NULL, NULL);
1060 priv->x_velocity = x_velocity;
1061 priv->y_velocity = y_velocity;
1062
1063 /* Zero out vector components for which we don't scroll */
1064 if (!may_hscroll (scrolled_window))
1065 priv->x_velocity = 0;
1066 if (!may_vscroll (scrolled_window))
1067 priv->y_velocity = 0;
1068
1069 if (priv->x_velocity != 0 || priv->y_velocity != 0 || overshoot)
1070 {
1071 gtk_scrolled_window_start_deceleration (scrolled_window);
1072 priv->x_velocity = priv->y_velocity = 0;
1073 }
1074 }
1075
1076 static void
scrolled_window_swipe_cb(GtkScrolledWindow * scrolled_window,gdouble x_velocity,gdouble y_velocity)1077 scrolled_window_swipe_cb (GtkScrolledWindow *scrolled_window,
1078 gdouble x_velocity,
1079 gdouble y_velocity)
1080 {
1081 gtk_scrolled_window_decelerate (scrolled_window, -x_velocity, -y_velocity);
1082 }
1083
1084 static void
scrolled_window_long_press_cb(GtkScrolledWindow * scrolled_window,gdouble x,gdouble y,GtkGesture * gesture)1085 scrolled_window_long_press_cb (GtkScrolledWindow *scrolled_window,
1086 gdouble x,
1087 gdouble y,
1088 GtkGesture *gesture)
1089 {
1090 GdkEventSequence *sequence;
1091
1092 sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
1093 gtk_gesture_set_sequence_state (gesture, sequence,
1094 GTK_EVENT_SEQUENCE_DENIED);
1095 }
1096
1097 static void
scrolled_window_long_press_cancelled_cb(GtkScrolledWindow * scrolled_window,GtkGesture * gesture)1098 scrolled_window_long_press_cancelled_cb (GtkScrolledWindow *scrolled_window,
1099 GtkGesture *gesture)
1100 {
1101 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
1102 GdkEventSequence *sequence;
1103 const GdkEvent *event;
1104
1105 sequence = gtk_gesture_get_last_updated_sequence (gesture);
1106 event = gtk_gesture_get_last_event (gesture, sequence);
1107
1108 if (event->type == GDK_TOUCH_BEGIN ||
1109 event->type == GDK_BUTTON_PRESS)
1110 gtk_gesture_set_sequence_state (gesture, sequence,
1111 GTK_EVENT_SEQUENCE_DENIED);
1112 else if (event->type != GDK_TOUCH_END &&
1113 event->type != GDK_BUTTON_RELEASE)
1114 priv->in_drag = TRUE;
1115 }
1116
1117 static void
gtk_scrolled_window_check_attach_pan_gesture(GtkScrolledWindow * sw)1118 gtk_scrolled_window_check_attach_pan_gesture (GtkScrolledWindow *sw)
1119 {
1120 GtkPropagationPhase phase = GTK_PHASE_NONE;
1121 GtkScrolledWindowPrivate *priv = sw->priv;
1122
1123 if (priv->kinetic_scrolling &&
1124 ((may_hscroll (sw) && !may_vscroll (sw)) ||
1125 (!may_hscroll (sw) && may_vscroll (sw))))
1126 {
1127 GtkOrientation orientation;
1128
1129 if (may_hscroll (sw))
1130 orientation = GTK_ORIENTATION_HORIZONTAL;
1131 else
1132 orientation = GTK_ORIENTATION_VERTICAL;
1133
1134 gtk_gesture_pan_set_orientation (GTK_GESTURE_PAN (priv->pan_gesture),
1135 orientation);
1136 phase = GTK_PHASE_CAPTURE;
1137 }
1138
1139 gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (priv->pan_gesture), phase);
1140 }
1141
1142 static void
indicator_set_over(Indicator * indicator,gboolean over)1143 indicator_set_over (Indicator *indicator,
1144 gboolean over)
1145 {
1146 GtkStyleContext *context;
1147
1148 if (indicator->over_timeout_id)
1149 {
1150 g_source_remove (indicator->over_timeout_id);
1151 indicator->over_timeout_id = 0;
1152 }
1153
1154 if (indicator->over == over)
1155 return;
1156
1157 context = gtk_widget_get_style_context (indicator->scrollbar);
1158 indicator->over = over;
1159
1160 if (indicator->over)
1161 gtk_style_context_add_class (context, "hovering");
1162 else
1163 gtk_style_context_remove_class (context, "hovering");
1164
1165 gtk_widget_queue_resize (indicator->scrollbar);
1166 }
1167
1168 static void
translate_to_widget(GtkWidget * widget,GdkEvent * event,gint * x,gint * y)1169 translate_to_widget (GtkWidget *widget,
1170 GdkEvent *event,
1171 gint *x,
1172 gint *y)
1173 {
1174 GtkWidget *event_widget;
1175 GdkWindow *event_widget_window;
1176 GdkWindow *window;
1177 gdouble event_x, event_y;
1178 gint wx, wy;
1179 GtkAllocation allocation;
1180
1181 event_widget = gtk_get_event_widget (event);
1182 event_widget_window = gtk_widget_get_window (event_widget);
1183 gdk_event_get_coords (event, &event_x, &event_y);
1184 window = event->any.window;
1185 while (window && window != event_widget_window)
1186 {
1187 gdk_window_get_position (window, &wx, &wy);
1188 event_x += wx;
1189 event_y += wy;
1190 window = gdk_window_get_effective_parent (window);
1191 }
1192
1193 if (!gtk_widget_get_has_window (event_widget))
1194 {
1195 gtk_widget_get_allocation (event_widget, &allocation);
1196 event_x -= allocation.x;
1197 event_y -= allocation.y;
1198 }
1199
1200 gtk_widget_translate_coordinates (event_widget, widget,
1201 (gint)event_x, (gint)event_y,
1202 x, y);
1203 }
1204
1205 static gboolean
event_close_to_indicator(GtkScrolledWindow * sw,Indicator * indicator,GdkEvent * event)1206 event_close_to_indicator (GtkScrolledWindow *sw,
1207 Indicator *indicator,
1208 GdkEvent *event)
1209 {
1210 GtkScrolledWindowPrivate *priv;
1211 GtkAllocation indicator_alloc;
1212 gint x, y;
1213 gint distance;
1214 gint win_x, win_y;
1215
1216 priv = sw->priv;
1217
1218 gtk_widget_get_allocation (indicator->scrollbar, &indicator_alloc);
1219 gdk_window_get_position (indicator->window, &win_x, &win_y);
1220 translate_to_widget (GTK_WIDGET (sw), event, &x, &y);
1221
1222 if (indicator->over)
1223 distance = INDICATOR_FAR_DISTANCE;
1224 else
1225 distance = INDICATOR_CLOSE_DISTANCE;
1226
1227 if (indicator == &priv->hindicator)
1228 {
1229 if (y >= win_y - distance &&
1230 y < win_y + indicator_alloc.height + distance)
1231 return TRUE;
1232 }
1233 else if (indicator == &priv->vindicator)
1234 {
1235 if (x >= win_x - distance &&
1236 x < win_x + indicator_alloc.width + distance)
1237 return TRUE;
1238 }
1239
1240 return FALSE;
1241 }
1242
1243 static gboolean
enable_over_timeout_cb(gpointer user_data)1244 enable_over_timeout_cb (gpointer user_data)
1245 {
1246 Indicator *indicator = user_data;
1247
1248 indicator_set_over (indicator, TRUE);
1249 return G_SOURCE_REMOVE;
1250 }
1251
1252 static gboolean
check_update_scrollbar_proximity(GtkScrolledWindow * sw,Indicator * indicator,GdkEvent * event)1253 check_update_scrollbar_proximity (GtkScrolledWindow *sw,
1254 Indicator *indicator,
1255 GdkEvent *event)
1256 {
1257 GtkScrolledWindowPrivate *priv = sw->priv;
1258 gboolean indicator_close, on_scrollbar, on_other_scrollbar;
1259 GtkWidget *event_widget;
1260
1261 event_widget = gtk_get_event_widget (event);
1262
1263 indicator_close = event_close_to_indicator (sw, indicator, event);
1264 on_scrollbar = (event_widget == indicator->scrollbar &&
1265 event->type != GDK_LEAVE_NOTIFY);
1266 on_other_scrollbar = (!on_scrollbar &&
1267 event->type != GDK_LEAVE_NOTIFY &&
1268 (event_widget == priv->hindicator.scrollbar ||
1269 event_widget == priv->vindicator.scrollbar));
1270
1271 if (indicator->over_timeout_id)
1272 {
1273 g_source_remove (indicator->over_timeout_id);
1274 indicator->over_timeout_id = 0;
1275 }
1276
1277 if (on_scrollbar)
1278 indicator_set_over (indicator, TRUE);
1279 else if (indicator_close && !on_other_scrollbar)
1280 indicator->over_timeout_id = gdk_threads_add_timeout (30, enable_over_timeout_cb, indicator);
1281 else
1282 indicator_set_over (indicator, FALSE);
1283
1284 return indicator_close;
1285 }
1286
1287 static gdouble
get_scroll_unit(GtkScrolledWindow * sw,GtkOrientation orientation)1288 get_scroll_unit (GtkScrolledWindow *sw,
1289 GtkOrientation orientation)
1290 {
1291 gdouble scroll_unit;
1292
1293 #ifndef GDK_WINDOWING_QUARTZ
1294 GtkScrolledWindowPrivate *priv = sw->priv;
1295 GtkRange *scrollbar;
1296 GtkAdjustment *adj;
1297 gdouble page_size;
1298 gdouble pow_unit;
1299
1300 if (orientation == GTK_ORIENTATION_HORIZONTAL)
1301 scrollbar = GTK_RANGE (priv->hscrollbar);
1302 else
1303 scrollbar = GTK_RANGE (priv->vscrollbar);
1304
1305 if (!scrollbar)
1306 return 0;
1307
1308 adj = gtk_range_get_adjustment (scrollbar);
1309 page_size = gtk_adjustment_get_page_size (adj);
1310
1311 /* see comment in _gtk_range_get_wheel_delta() */
1312 pow_unit = pow (page_size, 2.0 / 3.0);
1313 scroll_unit = MIN (pow_unit, page_size / 2.0);
1314 #else
1315 scroll_unit = 1;
1316 #endif
1317
1318 return scroll_unit;
1319 }
1320
1321 static void
scroll_history_push(GtkScrolledWindow * sw,GdkEventScroll * event,gboolean shifted)1322 scroll_history_push (GtkScrolledWindow *sw,
1323 GdkEventScroll *event,
1324 gboolean shifted)
1325 {
1326 GtkScrolledWindowPrivate *priv = sw->priv;
1327 ScrollHistoryElem new_item;
1328 guint i;
1329
1330 if (event->direction != GDK_SCROLL_SMOOTH)
1331 return;
1332
1333 for (i = 0; i < priv->scroll_history->len; i++)
1334 {
1335 ScrollHistoryElem *elem;
1336
1337 elem = &g_array_index (priv->scroll_history, ScrollHistoryElem, i);
1338
1339 if (elem->evtime >= event->time - SCROLL_CAPTURE_THRESHOLD_MS)
1340 break;
1341 }
1342
1343 if (i > 0)
1344 g_array_remove_range (priv->scroll_history, 0, i);
1345
1346 if (shifted)
1347 {
1348 new_item.dx = event->delta_y;
1349 new_item.dy = event->delta_x;
1350 }
1351 else
1352 {
1353 new_item.dx = event->delta_x;
1354 new_item.dy = event->delta_y;
1355 }
1356 new_item.evtime = event->time;
1357 g_array_append_val (priv->scroll_history, new_item);
1358 }
1359
1360 static void
scroll_history_reset(GtkScrolledWindow * sw)1361 scroll_history_reset (GtkScrolledWindow *sw)
1362 {
1363 GtkScrolledWindowPrivate *priv = sw->priv;
1364
1365 if (priv->scroll_history->len == 0)
1366 return;
1367
1368 g_array_remove_range (priv->scroll_history, 0,
1369 priv->scroll_history->len);
1370 }
1371
1372 static gboolean
scroll_history_finish(GtkScrolledWindow * sw,gdouble * velocity_x,gdouble * velocity_y)1373 scroll_history_finish (GtkScrolledWindow *sw,
1374 gdouble *velocity_x,
1375 gdouble *velocity_y)
1376 {
1377 GtkScrolledWindowPrivate *priv = sw->priv;
1378 gdouble accum_dx = 0, accum_dy = 0;
1379 guint32 first = 0, last = 0;
1380 gdouble xunit, yunit;
1381 guint i;
1382
1383 if (priv->scroll_history->len == 0)
1384 return FALSE;
1385
1386 for (i = 0; i < priv->scroll_history->len; i++)
1387 {
1388 ScrollHistoryElem *elem;
1389
1390 elem = &g_array_index (priv->scroll_history, ScrollHistoryElem, i);
1391 accum_dx += elem->dx;
1392 accum_dy += elem->dy;
1393 last = elem->evtime;
1394
1395 if (i == 0)
1396 first = elem->evtime;
1397 }
1398
1399 if (last == first)
1400 {
1401 scroll_history_reset (sw);
1402 return FALSE;
1403 }
1404
1405 xunit = get_scroll_unit (sw, GTK_ORIENTATION_HORIZONTAL);
1406 yunit = get_scroll_unit (sw, GTK_ORIENTATION_VERTICAL);
1407 *velocity_x = (accum_dx * 1000 * xunit) / (last - first);
1408 *velocity_y = (accum_dy * 1000 * yunit) / (last - first);
1409 scroll_history_reset (sw);
1410
1411 return TRUE;
1412 }
1413
1414 static void uninstall_scroll_cursor (GtkScrolledWindow *scrolled_window);
1415
1416 static gboolean
captured_event_cb(GtkWidget * widget,GdkEvent * event)1417 captured_event_cb (GtkWidget *widget,
1418 GdkEvent *event)
1419 {
1420 GtkScrolledWindowPrivate *priv;
1421 GtkScrolledWindow *sw;
1422 GdkInputSource input_source;
1423 GdkDevice *source_device;
1424 GtkWidget *event_widget;
1425 gboolean on_scrollbar;
1426
1427 sw = GTK_SCROLLED_WINDOW (widget);
1428 priv = sw->priv;
1429 source_device = gdk_event_get_source_device (event);
1430
1431 if (event->type == GDK_SCROLL)
1432 {
1433 GtkWidget *scrollable_child = gtk_bin_get_child (GTK_BIN (widget));
1434
1435 gtk_scrolled_window_cancel_deceleration (sw);
1436
1437 /* If a nested widget takes over the scroll, unset our scrolling cursor */
1438 if (gtk_get_event_widget (event) != scrollable_child)
1439 uninstall_scroll_cursor (sw);
1440
1441 return GDK_EVENT_PROPAGATE;
1442 }
1443
1444 if (!priv->use_indicators)
1445 return GDK_EVENT_PROPAGATE;
1446
1447 if (event->type != GDK_MOTION_NOTIFY &&
1448 event->type != GDK_LEAVE_NOTIFY)
1449 return GDK_EVENT_PROPAGATE;
1450
1451 input_source = gdk_device_get_source (source_device);
1452
1453 if (input_source == GDK_SOURCE_KEYBOARD ||
1454 input_source == GDK_SOURCE_TOUCHSCREEN)
1455 return GDK_EVENT_PROPAGATE;
1456
1457 event_widget = gtk_get_event_widget (event);
1458 on_scrollbar = (event_widget == priv->hindicator.scrollbar ||
1459 event_widget == priv->vindicator.scrollbar);
1460
1461 if (event->type == GDK_MOTION_NOTIFY)
1462 {
1463 if (priv->hscrollbar_visible)
1464 indicator_start_fade (&priv->hindicator, 1.0);
1465 if (priv->vscrollbar_visible)
1466 indicator_start_fade (&priv->vindicator, 1.0);
1467
1468 if (!on_scrollbar &&
1469 (event->motion.state &
1470 (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK)) != 0)
1471 {
1472 indicator_set_over (&priv->hindicator, FALSE);
1473 indicator_set_over (&priv->vindicator, FALSE);
1474 }
1475 else if (input_source == GDK_SOURCE_PEN ||
1476 input_source == GDK_SOURCE_ERASER ||
1477 input_source == GDK_SOURCE_TRACKPOINT)
1478 {
1479 indicator_set_over (&priv->hindicator, TRUE);
1480 indicator_set_over (&priv->vindicator, TRUE);
1481 }
1482 else
1483 {
1484 if (!check_update_scrollbar_proximity (sw, &priv->vindicator, event))
1485 check_update_scrollbar_proximity (sw, &priv->hindicator, event);
1486 else
1487 indicator_set_over (&priv->hindicator, FALSE);
1488 }
1489 }
1490 else if (event->type == GDK_LEAVE_NOTIFY && on_scrollbar &&
1491 event->crossing.mode == GDK_CROSSING_UNGRAB)
1492 {
1493 check_update_scrollbar_proximity (sw, &priv->vindicator, event);
1494 check_update_scrollbar_proximity (sw, &priv->hindicator, event);
1495 }
1496
1497 return GDK_EVENT_PROPAGATE;
1498 }
1499
1500 /*
1501 * _gtk_scrolled_window_get_spacing:
1502 * @scrolled_window: a scrolled window
1503 *
1504 * Gets the spacing between the scrolled window’s scrollbars and
1505 * the scrolled widget. Used by GtkCombo
1506 *
1507 * Returns: the spacing, in pixels.
1508 */
1509 static gint
_gtk_scrolled_window_get_scrollbar_spacing(GtkScrolledWindow * scrolled_window)1510 _gtk_scrolled_window_get_scrollbar_spacing (GtkScrolledWindow *scrolled_window)
1511 {
1512 GtkScrolledWindowClass *class;
1513
1514 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), 0);
1515
1516 class = GTK_SCROLLED_WINDOW_GET_CLASS (scrolled_window);
1517
1518 if (class->scrollbar_spacing >= 0)
1519 return class->scrollbar_spacing;
1520 else
1521 {
1522 gint scrollbar_spacing;
1523
1524 gtk_widget_style_get (GTK_WIDGET (scrolled_window),
1525 "scrollbar-spacing", &scrollbar_spacing,
1526 NULL);
1527
1528 return scrollbar_spacing;
1529 }
1530 }
1531
1532 static void
gtk_scrolled_window_allocate(GtkCssGadget * gadget,const GtkAllocation * allocation,int baseline,GtkAllocation * out_clip,gpointer data)1533 gtk_scrolled_window_allocate (GtkCssGadget *gadget,
1534 const GtkAllocation *allocation,
1535 int baseline,
1536 GtkAllocation *out_clip,
1537 gpointer data)
1538 {
1539 GtkWidget *widget = gtk_css_gadget_get_owner (gadget);
1540 GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
1541 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
1542 GtkBin *bin;
1543 GtkAllocation relative_allocation;
1544 GtkAllocation child_allocation;
1545 GtkWidget *child;
1546 gint sb_spacing;
1547 gint sb_width;
1548 gint sb_height;
1549
1550 bin = GTK_BIN (scrolled_window);
1551
1552 /* Get possible scrollbar dimensions */
1553 sb_spacing = _gtk_scrolled_window_get_scrollbar_spacing (scrolled_window);
1554 gtk_widget_get_preferred_height (priv->hscrollbar, &sb_height, NULL);
1555 gtk_widget_get_preferred_width (priv->vscrollbar, &sb_width, NULL);
1556
1557 if (priv->hscrollbar_policy == GTK_POLICY_ALWAYS)
1558 priv->hscrollbar_visible = TRUE;
1559 else if (priv->hscrollbar_policy == GTK_POLICY_NEVER ||
1560 priv->hscrollbar_policy == GTK_POLICY_EXTERNAL)
1561 priv->hscrollbar_visible = FALSE;
1562
1563 if (priv->vscrollbar_policy == GTK_POLICY_ALWAYS)
1564 priv->vscrollbar_visible = TRUE;
1565 else if (priv->vscrollbar_policy == GTK_POLICY_NEVER ||
1566 priv->vscrollbar_policy == GTK_POLICY_EXTERNAL)
1567 priv->vscrollbar_visible = FALSE;
1568
1569 child = gtk_bin_get_child (bin);
1570 if (child && gtk_widget_get_visible (child))
1571 {
1572 gint child_scroll_width;
1573 gint child_scroll_height;
1574 gboolean previous_hvis;
1575 gboolean previous_vvis;
1576 guint count = 0;
1577 GtkScrollable *scrollable_child = GTK_SCROLLABLE (child);
1578 GtkScrollablePolicy hscroll_policy = gtk_scrollable_get_hscroll_policy (scrollable_child);
1579 GtkScrollablePolicy vscroll_policy = gtk_scrollable_get_vscroll_policy (scrollable_child);
1580
1581 /* Determine scrollbar visibility first via hfw apis */
1582 if (gtk_widget_get_request_mode (child) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH)
1583 {
1584 if (hscroll_policy == GTK_SCROLL_MINIMUM)
1585 gtk_widget_get_preferred_width (child, &child_scroll_width, NULL);
1586 else
1587 gtk_widget_get_preferred_width (child, NULL, &child_scroll_width);
1588
1589 if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
1590 {
1591 /* First try without a vertical scrollbar if the content will fit the height
1592 * given the extra width of the scrollbar */
1593 if (vscroll_policy == GTK_SCROLL_MINIMUM)
1594 gtk_widget_get_preferred_height_for_width (child,
1595 MAX (allocation->width, child_scroll_width),
1596 &child_scroll_height, NULL);
1597 else
1598 gtk_widget_get_preferred_height_for_width (child,
1599 MAX (allocation->width, child_scroll_width),
1600 NULL, &child_scroll_height);
1601
1602 if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
1603 {
1604 /* Does the content height fit the allocation height ? */
1605 priv->vscrollbar_visible = child_scroll_height > allocation->height;
1606
1607 /* Does the content width fit the allocation with minus a possible scrollbar ? */
1608 priv->hscrollbar_visible =
1609 child_scroll_width > allocation->width -
1610 (priv->vscrollbar_visible && !priv->use_indicators ? sb_width + sb_spacing : 0);
1611
1612 /* Now that we've guessed the hscrollbar, does the content height fit
1613 * the possible new allocation height ?
1614 */
1615 priv->vscrollbar_visible =
1616 child_scroll_height > allocation->height -
1617 (priv->hscrollbar_visible && !priv->use_indicators ? sb_height + sb_spacing : 0);
1618
1619 /* Now that we've guessed the vscrollbar, does the content width fit
1620 * the possible new allocation width ?
1621 */
1622 priv->hscrollbar_visible =
1623 child_scroll_width > allocation->width -
1624 (priv->vscrollbar_visible && !priv->use_indicators ? sb_width + sb_spacing : 0);
1625 }
1626 else /* priv->hscrollbar_policy != GTK_POLICY_AUTOMATIC */
1627 {
1628 priv->hscrollbar_visible = policy_may_be_visible (priv->hscrollbar_policy);
1629 priv->vscrollbar_visible = child_scroll_height > allocation->height -
1630 (priv->hscrollbar_visible && !priv->use_indicators ? sb_height + sb_spacing : 0);
1631 }
1632 }
1633 else /* priv->vscrollbar_policy != GTK_POLICY_AUTOMATIC */
1634 {
1635 priv->vscrollbar_visible = policy_may_be_visible (priv->vscrollbar_policy);
1636
1637 if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
1638 priv->hscrollbar_visible =
1639 child_scroll_width > allocation->width -
1640 (priv->vscrollbar_visible && !priv->use_indicators ? 0 : sb_width + sb_spacing);
1641 else
1642 priv->hscrollbar_visible = policy_may_be_visible (priv->hscrollbar_policy);
1643 }
1644 }
1645 else /* GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT */
1646 {
1647 if (vscroll_policy == GTK_SCROLL_MINIMUM)
1648 gtk_widget_get_preferred_height (child, &child_scroll_height, NULL);
1649 else
1650 gtk_widget_get_preferred_height (child, NULL, &child_scroll_height);
1651
1652 if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
1653 {
1654 /* First try without a horizontal scrollbar if the content will fit the width
1655 * given the extra height of the scrollbar */
1656 if (hscroll_policy == GTK_SCROLL_MINIMUM)
1657 gtk_widget_get_preferred_width_for_height (child,
1658 MAX (allocation->height, child_scroll_height),
1659 &child_scroll_width, NULL);
1660 else
1661 gtk_widget_get_preferred_width_for_height (child,
1662 MAX (allocation->height, child_scroll_height),
1663 NULL, &child_scroll_width);
1664
1665 if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
1666 {
1667 /* Does the content width fit the allocation width ? */
1668 priv->hscrollbar_visible = child_scroll_width > allocation->width;
1669
1670 /* Does the content height fit the allocation with minus a possible scrollbar ? */
1671 priv->vscrollbar_visible =
1672 child_scroll_height > allocation->height -
1673 (priv->hscrollbar_visible && !priv->use_indicators ? sb_height + sb_spacing : 0);
1674
1675 /* Now that we've guessed the vscrollbar, does the content width fit
1676 * the possible new allocation width ?
1677 */
1678 priv->hscrollbar_visible =
1679 child_scroll_width > allocation->width -
1680 (priv->vscrollbar_visible && !priv->use_indicators ? sb_width + sb_spacing : 0);
1681
1682 /* Now that we've guessed the hscrollbar, does the content height fit
1683 * the possible new allocation height ?
1684 */
1685 priv->vscrollbar_visible =
1686 child_scroll_height > allocation->height -
1687 (priv->hscrollbar_visible && !priv->use_indicators ? sb_height + sb_spacing : 0);
1688 }
1689 else /* priv->vscrollbar_policy != GTK_POLICY_AUTOMATIC */
1690 {
1691 priv->vscrollbar_visible = policy_may_be_visible (priv->vscrollbar_policy);
1692 priv->hscrollbar_visible = child_scroll_width > allocation->width -
1693 (priv->vscrollbar_visible && !priv->use_indicators ? sb_width + sb_spacing : 0);
1694 }
1695 }
1696 else /* priv->hscrollbar_policy != GTK_POLICY_AUTOMATIC */
1697 {
1698 priv->hscrollbar_visible = policy_may_be_visible (priv->hscrollbar_policy);
1699
1700 if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
1701 priv->vscrollbar_visible =
1702 child_scroll_height > allocation->height -
1703 (priv->hscrollbar_visible && !priv->use_indicators ? sb_height + sb_spacing : 0);
1704 else
1705 priv->vscrollbar_visible = policy_may_be_visible (priv->vscrollbar_policy);
1706 }
1707 }
1708
1709 /* Now after guessing scrollbar visibility; fall back on the allocation loop which
1710 * observes the adjustments to detect scrollbar visibility and also avoids
1711 * infinite recursion
1712 */
1713 do
1714 {
1715 previous_hvis = priv->hscrollbar_visible;
1716 previous_vvis = priv->vscrollbar_visible;
1717 gtk_scrolled_window_allocate_child (scrolled_window, &relative_allocation);
1718
1719 /* Explicitly force scrollbar visibility checks.
1720 *
1721 * Since we make a guess above, the child might not decide to update the adjustments
1722 * if they logically did not change since the last configuration
1723 */
1724 if (priv->hscrollbar)
1725 gtk_scrolled_window_adjustment_changed
1726 (gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar)), scrolled_window);
1727
1728 if (priv->vscrollbar)
1729 gtk_scrolled_window_adjustment_changed
1730 (gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar)), scrolled_window);
1731
1732 /* If, after the first iteration, the hscrollbar and the
1733 * vscrollbar flip visiblity... or if one of the scrollbars flip
1734 * on each itteration indefinitly/infinitely, then we just need both
1735 * at this size.
1736 */
1737 if ((count &&
1738 previous_hvis != priv->hscrollbar_visible &&
1739 previous_vvis != priv->vscrollbar_visible) || count > 3)
1740 {
1741 priv->hscrollbar_visible = TRUE;
1742 priv->vscrollbar_visible = TRUE;
1743
1744 gtk_scrolled_window_allocate_child (scrolled_window, &relative_allocation);
1745
1746 break;
1747 }
1748
1749 count++;
1750 }
1751 while (previous_hvis != priv->hscrollbar_visible ||
1752 previous_vvis != priv->vscrollbar_visible);
1753 }
1754 else
1755 {
1756 priv->hscrollbar_visible = priv->hscrollbar_policy == GTK_POLICY_ALWAYS;
1757 priv->vscrollbar_visible = priv->vscrollbar_policy == GTK_POLICY_ALWAYS;
1758 }
1759
1760 gtk_widget_set_child_visible (priv->hscrollbar, priv->hscrollbar_visible);
1761 if (priv->hscrollbar_visible)
1762 {
1763 gtk_scrolled_window_allocate_scrollbar (scrolled_window,
1764 priv->hscrollbar,
1765 &child_allocation);
1766 if (priv->use_indicators)
1767 {
1768 if (gtk_widget_get_realized (widget))
1769 gdk_window_move_resize (priv->hindicator.window,
1770 child_allocation.x,
1771 child_allocation.y,
1772 child_allocation.width,
1773 child_allocation.height);
1774 child_allocation.x = 0;
1775 child_allocation.y = 0;
1776 }
1777 gtk_widget_size_allocate (priv->hscrollbar, &child_allocation);
1778 }
1779
1780 gtk_widget_set_child_visible (priv->vscrollbar, priv->vscrollbar_visible);
1781 if (priv->vscrollbar_visible)
1782 {
1783 gtk_scrolled_window_allocate_scrollbar (scrolled_window,
1784 priv->vscrollbar,
1785 &child_allocation);
1786 if (priv->use_indicators)
1787 {
1788 if (gtk_widget_get_realized (widget))
1789 gdk_window_move_resize (priv->vindicator.window,
1790 child_allocation.x,
1791 child_allocation.y,
1792 child_allocation.width,
1793 child_allocation.height);
1794 child_allocation.x = 0;
1795 child_allocation.y = 0;
1796 }
1797 gtk_widget_size_allocate (priv->vscrollbar, &child_allocation);
1798 }
1799
1800 gtk_scrolled_window_check_attach_pan_gesture (scrolled_window);
1801 }
1802
1803 static void
gtk_scrolled_window_measure(GtkCssGadget * gadget,GtkOrientation orientation,int for_size,int * minimum_size,int * natural_size,int * minimum_baseline,int * natural_baseline,gpointer data)1804 gtk_scrolled_window_measure (GtkCssGadget *gadget,
1805 GtkOrientation orientation,
1806 int for_size,
1807 int *minimum_size,
1808 int *natural_size,
1809 int *minimum_baseline,
1810 int *natural_baseline,
1811 gpointer data)
1812 {
1813 GtkWidget *widget = gtk_css_gadget_get_owner (gadget);
1814 GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
1815 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
1816 GtkBin *bin = GTK_BIN (scrolled_window);
1817 gint scrollbar_spacing;
1818 GtkRequisition hscrollbar_requisition;
1819 GtkRequisition vscrollbar_requisition;
1820 GtkRequisition minimum_req, natural_req;
1821 GtkWidget *child;
1822 gint min_child_size, nat_child_size;
1823 GtkBorder sborder = { 0 };
1824
1825 scrollbar_spacing = _gtk_scrolled_window_get_scrollbar_spacing (scrolled_window);
1826
1827 minimum_req.width = 0;
1828 minimum_req.height = 0;
1829 natural_req.width = 0;
1830 natural_req.height = 0;
1831
1832 gtk_widget_get_preferred_size (priv->hscrollbar,
1833 &hscrollbar_requisition, NULL);
1834 gtk_widget_get_preferred_size (priv->vscrollbar,
1835 &vscrollbar_requisition, NULL);
1836
1837 child = gtk_bin_get_child (bin);
1838
1839 if (child)
1840 gtk_scrollable_get_border (GTK_SCROLLABLE (child), &sborder);
1841
1842 /*
1843 * First collect the child requisition
1844 */
1845 if (child && gtk_widget_get_visible (child))
1846 {
1847 if (orientation == GTK_ORIENTATION_HORIZONTAL)
1848 {
1849 gtk_widget_get_preferred_width (child,
1850 &min_child_size,
1851 &nat_child_size);
1852
1853 if (priv->propagate_natural_width)
1854 natural_req.width += nat_child_size;
1855
1856 if (priv->hscrollbar_policy == GTK_POLICY_NEVER)
1857 {
1858 minimum_req.width += min_child_size;
1859 }
1860 else
1861 {
1862 gint min = priv->min_content_width >= 0 ? priv->min_content_width : 0;
1863 gint max = priv->max_content_width >= 0 ? priv->max_content_width : G_MAXINT;
1864
1865 minimum_req.width = CLAMP (minimum_req.width, min, max);
1866 natural_req.width = CLAMP (natural_req.width, min, max);
1867 }
1868 }
1869 else /* GTK_ORIENTATION_VERTICAL */
1870 {
1871 gtk_widget_get_preferred_height (child,
1872 &min_child_size,
1873 &nat_child_size);
1874
1875 if (priv->propagate_natural_height)
1876 natural_req.height += nat_child_size;
1877
1878 if (priv->vscrollbar_policy == GTK_POLICY_NEVER)
1879 {
1880 minimum_req.height += min_child_size;
1881 }
1882 else
1883 {
1884 gint min = priv->min_content_height >= 0 ? priv->min_content_height : 0;
1885 gint max = priv->max_content_height >= 0 ? priv->max_content_height : G_MAXINT;
1886
1887 minimum_req.height = CLAMP (minimum_req.height, min, max);
1888 natural_req.height = CLAMP (natural_req.height, min, max);
1889 }
1890 }
1891 }
1892
1893 /* Ensure we make requests with natural size >= minimum size */
1894 natural_req.height = MAX (minimum_req.height, natural_req.height);
1895 natural_req.width = MAX (minimum_req.width, natural_req.width);
1896
1897 /*
1898 * Now add to the requisition any additional space for surrounding scrollbars
1899 * and the special scrollable border.
1900 */
1901 if (policy_may_be_visible (priv->hscrollbar_policy))
1902 {
1903 int vscrollbar_extra_size;
1904
1905 if (!priv->use_indicators && policy_may_be_visible (priv->vscrollbar_policy))
1906 vscrollbar_extra_size = vscrollbar_requisition.width;
1907 else
1908 vscrollbar_extra_size = 0;
1909
1910 minimum_req.width = MAX (minimum_req.width,
1911 hscrollbar_requisition.width + sborder.left + sborder.right + vscrollbar_extra_size);
1912 natural_req.width = MAX (natural_req.width,
1913 hscrollbar_requisition.width + sborder.left + sborder.right + vscrollbar_extra_size);
1914
1915 if (!priv->use_indicators)
1916 {
1917 minimum_req.height += scrollbar_spacing + hscrollbar_requisition.height;
1918 natural_req.height += scrollbar_spacing + hscrollbar_requisition.height;
1919 }
1920 }
1921
1922 if (policy_may_be_visible (priv->vscrollbar_policy))
1923 {
1924 int hscrollbar_extra_size;
1925
1926 if (!priv->use_indicators && policy_may_be_visible (priv->hscrollbar_policy))
1927 hscrollbar_extra_size = hscrollbar_requisition.height;
1928 else
1929 hscrollbar_extra_size = 0;
1930
1931 minimum_req.height = MAX (minimum_req.height,
1932 vscrollbar_requisition.height + sborder.top + sborder.bottom + hscrollbar_extra_size);
1933 natural_req.height = MAX (natural_req.height,
1934 vscrollbar_requisition.height + sborder.top + sborder.bottom + hscrollbar_extra_size);
1935
1936 if (!priv->use_indicators)
1937 {
1938 minimum_req.width += scrollbar_spacing + vscrollbar_requisition.width;
1939 natural_req.width += scrollbar_spacing + vscrollbar_requisition.width;
1940 }
1941 }
1942
1943 if (orientation == GTK_ORIENTATION_HORIZONTAL)
1944 {
1945 *minimum_size = minimum_req.width;
1946 *natural_size = natural_req.width;
1947 }
1948 else
1949 {
1950 *minimum_size = minimum_req.height;
1951 *natural_size = natural_req.height;
1952 }
1953 }
1954
1955 static void
gtk_scrolled_window_draw_scrollbars_junction(GtkScrolledWindow * scrolled_window,cairo_t * cr)1956 gtk_scrolled_window_draw_scrollbars_junction (GtkScrolledWindow *scrolled_window,
1957 cairo_t *cr)
1958 {
1959 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
1960 GtkWidget *widget = GTK_WIDGET (scrolled_window);
1961 GtkAllocation content_allocation, hscr_allocation, vscr_allocation;
1962 GtkStyleContext *context;
1963 GdkRectangle junction_rect;
1964 gboolean is_rtl;
1965
1966 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
1967 gtk_widget_get_allocation (GTK_WIDGET (priv->hscrollbar), &hscr_allocation);
1968 gtk_widget_get_allocation (GTK_WIDGET (priv->vscrollbar), &vscr_allocation);
1969 gtk_css_gadget_get_content_allocation (priv->gadget, &content_allocation,
1970 NULL);
1971
1972 junction_rect.x = content_allocation.x;
1973 junction_rect.y = content_allocation.y;
1974 junction_rect.width = vscr_allocation.width;
1975 junction_rect.height = hscr_allocation.height;
1976
1977 if ((is_rtl &&
1978 (priv->window_placement == GTK_CORNER_TOP_RIGHT ||
1979 priv->window_placement == GTK_CORNER_BOTTOM_RIGHT)) ||
1980 (!is_rtl &&
1981 (priv->window_placement == GTK_CORNER_TOP_LEFT ||
1982 priv->window_placement == GTK_CORNER_BOTTOM_LEFT)))
1983 junction_rect.x += hscr_allocation.width;
1984
1985 if (priv->window_placement == GTK_CORNER_TOP_LEFT ||
1986 priv->window_placement == GTK_CORNER_TOP_RIGHT)
1987 junction_rect.y += vscr_allocation.height;
1988
1989 context = gtk_widget_get_style_context (widget);
1990 gtk_style_context_save_named (context, "junction");
1991
1992 gtk_render_background (context, cr,
1993 junction_rect.x, junction_rect.y,
1994 junction_rect.width, junction_rect.height);
1995 gtk_render_frame (context, cr,
1996 junction_rect.x, junction_rect.y,
1997 junction_rect.width, junction_rect.height);
1998
1999 gtk_style_context_restore (context);
2000 }
2001
2002 static void
gtk_scrolled_window_draw_overshoot(GtkScrolledWindow * scrolled_window,cairo_t * cr)2003 gtk_scrolled_window_draw_overshoot (GtkScrolledWindow *scrolled_window,
2004 cairo_t *cr)
2005 {
2006 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
2007 GtkWidget *widget = GTK_WIDGET (scrolled_window);
2008 gint overshoot_x, overshoot_y;
2009 GtkStyleContext *context;
2010 GdkRectangle rect;
2011
2012 if (!_gtk_scrolled_window_get_overshoot (scrolled_window, &overshoot_x, &overshoot_y))
2013 return;
2014
2015 context = gtk_widget_get_style_context (widget);
2016 gtk_scrolled_window_inner_allocation (widget, &rect);
2017
2018 overshoot_x = CLAMP (overshoot_x, - MAX_OVERSHOOT_DISTANCE, MAX_OVERSHOOT_DISTANCE);
2019 overshoot_y = CLAMP (overshoot_y, - MAX_OVERSHOOT_DISTANCE, MAX_OVERSHOOT_DISTANCE);
2020
2021 if (overshoot_x > 0)
2022 {
2023 gtk_style_context_save_to_node (context, priv->overshoot_node[GTK_POS_RIGHT]);
2024 gtk_render_background (context, cr, rect.x + rect.width - overshoot_x, rect.y, overshoot_x, rect.height);
2025 gtk_render_frame (context, cr, rect.x + rect.width - overshoot_x, rect.y, overshoot_x, rect.height);
2026 gtk_style_context_restore (context);
2027 }
2028 else if (overshoot_x < 0)
2029 {
2030 gtk_style_context_save_to_node (context, priv->overshoot_node[GTK_POS_LEFT]);
2031 gtk_render_background (context, cr, rect.x, rect.y, -overshoot_x, rect.height);
2032 gtk_render_frame (context, cr, rect.x, rect.y, -overshoot_x, rect.height);
2033 gtk_style_context_restore (context);
2034 }
2035
2036 if (overshoot_y > 0)
2037 {
2038 gtk_style_context_save_to_node (context, priv->overshoot_node[GTK_POS_BOTTOM]);
2039 gtk_render_background (context, cr, rect.x, rect.y + rect.height - overshoot_y, rect.width, overshoot_y);
2040 gtk_render_frame (context, cr, rect.x, rect.y + rect.height - overshoot_y, rect.width, overshoot_y);
2041 gtk_style_context_restore (context);
2042 }
2043 else if (overshoot_y < 0)
2044 {
2045 gtk_style_context_save_to_node (context, priv->overshoot_node[GTK_POS_TOP]);
2046 gtk_render_background (context, cr, rect.x, rect.y, rect.width, -overshoot_y);
2047 gtk_render_frame (context, cr, rect.x, rect.y, rect.width, -overshoot_y);
2048 gtk_style_context_restore (context);
2049 }
2050 }
2051
2052 static void
gtk_scrolled_window_draw_undershoot(GtkScrolledWindow * scrolled_window,cairo_t * cr)2053 gtk_scrolled_window_draw_undershoot (GtkScrolledWindow *scrolled_window,
2054 cairo_t *cr)
2055 {
2056 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
2057 GtkWidget *widget = GTK_WIDGET (scrolled_window);
2058 GtkStyleContext *context;
2059 GdkRectangle rect;
2060 GtkAdjustment *adj;
2061
2062 context = gtk_widget_get_style_context (widget);
2063 gtk_scrolled_window_inner_allocation (widget, &rect);
2064
2065 adj = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
2066 if (gtk_adjustment_get_value (adj) < gtk_adjustment_get_upper (adj) - gtk_adjustment_get_page_size (adj))
2067 {
2068 gtk_style_context_save_to_node (context, priv->undershoot_node[GTK_POS_RIGHT]);
2069 gtk_render_background (context, cr, rect.x + rect.width - UNDERSHOOT_SIZE, rect.y, UNDERSHOOT_SIZE, rect.height);
2070 gtk_render_frame (context, cr, rect.x + rect.width - UNDERSHOOT_SIZE, rect.y, UNDERSHOOT_SIZE, rect.height);
2071
2072 gtk_style_context_restore (context);
2073 }
2074 if (gtk_adjustment_get_value (adj) > gtk_adjustment_get_lower (adj))
2075 {
2076 gtk_style_context_save_to_node (context, priv->undershoot_node[GTK_POS_LEFT]);
2077 gtk_render_background (context, cr, rect.x, rect.y, UNDERSHOOT_SIZE, rect.height);
2078 gtk_render_frame (context, cr, rect.x, rect.y, UNDERSHOOT_SIZE, rect.height);
2079 gtk_style_context_restore (context);
2080 }
2081
2082 adj = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar));
2083 if (gtk_adjustment_get_value (adj) < gtk_adjustment_get_upper (adj) - gtk_adjustment_get_page_size (adj))
2084 {
2085 gtk_style_context_save_to_node (context, priv->undershoot_node[GTK_POS_BOTTOM]);
2086 gtk_render_background (context, cr, rect.x, rect.y + rect.height - UNDERSHOOT_SIZE, rect.width, UNDERSHOOT_SIZE);
2087 gtk_render_frame (context, cr, rect.x, rect.y + rect.height - UNDERSHOOT_SIZE, rect.width, UNDERSHOOT_SIZE);
2088 gtk_style_context_restore (context);
2089 }
2090 if (gtk_adjustment_get_value (adj) > gtk_adjustment_get_lower (adj))
2091 {
2092 gtk_style_context_save_to_node (context, priv->undershoot_node[GTK_POS_TOP]);
2093 gtk_render_background (context, cr, rect.x, rect.y, rect.width, UNDERSHOOT_SIZE);
2094 gtk_render_frame (context, cr, rect.x, rect.y, rect.width, UNDERSHOOT_SIZE);
2095 gtk_style_context_restore (context);
2096 }
2097 }
2098
2099 static gboolean
gtk_scrolled_window_render(GtkCssGadget * gadget,cairo_t * cr,int x,int y,int width,int height,gpointer data)2100 gtk_scrolled_window_render (GtkCssGadget *gadget,
2101 cairo_t *cr,
2102 int x,
2103 int y,
2104 int width,
2105 int height,
2106 gpointer data)
2107 {
2108 GtkWidget *widget = gtk_css_gadget_get_owner (gadget);
2109 GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
2110 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
2111
2112 if (gtk_cairo_should_draw_window (cr, gtk_widget_get_window (widget)))
2113 {
2114 if (priv->hscrollbar_visible &&
2115 priv->vscrollbar_visible)
2116 gtk_scrolled_window_draw_scrollbars_junction (scrolled_window, cr);
2117 }
2118
2119 GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->draw (widget, cr);
2120
2121 if (gtk_cairo_should_draw_window (cr, gtk_widget_get_window (widget)))
2122 {
2123 gtk_scrolled_window_draw_undershoot (scrolled_window, cr);
2124 gtk_scrolled_window_draw_overshoot (scrolled_window, cr);
2125 }
2126
2127 return FALSE;
2128 }
2129
2130 static void
gtk_scrolled_window_init(GtkScrolledWindow * scrolled_window)2131 gtk_scrolled_window_init (GtkScrolledWindow *scrolled_window)
2132 {
2133 GtkWidget *widget = GTK_WIDGET (scrolled_window);
2134 GtkScrolledWindowPrivate *priv;
2135 GtkCssNode *widget_node;
2136 GQuark classes[4] = {
2137 g_quark_from_static_string (GTK_STYLE_CLASS_LEFT),
2138 g_quark_from_static_string (GTK_STYLE_CLASS_RIGHT),
2139 g_quark_from_static_string (GTK_STYLE_CLASS_TOP),
2140 g_quark_from_static_string (GTK_STYLE_CLASS_BOTTOM),
2141 };
2142 gint i;
2143
2144 scrolled_window->priv = priv =
2145 gtk_scrolled_window_get_instance_private (scrolled_window);
2146
2147 gtk_widget_set_has_window (widget, TRUE);
2148 gtk_widget_set_can_focus (widget, TRUE);
2149
2150 /* Instantiated by gtk_scrolled_window_set_[hv]adjustment
2151 * which are both construct properties
2152 */
2153 priv->hscrollbar = NULL;
2154 priv->vscrollbar = NULL;
2155 priv->hscrollbar_policy = GTK_POLICY_AUTOMATIC;
2156 priv->vscrollbar_policy = GTK_POLICY_AUTOMATIC;
2157 priv->hscrollbar_visible = FALSE;
2158 priv->vscrollbar_visible = FALSE;
2159 priv->focus_out = FALSE;
2160 priv->auto_added_viewport = FALSE;
2161 priv->window_placement = GTK_CORNER_TOP_LEFT;
2162 priv->min_content_width = -1;
2163 priv->min_content_height = -1;
2164 priv->max_content_width = -1;
2165 priv->max_content_height = -1;
2166
2167 priv->overlay_scrolling = TRUE;
2168
2169 priv->drag_gesture = gtk_gesture_drag_new (widget);
2170 gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (priv->drag_gesture), TRUE);
2171 g_signal_connect_swapped (priv->drag_gesture, "drag-begin",
2172 G_CALLBACK (scrolled_window_drag_begin_cb),
2173 scrolled_window);
2174 g_signal_connect_swapped (priv->drag_gesture, "drag-update",
2175 G_CALLBACK (scrolled_window_drag_update_cb),
2176 scrolled_window);
2177 g_signal_connect_swapped (priv->drag_gesture, "end",
2178 G_CALLBACK (scrolled_window_drag_end_cb),
2179 scrolled_window);
2180
2181 priv->pan_gesture = gtk_gesture_pan_new (widget, GTK_ORIENTATION_VERTICAL);
2182 gtk_gesture_group (priv->pan_gesture, priv->drag_gesture);
2183 gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (priv->pan_gesture), TRUE);
2184
2185 priv->swipe_gesture = gtk_gesture_swipe_new (widget);
2186 gtk_gesture_group (priv->swipe_gesture, priv->drag_gesture);
2187 gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (priv->swipe_gesture), TRUE);
2188 g_signal_connect_swapped (priv->swipe_gesture, "swipe",
2189 G_CALLBACK (scrolled_window_swipe_cb),
2190 scrolled_window);
2191 priv->long_press_gesture = gtk_gesture_long_press_new (widget);
2192 gtk_gesture_group (priv->long_press_gesture, priv->drag_gesture);
2193 gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (priv->long_press_gesture), TRUE);
2194 g_signal_connect_swapped (priv->long_press_gesture, "pressed",
2195 G_CALLBACK (scrolled_window_long_press_cb),
2196 scrolled_window);
2197 g_signal_connect_swapped (priv->long_press_gesture, "cancelled",
2198 G_CALLBACK (scrolled_window_long_press_cancelled_cb),
2199 scrolled_window);
2200
2201 priv->scroll_history = g_array_new (FALSE, FALSE, sizeof (ScrollHistoryElem));
2202
2203 gtk_scrolled_window_set_kinetic_scrolling (scrolled_window, TRUE);
2204 gtk_scrolled_window_set_capture_button_press (scrolled_window, TRUE);
2205
2206 _gtk_widget_set_captured_event_handler (widget, captured_event_cb);
2207
2208 widget_node = gtk_widget_get_css_node (widget);
2209 priv->gadget = gtk_css_custom_gadget_new_for_node (widget_node,
2210 widget,
2211 gtk_scrolled_window_measure,
2212 gtk_scrolled_window_allocate,
2213 gtk_scrolled_window_render,
2214 NULL, NULL);
2215 for (i = 0; i < 4; i++)
2216 {
2217 priv->overshoot_node[i] = gtk_css_node_new ();
2218 gtk_css_node_set_name (priv->overshoot_node[i], I_("overshoot"));
2219 gtk_css_node_add_class (priv->overshoot_node[i], classes[i]);
2220 gtk_css_node_set_parent (priv->overshoot_node[i], widget_node);
2221 gtk_css_node_set_state (priv->overshoot_node[i], gtk_css_node_get_state (widget_node));
2222 g_object_unref (priv->overshoot_node[i]);
2223
2224 priv->undershoot_node[i] = gtk_css_node_new ();
2225 gtk_css_node_set_name (priv->undershoot_node[i], I_("undershoot"));
2226 gtk_css_node_add_class (priv->undershoot_node[i], classes[i]);
2227 gtk_css_node_set_parent (priv->undershoot_node[i], widget_node);
2228 gtk_css_node_set_state (priv->undershoot_node[i], gtk_css_node_get_state (widget_node));
2229 g_object_unref (priv->undershoot_node[i]);
2230 }
2231
2232 gtk_scrolled_window_update_use_indicators (scrolled_window);
2233 }
2234
2235 /**
2236 * gtk_scrolled_window_new:
2237 * @hadjustment: (nullable): horizontal adjustment
2238 * @vadjustment: (nullable): vertical adjustment
2239 *
2240 * Creates a new scrolled window.
2241 *
2242 * The two arguments are the scrolled window’s adjustments; these will be
2243 * shared with the scrollbars and the child widget to keep the bars in sync
2244 * with the child. Usually you want to pass %NULL for the adjustments, which
2245 * will cause the scrolled window to create them for you.
2246 *
2247 * Returns: a new scrolled window
2248 */
2249 GtkWidget*
gtk_scrolled_window_new(GtkAdjustment * hadjustment,GtkAdjustment * vadjustment)2250 gtk_scrolled_window_new (GtkAdjustment *hadjustment,
2251 GtkAdjustment *vadjustment)
2252 {
2253 GtkWidget *scrolled_window;
2254
2255 if (hadjustment)
2256 g_return_val_if_fail (GTK_IS_ADJUSTMENT (hadjustment), NULL);
2257
2258 if (vadjustment)
2259 g_return_val_if_fail (GTK_IS_ADJUSTMENT (vadjustment), NULL);
2260
2261 scrolled_window = g_object_new (GTK_TYPE_SCROLLED_WINDOW,
2262 "hadjustment", hadjustment,
2263 "vadjustment", vadjustment,
2264 NULL);
2265
2266 return scrolled_window;
2267 }
2268
2269 /**
2270 * gtk_scrolled_window_set_hadjustment:
2271 * @scrolled_window: a #GtkScrolledWindow
2272 * @hadjustment: (nullable): the #GtkAdjustment to use, or %NULL to create a new one
2273 *
2274 * Sets the #GtkAdjustment for the horizontal scrollbar.
2275 */
2276 void
gtk_scrolled_window_set_hadjustment(GtkScrolledWindow * scrolled_window,GtkAdjustment * hadjustment)2277 gtk_scrolled_window_set_hadjustment (GtkScrolledWindow *scrolled_window,
2278 GtkAdjustment *hadjustment)
2279 {
2280 GtkScrolledWindowPrivate *priv;
2281 GtkBin *bin;
2282 GtkWidget *child;
2283
2284 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
2285
2286 if (hadjustment)
2287 g_return_if_fail (GTK_IS_ADJUSTMENT (hadjustment));
2288 else
2289 hadjustment = (GtkAdjustment*) g_object_new (GTK_TYPE_ADJUSTMENT, NULL);
2290
2291 bin = GTK_BIN (scrolled_window);
2292 priv = scrolled_window->priv;
2293
2294 if (!priv->hscrollbar)
2295 {
2296 priv->hscrollbar = gtk_scrollbar_new (GTK_ORIENTATION_HORIZONTAL, hadjustment);
2297
2298 gtk_widget_set_parent (priv->hscrollbar, GTK_WIDGET (scrolled_window));
2299 gtk_widget_show (priv->hscrollbar);
2300 update_scrollbar_positions (scrolled_window);
2301 }
2302 else
2303 {
2304 GtkAdjustment *old_adjustment;
2305
2306 old_adjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
2307 if (old_adjustment == hadjustment)
2308 return;
2309
2310 g_signal_handlers_disconnect_by_func (old_adjustment,
2311 gtk_scrolled_window_adjustment_changed,
2312 scrolled_window);
2313 g_signal_handlers_disconnect_by_func (old_adjustment,
2314 gtk_scrolled_window_adjustment_value_changed,
2315 scrolled_window);
2316
2317 gtk_adjustment_enable_animation (old_adjustment, NULL, 0);
2318 gtk_range_set_adjustment (GTK_RANGE (priv->hscrollbar), hadjustment);
2319 }
2320
2321 hadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
2322
2323 g_signal_connect (hadjustment,
2324 "changed",
2325 G_CALLBACK (gtk_scrolled_window_adjustment_changed),
2326 scrolled_window);
2327 g_signal_connect (hadjustment,
2328 "value-changed",
2329 G_CALLBACK (gtk_scrolled_window_adjustment_value_changed),
2330 scrolled_window);
2331
2332 gtk_scrolled_window_adjustment_changed (hadjustment, scrolled_window);
2333 gtk_scrolled_window_adjustment_value_changed (hadjustment, scrolled_window);
2334
2335 child = gtk_bin_get_child (bin);
2336 if (child)
2337 gtk_scrollable_set_hadjustment (GTK_SCROLLABLE (child), hadjustment);
2338
2339 if (gtk_widget_should_animate (GTK_WIDGET (scrolled_window)))
2340 gtk_adjustment_enable_animation (hadjustment, gtk_widget_get_frame_clock (GTK_WIDGET (scrolled_window)), ANIMATION_DURATION);
2341
2342 g_object_notify_by_pspec (G_OBJECT (scrolled_window), properties[PROP_HADJUSTMENT]);
2343 }
2344
2345 /**
2346 * gtk_scrolled_window_set_vadjustment:
2347 * @scrolled_window: a #GtkScrolledWindow
2348 * @vadjustment: (nullable): the #GtkAdjustment to use, or %NULL to create a new one
2349 *
2350 * Sets the #GtkAdjustment for the vertical scrollbar.
2351 */
2352 void
gtk_scrolled_window_set_vadjustment(GtkScrolledWindow * scrolled_window,GtkAdjustment * vadjustment)2353 gtk_scrolled_window_set_vadjustment (GtkScrolledWindow *scrolled_window,
2354 GtkAdjustment *vadjustment)
2355 {
2356 GtkScrolledWindowPrivate *priv;
2357 GtkBin *bin;
2358 GtkWidget *child;
2359
2360 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
2361
2362 if (vadjustment)
2363 g_return_if_fail (GTK_IS_ADJUSTMENT (vadjustment));
2364 else
2365 vadjustment = (GtkAdjustment*) g_object_new (GTK_TYPE_ADJUSTMENT, NULL);
2366
2367 bin = GTK_BIN (scrolled_window);
2368 priv = scrolled_window->priv;
2369
2370 if (!priv->vscrollbar)
2371 {
2372 priv->vscrollbar = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, vadjustment);
2373
2374 gtk_widget_set_parent (priv->vscrollbar, GTK_WIDGET (scrolled_window));
2375 gtk_widget_show (priv->vscrollbar);
2376 update_scrollbar_positions (scrolled_window);
2377 }
2378 else
2379 {
2380 GtkAdjustment *old_adjustment;
2381
2382 old_adjustment = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar));
2383 if (old_adjustment == vadjustment)
2384 return;
2385
2386 g_signal_handlers_disconnect_by_func (old_adjustment,
2387 gtk_scrolled_window_adjustment_changed,
2388 scrolled_window);
2389 g_signal_handlers_disconnect_by_func (old_adjustment,
2390 gtk_scrolled_window_adjustment_value_changed,
2391 scrolled_window);
2392
2393 gtk_adjustment_enable_animation (old_adjustment, NULL, 0);
2394 gtk_range_set_adjustment (GTK_RANGE (priv->vscrollbar), vadjustment);
2395 }
2396
2397 vadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar));
2398
2399 g_signal_connect (vadjustment,
2400 "changed",
2401 G_CALLBACK (gtk_scrolled_window_adjustment_changed),
2402 scrolled_window);
2403 g_signal_connect (vadjustment,
2404 "value-changed",
2405 G_CALLBACK (gtk_scrolled_window_adjustment_value_changed),
2406 scrolled_window);
2407
2408 gtk_scrolled_window_adjustment_changed (vadjustment, scrolled_window);
2409 gtk_scrolled_window_adjustment_value_changed (vadjustment, scrolled_window);
2410
2411 child = gtk_bin_get_child (bin);
2412 if (child)
2413 gtk_scrollable_set_vadjustment (GTK_SCROLLABLE (child), vadjustment);
2414
2415 if (gtk_widget_should_animate (GTK_WIDGET (scrolled_window)))
2416 gtk_adjustment_enable_animation (vadjustment, gtk_widget_get_frame_clock (GTK_WIDGET (scrolled_window)), ANIMATION_DURATION);
2417
2418 g_object_notify_by_pspec (G_OBJECT (scrolled_window), properties[PROP_VADJUSTMENT]);
2419 }
2420
2421 /**
2422 * gtk_scrolled_window_get_hadjustment:
2423 * @scrolled_window: a #GtkScrolledWindow
2424 *
2425 * Returns the horizontal scrollbar’s adjustment, used to connect the
2426 * horizontal scrollbar to the child widget’s horizontal scroll
2427 * functionality.
2428 *
2429 * Returns: (transfer none): the horizontal #GtkAdjustment
2430 */
2431 GtkAdjustment*
gtk_scrolled_window_get_hadjustment(GtkScrolledWindow * scrolled_window)2432 gtk_scrolled_window_get_hadjustment (GtkScrolledWindow *scrolled_window)
2433 {
2434 GtkScrolledWindowPrivate *priv;
2435
2436 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), NULL);
2437
2438 priv = scrolled_window->priv;
2439
2440 return gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
2441 }
2442
2443 /**
2444 * gtk_scrolled_window_get_vadjustment:
2445 * @scrolled_window: a #GtkScrolledWindow
2446 *
2447 * Returns the vertical scrollbar’s adjustment, used to connect the
2448 * vertical scrollbar to the child widget’s vertical scroll functionality.
2449 *
2450 * Returns: (transfer none): the vertical #GtkAdjustment
2451 */
2452 GtkAdjustment*
gtk_scrolled_window_get_vadjustment(GtkScrolledWindow * scrolled_window)2453 gtk_scrolled_window_get_vadjustment (GtkScrolledWindow *scrolled_window)
2454 {
2455 GtkScrolledWindowPrivate *priv;
2456
2457 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), NULL);
2458
2459 priv = scrolled_window->priv;
2460
2461 return gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar));
2462 }
2463
2464 /**
2465 * gtk_scrolled_window_get_hscrollbar:
2466 * @scrolled_window: a #GtkScrolledWindow
2467 *
2468 * Returns the horizontal scrollbar of @scrolled_window.
2469 *
2470 * Returns: (transfer none): the horizontal scrollbar of the scrolled window.
2471 *
2472 * Since: 2.8
2473 */
2474 GtkWidget*
gtk_scrolled_window_get_hscrollbar(GtkScrolledWindow * scrolled_window)2475 gtk_scrolled_window_get_hscrollbar (GtkScrolledWindow *scrolled_window)
2476 {
2477 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), NULL);
2478
2479 return scrolled_window->priv->hscrollbar;
2480 }
2481
2482 /**
2483 * gtk_scrolled_window_get_vscrollbar:
2484 * @scrolled_window: a #GtkScrolledWindow
2485 *
2486 * Returns the vertical scrollbar of @scrolled_window.
2487 *
2488 * Returns: (transfer none): the vertical scrollbar of the scrolled window.
2489 *
2490 * Since: 2.8
2491 */
2492 GtkWidget*
gtk_scrolled_window_get_vscrollbar(GtkScrolledWindow * scrolled_window)2493 gtk_scrolled_window_get_vscrollbar (GtkScrolledWindow *scrolled_window)
2494 {
2495 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), NULL);
2496
2497 return scrolled_window->priv->vscrollbar;
2498 }
2499
2500 /**
2501 * gtk_scrolled_window_set_policy:
2502 * @scrolled_window: a #GtkScrolledWindow
2503 * @hscrollbar_policy: policy for horizontal bar
2504 * @vscrollbar_policy: policy for vertical bar
2505 *
2506 * Sets the scrollbar policy for the horizontal and vertical scrollbars.
2507 *
2508 * The policy determines when the scrollbar should appear; it is a value
2509 * from the #GtkPolicyType enumeration. If %GTK_POLICY_ALWAYS, the
2510 * scrollbar is always present; if %GTK_POLICY_NEVER, the scrollbar is
2511 * never present; if %GTK_POLICY_AUTOMATIC, the scrollbar is present only
2512 * if needed (that is, if the slider part of the bar would be smaller
2513 * than the trough — the display is larger than the page size).
2514 */
2515 void
gtk_scrolled_window_set_policy(GtkScrolledWindow * scrolled_window,GtkPolicyType hscrollbar_policy,GtkPolicyType vscrollbar_policy)2516 gtk_scrolled_window_set_policy (GtkScrolledWindow *scrolled_window,
2517 GtkPolicyType hscrollbar_policy,
2518 GtkPolicyType vscrollbar_policy)
2519 {
2520 GtkScrolledWindowPrivate *priv;
2521 GObject *object = G_OBJECT (scrolled_window);
2522
2523 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
2524
2525 priv = scrolled_window->priv;
2526
2527 if ((priv->hscrollbar_policy != hscrollbar_policy) ||
2528 (priv->vscrollbar_policy != vscrollbar_policy))
2529 {
2530 priv->hscrollbar_policy = hscrollbar_policy;
2531 priv->vscrollbar_policy = vscrollbar_policy;
2532
2533 gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
2534
2535 g_object_notify_by_pspec (object, properties[PROP_HSCROLLBAR_POLICY]);
2536 g_object_notify_by_pspec (object, properties[PROP_VSCROLLBAR_POLICY]);
2537 }
2538 }
2539
2540 /**
2541 * gtk_scrolled_window_get_policy:
2542 * @scrolled_window: a #GtkScrolledWindow
2543 * @hscrollbar_policy: (out) (optional): location to store the policy
2544 * for the horizontal scrollbar, or %NULL
2545 * @vscrollbar_policy: (out) (optional): location to store the policy
2546 * for the vertical scrollbar, or %NULL
2547 *
2548 * Retrieves the current policy values for the horizontal and vertical
2549 * scrollbars. See gtk_scrolled_window_set_policy().
2550 */
2551 void
gtk_scrolled_window_get_policy(GtkScrolledWindow * scrolled_window,GtkPolicyType * hscrollbar_policy,GtkPolicyType * vscrollbar_policy)2552 gtk_scrolled_window_get_policy (GtkScrolledWindow *scrolled_window,
2553 GtkPolicyType *hscrollbar_policy,
2554 GtkPolicyType *vscrollbar_policy)
2555 {
2556 GtkScrolledWindowPrivate *priv;
2557
2558 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
2559
2560 priv = scrolled_window->priv;
2561
2562 if (hscrollbar_policy)
2563 *hscrollbar_policy = priv->hscrollbar_policy;
2564 if (vscrollbar_policy)
2565 *vscrollbar_policy = priv->vscrollbar_policy;
2566 }
2567
2568 static void
gtk_scrolled_window_set_placement_internal(GtkScrolledWindow * scrolled_window,GtkCornerType window_placement)2569 gtk_scrolled_window_set_placement_internal (GtkScrolledWindow *scrolled_window,
2570 GtkCornerType window_placement)
2571 {
2572 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
2573
2574 if (priv->window_placement != window_placement)
2575 {
2576 priv->window_placement = window_placement;
2577 update_scrollbar_positions (scrolled_window);
2578
2579 gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
2580
2581 g_object_notify_by_pspec (G_OBJECT (scrolled_window), properties[PROP_WINDOW_PLACEMENT]);
2582 }
2583 }
2584
2585 /**
2586 * gtk_scrolled_window_set_placement:
2587 * @scrolled_window: a #GtkScrolledWindow
2588 * @window_placement: position of the child window
2589 *
2590 * Sets the placement of the contents with respect to the scrollbars
2591 * for the scrolled window.
2592 *
2593 * The default is %GTK_CORNER_TOP_LEFT, meaning the child is
2594 * in the top left, with the scrollbars underneath and to the right.
2595 * Other values in #GtkCornerType are %GTK_CORNER_TOP_RIGHT,
2596 * %GTK_CORNER_BOTTOM_LEFT, and %GTK_CORNER_BOTTOM_RIGHT.
2597 *
2598 * See also gtk_scrolled_window_get_placement() and
2599 * gtk_scrolled_window_unset_placement().
2600 */
2601 void
gtk_scrolled_window_set_placement(GtkScrolledWindow * scrolled_window,GtkCornerType window_placement)2602 gtk_scrolled_window_set_placement (GtkScrolledWindow *scrolled_window,
2603 GtkCornerType window_placement)
2604 {
2605 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
2606
2607 gtk_scrolled_window_set_placement_internal (scrolled_window, window_placement);
2608 }
2609
2610 /**
2611 * gtk_scrolled_window_get_placement:
2612 * @scrolled_window: a #GtkScrolledWindow
2613 *
2614 * Gets the placement of the contents with respect to the scrollbars
2615 * for the scrolled window. See gtk_scrolled_window_set_placement().
2616 *
2617 * Returns: the current placement value.
2618 *
2619 * See also gtk_scrolled_window_set_placement() and
2620 * gtk_scrolled_window_unset_placement().
2621 **/
2622 GtkCornerType
gtk_scrolled_window_get_placement(GtkScrolledWindow * scrolled_window)2623 gtk_scrolled_window_get_placement (GtkScrolledWindow *scrolled_window)
2624 {
2625 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), GTK_CORNER_TOP_LEFT);
2626
2627 return scrolled_window->priv->window_placement;
2628 }
2629
2630 /**
2631 * gtk_scrolled_window_unset_placement:
2632 * @scrolled_window: a #GtkScrolledWindow
2633 *
2634 * Unsets the placement of the contents with respect to the scrollbars
2635 * for the scrolled window. If no window placement is set for a scrolled
2636 * window, it defaults to %GTK_CORNER_TOP_LEFT.
2637 *
2638 * See also gtk_scrolled_window_set_placement() and
2639 * gtk_scrolled_window_get_placement().
2640 *
2641 * Since: 2.10
2642 **/
2643 void
gtk_scrolled_window_unset_placement(GtkScrolledWindow * scrolled_window)2644 gtk_scrolled_window_unset_placement (GtkScrolledWindow *scrolled_window)
2645 {
2646 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
2647
2648 gtk_scrolled_window_set_placement_internal (scrolled_window, GTK_CORNER_TOP_LEFT);
2649 }
2650
2651 /**
2652 * gtk_scrolled_window_set_shadow_type:
2653 * @scrolled_window: a #GtkScrolledWindow
2654 * @type: kind of shadow to draw around scrolled window contents
2655 *
2656 * Changes the type of shadow drawn around the contents of
2657 * @scrolled_window.
2658 **/
2659 void
gtk_scrolled_window_set_shadow_type(GtkScrolledWindow * scrolled_window,GtkShadowType type)2660 gtk_scrolled_window_set_shadow_type (GtkScrolledWindow *scrolled_window,
2661 GtkShadowType type)
2662 {
2663 GtkScrolledWindowPrivate *priv;
2664 GtkStyleContext *context;
2665
2666 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
2667 g_return_if_fail (type >= GTK_SHADOW_NONE && type <= GTK_SHADOW_ETCHED_OUT);
2668
2669 priv = scrolled_window->priv;
2670
2671 if (priv->shadow_type != type)
2672 {
2673 priv->shadow_type = type;
2674
2675 context = gtk_widget_get_style_context (GTK_WIDGET (scrolled_window));
2676 if (type != GTK_SHADOW_NONE)
2677 gtk_style_context_add_class (context, GTK_STYLE_CLASS_FRAME);
2678 else
2679 gtk_style_context_remove_class (context, GTK_STYLE_CLASS_FRAME);
2680
2681 if (gtk_widget_is_drawable (GTK_WIDGET (scrolled_window)))
2682 gtk_widget_queue_draw (GTK_WIDGET (scrolled_window));
2683
2684 gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
2685
2686 g_object_notify_by_pspec (G_OBJECT (scrolled_window), properties[PROP_SHADOW_TYPE]);
2687 }
2688 }
2689
2690 /**
2691 * gtk_scrolled_window_get_shadow_type:
2692 * @scrolled_window: a #GtkScrolledWindow
2693 *
2694 * Gets the shadow type of the scrolled window. See
2695 * gtk_scrolled_window_set_shadow_type().
2696 *
2697 * Returns: the current shadow type
2698 **/
2699 GtkShadowType
gtk_scrolled_window_get_shadow_type(GtkScrolledWindow * scrolled_window)2700 gtk_scrolled_window_get_shadow_type (GtkScrolledWindow *scrolled_window)
2701 {
2702 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), GTK_SHADOW_NONE);
2703
2704 return scrolled_window->priv->shadow_type;
2705 }
2706
2707 /**
2708 * gtk_scrolled_window_set_kinetic_scrolling:
2709 * @scrolled_window: a #GtkScrolledWindow
2710 * @kinetic_scrolling: %TRUE to enable kinetic scrolling
2711 *
2712 * Turns kinetic scrolling on or off.
2713 * Kinetic scrolling only applies to devices with source
2714 * %GDK_SOURCE_TOUCHSCREEN.
2715 *
2716 * Since: 3.4
2717 **/
2718 void
gtk_scrolled_window_set_kinetic_scrolling(GtkScrolledWindow * scrolled_window,gboolean kinetic_scrolling)2719 gtk_scrolled_window_set_kinetic_scrolling (GtkScrolledWindow *scrolled_window,
2720 gboolean kinetic_scrolling)
2721 {
2722 GtkPropagationPhase phase = GTK_PHASE_NONE;
2723 GtkScrolledWindowPrivate *priv;
2724
2725 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
2726
2727 priv = scrolled_window->priv;
2728 if (priv->kinetic_scrolling == kinetic_scrolling)
2729 return;
2730
2731 priv->kinetic_scrolling = kinetic_scrolling;
2732 gtk_scrolled_window_check_attach_pan_gesture (scrolled_window);
2733
2734 if (priv->kinetic_scrolling)
2735 phase = GTK_PHASE_CAPTURE;
2736 else
2737 gtk_scrolled_window_cancel_deceleration (scrolled_window);
2738
2739 gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (priv->drag_gesture), phase);
2740 gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (priv->swipe_gesture), phase);
2741 gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (priv->long_press_gesture), phase);
2742 gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (priv->pan_gesture), phase);
2743
2744 g_object_notify_by_pspec (G_OBJECT (scrolled_window), properties[PROP_KINETIC_SCROLLING]);
2745 }
2746
2747 /**
2748 * gtk_scrolled_window_get_kinetic_scrolling:
2749 * @scrolled_window: a #GtkScrolledWindow
2750 *
2751 * Returns the specified kinetic scrolling behavior.
2752 *
2753 * Returns: the scrolling behavior flags.
2754 *
2755 * Since: 3.4
2756 */
2757 gboolean
gtk_scrolled_window_get_kinetic_scrolling(GtkScrolledWindow * scrolled_window)2758 gtk_scrolled_window_get_kinetic_scrolling (GtkScrolledWindow *scrolled_window)
2759 {
2760 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), FALSE);
2761
2762 return scrolled_window->priv->kinetic_scrolling;
2763 }
2764
2765 /**
2766 * gtk_scrolled_window_set_capture_button_press:
2767 * @scrolled_window: a #GtkScrolledWindow
2768 * @capture_button_press: %TRUE to capture button presses
2769 *
2770 * Changes the behaviour of @scrolled_window with regard to the initial
2771 * event that possibly starts kinetic scrolling. When @capture_button_press
2772 * is set to %TRUE, the event is captured by the scrolled window, and
2773 * then later replayed if it is meant to go to the child widget.
2774 *
2775 * This should be enabled if any child widgets perform non-reversible
2776 * actions on #GtkWidget::button-press-event. If they don't, and handle
2777 * additionally handle #GtkWidget::grab-broken-event, it might be better
2778 * to set @capture_button_press to %FALSE.
2779 *
2780 * This setting only has an effect if kinetic scrolling is enabled.
2781 *
2782 * Since: 3.4
2783 */
2784 void
gtk_scrolled_window_set_capture_button_press(GtkScrolledWindow * scrolled_window,gboolean capture_button_press)2785 gtk_scrolled_window_set_capture_button_press (GtkScrolledWindow *scrolled_window,
2786 gboolean capture_button_press)
2787 {
2788 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
2789
2790 scrolled_window->priv->capture_button_press = capture_button_press;
2791 }
2792
2793 /**
2794 * gtk_scrolled_window_get_capture_button_press:
2795 * @scrolled_window: a #GtkScrolledWindow
2796 *
2797 * Return whether button presses are captured during kinetic
2798 * scrolling. See gtk_scrolled_window_set_capture_button_press().
2799 *
2800 * Returns: %TRUE if button presses are captured during kinetic scrolling
2801 *
2802 * Since: 3.4
2803 */
2804 gboolean
gtk_scrolled_window_get_capture_button_press(GtkScrolledWindow * scrolled_window)2805 gtk_scrolled_window_get_capture_button_press (GtkScrolledWindow *scrolled_window)
2806 {
2807 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), FALSE);
2808
2809 return scrolled_window->priv->capture_button_press;
2810 }
2811
2812 static void
gtk_scrolled_window_destroy(GtkWidget * widget)2813 gtk_scrolled_window_destroy (GtkWidget *widget)
2814 {
2815 GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
2816 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
2817 GtkWidget *child;
2818
2819 child = gtk_bin_get_child (GTK_BIN (widget));
2820 if (child)
2821 gtk_widget_destroy (child);
2822
2823 remove_indicator (scrolled_window, &priv->hindicator);
2824 remove_indicator (scrolled_window, &priv->vindicator);
2825 uninstall_scroll_cursor (scrolled_window);
2826
2827 if (priv->hscrollbar)
2828 {
2829 GtkAdjustment *hadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
2830
2831 g_signal_handlers_disconnect_by_data (hadjustment, scrolled_window);
2832 g_signal_handlers_disconnect_by_data (hadjustment, &priv->hindicator);
2833
2834 gtk_widget_unparent (priv->hscrollbar);
2835 priv->hscrollbar = NULL;
2836 }
2837
2838 if (priv->vscrollbar)
2839 {
2840 GtkAdjustment *vadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar));
2841
2842 g_signal_handlers_disconnect_by_data (vadjustment, scrolled_window);
2843 g_signal_handlers_disconnect_by_data (vadjustment, &priv->vindicator);
2844
2845 gtk_widget_unparent (priv->vscrollbar);
2846 priv->vscrollbar = NULL;
2847 }
2848
2849 if (priv->deceleration_id)
2850 {
2851 gtk_widget_remove_tick_callback (widget, priv->deceleration_id);
2852 priv->deceleration_id = 0;
2853 }
2854
2855 g_clear_pointer (&priv->hscrolling, gtk_kinetic_scrolling_free);
2856 g_clear_pointer (&priv->vscrolling, gtk_kinetic_scrolling_free);
2857
2858 if (priv->scroll_events_overshoot_id)
2859 {
2860 g_source_remove (priv->scroll_events_overshoot_id);
2861 priv->scroll_events_overshoot_id = 0;
2862 }
2863
2864 GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->destroy (widget);
2865 }
2866
2867 static void
gtk_scrolled_window_finalize(GObject * object)2868 gtk_scrolled_window_finalize (GObject *object)
2869 {
2870 GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (object);
2871 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
2872
2873 g_clear_object (&priv->drag_gesture);
2874 g_clear_object (&priv->swipe_gesture);
2875 g_clear_object (&priv->long_press_gesture);
2876 g_clear_object (&priv->pan_gesture);
2877 g_clear_object (&priv->gadget);
2878 g_clear_pointer (&priv->scroll_history, g_array_unref);
2879
2880 G_OBJECT_CLASS (gtk_scrolled_window_parent_class)->finalize (object);
2881 }
2882
2883 static void
gtk_scrolled_window_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)2884 gtk_scrolled_window_set_property (GObject *object,
2885 guint prop_id,
2886 const GValue *value,
2887 GParamSpec *pspec)
2888 {
2889 GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (object);
2890 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
2891
2892 switch (prop_id)
2893 {
2894 case PROP_HADJUSTMENT:
2895 gtk_scrolled_window_set_hadjustment (scrolled_window,
2896 g_value_get_object (value));
2897 break;
2898 case PROP_VADJUSTMENT:
2899 gtk_scrolled_window_set_vadjustment (scrolled_window,
2900 g_value_get_object (value));
2901 break;
2902 case PROP_HSCROLLBAR_POLICY:
2903 gtk_scrolled_window_set_policy (scrolled_window,
2904 g_value_get_enum (value),
2905 priv->vscrollbar_policy);
2906 break;
2907 case PROP_VSCROLLBAR_POLICY:
2908 gtk_scrolled_window_set_policy (scrolled_window,
2909 priv->hscrollbar_policy,
2910 g_value_get_enum (value));
2911 break;
2912 case PROP_WINDOW_PLACEMENT:
2913 gtk_scrolled_window_set_placement_internal (scrolled_window,
2914 g_value_get_enum (value));
2915 break;
2916 case PROP_WINDOW_PLACEMENT_SET:
2917 /* noop */
2918 break;
2919 case PROP_SHADOW_TYPE:
2920 gtk_scrolled_window_set_shadow_type (scrolled_window,
2921 g_value_get_enum (value));
2922 break;
2923 case PROP_MIN_CONTENT_WIDTH:
2924 gtk_scrolled_window_set_min_content_width (scrolled_window,
2925 g_value_get_int (value));
2926 break;
2927 case PROP_MIN_CONTENT_HEIGHT:
2928 gtk_scrolled_window_set_min_content_height (scrolled_window,
2929 g_value_get_int (value));
2930 break;
2931 case PROP_KINETIC_SCROLLING:
2932 gtk_scrolled_window_set_kinetic_scrolling (scrolled_window,
2933 g_value_get_boolean (value));
2934 break;
2935 case PROP_OVERLAY_SCROLLING:
2936 gtk_scrolled_window_set_overlay_scrolling (scrolled_window,
2937 g_value_get_boolean (value));
2938 break;
2939 case PROP_MAX_CONTENT_WIDTH:
2940 gtk_scrolled_window_set_max_content_width (scrolled_window,
2941 g_value_get_int (value));
2942 break;
2943 case PROP_MAX_CONTENT_HEIGHT:
2944 gtk_scrolled_window_set_max_content_height (scrolled_window,
2945 g_value_get_int (value));
2946 break;
2947 case PROP_PROPAGATE_NATURAL_WIDTH:
2948 gtk_scrolled_window_set_propagate_natural_width (scrolled_window,
2949 g_value_get_boolean (value));
2950 break;
2951 case PROP_PROPAGATE_NATURAL_HEIGHT:
2952 gtk_scrolled_window_set_propagate_natural_height (scrolled_window,
2953 g_value_get_boolean (value));
2954 break;
2955 default:
2956 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2957 break;
2958 }
2959 }
2960
2961 static void
gtk_scrolled_window_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)2962 gtk_scrolled_window_get_property (GObject *object,
2963 guint prop_id,
2964 GValue *value,
2965 GParamSpec *pspec)
2966 {
2967 GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (object);
2968 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
2969
2970 switch (prop_id)
2971 {
2972 case PROP_HADJUSTMENT:
2973 g_value_set_object (value,
2974 G_OBJECT (gtk_scrolled_window_get_hadjustment (scrolled_window)));
2975 break;
2976 case PROP_VADJUSTMENT:
2977 g_value_set_object (value,
2978 G_OBJECT (gtk_scrolled_window_get_vadjustment (scrolled_window)));
2979 break;
2980 case PROP_WINDOW_PLACEMENT:
2981 g_value_set_enum (value, priv->window_placement);
2982 break;
2983 case PROP_WINDOW_PLACEMENT_SET:
2984 g_value_set_boolean (value, TRUE);
2985 break;
2986 case PROP_SHADOW_TYPE:
2987 g_value_set_enum (value, priv->shadow_type);
2988 break;
2989 case PROP_HSCROLLBAR_POLICY:
2990 g_value_set_enum (value, priv->hscrollbar_policy);
2991 break;
2992 case PROP_VSCROLLBAR_POLICY:
2993 g_value_set_enum (value, priv->vscrollbar_policy);
2994 break;
2995 case PROP_MIN_CONTENT_WIDTH:
2996 g_value_set_int (value, priv->min_content_width);
2997 break;
2998 case PROP_MIN_CONTENT_HEIGHT:
2999 g_value_set_int (value, priv->min_content_height);
3000 break;
3001 case PROP_KINETIC_SCROLLING:
3002 g_value_set_boolean (value, priv->kinetic_scrolling);
3003 break;
3004 case PROP_OVERLAY_SCROLLING:
3005 g_value_set_boolean (value, priv->overlay_scrolling);
3006 break;
3007 case PROP_MAX_CONTENT_WIDTH:
3008 g_value_set_int (value, priv->max_content_width);
3009 break;
3010 case PROP_MAX_CONTENT_HEIGHT:
3011 g_value_set_int (value, priv->max_content_height);
3012 break;
3013 case PROP_PROPAGATE_NATURAL_WIDTH:
3014 g_value_set_boolean (value, priv->propagate_natural_width);
3015 break;
3016 case PROP_PROPAGATE_NATURAL_HEIGHT:
3017 g_value_set_boolean (value, priv->propagate_natural_height);
3018 break;
3019 default:
3020 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3021 break;
3022 }
3023 }
3024
3025 static void
gtk_scrolled_window_inner_allocation(GtkWidget * widget,GtkAllocation * rect)3026 gtk_scrolled_window_inner_allocation (GtkWidget *widget,
3027 GtkAllocation *rect)
3028 {
3029 GtkWidget *child;
3030 GtkBorder border = { 0 };
3031
3032 gtk_scrolled_window_relative_allocation (widget, rect);
3033 child = gtk_bin_get_child (GTK_BIN (widget));
3034 if (child && gtk_scrollable_get_border (GTK_SCROLLABLE (child), &border))
3035 {
3036 rect->x += border.left;
3037 rect->y += border.top;
3038 rect->width -= border.left + border.right;
3039 rect->height -= border.top + border.bottom;
3040 }
3041 }
3042
3043 static gboolean
gtk_scrolled_window_draw(GtkWidget * widget,cairo_t * cr)3044 gtk_scrolled_window_draw (GtkWidget *widget,
3045 cairo_t *cr)
3046 {
3047 GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
3048 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
3049
3050 gtk_css_gadget_draw (priv->gadget, cr);
3051
3052 return FALSE;
3053 }
3054
3055 static void
gtk_scrolled_window_forall(GtkContainer * container,gboolean include_internals,GtkCallback callback,gpointer callback_data)3056 gtk_scrolled_window_forall (GtkContainer *container,
3057 gboolean include_internals,
3058 GtkCallback callback,
3059 gpointer callback_data)
3060 {
3061 GtkScrolledWindowPrivate *priv;
3062 GtkScrolledWindow *scrolled_window;
3063
3064 GTK_CONTAINER_CLASS (gtk_scrolled_window_parent_class)->forall (container,
3065 include_internals,
3066 callback,
3067 callback_data);
3068 if (include_internals)
3069 {
3070 scrolled_window = GTK_SCROLLED_WINDOW (container);
3071 priv = scrolled_window->priv;
3072
3073 if (priv->vscrollbar)
3074 callback (priv->vscrollbar, callback_data);
3075 if (priv->hscrollbar)
3076 callback (priv->hscrollbar, callback_data);
3077 }
3078 }
3079
3080 static gboolean
gtk_scrolled_window_scroll_child(GtkScrolledWindow * scrolled_window,GtkScrollType scroll,gboolean horizontal)3081 gtk_scrolled_window_scroll_child (GtkScrolledWindow *scrolled_window,
3082 GtkScrollType scroll,
3083 gboolean horizontal)
3084 {
3085 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
3086 GtkAdjustment *adjustment = NULL;
3087
3088 switch (scroll)
3089 {
3090 case GTK_SCROLL_STEP_UP:
3091 scroll = GTK_SCROLL_STEP_BACKWARD;
3092 horizontal = FALSE;
3093 break;
3094 case GTK_SCROLL_STEP_DOWN:
3095 scroll = GTK_SCROLL_STEP_FORWARD;
3096 horizontal = FALSE;
3097 break;
3098 case GTK_SCROLL_STEP_LEFT:
3099 scroll = GTK_SCROLL_STEP_BACKWARD;
3100 horizontal = TRUE;
3101 break;
3102 case GTK_SCROLL_STEP_RIGHT:
3103 scroll = GTK_SCROLL_STEP_FORWARD;
3104 horizontal = TRUE;
3105 break;
3106 case GTK_SCROLL_PAGE_UP:
3107 scroll = GTK_SCROLL_PAGE_BACKWARD;
3108 horizontal = FALSE;
3109 break;
3110 case GTK_SCROLL_PAGE_DOWN:
3111 scroll = GTK_SCROLL_PAGE_FORWARD;
3112 horizontal = FALSE;
3113 break;
3114 case GTK_SCROLL_PAGE_LEFT:
3115 scroll = GTK_SCROLL_STEP_BACKWARD;
3116 horizontal = TRUE;
3117 break;
3118 case GTK_SCROLL_PAGE_RIGHT:
3119 scroll = GTK_SCROLL_STEP_FORWARD;
3120 horizontal = TRUE;
3121 break;
3122 case GTK_SCROLL_STEP_BACKWARD:
3123 case GTK_SCROLL_STEP_FORWARD:
3124 case GTK_SCROLL_PAGE_BACKWARD:
3125 case GTK_SCROLL_PAGE_FORWARD:
3126 case GTK_SCROLL_START:
3127 case GTK_SCROLL_END:
3128 break;
3129 default:
3130 g_warning ("Invalid scroll type %u for GtkScrolledWindow::scroll-child", scroll);
3131 return FALSE;
3132 }
3133
3134 if (horizontal)
3135 {
3136 if (may_hscroll (scrolled_window))
3137 adjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
3138 else
3139 return FALSE;
3140 }
3141 else
3142 {
3143 if (may_vscroll (scrolled_window))
3144 adjustment = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar));
3145 else
3146 return FALSE;
3147 }
3148
3149 if (adjustment)
3150 {
3151 gdouble value = gtk_adjustment_get_value (adjustment);
3152
3153 switch (scroll)
3154 {
3155 case GTK_SCROLL_STEP_FORWARD:
3156 value += gtk_adjustment_get_step_increment (adjustment);
3157 break;
3158 case GTK_SCROLL_STEP_BACKWARD:
3159 value -= gtk_adjustment_get_step_increment (adjustment);
3160 break;
3161 case GTK_SCROLL_PAGE_FORWARD:
3162 value += gtk_adjustment_get_page_increment (adjustment);
3163 break;
3164 case GTK_SCROLL_PAGE_BACKWARD:
3165 value -= gtk_adjustment_get_page_increment (adjustment);
3166 break;
3167 case GTK_SCROLL_START:
3168 value = gtk_adjustment_get_lower (adjustment);
3169 break;
3170 case GTK_SCROLL_END:
3171 value = gtk_adjustment_get_upper (adjustment);
3172 break;
3173 default:
3174 g_assert_not_reached ();
3175 break;
3176 }
3177
3178 gtk_adjustment_animate_to_value (adjustment, value);
3179
3180 return TRUE;
3181 }
3182
3183 return FALSE;
3184 }
3185
3186 static void
gtk_scrolled_window_move_focus_out(GtkScrolledWindow * scrolled_window,GtkDirectionType direction_type)3187 gtk_scrolled_window_move_focus_out (GtkScrolledWindow *scrolled_window,
3188 GtkDirectionType direction_type)
3189 {
3190 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
3191 GtkWidget *toplevel;
3192
3193 /* Focus out of the scrolled window entirely. We do this by setting
3194 * a flag, then propagating the focus motion to the notebook.
3195 */
3196 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (scrolled_window));
3197 if (!gtk_widget_is_toplevel (toplevel))
3198 return;
3199
3200 g_object_ref (scrolled_window);
3201
3202 priv->focus_out = TRUE;
3203 g_signal_emit_by_name (toplevel, "move-focus", direction_type);
3204 priv->focus_out = FALSE;
3205
3206 g_object_unref (scrolled_window);
3207 }
3208
3209 static void
gtk_scrolled_window_relative_allocation(GtkWidget * widget,GtkAllocation * allocation)3210 gtk_scrolled_window_relative_allocation (GtkWidget *widget,
3211 GtkAllocation *allocation)
3212 {
3213 GtkAllocation content_allocation;
3214 GtkScrolledWindow *scrolled_window;
3215 GtkScrolledWindowPrivate *priv;
3216 gint sb_spacing;
3217 gint sb_width;
3218 gint sb_height;
3219
3220 g_return_if_fail (widget != NULL);
3221 g_return_if_fail (allocation != NULL);
3222
3223 scrolled_window = GTK_SCROLLED_WINDOW (widget);
3224 priv = scrolled_window->priv;
3225
3226 /* Get possible scrollbar dimensions */
3227 sb_spacing = _gtk_scrolled_window_get_scrollbar_spacing (scrolled_window);
3228 gtk_widget_get_preferred_height (priv->hscrollbar, &sb_height, NULL);
3229 gtk_widget_get_preferred_width (priv->vscrollbar, &sb_width, NULL);
3230
3231 gtk_css_gadget_get_content_allocation (priv->gadget, &content_allocation, NULL);
3232
3233 allocation->x = content_allocation.x;
3234 allocation->y = content_allocation.y;
3235 allocation->width = content_allocation.width;
3236 allocation->height = content_allocation.height;
3237
3238 /* Subtract some things from our available allocation size */
3239 if (priv->vscrollbar_visible && !priv->use_indicators)
3240 {
3241 gboolean is_rtl;
3242
3243 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
3244
3245 if ((!is_rtl &&
3246 (priv->window_placement == GTK_CORNER_TOP_RIGHT ||
3247 priv->window_placement == GTK_CORNER_BOTTOM_RIGHT)) ||
3248 (is_rtl &&
3249 (priv->window_placement == GTK_CORNER_TOP_LEFT ||
3250 priv->window_placement == GTK_CORNER_BOTTOM_LEFT)))
3251 allocation->x += (sb_width + sb_spacing);
3252
3253 allocation->width = MAX (1, allocation->width - (sb_width + sb_spacing));
3254 }
3255
3256 if (priv->hscrollbar_visible && !priv->use_indicators)
3257 {
3258
3259 if (priv->window_placement == GTK_CORNER_BOTTOM_LEFT ||
3260 priv->window_placement == GTK_CORNER_BOTTOM_RIGHT)
3261 allocation->y += (sb_height + sb_spacing);
3262
3263 allocation->height = MAX (1, allocation->height - (sb_height + sb_spacing));
3264 }
3265 }
3266
3267 static gboolean
_gtk_scrolled_window_get_overshoot(GtkScrolledWindow * scrolled_window,gint * overshoot_x,gint * overshoot_y)3268 _gtk_scrolled_window_get_overshoot (GtkScrolledWindow *scrolled_window,
3269 gint *overshoot_x,
3270 gint *overshoot_y)
3271 {
3272 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
3273 GtkAdjustment *vadjustment, *hadjustment;
3274 gdouble lower, upper, x, y;
3275
3276 /* Vertical overshoot */
3277 vadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar));
3278 lower = gtk_adjustment_get_lower (vadjustment);
3279 upper = gtk_adjustment_get_upper (vadjustment) -
3280 gtk_adjustment_get_page_size (vadjustment);
3281
3282 if (priv->unclamped_vadj_value < lower)
3283 y = priv->unclamped_vadj_value - lower;
3284 else if (priv->unclamped_vadj_value > upper)
3285 y = priv->unclamped_vadj_value - upper;
3286 else
3287 y = 0;
3288
3289 /* Horizontal overshoot */
3290 hadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
3291 lower = gtk_adjustment_get_lower (hadjustment);
3292 upper = gtk_adjustment_get_upper (hadjustment) -
3293 gtk_adjustment_get_page_size (hadjustment);
3294
3295 if (priv->unclamped_hadj_value < lower)
3296 x = priv->unclamped_hadj_value - lower;
3297 else if (priv->unclamped_hadj_value > upper)
3298 x = priv->unclamped_hadj_value - upper;
3299 else
3300 x = 0;
3301
3302 if (overshoot_x)
3303 *overshoot_x = x;
3304
3305 if (overshoot_y)
3306 *overshoot_y = y;
3307
3308 return (x != 0 || y != 0);
3309 }
3310
3311 static void
gtk_scrolled_window_allocate_child(GtkScrolledWindow * swindow,GtkAllocation * relative_allocation)3312 gtk_scrolled_window_allocate_child (GtkScrolledWindow *swindow,
3313 GtkAllocation *relative_allocation)
3314 {
3315 GtkWidget *widget = GTK_WIDGET (swindow), *child;
3316 GtkAllocation child_allocation;
3317
3318 child = gtk_bin_get_child (GTK_BIN (widget));
3319
3320 gtk_scrolled_window_relative_allocation (widget, relative_allocation);
3321
3322 child_allocation.x = relative_allocation->x;
3323 child_allocation.y = relative_allocation->y;
3324 child_allocation.width = relative_allocation->width;
3325 child_allocation.height = relative_allocation->height;
3326
3327 gtk_widget_size_allocate (child, &child_allocation);
3328 }
3329
3330 static void
gtk_scrolled_window_allocate_scrollbar(GtkScrolledWindow * scrolled_window,GtkWidget * scrollbar,GtkAllocation * allocation)3331 gtk_scrolled_window_allocate_scrollbar (GtkScrolledWindow *scrolled_window,
3332 GtkWidget *scrollbar,
3333 GtkAllocation *allocation)
3334 {
3335 GtkAllocation child_allocation, content_allocation;
3336 GtkWidget *widget = GTK_WIDGET (scrolled_window);
3337 gint sb_spacing, sb_height, sb_width;
3338 GtkScrolledWindowPrivate *priv;
3339
3340 priv = scrolled_window->priv;
3341
3342 gtk_scrolled_window_inner_allocation (widget, &content_allocation);
3343 sb_spacing = _gtk_scrolled_window_get_scrollbar_spacing (scrolled_window);
3344 gtk_widget_get_preferred_height (priv->hscrollbar, &sb_height, NULL);
3345 gtk_widget_get_preferred_width (priv->vscrollbar, &sb_width, NULL);
3346
3347 if (scrollbar == priv->hscrollbar)
3348 {
3349 child_allocation.x = content_allocation.x;
3350
3351 if (priv->window_placement == GTK_CORNER_TOP_LEFT ||
3352 priv->window_placement == GTK_CORNER_TOP_RIGHT)
3353 {
3354 if (priv->use_indicators)
3355 child_allocation.y = content_allocation.y + content_allocation.height - sb_height;
3356 else
3357 child_allocation.y = content_allocation.y + content_allocation.height + sb_spacing;
3358 }
3359 else
3360 {
3361 if (priv->use_indicators)
3362 child_allocation.y = content_allocation.y;
3363 else
3364 child_allocation.y = content_allocation.y - sb_spacing - sb_height;
3365 }
3366
3367 child_allocation.width = content_allocation.width;
3368 child_allocation.height = sb_height;
3369 }
3370 else if (scrollbar == priv->vscrollbar)
3371 {
3372 if ((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL &&
3373 (priv->window_placement == GTK_CORNER_TOP_RIGHT ||
3374 priv->window_placement == GTK_CORNER_BOTTOM_RIGHT)) ||
3375 (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR &&
3376 (priv->window_placement == GTK_CORNER_TOP_LEFT ||
3377 priv->window_placement == GTK_CORNER_BOTTOM_LEFT)))
3378 {
3379 if (priv->use_indicators)
3380 child_allocation.x = content_allocation.x + content_allocation.width - sb_width;
3381 else
3382 child_allocation.x = content_allocation.x + content_allocation.width + sb_spacing;
3383 }
3384 else
3385 {
3386 if (priv->use_indicators)
3387 child_allocation.x = content_allocation.x;
3388 else
3389 child_allocation.x = content_allocation.x - sb_spacing - sb_width;
3390 }
3391
3392 child_allocation.y = content_allocation.y;
3393 child_allocation.width = sb_width;
3394 child_allocation.height = content_allocation.height;
3395 }
3396
3397 *allocation = child_allocation;
3398 }
3399
3400 static void
gtk_scrolled_window_size_allocate(GtkWidget * widget,GtkAllocation * allocation)3401 gtk_scrolled_window_size_allocate (GtkWidget *widget,
3402 GtkAllocation *allocation)
3403 {
3404 GtkScrolledWindow *scrolled_window;
3405 GtkScrolledWindowPrivate *priv;
3406 GtkAllocation clip, content_allocation;
3407
3408 scrolled_window = GTK_SCROLLED_WINDOW (widget);
3409 priv = scrolled_window->priv;
3410
3411 gtk_widget_set_allocation (widget, allocation);
3412
3413 if (gtk_widget_get_realized (widget))
3414 gdk_window_move_resize (gtk_widget_get_window (widget),
3415 allocation->x, allocation->y,
3416 allocation->width, allocation->height);
3417
3418 content_allocation = *allocation;
3419 content_allocation.x = content_allocation.y = 0;
3420 gtk_css_gadget_allocate (priv->gadget,
3421 &content_allocation,
3422 gtk_widget_get_allocated_baseline (widget),
3423 &clip);
3424
3425 clip.x += allocation->x;
3426 clip.y += allocation->y;
3427 gtk_widget_set_clip (widget, &clip);
3428 }
3429
3430 static void
clear_scroll_window(GtkScrolledWindow * scrolled_window)3431 clear_scroll_window (GtkScrolledWindow *scrolled_window)
3432 {
3433 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
3434 priv->scroll_window = NULL;
3435 g_clear_object (&priv->scroll_cursor);
3436 }
3437
3438 static void
finalize_scroll_window(gpointer data,GObject * where_the_object_was)3439 finalize_scroll_window (gpointer data,
3440 GObject *where_the_object_was)
3441 {
3442 clear_scroll_window ((GtkScrolledWindow *) data);
3443 }
3444
3445 static void
install_scroll_cursor(GtkScrolledWindow * scrolled_window,GdkWindow * window)3446 install_scroll_cursor (GtkScrolledWindow *scrolled_window,
3447 GdkWindow *window)
3448 {
3449 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
3450 GdkDisplay *display;
3451 GdkCursor *cursor;
3452
3453 if (priv->scroll_window)
3454 return;
3455
3456 priv->scroll_window = window;
3457 g_object_weak_ref (G_OBJECT (priv->scroll_window), finalize_scroll_window, scrolled_window);
3458
3459 priv->scroll_cursor = gdk_window_get_cursor (priv->scroll_window);
3460 if (priv->scroll_cursor)
3461 g_object_ref (priv->scroll_cursor);
3462
3463 display = gdk_window_get_display (priv->scroll_window);
3464 cursor = gdk_cursor_new_from_name (display, "all-scroll");
3465 gdk_window_set_cursor (priv->scroll_window, cursor);
3466 g_clear_object (&cursor);
3467 }
3468
3469 static void
uninstall_scroll_cursor(GtkScrolledWindow * scrolled_window)3470 uninstall_scroll_cursor (GtkScrolledWindow *scrolled_window)
3471 {
3472 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
3473
3474 if (priv->scroll_window)
3475 {
3476 gdk_window_set_cursor (priv->scroll_window, priv->scroll_cursor);
3477 g_object_weak_unref (G_OBJECT (priv->scroll_window), finalize_scroll_window, scrolled_window);
3478 clear_scroll_window (scrolled_window);
3479 }
3480 }
3481
3482 static gboolean
start_scroll_deceleration_cb(gpointer user_data)3483 start_scroll_deceleration_cb (gpointer user_data)
3484 {
3485 GtkScrolledWindow *scrolled_window = user_data;
3486 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
3487
3488 priv->scroll_events_overshoot_id = 0;
3489
3490 if (!priv->deceleration_id)
3491 {
3492 uninstall_scroll_cursor (scrolled_window);
3493 gtk_scrolled_window_start_deceleration (scrolled_window);
3494 }
3495
3496 return FALSE;
3497 }
3498
3499
3500 static gboolean
gtk_scrolled_window_scroll_event(GtkWidget * widget,GdkEventScroll * event)3501 gtk_scrolled_window_scroll_event (GtkWidget *widget,
3502 GdkEventScroll *event)
3503 {
3504 GtkScrolledWindowPrivate *priv;
3505 GtkScrolledWindow *scrolled_window;
3506 gboolean handled = FALSE;
3507 gdouble delta_x;
3508 gdouble delta_y;
3509 GdkScrollDirection direction;
3510 gboolean shifted, start_deceleration = FALSE;
3511 GdkDevice *source_device;
3512 GdkInputSource input_source;
3513
3514 shifted = (event->state & GDK_SHIFT_MASK) != 0;
3515
3516 scrolled_window = GTK_SCROLLED_WINDOW (widget);
3517 priv = scrolled_window->priv;
3518
3519 gtk_scrolled_window_invalidate_overshoot (scrolled_window);
3520 source_device = gdk_event_get_source_device ((GdkEvent *) event);
3521 input_source = gdk_device_get_source (source_device);
3522
3523 if (gdk_event_get_scroll_deltas ((GdkEvent *) event, &delta_x, &delta_y))
3524 {
3525 if (priv->scroll_device != source_device)
3526 {
3527 priv->scroll_device = source_device;
3528 scroll_history_reset (scrolled_window);
3529 }
3530
3531 scroll_history_push (scrolled_window, event, shifted);
3532
3533 if (input_source == GDK_SOURCE_TRACKPOINT ||
3534 input_source == GDK_SOURCE_TOUCHPAD)
3535 install_scroll_cursor (scrolled_window, gdk_event_get_window ((GdkEvent *)event));
3536
3537 if (shifted)
3538 {
3539 gdouble delta;
3540
3541 delta = delta_x;
3542 delta_x = delta_y;
3543 delta_y = delta;
3544 }
3545
3546 if (delta_x != 0.0 &&
3547 may_hscroll (scrolled_window))
3548 {
3549 GtkAdjustment *adj;
3550 gdouble new_value;
3551 gdouble scroll_unit;
3552
3553 adj = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
3554 scroll_unit = get_scroll_unit (scrolled_window, GTK_ORIENTATION_HORIZONTAL);
3555
3556 new_value = priv->unclamped_hadj_value + delta_x * scroll_unit;
3557 _gtk_scrolled_window_set_adjustment_value (scrolled_window, adj,
3558 new_value);
3559 handled = TRUE;
3560 }
3561
3562 if (delta_y != 0.0 &&
3563 may_vscroll (scrolled_window))
3564 {
3565 GtkAdjustment *adj;
3566 gdouble new_value;
3567 gdouble scroll_unit;
3568
3569 adj = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar));
3570 scroll_unit = get_scroll_unit (scrolled_window, GTK_ORIENTATION_VERTICAL);
3571
3572 new_value = priv->unclamped_vadj_value + delta_y * scroll_unit;
3573 _gtk_scrolled_window_set_adjustment_value (scrolled_window, adj,
3574 new_value);
3575 handled = TRUE;
3576 }
3577
3578 /* The libinput driver may generate a final event with dx=dy=0
3579 * after scrolling finished, start kinetic scrolling when this
3580 * happens.
3581 */
3582 if (gdk_event_is_scroll_stop_event ((GdkEvent *) event))
3583 {
3584 handled = TRUE;
3585 start_deceleration = TRUE;
3586 }
3587 }
3588 else if (gdk_event_get_scroll_direction ((GdkEvent *)event, &direction))
3589 {
3590 GtkWidget *range;
3591 gboolean may_scroll;
3592
3593 if ((!shifted && (direction == GDK_SCROLL_UP || direction == GDK_SCROLL_DOWN)) ||
3594 (shifted && (direction == GDK_SCROLL_LEFT || direction == GDK_SCROLL_RIGHT)))
3595 {
3596 range = priv->vscrollbar;
3597 may_scroll = may_vscroll (scrolled_window);
3598 }
3599 else
3600 {
3601 range = priv->hscrollbar;
3602 may_scroll = may_hscroll (scrolled_window);
3603 }
3604
3605 if (range && may_scroll)
3606 {
3607 GtkAdjustment *adj = gtk_range_get_adjustment (GTK_RANGE (range));
3608 gdouble new_value;
3609 gdouble delta;
3610
3611 delta = _gtk_range_get_wheel_delta (GTK_RANGE (range), event);
3612
3613 new_value = CLAMP (gtk_adjustment_get_value (adj) + delta,
3614 gtk_adjustment_get_lower (adj),
3615 gtk_adjustment_get_upper (adj) -
3616 gtk_adjustment_get_page_size (adj));
3617
3618 gtk_adjustment_set_value (adj, new_value);
3619
3620 handled = TRUE;
3621 }
3622 }
3623
3624 if (handled)
3625 {
3626 gdouble vel_x, vel_y;
3627
3628 gtk_scrolled_window_invalidate_overshoot (scrolled_window);
3629
3630 if (priv->scroll_events_overshoot_id)
3631 {
3632 g_source_remove (priv->scroll_events_overshoot_id);
3633 priv->scroll_events_overshoot_id = 0;
3634 }
3635
3636 if (start_deceleration)
3637 uninstall_scroll_cursor (scrolled_window);
3638
3639 if (start_deceleration &&
3640 scroll_history_finish (scrolled_window, &vel_x, &vel_y))
3641 gtk_scrolled_window_decelerate (scrolled_window, vel_x, vel_y);
3642 else if (_gtk_scrolled_window_get_overshoot (scrolled_window, NULL, NULL))
3643 {
3644 priv->scroll_events_overshoot_id =
3645 gdk_threads_add_timeout (50, start_scroll_deceleration_cb, scrolled_window);
3646 g_source_set_name_by_id (priv->scroll_events_overshoot_id,
3647 "[gtk+] start_scroll_deceleration_cb");
3648 }
3649 }
3650
3651 return handled;
3652 }
3653
3654 static void
_gtk_scrolled_window_set_adjustment_value(GtkScrolledWindow * scrolled_window,GtkAdjustment * adjustment,gdouble value)3655 _gtk_scrolled_window_set_adjustment_value (GtkScrolledWindow *scrolled_window,
3656 GtkAdjustment *adjustment,
3657 gdouble value)
3658 {
3659 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
3660 gdouble lower, upper, *prev_value;
3661 GtkPositionType edge_pos;
3662 gboolean vertical;
3663
3664 lower = gtk_adjustment_get_lower (adjustment) - MAX_OVERSHOOT_DISTANCE;
3665 upper = gtk_adjustment_get_upper (adjustment) -
3666 gtk_adjustment_get_page_size (adjustment) + MAX_OVERSHOOT_DISTANCE;
3667
3668 if (adjustment == gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar)))
3669 vertical = FALSE;
3670 else if (adjustment == gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar)))
3671 vertical = TRUE;
3672 else
3673 return;
3674
3675 if (vertical)
3676 prev_value = &priv->unclamped_vadj_value;
3677 else
3678 prev_value = &priv->unclamped_hadj_value;
3679
3680 value = CLAMP (value, lower, upper);
3681
3682 if (*prev_value == value)
3683 return;
3684
3685 *prev_value = value;
3686 gtk_adjustment_set_value (adjustment, value);
3687
3688 if (value == lower)
3689 edge_pos = vertical ? GTK_POS_TOP : GTK_POS_LEFT;
3690 else if (value == upper)
3691 edge_pos = vertical ? GTK_POS_BOTTOM : GTK_POS_RIGHT;
3692 else
3693 return;
3694
3695 /* Invert horizontal edge position on RTL */
3696 if (!vertical &&
3697 gtk_widget_get_direction (GTK_WIDGET (scrolled_window)) == GTK_TEXT_DIR_RTL)
3698 edge_pos = (edge_pos == GTK_POS_LEFT) ? GTK_POS_RIGHT : GTK_POS_LEFT;
3699
3700 g_signal_emit (scrolled_window, signals[EDGE_OVERSHOT], 0, edge_pos);
3701 }
3702
3703 static gboolean
scrolled_window_deceleration_cb(GtkWidget * widget,GdkFrameClock * frame_clock,gpointer user_data)3704 scrolled_window_deceleration_cb (GtkWidget *widget,
3705 GdkFrameClock *frame_clock,
3706 gpointer user_data)
3707 {
3708 GtkScrolledWindow *scrolled_window = user_data;
3709 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
3710 GtkAdjustment *hadjustment, *vadjustment;
3711 gint64 current_time;
3712 gdouble position, elapsed;
3713
3714 current_time = gdk_frame_clock_get_frame_time (frame_clock);
3715 elapsed = (current_time - priv->last_deceleration_time) / (double)G_TIME_SPAN_SECOND;
3716 priv->last_deceleration_time = current_time;
3717
3718 hadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
3719 vadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar));
3720
3721 gtk_scrolled_window_invalidate_overshoot (scrolled_window);
3722
3723 if (priv->hscrolling &&
3724 gtk_kinetic_scrolling_tick (priv->hscrolling, elapsed, &position, NULL))
3725 {
3726 priv->unclamped_hadj_value = position;
3727 gtk_adjustment_set_value (hadjustment, position);
3728 }
3729 else if (priv->hscrolling)
3730 g_clear_pointer (&priv->hscrolling, gtk_kinetic_scrolling_free);
3731
3732 if (priv->vscrolling &&
3733 gtk_kinetic_scrolling_tick (priv->vscrolling, elapsed, &position, NULL))
3734 {
3735 priv->unclamped_vadj_value = position;
3736 gtk_adjustment_set_value (vadjustment, position);
3737 }
3738 else if (priv->vscrolling)
3739 g_clear_pointer (&priv->vscrolling, gtk_kinetic_scrolling_free);
3740
3741 if (!priv->hscrolling && !priv->vscrolling)
3742 {
3743 gtk_scrolled_window_cancel_deceleration (scrolled_window);
3744 return G_SOURCE_REMOVE;
3745 }
3746
3747 gtk_scrolled_window_invalidate_overshoot (scrolled_window);
3748
3749 return G_SOURCE_CONTINUE;
3750 }
3751
3752 static void
gtk_scrolled_window_cancel_deceleration(GtkScrolledWindow * scrolled_window)3753 gtk_scrolled_window_cancel_deceleration (GtkScrolledWindow *scrolled_window)
3754 {
3755 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
3756
3757 if (priv->deceleration_id)
3758 {
3759 gtk_widget_remove_tick_callback (GTK_WIDGET (scrolled_window),
3760 priv->deceleration_id);
3761 priv->deceleration_id = 0;
3762 }
3763 }
3764
3765 static void
kinetic_scroll_stop_notify(GtkScrolledWindow * scrolled_window)3766 kinetic_scroll_stop_notify (GtkScrolledWindow *scrolled_window)
3767 {
3768 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
3769 priv->deceleration_id = 0;
3770 }
3771
3772 static void
gtk_scrolled_window_accumulate_velocity(GtkKineticScrolling ** scrolling,double elapsed,double * velocity)3773 gtk_scrolled_window_accumulate_velocity (GtkKineticScrolling **scrolling, double elapsed, double *velocity)
3774 {
3775 double last_velocity;
3776
3777 if (!*scrolling)
3778 return;
3779
3780 gtk_kinetic_scrolling_tick (*scrolling, elapsed, NULL, &last_velocity);
3781 if (((*velocity >= 0) == (last_velocity >= 0)) &&
3782 (fabs (*velocity) >= fabs (last_velocity) * VELOCITY_ACCUMULATION_FLOOR))
3783 {
3784 double min_velocity = last_velocity * VELOCITY_ACCUMULATION_FLOOR;
3785 double max_velocity = last_velocity * VELOCITY_ACCUMULATION_CEIL;
3786 double accumulation_multiplier = (*velocity - min_velocity) / (max_velocity - min_velocity);
3787 *velocity += last_velocity * fmin (accumulation_multiplier, VELOCITY_ACCUMULATION_MAX);
3788 }
3789 g_clear_pointer (scrolling, gtk_kinetic_scrolling_free);
3790 }
3791
3792 static void
gtk_scrolled_window_start_deceleration(GtkScrolledWindow * scrolled_window)3793 gtk_scrolled_window_start_deceleration (GtkScrolledWindow *scrolled_window)
3794 {
3795 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
3796 GdkFrameClock *frame_clock;
3797 gint64 current_time;
3798 double elapsed;
3799
3800 g_return_if_fail (priv->deceleration_id == 0);
3801
3802 frame_clock = gtk_widget_get_frame_clock (GTK_WIDGET (scrolled_window));
3803
3804 current_time = gdk_frame_clock_get_frame_time (frame_clock);
3805 elapsed = (current_time - priv->last_deceleration_time) / (double)G_TIME_SPAN_SECOND;
3806 priv->last_deceleration_time = current_time;
3807
3808 if (may_hscroll (scrolled_window))
3809 {
3810 gdouble lower,upper;
3811 GtkAdjustment *hadjustment;
3812
3813 gtk_scrolled_window_accumulate_velocity (&priv->hscrolling, elapsed, &priv->x_velocity);
3814
3815 hadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
3816 lower = gtk_adjustment_get_lower (hadjustment);
3817 upper = gtk_adjustment_get_upper (hadjustment);
3818 upper -= gtk_adjustment_get_page_size (hadjustment);
3819 priv->hscrolling =
3820 gtk_kinetic_scrolling_new (lower,
3821 upper,
3822 MAX_OVERSHOOT_DISTANCE,
3823 DECELERATION_FRICTION,
3824 OVERSHOOT_FRICTION,
3825 priv->unclamped_hadj_value,
3826 priv->x_velocity);
3827 }
3828 else
3829 g_clear_pointer (&priv->hscrolling, gtk_kinetic_scrolling_free);
3830
3831 if (may_vscroll (scrolled_window))
3832 {
3833 gdouble lower,upper;
3834 GtkAdjustment *vadjustment;
3835
3836 gtk_scrolled_window_accumulate_velocity (&priv->vscrolling, elapsed, &priv->y_velocity);
3837
3838 vadjustment = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar));
3839 lower = gtk_adjustment_get_lower(vadjustment);
3840 upper = gtk_adjustment_get_upper(vadjustment);
3841 upper -= gtk_adjustment_get_page_size(vadjustment);
3842 priv->vscrolling =
3843 gtk_kinetic_scrolling_new (lower,
3844 upper,
3845 MAX_OVERSHOOT_DISTANCE,
3846 DECELERATION_FRICTION,
3847 OVERSHOOT_FRICTION,
3848 priv->unclamped_vadj_value,
3849 priv->y_velocity);
3850 }
3851 else
3852 g_clear_pointer (&priv->vscrolling, gtk_kinetic_scrolling_free);
3853
3854 scrolled_window->priv->deceleration_id =
3855 gtk_widget_add_tick_callback (GTK_WIDGET (scrolled_window),
3856 scrolled_window_deceleration_cb, scrolled_window,
3857 (GDestroyNotify) kinetic_scroll_stop_notify);
3858 }
3859
3860 static gboolean
gtk_scrolled_window_focus(GtkWidget * widget,GtkDirectionType direction)3861 gtk_scrolled_window_focus (GtkWidget *widget,
3862 GtkDirectionType direction)
3863 {
3864 GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
3865 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
3866 GtkWidget *child;
3867 gboolean had_focus_child;
3868
3869 had_focus_child = gtk_container_get_focus_child (GTK_CONTAINER (widget)) != NULL;
3870
3871 if (priv->focus_out)
3872 {
3873 priv->focus_out = FALSE; /* Clear this to catch the wrap-around case */
3874 return FALSE;
3875 }
3876
3877 if (gtk_widget_is_focus (widget))
3878 return FALSE;
3879
3880 /* We only put the scrolled window itself in the focus chain if it
3881 * isn't possible to focus any children.
3882 */
3883 child = gtk_bin_get_child (GTK_BIN (widget));
3884 if (child)
3885 {
3886 if (gtk_widget_child_focus (child, direction))
3887 return TRUE;
3888 }
3889
3890 if (!had_focus_child && gtk_widget_get_can_focus (widget))
3891 {
3892 gtk_widget_grab_focus (widget);
3893 return TRUE;
3894 }
3895 else
3896 return FALSE;
3897 }
3898
3899 static void
gtk_scrolled_window_adjustment_changed(GtkAdjustment * adjustment,gpointer data)3900 gtk_scrolled_window_adjustment_changed (GtkAdjustment *adjustment,
3901 gpointer data)
3902 {
3903 GtkScrolledWindowPrivate *priv;
3904 GtkScrolledWindow *scrolled_window;
3905
3906 scrolled_window = GTK_SCROLLED_WINDOW (data);
3907 priv = scrolled_window->priv;
3908
3909 if (adjustment == gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar)))
3910 {
3911 if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
3912 {
3913 gboolean visible;
3914
3915 visible = priv->hscrollbar_visible;
3916 priv->hscrollbar_visible = (gtk_adjustment_get_upper (adjustment) - gtk_adjustment_get_lower (adjustment) >
3917 gtk_adjustment_get_page_size (adjustment));
3918
3919 if (priv->hscrollbar_visible != visible)
3920 gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
3921
3922 if (priv->hscrolling)
3923 {
3924 GtkKineticScrollingChange change;
3925 gdouble lower = gtk_adjustment_get_lower (adjustment);
3926 gdouble upper = gtk_adjustment_get_upper (adjustment);
3927 upper -= gtk_adjustment_get_page_size (adjustment);
3928
3929 change = gtk_kinetic_scrolling_update_size (priv->hscrolling, lower, upper);
3930
3931 if ((change & GTK_KINETIC_SCROLLING_CHANGE_IN_OVERSHOOT) &&
3932 (change & (GTK_KINETIC_SCROLLING_CHANGE_UPPER | GTK_KINETIC_SCROLLING_CHANGE_LOWER)))
3933 {
3934 g_clear_pointer (&priv->hscrolling, gtk_kinetic_scrolling_free);
3935 priv->unclamped_hadj_value = gtk_adjustment_get_value (adjustment);
3936 gtk_scrolled_window_invalidate_overshoot (scrolled_window);
3937 }
3938 }
3939 }
3940 }
3941 else if (adjustment == gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar)))
3942 {
3943 if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
3944 {
3945 gboolean visible;
3946
3947 visible = priv->vscrollbar_visible;
3948 priv->vscrollbar_visible = (gtk_adjustment_get_upper (adjustment) - gtk_adjustment_get_lower (adjustment) >
3949 gtk_adjustment_get_page_size (adjustment));
3950
3951 if (priv->vscrollbar_visible != visible)
3952 gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
3953
3954 if (priv->vscrolling)
3955 {
3956 GtkKineticScrollingChange change;
3957 gdouble lower = gtk_adjustment_get_lower (adjustment);
3958 gdouble upper = gtk_adjustment_get_upper (adjustment);
3959 upper -= gtk_adjustment_get_page_size (adjustment);
3960
3961 change = gtk_kinetic_scrolling_update_size (priv->vscrolling, lower, upper);
3962
3963 if ((change & GTK_KINETIC_SCROLLING_CHANGE_IN_OVERSHOOT) &&
3964 (change & (GTK_KINETIC_SCROLLING_CHANGE_UPPER | GTK_KINETIC_SCROLLING_CHANGE_LOWER)))
3965 {
3966 g_clear_pointer (&priv->vscrolling, gtk_kinetic_scrolling_free);
3967 priv->unclamped_vadj_value = gtk_adjustment_get_value (adjustment);
3968 gtk_scrolled_window_invalidate_overshoot (scrolled_window);
3969 }
3970 }
3971 }
3972 }
3973
3974 if (!priv->hscrolling && !priv->vscrolling)
3975 gtk_scrolled_window_cancel_deceleration (scrolled_window);
3976 }
3977
3978 static void
maybe_emit_edge_reached(GtkScrolledWindow * scrolled_window,GtkAdjustment * adjustment)3979 maybe_emit_edge_reached (GtkScrolledWindow *scrolled_window,
3980 GtkAdjustment *adjustment)
3981 {
3982 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
3983 gdouble value, lower, upper, page_size;
3984 GtkPositionType edge_pos;
3985 gboolean vertical;
3986
3987 if (adjustment == gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar)))
3988 vertical = FALSE;
3989 else if (adjustment == gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar)))
3990 vertical = TRUE;
3991 else
3992 return;
3993
3994 value = gtk_adjustment_get_value (adjustment);
3995 lower = gtk_adjustment_get_lower (adjustment);
3996 upper = gtk_adjustment_get_upper (adjustment);
3997 page_size = gtk_adjustment_get_page_size (adjustment);
3998
3999 if (value == lower)
4000 edge_pos = vertical ? GTK_POS_TOP: GTK_POS_LEFT;
4001 else if (value == upper - page_size)
4002 edge_pos = vertical ? GTK_POS_BOTTOM : GTK_POS_RIGHT;
4003 else
4004 return;
4005
4006 if (!vertical &&
4007 gtk_widget_get_direction (GTK_WIDGET (scrolled_window)) == GTK_TEXT_DIR_RTL)
4008 edge_pos = (edge_pos == GTK_POS_LEFT) ? GTK_POS_RIGHT : GTK_POS_LEFT;
4009
4010 g_signal_emit (scrolled_window, signals[EDGE_REACHED], 0, edge_pos);
4011 }
4012
4013 static void
gtk_scrolled_window_adjustment_value_changed(GtkAdjustment * adjustment,gpointer user_data)4014 gtk_scrolled_window_adjustment_value_changed (GtkAdjustment *adjustment,
4015 gpointer user_data)
4016 {
4017 GtkScrolledWindow *scrolled_window = user_data;
4018 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
4019
4020 maybe_emit_edge_reached (scrolled_window, adjustment);
4021
4022 /* Allow overshooting for kinetic scrolling operations */
4023 if (priv->drag_device || priv->deceleration_id)
4024 return;
4025
4026 /* Ensure GtkAdjustment and unclamped values are in sync */
4027 if (adjustment == gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar)))
4028 priv->unclamped_hadj_value = gtk_adjustment_get_value (adjustment);
4029 else if (adjustment == gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar)))
4030 priv->unclamped_vadj_value = gtk_adjustment_get_value (adjustment);
4031 }
4032
4033 static void
gtk_scrolled_window_add(GtkContainer * container,GtkWidget * child)4034 gtk_scrolled_window_add (GtkContainer *container,
4035 GtkWidget *child)
4036 {
4037 GtkScrolledWindowPrivate *priv;
4038 GtkScrolledWindow *scrolled_window;
4039 GtkBin *bin;
4040 GtkWidget *child_widget, *scrollable_child;
4041 GtkAdjustment *hadj, *vadj;
4042
4043 bin = GTK_BIN (container);
4044 child_widget = gtk_bin_get_child (bin);
4045 g_return_if_fail (child_widget == NULL);
4046
4047 scrolled_window = GTK_SCROLLED_WINDOW (container);
4048 priv = scrolled_window->priv;
4049
4050 /* gtk_scrolled_window_set_[hv]adjustment have the side-effect
4051 * of creating the scrollbars
4052 */
4053 if (!priv->hscrollbar)
4054 gtk_scrolled_window_set_hadjustment (scrolled_window, NULL);
4055
4056 if (!priv->vscrollbar)
4057 gtk_scrolled_window_set_vadjustment (scrolled_window, NULL);
4058
4059 hadj = gtk_range_get_adjustment (GTK_RANGE (priv->hscrollbar));
4060 vadj = gtk_range_get_adjustment (GTK_RANGE (priv->vscrollbar));
4061
4062 if (GTK_IS_SCROLLABLE (child))
4063 {
4064 scrollable_child = child;
4065 }
4066 else
4067 {
4068 scrollable_child = gtk_viewport_new (hadj, vadj);
4069 gtk_widget_show (scrollable_child);
4070 gtk_container_set_focus_hadjustment (GTK_CONTAINER (scrollable_child),
4071 gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (scrolled_window)));
4072 gtk_container_set_focus_vadjustment (GTK_CONTAINER (scrollable_child),
4073 gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (scrolled_window)));
4074 gtk_container_add (GTK_CONTAINER (scrollable_child), child);
4075 priv->auto_added_viewport = TRUE;
4076 }
4077
4078 _gtk_bin_set_child (bin, scrollable_child);
4079 gtk_widget_set_parent (scrollable_child, GTK_WIDGET (bin));
4080
4081 g_object_set (scrollable_child, "hadjustment", hadj, "vadjustment", vadj, NULL);
4082 }
4083
4084 static void
gtk_scrolled_window_remove(GtkContainer * container,GtkWidget * child)4085 gtk_scrolled_window_remove (GtkContainer *container,
4086 GtkWidget *child)
4087 {
4088 GtkScrolledWindowPrivate *priv;
4089 GtkScrolledWindow *scrolled_window;
4090 GtkWidget *scrollable_child;
4091
4092 scrolled_window = GTK_SCROLLED_WINDOW (container);
4093 priv = scrolled_window->priv;
4094
4095 if (!priv->auto_added_viewport)
4096 {
4097 scrollable_child = child;
4098 }
4099 else
4100 {
4101 scrollable_child = gtk_bin_get_child (GTK_BIN (container));
4102 if (scrollable_child == child)
4103 {
4104 /* @child is the automatically added viewport. */
4105 GtkWidget *grandchild = gtk_bin_get_child (GTK_BIN (child));
4106
4107 /* Remove the viewport's child, if any. */
4108 if (grandchild)
4109 gtk_container_remove (GTK_CONTAINER (child), grandchild);
4110 }
4111 else
4112 {
4113 /* @child is (assumed to be) the viewport's child. */
4114 gtk_container_remove (GTK_CONTAINER (scrollable_child), child);
4115 }
4116 }
4117
4118 g_object_set (scrollable_child, "hadjustment", NULL, "vadjustment", NULL, NULL);
4119
4120 GTK_CONTAINER_CLASS (gtk_scrolled_window_parent_class)->remove (container, scrollable_child);
4121
4122 priv->auto_added_viewport = FALSE;
4123 }
4124
4125 /**
4126 * gtk_scrolled_window_add_with_viewport:
4127 * @scrolled_window: a #GtkScrolledWindow
4128 * @child: the widget you want to scroll
4129 *
4130 * Used to add children without native scrolling capabilities. This
4131 * is simply a convenience function; it is equivalent to adding the
4132 * unscrollable child to a viewport, then adding the viewport to the
4133 * scrolled window. If a child has native scrolling, use
4134 * gtk_container_add() instead of this function.
4135 *
4136 * The viewport scrolls the child by moving its #GdkWindow, and takes
4137 * the size of the child to be the size of its toplevel #GdkWindow.
4138 * This will be very wrong for most widgets that support native scrolling;
4139 * for example, if you add a widget such as #GtkTreeView with a viewport,
4140 * the whole widget will scroll, including the column headings. Thus,
4141 * widgets with native scrolling support should not be used with the
4142 * #GtkViewport proxy.
4143 *
4144 * A widget supports scrolling natively if it implements the
4145 * #GtkScrollable interface.
4146 *
4147 * Deprecated: 3.8: gtk_container_add() will automatically add
4148 * a #GtkViewport if the child doesn’t implement #GtkScrollable.
4149 */
4150 void
gtk_scrolled_window_add_with_viewport(GtkScrolledWindow * scrolled_window,GtkWidget * child)4151 gtk_scrolled_window_add_with_viewport (GtkScrolledWindow *scrolled_window,
4152 GtkWidget *child)
4153 {
4154 GtkBin *bin;
4155 GtkWidget *viewport;
4156 GtkWidget *child_widget;
4157
4158 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
4159 g_return_if_fail (GTK_IS_WIDGET (child));
4160 g_return_if_fail (gtk_widget_get_parent (child) == NULL);
4161
4162 bin = GTK_BIN (scrolled_window);
4163 child_widget = gtk_bin_get_child (bin);
4164
4165 if (child_widget)
4166 {
4167 g_return_if_fail (GTK_IS_VIEWPORT (child_widget));
4168 g_return_if_fail (gtk_bin_get_child (GTK_BIN (child_widget)) == NULL);
4169
4170 viewport = child_widget;
4171 }
4172 else
4173 {
4174 viewport =
4175 gtk_viewport_new (gtk_scrolled_window_get_hadjustment (scrolled_window),
4176 gtk_scrolled_window_get_vadjustment (scrolled_window));
4177 gtk_container_set_focus_hadjustment (GTK_CONTAINER (viewport),
4178 gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (scrolled_window)));
4179 gtk_container_set_focus_vadjustment (GTK_CONTAINER (viewport),
4180 gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (scrolled_window)));
4181 gtk_container_add (GTK_CONTAINER (scrolled_window), viewport);
4182 }
4183
4184 gtk_widget_show (viewport);
4185 gtk_container_add (GTK_CONTAINER (viewport), child);
4186 }
4187
4188 static void
gtk_scrolled_window_get_preferred_width(GtkWidget * widget,gint * minimum_size,gint * natural_size)4189 gtk_scrolled_window_get_preferred_width (GtkWidget *widget,
4190 gint *minimum_size,
4191 gint *natural_size)
4192 {
4193 gtk_css_gadget_get_preferred_size (GTK_SCROLLED_WINDOW (widget)->priv->gadget,
4194 GTK_ORIENTATION_HORIZONTAL,
4195 -1,
4196 minimum_size, natural_size,
4197 NULL, NULL);
4198 }
4199
4200 static void
gtk_scrolled_window_get_preferred_height(GtkWidget * widget,gint * minimum_size,gint * natural_size)4201 gtk_scrolled_window_get_preferred_height (GtkWidget *widget,
4202 gint *minimum_size,
4203 gint *natural_size)
4204 {
4205 gtk_css_gadget_get_preferred_size (GTK_SCROLLED_WINDOW (widget)->priv->gadget,
4206 GTK_ORIENTATION_VERTICAL,
4207 -1,
4208 minimum_size, natural_size,
4209 NULL, NULL);
4210 }
4211
4212 static void
gtk_scrolled_window_get_preferred_height_for_width(GtkWidget * widget,gint width,gint * minimum_height,gint * natural_height)4213 gtk_scrolled_window_get_preferred_height_for_width (GtkWidget *widget,
4214 gint width,
4215 gint *minimum_height,
4216 gint *natural_height)
4217 {
4218 GTK_WIDGET_GET_CLASS (widget)->get_preferred_height (widget, minimum_height, natural_height);
4219 }
4220
4221 static void
gtk_scrolled_window_get_preferred_width_for_height(GtkWidget * widget,gint height,gint * minimum_width,gint * natural_width)4222 gtk_scrolled_window_get_preferred_width_for_height (GtkWidget *widget,
4223 gint height,
4224 gint *minimum_width,
4225 gint *natural_width)
4226 {
4227 GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, minimum_width, natural_width);
4228 }
4229
4230 static gboolean
gtk_widget_should_animate(GtkWidget * widget)4231 gtk_widget_should_animate (GtkWidget *widget)
4232 {
4233 if (!gtk_widget_get_mapped (widget))
4234 return FALSE;
4235
4236 return gtk_settings_get_enable_animations (gtk_widget_get_settings (widget));
4237 }
4238
4239 static void
gtk_scrolled_window_update_animating(GtkScrolledWindow * sw)4240 gtk_scrolled_window_update_animating (GtkScrolledWindow *sw)
4241 {
4242 GtkAdjustment *adjustment;
4243 GdkFrameClock *clock = NULL;
4244 guint duration = 0;
4245
4246 if (gtk_widget_should_animate (GTK_WIDGET (sw)))
4247 {
4248 clock = gtk_widget_get_frame_clock (GTK_WIDGET (sw)),
4249 duration = ANIMATION_DURATION;
4250 }
4251
4252 adjustment = gtk_range_get_adjustment (GTK_RANGE (sw->priv->hscrollbar));
4253 gtk_adjustment_enable_animation (adjustment, clock, duration);
4254
4255 adjustment = gtk_range_get_adjustment (GTK_RANGE (sw->priv->vscrollbar));
4256 gtk_adjustment_enable_animation (adjustment, clock, duration);
4257 }
4258
4259 static void
gtk_scrolled_window_map(GtkWidget * widget)4260 gtk_scrolled_window_map (GtkWidget *widget)
4261 {
4262 GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
4263
4264 GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->map (widget);
4265
4266 gtk_scrolled_window_update_animating (scrolled_window);
4267 gtk_scrolled_window_update_use_indicators (scrolled_window);
4268 }
4269
4270 static void
gtk_scrolled_window_unmap(GtkWidget * widget)4271 gtk_scrolled_window_unmap (GtkWidget *widget)
4272 {
4273 GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
4274
4275 GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->unmap (widget);
4276
4277 gtk_scrolled_window_update_animating (scrolled_window);
4278
4279 indicator_stop_fade (&scrolled_window->priv->hindicator);
4280 indicator_stop_fade (&scrolled_window->priv->vindicator);
4281 }
4282
4283 static GdkWindow *
create_indicator_window(GtkScrolledWindow * scrolled_window,GtkWidget * child)4284 create_indicator_window (GtkScrolledWindow *scrolled_window,
4285 GtkWidget *child)
4286 {
4287 GtkWidget *widget = GTK_WIDGET (scrolled_window);
4288 GdkRGBA transparent = { 0, 0, 0, 0 };
4289 GtkAllocation allocation;
4290 GdkWindow *window;
4291 GdkWindowAttr attributes;
4292 gint attributes_mask;
4293
4294 gtk_scrolled_window_allocate_scrollbar (scrolled_window, child, &allocation);
4295
4296 attributes.window_type = GDK_WINDOW_CHILD;
4297 attributes.wclass = GDK_INPUT_OUTPUT;
4298
4299 attributes.width = allocation.width;
4300 attributes.height = allocation.height;
4301 attributes.x = allocation.x;
4302 attributes.y = allocation.y;
4303 attributes.visual = gtk_widget_get_visual (widget);
4304 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
4305 attributes.event_mask = gtk_widget_get_events (widget);
4306
4307 window = gdk_window_new (gtk_widget_get_window (widget),
4308 &attributes, attributes_mask);
4309 gtk_widget_register_window (widget, window);
4310
4311 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
4312 gdk_window_set_background_rgba (window, &transparent);
4313 G_GNUC_END_IGNORE_DEPRECATIONS
4314
4315 if (scrolled_window->priv->use_indicators)
4316 gtk_widget_set_parent_window (child, window);
4317
4318 return window;
4319 }
4320
4321 static void
indicator_set_fade(Indicator * indicator,gdouble pos)4322 indicator_set_fade (Indicator *indicator,
4323 gdouble pos)
4324 {
4325 gboolean visible, changed;
4326
4327 changed = indicator->current_pos != pos;
4328 indicator->current_pos = pos;
4329
4330 visible = indicator->current_pos != 0.0 || indicator->target_pos != 0.0;
4331
4332 if (visible && !gdk_window_is_visible (indicator->window))
4333 {
4334 gdk_window_show (indicator->window);
4335 indicator->conceil_timer = g_timeout_add (INDICATOR_FADE_OUT_TIME, maybe_hide_indicator, indicator);
4336 }
4337 if (!visible && gdk_window_is_visible (indicator->window) &&
4338 indicator->conceil_timer != 0)
4339 {
4340 gdk_window_hide (indicator->window);
4341 g_source_remove (indicator->conceil_timer);
4342 indicator->conceil_timer = 0;
4343 }
4344
4345 if (changed)
4346 {
4347 gtk_widget_set_opacity (indicator->scrollbar, indicator->current_pos);
4348 gtk_widget_queue_draw (indicator->scrollbar);
4349 }
4350 }
4351
4352 static gboolean
indicator_fade_cb(GtkWidget * widget,GdkFrameClock * frame_clock,gpointer user_data)4353 indicator_fade_cb (GtkWidget *widget,
4354 GdkFrameClock *frame_clock,
4355 gpointer user_data)
4356 {
4357 Indicator *indicator = user_data;
4358 gdouble t;
4359
4360 gtk_progress_tracker_advance_frame (&indicator->tracker,
4361 gdk_frame_clock_get_frame_time (frame_clock));
4362 t = gtk_progress_tracker_get_ease_out_cubic (&indicator->tracker, FALSE);
4363
4364 indicator_set_fade (indicator,
4365 indicator->source_pos + (t * (indicator->target_pos - indicator->source_pos)));
4366
4367 if (gtk_progress_tracker_get_state (&indicator->tracker) == GTK_PROGRESS_STATE_AFTER)
4368 {
4369 indicator->tick_id = 0;
4370 return FALSE;
4371 }
4372
4373 return TRUE;
4374 }
4375
4376 static void
indicator_start_fade(Indicator * indicator,gdouble target)4377 indicator_start_fade (Indicator *indicator,
4378 gdouble target)
4379 {
4380 if (indicator->target_pos == target)
4381 return;
4382
4383 indicator->target_pos = target;
4384
4385 if (target != 0.0)
4386 indicator->last_scroll_time = g_get_monotonic_time ();
4387
4388 if (gtk_widget_should_animate (indicator->scrollbar))
4389 {
4390 indicator->source_pos = indicator->current_pos;
4391 gtk_progress_tracker_start (&indicator->tracker, INDICATOR_FADE_OUT_DURATION * 1000, 0, 1.0);
4392 if (indicator->tick_id == 0)
4393 indicator->tick_id = gtk_widget_add_tick_callback (indicator->scrollbar, indicator_fade_cb, indicator, NULL);
4394 }
4395 else
4396 indicator_set_fade (indicator, target);
4397 }
4398
4399 static void
indicator_stop_fade(Indicator * indicator)4400 indicator_stop_fade (Indicator *indicator)
4401 {
4402 if (indicator->tick_id != 0)
4403 {
4404 indicator_set_fade (indicator, indicator->target_pos);
4405 gtk_widget_remove_tick_callback (indicator->scrollbar, indicator->tick_id);
4406 indicator->tick_id = 0;
4407 }
4408
4409 if (indicator->conceil_timer)
4410 {
4411 g_source_remove (indicator->conceil_timer);
4412 indicator->conceil_timer = 0;
4413 }
4414
4415 gdk_window_hide (indicator->window);
4416 gtk_progress_tracker_finish (&indicator->tracker);
4417 indicator->current_pos = indicator->source_pos = indicator->target_pos = 0;
4418 indicator->last_scroll_time = 0;
4419 }
4420
4421 static gboolean
maybe_hide_indicator(gpointer data)4422 maybe_hide_indicator (gpointer data)
4423 {
4424 Indicator *indicator = data;
4425
4426 if (g_get_monotonic_time () - indicator->last_scroll_time >= INDICATOR_FADE_OUT_DELAY * 1000 &&
4427 !indicator->over)
4428 indicator_start_fade (indicator, 0.0);
4429
4430 return G_SOURCE_CONTINUE;
4431 }
4432
4433 static void
indicator_value_changed(GtkAdjustment * adjustment,Indicator * indicator)4434 indicator_value_changed (GtkAdjustment *adjustment,
4435 Indicator *indicator)
4436 {
4437 indicator->last_scroll_time = g_get_monotonic_time ();
4438 indicator_start_fade (indicator, 1.0);
4439 }
4440
4441 static void
setup_indicator(GtkScrolledWindow * scrolled_window,Indicator * indicator,GtkWidget * scrollbar)4442 setup_indicator (GtkScrolledWindow *scrolled_window,
4443 Indicator *indicator,
4444 GtkWidget *scrollbar)
4445 {
4446 GtkStyleContext *context;
4447 GtkAdjustment *adjustment;
4448
4449 if (scrollbar == NULL)
4450 return;
4451
4452 context = gtk_widget_get_style_context (scrollbar);
4453 adjustment = gtk_range_get_adjustment (GTK_RANGE (scrollbar));
4454
4455 indicator->scrollbar = scrollbar;
4456
4457 g_object_ref (scrollbar);
4458 gtk_widget_unparent (scrollbar);
4459 gtk_widget_set_parent_window (scrollbar, indicator->window);
4460 gtk_widget_set_parent (scrollbar, GTK_WIDGET (scrolled_window));
4461 g_object_unref (scrollbar);
4462
4463 gtk_style_context_add_class (context, "overlay-indicator");
4464 g_signal_connect (adjustment, "value-changed",
4465 G_CALLBACK (indicator_value_changed), indicator);
4466
4467 gdk_window_hide (indicator->window);
4468 gtk_widget_set_opacity (scrollbar, 0.0);
4469 indicator->current_pos = 0.0;
4470 }
4471
4472 static void
remove_indicator(GtkScrolledWindow * scrolled_window,Indicator * indicator)4473 remove_indicator (GtkScrolledWindow *scrolled_window,
4474 Indicator *indicator)
4475 {
4476 GtkWidget *scrollbar;
4477 GtkStyleContext *context;
4478 GtkAdjustment *adjustment;
4479
4480 if (indicator->scrollbar == NULL)
4481 return;
4482
4483 scrollbar = indicator->scrollbar;
4484 indicator->scrollbar = NULL;
4485
4486 context = gtk_widget_get_style_context (scrollbar);
4487 gtk_style_context_remove_class (context, "overlay-indicator");
4488
4489 adjustment = gtk_range_get_adjustment (GTK_RANGE (scrollbar));
4490 g_signal_handlers_disconnect_by_data (adjustment, indicator);
4491
4492 if (indicator->conceil_timer)
4493 {
4494 g_source_remove (indicator->conceil_timer);
4495 indicator->conceil_timer = 0;
4496 }
4497
4498 if (indicator->over_timeout_id)
4499 {
4500 g_source_remove (indicator->over_timeout_id);
4501 indicator->over_timeout_id = 0;
4502 }
4503
4504 if (indicator->tick_id)
4505 {
4506 gtk_widget_remove_tick_callback (scrollbar, indicator->tick_id);
4507 indicator->tick_id = 0;
4508 }
4509
4510 g_object_ref (scrollbar);
4511 gtk_widget_unparent (scrollbar);
4512 gtk_widget_set_parent (scrollbar, GTK_WIDGET (scrolled_window));
4513 g_object_unref (scrollbar);
4514
4515 if (indicator->window)
4516 gdk_window_hide (indicator->window);
4517
4518 gtk_widget_set_opacity (scrollbar, 1.0);
4519 indicator->current_pos = 1.0;
4520 }
4521
4522 static void
gtk_scrolled_window_sync_use_indicators(GtkScrolledWindow * scrolled_window)4523 gtk_scrolled_window_sync_use_indicators (GtkScrolledWindow *scrolled_window)
4524 {
4525 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
4526
4527 if (priv->use_indicators)
4528 {
4529 setup_indicator (scrolled_window, &priv->hindicator, priv->hscrollbar);
4530 setup_indicator (scrolled_window, &priv->vindicator, priv->vscrollbar);
4531 }
4532 else
4533 {
4534 remove_indicator (scrolled_window, &priv->hindicator);
4535 remove_indicator (scrolled_window, &priv->vindicator);
4536 }
4537 }
4538
4539 static void
gtk_scrolled_window_update_use_indicators(GtkScrolledWindow * scrolled_window)4540 gtk_scrolled_window_update_use_indicators (GtkScrolledWindow *scrolled_window)
4541 {
4542 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
4543 gboolean use_indicators;
4544 GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (scrolled_window));
4545 gboolean overlay_scrolling;
4546
4547 g_object_get (settings, "gtk-overlay-scrolling", &overlay_scrolling, NULL);
4548
4549 use_indicators = overlay_scrolling && priv->overlay_scrolling;
4550
4551 if (g_strcmp0 (g_getenv ("GTK_OVERLAY_SCROLLING"), "0") == 0)
4552 use_indicators = FALSE;
4553
4554 if (priv->use_indicators != use_indicators)
4555 {
4556 priv->use_indicators = use_indicators;
4557
4558 if (gtk_widget_get_realized (GTK_WIDGET (scrolled_window)))
4559 gtk_scrolled_window_sync_use_indicators (scrolled_window);
4560
4561 gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
4562 }
4563 }
4564
4565 static void
gtk_scrolled_window_realize(GtkWidget * widget)4566 gtk_scrolled_window_realize (GtkWidget *widget)
4567 {
4568 GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
4569 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
4570 GdkWindow *window;
4571 GtkAllocation allocation;
4572 GdkWindowAttr attributes;
4573 gint attributes_mask;
4574
4575 gtk_widget_get_allocation (widget, &allocation);
4576
4577 attributes.window_type = GDK_WINDOW_CHILD;
4578 attributes.wclass = GDK_INPUT_OUTPUT;
4579
4580 attributes.width = allocation.width;
4581 attributes.height = allocation.height;
4582 attributes.x = allocation.x;
4583 attributes.y = allocation.y;
4584 attributes.visual = gtk_widget_get_visual (widget);
4585 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
4586 attributes.event_mask = gtk_widget_get_events (widget) |
4587 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_POINTER_MOTION_MASK;
4588
4589 window = gdk_window_new (gtk_widget_get_parent_window (widget),
4590 &attributes, attributes_mask);
4591
4592 gtk_widget_set_window (widget, window);
4593 gtk_widget_register_window (widget, window);
4594 gtk_widget_set_realized (widget, TRUE);
4595
4596 priv->hindicator.window = create_indicator_window (scrolled_window, priv->hscrollbar);
4597 priv->vindicator.window = create_indicator_window (scrolled_window, priv->vscrollbar);
4598
4599 priv->hindicator.scrollbar = priv->hscrollbar;
4600 priv->vindicator.scrollbar = priv->vscrollbar;
4601
4602 gtk_scrolled_window_sync_use_indicators (scrolled_window);
4603 }
4604
4605 static void
indicator_reset(Indicator * indicator)4606 indicator_reset (Indicator *indicator)
4607 {
4608 if (indicator->conceil_timer)
4609 {
4610 g_source_remove (indicator->conceil_timer);
4611 indicator->conceil_timer = 0;
4612 }
4613
4614 if (indicator->over_timeout_id)
4615 {
4616 g_source_remove (indicator->over_timeout_id);
4617 indicator->over_timeout_id = 0;
4618 }
4619
4620 if (indicator->scrollbar && indicator->tick_id)
4621 {
4622 gtk_widget_remove_tick_callback (indicator->scrollbar,
4623 indicator->tick_id);
4624 indicator->tick_id = 0;
4625 }
4626
4627 if (indicator->window)
4628 {
4629 gdk_window_destroy (indicator->window);
4630 indicator->window = NULL;
4631 }
4632
4633 indicator->scrollbar = NULL;
4634 indicator->over = FALSE;
4635 gtk_progress_tracker_finish (&indicator->tracker);
4636 indicator->current_pos = indicator->source_pos = indicator->target_pos = 0;
4637 indicator->last_scroll_time = 0;
4638 }
4639
4640 static void
gtk_scrolled_window_unrealize(GtkWidget * widget)4641 gtk_scrolled_window_unrealize (GtkWidget *widget)
4642 {
4643 GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
4644 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
4645
4646 gtk_widget_set_parent_window (priv->hscrollbar, NULL);
4647 gtk_widget_unregister_window (widget, priv->hindicator.window);
4648 indicator_reset (&priv->hindicator);
4649
4650 gtk_widget_set_parent_window (priv->vscrollbar, NULL);
4651 gtk_widget_unregister_window (widget, priv->vindicator.window);
4652 indicator_reset (&priv->vindicator);
4653
4654 GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->unrealize (widget);
4655 }
4656
4657 static void
gtk_scrolled_window_grab_notify(GtkWidget * widget,gboolean was_grabbed)4658 gtk_scrolled_window_grab_notify (GtkWidget *widget,
4659 gboolean was_grabbed)
4660 {
4661 GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
4662 GtkScrolledWindowPrivate *priv = scrolled_window->priv;
4663
4664 if (priv->drag_device &&
4665 gtk_widget_device_is_shadowed (widget,
4666 priv->drag_device))
4667 {
4668 if (_gtk_scrolled_window_get_overshoot (scrolled_window, NULL, NULL))
4669 gtk_scrolled_window_start_deceleration (scrolled_window);
4670 else
4671 gtk_scrolled_window_cancel_deceleration (scrolled_window);
4672 }
4673 }
4674
4675 /**
4676 * gtk_scrolled_window_get_min_content_width:
4677 * @scrolled_window: a #GtkScrolledWindow
4678 *
4679 * Gets the minimum content width of @scrolled_window, or -1 if not set.
4680 *
4681 * Returns: the minimum content width
4682 *
4683 * Since: 3.0
4684 */
4685 gint
gtk_scrolled_window_get_min_content_width(GtkScrolledWindow * scrolled_window)4686 gtk_scrolled_window_get_min_content_width (GtkScrolledWindow *scrolled_window)
4687 {
4688 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), 0);
4689
4690 return scrolled_window->priv->min_content_width;
4691 }
4692
4693 /**
4694 * gtk_scrolled_window_set_min_content_width:
4695 * @scrolled_window: a #GtkScrolledWindow
4696 * @width: the minimal content width
4697 *
4698 * Sets the minimum width that @scrolled_window should keep visible.
4699 * Note that this can and (usually will) be smaller than the minimum
4700 * size of the content.
4701 *
4702 * It is a programming error to set the minimum content width to a
4703 * value greater than #GtkScrolledWindow:max-content-width.
4704 *
4705 * Since: 3.0
4706 */
4707 void
gtk_scrolled_window_set_min_content_width(GtkScrolledWindow * scrolled_window,gint width)4708 gtk_scrolled_window_set_min_content_width (GtkScrolledWindow *scrolled_window,
4709 gint width)
4710 {
4711 GtkScrolledWindowPrivate *priv;
4712
4713 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
4714
4715 priv = scrolled_window->priv;
4716
4717 g_return_if_fail (width == -1 || priv->max_content_width == -1 || width <= priv->max_content_width);
4718
4719 if (priv->min_content_width != width)
4720 {
4721 priv->min_content_width = width;
4722
4723 gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
4724
4725 g_object_notify_by_pspec (G_OBJECT (scrolled_window), properties[PROP_MIN_CONTENT_WIDTH]);
4726 }
4727 }
4728
4729 /**
4730 * gtk_scrolled_window_get_min_content_height:
4731 * @scrolled_window: a #GtkScrolledWindow
4732 *
4733 * Gets the minimal content height of @scrolled_window, or -1 if not set.
4734 *
4735 * Returns: the minimal content height
4736 *
4737 * Since: 3.0
4738 */
4739 gint
gtk_scrolled_window_get_min_content_height(GtkScrolledWindow * scrolled_window)4740 gtk_scrolled_window_get_min_content_height (GtkScrolledWindow *scrolled_window)
4741 {
4742 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), 0);
4743
4744 return scrolled_window->priv->min_content_height;
4745 }
4746
4747 /**
4748 * gtk_scrolled_window_set_min_content_height:
4749 * @scrolled_window: a #GtkScrolledWindow
4750 * @height: the minimal content height
4751 *
4752 * Sets the minimum height that @scrolled_window should keep visible.
4753 * Note that this can and (usually will) be smaller than the minimum
4754 * size of the content.
4755 *
4756 * It is a programming error to set the minimum content height to a
4757 * value greater than #GtkScrolledWindow:max-content-height.
4758 *
4759 * Since: 3.0
4760 */
4761 void
gtk_scrolled_window_set_min_content_height(GtkScrolledWindow * scrolled_window,gint height)4762 gtk_scrolled_window_set_min_content_height (GtkScrolledWindow *scrolled_window,
4763 gint height)
4764 {
4765 GtkScrolledWindowPrivate *priv;
4766
4767 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
4768
4769 priv = scrolled_window->priv;
4770
4771 g_return_if_fail (height == -1 || priv->max_content_height == -1 || height <= priv->max_content_height);
4772
4773 if (priv->min_content_height != height)
4774 {
4775 priv->min_content_height = height;
4776
4777 gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
4778
4779 g_object_notify_by_pspec (G_OBJECT (scrolled_window), properties[PROP_MIN_CONTENT_HEIGHT]);
4780 }
4781 }
4782
4783 /**
4784 * gtk_scrolled_window_set_overlay_scrolling:
4785 * @scrolled_window: a #GtkScrolledWindow
4786 * @overlay_scrolling: whether to enable overlay scrolling
4787 *
4788 * Enables or disables overlay scrolling for this scrolled window.
4789 *
4790 * Since: 3.16
4791 */
4792 void
gtk_scrolled_window_set_overlay_scrolling(GtkScrolledWindow * scrolled_window,gboolean overlay_scrolling)4793 gtk_scrolled_window_set_overlay_scrolling (GtkScrolledWindow *scrolled_window,
4794 gboolean overlay_scrolling)
4795 {
4796 GtkScrolledWindowPrivate *priv;
4797
4798 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
4799
4800 priv = scrolled_window->priv;
4801
4802 if (priv->overlay_scrolling != overlay_scrolling)
4803 {
4804 priv->overlay_scrolling = overlay_scrolling;
4805
4806 gtk_scrolled_window_update_use_indicators (scrolled_window);
4807
4808 g_object_notify_by_pspec (G_OBJECT (scrolled_window), properties[PROP_OVERLAY_SCROLLING]);
4809 }
4810 }
4811
4812 /**
4813 * gtk_scrolled_window_get_overlay_scrolling:
4814 * @scrolled_window: a #GtkScrolledWindow
4815 *
4816 * Returns whether overlay scrolling is enabled for this scrolled window.
4817 *
4818 * Returns: %TRUE if overlay scrolling is enabled
4819 *
4820 * Since: 3.16
4821 */
4822 gboolean
gtk_scrolled_window_get_overlay_scrolling(GtkScrolledWindow * scrolled_window)4823 gtk_scrolled_window_get_overlay_scrolling (GtkScrolledWindow *scrolled_window)
4824 {
4825 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), TRUE);
4826
4827 return scrolled_window->priv->overlay_scrolling;
4828 }
4829
4830 /**
4831 * gtk_scrolled_window_set_max_content_width:
4832 * @scrolled_window: a #GtkScrolledWindow
4833 * @width: the maximum content width
4834 *
4835 * Sets the maximum width that @scrolled_window should keep visible. The
4836 * @scrolled_window will grow up to this width before it starts scrolling
4837 * the content.
4838 *
4839 * It is a programming error to set the maximum content width to a value
4840 * smaller than #GtkScrolledWindow:min-content-width.
4841 *
4842 * Since: 3.22
4843 */
4844 void
gtk_scrolled_window_set_max_content_width(GtkScrolledWindow * scrolled_window,gint width)4845 gtk_scrolled_window_set_max_content_width (GtkScrolledWindow *scrolled_window,
4846 gint width)
4847 {
4848 GtkScrolledWindowPrivate *priv;
4849
4850 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
4851
4852 priv = scrolled_window->priv;
4853
4854 g_return_if_fail (width == -1 || priv->min_content_width == -1 || width >= priv->min_content_width);
4855
4856 if (width != priv->max_content_width)
4857 {
4858 priv->max_content_width = width;
4859 g_object_notify_by_pspec (G_OBJECT (scrolled_window), properties [PROP_MAX_CONTENT_WIDTH]);
4860 gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
4861 }
4862 }
4863
4864 /**
4865 * gtk_scrolled_window_get_max_content_width:
4866 * @scrolled_window: a #GtkScrolledWindow
4867 *
4868 * Returns the maximum content width set.
4869 *
4870 * Returns: the maximum content width, or -1
4871 *
4872 * Since: 3.22
4873 */
4874 gint
gtk_scrolled_window_get_max_content_width(GtkScrolledWindow * scrolled_window)4875 gtk_scrolled_window_get_max_content_width (GtkScrolledWindow *scrolled_window)
4876 {
4877 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), -1);
4878
4879 return scrolled_window->priv->max_content_width;
4880 }
4881
4882 /**
4883 * gtk_scrolled_window_set_max_content_height:
4884 * @scrolled_window: a #GtkScrolledWindow
4885 * @height: the maximum content height
4886 *
4887 * Sets the maximum height that @scrolled_window should keep visible. The
4888 * @scrolled_window will grow up to this height before it starts scrolling
4889 * the content.
4890 *
4891 * It is a programming error to set the maximum content height to a value
4892 * smaller than #GtkScrolledWindow:min-content-height.
4893 *
4894 * Since: 3.22
4895 */
4896 void
gtk_scrolled_window_set_max_content_height(GtkScrolledWindow * scrolled_window,gint height)4897 gtk_scrolled_window_set_max_content_height (GtkScrolledWindow *scrolled_window,
4898 gint height)
4899 {
4900 GtkScrolledWindowPrivate *priv;
4901
4902 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
4903
4904 priv = scrolled_window->priv;
4905
4906 g_return_if_fail (height == -1 || priv->min_content_height == -1 || height >= priv->min_content_height);
4907
4908 if (height != priv->max_content_height)
4909 {
4910 priv->max_content_height = height;
4911 g_object_notify_by_pspec (G_OBJECT (scrolled_window), properties [PROP_MAX_CONTENT_HEIGHT]);
4912 gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
4913 }
4914 }
4915
4916 /**
4917 * gtk_scrolled_window_get_max_content_height:
4918 * @scrolled_window: a #GtkScrolledWindow
4919 *
4920 * Returns the maximum content height set.
4921 *
4922 * Returns: the maximum content height, or -1
4923 *
4924 * Since: 3.22
4925 */
4926 gint
gtk_scrolled_window_get_max_content_height(GtkScrolledWindow * scrolled_window)4927 gtk_scrolled_window_get_max_content_height (GtkScrolledWindow *scrolled_window)
4928 {
4929 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), -1);
4930
4931 return scrolled_window->priv->max_content_height;
4932 }
4933
4934 /**
4935 * gtk_scrolled_window_set_propagate_natural_width:
4936 * @scrolled_window: a #GtkScrolledWindow
4937 * @propagate: whether to propagate natural width
4938 *
4939 * Sets whether the natural width of the child should be calculated and propagated
4940 * through the scrolled window’s requested natural width.
4941 *
4942 * Since: 3.22
4943 */
4944 void
gtk_scrolled_window_set_propagate_natural_width(GtkScrolledWindow * scrolled_window,gboolean propagate)4945 gtk_scrolled_window_set_propagate_natural_width (GtkScrolledWindow *scrolled_window,
4946 gboolean propagate)
4947 {
4948 GtkScrolledWindowPrivate *priv;
4949
4950 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
4951
4952 priv = scrolled_window->priv;
4953
4954 propagate = !!propagate;
4955
4956 if (priv->propagate_natural_width != propagate)
4957 {
4958 priv->propagate_natural_width = propagate;
4959 g_object_notify_by_pspec (G_OBJECT (scrolled_window), properties [PROP_PROPAGATE_NATURAL_WIDTH]);
4960 gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
4961 }
4962 }
4963
4964 /**
4965 * gtk_scrolled_window_get_propagate_natural_width:
4966 * @scrolled_window: a #GtkScrolledWindow
4967 *
4968 * Reports whether the natural width of the child will be calculated and propagated
4969 * through the scrolled window’s requested natural width.
4970 *
4971 * Returns: whether natural width propagation is enabled.
4972 *
4973 * Since: 3.22
4974 */
4975 gboolean
gtk_scrolled_window_get_propagate_natural_width(GtkScrolledWindow * scrolled_window)4976 gtk_scrolled_window_get_propagate_natural_width (GtkScrolledWindow *scrolled_window)
4977 {
4978 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), -1);
4979
4980 return scrolled_window->priv->propagate_natural_width;
4981 }
4982
4983 /**
4984 * gtk_scrolled_window_set_propagate_natural_height:
4985 * @scrolled_window: a #GtkScrolledWindow
4986 * @propagate: whether to propagate natural height
4987 *
4988 * Sets whether the natural height of the child should be calculated and propagated
4989 * through the scrolled window’s requested natural height.
4990 *
4991 * Since: 3.22
4992 */
4993 void
gtk_scrolled_window_set_propagate_natural_height(GtkScrolledWindow * scrolled_window,gboolean propagate)4994 gtk_scrolled_window_set_propagate_natural_height (GtkScrolledWindow *scrolled_window,
4995 gboolean propagate)
4996 {
4997 GtkScrolledWindowPrivate *priv;
4998
4999 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
5000
5001 priv = scrolled_window->priv;
5002
5003 propagate = !!propagate;
5004
5005 if (priv->propagate_natural_height != propagate)
5006 {
5007 priv->propagate_natural_height = propagate;
5008 g_object_notify_by_pspec (G_OBJECT (scrolled_window), properties [PROP_PROPAGATE_NATURAL_HEIGHT]);
5009 gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
5010 }
5011 }
5012
5013 /**
5014 * gtk_scrolled_window_get_propagate_natural_height:
5015 * @scrolled_window: a #GtkScrolledWindow
5016 *
5017 * Reports whether the natural height of the child will be calculated and propagated
5018 * through the scrolled window’s requested natural height.
5019 *
5020 * Returns: whether natural height propagation is enabled.
5021 *
5022 * Since: 3.22
5023 */
5024 gboolean
gtk_scrolled_window_get_propagate_natural_height(GtkScrolledWindow * scrolled_window)5025 gtk_scrolled_window_get_propagate_natural_height (GtkScrolledWindow *scrolled_window)
5026 {
5027 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), -1);
5028
5029 return scrolled_window->priv->propagate_natural_height;
5030 }
5031