diff --git a/Cargo.lock b/Cargo.lock index a06bff0b..5aab4f9c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -538,6 +538,26 @@ dependencies = [ "serde", ] +[[package]] +name = "bincode" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740" +dependencies = [ + "bincode_derive", + "serde", + "unty", +] + +[[package]] +name = "bincode_derive" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf95709a440f45e986983918d0e8a1f30a9b1df04918fc828670606804ac3c09" +dependencies = [ + "virtue", +] + [[package]] name = "bit-set" version = "0.8.0" @@ -730,6 +750,17 @@ dependencies = [ "wayland-client", ] +[[package]] +name = "cargo-hot-protocol" +version = "0.1.0" +source = "git+https://github.com/hecrj/cargo-hot.git?rev=b8dc518b8640928178a501257e353b73bc06cf47#b8dc518b8640928178a501257e353b73bc06cf47" +dependencies = [ + "anyhow", + "bincode 2.0.1", + "log", + "subsecond", +] + [[package]] name = "cast" version = "0.3.0" @@ -2464,7 +2495,7 @@ dependencies = [ name = "iced_beacon" version = "0.14.0-dev" dependencies = [ - "bincode", + "bincode 1.3.3", "futures", "iced_core", "log", @@ -2496,6 +2527,7 @@ dependencies = [ name = "iced_debug" version = "0.14.0-dev" dependencies = [ + "cargo-hot-protocol", "iced_beacon", "iced_core", "iced_futures", @@ -3293,6 +3325,15 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "memfd" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64" +dependencies = [ + "rustix 0.38.44", +] + [[package]] name = "memmap2" version = "0.9.5" @@ -5518,6 +5559,34 @@ dependencies = [ "rayon", ] +[[package]] +name = "subsecond" +version = "0.7.0-alpha.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43c5b40acd555d02d9a0b5bf4080dbf2cd085d5e2eb2ae7851cb14b9bf5af15c" +dependencies = [ + "js-sys", + "libc", + "libloading", + "memfd", + "memmap2", + "serde", + "subsecond-types", + "thiserror 2.0.12", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "subsecond-types" +version = "0.7.0-alpha.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bedadae58a56e137ac970c38c44bff38cee24400fef64c37d5a188a065b1ec1f" +dependencies = [ + "serde", +] + [[package]] name = "subtle" version = "2.6.1" @@ -5595,7 +5664,7 @@ version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "874dcfa363995604333cf947ae9f751ca3af4522c60886774c4963943b4746b1" dependencies = [ - "bincode", + "bincode 1.3.3", "bitflags 1.3.2", "flate2", "fnv", @@ -6281,6 +6350,12 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +[[package]] +name = "unty" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" + [[package]] name = "url" version = "2.5.4" @@ -6406,6 +6481,12 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "virtue" +version = "0.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "051eb1abcf10076295e815102942cc58f9d5e3b4560e46e53c21e8ff6f3af7b1" + [[package]] name = "visible_bounds" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index b38dfa2e..ce426151 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,10 +41,12 @@ qr_code = ["iced_widget/qr_code"] markdown = ["iced_widget/markdown"] # Enables lazy widgets lazy = ["iced_widget/lazy"] -# Enables a debug view in native platforms (press F12) +# Enables debug metrics in native platforms (press F12) debug = ["iced_winit/debug", "iced_devtools"] # Enables time-travel debugging (very experimental!) time-travel = ["debug", "iced_devtools/time-travel"] +# Enables hot reloading (very experimental!) +hot = ["debug", "iced_debug/hot"] # Enables the tester developer tool for recording and playing tests (press F12) tester = ["debug", "test", "iced_devtools/tester"] # Enables testing features (e.g. application presets) @@ -171,6 +173,7 @@ bincode = "1.3" bitflags = "2.0" bytemuck = { version = "1.0", features = ["derive"] } bytes = "1.6" +cargo-hot = { package = "cargo-hot-protocol", git = "https://github.com/hecrj/cargo-hot.git", rev = "b8dc518b8640928178a501257e353b73bc06cf47" } cosmic-text = "0.14" dark-light = "2.0" futures = { version = "0.3", default-features = false } diff --git a/core/src/layout/flex.rs b/core/src/layout/flex.rs index 2cff5bfd..f178a993 100644 --- a/core/src/layout/flex.rs +++ b/core/src/layout/flex.rs @@ -137,16 +137,24 @@ where // // We use the maximum cross length obtained in the first pass as the maximum // cross limit. + // + // We can defer the layout of any elements that have a fixed size in the main axis, + // allowing them to use the cross calculations of the next pass. if cross_compress && some_fill_cross { for (i, (child, tree)) in items.iter().zip(trees.iter_mut()).enumerate() { - let (fill_main_factor, fill_cross_factor) = { + let (main_size, cross_size) = { let size = child.as_widget().size(); - axis.pack(size.width.fill_factor(), size.height.fill_factor()) + axis.pack(size.width, size.height) }; - if fill_main_factor == 0 && fill_cross_factor != 0 { + if main_size.fill_factor() == 0 && cross_size.fill_factor() != 0 { + if let Length::Fixed(main) = main_size { + available -= main; + continue; + } + let (max_width, max_height) = axis.pack(available, cross); let child_limits = @@ -176,9 +184,9 @@ where }; // THIRD PASS - // We only have the elements that are fluid in the main axis left. + // We lay out the elements that are fluid in the main axis. // We use the remaining space to evenly allocate space based on fill factors. - for (i, (child, tree)) in items.iter().zip(trees).enumerate() { + for (i, (child, tree)) in items.iter().zip(trees.iter_mut()).enumerate() { let (fill_main_factor, fill_cross_factor) = { let size = child.as_widget().size(); @@ -224,10 +232,43 @@ where } } + // FOURTH PASS (conditional) + // We lay out any elements that were deferred in the second pass. + // These are elements that must be compressed in their cross axis and have + // a fixed length in the main axis. + if cross_compress && some_fill_cross { + for (i, (child, tree)) in items.iter().zip(trees).enumerate() { + let (main_size, cross_size) = { + let size = child.as_widget().size(); + + axis.pack(size.width, size.height) + }; + + if cross_size.fill_factor() != 0 { + let Length::Fixed(main) = main_size else { + continue; + }; + + let (max_width, max_height) = axis.pack(main, cross); + + let child_limits = + Limits::new(Size::ZERO, Size::new(max_width, max_height)); + + let layout = + child.as_widget().layout(tree, renderer, &child_limits); + let size = layout.size(); + + cross = cross.max(axis.cross(size)); + + nodes[i] = layout; + } + } + } + let pad = axis.pack(padding.left, padding.top); let mut main = pad.0; - // FOURTH PASS + // FIFTH PASS // We align all the laid out nodes in the cross axis, if needed. for (i, node) in nodes.iter_mut().enumerate() { if i > 0 { diff --git a/core/src/point.rs b/core/src/point.rs index cea57518..510f0d86 100644 --- a/core/src/point.rs +++ b/core/src/point.rs @@ -107,3 +107,13 @@ where write!(f, "Point {{ x: {}, y: {} }}", self.x, self.y) } } + +impl Point { + /// Snaps the [`Point`] to __unsigned__ integer coordinates. + pub fn snap(self) -> Point { + Point { + x: self.x.round() as u32, + y: self.y.round() as u32, + } + } +} diff --git a/core/src/rectangle.rs b/core/src/rectangle.rs index ba2b6137..8adb272f 100644 --- a/core/src/rectangle.rs +++ b/core/src/rectangle.rs @@ -244,16 +244,19 @@ impl Rectangle { /// Snaps the [`Rectangle`] to __unsigned__ integer coordinates. pub fn snap(self) -> Option> { - let width = self.width as u32; - let height = self.height as u32; + let top_left = self.position().snap(); + let bottom_right = (self.position() + Vector::from(self.size())).snap(); + + let width = bottom_right.x.checked_sub(top_left.x)?; + let height = bottom_right.y.checked_sub(top_left.y)?; if width < 1 || height < 1 { return None; } Some(Rectangle { - x: self.x as u32, - y: self.y as u32, + x: top_left.x, + y: top_left.y, width, height, }) diff --git a/core/src/shell.rs b/core/src/shell.rs index e3fcdf89..31659bfb 100644 --- a/core/src/shell.rs +++ b/core/src/shell.rs @@ -32,6 +32,7 @@ impl<'a, Message> Shell<'a, Message> { } /// Returns true if the [`Shell`] contains no published messages + #[must_use] pub fn is_empty(&self) -> bool { self.messages.is_empty() } @@ -50,11 +51,13 @@ impl<'a, Message> Shell<'a, Message> { } /// Returns the current [`event::Status`] of the [`Shell`]. + #[must_use] pub fn event_status(&self) -> event::Status { self.event_status } /// Returns whether the current event has been captured. + #[must_use] pub fn is_event_captured(&self) -> bool { self.event_status == event::Status::Captured } @@ -73,6 +76,7 @@ impl<'a, Message> Shell<'a, Message> { } /// Returns the request a redraw should happen, if any. + #[must_use] pub fn redraw_request(&self) -> window::RedrawRequest { self.redraw_request } @@ -101,16 +105,19 @@ impl<'a, Message> Shell<'a, Message> { } /// Returns the current [`InputMethod`] strategy. + #[must_use] pub fn input_method(&self) -> &InputMethod { &self.input_method } /// Returns the current [`InputMethod`] strategy. + #[must_use] pub fn input_method_mut(&mut self) -> &mut InputMethod { &mut self.input_method } /// Returns whether the current layout is invalid or not. + #[must_use] pub fn is_layout_invalid(&self) -> bool { self.is_layout_invalid } @@ -134,6 +141,7 @@ impl<'a, Message> Shell<'a, Message> { /// Returns whether the widgets of the current application have been /// invalidated. + #[must_use] pub fn are_widgets_invalid(&self) -> bool { self.are_widgets_invalid } diff --git a/core/src/window/position.rs b/core/src/window/position.rs index 1c8e86b6..2b31a5e2 100644 --- a/core/src/window/position.rs +++ b/core/src/window/position.rs @@ -1,7 +1,7 @@ use crate::{Point, Size}; /// The position of a window in a given screen. -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, Copy)] pub enum Position { /// The platform-specific default position for a new window. Default, diff --git a/debug/Cargo.toml b/debug/Cargo.toml index f6c7c843..1c0abda3 100644 --- a/debug/Cargo.toml +++ b/debug/Cargo.toml @@ -12,6 +12,7 @@ keywords.workspace = true [features] enable = ["dep:iced_beacon"] +hot = ["enable", "dep:cargo-hot"] [dependencies] iced_core.workspace = true @@ -21,3 +22,6 @@ log.workspace = true [target.'cfg(not(target_arch = "wasm32"))'.dependencies] iced_beacon.workspace = true iced_beacon.optional = true + +cargo-hot.workspace = true +cargo-hot.optional = true diff --git a/debug/src/lib.rs b/debug/src/lib.rs index 17463f9a..f79dde55 100644 --- a/debug/src/lib.rs +++ b/debug/src/lib.rs @@ -39,6 +39,7 @@ pub fn disable() { pub fn init(metadata: Metadata) { internal::init(metadata); + hot::init(); } pub fn quit() -> bool { @@ -113,6 +114,18 @@ pub fn commands() -> Subscription { internal::commands() } +pub fn hot(f: impl FnOnce() -> O) -> O { + hot::call(f) +} + +pub fn on_hotpatch(f: impl Fn() + Send + Sync + 'static) { + hot::on_hotpatch(f) +} + +pub fn is_stale() -> bool { + hot::is_stale() +} + #[cfg(all(feature = "enable", not(target_arch = "wasm32")))] mod internal { use crate::core::theme; @@ -399,3 +412,83 @@ mod internal { pub fn finish(self) {} } } + +#[cfg(feature = "hot")] +mod hot { + use std::collections::BTreeSet; + use std::sync::atomic::{self, AtomicBool}; + use std::sync::{Arc, Mutex, OnceLock}; + + static IS_STALE: AtomicBool = AtomicBool::new(false); + + static HOT_FUNCTIONS_PENDING: Mutex> = + Mutex::new(BTreeSet::new()); + + static HOT_FUNCTIONS: OnceLock> = OnceLock::new(); + + pub fn init() { + cargo_hot::connect(); + + cargo_hot::subsecond::register_handler(Arc::new(|| { + if HOT_FUNCTIONS.get().is_none() { + HOT_FUNCTIONS + .set(std::mem::take( + &mut HOT_FUNCTIONS_PENDING + .lock() + .expect("Lock hot functions"), + )) + .expect("Set hot functions"); + } + + IS_STALE.store(false, atomic::Ordering::Relaxed); + })); + } + + pub fn call(f: impl FnOnce() -> O) -> O { + let mut f = Some(f); + + // The `move` here is important. Hotpatching will not work + // otherwise. + let mut f = cargo_hot::subsecond::HotFn::current(move || { + f.take().expect("Hot function is stale")() + }); + + let address = f.ptr_address().0; + + if let Some(hot_functions) = HOT_FUNCTIONS.get() { + if hot_functions.contains(&address) { + IS_STALE.store(true, atomic::Ordering::Relaxed); + } + } else { + let _ = HOT_FUNCTIONS_PENDING + .lock() + .expect("Lock hot functions") + .insert(address); + } + + f.call(()) + } + + pub fn on_hotpatch(f: impl Fn() + Send + Sync + 'static) { + cargo_hot::subsecond::register_handler(Arc::new(f)); + } + + pub fn is_stale() -> bool { + IS_STALE.load(atomic::Ordering::Relaxed) + } +} + +#[cfg(not(feature = "hot"))] +mod hot { + pub fn init() {} + + pub fn call(f: impl FnOnce() -> O) -> O { + f() + } + + pub fn on_hotpatch(_f: impl Fn() + Send + Sync + 'static) {} + + pub fn is_stale() -> bool { + false + } +} diff --git a/devtools/src/lib.rs b/devtools/src/lib.rs index d7d7e074..4a50580d 100644 --- a/devtools/src/lib.rs +++ b/devtools/src/lib.rs @@ -397,13 +397,16 @@ where } .map(|mode| Element::from(mode).map(Event::Message)); - let notification = self.show_notification.then(|| { - bottom_right(opaque( - container(text("Press F12 to open developer tools")) - .padding(10) - .style(container::dark), - )) - }); + let notification = self + .show_notification + .then(|| text("Press F12 to open debug metrics")) + .or_else(|| { + debug::is_stale().then(|| { + text( + "Types have changed. Restart to re-enable hotpatching.", + ) + }) + }); let sidebar = if let Mode::Open { tester } = &self.mode { let title = monospace("Developer Tools"); @@ -429,7 +432,13 @@ where stack![content] .height(Fill) .push_maybe(setup.map(opaque)) - .push_maybe(notification), + .push_maybe(notification.map(|notification| { + bottom_right(opaque( + container(notification) + .padding(10) + .style(container::dark), + )) + })), ) .into() } diff --git a/examples/arc/src/main.rs b/examples/arc/src/main.rs index 3e80a5db..ce461ab6 100644 --- a/examples/arc/src/main.rs +++ b/examples/arc/src/main.rs @@ -36,7 +36,7 @@ impl Arc { self.cache.clear(); } - fn view(&self) -> Element { + fn view(&self) -> Element<'_, Message> { Canvas::new(self).width(Fill).height(Fill).into() } diff --git a/examples/bezier_tool/src/main.rs b/examples/bezier_tool/src/main.rs index 113577e6..8b040755 100644 --- a/examples/bezier_tool/src/main.rs +++ b/examples/bezier_tool/src/main.rs @@ -34,7 +34,7 @@ impl Example { } } - fn view(&self) -> Element { + fn view(&self) -> Element<'_, Message> { container(hover( self.bezier.view(&self.curves).map(Message::AddCurve), if self.curves.is_empty() { diff --git a/examples/changelog/src/main.rs b/examples/changelog/src/main.rs index fbb0cfbf..ba254c76 100644 --- a/examples/changelog/src/main.rs +++ b/examples/changelog/src/main.rs @@ -217,7 +217,7 @@ impl Generator { } } - fn view(&self) -> Element { + fn view(&self) -> Element<'_, Message> { match self { Self::Loading => center("Loading...").into(), Self::Done => center( diff --git a/examples/checkbox/src/main.rs b/examples/checkbox/src/main.rs index 563b721d..99c859a7 100644 --- a/examples/checkbox/src/main.rs +++ b/examples/checkbox/src/main.rs @@ -38,7 +38,7 @@ impl Example { } } - fn view(&self) -> Element { + fn view(&self) -> Element<'_, Message> { let default_checkbox = checkbox("Default", self.default) .on_toggle(Message::DefaultToggled); diff --git a/examples/clock/src/main.rs b/examples/clock/src/main.rs index 89855bc2..a1050a76 100644 --- a/examples/clock/src/main.rs +++ b/examples/clock/src/main.rs @@ -48,7 +48,7 @@ impl Clock { } } - fn view(&self) -> Element { + fn view(&self) -> Element<'_, Message> { let canvas = canvas(self as &Self).width(Fill).height(Fill); container(canvas).padding(20).into() @@ -166,7 +166,7 @@ impl canvas::Program for Clock { let y = radius * angle.0.sin(); frame.fill_text(canvas::Text { - content: format!("{}", hour), + content: format!("{hour}"), size: (radius / 5.0).into(), position: Point::new(x * 0.82, y * 0.82), color: palette.secondary.strong.text, diff --git a/examples/color_palette/src/main.rs b/examples/color_palette/src/main.rs index 10e72d3b..e5554dee 100644 --- a/examples/color_palette/src/main.rs +++ b/examples/color_palette/src/main.rs @@ -56,7 +56,7 @@ impl ColorPalette { self.theme = Theme::new(to_color(srgb)); } - fn view(&self) -> Element { + fn view(&self) -> Element<'_, Message> { let base = self.theme.base; let srgb = to_rgb(base); @@ -149,7 +149,7 @@ impl Theme { .chain(self.higher.iter()) } - pub fn view(&self) -> Element { + pub fn view(&self) -> Element<'_, Message> { Canvas::new(self).width(Fill).height(Fill).into() } @@ -296,7 +296,7 @@ trait ColorSpace: Sized { } impl ColorPicker { - fn view(&self, color: C) -> Element { + fn view(&self, color: C) -> Element<'_, C> { let [c1, c2, c3] = color.components(); let [cr1, cr2, cr3] = C::COMPONENT_RANGES; diff --git a/examples/combo_box/src/main.rs b/examples/combo_box/src/main.rs index 5124c8ef..a7a68590 100644 --- a/examples/combo_box/src/main.rs +++ b/examples/combo_box/src/main.rs @@ -47,7 +47,7 @@ impl Example { } } - fn view(&self) -> Element { + fn view(&self) -> Element<'_, Message> { let combo_box = combo_box( &self.languages, "Type a language...", diff --git a/examples/counter/src/main.rs b/examples/counter/src/main.rs index 1bb17af5..5027afd7 100644 --- a/examples/counter/src/main.rs +++ b/examples/counter/src/main.rs @@ -28,7 +28,7 @@ impl Counter { } } - fn view(&self) -> Column { + fn view(&self) -> Column<'_, Message> { column![ button("Increment").on_press(Message::Increment), text(self.value).size(50), diff --git a/examples/custom_quad/src/main.rs b/examples/custom_quad/src/main.rs index b23b8e96..4ea58511 100644 --- a/examples/custom_quad/src/main.rs +++ b/examples/custom_quad/src/main.rs @@ -74,7 +74,7 @@ impl Example { } } - fn view(&self) -> Element { + fn view(&self) -> Element<'_, Message> { let border::Radius { top_left, top_right, diff --git a/examples/custom_widget/src/main.rs b/examples/custom_widget/src/main.rs index db4255be..e5db6139 100644 --- a/examples/custom_widget/src/main.rs +++ b/examples/custom_widget/src/main.rs @@ -103,7 +103,7 @@ impl Example { } } - fn view(&self) -> Element { + fn view(&self) -> Element<'_, Message> { let content = column![ circle(self.radius), text!("Radius: {:.2}", self.radius), diff --git a/examples/download_progress/src/main.rs b/examples/download_progress/src/main.rs index c27c6204..53ee2823 100644 --- a/examples/download_progress/src/main.rs +++ b/examples/download_progress/src/main.rs @@ -61,7 +61,7 @@ impl Example { } } - fn view(&self) -> Element { + fn view(&self) -> Element<'_, Message> { let downloads = Column::with_children(self.downloads.iter().map(Download::view)) .push( @@ -152,7 +152,7 @@ impl Download { } } - pub fn view(&self) -> Element { + pub fn view(&self) -> Element<'_, Message> { let current_progress = match &self.state { State::Idle => 0.0, State::Downloading { progress, .. } => *progress, diff --git a/examples/editor/src/main.rs b/examples/editor/src/main.rs index af75f581..abad1abe 100644 --- a/examples/editor/src/main.rs +++ b/examples/editor/src/main.rs @@ -144,7 +144,7 @@ impl Editor { } } - fn view(&self) -> Element { + fn view(&self) -> Element<'_, Message> { let controls = row![ action(new_icon(), "New file", Some(Message::NewFile)), action( diff --git a/examples/events/src/main.rs b/examples/events/src/main.rs index c38b22b9..57b36f7c 100644 --- a/examples/events/src/main.rs +++ b/examples/events/src/main.rs @@ -55,7 +55,7 @@ impl Events { event::listen().map(Message::EventOccurred) } - fn view(&self) -> Element { + fn view(&self) -> Element<'_, Message> { let events = Column::with_children( self.last .iter() diff --git a/examples/exit/src/main.rs b/examples/exit/src/main.rs index 6d1b5c52..767ff93f 100644 --- a/examples/exit/src/main.rs +++ b/examples/exit/src/main.rs @@ -29,7 +29,7 @@ impl Exit { } } - fn view(&self) -> Element { + fn view(&self) -> Element<'_, Message> { let content = if self.show_confirm { column![ "Are you sure you want to exit?", diff --git a/examples/ferris/src/main.rs b/examples/ferris/src/main.rs index c28b04c6..add266b5 100644 --- a/examples/ferris/src/main.rs +++ b/examples/ferris/src/main.rs @@ -91,7 +91,7 @@ impl Image { } } - fn view(&self) -> Element { + fn view(&self) -> Element<'_, Message> { let i_am_ferris = column![ "Hello!", Element::from( diff --git a/examples/game_of_life/src/main.rs b/examples/game_of_life/src/main.rs index 8860759e..e6cb4dab 100644 --- a/examples/game_of_life/src/main.rs +++ b/examples/game_of_life/src/main.rs @@ -111,7 +111,7 @@ impl GameOfLife { } } - fn view(&self) -> Element { + fn view(&self) -> Element<'_, Message> { let version = self.version; let selected_speed = self.next_speed.unwrap_or(self.speed); let controls = view_controls( @@ -320,7 +320,7 @@ mod grid { } } - pub fn view(&self) -> Element { + pub fn view(&self) -> Element<'_, Message> { Canvas::new(self).width(Fill).height(Fill).into() } diff --git a/examples/gradient/src/main.rs b/examples/gradient/src/main.rs index 93c40e05..8c0c90d3 100644 --- a/examples/gradient/src/main.rs +++ b/examples/gradient/src/main.rs @@ -51,7 +51,7 @@ impl Gradient { } } - fn view(&self) -> Element { + fn view(&self) -> Element<'_, Message> { let Self { start, end, diff --git a/examples/integration/src/controls.rs b/examples/integration/src/controls.rs index 72db4c24..1f2f3805 100644 --- a/examples/integration/src/controls.rs +++ b/examples/integration/src/controls.rs @@ -38,7 +38,7 @@ impl Controls { } } - pub fn view(&self) -> Element { + pub fn view(&self) -> Element<'_, Message, Theme, Renderer> { let background_color = self.background_color; let sliders = row![ diff --git a/examples/layout/src/main.rs b/examples/layout/src/main.rs index 768180a8..f477c406 100644 --- a/examples/layout/src/main.rs +++ b/examples/layout/src/main.rs @@ -68,7 +68,7 @@ impl Layout { }) } - fn view(&self) -> Element { + fn view(&self) -> Element<'_, Message> { let header = row![ text(self.example.title).size(20).font(Font::MONOSPACE), horizontal_space(), @@ -121,7 +121,7 @@ impl Layout { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, Eq)] struct Example { title: &'static str, view: fn() -> Element<'static, Message>, @@ -190,7 +190,7 @@ impl Example { Self::LIST.get(index + 1).copied().unwrap_or(self) } - fn view(&self) -> Element { + fn view(&self) -> Element<'_, Message> { (self.view)() } } @@ -201,6 +201,12 @@ impl Default for Example { } } +impl PartialEq for Example { + fn eq(&self, other: &Self) -> bool { + self.title == other.title + } +} + fn centered<'a>() -> Element<'a, Message> { center(text("I am centered!").size(50)).into() } diff --git a/examples/lazy/src/main.rs b/examples/lazy/src/main.rs index 3d2eb61c..585b8c0a 100644 --- a/examples/lazy/src/main.rs +++ b/examples/lazy/src/main.rs @@ -154,7 +154,7 @@ impl App { } } - fn view(&self) -> Element { + fn view(&self) -> Element<'_, Message> { let options = lazy(self.version, |_| { let mut items: Vec<_> = self.items.iter().cloned().collect(); diff --git a/examples/loading_spinners/src/main.rs b/examples/loading_spinners/src/main.rs index 0cec5095..122b30fd 100644 --- a/examples/loading_spinners/src/main.rs +++ b/examples/loading_spinners/src/main.rs @@ -37,7 +37,7 @@ impl LoadingSpinners { } } - fn view(&self) -> Element { + fn view(&self) -> Element<'_, Message> { let column = [ &easing::EMPHASIZED, &easing::EMPHASIZED_DECELERATE, diff --git a/examples/loupe/src/main.rs b/examples/loupe/src/main.rs index 114f481d..c141e758 100644 --- a/examples/loupe/src/main.rs +++ b/examples/loupe/src/main.rs @@ -30,7 +30,7 @@ impl Loupe { } } - fn view(&self) -> Element { + fn view(&self) -> Element<'_, Message> { center(loupe( 3.0, column![ diff --git a/examples/markdown/overview.md b/examples/markdown/overview.md index 66336c5b..2bb2d931 100644 --- a/examples/markdown/overview.md +++ b/examples/markdown/overview.md @@ -34,7 +34,7 @@ Now, let's show the actual counter by putting it all together in our __view logi use iced::widget::{button, column, text, Column}; impl Counter { - pub fn view(&self) -> Column { + pub fn view(&self) -> Column<'_, Message> { // We use a column: a simple vertical layout column![ // The increment button. We tell it to produce an diff --git a/examples/markdown/src/main.rs b/examples/markdown/src/main.rs index 12d52dff..98d19595 100644 --- a/examples/markdown/src/main.rs +++ b/examples/markdown/src/main.rs @@ -174,7 +174,7 @@ impl Markdown { } } - fn view(&self) -> Element { + fn view(&self) -> Element<'_, Message> { let editor = text_editor(&self.raw) .placeholder("Type your Markdown here...") .on_action(Message::Edit) diff --git a/examples/modal/src/main.rs b/examples/modal/src/main.rs index 236f32f8..b4355004 100644 --- a/examples/modal/src/main.rs +++ b/examples/modal/src/main.rs @@ -92,7 +92,7 @@ impl App { } } - fn view(&self) -> Element { + fn view(&self) -> Element<'_, Message> { let content = container( column![ row![text("Top Left"), horizontal_space(), text("Top Right")] diff --git a/examples/multi_window/src/main.rs b/examples/multi_window/src/main.rs index 4470c834..77be505b 100644 --- a/examples/multi_window/src/main.rs +++ b/examples/multi_window/src/main.rs @@ -130,7 +130,7 @@ impl Example { } } - fn view(&self, window_id: window::Id) -> Element { + fn view(&self, window_id: window::Id) -> Element<'_, Message> { if let Some(window) = self.windows.get(&window_id) { center(window.view(window_id)).into() } else { @@ -161,14 +161,14 @@ impl Example { impl Window { fn new(count: usize) -> Self { Self { - title: format!("Window_{}", count), + title: format!("Window_{count}"), scale_input: "1.0".to_string(), current_scale: 1.0, theme: Theme::ALL[count % Theme::ALL.len()].clone(), } } - fn view(&self, id: window::Id) -> Element { + fn view(&self, id: window::Id) -> Element<'_, Message> { let scale_input = column![ text("Window scale factor:"), text_input("Window Scale", &self.scale_input) diff --git a/examples/multitouch/src/main.rs b/examples/multitouch/src/main.rs index 0db1c09a..6ea20511 100644 --- a/examples/multitouch/src/main.rs +++ b/examples/multitouch/src/main.rs @@ -43,7 +43,7 @@ impl Multitouch { } } - fn view(&self) -> Element { + fn view(&self) -> Element<'_, Message> { Canvas::new(self).width(Fill).height(Fill).into() } } diff --git a/examples/pane_grid/src/main.rs b/examples/pane_grid/src/main.rs index aad55122..c44fc1f1 100644 --- a/examples/pane_grid/src/main.rs +++ b/examples/pane_grid/src/main.rs @@ -129,7 +129,7 @@ impl Example { }) } - fn view(&self) -> Element { + fn view(&self) -> Element<'_, Message> { let focus = self.focus; let total_panes = self.panes.len(); diff --git a/examples/pick_list/src/main.rs b/examples/pick_list/src/main.rs index 33aa6cda..1023a30a 100644 --- a/examples/pick_list/src/main.rs +++ b/examples/pick_list/src/main.rs @@ -24,7 +24,7 @@ impl Example { } } - fn view(&self) -> Element { + fn view(&self) -> Element<'_, Message> { let pick_list = pick_list( &Language::ALL[..], self.selected_language, diff --git a/examples/pokedex/src/main.rs b/examples/pokedex/src/main.rs index 8fe23717..d56c0878 100644 --- a/examples/pokedex/src/main.rs +++ b/examples/pokedex/src/main.rs @@ -63,7 +63,7 @@ impl Pokedex { } } - fn view(&self) -> Element { + fn view(&self) -> Element<'_, Message> { let content: Element<_> = match self { Pokedex::Loading => { text("Searching for Pokémon...").size(40).into() @@ -100,7 +100,7 @@ struct Pokemon { impl Pokemon { const TOTAL: u16 = 807; - fn view(&self) -> Element { + fn view(&self) -> Element<'_, Message> { row![ image::viewer(self.image.clone()), column![ diff --git a/examples/progress_bar/src/main.rs b/examples/progress_bar/src/main.rs index df1c4dc2..8e43f090 100644 --- a/examples/progress_bar/src/main.rs +++ b/examples/progress_bar/src/main.rs @@ -30,7 +30,7 @@ impl Progress { } } - fn view(&self) -> Element { + fn view(&self) -> Element<'_, Message> { let bar = progress_bar(0.0..=100.0, self.value); column![ diff --git a/examples/qr_code/src/main.rs b/examples/qr_code/src/main.rs index 91625c95..20217615 100644 --- a/examples/qr_code/src/main.rs +++ b/examples/qr_code/src/main.rs @@ -63,7 +63,7 @@ impl QRGenerator { } } - fn view(&self) -> Element { + fn view(&self) -> Element<'_, Message> { let title = text("QR Code Generator").size(70); let input = diff --git a/examples/screenshot/src/main.rs b/examples/screenshot/src/main.rs index e4d0fc68..1f18e285 100644 --- a/examples/screenshot/src/main.rs +++ b/examples/screenshot/src/main.rs @@ -174,7 +174,7 @@ impl Example { |png_result| match png_result { Ok(path) => format!("Png saved as: {path:?}!"), Err(PngError(error)) => { - format!("Png could not be saved due to:\n{}", error) + format!("Png could not be saved due to:\n{error}") } }, ); diff --git a/examples/scrollable/src/main.rs b/examples/scrollable/src/main.rs index 793aae2a..087f8e51 100644 --- a/examples/scrollable/src/main.rs +++ b/examples/scrollable/src/main.rs @@ -118,7 +118,7 @@ impl ScrollableDemo { } } - fn view(&self) -> Element { + fn view(&self) -> Element<'_, Message> { let scrollbar_width_slider = slider( 0..=15, self.scrollbar_width, diff --git a/examples/sierpinski_triangle/src/main.rs b/examples/sierpinski_triangle/src/main.rs index f83ee5fd..f6911c29 100644 --- a/examples/sierpinski_triangle/src/main.rs +++ b/examples/sierpinski_triangle/src/main.rs @@ -1,7 +1,9 @@ use iced::mouse; 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}; +use iced::{ + Center, Color, Element, Fill, Point, Rectangle, Renderer, Size, Theme, +}; use rand::Rng; use std::fmt::Debug; @@ -46,7 +48,7 @@ impl SierpinskiEmulator { self.graph.redraw(); } - fn view(&self) -> iced::Element<'_, Message> { + fn view(&self) -> Element<'_, Message> { column![ Canvas::new(&self.graph).width(Fill).height(Fill), row![ diff --git a/examples/slider/src/main.rs b/examples/slider/src/main.rs index ea11aad9..eff453c6 100644 --- a/examples/slider/src/main.rs +++ b/examples/slider/src/main.rs @@ -27,7 +27,7 @@ impl Slider { } } - fn view(&self) -> Element { + fn view(&self) -> Element<'_, Message> { let h_slider = container( slider(1..=100, self.value, Message::SliderChanged) .default(50) diff --git a/examples/solar_system/src/main.rs b/examples/solar_system/src/main.rs index 90545e66..958fc672 100644 --- a/examples/solar_system/src/main.rs +++ b/examples/solar_system/src/main.rs @@ -55,7 +55,7 @@ impl SolarSystem { } } - fn view(&self) -> Element { + fn view(&self) -> Element<'_, Message> { canvas(&self.state).width(Fill).height(Fill).into() } diff --git a/examples/stopwatch/src/main.rs b/examples/stopwatch/src/main.rs index e4d5d7d0..9488f55a 100644 --- a/examples/stopwatch/src/main.rs +++ b/examples/stopwatch/src/main.rs @@ -83,7 +83,7 @@ impl Stopwatch { Subscription::batch(vec![tick, keyboard::on_key_press(handle_hotkey)]) } - fn view(&self) -> Element { + fn view(&self) -> Element<'_, Message> { const MINUTE: u64 = 60; const HOUR: u64 = 60 * MINUTE; diff --git a/examples/styling/snapshots/catppuccin_frappé-tiny-skia.sha256 b/examples/styling/snapshots/catppuccin_frappé-tiny-skia.sha256 index 0749a625..3309ffc7 100644 --- a/examples/styling/snapshots/catppuccin_frappé-tiny-skia.sha256 +++ b/examples/styling/snapshots/catppuccin_frappé-tiny-skia.sha256 @@ -1 +1 @@ -12ba47a34ed415825a23f8ef377a2d52950d2f8614a66bf46c0ec28d0cf15c85 \ No newline at end of file +30570747bb062e9f7730cdd58be961c84bcf4711a6983185bff6d903e8d29e9c \ No newline at end of file diff --git a/examples/styling/snapshots/catppuccin_latte-tiny-skia.sha256 b/examples/styling/snapshots/catppuccin_latte-tiny-skia.sha256 index deb39ab6..bf351053 100644 --- a/examples/styling/snapshots/catppuccin_latte-tiny-skia.sha256 +++ b/examples/styling/snapshots/catppuccin_latte-tiny-skia.sha256 @@ -1 +1 @@ -78d3afadbdca4a992c662541d602850e0dec0abafa585e2a3078daa1be5998b8 \ No newline at end of file +d5a086a08544f98087189bd4ece8815e5290722a07cd580b933f1bf77a040c52 \ No newline at end of file diff --git a/examples/styling/snapshots/catppuccin_macchiato-tiny-skia.sha256 b/examples/styling/snapshots/catppuccin_macchiato-tiny-skia.sha256 index 08924463..ec379562 100644 --- a/examples/styling/snapshots/catppuccin_macchiato-tiny-skia.sha256 +++ b/examples/styling/snapshots/catppuccin_macchiato-tiny-skia.sha256 @@ -1 +1 @@ -4e594cfec775d51f7f836646c59bf4a2de07252721d66ddddea69c17e9112bae \ No newline at end of file +30e523961db89a3ee97ad1eac09e727ecb3dec485faa362534a9f5ad083b32dd \ No newline at end of file diff --git a/examples/styling/snapshots/catppuccin_mocha-tiny-skia.sha256 b/examples/styling/snapshots/catppuccin_mocha-tiny-skia.sha256 index 9c7466a8..86583521 100644 --- a/examples/styling/snapshots/catppuccin_mocha-tiny-skia.sha256 +++ b/examples/styling/snapshots/catppuccin_mocha-tiny-skia.sha256 @@ -1 +1 @@ -2ab665b51387c61086ae0199c29e291105bfe4583bd4c4daa652e30917f10bd6 \ No newline at end of file +bce5427d5105f68e1d7fa18a34fcc551cb78c2fefd9a583ba44686331133436d \ No newline at end of file diff --git a/examples/styling/snapshots/dark-tiny-skia.sha256 b/examples/styling/snapshots/dark-tiny-skia.sha256 index 6fc0c8ec..5453059b 100644 --- a/examples/styling/snapshots/dark-tiny-skia.sha256 +++ b/examples/styling/snapshots/dark-tiny-skia.sha256 @@ -1 +1 @@ -61c9ee377b33ffa800f512877e45ad5f41fbac36f5d3f06d1b62d6af6ee9d7b2 \ No newline at end of file +c8a7edbd5a8bbf559134b84253e14e65340f4ffe3e22c272b21c8438e47ffaf7 \ No newline at end of file diff --git a/examples/styling/snapshots/dracula-tiny-skia.sha256 b/examples/styling/snapshots/dracula-tiny-skia.sha256 index 3be82338..d4b41911 100644 --- a/examples/styling/snapshots/dracula-tiny-skia.sha256 +++ b/examples/styling/snapshots/dracula-tiny-skia.sha256 @@ -1 +1 @@ -75f2fb12c9090a256708515de01a25e78905f71e134b7cda79f4fe44b2434052 \ No newline at end of file +63d646b22d3dffbb56dac2e3f345090bd26625a388dd6cc142359f2a7ac9c8df \ No newline at end of file diff --git a/examples/styling/snapshots/ferra-tiny-skia.sha256 b/examples/styling/snapshots/ferra-tiny-skia.sha256 index 04a5e3c8..2e2c2eab 100644 --- a/examples/styling/snapshots/ferra-tiny-skia.sha256 +++ b/examples/styling/snapshots/ferra-tiny-skia.sha256 @@ -1 +1 @@ -b4a1b42d2e21b2a493605745e6beb8e1f28cbeb01b73336e1e8d9061249a8311 \ No newline at end of file +d26f55674cbd96bc3b534ffdd098a13199718ef9c5ffe8ece0882ddab714b776 \ No newline at end of file diff --git a/examples/styling/snapshots/gruvbox_dark-tiny-skia.sha256 b/examples/styling/snapshots/gruvbox_dark-tiny-skia.sha256 index afb98574..8249f9c3 100644 --- a/examples/styling/snapshots/gruvbox_dark-tiny-skia.sha256 +++ b/examples/styling/snapshots/gruvbox_dark-tiny-skia.sha256 @@ -1 +1 @@ -eb52921b3ee23e1814268701c935d0dff387e7eb741c50443f75a7ab902b5e44 \ No newline at end of file +482c44c13d4ff3de19e71f3dddf93bbee170e54e2d353e818811069de28e18ed \ No newline at end of file diff --git a/examples/styling/snapshots/gruvbox_light-tiny-skia.sha256 b/examples/styling/snapshots/gruvbox_light-tiny-skia.sha256 index 0f7d8d13..0277b685 100644 --- a/examples/styling/snapshots/gruvbox_light-tiny-skia.sha256 +++ b/examples/styling/snapshots/gruvbox_light-tiny-skia.sha256 @@ -1 +1 @@ -bf6c4cbd6eeed0167d28509e37292f5ce26ed1d58bb156bedb861d0619a1945b \ No newline at end of file +6738cc4fc6eb8a5d406c613a4b0f08c0e8dcd2c1a5444445eebd3888f9303841 \ No newline at end of file diff --git a/examples/styling/snapshots/kanagawa_dragon-tiny-skia.sha256 b/examples/styling/snapshots/kanagawa_dragon-tiny-skia.sha256 index 0df00ff1..7995a848 100644 --- a/examples/styling/snapshots/kanagawa_dragon-tiny-skia.sha256 +++ b/examples/styling/snapshots/kanagawa_dragon-tiny-skia.sha256 @@ -1 +1 @@ -83726a4175900a9ef159b80925f2fa985b4ea87bff78d8bb01918fb6c40d6175 \ No newline at end of file +0a918c52538fc4848aa0c68d8f2d6f4c981ed68971dd9c725f0093a39ef7f353 \ No newline at end of file diff --git a/examples/styling/snapshots/kanagawa_lotus-tiny-skia.sha256 b/examples/styling/snapshots/kanagawa_lotus-tiny-skia.sha256 index aaf8d822..b68211a1 100644 --- a/examples/styling/snapshots/kanagawa_lotus-tiny-skia.sha256 +++ b/examples/styling/snapshots/kanagawa_lotus-tiny-skia.sha256 @@ -1 +1 @@ -54c8d44afbdd644f324cf40e744b3d7871263e44de2d9a91f6c10470c38ac3c6 \ No newline at end of file +de3e1a2c21e1a86d76ca99989c73e8a2596ef627bba95d246fab8f02d56bd0af \ No newline at end of file diff --git a/examples/styling/snapshots/kanagawa_wave-tiny-skia.sha256 b/examples/styling/snapshots/kanagawa_wave-tiny-skia.sha256 index 0d9d3f5b..d9848230 100644 --- a/examples/styling/snapshots/kanagawa_wave-tiny-skia.sha256 +++ b/examples/styling/snapshots/kanagawa_wave-tiny-skia.sha256 @@ -1 +1 @@ -5713843396e2efcfc7cc8abd00343d5d66ce8b8a195212a9b75dbfeec8edf7a7 \ No newline at end of file +3418ea4eb0f7786607ef02e7db4bc97309530f2f7c08f8aea15c768a13a09ca4 \ No newline at end of file diff --git a/examples/styling/snapshots/light-tiny-skia.sha256 b/examples/styling/snapshots/light-tiny-skia.sha256 index 73d274c7..e9864b81 100644 --- a/examples/styling/snapshots/light-tiny-skia.sha256 +++ b/examples/styling/snapshots/light-tiny-skia.sha256 @@ -1 +1 @@ -f1b20ab79f8242776d9eb1ad9cff7090435aa416811c48a7c22c69b09cd8e70f \ No newline at end of file +c8474e02a9df23f123816a489c1ea7ae6cb994a0eca429592dfe6d933de1beee \ No newline at end of file diff --git a/examples/styling/snapshots/moonfly-tiny-skia.sha256 b/examples/styling/snapshots/moonfly-tiny-skia.sha256 index 2d8f2b3c..6456da10 100644 --- a/examples/styling/snapshots/moonfly-tiny-skia.sha256 +++ b/examples/styling/snapshots/moonfly-tiny-skia.sha256 @@ -1 +1 @@ -2c4be9dc1340b65cad6d15d5318017412eba1247a016379b83db379dde0214af \ No newline at end of file +02095fd09c078be02dc41e29e55de25e8a79e6ad4293aa7e430257a9016dfb3d \ No newline at end of file diff --git a/examples/styling/snapshots/nightfly-tiny-skia.sha256 b/examples/styling/snapshots/nightfly-tiny-skia.sha256 index c174fbe6..f2ef8296 100644 --- a/examples/styling/snapshots/nightfly-tiny-skia.sha256 +++ b/examples/styling/snapshots/nightfly-tiny-skia.sha256 @@ -1 +1 @@ -2d5d6b9613ccb6a2f23142baf704288037808e7a60ee05bdc73490d8c8780064 \ No newline at end of file +d82588a2aba3e7211f25b85ebb812a42dfa59137dd4b59d26f5f60d5b28e537f \ No newline at end of file diff --git a/examples/styling/snapshots/nord-tiny-skia.sha256 b/examples/styling/snapshots/nord-tiny-skia.sha256 index 57bd936a..3905db54 100644 --- a/examples/styling/snapshots/nord-tiny-skia.sha256 +++ b/examples/styling/snapshots/nord-tiny-skia.sha256 @@ -1 +1 @@ -512b8dfd4687a609d202436e75ecf1a5553acc2157000e77e31c1578941b033c \ No newline at end of file +d6b73545929cc7794c1a918f069b5326ef129bed8f9ad2cd001be7d078a2b6a0 \ No newline at end of file diff --git a/examples/styling/snapshots/oxocarbon-tiny-skia.sha256 b/examples/styling/snapshots/oxocarbon-tiny-skia.sha256 index c36f7223..363fcf31 100644 --- a/examples/styling/snapshots/oxocarbon-tiny-skia.sha256 +++ b/examples/styling/snapshots/oxocarbon-tiny-skia.sha256 @@ -1 +1 @@ -4b3b7a2dc65307a3551227f1c5d2bb49da15d29e320ccaa160e3d9fca44c1037 \ No newline at end of file +0ec7251c69755becd678b7aec398a275edf31cc077960723cd6b9364e8678548 \ No newline at end of file diff --git a/examples/styling/snapshots/solarized_dark-tiny-skia.sha256 b/examples/styling/snapshots/solarized_dark-tiny-skia.sha256 index a3988fa4..05f529b9 100644 --- a/examples/styling/snapshots/solarized_dark-tiny-skia.sha256 +++ b/examples/styling/snapshots/solarized_dark-tiny-skia.sha256 @@ -1 +1 @@ -be1f056f0decaee8c138cd038e3e34352f02448b77c1e251796a8f0be8086913 \ No newline at end of file +4a15c475d45cf8eb0ccd6727cf6e493bd8c22454610b167a632a2328308faed1 \ No newline at end of file diff --git a/examples/styling/snapshots/solarized_light-tiny-skia.sha256 b/examples/styling/snapshots/solarized_light-tiny-skia.sha256 index 4b355311..159f3279 100644 --- a/examples/styling/snapshots/solarized_light-tiny-skia.sha256 +++ b/examples/styling/snapshots/solarized_light-tiny-skia.sha256 @@ -1 +1 @@ -31a7a46ed7951d69e1a29ab595d241e689cacdf66cea53dda6609db4927070e5 \ No newline at end of file +49a41af93e89aab0a4e352e9cedfba3c6e18caf4267955c9d362bad40264a165 \ No newline at end of file diff --git a/examples/styling/snapshots/tokyo_night-tiny-skia.sha256 b/examples/styling/snapshots/tokyo_night-tiny-skia.sha256 index 1288126c..fbcce8d7 100644 --- a/examples/styling/snapshots/tokyo_night-tiny-skia.sha256 +++ b/examples/styling/snapshots/tokyo_night-tiny-skia.sha256 @@ -1 +1 @@ -5eab001ed1aeeea3f24fe18e4aab9b8522cb35ac1328d7ca3532dbdfdf95780f \ No newline at end of file +8fcd80d4569dafdac4b4452b8ca8ab0cdceeb755f3c83d374ccd5ed4d0e8d43d \ No newline at end of file diff --git a/examples/styling/snapshots/tokyo_night_light-tiny-skia.sha256 b/examples/styling/snapshots/tokyo_night_light-tiny-skia.sha256 index 55e6a15c..eba9720a 100644 --- a/examples/styling/snapshots/tokyo_night_light-tiny-skia.sha256 +++ b/examples/styling/snapshots/tokyo_night_light-tiny-skia.sha256 @@ -1 +1 @@ -2fed695daa4c3da56f744832a041060704310a97d65820c97d556d297dfb271a \ No newline at end of file +c37a32784c769c046f3aa881914b121af373b8c6e175ced89304d15b626a653a \ No newline at end of file diff --git a/examples/styling/snapshots/tokyo_night_storm-tiny-skia.sha256 b/examples/styling/snapshots/tokyo_night_storm-tiny-skia.sha256 index 3bf421ff..ac5f0ad2 100644 --- a/examples/styling/snapshots/tokyo_night_storm-tiny-skia.sha256 +++ b/examples/styling/snapshots/tokyo_night_storm-tiny-skia.sha256 @@ -1 +1 @@ -0ecd51994f6eb37f111dc1b21cad72bb705499eb83156e9dc3ae2221ec392a42 \ No newline at end of file +533d25575e8bf1111036fb082b424d0d0e60947a7da8428ab8c71e0bda01469e \ No newline at end of file diff --git a/examples/styling/src/main.rs b/examples/styling/src/main.rs index b3386d35..934b9f5d 100644 --- a/examples/styling/src/main.rs +++ b/examples/styling/src/main.rs @@ -65,7 +65,7 @@ impl Styling { } } - fn view(&self) -> Element { + fn view(&self) -> Element<'_, Message> { let choose_theme = column![ text("Theme:"), pick_list(Theme::ALL, Some(&self.theme), Message::ThemeChanged) @@ -126,7 +126,7 @@ impl Styling { let content = column![ choose_theme, - horizontal_rule(38), + horizontal_rule(1), text_input, row![primary, success, warning, danger] .spacing(10) @@ -135,8 +135,8 @@ impl Styling { progress_bar(), row![ scrollable, - vertical_rule(38), - column![checkbox, toggler].spacing(20) + row![vertical_rule(1), column![checkbox, toggler].spacing(20)] + .spacing(20) ] .spacing(10) .height(100) diff --git a/examples/svg/src/main.rs b/examples/svg/src/main.rs index ce580ca1..53b064d5 100644 --- a/examples/svg/src/main.rs +++ b/examples/svg/src/main.rs @@ -24,7 +24,7 @@ impl Tiger { } } - fn view(&self) -> Element { + fn view(&self) -> Element<'_, Message> { let handle = svg::Handle::from_path(format!( "{}/resources/tiger.svg", env!("CARGO_MANIFEST_DIR") diff --git a/examples/system_information/src/main.rs b/examples/system_information/src/main.rs index f0bdb32d..61bca4c8 100644 --- a/examples/system_information/src/main.rs +++ b/examples/system_information/src/main.rs @@ -47,7 +47,7 @@ impl Example { } } - fn view(&self) -> Element { + fn view(&self) -> Element<'_, Message> { use bytesize::ByteSize; let content: Element<_> = match self { diff --git a/examples/the_matrix/src/main.rs b/examples/the_matrix/src/main.rs index 2f520c27..6bf3447d 100644 --- a/examples/the_matrix/src/main.rs +++ b/examples/the_matrix/src/main.rs @@ -34,7 +34,7 @@ impl TheMatrix { } } - fn view(&self) -> Element { + fn view(&self) -> Element<'_, Message> { canvas(self).width(Fill).height(Fill).into() } diff --git a/examples/toast/src/main.rs b/examples/toast/src/main.rs index f1a8cfa5..b4e93a32 100644 --- a/examples/toast/src/main.rs +++ b/examples/toast/src/main.rs @@ -613,12 +613,11 @@ mod toast { &self.viewport, renderer, ) - .max( - cursor - .is_over(layout.bounds()) - .then_some(mouse::Interaction::Idle) - .unwrap_or_default(), - ) + .max(if cursor.is_over(layout.bounds()) { + mouse::Interaction::Idle + } else { + Default::default() + }) }) .max() .unwrap_or_default() diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs index 2eb65157..10549117 100644 --- a/examples/todos/src/main.rs +++ b/examples/todos/src/main.rs @@ -186,7 +186,7 @@ impl Todos { } } - fn view(&self) -> Element { + fn view(&self) -> Element<'_, Message> { match self { Todos::Loading => loading_message(), Todos::Loaded(State { @@ -338,7 +338,7 @@ impl Task { } } - fn view(&self, i: usize) -> Element { + fn view(&self, i: usize) -> Element<'_, TaskMessage> { match &self.state { TaskState::Idle => { let checkbox = checkbox(&self.description, self.completed) @@ -385,7 +385,10 @@ impl Task { } } -fn view_controls(tasks: &[Task], current_filter: Filter) -> Element { +fn view_controls( + tasks: &[Task], + current_filter: Filter, +) -> Element<'_, Message> { let tasks_left = tasks.iter().filter(|task| !task.completed).count(); let filter_button = |label, filter, current_filter| { @@ -617,7 +620,7 @@ mod tests { use iced_test::selector::id; use iced_test::{Error, Simulator}; - fn simulator(todos: &Todos) -> Simulator { + fn simulator(todos: &Todos) -> Simulator<'_, Message> { Simulator::with_settings( Settings { fonts: vec![Todos::ICON_FONT.into()], diff --git a/examples/tooltip/src/main.rs b/examples/tooltip/src/main.rs index 599fcd4e..bf927271 100644 --- a/examples/tooltip/src/main.rs +++ b/examples/tooltip/src/main.rs @@ -33,7 +33,7 @@ impl Tooltip { } } - fn view(&self) -> Element { + fn view(&self) -> Element<'_, Message> { let tooltip = tooltip( button("Press to change position") .on_press(Message::ChangePosition), diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index 5d31cbd7..984cf272 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -76,7 +76,7 @@ impl Tour { Screen::End => "End", }; - format!("{} - Iced", screen) + format!("{screen} - Iced") } fn update(&mut self, event: Message) { @@ -141,7 +141,7 @@ impl Tour { } } - fn view(&self) -> Element { + fn view(&self) -> Element<'_, Message> { let controls = row![] .push_maybe(self.screen.previous().is_some().then(|| { @@ -197,7 +197,7 @@ impl Tour { } } - fn welcome(&self) -> Column { + fn welcome(&self) -> Column<'_, Message> { Self::container("Welcome!") .push( "This is a simple tour meant to showcase a bunch of \ @@ -244,7 +244,7 @@ impl Tour { ) } - fn slider(&self) -> Column { + fn slider(&self) -> Column<'_, Message> { Self::container("Slider") .push( "A slider allows you to smoothly select a value from a range \ @@ -258,7 +258,7 @@ impl Tour { .push(text(self.slider.to_string()).width(Fill).align_x(Center)) } - fn rows_and_columns(&self) -> Column { + fn rows_and_columns(&self) -> Column<'_, Message> { let row_radio = radio( "Row", Layout::Row, @@ -303,7 +303,7 @@ impl Tour { .push(spacing_section) } - fn text(&self) -> Column { + fn text(&self) -> Column<'_, Message> { let size = self.text_size; let color = self.text_color; @@ -339,7 +339,7 @@ impl Tour { .push(color_section) } - fn radio(&self) -> Column { + fn radio(&self) -> Column<'_, Message> { let question = column![ text("Iced is written in...").size(24), column( @@ -374,7 +374,7 @@ impl Tour { ) } - fn toggler(&self) -> Column { + fn toggler(&self) -> Column<'_, Message> { Self::container("Toggler") .push("A toggler is mostly used to enable or disable something.") .push( @@ -387,7 +387,7 @@ impl Tour { ) } - fn image(&self) -> Column { + fn image(&self) -> Column<'_, Message> { let width = self.image_width; let filter_method = self.image_filter_method; @@ -406,7 +406,7 @@ impl Tour { .align_x(Center) } - fn scrollable(&self) -> Column { + fn scrollable(&self) -> Column<'_, Message> { Self::container("Scrollable") .push( "Iced supports scrollable content. Try it out! Find the \ @@ -428,7 +428,7 @@ impl Tour { .push(text("You made it!").width(Fill).size(50).align_x(Center)) } - fn text_input(&self) -> Column { + fn text_input(&self) -> Column<'_, Message> { let value = &self.input_value; let is_secure = self.input_is_secure; let is_showing_icon = self.input_is_showing_icon; @@ -474,7 +474,7 @@ impl Tour { ) } - fn debugger(&self) -> Column { + fn debugger(&self) -> Column<'_, Message> { Self::container("Debugger") .push( "You can ask Iced to visually explain the layouting of the \ @@ -491,7 +491,7 @@ impl Tour { .push("Feel free to go back and take a look.") } - fn end(&self) -> Column { + fn end(&self) -> Column<'_, Message> { Self::container("You reached the end!") .push("This tour will be updated as more features are added.") .push("Make sure to keep an eye on it!") diff --git a/examples/url_handler/src/main.rs b/examples/url_handler/src/main.rs index 7c3e3f35..1bc295c9 100644 --- a/examples/url_handler/src/main.rs +++ b/examples/url_handler/src/main.rs @@ -31,7 +31,7 @@ impl App { event::listen_url().map(Message::UrlReceived) } - fn view(&self) -> Element { + fn view(&self) -> Element<'_, Message> { let content = match &self.url { Some(url) => text(url), None => text("No URL received yet!"), diff --git a/examples/vectorial_text/src/main.rs b/examples/vectorial_text/src/main.rs index 72525d55..d0ba91b9 100644 --- a/examples/vectorial_text/src/main.rs +++ b/examples/vectorial_text/src/main.rs @@ -48,7 +48,7 @@ impl VectorialText { self.state.cache.clear(); } - fn view(&self) -> Element { + fn view(&self) -> Element<'_, Message> { let slider_with_label = |label, range, value, message: fn(f32) -> _| { column![ row![text(label), horizontal_space(), text!("{:.2}", value)], diff --git a/examples/visible_bounds/src/main.rs b/examples/visible_bounds/src/main.rs index 0b152e44..8e5e4a07 100644 --- a/examples/visible_bounds/src/main.rs +++ b/examples/visible_bounds/src/main.rs @@ -59,7 +59,7 @@ impl Example { } } - fn view(&self) -> Element { + fn view(&self) -> Element<'_, Message> { let data_row = |label, value, color| { row![ text(label), diff --git a/examples/websocket/src/main.rs b/examples/websocket/src/main.rs index 81083a22..40e37ad6 100644 --- a/examples/websocket/src/main.rs +++ b/examples/websocket/src/main.rs @@ -93,7 +93,7 @@ impl WebSocket { ]) } - fn view(&self) -> Element { + fn view(&self) -> Element<'_, Message> { let message_log: Element<_> = if self.messages.is_empty() { center( text("Your messages will appear here...") diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 47bd92d9..457f723c 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -56,6 +56,9 @@ pub enum Action { /// Run a system action. System(system::Action), + /// Recreate all user interfaces and redraw all windows. + Reload, + /// Exits the runtime. /// /// This will normally close any application windows and @@ -79,6 +82,7 @@ impl Action { Action::Clipboard(action) => Err(Action::Clipboard(action)), Action::Window(action) => Err(Action::Window(action)), Action::System(action) => Err(Action::System(action)), + Action::Reload => Err(Action::Reload), Action::Exit => Err(Action::Exit), } } @@ -102,6 +106,7 @@ where } Action::Window(_) => write!(f, "Action::Window"), Action::System(action) => write!(f, "Action::System({action:?})"), + Action::Reload => write!(f, "Action::Reload"), Action::Exit => write!(f, "Action::Exit"), } } diff --git a/runtime/src/task.rs b/runtime/src/task.rs index 756f592b..731c0be8 100644 --- a/runtime/src/task.rs +++ b/runtime/src/task.rs @@ -17,7 +17,6 @@ pub use sipper::{Never, Sender, Sipper, Straw, sipper, stream}; /// A set of concurrent actions to be performed by the iced runtime. /// /// A [`Task`] _may_ produce a bunch of values of type `T`. -#[allow(missing_debug_implementations)] #[must_use = "`Task` must be returned to the runtime to take effect; normally in your `update` or `new` functions."] pub struct Task { stream: Option>>, @@ -278,6 +277,14 @@ impl Task { } } +impl std::fmt::Debug for Task { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct(&format!("Task<{}>", std::any::type_name::())) + .field("units", &self.units) + .finish() + } +} + /// A handle to a [`Task`] that can be used for aborting it. #[derive(Debug, Clone)] pub struct Handle { diff --git a/src/application.rs b/src/application.rs index 459f3a68..e832834c 100644 --- a/src/application.rs +++ b/src/application.rs @@ -38,6 +38,8 @@ use crate::{ Element, Executor, Font, Result, Settings, Size, Subscription, Task, }; +use iced_debug as debug; + use std::borrow::Cow; pub mod timed; @@ -128,7 +130,7 @@ where state: &mut Self::State, message: Self::Message, ) -> Task { - self.update.update(state, message) + debug::hot(|| self.update.update(state, message)) } fn view<'a>( @@ -136,7 +138,7 @@ where state: &'a Self::State, _window: window::Id, ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> { - self.view.view(state) + debug::hot(|| self.view.view(state)) } fn settings(&self) -> Settings { @@ -342,7 +344,7 @@ impl Application

{ > { Application { raw: program::with_title(self.raw, move |state, _window| { - title.title(state) + debug::hot(|| title.title(state)) }), settings: self.settings, window: self.window, @@ -359,7 +361,9 @@ impl Application

{ impl Program, > { Application { - raw: program::with_subscription(self.raw, f), + raw: program::with_subscription(self.raw, move |state| { + debug::hot(|| f(state)) + }), settings: self.settings, window: self.window, #[cfg(feature = "test")] @@ -375,7 +379,9 @@ impl Application

{ impl Program, > { Application { - raw: program::with_theme(self.raw, move |state, _window| f(state)), + raw: program::with_theme(self.raw, move |state, _window| { + debug::hot(|| f(state)) + }), settings: self.settings, window: self.window, #[cfg(feature = "test")] @@ -391,7 +397,9 @@ impl Application

{ impl Program, > { Application { - raw: program::with_style(self.raw, f), + raw: program::with_style(self.raw, move |state, theme| { + debug::hot(|| f(state, theme)) + }), settings: self.settings, window: self.window, #[cfg(feature = "test")] @@ -408,7 +416,7 @@ impl Application

{ > { Application { raw: program::with_scale_factor(self.raw, move |state, _window| { - f(state) + debug::hot(|| f(state)) }), settings: self.settings, window: self.window, diff --git a/src/application/timed.rs b/src/application/timed.rs index 18eba2b5..7deb3faa 100644 --- a/src/application/timed.rs +++ b/src/application/timed.rs @@ -6,6 +6,8 @@ use crate::time::Instant; use crate::window; use crate::{Element, Program, Settings, Subscription, Task}; +use iced_debug as debug; + /// Creates an [`Application`] with an `update` function that also /// takes the [`Instant`] of each `Message`. /// @@ -101,10 +103,12 @@ where state: &mut Self::State, (message, now): Self::Message, ) -> Task { - self.update - .update(state, message, now) - .into() - .map(|message| (message, Instant::now())) + debug::hot(move || { + self.update + .update(state, message, now) + .into() + .map(|message| (message, Instant::now())) + }) } fn view<'a>( @@ -112,16 +116,21 @@ where state: &'a Self::State, _window: window::Id, ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> { - self.view - .view(state) - .map(|message| (message, Instant::now())) + debug::hot(|| { + self.view + .view(state) + .map(|message| (message, Instant::now())) + }) } fn subscription( &self, state: &Self::State, ) -> self::Subscription { - (self.subscription)(state).map(|message| (message, Instant::now())) + debug::hot(|| { + (self.subscription)(state) + .map(|message| (message, Instant::now())) + }) } } diff --git a/src/daemon.rs b/src/daemon.rs index b1f6e5ab..d64b7712 100644 --- a/src/daemon.rs +++ b/src/daemon.rs @@ -6,6 +6,8 @@ use crate::theme; use crate::window; use crate::{Element, Executor, Font, Result, Settings, Subscription, Task}; +use iced_debug as debug; + use std::borrow::Cow; /// Creates an iced [`Daemon`] given its boot, update, and view logic. @@ -76,7 +78,7 @@ where state: &mut Self::State, message: Self::Message, ) -> Task { - self.update.update(state, message) + debug::hot(|| self.update.update(state, message)) } fn view<'a>( @@ -84,7 +86,7 @@ where state: &'a Self::State, window: window::Id, ) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> { - self.view.view(state, window) + debug::hot(|| self.view.view(state, window)) } } @@ -182,7 +184,7 @@ impl Daemon

{ > { Daemon { raw: program::with_title(self.raw, move |state, window| { - title.title(state, window) + debug::hot(|| title.title(state, window)) }), settings: self.settings, } @@ -196,7 +198,9 @@ impl Daemon

{ impl Program, > { Daemon { - raw: program::with_subscription(self.raw, f), + raw: program::with_subscription(self.raw, move |state| { + debug::hot(|| f(state)) + }), settings: self.settings, } } @@ -209,7 +213,9 @@ impl Daemon

{ impl Program, > { Daemon { - raw: program::with_theme(self.raw, f), + raw: program::with_theme(self.raw, move |state, window| { + debug::hot(|| f(state, window)) + }), settings: self.settings, } } @@ -222,7 +228,9 @@ impl Daemon

{ impl Program, > { Daemon { - raw: program::with_style(self.raw, f), + raw: program::with_style(self.raw, move |state, theme| { + debug::hot(|| f(state, theme)) + }), settings: self.settings, } } @@ -235,7 +243,9 @@ impl Daemon

{ impl Program, > { Daemon { - raw: program::with_scale_factor(self.raw, f), + raw: program::with_scale_factor(self.raw, move |state, window| { + debug::hot(|| f(state, window)) + }), settings: self.settings, } } diff --git a/src/lib.rs b/src/lib.rs index b77f0b21..180db986 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,7 +34,7 @@ //! iced::run(update, view) //! } //! # fn update(state: &mut (), message: ()) {} -//! # fn view(state: &()) -> iced::Element<()> { iced::widget::text("").into() } +//! # fn view(state: &()) -> iced::Element<'_, ()> { iced::widget::text("").into() } //! ``` //! //! Define an `update` function to __change__ your state: @@ -55,7 +55,7 @@ //! use iced::widget::{button, text}; //! use iced::Element; //! -//! fn view(counter: &u64) -> Element { +//! fn view(counter: &u64) -> Element<'_, Message> { //! button(text(counter)).on_press(Message::Increment).into() //! } //! # #[derive(Clone)] @@ -95,7 +95,7 @@ //! } //! } //! -//! fn view(counter: &Counter) -> Element { +//! fn view(counter: &Counter) -> Element<'_, Message> { //! button(text(counter.value)).on_press(Message::Increment).into() //! } //! ``` @@ -115,7 +115,7 @@ //! use iced::widget::{button, column, text}; //! use iced::Element; //! -//! fn view(counter: &Counter) -> Element { +//! fn view(counter: &Counter) -> Element<'_, Message> { //! column![ //! text(counter.value).size(20), //! button("Increment").on_press(Message::Increment), @@ -144,7 +144,7 @@ //! use iced::widget::{column, container, row}; //! use iced::{Fill, Element}; //! -//! fn view(state: &State) -> Element { +//! fn view(state: &State) -> Element<'_, Message> { //! container( //! column![ //! "Top", @@ -187,7 +187,7 @@ //! use iced::widget::container; //! use iced::Element; //! -//! fn view(state: &State) -> Element { +//! fn view(state: &State) -> Element<'_, Message> { //! container("I am 300px tall!").height(300).into() //! } //! ``` @@ -216,7 +216,7 @@ //! Theme::TokyoNight //! } //! # fn update(state: &mut State, message: ()) {} -//! # fn view(state: &State) -> iced::Element<()> { iced::widget::text("").into() } +//! # fn view(state: &State) -> iced::Element<'_, ()> { iced::widget::text("").into() } //! ``` //! //! The `theme` function takes the current state of the application, allowing the @@ -237,7 +237,7 @@ //! use iced::widget::container; //! use iced::Element; //! -//! fn view(state: &State) -> Element { +//! fn view(state: &State) -> Element<'_, Message> { //! container("I am a rounded box!").style(container::rounded_box).into() //! } //! ``` @@ -252,7 +252,7 @@ //! use iced::widget::button; //! use iced::{Element, Theme}; //! -//! fn view(state: &State) -> Element { +//! fn view(state: &State) -> Element<'_, Message> { //! button("I am a styled button!").style(|theme: &Theme, status| { //! let palette = theme.extended_palette(); //! @@ -359,7 +359,7 @@ //! } //! # fn new() -> State { State } //! # fn update(state: &mut State, message: Message) {} -//! # fn view(state: &State) -> iced::Element { iced::widget::text("").into() } +//! # fn view(state: &State) -> iced::Element<'_, Message> { iced::widget::text("").into() } //! ``` //! //! A [`Subscription`] is [a _declarative_ builder of streams](Subscription#the-lifetime-of-a-subscription) @@ -452,7 +452,7 @@ //! } //! } //! -//! fn view(state: &State) -> Element { +//! fn view(state: &State) -> Element<'_, Message> { //! match &state.screen { //! Screen::Contacts(contacts) => contacts.view().map(Message::Contacts), //! Screen::Conversation(conversation) => conversation.view().map(Message::Conversation), @@ -659,8 +659,8 @@ pub type Element< /// The result of running an iced program. pub type Result = std::result::Result<(), Error>; -/// Runs a basic iced application with default [`Settings`] given its title, -/// update, and view logic. +/// Runs a basic iced application with default [`Settings`] given its update +/// and view logic. /// /// This is equivalent to chaining [`application()`] with [`Application::run`]. /// diff --git a/test/src/emulator.rs b/test/src/emulator.rs index a2b6e6a4..a1465a4f 100644 --- a/test/src/emulator.rs +++ b/test/src/emulator.rs @@ -153,6 +153,9 @@ impl Emulator

{ Action::Exit => { // TODO } + Action::Reload => { + // TODO + } } } diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs index f2908e71..6fcc8e45 100644 --- a/wgpu/src/text.rs +++ b/wgpu/src/text.rs @@ -626,10 +626,10 @@ fn prepare( scale: transformation.scale_factor() * layer_transformation.scale_factor(), bounds: cryoglyph::TextBounds { - left: clip_bounds.x as i32, - top: clip_bounds.y as i32, - right: (clip_bounds.x + clip_bounds.width) as i32, - bottom: (clip_bounds.y + clip_bounds.height) as i32, + left: clip_bounds.x.round() as i32, + top: clip_bounds.y.round() as i32, + right: (clip_bounds.x + clip_bounds.width).round() as i32, + bottom: (clip_bounds.y + clip_bounds.height).round() as i32, }, default_color: to_color(color), }) diff --git a/wgpu/src/window/compositor.rs b/wgpu/src/window/compositor.rs index 60cb33bb..c729ddd0 100644 --- a/wgpu/src/window/compositor.rs +++ b/wgpu/src/window/compositor.rs @@ -96,7 +96,7 @@ impl Compositor { let adapter = instance .request_adapter(&adapter_options) .await - .ok_or(Error::NoAdapterFound(format!("{:?}", adapter_options)))?; + .ok_or(Error::NoAdapterFound(format!("{adapter_options:?}")))?; log::info!("Selected: {:#?}", adapter.get_info()); diff --git a/widget/src/container.rs b/widget/src/container.rs index 73b2502a..e925816c 100644 --- a/widget/src/container.rs +++ b/widget/src/container.rs @@ -400,9 +400,9 @@ where Renderer: core::Renderer + 'a, { fn from( - column: Container<'a, Message, Theme, Renderer>, + container: Container<'a, Message, Theme, Renderer>, ) -> Element<'a, Message, Theme, Renderer> { - Element::new(column) + Element::new(container) } } diff --git a/widget/src/markdown.rs b/widget/src/markdown.rs index f6360211..be88c5f9 100644 --- a/widget/src/markdown.rs +++ b/widget/src/markdown.rs @@ -50,7 +50,10 @@ use crate::core::theme; use crate::core::{ self, Color, Element, Length, Padding, Pixels, Theme, color, }; -use crate::{column, container, rich_text, row, scrollable, span, text}; +use crate::{ + column, container, horizontal_rule, rich_text, row, rule, scrollable, span, + text, vertical_rule, +}; use std::borrow::BorrowMut; use std::cell::{Cell, RefCell}; @@ -208,6 +211,10 @@ pub enum Item { /// The alternative text of the image. alt: Text, }, + /// A quote. + Quote(Vec), + /// A horizontal separator. + Rule, } /// A bunch of parsed Markdown text. @@ -339,8 +346,8 @@ impl Span { /// /// fn view(&self) -> Element<'_, Message> { /// markdown::view(&self.markdown, Theme::TokyoNight) -/// .map(Message::LinkClicked) -/// .into() +/// .map(Message::LinkClicked) +/// .into() /// } /// /// fn update(state: &mut State, message: Message) { @@ -454,6 +461,7 @@ fn parse_with<'a>( ) -> impl Iterator)> + 'a { enum Scope { List(List), + Quote(Vec), } struct List { @@ -524,6 +532,9 @@ fn parse_with<'a>( Scope::List(list) => { list.items.last_mut().expect("item context").push(item); } + Scope::Quote(items) => { + items.push(item); + } } None @@ -605,6 +616,22 @@ fn parse_with<'a>( None } + pulldown_cmark::Tag::BlockQuote(_kind) if !metadata && !table => { + let prev = if spans.is_empty() { + None + } else { + produce( + state.borrow_mut(), + &mut stack, + Item::Paragraph(Text::new(spans.drain(..).collect())), + source, + ) + }; + + stack.push(Scope::Quote(Vec::new())); + + prev + } pulldown_cmark::Tag::CodeBlock( pulldown_cmark::CodeBlockKind::Fenced(language), ) if !metadata && !table => { @@ -703,7 +730,9 @@ fn parse_with<'a>( pulldown_cmark::TagEnd::List(_) if !metadata && !table => { let scope = stack.pop()?; - let Scope::List(list) = scope; + let Scope::List(list) = scope else { + return None; + }; produce( state.borrow_mut(), @@ -715,6 +744,22 @@ fn parse_with<'a>( source, ) } + pulldown_cmark::TagEnd::BlockQuote(_kind) + if !metadata && !table => + { + let scope = stack.pop()?; + + let Scope::Quote(quote) = scope else { + return None; + }; + + produce( + state.borrow_mut(), + &mut stack, + Item::Quote(quote), + source, + ) + } pulldown_cmark::TagEnd::Image if !metadata && !table => { let (url, title) = image.take()?; let alt = Text::new(spans.drain(..).collect()); @@ -834,6 +879,9 @@ fn parse_with<'a>( }); None } + pulldown_cmark::Event::Rule => { + produce(state.borrow_mut(), &mut stack, Item::Rule, source) + } _ => None, }) } @@ -1063,6 +1111,8 @@ where start: Some(start), items, } => viewer.ordered_list(settings, *start, items), + Item::Quote(quote) => viewer.quote(settings, quote), + Item::Rule => viewer.rule(settings), } } @@ -1226,7 +1276,44 @@ where .into() } -/// A view strategy to display a Markdown [`Item`].j +/// Displays a quote using the default look. +pub fn quote<'a, Message, Theme, Renderer>( + viewer: &impl Viewer<'a, Message, Theme, Renderer>, + settings: Settings, + contents: &'a [Item], +) -> Element<'a, Message, Theme, Renderer> +where + Message: 'a, + Theme: Catalog + 'a, + Renderer: core::text::Renderer + 'a, +{ + row![ + vertical_rule(4), + column( + contents + .iter() + .enumerate() + .map(|(i, content)| item(viewer, settings, content, i)), + ) + .spacing(settings.spacing.0), + ] + .height(Length::Shrink) + .spacing(settings.spacing.0) + .into() +} + +/// Displays a rule using the default look. +pub fn rule<'a, Message, Theme, Renderer>() +-> Element<'a, Message, Theme, Renderer> +where + Message: 'a, + Theme: Catalog + 'a, + Renderer: core::text::Renderer + 'a, +{ + horizontal_rule(2).into() +} + +/// A view strategy to display a Markdown [`Item`]. pub trait Viewer<'a, Message, Theme = crate::Theme, Renderer = crate::Renderer> where Self: Sized + 'a, @@ -1321,6 +1408,27 @@ where ) -> Element<'a, Message, Theme, Renderer> { ordered_list(self, settings, start, items) } + + /// Displays a quote. + /// + /// By default, it calls [`quote`]. + fn quote( + &self, + settings: Settings, + contents: &'a [Item], + ) -> Element<'a, Message, Theme, Renderer> { + quote(self, settings, contents) + } + + /// Displays a rule. + /// + /// By default, it calls [`rule`](self::rule()). + fn rule( + &self, + _settings: Settings, + ) -> Element<'a, Message, Theme, Renderer> { + rule() + } } #[derive(Debug, Clone, Copy)] @@ -1338,7 +1446,7 @@ where /// The theme catalog of Markdown items. pub trait Catalog: - container::Catalog + scrollable::Catalog + text::Catalog + container::Catalog + scrollable::Catalog + rule::Catalog + text::Catalog { /// The styling class of a Markdown code block. fn code_block<'a>() -> ::Class<'a>; diff --git a/widget/src/pane_grid.rs b/widget/src/pane_grid.rs index 53206792..db60fabc 100644 --- a/widget/src/pane_grid.rs +++ b/widget/src/pane_grid.rs @@ -297,11 +297,11 @@ where } fn drag_enabled(&self) -> bool { - self.internal - .maximized() - .is_none() - .then(|| self.on_drag.is_some()) - .unwrap_or_default() + if self.internal.maximized().is_none() { + self.on_drag.is_some() + } else { + Default::default() + } } fn grid_interaction( diff --git a/widget/src/rule.rs b/widget/src/rule.rs index 65c0a6dc..2bb893b2 100644 --- a/widget/src/rule.rs +++ b/widget/src/rule.rs @@ -134,9 +134,7 @@ where let style = theme.style(&self.class); let bounds = if self.is_horizontal { - let line_y = (bounds.y + (bounds.height / 2.0) - - (style.width as f32 / 2.0)) - .round(); + let line_y = (bounds.y + (bounds.height / 2.0)).round(); let (offset, line_width) = style.fill_mode.fill(bounds.width); let line_x = bounds.x + offset; @@ -145,12 +143,10 @@ where x: line_x, y: line_y, width: line_width, - height: style.width as f32, + height: bounds.height, } } else { - let line_x = (bounds.x + (bounds.width / 2.0) - - (style.width as f32 / 2.0)) - .round(); + let line_x = (bounds.x + (bounds.width / 2.0)).round(); let (offset, line_height) = style.fill_mode.fill(bounds.height); let line_y = bounds.y + offset; @@ -158,7 +154,7 @@ where Rectangle { x: line_x, y: line_y, - width: style.width as f32, + width: bounds.width, height: line_height, } }; @@ -167,6 +163,7 @@ where renderer::Quad { bounds, border: border::rounded(style.radius), + snap: style.snap, ..renderer::Quad::default() }, style.color, @@ -191,12 +188,12 @@ where pub struct Style { /// The color of the rule. pub color: Color, - /// The width (thickness) of the rule line. - pub width: u16, /// The radius of the line corners. pub radius: border::Radius, /// The [`FillMode`] of the rule. pub fill_mode: FillMode, + /// Whether the rule should be snapped to the pixel grid. + pub snap: bool, } /// The fill mode of a rule. @@ -298,8 +295,8 @@ pub fn default(theme: &Theme) -> Style { Style { color: palette.background.strong.color, - width: 1, radius: 0.0.into(), fill_mode: FillMode::Full, + snap: true, } } diff --git a/widget/src/scrollable.rs b/widget/src/scrollable.rs index ff366db0..01c4ef7c 100644 --- a/widget/src/scrollable.rs +++ b/widget/src/scrollable.rs @@ -438,7 +438,7 @@ where }, |limits| { let child_limits = layout::Limits::new( - Size::new(limits.min().width, limits.min().height), + limits.min(), Size::new( if self.direction.horizontal().is_some() { f32::INFINITY diff --git a/winit/src/lib.rs b/winit/src/lib.rs index 7b94a878..c4e62863 100644 --- a/winit/src/lib.rs +++ b/winit/src/lib.rs @@ -85,6 +85,15 @@ where let (proxy, worker) = Proxy::new(event_loop.create_proxy()); + #[cfg(feature = "debug")] + { + let proxy = proxy.clone(); + + debug::on_hotpatch(move || { + proxy.send_action(Action::Reload); + }); + } + let mut runtime = { let executor = P::Executor::new().map_err(Error::ExecutorCreationFailed)?; @@ -527,7 +536,7 @@ async fn run_instance

( let create_compositor = { let window = window.clone(); - let mut proxy = proxy.clone(); + let proxy = proxy.clone(); let default_fonts = default_fonts.clone(); async move { @@ -801,7 +810,7 @@ async fn run_instance

( Err(error) => match error { // This is an unrecoverable error. compositor::SurfaceError::OutOfMemory => { - panic!("{:?}", error); + panic!("{error:?}"); } _ => { present_span.finish(); @@ -1075,9 +1084,9 @@ fn update( runtime.track(recipes); } -fn run_action( +fn run_action<'a, P, C>( action: Action, - program: &program::Instance

, + program: &'a program::Instance

, compositor: &mut Option, events: &mut Vec<(window::Id, core::Event)>, messages: &mut Vec, @@ -1085,7 +1094,7 @@ fn run_action( control_sender: &mut mpsc::UnboundedSender, interfaces: &mut FxHashMap< window::Id, - UserInterface<'_, P::Message, P::Theme, P::Renderer>, + UserInterface<'a, P::Message, P::Theme, P::Renderer>, >, window_manager: &mut WindowManager, ui_caches: &mut FxHashMap, @@ -1437,6 +1446,29 @@ fn run_action( let _ = channel.send(Ok(())); } } + Action::Reload => { + for (id, window) in window_manager.iter_mut() { + let Some(ui) = interfaces.remove(&id) else { + continue; + }; + + let cache = ui.into_cache(); + let size = window.size(); + + let _ = interfaces.insert( + id, + build_user_interface( + program, + cache, + &mut window.renderer, + size, + id, + ), + ); + + window.raw.request_redraw(); + } + } Action::Exit => { control_sender .start_send(Control::Exit) diff --git a/winit/src/proxy.rs b/winit/src/proxy.rs index d8d3f4a2..dcfcacff 100644 --- a/winit/src/proxy.rs +++ b/winit/src/proxy.rs @@ -77,7 +77,7 @@ impl Proxy { /// /// Note: This skips the backpressure mechanism with an unbounded /// channel. Use sparingly! - pub fn send(&mut self, value: T) + pub fn send(&self, value: T) where T: std::fmt::Debug, { @@ -88,13 +88,11 @@ impl Proxy { /// /// Note: This skips the backpressure mechanism with an unbounded /// channel. Use sparingly! - pub fn send_action(&mut self, action: Action) + pub fn send_action(&self, action: Action) where T: std::fmt::Debug, { - self.raw - .send_event(action) - .expect("Send message to event loop"); + let _ = self.raw.send_event(action); } /// Frees an amount of slots for additional messages to be queued in