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        layout: &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#[allow(clippy::large_enum_variant)]
71#[derive(Debug)]
72pub(crate) enum Ui {
73    Headless,
74    Tui(Tui),
75}
76
77impl From<EditorMode> for Ui {
78    fn from(mode: EditorMode) -> Self {
79        match mode {
80            EditorMode::Headless => Self::Headless,
81            EditorMode::Terminal => Self::Tui(Tui::new()),
82        }
83    }
84}
85
86impl UserInterface for Ui {
87    fn init(&mut self, tx: Sender<Event>) -> (usize, usize) {
88        match self {
89            Self::Headless => (60, 80),
90            Self::Tui(tui) => tui.init(tx),
91        }
92    }
93
94    fn shutdown(&mut self) {
95        match self {
96            Self::Headless => (),
97            Self::Tui(tui) => tui.shutdown(),
98        }
99    }
100
101    fn state_change(&mut self, change: StateChange) {
102        match self {
103            Self::Headless => (),
104            Self::Tui(tui) => tui.state_change(change),
105        }
106    }
107
108    fn refresh(
109        &mut self,
110        mode_name: &str,
111        layout: &Layout,
112        pending_keys: &[Input],
113        held_click: Option<&Click>,
114        mb: Option<MiniBufferState<'_>>,
115    ) {
116        match self {
117            Self::Headless => (),
118            Self::Tui(tui) => tui.refresh(mode_name, layout, pending_keys, held_click, mb),
119        }
120    }
121
122    fn set_cursor_shape(&mut self, cur_shape: CurShape) {
123        match self {
124            Self::Headless => (),
125            Self::Tui(tui) => tui.set_cursor_shape(cur_shape),
126        }
127    }
128}