1 | /* GTK - The GIMP Toolkit |
2 | * |
3 | * Copyright (C) 2012 Red Hat, Inc. |
4 | * |
5 | * This library is free software; you can redistribute it and/or |
6 | * modify it under the terms of the GNU Lesser General Public |
7 | * License as published by the Free Software Foundation; either |
8 | * version 2 of the License, or (at your option) any later version. |
9 | * |
10 | * This library 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 GNU |
13 | * Lesser General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU Lesser General Public |
16 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
17 | */ |
18 | |
19 | #include "config.h" |
20 | |
21 | #include "gtkcolorchooserprivate.h" |
22 | #include "gtkcolorchooserwidget.h" |
23 | #include "gtkcoloreditorprivate.h" |
24 | #include "gtkcolorswatchprivate.h" |
25 | #include "gtkgrid.h" |
26 | #include "gtklabel.h" |
27 | #include "gtkorientable.h" |
28 | #include "gtkprivate.h" |
29 | #include "gtkintl.h" |
30 | #include "gtksizegroup.h" |
31 | #include "gtkboxlayout.h" |
32 | #include "gtkwidgetprivate.h" |
33 | #include "gdkrgbaprivate.h" |
34 | |
35 | #include <math.h> |
36 | |
37 | /** |
38 | * GtkColorChooserWidget: |
39 | * |
40 | * The `GtkColorChooserWidget` widget lets the user select a color. |
41 | * |
42 | * By default, the chooser presents a predefined palette of colors, |
43 | * plus a small number of settable custom colors. It is also possible |
44 | * to select a different color with the single-color editor. |
45 | * |
46 | * To enter the single-color editing mode, use the context menu of any |
47 | * color of the palette, or use the '+' button to add a new custom color. |
48 | * |
49 | * The chooser automatically remembers the last selection, as well |
50 | * as custom colors. |
51 | * |
52 | * To create a `GtkColorChooserWidget`, use [ctor@Gtk.ColorChooserWidget.new]. |
53 | * |
54 | * To change the initially selected color, use |
55 | * [method@Gtk.ColorChooser.set_rgba]. To get the selected color use |
56 | * [method@Gtk.ColorChooser.get_rgba]. |
57 | * |
58 | * The `GtkColorChooserWidget` is used in the [class@Gtk.ColorChooserDialog] |
59 | * to provide a dialog for selecting colors. |
60 | * |
61 | * # CSS names |
62 | * |
63 | * `GtkColorChooserWidget` has a single CSS node with name colorchooser. |
64 | */ |
65 | |
66 | typedef struct _GtkColorChooserWidgetClass GtkColorChooserWidgetClass; |
67 | |
68 | struct _GtkColorChooserWidget |
69 | { |
70 | GtkWidget parent_instance; |
71 | |
72 | GtkWidget *palette; |
73 | GtkWidget *editor; |
74 | GtkSizeGroup *size_group; |
75 | |
76 | GtkWidget *custom_label; |
77 | GtkWidget *custom; |
78 | |
79 | GtkWidget *button; |
80 | GtkColorSwatch *current; |
81 | |
82 | gboolean use_alpha; |
83 | gboolean has_default_palette; |
84 | |
85 | GSettings *settings; |
86 | |
87 | int max_custom; |
88 | }; |
89 | |
90 | struct _GtkColorChooserWidgetClass |
91 | { |
92 | GtkWidgetClass parent_class; |
93 | }; |
94 | |
95 | enum |
96 | { |
97 | PROP_ZERO, |
98 | PROP_RGBA, |
99 | PROP_USE_ALPHA, |
100 | PROP_SHOW_EDITOR |
101 | }; |
102 | |
103 | static void gtk_color_chooser_widget_iface_init (GtkColorChooserInterface *iface); |
104 | |
105 | G_DEFINE_TYPE_WITH_CODE (GtkColorChooserWidget, gtk_color_chooser_widget, GTK_TYPE_WIDGET, |
106 | G_IMPLEMENT_INTERFACE (GTK_TYPE_COLOR_CHOOSER, |
107 | gtk_color_chooser_widget_iface_init)) |
108 | |
109 | static void |
110 | select_swatch (GtkColorChooserWidget *cc, |
111 | GtkColorSwatch *swatch) |
112 | { |
113 | GdkRGBA color; |
114 | double red, green, blue, alpha; |
115 | |
116 | if (cc->current == swatch) |
117 | return; |
118 | |
119 | if (cc->current != NULL) |
120 | gtk_widget_unset_state_flags (GTK_WIDGET (cc->current), flags: GTK_STATE_FLAG_SELECTED); |
121 | |
122 | gtk_widget_set_state_flags (GTK_WIDGET (swatch), flags: GTK_STATE_FLAG_SELECTED, FALSE); |
123 | cc->current = swatch; |
124 | |
125 | gtk_color_swatch_get_rgba (swatch, color: &color); |
126 | |
127 | red = color.red; |
128 | green = color.green; |
129 | blue = color.blue; |
130 | alpha = color.alpha; |
131 | g_settings_set (settings: cc->settings, key: "selected-color" , format: "(bdddd)" , |
132 | TRUE, red, green, blue, alpha); |
133 | |
134 | if (gtk_widget_get_visible (GTK_WIDGET (cc->editor))) |
135 | gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (cc->editor), color: &color); |
136 | else |
137 | g_object_notify (G_OBJECT (cc), property_name: "rgba" ); |
138 | } |
139 | |
140 | static void |
141 | swatch_selected (GtkColorSwatch *swatch, |
142 | GtkStateFlags previous, |
143 | GtkColorChooserWidget *cc) |
144 | { |
145 | GtkStateFlags flags; |
146 | |
147 | flags = gtk_widget_get_state_flags (GTK_WIDGET (swatch)); |
148 | if ((flags & GTK_STATE_FLAG_SELECTED) != (previous & GTK_STATE_FLAG_SELECTED) && |
149 | (flags & GTK_STATE_FLAG_SELECTED) != 0) |
150 | select_swatch (cc, swatch); |
151 | } |
152 | |
153 | static void |
154 | connect_swatch_signals (GtkWidget *p, |
155 | gpointer data) |
156 | { |
157 | g_signal_connect (p, "state-flags-changed" , G_CALLBACK (swatch_selected), data); |
158 | } |
159 | |
160 | static void |
161 | connect_button_signals (GtkWidget *p, |
162 | gpointer data) |
163 | { |
164 | // g_signal_connect (p, "activate", G_CALLBACK (button_activate), data); |
165 | } |
166 | |
167 | static void |
168 | save_custom_colors (GtkColorChooserWidget *cc) |
169 | { |
170 | GVariantBuilder builder; |
171 | GVariant *variant; |
172 | GdkRGBA color; |
173 | GtkWidget *child; |
174 | gboolean first; |
175 | |
176 | g_variant_builder_init (builder: &builder, G_VARIANT_TYPE ("a(dddd)" )); |
177 | |
178 | for (child = gtk_widget_get_first_child (widget: cc->custom), first = TRUE; |
179 | child != NULL; |
180 | child = gtk_widget_get_next_sibling (widget: child), first = FALSE) |
181 | { |
182 | if (first) |
183 | continue; |
184 | |
185 | if (gtk_color_swatch_get_rgba (GTK_COLOR_SWATCH (child), color: &color)) |
186 | { |
187 | double red, green, blue, alpha; |
188 | |
189 | red = color.red; |
190 | green = color.green; |
191 | blue = color.blue; |
192 | alpha = color.alpha; |
193 | g_variant_builder_add (builder: &builder, format_string: "(dddd)" , red, green, blue, alpha); |
194 | } |
195 | } |
196 | |
197 | variant = g_variant_builder_end (builder: &builder); |
198 | g_settings_set_value (settings: cc->settings, key: "custom-colors" , value: variant); |
199 | } |
200 | |
201 | static void |
202 | connect_custom_signals (GtkWidget *p, |
203 | gpointer data) |
204 | { |
205 | connect_swatch_signals (p, data); |
206 | g_signal_connect_swapped (p, "notify::rgba" , |
207 | G_CALLBACK (save_custom_colors), data); |
208 | } |
209 | |
210 | static void |
211 | gtk_color_chooser_widget_set_use_alpha (GtkColorChooserWidget *cc, |
212 | gboolean use_alpha) |
213 | { |
214 | GtkWidget *child; |
215 | GtkWidget *grid; |
216 | |
217 | if (cc->use_alpha == use_alpha) |
218 | return; |
219 | |
220 | cc->use_alpha = use_alpha; |
221 | gtk_color_chooser_set_use_alpha (GTK_COLOR_CHOOSER (cc->editor), use_alpha); |
222 | |
223 | for (grid = gtk_widget_get_first_child (widget: cc->palette); |
224 | grid != NULL; |
225 | grid = gtk_widget_get_next_sibling (widget: grid)) |
226 | { |
227 | for (child = gtk_widget_get_first_child (widget: grid); |
228 | child != NULL; |
229 | child = gtk_widget_get_next_sibling (widget: child)) |
230 | { |
231 | if (GTK_IS_COLOR_SWATCH (child)) |
232 | gtk_color_swatch_set_use_alpha (GTK_COLOR_SWATCH (child), use_alpha); |
233 | } |
234 | } |
235 | |
236 | gtk_widget_queue_draw (GTK_WIDGET (cc)); |
237 | g_object_notify (G_OBJECT (cc), property_name: "use-alpha" ); |
238 | } |
239 | |
240 | static void |
241 | gtk_color_chooser_widget_set_show_editor (GtkColorChooserWidget *cc, |
242 | gboolean show_editor) |
243 | { |
244 | if (show_editor) |
245 | { |
246 | GdkRGBA color = { 0.75, 0.25, 0.25, 1.0 }; |
247 | |
248 | if (cc->current) |
249 | gtk_color_swatch_get_rgba (swatch: cc->current, color: &color); |
250 | gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (cc->editor), color: &color); |
251 | } |
252 | |
253 | gtk_widget_set_visible (widget: cc->editor, visible: show_editor); |
254 | gtk_widget_set_visible (widget: cc->palette, visible: !show_editor); |
255 | } |
256 | |
257 | static void |
258 | update_from_editor (GtkColorEditor *editor, |
259 | GParamSpec *pspec, |
260 | GtkColorChooserWidget *widget) |
261 | { |
262 | if (gtk_widget_get_visible (GTK_WIDGET (editor))) |
263 | g_object_notify (G_OBJECT (widget), property_name: "rgba" ); |
264 | } |
265 | |
266 | /* UI construction {{{1 */ |
267 | |
268 | static void |
269 | remove_palette (GtkColorChooserWidget *cc) |
270 | { |
271 | GList *children, *l; |
272 | GtkWidget *widget; |
273 | |
274 | if (cc->current != NULL && |
275 | gtk_widget_get_parent (GTK_WIDGET (cc->current)) != cc->custom) |
276 | cc->current = NULL; |
277 | |
278 | children = NULL; |
279 | for (widget = gtk_widget_get_first_child (widget: cc->palette); |
280 | widget != NULL; |
281 | widget = gtk_widget_get_next_sibling (widget)) |
282 | children = g_list_prepend (list: children, data: widget); |
283 | |
284 | for (l = children; l; l = l->next) |
285 | { |
286 | widget = l->data; |
287 | if (widget == cc->custom_label || widget == cc->custom) |
288 | continue; |
289 | gtk_box_remove (GTK_BOX (cc->palette), child: widget); |
290 | } |
291 | g_list_free (list: children); |
292 | } |
293 | |
294 | static guint |
295 | scale_round (double value, |
296 | double scale) |
297 | { |
298 | value = floor (x: value * scale + 0.5); |
299 | value = MAX (value, 0); |
300 | value = MIN (value, scale); |
301 | return (guint)value; |
302 | } |
303 | |
304 | static char * |
305 | accessible_color_name (GdkRGBA *color) |
306 | { |
307 | if (color->alpha < 1.0) |
308 | return g_strdup_printf (_("Red %d%%, Green %d%%, Blue %d%%, Alpha %d%%" ), |
309 | scale_round (value: color->red, scale: 100), |
310 | scale_round (value: color->green, scale: 100), |
311 | scale_round (value: color->blue, scale: 100), |
312 | scale_round (value: color->alpha, scale: 100)); |
313 | else |
314 | return g_strdup_printf (_("Red %d%%, Green %d%%, Blue %d%%" ), |
315 | scale_round (value: color->red, scale: 100), |
316 | scale_round (value: color->green, scale: 100), |
317 | scale_round (value: color->blue, scale: 100)); |
318 | } |
319 | |
320 | static void |
321 | add_palette (GtkColorChooserWidget *cc, |
322 | GtkOrientation orientation, |
323 | int colors_per_line, |
324 | int n_colors, |
325 | GdkRGBA *colors, |
326 | const char **names) |
327 | { |
328 | GtkWidget *grid; |
329 | GtkWidget *p; |
330 | int line, pos; |
331 | int i; |
332 | int left, right; |
333 | |
334 | if (colors == NULL) |
335 | { |
336 | remove_palette (cc); |
337 | return; |
338 | } |
339 | |
340 | grid = gtk_grid_new (); |
341 | gtk_widget_set_margin_bottom (widget: grid, margin: 12); |
342 | gtk_grid_set_row_spacing (GTK_GRID (grid), spacing: 2); |
343 | gtk_grid_set_column_spacing (GTK_GRID (grid), spacing: 4); |
344 | gtk_box_append (GTK_BOX (cc->palette), child: grid); |
345 | |
346 | left = 0; |
347 | right = colors_per_line - 1; |
348 | if (gtk_widget_get_direction (GTK_WIDGET (cc)) == GTK_TEXT_DIR_RTL) |
349 | { |
350 | i = left; |
351 | left = right; |
352 | right = i; |
353 | } |
354 | |
355 | for (i = 0; i < n_colors; i++) |
356 | { |
357 | p = gtk_color_swatch_new (); |
358 | if (names) |
359 | { |
360 | gtk_accessible_update_property (self: GTK_ACCESSIBLE (ptr: p), |
361 | first_property: GTK_ACCESSIBLE_PROPERTY_LABEL, |
362 | g_dpgettext2 (GETTEXT_PACKAGE, context: "Color name" , msgid: names[i]), |
363 | -1); |
364 | } |
365 | else |
366 | { |
367 | char *name; |
368 | char *text; |
369 | |
370 | name = accessible_color_name (color: &colors[i]); |
371 | text = g_strdup_printf (_("Color: %s" ), name); |
372 | gtk_accessible_update_property (self: GTK_ACCESSIBLE (ptr: p), |
373 | first_property: GTK_ACCESSIBLE_PROPERTY_LABEL, text, |
374 | -1); |
375 | g_free (mem: name); |
376 | g_free (mem: text); |
377 | } |
378 | gtk_color_swatch_set_rgba (GTK_COLOR_SWATCH (p), color: &colors[i]); |
379 | connect_swatch_signals (p, data: cc); |
380 | |
381 | line = i / colors_per_line; |
382 | pos = i % colors_per_line; |
383 | |
384 | if (orientation == GTK_ORIENTATION_HORIZONTAL) |
385 | { |
386 | if (pos == left) |
387 | gtk_widget_add_css_class (widget: p, css_class: "left" ); |
388 | else if (pos == right) |
389 | gtk_widget_add_css_class (widget: p, css_class: "right" ); |
390 | |
391 | gtk_grid_attach (GTK_GRID (grid), child: p, column: pos, row: line, width: 1, height: 1); |
392 | } |
393 | else |
394 | { |
395 | if (pos == 0) |
396 | gtk_widget_add_css_class (widget: p, css_class: "top" ); |
397 | else if (pos == colors_per_line - 1) |
398 | gtk_widget_add_css_class (widget: p, css_class: "bottom" ); |
399 | |
400 | gtk_grid_attach (GTK_GRID (grid), child: p, column: line, row: pos, width: 1, height: 1); |
401 | } |
402 | } |
403 | |
404 | if (orientation == GTK_ORIENTATION_HORIZONTAL) |
405 | cc->max_custom = MAX (cc->max_custom, colors_per_line); |
406 | else |
407 | cc->max_custom = MAX (cc->max_custom, n_colors / colors_per_line); |
408 | } |
409 | |
410 | static void |
411 | remove_default_palette (GtkColorChooserWidget *cc) |
412 | { |
413 | if (!cc->has_default_palette) |
414 | return; |
415 | |
416 | remove_palette (cc); |
417 | cc->has_default_palette = FALSE; |
418 | cc->max_custom = 0; |
419 | } |
420 | |
421 | static void |
422 | add_default_palette (GtkColorChooserWidget *cc) |
423 | { |
424 | GdkRGBA colors[9*5] = { |
425 | GDK_RGBA("99c1f1" ), GDK_RGBA("62a0ea" ), GDK_RGBA("3584e4" ), GDK_RGBA("1c71d8" ), GDK_RGBA("1a5fb4" ), /* Blue */ |
426 | GDK_RGBA("8ff0a4" ), GDK_RGBA("57e389" ), GDK_RGBA("33d17a" ), GDK_RGBA("2ec27e" ), GDK_RGBA("26a269" ), /* Green */ |
427 | GDK_RGBA("f9f06b" ), GDK_RGBA("f8e45c" ), GDK_RGBA("f6d32d" ), GDK_RGBA("f5c211" ), GDK_RGBA("e5a50a" ), /* Yellow */ |
428 | GDK_RGBA("ffbe6f" ), GDK_RGBA("ffa348" ), GDK_RGBA("ff7800" ), GDK_RGBA("e66100" ), GDK_RGBA("c64600" ), /* Orange */ |
429 | GDK_RGBA("f66151" ), GDK_RGBA("ed333b" ), GDK_RGBA("e01b24" ), GDK_RGBA("c01c28" ), GDK_RGBA("a51d2d" ), /* Red */ |
430 | GDK_RGBA("dc8add" ), GDK_RGBA("c061cb" ), GDK_RGBA("9141ac" ), GDK_RGBA("813d9c" ), GDK_RGBA("613583" ), /* Purple */ |
431 | GDK_RGBA("cdab8f" ), GDK_RGBA("b5835a" ), GDK_RGBA("986a44" ), GDK_RGBA("865e3c" ), GDK_RGBA("63452c" ), /* Brown */ |
432 | GDK_RGBA("ffffff" ), GDK_RGBA("f6f5f4" ), GDK_RGBA("deddda" ), GDK_RGBA("c0bfbc" ), GDK_RGBA("9a9996" ), /* Light */ |
433 | GDK_RGBA("77767b" ), GDK_RGBA("5e5c64" ), GDK_RGBA("3d3846" ), GDK_RGBA("241f31" ), GDK_RGBA("000000" ) /* Dark */ |
434 | }; |
435 | const char *color_names[] = { |
436 | NC_("Color name" , "Very Light Blue" ), |
437 | NC_("Color name" , "Light Blue" ), |
438 | NC_("Color name" , "Blue" ), |
439 | NC_("Color name" , "Dark Blue" ), |
440 | NC_("Color name" , "Very Dark Blue" ), |
441 | NC_("Color name" , "Very Light Green" ), |
442 | NC_("Color name" , "Light Green" ), |
443 | NC_("Color name" , "Green" ), |
444 | NC_("Color name" , "Dark Green" ), |
445 | NC_("Color name" , "Very Dark Green" ), |
446 | NC_("Color name" , "Very Light Yellow" ), |
447 | NC_("Color name" , "Light Yellow" ), |
448 | NC_("Color name" , "Yellow" ), |
449 | NC_("Color name" , "Dark Yellow" ), |
450 | NC_("Color name" , "Very Dark Yellow" ), |
451 | NC_("Color name" , "Very Light Orange" ), |
452 | NC_("Color name" , "Light Orange" ), |
453 | NC_("Color name" , "Orange" ), |
454 | NC_("Color name" , "Dark Orange" ), |
455 | NC_("Color name" , "Very Dark Orange" ), |
456 | NC_("Color name" , "Very Light Red" ), |
457 | NC_("Color name" , "Light Red" ), |
458 | NC_("Color name" , "Red" ), |
459 | NC_("Color name" , "Dark Red" ), |
460 | NC_("Color name" , "Very Dark Red" ), |
461 | NC_("Color name" , "Very Light Purple" ), |
462 | NC_("Color name" , "Light Purple" ), |
463 | NC_("Color name" , "Purple" ), |
464 | NC_("Color name" , "Dark Purple" ), |
465 | NC_("Color name" , "Very Dark Purple" ), |
466 | NC_("Color name" , "Very Light Brown" ), |
467 | NC_("Color name" , "Light Brown" ), |
468 | NC_("Color name" , "Brown" ), |
469 | NC_("Color name" , "Dark Brown" ), |
470 | NC_("Color name" , "Very Dark Brown" ), |
471 | NC_("Color name" , "White" ), |
472 | NC_("Color name" , "Light Gray 1" ), |
473 | NC_("Color name" , "Light Gray 2" ), |
474 | NC_("Color name" , "Light Gray 3" ), |
475 | NC_("Color name" , "Light Gray 4" ), |
476 | NC_("Color name" , "Dark Gray 1" ), |
477 | NC_("Color name" , "Dark Gray 2" ), |
478 | NC_("Color name" , "Dark Gray 3" ), |
479 | NC_("Color name" , "Dark Gray 4" ), |
480 | NC_("Color name" , "Black" ), |
481 | }; |
482 | |
483 | add_palette (cc, orientation: GTK_ORIENTATION_VERTICAL, colors_per_line: 5, n_colors: 9*5, colors, names: color_names); |
484 | |
485 | cc->has_default_palette = TRUE; |
486 | } |
487 | |
488 | static void |
489 | gtk_color_chooser_widget_activate_color_customize (GtkWidget *widget, |
490 | const char *name, |
491 | GVariant *parameter) |
492 | { |
493 | GtkColorChooserWidget *cc = GTK_COLOR_CHOOSER_WIDGET (widget); |
494 | double red, green, blue, alpha; |
495 | GdkRGBA color; |
496 | |
497 | g_variant_get (value: parameter, format_string: "(dddd)" , &red, &green, &blue, &alpha); |
498 | color.red = red; |
499 | color.green = green; |
500 | color.blue = blue; |
501 | color.alpha = alpha; |
502 | |
503 | gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (cc->editor), color: &color); |
504 | |
505 | gtk_widget_hide (widget: cc->palette); |
506 | gtk_widget_show (widget: cc->editor); |
507 | g_object_notify (G_OBJECT (cc), property_name: "show-editor" ); |
508 | } |
509 | |
510 | static void |
511 | gtk_color_chooser_widget_activate_color_select (GtkWidget *widget, |
512 | const char *name, |
513 | GVariant *parameter) |
514 | { |
515 | GtkColorChooserWidget *cc = GTK_COLOR_CHOOSER_WIDGET (widget); |
516 | GdkRGBA color; |
517 | double red, green, blue, alpha; |
518 | |
519 | g_variant_get (value: parameter, format_string: "(dddd)" , &red, &green, &blue, &alpha); |
520 | color.red = red; |
521 | color.green = green; |
522 | color.blue = blue; |
523 | color.alpha = alpha; |
524 | |
525 | _gtk_color_chooser_color_activated (GTK_COLOR_CHOOSER (cc), color: &color); |
526 | } |
527 | |
528 | static void |
529 | gtk_color_chooser_widget_init (GtkColorChooserWidget *cc) |
530 | { |
531 | GtkWidget *box; |
532 | GtkWidget *p; |
533 | GtkWidget *button; |
534 | GtkWidget *label; |
535 | int i; |
536 | double color[4]; |
537 | GdkRGBA rgba; |
538 | GVariant *variant; |
539 | GVariantIter iter; |
540 | gboolean selected; |
541 | char *name; |
542 | char *text; |
543 | |
544 | cc->use_alpha = TRUE; |
545 | |
546 | cc->palette = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 0); |
547 | gtk_widget_set_parent (widget: cc->palette, GTK_WIDGET (cc)); |
548 | |
549 | add_default_palette (cc); |
550 | |
551 | /* translators: label for the custom section in the color chooser */ |
552 | cc->custom_label = label = gtk_label_new (_("Custom" )); |
553 | gtk_widget_set_halign (widget: label, align: GTK_ALIGN_START); |
554 | gtk_box_append (GTK_BOX (cc->palette), child: label); |
555 | |
556 | cc->custom = box = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 4); |
557 | g_object_set (object: box, first_property_name: "margin-top" , 12, NULL); |
558 | gtk_box_append (GTK_BOX (cc->palette), child: box); |
559 | |
560 | cc->button = button = gtk_color_swatch_new (); |
561 | gtk_widget_set_name (widget: button, name: "add-color-button" ); |
562 | connect_button_signals (p: button, data: cc); |
563 | gtk_color_swatch_set_icon (GTK_COLOR_SWATCH (button), icon: "list-add-symbolic" ); |
564 | gtk_color_swatch_set_selectable (GTK_COLOR_SWATCH (button), FALSE); |
565 | gtk_box_append (GTK_BOX (box), child: button); |
566 | |
567 | cc->settings = g_settings_new (schema_id: "org.gtk.gtk4.Settings.ColorChooser" ); |
568 | variant = g_settings_get_value (settings: cc->settings, I_("custom-colors" )); |
569 | g_variant_iter_init (iter: &iter, value: variant); |
570 | i = 0; |
571 | p = NULL; |
572 | while (g_variant_iter_loop (iter: &iter, format_string: "(dddd)" , &color[0], &color[1], &color[2], &color[3])) |
573 | { |
574 | i++; |
575 | p = gtk_color_swatch_new (); |
576 | |
577 | rgba.red = color[0]; |
578 | rgba.green = color[1]; |
579 | rgba.blue = color[2]; |
580 | rgba.alpha = color[3]; |
581 | |
582 | gtk_color_swatch_set_rgba (GTK_COLOR_SWATCH (p), color: &rgba); |
583 | |
584 | name = accessible_color_name (color: &rgba); |
585 | text = g_strdup_printf (_("Custom color %d: %s" ), i, name); |
586 | gtk_accessible_update_property (self: GTK_ACCESSIBLE (ptr: p), |
587 | first_property: GTK_ACCESSIBLE_PROPERTY_LABEL, text, |
588 | -1); |
589 | g_free (mem: name); |
590 | g_free (mem: text); |
591 | |
592 | gtk_color_swatch_set_can_drop (GTK_COLOR_SWATCH (p), TRUE); |
593 | connect_custom_signals (p, data: cc); |
594 | gtk_box_append (GTK_BOX (box), child: p); |
595 | |
596 | if (i == 8) |
597 | break; |
598 | } |
599 | g_variant_unref (value: variant); |
600 | |
601 | cc->editor = gtk_color_editor_new (); |
602 | gtk_widget_set_halign (widget: cc->editor, align: GTK_ALIGN_CENTER); |
603 | gtk_widget_set_hexpand (widget: cc->editor, TRUE); |
604 | g_signal_connect (cc->editor, "notify::rgba" , |
605 | G_CALLBACK (update_from_editor), cc); |
606 | |
607 | gtk_widget_set_parent (widget: cc->editor, GTK_WIDGET (cc)); |
608 | |
609 | g_settings_get (settings: cc->settings, I_("selected-color" ), format: "(bdddd)" , |
610 | &selected, |
611 | &color[0], &color[1], &color[2], &color[3]); |
612 | if (selected) |
613 | { |
614 | rgba.red = color[0]; |
615 | rgba.green = color[1]; |
616 | rgba.blue = color[2]; |
617 | rgba.alpha = color[3]; |
618 | gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (cc), color: &rgba); |
619 | } |
620 | |
621 | gtk_widget_hide (GTK_WIDGET (cc->editor)); |
622 | } |
623 | |
624 | /* GObject implementation {{{1 */ |
625 | |
626 | static void |
627 | gtk_color_chooser_widget_get_property (GObject *object, |
628 | guint prop_id, |
629 | GValue *value, |
630 | GParamSpec *pspec) |
631 | { |
632 | GtkColorChooserWidget *cc = GTK_COLOR_CHOOSER_WIDGET (object); |
633 | |
634 | switch (prop_id) |
635 | { |
636 | case PROP_RGBA: |
637 | { |
638 | GdkRGBA color; |
639 | |
640 | gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (cc), color: &color); |
641 | g_value_set_boxed (value, v_boxed: &color); |
642 | } |
643 | break; |
644 | case PROP_USE_ALPHA: |
645 | g_value_set_boolean (value, v_boolean: cc->use_alpha); |
646 | break; |
647 | case PROP_SHOW_EDITOR: |
648 | g_value_set_boolean (value, v_boolean: gtk_widget_get_visible (widget: cc->editor)); |
649 | break; |
650 | default: |
651 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
652 | break; |
653 | } |
654 | } |
655 | |
656 | static void |
657 | gtk_color_chooser_widget_set_property (GObject *object, |
658 | guint prop_id, |
659 | const GValue *value, |
660 | GParamSpec *pspec) |
661 | { |
662 | GtkColorChooserWidget *cc = GTK_COLOR_CHOOSER_WIDGET (object); |
663 | |
664 | switch (prop_id) |
665 | { |
666 | case PROP_RGBA: |
667 | gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (cc), |
668 | color: g_value_get_boxed (value)); |
669 | break; |
670 | case PROP_USE_ALPHA: |
671 | gtk_color_chooser_widget_set_use_alpha (cc, |
672 | use_alpha: g_value_get_boolean (value)); |
673 | break; |
674 | case PROP_SHOW_EDITOR: |
675 | gtk_color_chooser_widget_set_show_editor (cc, |
676 | show_editor: g_value_get_boolean (value)); |
677 | break; |
678 | default: |
679 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
680 | break; |
681 | } |
682 | } |
683 | |
684 | static void |
685 | gtk_color_chooser_widget_finalize (GObject *object) |
686 | { |
687 | GtkColorChooserWidget *cc = GTK_COLOR_CHOOSER_WIDGET (object); |
688 | |
689 | g_object_unref (object: cc->settings); |
690 | |
691 | gtk_widget_unparent (widget: cc->editor); |
692 | gtk_widget_unparent (widget: cc->palette); |
693 | |
694 | G_OBJECT_CLASS (gtk_color_chooser_widget_parent_class)->finalize (object); |
695 | } |
696 | |
697 | static void |
698 | gtk_color_chooser_widget_class_init (GtkColorChooserWidgetClass *class) |
699 | { |
700 | GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); |
701 | GObjectClass *object_class = G_OBJECT_CLASS (class); |
702 | |
703 | object_class->get_property = gtk_color_chooser_widget_get_property; |
704 | object_class->set_property = gtk_color_chooser_widget_set_property; |
705 | object_class->finalize = gtk_color_chooser_widget_finalize; |
706 | |
707 | widget_class->grab_focus = gtk_widget_grab_focus_child; |
708 | widget_class->focus = gtk_widget_focus_child; |
709 | |
710 | g_object_class_override_property (oclass: object_class, property_id: PROP_RGBA, name: "rgba" ); |
711 | g_object_class_override_property (oclass: object_class, property_id: PROP_USE_ALPHA, name: "use-alpha" ); |
712 | |
713 | /** |
714 | * GtkColorChooserWidget:show-editor: |
715 | * |
716 | * %TRUE when the color chooser is showing the single-color editor. |
717 | * |
718 | * It can be set to switch the color chooser into single-color editing mode. |
719 | */ |
720 | g_object_class_install_property (oclass: object_class, property_id: PROP_SHOW_EDITOR, |
721 | pspec: g_param_spec_boolean (name: "show-editor" , P_("Show editor" ), P_("Show editor" ), |
722 | FALSE, GTK_PARAM_READWRITE)); |
723 | |
724 | gtk_widget_class_set_css_name (widget_class, I_("colorchooser" )); |
725 | gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BOX_LAYOUT); |
726 | |
727 | /** |
728 | * GtkColorChooserWidget|color.select: |
729 | * @red: the red value, between 0 and 1 |
730 | * @green: the green value, between 0 and 1 |
731 | * @blue: the blue value, between 0 and 1 |
732 | * @alpha: the alpha value, between 0 and 1 |
733 | * |
734 | * Emits the [signal@Gtk.ColorChooser::color-activated] signal for |
735 | * the given color. |
736 | */ |
737 | gtk_widget_class_install_action (widget_class, action_name: "color.select" , parameter_type: "(dddd)" , |
738 | activate: gtk_color_chooser_widget_activate_color_select); |
739 | |
740 | /** |
741 | * GtkColorChooserWidget|color.customize: |
742 | * @red: the red value, between 0 and 1 |
743 | * @green: the green value, between 0 and 1 |
744 | * @blue: the blue value, between 0 and 1 |
745 | * @alpha: the alpha value, between 0 and 1 |
746 | * |
747 | * Activates the color editor for the given color. |
748 | */ |
749 | gtk_widget_class_install_action (widget_class, action_name: "color.customize" , parameter_type: "(dddd)" , |
750 | activate: gtk_color_chooser_widget_activate_color_customize); |
751 | } |
752 | |
753 | /* GtkColorChooser implementation {{{1 */ |
754 | |
755 | static void |
756 | gtk_color_chooser_widget_get_rgba (GtkColorChooser *chooser, |
757 | GdkRGBA *color) |
758 | { |
759 | GtkColorChooserWidget *cc = GTK_COLOR_CHOOSER_WIDGET (chooser); |
760 | |
761 | if (gtk_widget_get_visible (widget: cc->editor)) |
762 | gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (cc->editor), color); |
763 | else if (cc->current) |
764 | gtk_color_swatch_get_rgba (swatch: cc->current, color); |
765 | else |
766 | { |
767 | color->red = 1.0; |
768 | color->green = 1.0; |
769 | color->blue = 1.0; |
770 | color->alpha = 1.0; |
771 | } |
772 | |
773 | if (!cc->use_alpha) |
774 | color->alpha = 1.0; |
775 | } |
776 | |
777 | static void |
778 | add_custom_color (GtkColorChooserWidget *cc, |
779 | const GdkRGBA *color) |
780 | { |
781 | GtkWidget *widget; |
782 | GtkWidget *p; |
783 | int n; |
784 | |
785 | n = 0; |
786 | for (widget = gtk_widget_get_first_child (widget: cc->custom); |
787 | widget != NULL; |
788 | widget = gtk_widget_get_next_sibling (widget)) |
789 | n++; |
790 | |
791 | while (n >= cc->max_custom) |
792 | { |
793 | GtkWidget *last = gtk_widget_get_last_child (widget: cc->custom); |
794 | |
795 | if (last == (GtkWidget *)cc->current) |
796 | cc->current = NULL; |
797 | |
798 | gtk_box_remove (GTK_BOX (cc->custom), child: last); |
799 | n--; |
800 | } |
801 | |
802 | p = gtk_color_swatch_new (); |
803 | gtk_color_swatch_set_rgba (GTK_COLOR_SWATCH (p), color); |
804 | gtk_color_swatch_set_can_drop (GTK_COLOR_SWATCH (p), TRUE); |
805 | connect_custom_signals (p, data: cc); |
806 | |
807 | gtk_box_insert_child_after (GTK_BOX (cc->custom), child: p, sibling: gtk_widget_get_first_child (widget: cc->custom)); |
808 | gtk_widget_show (widget: p); |
809 | |
810 | select_swatch (cc, GTK_COLOR_SWATCH (p)); |
811 | save_custom_colors (cc); |
812 | } |
813 | |
814 | static void |
815 | gtk_color_chooser_widget_set_rgba (GtkColorChooser *chooser, |
816 | const GdkRGBA *color) |
817 | { |
818 | GtkColorChooserWidget *cc = GTK_COLOR_CHOOSER_WIDGET (chooser); |
819 | GtkWidget *swatch; |
820 | GtkWidget *w; |
821 | |
822 | GdkRGBA c; |
823 | |
824 | for (w = gtk_widget_get_first_child (widget: cc->palette); |
825 | w != NULL; |
826 | w = gtk_widget_get_next_sibling (widget: w)) |
827 | { |
828 | if (!GTK_IS_GRID (w) && !GTK_IS_BOX (w)) |
829 | continue; |
830 | |
831 | for (swatch = gtk_widget_get_first_child (widget: w); |
832 | swatch != NULL; |
833 | swatch = gtk_widget_get_next_sibling (widget: swatch)) |
834 | { |
835 | gtk_color_swatch_get_rgba (GTK_COLOR_SWATCH (swatch), color: &c); |
836 | if (!cc->use_alpha) |
837 | c.alpha = color->alpha; |
838 | if (gdk_rgba_equal (p1: color, p2: &c)) |
839 | { |
840 | select_swatch (cc, GTK_COLOR_SWATCH (swatch)); |
841 | return; |
842 | } |
843 | } |
844 | } |
845 | |
846 | add_custom_color (cc, color); |
847 | } |
848 | |
849 | static void |
850 | gtk_color_chooser_widget_add_palette (GtkColorChooser *chooser, |
851 | GtkOrientation orientation, |
852 | int colors_per_line, |
853 | int n_colors, |
854 | GdkRGBA *colors) |
855 | { |
856 | GtkColorChooserWidget *cc = GTK_COLOR_CHOOSER_WIDGET (chooser); |
857 | |
858 | remove_default_palette (cc); |
859 | add_palette (cc, orientation, colors_per_line, n_colors, colors, NULL); |
860 | |
861 | gtk_box_reorder_child_after (GTK_BOX (cc->palette), child: cc->custom_label, sibling: gtk_widget_get_last_child (widget: cc->palette)); |
862 | gtk_box_reorder_child_after (GTK_BOX (cc->palette), child: cc->custom, sibling: cc->custom_label); |
863 | } |
864 | |
865 | static void |
866 | gtk_color_chooser_widget_iface_init (GtkColorChooserInterface *iface) |
867 | { |
868 | iface->get_rgba = gtk_color_chooser_widget_get_rgba; |
869 | iface->set_rgba = gtk_color_chooser_widget_set_rgba; |
870 | iface->add_palette = gtk_color_chooser_widget_add_palette; |
871 | } |
872 | |
873 | /* Public API {{{1 */ |
874 | |
875 | /** |
876 | * gtk_color_chooser_widget_new: |
877 | * |
878 | * Creates a new `GtkColorChooserWidget`. |
879 | * |
880 | * Returns: a new `GtkColorChooserWidget` |
881 | */ |
882 | GtkWidget * |
883 | gtk_color_chooser_widget_new (void) |
884 | { |
885 | return g_object_new (GTK_TYPE_COLOR_CHOOSER_WIDGET, NULL); |
886 | } |
887 | |
888 | /* vim:set foldmethod=marker: */ |
889 | |