ad_editor/ui/
mod.rs

1//! The ad user interface
2use crate::{
3    editor::{Click, EditorMode, MiniBufferState},
4    input::Event,
5    key::Input,
6    term::CurShape,
7};
8use std::sync::mpsc::Sender;
9
10mod layout;
11mod tui;
12
13pub(crate) use layout::Layout;
14pub use tui::Tui;
15
16pub(crate) trait UserInterface {
17    /// Initialise the UI and start processing events.
18    ///
19    /// Called before entering the main editor event loop
20    fn init(&mut self, tx: Sender<Event>) -> (usize, usize);
21
22    /// Called when the editor event loop exits cleanly.
23    fn shutdown(&mut self);
24
25    /// Update internal state based on an Editor state change without rendering.
26    fn state_change(&mut self, change: StateChange);
27
28    /// Refresh the ui to display the current editor state.
29    fn refresh(
30        &mut self,
31        mode_name: &str,
32        windows: &Layout,
33        pending_keys: &[Input],
34        held_click: Option<&Click>,
35        mb: Option<MiniBufferState<'_>>,
36    );
37
38    /// Called when the editor mode changes and a new cursor shape is required
39    fn set_cursor_shape(&mut self, cur_shape: CurShape);
40}
41
42/// Sent by the [Editor] to a [Ui] when internal state has changed in such a way that
43/// a UI update _may_ be required.
44///
45/// In cases where the data is cheap to pass directly it is included, otherwise updates
46/// to the editor state can be requested through the `provide_buf_reqs` method.
47#[derive(Debug, Clone)]
48pub(crate) enum StateChange {
49    // /// The active buffer has been updated
50    // ActiveBuffer { id: usize },
51    // /// The given buffer has been closed
52    // BufferClosed { id: usize },
53    // /// A new buffer has been opened.
54    // /// If replace_active is true then the user wants to replace the active buffer
55    // /// with the id specified, if it is false then they want to open a new UI
56    // /// window to contain the buffer.
57    // BufferOpen { id: usize, replace_active: bool },
58    // /// The given buffer has had its contents modified in some way
59    // BufferModified { id: usize },
60    // /// The dot for the given buffer has been updated
61    // BufferDotUpdated { id: usize },
62    /// User level config has been modified in some way
63    ConfigUpdated,
64    // /// The tag for the given buffer has been updated
65    // TagModified { id: usize },
66    /// A new status message has been set
67    StatusMessage { msg: String },
68}
69
70#[derive(Debug)]
71pub(crate) enum Ui {
72    Headless,
73    Tui(Tui),
74}
75
76impl From<EditorMode> for Ui {
77    fn from(mode: EditorMode) -> Self {
78        match mode {
79            EditorMode::Headless => Self::Headless,
80            EditorMode::Terminal => Self::Tui(Tui::new()),
81        }
82    }
83}
84
85impl UserInterface for Ui {
86    fn init(&mut self, tx: Sender<Event>) -> (usize, usize) {
87        match self {
88            Self::Headless => (60, 80),
89            Self::Tui(tui) => tui.init(tx),
90        }
91    }
92
93    fn shutdown(&mut self) {
94        match self {
95            Self::Headless => (),
96            Self::Tui(tui) => tui.shutdown(),
97        }
98    }
99
100    fn state_change(&mut self, change: StateChange) {
101        match self {
102            Self::Headless => (),
103            Self::Tui(tui) => tui.state_change(change),
104        }
105    }
106
107    fn refresh(
108        &mut self,
109        mode_name: &str,
110        windows: &Layout,
111        pending_keys: &[Input],
112        held_click: Option<&Click>,
113        mb: Option<MiniBufferState<'_>>,
114    ) {
115        match self {
116            Self::Headless => (),
117            Self::Tui(tui) => tui.refresh(mode_name, windows, pending_keys, held_click, mb),
118        }
119    }
120
121    fn set_cursor_shape(&mut self, cur_shape: CurShape) {
122        match self {
123            Self::Headless => (),
124            Self::Tui(tui) => tui.set_cursor_shape(cur_shape),
125        }
126    }
127}