diff --git a/Cargo.toml b/Cargo.toml index 32127e8f78041c73f53d3d0c36ac1b134cd4d40a..2fb07defc349e07c49b92a9a1f049f4ca8f8d2ae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "kayak_ui" description = "A UI library built using the bevy game engine!" -version = "0.4.0" +version = "0.4.1" edition = "2021" resolver = "2" authors = ["John Mitchell"] diff --git a/examples/svg.rs b/examples/svg.rs index 6416404dc3743679d5d748957bde42012c74c25c..480c558b8dd5d90557b21896cd8aa5c8695e670c 100644 --- a/examples/svg.rs +++ b/examples/svg.rs @@ -20,17 +20,23 @@ fn startup( let parent_id = None; rsx! { <KayakAppBundle> - <KSvgBundle - svg={KSvg(svg)} + <ElementBundle styles={KStyle { position_type: StyleProp::Value(KPositionType::SelfDirected), - left: StyleProp::Value(Units::Pixels(10.0)), - top: StyleProp::Value(Units::Pixels(10.0)), - width: StyleProp::Value(Units::Pixels(800.0)), - height: StyleProp::Value(Units::Pixels(800.0)), + left: StyleProp::Value(Units::Pixels(-34.545261 * 7.6)), + top: StyleProp::Value(Units::Pixels(10.0 - 95.557219 * 7.6)), ..Default::default() }} - /> + > + <KSvgBundle + svg={KSvg(svg)} + styles={KStyle { + width: StyleProp::Value(Units::Pixels(800.0)), + height: StyleProp::Value(Units::Pixels(800.0)), + ..Default::default() + }} + /> + </ElementBundle> </KayakAppBundle> }; diff --git a/examples/tabs/tabs.rs b/examples/tabs/tabs.rs index efa60a918c9e06b7bf5084aa4084da0839ce3616..a695448d70733dcb7bea4112c2e85a8fb37c3fa4 100644 --- a/examples/tabs/tabs.rs +++ b/examples/tabs/tabs.rs @@ -75,10 +75,10 @@ fn startup( ..Default::default() }} > - <TabBundle tab={Tab { index: 0 }}> + <TabBundle key={"tab1"} tab={Tab { index: 0 }}> <TextWidgetBundle text={TextProps { content: "Tab 1 Content".into(), size: 14.0, line_height: Some(14.0), ..Default::default() }} /> </TabBundle> - <TabBundle tab={Tab { index: 1 }}> + <TabBundle key={"tab2"} tab={Tab { index: 1 }}> <TextWidgetBundle text={TextProps { content: "Tab 2 Content".into(), size: 14.0, line_height: Some(14.0), ..Default::default() }} /> </TabBundle> </ElementBundle> diff --git a/src/children.rs b/src/children.rs index 454e7041cae8499697a6416024cbe561c04db1fa..b6f5bbfe78ffeb68a4b633e6b8a1a836c18bf74d 100644 --- a/src/children.rs +++ b/src/children.rs @@ -63,6 +63,7 @@ impl KChildren { for child in self.inner.iter() { if let Some(parent_id) = parent_id { if let Some(mut entity_commands) = commands.get_entity(*child) { + entity_commands.remove::<Parent>(); entity_commands.set_parent(parent_id); } } diff --git a/src/context.rs b/src/context.rs index 7384776788cf7e0dda1965d3d0cc517b1c9cb34f..edc6b01317d22052d5a6f23b75bc1b5b6a06e8ae 100644 --- a/src/context.rs +++ b/src/context.rs @@ -227,10 +227,8 @@ impl KayakRootContext { 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); - } + self.context_entities + .add_context_entity::<T>(parent_id, context_entity); } /// Returns a new/existing widget entity. @@ -247,10 +245,10 @@ impl KayakRootContext { /// widget_context.add_widget(None, root_entity); /// } ///``` - pub fn spawn_widget( + pub fn spawn_widget<'a>( &self, commands: &mut Commands, - key: Option<&'static str>, + key: Option<&'a str>, parent_id: Option<Entity>, ) -> Entity { let mut entity = None; @@ -617,8 +615,6 @@ fn recurse_node_tree_to_build_primitives( }); } - // dbg!(&render_primitives); - (child_count + 1, current_global_z, total_opacity_layers) } @@ -666,7 +662,6 @@ pub fn update_widgets_sys(world: &mut World) { let mut new_ticks = HashMap::new(); - // dbg!("Updating widgets!"); update_widgets( context.camera_entity, world, @@ -694,7 +689,6 @@ pub fn update_widgets_sys(world: &mut World) { } } - // dbg!("Finished updating widgets!"); let tick = world.read_change_tick(); for (key, system) in context.systems.iter_mut() { @@ -705,16 +699,10 @@ pub fn update_widgets_sys(world: &mut World) { system.0.set_last_change_tick(tick); system.1.set_last_change_tick(tick); } - // system.apply_buffers(world); } // Clear out indices if let Ok(mut indices) = context.index.try_write() { - // for (entity, value) in indices.iter_mut() { - // if tree.root_node.unwrap().0.id() != entity.id() { - // *value = 0; - // } - // } indices.clear(); } @@ -741,6 +729,12 @@ fn update_widgets( unique_ids_parents: &Arc<RwLock<HashMap<Entity, Entity>>>, ) { for entity in widgets.iter() { + // if let (Some(entity_ref), Some(_)) = ( + // world.get_entity(entity.0), + // tree.try_write() + // .ok() + // .map(|tree| tree.contains(*entity).clone()), + // ) if let Some(entity_ref) = world.get_entity(entity.0) { if let Some(widget_type) = entity_ref.get::<WidgetName>() { let widget_context = KayakWidgetContext::new( @@ -776,7 +770,6 @@ fn update_widgets( if should_update_children { if let Ok(mut tree) = tree.write() { - let mut _had_removal = false; let diff = tree.diff_children(&widget_context, *entity, 0); for (_index, child, _parent, changes) in diff.changes.iter() { if changes @@ -787,19 +780,14 @@ fn update_widgets( cache.add(*child); } } - - if changes - .iter() - .any(|change| matches!(change, Change::Deleted)) - { - _had_removal = true; - } } // let had_change = diff.has_changes(); // if had_change { - // tree.dump_at(*entity); - // dbg!(&diff); + // println!("Tree Before:"); + // tree.dump_all_at(Some(world), entity.0); + // println!("Changes:"); + // diff.debug_print(world); // } // Children of this node need to be despawned. @@ -807,6 +795,7 @@ fn update_widgets( 'outer: for (_index, changed_entity, parent, changes) in diff.changes.iter() { // If a tree node goes from A to B we need to know and delete the descendants. + let mut remove_state = Vec::default(); if let Ok(previous_entities) = cloned_widget_entities.read() { if let Some(previous_entity) = previous_entities.get(&changed_entity.0) @@ -820,21 +809,73 @@ fn update_widgets( prev_entity_ref.get::<WidgetName>(), ) { if widget_name != prev_widget_name { - for child in - tree.down_iter_at(*changed_entity, false) - { - if let Some(parent) = tree.parent(child) { - // Due to a bug in bevy we need to remove the parent manually otherwise we'll panic later. - if let Some(mut entity_mut) = - world.get_entity_mut(child.0) + // It doesn't matter we always need to remove state + remove_state.push(changed_entity.0); + if tree.parent(*changed_entity).is_some() { + for child in + tree.down_iter_at(*changed_entity, false) + { + trace!( + "Removing AvsB children {}::{}", + entity_ref + .get::<WidgetName>() + .map(|n| n.0.clone()) + .unwrap_or("Unknown".into()), + changed_entity.0.index() + ); + let mut should_delete = true; + if let Ok(order_tree) = + order_tree.try_read() { - entity_mut.remove_parent(); + if let Some(order_tree_parent) = + order_tree.parent(*changed_entity) + { + 'back_up: for sibling in order_tree + .child_iter(order_tree_parent) + { + if sibling == *changed_entity { + continue 'back_up; + } + for child in tree + .down_iter_at(sibling, true) + { + // Ignore self again. + if child == *parent { + continue; + } + if let Some(entity_ref) = + world + .get_entity(child.0) + { + if let Some(children) = + entity_ref + .get::<KChildren>() + { + if children + .contains_entity( + changed_entity + .0, + ) + { + trace!("Caught an entity that was marked as deleted but wasn't! {:?} in {:?}", changed_entity.0, child.0); + // Don't despawn changed entity because it exists as a child passed via props + should_delete = + false; + break 'back_up; + } + } + } + } + } + } } - despawn_list.push((parent.0, child.0)); - if let Ok(mut order_tree) = - order_tree.try_write() - { - order_tree.remove(child); + if should_delete { + despawn_list.push((parent.0, child.0)); + if let Ok(mut order_tree) = + order_tree.try_write() + { + order_tree.remove(*changed_entity); + } } } } @@ -843,12 +884,34 @@ fn update_widgets( } } } + + for entity in remove_state.iter() { + if let Some(state_entity) = widget_state.remove(*entity) { + if let Some(mut entity_mut) = world.get_entity_mut(state_entity) + { + entity_mut.remove_parent(); + entity_mut.despawn_recursive(); + } + } + // Also remove all cloned widget entities + if let Ok(mut cloned_widget_entities) = + cloned_widget_entities.try_write() + { + if let Some(target) = cloned_widget_entities.get(entity) { + world.despawn(*target); + } + cloned_widget_entities.remove(entity); + } + } + if changes.iter().any(|change| *change == Change::Inserted) { if let Some(mut entity_commands) = world.get_entity_mut(changed_entity.0) { - entity_commands.insert(Mounted); + entity_commands.remove::<bevy::prelude::Parent>(); entity_commands.set_parent(parent.0); + entity_commands.insert(Mounted); + entity_commands.insert(DirtyNode); } if world.get_entity(changed_entity.0).is_some() { if let Some(mut entity_commands) = @@ -861,43 +924,46 @@ fn update_widgets( .iter() .any(|change| matches!(change, Change::Deleted)) { - // Remove from order tree. - // This is safe to do right away. - // if let Ok(mut order_tree) = order_tree.try_write() { - // order_tree.remove(*changed_entity); - // } // If the child exists as a child of one of the children we do not need to remove it. // TODO: This is kinda of expensive we should think of a way of making this faster.. if let Ok(order_tree) = order_tree.try_read() { - for child in order_tree.child_iter(*parent) { - if let Some(entity_ref) = world.get_entity(child.0) { - if let Some(children) = entity_ref.get::<KChildren>() { - if children.contains_entity(changed_entity.0) { - trace!("Caught an entity that was marked as deleted but wasn't! {:?}", changed_entity.0); - // Don't despawn changed entity because it exists as a child passed via props - continue 'outer; + if let Some(parent) = order_tree.parent(*changed_entity) { + for sibling in order_tree.child_iter(parent) { + for child in tree.down_iter_at(sibling, true) { + if let Some(entity_ref) = world.get_entity(child.0) + { + if let Some(children) = + entity_ref.get::<KChildren>() + { + if children + .contains_entity(changed_entity.0) + { + trace!("Caught an entity that was marked as deleted but wasn't! {:?}", changed_entity.0); + // Don't despawn changed entity because it exists as a child passed via props + continue 'outer; + } + } } } } } } - for child in tree.down_iter_at(*changed_entity, true) { - trace!( - "Trying to remove: {:?} with parent {:?}", - child.0, - tree.parent(child) - ); - // Due to a bug in bevy we need to remove the parent manually otherwise we'll panic later. - if let Some(mut entity_mut) = world.get_entity_mut(child.0) { - entity_mut.remove_parent(); - } - if let Some(parent) = tree.parent(child) { - despawn_list.push((parent.0, child.0)); - } + trace!( + "Trying to remove: {:?} with parent {:?}", + changed_entity.0, + tree.parent(*changed_entity) + ); + // Due to a bug in bevy we need to remove the parent manually otherwise we'll panic later. + if let Some(mut entity_mut) = world.get_entity_mut(changed_entity.0) + { + entity_mut.remove_parent(); + } + if let Some(parent) = tree.parent(*changed_entity) { + despawn_list.push((parent.0, changed_entity.0)); + } - if let Ok(mut order_tree) = order_tree.try_write() { - order_tree.remove(child); - } + if let Ok(mut order_tree) = order_tree.try_write() { + order_tree.remove(*changed_entity); } } } @@ -905,7 +971,8 @@ fn update_widgets( tree.merge(&widget_context, *entity, diff, UPDATE_DEPTH); // if had_change { - // tree.dump_at(*entity); + // println!("Tree After:"); + // tree.dump_all_at(Some(world), entity.0); // } for (parent, entity) in despawn_list.drain(..) { @@ -933,20 +1000,22 @@ fn update_widgets( // Remove state entity if let Some(state_entity) = widget_state.remove(entity) { - if let Some(entity_mut) = world.get_entity_mut(state_entity) { + if let Some(mut entity_mut) = world.get_entity_mut(state_entity) { + entity_mut.remove_parent(); entity_mut.despawn_recursive(); } } // Remove widget entity - if let Some(entity_mut) = world.get_entity_mut(entity) { + if let Some(mut entity_mut) = world.get_entity_mut(entity) { log::trace!( "Removing entity! {:?} - {:?} with parent {:?}", entity.index(), entity_mut.get::<WidgetName>(), parent.index(), - // entity.index() ); + entity_mut.remove::<Parent>(); + entity_mut.remove::<bevy::prelude::Children>(); entity_mut.despawn(); // Also remove all cloned widget entities @@ -1003,7 +1072,6 @@ fn update_widgets( vec![] }; - // dbg!((entity, &children)); update_widgets( camera_entity, world, @@ -1031,44 +1099,43 @@ fn update_widgets( // If the child exists as a child of one of the children we do not need to remove it. // TODO: This is kinda of expensive we should think of a way of making this faster.. let mut contained_in_children = false; - if let Ok(order_tree) = order_tree.try_read() { - if let Some(parent) = order_tree.parent(*entity) { - trace!("Had parent for: {}", entity.0.index()); - for child in order_tree.child_iter(parent) { - if let Some(entity_ref) = world.get_entity(child.0) { - if let Some(children) = entity_ref.get::<KChildren>() { - if children.contains_entity(entity.0) { - trace!("Caught an entity that was marked as dangling but wasn't! {:?}", entity.0); - contained_in_children = true; - // Don't despawn changed entity because it exists as a child passed via props - break; + if let Ok(tree) = tree.read() { + if let Ok(order_tree) = order_tree.try_read() { + if let Some(parent) = order_tree.parent(*entity) { + 'outside_loop: for sibling in order_tree.child_iter(parent) { + for child in tree.down_iter_at(sibling, true) { + if let Some(entity_ref) = world.get_entity(child.0) { + if let Some(children) = entity_ref.get::<KChildren>() { + if children.contains_entity(entity.0) { + trace!("Caught an entity that was marked as deleted but wasn't! {:?}", entity.0); + // Don't despawn changed entity because it exists as a child passed via props + contained_in_children = true; + break 'outside_loop; + } + } } } } } } } - if !contained_in_children { - let mut despawn_list = Vec::default(); if let Ok(mut tree) = tree.write() { - for child in tree.down_iter_at(*entity, true) { - despawn_list.push(child.0); - if let Ok(mut order_tree) = order_tree.try_write() { - // had_removal = true; - log::trace!( - "Removing dangling entity! {:?} inside of: {:?}", - child.0.index(), - entity.0.index() - ); - order_tree.remove(child); - } + if let Ok(mut order_tree) = order_tree.try_write() { + log::trace!("Removing dangling entity! {:?}", entity.0.index()); + order_tree.remove(*entity); } - - for entity in despawn_list.drain(..) { - tree.remove(WrappedIndex(entity)); - if let Some(entity_mut) = world.get_entity_mut(entity) { - entity_mut.despawn(); + tree.remove(*entity); + if let Some(mut entity_mut) = world.get_entity_mut(entity.0) { + entity_mut.remove_parent(); + entity_mut.remove::<bevy::prelude::Children>(); + entity_mut.despawn(); + } + // Remove state entity + if let Some(state_entity) = widget_state.remove(entity.0) { + if let Some(mut entity_mut) = world.get_entity_mut(state_entity) { + entity_mut.remove_parent(); + entity_mut.despawn_recursive(); } } } @@ -1133,7 +1200,9 @@ fn update_widget( } else { let target = world.spawn_empty().insert(PreviousWidget).id(); if let Some(parent_id) = old_parent_entity { - world.entity_mut(parent_id).add_child(target); + if let Some(mut entity_mut) = world.get_entity_mut(parent_id) { + entity_mut.add_child(target); + } } cloned_widget_entities.insert(entity.0, target); target @@ -1259,32 +1328,12 @@ fn update_widget( commands.entity(entity.0).remove::<Mounted>(); - // let diff = if let Ok(tree) = tree.read() { - // tree.diff_children(&widget_context, entity, 0) - // } else { - // panic!("Failed to acquire read lock."); - // }; - - // log::trace!("Entity: {:?}, Diff: {:?}", entity.0, &diff); - // Always mark widget dirty if it's re-rendered. // Mark node as needing a recalculation of rendering/layout. commands.entity(entity.0).insert(DirtyNode); command_queue.apply(world); - // if let Ok(tree) = order_tree.try_read() { - // tree.dump_all(); - // } - - // for (_, child_entity, _, changes) in diff.changes.iter() { - // // Clone to entity. - // if changes.iter().any(|change| *change == Change::Deleted) { - - // } - // } - // } - (widget_context, should_update_children) } @@ -1350,8 +1399,6 @@ impl Plugin for KayakContextPlugin { } fn calculate_ui(world: &mut World) { - // dbg!("Calculating nodes!"); - let mut context_data = Vec::new(); query_world::<Query<(Entity, &mut EventDispatcher, &mut KayakRootContext)>, _, _>( diff --git a/src/context_entities.rs b/src/context_entities.rs index 5037f39f96be6f600e0e5ad59d0f51c5f9b2ae13..b2db8f6922466fccf0f99c4cf478e6fe9c7f19ac 100644 --- a/src/context_entities.rs +++ b/src/context_entities.rs @@ -8,7 +8,7 @@ use dashmap::DashMap; #[derive(Debug, Clone)] pub struct ContextEntities { - ce: Arc<DashMap<Entity, DashMap<TypeId, Entity>>>, + ce: Arc<DashMap<Option<Entity>, DashMap<TypeId, Entity>>>, } impl ContextEntities { @@ -20,7 +20,7 @@ impl ContextEntities { pub fn add_context_entity<T: Default + 'static>( &self, - parent_id: Entity, + parent_id: Option<Entity>, context_entity: Entity, ) { if !self.ce.contains_key(&parent_id) { @@ -30,7 +30,10 @@ impl ContextEntities { inner.insert(T::default().type_id(), context_entity); } - pub fn get_context_entity<T: Default + 'static>(&self, parent_id: Entity) -> Option<Entity> { + pub fn get_context_entity<T: Default + 'static>( + &self, + parent_id: Option<Entity>, + ) -> Option<Entity> { if !self.ce.contains_key(&parent_id) { return None; } diff --git a/src/event_dispatcher.rs b/src/event_dispatcher.rs index a7fcc9893869e87db18d8ab111e25f448fd7ed03..f08bbf4b5cd0f78b278d018f5408bbda3309c836 100644 --- a/src/event_dispatcher.rs +++ b/src/event_dispatcher.rs @@ -425,9 +425,8 @@ impl EventDispatcher { let mut stack_children = Vec::new(); for child in children { let child_z = world - .entity(child.0) - .get::<Node>() - .map(|node| node.z) + .get_entity(child.0) + .map(|e| e.get::<Node>().map(|node| node.z).unwrap_or(0.0)) .unwrap_or(0.0); stack_children.push((child_z, (*child, depth + 1))); } @@ -862,7 +861,7 @@ impl EventDispatcher { } } -#[derive(Resource)] +#[derive(Resource, Default)] pub struct EventDispatcherContext { cursor_capture: Option<WrappedIndex>, } diff --git a/src/on_event.rs b/src/on_event.rs index 4f839a24799877928ccac9ad4190fa3db0189122..4b8014c7388041085d16a97cdebd8f98e7a5ce40 100644 --- a/src/on_event.rs +++ b/src/on_event.rs @@ -13,7 +13,7 @@ use crate::widget_state::WidgetState; /// as a parameter. #[derive(Component, Clone)] pub struct OnEvent { - has_initialized: bool, + has_initialized: Arc<RwLock<bool>>, system: Arc<RwLock<dyn System<In = Entity, Out = ()>>>, } @@ -22,7 +22,6 @@ impl Default for OnEvent { Self::new(|In(_entity)| {}) } } - impl OnEvent { /// Create a new event handler /// @@ -31,7 +30,7 @@ impl OnEvent { /// 2. The event pub fn new<Params>(system: impl IntoSystem<Entity, (), Params>) -> OnEvent { Self { - has_initialized: false, + has_initialized: Arc::new(RwLock::new(false)), system: Arc::new(RwLock::new(IntoSystem::into_system(system))), } } @@ -48,9 +47,11 @@ impl OnEvent { world: &mut World, ) -> (EventDispatcherContext, KEvent) { if let Ok(mut system) = self.system.try_write() { - if !self.has_initialized { - system.initialize(world); - self.has_initialized = true; + if let Ok(mut has_initialized) = self.has_initialized.try_write() { + if !*has_initialized { + system.initialize(world); + *has_initialized = true; + } } // Insert resources world.insert_resource(event_dispatcher_context); @@ -58,11 +59,11 @@ impl OnEvent { world.insert_resource(event); system.run(entity, world); + system.apply_buffers(world); event_dispatcher_context = world.remove_resource::<EventDispatcherContext>().unwrap(); event = world.remove_resource::<KEvent>().unwrap(); - - system.apply_buffers(world); + world.remove_resource::<WidgetState>().unwrap(); } (event_dispatcher_context, event) } diff --git a/src/render/ui_pass.rs b/src/render/ui_pass.rs index 9a51af7adc3465762939386f02b0e2838ec81317..e18f4e84bd3e1b907556e3ba2d8433a6bfdd765b 100644 --- a/src/render/ui_pass.rs +++ b/src/render/ui_pass.rs @@ -4,7 +4,7 @@ use bevy::ecs::prelude::*; use bevy::prelude::{Color, Image}; use bevy::render::render_asset::RenderAssets; use bevy::render::render_phase::{ - BatchedPhaseItem, CachedRenderPipelinePhaseItem, DrawFunctionId, PhaseItem, + BatchedPhaseItem, CachedRenderPipelinePhaseItem, DrawFunctionId, DrawFunctions, PhaseItem, }; use bevy::render::render_resource::{CachedRenderPipelineId, RenderPassColorAttachment}; use bevy::render::{ @@ -209,6 +209,10 @@ impl Node for MainPassUINode { if let Some(opacity_layer_manager) = opacity_layer_manager.camera_layers.get(&view_entity) { + let draw_functions = world.resource::<DrawFunctions<TransparentOpacityUI>>(); + let mut draw_functions = draw_functions.write(); + draw_functions.prepare(world); + for layer_id in 1..MAX_OPACITY_LAYERS { // Start new render pass. let gpu_images = world.get_resource::<RenderAssets<Image>>().unwrap(); @@ -230,7 +234,14 @@ impl Node for MainPassUINode { let mut tracked_pass = render_context.begin_tracked_render_pass(pass_descriptor); - transparent_opacity_phase.render(&mut tracked_pass, world, view_entity); + for item in transparent_opacity_phase + .items + .iter() + .filter(|i| i.opacity_layer == layer_id) + { + let draw_function = draw_functions.get_mut(item.draw_function()).unwrap(); + draw_function.draw(world, &mut tracked_pass, view_entity, item); + } } } } diff --git a/src/render/unified/pipeline.rs b/src/render/unified/pipeline.rs index 1f929da115cee549ac98ac45421da4905f4232ee..33febefa614692ce05e4d694964f7bde38b96ca0 100644 --- a/src/render/unified/pipeline.rs +++ b/src/render/unified/pipeline.rs @@ -950,12 +950,7 @@ pub fn queue_quads_inner( position: final_position.into(), color, uv: [0.0; 4], - pos_size: [ - sprite_rect.min.x, - sprite_rect.min.y, - sprite_rect.size().x, - sprite_rect.size().y, - ], + pos_size: [0.0, 0.0, sprite_rect.size().x, sprite_rect.size().y], }); } *index += indices.len() as u32; diff --git a/src/tree.rs b/src/tree.rs index e8f456c76f5046e56a9056a3ea2232fb6265d38f..65b6b3f994936abcd8ce5e18f1eda2662ff5593b 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -1,8 +1,9 @@ -use bevy::prelude::Entity; +use bevy::prelude::{Entity, World}; use bevy::utils::HashMap; use morphorm::Hierarchy; use std::iter::Rev; +use crate::context::WidgetName; use crate::node::WrappedIndex; #[derive(Default, Debug, Clone, PartialEq, Eq)] @@ -34,6 +35,25 @@ impl ChildChanges { .iter() .all(|change| change.3.iter().all(|c| *c == Change::Unchanged)) } + + pub fn debug_print(&self, world: &World) { + for (index, child, parent, changes) in self.changes.iter() { + if let Some(entity_ref) = world.get_entity(child.0) { + let name = entity_ref + .get::<WidgetName>() + .map(|n| n.0.clone()) + .unwrap_or("Unknown".into()); + println!( + "[name: {}, index: {}, entity: {}, parent: {}, change: {:?}]", + name, + index, + child.0.index(), + parent.0.index(), + changes + ); + } + } + } } impl From<Vec<(usize, WrappedIndex, WrappedIndex, Vec<Change>)>> for ChildChanges { @@ -59,6 +79,11 @@ impl Tree { } } + pub fn remove_without_children(&mut self, index: WrappedIndex) { + self.parents.remove(&index); + self.children.remove(&index); + } + /// Remove the given node and recursively removes its descendants pub fn remove(&mut self, index: WrappedIndex) -> Vec<WrappedIndex> { let parent = self.parents.remove(&index); @@ -630,19 +655,74 @@ impl Tree { /// Sometimes we need to see the entire tree even dangling nodes. /// This function will display everything. - pub fn dump_all(&self) { + pub fn dump_all(&self, world: &World) { let mut children = self.children.iter().collect::<Vec<_>>(); children.sort_by(|(a, _), (b, _)| a.0.index().partial_cmp(&b.0.index()).unwrap()); for (parent, children) in children.iter() { - println!("[{}]", parent.0.index()); + let name = if let Some(entity_ref) = world.get_entity(parent.0) { + entity_ref.get::<WidgetName>().map(|n| n.0.clone()) + } else { + None + }; + println!( + "[{}::{}]", + name.unwrap_or("Unknown".into()), + parent.0.index() + ); for child in children.iter() { - println!(" [{}]", child.0.index()); + let name = if let Some(entity_ref) = world.get_entity(parent.0) { + entity_ref.get::<WidgetName>().map(|n| n.0.clone()) + } else { + None + }; + println!( + " [{}::{}]", + name.unwrap_or("Unknown".into()), + child.0.index() + ); } println!(); } } + /// Sometimes we need to see the entire tree even dangling nodes. + /// This function will display everything. + pub fn dump_all_at(&self, world: Option<&World>, parent: Entity) { + let no_children = vec![]; + let children = self + .children + .get(&WrappedIndex(parent)) + .unwrap_or(&no_children); + + let name: Option<String> = if let Some(world) = world { + if let Some(entity_ref) = world.get_entity(parent) { + entity_ref.get::<WidgetName>().map(|n| n.0.clone()) + } else { + None + } + } else { + None + }; + println!("[{}::{}]", name.unwrap_or("Unknown".into()), parent.index()); + for child in children.iter() { + let name = if let Some(world) = world { + if let Some(entity_ref) = world.get_entity(child.0) { + entity_ref.get::<WidgetName>().map(|n| n.0.clone()) + } else { + None + } + } else { + None + }; + println!( + " [{}::{}]", + name.unwrap_or("Unknown".into()), + child.0.index() + ); + } + } + /// Dumps a section of the tree's current state to the console (starting from a specific index) /// /// To dump the entire tree, use [dump] instead. diff --git a/src/widget_context.rs b/src/widget_context.rs index 146af554d5bc1ed5c4cf622bcbbf25ae6c745776..8e4578dcede7c6335971f1b391861f0dbef42ef5 100644 --- a/src/widget_context.rs +++ b/src/widget_context.rs @@ -72,10 +72,8 @@ impl KayakWidgetContext { 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); - } + self.context_entities + .add_context_entity::<T>(parent_id, context_entity); } /// Finds the closest matching context entity by traversing up the tree. @@ -86,7 +84,7 @@ impl KayakWidgetContext { // Check self first.. if let Some(entity) = self .context_entities - .get_context_entity::<T>(current_entity) + .get_context_entity::<T>(Some(current_entity)) { return Some(entity); } @@ -97,12 +95,17 @@ impl KayakWidgetContext { while parent.is_some() { if let Some(entity) = self .context_entities - .get_context_entity::<T>(parent.unwrap().0) + .get_context_entity::<T>(Some(parent.unwrap().0)) { return Some(entity); } parent = tree.get_parent(parent.unwrap()); } + + // Finally check root AKA no parent. + if let Some(entity) = self.context_entities.get_context_entity::<T>(None) { + return Some(entity); + } } None @@ -197,7 +200,7 @@ impl KayakWidgetContext { pub fn spawn_widget( &self, commands: &mut Commands, - key: Option<&'static str>, + key: Option<&str>, parent_id: Option<Entity>, ) -> Entity { let mut entity = None; @@ -359,6 +362,11 @@ impl KayakWidgetContext { if let Ok(tree) = self.old_tree.read() { tree.dump_at(WrappedIndex(entity)); } + + println!("Order tree:"); + if let Ok(order_tree) = self.order_tree.read() { + order_tree.dump_all_at(None, entity); + } } /// Consumes the tree