1#![warn(missing_docs)]
47#![warn(clippy::pedantic)]
48#![allow(clippy::cast_possible_truncation)] use std::borrow::Cow;
51
52use ratatui::buffer::Buffer;
53use ratatui::layout::Rect;
54use ratatui::style::{Style, Styled};
55use ratatui::text::{Line, Span};
56use ratatui::widgets::{Block, Widget};
57
58pub mod symbols;
59
60#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Default)]
62pub enum LabelPosition {
63 #[default]
65 Right,
66 Left,
68 Top,
70 Bottom,
72}
73
74#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Default)]
76pub enum HorizontalAlignment {
77 #[default]
79 Left,
80 Center,
82 Right,
84}
85
86#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Default)]
88pub enum VerticalAlignment {
89 #[default]
91 Top,
92 Center,
94 Bottom,
96}
97
98#[expect(clippy::struct_field_names)] #[derive(Debug, Clone, Eq, PartialEq, Hash)]
130pub struct Checkbox<'a> {
131 label: Line<'a>,
133 checked: bool,
135 block: Option<Block<'a>>,
137 style: Style,
139 checkbox_style: Style,
141 label_style: Style,
143 checked_symbol: Cow<'a, str>,
145 unchecked_symbol: Cow<'a, str>,
147 label_position: LabelPosition,
149 horizontal_alignment: HorizontalAlignment,
151 vertical_alignment: VerticalAlignment,
153 min_width: Option<u16>,
155 max_width: Option<u16>,
157 wrap_label: bool,
159}
160
161impl Default for Checkbox<'_> {
162 fn default() -> Self {
183 Self {
184 label: Line::default(),
185 checked: false,
186 block: None,
187 style: Style::default(),
188 checkbox_style: Style::default(),
189 label_style: Style::default(),
190 checked_symbol: Cow::Borrowed(symbols::CHECKED),
191 unchecked_symbol: Cow::Borrowed(symbols::UNCHECKED),
192 label_position: LabelPosition::default(),
193 horizontal_alignment: HorizontalAlignment::default(),
194 vertical_alignment: VerticalAlignment::default(),
195 min_width: None,
196 max_width: None,
197 wrap_label: false,
198 }
199 }
200}
201
202impl<'a> Checkbox<'a> {
203 pub fn new<T>(label: T, checked: bool) -> Self
221 where
222 T: Into<Line<'a>>,
223 {
224 Self {
225 label: label.into(),
226 checked,
227 ..Default::default()
228 }
229 }
230
231 #[must_use = "method moves the value of self and returns the modified value"]
243 pub fn label<T>(mut self, label: T) -> Self
244 where
245 T: Into<Line<'a>>,
246 {
247 self.label = label.into();
248 self
249 }
250
251 #[must_use = "method moves the value of self and returns the modified value"]
261 pub const fn checked(mut self, checked: bool) -> Self {
262 self.checked = checked;
263 self
264 }
265
266 #[must_use = "method moves the value of self and returns the modified value"]
277 pub fn block(mut self, block: Block<'a>) -> Self {
278 self.block = Some(block);
279 self
280 }
281
282 #[must_use = "method moves the value of self and returns the modified value"]
301 pub fn style<S: Into<Style>>(mut self, style: S) -> Self {
302 self.style = style.into();
303 self
304 }
305
306 #[must_use = "method moves the value of self and returns the modified value"]
324 pub fn checkbox_style<S: Into<Style>>(mut self, style: S) -> Self {
325 self.checkbox_style = style.into();
326 self
327 }
328
329 #[must_use = "method moves the value of self and returns the modified value"]
347 pub fn label_style<S: Into<Style>>(mut self, style: S) -> Self {
348 self.label_style = style.into();
349 self
350 }
351
352 #[must_use = "method moves the value of self and returns the modified value"]
364 pub fn checked_symbol<T>(mut self, symbol: T) -> Self
365 where
366 T: Into<Cow<'a, str>>,
367 {
368 self.checked_symbol = symbol.into();
369 self
370 }
371
372 #[must_use = "method moves the value of self and returns the modified value"]
384 pub fn unchecked_symbol<T>(mut self, symbol: T) -> Self
385 where
386 T: Into<Cow<'a, str>>,
387 {
388 self.unchecked_symbol = symbol.into();
389 self
390 }
391
392 #[must_use = "method moves the value of self and returns the modified value"]
404 pub const fn label_position(mut self, position: LabelPosition) -> Self {
405 self.label_position = position;
406 self
407 }
408
409 #[must_use = "method moves the value of self and returns the modified value"]
422 pub const fn horizontal_alignment(mut self, alignment: HorizontalAlignment) -> Self {
423 self.horizontal_alignment = alignment;
424 self
425 }
426
427 #[must_use = "method moves the value of self and returns the modified value"]
440 pub const fn vertical_alignment(mut self, alignment: VerticalAlignment) -> Self {
441 self.vertical_alignment = alignment;
442 self
443 }
444
445 #[must_use = "method moves the value of self and returns the modified value"]
457 pub const fn min_width(mut self, width: u16) -> Self {
458 self.min_width = Some(width);
459 self
460 }
461
462 #[must_use = "method moves the value of self and returns the modified value"]
474 pub const fn max_width(mut self, width: u16) -> Self {
475 self.max_width = Some(width);
476 self
477 }
478
479 #[must_use = "method moves the value of self and returns the modified value"]
494 pub const fn wrap_label(mut self, wrap: bool) -> Self {
495 self.wrap_label = wrap;
496 self
497 }
498}
499
500impl Styled for Checkbox<'_> {
501 type Item = Self;
502
503 fn style(&self) -> Style {
504 self.style
505 }
506
507 fn set_style<S: Into<Style>>(mut self, style: S) -> Self::Item {
508 self.style = style.into();
509 self
510 }
511}
512
513impl Widget for Checkbox<'_> {
514 fn render(self, area: Rect, buf: &mut Buffer) {
515 Widget::render(&self, area, buf);
516 }
517}
518
519impl Widget for &Checkbox<'_> {
520 fn render(self, area: Rect, buf: &mut Buffer) {
521 buf.set_style(area, self.style);
522 let inner = if let Some(ref block) = self.block {
523 let inner_area = block.inner(area);
524 block.render(area, buf);
525 inner_area
526 } else {
527 area
528 };
529 self.render_checkbox(inner, buf);
530 }
531}
532
533impl Checkbox<'_> {
534 fn render_checkbox(&self, area: Rect, buf: &mut Buffer) {
535 if area.is_empty() {
536 return;
537 }
538
539 let symbol = if self.checked {
541 &self.checked_symbol
542 } else {
543 &self.unchecked_symbol
544 };
545
546 let checkbox_style = self.style.patch(self.checkbox_style);
548 let label_style = self.style.patch(self.label_style);
549
550 let mut render_area = area;
552 if let Some(min_width) = self.min_width {
553 render_area.width = render_area.width.max(min_width);
554 }
555 if let Some(max_width) = self.max_width {
556 render_area.width = render_area.width.min(max_width);
557 }
558
559 render_area.width = render_area.width.min(area.width);
561
562 let checkbox_span = Span::styled(symbol.as_ref(), checkbox_style);
564 let styled_label = self.label.clone().patch_style(label_style);
565 let owned_label = Line::from(
566 styled_label
567 .spans
568 .iter()
569 .map(|s| Span::styled(s.content.to_string(), s.style))
570 .collect::<Vec<_>>(),
571 );
572
573 match self.label_position {
575 LabelPosition::Right | LabelPosition::Left => {
576 self.render_horizontal(render_area, buf, checkbox_span, owned_label);
577 }
578 LabelPosition::Top | LabelPosition::Bottom => {
579 self.render_vertical(render_area, buf, checkbox_span, owned_label);
580 }
581 }
582 }
583
584 fn render_horizontal(
585 &self,
586 area: Rect,
587 buf: &mut Buffer,
588 checkbox_span: Span<'_>,
589 label: Line<'static>,
590 ) {
591 if area.height == 0 || area.width == 0 {
592 return;
593 }
594
595 let checkbox_width = checkbox_span.width() as u16;
596 let space_width = 1u16;
597
598 let label_lines = if self.wrap_label {
600 let available_width = area.width.saturating_sub(checkbox_width + space_width);
601 Self::wrap_text(&label, available_width)
602 } else {
603 vec![label]
604 };
605
606 let total_width = if label_lines.is_empty() {
607 checkbox_width
608 } else {
609 checkbox_width
610 + space_width
611 + label_lines
612 .iter()
613 .map(|l| l.width() as u16)
614 .max()
615 .unwrap_or(0)
616 };
617
618 let x_offset = match self.horizontal_alignment {
620 HorizontalAlignment::Left => 0,
621 HorizontalAlignment::Center => area.width.saturating_sub(total_width) / 2,
622 HorizontalAlignment::Right => area.width.saturating_sub(total_width),
623 };
624
625 let content_height = label_lines.len() as u16;
627 let y_offset = match self.vertical_alignment {
628 VerticalAlignment::Top => 0,
629 VerticalAlignment::Center => area.height.saturating_sub(content_height) / 2,
630 VerticalAlignment::Bottom => area.height.saturating_sub(content_height),
631 };
632
633 match self.label_position {
635 LabelPosition::Right => {
636 if x_offset < area.width && y_offset < area.height {
638 let checkbox_area = Rect {
639 x: area.x + x_offset,
640 y: area.y + y_offset,
641 width: checkbox_width.min(area.width.saturating_sub(x_offset)),
642 height: 1,
643 };
644 Line::from(vec![checkbox_span]).render(checkbox_area, buf);
645
646 for (i, label_line) in label_lines.iter().enumerate() {
648 let label_x = area.x + x_offset + checkbox_width + space_width;
649 let label_y = area.y + y_offset + i as u16;
650 if label_y < area.y + area.height && label_x < area.x + area.width {
651 let label_area = Rect {
652 x: label_x,
653 y: label_y,
654 width: area
655 .width
656 .saturating_sub(x_offset + checkbox_width + space_width),
657 height: 1,
658 };
659 label_line.clone().render(label_area, buf);
660 }
661 }
662 }
663 }
664 LabelPosition::Left => {
665 let max_label_width = label_lines
667 .iter()
668 .map(|l| l.width() as u16)
669 .max()
670 .unwrap_or(0);
671
672 for (i, label_line) in label_lines.iter().enumerate() {
674 let label_y = area.y + y_offset + i as u16;
675 if label_y < area.y + area.height && x_offset < area.width {
676 let label_area = Rect {
677 x: area.x + x_offset,
678 y: label_y,
679 width: max_label_width.min(area.width.saturating_sub(x_offset)),
680 height: 1,
681 };
682 label_line.clone().render(label_area, buf);
683 }
684 }
685
686 let checkbox_x = area.x + x_offset + max_label_width + space_width;
688 if checkbox_x < area.x + area.width && y_offset < area.height {
689 let checkbox_area = Rect {
690 x: checkbox_x,
691 y: area.y + y_offset,
692 width: checkbox_width.min(
693 area.width
694 .saturating_sub(x_offset + max_label_width + space_width),
695 ),
696 height: 1,
697 };
698 Line::from(vec![checkbox_span]).render(checkbox_area, buf);
699 }
700 }
701 _ => {}
702 }
703 }
704
705 fn render_vertical(
706 &self,
707 area: Rect,
708 buf: &mut Buffer,
709 checkbox_span: Span<'_>,
710 label: Line<'static>,
711 ) {
712 if area.height == 0 || area.width == 0 {
713 return;
714 }
715
716 let label_lines = if self.wrap_label {
718 Self::wrap_text(&label, area.width)
719 } else {
720 vec![label]
721 };
722
723 let checkbox_width = checkbox_span.width() as u16;
724 let label_height = label_lines.len() as u16;
725 let total_height = 1 + label_height; let y_offset = match self.vertical_alignment {
729 VerticalAlignment::Top => 0,
730 VerticalAlignment::Center => area.height.saturating_sub(total_height) / 2,
731 VerticalAlignment::Bottom => area.height.saturating_sub(total_height),
732 };
733
734 match self.label_position {
735 LabelPosition::Top => {
736 for (i, label_line) in label_lines.iter().enumerate() {
738 let label_y = area.y + y_offset + i as u16;
739 if label_y < area.y + area.height {
740 let x_offset = match self.horizontal_alignment {
741 HorizontalAlignment::Left => 0,
742 HorizontalAlignment::Center => {
743 area.width.saturating_sub(label_line.width() as u16) / 2
744 }
745 HorizontalAlignment::Right => {
746 area.width.saturating_sub(label_line.width() as u16)
747 }
748 };
749 let label_area = Rect {
750 x: area.x + x_offset,
751 y: label_y,
752 width: area.width.saturating_sub(x_offset),
753 height: 1,
754 };
755 label_line.clone().render(label_area, buf);
756 }
757 }
758
759 let checkbox_y = area.y + y_offset + label_height;
761 if checkbox_y < area.y + area.height {
762 let x_offset = match self.horizontal_alignment {
763 HorizontalAlignment::Left => 0,
764 HorizontalAlignment::Center => {
765 area.width.saturating_sub(checkbox_width) / 2
766 }
767 HorizontalAlignment::Right => area.width.saturating_sub(checkbox_width),
768 };
769 let checkbox_area = Rect {
770 x: area.x + x_offset,
771 y: checkbox_y,
772 width: checkbox_width.min(area.width.saturating_sub(x_offset)),
773 height: 1,
774 };
775 Line::from(vec![checkbox_span]).render(checkbox_area, buf);
776 }
777 }
778 LabelPosition::Bottom => {
779 let x_offset = match self.horizontal_alignment {
781 HorizontalAlignment::Left => 0,
782 HorizontalAlignment::Center => area.width.saturating_sub(checkbox_width) / 2,
783 HorizontalAlignment::Right => area.width.saturating_sub(checkbox_width),
784 };
785 let checkbox_area = Rect {
786 x: area.x + x_offset,
787 y: area.y + y_offset,
788 width: checkbox_width.min(area.width.saturating_sub(x_offset)),
789 height: 1,
790 };
791 Line::from(vec![checkbox_span]).render(checkbox_area, buf);
792
793 for (i, label_line) in label_lines.iter().enumerate() {
795 let label_y = area.y + y_offset + 1 + i as u16;
796 if label_y < area.y + area.height {
797 let x_offset = match self.horizontal_alignment {
798 HorizontalAlignment::Left => 0,
799 HorizontalAlignment::Center => {
800 area.width.saturating_sub(label_line.width() as u16) / 2
801 }
802 HorizontalAlignment::Right => {
803 area.width.saturating_sub(label_line.width() as u16)
804 }
805 };
806 let label_area = Rect {
807 x: area.x + x_offset,
808 y: label_y,
809 width: area.width.saturating_sub(x_offset),
810 height: 1,
811 };
812 label_line.clone().render(label_area, buf);
813 }
814 }
815 }
816 _ => {}
817 }
818 }
819
820 fn wrap_text(line: &Line<'_>, max_width: u16) -> Vec<Line<'static>> {
821 if max_width == 0 {
822 let owned = Line::from(
823 line.spans
824 .iter()
825 .map(|s| Span::styled(s.content.to_string(), s.style))
826 .collect::<Vec<_>>(),
827 );
828 return vec![owned];
829 }
830
831 let mut result = Vec::new();
832 let mut current_line = Vec::new();
833 let mut current_width = 0u16;
834
835 for span in &line.spans {
836 let text = span.content.as_ref();
837 let words: Vec<&str> = text.split(' ').collect();
838
839 for (i, word) in words.iter().enumerate() {
840 let word_width = word.chars().count() as u16;
841 let space_width = u16::from(i > 0 || !current_line.is_empty());
842
843 if current_width + space_width + word_width > max_width && !current_line.is_empty()
844 {
845 result.push(Line::from(current_line.clone()));
846 current_line.clear();
847 current_width = 0;
848 }
849
850 if i > 0 {
851 current_line.push(Span::styled(String::from(" "), span.style));
852 current_width += 1;
853 }
854
855 current_line.push(Span::styled(String::from(*word), span.style));
856 current_width += word_width;
857 }
858 }
859
860 if !current_line.is_empty() {
861 result.push(Line::from(current_line));
862 }
863
864 if result.is_empty() {
865 let owned = Line::from(
866 line.spans
867 .iter()
868 .map(|s| Span::styled(s.content.to_string(), s.style))
869 .collect::<Vec<_>>(),
870 );
871 result.push(owned);
872 }
873
874 result
875 }
876}
877
878#[cfg(test)]
879mod tests {
880 use ratatui::style::{Color, Modifier, Stylize};
881
882 use super::*;
883
884 #[test]
885 fn checkbox_new() {
886 let checkbox = Checkbox::new("Test", true);
887 assert_eq!(checkbox.label, Line::from("Test"));
888 assert!(checkbox.checked);
889 }
890
891 #[test]
892 fn checkbox_default() {
893 let checkbox = Checkbox::default();
894 assert_eq!(checkbox.label, Line::default());
895 assert!(!checkbox.checked);
896 }
897
898 #[test]
899 fn checkbox_label() {
900 let checkbox = Checkbox::default().label("New label");
901 assert_eq!(checkbox.label, Line::from("New label"));
902 }
903
904 #[test]
905 fn checkbox_checked() {
906 let checkbox = Checkbox::default().checked(true);
907 assert!(checkbox.checked);
908 }
909
910 #[test]
911 fn checkbox_style() {
912 let style = Style::default().fg(Color::Red);
913 let checkbox = Checkbox::default().style(style);
914 assert_eq!(checkbox.style, style);
915 }
916
917 #[test]
918 fn checkbox_checkbox_style() {
919 let style = Style::default().fg(Color::Green);
920 let checkbox = Checkbox::default().checkbox_style(style);
921 assert_eq!(checkbox.checkbox_style, style);
922 }
923
924 #[test]
925 fn checkbox_label_style() {
926 let style = Style::default().fg(Color::Blue);
927 let checkbox = Checkbox::default().label_style(style);
928 assert_eq!(checkbox.label_style, style);
929 }
930
931 #[test]
932 fn checkbox_checked_symbol() {
933 let checkbox = Checkbox::default().checked_symbol("[X]");
934 assert_eq!(checkbox.checked_symbol, "[X]");
935 }
936
937 #[test]
938 fn checkbox_unchecked_symbol() {
939 let checkbox = Checkbox::default().unchecked_symbol("[ ]");
940 assert_eq!(checkbox.unchecked_symbol, "[ ]");
941 }
942
943 #[test]
944 fn checkbox_styled_trait() {
945 let checkbox = Checkbox::default().red();
946 assert_eq!(checkbox.style, Style::default().fg(Color::Red));
947 }
948
949 #[test]
950 fn checkbox_render_unchecked() {
951 let checkbox = Checkbox::new("Test", false);
952 let mut buffer = Buffer::empty(Rect::new(0, 0, 10, 1));
953 checkbox.render(buffer.area, &mut buffer);
954
955 assert!(buffer
957 .cell(buffer.area.as_position())
958 .unwrap()
959 .symbol()
960 .starts_with('☐'));
961 }
962
963 #[test]
964 fn checkbox_render_checked() {
965 let checkbox = Checkbox::new("Test", true);
966 let mut buffer = Buffer::empty(Rect::new(0, 0, 10, 1));
967 checkbox.render(buffer.area, &mut buffer);
968
969 assert!(buffer
971 .cell(buffer.area.as_position())
972 .unwrap()
973 .symbol()
974 .starts_with('☑'));
975 }
976
977 #[test]
978 fn checkbox_render_empty_area() {
979 let checkbox = Checkbox::new("Test", true);
980 let mut buffer = Buffer::empty(Rect::new(0, 0, 0, 0));
981
982 checkbox.render(buffer.area, &mut buffer);
984 }
985
986 #[test]
987 fn checkbox_render_with_block() {
988 let checkbox = Checkbox::new("Test", true).block(Block::bordered());
989 let mut buffer = Buffer::empty(Rect::new(0, 0, 12, 3));
990
991 checkbox.render(buffer.area, &mut buffer);
993 }
994
995 #[test]
996 fn checkbox_render_with_custom_symbols() {
997 let checkbox = Checkbox::new("Test", true)
998 .checked_symbol("[X]")
999 .unchecked_symbol("[ ]");
1000
1001 let mut buffer = Buffer::empty(Rect::new(0, 0, 10, 1));
1002 checkbox.render(buffer.area, &mut buffer);
1003
1004 assert!(buffer
1005 .cell(buffer.area.as_position())
1006 .unwrap()
1007 .symbol()
1008 .starts_with('['));
1009 }
1010
1011 #[test]
1012 fn checkbox_with_styled_label() {
1013 let checkbox = Checkbox::new("Test".blue(), true);
1014 assert_eq!(checkbox.label.spans[0].style.fg, Some(Color::Blue));
1015 }
1016
1017 #[test]
1018 fn checkbox_complex_styling() {
1019 let checkbox = Checkbox::new("Feature", true)
1020 .style(Style::default().fg(Color::White))
1021 .checkbox_style(
1022 Style::default()
1023 .fg(Color::Green)
1024 .add_modifier(Modifier::BOLD),
1025 )
1026 .label_style(Style::default().fg(Color::Gray));
1027
1028 assert_eq!(checkbox.style.fg, Some(Color::White));
1029 assert_eq!(checkbox.checkbox_style.fg, Some(Color::Green));
1030 assert_eq!(checkbox.label_style.fg, Some(Color::Gray));
1031 }
1032
1033 #[test]
1034 fn checkbox_emoji_symbols() {
1035 let checkbox = Checkbox::new("Test", true)
1036 .checked_symbol("✅ ")
1037 .unchecked_symbol("⬜ ");
1038
1039 assert_eq!(checkbox.checked_symbol, "✅ ");
1040 assert_eq!(checkbox.unchecked_symbol, "⬜ ");
1041 }
1042
1043 #[test]
1044 fn checkbox_unicode_symbols() {
1045 let checkbox = Checkbox::new("Test", false)
1046 .checked_symbol("● ")
1047 .unchecked_symbol("○ ");
1048
1049 assert_eq!(checkbox.checked_symbol, "● ");
1050 assert_eq!(checkbox.unchecked_symbol, "○ ");
1051 }
1052
1053 #[test]
1054 fn checkbox_arrow_symbols() {
1055 let checkbox = Checkbox::new("Test", true)
1056 .checked_symbol("▶ ")
1057 .unchecked_symbol("▷ ");
1058
1059 assert_eq!(checkbox.checked_symbol, "▶ ");
1060 assert_eq!(checkbox.unchecked_symbol, "▷ ");
1061 }
1062
1063 #[test]
1064 fn checkbox_parenthesis_symbols() {
1065 let checkbox = Checkbox::new("Test", false)
1066 .checked_symbol("(X)")
1067 .unchecked_symbol("(O)");
1068
1069 assert_eq!(checkbox.checked_symbol, "(X)");
1070 assert_eq!(checkbox.unchecked_symbol, "(O)");
1071 }
1072
1073 #[test]
1074 fn checkbox_minus_symbols() {
1075 let checkbox = Checkbox::new("Test", false)
1076 .checked_symbol("[+]")
1077 .unchecked_symbol("[-]");
1078
1079 assert_eq!(checkbox.checked_symbol, "[+]");
1080 assert_eq!(checkbox.unchecked_symbol, "[-]");
1081 }
1082
1083 #[test]
1084 fn checkbox_predefined_minus_symbol() {
1085 use crate::symbols;
1086 let checkbox = Checkbox::new("Test", false).unchecked_symbol(symbols::UNCHECKED_MINUS);
1087
1088 assert_eq!(checkbox.unchecked_symbol, "[-]");
1089 }
1090
1091 #[test]
1092 fn checkbox_predefined_parenthesis_symbols() {
1093 use crate::symbols;
1094 let checkbox = Checkbox::new("Test", true)
1095 .checked_symbol(symbols::CHECKED_PARENTHESIS_X)
1096 .unchecked_symbol(symbols::UNCHECKED_PARENTHESIS_O);
1097
1098 assert_eq!(checkbox.checked_symbol, "(X)");
1099 assert_eq!(checkbox.unchecked_symbol, "(O)");
1100 }
1101
1102 #[test]
1103 fn checkbox_render_emoji() {
1104 let checkbox = Checkbox::new("Emoji", true)
1105 .checked_symbol("✅ ")
1106 .unchecked_symbol("⬜ ");
1107
1108 let mut buffer = Buffer::empty(Rect::new(0, 0, 15, 1));
1109 checkbox.render(buffer.area, &mut buffer);
1110
1111 assert!(buffer.area.area() > 0);
1113 }
1114
1115 #[test]
1116 fn checkbox_label_style_overrides() {
1117 let checkbox = Checkbox::new("Test", true)
1118 .style(Style::default().fg(Color::White))
1119 .label_style(Style::default().fg(Color::Blue));
1120
1121 assert_eq!(checkbox.style.fg, Some(Color::White));
1122 assert_eq!(checkbox.label_style.fg, Some(Color::Blue));
1123 }
1124}