1use alloc::collections::BTreeMap;
32use alloc::format;
33use alloc::rc::Rc;
34use alloc::string::String;
35
36use super::cmd::{Draw, Gfx};
37use super::consts::*;
38use super::keys::*;
39use super::value::{deg_atan2, f_round, part_id, px_mix, Track};
40use super::{Sp, Ui};
41use gfx::{Surface, FMT_PARGB};
42use rt::fx::{cvtd, minsd, sqrtd};
43
44const PART_SPEC: u32 = 0x2000_0000;
45const PART_HUE: u32 = 0x1000_0000;
46const PART_ALPHA: u32 = 0x3000_0000;
47const WHEEL_NAT: i32 = 160; const WC_MAX: usize = 4; const RG_BYTE: Range = Range::new(0, 255, 1, 0);
51const RG_HUE: Range = Range::new(0, 359, 1, 0);
52const RG_PCT: Range = Range::new(0, 100, 1, 0);
53const FLD_LEFT: [&str; 4] = ["R", "G", "B", "A"];
54const FLD_RIGHT: [&str; 4] = ["H", "S", "V", "Hex"];
55
56#[derive(Default)]
59struct Fields {
60 rgba: [i32; 4],
61 hsv: [i32; 3],
62 hex: String,
63}
64
65#[derive(Default)]
67struct Wc {
68 id: u32,
69 size: i32,
71 v: i32,
73 seen: u32,
74 ver: u32,
75 base: Surface,
76 disp: Rc<Surface>,
77}
78
79#[derive(Default)]
81pub(crate) struct S {
82 fields: BTreeMap<u32, Fields>,
83 wc: [Wc; WC_MAX],
84 wc_ver: u32,
85 spec: Option<Rc<SpectrumDraw>>,
86 hue: Option<Rc<HueDraw>>,
87}
88
89pub fn hsv_to_rgb(h: u32, s: u32, v: u32) -> u32 {
93 let vv = v.wrapping_mul(255).wrapping_add(500) / 1000;
95 if s == 0 {
96 return vv.wrapping_mul(0x01_0101) | 0xFF00_0000;
97 }
98 let mut sec = h / 600;
100 let f = h % 600;
101 if sec > 5 {
102 sec = 0;
103 }
104 let p = 1000u32.wrapping_sub(s).wrapping_mul(vv).wrapping_add(500) / 1000;
106 let q = 600_000u32.wrapping_sub(s.wrapping_mul(f)).wrapping_mul(vv).wrapping_add(300_000) / 600_000;
107 let t = 600_000u32.wrapping_sub(600u32.wrapping_sub(f).wrapping_mul(s)).wrapping_mul(vv).wrapping_add(300_000) / 600_000;
108 let (r, g, b) = match sec {
109 0 => (vv, t, p),
110 1 => (q, vv, p),
111 2 => (p, vv, t),
112 3 => (p, q, vv),
113 4 => (t, p, vv),
114 _ => (vv, p, q),
115 };
116 r << 16 | g << 8 | b | 0xFF00_0000
117}
118
119pub fn rgb_to_hsv(rgb: u32) -> (u32, u32, u32) {
121 let r = (rgb >> 16) & 255;
122 let g = (rgb >> 8) & 255;
123 let b = rgb & 255;
124 let max = r.max(g).max(b);
125 let min = r.min(g).min(b);
126 let v = (max * 1000 + 127) / 255;
128 let delta = max - min;
129 if max == 0 {
130 return (0, 0, v); }
132 let s = delta * 1000 / max;
133 let mut h: i32 = 0;
134 if delta != 0 {
135 let d = delta as i32;
136 let (r, g, b) = (r as i32, g as i32, b as i32);
137 if max as i32 == r {
138 h = (g - b) * 600 / d;
139 if h < 0 {
140 h += 3600;
141 }
142 } else if max as i32 == g {
143 h = (b - r) * 600 / d + 1200;
144 } else {
145 h = (r - g) * 600 / d + 2400;
146 }
147 }
148 let mut h = h as u32;
149 if h >= 3600 {
150 h -= 3600;
151 }
152 (h, s, v)
153}
154
155fn clamp1000(v: i32) -> i32 {
157 v.clamp(0, 1000)
158}
159
160fn put(g: &mut Gfx, x: i32, y: i32, c: u32) {
162 let o = y as usize * g.sf.stride as usize + x as usize * 4;
163 g.sf.px[o..o + 4].copy_from_slice(&c.to_le_bytes());
164}
165
166pub(crate) struct SpectrumDraw {
168 hue: u32,
169}
170
171impl Draw for SpectrumDraw {
172 fn draw(&self, g: &mut Gfx, r: [i32; 4]) {
173 let k = g.dc.clip;
174 let mut y = r[1].max(k[1]);
176 while y < r[1].wrapping_add(r[3]).min(k[3]) {
177 let v = 1000 - y.wrapping_sub(r[1]).wrapping_mul(1000) / r[3];
179 let mut x = r[0].max(k[0]);
180 while x < r[0].wrapping_add(r[2]).min(k[2]) {
181 let s = x.wrapping_sub(r[0]).wrapping_mul(1000) / r[2];
182 put(g, x, y, hsv_to_rgb(self.hue, s as u32, v as u32));
183 x += 1;
184 }
185 y += 1;
186 }
187 }
188}
189
190pub(crate) struct HueDraw;
192
193impl Draw for HueDraw {
194 fn draw(&self, g: &mut Gfx, r: [i32; 4]) {
195 let k = g.dc.clip;
196 let mut y = r[1].max(k[1]);
197 while y < r[1].wrapping_add(r[3]).min(k[3]) {
198 let mut x = r[0].max(k[0]);
199 while x < r[0].wrapping_add(r[2]).min(k[2]) {
200 let h = x.wrapping_sub(r[0]).wrapping_mul(3599) / r[2];
201 put(g, x, y, hsv_to_rgb(h as u32, 1000, 1000));
202 x += 1;
203 }
204 y += 1;
205 }
206 }
207}
208
209fn wc_render_base(sf: &mut Surface, side: i32) {
211 let half = side as f64 * 0.5; let rr = half - 1.0; let stride = sf.stride as usize;
214 for y in 0..side {
215 let row = y as usize * stride;
216 let fy = half - (y as f64 + 0.5);
218 for x in 0..side {
219 let fx = (x as f64 + 0.5) - half;
220 let r = sqrtd(fx * fx + fy * fy);
221 let c = (rr + 0.5) - r;
223 let o = row + x as usize * 4;
224 let px = if !(c > 0.0) {
225 0
226 } else {
227 let a = cvtd(minsd(c, 1.0) * 255.0); let sat = cvtd(minsd(r / rr, 1.0) * 1000.0);
230 let mut h = cvtd(deg_atan2(fy, fx) * 10.0);
232 if h < 0 {
233 h += 3600;
234 }
235 if h as u32 >= 3600 {
236 h -= 3600;
237 }
238 let c = hsv_to_rgb(h as u32, sat as u32, 1000);
239 if (a as u32) < 255 {
241 px_mix(0, c, a as u32) | (a as u32) << 24
242 } else {
243 c | 0xFF00_0000
244 }
245 };
246 sf.px[o..o + 4].copy_from_slice(&px.to_le_bytes());
247 }
248 }
249}
250
251fn wc_render_disp(base: &Surface, side: i32, v: u32) -> Surface {
253 let mut d = Surface::new(side, side, FMT_PARGB);
254 let (bs, ds) = (base.stride as usize, d.stride as usize);
255 for y in 0..side as usize {
256 for x in 0..side as usize {
257 let o = y * bs + x * 4;
258 let p = u32::from_le_bytes([base.px[o], base.px[o + 1], base.px[o + 2], base.px[o + 3]]);
259 let mut q = p & 0xFF00_0000; for sh in [0, 8, 16] {
261 let c = ((p >> sh) & 255).wrapping_mul(v).wrapping_add(500) / 1000;
262 q |= c << sh;
263 }
264 let o = y * ds + x * 4;
265 d.px[o..o + 4].copy_from_slice(&q.to_le_bytes());
266 }
267 }
268 d
269}
270
271impl Ui {
272 #[cfg_attr(jfgui_dev, track_caller)]
278 pub fn swatch(&mut self, id: u32, argb: u32, selected: bool) -> u32 {
279 let _src = self.src_scope();
280 let f = self.take_flags();
281 let st = self.style;
282 let mut sp = Sp::default();
283 let (w, h) = st.swatch_size(self, &mut sp);
284 self.place(w, h);
285 let hb = if f & ST_NOFOCUS != 0 {
286 0
287 } else {
288 self.kb_box_group(0x5357);
289 HB_FOCUS | if selected { HB_CURRENT } else { 0 }
290 };
291 let ev = self.behave(id, hb);
292 sp.place(self.wp.ctx.last_rect);
294 sp.ev = ev;
295 sp.value = argb as i32;
296 sp.value2 = selected as i32;
297 st.swatch(self, &sp);
298 self.focus_ring_draw(ev);
299 if selected {
300 self.wp.ctx.last_state |= SS_SELECTED as u32;
301 }
302 self.sem(K_SWATCH, id, None, argb as i32);
303 ev
304 }
305
306 #[cfg_attr(jfgui_dev, track_caller)]
310 pub fn color_picker(&mut self, id: u32, argb: &mut u32) -> u32 {
311 let _src = self.src_scope();
312 self.color_picker_ex(id, argb, 0)
313 }
314
315 fn cp_hsv(&mut self, id: u32) -> (u32, u32, u32) {
318 let s = self.slot(id);
319 (s.scrollx as u32, s.scrolly as u32, s.aux as u32)
320 }
321
322 fn cp_apply(&mut self, id: u32, argb: &mut u32) {
324 let (h, s, v) = self.cp_hsv(id);
325 let rgb = hsv_to_rgb(h, s, v) & 0xFF_FFFF;
326 let mut a = *argb & 0xFF00_0000;
327 if a == 0 {
328 a = 0xFF00_0000;
329 }
330 *argb = rgb | a;
331 self.slot(id).measw = *argb as i32;
332 self.repass();
333 }
334
335 fn cp_marker(&mut self, x: i32, y: i32) {
337 let st = self.style;
338 st.color_marker(self, x, y);
339 }
340
341 #[cfg_attr(jfgui_dev, track_caller)]
343 pub fn color_picker_ex(&mut self, id: u32, argb: &mut u32, flags: u32) -> u32 {
344 let _src = self.src_scope();
345 let mut ev = 0;
346 let old = *argb;
347 if let Some((owner, c)) = self.m.screen.pending {
349 if owner == id {
350 self.m.screen.pending = None;
351 let a = if *argb >> 24 == 0 { 0xFF00_0000 } else { *argb & 0xFF00_0000 };
352 *argb = a | (c & 0xFF_FFFF);
353 ev |= EV_COMMIT;
354 }
355 }
356 if *argb as i32 != self.slot(id).measw {
358 let c = *argb;
359 let (h, s, v) = rgb_to_hsv(c);
360 let sl = self.slot(id);
361 sl.measw = c as i32;
362 if s != 0 {
363 sl.scrollx = h as i32; }
365 sl.scrolly = s as i32;
366 sl.aux = v as i32;
367 }
368 self.box_begin(0, BOX_V, 12);
369 let rect = self.wp.ctx.last_rect; let (w, h) = (self.dp(256), self.dp(180));
373 let r = self.place(w, h);
374 self.ltr_begin(r[0], r[2]); let e = self.behave(id.wrapping_add(PART_SPEC), HB_FOCUS | HB_MOVES | CUR_CROSS << HB_CURSOR_SHIFT);
376 ev |= e;
377 let (dx, dy) = self.cp_keys(e, 10, true);
379 if dx | dy != 0 {
380 let sl = self.slot(id);
381 sl.scrolly = (sl.scrolly + dx).clamp(0, 1000);
382 sl.aux = (sl.aux - dy).clamp(0, 1000);
383 self.cp_apply(id, argb);
384 }
385 if e & EV_HELD != 0 {
386 let r = self.wp.ctx.last_rect;
387 let s = clamp1000(self.ltr_x(self.wp.ctx.mx).wrapping_sub(r[0]).wrapping_mul(1000) / r[2]);
388 let v = 1000 - clamp1000(self.wp.ctx.my.wrapping_sub(r[1]).wrapping_mul(1000) / r[3]);
389 let sl = *self.slot(id);
390 if s != sl.scrolly || v != sl.aux {
392 let sl = self.slot(id);
393 sl.scrolly = s;
394 sl.aux = v;
395 self.cp_apply(id, argb);
396 }
397 }
398 let r = self.wp.ctx.last_rect;
399 let (hue, sat, val) = self.cp_hsv(id);
400 let d = self.spectrum_drawing(hue);
401 self.cmd_custom(r[0], r[1], r[2], r[3], Some(d), hue as usize, hue);
402 let mx = ((sat.wrapping_mul(r[2] as u32) / 1000) as i32).wrapping_add(r[0]) << 8;
404 let my = (((1000u32.wrapping_sub(val)).wrapping_mul(r[3] as u32) / 1000) as i32).wrapping_add(r[1]) << 8;
405 self.cp_marker(mx, my);
406 self.cp_part_end(e, id.wrapping_add(PART_SPEC), super::i18n::tr("Sättigung und Helligkeit"), sat as i32, 1000);
407 self.ltr_end();
408
409 let (w, h) = (self.dp(256), self.dp(16));
411 let r = self.place(w, h);
412 self.ltr_begin(r[0], r[2]);
413 let e = self.behave(id.wrapping_add(PART_HUE), HB_FOCUS | CUR_HAND << HB_CURSOR_SHIFT | HB_GRAB_H | HB_GRAB_V);
414 ev |= e;
415 let (dx, _) = self.cp_keys(e, 10, false);
417 if dx != 0 {
418 let sl = self.slot(id);
419 sl.scrollx = (sl.scrollx + dx).clamp(0, 3599);
420 self.cp_apply(id, argb);
421 }
422 if e & EV_HELD != 0 {
423 let r = self.wp.ctx.last_rect;
424 let hh = (self.ltr_x(self.wp.ctx.mx).wrapping_sub(r[0]).wrapping_mul(3599) / r[2]).clamp(0, 3599);
425 if hh != self.slot(id).scrollx {
426 self.slot(id).scrollx = hh;
428 self.cp_apply(id, argb);
429 }
430 }
431 let r = self.wp.ctx.last_rect;
432 let d = self.hue_drawing();
433 self.cmd_custom(r[0], r[1], r[2], r[3], Some(d), 0, 0);
434 let hue = self.slot(id).scrollx as u32;
435 let mx = ((hue.wrapping_mul(r[2] as u32) / 3599) as i32).wrapping_add(r[0]) << 8;
436 let my = (r[3] << 7).wrapping_add(r[1] << 8);
437 self.cp_marker(mx, my);
438 self.cp_part_end(e, id.wrapping_add(PART_HUE), super::i18n::tr("Farbton"), hue as i32, 3599);
439 self.ltr_end();
440
441 if flags & CP_ALPHA != 0 {
443 let (w, h) = (self.dp(256), self.dp(16));
444 let r = self.place(w, h);
445 self.ltr_begin(r[0], r[2]);
446 let e = self.behave(id.wrapping_add(PART_ALPHA), HB_FOCUS | CUR_HAND << HB_CURSOR_SHIFT | HB_GRAB_H | HB_GRAB_V);
447 ev |= e;
448 let (dx, _) = self.cp_keys(e, 1, false);
450 if dx != 0 {
451 let d = if dx.abs() == 10 { dx / 10 * 16 } else { dx };
452 let a = ((*argb >> 24) as i32 + d).clamp(0, 255) as u32;
453 let na = a << 24 | (*argb & 0xFF_FFFF);
454 if na != *argb {
455 *argb = na;
456 self.slot(id).measw = na as i32; self.repass();
458 }
459 }
460 if e & EV_HELD != 0 {
461 let r = self.wp.ctx.last_rect;
462 let half = ((r[2] as u32) >> 1) as i32;
463 let a = (self.ltr_x(self.wp.ctx.mx).wrapping_sub(r[0]).wrapping_mul(255).wrapping_add(half) / r[2]).clamp(0, 255);
464 let na = (a as u32) << 24 | (*argb & 0xFF_FFFF);
465 if na != *argb {
466 *argb = na;
467 self.slot(id).measw = na as i32; self.repass();
469 }
470 }
471 let tk = Track::new(TK_RECT | TK_CHECKER, &[*argb & 0xFF_FFFF, *argb | 0xFF00_0000]);
472 let r = self.wp.ctx.last_rect;
473 self.cmd_track(r[0], r[1], r[2], r[3], &tk);
474 let mx = (((*argb >> 24).wrapping_mul(r[2] as u32) / 255) as i32).wrapping_add(r[0]) << 8;
475 let my = (r[3] << 7).wrapping_add(r[1] << 8);
476 self.cp_marker(mx, my);
477 self.cp_part_end(e, id.wrapping_add(PART_ALPHA), super::i18n::tr("Deckkraft"), (*argb >> 24) as i32, 255);
478 self.ltr_end();
479 }
480
481 self.box_begin(0, BOX_H | BOX_XCENTER, 12);
483 let (w, h) = (self.dp(48), self.dp(28)); self.place(w, h);
485 let st = self.style;
486 let r = self.wp.ctx.last_rect;
487 st.color_preview(self, *argb, r);
488 let hex = format!("#{:06X}", *argb & 0xFF_FFFF);
489 if flags & CP_FIELDS == 0 {
490 self.text(TS_BODY, &hex);
491 }
492 if flags & CP_PIPETTE != 0 && self.pick_available() {
494 let e = self.icon_button(part_id(id, 11), super::keys::IC_EYEDROPPER, super::i18n::tr("Farbe vom Bildschirm aufnehmen"));
495 if e & EV_ACT != 0 {
496 self.pick_start_for(id);
497 }
498 }
499 self.box_end();
500 if flags & CP_FIELDS != 0 {
501 ev |= self.cp_fields(id, argb, flags);
502 }
503 self.box_end();
504 self.wp.ctx.last_rect = rect;
505 self.wp.ctx.last_state = 0;
506 self.sem(K_SWATCH, id, None, *argb as i32);
507 let mut ret = ev & !EV_ACT;
508 if *argb != old {
509 ret |= EV_ACT;
510 }
511 ret
512 }
513
514 fn cp_keys(&mut self, ev: u32, step: i32, two_d: bool) -> (i32, i32) {
517 if ev & EV_FOCUS == 0 {
518 return (0, 0);
519 }
520 let s = if self.mods & MOD_SHIFT != 0 { step * 10 } else { step };
521 let mut h = (self.key(VK_RIGHT) as i32 - self.key(VK_LEFT) as i32) * s;
522 if self.rtl {
523 h = -h; }
525 let up = (self.key(VK_UP) as i32 - self.key(VK_DOWN) as i32) * s;
526 let (mut dx, dy) = if two_d { (h, -up) } else { (h + up, 0) };
527 if self.key(VK_HOME) {
528 dx = -100_000;
529 }
530 if self.key(VK_END) {
531 dx = 100_000;
532 }
533 if dx | dy != 0 {
534 self.wp.ctx.focus_visible = true;
535 }
536 (dx, dy)
537 }
538
539 fn cp_part_end(&mut self, ev: u32, id: u32, label: &str, value: i32, max: i32) {
541 self.focus_ring_draw(ev);
542 let r = self.sem(K_SLIDER, id, Some(label), value);
543 r.max = max;
544 }
545
546 fn spectrum_drawing(&mut self, hue: u32) -> Rc<dyn Draw> {
548 let s = &mut self.m.color;
549 match &s.spec {
550 Some(d) if d.hue == hue => d.clone(),
551 _ => {
552 let d = Rc::new(SpectrumDraw { hue });
553 s.spec = Some(d.clone());
554 d
555 }
556 }
557 }
558
559 fn hue_drawing(&mut self) -> Rc<dyn Draw> {
560 self.m.color.hue.get_or_insert_with(|| Rc::new(HueDraw)).clone()
561 }
562
563 fn cp_fields(&mut self, id: u32, argb: &mut u32, flags: u32) -> u32 {
566 let mut ev = 0;
567 let (h, s, v) = self.cp_hsv(id);
569 {
570 let f = self.m.color.fields.entry(id).or_default();
571 let c = *argb;
572 f.rgba = [((c >> 16) & 255) as i32, ((c >> 8) & 255) as i32, (c & 255) as i32, (c >> 24) as i32];
573 let mut hh = h.wrapping_add(5) / 10;
574 if hh >= 360 {
575 hh = 0;
576 }
577 f.hsv = [hh as i32, (s.wrapping_add(5) / 10) as i32, (v.wrapping_add(5) / 10) as i32];
578 }
579 if self.wp.ctx.focus != part_id(id, 10) {
581 self.cf_hex_text(id, *argb, flags);
582 }
583 self.box_begin(0, BOX_V, 4);
584 for row in 0..4usize {
585 self.box_begin(0, BOX_H | BOX_XCENTER, 8);
586 if row == 3 && flags & CP_ALPHA == 0 {
588 self.spacer(24 + 8 + 72);
589 } else {
590 self.next_size(24, 0);
591 self.text(TS_BODY, FLD_LEFT[row]);
592 self.next_size(72, 0);
593 let mut val = self.m.color.fields.get(&id).map_or(0, |f| f.rgba[row]);
594 let e = self.number_box(part_id(id, row as u32 + 3), &mut val, &RG_BYTE);
595 if let Some(f) = self.m.color.fields.get_mut(&id) {
596 f.rgba[row] = val;
597 }
598 ev |= e;
599 if e & EV_ACT != 0 {
600 self.cf_apply_rgba(id, argb, flags);
601 }
602 }
603 self.spacer(8);
604 self.next_size(32, 0);
605 self.text(TS_BODY, FLD_RIGHT[row]);
606 if row == 3 {
607 self.next_size(88, 0);
608 let mut t = self.m.color.fields.get(&id).map_or(String::new(), |f| f.hex.clone());
609 let e = self.textbox(part_id(id, 10), None, &mut t, 12);
610 if let Some(f) = self.m.color.fields.get_mut(&id) {
611 f.hex = t;
612 }
613 ev |= e & !EV_ACT;
614 if e & EV_COMMIT != 0 {
615 self.cf_apply_hex(id, argb, flags);
616 self.cf_hex_text(id, *argb, flags); self.repass();
618 }
619 } else {
620 self.next_size(72, 0);
621 let mut val = self.m.color.fields.get(&id).map_or(0, |f| f.hsv[row]);
622 let rg = if row == 0 { &RG_HUE } else { &RG_PCT };
623 let e = self.number_box(part_id(id, row as u32 + 7), &mut val, rg);
624 if let Some(f) = self.m.color.fields.get_mut(&id) {
625 f.hsv[row] = val;
626 }
627 ev |= e;
628 if e & EV_ACT != 0 {
629 self.cf_apply_hsv(id, argb);
630 }
631 }
632 self.box_end();
633 }
634 self.box_end();
635 ev
636 }
637
638 fn cf_hex_text(&mut self, id: u32, argb: u32, flags: u32) {
640 let t = if flags & CP_ALPHA != 0 { format!("{:08X}", argb) } else { format!("{:06X}", argb & 0xFF_FFFF) };
641 self.m.color.fields.entry(id).or_default().hex = t;
642 }
643
644 fn cf_apply_rgba(&mut self, id: u32, argb: &mut u32, flags: u32) {
646 let f = self.m.color.fields.entry(id).or_default().rgba;
647 let mut c = (f[3] as u32) << 24 | (f[0] as u32) << 16 | (f[1] as u32) << 8 | f[2] as u32;
648 if flags & CP_ALPHA == 0 {
649 c = (c & 0xFF_FFFF) | (*argb & 0xFF00_0000);
650 }
651 self.cf_set_argb(id, argb, c);
652 }
653
654 fn cf_apply_hsv(&mut self, id: u32, argb: &mut u32) {
656 let f = self.m.color.fields.entry(id).or_default().hsv;
657 let sl = self.slot(id);
658 sl.scrollx = f[0].wrapping_mul(10);
659 sl.scrolly = f[1].wrapping_mul(10);
660 sl.aux = f[2].wrapping_mul(10);
661 let (h, s, v) = (sl.scrollx as u32, sl.scrolly as u32, sl.aux as u32);
662 let c = (hsv_to_rgb(h, s, v) & 0xFF_FFFF) | (*argb & 0xFF00_0000);
663 *argb = c;
664 self.slot(id).measw = c as i32;
665 self.repass();
666 }
667
668 fn cf_apply_hex(&mut self, id: u32, argb: &mut u32, _flags: u32) {
670 let t = self.m.color.fields.entry(id).or_default().hex.clone();
671 let b = t.as_bytes();
672 let n = b.iter().take_while(|&&c| c != 0).filter(|&&c| matches!(c | 32, b'0'..=b'9' | b'a'..=b'f')).count();
674 if n == 0 {
675 return;
676 }
677 let mut i = 0;
678 while i < b.len() && b[i] == b' ' {
679 i += 1;
680 }
681 let (mut c, _) = super::runtime::parse_hex(&b[i..]);
682 if n <= 6 {
683 c = (c & 0xFF_FFFF) | (*argb & 0xFF00_0000);
684 }
685 self.cf_set_argb(id, argb, c);
686 }
687
688 fn cf_set_argb(&mut self, id: u32, argb: &mut u32, c: u32) {
690 if c == *argb {
691 return;
692 }
693 *argb = c;
694 let (h, s, v) = rgb_to_hsv(c);
695 let sl = self.slot(id);
696 sl.measw = c as i32;
697 if s != 0 {
698 sl.scrollx = h as i32;
699 }
700 sl.scrolly = s as i32;
701 sl.aux = v as i32;
702 self.repass();
703 }
704
705 fn wc_get(&mut self, id: u32, side: i32, v: i32) -> Rc<Surface> {
709 let frame = self.frame;
710 let s = &mut self.m.color;
711 let mut cand = 0;
713 let mut age = u32::MAX;
714 let mut found = None;
715 for (i, e) in s.wc.iter().enumerate() {
716 if e.id == id {
717 found = Some(i);
718 break;
719 }
720 let a = if e.id == 0 { 0 } else { e.seen };
721 if a < age {
722 age = a;
723 cand = i;
724 }
725 }
726 let i = match found {
727 Some(i) => i,
728 None => {
729 let e = &mut s.wc[cand];
730 *e = Wc { id, ..Wc::default() };
731 cand
732 }
733 };
734 let e = &mut s.wc[i];
735 e.seen = frame;
736 if e.size != side {
737 e.base = Surface::new(side, side, FMT_PARGB);
739 e.size = side;
740 e.v = -1;
741 wc_render_base(&mut e.base, side);
742 }
743 if e.v != v {
744 e.v = v;
745 e.disp = Rc::new(wc_render_disp(&e.base, side, v as u32));
746 s.wc_ver = s.wc_ver.wrapping_add(1);
747 e.ver = s.wc_ver;
748 }
749 s.wc[i].disp.clone()
750 }
751
752 #[cfg_attr(jfgui_dev, track_caller)]
755 pub fn color_wheel(&mut self, id: u32, color: &mut [u32], flags: u32) -> u32 {
756 let _src = self.src_scope();
757 let hsv = flags & CW_HSV != 0;
758 let mut keych = false;
759 let (mut h, mut s, v);
761 if hsv {
762 h = color[0] as i32;
763 s = color[1] as i32;
764 v = color[2] as i32;
765 } else {
766 let sl = *self.slot(id);
767 let valid = sl.afrom == 1 && sl.ato == 0; if color[0] as i32 == sl.measw && valid {
769 h = sl.scrollx;
770 s = sl.scrolly;
771 v = sl.aux;
772 } else {
773 let (hh, ss, vv) = rgb_to_hsv(color[0]);
774 h = hh as i32;
775 if ss == 0 && valid {
776 h = sl.scrollx; }
778 s = ss as i32;
779 v = vv as i32;
780 }
781 }
782 let (oh, os) = (h, s);
783 let stflags = self.take_flags();
784 let n = self.dp(WHEEL_NAT);
785 self.place(n, n);
786 let mut hb = HB_FOCUS | CUR_CROSS << HB_CURSOR_SHIFT;
787 if stflags & ST_DISABLED != 0 {
788 hb |= HB_DISABLED;
789 }
790 let mut ev = self.behave(id, hb);
791 let r = self.wp.ctx.last_rect;
793 self.ltr_begin(r[0], r[2]); let side = r[2].min(r[3]).max(8);
795 let wx = (r[2].wrapping_sub(side) >> 1).wrapping_add(r[0]);
796 let wy = (r[3].wrapping_sub(side) >> 1).wrapping_add(r[1]);
797 let rad = ((side as u32 >> 1) as i32).wrapping_sub(1);
798 if ev & EV_HELD != 0 {
799 let half = (side as u32 >> 1) as i32;
801 let dx = self.ltr_x(self.wp.ctx.mx).wrapping_sub(wx).wrapping_sub(half); let dy = wy.wrapping_add(half).wrapping_sub(self.wp.ctx.my); let fy = dy as f64;
804 let fx = dx as f64;
805 if dx | dy != 0 {
806 let mut hh = f_round(deg_atan2(fy, fx) * 10.0) as i32;
808 if hh < 0 {
809 hh += 3600;
810 }
811 if hh as u32 >= 3600 {
812 hh -= 3600;
813 }
814 h = hh;
815 }
816 let d = sqrtd(fy * fy + fx * fx) / rad as f64;
817 s = f_round(minsd(d, 1.0) * 1000.0) as i32;
818 } else if ev & EV_FOCUS != 0 {
819 let st = if self.mods & MOD_SHIFT != 0 { 100 } else { 10 }; let mut dh = self.key(VK_RIGHT) as i32;
821 dh -= self.key(VK_LEFT) as i32;
822 let mut ds = self.key(VK_UP) as i32;
823 ds -= self.key(VK_DOWN) as i32;
824 if dh | ds != 0 {
825 keych = true;
826 self.wp.ctx.focus_visible = true;
827 let mut rem = (h.wrapping_add(dh.wrapping_mul(st)).wrapping_add(3600)) % 3600; if rem < 0 {
829 rem += 3600;
830 }
831 h = rem;
832 s = s.wrapping_add(ds.wrapping_mul(st)).clamp(0, 1000);
833 }
834 }
835 if h != oh || s != os {
836 ev |= EV_ACT;
837 if keych {
838 ev |= EV_COMMIT;
839 }
840 self.repass();
841 if hsv {
842 color[0] = h as u32;
843 color[1] = s as u32;
844 } else {
845 let c = hsv_to_rgb(h as u32, s as u32, v as u32) & 0xFF_FFFF;
846 color[0] = c | (color[0] & 0xFF00_0000);
847 }
848 }
849 if !hsv {
851 let c = color[0];
852 let sl = self.slot(id);
853 sl.measw = c as i32;
854 sl.afrom = 1;
855 sl.ato = 0;
856 sl.scrollx = h;
857 sl.scrolly = s;
858 sl.aux = v;
859 }
860 let img = self.wc_get(id, side, v.clamp(0, 1000));
862 let ver = self.m.color.wc.iter().find(|e| e.id == id).map_or(0, |e| e.ver);
863 self.draw_image(&img, ver, wx, wy, 0, 0);
864 let st = self.style;
866 let mut sp = Sp { x: wx, y: wy, w: side, h: side, value: h, value2: s, ..Sp::default() };
867 sp.s[0] = rad;
868 sp.s[1] = self.focus_ring(ev) as i32;
869 st.color_wheel(self, &sp);
870 self.ltr_end();
871 self.sem(K_SLIDER, id, None, h).max = s;
872 ev
873 }
874}