diff --git a/mangades/src/component_demo.rs b/mangades/src/component_demo.rs index 2ab9645..ad9a3de 100644 --- a/mangades/src/component_demo.rs +++ b/mangades/src/component_demo.rs @@ -1,11 +1,13 @@ use std::sync::{Arc, Mutex, RwLock}; use mangui::{SharedNode, nodes::{layout::Layout, Style}, taffy::prelude::Size}; -use rusalka::{component::Component, nodes::{primitives::{Rectangle, RectangleAttributes}, insert, detach}, SharedComponent}; +use rusalka::{component::Component, nodes::{primitives::{Rectangle, RectangleAttributes}, insert, detach}, SharedComponent, WeakSharedComponent, invalidator::Invalidator}; pub struct DemoComponent { rect: SharedComponent, attrs: DemoComponentAttributes, - layout: Arc> + layout: Arc>, + selfref: WeakSharedComponent, + test: Arc>> } #[derive(Default)] @@ -23,14 +25,15 @@ impl From for PartialDemoComponentAttributes { impl Component for DemoComponent { type ComponentAttrs = DemoComponentAttributes; type PartialComponentAttrs = PartialDemoComponentAttributes; - fn new(attrs: Self::ComponentAttrs) -> Self { - Self { - rect: Arc::new(Mutex::new(Rectangle::new(RectangleAttributes { ..Default::default() }))), + fn new(attrs: Self::ComponentAttrs, selfref: WeakSharedComponent) -> Self { + let test = Arc::new(Mutex::new(Invalidator::new(false))); + let self_ = Self { + rect: Arc::new_cyclic(|selfref| Mutex::new(Rectangle::new(RectangleAttributes { ..Default::default() }, selfref.clone()))), layout: Arc::new(RwLock::new(Layout { style: Style { layout: mangui::nodes::TaffyStyle { min_size: Size { - width: mangui::taffy::style::Dimension::Points(50.), + width: mangui::taffy::style::Dimension::Points( if **test.lock().unwrap() { 50. } else { 100. }), height: mangui::taffy::style::Dimension::Points(100.) }, ..Default::default() @@ -40,7 +43,26 @@ impl Component for DemoComponent { ..Default::default() })), attrs, - } + selfref, + test + }; + let selfref = self_.selfref.clone(); + self_.layout.write().unwrap().events.add_handler(Box::new(move |event| { + let selfref = selfref.upgrade().unwrap(); + let self_ = selfref.lock().unwrap(); + let test = &self_.test; + let attrs = &self_.attrs; + match event.event { + mangui::events::InnerEvent::MouseDown(_) => { + **test.lock().unwrap() = true; + }, + mangui::events::InnerEvent::MouseUp(_) => { + **test.lock().unwrap() = false; + }, + _ => {} + } + })); + self_ } fn set(&mut self, _attrs: Self::PartialComponentAttrs) { } diff --git a/mangades/src/component_demo_syntax.rs b/mangades/src/component_demo_syntax.rs index 817e26b..98718e3 100644 --- a/mangades/src/component_demo_syntax.rs +++ b/mangades/src/component_demo_syntax.rs @@ -6,18 +6,32 @@ use rusalka::nodes::primitives::{Rectangle, RectangleAttributes}; make_component!( ComponentDemo, - Logic { + MainLogic { let radius = attrs.radius; } Attributes { radius: f32 } + Variables { + test: bool = false + } Component { @layout { @Rectangle { - radius, + radius: if $test { radius } else { 0. }, ..Default::default() } + $|event| { + match event.event { + mangui::events::InnerEvent::MouseDown(_) => { + $test = true; + }, + mangui::events::InnerEvent::MouseUp(_) => { + $test = false; + }, + _ => {} + } + } ..Default::default() } } diff --git a/mangades/src/main.rs b/mangades/src/main.rs index ff45582..3501d9b 100644 --- a/mangades/src/main.rs +++ b/mangades/src/main.rs @@ -1,6 +1,6 @@ use std::sync::{RwLock, Arc, mpsc}; -use mangui::{self, nodes::{layout::Layout, self, Style, TaffyStyle}, taffy::{self, prelude::Size, style::Dimension}, femtovg::{Paint, Color}, SharedNode, MainEntry}; +use mangui::{self, nodes::{layout::Layout, self, Style, TaffyStyle}, taffy::{self, prelude::Size, style::Dimension}, femtovg::{Paint, Color}, SharedNode, MainEntry, events::NodeEvent}; mod component_demo; mod component_demo_syntax; @@ -30,7 +30,8 @@ fn main() { })); root.children.push(right_node.clone()); right_node.clone().write().unwrap().events.add_handler(Box::new(move |event| { - match event.event { + let NodeEvent { target, path, event } = event; + match event { mangui::events::InnerEvent::MouseDown(_) => { right_node.write().unwrap().fill = Paint::color(Color::rgb(255, 0, 255)); }, diff --git a/rusalka-macro/src/lib.rs b/rusalka-macro/src/lib.rs index 2ea9709..2056dd9 100644 --- a/rusalka-macro/src/lib.rs +++ b/rusalka-macro/src/lib.rs @@ -8,6 +8,11 @@ struct Attribute { type_: TokenStream } +#[derive(Debug)] +struct EventListener { + callback: TokenStream +} + #[derive(Debug)] enum ComponentType { RealComponent, @@ -19,7 +24,8 @@ struct ComponentUsed { name: Ident, contents: TokenStream, parent: Option, - component_type: ComponentType + component_type: ComponentType, + event_listeners: Vec } #[proc_macro] @@ -41,7 +47,7 @@ pub fn make_component(item: TokenStream) -> TokenStream { let mut main_logic = Vec::new(); - // let mut reactive_variables = Vec::new(); + let mut reactive_variables = Vec::new(); let mut components_used: Vec = Vec::new(); @@ -52,7 +58,7 @@ pub fn make_component(item: TokenStream) -> TokenStream { let ident = ident.to_string(); match ident.as_str() { - "Logic" | "Component" | "Attributes" => {}, + "MainLogic" | "Component" | "Attributes" | "Variables" => {}, _ => panic!("Unknown identifier: {:?}", ident) } }, @@ -60,7 +66,7 @@ pub fn make_component(item: TokenStream) -> TokenStream { match &last_identifier { Some(ident) => { match ident.as_str() { - "Attributes" => { + "Attributes" | "Variables" => { // A struct-like definition of attributes // Example syntax: // Attributes { // we're here @@ -120,6 +126,12 @@ pub fn make_component(item: TokenStream) -> TokenStream { } } + let array = if ident.as_str() == "Variables" { + &mut reactive_variables + } else { + &mut attributes + }; + if last_was_set { let mut default = TokenStream::new(); @@ -141,13 +153,13 @@ pub fn make_component(item: TokenStream) -> TokenStream { } } - attributes.push(Attribute { + array.push(Attribute { name, default: Some(default), type_ }); } else { - attributes.push(Attribute { + array.push(Attribute { name, default: None, type_ @@ -155,7 +167,7 @@ pub fn make_component(item: TokenStream) -> TokenStream { } } }, - "Logic" => { + "MainLogic" => { main_logic.push(group.stream()); }, "Component" => { @@ -253,6 +265,19 @@ pub fn make_component(item: TokenStream) -> TokenStream { i+=1; } + for variable in &reactive_variables { + component_struct_stream.extend(Some(TokenTree::Ident(variable.name.clone()))); + component_struct_stream.extend(TokenStream::from(quote!(:))); + component_struct_stream.extend(TokenStream::from(quote!(Arc>>))); + component_struct_stream.extend(TokenStream::from(quote!(,))); + } + + // selfref + + component_struct_stream.extend(TokenStream::from(quote!(selfref: rusalka::WeakSharedComponent,))); + // Attributes struct let attributes_ident = Ident::new(&format!("{str_name}Attributes"), Span::call_site()); @@ -355,10 +380,29 @@ pub fn make_component(item: TokenStream) -> TokenStream { // fn new - component_impl_stream.extend(TokenStream::from(quote!(fn new(attrs: Self::ComponentAttrs) -> Self))); + component_impl_stream.extend(TokenStream::from(quote!(fn new(attrs: Self::ComponentAttrs, selfref: rusalka::WeakSharedComponent) -> Self))); let mut new_stream = TokenStream::new(); + for variable in &reactive_variables { + new_stream.extend(TokenStream::from(quote!(let))); + new_stream.extend(Some(TokenTree::Ident(variable.name.clone()))); + new_stream.extend(TokenStream::from(quote!(:))); + // new_stream.extend(TokenStream::from(quote!(Arc::new(Mutex::new(Invalidator::new))))); + + let mut invalidator = TokenStream::from(quote!(rusalka::invalidator::Invalidator::new)); + let mut invalidator_inner = TokenStream::new(); + if let Some(def) = &variable.default { + invalidator_inner.extend(def.clone()); + } else { + invalidator_inner.extend(TokenStream::from(quote!(Default::default()))); + } + invalidator.extend(Some(TokenTree::Group(Group::new(proc_macro::Delimiter::Parenthesis, invalidator_inner)))); + new_stream.extend(wrap_in_arc_mutex(invalidator)); + + new_stream.extend(TokenStream::from(quote!(;))); + } + new_stream.extend(main_logic); new_stream.extend(TokenStream::from(quote!(Self))); @@ -369,7 +413,6 @@ pub fn make_component(item: TokenStream) -> TokenStream { for component in &components_used { let ident = Ident::new(&format!("comp{}", i), Span::call_site()); let component_name = component.name.clone(); - dbg!(&component_name); new_returnvalue_stream.extend(Some(TokenTree::Ident(ident))); new_returnvalue_stream.extend(TokenStream::from(quote!(:))); @@ -394,12 +437,14 @@ pub fn make_component(item: TokenStream) -> TokenStream { let components_attributes_group = Group::new(proc_macro::Delimiter::Brace, component.contents.clone()); component_new_stream.extend(Some(TokenTree::Group(components_attributes_group))); + + component_new_stream.extend(TokenStream::from(quote!(, cselfref.clone()))); let component_new_group = Group::new(proc_macro::Delimiter::Parenthesis, component_new_stream); component_stream.extend(Some(TokenTree::Group(component_new_group))); - new_returnvalue_stream.extend(wrap_in_arcmutex(component_stream)); + new_returnvalue_stream.extend(wrap_in_arcmutex_cyclic(component_stream)); }, ComponentType::Node => { let node_group = Group::new(proc_macro::Delimiter::Brace, component.contents.clone()); @@ -412,7 +457,7 @@ pub fn make_component(item: TokenStream) -> TokenStream { i+=1; } - new_returnvalue_stream.extend(TokenStream::from(quote!(attrs))); + new_returnvalue_stream.extend(TokenStream::from(quote!(attrs, selfref))); let new_returnvalue_group = Group::new(proc_macro::Delimiter::Brace, new_returnvalue_stream); new_stream.extend(Some(TokenTree::Group(new_returnvalue_group))); @@ -483,12 +528,11 @@ pub fn make_component(item: TokenStream) -> TokenStream { match component.component_type { ComponentType::RealComponent => { - let mut component_stream = TokenStream::new(); - - component_stream.extend(TokenStream::from(quote!(self.))); - component_stream.extend(Some(TokenTree::Ident(ident))); - component_stream.extend(TokenStream::from(quote!(.lock().unwrap().mount))); - + // mount + mount_stream.extend(TokenStream::from(quote!(self.))); + mount_stream.extend(Some(TokenTree::Ident(ident))); + mount_stream.extend(TokenStream::from(quote!(.lock().unwrap().mount))); + let mut component_mount_stream = TokenStream::new(); match component.parent { @@ -514,9 +558,8 @@ pub fn make_component(item: TokenStream) -> TokenStream { let component_mount_group = Group::new(proc_macro::Delimiter::Parenthesis, component_mount_stream); - component_stream.extend(Some(TokenTree::Group(component_mount_group))); - - mount_stream.extend(component_stream); + mount_stream.extend(Some(TokenTree::Group(component_mount_group))); + mount_stream.extend(TokenStream::from(quote!(;))); }, ComponentType::Node => { @@ -605,10 +648,13 @@ pub fn make_component(item: TokenStream) -> TokenStream { output.extend(Some(TokenTree::Group(Group::new(proc_macro::Delimiter::Brace, component_impl_stream)))); + // dbg!(&output); dbg!(attributes); + dbg!(reactive_variables); + println!("{}", output.to_string()); println!(); @@ -616,12 +662,12 @@ pub fn make_component(item: TokenStream) -> TokenStream { output } -fn wrap_in_arcmutex(stream: TokenStream) -> TokenStream { +fn wrap_in_arc_mutex(stream: TokenStream) -> TokenStream { let mut output = TokenStream::new(); output.extend(TokenStream::from(quote!(std::sync::Arc::new))); - let mut mutex_group = Group::new(proc_macro::Delimiter::Parenthesis, stream); + let mutex_group = Group::new(proc_macro::Delimiter::Parenthesis, stream); let mut mutex_stream = TokenStream::new(); @@ -636,20 +682,42 @@ fn wrap_in_arcmutex(stream: TokenStream) -> TokenStream { output } +fn wrap_in_arcmutex_cyclic(stream: TokenStream) -> TokenStream { + let mut output = TokenStream::new(); + + output.extend(TokenStream::from(quote!(std::sync::Arc::new_cyclic))); + + let mutex_group = Group::new(proc_macro::Delimiter::Parenthesis, stream); + + let mut mutex_stream = TokenStream::new(); + + mutex_stream.extend(TokenStream::from(quote!(|cselfref|))); + + mutex_stream.extend(TokenStream::from(quote!(std::sync::Mutex::new))); + + mutex_stream.extend(Some(TokenTree::Group(mutex_group))); + + let arc_group = Group::new(proc_macro::Delimiter::Parenthesis, mutex_stream); + + output.extend(Some(TokenTree::Group(arc_group))); + + output +} + fn wrap_in_arcrwlock(stream: TokenStream) -> TokenStream { let mut output = TokenStream::new(); output.extend(TokenStream::from(quote!(std::sync::Arc::new))); - let mut mutex_group = Group::new(proc_macro::Delimiter::Parenthesis, stream); + let mutex_group = Group::new(proc_macro::Delimiter::Parenthesis, stream); - let mut mutex_stream = TokenStream::new(); + let mut rwlock_stream = TokenStream::new(); - mutex_stream.extend(TokenStream::from(quote!(std::sync::RwLock::new))); + rwlock_stream.extend(TokenStream::from(quote!(std::sync::RwLock::new))); - mutex_stream.extend(Some(TokenTree::Group(mutex_group))); + rwlock_stream.extend(Some(TokenTree::Group(mutex_group))); - let arc_group = Group::new(proc_macro::Delimiter::Parenthesis, mutex_stream); + let arc_group = Group::new(proc_macro::Delimiter::Parenthesis, rwlock_stream); output.extend(Some(TokenTree::Group(arc_group))); @@ -681,47 +749,64 @@ fn parse_components(name: Ident, group: Group, next: usize, parent: Option { - if punct.as_char() == '@' { - let ident = group.next().unwrap(); - let ident = match ident { - TokenTree::Ident(ident) => ident, - _ => panic!("Expected ident after @") - }; + TokenTree::Punct(punct) if punct.as_char() == '@' => { + let ident = group.next().unwrap(); + let ident = match ident { + TokenTree::Ident(ident) => ident, + _ => panic!("Expected ident after @") + }; - let group = group.next().unwrap(); - let group = match group { - TokenTree::Group(group) => group, - _ => panic!("Expected group after ident") - }; + let group = group.next().unwrap(); + let group = match group { + TokenTree::Group(group) => group, + _ => panic!("Expected group after ident") + }; - let components = parse_components(ident, group, next + components_found.len() + 1, Some(next)); - components_found.extend(components); - } else { - self_stream.extend(Some(TokenTree::Punct(punct))); - while let Some(token) = group.next() { - match token { - TokenTree::Punct(punct) => { - if punct.as_char() == ',' { - break; - } else { - self_stream.extend(Some(TokenTree::Punct(punct))); - } - }, - _ => { - self_stream.extend(Some(token)); - } - } - } + let components = parse_components(ident, group, next + components_found.len() + 1, Some(next)); + components_found.extend(components); + }, + TokenTree::Punct(punct) if punct.as_char() == '$' => { + // event handler + let fn_start = group.next().unwrap(); + match fn_start { + TokenTree::Punct(punct) if punct.as_char() == '|' => {}, + _ => panic!("Expected | after $ (event handlers). Move is added automatically") } + let fn_param = group.next().unwrap(); + let fn_param = match fn_param { + TokenTree::Ident(ident) => ident, + _ => panic!("Expected ident after |") + }; + let fn_end = group.next().unwrap(); + match fn_end { + TokenTree::Punct(punct) if punct.as_char() == '|' => {}, + _ => panic!("Expected | after fn param") + } + + let fn_group = group.next().unwrap(); + let fn_group = match fn_group { + TokenTree::Group(group) => group, + _ => panic!("Expected group after |param|") + }; + + let mut callback_stream = TokenStream::from(quote!(move |#)); + callback_stream.extend(Some(TokenTree::Ident(fn_param))); + callback_stream.extend(TokenStream::from(quote!(|))); + callback_stream.extend(Some(TokenTree::Group(fn_group))); + + + let this_component = components_found.get_mut(next).unwrap(); + this_component.event_listeners.push(EventListener { + callback: callback_stream + }); }, any => { // skip until next ',', writing to self_stream diff --git a/rusalka/src/component.rs b/rusalka/src/component.rs index f1b0bf3..d2e06fb 100644 --- a/rusalka/src/component.rs +++ b/rusalka/src/component.rs @@ -1,16 +1,19 @@ use mangui::{SharedNode, nodes::Node}; +use crate::WeakSharedComponent; + /// A rusalka component pub trait Component { type ComponentAttrs: Default; type PartialComponentAttrs: Default + From; const UPDATE_LENGTH : usize = 0; - fn new(attr: Self::ComponentAttrs) -> Self; + fn new(attr: Self::ComponentAttrs, selfref: WeakSharedComponent) -> Self; fn get(&self) -> &Self::ComponentAttrs; fn set(&mut self, attr: Self::PartialComponentAttrs); fn mount(&self, parent: &SharedNode, before: Option<&SharedNode>); fn update(&self, bitmap: &[u32]); fn unmount(&self); + // fn set_selfref(&mut self, selfref: SharedComponent); fn check_update(&self, bitmap: &[u32]) -> () { if bitmap.len() != Self::UPDATE_LENGTH { diff --git a/rusalka/src/invalidator.rs b/rusalka/src/invalidator.rs new file mode 100644 index 0000000..9d86091 --- /dev/null +++ b/rusalka/src/invalidator.rs @@ -0,0 +1,36 @@ +use std::ops::{Deref, DerefMut}; + +pub struct Invalidator { + inner: T, + invalidated: bool +} + +impl Invalidator { + pub fn new(inner: T) -> Self { + Self { + inner, + invalidated: false + } + } + pub fn reset(&mut self) { + self.invalidated = false; + } + pub fn invalidated(&self) -> bool { + self.invalidated + } +} + +impl Deref for Invalidator { + type Target = T; + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl DerefMut for Invalidator { + fn deref_mut(&mut self) -> &mut Self::Target { + self.invalidated = true; + &mut self.inner + } +} + diff --git a/rusalka/src/lib.rs b/rusalka/src/lib.rs index 63cf858..c48ae35 100644 --- a/rusalka/src/lib.rs +++ b/rusalka/src/lib.rs @@ -5,6 +5,8 @@ use mangui::nodes::Node; pub mod component; pub mod nodes; +pub mod invalidator; pub type SharedComponent = Arc>; +pub type WeakSharedComponent = std::sync::Weak>; pub type SharedNodeComponent = Arc>; \ No newline at end of file diff --git a/rusalka/src/main.rs b/rusalka/src/main.rs deleted file mode 100644 index e7a11a9..0000000 --- a/rusalka/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - println!("Hello, world!"); -} diff --git a/rusalka/src/nodes/primitives.rs b/rusalka/src/nodes/primitives.rs index c495524..0e5a8f2 100644 --- a/rusalka/src/nodes/primitives.rs +++ b/rusalka/src/nodes/primitives.rs @@ -4,13 +4,14 @@ use std::sync::{Arc, RwLock}; use mangui::{SharedNode, nodes::{primitives, Style}, taffy::prelude::Size, femtovg::{Paint, Color}}; -use crate::component::Component; +use crate::{component::Component, SharedComponent, WeakSharedComponent}; use super::{insert, detach}; pub struct Rectangle { node: Arc>, - attrs: RectangleAttributes + attrs: RectangleAttributes, + selfref: WeakSharedComponent } #[derive(Default)] @@ -36,7 +37,7 @@ impl Component for Rectangle { type ComponentAttrs = RectangleAttributes; type PartialComponentAttrs = PartialRectangleAttributes; const UPDATE_LENGTH : usize = 1; - fn new(attrs: Self::ComponentAttrs) -> Self { + fn new(attrs: Self::ComponentAttrs, selfref: WeakSharedComponent) -> Self { Self { node: Arc::new(RwLock::new(primitives::Rectangle { style: Style { @@ -53,7 +54,8 @@ impl Component for Rectangle { radius: attrs.radius, ..Default::default() })), - attrs + attrs, + selfref } }