1use 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, 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#[derive(Debug, Default)]
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#[derive(Debug)]
57pub struct ColorInputStyle {
58 pub text: TextStyle,
60 pub field_style: Option<Style>,
62 pub disable_modes: Option<bool>,
64 pub mode: Option<Mode>,
66 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#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
84pub enum Mode {
85 #[default]
86 RGB,
87 HEX,
88 HSV,
89}
90
91#[derive(Debug)]
93pub struct ColorInputState {
94 pub area: Rect,
97 pub widget_area: Rect,
99 pub mode_area: Rect,
102 pub label_area: Rect,
105
106 pub value: (f32, f32, f32),
108
109 pub disable_modes: bool,
112 pub mode: Mode,
114 pub widget: MaskedInputState,
116
117 pub non_exhaustive: NonExhaustive,
118}
119
120impl<'a> ColorInput<'a> {
121 pub fn new() -> Self {
122 Self::default()
123 }
124
125 #[inline]
127 pub fn styles(mut self, mut style: ColorInputStyle) -> Self {
128 self.style = style.text.style;
129 if let Some(border_style) = style.text.border_style {
130 self.block = self.block.map(|v| v.style(border_style));
131 }
132 if let Some(block) = style.text.block.take() {
133 self.block = Some(block);
134 }
135 self.widget = self.widget.styles(style.text);
136 if let Some(disable_modes) = style.disable_modes {
137 self.disable_modes = disable_modes;
138 }
139 if let Some(mode) = style.mode {
140 self.mode = Some(mode);
141 }
142 if let Some(field_style) = style.field_style {
143 self.widget = self.widget.text_style_idx(1, field_style);
144 }
145 self
146 }
147
148 #[inline]
150 pub fn style(mut self, style: impl Into<Style>) -> Self {
151 let style = style.into();
152 self.style = style;
153 self.widget = self.widget.style(style);
154 self
155 }
156
157 #[inline]
159 pub fn field_style(mut self, style: impl Into<Style>) -> Self {
160 self.widget = self.widget.text_style_idx(1, style.into());
161 self
162 }
163
164 #[inline]
166 pub fn disable_modes(mut self) -> Self {
167 self.disable_modes = true;
168 self
169 }
170
171 #[inline]
173 pub fn mode(mut self, mode: Mode) -> Self {
174 self.mode = Some(mode);
175 self
176 }
177
178 #[inline]
180 pub fn focus_style(mut self, style: impl Into<Style>) -> Self {
181 self.widget = self.widget.focus_style(style);
182 self
183 }
184
185 #[inline]
187 pub fn select_style(mut self, style: impl Into<Style>) -> Self {
188 self.widget = self.widget.select_style(style);
189 self
190 }
191
192 #[inline]
194 pub fn invalid_style(mut self, style: impl Into<Style>) -> Self {
195 self.widget = self.widget.invalid_style(style);
196 self
197 }
198
199 #[inline]
201 pub fn block(mut self, block: Block<'a>) -> Self {
202 self.block = Some(block);
203 self
204 }
205
206 #[inline]
208 pub fn on_focus_gained(mut self, of: TextFocusGained) -> Self {
209 self.widget = self.widget.on_focus_gained(of);
210 self
211 }
212
213 #[inline]
215 pub fn on_focus_lost(mut self, of: TextFocusLost) -> Self {
216 self.widget = self.widget.on_focus_lost(of);
217 self
218 }
219}
220
221impl<'a> StatefulWidget for &ColorInput<'a> {
222 type State = ColorInputState;
223
224 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
225 render(self, area, buf, state);
226 }
227}
228
229impl StatefulWidget for ColorInput<'_> {
230 type State = ColorInputState;
231
232 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
233 render(&self, area, buf, state);
234 }
235}
236
237fn render(widget: &ColorInput<'_>, area: Rect, buf: &mut Buffer, state: &mut ColorInputState) {
238 state.disable_modes = widget.disable_modes;
239 if let Some(mode) = widget.mode {
240 state.mode = mode;
241 }
242
243 let inner = widget.block.inner_if_some(area);
244
245 let mode_area = Rect::new(inner.x, inner.y, 4, inner.height);
246 let mode_label = Rect::new(mode_area.x, mode_area.y + mode_area.height / 2, 4, 1);
247 let widget_area = Rect::new(
248 inner.x + mode_area.width,
249 inner.y,
250 inner.width.saturating_sub(mode_area.width),
251 inner.height,
252 );
253
254 state.widget_area = inner;
255 state.mode_area = mode_area;
256 state.label_area = mode_label;
257
258 let bg = state.value();
259 let fg_colors = [Color::Black, Color::White];
260 let style = high_contrast_color(bg, &fg_colors);
261
262 widget.block.render(area, buf);
263
264 buf.set_style(mode_area, style);
265 let mode_str = match state.mode {
266 Mode::RGB => "RGB",
267 Mode::HEX => " #",
268 Mode::HSV => "HSV",
269 };
270 Line::from(mode_str).render(mode_label, buf);
271
272 (&widget.widget).render(widget_area, buf, &mut state.widget);
273}
274
275impl Default for ColorInputState {
276 fn default() -> Self {
277 let mut z = Self {
278 area: Default::default(),
279 widget_area: Default::default(),
280 mode_area: Default::default(),
281 label_area: Default::default(),
282 value: Default::default(),
283 disable_modes: Default::default(),
284 mode: Default::default(),
285 widget: Default::default(),
286 non_exhaustive: NonExhaustive,
287 };
288 z.set_mode(Mode::RGB);
289 z
290 }
291}
292
293impl HasFocus for ColorInputState {
294 fn build(&self, builder: &mut FocusBuilder) {
295 builder.widget_with_flags(
296 self.widget.focus(),
297 self.area,
298 self.widget.area_z(),
299 self.widget.navigable(),
300 );
301 }
302
303 fn focus(&self) -> FocusFlag {
304 self.widget.focus()
305 }
306
307 fn area(&self) -> Rect {
308 self.area
309 }
310}
311
312impl ColorInputState {
313 pub fn new() -> Self {
314 Self::default()
315 }
316
317 pub fn named(name: &str) -> Self {
318 Self {
319 widget: MaskedInputState::named(name),
320 ..Default::default()
321 }
322 }
323
324 #[inline]
328 pub fn set_overwrite(&mut self, overwrite: bool) {
329 self.widget.set_overwrite(overwrite);
330 }
331
332 #[inline]
334 pub fn overwrite(&self) -> bool {
335 self.widget.overwrite()
336 }
337}
338
339impl ColorInputState {
340 #[inline]
343 pub fn set_clipboard(&mut self, clip: Option<impl Clipboard + 'static>) {
344 self.widget.set_clipboard(clip);
345 }
346
347 #[inline]
350 pub fn clipboard(&self) -> Option<&dyn Clipboard> {
351 self.widget.clipboard()
352 }
353
354 #[inline]
356 pub fn copy_to_clip(&mut self) -> bool {
357 let Some(clip) = self.widget.value.clipboard() else {
358 return false;
359 };
360
361 if self.has_selection() {
362 _ = clip.set_string(self.selected_text().as_ref());
363 } else {
364 let r = (self.value.0 * 255f32) as u32;
365 let g = (self.value.1 * 255f32) as u32;
366 let b = (self.value.2 * 255f32) as u32;
367 let value_str = format!("{:06x}", (r << 16) + (g << 8) + b);
368 _ = clip.set_string(&value_str);
369 }
370 true
371 }
372
373 #[inline]
375 pub fn cut_to_clip(&mut self) -> bool {
376 let Some(clip) = self.widget.value.clipboard() else {
377 return false;
378 };
379
380 if self.has_selection() {
381 match clip.set_string(self.selected_text().as_ref()) {
382 Ok(_) => self.delete_range(self.selection()),
383 Err(_) => false,
384 }
385 } else {
386 let r = (self.value.0 * 255f32) as u32;
387 let g = (self.value.1 * 255f32) as u32;
388 let b = (self.value.2 * 255f32) as u32;
389 let value_str = format!("{:06x}", (r << 16) + (g << 8) + b);
390
391 match clip.set_string(&value_str) {
392 Ok(_) => self.clear(),
393 Err(_) => {}
394 }
395 true
396 }
397 }
398
399 #[inline]
401 pub fn paste_from_clip(&mut self) -> bool {
402 let Some(clip) = self.widget.value.clipboard() else {
403 return false;
404 };
405
406 if let Ok(text) = clip.get_string() {
407 if text.starts_with("#") && text.len() == 7 {
408 if let Ok(v) = u32::from_str_radix(&text[1..7], 16) {
410 self.set_value_u32(v);
411 }
412 } else if text.starts_with("#") && text.len() == 9 {
413 if let Ok(v) = u32::from_str_radix(&text[1..7], 16) {
415 self.set_value_u32(v);
416 }
417 } else if text.starts_with("0x") && text.len() == 8 {
418 if let Ok(v) = u32::from_str_radix(&text[2..8], 16) {
420 self.set_value_u32(v);
421 }
422 } else if text.starts_with("0x") && text.len() == 10 {
423 if let Ok(v) = u32::from_str_radix(&text[2..8], 16) {
425 self.set_value_u32(v);
426 }
427 } else if text.len() == 6 {
428 if let Ok(v) = u32::from_str_radix(&text[0..6], 16) {
430 self.set_value_u32(v);
431 }
432 } else if text.len() == 8 {
433 if let Ok(v) = u32::from_str_radix(&text[0..6], 16) {
435 self.set_value_u32(v);
436 }
437 } else {
438 for c in text.chars() {
439 self.widget.insert_char(c);
440 }
441 }
442 true
443 } else {
444 false
445 }
446 }
447}
448
449impl ColorInputState {
450 #[inline]
452 pub fn set_undo_buffer(&mut self, undo: Option<impl UndoBuffer + 'static>) {
453 self.widget.set_undo_buffer(undo);
454 }
455
456 #[inline]
458 pub fn undo_buffer(&self) -> Option<&dyn UndoBuffer> {
459 self.widget.undo_buffer()
460 }
461
462 #[inline]
464 pub fn undo_buffer_mut(&mut self) -> Option<&mut dyn UndoBuffer> {
465 self.widget.undo_buffer_mut()
466 }
467
468 #[inline]
470 pub fn recent_replay_log(&mut self) -> Vec<UndoEntry> {
471 self.widget.recent_replay_log()
472 }
473
474 #[inline]
476 pub fn replay_log(&mut self, replay: &[UndoEntry]) {
477 self.widget.replay_log(replay)
478 }
479
480 #[inline]
482 pub fn undo(&mut self) -> bool {
483 self.widget.undo()
484 }
485
486 #[inline]
488 pub fn redo(&mut self) -> bool {
489 self.widget.redo()
490 }
491}
492
493impl ColorInputState {
494 #[inline]
496 pub fn set_styles(&mut self, styles: Vec<(Range<usize>, usize)>) {
497 self.widget.set_styles(styles);
498 }
499
500 #[inline]
502 pub fn add_style(&mut self, range: Range<usize>, style: usize) {
503 self.widget.add_style(range, style);
504 }
505
506 #[inline]
509 pub fn add_range_style(
510 &mut self,
511 range: Range<upos_type>,
512 style: usize,
513 ) -> Result<(), TextError> {
514 self.widget.add_range_style(range, style)
515 }
516
517 #[inline]
519 pub fn remove_style(&mut self, range: Range<usize>, style: usize) {
520 self.widget.remove_style(range, style);
521 }
522
523 #[inline]
525 pub fn remove_range_style(
526 &mut self,
527 range: Range<upos_type>,
528 style: usize,
529 ) -> Result<(), TextError> {
530 self.widget.remove_range_style(range, style)
531 }
532
533 pub fn styles_in(&self, range: Range<usize>, buf: &mut Vec<(Range<usize>, usize)>) {
535 self.widget.styles_in(range, buf)
536 }
537
538 #[inline]
540 pub fn styles_at(&self, byte_pos: usize, buf: &mut Vec<(Range<usize>, usize)>) {
541 self.widget.styles_at(byte_pos, buf)
542 }
543
544 #[inline]
547 pub fn style_match(&self, byte_pos: usize, style: usize) -> Option<Range<usize>> {
548 self.widget.styles_at_match(byte_pos, style)
549 }
550
551 #[inline]
553 pub fn styles(&self) -> Option<impl Iterator<Item = (Range<usize>, usize)> + '_> {
554 self.widget.styles()
555 }
556}
557
558impl ColorInputState {
559 #[inline]
561 pub fn offset(&self) -> upos_type {
562 self.widget.offset()
563 }
564
565 #[inline]
567 pub fn set_offset(&mut self, offset: upos_type) {
568 self.widget.set_offset(offset)
569 }
570
571 #[inline]
573 pub fn cursor(&self) -> upos_type {
574 self.widget.cursor()
575 }
576
577 #[inline]
579 pub fn set_cursor(&mut self, cursor: upos_type, extend_selection: bool) -> bool {
580 self.widget.set_cursor(cursor, extend_selection)
581 }
582
583 #[inline]
585 pub fn set_default_cursor(&mut self) {
586 self.widget.set_default_cursor()
587 }
588
589 #[inline]
591 pub fn anchor(&self) -> upos_type {
592 self.widget.anchor()
593 }
594
595 #[inline]
597 pub fn has_selection(&self) -> bool {
598 self.widget.has_selection()
599 }
600
601 #[inline]
603 pub fn selection(&self) -> Range<upos_type> {
604 self.widget.selection()
605 }
606
607 #[inline]
609 pub fn set_selection(&mut self, anchor: upos_type, cursor: upos_type) -> bool {
610 self.widget.set_selection(anchor, cursor)
611 }
612
613 #[inline]
615 pub fn select_all(&mut self) {
616 self.widget.select_all();
617 }
618
619 #[inline]
621 pub fn selected_text(&self) -> &str {
622 self.widget.selected_text()
623 }
624}
625
626impl ColorInputState {
627 #[inline]
629 pub fn is_empty(&self) -> bool {
630 self.widget.is_empty()
631 }
632
633 pub fn value(&self) -> Color {
635 Color::Rgb(
636 (self.value.0 * 255f32) as u8,
637 (self.value.1 * 255f32) as u8,
638 (self.value.2 * 255f32) as u8,
639 )
640 }
641
642 fn parse_value(&self) -> (f32, f32, f32) {
643 let r = match self.mode {
644 Mode::RGB => {
645 let r = self.widget.section_value::<f32>(SEC_R).unwrap_or_default();
646 let g = self.widget.section_value::<f32>(SEC_G).unwrap_or_default();
647 let b = self.widget.section_value::<f32>(SEC_B).unwrap_or_default();
648 (r / 255f32, g / 255f32, b / 255f32)
649 }
650 Mode::HEX => {
651 let v = u32::from_str_radix(self.widget.section_text(1), 16).expect("hex");
652 let r = ((v >> 16) & 255) as f32;
653 let g = ((v >> 8) & 255) as f32;
654 let b = (v & 255) as f32;
655 (r / 255f32, g / 255f32, b / 255f32)
656 }
657 Mode::HSV => {
658 let h = self.widget.section_value::<f32>(SEC_H).unwrap_or_default();
659 let s = self.widget.section_value::<f32>(SEC_S).unwrap_or_default();
660 let v = self.widget.section_value::<f32>(SEC_V).unwrap_or_default();
661
662 let h = palette::RgbHue::from_degrees(h);
663 let s = s / 100f32;
664 let v = v / 100f32;
665
666 let hsv = Hsv::from_components((h, s, v));
667 let rgb = Srgb::from_color(hsv);
668
669 rgb.into_components()
670 }
671 };
672 r
673 }
674
675 #[inline]
677 pub fn len(&self) -> upos_type {
678 self.widget.len()
679 }
680
681 #[inline]
683 pub fn line_width(&self) -> upos_type {
684 self.widget.line_width()
685 }
686}
687
688impl ColorInputState {
689 #[inline]
691 pub fn clear(&mut self) {
692 self.widget.clear();
693 }
694
695 pub fn set_value_u32(&mut self, color: u32) {
697 let r = ((color >> 16) & 255) as f32;
698 let g = ((color >> 8) & 255) as f32;
699 let b = (color & 255) as f32;
700 self.value = (r / 255f32, g / 255f32, b / 255f32);
701 self.value_to_text();
702 }
703
704 pub fn set_value(&mut self, color: Color) {
706 let (r, g, b) = color2rgb(color);
707 self.value = (r as f32 / 255f32, g as f32 / 255f32, b as f32 / 255f32);
708 self.value_to_text();
709 }
710
711 fn value_to_text(&mut self) {
712 match self.mode {
713 Mode::RGB => {
714 let r = (self.value.0 * 255f32) as u8;
715 let g = (self.value.1 * 255f32) as u8;
716 let b = (self.value.2 * 255f32) as u8;
717 let value_str = format!("{:3} {:3} {:3}", r, g, b);
718 self.widget.set_text(value_str);
719 self.set_mode_styles();
720 }
721 Mode::HEX => {
722 let r = (self.value.0 * 255f32) as u32;
723 let g = (self.value.1 * 255f32) as u32;
724 let b = (self.value.2 * 255f32) as u32;
725 let value_str = format!("{:06x}", (r << 16) + (g << 8) + b);
726 self.widget.set_text(value_str);
727 self.set_mode_styles();
728 }
729 Mode::HSV => {
730 let r = self.value.0;
731 let g = self.value.1;
732 let b = self.value.2;
733 let srgb = Srgb::new(r, g, b);
734 let hsv = Hsv::from_color(srgb);
735 let (h, s, v) = hsv.into_components();
736 let h = h.into_positive_degrees() as u32;
737 let s = (s * 100f32) as u32;
738 let v = (v * 100f32) as u32;
739 let value_str = format!("{:3} {:3} {:3}", h, s, v);
740 self.widget.set_text(value_str);
741 self.set_mode_styles();
742 }
743 }
744 }
745
746 #[inline]
748 pub fn insert_char(&mut self, c: char) -> bool {
749 let r = self.widget.insert_char(c);
750 self.normalize();
751 r
752 }
753
754 #[inline]
757 pub fn delete_range(&mut self, range: Range<upos_type>) -> bool {
758 let r = self.widget.delete_range(range);
759 self.normalize();
760 r
761 }
762
763 #[inline]
766 pub fn try_delete_range(&mut self, range: Range<upos_type>) -> Result<bool, TextError> {
767 let r = self.widget.try_delete_range(range);
768 self.normalize();
769 r
770 }
771}
772
773const PAT_RGB: &'static str = "##0 ##0 ##0";
774const SEC_R: u16 = 1;
775const SEC_G: u16 = 3;
776const SEC_B: u16 = 5;
777
778const PAT_HEX: &'static str = "HHHHHH";
779#[allow(dead_code)]
780const SEC_X: u16 = 1;
781
782const PAT_HSV: &'static str = "##0 ##0 ##0";
783const SEC_H: u16 = 1;
784const SEC_S: u16 = 3;
785const SEC_V: u16 = 5;
786
787impl ColorInputState {
788 fn clamp_section(&mut self, section: u16, clamp: u32) -> bool {
789 let r = self
790 .widget
791 .section_value::<u32>(section)
792 .unwrap_or_default();
793 let r_min = min(r, clamp);
794 if r_min != r {
795 self.widget.set_section_value(section, r_min);
796 true
797 } else {
798 false
799 }
800 }
801
802 fn normalize(&mut self) -> bool {
804 let r = match self.mode {
805 Mode::RGB => {
806 self.clamp_section(SEC_R, 255)
807 || self.clamp_section(SEC_G, 255)
808 || self.clamp_section(SEC_B, 255)
809 }
810 Mode::HEX => {
811 false
813 }
814 Mode::HSV => {
815 self.clamp_section(SEC_H, 360)
816 || self.clamp_section(SEC_S, 100)
817 || self.clamp_section(SEC_V, 100)
818 }
819 };
820 self.set_mode_styles();
821 r
822 }
823
824 pub fn increment(&mut self, n: u32) -> bool {
826 let section = self.widget.section_id(self.widget.cursor());
827 let r = match self.mode {
828 Mode::RGB => match section {
829 SEC_R | SEC_G | SEC_B => {
830 let r = self
831 .widget
832 .section_value::<u32>(section)
833 .unwrap_or_default();
834 let r_min = min(r + n, 255);
835 if r_min != r {
836 self.widget.set_section_value(section, r_min);
837 self.set_mode_styles();
838 true
839 } else {
840 false
841 }
842 }
843 _ => false,
844 },
845 Mode::HEX => {
846 false
848 }
849 Mode::HSV => match section {
850 SEC_H => {
851 let r = self
852 .widget
853 .section_value::<u32>(section)
854 .unwrap_or_default();
855 let r_min = (r + n) % 360;
856 if r_min != r {
857 self.widget.set_section_value(section, r_min);
858 self.set_mode_styles();
859 true
860 } else {
861 false
862 }
863 }
864 SEC_S | SEC_V => {
865 let r = self
866 .widget
867 .section_value::<u32>(section)
868 .unwrap_or_default();
869 let r_min = min(r + n, 100);
870 if r_min != r {
871 self.widget.set_section_value(section, r_min);
872 self.set_mode_styles();
873 true
874 } else {
875 false
876 }
877 }
878 _ => false,
879 },
880 };
881
882 if r {
883 self.value = self.parse_value();
884 }
885 r
886 }
887
888 pub fn decrement(&mut self, n: u32) -> bool {
890 let section = self.widget.section_id(self.widget.cursor());
891 let r = match self.mode {
892 Mode::RGB => match section {
893 SEC_R | SEC_G | SEC_B => {
894 let r = self
895 .widget
896 .section_value::<u32>(section)
897 .unwrap_or_default();
898 let r_min = r.saturating_sub(n);
899 if r_min != r {
900 self.widget.set_section_value(section, r_min);
901 self.set_mode_styles();
902 true
903 } else {
904 false
905 }
906 }
907 _ => false,
908 },
909 Mode::HEX => {
910 false
912 }
913 Mode::HSV => match section {
914 SEC_H => {
915 let r = self
916 .widget
917 .section_value::<u32>(section)
918 .unwrap_or_default();
919 let r_min = if r > n {
920 r - n
921 } else {
922 360u32.saturating_sub(n - r)
923 };
924 if r_min != r {
925 self.widget.set_section_value(section, r_min);
926 self.set_mode_styles();
927 true
928 } else {
929 false
930 }
931 }
932 SEC_S | SEC_V => {
933 let r = self
934 .widget
935 .section_value::<u32>(section)
936 .unwrap_or_default();
937 let r_min = r.saturating_sub(n);
938 if r_min != r {
939 self.widget.set_section_value(section, r_min);
940 self.set_mode_styles();
941 true
942 } else {
943 false
944 }
945 }
946 _ => false,
947 },
948 };
949
950 if r {
951 self.value = self.parse_value();
952 }
953 r
954 }
955
956 fn set_mode_styles(&mut self) {
957 match self.mode {
958 Mode::RGB => {
959 self.widget.clear_styles();
961 self.widget.add_range_style(0..3, 1).expect("fine");
962 self.widget.add_range_style(4..7, 1).expect("fine");
963 self.widget.add_range_style(8..11, 1).expect("fine");
964 }
965 Mode::HEX => {
966 self.widget.clear_styles();
968 self.widget.add_range_style(0..6, 1).expect("fine");
969 }
970 Mode::HSV => {
971 self.widget.clear_styles();
973 self.widget.add_range_style(0..3, 1).expect("fine");
974 self.widget.add_range_style(4..7, 1).expect("fine");
975 self.widget.add_range_style(8..11, 1).expect("fine");
976 }
977 }
978 }
979
980 pub fn set_mode(&mut self, mode: Mode) {
982 self.mode = mode;
983 match self.mode {
984 Mode::RGB => {
985 self.widget.set_mask(PAT_RGB).expect("valid-mask");
987 }
988 Mode::HEX => {
989 self.widget.set_mask(PAT_HEX).expect("valid-mask");
991 }
992 Mode::HSV => {
993 self.widget.set_mask(PAT_HSV).expect("valid-mask");
995 }
996 }
997
998 self.set_mode_styles();
999 self.value_to_text();
1000 self.widget.set_default_cursor();
1001 }
1002
1003 pub fn next_mode(&mut self) {
1005 match self.mode {
1006 Mode::RGB => self.set_mode(Mode::HEX),
1007 Mode::HEX => self.set_mode(Mode::HSV),
1008 Mode::HSV => self.set_mode(Mode::RGB),
1009 }
1010 }
1011
1012 pub fn prev_mode(&mut self) {
1014 match self.mode {
1015 Mode::RGB => self.set_mode(Mode::HSV),
1016 Mode::HEX => self.set_mode(Mode::RGB),
1017 Mode::HSV => self.set_mode(Mode::HEX),
1018 }
1019 }
1020
1021 #[inline]
1023 pub fn move_right(&mut self, extend_selection: bool) -> bool {
1024 self.widget.move_right(extend_selection)
1025 }
1026
1027 #[inline]
1029 pub fn move_left(&mut self, extend_selection: bool) -> bool {
1030 self.widget.move_left(extend_selection)
1031 }
1032
1033 #[inline]
1035 pub fn move_to_line_start(&mut self, extend_selection: bool) -> bool {
1036 self.widget.move_to_line_start(extend_selection)
1037 }
1038
1039 #[inline]
1041 pub fn move_to_line_end(&mut self, extend_selection: bool) -> bool {
1042 self.widget.move_to_line_end(extend_selection)
1043 }
1044}
1045
1046impl HasScreenCursor for ColorInputState {
1047 #[inline]
1049 fn screen_cursor(&self) -> Option<(u16, u16)> {
1050 self.widget.screen_cursor()
1051 }
1052}
1053
1054impl RelocatableState for ColorInputState {
1055 fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
1056 self.widget.relocate(shift, clip);
1057 }
1058}
1059
1060impl ColorInputState {
1061 #[inline]
1064 pub fn col_to_screen(&self, pos: upos_type) -> Option<u16> {
1065 self.widget.col_to_screen(pos)
1066 }
1067
1068 #[inline]
1071 pub fn screen_to_col(&self, scx: i16) -> upos_type {
1072 self.widget.screen_to_col(scx)
1073 }
1074
1075 #[inline]
1079 pub fn set_screen_cursor(&mut self, cursor: i16, extend_selection: bool) -> bool {
1080 self.widget.set_screen_cursor(cursor, extend_selection)
1081 }
1082}
1083
1084fn luminance_bt_srgb(color: Color) -> f32 {
1086 let (r, g, b) = color2rgb(color);
1087 0.2126f32 * ((r as f32) / 255f32).powf(2.2f32)
1088 + 0.7152f32 * ((g as f32) / 255f32).powf(2.2f32)
1089 + 0.0722f32 * ((b as f32) / 255f32).powf(2.2f32)
1090}
1091
1092fn contrast_bt_srgb(color: Color, color2: Color) -> f32 {
1094 let lum1 = luminance_bt_srgb(color);
1095 let lum2 = luminance_bt_srgb(color2);
1096 (lum1 - lum2).abs()
1097 }
1101
1102pub fn high_contrast_color(bg: Color, text: &[Color]) -> Style {
1103 let mut color0 = text[0];
1104 let mut color1 = text[0];
1105 let mut contrast1 = contrast_bt_srgb(color1, bg);
1106
1107 for text_color in text {
1108 let test = contrast_bt_srgb(*text_color, bg);
1109 if test > contrast1 {
1110 color0 = color1;
1111 color1 = *text_color;
1112 contrast1 = test;
1113 }
1114 }
1115 _ = color0;
1117
1118 Style::new().bg(bg).fg(color1)
1119}
1120
1121const fn color2rgb(color: Color) -> (u8, u8, u8) {
1124 match color {
1125 Color::Black => (0x00, 0x00, 0x00),
1126 Color::Red => (0xaa, 0x00, 0x00),
1127 Color::Green => (0x00, 0xaa, 0x00),
1128 Color::Yellow => (0xaa, 0x55, 0x00),
1129 Color::Blue => (0x00, 0x00, 0xaa),
1130 Color::Magenta => (0xaa, 0x00, 0xaa),
1131 Color::Cyan => (0x00, 0xaa, 0xaa),
1132 Color::Gray => (0xaa, 0xaa, 0xaa),
1133 Color::DarkGray => (0x55, 0x55, 0x55),
1134 Color::LightRed => (0xff, 0x55, 0x55),
1135 Color::LightGreen => (0x55, 0xff, 0x55),
1136 Color::LightYellow => (0xff, 0xff, 0x55),
1137 Color::LightBlue => (0x55, 0x55, 0xff),
1138 Color::LightMagenta => (0xff, 0x55, 0xff),
1139 Color::LightCyan => (0x55, 0xff, 0xff),
1140 Color::White => (0xff, 0xff, 0xff),
1141 Color::Rgb(r, g, b) => (r, g, b),
1142 Color::Indexed(i) => {
1143 const VGA256: [(u8, u8, u8); 256] = [
1144 (0x00, 0x00, 0x00),
1145 (0x80, 0x00, 0x00),
1146 (0x00, 0x80, 0x00),
1147 (0x80, 0x80, 0x00),
1148 (0x00, 0x00, 0x80),
1149 (0x80, 0x00, 0x80),
1150 (0x00, 0x80, 0x80),
1151 (0xc0, 0xc0, 0xc0),
1152 (0x80, 0x80, 0x80),
1153 (0xff, 0x00, 0x00),
1154 (0x00, 0xff, 0x00),
1155 (0xff, 0xff, 0x00),
1156 (0x00, 0x00, 0xff),
1157 (0xff, 0x00, 0xff),
1158 (0x00, 0xff, 0xff),
1159 (0xff, 0xff, 0xff),
1160 (0x00, 0x00, 0x00),
1161 (0x00, 0x00, 0x5f),
1162 (0x00, 0x00, 0x87),
1163 (0x00, 0x00, 0xaf),
1164 (0x00, 0x00, 0xd7),
1165 (0x00, 0x00, 0xff),
1166 (0x00, 0x5f, 0x00),
1167 (0x00, 0x5f, 0x5f),
1168 (0x00, 0x5f, 0x87),
1169 (0x00, 0x5f, 0xaf),
1170 (0x00, 0x5f, 0xd7),
1171 (0x00, 0x5f, 0xff),
1172 (0x00, 0x87, 0x00),
1173 (0x00, 0x87, 0x5f),
1174 (0x00, 0x87, 0x87),
1175 (0x00, 0x87, 0xaf),
1176 (0x00, 0x87, 0xd7),
1177 (0x00, 0x87, 0xff),
1178 (0x00, 0xaf, 0x00),
1179 (0x00, 0xaf, 0x5f),
1180 (0x00, 0xaf, 0x87),
1181 (0x00, 0xaf, 0xaf),
1182 (0x00, 0xaf, 0xd7),
1183 (0x00, 0xaf, 0xff),
1184 (0x00, 0xd7, 0x00),
1185 (0x00, 0xd7, 0x5f),
1186 (0x00, 0xd7, 0x87),
1187 (0x00, 0xd7, 0xaf),
1188 (0x00, 0xd7, 0xd7),
1189 (0x00, 0xd7, 0xff),
1190 (0x00, 0xff, 0x00),
1191 (0x00, 0xff, 0x5f),
1192 (0x00, 0xff, 0x87),
1193 (0x00, 0xff, 0xaf),
1194 (0x00, 0xff, 0xd7),
1195 (0x00, 0xff, 0xff),
1196 (0x5f, 0x00, 0x00),
1197 (0x5f, 0x00, 0x5f),
1198 (0x5f, 0x00, 0x87),
1199 (0x5f, 0x00, 0xaf),
1200 (0x5f, 0x00, 0xd7),
1201 (0x5f, 0x00, 0xff),
1202 (0x5f, 0x5f, 0x00),
1203 (0x5f, 0x5f, 0x5f),
1204 (0x5f, 0x5f, 0x87),
1205 (0x5f, 0x5f, 0xaf),
1206 (0x5f, 0x5f, 0xd7),
1207 (0x5f, 0x5f, 0xff),
1208 (0x5f, 0x87, 0x00),
1209 (0x5f, 0x87, 0x5f),
1210 (0x5f, 0x87, 0x87),
1211 (0x5f, 0x87, 0xaf),
1212 (0x5f, 0x87, 0xd7),
1213 (0x5f, 0x87, 0xff),
1214 (0x5f, 0xaf, 0x00),
1215 (0x5f, 0xaf, 0x5f),
1216 (0x5f, 0xaf, 0x87),
1217 (0x5f, 0xaf, 0xaf),
1218 (0x5f, 0xaf, 0xd7),
1219 (0x5f, 0xaf, 0xff),
1220 (0x5f, 0xd7, 0x00),
1221 (0x5f, 0xd7, 0x5f),
1222 (0x5f, 0xd7, 0x87),
1223 (0x5f, 0xd7, 0xaf),
1224 (0x5f, 0xd7, 0xd7),
1225 (0x5f, 0xd7, 0xff),
1226 (0x5f, 0xff, 0x00),
1227 (0x5f, 0xff, 0x5f),
1228 (0x5f, 0xff, 0x87),
1229 (0x5f, 0xff, 0xaf),
1230 (0x5f, 0xff, 0xd7),
1231 (0x5f, 0xff, 0xff),
1232 (0x87, 0x00, 0x00),
1233 (0x87, 0x00, 0x5f),
1234 (0x87, 0x00, 0x87),
1235 (0x87, 0x00, 0xaf),
1236 (0x87, 0x00, 0xd7),
1237 (0x87, 0x00, 0xff),
1238 (0x87, 0x5f, 0x00),
1239 (0x87, 0x5f, 0x5f),
1240 (0x87, 0x5f, 0x87),
1241 (0x87, 0x5f, 0xaf),
1242 (0x87, 0x5f, 0xd7),
1243 (0x87, 0x5f, 0xff),
1244 (0x87, 0x87, 0x00),
1245 (0x87, 0x87, 0x5f),
1246 (0x87, 0x87, 0x87),
1247 (0x87, 0x87, 0xaf),
1248 (0x87, 0x87, 0xd7),
1249 (0x87, 0x87, 0xff),
1250 (0x87, 0xaf, 0x00),
1251 (0x87, 0xaf, 0x5f),
1252 (0x87, 0xaf, 0x87),
1253 (0x87, 0xaf, 0xaf),
1254 (0x87, 0xaf, 0xd7),
1255 (0x87, 0xaf, 0xff),
1256 (0x87, 0xd7, 0x00),
1257 (0x87, 0xd7, 0x5f),
1258 (0x87, 0xd7, 0x87),
1259 (0x87, 0xd7, 0xaf),
1260 (0x87, 0xd7, 0xd7),
1261 (0x87, 0xd7, 0xff),
1262 (0x87, 0xff, 0x00),
1263 (0x87, 0xff, 0x5f),
1264 (0x87, 0xff, 0x87),
1265 (0x87, 0xff, 0xaf),
1266 (0x87, 0xff, 0xd7),
1267 (0x87, 0xff, 0xff),
1268 (0xaf, 0x00, 0x00),
1269 (0xaf, 0x00, 0x5f),
1270 (0xaf, 0x00, 0x87),
1271 (0xaf, 0x00, 0xaf),
1272 (0xaf, 0x00, 0xd7),
1273 (0xaf, 0x00, 0xff),
1274 (0xaf, 0x5f, 0x00),
1275 (0xaf, 0x5f, 0x5f),
1276 (0xaf, 0x5f, 0x87),
1277 (0xaf, 0x5f, 0xaf),
1278 (0xaf, 0x5f, 0xd7),
1279 (0xaf, 0x5f, 0xff),
1280 (0xaf, 0x87, 0x00),
1281 (0xaf, 0x87, 0x5f),
1282 (0xaf, 0x87, 0x87),
1283 (0xaf, 0x87, 0xaf),
1284 (0xaf, 0x87, 0xd7),
1285 (0xaf, 0x87, 0xff),
1286 (0xaf, 0xaf, 0x00),
1287 (0xaf, 0xaf, 0x5f),
1288 (0xaf, 0xaf, 0x87),
1289 (0xaf, 0xaf, 0xaf),
1290 (0xaf, 0xaf, 0xd7),
1291 (0xaf, 0xaf, 0xff),
1292 (0xaf, 0xd7, 0x00),
1293 (0xaf, 0xd7, 0x5f),
1294 (0xaf, 0xd7, 0x87),
1295 (0xaf, 0xd7, 0xaf),
1296 (0xaf, 0xd7, 0xd7),
1297 (0xaf, 0xd7, 0xff),
1298 (0xaf, 0xff, 0x00),
1299 (0xaf, 0xff, 0x5f),
1300 (0xaf, 0xff, 0x87),
1301 (0xaf, 0xff, 0xaf),
1302 (0xaf, 0xff, 0xd7),
1303 (0xaf, 0xff, 0xff),
1304 (0xd7, 0x00, 0x00),
1305 (0xd7, 0x00, 0x5f),
1306 (0xd7, 0x00, 0x87),
1307 (0xd7, 0x00, 0xaf),
1308 (0xd7, 0x00, 0xd7),
1309 (0xd7, 0x00, 0xff),
1310 (0xd7, 0x5f, 0x00),
1311 (0xd7, 0x5f, 0x5f),
1312 (0xd7, 0x5f, 0x87),
1313 (0xd7, 0x5f, 0xaf),
1314 (0xd7, 0x5f, 0xd7),
1315 (0xd7, 0x5f, 0xff),
1316 (0xd7, 0x87, 0x00),
1317 (0xd7, 0x87, 0x5f),
1318 (0xd7, 0x87, 0x87),
1319 (0xd7, 0x87, 0xaf),
1320 (0xd7, 0x87, 0xd7),
1321 (0xd7, 0x87, 0xff),
1322 (0xd7, 0xaf, 0x00),
1323 (0xd7, 0xaf, 0x5f),
1324 (0xd7, 0xaf, 0x87),
1325 (0xd7, 0xaf, 0xaf),
1326 (0xd7, 0xaf, 0xd7),
1327 (0xd7, 0xaf, 0xff),
1328 (0xd7, 0xd7, 0x00),
1329 (0xd7, 0xd7, 0x5f),
1330 (0xd7, 0xd7, 0x87),
1331 (0xd7, 0xd7, 0xaf),
1332 (0xd7, 0xd7, 0xd7),
1333 (0xd7, 0xd7, 0xff),
1334 (0xd7, 0xff, 0x00),
1335 (0xd7, 0xff, 0x5f),
1336 (0xd7, 0xff, 0x87),
1337 (0xd7, 0xff, 0xaf),
1338 (0xd7, 0xff, 0xd7),
1339 (0xd7, 0xff, 0xff),
1340 (0xff, 0x00, 0x00),
1341 (0xff, 0x00, 0x5f),
1342 (0xff, 0x00, 0x87),
1343 (0xff, 0x00, 0xaf),
1344 (0xff, 0x00, 0xd7),
1345 (0xff, 0x00, 0xff),
1346 (0xff, 0x5f, 0x00),
1347 (0xff, 0x5f, 0x5f),
1348 (0xff, 0x5f, 0x87),
1349 (0xff, 0x5f, 0xaf),
1350 (0xff, 0x5f, 0xd7),
1351 (0xff, 0x5f, 0xff),
1352 (0xff, 0x87, 0x00),
1353 (0xff, 0x87, 0x5f),
1354 (0xff, 0x87, 0x87),
1355 (0xff, 0x87, 0xaf),
1356 (0xff, 0x87, 0xd7),
1357 (0xff, 0x87, 0xff),
1358 (0xff, 0xaf, 0x00),
1359 (0xff, 0xaf, 0x5f),
1360 (0xff, 0xaf, 0x87),
1361 (0xff, 0xaf, 0xaf),
1362 (0xff, 0xaf, 0xd7),
1363 (0xff, 0xaf, 0xff),
1364 (0xff, 0xd7, 0x00),
1365 (0xff, 0xd7, 0x5f),
1366 (0xff, 0xd7, 0x87),
1367 (0xff, 0xd7, 0xaf),
1368 (0xff, 0xd7, 0xd7),
1369 (0xff, 0xd7, 0xff),
1370 (0xff, 0xff, 0x00),
1371 (0xff, 0xff, 0x5f),
1372 (0xff, 0xff, 0x87),
1373 (0xff, 0xff, 0xaf),
1374 (0xff, 0xff, 0xd7),
1375 (0xff, 0xff, 0xff),
1376 (0x08, 0x08, 0x08),
1377 (0x12, 0x12, 0x12),
1378 (0x1c, 0x1c, 0x1c),
1379 (0x26, 0x26, 0x26),
1380 (0x30, 0x30, 0x30),
1381 (0x3a, 0x3a, 0x3a),
1382 (0x44, 0x44, 0x44),
1383 (0x4e, 0x4e, 0x4e),
1384 (0x58, 0x58, 0x58),
1385 (0x62, 0x62, 0x62),
1386 (0x6c, 0x6c, 0x6c),
1387 (0x76, 0x76, 0x76),
1388 (0x80, 0x80, 0x80),
1389 (0x8a, 0x8a, 0x8a),
1390 (0x94, 0x94, 0x94),
1391 (0x9e, 0x9e, 0x9e),
1392 (0xa8, 0xa8, 0xa8),
1393 (0xb2, 0xb2, 0xb2),
1394 (0xbc, 0xbc, 0xbc),
1395 (0xc6, 0xc6, 0xc6),
1396 (0xd0, 0xd0, 0xd0),
1397 (0xda, 0xda, 0xda),
1398 (0xe4, 0xe4, 0xe4),
1399 (0xee, 0xee, 0xee),
1400 ];
1401 VGA256[i as usize]
1402 }
1403 Color::Reset => (0, 0, 0),
1404 }
1405}
1406
1407impl HandleEvent<crossterm::event::Event, Regular, TextOutcome> for ColorInputState {
1410 fn handle(&mut self, event: &crossterm::event::Event, _keymap: Regular) -> TextOutcome {
1411 flow!(match event {
1412 ct_event!(key press '+') => {
1413 self.increment(1);
1414 TextOutcome::Changed
1415 }
1416 ct_event!(key press '-') => {
1417 self.decrement(1);
1418 TextOutcome::Changed
1419 }
1420 ct_event!(key press ALT-'+') => {
1421 self.increment(15);
1422 TextOutcome::Changed
1423 }
1424 ct_event!(key press ALT-'-') => {
1425 self.decrement(15);
1426 TextOutcome::Changed
1427 }
1428 ct_event!(key press CONTROL-'v') => {
1429 if self.paste_from_clip() {
1430 TextOutcome::Changed
1431 } else {
1432 TextOutcome::Continue
1433 }
1434 }
1435 ct_event!(key press CONTROL-'c') => {
1436 if self.copy_to_clip() {
1437 TextOutcome::Changed
1438 } else {
1439 TextOutcome::Continue
1440 }
1441 }
1442 ct_event!(key press CONTROL-'x') => {
1443 if self.cut_to_clip() {
1444 TextOutcome::Changed
1445 } else {
1446 TextOutcome::Continue
1447 }
1448 }
1449 _ => TextOutcome::Continue,
1450 });
1451 if !self.disable_modes {
1452 flow!(match event {
1453 ct_event!(key press 'r') => {
1454 self.set_mode(Mode::RGB);
1455 TextOutcome::Changed
1456 }
1457 ct_event!(key press 'h') => {
1458 self.set_mode(Mode::HSV);
1459 TextOutcome::Changed
1460 }
1461 ct_event!(key press 'x') => {
1462 self.set_mode(Mode::HEX);
1463 TextOutcome::Changed
1464 }
1465 ct_event!(key press 'm') | ct_event!(keycode press Up) => {
1466 self.next_mode();
1467 TextOutcome::Changed
1468 }
1469 ct_event!(key press SHIFT-'M') | ct_event!(keycode press Down) => {
1470 self.prev_mode();
1471 TextOutcome::Changed
1472 }
1473 _ => TextOutcome::Continue,
1474 });
1475 }
1476 match self.widget.handle(event, Regular) {
1477 TextOutcome::TextChanged => {
1478 self.normalize();
1479 self.value = self.parse_value();
1480 TextOutcome::TextChanged
1481 }
1482 r => r,
1483 }
1484 }
1485}
1486
1487impl HandleEvent<crossterm::event::Event, ReadOnly, TextOutcome> for ColorInputState {
1488 fn handle(&mut self, event: &crossterm::event::Event, _keymap: ReadOnly) -> TextOutcome {
1489 self.widget.handle(event, ReadOnly)
1490 }
1491}
1492
1493impl HandleEvent<crossterm::event::Event, MouseOnly, TextOutcome> for ColorInputState {
1494 fn handle(&mut self, event: &crossterm::event::Event, _keymap: MouseOnly) -> TextOutcome {
1495 self.widget.handle(event, MouseOnly)
1496 }
1497}
1498
1499pub fn handle_events(
1503 state: &mut ColorInputState,
1504 focus: bool,
1505 event: &crossterm::event::Event,
1506) -> TextOutcome {
1507 state.widget.focus.set(focus);
1508 HandleEvent::handle(state, event, Regular)
1509}
1510
1511pub fn handle_readonly_events(
1515 state: &mut ColorInputState,
1516 focus: bool,
1517 event: &crossterm::event::Event,
1518) -> TextOutcome {
1519 state.widget.focus.set(focus);
1520 state.handle(event, ReadOnly)
1521}
1522
1523pub fn handle_mouse_events(
1525 state: &mut ColorInputState,
1526 event: &crossterm::event::Event,
1527) -> TextOutcome {
1528 HandleEvent::handle(state, event, MouseOnly)
1529}