Experiment with call syntax only working for copy types

This commit is contained in:
Jonathan Kelley committed 2024-01-15 21:12:44 -08:00
1 parent bb750f5336
commit 33bba24867
19 files changed
+52 -76

No files matched your search

+2 -2
View File
@@ -22,7 +22,7 @@ fn app() -> Element {
let val = use_signal(|| String::from("0")); let val = use_signal(|| String::from("0"));
let input_digit = move |num: u8| { let input_digit = move |num: u8| {
if *val() == "0" { if val.cloned() == "0" {
val.set(String::new()); val.set(String::new());
} }
@@ -94,7 +94,7 @@ fn app() -> Element {
class: "calculator-key key-percent", class: "calculator-key key-percent",
onclick: move |_| { onclick: move |_| {
val.set( val.set(
format!("{}", calc_val(val().as_str()) / 100.0) format!("{}", calc_val(val.cloned().as_str()) / 100.0)
); );
}, },
"%" "%"
+2 -2
View File
@@ -22,7 +22,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))), VirtualDom::new_with_props(compose, Rc::new(move |s| tx.unbounded_send(s).unwrap())),
Default::default(), Default::default(),
); );
}; };
@@ -50,7 +50,7 @@ fn compose(send: Rc<dyn Fn(String)>) -> Element {
button { button {
onclick: move |_| { onclick: move |_| {
send(user_input.get().clone()); send(user_input.cloned());
dioxus_desktop::window().close(); dioxus_desktop::window().close();
}, },
"Click to send" "Click to send"
+1 -1
View File
@@ -12,7 +12,7 @@ fn app() -> Element {
use_future(move || async move { use_future(move || async move {
let mut focused = 0; let mut focused = 0;
if *running() { if running() {
loop { loop {
tokio::time::sleep(std::time::Duration::from_millis(10)).await; tokio::time::sleep(std::time::Duration::from_millis(10)).await;
if let Some(element) = elements.with(|f| f.get(focused).cloned()) { if let Some(element) = elements.with(|f| f.get(focused).cloned()) {
+1 -1
View File
@@ -11,7 +11,7 @@ fn app() -> Element {
div { div {
button { onclick: move |_| disabled.toggle(), button { onclick: move |_| disabled.toggle(),
"click to " "click to "
if *disabled() { "enable" } else { "disable" } if disabled() { "enable" } else { "disable" }
" the lower button" " the lower button"
} }
+3 -1
View File
@@ -33,7 +33,9 @@ fn app() -> Element {
Some(resp) => rsx! { Some(resp) => rsx! {
h1 { "Select a dog breed!" } h1 { "Select a dog breed!" }
div { height: "500px", display: "flex", div { height: "500px", display: "flex",
ul { flex: "50%", {breed_list} } ul { flex: "50%", {breed_list.value().cloned().unwrap_or_else(|| rsx! {
"loading breeds..."
})} }
div { flex: "50%", BreedPic { breed } } div { flex: "50%", BreedPic { breed } }
} }
}, },
-28
View File
@@ -1,28 +0,0 @@
//! This example shows that you can place heavy work on the main thread, and then
//!
//! You *should* be using `tokio::spawn_blocking` instead.
//!
//! Your app runs in an async runtime (Tokio), so you should avoid blocking
//! the rendering of the VirtualDom.
//!
//!
use dioxus::prelude::*;
fn main() {
dioxus_desktop::launch(app);
}
fn app() -> Element {
// This is discouraged
std::thread::sleep(std::time::Duration::from_millis(2_000));
// This is suggested
tokio::task::spawn_blocking(move || {
std::thread::sleep(std::time::Duration::from_millis(2_000));
});
rsx! {
div { "Hello, world!" }
}
}
+1 -1
View File
@@ -31,7 +31,7 @@ fn app() -> Element {
rsx! { rsx! {
h1 { "Login" } h1 { "Login" }
form { onsubmit: onsubmit, form { onsubmit,
input { r#type: "text", id: "username", name: "username" } input { r#type: "text", id: "username", name: "username" }
label { "Username" } label { "Username" }
br {} br {}
+1 -1
View File
@@ -45,7 +45,7 @@ fn app() -> Element {
height: "50%", height: "50%",
background_color: "red", background_color: "red",
onmounted: move |cx| div_element.set(Some(cx.inner().clone())), onmounted: move |cx| div_element.set(Some(cx.inner().clone())),
"This element is {*dimensions():?}" "This element is {dimensions():?}"
} }
button { button {
+1 -1
View File
@@ -20,7 +20,7 @@ fn app() -> Element {
button { button {
onclick: move |_| async move { onclick: move |_| async move {
if let Some(header) = header_element.read().as_ref().cloned() { if let Some(header) = header_element.cloned() {
let _ = header.scroll_to(ScrollBehavior::Smooth).await; let _ = header.scroll_to(ScrollBehavior::Smooth).await;
} }
}, },
+3 -3
View File
@@ -14,7 +14,7 @@ fn app() -> Element {
// Signals are backed by a runtime that is designed to deeply integrate with Dioxus apps // Signals are backed by a runtime that is designed to deeply integrate with Dioxus apps
use_future(|| async move { use_future(|| async move {
loop { loop {
if running.value() { if running.cloned() {
count += 1; count += 1;
} }
tokio::time::sleep(Duration::from_millis(400)).await; tokio::time::sleep(Duration::from_millis(400)).await;
@@ -26,11 +26,11 @@ fn app() -> Element {
button { onclick: move |_| count += 1, "Up high!" } button { onclick: move |_| count += 1, "Up high!" }
button { onclick: move |_| count -= 1, "Down low!" } button { onclick: move |_| count -= 1, "Down low!" }
button { onclick: move |_| running.toggle(), "Toggle counter" } button { onclick: move |_| running.toggle(), "Toggle counter" }
button { onclick: move |_| saved_values.push(count.value().to_string()), "Save this value" } button { onclick: move |_| saved_values.push(count.cloned().to_string()), "Save this value" }
button { onclick: move |_| saved_values.write().clear(), "Clear saved values" } button { onclick: move |_| saved_values.write().clear(), "Clear saved values" }
// We can do boolean operations on the current signal value // We can do boolean operations on the current signal value
if count.value() > 5 { if count() > 5 {
h2 { "High five!" } h2 { "High five!" }
} }
+2 -3
View File
@@ -1,8 +1,7 @@
use dioxus::{dioxus_core::NoOpMutations, prelude::*}; use dioxus::prelude::*;
fn main() { fn main() {
let mut dom = VirtualDom::new(app); let mut dom = VirtualDom::prebuilt(app);
let _ = dom.rebuild(&mut NoOpMutations);
let html = dioxus_ssr::render(&dom); let html = dioxus_ssr::render(&dom);
println!("{}", html); println!("{}", html);
+2 -3
View File
@@ -2,12 +2,11 @@
//! //!
//! This example shows how we can render the Dioxus Virtualdom using SSR. //! This example shows how we can render the Dioxus Virtualdom using SSR.
use dioxus::{core::NoOpMutations, prelude::*}; use dioxus::prelude::*;
fn main() { fn main() {
// We can render VirtualDoms // We can render VirtualDoms
let mut vdom = VirtualDom::new(app); let mut vdom = VirtualDom::prebuilt(app);
let _ = vdom.rebuild(&mut NoOpMutations);
println!("{}", dioxus_ssr::render(&vdom)); println!("{}", dioxus_ssr::render(&vdom));
// Or we can render rsx! calls themselves // Or we can render rsx! calls themselves
+1 -1
View File
@@ -31,7 +31,7 @@ fn Dice() -> Element {
]; ];
let value = use_signal(|| 5); let value = use_signal(|| 5);
let active_dots = use_selector(move || &DOTS_FOR_VALUE[(*value() - 1) as usize]); let active_dots = use_selector(move || &DOTS_FOR_VALUE[(value() - 1) as usize]);
rsx! { rsx! {
svg { svg {
+14 -13
View File
@@ -26,12 +26,13 @@ pub fn app() -> Element {
let filter = use_signal(|| FilterState::All); let filter = use_signal(|| FilterState::All);
let active_todo_count = let active_todo_count =
use_selector(move || todos().values().filter(|item| !item.checked).count()); use_selector(move || todos.read().values().filter(|item| !item.checked).count());
let filtered_todos = use_selector(move || { let filtered_todos = use_selector(move || {
let mut filtered_todos = todos() let mut filtered_todos = todos
.read()
.iter() .iter()
.filter(|(_, item)| match *filter() { .filter(|(_, item)| match filter() {
FilterState::All => true, FilterState::All => true,
FilterState::Active => !item.checked, FilterState::Active => !item.checked,
FilterState::Completed => item.checked, FilterState::Completed => item.checked,
@@ -49,7 +50,7 @@ pub fn app() -> Element {
style { {include_str!("./assets/todomvc.css")} } style { {include_str!("./assets/todomvc.css")} }
TodoHeader { todos } TodoHeader { todos }
section { class: "main", section { class: "main",
if !todos().is_empty() { if !todos.read().is_empty() {
input { input {
id: "toggle-all", id: "toggle-all",
class: "toggle-all", class: "toggle-all",
@@ -69,7 +70,7 @@ pub fn app() -> Element {
TodoEntry { key: "{id}", id, todos } TodoEntry { key: "{id}", id, todos }
} }
} }
if !todos().is_empty() { if !todos.read().is_empty() {
ListFooter { active_todo_count, todos, filter } ListFooter { active_todo_count, todos, filter }
} }
} }
@@ -84,8 +85,8 @@ pub fn TodoHeader(todos: Signal<HashMap<u32, TodoItem>>) -> Element {
let mut todo_id = use_signal(|| 0); let mut todo_id = use_signal(|| 0);
let onkeydown = move |evt: KeyboardEvent| { let onkeydown = move |evt: KeyboardEvent| {
if evt.key() == Key::Enter && !draft().is_empty() { if evt.key() == Key::Enter && !draft.read().is_empty() {
let id = *todo_id(); let id = todo_id();
let todo = TodoItem { let todo = TodoItem {
id, id,
checked: false, checked: false,
@@ -115,11 +116,11 @@ pub fn TodoHeader(todos: Signal<HashMap<u32, TodoItem>>) -> Element {
#[component] #[component]
pub fn TodoEntry(todos: Signal<HashMap<u32, TodoItem>>, id: u32) -> Element { pub fn TodoEntry(todos: Signal<HashMap<u32, TodoItem>>, id: u32) -> Element {
let is_editing = use_signal(|| false); let is_editing = use_signal(|| false);
let checked = use_selector(move || todos().get(&id).unwrap().checked); let checked = use_selector(move || todos.read().get(&id).unwrap().checked);
let contents = use_selector(move || todos().get(&id).unwrap().contents.clone()); let contents = use_selector(move || todos.read().get(&id).unwrap().contents.clone());
rsx! { rsx! {
li { class: if *checked() { "completed" }, class: if *is_editing() { "editing"}, li { class: if checked() { "completed" }, class: if is_editing() { "editing"},
div { class: "view", div { class: "view",
input { input {
class: "toggle", class: "toggle",
@@ -140,7 +141,7 @@ pub fn TodoEntry(todos: Signal<HashMap<u32, TodoItem>>, id: u32) -> Element {
prevent_default: "onclick" prevent_default: "onclick"
} }
} }
if *is_editing() { if is_editing() {
input { input {
class: "edit", class: "edit",
value: "{contents}", value: "{contents}",
@@ -165,7 +166,7 @@ pub fn ListFooter(
active_todo_count: ReadOnlySignal<usize>, active_todo_count: ReadOnlySignal<usize>,
filter: Signal<FilterState>, filter: Signal<FilterState>,
) -> Element { ) -> Element {
let show_clear_completed = use_selector(move || todos().values().any(|todo| todo.checked)); let show_clear_completed = use_selector(move || todos.read().values().any(|todo| todo.checked));
rsx! { rsx! {
footer { class: "footer", footer { class: "footer",
@@ -188,7 +189,7 @@ pub fn ListFooter(
li { li {
a { a {
href: url, href: url,
class: if *filter() == state { "selected" }, class: if filter() == state { "selected" },
onclick: move |_| filter.set(state), onclick: move |_| filter.set(state),
prevent_default: "onclick", prevent_default: "onclick",
{state_text} {state_text}
+4 -4
View File
@@ -34,8 +34,8 @@ fn app() -> Element {
class: "inline-flex items-center bg-gray-800 border-0 py-1 px-3 focus:outline-none hover:bg-gray-700 rounded text-base mt-4 md:mt-0", class: "inline-flex items-center bg-gray-800 border-0 py-1 px-3 focus:outline-none hover:bg-gray-700 rounded text-base mt-4 md:mt-0",
onmousedown: |evt| evt.stop_propagation(), onmousedown: |evt| evt.stop_propagation(),
onclick: move |_| { onclick: move |_| {
window().set_fullscreen(!*fullscreen()); window().set_fullscreen(!fullscreen());
window().set_resizable(*fullscreen()); window().set_resizable(fullscreen());
fullscreen.toggle(); fullscreen.toggle();
}, },
"Fullscreen" "Fullscreen"
@@ -58,7 +58,7 @@ fn app() -> Element {
class: "inline-flex items-center text-white bg-green-500 border-0 py-1 px-3 hover:bg-green-700 rounded", class: "inline-flex items-center text-white bg-green-500 border-0 py-1 px-3 hover:bg-green-700 rounded",
onmousedown: |evt| evt.stop_propagation(), onmousedown: |evt| evt.stop_propagation(),
onclick: move |_| { onclick: move |_| {
window().set_always_on_top(!*always_on_top()); window().set_always_on_top(!always_on_top());
always_on_top.toggle(); always_on_top.toggle();
}, },
"Always On Top" "Always On Top"
@@ -69,7 +69,7 @@ fn app() -> Element {
class: "inline-flex items-center text-white bg-blue-500 border-0 py-1 px-3 hover:bg-green-700 rounded", class: "inline-flex items-center text-white bg-blue-500 border-0 py-1 px-3 hover:bg-green-700 rounded",
onmousedown: |evt| evt.stop_propagation(), onmousedown: |evt| evt.stop_propagation(),
onclick: move |_| { onclick: move |_| {
window().set_decorations(!*decorations()); window().set_decorations(!decorations());
decorations.toggle(); decorations.toggle();
}, },
"Set Decorations" "Set Decorations"
+1 -1
View File
@@ -28,7 +28,7 @@ fn app() -> Element {
display: "flex", display: "flex",
flex_direction: "column", flex_direction: "column",
align_items: "center", align_items: "center",
if *focused() { if focused() {
"This window is focused!" "This window is focused!"
} else { } else {
"This window is not focused!" "This window is not focused!"
+1 -1
View File
@@ -19,7 +19,7 @@ fn app() -> Element {
let local_state = use_signal(|| 0); let local_state = use_signal(|| 0);
let computed = use_selector_with_dependencies((local_state.get(),), move |(local_state,)| { let computed = use_selector_with_dependencies((local_state.get(),), move |(local_state,)| {
local_state * 2 + signal.value() local_state * 2 + signal.cloned()
}); });
println!("Running app"); println!("Running app");
+11 -8
View File
@@ -330,7 +330,7 @@ impl<T: Clone + 'static> Signal<T> {
/// Get the current value of the signal. This will subscribe the current scope to the signal. /// Get the current value of the signal. This will subscribe the current scope to the signal.
/// If the signal has been dropped, this will panic. /// If the signal has been dropped, this will panic.
#[track_caller] #[track_caller]
pub fn value(&self) -> T { pub fn cloned(&self) -> T {
self.read().clone() self.read().clone()
} }
} }
@@ -338,7 +338,7 @@ impl<T: Clone + 'static> Signal<T> {
impl Signal<bool> { impl Signal<bool> {
/// Invert the boolean value of the signal. This will trigger an update on all subscribers. /// Invert the boolean value of the signal. This will trigger an update on all subscribers.
pub fn toggle(&self) { pub fn toggle(&self) {
self.set(!self.value()); self.set(!self.cloned());
} }
} }
@@ -348,8 +348,8 @@ impl<T: 'static> PartialEq for Signal<T> {
} }
} }
impl<T> Deref for Signal<T> { impl<T: Copy> Deref for Signal<T> {
type Target = dyn Fn() -> GenerationalRef<T>; type Target = dyn Fn() -> T;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
// https://github.com/dtolnay/case-studies/tree/master/callable-types // https://github.com/dtolnay/case-studies/tree/master/callable-types
@@ -357,7 +357,10 @@ impl<T> Deref for Signal<T> {
// First we create a closure that captures something with the Same in memory layout as Self (MaybeUninit<Self>). // First we create a closure that captures something with the Same in memory layout as Self (MaybeUninit<Self>).
let uninit_callable = MaybeUninit::<Self>::uninit(); let uninit_callable = MaybeUninit::<Self>::uninit();
// Then move that value into the closure. We assume that the closure now has a in memory layout of Self. // Then move that value into the closure. We assume that the closure now has a in memory layout of Self.
let uninit_closure = move || Self::read(unsafe { &*uninit_callable.as_ptr() }); let uninit_closure = move || {
let res = Self::read(unsafe { &*uninit_callable.as_ptr() });
res.clone()
};
// Check that the size of the closure is the same as the size of Self in case the compiler changed the layout of the closure. // Check that the size of the closure is the same as the size of Self in case the compiler changed the layout of the closure.
let size_of_closure = std::mem::size_of_val(&uninit_closure); let size_of_closure = std::mem::size_of_val(&uninit_closure);
@@ -483,8 +486,8 @@ impl<T: 'static> PartialEq for ReadOnlySignal<T> {
} }
} }
impl<T> Deref for ReadOnlySignal<T> { impl<T: Copy> Deref for ReadOnlySignal<T> {
type Target = dyn Fn() -> GenerationalRef<T>; type Target = dyn Fn() -> T;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
// https://github.com/dtolnay/case-studies/tree/master/callable-types // https://github.com/dtolnay/case-studies/tree/master/callable-types
@@ -492,7 +495,7 @@ impl<T> Deref for ReadOnlySignal<T> {
// First we create a closure that captures something with the Same in memory layout as Self (MaybeUninit<Self>). // First we create a closure that captures something with the Same in memory layout as Self (MaybeUninit<Self>).
let uninit_callable = MaybeUninit::<Self>::uninit(); let uninit_callable = MaybeUninit::<Self>::uninit();
// Then move that value into the closure. We assume that the closure now has a in memory layout of Self. // Then move that value into the closure. We assume that the closure now has a in memory layout of Self.
let uninit_closure = move || Self::read(unsafe { &*uninit_callable.as_ptr() }); let uninit_closure = move || Self::read(unsafe { &*uninit_callable.as_ptr() }).clone();
// Check that the size of the closure is the same as the size of Self in case the compiler changed the layout of the closure. // Check that the size of the closure is the same as the size of Self in case the compiler changed the layout of the closure.
let size_of_closure = std::mem::size_of_val(&uninit_closure); let size_of_closure = std::mem::size_of_val(&uninit_closure);
+1 -1
View File
@@ -29,7 +29,7 @@ fn memos_rerun() {
selector(move || { selector(move || {
counter.borrow_mut().effect += 1; counter.borrow_mut().effect += 1;
println!("Signal: {:?}", signal); println!("Signal: {:?}", signal);
signal.value() signal.cloned()
}) })
}); });
assert_eq!(memo.value(), 0); assert_eq!(memo.value(), 0);