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