From bdfc64560c182c1797f983878e032ce0a32d0d29 Mon Sep 17 00:00:00 2001
From: StarToaster <startoaster23@gmail.com>
Date: Fri, 28 Oct 2022 10:11:12 -0400
Subject: [PATCH] Improve text calculations. Improved bevy_scene example.

---
 examples/bevy_scene.rs | 13 +++---
 src/calculate_nodes.rs | 94 ++++++++++++++++++++++++------------------
 src/context.rs         | 28 ++++++++++---
 src/layout.rs          |  2 +-
 src/widgets/window.rs  |  5 ++-
 5 files changed, 91 insertions(+), 51 deletions(-)

diff --git a/examples/bevy_scene.rs b/examples/bevy_scene.rs
index 7804645..68a6568 100644
--- a/examples/bevy_scene.rs
+++ b/examples/bevy_scene.rs
@@ -79,17 +79,20 @@ fn move_active_tile(mut tile: Query<(&mut Transform, &ActiveTile)>) {
 
 /// A system that moves the ghost tile to the cursor's position
 fn move_ghost_tile(
+    event_context: Res<EventDispatcher>,
     mut tile: Query<&mut Transform, With<GhostTile>>,
     mut cursor_moved: EventReader<CursorMoved>,
     camera_transform: Query<&GlobalTransform, With<WorldCamera>>,
     windows: Res<Windows>,
 ) {
     for _ in cursor_moved.iter() {
-        let world_pos = cursor_to_world(&windows, &camera_transform.single());
-        let tile_pos = world_to_tile(world_pos);
-        let mut ghost = tile.single_mut();
-        ghost.translation.x = tile_pos.x;
-        ghost.translation.y = tile_pos.y;
+        if !event_context.contains_cursor() {
+            let world_pos = cursor_to_world(&windows, &camera_transform.single());
+            let tile_pos = world_to_tile(world_pos);
+            let mut ghost = tile.single_mut();
+            ghost.translation.x = tile_pos.x;
+            ghost.translation.y = tile_pos.y;
+        }
     }
 }
 
diff --git a/src/calculate_nodes.rs b/src/calculate_nodes.rs
index 593c86e..a053978 100644
--- a/src/calculate_nodes.rs
+++ b/src/calculate_nodes.rs
@@ -3,6 +3,7 @@ use bevy::{
     utils::HashMap,
 };
 use kayak_font::KayakFont;
+use morphorm::Hierarchy;
 
 use crate::{
     layout::{DataCache, Rect},
@@ -21,7 +22,6 @@ pub fn calculate_nodes(
     query: Query<Entity, With<DirtyNode>>,
     all_styles_query: Query<&KStyle>,
     node_query: Query<(Entity, &Node)>,
-    nodes_no_entity_query: Query<&'static Node>,
 ) {
     let mut new_nodes = HashMap::<Entity, (Node, bool)>::default();
     // This is the maximum recursion depth for this method.
@@ -95,6 +95,7 @@ pub fn calculate_nodes(
                 &context,
                 &fonts,
                 &font_mapping,
+                &query,
                 // &node_query,
                 dirty_entity,
                 &mut styles,
@@ -139,18 +140,30 @@ pub fn calculate_nodes(
                 log::trace!("{:?} needs layout!", entity.id());
             }
         }
+    }
+}
 
-        {
-            let context = context.as_mut();
-            if let Ok(tree) = context.tree.try_read() {
-                // tree.dump();
-                let node_tree = &*tree;
-                if let Ok(mut cache) = context.layout_cache.try_write() {
-                    let mut data_cache = DataCache {
-                        cache: &mut cache,
-                        query: &nodes_no_entity_query,
-                    };
-                    morphorm::layout(&mut data_cache, node_tree, &nodes_no_entity_query);
+pub fn calculate_layout(
+    mut commands: Commands,
+    mut context: ResMut<KayakRootContext>,
+    nodes_no_entity_query: Query<&'static Node>,
+) {
+    let context = context.as_mut();
+    if let Ok(tree) = context.tree.try_read() {
+        // tree.dump();
+        let node_tree = &*tree;
+        if let Ok(mut cache) = context.layout_cache.try_write() {
+            let mut data_cache = DataCache {
+                cache: &mut cache,
+                query: &nodes_no_entity_query,
+            };
+            morphorm::layout(&mut data_cache, node_tree, &nodes_no_entity_query);
+
+            for (entity, change) in cache.geometry_changed.iter() {
+                if !change.is_empty() {
+                    for child in tree.child_iter(*entity) {
+                        commands.entity(child.0).insert(DirtyNode);
+                    }
                 }
             }
         }
@@ -163,11 +176,12 @@ fn create_primitive(
     fonts: &Assets<KayakFont>,
     font_mapping: &FontMapping,
     // query: &Query<(Entity, &Node)>,
+    dirty: &Query<Entity, With<DirtyNode>>,
     id: WrappedIndex,
     styles: &mut KStyle,
 ) -> (RenderPrimitive, bool) {
     let mut render_primitive = RenderPrimitive::from(&styles.clone());
-    let mut needs_layout = false;
+    let mut needs_layout = true;
 
     match &mut render_primitive {
         RenderPrimitive::Text {
@@ -183,38 +197,40 @@ fn create_primitive(
                 // self.bind(id, &asset);
                 if let Ok(node_tree) = context.tree.try_read() {
                     if let Some(parent_id) = node_tree.get_parent(id) {
-                        if let Some(parent_layout) = context.get_layout(&parent_id) {
-                            properties.max_size = (parent_layout.width, parent_layout.height);
-
-                            if properties.max_size.0 == 0.0 || properties.max_size.1 == 0.0 {
-                                needs_layout = true;
-                            }
-
-                            // --- Calculate Text Layout --- //
-                            *text_layout = font.measure(&content, *properties);
-                            let measurement = text_layout.size();
-
-                            // --- Apply Layout --- //
-                            if matches!(styles.width, StyleProp::Default) {
-                                styles.width = StyleProp::Value(Units::Pixels(measurement.0));
+                        if !dirty.contains(parent_id.0) {
+                            if let Some(parent_layout) = context.get_layout(&parent_id) {
+                                properties.max_size = (parent_layout.width, parent_layout.height);
+
+                                if properties.max_size.0 == 0.0 || properties.max_size.1 == 0.0 {
+                                    needs_layout = true;
+                                } else {
+                                    needs_layout = false;
+                                }
+
+                                if context.get_geometry_changed(&parent_id) {
+                                    needs_layout = true;
+                                }
+
+                                // --- Calculate Text Layout --- //
+                                *text_layout = font.measure(&content, *properties);
+                                let measurement = text_layout.size();
+
+                                // --- Apply Layout --- //
+                                if matches!(styles.width, StyleProp::Default) {
+                                    styles.width = StyleProp::Value(Units::Pixels(measurement.0));
+                                }
+                                if matches!(styles.height, StyleProp::Default) {
+                                    styles.height = StyleProp::Value(Units::Pixels(measurement.1));
+                                }
                             }
-                            if matches!(styles.height, StyleProp::Default) {
-                                styles.height = StyleProp::Value(Units::Pixels(measurement.1));
-                            }
-                        } else {
-                            needs_layout = true;
                         }
-                    } else {
-                        needs_layout = true;
                     }
-                } else {
-                    needs_layout = true;
                 }
-            } else {
-                needs_layout = true;
             }
         }
-        _ => {}
+        _ => {
+            needs_layout = false;
+        }
     }
 
     if needs_layout {
diff --git a/src/context.rs b/src/context.rs
index d3f3d37..4096dc8 100644
--- a/src/context.rs
+++ b/src/context.rs
@@ -8,7 +8,7 @@ use bevy::{
 use morphorm::Hierarchy;
 
 use crate::{
-    calculate_nodes::calculate_nodes,
+    calculate_nodes::{calculate_layout, calculate_nodes},
     children::KChildren,
     clone_component::{clone_state, clone_system, EntityCloneSystems, PreviousWidget},
     context_entities::ContextEntities,
@@ -111,6 +111,18 @@ impl KayakRootContext {
         }
     }
 
+    pub(crate) fn get_geometry_changed(&self, id: &WrappedIndex) -> bool {
+        if let Ok(cache) = self.layout_cache.try_read() {
+            if let Some(geometry_changed) = cache.geometry_changed.get(id) {
+                !geometry_changed.is_empty()
+            } else {
+                false
+            }
+        } else {
+            false
+        }
+    }
+
     /// Adds a new set of systems for a widget type.
     /// Update systems are ran every frame and return true or false depending on if the widget has "changed".
     /// Render systems are ran only if the widget has changed and are meant to re-render children and handle
@@ -709,12 +721,18 @@ impl Plugin for KayakContextPlugin {
 
 fn calculate_ui(world: &mut World) {
     // dbg!("Calculating nodes!");
-    let mut system = IntoSystem::into_system(calculate_nodes);
-    system.initialize(world);
+    let mut node_system = IntoSystem::into_system(calculate_nodes);
+    node_system.initialize(world);
+
+    let mut layout_system = IntoSystem::into_system(calculate_layout);
+    layout_system.initialize(world);
 
     for _ in 0..5 {
-        system.run((), world);
-        system.apply_buffers(world);
+        node_system.run((), world);
+        node_system.apply_buffers(world);
+
+        layout_system.run((), world);
+        layout_system.apply_buffers(world);
         world.resource_scope::<KayakRootContext, _>(|world, mut context| {
             LayoutEventDispatcher::dispatch(&mut context, world);
         });
diff --git a/src/layout.rs b/src/layout.rs
index c09176e..8f49ce5 100644
--- a/src/layout.rs
+++ b/src/layout.rs
@@ -67,7 +67,7 @@ pub(crate) struct LayoutCache {
     ///
     /// This should only contain entries for nodes that have _at least one_ flag set.
     /// If a node does not have any flags set, then they should be removed from the map.
-    geometry_changed: HashMap<WrappedIndex, GeometryChanged>,
+    pub(crate) geometry_changed: HashMap<WrappedIndex, GeometryChanged>,
 
     visible: HashMap<WrappedIndex, bool>,
 }
diff --git a/src/widgets/window.rs b/src/widgets/window.rs
index 5fa45fd..d51c0f5 100644
--- a/src/widgets/window.rs
+++ b/src/widgets/window.rs
@@ -179,7 +179,10 @@ pub fn window_render(
                     }
                     <ClipBundle
                         styles={KStyle {
-                            padding: StyleProp::Value(Edge::all(Units::Pixels(10.0))),
+                            top: Units::Pixels(10.0).into(),
+                            left: Units::Pixels(10.0).into(),
+                            right: Units::Pixels(10.0).into(),
+                            bottom: Units::Pixels(10.0).into(),
                             ..Default::default()
                         }}
                         children={window_children.clone()}
-- 
GitLab