This allows us to introduce a platform-specific `Action` to both `iced_native` and `iced_web` and remove the `Clipboard` from `Application::update` to maintain purity. Additionally, this should let us implement further actions to let users query and modify the shell environment (e.g. window, clipboard, and more!)
171 lines
4.5 KiB
Rust
171 lines
4.5 KiB
Rust
use iced::{
|
|
button, executor, time, Align, Application, Button, Column, Command,
|
|
Container, Element, HorizontalAlignment, Length, Row, Settings,
|
|
Subscription, Text,
|
|
};
|
|
use std::time::{Duration, Instant};
|
|
|
|
pub fn main() -> iced::Result {
|
|
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 = executor::Default;
|
|
type Message = Message;
|
|
type Flags = ();
|
|
|
|
fn new(_flags: ()) -> (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 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.0,
|
|
shadow_offset: Vector::new(1.0, 1.0),
|
|
text_color: Color::WHITE,
|
|
..button::Style::default()
|
|
}
|
|
}
|
|
}
|
|
}
|