1 | /* GTK - The GIMP Toolkit |
2 | * Copyright (C) 2012 Red Hat, Inc. |
3 | * |
4 | * This library is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU Lesser General Public |
6 | * License as published by the Free Software Foundation; either |
7 | * version 2 of the License, or (at your option) any later version. |
8 | * |
9 | * This library is distributed in the hope that it will be useful, |
10 | * but 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 | * You should have received a copy of the GNU Lesser General Public |
15 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
16 | */ |
17 | |
18 | #include "config.h" |
19 | |
20 | #include "gtkcolorscaleprivate.h" |
21 | |
22 | #include "gtkcolorchooserprivate.h" |
23 | #include "gtkgesturelongpress.h" |
24 | #include "gtkgestureclick.h" |
25 | #include "gtkcolorutils.h" |
26 | #include "gtkorientable.h" |
27 | #include "gtkrangeprivate.h" |
28 | #include "gtkprivate.h" |
29 | #include "gtkintl.h" |
30 | #include "gtksnapshot.h" |
31 | #include "gtkshortcutcontroller.h" |
32 | #include "gtkshortcuttrigger.h" |
33 | #include "gtkshortcutaction.h" |
34 | #include "gtkshortcut.h" |
35 | |
36 | #include <math.h> |
37 | |
38 | struct _GtkColorScale |
39 | { |
40 | GtkScale parent_instance; |
41 | |
42 | GdkRGBA color; |
43 | GtkColorScaleType type; |
44 | GdkTexture *hue_texture; |
45 | }; |
46 | |
47 | typedef struct |
48 | { |
49 | GtkScaleClass parent_class; |
50 | } GtkColorScaleClass; |
51 | |
52 | enum |
53 | { |
54 | PROP_ZERO, |
55 | PROP_SCALE_TYPE |
56 | }; |
57 | |
58 | static void hold_action (GtkGestureLongPress *gesture, |
59 | double x, |
60 | double y, |
61 | GtkWidget *scale); |
62 | |
63 | static void click_action (GtkGestureClick *gesture, |
64 | guint n_presses, |
65 | double x, |
66 | double y, |
67 | GtkWidget *scale); |
68 | |
69 | G_DEFINE_TYPE (GtkColorScale, gtk_color_scale, GTK_TYPE_SCALE) |
70 | |
71 | void |
72 | gtk_color_scale_snapshot_trough (GtkColorScale *scale, |
73 | GtkSnapshot *snapshot, |
74 | int width, |
75 | int height) |
76 | { |
77 | GtkWidget *widget = GTK_WIDGET (scale); |
78 | |
79 | if (width <= 1 || height <= 1) |
80 | return; |
81 | |
82 | if (scale->hue_texture && |
83 | (width != gdk_texture_get_width (texture: scale->hue_texture) || |
84 | height != gdk_texture_get_height (texture: scale->hue_texture))) |
85 | g_clear_object (&scale->hue_texture); |
86 | |
87 | if (scale->type == GTK_COLOR_SCALE_HUE) |
88 | { |
89 | if (!scale->hue_texture) |
90 | { |
91 | const int stride = width * 3; |
92 | GBytes *bytes; |
93 | guchar *data, *p; |
94 | int hue_x, hue_y; |
95 | |
96 | data = g_malloc (n_bytes: height * stride); |
97 | |
98 | for (hue_y = 0; hue_y < height; hue_y++) |
99 | { |
100 | const float h = CLAMP ((float)hue_y / (height - 1), 0.0, 1.0); |
101 | float r, g, b; |
102 | |
103 | gtk_hsv_to_rgb (h, s: 1, v: 1, r: &r, g: &g, b: &b); |
104 | |
105 | p = data + hue_y * stride; |
106 | for (hue_x = 0; hue_x < stride; hue_x += 3) |
107 | { |
108 | p[hue_x + 0] = r * 255; |
109 | p[hue_x + 1] = g * 255; |
110 | p[hue_x + 2] = b * 255; |
111 | } |
112 | } |
113 | |
114 | bytes = g_bytes_new_take (data, size: height * stride); |
115 | scale->hue_texture = gdk_memory_texture_new (width, height, |
116 | format: GDK_MEMORY_R8G8B8, |
117 | bytes, |
118 | stride); |
119 | g_bytes_unref (bytes); |
120 | } |
121 | |
122 | gtk_snapshot_append_texture (snapshot, |
123 | texture: scale->hue_texture, |
124 | bounds: &GRAPHENE_RECT_INIT(0, 0, width, height)); |
125 | } |
126 | else if (scale->type == GTK_COLOR_SCALE_ALPHA) |
127 | { |
128 | graphene_point_t start, end; |
129 | const GdkRGBA *color; |
130 | |
131 | if (gtk_orientable_get_orientation (GTK_ORIENTABLE (widget)) == GTK_ORIENTATION_HORIZONTAL && |
132 | gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) |
133 | { |
134 | graphene_point_init (p: &start, x: width, y: 0); |
135 | graphene_point_init (p: &end, x: 0, y: 0); |
136 | } |
137 | else |
138 | { |
139 | graphene_point_init (p: &start, x: 0, y: 0); |
140 | graphene_point_init (p: &end, x: width, y: 0); |
141 | } |
142 | |
143 | _gtk_color_chooser_snapshot_checkered_pattern (snapshot, width, height); |
144 | |
145 | color = &scale->color; |
146 | |
147 | gtk_snapshot_append_linear_gradient (snapshot, |
148 | bounds: &GRAPHENE_RECT_INIT(0, 0, width, height), |
149 | start_point: &start, |
150 | end_point: &end, |
151 | stops: (GskColorStop[2]) { |
152 | { 0, { color->red, color->green, color->blue, 0 } }, |
153 | { 1, { color->red, color->green, color->blue, 1 } }, |
154 | }, |
155 | n_stops: 2); |
156 | } |
157 | } |
158 | |
159 | static void |
160 | gtk_color_scale_init (GtkColorScale *scale) |
161 | { |
162 | GtkGesture *gesture; |
163 | |
164 | gesture = gtk_gesture_long_press_new (); |
165 | g_signal_connect (gesture, "pressed" , |
166 | G_CALLBACK (hold_action), scale); |
167 | gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (gesture), |
168 | phase: GTK_PHASE_TARGET); |
169 | gtk_widget_add_controller (GTK_WIDGET (scale), GTK_EVENT_CONTROLLER (gesture)); |
170 | |
171 | gesture = gtk_gesture_click_new (); |
172 | gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), GDK_BUTTON_SECONDARY); |
173 | g_signal_connect (gesture, "pressed" , |
174 | G_CALLBACK (click_action), scale); |
175 | gtk_widget_add_controller (GTK_WIDGET (scale), GTK_EVENT_CONTROLLER (gesture)); |
176 | |
177 | gtk_widget_add_css_class (GTK_WIDGET (scale), css_class: "color" ); |
178 | } |
179 | |
180 | static void |
181 | scale_constructed (GObject *object) |
182 | { |
183 | GtkColorScale *scale = GTK_COLOR_SCALE (object); |
184 | GtkEventController *controller; |
185 | GtkShortcutTrigger *trigger; |
186 | GtkShortcutAction *action; |
187 | GtkShortcut *shortcut; |
188 | |
189 | controller = gtk_shortcut_controller_new (); |
190 | trigger = gtk_alternative_trigger_new (first: gtk_keyval_trigger_new (GDK_KEY_F10, modifiers: GDK_SHIFT_MASK), |
191 | second: gtk_keyval_trigger_new (GDK_KEY_Menu, modifiers: 0)); |
192 | action = gtk_named_action_new (name: "color.edit" ); |
193 | shortcut = gtk_shortcut_new_with_arguments (trigger, |
194 | action, |
195 | format_string: "s" , |
196 | scale->type == GTK_COLOR_SCALE_ALPHA |
197 | ? "a" : "h" ); |
198 | gtk_shortcut_controller_add_shortcut (GTK_SHORTCUT_CONTROLLER (controller), shortcut); |
199 | gtk_widget_add_controller (GTK_WIDGET (scale), controller); |
200 | } |
201 | |
202 | static void |
203 | scale_get_property (GObject *object, |
204 | guint prop_id, |
205 | GValue *value, |
206 | GParamSpec *pspec) |
207 | { |
208 | GtkColorScale *scale = GTK_COLOR_SCALE (object); |
209 | |
210 | switch (prop_id) |
211 | { |
212 | case PROP_SCALE_TYPE: |
213 | g_value_set_int (value, v_int: scale->type); |
214 | break; |
215 | |
216 | default: |
217 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
218 | break; |
219 | } |
220 | } |
221 | |
222 | static void |
223 | scale_set_property (GObject *object, |
224 | guint prop_id, |
225 | const GValue *value, |
226 | GParamSpec *pspec) |
227 | { |
228 | GtkColorScale *scale = GTK_COLOR_SCALE (object); |
229 | |
230 | switch (prop_id) |
231 | { |
232 | case PROP_SCALE_TYPE: |
233 | scale->type = (GtkColorScaleType) g_value_get_int (value); |
234 | break; |
235 | |
236 | default: |
237 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
238 | break; |
239 | } |
240 | } |
241 | |
242 | static void |
243 | hold_action (GtkGestureLongPress *gesture, |
244 | double x, |
245 | double y, |
246 | GtkWidget *scale) |
247 | { |
248 | gtk_widget_activate_action (widget: scale, |
249 | name: "color.edit" , |
250 | format_string: "s" , gtk_widget_get_name (widget: scale)); |
251 | } |
252 | |
253 | static void |
254 | click_action (GtkGestureClick *gesture, |
255 | guint n_presses, |
256 | double x, |
257 | double y, |
258 | GtkWidget *scale) |
259 | { |
260 | gtk_widget_activate_action (widget: scale, |
261 | name: "color.edit" , |
262 | format_string: "s" , gtk_widget_get_name (widget: scale)); |
263 | } |
264 | |
265 | static void |
266 | scale_finalize (GObject *object) |
267 | { |
268 | GtkColorScale *scale = GTK_COLOR_SCALE (object); |
269 | |
270 | g_clear_object (&scale->hue_texture); |
271 | |
272 | G_OBJECT_CLASS (gtk_color_scale_parent_class)->finalize (object); |
273 | } |
274 | |
275 | static void |
276 | gtk_color_scale_class_init (GtkColorScaleClass *class) |
277 | { |
278 | GObjectClass *object_class = G_OBJECT_CLASS (class); |
279 | |
280 | object_class->constructed = scale_constructed; |
281 | object_class->finalize = scale_finalize; |
282 | object_class->get_property = scale_get_property; |
283 | object_class->set_property = scale_set_property; |
284 | |
285 | g_object_class_install_property (oclass: object_class, property_id: PROP_SCALE_TYPE, |
286 | pspec: g_param_spec_int (name: "scale-type" , P_("Scale type" ), P_("Scale type" ), |
287 | minimum: 0, maximum: 1, default_value: 0, |
288 | GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); |
289 | } |
290 | |
291 | void |
292 | gtk_color_scale_set_rgba (GtkColorScale *scale, |
293 | const GdkRGBA *color) |
294 | { |
295 | scale->color = *color; |
296 | gtk_widget_queue_draw (widget: gtk_range_get_trough_widget (GTK_RANGE (scale))); |
297 | } |
298 | |
299 | GtkWidget * |
300 | gtk_color_scale_new (GtkAdjustment *adjustment, |
301 | GtkColorScaleType type) |
302 | { |
303 | return g_object_new (GTK_TYPE_COLOR_SCALE, |
304 | first_property_name: "adjustment" , adjustment, |
305 | "draw-value" , FALSE, |
306 | "scale-type" , type, |
307 | NULL); |
308 | } |
309 | |