Merge branch 'main' into fix/app-services-contracts-version

This commit is contained in:
Arlo 2024-02-29 19:23:45 -06:00 коммит произвёл GitHub
Родитель fa5e141d00 bec0919c64
Коммит a437e69164
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
100 изменённых файлов: 4579 добавлений и 83 удалений

Просмотреть файл

@ -3,7 +3,7 @@
"isRoot": true, "isRoot": true,
"tools": { "tools": {
"uno.check": { "uno.check": {
"version": "1.18.1", "version": "1.20.2",
"commands": [ "commands": [
"uno-check" "uno-check"
] ]
@ -15,7 +15,7 @@
] ]
}, },
"microsoft.visualstudio.slngen.tool": { "microsoft.visualstudio.slngen.tool": {
"version": "9.5.4", "version": "11.2.3",
"commands": [ "commands": [
"slngen" "slngen"
] ]

Просмотреть файл

@ -1,7 +1,7 @@
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.208.0/containers/dotnet/.devcontainer/base.Dockerfile # See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.208.0/containers/dotnet/.devcontainer/base.Dockerfile
# [Choice] .NET version: 6.0, 5.0, 3.1, 6.0-bullseye, 5.0-bullseye, 3.1-bullseye, 6.0-focal, 5.0-focal, 3.1-focal # [Choice] .NET version: 6.0, 5.0, 3.1, 6.0-bullseye, 5.0-bullseye, 3.1-bullseye, 6.0-focal, 5.0-focal, 3.1-focal, etc
ARG VARIANT="6.0-bullseye-slim" ARG VARIANT="8.0-bullseye-slim"
FROM mcr.microsoft.com/vscode/devcontainers/dotnet:0-${VARIANT} FROM mcr.microsoft.com/vscode/devcontainers/dotnet:0-${VARIANT}
# [Choice] Node.js version: none, lts/*, 16, 14, 12, 10 # [Choice] Node.js version: none, lts/*, 16, 14, 12, 10

Просмотреть файл

@ -16,7 +16,7 @@
"settings": {}, "settings": {},
// Add the IDs of extensions you want installed when the container is created. // Add the IDs of extensions you want installed when the container is created.
"extensions": [ "extensions": [
"ms-dotnettools.csharp", "ms-dotnettools.csdevkit",
"unoplatform.vscode", "unoplatform.vscode",
"ms-vsliveshare.vsliveshare", "ms-vsliveshare.vsliveshare",
"ms-vscode.powershell" "ms-vscode.powershell"

8
.github/workflows/build.yml поставляемый
Просмотреть файл

@ -17,7 +17,7 @@ on:
merge_group: merge_group:
env: env:
DOTNET_VERSION: ${{ '7.0.402' }} DOTNET_VERSION: ${{ '8.0.201' }}
ENABLE_DIAGNOSTICS: false ENABLE_DIAGNOSTICS: false
#COREHOST_TRACE: 1 #COREHOST_TRACE: 1
MSBUILD_VERBOSITY: normal MSBUILD_VERBOSITY: normal
@ -106,7 +106,6 @@ jobs:
- name: Restore dotnet tools - name: Restore dotnet tools
run: dotnet tool restore run: dotnet tool restore
# Pinning Manifest for 1.18 version of Uno.Check at the moment to unblock build, see https://github.com/CommunityToolkit/Windows/pull/320
- name: Run Uno Check to Install Dependencies - name: Run Uno Check to Install Dependencies
run: > run: >
dotnet tool run uno-check dotnet tool run uno-check
@ -117,10 +116,11 @@ jobs:
--skip androidemulator --skip androidemulator
--skip vswinworkloads --skip vswinworkloads
--verbose --verbose
--manifest https://raw.githubusercontent.com/unoplatform/uno.check/1660eba219684491362704c75153b40ce6ef7a35/manifests/uno.ui.manifest.json
- name: Add msbuild to PATH - name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v1.3.1 uses: microsoft/setup-msbuild@v2
with:
vs-version: '[17.9,)'
- name: Enable ${{ env.TARGET_PLATFORMS }} TargetFrameworks - name: Enable ${{ env.TARGET_PLATFORMS }} TargetFrameworks
working-directory: ./${{ env.MULTI_TARGET_DIRECTORY }} working-directory: ./${{ env.MULTI_TARGET_DIRECTORY }}

2
.vscode/extensions.json поставляемый
Просмотреть файл

@ -1,5 +1,5 @@
{ {
"recommendations": [ "recommendations": [
"fernandoescolar.vscode-solution-explorer", "ms-dotnettools.csdevkit",
] ]
} }

4
.vscode/settings.json поставляемый
Просмотреть файл

@ -1,5 +1,4 @@
{ {
"omnisharp.defaultLaunchSolution": "CommunityToolkit.AllComponents.sln",
"omnisharp.disableMSBuildDiagnosticWarning": true, "omnisharp.disableMSBuildDiagnosticWarning": true,
"omnisharp.enableRoslynAnalyzers": true, "omnisharp.enableRoslynAnalyzers": true,
"omnisharp.useGlobalMono": "never", "omnisharp.useGlobalMono": "never",
@ -8,5 +7,6 @@
"csharp.suppressDotnetRestoreNotification": true, "csharp.suppressDotnetRestoreNotification": true,
"csharp.semanticHighlighting.enabled": true, "csharp.semanticHighlighting.enabled": true,
"omnisharp.enableMsBuildLoadProjectsOnDemand": true, "omnisharp.enableMsBuildLoadProjectsOnDemand": true,
"dotnet.completion.showCompletionItemsFromUnimportedNamespaces": true "dotnet.completion.showCompletionItemsFromUnimportedNamespaces": true,
"dotnet.defaultSolution": "CommunityToolkit.AllComponents.sln"
} }

Просмотреть файл

@ -1,47 +0,0 @@
{
"version": "1.0",
"components": [
"Microsoft.VisualStudio.Component.CoreEditor",
"Microsoft.VisualStudio.Workload.CoreEditor",
"Microsoft.Net.Component.4.8.SDK",
"Microsoft.Net.Component.4.7.2.TargetingPack",
"Microsoft.Net.ComponentGroup.DevelopmentPrerequisites",
"Microsoft.VisualStudio.Component.TypeScript.TSServer",
"Microsoft.VisualStudio.ComponentGroup.WebToolsExtensions",
"Microsoft.VisualStudio.Component.JavaScript.TypeScript",
"Microsoft.VisualStudio.Component.Roslyn.Compiler",
"Microsoft.Component.MSBuild",
"Microsoft.VisualStudio.Component.Roslyn.LanguageServices",
"Microsoft.VisualStudio.Component.TextTemplating",
"Microsoft.VisualStudio.Component.NuGet",
"Microsoft.VisualStudio.Component.SQL.CLR",
"Microsoft.Component.ClickOnce",
"Microsoft.VisualStudio.Component.ManagedDesktop.Core",
"Microsoft.NetCore.Component.Runtime.6.0",
"Microsoft.NetCore.Component.SDK",
"Microsoft.VisualStudio.Component.FSharp",
"Microsoft.ComponentGroup.ClickOnce.Publish",
"Microsoft.NetCore.Component.DevelopmentTools",
"Microsoft.VisualStudio.Component.AppInsights.Tools",
"Microsoft.Net.Component.4.8.TargetingPack",
"Microsoft.Net.ComponentGroup.4.8.DeveloperTools",
"Microsoft.VisualStudio.Component.DiagnosticTools",
"Microsoft.VisualStudio.Component.EntityFramework",
"Microsoft.VisualStudio.Component.Debugger.JustInTime",
"Component.Microsoft.VisualStudio.LiveShare.2022",
"Microsoft.VisualStudio.Component.IntelliCode",
"Microsoft.VisualStudio.Component.Windows10SDK.19041",
"Microsoft.VisualStudio.Component.ManagedDesktop.Prerequisites",
"Microsoft.ComponentGroup.Blend",
"Microsoft.VisualStudio.Component.DotNetModelBuilder",
"Microsoft.VisualStudio.ComponentGroup.MSIX.Packaging",
"Microsoft.VisualStudio.Workload.ManagedDesktop",
"Microsoft.Component.NetFX.Native",
"Microsoft.VisualStudio.ComponentGroup.UWP.NetCoreAndStandard",
"Microsoft.VisualStudio.Component.Graphics",
"Microsoft.VisualStudio.ComponentGroup.UWP.Xamarin",
"Microsoft.VisualStudio.ComponentGroup.UWP.Support",
"Microsoft.VisualStudio.Workload.Universal",
"Microsoft.NetCore.Component.Runtime.5.0"
]
}

Просмотреть файл

@ -1,4 +1,5 @@
<Project Sdk="MSBuild.Sdk.Extras/3.0.23"> <Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))" Condition="Exists('$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))')" />
<PropertyGroup> <PropertyGroup>
<ToolkitComponentName>AppServices</ToolkitComponentName> <ToolkitComponentName>AppServices</ToolkitComponentName>
<Description>This package contains AppServices, to easily communicate between UWP apps and Win32 extensions.</Description> <Description>This package contains AppServices, to easily communicate between UWP apps and Win32 extensions.</Description>

Просмотреть файл

@ -1,4 +1,5 @@
<Project Sdk="MSBuild.Sdk.Extras/3.0.23"> <Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))" Condition="Exists('$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))')" />
<PropertyGroup> <PropertyGroup>
<ToolkitComponentName>CanvasLayout</ToolkitComponentName> <ToolkitComponentName>CanvasLayout</ToolkitComponentName>
</PropertyGroup> </PropertyGroup>

Просмотреть файл

@ -1,4 +1,5 @@
<Project Sdk="MSBuild.Sdk.Extras/3.0.23"> <Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))" Condition="Exists('$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))')" />
<PropertyGroup> <PropertyGroup>
<ToolkitComponentName>CanvasLayout</ToolkitComponentName> <ToolkitComponentName>CanvasLayout</ToolkitComponentName>
<Description>This package contains a CanvasLayout Layout for ItemsRepeater.</Description> <Description>This package contains a CanvasLayout Layout for ItemsRepeater.</Description>

Просмотреть файл

@ -1,4 +1,5 @@
<Project Sdk="MSBuild.Sdk.Extras/3.0.23"> <Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))" Condition="Exists('$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))')" />
<PropertyGroup> <PropertyGroup>
<ToolkitComponentName>CanvasView</ToolkitComponentName> <ToolkitComponentName>CanvasView</ToolkitComponentName>
</PropertyGroup> </PropertyGroup>

Просмотреть файл

@ -1,4 +1,5 @@
<Project Sdk="MSBuild.Sdk.Extras/3.0.23"> <Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))" Condition="Exists('$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))')" />
<PropertyGroup> <PropertyGroup>
<ToolkitComponentName>CanvasView</ToolkitComponentName> <ToolkitComponentName>CanvasView</ToolkitComponentName>
<Description>This package contains CanvasView.</Description> <Description>This package contains CanvasView.</Description>

Просмотреть файл

@ -1,4 +1,5 @@
<Project Sdk="MSBuild.Sdk.Extras/3.0.23"> <Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))" Condition="Exists('$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))')" />
<PropertyGroup> <PropertyGroup>
<ToolkitComponentName>DataTable</ToolkitComponentName> <ToolkitComponentName>DataTable</ToolkitComponentName>
</PropertyGroup> </PropertyGroup>

Просмотреть файл

@ -1,4 +1,5 @@
<Project Sdk="MSBuild.Sdk.Extras/3.0.23"> <Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))" Condition="Exists('$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))')" />
<PropertyGroup> <PropertyGroup>
<ToolkitComponentName>DataTable</ToolkitComponentName> <ToolkitComponentName>DataTable</ToolkitComponentName>
<Description>This package contains DataTable.</Description> <Description>This package contains DataTable.</Description>

Просмотреть файл

@ -1,4 +1,5 @@
<Project Sdk="MSBuild.Sdk.Extras/3.0.23"> <Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))" Condition="Exists('$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))')" />
<PropertyGroup> <PropertyGroup>
<ToolkitComponentName>Extensions.DependencyInjection</ToolkitComponentName> <ToolkitComponentName>Extensions.DependencyInjection</ToolkitComponentName>
<Description>This package contains Extensions.DependencyInjection.</Description> <Description>This package contains Extensions.DependencyInjection.</Description>

Просмотреть файл

@ -0,0 +1,3 @@
@ECHO OFF
powershell ..\..\tooling\ProjectHeads\GenerateSingleSampleHeads.ps1 -componentPath %CD% %*

Двоичные данные
components/MarkdownTextBlock/samples/Assets/MarkdownTextBlock.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.2 KiB

Просмотреть файл

