mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-10 06:34:20 +00:00
Fix global signal owner
This commit is contained in:
parent
673caec79f
commit
60f5697e8e
6 changed files with 91 additions and 98 deletions
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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}"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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())),
|
||||
|
|
|
@ -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" }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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};
|
||||
|
|
Loading…
Reference in a new issue