1 /*
2 This file is part of darktable,
3 Copyright (C) 2010-2021 darktable developers.
4
5 darktable 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 darktable 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 darktable. If not, see <http://www.gnu.org/licenses/>.
17 */
18 #include "togglebutton.h"
19 #include "bauhaus/bauhaus.h"
20 #include "button.h"
21 #include "gui/gtk.h"
22 #include <string.h>
23
24 static void _togglebutton_class_init(GtkDarktableToggleButtonClass *klass);
25 static void _togglebutton_init(GtkDarktableToggleButton *slider);
26 static gboolean _togglebutton_draw(GtkWidget *widget, cairo_t *cr);
27
_togglebutton_class_init(GtkDarktableToggleButtonClass * klass)28 static void _togglebutton_class_init(GtkDarktableToggleButtonClass *klass)
29 {
30 GtkWidgetClass *widget_class = (GtkWidgetClass *)klass;
31
32 widget_class->draw = _togglebutton_draw;
33 }
34
_togglebutton_init(GtkDarktableToggleButton * slider)35 static void _togglebutton_init(GtkDarktableToggleButton *slider)
36 {
37 }
38
_togglebutton_draw(GtkWidget * widget,cairo_t * cr)39 static gboolean _togglebutton_draw(GtkWidget *widget, cairo_t *cr)
40 {
41 g_return_val_if_fail(widget != NULL, FALSE);
42 g_return_val_if_fail(DTGTK_IS_TOGGLEBUTTON(widget), FALSE);
43
44 GtkDarktableToggleButton *button = DTGTK_TOGGLEBUTTON(widget);
45
46 GtkStateFlags state = gtk_widget_get_state_flags(widget);
47
48 GdkRGBA fg_color;
49 GtkStyleContext *context = gtk_widget_get_style_context(widget);
50
51 if(button->icon_flags & CPF_CUSTOM_FG)
52 fg_color = button->fg;
53 else if(button->icon_flags & CPF_IGNORE_FG_STATE)
54 gtk_style_context_get_color(context, state & ~GTK_STATE_FLAG_SELECTED, &fg_color);
55 else
56 gtk_style_context_get_color(context, state, &fg_color);
57
58 /* fetch flags */
59 int flags = DTGTK_TOGGLEBUTTON(widget)->icon_flags;
60
61 /* update active state paint flag */
62 const gboolean active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
63 if(active)
64 flags |= CPF_ACTIVE;
65 else
66 flags &= ~CPF_ACTIVE;
67
68 /* update focus state paint flag */
69 const gboolean hasfocus = ((DTGTK_TOGGLEBUTTON(widget)->icon_data == darktable.develop->gui_module)
70 && darktable.develop->gui_module);
71 if(hasfocus)
72 flags |= CPF_FOCUS;
73 else
74 flags &= ~CPF_FOCUS;
75
76 /* prelight */
77 if(state & GTK_STATE_FLAG_PRELIGHT)
78 flags |= CPF_PRELIGHT;
79 else
80 flags &= ~CPF_PRELIGHT;
81
82 /* begin cairo drawing */
83 /* get button total allocation */
84 GtkAllocation allocation;
85 gtk_widget_get_allocation(widget, &allocation);
86 const int width = allocation.width;
87 const int height = allocation.height;
88
89 /* get the css geometry properties of the button */
90 GtkBorder margin, border, padding;
91 gtk_style_context_get_margin(context, state, &margin);
92 gtk_style_context_get_border(context, state, &border);
93 gtk_style_context_get_padding(context, state, &padding);
94
95 /* for button frame and background, we remove css margin from allocation */
96 int startx = margin.left;
97 int starty = margin.top;
98 int cwidth = width - margin.left - margin.right;
99 int cheight = height - margin.top - margin.bottom;
100
101 /* draw standard button background if not transparent nor flat styled */
102 if((flags & CPF_STYLE_FLAT))
103 {
104 if((flags & CPF_PRELIGHT) || ((flags & CPF_ACTIVE) && !(flags & CPF_BG_TRANSPARENT)))
105 {
106 // When CPF_BG_TRANSPARENT is set, change the background on
107 // PRELIGHT, but not on ACTIVE
108 if(!(flags & CPF_BG_TRANSPARENT) || (flags & CPF_PRELIGHT))
109 gtk_render_background(context, cr, startx, starty, cwidth, cheight);
110 }
111 else if(!(flags & CPF_ACTIVE) || (flags & CPF_IGNORE_FG_STATE))
112 fg_color.alpha = CLAMP(fg_color.alpha / 2.0, 0.3, 1.0);
113 }
114 else if(!(flags & CPF_BG_TRANSPARENT))
115 gtk_render_background(context, cr, startx, starty, cwidth, cheight);
116
117 gtk_render_frame(context, cr, startx, starty, cwidth, cheight);
118
119 gdk_cairo_set_source_rgba(cr, &fg_color);
120
121 /* draw icon */
122 if(DTGTK_TOGGLEBUTTON(widget)->icon)
123 {
124 /* calculate the button content allocation */
125 startx += border.left + padding.left;
126 starty += border.top + padding.top;
127 cwidth -= border.left + border.right + padding.left + padding.right;
128 cheight -= border.top + border.bottom + padding.top + padding.bottom;
129
130 /* we have to leave some breathing room to the cairo icon paint function to possibly */
131 /* draw slightly outside the bounding box, for optical alignment and balancing of icons */
132 /* we do this by putting a drawing area widget inside the button and using the CSS */
133 /* margin property in px of the drawing area as extra room in percent (DPI safe) */
134 /* we do this because Gtk+ does not support CSS size in percent */
135 /* this extra margin can be also (slightly) negative */
136 GtkStyleContext *ccontext = gtk_widget_get_style_context(DTGTK_TOGGLEBUTTON(widget)->canvas);
137 GtkBorder cmargin;
138 gtk_style_context_get_margin(ccontext, state, &cmargin);
139
140 startx += round(cmargin.left * cwidth / 100.0f);
141 starty += round(cmargin.top * cheight / 100.0f);
142 cwidth = round((float)cwidth * (1.0 - (cmargin.left + cmargin.right) / 100.0f));
143 cheight = round((float)cheight * (1.0 - (cmargin.top + cmargin.bottom) / 100.0f));
144
145 void *icon_data = DTGTK_TOGGLEBUTTON(widget)->icon_data;
146
147 if(cwidth > 0 && cheight > 0)
148 DTGTK_TOGGLEBUTTON(widget)->icon(cr, startx, starty, cwidth, cheight, flags, icon_data);
149 }
150
151 return FALSE;
152 }
153
154 // Public functions
dtgtk_togglebutton_new(DTGTKCairoPaintIconFunc paint,gint paintflags,void * paintdata)155 GtkWidget *dtgtk_togglebutton_new(DTGTKCairoPaintIconFunc paint, gint paintflags, void *paintdata)
156 {
157 GtkDarktableToggleButton *button;
158 button = g_object_new(dtgtk_togglebutton_get_type(), NULL);
159 button->icon = paint;
160 button->icon_flags = paintflags;
161 button->icon_data = paintdata;
162 button->canvas = gtk_drawing_area_new();
163 gtk_container_add(GTK_CONTAINER(button), button->canvas);
164 gtk_widget_set_name(GTK_WIDGET(button), "dt-toggle-button");
165 gtk_widget_set_name(GTK_WIDGET(button->canvas), "button-canvas");
166 return (GtkWidget *)button;
167 }
168
dtgtk_togglebutton_get_type()169 GType dtgtk_togglebutton_get_type()
170 {
171 static GType dtgtk_togglebutton_type = 0;
172 if(!dtgtk_togglebutton_type)
173 {
174 static const GTypeInfo dtgtk_togglebutton_info = {
175 sizeof(GtkDarktableToggleButtonClass), (GBaseInitFunc)NULL, (GBaseFinalizeFunc)NULL,
176 (GClassInitFunc)_togglebutton_class_init, NULL, /* class_finalize */
177 NULL, /* class_data */
178 sizeof(GtkDarktableToggleButton), 0, /* n_preallocs */
179 (GInstanceInitFunc)_togglebutton_init,
180 };
181 dtgtk_togglebutton_type = g_type_register_static(GTK_TYPE_TOGGLE_BUTTON, "GtkDarktableToggleButton",
182 &dtgtk_togglebutton_info, 0);
183 }
184 return dtgtk_togglebutton_type;
185 }
186
187
dtgtk_togglebutton_set_paint(GtkDarktableToggleButton * button,DTGTKCairoPaintIconFunc paint,gint paintflags,void * paintdata)188 void dtgtk_togglebutton_set_paint(GtkDarktableToggleButton *button, DTGTKCairoPaintIconFunc paint,
189 gint paintflags, void *paintdata)
190 {
191 g_return_if_fail(button != NULL);
192 button->icon = paint;
193 button->icon_flags = paintflags;
194 button->icon_data = paintdata;
195 }
196
dtgtk_togglebutton_override_color(GtkDarktableToggleButton * button,GdkRGBA * color)197 void dtgtk_togglebutton_override_color(GtkDarktableToggleButton *button, GdkRGBA *color)
198 {
199 g_return_if_fail(button != NULL);
200 if(color)
201 {
202 button->fg = *color;
203 button->icon_flags |= CPF_CUSTOM_FG;
204 }
205 else
206 button->icon_flags &= ~CPF_CUSTOM_FG;
207 }
208
dtgtk_togglebutton_override_background_color(GtkDarktableToggleButton * button,GdkRGBA * color)209 void dtgtk_togglebutton_override_background_color(GtkDarktableToggleButton *button, GdkRGBA *color)
210 {
211 g_return_if_fail(button != NULL);
212 if(color)
213 {
214 button->bg = *color;
215 button->icon_flags |= CPF_CUSTOM_BG;
216 }
217 else
218 button->icon_flags &= ~CPF_CUSTOM_BG;
219 }
220
221 // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
222 // vim: shiftwidth=2 expandtab tabstop=2 cindent
223 // kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
224