zng_wgt_markdown/
view_fn.rs

1use std::num::NonZeroU32;
2
3pub use pulldown_cmark::HeadingLevel;
4use zng_ext_font::*;
5use zng_ext_image::ImageSource;
6use zng_wgt::*;
7use zng_wgt_access::{self as access, AccessRole, access_role};
8use zng_wgt_button::{Button, LinkStyle};
9use zng_wgt_container::{Container, child_align, padding};
10use zng_wgt_fill::background_color;
11use zng_wgt_filter::opacity;
12use zng_wgt_grid::{self as grid, Grid};
13use zng_wgt_size_offset::{offset, size};
14use zng_wgt_stack::{Stack, StackDirection};
15use zng_wgt_text::{FONT_COLOR_VAR, PARAGRAPH_SPACING_VAR, Text, font_size, font_weight};
16use zng_wgt_tooltip::*;
17use zng_wgt_transform::{scale, translate_y};
18use zng_wgt_wrap::Wrap;
19
20use super::*;
21
22/// Markdown text run style.
23#[derive(Default, Clone, Debug, serde::Serialize, serde::Deserialize)]
24#[non_exhaustive]
25pub struct MarkdownStyle {
26    /// Bold.
27    pub strong: bool,
28    /// Italic.
29    pub emphasis: bool,
30    /// Strikethrough.
31    pub strikethrough: bool,
32
33    /// Smaller text offset towards the bottom.
34    pub subscript: bool,
35    /// Smaller text offset towards the top.
36    pub superscript: bool,
37}
38
39/// Arguments for a markdown text view.
40///
41/// The text can be inside a paragraph, heading, list item or any other markdown block item.
42///
43/// See [`TEXT_FN_VAR`] for more details.
44#[non_exhaustive]
45pub struct TextFnArgs {
46    /// The text run.
47    pub txt: Txt,
48    /// The style.
49    pub style: MarkdownStyle,
50}
51impl TextFnArgs {
52    /// New args.
53    pub fn new(txt: impl Into<Txt>, style: MarkdownStyle) -> Self {
54        Self { txt: txt.into(), style }
55    }
56}
57
58/// Arguments for a markdown inlined link view.
59///
60/// See [`LINK_FN_VAR`] for more details.
61#[non_exhaustive]
62pub struct LinkFnArgs {
63    /// The link.
64    pub url: Txt,
65
66    /// Link title, usually displayed as a tooltip.
67    pub title: Txt,
68
69    /// Inline items.
70    pub items: UiVec,
71}
72impl LinkFnArgs {
73    /// New args.
74    pub fn new(url: impl Into<Txt>, title: impl Into<Txt>, items: UiVec) -> Self {
75        Self {
76            url: url.into(),
77            title: title.into(),
78            items,
79        }
80    }
81}
82
83/// Arguments for a markdown inlined code text view.
84///
85/// The text can be inside a paragraph, heading, list item or any other markdown block item.
86///
87/// See [`CODE_INLINE_FN_VAR`] for more details.
88#[non_exhaustive]
89pub struct CodeInlineFnArgs {
90    /// The code text run.
91    pub txt: Txt,
92    /// The style.
93    pub style: MarkdownStyle,
94}
95impl CodeInlineFnArgs {
96    /// New args.
97    pub fn new(txt: impl Into<Txt>, style: MarkdownStyle) -> Self {
98        Self { txt: txt.into(), style }
99    }
100}
101
102/// Arguments for a markdown code block view.
103///
104/// See [`CODE_BLOCK_FN_VAR`] for more details.
105#[non_exhaustive]
106pub struct CodeBlockFnArgs {
107    /// Code language, can be empty.
108    pub lang: Txt,
109    /// Raw text.
110    pub txt: Txt,
111}
112impl CodeBlockFnArgs {
113    /// New args.
114    pub fn new(lang: impl Into<Txt>, txt: impl Into<Txt>) -> Self {
115        Self {
116            lang: lang.into(),
117            txt: txt.into(),
118        }
119    }
120}
121
122/// Arguments for a markdown paragraph view.
123///
124/// See [`PARAGRAPH_FN_VAR`] for more details.
125#[non_exhaustive]
126pub struct ParagraphFnArgs {
127    /// Zero-sized index of the paragraph.
128    pub index: u32,
129    /// Inline items.
130    pub items: UiVec,
131}
132impl ParagraphFnArgs {
133    /// New args.
134    pub fn new(index: u32, items: UiVec) -> Self {
135        Self { index, items }
136    }
137}
138
139/// Arguments for a markdown heading view.
140#[non_exhaustive]
141pub struct HeadingFnArgs {
142    /// Level.
143    pub level: HeadingLevel,
144
145    /// Anchor label that identifies the header in the markdown context.
146    pub anchor: Txt,
147
148    /// Inline items.
149    pub items: UiVec,
150}
151impl HeadingFnArgs {
152    /// New args.
153    pub fn new(level: HeadingLevel, anchor: impl Into<Txt>, items: UiVec) -> Self {
154        Self {
155            level,
156            anchor: anchor.into(),
157            items,
158        }
159    }
160}
161
162/// Arguments for a markdown list view.
163#[non_exhaustive]
164pub struct ListFnArgs {
165    /// Nested list depth, starting from zero for the outer-list.
166    pub depth: u32,
167
168    /// If the list is *ordered*, the first item number.
169    pub first_num: Option<u64>,
170
171    /// List items.
172    ///
173    /// Each two items are the bullet or number followed by the item.
174    pub items: UiVec,
175}
176impl ListFnArgs {
177    /// New args.
178    pub fn new(depth: u32, first_num: Option<u64>, items: UiVec) -> Self {
179        Self { depth, first_num, items }
180    }
181}
182
183/// Arguments for a markdown list item bullet, check mark or number.
184#[derive(Clone, Copy)]
185#[non_exhaustive]
186pub struct ListItemBulletFnArgs {
187    /// Nested list depth, starting from zero for items in the outer-list.
188    pub depth: u32,
189
190    /// If the list is *ordered*, the item number.
191    pub num: Option<u64>,
192
193    /// If the list is checked. `Some(true)` is `[x]` and `Some(false)` is `[ ]`.
194    pub checked: Option<bool>,
195}
196impl ListItemBulletFnArgs {
197    /// New args.
198    pub fn new(depth: u32, num: Option<u64>, checked: Option<bool>) -> Self {
199        Self { depth, num, checked }
200    }
201}
202
203/// Arguments for a markdown list item view.
204#[non_exhaustive]
205pub struct ListItemFnArgs {
206    /// Copy of the bullet args.
207    pub bullet: ListItemBulletFnArgs,
208
209    /// Inline items of the list item.
210    pub items: UiVec,
211
212    /// Inner block items, paragraphs and nested lists.
213    pub blocks: UiVec,
214}
215impl ListItemFnArgs {
216    /// New args.
217    pub fn new(bullet: ListItemBulletFnArgs, items: UiVec, blocks: UiVec) -> Self {
218        Self { bullet, items, blocks }
219    }
220}
221
222/// Arguments for a markdown definition list.
223#[non_exhaustive]
224pub struct DefListArgs {
225    /// List items.
226    ///
227    /// Each two items are the title and definition.
228    pub items: UiVec,
229}
230impl DefListArgs {
231    /// New args.
232    pub fn new(items: UiVec) -> Self {
233        Self { items }
234    }
235}
236
237/// Arguments for a markdown definition list item title.
238#[non_exhaustive]
239pub struct DefListItemTitleArgs {
240    /// Inline items of the title.
241    pub items: UiVec,
242}
243impl DefListItemTitleArgs {
244    /// New args.
245    pub fn new(items: UiVec) -> Self {
246        Self { items }
247    }
248}
249
250/// Arguments for a markdown definition list item description.
251#[non_exhaustive]
252pub struct DefListItemDefinitionArgs {
253    /// Inline items of the description.
254    pub items: UiVec,
255}
256impl DefListItemDefinitionArgs {
257    /// New args.
258    pub fn new(items: UiVec) -> Self {
259        Self { items }
260    }
261}
262
263/// Arguments for a markdown image view.
264#[non_exhaustive]
265pub struct ImageFnArgs {
266    /// Image, resolved by the [`image_resolver`].
267    ///
268    /// [`image_resolver`]: fn@crate::image_resolver
269    pub source: ImageSource,
270    /// Image title, usually displayed as a tooltip.
271    pub title: Txt,
272    /// Items to display when the image does not load and for screen readers.
273    pub alt_items: UiVec,
274    /// Alt items in text form.
275    pub alt_txt: Txt,
276}
277impl ImageFnArgs {
278    /// New args.
279    pub fn new(source: ImageSource, title: impl Into<Txt>, alt_items: UiVec, alt_txt: impl Into<Txt>) -> Self {
280        Self {
281            source,
282            title: title.into(),
283            alt_items,
284            alt_txt: alt_txt.into(),
285        }
286    }
287}
288
289/// Arguments for a markdown rule view.
290///
291/// Currently no args.
292#[derive(Default)]
293#[non_exhaustive]
294pub struct RuleFnArgs {}
295
296/// Arguments for a markdown block quote view.
297#[non_exhaustive]
298pub struct BlockQuoteFnArgs {
299    /// Number of *parent* quotes in case of nesting.
300    ///
301    /// > 0
302    /// >> 1
303    /// >>> 2
304    pub level: u32,
305
306    /// Block items.
307    pub items: UiVec,
308}
309impl BlockQuoteFnArgs {
310    /// New args.
311    pub fn new(level: u32, items: UiVec) -> Self {
312        Self { level, items }
313    }
314}
315
316/// Arguments for a markdown footnote reference view.
317#[non_exhaustive]
318pub struct FootnoteRefFnArgs {
319    /// Footnote referenced.
320    pub label: Txt,
321}
322impl FootnoteRefFnArgs {
323    /// New args.
324    pub fn new(label: impl Into<Txt>) -> Self {
325        Self { label: label.into() }
326    }
327}
328
329/// Arguments for a markdown footnote definition view.
330///
331/// See [`PARAGRAPH_FN_VAR`] for more details.
332#[non_exhaustive]
333pub struct FootnoteDefFnArgs {
334    /// Identifier label.
335    pub label: Txt,
336    /// Block items.
337    pub items: UiVec,
338}
339impl FootnoteDefFnArgs {
340    /// New args.
341    pub fn new(label: impl Into<Txt>, items: UiVec) -> Self {
342        Self {
343            label: label.into(),
344            items,
345        }
346    }
347}
348
349/// Arguments for a markdown table view.
350///
351/// See [`TABLE_FN_VAR`] for more details.
352#[non_exhaustive]
353pub struct TableFnArgs {
354    /// Column definitions with align.
355    pub columns: Vec<Align>,
356    /// Cell items.
357    pub cells: UiVec,
358}
359impl TableFnArgs {
360    /// New args.
361    pub fn new(columns: Vec<Align>, cells: UiVec) -> Self {
362        Self { columns, cells }
363    }
364}
365
366/// Arguments for a markdown table cell view.
367///
368/// See [`TABLE_CELL_FN_VAR`] for more details.
369#[non_exhaustive]
370pub struct TableCellFnArgs {
371    /// If the cell is inside the header row.
372    pub is_heading: bool,
373
374    /// Column align.
375    pub col_align: Align,
376
377    /// Inline items.
378    pub items: UiVec,
379}
380impl TableCellFnArgs {
381    /// New args.
382    pub fn new(is_heading: bool, col_align: Align, items: UiVec) -> Self {
383        Self {
384            is_heading,
385            col_align,
386            items,
387        }
388    }
389}
390
391/// Arguments for a markdown panel.
392///
393/// See [`PANEL_FN_VAR`] for more details.
394#[non_exhaustive]
395pub struct PanelFnArgs {
396    /// Block items.
397    pub items: UiVec,
398}
399impl PanelFnArgs {
400    /// New args.
401    pub fn new(items: UiVec) -> Self {
402        Self { items }
403    }
404}
405
406context_var! {
407    /// Widget function for a markdown text segment.
408    pub static TEXT_FN_VAR: WidgetFn<TextFnArgs> = WidgetFn::new(default_text_fn);
409
410    /// Widget function for a markdown link segment.
411    pub static LINK_FN_VAR: WidgetFn<LinkFnArgs> = WidgetFn::new(default_link_fn);
412
413    /// Widget function for a markdown inline code segment.
414    pub static CODE_INLINE_FN_VAR: WidgetFn<CodeInlineFnArgs> = WidgetFn::new(default_code_inline_fn);
415
416    /// Widget function for a markdown code block segment.
417    pub static CODE_BLOCK_FN_VAR: WidgetFn<CodeBlockFnArgs> = WidgetFn::new(default_code_block_fn);
418
419    /// Widget function for a markdown paragraph.
420    pub static PARAGRAPH_FN_VAR: WidgetFn<ParagraphFnArgs> = WidgetFn::new(default_paragraph_fn);
421
422    /// Widget function for a markdown heading.
423    pub static HEADING_FN_VAR: WidgetFn<HeadingFnArgs> = WidgetFn::new(default_heading_fn);
424
425    /// Widget function for a markdown list.
426    pub static LIST_FN_VAR: WidgetFn<ListFnArgs> = WidgetFn::new(default_list_fn);
427
428    /// Widget function for a markdown list item bullet, check mark or number.
429    pub static LIST_ITEM_BULLET_FN_VAR: WidgetFn<ListItemBulletFnArgs> = WidgetFn::new(default_list_item_bullet_fn);
430
431    /// Widget function for a markdown list item content.
432    pub static LIST_ITEM_FN_VAR: WidgetFn<ListItemFnArgs> = WidgetFn::new(default_list_item_fn);
433
434    /// Widget function for a markdown definition list.
435    pub static DEF_LIST_FN_VAR: WidgetFn<DefListArgs> = WidgetFn::new(default_def_list_fn);
436
437    /// Widget function for a markdown definition list item title.
438    pub static DEF_LIST_ITEM_TITLE_FN_VAR: WidgetFn<DefListItemTitleArgs> = WidgetFn::new(default_def_list_item_title_fn);
439
440    /// Widget function for a markdown definition list item description.
441    pub static DEF_LIST_ITEM_DEFINITION_FN_VAR: WidgetFn<DefListItemDefinitionArgs> =
442        WidgetFn::new(default_def_list_item_definition_fn);
443
444    /// Widget function for a markdown image.
445    pub static IMAGE_FN_VAR: WidgetFn<ImageFnArgs> = WidgetFn::new(default_image_fn);
446
447    /// Widget function for a markdown rule line.
448    pub static RULE_FN_VAR: WidgetFn<RuleFnArgs> = WidgetFn::new(default_rule_fn);
449
450    /// Widget function for a markdown block quote.
451    pub static BLOCK_QUOTE_FN_VAR: WidgetFn<BlockQuoteFnArgs> = WidgetFn::new(default_block_quote_fn);
452
453    /// Widget function for an inline reference to a footnote.
454    pub static FOOTNOTE_REF_FN_VAR: WidgetFn<FootnoteRefFnArgs> = WidgetFn::new(default_footnote_ref_fn);
455
456    /// Widget function for a footnote definition block.
457    pub static FOOTNOTE_DEF_FN_VAR: WidgetFn<FootnoteDefFnArgs> = WidgetFn::new(default_footnote_def_fn);
458
459    /// Widget function for a markdown table.
460    pub static TABLE_FN_VAR: WidgetFn<TableFnArgs> = WidgetFn::new(default_table_fn);
461
462    /// Widget function for a markdown table body cell.
463    pub static TABLE_CELL_FN_VAR: WidgetFn<TableCellFnArgs> = WidgetFn::new(default_table_cell_fn);
464
465    /// Widget function for a markdown panel.
466    pub static PANEL_FN_VAR: WidgetFn<PanelFnArgs> = WidgetFn::new(default_panel_fn);
467}
468
469/// Widget function that converts [`TextFnArgs`] to widgets.
470///
471/// Sets the [`TEXT_FN_VAR`].
472#[property(CONTEXT, default(TEXT_FN_VAR), widget_impl(Markdown))]
473pub fn text_fn(child: impl IntoUiNode, wgt_fn: impl IntoVar<WidgetFn<TextFnArgs>>) -> UiNode {
474    with_context_var(child, TEXT_FN_VAR, wgt_fn)
475}
476
477/// Widget function that converts [`LinkFnArgs`] to widgets.
478///
479/// Sets the [`LINK_FN_VAR`].
480#[property(CONTEXT, default(LINK_FN_VAR), widget_impl(Markdown))]
481pub fn link_fn(child: impl IntoUiNode, wgt_fn: impl IntoVar<WidgetFn<LinkFnArgs>>) -> UiNode {
482    with_context_var(child, LINK_FN_VAR, wgt_fn)
483}
484
485/// Widget function that converts [`CodeInlineFnArgs`] to widgets.
486///
487/// Sets the [`CODE_INLINE_FN_VAR`].
488#[property(CONTEXT, default(CODE_INLINE_FN_VAR), widget_impl(Markdown))]
489pub fn code_inline_fn(child: impl IntoUiNode, wgt_fn: impl IntoVar<WidgetFn<CodeInlineFnArgs>>) -> UiNode {
490    with_context_var(child, CODE_INLINE_FN_VAR, wgt_fn)
491}
492
493/// Widget function that converts [`CodeBlockFnArgs`] to widgets.
494///
495/// Sets the [`CODE_BLOCK_FN_VAR`].
496#[property(CONTEXT, default(CODE_BLOCK_FN_VAR), widget_impl(Markdown))]
497pub fn code_block_fn(child: impl IntoUiNode, wgt_fn: impl IntoVar<WidgetFn<CodeBlockFnArgs>>) -> UiNode {
498    with_context_var(child, CODE_BLOCK_FN_VAR, wgt_fn)
499}
500
501/// Widget function that converts [`ParagraphFnArgs`] to widgets.
502///
503/// Sets the [`PARAGRAPH_FN_VAR`].
504#[property(CONTEXT, default(PARAGRAPH_FN_VAR), widget_impl(Markdown))]
505pub fn paragraph_fn(child: impl IntoUiNode, wgt_fn: impl IntoVar<WidgetFn<ParagraphFnArgs>>) -> UiNode {
506    with_context_var(child, PARAGRAPH_FN_VAR, wgt_fn)
507}
508
509/// Widget function that converts [`HeadingFnArgs`] to widgets.
510///
511/// Sets the [`HEADING_FN_VAR`].
512#[property(CONTEXT, default(HEADING_FN_VAR), widget_impl(Markdown))]
513pub fn heading_fn(child: impl IntoUiNode, wgt_fn: impl IntoVar<WidgetFn<HeadingFnArgs>>) -> UiNode {
514    with_context_var(child, HEADING_FN_VAR, wgt_fn)
515}
516
517/// Widget function that converts [`ListFnArgs`] to widgets.
518///
519/// Sets the [`LIST_FN_VAR`].
520#[property(CONTEXT, default(LIST_FN_VAR), widget_impl(Markdown))]
521pub fn list_fn(child: impl IntoUiNode, wgt_fn: impl IntoVar<WidgetFn<ListFnArgs>>) -> UiNode {
522    with_context_var(child, LIST_FN_VAR, wgt_fn)
523}
524
525/// Widget function that converts [`DefListArgs`] to widgets.
526///
527/// Sets the [`DEF_LIST_FN_VAR`].
528#[property(CONTEXT, default(DEF_LIST_FN_VAR), widget_impl(Markdown))]
529pub fn def_list_fn(child: impl IntoUiNode, wgt_fn: impl IntoVar<WidgetFn<DefListArgs>>) -> UiNode {
530    with_context_var(child, DEF_LIST_FN_VAR, wgt_fn)
531}
532
533/// Widget function that converts [`DefListItemTitleArgs`] to widgets.
534///
535/// Sets the [`DEF_LIST_ITEM_TITLE_FN_VAR`].
536#[property(CONTEXT, default(DEF_LIST_ITEM_TITLE_FN_VAR), widget_impl(Markdown))]
537pub fn def_list_item_title_fn(child: impl IntoUiNode, wgt_fn: impl IntoVar<WidgetFn<DefListItemTitleArgs>>) -> UiNode {
538    with_context_var(child, DEF_LIST_ITEM_TITLE_FN_VAR, wgt_fn)
539}
540
541/// Widget function that converts [`DefListItemDefinitionArgs`] to widgets.
542///
543/// Sets the [`DEF_LIST_ITEM_DEFINITION_FN_VAR`].
544#[property(CONTEXT, default(DEF_LIST_ITEM_DEFINITION_FN_VAR), widget_impl(Markdown))]
545pub fn def_list_item_definition_fn(child: impl IntoUiNode, wgt_fn: impl IntoVar<WidgetFn<DefListItemDefinitionArgs>>) -> UiNode {
546    with_context_var(child, DEF_LIST_ITEM_DEFINITION_FN_VAR, wgt_fn)
547}
548
549/// Widget function that converts [`ListItemBulletFnArgs`] to widgets.
550///
551/// Sets the [`LIST_ITEM_BULLET_FN_VAR`].
552#[property(CONTEXT, default(LIST_ITEM_BULLET_FN_VAR), widget_impl(Markdown))]
553pub fn list_item_bullet_fn(child: impl IntoUiNode, wgt_fn: impl IntoVar<WidgetFn<ListItemBulletFnArgs>>) -> UiNode {
554    with_context_var(child, LIST_ITEM_BULLET_FN_VAR, wgt_fn)
555}
556
557/// Widget function that converts [`ListItemFnArgs`] to widgets.
558///
559/// Sets the [`LIST_ITEM_FN_VAR`].
560#[property(CONTEXT, default(LIST_ITEM_FN_VAR), widget_impl(Markdown))]
561pub fn list_item_fn(child: impl IntoUiNode, wgt_fn: impl IntoVar<WidgetFn<ListItemFnArgs>>) -> UiNode {
562    with_context_var(child, LIST_ITEM_FN_VAR, wgt_fn)
563}
564
565/// Widget function that converts [`ImageFnArgs`] to widgets.
566///
567/// Sets the [`IMAGE_FN_VAR`].
568#[property(CONTEXT, default(IMAGE_FN_VAR), widget_impl(Markdown))]
569pub fn image_fn(child: impl IntoUiNode, wgt_fn: impl IntoVar<WidgetFn<ImageFnArgs>>) -> UiNode {
570    with_context_var(child, IMAGE_FN_VAR, wgt_fn)
571}
572
573/// Widget function that converts [`RuleFnArgs`] to widgets.
574///
575/// Sets the [`RULE_FN_VAR`].
576#[property(CONTEXT, default(RULE_FN_VAR), widget_impl(Markdown))]
577pub fn rule_fn(child: impl IntoUiNode, wgt_fn: impl IntoVar<WidgetFn<RuleFnArgs>>) -> UiNode {
578    with_context_var(child, RULE_FN_VAR, wgt_fn)
579}
580
581/// Widget function that converts [`BlockQuoteFnArgs`] to widgets.
582///
583/// Sets the [`BLOCK_QUOTE_FN_VAR`].
584#[property(CONTEXT, default(BLOCK_QUOTE_FN_VAR), widget_impl(Markdown))]
585pub fn block_quote_fn(child: impl IntoUiNode, wgt_fn: impl IntoVar<WidgetFn<BlockQuoteFnArgs>>) -> UiNode {
586    with_context_var(child, BLOCK_QUOTE_FN_VAR, wgt_fn)
587}
588
589/// Widget function that converts [`FootnoteRefFnArgs`] to widgets.
590///
591/// Sets the [`FOOTNOTE_REF_FN_VAR`].
592#[property(CONTEXT, default(FOOTNOTE_REF_FN_VAR), widget_impl(Markdown))]
593pub fn footnote_ref_fn(child: impl IntoUiNode, wgt_fn: impl IntoVar<WidgetFn<FootnoteRefFnArgs>>) -> UiNode {
594    with_context_var(child, FOOTNOTE_REF_FN_VAR, wgt_fn)
595}
596
597/// Widget function that converts [`FootnoteDefFnArgs`] to widgets.
598///
599/// Sets the [`FOOTNOTE_DEF_FN_VAR`].
600#[property(CONTEXT, default(FOOTNOTE_DEF_FN_VAR), widget_impl(Markdown))]
601pub fn footnote_def_fn(child: impl IntoUiNode, wgt_fn: impl IntoVar<WidgetFn<FootnoteDefFnArgs>>) -> UiNode {
602    with_context_var(child, FOOTNOTE_DEF_FN_VAR, wgt_fn)
603}
604
605/// Widget function that converts [`TableFnArgs`] to widgets.
606///
607/// Sets the [`TABLE_FN_VAR`].
608#[property(CONTEXT, default(TABLE_FN_VAR), widget_impl(Markdown))]
609pub fn table_fn(child: impl IntoUiNode, wgt_fn: impl IntoVar<WidgetFn<TableFnArgs>>) -> UiNode {
610    with_context_var(child, TABLE_FN_VAR, wgt_fn)
611}
612
613/// Widget function that converts [`PanelFnArgs`] to a widget.
614///
615/// This generates the panel that contains all markdown blocks, it is the child of the [`Markdown!`] widget.
616///
617/// Sets the [`PANEL_FN_VAR`].
618///
619/// [`Markdown!`]: struct@crate::Markdown
620#[property(CONTEXT, default(PANEL_FN_VAR), widget_impl(Markdown))]
621pub fn panel_fn(child: impl IntoUiNode, wgt_fn: impl IntoVar<WidgetFn<PanelFnArgs>>) -> UiNode {
622    with_context_var(child, PANEL_FN_VAR, wgt_fn)
623}
624
625fn text_view_builder(txt: Txt, style: MarkdownStyle) -> Text {
626    let mut builder = Text::widget_new();
627
628    widget_set! {
629        &mut builder;
630        txt;
631        // white_space = WhiteSpace::Merge;
632    }
633
634    if style.strong {
635        widget_set! {
636            &mut builder;
637            font_weight = FontWeight::BOLD;
638        }
639    }
640    if style.emphasis {
641        widget_set! {
642            &mut builder;
643            font_style = FontStyle::Italic;
644        }
645    }
646    if style.strikethrough {
647        widget_set! {
648            &mut builder;
649            strikethrough = 1, LineStyle::Solid;
650        }
651    }
652    if style.subscript {
653        widget_set! {
654            &mut builder;
655            font_size = 0.75.em();
656            translate_y = 0.25.em();
657        }
658    } else if style.superscript {
659        widget_set! {
660            &mut builder;
661            font_size = 0.75.em();
662            translate_y = -0.25.em();
663        }
664    }
665
666    builder
667}
668
669/// Default text view.
670///
671/// See [`TEXT_FN_VAR`] for more details.
672pub fn default_text_fn(args: TextFnArgs) -> UiNode {
673    let mut builder = text_view_builder(args.txt, args.style);
674    builder.widget_build()
675}
676
677/// Default inlined code text view.
678///
679/// See [`CODE_INLINE_FN_VAR`] for more details.
680pub fn default_code_inline_fn(args: CodeInlineFnArgs) -> UiNode {
681    let mut builder = text_view_builder(args.txt, args.style);
682
683    widget_set! {
684        &mut builder;
685        font_family = ["JetBrains Mono", "Consolas", "monospace"];
686        background_color = light_dark(rgb(0.95, 0.95, 0.95), rgb(0.05, 0.05, 0.05));
687    }
688
689    builder.widget_build()
690}
691
692/// Default inlined link view.
693///
694/// See [`LINK_FN_VAR`] for more details.
695pub fn default_link_fn(args: LinkFnArgs) -> UiNode {
696    if args.items.is_empty() {
697        UiNode::nil()
698    } else {
699        let url = args.url;
700
701        let mut items = args.items;
702        let items = if items.len() == 1 {
703            items.remove(0)
704        } else {
705            Wrap! {
706                children = items;
707            }
708        };
709
710        Button! {
711            style_fn = LinkStyle!();
712            child = items;
713
714            on_click = hn!(|args| {
715                args.propagation().stop();
716
717                let link = WINDOW.info().get(WIDGET.id()).unwrap().interaction_path();
718                LINK_EVENT.notify(LinkArgs::now(url.clone(), link));
719            });
720        }
721    }
722}
723
724/// Default code block view.
725///
726/// Is [`AnsiText!`] for the `ansi` and `console` languages, and only raw text for the rest.
727///
728/// See [`CODE_BLOCK_FN_VAR`] for more details.
729///
730/// [`AnsiText!`]: struct@zng_wgt_ansi_text::AnsiText
731pub fn default_code_block_fn(args: CodeBlockFnArgs) -> UiNode {
732    if ["ansi", "console"].contains(&args.lang.as_str()) {
733        zng_wgt_ansi_text::AnsiText! {
734            txt = args.txt;
735            padding = 6;
736            corner_radius = 4;
737            background_color = light_dark(rgb(0.95, 0.95, 0.95), rgb(0.05, 0.05, 0.05));
738        }
739    } else {
740        Text! {
741            txt = args.txt;
742            padding = 6;
743            corner_radius = 4;
744            font_family = ["JetBrains Mono", "Consolas", "monospace"];
745            background_color = light_dark(rgb(0.95, 0.95, 0.95), rgb(0.05, 0.05, 0.05));
746        }
747    }
748}
749
750/// Default paragraph view.
751///
752/// See [`PARAGRAPH_FN_VAR`] for more details.
753pub fn default_paragraph_fn(mut args: ParagraphFnArgs) -> UiNode {
754    if args.items.is_empty() {
755        UiNode::nil()
756    } else if args.items.len() == 1 {
757        args.items.remove(0)
758    } else {
759        Wrap! {
760            children = args.items;
761        }
762    }
763}
764
765/// Default heading view.
766///
767/// See [`HEADING_FN_VAR`] for more details.
768pub fn default_heading_fn(args: HeadingFnArgs) -> UiNode {
769    if args.items.is_empty() {
770        UiNode::nil()
771    } else {
772        Wrap! {
773            access_role = AccessRole::Heading;
774            access::level = NonZeroU32::new(args.level as _).unwrap();
775            font_size = match args.level {
776                HeadingLevel::H1 => 2.em(),
777                HeadingLevel::H2 => 1.5.em(),
778                HeadingLevel::H3 => 1.4.em(),
779                HeadingLevel::H4 => 1.3.em(),
780                HeadingLevel::H5 => 1.2.em(),
781                HeadingLevel::H6 => 1.1.em(),
782            };
783            children = args.items;
784            anchor = args.anchor;
785        }
786    }
787}
788
789/// Default list view.
790///
791/// Uses a [`Grid!`] with two columns, one default for the bullet or number, the other fills the leftover space.
792///
793/// See [`LIST_FN_VAR`] for more details.
794///
795/// [`Grid!`]: struct@Grid
796pub fn default_list_fn(args: ListFnArgs) -> UiNode {
797    if args.items.is_empty() {
798        UiNode::nil()
799    } else {
800        Grid! {
801            grid::cell::at = grid::cell::AT_AUTO; // in case it is nested
802
803            access_role = AccessRole::List;
804            margin = (0, 0, 0, 1.em());
805            cells = args.items;
806            columns = ui_vec![
807                grid::Column!(),
808                grid::Column! {
809                    width = 1.lft();
810                },
811            ];
812        }
813    }
814}
815
816/// Default definition list view.
817///
818/// Is a simple vertical [`Stack!`].
819///
820/// [`Stack!`]: struct@Stack
821pub fn default_def_list_fn(args: DefListArgs) -> UiNode {
822    if args.items.is_empty() {
823        UiNode::nil()
824    } else {
825        Stack! {
826            access_role = AccessRole::List;
827            direction = StackDirection::top_to_bottom();
828            spacing = PARAGRAPH_SPACING_VAR;
829            children = args.items;
830        }
831    }
832}
833
834/// Default definition list item title view.
835///
836/// Is a [`Wrap!`] with bold text.
837///
838/// [`Wrap!`]: struct@Wrap
839pub fn default_def_list_item_title_fn(args: DefListItemTitleArgs) -> UiNode {
840    if args.items.is_empty() {
841        UiNode::nil()
842    } else {
843        Wrap! {
844            access_role = AccessRole::Term;
845            children = args.items;
846            font_weight = FontWeight::BOLD;
847        }
848    }
849}
850
851/// Default definition list item description view.
852///
853/// Is a [`Wrap!`].
854///
855/// [`Wrap!`]: struct@Wrap
856pub fn default_def_list_item_definition_fn(args: DefListItemDefinitionArgs) -> UiNode {
857    if args.items.is_empty() {
858        UiNode::nil()
859    } else {
860        Wrap! {
861            access_role = AccessRole::Definition;
862            children = args.items;
863            margin = (0, 2.em());
864        }
865    }
866}
867
868/// Default list item bullet, check mark or number view.
869///
870/// See [`LIST_ITEM_BULLET_FN_VAR`] for more details.
871pub fn default_list_item_bullet_fn(args: ListItemBulletFnArgs) -> UiNode {
872    if let Some(checked) = args.checked {
873        Text! {
874            grid::cell::at = grid::cell::AT_AUTO;
875            align = Align::TOP;
876            txt = " ✓ ";
877            font_color = FONT_COLOR_VAR.map(move |c| if checked { *c } else { c.transparent() });
878            background_color = FONT_COLOR_VAR.map(|c| c.with_alpha(10.pct()));
879            corner_radius = 4;
880            scale = 0.8.fct();
881            offset = (-(0.1.fct()), 0);
882        }
883    } else if let Some(n) = args.num {
884        Text! {
885            grid::cell::at = grid::cell::AT_AUTO;
886            txt = formatx!("{n}. ");
887            align = Align::RIGHT;
888        }
889    } else {
890        match args.depth {
891            0 => Wgt! {
892                grid::cell::at = grid::cell::AT_AUTO;
893                align = Align::TOP;
894                size = (5, 5);
895                corner_radius = 5;
896                margin = (0.6.em(), 0.5.em(), 0, 0);
897                background_color = FONT_COLOR_VAR;
898            },
899            1 => Wgt! {
900                grid::cell::at = grid::cell::AT_AUTO;
901                align = Align::TOP;
902                size = (5, 5);
903                corner_radius = 5;
904                margin = (0.6.em(), 0.5.em(), 0, 0);
905                border = 1.px(), FONT_COLOR_VAR.map_into();
906            },
907            _ => Wgt! {
908                grid::cell::at = grid::cell::AT_AUTO;
909                align = Align::TOP;
910                size = (5, 5);
911                margin = (0.6.em(), 0.5.em(), 0, 0);
912                background_color = FONT_COLOR_VAR;
913            },
914        }
915    }
916}
917
918/// Default list item view.
919///
920/// See [`LIST_ITEM_FN_VAR`] for more details.
921pub fn default_list_item_fn(args: ListItemFnArgs) -> UiNode {
922    let mut blocks = args.blocks;
923    let mut items = args.items;
924
925    if items.is_empty() {
926        if blocks.is_empty() {
927            return UiNode::nil();
928        }
929    } else {
930        let r = if items.len() == 1 { items.remove(0) } else { Wrap!(items) };
931        blocks.insert(0, r);
932    }
933
934    if blocks.len() > 1 {
935        Stack! {
936            access_role = AccessRole::ListItem;
937            grid::cell::at = grid::cell::AT_AUTO;
938            direction = StackDirection::top_to_bottom();
939            children = blocks;
940        }
941    } else {
942        Container! {
943            access_role = AccessRole::ListItem;
944            grid::cell::at = grid::cell::AT_AUTO;
945            child = blocks.remove(0);
946        }
947    }
948}
949
950/// Default image view.
951///
952/// See [`IMAGE_FN_VAR`] for more details.
953pub fn default_image_fn(args: ImageFnArgs) -> UiNode {
954    let tooltip_fn = if args.title.is_empty() {
955        wgt_fn!()
956    } else {
957        let title = args.title;
958        wgt_fn!(|_| Tip!(Text!(title.clone())))
959    };
960
961    let alt_txt = args.alt_txt;
962    let mut alt_items = args.alt_items;
963    if alt_items.is_empty() {
964        zng_wgt_image::Image! {
965            align = Align::TOP_LEFT;
966            tooltip_fn;
967            access::label = alt_txt;
968            source = args.source;
969        }
970    } else {
971        let alt_items = if alt_items.len() == 1 {
972            alt_items.remove(0)
973        } else {
974            Wrap! {
975                children = alt_items;
976            }
977        };
978        let alt_items = ArcNode::new(alt_items);
979        zng_wgt_image::Image! {
980            align = Align::TOP_LEFT;
981            source = args.source;
982            tooltip_fn;
983            zng_wgt_access::label = alt_txt;
984            img_error_fn = wgt_fn!(|_| { alt_items.take_on_init() });
985        }
986    }
987}
988
989/// Default rule view.
990///
991/// See [`RULE_FN_VAR`] for more details.
992pub fn default_rule_fn(_: RuleFnArgs) -> UiNode {
993    zng_wgt_rule_line::hr::Hr! {
994        opacity = 50.pct();
995    }
996}
997
998/// Default block quote view.
999///
1000/// See [`BLOCK_QUOTE_FN_VAR`] for more details.
1001pub fn default_block_quote_fn(args: BlockQuoteFnArgs) -> UiNode {
1002    if args.items.is_empty() {
1003        UiNode::nil()
1004    } else {
1005        Stack! {
1006            direction = StackDirection::top_to_bottom();
1007            spacing = PARAGRAPH_SPACING_VAR;
1008            children = args.items;
1009            corner_radius = 2;
1010            background_color = if args.level < 3 {
1011                FONT_COLOR_VAR.map(|c| c.with_alpha(5.pct()))
1012            } else {
1013                colors::BLACK.transparent().into_var()
1014            };
1015            border = {
1016                widths: (0, 0, 0, 4u32.saturating_sub(args.level).max(1) as i32),
1017                sides: FONT_COLOR_VAR.map(|c| BorderSides::solid(c.with_alpha(60.pct()))),
1018            };
1019            padding = 4;
1020        }
1021    }
1022}
1023
1024/// Default markdown table.
1025///
1026/// See [`TABLE_FN_VAR`] for more details.
1027pub fn default_table_fn(args: TableFnArgs) -> UiNode {
1028    Grid! {
1029        access_role = AccessRole::Table;
1030        background_color = FONT_COLOR_VAR.map(|c| c.with_alpha(5.pct()));
1031        border = 1, FONT_COLOR_VAR.map(|c| c.with_alpha(30.pct()).into());
1032        align = Align::LEFT;
1033        auto_grow_fn = wgt_fn!(|args: grid::AutoGrowFnArgs| {
1034            grid::Row! {
1035                border = (0, 0, 1, 0), FONT_COLOR_VAR.map(|c| c.with_alpha(10.pct()).into());
1036                background_color = {
1037                    let alpha = if args.index.is_multiple_of(2) { 5.pct() } else { 0.pct() };
1038                    FONT_COLOR_VAR.map(move |c| c.with_alpha(alpha))
1039                };
1040
1041                when *#is_last {
1042                    border = 0, BorderStyle::Hidden;
1043                }
1044            }
1045        });
1046        columns = std::iter::repeat_with(|| grid::Column! {}).take(args.columns.len());
1047        cells = args.cells;
1048    }
1049}
1050
1051/// Default markdown table.
1052///
1053/// See [`TABLE_CELL_FN_VAR`] for more details.
1054pub fn default_table_cell_fn(args: TableCellFnArgs) -> UiNode {
1055    if args.items.is_empty() {
1056        UiNode::nil()
1057    } else if args.is_heading {
1058        Wrap! {
1059            access_role = AccessRole::Cell;
1060            grid::cell::at = grid::cell::AT_AUTO;
1061            font_weight = FontWeight::BOLD;
1062            padding = 6;
1063            child_align = args.col_align;
1064            children = args.items;
1065        }
1066    } else {
1067        Wrap! {
1068            access_role = AccessRole::Cell;
1069            grid::cell::at = grid::cell::AT_AUTO;
1070            padding = 6;
1071            child_align = args.col_align;
1072            children = args.items;
1073        }
1074    }
1075}
1076
1077/// Default markdown panel.
1078///
1079/// See [`PANEL_FN_VAR`] for more details.
1080pub fn default_panel_fn(args: PanelFnArgs) -> UiNode {
1081    if args.items.is_empty() {
1082        UiNode::nil()
1083    } else {
1084        Stack! {
1085            direction = StackDirection::top_to_bottom();
1086            spacing = PARAGRAPH_SPACING_VAR;
1087            children = args.items;
1088        }
1089    }
1090}
1091
1092/// Default markdown footnote reference.
1093///
1094/// See [`FOOTNOTE_REF_FN_VAR`] for more details.
1095pub fn default_footnote_ref_fn(args: FootnoteRefFnArgs) -> UiNode {
1096    let url = formatx!("#footnote-{}", args.label);
1097    Button! {
1098        style_fn = LinkStyle!();
1099        font_size = 0.7.em();
1100        offset = (0, -0.5.em());
1101        crate::anchor = formatx!("footnote-ref-{}", args.label);
1102        child = Text!("[{}]", args.label);
1103        on_click = hn!(|args| {
1104            args.propagation().stop();
1105
1106            let link = WINDOW.info().get(WIDGET.id()).unwrap().interaction_path();
1107            crate::LINK_EVENT.notify(crate::LinkArgs::now(url.clone(), link));
1108        });
1109    }
1110}
1111
1112/// Default markdown footnote definition.
1113///
1114/// See [`FOOTNOTE_DEF_FN_VAR`] for more details.
1115pub fn default_footnote_def_fn(args: FootnoteDefFnArgs) -> UiNode {
1116    let mut items = args.items;
1117    let items = if items.is_empty() {
1118        UiNode::nil()
1119    } else if items.len() == 1 {
1120        items.remove(0)
1121    } else {
1122        Stack! {
1123            direction = StackDirection::top_to_bottom();
1124            children = items;
1125        }
1126    };
1127
1128    let url_back = formatx!("#footnote-ref-{}", args.label);
1129    Stack! {
1130        direction = StackDirection::left_to_right();
1131        spacing = 0.5.em();
1132        anchor = formatx!("footnote-{}", args.label);
1133        children = ui_vec![
1134            Button! {
1135                style_fn = LinkStyle!();
1136                child = Text!("[^{}]", args.label);
1137                on_click = hn!(|args| {
1138                    args.propagation().stop();
1139
1140                    let link = WINDOW.info().get(WIDGET.id()).unwrap().interaction_path();
1141                    LINK_EVENT.notify(LinkArgs::now(url_back.clone(), link));
1142                });
1143            },
1144            items,
1145        ];
1146    }
1147}