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

source code of gtk/gtk/gtkscrolledwindow.c