summaryrefslogtreecommitdiffstats
path: root/examples
diff options
context:
space:
mode:
Diffstat (limited to 'examples')
-rw-r--r--examples/bezier_tool/src/main.rs80
-rw-r--r--examples/changelog/Cargo.toml3
-rw-r--r--examples/color_palette/src/main.rs1
-rw-r--r--examples/counter/Cargo.toml5
-rw-r--r--examples/counter/src/main.rs28
-rw-r--r--examples/custom_quad/src/main.rs2
-rw-r--r--examples/custom_shader/src/scene/pipeline.rs8
-rw-r--r--examples/custom_widget/src/main.rs4
-rw-r--r--examples/download_progress/src/download.rs19
-rw-r--r--examples/download_progress/src/main.rs68
-rw-r--r--examples/game_of_life/src/main.rs52
-rw-r--r--examples/geometry/src/main.rs2
-rw-r--r--examples/gradient/src/main.rs10
-rw-r--r--examples/integration/src/scene.rs4
-rw-r--r--examples/layout/src/main.rs24
-rw-r--r--examples/loading_spinners/src/circular.rs13
-rw-r--r--examples/loading_spinners/src/linear.rs13
-rw-r--r--examples/loupe/src/main.rs2
-rw-r--r--examples/multi_window/Cargo.toml2
-rw-r--r--examples/multitouch/src/main.rs33
-rw-r--r--examples/sierpinski_triangle/src/main.rs33
-rw-r--r--examples/styling/src/main.rs54
-rw-r--r--examples/system_information/src/main.rs20
-rw-r--r--examples/toast/src/main.rs79
-rw-r--r--examples/todos/Cargo.toml5
-rw-r--r--examples/todos/snapshots/creates_a_new_task.sha2561
-rw-r--r--examples/todos/src/main.rs53
-rw-r--r--examples/tour/Cargo.toml2
28 files changed, 383 insertions, 237 deletions
diff --git a/examples/bezier_tool/src/main.rs b/examples/bezier_tool/src/main.rs
index 949bfad7..e8f0efc9 100644
--- a/examples/bezier_tool/src/main.rs
+++ b/examples/bezier_tool/src/main.rs
@@ -57,8 +57,9 @@ impl Example {
mod bezier {
use iced::mouse;
- use iced::widget::canvas::event::{self, Event};
- use iced::widget::canvas::{self, Canvas, Frame, Geometry, Path, Stroke};
+ use iced::widget::canvas::{
+ self, Canvas, Event, Frame, Geometry, Path, Stroke,
+ };
use iced::{Element, Fill, Point, Rectangle, Renderer, Theme};
#[derive(Default)]
@@ -87,7 +88,7 @@ mod bezier {
curves: &'a [Curve],
}
- impl<'a> canvas::Program<Curve> for Bezier<'a> {
+ impl canvas::Program<Curve> for Bezier<'_> {
type State = Option<Pending>;
fn update(
@@ -96,48 +97,47 @@ mod bezier {
event: Event,
bounds: Rectangle,
cursor: mouse::Cursor,
- ) -> (event::Status, Option<Curve>) {
- let Some(cursor_position) = cursor.position_in(bounds) else {
- return (event::Status::Ignored, None);
- };
+ ) -> Option<canvas::Action<Curve>> {
+ let cursor_position = cursor.position_in(bounds)?;
match event {
- Event::Mouse(mouse_event) => {
- let message = match mouse_event {
- mouse::Event::ButtonPressed(mouse::Button::Left) => {
- match *state {
- None => {
- *state = Some(Pending::One {
- from: cursor_position,
- });
-
- None
- }
- Some(Pending::One { from }) => {
- *state = Some(Pending::Two {
- from,
- to: cursor_position,
- });
-
- None
- }
- Some(Pending::Two { from, to }) => {
- *state = None;
-
- Some(Curve {
- from,
- to,
- control: cursor_position,
- })
- }
- }
+ Event::Mouse(mouse::Event::ButtonPressed(
+ mouse::Button::Left,
+ )) => Some(
+ match *state {
+ None => {
+ *state = Some(Pending::One {
+ from: cursor_position,
+ });
+
+ canvas::Action::request_redraw()
}
- _ => None,
- };
+ Some(Pending::One { from }) => {
+ *state = Some(Pending::Two {
+ from,
+ to: cursor_position,
+ });
- (event::Status::Captured, message)
+ canvas::Action::request_redraw()
+ }
+ Some(Pending::Two { from, to }) => {
+ *state = None;
+
+ canvas::Action::publish(Curve {
+ from,
+ to,
+ control: cursor_position,
+ })
+ }
+ }
+ .and_capture(),
+ ),
+ Event::Mouse(mouse::Event::CursorMoved { .. })
+ if state.is_some() =>
+ {
+ Some(canvas::Action::request_redraw())
}
- _ => (event::Status::Ignored, None),
+ _ => None,
}
}
diff --git a/examples/changelog/Cargo.toml b/examples/changelog/Cargo.toml
index eb942235..eeb7b526 100644
--- a/examples/changelog/Cargo.toml
+++ b/examples/changelog/Cargo.toml
@@ -5,6 +5,9 @@ authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2021"
publish = false
+[lints.clippy]
+large_enum_variant = "allow"
+
[dependencies]
iced.workspace = true
iced.features = ["tokio", "markdown", "highlighter", "debug"]
diff --git a/examples/color_palette/src/main.rs b/examples/color_palette/src/main.rs
index 7f21003b..1a86b168 100644
--- a/examples/color_palette/src/main.rs
+++ b/examples/color_palette/src/main.rs
@@ -89,6 +89,7 @@ impl ColorPalette {
primary: *self.theme.lower.first().unwrap(),
text: *self.theme.higher.last().unwrap(),
success: *self.theme.lower.last().unwrap(),
+ warning: *self.theme.higher.last().unwrap(),
danger: *self.theme.higher.last().unwrap(),
},
)
diff --git a/examples/counter/Cargo.toml b/examples/counter/Cargo.toml
index 22f86064..02eac329 100644
--- a/examples/counter/Cargo.toml
+++ b/examples/counter/Cargo.toml
@@ -10,4 +10,7 @@ iced.workspace = true
[target.'cfg(target_arch = "wasm32")'.dependencies]
iced.workspace = true
-iced.features = ["webgl"]
+iced.features = ["webgl", "fira-sans"]
+
+[dev-dependencies]
+iced_test.workspace = true
diff --git a/examples/counter/src/main.rs b/examples/counter/src/main.rs
index 81684c1c..18bb8cfe 100644
--- a/examples/counter/src/main.rs
+++ b/examples/counter/src/main.rs
@@ -38,3 +38,31 @@ impl Counter {
.align_x(Center)
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use iced_test::selector::text;
+ use iced_test::{simulator, Error};
+
+ #[test]
+ fn it_counts() -> Result<(), Error> {
+ let mut counter = Counter { value: 0 };
+ let mut ui = simulator(counter.view());
+
+ let _ = ui.click(text("Increment"))?;
+ let _ = ui.click(text("Increment"))?;
+ let _ = ui.click(text("Decrement"))?;
+
+ for message in ui.into_messages() {
+ counter.update(message);
+ }
+
+ assert_eq!(counter.value, 1);
+
+ let mut ui = simulator(counter.view());
+ assert!(ui.find(text("1")).is_ok(), "Counter should display 1!");
+
+ Ok(())
+ }
+}
diff --git a/examples/custom_quad/src/main.rs b/examples/custom_quad/src/main.rs
index dc425cc6..f9c07da9 100644
--- a/examples/custom_quad/src/main.rs
+++ b/examples/custom_quad/src/main.rs
@@ -75,7 +75,7 @@ mod quad {
}
}
- impl<'a, Message> From<CustomQuad> for Element<'a, Message> {
+ impl<Message> From<CustomQuad> for Element<'_, Message> {
fn from(circle: CustomQuad) -> Self {
Self::new(circle)
}
diff --git a/examples/custom_shader/src/scene/pipeline.rs b/examples/custom_shader/src/scene/pipeline.rs
index 84a3e5e2..567ab00b 100644
--- a/examples/custom_shader/src/scene/pipeline.rs
+++ b/examples/custom_shader/src/scene/pipeline.rs
@@ -241,7 +241,7 @@ impl Pipeline {
layout: Some(&layout),
vertex: wgpu::VertexState {
module: &shader,
- entry_point: "vs_main",
+ entry_point: Some("vs_main"),
buffers: &[Vertex::desc(), cube::Raw::desc()],
compilation_options:
wgpu::PipelineCompilationOptions::default(),
@@ -261,7 +261,7 @@ impl Pipeline {
},
fragment: Some(wgpu::FragmentState {
module: &shader,
- entry_point: "fs_main",
+ entry_point: Some("fs_main"),
targets: &[Some(wgpu::ColorTargetState {
format,
blend: Some(wgpu::BlendState {
@@ -493,7 +493,7 @@ impl DepthPipeline {
layout: Some(&layout),
vertex: wgpu::VertexState {
module: &shader,
- entry_point: "vs_main",
+ entry_point: Some("vs_main"),
buffers: &[],
compilation_options:
wgpu::PipelineCompilationOptions::default(),
@@ -509,7 +509,7 @@ impl DepthPipeline {
multisample: wgpu::MultisampleState::default(),
fragment: Some(wgpu::FragmentState {
module: &shader,
- entry_point: "fs_main",
+ entry_point: Some("fs_main"),
targets: &[Some(wgpu::ColorTargetState {
format,
blend: Some(wgpu::BlendState::REPLACE),
diff --git a/examples/custom_widget/src/main.rs b/examples/custom_widget/src/main.rs
index 58f3c54a..d561c2e0 100644
--- a/examples/custom_widget/src/main.rs
+++ b/examples/custom_widget/src/main.rs
@@ -62,8 +62,8 @@ mod circle {
}
}
- impl<'a, Message, Theme, Renderer> From<Circle>
- for Element<'a, Message, Theme, Renderer>
+ impl<Message, Theme, Renderer> From<Circle>
+ for Element<'_, Message, Theme, Renderer>
where
Renderer: renderer::Renderer,
{
diff --git a/examples/download_progress/src/download.rs b/examples/download_progress/src/download.rs
index a8e7b404..d63fb906 100644
--- a/examples/download_progress/src/download.rs
+++ b/examples/download_progress/src/download.rs
@@ -1,24 +1,13 @@
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, Result<Progress, Error>)> {
- Subscription::run_with_id(
- id,
- download(url.to_string()).map(move |progress| (id, progress)),
- )
-}
-
-fn download(url: String) -> impl Stream<Item = Result<Progress, Error>> {
+pub fn download(
+ url: impl AsRef<str>,
+) -> impl Stream<Item = Result<Progress, Error>> {
try_channel(1, move |mut output| async move {
- let response = reqwest::get(&url).await?;
+ let response = reqwest::get(url.as_ref()).await?;
let total = response.content_length().ok_or(Error::NoContentLength)?;
let _ = output.send(Progress::Downloading { percent: 0.0 }).await;
diff --git a/examples/download_progress/src/main.rs b/examples/download_progress/src/main.rs
index bcc01606..f4b07203 100644
--- a/examples/download_progress/src/main.rs
+++ b/examples/download_progress/src/main.rs
@@ -1,7 +1,10 @@
mod download;
+use download::download;
+
+use iced::task;
use iced::widget::{button, center, column, progress_bar, text, Column};
-use iced::{Center, Element, Right, Subscription};
+use iced::{Center, Element, Right, Task};
pub fn main() -> iced::Result {
iced::application(
@@ -9,7 +12,6 @@ pub fn main() -> iced::Result {
Example::update,
Example::view,
)
- .subscription(Example::subscription)
.run()
}
@@ -23,7 +25,7 @@ struct Example {
pub enum Message {
Add,
Download(usize),
- DownloadProgressed((usize, Result<download::Progress, download::Error>)),
+ DownloadProgressed(usize, Result<download::Progress, download::Error>),
}
impl Example {
@@ -34,32 +36,38 @@ impl Example {
}
}
- fn update(&mut self, message: Message) {
+ fn update(&mut self, message: Message) -> Task<Message> {
match message {
Message::Add => {
self.last_id += 1;
self.downloads.push(Download::new(self.last_id));
+
+ Task::none()
}
Message::Download(index) => {
- if let Some(download) = self.downloads.get_mut(index) {
- download.start();
- }
+ let Some(download) = self.downloads.get_mut(index) else {
+ return Task::none();
+ };
+
+ let task = download.start();
+
+ task.map(move |progress| {
+ Message::DownloadProgressed(index, progress)
+ })
}
- Message::DownloadProgressed((id, progress)) => {
+ Message::DownloadProgressed(id, progress) => {
if let Some(download) =
self.downloads.iter_mut().find(|download| download.id == id)
{
download.progress(progress);
}
+
+ Task::none()
}
}
}
- fn subscription(&self) -> Subscription<Message> {
- Subscription::batch(self.downloads.iter().map(Download::subscription))
- }
-
fn view(&self) -> Element<Message> {
let downloads =
Column::with_children(self.downloads.iter().map(Download::view))
@@ -90,7 +98,7 @@ struct Download {
#[derive(Debug)]
enum State {
Idle,
- Downloading { progress: f32 },
+ Downloading { progress: f32, _task: task::Handle },
Finished,
Errored,
}
@@ -103,14 +111,28 @@ impl Download {
}
}
- pub fn start(&mut self) {
+ pub fn start(
+ &mut self,
+ ) -> Task<Result<download::Progress, download::Error>> {
match self.state {
State::Idle { .. }
| State::Finished { .. }
| State::Errored { .. } => {
- self.state = State::Downloading { progress: 0.0 };
+ let (task, handle) = Task::stream(download(
+ "https://huggingface.co/\
+ mattshumer/Reflection-Llama-3.1-70B/\
+ resolve/main/model-00001-of-00162.safetensors",
+ ))
+ .abortable();
+
+ self.state = State::Downloading {
+ progress: 0.0,
+ _task: handle.abort_on_drop(),
+ };
+
+ task
}
- State::Downloading { .. } => {}
+ State::Downloading { .. } => Task::none(),
}
}
@@ -118,7 +140,7 @@ impl Download {
&mut self,
new_progress: Result<download::Progress, download::Error>,
) {
- if let State::Downloading { progress } = &mut self.state {
+ if let State::Downloading { progress, .. } = &mut self.state {
match new_progress {
Ok(download::Progress::Downloading { percent }) => {
*progress = percent;
@@ -133,20 +155,10 @@ impl Download {
}
}
- pub fn subscription(&self) -> Subscription<Message> {
- match self.state {
- State::Downloading { .. } => {
- 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(),
- }
- }
-
pub fn view(&self) -> Element<Message> {
let current_progress = match &self.state {
State::Idle { .. } => 0.0,
- State::Downloading { progress } => *progress,
+ State::Downloading { progress, .. } => *progress,
State::Finished { .. } => 100.0,
State::Errored { .. } => 0.0,
};
diff --git a/examples/game_of_life/src/main.rs b/examples/game_of_life/src/main.rs
index 9dcebecc..7a7224d5 100644
--- a/examples/game_of_life/src/main.rs
+++ b/examples/game_of_life/src/main.rs
@@ -193,8 +193,9 @@ mod grid {
use iced::mouse;
use iced::touch;
use iced::widget::canvas;
- use iced::widget::canvas::event::{self, Event};
- use iced::widget::canvas::{Cache, Canvas, Frame, Geometry, Path, Text};
+ use iced::widget::canvas::{
+ Cache, Canvas, Event, Frame, Geometry, Path, Text,
+ };
use iced::{
Color, Element, Fill, Point, Rectangle, Renderer, Size, Theme, Vector,
};
@@ -383,14 +384,12 @@ mod grid {
event: Event,
bounds: Rectangle,
cursor: mouse::Cursor,
- ) -> (event::Status, Option<Message>) {
+ ) -> Option<canvas::Action<Message>> {
if let Event::Mouse(mouse::Event::ButtonReleased(_)) = event {
*interaction = Interaction::None;
}
- let Some(cursor_position) = cursor.position_in(bounds) else {
- return (event::Status::Ignored, None);
- };
+ let cursor_position = cursor.position_in(bounds)?;
let cell = Cell::at(self.project(cursor_position, bounds.size()));
let is_populated = self.state.contains(&cell);
@@ -413,7 +412,12 @@ mod grid {
populate.or(unpopulate)
};
- (event::Status::Captured, message)
+ Some(
+ message
+ .map(canvas::Action::publish)
+ .unwrap_or(canvas::Action::request_redraw())
+ .and_capture(),
+ )
}
Event::Mouse(mouse_event) => match mouse_event {
mouse::Event::ButtonPressed(button) => {
@@ -438,7 +442,12 @@ mod grid {
_ => None,
};
- (event::Status::Captured, message)
+ Some(
+ message
+ .map(canvas::Action::publish)
+ .unwrap_or(canvas::Action::request_redraw())
+ .and_capture(),
+ )
}
mouse::Event::CursorMoved { .. } => {
let message = match *interaction {
@@ -454,12 +463,14 @@ mod grid {
Interaction::None => None,
};
- let event_status = match interaction {
- Interaction::None => event::Status::Ignored,
- _ => event::Status::Captured,
- };
+ let action = message
+ .map(canvas::Action::publish)
+ .unwrap_or(canvas::Action::request_redraw());
- (event_status, message)
+ Some(match interaction {
+ Interaction::None => action,
+ _ => action.and_capture(),
+ })
}
mouse::Event::WheelScrolled { delta } => match delta {
mouse::ScrollDelta::Lines { y, .. }
@@ -496,18 +507,21 @@ mod grid {
None
};
- (
- event::Status::Captured,
- Some(Message::Scaled(scaling, translation)),
+ Some(
+ canvas::Action::publish(Message::Scaled(
+ scaling,
+ translation,
+ ))
+ .and_capture(),
)
} else {
- (event::Status::Captured, None)
+ Some(canvas::Action::capture())
}
}
},
- _ => (event::Status::Ignored, None),
+ _ => None,
},
- _ => (event::Status::Ignored, None),
+ _ => None,
}
}
diff --git a/examples/geometry/src/main.rs b/examples/geometry/src/main.rs
index 3c7969c5..d53ae6a5 100644
--- a/examples/geometry/src/main.rs
+++ b/examples/geometry/src/main.rs
@@ -145,7 +145,7 @@ mod rainbow {
}
}
- impl<'a, Message> From<Rainbow> for Element<'a, Message> {
+ impl<Message> From<Rainbow> for Element<'_, Message> {
fn from(rainbow: Rainbow) -> Self {
Self::new(rainbow)
}
diff --git a/examples/gradient/src/main.rs b/examples/gradient/src/main.rs
index b2de069f..910ea9fc 100644
--- a/examples/gradient/src/main.rs
+++ b/examples/gradient/src/main.rs
@@ -1,5 +1,5 @@
-use iced::application;
use iced::gradient;
+use iced::theme;
use iced::widget::{
checkbox, column, container, horizontal_space, row, slider, text,
};
@@ -95,16 +95,14 @@ impl Gradient {
.into()
}
- fn style(&self, theme: &Theme) -> application::Appearance {
- use application::DefaultStyle;
-
+ fn style(&self, theme: &Theme) -> theme::Style {
if self.transparent {
- application::Appearance {
+ theme::Style {
background_color: Color::TRANSPARENT,
text_color: theme.palette().text,
}
} else {
- Theme::default_style(theme)
+ theme::default(theme)
}
}
}
diff --git a/examples/integration/src/scene.rs b/examples/integration/src/scene.rs
index 15f97e08..7ba551aa 100644
--- a/examples/integration/src/scene.rs
+++ b/examples/integration/src/scene.rs
@@ -72,13 +72,13 @@ fn build_pipeline(
layout: Some(&pipeline_layout),
vertex: wgpu::VertexState {
module: &vs_module,
- entry_point: "main",
+ entry_point: Some("main"),
buffers: &[],
compilation_options: wgpu::PipelineCompilationOptions::default(),
},
fragment: Some(wgpu::FragmentState {
module: &fs_module,
- entry_point: "main",
+ entry_point: Some("main"),
targets: &[Some(wgpu::ColorTargetState {
format: texture_format,
blend: Some(wgpu::BlendState {
diff --git a/examples/layout/src/main.rs b/examples/layout/src/main.rs
index 4280a003..e83a1f7d 100644
--- a/examples/layout/src/main.rs
+++ b/examples/layout/src/main.rs
@@ -3,7 +3,8 @@ use iced::keyboard;
use iced::mouse;
use iced::widget::{
button, canvas, center, checkbox, column, container, horizontal_rule,
- horizontal_space, pick_list, row, scrollable, text, vertical_rule,
+ horizontal_space, pick_list, pin, row, scrollable, stack, text,
+ vertical_rule,
};
use iced::{
color, Center, Element, Fill, Font, Length, Point, Rectangle, Renderer,
@@ -151,6 +152,10 @@ impl Example {
title: "Quotes",
view: quotes,
},
+ Self {
+ title: "Pinning",
+ view: pinning,
+ },
];
fn is_first(self) -> bool {
@@ -309,6 +314,23 @@ fn quotes<'a>() -> Element<'a, Message> {
.into()
}
+fn pinning<'a>() -> Element<'a, Message> {
+ column![
+ "The pin widget can be used to position a widget \
+ at some fixed coordinates inside some other widget.",
+ stack![
+ container(pin("• (50, 50)").x(50).y(50))
+ .width(500)
+ .height(500)
+ .style(container::bordered_box),
+ pin("• (300, 300)").x(300).y(300),
+ ]
+ ]
+ .align_x(Center)
+ .spacing(10)
+ .into()
+}
+
fn square<'a>(size: impl Into<Length> + Copy) -> Element<'a, Message> {
struct Square;
diff --git a/examples/loading_spinners/src/circular.rs b/examples/loading_spinners/src/circular.rs
index 9239f01f..33232fac 100644
--- a/examples/loading_spinners/src/circular.rs
+++ b/examples/loading_spinners/src/circular.rs
@@ -3,11 +3,10 @@ use iced::advanced::layout;
use iced::advanced::renderer;
use iced::advanced::widget::tree::{self, Tree};
use iced::advanced::{self, Clipboard, Layout, Shell, Widget};
-use iced::event;
use iced::mouse;
use iced::time::Instant;
use iced::widget::canvas;
-use iced::window::{self, RedrawRequest};
+use iced::window;
use iced::{
Background, Color, Element, Event, Length, Radians, Rectangle, Renderer,
Size, Vector,
@@ -89,7 +88,7 @@ where
}
}
-impl<'a, Theme> Default for Circular<'a, Theme>
+impl<Theme> Default for Circular<'_, Theme>
where
Theme: StyleSheet,
{
@@ -262,7 +261,7 @@ where
layout::atomic(limits, self.size, self.size)
}
- fn on_event(
+ fn update(
&mut self,
tree: &mut Tree,
event: Event,
@@ -272,7 +271,7 @@ where
_clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>,
_viewport: &Rectangle,
- ) -> event::Status {
+ ) {
let state = tree.state.downcast_mut::<State>();
if let Event::Window(window::Event::RedrawRequested(now)) = event {
@@ -283,10 +282,8 @@ where
);
state.cache.clear();
- shell.request_redraw(RedrawRequest::NextFrame);
+ shell.request_redraw();
}
-
- event::Status::Ignored
}
fn draw(
diff --git a/examples/loading_spinners/src/linear.rs b/examples/loading_spinners/src/linear.rs
index 164993c6..a10b64f0 100644
--- a/examples/loading_spinners/src/linear.rs
+++ b/examples/loading_spinners/src/linear.rs
@@ -3,10 +3,9 @@ use iced::advanced::layout;
use iced::advanced::renderer::{self, Quad};
use iced::advanced::widget::tree::{self, Tree};
use iced::advanced::{self, Clipboard, Layout, Shell, Widget};
-use iced::event;
use iced::mouse;
use iced::time::Instant;
-use iced::window::{self, RedrawRequest};
+use iced::window;
use iced::{Background, Color, Element, Event, Length, Rectangle, Size};
use super::easing::{self, Easing};
@@ -71,7 +70,7 @@ where
}
}
-impl<'a, Theme> Default for Linear<'a, Theme>
+impl<Theme> Default for Linear<'_, Theme>
where
Theme: StyleSheet,
{
@@ -176,7 +175,7 @@ where
layout::atomic(limits, self.width, self.height)
}
- fn on_event(
+ fn update(
&mut self,
tree: &mut Tree,
event: Event,
@@ -186,16 +185,14 @@ where
_clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>,
_viewport: &Rectangle,
- ) -> event::Status {
+ ) {
let state = tree.state.downcast_mut::<State>();
if let Event::Window(window::Event::RedrawRequested(now)) = event {
*state = state.timed_transition(self.cycle_duration, now);
- shell.request_redraw(RedrawRequest::NextFrame);
+ shell.request_redraw();
}
-
- event::Status::Ignored
}
fn draw(
diff --git a/examples/loupe/src/main.rs b/examples/loupe/src/main.rs
index 1c748d42..6b7d053a 100644
--- a/examples/loupe/src/main.rs
+++ b/examples/loupe/src/main.rs
@@ -74,7 +74,7 @@ mod loupe {
content: Element<'a, Message>,
}
- impl<'a, Message> Widget<Message, Theme, Renderer> for Loupe<'a, Message> {
+ impl<Message> Widget<Message, Theme, Renderer> for Loupe<'_, Message> {
fn tag(&self) -> widget::tree::Tag {
self.content.as_widget().tag()
}
diff --git a/examples/multi_window/Cargo.toml b/examples/multi_window/Cargo.toml
index 2e222dfb..3f89417f 100644
--- a/examples/multi_window/Cargo.toml
+++ b/examples/multi_window/Cargo.toml
@@ -6,4 +6,4 @@ edition = "2021"
publish = false
[dependencies]
-iced = { path = "../..", features = ["debug", "multi-window"] }
+iced = { path = "../..", features = ["debug"] }
diff --git a/examples/multitouch/src/main.rs b/examples/multitouch/src/main.rs
index d5e5dffa..5f4a5c90 100644
--- a/examples/multitouch/src/main.rs
+++ b/examples/multitouch/src/main.rs
@@ -3,9 +3,8 @@
//! computers like Microsoft Surface.
use iced::mouse;
use iced::touch;
-use iced::widget::canvas::event;
use iced::widget::canvas::stroke::{self, Stroke};
-use iced::widget::canvas::{self, Canvas, Geometry};
+use iced::widget::canvas::{self, Canvas, Event, Geometry};
use iced::{Color, Element, Fill, Point, Rectangle, Renderer, Theme};
use std::collections::HashMap;
@@ -56,25 +55,25 @@ impl canvas::Program<Message> for Multitouch {
fn update(
&self,
_state: &mut Self::State,
- event: event::Event,
+ event: Event,
_bounds: Rectangle,
_cursor: mouse::Cursor,
- ) -> (event::Status, Option<Message>) {
- match event {
- event::Event::Touch(touch_event) => match touch_event {
+ ) -> Option<canvas::Action<Message>> {
+ let message = match event {
+ Event::Touch(
touch::Event::FingerPressed { id, position }
- | touch::Event::FingerMoved { id, position } => (
- event::Status::Captured,
- Some(Message::FingerPressed { id, position }),
- ),
+ | touch::Event::FingerMoved { id, position },
+ ) => Some(Message::FingerPressed { id, position }),
+ Event::Touch(
touch::Event::FingerLifted { id, .. }
- | touch::Event::FingerLost { id, .. } => (
- event::Status::Captured,
- Some(Message::FingerLifted { id }),
- ),
- },
- _ => (event::Status::Ignored, None),
- }
+ | touch::Event::FingerLost { id, .. },
+ ) => Some(Message::FingerLifted { id }),
+ _ => None,
+ };
+
+ message
+ .map(canvas::Action::publish)
+ .map(canvas::Action::and_capture)
}
fn draw(
diff --git a/examples/sierpinski_triangle/src/main.rs b/examples/sierpinski_triangle/src/main.rs
index 99e7900a..d4d483f5 100644
--- a/examples/sierpinski_triangle/src/main.rs
+++ b/examples/sierpinski_triangle/src/main.rs
@@ -1,6 +1,5 @@
use iced::mouse;
-use iced::widget::canvas::event::{self, Event};
-use iced::widget::canvas::{self, Canvas, Geometry};
+use iced::widget::canvas::{self, Canvas, Event, Geometry};
use iced::widget::{column, row, slider, text};
use iced::{Center, Color, Fill, Point, Rectangle, Renderer, Size, Theme};
@@ -80,26 +79,22 @@ impl canvas::Program<Message> for SierpinskiGraph {
event: Event,
bounds: Rectangle,
cursor: mouse::Cursor,
- ) -> (event::Status, Option<Message>) {
- let Some(cursor_position) = cursor.position_in(bounds) else {
- return (event::Status::Ignored, None);
- };
+ ) -> Option<canvas::Action<Message>> {
+ let cursor_position = cursor.position_in(bounds)?;
match event {
- Event::Mouse(mouse_event) => {
- let message = match mouse_event {
- iced::mouse::Event::ButtonPressed(
- iced::mouse::Button::Left,
- ) => Some(Message::PointAdded(cursor_position)),
- iced::mouse::Event::ButtonPressed(
- iced::mouse::Button::Right,
- ) => Some(Message::PointRemoved),
- _ => None,
- };
- (event::Status::Captured, message)
- }
- _ => (event::Status::Ignored, None),
+ Event::Mouse(mouse::Event::ButtonPressed(button)) => match button {
+ mouse::Button::Left => Some(canvas::Action::publish(
+ Message::PointAdded(cursor_position),
+ )),
+ mouse::Button::Right => {
+ Some(canvas::Action::publish(Message::PointRemoved))
+ }
+ _ => None,
+ },
+ _ => None,
}
+ .map(canvas::Action::and_capture)
}
fn draw(
diff --git a/examples/styling/src/main.rs b/examples/styling/src/main.rs
index 534f5e32..594be4a7 100644
--- a/examples/styling/src/main.rs
+++ b/examples/styling/src/main.rs
@@ -1,12 +1,14 @@
+use iced::keyboard;
use iced::widget::{
button, center, checkbox, column, horizontal_rule, pick_list, progress_bar,
row, scrollable, slider, text, text_input, toggler, vertical_rule,
vertical_space,
};
-use iced::{Center, Element, Fill, Theme};
+use iced::{Center, Element, Fill, Subscription, Theme};
pub fn main() -> iced::Result {
iced::application("Styling - Iced", Styling::update, Styling::view)
+ .subscription(Styling::subscription)
.theme(Styling::theme)
.run()
}
@@ -28,6 +30,8 @@ enum Message {
SliderChanged(f32),
CheckboxToggled(bool),
TogglerToggled(bool),
+ PreviousTheme,
+ NextTheme,
}
impl Styling {
@@ -41,6 +45,23 @@ impl Styling {
Message::SliderChanged(value) => self.slider_value = value,
Message::CheckboxToggled(value) => self.checkbox_value = value,
Message::TogglerToggled(value) => self.toggler_value = value,
+ Message::PreviousTheme | Message::NextTheme => {
+ if let Some(current) = Theme::ALL
+ .iter()
+ .position(|candidate| &self.theme == candidate)
+ {
+ self.theme = if matches!(message, Message::NextTheme) {
+ Theme::ALL[(current + 1) % Theme::ALL.len()].clone()
+ } else if current == 0 {
+ Theme::ALL
+ .last()
+ .expect("Theme::ALL must not be empty")
+ .clone()
+ } else {
+ Theme::ALL[current - 1].clone()
+ };
+ }
+ }
}
}
@@ -57,9 +78,16 @@ impl Styling {
.padding(10)
.size(20);
- let button = button("Submit")
- .padding(10)
- .on_press(Message::ButtonPressed);
+ let styled_button = |label| {
+ button(text(label).width(Fill).center())
+ .padding(10)
+ .on_press(Message::ButtonPressed)
+ };
+
+ let primary = styled_button("Primary");
+ let success = styled_button("Success").style(button::success);
+ let warning = styled_button("Warning").style(button::warning);
+ let danger = styled_button("Danger").style(button::danger);
let slider =
slider(0.0..=100.0, self.slider_value, Message::SliderChanged);
@@ -85,7 +113,10 @@ impl Styling {
let content = column![
choose_theme,
horizontal_rule(38),
- row![text_input, button].spacing(10).align_y(Center),
+ text_input,
+ row![primary, success, warning, danger]
+ .spacing(10)
+ .align_y(Center),
slider,
progress_bar,
row![
@@ -104,6 +135,19 @@ impl Styling {
center(content).into()
}
+ fn subscription(&self) -> Subscription<Message> {
+ keyboard::on_key_press(|key, _modifiers| match key {
+ keyboard::Key::Named(
+ keyboard::key::Named::ArrowUp | keyboard::key::Named::ArrowLeft,
+ ) => Some(Message::PreviousTheme),
+ keyboard::Key::Named(
+ keyboard::key::Named::ArrowDown
+ | keyboard::key::Named::ArrowRight,
+ ) => Some(Message::NextTheme),
+ _ => None,
+ })
+ }
+
fn theme(&self) -> Theme {
self.theme.clone()
}
diff --git a/examples/system_information/src/main.rs b/examples/system_information/src/main.rs
index 363df590..afa657d8 100644
--- a/examples/system_information/src/main.rs
+++ b/examples/system_information/src/main.rs
@@ -7,7 +7,7 @@ pub fn main() -> iced::Result {
Example::update,
Example::view,
)
- .run()
+ .run_with(Example::new)
}
#[derive(Default)]
@@ -28,20 +28,28 @@ enum Message {
}
impl Example {
+ fn new() -> (Self, Task<Message>) {
+ (
+ Self::Loading,
+ system::fetch_information().map(Message::InformationReceived),
+ )
+ }
+
fn update(&mut self, message: Message) -> Task<Message> {
match message {
Message::Refresh => {
- *self = Self::Loading;
+ let (state, refresh) = Self::new();
+
+ *self = state;
- return system::fetch_information()
- .map(Message::InformationReceived);
+ refresh
}
Message::InformationReceived(information) => {
*self = Self::Loaded { information };
+
+ Task::none()
}
}
-
- Task::none()
}
fn view(&self) -> Element<Message> {
diff --git a/examples/toast/src/main.rs b/examples/toast/src/main.rs
index 8f6a836e..a1b5886f 100644
--- a/examples/toast/src/main.rs
+++ b/examples/toast/src/main.rs
@@ -169,7 +169,6 @@ mod toast {
use iced::advanced::renderer;
use iced::advanced::widget::{self, Operation, Tree};
use iced::advanced::{Clipboard, Shell, Widget};
- use iced::event::{self, Event};
use iced::mouse;
use iced::theme;
use iced::widget::{
@@ -177,8 +176,8 @@ mod toast {
};
use iced::window;
use iced::{
- Alignment, Center, Element, Fill, Length, Point, Rectangle, Renderer,
- Size, Theme, Vector,
+ Alignment, Center, Element, Event, Fill, Length, Point, Rectangle,
+ Renderer, Size, Theme, Vector,
};
pub const DEFAULT_TIMEOUT: u64 = 5;
@@ -282,7 +281,7 @@ mod toast {
}
}
- impl<'a, Message> Widget<Message, Theme, Renderer> for Manager<'a, Message> {
+ impl<Message> Widget<Message, Theme, Renderer> for Manager<'_, Message> {
fn size(&self) -> Size<Length> {
self.content.as_widget().size()
}
@@ -359,7 +358,7 @@ mod toast {
});
}
- fn on_event(
+ fn update(
&mut self,
state: &mut Tree,
event: Event,
@@ -369,8 +368,8 @@ mod toast {
clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>,
viewport: &Rectangle,
- ) -> event::Status {
- self.content.as_widget_mut().on_event(
+ ) {
+ self.content.as_widget_mut().update(
&mut state.children[0],
event,
layout,
@@ -379,7 +378,7 @@ mod toast {
clipboard,
shell,
viewport,
- )
+ );
}
fn draw(
@@ -465,8 +464,8 @@ mod toast {
timeout_secs: u64,
}
- impl<'a, 'b, Message> overlay::Overlay<Message, Theme, Renderer>
- for Overlay<'a, 'b, Message>
+ impl<Message> overlay::Overlay<Message, Theme, Renderer>
+ for Overlay<'_, '_, Message>
{
fn layout(
&mut self,
@@ -490,7 +489,7 @@ mod toast {
.translate(Vector::new(self.position.x, self.position.y))
}
- fn on_event(
+ fn update(
&mut self,
event: Event,
layout: Layout<'_>,
@@ -498,10 +497,8 @@ mod toast {
renderer: &Renderer,
clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>,
- ) -> event::Status {
+ ) {
if let Event::Window(window::Event::RedrawRequested(now)) = &event {
- let mut next_redraw: Option<window::RedrawRequest> = None;
-
self.instants.iter_mut().enumerate().for_each(
|(index, maybe_instant)| {
if let Some(instant) = maybe_instant.as_mut() {
@@ -512,55 +509,43 @@ mod toast {
if remaining == Duration::ZERO {
maybe_instant.take();
shell.publish((self.on_close)(index));
- next_redraw =
- Some(window::RedrawRequest::NextFrame);
} else {
- let redraw_at =
- window::RedrawRequest::At(*now + remaining);
- next_redraw = next_redraw
- .map(|redraw| redraw.min(redraw_at))
- .or(Some(redraw_at));
+ shell.request_redraw_at(*now + remaining);
}
}
},
);
-
- if let Some(redraw) = next_redraw {
- shell.request_redraw(redraw);
- }
}
let viewport = layout.bounds();
- self.toasts
+ for (((child, state), layout), instant) in self
+ .toasts
.iter_mut()
.zip(self.state.iter_mut())
.zip(layout.children())
.zip(self.instants.iter_mut())
- .map(|(((child, state), layout), instant)| {
- let mut local_messages = vec![];
- let mut local_shell = Shell::new(&mut local_messages);
-
- let status = child.as_widget_mut().on_event(
- state,
- event.clone(),
- layout,
- cursor,
- renderer,
- clipboard,
- &mut local_shell,
- &viewport,
- );
+ {
+ let mut local_messages = vec![];
+ let mut local_shell = Shell::new(&mut local_messages);
- if !local_shell.is_empty() {
- instant.take();
- }
+ child.as_widget_mut().update(
+ state,
+ event.clone(),
+ layout,
+ cursor,
+ renderer,
+ clipboard,
+ &mut local_shell,
+ &viewport,
+ );
- shell.merge(local_shell, std::convert::identity);
+ if !local_shell.is_empty() {
+ instant.take();
+ }
- status
- })
- .fold(event::Status::Ignored, event::Status::merge)
+ shell.merge(local_shell, std::convert::identity);
+ }
}
fn draw(
diff --git a/examples/todos/Cargo.toml b/examples/todos/Cargo.toml
index 0d72be86..16f4fdd2 100644
--- a/examples/todos/Cargo.toml
+++ b/examples/todos/Cargo.toml
@@ -20,12 +20,15 @@ tracing-subscriber = "0.3"
[target.'cfg(target_arch = "wasm32")'.dependencies]
iced.workspace = true
-iced.features = ["debug", "webgl"]
+iced.features = ["debug", "webgl", "fira-sans"]
uuid = { version = "1.0", features = ["js"] }
web-sys = { workspace = true, features = ["Window", "Storage"] }
wasm-timer.workspace = true
+[dev-dependencies]
+iced_test.workspace = true
+
[package.metadata.deb]
assets = [
["target/release-opt/todos", "usr/bin/iced-todos", "755"],
diff --git a/examples/todos/snapshots/creates_a_new_task.sha256 b/examples/todos/snapshots/creates_a_new_task.sha256
new file mode 100644
index 00000000..193132c5
--- /dev/null
+++ b/examples/todos/snapshots/creates_a_new_task.sha256
@@ -0,0 +1 @@
+3160686067cb7c738802009cdf2f3c5f5a5bd8c89ada70517388b7adbe64c313 \ No newline at end of file
diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs
index 25e3ead2..a5bca235 100644
--- a/examples/todos/src/main.rs
+++ b/examples/todos/src/main.rs
@@ -15,7 +15,7 @@ pub fn main() -> iced::Result {
iced::application(Todos::title, Todos::update, Todos::view)
.subscription(Todos::subscription)
- .font(include_bytes!("../fonts/icons.ttf").as_slice())
+ .font(Todos::ICON_FONT)
.window_size((500.0, 800.0))
.run_with(Todos::new)
}
@@ -48,6 +48,8 @@ enum Message {
}
impl Todos {
+ const ICON_FONT: &'static [u8] = include_bytes!("../fonts/icons.ttf");
+
fn new() -> (Self, Command<Message>) {
(
Self::Loading,
@@ -449,11 +451,10 @@ fn empty_message(message: &str) -> Element<'_, Message> {
}
// Fonts
-const ICONS: Font = Font::with_name("Iced-Todos-Icons");
fn icon(unicode: char) -> Text<'static> {
text(unicode.to_string())
- .font(ICONS)
+ .font(Font::with_name("Iced-Todos-Icons"))
.width(20)
.align_x(Center)
}
@@ -584,3 +585,49 @@ impl SavedState {
Ok(())
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ use iced::{Settings, Theme};
+ use iced_test::selector::text;
+ use iced_test::{Error, Simulator};
+
+ fn simulator(todos: &Todos) -> Simulator<Message> {
+ Simulator::with_settings(
+ Settings {
+ fonts: vec![Todos::ICON_FONT.into()],
+ ..Settings::default()
+ },
+ todos.view(),
+ )
+ }
+
+ #[test]
+ fn it_creates_a_new_task() -> Result<(), Error> {
+ let (mut todos, _command) = Todos::new();
+ let _command = todos.update(Message::Loaded(Err(LoadError::File)));
+
+ let mut ui = simulator(&todos);
+ let _input = ui.click("new-task")?;
+
+ let _ = ui.typewrite("Create the universe");
+ let _ = ui.tap_key(keyboard::key::Named::Enter);
+
+ for message in ui.into_messages() {
+ let _command = todos.update(message);
+ }
+
+ let mut ui = simulator(&todos);
+ let _ = ui.find(text("Create the universe"))?;
+
+ let snapshot = ui.snapshot(&Theme::Dark)?;
+ assert!(
+ snapshot.matches_hash("snapshots/creates_a_new_task")?,
+ "snapshots should match!"
+ );
+
+ Ok(())
+ }
+}
diff --git a/examples/tour/Cargo.toml b/examples/tour/Cargo.toml
index 9e984ad1..719d355f 100644
--- a/examples/tour/Cargo.toml
+++ b/examples/tour/Cargo.toml
@@ -14,7 +14,7 @@ tracing-subscriber = "0.3"
[target.'cfg(target_arch = "wasm32")'.dependencies]
iced.workspace = true
-iced.features = ["image", "debug", "webgl"]
+iced.features = ["image", "debug", "webgl", "fira-sans"]
console_error_panic_hook = "0.1"
console_log = "1.0"