use { super::{ events::{Action, Event}, tui::Frame, }, crossterm::event::{KeyEvent, MouseEvent}, downcast_rs::{impl_downcast, Downcast}, eyre::Result, ratatui::layout::{Rect, Size}, std::collections::HashMap, tokio::sync::mpsc::UnboundedSender, }; pub type Children = HashMap>; // TODO: create a component! macro to simplify the creation of components, adding the boilerplate // code, similar to what we do with the `rustler!` macro in the `rustler` crate. /// `Component` is a trait that represents a visual and interactive element of the user interface. /// Implementors of this trait can be registered with the main application loop and will be able to /// receive events, /// update state, and be rendered on the screen. pub trait Component: Downcast { /// Register an action handler that can send actions for processing if necessary. /// /// # Arguments /// /// * `tx` - An unbounded sender that can send actions. /// /// # Returns /// /// * `Result<()>` - An Ok result or an error. #[allow(unused_variables)] fn register_action_handler(&mut self, tx: UnboundedSender) -> Result<()> { pass_action_handler_to_children(self, tx) } /// Initialize the component with a specified area if necessary. /// By default, this method will pass the initialization to the children. If you want to /// override this method, you will lose calling the children's `init` method. That's why we /// provide a helper function to initialize the children. Something like the following is /// recommended: /// /// ```rust /// fn init(&mut self, area: Rect) -> Result<()> { /// // Do something with the area /// /// // Initialize the children /// init_children(self, area) /// } /// ``` /// /// # Arguments /// /// * `area` - Rectangular area to initialize the component within. /// /// # Returns /// /// * `Result<()>` - An Ok result or an error. #[allow(unused)] fn init(&mut self, area: Size) -> Result<()> { init_children(self, area) } /// Handle incoming events and produce actions if necessary. /// In most cases, you should avoid overriding this method, as it will handle the children's /// events and also rerout actions to `self.handle_key_events` and `self.handle_mouse_events`. /// /// In most cases, you might want to implement those two methods instead of this one. /// /// # Arguments /// /// * `event` - An optional event to be processed. /// /// # Returns /// /// * `Result>` - An action to be processed or none. fn handle_events(&mut self, event: Option) -> Result> { let mut actions = vec![]; let action = match event { Some(Event::Key(key_event)) => self.handle_key_events(key_event)?, Some(Event::Mouse(mouse_event)) => self.handle_mouse_events(mouse_event)?, _ => None, }; if let Some(action) = action { actions.push(action); } if let Some(children) = self.get_children() { for child in children.values_mut() { let child_actions = child.handle_events(event.clone())?; actions.extend(child_actions); } } Ok(actions) } /// Handle key events and produce actions if necessary. /// /// # Arguments /// /// * `key` - A key event to be processed. /// /// # Returns /// /// * `Result>` - An action to be processed or none. #[allow(unused_variables)] fn handle_key_events(&mut self, key: KeyEvent) -> Result> { Ok(None) } /// Handle mouse events and produce actions if necessary. /// /// # Arguments /// /// * `mouse` - A mouse event to be processed. /// /// # Returns /// /// * `Result>` - An action to be processed or none. #[allow(unused_variables)] fn handle_mouse_events(&mut self, mouse: MouseEvent) -> Result> { Ok(None) } /// Update the state of the component based on a received action. If you want to override this /// method, you will lose calling the children's update methods. That's why we provide a helper /// function to update the children's state. Something like the following is recommended: /// /// ```rust /// fn update(&mut self, action: Action) -> Result<()> { /// // Do something with the action /// /// // Update the children /// update_children(self, action) /// } /// ``` /// /// # Arguments /// /// * `action` - An action that may modify the state of the component. /// /// # Returns /// /// * `Result>` - An action to be processed or none. #[allow(unused_variables)] fn update(&mut self, action: Action) -> Result<()> { update_children(self, action) } /// Receive a custom message, probably from another component. /// If you want to override this method, you will lose calling the children's `receive_message` /// method. That's why we provide a helper function to pass the message. Something like the /// following is recommended: /// /// ```rust /// fn receive_message(&mut self, message: String) -> Result<()> { /// // Do something with the message /// /// // Pass the message to the children /// pass_message_to_children(self, message) /// } /// ``` /// # Arguments /// /// * `message` - A string message to be processed. /// /// # Returns /// /// * `Result<()>` - An Ok result or an error. #[allow(unused_variables)] fn receive_message(&mut self, message: String) -> Result<()> { pass_message_to_children(self, message) } /// Render the component on the screen. (REQUIRED) /// /// # Arguments /// /// * `f` - A frame used for rendering. /// * `area` - The area in which the component should be drawn. /// /// # Returns /// /// * `Result<()>` - An Ok result or an error. fn draw(&mut self, f: &mut Frame<'_>, area: Rect) -> Result<()>; /// Get a child component by name as a mutable reference. /// /// # Arguments /// * `name` - The name of the child component. /// /// # Returns /// * `Option<&mut Box>` - A mutable reference to the child component or none. fn child_mut(&mut self, name: &str) -> Option<&mut Box> { if let Some(children) = self.get_children() { children.get_mut(name) } else { None } } /// Get a child component by name as an immutable reference. /// /// # Arguments /// * `name` - The name of the child component. /// /// # Returns /// * `Option<&Box>` - A reference to the child component or none. fn child(&mut self, name: &str) -> Option<&Box> { if let Some(children) = self.get_children() { children.get(name) } else { None } } /// get all child components. This is necessary if the component has children, as will be /// used by other functions to have knowledge of the children. /// /// # Attributes /// * `children`: `HashMap>` - All child components. /// /// # Returns /// /// * `Vec[&mut Box]` - A vector of mutable references to the child components. fn get_children(&mut self) -> Option<&mut Children> { None } /// gets the active state of the component. /// /// # Returns /// * `bool` - The active state of the component. fn is_active(&self) -> bool { true } /// Set the active state of the component. /// /// # Arguments /// * `active` - The active state of the component. fn set_active(&mut self, _active: bool) {} } impl_downcast!(Component); /// Update the children's state based on a received action. /// /// This helper function is used to update the children's state based on a received action. It was /// created to allow to easily override the default `update` method of a component implementation and /// be able to call the children's `update` method. pub fn update_children(this: &mut T, action: Action) -> Result<()> { if let Some(children) = this.get_children() { for child in children.values_mut() { child.update(action.clone())?; } } Ok(()) } /// Pass a message to the children of a component. /// /// This helper function is used to pass a message to the children of a component. It was created /// to allow to easily override the default `receive_message` method of a component implementation /// and be able pass the call to the children's `receive_message` method. pub fn pass_message_to_children( this: &mut T, message: String, ) -> Result<()> { if let Some(children) = this.get_children() { for child in children.values_mut() { child.receive_message(message.clone())?; } } Ok(()) } /// Initialize the children of a component. /// /// This helper function is used to initialize the children of a component. It was created to /// allow to easily override the default `init` method of a component implementation and be able /// to call the children's `init` method. pub fn init_children(this: &mut T, area: Size) -> Result<()> { if let Some(children) = this.get_children() { for child in children.values_mut() { child.init(area)?; } } Ok(()) } /// Pass the action handler to the children of a component. /// /// This helper function is used to pass the action handler to the children of a component. It was /// created to allow to easily override the default `register_action_handler` method of a component /// implementation and be able to call the children's `register_action_handler` method. pub fn pass_action_handler_to_children( this: &mut T, tx: UnboundedSender, ) -> Result<()> { if let Some(children) = this.get_children() { for child in children.values_mut() { child.register_action_handler(tx.clone())?; } } Ok(()) } /// Get a child downcasted to a specific type by name as a mutable reference. /// /// # Arguments /// * `name` - The name of the child component. /// /// # Returns /// * `Option<&mut T>` - A mutable reference to the child component or none. pub fn child_downcast_mut<'a, CastTo: Component, This: Component + ?Sized>( this: &'a mut This, name: &str, ) -> Option<&'a mut CastTo> { if let Some(child) = this.child_mut(name) { child.downcast_mut::() } else { None } } /// Get a child downcasted to a specific type by name as an immutable reference. /// /// # Arguments /// * `name` - The name of the child component. /// /// # Returns /// * `Option<&T>` - A reference to the child component or none. pub fn child_downcast<'a, CastTo: Component, This: Component + ?Sized>( this: &'a mut This, name: &str, ) -> Option<&'a CastTo> { if let Some(child) = this.child(name) { child.downcast_ref::() } else { None } }