Skip to main content

kimun_notes/components/
mod.rs

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