Merge pull request #3005 from iced-rs/feature/markdown-quotes
Quotes for `markdown` widget
This commit is contained in:
commit
2a144e7e76
27 changed files with 190 additions and 48 deletions
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
12ba47a34ed415825a23f8ef377a2d52950d2f8614a66bf46c0ec28d0cf15c85
|
||||
30570747bb062e9f7730cdd58be961c84bcf4711a6983185bff6d903e8d29e9c
|
||||
|
|
@ -1 +1 @@
|
|||
fa00d7e0ff95b366945d409712d7fe4ce44fff22425236cb56b8b96a88815ee6
|
||||
039a950d09d34df222606eaf2166e65f86d5536f8abfaff0e295219c974d54c0
|
||||
|
|
@ -1 +1 @@
|
|||
4e594cfec775d51f7f836646c59bf4a2de07252721d66ddddea69c17e9112bae
|
||||
30e523961db89a3ee97ad1eac09e727ecb3dec485faa362534a9f5ad083b32dd
|
||||
|
|
@ -1 +1 @@
|
|||
2ab665b51387c61086ae0199c29e291105bfe4583bd4c4daa652e30917f10bd6
|
||||
bce5427d5105f68e1d7fa18a34fcc551cb78c2fefd9a583ba44686331133436d
|
||||
|
|
@ -1 +1 @@
|
|||
61c9ee377b33ffa800f512877e45ad5f41fbac36f5d3f06d1b62d6af6ee9d7b2
|
||||
c8a7edbd5a8bbf559134b84253e14e65340f4ffe3e22c272b21c8438e47ffaf7
|
||||
|
|
@ -1 +1 @@
|
|||
75f2fb12c9090a256708515de01a25e78905f71e134b7cda79f4fe44b2434052
|
||||
63d646b22d3dffbb56dac2e3f345090bd26625a388dd6cc142359f2a7ac9c8df
|
||||
|
|
@ -1 +1 @@
|
|||
b4a1b42d2e21b2a493605745e6beb8e1f28cbeb01b73336e1e8d9061249a8311
|
||||
d26f55674cbd96bc3b534ffdd098a13199718ef9c5ffe8ece0882ddab714b776
|
||||
|
|
@ -1 +1 @@
|
|||
eb52921b3ee23e1814268701c935d0dff387e7eb741c50443f75a7ab902b5e44
|
||||
482c44c13d4ff3de19e71f3dddf93bbee170e54e2d353e818811069de28e18ed
|
||||
|
|
@ -1 +1 @@
|
|||
bf6c4cbd6eeed0167d28509e37292f5ce26ed1d58bb156bedb861d0619a1945b
|
||||
6738cc4fc6eb8a5d406c613a4b0f08c0e8dcd2c1a5444445eebd3888f9303841
|
||||
|
|
@ -1 +1 @@
|
|||
83726a4175900a9ef159b80925f2fa985b4ea87bff78d8bb01918fb6c40d6175
|
||||
0a918c52538fc4848aa0c68d8f2d6f4c981ed68971dd9c725f0093a39ef7f353
|
||||
|
|
@ -1 +1 @@
|
|||
54c8d44afbdd644f324cf40e744b3d7871263e44de2d9a91f6c10470c38ac3c6
|
||||
de3e1a2c21e1a86d76ca99989c73e8a2596ef627bba95d246fab8f02d56bd0af
|
||||
|
|
@ -1 +1 @@
|
|||
5713843396e2efcfc7cc8abd00343d5d66ce8b8a195212a9b75dbfeec8edf7a7
|
||||
3418ea4eb0f7786607ef02e7db4bc97309530f2f7c08f8aea15c768a13a09ca4
|
||||
|
|
@ -1 +1 @@
|
|||
f1b20ab79f8242776d9eb1ad9cff7090435aa416811c48a7c22c69b09cd8e70f
|
||||
c8474e02a9df23f123816a489c1ea7ae6cb994a0eca429592dfe6d933de1beee
|
||||
|
|
@ -1 +1 @@
|
|||
2c4be9dc1340b65cad6d15d5318017412eba1247a016379b83db379dde0214af
|
||||
02095fd09c078be02dc41e29e55de25e8a79e6ad4293aa7e430257a9016dfb3d
|
||||
|
|
@ -1 +1 @@
|
|||
2d5d6b9613ccb6a2f23142baf704288037808e7a60ee05bdc73490d8c8780064
|
||||
d82588a2aba3e7211f25b85ebb812a42dfa59137dd4b59d26f5f60d5b28e537f
|
||||
|
|
@ -1 +1 @@
|
|||
512b8dfd4687a609d202436e75ecf1a5553acc2157000e77e31c1578941b033c
|
||||
d6b73545929cc7794c1a918f069b5326ef129bed8f9ad2cd001be7d078a2b6a0
|
||||
|
|
@ -1 +1 @@
|
|||
4b3b7a2dc65307a3551227f1c5d2bb49da15d29e320ccaa160e3d9fca44c1037
|
||||
0ec7251c69755becd678b7aec398a275edf31cc077960723cd6b9364e8678548
|
||||
|
|
@ -1 +1 @@
|
|||
15aa476c65304bde23e3648ceb424d6394f525f6c1d3e49ee1150376b6fd3068
|
||||
d87f3a00de5ee52db12a21c09e8689d8d489cce6eb8c8ce1b89192b85587e9a0
|
||||
|
|
@ -1 +1 @@
|
|||
04fcb0da640c6e7c1d86c95c74570c9f9d8f56056c9802eef332187f52c003ca
|
||||
7be707e7b8fb91e82c3cc33bf6c12e5c1a37db65616a35f5999351c26bbd0e50
|
||||
|
|
@ -1 +1 @@
|
|||
26e9660da3caa88e5a995739c986b142487f1449c1a5dc555abf7bc7cbca2345
|
||||
3c0b41e13decdc826ddf05e3805c33f624bccc1f1fbe39b2fb8e6880ad18e472
|
||||
|
|
@ -1 +1 @@
|
|||
8d846a765ff96506ad4f26a672976cb1bfa997d4b09c5ecabf273d5e22ae0a3a
|
||||
1bb6f75eb390cae16dab87947623e15f0e7dafdb79e89f700d0fbf16129ffd1b
|
||||
|
|
@ -1 +1 @@
|
|||
0ecd51994f6eb37f111dc1b21cad72bb705499eb83156e9dc3ae2221ec392a42
|
||||
533d25575e8bf1111036fb082b424d0d0e60947a7da8428ab8c71e0bda01469e
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<Item>),
|
||||
/// A horizontal separator.
|
||||
Rule,
|
||||
}
|
||||
|
||||
/// A bunch of parsed Markdown text.
|
||||
|
|
@ -454,6 +461,7 @@ fn parse_with<'a>(
|
|||
) -> impl Iterator<Item = (Item, &'a str, HashSet<String>)> + 'a {
|
||||
enum Scope {
|
||||
List(List),
|
||||
Quote(Vec<Item>),
|
||||
}
|
||||
|
||||
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<Font = Font> + '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<Font = Font> + '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>() -> <Self as container::Catalog>::Class<'a>;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue