diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/lib.rs | 2 | ||||
| -rw-r--r-- | src/markdown.rs | 255 |
2 files changed, 257 insertions, 0 deletions
@@ -2,11 +2,13 @@ //! //! [`Paragraph`]: https://docs.iced.rs/iced_graphics/text/paragraph/struct.Paragraph.html +pub mod markdown; pub mod selection; pub mod text; use iced_widget::core; use iced_widget::graphics::text::Paragraph; +pub use markdown::view as markdown; #[doc(no_inline)] pub use text::Text; diff --git a/src/markdown.rs b/src/markdown.rs new file mode 100644 index 0000000..c54271a --- /dev/null +++ b/src/markdown.rs @@ -0,0 +1,255 @@ +//! A custom markdown viewer and its corresponding functions. +//! +//! To be used with [`view_with`] +//! +//! [`view_with`]: iced_widget::markdown::view_with +use iced_widget::graphics::text::Paragraph; +use iced_widget::markdown::{ + Catalog, HeadingLevel, Item, Settings, Text, Url, Viewer, view_with, +}; +use iced_widget::{column, container, row, scrollable}; + +use crate::core::Font; +use crate::core::alignment; +use crate::core::padding; +use crate::core::{self, Element, Length, Pixels}; +use crate::{rich_text, text}; + +/// Display a bunch of markdown items. +pub fn view<'a, Theme, Renderer>( + items: impl IntoIterator<Item = &'a Item>, + settings: impl Into<Settings>, +) -> Element<'a, Url, Theme, Renderer> +where + Theme: Catalog + text::Catalog + 'a, + Renderer: core::text::Renderer<Paragraph = Paragraph, Font = Font> + 'a, +{ + view_with(items, settings, &SelectableViewer) +} +/// Displays a heading using the default look. +pub fn heading<'a, Message, Theme, Renderer>( + settings: Settings, + level: &'a HeadingLevel, + text: &'a Text, + index: usize, + on_link_click: impl Fn(Url) -> Message + 'a, +) -> Element<'a, Message, Theme, Renderer> +where + Message: 'a, + Theme: Catalog + text::Catalog + 'a, + Renderer: core::text::Renderer<Paragraph = Paragraph, Font = Font> + 'a, +{ + let Settings { + h1_size, + h2_size, + h3_size, + h4_size, + h5_size, + h6_size, + text_size, + .. + } = settings; + + container( + rich_text(text.spans(settings.style)) + .on_link_click(on_link_click) + .size(match level { + HeadingLevel::H1 => h1_size, + HeadingLevel::H2 => h2_size, + HeadingLevel::H3 => h3_size, + HeadingLevel::H4 => h4_size, + HeadingLevel::H5 => h5_size, + HeadingLevel::H6 => h6_size, + }), + ) + .padding(padding::top(if index > 0 { + text_size / 2.0 + } else { + Pixels::ZERO + })) + .into() +} + +/// Displays a paragraph using the default look. +pub fn paragraph<'a, Message, Theme, Renderer>( + settings: Settings, + text: &Text, + on_link_click: impl Fn(Url) -> Message + 'a, +) -> Element<'a, Message, Theme, Renderer> +where + Message: 'a, + Theme: Catalog + text::Catalog + 'a, + Renderer: core::text::Renderer<Paragraph = Paragraph, Font = Font> + 'a, +{ + rich_text(text.spans(settings.style)) + .size(settings.text_size) + .on_link_click(on_link_click) + .into() +} + +/// Displays an unordered list using the default look and +/// calling the [`Viewer`] for each bullet point item. +pub fn unordered_list<'a, Message, Theme, Renderer>( + viewer: &impl Viewer<'a, Message, Theme, Renderer>, + settings: Settings, + items: &'a [Vec<Item>], +) -> Element<'a, Message, Theme, Renderer> +where + Message: 'a, + Theme: Catalog + text::Catalog + 'a, + Renderer: core::text::Renderer<Paragraph = Paragraph, Font = Font> + 'a, +{ + column(items.iter().map(|items| { + row![ + text("•").size(settings.text_size), + view_with( + items, + Settings { + spacing: settings.spacing * 0.6, + ..settings + }, + viewer, + ) + ] + .spacing(settings.spacing) + .into() + })) + .spacing(settings.spacing * 0.75) + .padding([0.0, settings.spacing.0]) + .into() +} + +/// Displays an ordered list using the default look and +/// calling the [`Viewer`] for each numbered item. +pub fn ordered_list<'a, Message, Theme, Renderer>( + viewer: &impl Viewer<'a, Message, Theme, Renderer>, + settings: Settings, + start: u64, + items: &'a [Vec<Item>], +) -> Element<'a, Message, Theme, Renderer> +where + Message: 'a, + Theme: Catalog + text::Catalog + 'a, + Renderer: core::text::Renderer<Paragraph = Paragraph, Font = Font> + 'a, +{ + let digits = ((start + items.len() as u64).max(1) as f32).log10().ceil(); + + column(items.iter().enumerate().map(|(i, items)| { + row![ + text!("{}.", i as u64 + start) + .size(settings.text_size) + .align_x(alignment::Horizontal::Right) + .width(settings.text_size * ((digits / 2.0).ceil() + 1.0)), + view_with( + items, + Settings { + spacing: settings.spacing * 0.6, + ..settings + }, + viewer, + ) + ] + .spacing(settings.spacing) + .into() + })) + .spacing(settings.spacing * 0.75) + .into() +} + +/// Displays a code block using the default look. +pub fn code_block<'a, Message, Theme, Renderer>( + settings: Settings, + lines: &'a [Text], + on_link_click: impl Fn(Url) -> Message + Clone + 'a, +) -> Element<'a, Message, Theme, Renderer> +where + Message: 'a, + Theme: Catalog + text::Catalog + 'a, + Renderer: core::text::Renderer<Paragraph = Paragraph, Font = Font> + 'a, +{ + container( + scrollable( + container(column(lines.iter().map(|line| { + rich_text(line.spans(settings.style)) + .on_link_click(on_link_click.clone()) + .font(Font::MONOSPACE) + .size(settings.code_size) + .into() + }))) + .padding(settings.code_size), + ) + .direction(scrollable::Direction::Horizontal( + scrollable::Scrollbar::default() + .width(settings.code_size / 2) + .scroller_width(settings.code_size / 2), + )), + ) + .width(Length::Fill) + .padding(settings.code_size / 4) + .class(Theme::code_block()) + .into() +} + +#[derive(Debug, Clone, Copy)] +struct SelectableViewer; + +impl<'a, Theme, Renderer> Viewer<'a, Url, Theme, Renderer> for SelectableViewer +where + Theme: Catalog + text::Catalog + 'a, + Renderer: core::text::Renderer<Paragraph = Paragraph, Font = Font> + 'a, +{ + fn on_link_click(url: Url) -> Url { + url + } + + fn heading( + &self, + settings: Settings, + level: &'a HeadingLevel, + text: &'a Text, + index: usize, + ) -> Element<'a, Url, Theme, Renderer> { + heading::<'a, Url, Theme, Renderer>( + settings, + level, + text, + index, + Self::on_link_click, + ) + } + + fn paragraph( + &self, + settings: Settings, + text: &Text, + ) -> Element<'a, Url, Theme, Renderer> { + paragraph(settings, text, Self::on_link_click) + } + + fn unordered_list( + &self, + settings: Settings, + items: &'a [Vec<Item>], + ) -> Element<'a, Url, Theme, Renderer> { + unordered_list(self, settings, items) + } + + fn ordered_list( + &self, + settings: Settings, + start: u64, + items: &'a [Vec<Item>], + ) -> Element<'a, Url, Theme, Renderer> { + ordered_list(self, settings, start, items) + } + + fn code_block( + &self, + settings: Settings, + _language: Option<&'a str>, + _code: &'a str, + lines: &'a [Text], + ) -> Element<'a, Url, Theme, Renderer> { + code_block(settings, lines, Self::on_link_click) + } +} |
