1 /* font-manager-font-scale.c
2 *
3 * Copyright (C) 2009 - 2021 Jerry Casiano
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program 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
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program.
17 *
18 * If not, see <http://www.gnu.org/licenses/gpl-3.0.txt>.
19 */
20
21 #include "font-manager-font-scale.h"
22
23 /**
24 * SECTION: font-manager-font-scale
25 * @short_description: Font size selection widget
26 * @title: Font Scale
27 * @include: font-manager-font-scale.h
28 *
29 * Widget allowing for font size selection through use of a #GtkScale,
30 * #GtkSpinButton and two clickable #GtkLabel widgets for quick selection
31 * of minimum and maximum sizes.
32 */
33
34 #define DEFAULT_OPACITY 0.60
35 #define FOCUSED_OPACITY 0.95
36 #define MIN_FONT_SIZE FONT_MANAGER_MIN_FONT_SIZE
37 #define MAX_FONT_SIZE FONT_MANAGER_MAX_FONT_SIZE
38 #define DEFAULT_PREVIEW_SIZE FONT_MANAGER_DEFAULT_PREVIEW_SIZE
39 #define MIN_LABEL "<span font=\"Serif Italic Bold\" size=\"small\"> A </span>"
40 #define MAX_LABEL "<span font=\"Serif Italic Bold\" size=\"large\"> A </span>"
41
42 struct _FontManagerFontScale
43 {
44 GtkEventBox parent_instance;
45
46 GtkWidget *min;
47 GtkWidget *max;
48 GtkWidget *scale;
49 GtkWidget *spin;
50 GtkAdjustment *adjustment;
51 };
52
53 G_DEFINE_TYPE(FontManagerFontScale, font_manager_font_scale, GTK_TYPE_EVENT_BOX)
54
55 enum
56 {
57 PROP_RESERVED,
58 PROP_VALUE,
59 PROP_ADJUSTMENT,
60 N_PROPERTIES
61 };
62
63 static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };
64
65 static void
font_manager_font_scale_dispose(GObject * gobject)66 font_manager_font_scale_dispose (GObject *gobject)
67 {
68 g_return_if_fail(gobject != NULL);
69 FontManagerFontScale *self = FONT_MANAGER_FONT_SCALE(gobject);
70 g_clear_object(&self->adjustment);
71 G_OBJECT_CLASS(font_manager_font_scale_parent_class)->dispose(gobject);
72 return;
73 }
74
75 static void
font_manager_font_scale_finalize(GObject * gobject)76 font_manager_font_scale_finalize (GObject *gobject)
77 {
78 g_return_if_fail(gobject != NULL);
79 FontManagerFontScale *self = FONT_MANAGER_FONT_SCALE(gobject);
80 g_clear_object(&self->adjustment);
81 G_OBJECT_CLASS(font_manager_font_scale_parent_class)->finalize(gobject);
82 return;
83 }
84
85 static void
font_manager_font_scale_get_property(GObject * gobject,guint property_id,GValue * value,GParamSpec * pspec)86 font_manager_font_scale_get_property (GObject *gobject,
87 guint property_id,
88 GValue *value,
89 GParamSpec *pspec)
90 {
91 g_return_if_fail(gobject != NULL);
92 FontManagerFontScale *self = FONT_MANAGER_FONT_SCALE(gobject);
93 switch (property_id) {
94 case PROP_ADJUSTMENT:
95 g_value_set_object(value, font_manager_font_scale_get_adjustment(self));
96 break;
97 case PROP_VALUE:
98 g_value_set_double(value, font_manager_font_scale_get_value(self));
99 break;
100 default:
101 G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, property_id, pspec);
102 }
103 return;
104 }
105
106 static void
font_manager_font_scale_set_property(GObject * gobject,guint property_id,const GValue * value,GParamSpec * pspec)107 font_manager_font_scale_set_property (GObject *gobject,
108 guint property_id,
109 const GValue *value,
110 GParamSpec *pspec)
111 {
112 g_return_if_fail(gobject != NULL);
113 FontManagerFontScale *self = FONT_MANAGER_FONT_SCALE(gobject);
114 switch (property_id) {
115 case PROP_ADJUSTMENT:
116 font_manager_font_scale_set_adjustment(self, g_value_get_object(value));
117 break;
118 case PROP_VALUE:
119 font_manager_font_scale_set_value(self, g_value_get_double(value));
120 break;
121 default:
122 G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, property_id, pspec);
123 }
124 return;
125 }
126
127 static void
font_manager_font_scale_class_init(FontManagerFontScaleClass * klass)128 font_manager_font_scale_class_init (FontManagerFontScaleClass *klass)
129 {
130 GObjectClass *object_class = G_OBJECT_CLASS(klass);
131
132 object_class->dispose = font_manager_font_scale_dispose;
133 object_class->finalize = font_manager_font_scale_finalize;
134 object_class->get_property = font_manager_font_scale_get_property;
135 object_class->set_property = font_manager_font_scale_set_property;
136
137 /**
138 * FontManagerFontScale:value:
139 *
140 * The current value of #FontManagerFontScale.
141 */
142 obj_properties[PROP_VALUE] = g_param_spec_double("value",
143 NULL,
144 "Current value",
145 MIN_FONT_SIZE,
146 MAX_FONT_SIZE,
147 DEFAULT_PREVIEW_SIZE,
148 G_PARAM_STATIC_STRINGS |
149 G_PARAM_READWRITE |
150 G_PARAM_EXPLICIT_NOTIFY);
151
152 /**
153 * FontManagerFontScale:adjustment:
154 *
155 * The #GtkAdjustment in use.
156 */
157 obj_properties[PROP_ADJUSTMENT] = g_param_spec_object("adjustment",
158 NULL,
159 "#GtkAdjustment in use",
160 GTK_TYPE_ADJUSTMENT,
161 G_PARAM_STATIC_STRINGS |
162 G_PARAM_READWRITE |
163 G_PARAM_EXPLICIT_NOTIFY);
164
165 g_object_class_install_properties(object_class, N_PROPERTIES, obj_properties);
166 return;
167 }
168
169 static gboolean
on_button_press_event(GtkWidget * wrapper,GdkEvent * event,FontManagerFontScale * self)170 on_button_press_event (GtkWidget *wrapper, GdkEvent *event, FontManagerFontScale *self)
171 {
172 GtkWidget *widget = gtk_bin_get_child(GTK_BIN(wrapper));
173 gdouble new_value = (widget == self->min) ? MIN_FONT_SIZE : MAX_FONT_SIZE;
174 gtk_adjustment_set_value(self->adjustment, new_value);
175 return FALSE;
176 }
177
178 static gboolean
on_enter_event(GtkWidget * wrapper,GdkEvent * event,GtkWidget * widget)179 on_enter_event (GtkWidget *wrapper, GdkEvent *event, GtkWidget *widget)
180 {
181 gtk_widget_set_opacity(widget, FOCUSED_OPACITY);
182 return FALSE;
183 }
184
185 static gboolean
on_leave_event(GtkWidget * wrapper,GdkEvent * event,GtkWidget * widget)186 on_leave_event (GtkWidget *wrapper, GdkEvent *event, GtkWidget *widget)
187 {
188 gtk_widget_set_opacity(widget, DEFAULT_OPACITY);
189 return FALSE;
190 }
191
192 GtkWidget *
get_reactive_widget(FontManagerFontScale * self,GtkWidget * widget)193 get_reactive_widget (FontManagerFontScale *self, GtkWidget *widget)
194 {
195 GtkWidget *reactive = gtk_event_box_new();
196 gtk_widget_add_events(reactive, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
197 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
198 GDK_STRUCTURE_MASK);
199 gtk_container_add(GTK_CONTAINER(reactive), widget);
200 g_signal_connect(reactive, "enter-notify-event", G_CALLBACK(on_enter_event), widget);
201 g_signal_connect(reactive, "leave-notify-event", G_CALLBACK(on_leave_event), widget);
202 g_signal_connect(reactive, "button-press-event", G_CALLBACK(on_button_press_event), self);
203 gtk_widget_set_opacity(widget, DEFAULT_OPACITY);
204 return reactive;
205 }
206
207 static void
font_manager_font_scale_init(FontManagerFontScale * self)208 font_manager_font_scale_init (FontManagerFontScale *self)
209 {
210 g_return_if_fail(self != NULL);
211 GtkStyleContext *ctx = gtk_widget_get_style_context(GTK_WIDGET(self));
212 gtk_style_context_add_class(ctx, GTK_STYLE_CLASS_VIEW);
213 gtk_widget_set_name(GTK_WIDGET(self), "FontManagerFontScale");
214 GtkWidget *box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
215 self->min = gtk_label_new(NULL);
216 self->max = gtk_label_new(NULL);
217 self->scale = gtk_scale_new(GTK_ORIENTATION_HORIZONTAL, NULL);
218 self->spin = gtk_spin_button_new(NULL, 0.5, 1);
219 self->adjustment = gtk_adjustment_new(DEFAULT_PREVIEW_SIZE, MIN_FONT_SIZE, MAX_FONT_SIZE, 0.5, 1.0, 0);
220 self->adjustment = g_object_ref_sink(self->adjustment);
221 font_manager_font_scale_set_adjustment(self, self->adjustment);
222 GtkWidget *min = get_reactive_widget(self, self->min);
223 GtkWidget *max = get_reactive_widget(self, self->max);
224 gtk_box_pack_start(GTK_BOX(box), min, FALSE, FALSE, 1);
225 gtk_box_pack_start(GTK_BOX(box), self->scale, TRUE, TRUE, 1);
226 gtk_box_pack_start(GTK_BOX(box), max, FALSE, FALSE, FONT_MANAGER_DEFAULT_MARGIN);
227 gtk_box_pack_start(GTK_BOX(box), self->spin, FALSE, FALSE, 1);
228 gtk_widget_set_hexpand(self->scale, TRUE);
229 gtk_widget_set_halign(self->scale, GTK_ALIGN_FILL);
230 gtk_scale_set_draw_value(GTK_SCALE(self->scale), FALSE);
231 gtk_label_set_markup(GTK_LABEL(self->min), MIN_LABEL);
232 gtk_label_set_markup(GTK_LABEL(self->max), MAX_LABEL);
233 gtk_widget_set_hexpand(GTK_WIDGET(self), TRUE);
234 gtk_widget_set_valign(GTK_WIDGET(self), GTK_ALIGN_END);
235 gtk_widget_set_can_focus(self->spin, FALSE);
236 gtk_container_add(GTK_CONTAINER(self), box);
237 font_manager_widget_set_margin(box, FONT_MANAGER_DEFAULT_MARGIN);
238 gtk_widget_show_all(box);
239 GBindingFlags flags = G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL;
240 g_object_bind_property(self, "value", self->adjustment, "value", flags);
241 return;
242 }
243
244 /**
245 * font_manager_font_scale_get_adjustment:
246 * @self: #FontManagerFontScale
247 *
248 * Returns: (transfer none) (nullable): The #GtkAdjustment currently in use or %NULL.
249 */
250 GtkAdjustment *
font_manager_font_scale_get_adjustment(FontManagerFontScale * self)251 font_manager_font_scale_get_adjustment (FontManagerFontScale *self)
252 {
253 g_return_val_if_fail(self != NULL, NULL);
254 return self->adjustment;
255 }
256
257 /**
258 * font_manager_font_scale_set_adjustment:
259 * @self: #FontManagerFontScale
260 * @adjustment: #GtkAdjustment to use
261 */
262 void
font_manager_font_scale_set_adjustment(FontManagerFontScale * self,GtkAdjustment * adjustment)263 font_manager_font_scale_set_adjustment (FontManagerFontScale *self, GtkAdjustment *adjustment)
264 {
265 g_return_if_fail(self != NULL);
266 if (g_set_object(&self->adjustment, adjustment))
267 g_object_notify_by_pspec(G_OBJECT(self), obj_properties[PROP_ADJUSTMENT]);
268 gtk_range_set_adjustment(GTK_RANGE(self->scale), self->adjustment);
269 gtk_spin_button_set_adjustment(GTK_SPIN_BUTTON(self->spin), self->adjustment);
270 return;
271 }
272
273 /**
274 * font_manager_font_scale_get_value:
275 * @self: #FontManagerFontScale
276 *
277 * Returns: The current value.
278 */
279 gdouble
font_manager_font_scale_get_value(FontManagerFontScale * self)280 font_manager_font_scale_get_value (FontManagerFontScale *self)
281 {
282 g_return_val_if_fail(self != NULL && self->adjustment != NULL, -1);
283 return gtk_adjustment_get_value(self->adjustment);
284 }
285
286 /**
287 * font_manager_font_scale_set_value:
288 * @self: #FontManagerFontScale
289 * @value: New value
290 */
291 void
font_manager_font_scale_set_value(FontManagerFontScale * self,gdouble value)292 font_manager_font_scale_set_value (FontManagerFontScale *self, gdouble value)
293 {
294 g_return_if_fail(self != NULL && self->adjustment != NULL);
295 gtk_adjustment_set_value(self->adjustment, CLAMP(value, MIN_FONT_SIZE, MAX_FONT_SIZE));
296 g_object_notify_by_pspec(G_OBJECT(self), obj_properties[PROP_VALUE]);
297 return;
298 }
299
300 /**
301 * font_manager_font_scale_new:
302 *
303 * Returns: (transfer full): A newly created #FontManagerFontScale.
304 * Free the returned object using #g_object_unref().
305 */
306 GtkWidget *
font_manager_font_scale_new(void)307 font_manager_font_scale_new (void)
308 {
309 return g_object_new(FONT_MANAGER_TYPE_FONT_SCALE, NULL);
310 }
311