diff options
Diffstat (limited to '')
-rw-r--r-- | examples/events.rs | 91 | ||||
-rw-r--r-- | examples/pokedex.rs | 23 | ||||
-rw-r--r-- | examples/stopwatch.rs | 182 | ||||
-rw-r--r-- | examples/todos.rs | 27 |
4 files changed, 298 insertions, 25 deletions
diff --git a/examples/events.rs b/examples/events.rs new file mode 100644 index 00000000..7d83fbd8 --- /dev/null +++ b/examples/events.rs @@ -0,0 +1,91 @@ +use iced::{ + Align, Application, Checkbox, Column, Command, Container, Element, Length, + Settings, Subscription, Text, +}; + +pub fn main() { + Events::run(Settings::default()) +} + +#[derive(Debug, Default)] +struct Events { + last: Vec<iced_native::Event>, + enabled: bool, +} + +#[derive(Debug, Clone)] +enum Message { + EventOccurred(iced_native::Event), + Toggled(bool), +} + +impl Application for Events { + type Message = Message; + + fn new() -> (Events, Command<Message>) { + (Events::default(), Command::none()) + } + + fn title(&self) -> String { + String::from("Events - Iced") + } + + fn update(&mut self, message: Message) -> Command<Message> { + match message { + Message::EventOccurred(event) => { + self.last.push(event); + + if self.last.len() > 5 { + let _ = self.last.remove(0); + } + } + Message::Toggled(enabled) => { + self.enabled = enabled; + } + }; + + Command::none() + } + + fn subscription(&self) -> Subscription<Message> { + if self.enabled { + iced_native::subscription::events().map(Message::EventOccurred) + } else { + Subscription::none() + } + } + + fn view(&mut self) -> Element<Message> { + let events = self.last.iter().fold( + Column::new().width(Length::Shrink).spacing(10), + |column, event| { + column.push( + Text::new(format!("{:?}", event)) + .size(40) + .width(Length::Shrink), + ) + }, + ); + + let toggle = Checkbox::new( + self.enabled, + "Listen to runtime events", + Message::Toggled, + ) + .width(Length::Shrink); + + let content = Column::new() + .width(Length::Shrink) + .align_items(Align::Center) + .spacing(20) + .push(events) + .push(toggle); + + Container::new(content) + .width(Length::Fill) + .height(Length::Fill) + .center_x() + .center_y() + .into() + } +} diff --git a/examples/pokedex.rs b/examples/pokedex.rs index b9daeabd..2d595ec4 100644 --- a/examples/pokedex.rs +++ b/examples/pokedex.rs @@ -150,7 +150,6 @@ impl Pokemon { async fn search() -> Result<Pokemon, Error> { use rand::Rng; use serde::Deserialize; - use std::io::Read; #[derive(Debug, Deserialize)] struct Entry { @@ -179,7 +178,11 @@ impl Pokemon { let url = format!("https://pokeapi.co/api/v2/pokemon-species/{}", id); let sprite = format!("https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/{}.png", id); - let entry: Entry = reqwest::get(&url)?.json()?; + let (entry, sprite): (Entry, _) = futures::future::try_join( + surf::get(&url).recv_json(), + surf::get(&sprite).recv_bytes(), + ) + .await?; let description = entry .flavor_text_entries @@ -188,13 +191,6 @@ impl Pokemon { .next() .ok_or(Error::LanguageError)?; - let mut sprite = reqwest::get(&sprite)?; - let mut bytes = Vec::new(); - - sprite - .read_to_end(&mut bytes) - .map_err(|_| Error::ImageError)?; - Ok(Pokemon { number: id, name: entry.name.to_uppercase(), @@ -203,7 +199,7 @@ impl Pokemon { .chars() .map(|c| if c.is_control() { ' ' } else { c }) .collect(), - image: image::Handle::from_memory(bytes), + image: image::Handle::from_memory(sprite), }) } } @@ -211,13 +207,12 @@ impl Pokemon { #[derive(Debug, Clone)] enum Error { APIError, - ImageError, LanguageError, } -impl From<reqwest::Error> for Error { - fn from(error: reqwest::Error) -> Error { - dbg!(&error); +impl From<surf::Exception> for Error { + fn from(exception: surf::Exception) -> Error { + dbg!(&exception); Error::APIError } diff --git a/examples/stopwatch.rs b/examples/stopwatch.rs new file mode 100644 index 00000000..7a7f0793 --- /dev/null +++ b/examples/stopwatch.rs @@ -0,0 +1,182 @@ +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<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, + )) + .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<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: I, + ) -> futures::stream::BoxStream<'static, Self::Output> { + use futures::stream::StreamExt; + + async_std::stream::interval(self.0) + .map(|_| std::time::Instant::now()) + .boxed() + } + } +} diff --git a/examples/todos.rs b/examples/todos.rs index 5f435fdc..42e88f65 100644 --- a/examples/todos.rs +++ b/examples/todos.rs @@ -517,21 +517,23 @@ impl SavedState { } async fn load() -> Result<SavedState, LoadError> { - use std::io::Read; + use async_std::prelude::*; let mut contents = String::new(); - let mut file = std::fs::File::open(Self::path()) + let mut file = async_std::fs::File::open(Self::path()) + .await .map_err(|_| LoadError::FileError)?; file.read_to_string(&mut contents) + .await .map_err(|_| LoadError::FileError)?; serde_json::from_str(&contents).map_err(|_| LoadError::FormatError) } async fn save(self) -> Result<(), SaveError> { - use std::io::Write; + use async_std::prelude::*; let json = serde_json::to_string_pretty(&self) .map_err(|_| SaveError::FormatError)?; @@ -539,20 +541,23 @@ impl SavedState { let path = Self::path(); if let Some(dir) = path.parent() { - std::fs::create_dir_all(dir) + async_std::fs::create_dir_all(dir) + .await .map_err(|_| SaveError::DirectoryError)?; } - let mut file = - std::fs::File::create(path).map_err(|_| SaveError::FileError)?; + { + let mut file = async_std::fs::File::create(path) + .await + .map_err(|_| SaveError::FileError)?; - file.write_all(json.as_bytes()) - .map_err(|_| SaveError::WriteError)?; + file.write_all(json.as_bytes()) + .await + .map_err(|_| SaveError::WriteError)?; + } // This is a simple way to save at most once every couple seconds - // We will be able to get rid of it once we implement event - // subscriptions - std::thread::sleep(std::time::Duration::from_secs(2)); + async_std::task::sleep(std::time::Duration::from_secs(2)).await; Ok(()) } |