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
38struct _GtkColorScale
39{
40 GtkScale parent_instance;
41
42 GdkRGBA color;
43 GtkColorScaleType type;
44 GdkTexture *hue_texture;
45};
46
47typedef struct
48{
49 GtkScaleClass parent_class;
50} GtkColorScaleClass;
51
52enum
53{
54 PROP_ZERO,
55 PROP_SCALE_TYPE
56};
57
58static void hold_action (GtkGestureLongPress *gesture,
59 double x,
60 double y,
61 GtkWidget *scale);
62
63static void click_action (GtkGestureClick *gesture,
64 guint n_presses,
65 double x,
66 double y,
67 GtkWidget *scale);
68
69G_DEFINE_TYPE (GtkColorScale, gtk_color_scale, GTK_TYPE_SCALE)
70
71void
72gtk_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
159static void
160gtk_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
180static void
181scale_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
202static void
203scale_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
222static void
223scale_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
242static void
243hold_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
253static void
254click_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
265static void
266scale_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
275static void
276gtk_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
291void
292gtk_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
299GtkWidget *
300gtk_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

source code of gtk/gtk/gtkcolorscale.c