merge upstream changes
This commit is contained in:
343 files changed
+20245
-14076
No files matched your search
+1
-33
@@ -18,8 +18,6 @@ cargo run --example hello_world
|
||||
|
||||
[custom_assets](./custom_assets.rs) - Include images
|
||||
|
||||
[custom_element](./custom_element.rs) - Render webcomponents
|
||||
|
||||
[custom_html](./custom_html.rs) - Customize wrapper HTML
|
||||
|
||||
[eval](./eval.rs) - Evaluate JS expressions
|
||||
@@ -132,36 +130,6 @@ cargo run --example hello_world
|
||||
|
||||
[todomvc](./todomvc.rs) - Todo task list example
|
||||
|
||||
## Terminal UI
|
||||
|
||||
[tui_all_events](../packages/tui/examples/tui_all_events.rs) - All of the terminal events
|
||||
|
||||
[tui_border](../packages/tui/examples/tui_border.rs) - Different styles of borders and corners
|
||||
|
||||
[tui_buttons](../packages/tui/examples/tui_buttons.rs) - A grid of buttons that work demonstrate the focus system
|
||||
|
||||
[tui_color_test](../packages/tui/examples/tui_color_test.rs) - Grid of colors to demonstrate compatablility with different terminal color rendering modes
|
||||
|
||||
[tui_colorpicker](../packages/tui/examples/tui_colorpicker.rs) - A colorpicker
|
||||
|
||||
[tui_components](../packages/tui/examples/tui_components.rs) - Simple component example
|
||||
|
||||
[tui_flex](../packages/tui/examples/tui_flex.rs) - Flexbox support
|
||||
|
||||
[tui_hover](../packages/tui/examples/tui_hover.rs) - Detect hover and mouse events
|
||||
|
||||
[tui_list](../packages/tui/examples/tui_list.rs) - Renders a list of items
|
||||
|
||||
[tui_margin](../packages/tui/examples/tui_margin.rs) - Margins around flexboxes
|
||||
|
||||
[tui_quadrants](../packages/tui/examples/tui_quadrants.rs) - Four quadrants
|
||||
|
||||
[tui_readme](../packages/tui/examples/tui_readme.rs) - The readme example
|
||||
|
||||
[tui_task](../packages/tui/examples/tui_task.rs) - Background tasks
|
||||
|
||||
[tui_text](../packages/tui/examples/tui_text.rs) - Simple text example
|
||||
|
||||
# TODO
|
||||
Missing Features
|
||||
- Fine-grained reactivity
|
||||
@@ -175,4 +143,4 @@ Missing examples
|
||||
- Custom elements
|
||||
- Component Children: Pass children into child components
|
||||
- Render To string: Render a mounted virtualdom to a string
|
||||
- Testing and Debugging
|
||||
- Testing and Debugging
|
||||
@@ -0,0 +1,18 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
dioxus_desktop::launch(app);
|
||||
}
|
||||
|
||||
fn app(cx: Scope) -> Element {
|
||||
cx.render(rsx! {
|
||||
button {
|
||||
onclick: |_| async move {
|
||||
println!("hello, desktop!");
|
||||
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
|
||||
println!("goodbye, desktop!");
|
||||
},
|
||||
"hello, desktop!"
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -82,7 +82,7 @@ fn app(cx: Scope) -> Element {
|
||||
onclick: move |_| {
|
||||
let temp = calc_val(val.as_str());
|
||||
if temp > 0.0 {
|
||||
val.set(format!("-{}", temp));
|
||||
val.set(format!("-{temp}"));
|
||||
} else {
|
||||
val.set(format!("{}", temp.abs()));
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ fn app(cx: Scope) -> Element {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
println!("{:#?}, ", res);
|
||||
println!("{res:#?}, ");
|
||||
});
|
||||
|
||||
cx.render(rsx! {
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
//! Example: README.md showcase
|
||||
//!
|
||||
//! The example from the README.md.
|
||||
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_signals::{use_init_signal_rt, use_signal};
|
||||
|
||||
fn main() {
|
||||
dioxus_desktop::launch(app);
|
||||
}
|
||||
|
||||
fn app(cx: Scope) -> Element {
|
||||
use_init_signal_rt(cx);
|
||||
|
||||
let mut count = use_signal(cx, || 0);
|
||||
|
||||
use_future!(cx, || async move {
|
||||
loop {
|
||||
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
|
||||
count += 1;
|
||||
println!("current: {count}");
|
||||
}
|
||||
});
|
||||
|
||||
cx.render(rsx! {
|
||||
div { "High-Five counter: {count}" }
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
//! This example shows how to create a popup window and send data back to the parent window.
|
||||
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_desktop::use_window;
|
||||
use futures_util::StreamExt;
|
||||
|
||||
fn main() {
|
||||
dioxus_desktop::launch(app);
|
||||
}
|
||||
|
||||
fn app(cx: Scope) -> Element {
|
||||
let window = use_window(cx);
|
||||
let emails_sent = use_ref(cx, Vec::new);
|
||||
|
||||
let tx = use_coroutine(cx, |mut rx: UnboundedReceiver<String>| {
|
||||
to_owned![emails_sent];
|
||||
async move {
|
||||
while let Some(message) = rx.next().await {
|
||||
emails_sent.write().push(message);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
h1 { "This is your email" }
|
||||
|
||||
button {
|
||||
onclick: move |_| {
|
||||
let dom = VirtualDom::new_with_props(compose, ComposeProps {
|
||||
app_tx: tx.clone()
|
||||
});
|
||||
|
||||
// this returns a weak reference to the other window
|
||||
// Be careful not to keep a strong reference to the other window or it will never be dropped
|
||||
// and the window will never close.
|
||||
window.new_window(dom, Default::default());
|
||||
},
|
||||
"Click to compose a new email"
|
||||
}
|
||||
|
||||
ul {
|
||||
emails_sent.read().iter().map(|message| cx.render(rsx! {
|
||||
li {
|
||||
h3 { "email" }
|
||||
span {"{message}"}
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
struct ComposeProps {
|
||||
app_tx: Coroutine<String>,
|
||||
}
|
||||
|
||||
fn compose(cx: Scope<ComposeProps>) -> Element {
|
||||
let user_input = use_state(cx, String::new);
|
||||
let window = use_window(cx);
|
||||
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
h1 { "Compose a new email" }
|
||||
|
||||
button {
|
||||
onclick: move |_| {
|
||||
cx.props.app_tx.send(user_input.get().clone());
|
||||
window.close();
|
||||
},
|
||||
"Click to send"
|
||||
}
|
||||
|
||||
input {
|
||||
oninput: move |e| {
|
||||
user_input.set(e.value.clone());
|
||||
},
|
||||
value: "{user_input}"
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
//! Comparison example with leptos' counter example
|
||||
//! https://github.com/leptos-rs/leptos/blob/main/examples/counters/src/lib.rs
|
||||
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
dioxus_desktop::launch(app);
|
||||
}
|
||||
|
||||
fn app(cx: Scope) -> Element {
|
||||
let counters = use_state(cx, || vec![0, 0, 0]);
|
||||
let sum: usize = counters.iter().copied().sum();
|
||||
|
||||
render! {
|
||||
div {
|
||||
button { onclick: move |_| counters.make_mut().push(0), "Add counter" }
|
||||
button { onclick: move |_| { counters.make_mut().pop(); }, "Remove counter" }
|
||||
p { "Total: {sum}" }
|
||||
for (i, counter) in counters.iter().enumerate() {
|
||||
li {
|
||||
button { onclick: move |_| counters.make_mut()[i] -= 1, "-1" }
|
||||
input {
|
||||
value: "{counter}",
|
||||
oninput: move |e| {
|
||||
if let Ok(value) = e.value.parse::<usize>() {
|
||||
counters.make_mut()[i] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
button { onclick: move |_| counters.make_mut()[i] += 1, "+1" }
|
||||
button { onclick: move |_| { counters.make_mut().remove(i); }, "x" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,8 @@ fn app(cx: Scope) -> Element {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
"This should show an image:"
|
||||
img { src: "examples/assets/logo.png", }
|
||||
img { src: "examples/assets/logo.png" }
|
||||
img { src: "/Users/jonkelley/Desktop/blitz.png" }
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
//! This example shows to wrap a webcomponent / custom element with a component.
|
||||
//!
|
||||
//! Oftentimes, a third party library will provide a webcomponent that you want
|
||||
//! to use in your application. This example shows how to create that custom element
|
||||
//! directly with the raw_element method on NodeFactory.
|
||||
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
let mut dom = VirtualDom::new(app);
|
||||
let _ = dom.rebuild();
|
||||
|
||||
let output = dioxus_ssr::render(&dom);
|
||||
|
||||
println!("{}", output);
|
||||
}
|
||||
|
||||
fn app(cx: Scope) -> Element {
|
||||
let g = cx.component(component, (), "component");
|
||||
let c = cx.make_node(g);
|
||||
cx.render(rsx! {
|
||||
div { c }
|
||||
})
|
||||
|
||||
// let nf = NodeFactory::new(cx);
|
||||
|
||||
// let mut attrs = dioxus::core::exports::bumpalo::collections::Vec::new_in(nf.bump());
|
||||
|
||||
// attrs.push(nf.attr("client-id", format_args!("abc123"), None, false));
|
||||
|
||||
// attrs.push(nf.attr("name", format_args!("bob"), None, false));
|
||||
|
||||
// attrs.push(nf.attr("age", format_args!("47"), None, false));
|
||||
|
||||
// Some(nf.raw_element("my-element", None, &[], attrs.into_bump_slice(), &[], None))
|
||||
}
|
||||
|
||||
fn component(cx: Scope) -> Element {
|
||||
todo!()
|
||||
}
|
||||
+1
-1
@@ -52,7 +52,7 @@ struct DogApi {
|
||||
#[inline_props]
|
||||
async fn breed_pic(cx: Scope, breed: String) -> Element {
|
||||
let fut = use_future!(cx, |breed| async move {
|
||||
reqwest::get(format!("https://dog.ceo/api/breed/{}/images/random", breed))
|
||||
reqwest::get(format!("https://dog.ceo/api/breed/{breed}/images/random"))
|
||||
.await
|
||||
.unwrap()
|
||||
.json::<DogApi>()
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
dioxus_desktop::launch(app);
|
||||
}
|
||||
|
||||
fn app(cx: Scope) -> Element {
|
||||
let count = if cx.generation() % 2 == 0 { 10 } else { 0 };
|
||||
|
||||
println!("Generation: {}", cx.generation());
|
||||
|
||||
if cx.generation() < 10 {
|
||||
cx.needs_update();
|
||||
}
|
||||
|
||||
render! {
|
||||
(0..count).map(|_| rsx!{
|
||||
drop_child {}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn drop_child(cx: Scope) -> Element {
|
||||
cx.use_hook(|| Drops);
|
||||
render! {
|
||||
div{}
|
||||
}
|
||||
}
|
||||
|
||||
struct Drops;
|
||||
|
||||
impl Drop for Drops {
|
||||
fn drop(&mut self) {
|
||||
println!("Dropped!");
|
||||
}
|
||||
}
|
||||
+9
-13
@@ -1,25 +1,17 @@
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_desktop::EvalResult;
|
||||
|
||||
fn main() {
|
||||
dioxus_desktop::launch(app);
|
||||
}
|
||||
|
||||
fn app(cx: Scope) -> Element {
|
||||
let script = use_state(cx, String::new);
|
||||
let eval = dioxus_desktop::use_eval(cx);
|
||||
let future: &UseRef<Option<EvalResult>> = use_ref(cx, || None);
|
||||
if future.read().is_some() {
|
||||
let future_clone = future.clone();
|
||||
cx.spawn(async move {
|
||||
if let Some(fut) = future_clone.with_mut(|o| o.take()) {
|
||||
println!("{:?}", fut.await)
|
||||
}
|
||||
});
|
||||
}
|
||||
let script = use_state(cx, String::new);
|
||||
let output = use_state(cx, String::new);
|
||||
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
p { "Output: {output}" }
|
||||
input {
|
||||
placeholder: "Enter an expression",
|
||||
value: "{script}",
|
||||
@@ -27,8 +19,12 @@ fn app(cx: Scope) -> Element {
|
||||
}
|
||||
button {
|
||||
onclick: move |_| {
|
||||
let fut = eval(script);
|
||||
future.set(Some(fut));
|
||||
to_owned![script, eval, output];
|
||||
cx.spawn(async move {
|
||||
if let Ok(res) = eval(script.to_string()).await {
|
||||
output.set(res.to_string());
|
||||
}
|
||||
});
|
||||
},
|
||||
"Execute"
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ fn main() {
|
||||
static NAME: Atom<String> = |_| "world".to_string();
|
||||
|
||||
fn app(cx: Scope) -> Element {
|
||||
use_init_atom_root(cx);
|
||||
let name = use_read(cx, NAME);
|
||||
|
||||
cx.render(rsx! {
|
||||
|
||||
@@ -89,7 +89,7 @@ impl Files {
|
||||
let paths = match std::fs::read_dir(cur_path) {
|
||||
Ok(e) => e,
|
||||
Err(err) => {
|
||||
let err = format!("An error occured: {:?}", err);
|
||||
let err = format!("An error occured: {err:?}");
|
||||
self.err = Some(err);
|
||||
self.path_stack.pop();
|
||||
return;
|
||||
|
||||
@@ -3,7 +3,7 @@ use dioxus_desktop::Config;
|
||||
|
||||
fn main() {
|
||||
let cfg = Config::new().with_file_drop_handler(|_w, e| {
|
||||
println!("{:?}", e);
|
||||
println!("{e:?}");
|
||||
true
|
||||
});
|
||||
|
||||
|
||||
+3
-3
@@ -42,7 +42,7 @@ fn app(cx: Scope) -> Element {
|
||||
// so the value of our input event will be either huey, dewey, louie, or true/false (because of the checkboxe)
|
||||
// be mindful in grouping inputs together, as they will all be handled by the same event handler
|
||||
oninput: move |evt| {
|
||||
println!("{:?}", evt);
|
||||
println!("{evt:?}");
|
||||
},
|
||||
div {
|
||||
input {
|
||||
@@ -104,7 +104,7 @@ fn app(cx: Scope) -> Element {
|
||||
name: "pdf",
|
||||
r#type: "checkbox",
|
||||
oninput: move |evt| {
|
||||
println!("{:?}", evt);
|
||||
println!("{evt:?}");
|
||||
},
|
||||
}
|
||||
label {
|
||||
@@ -121,7 +121,7 @@ fn app(cx: Scope) -> Element {
|
||||
r#type: "{field}",
|
||||
value: "{value}",
|
||||
oninput: move |evt: FormEvent| {
|
||||
println!("{:?}", evt);
|
||||
println!("{evt:?}");
|
||||
},
|
||||
}
|
||||
label {
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
dioxus_desktop::launch(app);
|
||||
}
|
||||
|
||||
fn app(cx: Scope) -> Element {
|
||||
let window = dioxus_desktop::use_window(cx);
|
||||
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
button {
|
||||
onclick: move |_| {
|
||||
let dom = VirtualDom::new(popup);
|
||||
window.new_window(dom, Default::default());
|
||||
},
|
||||
"New Window"
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn popup(cx: Scope) -> Element {
|
||||
cx.render(rsx! {
|
||||
div { "This is a popup!" }
|
||||
})
|
||||
}
|
||||
@@ -16,18 +16,18 @@ fn app(cx: Scope) -> Element {
|
||||
onclick: move |_| println!("clicked! top"),
|
||||
"- div"
|
||||
button {
|
||||
onclick: move |_| println!("clicked! bottom propoate"),
|
||||
"Propogate"
|
||||
onclick: move |_| println!("clicked! bottom propagate"),
|
||||
"Propagate"
|
||||
}
|
||||
button {
|
||||
onclick: move |evt| {
|
||||
println!("clicked! bottom no bubbling");
|
||||
evt.stop_propogation();
|
||||
evt.stop_propagation();
|
||||
},
|
||||
"Dont propogate"
|
||||
"Dont propagate"
|
||||
}
|
||||
button {
|
||||
"Does not handle clicks - only propogate"
|
||||
"Does not handle clicks - only propagate"
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_desktop::{tao::dpi::PhysicalPosition, use_window, LogicalSize, WindowBuilder};
|
||||
|
||||
fn main() {
|
||||
dioxus_desktop::launch_cfg(app, make_config());
|
||||
}
|
||||
|
||||
fn app(cx: Scope) -> Element {
|
||||
let window = use_window(cx);
|
||||
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
background_color: "red",
|
||||
border: "1px solid black",
|
||||
|
||||
div {
|
||||
width: "100%",
|
||||
height: "10px",
|
||||
background_color: "black",
|
||||
onmousedown: move |_| window.drag(),
|
||||
}
|
||||
|
||||
"This is an overlay!"
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn make_config() -> dioxus_desktop::Config {
|
||||
dioxus_desktop::Config::default()
|
||||
.with_window(make_window())
|
||||
.with_custom_head(
|
||||
r#"
|
||||
<style type="text/css">
|
||||
html, body {
|
||||
height: 100px;
|
||||
margin: 0;
|
||||
overscroll-behavior-y: none;
|
||||
overscroll-behavior-x: none;
|
||||
overflow: hidden;
|
||||
}
|
||||
#main, #bodywrap {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
overscroll-behavior-x: none;
|
||||
overscroll-behavior-y: none;
|
||||
}
|
||||
</style>
|
||||
"#
|
||||
.to_owned(),
|
||||
)
|
||||
}
|
||||
|
||||
fn make_window() -> WindowBuilder {
|
||||
WindowBuilder::new()
|
||||
.with_transparent(true)
|
||||
.with_decorations(false)
|
||||
.with_resizable(false)
|
||||
.with_always_on_top(true)
|
||||
.with_position(PhysicalPosition::new(0, 0))
|
||||
.with_max_inner_size(LogicalSize::new(100000, 50))
|
||||
}
|
||||
@@ -83,7 +83,7 @@ fn app(cx: Scope) -> Element {
|
||||
div {
|
||||
class: {
|
||||
const WORD: &str = "expressions";
|
||||
format_args!("Arguments can be passed in through curly braces for complex {}", WORD)
|
||||
format_args!("Arguments can be passed in through curly braces for complex {WORD}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -214,7 +214,7 @@ fn app(cx: Scope) -> Element {
|
||||
}
|
||||
|
||||
fn format_dollars(dollars: u32, cents: u32) -> String {
|
||||
format!("${}.{:02}", dollars, cents)
|
||||
format!("${dollars}.{cents:02}")
|
||||
}
|
||||
|
||||
fn helper<'a>(cx: &'a ScopeState, text: &str) -> Element<'a> {
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_desktop::tao::keyboard::ModifiersState;
|
||||
use dioxus_desktop::use_global_shortcut;
|
||||
|
||||
fn main() {
|
||||
dioxus_desktop::launch(app);
|
||||
}
|
||||
|
||||
fn app(cx: Scope) -> Element {
|
||||
let toggled = use_state(cx, || false);
|
||||
use_global_shortcut(cx, KeyCode::S, ModifiersState::CONTROL, {
|
||||
to_owned![toggled];
|
||||
move || toggled.modify(|t| !*t)
|
||||
});
|
||||
|
||||
cx.render(rsx!("toggle: {toggled.get()}"))
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_signals::{use_init_signal_rt, use_signal};
|
||||
use std::time::Duration;
|
||||
|
||||
fn main() {
|
||||
dioxus_desktop::launch(app);
|
||||
}
|
||||
|
||||
fn app(cx: Scope) -> Element {
|
||||
use_init_signal_rt(cx);
|
||||
|
||||
let mut count = use_signal(cx, || 0);
|
||||
|
||||
use_future!(cx, || async move {
|
||||
loop {
|
||||
count += 1;
|
||||
tokio::time::sleep(Duration::from_millis(400)).await;
|
||||
}
|
||||
});
|
||||
|
||||
cx.render(rsx! {
|
||||
h1 { "High-Five counter: {count}" }
|
||||
button { onclick: move |_| count += 1, "Up high!" }
|
||||
button { onclick: move |_| count -= 1, "Down low!" }
|
||||
|
||||
if count() > 5 {
|
||||
rsx!{ h2 { "High five!" } }
|
||||
}
|
||||
})
|
||||
}
|
||||
+1
-1
@@ -27,7 +27,7 @@ fn main() {
|
||||
let mut file = String::new();
|
||||
let mut renderer = dioxus_ssr::Renderer::default();
|
||||
renderer.render_to(&mut file, &vdom).unwrap();
|
||||
println!("{}", file);
|
||||
println!("{file}");
|
||||
}
|
||||
|
||||
fn app(cx: Scope) -> Element {
|
||||
|
||||
@@ -9,7 +9,7 @@ fn main() {
|
||||
fn app(cx: Scope) -> Element {
|
||||
let model = use_state(cx, || String::from("asd"));
|
||||
|
||||
println!("{}", model);
|
||||
println!("{model}");
|
||||
|
||||
cx.render(rsx! {
|
||||
textarea {
|
||||
|
||||
+86
-39
@@ -7,7 +7,7 @@ fn main() {
|
||||
dioxus_desktop::launch(app);
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||
pub enum FilterState {
|
||||
All,
|
||||
Active,
|
||||
@@ -39,56 +39,99 @@ pub fn app(cx: Scope<()>) -> Element {
|
||||
.collect::<Vec<_>>();
|
||||
filtered_todos.sort_unstable();
|
||||
|
||||
let show_clear_completed = todos.values().any(|todo| todo.checked);
|
||||
let items_left = filtered_todos.len();
|
||||
let item_text = match items_left {
|
||||
let active_todo_count = todos.values().filter(|item| !item.checked).count();
|
||||
let active_todo_text = match active_todo_count {
|
||||
1 => "item",
|
||||
_ => "items",
|
||||
};
|
||||
|
||||
cx.render(rsx!{
|
||||
let show_clear_completed = todos.values().any(|todo| todo.checked);
|
||||
|
||||
let selected = |state| {
|
||||
if *filter == state {
|
||||
"selected"
|
||||
} else {
|
||||
"false"
|
||||
}
|
||||
};
|
||||
|
||||
cx.render(rsx! {
|
||||
section { class: "todoapp",
|
||||
style { include_str!("./assets/todomvc.css") }
|
||||
div {
|
||||
header { class: "header",
|
||||
h1 {"todos"}
|
||||
input {
|
||||
class: "new-todo",
|
||||
placeholder: "What needs to be done?",
|
||||
value: "{draft}",
|
||||
autofocus: "true",
|
||||
oninput: move |evt| {
|
||||
draft.set(evt.value.clone());
|
||||
},
|
||||
onkeydown: move |evt| {
|
||||
if evt.key() == Key::Enter && !draft.is_empty() {
|
||||
todos.make_mut().insert(
|
||||
**todo_id,
|
||||
TodoItem {
|
||||
id: **todo_id,
|
||||
checked: false,
|
||||
contents: draft.to_string(),
|
||||
},
|
||||
);
|
||||
*todo_id.make_mut() += 1;
|
||||
draft.set("".to_string());
|
||||
}
|
||||
header { class: "header",
|
||||
h1 {"todos"}
|
||||
input {
|
||||
class: "new-todo",
|
||||
placeholder: "What needs to be done?",
|
||||
value: "{draft}",
|
||||
autofocus: "true",
|
||||
oninput: move |evt| {
|
||||
draft.set(evt.value.clone());
|
||||
},
|
||||
onkeydown: move |evt| {
|
||||
if evt.key() == Key::Enter && !draft.is_empty() {
|
||||
todos.make_mut().insert(
|
||||
**todo_id,
|
||||
TodoItem {
|
||||
id: **todo_id,
|
||||
checked: false,
|
||||
contents: draft.to_string(),
|
||||
},
|
||||
);
|
||||
*todo_id.make_mut() += 1;
|
||||
draft.set("".to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
section {
|
||||
class: "main",
|
||||
if !todos.is_empty() {
|
||||
rsx! {
|
||||
input {
|
||||
id: "toggle-all",
|
||||
class: "toggle-all",
|
||||
r#type: "checkbox",
|
||||
onchange: move |_| {
|
||||
let check = active_todo_count != 0;
|
||||
for (_, item) in todos.make_mut().iter_mut() {
|
||||
item.checked = check;
|
||||
}
|
||||
},
|
||||
checked: if active_todo_count == 0 { "true" } else { "false" },
|
||||
}
|
||||
label { r#for: "toggle-all" }
|
||||
}
|
||||
}
|
||||
ul { class: "todo-list",
|
||||
filtered_todos.iter().map(|id| rsx!(TodoEntry { key: "{id}", id: *id, todos: todos }))
|
||||
filtered_todos.iter().map(|id| rsx!(TodoEntry {
|
||||
key: "{id}",
|
||||
id: *id,
|
||||
todos: todos,
|
||||
}))
|
||||
}
|
||||
(!todos.is_empty()).then(|| rsx!(
|
||||
footer { class: "footer",
|
||||
span { class: "todo-count",
|
||||
strong {"{items_left} "}
|
||||
span {"{item_text} left"}
|
||||
strong {"{active_todo_count} "}
|
||||
span {"{active_todo_text} left"}
|
||||
}
|
||||
ul { class: "filters",
|
||||
li { class: "All", a { onclick: move |_| filter.set(FilterState::All), "All" }}
|
||||
li { class: "Active", a { onclick: move |_| filter.set(FilterState::Active), "Active" }}
|
||||
li { class: "Completed", a { onclick: move |_| filter.set(FilterState::Completed), "Completed" }}
|
||||
for (state, state_text, url) in [
|
||||
(FilterState::All, "All", "#/"),
|
||||
(FilterState::Active, "Active", "#/active"),
|
||||
(FilterState::Completed, "Completed", "#/completed"),
|
||||
] {
|
||||
li {
|
||||
a {
|
||||
href: url,
|
||||
class: selected(state),
|
||||
onclick: move |_| filter.set(state),
|
||||
prevent_default: "onclick",
|
||||
state_text
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
show_clear_completed.then(|| rsx!(
|
||||
button {
|
||||
@@ -102,8 +145,8 @@ pub fn app(cx: Scope<()>) -> Element {
|
||||
}
|
||||
}
|
||||
footer { class: "info",
|
||||
p {"Double-click to edit a todo"}
|
||||
p { "Created by ", a { href: "http://github.com/jkelleyrtp/", "jkelleyrtp" }}
|
||||
p { "Double-click to edit a todo" }
|
||||
p { "Created by ", a { href: "http://github.com/jkelleyrtp/", "jkelleyrtp" }}
|
||||
p { "Part of ", a { href: "http://todomvc.com", "TodoMVC" }}
|
||||
}
|
||||
})
|
||||
@@ -136,13 +179,17 @@ pub fn TodoEntry<'a>(cx: Scope<'a, TodoEntryProps<'a>>) -> Element {
|
||||
cx.props.todos.make_mut()[&cx.props.id].checked = evt.value.parse().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
label {
|
||||
r#for: "cbg-{todo.id}",
|
||||
onclick: move |_| is_editing.set(true),
|
||||
ondblclick: move |_| is_editing.set(true),
|
||||
prevent_default: "onclick",
|
||||
"{todo.contents}"
|
||||
}
|
||||
button {
|
||||
class: "destroy",
|
||||
onclick: move |_| { cx.props.todos.make_mut().remove(&todo.id); },
|
||||
prevent_default: "onclick",
|
||||
}
|
||||
}
|
||||
is_editing.then(|| rsx!{
|
||||
input {
|
||||
|
||||
@@ -35,13 +35,13 @@ fn app(cx: Scope) -> Element {
|
||||
nav { class: "md:ml-auto flex flex-wrap items-center text-base justify-center" }
|
||||
button {
|
||||
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_propogation(),
|
||||
onmousedown: |evt| evt.stop_propagation(),
|
||||
onclick: move |_| window.set_minimized(true),
|
||||
"Minimize"
|
||||
}
|
||||
button {
|
||||
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_propogation(),
|
||||
onmousedown: |evt| evt.stop_propagation(),
|
||||
onclick: move |_| {
|
||||
|
||||
window.set_fullscreen(!**fullscreen);
|
||||
@@ -52,7 +52,7 @@ fn app(cx: Scope) -> Element {
|
||||
}
|
||||
button {
|
||||
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_propogation(),
|
||||
onmousedown: |evt| evt.stop_propagation(),
|
||||
onclick: move |_| window.close(),
|
||||
"Close"
|
||||
}
|
||||
@@ -66,7 +66,7 @@ fn app(cx: Scope) -> Element {
|
||||
div {
|
||||
button {
|
||||
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_propogation(),
|
||||
onmousedown: |evt| evt.stop_propagation(),
|
||||
onclick: move |_| {
|
||||
window.set_always_on_top(!always_on_top);
|
||||
always_on_top.set(!always_on_top);
|
||||
@@ -77,7 +77,7 @@ fn app(cx: Scope) -> Element {
|
||||
div {
|
||||
button {
|
||||
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_propogation(),
|
||||
onmousedown: |evt| evt.stop_propagation(),
|
||||
onclick: move |_| {
|
||||
window.set_decorations(!decorations);
|
||||
decorations.set(!decorations);
|
||||
@@ -88,7 +88,7 @@ fn app(cx: Scope) -> Element {
|
||||
div {
|
||||
button {
|
||||
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_propogation(),
|
||||
onmousedown: |evt| evt.stop_propagation(),
|
||||
onclick: move |_| window.set_title("Dioxus Application"),
|
||||
"Change Title"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_desktop::tao::event::WindowEvent;
|
||||
use dioxus_desktop::use_wry_event_handler;
|
||||
use dioxus_desktop::wry::application::event::Event as WryEvent;
|
||||
|
||||
fn main() {
|
||||
dioxus_desktop::launch(app);
|
||||
}
|
||||
|
||||
fn app(cx: Scope) -> Element {
|
||||
let focused = use_state(cx, || false);
|
||||
|
||||
use_wry_event_handler(cx, {
|
||||
to_owned![focused];
|
||||
move |event, _| {
|
||||
if let WryEvent::WindowEvent {
|
||||
event: WindowEvent::Focused(new_focused),
|
||||
..
|
||||
} = event
|
||||
{
|
||||
focused.set(*new_focused);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
cx.render(rsx! {
|
||||
div{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
display: "flex",
|
||||
flex_direction: "column",
|
||||
align_items: "center",
|
||||
{
|
||||
if *focused.get() {
|
||||
"This window is focused!"
|
||||
} else {
|
||||
"This window is not focused!"
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -7,16 +7,17 @@ fn main() {
|
||||
|
||||
fn app(cx: Scope) -> Element {
|
||||
let window = use_window(cx);
|
||||
|
||||
let level = use_state(cx, || 1.0);
|
||||
|
||||
cx.render(rsx! {
|
||||
input {
|
||||
r#type: "number",
|
||||
value: "{level}",
|
||||
oninput: |e| {
|
||||
let num = e.value.parse::<f64>().unwrap_or(1.0);
|
||||
level.set(num);
|
||||
window.set_zoom_level(num);
|
||||
if let Ok(new_zoom) = e.value.parse::<f64>() {
|
||||
level.set(new_zoom);
|
||||
window.webview.zoom(new_zoom);
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
Reference in new issue
Block a user