use iced::alignment; use iced::executor; use iced::theme::{self, Theme}; use iced::time; use iced::widget::{button, column, container, row, text}; use iced::{ Alignment, Application, Command, Element, Length, Settings, Subscription, }; use std::time::{Duration, Instant}; pub fn main() -> iced::Result { Stopwatch::run(Settings::default()) } struct Stopwatch { duration: Duration, state: State, } enum State { Idle, Ticking { last_tick: Instant }, } #[derive(Debug, Clone)] enum Message { Toggle, Reset, Tick(Instant), } impl Application for Stopwatch { type Message = Message; type Theme = Theme; type Executor = executor::Default; type Flags = (); fn new(_flags: ()) -> (Stopwatch, Command) { ( Stopwatch { duration: Duration::default(), state: State::Idle, }, Command::none(), ) } fn title(&self) -> String { String::from("Stopwatch - Iced") } fn update(&mut self, message: Message) -> Command { match message { Message::Toggle => match self.state { State::Idle => { self.state = State::Ticking { last_tick: Instant::now(), }; } State::Ticking { .. } => { self.state = State::Idle; } }, Message::Tick(now) => { if let State::Ticking { last_tick } = &mut self.state { self.duration += now - *last_tick; *last_tick = now; } } Message::Reset => { self.duration = Duration::default(); } } Command::none() } fn subscription(&self) -> Subscription { match self.state { State::Idle => Subscription::none(), State::Ticking { .. } => { time::every(Duration::from_millis(10)).map(Message::Tick) } } } fn view(&self) -> Element { const MINUTE: u64 = 60; const HOUR: u64 = 60 * MINUTE; let seconds = self.duration.as_secs(); let duration = text(format!( "{:0>2}:{:0>2}:{:0>2}.{:0>2}", seconds / HOUR, (seconds % HOUR) / MINUTE, seconds % MINUTE, self.duration.subsec_millis() / 10, )) .size(40); let button = |label| { button( text(label).horizontal_alignment(alignment::Horizontal::Center), ) .padding(10) .width(80) }; let toggle_button = { let label = match self.state { State::Idle => "Start", State::Ticking { .. } => "Stop", }; button(label).on_press(Message::Toggle) }; let reset_button = button("Reset") .style(theme::Button::Destructive) .on_press(Message::Reset); let controls = row![toggle_button, reset_button].spacing(20); let content = column![duration, controls] .align_items(Alignment::Center) .spacing(20); container(content) .width(Length::Fill) .height(Length::Fill) .center_x() .center_y() .into() } fn theme(&self) -> Theme { Theme::Dark } }