.
1 /*
2 Copyright (C) 2015 Johan Mattsson
3
4 This library is free software; you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as
6 published by the Free Software Foundation; either version 3 of the
7 License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful, but
10 WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13 */
14
15 using Cairo;
16
17 namespace BirdFont {
18
19 public class ColorPicker : Tool {
20
21 double hue = 0;
22 double s = 0;
23 double b = 0;
24 double a = 1;
25
26 public signal void fill_color_updated ();
27 public signal void stroke_color_updated ();
28 public signal void gradient_color_updated ();
29
30 bool update_color = false;
31 public double bar_height;
32
33 int selected_bar = 0;
34
35 public bool has_stroke_color = false;
36 bool stroke_selected = false;
37
38 public Color stroke_color = new Color (0, 0, 0, 1);
39 public Color fill_color = new Color (0, 0, 0, 1);
40
41 public Gradient gradient = new Gradient ();
42 bool update_gradient = false;
43 int bars;
44 Stop current_stop = new Stop ();
45
46 public ColorPicker (string tooltip = "") {
47 base (null, tooltip);
48
49 bar_height = 22 * Toolbox.get_scale ();
50 bars = 5;
51 h = bars * bar_height;
52
53 stroke_color_updated.connect (() => {
54 redraw ();
55 GlyphCanvas.redraw ();
56 });
57
58 panel_press_action.connect ((selected, button, tx, ty) => {
59 if (y <= ty <= y + bars * bar_height) {
60 update_color = true;
61 selected_bar = (int) ((ty - y) / bar_height);
62 set_color_from_pointer (tx);
63 }
64 });
65
66 panel_move_action.connect ((selected, button, tx, ty) => {
67 if (update_color) {
68 set_color_from_pointer (tx);
69 }
70
71 return false;
72 });
73
74 panel_release_action.connect ((selected, button, tx, ty) => {
75 update_color = false;
76 });
77 }
78
79 public void set_gradient (Gradient g, Stop stop, bool update_gradient) {
80 gradient = g;
81 this.update_gradient = update_gradient;
82 current_stop = stop;
83 redraw ();
84 }
85
86 public void set_color (Color c) {
87 c.to_hsva (out hue, out s, out b, out a);
88 }
89
90 public void set_color_from_pointer (double tx) {
91 if (tx > Toolbox.allocation_width) {
92 tx = Toolbox.allocation_width;
93 }
94
95 if (tx < 0) {
96 tx = 0;
97 }
98
99 if (selected_bar == 0) {
100 hue = (double) tx / Toolbox.allocation_width;
101 } else if (selected_bar == 1) {
102 s = (double) tx / Toolbox.allocation_width;
103 } else if (selected_bar == 2) {
104 b = (double) tx / Toolbox.allocation_width;
105 } else if (selected_bar == 3) {
106 a = (double) tx / Toolbox.allocation_width;
107 } else if (!update_gradient && selected_bar == 4) {
108 if (has_stroke_color) {
109 stroke_selected = tx > Toolbox.allocation_width / 2.0;
110
111 if (stroke_selected) {
112 set_color (stroke_color);
113 } else {
114 set_color (fill_color);
115 }
116 }
117 } else if (update_gradient && selected_bar == 4) {
118 if (gradient.stops.size > 0) {
119 int g = (int) ((tx / Toolbox.allocation_width) * gradient.stops.size);
120 return_if_fail (0 <= g < gradient.stops.size);
121 current_stop = gradient.stops.get (g);
122 set_color (current_stop.color);
123 }
124 }
125
126 if (selected_bar != 4) {
127 if (update_gradient) {
128 current_stop.color = new Color.hsba (hue, s, b, a);
129 gradient_color_updated ();
130 } else {
131 if (has_stroke_color && stroke_selected) {
132 stroke_color = new Color.hsba (hue, s, b, a);
133 stroke_color_updated ();
134 } else {
135 fill_color = new Color.hsba (hue, s, b, a);
136 fill_color_updated ();
137 }
138 }
139 }
140 }
141
142 public Color get_stroke_color () {
143 return stroke_color;
144 }
145
146 public Color get_fill_color () {
147 return fill_color;
148 }
149
150 public override void draw_tool (Context cr, double px, double py) {
151 draw_bars (cr, px, py);
152 draw_dial (cr, px, py, 0, hue);
153 draw_dial (cr, px, py, 1, s);
154 draw_dial (cr, px, py, 2, b);
155 draw_dial (cr, px, py, 3, a);
156 }
157
158 public void draw_bars (Context cr, double px, double py) {
159 double scale = Toolbox.get_scale ();
160 double step = 1.0 / Toolbox.allocation_width;
161 Color c;
162 double y = this.y - py;
163
164 for (double p = 0; p < 1; p += step) {
165 c = new Color.hsba (p, 1, 1, 1);
166 cr.save ();
167 cr.set_source_rgba (c.r, c.g, c.b, c.a);
168 cr.rectangle (p * Toolbox.allocation_width, y, scale, bar_height);
169 cr.fill ();
170 cr.restore ();
171
172 c = new Color.hsba (hue, p, 1, 1);
173 cr.save ();
174 cr.set_source_rgba (c.r, c.g, c.b, c.a);
175 cr.rectangle (p * Toolbox.allocation_width, y + bar_height, scale, bar_height);
176 cr.fill ();
177 cr.restore ();
178
179 c = new Color.hsba (hue, s, p, 1);
180 cr.save ();
181 cr.set_source_rgba (c.r, c.g, c.b, c.a);
182 cr.rectangle (p * Toolbox.allocation_width, y + 2 * bar_height, scale, bar_height);
183 cr.fill ();
184 cr.restore ();
185
186 c = new Color.hsba (hue, s, b, p);
187 cr.save ();
188 cr.set_source_rgba (c.r, c.g, c.b, c.a);
189 cr.rectangle (p * Toolbox.allocation_width, y + 3 * bar_height, scale, bar_height);
190 cr.fill ();
191 cr.restore ();
192 }
193
194 if (!update_gradient) {
195 if (!has_stroke_color) {
196 c = fill_color;
197 cr.save ();
198 cr.set_source_rgba (c.r, c.g, c.b, c.a);
199 cr.rectangle (0, y + 4 * bar_height, Toolbox.allocation_width, bar_height);
200 cr.fill ();
201 cr.restore ();
202 } else {
203 double cw = Toolbox.allocation_width / 2.0 - 2 * scale;
204
205 cr.save ();
206 cr.set_source_rgba (fill_color.r, fill_color.g, fill_color.b, fill_color.a);
207 cr.rectangle (0, y + 4 * bar_height, cw, bar_height);
208 cr.fill ();
209 cr.restore ();
210
211 cr.save ();
212 cr.set_source_rgba (stroke_color.r, stroke_color.g, stroke_color.b, stroke_color.a);
213 cr.rectangle (cw + 4 * scale, y + 4 * bar_height, cw, bar_height);
214 cr.fill ();
215 cr.restore ();
216
217 if (has_stroke_color) {
218 if (stroke_selected) {
219 cr.save ();
220 Theme.color (cr, "Tool Foreground");
221 cr.set_line_width (1);
222 cr.rectangle (cw + 4 * scale, y + 4 * bar_height, cw, bar_height);
223 cr.stroke ();
224 cr.restore ();
225 } else {
226 cr.save ();
227 Theme.color (cr, "Tool Foreground");
228 cr.set_line_width (1);
229 cr.rectangle (0, y + 4 * bar_height, cw, bar_height);
230 cr.stroke ();
231 cr.restore ();
232 }
233 }
234 }
235 } else { // update gradient
236 int stop_size = (int) ((double) Toolbox.allocation_width / gradient.stops.size);
237 for (int i = 0; i < gradient.stops.size; i++) {
238 Stop s = gradient.stops.get (i);
239 c = s.color;
240 cr.save ();
241 cr.set_source_rgba (c.r, c.g, c.b, c.a);
242 cr.rectangle (i * stop_size, y + 4 * bar_height, stop_size, bar_height);
243 cr.fill ();
244 cr.restore ();
245 }
246
247 bool found = false;
248 for (int i = 0; i < gradient.stops.size; i++) {
249 Stop s = gradient.stops.get (i);
250 if (s == current_stop) {
251 found = true;
252 cr.save ();
253 Theme.color (cr, "Tool Foreground");
254 cr.set_line_width (1);
255 cr.rectangle (i * stop_size, y + 4 * bar_height, stop_size, bar_height);
256 cr.stroke ();
257 cr.restore ();
258 }
259 }
260
261 if (!found) {
262 warning ("No stop selected.");
263 }
264 }
265
266 }
267
268 void draw_dial (Context cr, double px, double py, int bar_index, double val) {
269 double y = this.y - py;
270 double scale = Toolbox.get_scale ();
271 double p;
272 p = bar_index * bar_height;
273
274 return_if_fail (y + p + bar_height - 2 * scale > 0);
275
276 cr.save ();
277 cr.set_line_width (1 * scale);
278 cr.set_source_rgba (1, 1, 1, 1);
279 cr.move_to (val * Toolbox.allocation_width * scale - 3 * scale, y + p + bar_height);
280 cr.line_to (val * Toolbox.allocation_width, y + p + bar_height - 2 * scale);
281 cr.line_to (val * Toolbox.allocation_width + 3 * scale, y + p + bar_height);
282 cr.stroke_preserve ();
283 cr.set_source_rgba (0, 0, 0, 1);
284 cr.fill ();
285 cr.restore ();
286
287 cr.save ();
288 cr.set_line_width (1 * scale);
289 cr.set_source_rgba (1, 1, 1, 1);
290 cr.move_to (val * Toolbox.allocation_width * scale - 3 * scale, y + p);
291 cr.line_to (val * Toolbox.allocation_width, y + p + 2 * scale);
292 cr.line_to (val * Toolbox.allocation_width + 3 * scale, y + p);
293 cr.stroke_preserve ();
294 cr.set_source_rgba (0, 0, 0, 1);
295 cr.fill ();
296 cr.restore ();
297 }
298 }
299
300 }
301