From 60f5697e8e03329856ba8a4726e007b4cf873928 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 19 Jan 2024 15:48:21 -0800 Subject: [PATCH] Fix global signal owner --- examples/all_events.rs | 78 +++++++++++++--------------------- examples/calculator.rs | 37 +++++++--------- examples/compose.rs | 2 +- examples/crm.rs | 38 +++++++++-------- packages/signals/src/global.rs | 22 +++++++--- packages/signals/src/lib.rs | 12 +++++- 6 files changed, 91 insertions(+), 98 deletions(-) diff --git a/examples/all_events.rs b/examples/all_events.rs index b46d2c25e..c11038d60 100644 --- a/examples/all_events.rs +++ b/examples/all_events.rs @@ -1,78 +1,60 @@ +use std::{collections::VecDeque, fmt::Debug, rc::Rc}; + use dioxus::{events::*, html::MouseEvent, prelude::*}; fn main() { launch(app); } -#[derive(Debug)] -enum Event { - MouseMove(MouseEvent), - MouseClick(MouseEvent), - MouseDoubleClick(MouseEvent), - MouseDown(MouseEvent), - MouseUp(MouseEvent), - - Wheel(WheelEvent), - - KeyDown(KeyboardEvent), - KeyUp(KeyboardEvent), - KeyPress(KeyboardEvent), - - FocusIn(FocusEvent), - FocusOut(FocusEvent), -} - const MAX_EVENTS: usize = 8; const CONTAINER_STYLE: &str = r#" - display: flex; - flex-direction: column; - align-items: center; - "#; + display: flex; + flex-direction: column; + align-items: center; +"#; const RECT_STYLE: &str = r#" - background: deepskyblue; - height: 50vh; - width: 50vw; - color: white; - padding: 20px; - margin: 20px; - text-aligh: center; - "#; + background: deepskyblue; + height: 50vh; + width: 50vw; + color: white; + padding: 20px; + margin: 20px; + text-aligh: center; +"#; fn app() -> Element { - let mut events = use_signal(std::collections::VecDeque::new); + let mut events = use_signal(|| std::collections::VecDeque::new() as VecDeque>); - let mut log_event = move |event: Event| { + let mut log_event = move |event: Rc| { let mut events = events.write(); if events.len() >= MAX_EVENTS { events.pop_front(); } + events.push_back(event); }; rsx! { div { style: "{CONTAINER_STYLE}", - div { - style: "{RECT_STYLE}", - // focusing is necessary to catch keyboard events - tabindex: "0", + // focusing is necessary to catch keyboard events + div { style: "{RECT_STYLE}", tabindex: "0", + onmousemove: move |event| log_event(event.inner().clone()), + onclick: move |event| log_event(event.inner().clone()), + ondoubleclick: move |event| log_event(event.inner().clone()), + onmousedown: move |event| log_event(event.inner().clone()), + onmouseup: move |event| log_event(event.inner().clone()), - onmousemove: move |event| log_event(Event::MouseMove(event)), - onclick: move |event| log_event(Event::MouseClick(event)), - ondoubleclick: move |event| log_event(Event::MouseDoubleClick(event)), - onmousedown: move |event| log_event(Event::MouseDown(event)), - onmouseup: move |event| log_event(Event::MouseUp(event)), + onwheel: move |event| log_event(event.inner().clone()), - onwheel: move |event| log_event(Event::Wheel(event)), + onkeydown: move |event| log_event(event.inner().clone()), + onkeyup: move |event| log_event(event.inner().clone()), + onkeypress: move |event| log_event(event.inner().clone()), - onkeydown: move |event| log_event(Event::KeyDown(event)), - onkeyup: move |event| log_event(Event::KeyUp(event)), - onkeypress: move |event| log_event(Event::KeyPress(event)), - - onfocusin: move |event| log_event(Event::FocusIn(event)), - onfocusout: move |event| log_event(Event::FocusOut(event)), + onfocusin: move |event| log_event(event.inner().clone()), + onfocusout: move |event| log_event(event.inner().clone()), "Hover, click, type or scroll to see the info down below" } diff --git a/examples/calculator.rs b/examples/calculator.rs index 07aacfc07..3412be6c0 100644 --- a/examples/calculator.rs +++ b/examples/calculator.rs @@ -9,24 +9,26 @@ use dioxus::prelude::*; use dioxus_desktop::{Config, LogicalSize, WindowBuilder}; fn main() { - let config = Config::new().with_window( - WindowBuilder::default() - .with_title("Calculator") - .with_inner_size(LogicalSize::new(300.0, 525.0)), - ); - - LaunchBuilder::desktop().with_cfg(config).launch(app); + LaunchBuilder::desktop() + .with_cfg( + Config::new().with_window( + WindowBuilder::default() + .with_title("Calculator") + .with_inner_size(LogicalSize::new(300.0, 525.0)), + ), + ) + .launch(app); } fn app() -> Element { let mut val = use_signal(|| String::from("0")); - let mut input_digit = move |num: u8| { - if val.cloned() == "0" { + let mut input_digit = move |num: String| { + if val() == "0" { val.set(String::new()); } - val.write().push_str(num.to_string().as_str()); + val.write().push_str(num.as_str()); }; let mut input_operator = move |key: &str| val.write().push_str(key); @@ -42,16 +44,7 @@ fn app() -> Element { "-" => input_operator("-"), "/" => input_operator("/"), "*" => input_operator("*"), - "0" => input_digit(0), - "1" => input_digit(1), - "2" => input_digit(2), - "3" => input_digit(3), - "4" => input_digit(4), - "5" => input_digit(5), - "6" => input_digit(6), - "7" => input_digit(7), - "8" => input_digit(8), - "9" => input_digit(9), + "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" => input_digit(character), _ => {} }, _ => {} @@ -99,7 +92,7 @@ fn app() -> Element { div { class: "digit-keys", button { class: "calculator-key key-0", - onclick: move |_| input_digit(0), + onclick: move |_| input_digit(0.to_string()), "0" } button { @@ -111,7 +104,7 @@ fn app() -> Element { button { class: "calculator-key {k}", name: "key-{k}", - onclick: move |_| input_digit(k), + onclick: move |_| input_digit(k.to_string()), "{k}" } } diff --git a/examples/compose.rs b/examples/compose.rs index 30e9e19aa..208dd7d18 100644 --- a/examples/compose.rs +++ b/examples/compose.rs @@ -19,7 +19,7 @@ fn app() -> Element { } }); - let open_compose_window = move |evt: MouseEvent| { + let open_compose_window = move |_evt: MouseEvent| { let tx = handle.tx(); dioxus_desktop::window().new_window( VirtualDom::new_with_props(compose, Rc::new(move |s| tx.unbounded_send(s).unwrap())), diff --git a/examples/crm.rs b/examples/crm.rs index c09e51294..e64599d51 100644 --- a/examples/crm.rs +++ b/examples/crm.rs @@ -3,17 +3,13 @@ use dioxus::prelude::*; use dioxus_router::prelude::*; fn main() { - launch_desktop(app); + launch(app); } -/// A type alias that reprsents a shared context between components -/// -/// Normally we'd wrap the Context in a newtype, but we only have one Signal> in this app -type Clients = Signal>; +/// We only have one list of clients for the whole app, so we can use a global signal. +static CLIENTS: GlobalSignal> = Signal::global(|| Vec::new()); fn app() -> Element { - use_context_provider::(|| Signal::new(vec![])); - rsx! { link { rel: "stylesheet", @@ -37,7 +33,6 @@ fn app() -> Element { } #[derive(Routable, Clone)] -#[rustfmt::skip] enum Route { #[route("/")] ClientList {}, @@ -58,7 +53,9 @@ pub struct Client { #[component] fn ClientList() -> Element { - let mut clients = use_context::(); + let clients = use_hook(|| CLIENTS.signal()); + + println!("Clients: {:?}", clients.read()); rsx! { h2 { "List of Clients" } @@ -80,21 +77,25 @@ fn ClientAdd() -> Element { let mut description = use_signal(String::new); let submit_client = move |_: FormEvent| { - consume_context::().write().push(Client { - first_name: first_name.to_string(), - last_name: last_name.to_string(), - description: description.to_string(), + // Write the client + CLIENTS.write().push(Client { + first_name: first_name(), + last_name: last_name(), + description: description(), }); + + println!("Added client: {:?}", CLIENTS.read()); + + // And then navigate back to the client list dioxus_router::router().push(Route::ClientList {}); }; rsx! { h2 { "Add new Client" } form { class: "pure-form pure-form-aligned", onsubmit: submit_client, - fieldset { div { class: "pure-control-group", - label { "for": "first_name", "First Name" } + label { r#for: "first_name", "First Name" } input { id: "first_name", r#type: "text", @@ -140,13 +141,14 @@ fn ClientAdd() -> Element { fn Settings() -> Element { rsx! { h2 { "Settings" } - button { class: "pure-button pure-button-primary red", - onclick: move |_| consume_context::().write().clear(), + onclick: move |_| { + CLIENTS.write().clear(); + dioxus_router::router().push(Route::ClientList {}); + }, "Remove all Clients" } - Link { to: Route::ClientList {}, class: "pure-button", "Go back" } } } diff --git a/packages/signals/src/global.rs b/packages/signals/src/global.rs index ea9ddf364..f151c2388 100644 --- a/packages/signals/src/global.rs +++ b/packages/signals/src/global.rs @@ -8,7 +8,7 @@ use std::{ }; use dioxus_core::{ - prelude::{provide_context, try_consume_context, IntoAttributeValue}, + prelude::{provide_context, provide_root_context, try_consume_context, IntoAttributeValue}, ScopeId, }; use generational_box::{GenerationalRef, GenerationalRefMut}; @@ -32,7 +32,7 @@ fn get_global_context() -> GlobalSignalContext { let context = GlobalSignalContext { signal: Rc::new(RefCell::new(HashMap::new())), }; - provide_context(context) + provide_root_context(context).unwrap() } } } @@ -46,19 +46,27 @@ impl GlobalSignal { /// Get the signal that backs this global. pub fn signal(&self) -> Signal { let key = self as *const _ as *const (); - let context = get_global_context(); - let read = context.signal.borrow(); match read.get(&key) { - Some(signal) => *signal.downcast_ref::>().unwrap(), + Some(signal) => { + let signal = signal.downcast_ref::>().unwrap(); + dbg!(signal.id()); + + *signal + } None => { drop(read); + // Constructors are always run in the root scope + // The signal also exists in the root scope let value = ScopeId::ROOT.in_runtime(self.initializer); - let signal = Signal::new(value); - context.signal.borrow_mut().insert(key, Box::new(signal)); + let signal = Signal::new_in_scope(value, ScopeId::ROOT); + + let entry = context.signal.borrow_mut().insert(key, Box::new(signal)); + debug_assert!(entry.is_none(), "Global signal already exists"); + signal } } diff --git a/packages/signals/src/lib.rs b/packages/signals/src/lib.rs index 84281ebf5..c1117df06 100644 --- a/packages/signals/src/lib.rs +++ b/packages/signals/src/lib.rs @@ -6,19 +6,27 @@ mod rt; pub use rt::*; + mod effect; pub use effect::*; -mod impls; + mod selector; pub use selector::*; + pub(crate) mod signal; pub use signal::*; + mod dependency; pub use dependency::*; + mod map; -pub use generational_box::{Storage, SyncStorage, UnsyncStorage}; pub use map::*; + mod comparer; pub use comparer::*; + mod global; pub use global::*; + +mod impls; +pub use generational_box::{Storage, SyncStorage, UnsyncStorage};