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 "gtkadjustment.h"
30 #include "gtkcsscustomgadgetprivate.h"
31 #include "gtkintl.h"
32 #include "gtkmarshalers.h"
33 #include "gtkpixelcacheprivate.h"
34 #include "gtkprivate.h"
35 #include "gtkscrollable.h"
36 #include "gtkrenderbackgroundprivate.h"
37 #include "gtkstylecontextprivate.h"
38 #include "gtktypebuiltins.h"
39 #include "gtkwidgetprivate.h"
40
41
42 /**
43 * SECTION:gtkviewport
44 * @Short_description: An adapter which makes widgets scrollable
45 * @Title: GtkViewport
46 * @See_also:#GtkScrolledWindow, #GtkAdjustment
47 *
48 * The #GtkViewport widget acts as an adaptor class, implementing
49 * scrollability for child widgets that lack their own scrolling
50 * capabilities. Use GtkViewport to scroll child widgets such as
51 * #GtkGrid, #GtkBox, and so on.
52 *
53 * If a widget has native scrolling abilities, such as #GtkTextView,
54 * #GtkTreeView or #GtkIconView, it can be added to a #GtkScrolledWindow
55 * with gtk_container_add(). If a widget does not, you must first add the
56 * widget to a #GtkViewport, then add the viewport to the scrolled window.
57 * gtk_container_add() does this automatically if a child that does not
58 * implement #GtkScrollable is added to a #GtkScrolledWindow, so you can
59 * ignore the presence of the viewport.
60 *
61 * The GtkViewport will start scrolling content only if allocated less
62 * than the child widget’s minimum size in a given orientation.
63 *
64 * # CSS nodes
65 *
66 * GtkViewport has a single CSS node with name viewport.
67 */
68
69 struct _GtkViewportPrivate
70 {
71 GtkAdjustment *hadjustment;
72 GtkAdjustment *vadjustment;
73 GtkShadowType shadow_type;
74
75 GdkWindow *bin_window;
76 GdkWindow *view_window;
77
78 GtkCssGadget *gadget;
79
80 GtkPixelCache *pixel_cache;
81
82 /* GtkScrollablePolicy needs to be checked when
83 * driving the scrollable adjustment values */
84 guint hscroll_policy : 1;
85 guint vscroll_policy : 1;
86 };
87
88 enum {
89 PROP_0,
90 PROP_HADJUSTMENT,
91 PROP_VADJUSTMENT,
92 PROP_HSCROLL_POLICY,
93 PROP_VSCROLL_POLICY,
94 PROP_SHADOW_TYPE
95 };
96
97
98 static void gtk_viewport_set_property (GObject *object,
99 guint prop_id,
100 const GValue *value,
101 GParamSpec *pspec);
102 static void gtk_viewport_get_property (GObject *object,
103 guint prop_id,
104 GValue *value,
105 GParamSpec *pspec);
106 static void gtk_viewport_finalize (GObject *object);
107 static void gtk_viewport_destroy (GtkWidget *widget);
108 static void gtk_viewport_realize (GtkWidget *widget);
109 static void gtk_viewport_unrealize (GtkWidget *widget);
110 static void gtk_viewport_map (GtkWidget *widget);
111 static void gtk_viewport_unmap (GtkWidget *widget);
112 static gint gtk_viewport_draw (GtkWidget *widget,
113 cairo_t *cr);
114 static void gtk_viewport_remove (GtkContainer *container,
115 GtkWidget *widget);
116 static void gtk_viewport_add (GtkContainer *container,
117 GtkWidget *widget);
118 static void gtk_viewport_size_allocate (GtkWidget *widget,
119 GtkAllocation *allocation);
120 static void gtk_viewport_adjustment_value_changed (GtkAdjustment *adjustment,
121 gpointer data);
122
123 static void gtk_viewport_get_preferred_width (GtkWidget *widget,
124 gint *minimum_size,
125 gint *natural_size);
126 static void gtk_viewport_get_preferred_height (GtkWidget *widget,
127 gint *minimum_size,
128 gint *natural_size);
129 static void gtk_viewport_get_preferred_width_for_height (GtkWidget *widget,
130 gint height,
131 gint *minimum_size,
132 gint *natural_size);
133 static void gtk_viewport_get_preferred_height_for_width (GtkWidget *widget,
134 gint width,
135 gint *minimum_size,
136 gint *natural_size);
137
138 static void viewport_set_adjustment (GtkViewport *viewport,
139 GtkOrientation orientation,
140 GtkAdjustment *adjustment);
141 static void gtk_viewport_queue_draw_region (GtkWidget *widget,
142 const cairo_region_t *region);
143
G_DEFINE_TYPE_WITH_CODE(GtkViewport,gtk_viewport,GTK_TYPE_BIN,G_ADD_PRIVATE (GtkViewport)G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE,NULL))144 G_DEFINE_TYPE_WITH_CODE (GtkViewport, gtk_viewport, GTK_TYPE_BIN,
145 G_ADD_PRIVATE (GtkViewport)
146 G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL))
147
148 static void
149 gtk_viewport_measure (GtkCssGadget *gadget,
150 GtkOrientation orientation,
151 int for_size,
152 int *minimum,
153 int *natural,
154 int *minimum_baseline,
155 int *natural_baseline,
156 gpointer data)
157 {
158 GtkWidget *widget = gtk_css_gadget_get_owner (gadget);
159 GtkWidget *child;
160
161 *minimum = *natural = 0;
162
163 child = gtk_bin_get_child (GTK_BIN (widget));
164 if (child && gtk_widget_get_visible (child))
165 _gtk_widget_get_preferred_size_for_size (child,
166 orientation,
167 for_size,
168 minimum, natural,
169 NULL, NULL);
170 }
171
172 static void
viewport_set_hadjustment_values(GtkViewport * viewport)173 viewport_set_hadjustment_values (GtkViewport *viewport)
174 {
175 GtkBin *bin = GTK_BIN (viewport);
176 GtkAllocation view_allocation;
177 GtkAdjustment *hadjustment = viewport->priv->hadjustment;
178 GtkWidget *child;
179 gdouble upper, value;
180
181 gtk_css_gadget_get_content_allocation (viewport->priv->gadget,
182 &view_allocation, NULL);
183
184 child = gtk_bin_get_child (bin);
185 if (child && gtk_widget_get_visible (child))
186 {
187 gint minimum_width, natural_width;
188 gint scroll_height;
189
190 if (viewport->priv->vscroll_policy == GTK_SCROLL_MINIMUM)
191 gtk_widget_get_preferred_height (child, &scroll_height, NULL);
192 else
193 gtk_widget_get_preferred_height (child, NULL, &scroll_height);
194
195 gtk_widget_get_preferred_width_for_height (child,
196 MAX (view_allocation.height, scroll_height),
197 &minimum_width,
198 &natural_width);
199
200 if (viewport->priv->hscroll_policy == GTK_SCROLL_MINIMUM)
201 upper = MAX (minimum_width, view_allocation.width);
202 else
203 upper = MAX (natural_width, view_allocation.width);
204 }
205 else
206 upper = view_allocation.width;
207
208 value = gtk_adjustment_get_value (hadjustment);
209 /* We clamp to the left in RTL mode */
210 if (gtk_widget_get_direction (GTK_WIDGET (viewport)) == GTK_TEXT_DIR_RTL)
211 {
212 gdouble dist = gtk_adjustment_get_upper (hadjustment)
213 - value
214 - gtk_adjustment_get_page_size (hadjustment);
215 value = upper - dist - view_allocation.width;
216 }
217
218 gtk_adjustment_configure (hadjustment,
219 value,
220 0,
221 upper,
222 view_allocation.width * 0.1,
223 view_allocation.width * 0.9,
224 view_allocation.width);
225 }
226
227 static void
viewport_set_vadjustment_values(GtkViewport * viewport)228 viewport_set_vadjustment_values (GtkViewport *viewport)
229 {
230 GtkBin *bin = GTK_BIN (viewport);
231 GtkAllocation view_allocation;
232 GtkAdjustment *vadjustment = viewport->priv->vadjustment;
233 GtkWidget *child;
234 gdouble upper;
235
236 gtk_css_gadget_get_content_allocation (viewport->priv->gadget,
237 &view_allocation, NULL);
238
239 child = gtk_bin_get_child (bin);
240 if (child && gtk_widget_get_visible (child))
241 {
242 gint minimum_height, natural_height;
243 gint scroll_width;
244
245 if (viewport->priv->hscroll_policy == GTK_SCROLL_MINIMUM)
246 gtk_widget_get_preferred_width (child, &scroll_width, NULL);
247 else
248 gtk_widget_get_preferred_width (child, NULL, &scroll_width);
249
250 gtk_widget_get_preferred_height_for_width (child,
251 MAX (view_allocation.width, scroll_width),
252 &minimum_height,
253 &natural_height);
254
255 if (viewport->priv->vscroll_policy == GTK_SCROLL_MINIMUM)
256 upper = MAX (minimum_height, view_allocation.height);
257 else
258 upper = MAX (natural_height, view_allocation.height);
259 }
260 else
261 upper = view_allocation.height;
262
263 gtk_adjustment_configure (vadjustment,
264 gtk_adjustment_get_value (vadjustment),
265 0,
266 upper,
267 view_allocation.height * 0.1,
268 view_allocation.height * 0.9,
269 view_allocation.height);
270 }
271
272 static void
gtk_viewport_allocate(GtkCssGadget * gadget,const GtkAllocation * allocation,int baseline,GtkAllocation * out_clip,gpointer data)273 gtk_viewport_allocate (GtkCssGadget *gadget,
274 const GtkAllocation *allocation,
275 int baseline,
276 GtkAllocation *out_clip,
277 gpointer data)
278 {
279 GtkWidget *widget = gtk_css_gadget_get_owner (gadget);
280 GtkViewport *viewport = GTK_VIEWPORT (widget);
281 GtkViewportPrivate *priv = viewport->priv;
282 GtkAdjustment *hadjustment = priv->hadjustment;
283 GtkAdjustment *vadjustment = priv->vadjustment;
284 GtkWidget *child;
285
286 g_object_freeze_notify (G_OBJECT (hadjustment));
287 g_object_freeze_notify (G_OBJECT (vadjustment));
288
289 viewport_set_hadjustment_values (viewport);
290 viewport_set_vadjustment_values (viewport);
291
292 if (gtk_widget_get_realized (widget))
293 {
294 gdk_window_move_resize (priv->view_window,
295 allocation->x,
296 allocation->y,
297 allocation->width,
298 allocation->height);
299 gdk_window_move_resize (priv->bin_window,
300 - gtk_adjustment_get_value (hadjustment),
301 - gtk_adjustment_get_value (vadjustment),
302 gtk_adjustment_get_upper (hadjustment),
303 gtk_adjustment_get_upper (vadjustment));
304 }
305
306 child = gtk_bin_get_child (GTK_BIN (widget));
307 if (child && gtk_widget_get_visible (child))
308 {
309 GtkAllocation child_allocation;
310
311 child_allocation.x = 0;
312 child_allocation.y = 0;
313 child_allocation.width = gtk_adjustment_get_upper (hadjustment);
314 child_allocation.height = gtk_adjustment_get_upper (vadjustment);
315
316 gtk_widget_size_allocate (child, &child_allocation);
317 }
318
319 g_object_thaw_notify (G_OBJECT (hadjustment));
320 g_object_thaw_notify (G_OBJECT (vadjustment));
321 }
322
323 static void
draw_bin(cairo_t * cr,gpointer user_data)324 draw_bin (cairo_t *cr,
325 gpointer user_data)
326 {
327 GtkWidget *widget = GTK_WIDGET (user_data);
328 GTK_WIDGET_CLASS (gtk_viewport_parent_class)->draw (widget, cr);
329 }
330
331 static gboolean
gtk_viewport_render(GtkCssGadget * gadget,cairo_t * cr,int x,int y,int width,int height,gpointer data)332 gtk_viewport_render (GtkCssGadget *gadget,
333 cairo_t *cr,
334 int x,
335 int y,
336 int width,
337 int height,
338 gpointer data)
339 {
340 GtkWidget *widget = gtk_css_gadget_get_owner (gadget);
341 GtkViewport *viewport = GTK_VIEWPORT (widget);
342 GtkViewportPrivate *priv = viewport->priv;
343
344 if (gtk_cairo_should_draw_window (cr, priv->bin_window))
345 {
346 cairo_rectangle_int_t view_rect;
347 cairo_rectangle_int_t canvas_rect;
348
349 gdk_window_get_position (priv->view_window, &view_rect.x, &view_rect.y);
350 view_rect.width = gdk_window_get_width (priv->view_window);
351 view_rect.height = gdk_window_get_height (priv->view_window);
352
353 gdk_window_get_position (priv->bin_window, &canvas_rect.x, &canvas_rect.y);
354 canvas_rect.width = gdk_window_get_width (priv->bin_window);
355 canvas_rect.height = gdk_window_get_height (priv->bin_window);
356
357 _gtk_pixel_cache_draw (priv->pixel_cache, cr, priv->bin_window,
358 &view_rect, &canvas_rect,
359 draw_bin, widget);
360 }
361
362 return FALSE;
363 }
364
365 static void
gtk_viewport_class_init(GtkViewportClass * class)366 gtk_viewport_class_init (GtkViewportClass *class)
367 {
368 GObjectClass *gobject_class;
369 GtkWidgetClass *widget_class;
370 GtkContainerClass *container_class;
371
372 gobject_class = G_OBJECT_CLASS (class);
373 widget_class = (GtkWidgetClass*) class;
374 container_class = (GtkContainerClass*) class;
375
376 gobject_class->set_property = gtk_viewport_set_property;
377 gobject_class->get_property = gtk_viewport_get_property;
378 gobject_class->finalize = gtk_viewport_finalize;
379
380 widget_class->destroy = gtk_viewport_destroy;
381 widget_class->realize = gtk_viewport_realize;
382 widget_class->unrealize = gtk_viewport_unrealize;
383 widget_class->map = gtk_viewport_map;
384 widget_class->unmap = gtk_viewport_unmap;
385 widget_class->draw = gtk_viewport_draw;
386 widget_class->size_allocate = gtk_viewport_size_allocate;
387 widget_class->get_preferred_width = gtk_viewport_get_preferred_width;
388 widget_class->get_preferred_height = gtk_viewport_get_preferred_height;
389 widget_class->get_preferred_width_for_height = gtk_viewport_get_preferred_width_for_height;
390 widget_class->get_preferred_height_for_width = gtk_viewport_get_preferred_height_for_width;
391 widget_class->queue_draw_region = gtk_viewport_queue_draw_region;
392
393 gtk_widget_class_set_accessible_role (widget_class, ATK_ROLE_VIEWPORT);
394
395 container_class->remove = gtk_viewport_remove;
396 container_class->add = gtk_viewport_add;
397 gtk_container_class_handle_border_width (container_class);
398
399 /* GtkScrollable implementation */
400 g_object_class_override_property (gobject_class, PROP_HADJUSTMENT, "hadjustment");
401 g_object_class_override_property (gobject_class, PROP_VADJUSTMENT, "vadjustment");
402 g_object_class_override_property (gobject_class, PROP_HSCROLL_POLICY, "hscroll-policy");
403 g_object_class_override_property (gobject_class, PROP_VSCROLL_POLICY, "vscroll-policy");
404
405 g_object_class_install_property (gobject_class,
406 PROP_SHADOW_TYPE,
407 g_param_spec_enum ("shadow-type",
408 P_("Shadow type"),
409 P_("Determines how the shadowed box around the viewport is drawn"),
410 GTK_TYPE_SHADOW_TYPE,
411 GTK_SHADOW_IN,
412 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
413
414 gtk_widget_class_set_css_name (widget_class, "viewport");
415 }
416
417 static void
gtk_viewport_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)418 gtk_viewport_set_property (GObject *object,
419 guint prop_id,
420 const GValue *value,
421 GParamSpec *pspec)
422 {
423 GtkViewport *viewport;
424
425 viewport = GTK_VIEWPORT (object);
426
427 switch (prop_id)
428 {
429 case PROP_HADJUSTMENT:
430 viewport_set_adjustment (viewport, GTK_ORIENTATION_HORIZONTAL, g_value_get_object (value));
431 break;
432 case PROP_VADJUSTMENT:
433 viewport_set_adjustment (viewport, GTK_ORIENTATION_VERTICAL, g_value_get_object (value));
434 break;
435 case PROP_HSCROLL_POLICY:
436 if (viewport->priv->hscroll_policy != g_value_get_enum (value))
437 {
438 viewport->priv->hscroll_policy = g_value_get_enum (value);
439 gtk_widget_queue_resize (GTK_WIDGET (viewport));
440 g_object_notify_by_pspec (object, pspec);
441 }
442 break;
443 case PROP_VSCROLL_POLICY:
444 if (viewport->priv->vscroll_policy != g_value_get_enum (value))
445 {
446 viewport->priv->vscroll_policy = g_value_get_enum (value);
447 gtk_widget_queue_resize (GTK_WIDGET (viewport));
448 g_object_notify_by_pspec (object, pspec);
449 }
450 break;
451 case PROP_SHADOW_TYPE:
452 gtk_viewport_set_shadow_type (viewport, g_value_get_enum (value));
453 break;
454 default:
455 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
456 break;
457 }
458 }
459
460 static void
gtk_viewport_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)461 gtk_viewport_get_property (GObject *object,
462 guint prop_id,
463 GValue *value,
464 GParamSpec *pspec)
465 {
466 GtkViewport *viewport = GTK_VIEWPORT (object);
467 GtkViewportPrivate *priv = viewport->priv;
468
469 switch (prop_id)
470 {
471 case PROP_HADJUSTMENT:
472 g_value_set_object (value, priv->hadjustment);
473 break;
474 case PROP_VADJUSTMENT:
475 g_value_set_object (value, priv->vadjustment);
476 break;
477 case PROP_HSCROLL_POLICY:
478 g_value_set_enum (value, priv->hscroll_policy);
479 break;
480 case PROP_VSCROLL_POLICY:
481 g_value_set_enum (value, priv->vscroll_policy);
482 break;
483 case PROP_SHADOW_TYPE:
484 g_value_set_enum (value, priv->shadow_type);
485 break;
486 default:
487 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
488 break;
489 }
490 }
491
492 static void
gtk_viewport_init(GtkViewport * viewport)493 gtk_viewport_init (GtkViewport *viewport)
494 {
495 GtkWidget *widget;
496 GtkViewportPrivate *priv;
497 GtkCssNode *widget_node;
498
499 viewport->priv = gtk_viewport_get_instance_private (viewport);
500 priv = viewport->priv;
501 widget = GTK_WIDGET (viewport);
502
503 gtk_widget_set_has_window (widget, TRUE);
504
505 priv->shadow_type = GTK_SHADOW_IN;
506 priv->view_window = NULL;
507 priv->bin_window = NULL;
508 priv->hadjustment = NULL;
509 priv->vadjustment = NULL;
510
511 priv->pixel_cache = _gtk_pixel_cache_new ();
512
513 widget_node = gtk_widget_get_css_node (widget);
514 priv->gadget = gtk_css_custom_gadget_new_for_node (widget_node,
515 widget,
516 gtk_viewport_measure,
517 gtk_viewport_allocate,
518 gtk_viewport_render,
519 NULL, NULL);
520
521 gtk_css_gadget_add_class (priv->gadget, GTK_STYLE_CLASS_FRAME);
522 viewport_set_adjustment (viewport, GTK_ORIENTATION_HORIZONTAL, NULL);
523 viewport_set_adjustment (viewport, GTK_ORIENTATION_VERTICAL, NULL);
524 }
525
526 /**
527 * gtk_viewport_new:
528 * @hadjustment: (allow-none): horizontal adjustment
529 * @vadjustment: (allow-none): vertical adjustment
530 *
531 * Creates a new #GtkViewport with the given adjustments, or with default
532 * adjustments if none are given.
533 *
534 * Returns: a new #GtkViewport
535 */
536 GtkWidget*
gtk_viewport_new(GtkAdjustment * hadjustment,GtkAdjustment * vadjustment)537 gtk_viewport_new (GtkAdjustment *hadjustment,
538 GtkAdjustment *vadjustment)
539 {
540 GtkWidget *viewport;
541
542 viewport = g_object_new (GTK_TYPE_VIEWPORT,
543 "hadjustment", hadjustment,
544 "vadjustment", vadjustment,
545 NULL);
546
547 return viewport;
548 }
549
550 #define ADJUSTMENT_POINTER(viewport, orientation) \
551 (((orientation) == GTK_ORIENTATION_HORIZONTAL) ? \
552 &(viewport)->priv->hadjustment : &(viewport)->priv->vadjustment)
553
554 static void
viewport_disconnect_adjustment(GtkViewport * viewport,GtkOrientation orientation)555 viewport_disconnect_adjustment (GtkViewport *viewport,
556 GtkOrientation orientation)
557 {
558 GtkAdjustment **adjustmentp = ADJUSTMENT_POINTER (viewport, orientation);
559
560 if (*adjustmentp)
561 {
562 g_signal_handlers_disconnect_by_func (*adjustmentp,
563 gtk_viewport_adjustment_value_changed,
564 viewport);
565 g_object_unref (*adjustmentp);
566 *adjustmentp = NULL;
567 }
568 }
569
570 static void
gtk_viewport_destroy(GtkWidget * widget)571 gtk_viewport_destroy (GtkWidget *widget)
572 {
573 GtkViewport *viewport = GTK_VIEWPORT (widget);
574 GtkViewportPrivate *priv = viewport->priv;
575
576 viewport_disconnect_adjustment (viewport, GTK_ORIENTATION_HORIZONTAL);
577 viewport_disconnect_adjustment (viewport, GTK_ORIENTATION_VERTICAL);
578
579 GTK_WIDGET_CLASS (gtk_viewport_parent_class)->destroy (widget);
580
581 g_clear_pointer (&priv->pixel_cache, _gtk_pixel_cache_free);
582 }
583
584 static void
gtk_viewport_finalize(GObject * object)585 gtk_viewport_finalize (GObject *object)
586 {
587 GtkViewport *viewport = GTK_VIEWPORT (object);
588 GtkViewportPrivate *priv = viewport->priv;
589
590 g_clear_object (&priv->gadget);
591
592 G_OBJECT_CLASS (gtk_viewport_parent_class)->finalize (object);
593 }
594
595 /**
596 * gtk_viewport_get_hadjustment:
597 * @viewport: a #GtkViewport.
598 *
599 * Returns the horizontal adjustment of the viewport.
600 *
601 * Returns: (transfer none): the horizontal adjustment of @viewport.
602 *
603 * Deprecated: 3.0: Use gtk_scrollable_get_hadjustment()
604 **/
605 GtkAdjustment*
gtk_viewport_get_hadjustment(GtkViewport * viewport)606 gtk_viewport_get_hadjustment (GtkViewport *viewport)
607 {
608 g_return_val_if_fail (GTK_IS_VIEWPORT (viewport), NULL);
609
610 return viewport->priv->hadjustment;
611 }
612
613 /**
614 * gtk_viewport_get_vadjustment:
615 * @viewport: a #GtkViewport.
616 *
617 * Returns the vertical adjustment of the viewport.
618 *
619 * Returns: (transfer none): the vertical adjustment of @viewport.
620 *
621 * Deprecated: 3.0: Use gtk_scrollable_get_vadjustment()
622 **/
623 GtkAdjustment*
gtk_viewport_get_vadjustment(GtkViewport * viewport)624 gtk_viewport_get_vadjustment (GtkViewport *viewport)
625 {
626 g_return_val_if_fail (GTK_IS_VIEWPORT (viewport), NULL);
627
628 return viewport->priv->vadjustment;
629 }
630
631 static void
viewport_set_adjustment(GtkViewport * viewport,GtkOrientation orientation,GtkAdjustment * adjustment)632 viewport_set_adjustment (GtkViewport *viewport,
633 GtkOrientation orientation,
634 GtkAdjustment *adjustment)
635 {
636 GtkAdjustment **adjustmentp = ADJUSTMENT_POINTER (viewport, orientation);
637
638 if (adjustment && adjustment == *adjustmentp)
639 return;
640
641 if (!adjustment)
642 adjustment = gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
643 viewport_disconnect_adjustment (viewport, orientation);
644 *adjustmentp = adjustment;
645 g_object_ref_sink (adjustment);
646
647 if (orientation == GTK_ORIENTATION_HORIZONTAL)
648 viewport_set_hadjustment_values (viewport);
649 else
650 viewport_set_vadjustment_values (viewport);
651
652 g_signal_connect (adjustment, "value-changed",
653 G_CALLBACK (gtk_viewport_adjustment_value_changed),
654 viewport);
655
656 gtk_viewport_adjustment_value_changed (adjustment, viewport);
657 }
658
659 /**
660 * gtk_viewport_set_hadjustment:
661 * @viewport: a #GtkViewport.
662 * @adjustment: (allow-none): a #GtkAdjustment.
663 *
664 * Sets the horizontal adjustment of the viewport.
665 *
666 * Deprecated: 3.0: Use gtk_scrollable_set_hadjustment()
667 **/
668 void
gtk_viewport_set_hadjustment(GtkViewport * viewport,GtkAdjustment * adjustment)669 gtk_viewport_set_hadjustment (GtkViewport *viewport,
670 GtkAdjustment *adjustment)
671 {
672 g_return_if_fail (GTK_IS_VIEWPORT (viewport));
673 if (adjustment)
674 g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
675
676 viewport_set_adjustment (viewport, GTK_ORIENTATION_HORIZONTAL, adjustment);
677
678 g_object_notify (G_OBJECT (viewport), "hadjustment");
679 }
680
681 /**
682 * gtk_viewport_set_vadjustment:
683 * @viewport: a #GtkViewport.
684 * @adjustment: (allow-none): a #GtkAdjustment.
685 *
686 * Sets the vertical adjustment of the viewport.
687 *
688 * Deprecated: 3.0: Use gtk_scrollable_set_vadjustment()
689 **/
690 void
gtk_viewport_set_vadjustment(GtkViewport * viewport,GtkAdjustment * adjustment)691 gtk_viewport_set_vadjustment (GtkViewport *viewport,
692 GtkAdjustment *adjustment)
693 {
694 g_return_if_fail (GTK_IS_VIEWPORT (viewport));
695 if (adjustment)
696 g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
697
698 viewport_set_adjustment (viewport, GTK_ORIENTATION_VERTICAL, adjustment);
699
700 g_object_notify (G_OBJECT (viewport), "vadjustment");
701 }
702
703 /**
704 * gtk_viewport_set_shadow_type:
705 * @viewport: a #GtkViewport.
706 * @type: the new shadow type.
707 *
708 * Sets the shadow type of the viewport.
709 **/
710 void
gtk_viewport_set_shadow_type(GtkViewport * viewport,GtkShadowType type)711 gtk_viewport_set_shadow_type (GtkViewport *viewport,
712 GtkShadowType type)
713 {
714 GtkViewportPrivate *priv;
715 GtkWidget *widget;
716 GtkStyleContext *context;
717
718 g_return_if_fail (GTK_IS_VIEWPORT (viewport));
719
720 widget = GTK_WIDGET (viewport);
721 priv = viewport->priv;
722
723 if ((GtkShadowType) priv->shadow_type != type)
724 {
725 priv->shadow_type = type;
726
727 context = gtk_widget_get_style_context (widget);
728 if (type != GTK_SHADOW_NONE)
729 gtk_style_context_add_class (context, GTK_STYLE_CLASS_FRAME);
730 else
731 gtk_style_context_remove_class (context, GTK_STYLE_CLASS_FRAME);
732
733 gtk_widget_queue_resize (widget);
734
735 g_object_notify (G_OBJECT (viewport), "shadow-type");
736 }
737 }
738
739 /**
740 * gtk_viewport_get_shadow_type:
741 * @viewport: a #GtkViewport
742 *
743 * Gets the shadow type of the #GtkViewport. See
744 * gtk_viewport_set_shadow_type().
745 *
746 * Returns: the shadow type
747 **/
748 GtkShadowType
gtk_viewport_get_shadow_type(GtkViewport * viewport)749 gtk_viewport_get_shadow_type (GtkViewport *viewport)
750 {
751 g_return_val_if_fail (GTK_IS_VIEWPORT (viewport), GTK_SHADOW_NONE);
752
753 return viewport->priv->shadow_type;
754 }
755
756 /**
757 * gtk_viewport_get_bin_window:
758 * @viewport: a #GtkViewport
759 *
760 * Gets the bin window of the #GtkViewport.
761 *
762 * Returns: (transfer none): a #GdkWindow
763 *
764 * Since: 2.20
765 **/
766 GdkWindow*
gtk_viewport_get_bin_window(GtkViewport * viewport)767 gtk_viewport_get_bin_window (GtkViewport *viewport)
768 {
769 g_return_val_if_fail (GTK_IS_VIEWPORT (viewport), NULL);
770
771 return viewport->priv->bin_window;
772 }
773
774 /**
775 * gtk_viewport_get_view_window:
776 * @viewport: a #GtkViewport
777 *
778 * Gets the view window of the #GtkViewport.
779 *
780 * Returns: (transfer none): a #GdkWindow
781 *
782 * Since: 2.22
783 **/
784 GdkWindow*
gtk_viewport_get_view_window(GtkViewport * viewport)785 gtk_viewport_get_view_window (GtkViewport *viewport)
786 {
787 g_return_val_if_fail (GTK_IS_VIEWPORT (viewport), NULL);
788
789 return viewport->priv->view_window;
790 }
791
792 static void
gtk_viewport_bin_window_invalidate_handler(GdkWindow * window,cairo_region_t * region)793 gtk_viewport_bin_window_invalidate_handler (GdkWindow *window,
794 cairo_region_t *region)
795 {
796 gpointer widget;
797 GtkViewport *viewport;
798 GtkViewportPrivate *priv;
799
800 gdk_window_get_user_data (window, &widget);
801 viewport = GTK_VIEWPORT (widget);
802 priv = viewport->priv;
803
804 _gtk_pixel_cache_invalidate (priv->pixel_cache, region);
805 }
806
807 static void
gtk_viewport_queue_draw_region(GtkWidget * widget,const cairo_region_t * region)808 gtk_viewport_queue_draw_region (GtkWidget *widget,
809 const cairo_region_t *region)
810 {
811 GtkViewport *viewport = GTK_VIEWPORT (widget);
812 GtkViewportPrivate *priv = viewport->priv;
813
814 /* There is no way we can know if a region targets the
815 not-currently-visible but in pixel cache region, so we
816 always just invalidate the whole thing whenever the
817 tree view gets a queue draw. This doesn't normally happen
818 in normal scrolling cases anyway. */
819 _gtk_pixel_cache_invalidate (priv->pixel_cache, NULL);
820
821 GTK_WIDGET_CLASS (gtk_viewport_parent_class)->queue_draw_region (widget,
822 region);
823 }
824
825
826 static void
gtk_viewport_realize(GtkWidget * widget)827 gtk_viewport_realize (GtkWidget *widget)
828 {
829 GtkViewport *viewport = GTK_VIEWPORT (widget);
830 GtkViewportPrivate *priv = viewport->priv;
831 GtkBin *bin = GTK_BIN (widget);
832 GtkAdjustment *hadjustment = priv->hadjustment;
833 GtkAdjustment *vadjustment = priv->vadjustment;
834 GtkAllocation allocation;
835 GtkAllocation view_allocation;
836 GtkWidget *child;
837 GdkWindow *window;
838 GdkWindowAttr attributes;
839 gint attributes_mask;
840 gint event_mask;
841
842 gtk_widget_set_realized (widget, TRUE);
843
844 gtk_widget_get_allocation (widget, &allocation);
845
846 attributes.x = allocation.x;
847 attributes.y = allocation.y;
848 attributes.width = allocation.width;
849 attributes.height = allocation.height;
850 attributes.window_type = GDK_WINDOW_CHILD;
851 attributes.wclass = GDK_INPUT_OUTPUT;
852 attributes.visual = gtk_widget_get_visual (widget);
853
854 event_mask = gtk_widget_get_events (widget);
855
856 attributes.event_mask = event_mask | GDK_SCROLL_MASK | GDK_TOUCH_MASK | GDK_SMOOTH_SCROLL_MASK;
857
858 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
859
860 window = gdk_window_new (gtk_widget_get_parent_window (widget),
861 &attributes, attributes_mask);
862 gtk_widget_set_window (widget, window);
863 gtk_widget_register_window (widget, window);
864
865 gtk_css_gadget_get_content_allocation (priv->gadget,
866 &view_allocation, NULL);
867
868 attributes.x = view_allocation.x;
869 attributes.y = view_allocation.y;
870 attributes.width = view_allocation.width;
871 attributes.height = view_allocation.height;
872 attributes.event_mask = 0;
873
874 priv->view_window = gdk_window_new (window,
875 &attributes, attributes_mask);
876 gtk_widget_register_window (widget, priv->view_window);
877
878 attributes.x = - gtk_adjustment_get_value (hadjustment);
879 attributes.y = - gtk_adjustment_get_value (vadjustment);
880 attributes.width = gtk_adjustment_get_upper (hadjustment);
881 attributes.height = gtk_adjustment_get_upper (vadjustment);
882
883 attributes.event_mask = event_mask;
884
885 priv->bin_window = gdk_window_new (priv->view_window, &attributes, attributes_mask);
886 gtk_widget_register_window (widget, priv->bin_window);
887 gdk_window_set_invalidate_handler (priv->bin_window,
888 gtk_viewport_bin_window_invalidate_handler);
889
890 child = gtk_bin_get_child (bin);
891 if (child)
892 gtk_widget_set_parent_window (child, priv->bin_window);
893
894 gdk_window_show (priv->bin_window);
895 gdk_window_show (priv->view_window);
896 }
897
898 static void
gtk_viewport_unrealize(GtkWidget * widget)899 gtk_viewport_unrealize (GtkWidget *widget)
900 {
901 GtkViewport *viewport = GTK_VIEWPORT (widget);
902 GtkViewportPrivate *priv = viewport->priv;
903
904 gtk_widget_unregister_window (widget, priv->view_window);
905 gdk_window_destroy (priv->view_window);
906 priv->view_window = NULL;
907
908 gtk_widget_unregister_window (widget, priv->bin_window);
909 gdk_window_destroy (priv->bin_window);
910 priv->bin_window = NULL;
911
912 GTK_WIDGET_CLASS (gtk_viewport_parent_class)->unrealize (widget);
913 }
914
915 static void
gtk_viewport_map(GtkWidget * widget)916 gtk_viewport_map (GtkWidget *widget)
917 {
918 GtkViewport *viewport = GTK_VIEWPORT (widget);
919 GtkViewportPrivate *priv = viewport->priv;
920
921 _gtk_pixel_cache_map (priv->pixel_cache);
922
923 GTK_WIDGET_CLASS (gtk_viewport_parent_class)->map (widget);
924 }
925
926 static void
gtk_viewport_unmap(GtkWidget * widget)927 gtk_viewport_unmap (GtkWidget *widget)
928 {
929 GtkViewport *viewport = GTK_VIEWPORT (widget);
930 GtkViewportPrivate *priv = viewport->priv;
931
932 GTK_WIDGET_CLASS (gtk_viewport_parent_class)->unmap (widget);
933
934 _gtk_pixel_cache_unmap (priv->pixel_cache);
935 }
936
937 static gint
gtk_viewport_draw(GtkWidget * widget,cairo_t * cr)938 gtk_viewport_draw (GtkWidget *widget,
939 cairo_t *cr)
940 {
941 GtkViewport *viewport = GTK_VIEWPORT (widget);
942 GtkViewportPrivate *priv = viewport->priv;
943
944 if (gtk_cairo_should_draw_window (cr, gtk_widget_get_window (widget)) ||
945 gtk_cairo_should_draw_window (cr, priv->bin_window))
946 gtk_css_gadget_draw (priv->gadget, cr);
947
948 return FALSE;
949 }
950
951 static void
gtk_viewport_update_pixelcache_opacity(GtkWidget * child,GtkViewport * viewport)952 gtk_viewport_update_pixelcache_opacity (GtkWidget *child,
953 GtkViewport *viewport)
954 {
955 GtkViewportPrivate *priv = viewport->priv;
956
957 gtk_pixel_cache_set_is_opaque (priv->pixel_cache,
958 gtk_css_style_render_background_is_opaque (
959 gtk_style_context_lookup_style (
960 gtk_widget_get_style_context (child))));
961 }
962
963 static void
gtk_viewport_remove(GtkContainer * container,GtkWidget * child)964 gtk_viewport_remove (GtkContainer *container,
965 GtkWidget *child)
966 {
967 GtkViewport *viewport = GTK_VIEWPORT (container);
968 GtkViewportPrivate *priv = viewport->priv;
969
970 if (g_signal_handlers_disconnect_by_func (child, gtk_viewport_update_pixelcache_opacity, viewport) != 1)
971 {
972 g_assert_not_reached ();
973 }
974
975 GTK_CONTAINER_CLASS (gtk_viewport_parent_class)->remove (container, child);
976
977 gtk_pixel_cache_set_is_opaque (priv->pixel_cache, FALSE);
978 }
979
980 static void
gtk_viewport_add(GtkContainer * container,GtkWidget * child)981 gtk_viewport_add (GtkContainer *container,
982 GtkWidget *child)
983 {
984 GtkBin *bin = GTK_BIN (container);
985 GtkViewport *viewport = GTK_VIEWPORT (bin);
986 GtkViewportPrivate *priv = viewport->priv;
987
988 g_return_if_fail (gtk_bin_get_child (bin) == NULL);
989
990 gtk_widget_set_parent_window (child, priv->bin_window);
991
992 GTK_CONTAINER_CLASS (gtk_viewport_parent_class)->add (container, child);
993
994 g_signal_connect (child, "style-updated", G_CALLBACK (gtk_viewport_update_pixelcache_opacity), viewport);
995 gtk_viewport_update_pixelcache_opacity (child, viewport);
996 }
997
998 static void
gtk_viewport_size_allocate(GtkWidget * widget,GtkAllocation * allocation)999 gtk_viewport_size_allocate (GtkWidget *widget,
1000 GtkAllocation *allocation)
1001 {
1002 GtkViewport *viewport = GTK_VIEWPORT (widget);
1003 GtkViewportPrivate *priv = viewport->priv;
1004 GtkAllocation clip, content_allocation, widget_allocation;
1005
1006 /* If our size changed, and we have a shadow, queue a redraw on widget->window to
1007 * redraw the shadow correctly.
1008 */
1009 gtk_widget_get_allocation (widget, &widget_allocation);
1010 if (gtk_widget_get_mapped (widget) &&
1011 priv->shadow_type != GTK_SHADOW_NONE &&
1012 (widget_allocation.width != allocation->width ||
1013 widget_allocation.height != allocation->height))
1014 gdk_window_invalidate_rect (gtk_widget_get_window (widget), NULL, FALSE);
1015
1016 gtk_widget_set_allocation (widget, allocation);
1017
1018 if (gtk_widget_get_realized (widget))
1019 gdk_window_move_resize (gtk_widget_get_window (widget),
1020 allocation->x,
1021 allocation->y,
1022 allocation->width,
1023 allocation->height);
1024
1025 content_allocation = *allocation;
1026 content_allocation.x = content_allocation.y = 0;
1027 gtk_css_gadget_allocate (priv->gadget,
1028 &content_allocation,
1029 gtk_widget_get_allocated_baseline (widget),
1030 &clip);
1031
1032 clip.x += allocation->x;
1033 clip.y += allocation->y;
1034 gtk_widget_set_clip (widget, &clip);
1035 }
1036
1037 static void
gtk_viewport_adjustment_value_changed(GtkAdjustment * adjustment,gpointer data)1038 gtk_viewport_adjustment_value_changed (GtkAdjustment *adjustment,
1039 gpointer data)
1040 {
1041 GtkViewport *viewport = GTK_VIEWPORT (data);
1042 GtkViewportPrivate *priv = viewport->priv;
1043 GtkBin *bin = GTK_BIN (data);
1044 GtkWidget *child;
1045
1046 child = gtk_bin_get_child (bin);
1047 if (child && gtk_widget_get_visible (child) &&
1048 gtk_widget_get_realized (GTK_WIDGET (viewport)))
1049 {
1050 GtkAdjustment *hadjustment = priv->hadjustment;
1051 GtkAdjustment *vadjustment = priv->vadjustment;
1052 gint old_x, old_y;
1053 gint new_x, new_y;
1054
1055 gdk_window_get_position (priv->bin_window, &old_x, &old_y);
1056 new_x = - gtk_adjustment_get_value (hadjustment);
1057 new_y = - gtk_adjustment_get_value (vadjustment);
1058
1059 if (new_x != old_x || new_y != old_y)
1060 gdk_window_move (priv->bin_window, new_x, new_y);
1061 }
1062 }
1063
1064 static void
gtk_viewport_get_preferred_width(GtkWidget * widget,gint * minimum_size,gint * natural_size)1065 gtk_viewport_get_preferred_width (GtkWidget *widget,
1066 gint *minimum_size,
1067 gint *natural_size)
1068 {
1069 gtk_css_gadget_get_preferred_size (GTK_VIEWPORT (widget)->priv->gadget,
1070 GTK_ORIENTATION_HORIZONTAL,
1071 -1,
1072 minimum_size, natural_size,
1073 NULL, NULL);
1074 }
1075
1076 static void
gtk_viewport_get_preferred_height(GtkWidget * widget,gint * minimum_size,gint * natural_size)1077 gtk_viewport_get_preferred_height (GtkWidget *widget,
1078 gint *minimum_size,
1079 gint *natural_size)
1080 {
1081 gtk_css_gadget_get_preferred_size (GTK_VIEWPORT (widget)->priv->gadget,
1082 GTK_ORIENTATION_VERTICAL,
1083 -1,
1084 minimum_size, natural_size,
1085 NULL, NULL);
1086 }
1087
1088 static void
gtk_viewport_get_preferred_width_for_height(GtkWidget * widget,gint height,gint * minimum_size,gint * natural_size)1089 gtk_viewport_get_preferred_width_for_height (GtkWidget *widget,
1090 gint height,
1091 gint *minimum_size,
1092 gint *natural_size)
1093 {
1094 gtk_css_gadget_get_preferred_size (GTK_VIEWPORT (widget)->priv->gadget,
1095 GTK_ORIENTATION_HORIZONTAL,
1096 height,
1097 minimum_size, natural_size,
1098 NULL, NULL);
1099 }
1100
1101 static void
gtk_viewport_get_preferred_height_for_width(GtkWidget * widget,gint width,gint * minimum_size,gint * natural_size)1102 gtk_viewport_get_preferred_height_for_width (GtkWidget *widget,
1103 gint width,
1104 gint *minimum_size,
1105 gint *natural_size)
1106 {
1107 gtk_css_gadget_get_preferred_size (GTK_VIEWPORT (widget)->priv->gadget,
1108 GTK_ORIENTATION_VERTICAL,
1109 width,
1110 minimum_size, natural_size,
1111 NULL, NULL);
1112 }
1113