Skip to content

Commit

Permalink
Introduce view_with customizers in markdown module
Browse files Browse the repository at this point in the history
  • Loading branch information
hecrj committed Feb 4, 2025
1 parent 782b96b commit c02ae0c
Showing 1 changed file with 186 additions and 94 deletions.
280 changes: 186 additions & 94 deletions widget/src/markdown.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,130 @@ pub enum Item {
},
}

impl Item {
/// Displays a Markdown [`Item`] using the default, built-in look for its children.
pub fn view<'a, 'b, Theme, Renderer>(
&'b self,
settings: Settings,
style: Style,
index: usize,
) -> Element<'a, Url, Theme, Renderer>
where
Theme: Catalog + 'a,
Renderer: core::text::Renderer<Font = Font> + 'a,
{
self.view_with(index, settings, style, &DefaultView)
}

/// Displays a Markdown [`Item`] using the given [`View`] for its children.
pub fn view_with<'a, 'b, Theme, Renderer>(
&'b self,
index: usize,
settings: Settings,
style: Style,
view: &dyn View<'a, 'b, Url, Theme, Renderer>,
) -> Element<'a, Url, Theme, Renderer>
where
Theme: Catalog + 'a,
Renderer: core::text::Renderer<Font = Font> + 'a,
{
let Settings {
text_size,
h1_size,
h2_size,
h3_size,
h4_size,
h5_size,
h6_size,
code_size,
spacing,
} = settings;

match self {
Item::Heading(level, heading) => {
container(rich_text(heading.spans(style)).size(match level {
pulldown_cmark::HeadingLevel::H1 => h1_size,
pulldown_cmark::HeadingLevel::H2 => h2_size,
pulldown_cmark::HeadingLevel::H3 => h3_size,
pulldown_cmark::HeadingLevel::H4 => h4_size,
pulldown_cmark::HeadingLevel::H5 => h5_size,
pulldown_cmark::HeadingLevel::H6 => h6_size,
}))
.padding(padding::top(if index > 0 {
text_size / 2.0
} else {
Pixels::ZERO
}))
.into()
}
Item::Paragraph(paragraph) => {
rich_text(paragraph.spans(style)).size(text_size).into()
}
Item::List { start: None, items } => {
column(items.iter().map(|items| {
row![
text("•").size(text_size),
view_with(
items,
Settings {
spacing: settings.spacing * 0.6,
..settings
},
style,
view
)
]
.spacing(spacing)
.into()
}))
.spacing(spacing * 0.75)
.into()
}
Item::List {
start: Some(start),
items,
} => column(items.iter().enumerate().map(|(i, items)| {
row![
text!("{}.", i as u64 + *start).size(text_size),
view_with(
items,
Settings {
spacing: settings.spacing * 0.6,
..settings
},
style,
view
)
]
.spacing(spacing)
.into()
}))
.spacing(spacing * 0.75)
.into(),
Item::CodeBlock(lines) => container(
scrollable(
container(column(lines.iter().map(|line| {
rich_text(line.spans(style))
.font(Font::MONOSPACE)
.size(code_size)
.into()
})))
.padding(spacing.0 / 2.0),
)
.direction(scrollable::Direction::Horizontal(
scrollable::Scrollbar::default()
.width(spacing.0 / 2.0)
.scroller_width(spacing.0 / 2.0),
)),
)
.width(Length::Fill)
.padding(spacing.0 / 2.0)
.class(Theme::code_block())
.into(),
}
}
}

