1 /* radio button widget
2 *
3 * Copyright (C) 2013-2016 Robin Gareus <robin@gareus.org>
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 2, or (at your option)
8 * 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; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19
20 #ifndef _ROB_TK_MBTN_H_
21 #define _ROB_TK_MBTN_H_
22
23 #define MBT_LED_RADIUS (11.0)
24
25 typedef struct {
26 RobWidget* rw;
27
28 bool sensitive;
29 bool prelight;
30
31 bool (*cb) (RobWidget* w, void* handle);
32 void* handle;
33
34 int num_mode;
35 int cur_mode;
36 int tog_mode;
37 int dfl_mode;
38
39 cairo_pattern_t* btn_enabled;
40 cairo_pattern_t* btn_inactive;
41 cairo_pattern_t* btn_led;
42
43 float w_width, w_height;
44 float *c_led;
45 } RobTkMBtn;
46
47 /******************************************************************************
48 * some helpers
49 */
50
robtk_mbtn_update_mode(RobTkMBtn * d,int mode)51 static void robtk_mbtn_update_mode(RobTkMBtn * d, int mode) {
52 if (mode != d->cur_mode && mode >=0 && mode <= d->num_mode) {
53 d->cur_mode = mode;
54 if (d->cb) d->cb(d->rw, d->handle);
55 queue_draw(d->rw);
56 }
57 }
58
create_mbtn_pattern(RobTkMBtn * d)59 static void create_mbtn_pattern(RobTkMBtn * d) {
60 float c_bg[4]; get_color_from_theme(1, c_bg);
61 d->btn_inactive = cairo_pattern_create_linear (0.0, 0.0, 0.0, d->w_height);
62 cairo_pattern_add_color_stop_rgb (d->btn_inactive, ISBRIGHT(c_bg) ? 0.5 : 0.0, SHADE_RGB(c_bg, 1.95));
63 cairo_pattern_add_color_stop_rgb (d->btn_inactive, ISBRIGHT(c_bg) ? 0.0 : 0.5, SHADE_RGB(c_bg, 0.75));
64
65 d->btn_enabled = cairo_pattern_create_linear (0.0, 0.0, 0.0, d->w_height);
66 cairo_pattern_add_color_stop_rgb (d->btn_enabled, ISBRIGHT(c_bg) ? 0.5 : 0.0, SHADE_RGB(c_bg, .95));
67 cairo_pattern_add_color_stop_rgb (d->btn_enabled, ISBRIGHT(c_bg) ? 0.0 : 0.5, SHADE_RGB(c_bg, 2.4));
68
69 d->btn_led = cairo_pattern_create_linear (0.0, 0.0, 0.0, MBT_LED_RADIUS);
70 cairo_pattern_add_color_stop_rgba (d->btn_led, 0.0, 0.0, 0.0, 0.0, 0.4);
71 cairo_pattern_add_color_stop_rgba (d->btn_led, 1.0, 1.0, 1.0, 1.0 , 0.7);
72 }
73
74
75 /******************************************************************************
76 * main drawing callback
77 */
78
robtk_mbtn_expose_event(RobWidget * handle,cairo_t * cr,cairo_rectangle_t * ev)79 static bool robtk_mbtn_expose_event(RobWidget* handle, cairo_t* cr, cairo_rectangle_t* ev) {
80 RobTkMBtn * d = (RobTkMBtn *)GET_HANDLE(handle);
81 cairo_rectangle (cr, ev->x, ev->y, ev->width, ev->height);
82 cairo_clip (cr);
83 cairo_scale (cr, d->rw->widget_scale, d->rw->widget_scale);
84
85 float led_r, led_g, led_b; // TODO consolidate with c[]
86
87 cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
88
89 float c[4];
90 get_color_from_theme(1, c);
91 cairo_set_source_rgb (cr, c[0], c[1], c[2]);
92 cairo_rectangle (cr, 0, 0, d->w_width, d->w_height);
93 cairo_fill(cr);
94
95 cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
96
97 if (!d->sensitive) {
98 led_r = c[0]; led_g = c[1]; led_b = c[2];
99 } else {
100 const int m = d->cur_mode;
101 led_r = d->c_led[m*3+0]; led_g = d->c_led[m*3+1]; led_b = d->c_led[m*3+2];
102 }
103
104 if (d->cur_mode > 0) {
105 cairo_set_source(cr, d->btn_enabled);
106 } else if (!d->sensitive) {
107 cairo_set_source_rgb (cr, c[0], c[1], c[2]);
108 } else {
109 cairo_set_source(cr, d->btn_inactive);
110 }
111
112 rounded_rectangle(cr, 2.5, 2.5, d->w_width - 4, d->w_height -4, C_RAD);
113 cairo_fill_preserve (cr);
114 if (!d->sensitive && d->cur_mode > 0) {
115 cairo_set_source_rgba (cr, c[0], c[1], c[2], .6);
116 cairo_fill_preserve (cr);
117 }
118 cairo_set_line_width (cr, .75);
119 cairo_set_source_rgba (cr, .0, .0, .0, 1.0);
120 cairo_stroke(cr);
121
122 cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
123 cairo_save(cr);
124 cairo_translate(cr, MBT_LED_RADIUS/2 + 7, d->w_height/2.0 + 1);
125
126 cairo_set_source (cr, d->btn_led);
127 cairo_arc (cr, 0, 0, MBT_LED_RADIUS/2, 0, 2 * M_PI);
128 cairo_fill(cr);
129
130 cairo_set_source_rgb (cr, 0, 0, 0);
131 cairo_arc (cr, 0, 0, MBT_LED_RADIUS/2 - 2, 0, 2 * M_PI);
132 cairo_fill(cr);
133 cairo_set_source_rgba (cr, led_r, led_g, led_b, 1.0);
134 cairo_arc (cr, 0, 0, MBT_LED_RADIUS/2 - 3, 0, 2 * M_PI);
135 cairo_fill(cr);
136 cairo_restore(cr);
137
138
139 if (d->sensitive && d->prelight) {
140 cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
141 if (ISBRIGHT(c)) {
142 cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, .1);
143 } else {
144 cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, .1);
145 }
146 rounded_rectangle(cr, 2.5, 2.5, d->w_width - 4, d->w_height -4, C_RAD);
147 cairo_fill_preserve(cr);
148 cairo_set_line_width (cr, .75);
149 cairo_set_source_rgba (cr, .0, .0, .0, 1.0);
150 cairo_stroke(cr);
151 }
152 return TRUE;
153 }
154
155
156 /******************************************************************************
157 * UI callbacks
158 */
159
robtk_mbtn_mouseup(RobWidget * handle,RobTkBtnEvent * ev)160 static RobWidget* robtk_mbtn_mouseup(RobWidget *handle, RobTkBtnEvent *ev) {
161 RobTkMBtn * d = (RobTkMBtn *)GET_HANDLE(handle);
162 if (!d->sensitive) { return NULL; }
163 if (!d->prelight) { return NULL; }
164 if (ev->state & ROBTK_MOD_SHIFT) {
165 robtk_mbtn_update_mode(d, d->dfl_mode);
166 } else if (ev->state & ROBTK_MOD_CTRL) {
167 int cur = d->cur_mode;
168 robtk_mbtn_update_mode(d, d->tog_mode);
169 d->tog_mode = cur;
170 } else {
171 robtk_mbtn_update_mode(d, (d->cur_mode + 1) % d->num_mode);
172 }
173 return NULL;
174 }
175
robtk_mbtn_enter_notify(RobWidget * handle)176 static void robtk_mbtn_enter_notify(RobWidget *handle) {
177 RobTkMBtn * d = (RobTkMBtn *)GET_HANDLE(handle);
178 if (!d->prelight) {
179 d->prelight = TRUE;
180 queue_draw(d->rw);
181 }
182 }
183
robtk_mbtn_leave_notify(RobWidget * handle)184 static void robtk_mbtn_leave_notify(RobWidget *handle) {
185 RobTkMBtn * d = (RobTkMBtn *)GET_HANDLE(handle);
186 if (d->prelight) {
187 d->prelight = FALSE;
188 queue_draw(d->rw);
189 }
190 }
191
192 /******************************************************************************
193 * RobWidget stuff
194 */
195
196 static void
priv_mbtn_size_request(RobWidget * handle,int * w,int * h)197 priv_mbtn_size_request(RobWidget* handle, int *w, int *h) {
198 RobTkMBtn* d = (RobTkMBtn*)GET_HANDLE(handle);
199 *w = d->w_width * d->rw->widget_scale;
200 *h = d->w_height * d->rw->widget_scale;
201 }
202
203
204 /******************************************************************************
205 * public functions
206 */
207
robtk_mbtn_new(int modes)208 static RobTkMBtn * robtk_mbtn_new(int modes) {
209 RobTkMBtn *d = (RobTkMBtn *) malloc(sizeof(RobTkMBtn));
210 assert(modes > 1);
211
212 d->cb = NULL;
213 d->handle = NULL;
214 d->sensitive = TRUE;
215 d->prelight = FALSE;
216 d->num_mode = modes;
217 d->cur_mode = 0;
218 d->tog_mode = 0;
219 d->dfl_mode = 0;
220
221 d->c_led = (float*) malloc(3 * modes * sizeof(float));
222 d->c_led[0] = d->c_led[1] = d->c_led[2] = .4;
223 for (int c = 1; c < modes; ++c) {
224 const float hue = /* (.0 / (modes-1.f)) + */ (c-1.f) / (modes-1.f) ;
225 const float sat = .95;
226 const float lum = .55;
227
228 const float cq = lum < 0.5 ? lum * (1 + sat) : lum + sat - lum * sat;
229 const float cp = 2.f * lum - cq;
230
231 d->c_led[c*3+0] = rtk_hue2rgb(cp, cq, hue + 1.f/3.f);
232 d->c_led[c*3+1] = rtk_hue2rgb(cp, cq, hue);
233 d->c_led[c*3+2] = rtk_hue2rgb(cp, cq, hue - 1.f/3.f);
234 //printf("h %d %.3f -> %.3f %.3f %.3f\n", c, hue, d->c_led[c*3+0], d->c_led[c*3+1], d->c_led[c*3+2]);
235 }
236
237 int ww, wh;
238 PangoFontDescription *fd = get_font_from_theme();
239 get_text_geometry("", fd, &ww, &wh);
240 pango_font_description_free(fd);
241
242 d->w_width = 13 + MBT_LED_RADIUS;
243 d->w_height = wh + 8;
244
245 d->rw = robwidget_new(d);
246 robwidget_set_alignment(d->rw, 0, .5);
247 ROBWIDGET_SETNAME(d->rw, "mbtn");
248
249 robwidget_set_size_request(d->rw, priv_mbtn_size_request);
250 robwidget_set_expose_event(d->rw, robtk_mbtn_expose_event);
251 robwidget_set_mouseup(d->rw, robtk_mbtn_mouseup);
252 robwidget_set_enter_notify(d->rw, robtk_mbtn_enter_notify);
253 robwidget_set_leave_notify(d->rw, robtk_mbtn_leave_notify);
254
255 create_mbtn_pattern(d);
256
257 return d;
258 }
259
robtk_mbtn_destroy(RobTkMBtn * d)260 static void robtk_mbtn_destroy(RobTkMBtn *d) {
261 robwidget_destroy(d->rw);
262 cairo_pattern_destroy(d->btn_enabled);
263 cairo_pattern_destroy(d->btn_inactive);
264 cairo_pattern_destroy(d->btn_led);
265 free(d->c_led);
266 free(d);
267 }
268
robtk_mbtn_set_alignment(RobTkMBtn * d,float x,float y)269 static void robtk_mbtn_set_alignment(RobTkMBtn *d, float x, float y) {
270 robwidget_set_alignment(d->rw, x, y);
271 }
272
robtk_mbtn_widget(RobTkMBtn * d)273 static RobWidget * robtk_mbtn_widget(RobTkMBtn *d) {
274 return d->rw;
275 }
276
robtk_mbtn_set_callback(RobTkMBtn * d,bool (* cb)(RobWidget * w,void * handle),void * handle)277 static void robtk_mbtn_set_callback(RobTkMBtn *d, bool (*cb) (RobWidget* w, void* handle), void* handle) {
278 d->cb = cb;
279 d->handle = handle;
280 }
281
robtk_mbtn_set_leds_rgb(RobTkMBtn * d,const float * c)282 static void robtk_mbtn_set_leds_rgb(RobTkMBtn *d, const float *c) {
283 memcpy(d->c_led, c, d->num_mode * 3 * sizeof(float));
284 }
285
robtk_mbtn_set_default(RobTkMBtn * d,int v)286 static void robtk_mbtn_set_default(RobTkMBtn *d, int v) {
287 d->dfl_mode = v;
288 }
289
robtk_mbtn_set_active(RobTkMBtn * d,int v)290 static void robtk_mbtn_set_active(RobTkMBtn *d, int v) {
291 robtk_mbtn_update_mode(d, v);
292 }
293
robtk_mbtn_set_sensitive(RobTkMBtn * d,bool s)294 static void robtk_mbtn_set_sensitive(RobTkMBtn *d, bool s) {
295 if (d->sensitive != s) {
296 d->sensitive = s;
297 queue_draw(d->rw);
298 }
299 }
300
robtk_mbtn_get_active(RobTkMBtn * d)301 static int robtk_mbtn_get_active(RobTkMBtn *d) {
302 return (d->cur_mode);
303 }
304 #endif
305