From ce55442dc9b342d57778bdf676a86ea12c214d0f Mon Sep 17 00:00:00 2001 From: StarArawn <toasterthegamer@gmail.com> Date: Mon, 13 Dec 2021 11:05:24 -0500 Subject: [PATCH] Lifetime handling of states. --- Cargo.lock | 2 +- examples/counter.rs | 2 +- kayak_core/Cargo.toml | 2 +- kayak_core/src/context.rs | 119 +++++++++++++++++++++++++------------- 4 files changed, 81 insertions(+), 44 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bbc3219..e5d1b53 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1483,7 +1483,7 @@ checksum = "398ea4fabe40b9b0d885340a2a991a44c8a645624075ad966d21f88688e2b69e" [[package]] name = "flo_binding" version = "2.0.2" -source = "git+https://github.com/StarArawn/flo_binding.git?rev=609883068c43dc897da2b277e91320a41892643d#609883068c43dc897da2b277e91320a41892643d" +source = "git+https://github.com/StarArawn/flo_binding.git?rev=c78431a56df5ec082b7e1c271871e6c0ac75e81e#c78431a56df5ec082b7e1c271871e6c0ac75e81e" dependencies = [ "desync", "flo_rope", diff --git a/examples/counter.rs b/examples/counter.rs index 08c0a73..3bfe4df 100644 --- a/examples/counter.rs +++ b/examples/counter.rs @@ -5,13 +5,13 @@ use bevy::{ PipelinedDefaultPlugins, }; use bevy_kayak_ui::{BevyContext, BevyKayakUIPlugin, FontMapping, UICameraBundle}; -use kayak_widgets::{Button, Text, Window}; use kayak_core::{ styles::{Style, StyleProp, Units}, Bound, EventType, Index, MutableBound, OnEvent, }; use kayak_ui::components::App; use kayak_ui::core::{rsx, widget}; +use kayak_widgets::{Button, Text, Window}; #[widget] fn Counter(context: &mut KayakContext) { diff --git a/kayak_core/Cargo.toml b/kayak_core/Cargo.toml index 4a5cc1a..ff68615 100644 --- a/kayak_core/Cargo.toml +++ b/kayak_core/Cargo.toml @@ -10,7 +10,7 @@ as-any = "0.2" dashmap = "4.0" diff-struct = "0.3" derivative = "2.2" -flo_binding = { git = "https://github.com/StarArawn/flo_binding.git", rev = "609883068c43dc897da2b277e91320a41892643d" } +flo_binding = { git = "https://github.com/StarArawn/flo_binding.git", rev = "c78431a56df5ec082b7e1c271871e6c0ac75e81e" } fontdue = "0.6" kayak_render_macros = { path = "../kayak_render_macros" } morphorm = { git = "https://github.com/geom3trik/morphorm" } diff --git a/kayak_core/src/context.rs b/kayak_core/src/context.rs index b2a60ca..a5cd1c4 100644 --- a/kayak_core/src/context.rs +++ b/kayak_core/src/context.rs @@ -4,9 +4,10 @@ use std::collections::HashMap; use crate::{node::NodeIndex, widget_manager::WidgetManager, Event, EventType, Index, InputEvent}; pub struct KayakContext { - component_states: HashMap<crate::Index, resources::Resources>, + widget_states: HashMap<crate::Index, resources::Resources>, global_bindings: HashMap<crate::Index, Vec<flo_binding::Uuid>>, - // component_state_lifetimes: DashMap<crate::Index, Vec<Box<dyn crate::Releasable>>>, + widget_state_lifetimes: + HashMap<crate::Index, HashMap<flo_binding::Uuid, Box<dyn crate::Releasable>>>, current_id: Index, pub widget_manager: WidgetManager, last_mouse_position: (f32, f32), @@ -25,9 +26,9 @@ impl std::fmt::Debug for KayakContext { impl KayakContext { pub fn new() -> Self { Self { - component_states: HashMap::new(), + widget_states: HashMap::new(), global_bindings: HashMap::new(), - // component_state_lifetimes: DashMap::new(), + widget_state_lifetimes: HashMap::new(), current_id: crate::Index::default(), widget_manager: WidgetManager::new(), last_mouse_position: (0.0, 0.0), @@ -37,7 +38,7 @@ impl KayakContext { } /// Binds some global state to the current widget. - pub fn bind<T: Clone + PartialEq + Send + 'static>( + pub fn bind<T: Clone + PartialEq + Send + Sync + 'static>( &mut self, global_state: &crate::Binding<T>, ) { @@ -50,18 +51,22 @@ impl KayakContext { if !global_binding_ids.contains(&global_state.id) { let cloned_id = self.current_id; let dirty_nodes = self.widget_manager.dirty_nodes.clone(); - let mut lifetime = global_state.when_changed(crate::notify(move || { + let lifetime = global_state.when_changed(crate::notify(move || { if let Ok(mut dirty_nodes) = dirty_nodes.lock() { dirty_nodes.insert(cloned_id); } })); - // TODO: Figure out how to store this so we can drop the lifetime on unbind. - lifetime.keep_alive(); + Self::insert_state_lifetime( + &mut self.widget_state_lifetimes, + self.current_id, + global_state.id, + lifetime, + ); global_binding_ids.push(global_state.id); } } - pub fn unbind<T: Clone + PartialEq + Send + 'static>( + pub fn unbind<T: Clone + PartialEq + Send + Sync + 'static>( &mut self, global_state: &crate::Binding<T>, ) { @@ -72,6 +77,12 @@ impl KayakContext { .position(|id| *id == global_state.id) { global_binding_ids.remove(index); + + Self::remove_state_lifetime( + &mut self.widget_state_lifetimes, + self.current_id, + global_state.id, + ); } } } @@ -84,23 +95,23 @@ impl KayakContext { &mut self, initial_state: T, ) -> Option<crate::Binding<T>> { - if self.component_states.contains_key(&self.current_id) { - let states = self.component_states.get_mut(&self.current_id).unwrap(); + if self.widget_states.contains_key(&self.current_id) { + let states = self.widget_states.get_mut(&self.current_id).unwrap(); if !states.contains::<crate::Binding<T>>() { let state = crate::bind(initial_state); let dirty_nodes = self.widget_manager.dirty_nodes.clone(); let cloned_id = self.current_id; - let mut lifetime = state.when_changed(crate::notify(move || { + let lifetime = state.when_changed(crate::notify(move || { if let Ok(mut dirty_nodes) = dirty_nodes.lock() { dirty_nodes.insert(cloned_id); } })); - lifetime.keep_alive(); - // Self::insert_state_lifetime( - // &mut self.component_state_lifetimes, - // self.current_id, - // lifetime, - // ); + Self::insert_state_lifetime( + &mut self.widget_state_lifetimes, + self.current_id, + state.id, + lifetime, + ); states.insert(state); } } else { @@ -108,40 +119,66 @@ impl KayakContext { let state = crate::bind(initial_state); let dirty_nodes = self.widget_manager.dirty_nodes.clone(); let cloned_id = self.current_id; - let mut lifetime = state.when_changed(crate::notify(move || { + let lifetime = state.when_changed(crate::notify(move || { if let Ok(mut dirty_nodes) = dirty_nodes.lock() { dirty_nodes.insert(cloned_id); } })); - lifetime.keep_alive(); - // Self::insert_state_lifetime( - // &mut self.component_state_lifetimes, - // self.current_id, - // lifetime, - // ); + Self::insert_state_lifetime( + &mut self.widget_state_lifetimes, + self.current_id, + state.id, + lifetime, + ); states.insert(state); - self.component_states.insert(self.current_id, states); + self.widget_states.insert(self.current_id, states); } return self.get_state(); } - // fn insert_state_lifetime( - // lifetimes: &mut DashMap<crate::Index, Vec<Box<dyn crate::Releasable>>>, - // id: Index, - // lifetime: Box<dyn crate::Releasable>, - // ) { - // if lifetimes.contains_key(&id) { - // if let Some(mut lifetimes) = lifetimes.get_mut(&id) { - // lifetimes.push(lifetime); - // } - // } else { - // lifetimes.insert(id, vec![lifetime]); - // } - // } + fn insert_state_lifetime( + lifetimes: &mut HashMap< + crate::Index, + HashMap<flo_binding::Uuid, Box<dyn crate::Releasable>>, + >, + id: Index, + binding_id: flo_binding::Uuid, + lifetime: Box<dyn crate::Releasable>, + ) { + if lifetimes.contains_key(&id) { + if let Some(lifetimes) = lifetimes.get_mut(&id) { + if !lifetimes.contains_key(&binding_id) { + lifetimes.insert(binding_id, lifetime); + } + } + } else { + let mut new_hashmap = HashMap::new(); + new_hashmap.insert(binding_id, lifetime); + lifetimes.insert(id, new_hashmap); + } + } + + fn remove_state_lifetime( + lifetimes: &mut HashMap< + crate::Index, + HashMap<flo_binding::Uuid, Box<dyn crate::Releasable>>, + >, + id: Index, + binding_id: flo_binding::Uuid, + ) { + if lifetimes.contains_key(&id) { + if let Some(lifetimes) = lifetimes.get_mut(&id) { + if lifetimes.contains_key(&binding_id) { + let mut binding_lifetime = lifetimes.remove(&binding_id).unwrap(); + binding_lifetime.done(); + } + } + } + } fn get_state<T: resources::Resource + Clone + PartialEq>(&self) -> Option<T> { - if self.component_states.contains_key(&self.current_id) { - let states = self.component_states.get(&self.current_id).unwrap(); + if self.widget_states.contains_key(&self.current_id) { + let states = self.widget_states.get(&self.current_id).unwrap(); if let Ok(state) = states.get::<T>() { return Some(state.clone()); } -- GitLab