diff options
Diffstat (limited to 'examples')
-rw-r--r-- | examples/image.rs | 202 | ||||
-rw-r--r-- | examples/pokedex.rs | 231 |
2 files changed, 231 insertions, 202 deletions
diff --git a/examples/image.rs b/examples/image.rs deleted file mode 100644 index a64c0782..00000000 --- a/examples/image.rs +++ /dev/null @@ -1,202 +0,0 @@ -use iced::{ - button, image, Align, Application, Background, Button, Color, Column, - Command, Container, Element, HorizontalAlignment, Image, Length, Row, - Settings, Text, -}; -use serde::Deserialize; - -pub fn main() { - Example::run(Settings::default()) -} - -#[derive(Default)] -struct Example { - cats_button: button::State, - dogs_button: button::State, - image: Option<image::Handle>, - state: State, -} - -enum State { - Idle, - Loading(Pet), - Error(LoadError), -} - -impl Default for State { - fn default() -> State { - State::Idle - } -} - -#[derive(Debug, Clone)] -enum Message { - PetChosen(Pet), - ImageLoaded(Result<image::Handle, LoadError>), -} - -#[derive(Debug, Clone, Copy)] -enum Pet { - Cat, - Dog, -} - -impl Application for Example { - type Message = Message; - - fn new() -> (Self, Command<Message>) { - (Self::default(), Command::none()) - } - - fn title(&self) -> String { - String::from("Image viewer - Iced") - } - - fn update(&mut self, message: Message) -> Command<Message> { - match message { - Message::PetChosen(pet) => match self.state { - State::Loading(_) => Command::none(), - _ => { - self.state = State::Loading(pet); - - Command::perform(get_pet_image(pet), Message::ImageLoaded) - } - }, - Message::ImageLoaded(Ok(image)) => { - self.image = Some(image); - self.state = State::Idle; - - Command::none() - } - Message::ImageLoaded(Err(error)) => { - self.state = State::Error(error); - - Command::none() - } - } - } - - fn view(&mut self) -> Element<Message> { - let Example { - cats_button, - dogs_button, - state, - image, - } = self; - - let choose: Element<_> = match state { - State::Loading(pet) => Text::new(format!( - "Getting your {} ready...", - match pet { - Pet::Cat => "cat", - Pet::Dog => "dog", - } - )) - .width(Length::Shrink) - .color([0.4, 0.4, 0.4]) - .into(), - _ => Row::new() - .width(Length::Shrink) - .spacing(20) - .push( - button( - cats_button, - "Cats", - Color::from_rgb8(0x89, 0x80, 0xF5), - ) - .on_press(Message::PetChosen(Pet::Cat)), - ) - .push( - button( - dogs_button, - "Dogs", - Color::from_rgb8(0x21, 0xD1, 0x9F), - ) - .on_press(Message::PetChosen(Pet::Dog)), - ) - .into(), - }; - - let content = Column::new() - .width(Length::Shrink) - .padding(20) - .spacing(20) - .align_items(Align::Center) - .push( - Text::new("What do you want to see?") - .width(Length::Shrink) - .horizontal_alignment(HorizontalAlignment::Center) - .size(40), - ) - .push(choose); - - let content = if let Some(image) = image { - content.push(Image::new(image.clone()).height(Length::Fill)) - } else { - content - }; - - Container::new(content) - .width(Length::Fill) - .height(Length::Fill) - .center_x() - .center_y() - .into() - } -} - -fn button<'a, Message>( - state: &'a mut button::State, - label: &str, - color: Color, -) -> Button<'a, Message> { - Button::new( - state, - Text::new(label) - .horizontal_alignment(HorizontalAlignment::Center) - .color(Color::WHITE) - .size(30), - ) - .padding(10) - .min_width(100) - .border_radius(10) - .background(Background::Color(color)) -} - -#[derive(Debug, Deserialize)] -pub struct SearchResult { - url: String, -} - -#[derive(Debug, Clone)] -enum LoadError { - RequestError, -} - -async fn get_pet_image(pet: Pet) -> Result<image::Handle, LoadError> { - use std::io::Read; - - let search = match pet { - Pet::Cat => "https://api.thecatapi.com/v1/images/search?limit=1&mime_types=jpg,png", - Pet::Dog => "https://api.thedogapi.com/v1/images/search?limit=1&mime_types=jpg,png", - }; - - let results: Vec<SearchResult> = reqwest::get(search)?.json()?; - let url = &results.first().unwrap().url; - - let mut image = reqwest::get(url)?; - let mut bytes = Vec::new(); - - image - .read_to_end(&mut bytes) - .map_err(|_| LoadError::RequestError)?; - - Ok(image::Handle::from_bytes(bytes)) -} - -impl From<reqwest::Error> for LoadError { - fn from(error: reqwest::Error) -> LoadError { - dbg!(&error); - LoadError::RequestError - } -} diff --git a/examples/pokedex.rs b/examples/pokedex.rs new file mode 100644 index 00000000..8e0af7b2 --- /dev/null +++ b/examples/pokedex.rs @@ -0,0 +1,231 @@ +use iced::{ + button, image, Align, Application, Background, Button, Color, Column, + Command, Container, Element, Image, Length, Row, Settings, Text, +}; + +pub fn main() { + Pokedex::run(Settings::default()) +} + +#[derive(Debug)] +enum Pokedex { + Loading, + Loaded { + pokemon: Pokemon, + search: button::State, + }, + Errored { + error: Error, + try_again: button::State, + }, +} + +#[derive(Debug, Clone)] +enum Message { + PokemonFound(Result<Pokemon, Error>), + Search, +} + +impl Application for Pokedex { + type Message = Message; + + fn new() -> (Pokedex, Command<Message>) { + ( + Pokedex::Loading, + Command::perform(Pokemon::search(), Message::PokemonFound), + ) + } + + fn title(&self) -> String { + let subtitle = match self { + Pokedex::Loading => "Loading", + Pokedex::Loaded { pokemon, .. } => &pokemon.name, + Pokedex::Errored { .. } => "Whoops!", + }; + + format!("{} - Pokédex", subtitle) + } + + fn update(&mut self, message: Message) -> Command<Message> { + match message { + Message::PokemonFound(Ok(pokemon)) => { + *self = Pokedex::Loaded { + pokemon, + search: button::State::new(), + }; + + Command::none() + } + Message::PokemonFound(Err(error)) => { + *self = Pokedex::Errored { + error, + try_again: button::State::new(), + }; + + Command::none() + } + Message::Search => match self { + Pokedex::Loading => Command::none(), + _ => { + *self = Pokedex::Loading; + + Command::perform(Pokemon::search(), Message::PokemonFound) + } + }, + } + } + + fn view(&mut self) -> Element<Message> { + let content = match self { + Pokedex::Loading => Column::new().width(Length::Shrink).push( + Text::new("Searching for Pokémon...") + .width(Length::Shrink) + .size(40), + ), + Pokedex::Loaded { pokemon, search } => Column::new() + .max_width(500) + .spacing(20) + .align_items(Align::End) + .push(pokemon.view()) + .push( + button(search, "Keep searching!").on_press(Message::Search), + ), + Pokedex::Errored { try_again, .. } => Column::new() + .width(Length::Shrink) + .spacing(20) + .align_items(Align::End) + .push( + Text::new("Whoops! Something went wrong...") + .width(Length::Shrink) + .size(40), + ) + .push(button(try_again, "Try again").on_press(Message::Search)), + }; + + Container::new(content) + .width(Length::Fill) + .height(Length::Fill) + .center_x() + .center_y() + .into() + } +} + +#[derive(Debug, Clone)] +struct Pokemon { + number: u16, + name: String, + description: String, + image: image::Handle, +} + +impl Pokemon { + const TOTAL: u16 = 807; + + fn view(&self) -> Element<Message> { + Row::new() + .spacing(20) + .align_items(Align::Center) + .push(Image::new(self.image.clone())) + .push( + Column::new() + .spacing(20) + .push( + Row::new() + .align_items(Align::Center) + .spacing(20) + .push(Text::new(&self.name).size(30)) + .push( + Text::new(format!("#{}", self.number)) + .width(Length::Shrink) + .size(20) + .color([0.5, 0.5, 0.5]), + ), + ) + .push(Text::new(&self.description)), + ) + .into() + } + + async fn search() -> Result<Pokemon, Error> { + use rand::Rng; + use serde::Deserialize; + use std::io::Read; + + #[derive(Debug, Deserialize)] + struct Entry { + id: u32, + name: String, + flavor_text_entries: Vec<FlavorText>, + } + + #[derive(Debug, Deserialize)] + struct FlavorText { + flavor_text: String, + language: Language, + } + + #[derive(Debug, Deserialize)] + struct Language { + name: String, + } + + let id = { + let mut rng = rand::thread_rng(); + + rng.gen_range(0, Pokemon::TOTAL) + }; + + 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 description = entry + .flavor_text_entries + .iter() + .filter(|text| text.language.name == "en") + .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(), + description: description + .flavor_text + .chars() + .map(|c| if c.is_control() { ' ' } else { c }) + .collect(), + image: image::Handle::from_memory(bytes), + }) + } +} + +#[derive(Debug, Clone)] +enum Error { + APIError, + ImageError, + LanguageError, +} + +impl From<reqwest::Error> for Error { + fn from(error: reqwest::Error) -> Error { + dbg!(&error); + + Error::APIError + } +} + +fn button<'a>(state: &'a mut button::State, text: &str) -> Button<'a, Message> { + Button::new(state, Text::new(text).color(Color::WHITE)) + .background(Background::Color([0.11, 0.42, 0.87].into())) + .border_radius(10) + .padding(10) +} |