#![allow(dead_code, unused)] use dioxus::desktop::use_window; use dioxus::prelude::*; use std::{ process::exit, time::{Duration, Instant}, }; use tokio::time::sleep; fn main() { dioxus::LaunchBuilder::desktop().launch(app); } struct WindowPreferences { always_on_top: bool, with_decorations: bool, exiting: Option, } impl Default for WindowPreferences { fn default() -> Self { Self { with_decorations: true, always_on_top: false, exiting: None, } } } impl WindowPreferences { fn new() -> Self { Self::default() } } #[derive(Default)] struct Timer { hours: u8, minutes: u8, seconds: u8, started_at: Option, } impl Timer { fn new() -> Self { Self::default() } fn duration(&self) -> Duration { Duration::from_secs( (self.hours as u64 * 60 + self.minutes as u64) * 60 + self.seconds as u64, ) } } const UPD_FREQ: Duration = Duration::from_millis(100); fn exit_button( delay: Duration, label: fn(Signal>, Duration) -> Option, ) -> Element { let mut trigger: Signal> = use_signal(|| None); use_future(move || async move { loop { sleep(UPD_FREQ).await; if let Some(true) = trigger.read().map(|e| e.elapsed() > delay) { exit(0); } } }); let stuff: Option = rsx! { button { onmouseup: move |_| { trigger.set(None); }, onmousedown: move |_| { trigger.set(Some(Instant::now())); }, width: 100, {label(trigger, delay)} } }; stuff } fn app() -> Element { let mut timer = use_signal(Timer::new); let mut window_preferences = use_signal(WindowPreferences::new); use_future(move || async move { loop { sleep(UPD_FREQ).await; timer.with_mut(|t| { if let Some(started_at) = t.started_at { if t.duration().saturating_sub(started_at.elapsed()) == Duration::ZERO { t.started_at = None; } } }); } }); rsx! { div { { let millis = timer .with(|t| { t .duration() .saturating_sub( t.started_at.map(|x| x.elapsed()).unwrap_or(Duration::ZERO), ) .as_millis() }); format!( "{:02}:{:02}:{:02}.{:01}", millis / 1000 / 3600 % 3600, millis / 1000 / 60 % 60, millis / 1000 % 60, millis / 100 % 10, ) } } div { input { r#type: "number", min: 0, max: 99, value: format!("{:02}", timer.read().hours), oninput: move |e| { timer.write().hours = e.value().parse().unwrap_or(0); }, } input { r#type: "number", min: 0, max: 59, value: format!("{:02}", timer.read().minutes), oninput: move |e| { timer.write().minutes = e.value().parse().unwrap_or(0); }, } input { r#type: "number", min: 0, max: 59, value: format!("{:02}", timer.read().seconds), oninput: move |e| { timer.write().seconds = e.value().parse().unwrap_or(0); }, } } button { id: "start_stop", onclick: move |_| { timer .with_mut(|t| { t.started_at = if t.started_at.is_none() { Some(Instant::now()) } else { None }; }) }, {timer.with(|t| if t.started_at.is_none() { "Start" } else { "Stop" })} } div { id: "app", button { onclick: move |_| { let decorations = window_preferences.read().with_decorations; use_window().set_decorations(!decorations); window_preferences.write().with_decorations = !decorations; }, { format!( "with decorations{}", if window_preferences.read().with_decorations { " ✓" } else { "" }, ) .to_string() } } button { onclick: move |_| { window_preferences .with_mut(|wp| { use_window().set_always_on_top(!wp.always_on_top); wp.always_on_top = !wp.always_on_top; }) }, width: 100, { format!( "always on top{}", if window_preferences.read().always_on_top { " ✓" } else { "" }, ) } } } { exit_button( Duration::from_secs(3), |trigger, delay| rsx! { { format!( "{:0.1?}", trigger .read() .map(|inst| (delay.as_secs_f32() - inst.elapsed().as_secs_f32())), ) } }, ) } } }