/// A bunch of parsed Markdown text.
#[derive(Debug, Clone)]
pub struct Text {
Expand Down Expand Up @@ -900,100 +1024,68 @@ where
Theme: Catalog + 'a,
Renderer: core::text::Renderer<Font = Font> + 'a,
{
let Settings {
text_size,
h1_size,
h2_size,
h3_size,
h4_size,
h5_size,
h6_size,
code_size,
spacing,
} = settings;

let blocks = items.into_iter().enumerate().map(|(i, item)| match item {
Item::Heading(level, heading) => {
container(rich_text(heading.spans(style)).size(match level {
pulldown_cmark::HeadingLevel::H1 => h1_size,
pulldown_cmark::HeadingLevel::H2 => h2_size,
pulldown_cmark::HeadingLevel::H3 => h3_size,
pulldown_cmark::HeadingLevel::H4 => h4_size,
pulldown_cmark::HeadingLevel::H5 => h5_size,
pulldown_cmark::HeadingLevel::H6 => h6_size,
}))
.padding(padding::top(if i > 0 {
text_size / 2.0
} else {
Pixels::ZERO
}))
.into()
}
Item::Paragraph(paragraph) => {
rich_text(paragraph.spans(style)).size(text_size).into()
}
Item::List { start: None, items } => {
column(items.iter().map(|items| {
row![
text("•").size(text_size),
view(
items,
Settings {
spacing: settings.spacing * 0.6,
..settings
},
style
)
]
.spacing(spacing)
.into()
}))
.spacing(spacing * 0.75)
.into()
}
Item::List {
start: Some(start),
items,
} => column(items.iter().enumerate().map(|(i, items)| {
row![
text!("{}.", i as u64 + *start).size(text_size),
view(
items,
Settings {
spacing: settings.spacing * 0.6,
..settings
},
style
)
]
.spacing(spacing)
.into()
}))
.spacing(spacing * 0.75)
.into(),
Item::CodeBlock(lines) => container(
scrollable(
container(column(lines.iter().map(|line| {
rich_text(line.spans(style))
.font(Font::MONOSPACE)
.size(code_size)
.into()
})))
.padding(spacing.0 / 2.0),
)
.direction(scrollable::Direction::Horizontal(
scrollable::Scrollbar::default()
.width(spacing.0 / 2.0)
.scroller_width(spacing.0 / 2.0),
)),
)
.width(Length::Fill)
.padding(spacing.0 / 2.0)
.class(Theme::code_block())
.into(),
});

Element::new(column(blocks).spacing(spacing))
view_with(items, settings, style, &DefaultView)
}

/// Runs [`view`] but with a custom [`View`] to turn an [`Item`] into
/// an [`Element`].
///
/// This is useful if you want to customize the look of certain Markdown
/// elements.
///
/// You can use [`Item::view`] and [`Item::view_with`] for the default
/// look.
pub fn view_with<'a, 'b, Message, Theme, Renderer>(
items: impl IntoIterator<Item = &'b Item>,
settings: Settings,
style: Style,
view: &dyn View<'a, 'b, Message, Theme, Renderer>,
) -> Element<'a, Message, Theme, Renderer>
where
Message: 'a,
Theme: Catalog + 'a,
Renderer: core::text::Renderer<Font = Font> + 'a,
{
let blocks = items
.into_iter()
.enumerate()
.map(move |(i, item)| view.view(settings, style, item, i));

Element::new(column(blocks).spacing(settings.spacing))
}

/// A view strategy to display a Markdown [`Item`].
pub trait View<'a, 'b, Message, Theme, Renderer> {
/// Displays a Markdown [`Item`] by projecting it into an [`Element`].
///
/// You can use [`Item::view`] and [`Item::view_with`] for the default
/// look.
fn view(
&self,
settings: Settings,
style: Style,
item: &'b Item,
index: usize,
) -> Element<'a, Message, Theme, Renderer>;
}

#[derive(Debug, Clone, Copy)]
struct DefaultView;

impl<'a, 'b, Theme, Renderer> View<'a, 'b, Url, Theme, Renderer> for DefaultView
where
Theme: Catalog + 'a,
Renderer: core::text::Renderer<Font = Font> + 'a,
{
fn view(
&self,
settings: Settings,
style: Style,
item: &'b Item,
index: usize,
) -> Element<'a, Url, Theme, Renderer> {
item.view(settings, style, index)
}
}

/// The theme catalog of Markdown items.
Expand Down

0 comments on commit c02ae0c

Please sign in to comment.