From 69ed631d449e74b38054aa052c620368cb65c72c Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 14 Dec 2019 00:45:38 +0100 Subject: Rename `timer` example to `stopwatch` --- examples/stopwatch.rs | 180 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 examples/stopwatch.rs (limited to 'examples/stopwatch.rs') diff --git a/examples/stopwatch.rs b/examples/stopwatch.rs new file mode 100644 index 00000000..4f931278 --- /dev/null +++ b/examples/stopwatch.rs @@ -0,0 +1,180 @@ +use iced::{ + button, Align, Application, Background, Button, Color, 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 Message = Message; + + fn new() -> (Stopwatch, Command) { + ( + 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 { + 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 subscriptions(&self) -> Subscription { + match self.state { + State::Idle => Subscription::none(), + State::Ticking { .. } => { + time::every(Duration::from_millis(10)).map(Message::Tick) + } + } + } + + fn view(&mut self) -> Element { + 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, + )) + .width(Length::Shrink) + .size(40); + + let button = |state, label, color: [f32; 3]| { + Button::new( + state, + Text::new(label) + .color(Color::WHITE) + .horizontal_alignment(HorizontalAlignment::Center), + ) + .min_width(80) + .background(Background::Color(color.into())) + .border_radius(10) + .padding(10) + }; + + let toggle_button = { + let (label, color) = match self.state { + State::Idle => ("Start", [0.11, 0.42, 0.87]), + State::Ticking { .. } => ("Stop", [0.9, 0.4, 0.4]), + }; + + button(&mut self.toggle, label, color).on_press(Message::Toggle) + }; + + let reset_button = button(&mut self.reset, "Reset", [0.7, 0.7, 0.7]) + .on_press(Message::Reset); + + let controls = Row::new() + .width(Length::Shrink) + .spacing(20) + .push(toggle_button) + .push(reset_button); + + let content = Column::new() + .width(Length::Shrink) + .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 { + pub fn every( + duration: std::time::Duration, + ) -> iced::Subscription { + iced::Subscription::from_recipe(Every(duration)) + } + + struct Every(std::time::Duration); + + impl iced_native::subscription::Recipe + for Every + { + type Output = std::time::Instant; + + fn hash(&self, state: &mut iced_native::Hasher) { + use std::hash::Hash; + + std::any::TypeId::of::().hash(state); + } + + fn stream( + &self, + _input: Input, + ) -> futures::stream::BoxStream<'static, Self::Output> { + use futures::stream::StreamExt; + + async_std::stream::interval(self.0) + .map(|_| std::time::Instant::now()) + .boxed() + } + } +} -- cgit From c688452d7beb1b17ef8416fc101f8868767fc457 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 14 Dec 2019 01:13:01 +0100 Subject: Consume `Recipe` when building a `Stream` --- examples/stopwatch.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'examples/stopwatch.rs') diff --git a/examples/stopwatch.rs b/examples/stopwatch.rs index 4f931278..b902baae 100644 --- a/examples/stopwatch.rs +++ b/examples/stopwatch.rs @@ -167,7 +167,7 @@ mod time { } fn stream( - &self, + self: Box, _input: Input, ) -> futures::stream::BoxStream<'static, Self::Output> { use futures::stream::StreamExt; -- cgit From 77154869061594706185709d7b79ac520c8a125a Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 14 Dec 2019 02:12:25 +0100 Subject: Use generic `Hasher` in `stopwatch` --- examples/stopwatch.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'examples/stopwatch.rs') diff --git a/examples/stopwatch.rs b/examples/stopwatch.rs index b902baae..686d239d 100644 --- a/examples/stopwatch.rs +++ b/examples/stopwatch.rs @@ -155,12 +155,13 @@ mod time { struct Every(std::time::Duration); - impl iced_native::subscription::Recipe - for Every + impl iced_native::subscription::Recipe for Every + where + Hasher: std::hash::Hasher, { type Output = std::time::Instant; - fn hash(&self, state: &mut iced_native::Hasher) { + fn hash(&self, state: &mut Hasher) { use std::hash::Hash; std::any::TypeId::of::().hash(state); -- cgit From c13ef73e8be5ba38451731c8f7e99ee54acf42f1 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 14 Dec 2019 02:20:45 +0100 Subject: Hash `Duration` of `time::Every` in `stopwatch` --- examples/stopwatch.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'examples/stopwatch.rs') diff --git a/examples/stopwatch.rs b/examples/stopwatch.rs index 686d239d..d21bdaa8 100644 --- a/examples/stopwatch.rs +++ b/examples/stopwatch.rs @@ -165,6 +165,7 @@ mod time { use std::hash::Hash; std::any::TypeId::of::().hash(state); + self.0.hash(state); } fn stream( -- cgit From 293314405f5b8d4003db5ef8f428e659ae36872d Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 14 Dec 2019 04:49:13 +0100 Subject: Make `iced_native` subscription input opaque --- examples/stopwatch.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'examples/stopwatch.rs') diff --git a/examples/stopwatch.rs b/examples/stopwatch.rs index d21bdaa8..0d52a091 100644 --- a/examples/stopwatch.rs +++ b/examples/stopwatch.rs @@ -155,13 +155,13 @@ mod time { struct Every(std::time::Duration); - impl iced_native::subscription::Recipe for Every + impl iced_native::subscription::Recipe for Every where - Hasher: std::hash::Hasher, + H: std::hash::Hasher, { type Output = std::time::Instant; - fn hash(&self, state: &mut Hasher) { + fn hash(&self, state: &mut H) { use std::hash::Hash; std::any::TypeId::of::().hash(state); @@ -170,7 +170,7 @@ mod time { fn stream( self: Box, - _input: Input, + _input: I, ) -> futures::stream::BoxStream<'static, Self::Output> { use futures::stream::StreamExt; -- cgit From d6c3da21f7fe7a79bcfbc2a180dc111e42300a04 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 14 Dec 2019 05:56:46 +0100 Subject: Write docs for subscriptions and reorganize a bit --- examples/stopwatch.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'examples/stopwatch.rs') diff --git a/examples/stopwatch.rs b/examples/stopwatch.rs index 0d52a091..7a7f0793 100644 --- a/examples/stopwatch.rs +++ b/examples/stopwatch.rs @@ -74,7 +74,7 @@ impl Application for Stopwatch { Command::none() } - fn subscriptions(&self) -> Subscription { + fn subscription(&self) -> Subscription { match self.state { State::Idle => Subscription::none(), State::Ticking { .. } => { -- cgit