From 8d0aa5249cdc31d78e6f740ba838bc6d624a6d6c Mon Sep 17 00:00:00 2001
From: bluepilledgreat <97983689+bluepilledgreat@users.noreply.github.com>
Date: Mon, 29 Jan 2024 20:10:43 +0000
Subject: [PATCH 1/4] more robust markdowntextblock
---
.../UI/Elements/Controls/MarkdownTextBlock.cs | 109 ++++++++++++++----
1 file changed, 89 insertions(+), 20 deletions(-)
diff --git a/Bloxstrap/UI/Elements/Controls/MarkdownTextBlock.cs b/Bloxstrap/UI/Elements/Controls/MarkdownTextBlock.cs
index 6e1e0d7..bd97b8f 100644
--- a/Bloxstrap/UI/Elements/Controls/MarkdownTextBlock.cs
+++ b/Bloxstrap/UI/Elements/Controls/MarkdownTextBlock.cs
@@ -6,6 +6,7 @@ using System.Windows;
using Markdig.Syntax;
using Markdig.Syntax.Inlines;
using Markdig;
+using System.Windows.Media;
namespace Bloxstrap.UI.Elements.Controls
{
@@ -19,15 +20,97 @@ namespace Bloxstrap.UI.Elements.Controls
{
public static readonly DependencyProperty MarkdownTextProperty =
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));
[Localizability(LocalizationCategory.Text)]
public string MarkdownText
{
- get => Inlines.ToString() ?? "";
+ get => (string)GetValue(MarkdownTextProperty);
set => SetValue(MarkdownTextProperty, value);
}
+ /// Span, skip
+ private static (Span, int) GetInlineUntilEndTagDetected(Markdig.Syntax.Inlines.Inline? inline, string tagName)
+ {
+ string endTag = $"<{tagName}/>"; // TODO: better way of doing this
+
+ var span = new Span();
+
+ int skip = 0;
+ var current = inline;
+ while (current is Markdig.Syntax.Inlines.Inline currentInline)
+ {
+ skip++;
+
+ if (currentInline is HtmlInline html)
+ {
+ if (html.Tag == endTag)
+ return (span, skip);
+ }
+
+ (var childInline, int childSkip) = GetWpfInlineFromMarkdownInline(currentInline);
+ if (childInline != null)
+ span.Inlines.Add(childInline);
+
+ skip += childSkip;
+
+ current = currentInline.NextSibling;
+ }
+
+ throw new Exception("End tag not detected");
+ }
+
+ /// Inline, skip
+ private static (System.Windows.Documents.Inline?, int) GetWpfInlineFromMarkdownInline(Markdig.Syntax.Inlines.Inline? inline)
+ {
+ if (inline is LiteralInline literalInline)
+ {
+ return (new Run(literalInline.ToString()), 0);
+ }
+ else if (inline is LinkInline linkInline)
+ {
+ string? url = linkInline.Url;
+ var textInline = linkInline.FirstChild;
+
+ if (string.IsNullOrEmpty(url))
+ {
+ return GetWpfInlineFromMarkdownInline(textInline);
+ }
+
+ (var childInline, int skip) = GetWpfInlineFromMarkdownInline(textInline);
+
+ return (new Hyperlink(childInline)
+ {
+ Command = GlobalViewModel.OpenWebpageCommand,
+ CommandParameter = url
+ }, skip);
+ }
+ else if (inline is HtmlInline htmlInline)
+ {
+ string? tag = htmlInline.Tag; // TODO: parse tag
+ var nextInline = htmlInline.NextSibling;
+
+ if (tag == "")
+ {
+ (var span, int skip) = GetInlineUntilEndTagDetected(nextInline, "highlight");
+ span.Background = new SolidColorBrush(Color.FromArgb(50,255,255,255));
+ return (span, skip);
+ }
+ }
+
+ return (null, 0);
+ }
+
+ /// Skip
+ private int AddMarkdownInline(Markdig.Syntax.Inlines.Inline? inline)
+ {
+ (var wpfInline, int skip) = GetWpfInlineFromMarkdownInline(inline);
+ if (wpfInline != null)
+ Inlines.Add(wpfInline);
+
+ return skip;
+ }
+
private static void OnTextMarkdownChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
if (dependencyObject is not MarkdownTextBlock markdownTextBlock)
@@ -43,26 +126,12 @@ namespace Bloxstrap.UI.Elements.Controls
if (document.FirstOrDefault() is not ParagraphBlock paragraphBlock || paragraphBlock.Inline == null)
return;
- foreach (var inline in paragraphBlock.Inline)
+ for (int i = 0; i < paragraphBlock.Inline.Count(); i++)
{
- if (inline is LiteralInline literalInline)
- {
- markdownTextBlock.Inlines.Add(new Run(literalInline.ToString()));
- }
- else if (inline is LinkInline linkInline)
- {
- string? url = linkInline.Url;
- string? text = linkInline.FirstChild?.ToString();
+ var inline = paragraphBlock.Inline.ElementAt(i);
- if (string.IsNullOrEmpty(url) || string.IsNullOrEmpty(text))
- continue;
-
- markdownTextBlock.Inlines.Add(new Hyperlink(new Run(text))
- {
- Command = GlobalViewModel.OpenWebpageCommand,
- CommandParameter = url
- });
- }
+ int skip = markdownTextBlock.AddMarkdownInline(inline);
+ i += skip;
}
}
}
From dd70d729f10dd1527c6e28b9d747c740c0ca6a3e Mon Sep 17 00:00:00 2001
From: bluepilledgreat <97983689+bluepilledgreat@users.noreply.github.com>
Date: Mon, 29 Jan 2024 20:13:42 +0000
Subject: [PATCH 2/4] oops
---
Bloxstrap/UI/Elements/Controls/MarkdownTextBlock.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Bloxstrap/UI/Elements/Controls/MarkdownTextBlock.cs b/Bloxstrap/UI/Elements/Controls/MarkdownTextBlock.cs
index bd97b8f..c99b236 100644
--- a/Bloxstrap/UI/Elements/Controls/MarkdownTextBlock.cs
+++ b/Bloxstrap/UI/Elements/Controls/MarkdownTextBlock.cs
@@ -32,7 +32,7 @@ namespace Bloxstrap.UI.Elements.Controls
/// Span, skip
private static (Span, int) GetInlineUntilEndTagDetected(Markdig.Syntax.Inlines.Inline? inline, string tagName)
{
- string endTag = $"<{tagName}/>"; // TODO: better way of doing this
+ string endTag = $"{tagName}>"; // TODO: better way of doing this
var span = new Span();
From 70a39376c8db3a126e6d067d137e541e9147ea96 Mon Sep 17 00:00:00 2001
From: bluepilledgreat <97983689+bluepilledgreat@users.noreply.github.com>
Date: Sat, 3 Feb 2024 00:33:31 +0000
Subject: [PATCH 3/4] bold, italic, and better marked text support
---
.../UI/Elements/Controls/MarkdownTextBlock.cs | 105 ++++++++----------
1 file changed, 47 insertions(+), 58 deletions(-)
diff --git a/Bloxstrap/UI/Elements/Controls/MarkdownTextBlock.cs b/Bloxstrap/UI/Elements/Controls/MarkdownTextBlock.cs
index c99b236..f571ce2 100644
--- a/Bloxstrap/UI/Elements/Controls/MarkdownTextBlock.cs
+++ b/Bloxstrap/UI/Elements/Controls/MarkdownTextBlock.cs
@@ -18,6 +18,8 @@ namespace Bloxstrap.UI.Elements.Controls
[Localizability(LocalizationCategory.Text)]
class MarkdownTextBlock : TextBlock
{
+ private static MarkdownPipeline _markdownPipeline;
+
public static readonly DependencyProperty MarkdownTextProperty =
DependencyProperty.Register(nameof(MarkdownText), typeof(string), typeof(MarkdownTextBlock),
new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, OnTextMarkdownChanged));
@@ -29,43 +31,39 @@ namespace Bloxstrap.UI.Elements.Controls
set => SetValue(MarkdownTextProperty, value);
}
- /// Span, skip
- private static (Span, int) GetInlineUntilEndTagDetected(Markdig.Syntax.Inlines.Inline? inline, string tagName)
- {
- string endTag = $"{tagName}>"; // TODO: better way of doing this
-
- var span = new Span();
-
- int skip = 0;
- var current = inline;
- while (current is Markdig.Syntax.Inlines.Inline currentInline)
- {
- skip++;
-
- if (currentInline is HtmlInline html)
- {
- if (html.Tag == endTag)
- return (span, skip);
- }
-
- (var childInline, int childSkip) = GetWpfInlineFromMarkdownInline(currentInline);
- if (childInline != null)
- span.Inlines.Add(childInline);
-
- skip += childSkip;
-
- current = currentInline.NextSibling;
- }
-
- throw new Exception("End tag not detected");
- }
-
- /// Inline, skip
- private static (System.Windows.Documents.Inline?, int) GetWpfInlineFromMarkdownInline(Markdig.Syntax.Inlines.Inline? inline)
+ private static System.Windows.Documents.Inline? GetWpfInlineFromMarkdownInline(Markdig.Syntax.Inlines.Inline? inline)
{
if (inline is LiteralInline literalInline)
{
- return (new Run(literalInline.ToString()), 0);
+ 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)
{
@@ -77,38 +75,23 @@ namespace Bloxstrap.UI.Elements.Controls
return GetWpfInlineFromMarkdownInline(textInline);
}
- (var childInline, int skip) = GetWpfInlineFromMarkdownInline(textInline);
+ var childInline = GetWpfInlineFromMarkdownInline(textInline);
- return (new Hyperlink(childInline)
+ return new Hyperlink(childInline)
{
Command = GlobalViewModel.OpenWebpageCommand,
CommandParameter = url
- }, skip);
- }
- else if (inline is HtmlInline htmlInline)
- {
- string? tag = htmlInline.Tag; // TODO: parse tag
- var nextInline = htmlInline.NextSibling;
-
- if (tag == "")
- {
- (var span, int skip) = GetInlineUntilEndTagDetected(nextInline, "highlight");
- span.Background = new SolidColorBrush(Color.FromArgb(50,255,255,255));
- return (span, skip);
- }
+ };
}
- return (null, 0);
+ return null;
}
- /// Skip
- private int AddMarkdownInline(Markdig.Syntax.Inlines.Inline? inline)
+ private void AddMarkdownInline(Markdig.Syntax.Inlines.Inline? inline)
{
- (var wpfInline, int skip) = GetWpfInlineFromMarkdownInline(inline);
+ var wpfInline = GetWpfInlineFromMarkdownInline(inline);
if (wpfInline != null)
Inlines.Add(wpfInline);
-
- return skip;
}
private static void OnTextMarkdownChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
@@ -119,7 +102,7 @@ namespace Bloxstrap.UI.Elements.Controls
if (dependencyPropertyChangedEventArgs.NewValue is not string rawDocument)
return;
- MarkdownDocument document = Markdown.Parse(rawDocument);
+ MarkdownDocument document = Markdown.Parse(rawDocument, _markdownPipeline);
markdownTextBlock.Inlines.Clear();
@@ -130,9 +113,15 @@ namespace Bloxstrap.UI.Elements.Controls
{
var inline = paragraphBlock.Inline.ElementAt(i);
- int skip = markdownTextBlock.AddMarkdownInline(inline);
- i += skip;
+ markdownTextBlock.AddMarkdownInline(inline);
}
}
+
+ static MarkdownTextBlock()
+ {
+ _markdownPipeline = new MarkdownPipelineBuilder()
+ .UseEmphasisExtras(Markdig.Extensions.EmphasisExtras.EmphasisExtraOptions.Marked) // enable '==' support
+ .Build();
+ }
}
}
From a97089491a12735ddf6ec4562c4c97b925e17d28 Mon Sep 17 00:00:00 2001
From: bluepilledgreat <97983689+bluepilledgreat@users.noreply.github.com>
Date: Sat, 3 Feb 2024 13:07:42 +0000
Subject: [PATCH 4/4] remove old comment
---
Bloxstrap/UI/Elements/Controls/MarkdownTextBlock.cs | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/Bloxstrap/UI/Elements/Controls/MarkdownTextBlock.cs b/Bloxstrap/UI/Elements/Controls/MarkdownTextBlock.cs
index f571ce2..48c732e 100644
--- a/Bloxstrap/UI/Elements/Controls/MarkdownTextBlock.cs
+++ b/Bloxstrap/UI/Elements/Controls/MarkdownTextBlock.cs
@@ -11,8 +11,7 @@ using System.Windows.Media;
namespace Bloxstrap.UI.Elements.Controls
{
///
- /// TextBlock with markdown support.
- /// Only supports text and urls.
+ /// TextBlock with markdown support.
///
[ContentProperty("MarkdownText")]
[Localizability(LocalizationCategory.Text)]