From 5ad08acd97a6fb6ed1e94f547d7b431d6f89f9b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Fri, 27 Jun 2025 16:47:05 +0200 Subject: [PATCH 1/5] Defer `flex` layout of elements with fixed length in the main axis --- core/src/layout/flex.rs | 53 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 6 deletions(-) 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 { From ca1bf717b33549c02784a27589b3fc694243d0c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Fri, 27 Jun 2025 16:47:54 +0200 Subject: [PATCH 2/5] Implement `Quote` support in `markdown` widget --- widget/src/container.rs | 4 +- widget/src/markdown.rs | 89 +++++++++++++++++++++++++++++++++++++++-- widget/src/rule.rs | 15 ++----- 3 files changed, 91 insertions(+), 17 deletions(-) diff --git a/widget/src/container.rs b/widget/src/container.rs index 4f6725b1..84bb5237 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 55fe34e4..a7d012a1 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, rich_text, row, rule, scrollable, span, text, + vertical_rule, +}; use std::borrow::BorrowMut; use std::cell::{Cell, RefCell}; @@ -208,6 +211,8 @@ pub enum Item { /// The alternative text of the image. alt: Text, }, + /// A quote. + Quote(Vec), } /// A bunch of parsed Markdown text. @@ -454,6 +459,7 @@ fn parse_with<'a>( ) -> impl Iterator)> + 'a { enum Scope { List(List), + Quote(Vec), } struct List { @@ -524,6 +530,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 +614,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 +728,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 +742,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()); @@ -1063,6 +1106,7 @@ where start: Some(start), items, } => viewer.ordered_list(settings, *start, items), + Item::Quote(quote) => viewer.quote(settings, quote), } } @@ -1226,7 +1270,33 @@ 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() +} + +/// 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 +1391,17 @@ where ) -> Element<'a, Message, Theme, Renderer> { ordered_list(self, settings, start, items) } + + /// Displays a quote. + /// + /// By default, it call [`quote`]. + fn quote( + &self, + settings: Settings, + contents: &'a [Item], + ) -> Element<'a, Message, Theme, Renderer> { + quote(self, settings, contents) + } } #[derive(Debug, Clone, Copy)] @@ -1338,7 +1419,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/rule.rs b/widget/src/rule.rs index 03c57c94..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, } }; @@ -192,8 +188,6 @@ 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. @@ -301,7 +295,6 @@ pub fn default(theme: &Theme) -> Style { Style { color: palette.background.strong.color, - width: 1, radius: 0.0.into(), fill_mode: FillMode::Full, snap: true, From 0ff616814d537ac43dea502e10ccc5c4324fe047 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Fri, 27 Jun 2025 16:51:00 +0200 Subject: [PATCH 3/5] Fix typo in docs of `markdown::Viewer::quote` --- widget/src/markdown.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widget/src/markdown.rs b/widget/src/markdown.rs index a7d012a1..3fc18bc3 100644 --- a/widget/src/markdown.rs +++ b/widget/src/markdown.rs @@ -1394,7 +1394,7 @@ where /// Displays a quote. /// - /// By default, it call [`quote`]. + /// By default, it calls [`quote`]. fn quote( &self, settings: Settings, From ef13eb58b2582ba4f87f250522bdc70945eb2f86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Fri, 27 Jun 2025 17:05:38 +0200 Subject: [PATCH 4/5] Fix `styling` example and regenerate snapshots --- .../styling/snapshots/catppuccin_frappé-tiny-skia.sha256 | 2 +- .../styling/snapshots/catppuccin_latte-tiny-skia.sha256 | 2 +- .../styling/snapshots/catppuccin_macchiato-tiny-skia.sha256 | 2 +- .../styling/snapshots/catppuccin_mocha-tiny-skia.sha256 | 2 +- examples/styling/snapshots/dark-tiny-skia.sha256 | 2 +- examples/styling/snapshots/dracula-tiny-skia.sha256 | 2 +- examples/styling/snapshots/ferra-tiny-skia.sha256 | 2 +- examples/styling/snapshots/gruvbox_dark-tiny-skia.sha256 | 2 +- examples/styling/snapshots/gruvbox_light-tiny-skia.sha256 | 2 +- examples/styling/snapshots/kanagawa_dragon-tiny-skia.sha256 | 2 +- examples/styling/snapshots/kanagawa_lotus-tiny-skia.sha256 | 2 +- examples/styling/snapshots/kanagawa_wave-tiny-skia.sha256 | 2 +- examples/styling/snapshots/light-tiny-skia.sha256 | 2 +- examples/styling/snapshots/moonfly-tiny-skia.sha256 | 2 +- examples/styling/snapshots/nightfly-tiny-skia.sha256 | 2 +- examples/styling/snapshots/nord-tiny-skia.sha256 | 2 +- examples/styling/snapshots/oxocarbon-tiny-skia.sha256 | 2 +- examples/styling/snapshots/solarized_dark-tiny-skia.sha256 | 2 +- examples/styling/snapshots/solarized_light-tiny-skia.sha256 | 2 +- examples/styling/snapshots/tokyo_night-tiny-skia.sha256 | 2 +- .../styling/snapshots/tokyo_night_light-tiny-skia.sha256 | 2 +- .../styling/snapshots/tokyo_night_storm-tiny-skia.sha256 | 2 +- examples/styling/src/main.rs | 6 +++--- 23 files changed, 25 insertions(+), 25 deletions(-) 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 1993b7c9..8c303beb 100644 --- a/examples/styling/snapshots/catppuccin_latte-tiny-skia.sha256 +++ b/examples/styling/snapshots/catppuccin_latte-tiny-skia.sha256 @@ -1 +1 @@ -fa00d7e0ff95b366945d409712d7fe4ce44fff22425236cb56b8b96a88815ee6 \ No newline at end of file +039a950d09d34df222606eaf2166e65f86d5536f8abfaff0e295219c974d54c0 \ 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 0b341569..6de6c786 100644 --- a/examples/styling/snapshots/solarized_dark-tiny-skia.sha256 +++ b/examples/styling/snapshots/solarized_dark-tiny-skia.sha256 @@ -1 +1 @@ -15aa476c65304bde23e3648ceb424d6394f525f6c1d3e49ee1150376b6fd3068 \ No newline at end of file +d87f3a00de5ee52db12a21c09e8689d8d489cce6eb8c8ce1b89192b85587e9a0 \ 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 b62d6107..163561bc 100644 --- a/examples/styling/snapshots/solarized_light-tiny-skia.sha256 +++ b/examples/styling/snapshots/solarized_light-tiny-skia.sha256 @@ -1 +1 @@ -04fcb0da640c6e7c1d86c95c74570c9f9d8f56056c9802eef332187f52c003ca \ No newline at end of file +7be707e7b8fb91e82c3cc33bf6c12e5c1a37db65616a35f5999351c26bbd0e50 \ 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 b4afd06f..2607513b 100644 --- a/examples/styling/snapshots/tokyo_night-tiny-skia.sha256 +++ b/examples/styling/snapshots/tokyo_night-tiny-skia.sha256 @@ -1 +1 @@ -26e9660da3caa88e5a995739c986b142487f1449c1a5dc555abf7bc7cbca2345 \ No newline at end of file +3c0b41e13decdc826ddf05e3805c33f624bccc1f1fbe39b2fb8e6880ad18e472 \ 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 29ba7bca..77b3136d 100644 --- a/examples/styling/snapshots/tokyo_night_light-tiny-skia.sha256 +++ b/examples/styling/snapshots/tokyo_night_light-tiny-skia.sha256 @@ -1 +1 @@ -8d846a765ff96506ad4f26a672976cb1bfa997d4b09c5ecabf273d5e22ae0a3a \ No newline at end of file +1bb6f75eb390cae16dab87947623e15f0e7dafdb79e89f700d0fbf16129ffd1b \ 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 b78e04b9..934b9f5d 100644 --- a/examples/styling/src/main.rs +++ b/examples/styling/src/main.rs @@ -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) From 946e2a73b780182f7002452fd2645d10b3f24064 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Fri, 27 Jun 2025 18:46:25 +0200 Subject: [PATCH 5/5] Implement `Rule` support in `markdown` widget --- widget/src/markdown.rs | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/widget/src/markdown.rs b/widget/src/markdown.rs index 3fc18bc3..be88c5f9 100644 --- a/widget/src/markdown.rs +++ b/widget/src/markdown.rs @@ -51,8 +51,8 @@ use crate::core::{ self, Color, Element, Length, Padding, Pixels, Theme, color, }; use crate::{ - column, container, rich_text, row, rule, scrollable, span, text, - vertical_rule, + column, container, horizontal_rule, rich_text, row, rule, scrollable, span, + text, vertical_rule, }; use std::borrow::BorrowMut; @@ -213,6 +213,8 @@ pub enum Item { }, /// A quote. Quote(Vec), + /// A horizontal separator. + Rule, } /// A bunch of parsed Markdown text. @@ -877,6 +879,9 @@ fn parse_with<'a>( }); None } + pulldown_cmark::Event::Rule => { + produce(state.borrow_mut(), &mut stack, Item::Rule, source) + } _ => None, }) } @@ -1107,6 +1112,7 @@ where items, } => viewer.ordered_list(settings, *start, items), Item::Quote(quote) => viewer.quote(settings, quote), + Item::Rule => viewer.rule(settings), } } @@ -1296,6 +1302,17 @@ where .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 @@ -1402,6 +1419,16 @@ where ) -> 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)]