use {
super::{
events::{Action, Event},
tui::Frame,
},
crossterm::event::{KeyEvent, MouseEvent},
downcast_rs::{impl_downcast, Downcast},
ratatui::layout::{Rect, Size},
std::collections::HashMap,
tokio::sync::mpsc::UnboundedSender,
};
pub type Children = HashMap<String, Box<dyn Component>>;
pub(crate) struct ComponentHandler {
c: Box<dyn Component>,
}
impl ComponentHandler {
pub fn for_(component: Box<dyn Component>) -> Self {
Self { c: component }
}
pub(crate) fn handle_init(&mut self, area: Size) {
init(self.c.as_mut(), area);
}
pub(crate) fn receive_action_handler(&mut self, tx: UnboundedSender<String>) {
receive_action_handler(self.c.as_mut(), tx);
}
pub(crate) fn handle_events(&mut self, event: Option<Event>) -> Vec<Action> {
handle_event_for(event, self.c.as_mut())
}
pub(crate) fn handle_update(&mut self, action: Action) {
update(self.c.as_mut(), &action);
}
pub(crate) fn handle_message(&mut self, message: String) {
handle_message(self.c.as_mut(), message);
}
pub(crate) fn handle_draw(&mut self, f: &mut Frame<'_>, area: Rect) {
if self.c.is_active() {
self.c.draw(f, area);
}
}
}
pub trait Component: Downcast + ComponentAccessors {
#[allow(unused)]
fn init(&mut self, area: Size) {}
#[allow(unused_variables)]
fn handle_key_events(&mut self, key: KeyEvent) -> Option<Action> {
None
}
#[allow(unused_variables)]
fn handle_mouse_events(&mut self, mouse: MouseEvent) -> Option<Action> {
None
}
#[allow(unused_variables)]
fn handle_tick_event(&mut self) -> Option<Action> {
None
}
#[allow(unused_variables)]
fn handle_frame_event(&mut self) -> Option<Action> {
None
}
#[allow(unused_variables)]
fn handle_paste_event(&mut self, message: String) -> Option<Action> {
None
}
#[allow(unused_variables)]
fn update(&mut self, action: &Action) {}
#[allow(unused_variables)]
fn receive_message(&mut self, message: String) {}
fn draw(&mut self, f: &mut Frame<'_>, area: Rect);
fn child_mut(&mut self, name: &str) -> Option<&mut Box<dyn Component>> {
if let Some(children) = self.get_children() {
children.get_mut(name)
} else {
None
}
}
#[allow(clippy::borrowed_box)]
fn child(&mut self, name: &str) -> Option<&Box<dyn Component>> {
if let Some(children) = self.get_children() {
children.get(name)
} else {
None
}
}
#[allow(unused_variables)]
fn on_active_changed(&mut self, active: bool) {}
}
impl_downcast!(Component);
fn update<T: Component + ?Sized>(c: &mut T, action: &Action) {
if c.is_active() {
c.update(&action);
if let Some(children) = c.get_children() {
for child in children.values_mut() {
update(child.as_mut(), action);
}
}
}
}
fn handle_message<T: Component + ?Sized>(c: &mut T, message: String) {
if c.is_active() {
c.receive_message(message.clone());
if let Some(children) = c.get_children() {
for child in children.values_mut() {
handle_message(child.as_mut(), message.clone());
}
}
}
}
fn init<T: Component + ?Sized>(c: &mut T, area: Size) {
c.init(area);
if let Some(children) = c.get_children() {
for child in children.values_mut() {
init(child.as_mut(), area);
}
}
}
fn receive_action_handler<T: Component + ?Sized>(c: &mut T, tx: UnboundedSender<String>) {
c.register_action_handler(tx.clone());
if let Some(children) = c.get_children() {
for child in children.values_mut() {
receive_action_handler(child.as_mut(), tx.clone());
}
}
}
fn handle_event_for<T: Component + ?Sized>(event: Option<Event>, c: &mut T) -> Vec<Action> {
if c.is_active() {
let mut actions = vec![];
let action = match event {
Some(Event::Key(key_event)) => c.handle_key_events(key_event),
Some(Event::Mouse(mouse_event)) => c.handle_mouse_events(mouse_event),
Some(Event::Tick) => c.handle_tick_event(),
Some(Event::Render) => c.handle_frame_event(),
Some(Event::Paste(ref event)) => c.handle_paste_event(event.clone()),
_ => None,
};
if let Some(action) = action {
actions.push(action);
}
if let Some(children) = c.get_children() {
for child in children.values_mut() {
let child_actions = handle_event_for(event.clone(), child.as_mut());
actions.extend(child_actions);
}
}
actions
} else {
vec![]
}
}
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::<CastTo>()
} else {
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::<CastTo>()
} else {
None
}
}
pub trait ComponentAccessors {
fn name(&self) -> String;
fn is_active(&self) -> bool;
fn set_active(&mut self, active: bool);
fn register_action_handler(&mut self, tx: UnboundedSender<String>);
fn send(&self, action: &str);
fn send_action(&self, action: Action);
#[allow(clippy::wrong_self_convention)]
fn as_active(self) -> Self
where
Self: Sized;
fn get_children(&mut self) -> Option<&mut Children>;
}