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