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 "gtkviewport.h"
28
29#include "gtkadjustmentprivate.h"
30#include "gtkintl.h"
31#include "gtkmarshalers.h"
32#include "gtkprivate.h"
33#include "gtkscrollable.h"
34#include "gtktypebuiltins.h"
35#include "gtkwidgetprivate.h"
36#include "gtkbuildable.h"
37#include "gtktext.h"
38
39
40/**
41 * GtkViewport:
42 *
43 * `GtkViewport` implements scrollability for widgets that lack their
44 * own scrolling capabilities.
45 *
46 * Use `GtkViewport` to scroll child widgets such as `GtkGrid`,
47 * `GtkBox`, and so on.
48 *
49 * The `GtkViewport` will start scrolling content only if allocated
50 * less than the child widget’s minimum size in a given orientation.
51 *
52 * # CSS nodes
53 *
54 * `GtkViewport` has a single CSS node with name `viewport`.
55 *
56 * # Accessibility
57 *
58 * `GtkViewport` uses the %GTK_ACCESSIBLE_ROLE_GROUP role.
59 */
60
61typedef struct _GtkViewportPrivate GtkViewportPrivate;
62typedef struct _GtkViewportClass GtkViewportClass;
63
64struct _GtkViewport
65{
66 GtkWidget parent_instance;
67
68 GtkWidget *child;
69
70 GtkAdjustment *adjustment[2];
71 GtkScrollablePolicy scroll_policy[2];
72 guint scroll_to_focus : 1;
73
74 gulong focus_handler;
75};
76
77struct _GtkViewportClass
78{
79 GtkWidgetClass parent_class;
80};
81
82enum {
83 PROP_0,
84 PROP_HADJUSTMENT,
85 PROP_VADJUSTMENT,
86 PROP_HSCROLL_POLICY,
87 PROP_VSCROLL_POLICY,
88 PROP_SCROLL_TO_FOCUS,
89 PROP_CHILD
90};
91
92
93static void gtk_viewport_set_property (GObject *object,
94 guint prop_id,
95 const GValue *value,
96 GParamSpec *pspec);
97static void gtk_viewport_get_property (GObject *object,
98 guint prop_id,
99 GValue *value,
100 GParamSpec *pspec);
101static void gtk_viewport_dispose (GObject *object);
102static void gtk_viewport_size_allocate (GtkWidget *widget,
103 int width,
104 int height,
105 int baseline);
106
107static void gtk_viewport_adjustment_value_changed (GtkAdjustment *adjustment,
108 gpointer data);
109static void viewport_set_adjustment (GtkViewport *viewport,
110 GtkOrientation orientation,
111 GtkAdjustment *adjustment);
112
113static void setup_focus_change_handler (GtkViewport *viewport);
114static void clear_focus_change_handler (GtkViewport *viewport);
115
116static void gtk_viewport_buildable_init (GtkBuildableIface *iface);
117
118
119G_DEFINE_TYPE_WITH_CODE (GtkViewport, gtk_viewport, GTK_TYPE_WIDGET,
120 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
121 gtk_viewport_buildable_init)
122 G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL))
123
124static GtkBuildableIface *parent_buildable_iface;
125
126static void
127gtk_viewport_buildable_add_child (GtkBuildable *buildable,
128 GtkBuilder *builder,
129 GObject *child,
130 const char *type)
131{
132 if (GTK_IS_WIDGET (child))
133 gtk_viewport_set_child (GTK_VIEWPORT (buildable), GTK_WIDGET (child));
134 else
135 parent_buildable_iface->add_child (buildable, builder, child, type);
136}
137
138static void
139gtk_viewport_buildable_init (GtkBuildableIface *iface)
140{
141 parent_buildable_iface = g_type_interface_peek_parent (g_iface: iface);
142
143 iface->add_child = gtk_viewport_buildable_add_child;
144}
145
146static void
147viewport_set_adjustment_values (GtkViewport *viewport,
148 GtkOrientation orientation,
149 int viewport_size,
150 int child_size)
151{
152 GtkAdjustment *adjustment;
153 double upper, value;
154
155 adjustment = viewport->adjustment[orientation];
156 upper = child_size;
157 value = gtk_adjustment_get_value (adjustment);
158
159 /* We clamp to the left in RTL mode */
160 if (orientation == GTK_ORIENTATION_HORIZONTAL &&
161 _gtk_widget_get_direction (GTK_WIDGET (viewport)) == GTK_TEXT_DIR_RTL)
162 {
163 double dist = gtk_adjustment_get_upper (adjustment)
164 - value
165 - gtk_adjustment_get_page_size (adjustment);
166 value = upper - dist - viewport_size;
167 }
168
169 gtk_adjustment_configure (adjustment,
170 value,
171 lower: 0,
172 upper,
173 step_increment: viewport_size * 0.1,
174 page_increment: viewport_size * 0.9,
175 page_size: viewport_size);
176}
177
178static void
179gtk_viewport_measure (GtkWidget *widget,
180 GtkOrientation orientation,
181 int for_size,
182 int *minimum,
183 int *natural,
184 int *minimum_baseline,
185 int *natural_baseline)
186{
187 GtkViewport *viewport = GTK_VIEWPORT (widget);
188
189 if (viewport->child)
190 gtk_widget_measure (widget: viewport->child,
191 orientation,
192 for_size,
193 minimum, natural,
194 NULL, NULL);
195}
196
197static void
198gtk_viewport_compute_expand (GtkWidget *widget,
199 gboolean *hexpand,
200 gboolean *vexpand)
201{
202 GtkViewport *viewport = GTK_VIEWPORT (widget);
203
204 if (viewport->child)
205 {
206 *hexpand = gtk_widget_compute_expand (widget: viewport->child, orientation: GTK_ORIENTATION_HORIZONTAL);
207 *vexpand = gtk_widget_compute_expand (widget: viewport->child, orientation: GTK_ORIENTATION_VERTICAL);
208 }
209 else
210 {
211 *hexpand = FALSE;
212 *vexpand = FALSE;
213 }
214}
215
216static GtkSizeRequestMode
217gtk_viewport_get_request_mode (GtkWidget *widget)
218{
219 GtkViewport *viewport = GTK_VIEWPORT (widget);
220
221 if (viewport->child)
222 return gtk_widget_get_request_mode (widget: viewport->child);
223 else
224 return GTK_SIZE_REQUEST_CONSTANT_SIZE;
225}
226
227#define ADJUSTMENT_POINTER(orientation) \
228 (((orientation) == GTK_ORIENTATION_HORIZONTAL) ? \
229 &viewport->adjustment[GTK_ORIENTATION_HORIZONTAL] : &viewport->adjustment[GTK_ORIENTATION_VERTICAL])
230
231static void
232viewport_disconnect_adjustment (GtkViewport *viewport,
233 GtkOrientation orientation)
234{
235 GtkAdjustment **adjustmentp = ADJUSTMENT_POINTER (orientation);
236
237 if (*adjustmentp)
238 {
239 g_signal_handlers_disconnect_by_func (*adjustmentp,
240 gtk_viewport_adjustment_value_changed,
241 viewport);
242 g_object_unref (object: *adjustmentp);
243 *adjustmentp = NULL;
244 }
245}
246
247static void
248gtk_viewport_dispose (GObject *object)
249{
250 GtkViewport *viewport = GTK_VIEWPORT (object);
251
252 viewport_disconnect_adjustment (viewport, orientation: GTK_ORIENTATION_HORIZONTAL);
253 viewport_disconnect_adjustment (viewport, orientation: GTK_ORIENTATION_VERTICAL);
254
255 clear_focus_change_handler (viewport);
256
257 g_clear_pointer (&viewport->child, gtk_widget_unparent);
258
259 G_OBJECT_CLASS (gtk_viewport_parent_class)->dispose (object);
260
261}
262
263static void
264gtk_viewport_root (GtkWidget *widget)
265{
266 GtkViewport *viewport = GTK_VIEWPORT (widget);
267
268 GTK_WIDGET_CLASS (gtk_viewport_parent_class)->root (widget);
269
270 if (viewport->scroll_to_focus)
271 setup_focus_change_handler (viewport);
272}
273
274static void
275gtk_viewport_unroot (GtkWidget *widget)
276{
277 GtkViewport *viewport = GTK_VIEWPORT (widget);
278
279 if (viewport->scroll_to_focus)
280 clear_focus_change_handler (viewport);
281
282 GTK_WIDGET_CLASS (gtk_viewport_parent_class)->unroot (widget);
283}
284
285static void
286gtk_viewport_class_init (GtkViewportClass *class)
287{
288 GObjectClass *gobject_class;
289 GtkWidgetClass *widget_class;
290
291 gobject_class = G_OBJECT_CLASS (class);
292 widget_class = (GtkWidgetClass*) class;
293
294 gobject_class->dispose = gtk_viewport_dispose;
295 gobject_class->set_property = gtk_viewport_set_property;
296 gobject_class->get_property = gtk_viewport_get_property;
297
298 widget_class->size_allocate = gtk_viewport_size_allocate;
299 widget_class->measure = gtk_viewport_measure;
300 widget_class->root = gtk_viewport_root;
301 widget_class->unroot = gtk_viewport_unroot;
302 widget_class->compute_expand = gtk_viewport_compute_expand;
303 widget_class->get_request_mode = gtk_viewport_get_request_mode;
304
305 /* GtkScrollable implementation */
306 g_object_class_override_property (oclass: gobject_class, property_id: PROP_HADJUSTMENT, name: "hadjustment");
307 g_object_class_override_property (oclass: gobject_class, property_id: PROP_VADJUSTMENT, name: "vadjustment");
308 g_object_class_override_property (oclass: gobject_class, property_id: PROP_HSCROLL_POLICY, name: "hscroll-policy");
309 g_object_class_override_property (oclass: gobject_class, property_id: PROP_VSCROLL_POLICY, name: "vscroll-policy");
310
311 /**
312 * GtkViewport:scroll-to-focus: (attributes org.gtk.Property.get=gtk_viewport_get_scroll_to_focus org.gtk.Property.set=gtk_viewport_set_scroll_to_focus)
313 *
314 * Whether to scroll when the focus changes.
315 *
316 * Before 4.6.2, this property was mistakenly defaulting to FALSE, so if your
317 * code needs to work with older versions, consider setting it explicitly to
318 * TRUE.
319 */
320 g_object_class_install_property (oclass: gobject_class,
321 property_id: PROP_SCROLL_TO_FOCUS,
322 pspec: g_param_spec_boolean (name: "scroll-to-focus",
323 P_("Scroll to focus"),
324 P_("Whether to scroll when the focus changes"),
325 TRUE,
326 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
327
328 /**
329 * GtkViewport:child: (attributes org.gtk.Property.get=gtk_viewport_get_child org.gtk.Property.set=gtk_viewport_set_child)
330 *
331 * The child widget.
332 */
333 g_object_class_install_property (oclass: gobject_class,
334 property_id: PROP_CHILD,
335 pspec: g_param_spec_object (name: "child",
336 P_("Child"),
337 P_("The child widget"),
338 GTK_TYPE_WIDGET,
339 GTK_PARAM_READWRITE));
340
341 gtk_widget_class_set_css_name (widget_class, I_("viewport"));
342 gtk_widget_class_set_accessible_role (widget_class, accessible_role: GTK_ACCESSIBLE_ROLE_GROUP);
343}
344
345static void
346gtk_viewport_set_property (GObject *object,
347 guint prop_id,
348 const GValue *value,
349 GParamSpec *pspec)
350{
351 GtkViewport *viewport = GTK_VIEWPORT (object);
352
353 switch (prop_id)
354 {
355 case PROP_HADJUSTMENT:
356 viewport_set_adjustment (viewport, orientation: GTK_ORIENTATION_HORIZONTAL, adjustment: g_value_get_object (value));
357 break;
358 case PROP_VADJUSTMENT:
359 viewport_set_adjustment (viewport, orientation: GTK_ORIENTATION_VERTICAL, adjustment: g_value_get_object (value));
360 break;
361 case PROP_HSCROLL_POLICY:
362 if (viewport->scroll_policy[GTK_ORIENTATION_HORIZONTAL] != g_value_get_enum (value))
363 {
364 viewport->scroll_policy[GTK_ORIENTATION_HORIZONTAL] = g_value_get_enum (value);
365 gtk_widget_queue_resize (GTK_WIDGET (viewport));
366 g_object_notify_by_pspec (object, pspec);
367 }
368 break;
369 case PROP_VSCROLL_POLICY:
370 if (viewport->scroll_policy[GTK_ORIENTATION_VERTICAL] != g_value_get_enum (value))
371 {
372 viewport->scroll_policy[GTK_ORIENTATION_VERTICAL] = g_value_get_enum (value);
373 gtk_widget_queue_resize (GTK_WIDGET (viewport));
374 g_object_notify_by_pspec (object, pspec);
375 }
376 break;
377 case PROP_SCROLL_TO_FOCUS:
378 gtk_viewport_set_scroll_to_focus (viewport, scroll_to_focus: g_value_get_boolean (value));
379 break;
380 case PROP_CHILD:
381 gtk_viewport_set_child (viewport, child: g_value_get_object (value));
382 break;
383 default:
384 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
385 break;
386 }
387}
388
389static void
390gtk_viewport_get_property (GObject *object,
391 guint prop_id,
392 GValue *value,
393 GParamSpec *pspec)
394{
395 GtkViewport *viewport = GTK_VIEWPORT (object);
396
397 switch (prop_id)
398 {
399 case PROP_HADJUSTMENT:
400 g_value_set_object (value, v_object: viewport->adjustment[GTK_ORIENTATION_HORIZONTAL]);
401 break;
402 case PROP_VADJUSTMENT:
403 g_value_set_object (value, v_object: viewport->adjustment[GTK_ORIENTATION_VERTICAL]);
404 break;
405 case PROP_HSCROLL_POLICY:
406 g_value_set_enum (value, v_enum: viewport->scroll_policy[GTK_ORIENTATION_HORIZONTAL]);
407 break;
408 case PROP_VSCROLL_POLICY:
409 g_value_set_enum (value, v_enum: viewport->scroll_policy[GTK_ORIENTATION_VERTICAL]);
410 break;
411 case PROP_SCROLL_TO_FOCUS:
412 g_value_set_boolean (value, v_boolean: viewport->scroll_to_focus);
413 break;
414 case PROP_CHILD:
415 g_value_set_object (value, v_object: gtk_viewport_get_child (viewport));
416 break;
417 default:
418 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
419 break;
420 }
421}
422
423static void
424gtk_viewport_init (GtkViewport *viewport)
425{
426 GtkWidget *widget;
427
428 widget = GTK_WIDGET (viewport);
429
430 gtk_widget_set_overflow (widget, overflow: GTK_OVERFLOW_HIDDEN);
431
432 viewport->adjustment[GTK_ORIENTATION_HORIZONTAL] = NULL;
433 viewport->adjustment[GTK_ORIENTATION_VERTICAL] = NULL;
434
435 viewport_set_adjustment (viewport, orientation: GTK_ORIENTATION_HORIZONTAL, NULL);
436 viewport_set_adjustment (viewport, orientation: GTK_ORIENTATION_VERTICAL, NULL);
437
438 viewport->scroll_to_focus = TRUE;
439}
440
441/**
442 * gtk_viewport_new:
443 * @hadjustment: (nullable): horizontal adjustment
444 * @vadjustment: (nullable): vertical adjustment
445 *
446 * Creates a new `GtkViewport`.
447 *
448 * The new viewport uses the given adjustments, or default
449 * adjustments if none are given.
450 *
451 * Returns: a new `GtkViewport`
452 */
453GtkWidget*
454gtk_viewport_new (GtkAdjustment *hadjustment,
455 GtkAdjustment *vadjustment)
456{
457 GtkWidget *viewport;
458
459 viewport = g_object_new (GTK_TYPE_VIEWPORT,
460 first_property_name: "hadjustment", hadjustment,
461 "vadjustment", vadjustment,
462 NULL);
463
464 return viewport;
465}
466
467static void
468viewport_set_adjustment (GtkViewport *viewport,
469 GtkOrientation orientation,
470 GtkAdjustment *adjustment)
471{
472 GtkAdjustment **adjustmentp = ADJUSTMENT_POINTER (orientation);
473
474 if (adjustment && adjustment == *adjustmentp)
475 return;
476
477 if (!adjustment)
478 adjustment = gtk_adjustment_new (value: 0.0, lower: 0.0, upper: 0.0, step_increment: 0.0, page_increment: 0.0, page_size: 0.0);
479 viewport_disconnect_adjustment (viewport, orientation);
480 *adjustmentp = adjustment;
481 g_object_ref_sink (adjustment);
482
483 g_signal_connect (adjustment, "value-changed",
484 G_CALLBACK (gtk_viewport_adjustment_value_changed),
485 viewport);
486
487 gtk_viewport_adjustment_value_changed (adjustment, data: viewport);
488}
489
490static void
491gtk_viewport_size_allocate (GtkWidget *widget,
492 int width,
493 int height,
494 int baseline)
495{
496 GtkViewport *viewport = GTK_VIEWPORT (widget);
497 int child_size[2];
498
499 g_object_freeze_notify (G_OBJECT (viewport->adjustment[GTK_ORIENTATION_HORIZONTAL]));
500 g_object_freeze_notify (G_OBJECT (viewport->adjustment[GTK_ORIENTATION_VERTICAL]));
501
502 child_size[GTK_ORIENTATION_HORIZONTAL] = width;
503 child_size[GTK_ORIENTATION_VERTICAL] = height;
504
505 if (viewport->child && gtk_widget_get_visible (widget: viewport->child))
506 {
507 GtkOrientation orientation, opposite;
508 int min, nat;
509
510 if (gtk_widget_get_request_mode (widget: viewport->child) == GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT)
511 orientation = GTK_ORIENTATION_VERTICAL;
512 else
513 orientation = GTK_ORIENTATION_HORIZONTAL;
514 opposite = OPPOSITE_ORIENTATION (orientation);
515
516 gtk_widget_measure (widget: viewport->child,
517 orientation, for_size: -1,
518 minimum: &min, natural: &nat,
519 NULL, NULL);
520 if (viewport->scroll_policy[orientation] == GTK_SCROLL_MINIMUM)
521 child_size[orientation] = MAX (child_size[orientation], min);
522 else
523 child_size[orientation] = MAX (child_size[orientation], nat);
524
525 gtk_widget_measure (widget: viewport->child,
526 orientation: opposite, for_size: child_size[orientation],
527 minimum: &min, natural: &nat,
528 NULL, NULL);
529 if (viewport->scroll_policy[opposite] == GTK_SCROLL_MINIMUM)
530 child_size[opposite] = MAX (child_size[opposite], min);
531 else
532 child_size[opposite] = MAX (child_size[opposite], nat);
533 }
534
535 viewport_set_adjustment_values (viewport, orientation: GTK_ORIENTATION_HORIZONTAL, viewport_size: width, child_size: child_size[GTK_ORIENTATION_HORIZONTAL]);
536 viewport_set_adjustment_values (viewport, orientation: GTK_ORIENTATION_VERTICAL, viewport_size: height, child_size: child_size[GTK_ORIENTATION_VERTICAL]);
537
538 if (viewport->child && gtk_widget_get_visible (widget: viewport->child))
539 {
540 GtkAllocation child_allocation;
541
542 child_allocation.width = child_size[GTK_ORIENTATION_HORIZONTAL];
543 child_allocation.height = child_size[GTK_ORIENTATION_VERTICAL];
544 child_allocation.x = - gtk_adjustment_get_value (adjustment: viewport->adjustment[GTK_ORIENTATION_HORIZONTAL]);
545 child_allocation.y = - gtk_adjustment_get_value (adjustment: viewport->adjustment[GTK_ORIENTATION_VERTICAL]);
546
547 gtk_widget_size_allocate (widget: viewport->child, allocation: &child_allocation, baseline: -1);
548 }
549
550 g_object_thaw_notify (G_OBJECT (viewport->adjustment[GTK_ORIENTATION_HORIZONTAL]));
551 g_object_thaw_notify (G_OBJECT (viewport->adjustment[GTK_ORIENTATION_VERTICAL]));
552}
553
554static void
555gtk_viewport_adjustment_value_changed (GtkAdjustment *adjustment,
556 gpointer data)
557{
558 gtk_widget_queue_allocate (GTK_WIDGET (data));
559}
560
561/**
562 * gtk_viewport_get_scroll_to_focus: (attributes org.gtk.Method.get_property=scroll-to-focus)
563 * @viewport: a `GtkViewport`
564 *
565 * Gets whether the viewport is scrolling to keep the focused
566 * child in view.
567 *
568 * Returns: %TRUE if the viewport keeps the focus child scrolled to view
569 */
570gboolean
571gtk_viewport_get_scroll_to_focus (GtkViewport *viewport)
572{
573 g_return_val_if_fail (GTK_IS_VIEWPORT (viewport), FALSE);
574
575 return viewport->scroll_to_focus;
576}
577
578/**
579 * gtk_viewport_set_scroll_to_focus: (attributes org.gtk.Method.set_property=scroll-to-focus)
580 * @viewport: a `GtkViewport`
581 * @scroll_to_focus: whether to keep the focus widget scrolled to view
582 *
583 * Sets whether the viewport should automatically scroll
584 * to keep the focused child in view.
585 */
586void
587gtk_viewport_set_scroll_to_focus (GtkViewport *viewport,
588 gboolean scroll_to_focus)
589{
590 g_return_if_fail (GTK_IS_VIEWPORT (viewport));
591
592 if (viewport->scroll_to_focus == scroll_to_focus)
593 return;
594
595 viewport->scroll_to_focus = scroll_to_focus;
596
597 if (gtk_widget_get_root (GTK_WIDGET (viewport)))
598 {
599 if (scroll_to_focus)
600 setup_focus_change_handler (viewport);
601 else
602 clear_focus_change_handler (viewport);
603 }
604
605 g_object_notify (G_OBJECT (viewport), property_name: "scroll-to-focus");
606}
607
608static void
609scroll_to_view (GtkAdjustment *adj,
610 double pos,
611 double size)
612{
613 double value, page_size;
614
615 value = gtk_adjustment_get_value (adjustment: adj);
616 page_size = gtk_adjustment_get_page_size (adjustment: adj);
617
618 if (pos < 0)
619 gtk_adjustment_animate_to_value (adjustment: adj, value: value + pos);
620 else if (pos + size >= page_size)
621 gtk_adjustment_animate_to_value (adjustment: adj, value: value + pos + size - page_size);
622}
623
624static void
625focus_change_handler (GtkWidget *widget)
626{
627 GtkViewport *viewport = GTK_VIEWPORT (widget);
628 GtkRoot *root;
629 GtkWidget *focus_widget;
630 graphene_rect_t rect;
631 double x, y;
632
633 if ((gtk_widget_get_state_flags (widget) & GTK_STATE_FLAG_FOCUS_WITHIN) == 0)
634 return;
635
636 root = gtk_widget_get_root (widget);
637 focus_widget = gtk_root_get_focus (self: root);
638
639 if (!focus_widget)
640 return;
641
642 if (GTK_IS_TEXT (focus_widget))
643 focus_widget = gtk_widget_get_parent (widget: focus_widget);
644
645 if (!gtk_widget_compute_bounds (widget: focus_widget, target: viewport->child, out_bounds: &rect))
646 return;
647
648 gtk_widget_translate_coordinates (src_widget: viewport->child, dest_widget: widget,
649 src_x: rect.origin.x,
650 src_y: rect.origin.y,
651 dest_x: &x, dest_y: &y);
652
653 scroll_to_view (adj: viewport->adjustment[GTK_ORIENTATION_HORIZONTAL], pos: x, size: rect.size.width);
654 scroll_to_view (adj: viewport->adjustment[GTK_ORIENTATION_VERTICAL], pos: y, size: rect.size.height);
655}
656
657static void
658setup_focus_change_handler (GtkViewport *viewport)
659{
660 GtkRoot *root;
661
662 root = gtk_widget_get_root (GTK_WIDGET (viewport));
663
664 viewport->focus_handler = g_signal_connect_swapped (root, "notify::focus-widget",
665 G_CALLBACK (focus_change_handler), viewport);
666}
667
668static void
669clear_focus_change_handler (GtkViewport *viewport)
670{
671 GtkRoot *root;
672
673 root = gtk_widget_get_root (GTK_WIDGET (viewport));
674
675 if (viewport->focus_handler)
676 {
677 g_signal_handler_disconnect (instance: root, handler_id: viewport->focus_handler);
678 viewport->focus_handler = 0;
679 }
680}
681
682/**
683 * gtk_viewport_set_child: (attributes org.gtk.Method.set_property=child)
684 * @viewport: a `GtkViewport`
685 * @child: (nullable): the child widget
686 *
687 * Sets the child widget of @viewport.
688 */
689void
690gtk_viewport_set_child (GtkViewport *viewport,
691 GtkWidget *child)
692{
693 g_return_if_fail (GTK_IS_VIEWPORT (viewport));
694 g_return_if_fail (child == NULL || GTK_IS_WIDGET (child));
695
696 if (viewport->child == child)
697 return;
698
699 g_clear_pointer (&viewport->child, gtk_widget_unparent);
700
701 if (child)
702 {
703 viewport->child = child;
704 gtk_widget_set_parent (widget: child, GTK_WIDGET (viewport));
705 }
706
707 g_object_notify (G_OBJECT (viewport), property_name: "child");
708}
709
710/**
711 * gtk_viewport_get_child: (attributes org.gtk.Method.get_property=child)
712 * @viewport: a `GtkViewport`
713 *
714 * Gets the child widget of @viewport.
715 *
716 * Returns: (nullable) (transfer none): the child widget of @viewport
717 */
718GtkWidget *
719gtk_viewport_get_child (GtkViewport *viewport)
720{
721 g_return_val_if_fail (GTK_IS_VIEWPORT (viewport), NULL);
722
723 return viewport->child;
724}
725
726

source code of gtk/gtk/gtkviewport.c