From be14aca07506385a209e89cd99256744a7ec3c0f Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 24 Feb 2020 20:08:40 +0100 Subject: Make output format of `iced_wgpu` configurable --- examples/integration/src/main.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/integration/src/main.rs b/examples/integration/src/main.rs index ed36f736..4be913c1 100644 --- a/examples/integration/src/main.rs +++ b/examples/integration/src/main.rs @@ -40,11 +40,12 @@ pub fn main() { }); let surface = wgpu::Surface::create(&window); + let format = wgpu::TextureFormat::Bgra8UnormSrgb; let mut swap_chain = { let size = window.inner_size(); - SwapChain::new(&device, &surface, size.width, size.height) + SwapChain::new(&device, &surface, format, size.width, size.height) }; let mut resized = false; @@ -163,6 +164,7 @@ pub fn main() { swap_chain = SwapChain::new( &device, &surface, + format, size.width, size.height, ); -- cgit From 883a9f22e2e868fa0f85b1ac251392c12bf83696 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 26 Feb 2020 20:11:01 +0100 Subject: Add `env_logger` to `svg` example --- examples/svg/Cargo.toml | 1 + examples/svg/src/main.rs | 11 ++++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'examples') diff --git a/examples/svg/Cargo.toml b/examples/svg/Cargo.toml index d8f83ac2..161ee6a8 100644 --- a/examples/svg/Cargo.toml +++ b/examples/svg/Cargo.toml @@ -7,3 +7,4 @@ publish = false [dependencies] iced = { path = "../..", features = ["svg"] } +env_logger = "0.7" diff --git a/examples/svg/src/main.rs b/examples/svg/src/main.rs index 1fb80534..811fdfb5 100644 --- a/examples/svg/src/main.rs +++ b/examples/svg/src/main.rs @@ -1,6 +1,8 @@ use iced::{Column, Container, Element, Length, Sandbox, Settings, Svg}; pub fn main() { + env_logger::init(); + Tiger::run(Settings::default()) } @@ -22,9 +24,12 @@ impl Sandbox for Tiger { fn view(&mut self) -> Element<()> { let content = Column::new().padding(20).push( - Svg::new(format!("{}/resources/tiger.svg", env!("CARGO_MANIFEST_DIR"))) - .width(Length::Fill) - .height(Length::Fill), + Svg::new(format!( + "{}/resources/tiger.svg", + env!("CARGO_MANIFEST_DIR") + )) + .width(Length::Fill) + .height(Length::Fill), ); Container::new(content) -- cgit From 29219500b7144f31dbf50fcc64653f7d2ce806d0 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 6 Mar 2020 03:58:38 +0100 Subject: Update `reqwest` in `pokedex` example --- examples/pokedex/Cargo.toml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'examples') diff --git a/examples/pokedex/Cargo.toml b/examples/pokedex/Cargo.toml index c1e3edb5..94320086 100644 --- a/examples/pokedex/Cargo.toml +++ b/examples/pokedex/Cargo.toml @@ -7,12 +7,16 @@ publish = false [dependencies] iced = { path = "../..", features = ["image", "debug", "tokio"] } -serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -rand = { version = "0.7", features = ["wasm-bindgen"] } + +[dependencies.serde] +version = "1.0" +features = ["derive"] [dependencies.reqwest] -version = "0.10" -git = "https://github.com/hecrj/reqwest.git" -branch = "feature/wasm-deserialize-json" +version = "0.10.2" features = ["json"] + +[dependencies.rand] +version = "0.7" +features = ["wasm-bindgen"] -- cgit From b74e7e7353d69ffb54cf0c0f0574ea7abf0f3a68 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 7 Mar 2020 23:45:54 +0100 Subject: Implement `Primitive::Cached` --- examples/bezier_tool/src/main.rs | 5 ++--- examples/geometry/src/main.rs | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) (limited to 'examples') diff --git a/examples/bezier_tool/src/main.rs b/examples/bezier_tool/src/main.rs index 023eb0f7..c3fbf276 100644 --- a/examples/bezier_tool/src/main.rs +++ b/examples/bezier_tool/src/main.rs @@ -23,7 +23,6 @@ mod bezier { basic_shapes, BuffersBuilder, StrokeAttributes, StrokeOptions, StrokeTessellator, VertexBuffers, }; - use std::sync::Arc; pub struct Bezier<'a, Message> { state: &'a mut State, @@ -175,10 +174,10 @@ mod bezier { let mesh = Primitive::Mesh2D { origin: Point::new(bounds.x, bounds.y), - buffers: Arc::new(Mesh2D { + buffers: Mesh2D { vertices: buffer.vertices, indices: buffer.indices, - }), + }, }; ( diff --git a/examples/geometry/src/main.rs b/examples/geometry/src/main.rs index 795c6a71..13a687ab 100644 --- a/examples/geometry/src/main.rs +++ b/examples/geometry/src/main.rs @@ -87,7 +87,7 @@ mod rainbow { ( Primitive::Mesh2D { origin: Point::new(b.x, b.y), - buffers: std::sync::Arc::new(Mesh2D { + buffers: Mesh2D { vertices: vec![ Vertex2D { position: posn_center, @@ -136,7 +136,7 @@ mod rainbow { 0, 7, 8, // BL 0, 8, 1, // L ], - }), + }, }, MouseCursor::OutOfBounds, ) -- cgit From aac7ad3e14aaa9faf7d52fe9b91bc64cb037d576 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 9 Mar 2020 22:35:25 +0100 Subject: Update `winit` to `0.22` --- examples/integration/src/main.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'examples') diff --git a/examples/integration/src/main.rs b/examples/integration/src/main.rs index 4be913c1..2cb89ffc 100644 --- a/examples/integration/src/main.rs +++ b/examples/integration/src/main.rs @@ -10,7 +10,7 @@ use iced_wgpu::{ use iced_winit::{winit, Cache, Clipboard, MouseCursor, Size, UserInterface}; use winit::{ - event::{DeviceEvent, Event, ModifiersState, WindowEvent}, + event::{Event, ModifiersState, WindowEvent}, event_loop::{ControlFlow, EventLoop}, }; @@ -66,14 +66,11 @@ pub fn main() { *control_flow = ControlFlow::Wait; match event { - Event::DeviceEvent { - event: DeviceEvent::ModifiersChanged(new_modifiers), - .. - } => { - modifiers = new_modifiers; - } Event::WindowEvent { event, .. } => { match event { + WindowEvent::ModifiersChanged(new_modifiers) => { + modifiers = new_modifiers; + } WindowEvent::Resized(new_size) => { logical_size = new_size.to_logical(window.scale_factor()); @@ -82,6 +79,7 @@ pub fn main() { WindowEvent::CloseRequested => { *control_flow = ControlFlow::Exit; } + _ => {} } -- cgit From e1438774af809c2951c4c7446638500446c81111 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 14 Mar 2020 23:25:19 +0100 Subject: Fix `Scrollable` width consistency --- examples/styling/src/main.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'examples') diff --git a/examples/styling/src/main.rs b/examples/styling/src/main.rs index 47408624..d6f41b04 100644 --- a/examples/styling/src/main.rs +++ b/examples/styling/src/main.rs @@ -93,6 +93,7 @@ impl Sandbox for Styling { ProgressBar::new(0.0..=100.0, self.slider_value).style(self.theme); let scrollable = Scrollable::new(&mut self.scroll) + .width(Length::Fill) .height(Length::Units(100)) .style(self.theme) .push(Text::new("Scroll me!")) -- cgit From 56ba6215a25fe90a50be8feebeb74031967e92b0 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 17 Mar 2020 04:23:28 +0100 Subject: Add simple `pane_grid` example --- examples/pane_grid/Cargo.toml | 9 ++ examples/pane_grid/README.md | 18 ++++ examples/pane_grid/src/main.rs | 202 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 229 insertions(+) create mode 100644 examples/pane_grid/Cargo.toml create mode 100644 examples/pane_grid/README.md create mode 100644 examples/pane_grid/src/main.rs (limited to 'examples') diff --git a/examples/pane_grid/Cargo.toml b/examples/pane_grid/Cargo.toml new file mode 100644 index 00000000..3ed912ac --- /dev/null +++ b/examples/pane_grid/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "pane_grid" +version = "0.1.0" +authors = ["Héctor Ramón Jiménez "] +edition = "2018" +publish = false + +[dependencies] +iced = { path = "../.." } diff --git a/examples/pane_grid/README.md b/examples/pane_grid/README.md new file mode 100644 index 00000000..4d9fc5b9 --- /dev/null +++ b/examples/pane_grid/README.md @@ -0,0 +1,18 @@ +## Counter + +The classic counter example explained in the [`README`](../../README.md). + +The __[`main`]__ file contains all the code of the example. + + + +You can run it with `cargo run`: +``` +cargo run --package counter +``` + +[`main`]: src/main.rs diff --git a/examples/pane_grid/src/main.rs b/examples/pane_grid/src/main.rs new file mode 100644 index 00000000..09969630 --- /dev/null +++ b/examples/pane_grid/src/main.rs @@ -0,0 +1,202 @@ +use iced::{ + button, pane_grid, scrollable, Align, Button, Column, Container, Element, + HorizontalAlignment, Length, PaneGrid, Sandbox, Scrollable, Settings, Text, +}; + +pub fn main() { + Example::run(Settings::default()) +} + +struct Example { + panes: pane_grid::State, + panes_created: usize, +} + +#[derive(Debug, Clone, Copy)] +enum Message { + Split(pane_grid::Axis, pane_grid::Pane), + SplitFocused(pane_grid::Axis), + Dragged(pane_grid::DragEvent), + Resized(pane_grid::ResizeEvent), + Close(pane_grid::Pane), +} + +impl Sandbox for Example { + type Message = Message; + + fn new() -> Self { + let (panes, _) = pane_grid::State::new(Content::new(0)); + + Example { + panes, + panes_created: 1, + } + } + + fn title(&self) -> String { + String::from("Pane grid - Iced") + } + + fn update(&mut self, message: Message) { + match message { + Message::Split(axis, pane) => { + let _ = self.panes.split( + axis, + &pane, + Content::new(self.panes_created), + ); + + self.panes_created += 1; + } + Message::SplitFocused(axis) => { + if let Some(pane) = self.panes.active() { + let _ = self.panes.split( + axis, + &pane, + Content::new(self.panes_created), + ); + + self.panes_created += 1; + } + } + Message::Resized(pane_grid::ResizeEvent { split, ratio }) => { + self.panes.resize(&split, ratio); + } + Message::Dragged(pane_grid::DragEvent::Dropped { + pane, + target, + }) => { + self.panes.swap(&pane, &target); + } + Message::Dragged(_) => {} + Message::Close(pane) => { + let _ = self.panes.close(&pane); + } + } + } + + fn view(&mut self) -> Element { + let total_panes = self.panes.len(); + + let pane_grid = + PaneGrid::new(&mut self.panes, |pane, content, focus| { + content.view(pane, focus, total_panes) + }) + .width(Length::Fill) + .height(Length::Fill) + .spacing(5) + .on_drag(Message::Dragged) + .on_resize(Message::Resized); + + Column::new() + .width(Length::Fill) + .height(Length::Fill) + .padding(10) + .push(pane_grid) + .into() + } +} + +struct Content { + id: usize, + scroll: scrollable::State, + split_horizontally: button::State, + split_vertically: button::State, + close: button::State, +} + +impl Content { + fn new(id: usize) -> Self { + Content { + id, + scroll: scrollable::State::new(), + split_horizontally: button::State::new(), + split_vertically: button::State::new(), + close: button::State::new(), + } + } + fn view( + &mut self, + pane: pane_grid::Pane, + focus: Option, + total_panes: usize, + ) -> Element { + let Content { + id, + scroll, + split_horizontally, + split_vertically, + close, + } = self; + + let button = |state, label, message| { + Button::new( + state, + Text::new(label) + .width(Length::Fill) + .horizontal_alignment(HorizontalAlignment::Center) + .size(16), + ) + .width(Length::Fill) + .on_press(message) + }; + + let mut controls = Column::new() + .spacing(5) + .max_width(150) + .push(button( + split_horizontally, + "Split horizontally", + Message::Split(pane_grid::Axis::Horizontal, pane), + )) + .push(button( + split_vertically, + "Split vertically", + Message::Split(pane_grid::Axis::Vertical, pane), + )); + + if total_panes > 1 { + controls = + controls.push(button(close, "Close", Message::Close(pane))); + } + + let content = Scrollable::new(scroll) + .width(Length::Fill) + .spacing(10) + .align_items(Align::Center) + .push(Text::new(format!("Pane {}", id)).size(30)) + .push(controls); + + Container::new(Column::new().padding(10).push(content)) + .width(Length::Fill) + .height(Length::Fill) + .center_y() + .style(style::Pane { + is_focused: focus.is_some(), + }) + .into() + } +} + +mod style { + use iced::{container, Background, Color}; + + pub struct Pane { + pub is_focused: bool, + } + + impl container::StyleSheet for Pane { + fn style(&self) -> container::Style { + container::Style { + background: Some(Background::Color(Color::WHITE)), + border_width: 1, + border_color: if self.is_focused { + Color::from_rgb8(0x25, 0x7A, 0xFD) + } else { + Color::BLACK + }, + ..Default::default() + } + } + } +} -- cgit From 6f9cf6c70d8ef01446dae4d093c6e8ff2c7e7708 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 17 Mar 2020 06:54:25 +0100 Subject: Implement hotkey logic in `pane_grid` example --- examples/pane_grid/Cargo.toml | 1 + examples/pane_grid/src/main.rs | 42 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 41 insertions(+), 2 deletions(-) (limited to 'examples') diff --git a/examples/pane_grid/Cargo.toml b/examples/pane_grid/Cargo.toml index 3ed912ac..fb160a8d 100644 --- a/examples/pane_grid/Cargo.toml +++ b/examples/pane_grid/Cargo.toml @@ -7,3 +7,4 @@ publish = false [dependencies] iced = { path = "../.." } +iced_native = { path = "../../native" } diff --git a/examples/pane_grid/src/main.rs b/examples/pane_grid/src/main.rs index 09969630..7ab68393 100644 --- a/examples/pane_grid/src/main.rs +++ b/examples/pane_grid/src/main.rs @@ -2,6 +2,7 @@ use iced::{ button, pane_grid, scrollable, Align, Button, Column, Container, Element, HorizontalAlignment, Length, PaneGrid, Sandbox, Scrollable, Settings, Text, }; +use iced_native::input::keyboard; pub fn main() { Example::run(Settings::default()) @@ -16,9 +17,11 @@ struct Example { enum Message { Split(pane_grid::Axis, pane_grid::Pane), SplitFocused(pane_grid::Axis), + FocusAdjacent(pane_grid::Direction), Dragged(pane_grid::DragEvent), Resized(pane_grid::ResizeEvent), Close(pane_grid::Pane), + CloseFocused, } impl Sandbox for Example { @@ -59,6 +62,15 @@ impl Sandbox for Example { self.panes_created += 1; } } + Message::FocusAdjacent(direction) => { + if let Some(pane) = self.panes.active() { + if let Some(adjacent) = + self.panes.adjacent(&pane, direction) + { + self.panes.focus(&adjacent); + } + } + } Message::Resized(pane_grid::ResizeEvent { split, ratio }) => { self.panes.resize(&split, ratio); } @@ -72,6 +84,11 @@ impl Sandbox for Example { Message::Close(pane) => { let _ = self.panes.close(&pane); } + Message::CloseFocused => { + if let Some(pane) = self.panes.active() { + let _ = self.panes.close(&pane); + } + } } } @@ -86,7 +103,8 @@ impl Sandbox for Example { .height(Length::Fill) .spacing(5) .on_drag(Message::Dragged) - .on_resize(Message::Resized); + .on_resize(Message::Resized) + .on_key_press(handle_hotkey); Column::new() .width(Length::Fill) @@ -97,6 +115,26 @@ impl Sandbox for Example { } } +fn handle_hotkey(key_code: keyboard::KeyCode) -> Option { + use keyboard::KeyCode; + use pane_grid::{Axis, Direction}; + + let direction = match key_code { + KeyCode::Up => Some(Direction::Up), + KeyCode::Down => Some(Direction::Down), + KeyCode::Left => Some(Direction::Left), + KeyCode::Right => Some(Direction::Right), + _ => None, + }; + + match key_code { + KeyCode::V => Some(Message::SplitFocused(Axis::Vertical)), + KeyCode::H => Some(Message::SplitFocused(Axis::Horizontal)), + KeyCode::W => Some(Message::CloseFocused), + _ => direction.map(Message::FocusAdjacent), + } +} + struct Content { id: usize, scroll: scrollable::State, @@ -189,7 +227,7 @@ mod style { fn style(&self) -> container::Style { container::Style { background: Some(Background::Color(Color::WHITE)), - border_width: 1, + border_width: if self.is_focused { 2 } else { 1 }, border_color: if self.is_focused { Color::from_rgb8(0x25, 0x7A, 0xFD) } else { -- cgit From 1cd1582506810255394d2f9019597e9252bd8daa Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 17 Mar 2020 07:16:54 +0100 Subject: Add `modifiers` to `KeyPressEvent` in `pane_grid` --- examples/pane_grid/src/main.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'examples') diff --git a/examples/pane_grid/src/main.rs b/examples/pane_grid/src/main.rs index 7ab68393..461ffc30 100644 --- a/examples/pane_grid/src/main.rs +++ b/examples/pane_grid/src/main.rs @@ -115,11 +115,11 @@ impl Sandbox for Example { } } -fn handle_hotkey(key_code: keyboard::KeyCode) -> Option { +fn handle_hotkey(event: pane_grid::KeyPressEvent) -> Option { use keyboard::KeyCode; use pane_grid::{Axis, Direction}; - let direction = match key_code { + let direction = match event.key_code { KeyCode::Up => Some(Direction::Up), KeyCode::Down => Some(Direction::Down), KeyCode::Left => Some(Direction::Left), @@ -127,7 +127,7 @@ fn handle_hotkey(key_code: keyboard::KeyCode) -> Option { _ => None, }; - match key_code { + match event.key_code { KeyCode::V => Some(Message::SplitFocused(Axis::Vertical)), KeyCode::H => Some(Message::SplitFocused(Axis::Horizontal)), KeyCode::W => Some(Message::CloseFocused), -- cgit From 05beb878527b4d4e3141ca5ba09337d6ada858be Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 17 Mar 2020 07:28:28 +0100 Subject: Move common keyboard types to `iced_core` Also expose them in `iced` through `iced_native` and `iced_web`. --- examples/pane_grid/Cargo.toml | 1 - examples/pane_grid/src/main.rs | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) (limited to 'examples') diff --git a/examples/pane_grid/Cargo.toml b/examples/pane_grid/Cargo.toml index fb160a8d..3ed912ac 100644 --- a/examples/pane_grid/Cargo.toml +++ b/examples/pane_grid/Cargo.toml @@ -7,4 +7,3 @@ publish = false [dependencies] iced = { path = "../.." } -iced_native = { path = "../../native" } diff --git a/examples/pane_grid/src/main.rs b/examples/pane_grid/src/main.rs index 461ffc30..9e6283ab 100644 --- a/examples/pane_grid/src/main.rs +++ b/examples/pane_grid/src/main.rs @@ -1,8 +1,8 @@ use iced::{ - button, pane_grid, scrollable, Align, Button, Column, Container, Element, - HorizontalAlignment, Length, PaneGrid, Sandbox, Scrollable, Settings, Text, + button, keyboard, pane_grid, scrollable, Align, Button, Column, Container, + Element, HorizontalAlignment, Length, PaneGrid, Sandbox, Scrollable, + Settings, Text, }; -use iced_native::input::keyboard; pub fn main() { Example::run(Settings::default()) -- cgit From b8a035d2dae43f590f681686d856b8b22630141b Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 18 Mar 2020 01:27:23 +0100 Subject: Add some styling to `pane_grid` buttons --- examples/pane_grid/src/main.rs | 51 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 4 deletions(-) (limited to 'examples') diff --git a/examples/pane_grid/src/main.rs b/examples/pane_grid/src/main.rs index 9e6283ab..c5dae016 100644 --- a/examples/pane_grid/src/main.rs +++ b/examples/pane_grid/src/main.rs @@ -167,7 +167,7 @@ impl Content { close, } = self; - let button = |state, label, message| { + let button = |state, label, message, style| { Button::new( state, Text::new(label) @@ -176,7 +176,9 @@ impl Content { .size(16), ) .width(Length::Fill) + .padding(8) .on_press(message) + .style(style) }; let mut controls = Column::new() @@ -186,16 +188,22 @@ impl Content { split_horizontally, "Split horizontally", Message::Split(pane_grid::Axis::Horizontal, pane), + style::Button::Primary, )) .push(button( split_vertically, "Split vertically", Message::Split(pane_grid::Axis::Vertical, pane), + style::Button::Primary, )); if total_panes > 1 { - controls = - controls.push(button(close, "Close", Message::Close(pane))); + controls = controls.push(button( + close, + "Close", + Message::Close(pane), + style::Button::Destructive, + )); } let content = Scrollable::new(scroll) @@ -217,7 +225,7 @@ impl Content { } mod style { - use iced::{container, Background, Color}; + use iced::{button, container, Background, Color, Vector}; pub struct Pane { pub is_focused: bool, @@ -237,4 +245,39 @@ mod style { } } } + + pub enum Button { + Primary, + Destructive, + } + + impl button::StyleSheet for Button { + fn active(&self) -> button::Style { + let color = match self { + Button::Primary => Color::from_rgb8(0x25, 0x7A, 0xFD), + Button::Destructive => Color::from_rgb(0.8, 0.2, 0.2), + }; + + button::Style { + background: None, + border_color: color, + border_radius: 5, + border_width: 1, + shadow_offset: Vector::new(0.0, 0.0), + text_color: color, + ..button::Style::default() + } + } + + fn hovered(&self) -> button::Style { + let active = self.active(); + + button::Style { + background: Some(Background::Color(active.border_color)), + text_color: Color::WHITE, + border_width: 0, + ..active + } + } + } } -- cgit From 36abf7457fdc5a50b53f7e9ae63b978f07fbcda4 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 18 Mar 2020 05:53:41 +0100 Subject: Improve styling of `pane_grid` example --- examples/pane_grid/src/main.rs | 59 +++++++++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 18 deletions(-) (limited to 'examples') diff --git a/examples/pane_grid/src/main.rs b/examples/pane_grid/src/main.rs index c5dae016..dafc396c 100644 --- a/examples/pane_grid/src/main.rs +++ b/examples/pane_grid/src/main.rs @@ -101,7 +101,7 @@ impl Sandbox for Example { }) .width(Length::Fill) .height(Length::Fill) - .spacing(5) + .spacing(10) .on_drag(Message::Dragged) .on_resize(Message::Resized) .on_key_press(handle_hotkey); @@ -213,7 +213,7 @@ impl Content { .push(Text::new(format!("Pane {}", id)).size(30)) .push(controls); - Container::new(Column::new().padding(10).push(content)) + Container::new(Column::new().padding(5).push(content)) .width(Length::Fill) .height(Length::Fill) .center_y() @@ -227,6 +227,24 @@ impl Content { mod style { use iced::{button, container, Background, Color, Vector}; + const SURFACE: Color = Color::from_rgb( + 0xF2 as f32 / 255.0, + 0xF3 as f32 / 255.0, + 0xF5 as f32 / 255.0, + ); + + const ACTIVE: Color = Color::from_rgb( + 0x72 as f32 / 255.0, + 0x89 as f32 / 255.0, + 0xDA as f32 / 255.0, + ); + + const HOVERED: Color = Color::from_rgb( + 0x67 as f32 / 255.0, + 0x7B as f32 / 255.0, + 0xC4 as f32 / 255.0, + ); + pub struct Pane { pub is_focused: bool, } @@ -234,12 +252,11 @@ mod style { impl container::StyleSheet for Pane { fn style(&self) -> container::Style { container::Style { - background: Some(Background::Color(Color::WHITE)), - border_width: if self.is_focused { 2 } else { 1 }, - border_color: if self.is_focused { - Color::from_rgb8(0x25, 0x7A, 0xFD) - } else { - Color::BLACK + background: Some(Background::Color(SURFACE)), + border_width: 2, + border_color: Color { + a: if self.is_focused { 1.0 } else { 0.3 }, + ..Color::BLACK }, ..Default::default() } @@ -253,18 +270,18 @@ mod style { impl button::StyleSheet for Button { fn active(&self) -> button::Style { - let color = match self { - Button::Primary => Color::from_rgb8(0x25, 0x7A, 0xFD), - Button::Destructive => Color::from_rgb(0.8, 0.2, 0.2), + let (background, text_color) = match self { + Button::Primary => (Some(ACTIVE), Color::WHITE), + Button::Destructive => { + (None, Color::from_rgb8(0xFF, 0x47, 0x47)) + } }; button::Style { - background: None, - border_color: color, + text_color, + background: background.map(Background::Color), border_radius: 5, - border_width: 1, shadow_offset: Vector::new(0.0, 0.0), - text_color: color, ..button::Style::default() } } @@ -272,10 +289,16 @@ mod style { fn hovered(&self) -> button::Style { let active = self.active(); + let background = match self { + Button::Primary => Some(HOVERED), + Button::Destructive => Some(Color { + a: 0.2, + ..active.text_color + }), + }; + button::Style { - background: Some(Background::Color(active.border_color)), - text_color: Color::WHITE, - border_width: 0, + background: background.map(Background::Color), ..active } } -- cgit From eba2ded88a92479bee93b727b31f5f84899339cf Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 18 Mar 2020 06:35:55 +0100 Subject: Update `README` of examples --- examples/README.md | 1 + examples/pane_grid/README.md | 20 +++++++++++++++----- 2 files changed, 16 insertions(+), 5 deletions(-) (limited to 'examples') diff --git a/examples/README.md b/examples/README.md index 04399b93..a7673705 100644 --- a/examples/README.md +++ b/examples/README.md @@ -76,6 +76,7 @@ A bunch of simpler examples exist: - [`events`](events), a log of native events displayed using a conditional `Subscription`. - [`geometry`](geometry), a custom widget showcasing how to draw geometry with the `Mesh2D` primitive in [`iced_wgpu`](../wgpu). - [`integration`](integration), a demonstration of how to integrate Iced in an existing graphical application. +- [`pane_grid`](pane_grid), a grid of panes that can be split, resized, and reorganized. - [`pokedex`](pokedex), an application that displays a random Pokédex entry (sprite included!) by using the [PokéAPI]. - [`progress_bar`](progress_bar), a simple progress bar that can be filled by using a slider. - [`solar_system`](solar_system), an animated solar system drawn using the `Canvas` widget and showcasing how to compose different transforms. diff --git a/examples/pane_grid/README.md b/examples/pane_grid/README.md index 4d9fc5b9..3653fc5b 100644 --- a/examples/pane_grid/README.md +++ b/examples/pane_grid/README.md @@ -1,18 +1,28 @@ -## Counter +## Pane grid -The classic counter example explained in the [`README`](../../README.md). +A grid of panes that can be split, resized, and reorganized. + +This example showcases the `PaneGrid` widget, which features: + +* Vertical and horizontal splits +* Tracking of the last active pane +* Mouse-based resizing +* Drag and drop to reorganize panes +* Hotkey support +* Configurable modifier keys +* API to perform actions programmatically (`split`, `swap`, `resize`, etc.) The __[`main`]__ file contains all the code of the example. You can run it with `cargo run`: ``` -cargo run --package counter +cargo run --package pane_grid ``` [`main`]: src/main.rs -- cgit From fff333f89ba99f32171641f0e8d78c9cdfe291b4 Mon Sep 17 00:00:00 2001 From: Songtronix Date: Mon, 23 Mar 2020 15:54:23 +0100 Subject: Add example for download with progress tracking --- examples/download_progress/Cargo.toml | 13 +++ examples/download_progress/README.md | 15 ++++ examples/download_progress/src/downloader.rs | 99 +++++++++++++++++++++++ examples/download_progress/src/main.rs | 116 +++++++++++++++++++++++++++ 4 files changed, 243 insertions(+) create mode 100644 examples/download_progress/Cargo.toml create mode 100644 examples/download_progress/README.md create mode 100644 examples/download_progress/src/downloader.rs create mode 100644 examples/download_progress/src/main.rs (limited to 'examples') 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 "] +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`. + + + +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(url: T) -> iced::Subscription { + iced::Subscription::from_recipe(Downloader { + url: url.to_string(), + }) +} + +pub struct Downloader { + url: String, +} + +// Make sure iced can use our download stream +impl iced_native::subscription::Recipe for Downloader +where + H: std::hash::Hasher, +{ + type Output = DownloadMessage; + + fn hash(&self, state: &mut H) { + use std::hash::Hash; + std::any::TypeId::of::().hash(state); + } + + fn stream( + 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| 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) { + (Downloader::default(), Command::none()) + } + + fn title(&self) -> String { + 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::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 { + if self.enabled { + downloader::file("https://speed.hetzner.de/100MB.bin") + .map(Message::DownloadUpdate) + } else { + Subscription::none() + } + } + + fn view(&mut self) -> Element { + // Construct widgets + + let toggle_text = match self.enabled { + true => "Downloading...", + false => "Start the download!", + }; + + 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, 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() + } +} -- cgit From 30c7db3f25d12461f2dec493f92c3f3282bd264d Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 23 Mar 2020 20:34:16 +0100 Subject: Improve `download_progress` example - Use `reqwest` with `Response::chunk` to notify progress. - Turn example state into an enum --- examples/download_progress/Cargo.toml | 5 +- examples/download_progress/src/downloader.rs | 97 ++++++++++---------- examples/download_progress/src/main.rs | 129 ++++++++++++++------------- 3 files changed, 117 insertions(+), 114 deletions(-) (limited to 'examples') 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();*/ - - 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) { - (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, 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) -- cgit From b92e1f957408e3254e5fe0da389808474de6c4a9 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 23 Mar 2020 20:37:30 +0100 Subject: Rename `downloader` module to `download` --- examples/download_progress/src/download.rs | 94 ++++++++++++++++++++++++++++ examples/download_progress/src/downloader.rs | 94 ---------------------------- examples/download_progress/src/main.rs | 12 ++-- 3 files changed, 100 insertions(+), 100 deletions(-) create mode 100644 examples/download_progress/src/download.rs delete mode 100644 examples/download_progress/src/downloader.rs (limited to 'examples') diff --git a/examples/download_progress/src/download.rs b/examples/download_progress/src/download.rs new file mode 100644 index 00000000..0562f54d --- /dev/null +++ b/examples/download_progress/src/download.rs @@ -0,0 +1,94 @@ +use iced_futures::futures; + +// Just a little utility function +pub fn file(url: T) -> iced::Subscription { + iced::Subscription::from_recipe(Download { + url: url.to_string(), + }) +} + +pub struct Download { + url: String, +} + +// Make sure iced can use our download stream +impl iced_native::subscription::Recipe for Download +where + H: std::hash::Hasher, +{ + type Output = Progress; + + fn hash(&self, state: &mut H) { + use std::hash::Hash; + std::any::TypeId::of::().hash(state); + } + + fn stream( + self: Box, + _input: futures::stream::BoxStream<'static, I>, + ) -> futures::stream::BoxStream<'static, Self::Output> { + Box::pin(futures::stream::unfold( + State::Ready(self.url), + |state| async move { + match state { + State::Ready(url) => { + let response = reqwest::get(&url).await; + + match response { + Ok(response) => Some(( + Progress::Started, + State::Downloading { + total: response.content_length().unwrap(), + downloaded: 0, + response, + }, + )), + Err(_) => 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, 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/downloader.rs b/examples/download_progress/src/downloader.rs deleted file mode 100644 index 3b54341e..00000000 --- a/examples/download_progress/src/downloader.rs +++ /dev/null @@ -1,94 +0,0 @@ -use iced_futures::futures; - -// Just a little utility function -pub fn file(url: T) -> iced::Subscription { - iced::Subscription::from_recipe(Downloader { - url: url.to_string(), - }) -} - -pub struct Downloader { - url: String, -} - -// Make sure iced can use our download stream -impl iced_native::subscription::Recipe for Downloader -where - H: std::hash::Hasher, -{ - type Output = Progress; - - fn hash(&self, state: &mut H) { - use std::hash::Hash; - std::any::TypeId::of::().hash(state); - } - - fn stream( - self: Box, - _input: futures::stream::BoxStream<'static, I>, - ) -> futures::stream::BoxStream<'static, Self::Output> { - Box::pin(futures::stream::unfold( - State::Ready(self.url), - |state| async move { - match state { - State::Ready(url) => { - let response = reqwest::get(&url).await; - - match response { - Ok(response) => Some(( - Progress::Started, - State::Downloading { - total: response.content_length().unwrap(), - downloaded: 0, - response, - }, - )), - Err(_) => 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, 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 75e3bee0..f3da3d7b 100644 --- a/examples/download_progress/src/main.rs +++ b/examples/download_progress/src/main.rs @@ -3,7 +3,7 @@ use iced::{ Element, Length, ProgressBar, Settings, Subscription, Text, }; -mod downloader; +mod download; pub fn main() { Example::run(Settings::default()) @@ -18,7 +18,7 @@ enum Example { #[derive(Debug, Clone)] pub enum Message { - DownloadProgressed(downloader::Progress), + DownloadProgressed(download::Progress), Download, } @@ -49,13 +49,13 @@ impl Application for Example { }, Message::DownloadProgressed(message) => match self { Example::Downloading { progress } => match message { - downloader::Progress::Started => { + download::Progress::Started => { *progress = 0.0; } - downloader::Progress::Advanced(percentage) => { + download::Progress::Advanced(percentage) => { *progress = percentage; } - downloader::Progress::Finished => { + download::Progress::Finished => { *self = Example::Finished { button: button::State::new(), } @@ -71,7 +71,7 @@ impl Application for Example { fn subscription(&self) -> Subscription { match self { Example::Downloading { .. } => { - downloader::file("https://speed.hetzner.de/100MB.bin") + download::file("https://speed.hetzner.de/100MB.bin") .map(Message::DownloadProgressed) } _ => Subscription::none(), -- cgit From 0d719bbdf336a022c073986e1e5a91cf632a270c Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 23 Mar 2020 20:43:55 +0100 Subject: Handle errors in `download_progress` example --- examples/download_progress/src/download.rs | 38 +++++++++++++++++++++--------- examples/download_progress/src/main.rs | 16 +++++++++++++ 2 files changed, 43 insertions(+), 11 deletions(-) (limited to 'examples') diff --git a/examples/download_progress/src/download.rs b/examples/download_progress/src/download.rs index 0562f54d..96e1dc28 100644 --- a/examples/download_progress/src/download.rs +++ b/examples/download_progress/src/download.rs @@ -35,15 +35,23 @@ where let response = reqwest::get(&url).await; match response { - Ok(response) => Some(( - Progress::Started, - State::Downloading { - total: response.content_length().unwrap(), - downloaded: 0, - response, - }, - )), - Err(_) => None, + Ok(response) => { + if let Some(total) = response.content_length() { + Some(( + Progress::Started, + State::Downloading { + response, + total, + downloaded: 0, + }, + )) + } else { + Some((Progress::Errored, State::Finished)) + } + } + Err(_) => { + Some((Progress::Errored, State::Finished)) + } } } State::Downloading { @@ -67,9 +75,16 @@ where )) } Ok(None) => Some((Progress::Finished, State::Finished)), - Err(_) => None, + Err(_) => Some((Progress::Errored, State::Finished)), }, - State::Finished => None, + State::Finished => { + // We do not let the stream die, as it would start a + // new download repeatedly if the user is not careful + // in case of errors. + let _: () = iced::futures::future::pending().await; + + None + } } }, )) @@ -81,6 +96,7 @@ pub enum Progress { Started, Advanced(f32), Finished, + Errored, } pub enum State { diff --git a/examples/download_progress/src/main.rs b/examples/download_progress/src/main.rs index f3da3d7b..817a45ac 100644 --- a/examples/download_progress/src/main.rs +++ b/examples/download_progress/src/main.rs @@ -14,6 +14,7 @@ enum Example { Idle { button: button::State }, Downloading { progress: f32 }, Finished { button: button::State }, + Errored { button: button::State }, } #[derive(Debug, Clone)] @@ -60,6 +61,11 @@ impl Application for Example { button: button::State::new(), } } + download::Progress::Errored => { + *self = Example::Errored { + button: button::State::new(), + }; + } }, _ => {} }, @@ -83,6 +89,7 @@ impl Application for Example { Example::Idle { .. } => 0.0, Example::Downloading { progress } => *progress, Example::Finished { .. } => 100.0, + Example::Errored { .. } => 0.0, }; let progress_bar = ProgressBar::new(0.0..=100.0, current_progress); @@ -106,6 +113,15 @@ impl Application for Example { Text::new(format!("Downloading... {:.2}%", current_progress)) .into() } + Example::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), + ) + .into(), }; let content = Column::new() -- cgit From 8e073d10d71f80077b843c49013a8bdfeb5a7f44 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 23 Mar 2020 21:04:20 +0100 Subject: Update `README` of examples --- examples/README.md | 1 + examples/download_progress/README.md | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) (limited to 'examples') diff --git a/examples/README.md b/examples/README.md index a7673705..5aea51eb 100644 --- a/examples/README.md +++ b/examples/README.md @@ -73,6 +73,7 @@ A bunch of simpler examples exist: - [`clock`](clock), an application that uses the `Canvas` widget to draw a clock and its hands to display the current time. - [`counter`](counter), the classic counter example explained in the [`README`](../README.md). - [`custom_widget`](custom_widget), a demonstration of how to build a custom widget that draws a circle. +- [`download_progress`](download_progress), a basic application that asynchronously downloads a dummy file of 100 MB and tracks the download progress. - [`events`](events), a log of native events displayed using a conditional `Subscription`. - [`geometry`](geometry), a custom widget showcasing how to draw geometry with the `Mesh2D` primitive in [`iced_wgpu`](../wgpu). - [`integration`](integration), a demonstration of how to integrate Iced in an existing graphical application. diff --git a/examples/download_progress/README.md b/examples/download_progress/README.md index c6311163..c606c5f9 100644 --- a/examples/download_progress/README.md +++ b/examples/download_progress/README.md @@ -1,10 +1,12 @@ -## Download Progress +## Download progress -Downloading a file asynchronously with a `Subscription` while displaying the progress with a `ProgressBar`. +A basic application that asynchronously downloads a dummy file of 100 MB and tracks the download progress. + +The example implements a custom `Subscription` in the __[`download`](src/download.rs)__ module. This subscription downloads and produces messages that can be used to keep track of its progress. -- cgit From 8e0dcd212d71ff334aa590ee3b565da7b8d24713 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 23 Mar 2020 21:08:03 +0100 Subject: Fix retry button on `download_progress` example --- examples/download_progress/src/main.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'examples') diff --git a/examples/download_progress/src/main.rs b/examples/download_progress/src/main.rs index 817a45ac..6c3094f7 100644 --- a/examples/download_progress/src/main.rs +++ b/examples/download_progress/src/main.rs @@ -19,8 +19,8 @@ enum Example { #[derive(Debug, Clone)] pub enum Message { - DownloadProgressed(download::Progress), Download, + DownloadProgressed(download::Progress), } impl Application for Example { @@ -43,7 +43,9 @@ impl Application for Example { fn update(&mut self, message: Message) -> Command { match message { Message::Download => match self { - Example::Idle { .. } | Example::Finished { .. } => { + Example::Idle { .. } + | Example::Finished { .. } + | Example::Errored { .. } => { *self = Example::Downloading { progress: 0.0 }; } _ => {} -- cgit From fd7d9622e333a0a2cd5c2e8e6cc38cc09d7981e4 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 24 Mar 2020 14:07:34 +0100 Subject: Fix `Recipe::hash` in `download_progress` example --- examples/download_progress/src/download.rs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'examples') diff --git a/examples/download_progress/src/download.rs b/examples/download_progress/src/download.rs index 96e1dc28..f46a01f7 100644 --- a/examples/download_progress/src/download.rs +++ b/examples/download_progress/src/download.rs @@ -20,7 +20,9 @@ where fn hash(&self, state: &mut H) { use std::hash::Hash; + std::any::TypeId::of::().hash(state); + self.url.hash(state); } fn stream( -- cgit