summaryrefslogtreecommitdiffstats
path: root/examples/download_progress
diff options
context:
space:
mode:
authorLibravatar Songtronix <contact@songtronix.com>2020-03-23 15:54:23 +0100
committerLibravatar Songtronix <contact@songtronix.com>2020-03-23 17:53:57 +0100
commitfff333f89ba99f32171641f0e8d78c9cdfe291b4 (patch)
tree08d267252d154f0fb936f14195cb5acc40340510 /examples/download_progress
parent092e9fb4cc3edcf2083b4464d24d50c82a1163d2 (diff)
downloadiced-fff333f89ba99f32171641f0e8d78c9cdfe291b4.tar.gz
iced-fff333f89ba99f32171641f0e8d78c9cdfe291b4.tar.bz2
iced-fff333f89ba99f32171641f0e8d78c9cdfe291b4.zip
Add example for download with progress tracking
Diffstat (limited to 'examples/download_progress')
-rw-r--r--examples/download_progress/Cargo.toml13
-rw-r--r--examples/download_progress/README.md15
-rw-r--r--examples/download_progress/src/downloader.rs99
-rw-r--r--examples/download_progress/src/main.rs116
4 files changed, 243 insertions, 0 deletions
diff --git a/examples/download_progress/Cargo.toml b/examples/download_progress/Cargo.toml
new file mode 100644
index 00000000..ce0435fd
--- /dev/null
+++ b/examples/download_progress/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "download_progress"
+version = "0.1.0"
+authors = ["Songtronix <contact@songtronix.com>"]
+edition = "2018"
+publish = false
+
+[dependencies]
+iced = { path = "../.." }
+iced_native = { path = "../../native" }
+iced_futures = { path = "../../futures" }
+async-std = { version = "1.0", features = ["unstable"] }
+isahc = "0.9.1"
diff --git a/examples/download_progress/README.md b/examples/download_progress/README.md
new file mode 100644
index 00000000..c6311163
--- /dev/null
+++ b/examples/download_progress/README.md
@@ -0,0 +1,15 @@
+## Download Progress
+
+Downloading a file asynchronously with a `Subscription` while displaying the progress with a `ProgressBar`.
+
+<div align="center">
+ <a href="https://gfycat.com/watchfulwelcomeladybird">
+ <img src="https://gfycat.com/watchfulwelcomeladybird">
+ </a>
+</div>
+
+You can run it with `cargo run`:
+
+```
+cargo run --package download_progress
+```
diff --git a/examples/download_progress/src/downloader.rs b/examples/download_progress/src/downloader.rs
new file mode 100644
index 00000000..62f943fd
--- /dev/null
+++ b/examples/download_progress/src/downloader.rs
@@ -0,0 +1,99 @@
+use iced_futures::futures;
+
+// Just a little utility function
+pub fn file<T: ToString>(url: T) -> iced::Subscription<DownloadMessage> {
+ iced::Subscription::from_recipe(Downloader {
+ url: url.to_string(),
+ })
+}
+
+pub struct Downloader {
+ url: String,
+}
+
+// Make sure iced can use our download stream
+impl<H, I> iced_native::subscription::Recipe<H, I> for Downloader
+where
+ H: std::hash::Hasher,
+{
+ type Output = DownloadMessage;
+
+ fn hash(&self, state: &mut H) {
+ use std::hash::Hash;
+ std::any::TypeId::of::<Self>().hash(state);
+ }
+
+ fn stream(
+ 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| 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
+ ));
+
+ 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),
+ ))
+ }
+ }
+ DownloadState::Finished => None,
+ }
+ },
+ ))
+ }
+}
+
+#[derive(Debug)]
+pub enum DownloadMessage {
+ DownloadStarted,
+ Downloading(u64),
+ Done,
+}
+
+pub enum DownloadState {
+ Ready(String),
+ Downloading(isahc::Metrics),
+ Finished,
+}
diff --git a/examples/download_progress/src/main.rs b/examples/download_progress/src/main.rs
new file mode 100644
index 00000000..936144d5
--- /dev/null
+++ b/examples/download_progress/src/main.rs
@@ -0,0 +1,116 @@
+use iced::{
+ button, executor, Align, Application, Button, Column, Command, Container,
+ Element, Length, ProgressBar, Settings, Subscription, Text,
+};
+
+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,
+}
+
+#[derive(Debug)]
+pub enum Message {
+ DownloadUpdate(downloader::DownloadMessage),
+ Interaction(Interaction),
+}
+
+// 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,
+}
+
+impl Application for Downloader {
+ type Executor = executor::Default;
+ type Message = Message;
+
+ fn new() -> (Downloader, Command<Message>) {
+ (Downloader::default(), Command::none())
+ }
+
+ fn title(&self) -> String {
+ 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::DownloadUpdate(update) => match update {
+ downloader::DownloadMessage::Downloading(percentage) => {
+ self.current_progress = percentage;
+ }
+ downloader::DownloadMessage::Done => {
+ self.current_progress = 100;
+ self.enabled = false;
+ }
+ _ => {}
+ },
+ };
+
+ Command::none()
+ }
+
+ fn subscription(&self) -> Subscription<Message> {
+ if self.enabled {
+ downloader::file("https://speed.hetzner.de/100MB.bin")
+ .map(Message::DownloadUpdate)
+ } else {
+ Subscription::none()
+ }
+ }
+
+ fn view(&mut self) -> Element<Message> {
+ // Construct widgets
+
+ let toggle_text = match self.enabled {
+ true => "Downloading...",
+ false => "Start the download!",
+ };
+
+ 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(),
+ };
+
+ // Construct layout
+ let content = Column::new()
+ .align_items(Align::Center)
+ .spacing(20)
+ .padding(20)
+ .push(Text::new(progress_text))
+ .push(progress_bar)
+ .push(toggle.map(Message::Interaction));
+
+ Container::new(content)
+ .width(Length::Fill)
+ .height(Length::Fill)
+ .center_x()
+ .center_y()
+ .into()
+ }
+}