diff options
Diffstat (limited to 'examples')
-rw-r--r-- | examples/component/Cargo.toml | 10 | ||||
-rw-r--r-- | examples/component/src/main.rs | 149 | ||||
-rw-r--r-- | examples/custom_widget/src/main.rs | 9 | ||||
-rw-r--r-- | examples/download_progress/Cargo.toml | 2 | ||||
-rw-r--r-- | examples/download_progress/index.html | 12 | ||||
-rw-r--r-- | examples/download_progress/src/download.rs | 103 | ||||
-rw-r--r-- | examples/download_progress/src/main.rs | 20 | ||||
-rw-r--r-- | examples/editor/src/main.rs | 44 | ||||
-rw-r--r-- | examples/markdown/src/main.rs | 18 | ||||
-rw-r--r-- | examples/multitouch/src/main.rs | 2 | ||||
-rw-r--r-- | examples/pane_grid/src/main.rs | 22 | ||||
-rw-r--r-- | examples/styling/src/main.rs | 10 | ||||
-rw-r--r-- | examples/todos/src/main.rs | 3 | ||||
-rw-r--r-- | examples/tour/src/main.rs | 10 |
14 files changed, 129 insertions, 285 deletions
diff --git a/examples/component/Cargo.toml b/examples/component/Cargo.toml deleted file mode 100644 index 83b7b8a4..00000000 --- a/examples/component/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "component" -version = "0.1.0" -authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"] -edition = "2021" -publish = false - -[dependencies] -iced.workspace = true -iced.features = ["debug", "lazy"] diff --git a/examples/component/src/main.rs b/examples/component/src/main.rs deleted file mode 100644 index a5d2e508..00000000 --- a/examples/component/src/main.rs +++ /dev/null @@ -1,149 +0,0 @@ -use iced::widget::center; -use iced::Element; - -use numeric_input::numeric_input; - -pub fn main() -> iced::Result { - iced::run("Component - Iced", Component::update, Component::view) -} - -#[derive(Default)] -struct Component { - value: Option<u32>, -} - -#[derive(Debug, Clone, Copy)] -enum Message { - NumericInputChanged(Option<u32>), -} - -impl Component { - fn update(&mut self, message: Message) { - match message { - Message::NumericInputChanged(value) => { - self.value = value; - } - } - } - - fn view(&self) -> Element<Message> { - center(numeric_input(self.value, Message::NumericInputChanged)) - .padding(20) - .into() - } -} - -mod numeric_input { - use iced::widget::{button, component, row, text, text_input, Component}; - use iced::{Center, Element, Fill, Length, Size}; - - pub struct NumericInput<Message> { - value: Option<u32>, - on_change: Box<dyn Fn(Option<u32>) -> Message>, - } - - pub fn numeric_input<Message>( - value: Option<u32>, - on_change: impl Fn(Option<u32>) -> Message + 'static, - ) -> NumericInput<Message> { - NumericInput::new(value, on_change) - } - - #[derive(Debug, Clone)] - pub enum Event { - InputChanged(String), - IncrementPressed, - DecrementPressed, - } - - impl<Message> NumericInput<Message> { - pub fn new( - value: Option<u32>, - on_change: impl Fn(Option<u32>) -> Message + 'static, - ) -> Self { - Self { - value, - on_change: Box::new(on_change), - } - } - } - - impl<Message, Theme> Component<Message, Theme> for NumericInput<Message> - where - Theme: text::Catalog + button::Catalog + text_input::Catalog + 'static, - { - type State = (); - type Event = Event; - - fn update( - &mut self, - _state: &mut Self::State, - event: Event, - ) -> Option<Message> { - match event { - Event::IncrementPressed => Some((self.on_change)(Some( - self.value.unwrap_or_default().saturating_add(1), - ))), - Event::DecrementPressed => Some((self.on_change)(Some( - self.value.unwrap_or_default().saturating_sub(1), - ))), - Event::InputChanged(value) => { - if value.is_empty() { - Some((self.on_change)(None)) - } else { - value - .parse() - .ok() - .map(Some) - .map(self.on_change.as_ref()) - } - } - } - } - - fn view(&self, _state: &Self::State) -> Element<'_, Event, Theme> { - let button = |label, on_press| { - button(text(label).width(Fill).height(Fill).center()) - .width(40) - .height(40) - .on_press(on_press) - }; - - row![ - button("-", Event::DecrementPressed), - text_input( - "Type a number", - self.value - .as_ref() - .map(u32::to_string) - .as_deref() - .unwrap_or(""), - ) - .on_input(Event::InputChanged) - .padding(10), - button("+", Event::IncrementPressed), - ] - .align_y(Center) - .spacing(10) - .into() - } - - fn size_hint(&self) -> Size<Length> { - Size { - width: Length::Fill, - height: Length::Shrink, - } - } - } - - impl<'a, Message, Theme> From<NumericInput<Message>> - for Element<'a, Message, Theme> - where - Theme: text::Catalog + button::Catalog + text_input::Catalog + 'static, - Message: 'a, - { - fn from(numeric_input: NumericInput<Message>) -> Self { - component(numeric_input) - } - } -} diff --git a/examples/custom_widget/src/main.rs b/examples/custom_widget/src/main.rs index dc3f74ac..58f3c54a 100644 --- a/examples/custom_widget/src/main.rs +++ b/examples/custom_widget/src/main.rs @@ -1,14 +1,5 @@ //! This example showcases a simple native custom widget that draws a circle. mod circle { - // For now, to implement a custom native widget you will need to add - // `iced_native` and `iced_wgpu` to your dependencies. - // - // Then, you simply need to define your widget type and implement the - // `iced_native::Widget` trait with the `iced_wgpu::Renderer`. - // - // Of course, you can choose to make the implementation renderer-agnostic, - // if you wish to, by creating your own `Renderer` trait, which could be - // implemented by `iced_wgpu` and other renderers. use iced::advanced::layout::{self, Layout}; use iced::advanced::renderer; use iced::advanced::widget::{self, Widget}; diff --git a/examples/download_progress/Cargo.toml b/examples/download_progress/Cargo.toml index f78df529..61a1b257 100644 --- a/examples/download_progress/Cargo.toml +++ b/examples/download_progress/Cargo.toml @@ -12,4 +12,4 @@ iced.features = ["tokio"] [dependencies.reqwest] version = "0.12" default-features = false -features = ["rustls-tls"] +features = ["stream", "rustls-tls"] diff --git a/examples/download_progress/index.html b/examples/download_progress/index.html new file mode 100644 index 00000000..c79e32c1 --- /dev/null +++ b/examples/download_progress/index.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<html lang="en" style="height: 100%"> +<head> + <meta charset="utf-8" content="text/html; charset=utf-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1" /> + <title>Download_Progress - Iced</title> + <base data-trunk-public-url /> +</head> +<body style="height: 100%; margin: 0"> +<link data-trunk rel="rust" href="Cargo.toml" data-wasm-opt="z" data-bin="download_progress" /> +</body> +</html> diff --git a/examples/download_progress/src/download.rs b/examples/download_progress/src/download.rs index bdf57290..a8e7b404 100644 --- a/examples/download_progress/src/download.rs +++ b/examples/download_progress/src/download.rs @@ -1,91 +1,62 @@ -use iced::futures; +use iced::futures::{SinkExt, Stream, StreamExt}; +use iced::stream::try_channel; use iced::Subscription; use std::hash::Hash; +use std::sync::Arc; // Just a little utility function pub fn file<I: 'static + Hash + Copy + Send + Sync, T: ToString>( id: I, url: T, -) -> iced::Subscription<(I, Progress)> { +) -> iced::Subscription<(I, Result<Progress, Error>)> { Subscription::run_with_id( id, - futures::stream::unfold(State::Ready(url.to_string()), move |state| { - use iced::futures::FutureExt; - - download(id, state).map(Some) - }), + download(url.to_string()).map(move |progress| (id, progress)), ) } -async fn download<I: Copy>(id: I, state: State) -> ((I, Progress), State) { - match state { - State::Ready(url) => { - let response = reqwest::get(&url).await; +fn download(url: String) -> impl Stream<Item = Result<Progress, Error>> { + try_channel(1, move |mut output| async move { + let response = reqwest::get(&url).await?; + let total = response.content_length().ok_or(Error::NoContentLength)?; - match response { - Ok(response) => { - if let Some(total) = response.content_length() { - ( - (id, Progress::Started), - State::Downloading { - response, - total, - downloaded: 0, - }, - ) - } else { - ((id, Progress::Errored), State::Finished) - } - } - Err(_) => ((id, Progress::Errored), State::Finished), - } - } - State::Downloading { - mut response, - total, - downloaded, - } => match response.chunk().await { - Ok(Some(chunk)) => { - let downloaded = downloaded + chunk.len() as u64; + let _ = output.send(Progress::Downloading { percent: 0.0 }).await; + + let mut byte_stream = response.bytes_stream(); + let mut downloaded = 0; - let percentage = (downloaded as f32 / total as f32) * 100.0; + while let Some(next_bytes) = byte_stream.next().await { + let bytes = next_bytes?; + downloaded += bytes.len(); - ( - (id, Progress::Advanced(percentage)), - State::Downloading { - response, - total, - downloaded, - }, - ) - } - Ok(None) => ((id, Progress::Finished), State::Finished), - Err(_) => ((id, Progress::Errored), State::Finished), - }, - 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. - iced::futures::future::pending().await + let _ = output + .send(Progress::Downloading { + percent: 100.0 * downloaded as f32 / total as f32, + }) + .await; } - } + + let _ = output.send(Progress::Finished).await; + + Ok(()) + }) } #[derive(Debug, Clone)] pub enum Progress { - Started, - Advanced(f32), + Downloading { percent: f32 }, Finished, - Errored, } -pub enum State { - Ready(String), - Downloading { - response: reqwest::Response, - total: u64, - downloaded: u64, - }, - Finished, +#[derive(Debug, Clone)] +pub enum Error { + RequestFailed(Arc<reqwest::Error>), + NoContentLength, +} + +impl From<reqwest::Error> for Error { + fn from(error: reqwest::Error) -> Self { + Error::RequestFailed(Arc::new(error)) + } } diff --git a/examples/download_progress/src/main.rs b/examples/download_progress/src/main.rs index 667fb448..bcc01606 100644 --- a/examples/download_progress/src/main.rs +++ b/examples/download_progress/src/main.rs @@ -23,7 +23,7 @@ struct Example { pub enum Message { Add, Download(usize), - DownloadProgressed((usize, download::Progress)), + DownloadProgressed((usize, Result<download::Progress, download::Error>)), } impl Example { @@ -114,19 +114,19 @@ impl Download { } } - pub fn progress(&mut self, new_progress: download::Progress) { + pub fn progress( + &mut self, + new_progress: Result<download::Progress, download::Error>, + ) { if let State::Downloading { progress } = &mut self.state { match new_progress { - download::Progress::Started => { - *progress = 0.0; + Ok(download::Progress::Downloading { percent }) => { + *progress = percent; } - download::Progress::Advanced(percentage) => { - *progress = percentage; - } - download::Progress::Finished => { + Ok(download::Progress::Finished) => { self.state = State::Finished; } - download::Progress::Errored => { + Err(_error) => { self.state = State::Errored; } } @@ -136,7 +136,7 @@ impl Download { pub fn subscription(&self) -> Subscription<Message> { match self.state { State::Downloading { .. } => { - download::file(self.id, "https://speed.hetzner.de/100MB.bin?") + download::file(self.id, "https://huggingface.co/mattshumer/Reflection-Llama-3.1-70B/resolve/main/model-00001-of-00162.safetensors") .map(Message::DownloadProgressed) } _ => Subscription::none(), diff --git a/examples/editor/src/main.rs b/examples/editor/src/main.rs index aa07b328..d55f9bdf 100644 --- a/examples/editor/src/main.rs +++ b/examples/editor/src/main.rs @@ -2,9 +2,9 @@ use iced::highlighter; use iced::keyboard; use iced::widget::{ self, button, column, container, horizontal_space, pick_list, row, text, - text_editor, tooltip, + text_editor, toggler, tooltip, }; -use iced::{Center, Element, Fill, Font, Subscription, Task, Theme}; +use iced::{Center, Element, Fill, Font, Task, Theme}; use std::ffi; use std::io; @@ -13,7 +13,6 @@ use std::sync::Arc; pub fn main() -> iced::Result { iced::application("Editor - Iced", Editor::update, Editor::view) - .subscription(Editor::subscription) .theme(Editor::theme) .font(include_bytes!("../fonts/icons.ttf").as_slice()) .default_font(Font::MONOSPACE) @@ -24,6 +23,7 @@ struct Editor { file: Option<PathBuf>, content: text_editor::Content, theme: highlighter::Theme, + word_wrap: bool, is_loading: bool, is_dirty: bool, } @@ -32,6 +32,7 @@ struct Editor { enum Message { ActionPerformed(text_editor::Action), ThemeSelected(highlighter::Theme), + WordWrapToggled(bool), NewFile, OpenFile, FileOpened(Result<(PathBuf, Arc<String>), Error>), @@ -46,6 +47,7 @@ impl Editor { file: None, content: text_editor::Content::new(), theme: highlighter::Theme::SolarizedDark, + word_wrap: true, is_loading: true, is_dirty: false, }, @@ -76,6 +78,11 @@ impl Editor { Task::none() } + Message::WordWrapToggled(word_wrap) => { + self.word_wrap = word_wrap; + + Task::none() + } Message::NewFile => { if !self.is_loading { self.file = None; @@ -129,15 +136,6 @@ impl Editor { } } - fn subscription(&self) -> Subscription<Message> { - keyboard::on_key_press(|key, modifiers| match key.as_ref() { - keyboard::Key::Character("s") if modifiers.command() => { - Some(Message::SaveFile) - } - _ => None, - }) - } - fn view(&self) -> Element<Message> { let controls = row![ action(new_icon(), "New file", Some(Message::NewFile)), @@ -152,6 +150,9 @@ impl Editor { self.is_dirty.then_some(Message::SaveFile) ), horizontal_space(), + toggler(self.word_wrap) + .label("Word Wrap") + .on_toggle(Message::WordWrapToggled), pick_list( highlighter::Theme::ALL, Some(self.theme), @@ -189,6 +190,11 @@ impl Editor { text_editor(&self.content) .height(Fill) .on_action(Message::ActionPerformed) + .wrapping(if self.word_wrap { + text::Wrapping::Word + } else { + text::Wrapping::None + }) .highlight( self.file .as_deref() @@ -196,7 +202,19 @@ impl Editor { .and_then(ffi::OsStr::to_str) .unwrap_or("rs"), self.theme, - ), + ) + .key_binding(|key_press| { + match key_press.key.as_ref() { + keyboard::Key::Character("s") + if key_press.modifiers.command() => + { + Some(text_editor::Binding::Custom( + Message::SaveFile, + )) + } + _ => text_editor::Binding::from_key_press(key_press), + } + }), status, ] .spacing(10) diff --git a/examples/markdown/src/main.rs b/examples/markdown/src/main.rs index eb51f985..5605478f 100644 --- a/examples/markdown/src/main.rs +++ b/examples/markdown/src/main.rs @@ -29,8 +29,7 @@ impl Markdown { ( Self { content: text_editor::Content::with_text(INITIAL_CONTENT), - items: markdown::parse(INITIAL_CONTENT, theme.palette()) - .collect(), + items: markdown::parse(INITIAL_CONTENT).collect(), theme, }, widget::focus_next(), @@ -45,11 +44,8 @@ impl Markdown { self.content.perform(action); if is_edit { - self.items = markdown::parse( - &self.content.text(), - self.theme.palette(), - ) - .collect(); + self.items = + markdown::parse(&self.content.text()).collect(); } } Message::LinkClicked(link) => { @@ -67,8 +63,12 @@ impl Markdown { .font(Font::MONOSPACE) .highlight("markdown", highlighter::Theme::Base16Ocean); - let preview = markdown(&self.items, markdown::Settings::default()) - .map(Message::LinkClicked); + let preview = markdown( + &self.items, + markdown::Settings::default(), + markdown::Style::from_palette(self.theme.palette()), + ) + .map(Message::LinkClicked); row![editor, scrollable(preview).spacing(10).height(Fill)] .spacing(10) diff --git a/examples/multitouch/src/main.rs b/examples/multitouch/src/main.rs index a0105a8a..d5e5dffa 100644 --- a/examples/multitouch/src/main.rs +++ b/examples/multitouch/src/main.rs @@ -126,7 +126,7 @@ impl canvas::Program<Message> for Multitouch { let path = builder.build(); - let color_r = (10 % zone.0) as f32 / 20.0; + let color_r = (10 % (zone.0 + 1)) as f32 / 20.0; let color_g = (10 % (zone.0 + 8)) as f32 / 20.0; let color_b = (10 % (zone.0 + 3)) as f32 / 20.0; diff --git a/examples/pane_grid/src/main.rs b/examples/pane_grid/src/main.rs index f18fc5f3..67f4d27f 100644 --- a/examples/pane_grid/src/main.rs +++ b/examples/pane_grid/src/main.rs @@ -154,11 +154,23 @@ impl Example { .spacing(5); let title_bar = pane_grid::TitleBar::new(title) - .controls(view_controls( - id, - total_panes, - pane.is_pinned, - is_maximized, + .controls(pane_grid::Controls::dynamic( + view_controls( + id, + total_panes, + pane.is_pinned, + is_maximized, + ), + button(text("X").size(14)) + .style(button::danger) + .padding(3) + .on_press_maybe( + if total_panes > 1 && !pane.is_pinned { + Some(Message::Close(id)) + } else { + None + }, + ), )) .padding(10) .style(if is_focused { diff --git a/examples/styling/src/main.rs b/examples/styling/src/main.rs index 527aaa29..534f5e32 100644 --- a/examples/styling/src/main.rs +++ b/examples/styling/src/main.rs @@ -77,12 +77,10 @@ impl Styling { let checkbox = checkbox("Check me!", self.checkbox_value) .on_toggle(Message::CheckboxToggled); - let toggler = toggler( - String::from("Toggle me!"), - self.toggler_value, - Message::TogglerToggled, - ) - .spacing(10); + let toggler = toggler(self.toggler_value) + .label("Toggle me!") + .on_toggle(Message::TogglerToggled) + .spacing(10); let content = column![ choose_theme, diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs index 86845f87..a5f7b36a 100644 --- a/examples/todos/src/main.rs +++ b/examples/todos/src/main.rs @@ -202,7 +202,8 @@ impl Todos { .on_input(Message::InputChanged) .on_submit(Message::CreateTask) .padding(15) - .size(30); + .size(30) + .align_x(Center); let controls = view_controls(tasks, *filter); let filtered_tasks = diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index ee4754e6..d8c0b29a 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -357,11 +357,11 @@ impl Tour { Self::container("Toggler") .push("A toggler is mostly used to enable or disable something.") .push( - Container::new(toggler( - "Toggle me to continue...".to_owned(), - self.toggler, - Message::TogglerChanged, - )) + Container::new( + toggler(self.toggler) + .label("Toggle me to continue...") + .on_toggle(Message::TogglerChanged), + ) .padding([0, 40]), ) } |