Skip to main content

mxr_tui/app/
draw.rs

1use super::*;
2
3impl App {
4    pub fn draw(&mut self, frame: &mut Frame) {
5        let theme = &self.theme;
6        let area = frame.area();
7
8        // Layout: tabs (1 line) | hint bar (2 lines) | content | status bar (1 line)
9        let outer_chunks = Layout::default()
10            .direction(Direction::Vertical)
11            .constraints([
12                Constraint::Length(1), // tab bar
13                Constraint::Length(2), // hint bar
14                Constraint::Min(0),    // content
15                Constraint::Length(1), // status bar
16            ])
17            .split(area);
18
19        let tab_bar_area = outer_chunks[0];
20        let hint_bar_area = outer_chunks[1];
21        let content_area = outer_chunks[2];
22        // Update visible height based on actual terminal size (subtract borders/header)
23        self.visible_height = content_area.height.saturating_sub(2) as usize;
24        let bottom_bar_area = outer_chunks[3];
25
26        // Tab bar
27        let tab_titles = vec!["Mailbox", "Search", "Rules", "Accounts", "Diagnostics"];
28        let selected_tab = match self.screen {
29            Screen::Mailbox => 0,
30            Screen::Search => 1,
31            Screen::Rules => 2,
32            Screen::Accounts => 3,
33            Screen::Diagnostics => 4,
34        };
35        let tabs = ratatui::widgets::Tabs::new(tab_titles)
36            .select(selected_tab)
37            .style(Style::default().fg(theme.text_muted))
38            .highlight_style(Style::default().fg(theme.accent).bold())
39            .divider(Span::styled(" | ", Style::default().fg(theme.text_muted)));
40        frame.render_widget(tabs, tab_bar_area);
41
42        // Hint bar
43        ui::hint_bar::draw(
44            frame,
45            hint_bar_area,
46            ui::hint_bar::HintBarState {
47                screen: self.screen,
48                active_pane: &self.active_pane,
49                search_active: self.search_bar.active,
50                help_modal_open: self.help_modal_open,
51                selected_count: self.selected_set.len(),
52                bulk_confirm_open: self.pending_bulk_confirm.is_some(),
53                sync_status: self.last_sync_status.clone(),
54            },
55            theme,
56        );
57
58        match self.screen {
59            Screen::Mailbox => match self.layout_mode {
60                LayoutMode::TwoPane => {
61                    let chunks = Layout::default()
62                        .direction(Direction::Horizontal)
63                        .constraints([Constraint::Percentage(20), Constraint::Percentage(80)])
64                        .split(content_area);
65
66                    ui::sidebar::draw(
67                        frame,
68                        chunks[0],
69                        &ui::sidebar::SidebarView {
70                            labels: &self.labels,
71                            active_pane: &self.active_pane,
72                            saved_searches: &self.saved_searches,
73                            sidebar_selected: self.sidebar_selected,
74                            all_mail_active: !self.search_active
75                                && self.mailbox_view == MailboxView::Messages
76                                && self.active_label.is_none()
77                                && self.pending_active_label.is_none(),
78                            subscriptions_active: self.mailbox_view == MailboxView::Subscriptions,
79                            subscription_count: self.subscriptions_page.entries.len(),
80                            system_expanded: self.sidebar_system_expanded,
81                            user_expanded: self.sidebar_user_expanded,
82                            saved_searches_expanded: self.sidebar_saved_searches_expanded,
83                            active_label: self
84                                .pending_active_label
85                                .as_ref()
86                                .or(self.active_label.as_ref()),
87                        },
88                        theme,
89                    );
90
91                    if self.mailbox_view == MailboxView::Subscriptions {
92                        ui::subscriptions_page::draw(
93                            frame,
94                            chunks[1],
95                            &ui::subscriptions_page::SubscriptionsPageView {
96                                entries: &self.subscriptions_page.entries,
97                                selected_index: self.selected_index,
98                                scroll_offset: self.scroll_offset,
99                                active_pane: &self.active_pane,
100                                preview_blocks: &self.thread_message_blocks(),
101                                message_scroll_offset: self.message_scroll_offset,
102                            },
103                            theme,
104                        );
105                    } else {
106                        let mail_title = self.mail_list_title();
107                        ui::mail_list::draw_view(
108                            frame,
109                            chunks[1],
110                            &ui::mail_list::MailListView {
111                                rows: &self.mail_list_rows(),
112                                selected_index: self.selected_index,
113                                scroll_offset: self.scroll_offset,
114                                active_pane: &self.active_pane,
115                                title: &mail_title,
116                                selected_set: &self.selected_set,
117                                mode: self.mail_list_mode,
118                            },
119                            theme,
120                        );
121                    }
122                }
123                LayoutMode::ThreePane => {
124                    let chunks = Layout::default()
125                        .direction(Direction::Horizontal)
126                        .constraints([Constraint::Percentage(15), Constraint::Percentage(85)])
127                        .split(content_area);
128
129                    ui::sidebar::draw(
130                        frame,
131                        chunks[0],
132                        &ui::sidebar::SidebarView {
133                            labels: &self.labels,
134                            active_pane: &self.active_pane,
135                            saved_searches: &self.saved_searches,
136                            sidebar_selected: self.sidebar_selected,
137                            all_mail_active: !self.search_active
138                                && self.mailbox_view == MailboxView::Messages
139                                && self.active_label.is_none()
140                                && self.pending_active_label.is_none(),
141                            subscriptions_active: self.mailbox_view == MailboxView::Subscriptions,
142                            subscription_count: self.subscriptions_page.entries.len(),
143                            system_expanded: self.sidebar_system_expanded,
144                            user_expanded: self.sidebar_user_expanded,
145                            saved_searches_expanded: self.sidebar_saved_searches_expanded,
146                            active_label: self
147                                .pending_active_label
148                                .as_ref()
149                                .or(self.active_label.as_ref()),
150                        },
151                        theme,
152                    );
153
154                    if self.mailbox_view == MailboxView::Subscriptions {
155                        ui::subscriptions_page::draw(
156                            frame,
157                            chunks[1],
158                            &ui::subscriptions_page::SubscriptionsPageView {
159                                entries: &self.subscriptions_page.entries,
160                                selected_index: self.selected_index,
161                                scroll_offset: self.scroll_offset,
162                                active_pane: &self.active_pane,
163                                preview_blocks: &self.thread_message_blocks(),
164                                message_scroll_offset: self.message_scroll_offset,
165                            },
166                            theme,
167                        );
168                    } else {
169                        let inner = Layout::default()
170                            .direction(Direction::Horizontal)
171                            .constraints([Constraint::Percentage(41), Constraint::Percentage(59)])
172                            .split(chunks[1]);
173                        let mail_title = self.mail_list_title();
174                        ui::mail_list::draw_view(
175                            frame,
176                            inner[0],
177                            &ui::mail_list::MailListView {
178                                rows: &self.mail_list_rows(),
179                                selected_index: self.selected_index,
180                                scroll_offset: self.scroll_offset,
181                                active_pane: &self.active_pane,
182                                title: &mail_title,
183                                selected_set: &self.selected_set,
184                                mode: self.mail_list_mode,
185                            },
186                            theme,
187                        );
188                        ui::message_view::draw(
189                            frame,
190                            inner[1],
191                            &self.thread_message_blocks(),
192                            self.message_scroll_offset,
193                            &self.active_pane,
194                            theme,
195                        );
196                    }
197                }
198                LayoutMode::FullScreen => {
199                    if self.mailbox_view == MailboxView::Subscriptions {
200                        ui::subscriptions_page::draw(
201                            frame,
202                            content_area,
203                            &ui::subscriptions_page::SubscriptionsPageView {
204                                entries: &self.subscriptions_page.entries,
205                                selected_index: self.selected_index,
206                                scroll_offset: self.scroll_offset,
207                                active_pane: &self.active_pane,
208                                preview_blocks: &self.thread_message_blocks(),
209                                message_scroll_offset: self.message_scroll_offset,
210                            },
211                            theme,
212                        );
213                    } else {
214                        ui::message_view::draw(
215                            frame,
216                            content_area,
217                            &self.thread_message_blocks(),
218                            self.message_scroll_offset,
219                            &self.active_pane,
220                            theme,
221                        );
222                    }
223                }
224            },
225            Screen::Search => {
226                let rows = self.search_mail_list_rows();
227                ui::search_page::draw(
228                    frame,
229                    content_area,
230                    &self.search_page,
231                    &rows,
232                    self.mail_list_mode,
233                    &self.thread_message_blocks(),
234                    self.message_scroll_offset,
235                    theme,
236                );
237            }
238            Screen::Rules => {
239                ui::rules_page::draw(frame, content_area, &self.rules_page, theme);
240            }
241            Screen::Diagnostics => {
242                ui::diagnostics_page::draw(frame, content_area, &self.diagnostics_page, theme);
243            }
244            Screen::Accounts => {
245                ui::accounts_page::draw(frame, content_area, &self.accounts_page, theme);
246            }
247        }
248
249        let status_bar = self.status_bar_state();
250        ui::status_bar::draw(frame, bottom_bar_area, &status_bar, theme);
251
252        if self.search_bar.active {
253            ui::search_bar::draw(frame, area, &self.search_bar, theme);
254        }
255
256        // Command palette overlay
257        ui::command_palette::draw(frame, area, &self.command_palette, theme);
258
259        // Label picker overlay
260        ui::label_picker::draw(frame, area, &self.label_picker, theme);
261
262        // Compose picker overlay
263        ui::compose_picker::draw(frame, area, &self.compose_picker, theme);
264
265        // Attachment overlay
266        ui::attachment_modal::draw(frame, area, &self.attachment_panel, theme);
267
268        // URL picker overlay
269        ui::url_modal::draw(frame, area, self.url_modal.as_ref(), theme);
270
271        // Snooze overlay
272        ui::snooze_modal::draw(frame, area, &self.snooze_panel, &self.snooze_config, theme);
273
274        // Send confirmation overlay
275        ui::send_confirm_modal::draw(frame, area, self.pending_send_confirm.as_ref(), theme);
276
277        // Bulk confirmation overlay
278        ui::bulk_confirm_modal::draw(frame, area, self.pending_bulk_confirm.as_ref(), theme);
279
280        // Unsubscribe confirmation overlay
281        ui::unsubscribe_modal::draw(
282            frame,
283            area,
284            self.pending_unsubscribe_confirm.as_ref(),
285            theme,
286        );
287
288        // Help overlay
289        ui::help_modal::draw(
290            frame,
291            area,
292            ui::help_modal::HelpModalState {
293                open: self.help_modal_open,
294                screen: self.screen,
295                active_pane: &self.active_pane,
296                selected_count: self.selected_set.len(),
297                scroll_offset: self.help_scroll_offset,
298            },
299            theme,
300        );
301    }
302}