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