1#![allow(unused)]
2
3use ratatui::{prelude::*, widgets::Widget};
4
5use crate::events::*;
6
7#[derive(Debug, Clone)]
8pub struct Button<'text> {
9 text: Text<'text>,
10 theme: Theme,
11 state: State,
12}
13
14#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
15pub enum State {
16 #[default]
17 Normal,
18 Selected,
19 Pressed,
20}
21
22#[derive(Debug, Clone, Copy)]
23pub struct Theme {
24 normal_text: Color,
25 normal_background: Color,
26 selected_text: Color,
27 selected_background: Color,
28 pressed_text: Color,
29 pressed_background: Color,
30 highlight: Color,
31 shadow: Color,
32}
33
34impl Default for Theme {
35 fn default() -> Self {
36 themes::NORMAL
37 }
38}
39
40impl<'text> Button<'text> {
42 pub fn new<T: Into<Text<'text>>>(text: T) -> Self {
43 Self {
44 text: text.into(),
45 theme: Theme::default(),
46 state: State::default(),
47 }
48 }
49
50 pub fn with_theme(mut self, theme: Theme) -> Self {
51 self.theme = theme;
52 self
53 }
54}
55
56impl EventHandler for Button<'_> {
57 fn handle_key(&mut self, key_event: KeyPressedEvent) {
58 match key_event.key {
59 Key::Char(' ') | Key::Enter => self.toggle_press(),
60 _ => {}
61 }
62 }
63}
64
65impl Button<'_> {
66 pub fn toggle_press(&mut self) {
67 match self.state {
68 State::Normal => self.press(),
69 State::Selected => self.press(),
70 State::Pressed => self.select(),
71 }
72 }
73
74 pub fn press(&mut self) {
75 self.state = State::Pressed;
76 }
77
78 pub fn normal(&mut self) {
79 self.state = State::Normal;
80 }
81
82 pub fn select(&mut self) {
83 self.state = State::Selected;
84 }
85}
86
87impl Widget for &Button<'_> {
88 fn render(self, area: Rect, buf: &mut Buffer) {
89 let theme = self.theme;
90
91 let fg = match self.state {
93 State::Normal => theme.normal_text,
94 State::Selected => theme.selected_text,
95 State::Pressed => theme.pressed_text,
96 };
97 let bg = match self.state {
98 State::Normal => theme.normal_background,
99 State::Selected => theme.selected_background,
100 State::Pressed => theme.pressed_background,
101 };
102 let (top, bottom) = if self.state == State::Pressed {
103 (theme.shadow, theme.highlight)
104 } else {
105 (theme.highlight, theme.shadow)
106 };
107
108 buf.set_style(area, (fg, bg));
109
110 let rows = area.rows().collect::<Vec<_>>();
111 let last_index = rows.len().saturating_sub(1);
112 let (first, middle, last) = match rows.len() {
113 0 | 1 => (None, &rows[..], None),
114 2 => (None, &rows[..last_index], Some(rows[last_index])),
115 _ => (Some(rows[0]), &rows[1..last_index], Some(rows[last_index])),
116 };
117
118 if let Some(first) = first {
120 "▔"
121 .repeat(area.width as usize)
122 .fg(top)
123 .bg(bg)
124 .render(first, buf);
125 }
126 if let Some(last) = last {
128 "▁"
129 .repeat(area.width as usize)
130 .fg(bottom)
131 .bg(bg)
132 .render(last, buf);
133 }
134 self.text.clone().centered().render(middle[0], buf);
135 }
136}
137
138pub mod themes {
139 use super::Theme;
140 use ratatui::style::palette::tailwind;
141
142 pub const NORMAL: Theme = Theme {
143 normal_text: tailwind::GRAY.c200,
144 normal_background: tailwind::GRAY.c800,
145 selected_text: tailwind::GRAY.c100,
146 selected_background: tailwind::GRAY.c700,
147 pressed_text: tailwind::GRAY.c300,
148 pressed_background: tailwind::GRAY.c900,
149 highlight: tailwind::GRAY.c600,
150 shadow: tailwind::GRAY.c950,
151 };
152
153 pub const RED: Theme = Theme {
154 normal_text: tailwind::RED.c200,
155 normal_background: tailwind::RED.c800,
156 selected_text: tailwind::RED.c100,
157 selected_background: tailwind::RED.c700,
158 pressed_text: tailwind::RED.c300,
159 pressed_background: tailwind::RED.c900,
160 highlight: tailwind::RED.c600,
161 shadow: tailwind::RED.c950,
162 };
163
164 pub const GREEN: Theme = Theme {
165 normal_text: tailwind::GREEN.c200,
166 normal_background: tailwind::GREEN.c800,
167 selected_text: tailwind::GREEN.c100,
168 selected_background: tailwind::GREEN.c700,
169 pressed_text: tailwind::GREEN.c300,
170 pressed_background: tailwind::GREEN.c900,
171 highlight: tailwind::GREEN.c600,
172 shadow: tailwind::GREEN.c950,
173 };
174
175 pub const BLUE: Theme = Theme {
176 normal_text: tailwind::BLUE.c200,
177 normal_background: tailwind::BLUE.c800,
178 selected_text: tailwind::BLUE.c100,
179 selected_background: tailwind::BLUE.c700,
180 pressed_text: tailwind::BLUE.c300,
181 pressed_background: tailwind::BLUE.c900,
182 highlight: tailwind::BLUE.c600,
183 shadow: tailwind::BLUE.c950,
184 };
185}