1use crate::{makepad_derive_widget::*, makepad_draw::*, widget::*};
2
3live_design!{
4 use makepad_draw::shader::std::*;
5
6 DrawColorWheel= {{DrawColorWheel}} {
7 instance hover: float
8 instance down: float
9
10 fn circ_to_rect(u: float, v: float) -> vec2 {
11 let u2 = u * u;
12 let v2 = v * v;
13 return vec2(
14 0.5 * sqrt(2. + 2. * sqrt(2.) * u + u2 - v2) -
15 0.5 * sqrt(2. - 2. * sqrt(2.) * u + u2 - v2),
16 0.5 * sqrt(2. + 2. * sqrt(2.) * v - u2 + v2) -
17 0.5 * sqrt(2. - 2. * sqrt(2.) * v - u2 + v2)
18 );
19 }
20
21 fn pixel(self) -> vec4 {
22
23 let rgbv = Pal::hsv2rgb(vec4(self.hue, self.sat, self.val, 1.));
24 let w = self.rect_size.x;
25 let h = self.rect_size.y;
26 let sdf = Sdf2d::viewport(self.pos * vec2(w, h));
27 let cx = w * 0.5;
28 let cy = h * 0.5;
29
30 let radius = w * 0.37;
31 let inner = w * 0.28;
32
33 sdf.hexagon(cx, cy, w * 0.45);
34 sdf.hexagon(cx, cy, w * 0.4);
35 sdf.subtract();
36 let ang = atan(self.pos.x * w - cx, 0.0001 + self.pos.y * h - cy) / PI * 0.5 - 0.33333;
37 sdf.fill(Pal::hsv2rgb(vec4(ang, 1.0, 1.0, 1.0)));
38
39 let rsize = inner / sqrt(2.0);
40 sdf.rect(cx - rsize, cy - rsize, rsize * 2.0, rsize * 2.0);
41
42 let norm_rect = vec2(self.pos.x * w - (cx - inner), self.pos.y * h - (cy - inner)) / (2. * inner);
43 let circ = clamp(circ_to_rect(norm_rect.x * 2. - 1., norm_rect.y * 2. - 1.), vec2(-1.), vec2(1.));
44
45 sdf.fill(Pal::hsv2rgb(vec4(self.hue, (circ.x * .5 + .5), 1. - (circ.y * .5 + .5), 1.)));
46
47 let col_angle = (self.hue + .333333) * 2. * PI;
48 let circle_puk = vec2(sin(col_angle) * radius + cx, cos(col_angle) * radius + cy);
49
50 let rect_puk = vec2(cx + self.sat * 2. * rsize - rsize, cy + (1. - self.val) * 2. * rsize - rsize);
51
52 let color = mix(mix(#3, #E, self.hover), #F, self.down);
53 let puck_size = 0.1 * w;
54 sdf.circle(rect_puk.x, rect_puk.y, puck_size);
55 sdf.rect(cx - rsize, cy - rsize, rsize * 2.0, rsize * 2.0);
56 sdf.intersect();
57 sdf.fill(color);
58 sdf.circle(rect_puk.x, rect_puk.y, puck_size - 1. - 2. * self.hover + self.down);
59 sdf.rect(cx - rsize, cy - rsize, rsize * 2.0, rsize * 2.0);
60 sdf.intersect();
61 sdf.fill(rgbv);
62
63 sdf.circle(circle_puk.x, circle_puk.y, puck_size);
64 sdf.fill(color);
65 sdf.circle(circle_puk.x, circle_puk.y, puck_size - 1. - 2. * self.hover + self.down);
66 sdf.fill(rgbv);
67
68 return sdf.result;
69 }
70 }
71
72 ColorPicker= {{ColorPicker}} {
73
74 animator: {
75 hover = {
76 default: off
77 off = {
78 from: {all: Forward {duration: 0.1}}
79 apply: {
80 draw_wheel: { down: 0.0, hover: 0.0}
81 }
82 }
83
84 on = {
85 cursor: Arrow,
86 from: {
87 all: Forward {duration: 0.1}
88 down: Forward {duration: 0.01}
89 }
90 apply: {
91 draw_wheel: {
92 down: 0.0,
93 hover: [{time: 0.0, value: 1.0}],
94 }
95 }
96 }
97
98 down = {
99 cursor: Arrow,
100 from: {all: Forward {duration: 0.2}}
101 apply: {
102 draw_wheel: {
103 down: [{time: 0.0, value: 1.0}],
104 hover: 1.0,
105 }
106 }
107 }
108 }
109 }
110 }
111}
112
113
114#[derive(Live, LiveHook, LiveRegister)]
115#[repr(C)]
116pub struct DrawColorWheel {
117 #[deref] draw_super: DrawQuad,
118 #[live] hue: f32,
119 #[live] sat: f32,
120 #[live] val: f32,
121}
122
123#[derive(Live, LiveHook, Widget)]
124pub struct ColorPicker {
125 #[redraw] #[live] draw_wheel: DrawColorWheel,
126
127 #[animator] animator: Animator,
128
129 #[walk] walk: Walk,
130
131 #[rust] pub size: f64,
132 #[rust] hue: f32,
133 #[rust] sat: f32,
134 #[rust] val: f32,
135 #[rust(ColorPickerDragMode::None)] drag_mode: ColorPickerDragMode
136}
137
138#[derive(Clone, Debug, DefaultNone)]
139pub enum ColorPickerAction {
140 Change {rgba: Vec4},
141 DoneChanging,
142 None
143}
144
145#[derive(Clone, Debug, PartialEq)]
146pub enum ColorPickerDragMode {
147 Wheel,
148 Rect,
149 None
150}
151
152impl ColorPicker {
153
154 pub fn handle_finger(&mut self, cx: &mut Cx, rel: DVec2, scope:&mut Scope) {
155
156 fn clamp(x: f64, mi: f64, ma: f64) -> f64 {if x < mi {mi} else if x > ma {ma} else {x}}
157
158 let vx = rel.x - 0.5 * self.size;
159 let vy = rel.y - 0.5 * self.size;
160 let rsize = (self.size * 0.28) / 2.0f64.sqrt();
161 let last_hue = self.hue;
162 let last_sat = self.sat;
163 let last_val = self.val;
164
165 match self.drag_mode {
166 ColorPickerDragMode::Rect => {
167 self.sat = clamp((vx + rsize) / (2.0 * rsize), 0.0, 1.0) as f32;
168 self.val = 1.0 - clamp((vy + rsize) / (2.0 * rsize), 0.0, 1.0) as f32;
169 },
170 ColorPickerDragMode::Wheel => {
171 self.hue = ((vx.atan2(vy) / std::f64::consts::PI * 0.5) - 0.33333 + 1.0) as f32;
172 },
173 _ => ()
174 }
175 let mut changed = false;
177
178 if last_hue != self.hue {
179 self.draw_wheel.apply_over(cx, live!{hue: (self.hue)});
180 changed = true;
181 }
182 if last_sat != self.sat {
183 self.draw_wheel.apply_over(cx, live!{sat: (self.sat)});
184 changed = true;
185 }
186 if last_val != self.val {
187 self.draw_wheel.apply_over(cx, live!{val: (self.val)});
188 changed = true;
189 }
190 if changed {
191 let uid = self.widget_uid();
192 cx.widget_action(uid, &scope.path, ColorPickerAction::Change {rgba: self.to_rgba()});
193 }
194 }
195
196 pub fn to_rgba(&self) -> Vec4 {
197 Vec4::from_hsva(Vec4 {x: self.hue, y: self.sat, z: self.val, w: 1.0})
198 }
199
200
201 pub fn draw_color_picker(&mut self, cx: &mut Cx2d, rgba: Vec4, walk:Walk) {
202 if self.drag_mode == ColorPickerDragMode::None {
203 let old_rgba = self.to_rgba();
205 if !rgba.is_equal_enough(&old_rgba, 0.0001) {
206 let hsva = rgba.to_hsva();
207 self.hue = hsva.x;
208 self.sat = hsva.y;
209 self.val = hsva.z;
210 }
211 }
212 self.size = cx.turtle().rect().size.y;
215 self.draw_wheel.hue = self.hue;
216 self.draw_wheel.sat = self.sat;
217 self.draw_wheel.val = self.val;
218 self.draw_wheel.draw_walk(cx, walk);
219 }
220}
221
222
223impl Widget for ColorPicker {
224 fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
225 self.animator_handle_event(cx, event);
226
227 match event.hits(cx, self.draw_wheel.area()) {
228 Hit::FingerHoverIn(_) => {
229 self.animator_play(cx, id!(hover.on));
230 }
231 Hit::FingerHoverOut(_) => {
232 self.animator_play(cx, id!(hover.off));
233 },
234 Hit::FingerDown(fe) => {
235 self.animator_play(cx, id!(hover.down));
236 let rsize = (self.size * 0.28) / 2.0f64.sqrt();
237 let rel = fe.abs - fe.rect.pos;
238 let vx = rel.x - 0.5 * self.size;
239 let vy = rel.y - 0.5 * self.size;
240 if vx >= -rsize && vx <= rsize && vy >= -rsize && vy <= rsize {
241 self.drag_mode = ColorPickerDragMode::Rect;
242 }
243 else if vx >= -0.5 * self.size && vx <= 0.5 * self.size && vy >= -0.5 * self.size && vy <= 0.5 * self.size {
244 self.drag_mode = ColorPickerDragMode::Wheel;
245 }
246 else {
247 self.drag_mode = ColorPickerDragMode::None;
248 }
249 return self.handle_finger(cx, rel, scope);
250 },
252 Hit::FingerUp(fe) => {
253 if fe.is_over && fe.device.has_hovers() {
254 self.animator_play(cx, id!(hover.on));
255 }
256 else {
257 self.animator_play(cx, id!(hover.off));
258 }
259 self.drag_mode = ColorPickerDragMode::None;
260 let uid = self.widget_uid();
261 cx.widget_action(uid, &scope.path, ColorPickerAction::DoneChanging);
262 }
263 Hit::FingerMove(fe) => {
264 let rel = fe.abs - fe.rect.pos;
265 return self.handle_finger(cx, rel, scope)
266
267 },
268 _ => ()
269 }
270 }
271
272 fn draw_walk(&mut self, cx: &mut Cx2d, _scope: &mut Scope, walk: Walk) -> DrawStep {
273 self.draw_color_picker(cx, vec4(1.0,0.0,0.0,1.0), walk);
274 DrawStep::done()
275 }
276}