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 "gtkbuildable.h"
32 #include "gtkdragsourceprivate.h"
33 #include "gtkeventcontrollermotion.h"
34 #include "gtkeventcontrollerscroll.h"
35 #include "gtkeventcontrollerprivate.h"
36 #include "gtkgesturedrag.h"
37 #include "gtkgesturelongpress.h"
38 #include "gtkgesturepan.h"
39 #include "gtkgesturesingle.h"
40 #include "gtkgestureswipe.h"
41 #include "gtkgestureprivate.h"
42 #include "gtkintl.h"
43 #include "gtkkineticscrollingprivate.h"
44 #include "gtkmain.h"
45 #include "gtkmarshalers.h"
46 #include "gtkprivate.h"
47 #include "gtkprogresstrackerprivate.h"
48 #include "gtkscrollable.h"
49 #include "gtkscrollbar.h"
50 #include "gtksettingsprivate.h"
51 #include "gtksnapshot.h"
52 #include "gtkstylecontextprivate.h"
53 #include "gtktypebuiltins.h"
54 #include "gtkviewport.h"
55 #include "gtkwidgetprivate.h"
56
57 #include <math.h>
58
59 /**
60 * GtkScrolledWindow:
61 *
62 * `GtkScrolledWindow` is a container that makes its child scrollable.
63 *
64 * It does so using either internally added scrollbars or externally
65 * associated adjustments, and optionally draws a frame around the child.
66 *
67 * Widgets with native scrolling support, i.e. those whose classes implement
68 * the [iface@Gtk.Scrollable] interface, are added directly. For other types
69 * of widget, the class [class@Gtk.Viewport] acts as an adaptor, giving
70 * scrollability to other widgets. [method@Gtk.ScrolledWindow.set_child]
71 * intelligently accounts for whether or not the added child is a `GtkScrollable`.
72 * If it isn’t, then it wraps the child in a `GtkViewport`. Therefore, you can
73 * just add any child widget and not worry about the details.
74 *
75 * If [method@Gtk.ScrolledWindow.set_child] has added a `GtkViewport` for you,
76 * you can remove both your added child widget from the `GtkViewport`, and the
77 * `GtkViewport` from the `GtkScrolledWindow`, like this:
78 *
79 * ```c
80 * GtkWidget *scrolled_window = gtk_scrolled_window_new ();
81 * GtkWidget *child_widget = gtk_button_new ();
82 *
83 * // GtkButton is not a GtkScrollable, so GtkScrolledWindow will automatically
84 * // add a GtkViewport.
85 * gtk_box_append (GTK_BOX (scrolled_window), child_widget);
86 *
87 * // Either of these will result in child_widget being unparented:
88 * gtk_box_remove (GTK_BOX (scrolled_window), child_widget);
89 * // or
90 * gtk_box_remove (GTK_BOX (scrolled_window),
91 * gtk_bin_get_child (GTK_BIN (scrolled_window)));
92 * ```
93 *
94 * Unless [property@Gtk.ScrolledWindow:hscrollbar-policy] and
95 * [property@Gtk.ScrolledWindow:vscrollbar-policy] are %GTK_POLICY_NEVER or
96 * %GTK_POLICY_EXTERNAL, `GtkScrolledWindow` adds internal `GtkScrollbar` widgets
97 * around its child. The scroll position of the child, and if applicable the
98 * scrollbars, is controlled by the [property@Gtk.ScrolledWindow:hadjustment]
99 * and [property@Gtk.ScrolledWindow:vadjustment] that are associated with the
100 * `GtkScrolledWindow`. See the docs on [class@Gtk.Scrollbar] for the details,
101 * but note that the “step_increment” and “page_increment” fields are only
102 * effective if the policy causes scrollbars to be present.
103 *
104 * If a `GtkScrolledWindow` doesn’t behave quite as you would like, or
105 * doesn’t have exactly the right layout, it’s very possible to set up
106 * your own scrolling with `GtkScrollbar` and for example a `GtkGrid`.
107 *
108 * # Touch support
109 *
110 * `GtkScrolledWindow` has built-in support for touch devices. When a
111 * touchscreen is used, swiping will move the scrolled window, and will
112 * expose 'kinetic' behavior. This can be turned off with the
113 * [property@Gtk.ScrolledWindow:kinetic-scrolling] property if it is undesired.
114 *
115 * `GtkScrolledWindow` also displays visual 'overshoot' indication when
116 * the content is pulled beyond the end, and this situation can be
117 * captured with the [signal@Gtk.ScrolledWindow::edge-overshot] signal.
118 *
119 * If no mouse device is present, the scrollbars will overlaid as
120 * narrow, auto-hiding indicators over the content. If traditional
121 * scrollbars are desired although no mouse is present, this behaviour
122 * can be turned off with the [property@Gtk.ScrolledWindow:overlay-scrolling]
123 * property.
124 *
125 * # CSS nodes
126 *
127 * `GtkScrolledWindow` has a main CSS node with name scrolledwindow.
128 * It gets a .frame style class added when [property@Gtk.ScrolledWindow:has-frame]
129 * is %TRUE.
130 *
131 * It uses subnodes with names overshoot and undershoot to draw the overflow
132 * and underflow indications. These nodes get the .left, .right, .top or .bottom
133 * style class added depending on where the indication is drawn.
134 *
135 * `GtkScrolledWindow` also sets the positional style classes (.left, .right,
136 * .top, .bottom) and style classes related to overlay scrolling
137 * (.overlay-indicator, .dragging, .hovering) on its scrollbars.
138 *
139 * If both scrollbars are visible, the area where they meet is drawn
140 * with a subnode named junction.
141 *
142 * # Accessibility
143 *
144 * `GtkScrolledWindow` uses the %GTK_ACCESSIBLE_ROLE_GROUP role.
145 */
146
147
148 /* scrolled window policy and size requisition handling:
149 *
150 * gtk size requisition works as follows:
151 * a widget upon size-request reports the width and height that it finds
152 * to be best suited to display its contents, including children.
153 * the width and/or height reported from a widget upon size requisition
154 * may be overridden by the user by specifying a width and/or height
155 * other than 0 through gtk_widget_set_size_request().
156 *
157 * a scrolled window needs (for implementing all three policy types) to
158 * request its width and height based on two different rationales.
159 * 1) the user wants the scrolled window to just fit into the space
160 * that it gets allocated for a specific dimension.
161 * 1.1) this does not apply if the user specified a concrete value
162 * value for that specific dimension by either specifying usize for the
163 * scrolled window or for its child.
164 * 2) the user wants the scrolled window to take as much space up as
165 * is desired by the child for a specific dimension (i.e. POLICY_NEVER).
166 *
167 * also, kinda obvious:
168 * 3) a user would certainly not have chosen a scrolled window as a container
169 * for the child, if the resulting allocation takes up more space than the
170 * child would have allocated without the scrolled window.
171 *
172 * conclusions:
173 * A) from 1) follows: the scrolled window shouldn’t request more space for a
174 * specific dimension than is required at minimum.
175 * B) from 1.1) follows: the requisition may be overridden by usize of the scrolled
176 * window (done automatically) or by usize of the child (needs to be checked).
177 * C) from 2) follows: for POLICY_NEVER, the scrolled window simply reports the
178 * child’s dimension.
179 * D) from 3) follows: the scrolled window child’s minimum width and minimum height
180 * under A) at least correspond to the space taken up by its scrollbars.
181 */
182
183 /* Kinetic scrolling */
184 #define MAX_OVERSHOOT_DISTANCE 100
185 #define DECELERATION_FRICTION 4
186 #define OVERSHOOT_FRICTION 20
187 #define VELOCITY_ACCUMULATION_FLOOR 0.33
188 #define VELOCITY_ACCUMULATION_CEIL 1.0
189 #define VELOCITY_ACCUMULATION_MAX 6.0
190
191 /* Animated scrolling */
192 #define ANIMATION_DURATION 200
193
194 /* Overlay scrollbars */
195 #define INDICATOR_FADE_OUT_DELAY 2000
196 #define INDICATOR_FADE_OUT_DURATION 1000
197 #define INDICATOR_FADE_OUT_TIME 500
198 #define INDICATOR_CLOSE_DISTANCE 5
199 #define INDICATOR_FAR_DISTANCE 10
200
201 /* Scrolled off indication */
202 #define UNDERSHOOT_SIZE 40
203
204 typedef struct _GtkScrolledWindowClass GtkScrolledWindowClass;
205
206 struct _GtkScrolledWindow
207 {
208 GtkWidget parent_instance;
209 };
210
211 struct _GtkScrolledWindowClass
212 {
213 GtkWidgetClass parent_class;
214
215 /* Unfortunately, GtkScrollType is deficient in that there is
216 * no horizontal/vertical variants for GTK_SCROLL_START/END,
217 * so we have to add an additional boolean flag.
218 */
219 gboolean (*scroll_child) (GtkScrolledWindow *scrolled_window,
220 GtkScrollType scroll,
221 gboolean horizontal);
222
223 void (* move_focus_out) (GtkScrolledWindow *scrolled_window,
224 GtkDirectionType direction);
225 };
226
227 typedef struct
228 {
229 GtkWidget *scrollbar;
230 gboolean over; /* either mouse over, or while dragging */
231 gint64 last_scroll_time;
232 guint conceil_timer;
233
234 double current_pos;
235 double source_pos;
236 double target_pos;
237 GtkProgressTracker tracker;
238 guint tick_id;
239 guint over_timeout_id;
240 } Indicator;
241
242 typedef struct
243 {
244 GtkWidget *child;
245
246 GtkWidget *hscrollbar;
247 GtkWidget *vscrollbar;
248
249 GtkCssNode *overshoot_node[4];
250 GtkCssNode *undershoot_node[4];
251 GtkCssNode *junction_node;
252
253 Indicator hindicator;
254 Indicator vindicator;
255
256 GtkCornerType window_placement;
257 guint has_frame : 1;
258 guint hscrollbar_policy : 2;
259 guint vscrollbar_policy : 2;
260 guint hscrollbar_visible : 1;
261 guint vscrollbar_visible : 1;
262 guint focus_out : 1; /* used by ::move-focus-out implementation */
263 guint overlay_scrolling : 1;
264 guint use_indicators : 1;
265 guint auto_added_viewport : 1;
266 guint propagate_natural_width : 1;
267 guint propagate_natural_height : 1;
268 guint smooth_scroll : 1;
269
270 int min_content_width;
271 int min_content_height;
272 int max_content_width;
273 int max_content_height;
274
275 guint scroll_events_overshoot_id;
276
277 /* Kinetic scrolling */
278 GtkGesture *long_press_gesture;
279 GtkGesture *swipe_gesture;
280 GtkKineticScrolling *hscrolling;
281 GtkKineticScrolling *vscrolling;
282 gint64 last_deceleration_time;
283
284 /* These two gestures are mutually exclusive */
285 GtkGesture *drag_gesture;
286 GtkGesture *pan_gesture;
287
288 double drag_start_x;
289 double drag_start_y;
290
291 guint kinetic_scrolling : 1;
292 guint in_drag : 1;
293
294 guint deceleration_id;
295
296 double x_velocity;
297 double y_velocity;
298
299 double unclamped_hadj_value;
300 double unclamped_vadj_value;
301 } GtkScrolledWindowPrivate;
302
303 enum {
304 PROP_0,
305 PROP_HADJUSTMENT,
306 PROP_VADJUSTMENT,
307 PROP_HSCROLLBAR_POLICY,
308 PROP_VSCROLLBAR_POLICY,
309 PROP_WINDOW_PLACEMENT,
310 PROP_HAS_FRAME,
311 PROP_MIN_CONTENT_WIDTH,
312 PROP_MIN_CONTENT_HEIGHT,
313 PROP_KINETIC_SCROLLING,
314 PROP_OVERLAY_SCROLLING,
315 PROP_MAX_CONTENT_WIDTH,
316 PROP_MAX_CONTENT_HEIGHT,
317 PROP_PROPAGATE_NATURAL_WIDTH,
318 PROP_PROPAGATE_NATURAL_HEIGHT,
319 PROP_CHILD,
320 NUM_PROPERTIES
321 };
322
323 /* Signals */
324 enum
325 {
326 SCROLL_CHILD,
327 MOVE_FOCUS_OUT,
328 EDGE_OVERSHOT,
329 EDGE_REACHED,
330 LAST_SIGNAL
331 };
332
333 static void gtk_scrolled_window_set_property (GObject *object,
334 guint prop_id,
335 const GValue *value,
336 GParamSpec *pspec);
337 static void gtk_scrolled_window_get_property (GObject *object,
338 guint prop_id,
339 GValue *value,
340 GParamSpec *pspec);
341 static void gtk_scrolled_window_dispose (GObject *object);
342
343 static void gtk_scrolled_window_snapshot (GtkWidget *widget,
344 GtkSnapshot *snapshot);
345 static void gtk_scrolled_window_size_allocate (GtkWidget *widget,
346 int width,
347 int height,
348 int baseline);
349 static gboolean gtk_scrolled_window_focus (GtkWidget *widget,
350 GtkDirectionType direction);
351 static gboolean gtk_scrolled_window_scroll_child (GtkScrolledWindow *scrolled_window,
352 GtkScrollType scroll,
353 gboolean horizontal);
354 static void gtk_scrolled_window_move_focus_out (GtkScrolledWindow *scrolled_window,
355 GtkDirectionType direction_type);
356
357 static void gtk_scrolled_window_relative_allocation(GtkScrolledWindow *scrolled_window,
358 GtkAllocation *allocation);
359 static void gtk_scrolled_window_inner_allocation (GtkScrolledWindow *scrolled_window,
360 GtkAllocation *rect);
361 static void gtk_scrolled_window_allocate_scrollbar (GtkScrolledWindow *scrolled_window,
362 GtkWidget *scrollbar,
363 GtkAllocation *allocation);
364 static void gtk_scrolled_window_allocate_child (GtkScrolledWindow *swindow,
365 int width,
366 int height);
367 static void gtk_scrolled_window_adjustment_changed (GtkAdjustment *adjustment,
368 gpointer data);
369 static void gtk_scrolled_window_adjustment_value_changed (GtkAdjustment *adjustment,
370 gpointer data);
371 static gboolean gtk_widget_should_animate (GtkWidget *widget);
372 static void gtk_scrolled_window_measure (GtkWidget *widget,
373 GtkOrientation orientation,
374 int for_size,
375 int *minimum_size,
376 int *natural_size,
377 int *minimum_baseline,
378 int *natural_baseline);
379 static void gtk_scrolled_window_map (GtkWidget *widget);
380 static void gtk_scrolled_window_unmap (GtkWidget *widget);
381 static void gtk_scrolled_window_realize (GtkWidget *widget);
382 static void _gtk_scrolled_window_set_adjustment_value (GtkScrolledWindow *scrolled_window,
383 GtkAdjustment *adjustment,
384 double value);
385
386 static void gtk_scrolled_window_cancel_deceleration (GtkScrolledWindow *scrolled_window);
387
388 static gboolean _gtk_scrolled_window_get_overshoot (GtkScrolledWindow *scrolled_window,
389 int *overshoot_x,
390 int *overshoot_y);
391
392 static void gtk_scrolled_window_start_deceleration (GtkScrolledWindow *scrolled_window);
393
394 static void gtk_scrolled_window_update_use_indicators (GtkScrolledWindow *scrolled_window);
395 static void remove_indicator (GtkScrolledWindow *sw,
396 Indicator *indicator);
397 static gboolean maybe_hide_indicator (gpointer data);
398
399 static void indicator_start_fade (Indicator *indicator,
400 double pos);
401 static void indicator_set_over (Indicator *indicator,
402 gboolean over);
403
404 static void scrolled_window_scroll (GtkScrolledWindow *scrolled_window,
405 double delta_x,
406 double delta_y,
407 GtkEventControllerScroll *scroll);
408
409 static guint signals[LAST_SIGNAL] = {0};
410 static GParamSpec *properties[NUM_PROPERTIES];
411
412 static void gtk_scrolled_window_buildable_init (GtkBuildableIface *iface);
413
414 G_DEFINE_TYPE_WITH_CODE (GtkScrolledWindow, gtk_scrolled_window, GTK_TYPE_WIDGET,
415 G_ADD_PRIVATE (GtkScrolledWindow)
416 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
417 gtk_scrolled_window_buildable_init))
418
419 static GtkBuildableIface *parent_buildable_iface;
420
421 static void
gtk_scrolled_window_buildable_add_child(GtkBuildable * buildable,GtkBuilder * builder,GObject * child,const char * type)422 gtk_scrolled_window_buildable_add_child (GtkBuildable *buildable,
423 GtkBuilder *builder,
424 GObject *child,
425 const char *type)
426 {
427 if (GTK_IS_WIDGET (child))
428 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(buildable), GTK_WIDGET (child));
429 else
430 parent_buildable_iface->add_child (buildable, builder, child, type);
431 }
432
433 static void
gtk_scrolled_window_buildable_init(GtkBuildableIface * iface)434 gtk_scrolled_window_buildable_init (GtkBuildableIface *iface)
435 {
436 parent_buildable_iface = g_type_interface_peek_parent (iface);
437
438 iface->add_child = gtk_scrolled_window_buildable_add_child;
439 }
440
441 static void
add_scroll_binding(GtkWidgetClass * widget_class,guint keyval,GdkModifierType mask,GtkScrollType scroll,gboolean horizontal)442 add_scroll_binding (GtkWidgetClass *widget_class,
443 guint keyval,
444 GdkModifierType mask,
445 GtkScrollType scroll,
446 gboolean horizontal)
447 {
448 guint keypad_keyval = keyval - GDK_KEY_Left + GDK_KEY_KP_Left;
449
450 gtk_widget_class_add_binding_signal (widget_class,
451 keyval, mask,
452 "scroll-child",
453 "(ib)", scroll, horizontal);
454 gtk_widget_class_add_binding_signal (widget_class,
455 keypad_keyval, mask,
456 "scroll-child",
457 "(ib)", scroll, horizontal);
458 }
459
460 static void
add_tab_bindings(GtkWidgetClass * widget_class,GdkModifierType modifiers,GtkDirectionType direction)461 add_tab_bindings (GtkWidgetClass *widget_class,
462 GdkModifierType modifiers,
463 GtkDirectionType direction)
464 {
465 gtk_widget_class_add_binding_signal (widget_class,
466 GDK_KEY_Tab, modifiers,
467 "move-focus-out",
468 "(i)", direction);
469 gtk_widget_class_add_binding_signal (widget_class,
470 GDK_KEY_KP_Tab, modifiers,
471 "move-focus-out",
472 "(i)", direction);
473 }
474
475 static void
motion_controller_leave(GtkEventController * controller,GtkScrolledWindow * scrolled_window)476 motion_controller_leave (GtkEventController *controller,
477 GtkScrolledWindow *scrolled_window)
478 {
479 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
480
481 if (priv->use_indicators)
482 {
483 indicator_set_over (&priv->hindicator, FALSE);
484 indicator_set_over (&priv->vindicator, FALSE);
485 }
486 }
487
488 static void
update_scrollbar_positions(GtkScrolledWindow * scrolled_window)489 update_scrollbar_positions (GtkScrolledWindow *scrolled_window)
490 {
491 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
492 gboolean is_rtl;
493
494 if (priv->hscrollbar != NULL)
495 {
496 if (priv->window_placement == GTK_CORNER_TOP_LEFT ||
497 priv->window_placement == GTK_CORNER_TOP_RIGHT)
498 {
499 gtk_widget_add_css_class (priv->hscrollbar, "bottom");
500 gtk_widget_remove_css_class (priv->hscrollbar, "top");
501 }
502 else
503 {
504 gtk_widget_add_css_class (priv->hscrollbar, "top");
505 gtk_widget_remove_css_class (priv->hscrollbar, "bottom");
506 }
507 }
508
509 if (priv->vscrollbar != NULL)
510 {
511 is_rtl = _gtk_widget_get_direction (GTK_WIDGET (scrolled_window)) == GTK_TEXT_DIR_RTL;
512 if ((is_rtl &&
513 (priv->window_placement == GTK_CORNER_TOP_RIGHT ||
514 priv->window_placement == GTK_CORNER_BOTTOM_RIGHT)) ||
515 (!is_rtl &&
516 (priv->window_placement == GTK_CORNER_TOP_LEFT ||
517 priv->window_placement == GTK_CORNER_BOTTOM_LEFT)))
518 {
519 gtk_widget_add_css_class (priv->vscrollbar, "right");
520 gtk_widget_remove_css_class (priv->vscrollbar, "left");
521 }
522 else
523 {
524 gtk_widget_add_css_class (priv->vscrollbar, "left");
525 gtk_widget_remove_css_class (priv->vscrollbar, "right");
526 }
527 }
528 }
529
530 static void
gtk_scrolled_window_direction_changed(GtkWidget * widget,GtkTextDirection previous_dir)531 gtk_scrolled_window_direction_changed (GtkWidget *widget,
532 GtkTextDirection previous_dir)
533 {
534 GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
535
536 update_scrollbar_positions (scrolled_window);
537
538 GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->direction_changed (widget, previous_dir);
539 }
540
541 static void
gtk_scrolled_window_compute_expand(GtkWidget * widget,gboolean * hexpand,gboolean * vexpand)542 gtk_scrolled_window_compute_expand (GtkWidget *widget,
543 gboolean *hexpand,
544 gboolean *vexpand)
545 {
546 GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
547 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
548
549 if (priv->child)
550 {
551 *hexpand = gtk_widget_compute_expand (priv->child, GTK_ORIENTATION_HORIZONTAL);
552 *vexpand = gtk_widget_compute_expand (priv->child, GTK_ORIENTATION_VERTICAL);
553 }
554 else
555 {
556 *hexpand = FALSE;
557 *vexpand = FALSE;
558 }
559 }
560
561 static GtkSizeRequestMode
gtk_scrolled_window_get_request_mode(GtkWidget * widget)562 gtk_scrolled_window_get_request_mode (GtkWidget *widget)
563 {
564 GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
565 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
566
567 if (priv->child)
568 return gtk_widget_get_request_mode (priv->child);
569 else
570 return GTK_SIZE_REQUEST_CONSTANT_SIZE;
571 }
572
573 static void
gtk_scrolled_window_class_init(GtkScrolledWindowClass * class)574 gtk_scrolled_window_class_init (GtkScrolledWindowClass *class)
575 {
576 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
577 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
578
579 gobject_class->set_property = gtk_scrolled_window_set_property;
580 gobject_class->get_property = gtk_scrolled_window_get_property;
581 gobject_class->dispose = gtk_scrolled_window_dispose;
582
583 widget_class->snapshot = gtk_scrolled_window_snapshot;
584 widget_class->size_allocate = gtk_scrolled_window_size_allocate;
585 widget_class->measure = gtk_scrolled_window_measure;
586 widget_class->focus = gtk_scrolled_window_focus;
587 widget_class->map = gtk_scrolled_window_map;
588 widget_class->unmap = gtk_scrolled_window_unmap;
589 widget_class->realize = gtk_scrolled_window_realize;
590 widget_class->direction_changed = gtk_scrolled_window_direction_changed;
591 widget_class->compute_expand = gtk_scrolled_window_compute_expand;
592 widget_class->get_request_mode = gtk_scrolled_window_get_request_mode;
593
594 class->scroll_child = gtk_scrolled_window_scroll_child;
595 class->move_focus_out = gtk_scrolled_window_move_focus_out;
596
597 /**
598 * GtkScrolleWindow:hadjustment: (attributes org.gtk.Property.get=gtk_scrolled_window_get_hadjustment org.gtk.Property.set=gtk_scrolled_window_set_hadjustment)
599 *
600 * The `GtkAdjustment` for the horizontal position.
601 */
602 properties[PROP_HADJUSTMENT] =
603 g_param_spec_object ("hadjustment",
604 P_("Horizontal Adjustment"),
605 P_("The GtkAdjustment for the horizontal position"),
606 GTK_TYPE_ADJUSTMENT,
607 GTK_PARAM_READWRITE|G_PARAM_CONSTRUCT);
608
609 /**
610 * GtkScrolleWindow:vadjustment: (attributes org.gtk.Property.get=gtk_scrolled_window_get_vadjustment org.gtk.Property.set=gtk_scrolled_window_set_vadjustment)
611 *
612 * The `GtkAdjustment` for the vertical position.
613 */
614 properties[PROP_VADJUSTMENT] =
615 g_param_spec_object ("vadjustment",
616 P_("Vertical Adjustment"),
617 P_("The GtkAdjustment for the vertical position"),
618 GTK_TYPE_ADJUSTMENT,
619 GTK_PARAM_READWRITE|G_PARAM_CONSTRUCT);
620
621 /**
622 * GtkScrolledWindow:hscrollbar-policy:
623 *
624 * When the horizontal scrollbar is displayed.
625 *
626 * Use [method@Gtk.ScrolledWindow.set_policy] to set
627 * this property.
628 */
629 properties[PROP_HSCROLLBAR_POLICY] =
630 g_param_spec_enum ("hscrollbar-policy",
631 P_("Horizontal Scrollbar Policy"),
632 P_("When the horizontal scrollbar is displayed"),
633 GTK_TYPE_POLICY_TYPE,
634 GTK_POLICY_AUTOMATIC,
635 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
636
637 /**
638 * GtkScrolledWindow:vscrollbar-policy:
639 *
640 * When the vertical scrollbar is displayed.
641 *
642 * Use [method@Gtk.ScrolledWindow.set_policy] to set
643 * this property.
644 */
645 properties[PROP_VSCROLLBAR_POLICY] =
646 g_param_spec_enum ("vscrollbar-policy",
647 P_("Vertical Scrollbar Policy"),
648 P_("When the vertical scrollbar is displayed"),
649 GTK_TYPE_POLICY_TYPE,
650 GTK_POLICY_AUTOMATIC,
651 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
652
653 /**
654 * GtkScrolledWindow:window-placement: (attributes org.gtk.Property.get=gtk_scrolled_window_get_placement org.gtk.Property.set=gtk_scrolled_window_set_placement)
655 *
656 * Where the contents are located with respect to the scrollbars.
657 */
658 properties[PROP_WINDOW_PLACEMENT] =
659 g_param_spec_enum ("window-placement",
660 P_("Window Placement"),
661 P_("Where the contents are located with respect to the scrollbars."),
662 GTK_TYPE_CORNER_TYPE,
663 GTK_CORNER_TOP_LEFT,
664 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
665
666 /**
667 * GtkScrolledWindow:has-frame: (attributes org.gtk.Property.get=gtk_scrolled_window_get_has_frame org.gtk.Property.set=gtk_scrolled_window_set_has_frame)
668 *
669 * Whether to draw a frame around the contents.
670 */
671 properties[PROP_HAS_FRAME] =
672 g_param_spec_boolean ("has-frame",
673 P_("Has Frame"),
674 P_("Whether to draw a frame around the contents"),
675 FALSE,
676 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
677
678 /**
679 * GtkScrolledWindow:min-content-width: (attributes org.gtk.Property.get=gtk_scrolled_window_get_min_content_width org.gtk.Property.set=gtk_scrolled_window_set_min_content_width)
680 *
681 * The minimum content width of @scrolled_window.
682 */
683 properties[PROP_MIN_CONTENT_WIDTH] =
684 g_param_spec_int ("min-content-width",
685 P_("Minimum Content Width"),
686 P_("The minimum width that the scrolled window will allocate to its content"),
687 -1, G_MAXINT, -1,
688 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
689
690 /**
691 * GtkScrolledWindow:min-content-height: (attributes org.gtk.Property.get=gtk_scrolled_window_get_min_content_height org.gtk.Property.set=gtk_scrolled_window_set_min_content_height)
692 *
693 * The minimum content height of @scrolled_window.
694 */
695 properties[PROP_MIN_CONTENT_HEIGHT] =
696 g_param_spec_int ("min-content-height",
697 P_("Minimum Content Height"),
698 P_("The minimum height that the scrolled window will allocate to its content"),
699 -1, G_MAXINT, -1,
700 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
701
702 /**
703 * GtkScrolledWindow:kinetic-scrolling: (attributes org.gtk.Property.get=gtk_scrolled_window_get_kinetic_scrolling org.gtk.Property.set=gtk_scrolled_window_set_overlay_scrolling)
704 *
705 * Whether kinetic scrolling is enabled or not.
706 *
707 * Kinetic scrolling only applies to devices with source %GDK_SOURCE_TOUCHSCREEN.
708 */
709 properties[PROP_KINETIC_SCROLLING] =
710 g_param_spec_boolean ("kinetic-scrolling",
711 P_("Kinetic Scrolling"),
712 P_("Kinetic scrolling mode."),
713 TRUE,
714 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
715
716 /**
717 * GtkScrolledWindow:overlay-scrolling: (attributes org.gtk.Property.get=gtk_scrolled_window_get_overlay_scrolling org.gtk.Property.set=gtk_scrolled_window_set_overlay_scrolling)
718 *
719 * Whether overlay scrolling is enabled or not.
720 *
721 * If it is, the scrollbars are only added as traditional widgets
722 * when a mouse is present. Otherwise, they are overlaid on top of
723 * the content, as narrow indicators.
724 *
725 * Note that overlay scrolling can also be globally disabled, with
726 * the [property@Gtk.Settings:gtk-overlay-scrolling] setting.
727 */
728 properties[PROP_OVERLAY_SCROLLING] =
729 g_param_spec_boolean ("overlay-scrolling",
730 P_("Overlay Scrolling"),
731 P_("Overlay scrolling mode"),
732 TRUE,
733 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
734
735 /**
736 * GtkScrolledWindow:max-content-width: (attributes org.gtk.Property.get=gtk_scrolled_window_get_max_content_width org.gtk.Property.set=gtk_scrolled_window_set_max_content_width)
737 *
738 * The maximum content width of @scrolled_window.
739 */
740 properties[PROP_MAX_CONTENT_WIDTH] =
741 g_param_spec_int ("max-content-width",
742 P_("Maximum Content Width"),
743 P_("The maximum width that the scrolled window will allocate to its content"),
744 -1, G_MAXINT, -1,
745 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
746
747 /**
748 * GtkScrolledWindow:max-content-height: (attributes org.gtk.Property.get=gtk_scrolled_window_get_max_content_height org.gtk.Property.set=gtk_scrolled_window_set_max_content_height)
749 *
750 * The maximum content height of @scrolled_window.
751 */
752 properties[PROP_MAX_CONTENT_HEIGHT] =
753 g_param_spec_int ("max-content-height",
754 P_("Maximum Content Height"),
755 P_("The maximum height that the scrolled window will allocate to its content"),
756 -1, G_MAXINT, -1,
757 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
758
759 /**
760 * GtkScrolledWindow:propagate-natural-width: (attributes org.gtk.Property.get=gtk_scrolled_window_get_propagate_natural_width org.gtk.Property.set=gtk_scrolled_window_set_propagate_natural_width)
761 *
762 * Whether the natural width of the child should be calculated and propagated
763 * through the scrolled window’s requested natural width.
764 *
765 * This is useful in cases where an attempt should be made to allocate exactly
766 * enough space for the natural size of the child.
767 */
768 properties[PROP_PROPAGATE_NATURAL_WIDTH] =
769 g_param_spec_boolean ("propagate-natural-width",
770 P_("Propagate Natural Width"),
771 P_("Propagate Natural Width"),
772 FALSE,
773 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
774
775 /**
776 * GtkScrolledWindow:propagate-natural-height: (attributes org.gtk.Property.get=gtk_scrolled_window_get_propagate_natural_height org.gtk.Property.set=gtk_scrolled_window_set_propagate_natural_height)
777 *
778 * Whether the natural height of the child should be calculated and propagated
779 * through the scrolled window’s requested natural height.
780 *
781 * This is useful in cases where an attempt should be made to allocate exactly
782 * enough space for the natural size of the child.
783 */
784 properties[PROP_PROPAGATE_NATURAL_HEIGHT] =
785 g_param_spec_boolean ("propagate-natural-height",
786 P_("Propagate Natural Height"),
787 P_("Propagate Natural Height"),
788 FALSE,
789 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
790
791 /**
792 * GtkScrolledWindow:child: (attributes org.gtk.Property.get=gtk_scrolled_window_get_child org.gtk.Property.set=gtk_scrolled_window_set_child)
793 *
794 * The child widget.
795 */
796 properties[PROP_CHILD] =
797 g_param_spec_object ("child",
798 P_("Child"),
799 P_("The child widget"),
800 GTK_TYPE_WIDGET,
801 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
802
803 g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
804
805 /**
806 * GtkScrolledWindow::scroll-child:
807 * @scrolled_window: a `GtkScrolledWindow`
808 * @scroll: a `GtkScrollType` describing how much to scroll
809 * @horizontal: whether the keybinding scrolls the child
810 * horizontally or not
811 *
812 * Emitted when a keybinding that scrolls is pressed.
813 *
814 * This is a [keybinding signal](class.SignalAction.html).
815 *
816 * The horizontal or vertical adjustment is updated which triggers a
817 * signal that the scrolled window’s child may listen to and scroll itself.
818 */
819 signals[SCROLL_CHILD] =
820 g_signal_new (I_("scroll-child"),
821 G_TYPE_FROM_CLASS (gobject_class),
822 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
823 G_STRUCT_OFFSET (GtkScrolledWindowClass, scroll_child),
824 NULL, NULL,
825 _gtk_marshal_BOOLEAN__ENUM_BOOLEAN,
826 G_TYPE_BOOLEAN, 2,
827 GTK_TYPE_SCROLL_TYPE,
828 G_TYPE_BOOLEAN);
829
830 /**
831 * GtkScrolledWindow::move-focus-out:
832 * @scrolled_window: a `GtkScrolledWindow`
833 * @direction_type: either %GTK_DIR_TAB_FORWARD or
834 * %GTK_DIR_TAB_BACKWARD
835 *
836 * Emitted when focus is moved away from the scrolled window by a
837 * keybinding.
838 *
839 * This is a [keybinding signal](class.SignalAction.html).
840 *
841 * The default bindings for this signal are
842 * `Ctrl + Tab` to move forward and `Ctrl + Shift + Tab` to
843 * move backward.
844 */
845 signals[MOVE_FOCUS_OUT] =
846 g_signal_new (I_("move-focus-out"),
847 G_TYPE_FROM_CLASS (gobject_class),
848 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
849 G_STRUCT_OFFSET (GtkScrolledWindowClass, move_focus_out),
850 NULL, NULL,
851 NULL,
852 G_TYPE_NONE, 1,
853 GTK_TYPE_DIRECTION_TYPE);
854
855 /**
856 * GtkScrolledWindow::edge-overshot:
857 * @scrolled_window: a `GtkScrolledWindow`
858 * @pos: edge side that was hit
859 *
860 * Emitted whenever user initiated scrolling makes the scrolled
861 * window firmly surpass the limits defined by the adjustment
862 * in that orientation.
863 *
864 * A similar behavior without edge resistance is provided by the
865 * [signal@Gtk.ScrolledWindow::edge-reached] signal.
866 *
867 * Note: The @pos argument is LTR/RTL aware, so callers should be
868 * aware too if intending to provide behavior on horizontal edges.
869 */
870 signals[EDGE_OVERSHOT] =
871 g_signal_new (I_("edge-overshot"),
872 G_TYPE_FROM_CLASS (gobject_class),
873 G_SIGNAL_RUN_LAST, 0,
874 NULL, NULL, NULL,
875 G_TYPE_NONE, 1, GTK_TYPE_POSITION_TYPE);
876
877 /**
878 * GtkScrolledWindow::edge-reached:
879 * @scrolled_window: a `GtkScrolledWindow`
880 * @pos: edge side that was reached
881 *
882 * Emitted whenever user-initiated scrolling makes the scrolled
883 * window exactly reach the lower or upper limits defined by the
884 * adjustment in that orientation.
885 *
886 * A similar behavior with edge resistance is provided by the
887 * [signal@Gtk.ScrolledWindow::edge-overshot] signal.
888 *
889 * Note: The @pos argument is LTR/RTL aware, so callers should be
890 * aware too if intending to provide behavior on horizontal edges.
891 */
892 signals[EDGE_REACHED] =
893 g_signal_new (I_("edge-reached"),
894 G_TYPE_FROM_CLASS (gobject_class),
895 G_SIGNAL_RUN_LAST, 0,
896 NULL, NULL, NULL,
897 G_TYPE_NONE, 1, GTK_TYPE_POSITION_TYPE);
898
899 add_scroll_binding (widget_class, GDK_KEY_Left, GDK_CONTROL_MASK, GTK_SCROLL_STEP_BACKWARD, TRUE);
900 add_scroll_binding (widget_class, GDK_KEY_Right, GDK_CONTROL_MASK, GTK_SCROLL_STEP_FORWARD, TRUE);
901 add_scroll_binding (widget_class, GDK_KEY_Up, GDK_CONTROL_MASK, GTK_SCROLL_STEP_BACKWARD, FALSE);
902 add_scroll_binding (widget_class, GDK_KEY_Down, GDK_CONTROL_MASK, GTK_SCROLL_STEP_FORWARD, FALSE);
903
904 add_scroll_binding (widget_class, GDK_KEY_Page_Up, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_BACKWARD, TRUE);
905 add_scroll_binding (widget_class, GDK_KEY_Page_Down, GDK_CONTROL_MASK, GTK_SCROLL_PAGE_FORWARD, TRUE);
906 add_scroll_binding (widget_class, GDK_KEY_Page_Up, 0, GTK_SCROLL_PAGE_BACKWARD, FALSE);
907 add_scroll_binding (widget_class, GDK_KEY_Page_Down, 0, GTK_SCROLL_PAGE_FORWARD, FALSE);
908
909 add_scroll_binding (widget_class, GDK_KEY_Home, GDK_CONTROL_MASK, GTK_SCROLL_START, TRUE);
910 add_scroll_binding (widget_class, GDK_KEY_End, GDK_CONTROL_MASK, GTK_SCROLL_END, TRUE);
911 add_scroll_binding (widget_class, GDK_KEY_Home, 0, GTK_SCROLL_START, FALSE);
912 add_scroll_binding (widget_class, GDK_KEY_End, 0, GTK_SCROLL_END, FALSE);
913
914 add_tab_bindings (widget_class, GDK_CONTROL_MASK, GTK_DIR_TAB_FORWARD);
915 add_tab_bindings (widget_class, GDK_CONTROL_MASK | GDK_SHIFT_MASK, GTK_DIR_TAB_BACKWARD);
916
917 gtk_widget_class_set_css_name (widget_class, I_("scrolledwindow"));
918 gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_GROUP);
919 }
920
921 static gboolean
may_hscroll(GtkScrolledWindow * scrolled_window)922 may_hscroll (GtkScrolledWindow *scrolled_window)
923 {
924 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
925
926 return priv->hscrollbar_visible || priv->hscrollbar_policy == GTK_POLICY_EXTERNAL;
927 }
928
929 static gboolean
may_vscroll(GtkScrolledWindow * scrolled_window)930 may_vscroll (GtkScrolledWindow *scrolled_window)
931 {
932 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
933
934 return priv->vscrollbar_visible || priv->vscrollbar_policy == GTK_POLICY_EXTERNAL;
935 }
936
937 static inline gboolean
policy_may_be_visible(GtkPolicyType policy)938 policy_may_be_visible (GtkPolicyType policy)
939 {
940 return policy == GTK_POLICY_ALWAYS || policy == GTK_POLICY_AUTOMATIC;
941 }
942
943 static void
scrolled_window_drag_begin_cb(GtkScrolledWindow * scrolled_window,double start_x,double start_y,GtkGesture * gesture)944 scrolled_window_drag_begin_cb (GtkScrolledWindow *scrolled_window,
945 double start_x,
946 double start_y,
947 GtkGesture *gesture)
948 {
949 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
950 GdkEventSequence *sequence;
951 GtkWidget *event_widget;
952
953 priv->in_drag = FALSE;
954 priv->drag_start_x = priv->unclamped_hadj_value;
955 priv->drag_start_y = priv->unclamped_vadj_value;
956 gtk_scrolled_window_cancel_deceleration (scrolled_window);
957 sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
958 event_widget = gtk_gesture_get_last_target (gesture, sequence);
959
960 if (event_widget == priv->vscrollbar || event_widget == priv->hscrollbar ||
961 (!may_hscroll (scrolled_window) && !may_vscroll (scrolled_window)))
962 gtk_gesture_set_sequence_state (gesture, sequence, GTK_EVENT_SEQUENCE_DENIED);
963 }
964
965 static void
gtk_scrolled_window_invalidate_overshoot(GtkScrolledWindow * scrolled_window)966 gtk_scrolled_window_invalidate_overshoot (GtkScrolledWindow *scrolled_window)
967 {
968 GtkAllocation child_allocation;
969 int overshoot_x, overshoot_y;
970
971 if (!_gtk_scrolled_window_get_overshoot (scrolled_window, &overshoot_x, &overshoot_y))
972 return;
973
974 gtk_scrolled_window_relative_allocation (scrolled_window,
975 &child_allocation);
976 if (overshoot_x != 0)
977 gtk_widget_queue_draw (GTK_WIDGET (scrolled_window));
978
979 if (overshoot_y != 0)
980 gtk_widget_queue_draw (GTK_WIDGET (scrolled_window));
981 }
982
983 static void
scrolled_window_drag_update_cb(GtkScrolledWindow * scrolled_window,double offset_x,double offset_y,GtkGesture * gesture)984 scrolled_window_drag_update_cb (GtkScrolledWindow *scrolled_window,
985 double offset_x,
986 double offset_y,
987 GtkGesture *gesture)
988 {
989 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
990 GdkEventSequence *sequence;
991 GtkAdjustment *hadjustment;
992 GtkAdjustment *vadjustment;
993 double dx, dy;
994
995 sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
996
997 if (gtk_gesture_get_sequence_state (gesture, sequence) != GTK_EVENT_SEQUENCE_CLAIMED &&
998 !gtk_drag_check_threshold_double (GTK_WIDGET (scrolled_window),
999 0, 0, offset_x, offset_y))
1000 return;
1001
1002 gtk_scrolled_window_invalidate_overshoot (scrolled_window);
1003 gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_CLAIMED);
1004
1005 hadjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->hscrollbar));
1006 if (hadjustment && may_hscroll (scrolled_window))
1007 {
1008 dx = priv->drag_start_x - offset_x;
1009 _gtk_scrolled_window_set_adjustment_value (scrolled_window,
1010 hadjustment, dx);
1011 }
1012
1013 vadjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->vscrollbar));
1014 if (vadjustment && may_vscroll (scrolled_window))
1015 {
1016 dy = priv->drag_start_y - offset_y;
1017 _gtk_scrolled_window_set_adjustment_value (scrolled_window,
1018 vadjustment, dy);
1019 }
1020
1021 gtk_scrolled_window_invalidate_overshoot (scrolled_window);
1022 }
1023
1024 static void
scrolled_window_drag_end_cb(GtkScrolledWindow * scrolled_window,GdkEventSequence * sequence,GtkGesture * gesture)1025 scrolled_window_drag_end_cb (GtkScrolledWindow *scrolled_window,
1026 GdkEventSequence *sequence,
1027 GtkGesture *gesture)
1028 {
1029 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
1030
1031 if (!priv->in_drag || !gtk_gesture_handles_sequence (gesture, sequence))
1032 gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_DENIED);
1033 }
1034
1035 static void
gtk_scrolled_window_decelerate(GtkScrolledWindow * scrolled_window,double x_velocity,double y_velocity)1036 gtk_scrolled_window_decelerate (GtkScrolledWindow *scrolled_window,
1037 double x_velocity,
1038 double y_velocity)
1039 {
1040 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
1041 gboolean overshoot;
1042
1043 overshoot = _gtk_scrolled_window_get_overshoot (scrolled_window, NULL, NULL);
1044 priv->x_velocity = x_velocity;
1045 priv->y_velocity = y_velocity;
1046
1047 /* Zero out vector components for which we don't scroll */
1048 if (!may_hscroll (scrolled_window))
1049 priv->x_velocity = 0;
1050 if (!may_vscroll (scrolled_window))
1051 priv->y_velocity = 0;
1052
1053 if (priv->x_velocity != 0 || priv->y_velocity != 0 || overshoot)
1054 {
1055 gtk_scrolled_window_start_deceleration (scrolled_window);
1056 priv->x_velocity = priv->y_velocity = 0;
1057 }
1058 }
1059
1060 static void
scrolled_window_swipe_cb(GtkScrolledWindow * scrolled_window,double x_velocity,double y_velocity)1061 scrolled_window_swipe_cb (GtkScrolledWindow *scrolled_window,
1062 double x_velocity,
1063 double y_velocity)
1064 {
1065 gtk_scrolled_window_decelerate (scrolled_window, -x_velocity, -y_velocity);
1066 }
1067
1068 static void
scrolled_window_long_press_cb(GtkScrolledWindow * scrolled_window,double x,double y,GtkGesture * gesture)1069 scrolled_window_long_press_cb (GtkScrolledWindow *scrolled_window,
1070 double x,
1071 double y,
1072 GtkGesture *gesture)
1073 {
1074 GdkEventSequence *sequence;
1075
1076 sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
1077 gtk_gesture_set_sequence_state (gesture, sequence,
1078 GTK_EVENT_SEQUENCE_DENIED);
1079 }
1080
1081 static void
scrolled_window_long_press_cancelled_cb(GtkScrolledWindow * scrolled_window,GtkGesture * gesture)1082 scrolled_window_long_press_cancelled_cb (GtkScrolledWindow *scrolled_window,
1083 GtkGesture *gesture)
1084 {
1085 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
1086 GdkEventSequence *sequence;
1087 GdkEvent *event;
1088 GdkEventType event_type;
1089
1090 sequence = gtk_gesture_get_last_updated_sequence (gesture);
1091 event = gtk_gesture_get_last_event (gesture, sequence);
1092 event_type = gdk_event_get_event_type (event);
1093
1094 if (event_type == GDK_TOUCH_BEGIN ||
1095 event_type == GDK_BUTTON_PRESS)
1096 gtk_gesture_set_sequence_state (gesture, sequence,
1097 GTK_EVENT_SEQUENCE_DENIED);
1098 else if (event_type != GDK_TOUCH_END &&
1099 event_type != GDK_BUTTON_RELEASE)
1100 priv->in_drag = TRUE;
1101 }
1102
1103 static void
gtk_scrolled_window_check_attach_pan_gesture(GtkScrolledWindow * sw)1104 gtk_scrolled_window_check_attach_pan_gesture (GtkScrolledWindow *sw)
1105 {
1106 GtkPropagationPhase phase = GTK_PHASE_NONE;
1107 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (sw);
1108
1109 if (priv->kinetic_scrolling &&
1110 ((may_hscroll (sw) && !may_vscroll (sw)) ||
1111 (!may_hscroll (sw) && may_vscroll (sw))))
1112 {
1113 GtkOrientation orientation;
1114
1115 if (may_hscroll (sw))
1116 orientation = GTK_ORIENTATION_HORIZONTAL;
1117 else
1118 orientation = GTK_ORIENTATION_VERTICAL;
1119
1120 gtk_gesture_pan_set_orientation (GTK_GESTURE_PAN (priv->pan_gesture),
1121 orientation);
1122 phase = GTK_PHASE_CAPTURE;
1123 }
1124
1125 gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (priv->pan_gesture), phase);
1126 }
1127
1128 static void
indicator_set_over(Indicator * indicator,gboolean over)1129 indicator_set_over (Indicator *indicator,
1130 gboolean over)
1131 {
1132 g_clear_handle_id (&indicator->over_timeout_id, g_source_remove);
1133
1134 if (indicator->over == over)
1135 return;
1136
1137 indicator->over = over;
1138
1139 if (indicator->over)
1140 gtk_widget_add_css_class (indicator->scrollbar, "hovering");
1141 else
1142 gtk_widget_remove_css_class (indicator->scrollbar, "hovering");
1143
1144 gtk_widget_queue_resize (indicator->scrollbar);
1145 }
1146
1147 static gboolean
coords_close_to_indicator(GtkScrolledWindow * sw,Indicator * indicator,double x,double y)1148 coords_close_to_indicator (GtkScrolledWindow *sw,
1149 Indicator *indicator,
1150 double x,
1151 double y)
1152 {
1153 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (sw);
1154 graphene_rect_t indicator_bounds;
1155 int distance;
1156
1157 if (!gtk_widget_compute_bounds (indicator->scrollbar, GTK_WIDGET (sw), &indicator_bounds))
1158 return FALSE;
1159
1160 if (indicator->over)
1161 distance = INDICATOR_FAR_DISTANCE;
1162 else
1163 distance = INDICATOR_CLOSE_DISTANCE;
1164
1165 graphene_rect_inset (&indicator_bounds, - distance, - distance);
1166
1167 if (indicator == &priv->hindicator)
1168 {
1169 if (y >= indicator_bounds.origin.y &&
1170 y < indicator_bounds.origin.y + indicator_bounds.size.height)
1171 return TRUE;
1172 }
1173 else if (indicator == &priv->vindicator)
1174 {
1175 if (x >= indicator_bounds.origin.x &&
1176 x < indicator_bounds.origin.x + indicator_bounds.size.width)
1177 return TRUE;
1178 }
1179
1180 return FALSE;
1181 }
1182
1183 static gboolean
enable_over_timeout_cb(gpointer user_data)1184 enable_over_timeout_cb (gpointer user_data)
1185 {
1186 Indicator *indicator = user_data;
1187
1188 indicator_set_over (indicator, TRUE);
1189 return G_SOURCE_REMOVE;
1190 }
1191
1192 static gboolean
check_update_scrollbar_proximity(GtkScrolledWindow * sw,Indicator * indicator,GtkWidget * target,double x,double y)1193 check_update_scrollbar_proximity (GtkScrolledWindow *sw,
1194 Indicator *indicator,
1195 GtkWidget *target,
1196 double x,
1197 double y)
1198 {
1199 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (sw);
1200 gboolean indicator_close, on_scrollbar, on_other_scrollbar;
1201
1202 indicator_close = coords_close_to_indicator (sw, indicator, x, y);
1203 on_scrollbar = (target == indicator->scrollbar ||
1204 gtk_widget_is_ancestor (target, indicator->scrollbar));
1205 on_other_scrollbar = (!on_scrollbar &&
1206 (target == priv->hindicator.scrollbar ||
1207 target == priv->vindicator.scrollbar ||
1208 gtk_widget_is_ancestor (target, priv->hindicator.scrollbar) ||
1209 gtk_widget_is_ancestor (target, priv->vindicator.scrollbar)));
1210
1211
1212 g_clear_handle_id (&indicator->over_timeout_id, g_source_remove);
1213
1214 if (on_scrollbar)
1215 indicator_set_over (indicator, TRUE);
1216 else if (indicator_close && !on_other_scrollbar)
1217 {
1218 indicator->over_timeout_id = g_timeout_add (30, enable_over_timeout_cb, indicator);
1219 gdk_source_set_static_name_by_id (indicator->over_timeout_id, "[gtk] enable_over_timeout_cb");
1220 }
1221 else
1222 indicator_set_over (indicator, FALSE);
1223
1224 return indicator_close;
1225 }
1226
1227 static double
get_scroll_unit(GtkScrolledWindow * sw,GtkOrientation orientation)1228 get_scroll_unit (GtkScrolledWindow *sw,
1229 GtkOrientation orientation)
1230 {
1231 double scroll_unit;
1232
1233 #ifndef GDK_WINDOWING_MACOS
1234 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (sw);
1235 GtkScrollbar *scrollbar;
1236 GtkAdjustment *adj;
1237 double page_size;
1238
1239 if (orientation == GTK_ORIENTATION_HORIZONTAL)
1240 scrollbar = GTK_SCROLLBAR (priv->hscrollbar);
1241 else
1242 scrollbar = GTK_SCROLLBAR (priv->vscrollbar);
1243
1244 if (!scrollbar)
1245 return 0;
1246
1247 adj = gtk_scrollbar_get_adjustment (scrollbar);
1248 page_size = gtk_adjustment_get_page_size (adj);
1249 scroll_unit = pow (page_size, 2.0 / 3.0);
1250 #else
1251 scroll_unit = 1;
1252 #endif
1253
1254 return scroll_unit;
1255 }
1256
1257 static gboolean
captured_scroll_cb(GtkEventControllerScroll * scroll,double delta_x,double delta_y,GtkScrolledWindow * scrolled_window)1258 captured_scroll_cb (GtkEventControllerScroll *scroll,
1259 double delta_x,
1260 double delta_y,
1261 GtkScrolledWindow *scrolled_window)
1262 {
1263 GtkScrolledWindowPrivate *priv =
1264 gtk_scrolled_window_get_instance_private (scrolled_window);
1265
1266 gtk_scrolled_window_cancel_deceleration (scrolled_window);
1267
1268 if (priv->smooth_scroll)
1269 {
1270 scrolled_window_scroll (scrolled_window, delta_x, delta_y, scroll);
1271 return GDK_EVENT_STOP;
1272 }
1273
1274 return GDK_EVENT_PROPAGATE;
1275 }
1276
1277 static void
captured_motion(GtkEventController * controller,double x,double y,GtkScrolledWindow * sw)1278 captured_motion (GtkEventController *controller,
1279 double x,
1280 double y,
1281 GtkScrolledWindow *sw)
1282 {
1283 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (sw);
1284 GdkDevice *source_device;
1285 GdkInputSource input_source;
1286 GdkModifierType state;
1287 GdkEvent *event;
1288 GtkWidget *target;
1289
1290 if (!priv->use_indicators)
1291 return;
1292
1293 if (!priv->child)
1294 return;
1295
1296 target = gtk_event_controller_get_target (controller);
1297 state = gtk_event_controller_get_current_event_state (controller);
1298 event = gtk_event_controller_get_current_event (controller);
1299
1300 source_device = gdk_event_get_device (event);
1301 input_source = gdk_device_get_source (source_device);
1302
1303 if (priv->hscrollbar_visible)
1304 indicator_start_fade (&priv->hindicator, 1.0);
1305 if (priv->vscrollbar_visible)
1306 indicator_start_fade (&priv->vindicator, 1.0);
1307
1308 if ((target == priv->child ||
1309 gtk_widget_is_ancestor (target, priv->child)) &&
1310 (state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK)) != 0)
1311 {
1312 indicator_set_over (&priv->hindicator, FALSE);
1313 indicator_set_over (&priv->vindicator, FALSE);
1314 }
1315 else if (input_source == GDK_SOURCE_PEN ||
1316 input_source == GDK_SOURCE_TRACKPOINT)
1317 {
1318 indicator_set_over (&priv->hindicator, TRUE);
1319 indicator_set_over (&priv->vindicator, TRUE);
1320 }
1321 else
1322 {
1323 if (!check_update_scrollbar_proximity (sw, &priv->vindicator, target, x, y))
1324 check_update_scrollbar_proximity (sw, &priv->hindicator, target, x, y);
1325 else
1326 indicator_set_over (&priv->hindicator, FALSE);
1327 }
1328 }
1329
1330 static gboolean
start_scroll_deceleration_cb(gpointer user_data)1331 start_scroll_deceleration_cb (gpointer user_data)
1332 {
1333 GtkScrolledWindow *scrolled_window = user_data;
1334 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
1335
1336 priv->scroll_events_overshoot_id = 0;
1337
1338 if (!priv->deceleration_id)
1339 gtk_scrolled_window_start_deceleration (scrolled_window);
1340
1341 return FALSE;
1342 }
1343
1344 static void
scroll_controller_scroll_begin(GtkEventControllerScroll * scroll,GtkScrolledWindow * scrolled_window)1345 scroll_controller_scroll_begin (GtkEventControllerScroll *scroll,
1346 GtkScrolledWindow *scrolled_window)
1347 {
1348 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
1349
1350 priv->smooth_scroll = TRUE;
1351 }
1352
1353 static void
scrolled_window_scroll(GtkScrolledWindow * scrolled_window,double delta_x,double delta_y,GtkEventControllerScroll * scroll)1354 scrolled_window_scroll (GtkScrolledWindow *scrolled_window,
1355 double delta_x,
1356 double delta_y,
1357 GtkEventControllerScroll *scroll)
1358 {
1359 GtkScrolledWindowPrivate *priv =
1360 gtk_scrolled_window_get_instance_private (scrolled_window);
1361 gboolean shifted;
1362 GdkModifierType state;
1363
1364 state = gtk_event_controller_get_current_event_state (GTK_EVENT_CONTROLLER (scroll));
1365 shifted = (state & GDK_SHIFT_MASK) != 0;
1366
1367 gtk_scrolled_window_invalidate_overshoot (scrolled_window);
1368
1369 if (shifted)
1370 {
1371 double delta;
1372
1373 delta = delta_x;
1374 delta_x = delta_y;
1375 delta_y = delta;
1376 }
1377
1378 if (delta_x != 0.0 &&
1379 may_hscroll (scrolled_window))
1380 {
1381 GtkAdjustment *adj;
1382 double new_value;
1383 double scroll_unit;
1384
1385 adj = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->hscrollbar));
1386 scroll_unit = get_scroll_unit (scrolled_window, GTK_ORIENTATION_HORIZONTAL);
1387
1388 new_value = priv->unclamped_hadj_value + delta_x * scroll_unit;
1389 _gtk_scrolled_window_set_adjustment_value (scrolled_window, adj,
1390 new_value);
1391 }
1392
1393 if (delta_y != 0.0 &&
1394 may_vscroll (scrolled_window))
1395 {
1396 GtkAdjustment *adj;
1397 double new_value;
1398 double scroll_unit;
1399
1400 adj = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->vscrollbar));
1401 scroll_unit = get_scroll_unit (scrolled_window, GTK_ORIENTATION_VERTICAL);
1402
1403 new_value = priv->unclamped_vadj_value + delta_y * scroll_unit;
1404 _gtk_scrolled_window_set_adjustment_value (scrolled_window, adj,
1405 new_value);
1406 }
1407
1408 g_clear_handle_id (&priv->scroll_events_overshoot_id, g_source_remove);
1409
1410 if (!priv->smooth_scroll &&
1411 _gtk_scrolled_window_get_overshoot (scrolled_window, NULL, NULL))
1412 {
1413 priv->scroll_events_overshoot_id =
1414 g_timeout_add (50, start_scroll_deceleration_cb, scrolled_window);
1415 gdk_source_set_static_name_by_id (priv->scroll_events_overshoot_id,
1416 "[gtk] start_scroll_deceleration_cb");
1417 }
1418 }
1419
1420 static gboolean
scroll_controller_scroll(GtkEventControllerScroll * scroll,double delta_x,double delta_y,GtkScrolledWindow * scrolled_window)1421 scroll_controller_scroll (GtkEventControllerScroll *scroll,
1422 double delta_x,
1423 double delta_y,
1424 GtkScrolledWindow *scrolled_window)
1425 {
1426 GtkScrolledWindowPrivate *priv =
1427 gtk_scrolled_window_get_instance_private (scrolled_window);
1428
1429 if (!priv->smooth_scroll)
1430 scrolled_window_scroll (scrolled_window, delta_x, delta_y, scroll);
1431
1432 return GDK_EVENT_STOP;
1433 }
1434
1435 static void
scroll_controller_scroll_end(GtkEventControllerScroll * scroll,GtkScrolledWindow * scrolled_window)1436 scroll_controller_scroll_end (GtkEventControllerScroll *scroll,
1437 GtkScrolledWindow *scrolled_window)
1438 {
1439 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
1440
1441 priv->smooth_scroll = FALSE;
1442 }
1443
1444 static void
scroll_controller_decelerate(GtkEventControllerScroll * scroll,double initial_vel_x,double initial_vel_y,GtkScrolledWindow * scrolled_window)1445 scroll_controller_decelerate (GtkEventControllerScroll *scroll,
1446 double initial_vel_x,
1447 double initial_vel_y,
1448 GtkScrolledWindow *scrolled_window)
1449 {
1450 double unit_x, unit_y;
1451 gboolean shifted;
1452 GdkModifierType state;
1453
1454
1455 state = gtk_event_controller_get_current_event_state (GTK_EVENT_CONTROLLER (scroll));
1456
1457 shifted = (state & GDK_SHIFT_MASK) != 0;
1458
1459 unit_x = get_scroll_unit (scrolled_window, GTK_ORIENTATION_HORIZONTAL);
1460 unit_y = get_scroll_unit (scrolled_window, GTK_ORIENTATION_VERTICAL);
1461
1462 if (shifted)
1463 {
1464 gtk_scrolled_window_decelerate (scrolled_window,
1465 initial_vel_y * unit_x,
1466 initial_vel_x * unit_y);
1467 }
1468 else
1469 {
1470 gtk_scrolled_window_decelerate (scrolled_window,
1471 initial_vel_x * unit_x,
1472 initial_vel_y * unit_y);
1473 }
1474 }
1475
1476 static void
gtk_scrolled_window_update_scrollbar_visibility_flags(GtkScrolledWindow * scrolled_window,GtkWidget * scrollbar)1477 gtk_scrolled_window_update_scrollbar_visibility_flags (GtkScrolledWindow *scrolled_window,
1478 GtkWidget *scrollbar)
1479 {
1480 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
1481 GtkAdjustment *adjustment;
1482
1483 if (scrollbar == NULL)
1484 return;
1485
1486 adjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (scrollbar));
1487
1488 if (scrollbar == priv->hscrollbar)
1489 {
1490 if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
1491 {
1492 priv->hscrollbar_visible = (gtk_adjustment_get_upper (adjustment) - gtk_adjustment_get_lower (adjustment) >
1493 gtk_adjustment_get_page_size (adjustment));
1494 }
1495 }
1496 else if (scrollbar == priv->vscrollbar)
1497 {
1498 if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
1499 {
1500 priv->vscrollbar_visible = (gtk_adjustment_get_upper (adjustment) - gtk_adjustment_get_lower (adjustment) >
1501 gtk_adjustment_get_page_size (adjustment));
1502 }
1503 }
1504 }
1505
1506 static void
gtk_scrolled_window_size_allocate(GtkWidget * widget,int width,int height,int baseline)1507 gtk_scrolled_window_size_allocate (GtkWidget *widget,
1508 int width,
1509 int height,
1510 int baseline)
1511 {
1512 GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
1513 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
1514 GtkAllocation child_allocation;
1515 int sb_width;
1516 int sb_height;
1517
1518 /* Get possible scrollbar dimensions */
1519 gtk_widget_measure (priv->vscrollbar, GTK_ORIENTATION_HORIZONTAL, -1,
1520 &sb_width, NULL, NULL, NULL);
1521 gtk_widget_measure (priv->hscrollbar, GTK_ORIENTATION_VERTICAL, -1,
1522 &sb_height, NULL, NULL, NULL);
1523
1524 if (priv->hscrollbar_policy == GTK_POLICY_ALWAYS)
1525 priv->hscrollbar_visible = TRUE;
1526 else if (priv->hscrollbar_policy == GTK_POLICY_NEVER ||
1527 priv->hscrollbar_policy == GTK_POLICY_EXTERNAL)
1528 priv->hscrollbar_visible = FALSE;
1529
1530 if (priv->vscrollbar_policy == GTK_POLICY_ALWAYS)
1531 priv->vscrollbar_visible = TRUE;
1532 else if (priv->vscrollbar_policy == GTK_POLICY_NEVER ||
1533 priv->vscrollbar_policy == GTK_POLICY_EXTERNAL)
1534 priv->vscrollbar_visible = FALSE;
1535
1536 if (priv->child && gtk_widget_get_visible (priv->child))
1537 {
1538 int child_scroll_width;
1539 int child_scroll_height;
1540 gboolean previous_hvis;
1541 gboolean previous_vvis;
1542 guint count = 0;
1543 GtkScrollable *scrollable_child = GTK_SCROLLABLE (priv->child);
1544 GtkScrollablePolicy hscroll_policy = gtk_scrollable_get_hscroll_policy (scrollable_child);
1545 GtkScrollablePolicy vscroll_policy = gtk_scrollable_get_vscroll_policy (scrollable_child);
1546
1547 /* Determine scrollbar visibility first via hfw apis */
1548 if (gtk_widget_get_request_mode (priv->child) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH)
1549 {
1550 if (hscroll_policy == GTK_SCROLL_MINIMUM)
1551 gtk_widget_measure (priv->child, GTK_ORIENTATION_HORIZONTAL, -1,
1552 &child_scroll_width, NULL, NULL, NULL);
1553 else
1554 gtk_widget_measure (priv->child, GTK_ORIENTATION_HORIZONTAL, -1,
1555 NULL, &child_scroll_width, NULL, NULL);
1556
1557 if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
1558 {
1559 /* First try without a vertical scrollbar if the content will fit the height
1560 * given the extra width of the scrollbar */
1561 if (vscroll_policy == GTK_SCROLL_MINIMUM)
1562 gtk_widget_measure (priv->child, GTK_ORIENTATION_VERTICAL,
1563 MAX (width, child_scroll_width),
1564 &child_scroll_height, NULL, NULL, NULL);
1565 else
1566 gtk_widget_measure (priv->child, GTK_ORIENTATION_VERTICAL,
1567 MAX (width, child_scroll_width),
1568 NULL, &child_scroll_height, NULL, NULL);
1569
1570 if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
1571 {
1572 /* Does the content height fit the allocation height ? */
1573 priv->vscrollbar_visible = child_scroll_height > height;
1574
1575 /* Does the content width fit the allocation with minus a possible scrollbar ? */
1576 priv->hscrollbar_visible = child_scroll_width > width -
1577 (priv->vscrollbar_visible && !priv->use_indicators ? sb_width : 0);
1578
1579 /* Now that we've guessed the hscrollbar, does the content height fit
1580 * the possible new allocation height ?
1581 */
1582 priv->vscrollbar_visible = child_scroll_height > height -
1583 (priv->hscrollbar_visible && !priv->use_indicators ? sb_height : 0);
1584
1585 /* Now that we've guessed the vscrollbar, does the content width fit
1586 * the possible new allocation width ?
1587 */
1588 priv->hscrollbar_visible = child_scroll_width > width -
1589 (priv->vscrollbar_visible && !priv->use_indicators ? sb_width : 0);
1590 }
1591 else /* priv->hscrollbar_policy != GTK_POLICY_AUTOMATIC */
1592 {
1593 priv->hscrollbar_visible = policy_may_be_visible (priv->hscrollbar_policy);
1594 priv->vscrollbar_visible = child_scroll_height > height -
1595 (priv->hscrollbar_visible && !priv->use_indicators ? sb_height : 0);
1596 }
1597 }
1598 else /* priv->vscrollbar_policy != GTK_POLICY_AUTOMATIC */
1599 {
1600 priv->vscrollbar_visible = policy_may_be_visible (priv->vscrollbar_policy);
1601
1602 if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
1603 priv->hscrollbar_visible = child_scroll_width > width -
1604 (priv->vscrollbar_visible && !priv->use_indicators ? 0 : sb_width);
1605 else
1606 priv->hscrollbar_visible = policy_may_be_visible (priv->hscrollbar_policy);
1607 }
1608 }
1609 else /* GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT */
1610 {
1611 if (vscroll_policy == GTK_SCROLL_MINIMUM)
1612 gtk_widget_measure (priv->child, GTK_ORIENTATION_VERTICAL, -1,
1613 &child_scroll_height, NULL, NULL, NULL);
1614 else
1615 gtk_widget_measure (priv->child, GTK_ORIENTATION_VERTICAL, -1,
1616 NULL, &child_scroll_height, NULL, NULL);
1617
1618 if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
1619 {
1620 /* First try without a horizontal scrollbar if the content will fit the width
1621 * given the extra height of the scrollbar */
1622 if (hscroll_policy == GTK_SCROLL_MINIMUM)
1623 gtk_widget_measure (priv->child, GTK_ORIENTATION_HORIZONTAL,
1624 MAX (height, child_scroll_height),
1625 &child_scroll_width, NULL, NULL, NULL);
1626 else
1627 gtk_widget_measure (priv->child, GTK_ORIENTATION_HORIZONTAL,
1628 MAX (height, child_scroll_height),
1629 NULL, &child_scroll_width, NULL, NULL);
1630
1631 if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
1632 {
1633 /* Does the content width fit the allocation width ? */
1634 priv->hscrollbar_visible = child_scroll_width > width;
1635
1636 /* Does the content height fit the allocation with minus a possible scrollbar ? */
1637 priv->vscrollbar_visible = child_scroll_height > height -
1638 (priv->hscrollbar_visible && !priv->use_indicators ? sb_height : 0);
1639
1640 /* Now that we've guessed the vscrollbar, does the content width fit
1641 * the possible new allocation width ?
1642 */
1643 priv->hscrollbar_visible = child_scroll_width > width -
1644 (priv->vscrollbar_visible && !priv->use_indicators ? sb_width : 0);
1645
1646 /* Now that we've guessed the hscrollbar, does the content height fit
1647 * the possible new allocation height ?
1648 */
1649 priv->vscrollbar_visible = child_scroll_height > height -
1650 (priv->hscrollbar_visible && !priv->use_indicators ? sb_height : 0);
1651 }
1652 else /* priv->vscrollbar_policy != GTK_POLICY_AUTOMATIC */
1653 {
1654 priv->vscrollbar_visible = policy_may_be_visible (priv->vscrollbar_policy);
1655 priv->hscrollbar_visible = child_scroll_width > width -
1656 (priv->vscrollbar_visible && !priv->use_indicators ? sb_width : 0);
1657 }
1658 }
1659 else /* priv->hscrollbar_policy != GTK_POLICY_AUTOMATIC */
1660 {
1661 priv->hscrollbar_visible = policy_may_be_visible (priv->hscrollbar_policy);
1662
1663 if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
1664 priv->vscrollbar_visible = child_scroll_height > height -
1665 (priv->hscrollbar_visible && !priv->use_indicators ? sb_height : 0);
1666 else
1667 priv->vscrollbar_visible = policy_may_be_visible (priv->vscrollbar_policy);
1668 }
1669 }
1670
1671 /* Now after guessing scrollbar visibility; fall back on the allocation loop which
1672 * observes the adjustments to detect scrollbar visibility and also avoids
1673 * infinite recursion
1674 */
1675 do
1676 {
1677 previous_hvis = priv->hscrollbar_visible;
1678 previous_vvis = priv->vscrollbar_visible;
1679
1680 gtk_scrolled_window_allocate_child (scrolled_window, width, height);
1681
1682 /* Explicitly force scrollbar visibility checks.
1683 *
1684 * Since we make a guess above, the child might not decide to update the adjustments
1685 * if they logically did not change since the last configuration
1686 *
1687 * These will update priv->hscrollbar_visible and priv->vscrollbar_visible.
1688 */
1689 gtk_scrolled_window_update_scrollbar_visibility_flags (scrolled_window,
1690 priv->hscrollbar);
1691
1692 gtk_scrolled_window_update_scrollbar_visibility_flags (scrolled_window,
1693 priv->vscrollbar);
1694
1695 /* If, after the first iteration, the hscrollbar and the
1696 * vscrollbar flip visibility... or if one of the scrollbars flip
1697 * on each iteration indefinitely/infinitely, then we just need both
1698 * at this size.
1699 */
1700 if ((count &&
1701 previous_hvis != priv->hscrollbar_visible &&
1702 previous_vvis != priv->vscrollbar_visible) || count > 3)
1703 {
1704 priv->hscrollbar_visible = TRUE;
1705 priv->vscrollbar_visible = TRUE;
1706
1707 gtk_scrolled_window_allocate_child (scrolled_window, width, height);
1708
1709 break;
1710 }
1711
1712 count++;
1713 }
1714 while (previous_hvis != priv->hscrollbar_visible ||
1715 previous_vvis != priv->vscrollbar_visible);
1716 }
1717 else
1718 {
1719 priv->hscrollbar_visible = priv->hscrollbar_policy == GTK_POLICY_ALWAYS;
1720 priv->vscrollbar_visible = priv->vscrollbar_policy == GTK_POLICY_ALWAYS;
1721 }
1722
1723 gtk_widget_set_child_visible (priv->hscrollbar, priv->hscrollbar_visible);
1724 if (priv->hscrollbar_visible)
1725 {
1726 gtk_scrolled_window_allocate_scrollbar (scrolled_window,
1727 priv->hscrollbar,
1728 &child_allocation);
1729 gtk_widget_size_allocate (priv->hscrollbar, &child_allocation, -1);
1730 }
1731
1732 gtk_widget_set_child_visible (priv->vscrollbar, priv->vscrollbar_visible);
1733 if (priv->vscrollbar_visible)
1734 {
1735 gtk_scrolled_window_allocate_scrollbar (scrolled_window,
1736 priv->vscrollbar,
1737 &child_allocation);
1738 gtk_widget_size_allocate (priv->vscrollbar, &child_allocation, -1);
1739 }
1740
1741 gtk_scrolled_window_check_attach_pan_gesture (scrolled_window);
1742 }
1743
1744 static void
gtk_scrolled_window_measure(GtkWidget * widget,GtkOrientation orientation,int for_size,int * minimum_size,int * natural_size,int * minimum_baseline,int * natural_baseline)1745 gtk_scrolled_window_measure (GtkWidget *widget,
1746 GtkOrientation orientation,
1747 int for_size,
1748 int *minimum_size,
1749 int *natural_size,
1750 int *minimum_baseline,
1751 int *natural_baseline)
1752 {
1753 GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
1754 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
1755 int minimum_req = 0, natural_req = 0;
1756 GtkBorder sborder = { 0 };
1757
1758 if (priv->child)
1759 gtk_scrollable_get_border (GTK_SCROLLABLE (priv->child), &sborder);
1760
1761 /*
1762 * First collect the child requisition
1763 */
1764 if (priv->child && gtk_widget_get_visible (priv->child))
1765 {
1766 int min_child_size, nat_child_size;
1767
1768 gtk_widget_measure (priv->child, orientation, -1,
1769 &min_child_size, &nat_child_size,
1770 NULL, NULL);
1771
1772 if (orientation == GTK_ORIENTATION_HORIZONTAL)
1773 {
1774 if (priv->propagate_natural_width)
1775 natural_req += nat_child_size;
1776
1777 if (priv->hscrollbar_policy == GTK_POLICY_NEVER)
1778 {
1779 minimum_req += min_child_size;
1780 }
1781 else
1782 {
1783 int min = priv->min_content_width >= 0 ? priv->min_content_width : 0;
1784 int max = priv->max_content_width >= 0 ? priv->max_content_width : G_MAXINT;
1785
1786 minimum_req = CLAMP (minimum_req, min, max);
1787 natural_req = CLAMP (natural_req, min, max);
1788 }
1789 }
1790 else /* GTK_ORIENTATION_VERTICAL */
1791 {
1792 if (priv->propagate_natural_height)
1793 natural_req += nat_child_size;
1794
1795 if (priv->vscrollbar_policy == GTK_POLICY_NEVER)
1796 {
1797 minimum_req += min_child_size;
1798 }
1799 else
1800 {
1801 int min = priv->min_content_height >= 0 ? priv->min_content_height : 0;
1802 int max = priv->max_content_height >= 0 ? priv->max_content_height : G_MAXINT;
1803
1804 minimum_req = CLAMP (minimum_req, min, max);
1805 natural_req = CLAMP (natural_req, min, max);
1806 }
1807 }
1808 }
1809
1810 /* Ensure we make requests with natural size >= minimum size */
1811 natural_req = MAX (minimum_req, natural_req);
1812
1813 /*
1814 * Now add to the requisition any additional space for surrounding scrollbars
1815 * and the special scrollable border.
1816 */
1817 if (policy_may_be_visible (priv->hscrollbar_policy))
1818 {
1819 if (orientation == GTK_ORIENTATION_HORIZONTAL)
1820 {
1821 int min_scrollbar_width, nat_scrollbar_width;
1822
1823 gtk_widget_measure (priv->hscrollbar, GTK_ORIENTATION_HORIZONTAL, -1,
1824 &min_scrollbar_width, &nat_scrollbar_width,
1825 NULL, NULL);
1826 minimum_req = MAX (minimum_req, min_scrollbar_width + sborder.left + sborder.right);
1827 natural_req = MAX (natural_req, nat_scrollbar_width + sborder.left + sborder.right);
1828 }
1829 else if (!priv->use_indicators && priv->hscrollbar_policy == GTK_POLICY_ALWAYS)
1830 {
1831 int min_scrollbar_height, nat_scrollbar_height;
1832
1833 gtk_widget_measure (priv->hscrollbar, GTK_ORIENTATION_VERTICAL, -1,
1834 &min_scrollbar_height, &nat_scrollbar_height,
1835 NULL, NULL);
1836
1837 minimum_req += min_scrollbar_height;
1838 natural_req += nat_scrollbar_height;
1839 }
1840 }
1841
1842 if (policy_may_be_visible (priv->vscrollbar_policy))
1843 {
1844 if (orientation == GTK_ORIENTATION_VERTICAL)
1845 {
1846 int min_scrollbar_height, nat_scrollbar_height;
1847
1848 gtk_widget_measure (priv->vscrollbar, GTK_ORIENTATION_VERTICAL, -1,
1849 &min_scrollbar_height, &nat_scrollbar_height,
1850 NULL, NULL);
1851 minimum_req = MAX (minimum_req, min_scrollbar_height + sborder.top + sborder.bottom);
1852 natural_req = MAX (natural_req, nat_scrollbar_height + sborder.top + sborder.bottom);
1853 }
1854 else if (!priv->use_indicators && priv->vscrollbar_policy == GTK_POLICY_ALWAYS)
1855 {
1856 int min_scrollbar_width, nat_scrollbar_width;
1857
1858 gtk_widget_measure (priv->vscrollbar, GTK_ORIENTATION_HORIZONTAL, -1,
1859 &min_scrollbar_width, &nat_scrollbar_width,
1860 NULL, NULL);
1861 minimum_req += min_scrollbar_width;
1862 natural_req += nat_scrollbar_width;
1863 }
1864 }
1865
1866 *minimum_size = minimum_req;
1867 *natural_size = natural_req;
1868 }
1869
1870 static void
gtk_scrolled_window_snapshot_scrollbars_junction(GtkScrolledWindow * scrolled_window,GtkSnapshot * snapshot)1871 gtk_scrolled_window_snapshot_scrollbars_junction (GtkScrolledWindow *scrolled_window,
1872 GtkSnapshot *snapshot)
1873 {
1874 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
1875 GtkWidget *widget = GTK_WIDGET (scrolled_window);
1876 GtkAllocation hscr_allocation, vscr_allocation;
1877 GtkStyleContext *context;
1878 GdkRectangle junction_rect;
1879
1880 gtk_widget_get_allocation (GTK_WIDGET (priv->hscrollbar), &hscr_allocation);
1881 gtk_widget_get_allocation (GTK_WIDGET (priv->vscrollbar), &vscr_allocation);
1882
1883 junction_rect.x = vscr_allocation.x;
1884 junction_rect.y = hscr_allocation.y;
1885 junction_rect.width = vscr_allocation.width;
1886 junction_rect.height = hscr_allocation.height;
1887
1888 context = gtk_widget_get_style_context (widget);
1889 gtk_style_context_save_to_node (context, priv->junction_node);
1890
1891 gtk_snapshot_render_background (snapshot, context,
1892 junction_rect.x, junction_rect.y,
1893 junction_rect.width, junction_rect.height);
1894 gtk_snapshot_render_frame (snapshot, context,
1895 junction_rect.x, junction_rect.y,
1896 junction_rect.width, junction_rect.height);
1897
1898 gtk_style_context_restore (context);
1899 }
1900
1901 static void
gtk_scrolled_window_snapshot_overshoot(GtkScrolledWindow * scrolled_window,GtkSnapshot * snapshot)1902 gtk_scrolled_window_snapshot_overshoot (GtkScrolledWindow *scrolled_window,
1903 GtkSnapshot *snapshot)
1904 {
1905 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
1906 GtkWidget *widget = GTK_WIDGET (scrolled_window);
1907 int overshoot_x, overshoot_y;
1908 GtkStyleContext *context;
1909 GdkRectangle rect;
1910
1911 if (!_gtk_scrolled_window_get_overshoot (scrolled_window, &overshoot_x, &overshoot_y))
1912 return;
1913
1914 context = gtk_widget_get_style_context (widget);
1915 gtk_scrolled_window_inner_allocation (scrolled_window, &rect);
1916
1917 overshoot_x = CLAMP (overshoot_x, - MAX_OVERSHOOT_DISTANCE, MAX_OVERSHOOT_DISTANCE);
1918 overshoot_y = CLAMP (overshoot_y, - MAX_OVERSHOOT_DISTANCE, MAX_OVERSHOOT_DISTANCE);
1919
1920 if (overshoot_x > 0)
1921 {
1922 gtk_style_context_save_to_node (context, priv->overshoot_node[GTK_POS_RIGHT]);
1923 gtk_snapshot_render_background (snapshot, context, rect.x + rect.width - overshoot_x, rect.y, overshoot_x, rect.height);
1924 gtk_snapshot_render_frame (snapshot, context, rect.x + rect.width - overshoot_x, rect.y, overshoot_x, rect.height);
1925 gtk_style_context_restore (context);
1926 }
1927 else if (overshoot_x < 0)
1928 {
1929 gtk_style_context_save_to_node (context, priv->overshoot_node[GTK_POS_LEFT]);
1930 gtk_snapshot_render_background (snapshot, context, rect.x, rect.y, -overshoot_x, rect.height);
1931 gtk_snapshot_render_frame (snapshot, context, rect.x, rect.y, -overshoot_x, rect.height);
1932 gtk_style_context_restore (context);
1933 }
1934
1935 if (overshoot_y > 0)
1936 {
1937 gtk_style_context_save_to_node (context, priv->overshoot_node[GTK_POS_BOTTOM]);
1938 gtk_snapshot_render_background (snapshot, context, rect.x, rect.y + rect.height - overshoot_y, rect.width, overshoot_y);
1939 gtk_snapshot_render_frame (snapshot, context, rect.x, rect.y + rect.height - overshoot_y, rect.width, overshoot_y);
1940 gtk_style_context_restore (context);
1941 }
1942 else if (overshoot_y < 0)
1943 {
1944 gtk_style_context_save_to_node (context, priv->overshoot_node[GTK_POS_TOP]);
1945 gtk_snapshot_render_background (snapshot, context, rect.x, rect.y, rect.width, -overshoot_y);
1946 gtk_snapshot_render_frame (snapshot, context, rect.x, rect.y, rect.width, -overshoot_y);
1947 gtk_style_context_restore (context);
1948 }
1949 }
1950
1951 static void
gtk_scrolled_window_snapshot_undershoot(GtkScrolledWindow * scrolled_window,GtkSnapshot * snapshot)1952 gtk_scrolled_window_snapshot_undershoot (GtkScrolledWindow *scrolled_window,
1953 GtkSnapshot *snapshot)
1954 {
1955 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
1956 GtkWidget *widget = GTK_WIDGET (scrolled_window);
1957 GtkStyleContext *context;
1958 GdkRectangle rect;
1959 GtkAdjustment *adj;
1960
1961 context = gtk_widget_get_style_context (widget);
1962 gtk_scrolled_window_inner_allocation (scrolled_window, &rect);
1963
1964 adj = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->hscrollbar));
1965 if (gtk_adjustment_get_value (adj) < gtk_adjustment_get_upper (adj) - gtk_adjustment_get_page_size (adj))
1966 {
1967 gtk_style_context_save_to_node (context, priv->undershoot_node[GTK_POS_RIGHT]);
1968 gtk_snapshot_render_background (snapshot, context, rect.x + rect.width - UNDERSHOOT_SIZE, rect.y, UNDERSHOOT_SIZE, rect.height);
1969 gtk_snapshot_render_frame (snapshot, context, rect.x + rect.width - UNDERSHOOT_SIZE, rect.y, UNDERSHOOT_SIZE, rect.height);
1970
1971 gtk_style_context_restore (context);
1972 }
1973 if (gtk_adjustment_get_value (adj) > gtk_adjustment_get_lower (adj))
1974 {
1975 gtk_style_context_save_to_node (context, priv->undershoot_node[GTK_POS_LEFT]);
1976 gtk_snapshot_render_background (snapshot, context, rect.x, rect.y, UNDERSHOOT_SIZE, rect.height);
1977 gtk_snapshot_render_frame (snapshot, context, rect.x, rect.y, UNDERSHOOT_SIZE, rect.height);
1978 gtk_style_context_restore (context);
1979 }
1980
1981 adj = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->vscrollbar));
1982 if (gtk_adjustment_get_value (adj) < gtk_adjustment_get_upper (adj) - gtk_adjustment_get_page_size (adj))
1983 {
1984 gtk_style_context_save_to_node (context, priv->undershoot_node[GTK_POS_BOTTOM]);
1985 gtk_snapshot_render_background (snapshot, context, rect.x, rect.y + rect.height - UNDERSHOOT_SIZE, rect.width, UNDERSHOOT_SIZE);
1986 gtk_snapshot_render_frame (snapshot, context, rect.x, rect.y + rect.height - UNDERSHOOT_SIZE, rect.width, UNDERSHOOT_SIZE);
1987 gtk_style_context_restore (context);
1988 }
1989 if (gtk_adjustment_get_value (adj) > gtk_adjustment_get_lower (adj))
1990 {
1991 gtk_style_context_save_to_node (context, priv->undershoot_node[GTK_POS_TOP]);
1992 gtk_snapshot_render_background (snapshot, context, rect.x, rect.y, rect.width, UNDERSHOOT_SIZE);
1993 gtk_snapshot_render_frame (snapshot, context, rect.x, rect.y, rect.width, UNDERSHOOT_SIZE);
1994 gtk_style_context_restore (context);
1995 }
1996 }
1997
1998 static void
gtk_scrolled_window_init(GtkScrolledWindow * scrolled_window)1999 gtk_scrolled_window_init (GtkScrolledWindow *scrolled_window)
2000 {
2001 GtkWidget *widget = GTK_WIDGET (scrolled_window);
2002 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
2003 GtkEventController *controller;
2004 GtkCssNode *widget_node;
2005 GQuark classes[4] = {
2006 g_quark_from_static_string ("left"),
2007 g_quark_from_static_string ("right"),
2008 g_quark_from_static_string ("top"),
2009 g_quark_from_static_string ("bottom"),
2010 };
2011 int i;
2012
2013 gtk_widget_set_focusable (widget, TRUE);
2014
2015 /* Instantiated by gtk_scrolled_window_set_[hv]adjustment
2016 * which are both construct properties
2017 */
2018 priv->hscrollbar = NULL;
2019 priv->vscrollbar = NULL;
2020 priv->hscrollbar_policy = GTK_POLICY_AUTOMATIC;
2021 priv->vscrollbar_policy = GTK_POLICY_AUTOMATIC;
2022 priv->hscrollbar_visible = FALSE;
2023 priv->vscrollbar_visible = FALSE;
2024 priv->focus_out = FALSE;
2025 priv->auto_added_viewport = FALSE;
2026 priv->window_placement = GTK_CORNER_TOP_LEFT;
2027 priv->min_content_width = -1;
2028 priv->min_content_height = -1;
2029 priv->max_content_width = -1;
2030 priv->max_content_height = -1;
2031
2032 priv->overlay_scrolling = TRUE;
2033
2034 priv->drag_gesture = gtk_gesture_drag_new ();
2035 gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (priv->drag_gesture), TRUE);
2036 g_signal_connect_swapped (priv->drag_gesture, "drag-begin",
2037 G_CALLBACK (scrolled_window_drag_begin_cb),
2038 scrolled_window);
2039 g_signal_connect_swapped (priv->drag_gesture, "drag-update",
2040 G_CALLBACK (scrolled_window_drag_update_cb),
2041 scrolled_window);
2042 g_signal_connect_swapped (priv->drag_gesture, "end",
2043 G_CALLBACK (scrolled_window_drag_end_cb),
2044 scrolled_window);
2045 gtk_widget_add_controller (widget, GTK_EVENT_CONTROLLER (priv->drag_gesture));
2046
2047 priv->pan_gesture = gtk_gesture_pan_new (GTK_ORIENTATION_VERTICAL);
2048 gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (priv->pan_gesture), TRUE);
2049 gtk_widget_add_controller (widget, GTK_EVENT_CONTROLLER (priv->pan_gesture));
2050 gtk_gesture_group (priv->pan_gesture, priv->drag_gesture);
2051
2052 priv->swipe_gesture = gtk_gesture_swipe_new ();
2053 gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (priv->swipe_gesture), TRUE);
2054 g_signal_connect_swapped (priv->swipe_gesture, "swipe",
2055 G_CALLBACK (scrolled_window_swipe_cb),
2056 scrolled_window);
2057 gtk_widget_add_controller (widget, GTK_EVENT_CONTROLLER (priv->swipe_gesture));
2058 gtk_gesture_group (priv->swipe_gesture, priv->drag_gesture);
2059
2060 priv->long_press_gesture = gtk_gesture_long_press_new ();
2061 gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (priv->long_press_gesture), TRUE);
2062 g_signal_connect_swapped (priv->long_press_gesture, "pressed",
2063 G_CALLBACK (scrolled_window_long_press_cb),
2064 scrolled_window);
2065 g_signal_connect_swapped (priv->long_press_gesture, "cancelled",
2066 G_CALLBACK (scrolled_window_long_press_cancelled_cb),
2067 scrolled_window);
2068 gtk_widget_add_controller (widget, GTK_EVENT_CONTROLLER (priv->long_press_gesture));
2069 gtk_gesture_group (priv->long_press_gesture, priv->drag_gesture);
2070
2071 gtk_scrolled_window_set_kinetic_scrolling (scrolled_window, TRUE);
2072
2073 controller = gtk_event_controller_motion_new ();
2074 gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_CAPTURE);
2075 g_signal_connect (controller, "motion",
2076 G_CALLBACK (captured_motion), scrolled_window);
2077 gtk_widget_add_controller (widget, controller);
2078
2079 widget_node = gtk_widget_get_css_node (widget);
2080 for (i = 0; i < 4; i++)
2081 {
2082 priv->overshoot_node[i] = gtk_css_node_new ();
2083 gtk_css_node_set_name (priv->overshoot_node[i], g_quark_from_static_string ("overshoot"));
2084 gtk_css_node_add_class (priv->overshoot_node[i], classes[i]);
2085 gtk_css_node_set_parent (priv->overshoot_node[i], widget_node);
2086 gtk_css_node_set_state (priv->overshoot_node[i], gtk_css_node_get_state (widget_node));
2087 g_object_unref (priv->overshoot_node[i]);
2088
2089 priv->undershoot_node[i] = gtk_css_node_new ();
2090 gtk_css_node_set_name (priv->undershoot_node[i], g_quark_from_static_string ("undershoot"));
2091 gtk_css_node_add_class (priv->undershoot_node[i], classes[i]);
2092 gtk_css_node_set_parent (priv->undershoot_node[i], widget_node);
2093 gtk_css_node_set_state (priv->undershoot_node[i], gtk_css_node_get_state (widget_node));
2094 g_object_unref (priv->undershoot_node[i]);
2095 }
2096
2097 gtk_scrolled_window_update_use_indicators (scrolled_window);
2098
2099 priv->junction_node = gtk_css_node_new ();
2100 gtk_css_node_set_name (priv->junction_node, g_quark_from_static_string ("junction"));
2101 gtk_css_node_set_parent (priv->junction_node, widget_node);
2102 gtk_css_node_set_state (priv->junction_node, gtk_css_node_get_state (widget_node));
2103 g_object_unref (priv->junction_node);
2104
2105 controller = gtk_event_controller_scroll_new (GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES |
2106 GTK_EVENT_CONTROLLER_SCROLL_KINETIC);
2107 g_signal_connect (controller, "scroll-begin",
2108 G_CALLBACK (scroll_controller_scroll_begin), scrolled_window);
2109 g_signal_connect (controller, "scroll",
2110 G_CALLBACK (scroll_controller_scroll), scrolled_window);
2111 g_signal_connect (controller, "scroll-end",
2112 G_CALLBACK (scroll_controller_scroll_end), scrolled_window);
2113 gtk_widget_add_controller (widget, controller);
2114
2115 controller = gtk_event_controller_scroll_new (GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES |
2116 GTK_EVENT_CONTROLLER_SCROLL_KINETIC);
2117 gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_CAPTURE);
2118 g_signal_connect (controller, "scroll",
2119 G_CALLBACK (captured_scroll_cb), scrolled_window);
2120 g_signal_connect (controller, "decelerate",
2121 G_CALLBACK (scroll_controller_decelerate), scrolled_window);
2122 gtk_widget_add_controller (widget, controller);
2123
2124 controller = gtk_event_controller_motion_new ();
2125 g_signal_connect (controller, "leave",
2126 G_CALLBACK (motion_controller_leave), scrolled_window);
2127 gtk_widget_add_controller (widget, controller);
2128 }
2129
2130 /**
2131 * gtk_scrolled_window_new:
2132 *
2133 * Creates a new scrolled window.
2134 *
2135 * Returns: a new scrolled window
2136 */
2137 GtkWidget *
gtk_scrolled_window_new(void)2138 gtk_scrolled_window_new (void)
2139 {
2140 return g_object_new (GTK_TYPE_SCROLLED_WINDOW, NULL);
2141 }
2142
2143 /**
2144 * gtk_scrolled_window_set_hadjustment: (attributes org.gtk.Method.set_property=hadjustment)
2145 * @scrolled_window: a `GtkScrolledWindow`
2146 * @hadjustment: (nullable): the `GtkAdjustment` to use, or %NULL to create a new one
2147 *
2148 * Sets the `GtkAdjustment` for the horizontal scrollbar.
2149 */
2150 void
gtk_scrolled_window_set_hadjustment(GtkScrolledWindow * scrolled_window,GtkAdjustment * hadjustment)2151 gtk_scrolled_window_set_hadjustment (GtkScrolledWindow *scrolled_window,
2152 GtkAdjustment *hadjustment)
2153 {
2154 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
2155
2156 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
2157
2158 if (hadjustment)
2159 g_return_if_fail (GTK_IS_ADJUSTMENT (hadjustment));
2160 else
2161 hadjustment = (GtkAdjustment*) g_object_new (GTK_TYPE_ADJUSTMENT, NULL);
2162
2163 if (!priv->hscrollbar)
2164 {
2165 priv->hscrollbar = gtk_scrollbar_new (GTK_ORIENTATION_HORIZONTAL, hadjustment);
2166
2167 gtk_widget_insert_before (priv->hscrollbar, GTK_WIDGET (scrolled_window), priv->vscrollbar);
2168 update_scrollbar_positions (scrolled_window);
2169 }
2170 else
2171 {
2172 GtkAdjustment *old_adjustment;
2173
2174 old_adjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->hscrollbar));
2175 if (old_adjustment == hadjustment)
2176 return;
2177
2178 g_signal_handlers_disconnect_by_func (old_adjustment,
2179 gtk_scrolled_window_adjustment_changed,
2180 scrolled_window);
2181 g_signal_handlers_disconnect_by_func (old_adjustment,
2182 gtk_scrolled_window_adjustment_value_changed,
2183 scrolled_window);
2184
2185 gtk_adjustment_enable_animation (old_adjustment, NULL, 0);
2186 gtk_scrollbar_set_adjustment (GTK_SCROLLBAR (priv->hscrollbar), hadjustment);
2187 }
2188
2189 hadjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->hscrollbar));
2190
2191 g_signal_connect (hadjustment,
2192 "changed",
2193 G_CALLBACK (gtk_scrolled_window_adjustment_changed),
2194 scrolled_window);
2195 g_signal_connect (hadjustment,
2196 "value-changed",
2197 G_CALLBACK (gtk_scrolled_window_adjustment_value_changed),
2198 scrolled_window);
2199
2200 gtk_scrolled_window_adjustment_changed (hadjustment, scrolled_window);
2201 gtk_scrolled_window_adjustment_value_changed (hadjustment, scrolled_window);
2202
2203 if (priv->child)
2204 gtk_scrollable_set_hadjustment (GTK_SCROLLABLE (priv->child), hadjustment);
2205
2206 if (gtk_widget_should_animate (GTK_WIDGET (scrolled_window)))
2207 gtk_adjustment_enable_animation (hadjustment, gtk_widget_get_frame_clock (GTK_WIDGET (scrolled_window)), ANIMATION_DURATION);
2208
2209 g_object_notify_by_pspec (G_OBJECT (scrolled_window), properties[PROP_HADJUSTMENT]);
2210 }
2211
2212 /**
2213 * gtk_scrolled_window_set_vadjustment: (attributes org.gtk.Method.set_property=vadjustment)
2214 * @scrolled_window: a `GtkScrolledWindow`
2215 * @vadjustment: (nullable): the `GtkAdjustment` to use, or %NULL to create a new one
2216 *
2217 * Sets the `GtkAdjustment` for the vertical scrollbar.
2218 */
2219 void
gtk_scrolled_window_set_vadjustment(GtkScrolledWindow * scrolled_window,GtkAdjustment * vadjustment)2220 gtk_scrolled_window_set_vadjustment (GtkScrolledWindow *scrolled_window,
2221 GtkAdjustment *vadjustment)
2222 {
2223 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
2224
2225 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
2226
2227 if (vadjustment)
2228 g_return_if_fail (GTK_IS_ADJUSTMENT (vadjustment));
2229 else
2230 vadjustment = (GtkAdjustment*) g_object_new (GTK_TYPE_ADJUSTMENT, NULL);
2231
2232 if (!priv->vscrollbar)
2233 {
2234 priv->vscrollbar = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, vadjustment);
2235
2236 gtk_widget_insert_after (priv->vscrollbar, GTK_WIDGET (scrolled_window), priv->hscrollbar);
2237 update_scrollbar_positions (scrolled_window);
2238 }
2239 else
2240 {
2241 GtkAdjustment *old_adjustment;
2242
2243 old_adjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->vscrollbar));
2244 if (old_adjustment == vadjustment)
2245 return;
2246
2247 g_signal_handlers_disconnect_by_func (old_adjustment,
2248 gtk_scrolled_window_adjustment_changed,
2249 scrolled_window);
2250 g_signal_handlers_disconnect_by_func (old_adjustment,
2251 gtk_scrolled_window_adjustment_value_changed,
2252 scrolled_window);
2253
2254 gtk_adjustment_enable_animation (old_adjustment, NULL, 0);
2255 gtk_scrollbar_set_adjustment (GTK_SCROLLBAR (priv->vscrollbar), vadjustment);
2256 }
2257
2258 vadjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->vscrollbar));
2259
2260 g_signal_connect (vadjustment,
2261 "changed",
2262 G_CALLBACK (gtk_scrolled_window_adjustment_changed),
2263 scrolled_window);
2264 g_signal_connect (vadjustment,
2265 "value-changed",
2266 G_CALLBACK (gtk_scrolled_window_adjustment_value_changed),
2267 scrolled_window);
2268
2269 gtk_scrolled_window_adjustment_changed (vadjustment, scrolled_window);
2270 gtk_scrolled_window_adjustment_value_changed (vadjustment, scrolled_window);
2271
2272 if (priv->child)
2273 gtk_scrollable_set_vadjustment (GTK_SCROLLABLE (priv->child), vadjustment);
2274
2275 if (gtk_widget_should_animate (GTK_WIDGET (scrolled_window)))
2276 gtk_adjustment_enable_animation (vadjustment, gtk_widget_get_frame_clock (GTK_WIDGET (scrolled_window)), ANIMATION_DURATION);
2277
2278 g_object_notify_by_pspec (G_OBJECT (scrolled_window), properties[PROP_VADJUSTMENT]);
2279 }
2280
2281 /**
2282 * gtk_scrolled_window_get_hadjustment: (attributes org.gtk.Method.get_property=hadjustment)
2283 * @scrolled_window: a `GtkScrolledWindow`
2284 *
2285 * Returns the horizontal scrollbar’s adjustment.
2286 *
2287 * This is the adjustment used to connect the horizontal scrollbar
2288 * to the child widget’s horizontal scroll functionality.
2289 *
2290 * Returns: (transfer none): the horizontal `GtkAdjustment`
2291 */
2292 GtkAdjustment*
gtk_scrolled_window_get_hadjustment(GtkScrolledWindow * scrolled_window)2293 gtk_scrolled_window_get_hadjustment (GtkScrolledWindow *scrolled_window)
2294 {
2295 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
2296
2297 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), NULL);
2298
2299 return gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->hscrollbar));
2300 }
2301
2302 /**
2303 * gtk_scrolled_window_get_vadjustment: (attributes org.gtk.Method.get_property=vadjustment)
2304 * @scrolled_window: a `GtkScrolledWindow`
2305 *
2306 * Returns the vertical scrollbar’s adjustment.
2307 *
2308 * This is the adjustment used to connect the vertical
2309 * scrollbar to the child widget’s vertical scroll functionality.
2310 *
2311 * Returns: (transfer none): the vertical `GtkAdjustment`
2312 */
2313 GtkAdjustment*
gtk_scrolled_window_get_vadjustment(GtkScrolledWindow * scrolled_window)2314 gtk_scrolled_window_get_vadjustment (GtkScrolledWindow *scrolled_window)
2315 {
2316 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
2317
2318 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), NULL);
2319
2320 return gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->vscrollbar));
2321 }
2322
2323 /**
2324 * gtk_scrolled_window_get_hscrollbar:
2325 * @scrolled_window: a `GtkScrolledWindow`
2326 *
2327 * Returns the horizontal scrollbar of @scrolled_window.
2328 *
2329 * Returns: (transfer none): the horizontal scrollbar of the scrolled window.
2330 */
2331 GtkWidget*
gtk_scrolled_window_get_hscrollbar(GtkScrolledWindow * scrolled_window)2332 gtk_scrolled_window_get_hscrollbar (GtkScrolledWindow *scrolled_window)
2333 {
2334 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
2335
2336 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), NULL);
2337
2338 return priv->hscrollbar;
2339 }
2340
2341 /**
2342 * gtk_scrolled_window_get_vscrollbar:
2343 * @scrolled_window: a `GtkScrolledWindow`
2344 *
2345 * Returns the vertical scrollbar of @scrolled_window.
2346 *
2347 * Returns: (transfer none): the vertical scrollbar of the scrolled window.
2348 */
2349 GtkWidget*
gtk_scrolled_window_get_vscrollbar(GtkScrolledWindow * scrolled_window)2350 gtk_scrolled_window_get_vscrollbar (GtkScrolledWindow *scrolled_window)
2351 {
2352 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
2353
2354 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), NULL);
2355
2356 return priv->vscrollbar;
2357 }
2358
2359 /**
2360 * gtk_scrolled_window_set_policy:
2361 * @scrolled_window: a `GtkScrolledWindow`
2362 * @hscrollbar_policy: policy for horizontal bar
2363 * @vscrollbar_policy: policy for vertical bar
2364 *
2365 * Sets the scrollbar policy for the horizontal and vertical scrollbars.
2366 *
2367 * The policy determines when the scrollbar should appear; it is a value
2368 * from the [enum@Gtk.PolicyType] enumeration. If %GTK_POLICY_ALWAYS, the
2369 * scrollbar is always present; if %GTK_POLICY_NEVER, the scrollbar is
2370 * never present; if %GTK_POLICY_AUTOMATIC, the scrollbar is present only
2371 * if needed (that is, if the slider part of the bar would be smaller
2372 * than the trough — the display is larger than the page size).
2373 */
2374 void
gtk_scrolled_window_set_policy(GtkScrolledWindow * scrolled_window,GtkPolicyType hscrollbar_policy,GtkPolicyType vscrollbar_policy)2375 gtk_scrolled_window_set_policy (GtkScrolledWindow *scrolled_window,
2376 GtkPolicyType hscrollbar_policy,
2377 GtkPolicyType vscrollbar_policy)
2378 {
2379 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
2380 GObject *object = G_OBJECT (scrolled_window);
2381
2382 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
2383
2384 if ((priv->hscrollbar_policy != hscrollbar_policy) ||
2385 (priv->vscrollbar_policy != vscrollbar_policy))
2386 {
2387 priv->hscrollbar_policy = hscrollbar_policy;
2388 priv->vscrollbar_policy = vscrollbar_policy;
2389
2390 gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
2391
2392 g_object_notify_by_pspec (object, properties[PROP_HSCROLLBAR_POLICY]);
2393 g_object_notify_by_pspec (object, properties[PROP_VSCROLLBAR_POLICY]);
2394 }
2395 }
2396
2397 /**
2398 * gtk_scrolled_window_get_policy:
2399 * @scrolled_window: a `GtkScrolledWindow`
2400 * @hscrollbar_policy: (out) (optional): location to store the policy
2401 * for the horizontal scrollbar
2402 * @vscrollbar_policy: (out) (optional): location to store the policy
2403 * for the vertical scrollbar
2404 *
2405 * Retrieves the current policy values for the horizontal and vertical
2406 * scrollbars.
2407 *
2408 * See [method@Gtk.ScrolledWindow.set_policy].
2409 */
2410 void
gtk_scrolled_window_get_policy(GtkScrolledWindow * scrolled_window,GtkPolicyType * hscrollbar_policy,GtkPolicyType * vscrollbar_policy)2411 gtk_scrolled_window_get_policy (GtkScrolledWindow *scrolled_window,
2412 GtkPolicyType *hscrollbar_policy,
2413 GtkPolicyType *vscrollbar_policy)
2414 {
2415 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
2416
2417 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
2418
2419 if (hscrollbar_policy)
2420 *hscrollbar_policy = priv->hscrollbar_policy;
2421 if (vscrollbar_policy)
2422 *vscrollbar_policy = priv->vscrollbar_policy;
2423 }
2424
2425 /**
2426 * gtk_scrolled_window_set_placement: (attributes org.gtk.Method.set_property=window-placement)
2427 * @scrolled_window: a `GtkScrolledWindow`
2428 * @window_placement: position of the child window
2429 *
2430 * Sets the placement of the contents with respect to the scrollbars
2431 * for the scrolled window.
2432 *
2433 * The default is %GTK_CORNER_TOP_LEFT, meaning the child is
2434 * in the top left, with the scrollbars underneath and to the right.
2435 * Other values in [enum@Gtk.CornerType] are %GTK_CORNER_TOP_RIGHT,
2436 * %GTK_CORNER_BOTTOM_LEFT, and %GTK_CORNER_BOTTOM_RIGHT.
2437 *
2438 * See also [method@Gtk.ScrolledWindow.get_placement] and
2439 * [method@Gtk.ScrolledWindow.unset_placement].
2440 */
2441 void
gtk_scrolled_window_set_placement(GtkScrolledWindow * scrolled_window,GtkCornerType window_placement)2442 gtk_scrolled_window_set_placement (GtkScrolledWindow *scrolled_window,
2443 GtkCornerType window_placement)
2444 {
2445 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
2446
2447 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
2448
2449 if (priv->window_placement != window_placement)
2450 {
2451 priv->window_placement = window_placement;
2452 update_scrollbar_positions (scrolled_window);
2453
2454 gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
2455
2456 g_object_notify_by_pspec (G_OBJECT (scrolled_window), properties[PROP_WINDOW_PLACEMENT]);
2457 }
2458 }
2459
2460 /**
2461 * gtk_scrolled_window_get_placement: (attributes org.gtk.Method.get_property=window-placement)
2462 * @scrolled_window: a `GtkScrolledWindow`
2463 *
2464 * Gets the placement of the contents with respect to the scrollbars.
2465 *
2466 * Returns: the current placement value.
2467 */
2468 GtkCornerType
gtk_scrolled_window_get_placement(GtkScrolledWindow * scrolled_window)2469 gtk_scrolled_window_get_placement (GtkScrolledWindow *scrolled_window)
2470 {
2471 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
2472
2473 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), GTK_CORNER_TOP_LEFT);
2474
2475 return priv->window_placement;
2476 }
2477
2478 /**
2479 * gtk_scrolled_window_unset_placement:
2480 * @scrolled_window: a `GtkScrolledWindow`
2481 *
2482 * Unsets the placement of the contents with respect to the scrollbars.
2483 *
2484 * If no window placement is set for a scrolled window,
2485 * it defaults to %GTK_CORNER_TOP_LEFT.
2486 */
2487 void
gtk_scrolled_window_unset_placement(GtkScrolledWindow * scrolled_window)2488 gtk_scrolled_window_unset_placement (GtkScrolledWindow *scrolled_window)
2489 {
2490 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
2491
2492 gtk_scrolled_window_set_placement (scrolled_window, GTK_CORNER_TOP_LEFT);
2493 }
2494
2495 /**
2496 * gtk_scrolled_window_set_has_frame: (attributes org.gtk.Method.set_property=has-frame)
2497 * @scrolled_window: a `GtkScrolledWindow`
2498 * @has_frame: whether to draw a frame around scrolled window contents
2499 *
2500 * Changes the frame drawn around the contents of @scrolled_window.
2501 */
2502 void
gtk_scrolled_window_set_has_frame(GtkScrolledWindow * scrolled_window,gboolean has_frame)2503 gtk_scrolled_window_set_has_frame (GtkScrolledWindow *scrolled_window,
2504 gboolean has_frame)
2505 {
2506 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
2507
2508 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
2509
2510 if (priv->has_frame == !!has_frame)
2511 return;
2512
2513 priv->has_frame = has_frame;
2514
2515 if (has_frame)
2516 gtk_widget_add_css_class (GTK_WIDGET (scrolled_window), "frame");
2517 else
2518 gtk_widget_remove_css_class (GTK_WIDGET (scrolled_window), "frame");
2519
2520 g_object_notify_by_pspec (G_OBJECT (scrolled_window), properties[PROP_HAS_FRAME]);
2521 }
2522
2523 /**
2524 * gtk_scrolled_window_get_has_frame: (attributes org.gtk.Method.get_property=has-frame)
2525 * @scrolled_window: a `GtkScrolledWindow`
2526 *
2527 * Gets whether the scrolled window draws a frame.
2528 *
2529 * Returns: %TRUE if the @scrolled_window has a frame
2530 */
2531 gboolean
gtk_scrolled_window_get_has_frame(GtkScrolledWindow * scrolled_window)2532 gtk_scrolled_window_get_has_frame (GtkScrolledWindow *scrolled_window)
2533 {
2534 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
2535
2536 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), FALSE);
2537
2538 return priv->has_frame;
2539 }
2540
2541 /**
2542 * gtk_scrolled_window_set_kinetic_scrolling: (attributes org.gtk.Method.set_property=kinetic-scrolling)
2543 * @scrolled_window: a `GtkScrolledWindow`
2544 * @kinetic_scrolling: %TRUE to enable kinetic scrolling
2545 *
2546 * Turns kinetic scrolling on or off.
2547 *
2548 * Kinetic scrolling only applies to devices with source
2549 * %GDK_SOURCE_TOUCHSCREEN.
2550 **/
2551 void
gtk_scrolled_window_set_kinetic_scrolling(GtkScrolledWindow * scrolled_window,gboolean kinetic_scrolling)2552 gtk_scrolled_window_set_kinetic_scrolling (GtkScrolledWindow *scrolled_window,
2553 gboolean kinetic_scrolling)
2554 {
2555 GtkPropagationPhase phase = GTK_PHASE_NONE;
2556 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
2557
2558 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
2559
2560 if (priv->kinetic_scrolling == kinetic_scrolling)
2561 return;
2562
2563 priv->kinetic_scrolling = kinetic_scrolling;
2564 gtk_scrolled_window_check_attach_pan_gesture (scrolled_window);
2565
2566 if (priv->kinetic_scrolling)
2567 phase = GTK_PHASE_CAPTURE;
2568 else
2569 gtk_scrolled_window_cancel_deceleration (scrolled_window);
2570
2571 gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (priv->drag_gesture), phase);
2572 gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (priv->swipe_gesture), phase);
2573 gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (priv->long_press_gesture), phase);
2574 gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (priv->pan_gesture), phase);
2575
2576 g_object_notify_by_pspec (G_OBJECT (scrolled_window), properties[PROP_KINETIC_SCROLLING]);
2577 }
2578
2579 /**
2580 * gtk_scrolled_window_get_kinetic_scrolling: (attributes org.gtk.Method.get_property=kinetic-scrolling)
2581 * @scrolled_window: a `GtkScrolledWindow`
2582 *
2583 * Returns the specified kinetic scrolling behavior.
2584 *
2585 * Returns: the scrolling behavior flags.
2586 */
2587 gboolean
gtk_scrolled_window_get_kinetic_scrolling(GtkScrolledWindow * scrolled_window)2588 gtk_scrolled_window_get_kinetic_scrolling (GtkScrolledWindow *scrolled_window)
2589 {
2590 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
2591
2592 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), FALSE);
2593
2594 return priv->kinetic_scrolling;
2595 }
2596
2597 static void
gtk_scrolled_window_dispose(GObject * object)2598 gtk_scrolled_window_dispose (GObject *object)
2599 {
2600 GtkScrolledWindow *self = GTK_SCROLLED_WINDOW (object);
2601 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (self);
2602
2603 g_clear_pointer (&priv->child, gtk_widget_unparent);
2604
2605 remove_indicator (self, &priv->hindicator);
2606 remove_indicator (self, &priv->vindicator);
2607
2608 if (priv->hscrollbar)
2609 {
2610 GtkAdjustment *hadjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->hscrollbar));
2611
2612 g_signal_handlers_disconnect_by_data (hadjustment, self);
2613 g_signal_handlers_disconnect_by_data (hadjustment, &priv->hindicator);
2614
2615 gtk_widget_unparent (priv->hscrollbar);
2616 priv->hscrollbar = NULL;
2617 }
2618
2619 if (priv->vscrollbar)
2620 {
2621 GtkAdjustment *vadjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->vscrollbar));
2622
2623 g_signal_handlers_disconnect_by_data (vadjustment, self);
2624 g_signal_handlers_disconnect_by_data (vadjustment, &priv->vindicator);
2625
2626 gtk_widget_unparent (priv->vscrollbar);
2627 priv->vscrollbar = NULL;
2628 }
2629
2630 if (priv->deceleration_id)
2631 {
2632 gtk_widget_remove_tick_callback (GTK_WIDGET (self), priv->deceleration_id);
2633 priv->deceleration_id = 0;
2634 }
2635
2636 g_clear_pointer (&priv->hscrolling, gtk_kinetic_scrolling_free);
2637 g_clear_pointer (&priv->vscrolling, gtk_kinetic_scrolling_free);
2638 g_clear_handle_id (&priv->scroll_events_overshoot_id, g_source_remove);
2639
2640 G_OBJECT_CLASS (gtk_scrolled_window_parent_class)->dispose (object);
2641 }
2642
2643 static void
gtk_scrolled_window_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)2644 gtk_scrolled_window_set_property (GObject *object,
2645 guint prop_id,
2646 const GValue *value,
2647 GParamSpec *pspec)
2648 {
2649 GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (object);
2650 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
2651
2652 switch (prop_id)
2653 {
2654 case PROP_HADJUSTMENT:
2655 gtk_scrolled_window_set_hadjustment (scrolled_window,
2656 g_value_get_object (value));
2657 break;
2658 case PROP_VADJUSTMENT:
2659 gtk_scrolled_window_set_vadjustment (scrolled_window,
2660 g_value_get_object (value));
2661 break;
2662 case PROP_HSCROLLBAR_POLICY:
2663 gtk_scrolled_window_set_policy (scrolled_window,
2664 g_value_get_enum (value),
2665 priv->vscrollbar_policy);
2666 break;
2667 case PROP_VSCROLLBAR_POLICY:
2668 gtk_scrolled_window_set_policy (scrolled_window,
2669 priv->hscrollbar_policy,
2670 g_value_get_enum (value));
2671 break;
2672 case PROP_WINDOW_PLACEMENT:
2673 gtk_scrolled_window_set_placement (scrolled_window,
2674 g_value_get_enum (value));
2675 break;
2676 case PROP_HAS_FRAME:
2677 gtk_scrolled_window_set_has_frame (scrolled_window,
2678 g_value_get_boolean (value));
2679 break;
2680 case PROP_MIN_CONTENT_WIDTH:
2681 gtk_scrolled_window_set_min_content_width (scrolled_window,
2682 g_value_get_int (value));
2683 break;
2684 case PROP_MIN_CONTENT_HEIGHT:
2685 gtk_scrolled_window_set_min_content_height (scrolled_window,
2686 g_value_get_int (value));
2687 break;
2688 case PROP_KINETIC_SCROLLING:
2689 gtk_scrolled_window_set_kinetic_scrolling (scrolled_window,
2690 g_value_get_boolean (value));
2691 break;
2692 case PROP_OVERLAY_SCROLLING:
2693 gtk_scrolled_window_set_overlay_scrolling (scrolled_window,
2694 g_value_get_boolean (value));
2695 break;
2696 case PROP_MAX_CONTENT_WIDTH:
2697 gtk_scrolled_window_set_max_content_width (scrolled_window,
2698 g_value_get_int (value));
2699 break;
2700 case PROP_MAX_CONTENT_HEIGHT:
2701 gtk_scrolled_window_set_max_content_height (scrolled_window,
2702 g_value_get_int (value));
2703 break;
2704 case PROP_PROPAGATE_NATURAL_WIDTH:
2705 gtk_scrolled_window_set_propagate_natural_width (scrolled_window,
2706 g_value_get_boolean (value));
2707 break;
2708 case PROP_PROPAGATE_NATURAL_HEIGHT:
2709 gtk_scrolled_window_set_propagate_natural_height (scrolled_window,
2710 g_value_get_boolean (value));
2711 break;
2712 case PROP_CHILD:
2713 gtk_scrolled_window_set_child (scrolled_window, g_value_get_object (value));
2714 break;
2715 default:
2716 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2717 break;
2718 }
2719 }
2720
2721 static void
gtk_scrolled_window_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)2722 gtk_scrolled_window_get_property (GObject *object,
2723 guint prop_id,
2724 GValue *value,
2725 GParamSpec *pspec)
2726 {
2727 GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (object);
2728 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
2729
2730 switch (prop_id)
2731 {
2732 case PROP_HADJUSTMENT:
2733 g_value_set_object (value,
2734 G_OBJECT (gtk_scrolled_window_get_hadjustment (scrolled_window)));
2735 break;
2736 case PROP_VADJUSTMENT:
2737 g_value_set_object (value,
2738 G_OBJECT (gtk_scrolled_window_get_vadjustment (scrolled_window)));
2739 break;
2740 case PROP_WINDOW_PLACEMENT:
2741 g_value_set_enum (value, priv->window_placement);
2742 break;
2743 case PROP_HAS_FRAME:
2744 g_value_set_boolean (value, priv->has_frame);
2745 break;
2746 case PROP_HSCROLLBAR_POLICY:
2747 g_value_set_enum (value, priv->hscrollbar_policy);
2748 break;
2749 case PROP_VSCROLLBAR_POLICY:
2750 g_value_set_enum (value, priv->vscrollbar_policy);
2751 break;
2752 case PROP_MIN_CONTENT_WIDTH:
2753 g_value_set_int (value, priv->min_content_width);
2754 break;
2755 case PROP_MIN_CONTENT_HEIGHT:
2756 g_value_set_int (value, priv->min_content_height);
2757 break;
2758 case PROP_KINETIC_SCROLLING:
2759 g_value_set_boolean (value, priv->kinetic_scrolling);
2760 break;
2761 case PROP_OVERLAY_SCROLLING:
2762 g_value_set_boolean (value, priv->overlay_scrolling);
2763 break;
2764 case PROP_MAX_CONTENT_WIDTH:
2765 g_value_set_int (value, priv->max_content_width);
2766 break;
2767 case PROP_MAX_CONTENT_HEIGHT:
2768 g_value_set_int (value, priv->max_content_height);
2769 break;
2770 case PROP_PROPAGATE_NATURAL_WIDTH:
2771 g_value_set_boolean (value, priv->propagate_natural_width);
2772 break;
2773 case PROP_PROPAGATE_NATURAL_HEIGHT:
2774 g_value_set_boolean (value, priv->propagate_natural_height);
2775 break;
2776 case PROP_CHILD:
2777 g_value_set_object (value, gtk_scrolled_window_get_child (scrolled_window));
2778 break;
2779 default:
2780 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2781 break;
2782 }
2783 }
2784
2785 static void
gtk_scrolled_window_inner_allocation(GtkScrolledWindow * scrolled_window,GtkAllocation * rect)2786 gtk_scrolled_window_inner_allocation (GtkScrolledWindow *scrolled_window,
2787 GtkAllocation *rect)
2788 {
2789 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
2790 GtkBorder border = { 0 };
2791
2792 gtk_scrolled_window_relative_allocation (scrolled_window, rect);
2793 rect->x = 0;
2794 rect->y = 0;
2795 if (priv->child && gtk_scrollable_get_border (GTK_SCROLLABLE (priv->child), &border))
2796 {
2797 rect->x += border.left;
2798 rect->y += border.top;
2799 rect->width -= border.left + border.right;
2800 rect->height -= border.top + border.bottom;
2801 }
2802 }
2803
2804 static void
gtk_scrolled_window_snapshot(GtkWidget * widget,GtkSnapshot * snapshot)2805 gtk_scrolled_window_snapshot (GtkWidget *widget,
2806 GtkSnapshot *snapshot)
2807 {
2808 GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
2809 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
2810
2811 if (priv->hscrollbar_visible &&
2812 priv->vscrollbar_visible &&
2813 !priv->use_indicators)
2814 gtk_scrolled_window_snapshot_scrollbars_junction (scrolled_window, snapshot);
2815
2816 GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->snapshot (widget, snapshot);
2817
2818 gtk_scrolled_window_snapshot_undershoot (scrolled_window, snapshot);
2819 gtk_scrolled_window_snapshot_overshoot (scrolled_window, snapshot);
2820 }
2821
2822 static gboolean
gtk_scrolled_window_scroll_child(GtkScrolledWindow * scrolled_window,GtkScrollType scroll,gboolean horizontal)2823 gtk_scrolled_window_scroll_child (GtkScrolledWindow *scrolled_window,
2824 GtkScrollType scroll,
2825 gboolean horizontal)
2826 {
2827 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
2828 GtkAdjustment *adjustment = NULL;
2829
2830 switch (scroll)
2831 {
2832 case GTK_SCROLL_STEP_UP:
2833 scroll = GTK_SCROLL_STEP_BACKWARD;
2834 horizontal = FALSE;
2835 break;
2836 case GTK_SCROLL_STEP_DOWN:
2837 scroll = GTK_SCROLL_STEP_FORWARD;
2838 horizontal = FALSE;
2839 break;
2840 case GTK_SCROLL_STEP_LEFT:
2841 scroll = GTK_SCROLL_STEP_BACKWARD;
2842 horizontal = TRUE;
2843 break;
2844 case GTK_SCROLL_STEP_RIGHT:
2845 scroll = GTK_SCROLL_STEP_FORWARD;
2846 horizontal = TRUE;
2847 break;
2848 case GTK_SCROLL_PAGE_UP:
2849 scroll = GTK_SCROLL_PAGE_BACKWARD;
2850 horizontal = FALSE;
2851 break;
2852 case GTK_SCROLL_PAGE_DOWN:
2853 scroll = GTK_SCROLL_PAGE_FORWARD;
2854 horizontal = FALSE;
2855 break;
2856 case GTK_SCROLL_PAGE_LEFT:
2857 scroll = GTK_SCROLL_STEP_BACKWARD;
2858 horizontal = TRUE;
2859 break;
2860 case GTK_SCROLL_PAGE_RIGHT:
2861 scroll = GTK_SCROLL_STEP_FORWARD;
2862 horizontal = TRUE;
2863 break;
2864 case GTK_SCROLL_STEP_BACKWARD:
2865 case GTK_SCROLL_STEP_FORWARD:
2866 case GTK_SCROLL_PAGE_BACKWARD:
2867 case GTK_SCROLL_PAGE_FORWARD:
2868 case GTK_SCROLL_START:
2869 case GTK_SCROLL_END:
2870 break;
2871 case GTK_SCROLL_NONE:
2872 case GTK_SCROLL_JUMP:
2873 default:
2874 g_warning ("Invalid scroll type %u for GtkScrolledWindow::scroll-child", scroll);
2875 return FALSE;
2876 }
2877
2878 if (horizontal)
2879 {
2880 if (may_hscroll (scrolled_window))
2881 adjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->hscrollbar));
2882 else
2883 return FALSE;
2884 }
2885 else
2886 {
2887 if (may_vscroll (scrolled_window))
2888 adjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->vscrollbar));
2889 else
2890 return FALSE;
2891 }
2892
2893 if (adjustment)
2894 {
2895 double value = gtk_adjustment_get_value (adjustment);
2896
2897 switch (scroll)
2898 {
2899 case GTK_SCROLL_STEP_FORWARD:
2900 value += gtk_adjustment_get_step_increment (adjustment);
2901 break;
2902 case GTK_SCROLL_STEP_BACKWARD:
2903 value -= gtk_adjustment_get_step_increment (adjustment);
2904 break;
2905 case GTK_SCROLL_PAGE_FORWARD:
2906 value += gtk_adjustment_get_page_increment (adjustment);
2907 break;
2908 case GTK_SCROLL_PAGE_BACKWARD:
2909 value -= gtk_adjustment_get_page_increment (adjustment);
2910 break;
2911 case GTK_SCROLL_START:
2912 value = gtk_adjustment_get_lower (adjustment);
2913 break;
2914 case GTK_SCROLL_END:
2915 value = gtk_adjustment_get_upper (adjustment);
2916 break;
2917 case GTK_SCROLL_STEP_UP:
2918 case GTK_SCROLL_STEP_DOWN:
2919 case GTK_SCROLL_STEP_LEFT:
2920 case GTK_SCROLL_STEP_RIGHT:
2921 case GTK_SCROLL_PAGE_UP:
2922 case GTK_SCROLL_PAGE_DOWN:
2923 case GTK_SCROLL_PAGE_LEFT:
2924 case GTK_SCROLL_PAGE_RIGHT:
2925 case GTK_SCROLL_NONE:
2926 case GTK_SCROLL_JUMP:
2927 default:
2928 g_assert_not_reached ();
2929 break;
2930 }
2931
2932 gtk_adjustment_animate_to_value (adjustment, value);
2933
2934 return TRUE;
2935 }
2936
2937 return FALSE;
2938 }
2939
2940 static void
gtk_scrolled_window_move_focus_out(GtkScrolledWindow * scrolled_window,GtkDirectionType direction_type)2941 gtk_scrolled_window_move_focus_out (GtkScrolledWindow *scrolled_window,
2942 GtkDirectionType direction_type)
2943 {
2944 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
2945 GtkWidget *toplevel;
2946
2947 /* Focus out of the scrolled window entirely. We do this by setting
2948 * a flag, then propagating the focus motion to the notebook.
2949 */
2950 toplevel = GTK_WIDGET (gtk_widget_get_root (GTK_WIDGET (scrolled_window)));
2951 if (!GTK_IS_ROOT (toplevel))
2952 return;
2953
2954 g_object_ref (scrolled_window);
2955
2956 priv->focus_out = TRUE;
2957 g_signal_emit_by_name (toplevel, "move-focus", direction_type);
2958 priv->focus_out = FALSE;
2959
2960 g_object_unref (scrolled_window);
2961 }
2962
2963 static void
gtk_scrolled_window_relative_allocation(GtkScrolledWindow * scrolled_window,GtkAllocation * allocation)2964 gtk_scrolled_window_relative_allocation (GtkScrolledWindow *scrolled_window,
2965 GtkAllocation *allocation)
2966 {
2967 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
2968 int sb_width;
2969 int sb_height;
2970 int width, height;
2971
2972 g_return_if_fail (scrolled_window != NULL);
2973 g_return_if_fail (allocation != NULL);
2974
2975 /* Get possible scrollbar dimensions */
2976 gtk_widget_measure (priv->vscrollbar, GTK_ORIENTATION_HORIZONTAL, -1,
2977 &sb_width, NULL, NULL, NULL);
2978 gtk_widget_measure (priv->hscrollbar, GTK_ORIENTATION_VERTICAL, -1,
2979 &sb_height, NULL, NULL, NULL);
2980
2981 width = gtk_widget_get_width (GTK_WIDGET (scrolled_window));
2982 height = gtk_widget_get_height (GTK_WIDGET (scrolled_window));
2983
2984 allocation->x = 0;
2985 allocation->y = 0;
2986 allocation->width = width;
2987 allocation->height = height;
2988
2989 /* Subtract some things from our available allocation size */
2990 if (priv->vscrollbar_visible && !priv->use_indicators)
2991 {
2992 gboolean is_rtl;
2993
2994 is_rtl = _gtk_widget_get_direction (GTK_WIDGET (scrolled_window)) == GTK_TEXT_DIR_RTL;
2995
2996 if ((!is_rtl &&
2997 (priv->window_placement == GTK_CORNER_TOP_RIGHT ||
2998 priv->window_placement == GTK_CORNER_BOTTOM_RIGHT)) ||
2999 (is_rtl &&
3000 (priv->window_placement == GTK_CORNER_TOP_LEFT ||
3001 priv->window_placement == GTK_CORNER_BOTTOM_LEFT)))
3002 allocation->x += sb_width;
3003
3004 allocation->width = MAX (1, width - sb_width);
3005 }
3006
3007 if (priv->hscrollbar_visible && !priv->use_indicators)
3008 {
3009
3010 if (priv->window_placement == GTK_CORNER_BOTTOM_LEFT ||
3011 priv->window_placement == GTK_CORNER_BOTTOM_RIGHT)
3012 allocation->y += (sb_height);
3013
3014 allocation->height = MAX (1, height - sb_height);
3015 }
3016 }
3017
3018 static gboolean
_gtk_scrolled_window_get_overshoot(GtkScrolledWindow * scrolled_window,int * overshoot_x,int * overshoot_y)3019 _gtk_scrolled_window_get_overshoot (GtkScrolledWindow *scrolled_window,
3020 int *overshoot_x,
3021 int *overshoot_y)
3022 {
3023 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
3024 GtkAdjustment *vadjustment, *hadjustment;
3025 double lower, upper, x, y;
3026
3027 /* Vertical overshoot */
3028 vadjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->vscrollbar));
3029 lower = gtk_adjustment_get_lower (vadjustment);
3030 upper = gtk_adjustment_get_upper (vadjustment) -
3031 gtk_adjustment_get_page_size (vadjustment);
3032
3033 if (priv->unclamped_vadj_value < lower)
3034 y = priv->unclamped_vadj_value - lower;
3035 else if (priv->unclamped_vadj_value > upper)
3036 y = priv->unclamped_vadj_value - upper;
3037 else
3038 y = 0;
3039
3040 /* Horizontal overshoot */
3041 hadjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->hscrollbar));
3042 lower = gtk_adjustment_get_lower (hadjustment);
3043 upper = gtk_adjustment_get_upper (hadjustment) -
3044 gtk_adjustment_get_page_size (hadjustment);
3045
3046 if (priv->unclamped_hadj_value < lower)
3047 x = priv->unclamped_hadj_value - lower;
3048 else if (priv->unclamped_hadj_value > upper)
3049 x = priv->unclamped_hadj_value - upper;
3050 else
3051 x = 0;
3052
3053 if (overshoot_x)
3054 *overshoot_x = x;
3055
3056 if (overshoot_y)
3057 *overshoot_y = y;
3058
3059 return (x != 0 || y != 0);
3060 }
3061
3062 static void
gtk_scrolled_window_allocate_child(GtkScrolledWindow * swindow,int width,int height)3063 gtk_scrolled_window_allocate_child (GtkScrolledWindow *swindow,
3064 int width,
3065 int height)
3066 {
3067 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (swindow);
3068 GtkWidget *widget = GTK_WIDGET (swindow);
3069 GtkAllocation child_allocation;
3070 int sb_width;
3071 int sb_height;
3072
3073 child_allocation = (GtkAllocation) {0, 0, width, height};
3074
3075 /* Get possible scrollbar dimensions */
3076 gtk_widget_measure (priv->vscrollbar, GTK_ORIENTATION_HORIZONTAL, -1,
3077 &sb_width, NULL, NULL, NULL);
3078 gtk_widget_measure (priv->hscrollbar, GTK_ORIENTATION_VERTICAL, -1,
3079 &sb_height, NULL, NULL, NULL);
3080
3081 /* Subtract some things from our available allocation size */
3082 if (priv->vscrollbar_visible && !priv->use_indicators)
3083 {
3084 gboolean is_rtl;
3085
3086 is_rtl = _gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
3087
3088 if ((!is_rtl &&
3089 (priv->window_placement == GTK_CORNER_TOP_RIGHT ||
3090 priv->window_placement == GTK_CORNER_BOTTOM_RIGHT)) ||
3091 (is_rtl &&
3092 (priv->window_placement == GTK_CORNER_TOP_LEFT ||
3093 priv->window_placement == GTK_CORNER_BOTTOM_LEFT)))
3094 child_allocation.x += sb_width;
3095
3096 child_allocation.width = MAX (1, child_allocation.width - sb_width);
3097 }
3098
3099 if (priv->hscrollbar_visible && !priv->use_indicators)
3100 {
3101
3102 if (priv->window_placement == GTK_CORNER_BOTTOM_LEFT ||
3103 priv->window_placement == GTK_CORNER_BOTTOM_RIGHT)
3104 child_allocation.y += (sb_height);
3105
3106 child_allocation.height = MAX (1, child_allocation.height - sb_height);
3107 }
3108
3109 gtk_widget_size_allocate (priv->child, &child_allocation, -1);
3110 }
3111
3112 static void
gtk_scrolled_window_allocate_scrollbar(GtkScrolledWindow * scrolled_window,GtkWidget * scrollbar,GtkAllocation * allocation)3113 gtk_scrolled_window_allocate_scrollbar (GtkScrolledWindow *scrolled_window,
3114 GtkWidget *scrollbar,
3115 GtkAllocation *allocation)
3116 {
3117 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
3118 GtkAllocation child_allocation, content_allocation;
3119 GtkWidget *widget = GTK_WIDGET (scrolled_window);
3120 int sb_height, sb_width;
3121
3122 gtk_scrolled_window_inner_allocation (scrolled_window, &content_allocation);
3123 gtk_widget_measure (priv->vscrollbar, GTK_ORIENTATION_HORIZONTAL, -1,
3124 &sb_width, NULL, NULL, NULL);
3125 gtk_widget_measure (priv->hscrollbar, GTK_ORIENTATION_VERTICAL, -1,
3126 &sb_height, NULL, NULL, NULL);
3127
3128 if (scrollbar == priv->hscrollbar)
3129 {
3130 child_allocation.x = content_allocation.x;
3131
3132 if (priv->window_placement == GTK_CORNER_TOP_LEFT ||
3133 priv->window_placement == GTK_CORNER_TOP_RIGHT)
3134 {
3135 if (priv->use_indicators)
3136 child_allocation.y = content_allocation.y + content_allocation.height - sb_height;
3137 else
3138 child_allocation.y = content_allocation.y + content_allocation.height;
3139 }
3140 else
3141 {
3142 if (priv->use_indicators)
3143 child_allocation.y = content_allocation.y;
3144 else
3145 child_allocation.y = content_allocation.y - sb_height;
3146 }
3147
3148 child_allocation.width = content_allocation.width;
3149 child_allocation.height = sb_height;
3150 }
3151 else
3152 {
3153 g_assert (scrollbar == priv->vscrollbar);
3154
3155 if ((_gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL &&
3156 (priv->window_placement == GTK_CORNER_TOP_RIGHT ||
3157 priv->window_placement == GTK_CORNER_BOTTOM_RIGHT)) ||
3158 (_gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR &&
3159 (priv->window_placement == GTK_CORNER_TOP_LEFT ||
3160 priv->window_placement == GTK_CORNER_BOTTOM_LEFT)))
3161 {
3162 if (priv->use_indicators)
3163 child_allocation.x = content_allocation.x + content_allocation.width - sb_width;
3164 else
3165 child_allocation.x = content_allocation.x + content_allocation.width;
3166 }
3167 else
3168 {
3169 if (priv->use_indicators)
3170 child_allocation.x = content_allocation.x;
3171 else
3172 child_allocation.x = content_allocation.x - sb_width;
3173 }
3174
3175 child_allocation.y = content_allocation.y;
3176 child_allocation.width = sb_width;
3177 child_allocation.height = content_allocation.height;
3178 }
3179
3180 *allocation = child_allocation;
3181 }
3182
3183 static void
_gtk_scrolled_window_set_adjustment_value(GtkScrolledWindow * scrolled_window,GtkAdjustment * adjustment,double value)3184 _gtk_scrolled_window_set_adjustment_value (GtkScrolledWindow *scrolled_window,
3185 GtkAdjustment *adjustment,
3186 double value)
3187 {
3188 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
3189 double lower, upper, *prev_value;
3190 GtkPositionType edge_pos;
3191 gboolean vertical;
3192
3193 lower = gtk_adjustment_get_lower (adjustment) - MAX_OVERSHOOT_DISTANCE;
3194 upper = gtk_adjustment_get_upper (adjustment) -
3195 gtk_adjustment_get_page_size (adjustment) + MAX_OVERSHOOT_DISTANCE;
3196
3197 if (adjustment == gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->hscrollbar)))
3198 vertical = FALSE;
3199 else if (adjustment == gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->vscrollbar)))
3200 vertical = TRUE;
3201 else
3202 return;
3203
3204 if (vertical)
3205 prev_value = &priv->unclamped_vadj_value;
3206 else
3207 prev_value = &priv->unclamped_hadj_value;
3208
3209 value = CLAMP (value, lower, upper);
3210
3211 if (*prev_value == value)
3212 return;
3213
3214 *prev_value = value;
3215 gtk_adjustment_set_value (adjustment, value);
3216
3217 if (value == lower)
3218 edge_pos = vertical ? GTK_POS_TOP : GTK_POS_LEFT;
3219 else if (value == upper)
3220 edge_pos = vertical ? GTK_POS_BOTTOM : GTK_POS_RIGHT;
3221 else
3222 return;
3223
3224 /* Invert horizontal edge position on RTL */
3225 if (!vertical &&
3226 _gtk_widget_get_direction (GTK_WIDGET (scrolled_window)) == GTK_TEXT_DIR_RTL)
3227 edge_pos = (edge_pos == GTK_POS_LEFT) ? GTK_POS_RIGHT : GTK_POS_LEFT;
3228
3229 g_signal_emit (scrolled_window, signals[EDGE_OVERSHOT], 0, edge_pos);
3230 }
3231
3232 static gboolean
scrolled_window_deceleration_cb(GtkWidget * widget,GdkFrameClock * frame_clock,gpointer user_data)3233 scrolled_window_deceleration_cb (GtkWidget *widget,
3234 GdkFrameClock *frame_clock,
3235 gpointer user_data)
3236 {
3237 GtkScrolledWindow *scrolled_window = user_data;
3238 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
3239 GtkAdjustment *hadjustment, *vadjustment;
3240 gint64 current_time;
3241 double position, elapsed;
3242
3243 current_time = gdk_frame_clock_get_frame_time (frame_clock);
3244 elapsed = (current_time - priv->last_deceleration_time) / (double)G_TIME_SPAN_SECOND;
3245 priv->last_deceleration_time = current_time;
3246
3247 hadjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->hscrollbar));
3248 vadjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->vscrollbar));
3249
3250 gtk_scrolled_window_invalidate_overshoot (scrolled_window);
3251
3252 if (priv->hscrolling &&
3253 gtk_kinetic_scrolling_tick (priv->hscrolling, elapsed, &position, NULL))
3254 {
3255 priv->unclamped_hadj_value = position;
3256 gtk_adjustment_set_value (hadjustment, position);
3257 }
3258 else if (priv->hscrolling)
3259 g_clear_pointer (&priv->hscrolling, gtk_kinetic_scrolling_free);
3260
3261 if (priv->vscrolling &&
3262 gtk_kinetic_scrolling_tick (priv->vscrolling, elapsed, &position, NULL))
3263 {
3264 priv->unclamped_vadj_value = position;
3265 gtk_adjustment_set_value (vadjustment, position);
3266 }
3267 else if (priv->vscrolling)
3268 g_clear_pointer (&priv->vscrolling, gtk_kinetic_scrolling_free);
3269
3270 if (!priv->hscrolling && !priv->vscrolling)
3271 {
3272 gtk_scrolled_window_cancel_deceleration (scrolled_window);
3273 return G_SOURCE_REMOVE;
3274 }
3275
3276 gtk_scrolled_window_invalidate_overshoot (scrolled_window);
3277
3278 return G_SOURCE_CONTINUE;
3279 }
3280
3281 static void
gtk_scrolled_window_cancel_deceleration(GtkScrolledWindow * scrolled_window)3282 gtk_scrolled_window_cancel_deceleration (GtkScrolledWindow *scrolled_window)
3283 {
3284 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
3285
3286 if (priv->deceleration_id)
3287 {
3288 gtk_widget_remove_tick_callback (GTK_WIDGET (scrolled_window),
3289 priv->deceleration_id);
3290 priv->deceleration_id = 0;
3291 }
3292 }
3293
3294 static void
kinetic_scroll_stop_notify(GtkScrolledWindow * scrolled_window)3295 kinetic_scroll_stop_notify (GtkScrolledWindow *scrolled_window)
3296 {
3297 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
3298 priv->deceleration_id = 0;
3299 }
3300
3301 static void
gtk_scrolled_window_accumulate_velocity(GtkKineticScrolling ** scrolling,double elapsed,double * velocity)3302 gtk_scrolled_window_accumulate_velocity (GtkKineticScrolling **scrolling, double elapsed, double *velocity)
3303 {
3304 if (!*scrolling)
3305 return;
3306
3307 double last_velocity;
3308 gtk_kinetic_scrolling_tick (*scrolling, elapsed, NULL, &last_velocity);
3309 if (((*velocity >= 0) == (last_velocity >= 0)) &&
3310 (fabs (*velocity) >= fabs (last_velocity) * VELOCITY_ACCUMULATION_FLOOR))
3311 {
3312 double min_velocity = last_velocity * VELOCITY_ACCUMULATION_FLOOR;
3313 double max_velocity = last_velocity * VELOCITY_ACCUMULATION_CEIL;
3314 double accumulation_multiplier = (*velocity - min_velocity) / (max_velocity - min_velocity);
3315 *velocity += last_velocity * fmin (accumulation_multiplier, VELOCITY_ACCUMULATION_MAX);
3316 }
3317 g_clear_pointer (scrolling, gtk_kinetic_scrolling_free);
3318 }
3319
3320 static void
gtk_scrolled_window_start_deceleration(GtkScrolledWindow * scrolled_window)3321 gtk_scrolled_window_start_deceleration (GtkScrolledWindow *scrolled_window)
3322 {
3323 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
3324 GdkFrameClock *frame_clock;
3325 gint64 current_time;
3326 double elapsed;
3327
3328 g_return_if_fail (priv->deceleration_id == 0);
3329
3330 frame_clock = gtk_widget_get_frame_clock (GTK_WIDGET (scrolled_window));
3331
3332 current_time = gdk_frame_clock_get_frame_time (frame_clock);
3333 elapsed = (current_time - priv->last_deceleration_time) / (double)G_TIME_SPAN_SECOND;
3334 priv->last_deceleration_time = current_time;
3335
3336 if (may_hscroll (scrolled_window))
3337 {
3338 double lower,upper;
3339 GtkAdjustment *hadjustment;
3340
3341 gtk_scrolled_window_accumulate_velocity (&priv->hscrolling, elapsed, &priv->x_velocity);
3342
3343 hadjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->hscrollbar));
3344 lower = gtk_adjustment_get_lower (hadjustment);
3345 upper = gtk_adjustment_get_upper (hadjustment);
3346 upper -= gtk_adjustment_get_page_size (hadjustment);
3347 priv->hscrolling =
3348 gtk_kinetic_scrolling_new (lower,
3349 upper,
3350 MAX_OVERSHOOT_DISTANCE,
3351 DECELERATION_FRICTION,
3352 OVERSHOOT_FRICTION,
3353 priv->unclamped_hadj_value,
3354 priv->x_velocity);
3355 }
3356 else
3357 g_clear_pointer (&priv->hscrolling, gtk_kinetic_scrolling_free);
3358
3359 if (may_vscroll (scrolled_window))
3360 {
3361 double lower,upper;
3362 GtkAdjustment *vadjustment;
3363
3364 gtk_scrolled_window_accumulate_velocity (&priv->vscrolling, elapsed, &priv->y_velocity);
3365
3366 vadjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->vscrollbar));
3367 lower = gtk_adjustment_get_lower(vadjustment);
3368 upper = gtk_adjustment_get_upper(vadjustment);
3369 upper -= gtk_adjustment_get_page_size(vadjustment);
3370 priv->vscrolling =
3371 gtk_kinetic_scrolling_new (lower,
3372 upper,
3373 MAX_OVERSHOOT_DISTANCE,
3374 DECELERATION_FRICTION,
3375 OVERSHOOT_FRICTION,
3376 priv->unclamped_vadj_value,
3377 priv->y_velocity);
3378 }
3379 else
3380 g_clear_pointer (&priv->vscrolling, gtk_kinetic_scrolling_free);
3381
3382 priv->deceleration_id = gtk_widget_add_tick_callback (GTK_WIDGET (scrolled_window),
3383 scrolled_window_deceleration_cb, scrolled_window,
3384 (GDestroyNotify) kinetic_scroll_stop_notify);
3385 }
3386
3387 static gboolean
gtk_scrolled_window_focus(GtkWidget * widget,GtkDirectionType direction)3388 gtk_scrolled_window_focus (GtkWidget *widget,
3389 GtkDirectionType direction)
3390 {
3391 GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
3392 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
3393 gboolean had_focus_child;
3394
3395 had_focus_child = gtk_widget_get_focus_child (widget) != NULL;
3396
3397 if (priv->focus_out)
3398 {
3399 priv->focus_out = FALSE; /* Clear this to catch the wrap-around case */
3400 return FALSE;
3401 }
3402
3403 if (gtk_widget_is_focus (widget))
3404 return FALSE;
3405
3406 /* We only put the scrolled window itself in the focus chain if it
3407 * isn't possible to focus any children.
3408 */
3409 if (priv->child)
3410 {
3411 if (gtk_widget_child_focus (priv->child, direction))
3412 return TRUE;
3413 }
3414
3415 if (!had_focus_child && gtk_widget_get_can_focus (widget))
3416 {
3417 gtk_widget_grab_focus (widget);
3418 return TRUE;
3419 }
3420 else
3421 return FALSE;
3422 }
3423
3424 static void
gtk_scrolled_window_adjustment_changed(GtkAdjustment * adjustment,gpointer data)3425 gtk_scrolled_window_adjustment_changed (GtkAdjustment *adjustment,
3426 gpointer data)
3427 {
3428 GtkScrolledWindow *scrolled_window = data;
3429 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
3430
3431 if (adjustment == gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->hscrollbar)))
3432 {
3433 if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
3434 {
3435 gboolean visible;
3436
3437 visible = priv->hscrollbar_visible;
3438 gtk_scrolled_window_update_scrollbar_visibility_flags (scrolled_window, priv->hscrollbar);
3439
3440 if (priv->hscrollbar_visible != visible)
3441 gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
3442
3443 if (priv->hscrolling)
3444 {
3445 GtkKineticScrollingChange change;
3446 double lower = gtk_adjustment_get_lower (adjustment);
3447 double upper = gtk_adjustment_get_upper (adjustment);
3448 upper -= gtk_adjustment_get_page_size (adjustment);
3449
3450 change = gtk_kinetic_scrolling_update_size (priv->hscrolling, lower, upper);
3451
3452 if ((change & GTK_KINETIC_SCROLLING_CHANGE_IN_OVERSHOOT) &&
3453 (change & (GTK_KINETIC_SCROLLING_CHANGE_UPPER | GTK_KINETIC_SCROLLING_CHANGE_LOWER)))
3454 {
3455 g_clear_pointer (&priv->hscrolling, gtk_kinetic_scrolling_free);
3456 priv->unclamped_hadj_value = gtk_adjustment_get_value (adjustment);
3457 gtk_scrolled_window_invalidate_overshoot (scrolled_window);
3458 }
3459 }
3460 }
3461 }
3462 else if (adjustment == gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->vscrollbar)))
3463 {
3464 if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
3465 {
3466 gboolean visible;
3467
3468 visible = priv->vscrollbar_visible;
3469 gtk_scrolled_window_update_scrollbar_visibility_flags (scrolled_window, priv->vscrollbar);
3470
3471 if (priv->vscrollbar_visible != visible)
3472 gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
3473
3474 if (priv->vscrolling)
3475 {
3476 GtkKineticScrollingChange change;
3477 double lower = gtk_adjustment_get_lower (adjustment);
3478 double upper = gtk_adjustment_get_upper (adjustment);
3479 upper -= gtk_adjustment_get_page_size (adjustment);
3480
3481 change = gtk_kinetic_scrolling_update_size (priv->vscrolling, lower, upper);
3482
3483 if ((change & GTK_KINETIC_SCROLLING_CHANGE_IN_OVERSHOOT) &&
3484 (change & (GTK_KINETIC_SCROLLING_CHANGE_UPPER | GTK_KINETIC_SCROLLING_CHANGE_LOWER)))
3485 {
3486 g_clear_pointer (&priv->vscrolling, gtk_kinetic_scrolling_free);
3487 priv->unclamped_vadj_value = gtk_adjustment_get_value (adjustment);
3488 gtk_scrolled_window_invalidate_overshoot (scrolled_window);
3489 }
3490 }
3491 }
3492 }
3493
3494 if (!priv->hscrolling && !priv->vscrolling)
3495 gtk_scrolled_window_cancel_deceleration (scrolled_window);
3496 }
3497
3498 static void
maybe_emit_edge_reached(GtkScrolledWindow * scrolled_window,GtkAdjustment * adjustment)3499 maybe_emit_edge_reached (GtkScrolledWindow *scrolled_window,
3500 GtkAdjustment *adjustment)
3501 {
3502 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
3503 double value, lower, upper, page_size;
3504 GtkPositionType edge_pos;
3505 gboolean vertical;
3506
3507 if (adjustment == gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->hscrollbar)))
3508 vertical = FALSE;
3509 else if (adjustment == gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->vscrollbar)))
3510 vertical = TRUE;
3511 else
3512 return;
3513
3514 value = gtk_adjustment_get_value (adjustment);
3515 lower = gtk_adjustment_get_lower (adjustment);
3516 upper = gtk_adjustment_get_upper (adjustment);
3517 page_size = gtk_adjustment_get_page_size (adjustment);
3518
3519 if (value == lower)
3520 edge_pos = vertical ? GTK_POS_TOP: GTK_POS_LEFT;
3521 else if (value == upper - page_size)
3522 edge_pos = vertical ? GTK_POS_BOTTOM : GTK_POS_RIGHT;
3523 else
3524 return;
3525
3526 if (!vertical &&
3527 _gtk_widget_get_direction (GTK_WIDGET (scrolled_window)) == GTK_TEXT_DIR_RTL)
3528 edge_pos = (edge_pos == GTK_POS_LEFT) ? GTK_POS_RIGHT : GTK_POS_LEFT;
3529
3530 g_signal_emit (scrolled_window, signals[EDGE_REACHED], 0, edge_pos);
3531 }
3532
3533 static void
gtk_scrolled_window_adjustment_value_changed(GtkAdjustment * adjustment,gpointer user_data)3534 gtk_scrolled_window_adjustment_value_changed (GtkAdjustment *adjustment,
3535 gpointer user_data)
3536 {
3537 GtkScrolledWindow *scrolled_window = user_data;
3538 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
3539
3540 maybe_emit_edge_reached (scrolled_window, adjustment);
3541
3542 /* Allow overshooting for kinetic scrolling operations */
3543 if (priv->deceleration_id)
3544 return;
3545
3546 /* Ensure GtkAdjustment and unclamped values are in sync */
3547 if (adjustment == gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->hscrollbar)))
3548 priv->unclamped_hadj_value = gtk_adjustment_get_value (adjustment);
3549 else if (adjustment == gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->vscrollbar)))
3550 priv->unclamped_vadj_value = gtk_adjustment_get_value (adjustment);
3551 }
3552
3553 static gboolean
gtk_widget_should_animate(GtkWidget * widget)3554 gtk_widget_should_animate (GtkWidget *widget)
3555 {
3556 if (!gtk_widget_get_mapped (widget))
3557 return FALSE;
3558
3559 return gtk_settings_get_enable_animations (gtk_widget_get_settings (widget));
3560 }
3561
3562 static void
gtk_scrolled_window_update_animating(GtkScrolledWindow * sw)3563 gtk_scrolled_window_update_animating (GtkScrolledWindow *sw)
3564 {
3565 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (sw);
3566 GtkAdjustment *adjustment;
3567 GdkFrameClock *clock = NULL;
3568 guint duration = 0;
3569
3570 if (gtk_widget_should_animate (GTK_WIDGET (sw)))
3571 {
3572 clock = gtk_widget_get_frame_clock (GTK_WIDGET (sw)),
3573 duration = ANIMATION_DURATION;
3574 }
3575
3576 adjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->hscrollbar));
3577 gtk_adjustment_enable_animation (adjustment, clock, duration);
3578
3579 adjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->vscrollbar));
3580 gtk_adjustment_enable_animation (adjustment, clock, duration);
3581 }
3582
3583 static void
gtk_scrolled_window_map(GtkWidget * widget)3584 gtk_scrolled_window_map (GtkWidget *widget)
3585 {
3586 GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
3587
3588 GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->map (widget);
3589
3590 gtk_scrolled_window_update_animating (scrolled_window);
3591 gtk_scrolled_window_update_use_indicators (scrolled_window);
3592 }
3593
3594 static void
indicator_reset(Indicator * indicator)3595 indicator_reset (Indicator *indicator)
3596 {
3597 g_clear_handle_id (&indicator->conceil_timer, g_source_remove);
3598 g_clear_handle_id (&indicator->over_timeout_id, g_source_remove);
3599
3600 if (indicator->scrollbar && indicator->tick_id)
3601 {
3602 gtk_widget_remove_tick_callback (indicator->scrollbar,
3603 indicator->tick_id);
3604 indicator->tick_id = 0;
3605 }
3606
3607 indicator->over = FALSE;
3608 gtk_progress_tracker_finish (&indicator->tracker);
3609 indicator->current_pos = indicator->source_pos = indicator->target_pos = 0;
3610 indicator->last_scroll_time = 0;
3611 }
3612
3613 static void
gtk_scrolled_window_unmap(GtkWidget * widget)3614 gtk_scrolled_window_unmap (GtkWidget *widget)
3615 {
3616 GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
3617 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
3618
3619 GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->unmap (widget);
3620
3621 gtk_scrolled_window_update_animating (scrolled_window);
3622
3623 indicator_reset (&priv->hindicator);
3624 indicator_reset (&priv->vindicator);
3625 }
3626
3627 static void
indicator_set_fade(Indicator * indicator,double pos)3628 indicator_set_fade (Indicator *indicator,
3629 double pos)
3630 {
3631 gboolean visible, changed;
3632
3633 changed = indicator->current_pos != pos;
3634 indicator->current_pos = pos;
3635
3636 visible = indicator->current_pos != 0.0 || indicator->target_pos != 0.0;
3637
3638 if (visible && indicator->conceil_timer == 0)
3639 {
3640 indicator->conceil_timer = g_timeout_add (INDICATOR_FADE_OUT_TIME, maybe_hide_indicator, indicator);
3641 gdk_source_set_static_name_by_id (indicator->conceil_timer, "[gtk] maybe_hide_indicator");
3642 }
3643 if (!visible && indicator->conceil_timer != 0)
3644 {
3645 g_source_remove (indicator->conceil_timer);
3646 indicator->conceil_timer = 0;
3647 }
3648
3649 if (changed)
3650 {
3651 gtk_widget_set_opacity (indicator->scrollbar, indicator->current_pos);
3652 }
3653 }
3654
3655 static gboolean
indicator_fade_cb(GtkWidget * widget,GdkFrameClock * frame_clock,gpointer user_data)3656 indicator_fade_cb (GtkWidget *widget,
3657 GdkFrameClock *frame_clock,
3658 gpointer user_data)
3659 {
3660 Indicator *indicator = user_data;
3661 double t;
3662
3663 gtk_progress_tracker_advance_frame (&indicator->tracker,
3664 gdk_frame_clock_get_frame_time (frame_clock));
3665 t = gtk_progress_tracker_get_ease_out_cubic (&indicator->tracker, FALSE);
3666
3667 indicator_set_fade (indicator,
3668 indicator->source_pos + (t * (indicator->target_pos - indicator->source_pos)));
3669
3670 if (gtk_progress_tracker_get_state (&indicator->tracker) == GTK_PROGRESS_STATE_AFTER)
3671 {
3672 indicator->tick_id = 0;
3673 return FALSE;
3674 }
3675
3676 return TRUE;
3677 }
3678
3679 static void
indicator_start_fade(Indicator * indicator,double target)3680 indicator_start_fade (Indicator *indicator,
3681 double target)
3682 {
3683 if (indicator->target_pos == target)
3684 return;
3685
3686 indicator->target_pos = target;
3687
3688 if (target != 0.0)
3689 indicator->last_scroll_time = g_get_monotonic_time ();
3690
3691 if (gtk_widget_should_animate (indicator->scrollbar))
3692 {
3693 indicator->source_pos = indicator->current_pos;
3694 gtk_progress_tracker_start (&indicator->tracker, INDICATOR_FADE_OUT_DURATION * 1000, 0, 1.0);
3695 if (indicator->tick_id == 0)
3696 indicator->tick_id = gtk_widget_add_tick_callback (indicator->scrollbar, indicator_fade_cb, indicator, NULL);
3697 }
3698 else
3699 indicator_set_fade (indicator, target);
3700 }
3701
3702 static gboolean
maybe_hide_indicator(gpointer data)3703 maybe_hide_indicator (gpointer data)
3704 {
3705 Indicator *indicator = data;
3706
3707 if (g_get_monotonic_time () - indicator->last_scroll_time >= INDICATOR_FADE_OUT_DELAY * 1000 &&
3708 !indicator->over)
3709 indicator_start_fade (indicator, 0.0);
3710
3711 return G_SOURCE_CONTINUE;
3712 }
3713
3714 static void
indicator_value_changed(GtkAdjustment * adjustment,Indicator * indicator)3715 indicator_value_changed (GtkAdjustment *adjustment,
3716 Indicator *indicator)
3717 {
3718 indicator->last_scroll_time = g_get_monotonic_time ();
3719 indicator_start_fade (indicator, 1.0);
3720 }
3721
3722 static void
setup_indicator(GtkScrolledWindow * scrolled_window,Indicator * indicator,GtkWidget * scrollbar)3723 setup_indicator (GtkScrolledWindow *scrolled_window,
3724 Indicator *indicator,
3725 GtkWidget *scrollbar)
3726 {
3727 GtkAdjustment *adjustment;
3728
3729 if (scrollbar == NULL)
3730 return;
3731
3732 adjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (scrollbar));
3733
3734 indicator->scrollbar = scrollbar;
3735
3736 gtk_widget_add_css_class (scrollbar, "overlay-indicator");
3737 g_signal_connect (adjustment, "value-changed",
3738 G_CALLBACK (indicator_value_changed), indicator);
3739
3740 gtk_widget_set_opacity (scrollbar, 0.0);
3741 indicator->current_pos = 0.0;
3742 }
3743
3744 static void
remove_indicator(GtkScrolledWindow * scrolled_window,Indicator * indicator)3745 remove_indicator (GtkScrolledWindow *scrolled_window,
3746 Indicator *indicator)
3747 {
3748 GtkWidget *scrollbar;
3749 GtkAdjustment *adjustment;
3750
3751 if (indicator->scrollbar == NULL)
3752 return;
3753
3754 scrollbar = indicator->scrollbar;
3755 indicator->scrollbar = NULL;
3756
3757 gtk_widget_remove_css_class (scrollbar, "overlay-indicator");
3758
3759 adjustment = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (scrollbar));
3760 g_signal_handlers_disconnect_by_data (adjustment, indicator);
3761
3762 if (indicator->conceil_timer)
3763 {
3764 g_source_remove (indicator->conceil_timer);
3765 indicator->conceil_timer = 0;
3766 }
3767
3768 if (indicator->over_timeout_id)
3769 {
3770 g_source_remove (indicator->over_timeout_id);
3771 indicator->over_timeout_id = 0;
3772 }
3773
3774 if (indicator->tick_id)
3775 {
3776 gtk_widget_remove_tick_callback (scrollbar, indicator->tick_id);
3777 indicator->tick_id = 0;
3778 }
3779
3780 gtk_widget_set_opacity (scrollbar, 1.0);
3781 indicator->current_pos = 1.0;
3782 }
3783
3784 static void
gtk_scrolled_window_sync_use_indicators(GtkScrolledWindow * scrolled_window)3785 gtk_scrolled_window_sync_use_indicators (GtkScrolledWindow *scrolled_window)
3786 {
3787 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
3788
3789 if (priv->use_indicators)
3790 {
3791 setup_indicator (scrolled_window, &priv->hindicator, priv->hscrollbar);
3792 setup_indicator (scrolled_window, &priv->vindicator, priv->vscrollbar);
3793 }
3794 else
3795 {
3796 remove_indicator (scrolled_window, &priv->hindicator);
3797 remove_indicator (scrolled_window, &priv->vindicator);
3798 }
3799 }
3800
3801 static void
gtk_scrolled_window_update_use_indicators(GtkScrolledWindow * scrolled_window)3802 gtk_scrolled_window_update_use_indicators (GtkScrolledWindow *scrolled_window)
3803 {
3804 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
3805 gboolean use_indicators;
3806 GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (scrolled_window));
3807 gboolean overlay_scrolling;
3808
3809 g_object_get (settings, "gtk-overlay-scrolling", &overlay_scrolling, NULL);
3810
3811 use_indicators = overlay_scrolling && priv->overlay_scrolling;
3812
3813 if (priv->use_indicators != use_indicators)
3814 {
3815 priv->use_indicators = use_indicators;
3816
3817 if (gtk_widget_get_realized (GTK_WIDGET (scrolled_window)))
3818 gtk_scrolled_window_sync_use_indicators (scrolled_window);
3819
3820 gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
3821 }
3822 }
3823
3824 static void
gtk_scrolled_window_realize(GtkWidget * widget)3825 gtk_scrolled_window_realize (GtkWidget *widget)
3826 {
3827 GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
3828 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
3829
3830 priv->hindicator.scrollbar = priv->hscrollbar;
3831 priv->vindicator.scrollbar = priv->vscrollbar;
3832
3833 gtk_scrolled_window_sync_use_indicators (scrolled_window);
3834
3835 GTK_WIDGET_CLASS (gtk_scrolled_window_parent_class)->realize (widget);
3836 }
3837
3838 /**
3839 * gtk_scrolled_window_get_min_content_width: (attributes org.gtk.Method.get_property=min-content-width)
3840 * @scrolled_window: a `GtkScrolledWindow`
3841 *
3842 * Gets the minimum content width of @scrolled_window.
3843 *
3844 * Returns: the minimum content width
3845 */
3846 int
gtk_scrolled_window_get_min_content_width(GtkScrolledWindow * scrolled_window)3847 gtk_scrolled_window_get_min_content_width (GtkScrolledWindow *scrolled_window)
3848 {
3849 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
3850
3851 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), 0);
3852
3853 return priv->min_content_width;
3854 }
3855
3856 /**
3857 * gtk_scrolled_window_set_min_content_width: (attributes org.gtk.Method.set_property=min-content-width)
3858 * @scrolled_window: a `GtkScrolledWindow`
3859 * @width: the minimal content width
3860 *
3861 * Sets the minimum width that @scrolled_window should keep visible.
3862 *
3863 * Note that this can and (usually will) be smaller than the minimum
3864 * size of the content.
3865 *
3866 * It is a programming error to set the minimum content width to a
3867 * value greater than [property@Gtk.ScrolledWindow:max-content-width].
3868 */
3869 void
gtk_scrolled_window_set_min_content_width(GtkScrolledWindow * scrolled_window,int width)3870 gtk_scrolled_window_set_min_content_width (GtkScrolledWindow *scrolled_window,
3871 int width)
3872 {
3873 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
3874
3875 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
3876
3877 g_return_if_fail (width == -1 || priv->max_content_width == -1 || width <= priv->max_content_width);
3878
3879 if (priv->min_content_width != width)
3880 {
3881 priv->min_content_width = width;
3882
3883 gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
3884
3885 g_object_notify_by_pspec (G_OBJECT (scrolled_window), properties[PROP_MIN_CONTENT_WIDTH]);
3886 }
3887 }
3888
3889 /**
3890 * gtk_scrolled_window_get_min_content_height: (attributes org.gtk.Method.get_property=min-content-height)
3891 * @scrolled_window: a `GtkScrolledWindow`
3892 *
3893 * Gets the minimal content height of @scrolled_window.
3894 *
3895 * Returns: the minimal content height
3896 */
3897 int
gtk_scrolled_window_get_min_content_height(GtkScrolledWindow * scrolled_window)3898 gtk_scrolled_window_get_min_content_height (GtkScrolledWindow *scrolled_window)
3899 {
3900 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
3901
3902 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), 0);
3903
3904 return priv->min_content_height;
3905 }
3906
3907 /**
3908 * gtk_scrolled_window_set_min_content_height: (attributes org.gtk.Method.set_property=min-content-height)
3909 * @scrolled_window: a `GtkScrolledWindow`
3910 * @height: the minimal content height
3911 *
3912 * Sets the minimum height that @scrolled_window should keep visible.
3913 *
3914 * Note that this can and (usually will) be smaller than the minimum
3915 * size of the content.
3916 *
3917 * It is a programming error to set the minimum content height to a
3918 * value greater than [property@Gtk.ScrolledWindow:max-content-height].
3919 */
3920 void
gtk_scrolled_window_set_min_content_height(GtkScrolledWindow * scrolled_window,int height)3921 gtk_scrolled_window_set_min_content_height (GtkScrolledWindow *scrolled_window,
3922 int height)
3923 {
3924 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
3925
3926 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
3927
3928 g_return_if_fail (height == -1 || priv->max_content_height == -1 || height <= priv->max_content_height);
3929
3930 if (priv->min_content_height != height)
3931 {
3932 priv->min_content_height = height;
3933
3934 gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
3935
3936 g_object_notify_by_pspec (G_OBJECT (scrolled_window), properties[PROP_MIN_CONTENT_HEIGHT]);
3937 }
3938 }
3939
3940 /**
3941 * gtk_scrolled_window_set_overlay_scrolling: (attributes org.gtk.Method.set_property=overlay-scrolling)
3942 * @scrolled_window: a `GtkScrolledWindow`
3943 * @overlay_scrolling: whether to enable overlay scrolling
3944 *
3945 * Enables or disables overlay scrolling for this scrolled window.
3946 */
3947 void
gtk_scrolled_window_set_overlay_scrolling(GtkScrolledWindow * scrolled_window,gboolean overlay_scrolling)3948 gtk_scrolled_window_set_overlay_scrolling (GtkScrolledWindow *scrolled_window,
3949 gboolean overlay_scrolling)
3950 {
3951 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
3952
3953 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
3954
3955 if (priv->overlay_scrolling != overlay_scrolling)
3956 {
3957 priv->overlay_scrolling = overlay_scrolling;
3958
3959 gtk_scrolled_window_update_use_indicators (scrolled_window);
3960
3961 g_object_notify_by_pspec (G_OBJECT (scrolled_window), properties[PROP_OVERLAY_SCROLLING]);
3962 }
3963 }
3964
3965 /**
3966 * gtk_scrolled_window_get_overlay_scrolling: (attributes org.gtk.Method.get_property=overlay-scrolling)
3967 * @scrolled_window: a `GtkScrolledWindow`
3968 *
3969 * Returns whether overlay scrolling is enabled for this scrolled window.
3970 *
3971 * Returns: %TRUE if overlay scrolling is enabled
3972 */
3973 gboolean
gtk_scrolled_window_get_overlay_scrolling(GtkScrolledWindow * scrolled_window)3974 gtk_scrolled_window_get_overlay_scrolling (GtkScrolledWindow *scrolled_window)
3975 {
3976 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
3977
3978 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), TRUE);
3979
3980 return priv->overlay_scrolling;
3981 }
3982
3983 /**
3984 * gtk_scrolled_window_set_max_content_width: (attributes org.gtk.Method.set_property=max-content-width)
3985 * @scrolled_window: a `GtkScrolledWindow`
3986 * @width: the maximum content width
3987 *
3988 * Sets the maximum width that @scrolled_window should keep visible.
3989 *
3990 * The @scrolled_window will grow up to this width before it starts
3991 * scrolling the content.
3992 *
3993 * It is a programming error to set the maximum content width to a
3994 * value smaller than [property@Gtk.ScrolledWindow:min-content-width].
3995 */
3996 void
gtk_scrolled_window_set_max_content_width(GtkScrolledWindow * scrolled_window,int width)3997 gtk_scrolled_window_set_max_content_width (GtkScrolledWindow *scrolled_window,
3998 int width)
3999 {
4000 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
4001
4002 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
4003
4004 g_return_if_fail (width == -1 || priv->min_content_width == -1 || width >= priv->min_content_width);
4005
4006 if (width != priv->max_content_width)
4007 {
4008 priv->max_content_width = width;
4009 g_object_notify_by_pspec (G_OBJECT (scrolled_window), properties [PROP_MAX_CONTENT_WIDTH]);
4010 gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
4011 }
4012 }
4013
4014 /**
4015 * gtk_scrolled_window_get_max_content_width: (attributes org.gtk.Method.get_property=max-content-width)
4016 * @scrolled_window: a `GtkScrolledWindow`
4017 *
4018 * Returns the maximum content width set.
4019 *
4020 * Returns: the maximum content width, or -1
4021 */
4022 int
gtk_scrolled_window_get_max_content_width(GtkScrolledWindow * scrolled_window)4023 gtk_scrolled_window_get_max_content_width (GtkScrolledWindow *scrolled_window)
4024 {
4025 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
4026
4027 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), -1);
4028
4029 return priv->max_content_width;
4030 }
4031
4032 /**
4033 * gtk_scrolled_window_set_max_content_height: (attributes org.gtk.Method.set_property=max-content-height)
4034 * @scrolled_window: a `GtkScrolledWindow`
4035 * @height: the maximum content height
4036 *
4037 * Sets the maximum height that @scrolled_window should keep visible.
4038 *
4039 * The @scrolled_window will grow up to this height before it starts
4040 * scrolling the content.
4041 *
4042 * It is a programming error to set the maximum content height to a value
4043 * smaller than [property@Gtk.ScrolledWindow:min-content-height].
4044 */
4045 void
gtk_scrolled_window_set_max_content_height(GtkScrolledWindow * scrolled_window,int height)4046 gtk_scrolled_window_set_max_content_height (GtkScrolledWindow *scrolled_window,
4047 int height)
4048 {
4049 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
4050
4051 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
4052
4053 g_return_if_fail (height == -1 || priv->min_content_height == -1 || height >= priv->min_content_height);
4054
4055 if (height != priv->max_content_height)
4056 {
4057 priv->max_content_height = height;
4058 g_object_notify_by_pspec (G_OBJECT (scrolled_window), properties [PROP_MAX_CONTENT_HEIGHT]);
4059 gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
4060 }
4061 }
4062
4063 /**
4064 * gtk_scrolled_window_get_max_content_height: (attributes org.gtk.Method.get_property=max-content-height)
4065 * @scrolled_window: a `GtkScrolledWindow`
4066 *
4067 * Returns the maximum content height set.
4068 *
4069 * Returns: the maximum content height, or -1
4070 */
4071 int
gtk_scrolled_window_get_max_content_height(GtkScrolledWindow * scrolled_window)4072 gtk_scrolled_window_get_max_content_height (GtkScrolledWindow *scrolled_window)
4073 {
4074 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
4075
4076 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), -1);
4077
4078 return priv->max_content_height;
4079 }
4080
4081 /**
4082 * gtk_scrolled_window_set_propagate_natural_width: (attributes org.gtk.Method.set_property=propagate-natural-width)
4083 * @scrolled_window: a `GtkScrolledWindow`
4084 * @propagate: whether to propagate natural width
4085 *
4086 * Sets whether the natural width of the child should be calculated
4087 * and propagated through the scrolled window’s requested natural width.
4088 */
4089 void
gtk_scrolled_window_set_propagate_natural_width(GtkScrolledWindow * scrolled_window,gboolean propagate)4090 gtk_scrolled_window_set_propagate_natural_width (GtkScrolledWindow *scrolled_window,
4091 gboolean propagate)
4092 {
4093 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
4094
4095 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
4096
4097 propagate = !!propagate;
4098
4099 if (priv->propagate_natural_width != propagate)
4100 {
4101 priv->propagate_natural_width = propagate;
4102 g_object_notify_by_pspec (G_OBJECT (scrolled_window), properties [PROP_PROPAGATE_NATURAL_WIDTH]);
4103 gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
4104 }
4105 }
4106
4107 /**
4108 * gtk_scrolled_window_get_propagate_natural_width: (attributes org.gtk.Method.get_property=propagate-natural-width)
4109 * @scrolled_window: a `GtkScrolledWindow`
4110 *
4111 * Reports whether the natural width of the child will be calculated
4112 * and propagated through the scrolled window’s requested natural width.
4113 *
4114 * Returns: whether natural width propagation is enabled.
4115 */
4116 gboolean
gtk_scrolled_window_get_propagate_natural_width(GtkScrolledWindow * scrolled_window)4117 gtk_scrolled_window_get_propagate_natural_width (GtkScrolledWindow *scrolled_window)
4118 {
4119 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
4120
4121 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), -1);
4122
4123 return priv->propagate_natural_width;
4124 }
4125
4126 /**
4127 * gtk_scrolled_window_set_propagate_natural_height: (attributes org.gtk.Method.set_property=propagate-natural-height)
4128 * @scrolled_window: a `GtkScrolledWindow`
4129 * @propagate: whether to propagate natural height
4130 *
4131 * Sets whether the natural height of the child should be calculated
4132 * and propagated through the scrolled window’s requested natural height.
4133 */
4134 void
gtk_scrolled_window_set_propagate_natural_height(GtkScrolledWindow * scrolled_window,gboolean propagate)4135 gtk_scrolled_window_set_propagate_natural_height (GtkScrolledWindow *scrolled_window,
4136 gboolean propagate)
4137 {
4138 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
4139
4140 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
4141
4142 propagate = !!propagate;
4143
4144 if (priv->propagate_natural_height != propagate)
4145 {
4146 priv->propagate_natural_height = propagate;
4147 g_object_notify_by_pspec (G_OBJECT (scrolled_window), properties [PROP_PROPAGATE_NATURAL_HEIGHT]);
4148 gtk_widget_queue_resize (GTK_WIDGET (scrolled_window));
4149 }
4150 }
4151
4152 /**
4153 * gtk_scrolled_window_get_propagate_natural_height: (attributes org.gtk.Method.get_property=propagate-natural-height)
4154 * @scrolled_window: a `GtkScrolledWindow`
4155 *
4156 * Reports whether the natural height of the child will be calculated
4157 * and propagated through the scrolled window’s requested natural height.
4158 *
4159 * Returns: whether natural height propagation is enabled.
4160 */
4161 gboolean
gtk_scrolled_window_get_propagate_natural_height(GtkScrolledWindow * scrolled_window)4162 gtk_scrolled_window_get_propagate_natural_height (GtkScrolledWindow *scrolled_window)
4163 {
4164 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
4165
4166 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), -1);
4167
4168 return priv->propagate_natural_height;
4169 }
4170
4171 /**
4172 * gtk_scrolled_window_set_child: (attributes org.gtk.Method.set_property=child)
4173 * @scrolled_window: a `GtkScrolledWindow`
4174 * @child: (nullable): the child widget
4175 *
4176 * Sets the child widget of @scrolled_window.
4177 */
4178 void
gtk_scrolled_window_set_child(GtkScrolledWindow * scrolled_window,GtkWidget * child)4179 gtk_scrolled_window_set_child (GtkScrolledWindow *scrolled_window,
4180 GtkWidget *child)
4181 {
4182 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
4183 GtkWidget *scrollable_child;
4184
4185 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window));
4186
4187 if (priv->child)
4188 {
4189 if (priv->auto_added_viewport)
4190 {
4191 gtk_viewport_set_child (GTK_VIEWPORT (priv->child), NULL);
4192 }
4193
4194 g_object_set (priv->child,
4195 "hadjustment", NULL,
4196 "vadjustment", NULL,
4197 NULL);
4198
4199 g_clear_pointer (&priv->child, gtk_widget_unparent);
4200 }
4201
4202 if (child)
4203 {
4204 GtkAdjustment *hadj, *vadj;
4205
4206 /* gtk_scrolled_window_set_[hv]adjustment have the side-effect
4207 * of creating the scrollbars
4208 */
4209 if (!priv->hscrollbar)
4210 gtk_scrolled_window_set_hadjustment (scrolled_window, NULL);
4211
4212 if (!priv->vscrollbar)
4213 gtk_scrolled_window_set_vadjustment (scrolled_window, NULL);
4214
4215 hadj = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->hscrollbar));
4216 vadj = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->vscrollbar));
4217
4218 if (GTK_IS_SCROLLABLE (child))
4219 {
4220 scrollable_child = child;
4221 priv->auto_added_viewport = FALSE;
4222 }
4223 else
4224 {
4225 scrollable_child = gtk_viewport_new (hadj, vadj);
4226 gtk_viewport_set_child (GTK_VIEWPORT (scrollable_child), child);
4227 priv->auto_added_viewport = TRUE;
4228 }
4229
4230 priv->child = scrollable_child;
4231 gtk_widget_insert_after (scrollable_child, GTK_WIDGET (scrolled_window), NULL);
4232
4233 g_object_set (scrollable_child,
4234 "hadjustment", hadj,
4235 "vadjustment", vadj,
4236 NULL);
4237 }
4238
4239 if (priv->child)
4240 {
4241 gtk_accessible_update_relation (GTK_ACCESSIBLE (priv->hscrollbar),
4242 GTK_ACCESSIBLE_RELATION_CONTROLS, priv->child, NULL,
4243 -1);
4244 gtk_accessible_update_relation (GTK_ACCESSIBLE (priv->vscrollbar),
4245 GTK_ACCESSIBLE_RELATION_CONTROLS, priv->child, NULL,
4246 -1);
4247 }
4248 else
4249 {
4250 gtk_accessible_reset_relation (GTK_ACCESSIBLE (priv->hscrollbar),
4251 GTK_ACCESSIBLE_RELATION_CONTROLS);
4252 gtk_accessible_reset_relation (GTK_ACCESSIBLE (priv->vscrollbar),
4253 GTK_ACCESSIBLE_RELATION_CONTROLS);
4254 }
4255
4256 g_object_notify_by_pspec (G_OBJECT (scrolled_window), properties[PROP_CHILD]);
4257 }
4258
4259 /**
4260 * gtk_scrolled_window_get_child: (attributes org.gtk.Method.get_property=child)
4261 * @scrolled_window: a `GtkScrolledWindow`
4262 *
4263 * Gets the child widget of @scrolled_window.
4264 *
4265 * Returns: (nullable) (transfer none): the child widget of @scrolled_window
4266 */
4267 GtkWidget *
gtk_scrolled_window_get_child(GtkScrolledWindow * scrolled_window)4268 gtk_scrolled_window_get_child (GtkScrolledWindow *scrolled_window)
4269 {
4270 GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (scrolled_window);
4271
4272 g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (scrolled_window), NULL);
4273
4274 return priv->child;
4275 }
4276