diff options
Diffstat (limited to 'examples/stopwatch/src')
| -rw-r--r-- | examples/stopwatch/src/main.rs | 206 | 
1 files changed, 206 insertions, 0 deletions
diff --git a/examples/stopwatch/src/main.rs b/examples/stopwatch/src/main.rs new file mode 100644 index 00000000..d84c4817 --- /dev/null +++ b/examples/stopwatch/src/main.rs @@ -0,0 +1,206 @@ +use iced::{ +    button, Align, Application, Button, Column, Command, Container, Element, +    HorizontalAlignment, Length, Row, Settings, Subscription, Text, +}; +use std::time::{Duration, Instant}; + +pub fn main() { +    Stopwatch::run(Settings::default()) +} + +struct Stopwatch { +    duration: Duration, +    state: State, +    toggle: button::State, +    reset: button::State, +} + +enum State { +    Idle, +    Ticking { last_tick: Instant }, +} + +#[derive(Debug, Clone)] +enum Message { +    Toggle, +    Reset, +    Tick(Instant), +} + +impl Application for Stopwatch { +    type Executor = iced_futures::executor::AsyncStd; +    type Message = Message; + +    fn new() -> (Stopwatch, Command<Message>) { +        ( +            Stopwatch { +                duration: Duration::default(), +                state: State::Idle, +                toggle: button::State::new(), +                reset: button::State::new(), +            }, +            Command::none(), +        ) +    } + +    fn title(&self) -> String { +        String::from("Stopwatch - Iced") +    } + +    fn update(&mut self, message: Message) -> Command<Message> { +        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) => match &mut self.state { +                State::Ticking { last_tick } => { +                    self.duration += now - *last_tick; +                    *last_tick = now; +                } +                _ => {} +            }, +            Message::Reset => { +                self.duration = Duration::default(); +            } +        } + +        Command::none() +    } + +    fn subscription(&self) -> Subscription<Message> { +        match self.state { +            State::Idle => Subscription::none(), +            State::Ticking { .. } => { +                time::every(Duration::from_millis(10)).map(Message::Tick) +            } +        } +    } + +    fn view(&mut self) -> Element<Message> { +        const MINUTE: u64 = 60; +        const HOUR: u64 = 60 * MINUTE; + +        let seconds = self.duration.as_secs(); + +        let duration = Text::new(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 = |state, label, style| { +            Button::new( +                state, +                Text::new(label) +                    .horizontal_alignment(HorizontalAlignment::Center), +            ) +            .min_width(80) +            .padding(10) +            .style(style) +        }; + +        let toggle_button = { +            let (label, color) = match self.state { +                State::Idle => ("Start", style::Button::Primary), +                State::Ticking { .. } => ("Stop", style::Button::Destructive), +            }; + +            button(&mut self.toggle, label, color).on_press(Message::Toggle) +        }; + +        let reset_button = +            button(&mut self.reset, "Reset", style::Button::Secondary) +                .on_press(Message::Reset); + +        let controls = Row::new() +            .spacing(20) +            .push(toggle_button) +            .push(reset_button); + +        let content = Column::new() +            .align_items(Align::Center) +            .spacing(20) +            .push(duration) +            .push(controls); + +        Container::new(content) +            .width(Length::Fill) +            .height(Length::Fill) +            .center_x() +            .center_y() +            .into() +    } +} + +mod time { +    use iced::futures; + +    pub fn every( +        duration: std::time::Duration, +    ) -> iced::Subscription<std::time::Instant> { +        iced::Subscription::from_recipe(Every(duration)) +    } + +    struct Every(std::time::Duration); + +    impl<H, I> iced_native::subscription::Recipe<H, I> for Every +    where +        H: std::hash::Hasher, +    { +        type Output = std::time::Instant; + +        fn hash(&self, state: &mut H) { +            use std::hash::Hash; + +            std::any::TypeId::of::<Self>().hash(state); +            self.0.hash(state); +        } + +        fn stream( +            self: Box<Self>, +            _input: futures::stream::BoxStream<'static, I>, +        ) -> futures::stream::BoxStream<'static, Self::Output> { +            use futures::stream::StreamExt; + +            async_std::stream::interval(self.0) +                .map(|_| std::time::Instant::now()) +                .boxed() +        } +    } +} + +mod style { +    use iced::{button, Background, Color, Vector}; + +    pub enum Button { +        Primary, +        Secondary, +        Destructive, +    } + +    impl button::StyleSheet for Button { +        fn active(&self) -> button::Style { +            button::Style { +                background: Some(Background::Color(match self { +                    Button::Primary => Color::from_rgb(0.11, 0.42, 0.87), +                    Button::Secondary => Color::from_rgb(0.5, 0.5, 0.5), +                    Button::Destructive => Color::from_rgb(0.8, 0.2, 0.2), +                })), +                border_radius: 12, +                shadow_offset: Vector::new(1.0, 1.0), +                text_color: Color::WHITE, +                ..button::Style::default() +            } +        } +    } +}  | 