@ -0,0 +1,31 @@
<!--
WinUI 2 under UWP uses TargetFramework uap10.0.*
WinUI 3 under WinAppSdk uses TargetFramework net6.0-windows10.*
However, under Uno-powered platforms, both WinUI 2 and 3 can share the same TargetFramework.
MSBuild doesn't play nicely with this out of the box, so we've made it easy for you.
For .NET Standard packages, you can use the Nuget Package Manager in Visual Studio.
For UWP / WinAppSDK / Uno packages, place the package references here.
-->
<Project>
<!-- WinUI 2 / UWP -->
<ItemGroup Condition="'$(IsUwp)' == 'true'">
<!-- <PackageReference Include="Microsoft.Toolkit.Uwp.UI.Controls.Primitives" Version="7.1.2"/> -->
</ItemGroup>
<!-- WinUI 2 / Uno -->
<ItemGroup Condition="'$(IsUno)' == 'true' AND '$(WinUIMajorVersion)' == '2'">
<!-- <PackageReference Include="Uno.Microsoft.Toolkit.Uwp.UI.Controls.Primitives" Version="7.1.11"/> -->
</ItemGroup>
<!-- WinUI 3 / WinAppSdk -->
<ItemGroup Condition="'$(IsWinAppSdk)' == 'true'">
<!-- <PackageReference Include="CommunityToolkit.WinUI.UI.Controls.Primitives" Version="7.1.2"/> -->
</ItemGroup>
<!-- WinUI 3 / Uno -->
<ItemGroup Condition="'$(IsUno)' == 'true' AND '$(WinUIMajorVersion)' == '3'">
<!-- <PackageReference Include="Uno.CommunityToolkit.WinUI.UI.Controls.Primitives" Version="7.1.100-dev.15.g12261e2626"/> -->
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,17 @@
<Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))" Condition="Exists('$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))')" />
<PropertyGroup>
<ToolkitComponentName>MarkdownTextBlock</ToolkitComponentName>
</PropertyGroup>
<!-- Sets this up as a toolkit component's sample project -->
<Import Project="$(ToolingDirectory)\ToolkitComponent.SampleProject.props" />
<ItemGroup>
<None Remove="Assets\MarkdownTextBlock.png" />
</ItemGroup>
<ItemGroup>
<Content Include="Assets\MarkdownTextBlock.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,34 @@
---
title: MarkdownTextBlock
author: nerocui
description: A control for displaying markdown natively.
keywords: MarkdownTextBlock, Control, Layout
dev_langs:
- csharp
category: Controls
subcategory: StatusAndInfo
experimental: true
discussion-id: 0
issue-id: 0
icon: Assets/MarkdownTextBlock.png
---
<!-- To know about all the available Markdown syntax, Check out https://docs.microsoft.com/contribute/markdown-reference -->
<!-- Ensure you remove all comments before submission, to ensure that there are no formatting issues when displaying this page. -->
<!-- It is recommended to check how the Documentation will look in the sample app, before Merging a PR -->
<!-- **Note:** All links to other docs.microsoft.com pages should be relative without locale, i.e. for the one above would be /contribute/markdown-reference -->
<!-- Included images should be optimized for size and not include any Intellectual Property references. -->
<!-- Be sure to update the discussion/issue numbers above with your Labs discussion/issue id numbers in order for UI links to them from the sample app to work. -->
# MarkdownTextBlock
MarkdownTextBlock is a evolution of the existing MarkdownTextBlock in the community toolkit. This new implementation uses the popular [Markdig](https://github.com/xoofx/markdig) library for parsing. This solves some long standing bugs and feature gaps in our existing implementation.
## Templated Controls
The Toolkit is built with templated controls. This provides developers a flexible way to restyle components
easily while still inheriting the general functionality a control provides. The examples below show
how a component can use a default style and then get overridden by the end developer.
> [!Sample MarkdownTextBlockCustomSample]

Просмотреть файл

@ -0,0 +1,44 @@
<!-- 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. -->
<Page x:Class="MarkdownTextBlockExperiment.Samples.MarkdownTextBlockCustomSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:CommunityToolkit.Labs.WinUI.MarkdownTextBlock"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:MarkdownTextBlockExperiment.Samples"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0"
Margin="0,0,0,12"
FontSize="16"
FontWeight="Bold"
Text="Try it live!" />
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<controls:MarkdownTextBlock Grid.Column="0"
Config="{x:Bind LiveMarkdownConfig, Mode=OneTime}"
Text="{x:Bind MarkdownTextBox.Text, Mode=OneWay}" />
<TextBox x:Name="MarkdownTextBox"
Grid.Column="1"
AcceptsReturn="True" />
</Grid>
<TextBlock Grid.Row="2"
Margin="0,0,0,12"
FontSize="16"
FontWeight="Bold"
Text="Built-in Sample" />
<controls:MarkdownTextBlock Grid.Row="3"
Config="{x:Bind MarkdownConfig, Mode=OneTime}"
Text="{x:Bind Text, Mode=OneTime}" />
</Grid>
</Page>

Просмотреть файл

@ -0,0 +1,611 @@
// 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 CommunityToolkit.Labs.WinUI.MarkdownTextBlock;
namespace MarkdownTextBlockExperiment.Samples;
/// <summary>
/// An example sample page of a custom control inheriting from Panel.
/// </summary>
[ToolkitSample(id: nameof(MarkdownTextBlockCustomSample), "Custom control", description: $"A sample for showing how to create and use a {nameof(CommunityToolkit.Labs.WinUI.MarkdownTextBlock)} custom control.")]
public sealed partial class MarkdownTextBlockCustomSample : Page
{
private MarkdownConfig _config;
private MarkdownConfig _liveConfig;
private string _text;
private const string _markdown = @"
This control was originally written by [Nero Cui](https://github.com/nerocui) for [JitHub](https://github.com/JitHubApp/JitHubV2). The control is powered by the popular [Markdig](https://github.com/xoofx/markdig) markdown parsing library and *almost* supports the full markdown syntax, with a focus on super-efficient parsing and rendering.
*Note:* For a full list of markdown syntax, see the [official syntax guide](http://daringfireball.net/projects/markdown/syntax).
&nbsp;
**Try it live!** Type in the *unformatted text box*!
&nbsp;
# COMMENTS
Comments can be added in Markdown, and they won't be rendered to the screen.
To create a comment, enclose in XML style comment tags:
><\!-- Comments are now Implemented -->
There is a Comment below this line.
<!-- Comments are now Implemented -->
&nbsp;
# PARAGRAPHS
Paragraphs are delimited by a blank line. Simply starting text on a new line won't create a new paragraph; It will remain on the same line in the final, rendered version as the previous line. You need an extra, blank line to start a new paragraph. This is especially important when dealing with quotes and, to a lesser degree, lists.
You can also add non-paragraph line breaks by ending a line with two spaces. The difference is subtle:
Paragraph 1, Line 1
Paragraph 1, Line 2
Paragraph 2
*****
# FONT FORMATTING
### Italics
Text can be displayed in an italic font by surrounding a word or words with either single asterisks (\*) or single underscores (\_).
For example:
>This sentence includes \*italic text\*.
is displayed as:
>This sentence includes *italic text*.
### Bold
Text can be displayed in a bold font by surrounding a word or words with either double asterisks (\*) or double underscores (\_).
For example:
>This sentence includes \*\*bold text\*\*.
is displayed as:
>This sentence includes **bold text**.
### Bold & Italics
Text can be displayed in a bold font by surrounding a word or words with either triple asterisks (\*) or triple underscores (\_).
For example:
>This sentence includes \*\*\*bold & italic text\*\*\*.
is displayed as:
>This sentence includes ***bold & italic text***.
### Strikethrough
Text can be displayed in a strikethrough font by surrounding a word or words with double tildes (~~). For example:
>This sentence includes ~ ~strikethrough text~ ~
>*(but with no spaces between the tildes; escape sequences [see far below] appear not to work with tildes, so I can't demonstrate the exact usage).*
is displayed as:
>This sentence includes ~~strikethrough text~~.
### Superscript
Text can be displayed in a superscript font by preceding it with a caret ( ^ ).
>This sentence includes super^ script
>*(but with no spaces after the caret; Like strikethrough, the superscript syntax doesn't play nicely with escape sequences).*
is displayed as:
>This sentence includes super^script.
Superscripts can even be nested: just^like^this .
However, note that the superscript font will be reset by a space. To get around this, you can enclose the text in the superscript with parentheses. The parentheses won't be displayed in the comment, and everything inside of them will be superscripted, regardless of spaces:
>This sentence^ (has a superscript with multiple words)
>*Once again, with no space after the caret.*
is displayed as
>This sentence^(has a superscript with multiple words)
### Subscript
Text can be displayed in a subscript font by preceding it with a caret ( <sub></sub> ).
>This sentence includes \<sub>sub\</sub> script
>This sentence includes <sub>sub</sub>script.
### Headers
Markdown supports 6 levels of headers (some of which don't actually display as headers in reddit):
# Header 1
## Header 2
### Header 3
#### Header 4
##### Header 5
###### Header 6
...which can be created in a couple of different ways. Level 1 and 2 headers can be created by adding a line of equals signs (=) or dashes (\-), respectively, underneath the header text.
However, *all* types of headers can be created with a second method. Simply prepend a number of hashes (#) corresponding to the header level you want, so:
>\# Header 1
>\#\# Header 2
>\#\#\# Header 3
>\#\#\#\# Header 4
>\#\#\#\#\# Header 5
>\#\#\#\#\#\# Header 6
results in:
># Header 1
>## Header 2
>### Header 3
>#### Header 4
>##### Header 5
>###### Header 6
Note: you can add hashes after the header text to balance out how the source code looks without affecting what is displayed. So:
>\#\# Header 2 ##
also produces:
>## Header 2 ##
*****
# LISTS
Markdown supports two types of lists: ordered and unordered.
### Unordered Lists
Prepend each element in the list with either a plus (+), dash (-), or asterisk (*) plus a space. Line openers can be mixed. So
>\* Item 1
>\+ Item 2
>\- Item 3
results in
>* Item 1
>+ Item 2
>- Item 3
### Ordered Lists
Ordered lists work roughly the same way, but you prepend each item in the list with a number plus a period (.) plus a space. The number will increment by 1 starting from the number from the number of the first list item. So
>7\. Item 1
>2\. Item 2
>5\. Item 3
results in
>7. Item 1
>2. Item 2
>5. Item 3
Also, you can nest lists, like so:
1. Ordered list item 1
2. * Bullet 1 in list item 2
* Bullet 2 in list item 2
3. List item 3
Note: If your list items consist of multiple paragraphs, you can force each new paragraph to remain in the previous list item by indenting it by one tab or four spaces. So
>\* This item has multiple paragraphs.
>
>(*four spaces here*)This is the second paragraph
>
>\* Item 2
results in:
>* This item has multiple paragraphs.
>
> This is the second paragraph
>* Item 2
### Task List
You can also use the GitHub style task list. The syntax is the following:
- [ ] Task 1
- [x] Task 2
- [ ] Task 3
Notice how the spaces in my source were stripped out? What if you need to preserve formatting? That brings us to:
*****
# CODE BLOCKS AND INLINE CODE
Inline code is easy. Simply surround any text with backticks (\`), **not to be confused with apostrophes (')**. Anything between the backticks will be rendered in a fixed-width font, and none of the formatting syntax we're exploring will be applied. So
>Here is some `` ` ``inline code with \*\*formatting\*\*`` ` ``
is displayed as:
>Here is some `inline code with **formatting**`
Note that this is why you should use the normal apostrophe when typing out possessive nouns or contractions. Otherwise you may end up with something like:
>I couldn`t believe that he didn`t know that!
Sometimes you need to preserve indentation, too. In those cases, you can create a block code element by starting every line of your code with four spaces (followed by other spaces that will be preserved). You can get results like the following:
public void main(Strings argv[]){
System.out.println(""Hello world!"");
}
Starting with Windows Community Toolkit v1.4, you can also use GitHub code notification by creating a block surrounded by 3x\` (3 backticks). This can also be used with Language Identifiers on the entering backticks, such as:
\`\`\`csharp
public static void Main(string[] args)
{
Console.WriteLine(""Hello world!"");
}
\`\`\`
will produce:
```csharp
public static void Main(string[] args)
{
Console.WriteLine(""Hello world!"");
}
```
*****
# LINKS
There are a couple of ways to get HTML links. The easiest is to just paste a valid URL, which will be automatically parsed as a link. Like so:
>http://en.wikipedia.org
However, usually you'll want to have text that functions as a link. In that case, include the text inside of square brackets followed by the URL in parentheses. So:
>\[Wikipedia\]\(http\://en.wikipedia.org).
results in:
>[Wikipedia](http://en.wikipedia.org).
You can also provide tooltip text for links like so:
>\[Wikipedia\]\(http\://en.wikipedia.org ""tooltip text""\).
results in:
>[Wikipedia](http://en.wikipedia.org ""tooltip text"").
There are other methods of generating links that aren't appropriate for discussion-board style comments. See the [Markdown Syntax](http://daringfireball.net/projects/markdown/syntax#link) if you're interested in more info.
&nbsp;
Relative links are also supported
>\[Relative Link\]\(/Assets/Photos/Photos.json\)
results in:
>[Relative Link](/Assets/Photos/Photos.json)
&nbsp;
>\[Relative Link 2\]\(../Photos/Photos.json\)
results in:
>[Relative Link 2](../Photos/Photos.json)
**Note:** Relative Links has to be Manually Handled in `LinkClicked` Event.
Custom Scheme's can be added now using `SchemeList` Property. Scheme's should be separated by a comma( , )
*Example*:
If `SchemeList=""companyportal,randomscheme""` then markdown will render
`companyportal://mycompanyportal.com` to companyportal://mycompanyportal.com
and
`randomscheme://www.randomscheme.render` to randomscheme://www.randomscheme.render
*****
# Email Links
Emails can be used as Masked Links.
>\[Email\]\(email@email.com)
will be rendered to [Email](email@email.com)
*****
# IMAGES
To add an image, it is almost like a link. You just need to add a \! before.
So inline image syntax looks like this:
>\!\[Helpers Image](https\://raw.githubusercontent.com/CommunityToolkit/WindowsCommunityToolkit/main/Microsoft.Toolkit.Uwp.SampleApp/Assets/Helpers.png)
which renders in:
![Helpers Image](https://raw.githubusercontent.com/CommunityToolkit/WindowsCommunityToolkit/main/Microsoft.Toolkit.Uwp.SampleApp/Assets/Helpers.png)
Rendering Images is now supported through prefix. use property **UriPrefix**
&nbsp;
Example: if you set **UriPrefix** to **ms-appx://** then
>\!\[Local Image](/Assets/NotificationAssets/Sunny-Square.png)
&nbsp;
renders in
![Local Image](/Assets/NotificationAssets/Sunny-Square.png)
You can also specify image width like this:
>\!\[SVG logo](https\://upload.wikimedia.org/wikipedia/commons/0/02/SVG_logo.svg =32) (width is set to 32)
>\!\[SVG logo](https\://upload.wikimedia.org/wikipedia/commons/0/02/SVG_logo.svg =x64) (height is set to 64)
>\!\[SVG logo](https\://upload.wikimedia.org/wikipedia/commons/0/02/SVG_logo.svg =128x64) (width=128, height=64)
which renders in:
![SVG logo](https://upload.wikimedia.org/wikipedia/commons/0/02/SVG_logo.svg =32)
![SVG logo](https://upload.wikimedia.org/wikipedia/commons/0/02/SVG_logo.svg =x64)
![SVG logo](https://upload.wikimedia.org/wikipedia/commons/0/02/SVG_logo.svg =128x64)
MarkdownTextblock supports links wrapped with Images.
>\[!\[image](https\://raw.githubusercontent.com/CommunityToolkit/WindowsCommunityToolkit/main/build/nuget.png)](https\://docs.microsoft.com/windows/uwpcommunitytoolkit/)
will render into
[![image](https://raw.githubusercontent.com/CommunityToolkit/WindowsCommunityToolkit/main/build/nuget.png)](https://docs.microsoft.com/windows/uwpcommunitytoolkit/)
and when clicked will go to the Linked Page.
MarkdownTextBlock also supports Reference based links.
```
[![image][1]][2]
[1]:https://raw.githubusercontent.com/CommunityToolkit/WindowsCommunityToolkit/main/build/nuget.png
[2]:https://docs.microsoft.com/windows/uwpcommunitytoolkit/
```
will render into
[![image][1]][2]
[1]:https://raw.githubusercontent.com/CommunityToolkit/WindowsCommunityToolkit/main/build/nuget.png
[2]:https://docs.microsoft.com/windows/uwpcommunitytoolkit/
*****
# BLOCK QUOTES
You'll probably do a lot of quoting of other redditors. In those cases, you'll want to use block quotes. Simple begin each line you want quoted with a right angle bracket (>). Multiple angle brackets can be used for nested quotes. To cause a new paragraph to be quoted, begin that paragraph with another angle bracket. So the following:
>Quote1
>Quote2.1
>>Quote2.Nest1.1
>>
>>Quote2.Nest1.2
>
>Quote2.3
>Quote3.1
>Quote3.2
>Quote4.1
>
>Quote4.2
>Quote5.1
Quote5.2
>Quote6
Plain text.
Is displayed as:
>Quote1
>Quote2.1
>>Quote2.Nest1.1
>>
>>Quote2.Nest1.2
>
>Quote2.3
>Quote3.1
>Quote3.2
>Quote4.1
>
>Quote4.2
>Quote5.1
Quote5.2
>Quote6
Plain text.
*****
# EMOJIS
You can use nearly all emojis from this [list](https://gist.github.com/rxaviers/7360908). Text like `:smile:` will display :smile: emoji.
*****
# MISCELLANEOUS
### Yaml Headers
The parsing of YAML metadata is rendered as a form. For example:
title|date
:-:|:-:
Windows Community Toolkit|2018/10/17
Which is produced with the following markdown:
>`---`
>`title: Windows Community Toolkit`
>`date: 2018/10/17`
>`---`
When you use YAML, you should pay attention to:
* Must be written at the beginning of the document.
* The start and end are represented by three short horizontal lines respectively.
* The format should conform to the YAML specification.
### Tables
Reddit has the ability to represent tabular data in fancy-looking tables. For example:
some|header|labels
:---|:--:|---:
Left-justified|center-justified|right-justified
a|b|c
d|e|f
Which is produced with the following markdown:
>`some|header|labels`
>`:---|:--:|---:`
>`Left-justified|center-justified|right-justified`
>`a|b|c`
>`d|e|f`
All you need to produce a table is a row of headers separated by ""pipes"" (**|**), a row indicating how to justify the columns, and 1 or more rows of data (again, pipe-separated).
The only real ""magic"" is in the row between the headers and the data. It should ideally be formed with rows dashes separated by pipes. If you add a colon to the left of the dashes for a column, that column will be left-justified. To the right for right justification, and on both sides for centered data. If there's no colon, it defaults to left-justified.
Any number of dashes will do, even just one. You can use none at all if you want it to default to left-justified, but it's just easier to see what you're doing if you put a few in there.
Also note that the pipes (signifying the dividing line between cells) don't have to line up. You just need the same number of them in every row.
### Escaping special characters
If you need to display any of the special characters, you can escape that character with a backslash (\\). For example:
>Escaped \\\*italics\\\*
results in:
>Escaped \*italics\*
###Horizontal rules
Finally, to create a horizontal rule, create a separate paragraph with 5 or more asterisks (\*).
>\*\*\*\*\*
results in
>*****
Source: https://www.reddit.com/r/reddit.com/comments/6ewgt/reddit_markdown_primer_or_how_do_you_do_all_that/c03nik6
";
public MarkdownConfig MarkdownConfig
{
get => _config;
set => _config = value;
}
public MarkdownConfig LiveMarkdownConfig
{
get => _liveConfig;
set => _liveConfig = value;
}
public string Text
{
get => _text;
set => _text = value;
}
public MarkdownTextBlockCustomSample()
{
this.InitializeComponent();
_config = new MarkdownConfig();
_liveConfig = new MarkdownConfig();
_text = _markdown;
MarkdownTextBox.Text = "# Hello World\n\n";
}
}

Просмотреть файл

@ -0,0 +1,48 @@
<Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))" Condition="Exists('$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))')" />
<PropertyGroup>
<ToolkitComponentName>MarkdownTextBlock</ToolkitComponentName>
<Description>This package contains MarkdownTextBlock.</Description>
<!-- Rns suffix is required for namespaces shared across projects. See https://github.com/CommunityToolkit/Labs-Windows/issues/152 -->
<RootNamespace>CommunityToolkit.WinUI.Controls.MarkdownTextBlockRns</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<!-- Sets this up as a toolkit component's source project -->
<Import Project="$(ToolingDirectory)\ToolkitComponent.SourceProject.props" />
<PropertyGroup>
<PackageId>$(PackageIdPrefix).$(PackageIdVariant).Controls.$(ToolkitComponentName)</PackageId>
</PropertyGroup>
<ItemGroup>
<UpToDateCheckInput Remove="MarkdownTextBlock.xaml" />
</ItemGroup>
<ItemGroup>
<!--<PackageReference Include="ColorCode.Core" Version="2.0.14" />-->
<PackageReference Include="HtmlAgilityPack" Version="1.11.48" />
<PackageReference Include="Markdig" Version="0.31.0" />
<PackageReference Include="Roman-Numerals" Version="2.0.0" />
</ItemGroup>
<ItemGroup Condition="'$(IsUwp)' == 'true'">
<Using Include="Windows.UI" />
<Using Include="Windows.UI.Xaml.Documents" />
<Using Include="Windows.UI.Xaml.Media.Imaging" />
<Using Include="Windows.UI.Xaml.Media.Media3D" />
<Using Include="Windows.UI.Xaml.Shapes" />
<Using Include="Windows.UI.Text" />
</ItemGroup>
<ItemGroup Condition="'$(IsWinAppSdk)' == 'true'">
<Using Include="Microsoft.UI" />
<Using Include="Microsoft.UI.Xaml.Documents" />
<Using Include="Microsoft.UI.Xaml.Media.Imaging" />
<Using Include="Microsoft.UI.Xaml.Media.Media3D" />
<Using Include="Microsoft.UI.Xaml.Shapes" />
<Using Include="Microsoft.UI.Text" />
</ItemGroup>
<ItemGroup>
<Compile Update="MarkdownTextBlock.xaml.cs">
<DependentUpon>MarkdownTextBlock.xaml</DependentUpon>
</Compile>
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,40 @@
// 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;
internal class DefaultSVGRenderer : ISVGRenderer
{
public async Task<Image> SvgToImage(string svgString)
{
SvgImageSource svgImageSource = new SvgImageSource();
var image = new Image();
// Create a MemoryStream object and write the SVG string to it
using (var memoryStream = new MemoryStream())
using (var streamWriter = new StreamWriter(memoryStream))
{
await streamWriter.WriteAsync(svgString);
await streamWriter.FlushAsync();
// Rewind the MemoryStream
memoryStream.Position = 0;
// Load the SVG from the MemoryStream
await svgImageSource.SetSourceAsync(memoryStream.AsRandomAccessStream());
}
// Set the Source property of the Image control to the SvgImageSource object
image.Source = svgImageSource;
var size = Extensions.GetSvgSize(svgString);
if (size.Width != 0)
{
image.Width = size.Width;
}
if (size.Height != 0)
{
image.Height = size.Height;
}
return image;
}
}

Просмотреть файл

@ -0,0 +1,35 @@
<!--
WinUI 2 under UWP uses TargetFramework uap10.0.*
WinUI 3 under WinAppSdk uses TargetFramework net6.0-windows10.*
However, under Uno-powered platforms, both WinUI 2 and 3 can share the same TargetFramework.
MSBuild doesn't play nicely with this out of the box, so we've made it easy for you.
For .NET Standard packages, you can use the Nuget Package Manager in Visual Studio.
For UWP / WinAppSDK / Uno packages, place the package references here.
-->
<Project>
<!-- WinUI 2 / UWP -->
<ItemGroup Condition="'$(IsUwp)' == 'true'">
<!--<PackageReference Include="ColorCode.UWP" Version="2.0.14" />-->
<!-- <PackageReference Include="Microsoft.Toolkit.Uwp.UI.Controls.Primitives" Version="7.1.2"/> -->
</ItemGroup>
<!-- WinUI 2 / Uno -->
<ItemGroup Condition="'$(IsUno)' == 'true' AND '$(WinUIMajorVersion)' == '2'">
<!--<PackageReference Include="Uno.ColorCode.UWP" Version="2.1.0-uno.36" />-->
<!-- <PackageReference Include="Uno.Microsoft.Toolkit.Uwp.UI.Controls.Primitives" Version="7.1.11"/> -->
</ItemGroup>
<!-- WinUI 3 / WinAppSdk -->
<ItemGroup Condition="'$(IsWinAppSdk)' == 'true'">
<!--<PackageReference Include="ColorCode.WinUI" Version="2.0.14" />-->
<!-- <PackageReference Include="CommunityToolkit.WinUI.UI.Controls.Primitives" Version="7.1.2"/> -->
</ItemGroup>
<!-- WinUI 3 / Uno -->
<ItemGroup Condition="'$(IsUno)' == 'true' AND '$(WinUIMajorVersion)' == '3'">
<!--<PackageReference Include="Uno.ColorCode.WinUI" Version="2.1.0-uno.36" />-->
<!-- <PackageReference Include="Uno.CommunityToolkit.WinUI.UI.Controls.Primitives" Version="7.1.100-dev.15.g12261e2626"/> -->
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,725 @@
// 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 ColorCode;
//using ColorCode.Common;
//using ColorCode.Styling;
using Markdig.Syntax;
using Markdig.Syntax.Inlines;
using CommunityToolkit.Labs.WinUI.MarkdownTextBlock.TextElements;
using System.Xml.Linq;
using System.Globalization;
using Windows.UI.ViewManagement;
namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock;
public static class Extensions
{
private const string OneDarkBackground = "#282c34";
private const string OneDarkPlainText = "#abb2bf";
private const string OneDarkXMLDelimiter = "#e06c75";
private const string OneDarkXMLName = "#d19a66";
private const string OneDarkXMLAttribute = "#61afef";
private const string OneDarkXAMLCData = "#98c379";
private const string OneDarkXMLComment = "#5c6370";
private const string OneDarkComment = "#5c6370";
private const string OneDarkKeyword = "#c678dd";
private const string OneDarkGray = "#9b9b9b";
private const string OneDarkNumber = "#d19a66";
private const string OneDarkClass = "#e5c07b";
private const string OneDarkString = "#98c379";
public const string Blue = "#FF0000FF";
public const string White = "#FFFFFFFF";
public const string Black = "#FF000000";
public const string DullRed = "#FFA31515";
public const string Yellow = "#FFFFFF00";
public const string Green = "#FF008000";
public const string PowderBlue = "#FFB0E0E6";
public const string Teal = "#FF008080";
public const string Gray = "#FF808080";
public const string Navy = "#FF000080";
public const string OrangeRed = "#FFFF4500";
public const string Purple = "#FF800080";
public const string Red = "#FFFF0000";
public const string MediumTurqoise = "FF48D1CC";
public const string Magenta = "FFFF00FF";
public const string OliveDrab = "#FF6B8E23";
public const string DarkOliveGreen = "#FF556B2F";
public const string DarkCyan = "#FF008B8B";
//public static ILanguage ToLanguage(this FencedCodeBlock fencedCodeBlock)
//{
// switch (fencedCodeBlock.Info?.ToLower())
// {
// case "aspx":
// return Languages.Aspx;
// case "aspx - vb":
// return Languages.AspxVb;
// case "asax":
// return Languages.Asax;
// case "ascx":
// return Languages.AspxCs;
// case "ashx":
// case "asmx":
// case "axd":
// return Languages.Ashx;
// case "cs":
// case "csharp":
// case "c#":
// return Languages.CSharp;
// case "xhtml":
// case "html":
// case "hta":
// case "htm":
// case "html.hl":
// case "inc":
// case "xht":
// return Languages.Html;
// case "java":
// case "jav":
// case "jsh":
// return Languages.Java;
// case "js":
// case "node":
// case "_js":
// case "bones":
// case "cjs":
// case "es":
// case "es6":
// case "frag":
// case "gs":
// case "jake":
// case "javascript":
// case "jsb":
// case "jscad":
// case "jsfl":
// case "jslib":
// case "jsm":
// case "jspre":
// case "jss":
// case "jsx":
// case "mjs":
// case "njs":
// case "pac":
// case "sjs":
// case "ssjs":
// case "xsjs":
// case "xsjslib":
// return Languages.JavaScript;
// case "posh":
// case "pwsh":
// case "ps1":
// case "psd1":
// case "psm1":
// return Languages.PowerShell;
// case "sql":
// case "cql":
// case "ddl":
// case "mysql":
// case "prc":
// case "tab":
// case "udf":
// case "viw":
// return Languages.Sql;
// case "vb":
// case "vbhtml":
// case "visual basic":
// case "vbnet":
// case "vb .net":
// case "vb.net":
// return Languages.VbDotNet;
// case "rss":
// case "xsd":
// case "wsdl":
// case "xml":
// case "adml":
// case "admx":
// case "ant":
// case "axaml":
// case "axml":
// case "builds":
// case "ccproj":
// case "ccxml":
// case "clixml":
// case "cproject":
// case "cscfg":
// case "csdef":
// case "csl":
// case "csproj":
// case "ct":
// case "depproj":
// case "dita":
// case "ditamap":
// case "ditaval":
// case "dll.config":
// case "dotsettings":
// case "filters":
// case "fsproj":
// case "fxml":
// case "glade":
// case "gml":
// case "gmx":
// case "grxml":
// case "gst":
// case "hzp":
// case "iml":
// case "ivy":
// case "jelly":
// case "jsproj":
// case "kml":
// case "launch":
// case "mdpolicy":
// case "mjml":
// case "mm":
// case "mod":
// case "mxml":
// case "natvis":
// case "ncl":
// case "ndproj":
// case "nproj":
// case "nuspec":
// case "odd":
// case "osm":
// case "pkgproj":
// case "pluginspec":
// case "proj":
// case "props":
// case "ps1xml":
// case "psc1":
// case "pt":
// case "qhelp":
// case "rdf":
// case "res":
// case "resx":
// case "rs":
// case "sch":
// case "scxml":
// case "sfproj":
// case "shproj":
// case "srdf":
// case "storyboard":
// case "sublime-snippet":
// case "sw":
// case "targets":
// case "tml":
// case "ui":
// case "urdf":
// case "ux":
// case "vbproj":
// case "vcxproj":
// case "vsixmanifest":
// case "vssettings":
// case "vstemplate":
// case "vxml":
// case "wixproj":
// case "workflow":
// case "wsf":
// case "wxi":
// case "wxl":
// case "wxs":
// case "x3d":
// case "xacro":
// case "xaml":
// case "xib":
// case "xlf":
// case "xliff":
// case "xmi":
// case "xml.dist":
// case "xmp":
// case "xproj":
// case "xspec":
// case "xul":
// case "zcml":
// return Languages.Xml;
// case "php":
// case "aw":
// case "ctp":
// case "fcgi":
// case "php3":
// case "php4":
// case "php5":
// case "phps":
// case "phpt":
// return Languages.Php;
// case "css":
// case "scss":
// case "less":
// return Languages.Css;
// case "cpp":
// case "c++":
// case "cc":
// case "cp":
// case "cxx":
// case "h":
// case "h++":
// case "hh":
// case "hpp":
// case "hxx":
// case "inl":
// case "ino":
// case "ipp":
// case "ixx":
// case "re":
// case "tcc":
// case "tpp":
// return Languages.Cpp;
// case "ts":
// case "tsx":
// case "cts":
// case "mts":
// return Languages.Typescript;
// case "fsharp":
// case "fs":
// case "fsi":
// case "fsx":
// return Languages.FSharp;
// case "koka":
// return Languages.Koka;
// case "hs":
// case "hs-boot":
// case "hsc":
// return Languages.Haskell;
// case "pandoc":
// case "md":
// case "livemd":
// case "markdown":
// case "mdown":
// case "mdwn":
// case "mdx":
// case "mkd":
// case "mkdn":
// case "mkdown":
// case "ronn":
// case "scd":
// case "workbook":
// return Languages.Markdown;
// case "fortran":
// case "f":
// case "f77":
// case "for":
// case "fpp":
// return Languages.Fortran;
// case "python":
// case "py":
// case "cgi":
// case "gyp":
// case "gypi":
// case "lmi":
// case "py3":
// case "pyde":
// case "pyi":
// case "pyp":
// case "pyt":
// case "pyw":
// case "rpy":
// case "smk":
// case "spec":
// case "tac":
// case "wsgi":
// case "xpy":
// return Languages.Python;
// case "matlab":
// case "m":
// return Languages.MATLAB;
// default:
// return Languages.JavaScript;
// }
//}
public static string ToAlphabetical(this int index)
{
var alphabetical = "abcdefghijklmnopqrstuvwxyz";
var remainder = index;
var stringBuilder = new StringBuilder();
while (remainder != 0)
{
if (remainder > 26)
{
var newRemainder = remainder % 26;
var i = (remainder - newRemainder) / 26;
stringBuilder.Append(alphabetical[i - 1]);
remainder = newRemainder;
}
else
{
stringBuilder.Append(alphabetical[remainder - 1]);
remainder = 0;
}
}
return stringBuilder.ToString();
}
public static TextPointer? GetNextInsertionPosition(this TextPointer position, LogicalDirection logicalDirection)
{
// Check if the current position is already an insertion position
if (position.IsAtInsertionPosition(logicalDirection))
{
// Return the same position
return position;
}
else
{
// Try to find the next insertion position by moving one symbol forward
TextPointer next = position.GetPositionAtOffset(1, logicalDirection);
// If there is no next position, return null
if (next == null)
{
return null;
}
else
{
// Recursively call this method until an insertion position is found or null is returned
return GetNextInsertionPosition(next, logicalDirection);
}
}
}
public static bool IsAtInsertionPosition(this TextPointer position, LogicalDirection logicalDirection)
{
// Get the character rect of the current position
Rect currentRect = position.GetCharacterRect(logicalDirection);
// Try to get the next position by moving one symbol forward
TextPointer next = position.GetPositionAtOffset(1, logicalDirection);
// If there is no next position, return false
if (next == null)
{
return false;
}
else
{
// Get the character rect of the next position
Rect nextRect = next.GetCharacterRect(logicalDirection);
// Compare the two rects and return true if they are different
return !currentRect.Equals(nextRect);
}
}
public static string RemoveImageSize(string? url)
{
if (string.IsNullOrEmpty(url))
{
throw new ArgumentException("URL must not be null or empty", nameof(url));
}
// Create a regex pattern to match the URL with width and height
var pattern = @"([^)\s]+)\s*=\s*\d+x\d+\s*";
// Replace the matched URL with the URL only
var result = Regex.Replace(url, pattern, "$1");
return result;
}
public static Uri GetUri(string? url, string? @base)
{
var validUrl = RemoveImageSize(url);
Uri result;
#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
if (Uri.TryCreate(validUrl, UriKind.Absolute, out result))
{
//the url is already absolute
return result;
}
else if (!string.IsNullOrWhiteSpace(@base))
{
//the url is relative, so append the base
//trim any trailing "/" from the base and any leading "/" from the url
@base = @base?.TrimEnd('/');
validUrl = validUrl.TrimStart('/');
//return the base and the url separated by a single "/"
return new Uri(@base + "/" + validUrl);
}
else
{
//the url is relative to the file system
//add ms-appx
validUrl = validUrl.TrimStart('/');
return new Uri("ms-appx:///" + validUrl);
}
#pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type.
}
//public static StyleDictionary GetOneDarkProStyle()
//{
// return new StyleDictionary
// {
// new ColorCode.Styling.Style(ScopeName.PlainText)
// {
// Foreground = OneDarkPlainText,
// Background = OneDarkBackground,
// ReferenceName = "plainText"
// },
// new ColorCode.Styling.Style(ScopeName.HtmlServerSideScript)
// {
// Background = Yellow,
// ReferenceName = "htmlServerSideScript"
// },
// new ColorCode.Styling.Style(ScopeName.HtmlComment)
// {
// Foreground = OneDarkComment,
// ReferenceName = "htmlComment"
// },
// new ColorCode.Styling.Style(ScopeName.HtmlTagDelimiter)
// {
// Foreground = OneDarkKeyword,
// ReferenceName = "htmlTagDelimiter"
// },
// new ColorCode.Styling.Style(ScopeName.HtmlElementName)
// {
// Foreground = DullRed,
// ReferenceName = "htmlElementName"
// },
// new ColorCode.Styling.Style(ScopeName.HtmlAttributeName)
// {
// Foreground = Red,
// ReferenceName = "htmlAttributeName"
// },
// new ColorCode.Styling.Style(ScopeName.HtmlAttributeValue)
// {
// Foreground = OneDarkKeyword,
// ReferenceName = "htmlAttributeValue"
// },
// new ColorCode.Styling.Style(ScopeName.HtmlOperator)
// {
// Foreground = OneDarkKeyword,
// ReferenceName = "htmlOperator"
// },
// new ColorCode.Styling.Style(ScopeName.Comment)
// {
// Foreground = OneDarkComment,
// ReferenceName = "comment"
// },
// new ColorCode.Styling.Style(ScopeName.XmlDocTag)
// {
// Foreground = OneDarkXMLComment,
// ReferenceName = "xmlDocTag"
// },
// new ColorCode.Styling.Style(ScopeName.XmlDocComment)
// {
// Foreground = OneDarkXMLComment,
// ReferenceName = "xmlDocComment"
// },
// new ColorCode.Styling.Style(ScopeName.String)
// {
// Foreground = OneDarkString,
// ReferenceName = "string"
// },
// new ColorCode.Styling.Style(ScopeName.StringCSharpVerbatim)
// {
// Foreground = OneDarkString,
// ReferenceName = "stringCSharpVerbatim"
// },
// new ColorCode.Styling.Style(ScopeName.Keyword)
// {
// Foreground = OneDarkKeyword,
// ReferenceName = "keyword"
// },
// new ColorCode.Styling.Style(ScopeName.PreprocessorKeyword)
// {
// Foreground = OneDarkKeyword,
// ReferenceName = "preprocessorKeyword"
// },
// new ColorCode.Styling.Style(ScopeName.Number)
// {
// Foreground=OneDarkNumber,
// ReferenceName="number"
// },
// new ColorCode.Styling.Style(ScopeName.CssPropertyName)
// {
// Foreground=OneDarkClass,
// ReferenceName="cssPropertyName"
// },
// new ColorCode.Styling.Style(ScopeName.CssPropertyValue)
// {
// Foreground=OneDarkString,
// ReferenceName="cssPropertyValue"
// },
// new ColorCode.Styling.Style(ScopeName.CssSelector)
// {
// Foreground=OneDarkKeyword,
// ReferenceName="cssSelector"
// },
// new ColorCode.Styling.Style(ScopeName.SqlSystemFunction)
// {
// Foreground=OneDarkClass,
// ReferenceName="sqlSystemFunction"
// },
// new ColorCode.Styling.Style(ScopeName.XmlAttribute)
// {
// Foreground=OneDarkXMLAttribute,
// ReferenceName="xmlAttribute"
// },
// new ColorCode.Styling.Style(ScopeName.XmlAttributeQuotes)
// {
// Foreground=OneDarkXMLDelimiter,
// ReferenceName="xmlAttributeQuotes"
// },
// new ColorCode.Styling.Style(ScopeName.XmlAttributeValue)
// {
// Foreground=OneDarkString,
// ReferenceName="xmlAttributeValue"
// },
// new ColorCode.Styling.Style(ScopeName.XmlCDataSection)
// {
// Foreground=OneDarkXAMLCData,
// ReferenceName="xmlCDataSection"
// },
// new ColorCode.Styling.Style(ScopeName.XmlComment)
// {
// Foreground=OneDarkXMLComment,
// ReferenceName="xmlComment"
// },
// new ColorCode.Styling.Style(ScopeName.XmlDelimiter)
// {
// Foreground=OneDarkXMLDelimiter,
// ReferenceName="xmlDelimiter"
// },
// new ColorCode.Styling.Style(ScopeName.XmlName)
// {
// Foreground=OneDarkXMLName,
// ReferenceName="xmlName"
// }
// };
//}
public static HtmlElementType TagToType(this string tag)
{
switch (tag.ToLower())
{
case "address":
case "article":
case "aside":
case "details":
case "blockquote":
case "canvas":
case "dd":
case "div":
case "dl":
case "dt":
case "fieldset":
case "figcaption":
case "figure":
case "footer":
case "form":
case "h1":
case "h2":
case "h3":
case "h4":
case "h5":
case "h6":
case "header":
case "hr":
case "li":
case "main":
case "nav":
case "noscript":
case "ol":
case "p":
case "pre":
case "section":
case "table":
case "tfoot":
case "ul": return HtmlElementType.Block;
default: return HtmlElementType.Inline;
}
}
public static bool IsHeading(this string tag)
{
var headings = new List<string>() { "h1", "h2", "h3", "h4", "h5", "h6" };
return headings.Contains(tag.ToLower());
}
public static Size GetSvgSize(string svgString)
{
// Parse the SVG string as an XML document
XDocument svgDocument = XDocument.Parse(svgString);
// Get the root element of the document
#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
XElement svgElement = svgDocument.Root;
// Get the height and width attributes of the root element
#pragma warning disable CS8602 // Dereference of a possibly null reference.
XAttribute heightAttribute = svgElement.Attribute("height");
#pragma warning restore CS8602 // Dereference of a possibly null reference.
XAttribute widthAttribute = svgElement.Attribute("width");
#pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type.
// Convert the attribute values to double
double.TryParse(heightAttribute?.Value, NumberStyles.Number, CultureInfo.InvariantCulture, out double height);
double.TryParse(widthAttribute?.Value, NumberStyles.Number, CultureInfo.InvariantCulture, out double width);
// Return the height and width as a tuple
return new(width, height);
}
public static Size GetMarkdownImageSize(LinkInline link)
{
if (link == null || !link.IsImage)
{
throw new ArgumentException("Link must be an image", nameof(link));
}
var url = link.Url;
if (string.IsNullOrEmpty(url))
{
throw new ArgumentException("Link must have a valid URL", nameof(link));
}
// Try to parse the width and height from the URL
var parts = url?.Split('=');
if (parts?.Length == 2)
{
var dimensions = parts[1].Split('x');
if (dimensions.Length == 2 && int.TryParse(dimensions[0], out int width) && int.TryParse(dimensions[1], out int height))
{
return new(width, height);
}
}
// not using this one as it's seems to be from the HTML renderer
//// Try to parse the width and height from the special attributes
//var attributes = link.GetAttributes();
//if (attributes != null && attributes.Properties != null)
//{
// var width = attributes.Properties.FirstOrDefault(p => p.Key == "width")?.Value;
// var height = attributes.Properties.FirstOrDefault(p => p.Key == "height")?.Value;
// if (!string.IsNullOrEmpty(width) && !string.IsNullOrEmpty(height) && int.TryParse(width, out int w) && int.TryParse(height, out int h))
// {
// return new(w, h);
// }
//}
// Return default values if no width and height are found
return new(0, 0);
}
public static SolidColorBrush GetAccentColorBrush()
{
// Create a UISettings object to get the accent color
var uiSettings = new UISettings();
// Get the accent color as a Color value
var accentColor = uiSettings.GetColorValue(UIColorType.Accent);
// Create a SolidColorBrush from the accent color
var accentBrush = new SolidColorBrush(accentColor);
return accentBrush;
}
}

Просмотреть файл

@ -0,0 +1,86 @@
// 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 HtmlAgilityPack;
using CommunityToolkit.Labs.WinUI.MarkdownTextBlock.Renderers;
using CommunityToolkit.Labs.WinUI.MarkdownTextBlock.TextElements.Html;
using CommunityToolkit.Labs.WinUI.MarkdownTextBlock.TextElements;
namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock;
internal class HtmlWriter
{
public static void WriteHtml(WinUIRenderer renderer, HtmlNodeCollection nodes)
{
if (nodes == null || nodes.Count == 0) return;
foreach (var node in nodes)
{
if (node.NodeType == HtmlNodeType.Text)
{
renderer.WriteText(node.InnerText);
}
else if (node.NodeType == HtmlNodeType.Element && node.Name.TagToType() == TextElements.HtmlElementType.Inline)
{
// detect br here
var inlineTagName = node.Name.ToLower();
if (inlineTagName == "br")
{
renderer.WriteInline(new MyLineBreak());
}
else if (inlineTagName == "a")
{
IAddChild hyperLink;
if (node.ChildNodes.Any(n => n.Name != "#text"))
{
hyperLink = new MyHyperlinkButton(node, renderer.Config.BaseUrl);
}
else
{
hyperLink = new MyHyperlink(node, renderer.Config.BaseUrl);
}
renderer.Push(hyperLink);
WriteHtml(renderer, node.ChildNodes);
renderer.Pop();
}
else if (inlineTagName == "img")
{
var image = new MyImage(node, renderer.Config);
renderer.WriteInline(image);
}
else
{
var inline = new MyInline(node);
renderer.Push(inline);
WriteHtml(renderer, node.ChildNodes);
renderer.Pop();
}
}
else if (node.NodeType == HtmlNodeType.Element && node.Name.TagToType() == TextElements.HtmlElementType.Block)
{
IAddChild block;
var tag = node.Name.ToLower();
if (tag == "details")
{
block = new MyDetails(node);
node.ChildNodes.Remove(node.ChildNodes.FirstOrDefault(x => x.Name == "summary" || x.Name == "header"));
renderer.Push(block);
WriteHtml(renderer, node.ChildNodes);
}
else if (tag.IsHeading())
{
var heading = new MyHeading(node, renderer.Config);
renderer.Push(heading);
WriteHtml(renderer, node.ChildNodes);
}
else
{
block = new MyBlock(node);
renderer.Push(block);
WriteHtml(renderer, node.ChildNodes);
}
renderer.Pop();
}
}
}
}

Просмотреть файл

@ -0,0 +1,10 @@
// 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 interface ISVGRenderer
{
Task<Image> SvgToImage(string svgString);
}

Просмотреть файл

@ -0,0 +1,11 @@
// 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 interface IImageProvider
{
Task<Image> GetImage(string url);
bool ShouldUseThisProvider(string url);
}

Просмотреть файл

@ -0,0 +1,17 @@
// 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 CommunityToolkit.WinUI.Controls.MarkdownTextBlockRns;
namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock;
public record MarkdownConfig
{
public string? BaseUrl { get; set; }
public IImageProvider? ImageProvider { get; set; }
public ISVGRenderer? SVGRenderer { get; set; }
public MarkdownThemes Themes { get; set; } = MarkdownThemes.Default;
public static MarkdownConfig Default = new();
}

Просмотреть файл

@ -0,0 +1,25 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:labs="using:CommunityToolkit.Labs.WinUI.MarkdownTextBlock">
<Style BasedOn="{StaticResource DefaultMarkdownTextBlockStyle}"
TargetType="labs:MarkdownTextBlock" />
<Style x:Key="DefaultMarkdownTextBlockStyle"
TargetType="labs:MarkdownTextBlock">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="labs:MarkdownTextBlock">
<Grid x:Name="MarkdownContainer"
Padding="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
VerticalAlignment="{TemplateBinding VerticalAlignment}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

Просмотреть файл

@ -0,0 +1,115 @@
// 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 CommunityToolkit.Labs.WinUI.MarkdownTextBlock.Renderers;
using CommunityToolkit.Labs.WinUI.MarkdownTextBlock.TextElements;
using Markdig;
namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock;
[TemplatePart(Name = MarkdownContainerName, Type = typeof(Grid))]
public partial class MarkdownTextBlock : Control
{
private const string MarkdownContainerName = "MarkdownContainer";
private Grid? _container;
private MarkdownPipeline _pipeline;
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));
public MarkdownConfig Config
{
get => (MarkdownConfig)GetValue(ConfigProperty);
set => SetValue(ConfigProperty, value);
}
public string Text
{
get => (string)GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
private static void OnConfigChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is MarkdownTextBlock self && e.NewValue != null)
{
self.ApplyConfig(self.Config);
}
}
private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is MarkdownTextBlock self && e.NewValue != null)
{
self.ApplyText(self.Text, true);
}
}
public MarkdownTextBlock()
{
this.DefaultStyleKey = typeof(MarkdownTextBlock);
_document = new MyFlowDocument();
_pipeline = new MarkdownPipelineBuilder()
.UseEmphasisExtras()
.UseAutoLinks()
.UseTaskLists()
.UsePipeTables()
.Build();
}
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
_container = (Grid)GetTemplateChild(MarkdownContainerName);
_container.Children.Clear();
_container.Children.Add(_document.RichTextBlock);
Build();
}
private void ApplyConfig(MarkdownConfig config)
{
if (_renderer != null)
{
_renderer.Config = config;
}
}
private void ApplyText(string text, bool rerender)
{
var markdown = Markdown.Parse(text, _pipeline);
if (_renderer != null)
{
if (rerender)
{
_renderer.ReloadDocument();
}
_renderer.Render(markdown);
}
}
private void Build()
{
if (Config != null)
{
if (_renderer == null)
{
_renderer = new WinUIRenderer(_document, Config);
}
_pipeline.Setup(_renderer);
ApplyText(Text, false);
}
}
}

Просмотреть файл

@ -0,0 +1,65 @@
// 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 CommunityToolkit.Labs.WinUI.MarkdownTextBlock;
#if !WINAPPSDK
using FontWeight = Windows.UI.Text.FontWeight;
using FontWeights = Windows.UI.Text.FontWeights;
#else
using FontWeight = Windows.UI.Text.FontWeight;
using FontWeights = Microsoft.UI.Text.FontWeights;
#endif
namespace CommunityToolkit.WinUI.Controls.MarkdownTextBlockRns;
public sealed class MarkdownThemes : DependencyObject
{
internal static MarkdownThemes Default { get; } = new();
public Thickness Padding { get; set; } = new(8);
public Thickness InternalMargin { get; set; } = new(4);
public CornerRadius CornerRadius { get; set; } = new(4);
public double H1FontSize { get; set; } = 22;
public double H2FontSize { get; set; } = 20;
public double H3FontSize { get; set; } = 18;
public double H4FontSize { get; set; } = 16;
public double H5FontSize { get; set; } = 14;
public double H6FontSize { get; set; } = 12;
public Brush HeadingForeground { get; set; } = Extensions.GetAccentColorBrush();
public FontWeight H1FontWeight { get; set; } = FontWeights.Bold;
public FontWeight H2FontWeight { get; set; } = FontWeights.Normal;
public FontWeight H3FontWeight { get; set; } = FontWeights.Normal;
public FontWeight H4FontWeight { get; set;} = FontWeights.Normal;
public FontWeight H5FontWeight { get; set; } = FontWeights.Normal;
public FontWeight H6FontWeight { get; set; } = FontWeights.Normal;
public Brush InlineCodeBackground { get; set; } = (Brush)Application.Current.Resources["ExpanderHeaderBackground"];
public Brush InlineCodeBorderBrush { get; set; } = new SolidColorBrush(Colors.Gray);
public Thickness InlineCodeBorderThickness { get; set; } = new (1);
public CornerRadius InlineCodeCornerRadius { get; set; } = new (2);
public Thickness InlineCodePadding { get; set; } = new(0);
public double InlineCodeFontSize { get; set; } = 10;
public FontWeight InlineCodeFontWeight { get; set; } = FontWeights.Normal;
}

Просмотреть файл

@ -0,0 +1,9 @@
<Project>
<PropertyGroup>
<!--
MultiTarget is a custom property that indicates which target a project is designed to be built for / run on.
Used to create project references, generate solution files, enable/disable TargetFrameworks, and build nuget packages.
-->
<MultiTarget>uwp;wasdk;</MultiTarget>
</PropertyGroup>
</Project>

Просмотреть файл

@ -0,0 +1,18 @@
// 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;
using CommunityToolkit.Labs.WinUI.MarkdownTextBlock.TextElements;
namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock.Renderers.ObjectRenderers;
internal class CodeBlockRenderer : UWPObjectRenderer<CodeBlock>
{
protected override void Write(WinUIRenderer renderer, CodeBlock obj)
{
var code = new MyCodeBlock(obj, renderer.Config);
renderer.Push(code);
renderer.Pop();
}
}

Просмотреть файл

@ -0,0 +1,60 @@
// 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.Extensions.Tables;
using CommunityToolkit.Labs.WinUI.MarkdownTextBlock.TextElements;
namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock.Renderers.ObjectRenderers.Extensions;
public class TableRenderer : UWPObjectRenderer<Table>
{
protected override void Write(WinUIRenderer renderer, Table table)
{
if (renderer == null) throw new ArgumentNullException(nameof(renderer));
if (table == null) throw new ArgumentNullException(nameof(table));
var myTable = new MyTable(table);
renderer.Push(myTable);
for (var rowIndex = 0; rowIndex < table.Count; rowIndex++)
{
var rowObj = table[rowIndex];
var row = (TableRow)rowObj;
for (var i = 0; i < row.Count; i++)
{
var cellObj = row[i];
var cell = (TableCell)cellObj;
var textAlignment = TextAlignment.Left;
var columnIndex = i;
if (table.ColumnDefinitions.Count > 0)
{
columnIndex = cell.ColumnIndex < 0 || cell.ColumnIndex >= table.ColumnDefinitions.Count
? i
: cell.ColumnIndex;
columnIndex = columnIndex >= table.ColumnDefinitions.Count ? table.ColumnDefinitions.Count - 1 : columnIndex;
var alignment = table.ColumnDefinitions[columnIndex].Alignment;
textAlignment = alignment switch
{
TableColumnAlign.Center => TextAlignment.Center,
TableColumnAlign.Left => TextAlignment.Left,
TableColumnAlign.Right => TextAlignment.Right,
_ => TextAlignment.Left,
};
}
var myCell = new MyTableCell(cell, textAlignment, row.IsHeader, columnIndex, rowIndex);
renderer.Push(myCell);
renderer.Write(cell);
renderer.Pop();
}
}
renderer.Pop();
}
}

Просмотреть файл

@ -0,0 +1,20 @@
// 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.Extensions.TaskLists;
using CommunityToolkit.Labs.WinUI.MarkdownTextBlock.TextElements;
namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock.Renderers.ObjectRenderers.Extensions;
internal class TaskListRenderer : UWPObjectRenderer<TaskList>
{
protected override void Write(WinUIRenderer renderer, TaskList taskList)
{
if (renderer == null) throw new ArgumentNullException(nameof(renderer));
if (taskList == null) throw new ArgumentNullException(nameof(taskList));
var checkBox = new MyTaskListCheckBox(taskList);
renderer.WriteInline(checkBox);
}
}

Просмотреть файл

@ -0,0 +1,22 @@
// 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;
using CommunityToolkit.Labs.WinUI.MarkdownTextBlock.TextElements;
namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock.Renderers.ObjectRenderers;
internal class HeadingRenderer : UWPObjectRenderer<HeadingBlock>
{
protected override void Write(WinUIRenderer renderer, HeadingBlock obj)
{
if (renderer == null) throw new ArgumentNullException(nameof(renderer));
if (obj == null) throw new ArgumentNullException(nameof(obj));
var paragraph = new MyHeading(obj, renderer.Config);
renderer.Push(paragraph);
renderer.WriteLeafInline(obj);
renderer.Pop();
}
}

Просмотреть файл

@ -0,0 +1,34 @@
// 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 HtmlAgilityPack;
using Markdig.Syntax;
namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock.Renderers.ObjectRenderers;
internal class HtmlBlockRenderer : UWPObjectRenderer<HtmlBlock>
{
protected override void Write(WinUIRenderer renderer, HtmlBlock obj)
{
if (renderer == null) throw new ArgumentNullException(nameof(renderer));
if (obj == null) throw new ArgumentNullException(nameof(obj));
var stringBuilder = new StringBuilder();
foreach (var line in obj.Lines.Lines)
{
var lineText = line.Slice.ToString().Trim();
if (String.IsNullOrWhiteSpace(lineText))
{
continue;
}
stringBuilder.AppendLine(lineText);
}
var html = Regex.Replace(stringBuilder.ToString(), @"\t|\n|\r", "", RegexOptions.Compiled);
html = Regex.Replace(html, @"&nbsp;", " ", RegexOptions.Compiled);
var doc = new HtmlDocument();
doc.LoadHtml(html);
HtmlWriter.WriteHtml(renderer, doc.DocumentNode.ChildNodes);
}
}

Просмотреть файл

@ -0,0 +1,35 @@
// 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.Inlines;
using CommunityToolkit.Labs.WinUI.MarkdownTextBlock.TextElements;
namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock.Renderers.ObjectRenderers.Inlines;
internal class AutoLinkInlineRenderer : UWPObjectRenderer<AutolinkInline>
{
protected override void Write(WinUIRenderer renderer, AutolinkInline link)
{
if (renderer == null) throw new ArgumentNullException(nameof(renderer));
if (link == null) throw new ArgumentNullException(nameof(link));
var url = link.Url;
if (link.IsEmail)
{
url = "mailto:" + url;
}
if (!Uri.IsWellFormedUriString(url, UriKind.RelativeOrAbsolute))
{
url = "#";
}
var autolink = new MyAutolinkInline(link);
renderer.Push(autolink);
renderer.WriteText(link.Url);
renderer.Pop();
}
}

Просмотреть файл

@ -0,0 +1,19 @@
// 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.Inlines;
using CommunityToolkit.Labs.WinUI.MarkdownTextBlock.TextElements;
namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock.Renderers.ObjectRenderers.Inlines;
internal class CodeInlineRenderer : UWPObjectRenderer<CodeInline>
{
protected override void Write(WinUIRenderer renderer, CodeInline obj)
{
if (renderer == null) throw new ArgumentNullException(nameof(renderer));
if (obj == null) throw new ArgumentNullException(nameof(obj));
renderer.WriteInline(new MyInlineCode(obj, renderer.Config));
}
}

Просмотреть файл

@ -0,0 +1,18 @@
// 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.Inlines;
namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock.Renderers.ObjectRenderers.Inlines;
internal class ContainerInlineRenderer : UWPObjectRenderer<ContainerInline>
{
protected override void Write(WinUIRenderer renderer, ContainerInline obj)
{
foreach (var inline in obj)
{
renderer.Write(inline);
}
}
}

Просмотреть файл

@ -0,0 +1,21 @@
// 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.Inlines;
namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock.Renderers.ObjectRenderers.Inlines;
internal class DelimiterInlineRenderer : UWPObjectRenderer<DelimiterInline>
{
protected override void Write(WinUIRenderer renderer, DelimiterInline obj)
{
if (renderer == null) throw new ArgumentNullException(nameof(renderer));
if (obj == null) throw new ArgumentNullException(nameof(obj));
// delimiter's children are emphasized text, we don't need to explicitly render them
// Just need to render the children of the delimiter, I think..
//renderer.WriteText(obj.ToLiteral());
renderer.WriteChildren(obj);
}
}

Просмотреть файл

@ -0,0 +1,47 @@
// 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.Inlines;
using CommunityToolkit.Labs.WinUI.MarkdownTextBlock.TextElements;
namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock.Renderers.ObjectRenderers.Inlines;
internal class EmphasisInlineRenderer : UWPObjectRenderer<EmphasisInline>
{
protected override void Write(WinUIRenderer renderer, EmphasisInline obj)
{
if (renderer == null) throw new ArgumentNullException(nameof(renderer));
if (obj == null) throw new ArgumentNullException(nameof(obj));
MyEmphasisInline? span = null;
switch (obj.DelimiterChar)
{
case '*':
case '_':
span = new MyEmphasisInline(obj);
if (obj.DelimiterCount == 2) { span.SetBold(); } else { span.SetItalic(); }
break;
case '~':
span = new MyEmphasisInline(obj);
if (obj.DelimiterCount == 2) { span.SetStrikeThrough(); } else { span.SetSubscript(); }
break;
case '^':
span = new MyEmphasisInline(obj);
span.SetSuperscript();
break;
}
if (span != null)
{
renderer.Push(span);
renderer.WriteChildren(obj);
renderer.Pop();
}
else
{
renderer.WriteChildren(obj);
}
}
}

Просмотреть файл

@ -0,0 +1,20 @@
// 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.Inlines;
namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock.Renderers.ObjectRenderers.Inlines;
internal class HtmlEntityInlineRenderer : UWPObjectRenderer<HtmlEntityInline>
{
protected override void Write(WinUIRenderer renderer, HtmlEntityInline obj)
{
if (renderer == null) throw new ArgumentNullException(nameof(renderer));
if (obj == null) throw new ArgumentNullException(nameof(obj));
var transcoded = obj.Transcoded;
renderer.WriteText(ref transcoded);
// todo: wtf is this?
}
}

Просмотреть файл

@ -0,0 +1,22 @@
// 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 HtmlAgilityPack;
using Markdig.Syntax.Inlines;
namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock.Renderers.ObjectRenderers.Inlines;
internal class HtmlInlineRenderer : UWPObjectRenderer<HtmlInline>
{
protected override void Write(WinUIRenderer renderer, HtmlInline obj)
{
if (renderer == null) throw new ArgumentNullException(nameof(renderer));
if (obj == null) throw new ArgumentNullException(nameof(obj));
var html = obj.Tag;
var doc = new HtmlDocument();
doc.LoadHtml(html);
HtmlWriter.WriteHtml(renderer, doc.DocumentNode.ChildNodes);
}
}

Просмотреть файл

@ -0,0 +1,27 @@
// 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.Inlines;
using CommunityToolkit.Labs.WinUI.MarkdownTextBlock.TextElements;
namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock.Renderers.ObjectRenderers.Inlines;
internal class LineBreakInlineRenderer : UWPObjectRenderer<LineBreakInline>
{
protected override void Write(WinUIRenderer renderer, LineBreakInline obj)
{
if (renderer == null) throw new ArgumentNullException(nameof(renderer));
if (obj == null) throw new ArgumentNullException(nameof(obj));
if (obj.IsHard)
{
renderer.WriteInline(new MyLineBreak());
}
else
{
// Soft line break.
renderer.WriteText(" ");
}
}
}

Просмотреть файл

@ -0,0 +1,46 @@
// 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.Inlines;
using CommunityToolkit.Labs.WinUI.MarkdownTextBlock.TextElements;
namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock.Renderers.ObjectRenderers.Inlines;
internal class LinkInlineRenderer : UWPObjectRenderer<LinkInline>
{
protected override void Write(WinUIRenderer renderer, LinkInline link)
{
if (renderer == null) throw new ArgumentNullException(nameof(renderer));
if (link == null) throw new ArgumentNullException(nameof(link));
var url = link.GetDynamicUrl != null ? link.GetDynamicUrl() ?? link.Url : link.Url;
if (!Uri.IsWellFormedUriString(url, UriKind.RelativeOrAbsolute))
{
url = "#";
}
if (link.IsImage)
{
var image = new MyImage(link, CommunityToolkit.Labs.WinUI.MarkdownTextBlock.Extensions.GetUri(url, renderer.Config.BaseUrl), renderer.Config);
renderer.WriteInline(image);
}
else
{
if (link.FirstChild is LinkInline linkInlineChild && linkInlineChild.IsImage)
{
renderer.Push(new MyHyperlinkButton(link, renderer.Config.BaseUrl));
}
else
{
var hyperlink = new MyHyperlink(link, renderer.Config.BaseUrl);
renderer.Push(hyperlink);
}
renderer.WriteChildren(link);
renderer.Pop();
}
}
}

Просмотреть файл

@ -0,0 +1,21 @@
// 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.Inlines;
namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock.Renderers.ObjectRenderers.Inlines;
internal class LiteralInlineRenderer : UWPObjectRenderer<LiteralInline>
{
protected override void Write(WinUIRenderer renderer, LiteralInline obj)
{
if (renderer == null) throw new ArgumentNullException(nameof(renderer));
if (obj == null) throw new ArgumentNullException(nameof(obj));
if (obj.Content.IsEmpty)
return;
renderer.WriteText(ref obj.Content);
}
}

Просмотреть файл

@ -0,0 +1,32 @@
// 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;
using CommunityToolkit.Labs.WinUI.MarkdownTextBlock.TextElements;
namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock.Renderers.ObjectRenderers;
internal class ListRenderer : UWPObjectRenderer<ListBlock>
{
protected override void Write(WinUIRenderer renderer, ListBlock listBlock)
{
if (renderer == null) throw new ArgumentNullException(nameof(renderer));
if (listBlock == null) throw new ArgumentNullException(nameof(listBlock));
var list = new MyList(listBlock);
renderer.Push(list);
foreach (var item in listBlock)
{
var listItemBlock = (ListItemBlock)item;
var listItem = new MyBlockContainer(listItemBlock);
renderer.Push(listItem);
renderer.WriteChildren(listItemBlock);
renderer.Pop();
}
renderer.Pop();
}
}

Просмотреть файл

@ -0,0 +1,23 @@
// 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;
using CommunityToolkit.Labs.WinUI.MarkdownTextBlock.TextElements;
namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock.Renderers.ObjectRenderers;
internal class ParagraphRenderer : UWPObjectRenderer<ParagraphBlock>
{
protected override void Write(WinUIRenderer renderer, ParagraphBlock obj)
{
if (renderer == null) throw new ArgumentNullException(nameof(renderer));
if (obj == null) throw new ArgumentNullException(nameof(obj));
var paragraph = new MyParagraph(obj);
// set style
renderer.Push(paragraph);
renderer.WriteLeafInline(obj);
renderer.Pop();
}
}

Просмотреть файл

@ -0,0 +1,23 @@
// 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;
using CommunityToolkit.Labs.WinUI.MarkdownTextBlock.TextElements;
namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock.Renderers.ObjectRenderers;
internal class QuoteBlockRenderer : UWPObjectRenderer<QuoteBlock>
{
protected override void Write(WinUIRenderer renderer, QuoteBlock obj)
{
if (renderer == null) throw new ArgumentNullException(nameof(renderer));
if (obj == null) throw new ArgumentNullException(nameof(obj));
var quote = new MyQuote(obj);
renderer.Push(quote);
renderer.WriteChildren(obj);
renderer.Pop();
}
}

Просмотреть файл

@ -0,0 +1,21 @@
// 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;
using CommunityToolkit.Labs.WinUI.MarkdownTextBlock.TextElements;
namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock.Renderers.ObjectRenderers;
internal class ThematicBreakRenderer : UWPObjectRenderer<ThematicBreakBlock>
{
protected override void Write(WinUIRenderer renderer, ThematicBreakBlock obj)
{
if (renderer == null) throw new ArgumentNullException(nameof(renderer));
if (obj == null) throw new ArgumentNullException(nameof(obj));
var thematicBreak = new MyThematicBreak(obj);
renderer.WriteBlock(thematicBreak);
}
}

Просмотреть файл

@ -0,0 +1,13 @@
// 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.Renderers;
using Markdig.Syntax;
namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock.Renderers;
public abstract class UWPObjectRenderer<TObject> : MarkdownObjectRenderer<WinUIRenderer, TObject>
where TObject : MarkdownObject
{
}

Просмотреть файл

@ -0,0 +1,174 @@
// 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 CommunityToolkit.Labs.WinUI.MarkdownTextBlock.Renderers.ObjectRenderers;
using CommunityToolkit.Labs.WinUI.MarkdownTextBlock.Renderers.ObjectRenderers.Inlines;
using CommunityToolkit.Labs.WinUI.MarkdownTextBlock.Renderers.ObjectRenderers.Extensions;
using CommunityToolkit.Labs.WinUI.MarkdownTextBlock.TextElements;
using Markdig.Renderers;
using Markdig.Syntax;
using Markdig.Helpers;
namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock.Renderers;
public class WinUIRenderer : RendererBase
{
private readonly Stack<IAddChild> _stack = new Stack<IAddChild>();
private char[] _buffer;
private MarkdownConfig _config = MarkdownConfig.Default;
public MyFlowDocument FlowDocument { get; private set; }
public MarkdownConfig Config
{
get => _config;
set => _config = value;
}
public WinUIRenderer(MyFlowDocument document, MarkdownConfig config)
{
_buffer = new char[1024];
Config = config;
FlowDocument = document;
// set style
_stack.Push(FlowDocument);
LoadOverridenRenderers();
}
private void LoadOverridenRenderers()
{
LoadRenderers();
}
public override object Render(MarkdownObject markdownObject)
{
Write(markdownObject);
return FlowDocument ?? new();
}
public void ReloadDocument()
{
_stack.Clear();
FlowDocument.RichTextBlock.Blocks.Clear();
_stack.Push(FlowDocument);
LoadOverridenRenderers();
}
public void WriteLeafInline(LeafBlock leafBlock)
{
if (leafBlock == null || leafBlock.Inline == null) throw new ArgumentNullException(nameof(leafBlock));
var inline = (Markdig.Syntax.Inlines.Inline)leafBlock.Inline;
while (inline != null)
{
Write(inline);
inline = inline.NextSibling;
}
}
public void WriteLeafRawLines(LeafBlock leafBlock)
{
if (leafBlock == null) throw new ArgumentNullException(nameof(leafBlock));
if (leafBlock.Lines.Lines != null)
{
var lines = leafBlock.Lines;
var slices = lines.Lines;
for (var i = 0; i < lines.Count; i++)
{
if (i != 0)
WriteInline(new MyLineBreak());
WriteText(ref slices[i].Slice);
}
}
}
public void Push(IAddChild child)
{
_stack.Push(child);
}
public void Pop()
{
var popped = _stack.Pop();
_stack.Peek().AddChild(popped);
}
public void WriteBlock(IAddChild obj)
{
_stack.Peek().AddChild(obj);
}
public void WriteInline(IAddChild inline)
{
AddInline(_stack.Peek(), inline);
}
public void WriteText(ref StringSlice slice)
{
if (slice.Start > slice.End)
return;
WriteText(slice.Text, slice.Start, slice.Length);
}
public void WriteText(string? text)
{
WriteInline(new MyInlineText(text ?? ""));
}
public void WriteText(string? text, int offset, int length)
{
if (text == null)
return;
if (offset == 0 && text.Length == length)
{
WriteText(text);
}
else
{
if (length > _buffer.Length)
{
_buffer = text.ToCharArray();
WriteText(new string(_buffer, offset, length));
}
else
{
text.CopyTo(offset, _buffer, 0, length);
WriteText(new string(_buffer, 0, length));
}
}
}
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());
}
}

Просмотреть файл

@ -0,0 +1,71 @@
// 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 HtmlAgilityPack;
using Windows.UI.Text;
namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock.TextElements.Html;
internal class MyBlock : IAddChild
{
private HtmlNode _htmlNode;
private Paragraph _paragraph;
private List<RichTextBlock> _richTextBlocks;
public TextElement TextElement
{
get => _paragraph;
}
public MyBlock(HtmlNode block)
{
_htmlNode = block;
var align = _htmlNode.GetAttributeValue("align", "left");
_richTextBlocks = new List<RichTextBlock>();
_paragraph = new Paragraph();
_paragraph.TextAlignment = align switch
{
"left" => TextAlignment.Left,
"right" => TextAlignment.Right,
"center" => TextAlignment.Center,
"justify" => TextAlignment.Justify,
_ => TextAlignment.Left,
};
StyleBlock();
}
public void AddChild(IAddChild child)
{
if (child.TextElement is Block blockChild)
{
_paragraph.Inlines.Add(new LineBreak());
var inlineUIContainer = new InlineUIContainer();
var richTextBlock = new RichTextBlock();
richTextBlock.Blocks.Add(blockChild);
inlineUIContainer.Child = richTextBlock;
_richTextBlocks.Add(richTextBlock);
_paragraph.Inlines.Add(inlineUIContainer);
_paragraph.Inlines.Add(new LineBreak());
}
else if (child.TextElement is Inline inlineChild)
{
_paragraph.Inlines.Add(inlineChild);
}
}
private void StyleBlock()
{
switch (_htmlNode.Name.ToLower())
{
case "address":
_paragraph.FontStyle = FontStyle.Italic;
foreach (var richTextBlock in _richTextBlocks)
{
richTextBlock.FontStyle = FontStyle.Italic;
}
//_flowDocument.RichTextBlock.Style = (Windows.UI.Xaml.Style)Windows.UI.Xaml.Application.Current.Resources["AddressBlockStyle"];
break;
}
}
}

Просмотреть файл

@ -0,0 +1,54 @@
// 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 HtmlAgilityPack;
using Microsoft.UI.Xaml.Controls;
namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock.TextElements.Html;
// block
internal class MyDetails : IAddChild
{
private HtmlNode _htmlNode;
private InlineUIContainer _inlineUIContainer;
private Expander _expander;
private MyFlowDocument _flowDocument;
private Paragraph _paragraph;
public TextElement TextElement
{
get => _paragraph;
}
public MyDetails(HtmlNode details)
{
_htmlNode = details;
var header = _htmlNode.ChildNodes
.FirstOrDefault(
x => x.Name == "summary" ||
x.Name == "header");
_inlineUIContainer = new InlineUIContainer();
_expander = new Expander();
_expander.HorizontalAlignment = HorizontalAlignment.Stretch;
_flowDocument = new MyFlowDocument(details);
_flowDocument.RichTextBlock.HorizontalAlignment = HorizontalAlignment.Stretch;
_expander.Content = _flowDocument.RichTextBlock;
var headerBlock = new TextBlock()
{
Text = header?.InnerText
};
headerBlock.HorizontalAlignment = HorizontalAlignment.Stretch;
_expander.Header = headerBlock;
_inlineUIContainer.Child = _expander;
_paragraph = new Paragraph();
_paragraph.Inlines.Add(_inlineUIContainer);
}
public void AddChild(IAddChild child)
{
_flowDocument.AddChild(child);
}
}

Просмотреть файл

@ -0,0 +1,52 @@
// 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 HtmlAgilityPack;
namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock.TextElements.Html;
internal class MyInline : IAddChild
{
private HtmlNode _htmlNode;
private Paragraph _paragraph;
private InlineUIContainer _inlineUIContainer;
private RichTextBlock _richTextBlock;
public TextElement TextElement
{
get => _inlineUIContainer;
}
public MyInline(HtmlNode inline)
{
_htmlNode = inline;
_paragraph = new Paragraph();
_inlineUIContainer = new InlineUIContainer();
_richTextBlock = new RichTextBlock();
_richTextBlock.Blocks.Add(_paragraph);
_richTextBlock.HorizontalAlignment = HorizontalAlignment.Stretch;
_inlineUIContainer.Child = _richTextBlock;
}
public void AddChild(IAddChild child)
{
if (child.TextElement is Inline inlineChild)
{
_paragraph.Inlines.Add(inlineChild);
}
// we shouldn't support rendering block in inline
// but if we want to support it, we can do it like this:
//else if (child.TextElement is Block blockChild)
//{
// _richTextBlock.Blocks.Add(blockChild);
// // if we add a new block to an inline container,
// // if the next child is inline, it needs to be added after the block
// // so we add a new paragraph. This way the next time
// // AddChild is called, it's added to the new paragraph
// _paragraph = new Paragraph();
// _richTextBlock.Blocks.Add(_paragraph);
//}
}
}

Просмотреть файл

@ -0,0 +1,11 @@
// 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.TextElements;
public enum HtmlElementType
{
Block,
Inline,
}

Просмотреть файл

@ -0,0 +1,11 @@
// 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.TextElements;
public interface IAddChild
{
TextElement TextElement { get; }
void AddChild(IAddChild child);
}

Просмотреть файл

@ -0,0 +1,37 @@
// 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.Inlines;
namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock.TextElements;
internal class MyAutolinkInline : IAddChild
{
private AutolinkInline _autoLinkInline;
public TextElement TextElement { get; private set; }
public MyAutolinkInline(AutolinkInline autoLinkInline)
{
_autoLinkInline = autoLinkInline;
TextElement = new Hyperlink()
{
NavigateUri = new Uri(autoLinkInline.Url),
};
}
public void AddChild(IAddChild child)
{
try
{
var text = (MyInlineText)child;
((Hyperlink)TextElement).Inlines.Add((Run)text.TextElement);
}
catch (Exception ex)
{
throw new Exception("Error adding child to MyAutolinkInline", ex);
}
}
}

Просмотреть файл

@ -0,0 +1,35 @@
// 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.TextElements;
internal class MyBlockContainer : IAddChild
{
private ContainerBlock _containerBlock;
private InlineUIContainer _inlineUIContainer;
private MyFlowDocument _flowDocument;
private Paragraph _paragraph;
public TextElement TextElement
{
get => _paragraph;
}
public MyBlockContainer(ContainerBlock containerBlock)
{
_containerBlock = containerBlock;
_inlineUIContainer = new InlineUIContainer();
_flowDocument = new MyFlowDocument(containerBlock);
_inlineUIContainer.Child = _flowDocument.RichTextBlock;
_paragraph = new Paragraph();
_paragraph.Inlines.Add(_inlineUIContainer);
}
public void AddChild(IAddChild child)
{
_flowDocument.AddChild(child);
}
}

Просмотреть файл

@ -0,0 +1,88 @@
// 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.TextElements;
internal class MyCodeBlock : IAddChild
{
private CodeBlock _codeBlock;
private Paragraph _paragraph;
private MarkdownConfig _config;
public TextElement TextElement
{
get => _paragraph;
}
public MyCodeBlock(CodeBlock codeBlock, MarkdownConfig config)
{
_codeBlock = codeBlock;
_config = config;
_paragraph = new Paragraph();
var container = new InlineUIContainer();
var border = new Border();
border.Background = (Brush)Application.Current.Resources["ExpanderHeaderBackground"];
border.Padding = _config.Themes.Padding;
border.Margin = _config.Themes.InternalMargin;
border.CornerRadius = _config.Themes.CornerRadius;
var richTextBlock = new RichTextBlock();
if (codeBlock is FencedCodeBlock fencedCodeBlock)
{
//#if !WINAPPSDK
// var formatter = new ColorCode.RichTextBlockFormatter(Extensions.GetOneDarkProStyle());
//#else
// var formatter = new ColorCode.RichTextBlockFormatter(Extensions.GetOneDarkProStyle());
//#endif
var stringBuilder = new StringBuilder();
// go through all the lines backwards and only add the lines to a stack if we have encountered the first non-empty line
var lines = fencedCodeBlock.Lines.Lines;
var stack = new Stack<string>();
var encounteredFirstNonEmptyLine = false;
if (lines != null)
{
for (var i = lines.Length - 1; i >= 0; i--)
{
var line = lines[i];
if (String.IsNullOrWhiteSpace(line.ToString()) && !encounteredFirstNonEmptyLine)
{
continue;
}
encounteredFirstNonEmptyLine = true;
stack.Push(line.ToString());
}
// append all the lines in the stack to the string builder
while (stack.Count > 0)
{
stringBuilder.AppendLine(stack.Pop());
}
}
//formatter.FormatRichTextBlock(stringBuilder.ToString(), fencedCodeBlock.ToLanguage(), richTextBlock);
}
else
{
foreach (var line in codeBlock.Lines.Lines)
{
var paragraph = new Paragraph();
var lineString = line.ToString();
if (!String.IsNullOrWhiteSpace(lineString))
{
paragraph.Inlines.Add(new Run() { Text = lineString });
}
richTextBlock.Blocks.Add(paragraph);
}
}
border.Child = richTextBlock;
container.Child = border;
_paragraph.Inlines.Add(container);
}
public void AddChild(IAddChild child) {}
}

Просмотреть файл

@ -0,0 +1,79 @@
// 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.Inlines;
using Windows.UI.Text;
namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock.TextElements;
internal class MyEmphasisInline : IAddChild
{
private Span _span;
private EmphasisInline _markdownObject;
private bool _isBold;
private bool _isItalic;
private bool _isStrikeThrough;
public TextElement TextElement
{
get => _span;
}
public MyEmphasisInline(EmphasisInline emphasisInline)
{
_span = new Span();
_markdownObject = emphasisInline;
}
public void AddChild(IAddChild child)
{
try
{
if (child is MyInlineText inlineText)
{
_span.Inlines.Add((Run)inlineText.TextElement);
}
else if (child is MyEmphasisInline emphasisInline)
{
if (emphasisInline._isBold) { SetBold(); }
if (emphasisInline._isItalic) { SetItalic(); }
if (emphasisInline._isStrikeThrough) { SetStrikeThrough(); }
_span.Inlines.Add(emphasisInline._span);
}
}
catch (Exception ex)
{
throw new Exception($"Error in {nameof(MyEmphasisInline)}.{nameof(AddChild)}: {ex.Message}");
}
}
public void SetBold()
{
_span.FontWeight = FontWeights.Bold;
_isBold = true;
}
public void SetItalic()
{
_span.FontStyle = FontStyle.Italic;
_isItalic = true;
}
public void SetStrikeThrough()
{
_span.TextDecorations = TextDecorations.Strikethrough;
_isStrikeThrough = true;
}
public void SetSubscript()
{
_span.SetValue(Typography.VariantsProperty, FontVariants.Subscript);
}
public void SetSuperscript()
{
_span.SetValue(Typography.VariantsProperty, FontVariants.Superscript);
}
}

Просмотреть файл

@ -0,0 +1,66 @@
// 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 HtmlAgilityPack;
using Markdig.Syntax;
#if !WINAPPSDK
using Block = Windows.UI.Xaml.Documents.Block;
using Inline = Windows.UI.Xaml.Documents.Inline;
#else
using Block = Microsoft.UI.Xaml.Documents.Block;
using Inline = Microsoft.UI.Xaml.Documents.Inline;
#endif
namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock.TextElements;
public class MyFlowDocument : IAddChild
{
private HtmlNode? _htmlNode;
private RichTextBlock _richTextBlock = new RichTextBlock();
private MarkdownObject? _markdownObject;
// useless property
public TextElement TextElement { get; set; } = new Run();
//
public RichTextBlock RichTextBlock
{
get => _richTextBlock;
set => _richTextBlock = value;
}
public bool IsHtml => _htmlNode != null;
public MyFlowDocument()
{
}
public MyFlowDocument(MarkdownObject markdownObject)
{
_markdownObject = markdownObject;
}
public MyFlowDocument(HtmlNode node)
{
_htmlNode = node;
}
public void AddChild(IAddChild child)
{
TextElement element = child.TextElement;
if (element != null)
{
if (element is Block block)
{
_richTextBlock.Blocks.Add(block);
}
else if (element is Inline inline)
{
var paragraph = new Paragraph();
paragraph.Inlines.Add(inline);
_richTextBlock.Blocks.Add(paragraph);
}
}
}
}

Просмотреть файл

@ -0,0 +1,97 @@
// 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 HtmlAgilityPack;
using Markdig.Syntax;
namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock.TextElements;
internal class MyHeading : IAddChild
{
private Paragraph _paragraph;
private HeadingBlock? _headingBlock;
private HtmlNode? _htmlNode;
private MarkdownConfig _config;
public bool IsHtml => _htmlNode != null;
public TextElement TextElement
{
get => _paragraph;
}
public MyHeading(HeadingBlock headingBlock, MarkdownConfig config)
{
_headingBlock = headingBlock;
_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,
};
}
public MyHeading(HtmlNode htmlNode, MarkdownConfig config)
{
_htmlNode = htmlNode;
_paragraph = new Paragraph();
_config = config;
var align = _htmlNode.GetAttributeValue("align", "left");
_paragraph.TextAlignment = align switch
{
"left" => TextAlignment.Left,
"right" => TextAlignment.Right,
"center" => TextAlignment.Center,
"justify" => TextAlignment.Justify,
_ => TextAlignment.Left,
};
var level = int.Parse(htmlNode.Name.Substring(1));
_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,
};
}
public void AddChild(IAddChild child)
{
if (child.TextElement is Inline inlineChild)
{
_paragraph.Inlines.Add(inlineChild);
}
}
}

Просмотреть файл

@ -0,0 +1,70 @@
// 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 HtmlAgilityPack;
using Markdig.Syntax.Inlines;
namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock.TextElements;
internal class MyHyperlink : IAddChild
{
private Hyperlink _hyperlink;
private LinkInline? _linkInline;
private HtmlNode? _htmlNode;
private string? _baseUrl;
public bool IsHtml => _htmlNode != null;
public TextElement TextElement
{
get => _hyperlink;
}
public MyHyperlink(LinkInline linkInline, string? baseUrl)
{
_baseUrl = baseUrl;
var url = linkInline.GetDynamicUrl != null ? linkInline.GetDynamicUrl() ?? linkInline.Url : linkInline.Url;
_linkInline = linkInline;
_hyperlink = new Hyperlink()
{
NavigateUri = Extensions.GetUri(url, baseUrl),
};
}
public MyHyperlink(HtmlNode htmlNode, string? baseUrl)
{
_baseUrl = baseUrl;
var url = htmlNode.GetAttributeValue("href", "#");
_htmlNode = htmlNode;
_hyperlink = new Hyperlink()
{
NavigateUri = Extensions.GetUri(url, baseUrl),
};
}
public void AddChild(IAddChild child)
{
#if !WINAPPSDK
if (child.TextElement is Windows.UI.Xaml.Documents.Inline inlineChild)
{
try
{
_hyperlink.Inlines.Add(inlineChild);
// TODO: Add support for click handler
}
catch { }
}
#else
if (child.TextElement is Microsoft.UI.Xaml.Documents.Inline inlineChild)
{
try
{
_hyperlink.Inlines.Add(inlineChild);
// TODO: Add support for click handler
}
catch { }
}
#endif
}
}

Просмотреть файл

@ -0,0 +1,66 @@
// 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 HtmlAgilityPack;
using Markdig.Syntax.Inlines;
namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock.TextElements;
internal class MyHyperlinkButton : IAddChild
{
private HyperlinkButton? _hyperLinkButton;
private InlineUIContainer _inlineUIContainer = new InlineUIContainer();
private MyFlowDocument? _flowDoc;
private string? _baseUrl;
private LinkInline? _linkInline;
private HtmlNode? _htmlNode;
public bool IsHtml => _htmlNode != null;
public TextElement TextElement
{
get => _inlineUIContainer;
}
public MyHyperlinkButton(LinkInline linkInline, string? baseUrl)
{
_baseUrl = baseUrl;
var url = linkInline.GetDynamicUrl != null ? linkInline.GetDynamicUrl() ?? linkInline.Url : linkInline.Url;
_linkInline = linkInline;
Init(url, baseUrl);
}
public MyHyperlinkButton(HtmlNode htmlNode, string? baseUrl)
{
_baseUrl = baseUrl;
_htmlNode = htmlNode;
var url = htmlNode.GetAttributeValue("href", "#");
Init(url, baseUrl);
}
private void Init(string? url, string? baseUrl)
{
_hyperLinkButton = new HyperlinkButton()
{
NavigateUri = Extensions.GetUri(url, baseUrl),
};
_hyperLinkButton.Padding = new Thickness(0);
_hyperLinkButton.Margin = new Thickness(0);
if (IsHtml && _htmlNode != null)
{
_flowDoc = new MyFlowDocument(_htmlNode);
}
else if (_linkInline != null)
{
_flowDoc = new MyFlowDocument(_linkInline);
}
_inlineUIContainer.Child = _hyperLinkButton;
_hyperLinkButton.Content = _flowDoc?.RichTextBlock;
}
public void AddChild(IAddChild child)
{
_flowDoc?.AddChild(child);
}
}

Просмотреть файл

@ -0,0 +1,158 @@
// 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.Inlines;
using HtmlAgilityPack;
using System.Globalization;
using Windows.Storage.Streams;
namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock.TextElements;
internal class MyImage : IAddChild
{
private InlineUIContainer _container = new InlineUIContainer();
private LinkInline? _linkInline;
private Image _image = new Image();
private Uri _uri;
private HtmlNode? _htmlNode;
private IImageProvider? _imageProvider;
private ISVGRenderer _svgRenderer;
private double _precedentWidth;
private double _precedentHeight;
private bool _loaded;
public TextElement TextElement
{
get => _container;
}
public MyImage(LinkInline linkInline, Uri uri, MarkdownConfig config)
{
_linkInline = linkInline;
_uri = uri;
_imageProvider = config.ImageProvider;
_svgRenderer = config.SVGRenderer == null ? new DefaultSVGRenderer() : config.SVGRenderer;
Init();
var size = Extensions.GetMarkdownImageSize(linkInline);
if (size.Width != 0)
{
_precedentWidth = size.Width;
}
if (size.Height != 0)
{
_precedentHeight = size.Height;
}
}
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
public MyImage(HtmlNode htmlNode, MarkdownConfig? config)
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
{
#pragma warning disable CS8601 // Possible null reference assignment.
Uri.TryCreate(htmlNode.GetAttributeValue("src", "#"), UriKind.RelativeOrAbsolute, out _uri);
#pragma warning restore CS8601 // Possible null reference assignment.
_htmlNode = htmlNode;
_imageProvider = config?.ImageProvider;
_svgRenderer = config?.SVGRenderer == null ? new DefaultSVGRenderer() : config.SVGRenderer;
Init();
int.TryParse(
htmlNode.GetAttributeValue("width", "0"),
NumberStyles.Integer,
CultureInfo.InvariantCulture,
out var width
);
int.TryParse(
htmlNode.GetAttributeValue("height", "0"),
NumberStyles.Integer,
CultureInfo.InvariantCulture,
out var height
);
if (width > 0)
{
_precedentWidth = width;
}
if (height > 0)
{
_precedentHeight = height;
}
}
private void Init()
{
_image.Loaded += LoadImage;
_container.Child = _image;
}
private async void LoadImage(object sender, RoutedEventArgs e)
{
if (_loaded) return;
try
{
if (_imageProvider != null && _imageProvider.ShouldUseThisProvider(_uri.AbsoluteUri))
{
_image = await _imageProvider.GetImage(_uri.AbsoluteUri);
_container.Child = _image;
}
else
{
HttpClient client = new HttpClient();
// Download data from URL
HttpResponseMessage response = await client.GetAsync(_uri);
// Get the Content-Type header
#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
#pragma warning disable CS8602 // Dereference of a possibly null reference.
string contentType = response.Content.Headers.ContentType.MediaType;
#pragma warning restore CS8602 // Dereference of a possibly null reference.
#pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type.
if (contentType == "image/svg+xml")
{
var svgString = await response.Content.ReadAsStringAsync();
var resImage = await _svgRenderer.SvgToImage(svgString);
if (resImage != null)
{
_image = resImage;
_container.Child = _image;
}
}
else
{
byte[] data = await response.Content.ReadAsByteArrayAsync();
// Create a BitmapImage for other supported formats
BitmapImage bitmap = new BitmapImage();
using (InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream())
{
// Write the data to the stream
await stream.WriteAsync(data.AsBuffer());
stream.Seek(0);
// Set the source of the BitmapImage
await bitmap.SetSourceAsync(stream);
}
_image.Source = bitmap;
_image.Width = bitmap.PixelWidth == 0 ? bitmap.DecodePixelWidth : bitmap.PixelWidth;
_image.Height = bitmap.PixelHeight == 0 ? bitmap.DecodePixelHeight : bitmap.PixelHeight;
}
_loaded = true;
}
if (_precedentWidth != 0)
{
_image.Width = _precedentWidth;
}
if (_precedentHeight != 0)
{
_image.Height = _precedentHeight;
}
}
catch (Exception) { }
}
public void AddChild(IAddChild child) {}
}

Просмотреть файл

@ -0,0 +1,45 @@
// 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.Inlines;
namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock.TextElements;
internal class MyInlineCode : IAddChild
{
private CodeInline _codeInline;
private InlineUIContainer _inlineContainer;
private MarkdownConfig _config;
public TextElement TextElement
{
get => _inlineContainer;
}
public MyInlineCode(CodeInline codeInline, MarkdownConfig config)
{
_codeInline = codeInline;
_config = config;
_inlineContainer = new InlineUIContainer();
var border = new Border();
border.VerticalAlignment = VerticalAlignment.Bottom;
border.Background = _config.Themes.InlineCodeBackground;
border.BorderBrush = _config.Themes.InlineCodeBorderBrush;
border.BorderThickness = _config.Themes.InlineCodeBorderThickness;
border.CornerRadius = _config.Themes.InlineCodeCornerRadius;
border.Padding = _config.Themes.InlineCodePadding;
CompositeTransform3D transform = new CompositeTransform3D();
transform.TranslateY = 4;
border.Transform3D = transform;
var textBlock = new TextBlock();
textBlock.FontSize = _config.Themes.InlineCodeFontSize;
textBlock.FontWeight = _config.Themes.InlineCodeFontWeight;
textBlock.Text = codeInline.Content.ToString();
border.Child = textBlock;
_inlineContainer.Child = border;
}
public void AddChild(IAddChild child) {}
}

Просмотреть файл

@ -0,0 +1,25 @@
// 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.TextElements;
internal class MyInlineText : IAddChild
{
private Run _run;
public TextElement TextElement
{
get => _run;
}
public MyInlineText(string text)
{
_run = new Run()
{
Text = text
};
}
public void AddChild(IAddChild child) {}
}

Просмотреть файл

@ -0,0 +1,22 @@
// 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.TextElements;
internal class MyLineBreak : IAddChild
{
private LineBreak _lineBreak;
public TextElement TextElement
{
get => _lineBreak;
}
public MyLineBreak()
{
_lineBreak = new LineBreak();
}
public void AddChild(IAddChild child) {}
}

Просмотреть файл

@ -0,0 +1,123 @@
// 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;
using RomanNumerals;
using System.Globalization;
namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock.TextElements;
internal class MyList : IAddChild
{
private Paragraph _paragraph;
private InlineUIContainer _container;
private StackPanel _stackPanel;
private ListBlock _listBlock;
private BulletType _bulletType;
private bool _isOrdered;
private int _startIndex = 1;
private int _index = 1;
private const string _dot = "• ";
public TextElement TextElement
{
get => _paragraph;
}
public MyList(ListBlock listBlock)
{
_paragraph = new Paragraph();
_container = new InlineUIContainer();
_stackPanel = new StackPanel();
_listBlock = listBlock;
if (listBlock.IsOrdered)
{
_isOrdered = true;
_bulletType = ToBulletType(listBlock.BulletType);
if (listBlock.OrderedStart != null && (listBlock.DefaultOrderedStart != listBlock.OrderedStart))
{
_startIndex = int.Parse(listBlock.OrderedStart, NumberFormatInfo.InvariantInfo);
_index = _startIndex;
}
}
_stackPanel.Orientation = Orientation.Vertical;
_container.Child = _stackPanel;
_paragraph.Inlines.Add(_container);
}
public void AddChild(IAddChild child)
{
var grid = new Grid();
grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Auto) });
grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) });
string bullet;
if (_isOrdered)
{
bullet = _bulletType switch
{
BulletType.Number => $"{_index}. ",
BulletType.LowerAlpha => $"{_index.ToAlphabetical()}. ",
BulletType.UpperAlpha => $"{_index.ToAlphabetical().ToUpper()}. ",
BulletType.LowerRoman => $"{_index.ToRomanNumerals().ToLower()} ",
BulletType.UpperRoman => $"{_index.ToRomanNumerals().ToUpper()} ",
BulletType.Circle => _dot,
_ => _dot
};
_index++;
}
else
{
bullet = _dot;
}
var textBlock = new TextBlock()
{
Text = bullet,
};
textBlock.SetValue(Grid.ColumnProperty, 0);
textBlock.VerticalAlignment = VerticalAlignment.Top;
grid.Children.Add(textBlock);
var flowDoc = new MyFlowDocument();
flowDoc.AddChild(child);
flowDoc.RichTextBlock.SetValue(Grid.ColumnProperty, 1);
flowDoc.RichTextBlock.Padding = new Thickness(0);
flowDoc.RichTextBlock.VerticalAlignment = VerticalAlignment.Top;
grid.Children.Add(flowDoc.RichTextBlock);
_stackPanel.Children.Add(grid);
}
private BulletType ToBulletType(char bullet)
{
/// Gets or sets the type of the bullet (e.g: '1', 'a', 'A', 'i', 'I').
switch (bullet)
{
case '1':
return BulletType.Number;
case 'a':
return BulletType.LowerAlpha;
case 'A':
return BulletType.UpperAlpha;
case 'i':
return BulletType.LowerRoman;
case 'I':
return BulletType.UpperRoman;
default:
return BulletType.Circle;
}
}
}
internal enum BulletType
{
Circle,
Number,
LowerAlpha,
UpperAlpha,
LowerRoman,
UpperRoman
}

Просмотреть файл

@ -0,0 +1,45 @@
// 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.TextElements;
internal class MyParagraph : IAddChild
{
private ParagraphBlock _paragraphBlock;
private Paragraph _paragraph;
public TextElement TextElement
{
get => _paragraph;
}
public MyParagraph(ParagraphBlock paragraphBlock)
{
_paragraphBlock = paragraphBlock;
_paragraph = new Paragraph();
}
public void AddChild(IAddChild child)
{
if (child.TextElement is Inline inlineChild)
{
_paragraph.Inlines.Add(inlineChild);
}
#if !WINAPPSDK
else if (child.TextElement is Windows.UI.Xaml.Documents.Block blockChild)
#else
else if (child.TextElement is Microsoft.UI.Xaml.Documents.Block blockChild)
#endif
{
var inlineUIContainer = new InlineUIContainer();
var richTextBlock = new RichTextBlock();
richTextBlock.TextWrapping = TextWrapping.Wrap;
richTextBlock.Blocks.Add(blockChild);
inlineUIContainer.Child = richTextBlock;
_paragraph.Inlines.Add(inlineUIContainer);
}
}
}

Просмотреть файл

@ -0,0 +1,57 @@
// 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.TextElements;
internal class MyQuote : IAddChild
{
private Paragraph _paragraph;
private MyFlowDocument _flowDocument;
private QuoteBlock _quoteBlock;
public TextElement TextElement
{
get => _paragraph;
}
public MyQuote(QuoteBlock quoteBlock)
{
_quoteBlock = quoteBlock;
_paragraph = new Paragraph();
_flowDocument = new MyFlowDocument(quoteBlock);
var inlineUIContainer = new InlineUIContainer();
var grid = new Grid();
grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Auto) });
grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Auto) });
var bar = new Grid();
bar.Width = 4;
bar.Background = new SolidColorBrush(Colors.Gray);
bar.SetValue(Grid.ColumnProperty, 0);
bar.VerticalAlignment = VerticalAlignment.Stretch;
bar.Margin = new Thickness(0, 0, 4, 0);
grid.Children.Add(bar);
var rightGrid = new Grid();
rightGrid.Padding = new Thickness(4);
rightGrid.Children.Add(_flowDocument.RichTextBlock);
rightGrid.SetValue(Grid.ColumnProperty, 1);
grid.Children.Add(rightGrid);
grid.Margin = new Thickness(0, 2, 0, 2);
inlineUIContainer.Child = grid;
_paragraph.Inlines.Add(inlineUIContainer);
}
public void AddChild(IAddChild child)
{
_flowDocument.AddChild(child);
}
}

