use std::borrow::Cow;
use std::fmt::{Debug, Display, Formatter};
use std::sync::atomic::{AtomicUsize, Ordering};
use log::debug;
use crate::config::theme::Theme;
use crate::experiments::screenspace::Screenspace;
use crate::io::input_event::InputEvent;
use crate::io::output::Output;
use crate::primitives::xy::XY;
use crate::widget::any_msg::AnyMsg;
use crate::widget::context_bar_item::ContextBarItem;
use crate::widget::fill_policy::SizePolicy;
pub type WidgetAction<W> = Box<dyn Fn(&W) -> Option<Box<dyn AnyMsg>>>;
pub type WidgetActionParam<W, P> = Box<dyn Fn(&W, P) -> Option<Box<dyn AnyMsg>>>;
pub type WID = usize;
pub trait Widget: 'static {
fn id(&self) -> WID;
fn static_typename() -> &'static str
where
Self: Sized;
fn typename(&self) -> &'static str;
fn desc(&self) -> String {
format!("{}{}", self.typename(), self.id())
}
fn prelayout(&mut self) {}
fn full_size(&self) -> XY;
fn size_policy(&self) -> SizePolicy {
SizePolicy::SELF_DETERMINED
}
fn layout(&mut self, screenspace: Screenspace);
fn on_input(&self, input_event: InputEvent) -> Option<Box<dyn AnyMsg>>;
fn update(&mut self, msg: Box<dyn AnyMsg>) -> Option<Box<dyn AnyMsg>>;
fn get_focused(&self) -> Option<&dyn Widget> {
None
}
fn get_focused_mut(&mut self) -> Option<&mut dyn Widget> {
None
}
fn render(&self, theme: &Theme, focused: bool, output: &mut dyn Output);
fn kite(&self) -> XY {
XY::ZERO
}
fn as_any(&self) -> &dyn Widget
where
Self: Sized,
{
self as &dyn Widget
}
fn as_any_mut(&mut self) -> &mut dyn Widget
where
Self: Sized,
{
self as &mut dyn Widget
}
fn act_on(&mut self, input_event: InputEvent) -> (bool, Option<Box<dyn AnyMsg>>) {
let self_desc = self.desc();
debug!(target: "act_on", "1: {} acting on input {:?}", &self_desc, &input_event);
self.pre_act_on(&input_event);
let (consumed, message_to_self_op) = if let Some(child) = self.get_focused_mut() {
let result = child.act_on(input_event);
debug!(target: "act_on", "2: {}'s child {} consumed ({}), and returned message {:?}", &self_desc, child.desc(), result.0, result.1);
result
} else {
debug!(target: "act_on", "2: {} has no focused child (end of focus path)", &self_desc);
(false, None)
};
if let Some(message_to_self) = message_to_self_op {
debug_assert!(consumed, "one can't return a message without consuming input in Bernardo paradigm");
debug!(target: "act_on", "3: {} will update on message {:?}", &self_desc, &message_to_self);
let message_to_parent = self.update(message_to_self);
debug!(target: "act_on", "4: {} after update produced message {:?} to it's parent", &self_desc, &message_to_parent);
return (true, message_to_parent);
} else {
debug!(target: "act_on", "3: {} has no message_to_self", &self_desc);
}
if !consumed {
debug!(target: "act_on", "5: {}'s children did not consume input, considering themselves", &self_desc);
if let Some(msg) = self.on_input(input_event) {
debug!(target: "act_on", "6: {} consumed, produced {:?}", &self_desc, &msg);
let result = (true, self.update(msg));
debug!(target: "act_on", "7: {} produced {:?} message", &self_desc, &result.1);
return result;
}
}
(consumed, None)
}
fn is_focusable(&self) -> bool {
true
}
fn pre_act_on(&mut self, _input_event: &InputEvent) {}
fn get_status_description(&self) -> Option<Cow<'_, str>> {
None
}
fn get_widget_actions(&self) -> Option<ContextBarItem> {
None
}
}
impl<'a> Debug for dyn Widget + 'a {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "W{}:{}", self.typename(), self.id())
}
}
impl<'a> Display for dyn Widget + 'a {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "W{}:{}", self.typename(), self.id())
}
}
pub fn get_new_widget_id() -> WID {
static COUNTER: AtomicUsize = AtomicUsize::new(1);
COUNTER.fetch_add(1, Ordering::Relaxed) as WID
}
pub const WIDGET_NONE: usize = 0;