diff --git a/examples/download_progress/Cargo.toml b/examples/download_progress/Cargo.toml index ce0435fd..34e6a132 100644 --- a/examples/download_progress/Cargo.toml +++ b/examples/download_progress/Cargo.toml @@ -6,8 +6,7 @@ edition = "2018" publish = false [dependencies] -iced = { path = "../.." } +iced = { path = "../..", features = ["tokio"] } iced_native = { path = "../../native" } iced_futures = { path = "../../futures" } -async-std = { version = "1.0", features = ["unstable"] } -isahc = "0.9.1" +reqwest = "0.10" diff --git a/examples/download_progress/src/downloader.rs b/examples/download_progress/src/downloader.rs index 62f943fd..3b54341e 100644 --- a/examples/download_progress/src/downloader.rs +++ b/examples/download_progress/src/downloader.rs @@ -1,7 +1,7 @@ use iced_futures::futures; // Just a little utility function -pub fn file(url: T) -> iced::Subscription { +pub fn file(url: T) -> iced::Subscription { iced::Subscription::from_recipe(Downloader { url: url.to_string(), }) @@ -16,7 +16,7 @@ impl iced_native::subscription::Recipe for Downloader where H: std::hash::Hasher, { - type Output = DownloadMessage; + type Output = Progress; fn hash(&self, state: &mut H) { use std::hash::Hash; @@ -27,73 +27,68 @@ where self: Box, _input: futures::stream::BoxStream<'static, I>, ) -> futures::stream::BoxStream<'static, Self::Output> { - use isahc::prelude::*; - Box::pin(futures::stream::unfold( - DownloadState::Ready(self.url), + State::Ready(self.url), |state| async move { match state { - DownloadState::Ready(url) => { - let resp = Request::get(&url) - .metrics(true) - .body(()) - .unwrap() - .send_async() - .await - .unwrap(); - let metrics = resp.metrics().unwrap().clone(); - // If you actually want to download: - /*let file = async_std::fs::File::create("download.bin") - .await - .unwrap();*/ + State::Ready(url) => { + let response = reqwest::get(&url).await; - async_std::task::spawn(async_std::io::copy( - resp.into_body(), - async_std::io::sink(), //file - )); - - Some(( - DownloadMessage::DownloadStarted, - DownloadState::Downloading(metrics), - )) - } - DownloadState::Downloading(metrics) => { - async_std::task::sleep( - std::time::Duration::from_millis(100), - ) - .await; - - let percentage = metrics.download_progress().0 * 100 - / metrics.download_progress().1; - - if percentage == 100 { - Some(( - DownloadMessage::Done, - DownloadState::Finished, - )) - } else { - Some(( - DownloadMessage::Downloading(percentage), - DownloadState::Downloading(metrics), - )) + match response { + Ok(response) => Some(( + Progress::Started, + State::Downloading { + total: response.content_length().unwrap(), + downloaded: 0, + response, + }, + )), + Err(_) => None, } } - DownloadState::Finished => None, + State::Downloading { + mut response, + total, + downloaded, + } => match response.chunk().await { + Ok(Some(chunk)) => { + let downloaded = downloaded + chunk.len() as u64; + + let percentage = + (downloaded as f32 / total as f32) * 100.0; + + Some(( + Progress::Advanced(percentage), + State::Downloading { + response, + total, + downloaded, + }, + )) + } + Ok(None) => Some((Progress::Finished, State::Finished)), + Err(_) => None, + }, + State::Finished => None, } }, )) } } -#[derive(Debug)] -pub enum DownloadMessage { - DownloadStarted, - Downloading(u64), - Done, -} - -pub enum DownloadState { - Ready(String), - Downloading(isahc::Metrics), +#[derive(Debug, Clone)] +pub enum Progress { + Started, + Advanced(f32), + Finished, +} + +pub enum State { + Ready(String), + Downloading { + response: reqwest::Response, + total: u64, + downloaded: u64, + }, Finished, } diff --git a/examples/download_progress/src/main.rs b/examples/download_progress/src/main.rs index 936144d5..75e3bee0 100644 --- a/examples/download_progress/src/main.rs +++ b/examples/download_progress/src/main.rs @@ -6,60 +6,61 @@ use iced::{ mod downloader; pub fn main() { - Downloader::run(Settings::default()) -} - -#[derive(Debug, Default)] -struct Downloader { - // Whether to start the download or not. - enabled: bool, - // The current percentage of the download - current_progress: u64, - - btn_state: button::State, + Example::run(Settings::default()) } #[derive(Debug)] -pub enum Message { - DownloadUpdate(downloader::DownloadMessage), - Interaction(Interaction), +enum Example { + Idle { button: button::State }, + Downloading { progress: f32 }, + Finished { button: button::State }, } -// For explanation of why we use an Interaction enum see here: -// https://github.com/hecrj/iced/pull/155#issuecomment-573523405 #[derive(Debug, Clone)] -pub enum Interaction { - // User pressed the button to start the download - StartDownload, +pub enum Message { + DownloadProgressed(downloader::Progress), + Download, } -impl Application for Downloader { +impl Application for Example { type Executor = executor::Default; type Message = Message; - fn new() -> (Downloader, Command) { - (Downloader::default(), Command::none()) + fn new() -> (Example, Command) { + ( + Example::Idle { + button: button::State::new(), + }, + Command::none(), + ) } fn title(&self) -> String { - String::from("Download Progress - Iced") + String::from("Download progress - Iced") } fn update(&mut self, message: Message) -> Command { match message { - Message::Interaction(action) => match action { - Interaction::StartDownload => { - self.enabled = true; + Message::Download => match self { + Example::Idle { .. } | Example::Finished { .. } => { + *self = Example::Downloading { progress: 0.0 }; } + _ => {} }, - Message::DownloadUpdate(update) => match update { - downloader::DownloadMessage::Downloading(percentage) => { - self.current_progress = percentage; - } - downloader::DownloadMessage::Done => { - self.current_progress = 100; - self.enabled = false; - } + Message::DownloadProgressed(message) => match self { + Example::Downloading { progress } => match message { + downloader::Progress::Started => { + *progress = 0.0; + } + downloader::Progress::Advanced(percentage) => { + *progress = percentage; + } + downloader::Progress::Finished => { + *self = Example::Finished { + button: button::State::new(), + } + } + }, _ => {} }, }; @@ -68,43 +69,51 @@ impl Application for Downloader { } fn subscription(&self) -> Subscription { - if self.enabled { - downloader::file("https://speed.hetzner.de/100MB.bin") - .map(Message::DownloadUpdate) - } else { - Subscription::none() + match self { + Example::Downloading { .. } => { + downloader::file("https://speed.hetzner.de/100MB.bin") + .map(Message::DownloadProgressed) + } + _ => Subscription::none(), } } fn view(&mut self) -> Element { - // Construct widgets - - let toggle_text = match self.enabled { - true => "Downloading...", - false => "Start the download!", + let current_progress = match self { + Example::Idle { .. } => 0.0, + Example::Downloading { progress } => *progress, + Example::Finished { .. } => 100.0, }; - let toggle: Element = - Button::new(&mut self.btn_state, Text::new(toggle_text)) - .on_press(Interaction::StartDownload) - .into(); + let progress_bar = ProgressBar::new(0.0..=100.0, current_progress); - let progress_bar = - ProgressBar::new(0.0..=100.0, self.current_progress as f32); - - let progress_text = &match self.enabled { - true => format!("Downloading {}%", self.current_progress), - false => "Ready to rock!".into(), + let control: Element<_> = match self { + Example::Idle { button } => { + Button::new(button, Text::new("Start the download!")) + .on_press(Message::Download) + .into() + } + Example::Finished { button } => Column::new() + .spacing(10) + .align_items(Align::Center) + .push(Text::new("Download finished!")) + .push( + Button::new(button, Text::new("Start again")) + .on_press(Message::Download), + ) + .into(), + Example::Downloading { .. } => { + Text::new(format!("Downloading... {:.2}%", current_progress)) + .into() + } }; - // Construct layout let content = Column::new() + .spacing(10) + .padding(10) .align_items(Align::Center) - .spacing(20) - .padding(20) - .push(Text::new(progress_text)) .push(progress_bar) - .push(toggle.map(Message::Interaction)); + .push(control); Container::new(content) .width(Length::Fill)