1use crate::_private::NonExhaustive;
24use palette::{FromColor, Hsv, Srgb};
25use rat_event::{HandleEvent, MouseOnly, Regular, ct_event, flow};
26use rat_focus::{FocusBuilder, FocusFlag, HasFocus};
27use rat_reloc::RelocatableState;
28use rat_text::clipboard::Clipboard;
29use rat_text::event::{ReadOnly, TextOutcome};
30use rat_text::text_input_mask::{MaskedInput, MaskedInputState};
31use rat_text::{
32 TextError, TextFocusGained, TextFocusLost, TextStyle, TextTab, derive_text_widget_state,
33 upos_type,
34};
35use ratatui_core::buffer::Buffer;
36use ratatui_core::layout::Rect;
37use ratatui_core::style::{Color, Style};
38use ratatui_core::text::Line;
39use ratatui_core::widgets::{StatefulWidget, Widget};
40use ratatui_crossterm::crossterm::event::Event;
41use ratatui_widgets::block::{Block, BlockExt};
42use std::cmp::min;
43use std::ops::Range;
44
45#[derive(Debug, Clone)]
50pub struct ColorInput<'a> {
51 style: Style,
52 block: Option<Block<'a>>,
53
54 disable_modes: bool,
55 mode: Option<Mode>,
56
57 widget: MaskedInput<'a>,
58}
59
60#[derive(Debug)]
62pub struct ColorInputStyle {
63 pub text: TextStyle,
65 pub field_style: Option<Style>,
67 pub disable_modes: Option<bool>,
69 pub mode: Option<Mode>,
71 pub non_exhaustive: NonExhaustive,
73}
74
75impl Default for ColorInputStyle {
76 fn default() -> Self {
77 Self {
78 text: Default::default(),
79 field_style: Default::default(),
80 disable_modes: Default::default(),
81 mode: Default::default(),
82 non_exhaustive: NonExhaustive,
83 }
84 }
85}
86
87#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
89pub enum Mode {
90 #[default]
91 RGB,
92 HEX,
93 HSV,
94}
95
96#[derive(Debug, Clone)]
98pub struct ColorInputState {
99 pub area: Rect,
102 pub inner: Rect,
104 pub mode_area: Rect,
107 pub label_area: Rect,
110
111 value: (f32, f32, f32),
113
114 disable_modes: bool,
117 mode: Mode,
119 pub widget: MaskedInputState,
121
122 pub non_exhaustive: NonExhaustive,
123}
124
125impl<'a> Default for ColorInput<'a> {
126 fn default() -> Self {
127 let mut z = Self {
128 style: Default::default(),
129 disable_modes: Default::default(),
130 mode: Default::default(),
131 block: Default::default(),
132 widget: MaskedInput::default(),
133 };
134 z.widget = z.widget.on_focus_lost(TextFocusLost::Position0);
135 z
136 }
137}
138
139impl<'a> ColorInput<'a> {
142 pub fn new() -> Self {
143 Self::default()
144 }
145
146 #[inline]
148 pub fn styles(mut self, mut style: ColorInputStyle) -> Self {
149 self.style = style.text.style;
150 if let Some(block) = style.text.block.take() {
151 self.block = Some(block);
152 }
153 if let Some(border_style) = style.text.border_style {
154 self.block = self.block.map(|v| v.style(border_style));
155 }
156 if let Some(title_style) = style.text.title_style {
157 self.block = self.block.map(|v| v.style(title_style));
158 }
159 self.block = self.block.map(|v| v.style(self.style));
160 self.widget = self.widget.styles(style.text);
161 if let Some(disable_modes) = style.disable_modes {
162 self.disable_modes = disable_modes;
163 }
164 if let Some(mode) = style.mode {
165 self.mode = Some(mode);
166 }
167 if let Some(field_style) = style.field_style {
168 self.widget = self.widget.text_style_idx(1, field_style);
169 }
170 self
171 }
172
173 #[inline]
175 pub fn style(mut self, style: impl Into<Style>) -> Self {
176 let style = style.into();
177 self.style = style;
178 self.block = self.block.map(|v| v.style(style));
179 self.widget = self.widget.style(style);
180 self
181 }
182
183 #[inline]
185 pub fn field_style(mut self, style: impl Into<Style>) -> Self {
186 self.widget = self.widget.text_style_idx(1, style.into());
187 self
188 }
189
190 #[inline]
192 pub fn disable_modes(mut self) -> Self {
193 self.disable_modes = true;
194 self
195 }
196
197 #[inline]
199 pub fn mode(mut self, mode: Mode) -> Self {
200 self.mode = Some(mode);
201 self
202 }
203
204 #[inline]
206 pub fn focus_style(mut self, style: impl Into<Style>) -> Self {
207 self.widget = self.widget.focus_style(style);
208 self
209 }
210
211 #[inline]
213 pub fn select_style(mut self, style: impl Into<Style>) -> Self {
214 self.widget = self.widget.select_style(style);
215 self
216 }
217
218 #[inline]
220 pub fn invalid_style(mut self, style: impl Into<Style>) -> Self {
221 self.widget = self.widget.invalid_style(style);
222 self
223 }
224
225 #[inline]
227 pub fn block(mut self, block: Block<'a>) -> Self {
228 self.block = Some(block);
229 self
230 }
231
232 #[inline]
234 pub fn on_focus_gained(mut self, of: TextFocusGained) -> Self {
235 self.widget = self.widget.on_focus_gained(of);
236 self
237 }
238
239 #[inline]
241 pub fn on_focus_lost(mut self, of: TextFocusLost) -> Self {
242 self.widget = self.widget.on_focus_lost(of);
243 self
244 }
245
246 #[inline]
248 pub fn on_tab(mut self, of: TextTab) -> Self {
249 self.widget = self.widget.on_tab(of);
250 self
251 }
252
253 pub fn width(&self) -> u16 {
255 16
256 }
257
258 pub fn height(&self) -> u16 {
260 1
261 }
262}
263
264impl<'a> StatefulWidget for &ColorInput<'a> {
265 type State = ColorInputState;
266
267 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
268 render(self, area, buf, state);
269 }
270}
271
272impl StatefulWidget for ColorInput<'_> {
273 type State = ColorInputState;
274
275 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
276 render(&self, area, buf, state);
277 }
278}
279
280fn render(widget: &ColorInput<'_>, area: Rect, buf: &mut Buffer, state: &mut ColorInputState) {
281 state.disable_modes = widget.disable_modes;
282 if let Some(mode) = widget.mode {
283 state.mode = mode;
284 }
285
286 let inner = widget.block.inner_if_some(area);
287
288 let mode_area = Rect::new(inner.x, inner.y, 4, inner.height);
289 let mode_label = Rect::new(mode_area.x, mode_area.y + mode_area.height / 2, 4, 1);
290 let widget_area = Rect::new(
291 inner.x + mode_area.width,
292 inner.y,
293 inner.width.saturating_sub(mode_area.width),
294 inner.height,
295 );
296
297 state.area = area;
298 state.inner = inner;
299 state.mode_area = mode_area;
300 state.label_area = mode_label;
301
302 let bg = state.value();
303 let fg_colors = [Color::Black, Color::White];
304 let style = high_contrast_color(bg, &fg_colors);
305
306 widget.block.clone().render(area, buf);
307
308 buf.set_style(mode_area, style);
309 let mode_str = match state.mode {
310 Mode::RGB => "RGB",
311 Mode::HEX => " #",
312 Mode::HSV => "HSV",
313 };
314 Line::from(mode_str).render(mode_label, buf);
315
316 (&widget.widget).render(widget_area, buf, &mut state.widget);
317}
318
319derive_text_widget_state!( BASE ColorInputState );
320derive_text_widget_state!( UNDO ColorInputState );
322derive_text_widget_state!( STYLE ColorInputState );
323derive_text_widget_state!( OFFSET ColorInputState );
324derive_text_widget_state!( SCREENCURSOR ColorInputState );
327impl Default for ColorInputState {
330 fn default() -> Self {
331 let mut z = Self {
332 area: Default::default(),
333 inner: Default::default(),
334 mode_area: Default::default(),
335 label_area: Default::default(),
336 value: Default::default(),
337 disable_modes: Default::default(),
338 mode: Default::default(),
339 widget: Default::default(),
340 non_exhaustive: NonExhaustive,
341 };
342 z.set_mode(Mode::RGB);
343 z
344 }
345}
346
347impl HasFocus for ColorInputState {
348 fn build(&self, builder: &mut FocusBuilder) {
349 builder.leaf_with_flags(
350 self.widget.focus(),
351 self.area,
352 self.widget.area_z(),
353 self.widget.navigable(),
354 );
355 }
356
357 fn focus(&self) -> FocusFlag {
358 self.widget.focus()
359 }
360
361 fn area(&self) -> Rect {
362 self.area
363 }
364}
365
366impl ColorInputState {
367 pub fn new() -> Self {
368 Self::default()
369 }
370
371 pub fn named(name: &str) -> Self {
372 let mut z = Self::default();
373 z.widget.focus = z.widget.focus.with_name(name);
374 z
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 if clip.set_string(&value_str).is_ok() {
431 self.clear()
432 }
433 true
434 }
435 }
436
437 #[inline]
439 pub fn paste_from_clip(&mut self) -> bool {
440 let Some(clip) = self.widget.value.clipboard() else {
441 return false;
442 };
443
444 if let Ok(text) = clip.get_string() {
445 if text.starts_with("#") && text.len() == 7 {
446 if let Ok(v) = u32::from_str_radix(&text[1..7], 16) {
448 self.set_value_u32(v);
449 }
450 } else if text.starts_with("#") && text.len() == 9 {
451 if let Ok(v) = u32::from_str_radix(&text[1..7], 16) {
453 self.set_value_u32(v);
454 }
455 } else if text.starts_with("0x") && text.len() == 8 {
456 if let Ok(v) = u32::from_str_radix(&text[2..8], 16) {
458 self.set_value_u32(v);
459 }
460 } else if text.starts_with("0x") && text.len() == 10 {
461 if let Ok(v) = u32::from_str_radix(&text[2..8], 16) {
463 self.set_value_u32(v);
464 }
465 } else if text.len() == 6 {
466 if let Ok(v) = u32::from_str_radix(&text[0..6], 16) {
468 self.set_value_u32(v);
469 }
470 } else if text.len() == 8 {
471 if let Ok(v) = u32::from_str_radix(&text[0..6], 16) {
473 self.set_value_u32(v);
474 }
475 } else {
476 for c in text.chars() {
477 self.widget.insert_char(c);
478 }
479 }
480 true
481 } else {
482 false
483 }
484 }
485}
486
487impl ColorInputState {
488 pub fn value(&self) -> Color {
490 Color::Rgb(
491 (self.value.0 * 255f32) as u8,
492 (self.value.1 * 255f32) as u8,
493 (self.value.2 * 255f32) as u8,
494 )
495 }
496
497 pub fn value_f32(&self) -> (f32, f32, f32) {
499 self.value
500 }
501
502 pub fn value_u32(&self) -> u32 {
504 (((self.value.0 * 255f32) as u32) << 16)
505 + (((self.value.1 * 255f32) as u32) << 8)
506 + ((self.value.2 * 255f32) as u32)
507 }
508
509 fn parse_value(&self) -> (f32, f32, f32) {
510 match self.mode {
511 Mode::RGB => {
512 let r = self.widget.section_value::<f32>(SEC_R).unwrap_or_default();
513 let g = self.widget.section_value::<f32>(SEC_G).unwrap_or_default();
514 let b = self.widget.section_value::<f32>(SEC_B).unwrap_or_default();
515 (r / 255f32, g / 255f32, b / 255f32)
516 }
517 Mode::HEX => {
518 let v = u32::from_str_radix(self.widget.section_text(1), 16).expect("hex");
519 let r = ((v >> 16) & 255) as f32;
520 let g = ((v >> 8) & 255) as f32;
521 let b = (v & 255) as f32;
522 (r / 255f32, g / 255f32, b / 255f32)
523 }
524 Mode::HSV => {
525 let h = self.widget.section_value::<f32>(SEC_H).unwrap_or_default();
526 let s = self.widget.section_value::<f32>(SEC_S).unwrap_or_default();
527 let v = self.widget.section_value::<f32>(SEC_V).unwrap_or_default();
528
529 let h = palette::RgbHue::from_degrees(h);
530 let s = s / 100f32;
531 let v = v / 100f32;
532
533 let hsv = Hsv::from_components((h, s, v));
534 let rgb = Srgb::from_color(hsv);
535
536 rgb.into_components()
537 }
538 }
539 }
540}
541
542impl ColorInputState {
543 #[inline]
545 pub fn clear(&mut self) {
546 self.widget.clear();
547 }
548
549 pub fn set_value_f32(&mut self, color: (f32, f32, f32)) {
551 self.value = color;
552 self.value_to_text();
553 }
554
555 pub fn set_value_u32(&mut self, color: u32) {
557 let r = ((color >> 16) & 255) as f32;
558 let g = ((color >> 8) & 255) as f32;
559 let b = (color & 255) as f32;
560 self.value = (r / 255f32, g / 255f32, b / 255f32);
561 self.value_to_text();
562 }
563
564 pub fn set_value(&mut self, color: Color) {
566 let (r, g, b) = color2rgb(color);
567 self.value = (r as f32 / 255f32, g as f32 / 255f32, b as f32 / 255f32);
568 self.value_to_text();
569 }
570
571 fn value_to_text(&mut self) {
572 match self.mode {
573 Mode::RGB => {
574 let r = (self.value.0 * 255f32) as u8;
575 let g = (self.value.1 * 255f32) as u8;
576 let b = (self.value.2 * 255f32) as u8;
577 let value_str = format!("{:3} {:3} {:3}", r, g, b);
578 self.widget.set_text(value_str);
579 self.set_mode_styles();
580 }
581 Mode::HEX => {
582 let r = (self.value.0 * 255f32) as u32;
583 let g = (self.value.1 * 255f32) as u32;
584 let b = (self.value.2 * 255f32) as u32;
585 let value_str = format!("{:06x}", (r << 16) + (g << 8) + b);
586 self.widget.set_text(value_str);
587 self.set_mode_styles();
588 }
589 Mode::HSV => {
590 let r = self.value.0;
591 let g = self.value.1;
592 let b = self.value.2;
593 let srgb = Srgb::new(r, g, b);
594 let hsv = Hsv::from_color(srgb);
595 let (h, s, v) = hsv.into_components();
596 let h = h.into_positive_degrees() as u32;
597 let s = (s * 100f32) as u32;
598 let v = (v * 100f32) as u32;
599 let value_str = format!("{:3} {:3} {:3}", h, s, v);
600 self.widget.set_text(value_str);
601 self.set_mode_styles();
602 }
603 }
604 }
605
606 #[inline]
608 pub fn insert_char(&mut self, c: char) -> bool {
609 let r = self.widget.insert_char(c);
610 self.normalize();
611 r
612 }
613
614 #[inline]
617 pub fn delete_range(&mut self, range: Range<upos_type>) -> bool {
618 let r = self.widget.delete_range(range);
619 self.normalize();
620 r
621 }
622
623 #[inline]
626 pub fn try_delete_range(&mut self, range: Range<upos_type>) -> Result<bool, TextError> {
627 let r = self.widget.try_delete_range(range);
628 self.normalize();
629 r
630 }
631}
632
633const PAT_RGB: &str = "##0 ##0 ##0";
634const SEC_R: u16 = 1;
635const SEC_G: u16 = 3;
636const SEC_B: u16 = 5;
637
638const PAT_HEX: &str = "HHHHHH";
639#[allow(dead_code)]
640const SEC_X: u16 = 1;
641
642const PAT_HSV: &str = "##0 ##0 ##0";
643const SEC_H: u16 = 1;
644const SEC_S: u16 = 3;
645const SEC_V: u16 = 5;
646
647impl ColorInputState {
648 fn clamp_section(&mut self, section: u16, clamp: u32) -> bool {
649 let r = self
650 .widget
651 .section_value::<u32>(section)
652 .unwrap_or_default();
653 let r_min = min(r, clamp);
654 if r_min != r {
655 self.widget.set_section_value(section, r_min);
656 true
657 } else {
658 false
659 }
660 }
661
662 fn normalize(&mut self) -> bool {
664 let r = match self.mode {
665 Mode::RGB => {
666 self.clamp_section(SEC_R, 255)
667 || self.clamp_section(SEC_G, 255)
668 || self.clamp_section(SEC_B, 255)
669 }
670 Mode::HEX => {
671 false
673 }
674 Mode::HSV => {
675 self.clamp_section(SEC_H, 360)
676 || self.clamp_section(SEC_S, 100)
677 || self.clamp_section(SEC_V, 100)
678 }
679 };
680 self.set_mode_styles();
681 r
682 }
683
684 pub fn change_section(&mut self, n: i32) -> bool {
686 self.change_section_pos(self.cursor(), n)
687 }
688
689 pub fn change_section_pos(&mut self, pos: upos_type, n: i32) -> bool {
690 let section = self.widget.section_id(pos);
691 let r = match self.mode {
692 Mode::RGB => match section {
693 SEC_R | SEC_G | SEC_B => {
694 let r = self
695 .widget
696 .section_value::<u32>(section)
697 .unwrap_or_default();
698 let r_min = min(r.saturating_add_signed(n), 255);
699 if r_min != r {
700 self.widget.set_section_value(section, r_min);
701 self.set_mode_styles();
702 true
703 } else {
704 false
705 }
706 }
707 _ => false,
708 },
709 Mode::HEX => {
710 false
712 }
713 Mode::HSV => match section {
714 SEC_H => {
715 let r = self
716 .widget
717 .section_value::<u32>(section)
718 .unwrap_or_default();
719 let r_min = {
720 let mut r_min = (r as i32 + n) % 360;
721 if r_min < 0 {
722 r_min += 360;
723 }
724 r_min as u32
725 };
726 if r_min != r {
727 self.widget.set_section_value(section, r_min);
728 self.set_mode_styles();
729 true
730 } else {
731 false
732 }
733 }
734 SEC_S | SEC_V => {
735 let r = self
736 .widget
737 .section_value::<u32>(section)
738 .unwrap_or_default();
739 let r_min = min(r.saturating_add_signed(n), 100);
740 if r_min != r {
741 self.widget.set_section_value(section, r_min);
742 self.set_mode_styles();
743 true
744 } else {
745 false
746 }
747 }
748 _ => false,
749 },
750 };
751
752 if r {
753 self.value = self.parse_value();
754 }
755 r
756 }
757
758 fn set_mode_styles(&mut self) {
759 match self.mode {
760 Mode::RGB => {
761 self.widget.clear_styles();
763 self.widget.add_range_style(0..3, 1).expect("fine");
764 self.widget.add_range_style(4..7, 1).expect("fine");
765 self.widget.add_range_style(8..11, 1).expect("fine");
766 }
767 Mode::HEX => {
768 self.widget.clear_styles();
770 self.widget.add_range_style(0..6, 1).expect("fine");
771 }
772 Mode::HSV => {
773 self.widget.clear_styles();
775 self.widget.add_range_style(0..3, 1).expect("fine");
776 self.widget.add_range_style(4..7, 1).expect("fine");
777 self.widget.add_range_style(8..11, 1).expect("fine");
778 }
779 }
780 }
781
782 pub fn mode(&self) -> Mode {
784 self.mode
785 }
786
787 pub fn set_mode(&mut self, mode: Mode) -> bool {
789 self.mode = mode;
790 match self.mode {
791 Mode::RGB => {
792 self.widget.set_mask(PAT_RGB).expect("valid-mask");
794 }
795 Mode::HEX => {
796 self.widget.set_mask(PAT_HEX).expect("valid-mask");
798 }
799 Mode::HSV => {
800 self.widget.set_mask(PAT_HSV).expect("valid-mask");
802 }
803 }
804
805 self.set_mode_styles();
806 self.value_to_text();
807 self.widget.set_default_cursor();
808 true
809 }
810
811 pub fn next_mode(&mut self) -> bool {
813 match self.mode {
814 Mode::RGB => self.set_mode(Mode::HEX),
815 Mode::HEX => self.set_mode(Mode::HSV),
816 Mode::HSV => self.set_mode(Mode::RGB),
817 }
818 }
819
820 pub fn prev_mode(&mut self) -> bool {
822 match self.mode {
823 Mode::RGB => self.set_mode(Mode::HSV),
824 Mode::HEX => self.set_mode(Mode::RGB),
825 Mode::HSV => self.set_mode(Mode::HEX),
826 }
827 }
828
829 #[inline]
831 pub fn move_right(&mut self, extend_selection: bool) -> bool {
832 self.widget.move_right(extend_selection)
833 }
834
835 #[inline]
837 pub fn move_left(&mut self, extend_selection: bool) -> bool {
838 self.widget.move_left(extend_selection)
839 }
840
841 #[inline]
843 pub fn move_to_line_start(&mut self, extend_selection: bool) -> bool {
844 self.widget.move_to_line_start(extend_selection)
845 }
846
847 #[inline]
849 pub fn move_to_line_end(&mut self, extend_selection: bool) -> bool {
850 self.widget.move_to_line_end(extend_selection)
851 }
852}
853
854impl RelocatableState for ColorInputState {
855 fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
856 self.area.relocate(shift, clip);
857 self.inner.relocate(shift, clip);
858 self.mode_area.relocate(shift, clip);
859 self.label_area.relocate(shift, clip);
860 self.widget.relocate(shift, clip);
861 }
862}
863
864fn luminance_bt_srgb(color: Color) -> f32 {
866 let (r, g, b) = color2rgb(color);
867 0.2126f32 * ((r as f32) / 255f32).powf(2.2f32)
868 + 0.7152f32 * ((g as f32) / 255f32).powf(2.2f32)
869 + 0.0722f32 * ((b as f32) / 255f32).powf(2.2f32)
870}
871
872fn contrast_bt_srgb(color: Color, color2: Color) -> f32 {
874 let lum1 = luminance_bt_srgb(color);
875 let lum2 = luminance_bt_srgb(color2);
876 (lum1 - lum2).abs()
877 }
881
882pub fn high_contrast_color(bg: Color, text: &[Color]) -> Style {
883 let mut color0 = text[0];
884 let mut color1 = text[0];
885 let mut contrast1 = contrast_bt_srgb(color1, bg);
886
887 for text_color in text {
888 let test = contrast_bt_srgb(*text_color, bg);
889 if test > contrast1 {
890 color0 = color1;
891 color1 = *text_color;
892 contrast1 = test;
893 }
894 }
895 _ = color0;
897
898 Style::new().bg(bg).fg(color1)
899}
900
901const fn color2rgb(color: Color) -> (u8, u8, u8) {
904 match color {
905 Color::Black => (0x00, 0x00, 0x00),
906 Color::Red => (0xaa, 0x00, 0x00),
907 Color::Green => (0x00, 0xaa, 0x00),
908 Color::Yellow => (0xaa, 0x55, 0x00),
909 Color::Blue => (0x00, 0x00, 0xaa),
910 Color::Magenta => (0xaa, 0x00, 0xaa),
911 Color::Cyan => (0x00, 0xaa, 0xaa),
912 Color::Gray => (0xaa, 0xaa, 0xaa),
913 Color::DarkGray => (0x55, 0x55, 0x55),
914 Color::LightRed => (0xff, 0x55, 0x55),
915 Color::LightGreen => (0x55, 0xff, 0x55),
916 Color::LightYellow => (0xff, 0xff, 0x55),
917 Color::LightBlue => (0x55, 0x55, 0xff),
918 Color::LightMagenta => (0xff, 0x55, 0xff),
919 Color::LightCyan => (0x55, 0xff, 0xff),
920 Color::White => (0xff, 0xff, 0xff),
921 Color::Rgb(r, g, b) => (r, g, b),
922 Color::Indexed(i) => {
923 const VGA256: [(u8, u8, u8); 256] = [
924 (0x00, 0x00, 0x00),
925 (0x80, 0x00, 0x00),
926 (0x00, 0x80, 0x00),
927 (0x80, 0x80, 0x00),
928 (0x00, 0x00, 0x80),
929 (0x80, 0x00, 0x80),
930 (0x00, 0x80, 0x80),
931 (0xc0, 0xc0, 0xc0),
932 (0x80, 0x80, 0x80),
933 (0xff, 0x00, 0x00),
934 (0x00, 0xff, 0x00),
935 (0xff, 0xff, 0x00),
936 (0x00, 0x00, 0xff),
937 (0xff, 0x00, 0xff),
938 (0x00, 0xff, 0xff),
939 (0xff, 0xff, 0xff),
940 (0x00, 0x00, 0x00),
941 (0x00, 0x00, 0x5f),
942 (0x00, 0x00, 0x87),
943 (0x00, 0x00, 0xaf),
944 (0x00, 0x00, 0xd7),
945 (0x00, 0x00, 0xff),
946 (0x00, 0x5f, 0x00),
947 (0x00, 0x5f, 0x5f),
948 (0x00, 0x5f, 0x87),
949 (0x00, 0x5f, 0xaf),
950 (0x00, 0x5f, 0xd7),
951 (0x00, 0x5f, 0xff),
952 (0x00, 0x87, 0x00),
953 (0x00, 0x87, 0x5f),
954 (0x00, 0x87, 0x87),
955 (0x00, 0x87, 0xaf),
956 (0x00, 0x87, 0xd7),
957 (0x00, 0x87, 0xff),
958 (0x00, 0xaf, 0x00),
959 (0x00, 0xaf, 0x5f),
960 (0x00, 0xaf, 0x87),
961 (0x00, 0xaf, 0xaf),
962 (0x00, 0xaf, 0xd7),
963 (0x00, 0xaf, 0xff),
964 (0x00, 0xd7, 0x00),
965 (0x00, 0xd7, 0x5f),
966 (0x00, 0xd7, 0x87),
967 (0x00, 0xd7, 0xaf),
968 (0x00, 0xd7, 0xd7),
969 (0x00, 0xd7, 0xff),
970 (0x00, 0xff, 0x00),
971 (0x00, 0xff, 0x5f),
972 (0x00, 0xff, 0x87),
973 (0x00, 0xff, 0xaf),
974 (0x00, 0xff, 0xd7),
975 (0x00, 0xff, 0xff),
976 (0x5f, 0x00, 0x00),
977 (0x5f, 0x00, 0x5f),
978 (0x5f, 0x00, 0x87),
979 (0x5f, 0x00, 0xaf),
980 (0x5f, 0x00, 0xd7),
981 (0x5f, 0x00, 0xff),
982 (0x5f, 0x5f, 0x00),
983 (0x5f, 0x5f, 0x5f),
984 (0x5f, 0x5f, 0x87),
985 (0x5f, 0x5f, 0xaf),
986 (0x5f, 0x5f, 0xd7),
987 (0x5f, 0x5f, 0xff),
988 (0x5f, 0x87, 0x00),
989 (0x5f, 0x87, 0x5f),
990 (0x5f, 0x87, 0x87),
991 (0x5f, 0x87, 0xaf),
992 (0x5f, 0x87, 0xd7),
993 (0x5f, 0x87, 0xff),
994 (0x5f, 0xaf, 0x00),
995 (0x5f, 0xaf, 0x5f),
996 (0x5f, 0xaf, 0x87),
997 (0x5f, 0xaf, 0xaf),
998 (0x5f, 0xaf, 0xd7),
999 (0x5f, 0xaf, 0xff),
1000 (0x5f, 0xd7, 0x00),
1001 (0x5f, 0xd7, 0x5f),
1002 (0x5f, 0xd7, 0x87),
1003 (0x5f, 0xd7, 0xaf),
1004 (0x5f, 0xd7, 0xd7),
1005 (0x5f, 0xd7, 0xff),
1006 (0x5f, 0xff, 0x00),
1007 (0x5f, 0xff, 0x5f),
1008 (0x5f, 0xff, 0x87),
1009 (0x5f, 0xff, 0xaf),
1010 (0x5f, 0xff, 0xd7),
1011 (0x5f, 0xff, 0xff),
1012 (0x87, 0x00, 0x00),
1013 (0x87, 0x00, 0x5f),
1014 (0x87, 0x00, 0x87),
1015 (0x87, 0x00, 0xaf),
1016 (0x87, 0x00, 0xd7),
1017 (0x87, 0x00, 0xff),
1018 (0x87, 0x5f, 0x00),
1019 (0x87, 0x5f, 0x5f),
1020 (0x87, 0x5f, 0x87),
1021 (0x87, 0x5f, 0xaf),
1022 (0x87, 0x5f, 0xd7),
1023 (0x87, 0x5f, 0xff),
1024 (0x87, 0x87, 0x00),
1025 (0x87, 0x87, 0x5f),
1026 (0x87, 0x87, 0x87),
1027 (0x87, 0x87, 0xaf),
1028 (0x87, 0x87, 0xd7),
1029 (0x87, 0x87, 0xff),
1030 (0x87, 0xaf, 0x00),
1031 (0x87, 0xaf, 0x5f),
1032 (0x87, 0xaf, 0x87),
1033 (0x87, 0xaf, 0xaf),
1034 (0x87, 0xaf, 0xd7),
1035 (0x87, 0xaf, 0xff),
1036 (0x87, 0xd7, 0x00),
1037 (0x87, 0xd7, 0x5f),
1038 (0x87, 0xd7, 0x87),
1039 (0x87, 0xd7, 0xaf),
1040 (0x87, 0xd7, 0xd7),
1041 (0x87, 0xd7, 0xff),
1042 (0x87, 0xff, 0x00),
1043 (0x87, 0xff, 0x5f),
1044 (0x87, 0xff, 0x87),
1045 (0x87, 0xff, 0xaf),
1046 (0x87, 0xff, 0xd7),
1047 (0x87, 0xff, 0xff),
1048 (0xaf, 0x00, 0x00),
1049 (0xaf, 0x00, 0x5f),
1050 (0xaf, 0x00, 0x87),
1051 (0xaf, 0x00, 0xaf),
1052 (0xaf, 0x00, 0xd7),
1053 (0xaf, 0x00, 0xff),
1054 (0xaf, 0x5f, 0x00),
1055 (0xaf, 0x5f, 0x5f),
1056 (0xaf, 0x5f, 0x87),
1057 (0xaf, 0x5f, 0xaf),
1058 (0xaf, 0x5f, 0xd7),
1059 (0xaf, 0x5f, 0xff),
1060 (0xaf, 0x87, 0x00),
1061 (0xaf, 0x87, 0x5f),
1062 (0xaf, 0x87, 0x87),
1063 (0xaf, 0x87, 0xaf),
1064 (0xaf, 0x87, 0xd7),
1065 (0xaf, 0x87, 0xff),
1066 (0xaf, 0xaf, 0x00),
1067 (0xaf, 0xaf, 0x5f),
1068 (0xaf, 0xaf, 0x87),
1069 (0xaf, 0xaf, 0xaf),
1070 (0xaf, 0xaf, 0xd7),
1071 (0xaf, 0xaf, 0xff),
1072 (0xaf, 0xd7, 0x00),
1073 (0xaf, 0xd7, 0x5f),
1074 (0xaf, 0xd7, 0x87),
1075 (0xaf, 0xd7, 0xaf),
1076 (0xaf, 0xd7, 0xd7),
1077 (0xaf, 0xd7, 0xff),
1078 (0xaf, 0xff, 0x00),
1079 (0xaf, 0xff, 0x5f),
1080 (0xaf, 0xff, 0x87),
1081 (0xaf, 0xff, 0xaf),
1082 (0xaf, 0xff, 0xd7),
1083 (0xaf, 0xff, 0xff),
1084 (0xd7, 0x00, 0x00),
1085 (0xd7, 0x00, 0x5f),
1086 (0xd7, 0x00, 0x87),
1087 (0xd7, 0x00, 0xaf),
1088 (0xd7, 0x00, 0xd7),
1089 (0xd7, 0x00, 0xff),
1090 (0xd7, 0x5f, 0x00),
1091 (0xd7, 0x5f, 0x5f),
1092 (0xd7, 0x5f, 0x87),
1093 (0xd7, 0x5f, 0xaf),
1094 (0xd7, 0x5f, 0xd7),
1095 (0xd7, 0x5f, 0xff),
1096 (0xd7, 0x87, 0x00),
1097 (0xd7, 0x87, 0x5f),
1098 (0xd7, 0x87, 0x87),
1099 (0xd7, 0x87, 0xaf),
1100 (0xd7, 0x87, 0xd7),
1101 (0xd7, 0x87, 0xff),
1102 (0xd7, 0xaf, 0x00),
1103 (0xd7, 0xaf, 0x5f),
1104 (0xd7, 0xaf, 0x87),
1105 (0xd7, 0xaf, 0xaf),
1106 (0xd7, 0xaf, 0xd7),
1107 (0xd7, 0xaf, 0xff),
1108 (0xd7, 0xd7, 0x00),
1109 (0xd7, 0xd7, 0x5f),
1110 (0xd7, 0xd7, 0x87),
1111 (0xd7, 0xd7, 0xaf),
1112 (0xd7, 0xd7, 0xd7),
1113 (0xd7, 0xd7, 0xff),
1114 (0xd7, 0xff, 0x00),
1115 (0xd7, 0xff, 0x5f),
1116 (0xd7, 0xff, 0x87),
1117 (0xd7, 0xff, 0xaf),
1118 (0xd7, 0xff, 0xd7),
1119 (0xd7, 0xff, 0xff),
1120 (0xff, 0x00, 0x00),
1121 (0xff, 0x00, 0x5f),
1122 (0xff, 0x00, 0x87),
1123 (0xff, 0x00, 0xaf),
1124 (0xff, 0x00, 0xd7),
1125 (0xff, 0x00, 0xff),
1126 (0xff, 0x5f, 0x00),
1127 (0xff, 0x5f, 0x5f),
1128 (0xff, 0x5f, 0x87),
1129 (0xff, 0x5f, 0xaf),
1130 (0xff, 0x5f, 0xd7),
1131 (0xff, 0x5f, 0xff),
1132 (0xff, 0x87, 0x00),
1133 (0xff, 0x87, 0x5f),
1134 (0xff, 0x87, 0x87),
1135 (0xff, 0x87, 0xaf),
1136 (0xff, 0x87, 0xd7),
1137 (0xff, 0x87, 0xff),
1138 (0xff, 0xaf, 0x00),
1139 (0xff, 0xaf, 0x5f),
1140 (0xff, 0xaf, 0x87),
1141 (0xff, 0xaf, 0xaf),
1142 (0xff, 0xaf, 0xd7),
1143 (0xff, 0xaf, 0xff),
1144 (0xff, 0xd7, 0x00),
1145 (0xff, 0xd7, 0x5f),
1146 (0xff, 0xd7, 0x87),
1147 (0xff, 0xd7, 0xaf),
1148 (0xff, 0xd7, 0xd7),
1149 (0xff, 0xd7, 0xff),
1150 (0xff, 0xff, 0x00),
1151 (0xff, 0xff, 0x5f),
1152 (0xff, 0xff, 0x87),
1153 (0xff, 0xff, 0xaf),
1154 (0xff, 0xff, 0xd7),
1155 (0xff, 0xff, 0xff),
1156 (0x08, 0x08, 0x08),
1157 (0x12, 0x12, 0x12),
1158 (0x1c, 0x1c, 0x1c),
1159 (0x26, 0x26, 0x26),
1160 (0x30, 0x30, 0x30),
1161 (0x3a, 0x3a, 0x3a),
1162 (0x44, 0x44, 0x44),
1163 (0x4e, 0x4e, 0x4e),
1164 (0x58, 0x58, 0x58),
1165 (0x62, 0x62, 0x62),
1166 (0x6c, 0x6c, 0x6c),
1167 (0x76, 0x76, 0x76),
1168 (0x80, 0x80, 0x80),
1169 (0x8a, 0x8a, 0x8a),
1170 (0x94, 0x94, 0x94),
1171 (0x9e, 0x9e, 0x9e),
1172 (0xa8, 0xa8, 0xa8),
1173 (0xb2, 0xb2, 0xb2),
1174 (0xbc, 0xbc, 0xbc),
1175 (0xc6, 0xc6, 0xc6),
1176 (0xd0, 0xd0, 0xd0),
1177 (0xda, 0xda, 0xda),
1178 (0xe4, 0xe4, 0xe4),
1179 (0xee, 0xee, 0xee),
1180 ];
1181 VGA256[i as usize]
1182 }
1183 Color::Reset => (0, 0, 0),
1184 }
1185}
1186
1187impl HandleEvent<Event, Regular, TextOutcome> for ColorInputState {
1190 fn handle(&mut self, event: &Event, _keymap: Regular) -> TextOutcome {
1191 if self.is_focused() {
1192 flow!(match event {
1193 ct_event!(key press '+') | ct_event!(keycode press Up) =>
1194 self.change_section(1).into(),
1195 ct_event!(key press '-') | ct_event!(keycode press Down) =>
1196 self.change_section(-1).into(),
1197 ct_event!(key press ALT-'+') | ct_event!(keycode press ALT-Up) =>
1198 self.change_section(7).into(),
1199 ct_event!(key press ALT-'-') | ct_event!(keycode press ALT-Down) =>
1200 self.change_section(-7).into(),
1201 ct_event!(key press CONTROL-'v') => self.paste_from_clip().into(),
1202 ct_event!(key press CONTROL-'c') => self.copy_to_clip().into(),
1203 ct_event!(key press CONTROL-'x') => self.cut_to_clip().into(),
1204 _ => TextOutcome::Continue,
1205 });
1206 if !self.disable_modes {
1207 flow!(match event {
1208 ct_event!(key press 'r') => self.set_mode(Mode::RGB).into(),
1209 ct_event!(key press 'h') => self.set_mode(Mode::HSV).into(),
1210 ct_event!(key press 'x') => self.set_mode(Mode::HEX).into(),
1211 ct_event!(key press 'm') | ct_event!(keycode press PageUp) =>
1212 self.next_mode().into(),
1213 ct_event!(key press SHIFT-'M') | ct_event!(keycode press PageDown) =>
1214 self.prev_mode().into(),
1215 _ => TextOutcome::Continue,
1216 });
1217 }
1218 }
1219
1220 flow!(handle_mouse(self, event));
1221
1222 match self.widget.handle(event, Regular) {
1223 TextOutcome::TextChanged => {
1224 self.normalize();
1225 self.value = self.parse_value();
1226 TextOutcome::TextChanged
1227 }
1228 r => r,
1229 }
1230 }
1231}
1232
1233impl HandleEvent<Event, ReadOnly, TextOutcome> for ColorInputState {
1234 fn handle(&mut self, event: &Event, _keymap: ReadOnly) -> TextOutcome {
1235 self.widget.handle(event, ReadOnly)
1236 }
1237}
1238
1239impl HandleEvent<Event, MouseOnly, TextOutcome> for ColorInputState {
1240 fn handle(&mut self, event: &Event, _keymap: MouseOnly) -> TextOutcome {
1241 flow!(handle_mouse(self, event));
1242 self.widget.handle(event, MouseOnly)
1243 }
1244}
1245
1246fn handle_mouse(state: &mut ColorInputState, event: &Event) -> TextOutcome {
1247 if state.is_focused() {
1248 if !state.has_mouse_focus() {
1249 return TextOutcome::Continue;
1250 }
1251
1252 match event {
1253 ct_event!(scroll ALT down for x,y) if state.mode_area.contains((*x, *y).into()) => {
1254 state.next_mode().into()
1255 }
1256 ct_event!(scroll ALT up for x,y) if state.mode_area.contains((*x, *y).into()) => {
1257 state.prev_mode().into()
1258 }
1259 ct_event!(scroll down for x,y) if state.widget.area.contains((*x, *y).into()) => {
1260 let rx = state
1261 .widget
1262 .screen_to_col((*x - state.widget.area.x) as i16);
1263 state.change_section_pos(rx, -1).into()
1264 }
1265 ct_event!(scroll up for x,y) if state.widget.area.contains((*x, *y).into()) => {
1266 let rx = state
1267 .widget
1268 .screen_to_col((*x - state.widget.area.x) as i16);
1269 state.change_section_pos(rx, 1).into()
1270 }
1271 ct_event!(scroll ALT down for x,y) if state.widget.area.contains((*x, *y).into()) => {
1272 let rx = state
1273 .widget
1274 .screen_to_col((*x - state.widget.area.x) as i16);
1275 state.change_section_pos(rx, -7).into()
1276 }
1277 ct_event!(scroll ALT up for x,y) if state.widget.area.contains((*x, *y).into()) => {
1278 let rx = state
1279 .widget
1280 .screen_to_col((*x - state.widget.area.x) as i16);
1281 state.change_section_pos(rx, 7).into()
1282 }
1283 _ => TextOutcome::Continue,
1284 }
1285 } else {
1286 TextOutcome::Continue
1287 }
1288}
1289
1290pub fn handle_events(state: &mut ColorInputState, focus: bool, event: &Event) -> TextOutcome {
1294 state.widget.focus.set(focus);
1295 HandleEvent::handle(state, event, Regular)
1296}
1297
1298pub fn handle_readonly_events(
1302 state: &mut ColorInputState,
1303 focus: bool,
1304 event: &Event,
1305) -> TextOutcome {
1306 state.widget.focus.set(focus);
1307 state.handle(event, ReadOnly)
1308}
1309
1310pub fn handle_mouse_events(state: &mut ColorInputState, event: &Event) -> TextOutcome {
1312 HandleEvent::handle(state, event, MouseOnly)
1313}