iced_7guis 0.3.0

An implementation of 7 GUIs using iced.
use iced::widget::{button, column, progress_bar, row, slider, text};
use iced::{Element, Size, Subscription, window};

use std::time::{Duration, Instant};

/// # Errors
///
/// The application may error.
pub fn main() -> iced::Result {
    iced::application(Timer::default, Timer::update, Timer::view)
        .title("Timer")
        .window(window::Settings {
            size: Size {
                width: 200.0,
                height: 180.0,
            },
            ..Default::default()
        })
        .subscription(Timer::run_timer)
        .run()
}

#[derive(Debug)]
struct InstantExt {
    inner: Instant,
}

impl Default for InstantExt {
    fn default() -> Self {
        InstantExt {
            inner: Instant::now(),
        }
    }
}

#[derive(Debug, Default)]
struct Timer {
    t0: InstantExt,
    t1: InstantExt,
    time: Vec<Duration>,
    starting: bool,
    duration: Duration,
    duration_max: f32,
    elapsed_time: f32,
}

#[derive(Debug, Clone, Copy)]
enum Message {
    Reset,
    SliderChanged(f32),
    Tick(Instant),
}

impl Timer {
    fn update(&mut self, message: Message) {
        match message {
            Message::Reset => {
                self.time.clear();
                self.t0 = InstantExt::default();
                self.starting = true;
            }
            Message::SliderChanged(max) => {
                self.duration_max = max;

                if self.starting && self.elapsed_time >= max {
                    self.starting = false;
                    self.time.push(self.t1.inner - self.t0.inner);
                }

                if !self.starting && self.elapsed_time < max {
                    self.starting = true;
                    self.t0 = InstantExt::default();
                }
            }
            Message::Tick(instant) => {
                if self.starting {
                    self.t1 = InstantExt { inner: instant };

                    if self.elapsed_time >= self.duration_max {
                        self.starting = false;
                        self.time.push(self.t1.inner - self.t0.inner);
                    }
                }

                self.duration = self.time.iter().sum();
                if self.starting {
                    self.duration += self.t1.inner - self.t0.inner;
                }

                self.elapsed_time = self.duration.as_secs_f32();
            }
        }
    }

    fn view(&self) -> Element<'_, Message> {
        let elapsed_time = row![
            "Elapsed Time: ",
            progress_bar(0.0..=self.duration_max, self.elapsed_time),
        ]
        .padding(10);

        let time_seconds = row![text(format!("{:.1}s", self.elapsed_time)),].padding(10);

        let duration = row![
            "Duration: ",
            slider(0.0001..=60.0, self.duration_max, Message::SliderChanged),
        ]
        .padding(10);

        let reset = row![button("Reset").on_press(Message::Reset),].padding(10);

        column![elapsed_time, time_seconds, duration, reset,].into()
    }

    fn run_timer(_self: &Self) -> Subscription<Message> {
        iced::time::every(Duration::from_millis(100)).map(|_| Message::Tick(Instant::now()))
    }
}