1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  * Copyright (C) 2001 Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 /*
20  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
21  * file for a list of people on the GTK+ Team.  See the ChangeLog
22  * files for a list of changes.  These files are distributed with
23  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
24  */
25 
26 #include "config.h"
27 
28 #include "gtkscrollbar.h"
29 #include "gtkrange.h"
30 
31 #include "gtkadjustment.h"
32 #include "gtkintl.h"
33 #include "gtkorientable.h"
34 #include "gtkprivate.h"
35 #include "gtkwidgetprivate.h"
36 #include "gtkboxlayout.h"
37 
38 
39 /**
40  * GtkScrollbar:
41  *
42  * The `GtkScrollbar` widget is a horizontal or vertical scrollbar.
43  *
44  * ![An example GtkScrollbar](scrollbar.png)
45  *
46  * Its position and movement are controlled by the adjustment that is passed to
47  * or created by [ctor@Gtk.Scrollbar.new]. See [class@Gtk.Adjustment] for more
48  * details. The [property@Gtk.Adjustment:value] field sets the position of the
49  * thumb and must be between [property@Gtk.Adjustment:lower] and
50  * [property@Gtk.Adjustment:upper] - [property@Gtk.Adjustment:page-size].
51  * The [property@Gtk.Adjustment:page-size] represents the size of the visible
52  * scrollable area.
53  *
54  * The fields [property@Gtk.Adjustment:step-increment] and
55  * [property@Gtk.Adjustment:page-increment] fields are added to or subtracted
56  * from the [property@Gtk.Adjustment:value] when the user asks to move by a step
57  * (using e.g. the cursor arrow keys) or by a page (using e.g. the Page Down/Up
58  * keys).
59  *
60  * # CSS nodes
61  *
62  * ```
63  * scrollbar
64  * ╰── range[.fine-tune]
65  *     ╰── trough
66  *         ╰── slider
67  * ```
68  *
69  * `GtkScrollbar` has a main CSS node with name scrollbar and a subnode for its
70  * contents. The main node gets the .horizontal or .vertical style classes applied,
71  * depending on the scrollbar's orientation.
72  *
73  * The range node gets the style class .fine-tune added when the scrollbar is
74  * in 'fine-tuning' mode.
75  *
76  * Other style classes that may be added to scrollbars inside
77  * [class@Gtk.ScrolledWindow] include the positional classes (.left, .right,
78  * .top, .bottom) and style classes related to overlay scrolling (.overlay-indicator,
79  * .dragging, .hovering).
80  *
81  * # Accessibility
82  *
83  * `GtkScrollbar` uses the %GTK_ACCESSIBLE_ROLE_SCROLLBAR role.
84  */
85 
86 typedef struct _GtkScrollbarClass   GtkScrollbarClass;
87 
88 struct _GtkScrollbar
89 {
90   GtkWidget parent_instance;
91 };
92 
93 struct _GtkScrollbarClass
94 {
95   GtkWidgetClass parent_class;
96 };
97 
98 typedef struct {
99   GtkOrientation orientation;
100   GtkWidget *range;
101 } GtkScrollbarPrivate;
102 
103 enum {
104   PROP_0,
105   PROP_ADJUSTMENT,
106 
107   PROP_ORIENTATION,
108   LAST_PROP = PROP_ORIENTATION
109 };
110 
111 G_DEFINE_TYPE_WITH_CODE (GtkScrollbar, gtk_scrollbar, GTK_TYPE_WIDGET,
112                          G_ADD_PRIVATE (GtkScrollbar)
113                          G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL))
114 
115 
116 static GParamSpec *props[LAST_PROP] = { NULL, };
117 
118 static void
gtk_scrollbar_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)119 gtk_scrollbar_get_property (GObject    *object,
120                             guint       property_id,
121                             GValue     *value,
122                             GParamSpec *pspec)
123 {
124   GtkScrollbar *self = GTK_SCROLLBAR (object);
125   GtkScrollbarPrivate *priv = gtk_scrollbar_get_instance_private (self);
126 
127   switch (property_id)
128    {
129     case PROP_ADJUSTMENT:
130       g_value_set_object (value, gtk_scrollbar_get_adjustment (self));
131       break;
132     case PROP_ORIENTATION:
133       g_value_set_enum (value, priv->orientation);
134       break;
135     default:
136       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
137       break;
138     }
139 }
140 
141 static void
gtk_scrollbar_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)142 gtk_scrollbar_set_property (GObject      *object,
143                             guint         property_id,
144                             const GValue *value,
145                             GParamSpec   *pspec)
146 {
147   GtkScrollbar *self = GTK_SCROLLBAR (object);
148   GtkScrollbarPrivate *priv = gtk_scrollbar_get_instance_private (self);
149 
150   switch (property_id)
151     {
152     case PROP_ADJUSTMENT:
153       gtk_scrollbar_set_adjustment (self, g_value_get_object (value));
154       break;
155     case PROP_ORIENTATION:
156       {
157         GtkOrientation orientation = g_value_get_enum (value);
158 
159         if (orientation != priv->orientation)
160           {
161             GtkLayoutManager *layout = gtk_widget_get_layout_manager (GTK_WIDGET (self));
162             gtk_orientable_set_orientation (GTK_ORIENTABLE (layout), orientation);
163             gtk_orientable_set_orientation (GTK_ORIENTABLE (priv->range), orientation);
164             priv->orientation = orientation;
165             gtk_widget_update_orientation (GTK_WIDGET (self), priv->orientation);
166             gtk_widget_queue_resize (GTK_WIDGET (self));
167             g_object_notify_by_pspec (object, pspec);
168             gtk_accessible_update_property (GTK_ACCESSIBLE (self),
169                                             GTK_ACCESSIBLE_PROPERTY_ORIENTATION, orientation,
170                                             -1);
171           }
172       }
173       break;
174     default:
175       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
176       break;
177     }
178 }
179 
180 static void gtk_scrollbar_adjustment_changed       (GtkAdjustment *adjustment,
181                                                     gpointer       data);
182 static void gtk_scrollbar_adjustment_value_changed (GtkAdjustment *adjustment,
183                                                     gpointer       data);
184 
185 static void
gtk_scrollbar_dispose(GObject * object)186 gtk_scrollbar_dispose (GObject *object)
187 {
188   GtkScrollbar *self = GTK_SCROLLBAR (object);
189   GtkScrollbarPrivate *priv = gtk_scrollbar_get_instance_private (self);
190   GtkAdjustment *adj;
191 
192   adj = gtk_range_get_adjustment (GTK_RANGE (priv->range));
193   if (adj)
194     {
195       g_signal_handlers_disconnect_by_func (adj, gtk_scrollbar_adjustment_changed, self);
196       g_signal_handlers_disconnect_by_func (adj, gtk_scrollbar_adjustment_value_changed, self);
197     }
198 
199   g_clear_pointer (&priv->range, gtk_widget_unparent);
200 
201   G_OBJECT_CLASS (gtk_scrollbar_parent_class)->dispose (object);
202 }
203 
204 static void
gtk_scrollbar_class_init(GtkScrollbarClass * class)205 gtk_scrollbar_class_init (GtkScrollbarClass *class)
206 {
207   GObjectClass *object_class = G_OBJECT_CLASS (class);
208   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
209 
210   object_class->get_property = gtk_scrollbar_get_property;
211   object_class->set_property = gtk_scrollbar_set_property;
212   object_class->dispose = gtk_scrollbar_dispose;
213 
214   /**
215    * GtkScrollbar:adjustment: (attributes org.gtk.Property.get=gtk_scrollbar_get_adjustment org.gtk.Property.set=gtk_scrollbar_set_adjustment)
216    *
217    * The `GtkAdjustment` controlled by this scrollbar.
218    */
219   props[PROP_ADJUSTMENT] =
220       g_param_spec_object ("adjustment",
221                            P_("Adjustment"),
222                            P_("The GtkAdjustment that contains the current value of this scrollbar"),
223                            GTK_TYPE_ADJUSTMENT,
224                            GTK_PARAM_READWRITE|G_PARAM_CONSTRUCT);
225 
226   g_object_class_install_properties (object_class, LAST_PROP, props);
227 
228   g_object_class_override_property (object_class, PROP_ORIENTATION, "orientation");
229 
230   gtk_widget_class_set_css_name (widget_class, I_("scrollbar"));
231   gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BOX_LAYOUT);
232   gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_SCROLLBAR);
233 }
234 
235 static void
gtk_scrollbar_init(GtkScrollbar * self)236 gtk_scrollbar_init (GtkScrollbar *self)
237 {
238   GtkScrollbarPrivate *priv = gtk_scrollbar_get_instance_private (self);
239 
240   priv->orientation = GTK_ORIENTATION_HORIZONTAL;
241 
242   priv->range = g_object_new (GTK_TYPE_RANGE, NULL);
243   gtk_widget_set_hexpand (priv->range, TRUE);
244   gtk_widget_set_vexpand (priv->range, TRUE);
245   gtk_widget_set_parent (priv->range, GTK_WIDGET (self));
246   gtk_widget_update_orientation (GTK_WIDGET (self), priv->orientation);
247   gtk_accessible_update_property (GTK_ACCESSIBLE (self),
248                                   GTK_ACCESSIBLE_PROPERTY_ORIENTATION, priv->orientation,
249                                   -1);
250 }
251 
252 /**
253  * gtk_scrollbar_new:
254  * @orientation: the scrollbar’s orientation.
255  * @adjustment: (nullable): the [class@Gtk.Adjustment] to use, or %NULL
256  *   to create a new adjustment.
257  *
258  * Creates a new scrollbar with the given orientation.
259  *
260  * Returns:  the new `GtkScrollbar`.
261  */
262 GtkWidget *
gtk_scrollbar_new(GtkOrientation orientation,GtkAdjustment * adjustment)263 gtk_scrollbar_new (GtkOrientation  orientation,
264                    GtkAdjustment  *adjustment)
265 {
266   g_return_val_if_fail (adjustment == NULL || GTK_IS_ADJUSTMENT (adjustment),
267                         NULL);
268 
269   return g_object_new (GTK_TYPE_SCROLLBAR,
270                        "orientation", orientation,
271                        "adjustment",  adjustment,
272                        NULL);
273 }
274 
275 static void
gtk_scrollbar_adjustment_changed(GtkAdjustment * adjustment,gpointer data)276 gtk_scrollbar_adjustment_changed (GtkAdjustment *adjustment,
277                                   gpointer       data)
278 {
279   GtkScrollbar *self = data;
280 
281   gtk_accessible_update_property (GTK_ACCESSIBLE (self),
282                                   GTK_ACCESSIBLE_PROPERTY_VALUE_MAX, gtk_adjustment_get_upper (adjustment),
283                                   GTK_ACCESSIBLE_PROPERTY_VALUE_MIN, gtk_adjustment_get_lower (adjustment),
284                                   -1);
285 }
286 
287 static void
gtk_scrollbar_adjustment_value_changed(GtkAdjustment * adjustment,gpointer data)288 gtk_scrollbar_adjustment_value_changed (GtkAdjustment *adjustment,
289                                         gpointer       data)
290 {
291   GtkScrollbar *self = data;
292 
293   gtk_accessible_update_property (GTK_ACCESSIBLE (self),
294                                   GTK_ACCESSIBLE_PROPERTY_VALUE_NOW, gtk_adjustment_get_value (adjustment),
295                                   -1);
296 }
297 
298 /**
299  * gtk_scrollbar_set_adjustment: (attributes org.gtk.Method.set_property=adjustment)
300  * @self: a `GtkScrollbar`
301  * @adjustment: (nullable): the adjustment to set
302  *
303  * Makes the scrollbar use the given adjustment.
304  */
305 void
gtk_scrollbar_set_adjustment(GtkScrollbar * self,GtkAdjustment * adjustment)306 gtk_scrollbar_set_adjustment (GtkScrollbar  *self,
307                               GtkAdjustment *adjustment)
308 {
309   GtkScrollbarPrivate *priv = gtk_scrollbar_get_instance_private (self);
310   GtkAdjustment *adj;
311 
312   g_return_if_fail (GTK_IS_SCROLLBAR (self));
313   g_return_if_fail (adjustment == NULL || GTK_IS_ADJUSTMENT (adjustment));
314 
315   adj = gtk_range_get_adjustment (GTK_RANGE (priv->range));
316   if (adj)
317     {
318       g_signal_handlers_disconnect_by_func (adj, gtk_scrollbar_adjustment_changed, self);
319       g_signal_handlers_disconnect_by_func (adj, gtk_scrollbar_adjustment_value_changed, self);
320     }
321 
322   gtk_range_set_adjustment (GTK_RANGE (priv->range), adjustment);
323 
324   if (adjustment)
325     {
326       g_signal_connect (adjustment, "changed",
327                         G_CALLBACK (gtk_scrollbar_adjustment_changed), self);
328       g_signal_connect (adjustment, "value-changed",
329                         G_CALLBACK (gtk_scrollbar_adjustment_value_changed), self);
330 
331       gtk_accessible_update_property (GTK_ACCESSIBLE (self),
332                                       GTK_ACCESSIBLE_PROPERTY_VALUE_MAX, gtk_adjustment_get_upper (adjustment),
333                                       GTK_ACCESSIBLE_PROPERTY_VALUE_MIN, gtk_adjustment_get_lower (adjustment),
334                                       GTK_ACCESSIBLE_PROPERTY_VALUE_NOW, gtk_adjustment_get_value (adjustment),
335                                       -1);
336     }
337 
338   g_object_notify_by_pspec (G_OBJECT (self), props[PROP_ADJUSTMENT]);
339 }
340 
341 /**
342  * gtk_scrollbar_get_adjustment: (attributes org.gtk.Method.get_property=adjustment)
343  * @self: a `GtkScrollbar`
344  *
345  * Returns the scrollbar's adjustment.
346  *
347  * Returns: (transfer none): the scrollbar's adjustment
348  */
349 GtkAdjustment *
gtk_scrollbar_get_adjustment(GtkScrollbar * self)350 gtk_scrollbar_get_adjustment (GtkScrollbar  *self)
351 {
352   GtkScrollbarPrivate *priv = gtk_scrollbar_get_instance_private (self);
353 
354   g_return_val_if_fail (GTK_IS_SCROLLBAR (self), NULL);
355 
356   if (priv->range)
357     return gtk_range_get_adjustment (GTK_RANGE (priv->range));
358 
359   return NULL;
360 }
361