1 /*
2 * Copyright (C) 2019-2021 Alexandros Theodotou <alex at zrythm dot org>
3 *
4 * This file is part of Zrythm
5 *
6 * Zrythm is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Zrythm is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
15 *
16 * You should have received a copy of the GNU Affero General Public License
17 * along with Zrythm. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20 #include <stdbool.h>
21 #include <stdlib.h>
22
23 #include "gui/widgets/custom_button.h"
24 #include "utils/cairo.h"
25 #include "utils/objects.h"
26 #include "utils/ui.h"
27 #include "zrythm.h"
28 #include "zrythm_app.h"
29
30 static void
init(CustomButtonWidget * self)31 init (CustomButtonWidget * self)
32 {
33 gdk_rgba_parse (
34 &self->def_color, UI_COLOR_BUTTON_NORMAL);
35 gdk_rgba_parse (
36 &self->hovered_color, UI_COLOR_BUTTON_HOVER);
37 g_return_if_fail (UI_COLORS);
38 self->toggled_color = UI_COLORS->bright_orange;
39 self->held_color = UI_COLORS->bright_orange;
40 self->aspect = 1.0;
41 self->corner_radius = 1.6;
42 }
43
44 /**
45 * Updates the drawing caches.
46 */
47 /*static void*/
48 /*update_caches (*/
49 /*CustomButtonWidget * self)*/
50 /*{*/
51 /*}*/
52
53 /**
54 * Creates a new track widget from the given track.
55 */
56 CustomButtonWidget *
custom_button_widget_new(const char * icon_name,int size)57 custom_button_widget_new (
58 const char * icon_name,
59 int size)
60 {
61 CustomButtonWidget * self =
62 object_new (CustomButtonWidget);
63
64 strcpy (self->icon_name, icon_name);
65 self->size = size;
66 init (self);
67
68 self->icon_surface =
69 z_cairo_get_surface_from_icon_name (
70 self->icon_name, self->size - 2, 0);
71 if (!self->icon_surface)
72 {
73 g_critical (
74 "Failed to get icon surface for %s",
75 self->icon_name);
76 free (self);
77 return NULL;
78 }
79
80 return self;
81 }
82
83 static void
get_color_for_state(CustomButtonWidget * self,CustomButtonWidgetState state,GdkRGBA * c)84 get_color_for_state (
85 CustomButtonWidget * self,
86 CustomButtonWidgetState state,
87 GdkRGBA * c)
88 {
89 (void) c;
90 switch (state)
91 {
92 case CUSTOM_BUTTON_WIDGET_STATE_NORMAL:
93 *c = self->def_color;
94 break;
95 case CUSTOM_BUTTON_WIDGET_STATE_HOVERED:
96 *c = self->hovered_color;
97 break;
98 case CUSTOM_BUTTON_WIDGET_STATE_ACTIVE:
99 *c = self->held_color;
100 break;
101 case CUSTOM_BUTTON_WIDGET_STATE_TOGGLED:
102 *c = self->toggled_color;
103 break;
104 case CUSTOM_BUTTON_WIDGET_STATE_SEMI_TOGGLED:
105 *c = self->def_color;
106 break;
107 default:
108 g_warn_if_reached ();
109 }
110 }
111
112 static void
draw_bg(CustomButtonWidget * self,cairo_t * cr,double x,double y,double width,int draw_frame,CustomButtonWidgetState state)113 draw_bg (
114 CustomButtonWidget * self,
115 cairo_t * cr,
116 double x,
117 double y,
118 double width,
119 int draw_frame,
120 CustomButtonWidgetState state)
121 {
122 if (draw_frame)
123 {
124 cairo_set_source_rgba (
125 cr, 1, 1, 1, 0.4);
126 cairo_set_line_width (cr, 0.5);
127 z_cairo_rounded_rectangle (
128 cr, x, y, self->size, self->size,
129 self->aspect, self->corner_radius);
130 cairo_stroke (cr);
131 }
132
133 /* draw bg with fade from last state */
134 GdkRGBA c;
135 get_color_for_state (self, state, &c);
136 if (self->last_state != state)
137 {
138 self->transition_frames =
139 CUSTOM_BUTTON_WIDGET_MAX_TRANSITION_FRAMES;
140 }
141
142 /* draw transition if transition frames exist */
143 if (self->transition_frames)
144 {
145 GdkRGBA mid_c;
146 ui_get_mid_color (
147 &mid_c, &c, &self->last_color,
148 1.0 -
149 (double) self->transition_frames /
150 (double) CUSTOM_BUTTON_WIDGET_MAX_TRANSITION_FRAMES);
151 c = mid_c;
152 self->transition_frames--;
153 }
154 gdk_cairo_set_source_rgba (cr, &c);
155 self->last_color = c;
156
157 z_cairo_rounded_rectangle (
158 cr, x, y, width, self->size, self->aspect,
159 self->corner_radius);
160 if (state ==
161 CUSTOM_BUTTON_WIDGET_STATE_SEMI_TOGGLED)
162 {
163 cairo_fill_preserve (cr);
164 get_color_for_state (
165 self, CUSTOM_BUTTON_WIDGET_STATE_TOGGLED, &c);
166 gdk_cairo_set_source_rgba (cr, &c);
167 cairo_stroke (cr);
168 }
169 else
170 {
171 cairo_fill (cr);
172 }
173 }
174
175 static void
draw_icon_with_shadow(CustomButtonWidget * self,cairo_t * cr,double x,double y,CustomButtonWidgetState state)176 draw_icon_with_shadow (
177 CustomButtonWidget * self,
178 cairo_t * cr,
179 double x,
180 double y,
181 CustomButtonWidgetState state)
182 {
183 /* show icon with shadow */
184 cairo_set_source_rgba (
185 cr, 0, 0, 0, 0.4);
186 cairo_mask_surface (
187 cr, self->icon_surface, x + 1, y + 1);
188 cairo_fill (cr);
189
190 /* add main icon */
191 cairo_set_source_rgba (
192 cr, 1, 1, 1, 1);
193 cairo_set_source_surface (
194 cr, self->icon_surface, x + 1, y + 1);
195 cairo_paint (cr);
196 }
197
198 void
custom_button_widget_draw(CustomButtonWidget * self,cairo_t * cr,double x,double y,CustomButtonWidgetState state)199 custom_button_widget_draw (
200 CustomButtonWidget * self,
201 cairo_t * cr,
202 double x,
203 double y,
204 CustomButtonWidgetState state)
205 {
206 draw_bg (self, cr, x, y, self->size, false, state);
207
208 draw_icon_with_shadow (self, cr, x, y, state);
209
210 self->last_state = state;
211 }
212
213 /**
214 * @param width Max width for the button to use.
215 */
216 void
custom_button_widget_draw_with_text(CustomButtonWidget * self,cairo_t * cr,double x,double y,double width,CustomButtonWidgetState state)217 custom_button_widget_draw_with_text (
218 CustomButtonWidget * self,
219 cairo_t * cr,
220 double x,
221 double y,
222 double width,
223 CustomButtonWidgetState state)
224 {
225 draw_bg (self, cr, x, y, width, 0, state);
226
227 draw_icon_with_shadow (self, cr, x, y, state);
228
229 /* draw text */
230 cairo_set_source_rgba (
231 cr, 1, 1, 1, 1);
232 cairo_move_to (
233 cr, x + self->size + 2,
234 (y + self->size / 2) - self->text_height / 2);
235 PangoLayout * layout = self->layout;
236 pango_layout_set_text (
237 layout, self->text, -1);
238 pango_cairo_show_layout (cr, layout);
239
240 self->width = (int) width;
241 self->last_state = state;
242 }
243
244 /**
245 * Sets the text and layout to draw the text width.
246 *
247 * @param font_descr Font description to set the
248 * pango layout font to.
249 */
250 void
custom_button_widget_set_text(CustomButtonWidget * self,PangoLayout * layout,char * text,const char * font_descr)251 custom_button_widget_set_text (
252 CustomButtonWidget * self,
253 PangoLayout * layout,
254 char * text,
255 const char * font_descr)
256 {
257 g_return_if_fail (text && layout);
258
259 self->text = g_strdup (text);
260 self->layout = pango_layout_copy (layout);
261 PangoFontDescription * desc =
262 pango_font_description_from_string (font_descr);
263 pango_layout_set_font_description (
264 self->layout, desc);
265 pango_font_description_free (desc);
266 pango_layout_get_pixel_size (
267 self->layout, NULL, &self->text_height);
268 }
269
270 void
custom_button_widget_free(CustomButtonWidget * self)271 custom_button_widget_free (
272 CustomButtonWidget * self)
273 {
274 if (self->text)
275 g_free (self->text);
276 if (self->layout)
277 g_object_unref (self->layout);
278
279 free (self);
280 }
281