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 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, Clone)]
93pub struct ColorInputState {
94 pub area: Rect,
97 pub inner: 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> 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_focus_lost(TextFocusLost::Position0);
131 z
132 }
133}
134
135impl<'a> ColorInput<'a> {
136 pub fn new() -> Self {
137 Self::default()
138 }
139
140 #[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 #[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 #[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 #[inline]
181 pub fn disable_modes(mut self) -> Self {
182 self.disable_modes = true;
183 self
184 }
185
186 #[inline]
188 pub fn mode(mut self, mode: Mode) -> Self {
189 self.mode = Some(mode);
190 self
191 }
192
193 #[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 #[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 #[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 #[inline]
216 pub fn block(mut self, block: Block<'a>) -> Self {
217 self.block = Some(block);
218 self
219 }
220
221 #[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 #[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 #[inline]
237 pub fn on_tab(mut self, of: TextTab) -> Self {
238 self.widget = self.widget.on_tab(of);
239 self
240 }
241
242 pub fn width(&self) -> u16 {
244 16
245 }
246
247 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 #[inline]
360 pub fn set_overwrite(&mut self, overwrite: bool) {
361 self.widget.set_overwrite(overwrite);
362 }
363
364 #[inline]
366 pub fn overwrite(&self) -> bool {
367 self.widget.overwrite()
368 }
369}
370
371impl ColorInputState {
372 #[inline]
375 pub fn set_clipboard(&mut self, clip: Option<impl Clipboard + 'static>) {
376 self.widget.set_clipboard(clip);
377 }
378
379 #[inline]
382 pub fn clipboard(&self) -> Option<&dyn Clipboard> {
383 self.widget.clipboard()
384 }
385
386 #[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 #[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 #[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 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 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 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 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 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 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 #[inline]
484 pub fn set_undo_buffer(&mut self, undo: Option<impl UndoBuffer + 'static>) {
485 self.widget.set_undo_buffer(undo);
486 }
487
488 #[inline]
490 pub fn undo_buffer(&self) -> Option<&dyn UndoBuffer> {
491 self.widget.undo_buffer()
492 }
493
494 #[inline]
496 pub fn undo_buffer_mut(&mut self) -> Option<&mut dyn UndoBuffer> {
497 self.widget.undo_buffer_mut()
498 }
499
500 #[inline]
502 pub fn recent_replay_log(&mut self) -> Vec<UndoEntry> {
503 self.widget.recent_replay_log()
504 }
505
506 #[inline]
508 pub fn replay_log(&mut self, replay: &[UndoEntry]) {
509 self.widget.replay_log(replay)
510 }
511
512 #[inline]
514 pub fn undo(&mut self) -> bool {
515 self.widget.undo()
516 }
517
518 #[inline]
520 pub fn redo(&mut self) -> bool {
521 self.widget.redo()
522 }
523}
524
525impl ColorInputState {
526 #[inline]
528 pub fn set_styles(&mut self, styles: Vec<(Range<usize>, usize)>) {
529 self.widget.set_styles(styles);
530 }
531
532 #[inline]
534 pub fn add_style(&mut self, range: Range<usize>, style: usize) {
535 self.widget.add_style(range, style);
536 }
537
538 #[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 #[inline]
551 pub fn remove_style(&mut self, range: Range<usize>, style: usize) {
552 self.widget.remove_style(range, style);
553 }
554
555 #[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 pub fn styles_in(&self, range: Range<usize>, buf: &mut Vec<(Range<usize>, usize)>) {
567 self.widget.styles_in(range, buf)
568 }
569
570 #[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 #[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 #[inline]
585 pub fn styles(&self) -> Option<impl Iterator<Item = (Range<usize>, usize)> + '_> {
586 self.widget.styles()
587 }
588}
589
590impl ColorInputState {
591 #[inline]
593 pub fn offset(&self) -> upos_type {
594 self.widget.offset()
595 }
596
597 #[inline]
599 pub fn set_offset(&mut self, offset: upos_type) {
600 self.widget.set_offset(offset)
601 }
602
603 #[inline]
605 pub fn cursor(&self) -> upos_type {
606 self.widget.cursor()
607 }
608
609 #[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 #[inline]
617 pub fn set_default_cursor(&mut self) {
618 self.widget.set_default_cursor()
619 }
620
621 #[inline]
623 pub fn anchor(&self) -> upos_type {
624 self.widget.anchor()
625 }
626
627 #[inline]
629 pub fn has_selection(&self) -> bool {
630 self.widget.has_selection()
631 }
632
633 #[inline]
635 pub fn selection(&self) -> Range<upos_type> {
636 self.widget.selection()
637 }
638
639 #[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 #[inline]
647 pub fn select_all(&mut self) {
648 self.widget.select_all();
649 }
650
651 #[inline]
653 pub fn selected_text(&self) -> &str {
654 self.widget.selected_text()
655 }
656}
657
658impl ColorInputState {
659 #[inline]
661 pub fn is_empty(&self) -> bool {
662 self.widget.is_empty()
663 }
664
665 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 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 #[inline]
716 pub fn len(&self) -> upos_type {
717 self.widget.len()
718 }
719
720 #[inline]
722 pub fn line_width(&self) -> upos_type {
723 self.widget.line_width()
724 }
725}
726
727impl ColorInputState {
728 #[inline]
730 pub fn clear(&mut self) {
731 self.widget.clear();
732 }
733
734 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 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 #[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 #[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 #[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 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 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 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 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 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 self.widget.clear_styles();
949 self.widget.add_range_style(0..6, 1).expect("fine");
950 }
951 Mode::HSV => {
952 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 pub fn mode(&self) -> Mode {
963 self.mode
964 }
965
966 pub fn set_mode(&mut self, mode: Mode) -> bool {
968 self.mode = mode;
969 match self.mode {
970 Mode::RGB => {
971 self.widget.set_mask(PAT_RGB).expect("valid-mask");
973 }
974 Mode::HEX => {
975 self.widget.set_mask(PAT_HEX).expect("valid-mask");
977 }
978 Mode::HSV => {
979 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 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 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 #[inline]
1010 pub fn move_right(&mut self, extend_selection: bool) -> bool {
1011 self.widget.move_right(extend_selection)
1012 }
1013
1014 #[inline]
1016 pub fn move_left(&mut self, extend_selection: bool) -> bool {
1017 self.widget.move_left(extend_selection)
1018 }
1019
1020 #[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 #[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 #[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 #[inline]
1055 pub fn col_to_screen(&self, pos: upos_type) -> Option<u16> {
1056 self.widget.col_to_screen(pos)
1057 }
1058
1059 #[inline]
1062 pub fn screen_to_col(&self, scx: i16) -> upos_type {
1063 self.widget.screen_to_col(scx)
1064 }
1065
1066 #[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
1075fn 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
1083fn 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 }
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 _ = color0;
1108
1109 Style::new().bg(bg).fg(color1)
1110}
1111
1112const 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
1398impl 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
1493pub 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
1505pub 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
1517pub fn handle_mouse_events(
1519 state: &mut ColorInputState,
1520 event: &crossterm::event::Event,
1521) -> TextOutcome {
1522 HandleEvent::handle(state, event, MouseOnly)
1523}