use std::collections::HashMap;
use rs_math3d::{Dimensioni, Recti};
use crate::input::{ControlState, ResourceState};
use super::NodeId;
#[derive(Copy, Clone, Debug, Default)]
pub struct NodeLayout {
pub rect: Recti,
pub body: Recti,
pub content_size: Dimensioni,
}
impl NodeLayout {
pub const fn new(rect: Recti, body: Recti, content_size: Dimensioni) -> Self {
Self { rect, body, content_size }
}
}
#[derive(Copy, Clone, Debug)]
pub struct NodeInteraction {
pub control: ControlState,
pub result: ResourceState,
}
impl NodeInteraction {
pub const fn new(control: ControlState, result: ResourceState) -> Self {
Self { control, result }
}
}
impl Default for NodeInteraction {
fn default() -> Self {
Self::new(ControlState::default(), ResourceState::NONE)
}
}
#[derive(Copy, Clone, Debug, Default)]
pub struct NodeFrameState {
pub layout: NodeLayout,
pub interaction: NodeInteraction,
}
impl NodeFrameState {
pub const fn new(layout: NodeLayout, interaction: NodeInteraction) -> Self {
Self { layout, interaction }
}
}
#[derive(Default)]
pub struct WidgetTreeCache {
prev_layout: HashMap<NodeId, NodeLayout>,
curr_layout: HashMap<NodeId, NodeLayout>,
prev_interaction: HashMap<NodeId, NodeInteraction>,
curr_interaction: HashMap<NodeId, NodeInteraction>,
}
impl WidgetTreeCache {
pub fn begin_frame(&mut self) {
self.curr_layout.clear();
self.curr_interaction.clear();
}
pub fn finish_frame(&mut self) {
std::mem::swap(&mut self.prev_layout, &mut self.curr_layout);
std::mem::swap(&mut self.prev_interaction, &mut self.curr_interaction);
self.curr_layout.clear();
self.curr_interaction.clear();
}
pub fn clear(&mut self) {
self.prev_layout.clear();
self.curr_layout.clear();
self.prev_interaction.clear();
self.curr_interaction.clear();
}
pub fn prev_layout(&self, node_id: NodeId) -> Option<&NodeLayout> {
self.prev_layout.get(&node_id)
}
pub fn current_layout(&self, node_id: NodeId) -> Option<&NodeLayout> {
self.curr_layout.get(&node_id)
}
pub fn prev_interaction(&self, node_id: NodeId) -> Option<&NodeInteraction> {
self.prev_interaction.get(&node_id)
}
pub fn current_interaction(&self, node_id: NodeId) -> Option<&NodeInteraction> {
self.curr_interaction.get(&node_id)
}
pub fn prev_state(&self, node_id: NodeId) -> Option<NodeFrameState> {
let layout = self.prev_layout(node_id).copied()?;
let interaction = self.prev_interaction(node_id).copied().unwrap_or_default();
Some(NodeFrameState::new(layout, interaction))
}
pub fn current_state(&self, node_id: NodeId) -> Option<NodeFrameState> {
let layout = self.current_layout(node_id).copied()?;
let interaction = self.current_interaction(node_id).copied().unwrap_or_default();
Some(NodeFrameState::new(layout, interaction))
}
pub fn record_layout(&mut self, node_id: NodeId, layout: NodeLayout) {
let prev = self.curr_layout.insert(node_id, layout);
debug_assert!(prev.is_none(), "Node {:?} layout was recorded more than once in the same frame", node_id);
}
pub fn record_interaction(&mut self, node_id: NodeId, interaction: NodeInteraction) {
let prev = self.curr_interaction.insert(node_id, interaction);
debug_assert!(prev.is_none(), "Node {:?} interaction was recorded more than once in the same frame", node_id);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn layout_and_interaction_generations_are_independent() {
let node_id = NodeId::new(7);
let layout = NodeLayout::new(Recti::new(1, 2, 3, 4), Recti::new(5, 6, 7, 8), Dimensioni::new(9, 10));
let interaction = NodeInteraction::new(ControlState::default(), ResourceState::SUBMIT);
let mut cache = WidgetTreeCache::default();
cache.record_layout(node_id, layout);
let current_layout = cache.current_layout(node_id).copied().unwrap();
assert_eq!(current_layout.rect.x, layout.rect.x);
assert_eq!(current_layout.rect.y, layout.rect.y);
assert_eq!(current_layout.rect.width, layout.rect.width);
assert_eq!(current_layout.rect.height, layout.rect.height);
assert!(cache.current_interaction(node_id).is_none());
cache.record_interaction(node_id, interaction);
cache.finish_frame();
let committed_layout = cache.prev_layout(node_id).copied().unwrap();
assert_eq!(committed_layout.content_size.width, layout.content_size.width);
assert_eq!(committed_layout.content_size.height, layout.content_size.height);
assert!(cache.prev_interaction(node_id).copied().unwrap().result.is_submitted());
}
}