Skip to content

Commit c50a4b8

Browse files
author
Juan Osorio
committed
MarkdownTextBlock: Rewrite lists to be text-based
1 parent 1f6cda0 commit c50a4b8

File tree

8 files changed

+149
-144
lines changed

8 files changed

+149
-144
lines changed

components/MarkdownTextBlock/src/MarkdownTextBlock.xaml.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,8 @@ private void Build()
106106

107107
// Default block renderers
108108
_renderer.ObjectRenderers.Add(new CodeBlockRenderer());
109-
_renderer.ObjectRenderers.Add(new ListRenderer());
109+
_renderer.ObjectRenderers.Add(new ListRenderer());
110+
_renderer.ObjectRenderers.Add(new ListItemRenderer());
110111
_renderer.ObjectRenderers.Add(new HeadingRenderer());
111112
_renderer.ObjectRenderers.Add(new ParagraphRenderer());
112113
_renderer.ObjectRenderers.Add(new QuoteBlockRenderer());
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using Markdig.Syntax;
6+
7+
namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock.Renderers.ObjectRenderers;
8+
9+
internal class ListItemRenderer : UWPObjectRenderer<ListItemBlock>
10+
{
11+
protected override void Write(WinUIRenderer renderer, ListItemBlock listItem)
12+
{
13+
if (renderer == null) throw new ArgumentNullException(nameof(renderer));
14+
if (listItem == null) throw new ArgumentNullException(nameof(listItem));
15+
16+
renderer.WriteChildren(listItem);
17+
}
18+
}

components/MarkdownTextBlock/src/Renderers/ObjectRenderers/ListRenderer.cs

Lines changed: 64 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,82 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5-
using Markdig.Syntax;
5+
using System.Globalization;
66
using CommunityToolkit.Labs.WinUI.MarkdownTextBlock.TextElements;
7+
using Markdig.Syntax;
8+
using RomanNumerals;
79

810
namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock.Renderers.ObjectRenderers;
911

1012
internal class ListRenderer : UWPObjectRenderer<ListBlock>
1113
{
14+
public const string UnorderedListDot = "• ";
15+
1216
protected override void Write(WinUIRenderer renderer, ListBlock listBlock)
1317
{
14-
if (renderer == null) throw new ArgumentNullException(nameof(renderer));
15-
if (listBlock == null) throw new ArgumentNullException(nameof(listBlock));
16-
17-
var list = new MyList(listBlock);
18+
int index = 1;
19+
bool isOrdered = false;
20+
BulletType bulletType = BulletType.Circle;
21+
if (listBlock.IsOrdered)
22+
{
23+
isOrdered = true;
24+
bulletType = ToOrderedBulletType(listBlock.BulletType);
1825

19-
renderer.Push(list);
26+
if (listBlock.OrderedStart != null && listBlock.DefaultOrderedStart != listBlock.OrderedStart)
27+
{
28+
int.TryParse(listBlock.OrderedStart, NumberStyles.Number, NumberFormatInfo.InvariantInfo, out index);
29+
}
30+
}
2031

21-
foreach (var item in listBlock)
32+
bool parentIsFirstBulletItem = renderer.IsFirstBulletItem;
33+
bool parentIsLastBulletItem = renderer.IsLastBulletItem;
34+
renderer.IsFirstBulletItem = true;
35+
renderer.IsLastBulletItem = listBlock.Count == 1;
36+
foreach (var listItem in listBlock)
2237
{
23-
var listItemBlock = (ListItemBlock)item;
24-
var listItem = new MyBlockContainer(listItemBlock);
25-
renderer.Push(listItem);
26-
renderer.WriteChildren(listItemBlock);
27-
renderer.Pop();
38+
renderer.PushListBullet(GetBulletString(isOrdered, bulletType, index));
39+
renderer.Write(listItem);
40+
renderer.PopListBullet();
41+
index++;
42+
43+
renderer.IsFirstBulletItem = false;
44+
renderer.IsLastBulletItem = listBlock.Count == index;
2845
}
46+
renderer.IsFirstBulletItem = parentIsFirstBulletItem;
47+
renderer.IsLastBulletItem = parentIsLastBulletItem;
48+
}
2949

30-
renderer.Pop();
50+
internal static BulletType ToOrderedBulletType(char bullet)
51+
{
52+
return bullet switch
53+
{
54+
'1' => BulletType.Number,
55+
'a' => BulletType.LowerAlpha,
56+
'A' => BulletType.UpperAlpha,
57+
'i' => BulletType.LowerRoman,
58+
'I' => BulletType.UpperRoman,
59+
_ => BulletType.Number,
60+
};
61+
}
62+
63+
private static string GetBulletString(bool isOrdered, BulletType bulletType, int index)
64+
{
65+
if (isOrdered)
66+
{
67+
return bulletType switch
68+
{
69+
BulletType.Number => $"{index}. ",
70+
BulletType.LowerAlpha => $"{index.ToAlphabetical()}. ",
71+
BulletType.UpperAlpha => $"{index.ToAlphabetical().ToUpper(CultureInfo.CurrentCulture)}. ",
72+
BulletType.LowerRoman => $"{index.ToRomanNumerals().ToLower(CultureInfo.CurrentCulture)} ",
73+
BulletType.UpperRoman => $"{index.ToRomanNumerals().ToUpper(CultureInfo.CurrentCulture)} ",
74+
BulletType.Circle => UnorderedListDot,
75+
_ => $"{index}. "
76+
};
77+
}
78+
else
79+
{
80+
return UnorderedListDot;
81+
}
3182
}
3283
}

