use std::{
cell::RefCell,
collections::hash_map::DefaultHasher,
hash::{Hash, Hasher},
rc::Rc,
};
use rs_math3d::Dimensioni;
use crate::{
input::{ContainerOption, WidgetBehaviourOption},
layout::{SizePolicy, StackDirection},
widget::Widget,
ContainerHandle, Custom, CustomRenderArgs, Node, TextBlock, TextWrap,
};
use super::{erased_widget_state, widget_handle, NodeId, Policy, TreeCustomRender, WidgetHandle, WidgetTree, WidgetTreeNode, WidgetTreeNodeKind};
struct BuilderFrame {
scope_seed: u64,
next_auto: u64,
nodes: Vec<WidgetTreeNode>,
}
impl BuilderFrame {
fn root(seed: u64) -> Self {
Self {
scope_seed: seed,
next_auto: 0,
nodes: Vec::new(),
}
}
fn child(seed: u64) -> Self {
Self {
scope_seed: seed,
next_auto: 0,
nodes: Vec::new(),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct NodeOptions {
policy: Policy,
key: Option<u64>,
}
impl Default for NodeOptions {
fn default() -> Self {
Self::new()
}
}
impl NodeOptions {
pub const fn new() -> Self {
Self { policy: Policy::auto(), key: None }
}
pub const fn with_policy(policy: Policy) -> Self {
Self { policy, key: None }
}
pub fn keyed<K: Hash>(key: K) -> Self {
Self::new().key(key)
}
pub const fn policy(mut self, policy: Policy) -> Self {
self.policy = policy;
self
}
pub fn key<K: Hash>(mut self, key: K) -> Self {
self.key = Some(hash_builder_key(key));
self
}
}
fn hash_builder_key<K: Hash>(key: K) -> u64 {
let mut hasher = DefaultHasher::new();
key.hash(&mut hasher);
hasher.finish()
}
pub struct WidgetTreeBuilder {
frames: Vec<BuilderFrame>,
}
impl Default for WidgetTreeBuilder {
fn default() -> Self {
Self::new()
}
}
impl WidgetTreeBuilder {
pub const DEFAULT_ROOT_SEED: u64 = 0x9e37_79b9_7f4a_7c15;
pub fn new() -> Self {
Self::with_seed(Self::DEFAULT_ROOT_SEED)
}
pub fn with_seed(seed: u64) -> Self {
Self { frames: vec![BuilderFrame::root(seed)] }
}
pub fn build(f: impl FnOnce(&mut Self)) -> WidgetTree {
let mut builder = Self::new();
f(&mut builder);
builder.finish()
}
pub fn build_with_seed(seed: u64, f: impl FnOnce(&mut Self)) -> WidgetTree {
let mut builder = Self::with_seed(seed);
f(&mut builder);
builder.finish()
}
pub fn finish(mut self) -> WidgetTree {
debug_assert_eq!(self.frames.len(), 1, "widget tree builder scopes must be balanced");
let frame = self.frames.pop().expect("root frame missing");
WidgetTree { roots: frame.nodes }
}
pub fn widget<W: Widget + 'static>(&mut self, widget: WidgetHandle<W>) -> NodeId {
self.widget_with(NodeOptions::new(), widget)
}
pub fn widget_with<W: Widget + 'static>(&mut self, options: NodeOptions, widget: WidgetHandle<W>) -> NodeId {
self.push_leaf(options, WidgetTreeNodeKind::Widget { widget: erased_widget_state(widget) })
}
pub fn text(&mut self, text: impl Into<String>) -> NodeId {
let text = text.into();
self.widget(widget_handle(TextBlock::new(text)))
}
pub fn text_with_wrap(&mut self, text: impl Into<String>, wrap: TextWrap) -> NodeId {
let text = text.into();
self.widget(widget_handle(TextBlock::with_wrap(text, wrap)))
}
pub fn custom_render<F>(&mut self, state: WidgetHandle<Custom>, f: F) -> NodeId
where
F: FnMut(Dimensioni, &CustomRenderArgs) + 'static,
{
self.custom_render_with(NodeOptions::new(), state, f)
}
pub fn custom_render_with<F>(&mut self, options: NodeOptions, state: WidgetHandle<Custom>, f: F) -> NodeId
where
F: FnMut(Dimensioni, &CustomRenderArgs) + 'static,
{
let render: TreeCustomRender = Rc::new(RefCell::new(Box::new(f)));
self.push_leaf(options, WidgetTreeNodeKind::CustomRender { state, render })
}
pub fn container(&mut self, handle: ContainerHandle, opt: ContainerOption, behaviour: WidgetBehaviourOption, f: impl FnOnce(&mut Self)) -> NodeId {
self.container_with(NodeOptions::new(), handle, opt, behaviour, f)
}
pub fn container_with(
&mut self,
options: NodeOptions,
handle: ContainerHandle,
opt: ContainerOption,
behaviour: WidgetBehaviourOption,
f: impl FnOnce(&mut Self),
) -> NodeId {
self.push_group(options, WidgetTreeNodeKind::Container { handle, opt, behaviour }, f)
}
pub fn header(&mut self, state: WidgetHandle<Node>, f: impl FnOnce(&mut Self)) -> NodeId {
self.header_with(NodeOptions::new(), state, f)
}
pub fn header_with(&mut self, options: NodeOptions, state: WidgetHandle<Node>, f: impl FnOnce(&mut Self)) -> NodeId {
self.push_group(options, WidgetTreeNodeKind::Header { state }, f)
}
pub fn tree_node(&mut self, state: WidgetHandle<Node>, f: impl FnOnce(&mut Self)) -> NodeId {
self.tree_node_with(NodeOptions::new(), state, f)
}
pub fn tree_node_with(&mut self, options: NodeOptions, state: WidgetHandle<Node>, f: impl FnOnce(&mut Self)) -> NodeId {
self.push_group(options, WidgetTreeNodeKind::Tree { state }, f)
}
pub fn row(&mut self, widths: &[SizePolicy], height: SizePolicy, f: impl FnOnce(&mut Self)) -> NodeId {
self.row_with(NodeOptions::new(), widths, height, f)
}
pub fn row_with(&mut self, options: NodeOptions, widths: &[SizePolicy], height: SizePolicy, f: impl FnOnce(&mut Self)) -> NodeId {
self.push_group(options, WidgetTreeNodeKind::Row { widths: widths.to_vec(), height }, f)
}
pub fn grid(&mut self, widths: &[SizePolicy], heights: &[SizePolicy], f: impl FnOnce(&mut Self)) -> NodeId {
self.grid_with(NodeOptions::new(), widths, heights, f)
}
pub fn grid_with(&mut self, options: NodeOptions, widths: &[SizePolicy], heights: &[SizePolicy], f: impl FnOnce(&mut Self)) -> NodeId {
self.push_group(
options,
WidgetTreeNodeKind::Grid {
widths: widths.to_vec(),
heights: heights.to_vec(),
},
f,
)
}
pub fn column(&mut self, f: impl FnOnce(&mut Self)) -> NodeId {
self.column_with(NodeOptions::new(), f)
}
pub fn column_with(&mut self, options: NodeOptions, f: impl FnOnce(&mut Self)) -> NodeId {
self.push_group(options, WidgetTreeNodeKind::Column, f)
}
pub fn stack(&mut self, width: SizePolicy, height: SizePolicy, direction: StackDirection, f: impl FnOnce(&mut Self)) -> NodeId {
self.stack_with(NodeOptions::new(), width, height, direction, f)
}
pub fn stack_with(&mut self, options: NodeOptions, width: SizePolicy, height: SizePolicy, direction: StackDirection, f: impl FnOnce(&mut Self)) -> NodeId {
self.push_group(options, WidgetTreeNodeKind::Stack { width, height, direction }, f)
}
fn push_leaf(&mut self, options: NodeOptions, kind: WidgetTreeNodeKind) -> NodeId {
let id = self.alloc_id(kind.tag(), options.key);
self.current_frame_mut().nodes.push(WidgetTreeNode {
id,
policy: options.policy,
kind,
children: Vec::new(),
});
id
}
fn push_group(&mut self, options: NodeOptions, kind: WidgetTreeNodeKind, f: impl FnOnce(&mut Self)) -> NodeId {
let id = self.alloc_id(kind.tag(), options.key);
self.frames.push(BuilderFrame::child(id.raw() as u64));
f(self);
let frame = self.frames.pop().expect("child frame missing");
self.current_frame_mut().nodes.push(WidgetTreeNode {
id,
policy: options.policy,
kind,
children: frame.nodes,
});
id
}
fn alloc_id(&mut self, tag: u8, key: Option<u64>) -> NodeId {
let frame = self.current_frame_mut();
let ordinal = frame.next_auto;
frame.next_auto += 1;
let mut hasher = DefaultHasher::new();
frame.scope_seed.hash(&mut hasher);
tag.hash(&mut hasher);
match key {
Some(key) => {
1u8.hash(&mut hasher);
key.hash(&mut hasher);
}
None => {
0u8.hash(&mut hasher);
ordinal.hash(&mut hasher);
}
}
NodeId::new(hasher.finish())
}
fn current_frame_mut(&mut self) -> &mut BuilderFrame {
self.frames.last_mut().expect("widget tree builder frame missing")
}
}