basalt_tui/
theme_selector_modal.rs1use std::marker::PhantomData;
2
3use ratatui::{
4 buffer::Buffer,
5 layout::{Constraint, Flex, Layout, Rect},
6 widgets::{BorderType, Clear, StatefulWidget, Widget},
7};
8
9use crate::{
10 app::Message as AppMessage,
11 config::Theme,
12 theme_selector::{ThemeSelector, ThemeSelectorState},
13};
14
15#[derive(Clone, Debug, PartialEq)]
16pub enum Message {
17 Toggle,
18 Up,
19 Down,
20 Select,
21 Close,
22}
23
24pub fn update<'a>(
28 message: &Message,
29 state: &mut ThemeSelectorModalState,
30 current: Theme,
31) -> Option<AppMessage<'a>> {
32 match message {
33 Message::Toggle if !state.visible => {
34 state.open(current);
35 state.preview()
36 }
37 Message::Toggle | Message::Close => {
38 state.hide();
39 Some(AppMessage::PreviewTheme(state.original))
40 }
41 Message::Up => {
42 state.theme_selector_state.previous();
43 state.preview()
44 }
45 Message::Down => {
46 state.theme_selector_state.next();
47 state.preview()
48 }
49 Message::Select => {
50 let name = state
51 .theme_selector_state
52 .selected_name()
53 .map(str::to_string);
54 state.hide();
55 name.map(AppMessage::SaveTheme)
56 }
57 }
58}
59
60#[derive(Debug, Default, Clone, PartialEq)]
61pub struct ThemeSelectorModalState {
62 pub theme_selector_state: ThemeSelectorState,
63 pub visible: bool,
64 original: Theme,
65}
66
67impl ThemeSelectorModalState {
68 pub fn new(items: Vec<(String, Theme)>) -> Self {
69 Self {
70 theme_selector_state: ThemeSelectorState::new(items),
71 visible: false,
72 original: Theme::default(),
73 }
74 }
75
76 fn open(&mut self, current: Theme) {
77 self.original = current;
78 self.theme_selector_state.select_theme(¤t);
79 self.visible = true;
80 }
81
82 fn hide(&mut self) {
83 self.visible = false;
84 }
85
86 fn preview<'a>(&self) -> Option<AppMessage<'a>> {
87 self.theme_selector_state
88 .selected_theme()
89 .map(AppMessage::PreviewTheme)
90 }
91}
92
93pub struct ThemeSelectorModal<'a> {
94 _lifetime: PhantomData<&'a ()>,
95 pub border_type: BorderType,
96 pub theme: Theme,
97}
98
99impl<'a> ThemeSelectorModal<'a> {
100 pub fn new(border_type: BorderType, theme: Theme) -> Self {
101 Self {
102 _lifetime: PhantomData,
103 border_type,
104 theme,
105 }
106 }
107
108 fn modal_area(&self, area: Rect) -> Rect {
109 let vertical = Layout::vertical([Constraint::Percentage(50)]).flex(Flex::Center);
110 let horizontal = Layout::horizontal([Constraint::Length(40)]).flex(Flex::Center);
111 let [area] = vertical.areas(area);
112 let [area] = horizontal.areas(area);
113 area
114 }
115}
116
117impl<'a> StatefulWidget for ThemeSelectorModal<'a> {
118 type State = ThemeSelectorModalState;
119
120 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
121 let area = self.modal_area(area);
122 Widget::render(Clear, area, buf);
123 ThemeSelector::new(self.border_type, self.theme).render(
124 area,
125 buf,
126 &mut state.theme_selector_state,
127 );
128 }
129}