Skip to content

Commit c02ae0c

Browse files
committed
Introduce view_with customizers in markdown module
1 parent 782b96b commit c02ae0c

File tree

1 file changed

+186
-94
lines changed

1 file changed

+186
-94
lines changed

widget/src/markdown.rs

Lines changed: 186 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,130 @@ pub enum Item {
189189
},
190190
}
191191

192+
impl Item {
193+
/// Displays a Markdown [`Item`] using the default, built-in look for its children.
194+
pub fn view<'a, 'b, Theme, Renderer>(
195+
&'b self,
196+
settings: Settings,
197+
style: Style,
198+
index: usize,
199+
) -> Element<'a, Url, Theme, Renderer>
200+
where
201+
Theme: Catalog + 'a,
202+
Renderer: core::text::Renderer<Font = Font> + 'a,
203+
{
204+
self.view_with(index, settings, style, &DefaultView)
205+
}
206+
207+
/// Displays a Markdown [`Item`] using the given [`View`] for its children.
208+
pub fn view_with<'a, 'b, Theme, Renderer>(
209+
&'b self,
210+
index: usize,
211+
settings: Settings,
212+
style: Style,
213+
view: &dyn View<'a, 'b, Url, Theme, Renderer>,
214+
) -> Element<'a, Url, Theme, Renderer>
215+
where
216+
Theme: Catalog + 'a,
217+
Renderer: core::text::Renderer<Font = Font> + 'a,
218+
{
219+
let Settings {
220+
text_size,
221+
h1_size,
222+
h2_size,
223+
h3_size,
224+
h4_size,
225+
h5_size,
226+
h6_size,
227+
code_size,
228+
spacing,
229+
} = settings;
230+
231+
match self {
232+
Item::Heading(level, heading) => {
233+
container(rich_text(heading.spans(style)).size(match level {
234+
pulldown_cmark::HeadingLevel::H1 => h1_size,
235+
pulldown_cmark::HeadingLevel::H2 => h2_size,
236+
pulldown_cmark::HeadingLevel::H3 => h3_size,
237+
pulldown_cmark::HeadingLevel::H4 => h4_size,
238+
pulldown_cmark::HeadingLevel::H5 => h5_size,
239+
pulldown_cmark::HeadingLevel::H6 => h6_size,
240+
}))
241+
.padding(padding::top(if index > 0 {
242+
text_size / 2.0
243+
} else {
244+
Pixels::ZERO
245+
}))
246+
.into()
247+
}
248+
Item::Paragraph(paragraph) => {
249+
rich_text(paragraph.spans(style)).size(text_size).into()
250+
}
251+
Item::List { start: None, items } => {
252+
column(items.iter().map(|items| {
253+
row![
254+
text("•").size(text_size),
255+
view_with(
256+
items,
257+
Settings {
258+
spacing: settings.spacing * 0.6,
259+
..settings
260+
},
261+
style,
262+
view
263+
)
264+
]
265+
.spacing(spacing)
266+
.into()
267+
}))
268+
.spacing(spacing * 0.75)
269+
.into()
270+
}
271+
Item::List {
272+
start: Some(start),
273+
items,
274+
} => column(items.iter().enumerate().map(|(i, items)| {
275+
row![
276+
text!("{}.", i as u64 + *start).size(text_size),
277+
view_with(
278+
items,
279+
Settings {
280+
spacing: settings.spacing * 0.6,
281+
..settings
282+
},
283+
style,
284+
view
285+
)
286+
]
287+
.spacing(spacing)
288+
.into()
289+
}))
290+
.spacing(spacing * 0.75)
291+
.into(),
292+
Item::CodeBlock(lines) => container(
293+
scrollable(
294+
container(column(lines.iter().map(|line| {
295+
rich_text(line.spans(style))
296+
.font(Font::MONOSPACE)
297+
.size(code_size)
298+
.into()
299+
})))
300+
.padding(spacing.0 / 2.0),
301+
)
302+
.direction(scrollable::Direction::Horizontal(
303+
scrollable::Scrollbar::default()
304+
.width(spacing.0 / 2.0)
305+
.scroller_width(spacing.0 / 2.0),
306+
)),
307+
)
308+
.width(Length::Fill)
309+
.padding(spacing.0 / 2.0)
310+
.class(Theme::code_block())
311+
.into(),
312+
}
313+
}
314+
}
315+
192316
/// A bunch of parsed Markdown text.
193317
#[derive(Debug, Clone)]
194318
pub struct Text {
@@ -900,100 +1024,68 @@ where
9001024
Theme: Catalog + 'a,
9011025
Renderer: core::text::Renderer<Font = Font> + 'a,
9021026
{
903-
let Settings {
904-
text_size,
905-
h1_size,
906-
h2_size,
907-
h3_size,
908-
h4_size,
909-
h5_size,
910-
h6_size,
911-
code_size,
912-
spacing,
913-
} = settings;
914-
915-
let blocks = items.into_iter().enumerate().map(|(i, item)| match item {
916-
Item::Heading(level, heading) => {
917-
container(rich_text(heading.spans(style)).size(match level {
918-
pulldown_cmark::HeadingLevel::H1 => h1_size,
919-
pulldown_cmark::HeadingLevel::H2 => h2_size,
920-
pulldown_cmark::HeadingLevel::H3 => h3_size,
921-
pulldown_cmark::HeadingLevel::H4 => h4_size,
922-
pulldown_cmark::HeadingLevel::H5 => h5_size,
923-
pulldown_cmark::HeadingLevel::H6 => h6_size,
924-
}))
925-
.padding(padding::top(if i > 0 {
926-
text_size / 2.0
927-
} else {
928-
Pixels::ZERO
929-
}))
930-
.into()
931-
}
932-
Item::Paragraph(paragraph) => {
933-
rich_text(paragraph.spans(style)).size(text_size).into()
934-
}
935-
Item::List { start: None, items } => {
936-
column(items.iter().map(|items| {
937-
row![
938-
text("•").size(text_size),
939-
view(
940-
items,
941-
Settings {
942-
spacing: settings.spacing * 0.6,
943-
..settings
944-
},
945-
style
946-
)
947-
]
948-
.spacing(spacing)
949-
.into()
950-
}))
951-
.spacing(spacing * 0.75)
952-
.into()
953-
}
954-
Item::List {
955-
start: Some(start),
956-
items,
957-
} => column(items.iter().enumerate().map(|(i, items)| {
958-
row![
959-
text!("{}.", i as u64 + *start).size(text_size),
960-
view(
961-
items,
962-
Settings {
963-
spacing: settings.spacing * 0.6,
964-
..settings
965-
},
966-
style
967-
)
968-
]
969-
.spacing(spacing)
970-
.into()
971-
}))
972-
.spacing(spacing * 0.75)
973-
.into(),
974-
Item::CodeBlock(lines) => container(
975-
scrollable(
976-
container(column(lines.iter().map(|line| {
977-
rich_text(line.spans(style))
978-
.font(Font::MONOSPACE)
979-
.size(code_size)
980-
.into()
981-
})))
982-
.padding(spacing.0 / 2.0),
983-
)
984-
.direction(scrollable::Direction::Horizontal(
985-
scrollable::Scrollbar::default()
986-
.width(spacing.0 / 2.0)
987-
.scroller_width(spacing.0 / 2.0),
988-
)),
989-
)
990-
.width(Length::Fill)
991-
.padding(spacing.0 / 2.0)
992-
.class(Theme::code_block())
993-
.into(),
994-
});
995-
996-
Element::new(column(blocks).spacing(spacing))
1027+
view_with(items, settings, style, &DefaultView)
1028+
}
1029+
1030+
/// Runs [`view`] but with a custom [`View`] to turn an [`Item`] into
1031+
/// an [`Element`].
1032+
///
1033+
/// This is useful if you want to customize the look of certain Markdown
1034+
/// elements.
1035+
///
1036+
/// You can use [`Item::view`] and [`Item::view_with`] for the default
1037+
/// look.
1038+
pub fn view_with<'a, 'b, Message, Theme, Renderer>(
1039+
items: impl IntoIterator<Item = &'b Item>,
1040+
settings: Settings,
1041+
style: Style,
1042+
view: &dyn View<'a, 'b, Message, Theme, Renderer>,
1043+
) -> Element<'a, Message, Theme, Renderer>
1044+
where
1045+
Message: 'a,
1046+
Theme: Catalog + 'a,
1047+
Renderer: core::text::Renderer<Font = Font> + 'a,
1048+
{
1049+
let blocks = items
1050+
.into_iter()
1051+
.enumerate()
1052+
.map(move |(i, item)| view.view(settings, style, item, i));
1053+
1054+
Element::new(column(blocks).spacing(settings.spacing))
1055+
}
1056+
1057+
/// A view strategy to display a Markdown [`Item`].
1058+
pub trait View<'a, 'b, Message, Theme, Renderer> {
1059+
/// Displays a Markdown [`Item`] by projecting it into an [`Element`].
1060+
///
1061+
/// You can use [`Item::view`] and [`Item::view_with`] for the default
1062+
/// look.
1063+
fn view(
1064+
&self,
1065+
settings: Settings,
1066+
style: Style,
1067+
item: &'b Item,
1068+
index: usize,
1069+
) -> Element<'a, Message, Theme, Renderer>;
1070+
}
1071+
1072+
#[derive(Debug, Clone, Copy)]
1073+
struct DefaultView;
1074+
1075+
impl<'a, 'b, Theme, Renderer> View<'a, 'b, Url, Theme, Renderer> for DefaultView
1076+
where
1077+
Theme: Catalog + 'a,
1078+
Renderer: core::text::Renderer<Font = Font> + 'a,
1079+
{
1080+
fn view(
1081+
&self,
1082+
settings: Settings,
1083+
style: Style,
1084+
item: &'b Item,
1085+
index: usize,
1086+
) -> Element<'a, Url, Theme, Renderer> {
1087+
item.view(settings, style, index)
1088+
}
9971089
}
9981090

9991091
/// The theme catalog of Markdown items.

0 commit comments

Comments
 (0)