use std::cell::Ref;
use std::ops::Deref;
use platform_core::Event;
use renderer_core::DrawCommand;
use ui_core::{Component, ComponentList, EventResult};
use ui_tree::SegmentNodeInfo;
pub enum Frame<'a> {
Borrowed(Ref<'a, Vec<DrawCommand>>),
Owned(Vec<DrawCommand>),
}
impl Deref for Frame<'_> {
type Target = [DrawCommand];
fn deref(&self) -> &[DrawCommand] {
match self {
Frame::Borrowed(r) => r,
Frame::Owned(v) => v,
}
}
}
pub trait UiTree {
fn on_event(&mut self, event: &Event) -> EventResult;
fn frame(&self) -> Frame<'_>;
fn is_dirty(&self) -> bool;
fn generation(&self) -> u64;
fn walk(&self, out: &mut Vec<SegmentNodeInfo>);
fn bump_force_ticks(&self) {}
fn end_frame(&self) {}
}
pub struct LocalTree(ComponentList);
impl LocalTree {
pub fn new(root: Box<dyn Component>) -> Self {
Self(ComponentList::new(root))
}
}
impl UiTree for LocalTree {
fn on_event(&mut self, event: &Event) -> EventResult {
self.0.on_event(event)
}
fn frame(&self) -> Frame<'_> {
Frame::Borrowed(self.0.commands())
}
fn is_dirty(&self) -> bool {
self.0.is_dirty()
}
fn generation(&self) -> u64 {
self.0.generation()
}
fn walk(&self, out: &mut Vec<SegmentNodeInfo>) {
self.0.walk_tree(out);
}
fn bump_force_ticks(&self) {
self.0.bump_force_ticks();
}
}
pub struct HotTree {
tree: ComponentList,
}
impl HotTree {
pub fn mount(app: &dyn crate::app::App) -> *mut HotTree {
Box::into_raw(Box::new(HotTree {
tree: ComponentList::new(app.root()),
}))
}
pub unsafe fn release(ptr: *mut HotTree) {
drop(unsafe { Box::from_raw(ptr) });
}
pub unsafe fn on_event(ptr: *mut HotTree, event: &Event) -> bool {
let this = unsafe { &mut *ptr };
ui_core::observe_keyboard(event);
ui_core::observe_pointer(event);
this.tree.on_event(event) == EventResult::Handled
}
pub unsafe fn end_frame(ptr: *mut HotTree) {
let _ = ptr;
ui_core::end_keyboard_frame();
}
pub unsafe fn paint(ptr: *mut HotTree) -> Vec<DrawCommand> {
let this = unsafe { &*ptr };
this.tree.commands().clone()
}
pub unsafe fn is_dirty(ptr: *mut HotTree) -> bool {
let this = unsafe { &*ptr };
this.tree.is_dirty()
}
pub unsafe fn generation(ptr: *mut HotTree) -> u64 {
let this = unsafe { &*ptr };
this.tree.generation()
}
pub unsafe fn walk(ptr: *mut HotTree) -> Vec<SegmentNodeInfo> {
let this = unsafe { &*ptr };
let mut out = Vec::new();
this.tree.walk_tree(&mut out);
out
}
}
#[cfg(test)]
mod tests {
use super::*;
struct Blank;
impl crate::app::App for Blank {
fn root(&self) -> Box<dyn Component> {
ui_core::reset_layout_runtime();
Box::new(
ui_core::Rectangle::new(
layout_core::LayoutStyle::new().width(10.0).height(10.0),
|| renderer_core::RectStyle::filled(renderer_core::Color::BLACK, 0.0),
)
.expect("a rectangle builds"),
)
}
}
#[test]
fn the_hot_tree_feeds_the_registries_its_own_widgets_read() {
ui_core::reset_keyboard();
ui_core::reset_pointer();
let tree = HotTree::mount(&Blank);
assert!(!ui_core::modifiers().is_shift);
unsafe {
HotTree::on_event(
tree,
&Event::ModifiersChanged {
modifiers: platform_core::ModifiersState {
is_shift: true,
..Default::default()
},
},
);
}
assert!(
ui_core::modifiers().is_shift,
"the side that dispatched has to be the side that knows"
);
unsafe {
HotTree::on_event(
tree,
&Event::PointerPressed {
x: 5.0,
y: 5.0,
button: platform_core::PointerButton::Secondary,
source: platform_core::PointerSource::Mouse,
},
);
}
assert!(
ui_core::pointer_buttons().secondary,
"and the same for which button is down"
);
unsafe { HotTree::release(tree) };
ui_core::reset_keyboard();
ui_core::reset_pointer();
}
}