diff --git a/components/MarkdownTextBlock/samples/MarkdownTextBlockCustomSample.xaml b/components/MarkdownTextBlock/samples/MarkdownTextBlockCustomSample.xaml index 1ead34838..69430b398 100644 --- a/components/MarkdownTextBlock/samples/MarkdownTextBlockCustomSample.xaml +++ b/components/MarkdownTextBlock/samples/MarkdownTextBlockCustomSample.xaml @@ -41,6 +41,11 @@ + Text="{x:Bind Text, Mode=OneTime}" + UseAutoLinks="True" + UseEmphasisExtras="True" + UseListExtras="True" + UsePipeTables="True" + UseTaskLists="True" /> diff --git a/components/MarkdownTextBlock/src/MarkdownTextBlock.Properties.cs b/components/MarkdownTextBlock/src/MarkdownTextBlock.Properties.cs new file mode 100644 index 000000000..fb0bd4180 --- /dev/null +++ b/components/MarkdownTextBlock/src/MarkdownTextBlock.Properties.cs @@ -0,0 +1,170 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Markdig.Syntax; + +namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock; + +public partial class MarkdownTextBlock +{ + /// + /// Identifies the dependency property. + /// + private static readonly DependencyProperty ConfigProperty = DependencyProperty.Register( + nameof(Config), + typeof(MarkdownConfig), + typeof(MarkdownTextBlock), + new PropertyMetadata(null, OnConfigChanged) + ); + + /// + /// Identifies the dependency property. + /// + private static readonly DependencyProperty TextProperty = DependencyProperty.Register( + nameof(Text), + typeof(string), + typeof(MarkdownTextBlock), + new PropertyMetadata(null, OnTextChanged)); + + /// + /// Identifies the dependency property. + /// + private static readonly DependencyProperty UseEmphasisExtrasProperty = DependencyProperty.Register( + nameof(UseEmphasisExtras), + typeof(bool), + typeof(MarkdownTextBlock), + new PropertyMetadata(false)); + + /// + /// Identifies the dependency property. + /// + private static readonly DependencyProperty UsePipeTablesProperty = DependencyProperty.Register( + nameof(UsePipeTables), + typeof(bool), + typeof(MarkdownTextBlock), + new PropertyMetadata(false)); + + /// + /// Identifies the dependency property. + /// + private static readonly DependencyProperty UseListExtrasProperty = DependencyProperty.Register( + nameof(UseListExtras), + typeof(bool), + typeof(MarkdownTextBlock), + new PropertyMetadata(false)); + + /// + /// Identifies the dependency property. + /// + private static readonly DependencyProperty UseTaskListsProperty = DependencyProperty.Register( + nameof(UseTaskLists), + typeof(bool), + typeof(MarkdownTextBlock), + new PropertyMetadata(false)); + + /// + /// Identifies the dependency property. + /// + private static readonly DependencyProperty UseAutoLinksProperty = DependencyProperty.Register( + nameof(UseAutoLinks), + typeof(bool), + typeof(MarkdownTextBlock), + new PropertyMetadata(false)); + + /// + /// Identifies the dependency property. + /// + private static readonly DependencyProperty UseSoftlineBreakAsHardlineBreakProperty = DependencyProperty.Register( + nameof(UseSoftlineBreakAsHardlineBreak), + typeof(bool), + typeof(MarkdownTextBlock), + new PropertyMetadata(false)); + + /// + /// Identifies the dependency property. + /// + private static readonly DependencyProperty MarkdownDocumentProperty = DependencyProperty.Register( + nameof(MarkdownDocument), + typeof(MarkdownDocument), + typeof(MarkdownTextBlock), + new PropertyMetadata(null)); + + public MarkdownConfig Config + { + get => (MarkdownConfig)GetValue(ConfigProperty); + set => SetValue(ConfigProperty, value); + } + + /// + /// Gets or sets the markdown text to display. + /// + public string Text + { + get => (string)GetValue(TextProperty); + set => SetValue(TextProperty, value); + } + + /// + /// If true, adds support for strikethroughs, superscript, subscript, inserted, and marked text. + /// + public bool UseEmphasisExtras + { + get => (bool)GetValue(UseEmphasisExtrasProperty); + set => SetValue(UseEmphasisExtrasProperty, value); + } + + /// + /// If true, adds support for GitHub-style pipe tables. + /// + public bool UsePipeTables + { + get => (bool)GetValue(UsePipeTablesProperty); + set => SetValue(UsePipeTablesProperty, value); + } + + /// + /// If true, adds support for alphabetic and roman numbering in lists. + /// + public bool UseListExtras + { + get => (bool)GetValue(UseListExtrasProperty); + set => SetValue(UseListExtrasProperty, value); + } + + /// + /// If true, adds support for GitHub-style task lists using the [ ] and [x] syntax. + /// + public bool UseTaskLists + { + get => (bool)GetValue(UseTaskListsProperty); + set => SetValue(UseTaskListsProperty, value); + } + + /// + /// If true, parses text that looks like URIs into hyperlinks (e.g. https://...). + /// + public bool UseAutoLinks + { + get => (bool)GetValue(UseAutoLinksProperty); + set => SetValue(UseAutoLinksProperty, value); + } + + /// + /// If true, considers single newlines as hardline breaks. + /// + public bool UseSoftlineBreakAsHardlineBreak + { + get => (bool)GetValue(UseSoftlineBreakAsHardlineBreakProperty); + set => SetValue(UseSoftlineBreakAsHardlineBreakProperty, value); + } + + /// + /// Gets the parsed markdown document. May be null if the document has not been parsed yet. + /// + public MarkdownDocument? MarkdownDocument + { + get => (MarkdownDocument)GetValue(MarkdownDocumentProperty); + private set => SetValue(MarkdownDocumentProperty, value); + } +} diff --git a/components/MarkdownTextBlock/src/MarkdownTextBlock.xaml.cs b/components/MarkdownTextBlock/src/MarkdownTextBlock.xaml.cs index 01e05751f..d9b8be705 100644 --- a/components/MarkdownTextBlock/src/MarkdownTextBlock.xaml.cs +++ b/components/MarkdownTextBlock/src/MarkdownTextBlock.xaml.cs @@ -3,6 +3,9 @@ // See the LICENSE file in the project root for more information. using CommunityToolkit.Labs.WinUI.MarkdownTextBlock.Renderers; +using CommunityToolkit.Labs.WinUI.MarkdownTextBlock.Renderers.ObjectRenderers; +using CommunityToolkit.Labs.WinUI.MarkdownTextBlock.Renderers.ObjectRenderers.Extensions; +using CommunityToolkit.Labs.WinUI.MarkdownTextBlock.Renderers.ObjectRenderers.Inlines; using CommunityToolkit.Labs.WinUI.MarkdownTextBlock.TextElements; using Markdig; using Markdig.Syntax; @@ -14,47 +17,10 @@ public partial class MarkdownTextBlock : Control { private const string MarkdownContainerName = "MarkdownContainer"; private Grid? _container; - private MarkdownPipeline _pipeline; + private MarkdownPipeline _pipeline = null!; private MyFlowDocument _document; private WinUIRenderer? _renderer; - private static readonly DependencyProperty ConfigProperty = DependencyProperty.Register( - nameof(Config), - typeof(MarkdownConfig), - typeof(MarkdownTextBlock), - new PropertyMetadata(null, OnConfigChanged) - ); - - private static readonly DependencyProperty TextProperty = DependencyProperty.Register( - nameof(Text), - typeof(string), - typeof(MarkdownTextBlock), - new PropertyMetadata(null, OnTextChanged)); - - private static readonly DependencyProperty MarkdownDocumentProperty = DependencyProperty.Register( - nameof(MarkdownDocument), - typeof(MarkdownDocument), - typeof(MarkdownTextBlock), - new PropertyMetadata(null)); - - public MarkdownConfig Config - { - get => (MarkdownConfig)GetValue(ConfigProperty); - set => SetValue(ConfigProperty, value); - } - - public string Text - { - get => (string)GetValue(TextProperty); - set => SetValue(TextProperty, value); - } - - public MarkdownDocument? MarkdownDocument - { - get => (MarkdownDocument)GetValue(MarkdownDocumentProperty); - private set => SetValue(MarkdownDocumentProperty, value); - } - public event EventHandler? OnLinkClicked; internal void RaiseLinkClickedEvent(Uri uri) => OnLinkClicked?.Invoke(this, new LinkClickedEventArgs(uri)); @@ -79,17 +45,24 @@ public MarkdownTextBlock() { this.DefaultStyleKey = typeof(MarkdownTextBlock); _document = new MyFlowDocument(); - _pipeline = new MarkdownPipelineBuilder() - .UseEmphasisExtras() - .UseAutoLinks() - .UseTaskLists() - .UsePipeTables() - .Build(); } protected override void OnApplyTemplate() { base.OnApplyTemplate(); + + var pipelineBuilder = new MarkdownPipelineBuilder(); + + // NOTE: Order matters here + if (UseEmphasisExtras) pipelineBuilder = pipelineBuilder.UseEmphasisExtras(); + if (UsePipeTables) pipelineBuilder = pipelineBuilder.UsePipeTables(); + if (UseListExtras) pipelineBuilder = pipelineBuilder.UseListExtras(); + if (UseTaskLists) pipelineBuilder = pipelineBuilder.UseTaskLists(); + if (UseAutoLinks) pipelineBuilder = pipelineBuilder.UseAutoLinks(); + if (UseSoftlineBreakAsHardlineBreak) pipelineBuilder = pipelineBuilder.UseSoftlineBreakAsHardlineBreak(); + + _pipeline = pipelineBuilder.Build(); + _container = (Grid)GetTemplateChild(MarkdownContainerName); _container.Children.Clear(); _container.Children.Add(_document.RichTextBlock); @@ -128,6 +101,31 @@ private void Build() if (_renderer == null) { _renderer = new WinUIRenderer(_document, Config, this); + + // Default block renderers + _renderer.ObjectRenderers.Add(new CodeBlockRenderer()); + _renderer.ObjectRenderers.Add(new ListRenderer()); + _renderer.ObjectRenderers.Add(new HeadingRenderer()); + _renderer.ObjectRenderers.Add(new ParagraphRenderer()); + _renderer.ObjectRenderers.Add(new QuoteBlockRenderer()); + _renderer.ObjectRenderers.Add(new ThematicBreakRenderer()); + _renderer.ObjectRenderers.Add(new HtmlBlockRenderer()); + + // Default inline renderers + if (UseAutoLinks) _renderer.ObjectRenderers.Add(new AutoLinkInlineRenderer()); + _renderer.ObjectRenderers.Add(new CodeInlineRenderer()); + _renderer.ObjectRenderers.Add(new DelimiterInlineRenderer()); + _renderer.ObjectRenderers.Add(new EmphasisInlineRenderer()); + _renderer.ObjectRenderers.Add(new HtmlEntityInlineRenderer()); + _renderer.ObjectRenderers.Add(new LineBreakInlineRenderer()); + _renderer.ObjectRenderers.Add(new LinkInlineRenderer()); + _renderer.ObjectRenderers.Add(new LiteralInlineRenderer()); + _renderer.ObjectRenderers.Add(new ContainerInlineRenderer()); + + // Extension renderers + if (UsePipeTables) _renderer.ObjectRenderers.Add(new TableRenderer()); + if (UseTaskLists) _renderer.ObjectRenderers.Add(new TaskListRenderer()); + _renderer.ObjectRenderers.Add(new HtmlInlineRenderer()); } _pipeline.Setup(_renderer); ApplyText(false); diff --git a/components/MarkdownTextBlock/src/Renderers/WinUIRenderer.cs b/components/MarkdownTextBlock/src/Renderers/WinUIRenderer.cs index a9f772423..c4e00543b 100644 --- a/components/MarkdownTextBlock/src/Renderers/WinUIRenderer.cs +++ b/components/MarkdownTextBlock/src/Renderers/WinUIRenderer.cs @@ -33,12 +33,6 @@ public WinUIRenderer(MyFlowDocument document, MarkdownConfig config, MarkdownTex FlowDocument = document; // set style _stack.Push(FlowDocument); - LoadOverridenRenderers(); - } - - private void LoadOverridenRenderers() - { - LoadRenderers(); } public override object Render(MarkdownObject markdownObject) @@ -52,7 +46,6 @@ public void ReloadDocument() _stack.Clear(); FlowDocument.RichTextBlock.Blocks.Clear(); _stack.Push(FlowDocument); - LoadOverridenRenderers(); } public void WriteLeafInline(LeafBlock leafBlock) @@ -145,32 +138,4 @@ private static void AddInline(IAddChild parent, IAddChild inline) { parent.AddChild(inline); } - - protected virtual void LoadRenderers() - { - // Default block renderers - ObjectRenderers.Add(new CodeBlockRenderer()); - ObjectRenderers.Add(new ListRenderer()); - ObjectRenderers.Add(new HeadingRenderer()); - ObjectRenderers.Add(new ParagraphRenderer()); - ObjectRenderers.Add(new QuoteBlockRenderer()); - ObjectRenderers.Add(new ThematicBreakRenderer()); - ObjectRenderers.Add(new HtmlBlockRenderer()); - - // Default inline renderers - ObjectRenderers.Add(new AutoLinkInlineRenderer()); - ObjectRenderers.Add(new CodeInlineRenderer()); - ObjectRenderers.Add(new DelimiterInlineRenderer()); - ObjectRenderers.Add(new EmphasisInlineRenderer()); - ObjectRenderers.Add(new HtmlEntityInlineRenderer()); - ObjectRenderers.Add(new LineBreakInlineRenderer()); - ObjectRenderers.Add(new LinkInlineRenderer()); - ObjectRenderers.Add(new LiteralInlineRenderer()); - ObjectRenderers.Add(new ContainerInlineRenderer()); - - // Extension renderers - ObjectRenderers.Add(new TableRenderer()); - ObjectRenderers.Add(new TaskListRenderer()); - ObjectRenderers.Add(new HtmlInlineRenderer()); - } }