Skip to main content

kimun_notes/components/
mod.rs

1pub mod activity_rail;
2pub mod attachment_view;
3pub mod autocomplete;
4pub mod autosave_timer;
5pub mod command_palette;
6pub mod config_panel;
7pub mod dialogs;
8pub mod dir_browser;
9pub mod drawer;
10pub mod drawer_views;
11pub mod event_state;
12pub mod events;
13pub mod file_list;
14pub mod footer_bar;
15pub mod hints;
16pub mod indexing;
17pub mod note_browser;
18pub mod overlay;
19pub mod panel;
20pub mod preferences;
21pub mod preview_highlight;
22pub mod preview_pane;
23pub mod query_highlight;
24pub mod query_list_panel;
25pub mod query_panel;
26pub mod query_vars;
27pub mod rag_answer;
28pub mod rich_row;
29pub mod saved_search_breadcrumb;
30pub mod saved_searches_modal;
31pub mod search_list;
32pub mod semantic_search;
33pub mod sidebar;
34pub mod single_line_input;
35pub mod text_editor;
36pub mod which_key;
37
38use ratatui::Frame;
39use ratatui::layout::Rect;
40
41use crate::components::event_state::EventState;
42use crate::components::events::{AppTx, InputEvent};
43use crate::settings::themes::Theme;
44
45/// Centre a popup occupying `percent_x`% × `percent_y`% of `area`.
46/// A centered rect of fixed cell size, clamped to `r` — the counterpart to
47/// the percentage-based [`centered_rect`] for dialogs with intrinsic sizes.
48pub fn fixed_centered_rect(width: u16, height: u16, r: Rect) -> Rect {
49    let width = width.min(r.width);
50    let height = height.min(r.height);
51    Rect {
52        x: r.x + (r.width - width) / 2,
53        y: r.y + (r.height - height) / 2,
54        width,
55        height,
56    }
57}
58
59pub fn centered_rect(percent_x: u16, percent_y: u16, area: Rect) -> Rect {
60    let popup_height = (area.height as u32 * percent_y as u32 / 100) as u16;
61    let popup_width = (area.width as u32 * percent_x as u32 / 100) as u16;
62    Rect {
63        x: area.x + (area.width.saturating_sub(popup_width)) / 2,
64        y: area.y + (area.height.saturating_sub(popup_height)) / 2,
65        width: popup_width,
66        height: popup_height,
67    }
68}
69
70pub trait Component {
71    /// Handle an event. Send `AppEvent`s through `tx` for app-level effects.
72    /// Returns whether this component consumed the event.
73    fn handle_input(&mut self, event: &InputEvent, tx: &AppTx) -> EventState {
74        let _ = (event, tx);
75        EventState::NotConsumed
76    }
77
78    fn render(&mut self, f: &mut Frame, rect: Rect, theme: &Theme, focused: bool);
79
80    /// Context-sensitive shortcut hints shown in the hints bar when this
81    /// component is focused.  Each entry is `(key_display, label)`.
82    fn hint_shortcuts(&self) -> Vec<(String, String)> {
83        vec![]
84    }
85}
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90
91    #[test]
92    fn centered_rect_is_centered() {
93        let area = Rect {
94            x: 0,
95            y: 0,
96            width: 100,
97            height: 40,
98        };
99        let r = centered_rect(80, 75, area);
100        assert_eq!(r.width, 80);
101        assert_eq!(r.height, 30);
102        assert_eq!(r.x, 10); // (100 - 80) / 2
103        assert_eq!(r.y, 5); // (40 - 30) / 2
104    }
105
106    #[test]
107    fn centered_rect_does_not_underflow() {
108        // Very small area — must not panic.
109        let area = Rect {
110            x: 0,
111            y: 0,
112            width: 5,
113            height: 5,
114        };
115        let _ = centered_rect(80, 75, area);
116    }
117}