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
86typedef struct _GtkScrollbarClass GtkScrollbarClass;
87
88struct _GtkScrollbar
89{
90 GtkWidget parent_instance;
91};
92
93struct _GtkScrollbarClass
94{
95 GtkWidgetClass parent_class;
96};
97
98typedef struct {
99 GtkOrientation orientation;
100 GtkWidget *range;
101} GtkScrollbarPrivate;
102
103enum {
104 PROP_0,
105 PROP_ADJUSTMENT,
106
107 PROP_ORIENTATION,
108 LAST_PROP = PROP_ORIENTATION
109};
110
111G_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
116static GParamSpec *props[LAST_PROP] = { NULL, };
117
118static void
119gtk_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, v_object: gtk_scrollbar_get_adjustment (self));
131 break;
132 case PROP_ORIENTATION:
133 g_value_set_enum (value, v_enum: priv->orientation);
134 break;
135 default:
136 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
137 break;
138 }
139}
140
141static void
142gtk_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, adjustment: 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), orientation: priv->orientation);
166 gtk_widget_queue_resize (GTK_WIDGET (self));
167 g_object_notify_by_pspec (object, pspec);
168 gtk_accessible_update_property (self: GTK_ACCESSIBLE (ptr: self),
169 first_property: 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
180static void gtk_scrollbar_adjustment_changed (GtkAdjustment *adjustment,
181 gpointer data);
182static void gtk_scrollbar_adjustment_value_changed (GtkAdjustment *adjustment,
183 gpointer data);
184
185static void
186gtk_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
204static void
205gtk_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 (name: "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 (oclass: object_class, n_pspecs: LAST_PROP, pspecs: props);
227
228 g_object_class_override_property (oclass: object_class, property_id: PROP_ORIENTATION, name: "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, accessible_role: GTK_ACCESSIBLE_ROLE_SCROLLBAR);
233}
234
235static void
236gtk_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 (widget: priv->range, TRUE);
244 gtk_widget_set_vexpand (widget: priv->range, TRUE);
245 gtk_widget_set_parent (widget: priv->range, GTK_WIDGET (self));
246 gtk_widget_update_orientation (GTK_WIDGET (self), orientation: priv->orientation);
247 gtk_accessible_update_property (self: GTK_ACCESSIBLE (ptr: self),
248 first_property: 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 */
262GtkWidget *
263gtk_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 first_property_name: "orientation", orientation,
271 "adjustment", adjustment,
272 NULL);
273}
274
275static void
276gtk_scrollbar_adjustment_changed (GtkAdjustment *adjustment,
277 gpointer data)
278{
279 GtkScrollbar *self = data;
280
281 gtk_accessible_update_property (self: GTK_ACCESSIBLE (ptr: self),
282 first_property: 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
287static void
288gtk_scrollbar_adjustment_value_changed (GtkAdjustment *adjustment,
289 gpointer data)
290{
291 GtkScrollbar *self = data;
292
293 gtk_accessible_update_property (self: GTK_ACCESSIBLE (ptr: self),
294 first_property: 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 */
305void
306gtk_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 (self: GTK_ACCESSIBLE (ptr: self),
332 first_property: 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), pspec: 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 */
349GtkAdjustment *
350gtk_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

source code of gtk/gtk/gtkscrollbar.c