1 // Each component declares it's own size constraints and gets fitted based on it's parent.
2 // Q: how does this work with popups?
3 // cursive does compositor.screen_mut().add_layer_at(pos::absolute(x, y), <component>)
4 use helix_core::Position;
5 use helix_view::graphics::{CursorKind, Rect};
6 
7 use crossterm::event::Event;
8 use tui::buffer::Buffer as Surface;
9 
10 pub type Callback = Box<dyn FnOnce(&mut Compositor)>;
11 
12 // --> EventResult should have a callback that takes a context with methods like .popup(),
13 // .prompt() etc. That way we can abstract it from the renderer.
14 // Q: How does this interact with popups where we need to be able to specify the rendering of the
15 // popup?
16 // A: It could just take a textarea.
17 //
18 // If Compositor was specified in the callback that's then problematic because of
19 
20 // Cursive-inspired
21 pub enum EventResult {
22     Ignored,
23     Consumed(Option<Callback>),
24 }
25 
26 use helix_view::Editor;
27 
28 use crate::job::Jobs;
29 
30 pub struct Context<'a> {
31     pub editor: &'a mut Editor,
32     pub scroll: Option<usize>,
33     pub jobs: &'a mut Jobs,
34 }
35 
36 pub trait Component: Any + AnyComponent {
37     /// Process input events, return true if handled.
handle_event(&mut self, _event: Event, _ctx: &mut Context) -> EventResult38     fn handle_event(&mut self, _event: Event, _ctx: &mut Context) -> EventResult {
39         EventResult::Ignored
40     }
41     // , args: ()
42 
43     /// Should redraw? Useful for saving redraw cycles if we know component didn't change.
should_update(&self) -> bool44     fn should_update(&self) -> bool {
45         true
46     }
47 
48     /// Render the component onto the provided surface.
render(&mut self, area: Rect, frame: &mut Surface, ctx: &mut Context)49     fn render(&mut self, area: Rect, frame: &mut Surface, ctx: &mut Context);
50 
51     /// Get cursor position and cursor kind.
cursor(&self, _area: Rect, _ctx: &Editor) -> (Option<Position>, CursorKind)52     fn cursor(&self, _area: Rect, _ctx: &Editor) -> (Option<Position>, CursorKind) {
53         (None, CursorKind::Hidden)
54     }
55 
56     /// May be used by the parent component to compute the child area.
57     /// viewport is the maximum allowed area, and the child should stay within those bounds.
required_size(&mut self, _viewport: (u16, u16)) -> Option<(u16, u16)>58     fn required_size(&mut self, _viewport: (u16, u16)) -> Option<(u16, u16)> {
59         // TODO: for scrolling, the scroll wrapper should place a size + offset on the Context
60         // that way render can use it
61         None
62     }
63 
type_name(&self) -> &'static str64     fn type_name(&self) -> &'static str {
65         std::any::type_name::<Self>()
66     }
67 }
68 
69 use anyhow::Error;
70 use std::io::stdout;
71 use tui::backend::{Backend, CrosstermBackend};
72 type Terminal = tui::terminal::Terminal<CrosstermBackend<std::io::Stdout>>;
73 
74 pub struct Compositor {
75     layers: Vec<Box<dyn Component>>,
76     terminal: Terminal,
77 
78     pub(crate) last_picker: Option<Box<dyn Component>>,
79 }
80 
81 impl Compositor {
new() -> Result<Self, Error>82     pub fn new() -> Result<Self, Error> {
83         let backend = CrosstermBackend::new(stdout());
84         let terminal = Terminal::new(backend)?;
85         Ok(Self {
86             layers: Vec::new(),
87             terminal,
88             last_picker: None,
89         })
90     }
91 
size(&self) -> Rect92     pub fn size(&self) -> Rect {
93         self.terminal.size().expect("couldn't get terminal size")
94     }
95 
resize(&mut self, width: u16, height: u16)96     pub fn resize(&mut self, width: u16, height: u16) {
97         self.terminal
98             .resize(Rect::new(0, 0, width, height))
99             .expect("Unable to resize terminal")
100     }
101 
save_cursor(&mut self)102     pub fn save_cursor(&mut self) {
103         if self.terminal.cursor_kind() == CursorKind::Hidden {
104             self.terminal
105                 .backend_mut()
106                 .show_cursor(CursorKind::Block)
107                 .ok();
108         }
109     }
110 
load_cursor(&mut self)111     pub fn load_cursor(&mut self) {
112         if self.terminal.cursor_kind() == CursorKind::Hidden {
113             self.terminal.backend_mut().hide_cursor().ok();
114         }
115     }
116 
push(&mut self, mut layer: Box<dyn Component>)117     pub fn push(&mut self, mut layer: Box<dyn Component>) {
118         let size = self.size();
119         // trigger required_size on init
120         layer.required_size((size.width, size.height));
121         self.layers.push(layer);
122     }
123 
pop(&mut self) -> Option<Box<dyn Component>>124     pub fn pop(&mut self) -> Option<Box<dyn Component>> {
125         self.layers.pop()
126     }
127 
handle_event(&mut self, event: Event, cx: &mut Context) -> bool128     pub fn handle_event(&mut self, event: Event, cx: &mut Context) -> bool {
129         // propagate events through the layers until we either find a layer that consumes it or we
130         // run out of layers (event bubbling)
131         for layer in self.layers.iter_mut().rev() {
132             match layer.handle_event(event, cx) {
133                 EventResult::Consumed(Some(callback)) => {
134                     callback(self);
135                     return true;
136                 }
137                 EventResult::Consumed(None) => return true,
138                 EventResult::Ignored => false,
139             };
140         }
141         false
142     }
143 
render(&mut self, cx: &mut Context)144     pub fn render(&mut self, cx: &mut Context) {
145         self.terminal
146             .autoresize()
147             .expect("Unable to determine terminal size");
148 
149         // TODO: need to recalculate view tree if necessary
150 
151         let surface = self.terminal.current_buffer_mut();
152 
153         let area = *surface.area();
154 
155         for layer in &mut self.layers {
156             layer.render(area, surface, cx);
157         }
158 
159         let (pos, kind) = self.cursor(area, cx.editor);
160         let pos = pos.map(|pos| (pos.col as u16, pos.row as u16));
161 
162         self.terminal.draw(pos, kind).unwrap();
163     }
164 
cursor(&self, area: Rect, editor: &Editor) -> (Option<Position>, CursorKind)165     pub fn cursor(&self, area: Rect, editor: &Editor) -> (Option<Position>, CursorKind) {
166         for layer in self.layers.iter().rev() {
167             if let (Some(pos), kind) = layer.cursor(area, editor) {
168                 return (Some(pos), kind);
169             }
170         }
171         (None, CursorKind::Hidden)
172     }
173 
has_component(&self, type_name: &str) -> bool174     pub fn has_component(&self, type_name: &str) -> bool {
175         self.layers
176             .iter()
177             .any(|component| component.type_name() == type_name)
178     }
179 
find(&mut self, type_name: &str) -> Option<&mut dyn Component>180     pub fn find(&mut self, type_name: &str) -> Option<&mut dyn Component> {
181         self.layers
182             .iter_mut()
183             .find(|component| component.type_name() == type_name)
184             .map(|component| component.as_mut())
185     }
186 }
187 
188 // View casting, taken straight from Cursive
189 
190 use std::any::Any;
191 
192 /// A view that can be downcasted to its concrete type.
193 ///
194 /// This trait is automatically implemented for any `T: Component`.
195 pub trait AnyComponent {
196     /// Downcast self to a `Any`.
as_any(&self) -> &dyn Any197     fn as_any(&self) -> &dyn Any;
198 
199     /// Downcast self to a mutable `Any`.
as_any_mut(&mut self) -> &mut dyn Any200     fn as_any_mut(&mut self) -> &mut dyn Any;
201 
202     /// Returns a boxed any from a boxed self.
203     ///
204     /// Can be used before `Box::downcast()`.
205     ///
206     /// # Examples
207     ///
208     /// ```rust
209     /// use helix_term::{ui::Text, compositor::Component};
210     /// let boxed: Box<dyn Component> = Box::new(Text::new("text".to_string()));
211     /// let text: Box<Text> = boxed.as_boxed_any().downcast().unwrap();
212     /// ```
as_boxed_any(self: Box<Self>) -> Box<dyn Any>213     fn as_boxed_any(self: Box<Self>) -> Box<dyn Any>;
214 }
215 
216 impl<T: Component> AnyComponent for T {
217     /// Downcast self to a `Any`.
as_any(&self) -> &dyn Any218     fn as_any(&self) -> &dyn Any {
219         self
220     }
221 
222     /// Downcast self to a mutable `Any`.
as_any_mut(&mut self) -> &mut dyn Any223     fn as_any_mut(&mut self) -> &mut dyn Any {
224         self
225     }
226 
as_boxed_any(self: Box<Self>) -> Box<dyn Any>227     fn as_boxed_any(self: Box<Self>) -> Box<dyn Any> {
228         self
229     }
230 }
231 
232 impl dyn AnyComponent {
233     /// Attempts to downcast `self` to a concrete type.
downcast_ref<T: Any>(&self) -> Option<&T>234     pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
235         self.as_any().downcast_ref()
236     }
237 
238     /// Attempts to downcast `self` to a concrete type.
downcast_mut<T: Any>(&mut self) -> Option<&mut T>239     pub fn downcast_mut<T: Any>(&mut self) -> Option<&mut T> {
240         self.as_any_mut().downcast_mut()
241     }
242 
243     /// Attempts to downcast `Box<Self>` to a concrete type.
downcast<T: Any>(self: Box<Self>) -> Result<Box<T>, Box<Self>>244     pub fn downcast<T: Any>(self: Box<Self>) -> Result<Box<T>, Box<Self>> {
245         // Do the check here + unwrap, so the error
246         // value is `Self` and not `dyn Any`.
247         if self.as_any().is::<T>() {
248             Ok(self.as_boxed_any().downcast().unwrap())
249         } else {
250             Err(self)
251         }
252     }
253 
254     /// Checks if this view is of type `T`.
is<T: Any>(&mut self) -> bool255     pub fn is<T: Any>(&mut self) -> bool {
256         self.as_any().is::<T>()
257     }
258 }
259