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