summaryrefslogblamecommitdiffstats
path: root/examples/image.rs
blob: a64c07822d885236f37838a2731b23b8fb191c23 (plain) (tree)









































































































































































































                                                                                            
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
    }
}