Merge pull request #1219 from bluepilledgreat/feature/robust-markdowntextblock

More robust MarkdownTextBlock
This commit is contained in:
pizzaboxer 2024-02-03 18:11:29 +00:00 committed by GitHub
commit 9622a5d6ee
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -6,17 +6,19 @@ using System.Windows;
using Markdig.Syntax; using Markdig.Syntax;
using Markdig.Syntax.Inlines; using Markdig.Syntax.Inlines;
using Markdig; using Markdig;
using System.Windows.Media;
namespace Bloxstrap.UI.Elements.Controls namespace Bloxstrap.UI.Elements.Controls
{ {
/// <summary> /// <summary>
/// TextBlock with markdown support. <br/> /// TextBlock with markdown support.
/// Only supports text and urls.
/// </summary> /// </summary>
[ContentProperty("MarkdownText")] [ContentProperty("MarkdownText")]
[Localizability(LocalizationCategory.Text)] [Localizability(LocalizationCategory.Text)]
class MarkdownTextBlock : TextBlock class MarkdownTextBlock : TextBlock
{ {
private static MarkdownPipeline _markdownPipeline;
public static readonly DependencyProperty MarkdownTextProperty = public static readonly DependencyProperty MarkdownTextProperty =
DependencyProperty.Register(nameof(MarkdownText), typeof(string), typeof(MarkdownTextBlock), DependencyProperty.Register(nameof(MarkdownText), typeof(string), typeof(MarkdownTextBlock),
new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, OnTextMarkdownChanged)); new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, OnTextMarkdownChanged));
@ -24,10 +26,73 @@ namespace Bloxstrap.UI.Elements.Controls
[Localizability(LocalizationCategory.Text)] [Localizability(LocalizationCategory.Text)]
public string MarkdownText public string MarkdownText
{ {
get => Inlines.ToString() ?? ""; get => (string)GetValue(MarkdownTextProperty);
set => SetValue(MarkdownTextProperty, value); set => SetValue(MarkdownTextProperty, value);
} }
private static System.Windows.Documents.Inline? GetWpfInlineFromMarkdownInline(Markdig.Syntax.Inlines.Inline? inline)
{
if (inline is LiteralInline literalInline)
{
return new Run(literalInline.ToString());
}
else if (inline is EmphasisInline emphasisInline)
{
switch (emphasisInline.DelimiterChar)
{
case '*':
case '_':
{
if (emphasisInline.DelimiterCount == 1) // 1 = italic
{
var childInline = new Italic(GetWpfInlineFromMarkdownInline(emphasisInline.FirstChild));
return childInline;
}
else // 2 = bold
{
var childInline = new Bold(GetWpfInlineFromMarkdownInline(emphasisInline.FirstChild));
return childInline;
}
}
case '=': // marked
{
var childInline = new Span(GetWpfInlineFromMarkdownInline(emphasisInline.FirstChild));
childInline.Background = new SolidColorBrush(Color.FromArgb(50, 255, 255, 255)); // TODO: better colour?
return childInline;
}
}
}
else if (inline is LinkInline linkInline)
{
string? url = linkInline.Url;
var textInline = linkInline.FirstChild;
if (string.IsNullOrEmpty(url))
{
return GetWpfInlineFromMarkdownInline(textInline);
}
var childInline = GetWpfInlineFromMarkdownInline(textInline);
return new Hyperlink(childInline)
{
Command = GlobalViewModel.OpenWebpageCommand,
CommandParameter = url
};
}
return null;
}
private void AddMarkdownInline(Markdig.Syntax.Inlines.Inline? inline)
{
var wpfInline = GetWpfInlineFromMarkdownInline(inline);
if (wpfInline != null)
Inlines.Add(wpfInline);
}
private static void OnTextMarkdownChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs) private static void OnTextMarkdownChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{ {
if (dependencyObject is not MarkdownTextBlock markdownTextBlock) if (dependencyObject is not MarkdownTextBlock markdownTextBlock)
@ -36,34 +101,26 @@ namespace Bloxstrap.UI.Elements.Controls
if (dependencyPropertyChangedEventArgs.NewValue is not string rawDocument) if (dependencyPropertyChangedEventArgs.NewValue is not string rawDocument)
return; return;
MarkdownDocument document = Markdown.Parse(rawDocument); MarkdownDocument document = Markdown.Parse(rawDocument, _markdownPipeline);
markdownTextBlock.Inlines.Clear(); markdownTextBlock.Inlines.Clear();
if (document.FirstOrDefault() is not ParagraphBlock paragraphBlock || paragraphBlock.Inline == null) if (document.FirstOrDefault() is not ParagraphBlock paragraphBlock || paragraphBlock.Inline == null)
return; return;
foreach (var inline in paragraphBlock.Inline) for (int i = 0; i < paragraphBlock.Inline.Count(); i++)
{ {
if (inline is LiteralInline literalInline) var inline = paragraphBlock.Inline.ElementAt(i);
{
markdownTextBlock.Inlines.Add(new Run(literalInline.ToString()));
}
else if (inline is LinkInline linkInline)
{
string? url = linkInline.Url;
string? text = linkInline.FirstChild?.ToString();
if (string.IsNullOrEmpty(url) || string.IsNullOrEmpty(text)) markdownTextBlock.AddMarkdownInline(inline);
continue; }
}
markdownTextBlock.Inlines.Add(new Hyperlink(new Run(text)) static MarkdownTextBlock()
{ {
Command = GlobalViewModel.OpenWebpageCommand, _markdownPipeline = new MarkdownPipelineBuilder()
CommandParameter = url .UseEmphasisExtras(Markdig.Extensions.EmphasisExtras.EmphasisExtraOptions.Marked) // enable '==' support
}); .Build();
}
}
} }
} }
} }