diff options
Diffstat (limited to '')
| -rw-r--r-- | examples/download_progress/src/download.rs | 46 | ||||
| -rw-r--r-- | examples/download_progress/src/main.rs | 205 | 
2 files changed, 171 insertions, 80 deletions
| diff --git a/examples/download_progress/src/download.rs b/examples/download_progress/src/download.rs index f46a01f7..08805f13 100644 --- a/examples/download_progress/src/download.rs +++ b/examples/download_progress/src/download.rs @@ -1,37 +1,46 @@  use iced_futures::futures; +use std::hash::{Hash, Hasher};  // Just a little utility function -pub fn file<T: ToString>(url: T) -> iced::Subscription<Progress> { +pub fn file<I: 'static + Hash + Copy + Send, T: ToString>( +    id: I, +    url: T, +) -> iced::Subscription<(I, Progress)> {      iced::Subscription::from_recipe(Download { +        id,          url: url.to_string(),      })  } -pub struct Download { +pub struct Download<I> { +    id: I,      url: String,  }  // Make sure iced can use our download stream -impl<H, I> iced_native::subscription::Recipe<H, I> for Download +impl<H, I, T> iced_native::subscription::Recipe<H, I> for Download<T>  where -    H: std::hash::Hasher, +    T: 'static + Hash + Copy + Send, +    H: Hasher,  { -    type Output = Progress; +    type Output = (T, Progress);      fn hash(&self, state: &mut H) { -        use std::hash::Hash; +        struct Marker; +        std::any::TypeId::of::<Marker>().hash(state); -        std::any::TypeId::of::<Self>().hash(state); -        self.url.hash(state); +        self.id.hash(state);      }      fn stream(          self: Box<Self>,          _input: futures::stream::BoxStream<'static, I>,      ) -> futures::stream::BoxStream<'static, Self::Output> { +        let id = self.id; +          Box::pin(futures::stream::unfold(              State::Ready(self.url), -            |state| async move { +            move |state| async move {                  match state {                      State::Ready(url) => {                          let response = reqwest::get(&url).await; @@ -40,7 +49,7 @@ where                              Ok(response) => {                                  if let Some(total) = response.content_length() {                                      Some(( -                                        Progress::Started, +                                        (id, Progress::Started),                                          State::Downloading {                                              response,                                              total, @@ -48,11 +57,14 @@ where                                          },                                      ))                                  } else { -                                    Some((Progress::Errored, State::Finished)) +                                    Some(( +                                        (id, Progress::Errored), +                                        State::Finished, +                                    ))                                  }                              }                              Err(_) => { -                                Some((Progress::Errored, State::Finished)) +                                Some(((id, Progress::Errored), State::Finished))                              }                          }                      } @@ -68,7 +80,7 @@ where                                  (downloaded as f32 / total as f32) * 100.0;                              Some(( -                                Progress::Advanced(percentage), +                                (id, Progress::Advanced(percentage)),                                  State::Downloading {                                      response,                                      total, @@ -76,8 +88,12 @@ where                                  },                              ))                          } -                        Ok(None) => Some((Progress::Finished, State::Finished)), -                        Err(_) => Some((Progress::Errored, State::Finished)), +                        Ok(None) => { +                            Some(((id, Progress::Finished), State::Finished)) +                        } +                        Err(_) => { +                            Some(((id, Progress::Errored), State::Finished)) +                        }                      },                      State::Finished => {                          // We do not let the stream die, as it would start a diff --git a/examples/download_progress/src/main.rs b/examples/download_progress/src/main.rs index 77b01354..6f844e66 100644 --- a/examples/download_progress/src/main.rs +++ b/examples/download_progress/src/main.rs @@ -1,6 +1,6 @@  use iced::{ -    button, executor, Align, Application, Button, Column, Command, Container, -    Element, Length, ProgressBar, Settings, Subscription, Text, +    button, executor, Align, Application, Button, Clipboard, Column, Command, +    Container, Element, Length, ProgressBar, Settings, Subscription, Text,  };  mod download; @@ -10,17 +10,17 @@ pub fn main() -> iced::Result {  }  #[derive(Debug)] -enum Example { -    Idle { button: button::State }, -    Downloading { progress: f32 }, -    Finished { button: button::State }, -    Errored { button: button::State }, +struct Example { +    downloads: Vec<Download>, +    last_id: usize, +    add: button::State,  }  #[derive(Debug, Clone)]  pub enum Message { -    Download, -    DownloadProgressed(download::Progress), +    Add, +    Download(usize), +    DownloadProgressed((usize, download::Progress)),  }  impl Application for Example { @@ -30,8 +30,10 @@ impl Application for Example {      fn new(_flags: ()) -> (Example, Command<Message>) {          ( -            Example::Idle { -                button: button::State::new(), +            Example { +                downloads: vec![Download::new(0)], +                last_id: 0, +                add: button::State::new(),              },              Command::none(),          ) @@ -41,104 +43,177 @@ impl Application for Example {          String::from("Download progress - Iced")      } -    fn update(&mut self, message: Message) -> Command<Message> { +    fn update( +        &mut self, +        message: Message, +        _clipboard: &mut Clipboard, +    ) -> Command<Message> {          match message { -            Message::Download => match self { -                Example::Idle { .. } -                | Example::Finished { .. } -                | Example::Errored { .. } => { -                    *self = Example::Downloading { progress: 0.0 }; +            Message::Add => { +                self.last_id = self.last_id + 1; + +                self.downloads.push(Download::new(self.last_id)); +            } +            Message::Download(index) => { +                if let Some(download) = self.downloads.get_mut(index) { +                    download.start();                  } -                _ => {} -            }, -            Message::DownloadProgressed(message) => match self { -                Example::Downloading { progress } => match message { -                    download::Progress::Started => { -                        *progress = 0.0; -                    } -                    download::Progress::Advanced(percentage) => { -                        *progress = percentage; -                    } -                    download::Progress::Finished => { -                        *self = Example::Finished { -                            button: button::State::new(), -                        } -                    } -                    download::Progress::Errored => { -                        *self = Example::Errored { -                            button: button::State::new(), -                        }; -                    } -                }, -                _ => {} -            }, +            } +            Message::DownloadProgressed((id, progress)) => { +                if let Some(download) = +                    self.downloads.iter_mut().find(|download| download.id == id) +                { +                    download.progress(progress); +                } +            }          };          Command::none()      }      fn subscription(&self) -> Subscription<Message> { -        match self { -            Example::Downloading { .. } => { -                download::file("https://speed.hetzner.de/100MB.bin") +        Subscription::batch(self.downloads.iter().map(Download::subscription)) +    } + +    fn view(&mut self) -> Element<Message> { +        let downloads = self +            .downloads +            .iter_mut() +            .fold(Column::new().spacing(20), |column, download| { +                column.push(download.view()) +            }) +            .push( +                Button::new(&mut self.add, Text::new("Add another download")) +                    .on_press(Message::Add) +                    .padding(10), +            ) +            .align_items(Align::End); + +        Container::new(downloads) +            .width(Length::Fill) +            .height(Length::Fill) +            .center_x() +            .center_y() +            .padding(20) +            .into() +    } +} + +#[derive(Debug)] +struct Download { +    id: usize, +    state: State, +} + +#[derive(Debug)] +enum State { +    Idle { button: button::State }, +    Downloading { progress: f32 }, +    Finished { button: button::State }, +    Errored { button: button::State }, +} + +impl Download { +    pub fn new(id: usize) -> Self { +        Download { +            id, +            state: State::Idle { +                button: button::State::new(), +            }, +        } +    } + +    pub fn start(&mut self) { +        match self.state { +            State::Idle { .. } +            | State::Finished { .. } +            | State::Errored { .. } => { +                self.state = State::Downloading { progress: 0.0 }; +            } +            _ => {} +        } +    } + +    pub fn progress(&mut self, new_progress: download::Progress) { +        match &mut self.state { +            State::Downloading { progress } => match new_progress { +                download::Progress::Started => { +                    *progress = 0.0; +                } +                download::Progress::Advanced(percentage) => { +                    *progress = percentage; +                } +                download::Progress::Finished => { +                    self.state = State::Finished { +                        button: button::State::new(), +                    } +                } +                download::Progress::Errored => { +                    self.state = State::Errored { +                        button: button::State::new(), +                    }; +                } +            }, +            _ => {} +        } +    } + +    pub fn subscription(&self) -> Subscription<Message> { +        match self.state { +            State::Downloading { .. } => { +                download::file(self.id, "https://speed.hetzner.de/100MB.bin?")                      .map(Message::DownloadProgressed)              }              _ => Subscription::none(),          }      } -    fn view(&mut self) -> Element<Message> { -        let current_progress = match self { -            Example::Idle { .. } => 0.0, -            Example::Downloading { progress } => *progress, -            Example::Finished { .. } => 100.0, -            Example::Errored { .. } => 0.0, +    pub fn view(&mut self) -> Element<Message> { +        let current_progress = match &self.state { +            State::Idle { .. } => 0.0, +            State::Downloading { progress } => *progress, +            State::Finished { .. } => 100.0, +            State::Errored { .. } => 0.0,          };          let progress_bar = ProgressBar::new(0.0..=100.0, current_progress); -        let control: Element<_> = match self { -            Example::Idle { button } => { +        let control: Element<_> = match &mut self.state { +            State::Idle { button } => {                  Button::new(button, Text::new("Start the download!")) -                    .on_press(Message::Download) +                    .on_press(Message::Download(self.id))                      .into()              } -            Example::Finished { button } => Column::new() +            State::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), +                        .on_press(Message::Download(self.id)),                  )                  .into(), -            Example::Downloading { .. } => { +            State::Downloading { .. } => {                  Text::new(format!("Downloading... {:.2}%", current_progress))                      .into()              } -            Example::Errored { button } => Column::new() +            State::Errored { button } => Column::new()                  .spacing(10)                  .align_items(Align::Center)                  .push(Text::new("Something went wrong :("))                  .push(                      Button::new(button, Text::new("Try again")) -                        .on_press(Message::Download), +                        .on_press(Message::Download(self.id)),                  )                  .into(),          }; -        let content = Column::new() +        Column::new()              .spacing(10)              .padding(10)              .align_items(Align::Center)              .push(progress_bar) -            .push(control); - -        Container::new(content) -            .width(Length::Fill) -            .height(Length::Fill) -            .center_x() -            .center_y() +            .push(control)              .into()      }  } | 
