1use crate::{
6 color_picker,
7 core::{
8 color::{HexString, Hsv},
9 overlay::Position,
10 },
11 style::{self, Status, color_picker::Style, style_state::StyleState},
12};
13
14use crate::iced_aw_font::advanced_text::{cancel, ok};
15use iced_core::{
16 Alignment, Border, Clipboard, Color, Element, Event, Layout, Length, Overlay, Padding, Pixels,
17 Point, Rectangle, Renderer as _, Shell, Size, Text, Vector, Widget,
18 alignment::{Horizontal, Vertical},
19 event, keyboard,
20 layout::{Limits, Node},
21 mouse::{self, Cursor},
22 overlay, renderer,
23 text::Renderer as _,
24 touch,
25 widget::{self, tree::Tree},
26};
27use iced_widget::{
28 Button, Column, Renderer, Row,
29 canvas::{self, LineCap, Path, Stroke},
30 graphics::geometry::Renderer as _,
31 text::{self, Wrapping},
32};
33use std::collections::HashMap;
34
35const PADDING: Padding = Padding::new(10.0);
37const SPACING: Pixels = Pixels(15.0);
39const BUTTON_SPACING: Pixels = Pixels(5.0);
41
42const SAT_VALUE_STEP: f32 = 0.005;
44const HUE_STEP: i32 = 1;
46const RGBA_STEP: i16 = 1;
48
49#[allow(missing_debug_implementations)]
51pub struct ColorPickerOverlay<'a, 'b, Message, Theme>
52where
53 Message: Clone,
54 Theme: style::color_picker::Catalog + iced_widget::button::Catalog,
55 'b: 'a,
56{
57 state: &'a mut State,
59 cancel_button: Button<'a, Message, Theme, Renderer>,
61 submit_button: Button<'a, Message, Theme, Renderer>,
63 on_submit: &'a dyn Fn(Color) -> Message,
65 position: Point,
67 class: &'a <Theme as style::color_picker::Catalog>::Class<'b>,
69 tree: &'a mut Tree,
71 viewport: Rectangle,
72}
73
74impl<'a, 'b, Message, Theme> ColorPickerOverlay<'a, 'b, Message, Theme>
75where
76 Message: 'static + Clone,
77 Theme: 'a
78 + style::color_picker::Catalog
79 + iced_widget::button::Catalog
80 + iced_widget::text::Catalog,
81 'b: 'a,
82{
83 pub fn new(
85 state: &'a mut color_picker::State,
86 on_cancel: Message,
87 on_submit: &'a dyn Fn(Color) -> Message,
88 position: Point,
89 class: &'a <Theme as style::color_picker::Catalog>::Class<'b>,
90 tree: &'a mut Tree,
91 viewport: Rectangle,
92 ) -> Self {
93 let color_picker::State { overlay_state, .. } = state;
94
95 let (cancel_content, cancel_font, _cancel_shaping) = cancel();
96 let (submit_content, submit_font, _submit_shaping) = ok();
97
98 ColorPickerOverlay {
99 state: overlay_state,
100 cancel_button: Button::new(
101 iced_widget::Text::new(cancel_content)
102 .align_x(Horizontal::Center)
103 .width(Length::Fill)
104 .font(cancel_font),
105 )
106 .width(Length::Fill)
107 .on_press(on_cancel.clone()),
108 submit_button: Button::new(
109 iced_widget::Text::new(submit_content)
110 .align_x(Horizontal::Center)
111 .width(Length::Fill)
112 .font(submit_font),
113 )
114 .width(Length::Fill)
115 .on_press(on_cancel), on_submit,
117 position,
118 class,
119 tree,
120 viewport,
121 }
122 }
123
124 #[must_use]
126 pub fn overlay(self) -> overlay::Element<'a, Message, Theme, Renderer> {
127 overlay::Element::new(Box::new(self))
128 }
129
130 fn clear_cache(&self) {
132 self.state.clear_cache();
133 }
134
135 fn on_event_hsv_color(
137 &mut self,
138 event: &Event,
139 layout: Layout<'_>,
140 cursor: Cursor,
141 ) -> event::Status {
142 let mut hsv_color_children = layout.children();
143
144 let hsv_color: Hsv = self.state.color.into();
145 let mut color_changed = false;
146
147 let sat_value_bounds = hsv_color_children
148 .next()
149 .expect("widget: Layout should have a sat/value layout")
150 .bounds();
151 let hue_bounds = hsv_color_children
152 .next()
153 .expect("widget: Layout should have a hue layout")
154 .bounds();
155
156 match event {
157 Event::Mouse(mouse::Event::WheelScrolled { delta }) => match delta {
158 mouse::ScrollDelta::Lines { y, .. } | mouse::ScrollDelta::Pixels { y, .. } => {
159 let move_value =
160 |value: u16, y: f32| ((i32::from(value) + y as i32).rem_euclid(360)) as u16;
161
162 if cursor.is_over(hue_bounds) {
163 self.state.color = Color {
164 a: self.state.color.a,
165 ..Hsv {
166 hue: move_value(hsv_color.hue, *y),
167 ..hsv_color
168 }
169 .into()
170 };
171 color_changed = true;
172 }
173 }
174 },
175 Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left))
176 | Event::Touch(touch::Event::FingerPressed { .. }) => {
177 if cursor.is_over(sat_value_bounds) {
178 self.state.color_bar_dragged = ColorBarDragged::SatValue;
179 self.state.focus = Focus::SatValue;
180 }
181 if cursor.is_over(hue_bounds) {
182 self.state.color_bar_dragged = ColorBarDragged::Hue;
183 self.state.focus = Focus::Hue;
184 }
185 }
186 Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left))
187 | Event::Touch(touch::Event::FingerLifted { .. } | touch::Event::FingerLost { .. }) => {
188 self.state.color_bar_dragged = ColorBarDragged::None;
189 }
190 _ => {}
191 }
192
193 let calc_percentage_sat =
194 |cursor_position: Point| (cursor_position.x.max(0.0) / sat_value_bounds.width).min(1.0);
195
196 let calc_percentage_value = |cursor_position: Point| {
197 (cursor_position.y.max(0.0) / sat_value_bounds.height).min(1.0)
198 };
199
200 let calc_hue = |cursor_position: Point| {
201 ((cursor_position.x.max(0.0) / hue_bounds.width).min(1.0) * 360.0) as u16
202 };
203
204 match self.state.color_bar_dragged {
205 ColorBarDragged::SatValue => {
206 self.state.color = Color {
207 a: self.state.color.a,
208 ..Hsv {
209 saturation: cursor
210 .position_in(sat_value_bounds)
211 .map(calc_percentage_sat)
212 .unwrap_or_default(),
213 value: cursor
214 .position_in(sat_value_bounds)
215 .map(calc_percentage_value)
216 .unwrap_or_default(),
217 ..hsv_color
218 }
219 .into()
220 };
221 color_changed = true;
222 }
223 ColorBarDragged::Hue => {
224 self.state.color = Color {
225 a: self.state.color.a,
226 ..Hsv {
227 hue: cursor
228 .position_in(hue_bounds)
229 .map(calc_hue)
230 .unwrap_or_default(),
231 ..hsv_color
232 }
233 .into()
234 };
235 color_changed = true;
236 }
237 _ => {}
238 }
239
240 if color_changed {
241 event::Status::Captured
242 } else {
243 event::Status::Ignored
244 }
245 }
246
247 #[allow(clippy::too_many_lines)]
249 fn on_event_rgba_color(
250 &mut self,
251 event: &Event,
252 layout: Layout<'_>,
253 cursor: Cursor,
254 ) -> event::Status {
255 let mut rgba_color_children = layout.children();
256 let mut color_changed = false;
257
258 let mut red_row_children = rgba_color_children
259 .next()
260 .expect("widget: Layout should have a red row layout")
261 .children();
262 let _ = red_row_children.next();
263 let red_bar_bounds = red_row_children
264 .next()
265 .expect("widget: Layout should have a red bar layout")
266 .bounds();
267
268 let mut green_row_children = rgba_color_children
269 .next()
270 .expect("widget: Layout should have a green row layout")
271 .children();
272 let _ = green_row_children.next();
273 let green_bar_bounds = green_row_children
274 .next()
275 .expect("widget: Layout should have a green bar layout")
276 .bounds();
277
278 let mut blue_row_children = rgba_color_children
279 .next()
280 .expect("widget: Layout should have a blue row layout")
281 .children();
282 let _ = blue_row_children.next();
283 let blue_bar_bounds = blue_row_children
284 .next()
285 .expect("widget: Layout should have a blue bar layout")
286 .bounds();
287
288 let mut alpha_row_children = rgba_color_children
289 .next()
290 .expect("widget: Layout should have an alpha row layout")
291 .children();
292 let _ = alpha_row_children.next();
293 let alpha_bar_bounds = alpha_row_children
294 .next()
295 .expect("widget: Layout should have an alpha bar layout")
296 .bounds();
297
298 match event {
299 Event::Mouse(mouse::Event::WheelScrolled { delta }) => match delta {
300 mouse::ScrollDelta::Lines { y, .. } | mouse::ScrollDelta::Pixels { y, .. } => {
301 let move_value =
302 |value: f32, y: f32| value.mul_add(255.0, y).clamp(0.0, 255.0) / 255.0;
304
305 if cursor.is_over(red_bar_bounds) {
306 self.state.color = Color {
307 r: move_value(self.state.color.r, *y),
308 ..self.state.color
309 };
310 color_changed = true;
311 }
312 if cursor.is_over(green_bar_bounds) {
313 self.state.color = Color {
314 g: move_value(self.state.color.g, *y),
315 ..self.state.color
316 };
317 color_changed = true;
318 }
319 if cursor.is_over(blue_bar_bounds) {
320 self.state.color = Color {
321 b: move_value(self.state.color.b, *y),
322 ..self.state.color
323 };
324 color_changed = true;
325 }
326 if cursor.is_over(alpha_bar_bounds) {
327 self.state.color = Color {
328 a: move_value(self.state.color.a, *y),
329 ..self.state.color
330 };
331 color_changed = true;
332 }
333 }
334 },
335 Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left))
336 | Event::Touch(touch::Event::FingerPressed { .. }) => {
337 if cursor.is_over(red_bar_bounds) {
338 self.state.color_bar_dragged = ColorBarDragged::Red;
339 self.state.focus = Focus::Red;
340 }
341 if cursor.is_over(green_bar_bounds) {
342 self.state.color_bar_dragged = ColorBarDragged::Green;
343 self.state.focus = Focus::Green;
344 }
345 if cursor.is_over(blue_bar_bounds) {
346 self.state.color_bar_dragged = ColorBarDragged::Blue;
347 self.state.focus = Focus::Blue;
348 }
349 if cursor.is_over(alpha_bar_bounds) {
350 self.state.color_bar_dragged = ColorBarDragged::Alpha;
351 self.state.focus = Focus::Alpha;
352 }
353 }
354 Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left))
355 | Event::Touch(touch::Event::FingerLifted { .. } | touch::Event::FingerLost { .. }) => {
356 self.state.color_bar_dragged = ColorBarDragged::None;
357 }
358 _ => {}
359 }
360
361 let calc_percentage = |bounds: Rectangle, cursor_position: Point| {
362 (cursor_position.x.max(0.0) / bounds.width).min(1.0)
363 };
364
365 match self.state.color_bar_dragged {
366 ColorBarDragged::Red => {
367 self.state.color = Color {
368 r: cursor
369 .position_in(red_bar_bounds)
370 .map(|position| calc_percentage(red_bar_bounds, position))
371 .unwrap_or_default(),
372 ..self.state.color
373 };
374 color_changed = true;
375 }
376 ColorBarDragged::Green => {
377 self.state.color = Color {
378 g: cursor
379 .position_in(green_bar_bounds)
380 .map(|position| calc_percentage(green_bar_bounds, position))
381 .unwrap_or_default(),
382 ..self.state.color
383 };
384 color_changed = true;
385 }
386 ColorBarDragged::Blue => {
387 self.state.color = Color {
388 b: cursor
389 .position_in(blue_bar_bounds)
390 .map(|position| calc_percentage(blue_bar_bounds, position))
391 .unwrap_or_default(),
392 ..self.state.color
393 };
394 color_changed = true;
395 }
396 ColorBarDragged::Alpha => {
397 self.state.color = Color {
398 a: cursor
399 .position_in(alpha_bar_bounds)
400 .map(|position| calc_percentage(alpha_bar_bounds, position))
401 .unwrap_or_default(),
402 ..self.state.color
403 };
404 color_changed = true;
405 }
406 _ => {}
407 }
408
409 if color_changed {
410 event::Status::Captured
411 } else {
412 event::Status::Ignored
413 }
414 }
415
416 fn on_event_keyboard(&mut self, event: &Event) -> event::Status {
418 if self.state.focus == Focus::None {
419 return event::Status::Ignored;
420 }
421
422 if let Event::Keyboard(keyboard::Event::KeyPressed { key, .. }) = event {
423 let mut status = event::Status::Ignored;
424
425 if matches!(key, keyboard::Key::Named(keyboard::key::Named::Tab)) {
426 if self.state.keyboard_modifiers.shift() {
427 self.state.focus = self.state.focus.previous();
428 } else {
429 self.state.focus = self.state.focus.next();
430 }
431 self.clear_cache();
433 } else {
434 let sat_value_handle = |key_code: &keyboard::Key, color: &mut Color| {
435 let mut hsv_color: Hsv = (*color).into();
436 let mut status = event::Status::Ignored;
437
438 match key_code {
439 keyboard::Key::Named(keyboard::key::Named::ArrowLeft) => {
440 hsv_color.saturation -= SAT_VALUE_STEP;
441 status = event::Status::Captured;
442 }
443 keyboard::Key::Named(keyboard::key::Named::ArrowRight) => {
444 hsv_color.saturation += SAT_VALUE_STEP;
445 status = event::Status::Captured;
446 }
447 keyboard::Key::Named(keyboard::key::Named::ArrowUp) => {
448 hsv_color.value -= SAT_VALUE_STEP;
449 status = event::Status::Captured;
450 }
451 keyboard::Key::Named(keyboard::key::Named::ArrowDown) => {
452 hsv_color.value += SAT_VALUE_STEP;
453 status = event::Status::Captured;
454 }
455 _ => {}
456 }
457
458 hsv_color.saturation = hsv_color.saturation.clamp(0.0, 1.0);
459 hsv_color.value = hsv_color.value.clamp(0.0, 1.0);
460
461 *color = Color {
462 a: color.a,
463 ..hsv_color.into()
464 };
465 status
466 };
467
468 let hue_handle = |key_code: &keyboard::Key, color: &mut Color| {
469 let mut hsv_color: Hsv = (*color).into();
470 let mut status = event::Status::Ignored;
471
472 let mut value = i32::from(hsv_color.hue);
473
474 match key_code {
475 keyboard::Key::Named(
476 keyboard::key::Named::ArrowLeft | keyboard::key::Named::ArrowDown,
477 ) => {
478 value -= HUE_STEP;
479 status = event::Status::Captured;
480 }
481 keyboard::Key::Named(
482 keyboard::key::Named::ArrowRight | keyboard::key::Named::ArrowUp,
483 ) => {
484 value += HUE_STEP;
485 status = event::Status::Captured;
486 }
487 _ => {}
488 }
489
490 hsv_color.hue = value.rem_euclid(360) as u16;
491
492 *color = Color {
493 a: color.a,
494 ..hsv_color.into()
495 };
496
497 status
498 };
499
500 let rgba_bar_handle = |key_code: &keyboard::Key, value: &mut f32| {
501 let mut byte_value = (*value * 255.0) as i16;
502 let mut status = event::Status::Captured;
503
504 match key_code {
505 keyboard::Key::Named(
506 keyboard::key::Named::ArrowLeft | keyboard::key::Named::ArrowDown,
507 ) => {
508 byte_value -= RGBA_STEP;
509 status = event::Status::Captured;
510 }
511 keyboard::Key::Named(
512 keyboard::key::Named::ArrowRight | keyboard::key::Named::ArrowUp,
513 ) => {
514 byte_value += RGBA_STEP;
515 status = event::Status::Captured;
516 }
517 _ => {}
518 }
519 *value = f32::from(byte_value.clamp(0, 255)) / 255.0;
520
521 status
522 };
523
524 match self.state.focus {
525 Focus::SatValue => status = sat_value_handle(key, &mut self.state.color),
526 Focus::Hue => status = hue_handle(key, &mut self.state.color),
527 Focus::Red => status = rgba_bar_handle(key, &mut self.state.color.r),
528 Focus::Green => status = rgba_bar_handle(key, &mut self.state.color.g),
529 Focus::Blue => status = rgba_bar_handle(key, &mut self.state.color.b),
530 Focus::Alpha => status = rgba_bar_handle(key, &mut self.state.color.a),
531 _ => {}
532 }
533 }
534
535 status
536 } else if let Event::Keyboard(keyboard::Event::ModifiersChanged(modifiers)) = event {
537 self.state.keyboard_modifiers = *modifiers;
538 event::Status::Ignored
539 } else {
540 event::Status::Ignored
541 }
542 }
543}
544
545impl<'a, Message, Theme> Overlay<Message, Theme, Renderer>
546 for ColorPickerOverlay<'a, '_, Message, Theme>
547where
548 Message: 'static + Clone,
549 Theme: 'a
550 + style::color_picker::Catalog
551 + iced_widget::button::Catalog
552 + iced_widget::text::Catalog,
553{
554 fn layout(&mut self, renderer: &Renderer, bounds: Size) -> Node {
555 let (max_width, max_height) = if bounds.width > bounds.height {
556 (600.0, 300.0)
557 } else {
558 (300.0, 600.0)
559 };
560
561 let limits = Limits::new(Size::ZERO, bounds)
562 .shrink(PADDING)
563 .width(Length::Fill)
564 .height(Length::Fill)
565 .max_width(max_width)
566 .max_height(max_height);
567
568 let divider = if bounds.width > bounds.height {
569 Row::<(), Theme, Renderer>::new()
570 .spacing(SPACING)
571 .push(Row::new().width(Length::Fill).height(Length::Fill))
572 .push(Row::new().width(Length::Fill).height(Length::Fill))
573 .layout(self.tree, renderer, &limits)
574 } else {
575 Column::<(), Theme, Renderer>::new()
576 .spacing(SPACING)
577 .push(Row::new().width(Length::Fill).height(Length::Fill))
578 .push(Row::new().width(Length::Fill).height(Length::Fill))
579 .layout(self.tree, renderer, &limits)
580 };
581
582 let mut divider_children = divider.children().iter();
583
584 let block1_bounds = divider_children
585 .next()
586 .expect("Divider should have a first child")
587 .bounds();
588 let block2_bounds = divider_children
589 .next()
590 .expect("Divider should have a second child")
591 .bounds();
592
593 let block1_node = block1_layout(self, renderer, block1_bounds);
595
596 let block2_node = block2_layout(self, renderer, block2_bounds);
598
599 let (width, height) = if bounds.width > bounds.height {
600 (
601 block1_node.size().width + block2_node.size().width + SPACING.0, block2_node.size().height,
603 )
604 } else {
605 (
606 block2_node.size().width,
607 block1_node.size().height + block2_node.size().height + SPACING.0,
608 )
609 };
610
611 let mut node =
612 Node::with_children(Size::new(width, height), vec![block1_node, block2_node]);
613
614 node.center_and_bounce(self.position, bounds);
615 node
616 }
617
618 fn update(
619 &mut self,
620 event: &Event,
621 layout: Layout<'_>,
622 cursor: Cursor,
623 renderer: &Renderer,
624 clipboard: &mut dyn Clipboard,
625 shell: &mut Shell<Message>,
626 ) {
627 if event::Status::Captured == self.on_event_keyboard(event) {
628 self.clear_cache();
629 shell.capture_event();
630 shell.request_redraw();
631 return;
632 }
633
634 let mut children = layout.children();
635 let block1_layout = children
637 .next()
638 .expect("widget: Layout should have a 1. block layout");
639 let hsv_color_status = self.on_event_hsv_color(event, block1_layout, cursor);
640 let mut block2_children = children
644 .next()
645 .expect("widget: Layout should have a 2. block layout")
646 .children();
647
648 let rgba_color_layout = block2_children
650 .next()
651 .expect("widget: Layout should have a RGBA color layout");
652 let rgba_color_status = self.on_event_rgba_color(event, rgba_color_layout, cursor);
653
654 let mut fake_messages: Vec<Message> = Vec::new();
655
656 let _text_input_layout = block2_children
658 .next()
659 .expect("widget: Layout should have a hex text layout");
660
661 let cancel_button_layout = block2_children
663 .next()
664 .expect("widget: Layout should have a cancel button layout for a ColorPicker");
665 self.cancel_button.update(
666 &mut self.tree.children[0],
667 event,
668 cancel_button_layout,
669 cursor,
670 renderer,
671 clipboard,
672 shell,
673 &layout.bounds(),
674 );
675
676 let submit_button_layout = block2_children
677 .next()
678 .expect("widget: Layout should have a submit button layout for a ColorPicker");
679 self.submit_button.update(
680 &mut self.tree.children[1],
681 event,
682 submit_button_layout,
683 cursor,
684 renderer,
685 clipboard,
686 &mut Shell::new(&mut fake_messages),
687 &layout.bounds(),
688 );
689
690 if !fake_messages.is_empty() {
691 shell.publish((self.on_submit)(self.state.color));
692 shell.capture_event();
693 shell.request_redraw();
694 }
695 if hsv_color_status == event::Status::Captured
698 || rgba_color_status == event::Status::Captured
699 {
700 self.clear_cache();
701 shell.capture_event();
702 shell.request_redraw();
703 }
704 }
705
706 fn mouse_interaction(
707 &self,
708 layout: Layout<'_>,
709 cursor: mouse::Cursor,
710 renderer: &Renderer,
711 ) -> mouse::Interaction {
712 let mut children = layout.children();
713
714 let mouse_interaction = mouse::Interaction::default();
715
716 let block1_layout = children
718 .next()
719 .expect("Graphics: Layout should have a 1. block layout");
720 let mut block1_mouse_interaction = mouse::Interaction::default();
721 let mut hsv_color_children = block1_layout.children();
723 let sat_value_layout = hsv_color_children
724 .next()
725 .expect("Graphics: Layout should have a sat/value layout");
726 if cursor.is_over(sat_value_layout.bounds()) {
727 block1_mouse_interaction = block1_mouse_interaction.max(mouse::Interaction::Pointer);
728 }
729 let hue_layout = hsv_color_children
730 .next()
731 .expect("Graphics: Layout should have a hue layout");
732 if cursor.is_over(hue_layout.bounds()) {
733 block1_mouse_interaction = block1_mouse_interaction.max(mouse::Interaction::Pointer);
734 }
735
736 let block2_layout = children
738 .next()
739 .expect("Graphics: Layout should have a 2. block layout");
740 let mut block2_mouse_interaction = mouse::Interaction::default();
741 let mut block2_children = block2_layout.children();
742 let rgba_color_layout = block2_children
744 .next()
745 .expect("Graphics: Layout should have a RGBA color layout");
746 let mut rgba_color_children = rgba_color_layout.children();
747
748 let f = |layout: Layout<'_>, cursor: Cursor| {
749 let mut children = layout.children();
750
751 let _label_layout = children.next();
752 let bar_layout = children
753 .next()
754 .expect("Graphics: Layout should have a bar layout");
755
756 if cursor.is_over(bar_layout.bounds()) {
757 mouse::Interaction::ResizingHorizontally
758 } else {
759 mouse::Interaction::default()
760 }
761 };
762 let red_row_layout = rgba_color_children
763 .next()
764 .expect("Graphics: Layout should have a red row layout");
765 block2_mouse_interaction = block2_mouse_interaction.max(f(red_row_layout, cursor));
766 let green_row_layout = rgba_color_children
767 .next()
768 .expect("Graphics: Layout should have a green row layout");
769 block2_mouse_interaction = block2_mouse_interaction.max(f(green_row_layout, cursor));
770 let blue_row_layout = rgba_color_children
771 .next()
772 .expect("Graphics: Layout should have a blue row layout");
773 block2_mouse_interaction = block2_mouse_interaction.max(f(blue_row_layout, cursor));
774 let alpha_row_layout = rgba_color_children
775 .next()
776 .expect("Graphics: Layout should have an alpha row layout");
777 block2_mouse_interaction = block2_mouse_interaction.max(f(alpha_row_layout, cursor));
778
779 let _hex_text_layout = block2_children.next();
780
781 let cancel_button_layout = block2_children
783 .next()
784 .expect("Graphics: Layout should have a cancel button layout for a ColorPicker");
785 let cancel_mouse_interaction = self.cancel_button.mouse_interaction(
786 &self.tree.children[1],
787 cancel_button_layout,
788 cursor,
789 &self.viewport,
790 renderer,
791 );
792
793 let submit_button_layout = block2_children
794 .next()
795 .expect("Graphics: Layout should have a submit button layout for a ColorPicker");
796 let submit_mouse_interaction = self.submit_button.mouse_interaction(
797 &self.tree.children[1],
798 submit_button_layout,
799 cursor,
800 &self.viewport,
801 renderer,
802 );
803
804 mouse_interaction
805 .max(block1_mouse_interaction)
806 .max(block2_mouse_interaction)
807 .max(cancel_mouse_interaction)
808 .max(submit_mouse_interaction)
809 }
810
811 fn draw(
812 &self,
813 renderer: &mut Renderer,
814 theme: &Theme,
815 style: &renderer::Style,
816 layout: Layout<'_>,
817 cursor: Cursor,
818 ) {
819 let bounds = layout.bounds();
820 let mut children = layout.children();
821
822 let mut style_sheet: HashMap<StyleState, Style> = HashMap::new();
823 let _ = style_sheet.insert(
824 StyleState::Active,
825 style::color_picker::Catalog::style(theme, self.class, Status::Active),
826 );
827 let _ = style_sheet.insert(
828 StyleState::Selected,
829 style::color_picker::Catalog::style(theme, self.class, Status::Selected),
830 );
831 let _ = style_sheet.insert(
832 StyleState::Hovered,
833 style::color_picker::Catalog::style(theme, self.class, Status::Hovered),
834 );
835 let _ = style_sheet.insert(
836 StyleState::Focused,
837 style::color_picker::Catalog::style(theme, self.class, Status::Focused),
838 );
839
840 let mut style_state = StyleState::Active;
841 if self.state.focus == Focus::Overlay {
842 style_state = style_state.max(StyleState::Focused);
843 }
844 if cursor.is_over(bounds) {
845 style_state = style_state.max(StyleState::Hovered);
846 }
847
848 if (bounds.width > 0.) && (bounds.height > 0.) {
849 renderer.fill_quad(
850 renderer::Quad {
851 bounds,
852 border: Border {
853 radius: style_sheet[&style_state].border_radius.into(),
854 width: style_sheet[&style_state].border_width,
855 color: style_sheet[&style_state].border_color,
856 },
857 ..renderer::Quad::default()
858 },
859 style_sheet[&style_state].background,
860 );
861 }
862
863 let block1_layout = children
865 .next()
866 .expect("Graphics: Layout should have a 1. block layout");
867 block1(renderer, self, block1_layout, cursor, &style_sheet);
868
869 let block2_layout = children
871 .next()
872 .expect("Graphics: Layout should have a 2. block layout");
873 block2(
874 renderer,
875 self,
876 block2_layout,
877 cursor,
878 theme,
879 style,
880 &bounds,
881 &style_sheet,
882 );
883 }
884}
885
886fn block1_layout<'a, Message, Theme>(
888 color_picker: &mut ColorPickerOverlay<'_, '_, Message, Theme>,
889 renderer: &Renderer,
890 bounds: Rectangle,
891) -> Node
892where
893 Message: 'static + Clone,
894 Theme: 'a
895 + style::color_picker::Catalog
896 + iced_widget::button::Catalog
897 + iced_widget::text::Catalog,
898{
899 let block1_limits = Limits::new(Size::ZERO, bounds.size())
900 .width(Length::Fill)
901 .height(Length::Fill);
902
903 let block1_node = Column::<(), Theme, Renderer>::new()
904 .spacing(PADDING.y() / 2.) .push(
906 Row::new()
907 .width(Length::Fill)
908 .height(Length::FillPortion(7)),
909 )
910 .push(
911 Row::new()
912 .width(Length::Fill)
913 .height(Length::FillPortion(1)),
914 )
915 .layout(color_picker.tree, renderer, &block1_limits);
916
917 block1_node.move_to(Point::new(bounds.x + PADDING.left, bounds.y + PADDING.top))
918}
919
920fn block2_layout<'a, Message, Theme>(
922 color_picker: &mut ColorPickerOverlay<'_, '_, Message, Theme>,
923 renderer: &Renderer,
924 bounds: Rectangle,
925) -> Node
926where
927 Message: 'static + Clone,
928 Theme: 'a
929 + style::color_picker::Catalog
930 + iced_widget::button::Catalog
931 + iced_widget::text::Catalog,
932{
933 let block2_limits = Limits::new(Size::ZERO, bounds.size())
934 .width(Length::Fill)
935 .height(Length::Fill);
936
937 let cancel_limits = block2_limits;
939 let cancel_button = color_picker.cancel_button.layout(
940 &mut color_picker.tree.children[0],
941 renderer,
942 &cancel_limits,
943 );
944
945 let hex_text_limits = block2_limits;
946
947 let mut hex_text_layout = Row::<Message, Theme, Renderer>::new()
948 .width(Length::Fill)
949 .height(Length::Fixed(renderer.default_size().0 + PADDING.y()))
950 .layout(color_picker.tree, renderer, &hex_text_limits);
951
952 let block2_limits = block2_limits.shrink(Size::new(
953 0.0,
954 cancel_button.bounds().height + hex_text_layout.bounds().height + 2.0 * SPACING.0,
955 ));
956
957 let mut rgba_colors: Column<'_, Message, Theme, Renderer> =
959 Column::<Message, Theme, Renderer>::new();
960
961 for _ in 0..4 {
962 rgba_colors = rgba_colors.push(
963 Row::new()
964 .align_y(Alignment::Center)
965 .spacing(SPACING)
966 .padding(PADDING)
967 .height(Length::Fill)
968 .push(
969 widget::Text::new("X:")
970 .align_x(Horizontal::Center)
971 .align_y(Vertical::Center),
972 )
973 .push(
974 Row::new()
975 .width(Length::FillPortion(5))
976 .height(Length::Fill),
977 )
978 .push(
979 widget::Text::new("XXX")
980 .align_x(Horizontal::Center)
981 .align_y(Vertical::Center),
982 ),
983 );
984 }
985 let mut element: Element<Message, Theme, Renderer> = Element::new(rgba_colors);
986 let rgba_tree = if let Some(child_tree) = color_picker.tree.children.get_mut(2) {
987 child_tree.diff(element.as_widget_mut());
988 child_tree
989 } else {
990 let child_tree = Tree::new(element.as_widget());
991 color_picker.tree.children.insert(2, child_tree);
992 &mut color_picker.tree.children[2]
993 };
994
995 let mut rgba_colors = element
996 .as_widget_mut()
997 .layout(rgba_tree, renderer, &block2_limits);
998
999 let rgba_bounds = rgba_colors.bounds();
1000 rgba_colors = rgba_colors.move_to(Point::new(
1001 rgba_bounds.x + PADDING.left,
1002 rgba_bounds.y + PADDING.top,
1003 ));
1004 let rgba_bounds = rgba_colors.bounds();
1005
1006 let hex_bounds = hex_text_layout.bounds();
1008 hex_text_layout = hex_text_layout.move_to(Point::new(
1009 hex_bounds.x + PADDING.left,
1010 hex_bounds.y + rgba_bounds.height + PADDING.top + SPACING.0,
1011 ));
1012 let hex_bounds = hex_text_layout.bounds();
1013
1014 let cancel_limits =
1016 block2_limits.max_width(((rgba_bounds.width / 2.0) - BUTTON_SPACING.0).max(0.0));
1017
1018 let mut cancel_button = color_picker.cancel_button.layout(
1019 &mut color_picker.tree.children[0],
1020 renderer,
1021 &cancel_limits,
1022 );
1023
1024 let submit_limits =
1025 block2_limits.max_width(((rgba_bounds.width / 2.0) - BUTTON_SPACING.0).max(0.0));
1026
1027 let mut submit_button = color_picker.submit_button.layout(
1028 &mut color_picker.tree.children[1],
1029 renderer,
1030 &submit_limits,
1031 );
1032
1033 let cancel_bounds = cancel_button.bounds();
1034 cancel_button = cancel_button.move_to(Point::new(
1035 cancel_bounds.x + PADDING.left,
1036 cancel_bounds.y + rgba_bounds.height + hex_bounds.height + PADDING.top + 2.0 * SPACING.0,
1037 ));
1038 let cancel_bounds = cancel_button.bounds();
1039
1040 let submit_bounds = submit_button.bounds();
1041 submit_button = submit_button.move_to(Point::new(
1042 submit_bounds.x + rgba_colors.bounds().width - submit_bounds.width + PADDING.left,
1043 submit_bounds.y + rgba_bounds.height + hex_bounds.height + PADDING.top + 2.0 * SPACING.0,
1044 ));
1045
1046 Node::with_children(
1047 Size::new(
1048 rgba_bounds.width + PADDING.x(),
1049 rgba_bounds.height
1050 + hex_bounds.height
1051 + cancel_bounds.height
1052 + PADDING.y()
1053 + (2.0 * SPACING.0),
1054 ),
1055 vec![rgba_colors, hex_text_layout, cancel_button, submit_button],
1056 )
1057 .move_to(Point::new(bounds.x, bounds.y))
1058}
1059
1060fn block1<Message, Theme>(
1062 renderer: &mut Renderer,
1063 color_picker: &ColorPickerOverlay<'_, '_, Message, Theme>,
1064 layout: Layout<'_>,
1065 cursor: Cursor,
1066 style_sheet: &HashMap<StyleState, Style>,
1067) where
1068 Message: Clone,
1069 Theme: style::color_picker::Catalog + iced_widget::button::Catalog + iced_widget::text::Catalog,
1070{
1071 let hsv_color_layout = layout;
1073
1074 hsv_color(
1076 renderer,
1077 color_picker,
1078 hsv_color_layout,
1079 cursor,
1080 style_sheet,
1081 );
1082
1083 }
1085
1086#[allow(clippy::too_many_arguments)]
1088fn block2<Message, Theme>(
1089 renderer: &mut Renderer,
1090 color_picker: &ColorPickerOverlay<'_, '_, Message, Theme>,
1091 layout: Layout<'_>,
1092 cursor: Cursor,
1093 theme: &Theme,
1094 style: &renderer::Style,
1095 viewport: &Rectangle,
1096 style_sheet: &HashMap<StyleState, Style>,
1097) where
1098 Message: Clone,
1099 Theme: style::color_picker::Catalog + iced_widget::button::Catalog + iced_widget::text::Catalog,
1100{
1101 let mut block2_children = layout.children();
1103
1104 let rgba_color_layout = block2_children
1106 .next()
1107 .expect("Graphics: Layout should have a RGBA color layout");
1108 rgba_color(
1109 renderer,
1110 rgba_color_layout,
1111 &color_picker.state.color,
1112 cursor,
1113 style,
1114 style_sheet,
1115 color_picker.state.focus,
1116 );
1117
1118 let hex_text_layout = block2_children
1120 .next()
1121 .expect("Graphics: Layout should have a hex text layout");
1122 hex_text(
1123 renderer,
1124 hex_text_layout,
1125 &color_picker.state.color,
1126 cursor,
1127 style,
1128 style_sheet,
1129 color_picker.state.focus,
1130 );
1131
1132 let cancel_button_layout = block2_children
1134 .next()
1135 .expect("Graphics: Layout should have a cancel button layout for a ColorPicker");
1136
1137 color_picker.cancel_button.draw(
1138 &color_picker.tree.children[0],
1139 renderer,
1140 theme,
1141 style,
1142 cancel_button_layout,
1143 cursor,
1144 viewport,
1145 );
1146
1147 let submit_button_layout = block2_children
1148 .next()
1149 .expect("Graphics: Layout should have a submit button layout for a ColorPicker");
1150
1151 color_picker.submit_button.draw(
1152 &color_picker.tree.children[1],
1153 renderer,
1154 theme,
1155 style,
1156 submit_button_layout,
1157 cursor,
1158 viewport,
1159 );
1160
1161 if color_picker.state.focus == Focus::Cancel {
1163 let bounds = cancel_button_layout.bounds();
1164 if (bounds.width > 0.) && (bounds.height > 0.) {
1165 renderer.fill_quad(
1166 renderer::Quad {
1167 bounds,
1168 border: Border {
1169 radius: style_sheet[&StyleState::Focused].border_radius.into(),
1170 width: style_sheet[&StyleState::Focused].border_width,
1171 color: style_sheet[&StyleState::Focused].border_color,
1172 },
1173 ..renderer::Quad::default()
1174 },
1175 Color::TRANSPARENT,
1176 );
1177 }
1178 }
1179
1180 if color_picker.state.focus == Focus::Submit {
1181 let bounds = submit_button_layout.bounds();
1182 if (bounds.width > 0.) && (bounds.height > 0.) {
1183 renderer.fill_quad(
1184 renderer::Quad {
1185 bounds,
1186 border: Border {
1187 radius: style_sheet[&StyleState::Focused].border_radius.into(),
1188 width: style_sheet[&StyleState::Focused].border_width,
1189 color: style_sheet[&StyleState::Focused].border_color,
1190 },
1191 ..renderer::Quad::default()
1192 },
1193 Color::TRANSPARENT,
1194 );
1195 }
1196 }
1197 }
1199
1200#[allow(clippy::too_many_lines)]
1202fn hsv_color<Message, Theme>(
1203 renderer: &mut Renderer,
1204 color_picker: &ColorPickerOverlay<'_, '_, Message, Theme>,
1205 layout: Layout<'_>,
1206 cursor: Cursor,
1207 style_sheet: &HashMap<StyleState, Style>,
1208) where
1209 Message: Clone,
1210 Theme: style::color_picker::Catalog + iced_widget::button::Catalog + iced_widget::text::Catalog,
1211{
1212 let mut hsv_color_children = layout.children();
1213 let hsv_color: Hsv = color_picker.state.color.into();
1214
1215 let sat_value_layout = hsv_color_children
1216 .next()
1217 .expect("Graphics: Layout should have a sat/value layout");
1218 let mut sat_value_style_state = StyleState::Active;
1219 if color_picker.state.focus == Focus::SatValue {
1220 sat_value_style_state = sat_value_style_state.max(StyleState::Focused);
1221 }
1222 if cursor.is_over(sat_value_layout.bounds()) {
1223 sat_value_style_state = sat_value_style_state.max(StyleState::Hovered);
1224 }
1225
1226 let geometry = color_picker.state.sat_value_canvas_cache.draw(
1227 renderer,
1228 sat_value_layout.bounds().size(),
1229 |frame| {
1230 let column_count = frame.width() as u16;
1231 let row_count = frame.height() as u16;
1232
1233 for column in 0..column_count {
1234 for row in 0..row_count {
1235 let saturation = f32::from(column) / frame.width();
1236 let value = f32::from(row) / frame.height();
1237
1238 frame.fill_rectangle(
1239 Point::new(f32::from(column), f32::from(row)),
1240 Size::new(1.0, 1.0),
1241 Color::from(Hsv::from_hsv(hsv_color.hue, saturation, value)),
1242 );
1243 }
1244 }
1245
1246 let stroke = Stroke {
1247 style: canvas::Style::Solid(
1248 Hsv {
1249 hue: 0,
1250 saturation: 0.0,
1251 value: 1.0 - hsv_color.value,
1252 }
1253 .into(),
1254 ),
1255 width: 3.0,
1256 line_cap: LineCap::Round,
1257 ..Stroke::default()
1258 };
1259
1260 let saturation = hsv_color.saturation * frame.width();
1261 let value = hsv_color.value * frame.height();
1262
1263 frame.stroke(
1264 &Path::line(
1265 Point::new(saturation, 0.0),
1266 Point::new(saturation, frame.height()),
1267 ),
1268 stroke,
1269 );
1270
1271 frame.stroke(
1272 &Path::line(Point::new(0.0, value), Point::new(frame.width(), value)),
1273 stroke,
1274 );
1275
1276 let stroke = Stroke {
1277 style: canvas::Style::Solid(
1278 style_sheet
1279 .get(&sat_value_style_state)
1280 .expect("Style Sheet not found.")
1281 .bar_border_color,
1282 ),
1283 width: 2.0,
1284 line_cap: LineCap::Round,
1285 ..Stroke::default()
1286 };
1287
1288 frame.stroke(
1289 &Path::rectangle(
1290 Point::new(0.0, 0.0),
1291 Size::new(frame.size().width - 0.0, frame.size().height - 0.0),
1292 ),
1293 stroke,
1294 );
1295 },
1296 );
1297
1298 let translation = Vector::new(sat_value_layout.bounds().x, sat_value_layout.bounds().y);
1299 renderer.with_translation(translation, |renderer| {
1300 renderer.draw_geometry(geometry);
1301 });
1302
1303 let hue_layout = hsv_color_children
1304 .next()
1305 .expect("Graphics: Layout should have a hue layout");
1306 let mut hue_style_state = StyleState::Active;
1307 if color_picker.state.focus == Focus::Hue {
1308 hue_style_state = hue_style_state.max(StyleState::Focused);
1309 }
1310 if cursor.is_over(hue_layout.bounds()) {
1311 hue_style_state = hue_style_state.max(StyleState::Hovered);
1312 }
1313
1314 let geometry =
1315 color_picker
1316 .state
1317 .hue_canvas_cache
1318 .draw(renderer, hue_layout.bounds().size(), |frame| {
1319 let column_count = frame.width() as u16;
1320
1321 for column in 0..column_count {
1322 let hue = (f32::from(column) * 360.0 / frame.width()) as u16;
1323
1324 let hsv_color = Hsv::from_hsv(hue, 1.0, 1.0);
1325 let stroke = Stroke {
1326 style: canvas::Style::Solid(hsv_color.into()),
1327 width: 1.0,
1328 line_cap: LineCap::Round,
1329 ..Stroke::default()
1330 };
1331
1332 frame.stroke(
1333 &Path::line(
1334 Point::new(f32::from(column), 0.0),
1335 Point::new(f32::from(column), frame.height()),
1336 ),
1337 stroke,
1338 );
1339 }
1340
1341 let stroke = Stroke {
1342 style: canvas::Style::Solid(Color::BLACK),
1343 width: 3.0,
1344 line_cap: LineCap::Round,
1345 ..Stroke::default()
1346 };
1347
1348 let column = f32::from(hsv_color.hue) * frame.width() / 360.0;
1349
1350 frame.stroke(
1351 &Path::line(Point::new(column, 0.0), Point::new(column, frame.height())),
1352 stroke,
1353 );
1354
1355 let stroke = Stroke {
1356 style: canvas::Style::Solid(
1357 style_sheet
1358 .get(&hue_style_state)
1359 .expect("Style Sheet not found.")
1360 .bar_border_color,
1361 ),
1362 width: 2.0,
1363 line_cap: LineCap::Round,
1364 ..Stroke::default()
1365 };
1366
1367 frame.stroke(
1368 &Path::rectangle(
1369 Point::new(0.0, 0.0),
1370 Size::new(frame.size().width, frame.size().height),
1371 ),
1372 stroke,
1373 );
1374 });
1375
1376 let translation = Vector::new(hue_layout.bounds().x, hue_layout.bounds().y);
1377 renderer.with_translation(translation, |renderer| {
1378 renderer.draw_geometry(geometry);
1379 });
1380}
1381
1382#[allow(clippy::too_many_lines)]
1384fn rgba_color(
1385 renderer: &mut Renderer,
1386 layout: Layout<'_>,
1387 color: &Color,
1388 cursor: Cursor,
1389 style: &renderer::Style,
1390 style_sheet: &HashMap<StyleState, Style>,
1391 focus: Focus,
1392) {
1393 let mut rgba_color_children = layout.children();
1394
1395 let f = |renderer: &mut Renderer,
1396 layout: Layout,
1397 label: &str,
1398 color: Color,
1399 value: f32,
1400 cursor: Cursor,
1401 target: Focus| {
1402 let mut children = layout.children();
1403
1404 let label_layout = children
1405 .next()
1406 .expect("Graphics: Layout should have a label layout");
1407 let bar_layout = children
1408 .next()
1409 .expect("Graphics: Layout should have a bar layout");
1410 let value_layout = children
1411 .next()
1412 .expect("Graphics: Layout should have a value layout");
1413
1414 renderer.fill_text(
1416 Text {
1417 content: label.to_owned(),
1418 bounds: Size::new(label_layout.bounds().width, label_layout.bounds().height),
1419 size: renderer.default_size(),
1420 font: renderer.default_font(),
1421 align_x: text::Alignment::Center,
1422 align_y: Vertical::Center,
1423 line_height: text::LineHeight::Relative(1.3),
1424 shaping: text::Shaping::Basic,
1425 wrapping: Wrapping::None,
1426 },
1427 Point::new(
1428 label_layout.bounds().center_x(),
1429 label_layout.bounds().center_y(),
1430 ),
1431 style.text_color,
1432 label_layout.bounds(),
1433 );
1434
1435 let bar_bounds = bar_layout.bounds();
1436
1437 let bar_style_state = if cursor.is_over(bar_bounds) {
1438 StyleState::Hovered
1439 } else {
1440 StyleState::Active
1441 };
1442
1443 let background_bounds = Rectangle {
1445 x: bar_bounds.x,
1446 y: bar_bounds.y,
1447 width: bar_bounds.width * value,
1448 height: bar_bounds.height,
1449 };
1450 if (background_bounds.width > 0.) && (background_bounds.height > 0.) {
1451 renderer.fill_quad(
1452 renderer::Quad {
1453 bounds: background_bounds,
1454 border: Border {
1455 radius: style_sheet
1456 .get(&bar_style_state)
1457 .expect("Style Sheet not found.")
1458 .bar_border_radius
1459 .into(),
1460 width: style_sheet
1461 .get(&bar_style_state)
1462 .expect("Style Sheet not found.")
1463 .bar_border_width,
1464 color: Color::TRANSPARENT,
1465 },
1466 ..renderer::Quad::default()
1467 },
1468 color,
1469 );
1470 }
1471
1472 if (bar_bounds.width > 0.) && (bar_bounds.height > 0.) {
1474 renderer.fill_quad(
1475 renderer::Quad {
1476 bounds: bar_bounds,
1477 border: Border {
1478 radius: style_sheet
1479 .get(&bar_style_state)
1480 .expect("Style Sheet not found.")
1481 .bar_border_radius
1482 .into(),
1483 width: style_sheet
1484 .get(&bar_style_state)
1485 .expect("Style Sheet not found.")
1486 .bar_border_width,
1487 color: style_sheet
1488 .get(&bar_style_state)
1489 .expect("Style Sheet not found.")
1490 .bar_border_color,
1491 },
1492 ..renderer::Quad::default()
1493 },
1494 Color::TRANSPARENT,
1495 );
1496 }
1497
1498 renderer.fill_text(
1500 Text {
1501 content: format!("{}", (255.0 * value) as u8),
1502 bounds: Size::new(value_layout.bounds().width, value_layout.bounds().height),
1503 size: renderer.default_size(),
1504 font: renderer.default_font(),
1505 align_x: text::Alignment::Center,
1506 align_y: Vertical::Center,
1507 line_height: iced_widget::text::LineHeight::Relative(1.3),
1508 shaping: iced_widget::text::Shaping::Basic,
1509 wrapping: Wrapping::None,
1510 },
1511 Point::new(
1512 value_layout.bounds().center_x(),
1513 value_layout.bounds().center_y(),
1514 ),
1515 style.text_color,
1516 value_layout.bounds(),
1517 );
1518
1519 let bounds = layout.bounds();
1520 if (focus == target) && (bounds.width > 0.) && (bounds.height > 0.) {
1521 renderer.fill_quad(
1522 renderer::Quad {
1523 bounds,
1524 border: Border {
1525 radius: style_sheet
1526 .get(&StyleState::Focused)
1527 .expect("Style Sheet not found.")
1528 .border_radius
1529 .into(),
1530 width: style_sheet
1531 .get(&StyleState::Focused)
1532 .expect("Style Sheet not found.")
1533 .border_width,
1534 color: style_sheet
1535 .get(&StyleState::Focused)
1536 .expect("Style Sheet not found.")
1537 .border_color,
1538 },
1539 ..renderer::Quad::default()
1540 },
1541 Color::TRANSPARENT,
1542 );
1543 }
1544 };
1545
1546 let red_row_layout = rgba_color_children
1548 .next()
1549 .expect("Graphics: Layout should have a red row layout");
1550
1551 f(
1552 renderer,
1553 red_row_layout,
1554 "R",
1555 Color::from_rgb(color.r, 0.0, 0.0),
1556 color.r,
1557 cursor,
1558 Focus::Red,
1559 );
1560
1561 let green_row_layout = rgba_color_children
1563 .next()
1564 .expect("Graphics: Layout should have a green row layout");
1565
1566 f(
1567 renderer,
1568 green_row_layout,
1569 "G",
1570 Color::from_rgb(0.0, color.g, 0.0),
1571 color.g,
1572 cursor,
1573 Focus::Green,
1574 );
1575
1576 let blue_row_layout = rgba_color_children
1578 .next()
1579 .expect("Graphics: Layout should have a blue row layout");
1580
1581 f(
1582 renderer,
1583 blue_row_layout,
1584 "B",
1585 Color::from_rgb(0.0, 0.0, color.b),
1586 color.b,
1587 cursor,
1588 Focus::Blue,
1589 );
1590
1591 let alpha_row_layout = rgba_color_children
1593 .next()
1594 .expect("Graphics: Layout should have an alpha row layout");
1595
1596 f(
1597 renderer,
1598 alpha_row_layout,
1599 "A",
1600 Color::from_rgba(0.0, 0.0, 0.0, color.a),
1601 color.a,
1602 cursor,
1603 Focus::Alpha,
1604 );
1605}
1606
1607fn hex_text(
1609 renderer: &mut Renderer,
1610 layout: Layout<'_>,
1611 color: &Color,
1612 cursor: Cursor,
1613 _style: &renderer::Style,
1614 style_sheet: &HashMap<StyleState, Style>,
1615 _focus: Focus,
1616) {
1617 let hsv: Hsv = (*color).into();
1618
1619 let hex_text_style_state = if cursor.is_over(layout.bounds()) {
1620 StyleState::Hovered
1621 } else {
1622 StyleState::Active
1623 };
1624
1625 let bounds = layout.bounds();
1626 if (bounds.width > 0.) && (bounds.height > 0.) {
1627 renderer.fill_quad(
1628 renderer::Quad {
1629 bounds,
1630 border: Border {
1631 radius: style_sheet[&hex_text_style_state].bar_border_radius.into(),
1632 width: style_sheet[&hex_text_style_state].bar_border_width,
1633 color: style_sheet[&hex_text_style_state].bar_border_color,
1634 },
1635 ..renderer::Quad::default()
1636 },
1637 *color,
1638 );
1639 }
1640
1641 renderer.fill_text(
1642 Text {
1643 content: color.as_hex_string(),
1644 bounds: Size::new(bounds.width, bounds.height),
1645 size: renderer.default_size(),
1646 font: renderer.default_font(),
1647 align_x: text::Alignment::Center,
1648 align_y: Vertical::Center,
1649 line_height: text::LineHeight::Relative(1.3),
1650 shaping: text::Shaping::Basic,
1651 wrapping: Wrapping::default(),
1652 },
1653 Point::new(bounds.center_x(), bounds.center_y()),
1654 Color {
1655 a: 1.0,
1656 ..Hsv {
1657 hue: 0,
1658 saturation: 0.0,
1659 value: if hsv.value < 0.5 { 1.0 } else { 0.0 },
1660 }
1661 .into()
1662 },
1663 bounds,
1664 );
1665}
1666
1667#[derive(Debug)]
1669pub struct State {
1670 pub(crate) color: Color,
1672 pub(crate) initial_color: Color,
1674 pub(crate) sat_value_canvas_cache: canvas::Cache,
1676 pub(crate) hue_canvas_cache: canvas::Cache,
1678 pub(crate) color_bar_dragged: ColorBarDragged,
1680 pub(crate) focus: Focus,
1682 pub(crate) keyboard_modifiers: keyboard::Modifiers,
1684}
1685
1686impl State {
1687 #[must_use]
1689 pub fn new(color: Color) -> Self {
1690 Self {
1691 color,
1692 initial_color: color,
1693 ..Self::default()
1694 }
1695 }
1696
1697 fn clear_cache(&self) {
1702 self.sat_value_canvas_cache.clear();
1703 self.hue_canvas_cache.clear();
1704 }
1705
1706 pub(crate) fn force_synchronize(&mut self, color: Color) {
1708 self.initial_color = color;
1709 self.color = color;
1710 self.clear_cache();
1711 }
1712}
1713
1714impl Default for State {
1715 fn default() -> Self {
1716 let default_color = Color::from_rgb(0.5, 0.25, 0.25);
1717 Self {
1718 color: default_color,
1719 initial_color: default_color,
1720 sat_value_canvas_cache: canvas::Cache::default(),
1721 hue_canvas_cache: canvas::Cache::default(),
1722 color_bar_dragged: ColorBarDragged::None,
1723 focus: Focus::default(),
1724 keyboard_modifiers: keyboard::Modifiers::default(),
1725 }
1726 }
1727}
1728
1729#[allow(missing_debug_implementations)]
1731pub struct ColorPickerOverlayButtons<'a, Message, Theme>
1732where
1733 Message: Clone,
1734 Theme: style::color_picker::Catalog + iced_widget::button::Catalog,
1735{
1736 cancel_button: Element<'a, Message, Theme, Renderer>,
1738 submit_button: Element<'a, Message, Theme, Renderer>,
1740}
1741
1742impl<'a, Message, Theme> Default for ColorPickerOverlayButtons<'a, Message, Theme>
1743where
1744 Message: 'a + Clone,
1745 Theme: 'a
1746 + style::color_picker::Catalog
1747 + iced_widget::button::Catalog
1748 + iced_widget::text::Catalog,
1749{
1750 fn default() -> Self {
1751 let (cancel_content, cancel_font, _cancel_shaping) = cancel();
1752 let (submit_content, submit_font, _submit_shaping) = ok();
1753
1754 Self {
1755 cancel_button: Button::new(widget::Text::new(cancel_content).font(cancel_font)).into(),
1756 submit_button: Button::new(widget::Text::new(submit_content).font(submit_font)).into(),
1757 }
1758 }
1759}
1760
1761#[allow(clippy::unimplemented)]
1762impl<Message, Theme> Widget<Message, Theme, Renderer>
1763 for ColorPickerOverlayButtons<'_, Message, Theme>
1764where
1765 Message: Clone,
1766 Theme: style::color_picker::Catalog + iced_widget::button::Catalog + iced_widget::text::Catalog,
1767{
1768 fn children(&self) -> Vec<Tree> {
1769 vec![
1770 Tree::new(&self.cancel_button),
1771 Tree::new(&self.submit_button),
1772 ]
1773 }
1774
1775 fn diff(&self, tree: &mut Tree) {
1776 tree.diff_children(&[&self.cancel_button, &self.submit_button]);
1777 }
1778
1779 fn size(&self) -> Size<Length> {
1780 unimplemented!("This should never be reached!")
1781 }
1782
1783 fn layout(&mut self, _tree: &mut Tree, _renderer: &Renderer, _limits: &Limits) -> Node {
1784 unimplemented!("This should never be reached!")
1785 }
1786
1787 fn draw(
1788 &self,
1789 _state: &Tree,
1790 _renderer: &mut Renderer,
1791 _theme: &Theme,
1792 _style: &renderer::Style,
1793 _layout: Layout<'_>,
1794 _cursor: Cursor,
1795 _viewport: &Rectangle,
1796 ) {
1797 unimplemented!("This should never be reached!")
1798 }
1799}
1800
1801impl<'a, Message, Theme> From<ColorPickerOverlayButtons<'a, Message, Theme>>
1802 for Element<'a, Message, Theme, Renderer>
1803where
1804 Message: 'a + Clone,
1805 Theme: 'a
1806 + style::color_picker::Catalog
1807 + iced_widget::button::Catalog
1808 + iced_widget::text::Catalog,
1809{
1810 fn from(overlay: ColorPickerOverlayButtons<'a, Message, Theme>) -> Self {
1811 Self::new(overlay)
1812 }
1813}
1814
1815#[derive(Copy, Clone, Debug, Default)]
1817pub enum ColorBarDragged {
1818 #[default]
1820 None,
1821
1822 SatValue,
1824
1825 Hue,
1827
1828 Red,
1830
1831 Green,
1833
1834 Blue,
1836
1837 Alpha,
1839}
1840
1841#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
1843pub enum Focus {
1844 #[default]
1846 None,
1847
1848 Overlay,
1850
1851 SatValue,
1853
1854 Hue,
1856
1857 Red,
1859
1860 Green,
1862
1863 Blue,
1865
1866 Alpha,
1868
1869 Cancel,
1871
1872 Submit,
1874}
1875
1876impl Focus {
1877 #[must_use]
1879 pub const fn next(self) -> Self {
1880 match self {
1881 Self::Overlay => Self::SatValue,
1882 Self::SatValue => Self::Hue,
1883 Self::Hue => Self::Red,
1884 Self::Red => Self::Green,
1885 Self::Green => Self::Blue,
1886 Self::Blue => Self::Alpha,
1887 Self::Alpha => Self::Cancel,
1888 Self::Cancel => Self::Submit,
1889 Self::Submit | Self::None => Self::Overlay,
1890 }
1891 }
1892
1893 #[must_use]
1895 pub const fn previous(self) -> Self {
1896 match self {
1897 Self::None => Self::None,
1898 Self::Overlay => Self::Submit,
1899 Self::SatValue => Self::Overlay,
1900 Self::Hue => Self::SatValue,
1901 Self::Red => Self::Hue,
1902 Self::Green => Self::Red,
1903 Self::Blue => Self::Green,
1904 Self::Alpha => Self::Blue,
1905 Self::Cancel => Self::Alpha,
1906 Self::Submit => Self::Cancel,
1907 }
1908 }
1909}