Fix global signal owner
This commit is contained in:
6 files changed
+91
-98
No files matched your search
+30
-48
@@ -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
@@ -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
@@ -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
@@ -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" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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};
|
||||||
Reference in new issue
Block a user