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