summaryrefslogtreecommitdiffstats
path: root/examples/markdown
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón Jiménez <hector@hecrj.dev>2024-07-18 14:34:00 +0200
committerLibravatar Héctor Ramón Jiménez <hector@hecrj.dev>2024-07-18 14:34:00 +0200
commit47b7a36f36b99e346909390621b04f6691ff46d4 (patch)
treeab3d5c7ca7c81eb14fd5e2b0332e63fe68a6a7f8 /examples/markdown
parentaa62fa2ce992949d20ddbe8683ed2be0d922a568 (diff)
downloadiced-47b7a36f36b99e346909390621b04f6691ff46d4.tar.gz
iced-47b7a36f36b99e346909390621b04f6691ff46d4.tar.bz2
iced-47b7a36f36b99e346909390621b04f6691ff46d4.zip
Create `markdown` widget helpers in `iced_widget`
Diffstat (limited to 'examples/markdown')
-rw-r--r--examples/markdown/Cargo.toml4
-rw-r--r--examples/markdown/src/main.rs224
2 files changed, 10 insertions, 218 deletions
diff --git a/examples/markdown/Cargo.toml b/examples/markdown/Cargo.toml
index 6875ee94..9404d5d2 100644
--- a/examples/markdown/Cargo.toml
+++ b/examples/markdown/Cargo.toml
@@ -7,6 +7,4 @@ publish = false
[dependencies]
iced.workspace = true
-iced.features = ["highlighter", "debug"]
-
-pulldown-cmark = "0.11"
+iced.features = ["markdown", "highlighter", "debug"]
diff --git a/examples/markdown/src/main.rs b/examples/markdown/src/main.rs
index 1e3769ff..28b5941f 100644
--- a/examples/markdown/src/main.rs
+++ b/examples/markdown/src/main.rs
@@ -1,7 +1,4 @@
-use iced::widget::{
- self, column, container, rich_text, row, scrollable, span, text,
- text_editor,
-};
+use iced::widget::{self, markdown, row, scrollable, text_editor};
use iced::{Element, Fill, Font, Task, Theme};
pub fn main() -> iced::Result {
@@ -12,7 +9,7 @@ pub fn main() -> iced::Result {
struct Markdown {
content: text_editor::Content,
- items: Vec<Item>,
+ items: Vec<markdown::Item>,
theme: Theme,
}
@@ -30,7 +27,8 @@ impl Markdown {
(
Self {
content: text_editor::Content::with_text(INITIAL_CONTENT),
- items: parse(INITIAL_CONTENT, &theme).collect(),
+ items: markdown::parse(INITIAL_CONTENT, theme.palette())
+ .collect(),
theme,
},
widget::focus_next(),
@@ -44,8 +42,11 @@ impl Markdown {
self.content.perform(action);
if is_edit {
- self.items =
- parse(&self.content.text(), &self.theme).collect();
+ self.items = markdown::parse(
+ &self.content.text(),
+ self.theme.palette(),
+ )
+ .collect();
}
}
}
@@ -70,210 +71,3 @@ impl Markdown {
Theme::TokyoNight
}
}
-
-fn markdown<'a>(
- items: impl IntoIterator<Item = &'a Item>,
-) -> Element<'a, Message> {
- use iced::padding;
-
- let blocks = items.into_iter().enumerate().map(|(i, item)| match item {
- Item::Heading(heading) => container(rich_text(heading))
- .padding(padding::top(if i > 0 { 8 } else { 0 }))
- .into(),
- Item::Paragraph(paragraph) => rich_text(paragraph).into(),
- Item::List { start: None, items } => column(
- items
- .iter()
- .map(|item| row!["•", rich_text(item)].spacing(10).into()),
- )
- .spacing(10)
- .into(),
- Item::List {
- start: Some(start),
- items,
- } => column(items.iter().enumerate().map(|(i, item)| {
- row![text!("{}.", i as u64 + *start), rich_text(item)]
- .spacing(10)
- .into()
- }))
- .spacing(10)
- .into(),
- Item::CodeBlock(code) => {
- container(rich_text(code).font(Font::MONOSPACE).size(12))
- .width(Fill)
- .padding(10)
- .style(container::rounded_box)
- .into()
- }
- });
-
- column(blocks).width(Fill).spacing(16).into()
-}
-
-#[derive(Debug, Clone)]
-enum Item {
- Heading(Vec<text::Span<'static>>),
- Paragraph(Vec<text::Span<'static>>),
- CodeBlock(Vec<text::Span<'static>>),
- List {
- start: Option<u64>,
- items: Vec<Vec<text::Span<'static>>>,
- },
-}
-
-fn parse<'a>(
- markdown: &'a str,
- theme: &'a Theme,
-) -> impl Iterator<Item = Item> + 'a {
- use iced::font;
- use iced::highlighter::{self, Highlighter};
- use text::Highlighter as _;
-
- let mut spans = Vec::new();
- let mut heading = None;
- let mut strong = false;
- let mut emphasis = false;
- let mut link = false;
- let mut list = Vec::new();
- let mut list_start = None;
- let mut highlighter = None;
-
- let parser = pulldown_cmark::Parser::new(markdown);
-
- // We want to keep the `spans` capacity
- #[allow(clippy::drain_collect)]
- parser.filter_map(move |event| match event {
- pulldown_cmark::Event::Start(tag) => match tag {
- pulldown_cmark::Tag::Heading { level, .. } => {
- heading = Some(level);
- None
- }
- pulldown_cmark::Tag::Strong => {
- strong = true;
- None
- }
- pulldown_cmark::Tag::Emphasis => {
- emphasis = true;
- None
- }
- pulldown_cmark::Tag::Link { .. } => {
- link = true;
- None
- }
- pulldown_cmark::Tag::List(first_item) => {
- list_start = first_item;
- None
- }
- pulldown_cmark::Tag::CodeBlock(
- pulldown_cmark::CodeBlockKind::Fenced(language),
- ) => {
- highlighter = Some(Highlighter::new(&highlighter::Settings {
- theme: highlighter::Theme::Base16Ocean,
- token: language.to_string(),
- }));
-
- None
- }
- _ => None,
- },
- pulldown_cmark::Event::End(tag) => match tag {
- pulldown_cmark::TagEnd::Heading(_) => {
- heading = None;
- Some(Item::Heading(spans.drain(..).collect()))
- }
- pulldown_cmark::TagEnd::Emphasis => {
- emphasis = false;
- None
- }
- pulldown_cmark::TagEnd::Strong => {
- strong = false;
- None
- }
- pulldown_cmark::TagEnd::Link => {
- link = false;
- None
- }
- pulldown_cmark::TagEnd::Paragraph => {
- Some(Item::Paragraph(spans.drain(..).collect()))
- }
- pulldown_cmark::TagEnd::List(_) => Some(Item::List {
- start: list_start,
- items: list.drain(..).collect(),
- }),
- pulldown_cmark::TagEnd::Item => {
- list.push(spans.drain(..).collect());
- None
- }
- pulldown_cmark::TagEnd::CodeBlock => {
- highlighter = None;
- Some(Item::CodeBlock(spans.drain(..).collect()))
- }
- _ => None,
- },
- pulldown_cmark::Event::Text(text) => {
- if let Some(highlighter) = &mut highlighter {
- for (range, highlight) in
- highlighter.highlight_line(text.as_ref())
- {
- let span = span(text[range].to_owned())
- .color_maybe(highlight.color())
- .font_maybe(highlight.font());
-
- spans.push(span);
- }
- } else {
- let span = span(text.into_string());
-
- let span = match heading {
- None => span,
- Some(heading) => span.size(match heading {
- pulldown_cmark::HeadingLevel::H1 => 32,
- pulldown_cmark::HeadingLevel::H2 => 28,
- pulldown_cmark::HeadingLevel::H3 => 24,
- pulldown_cmark::HeadingLevel::H4 => 20,
- pulldown_cmark::HeadingLevel::H5 => 16,
- pulldown_cmark::HeadingLevel::H6 => 16,
- }),
- };
-
- let span = if strong || emphasis {
- span.font(Font {
- weight: if strong {
- font::Weight::Bold
- } else {
- font::Weight::Normal
- },
- style: if emphasis {
- font::Style::Italic
- } else {
- font::Style::Normal
- },
- ..Font::default()
- })
- } else {
- span
- };
-
- let span =
- span.color_maybe(link.then(|| theme.palette().primary));
-
- spans.push(span);
- }
-
- None
- }
- pulldown_cmark::Event::Code(code) => {
- spans.push(span(code.into_string()).font(Font::MONOSPACE));
- None
- }
- pulldown_cmark::Event::SoftBreak => {
- spans.push(span(" "));
- None
- }
- pulldown_cmark::Event::HardBreak => {
- spans.push(span("\n"));
- None
- }
- _ => None,
- })
-}