summaryrefslogtreecommitdiffstats
path: root/examples/download_progress
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón Jiménez <hector0193@gmail.com>2020-03-23 20:34:16 +0100
committerLibravatar Héctor Ramón Jiménez <hector0193@gmail.com>2020-03-23 20:34:16 +0100
commit30c7db3f25d12461f2dec493f92c3f3282bd264d (patch)
treef7e1d6df642715d033686b86f41d77ebf1ac810e /examples/download_progress
parentfff333f89ba99f32171641f0e8d78c9cdfe291b4 (diff)
downloadiced-30c7db3f25d12461f2dec493f92c3f3282bd264d.tar.gz
iced-30c7db3f25d12461f2dec493f92c3f3282bd264d.tar.bz2
iced-30c7db3f25d12461f2dec493f92c3f3282bd264d.zip
Improve `download_progress` example
- Use `reqwest` with `Response::chunk` to notify progress. - Turn example state into an enum
Diffstat (limited to 'examples/download_progress')
-rw-r--r--examples/download_progress/Cargo.toml5
-rw-r--r--examples/download_progress/src/downloader.rs97
-rw-r--r--examples/download_progress/src/main.rs129
3 files changed, 117 insertions, 114 deletions
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<T: ToString>(url: T) -> iced::Subscription<DownloadMessage> {
+pub fn file<T: ToString>(url: T) -> iced::Subscription<Progress> {
iced::Subscription::from_recipe(Downloader {
url: url.to_string(),
})
@@ -16,7 +16,7 @@ impl<H, I> iced_native::subscription::Recipe<H, I> 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<Self>,
_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();*/
-
- async_std::task::spawn(async_std::io::copy(
- resp.into_body(),
- async_std::io::sink(), //file
- ));
+ State::Ready(url) => {
+ let response = reqwest::get(&url).await;
- Some((
- DownloadMessage::DownloadStarted,
- DownloadState::Downloading(metrics),
- ))
+ match response {
+ Ok(response) => Some((
+ Progress::Started,
+ State::Downloading {
+ total: response.content_length().unwrap(),
+ downloaded: 0,
+ response,
+ },
+ )),
+ Err(_) => None,
+ }
}
- DownloadState::Downloading(metrics) => {
- async_std::task::sleep(
- std::time::Duration::from_millis(100),
- )
- .await;
+ State::Downloading {
+ mut response,
+ total,
+ downloaded,
+ } => match response.chunk().await {
+ Ok(Some(chunk)) => {
+ let downloaded = downloaded + chunk.len() as u64;
- let percentage = metrics.download_progress().0 * 100
- / metrics.download_progress().1;
+ let percentage =
+ (downloaded as f32 / total as f32) * 100.0;
- if percentage == 100 {
Some((
- DownloadMessage::Done,
- DownloadState::Finished,
- ))
- } else {
- Some((
- DownloadMessage::Downloading(percentage),
- DownloadState::Downloading(metrics),
+ Progress::Advanced(percentage),
+ State::Downloading {
+ response,
+ total,
+ downloaded,
+ },
))
}
- }
- DownloadState::Finished => None,
+ Ok(None) => Some((Progress::Finished, State::Finished)),
+ Err(_) => None,
+ },
+ State::Finished => None,
}
},
))
}
}
-#[derive(Debug)]
-pub enum DownloadMessage {
- DownloadStarted,
- Downloading(u64),
- Done,
+#[derive(Debug, Clone)]
+pub enum Progress {
+ Started,
+ Advanced(f32),
+ Finished,
}
-pub enum DownloadState {
+pub enum State {
Ready(String),
- Downloading(isahc::Metrics),
+ 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<Message>) {
- (Downloader::default(), Command::none())
+ fn new() -> (Example, Command<Message>) {
+ (
+ 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<Message> {
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<Message> {
- 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<Message> {
- // 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<Interaction> =
- Button::new(&mut self.btn_state, Text::new(toggle_text))
- .on_press(Interaction::StartDownload)
- .into();
-
- 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 progress_bar = ProgressBar::new(0.0..=100.0, current_progress);
+
+ 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)