use std::any::Any;
use std::fmt::Debug;
use std::fmt::Display;
use std::fmt::Formatter;
use std::fmt::Result;
use std::future::Future;
use std::ops::Deref;
use std::pin::Pin;
use std::rc::Rc;
use std::slice::Iter;
#[cfg(debug_assertions)]
use std::sync::atomic::AtomicUsize;
#[cfg(debug_assertions)]
use std::sync::atomic::Ordering;
use async_trait::async_trait;
use crate::BBox;
use crate::Mergeable;
use crate::Placeholder;
use crate::Renderer;
use crate::Widget;
#[derive(Clone, Copy, Debug, Eq, Ord, Hash, PartialEq, PartialOrd)]
struct Index {
idx: usize,
}
impl Index {
fn new(idx: usize) -> Self {
Self { idx }
}
}
impl Display for Index {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
write!(f, "{}", self.idx)
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Id {
#[cfg(debug_assertions)]
ui_id: usize,
idx: Index,
}
impl Id {
#[allow(unused_variables)]
fn new<E, M>(idx: usize, ui: &Ui<E, M>) -> Id {
Self {
#[cfg(debug_assertions)]
ui_id: ui.id,
idx: Index::new(idx),
}
}
}
impl Display for Id {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
write!(f, "{}", self.idx)
}
}
#[async_trait(?Send)]
trait Hooker<E, M> {
async fn invoke(
&self,
ui: &mut Ui<E, M>,
pre_hook_event: Option<E>,
unhandled: Option<E>,
event: Option<&E>,
) -> Option<E>;
}
fn merge<E>(e1: Option<E>, e2: Option<E>) -> Option<E>
where
E: Mergeable,
{
match (e1, e2) {
(Some(e1), Some(e2)) => Some(e1.merge_with(e2)),
(Some(e), None) | (None, Some(e)) => Some(e),
(None, None) => None,
}
}
struct Hooked {}
#[async_trait(?Send)]
impl<E, M> Hooker<E, M> for Hooked
where
E: Mergeable,
{
async fn invoke(
&self,
ui: &mut Ui<E, M>,
pre_hook_event: Option<E>,
unhandled: Option<E>,
event: Option<&E>,
) -> Option<E> {
let mut result = None;
for idx in ui.hooked.clone().as_ref() {
match &ui.widgets[idx.idx].0.event_hook {
Some(hook_fn) => {
let widget = ui.widgets[idx.idx].1.clone();
let event = hook_fn.0(widget.as_ref(), ui, event).await;
result = merge(result, event);
},
None => debug_assert!(false, "Widget registered as hooked but no hook func found"),
};
}
merge(merge(pre_hook_event, unhandled), result)
}
}
struct NotHooked {}
#[async_trait(?Send)]
impl<E, M> Hooker<E, M> for NotHooked {
async fn invoke(
&self,
_ui: &mut Ui<E, M>,
pre_hook_event: Option<E>,
unhandled: Option<E>,
_event: Option<&E>,
) -> Option<E> {
debug_assert!(pre_hook_event.is_none());
unhandled
}
}
pub(crate) type ChildIter<'widget> = Iter<'widget, Id>;
type NewDataFn = dyn FnOnce() -> Box<dyn Any>;
type NewWidgetFn<E, M> = dyn FnOnce(Id, &mut dyn MutCap<E, M>) -> Box<dyn Widget<E, M>>;
type EventHookFn<E, M> = &'static dyn for<'f> Fn(
&'f dyn Widget<E, M>,
&'f mut dyn MutCap<E, M>,
Option<&'f E>,
) -> Pin<Box<dyn Future<Output = Option<E>> + 'f>>;
mod private {
pub trait Sealed {}
}
pub trait Cap: Debug + private::Sealed {
fn data(&self, widget: Id) -> &dyn Any;
fn children(&self, widget: Id) -> ChildIter<'_>;
fn root_id(&self) -> Id;
fn parent_id(&self, widget: Id) -> Option<Id>;
fn is_visible(&self, widget: Id) -> bool;
fn is_displayed(&self, widget: Id) -> bool;
fn focused(&self) -> Option<Id>;
fn is_focused(&self, widget: Id) -> bool;
}
#[async_trait(?Send)]
pub trait MutCap<E, M>: Cap + Deref<Target = dyn Cap> {
fn data_mut(&mut self, widget: Id) -> &mut dyn Any;
fn add_widget(
&mut self,
parent: Id,
new_data: Box<NewDataFn>,
new_widget: Box<NewWidgetFn<E, M>>,
) -> Id;
fn show(&mut self, widget: Id);
fn hide(&mut self, widget: Id);
fn focus(&mut self, widget: Id);
fn hook_events(
&mut self,
widget: Id,
hook_fn: Option<EventHookFn<E, M>>,
) -> Option<EventHookFn<E, M>>
where
E: Mergeable;
async fn send(&mut self, widget: Id, message: M) -> Option<M>;
async fn call(&mut self, widget: Id, message: &mut M) -> Option<M>;
}
#[cfg(debug_assertions)]
fn get_next_ui_id() -> usize {
static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
NEXT_ID.fetch_add(1, Ordering::Relaxed)
}
#[derive(Debug)]
struct WidgetData<E, M>
where
E: 'static,
M: 'static,
{
parent_idx: Option<Index>,
data: Box<dyn Any>,
children: Vec<Id>,
event_hook: Option<EventHook<E, M>>,
visible: bool,
}
impl<E, M> WidgetData<E, M> {
fn new(parent_idx: Option<Index>, data: Box<dyn Any>) -> Self {
Self {
parent_idx,
data,
children: Default::default(),
event_hook: None,
visible: true,
}
}
}
struct EventHook<E, M>(EventHookFn<E, M>)
where
E: 'static,
M: 'static;
impl<E, M> Debug for EventHook<E, M> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
write!(f, "{:p}", self.0)
}
}
pub struct Ui<E, M>
where
E: 'static,
M: 'static,
{
#[cfg(debug_assertions)]
id: usize,
#[allow(clippy::type_complexity)]
widgets: Vec<(WidgetData<E, M>, Rc<dyn Widget<E, M>>)>,
hooker: &'static dyn Hooker<E, M>,
hooked: Rc<Vec<Index>>,
focused: Option<Index>,
}
impl<E, M> Ui<E, M> {
#[allow(clippy::new_ret_no_self)]
pub fn new<D, W>(new_data: D, new_root_widget: W) -> (Self, Id)
where
D: FnOnce() -> Box<dyn Any>,
W: FnOnce(Id, &mut dyn MutCap<E, M>) -> Box<dyn Widget<E, M>>,
{
static NOT_HOOKED: NotHooked = NotHooked {};
let mut ui = Self {
#[cfg(debug_assertions)]
id: get_next_ui_id(),
widgets: Default::default(),
hooker: &NOT_HOOKED,
hooked: Default::default(),
focused: None,
};
let id = ui._add_widget(None, new_data, new_root_widget);
debug_assert_eq!(id.idx.idx, 0);
(ui, id)
}
pub fn add_ui_widget<D, W>(&mut self, parent: Id, new_data: D, new_widget: W) -> Id
where
D: FnOnce() -> Box<dyn Any>,
W: FnOnce(Id, &mut dyn MutCap<E, M>) -> Box<dyn Widget<E, M>>,
{
let parent_idx = self.validate(parent);
self._add_widget(Some(parent_idx), new_data, new_widget)
}
fn _add_widget<D, W>(&mut self, parent_idx: Option<Index>, new_data: D, new_widget: W) -> Id
where
D: FnOnce() -> Box<dyn Any>,
W: FnOnce(Id, &mut dyn MutCap<E, M>) -> Box<dyn Widget<E, M>>,
{
let idx = Index::new(self.widgets.len());
let id = Id::new(idx.idx, self);
let dummy = Placeholder;
let data = new_data();
let data = WidgetData::new(parent_idx, data);
self.widgets.push((data, Rc::new(dummy)));
if let Some(parent_idx) = parent_idx {
self.widgets[parent_idx.idx].0.children.push(id)
}
let widget = Rc::<dyn Widget<E, M>>::from(new_widget(id, self));
debug_assert_eq!(widget.id(), id, "Created widget does not have provided Id");
self.widgets[idx.idx].1 = widget;
id
}
#[inline]
fn validate(&self, id: Id) -> Index {
#[cfg(debug_assertions)]
debug_assert_eq!(id.ui_id, self.id, "The given Id belongs to a different Ui");
id.idx
}
fn lookup(&self, idx: Index) -> &dyn Widget<E, M> {
self.widgets[idx.idx].1.as_ref()
}
fn children(&self, idx: Index) -> ChildIter<'_> {
self.widgets[idx.idx].0.children.iter()
}
fn show<F>(&mut self, idx: Index, reorder_fn: F)
where
F: Fn(&mut Ui<E, M>, Index),
{
reorder_fn(self, idx);
let data = &mut self.widgets[idx.idx].0;
data.visible = true;
if let Some(parent_idx) = data.parent_idx {
self.show(parent_idx, reorder_fn)
}
}
fn reorder<F>(&mut self, idx: Index, new_idx_fn: F)
where
F: FnOnce(&Ui<E, M>, &[Id]) -> usize,
{
if let Some(parent_idx) = self.widgets[idx.idx].0.parent_idx {
let children = &self.widgets[parent_idx.idx].0.children;
let id = Id::new(idx.idx, self);
let cur_idx = children.iter().position(|x| *x == id).unwrap();
let id = self.widgets[parent_idx.idx].0.children.remove(cur_idx);
let new_idx = new_idx_fn(self, &self.widgets[parent_idx.idx].0.children);
self.widgets[parent_idx.idx].0.children.insert(new_idx, id)
} else {
}
}
fn reorder_as_focused(&mut self, idx: Index) {
if !self.is_top_most_child(idx) {
self.reorder(idx, |_, _| 0);
}
}
fn is_visible(&self, idx: Index) -> bool {
self.widgets[idx.idx].0.visible
}
fn is_displayed(&self, idx: Index) -> bool {
let data = &self.widgets[idx.idx].0;
data.visible && data.parent_idx.map_or(true, |x| self.is_displayed(x))
}
fn is_top_most_child(&self, idx: Index) -> bool {
let parent_idx = self.widgets[idx.idx].0.parent_idx;
if let Some(parent_idx) = parent_idx {
let children = &self.widgets[parent_idx.idx].0.children;
children[0].idx == idx
} else {
true
}
}
fn focus(&mut self, idx: Index) {
self.show(idx, Ui::reorder_as_focused);
self.focused = Some(idx);
}
pub fn render(&self, renderer: &dyn Renderer) {
let idx = self.validate(self.root_id());
let root = self.lookup(idx);
let bbox = renderer.renderable_area();
renderer.pre_render();
self.render_all(idx, root, renderer, bbox);
renderer.post_render();
}
fn render_all(&self, idx: Index, widget: &dyn Widget<E, M>, renderer: &dyn Renderer, bbox: BBox) {
if self.is_visible(idx) {
let inner_bbox = widget.render(self, renderer, bbox);
if inner_bbox.w != 0 && inner_bbox.h != 0 {
for child_id in self.children(idx).rev() {
let child_idx = self.validate(*child_id);
let child = self.lookup(child_idx);
self.render_all(child_idx, child, renderer, inner_bbox)
}
}
let () = widget.render_done(self, renderer, bbox);
}
}
pub async fn handle<T>(&mut self, event: T) -> Option<E>
where
T: Into<E>,
{
let event = event.into();
let hook_event = self.hooker.invoke(self, None, None, Some(&event)).await;
let idx = self.focused;
let unhandled = self.try_handle_event(idx, event).await;
self.hooker.invoke(self, hook_event, unhandled, None).await
}
fn handle_event(
&mut self,
idx: Index,
event: E,
) -> Pin<Box<dyn Future<Output = Option<E>> + '_>> {
Box::pin(async move {
let widget = self.widgets[idx.idx].1.clone();
let event = widget.handle(self, event).await;
let parent_idx = self.widgets[idx.idx].0.parent_idx;
if let Some(event) = event {
self.try_handle_event(parent_idx, event).await
} else {
None
}
})
}
async fn try_handle_event(&mut self, idx: Option<Index>, event: E) -> Option<E> {
if let Some(idx) = idx {
self.handle_event(idx, event).await
} else {
Some(event)
}
}
}
impl<E, M> private::Sealed for Ui<E, M> {}
impl<E, M> Cap for Ui<E, M> {
fn data(&self, widget: Id) -> &dyn Any {
let idx = self.validate(widget);
self.widgets[idx.idx].0.data.as_ref()
}
fn children(&self, widget: Id) -> ChildIter<'_> {
self.children(self.validate(widget))
}
fn root_id(&self) -> Id {
debug_assert!(!self.widgets.is_empty());
debug_assert_eq!(self.validate(self.widgets[0].1.id()).idx, 0);
Id::new(0, self)
}
fn parent_id(&self, widget: Id) -> Option<Id> {
let idx = self.validate(widget);
let parent_idx = self.widgets[idx.idx].0.parent_idx;
let parent_id = parent_idx.map(|x| Id::new(x.idx, self));
debug_assert!(parent_id.map_or(true, |x| Cap::children(self, x).any(|x| *x == widget)));
parent_id
}
fn is_visible(&self, widget: Id) -> bool {
self.is_visible(self.validate(widget))
}
fn is_displayed(&self, widget: Id) -> bool {
self.is_displayed(self.validate(widget))
}
fn focused(&self) -> Option<Id> {
self.focused.map(|x| Id::new(x.idx, self))
}
#[allow(clippy::nonminimal_bool)]
fn is_focused(&self, widget: Id) -> bool {
let idx = self.validate(widget);
let result = self.focused == Some(idx);
debug_assert!(result && self.is_displayed(idx) || !result);
debug_assert!(result && self.is_top_most_child(idx) || !result);
result
}
}
#[async_trait(?Send)]
impl<E, M> MutCap<E, M> for Ui<E, M> {
fn data_mut(&mut self, widget: Id) -> &mut dyn Any {
let idx = self.validate(widget);
self.widgets[idx.idx].0.data.as_mut()
}
fn add_widget(
&mut self,
parent: Id,
new_data: Box<NewDataFn>,
new_widget: Box<NewWidgetFn<E, M>>,
) -> Id {
self.add_ui_widget(parent, new_data, new_widget)
}
fn show(&mut self, widget: Id) {
let idx = self.validate(widget);
self.show(idx, |_, _| ());
}
fn hide(&mut self, widget: Id) {
if self.is_focused(widget) {
self.focused = None
}
let idx = self.validate(widget);
self.widgets[idx.idx].0.visible = false;
}
fn focus(&mut self, widget: Id) {
let idx = self.validate(widget);
self.focus(idx)
}
fn hook_events(
&mut self,
widget: Id,
hook_fn: Option<EventHookFn<E, M>>,
) -> Option<EventHookFn<E, M>>
where
E: Mergeable,
{
static HOOKED: Hooked = Hooked {};
self.hooker = &HOOKED;
let idx = self.validate(widget);
let data = &mut self.widgets[idx.idx].0;
let result = self.hooked.binary_search(&idx);
debug_assert_eq!(result.is_ok(), data.event_hook.is_some());
match hook_fn {
Some(_) => {
if let Err(i) = result {
Rc::make_mut(&mut self.hooked).insert(i, idx);
}
},
None => {
if let Ok(i) = result {
let _ = Rc::make_mut(&mut self.hooked).remove(i);
}
},
};
let prev_hook = data.event_hook.take();
data.event_hook = hook_fn.map(|x| EventHook(x));
prev_hook.map(|x| x.0)
}
async fn send(&mut self, widget: Id, message: M) -> Option<M> {
let idx = self.validate(widget);
let widget = self.widgets[idx.idx].1.clone();
widget.react(message, self).await
}
async fn call(&mut self, widget: Id, message: &mut M) -> Option<M> {
let idx = self.validate(widget);
let widget = self.widgets[idx.idx].1.clone();
widget.respond(message, self).await
}
}
impl<E, M> Debug for Ui<E, M> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
let mut debug = f.debug_struct("Ui");
#[cfg(debug_assertions)]
let _ = debug.field("id", &self.id);
debug.finish()
}
}
impl<E, M> Deref for Ui<E, M> {
type Target = dyn Cap;
fn deref(&self) -> &Self::Target {
self
}
}