Просмотреть файл

@ -0,0 +1,54 @@
// 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.Extensions.Tables;
namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock.TextElements;
internal class MyTable : IAddChild
{
private Table _table;
private Paragraph _paragraph;
private MyTableUIElement _tableElement;
public TextElement TextElement
{
get => _paragraph;
}
public MyTable(Table table)
{
_table = table;
_paragraph = new Paragraph();
var row = table.FirstOrDefault() as TableRow;
var column = row == null ? 0 : row.Count;
_tableElement = new MyTableUIElement
(
column,
table.Count,
1,
new SolidColorBrush(Colors.Gray)
);
var inlineUIContainer = new InlineUIContainer();
inlineUIContainer.Child = _tableElement;
_paragraph.Inlines.Add(inlineUIContainer);
}
public void AddChild(IAddChild child)
{
if (child is MyTableCell cellChild)
{
var cell = cellChild.Container;
Grid.SetColumn(cell, cellChild.ColumnIndex);
Grid.SetRow(cell, cellChild.RowIndex);
Grid.SetColumnSpan(cell, cellChild.ColumnSpan);
Grid.SetRowSpan(cell, cellChild.RowSpan);
_tableElement.Children.Add(cell);
}
}
}

Просмотреть файл

@ -0,0 +1,88 @@
// 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.Extensions.Tables;
namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock.TextElements;
internal class MyTableCell : IAddChild
{
private TableCell _tableCell;
private Paragraph _paragraph = new Paragraph();
private MyFlowDocument _flowDocument;
private bool _isHeader;
private int _columnIndex;
private int _rowIndex;
private Grid _container;
public TextElement TextElement
{
get => _paragraph;
}
public Grid Container
{
get => _container;
}
public int ColumnSpan
{
get => _tableCell.ColumnSpan;
}
public int RowSpan
{
get => _tableCell.RowSpan;
}
public int ColumnIndex
{
get => _columnIndex;
}
public int RowIndex
{
get => _rowIndex;
}
public MyTableCell(TableCell tableCell, TextAlignment textAlignment, bool isHeader, int columnIndex, int rowIndex)
{
_isHeader = isHeader;
_tableCell = tableCell;
_columnIndex = columnIndex;
_rowIndex = rowIndex;
_container = new Grid();
_flowDocument = new MyFlowDocument(tableCell);
_flowDocument.RichTextBlock.TextWrapping = TextWrapping.Wrap;
_flowDocument.RichTextBlock.TextAlignment = textAlignment;
_flowDocument.RichTextBlock.HorizontalTextAlignment = textAlignment;
_flowDocument.RichTextBlock.HorizontalAlignment = textAlignment switch
{
TextAlignment.Left => HorizontalAlignment.Left,
TextAlignment.Center => HorizontalAlignment.Center,
TextAlignment.Right => HorizontalAlignment.Right,
_ => HorizontalAlignment.Left,
};
_container.Padding = new Thickness(4);
if (_isHeader)
{
_flowDocument.RichTextBlock.FontWeight = FontWeights.Bold;
}
_flowDocument.RichTextBlock.HorizontalAlignment = textAlignment switch
{
TextAlignment.Left => HorizontalAlignment.Left,
TextAlignment.Center => HorizontalAlignment.Center,
TextAlignment.Right => HorizontalAlignment.Right,
_ => HorizontalAlignment.Left,
};
_container.Children.Add(_flowDocument.RichTextBlock);
}
public void AddChild(IAddChild child)
{
_flowDocument.AddChild(child);
}
}

