1 /*
2  * Copyright (C) 2020 Alexandros Theodotou <alex at zrythm dot org>
3  *
4  * This file is part of ZToolkit
5  *
6  * ZToolkit is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Affero General Public License as
8  * published by the Free Software Foundation, either version 3 of the
9  * License, or (at your option) any later version.
10  *
11  * ZToolkit 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 General Affero Public License
17  * along with ZToolkit.  If not, see <https://www.gnu.org/licenses/>.
18  */
19 
20 #include <stdlib.h>
21 #include <string.h>
22 
23 #include <ztoolkit/ztk.h>
24 #include "pugl.h"
25 
26 static void
button_draw_cb(ZtkWidget * widget,cairo_t * cr,ZtkRect * draw_rect,void * data)27 button_draw_cb (
28   ZtkWidget * widget,
29   cairo_t *   cr,
30   ZtkRect *   draw_rect,
31   void *      data)
32 {
33   ZtkButton * self = (ZtkButton *) widget;
34 
35   ZtkWidgetState state = widget->state;
36   if (self->has_bg_colors)
37     {
38       if ((state & ZTK_WIDGET_STATE_PRESSED) ||
39           (self->is_toggle &&
40              self->toggled_getter (
41                self, widget->user_data)))
42         {
43           ztk_color_set_for_cairo (
44             &self->clicked_color, cr);
45         }
46       else if (state & ZTK_WIDGET_STATE_HOVERED)
47         {
48           ztk_color_set_for_cairo (
49             &self->hovered_color, cr);
50         }
51       else
52         {
53           ztk_color_set_for_cairo (
54             &self->normal_color, cr);
55         }
56       cairo_rectangle (
57         cr, widget->rect.x, widget->rect.y,
58         widget->rect.width, widget->rect.height);
59       cairo_fill (cr);
60     }
61   else if (self->bg_draw_cb)
62     {
63       self->bg_draw_cb (
64         widget, cr, draw_rect, data);
65     }
66 
67 #define DRAW_SVG(svg) \
68   ZtkRect rect = { \
69     widget->rect.x + self->hpadding, \
70     widget->rect.y + self->vpadding, \
71     widget->rect.width - self->hpadding * 2, \
72     widget->rect.height - self->vpadding * 2 }; \
73   ztk_rsvg_draw ( \
74     self->svg, cr, &rect)
75 
76   switch (self->type)
77     {
78     case ZTK_BTN_LBL:
79       /* TODO draw label */
80       break;
81 #ifdef HAVE_RSVG
82     case ZTK_BTN_SVG:
83       if ((state & ZTK_WIDGET_STATE_PRESSED) ||
84           (self->is_toggle &&
85              self->toggled_getter (
86                self, widget->user_data)))
87         {
88           DRAW_SVG (clicked_svg);
89         }
90       else if (state & ZTK_WIDGET_STATE_HOVERED)
91         {
92           DRAW_SVG (hover_svg);
93         }
94       else
95         {
96           DRAW_SVG (normal_svg);
97         }
98       break;
99 #endif
100     case ZTK_BTN_CUSTOM:
101       self->custom_draw_cb (
102         widget, cr, draw_rect, data);
103       break;
104     default:
105       break;
106     }
107 
108 #undef DRAW_SVG
109 }
110 
111 static void
update_cb(ZtkWidget * w,void * data)112 update_cb (
113   ZtkWidget * w,
114   void *      data)
115 {
116 }
117 
118 static int
on_button_event(ZtkWidget * w,const PuglEventButton * btn,void * data)119 on_button_event (
120   ZtkWidget *             w,
121   const PuglEventButton * btn,
122   void *                  data)
123 {
124   ZtkButton * self = (ZtkButton *) w;
125 
126   ZtkWidgetState state = w->state;
127   if (state & ZTK_WIDGET_STATE_PRESSED)
128     {
129       if (ztk_widget_is_hit (w, btn->x, btn->y))
130         {
131           self->was_pressed = 1;
132         }
133     }
134   else
135     {
136       if (self->was_pressed)
137         {
138           self->activate_cb (w, data);
139         }
140       self->was_pressed = 0;
141     }
142 
143   return 1;
144 }
145 
146 static void
free_cb(ZtkWidget * widget,void * data)147 free_cb (
148   ZtkWidget * widget,
149   void *      data)
150 {
151   ZtkButton * self = (ZtkButton *) widget;
152 
153   free (self);
154 }
155 
156 /**
157  * Creates a new ZtkButton.
158  *
159  * This must then be set to its type using the setters
160  * below.
161  */
162 ZtkButton *
ztk_button_new(ZtkRect * rect,ZtkWidgetActivateCallback activate_cb,void * user_data)163 ztk_button_new (
164   ZtkRect *                 rect,
165   ZtkWidgetActivateCallback activate_cb,
166   void *                    user_data)
167 {
168   ZtkButton * self = calloc (1, sizeof (ZtkButton));
169   ZtkWidget * widget = (ZtkWidget *) self;
170   ztk_widget_init (
171     (ZtkWidget *) self, ZTK_WIDGET_TYPE_BUTTON, rect,
172     update_cb, button_draw_cb, free_cb);
173 
174   self->activate_cb = activate_cb;
175 
176   widget->button_event_cb = on_button_event;
177   widget->user_data = user_data;
178 
179   return self;
180 }
181 
182 void
ztk_button_make_toggled(ZtkButton * self,ZtkButtonToggledGetter toggled_getter)183 ztk_button_make_toggled (
184   ZtkButton *            self,
185   ZtkButtonToggledGetter toggled_getter)
186 {
187   self->is_toggle = 1;
188   self->toggled_getter = toggled_getter;
189 }
190 
191 /**
192  * Makes a button with a label.
193  */
194 void
ztk_button_make_labeled(ZtkButton * self,const char * label)195 ztk_button_make_labeled (
196   ZtkButton *           self,
197   const char *          label)
198 {
199   self->type = ZTK_BTN_LBL;
200 
201   strcpy (self->lbl, label);
202 }
203 
204 #ifdef HAVE_RSVG
205 /**
206  * Makes a button with SVGs.
207  */
208 void
ztk_button_make_svged(ZtkButton * self,int hpadding,int vpadding,ZtkRsvgHandle * svg_normal,ZtkRsvgHandle * svg_hover,ZtkRsvgHandle * svg_clicked)209 ztk_button_make_svged (
210   ZtkButton *     self,
211   int             hpadding,
212   int             vpadding,
213   ZtkRsvgHandle * svg_normal,
214   ZtkRsvgHandle * svg_hover,
215   ZtkRsvgHandle * svg_clicked)
216 {
217   self->type = ZTK_BTN_SVG;
218 
219   self->hpadding = hpadding;
220   self->vpadding = vpadding;
221   self->normal_svg = svg_normal;
222   self->hover_svg = svg_hover;
223   self->clicked_svg = svg_clicked;
224 }
225 #endif
226 
227 /**
228  * Makes a customly drawn button.
229  */
230 void
ztk_button_make_custom(ZtkButton * self,ZtkWidgetDrawCallback draw_cb)231 ztk_button_make_custom (
232   ZtkButton *           self,
233   ZtkWidgetDrawCallback draw_cb)
234 {
235   self->type = ZTK_BTN_CUSTOM;
236 
237   self->custom_draw_cb = draw_cb;
238 }
239 
240 void
ztk_button_set_background_colors(ZtkButton * self,ZtkColor * normal,ZtkColor * hovered,ZtkColor * clicked)241 ztk_button_set_background_colors (
242   ZtkButton * self,
243   ZtkColor *  normal,
244   ZtkColor *  hovered,
245   ZtkColor *  clicked)
246 {
247   self->has_bg_colors = 1;
248 
249   self->normal_color = * normal;
250   self->hovered_color = * hovered;
251   self->clicked_color = * clicked;
252 }
253 
254 /**
255  * Add callback for drawing the background.
256  */
257 void
ztk_button_add_background_callback(ZtkButton * self,ZtkWidgetDrawCallback draw_cb)258 ztk_button_add_background_callback (
259   ZtkButton *           self,
260   ZtkWidgetDrawCallback draw_cb)
261 {
262   self->bg_draw_cb = draw_cb;
263 }
264