Newer
Older
use bevy::{
prelude::{Commands, Component, Entity},
utils::HashMap,
};
use morphorm::Hierarchy;
use crate::{
context_entities::ContextEntities, layout::LayoutCache, node::WrappedIndex, prelude::Tree,
/// KayakWidgetContext manages tree, state, and context updates within a single widget.
/// Unlike the root context this manages a single widget and it's children.
/// At the end of a render system call KayakWidgetContext will be consumed by the root context.
/// It has some knowledge about the existing tree and it knows about a subset of the new tree.
/// It is not possible to create a KayakWidgetContext from scratch. One will be provided
/// to the render system via it's In parameters.
pub struct KayakWidgetContext {
old_tree: Arc<RwLock<Tree>>,
new_tree: Arc<RwLock<Tree>>,
context_entities: ContextEntities,
layout_cache: Arc<RwLock<LayoutCache>>,
pub(crate) index: Arc<RwLock<HashMap<Entity, usize>>>,
order_tree: Arc<RwLock<Tree>>,
impl KayakWidgetContext {
pub(crate) fn new(
old_tree: Arc<RwLock<Tree>>,
context_entities: ContextEntities,
layout_cache: Arc<RwLock<LayoutCache>>,
order_tree: Arc<RwLock<Tree>>,
index: Arc<RwLock<HashMap<Entity, usize>>>,
) -> Self {
Self {
old_tree,
new_tree: Arc::new(RwLock::new(Tree::default())),
context_entities,
layout_cache,
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
}
}
pub(crate) fn store(&self, new_tree: &Tree) {
if let Ok(mut tree) = self.new_tree.write() {
*tree = new_tree.clone();
}
}
/// Creates a new context using the context entity for the given type_id + parent id.
pub fn set_context_entity<T: Default + 'static>(
&self,
parent_id: Option<Entity>,
context_entity: Entity,
) {
if let Some(parent_id) = parent_id {
self.context_entities
.add_context_entity::<T>(parent_id, context_entity);
}
}
/// Finds the closest matching context entity by traversing up the tree.
pub fn get_context_entity<T: Default + 'static>(
&self,
current_entity: Entity,
) -> Option<Entity> {
// Check self first..
if let Some(entity) = self
.context_entities
.get_context_entity::<T>(current_entity)
{
return Some(entity);
}
// Check parents
if let Ok(tree) = self.old_tree.read() {
let mut parent = tree.get_parent(WrappedIndex(current_entity));
while parent.is_some() {
if let Some(entity) = self
.context_entities
.get_context_entity::<T>(parent.unwrap().0)
{
return Some(entity);
}
parent = tree.get_parent(parent.unwrap());
}
}
None
}
pub(crate) fn copy_from_point(&self, other_tree: &Arc<RwLock<Tree>>, entity: WrappedIndex) {
if let Ok(other_tree) = other_tree.read() {
if let Ok(mut tree) = self.new_tree.write() {
tree.copy_from_point(&other_tree, entity);
}
}
}
/// Removes all children from the new tree.
/// Changes to the current tree will happen when KayakWidgetContext
/// is consumed.
pub fn clear_children(&self, entity: Entity) {
if let Ok(mut tree) = self.new_tree.write() {
tree.children.insert(WrappedIndex(entity), vec![]);
}
}
/// Retrieves a list of all children.
pub fn get_children(&self, entity: Entity) -> Vec<Entity> {
let mut children = vec![];
if let Ok(tree) = self.new_tree.read() {
let iterator = tree.child_iter(WrappedIndex(entity));
children = iterator.map(|index| index.0).collect::<Vec<_>>();
}
children
}
fn get_children_ordered(&self, entity: Entity) -> Vec<Entity> {
if let Ok(tree) = self.order_tree.read() {
let iterator = tree.child_iter(WrappedIndex(entity));
children = iterator.map(|index| index.0).collect::<Vec<_>>();
}
children
}
fn get_and_add_index(&self, parent: Entity) -> usize {
if let Ok(mut hash_map) = self.index.try_write() {
if hash_map.contains_key(&parent) {
let index = hash_map.get_mut(&parent).unwrap();
let current_index = *index;
*index += 1;
return current_index;
} else {
hash_map.insert(parent, 1);
return 0;
}
}
0
}
/// Creates or grabs the existing state entity
pub fn use_state<State: Component + PartialEq + Clone + Default>(
&self,
commands: &mut Commands,
widget_entity: Entity,
initial_state: State,
) -> Entity {
self.widget_state
.add(commands, widget_entity, initial_state)
}
/// Grabs the existing state returns none if it does not exist.
pub fn get_state(&self, widget_entity: Entity) -> Option<Entity> {
self.widget_state.get(widget_entity)
}
/// Returns a new/existing widget entity.
/// Because a re-render can potentially spawn new entities it's advised to use this
/// to avoid creating a new entity.
pub fn spawn_widget(&self, commands: &mut Commands, parent_id: Option<Entity>) -> Entity {
let mut entity = None;
if let Some(parent_entity) = parent_id {
let children = self.get_children_ordered(parent_entity);
let index = self.get_and_add_index(parent_entity);
let child = children.get(index).cloned();
if let Some(child) = child {
log::trace!(
"Reusing widget entity {:?} with parent: {:?}!",
);
entity = Some(commands.get_or_spawn(child).id());
}
// If we have no entity spawn it!
if entity.is_none() {
entity = Some(commands.spawn_empty().id());
log::trace!(
"Spawning new widget with entity {:?}!",
);
// We need to add it to the ordered tree
if let Ok(mut tree) = self.order_tree.try_write() {
tree.add(WrappedIndex(entity.unwrap()), parent_id.map(WrappedIndex))
}
}
entity.unwrap()
/// Removes all matching children from the tree.
pub fn remove_children(&self, children_to_remove: Vec<Entity>) {
if let Ok(mut tree) = self.new_tree.write() {
for child in children_to_remove.iter() {
tree.remove(WrappedIndex(*child));
}
}
}
/// Adds a new widget to the tree with a given parent.
pub fn add_widget(&self, parent: Option<Entity>, entity: Entity) {
if let Some(parent) = parent {
assert!(parent != entity, "Parent cannot equal entity!");
}
tree.add(WrappedIndex(entity), parent.map(WrappedIndex));
}
}
/// Attempts to get the layout rect for the widget with the given ID
///
/// # Arguments
///
/// * `id`: The ID of the widget
///
pub fn get_layout(&self, widget_id: Entity) -> Option<crate::layout::Rect> {
if let Ok(cache) = self.layout_cache.try_read() {
cache.rect.get(&WrappedIndex(widget_id)).cloned()
} else {
None
}
}
/// Dumps the tree to the console in a human readable format.
/// This is relatively slow to do if the tree is large
/// so avoid doing unless necessary.
pub fn dbg_tree(&self) {
if let Ok(tree) = self.new_tree.read() {
tree.dump()
}
}
/// Consumes the tree
pub(crate) fn take(self) -> Tree {
Arc::try_unwrap(self.new_tree)
.unwrap()
.into_inner()
.unwrap()
}
}