Просмотреть файл

@ -0,0 +1,41 @@
// 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.Extensions.Tables;
namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock.TextElements;
internal class MyTableRow : IAddChild
{
private TableRow _tableRow;
private StackPanel _stackPanel;
private Paragraph _paragraph;
public TextElement TextElement
{
get => _paragraph;
}
public MyTableRow(TableRow tableRow)
{
_tableRow = tableRow;
_paragraph = new Paragraph();
_stackPanel = new StackPanel();
_stackPanel.Orientation = Orientation.Horizontal;
var inlineUIContainer = new InlineUIContainer();
inlineUIContainer.Child = _stackPanel;
_paragraph.Inlines.Add(inlineUIContainer);
}
public void AddChild(IAddChild child)
{
if (child is MyTableCell cellChild)
{
var richTextBlock = new RichTextBlock();
richTextBlock.Blocks.Add((Paragraph)cellChild.TextElement);
_stackPanel.Children.Add(richTextBlock);
}
}
}

Просмотреть файл

@ -0,0 +1,200 @@
// 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.TextElements;
internal class MyTableUIElement : Panel
{
private readonly int _columnCount;
private readonly int _rowCount;
private readonly double _borderThickness;
private double[]? _columnWidths;
private double[]? _rowHeights;
public MyTableUIElement(int columnCount, int rowCount, double borderThickness, Brush borderBrush)
{
_columnCount = columnCount;
_rowCount = rowCount;
_borderThickness = borderThickness;
for (int col = 0; col < columnCount + 1; col++)
{
Children.Add(new Rectangle { Fill = borderBrush });
}
for (int row = 0; row < rowCount + 1; row++)
{
Children.Add(new Rectangle { Fill = borderBrush });
}
}
// Helper method to enumerate FrameworkElements instead of UIElements.
private IEnumerable<FrameworkElement> ContentChildren
{
get
{
for (int i = _columnCount + _rowCount + 2; i < Children.Count; i++)
{
yield return (FrameworkElement)Children[i];
}
}
}
// Helper method to get table vertical edges.
private IEnumerable<Rectangle> VerticalLines
{
get
{
for (int i = 0; i < _columnCount + 1; i++)
{
yield return (Rectangle)Children[i];
}
}
}
// Helper method to get table horizontal edges.
private IEnumerable<Rectangle> HorizontalLines
{
get
{
for (int i = _columnCount + 1; i < _columnCount + _rowCount + 2; i++)
{
yield return (Rectangle)Children[i];
}
}
}
protected override Size MeasureOverride(Size availableSize)
{
// Measure the width of each column, with no horizontal width restrictions.
var naturalColumnWidths = new double[_columnCount];
foreach (var child in ContentChildren)
{
var columnIndex = Grid.GetColumn(child);
child.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
naturalColumnWidths[columnIndex] = Math.Max(naturalColumnWidths[columnIndex], child.DesiredSize.Width);
}
// Now figure out the actual column widths.
var remainingContentWidth = availableSize.Width - ((_columnCount + 1) * _borderThickness);
_columnWidths = new double[_columnCount];
int remainingColumnCount = _columnCount;
while (remainingColumnCount > 0)
{
// Calculate the fair width of all columns.
double fairWidth = Math.Max(0, remainingContentWidth / remainingColumnCount);
// Are there any columns less than that? If so, they get what they are asking for.
bool recalculationNeeded = false;
for (int i = 0; i < _columnCount; i++)
{
if (_columnWidths[i] == 0 && naturalColumnWidths[i] < fairWidth)
{
_columnWidths[i] = naturalColumnWidths[i];
remainingColumnCount--;
remainingContentWidth -= _columnWidths[i];
recalculationNeeded = true;
}
}
// If there are no columns less than the fair width, every remaining column gets that width.
if (recalculationNeeded == false)
{
for (int i = 0; i < _columnCount; i++)
{
if (_columnWidths[i] == 0)
{
_columnWidths[i] = fairWidth;
}
}
break;
}
}
// TODO: we can skip this step if none of the column widths changed, and just re-use
// the row heights we obtained earlier.
// Now measure row heights.
_rowHeights = new double[_rowCount];
foreach (var child in ContentChildren)
{
var columnIndex = Grid.GetColumn(child);
var rowIndex = Grid.GetRow(child);
child.Measure(new Size(_columnWidths[columnIndex], double.PositiveInfinity));
_rowHeights[rowIndex] = Math.Max(_rowHeights[rowIndex], child.DesiredSize.Height);
}
return new Size(
_columnWidths.Sum() + (_borderThickness * (_columnCount + 1)),
_rowHeights.Sum() + ((_rowCount + 1) * _borderThickness));
}
protected override Size ArrangeOverride(Size finalSize)
{
if (_columnWidths == null || _rowHeights == null)
{
throw new InvalidOperationException("Expected Measure to be called first.");
}
// Arrange content.
foreach (var child in ContentChildren)
{
var columnIndex = Grid.GetColumn(child);
var rowIndex = Grid.GetRow(child);
var rect = new Rect(_borderThickness, 0, 0, 0);
for (int col = 0; col < columnIndex; col++)
{
rect.X += _borderThickness + _columnWidths[col];
}
rect.Y = _borderThickness;
for (int row = 0; row < rowIndex; row++)
{
rect.Y += _borderThickness + _rowHeights[row];
}
rect.Width = _columnWidths[columnIndex];
rect.Height = _rowHeights[rowIndex];
child.Arrange(rect);
}
// Arrange vertical border elements.
{
int colIndex = 0;
double x = 0;
foreach (var borderLine in VerticalLines)
{
borderLine.Arrange(new Rect(x, 0, _borderThickness, finalSize.Height));
if (colIndex >= _columnWidths.Length)
{
break;
}
x += _borderThickness + _columnWidths[colIndex];
colIndex++;
}
}
// Arrange horizontal border elements.
{
int rowIndex = 0;
double y = 0;
foreach (var borderLine in HorizontalLines)
{
borderLine.Arrange(new Rect(0, y, finalSize.Width, _borderThickness));
if (rowIndex >= _rowHeights.Length)
{
break;
}
y += _borderThickness + _rowHeights[rowIndex];
rowIndex++;
}
}
return finalSize;
}
}

Просмотреть файл

@ -0,0 +1,42 @@
// 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.Extensions.TaskLists;
namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock.TextElements;
internal class MyTaskListCheckBox : IAddChild
{
private TaskList _taskList;
public TextElement TextElement { get; private set; }
public MyTaskListCheckBox(TaskList taskList)
{
_taskList = taskList;
var grid = new Grid();
CompositeTransform3D transform = new CompositeTransform3D();
transform.TranslateY = 2;
grid.Transform3D = transform;
grid.Width = 16;
grid.Height = 16;
grid.Margin = new Thickness(2, 0, 2, 0);
grid.BorderThickness = new Thickness(1);
grid.BorderBrush = new SolidColorBrush(Colors.Gray);
FontIcon icon = new FontIcon();
icon.FontSize = 16;
icon.HorizontalAlignment = HorizontalAlignment.Center;
icon.VerticalAlignment = VerticalAlignment.Center;
icon.Glyph = "\uE73E";
grid.Children.Add(taskList.Checked ? icon : new TextBlock());
grid.Padding = new Thickness(0);
grid.CornerRadius = new CornerRadius(2);
var inlineUIContainer = new InlineUIContainer();
inlineUIContainer.Child = grid;
TextElement = inlineUIContainer;
}
public void AddChild(IAddChild child)
{
}
}

Просмотреть файл

@ -0,0 +1,37 @@
// 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.TextElements;
internal class MyThematicBreak : IAddChild
{
private ThematicBreakBlock _thematicBreakBlock;
private Paragraph _paragraph;
public TextElement TextElement
{
get => _paragraph;
}
public MyThematicBreak(ThematicBreakBlock thematicBreakBlock)
{
_thematicBreakBlock = thematicBreakBlock;
_paragraph = new Paragraph();
var inlineUIContainer = new InlineUIContainer();
var border = new Border();
border.Width = 500;
border.BorderThickness = new Thickness(1);
border.Margin = new Thickness(0, 4, 0, 4);
border.BorderBrush = new SolidColorBrush(Colors.Gray);
border.Height = 1;
border.HorizontalAlignment = HorizontalAlignment.Stretch;
inlineUIContainer.Child = border;
_paragraph.Inlines.Add(inlineUIContainer);
}
public void AddChild(IAddChild child) {}
}

Просмотреть файл

@ -0,0 +1,9 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:CommunityToolkit.WinUI.Controls">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ms-appx:///CommunityToolkit.WinUI.Controls.MarkdownTextBlock/MarkdownTextBlock.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

Просмотреть файл

@ -0,0 +1,134 @@
// 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 CommunityToolkit.Tooling.TestGen;
using CommunityToolkit.Tests;
using CommunityToolkit.Labs.WinUI.MarkdownTextBlock;
namespace MarkdownTextBlockExperiment.Tests;
[TestClass]
public partial class ExampleMarkdownTextBlockTestClass : VisualUITestBase
{
// If you don't need access to UI objects directly or async code, use this pattern.
[TestMethod]
public void SimpleSynchronousExampleTest()
{
var assembly = typeof(MarkdownTextBlock).Assembly;
var type = assembly.GetType(typeof(MarkdownTextBlock).FullName ?? string.Empty);
Assert.IsNotNull(type, "Could not find MarkdownTextBlock type.");
Assert.AreEqual(typeof(MarkdownTextBlock), type, "Type of MarkdownTextBlock does not match expected type.");
}
// If you don't need access to UI objects directly, use this pattern.
[TestMethod]
public async Task SimpleAsyncExampleTest()
{
await Task.Delay(250);
Assert.IsTrue(true);
}
// Example that shows how to check for exception throwing.
[TestMethod]
public void SimpleExceptionCheckTest()
{
// If you need to check exceptions occur for invalid inputs, etc...
// Use Assert.ThrowsException to limit the scope to where you expect the error to occur.
// Otherwise, using the ExpectedException attribute could swallow or
// catch other issues in setup code.
Assert.ThrowsException<NotImplementedException>(() => throw new NotImplementedException());
}
// The UIThreadTestMethod automatically dispatches to the UI for us to work with UI objects.
[UIThreadTestMethod]
public void SimpleUIAttributeExampleTest()
{
var component = new MarkdownTextBlock();
Assert.IsNotNull(component);
}
// The UIThreadTestMethod can also easily grab a XAML Page for us by passing its type as a parameter.
// This lets us actually test a control as it would behave within an actual application.
// The page will already be loaded by the time your test is called.
[UIThreadTestMethod]
public void SimpleUIExamplePageTest(ExampleMarkdownTextBlockTestPage page)
{
// You can use the Toolkit Visual Tree helpers here to find the component by type or name:
var component = page.FindDescendant<MarkdownTextBlock>();
Assert.IsNotNull(component);
var componentByName = page.FindDescendant("MarkdownTextBlockControl");
Assert.IsNotNull(componentByName);
}
// You can still do async work with a UIThreadTestMethod as well.
[UIThreadTestMethod]
public async Task SimpleAsyncUIExamplePageTest(ExampleMarkdownTextBlockTestPage page)
{
// This helper can be used to wait for a rendering pass to complete.
// Note, this is already done by loading a Page with the [UIThreadTestMethod] helper.
await CompositionTargetHelper.ExecuteAfterCompositionRenderingAsync(() => { });
var component = page.FindDescendant<MarkdownTextBlock>();
Assert.IsNotNull(component);
}
//// ----------------------------- ADVANCED TEST SCENARIOS -----------------------------
// If you need to use DataRow, you can use this pattern with the UI dispatch still.
// Otherwise, checkout the UIThreadTestMethod attribute above.
// See https://github.com/CommunityToolkit/Labs-Windows/issues/186
[TestMethod]
public async Task ComplexAsyncUIExampleTest()
{
await EnqueueAsync(() =>
{
var component = new MarkdownTextBlock();
Assert.IsNotNull(component);
});
}
// If you want to load other content not within a XAML page using the UIThreadTestMethod above.
// Then you can do that using the Load/UnloadTestContentAsync methods.
[TestMethod]
public async Task ComplexAsyncLoadUIExampleTest()
{
await EnqueueAsync(async () =>
{
var component = new MarkdownTextBlock();
Assert.IsNotNull(component);
Assert.IsFalse(component.IsLoaded);
await LoadTestContentAsync(component);
Assert.IsTrue(component.IsLoaded);
await UnloadTestContentAsync(component);
Assert.IsFalse(component.IsLoaded);
});
}
// You can still use the UIThreadTestMethod to remove the extra layer for the dispatcher as well:
[UIThreadTestMethod]
public async Task ComplexAsyncLoadUIExampleWithoutDispatcherTest()
{
var component = new MarkdownTextBlock();
Assert.IsNotNull(component);
Assert.IsFalse(component.IsLoaded);
await LoadTestContentAsync(component);
Assert.IsTrue(component.IsLoaded);
await UnloadTestContentAsync(component);
Assert.IsFalse(component.IsLoaded);
}
}

Просмотреть файл

@ -0,0 +1,14 @@
<!-- 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. -->
<Page x:Class="MarkdownTextBlockExperiment.Tests.ExampleMarkdownTextBlockTestPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:CommunityToolkit.Labs.WinUI.MarkdownTextBlock"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
mc:Ignorable="d">
<Grid>
<controls:MarkdownTextBlock x:Name="MarkdownTextBlockControl" />
</Grid>
</Page>

Просмотреть файл

@ -0,0 +1,16 @@
// 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 MarkdownTextBlockExperiment.Tests;
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class ExampleMarkdownTextBlockTestPage : Page
{
public ExampleMarkdownTextBlockTestPage()
{
this.InitializeComponent();
}
}

Просмотреть файл

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<MSBuildAllProjects Condition="'$(MSBuildVersion)' == '' Or '$(MSBuildVersion)' &lt; '16.0'">$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
<HasSharedItems>true</HasSharedItems>
<SharedGUID>6F0FA793-7CF0-41F9-B7D9-260039B62C8A</SharedGUID>
</PropertyGroup>
<PropertyGroup Label="Configuration">
<Import_RootNamespace>MarkdownTextBlockExperiment.Tests</Import_RootNamespace>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)ExampleMarkdownTextBlockTestClass.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ExampleMarkdownTextBlockTestPage.xaml.cs">
<DependentUpon>ExampleMarkdownTextBlockTestPage.xaml</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<Page Include="$(MSBuildThisFileDirectory)ExampleMarkdownTextBlockTestPage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="Globals">
<ProjectGuid>6F0FA793-7CF0-41F9-B7D9-260039B62C8A</ProjectGuid>
<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
</PropertyGroup>
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.Default.props" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.props" />
<PropertyGroup />
<Import Project="MarkdownTextBlock.Tests.projitems" Label="Shared" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.CSharp.targets" />
</Project>

Просмотреть файл

@ -1,4 +1,5 @@
<Project Sdk="MSBuild.Sdk.Extras/3.0.23"> <Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))" Condition="Exists('$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))')" />
<PropertyGroup> <PropertyGroup>
<ToolkitComponentName>MarqueeText</ToolkitComponentName> <ToolkitComponentName>MarqueeText</ToolkitComponentName>
</PropertyGroup> </PropertyGroup>

Просмотреть файл

@ -1,4 +1,5 @@
<Project Sdk="MSBuild.Sdk.Extras/3.0.23"> <Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))" Condition="Exists('$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))')" />
<PropertyGroup> <PropertyGroup>
<ToolkitComponentName>MarqueeText</ToolkitComponentName> <ToolkitComponentName>MarqueeText</ToolkitComponentName>
<Description>This package contains MarqueeText.</Description> <Description>This package contains MarqueeText.</Description>

Просмотреть файл

@ -1,4 +1,5 @@
<Project Sdk="MSBuild.Sdk.Extras/3.0.23"> <Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))" Condition="Exists('$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))')" />
<PropertyGroup> <PropertyGroup>
<ToolkitComponentName>RivePlayer</ToolkitComponentName> <ToolkitComponentName>RivePlayer</ToolkitComponentName>
</PropertyGroup> </PropertyGroup>

Просмотреть файл

@ -1,4 +1,5 @@
<Project Sdk="MSBuild.Sdk.Extras/3.0.23"> <Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))" Condition="Exists('$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))')" />
<PropertyGroup> <PropertyGroup>
<ToolkitComponentName>RivePlayer</ToolkitComponentName> <ToolkitComponentName>RivePlayer</ToolkitComponentName>
<Description>This package contains RivePlayer.</Description> <Description>This package contains RivePlayer.</Description>

Просмотреть файл

@ -1,4 +1,5 @@
<Project Sdk="MSBuild.Sdk.Extras/3.0.23"> <Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))" Condition="Exists('$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))')" />
<PropertyGroup> <PropertyGroup>
<ToolkitComponentName>Shimmer</ToolkitComponentName> <ToolkitComponentName>Shimmer</ToolkitComponentName>
</PropertyGroup> </PropertyGroup>

Просмотреть файл

@ -1,4 +1,5 @@
<Project Sdk="MSBuild.Sdk.Extras/3.0.23"> <Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))" Condition="Exists('$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))')" />
<PropertyGroup> <PropertyGroup>
<ToolkitComponentName>Shimmer</ToolkitComponentName> <ToolkitComponentName>Shimmer</ToolkitComponentName>
<Description>This package contains Shimmer.</Description> <Description>This package contains Shimmer.</Description>

Просмотреть файл

@ -159,10 +159,10 @@ public partial class Shimmer : Control
{ {
case ElementTheme.Default: case ElementTheme.Default:
case ElementTheme.Dark: case ElementTheme.Dark:
_gradientStop1!.Color = Color.FromArgb((byte)(255 * 3.26 / 100), 255, 255, 255); _gradientStop1!.Color = Color.FromArgb((byte)(255 * 6.05 / 100), 255, 255, 255);
_gradientStop2!.Color = Color.FromArgb((byte)(255 * 6.05 / 100), 255, 255, 255); _gradientStop2!.Color = Color.FromArgb((byte)(255 * 3.26 / 100), 255, 255, 255);
_gradientStop3!.Color = Color.FromArgb((byte)(255 * 6.05 / 100), 255, 255, 255); _gradientStop3!.Color = Color.FromArgb((byte)(255 * 3.26 / 100), 255, 255, 255);
_gradientStop4!.Color = Color.FromArgb((byte)(255 * 3.26 / 100), 255, 255, 255); _gradientStop4!.Color = Color.FromArgb((byte)(255 * 6.05 / 100), 255, 255, 255);
break; break;
case ElementTheme.Light: case ElementTheme.Light:
_gradientStop1!.Color = Color.FromArgb((byte)(255 * 5.37 / 100), 0, 0, 0); _gradientStop1!.Color = Color.FromArgb((byte)(255 * 5.37 / 100), 0, 0, 0);

Просмотреть файл

@ -1,4 +1,5 @@
<Project Sdk="MSBuild.Sdk.Extras/3.0.23"> <Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))" Condition="Exists('$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))')" />
<PropertyGroup> <PropertyGroup>
<ToolkitComponentName>TokenView</ToolkitComponentName> <ToolkitComponentName>TokenView</ToolkitComponentName>
</PropertyGroup> </PropertyGroup>

Просмотреть файл

@ -1,4 +1,5 @@
<Project Sdk="MSBuild.Sdk.Extras/3.0.23"> <Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))" Condition="Exists('$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))')" />
<PropertyGroup> <PropertyGroup>
<ToolkitComponentName>TokenView</ToolkitComponentName> <ToolkitComponentName>TokenView</ToolkitComponentName>
<Description>This package contains TokenView.</Description> <Description>This package contains TokenView.</Description>

Просмотреть файл

@ -1,4 +1,5 @@
<Project Sdk="MSBuild.Sdk.Extras/3.0.23"> <Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))" Condition="Exists('$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))')" />
<PropertyGroup> <PropertyGroup>
<ToolkitComponentName>TransitionHelper</ToolkitComponentName> <ToolkitComponentName>TransitionHelper</ToolkitComponentName>
</PropertyGroup> </PropertyGroup>

Просмотреть файл

@ -1,4 +1,5 @@
<Project Sdk="MSBuild.Sdk.Extras/3.0.23"> <Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))" Condition="Exists('$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))')" />
<PropertyGroup> <PropertyGroup>
<ToolkitComponentName>TransitionHelper</ToolkitComponentName> <ToolkitComponentName>TransitionHelper</ToolkitComponentName>
<Description>This package contains a TransitionHelper.</Description> <Description>This package contains a TransitionHelper.</Description>

Просмотреть файл

@ -1,6 +1,6 @@
{ {
"sdk": { "sdk": {
"version": "7.0.402", "version": "8.0.201",
"rollForward": "latestFeature" "rollForward": "latestFeature"
}, },
"msbuild-sdks": "msbuild-sdks":

@ -1 +1 @@
Subproject commit 5a91d827a090b79db5ac1fea190250384ac0ebad Subproject commit 3be6991a048b8f1a2656e7e7e4ec2f2cb9b08a70