iced_aw/widget/overlay/
color_picker.rs

1//! Use a color picker as an input element for picking colors.
2//!
3//! *This API requires the following crate features to be activated: `color_picker`*
4
5use 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
35/// The padding around the elements.
36const PADDING: Padding = Padding::new(10.0);
37/// The spacing between the element.
38const SPACING: Pixels = Pixels(15.0);
39/// The spacing between the buttons.
40const BUTTON_SPACING: Pixels = Pixels(5.0);
41
42/// The step value of the keyboard change of the sat/value color values.
43const SAT_VALUE_STEP: f32 = 0.005;
44/// The step value of the keyboard change of the hue color value.
45const HUE_STEP: i32 = 1;
46/// The step value of the keyboard change of the RGBA color values.
47const RGBA_STEP: i16 = 1;
48
49/// The overlay of the [`ColorPicker`](crate::widget::ColorPicker).
50#[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    /// The state of the [`ColorPickerOverlay`].
58    state: &'a mut State,
59    /// The cancel button of the [`ColorPickerOverlay`].
60    cancel_button: Button<'a, Message, Theme, Renderer>,
61    /// The submit button of the [`ColorPickerOverlay`].
62    submit_button: Button<'a, Message, Theme, Renderer>,
63    /// The function that produces a message when the submit button of the [`ColorPickerOverlay`].
64    on_submit: &'a dyn Fn(Color) -> Message,
65    /// The position of the [`ColorPickerOverlay`].
66    position: Point,
67    /// The style of the [`ColorPickerOverlay`].
68    class: &'a <Theme as style::color_picker::Catalog>::Class<'b>,
69    /// The reference to the tree holding the state of this overlay.
70    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    /// Creates a new [`ColorPickerOverlay`] on the given position.
84    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), // Sending a fake message
116            on_submit,
117            position,
118            class,
119            tree,
120            viewport,
121        }
122    }
123
124    /// Turn this [`ColorPickerOverlay`] into an overlay [`Element`](overlay::Element).
125    #[must_use]
126    pub fn overlay(self) -> overlay::Element<'a, Message, Theme, Renderer> {
127        overlay::Element::new(Box::new(self))
128    }
129
130    /// Force redraw all components if the internal state was changed
131    fn clear_cache(&self) {
132        self.state.clear_cache();
133    }
134
135    /// The event handling for the HSV color area.
136    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    /// The event handling for the RGBA color area.
248    #[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 * 255.0 + y).clamp(0.0, 255.0) / 255.0;
303                        |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    /// The even handling for the keyboard input.
417    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                // TODO: maybe place this better
432                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        // ----------- Block 1 ----------------------
594        let block1_node = block1_layout(self, renderer, block1_bounds);
595
596        // ----------- Block 2 ----------------------
597        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, // + (2.0 * PADDING as f32),
602                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        // ----------- Block 1 ----------------------
636        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        // ----------- Block 1 end ------------------
641
642        // ----------- Block 2 ----------------------
643        let mut block2_children = children
644            .next()
645            .expect("widget: Layout should have a 2. block layout")
646            .children();
647
648        // ----------- RGB Color -----------------------
649        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        // ----------- Text input ----------------------
657        let _text_input_layout = block2_children
658            .next()
659            .expect("widget: Layout should have a hex text layout");
660
661        // ----------- Buttons -------------------------
662        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        // ----------- Block 2 end ------------------
696
697        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        // Block 1
717        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        // HSV color
722        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        // Block 2
737        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        // RGBA color
743        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        // Buttons
782        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        // ----------- Block 1 ----------------------
864        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        // ----------- Block 2 ----------------------
870        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
886/// Defines the layout of the 1. block of the color picker containing the HSV part.
887fn 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.) // Average vertical padding
905        .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
920/// Defines the layout of the 2. block of the color picker containing the RGBA part, Hex and buttons.
921fn 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    // Pre-Buttons TODO: get rid of it
938    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    // RGBA Colors
958    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    // Hex text
1007    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    // Buttons
1015    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
1060/// Draws the 1. block of the color picker containing the HSV part.
1061fn 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    // ----------- Block 1 ----------------------
1072    let hsv_color_layout = layout;
1073
1074    // ----------- HSV Color ----------------------
1075    hsv_color(
1076        renderer,
1077        color_picker,
1078        hsv_color_layout,
1079        cursor,
1080        style_sheet,
1081    );
1082
1083    // ----------- Block 1 end ------------------
1084}
1085
1086/// Draws the 2. block of the color picker containing the RGBA part, Hex and buttons.
1087#[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    // ----------- Block 2 ----------------------
1102    let mut block2_children = layout.children();
1103
1104    // ----------- RGBA Color ----------------------
1105    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    // ----------- Hex text ----------------------
1119    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    // ----------- Buttons -------------------------
1133    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    // Buttons are not focusable right now...
1162    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    // ----------- Block 2 end ------------------
1198}
1199
1200/// Draws the HSV color area.
1201#[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/// Draws the RGBA color area.
1383#[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        // Label
1415        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        // Bar background
1444        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        // Bar
1473        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        // Value
1499        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    // Red
1547    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    // Green
1562    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    // Blue
1577    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    // Alpha
1592    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
1607/// Draws the hex text representation of the color.
1608fn 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/// The state of the [`ColorPickerOverlay`].
1668#[derive(Debug)]
1669pub struct State {
1670    /// The selected color of the [`ColorPickerOverlay`].
1671    pub(crate) color: Color,
1672    /// The color used to initialize [`ColorPickerOverlay`].
1673    pub(crate) initial_color: Color,
1674    /// The cache of the sat/value canvas of the [`ColorPickerOverlay`].
1675    pub(crate) sat_value_canvas_cache: canvas::Cache,
1676    /// The cache of the hue canvas of the [`ColorPickerOverlay`].
1677    pub(crate) hue_canvas_cache: canvas::Cache,
1678    /// The dragged color bar of the [`ColorPickerOverlay`].
1679    pub(crate) color_bar_dragged: ColorBarDragged,
1680    /// the focus of the [`ColorPickerOverlay`].
1681    pub(crate) focus: Focus,
1682    /// The previously pressed keyboard modifiers.
1683    pub(crate) keyboard_modifiers: keyboard::Modifiers,
1684}
1685
1686impl State {
1687    /// Creates a new State with the given color.
1688    #[must_use]
1689    pub fn new(color: Color) -> Self {
1690        Self {
1691            color,
1692            initial_color: color,
1693            ..Self::default()
1694        }
1695    }
1696
1697    /// Reset cached canvas when internal state is modified.
1698    ///
1699    /// If the color has changed, empty all canvas caches
1700    /// as they (unfortunately) do not depend on the picker state.
1701    fn clear_cache(&self) {
1702        self.sat_value_canvas_cache.clear();
1703        self.hue_canvas_cache.clear();
1704    }
1705
1706    /// Synchronize the color with an externally provided value.
1707    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/// Just a workaround to pass the button states from the tree to the overlay
1730#[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    /// The cancel button of the [`ColorPickerOverlay`].
1737    cancel_button: Element<'a, Message, Theme, Renderer>,
1738    /// The submit button of the [`ColorPickerOverlay`].
1739    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/// The state of the currently dragged area.
1816#[derive(Copy, Clone, Debug, Default)]
1817pub enum ColorBarDragged {
1818    /// No area is focussed.
1819    #[default]
1820    None,
1821
1822    /// The saturation/value area is focussed.
1823    SatValue,
1824
1825    /// The hue area is focussed.
1826    Hue,
1827
1828    /// The red area is focussed.
1829    Red,
1830
1831    /// The green area is focussed.
1832    Green,
1833
1834    /// The blue area is focussed.
1835    Blue,
1836
1837    /// The alpha area is focussed.
1838    Alpha,
1839}
1840
1841/// An enumeration of all focusable element of the [`ColorPickerOverlay`].
1842#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
1843pub enum Focus {
1844    /// Nothing is in focus.
1845    #[default]
1846    None,
1847
1848    /// The overlay itself is in focus.
1849    Overlay,
1850
1851    /// The saturation and value area is in focus.
1852    SatValue,
1853
1854    /// The hue bar is in focus.
1855    Hue,
1856
1857    /// The red bar is in focus.
1858    Red,
1859
1860    /// The green bar is in focus.
1861    Green,
1862
1863    /// The blue bar is in focus.
1864    Blue,
1865
1866    /// The alpha bar is in focus.
1867    Alpha,
1868
1869    /// The cancel button is in focus.
1870    Cancel,
1871
1872    /// The submit button is in focus.
1873    Submit,
1874}
1875
1876impl Focus {
1877    /// Gets the next focusable element.
1878    #[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    /// Gets the previous focusable element.
1894    #[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}