Implement Quote support in markdown widget
This commit is contained in:
parent
5ad08acd97
commit
ca1bf717b3
3 changed files with 91 additions and 17 deletions
|
|
@ -400,9 +400,9 @@ where
|
||||||
Renderer: core::Renderer + 'a,
|
Renderer: core::Renderer + 'a,
|
||||||
{
|
{
|
||||||
fn from(
|
fn from(
|
||||||
column: Container<'a, Message, Theme, Renderer>,
|
container: Container<'a, Message, Theme, Renderer>,
|
||||||
) -> Element<'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::{
|
use crate::core::{
|
||||||
self, Color, Element, Length, Padding, Pixels, Theme, color,
|
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::borrow::BorrowMut;
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
|
|
@ -208,6 +211,8 @@ pub enum Item {
|
||||||
/// The alternative text of the image.
|
/// The alternative text of the image.
|
||||||
alt: Text,
|
alt: Text,
|
||||||
},
|
},
|
||||||
|
/// A quote.
|
||||||
|
Quote(Vec<Item>),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A bunch of parsed Markdown text.
|
/// A bunch of parsed Markdown text.
|
||||||
|
|
@ -454,6 +459,7 @@ fn parse_with<'a>(
|
||||||
) -> impl Iterator<Item = (Item, &'a str, HashSet<String>)> + 'a {
|
) -> impl Iterator<Item = (Item, &'a str, HashSet<String>)> + 'a {
|
||||||
enum Scope {
|
enum Scope {
|
||||||
List(List),
|
List(List),
|
||||||
|
Quote(Vec<Item>),
|
||||||
}
|
}
|
||||||
|
|
||||||
struct List {
|
struct List {
|
||||||
|
|
@ -524,6 +530,9 @@ fn parse_with<'a>(
|
||||||
Scope::List(list) => {
|
Scope::List(list) => {
|
||||||
list.items.last_mut().expect("item context").push(item);
|
list.items.last_mut().expect("item context").push(item);
|
||||||
}
|
}
|
||||||
|
Scope::Quote(items) => {
|
||||||
|
items.push(item);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
|
|
@ -605,6 +614,22 @@ fn parse_with<'a>(
|
||||||
|
|
||||||
None
|
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::Tag::CodeBlock(
|
||||||
pulldown_cmark::CodeBlockKind::Fenced(language),
|
pulldown_cmark::CodeBlockKind::Fenced(language),
|
||||||
) if !metadata && !table => {
|
) if !metadata && !table => {
|
||||||
|
|
@ -703,7 +728,9 @@ fn parse_with<'a>(
|
||||||
pulldown_cmark::TagEnd::List(_) if !metadata && !table => {
|
pulldown_cmark::TagEnd::List(_) if !metadata && !table => {
|
||||||
let scope = stack.pop()?;
|
let scope = stack.pop()?;
|
||||||
|
|
||||||
let Scope::List(list) = scope;
|
let Scope::List(list) = scope else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
produce(
|
produce(
|
||||||
state.borrow_mut(),
|
state.borrow_mut(),
|
||||||
|
|
@ -715,6 +742,22 @@ fn parse_with<'a>(
|
||||||
source,
|
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 => {
|
pulldown_cmark::TagEnd::Image if !metadata && !table => {
|
||||||
let (url, title) = image.take()?;
|
let (url, title) = image.take()?;
|
||||||
let alt = Text::new(spans.drain(..).collect());
|
let alt = Text::new(spans.drain(..).collect());
|
||||||
|
|
@ -1063,6 +1106,7 @@ where
|
||||||
start: Some(start),
|
start: Some(start),
|
||||||
items,
|
items,
|
||||||
} => viewer.ordered_list(settings, *start, items),
|
} => viewer.ordered_list(settings, *start, items),
|
||||||
|
Item::Quote(quote) => viewer.quote(settings, quote),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1226,7 +1270,33 @@ where
|
||||||
.into()
|
.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()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A view strategy to display a Markdown [`Item`].
|
||||||
pub trait Viewer<'a, Message, Theme = crate::Theme, Renderer = crate::Renderer>
|
pub trait Viewer<'a, Message, Theme = crate::Theme, Renderer = crate::Renderer>
|
||||||
where
|
where
|
||||||
Self: Sized + 'a,
|
Self: Sized + 'a,
|
||||||
|
|
@ -1321,6 +1391,17 @@ where
|
||||||
) -> Element<'a, Message, Theme, Renderer> {
|
) -> Element<'a, Message, Theme, Renderer> {
|
||||||
ordered_list(self, settings, start, items)
|
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)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
|
@ -1338,7 +1419,7 @@ where
|
||||||
|
|
||||||
/// The theme catalog of Markdown items.
|
/// The theme catalog of Markdown items.
|
||||||
pub trait Catalog:
|
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.
|
/// The styling class of a Markdown code block.
|
||||||
fn code_block<'a>() -> <Self as container::Catalog>::Class<'a>;
|
fn code_block<'a>() -> <Self as container::Catalog>::Class<'a>;
|
||||||
|
|
|
||||||
|
|
@ -134,9 +134,7 @@ where
|
||||||
let style = theme.style(&self.class);
|
let style = theme.style(&self.class);
|
||||||
|
|
||||||
let bounds = if self.is_horizontal {
|
let bounds = if self.is_horizontal {
|
||||||
let line_y = (bounds.y + (bounds.height / 2.0)
|
let line_y = (bounds.y + (bounds.height / 2.0)).round();
|
||||||
- (style.width as f32 / 2.0))
|
|
||||||
.round();
|
|
||||||
|
|
||||||
let (offset, line_width) = style.fill_mode.fill(bounds.width);
|
let (offset, line_width) = style.fill_mode.fill(bounds.width);
|
||||||
let line_x = bounds.x + offset;
|
let line_x = bounds.x + offset;
|
||||||
|
|
@ -145,12 +143,10 @@ where
|
||||||
x: line_x,
|
x: line_x,
|
||||||
y: line_y,
|
y: line_y,
|
||||||
width: line_width,
|
width: line_width,
|
||||||
height: style.width as f32,
|
height: bounds.height,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let line_x = (bounds.x + (bounds.width / 2.0)
|
let line_x = (bounds.x + (bounds.width / 2.0)).round();
|
||||||
- (style.width as f32 / 2.0))
|
|
||||||
.round();
|
|
||||||
|
|
||||||
let (offset, line_height) = style.fill_mode.fill(bounds.height);
|
let (offset, line_height) = style.fill_mode.fill(bounds.height);
|
||||||
let line_y = bounds.y + offset;
|
let line_y = bounds.y + offset;
|
||||||
|
|
@ -158,7 +154,7 @@ where
|
||||||
Rectangle {
|
Rectangle {
|
||||||
x: line_x,
|
x: line_x,
|
||||||
y: line_y,
|
y: line_y,
|
||||||
width: style.width as f32,
|
width: bounds.width,
|
||||||
height: line_height,
|
height: line_height,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -192,8 +188,6 @@ where
|
||||||
pub struct Style {
|
pub struct Style {
|
||||||
/// The color of the rule.
|
/// The color of the rule.
|
||||||
pub color: Color,
|
pub color: Color,
|
||||||
/// The width (thickness) of the rule line.
|
|
||||||
pub width: u16,
|
|
||||||
/// The radius of the line corners.
|
/// The radius of the line corners.
|
||||||
pub radius: border::Radius,
|
pub radius: border::Radius,
|
||||||
/// The [`FillMode`] of the rule.
|
/// The [`FillMode`] of the rule.
|
||||||
|
|
@ -301,7 +295,6 @@ pub fn default(theme: &Theme) -> Style {
|
||||||
|
|
||||||
Style {
|
Style {
|
||||||
color: palette.background.strong.color,
|
color: palette.background.strong.color,
|
||||||
width: 1,
|
|
||||||
radius: 0.0.into(),
|
radius: 0.0.into(),
|
||||||
fill_mode: FillMode::Full,
|
fill_mode: FillMode::Full,
|
||||||
snap: true,
|
snap: true,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue