diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 02c0edab0..bc9dec043 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,6 +18,8 @@ on: env: DOTNET_VERSION: ${{ '8.0.201' }} + DOTNET_INSTALL_DIR: dotnet-install + DOTNET_ROOT: dotnet-install ENABLE_DIAGNOSTICS: false #COREHOST_TRACE: 1 MSBUILD_VERBOSITY: normal @@ -257,9 +259,19 @@ jobs: with: vs-version: '[17.9,)' + - name: Define excluded MultiTargets (WinUI 2) + if: ${{ matrix.winui == '2' }} + run: | + echo "EXCLUDED_MULTITARGETS=wasdk" >> $env:GITHUB_ENV + + - name: Define excluded MultiTargets (WinUI 3) + if: ${{ matrix.winui == '3' }} + run: | + echo "EXCLUDED_MULTITARGETS=uwp" >> $env:GITHUB_ENV + # Build and pack component nupkg - name: Build and pack component packages - run: ./tooling/Build-Toolkit-Components.ps1 -MultiTargets all -WinUIMajorVersion ${{ matrix.winui }} -DateForVersion ${{ env.VERSION_DATE }} ${{ env.VERSION_PROPERTY != '' && format('-PreviewVersion "{0}"', env.VERSION_PROPERTY) || '' }} ${{ env.ENABLE_DIAGNOSTICS == 'true' && '-EnableBinlogs' || '' }} ${{ env.ENABLE_DIAGNOSTICS == 'true' && '-Verbose' || '' }} -BinlogOutput ./ -NupkgOutput ./ -Release + run: ./tooling/Build-Toolkit-Components.ps1 -MultiTargets all -ExcludeMultiTargets ${{ env.EXCLUDED_MULTITARGETS }} -WinUIMajorVersion ${{ matrix.winui }} -DateForVersion ${{ env.VERSION_DATE }} ${{ env.VERSION_PROPERTY != '' && format('-PreviewVersion "{0}"', env.VERSION_PROPERTY) || '' }} ${{ env.ENABLE_DIAGNOSTICS == 'true' && '-EnableBinlogs' || '' }} ${{ env.ENABLE_DIAGNOSTICS == 'true' && '-Verbose' || '' }} -BinlogOutput ./ -NupkgOutput ./ -Release - name: Validate package names if: ${{ env.VERSION_PROPERTY != '' }} diff --git a/Directory.Build.props b/Directory.Build.props index 74436b013..72e05671d 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -12,7 +12,10 @@ true true - $(NoWarn);Uno0001 + $(NoWarn);Uno0001 + + + NU1901;NU1902;NU1903;NU1904 diff --git a/components/MarkdownTextBlock/samples/MarkdownTextBlockCustomSample.xaml b/components/MarkdownTextBlock/samples/MarkdownTextBlockCustomSample.xaml index 7173b399d..1ead34838 100644 --- a/components/MarkdownTextBlock/samples/MarkdownTextBlockCustomSample.xaml +++ b/components/MarkdownTextBlock/samples/MarkdownTextBlockCustomSample.xaml @@ -1,4 +1,4 @@ - + - - diff --git a/components/MarkdownTextBlock/samples/MarkdownTextBlockCustomSample.xaml.cs b/components/MarkdownTextBlock/samples/MarkdownTextBlockCustomSample.xaml.cs index 01e3127dd..413bd1d6e 100644 --- a/components/MarkdownTextBlock/samples/MarkdownTextBlockCustomSample.xaml.cs +++ b/components/MarkdownTextBlock/samples/MarkdownTextBlockCustomSample.xaml.cs @@ -607,5 +607,12 @@ public MarkdownTextBlockCustomSample() _liveConfig = new MarkdownConfig(); _text = _markdown; MarkdownTextBox.Text = "# Hello World\n\n"; + MarkdownTextBlock1.OnLinkClicked += MarkdownTextBlock_OnLinkClicked; + MarkdownTextBlock2.OnLinkClicked += MarkdownTextBlock_OnLinkClicked; + } + + private void MarkdownTextBlock_OnLinkClicked(object? sender, LinkClickedEventArgs e) + { + Debug.WriteLine($"Link Clicked: {e.Uri}"); } } diff --git a/components/MarkdownTextBlock/src/HtmlWriter.cs b/components/MarkdownTextBlock/src/HtmlWriter.cs index 7875c0f01..3a9ece776 100644 --- a/components/MarkdownTextBlock/src/HtmlWriter.cs +++ b/components/MarkdownTextBlock/src/HtmlWriter.cs @@ -33,11 +33,21 @@ public static void WriteHtml(WinUIRenderer renderer, HtmlNodeCollection nodes) IAddChild hyperLink; if (node.ChildNodes.Any(n => n.Name != "#text")) { - hyperLink = new MyHyperlinkButton(node, renderer.Config.BaseUrl); + var myHyperlinkButton = new MyHyperlinkButton(node, renderer.Config.BaseUrl); + myHyperlinkButton.ClickEvent += (sender, e) => + { + renderer.MarkdownTextBlock.RaiseLinkClickedEvent(((HyperlinkButton)sender).NavigateUri); + }; + hyperLink = myHyperlinkButton; } else { - hyperLink = new MyHyperlink(node, renderer.Config.BaseUrl); + var myHyperlink = new MyHyperlink(node, renderer.Config.BaseUrl); + myHyperlink.ClickEvent += (sender, e) => + { + renderer.MarkdownTextBlock.RaiseLinkClickedEvent(sender.NavigateUri); + }; + hyperLink = myHyperlink; } renderer.Push(hyperLink); WriteHtml(renderer, node.ChildNodes); diff --git a/components/MarkdownTextBlock/src/LinkClickedEventArgs.cs b/components/MarkdownTextBlock/src/LinkClickedEventArgs.cs new file mode 100644 index 000000000..9b1278bc8 --- /dev/null +++ b/components/MarkdownTextBlock/src/LinkClickedEventArgs.cs @@ -0,0 +1,15 @@ +// 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. + +namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock; + +public class LinkClickedEventArgs : EventArgs +{ + public Uri Uri { get; } + + public LinkClickedEventArgs(Uri uri) + { + this.Uri = uri; + } +} diff --git a/components/MarkdownTextBlock/src/MarkdownTextBlock.xaml.cs b/components/MarkdownTextBlock/src/MarkdownTextBlock.xaml.cs index efba3cb4b..eef90cf74 100644 --- a/components/MarkdownTextBlock/src/MarkdownTextBlock.xaml.cs +++ b/components/MarkdownTextBlock/src/MarkdownTextBlock.xaml.cs @@ -42,6 +42,10 @@ public string Text set => SetValue(TextProperty, value); } + public event EventHandler? OnLinkClicked; + + internal void RaiseLinkClickedEvent(Uri uri) => OnLinkClicked?.Invoke(this, new LinkClickedEventArgs(uri)); + private static void OnConfigChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is MarkdownTextBlock self && e.NewValue != null) @@ -110,7 +114,7 @@ private void Build() { if (_renderer == null) { - _renderer = new WinUIRenderer(_document, Config); + _renderer = new WinUIRenderer(_document, Config, this); } _pipeline.Setup(_renderer); ApplyText(false); diff --git a/components/MarkdownTextBlock/src/MarkdownThemes.cs b/components/MarkdownTextBlock/src/MarkdownThemes.cs index 0b1aaf734..ada70c9b4 100644 --- a/components/MarkdownTextBlock/src/MarkdownThemes.cs +++ b/components/MarkdownTextBlock/src/MarkdownThemes.cs @@ -49,7 +49,15 @@ public sealed class MarkdownThemes : DependencyObject public FontWeight H6FontWeight { get; set; } = FontWeights.Normal; + public Thickness H1Margin { get; set; } = new(0); + public Thickness H2Margin { get; set; } = new(0); + public Thickness H3Margin { get; set; } = new(0); + public Thickness H4Margin { get; set; } = new(0); + public Thickness H5Margin { get; set; } = new(0); + public Thickness H6Margin { get; set; } = new(0); + public Brush InlineCodeBackground { get; set; } = (Brush)Application.Current.Resources["ExpanderHeaderBackground"]; + public Brush InlineCodeForeground { get; set; } = (Brush)Application.Current.Resources["TextFillColorPrimaryBrush"]; public Brush InlineCodeBorderBrush { get; set; } = new SolidColorBrush(Colors.Gray); diff --git a/components/MarkdownTextBlock/src/Renderers/ObjectRenderers/Inlines/AutoLinkInlineRenderer.cs b/components/MarkdownTextBlock/src/Renderers/ObjectRenderers/Inlines/AutoLinkInlineRenderer.cs index 4e9d01f06..5dddedc7e 100644 --- a/components/MarkdownTextBlock/src/Renderers/ObjectRenderers/Inlines/AutoLinkInlineRenderer.cs +++ b/components/MarkdownTextBlock/src/Renderers/ObjectRenderers/Inlines/AutoLinkInlineRenderer.cs @@ -26,6 +26,10 @@ protected override void Write(WinUIRenderer renderer, AutolinkInline link) } var autolink = new MyAutolinkInline(link); + autolink.ClickEvent += (sender, e) => + { + renderer.MarkdownTextBlock.RaiseLinkClickedEvent(sender.NavigateUri); + }; renderer.Push(autolink); diff --git a/components/MarkdownTextBlock/src/Renderers/ObjectRenderers/Inlines/LinkInlineRenderer.cs b/components/MarkdownTextBlock/src/Renderers/ObjectRenderers/Inlines/LinkInlineRenderer.cs index fa05d400a..5d78c4824 100644 --- a/components/MarkdownTextBlock/src/Renderers/ObjectRenderers/Inlines/LinkInlineRenderer.cs +++ b/components/MarkdownTextBlock/src/Renderers/ObjectRenderers/Inlines/LinkInlineRenderer.cs @@ -30,11 +30,20 @@ protected override void Write(WinUIRenderer renderer, LinkInline link) { if (link.FirstChild is LinkInline linkInlineChild && linkInlineChild.IsImage) { - renderer.Push(new MyHyperlinkButton(link, renderer.Config.BaseUrl)); + var myHyperlinkButton = new MyHyperlinkButton(link, renderer.Config.BaseUrl); + myHyperlinkButton.ClickEvent += (sender, e) => + { + renderer.MarkdownTextBlock.RaiseLinkClickedEvent(((HyperlinkButton)sender).NavigateUri); + }; + renderer.Push(myHyperlinkButton); } else { var hyperlink = new MyHyperlink(link, renderer.Config.BaseUrl); + hyperlink.ClickEvent += (sender, e) => + { + renderer.MarkdownTextBlock.RaiseLinkClickedEvent(sender.NavigateUri); + }; renderer.Push(hyperlink); } diff --git a/components/MarkdownTextBlock/src/Renderers/WinUIRenderer.cs b/components/MarkdownTextBlock/src/Renderers/WinUIRenderer.cs index af4d6cb01..a9f772423 100644 --- a/components/MarkdownTextBlock/src/Renderers/WinUIRenderer.cs +++ b/components/MarkdownTextBlock/src/Renderers/WinUIRenderer.cs @@ -23,11 +23,13 @@ public MarkdownConfig Config get => _config; set => _config = value; } + public MarkdownTextBlock MarkdownTextBlock { get; } - public WinUIRenderer(MyFlowDocument document, MarkdownConfig config) + public WinUIRenderer(MyFlowDocument document, MarkdownConfig config, MarkdownTextBlock markdownTextBlock) { _buffer = new char[1024]; Config = config; + MarkdownTextBlock = markdownTextBlock; FlowDocument = document; // set style _stack.Push(FlowDocument); diff --git a/components/MarkdownTextBlock/src/TextElements/MyAutolinkInline.cs b/components/MarkdownTextBlock/src/TextElements/MyAutolinkInline.cs index bdea36eae..465d3c54b 100644 --- a/components/MarkdownTextBlock/src/TextElements/MyAutolinkInline.cs +++ b/components/MarkdownTextBlock/src/TextElements/MyAutolinkInline.cs @@ -12,6 +12,18 @@ internal class MyAutolinkInline : IAddChild public TextElement TextElement { get; private set; } + public event TypedEventHandler? ClickEvent + { + add + { + ((Hyperlink)TextElement).Click += value; + } + remove + { + ((Hyperlink)TextElement).Click -= value; + } + } + public MyAutolinkInline(AutolinkInline autoLinkInline) { _autoLinkInline = autoLinkInline; @@ -21,7 +33,6 @@ public MyAutolinkInline(AutolinkInline autoLinkInline) }; } - public void AddChild(IAddChild child) { try diff --git a/components/MarkdownTextBlock/src/TextElements/MyHeading.cs b/components/MarkdownTextBlock/src/TextElements/MyHeading.cs index f06c62413..0c52a1be8 100644 --- a/components/MarkdownTextBlock/src/TextElements/MyHeading.cs +++ b/components/MarkdownTextBlock/src/TextElements/MyHeading.cs @@ -27,26 +27,7 @@ public MyHeading(HeadingBlock headingBlock, MarkdownConfig config) _paragraph = new Paragraph(); _config = config; - var level = headingBlock.Level; - _paragraph.FontSize = level switch - { - 1 => _config.Themes.H1FontSize, - 2 => _config.Themes.H2FontSize, - 3 => _config.Themes.H3FontSize, - 4 => _config.Themes.H4FontSize, - 5 => _config.Themes.H5FontSize, - _ => _config.Themes.H6FontSize, - }; - _paragraph.Foreground = _config.Themes.HeadingForeground; - _paragraph.FontWeight = level switch - { - 1 => _config.Themes.H1FontWeight, - 2 => _config.Themes.H2FontWeight, - 3 => _config.Themes.H3FontWeight, - 4 => _config.Themes.H4FontWeight, - 5 => _config.Themes.H5FontWeight, - _ => _config.Themes.H6FontWeight, - }; + SetHProperties(headingBlock.Level); } public MyHeading(HtmlNode htmlNode, MarkdownConfig config) @@ -65,7 +46,11 @@ public MyHeading(HtmlNode htmlNode, MarkdownConfig config) _ => TextAlignment.Left, }; - var level = int.Parse(htmlNode.Name.Substring(1)); + SetHProperties(int.Parse(htmlNode.Name.Substring(1))); + } + + private void SetHProperties(int level) + { _paragraph.FontSize = level switch { 1 => _config.Themes.H1FontSize, @@ -85,6 +70,15 @@ public MyHeading(HtmlNode htmlNode, MarkdownConfig config) 5 => _config.Themes.H5FontWeight, _ => _config.Themes.H6FontWeight, }; + _paragraph.Margin = level switch + { + 1 => _config.Themes.H1Margin, + 2 => _config.Themes.H2Margin, + 3 => _config.Themes.H3Margin, + 4 => _config.Themes.H4Margin, + 5 => _config.Themes.H5Margin, + _ => _config.Themes.H6Margin, + }; } public void AddChild(IAddChild child) diff --git a/components/MarkdownTextBlock/src/TextElements/MyHyperlink.cs b/components/MarkdownTextBlock/src/TextElements/MyHyperlink.cs index 6a36a8d94..49dfa6338 100644 --- a/components/MarkdownTextBlock/src/TextElements/MyHyperlink.cs +++ b/components/MarkdownTextBlock/src/TextElements/MyHyperlink.cs @@ -14,6 +14,18 @@ internal class MyHyperlink : IAddChild private HtmlNode? _htmlNode; private string? _baseUrl; + public event TypedEventHandler ClickEvent + { + add + { + _hyperlink.Click += value; + } + remove + { + _hyperlink.Click -= value; + } + } + public bool IsHtml => _htmlNode != null; public TextElement TextElement diff --git a/components/MarkdownTextBlock/src/TextElements/MyHyperlinkButton.cs b/components/MarkdownTextBlock/src/TextElements/MyHyperlinkButton.cs index 9d5297fea..deba711ce 100644 --- a/components/MarkdownTextBlock/src/TextElements/MyHyperlinkButton.cs +++ b/components/MarkdownTextBlock/src/TextElements/MyHyperlinkButton.cs @@ -9,12 +9,24 @@ namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock.TextElements; internal class MyHyperlinkButton : IAddChild { - private HyperlinkButton? _hyperLinkButton; + private HyperlinkButton _hyperLinkButton; private InlineUIContainer _inlineUIContainer = new InlineUIContainer(); - private MyFlowDocument? _flowDoc; + private MyFlowDocument _flowDoc; private string? _baseUrl; private LinkInline? _linkInline; private HtmlNode? _htmlNode; + + public event RoutedEventHandler? ClickEvent + { + add + { + _hyperLinkButton.Click += value; + } + remove + { + _hyperLinkButton.Click -= value; + } + } public bool IsHtml => _htmlNode != null; @@ -24,43 +36,40 @@ public TextElement TextElement } public MyHyperlinkButton(LinkInline linkInline, string? baseUrl) + : this(linkInline.GetDynamicUrl != null ? linkInline.GetDynamicUrl() ?? linkInline.Url : linkInline.Url, baseUrl, null, linkInline) { - _baseUrl = baseUrl; - var url = linkInline.GetDynamicUrl != null ? linkInline.GetDynamicUrl() ?? linkInline.Url : linkInline.Url; - _linkInline = linkInline; - Init(url, baseUrl); } public MyHyperlinkButton(HtmlNode htmlNode, string? baseUrl) + : this(htmlNode.GetAttribute("href", "#"), baseUrl, htmlNode, null) { - _baseUrl = baseUrl; - _htmlNode = htmlNode; - var url = htmlNode.GetAttribute("href", "#"); - Init(url, baseUrl); } - private void Init(string? url, string? baseUrl) + private MyHyperlinkButton(string? url, string? baseUrl, HtmlNode? htmlNode, LinkInline? linkInline) { - _hyperLinkButton = new HyperlinkButton() + _baseUrl = baseUrl; + _htmlNode = htmlNode; + _linkInline = linkInline; + _hyperLinkButton = new HyperlinkButton { NavigateUri = Extensions.GetUri(url, baseUrl), }; _hyperLinkButton.Padding = new Thickness(0); _hyperLinkButton.Margin = new Thickness(0); - if (IsHtml && _htmlNode != null) + if (_htmlNode != null) { _flowDoc = new MyFlowDocument(_htmlNode); } - else if (_linkInline != null) + else { - _flowDoc = new MyFlowDocument(_linkInline); + _flowDoc = new MyFlowDocument(_linkInline!); } _inlineUIContainer.Child = _hyperLinkButton; - _hyperLinkButton.Content = _flowDoc?.RichTextBlock; + _hyperLinkButton.Content = _flowDoc.RichTextBlock; } public void AddChild(IAddChild child) { - _flowDoc?.AddChild(child); + _flowDoc.AddChild(child); } } diff --git a/components/MarkdownTextBlock/src/TextElements/MyInlineCode.cs b/components/MarkdownTextBlock/src/TextElements/MyInlineCode.cs index 8e97d5963..30c3765d7 100644 --- a/components/MarkdownTextBlock/src/TextElements/MyInlineCode.cs +++ b/components/MarkdownTextBlock/src/TextElements/MyInlineCode.cs @@ -34,6 +34,7 @@ public MyInlineCode(CodeInline codeInline, MarkdownConfig config) border.Transform3D = transform; var textBlock = new TextBlock(); textBlock.FontSize = _config.Themes.InlineCodeFontSize; + textBlock.Foreground = _config.Themes.InlineCodeForeground; textBlock.FontWeight = _config.Themes.InlineCodeFontWeight; textBlock.Text = codeInline.Content.ToString(); border.Child = textBlock;