kimun_notes/components/preferences/
sorting_section.rs1use ratatui::Frame;
2use ratatui::layout::{Constraint, Direction, Layout, Rect};
3use ratatui::style::Style;
4use ratatui::widgets::{Block, Borders, List, ListItem, ListState};
5
6use crate::components::Component;
7use crate::components::event_state::EventState;
8use crate::components::events::{AppTx, InputEvent};
9use crate::settings::themes::Theme;
10use crate::settings::{SortFieldSetting, SortOrderSetting};
11
12pub struct SortingSection {
13 pub default_sort_field: SortFieldSetting,
14 pub default_sort_order: SortOrderSetting,
15 pub journal_sort_field: SortFieldSetting,
16 pub journal_sort_order: SortOrderSetting,
17 list_state: ListState,
18}
19
20impl SortingSection {
21 pub fn new(
22 default_sort_field: SortFieldSetting,
23 default_sort_order: SortOrderSetting,
24 journal_sort_field: SortFieldSetting,
25 journal_sort_order: SortOrderSetting,
26 ) -> Self {
27 let mut list_state = ListState::default();
28 list_state.select(Some(0));
29 Self {
30 default_sort_field,
31 default_sort_order,
32 journal_sort_field,
33 journal_sort_order,
34 list_state,
35 }
36 }
37
38 const ROW_COUNT: usize = 4;
39
40 fn cycle_field(f: SortFieldSetting) -> SortFieldSetting {
41 match f {
42 SortFieldSetting::Name => SortFieldSetting::Title,
43 SortFieldSetting::Title => SortFieldSetting::Name,
44 }
45 }
46
47 fn cycle_order(o: SortOrderSetting) -> SortOrderSetting {
48 match o {
49 SortOrderSetting::Ascending => SortOrderSetting::Descending,
50 SortOrderSetting::Descending => SortOrderSetting::Ascending,
51 }
52 }
53
54 fn field_label(f: SortFieldSetting) -> &'static str {
55 match f {
56 SortFieldSetting::Name => "Name",
57 SortFieldSetting::Title => "Title",
58 }
59 }
60
61 fn order_label(o: SortOrderSetting) -> &'static str {
62 match o {
63 SortOrderSetting::Ascending => "Ascending",
64 SortOrderSetting::Descending => "Descending",
65 }
66 }
67}
68
69impl Component for SortingSection {
70 fn handle_input(&mut self, event: &InputEvent, _tx: &AppTx) -> EventState {
71 let InputEvent::Key(key) = event else {
72 return EventState::NotConsumed;
73 };
74 let selected = self.list_state.selected().unwrap_or(0);
75 match key.code {
76 ratatui::crossterm::event::KeyCode::Up
77 | ratatui::crossterm::event::KeyCode::Char('k') => {
78 self.list_state
79 .select(Some((selected + Self::ROW_COUNT - 1) % Self::ROW_COUNT));
80 EventState::Consumed
81 }
82 ratatui::crossterm::event::KeyCode::Down
83 | ratatui::crossterm::event::KeyCode::Char('j') => {
84 self.list_state
85 .select(Some((selected + 1) % Self::ROW_COUNT));
86 EventState::Consumed
87 }
88 ratatui::crossterm::event::KeyCode::Enter
89 | ratatui::crossterm::event::KeyCode::Char(' ') => {
90 match selected {
91 0 => self.default_sort_field = Self::cycle_field(self.default_sort_field),
92 1 => self.default_sort_order = Self::cycle_order(self.default_sort_order),
93 2 => self.journal_sort_field = Self::cycle_field(self.journal_sort_field),
94 3 => self.journal_sort_order = Self::cycle_order(self.journal_sort_order),
95 _ => {}
96 }
97 EventState::Consumed
98 }
99 _ => EventState::NotConsumed,
100 }
101 }
102
103 fn render(&mut self, f: &mut Frame, rect: Rect, theme: &Theme, focused: bool) {
104 let border_style = theme.border_style(focused);
105
106 let outer = Block::default()
108 .title("Sorting")
109 .borders(Borders::ALL)
110 .border_style(border_style)
111 .style(theme.base_style());
112 let inner = outer.inner(rect);
113 f.render_widget(outer, rect);
114
115 let halves = Layout::default()
118 .direction(Direction::Vertical)
119 .constraints([Constraint::Length(4), Constraint::Length(4)])
120 .split(inner);
121
122 let selected = self.list_state.selected().unwrap_or(0);
123 let highlight = Style::default()
124 .fg(theme.selection_fg.to_ratatui())
125 .bg(theme.selection_bg.to_ratatui());
126
127 let default_items = vec![
129 ListItem::new(format!(
130 " Sort field: [{}]",
131 Self::field_label(self.default_sort_field)
132 ))
133 .style(Style::default().fg(theme.fg.to_ratatui())),
134 ListItem::new(format!(
135 " Sort order: [{}]",
136 Self::order_label(self.default_sort_order)
137 ))
138 .style(Style::default().fg(theme.fg.to_ratatui())),
139 ];
140 let mut default_state = ListState::default();
141 default_state.select(if selected < 2 { Some(selected) } else { None });
142 let default_block = Block::default()
143 .title("Default")
144 .borders(Borders::ALL)
145 .border_style(theme.border_style(focused && selected < 2))
146 .style(theme.base_style());
147 f.render_stateful_widget(
148 List::new(default_items)
149 .block(default_block)
150 .highlight_style(highlight),
151 halves[0],
152 &mut default_state,
153 );
154
155 let journal_items = vec![
157 ListItem::new(format!(
158 " Sort field: [{}]",
159 Self::field_label(self.journal_sort_field)
160 ))
161 .style(Style::default().fg(theme.fg.to_ratatui())),
162 ListItem::new(format!(
163 " Sort order: [{}]",
164 Self::order_label(self.journal_sort_order)
165 ))
166 .style(Style::default().fg(theme.fg.to_ratatui())),
167 ];
168 let mut journal_state = ListState::default();
169 journal_state.select(if selected >= 2 {
170 Some(selected - 2)
171 } else {
172 None
173 });
174 let journal_block = Block::default()
175 .title("Journal")
176 .borders(Borders::ALL)
177 .border_style(theme.border_style(focused && selected >= 2))
178 .style(theme.base_style());
179 f.render_stateful_widget(
180 List::new(journal_items)
181 .block(journal_block)
182 .highlight_style(highlight),
183 halves[1],
184 &mut journal_state,
185 );
186 }
187}