components/MarkdownTextBlock/src/Renderers/ObjectRenderers/ParagraphRenderer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ protected override void Write(WinUIRenderer renderer, ParagraphBlock obj)
1414
if (renderer == null) throw new ArgumentNullException(nameof(renderer));
1515
if (obj == null) throw new ArgumentNullException(nameof(obj));
1616

17-
var paragraph = new MyParagraph(obj);
17+
var paragraph = new MyParagraph(obj, renderer);
1818
// set style
1919
renderer.Push(paragraph);
2020
renderer.WriteLeafInline(obj);

components/MarkdownTextBlock/src/Renderers/WinUIRenderer.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ public class WinUIRenderer : RendererBase
1717
private readonly Stack<IAddChild> _stack = new Stack<IAddChild>();
1818
private char[] _buffer;
1919
private MarkdownConfig _config = MarkdownConfig.Default;
20+
private readonly Stack<string> _listBullets = new();
21+
2022
public MyFlowDocument FlowDocument { get; private set; }
2123
public MarkdownConfig Config
2224
{
@@ -25,6 +27,10 @@ public MarkdownConfig Config
2527
}
2628
public MarkdownTextBlock MarkdownTextBlock { get; }
2729

30+
internal bool IsFirstBulletItem { get; set; }
31+
32+
internal bool IsLastBulletItem { get; set; }
33+
2834
public WinUIRenderer(MyFlowDocument document, MarkdownConfig config, MarkdownTextBlock markdownTextBlock)
2935
{
3036
_buffer = new char[1024];
@@ -134,6 +140,29 @@ public void WriteText(string? text, int offset, int length)
134140
}
135141
}
136142

143+
public void PushListBullet(string bullet)
144+
{
145+
_listBullets.Push(bullet);
146+
}
147+
148+
public string PeekListBullet()
149+
{
150+
return _listBullets.Count > 0 ? _listBullets.Peek() : string.Empty;
151+
}
152+
153+
public int GetListBulletCount()
154+
{
155+
return _listBullets.Count;
156+
}
157+
158+
public void PopListBullet()
159+
{
160+
if (_listBullets.Count > 0)
161+
{
162+
_listBullets.Pop();
163+
}
164+
}
165+
137166
private static void AddInline(IAddChild parent, IAddChild inline)
138167
{
139168
parent.AddChild(inline);
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock.TextElements;
6+
7+
internal enum BulletType
8+
{
9+
Circle,
10+
Number,
11+
LowerAlpha,
12+
UpperAlpha,
13+
LowerRoman,
14+
UpperRoman
15+
}

components/MarkdownTextBlock/src/TextElements/MyList.cs

Lines changed: 0 additions & 128 deletions
This file was deleted.

components/MarkdownTextBlock/src/TextElements/MyParagraph.cs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5+
using CommunityToolkit.Labs.WinUI.MarkdownTextBlock.Renderers;
56
using Markdig.Syntax;
67

78
namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock.TextElements;
89

910
internal class MyParagraph : IAddChild
1011
{
12+
private readonly WinUIRenderer _renderer;
1113
private ParagraphBlock _paragraphBlock;
1214
private Paragraph _paragraph;
1315

@@ -16,10 +18,27 @@ public TextElement TextElement
1618
get => _paragraph;
1719
}
1820

19-
public MyParagraph(ParagraphBlock paragraphBlock)
21+
public MyParagraph(ParagraphBlock paragraphBlock, WinUIRenderer renderer)
2022
{
2123
_paragraphBlock = paragraphBlock;
2224
_paragraph = new Paragraph();
25+
_renderer = renderer;
26+
27+
// Lists are plain Paragraph_s, one per item.
28+
// This is so that you can select across list items.
29+
Thickness margin = new Thickness(0, 8, 0, 8); // renderer.Config.Themes.BlockMargin;
30+
int bulletCount = renderer.GetListBulletCount();
31+
margin.Left += 30 * bulletCount;
32+
_paragraph.Margin = margin;
33+
34+
if (bulletCount != 0)
35+
{
36+
string bullet = renderer.PeekListBullet();
37+
Run bulletRun = new Run { Text = bullet + "\t" };
38+
39+
_paragraph.Inlines.Add(bulletRun);
40+
_paragraph.TextIndent = -30;
41+
}
2342
}
2443

2544
public void AddChild(IAddChild child)

0 commit comments

Comments
 (0)