Fix global signal owner

This commit is contained in:
Jonathan Kelley committed 2024-01-19 15:48:21 -08:00
1 parent 673caec79f
commit 60f5697e8e
6 files changed
+91 -98

No files matched your search

+30 -48
View File
@@ -1,78 +1,60 @@
use std::{collections::VecDeque, fmt::Debug, rc::Rc};
use dioxus::{events::*, html::MouseEvent, prelude::*}; use dioxus::{events::*, html::MouseEvent, prelude::*};
fn main() { fn main() {
launch(app); 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 MAX_EVENTS: usize = 8;
const CONTAINER_STYLE: &str = r#" const CONTAINER_STYLE: &str = r#"
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
"#; "#;
const RECT_STYLE: &str = r#" const RECT_STYLE: &str = r#"
background: deepskyblue; background: deepskyblue;
height: 50vh; height: 50vh;
width: 50vw; width: 50vw;
color: white; color: white;
padding: 20px; padding: 20px;
margin: 20px; margin: 20px;
text-aligh: center; text-aligh: center;
"#; "#;
fn app() -> Element { fn app() -> Element {
let mut events = use_signal(std::collections::VecDeque::new); let mut events = use_signal(|| std::collections::VecDeque::new() as VecDeque<Rc<dyn Debug>>);
let mut log_event = move |event: Event| { let mut log_event = move |event: Rc<dyn Debug>| {
let mut events = events.write(); let mut events = events.write();
if events.len() >= MAX_EVENTS { if events.len() >= MAX_EVENTS {
events.pop_front(); events.pop_front();
} }
events.push_back(event); events.push_back(event);
}; };
rsx! { rsx! {
div { style: "{CONTAINER_STYLE}", div { style: "{CONTAINER_STYLE}",
div { // focusing is necessary to catch keyboard events
style: "{RECT_STYLE}", div { style: "{RECT_STYLE}", tabindex: "0",
// focusing is necessary to catch keyboard events onmousemove: move |event| log_event(event.inner().clone()),
tabindex: "0", 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)), onwheel: move |event| log_event(event.inner().clone()),
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::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)), onfocusin: move |event| log_event(event.inner().clone()),
onkeyup: move |event| log_event(Event::KeyUp(event)), onfocusout: move |event| log_event(event.inner().clone()),
onkeypress: move |event| log_event(Event::KeyPress(event)),
onfocusin: move |event| log_event(Event::FocusIn(event)),
onfocusout: move |event| log_event(Event::FocusOut(event)),
"Hover, click, type or scroll to see the info down below" "Hover, click, type or scroll to see the info down below"
} }
+15 -22
View File
@@ -9,24 +9,26 @@ use dioxus::prelude::*;
use dioxus_desktop::{Config, LogicalSize, WindowBuilder}; use dioxus_desktop::{Config, LogicalSize, WindowBuilder};
fn main() { fn main() {
let config = Config::new().with_window( LaunchBuilder::desktop()
WindowBuilder::default() .with_cfg(
.with_title("Calculator") Config::new().with_window(
.with_inner_size(LogicalSize::new(300.0, 525.0)), WindowBuilder::default()
); .with_title("Calculator")
.with_inner_size(LogicalSize::new(300.0, 525.0)),
LaunchBuilder::desktop().with_cfg(config).launch(app); ),
)
.launch(app);
} }
fn app() -> Element { fn app() -> Element {
let mut val = use_signal(|| String::from("0")); let mut val = use_signal(|| String::from("0"));
let mut input_digit = move |num: u8| { let mut input_digit = move |num: String| {
if val.cloned() == "0" { if val() == "0" {
val.set(String::new()); 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); 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("/"), "/" => input_operator("/"),
"*" => input_operator("*"), "*" => input_operator("*"),
"0" => input_digit(0), "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" => input_digit(character),
"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),
_ => {} _ => {}
}, },
_ => {} _ => {}
@@ -99,7 +92,7 @@ fn app() -> Element {
div { class: "digit-keys", div { class: "digit-keys",
button { button {
class: "calculator-key key-0", class: "calculator-key key-0",
onclick: move |_| input_digit(0), onclick: move |_| input_digit(0.to_string()),
"0" "0"
} }
button { button {
@@ -111,7 +104,7 @@ fn app() -> Element {
button { button {
class: "calculator-key {k}", class: "calculator-key {k}",
name: "key-{k}", name: "key-{k}",
onclick: move |_| input_digit(k), onclick: move |_| input_digit(k.to_string()),
"{k}" "{k}"
} }
} }
+1 -1
View File
@@ -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(); let tx = handle.tx();
dioxus_desktop::window().new_window( dioxus_desktop::window().new_window(
VirtualDom::new_with_props(compose, Rc::new(move |s| tx.unbounded_send(s).unwrap())), VirtualDom::new_with_props(compose, Rc::new(move |s| tx.unbounded_send(s).unwrap())),
+20 -18
View File
@@ -3,17 +3,13 @@ use dioxus::prelude::*;
use dioxus_router::prelude::*; use dioxus_router::prelude::*;
fn main() { fn main() {
launch_desktop(app); launch(app);
} }
/// A type alias that reprsents a shared context between components /// We only have one list of clients for the whole app, so we can use a global signal.
/// static CLIENTS: GlobalSignal<Vec<Client>> = Signal::global(|| Vec::new());
/// Normally we'd wrap the Context in a newtype, but we only have one Signal<Vec<Client>> in this app
type Clients = Signal<Vec<Client>>;
fn app() -> Element { fn app() -> Element {
use_context_provider::<Clients>(|| Signal::new(vec![]));
rsx! { rsx! {
link { link {
rel: "stylesheet", rel: "stylesheet",
@@ -37,7 +33,6 @@ fn app() -> Element {
} }
#[derive(Routable, Clone)] #[derive(Routable, Clone)]
#[rustfmt::skip]
enum Route { enum Route {
#[route("/")] #[route("/")]
ClientList {}, ClientList {},
@@ -58,7 +53,9 @@ pub struct Client {
#[component] #[component]
fn ClientList() -> Element { fn ClientList() -> Element {
let mut clients = use_context::<Clients>(); let clients = use_hook(|| CLIENTS.signal());
println!("Clients: {:?}", clients.read());
rsx! { rsx! {
h2 { "List of Clients" } h2 { "List of Clients" }
@@ -80,21 +77,25 @@ fn ClientAdd() -> Element {
let mut description = use_signal(String::new); let mut description = use_signal(String::new);
let submit_client = move |_: FormEvent| { let submit_client = move |_: FormEvent| {
consume_context::<Clients>().write().push(Client { // Write the client
first_name: first_name.to_string(), CLIENTS.write().push(Client {
last_name: last_name.to_string(), first_name: first_name(),
description: description.to_string(), 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 {}); dioxus_router::router().push(Route::ClientList {});
}; };
rsx! { rsx! {
h2 { "Add new Client" } h2 { "Add new Client" }
form { class: "pure-form pure-form-aligned", onsubmit: submit_client, form { class: "pure-form pure-form-aligned", onsubmit: submit_client,
fieldset { fieldset {
div { class: "pure-control-group", div { class: "pure-control-group",
label { "for": "first_name", "First Name" } label { r#for: "first_name", "First Name" }
input { input {
id: "first_name", id: "first_name",
r#type: "text", r#type: "text",
@@ -140,13 +141,14 @@ fn ClientAdd() -> Element {
fn Settings() -> Element { fn Settings() -> Element {
rsx! { rsx! {
h2 { "Settings" } h2 { "Settings" }
button { button {
class: "pure-button pure-button-primary red", class: "pure-button pure-button-primary red",
onclick: move |_| consume_context::<Clients>().write().clear(), onclick: move |_| {
CLIENTS.write().clear();
dioxus_router::router().push(Route::ClientList {});
},
"Remove all Clients" "Remove all Clients"
} }
Link { to: Route::ClientList {}, class: "pure-button", "Go back" } Link { to: Route::ClientList {}, class: "pure-button", "Go back" }
} }
} }
+15 -7
View File
@@ -8,7 +8,7 @@ use std::{
}; };
use dioxus_core::{ use dioxus_core::{
prelude::{provide_context, try_consume_context, IntoAttributeValue}, prelude::{provide_context, provide_root_context, try_consume_context, IntoAttributeValue},
ScopeId, ScopeId,
}; };
use generational_box::{GenerationalRef, GenerationalRefMut}; use generational_box::{GenerationalRef, GenerationalRefMut};
@@ -32,7 +32,7 @@ fn get_global_context() -> GlobalSignalContext {
let context = GlobalSignalContext { let context = GlobalSignalContext {
signal: Rc::new(RefCell::new(HashMap::new())), signal: Rc::new(RefCell::new(HashMap::new())),
}; };
provide_context(context) provide_root_context(context).unwrap()
} }
} }
} }
@@ -46,19 +46,27 @@ impl<T: 'static> GlobalSignal<T> {
/// Get the signal that backs this global. /// Get the signal that backs this global.
pub fn signal(&self) -> Signal<T> { pub fn signal(&self) -> Signal<T> {
let key = self as *const _ as *const (); let key = self as *const _ as *const ();
let context = get_global_context(); let context = get_global_context();
let read = context.signal.borrow(); let read = context.signal.borrow();
match read.get(&key) { match read.get(&key) {
Some(signal) => *signal.downcast_ref::<Signal<T>>().unwrap(), Some(signal) => {
let signal = signal.downcast_ref::<Signal<T>>().unwrap();
dbg!(signal.id());
*signal
}
None => { None => {
drop(read); drop(read);
// Constructors are always run in the root scope // 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 value = ScopeId::ROOT.in_runtime(self.initializer);
let signal = Signal::new(value); let signal = Signal::new_in_scope(value, ScopeId::ROOT);
context.signal.borrow_mut().insert(key, Box::new(signal));
let entry = context.signal.borrow_mut().insert(key, Box::new(signal));
debug_assert!(entry.is_none(), "Global signal already exists");
signal signal
} }
} }
+10 -2
View File
@@ -6,19 +6,27 @@
mod rt; mod rt;
pub use rt::*; pub use rt::*;
mod effect; mod effect;
pub use effect::*; pub use effect::*;
mod impls;
mod selector; mod selector;
pub use selector::*; pub use selector::*;
pub(crate) mod signal; pub(crate) mod signal;
pub use signal::*; pub use signal::*;
mod dependency; mod dependency;
pub use dependency::*; pub use dependency::*;
mod map; mod map;
pub use generational_box::{Storage, SyncStorage, UnsyncStorage};
pub use map::*; pub use map::*;
mod comparer; mod comparer;
pub use comparer::*; pub use comparer::*;
mod global; mod global;
pub use global::*; pub use global::*;
mod impls;
pub use generational_box::{Storage, SyncStorage, UnsyncStorage};