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}