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