Skip to main content

kimun_notes/components/
mod.rs

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