diff options
author | 2024-07-18 14:34:00 +0200 | |
---|---|---|
committer | 2024-07-18 14:34:00 +0200 | |
commit | 47b7a36f36b99e346909390621b04f6691ff46d4 (patch) | |
tree | ab3d5c7ca7c81eb14fd5e2b0332e63fe68a6a7f8 /examples/markdown | |
parent | aa62fa2ce992949d20ddbe8683ed2be0d922a568 (diff) | |
download | iced-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.toml | 4 | ||||
-rw-r--r-- | examples/markdown/src/main.rs | 224 |
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, - }) -} |