tca_ratatui/widgets/
color_picker.rs1use crate::{ColorRamp, TcaTheme};
2use ratatui::{
3 buffer::Buffer,
4 layout::{Constraint, Layout, Rect},
5 style::{Style, Stylize},
6 text::Line,
7 widgets::{Block, Borders, Paragraph, Widget, Wrap},
8};
9
10#[derive(Debug, Clone, PartialEq, Eq)]
29pub struct ColorPicker<'a> {
30 theme: &'a TcaTheme,
31 title: Option<String>,
32 instructions: Option<String>,
33}
34
35impl<'a> ColorPicker<'a> {
36 pub const fn new(theme: &'a TcaTheme) -> Self {
38 Self {
39 theme,
40 title: None,
41 instructions: None,
42 }
43 }
44
45 pub fn title(mut self, title: impl Into<String>) -> Self {
47 self.title = Some(title.into());
48 self
49 }
50
51 pub fn instructions(mut self, instructions: impl Into<String>) -> Self {
53 self.instructions = Some(instructions.into());
54 self
55 }
56}
57
58impl Widget for ColorPicker<'_> {
59 fn render(self, area: Rect, buf: &mut Buffer) {
60 let theme = self.theme;
61
62 let border_color = theme.ui.border_primary;
63 let title_color = theme.ui.fg_primary;
64 let bg = theme.ui.bg_primary;
65 let fg = theme.ui.fg_primary;
66
67 let mut block = Block::bordered()
68 .bg(bg)
69 .fg(fg)
70 .borders(Borders::ALL)
71 .border_style(Style::default().fg(border_color))
72 .title_style(Style::default().fg(title_color));
73
74 if let Some(title) = self.title {
75 block = block.title(Line::from(title).centered());
76 }
77
78 if let Some(instructions) = self.instructions {
79 block = block.title_bottom(Line::from(instructions).centered());
80 }
81
82 let inner = block.inner(area);
83 block.render(area, buf);
84
85 let chunks = Layout::horizontal([
86 Constraint::Percentage(40),
87 Constraint::Percentage(30),
88 Constraint::Percentage(30),
89 ])
90 .split(inner);
91
92 let mut left_lines = vec![Line::from(format!("Theme: {}", theme.meta.name))
93 .style(Style::default().fg(title_color))];
94 if let Some(author) = &theme.meta.author {
95 left_lines.push(Line::from(format!("Author: {}", author)));
96 }
97 if let Some(version) = &theme.meta.version {
98 left_lines.push(Line::from(format!("Version: {}", version)));
99 }
100 if let Some(description) = &theme.meta.description {
101 left_lines.push(Line::from(format!("Description: {}", description)));
102 }
103
104 left_lines.push(Line::from(""));
105 left_lines.push(Line::from("ANSI Colors:"));
106 left_lines.extend([
107 Line::from(" black").style(Style::default().fg(theme.ansi.black)),
108 Line::from(" red").style(Style::default().fg(theme.ansi.red)),
109 Line::from(" green").style(Style::default().fg(theme.ansi.green)),
110 Line::from(" yellow").style(Style::default().fg(theme.ansi.yellow)),
111 Line::from(" blue").style(Style::default().fg(theme.ansi.blue)),
112 Line::from(" magenta").style(Style::default().fg(theme.ansi.magenta)),
113 Line::from(" cyan").style(Style::default().fg(theme.ansi.cyan)),
114 Line::from(" white").style(Style::default().fg(theme.ansi.white)),
115 Line::from(" bright_black").style(Style::default().fg(theme.ansi.bright_black)),
116 Line::from(" bright_red").style(Style::default().fg(theme.ansi.bright_red)),
117 Line::from(" bright_green").style(Style::default().fg(theme.ansi.bright_green)),
118 Line::from(" bright_yellow").style(Style::default().fg(theme.ansi.bright_yellow)),
119 Line::from(" bright_blue").style(Style::default().fg(theme.ansi.bright_blue)),
120 Line::from(" bright_magenta").style(Style::default().fg(theme.ansi.bright_magenta)),
121 Line::from(" bright_cyan").style(Style::default().fg(theme.ansi.bright_cyan)),
122 Line::from(" bright_white").style(Style::default().fg(theme.ansi.bright_white)),
123 ]);
124
125 let mut center_lines = vec![Line::from("Semantic Colors:")];
126 center_lines.extend([
127 Line::from(" error").style(Style::default().fg(theme.semantic.error)),
128 Line::from(" warning").style(Style::default().fg(theme.semantic.warning)),
129 Line::from(" success").style(Style::default().fg(theme.semantic.success)),
130 Line::from(" info").style(Style::default().fg(theme.semantic.info)),
131 Line::from(" highlight").style(Style::default().fg(theme.semantic.highlight)),
132 Line::from(" link").style(Style::default().fg(theme.semantic.link)),
133 ]);
134
135 center_lines.push(Line::from(""));
136 center_lines.push(Line::from("UI Colors:"));
137 center_lines.extend([
138 Line::from(" bg_primary").style(
139 Style::default()
140 .fg(theme.ui.bg_primary)
141 .bg(theme.ui.fg_primary),
142 ),
143 Line::from(" bg_secondary").style(
144 Style::default()
145 .fg(theme.ui.bg_secondary)
146 .bg(theme.ui.fg_primary),
147 ),
148 Line::from(" fg_primary").style(Style::default().fg(theme.ui.fg_primary)),
149 Line::from(" fg_secondary").style(Style::default().fg(theme.ui.fg_secondary)),
150 Line::from(" fg_muted").style(Style::default().fg(theme.ui.fg_muted)),
151 Line::from(" border_primary").style(Style::default().fg(theme.ui.border_primary)),
152 Line::from(" border_muted").style(Style::default().fg(theme.ui.border_muted)),
153 Line::from(" cursor_primary").style(Style::default().bg(theme.ui.cursor_primary)),
154 Line::from(" cursor_muted").style(Style::default().bg(theme.ui.cursor_muted)),
155 Line::from(" selection_bg").style(
156 Style::default()
157 .bg(theme.ui.selection_bg)
158 .fg(theme.ui.fg_primary),
159 ),
160 Line::from(" selection_fg").style(Style::default().fg(theme.ui.selection_fg)),
161 ]);
162
163 let mut right_lines = vec![Line::from("Base16 Colors:")];
164 for (key, value) in theme.base16.entries() {
165 right_lines.push(Line::from(format!(" {}", key)).style(Style::default().fg(value)));
166 }
167
168 right_lines.push(Line::from(""));
169 right_lines.push(Line::from("Palette:"));
170 for name in theme.palette.ramp_names() {
171 if let Some(ramp) = theme.palette.get_ramp(name) {
172 right_lines.push(render_color_ramp(name, ramp));
173 }
174 }
175 Paragraph::new(left_lines)
176 .wrap(Wrap { trim: false })
177 .render(chunks[0], buf);
178 Paragraph::new(center_lines).render(chunks[1], buf);
179 Paragraph::new(right_lines).render(chunks[2], buf);
180 }
181}
182
183fn render_color_ramp(name: &str, ramp: &ColorRamp) -> Line<'static> {
184 let mut spans = vec![ratatui::text::Span::raw(format!(" {}: ", name))];
185 for &color in &ramp.colors {
186 spans.push(ratatui::text::Span::styled("█", Style::default().fg(color)));
187 }
188 Line::from(spans)
189}