Fix global signal owner

This commit is contained in:
Jonathan Kelley 2024-01-19 15:48:21 -08:00
parent 673caec79f
commit 60f5697e8e
No known key found for this signature in database
GPG key ID: 1FBB50F7EB0A08BE
6 changed files with 91 additions and 98 deletions

View file

@ -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<Rc<dyn Debug>>);
let mut log_event = move |event: Event| {
let mut log_event = move |event: Rc<dyn Debug>| {
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"
}

View file

@ -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}"
}
}

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();
dioxus_desktop::window().new_window(
VirtualDom::new_with_props(compose, Rc::new(move |s| tx.unbounded_send(s).unwrap())),

View file

@ -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<Vec<Client>> in this app
type Clients = Signal<Vec<Client>>;
/// 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());
fn app() -> Element {
use_context_provider::<Clients>(|| 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::<Clients>();
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::<Clients>().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::<Clients>().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" }
}
}

View file

@ -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<T: 'static> GlobalSignal<T> {
/// Get the signal that backs this global.
pub fn signal(&self) -> Signal<T> {
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::<Signal<T>>().unwrap(),
Some(signal) => {
let signal = signal.downcast_ref::<Signal<T>>().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
}
}

View file

@ -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};