rat_text/
color_input.rs

1/// Widget for color input.
2///
3/// Currently, supports
4/// * RGB
5/// * HSV
6/// * hexdigits
7///
8/// __Keybindings__
9///
10/// * Switch between color-mode with Up/Down. (or m/M)
11/// * '+' and Alt-'+' increase the value
12/// * '-' and Alt-'-' decrease the value
13/// * 'r', 'h', 'x' switch mode
14///
15/// __Clipboard__
16///
17/// Recognizes common formats when pasted from the clipboard.
18/// * #000000 and #00000000
19/// * 0x000000 and 0x00000000
20/// * 000000 and 00000000
21///
22use crate::_private::NonExhaustive;
23use crate::clipboard::Clipboard;
24use crate::event::{ReadOnly, TextOutcome};
25use crate::text_input_mask::{MaskedInput, MaskedInputState};
26use crate::undo_buffer::{UndoBuffer, UndoEntry};
27use crate::{TextError, TextFocusGained, TextFocusLost, TextStyle, TextTab, upos_type};
28use palette::{FromColor, Hsv, Srgb};
29use rat_cursor::HasScreenCursor;
30use rat_event::{HandleEvent, MouseOnly, Regular, ct_event, flow};
31use rat_focus::{FocusBuilder, FocusFlag, HasFocus};
32use rat_reloc::RelocatableState;
33use ratatui::buffer::Buffer;
34use ratatui::layout::Rect;
35use ratatui::prelude::BlockExt;
36use ratatui::style::{Color, Style};
37use ratatui::text::Line;
38use ratatui::widgets::{Block, StatefulWidget, Widget};
39use std::cmp::min;
40use std::ops::Range;
41
42/// Color input widget.
43///
44/// A text input for colors.
45///
46#[derive(Debug, Clone)]
47pub struct ColorInput<'a> {
48    style: Style,
49    disable_modes: bool,
50    mode: Option<Mode>,
51    block: Option<Block<'a>>,
52    widget: MaskedInput<'a>,
53}
54
55/// Combined styles.
56#[derive(Debug)]
57pub struct ColorInputStyle {
58    /// Base style.
59    pub text: TextStyle,
60    /// Highlighting the field of the input.
61    pub field_style: Option<Style>,
62    /// Disable mode switching.
63    pub disable_modes: Option<bool>,
64    /// Define default mode.
65    pub mode: Option<Mode>,
66    ///
67    pub non_exhaustive: NonExhaustive,
68}
69
70impl Default for ColorInputStyle {
71    fn default() -> Self {
72        Self {
73            text: Default::default(),
74            field_style: Default::default(),
75            disable_modes: Default::default(),
76            mode: Default::default(),
77            non_exhaustive: NonExhaustive,
78        }
79    }
80}
81
82/// Color mode.
83#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
84pub enum Mode {
85    #[default]
86    RGB,
87    HEX,
88    HSV,
89}
90
91/// State for the color input.
92#[derive(Debug, Clone)]
93pub struct ColorInputState {
94    /// Area of the widget.
95    /// __read only__ renewed with each render.
96    pub area: Rect,
97    /// Area inside the block.
98    pub inner: Rect,
99    /// Area for the mode.
100    /// __read_only__ renewed with each render.
101    pub mode_area: Rect,
102    /// Area for the mode label.
103    /// __read_only__ renewed with each render.
104    pub label_area: Rect,
105
106    /// value as RGB with 0.0..1.0 ranges.
107    pub value: (f32, f32, f32),
108
109    /// Disable keys for mode switching.
110    /// __read only__
111    pub disable_modes: bool,
112    /// __read only__
113    pub mode: Mode,
114    /// __read only__
115    pub widget: MaskedInputState,
116
117    pub non_exhaustive: NonExhaustive,
118}
119
120impl<'a> Default for ColorInput<'a> {
121    fn default() -> Self {
122        let mut z = Self {
123            style: Default::default(),
124            disable_modes: Default::default(),
125            mode: Default::default(),
126            block: Default::default(),
127            widget: MaskedInput::default(),
128        };
129        // z.widget = z.widget.on_tab(TextTab::MoveToNextWidget);
130        z.widget = z.widget.on_focus_lost(TextFocusLost::Position0);
131        z
132    }
133}
134
135impl<'a> ColorInput<'a> {
136    pub fn new() -> Self {
137        Self::default()
138    }
139
140    /// Set the combined style.
141    #[inline]
142    pub fn styles(mut self, mut style: ColorInputStyle) -> Self {
143        self.style = style.text.style;
144        if let Some(border_style) = style.text.border_style {
145            self.block = self.block.map(|v| v.style(border_style));
146        }
147        if let Some(block) = style.text.block.take() {
148            self.block = Some(block);
149        }
150        self.widget = self.widget.styles(style.text);
151        if let Some(disable_modes) = style.disable_modes {
152            self.disable_modes = disable_modes;
153        }
154        if let Some(mode) = style.mode {
155            self.mode = Some(mode);
156        }
157        if let Some(field_style) = style.field_style {
158            self.widget = self.widget.text_style_idx(1, field_style);
159        }
160        self
161    }
162
163    /// Base text style.
164    #[inline]
165    pub fn style(mut self, style: impl Into<Style>) -> Self {
166        let style = style.into();
167        self.style = style;
168        self.widget = self.widget.style(style);
169        self
170    }
171
172    /// Style for the fields of the input.
173    #[inline]
174    pub fn field_style(mut self, style: impl Into<Style>) -> Self {
175        self.widget = self.widget.text_style_idx(1, style.into());
176        self
177    }
178
179    /// Disable switching the mode.
180    #[inline]
181    pub fn disable_modes(mut self) -> Self {
182        self.disable_modes = true;
183        self
184    }
185
186    /// Color mode.
187    #[inline]
188    pub fn mode(mut self, mode: Mode) -> Self {
189        self.mode = Some(mode);
190        self
191    }
192
193    /// Style when focused.
194    #[inline]
195    pub fn focus_style(mut self, style: impl Into<Style>) -> Self {
196        self.widget = self.widget.focus_style(style);
197        self
198    }
199
200    /// Style for selection
201    #[inline]
202    pub fn select_style(mut self, style: impl Into<Style>) -> Self {
203        self.widget = self.widget.select_style(style);
204        self
205    }
206
207    /// Style for the invalid indicator.
208    #[inline]
209    pub fn invalid_style(mut self, style: impl Into<Style>) -> Self {
210        self.widget = self.widget.invalid_style(style);
211        self
212    }
213
214    /// Block
215    #[inline]
216    pub fn block(mut self, block: Block<'a>) -> Self {
217        self.block = Some(block);
218        self
219    }
220
221    /// Focus behaviour
222    #[inline]
223    pub fn on_focus_gained(mut self, of: TextFocusGained) -> Self {
224        self.widget = self.widget.on_focus_gained(of);
225        self
226    }
227
228    /// Focus behaviour
229    #[inline]
230    pub fn on_focus_lost(mut self, of: TextFocusLost) -> Self {
231        self.widget = self.widget.on_focus_lost(of);
232        self
233    }
234
235    /// `Tab` behaviour
236    #[inline]
237    pub fn on_tab(mut self, of: TextTab) -> Self {
238        self.widget = self.widget.on_tab(of);
239        self
240    }
241
242    /// Preferred width
243    pub fn width(&self) -> u16 {
244        16
245    }
246
247    /// Preferred height
248    pub fn height(&self) -> u16 {
249        1
250    }
251}
252
253impl<'a> StatefulWidget for &ColorInput<'a> {
254    type State = ColorInputState;
255
256    fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
257        render(self, area, buf, state);
258    }
259}
260
261impl StatefulWidget for ColorInput<'_> {
262    type State = ColorInputState;
263
264    fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
265        render(&self, area, buf, state);
266    }
267}
268
269fn render(widget: &ColorInput<'_>, area: Rect, buf: &mut Buffer, state: &mut ColorInputState) {
270    state.disable_modes = widget.disable_modes;
271    if let Some(mode) = widget.mode {
272        state.mode = mode;
273    }
274
275    let inner = widget.block.inner_if_some(area);
276
277    let mode_area = Rect::new(inner.x, inner.y, 4, inner.height);
278    let mode_label = Rect::new(mode_area.x, mode_area.y + mode_area.height / 2, 4, 1);
279    let widget_area = Rect::new(
280        inner.x + mode_area.width,
281        inner.y,
282        inner.width.saturating_sub(mode_area.width),
283        inner.height,
284    );
285
286    state.area = area;
287    state.inner = inner;
288    state.mode_area = mode_area;
289    state.label_area = mode_label;
290
291    let bg = state.value();
292    let fg_colors = [Color::Black, Color::White];
293    let style = high_contrast_color(bg, &fg_colors);
294
295    widget.block.render(area, buf);
296
297    buf.set_style(mode_area, style);
298    let mode_str = match state.mode {
299        Mode::RGB => "RGB",
300        Mode::HEX => "  #",
301        Mode::HSV => "HSV",
302    };
303    Line::from(mode_str).render(mode_label, buf);
304
305    (&widget.widget).render(widget_area, buf, &mut state.widget);
306}
307
308impl Default for ColorInputState {
309    fn default() -> Self {
310        let mut z = Self {
311            area: Default::default(),
312            inner: Default::default(),
313            mode_area: Default::default(),
314            label_area: Default::default(),
315            value: Default::default(),
316            disable_modes: Default::default(),
317            mode: Default::default(),
318            widget: Default::default(),
319            non_exhaustive: NonExhaustive,
320        };
321        z.set_mode(Mode::RGB);
322        z
323    }
324}
325
326impl HasFocus for ColorInputState {
327    fn build(&self, builder: &mut FocusBuilder) {
328        builder.widget_with_flags(
329            self.widget.focus(),
330            self.area,
331            self.widget.area_z(),
332            self.widget.navigable(),
333        );
334    }
335
336    fn focus(&self) -> FocusFlag {
337        self.widget.focus()
338    }
339
340    fn area(&self) -> Rect {
341        self.area
342    }
343}
344
345impl ColorInputState {
346    pub fn new() -> Self {
347        Self::default()
348    }
349
350    pub fn named(name: &str) -> Self {
351        let mut z = Self::default();
352        z.widget.focus = z.widget.focus.with_name(name);
353        z
354    }
355
356    /// The next edit operation will overwrite the current content
357    /// instead of adding text. Any move operations will cancel
358    /// this overwrite.
359    #[inline]
360    pub fn set_overwrite(&mut self, overwrite: bool) {
361        self.widget.set_overwrite(overwrite);
362    }
363
364    /// Will the next edit operation overwrite the content?
365    #[inline]
366    pub fn overwrite(&self) -> bool {
367        self.widget.overwrite()
368    }
369}
370
371impl ColorInputState {
372    /// Clipboard used.
373    /// Default is to use the [global_clipboard](crate::clipboard::global_clipboard).
374    #[inline]
375    pub fn set_clipboard(&mut self, clip: Option<impl Clipboard + 'static>) {
376        self.widget.set_clipboard(clip);
377    }
378
379    /// Clipboard used.
380    /// Default is to use the [global_clipboard](crate::clipboard::global_clipboard).
381    #[inline]
382    pub fn clipboard(&self) -> Option<&dyn Clipboard> {
383        self.widget.clipboard()
384    }
385
386    /// Copy to clipboard
387    #[inline]
388    pub fn copy_to_clip(&mut self) -> bool {
389        let Some(clip) = self.widget.value.clipboard() else {
390            return false;
391        };
392
393        if self.has_selection() {
394            _ = clip.set_string(self.selected_text().as_ref());
395        } else {
396            let r = (self.value.0 * 255f32) as u32;
397            let g = (self.value.1 * 255f32) as u32;
398            let b = (self.value.2 * 255f32) as u32;
399            let value_str = format!("{:06x}", (r << 16) + (g << 8) + b);
400            _ = clip.set_string(&value_str);
401        }
402        true
403    }
404
405    /// Cut to clipboard
406    #[inline]
407    pub fn cut_to_clip(&mut self) -> bool {
408        let Some(clip) = self.widget.value.clipboard() else {
409            return false;
410        };
411
412        if self.has_selection() {
413            match clip.set_string(self.selected_text().as_ref()) {
414                Ok(_) => self.delete_range(self.selection()),
415                Err(_) => false,
416            }
417        } else {
418            let r = (self.value.0 * 255f32) as u32;
419            let g = (self.value.1 * 255f32) as u32;
420            let b = (self.value.2 * 255f32) as u32;
421            let value_str = format!("{:06x}", (r << 16) + (g << 8) + b);
422
423            match clip.set_string(&value_str) {
424                Ok(_) => self.clear(),
425                Err(_) => {}
426            }
427            true
428        }
429    }
430
431    /// Paste from clipboard.
432    #[inline]
433    pub fn paste_from_clip(&mut self) -> bool {
434        let Some(clip) = self.widget.value.clipboard() else {
435            return false;
436        };
437
438        if let Ok(text) = clip.get_string() {
439            if text.starts_with("#") && text.len() == 7 {
440                // #aabbcc
441                if let Ok(v) = u32::from_str_radix(&text[1..7], 16) {
442                    self.set_value_u32(v);
443                }
444            } else if text.starts_with("#") && text.len() == 9 {
445                // #aabbccdd
446                if let Ok(v) = u32::from_str_radix(&text[1..7], 16) {
447                    self.set_value_u32(v);
448                }
449            } else if text.starts_with("0x") && text.len() == 8 {
450                // 0xaabbcc
451                if let Ok(v) = u32::from_str_radix(&text[2..8], 16) {
452                    self.set_value_u32(v);
453                }
454            } else if text.starts_with("0x") && text.len() == 10 {
455                // 0xaabbccdd
456                if let Ok(v) = u32::from_str_radix(&text[2..8], 16) {
457                    self.set_value_u32(v);
458                }
459            } else if text.len() == 6 {
460                // aabbcc
461                if let Ok(v) = u32::from_str_radix(&text[0..6], 16) {
462                    self.set_value_u32(v);
463                }
464            } else if text.len() == 8 {
465                // aabbccdd
466                if let Ok(v) = u32::from_str_radix(&text[0..6], 16) {
467                    self.set_value_u32(v);
468                }
469            } else {
470                for c in text.chars() {
471                    self.widget.insert_char(c);
472                }
473            }
474            true
475        } else {
476            false
477        }
478    }
479}
480
481impl ColorInputState {
482    /// Set undo buffer.
483    #[inline]
484    pub fn set_undo_buffer(&mut self, undo: Option<impl UndoBuffer + 'static>) {
485        self.widget.set_undo_buffer(undo);
486    }
487
488    /// Undo
489    #[inline]
490    pub fn undo_buffer(&self) -> Option<&dyn UndoBuffer> {
491        self.widget.undo_buffer()
492    }
493
494    /// Undo
495    #[inline]
496    pub fn undo_buffer_mut(&mut self) -> Option<&mut dyn UndoBuffer> {
497        self.widget.undo_buffer_mut()
498    }
499
500    /// Get all recent replay recordings.
501    #[inline]
502    pub fn recent_replay_log(&mut self) -> Vec<UndoEntry> {
503        self.widget.recent_replay_log()
504    }
505
506    /// Apply the replay recording.
507    #[inline]
508    pub fn replay_log(&mut self, replay: &[UndoEntry]) {
509        self.widget.replay_log(replay)
510    }
511
512    /// Undo operation
513    #[inline]
514    pub fn undo(&mut self) -> bool {
515        self.widget.undo()
516    }
517
518    /// Redo operation
519    #[inline]
520    pub fn redo(&mut self) -> bool {
521        self.widget.redo()
522    }
523}
524
525impl ColorInputState {
526    /// Set and replace all styles.
527    #[inline]
528    pub fn set_styles(&mut self, styles: Vec<(Range<usize>, usize)>) {
529        self.widget.set_styles(styles);
530    }
531
532    /// Add a style for a byte-range.
533    #[inline]
534    pub fn add_style(&mut self, range: Range<usize>, style: usize) {
535        self.widget.add_style(range, style);
536    }
537
538    /// Add a style for a `Range<upos_type>` .
539    /// The style-nr refers to one of the styles set with the widget.
540    #[inline]
541    pub fn add_range_style(
542        &mut self,
543        range: Range<upos_type>,
544        style: usize,
545    ) -> Result<(), TextError> {
546        self.widget.add_range_style(range, style)
547    }
548
549    /// Remove the exact TextRange and style.
550    #[inline]
551    pub fn remove_style(&mut self, range: Range<usize>, style: usize) {
552        self.widget.remove_style(range, style);
553    }
554
555    /// Remove the exact `Range<upos_type>` and style.
556    #[inline]
557    pub fn remove_range_style(
558        &mut self,
559        range: Range<upos_type>,
560        style: usize,
561    ) -> Result<(), TextError> {
562        self.widget.remove_range_style(range, style)
563    }
564
565    /// Find all styles that touch the given range.
566    pub fn styles_in(&self, range: Range<usize>, buf: &mut Vec<(Range<usize>, usize)>) {
567        self.widget.styles_in(range, buf)
568    }
569
570    /// All styles active at the given position.
571    #[inline]
572    pub fn styles_at(&self, byte_pos: usize, buf: &mut Vec<(Range<usize>, usize)>) {
573        self.widget.styles_at(byte_pos, buf)
574    }
575
576    /// Check if the given style applies at the position and
577    /// return the complete range for the style.
578    #[inline]
579    pub fn style_match(&self, byte_pos: usize, style: usize) -> Option<Range<usize>> {
580        self.widget.styles_at_match(byte_pos, style)
581    }
582
583    /// List of all styles.
584    #[inline]
585    pub fn styles(&self) -> Option<impl Iterator<Item = (Range<usize>, usize)> + '_> {
586        self.widget.styles()
587    }
588}
589
590impl ColorInputState {
591    /// Offset shown.
592    #[inline]
593    pub fn offset(&self) -> upos_type {
594        self.widget.offset()
595    }
596
597    /// Offset shown. This is corrected if the cursor wouldn't be visible.
598    #[inline]
599    pub fn set_offset(&mut self, offset: upos_type) {
600        self.widget.set_offset(offset)
601    }
602
603    /// Cursor position
604    #[inline]
605    pub fn cursor(&self) -> upos_type {
606        self.widget.cursor()
607    }
608
609    /// Set the cursor position, reset selection.
610    #[inline]
611    pub fn set_cursor(&mut self, cursor: upos_type, extend_selection: bool) -> bool {
612        self.widget.set_cursor(cursor, extend_selection)
613    }
614
615    /// Place cursor at some sensible position according to the mask.
616    #[inline]
617    pub fn set_default_cursor(&mut self) {
618        self.widget.set_default_cursor()
619    }
620
621    /// Selection anchor.
622    #[inline]
623    pub fn anchor(&self) -> upos_type {
624        self.widget.anchor()
625    }
626
627    /// Selection
628    #[inline]
629    pub fn has_selection(&self) -> bool {
630        self.widget.has_selection()
631    }
632
633    /// Selection
634    #[inline]
635    pub fn selection(&self) -> Range<upos_type> {
636        self.widget.selection()
637    }
638
639    /// Selection
640    #[inline]
641    pub fn set_selection(&mut self, anchor: upos_type, cursor: upos_type) -> bool {
642        self.widget.set_selection(anchor, cursor)
643    }
644
645    /// Select all text.
646    #[inline]
647    pub fn select_all(&mut self) {
648        self.widget.select_all();
649    }
650
651    /// Selection
652    #[inline]
653    pub fn selected_text(&self) -> &str {
654        self.widget.selected_text()
655    }
656}
657
658impl ColorInputState {
659    /// Empty
660    #[inline]
661    pub fn is_empty(&self) -> bool {
662        self.widget.is_empty()
663    }
664
665    /// Value as Color.
666    pub fn value(&self) -> Color {
667        Color::Rgb(
668            (self.value.0 * 255f32) as u8,
669            (self.value.1 * 255f32) as u8,
670            (self.value.2 * 255f32) as u8,
671        )
672    }
673
674    /// Get the value as u32
675    pub fn value_u32(&self) -> u32 {
676        (((self.value.0 * 255f32) as u32) << 16)
677            + (((self.value.1 * 255f32) as u32) << 8)
678            + ((self.value.2 * 255f32) as u32)
679    }
680
681    fn parse_value(&self) -> (f32, f32, f32) {
682        let r = match self.mode {
683            Mode::RGB => {
684                let r = self.widget.section_value::<f32>(SEC_R).unwrap_or_default();
685                let g = self.widget.section_value::<f32>(SEC_G).unwrap_or_default();
686                let b = self.widget.section_value::<f32>(SEC_B).unwrap_or_default();
687                (r / 255f32, g / 255f32, b / 255f32)
688            }
689            Mode::HEX => {
690                let v = u32::from_str_radix(self.widget.section_text(1), 16).expect("hex");
691                let r = ((v >> 16) & 255) as f32;
692                let g = ((v >> 8) & 255) as f32;
693                let b = (v & 255) as f32;
694                (r / 255f32, g / 255f32, b / 255f32)
695            }
696            Mode::HSV => {
697                let h = self.widget.section_value::<f32>(SEC_H).unwrap_or_default();
698                let s = self.widget.section_value::<f32>(SEC_S).unwrap_or_default();
699                let v = self.widget.section_value::<f32>(SEC_V).unwrap_or_default();
700
701                let h = palette::RgbHue::from_degrees(h);
702                let s = s / 100f32;
703                let v = v / 100f32;
704
705                let hsv = Hsv::from_components((h, s, v));
706                let rgb = Srgb::from_color(hsv);
707
708                rgb.into_components()
709            }
710        };
711        r
712    }
713
714    /// Length in grapheme count.
715    #[inline]
716    pub fn len(&self) -> upos_type {
717        self.widget.len()
718    }
719
720    /// Length as grapheme count.
721    #[inline]
722    pub fn line_width(&self) -> upos_type {
723        self.widget.line_width()
724    }
725}
726
727impl ColorInputState {
728    /// Reset to empty.
729    #[inline]
730    pub fn clear(&mut self) {
731        self.widget.clear();
732    }
733
734    /// Set the color as u32
735    pub fn set_value_u32(&mut self, color: u32) {
736        let r = ((color >> 16) & 255) as f32;
737        let g = ((color >> 8) & 255) as f32;
738        let b = (color & 255) as f32;
739        self.value = (r / 255f32, g / 255f32, b / 255f32);
740        self.value_to_text();
741    }
742
743    /// Set the color as Color.
744    pub fn set_value(&mut self, color: Color) {
745        let (r, g, b) = color2rgb(color);
746        self.value = (r as f32 / 255f32, g as f32 / 255f32, b as f32 / 255f32);
747        self.value_to_text();
748    }
749
750    fn value_to_text(&mut self) {
751        match self.mode {
752            Mode::RGB => {
753                let r = (self.value.0 * 255f32) as u8;
754                let g = (self.value.1 * 255f32) as u8;
755                let b = (self.value.2 * 255f32) as u8;
756                let value_str = format!("{:3} {:3} {:3}", r, g, b);
757                self.widget.set_text(value_str);
758                self.set_mode_styles();
759            }
760            Mode::HEX => {
761                let r = (self.value.0 * 255f32) as u32;
762                let g = (self.value.1 * 255f32) as u32;
763                let b = (self.value.2 * 255f32) as u32;
764                let value_str = format!("{:06x}", (r << 16) + (g << 8) + b);
765                self.widget.set_text(value_str);
766                self.set_mode_styles();
767            }
768            Mode::HSV => {
769                let r = self.value.0;
770                let g = self.value.1;
771                let b = self.value.2;
772                let srgb = Srgb::new(r, g, b);
773                let hsv = Hsv::from_color(srgb);
774                let (h, s, v) = hsv.into_components();
775                let h = h.into_positive_degrees() as u32;
776                let s = (s * 100f32) as u32;
777                let v = (v * 100f32) as u32;
778                let value_str = format!("{:3} {:3} {:3}", h, s, v);
779                self.widget.set_text(value_str);
780                self.set_mode_styles();
781            }
782        }
783    }
784
785    /// Insert a char at the current position.
786    #[inline]
787    pub fn insert_char(&mut self, c: char) -> bool {
788        let r = self.widget.insert_char(c);
789        self.normalize();
790        r
791    }
792
793    /// Remove the selected range. The text will be replaced with the default value
794    /// as defined by the mask.
795    #[inline]
796    pub fn delete_range(&mut self, range: Range<upos_type>) -> bool {
797        let r = self.widget.delete_range(range);
798        self.normalize();
799        r
800    }
801
802    /// Remove the selected range. The text will be replaced with the default value
803    /// as defined by the mask.
804    #[inline]
805    pub fn try_delete_range(&mut self, range: Range<upos_type>) -> Result<bool, TextError> {
806        let r = self.widget.try_delete_range(range);
807        self.normalize();
808        r
809    }
810}
811
812const PAT_RGB: &'static str = "##0 ##0 ##0";
813const SEC_R: u16 = 1;
814const SEC_G: u16 = 3;
815const SEC_B: u16 = 5;
816
817const PAT_HEX: &'static str = "HHHHHH";
818#[allow(dead_code)]
819const SEC_X: u16 = 1;
820
821const PAT_HSV: &'static str = "##0 ##0 ##0";
822const SEC_H: u16 = 1;
823const SEC_S: u16 = 3;
824const SEC_V: u16 = 5;
825
826impl ColorInputState {
827    fn clamp_section(&mut self, section: u16, clamp: u32) -> bool {
828        let r = self
829            .widget
830            .section_value::<u32>(section)
831            .unwrap_or_default();
832        let r_min = min(r, clamp);
833        if r_min != r {
834            self.widget.set_section_value(section, r_min);
835            true
836        } else {
837            false
838        }
839    }
840
841    /// Correct the numeric values for each component.
842    fn normalize(&mut self) -> bool {
843        let r = match self.mode {
844            Mode::RGB => {
845                self.clamp_section(SEC_R, 255)
846                    || self.clamp_section(SEC_G, 255)
847                    || self.clamp_section(SEC_B, 255)
848            }
849            Mode::HEX => {
850                // noop
851                false
852            }
853            Mode::HSV => {
854                self.clamp_section(SEC_H, 360)
855                    || self.clamp_section(SEC_S, 100)
856                    || self.clamp_section(SEC_V, 100)
857            }
858        };
859        self.set_mode_styles();
860        r
861    }
862
863    /// Increment the value at the cursor position.
864    pub fn change_section(&mut self, n: i32) -> bool {
865        self.change_section_pos(self.cursor(), n)
866    }
867
868    pub fn change_section_pos(&mut self, pos: upos_type, n: i32) -> bool {
869        let section = self.widget.section_id(pos);
870        let r = match self.mode {
871            Mode::RGB => match section {
872                SEC_R | SEC_G | SEC_B => {
873                    let r = self
874                        .widget
875                        .section_value::<u32>(section)
876                        .unwrap_or_default();
877                    let r_min = min(r.saturating_add_signed(n), 255);
878                    if r_min != r {
879                        self.widget.set_section_value(section, r_min);
880                        self.set_mode_styles();
881                        true
882                    } else {
883                        false
884                    }
885                }
886                _ => false,
887            },
888            Mode::HEX => {
889                // noop
890                false
891            }
892            Mode::HSV => match section {
893                SEC_H => {
894                    let r = self
895                        .widget
896                        .section_value::<u32>(section)
897                        .unwrap_or_default();
898                    let r_min = {
899                        let mut r_min = (r as i32 + n) % 360;
900                        if r_min < 0 {
901                            r_min += 360;
902                        }
903                        r_min as u32
904                    };
905                    if r_min != r {
906                        self.widget.set_section_value(section, r_min);
907                        self.set_mode_styles();
908                        true
909                    } else {
910                        false
911                    }
912                }
913                SEC_S | SEC_V => {
914                    let r = self
915                        .widget
916                        .section_value::<u32>(section)
917                        .unwrap_or_default();
918                    let r_min = min(r.saturating_add_signed(n), 100);
919                    if r_min != r {
920                        self.widget.set_section_value(section, r_min);
921                        self.set_mode_styles();
922                        true
923                    } else {
924                        false
925                    }
926                }
927                _ => false,
928            },
929        };
930
931        if r {
932            self.value = self.parse_value();
933        }
934        r
935    }
936
937    fn set_mode_styles(&mut self) {
938        match self.mode {
939            Mode::RGB => {
940                // "##0 ##0 ##0"
941                self.widget.clear_styles();
942                self.widget.add_range_style(0..3, 1).expect("fine");
943                self.widget.add_range_style(4..7, 1).expect("fine");
944                self.widget.add_range_style(8..11, 1).expect("fine");
945            }
946            Mode::HEX => {
947                // "hhhhhH"
948                self.widget.clear_styles();
949                self.widget.add_range_style(0..6, 1).expect("fine");
950            }
951            Mode::HSV => {
952                // "##0 ##0 ##0"
953                self.widget.clear_styles();
954                self.widget.add_range_style(0..3, 1).expect("fine");
955                self.widget.add_range_style(4..7, 1).expect("fine");
956                self.widget.add_range_style(8..11, 1).expect("fine");
957            }
958        }
959    }
960
961    /// Color mode.
962    pub fn mode(&self) -> Mode {
963        self.mode
964    }
965
966    /// Set the color mode.
967    pub fn set_mode(&mut self, mode: Mode) -> bool {
968        self.mode = mode;
969        match self.mode {
970            Mode::RGB => {
971                // "##0 ##0 ##0"
972                self.widget.set_mask(PAT_RGB).expect("valid-mask");
973            }
974            Mode::HEX => {
975                // "hhhhhH"
976                self.widget.set_mask(PAT_HEX).expect("valid-mask");
977            }
978            Mode::HSV => {
979                // "##0 ##0 ##0"
980                self.widget.set_mask(PAT_HSV).expect("valid-mask");
981            }
982        }
983
984        self.set_mode_styles();
985        self.value_to_text();
986        self.widget.set_default_cursor();
987        true
988    }
989
990    /// Switch to next mode.
991    pub fn next_mode(&mut self) -> bool {
992        match self.mode {
993            Mode::RGB => self.set_mode(Mode::HEX),
994            Mode::HEX => self.set_mode(Mode::HSV),
995            Mode::HSV => self.set_mode(Mode::RGB),
996        }
997    }
998
999    /// Switch to prev mode.
1000    pub fn prev_mode(&mut self) -> bool {
1001        match self.mode {
1002            Mode::RGB => self.set_mode(Mode::HSV),
1003            Mode::HEX => self.set_mode(Mode::RGB),
1004            Mode::HSV => self.set_mode(Mode::HEX),
1005        }
1006    }
1007
1008    /// Move to the next char.
1009    #[inline]
1010    pub fn move_right(&mut self, extend_selection: bool) -> bool {
1011        self.widget.move_right(extend_selection)
1012    }
1013
1014    /// Move to the previous char.
1015    #[inline]
1016    pub fn move_left(&mut self, extend_selection: bool) -> bool {
1017        self.widget.move_left(extend_selection)
1018    }
1019
1020    /// Start of line
1021    #[inline]
1022    pub fn move_to_line_start(&mut self, extend_selection: bool) -> bool {
1023        self.widget.move_to_line_start(extend_selection)
1024    }
1025
1026    /// End of line
1027    #[inline]
1028    pub fn move_to_line_end(&mut self, extend_selection: bool) -> bool {
1029        self.widget.move_to_line_end(extend_selection)
1030    }
1031}
1032
1033impl HasScreenCursor for ColorInputState {
1034    /// The current text cursor as an absolute screen position.
1035    #[inline]
1036    fn screen_cursor(&self) -> Option<(u16, u16)> {
1037        self.widget.screen_cursor()
1038    }
1039}
1040
1041impl RelocatableState for ColorInputState {
1042    fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
1043        self.area.relocate(shift, clip);
1044        self.inner.relocate(shift, clip);
1045        self.mode_area.relocate(shift, clip);
1046        self.label_area.relocate(shift, clip);
1047        self.widget.relocate(shift, clip);
1048    }
1049}
1050
1051impl ColorInputState {
1052    /// Converts a grapheme based position to a screen position
1053    /// relative to the widget area.
1054    #[inline]
1055    pub fn col_to_screen(&self, pos: upos_type) -> Option<u16> {
1056        self.widget.col_to_screen(pos)
1057    }
1058
1059    /// Converts from a widget relative screen coordinate to a grapheme index.
1060    /// x is the relative screen position.
1061    #[inline]
1062    pub fn screen_to_col(&self, scx: i16) -> upos_type {
1063        self.widget.screen_to_col(scx)
1064    }
1065
1066    /// Set the cursor position from a screen position relative to the origin
1067    /// of the widget. This value can be negative, which selects a currently
1068    /// not visible position and scrolls to it.
1069    #[inline]
1070    pub fn set_screen_cursor(&mut self, cursor: i16, extend_selection: bool) -> bool {
1071        self.widget.set_screen_cursor(cursor, extend_selection)
1072    }
1073}
1074
1075/// Gives the luminance according to BT.709.
1076fn luminance_bt_srgb(color: Color) -> f32 {
1077    let (r, g, b) = color2rgb(color);
1078    0.2126f32 * ((r as f32) / 255f32).powf(2.2f32)
1079        + 0.7152f32 * ((g as f32) / 255f32).powf(2.2f32)
1080        + 0.0722f32 * ((b as f32) / 255f32).powf(2.2f32)
1081}
1082
1083/// Contrast between two colors.
1084fn contrast_bt_srgb(color: Color, color2: Color) -> f32 {
1085    let lum1 = luminance_bt_srgb(color);
1086    let lum2 = luminance_bt_srgb(color2);
1087    (lum1 - lum2).abs()
1088    // Don't use this prescribed method.
1089    // The abs diff comes out better.
1090    // (lum1 + 0.05f32) / (lum2 + 0.05f32)
1091}
1092
1093pub fn high_contrast_color(bg: Color, text: &[Color]) -> Style {
1094    let mut color0 = text[0];
1095    let mut color1 = text[0];
1096    let mut contrast1 = contrast_bt_srgb(color1, bg);
1097
1098    for text_color in text {
1099        let test = contrast_bt_srgb(*text_color, bg);
1100        if test > contrast1 {
1101            color0 = color1;
1102            color1 = *text_color;
1103            contrast1 = test;
1104        }
1105    }
1106    // don't use the second brightest.
1107    _ = color0;
1108
1109    Style::new().bg(bg).fg(color1)
1110}
1111
1112/// Gives back the rgb for any ratatui Color.
1113/// Has the indexed and the named colors too.
1114const fn color2rgb(color: Color) -> (u8, u8, u8) {
1115    match color {
1116        Color::Black => (0x00, 0x00, 0x00),
1117        Color::Red => (0xaa, 0x00, 0x00),
1118        Color::Green => (0x00, 0xaa, 0x00),
1119        Color::Yellow => (0xaa, 0x55, 0x00),
1120        Color::Blue => (0x00, 0x00, 0xaa),
1121        Color::Magenta => (0xaa, 0x00, 0xaa),
1122        Color::Cyan => (0x00, 0xaa, 0xaa),
1123        Color::Gray => (0xaa, 0xaa, 0xaa),
1124        Color::DarkGray => (0x55, 0x55, 0x55),
1125        Color::LightRed => (0xff, 0x55, 0x55),
1126        Color::LightGreen => (0x55, 0xff, 0x55),
1127        Color::LightYellow => (0xff, 0xff, 0x55),
1128        Color::LightBlue => (0x55, 0x55, 0xff),
1129        Color::LightMagenta => (0xff, 0x55, 0xff),
1130        Color::LightCyan => (0x55, 0xff, 0xff),
1131        Color::White => (0xff, 0xff, 0xff),
1132        Color::Rgb(r, g, b) => (r, g, b),
1133        Color::Indexed(i) => {
1134            const VGA256: [(u8, u8, u8); 256] = [
1135                (0x00, 0x00, 0x00),
1136                (0x80, 0x00, 0x00),
1137                (0x00, 0x80, 0x00),
1138                (0x80, 0x80, 0x00),
1139                (0x00, 0x00, 0x80),
1140                (0x80, 0x00, 0x80),
1141                (0x00, 0x80, 0x80),
1142                (0xc0, 0xc0, 0xc0),
1143                (0x80, 0x80, 0x80),
1144                (0xff, 0x00, 0x00),
1145                (0x00, 0xff, 0x00),
1146                (0xff, 0xff, 0x00),
1147                (0x00, 0x00, 0xff),
1148                (0xff, 0x00, 0xff),
1149                (0x00, 0xff, 0xff),
1150                (0xff, 0xff, 0xff),
1151                (0x00, 0x00, 0x00),
1152                (0x00, 0x00, 0x5f),
1153                (0x00, 0x00, 0x87),
1154                (0x00, 0x00, 0xaf),
1155                (0x00, 0x00, 0xd7),
1156                (0x00, 0x00, 0xff),
1157                (0x00, 0x5f, 0x00),
1158                (0x00, 0x5f, 0x5f),
1159                (0x00, 0x5f, 0x87),
1160                (0x00, 0x5f, 0xaf),
1161                (0x00, 0x5f, 0xd7),
1162                (0x00, 0x5f, 0xff),
1163                (0x00, 0x87, 0x00),
1164                (0x00, 0x87, 0x5f),
1165                (0x00, 0x87, 0x87),
1166                (0x00, 0x87, 0xaf),
1167                (0x00, 0x87, 0xd7),
1168                (0x00, 0x87, 0xff),
1169                (0x00, 0xaf, 0x00),
1170                (0x00, 0xaf, 0x5f),
1171                (0x00, 0xaf, 0x87),
1172                (0x00, 0xaf, 0xaf),
1173                (0x00, 0xaf, 0xd7),
1174                (0x00, 0xaf, 0xff),
1175                (0x00, 0xd7, 0x00),
1176                (0x00, 0xd7, 0x5f),
1177                (0x00, 0xd7, 0x87),
1178                (0x00, 0xd7, 0xaf),
1179                (0x00, 0xd7, 0xd7),
1180                (0x00, 0xd7, 0xff),
1181                (0x00, 0xff, 0x00),
1182                (0x00, 0xff, 0x5f),
1183                (0x00, 0xff, 0x87),
1184                (0x00, 0xff, 0xaf),
1185                (0x00, 0xff, 0xd7),
1186                (0x00, 0xff, 0xff),
1187                (0x5f, 0x00, 0x00),
1188                (0x5f, 0x00, 0x5f),
1189                (0x5f, 0x00, 0x87),
1190                (0x5f, 0x00, 0xaf),
1191                (0x5f, 0x00, 0xd7),
1192                (0x5f, 0x00, 0xff),
1193                (0x5f, 0x5f, 0x00),
1194                (0x5f, 0x5f, 0x5f),
1195                (0x5f, 0x5f, 0x87),
1196                (0x5f, 0x5f, 0xaf),
1197                (0x5f, 0x5f, 0xd7),
1198                (0x5f, 0x5f, 0xff),
1199                (0x5f, 0x87, 0x00),
1200                (0x5f, 0x87, 0x5f),
1201                (0x5f, 0x87, 0x87),
1202                (0x5f, 0x87, 0xaf),
1203                (0x5f, 0x87, 0xd7),
1204                (0x5f, 0x87, 0xff),
1205                (0x5f, 0xaf, 0x00),
1206                (0x5f, 0xaf, 0x5f),
1207                (0x5f, 0xaf, 0x87),
1208                (0x5f, 0xaf, 0xaf),
1209                (0x5f, 0xaf, 0xd7),
1210                (0x5f, 0xaf, 0xff),
1211                (0x5f, 0xd7, 0x00),
1212                (0x5f, 0xd7, 0x5f),
1213                (0x5f, 0xd7, 0x87),
1214                (0x5f, 0xd7, 0xaf),
1215                (0x5f, 0xd7, 0xd7),
1216                (0x5f, 0xd7, 0xff),
1217                (0x5f, 0xff, 0x00),
1218                (0x5f, 0xff, 0x5f),
1219                (0x5f, 0xff, 0x87),
1220                (0x5f, 0xff, 0xaf),
1221                (0x5f, 0xff, 0xd7),
1222                (0x5f, 0xff, 0xff),
1223                (0x87, 0x00, 0x00),
1224                (0x87, 0x00, 0x5f),
1225                (0x87, 0x00, 0x87),
1226                (0x87, 0x00, 0xaf),
1227                (0x87, 0x00, 0xd7),
1228                (0x87, 0x00, 0xff),
1229                (0x87, 0x5f, 0x00),
1230                (0x87, 0x5f, 0x5f),
1231                (0x87, 0x5f, 0x87),
1232                (0x87, 0x5f, 0xaf),
1233                (0x87, 0x5f, 0xd7),
1234                (0x87, 0x5f, 0xff),
1235                (0x87, 0x87, 0x00),
1236                (0x87, 0x87, 0x5f),
1237                (0x87, 0x87, 0x87),
1238                (0x87, 0x87, 0xaf),
1239                (0x87, 0x87, 0xd7),
1240                (0x87, 0x87, 0xff),
1241                (0x87, 0xaf, 0x00),
1242                (0x87, 0xaf, 0x5f),
1243                (0x87, 0xaf, 0x87),
1244                (0x87, 0xaf, 0xaf),
1245                (0x87, 0xaf, 0xd7),
1246                (0x87, 0xaf, 0xff),
1247                (0x87, 0xd7, 0x00),
1248                (0x87, 0xd7, 0x5f),
1249                (0x87, 0xd7, 0x87),
1250                (0x87, 0xd7, 0xaf),
1251                (0x87, 0xd7, 0xd7),
1252                (0x87, 0xd7, 0xff),
1253                (0x87, 0xff, 0x00),
1254                (0x87, 0xff, 0x5f),
1255                (0x87, 0xff, 0x87),
1256                (0x87, 0xff, 0xaf),
1257                (0x87, 0xff, 0xd7),
1258                (0x87, 0xff, 0xff),
1259                (0xaf, 0x00, 0x00),
1260                (0xaf, 0x00, 0x5f),
1261                (0xaf, 0x00, 0x87),
1262                (0xaf, 0x00, 0xaf),
1263                (0xaf, 0x00, 0xd7),
1264                (0xaf, 0x00, 0xff),
1265                (0xaf, 0x5f, 0x00),
1266                (0xaf, 0x5f, 0x5f),
1267                (0xaf, 0x5f, 0x87),
1268                (0xaf, 0x5f, 0xaf),
1269                (0xaf, 0x5f, 0xd7),
1270                (0xaf, 0x5f, 0xff),
1271                (0xaf, 0x87, 0x00),
1272                (0xaf, 0x87, 0x5f),
1273                (0xaf, 0x87, 0x87),
1274                (0xaf, 0x87, 0xaf),
1275                (0xaf, 0x87, 0xd7),
1276                (0xaf, 0x87, 0xff),
1277                (0xaf, 0xaf, 0x00),
1278                (0xaf, 0xaf, 0x5f),
1279                (0xaf, 0xaf, 0x87),
1280                (0xaf, 0xaf, 0xaf),
1281                (0xaf, 0xaf, 0xd7),
1282                (0xaf, 0xaf, 0xff),
1283                (0xaf, 0xd7, 0x00),
1284                (0xaf, 0xd7, 0x5f),
1285                (0xaf, 0xd7, 0x87),
1286                (0xaf, 0xd7, 0xaf),
1287                (0xaf, 0xd7, 0xd7),
1288                (0xaf, 0xd7, 0xff),
1289                (0xaf, 0xff, 0x00),
1290                (0xaf, 0xff, 0x5f),
1291                (0xaf, 0xff, 0x87),
1292                (0xaf, 0xff, 0xaf),
1293                (0xaf, 0xff, 0xd7),
1294                (0xaf, 0xff, 0xff),
1295                (0xd7, 0x00, 0x00),
1296                (0xd7, 0x00, 0x5f),
1297                (0xd7, 0x00, 0x87),
1298                (0xd7, 0x00, 0xaf),
1299                (0xd7, 0x00, 0xd7),
1300                (0xd7, 0x00, 0xff),
1301                (0xd7, 0x5f, 0x00),
1302                (0xd7, 0x5f, 0x5f),
1303                (0xd7, 0x5f, 0x87),
1304                (0xd7, 0x5f, 0xaf),
1305                (0xd7, 0x5f, 0xd7),
1306                (0xd7, 0x5f, 0xff),
1307                (0xd7, 0x87, 0x00),
1308                (0xd7, 0x87, 0x5f),
1309                (0xd7, 0x87, 0x87),
1310                (0xd7, 0x87, 0xaf),
1311                (0xd7, 0x87, 0xd7),
1312                (0xd7, 0x87, 0xff),
1313                (0xd7, 0xaf, 0x00),
1314                (0xd7, 0xaf, 0x5f),
1315                (0xd7, 0xaf, 0x87),
1316                (0xd7, 0xaf, 0xaf),
1317                (0xd7, 0xaf, 0xd7),
1318                (0xd7, 0xaf, 0xff),
1319                (0xd7, 0xd7, 0x00),
1320                (0xd7, 0xd7, 0x5f),
1321                (0xd7, 0xd7, 0x87),
1322                (0xd7, 0xd7, 0xaf),
1323                (0xd7, 0xd7, 0xd7),
1324                (0xd7, 0xd7, 0xff),
1325                (0xd7, 0xff, 0x00),
1326                (0xd7, 0xff, 0x5f),
1327                (0xd7, 0xff, 0x87),
1328                (0xd7, 0xff, 0xaf),
1329                (0xd7, 0xff, 0xd7),
1330                (0xd7, 0xff, 0xff),
1331                (0xff, 0x00, 0x00),
1332                (0xff, 0x00, 0x5f),
1333                (0xff, 0x00, 0x87),
1334                (0xff, 0x00, 0xaf),
1335                (0xff, 0x00, 0xd7),
1336                (0xff, 0x00, 0xff),
1337                (0xff, 0x5f, 0x00),
1338                (0xff, 0x5f, 0x5f),
1339                (0xff, 0x5f, 0x87),
1340                (0xff, 0x5f, 0xaf),
1341                (0xff, 0x5f, 0xd7),
1342                (0xff, 0x5f, 0xff),
1343                (0xff, 0x87, 0x00),
1344                (0xff, 0x87, 0x5f),
1345                (0xff, 0x87, 0x87),
1346                (0xff, 0x87, 0xaf),
1347                (0xff, 0x87, 0xd7),
1348                (0xff, 0x87, 0xff),
1349                (0xff, 0xaf, 0x00),
1350                (0xff, 0xaf, 0x5f),
1351                (0xff, 0xaf, 0x87),
1352                (0xff, 0xaf, 0xaf),
1353                (0xff, 0xaf, 0xd7),
1354                (0xff, 0xaf, 0xff),
1355                (0xff, 0xd7, 0x00),
1356                (0xff, 0xd7, 0x5f),
1357                (0xff, 0xd7, 0x87),
1358                (0xff, 0xd7, 0xaf),
1359                (0xff, 0xd7, 0xd7),
1360                (0xff, 0xd7, 0xff),
1361                (0xff, 0xff, 0x00),
1362                (0xff, 0xff, 0x5f),
1363                (0xff, 0xff, 0x87),
1364                (0xff, 0xff, 0xaf),
1365                (0xff, 0xff, 0xd7),
1366                (0xff, 0xff, 0xff),
1367                (0x08, 0x08, 0x08),
1368                (0x12, 0x12, 0x12),
1369                (0x1c, 0x1c, 0x1c),
1370                (0x26, 0x26, 0x26),
1371                (0x30, 0x30, 0x30),
1372                (0x3a, 0x3a, 0x3a),
1373                (0x44, 0x44, 0x44),
1374                (0x4e, 0x4e, 0x4e),
1375                (0x58, 0x58, 0x58),
1376                (0x62, 0x62, 0x62),
1377                (0x6c, 0x6c, 0x6c),
1378                (0x76, 0x76, 0x76),
1379                (0x80, 0x80, 0x80),
1380                (0x8a, 0x8a, 0x8a),
1381                (0x94, 0x94, 0x94),
1382                (0x9e, 0x9e, 0x9e),
1383                (0xa8, 0xa8, 0xa8),
1384                (0xb2, 0xb2, 0xb2),
1385                (0xbc, 0xbc, 0xbc),
1386                (0xc6, 0xc6, 0xc6),
1387                (0xd0, 0xd0, 0xd0),
1388                (0xda, 0xda, 0xda),
1389                (0xe4, 0xe4, 0xe4),
1390                (0xee, 0xee, 0xee),
1391            ];
1392            VGA256[i as usize]
1393        }
1394        Color::Reset => (0, 0, 0),
1395    }
1396}
1397
1398// + #
1399
1400impl HandleEvent<crossterm::event::Event, Regular, TextOutcome> for ColorInputState {
1401    fn handle(&mut self, event: &crossterm::event::Event, _keymap: Regular) -> TextOutcome {
1402        if self.is_focused() {
1403            flow!(match event {
1404                ct_event!(key press '+') | ct_event!(keycode press Up) =>
1405                    self.change_section(1).into(),
1406                ct_event!(key press '-') | ct_event!(keycode press Down) =>
1407                    self.change_section(-1).into(),
1408                ct_event!(key press ALT-'+') | ct_event!(keycode press ALT-Up) =>
1409                    self.change_section(7).into(),
1410                ct_event!(key press ALT-'-') | ct_event!(keycode press ALT-Down) =>
1411                    self.change_section(-7).into(),
1412                ct_event!(key press CONTROL-'v') => self.paste_from_clip().into(),
1413                ct_event!(key press CONTROL-'c') => self.copy_to_clip().into(),
1414                ct_event!(key press CONTROL-'x') => self.cut_to_clip().into(),
1415                _ => TextOutcome::Continue,
1416            });
1417            if !self.disable_modes {
1418                flow!(match event {
1419                    ct_event!(key press 'r') => self.set_mode(Mode::RGB).into(),
1420                    ct_event!(key press 'h') => self.set_mode(Mode::HSV).into(),
1421                    ct_event!(key press 'x') => self.set_mode(Mode::HEX).into(),
1422                    ct_event!(key press 'm') | ct_event!(keycode press PageUp) =>
1423                        self.next_mode().into(),
1424                    ct_event!(key press SHIFT-'M') | ct_event!(keycode press PageDown) =>
1425                        self.prev_mode().into(),
1426                    _ => TextOutcome::Continue,
1427                });
1428            }
1429        }
1430
1431        flow!(handle_mouse(self, event));
1432
1433        match self.widget.handle(event, Regular) {
1434            TextOutcome::TextChanged => {
1435                self.normalize();
1436                self.value = self.parse_value();
1437                TextOutcome::TextChanged
1438            }
1439            r => r,
1440        }
1441    }
1442}
1443
1444impl HandleEvent<crossterm::event::Event, ReadOnly, TextOutcome> for ColorInputState {
1445    fn handle(&mut self, event: &crossterm::event::Event, _keymap: ReadOnly) -> TextOutcome {
1446        self.widget.handle(event, ReadOnly)
1447    }
1448}
1449
1450impl HandleEvent<crossterm::event::Event, MouseOnly, TextOutcome> for ColorInputState {
1451    fn handle(&mut self, event: &crossterm::event::Event, _keymap: MouseOnly) -> TextOutcome {
1452        flow!(handle_mouse(self, event));
1453        self.widget.handle(event, MouseOnly)
1454    }
1455}
1456
1457fn handle_mouse(state: &mut ColorInputState, event: &crossterm::event::Event) -> TextOutcome {
1458    match event {
1459        ct_event!(scroll ALT down for x,y) if state.mode_area.contains((*x, *y).into()) => {
1460            state.next_mode().into()
1461        }
1462        ct_event!(scroll ALT up for x,y) if state.mode_area.contains((*x, *y).into()) => {
1463            state.prev_mode().into()
1464        }
1465        ct_event!(scroll down for x,y) if state.widget.area.contains((*x, *y).into()) => {
1466            let rx = state
1467                .widget
1468                .screen_to_col((*x - state.widget.area.x) as i16);
1469            state.change_section_pos(rx, -1).into()
1470        }
1471        ct_event!(scroll up for x,y) if state.widget.area.contains((*x, *y).into()) => {
1472            let rx = state
1473                .widget
1474                .screen_to_col((*x - state.widget.area.x) as i16);
1475            state.change_section_pos(rx, 1).into()
1476        }
1477        ct_event!(scroll ALT down for x,y) if state.widget.area.contains((*x, *y).into()) => {
1478            let rx = state
1479                .widget
1480                .screen_to_col((*x - state.widget.area.x) as i16);
1481            state.change_section_pos(rx, -7).into()
1482        }
1483        ct_event!(scroll ALT up for x,y) if state.widget.area.contains((*x, *y).into()) => {
1484            let rx = state
1485                .widget
1486                .screen_to_col((*x - state.widget.area.x) as i16);
1487            state.change_section_pos(rx, 7).into()
1488        }
1489        _ => TextOutcome::Continue,
1490    }
1491}
1492
1493/// Handle all events.
1494/// Text events are only processed if focus is true.
1495/// Mouse events are processed if they are in range.
1496pub fn handle_events(
1497    state: &mut ColorInputState,
1498    focus: bool,
1499    event: &crossterm::event::Event,
1500) -> TextOutcome {
1501    state.widget.focus.set(focus);
1502    HandleEvent::handle(state, event, Regular)
1503}
1504
1505/// Handle only navigation events.
1506/// Text events are only processed if focus is true.
1507/// Mouse events are processed if they are in range.
1508pub fn handle_readonly_events(
1509    state: &mut ColorInputState,
1510    focus: bool,
1511    event: &crossterm::event::Event,
1512) -> TextOutcome {
1513    state.widget.focus.set(focus);
1514    state.handle(event, ReadOnly)
1515}
1516
1517/// Handle only mouse-events.
1518pub fn handle_mouse_events(
1519    state: &mut ColorInputState,
1520    event: &crossterm::event::Event,
1521) -> TextOutcome {
1522    HandleEvent::handle(state, event, MouseOnly)
1523}