From d79c3b470e11a8ee2c235a19999a6fd8f725b872 Mon Sep 17 00:00:00 2001 From: Shankar Date: Tue, 19 Nov 2024 02:05:11 +0530 Subject: [PATCH 1/5] Improvements to MarkdownTextBlock - Added support for colored code syntax rendering: ColorCode.Core dep, Cascadia Code font. - Document Outline: headers are supported for now and they can be navigated. - Styling update: now majority of the rendered items are theme aware and can be styled. - Tables: better rendering with support for themes and corner radius. - Quotes: all stylings for Alerts have been added. --- Directory.Packages.props | 3 +- .../EditorPages/MarkdownEditorPage.xaml | 14 +- .../EditorPages/MarkdownEditorPage.xaml.cs | 62 ++++- .../ColorCode/RichTextBlockFormatter.cs | 178 ++++++++++++++ .../Converters/AlertKindToStringConverter.cs | 16 ++ .../AlertKindToVisibilityConverter.cs | 20 ++ src/Symptum.UI/Fonts/CascadiaCode-Regular.ttf | Bin 0 -> 600344 bytes src/Symptum.UI/Markdown/DefaultLinkHandler.cs | 29 +++ src/Symptum.UI/Markdown/DefaultSVGRenderer.cs | 23 +- src/Symptum.UI/Markdown/DocumentOutline.cs | 75 ++++++ src/Symptum.UI/Markdown/Extensions.cs | 67 +++--- src/Symptum.UI/Markdown/IImageProvider.cs | 8 + src/Symptum.UI/Markdown/ILinkHandler.cs | 6 + src/Symptum.UI/Markdown/ISVGRenderer.cs | 6 +- src/Symptum.UI/Markdown/ImageProvider.cs | 12 - src/Symptum.UI/Markdown/MarkdownConfig.cs | 18 -- .../Markdown/MarkdownConfiguration.cs | 26 +++ .../Markdown/MarkdownTextBlock.xaml | 8 +- .../Markdown/MarkdownTextBlock.xaml.cs | 74 +++--- src/Symptum.UI/Markdown/MarkdownThemes.cs | 118 +++++----- src/Symptum.UI/Markdown/QuoteControl.xaml | 135 +++++++++++ src/Symptum.UI/Markdown/QuoteControl.xaml.cs | 81 +++++++ .../ObjectRenderers/CodeBlockRenderer.cs | 8 +- .../Extensions/TableRenderer.cs | 12 +- .../Extensions/TaskListRenderer.cs | 8 +- .../ObjectRenderers/HeadingRenderer.cs | 8 +- .../Inlines/AutoLinkInlineRenderer.cs | 8 +- .../Inlines/CodeInlineRenderer.cs | 8 +- .../Inlines/ContainerInlineRenderer.cs | 10 +- .../Inlines/DelimiterInlineRenderer.cs | 6 +- .../Inlines/EmphasisInlineRenderer.cs | 14 +- .../Inlines/HtmlEntityInlineRenderer.cs | 10 +- .../Inlines/LineBreakInlineRenderer.cs | 8 +- .../Inlines/LinkInlineRenderer.cs | 12 +- .../Inlines/LiteralInlineRenderer.cs | 6 +- .../Renderers/ObjectRenderers/ListRenderer.cs | 10 +- .../ObjectRenderers/ParagraphRenderer.cs | 8 +- .../ObjectRenderers/QuoteBlockRenderer.cs | 8 +- .../ObjectRenderers/ThematicBreakRenderer.cs | 8 +- .../Markdown/Renderers/WinUIObjectRenderer.cs | 4 - .../Markdown/Renderers/WinUIRenderer.cs | 24 +- ...linkInline.cs => AutolinkInlineElement.cs} | 12 +- .../TextElements/BlockContainerElement.cs | 27 +++ .../Markdown/TextElements/CodeBlockElement.cs | 81 +++++++ .../TextElements/CodeInlineElement.cs | 34 +++ ...asisInline.cs => EmphasisInlineElement.cs} | 23 +- .../TextElements/FlowDocumentElement.cs | 60 +++++ .../Markdown/TextElements/HeadingElement.cs | 62 +++++ ...inkButton.cs => HyperlinkButtonElement.cs} | 17 +- .../{MyHyperlink.cs => HyperlinkElement.cs} | 24 +- .../Markdown/TextElements/IAddChild.cs | 4 - .../{MyImage.cs => ImageElement.cs} | 36 ++- .../Markdown/TextElements/LineBreakElement.cs | 23 ++ .../{MyList.cs => ListElement.cs} | 15 +- .../Markdown/TextElements/MyBlockContainer.cs | 34 --- .../Markdown/TextElements/MyCodeBlock.cs | 88 ------- .../Markdown/TextElements/MyFlowDocument.cs | 59 ----- .../Markdown/TextElements/MyHeading.cs | 63 ----- .../Markdown/TextElements/MyInlineCode.cs | 47 ---- .../Markdown/TextElements/MyLineBreak.cs | 32 --- .../Markdown/TextElements/MyParagraph.cs | 38 --- .../Markdown/TextElements/MyQuote.cs | 82 ------- .../Markdown/TextElements/MyTable.cs | 52 ----- .../Markdown/TextElements/MyTableCell.cs | 86 ------- .../Markdown/TextElements/MyTableRow.cs | 39 ---- .../Markdown/TextElements/MyTableUIElement.cs | 217 ------------------ .../Markdown/TextElements/MyThematicBreak.cs | 36 --- .../Markdown/TextElements/ParagraphElement.cs | 27 +++ .../Markdown/TextElements/QuoteElement.cs | 63 +++++ .../Markdown/TextElements/STextElements.cs | 52 +++-- .../Markdown/TextElements/TableCellElement.cs | 72 ++++++ .../Markdown/TextElements/TableElement.cs | 85 +++++++ ...CheckBox.cs => TaskListCheckBoxElement.cs} | 21 +- .../{MyInlineText.cs => TextInlineElement.cs} | 8 +- .../TextElements/ThematicBreakElement.cs | 27 +++ src/Symptum.UI/Symptum.UI.csproj | 15 +- src/Symptum.UI/Themes/Generic.xaml | 7 +- src/Symptum.UI/Themes/Styles.xaml | 128 +++++++---- 78 files changed, 1590 insertions(+), 1365 deletions(-) create mode 100644 src/Symptum.UI/ColorCode/RichTextBlockFormatter.cs create mode 100644 src/Symptum.UI/Converters/AlertKindToStringConverter.cs create mode 100644 src/Symptum.UI/Converters/AlertKindToVisibilityConverter.cs create mode 100644 src/Symptum.UI/Fonts/CascadiaCode-Regular.ttf create mode 100644 src/Symptum.UI/Markdown/DefaultLinkHandler.cs create mode 100644 src/Symptum.UI/Markdown/DocumentOutline.cs create mode 100644 src/Symptum.UI/Markdown/IImageProvider.cs create mode 100644 src/Symptum.UI/Markdown/ILinkHandler.cs delete mode 100644 src/Symptum.UI/Markdown/ImageProvider.cs delete mode 100644 src/Symptum.UI/Markdown/MarkdownConfig.cs create mode 100644 src/Symptum.UI/Markdown/MarkdownConfiguration.cs create mode 100644 src/Symptum.UI/Markdown/QuoteControl.xaml create mode 100644 src/Symptum.UI/Markdown/QuoteControl.xaml.cs rename src/Symptum.UI/Markdown/TextElements/{MyAutolinkInline.cs => AutolinkInlineElement.cs} (60%) create mode 100644 src/Symptum.UI/Markdown/TextElements/BlockContainerElement.cs create mode 100644 src/Symptum.UI/Markdown/TextElements/CodeBlockElement.cs create mode 100644 src/Symptum.UI/Markdown/TextElements/CodeInlineElement.cs rename src/Symptum.UI/Markdown/TextElements/{MyEmphasisInline.cs => EmphasisInlineElement.cs} (75%) create mode 100644 src/Symptum.UI/Markdown/TextElements/FlowDocumentElement.cs create mode 100644 src/Symptum.UI/Markdown/TextElements/HeadingElement.cs rename src/Symptum.UI/Markdown/TextElements/{MyHyperlinkButton.cs => HyperlinkButtonElement.cs} (67%) rename src/Symptum.UI/Markdown/TextElements/{MyHyperlink.cs => HyperlinkElement.cs} (64%) rename src/Symptum.UI/Markdown/TextElements/{MyImage.cs => ImageElement.cs} (71%) create mode 100644 src/Symptum.UI/Markdown/TextElements/LineBreakElement.cs rename src/Symptum.UI/Markdown/TextElements/{MyList.cs => ListElement.cs} (86%) delete mode 100644 src/Symptum.UI/Markdown/TextElements/MyBlockContainer.cs delete mode 100644 src/Symptum.UI/Markdown/TextElements/MyCodeBlock.cs delete mode 100644 src/Symptum.UI/Markdown/TextElements/MyFlowDocument.cs delete mode 100644 src/Symptum.UI/Markdown/TextElements/MyHeading.cs delete mode 100644 src/Symptum.UI/Markdown/TextElements/MyInlineCode.cs delete mode 100644 src/Symptum.UI/Markdown/TextElements/MyLineBreak.cs delete mode 100644 src/Symptum.UI/Markdown/TextElements/MyParagraph.cs delete mode 100644 src/Symptum.UI/Markdown/TextElements/MyQuote.cs delete mode 100644 src/Symptum.UI/Markdown/TextElements/MyTable.cs delete mode 100644 src/Symptum.UI/Markdown/TextElements/MyTableCell.cs delete mode 100644 src/Symptum.UI/Markdown/TextElements/MyTableRow.cs delete mode 100644 src/Symptum.UI/Markdown/TextElements/MyTableUIElement.cs delete mode 100644 src/Symptum.UI/Markdown/TextElements/MyThematicBreak.cs create mode 100644 src/Symptum.UI/Markdown/TextElements/ParagraphElement.cs create mode 100644 src/Symptum.UI/Markdown/TextElements/QuoteElement.cs create mode 100644 src/Symptum.UI/Markdown/TextElements/TableCellElement.cs create mode 100644 src/Symptum.UI/Markdown/TextElements/TableElement.cs rename src/Symptum.UI/Markdown/TextElements/{MyTaskListCheckBox.cs => TaskListCheckBoxElement.cs} (56%) rename src/Symptum.UI/Markdown/TextElements/{MyInlineText.cs => TextInlineElement.cs} (57%) create mode 100644 src/Symptum.UI/Markdown/TextElements/ThematicBreakElement.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index 7d72f1e..0b93af8 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -6,6 +6,7 @@ --> + @@ -21,4 +22,4 @@ - \ No newline at end of file + diff --git a/src/Symptum.Editor/EditorPages/MarkdownEditorPage.xaml b/src/Symptum.Editor/EditorPages/MarkdownEditorPage.xaml index a9df098..1e7a041 100644 --- a/src/Symptum.Editor/EditorPages/MarkdownEditorPage.xaml +++ b/src/Symptum.Editor/EditorPages/MarkdownEditorPage.xaml @@ -8,6 +8,10 @@ xmlns:md="using:Symptum.UI.Markdown" mc:Ignorable="d"> + + + + @@ -15,6 +19,14 @@ - + + + + + + + + diff --git a/src/Symptum.Editor/EditorPages/MarkdownEditorPage.xaml.cs b/src/Symptum.Editor/EditorPages/MarkdownEditorPage.xaml.cs index 654e5e3..aaeea45 100644 --- a/src/Symptum.Editor/EditorPages/MarkdownEditorPage.xaml.cs +++ b/src/Symptum.Editor/EditorPages/MarkdownEditorPage.xaml.cs @@ -1,17 +1,21 @@ +using Symptum.UI.Markdown; + namespace Symptum.Editor.EditorPages; public sealed partial class MarkdownEditorPage : EditorPageBase { public MarkdownEditorPage() { + mrkd = string.Join("\r\n", quotesMD, tabledMd); InitializeComponent(); } - private string mrkd = @"Hello **bold** `code` Hello **bold** ***italic***`code 2` hello **bold**`code 3` hello **bold**`code 4` + private string mrkd; + private string quotesMD = @"# Quotes -`code 5` Hello **bold** ***italic*** `code 6` `code 7` Hello **bold** ***italic*** +> Text that is a quote -# ALERT +## Alerts > [!NOTE] > Useful information that users should know, even when skimming content. @@ -26,6 +30,56 @@ public MarkdownEditorPage() > Urgent info that needs immediate user attention to avoid problems. > [!CAUTION] -> Advises about risks or negative outcomes of certain actions."; +> Advises about risks or negative outcomes of certain actions. +"; + private string tabledMd = @"# Tables + +| abc | def | ghi | +|:---:|-----|----:| +| 1 | 2 | 3 | + ++---------+---------+ +| Header | Header | +| Column1 | Column2 | ++=========+=========+ +| 1. ab | > This is a quote +| 2. cde | > For the second column +| 3. f | ++---------+---------+ +| Second row spanning +| on two columns ++---------+---------+ +| Back | | +| to | | +| one | | +| column | | + ++---------+---------+ +| This is | a table | ++=========+=========+ ++---+---+---+ +| AAAAA | B | ++ AAAAA +---+ +| AAAAA | C | ++---+---+---+ +| D | E | F | ++---+---+---+ + ++---+---+---+ +| AAAAA | B | ++---+---+ B + +| D | E | B | ++ D +---+---+ +| D | CCCCC | ++---+---+---+ +"; + + private void TreeView_ItemInvoked(TreeView sender, TreeViewItemInvokedEventArgs args) + { + if (args.InvokedItem is DocumentNode node && node.Navigate is Action navigate) + { + navigate(); + } + } } diff --git a/src/Symptum.UI/ColorCode/RichTextBlockFormatter.cs b/src/Symptum.UI/ColorCode/RichTextBlockFormatter.cs new file mode 100644 index 0000000..bcead99 --- /dev/null +++ b/src/Symptum.UI/ColorCode/RichTextBlockFormatter.cs @@ -0,0 +1,178 @@ +using ColorCode.Parsing; +using ColorCode.Styling; +using Windows.UI.Text; +using ColorCode.Common; +using Microsoft.UI.Text; +using Microsoft.UI.Xaml.Documents; + +namespace ColorCode.Uno; + +/// +/// Creates a , for rendering Syntax Highlighted code to a RichTextBlock. +/// +public class RichTextBlockFormatter : CodeColorizerBase +{ + /// + /// Creates a , for rendering Syntax Highlighted code to a RichTextBlock. + /// + /// The Theme to use, determines whether to use Default Light or Default Dark. + public RichTextBlockFormatter(ElementTheme Theme, ILanguageParser languageParser = null) : this(Theme == ElementTheme.Dark ? StyleDictionary.DefaultDark : StyleDictionary.DefaultLight, languageParser) + { + } + + /// + /// Creates a , for rendering Syntax Highlighted code to a RichTextBlock. + /// + /// The Custom styles to Apply to the formatted Code. + /// The language parser that the instance will use for its lifetime. + public RichTextBlockFormatter(StyleDictionary Style = null, ILanguageParser languageParser = null) : base(Style, languageParser) + { + } + + /// + /// Adds Syntax Highlighted Source Code to the provided RichTextBlock. + /// + /// The source code to colorize. + /// The language to use to colorize the source code. + /// The Control to add the Text to. + public void FormatRichTextBlock(string sourceCode, ILanguage Language, RichTextBlock RichText) + { + var paragraph = new Paragraph(); + RichText.Blocks.Add(paragraph); + FormatInlines(sourceCode, Language, paragraph.Inlines); + } + + /// + /// Adds Syntax Highlighted Source Code to the provided InlineCollection. + /// + /// The source code to colorize. + /// The language to use to colorize the source code. + /// InlineCollection to add the Text to. + public void FormatInlines(string sourceCode, ILanguage Language, InlineCollection InlineCollection) + { + this.InlineCollection = InlineCollection; + languageParser.Parse(sourceCode, Language, (parsedSourceCode, captures) => Write(parsedSourceCode, captures)); + } + + private InlineCollection InlineCollection { get; set; } + + protected override void Write(string parsedSourceCode, IList scopes) + { + var styleInsertions = new List(); + + foreach (Scope scope in scopes) + GetStyleInsertionsForCapturedStyle(scope, styleInsertions); + + styleInsertions.SortStable((x, y) => x.Index.CompareTo(y.Index)); + + int offset = 0; + + Scope PreviousScope = null; + + foreach (var styleinsertion in styleInsertions) + { + var text = parsedSourceCode.Substring(offset, styleinsertion.Index - offset); + CreateSpan(text, PreviousScope); + if (!string.IsNullOrWhiteSpace(styleinsertion.Text)) + { + CreateSpan(text, PreviousScope); + } + offset = styleinsertion.Index; + + PreviousScope = styleinsertion.Scope; + } + + var remaining = parsedSourceCode.Substring(offset); + // Ensures that those loose carriages don't run away! + if (remaining != "\r") + { + CreateSpan(remaining, null); + } + } + + private void CreateSpan(string Text, Scope scope) + { + var span = new Span(); + var run = new Run + { + Text = Text + }; + + // Styles and writes the text to the span. + if (scope != null) StyleRun(run, scope); + span.Inlines.Add(run); + + InlineCollection.Add(span); + } + + private void StyleRun(Run Run, Scope Scope) + { + string? foreground = null; + string? background = null; + bool italic = false; + bool bold = false; + + if (Styles.Contains(Scope.Name)) + { + Styling.Style style = Styles[Scope.Name]; + + foreground = style.Foreground; + background = style.Background; + italic = style.Italic; + bold = style.Bold; + } + + if (!string.IsNullOrWhiteSpace(foreground)) + Run.Foreground = foreground.GetSolidColorBrush(); + + //Background isn't supported, but a workaround could be created. + + if (italic) + Run.FontStyle = FontStyle.Italic; + + if (bold) + Run.FontWeight = FontWeights.Bold; + } + + private void GetStyleInsertionsForCapturedStyle(Scope scope, ICollection styleInsertions) + { + styleInsertions.Add(new TextInsertion + { + Index = scope.Index, + Scope = scope + }); + + foreach (Scope childScope in scope.Children) + GetStyleInsertionsForCapturedStyle(childScope, styleInsertions); + + styleInsertions.Add(new TextInsertion + { + Index = scope.Index + scope.Length + }); + } +} + +public static class ExtensionMethods +{ + public static SolidColorBrush GetSolidColorBrush(this string hex) + { + hex = hex.Replace("#", string.Empty); + + byte a = 255; + int index = 0; + + if (hex.Length == 8) + { + a = (byte)(Convert.ToUInt32(hex.Substring(index, 2), 16)); + index += 2; + } + + byte r = (byte)(Convert.ToUInt32(hex.Substring(index, 2), 16)); + index += 2; + byte g = (byte)(Convert.ToUInt32(hex.Substring(index, 2), 16)); + index += 2; + byte b = (byte)(Convert.ToUInt32(hex.Substring(index, 2), 16)); + SolidColorBrush myBrush = new SolidColorBrush(Windows.UI.Color.FromArgb(a, r, g, b)); + return myBrush; + } +} diff --git a/src/Symptum.UI/Converters/AlertKindToStringConverter.cs b/src/Symptum.UI/Converters/AlertKindToStringConverter.cs new file mode 100644 index 0000000..73c663d --- /dev/null +++ b/src/Symptum.UI/Converters/AlertKindToStringConverter.cs @@ -0,0 +1,16 @@ +using Microsoft.UI.Xaml.Data; + +namespace Symptum.UI.Converters; + +public class AlertKindToStringConverter : IValueConverter +{ + public object Convert(object value, Type targetType, object parameter, string language) + { + return value?.ToString() ?? string.Empty; + } + + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + throw new NotImplementedException(); + } +} diff --git a/src/Symptum.UI/Converters/AlertKindToVisibilityConverter.cs b/src/Symptum.UI/Converters/AlertKindToVisibilityConverter.cs new file mode 100644 index 0000000..a093eae --- /dev/null +++ b/src/Symptum.UI/Converters/AlertKindToVisibilityConverter.cs @@ -0,0 +1,20 @@ +using Microsoft.UI.Xaml.Data; +using Symptum.UI.Markdown; + +namespace Symptum.UI.Converters; + +public class AlertKindToVisibilityConverter : IValueConverter +{ + public object Convert(object value, Type targetType, object parameter, string language) + { + return value is AlertKind kind + ? kind switch + { AlertKind.None => Visibility.Collapsed, _ => Visibility.Visible } + : Visibility.Collapsed; + } + + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + throw new NotImplementedException(); + } +} diff --git a/src/Symptum.UI/Fonts/CascadiaCode-Regular.ttf b/src/Symptum.UI/Fonts/CascadiaCode-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..6da3f5bc648c4e9ee128f225be429b50fade3c68 GIT binary patch literal 600344 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 @!&RI=biRB9qm(U=g%@Ge*Qm8 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

RrSd#GuXXFwO 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

c@IHz$F7 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 SvgToImage(string svgString) + public async Task SvgToImageSource(string svgString) { SvgImageSource svgImageSource = new(); - Image image = new(); // Create a MemoryStream object and write the SVG string to it using (MemoryStream memoryStream = new()) using (StreamWriter streamWriter = new(memoryStream)) @@ -25,18 +20,6 @@ public async Task SvgToImage(string svgString) // 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; - Windows.Foundation.Size size = Extensions.GetSvgSize(svgString); - if (size.Width != 0) - { - image.Width = size.Width; - } - if (size.Height != 0) - { - image.Height = size.Height; - } - return image; + return svgImageSource; } } diff --git a/src/Symptum.UI/Markdown/DocumentOutline.cs b/src/Symptum.UI/Markdown/DocumentOutline.cs new file mode 100644 index 0000000..185a1c3 --- /dev/null +++ b/src/Symptum.UI/Markdown/DocumentOutline.cs @@ -0,0 +1,75 @@ +using System.Collections.ObjectModel; + +namespace Symptum.UI.Markdown; + +public class DocumentOutline +{ + private Stack _nodeStack = new(); + + public ObservableCollection Nodes { get; private set; } = []; + + public Dictionary IdNavigateCollection { get; } = []; + + public void PushNode(DocumentNode node) + { + if (_nodeStack.TryPeek(out DocumentNode? _node)) // Gets the last node. + { + // Checks if the last node is higher in level than the new one (lower number => higher level) + if (_node.Level < node.Level && _node.Level < DocumentLevel.Heading6) + { + _node.Children.Add(node); // If so add it as a child node. + _nodeStack.Push(node); // Put this as the last node. + IdNavigateCollection.TryAdd(node.Id, node.Navigate); + } + else + { + _nodeStack.Pop(); // Removes the node because it's lower in level than the new one. + PushNode(node); // Recursively check for a best suit parent node and add to it. + } + } + else + { + // Add the node to stack if it's empty + _nodeStack.Push(node); + Nodes.Add(node); + IdNavigateCollection.TryAdd(node.Id, node.Navigate); + } + } + + public void Clear() + { + Nodes.Clear(); + _nodeStack.Clear(); + IdNavigateCollection.Clear(); + } +} + +public class DocumentNode +{ + public string? Title { get; set; } + + public string? Id { get; set; } + + public DocumentLevel Level { get; set; } + + public ObservableCollection Children { get; private set; } = []; + + public Action? Navigate { get; set; } + + public override string ToString() + { + return $"{Title} {{#{Id}}} ({Level})"; + } +} + +public enum DocumentLevel +{ + Heading1 = 1, + Heading2, + Heading3, + Heading4, + Heading5, + Heading6, + Quote, + Table, +} diff --git a/src/Symptum.UI/Markdown/Extensions.cs b/src/Symptum.UI/Markdown/Extensions.cs index 956faec..59bc86d 100644 --- a/src/Symptum.UI/Markdown/Extensions.cs +++ b/src/Symptum.UI/Markdown/Extensions.cs @@ -1,10 +1,3 @@ -// 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.Inlines; using System.Xml.Linq; using System.Globalization; @@ -13,38 +6,44 @@ using System.Text.RegularExpressions; using Windows.Foundation; using System.Text; - -/* Unmerged change from project 'Symptum.UI (net9.0-maccatalyst)' -Added: -using CommunityToolkit; -using CommunityToolkit.Labs; -using CommunityToolkit.Labs.WinUI; -using Symptum.UI.Markdown; -using Symptum.UI.Markdown; -*/ +using ColorCode; +using Markdig.Syntax; namespace Symptum.UI.Markdown; public static class Extensions { - 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) + { + return (fencedCodeBlock.Info?.ToLower()) switch + { + "aspx" => Languages.Aspx, + "aspx - vb" => Languages.AspxVb, + "asax" => Languages.Asax, + "ascx" => Languages.AspxCs, + "ashx" or "asmx" or "axd" => Languages.Ashx, + "cs" or "csharp" or "c#" => Languages.CSharp, + "xhtml" or "html" or "hta" or "htm" or "html.hl" or "inc" or "xht" => Languages.Html, + "java" or "jav" or "jsh" => Languages.Java, + "js" or "node" or "_js" or "bones" or "cjs" or "es" or "es6" or "frag" or "gs" or "jake" or "javascript" or "jsb" or "jscad" or "jsfl" or "jslib" or "jsm" or "jspre" or "jss" or "jsx" or "mjs" or "njs" or "pac" or "sjs" or "ssjs" or "xsjs" or "xsjslib" => Languages.JavaScript, + "posh" or "pwsh" or "ps1" or "psd1" or "psm1" => Languages.PowerShell, + "sql" or "cql" or "ddl" or "mysql" or "prc" or "tab" or "udf" or "viw" => Languages.Sql, + "vb" or "vbhtml" or "visual basic" or "vbnet" or "vb .net" or "vb.net" => Languages.VbDotNet, + "rss" or "xsd" or "wsdl" or "xml" or "adml" or "admx" or "ant" or "axaml" or "axml" or "builds" or "ccproj" or "ccxml" or "clixml" or "cproject" or "cscfg" or "csdef" or "csl" or "csproj" or "ct" or "depproj" or "dita" or "ditamap" or "ditaval" or "dll.config" or "dotsettings" or "filters" or "fsproj" or "fxml" or "glade" or "gml" or "gmx" or "grxml" or "gst" or "hzp" or "iml" or "ivy" or "jelly" or "jsproj" or "kml" or "launch" or "mdpolicy" or "mjml" or "mm" or "mod" or "mxml" or "natvis" or "ncl" or "ndproj" or "nproj" or "nuspec" or "odd" or "osm" or "pkgproj" or "pluginspec" or "proj" or "props" or "ps1xml" or "psc1" or "pt" or "qhelp" or "rdf" or "res" or "resx" or "rs" or "sch" or "scxml" or "sfproj" or "shproj" or "srdf" or "storyboard" or "sublime-snippet" or "sw" or "targets" or "tml" or "ui" or "urdf" or "ux" or "vbproj" or "vcxproj" or "vsixmanifest" or "vssettings" or "vstemplate" or "vxml" or "wixproj" or "workflow" or "wsf" or "wxi" or "wxl" or "wxs" or "x3d" or "xacro" or "xaml" or "xib" or "xlf" or "xliff" or "xmi" or "xml.dist" or "xmp" or "xproj" or "xspec" or "xul" or "zcml" => Languages.Xml, + "php" or "aw" or "ctp" or "fcgi" or "php3" or "php4" or "php5" or "phps" or "phpt" => Languages.Php, + "css" or "scss" or "less" => Languages.Css, + "cpp" or "c++" or "cc" or "cp" or "cxx" or "h" or "h++" or "hh" or "hpp" or "hxx" or "inl" or "ino" or "ipp" or "ixx" or "re" or "tcc" or "tpp" => Languages.Cpp, + "ts" or "tsx" or "cts" or "mts" => Languages.Typescript, + "fsharp" or "fs" or "fsi" or "fsx" => Languages.FSharp, + "koka" => Languages.Koka, + "hs" or "hs-boot" or "hsc" => Languages.Haskell, + "pandoc" or "md" or "livemd" or "markdown" or "mdown" or "mdwn" or "mdx" or "mkd" or "mkdn" or "mkdown" or "ronn" or "scd" or "workbook" => Languages.Markdown, + "fortran" or "f" or "f77" or "for" or "fpp" => Languages.Fortran, + "python" or "py" or "cgi" or "gyp" or "gypi" or "lmi" or "py3" or "pyde" or "pyi" or "pyp" or "pyt" or "pyw" or "rpy" or "smk" or "spec" or "tac" or "wsgi" or "xpy" => Languages.Python, + "matlab" or "m" => Languages.MATLAB, + _ => Languages.JavaScript, + }; + } public static string ToAlphabetical(this int index) { diff --git a/src/Symptum.UI/Markdown/IImageProvider.cs b/src/Symptum.UI/Markdown/IImageProvider.cs new file mode 100644 index 0000000..fc4a549 --- /dev/null +++ b/src/Symptum.UI/Markdown/IImageProvider.cs @@ -0,0 +1,8 @@ +namespace Symptum.UI.Markdown; + +public interface IImageProvider +{ + Task GetImageSource(string url); + + bool ShouldUseThisProvider(string url); +} diff --git a/src/Symptum.UI/Markdown/ILinkHandler.cs b/src/Symptum.UI/Markdown/ILinkHandler.cs new file mode 100644 index 0000000..1f95460 --- /dev/null +++ b/src/Symptum.UI/Markdown/ILinkHandler.cs @@ -0,0 +1,6 @@ +namespace Symptum.UI.Markdown; + +public interface ILinkHandler +{ + public void HandleNavigation(string? url, string? baseUrl); +} diff --git a/src/Symptum.UI/Markdown/ISVGRenderer.cs b/src/Symptum.UI/Markdown/ISVGRenderer.cs index b9f0d48..5fd6277 100644 --- a/src/Symptum.UI/Markdown/ISVGRenderer.cs +++ b/src/Symptum.UI/Markdown/ISVGRenderer.cs @@ -1,10 +1,6 @@ -// 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 Symptum.UI.Markdown; public interface ISVGRenderer { - Task SvgToImage(string svgString); + Task SvgToImageSource(string svgString); } diff --git a/src/Symptum.UI/Markdown/ImageProvider.cs b/src/Symptum.UI/Markdown/ImageProvider.cs deleted file mode 100644 index e80e593..0000000 --- a/src/Symptum.UI/Markdown/ImageProvider.cs +++ /dev/null @@ -1,12 +0,0 @@ -// 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 Symptum.UI.Markdown; - -public interface IImageProvider -{ - Task GetImage(string url); - - bool ShouldUseThisProvider(string url); -} diff --git a/src/Symptum.UI/Markdown/MarkdownConfig.cs b/src/Symptum.UI/Markdown/MarkdownConfig.cs deleted file mode 100644 index 4191479..0000000 --- a/src/Symptum.UI/Markdown/MarkdownConfig.cs +++ /dev/null @@ -1,18 +0,0 @@ -// 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 Symptum.UI.Markdown; - -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(); -} diff --git a/src/Symptum.UI/Markdown/MarkdownConfiguration.cs b/src/Symptum.UI/Markdown/MarkdownConfiguration.cs new file mode 100644 index 0000000..915951d --- /dev/null +++ b/src/Symptum.UI/Markdown/MarkdownConfiguration.cs @@ -0,0 +1,26 @@ +namespace Symptum.UI.Markdown; + +public record MarkdownConfiguration +{ + public string? BaseUrl { get; set; } + + public DocumentOutline DocumentOutline { get; private set; } + + public IImageProvider? ImageProvider { get; set; } + + public ILinkHandler? LinkHandler { get; set; } + + public ISVGRenderer? SVGRenderer { get; set; } + + public MarkdownThemes Themes { get; set; } + + public static MarkdownConfiguration Default = new(); + + public MarkdownConfiguration() + { + DocumentOutline = new(); + LinkHandler = new DefaultLinkHandler(DocumentOutline); + SVGRenderer = new DefaultSVGRenderer(); + Themes = MarkdownThemes.Default; + } +} diff --git a/src/Symptum.UI/Markdown/MarkdownTextBlock.xaml b/src/Symptum.UI/Markdown/MarkdownTextBlock.xaml index dcc51f4..43bea33 100644 --- a/src/Symptum.UI/Markdown/MarkdownTextBlock.xaml +++ b/src/Symptum.UI/Markdown/MarkdownTextBlock.xaml @@ -1,15 +1,15 @@  + xmlns:md="using:Symptum.UI.Markdown"> + + + + + + + + + + + + + + diff --git a/src/Symptum.UI/Markdown/QuoteControl.xaml.cs b/src/Symptum.UI/Markdown/QuoteControl.xaml.cs new file mode 100644 index 0000000..4b62961 --- /dev/null +++ b/src/Symptum.UI/Markdown/QuoteControl.xaml.cs @@ -0,0 +1,81 @@ +namespace Symptum.UI.Markdown; + +[TemplatePart(Name = QuoteContainerName, Type = typeof(ContentControl))] +public sealed partial class QuoteControl : Control +{ + private const string QuoteContainerName = "QuoteContainer"; + private ContentControl? _container; + + #region Properties + + #region Alert Kind + + public static readonly DependencyProperty KindProperty = DependencyProperty.Register( + nameof(Kind), + typeof(AlertKind), + typeof(QuoteControl), + new PropertyMetadata(AlertKind.None)); + + public AlertKind Kind + { + get => (AlertKind)GetValue(KindProperty); + set => SetValue(KindProperty, value); + } + + #endregion + + #region Alert Foreground + + public static readonly DependencyProperty AlertForegroundProperty = DependencyProperty.Register( + nameof(AlertForeground), + typeof(Brush), + typeof(QuoteControl), + new PropertyMetadata(null)); + + public Brush AlertForeground + { + get => (Brush)GetValue(AlertForegroundProperty); + set => SetValue(AlertForegroundProperty, value); + } + + #endregion + + #region Content + + public static readonly DependencyProperty ContentProperty = DependencyProperty.Register( + nameof(Content), + typeof(object), + typeof(QuoteControl), + new PropertyMetadata(null)); + + public object Content + { + get => GetValue(ContentProperty); + set => SetValue(ContentProperty, value); + } + + #endregion + + #endregion + + public QuoteControl() + { + DefaultStyleKey = typeof(QuoteControl); + } + + protected override void OnApplyTemplate() + { + base.OnApplyTemplate(); + _container = (ContentControl)GetTemplateChild(QuoteContainerName); + } +} + +public enum AlertKind +{ + None, + Note, + Tip, + Important, + Warning, + Caution, +} diff --git a/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/CodeBlockRenderer.cs b/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/CodeBlockRenderer.cs index 5845af4..8ba0e6d 100644 --- a/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/CodeBlockRenderer.cs +++ b/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/CodeBlockRenderer.cs @@ -1,17 +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.Syntax; using Symptum.UI.Markdown.TextElements; namespace Symptum.UI.Markdown.Renderers.ObjectRenderers; -internal class CodeBlockRenderer : WinUIObjectRenderer +public class CodeBlockRenderer : WinUIObjectRenderer { protected override void Write(WinUIRenderer renderer, CodeBlock obj) { - MyCodeBlock code = new(obj, renderer.Config); + CodeBlockElement code = new(obj, renderer.Configuration); renderer.Push(code); renderer.Pop(); } diff --git a/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Extensions/TableRenderer.cs b/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Extensions/TableRenderer.cs index 80dc873..6305acd 100644 --- a/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Extensions/TableRenderer.cs +++ b/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Extensions/TableRenderer.cs @@ -1,7 +1,3 @@ -// 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 Symptum.UI.Markdown.TextElements; @@ -14,9 +10,9 @@ protected override void Write(WinUIRenderer renderer, Table table) ArgumentNullException.ThrowIfNull(renderer); ArgumentNullException.ThrowIfNull(table); - MyTable myTable = new(table); + TableElement _table = new(table, renderer.Configuration); - renderer.Push(myTable); + renderer.Push(_table); for (int rowIndex = 0; rowIndex < table.Count; rowIndex++) { @@ -47,9 +43,9 @@ protected override void Write(WinUIRenderer renderer, Table table) }; } - MyTableCell myCell = new(cell, renderer.Config, textAlignment, row.IsHeader, columnIndex, rowIndex); + TableCellElement _cell = new(cell, renderer.Configuration, textAlignment, row.IsHeader, columnIndex, rowIndex); - renderer.Push(myCell); + renderer.Push(_cell); renderer.Write(cell); renderer.Pop(); } diff --git a/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Extensions/TaskListRenderer.cs b/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Extensions/TaskListRenderer.cs index b8882c1..85255b1 100644 --- a/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Extensions/TaskListRenderer.cs +++ b/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Extensions/TaskListRenderer.cs @@ -1,20 +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. - using Markdig.Extensions.TaskLists; using Symptum.UI.Markdown.TextElements; namespace Symptum.UI.Markdown.Renderers.ObjectRenderers.Extensions; -internal class TaskListRenderer : WinUIObjectRenderer +public class TaskListRenderer : WinUIObjectRenderer { protected override void Write(WinUIRenderer renderer, TaskList taskList) { ArgumentNullException.ThrowIfNull(renderer); ArgumentNullException.ThrowIfNull(taskList); - MyTaskListCheckBox checkBox = new(taskList); + TaskListCheckBoxElement checkBox = new(taskList); renderer.WriteInline(checkBox); } } diff --git a/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/HeadingRenderer.cs b/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/HeadingRenderer.cs index 523e79a..58e992d 100644 --- a/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/HeadingRenderer.cs +++ b/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/HeadingRenderer.cs @@ -1,20 +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. - using Markdig.Syntax; using Symptum.UI.Markdown.TextElements; namespace Symptum.UI.Markdown.Renderers.ObjectRenderers; -internal class HeadingRenderer : WinUIObjectRenderer +public class HeadingRenderer : WinUIObjectRenderer { protected override void Write(WinUIRenderer renderer, HeadingBlock obj) { ArgumentNullException.ThrowIfNull(renderer); ArgumentNullException.ThrowIfNull(obj); - MyHeading paragraph = new(obj, renderer.Config); + HeadingElement paragraph = new(obj, renderer.Configuration); renderer.Push(paragraph); renderer.WriteLeafInline(obj); renderer.Pop(); diff --git a/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Inlines/AutoLinkInlineRenderer.cs b/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Inlines/AutoLinkInlineRenderer.cs index 0aaf101..7165ba9 100644 --- a/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Inlines/AutoLinkInlineRenderer.cs +++ b/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Inlines/AutoLinkInlineRenderer.cs @@ -1,13 +1,9 @@ -// 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 Symptum.UI.Markdown.TextElements; namespace Symptum.UI.Markdown.Renderers.ObjectRenderers.Inlines; -internal class AutoLinkInlineRenderer : WinUIObjectRenderer +public class AutoLinkInlineRenderer : WinUIObjectRenderer { protected override void Write(WinUIRenderer renderer, AutolinkInline link) { @@ -25,7 +21,7 @@ protected override void Write(WinUIRenderer renderer, AutolinkInline link) url = "#"; } - MyAutolinkInline autolink = new(link); + AutolinkInlineElement autolink = new(link); renderer.Push(autolink); diff --git a/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Inlines/CodeInlineRenderer.cs b/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Inlines/CodeInlineRenderer.cs index dfe3fe8..c63ffdc 100644 --- a/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Inlines/CodeInlineRenderer.cs +++ b/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Inlines/CodeInlineRenderer.cs @@ -1,19 +1,15 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - using Markdig.Syntax.Inlines; using Symptum.UI.Markdown.TextElements; namespace Symptum.UI.Markdown.Renderers.ObjectRenderers.Inlines; -internal class CodeInlineRenderer : WinUIObjectRenderer +public class CodeInlineRenderer : WinUIObjectRenderer { protected override void Write(WinUIRenderer renderer, CodeInline obj) { ArgumentNullException.ThrowIfNull(renderer); ArgumentNullException.ThrowIfNull(obj); - renderer.WriteInline(new MyInlineCode(obj, renderer.Config)); + renderer.WriteInline(new CodeInlineElement(obj, renderer.Configuration)); } } diff --git a/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Inlines/ContainerInlineRenderer.cs b/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Inlines/ContainerInlineRenderer.cs index b794780..5c1b174 100644 --- a/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Inlines/ContainerInlineRenderer.cs +++ b/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Inlines/ContainerInlineRenderer.cs @@ -1,17 +1,9 @@ -// 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. - - -// 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 Symptum.UI.Markdown.Renderers.ObjectRenderers.Inlines; -internal class ContainerInlineRenderer : WinUIObjectRenderer +public class ContainerInlineRenderer : WinUIObjectRenderer { protected override void Write(WinUIRenderer renderer, ContainerInline obj) { diff --git a/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Inlines/DelimiterInlineRenderer.cs b/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Inlines/DelimiterInlineRenderer.cs index d10f7be..a20fc01 100644 --- a/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Inlines/DelimiterInlineRenderer.cs +++ b/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Inlines/DelimiterInlineRenderer.cs @@ -1,12 +1,8 @@ -// 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 Symptum.UI.Markdown.Renderers.ObjectRenderers.Inlines; -internal class DelimiterInlineRenderer : WinUIObjectRenderer +public class DelimiterInlineRenderer : WinUIObjectRenderer { protected override void Write(WinUIRenderer renderer, DelimiterInline obj) { diff --git a/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Inlines/EmphasisInlineRenderer.cs b/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Inlines/EmphasisInlineRenderer.cs index 2f49cc0..0846401 100644 --- a/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Inlines/EmphasisInlineRenderer.cs +++ b/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Inlines/EmphasisInlineRenderer.cs @@ -1,34 +1,30 @@ -// 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 Symptum.UI.Markdown.TextElements; namespace Symptum.UI.Markdown.Renderers.ObjectRenderers.Inlines; -internal class EmphasisInlineRenderer : WinUIObjectRenderer +public class EmphasisInlineRenderer : WinUIObjectRenderer { protected override void Write(WinUIRenderer renderer, EmphasisInline obj) { ArgumentNullException.ThrowIfNull(renderer); ArgumentNullException.ThrowIfNull(obj); - MyEmphasisInline? span = null; + EmphasisInlineElement? span = null; switch (obj.DelimiterChar) { case '*': case '_': - span = new MyEmphasisInline(obj); + span = new EmphasisInlineElement(obj); if (obj.DelimiterCount == 2) { span.SetBold(); } else { span.SetItalic(); } break; case '~': - span = new MyEmphasisInline(obj); + span = new EmphasisInlineElement(obj); if (obj.DelimiterCount == 2) { span.SetStrikeThrough(); } else { span.SetSubscript(); } break; case '^': - span = new MyEmphasisInline(obj); + span = new EmphasisInlineElement(obj); span.SetSuperscript(); break; } diff --git a/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Inlines/HtmlEntityInlineRenderer.cs b/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Inlines/HtmlEntityInlineRenderer.cs index e3c643d..6d2186d 100644 --- a/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Inlines/HtmlEntityInlineRenderer.cs +++ b/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Inlines/HtmlEntityInlineRenderer.cs @@ -1,17 +1,9 @@ -// 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. - - -// 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 Symptum.UI.Markdown.Renderers.ObjectRenderers.Inlines; -internal class HtmlEntityInlineRenderer : WinUIObjectRenderer +public class HtmlEntityInlineRenderer : WinUIObjectRenderer { protected override void Write(WinUIRenderer renderer, HtmlEntityInline obj) { diff --git a/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Inlines/LineBreakInlineRenderer.cs b/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Inlines/LineBreakInlineRenderer.cs index 21aadc7..9485d80 100644 --- a/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Inlines/LineBreakInlineRenderer.cs +++ b/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Inlines/LineBreakInlineRenderer.cs @@ -1,13 +1,9 @@ -// 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 Symptum.UI.Markdown.TextElements; namespace Symptum.UI.Markdown.Renderers.ObjectRenderers.Inlines; -internal class LineBreakInlineRenderer : WinUIObjectRenderer +public class LineBreakInlineRenderer : WinUIObjectRenderer { protected override void Write(WinUIRenderer renderer, LineBreakInline obj) { @@ -16,7 +12,7 @@ protected override void Write(WinUIRenderer renderer, LineBreakInline obj) if (obj.IsHard) { - renderer.WriteInline(new MyLineBreak()); + renderer.WriteInline(new LineBreakElement()); } else { diff --git a/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Inlines/LinkInlineRenderer.cs b/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Inlines/LinkInlineRenderer.cs index 09546f5..11931e0 100644 --- a/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Inlines/LinkInlineRenderer.cs +++ b/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Inlines/LinkInlineRenderer.cs @@ -1,13 +1,9 @@ -// 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 Symptum.UI.Markdown.TextElements; namespace Symptum.UI.Markdown.Renderers.ObjectRenderers.Inlines; -internal class LinkInlineRenderer : WinUIObjectRenderer +public class LinkInlineRenderer : WinUIObjectRenderer { protected override void Write(WinUIRenderer renderer, LinkInline link) { @@ -23,18 +19,18 @@ protected override void Write(WinUIRenderer renderer, LinkInline link) if (link.IsImage) { - MyImage image = new(link, Markdown.Extensions.GetUri(url, renderer.Config.BaseUrl), renderer.Config); + ImageElement image = new(link, Markdown.Extensions.GetUri(url, renderer.Configuration.BaseUrl), renderer.Configuration); renderer.WriteInline(image); } else { if (link.FirstChild is LinkInline linkInlineChild && linkInlineChild.IsImage) { - renderer.Push(new MyHyperlinkButton(link, renderer.Config.BaseUrl)); + renderer.Push(new HyperlinkButtonElement(link, renderer.Configuration.BaseUrl, renderer.Configuration)); } else { - MyHyperlink hyperlink = new(link, renderer.Config.BaseUrl); + HyperlinkElement hyperlink = new(link, renderer.Configuration.BaseUrl, renderer.Configuration); renderer.Push(hyperlink); } diff --git a/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Inlines/LiteralInlineRenderer.cs b/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Inlines/LiteralInlineRenderer.cs index 934bff3..5b433be 100644 --- a/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Inlines/LiteralInlineRenderer.cs +++ b/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Inlines/LiteralInlineRenderer.cs @@ -1,12 +1,8 @@ -// 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 Symptum.UI.Markdown.Renderers.ObjectRenderers.Inlines; -internal class LiteralInlineRenderer : WinUIObjectRenderer +public class LiteralInlineRenderer : WinUIObjectRenderer { protected override void Write(WinUIRenderer renderer, LiteralInline obj) { diff --git a/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/ListRenderer.cs b/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/ListRenderer.cs index 3c33dae..2413658 100644 --- a/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/ListRenderer.cs +++ b/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/ListRenderer.cs @@ -1,27 +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 Symptum.UI.Markdown.TextElements; namespace Symptum.UI.Markdown.Renderers.ObjectRenderers; -internal class ListRenderer : WinUIObjectRenderer +public class ListRenderer : WinUIObjectRenderer { protected override void Write(WinUIRenderer renderer, ListBlock listBlock) { ArgumentNullException.ThrowIfNull(renderer); ArgumentNullException.ThrowIfNull(listBlock); - MyList list = new(listBlock, renderer.Config, listBlock.Parent is MarkdownDocument); + ListElement list = new(listBlock, renderer.Configuration, listBlock.Parent is MarkdownDocument); renderer.Push(list); foreach (Block item in listBlock) { ListItemBlock listItemBlock = (ListItemBlock)item; - MyBlockContainer listItem = new(listItemBlock, renderer.Config); + BlockContainerElement listItem = new(listItemBlock, renderer.Configuration); renderer.Push(listItem); renderer.WriteChildren(listItemBlock); renderer.Pop(); diff --git a/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/ParagraphRenderer.cs b/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/ParagraphRenderer.cs index aa9fd48..66ec6bd 100644 --- a/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/ParagraphRenderer.cs +++ b/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/ParagraphRenderer.cs @@ -1,20 +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. - using Markdig.Syntax; using Symptum.UI.Markdown.TextElements; namespace Symptum.UI.Markdown.Renderers.ObjectRenderers; -internal class ParagraphRenderer : WinUIObjectRenderer +public class ParagraphRenderer : WinUIObjectRenderer { protected override void Write(WinUIRenderer renderer, ParagraphBlock obj) { ArgumentNullException.ThrowIfNull(renderer); ArgumentNullException.ThrowIfNull(obj); - MyParagraph paragraph = new(obj); + ParagraphElement paragraph = new(obj, renderer.Configuration); // set style renderer.Push(paragraph); renderer.WriteLeafInline(obj); diff --git a/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/QuoteBlockRenderer.cs b/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/QuoteBlockRenderer.cs index 015240f..37a6652 100644 --- a/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/QuoteBlockRenderer.cs +++ b/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/QuoteBlockRenderer.cs @@ -1,21 +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 Markdig.Extensions.Alerts; using Markdig.Syntax; using Symptum.UI.Markdown.TextElements; namespace Symptum.UI.Markdown.Renderers.ObjectRenderers; -internal class QuoteBlockRenderer : WinUIObjectRenderer +public class QuoteBlockRenderer : WinUIObjectRenderer { protected override void Write(WinUIRenderer renderer, QuoteBlock obj) { ArgumentNullException.ThrowIfNull(renderer); ArgumentNullException.ThrowIfNull(obj); - MyQuote quote = new(obj, renderer.Config, (obj as AlertBlock)?.Kind); + QuoteElement quote = new(obj, renderer.Configuration, (obj as AlertBlock)?.Kind); renderer.Push(quote); renderer.WriteChildren(obj); diff --git a/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/ThematicBreakRenderer.cs b/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/ThematicBreakRenderer.cs index de9a361..08b7d83 100644 --- a/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/ThematicBreakRenderer.cs +++ b/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/ThematicBreakRenderer.cs @@ -1,20 +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. - using Markdig.Syntax; using Symptum.UI.Markdown.TextElements; namespace Symptum.UI.Markdown.Renderers.ObjectRenderers; -internal class ThematicBreakRenderer : WinUIObjectRenderer +public class ThematicBreakRenderer : WinUIObjectRenderer { protected override void Write(WinUIRenderer renderer, ThematicBreakBlock obj) { ArgumentNullException.ThrowIfNull(renderer); ArgumentNullException.ThrowIfNull(obj); - MyThematicBreak thematicBreak = new(obj); + ThematicBreakElement thematicBreak = new(obj, renderer.Configuration); renderer.WriteBlock(thematicBreak); } diff --git a/src/Symptum.UI/Markdown/Renderers/WinUIObjectRenderer.cs b/src/Symptum.UI/Markdown/Renderers/WinUIObjectRenderer.cs index 25f7d1d..816ec5a 100644 --- a/src/Symptum.UI/Markdown/Renderers/WinUIObjectRenderer.cs +++ b/src/Symptum.UI/Markdown/Renderers/WinUIObjectRenderer.cs @@ -1,7 +1,3 @@ -// 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; diff --git a/src/Symptum.UI/Markdown/Renderers/WinUIRenderer.cs b/src/Symptum.UI/Markdown/Renderers/WinUIRenderer.cs index 2b7f444..7c82a31 100644 --- a/src/Symptum.UI/Markdown/Renderers/WinUIRenderer.cs +++ b/src/Symptum.UI/Markdown/Renderers/WinUIRenderer.cs @@ -1,7 +1,3 @@ -// 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; using Markdig.Helpers; @@ -16,20 +12,21 @@ public class WinUIRenderer : RendererBase { private readonly Stack _stack = new(); private char[] _buffer; - private MarkdownConfig _config = MarkdownConfig.Default; - public MyFlowDocument FlowDocument { get; private set; } - public MarkdownConfig Config + private MarkdownConfiguration _config = MarkdownConfiguration.Default; + + public FlowDocumentElement FlowDocument { get; private set; } + + public MarkdownConfiguration Configuration { get => _config; set => _config = value; } - public WinUIRenderer(MyFlowDocument document, MarkdownConfig config) + public WinUIRenderer(FlowDocumentElement document, MarkdownConfiguration config) { _buffer = new char[1024]; - Config = config; + Configuration = config; FlowDocument = document; - // set style _stack.Push(FlowDocument); LoadOverridenRenderers(); } @@ -42,13 +39,14 @@ private void LoadOverridenRenderers() public override object Render(MarkdownObject markdownObject) { Write(markdownObject); - return FlowDocument ?? new(Config); + return FlowDocument ?? new(Configuration); } public void ReloadDocument() { _stack.Clear(); FlowDocument.StackPanel.Children.Clear(); + Configuration.DocumentOutline.Clear(); _stack.Push(FlowDocument); LoadOverridenRenderers(); } @@ -74,7 +72,7 @@ public void WriteLeafRawLines(LeafBlock leafBlock) for (int i = 0; i < lines.Count; i++) { if (i != 0) - WriteInline(new MyLineBreak()); + WriteInline(new LineBreakElement()); WriteText(ref slices[i].Slice); } @@ -112,7 +110,7 @@ public void WriteText(ref StringSlice slice) public void WriteText(string? text) { - WriteInline(new MyInlineText(text ?? "")); + WriteInline(new TextInlineElement(text ?? "")); } public void WriteText(string? text, int offset, int length) diff --git a/src/Symptum.UI/Markdown/TextElements/MyAutolinkInline.cs b/src/Symptum.UI/Markdown/TextElements/AutolinkInlineElement.cs similarity index 60% rename from src/Symptum.UI/Markdown/TextElements/MyAutolinkInline.cs rename to src/Symptum.UI/Markdown/TextElements/AutolinkInlineElement.cs index da9379c..2b09ef4 100644 --- a/src/Symptum.UI/Markdown/TextElements/MyAutolinkInline.cs +++ b/src/Symptum.UI/Markdown/TextElements/AutolinkInlineElement.cs @@ -1,13 +1,9 @@ -// 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 Microsoft.UI.Xaml.Documents; namespace Symptum.UI.Markdown.TextElements; -internal class MyAutolinkInline : IAddChild +public class AutolinkInlineElement : IAddChild { private AutolinkInline _autoLinkInline; private SInline inline; @@ -15,7 +11,7 @@ internal class MyAutolinkInline : IAddChild public STextElement TextElement { get; private set; } - public MyAutolinkInline(AutolinkInline autoLinkInline) + public AutolinkInlineElement(AutolinkInline autoLinkInline) { _autoLinkInline = autoLinkInline; _hyperlink = new Hyperlink() @@ -32,14 +28,14 @@ public MyAutolinkInline(AutolinkInline autoLinkInline) public void AddChild(IAddChild child) { - if (child is MyInlineText text && text.TextElement is SInline inline && inline.Inline is Run run) + if (child is TextInlineElement text && text.TextElement is SInline inline && inline.Inline is Run run) try { _hyperlink?.Inlines.Add(run); } catch (Exception ex) { - throw new Exception("Error adding child to MyAutolinkInline", ex); + throw new Exception("Error adding child to AutolinkInlineElement", ex); } } } diff --git a/src/Symptum.UI/Markdown/TextElements/BlockContainerElement.cs b/src/Symptum.UI/Markdown/TextElements/BlockContainerElement.cs new file mode 100644 index 0000000..94e5115 --- /dev/null +++ b/src/Symptum.UI/Markdown/TextElements/BlockContainerElement.cs @@ -0,0 +1,27 @@ +using Markdig.Syntax; + +namespace Symptum.UI.Markdown.TextElements; + +public class BlockContainerElement : IAddChild +{ + private ContainerBlock _containerBlock; + private SContainer _container = new(); + private FlowDocumentElement _flowDocument; + + public STextElement TextElement + { + get => _container; + } + + public BlockContainerElement(ContainerBlock containerBlock, MarkdownConfiguration config) + { + _containerBlock = containerBlock; + _flowDocument = new FlowDocumentElement(config, false); + _container.UIElement = _flowDocument.StackPanel; + } + + public void AddChild(IAddChild child) + { + _flowDocument.AddChild(child); + } +} diff --git a/src/Symptum.UI/Markdown/TextElements/CodeBlockElement.cs b/src/Symptum.UI/Markdown/TextElements/CodeBlockElement.cs new file mode 100644 index 0000000..6785f44 --- /dev/null +++ b/src/Symptum.UI/Markdown/TextElements/CodeBlockElement.cs @@ -0,0 +1,81 @@ +using System.Text; +using ColorCode.Uno; +using Markdig.Helpers; +using Markdig.Syntax; + +namespace Symptum.UI.Markdown.TextElements; + +public class CodeBlockElement : IAddChild +{ + private CodeBlock _codeBlock; + private SContainer _container = new(); + private MarkdownConfiguration _config; + + public STextElement TextElement + { + get => _container; + } + + public CodeBlockElement(CodeBlock codeBlock, MarkdownConfiguration config) + { + _codeBlock = codeBlock; + _config = config; + Border border = new() + { + Style = _config.Themes.CodeBlockBorderStyle + }; + TextBlock textBlock = new() + { + Style = config.Themes.CodeTextBlockStyle + }; + + StringBuilder stringBuilder = new(); + + if (codeBlock is FencedCodeBlock fencedCodeBlock) + { + var formatter = new RichTextBlockFormatter(ElementTheme.Dark); + + // go through all the lines backwards and only add the lines to a stack if we have encountered the first non-empty line + StringLine[] lines = fencedCodeBlock.Lines.Lines; + Stack stack = new(); + bool encounteredFirstNonEmptyLine = false; + if (lines != null) + { + for (int i = lines.Length - 1; i >= 0; i--) + { + StringLine 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.FormatInlines(stringBuilder.ToString().TrimEnd("\r\n"), fencedCodeBlock.ToLanguage(), textBlock.Inlines); + } + else + { + for (int i = 0; i < codeBlock.Lines.Lines.Length; i++) + { + StringLine line = codeBlock.Lines.Lines[i]; + stringBuilder.Append(line.ToString()); + + if (i < codeBlock.Lines.Lines.Length - 1) stringBuilder.AppendLine(); + } + textBlock.Text = stringBuilder.ToString().TrimEnd("\r\n"); + } + border.Child = new ScrollViewer() { Content = textBlock, HorizontalScrollBarVisibility = ScrollBarVisibility.Auto }; + _container.UIElement = border; + } + + public void AddChild(IAddChild child) { } +} diff --git a/src/Symptum.UI/Markdown/TextElements/CodeInlineElement.cs b/src/Symptum.UI/Markdown/TextElements/CodeInlineElement.cs new file mode 100644 index 0000000..eeb3cde --- /dev/null +++ b/src/Symptum.UI/Markdown/TextElements/CodeInlineElement.cs @@ -0,0 +1,34 @@ +using Markdig.Syntax.Inlines; + +namespace Symptum.UI.Markdown.TextElements; + +public class CodeInlineElement : IAddChild +{ + private CodeInline _codeInline; + private SContainer _container = new(); + private MarkdownConfiguration _config; + + public STextElement TextElement + { + get => _container; + } + + public CodeInlineElement(CodeInline codeInline, MarkdownConfiguration config) + { + _codeInline = codeInline; + _config = config; + Border border = new() + { + Style = config.Themes.CodeInlineBorderStyle, + }; + TextBlock textBlock = new() + { + Text = codeInline.Content, + Style = config.Themes.CodeTextBlockStyle, + }; + border.Child = textBlock; + _container.UIElement = border; + } + + public void AddChild(IAddChild child) { } +} diff --git a/src/Symptum.UI/Markdown/TextElements/MyEmphasisInline.cs b/src/Symptum.UI/Markdown/TextElements/EmphasisInlineElement.cs similarity index 75% rename from src/Symptum.UI/Markdown/TextElements/MyEmphasisInline.cs rename to src/Symptum.UI/Markdown/TextElements/EmphasisInlineElement.cs index 3398259..5c7466a 100644 --- a/src/Symptum.UI/Markdown/TextElements/MyEmphasisInline.cs +++ b/src/Symptum.UI/Markdown/TextElements/EmphasisInlineElement.cs @@ -1,14 +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. - using Markdig.Syntax.Inlines; using Microsoft.UI.Xaml.Documents; using Windows.UI.Text; namespace Symptum.UI.Markdown.TextElements; -internal class MyEmphasisInline : IAddChild +public class EmphasisInlineElement : IAddChild { private Span _span; private SInline inline; @@ -23,7 +19,7 @@ public STextElement TextElement get => inline; } - public MyEmphasisInline(EmphasisInline emphasisInline) + public EmphasisInlineElement(EmphasisInline emphasisInline) { _span = new Span(); _markdownObject = emphasisInline; @@ -35,18 +31,21 @@ public MyEmphasisInline(EmphasisInline emphasisInline) public void AddChild(IAddChild child) { - - if (child is MyInlineText inlineText && inlineText.TextElement is SInline inline) - { - _span.Inlines.Add(inline.Inline); - } - else if (child is MyEmphasisInline emphasisInline) + if (child is EmphasisInlineElement emphasisInline) { if (emphasisInline._isBold) { SetBold(); } if (emphasisInline._isItalic) { SetItalic(); } if (emphasisInline._isStrikeThrough) { SetStrikeThrough(); } _span.Inlines.Add(emphasisInline._span); } + else if (child is IAddChild textElement && textElement.TextElement is SInline _inline) + { + _span.Inlines.Add(_inline.Inline); + } + else + { + + } } public void SetBold() diff --git a/src/Symptum.UI/Markdown/TextElements/FlowDocumentElement.cs b/src/Symptum.UI/Markdown/TextElements/FlowDocumentElement.cs new file mode 100644 index 0000000..8ec8f60 --- /dev/null +++ b/src/Symptum.UI/Markdown/TextElements/FlowDocumentElement.cs @@ -0,0 +1,60 @@ +using Windows.UI.Text; + +namespace Symptum.UI.Markdown.TextElements; + +public class FlowDocumentElement : IAddChild +{ + private StackPanel _stackPanel = new(); + private SContainer _container = new(); + private MarkdownConfiguration _config; + + public STextElement TextElement + { + get => _container; + } + + public StackPanel StackPanel + { + get => _stackPanel; + set => _stackPanel = value; + } + + public Style? TextBlockStyle { get; set; } + + public FlowDocumentElement(MarkdownConfiguration config, bool isTopLevel = true) + { + _stackPanel.Spacing = config.Themes.Spacing; + if (isTopLevel) _stackPanel.Padding = config.Themes.Padding; + _config = config; + + _container.UIElement = _stackPanel; + } + + public void AddChild(IAddChild child) + { + STextElement element = child.TextElement; + if (element != null) + { + if (element is SInline inline) + { + TextBlock _textBlock = new() + { + Style = TextBlockStyle ?? _config.Themes.BodyTextBlockStyle + }; + _textBlock.Inlines.Add(inline.Inline); + _stackPanel.Children.Add(_textBlock); + } + else if (element is SBlock block) + { + if (block is SParagraph paragraph) + { + if (TextBlockStyle != null) paragraph.TextBlockStyle = TextBlockStyle; + paragraph.CreateUIElement(); + } + + if (block.UIElement is UIElement ui) + _stackPanel.Children.Add(ui); + } + } + } +} diff --git a/src/Symptum.UI/Markdown/TextElements/HeadingElement.cs b/src/Symptum.UI/Markdown/TextElements/HeadingElement.cs new file mode 100644 index 0000000..a609875 --- /dev/null +++ b/src/Symptum.UI/Markdown/TextElements/HeadingElement.cs @@ -0,0 +1,62 @@ +using Markdig.Renderers.Html; +using Markdig.Syntax; + +namespace Symptum.UI.Markdown.TextElements; + +public class HeadingElement : IAddChild +{ + private SParagraph _paragraph; + private HeadingBlock? _headingBlock; + private MarkdownConfiguration _config; + + public STextElement TextElement + { + get => _paragraph; + } + + public HeadingElement(HeadingBlock headingBlock, MarkdownConfiguration config) + { + _headingBlock = headingBlock; + _paragraph = new(); + _config = config; + + int level = headingBlock.Level; + _paragraph.TextBlockStyle = level switch + { + 1 => _config.Themes.H1TextBlockStyle, + 2 => _config.Themes.H2TextBlockStyle, + 3 => _config.Themes.H3TextBlockStyle, + 4 => _config.Themes.H4TextBlockStyle, + 5 => _config.Themes.H5TextBlockStyle, + _ => _config.Themes.H6TextBlockStyle, + }; + + DocumentNode node = new() + { + Id = headingBlock.GetAttributes().Id, + Level = level switch + { + 1 => DocumentLevel.Heading1, + 2 => DocumentLevel.Heading2, + 3 => DocumentLevel.Heading3, + 4 => DocumentLevel.Heading4, + 5 => DocumentLevel.Heading5, + _ => DocumentLevel.Heading6, + }, + Navigate = OnNavigate, + Title = headingBlock.Inline?.FirstChild?.ToString() + }; + + config.DocumentOutline.PushNode(node); + } + + public void OnNavigate() + { + _paragraph.UIElement?.StartBringIntoView(new() { HorizontalAlignmentRatio = 0, VerticalAlignmentRatio = 0 }); + } + + public void AddChild(IAddChild child) + { + _paragraph.AddInline(child.TextElement); + } +} diff --git a/src/Symptum.UI/Markdown/TextElements/MyHyperlinkButton.cs b/src/Symptum.UI/Markdown/TextElements/HyperlinkButtonElement.cs similarity index 67% rename from src/Symptum.UI/Markdown/TextElements/MyHyperlinkButton.cs rename to src/Symptum.UI/Markdown/TextElements/HyperlinkButtonElement.cs index 7d4d730..ddfa67b 100644 --- a/src/Symptum.UI/Markdown/TextElements/MyHyperlinkButton.cs +++ b/src/Symptum.UI/Markdown/TextElements/HyperlinkButtonElement.cs @@ -1,27 +1,24 @@ -// 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 Symptum.UI.Markdown.TextElements; -internal class MyHyperlinkButton : IAddChild +public class HyperlinkButtonElement : IAddChild { private HyperlinkButton? _hyperLinkButton; - private SContainer _container; - private MyFlowDocument? _flowDoc; + private SContainer _container = new(); + private FlowDocumentElement? _flowDoc; private string? _baseUrl; private LinkInline? _linkInline; + private MarkdownConfiguration _config; public STextElement TextElement { get => _container; } - public MyHyperlinkButton(LinkInline linkInline, string? baseUrl) + public HyperlinkButtonElement(LinkInline linkInline, string? baseUrl, MarkdownConfiguration config) { - _container = new(); + _config = config; _baseUrl = baseUrl; string? url = linkInline.GetDynamicUrl != null ? linkInline.GetDynamicUrl() ?? linkInline.Url : linkInline.Url; _linkInline = linkInline; @@ -38,7 +35,7 @@ private void Init(string? url, string? baseUrl) }; if (_linkInline != null) { - _flowDoc = new MyFlowDocument(MarkdownConfig.Default, false); + _flowDoc = new FlowDocumentElement(_config, false); } _container.UIElement = _hyperLinkButton; _hyperLinkButton.Content = _flowDoc?.StackPanel; diff --git a/src/Symptum.UI/Markdown/TextElements/MyHyperlink.cs b/src/Symptum.UI/Markdown/TextElements/HyperlinkElement.cs similarity index 64% rename from src/Symptum.UI/Markdown/TextElements/MyHyperlink.cs rename to src/Symptum.UI/Markdown/TextElements/HyperlinkElement.cs index 48374bb..0274c94 100644 --- a/src/Symptum.UI/Markdown/TextElements/MyHyperlink.cs +++ b/src/Symptum.UI/Markdown/TextElements/HyperlinkElement.cs @@ -1,33 +1,31 @@ -// 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 Microsoft.UI.Xaml.Documents; namespace Symptum.UI.Markdown.TextElements; -internal class MyHyperlink : IAddChild +public class HyperlinkElement : IAddChild { private Hyperlink _hyperlink; private SInline inline; private LinkInline? _linkInline; private string? _baseUrl; + private ILinkHandler? _linkHandler; + private string? _url; public STextElement TextElement { get => inline; } - public MyHyperlink(LinkInline linkInline, string? baseUrl) + public HyperlinkElement(LinkInline linkInline, string? baseUrl, MarkdownConfiguration config) { _baseUrl = baseUrl; string? url = linkInline.GetDynamicUrl != null ? linkInline.GetDynamicUrl() ?? linkInline.Url : linkInline.Url; + _url = url; _linkInline = linkInline; - _hyperlink = new Hyperlink() - { - NavigateUri = Extensions.GetUri(url, baseUrl), - }; + _linkHandler = config.LinkHandler; + _hyperlink = new Hyperlink(); + _hyperlink.Click += Hyperlink_Click; ToolTipService.SetToolTip(_hyperlink, linkInline.Title); inline = new() { @@ -35,6 +33,11 @@ public MyHyperlink(LinkInline linkInline, string? baseUrl) }; } + private void Hyperlink_Click(Hyperlink sender, HyperlinkClickEventArgs args) + { + _linkHandler?.HandleNavigation(_url, _baseUrl); + } + public void AddChild(IAddChild child) { if (child.TextElement is SInline inlineChild) @@ -42,7 +45,6 @@ public void AddChild(IAddChild child) try { _hyperlink.Inlines.Add(inlineChild.Inline); - // TODO: Add support for click handler } catch { } } diff --git a/src/Symptum.UI/Markdown/TextElements/IAddChild.cs b/src/Symptum.UI/Markdown/TextElements/IAddChild.cs index a0a96f8..0998104 100644 --- a/src/Symptum.UI/Markdown/TextElements/IAddChild.cs +++ b/src/Symptum.UI/Markdown/TextElements/IAddChild.cs @@ -1,7 +1,3 @@ -// 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 Symptum.UI.Markdown.TextElements; public interface IAddChild diff --git a/src/Symptum.UI/Markdown/TextElements/MyImage.cs b/src/Symptum.UI/Markdown/TextElements/ImageElement.cs similarity index 71% rename from src/Symptum.UI/Markdown/TextElements/MyImage.cs rename to src/Symptum.UI/Markdown/TextElements/ImageElement.cs index bdc7657..fee89bb 100644 --- a/src/Symptum.UI/Markdown/TextElements/MyImage.cs +++ b/src/Symptum.UI/Markdown/TextElements/ImageElement.cs @@ -1,17 +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. - using Markdig.Syntax.Inlines; using Windows.Storage.Streams; using Microsoft.UI.Xaml.Media.Imaging; using System.Runtime.InteropServices.WindowsRuntime; +using Windows.Foundation; namespace Symptum.UI.Markdown.TextElements; -internal class MyImage : IAddChild +public class ImageElement : IAddChild { - private SContainer _containerBlock = new(); + private SContainer _container = new(); private LinkInline? _linkInline; private Image _image = new(); private Uri _uri; @@ -23,17 +20,17 @@ internal class MyImage : IAddChild public STextElement TextElement { - get => _containerBlock; + get => _container; } - public MyImage(LinkInline linkInline, Uri uri, MarkdownConfig config) + public ImageElement(LinkInline linkInline, Uri uri, MarkdownConfiguration config) { _linkInline = linkInline; _uri = uri; _imageProvider = config.ImageProvider; - _svgRenderer = config.SVGRenderer == null ? new DefaultSVGRenderer() : config.SVGRenderer; + _svgRenderer = config.SVGRenderer ?? new DefaultSVGRenderer(); Init(); - Windows.Foundation.Size size = Extensions.GetMarkdownImageSize(linkInline); + Size size = Extensions.GetMarkdownImageSize(linkInline); if (size.Width != 0) { _precedentWidth = size.Width; @@ -47,7 +44,7 @@ public MyImage(LinkInline linkInline, Uri uri, MarkdownConfig config) private void Init() { _image.Loaded += LoadImage; - _containerBlock.UIElement = _image; + _container.UIElement = _image; } private async void LoadImage(object sender, RoutedEventArgs e) @@ -57,8 +54,7 @@ private async void LoadImage(object sender, RoutedEventArgs e) { if (_imageProvider != null && _imageProvider.ShouldUseThisProvider(_uri.AbsoluteUri)) { - _image = await _imageProvider.GetImage(_uri.AbsoluteUri); - _containerBlock.UIElement = _image; + _image.Source = await _imageProvider.GetImageSource(_uri.AbsoluteUri); } else { @@ -67,15 +63,18 @@ private async void LoadImage(object sender, RoutedEventArgs e) // Download data from URL HttpResponseMessage response = await client.GetAsync(_uri); - string? contentType = response?.Content?.Headers?.ContentType?.MediaType; + string? contentType = response.Content.Headers.ContentType.MediaType; if (contentType == "image/svg+xml") { - string? svgString = await response?.Content?.ReadAsStringAsync(); - Image resImage = await _svgRenderer.SvgToImage(svgString); + + string? svgString = await response.Content.ReadAsStringAsync(); + ImageSource resImage = await _svgRenderer.SvgToImageSource(svgString); if (resImage != null) { - _image = resImage; - _containerBlock.UIElement = _image; + _image.Source = resImage; + Size size = Extensions.GetSvgSize(svgString); + if (size.Width > 0) _image.Width = size.Width; + if (size.Height > 0) _image.Height = size.Height; } } else @@ -95,7 +94,6 @@ private async void LoadImage(object sender, RoutedEventArgs e) _image.Source = bitmap; _image.Width = bitmap.PixelWidth == 0 ? bitmap.DecodePixelWidth : bitmap.PixelWidth; _image.Height = bitmap.PixelHeight == 0 ? bitmap.DecodePixelHeight : bitmap.PixelHeight; - } _loaded = true; diff --git a/src/Symptum.UI/Markdown/TextElements/LineBreakElement.cs b/src/Symptum.UI/Markdown/TextElements/LineBreakElement.cs new file mode 100644 index 0000000..2321c7b --- /dev/null +++ b/src/Symptum.UI/Markdown/TextElements/LineBreakElement.cs @@ -0,0 +1,23 @@ +using Microsoft.UI.Xaml.Documents; + +namespace Symptum.UI.Markdown.TextElements; + +public class LineBreakElement : IAddChild +{ + private SInline inline; + + public STextElement TextElement + { + get => inline; + } + + public LineBreakElement() + { + inline = new() + { + Inline = new LineBreak() + }; + } + + public void AddChild(IAddChild child) { } +} diff --git a/src/Symptum.UI/Markdown/TextElements/MyList.cs b/src/Symptum.UI/Markdown/TextElements/ListElement.cs similarity index 86% rename from src/Symptum.UI/Markdown/TextElements/MyList.cs rename to src/Symptum.UI/Markdown/TextElements/ListElement.cs index d3a4896..d7b2215 100644 --- a/src/Symptum.UI/Markdown/TextElements/MyList.cs +++ b/src/Symptum.UI/Markdown/TextElements/ListElement.cs @@ -1,18 +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. - using Markdig.Syntax; using RomanNumerals; using System.Globalization; namespace Symptum.UI.Markdown.TextElements; -internal class MyList : IAddChild +public class ListElement : IAddChild { private SContainer _container; private StackPanel _stackPanel; - private MarkdownConfig _config; + private MarkdownConfiguration _config; private BulletType _bulletType; private bool _isOrdered; private int _startIndex = 1; @@ -24,7 +20,7 @@ public STextElement TextElement get => _container; } - public MyList(ListBlock listBlock, MarkdownConfig config, bool isTopLevel = true) + public ListElement(ListBlock listBlock, MarkdownConfiguration config, bool isTopLevel = true) { _container = new SContainer(); _config = config; @@ -32,7 +28,7 @@ public MyList(ListBlock listBlock, MarkdownConfig config, bool isTopLevel = true if (listBlock.IsOrdered) { _isOrdered = true; - _bulletType = MyList.ToBulletType(listBlock.BulletType); + _bulletType = ToBulletType(listBlock.BulletType); if (listBlock.OrderedStart != null && listBlock.DefaultOrderedStart != listBlock.OrderedStart) { @@ -80,11 +76,12 @@ public void AddChild(IAddChild child) TextBlock textBlock = new() { Text = bullet, + Style = _config.Themes.BodyTextBlockStyle }; textBlock.SetValue(Grid.ColumnProperty, 0); textBlock.VerticalAlignment = VerticalAlignment.Top; grid.Children.Add(textBlock); - MyFlowDocument flowDoc = new(_config, false); + FlowDocumentElement flowDoc = new(_config, false); flowDoc.AddChild(child); flowDoc.StackPanel.SetValue(Grid.ColumnProperty, 1); diff --git a/src/Symptum.UI/Markdown/TextElements/MyBlockContainer.cs b/src/Symptum.UI/Markdown/TextElements/MyBlockContainer.cs deleted file mode 100644 index ba45a91..0000000 --- a/src/Symptum.UI/Markdown/TextElements/MyBlockContainer.cs +++ /dev/null @@ -1,34 +0,0 @@ -// 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 Symptum.UI.Markdown.TextElements; - -internal class MyBlockContainer : IAddChild -{ - private ContainerBlock _containerBlock; - private SContainer _container; - private MyFlowDocument _flowDocument; - - public STextElement TextElement - { - get => _container; - } - - public MyBlockContainer(ContainerBlock containerBlock, MarkdownConfig config) - { - _containerBlock = containerBlock; - _flowDocument = new MyFlowDocument(config, false); - _container = new SContainer - { - UIElement = _flowDocument.StackPanel - }; - } - - public void AddChild(IAddChild child) - { - _flowDocument.AddChild(child); - } -} diff --git a/src/Symptum.UI/Markdown/TextElements/MyCodeBlock.cs b/src/Symptum.UI/Markdown/TextElements/MyCodeBlock.cs deleted file mode 100644 index ac03d3c..0000000 --- a/src/Symptum.UI/Markdown/TextElements/MyCodeBlock.cs +++ /dev/null @@ -1,88 +0,0 @@ -// 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 System.Text; -using Markdig.Syntax; -using Microsoft.UI.Xaml.Documents; - -namespace Symptum.UI.Markdown.TextElements; - -internal class MyCodeBlock : IAddChild -{ - private CodeBlock _codeBlock; - private SContainer _container; - private MarkdownConfig _config; - - public STextElement TextElement - { - get => _container; - } - - public MyCodeBlock(CodeBlock codeBlock, MarkdownConfig config) - { - _codeBlock = codeBlock; - _config = config; - _container = new(); - Border border = new() - { - Background = (Brush)Application.Current.Resources["ExpanderHeaderBackground"], - Padding = _config.Themes.CodeBlockPadding, - Margin = _config.Themes.InternalMargin, - CornerRadius = _config.Themes.CornerRadius - }; - TextBlock textBlock = new(); - - if (codeBlock is FencedCodeBlock fencedCodeBlock) - { - //#if !WINAPPSDK - // var formatter = new ColorCode.RichTextBlockFormatter(Extensions.GetOneDarkProStyle()); - //#else - // var formatter = new ColorCode.RichTextBlockFormatter(Extensions.GetOneDarkProStyle()); - //#endif - StringBuilder stringBuilder = new(); - - // go through all the lines backwards and only add the lines to a stack if we have encountered the first non-empty line - Markdig.Helpers.StringLine[] lines = fencedCodeBlock.Lines.Lines; - Stack stack = new(); - bool encounteredFirstNonEmptyLine = false; - if (lines != null) - { - for (int i = lines.Length - 1; i >= 0; i--) - { - Markdig.Helpers.StringLine 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 (Markdig.Helpers.StringLine line in codeBlock.Lines.Lines) - { - string lineString = line.ToString(); - if (!string.IsNullOrWhiteSpace(lineString)) - { - textBlock.Inlines.Add(new Run() { Text = lineString }); - } - } - } - border.Child = textBlock; - _container.UIElement = border; - } - - public void AddChild(IAddChild child) { } -} diff --git a/src/Symptum.UI/Markdown/TextElements/MyFlowDocument.cs b/src/Symptum.UI/Markdown/TextElements/MyFlowDocument.cs deleted file mode 100644 index a24e65b..0000000 --- a/src/Symptum.UI/Markdown/TextElements/MyFlowDocument.cs +++ /dev/null @@ -1,59 +0,0 @@ -// 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 Microsoft.UI.Text; -using Windows.UI.Text; - -namespace Symptum.UI.Markdown.TextElements; - -public class MyFlowDocument : IAddChild -{ - private StackPanel _stackPanel = new(); - - // useless property - public STextElement TextElement { get; set; } - // - - public StackPanel StackPanel - { - get => _stackPanel; - set => _stackPanel = value; - } - - public FontWeight FontWeight { get; set; } = FontWeights.Normal; - - public MyFlowDocument(MarkdownConfig config, bool isTopLevel = true) - { - if (config != null) - { - _stackPanel.Spacing = config.Themes.Spacing; - if (isTopLevel) _stackPanel.Padding = config.Themes.Padding; - } - - TextElement = new SContainer() { UIElement = _stackPanel }; - } - - public void AddChild(IAddChild child) - { - STextElement element = child.TextElement; - if (element != null) - { - if (element is SInline inline) - { - TextBlock _textBlock = new() - { - FontWeight = FontWeight, - TextWrapping = TextWrapping.Wrap, - VerticalAlignment = VerticalAlignment.Center - }; - _textBlock.Inlines.Add(inline.Inline); - _stackPanel.Children.Add(_textBlock); - } - else if (element is SBlock block && block.GetUIElement() is UIElement ui) - { - _stackPanel.Children.Add(ui); - } - } - } -} diff --git a/src/Symptum.UI/Markdown/TextElements/MyHeading.cs b/src/Symptum.UI/Markdown/TextElements/MyHeading.cs deleted file mode 100644 index 453329b..0000000 --- a/src/Symptum.UI/Markdown/TextElements/MyHeading.cs +++ /dev/null @@ -1,63 +0,0 @@ -// 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 Symptum.UI.Markdown.TextElements; - -internal class MyHeading : IAddChild -{ - private SParagraph _paragraph; - private HeadingBlock? _headingBlock; - private MarkdownConfig _config; - - public STextElement TextElement - { - get => _paragraph; - } - - public MyHeading(HeadingBlock headingBlock, MarkdownConfig config) - { - _headingBlock = headingBlock; - _paragraph = new(); - _config = config; - - int 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 = level switch - { - 1 => _config.Themes.H1Foreground, - 2 => _config.Themes.H2Foreground, - 3 => _config.Themes.H3Foreground, - 4 => _config.Themes.H4Foreground, - 5 => _config.Themes.H5Foreground, - _ => _config.Themes.H6Foreground, - }; - _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 SInline inlineChild) - { - _paragraph.Inlines.Add(inlineChild.Inline); - } - } -} diff --git a/src/Symptum.UI/Markdown/TextElements/MyInlineCode.cs b/src/Symptum.UI/Markdown/TextElements/MyInlineCode.cs deleted file mode 100644 index 78f450e..0000000 --- a/src/Symptum.UI/Markdown/TextElements/MyInlineCode.cs +++ /dev/null @@ -1,47 +0,0 @@ -// 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 Symptum.UI.Markdown.TextElements; - -internal class MyInlineCode : IAddChild -{ - private CodeInline _codeInline; - private SContainer _container; - private MarkdownConfig _config; - - public STextElement TextElement - { - get => _container; - } - - public MyInlineCode(CodeInline codeInline, MarkdownConfig config) - { - _codeInline = codeInline; - _config = config; - _container = new(); - Border border = new() - { - VerticalAlignment = VerticalAlignment.Bottom, - Background = _config.Themes.InlineCodeBackground, - BorderBrush = _config.Themes.InlineCodeBorderBrush, - BorderThickness = _config.Themes.InlineCodeBorderThickness, - CornerRadius = _config.Themes.InlineCodeCornerRadius, - Padding = _config.Themes.InlineCodePadding - }; - TextBlock textBlock = new() - { - FontSize = _config.Themes.InlineCodeFontSize, - FontWeight = _config.Themes.InlineCodeFontWeight, - Text = codeInline.Content.ToString(), - TextWrapping = TextWrapping.Wrap - }; - border.Child = textBlock; - _container.UIElement = border; - } - - - public void AddChild(IAddChild child) { } -} diff --git a/src/Symptum.UI/Markdown/TextElements/MyLineBreak.cs b/src/Symptum.UI/Markdown/TextElements/MyLineBreak.cs deleted file mode 100644 index bf8d08e..0000000 --- a/src/Symptum.UI/Markdown/TextElements/MyLineBreak.cs +++ /dev/null @@ -1,32 +0,0 @@ -// 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. - - -// 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 Microsoft.UI.Xaml.Documents; - -namespace Symptum.UI.Markdown.TextElements; - -internal class MyLineBreak : IAddChild -{ - private SInline inline; - - public STextElement TextElement - { - get => inline; - } - - public MyLineBreak() - { - inline = new() - { - Inline = new LineBreak() - }; - } - - public void AddChild(IAddChild child) { } -} diff --git a/src/Symptum.UI/Markdown/TextElements/MyParagraph.cs b/src/Symptum.UI/Markdown/TextElements/MyParagraph.cs deleted file mode 100644 index 6b37a1d..0000000 --- a/src/Symptum.UI/Markdown/TextElements/MyParagraph.cs +++ /dev/null @@ -1,38 +0,0 @@ -// 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 Symptum.UI.Markdown.TextElements; - -internal class MyParagraph : IAddChild -{ - private ParagraphBlock _paragraphBlock; - private SParagraph _paragraph; - - public STextElement TextElement - { - get => _paragraph; - } - - public MyParagraph(ParagraphBlock paragraphBlock) - { - _paragraphBlock = paragraphBlock; - _paragraph = new(); - } - - public void AddChild(IAddChild child) - { - if (child.TextElement is SInline inlineChild) - { - _paragraph.Inlines.Add(inlineChild.Inline); - } - else if (child.TextElement is SBlock blockChild && blockChild.GetUIElement() is UIElement uiElement) - { - _paragraph.UIIndices.Add(_paragraph.Inlines.Count); // Notes the position of the UIElement with respect to the previous inline - _paragraph.ContainsUI = true; - _paragraph.UIElements.Add(uiElement); - } - } -} diff --git a/src/Symptum.UI/Markdown/TextElements/MyQuote.cs b/src/Symptum.UI/Markdown/TextElements/MyQuote.cs deleted file mode 100644 index 59d7eff..0000000 --- a/src/Symptum.UI/Markdown/TextElements/MyQuote.cs +++ /dev/null @@ -1,82 +0,0 @@ -// 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.Helpers; -using Markdig.Syntax; - -namespace Symptum.UI.Markdown.TextElements; - -internal class MyQuote : IAddChild -{ - private SContainer _container; - private MyFlowDocument _flowDocument; - private QuoteBlock _quoteBlock; - - public STextElement TextElement - { - get => _container; - } - - public MyQuote(QuoteBlock quoteBlock, MarkdownConfig config, StringSlice? kind = null) - { - TextBlock? _alertKindTB = null; - _quoteBlock = quoteBlock; - _container = new(); - - _flowDocument = new MyFlowDocument(config, false); - AlertKind alertKind = AlertKind.None; - - if (kind != null && kind?.Length < 16) - { - Span upperKind = stackalloc char[kind?.Length ?? 0]; - kind?.AsSpan().ToUpperInvariant(upperKind); - alertKind = upperKind switch - { - "NOTE" => AlertKind.Note, - "TIP" => AlertKind.Tip, - "IMPORTANT" => AlertKind.Important, - "WARNING" => AlertKind.Warning, - "CAUTION" => AlertKind.Caution, - _ => AlertKind.None - }; - - _alertKindTB = new() - { - Text = alertKind.ToString() - }; - _flowDocument.StackPanel.Children.Insert(0, _alertKindTB); - } - - Border border = new() - { - Child = _flowDocument.StackPanel, - Style = alertKind switch - { - AlertKind.Tip => config.Themes.TipQuoteBlockStyle, - AlertKind.Warning => config.Themes.WarningQuoteBlockStyle, - AlertKind.Caution => config.Themes.CautionQuoteBlockStyle, - _ => config.Themes.DefaultQuoteBlockStyle - } - }; - - if (_alertKindTB != null) _alertKindTB.Foreground = border.BorderBrush; - - _container.UIElement = border; - } - - public void AddChild(IAddChild child) - { - _flowDocument.AddChild(child); - } -} - -public enum AlertKind -{ - None, - Note, - Tip, - Important, - Warning, - Caution, -} diff --git a/src/Symptum.UI/Markdown/TextElements/MyTable.cs b/src/Symptum.UI/Markdown/TextElements/MyTable.cs deleted file mode 100644 index 433a968..0000000 --- a/src/Symptum.UI/Markdown/TextElements/MyTable.cs +++ /dev/null @@ -1,52 +0,0 @@ -// 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 Microsoft.UI; - -namespace Symptum.UI.Markdown.TextElements; - -internal class MyTable : IAddChild -{ - private Table _table; - private SContainer _container; - private MyTableUIElement _tableElement; - - public STextElement TextElement - { - get => _container; - } - - public MyTable(Table table) - { - _table = table; - _container = new(); - int column = table.FirstOrDefault() is TableRow row ? row.Count : 0; - - _tableElement = new MyTableUIElement - ( - column, - table.Count, - 3, - new SolidColorBrush(Colors.Gray) - ) - { HorizontalAlignment = HorizontalAlignment.Center }; - _container.UIElement = _tableElement; - } - - public void AddChild(IAddChild child) - { - if (child is MyTableCell cellChild) - { - Grid 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); - } - } -} diff --git a/src/Symptum.UI/Markdown/TextElements/MyTableCell.cs b/src/Symptum.UI/Markdown/TextElements/MyTableCell.cs deleted file mode 100644 index 04a50b7..0000000 --- a/src/Symptum.UI/Markdown/TextElements/MyTableCell.cs +++ /dev/null @@ -1,86 +0,0 @@ -// 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 Microsoft.UI.Text; - -namespace Symptum.UI.Markdown.TextElements; - -internal class MyTableCell : IAddChild -{ - private TableCell _tableCell; - private SParagraph _paragraph = new(); - private MyFlowDocument _flowDocument; - private bool _isHeader; - private int _columnIndex; - private int _rowIndex; - private Grid _container; - - public STextElement 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, MarkdownConfig config, TextAlignment textAlignment, bool isHeader, int columnIndex, int rowIndex) - { - _isHeader = isHeader; - _tableCell = tableCell; - _columnIndex = columnIndex; - _rowIndex = rowIndex; - _container = new(); - - _flowDocument = new MyFlowDocument(config, false); - _flowDocument.StackPanel.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.FontWeight = FontWeights.Bold; - } - _flowDocument.StackPanel.HorizontalAlignment = textAlignment switch - { - TextAlignment.Left => HorizontalAlignment.Left, - TextAlignment.Center => HorizontalAlignment.Center, - TextAlignment.Right => HorizontalAlignment.Right, - _ => HorizontalAlignment.Left, - }; - _container.Children.Add(_flowDocument.StackPanel); - } - - public void AddChild(IAddChild child) - { - _flowDocument.AddChild(child); - } -} diff --git a/src/Symptum.UI/Markdown/TextElements/MyTableRow.cs b/src/Symptum.UI/Markdown/TextElements/MyTableRow.cs deleted file mode 100644 index 4d38de9..0000000 --- a/src/Symptum.UI/Markdown/TextElements/MyTableRow.cs +++ /dev/null @@ -1,39 +0,0 @@ -// 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 Symptum.UI.Markdown.TextElements; - -internal class MyTableRow : IAddChild -{ - private TableRow _tableRow; - private StackPanel _stackPanel; - private SContainer _container = new(); - - public STextElement TextElement - { - get => _container; - } - - public MyTableRow(TableRow tableRow) - { - _tableRow = tableRow; - _stackPanel = new() - { - Orientation = Orientation.Horizontal - }; - _container.UIElement = _stackPanel; - } - - public void AddChild(IAddChild child) - { - if (child is MyTableCell cellChild) - { - //var richTextBlock = new RichTextBlock(); - //richTextBlock.Blocks.Add((Paragraph)cellChild.TextElement); - //_stackPanel.Children.Add(richTextBlock); - } - } -} diff --git a/src/Symptum.UI/Markdown/TextElements/MyTableUIElement.cs b/src/Symptum.UI/Markdown/TextElements/MyTableUIElement.cs deleted file mode 100644 index fbeb182..0000000 --- a/src/Symptum.UI/Markdown/TextElements/MyTableUIElement.cs +++ /dev/null @@ -1,217 +0,0 @@ -// 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 Microsoft.UI.Xaml.Shapes; -using Windows.Foundation; - -namespace Symptum.UI.Markdown.TextElements; - -internal partial class MyTableUIElement : Panel -{ - private Size size; - 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; - Children.Add(new Border() - { - BorderBrush = borderBrush, - BorderThickness = new Thickness(borderThickness), - CornerRadius = new(4) - }); - 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 ContentChildren - { - get - { - for (int i = _columnCount + _rowCount - 1; i < Children.Count; i++) - { - yield return (FrameworkElement)Children[i]; - } - } - } - - // Helper method to get table vertical edges. - private IEnumerable VerticalLines - { - get - { - for (int i = 1; i < _columnCount; i++) - { - yield return (Rectangle)Children[i]; - } - } - } - - // Helper method to get table horizontal edges. - private IEnumerable HorizontalLines - { - get - { - for (int i = _columnCount; i < _columnCount + _rowCount - 1; i++) - { - yield return (Rectangle)Children[i]; - } - } - } - - protected override Size MeasureOverride(Size availableSize) - { - // Measure the width of each column, with no horizontal width restrictions. - double[] naturalColumnWidths = new double[_columnCount]; - foreach (FrameworkElement child in ContentChildren) - { - int 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. - double 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 (FrameworkElement child in ContentChildren) - { - int columnIndex = Grid.GetColumn(child); - int rowIndex = Grid.GetRow(child); - child.Measure(new Size(_columnWidths[columnIndex], double.PositiveInfinity)); - _rowHeights[rowIndex] = Math.Max(_rowHeights[rowIndex], child.DesiredSize.Height); - } - - size = new Size( - _columnWidths.Sum() + (_borderThickness * (_columnCount + 1)), - _rowHeights.Sum() + ((_rowCount + 1) * _borderThickness)); - - return size; - } - - protected override Size ArrangeOverride(Size finalSize) - { - if (_columnWidths == null || _rowHeights == null) - { - throw new InvalidOperationException("Expected Measure to be called first."); - } - - // Arrange content. - foreach (FrameworkElement child in ContentChildren) - { - int columnIndex = Grid.GetColumn(child); - int rowIndex = Grid.GetRow(child); - - Rect rect = new(_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 = _borderThickness + _columnWidths[colIndex]; - foreach (Rectangle borderLine in VerticalLines) - { - borderLine.Arrange(new Rect(x, 0, _borderThickness, finalSize.Height)); - if (colIndex >= _columnWidths.Length - 2) - { - break; - } - - colIndex++; - x += _borderThickness + _columnWidths[colIndex]; - } - } - - // Arrange horizontal border elements. - { - int rowIndex = 0; - double y = _borderThickness + _rowHeights[rowIndex]; - foreach (Rectangle borderLine in HorizontalLines) - { - borderLine.Arrange(new Rect(0, y, finalSize.Width, _borderThickness)); - if (rowIndex >= _rowHeights.Length - 2) - { - break; - } - - rowIndex++; - y += _borderThickness + _rowHeights[rowIndex]; - } - } - - if (Children[0] is Border border) - { - border.Arrange(new Rect(new(0, 0), size)); - } - - return finalSize; - } -} diff --git a/src/Symptum.UI/Markdown/TextElements/MyThematicBreak.cs b/src/Symptum.UI/Markdown/TextElements/MyThematicBreak.cs deleted file mode 100644 index 5a873be..0000000 --- a/src/Symptum.UI/Markdown/TextElements/MyThematicBreak.cs +++ /dev/null @@ -1,36 +0,0 @@ -// 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 Microsoft.UI; - -namespace Symptum.UI.Markdown.TextElements; - -internal class MyThematicBreak : IAddChild -{ - private ThematicBreakBlock _thematicBreakBlock; - private SContainer _container = new(); - - public STextElement TextElement - { - get => _container; - } - - public MyThematicBreak(ThematicBreakBlock thematicBreakBlock) - { - _thematicBreakBlock = thematicBreakBlock; - - Border border = new() - { - BorderThickness = new Thickness(1), - Margin = new Thickness(8), - BorderBrush = new SolidColorBrush(Colors.Gray), - Width = 500, - HorizontalAlignment = HorizontalAlignment.Stretch - }; - _container.UIElement = border; - } - - public void AddChild(IAddChild child) { } -} diff --git a/src/Symptum.UI/Markdown/TextElements/ParagraphElement.cs b/src/Symptum.UI/Markdown/TextElements/ParagraphElement.cs new file mode 100644 index 0000000..3ec61ea --- /dev/null +++ b/src/Symptum.UI/Markdown/TextElements/ParagraphElement.cs @@ -0,0 +1,27 @@ +using Markdig.Syntax; + +namespace Symptum.UI.Markdown.TextElements; + +public class ParagraphElement : IAddChild +{ + private ParagraphBlock _paragraphBlock; + private SParagraph _paragraph; + private MarkdownConfiguration _config; + + public STextElement TextElement + { + get => _paragraph; + } + + public ParagraphElement(ParagraphBlock paragraphBlock, MarkdownConfiguration config) + { + _paragraphBlock = paragraphBlock; + _config = config; + _paragraph = new() { TextBlockStyle = config.Themes.BodyTextBlockStyle }; + } + + public void AddChild(IAddChild child) + { + _paragraph.AddInline(child.TextElement); + } +} diff --git a/src/Symptum.UI/Markdown/TextElements/QuoteElement.cs b/src/Symptum.UI/Markdown/TextElements/QuoteElement.cs new file mode 100644 index 0000000..9dada39 --- /dev/null +++ b/src/Symptum.UI/Markdown/TextElements/QuoteElement.cs @@ -0,0 +1,63 @@ +using Markdig.Helpers; +using Markdig.Syntax; + +namespace Symptum.UI.Markdown.TextElements; + +public class QuoteElement : IAddChild +{ + private SContainer _container; + private FlowDocumentElement _flowDocument; + private QuoteBlock _quoteBlock; + + public STextElement TextElement + { + get => _container; + } + + public QuoteElement(QuoteBlock quoteBlock, MarkdownConfiguration config, StringSlice? kind = null) + { + _quoteBlock = quoteBlock; + _container = new(); + + _flowDocument = new FlowDocumentElement(config, false); + AlertKind alertKind = AlertKind.None; + + if (kind != null && kind?.Length < 16) + { + Span upperKind = stackalloc char[kind?.Length ?? 0]; + kind?.AsSpan().ToUpperInvariant(upperKind); + alertKind = upperKind switch + { + "NOTE" => AlertKind.Note, + "TIP" => AlertKind.Tip, + "IMPORTANT" => AlertKind.Important, + "WARNING" => AlertKind.Warning, + "CAUTION" => AlertKind.Caution, + _ => AlertKind.None + }; + } + + QuoteControl quote = new() + { + Kind = alertKind, + Content = _flowDocument.StackPanel + }; + + _container.UIElement = quote; + + quote.Style = alertKind switch + { + AlertKind.Note => config.Themes.NoteQuoteControlStyle, + AlertKind.Tip => config.Themes.TipQuoteControlStyle, + AlertKind.Important => config.Themes.ImportantQuoteControlStyle, + AlertKind.Warning => config.Themes.WarningQuoteControlStyle, + AlertKind.Caution => config.Themes.CautionQuoteControlStyle, + _ => config.Themes.DefaultQuoteControlStyle + }; + } + + public void AddChild(IAddChild child) + { + _flowDocument.AddChild(child); + } +} diff --git a/src/Symptum.UI/Markdown/TextElements/STextElements.cs b/src/Symptum.UI/Markdown/TextElements/STextElements.cs index 4962c42..1fe365d 100644 --- a/src/Symptum.UI/Markdown/TextElements/STextElements.cs +++ b/src/Symptum.UI/Markdown/TextElements/STextElements.cs @@ -1,6 +1,4 @@ -using Microsoft.UI.Text; using Microsoft.UI.Xaml.Documents; -using Windows.UI.Text; namespace Symptum.UI.Markdown.TextElements; @@ -14,20 +12,16 @@ public class SInline : STextElement public class SBlock : STextElement { - public virtual UIElement? GetUIElement() => null; + public virtual UIElement? UIElement { get; set; } } public class SParagraph : SBlock { public List Inlines { get; private set; } = []; - public double FontSize { get; internal set; } + public Style? TextBlockStyle { get; set; } - public Brush? Foreground { get; internal set; } = null; - - public FontWeight FontWeight { get; internal set; } = FontWeights.Normal; - - public TextAlignment TextAlignment { get; internal set; } = TextAlignment.Left; + public TextAlignment TextAlignment { get; set; } = TextAlignment.Left; #region Include UI @@ -40,11 +34,13 @@ public class SParagraph : SBlock #endregion - public override UIElement? GetUIElement() + // This method should be called after all the inlines have been added to the Paragraph and finalized. + // It decides whether the resultant UI should be a TextBlock or a StackPanel based on the inclusion of inline UI. + public void CreateUIElement() { if (!ContainsUI || UIIndices.Count == 0) { - return CreateTextBlock(Inlines); + UIElement = CreateTextBlock(Inlines); } else // Ugly hack to include UIElements in Paragraph { @@ -73,7 +69,25 @@ public class SParagraph : SBlock } } - return wrapPanel; + UIElement = wrapPanel; + } + } + + public void AddInline(STextElement element) + { + if (element is SInline inlineChild) + { + Inlines.Add(inlineChild.Inline); + } + else if (element is SBlock blockChild) + { + if (blockChild is SParagraph _para) + _para.CreateUIElement(); + if (blockChild.UIElement is not UIElement uiElement) return; + + UIIndices.Add(Inlines.Count); + ContainsUI = true; + UIElements.Add(uiElement); } } @@ -81,17 +95,10 @@ private TextBlock CreateTextBlock(List inlines) { TextBlock _textBlock = new() { - FontWeight = FontWeight, - HorizontalAlignment = HorizontalAlignment.Stretch, - TextAlignment = TextAlignment, + Style = TextBlockStyle, TextWrapping = TextWrapping.Wrap }; - if (FontSize > 0) - _textBlock.FontSize = FontSize; - if (Foreground != null) - _textBlock.Foreground = Foreground; - foreach (Inline _inline in inlines) _textBlock.Inlines.Add(_inline); @@ -101,9 +108,4 @@ private TextBlock CreateTextBlock(List inlines) public class SContainer : SBlock { - public UIElement? UIElement { get; set; } - - public bool PutUIInfront { get; internal set; } = false; - - public override UIElement? GetUIElement() => UIElement; } diff --git a/src/Symptum.UI/Markdown/TextElements/TableCellElement.cs b/src/Symptum.UI/Markdown/TextElements/TableCellElement.cs new file mode 100644 index 0000000..ec289c4 --- /dev/null +++ b/src/Symptum.UI/Markdown/TextElements/TableCellElement.cs @@ -0,0 +1,72 @@ +using Markdig.Extensions.Tables; + +namespace Symptum.UI.Markdown.TextElements; + +public class TableCellElement : IAddChild +{ + private TableCell _tableCell; + private FlowDocumentElement _flowDocument; + private bool _isHeader; + private int _columnIndex; + private int _rowIndex; + private Grid _grid; + private SContainer _container = new(); + + public STextElement TextElement + { + 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 TableCellElement(TableCell tableCell, MarkdownConfiguration config, TextAlignment textAlignment, bool isHeader, int columnIndex, int rowIndex) + { + _isHeader = isHeader; + _tableCell = tableCell; + _columnIndex = columnIndex; + _rowIndex = rowIndex; + + _flowDocument = new FlowDocumentElement(config, false); + + if (isHeader) + _flowDocument.TextBlockStyle = config.Themes.TableHeaderTextBlockStyle; + + _flowDocument.StackPanel.HorizontalAlignment = textAlignment switch + { + TextAlignment.Left => HorizontalAlignment.Left, + TextAlignment.Center => HorizontalAlignment.Center, + TextAlignment.Right => HorizontalAlignment.Right, + _ => HorizontalAlignment.Left, + }; + + _grid = new() + { + Style = isHeader ? config.Themes.TableHeaderCellGridStyle : config.Themes.TableCellGridStyle, + }; + _grid.Children.Add(_flowDocument.StackPanel); + _container.UIElement = _grid; + } + + public void AddChild(IAddChild child) + { + _flowDocument.AddChild(child); + } +} diff --git a/src/Symptum.UI/Markdown/TextElements/TableElement.cs b/src/Symptum.UI/Markdown/TextElements/TableElement.cs new file mode 100644 index 0000000..18c209d --- /dev/null +++ b/src/Symptum.UI/Markdown/TextElements/TableElement.cs @@ -0,0 +1,85 @@ +using Markdig.Extensions.Tables; + +namespace Symptum.UI.Markdown.TextElements; + +public class TableElement : IAddChild +{ + private Table _table; + private SContainer _container; + private Grid _grid; + private int _columnCount; + private int _rowCount; + private MarkdownConfiguration _config; + + public STextElement TextElement + { + get => _container; + } + + public TableElement(Table table, MarkdownConfiguration config) + { + _table = table; + _config = config; + _container = new(); + _columnCount = table.FirstOrDefault() is TableRow row ? row.Select(x => x is TableCell cell ? cell.ColumnSpan : 1).Sum() : 0; + _rowCount = table.Count; + + _grid = new() + { + HorizontalAlignment = HorizontalAlignment.Center + }; + + for (int i = 0; i < _columnCount; i++) + { + _grid.ColumnDefinitions.Add(new()); + } + for (int i = 0; i < _rowCount; i++) + { + _grid.RowDefinitions.Add(new()); + } + + _container.UIElement = _grid; + } + + public void AddChild(IAddChild child) + { + if (child is TableCellElement cellChild && cellChild.TextElement is SContainer container && container.UIElement is Grid cell) + { + Grid.SetColumn(cell, cellChild.ColumnIndex); + Grid.SetRow(cell, cellChild.RowIndex); + Grid.SetColumnSpan(cell, cellChild.ColumnSpan); + Grid.SetRowSpan(cell, cellChild.RowSpan); + + cell.BorderThickness = GetBorderThickness(cellChild.RowIndex, cellChild.ColumnIndex); + cell.CornerRadius = GetCornerRadius(cellChild.RowIndex, cellChild.ColumnIndex, cellChild.RowSpan, cellChild.ColumnSpan); + if (cellChild.RowIndex % 2 == 0 && cellChild.RowIndex != 0) + { + cell.Style = _config.Themes.AltTableCellGridStyle; + } + + _grid.Children.Add(cell); + } + } + + private double uniformBorderThickness = 1; + + private Thickness GetBorderThickness(int rowIndex, int columnIndex) + { + double l = columnIndex == 0 ? uniformBorderThickness : 0; + double t = rowIndex == 0 ? uniformBorderThickness : 0; + double r = uniformBorderThickness; + double b = uniformBorderThickness; + return new(l, t, r, b); + } + + private double cornerRadius = 4; + + private CornerRadius GetCornerRadius(int rowIndex, int columnIndex, int rowSpan, int columnSpan) + { + double tl = rowIndex == 0 && columnIndex == 0 ? cornerRadius : 0; + double tr = rowIndex == 0 && columnIndex == _columnCount - columnSpan ? cornerRadius : 0; + double br = rowIndex == _rowCount - rowSpan && columnIndex == _columnCount - columnSpan ? cornerRadius : 0; + double bl = rowIndex == _rowCount - rowSpan && columnIndex == 0 ? cornerRadius : 0; + return new(tl, tr, br, bl); + } +} diff --git a/src/Symptum.UI/Markdown/TextElements/MyTaskListCheckBox.cs b/src/Symptum.UI/Markdown/TextElements/TaskListCheckBoxElement.cs similarity index 56% rename from src/Symptum.UI/Markdown/TextElements/MyTaskListCheckBox.cs rename to src/Symptum.UI/Markdown/TextElements/TaskListCheckBoxElement.cs index f233b27..72d46d5 100644 --- a/src/Symptum.UI/Markdown/TextElements/MyTaskListCheckBox.cs +++ b/src/Symptum.UI/Markdown/TextElements/TaskListCheckBoxElement.cs @@ -1,19 +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.Extensions.TaskLists; using Microsoft.UI; namespace Symptum.UI.Markdown.TextElements; -internal class MyTaskListCheckBox : IAddChild +public class TaskListCheckBoxElement : IAddChild { private TaskList _taskList; + private SContainer _container = new(); - public STextElement TextElement { get; private set; } + public STextElement TextElement + { + get => _container; + } - public MyTaskListCheckBox(TaskList taskList) + public TaskListCheckBoxElement(TaskList taskList) { _taskList = taskList; Grid grid = new() @@ -22,7 +22,8 @@ public MyTaskListCheckBox(TaskList taskList) Height = 16, Margin = new Thickness(2, 2, 2, 0), BorderThickness = new Thickness(1), - BorderBrush = new SolidColorBrush(Colors.Gray) + BorderBrush = new SolidColorBrush(Colors.Gray), + VerticalAlignment = VerticalAlignment.Bottom }; FontIcon icon = new() { @@ -31,10 +32,10 @@ public MyTaskListCheckBox(TaskList taskList) VerticalAlignment = VerticalAlignment.Center, Glyph = "\uE73E" }; - grid.Children.Add(taskList.Checked ? icon : new TextBlock()); + if (taskList.Checked) grid.Children.Add(icon); grid.Padding = new Thickness(0); grid.CornerRadius = new CornerRadius(2); - TextElement = new SContainer() { UIElement = grid, PutUIInfront = true }; + _container.UIElement = grid; } public void AddChild(IAddChild child) diff --git a/src/Symptum.UI/Markdown/TextElements/MyInlineText.cs b/src/Symptum.UI/Markdown/TextElements/TextInlineElement.cs similarity index 57% rename from src/Symptum.UI/Markdown/TextElements/MyInlineText.cs rename to src/Symptum.UI/Markdown/TextElements/TextInlineElement.cs index 3bb5838..df58856 100644 --- a/src/Symptum.UI/Markdown/TextElements/MyInlineText.cs +++ b/src/Symptum.UI/Markdown/TextElements/TextInlineElement.cs @@ -1,12 +1,8 @@ -// 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 Microsoft.UI.Xaml.Documents; namespace Symptum.UI.Markdown.TextElements; -internal class MyInlineText : IAddChild +public class TextInlineElement : IAddChild { private SInline inline; @@ -15,7 +11,7 @@ public STextElement TextElement get => inline; } - public MyInlineText(string text) + public TextInlineElement(string text) { inline = new() { diff --git a/src/Symptum.UI/Markdown/TextElements/ThematicBreakElement.cs b/src/Symptum.UI/Markdown/TextElements/ThematicBreakElement.cs new file mode 100644 index 0000000..12193f9 --- /dev/null +++ b/src/Symptum.UI/Markdown/TextElements/ThematicBreakElement.cs @@ -0,0 +1,27 @@ +using Markdig.Syntax; + +namespace Symptum.UI.Markdown.TextElements; + +public class ThematicBreakElement : IAddChild +{ + private ThematicBreakBlock _thematicBreakBlock; + private SContainer _container = new(); + + public STextElement TextElement + { + get => _container; + } + + public ThematicBreakElement(ThematicBreakBlock thematicBreakBlock, MarkdownConfiguration config) + { + _thematicBreakBlock = thematicBreakBlock; + + Border border = new() + { + Style = config.Themes.ThematicBreakBorderStyle + }; + _container.UIElement = border; + } + + public void AddChild(IAddChild child) { } +} diff --git a/src/Symptum.UI/Symptum.UI.csproj b/src/Symptum.UI/Symptum.UI.csproj index 007d1e9..bfcef8c 100644 --- a/src/Symptum.UI/Symptum.UI.csproj +++ b/src/Symptum.UI/Symptum.UI.csproj @@ -1,6 +1,8 @@ - net9.0;net9.0-ios;net9.0-maccatalyst;net9.0-android;net9.0-windows10.0.22621;;net9.0-desktop + + net9.0;net9.0-ios;net9.0-maccatalyst;net9.0-android;net9.0-windows10.0.22621;;net9.0-desktop + true Library @@ -33,6 +35,12 @@ + + + + + + @@ -41,9 +49,4 @@ - - - MSBuild:Compile - - diff --git a/src/Symptum.UI/Themes/Generic.xaml b/src/Symptum.UI/Themes/Generic.xaml index 579e237..6150b40 100644 --- a/src/Symptum.UI/Themes/Generic.xaml +++ b/src/Symptum.UI/Themes/Generic.xaml @@ -1,8 +1,9 @@  - - - + + + + diff --git a/src/Symptum.UI/Themes/Styles.xaml b/src/Symptum.UI/Themes/Styles.xaml index 0cc61e1..f1a41a9 100644 --- a/src/Symptum.UI/Themes/Styles.xaml +++ b/src/Symptum.UI/Themes/Styles.xaml @@ -1,58 +1,98 @@  - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - + + - - - From 1190f2159834cd0f6ac34aa8a897a114f5f58b25 Mon Sep 17 00:00:00 2001 From: Shankar Date: Tue, 3 Dec 2024 16:16:11 +0530 Subject: [PATCH 2/5] Major Changes - Resources: - Most derivates of MetadataResource now inherit from CategoryResource for reduced boiler-plate code and uniformity with support for serialization. - CategoryResource allows children only if they implement TCondition. - PackageResource is also updated similar to CategoryResource. - Resource JSON Serializers now support polymorphic split JSON. - Subject now allows children other than QuestionBank. - StringExtensions: updated Contains function, added support for matching case and whole word and searching text. - AddNewItemDialog: now supports filtering using AutoSuggestBox. - FindControl: FindFlyout is now a UserControl with added support for matching whole word. - MarkdownEditorPage: now has a Ribbon Control, a status bar, ability to load, edit and save Markdown files and searching text. - ReferenceInline support. - Moved ILinkHandler from MarkdownConfiguration to WinUIRenderer and DocumentOutline to MarkdownTextBlock. --- Directory.Build.props | 2 +- Directory.Packages.props | 17 +- src/Symptum.Common/Helpers/ResourceHelper.cs | 111 ++++- src/Symptum.Common/Symptum.Common.csproj | 4 - .../NutrientQuantityGenerator.cs | 2 +- .../Data/Bibliography/BookReference.cs | 5 +- .../Data/Nutrition/FoodMeasure.cs | 10 +- .../Data/Nutrition/NutritionDataSet.cs | 44 +- .../Data/Nutrition/NutritionPackage.cs | 15 +- src/Symptum.Core/Data/Quantity.cs | 5 +- .../ReferenceValues/ReferenceValueEntry.cs | 5 +- .../ReferenceValues/ReferenceValueFamily.cs | 44 +- .../ReferenceValues/ReferenceValuesPackage.cs | 44 +- .../Extensions/StringExtensions.cs | 105 ++++- src/Symptum.Core/Helpers/FileHelper.cs | 2 + .../Management/Deployment/PackageManager.cs | 5 +- .../Management/Resources/CategoryResource.cs | 54 ++- .../Management/Resources/CsvFileResource.cs | 25 +- .../Management/Resources/FileResource.cs | 44 +- .../Management/Resources/IResource.cs | 5 + .../Management/Resources/ImageFileResource.cs | 20 +- .../Resources/MarkdownFileResource.cs | 15 +- .../Management/Resources/MetadataResource.cs | 23 +- .../Management/Resources/PackageResource.cs | 90 ++-- .../Management/Resources/ResourceBase.cs | 17 +- .../Management/Resources/ResourceManager.cs | 5 +- src/Symptum.Core/Math/NumericalValue.cs | 18 +- .../Serialization/AuthorInfoConverter.cs | 5 +- .../Serialization/QuantityConverter.cs | 5 +- .../Serialization/ResourceConverter.cs | 124 ++++-- src/Symptum.Core/Subjects/Books/Book.cs | 49 +-- .../Subjects/QuestionBanks/QuestionBank.cs | 54 +-- .../QuestionBanks/QuestionBankPaper.cs | 44 +- .../Subjects/QuestionBanks/QuestionEntry.cs | 23 +- .../Subjects/QuestionBanks/QuestionId.cs | 5 +- src/Symptum.Core/Subjects/Subject.cs | 72 +-- src/Symptum.Core/Subjects/SubjectsManager.cs | 10 +- src/Symptum.Editor/App.xaml | 1 + src/Symptum.Editor/Assets/SharedAssets.md | 32 -- .../Common/DefaultIconSources.cs | 7 +- .../Controls/AddNewItemDialog.xaml | 2 +- .../Controls/AddNewItemDialog.xaml.cs | 39 +- src/Symptum.Editor/Controls/FindControl.xaml | 56 +++ .../Controls/FindControl.xaml.cs | 255 +++++++++++ src/Symptum.Editor/Controls/FindFlyout.xaml | 60 --- .../Controls/FindFlyout.xaml.cs | 277 ------------ .../EditorPages/DefaultEditorPage.xaml | 10 +- .../EditorPages/DefaultEditorPage.xaml.cs | 16 +- .../EditorPages/EditorPageBase.cs | 13 - .../EditorPages/EditorPagesManager.cs | 3 +- .../EditorPages/FoodGroupEditorPage.xaml | 8 +- .../EditorPages/FoodGroupEditorPage.xaml.cs | 109 ++--- src/Symptum.Editor/EditorPages/IEditorPage.cs | 2 - .../EditorPages/MarkdownEditorPage.xaml | 236 +++++++++- .../EditorPages/MarkdownEditorPage.xaml.cs | 413 +++++++++++++++++- .../EditorPages/QuestionTopicEditorPage.xaml | 8 +- .../QuestionTopicEditorPage.xaml.cs | 146 +++---- .../ReferenceValueGroupEditorPage.xaml | 8 +- .../ReferenceValueGroupEditorPage.xaml.cs | 109 ++--- src/Symptum.Editor/GlobalUsings.cs | 3 +- src/Symptum.Editor/MainPage.xaml | 18 +- src/Symptum.Editor/MainPage.xaml.cs | 3 +- src/Symptum.Editor/Symptum.Editor.csproj | 5 + .../Reference/ReferenceInline.cs | 13 + .../Reference/ReferenceInlineExtension.cs | 19 + .../Reference/ReferenceInlineParser.cs | 68 +++ src/Symptum.UI/Markdown/DefaultLinkHandler.cs | 6 +- src/Symptum.UI/Markdown/DocumentOutline.cs | 2 +- src/Symptum.UI/Markdown/Extensions.cs | 6 +- .../Markdown/MarkdownConfiguration.cs | 6 - src/Symptum.UI/Markdown/MarkdownTextBlock.md | 1 + .../Markdown/MarkdownTextBlock.xaml.cs | 7 +- src/Symptum.UI/Markdown/QuoteControl.xaml | 5 +- .../Extensions/ReferenceInlineRenderer.cs | 15 + .../ObjectRenderers/HeadingRenderer.cs | 2 +- .../Inlines/LinkInlineRenderer.cs | 4 +- .../Markdown/Renderers/WinUIRenderer.cs | 12 +- .../TextElements/EmphasisInlineElement.cs | 2 - .../TextElements/FlowDocumentElement.cs | 2 - .../Markdown/TextElements/HeadingElement.cs | 4 +- .../TextElements/HyperlinkButtonElement.cs | 25 +- .../Markdown/TextElements/HyperlinkElement.cs | 25 +- .../TextElements/ReferenceInlineElement.cs | 31 ++ .../Markdown/TextElements/TableElement.cs | 3 +- .../TextElements/TaskListCheckBoxElement.cs | 21 +- src/Symptum.UI/Symptum.UI.csproj | 9 +- src/Symptum.UI/Themes/Styles.xaml | 2 +- src/Symptum/Symptum.csproj | 4 + 88 files changed, 1884 insertions(+), 1376 deletions(-) delete mode 100644 src/Symptum.Editor/Assets/SharedAssets.md create mode 100644 src/Symptum.Editor/Controls/FindControl.xaml create mode 100644 src/Symptum.Editor/Controls/FindControl.xaml.cs delete mode 100644 src/Symptum.Editor/Controls/FindFlyout.xaml delete mode 100644 src/Symptum.Editor/Controls/FindFlyout.xaml.cs create mode 100644 src/Symptum.Markdown/Reference/ReferenceInline.cs create mode 100644 src/Symptum.Markdown/Reference/ReferenceInlineExtension.cs create mode 100644 src/Symptum.Markdown/Reference/ReferenceInlineParser.cs create mode 100644 src/Symptum.UI/Markdown/MarkdownTextBlock.md create mode 100644 src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Extensions/ReferenceInlineRenderer.cs create mode 100644 src/Symptum.UI/Markdown/TextElements/ReferenceInlineElement.cs diff --git a/Directory.Build.props b/Directory.Build.props index 8daf569..26e4c37 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -3,7 +3,7 @@ enable enable true - + preview - - - - diff --git a/src/Symptum.Core.SourceGenerators/NutrientQuantityGenerator.cs b/src/Symptum.Core.SourceGenerators/NutrientQuantityGenerator.cs index 6705c97..2fb462c 100644 --- a/src/Symptum.Core.SourceGenerators/NutrientQuantityGenerator.cs +++ b/src/Symptum.Core.SourceGenerators/NutrientQuantityGenerator.cs @@ -82,7 +82,7 @@ private static (string? fieldName, string? propertyName, string? header, string? public void Execute(GeneratorExecutionContext context) { string? nutrientIndexPath = context.AdditionalFiles - .FirstOrDefault(x => Path.GetFileName(x.Path).Equals("NutrientIndex.txt", StringComparison.OrdinalIgnoreCase))?.Path; + .FirstOrDefault(x => Path.GetFileName(x.Path).Equals("NutrientIndex.txt", StringComparison.InvariantCultureIgnoreCase))?.Path; if (nutrientIndexPath == null) return; diff --git a/src/Symptum.Core/Data/Bibliography/BookReference.cs b/src/Symptum.Core/Data/Bibliography/BookReference.cs index d1e7cfe..0b5bf07 100644 --- a/src/Symptum.Core/Data/Bibliography/BookReference.cs +++ b/src/Symptum.Core/Data/Bibliography/BookReference.cs @@ -81,9 +81,6 @@ public override string ToString() return "@book?" + col.ToString() + ParserHelper.BookReferenceDelimiter + Pages; } - public override string GetPreviewText() - { - return $"{Title} by {Authors}, " + + public override string GetPreviewText() => $"{Title} by {Authors}, " + $"Edition: {Edition}, Volume: {Volume}, Pages: {Pages}"; - } } diff --git a/src/Symptum.Core/Data/Nutrition/FoodMeasure.cs b/src/Symptum.Core/Data/Nutrition/FoodMeasure.cs index 4e77cdf..7ce601e 100644 --- a/src/Symptum.Core/Data/Nutrition/FoodMeasure.cs +++ b/src/Symptum.Core/Data/Nutrition/FoodMeasure.cs @@ -52,15 +52,9 @@ public static bool TryParse(string? text, [NotNullWhen(true)] out FoodMeasure? m return parsed; } - public override string ToString() - { - return JsonSerializer.Serialize(this, options); - } + public override string ToString() => JsonSerializer.Serialize(this, options); - public string GetPreviewText() - { - return $"{_title}: {_weight}"; - } + public string GetPreviewText() => $"{_title}: {_weight}"; private static JsonSerializerOptions options = new() { diff --git a/src/Symptum.Core/Data/Nutrition/NutritionDataSet.cs b/src/Symptum.Core/Data/Nutrition/NutritionDataSet.cs index 2590fe4..1e06960 100644 --- a/src/Symptum.Core/Data/Nutrition/NutritionDataSet.cs +++ b/src/Symptum.Core/Data/Nutrition/NutritionDataSet.cs @@ -1,10 +1,10 @@ using System.Collections.ObjectModel; using Symptum.Core.Management.Resources; -using Symptum.Core.Extensions; +using System.Text.Json.Serialization; namespace Symptum.Core.Data.Nutrition; -public class NutritionDataSet : MetadataResource +public class NutritionDataSet : CategoryResource { public NutritionDataSet() { } @@ -16,44 +16,12 @@ public NutritionDataSet(string title) #region Properties - private ObservableCollection? groups; - - public ObservableCollection? Groups + [JsonPropertyName("Groups")] + public override ObservableCollection? Items { - get => groups; - set - { - UnobserveCollection(groups); - SetProperty(ref groups, value); - SetChildrenResources(groups); - } + get => base.Items; + set => base.Items = value; } #endregion - - protected override void OnInitializeResource(IResource? parent) - { - SetChildrenResources(groups); - } - - public override bool CanHandleChildResourceType(Type childResourceType) - { - return childResourceType == typeof(FoodGroup); - } - - public override bool CanAddChildResourceType(Type childResourceType) - { - return childResourceType == typeof(FoodGroup); - } - - protected override void OnAddChildResource(IResource? childResource) - { - Groups ??= []; - Groups.AddItemToListIfNotExists(childResource); - } - - protected override void OnRemoveChildResource(IResource? childResource) - { - Groups.RemoveItemFromListIfExists(childResource); - } } diff --git a/src/Symptum.Core/Data/Nutrition/NutritionPackage.cs b/src/Symptum.Core/Data/Nutrition/NutritionPackage.cs index 51e7f7e..84f1870 100644 --- a/src/Symptum.Core/Data/Nutrition/NutritionPackage.cs +++ b/src/Symptum.Core/Data/Nutrition/NutritionPackage.cs @@ -36,20 +36,9 @@ protected override void OnInitializeResource(IResource? parent) AddChildResourceInternal(DataSet); } - public override bool CanHandleChildResourceType(Type childResourceType) - { - return childResourceType == typeof(NutritionDataSet); - } + public override bool CanHandleChildResourceType(Type childResourceType) => childResourceType == typeof(NutritionDataSet); - public override bool CanAddChildResourceType(Type childResourceType) - { - if (childResourceType == typeof(NutritionDataSet)) - { - return DataSet == null; - } - - return false; - } + public override bool CanAddChildResourceType(Type childResourceType) => DataSet == null; protected override void OnAddChildResource(IResource? childResource) { diff --git a/src/Symptum.Core/Data/Quantity.cs b/src/Symptum.Core/Data/Quantity.cs index ec87e11..ed94ecb 100644 --- a/src/Symptum.Core/Data/Quantity.cs +++ b/src/Symptum.Core/Data/Quantity.cs @@ -65,10 +65,7 @@ public static bool TryParse(string? value, [NotNullWhen(true)] out Quantity? qua return false; } - public override string ToString() - { - return _value + (!string.IsNullOrWhiteSpace(_unit) ? " " + _unit : string.Empty); - } + public override string ToString() => _value + (!string.IsNullOrWhiteSpace(_unit) ? " " + _unit : string.Empty); public static implicit operator Quantity?(string? value) { diff --git a/src/Symptum.Core/Data/ReferenceValues/ReferenceValueEntry.cs b/src/Symptum.Core/Data/ReferenceValues/ReferenceValueEntry.cs index ac6c4d3..171f027 100644 --- a/src/Symptum.Core/Data/ReferenceValues/ReferenceValueEntry.cs +++ b/src/Symptum.Core/Data/ReferenceValues/ReferenceValueEntry.cs @@ -61,10 +61,7 @@ public static bool TryParse(string? text, [NotNullWhen(true)] out ReferenceValue return parsed; } - public override string ToString() - { - return JsonSerializer.Serialize(this, options); - } + public override string ToString() => JsonSerializer.Serialize(this, options); private static JsonSerializerOptions options = new() { diff --git a/src/Symptum.Core/Data/ReferenceValues/ReferenceValueFamily.cs b/src/Symptum.Core/Data/ReferenceValues/ReferenceValueFamily.cs index 98b0ddd..f6fd6bf 100644 --- a/src/Symptum.Core/Data/ReferenceValues/ReferenceValueFamily.cs +++ b/src/Symptum.Core/Data/ReferenceValues/ReferenceValueFamily.cs @@ -1,10 +1,10 @@ using System.Collections.ObjectModel; -using Symptum.Core.Extensions; +using System.Text.Json.Serialization; using Symptum.Core.Management.Resources; namespace Symptum.Core.Data.ReferenceValues; -public class ReferenceValueFamily : MetadataResource +public class ReferenceValueFamily : CategoryResource { public ReferenceValueFamily() { } @@ -16,44 +16,12 @@ public ReferenceValueFamily(string title) #region Properties - private ObservableCollection? groups; - - public ObservableCollection? Groups + [JsonPropertyName("Groups")] + public override ObservableCollection? Items { - get => groups; - set - { - UnobserveCollection(groups); - SetProperty(ref groups, value); - SetChildrenResources(groups); - } + get => base.Items; + set => base.Items = value; } #endregion - - protected override void OnInitializeResource(IResource? parent) - { - SetChildrenResources(groups); - } - - public override bool CanHandleChildResourceType(Type childResourceType) - { - return childResourceType == typeof(ReferenceValueGroup); - } - - public override bool CanAddChildResourceType(Type childResourceType) - { - return childResourceType == typeof(ReferenceValueGroup); - } - - protected override void OnAddChildResource(IResource? childResource) - { - Groups ??= []; - Groups.AddItemToListIfNotExists(childResource); - } - - protected override void OnRemoveChildResource(IResource? childResource) - { - Groups.RemoveItemFromListIfExists(childResource); - } } diff --git a/src/Symptum.Core/Data/ReferenceValues/ReferenceValuesPackage.cs b/src/Symptum.Core/Data/ReferenceValues/ReferenceValuesPackage.cs index 5cc4660..9934730 100644 --- a/src/Symptum.Core/Data/ReferenceValues/ReferenceValuesPackage.cs +++ b/src/Symptum.Core/Data/ReferenceValues/ReferenceValuesPackage.cs @@ -1,11 +1,11 @@ using System.Collections.ObjectModel; -using Symptum.Core.Extensions; +using System.Text.Json.Serialization; using Symptum.Core.Management.Resources; using Symptum.Core.Serialization; namespace Symptum.Core.Data.ReferenceValues; -public class ReferenceValuesPackage : PackageResource +public class ReferenceValuesPackage : PackageResource { public ReferenceValuesPackage() { } @@ -17,45 +17,13 @@ public ReferenceValuesPackage(string title) #region Properties - private ObservableCollection? families; - + [JsonPropertyName("Families")] [ListOfMetadataResource] - public ObservableCollection? Families + public override ObservableCollection? Contents { - get => families; - set - { - UnobserveCollection(families); - SetProperty(ref families, value); - SetChildrenResources(families); - } + get => base.Contents; + set => base.Contents = value; } #endregion - - protected override void OnInitializeResource(IResource? parent) - { - SetChildrenResources(families); - } - - public override bool CanHandleChildResourceType(Type childResourceType) - { - return childResourceType == typeof(ReferenceValueFamily); - } - - public override bool CanAddChildResourceType(Type childResourceType) - { - return childResourceType == typeof(ReferenceValueFamily); - } - - protected override void OnAddChildResource(IResource? childResource) - { - Families ??= []; - Families?.AddItemToListIfNotExists(childResource); - } - - protected override void OnRemoveChildResource(IResource? childResource) - { - Families.RemoveItemFromListIfExists(childResource); - } } diff --git a/src/Symptum.Core/Extensions/StringExtensions.cs b/src/Symptum.Core/Extensions/StringExtensions.cs index 6224b11..1ac33c1 100644 --- a/src/Symptum.Core/Extensions/StringExtensions.cs +++ b/src/Symptum.Core/Extensions/StringExtensions.cs @@ -1,3 +1,5 @@ +using System; + namespace Symptum.Core.Extensions; public static class StringExtensions @@ -13,26 +15,99 @@ public static class StringExtensions /// otherwise, . public static bool Contains(this string? string1, string? string2, int offset, char? endChar = null) { - if (string1 != null && string2 != null) + if (string1 == null || string2 == null + || offset < 0 || offset >= string2.Length) + return false; + + for (int i = offset; i < string2.Length; i++) + { + if (string2[i] != string1[i]) + return false; + } + + if (string2.Length == string1.Length || endChar == null + || (string1.Length > string2.Length && string1[string2.Length] == endChar)) + return true; + + return false; + } + + public static bool Contains(this string? text, string? value, bool matchCase = false, bool matchWholeWord = false) + { + if (text == null || value == null || value.Length > text.Length) + return false; + + StringComparison comparisonType = matchCase ? StringComparison.InvariantCulture : StringComparison.InvariantCultureIgnoreCase; + + ReadOnlySpan span = text.AsSpan(); + ReadOnlySpan searchSpan = value.AsSpan(); + + int searchLength = searchSpan.Length; + int textLength = span.Length; + + for (int _start = 0; _start <= textLength - searchLength; _start++) + { + if (span.Slice(_start, searchLength).Equals(searchSpan, comparisonType) + && (!matchWholeWord || IsWholeWordMatch(span, _start, searchLength))) + return true; + } + + return false; + } + + public static int[] SearchTextAndFindAllMatches(this string? text, string? searchText, int searchStart = 0, int searchEnd = 0, bool matchCase = false, bool matchWholeWord = false) + { + if (string.IsNullOrWhiteSpace(text) || string.IsNullOrWhiteSpace(searchText) || searchText.Length > text.Length || searchStart < 0) + return []; + + StringComparison comparisonType = matchCase ? StringComparison.InvariantCulture : StringComparison.InvariantCultureIgnoreCase; + + ReadOnlySpan span = text.AsSpan(); + ReadOnlySpan searchSpan = searchText.AsSpan(); + + List results = []; + + int searchLength = searchSpan.Length; + int textLength = searchEnd > 0 ? searchEnd : span.Length; + + for (int _start = searchStart; _start <= textLength - searchLength; _start++) { - var span1 = string1.AsSpan(); - var span2 = string2.AsSpan(); + if (span.Slice(_start, searchLength).Equals(searchSpan, comparisonType) + && (!matchWholeWord || IsWholeWordMatch(span, _start, searchLength))) + results.Add(_start); + } + + return [.. results]; + } - for (int i = offset; i < span2.Length; i++) + private static bool IsWholeWordMatch(ReadOnlySpan span, int start, int length) + { + int end = start + length; + return (start == 0 || !char.IsLetterOrDigit(span[start - 1])) && + (end == span.Length || !char.IsLetterOrDigit(span[end])); + } + + public static (int lineIndex, int columnIndex) GetLineAndColumnIndex(this string? text, int position) + { + if (string.IsNullOrEmpty(text) || position < 0 || position > text.Length) + return (0, 0); + + int line = 1; + int lastLineStart = 0; + + for (int i = 1; i <= position; i++) + { + if (i > 1 && text[i - 1] == '\r' || text[i - 1] == '\n') { - if (span2[i] != span1[i]) - break; - - if (i == span2.Length - 1) - { - if (span2.Length == span1.Length || - endChar == null || - (span1.Length > i && span1[i + 1] == endChar)) - return true; - } + line++; + lastLineStart = i; + + // Skip the \n in CRLF + if (text[i - 1] == '\r' && i < text.Length && text[i] == '\n') + i++; } } - return false; + return (line, position - lastLineStart + 1); } } diff --git a/src/Symptum.Core/Helpers/FileHelper.cs b/src/Symptum.Core/Helpers/FileHelper.cs index d979639..33265d1 100644 --- a/src/Symptum.Core/Helpers/FileHelper.cs +++ b/src/Symptum.Core/Helpers/FileHelper.cs @@ -10,6 +10,8 @@ public static class FileHelper public const string CsvFileExtension = ".csv"; + public const string MarkdownFileExtension = ".md"; + public const string PackageFileExtension = ".zip"; public static (string folder, string fileName, string extension) GetDetailsFromFilePath(string? filePath) diff --git a/src/Symptum.Core/Management/Deployment/PackageManager.cs b/src/Symptum.Core/Management/Deployment/PackageManager.cs index 43d7e2c..0475239 100644 --- a/src/Symptum.Core/Management/Deployment/PackageManager.cs +++ b/src/Symptum.Core/Management/Deployment/PackageManager.cs @@ -7,10 +7,7 @@ public class PackageManager { private static Func>? _loadPackageCallback; - public static void Initialize(Func> loadPackageCallback) - { - _loadPackageCallback = loadPackageCallback; - } + public static void Initialize(Func> loadPackageCallback) => _loadPackageCallback = loadPackageCallback; #region Dependency Resolution diff --git a/src/Symptum.Core/Management/Resources/CategoryResource.cs b/src/Symptum.Core/Management/Resources/CategoryResource.cs index fe5fa72..859bcfe 100644 --- a/src/Symptum.Core/Management/Resources/CategoryResource.cs +++ b/src/Symptum.Core/Management/Resources/CategoryResource.cs @@ -1,10 +1,11 @@ using System.Collections.ObjectModel; +using System.Text.Json.Serialization; using Symptum.Core.Extensions; using Symptum.Core.Serialization; namespace Symptum.Core.Management.Resources; -public class CategoryResource : CategoryResource +public sealed class CategoryResource : CategoryResource, ISubjectCategoryResource { [ListOfMetadataResource] public override ObservableCollection? Items @@ -14,9 +15,27 @@ public override ObservableCollection? Items } } -public class MarkdownCategoryResource : CategoryResource { } +public sealed class MarkdownCategoryResource : CategoryResource, ISubjectCategoryResource +{ + [JsonPropertyName("Documents")] + public override ObservableCollection? Items + { + get => base.Items; + set => base.Items = value; + } +} + +public sealed class ImageCategoryResource : CategoryResource, ISubjectCategoryResource +{ + [JsonPropertyName("Images")] + public override ObservableCollection? Items + { + get => base.Items; + set => base.Items = value; + } +} -public class ImageCategoryResource : CategoryResource { } +public interface ISubjectCategoryResource { } public class CategoryResource : MetadataResource where T : IResource { @@ -24,6 +43,7 @@ public class CategoryResource : MetadataResource where T : IResource private ObservableCollection? items; + [JsonIgnore] public virtual ObservableCollection? Items { get => items; @@ -37,20 +57,14 @@ public virtual ObservableCollection? Items #endregion - protected override void OnInitializeResource(IResource? parent) - { - SetChildrenResources(Items); - } + protected override void OnInitializeResource(IResource? parent) => SetChildrenResources(Items); - public override bool CanHandleChildResourceType(Type childResourceType) - { - return typeof(T).IsAssignableFrom(childResourceType); - } + protected virtual bool ChildRestraint(Type childResourceType) => true; - public override bool CanAddChildResourceType(Type childResourceType) - { - return CanHandleChildResourceType(childResourceType); - } + public override bool CanHandleChildResourceType(Type childResourceType) => + typeof(T).IsAssignableFrom(childResourceType) && ChildRestraint(childResourceType); + + public override bool CanAddChildResourceType(Type childResourceType) => CanHandleChildResourceType(childResourceType); protected override void OnAddChildResource(IResource? childResource) { @@ -58,8 +72,10 @@ protected override void OnAddChildResource(IResource? childResource) Items.AddItemToListIfNotExists(childResource); } - protected override void OnRemoveChildResource(IResource? childResource) - { - Items.RemoveItemFromListIfExists(childResource); - } + protected override void OnRemoveChildResource(IResource? childResource) => Items.RemoveItemFromListIfExists(childResource); +} + +public class CategoryResource : CategoryResource where T : IResource +{ + protected override bool ChildRestraint(Type childResourceType) => typeof(TCondition).IsAssignableFrom(childResourceType); } diff --git a/src/Symptum.Core/Management/Resources/CsvFileResource.cs b/src/Symptum.Core/Management/Resources/CsvFileResource.cs index 9914c56..b62c412 100644 --- a/src/Symptum.Core/Management/Resources/CsvFileResource.cs +++ b/src/Symptum.Core/Management/Resources/CsvFileResource.cs @@ -8,24 +8,13 @@ public abstract class CsvFileResource : FileResource [JsonIgnore] public override ContentFileType FileType { get; } = ContentFileType.Csv; - protected override void OnReadFileText(string text) - { - OnReadCSV(text); - } - - protected override void OnReadFileStream(Stream stream) - { - } - - protected override string OnWriteFileText() - { - return OnWriteCSV(); - } - - protected override Stream? OnWriteFileStream() - { - return null; - } + protected override void OnReadFileText(string text) => OnReadCSV(text); + + protected override void OnReadFileStream(Stream stream) { } + + protected override string OnWriteFileText() => OnWriteCSV(); + + protected override Stream? OnWriteFileStream() => null; protected abstract void OnReadCSV(string csv); diff --git a/src/Symptum.Core/Management/Resources/FileResource.cs b/src/Symptum.Core/Management/Resources/FileResource.cs index 536f3eb..d127b37 100644 --- a/src/Symptum.Core/Management/Resources/FileResource.cs +++ b/src/Symptum.Core/Management/Resources/FileResource.cs @@ -65,29 +65,15 @@ public string? FilePath #endregion - protected override void OnInitializeResource(IResource? parent) - { - } + protected override void OnInitializeResource(IResource? parent) { } - internal void ReadFileText(string content) - { - OnReadFileText(content); - } + internal void ReadFileText(string content) => OnReadFileText(content); - internal void ReadFileStream(Stream stream) - { - OnReadFileStream(stream); - } + internal void ReadFileStream(Stream stream) => OnReadFileStream(stream); - internal string WriteFileText() - { - return OnWriteFileText(); - } + internal string WriteFileText() => OnWriteFileText(); - internal Stream? WriteFileStream() - { - return null; - } + internal Stream? WriteFileStream() => OnWriteFileStream(); protected abstract void OnReadFileText(string content); @@ -105,25 +91,13 @@ internal string WriteFileText() [JsonIgnore] public override bool CanHandleChildren => false; - public override bool CanHandleChildResourceType(Type childResourceType) - { - return false; - } + public override bool CanHandleChildResourceType(Type childResourceType) => false; - public override bool CanAddChildResourceType(Type childResourceType) - { - return false; - } + public override bool CanAddChildResourceType(Type childResourceType) => false; - protected override void OnAddChildResource(IResource? childResource) - { - throw new NotImplementedException(); - } + protected override void OnAddChildResource(IResource? childResource) => throw new NotImplementedException(); - protected override void OnRemoveChildResource(IResource? childResource) - { - throw new NotImplementedException(); - } + protected override void OnRemoveChildResource(IResource? childResource) => throw new NotImplementedException(); #endregion } diff --git a/src/Symptum.Core/Management/Resources/IResource.cs b/src/Symptum.Core/Management/Resources/IResource.cs index 88e8597..c6baee7 100644 --- a/src/Symptum.Core/Management/Resources/IResource.cs +++ b/src/Symptum.Core/Management/Resources/IResource.cs @@ -1,3 +1,5 @@ +using System.Text.Json.Serialization; + namespace Symptum.Core.Management.Resources; public interface IResource @@ -10,14 +12,17 @@ public interface IResource // Will be used mostly for navigation, will be overlapping with Id in most cases but Id will be different in case of cross linking and embedding public Uri? Uri { get; set; } // symptum://subjects/an/sm/notes/abdomen/liver; symptum://subjects/an/qbank/1/abdomen#S_AN_12.3.4 + [JsonIgnore] public IResource? ParentResource { get; } // Id: Subjects.Anatomy.StudyMaterials.Notes.Abdomen + [JsonIgnore] public IReadOnlyList? ChildrenResources { get; } // null if end resource //public IList? Dependencies { get; set; } //public IList? DependencyIds { get; set; } + [JsonIgnore] public bool CanHandleChildren { get; } public void InitializeResource(IResource? parent); diff --git a/src/Symptum.Core/Management/Resources/ImageFileResource.cs b/src/Symptum.Core/Management/Resources/ImageFileResource.cs index a0b53f8..da6784a 100644 --- a/src/Symptum.Core/Management/Resources/ImageFileResource.cs +++ b/src/Symptum.Core/Management/Resources/ImageFileResource.cs @@ -7,23 +7,11 @@ public class ImageFileResource : FileResource [JsonIgnore] public override ContentFileType FileType { get; } = ContentFileType.Image; - protected override void OnReadFileStream(Stream stream) - { - throw new NotImplementedException(); - } + protected override void OnReadFileStream(Stream stream) => throw new NotImplementedException(); - protected override void OnReadFileText(string content) - { - throw new NotImplementedException(); - } + protected override void OnReadFileText(string content) => throw new NotImplementedException(); - protected override Stream? OnWriteFileStream() - { - throw new NotImplementedException(); - } + protected override Stream? OnWriteFileStream() => throw new NotImplementedException(); - protected override string OnWriteFileText() - { - throw new NotImplementedException(); - } + protected override string OnWriteFileText() => throw new NotImplementedException(); } diff --git a/src/Symptum.Core/Management/Resources/MarkdownFileResource.cs b/src/Symptum.Core/Management/Resources/MarkdownFileResource.cs index 23c5e0a..ef03264 100644 --- a/src/Symptum.Core/Management/Resources/MarkdownFileResource.cs +++ b/src/Symptum.Core/Management/Resources/MarkdownFileResource.cs @@ -1,9 +1,22 @@ using System.Text.Json.Serialization; +using CommunityToolkit.Mvvm.ComponentModel; namespace Symptum.Core.Management.Resources; -public abstract class MarkdownFileResource : FileResource +public sealed partial class MarkdownFileResource : FileResource { [JsonIgnore] public override ContentFileType FileType { get; } = ContentFileType.Markdown; + + [JsonIgnore] + [ObservableProperty] + public partial string Markdown { get; set; } + + protected override void OnReadFileText(string content) => Markdown = content; + + protected override void OnReadFileStream(Stream stream) { } + + protected override string OnWriteFileText() => Markdown; + + protected override Stream? OnWriteFileStream() => null; } diff --git a/src/Symptum.Core/Management/Resources/MetadataResource.cs b/src/Symptum.Core/Management/Resources/MetadataResource.cs index 7384b2c..c13b823 100644 --- a/src/Symptum.Core/Management/Resources/MetadataResource.cs +++ b/src/Symptum.Core/Management/Resources/MetadataResource.cs @@ -1,10 +1,17 @@ using System.Text.Json.Serialization; using Symptum.Core.Management.Navigation; using Symptum.Core.Serialization; +using Symptum.Core.Subjects.QuestionBanks; namespace Symptum.Core.Management.Resources; -public abstract class MetadataResource : NavigableResource +[JsonPolymorphic(TypeDiscriminatorPropertyName = "$type", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToNearestAncestor)] +[JsonDerivedType(typeof(QuestionBank), "questionBank")] +[JsonDerivedType(typeof(CategoryResource), "category")] +[JsonDerivedType(typeof(ImageCategoryResource), "imageCategory")] +[JsonDerivedType(typeof(MarkdownCategoryResource), "markdownCategory")] +public abstract class MetadataResource : NavigableResource, IMetadataResource { #region Properties @@ -15,7 +22,7 @@ public abstract class MetadataResource : NavigableResource public string? MetadataPath { get; set; } [JsonIgnore] - public bool IsMetadataLoaded { get; internal set; } = true; + public bool IsMetadataLoaded { get; set; } = true; #endregion @@ -26,3 +33,15 @@ internal void LoadMetadata(string metadata) IsMetadataLoaded = true; } } + +public interface IMetadataResource : IResource +{ + [JsonIgnore] + public bool SplitMetadata { get; set; } + + [JsonIgnore] + public string? MetadataPath { get; set; } + + [JsonIgnore] + public bool IsMetadataLoaded { get; set; } +} diff --git a/src/Symptum.Core/Management/Resources/PackageResource.cs b/src/Symptum.Core/Management/Resources/PackageResource.cs index 4ea9eb0..ffe434a 100644 --- a/src/Symptum.Core/Management/Resources/PackageResource.cs +++ b/src/Symptum.Core/Management/Resources/PackageResource.cs @@ -1,7 +1,10 @@ +using System.Collections.ObjectModel; using System.Text.Json.Serialization; +using CommunityToolkit.Mvvm.ComponentModel; using Symptum.Core.Data; using Symptum.Core.Data.Nutrition; using Symptum.Core.Data.ReferenceValues; +using Symptum.Core.Extensions; using Symptum.Core.Management.Deployment; using Symptum.Core.Subjects; @@ -12,67 +15,72 @@ namespace Symptum.Core.Management.Resources; [JsonDerivedType(typeof(Subject), "subject")] [JsonDerivedType(typeof(ReferenceValuesPackage), "referenceValues")] [JsonDerivedType(typeof(NutritionPackage), "nutrition")] -public abstract class PackageResource : MetadataResource, IPackageResource +public abstract partial class PackageResource : MetadataResource, IPackageResource { #region Properties - private string? description; + [ObservableProperty] + public partial string? Description { get; set; } - public string? Description - { - get => description; - set => SetProperty(ref description, value); - } + [ObservableProperty] + public partial Version? Version { get; set; } - private Version? version; + [ObservableProperty] + public partial IList? Authors { get; set; } - public Version? Version - { - get => version; - set => SetProperty(ref version, value); - } + [JsonIgnore] + [ObservableProperty] + public partial IList? Dependencies { get; set; } - private IList? authors; + [JsonPropertyName(nameof(Dependencies))] + [ObservableProperty] + public partial IList? DependencyIds { get; set; } - public IList? Authors - { - get => authors; - set => SetProperty(ref authors, value); - } + [ObservableProperty] + public partial IList? Tags { get; set; } + + #endregion +} - private IList? dependencies; +public abstract class PackageResource : PackageResource where T : IResource +{ + #region Properties + + private ObservableCollection? contents; [JsonIgnore] - public IList? Dependencies + public virtual ObservableCollection? Contents { - get => dependencies; - set => SetProperty(ref dependencies, value); + get => contents; + set + { + UnobserveCollection(contents); + SetProperty(ref contents, value); + SetChildrenResources(contents); + } } - private IList? dependencyIds; + #endregion - [JsonPropertyName(nameof(Dependencies))] - public IList? DependencyIds - { - get => dependencyIds; - set => SetProperty(ref dependencyIds, value); - } + protected override void OnInitializeResource(IResource? parent) => SetChildrenResources(Contents); - //private IList? contents; + protected virtual bool ChildRestraint(Type childResourceType) => true; - //public IList? Contents - //{ - // get => contents; - // set => SetProperty(ref contents, value); - //} + public override bool CanHandleChildResourceType(Type childResourceType) => + typeof(T).IsAssignableFrom(childResourceType) && ChildRestraint(childResourceType); - private IList? tags; + public override bool CanAddChildResourceType(Type childResourceType) => CanHandleChildResourceType(childResourceType); - public IList? Tags + protected override void OnAddChildResource(IResource? childResource) { - get => tags; - set => SetProperty(ref tags, value); + Contents ??= []; + Contents.AddItemToListIfNotExists(childResource); } - #endregion + protected override void OnRemoveChildResource(IResource? childResource) => Contents.RemoveItemFromListIfExists(childResource); +} + +public abstract class PackageResource : PackageResource where T : IResource +{ + protected override bool ChildRestraint(Type childResourceType) => typeof(TCondition).IsAssignableFrom(childResourceType); } diff --git a/src/Symptum.Core/Management/Resources/ResourceBase.cs b/src/Symptum.Core/Management/Resources/ResourceBase.cs index 1d15349..09db4f6 100644 --- a/src/Symptum.Core/Management/Resources/ResourceBase.cs +++ b/src/Symptum.Core/Management/Resources/ResourceBase.cs @@ -8,7 +8,7 @@ namespace Symptum.Core.Management.Resources; // This is the base implementation of IResource, it doesn't implement INavigable. -// This class will contain all the properties, logic for loading, adding and removing children resources. +// This class contains all the properties, logic for loading, adding and removing children resources. public abstract class ResourceBase : ObservableObject, IResource { #region Properties @@ -112,10 +112,7 @@ public void AddChildResource(IResource? childResource) childResource?.InitializeResource(this); // Temporary } - public void RemoveChildResource(IResource? childResource) - { - OnRemoveChildResource(childResource); - } + public void RemoveChildResource(IResource? childResource) => OnRemoveChildResource(childResource); protected abstract void OnAddChildResource(IResource? childResource); @@ -132,10 +129,7 @@ protected void AddChildrenResourcesInternal(IList? children) } } - protected void AddChildResourceInternal(IResource? childResource) - { - childrenResources?.AddItemToListIfNotExists(childResource); - } + protected void AddChildResourceInternal(IResource? childResource) => childrenResources?.AddItemToListIfNotExists(childResource); protected void RemoveChildrenResourcesInternal(IList? children) { @@ -148,10 +142,7 @@ protected void RemoveChildrenResourcesInternal(IList? children) } } - protected void RemoveChildResourceInternal(IResource? childResource) - { - childrenResources?.RemoveItemFromListIfExists(childResource); - } + protected void RemoveChildResourceInternal(IResource? childResource) => childrenResources?.RemoveItemFromListIfExists(childResource); protected void SetChildrenResources(ObservableCollection? collection) where T : IResource { diff --git a/src/Symptum.Core/Management/Resources/ResourceManager.cs b/src/Symptum.Core/Management/Resources/ResourceManager.cs index 955fec2..ebb0d3a 100644 --- a/src/Symptum.Core/Management/Resources/ResourceManager.cs +++ b/src/Symptum.Core/Management/Resources/ResourceManager.cs @@ -50,10 +50,7 @@ public static string GetResourceFilePath(IResource? resource, string? extension) public static PackageResource? LoadPackageFromMetadata(string metadata) => JsonSerializer.Deserialize(metadata); - public static void LoadResourceMetadata(MetadataResource? resource, string metadata) - { - resource?.LoadMetadata(metadata); - } + public static void LoadResourceMetadata(MetadataResource? resource, string metadata) => resource?.LoadMetadata(metadata); private static readonly JsonSerializerOptions options = new() { diff --git a/src/Symptum.Core/Math/NumericalValue.cs b/src/Symptum.Core/Math/NumericalValue.cs index fde5e1d..e8b9799 100644 --- a/src/Symptum.Core/Math/NumericalValue.cs +++ b/src/Symptum.Core/Math/NumericalValue.cs @@ -161,24 +161,18 @@ public override string ToString() return string.Empty; } - public override bool Equals(object? obj) + public override bool Equals(object? obj) => obj switch { - return obj switch - { - NumericalValue value => Equals(value), - double number => Contains(number), - _ => false - }; - } + NumericalValue value => Equals(value), + double number => Contains(number), + _ => false + }; public override int GetHashCode() => HashCode.Combine(Value, IsInterval, Minimum, IncludesMinimum, Maximum, IncludesMaximum, IsErrorInterval, Error); - public bool Equals(NumericalValue other) - { - return IsInterval == other.IsInterval && IncludesMinimum == other.IncludesMinimum && IncludesMaximum == other.IncludesMaximum && + public bool Equals(NumericalValue other) => IsInterval == other.IsInterval && IncludesMinimum == other.IncludesMinimum && IncludesMaximum == other.IncludesMaximum && Value.Equals(other.Value) && Minimum.Equals(other.Minimum) && Maximum.Equals(other.Maximum) && IsErrorInterval == other.IsErrorInterval && Error.Equals(other.Error); - } public bool Contains(double value) { diff --git a/src/Symptum.Core/Serialization/AuthorInfoConverter.cs b/src/Symptum.Core/Serialization/AuthorInfoConverter.cs index 5bf4b65..2d6bc03 100644 --- a/src/Symptum.Core/Serialization/AuthorInfoConverter.cs +++ b/src/Symptum.Core/Serialization/AuthorInfoConverter.cs @@ -18,8 +18,5 @@ public override AuthorInfo Read(ref Utf8JsonReader reader, Type typeToConvert, J return default; } - public override void Write(Utf8JsonWriter writer, AuthorInfo value, JsonSerializerOptions options) - { - writer.WriteStringValue(value.ToString()); - } + public override void Write(Utf8JsonWriter writer, AuthorInfo value, JsonSerializerOptions options) => writer.WriteStringValue(value.ToString()); } diff --git a/src/Symptum.Core/Serialization/QuantityConverter.cs b/src/Symptum.Core/Serialization/QuantityConverter.cs index 518caaa..7c4c07e 100644 --- a/src/Symptum.Core/Serialization/QuantityConverter.cs +++ b/src/Symptum.Core/Serialization/QuantityConverter.cs @@ -18,8 +18,5 @@ internal class QuantityJsonConverter : JsonConverter return null; } - public override void Write(Utf8JsonWriter writer, Quantity value, JsonSerializerOptions options) - { - writer.WriteStringValue(value?.ToString()); - } + public override void Write(Utf8JsonWriter writer, Quantity value, JsonSerializerOptions options) => writer.WriteStringValue(value?.ToString()); } diff --git a/src/Symptum.Core/Serialization/ResourceConverter.cs b/src/Symptum.Core/Serialization/ResourceConverter.cs index 92285e9..9f9dfb4 100644 --- a/src/Symptum.Core/Serialization/ResourceConverter.cs +++ b/src/Symptum.Core/Serialization/ResourceConverter.cs @@ -1,5 +1,5 @@ using System.Collections; -using System.Diagnostics; +using System.Reflection; using System.Text.Json; using System.Text.Json.Serialization; using Symptum.Core.Management.Resources; @@ -8,29 +8,25 @@ namespace Symptum.Core.Serialization; ///

-/// Converts derivates of to or from JSON. -/// This converter will check the property. +/// Converts derivates of to or from JSON. +/// This converter will check the property. /// If it's set to , then the converter will use a path to another JSON file where it will be stored. /// -/// Derivate of -public class MetadataResourceConverter : JsonConverter where T : MetadataResource +/// Derivate of +public class MetadataResourceConverter : JsonConverter where T : IMetadataResource { public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.TokenType == JsonTokenType.String) { - string json = reader.GetString() ?? string.Empty; - if (json.StartsWith(PathSeparator)) + string? json = reader.GetString(); + (Type? derivedType, string? filePath) = MetadataSerializationHelper.ParseSplitJson(json); + if (derivedType != null && Activator.CreateInstance(derivedType) is T obj) { - string filePath = json; - Debug.WriteLine(filePath); - if (Activator.CreateInstance(typeof(T)) is T obj) - { - obj.SplitMetadata = true; - obj.MetadataPath = filePath; - obj.IsMetadataLoaded = false; - return obj; - } + obj.SplitMetadata = true; + obj.MetadataPath = filePath; + obj.IsMetadataLoaded = false; + return obj; } } @@ -44,7 +40,8 @@ public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions if (value.SplitMetadata) { string filePath = ResourceManager.GetResourceFilePath(value, JsonFileExtension); - writer.WriteStringValue(filePath); + string json = MetadataSerializationHelper.GetStronglyTypedJsonFilePath(value, filePath); + writer.WriteStringValue(json); value.MetadataPath = filePath; } else @@ -55,13 +52,13 @@ public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions } /// -/// Converts a list of derivates of to or from JSON. -/// This converter will check the property of each item in a list. +/// Converts a list of derivates of to or from JSON. +/// This converter will check the property of each item in a list. /// If it's set to , then the converter will use a path to another JSON file where it will be stored. /// /// is -/// is -public class ListOfMetadataResourceConverter : JsonConverter where TList : IList where TResource : MetadataResource +/// is +public class ListOfMetadataResourceConverter : JsonConverter where TList : IList where TResource : IMetadataResource { public override TList? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { @@ -81,18 +78,14 @@ public class ListOfMetadataResourceConverter : JsonConverter(json); + if (derivedType != null && Activator.CreateInstance(derivedType) is TResource obj) { - string filePath = json; - Debug.WriteLine(filePath); - if (Activator.CreateInstance(typeof(TResource)) is TResource obj) - { - obj.SplitMetadata = true; - obj.MetadataPath = filePath; - obj.IsMetadataLoaded = false; - list.Add(obj); - } + obj.SplitMetadata = true; + obj.MetadataPath = filePath; + obj.IsMetadataLoaded = false; + list.Add(obj); } } else if (JsonSerializer.Deserialize(ref reader, options) is TResource obj) @@ -117,7 +110,8 @@ public override void Write(Utf8JsonWriter writer, TList value, JsonSerializerOpt if (item.SplitMetadata) { string filePath = ResourceManager.GetResourceFilePath(item, JsonFileExtension); - writer.WriteStringValue(filePath); + string json = MetadataSerializationHelper.GetStronglyTypedJsonFilePath(item, filePath); + writer.WriteStringValue(json); item.MetadataPath = filePath; } else @@ -131,15 +125,15 @@ public override void Write(Utf8JsonWriter writer, TList value, JsonSerializerOpt } /// -/// Specifies the property is a derivate of . -/// Must be placed on all properties of derivates of to support splitting Metadata. +/// Specifies the property is a derivate of . +/// Must be placed on all properties of derivates of to support splitting Metadata. /// [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] public class MetadataResourceAttribute : JsonConverterAttribute { public override JsonConverter? CreateConverter(Type typeToConvert) { - if (!typeof(MetadataResource).IsAssignableFrom(typeToConvert)) + if (!typeof(IMetadataResource).IsAssignableFrom(typeToConvert)) throw new NotSupportedException(); return Activator.CreateInstance(typeof(MetadataResourceConverter<>).MakeGenericType([typeToConvert])) as JsonConverter; @@ -163,9 +157,65 @@ public class ListOfMetadataResourceAttribute : JsonConverterAttribute Type elementType = typeToConvert.GetGenericArguments()[0]; - if (!typeof(MetadataResource).IsAssignableFrom(elementType)) + if (!typeof(IMetadataResource).IsAssignableFrom(elementType)) throw new NotSupportedException(); return Activator.CreateInstance(typeof(ListOfMetadataResourceConverter<,>).MakeGenericType([typeToConvert, elementType])) as JsonConverter; } } + +internal static class MetadataSerializationHelper +{ + public const char TypeDiscriminatorChar = '$'; + + private static Dictionary derivedTypeDiscriminators = []; + + public static Dictionary DerivedTypeDiscriminators { get => derivedTypeDiscriminators; } + + static MetadataSerializationHelper() + { + var attrs = typeof(MetadataResource).GetCustomAttributes(); + foreach (var attr in attrs) + { + derivedTypeDiscriminators.Add(attr.DerivedType, attr.TypeDiscriminator as string); + } + } + + public static string GetStronglyTypedJsonFilePath(TResource item, string filePath) where TResource : IMetadataResource + { + if (typeof(TResource) == typeof(MetadataResource) + || typeof(TResource) == typeof(IMetadataResource)) // Not strongly typed + { + if (DerivedTypeDiscriminators.TryGetValue(item.GetType(), out string? discriminator)) + { + return TypeDiscriminatorChar + discriminator + filePath; + } + } + + return filePath; + } + + public static (Type? derivedType, string? filePath) ParseSplitJson(string? json) where TResource : IMetadataResource + { + if (!string.IsNullOrWhiteSpace(json)) + { + if (json.StartsWith(TypeDiscriminatorChar)) + { + int i = json.IndexOf(PathSeparator); + if (i > 0) + { + string typeDiscriminator = json[1..i]; + string filePath = json[i..]; + + return (DerivedTypeDiscriminators.FirstOrDefault(x => x.Value == typeDiscriminator).Key, filePath); + } + } + else if (json.StartsWith(PathSeparator)) // Only filePath is present. + { + return (typeof(TResource), json); + } + } + + return (null, null); + } +} diff --git a/src/Symptum.Core/Subjects/Books/Book.cs b/src/Symptum.Core/Subjects/Books/Book.cs index 3c74193..84fbd9a 100644 --- a/src/Symptum.Core/Subjects/Books/Book.cs +++ b/src/Symptum.Core/Subjects/Books/Book.cs @@ -3,42 +3,22 @@ namespace Symptum.Core.Subjects.Books; -public class Book : ObservableObject +public partial class Book : ObservableObject { #region Properties - private string _id = string.Empty; + [ObservableProperty] + public partial string? Id { get; set; } - public string Id - { - get => _id; - set => SetProperty(ref _id, value); - } - - private string _title = string.Empty; - - public string Title - { - get => _title; - set => SetProperty(ref _title, value); - } - - private string _authors = string.Empty; + [ObservableProperty] + public partial string? Title { get; set; } - public string Authors - { - get => _authors; - set => SetProperty(ref _authors, value); - } - - private SubjectList _subjectList; + [ObservableProperty] + public partial string? Authors { get; set; } [Ignore] - public SubjectList Subject - { - get => _subjectList; - set => SetProperty(ref _subjectList, value); - } + [ObservableProperty] + public partial SubjectList Subject { get; set; } #endregion @@ -48,13 +28,10 @@ public Book() public Book(string code, string title, string author) { - _id = code; - _title = title; - _authors = author; + Id = code; + Title = title; + Authors = author; } - public override string ToString() - { - return Title + " by " + Authors + " (" + Id + ")"; - } + public override string ToString() => Title + " by " + Authors + " (" + Id + ")"; } diff --git a/src/Symptum.Core/Subjects/QuestionBanks/QuestionBank.cs b/src/Symptum.Core/Subjects/QuestionBanks/QuestionBank.cs index fd69d6f..ec5d4be 100644 --- a/src/Symptum.Core/Subjects/QuestionBanks/QuestionBank.cs +++ b/src/Symptum.Core/Subjects/QuestionBanks/QuestionBank.cs @@ -1,64 +1,28 @@ using System.Collections.ObjectModel; -using Symptum.Core.Extensions; +using System.Text.Json.Serialization; +using CommunityToolkit.Mvvm.ComponentModel; using Symptum.Core.Management.Resources; using Symptum.Core.Serialization; namespace Symptum.Core.Subjects.QuestionBanks; -public class QuestionBank : MetadataResource +public partial class QuestionBank : CategoryResource, ISubjectCategoryResource { public QuestionBank() { } #region Properties - private SubjectList _subjectCode; - - public SubjectList SubjectCode - { - get => _subjectCode; - set => SetProperty(ref _subjectCode, value); - } - - private ObservableCollection? papers; + [ObservableProperty] + public partial SubjectList SubjectCode { get; set; } + [JsonPropertyName("Papers")] [ListOfMetadataResource] - public ObservableCollection? Papers + public override ObservableCollection? Items { - get => papers; - set - { - UnobserveCollection(papers); - SetProperty(ref papers, value); - SetChildrenResources(papers); - } + get => base.Items; + set => base.Items = value; } #endregion - - protected override void OnInitializeResource(IResource? parent) - { - SetChildrenResources(papers); - } - - public override bool CanHandleChildResourceType(Type childResourceType) - { - return childResourceType == typeof(QuestionBankPaper); - } - - public override bool CanAddChildResourceType(Type childResourceType) - { - return childResourceType == typeof(QuestionBankPaper); - } - - protected override void OnAddChildResource(IResource? childResource) - { - Papers ??= []; - Papers?.AddItemToListIfNotExists(childResource); - } - - protected override void OnRemoveChildResource(IResource? childResource) - { - Papers.RemoveItemFromListIfExists(childResource); - } } diff --git a/src/Symptum.Core/Subjects/QuestionBanks/QuestionBankPaper.cs b/src/Symptum.Core/Subjects/QuestionBanks/QuestionBankPaper.cs index 59a73f6..6154832 100644 --- a/src/Symptum.Core/Subjects/QuestionBanks/QuestionBankPaper.cs +++ b/src/Symptum.Core/Subjects/QuestionBanks/QuestionBankPaper.cs @@ -1,10 +1,10 @@ using System.Collections.ObjectModel; -using Symptum.Core.Extensions; +using System.Text.Json.Serialization; using Symptum.Core.Management.Resources; namespace Symptum.Core.Subjects.QuestionBanks; -public class QuestionBankPaper : MetadataResource +public class QuestionBankPaper : CategoryResource { public QuestionBankPaper() { } @@ -16,44 +16,12 @@ public QuestionBankPaper(string title) #region Properties - private ObservableCollection? topics; - - public ObservableCollection? Topics + [JsonPropertyName("Topics")] + public override ObservableCollection? Items { - get => topics; - set - { - UnobserveCollection(topics); - SetProperty(ref topics, value); - SetChildrenResources(topics); - } + get => base.Items; + set => base.Items = value; } #endregion - - protected override void OnInitializeResource(IResource? parent) - { - SetChildrenResources(topics); - } - - public override bool CanHandleChildResourceType(Type childResourceType) - { - return childResourceType == typeof(QuestionBankTopic); - } - - public override bool CanAddChildResourceType(Type childResourceType) - { - return childResourceType == typeof(QuestionBankTopic); - } - - protected override void OnAddChildResource(IResource? childResource) - { - Topics ??= []; - Topics.AddItemToListIfNotExists(childResource); - } - - protected override void OnRemoveChildResource(IResource? childResource) - { - Topics.RemoveItemFromListIfExists(childResource); - } } diff --git a/src/Symptum.Core/Subjects/QuestionBanks/QuestionEntry.cs b/src/Symptum.Core/Subjects/QuestionBanks/QuestionEntry.cs index 773d425..6d6ac38 100644 --- a/src/Symptum.Core/Subjects/QuestionBanks/QuestionEntry.cs +++ b/src/Symptum.Core/Subjects/QuestionBanks/QuestionEntry.cs @@ -84,20 +84,17 @@ public List? References #endregion - public QuestionEntry Clone() + public QuestionEntry Clone() => new QuestionEntry() { - return new QuestionEntry() - { - Id = id != null ? new() { QuestionType = id.QuestionType, SubjectCode = id.SubjectCode, CompetencyNumbers = id.CompetencyNumbers } : new(), - Title = title, - Descriptions = descriptions.CloneList(), - HasPreviouslyBeenAsked = hasPreviouslyBeenAsked, - Importance = importance, - YearsAsked = yearsAsked.CloneList(), - ProbableCases = probableCases.CloneList(), - References = references.CloneList(x => x with { }) - }; - } + Id = id != null ? new() { QuestionType = id.QuestionType, SubjectCode = id.SubjectCode, CompetencyNumbers = id.CompetencyNumbers } : new(), + Title = title, + Descriptions = descriptions.CloneList(), + HasPreviouslyBeenAsked = hasPreviouslyBeenAsked, + Importance = importance, + YearsAsked = yearsAsked.CloneList(), + ProbableCases = probableCases.CloneList(), + References = references.CloneList(x => x with { }) + }; public int CompareTo(QuestionEntry? other) { diff --git a/src/Symptum.Core/Subjects/QuestionBanks/QuestionId.cs b/src/Symptum.Core/Subjects/QuestionBanks/QuestionId.cs index c1307c2..fc9e1d5 100644 --- a/src/Symptum.Core/Subjects/QuestionBanks/QuestionId.cs +++ b/src/Symptum.Core/Subjects/QuestionBanks/QuestionId.cs @@ -96,10 +96,7 @@ public string IdString return questionId; } - private void UpdateIdString() - { - IdString = ToString(); - } + private void UpdateIdString() => IdString = ToString(); public override string ToString() { diff --git a/src/Symptum.Core/Subjects/Subject.cs b/src/Symptum.Core/Subjects/Subject.cs index 8701b4c..ba1011d 100644 --- a/src/Symptum.Core/Subjects/Subject.cs +++ b/src/Symptum.Core/Subjects/Subject.cs @@ -1,10 +1,11 @@ +using System.Collections.ObjectModel; +using CommunityToolkit.Mvvm.ComponentModel; using Symptum.Core.Management.Resources; using Symptum.Core.Serialization; -using Symptum.Core.Subjects.QuestionBanks; namespace Symptum.Core.Subjects; -public class Subject : PackageResource +public partial class Subject : PackageResource { public Subject() { } @@ -21,70 +22,15 @@ public Subject(SubjectList subjectCode) #region Properties - private SubjectList _subjectCode; + [ObservableProperty] + public partial SubjectList SubjectCode { get; set; } - public SubjectList SubjectCode + [ListOfMetadataResource] + public override ObservableCollection? Contents { - get => _subjectCode; - set => SetProperty(ref _subjectCode, value); - } - - private QuestionBank? questionBank; - - [MetadataResource] - public QuestionBank? QuestionBank - { - get => questionBank; - set - { - UpdateChildrenResources(value); - SetProperty(ref questionBank, value); - } + get => base.Contents; + set => base.Contents = value; } #endregion - - protected override void OnInitializeResource(IResource? parent) - { - if (questionBank != null) - AddChildResourceInternal(questionBank); - } - - public override bool CanHandleChildResourceType(Type childResourceType) - { - return childResourceType == typeof(QuestionBank); - } - - public override bool CanAddChildResourceType(Type childResourceType) - { - if (childResourceType == typeof(QuestionBank)) - { - return QuestionBank == null; - } - - return false; - } - - protected override void OnAddChildResource(IResource? childResource) - { - if (childResource is QuestionBank _qb) - QuestionBank = _qb; - } - - protected override void OnRemoveChildResource(IResource? childResource) - { - if (childResource is QuestionBank) - QuestionBank = null; - } - - private void UpdateChildrenResources(QuestionBank? value) - { - if (ChildrenResources != null) - { - if (questionBank != null && ChildrenResources.Contains(questionBank)) - RemoveChildResourceInternal(questionBank); - if (value != null) - AddChildResourceInternal(value); - } - } } diff --git a/src/Symptum.Core/Subjects/SubjectsManager.cs b/src/Symptum.Core/Subjects/SubjectsManager.cs index 0886cb0..8fd7405 100644 --- a/src/Symptum.Core/Subjects/SubjectsManager.cs +++ b/src/Symptum.Core/Subjects/SubjectsManager.cs @@ -7,13 +7,7 @@ public class SubjectsManager { public static ObservableCollection Subjects { get; } = []; - public static void RegisterSubject(Subject subject) - { - Subjects.AddItemToListIfNotExists(subject); - } + public static void RegisterSubject(Subject subject) => Subjects.AddItemToListIfNotExists(subject); - public static void UnregisterSubject(Subject subject) - { - Subjects.RemoveItemFromListIfExists(subject); - } + public static void UnregisterSubject(Subject subject) => Subjects.RemoveItemFromListIfExists(subject); } diff --git a/src/Symptum.Editor/App.xaml b/src/Symptum.Editor/App.xaml index 9c090cc..ef992ca 100644 --- a/src/Symptum.Editor/App.xaml +++ b/src/Symptum.Editor/App.xaml @@ -13,6 +13,7 @@ + diff --git a/src/Symptum.Editor/Assets/SharedAssets.md b/src/Symptum.Editor/Assets/SharedAssets.md deleted file mode 100644 index 1b84a74..0000000 --- a/src/Symptum.Editor/Assets/SharedAssets.md +++ /dev/null @@ -1,32 +0,0 @@ -# Shared Assets - -See documentation about assets here: https://github.com/unoplatform/uno/blob/master/doc/articles/features/working-with-assets.md - -## Here is a cheat sheet - -1. Add the image file to the `Assets` directory of a shared project. -2. Set the build action to `Content`. -3. (Recommended) Provide an asset for various scales/dpi - -### Examples - -```text -\Assets\Images\logo.scale-100.png -\Assets\Images\logo.scale-200.png -\Assets\Images\logo.scale-400.png - -\Assets\Images\scale-100\logo.png -\Assets\Images\scale-200\logo.png -\Assets\Images\scale-400\logo.png -``` - -### Table of scales - -| Scale | WinUI | iOS/MacCatalyst | Android | -|-------|:-----------:|:---------------:|:-------:| -| `100` | scale-100 | @1x | mdpi | -| `125` | scale-125 | N/A | N/A | -| `150` | scale-150 | N/A | hdpi | -| `200` | scale-200 | @2x | xhdpi | -| `300` | scale-300 | @3x | xxhdpi | -| `400` | scale-400 | N/A | xxxhdpi | diff --git a/src/Symptum.Editor/Common/DefaultIconSources.cs b/src/Symptum.Editor/Common/DefaultIconSources.cs index 06a96ab..d2ba17b 100644 --- a/src/Symptum.Editor/Common/DefaultIconSources.cs +++ b/src/Symptum.Editor/Common/DefaultIconSources.cs @@ -2,6 +2,9 @@ namespace Symptum.Editor.Common; public static class DefaultIconSources { - public static readonly IconSource DataGridIconSource = new SymbolIconSource() { Symbol = Symbol.List }; - public static readonly IconSource PropertiesIconSource = new SymbolIconSource() { Symbol = Symbol.Repair }; + public static IconSource DataGridIconSource { get; } = new SymbolIconSource() { Symbol = Symbol.List }; + + public static IconSource PropertiesIconSource { get; } = new SymbolIconSource() { Symbol = Symbol.Repair }; + + public static IconSource DocumentIconSource { get; } = new SymbolIconSource() { Symbol = Symbol.Document }; } diff --git a/src/Symptum.Editor/Controls/AddNewItemDialog.xaml b/src/Symptum.Editor/Controls/AddNewItemDialog.xaml index 39e31a5..2a3eacc 100644 --- a/src/Symptum.Editor/Controls/AddNewItemDialog.xaml +++ b/src/Symptum.Editor/Controls/AddNewItemDialog.xaml @@ -9,6 +9,7 @@ PrimaryButtonText="Add" CloseButtonText="Cancel" DefaultButton="Primary"> + @@ -29,7 +30,6 @@ - diff --git a/src/Symptum.Editor/Controls/AddNewItemDialog.xaml.cs b/src/Symptum.Editor/Controls/AddNewItemDialog.xaml.cs index 07c49a4..dc05c48 100644 --- a/src/Symptum.Editor/Controls/AddNewItemDialog.xaml.cs +++ b/src/Symptum.Editor/Controls/AddNewItemDialog.xaml.cs @@ -10,7 +10,12 @@ public sealed partial class AddNewItemDialog : ContentDialog { private static readonly List itemTypes = [ - new ("Subject", typeof(Subject), "Subjects"), + new("Subject", typeof(Subject), "Subjects"), + new("Category", typeof(CategoryResource), "Common"), + new("Image Category", typeof(ImageCategoryResource), "Common"), + new("Markdown Category", typeof(MarkdownCategoryResource), "Common"), + new("Image File", typeof(ImageFileResource), "Common"), + new("Markdown File", typeof(MarkdownFileResource), "Common"), new("Question Bank", typeof(QuestionBank), "Question Banks"), new("Question Bank Paper", typeof(QuestionBankPaper), "Question Banks"), new("Question Bank Topic", typeof(QuestionBankTopic), "Question Banks"), @@ -20,10 +25,6 @@ public sealed partial class AddNewItemDialog : ContentDialog new("Nutrition Package", typeof(NutritionPackage), "Nutrition"), new("Nutrition Data Set", typeof(NutritionDataSet), "Nutrition"), new("Food Group", typeof(FoodGroup), "Nutrition"), - new("Category", typeof(CategoryResource), "Common"), - new("Image Category", typeof(ImageCategoryResource), "Common"), - new("Markdown Category", typeof(MarkdownCategoryResource), "Common"), - new("Image File", typeof(ImageFileResource), "Common"), ]; private List? availItemTypes; @@ -47,10 +48,38 @@ public AddNewItemDialog() Opened += AddNewItemDialog_Opened; PrimaryButtonClick += AddNewItemDialog_PrimaryButtonClick; CloseButtonClick += AddNewItemDialog_CloseButtonClick; + queryBox.TextChanged += QueryBox_TextChanged; + queryBox.QuerySubmitted += QueryBox_QuerySubmitted; + } + + private void QueryBox_QuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args) + { + SearchItems(args.QueryText); + } + + private void QueryBox_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args) + { + if (args.Reason == AutoSuggestionBoxTextChangeReason.UserInput) + SearchItems(sender.Text); + } + + private void SearchItems(string? queryText) + { + if (string.IsNullOrWhiteSpace(queryText)) + { + newItemsLV.ItemsSource = availItemTypes; + return; + } + + List suitableItems = availItemTypes?.FindAll(x => + x.DisplayName?.Contains(queryText, StringComparison.InvariantCultureIgnoreCase) ?? false) ?? []; + + newItemsLV.ItemsSource = suitableItems; } private void AddNewItemDialog_Opened(ContentDialog sender, ContentDialogOpenedEventArgs args) { + queryBox.Text = string.Empty; errorInfoBar.IsOpen = false; errorInfoBar.Message = string.Empty; } diff --git a/src/Symptum.Editor/Controls/FindControl.xaml b/src/Symptum.Editor/Controls/FindControl.xaml new file mode 100644 index 0000000..13f2f6c --- /dev/null +++ b/src/Symptum.Editor/Controls/FindControl.xaml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + ab + + + + + diff --git a/src/Symptum.Editor/Controls/FindControl.xaml.cs b/src/Symptum.Editor/Controls/FindControl.xaml.cs new file mode 100644 index 0000000..c977324 --- /dev/null +++ b/src/Symptum.Editor/Controls/FindControl.xaml.cs @@ -0,0 +1,255 @@ +using System.Collections.ObjectModel; +using Microsoft.UI.Xaml.Data; +using Symptum.Editor.Converters; + +namespace Symptum.Editor.Controls; + +public enum FindDirection +{ + Next, + Previous, + All +} + +public enum FindError +{ + NoMatches, + NoMoreMatches +} + +public sealed partial class FindControl : UserControl +{ + private readonly ObservableCollection _queries = []; + + public FindControl() + { + InitializeComponent(); + queryBox.ItemsSource = _queries; + queryBox.QuerySubmitted += QueryBox_QuerySubmitted; + fNextButton.Click += (s, e) => Find(FindDirection.Next); + fPrevButton.Click += (s, e) => Find(FindDirection.Previous); + fAllButton.Click += (s, e) => Find(FindDirection.All); + fClearButton.Click += (s, e) => Clear(); + + Binding binding = new() { Path = new(nameof(InfoBar.IsOpen)), Source = errorInfoBar, Converter = new BooleanToVisibilityConverter() }; + errorInfoBar.SetBinding(VisibilityProperty, binding); + } + + public event EventHandler QuerySubmitted; + + public event EventHandler QueryCleared; + + #region Properties + + #region QueryText + + public static readonly DependencyProperty QueryTextProperty = + DependencyProperty.Register( + nameof(QueryText), + typeof(string), + typeof(FindControl), + new PropertyMetadata(string.Empty)); + + public string QueryText + { + get => (string)GetValue(QueryTextProperty); + set => SetValue(QueryTextProperty, value); + } + + #endregion + + #region FindContexts + + public static readonly DependencyProperty FindContextsProperty = + DependencyProperty.Register( + nameof(FindContexts), + typeof(IList), + typeof(FindControl), + new PropertyMetadata(null)); + + public IList? FindContexts + { + get => (IList?)GetValue(FindContextsProperty); + set => SetValue(FindContextsProperty, value); + } + + #endregion + + #region SelectedContext + + public static readonly DependencyProperty SelectedContextProperty = + DependencyProperty.Register( + nameof(SelectedContext), + typeof(string), + typeof(FindControl), + new PropertyMetadata(string.Empty)); + + public string SelectedContext + { + get => (string)GetValue(SelectedContextProperty); + set => SetValue(SelectedContextProperty, value); + } + + #endregion + + #region MatchCase + + public static readonly DependencyProperty MatchCaseProperty = + DependencyProperty.Register( + nameof(MatchCase), + typeof(bool), + typeof(FindControl), + new PropertyMetadata(false)); + + public bool MatchCase + { + get => (bool)GetValue(MatchCaseProperty); + set => SetValue(MatchCaseProperty, value); + } + + #endregion + + #region MatchWholeWord + + public static readonly DependencyProperty MatchWholeWordProperty = + DependencyProperty.Register( + nameof(MatchWholeWord), + typeof(bool), + typeof(FindControl), + new PropertyMetadata(false)); + + public bool MatchWholeWord + { + get => (bool)GetValue(MatchWholeWordProperty); + set => SetValue(MatchWholeWordProperty, value); + } + + #endregion + + #region FindNextEnabled + + public static readonly DependencyProperty FindNextEnabledProperty = + DependencyProperty.Register( + nameof(FindNextEnabled), + typeof(bool), + typeof(FindControl), + new PropertyMetadata(false)); + + public bool FindNextEnabled + { + get => (bool)GetValue(FindNextEnabledProperty); + set => SetValue(FindNextEnabledProperty, value); + } + + #endregion + + #region FindPreviousEnabled + + public static readonly DependencyProperty FindPreviousEnabledProperty = + DependencyProperty.Register( + nameof(FindPreviousEnabled), + typeof(bool), + typeof(FindControl), + new PropertyMetadata(false)); + + public bool FindPreviousEnabled + { + get => (bool)GetValue(FindPreviousEnabledProperty); + set => SetValue(FindPreviousEnabledProperty, value); + } + + #endregion + + #region FindPreviousEnabled + + public static readonly DependencyProperty FindAllEnabledProperty = + DependencyProperty.Register( + nameof(FindAllEnabled), + typeof(bool), + typeof(FindControl), + new PropertyMetadata(true)); + + public bool FindAllEnabled + { + get => (bool)GetValue(FindAllEnabledProperty); + set => SetValue(FindAllEnabledProperty, value); + } + + #endregion + + #endregion + + public void ShowErrorMessage(FindError findError) + { + errorInfoBar.Message = findError switch + { + FindError.NoMatches => $"Can't find \"{QueryText}\"", + FindError.NoMoreMatches => $"No more occurrences of \"{QueryText}\"", + _ => string.Empty + }; + errorInfoBar.Severity = findError switch + { + FindError.NoMatches => InfoBarSeverity.Error, + _ => InfoBarSeverity.Warning + }; + + errorInfoBar.IsOpen = true; + } + + private void QueryBox_QuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args) + { + if (!string.IsNullOrEmpty(args.QueryText) && !_queries.Contains(args.QueryText)) + _queries.Add(args.QueryText); + QueryText = args.QueryText; + Find(FindDirection.All); + } + + private void Find(FindDirection findDirection) + { + errorInfoBar.IsOpen = false; + if (string.IsNullOrEmpty(QueryText) || string.IsNullOrWhiteSpace(QueryText)) + { + Clear(); + return; + } + + FindControlQuerySubmittedEventArgs args = new() + { + Context = SelectedContext, + FindDirection = findDirection, + MatchCase = MatchCase, + MatchWholeWord = MatchWholeWord, + QueryText = QueryText + }; + + RaiseQuerySubmitted(args); + } + + private void Clear() + { + QueryText = string.Empty; + QueryCleared?.Invoke(this, new()); + } + + private void RaiseQuerySubmitted(FindControlQuerySubmittedEventArgs e) + { + QuerySubmitted?.Invoke(this, e); + } +} + +public class FindControlQuerySubmittedEventArgs : EventArgs +{ + public string QueryText { get; init; } = string.Empty; + + public FindDirection FindDirection { get; init; } = FindDirection.Next; + + public string Context { get; init; } = string.Empty; + + public bool MatchCase { get; init; } = false; + + public bool MatchWholeWord { get; init; } = false; + + public FindControlQuerySubmittedEventArgs() + { + } +} diff --git a/src/Symptum.Editor/Controls/FindFlyout.xaml b/src/Symptum.Editor/Controls/FindFlyout.xaml deleted file mode 100644 index 017dd38..0000000 --- a/src/Symptum.Editor/Controls/FindFlyout.xaml +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ab - - - - - diff --git a/src/Symptum.Editor/Controls/FindFlyout.xaml.cs b/src/Symptum.Editor/Controls/FindFlyout.xaml.cs deleted file mode 100644 index 018cf0e..0000000 --- a/src/Symptum.Editor/Controls/FindFlyout.xaml.cs +++ /dev/null @@ -1,277 +0,0 @@ -using System.Collections.ObjectModel; -using Microsoft.UI.Xaml.Controls.Primitives; - -namespace Symptum.Editor.Controls; - -public enum FindDirection -{ - Next, - Previous, - All -} - -public sealed partial class FindFlyout : Flyout -{ - private readonly ObservableCollection _queries = []; - - public FindFlyout() - { - InitializeComponent(); - Opened += FindFlyout_Opened; - queryBox.ItemsSource = _queries; - queryBox.TextChanged += (s, e) => QueryText = queryBox.Text; - queryBox.QuerySubmitted += QueryBox_QuerySubmitted; - fNextButton.Click += (s, e) => Find(FindDirection.Next); - fPrevButton.Click += (s, e) => Find(FindDirection.Previous); - fAllButton.Click += (s, e) => Find(FindDirection.All); - fClearButton.Click += (s, e) => Clear(); - fContextComboBox.SelectionChanged += (s, e) => - { - if (e.AddedItems.Count > 0) - { - SelectedContext = e.AddedItems[0].ToString(); - } - }; - mCaseButton.Click += (s, e) => MatchCase = mCaseButton.IsChecked ?? false; - mWordButton.Click += (s, e) => MatchWholeWord = mWordButton.IsChecked ?? false; - } - - public event EventHandler QuerySubmitted; - - public event EventHandler QueryCleared; - - #region Properties - - #region QueryText - - public static readonly DependencyProperty QueryTextProperty = - DependencyProperty.Register( - nameof(QueryText), - typeof(string), - typeof(FindFlyout), - new PropertyMetadata(string.Empty, OnQueryTextChanged)); - - private static void OnQueryTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - if (d is FindFlyout findFlyout) - { - findFlyout.queryBox.Text = e.NewValue.ToString(); - } - } - - public string QueryText - { - get => (string)GetValue(QueryTextProperty); - set => SetValue(QueryTextProperty, value); - } - - #endregion - - #region FindContexts - - public static readonly DependencyProperty FindContextsProperty = - DependencyProperty.Register( - nameof(FindContexts), - typeof(IList), - typeof(FindFlyout), - new PropertyMetadata(null, OnFindContextsChanged)); - - private static void OnFindContextsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - if (d is FindFlyout findFlyout) - { - findFlyout.fContextComboBox.ItemsSource = e.NewValue; - } - } - - public IList? FindContexts - { - get => (IList?)GetValue(FindContextsProperty); - set => SetValue(FindContextsProperty, value); - } - - #endregion - - #region SelectedContext - - public static readonly DependencyProperty SelectedContextProperty = - DependencyProperty.Register( - nameof(SelectedContext), - typeof(string), - typeof(FindFlyout), - new PropertyMetadata(string.Empty, OnSelectedContextChanged)); - - private static void OnSelectedContextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - if (d is FindFlyout findFlyout) - { - var contexts = findFlyout.FindContexts; - if (contexts != null && findFlyout.fContextComboBox.ItemsSource == contexts) - { - foreach (var context in contexts) - { - if (context == e.NewValue.ToString()) - findFlyout.fContextComboBox.SelectedItem = context; - return; - } - } - - findFlyout.fContextComboBox.SelectedItem = null; - } - } - - public string SelectedContext - { - get => (string)GetValue(SelectedContextProperty); - set => SetValue(SelectedContextProperty, value); - } - - #endregion - - #region MatchCase - - public static readonly DependencyProperty MatchCaseProperty = - DependencyProperty.Register( - nameof(MatchCase), - typeof(bool), - typeof(FindFlyout), - new PropertyMetadata(false, OnMatchCaseChanged)); - - private static void OnMatchCaseChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - if (d is FindFlyout findFlyout) - { - findFlyout.mCaseButton.IsChecked = (bool)e.NewValue; - } - } - - public bool MatchCase - { - get => (bool)GetValue(MatchCaseProperty); - set => SetValue(MatchCaseProperty, value); - } - - #endregion - - #region MatchWholeWord - - public static readonly DependencyProperty MatchWholeWordProperty = - DependencyProperty.Register( - nameof(MatchWholeWord), - typeof(bool), - typeof(FindFlyout), - new PropertyMetadata(false, OnMatchWholeWordChanged)); - - private static void OnMatchWholeWordChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - if (d is FindFlyout findFlyout) - { - findFlyout.mWordButton.IsChecked = (bool)e.NewValue; - } - } - - public bool MatchWholeWord - { - get => (bool)GetValue(MatchWholeWordProperty); - set => SetValue(MatchWholeWordProperty, value); - } - - #endregion - - #endregion - - public void ShowAt(FindOptions? findOptions = null, DependencyObject? placementTarget = null, FlyoutShowOptions? showOptions = null) - { - showOptions ??= new() - { - Position = new(), - ShowMode = FlyoutShowMode.Standard, - Placement = FlyoutPlacementMode.Bottom - }; - - if (findOptions != null) - { - QueryText = findOptions.InitialQueryText; - FindContexts = findOptions.Contexts; - MatchCase = findOptions.MatchCase; - MatchWholeWord = findOptions.MatchWholeWord; - } - ShowAt(placementTarget, showOptions); - } - - private void QueryBox_QuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args) - { - if (!string.IsNullOrEmpty(args.QueryText) && !_queries.Contains(args.QueryText)) - _queries.Add(args.QueryText); - QueryText = args.QueryText; - Find(FindDirection.All); - } - - private void FindFlyout_Opened(object sender, object e) - { - queryBox.Focus(FocusState.Programmatic); - } - - private void Find(FindDirection findDirection) - { - if (string.IsNullOrEmpty(QueryText) || string.IsNullOrWhiteSpace(QueryText)) - { - Clear(); - return; - } - - FindFlyoutQuerySubmittedEventArgs args = new() - { - Context = SelectedContext, - FindDirection = findDirection, - MatchCase = MatchCase, - MatchWholeWord = MatchWholeWord, - QueryText = QueryText - }; - - RaiseQuerySubmitted(args); - } - - private void Clear() - { - QueryText = string.Empty; - QueryCleared?.Invoke(this, new()); - } - - private void RaiseQuerySubmitted(FindFlyoutQuerySubmittedEventArgs e) - { - QuerySubmitted?.Invoke(this, e); - } -} - -public class FindFlyoutQuerySubmittedEventArgs : EventArgs -{ - public string QueryText { get; init; } = string.Empty; - - public FindDirection FindDirection { get; init; } = FindDirection.Next; - - public string Context { get; init; } = string.Empty; - - public bool MatchCase { get; init; } = false; - - public bool MatchWholeWord { get; init; } = false; - - public FindFlyoutQuerySubmittedEventArgs() - { - } -} - -public class FindOptions : ObservableObject -{ - public string InitialQueryText { get; init; } = string.Empty; - - public IList? Contexts { get; init; } = null; - - public bool MatchCase { get; init; } = false; - - public bool MatchWholeWord { get; init; } = false; - - public FindOptions() - { - } -} diff --git a/src/Symptum.Editor/EditorPages/DefaultEditorPage.xaml b/src/Symptum.Editor/EditorPages/DefaultEditorPage.xaml index 7685641..62ee4c1 100644 --- a/src/Symptum.Editor/EditorPages/DefaultEditorPage.xaml +++ b/src/Symptum.Editor/EditorPages/DefaultEditorPage.xaml @@ -15,7 +15,7 @@ - + @@ -23,10 +23,10 @@ - - + - + + + + + + - diff --git a/src/Symptum.Editor/MainPage.xaml.cs b/src/Symptum.Editor/MainPage.xaml.cs index 050a2e3..10131cc 100644 --- a/src/Symptum.Editor/MainPage.xaml.cs +++ b/src/Symptum.Editor/MainPage.xaml.cs @@ -17,6 +17,8 @@ namespace Symptum.Editor; public sealed partial class MainPage : Page { + private bool _collapsed = false; + private bool _showResourcesPane = true; private readonly AddNewItemDialog addNewItemDialog = new(); private readonly QuestionBankContextConfigureDialog contextConfigureDialog = new(); @@ -67,7 +69,7 @@ public MainPage() } }; - expandPaneButton.Click += (s, e) => + expandResourcesPaneButton.Click += (s, e) => { splitView.IsPaneOpen = true; }; @@ -75,6 +77,23 @@ public MainPage() EditorPagesManager.CurrentEditorChanged += (s, e) => editorsTabView.SelectedItem = e; EditorPagesManager.EditorPages.Add(new MarkdownEditorPage()); + + SizeChanged += MainPage_SizeChanged; + } + + private void MainPage_SizeChanged(object sender, SizeChangedEventArgs args) + { + bool collapsed = args.NewSize.Width switch + { + >= 1007 => false, + _ => true + }; + + if (collapsed != _collapsed) + { + _collapsed = collapsed; + VisualStateManager.GoToState(this, collapsed || !_showResourcesPane ? "MinimalState" : "DefaultState", true); + } } private async void Markdown_Click(object sender, RoutedEventArgs e) @@ -304,6 +323,14 @@ private void MultiSelectButton_Click(object sender, RoutedEventArgs e) UpdateDeleteButtonEnabled(); } + private void ShowResourcesPaneButton_Click(object sender, RoutedEventArgs e) + { + _showResourcesPane = !_showResourcesPane; + ToolTipService.SetToolTip(showResourcesPaneButton, _showResourcesPane ? "Unpin" : "Pin"); + resourcesPaneButtonSymbolIcon.Symbol = _showResourcesPane ? Symbol.UnPin : Symbol.Pin; + VisualStateManager.GoToState(this, _showResourcesPane && !_collapsed ? "DefaultState" : "MinimalState", true); + } + private void UpdateDeleteButtonEnabled() { int count = treeView.SelectedItems.Count; From e5478bf4c4d1767a976c4a1766ff59e48c876055 Mon Sep 17 00:00:00 2001 From: Shankar Date: Sun, 8 Dec 2024 00:49:08 +0530 Subject: [PATCH 4/5] Further Improvements to Markdown Editor - Markdown Editor: - UI improvements. - MarkdownEditorInsertLinkDialog and TableDialog are fully operational. - Clipboard actions are implemented. - Headings can be switched from one to another. - Brought back HTML support: p, a, img, details, address, h1-h6, b, u, em, s. - Ported some changes from upstream repo. --- Directory.Packages.props | 3 +- src/Symptum.Editor/Controls/FindControl.xaml | 3 +- .../MarkdownEditorInsertLinkDialog.xaml | 20 ++ .../MarkdownEditorInsertLinkDialog.xaml.cs | 66 ++++ .../MarkdownEditorInsertTableDialog.xaml | 25 +- .../MarkdownEditorInsertTableDialog.xaml.cs | 41 ++- .../EditorPages/MarkdownEditorPage.xaml | 95 +++-- .../EditorPages/MarkdownEditorPage.xaml.cs | 330 +++++++++++------- src/Symptum.Editor/MainPage.xaml | 16 +- src/Symptum.Editor/Symptum.Editor.csproj | 1 + src/Symptum.UI/Markdown/Extensions.cs | 24 +- src/Symptum.UI/Markdown/HtmlWriter.cs | 84 +++++ .../Markdown/MarkdownTextBlock.xaml.cs | 40 ++- src/Symptum.UI/Markdown/MarkdownThemes.cs | 7 + .../ObjectRenderers/HtmlBlockRenderer.cs | 38 ++ .../Inlines/HtmlEntityInlineRenderer.cs | 2 - .../Inlines/HtmlInlineRenderer.cs | 18 + .../Markdown/Renderers/WinUIRenderer.cs | 6 +- .../TextElements/BlockContainerElement.cs | 5 +- .../Markdown/TextElements/CodeBlockElement.cs | 5 +- .../TextElements/CodeInlineElement.cs | 5 +- .../TextElements/EmphasisInlineElement.cs | 5 +- .../TextElements/FlowDocumentElement.cs | 5 +- .../Markdown/TextElements/HeadingElement.cs | 41 ++- .../TextElements/Html/HtmlBlockElement.cs | 43 +++ .../TextElements/Html/HtmlDetailsElement.cs | 44 +++ .../TextElements/Html/HtmlElementType.cs | 7 + .../TextElements/Html/HtmlInlineElement.cs | 56 +++ .../TextElements/HyperlinkButtonElement.cs | 34 +- .../Markdown/TextElements/HyperlinkElement.cs | 29 +- .../Markdown/TextElements/ImageElement.cs | 37 +- .../Markdown/TextElements/LineBreakElement.cs | 5 +- .../Markdown/TextElements/ListElement.cs | 5 +- .../Markdown/TextElements/ParagraphElement.cs | 5 +- .../Markdown/TextElements/QuoteElement.cs | 5 +- .../TextElements/ReferenceInlineElement.cs | 5 +- .../Markdown/TextElements/STextElements.cs | 3 +- .../Markdown/TextElements/TableCellElement.cs | 27 +- .../Markdown/TextElements/TableElement.cs | 7 +- .../TextElements/TaskListCheckBoxElement.cs | 5 +- .../TextElements/TextInlineElement.cs | 5 +- .../TextElements/ThematicBreakElement.cs | 5 +- src/Symptum.UI/Symptum.UI.csproj | 1 + src/Symptum.UI/Themes/Styles.xaml | 5 + 44 files changed, 905 insertions(+), 313 deletions(-) create mode 100644 src/Symptum.Editor/Controls/MarkdownEditorInsertLinkDialog.xaml create mode 100644 src/Symptum.Editor/Controls/MarkdownEditorInsertLinkDialog.xaml.cs create mode 100644 src/Symptum.UI/Markdown/HtmlWriter.cs create mode 100644 src/Symptum.UI/Markdown/Renderers/ObjectRenderers/HtmlBlockRenderer.cs create mode 100644 src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Inlines/HtmlInlineRenderer.cs create mode 100644 src/Symptum.UI/Markdown/TextElements/Html/HtmlBlockElement.cs create mode 100644 src/Symptum.UI/Markdown/TextElements/Html/HtmlDetailsElement.cs create mode 100644 src/Symptum.UI/Markdown/TextElements/Html/HtmlElementType.cs create mode 100644 src/Symptum.UI/Markdown/TextElements/Html/HtmlInlineElement.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index c7badf4..d8f6ab4 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -11,10 +11,11 @@ + - + diff --git a/src/Symptum.Editor/Controls/FindControl.xaml b/src/Symptum.Editor/Controls/FindControl.xaml index 13f2f6c..ade0a6c 100644 --- a/src/Symptum.Editor/Controls/FindControl.xaml +++ b/src/Symptum.Editor/Controls/FindControl.xaml @@ -6,7 +6,8 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:controls="using:CommunityToolkit.WinUI.UI.Controls" mc:Ignorable="d" AllowFocusOnInteraction="True"> - + diff --git a/src/Symptum.Editor/Controls/MarkdownEditorInsertLinkDialog.xaml b/src/Symptum.Editor/Controls/MarkdownEditorInsertLinkDialog.xaml new file mode 100644 index 0000000..febc968 --- /dev/null +++ b/src/Symptum.Editor/Controls/MarkdownEditorInsertLinkDialog.xaml @@ -0,0 +1,20 @@ + + + + + + + + + + + + diff --git a/src/Symptum.Editor/Controls/MarkdownEditorInsertLinkDialog.xaml.cs b/src/Symptum.Editor/Controls/MarkdownEditorInsertLinkDialog.xaml.cs new file mode 100644 index 0000000..eda52d1 --- /dev/null +++ b/src/Symptum.Editor/Controls/MarkdownEditorInsertLinkDialog.xaml.cs @@ -0,0 +1,66 @@ +using System.Text; + +namespace Symptum.Editor.Controls; + +public sealed partial class MarkdownEditorInsertLinkDialog : ContentDialog +{ + public EditorResult EditResult { get; private set; } = EditorResult.None; + + public string Markdown { get; private set; } = string.Empty; + + public MarkdownEditorInsertLinkDialog() + { + InitializeComponent(); + Opened += MarkdownEditorInsertLinkDialog_Opened; + PrimaryButtonClick += MarkdownEditorInsertLinkDialog_PrimaryButtonClick; + SecondaryButtonClick += MarkdownEditorInsertLinkDialog_SecondaryButtonClick; + } + + private void MarkdownEditorInsertLinkDialog_Opened(ContentDialog sender, ContentDialogOpenedEventArgs args) + { + textTB.Text = null; + urlTB.Text = null; + titleTB.Text = null; + } + + private void MarkdownEditorInsertLinkDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) + { + GenerateMarkdown(); + EditResult = EditorResult.Create; + } + + private void MarkdownEditorInsertLinkDialog_SecondaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) + { + EditResult = EditorResult.Cancel; + } + + public async Task CreateAsync() + { + await ShowAsync(); + return EditResult; + } + + private void GenerateMarkdown() + { + StringBuilder result = new(); + + string text = textTB.Text; + string url = urlTB.Text; + string title = titleTB.Text; + + result.Append('[') + .Append(text) + .Append(']') + .Append('(') + .Append(url); + + if (!string.IsNullOrWhiteSpace(title)) + { + result.Append(' ').Append('\"') + .Append(title).Append('\"'); + } + result.Append(')'); + + Markdown = result.ToString(); + } +} diff --git a/src/Symptum.Editor/Controls/MarkdownEditorInsertTableDialog.xaml b/src/Symptum.Editor/Controls/MarkdownEditorInsertTableDialog.xaml index f120f99..6aaf0b1 100644 --- a/src/Symptum.Editor/Controls/MarkdownEditorInsertTableDialog.xaml +++ b/src/Symptum.Editor/Controls/MarkdownEditorInsertTableDialog.xaml @@ -4,8 +4,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:Symptum.Editor.Controls" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:ui="using:CommunityToolkit.WinUI" - xmlns:tables="using:Markdig.Extensions.Tables" + xmlns:controls="using:CommunityToolkit.WinUI.Controls" Style="{ThemeResource DefaultContentDialogStyle}" Title="Insert Table" PrimaryButtonStyle="{ThemeResource AccentButtonStyle}" PrimaryButtonText="Insert" SecondaryButtonText="Cancel"> @@ -25,24 +24,26 @@ - + - - - + + + + + + + - + - + diff --git a/src/Symptum.Editor/Controls/MarkdownEditorInsertTableDialog.xaml.cs b/src/Symptum.Editor/Controls/MarkdownEditorInsertTableDialog.xaml.cs index 33e68bc..6c3a89d 100644 --- a/src/Symptum.Editor/Controls/MarkdownEditorInsertTableDialog.xaml.cs +++ b/src/Symptum.Editor/Controls/MarkdownEditorInsertTableDialog.xaml.cs @@ -1,7 +1,6 @@ using System.Collections.ObjectModel; using System.Text; using CommunityToolkit.Mvvm.ComponentModel; -using Markdig.Extensions.Tables; namespace Symptum.Editor.Controls; @@ -16,10 +15,17 @@ public sealed partial class MarkdownEditorInsertTableDialog : ContentDialog public MarkdownEditorInsertTableDialog() { InitializeComponent(); + Opened += MarkdownEditorInsertTableDialog_Opened; PrimaryButtonClick += MarkdownEditorInsertTableDialog_PrimaryButtonClick; SecondaryButtonClick += MarkdownEditorInsertTableDialog_SecondaryButtonClick; } + private void MarkdownEditorInsertTableDialog_Opened(ContentDialog sender, ContentDialogOpenedEventArgs args) + { + ModifyColumns(1); + ModifyRows(1); + } + private void MarkdownEditorInsertTableDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) { GenerateMarkdown(); @@ -43,9 +49,6 @@ private void Reset() columnsNB.Value = 1; rowsNB.Value = 1; _reset = false; - - ModifyColumns(1); - ModifyRows(1); } public async Task CreateAsync() @@ -70,10 +73,10 @@ private void GenerateMarkdown() { int rows = (int)rowsNB.Value; int columns = (int)columnsNB.Value; - StringBuilder stringBuilder = new(); + StringBuilder result = new(); for (int r = 0; r < rows + 2; r++) { - stringBuilder.Append('|'); + result.Append('|'); for (int c = 0; c < columns; c++) { var column = Columns[c]; @@ -81,34 +84,34 @@ private void GenerateMarkdown() bool isDivider = r == 1; if (isHeader) { - stringBuilder.Append(' '); + result.Append(' '); string? columnHeader = column.Header; - stringBuilder.Append(columnHeader); - stringBuilder.Append(' '); + result.Append(columnHeader); + result.Append(' '); } else if (isDivider) { string divider = column.Alignment switch { - TableColumnAlign.Center => " :-: ", - TableColumnAlign.Right => " --: ", + 1 => " :-: ", + 2 => " --: ", _ => " --- ", }; - stringBuilder.Append(divider); + result.Append(divider); } else { - stringBuilder.Append(' '); + result.Append(' '); string? cellContent = Columns[c].Cells[r - 2].Content; - stringBuilder.Append(cellContent); - stringBuilder.Append(' '); + result.Append(cellContent); + result.Append(' '); } - stringBuilder.Append('|'); + result.Append('|'); } - stringBuilder.AppendLine(); + if (r < rows + 1) result.AppendLine(); } - Markdown = stringBuilder.ToString(); + Markdown = result.ToString(); } private void ModifyColumns(int newCount) @@ -180,7 +183,7 @@ public partial class MarkdownEditorTableColumn : ObservableObject [ObservableProperty] public partial string Header { get; set; } - public TableColumnAlign Alignment { get; set; } + public int Alignment { get; set; } = 0; public ObservableCollection Cells { get; private set; } = []; } diff --git a/src/Symptum.Editor/EditorPages/MarkdownEditorPage.xaml b/src/Symptum.Editor/EditorPages/MarkdownEditorPage.xaml index fdd3419..bf4ecd9 100644 --- a/src/Symptum.Editor/EditorPages/MarkdownEditorPage.xaml +++ b/src/Symptum.Editor/EditorPages/MarkdownEditorPage.xaml @@ -21,8 +21,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -35,7 +69,7 @@ - + @@ -86,11 +120,11 @@ + ToolTipService.ToolTip="Cut" Label="Cut" Click="CutButton_Click" /> + ToolTipService.ToolTip="Copy" Label="Copy" Click="CopyButton_Click" /> + ToolTipService.ToolTip="Paste" Label="Paste" Click="PasteButton_Click" /> - + @@ -232,7 +266,7 @@ + ToolTipService.ToolTip="Link" Label="Link" Click="LinkButton_Click" /> - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + @@ -288,6 +328,7 @@ + @@ -300,8 +341,12 @@ - + + + + diff --git a/src/Symptum.Editor/EditorPages/MarkdownEditorPage.xaml.cs b/src/Symptum.Editor/EditorPages/MarkdownEditorPage.xaml.cs index 416ecbe..3676d1d 100644 --- a/src/Symptum.Editor/EditorPages/MarkdownEditorPage.xaml.cs +++ b/src/Symptum.Editor/EditorPages/MarkdownEditorPage.xaml.cs @@ -15,6 +15,7 @@ public sealed partial class MarkdownEditorPage : EditorPageBase private MarkdownFileResource? _markdownResource; private ResourcePropertiesEditorDialog propertyEditorDialog = new(); private MarkdownEditorInsertTableDialog insertTableDialog = new(); + private MarkdownEditorInsertLinkDialog insertLinkDialog = new(); private const string m_CurrentDocument = "Current Document"; private const string m_Selection = "Selection"; private const string m_Indentation = " "; // NOTE: Should this support switching between Tabs ("\t") vs 4 Spaces (" ")? @@ -31,25 +32,20 @@ public MarkdownEditorPage() mdText.PreviewKeyDown += MdText_KeyDown; PreviewKeyDown += Page_PreviewKeyDown; PreviewKeyUp += Page_PreviewKeyUp; + mdText.CuttingToClipboard += (s, e) => OnClipboardEvent(); #else mdText.KeyDown += MdText_KeyDown; KeyDown += Page_KeyDown; KeyUp += Page_KeyUp; #endif - mdText.Paste += MdText_Paste; + mdText.Paste += (s, e) => OnClipboardEvent(); UpdateStatusBar(true, true); SetupFindControl(); } - private void MdText_Paste(object sender, TextControlPasteEventArgs e) - { - _textChangedReason = TextChangedReason.Paste; - StoreCurrentTextState(); - } - private void MdText_TextChanged(object sender, TextChangedEventArgs e) { - ProcessPaste(); + ProcessClipboardEvent(); _mdDirtyForSearch = true; HasUnsavedChanges = true; UpdateStatusBar(onlyCount: true); @@ -103,7 +99,7 @@ private void MdText_KeyDown(object sender, Microsoft.UI.Xaml.Input.KeyRoutedEven if (broken || invalid) { SetTextChanged(); - if (invalid) return; + if (invalid || mdText.Text.Length == 0) return; } SetTextChanging( @@ -179,74 +175,6 @@ protected override void OnSetEditableContent(IResource? resource) } } - #region Temp - - private string quotesMD = @"# Quotes - -> Text that is a quote - -## Alerts - -> [!NOTE] -> Useful information that users should know, even when skimming content. - -> [!TIP] -> Helpful advice for doing things better or more easily. - -> [!IMPORTANT] -> Key information users need to know to achieve their goal. - -> [!WARNING] -> Urgent info that needs immediate user attention to avoid problems. - -> [!CAUTION] -> Advises about risks or negative outcomes of certain actions. -"; - private string tabledMd = @"# Tables - -| abc | def | ghi | -|:---:|-----|----:| -| 1 | 2 | 3 | - -+---------+---------+ -| Header | Header | -| Column1 | Column2 | -+=========+=========+ -| 1. ab | > This is a quote -| 2. cde | > For the second column -| 3. f | -+---------+---------+ -| Second row spanning -| on two columns -+---------+---------+ -| Back | | -| to | | -| one | | -| column | | - -+---------+---------+ -| This is | a table | -+=========+=========+ - -+---+---+---+ -| AAAAA | B | -+ AAAAA +---+ -| AAAAA | C | -+---+---+---+ -| D | E | F | -+---+---+---+ - -+---+---+---+ -| AAAAA | B | -+---+---+ B + -| D | E | B | -+ D +---+---+ -| D | CCCCC | -+---+---+---+ -"; - - #endregion - private void TreeView_ItemInvoked(TreeView sender, TreeViewItemInvokedEventArgs args) { if (args.InvokedItem is DocumentNode node && node.Navigate is Action navigate) @@ -283,41 +211,17 @@ private async void PropsButton_Click(object sender, RoutedEventArgs e) } } - private void PreviewButton_Checked(object sender, RoutedEventArgs e) - { - mdText.SetValue(Grid.ColumnSpanProperty, 1); - mdTB.Visibility = Visibility.Visible; - mdTB.SetBinding(MarkdownTextBlock.TextProperty, - new Binding() { Path = new(nameof(TextBox.Text)), Source = mdText, Mode = BindingMode.OneWay }); - - outlineButton.IsEnabled = true; - if (viewDocOutline) - docOutlineTV.Visibility = sizer.Visibility = Visibility.Visible; - } - - private void PreviewButton_Unchecked(object sender, RoutedEventArgs e) + private void CutButton_Click(object sender, RoutedEventArgs e) { - mdText.SetValue(Grid.ColumnSpanProperty, 4); - mdTB.Visibility = Visibility.Collapsed; - mdTB.ClearValue(MarkdownTextBlock.TextProperty); - - outlineButton.IsEnabled = false; - docOutlineTV.Visibility = sizer.Visibility = Visibility.Collapsed; +#if HAS_UNO + OnClipboardEvent(); +#endif + mdText.CutSelectionToClipboard(); } - private bool viewDocOutline = false; - - private void OutlineButton_Checked(object sender, RoutedEventArgs e) - { - viewDocOutline = true; - docOutlineTV.Visibility = sizer.Visibility = Visibility.Visible; - } + private void CopyButton_Click(object sender, RoutedEventArgs e) => mdText.CopySelectionToClipboard(); - private void OutlineButton_Unchecked(object sender, RoutedEventArgs e) - { - viewDocOutline = false; - docOutlineTV.Visibility = sizer.Visibility = Visibility.Collapsed; - } + private void PasteButton_Click(object sender, RoutedEventArgs e) => mdText.PasteFromClipboard(); #region Find @@ -333,10 +237,7 @@ private void SetupFindControl() findControl.SelectedContext = columns[0]; } - private void FindButton_Click(object sender, RoutedEventArgs e) - { - findControl.Visibility = Visibility.Visible; - } + private void FindButton_Click(object sender, RoutedEventArgs e) => findControl.Visibility = Visibility.Visible; private void FindControl_QueryCleared(object? sender, EventArgs e) { @@ -414,10 +315,103 @@ private void FindControl_QuerySubmitted(object? sender, FindControlQuerySubmitte #endregion + #region Editor Visual States + + private void GoToEditorVisualState() + { + VisualStateManager.GoToState(this, preview ? "PreviewState" : "NoPreviewState", true); + VisualStateManager.GoToState(this, preview && viewDocOutline ? "DocOutlineState" : "NoDocOutlineState", true); + VisualStateManager.GoToState(this, preview && expandPreview ? "ExpandedState" : "NotExpandedState", true); + } + + private bool preview = false; + + private void PreviewButton_Checked(object sender, RoutedEventArgs e) + { + mdTB.SetBinding(MarkdownTextBlock.TextProperty, + new Binding() { Path = new(nameof(TextBox.Text)), Source = mdText, Mode = BindingMode.OneWay }); + + preview = true; + GoToEditorVisualState(); + } + + private void PreviewButton_Unchecked(object sender, RoutedEventArgs e) + { + mdTB.ClearValue(MarkdownTextBlock.TextProperty); + preview = false; + GoToEditorVisualState(); + } + + private bool viewDocOutline = false; + + private void DocOutlineButton_Checked(object sender, RoutedEventArgs e) + { + viewDocOutline = true; + GoToEditorVisualState(); + } + + private void DocOutlineButton_Unchecked(object sender, RoutedEventArgs e) + { + viewDocOutline = false; + GoToEditorVisualState(); + } + + private bool expandPreview = false; + + private void ExpandPreviewButton_Checked(object sender, RoutedEventArgs e) + { + expandPreview = true; + GoToEditorVisualState(); + } + + private void ExpandPreviewButton_Unchecked(object sender, RoutedEventArgs e) + { + expandPreview = false; + GoToEditorVisualState(); + } + + #endregion + #region Formatting #region Insertion + private void InsertBlock(string block) + { + if (string.IsNullOrEmpty(block)) return; // Whitespaces are allowed. + + string text = mdText.Text; + int start = mdText.SelectionStart; + int selLen = mdText.SelectionLength; + var span = text.AsSpan(); + StringBuilder result = new(); + + if (selLen == 0) + { + int newStart = start; + for (int i = start; i < span.Length; i++) // Make sure to insert the block in next line. + { + if (span[i] == '\n' || span[i] == '\r' || span[i] == '\0') + { + newStart = i; + break; + } + if (i == span.Length - 1) // If there are no line endings, just insert it at the end. + newStart = span.Length; + } + result.Append(span[..newStart]).AppendLine().AppendLine() + .Append(block).Append(span[newStart..]).AppendLine().AppendLine(); + } + else + { + result.Append(span[..start]).AppendLine().AppendLine() + .Append(block).Append(span[(start + selLen)..]).AppendLine().AppendLine(); + selLen = block.Length + 4; + } + + CommitTextFormatting(result.ToString(), start, selLen); + } + private void ToggleLineStarts(string decor, bool onlyInsert = false, bool shouldInsert = true) { int len = mdText.SelectionLength; @@ -441,6 +435,7 @@ private void ToggleLineStarts(string decor, bool onlyInsert = false, bool should StringBuilder result = new(); int currentPos = 0; int addedLen = 0; + int startOffset = 0; while (currentPos < end) { @@ -448,11 +443,11 @@ private void ToggleLineStarts(string decor, bool onlyInsert = false, bool should int lineEnd = span[currentPos..].IndexOfAny('\r', '\n'); lineEnd = lineEnd >= 0 ? currentPos + lineEnd : span.Length; var currentLine = span[lineStart..lineEnd]; - if (currentPos < start) + if (currentPos < start && lineEnd < start) { result.Append(currentLine).AppendLine(); } - else // Within selection + else // Within selection or the line of selection { if (onlyInsert || !currentLine.StartsWith(decorSpan)) { @@ -461,6 +456,7 @@ private void ToggleLineStarts(string decor, bool onlyInsert = false, bool should // Inserting the decor in front of the line. result.Append(decorSpan); addedLen += decorLen; + if (currentPos < start) startOffset = decorLen; } result.Append(currentLine).AppendLine(); } @@ -468,7 +464,10 @@ private void ToggleLineStarts(string decor, bool onlyInsert = false, bool should { // Removing the decor from the line start. result.Append(currentLine[decorLen..]).AppendLine(); - addedLen -= decorLen; + if (currentPos < start) + startOffset = -decorLen; + else + addedLen -= decorLen; } } @@ -482,7 +481,7 @@ private void ToggleLineStarts(string decor, bool onlyInsert = false, bool should if (currentPos < span.Length) result.Append(span[currentPos..]); - CommitTextFormatting(result.ToString(), start, len + addedLen); + CommitTextFormatting(result.ToString(), start + startOffset, len + addedLen); } private void InsertInFrontOfLines(string decor) => ToggleLineStarts(decor, true); @@ -537,6 +536,55 @@ private void ToggleLineStart(string decor, bool onlyInsert = false, bool shouldI private void RemoveFromFrontOfLine(string decor) => ToggleLineStart(decor, false, false); + private void SetHeadingLevel(int level) + { + int start = mdText.SelectionStart; + ReadOnlySpan span = mdText.Text.AsSpan(); + + if (start < 0 || span.Length < start || level < 1) return; + + int lineStart = -1; + for (int i = start - 1; i > 0; i--) + { + if (span[i] == '\n' || span[i] == '\r' || span[i] == '\0') + { + lineStart = i + 1; + break; + } + } + if (lineStart < 0) lineStart = 0; + + var before = span[..lineStart]; + var after = span[lineStart..]; + char decor = '#'; + StringBuilder result = new(); + if (after.Length > 0 && after[0] == decor) + { + // Count the old heading level. + int decorLen = 0; + for (int i = 0; i < after.Length; i++) + { + if (after[i] == decor) + decorLen++; + } + + if (decorLen == level) + // Remove the heading if set the same level again (i.e. toggle). + result.Append(before).Append(after[decorLen..].TrimStart()); + else + // Replace it with the new heading level. + result.Append(before).Append(new string(decor, level)).Append(after[decorLen..]); + } + else + { + // Insert the new heading level. + result.Append(before).Append(new string(decor, level)) + .Append(' ').Append(after); + } + + CommitTextFormatting(result.ToString(), start, 0); + } + #endregion #region Wrapping @@ -625,7 +673,7 @@ private void ToggleWrap(string decor, bool onlyWrap = false, bool shouldWrap = t private void CodeInlineButton_Click(object sender, RoutedEventArgs e) => ToggleWrap('`'); - private void ThBreakButton_Click(object sender, RoutedEventArgs e) => InsertInFrontOfLine(newLine + "---" + newLine); + private void ThBreakButton_Click(object sender, RoutedEventArgs e) => InsertBlock("---"); private async void TableButton_Click(object sender, RoutedEventArgs e) { @@ -635,7 +683,41 @@ private async void TableButton_Click(object sender, RoutedEventArgs e) if (result == EditorResult.Create) { HasUnsavedChanges = true; - mdText.Text = insertTableDialog.Markdown; + InsertBlock(insertTableDialog.Markdown); + } + } + + private async void LinkButton_Click(object sender, RoutedEventArgs e) + { + + insertLinkDialog.XamlRoot = XamlRoot; + var result = await insertLinkDialog.CreateAsync(); + if (result == EditorResult.Create) + { + HasUnsavedChanges = true; + InsertBlock(insertLinkDialog.Markdown); + } + } + + private void GridView_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + if (e.AddedItems.Count > 0) + { + if (e.AddedItems[0] is string h) + { + int level = h switch + { + "H1" => 1, + "H2" => 2, + "H3" => 3, + "H4" => 4, + "H5" => 5, + "H6" => 6, + _ => 1 + }; + SetHeadingLevel(level); + if (sender is GridView gv) gv.SelectedItem = null; + } } } @@ -655,9 +737,15 @@ private async void TableButton_Click(object sender, RoutedEventArgs e) private int _prevSelectionLength; private TextChangedReason _textChangedReason; - private void ProcessPaste() + private void OnClipboardEvent() + { + _textChangedReason = TextChangedReason.Clipboard; + StoreCurrentTextState(); + } + + private void ProcessClipboardEvent() { - if (_textChangedReason == TextChangedReason.Paste) + if (_textChangedReason == TextChangedReason.Clipboard) CommitHistory(); } @@ -782,7 +870,7 @@ private enum TextChangedReason TextTyped, HistoryApplied, FormattingApplied, - Paste + Clipboard } private record struct TextHistory(string OldText, int OldSelectionStart, int OldSelectionLength, string NewText, int NewSelectionStart, int NewSelectionLength); diff --git a/src/Symptum.Editor/MainPage.xaml b/src/Symptum.Editor/MainPage.xaml index f808a7f..8bfeead 100644 --- a/src/Symptum.Editor/MainPage.xaml +++ b/src/Symptum.Editor/MainPage.xaml @@ -105,10 +105,11 @@ + IsPaneOpen="True" OpenPaneLength="301" CompactPaneLength="48" DisplayMode="Inline"> - + + @@ -136,14 +137,11 @@ - - + + - - - - + ItemTemplate="{StaticResource DefaultResourceDataTemplate}" /> diff --git a/src/Symptum.Editor/Symptum.Editor.csproj b/src/Symptum.Editor/Symptum.Editor.csproj index 697fa5b..743902a 100644 --- a/src/Symptum.Editor/Symptum.Editor.csproj +++ b/src/Symptum.Editor/Symptum.Editor.csproj @@ -62,6 +62,7 @@ + diff --git a/src/Symptum.UI/Markdown/Extensions.cs b/src/Symptum.UI/Markdown/Extensions.cs index 9f3a654..70aba00 100644 --- a/src/Symptum.UI/Markdown/Extensions.cs +++ b/src/Symptum.UI/Markdown/Extensions.cs @@ -8,6 +8,8 @@ using System.Text; using ColorCode; using Markdig.Syntax; +using HtmlAgilityPack; +using Symptum.UI.Markdown.TextElements.Html; namespace Symptum.UI.Markdown; @@ -158,9 +160,22 @@ public static Uri GetUri(string? url, string? @base) #pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type. } + internal static HtmlElementType TagToType(this string tag) + { + return tag.ToLower() switch + { + "address" or "article" or "aside" or "details" or "blockquote" or + "canvas" or "dd" or "div" or "dl" or "dt" or "fieldset" or "figcaption" or + "figure" or "footer" or "form" or "h1" or "h2" or "h3" or "h4" or "h5" or "h6" + or "header" or "hr" or "li" or "main" or "nav" or "noscript" or "ol" or "p" or + "pre" or "section" or "table" or "tfoot" or "ul" => HtmlElementType.Block, + _ => HtmlElementType.Inline, + }; + } + public static bool IsHeading(this string tag) { - List headings = new() { "h1", "h2", "h3", "h4", "h5", "h6" }; + List headings = ["h1", "h2", "h3", "h4", "h5", "h6"]; return headings.Contains(tag.ToLower()); } @@ -242,4 +257,11 @@ public static SolidColorBrush GetAccentColorBrush() return accentBrush; } + + public static string GetAttribute(this HtmlNode node, string attributeName, string defaultValue) + { + ArgumentNullException.ThrowIfNull(attributeName); + + return node.Attributes?[attributeName]?.Value ?? defaultValue; + } } diff --git a/src/Symptum.UI/Markdown/HtmlWriter.cs b/src/Symptum.UI/Markdown/HtmlWriter.cs new file mode 100644 index 0000000..5249efd --- /dev/null +++ b/src/Symptum.UI/Markdown/HtmlWriter.cs @@ -0,0 +1,84 @@ +using HtmlAgilityPack; +using Symptum.UI.Markdown.Renderers; +using Symptum.UI.Markdown.TextElements; +using Symptum.UI.Markdown.TextElements.Html; + +namespace Symptum.UI.Markdown; + +internal class HtmlWriter +{ + public static void WriteHtml(WinUIRenderer renderer, HtmlNodeCollection nodes) + { + if (nodes == null || nodes.Count == 0) return; + foreach (var node in nodes) + { + HtmlElementType elementType = node.Name.TagToType(); + if (node.NodeType == HtmlNodeType.Text) + { + renderer.WriteText(node.InnerText); + } + else if (node.NodeType == HtmlNodeType.Element && elementType == HtmlElementType.Inline) + { + // detect br here + var inlineTagName = node.Name.ToLower(); + if (inlineTagName == "br") + { + renderer.WriteInline(new LineBreakElement()); + } + else if (inlineTagName == "a") + { + IAddChild hyperLink; + if (node.ChildNodes.Any(n => n.Name != "#text")) + { + hyperLink = new HyperlinkButtonElement(node, renderer.Configuration.BaseUrl, renderer.Configuration, renderer.LinkHandler); + } + else + { + hyperLink = new HyperlinkElement(node, renderer.Configuration.BaseUrl, renderer.LinkHandler); + } + renderer.Push(hyperLink); + WriteHtml(renderer, node.ChildNodes); + renderer.Pop(); + } + else if (inlineTagName == "img") + { + var image = new ImageElement(node, renderer.Configuration); + renderer.WriteInline(image); + } + else + { + var inline = new HtmlInlineElement(node); + renderer.Push(inline); + WriteHtml(renderer, node.ChildNodes); + renderer.Pop(); + } + } + else if (node.NodeType == HtmlNodeType.Element && elementType == HtmlElementType.Block) + { + IAddChild block; + var tag = node.Name.ToLower(); + if (tag == "details") + { + block = new HtmlDetailsElement(node, renderer.Configuration); + if (node.ChildNodes.FirstOrDefault(x => x.Name == "summary" || x.Name == "header") is HtmlNode child) + node.ChildNodes.Remove(child); + renderer.Push(block); + WriteHtml(renderer, node.ChildNodes); + } + else if (tag.IsHeading()) + { + var heading = new HeadingElement(node, renderer.Configuration, renderer.DocumentOutline); + renderer.Push(heading); + WriteHtml(renderer, node.ChildNodes); + } + else + { + block = new HtmlBlockElement(node, renderer.Configuration); + renderer.Push(block); + WriteHtml(renderer, node.ChildNodes); + } + renderer.Pop(); + } + } + } +} diff --git a/src/Symptum.UI/Markdown/MarkdownTextBlock.xaml.cs b/src/Symptum.UI/Markdown/MarkdownTextBlock.xaml.cs index 0b85ea9..053501f 100644 --- a/src/Symptum.UI/Markdown/MarkdownTextBlock.xaml.cs +++ b/src/Symptum.UI/Markdown/MarkdownTextBlock.xaml.cs @@ -1,4 +1,5 @@ using Markdig; +using Markdig.Syntax; using Symptum.Markdown.Reference; using Symptum.UI.Markdown.Renderers; using Symptum.UI.Markdown.TextElements; @@ -58,12 +59,28 @@ private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedE { if (d is MarkdownTextBlock self && e.NewValue is string text) { - self.ApplyText(text, true); + self.ApplyText(true); } } #endregion + #region MarkdownDocument + + public static readonly DependencyProperty MarkdownDocumentProperty = DependencyProperty.Register( + nameof(MarkdownDocument), + typeof(MarkdownDocument), + typeof(MarkdownTextBlock), + new PropertyMetadata(null)); + + public MarkdownDocument? MarkdownDocument + { + get => (MarkdownDocument)GetValue(MarkdownDocumentProperty); + private set => SetValue(MarkdownDocumentProperty, value); + } + + #endregion + public DocumentOutline DocumentOutline { get; } #endregion @@ -103,17 +120,30 @@ private void ApplyConfig(MarkdownConfiguration config) } } - private void ApplyText(string text, bool rerender) + private void ApplyText(bool rerender) { - Markdig.Syntax.MarkdownDocument markdown = Markdig.Markdown.Parse(text ?? string.Empty, _pipeline); if (_renderer != null) { if (rerender) { _renderer.ReloadDocument(); } - _renderer.Render(markdown); + + MarkdownDocument? markdown = null; + try + { + if (!string.IsNullOrEmpty(Text)) + markdown = Markdig.Markdown.Parse(Text, _pipeline); + } + catch { } + + if (markdown != null) + { + MarkdownDocument = markdown; + _renderer.Render(markdown); + } } + else _document.StackPanel.Children.Clear(); } private void Build() @@ -125,7 +155,7 @@ private void Build() _renderer = new WinUIRenderer(_document, Configuration, DocumentOutline); } _pipeline.Setup(_renderer); - ApplyText(Text, false); + ApplyText(false); } } } diff --git a/src/Symptum.UI/Markdown/MarkdownThemes.cs b/src/Symptum.UI/Markdown/MarkdownThemes.cs index da7f3c3..f219609 100644 --- a/src/Symptum.UI/Markdown/MarkdownThemes.cs +++ b/src/Symptum.UI/Markdown/MarkdownThemes.cs @@ -106,5 +106,12 @@ public sealed partial class MarkdownThemes : DependencyObject #endregion + #region Address Block + + public Style? AddressBlockTextBlockStyle { get; set; } = Application.Current.Resources.ContainsKey("DefaultAddressBlockTextBlockStyle") ? + Application.Current.Resources["DefaultAddressBlockTextBlockStyle"] as Style : null; + + #endregion + public MarkdownThemes() { } } diff --git a/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/HtmlBlockRenderer.cs b/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/HtmlBlockRenderer.cs new file mode 100644 index 0000000..fa1a05a --- /dev/null +++ b/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/HtmlBlockRenderer.cs @@ -0,0 +1,38 @@ +using System.Text; +using System.Text.RegularExpressions; +using HtmlAgilityPack; +using Markdig.Syntax; + +namespace Symptum.UI.Markdown.Renderers.ObjectRenderers; + +internal partial class HtmlBlockRenderer : WinUIObjectRenderer +{ + protected override void Write(WinUIRenderer renderer, HtmlBlock obj) + { + ArgumentNullException.ThrowIfNull(renderer); + ArgumentNullException.ThrowIfNull(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 = WhiteSpaceRegex().Replace(stringBuilder.ToString(), ""); + html = HtmlWhiteSpaceRegex().Replace(html, " "); + var doc = new HtmlDocument(); + doc.LoadHtml(html); + HtmlWriter.WriteHtml(renderer, doc.DocumentNode.ChildNodes); + } + + [GeneratedRegex(@"\t|\n|\r", RegexOptions.Compiled)] + private static partial Regex WhiteSpaceRegex(); + + [GeneratedRegex(@" ", RegexOptions.Compiled)] + private static partial Regex HtmlWhiteSpaceRegex(); +} diff --git a/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Inlines/HtmlEntityInlineRenderer.cs b/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Inlines/HtmlEntityInlineRenderer.cs index 6d2186d..3e14b42 100644 --- a/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Inlines/HtmlEntityInlineRenderer.cs +++ b/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Inlines/HtmlEntityInlineRenderer.cs @@ -1,4 +1,3 @@ - using Markdig.Syntax.Inlines; namespace Symptum.UI.Markdown.Renderers.ObjectRenderers.Inlines; @@ -12,6 +11,5 @@ protected override void Write(WinUIRenderer renderer, HtmlEntityInline obj) Markdig.Helpers.StringSlice transcoded = obj.Transcoded; renderer.WriteText(ref transcoded); - // todo: wtf is this? } } diff --git a/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Inlines/HtmlInlineRenderer.cs b/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Inlines/HtmlInlineRenderer.cs new file mode 100644 index 0000000..57bb3d6 --- /dev/null +++ b/src/Symptum.UI/Markdown/Renderers/ObjectRenderers/Inlines/HtmlInlineRenderer.cs @@ -0,0 +1,18 @@ +using HtmlAgilityPack; +using Markdig.Syntax.Inlines; + +namespace Symptum.UI.Markdown.Renderers.ObjectRenderers.Inlines; + +internal class HtmlInlineRenderer : WinUIObjectRenderer +{ + protected override void Write(WinUIRenderer renderer, HtmlInline obj) + { + ArgumentNullException.ThrowIfNull(renderer); + ArgumentNullException.ThrowIfNull(obj); + + var html = obj.Tag; + var doc = new HtmlDocument(); + doc.LoadHtml(html); + HtmlWriter.WriteHtml(renderer, doc.DocumentNode.ChildNodes); + } +} diff --git a/src/Symptum.UI/Markdown/Renderers/WinUIRenderer.cs b/src/Symptum.UI/Markdown/Renderers/WinUIRenderer.cs index 3c2647a..5566b3c 100644 --- a/src/Symptum.UI/Markdown/Renderers/WinUIRenderer.cs +++ b/src/Symptum.UI/Markdown/Renderers/WinUIRenderer.cs @@ -163,12 +163,16 @@ protected virtual void LoadRenderers() 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()); + // Html renderers + ObjectRenderers.Add(new HtmlBlockRenderer()); + ObjectRenderers.Add(new HtmlEntityInlineRenderer()); + ObjectRenderers.Add(new HtmlInlineRenderer()); + // Extension renderers ObjectRenderers.Add(new TableRenderer()); ObjectRenderers.Add(new TaskListRenderer()); diff --git a/src/Symptum.UI/Markdown/TextElements/BlockContainerElement.cs b/src/Symptum.UI/Markdown/TextElements/BlockContainerElement.cs index 94e5115..e37aa54 100644 --- a/src/Symptum.UI/Markdown/TextElements/BlockContainerElement.cs +++ b/src/Symptum.UI/Markdown/TextElements/BlockContainerElement.cs @@ -8,10 +8,7 @@ public class BlockContainerElement : IAddChild private SContainer _container = new(); private FlowDocumentElement _flowDocument; - public STextElement TextElement - { - get => _container; - } + public STextElement TextElement => _container; public BlockContainerElement(ContainerBlock containerBlock, MarkdownConfiguration config) { diff --git a/src/Symptum.UI/Markdown/TextElements/CodeBlockElement.cs b/src/Symptum.UI/Markdown/TextElements/CodeBlockElement.cs index 6785f44..20235ba 100644 --- a/src/Symptum.UI/Markdown/TextElements/CodeBlockElement.cs +++ b/src/Symptum.UI/Markdown/TextElements/CodeBlockElement.cs @@ -11,10 +11,7 @@ public class CodeBlockElement : IAddChild private SContainer _container = new(); private MarkdownConfiguration _config; - public STextElement TextElement - { - get => _container; - } + public STextElement TextElement => _container; public CodeBlockElement(CodeBlock codeBlock, MarkdownConfiguration config) { diff --git a/src/Symptum.UI/Markdown/TextElements/CodeInlineElement.cs b/src/Symptum.UI/Markdown/TextElements/CodeInlineElement.cs index eeb3cde..095eaa9 100644 --- a/src/Symptum.UI/Markdown/TextElements/CodeInlineElement.cs +++ b/src/Symptum.UI/Markdown/TextElements/CodeInlineElement.cs @@ -8,10 +8,7 @@ public class CodeInlineElement : IAddChild private SContainer _container = new(); private MarkdownConfiguration _config; - public STextElement TextElement - { - get => _container; - } + public STextElement TextElement => _container; public CodeInlineElement(CodeInline codeInline, MarkdownConfiguration config) { diff --git a/src/Symptum.UI/Markdown/TextElements/EmphasisInlineElement.cs b/src/Symptum.UI/Markdown/TextElements/EmphasisInlineElement.cs index 4cc2481..d757e5a 100644 --- a/src/Symptum.UI/Markdown/TextElements/EmphasisInlineElement.cs +++ b/src/Symptum.UI/Markdown/TextElements/EmphasisInlineElement.cs @@ -14,10 +14,7 @@ public class EmphasisInlineElement : IAddChild private bool _isItalic; private bool _isStrikeThrough; - public STextElement TextElement - { - get => inline; - } + public STextElement TextElement => inline; public EmphasisInlineElement(EmphasisInline emphasisInline) { diff --git a/src/Symptum.UI/Markdown/TextElements/FlowDocumentElement.cs b/src/Symptum.UI/Markdown/TextElements/FlowDocumentElement.cs index 846957f..3f35663 100644 --- a/src/Symptum.UI/Markdown/TextElements/FlowDocumentElement.cs +++ b/src/Symptum.UI/Markdown/TextElements/FlowDocumentElement.cs @@ -6,10 +6,7 @@ public class FlowDocumentElement : IAddChild private SContainer _container = new(); private MarkdownConfiguration _config; - public STextElement TextElement - { - get => _container; - } + public STextElement TextElement => _container; public StackPanel StackPanel { diff --git a/src/Symptum.UI/Markdown/TextElements/HeadingElement.cs b/src/Symptum.UI/Markdown/TextElements/HeadingElement.cs index 844c234..d891405 100644 --- a/src/Symptum.UI/Markdown/TextElements/HeadingElement.cs +++ b/src/Symptum.UI/Markdown/TextElements/HeadingElement.cs @@ -1,3 +1,4 @@ +using HtmlAgilityPack; using Markdig.Renderers.Html; using Markdig.Syntax; @@ -5,22 +6,40 @@ namespace Symptum.UI.Markdown.TextElements; public class HeadingElement : IAddChild { - private SParagraph _paragraph; + private SParagraph _paragraph = new(); private HeadingBlock? _headingBlock; - private MarkdownConfiguration _config; + private HtmlNode? _htmlNode; + private MarkdownConfiguration? _config; - public STextElement TextElement - { - get => _paragraph; - } + public STextElement TextElement => _paragraph; public HeadingElement(HeadingBlock headingBlock, MarkdownConfiguration config, DocumentOutline outline) { _headingBlock = headingBlock; - _paragraph = new(); - _config = config; + LoadHeadingElement(config, outline, headingBlock.Level, + headingBlock.GetAttributes().Id, headingBlock.Inline?.FirstChild?.ToString()); + } + + public HeadingElement(HtmlNode htmlNode, MarkdownConfiguration config, DocumentOutline outline) + { + _htmlNode = htmlNode; + var align = _htmlNode.GetAttribute("align", "left"); + _paragraph.TextAlignment = align switch + { + "left" => TextAlignment.Left, + "right" => TextAlignment.Right, + "center" => TextAlignment.Center, + "justify" => TextAlignment.Justify, + _ => TextAlignment.Left, + }; + + if (int.TryParse(htmlNode.Name.AsSpan(1), out int level)) + LoadHeadingElement(config, outline, level, htmlNode.Id, htmlNode.InnerText); + } - int level = headingBlock.Level; + private void LoadHeadingElement(MarkdownConfiguration config, DocumentOutline outline, int level, string? id, string? title) + { + _config = config; _paragraph.TextBlockStyle = level switch { 1 => _config.Themes.H1TextBlockStyle, @@ -33,7 +52,7 @@ public HeadingElement(HeadingBlock headingBlock, MarkdownConfiguration config, D DocumentNode node = new() { - Id = headingBlock.GetAttributes().Id, + Id = id, Level = level switch { 1 => DocumentLevel.Heading1, @@ -44,7 +63,7 @@ public HeadingElement(HeadingBlock headingBlock, MarkdownConfiguration config, D _ => DocumentLevel.Heading6, }, Navigate = OnNavigate, - Title = headingBlock.Inline?.FirstChild?.ToString() + Title = title }; outline.PushNode(node); diff --git a/src/Symptum.UI/Markdown/TextElements/Html/HtmlBlockElement.cs b/src/Symptum.UI/Markdown/TextElements/Html/HtmlBlockElement.cs new file mode 100644 index 0000000..d6ae19e --- /dev/null +++ b/src/Symptum.UI/Markdown/TextElements/Html/HtmlBlockElement.cs @@ -0,0 +1,43 @@ +using HtmlAgilityPack; + +namespace Symptum.UI.Markdown.TextElements.Html; + +internal class HtmlBlockElement : IAddChild +{ + private HtmlNode _htmlNode; + private SParagraph _paragraph; + private MarkdownConfiguration _config; + + public STextElement TextElement => _paragraph; + + public HtmlBlockElement(HtmlNode node, MarkdownConfiguration config) + { + _htmlNode = node; + _config = config; + var align = _htmlNode.GetAttribute("align", "left"); + _paragraph = new() + { + TextAlignment = align switch + { + "left" => TextAlignment.Left, + "right" => TextAlignment.Right, + "center" => TextAlignment.Center, + "justify" => TextAlignment.Justify, + _ => TextAlignment.Left, + } + }; + StyleBlock(); + } + + public void AddChild(IAddChild child) => _paragraph.AddInline(child.TextElement); + + private void StyleBlock() + { + switch (_htmlNode.Name.ToLower()) + { + case "address": + _paragraph.TextBlockStyle = _config.Themes.AddressBlockTextBlockStyle; + break; + } + } +} diff --git a/src/Symptum.UI/Markdown/TextElements/Html/HtmlDetailsElement.cs b/src/Symptum.UI/Markdown/TextElements/Html/HtmlDetailsElement.cs new file mode 100644 index 0000000..f8162b1 --- /dev/null +++ b/src/Symptum.UI/Markdown/TextElements/Html/HtmlDetailsElement.cs @@ -0,0 +1,44 @@ +using HtmlAgilityPack; + +namespace Symptum.UI.Markdown.TextElements.Html; + +internal class HtmlDetailsElement : IAddChild +{ + private HtmlNode _htmlNode; + private SContainer _container = new(); + private Expander _expander; + private FlowDocumentElement _flowDocument; + + public STextElement TextElement => _container; + + public HtmlDetailsElement(HtmlNode details, MarkdownConfiguration config) + { + _htmlNode = details; + + var header = _htmlNode.ChildNodes + .FirstOrDefault( + x => x.Name == "summary" || + x.Name == "header"); + + _expander = new Expander + { + HorizontalAlignment = HorizontalAlignment.Stretch + }; + _flowDocument = new(config, false); + _flowDocument.StackPanel.HorizontalAlignment = HorizontalAlignment.Stretch; + _expander.Content = _flowDocument.StackPanel; + var headerBlock = new TextBlock + { + Text = header?.InnerText, + HorizontalAlignment = HorizontalAlignment.Stretch + }; + _expander.Header = headerBlock; + _container.UIElement = _expander; + } + + public void AddChild(IAddChild child) + { + _flowDocument.AddChild(child); + } +} + diff --git a/src/Symptum.UI/Markdown/TextElements/Html/HtmlElementType.cs b/src/Symptum.UI/Markdown/TextElements/Html/HtmlElementType.cs new file mode 100644 index 0000000..ad3afef --- /dev/null +++ b/src/Symptum.UI/Markdown/TextElements/Html/HtmlElementType.cs @@ -0,0 +1,7 @@ +namespace Symptum.UI.Markdown.TextElements.Html; + +internal enum HtmlElementType +{ + Block, + Inline, +} diff --git a/src/Symptum.UI/Markdown/TextElements/Html/HtmlInlineElement.cs b/src/Symptum.UI/Markdown/TextElements/Html/HtmlInlineElement.cs new file mode 100644 index 0000000..05b14f3 --- /dev/null +++ b/src/Symptum.UI/Markdown/TextElements/Html/HtmlInlineElement.cs @@ -0,0 +1,56 @@ +using HtmlAgilityPack; +using Microsoft.UI.Text; +using Microsoft.UI.Xaml.Documents; +using Windows.UI.Text; + +namespace Symptum.UI.Markdown.TextElements.Html; + +internal class HtmlInlineElement : IAddChild +{ + private HtmlNode _htmlNode; + private Span _span = new(); + private SInline inline; + + public STextElement TextElement => inline; + + public HtmlInlineElement(HtmlNode node) + { + _htmlNode = node; + inline = new() + { + Inline = _span + }; + StyleInline(); + } + + public void AddChild(IAddChild child) + { + if (child is IAddChild textElement && textElement.TextElement is SInline _inline) + { + _span.Inlines.Add(_inline.Inline); + } + else + { + + } + } + + private void StyleInline() + { + switch (_htmlNode.Name.ToLower()) + { + case "em": + _span.FontStyle = FontStyle.Italic; + break; + case "b": + _span.FontWeight = FontWeights.Bold; + break; + case "s": + _span.TextDecorations = TextDecorations.Strikethrough; + break; + case "u": + _span.TextDecorations = TextDecorations.Underline; + break; + } + } +} diff --git a/src/Symptum.UI/Markdown/TextElements/HyperlinkButtonElement.cs b/src/Symptum.UI/Markdown/TextElements/HyperlinkButtonElement.cs index e18cd02..947855e 100644 --- a/src/Symptum.UI/Markdown/TextElements/HyperlinkButtonElement.cs +++ b/src/Symptum.UI/Markdown/TextElements/HyperlinkButtonElement.cs @@ -1,3 +1,4 @@ +using HtmlAgilityPack; using Markdig.Syntax.Inlines; namespace Symptum.UI.Markdown.TextElements; @@ -9,21 +10,30 @@ public class HyperlinkButtonElement : IAddChild private FlowDocumentElement? _flowDoc; private string? _baseUrl; private LinkInline? _linkInline; + private HtmlNode? _htmlNode; private MarkdownConfiguration _config; private ILinkHandler? _linkHandler; private string? _url; - public STextElement TextElement - { - get => _container; - } + public STextElement TextElement => _container; + + public HyperlinkButtonElement(LinkInline linkInline, string? baseUrl, MarkdownConfiguration config, ILinkHandler? linkHandler) : + this(linkInline.GetDynamicUrl != null ? linkInline.GetDynamicUrl() ?? linkInline.Url : linkInline.Url, + baseUrl, config, linkHandler, linkInline.Title, linkInline) + { } + + public HyperlinkButtonElement(HtmlNode htmlNode, string? baseUrl, MarkdownConfiguration config, ILinkHandler? linkHandler) : + this(htmlNode.GetAttribute("href", "#"), baseUrl, config, linkHandler, htmlNode.InnerText, htmlNode: htmlNode) + { } - public HyperlinkButtonElement(LinkInline linkInline, string? baseUrl, MarkdownConfiguration config, ILinkHandler? linkHandler) + private HyperlinkButtonElement(string? url, string? baseUrl, MarkdownConfiguration config, ILinkHandler? linkHandler, + string? title, LinkInline? linkInline = null, HtmlNode? htmlNode = null) { + _linkInline = linkInline; + _htmlNode = htmlNode; _baseUrl = baseUrl; _config = config; - _url = linkInline.GetDynamicUrl != null ? linkInline.GetDynamicUrl() ?? linkInline.Url : linkInline.Url; - _linkInline = linkInline; + _url = url; _linkHandler = linkHandler; _hyperLinkButton = new HyperlinkButton @@ -35,15 +45,13 @@ public HyperlinkButtonElement(LinkInline linkInline, string? baseUrl, MarkdownCo { _linkHandler?.HandleNavigation(_url, _baseUrl); }; - if (!string.IsNullOrWhiteSpace(linkInline.Title)) - ToolTipService.SetToolTip(_hyperLinkButton, linkInline.Title); + + if (!string.IsNullOrWhiteSpace(title)) + ToolTipService.SetToolTip(_hyperLinkButton, title); else ToolTipService.SetToolTip(_hyperLinkButton, _url); - if (_linkInline != null) - { - _flowDoc = new FlowDocumentElement(_config, false); - } + _flowDoc = new FlowDocumentElement(_config, false); _hyperLinkButton.Content = _flowDoc?.StackPanel; _container.UIElement = _hyperLinkButton; } diff --git a/src/Symptum.UI/Markdown/TextElements/HyperlinkElement.cs b/src/Symptum.UI/Markdown/TextElements/HyperlinkElement.cs index 1cc36c5..25e04f9 100644 --- a/src/Symptum.UI/Markdown/TextElements/HyperlinkElement.cs +++ b/src/Symptum.UI/Markdown/TextElements/HyperlinkElement.cs @@ -1,3 +1,4 @@ +using HtmlAgilityPack; using Markdig.Syntax.Inlines; using Microsoft.UI.Xaml.Documents; @@ -7,21 +8,29 @@ public class HyperlinkElement : IAddChild { private Hyperlink _hyperlink; private SInline inline; - private LinkInline? _linkInline; private string? _baseUrl; + private LinkInline? _linkInline; + private HtmlNode? _htmlNode; private ILinkHandler? _linkHandler; private string? _url; - public STextElement TextElement - { - get => inline; - } + public STextElement TextElement => inline; - public HyperlinkElement(LinkInline linkInline, string? baseUrl, ILinkHandler? linkHandler) + public HyperlinkElement(LinkInline linkInline, string? baseUrl, ILinkHandler? linkHandler) : + this(linkInline.GetDynamicUrl != null ? linkInline.GetDynamicUrl() ?? linkInline.Url : linkInline.Url, + baseUrl, linkHandler, linkInline.Title, linkInline) + { } + + public HyperlinkElement(HtmlNode htmlNode, string? baseUrl, ILinkHandler? linkHandler) : + this(htmlNode.GetAttribute("href", "#"), baseUrl, linkHandler, htmlNode.InnerText, htmlNode: htmlNode) + { } + + private HyperlinkElement(string? url, string? baseUrl, ILinkHandler? linkHandler, string? title, LinkInline? linkInline = null, HtmlNode? htmlNode = null) { - _baseUrl = baseUrl; - _url = linkInline.GetDynamicUrl != null ? linkInline.GetDynamicUrl() ?? linkInline.Url : linkInline.Url; _linkInline = linkInline; + _htmlNode = htmlNode; + _url = url; + _baseUrl = baseUrl; _linkHandler = linkHandler; _hyperlink = new Hyperlink(); @@ -30,8 +39,8 @@ public HyperlinkElement(LinkInline linkInline, string? baseUrl, ILinkHandler? li _linkHandler?.HandleNavigation(_url, _baseUrl); }; - if (!string.IsNullOrWhiteSpace(linkInline.Title)) - ToolTipService.SetToolTip(_hyperlink, linkInline.Title); + if (!string.IsNullOrWhiteSpace(title)) + ToolTipService.SetToolTip(_hyperlink, title); else ToolTipService.SetToolTip(_hyperlink, _url); diff --git a/src/Symptum.UI/Markdown/TextElements/ImageElement.cs b/src/Symptum.UI/Markdown/TextElements/ImageElement.cs index fee89bb..8b96e92 100644 --- a/src/Symptum.UI/Markdown/TextElements/ImageElement.cs +++ b/src/Symptum.UI/Markdown/TextElements/ImageElement.cs @@ -3,6 +3,8 @@ using Microsoft.UI.Xaml.Media.Imaging; using System.Runtime.InteropServices.WindowsRuntime; using Windows.Foundation; +using HtmlAgilityPack; +using System.Globalization; namespace Symptum.UI.Markdown.TextElements; @@ -10,6 +12,7 @@ public class ImageElement : IAddChild { private SContainer _container = new(); private LinkInline? _linkInline; + private HtmlNode? _htmlNode; private Image _image = new(); private Uri _uri; private IImageProvider? _imageProvider; @@ -18,10 +21,7 @@ public class ImageElement : IAddChild private double _precedentHeight; private bool _loaded; - public STextElement TextElement - { - get => _container; - } + public STextElement TextElement => _container; public ImageElement(LinkInline linkInline, Uri uri, MarkdownConfiguration config) { @@ -41,6 +41,35 @@ public ImageElement(LinkInline linkInline, Uri uri, MarkdownConfiguration config } } + public ImageElement(HtmlNode htmlNode, MarkdownConfiguration config) + { + if (Uri.TryCreate(htmlNode.GetAttribute("src", "#"), UriKind.RelativeOrAbsolute, out Uri? uri)) + _uri = uri; + + _htmlNode = htmlNode; + _imageProvider = config.ImageProvider; + _svgRenderer = config.SVGRenderer ?? new DefaultSVGRenderer(); + Init(); + int.TryParse(htmlNode.GetAttribute("width", "0"), + NumberStyles.Integer, + CultureInfo.InvariantCulture, + out var width); + + int.TryParse(htmlNode.GetAttribute("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; diff --git a/src/Symptum.UI/Markdown/TextElements/LineBreakElement.cs b/src/Symptum.UI/Markdown/TextElements/LineBreakElement.cs index 2321c7b..c572935 100644 --- a/src/Symptum.UI/Markdown/TextElements/LineBreakElement.cs +++ b/src/Symptum.UI/Markdown/TextElements/LineBreakElement.cs @@ -6,10 +6,7 @@ public class LineBreakElement : IAddChild { private SInline inline; - public STextElement TextElement - { - get => inline; - } + public STextElement TextElement => inline; public LineBreakElement() { diff --git a/src/Symptum.UI/Markdown/TextElements/ListElement.cs b/src/Symptum.UI/Markdown/TextElements/ListElement.cs index d7b2215..438a5ed 100644 --- a/src/Symptum.UI/Markdown/TextElements/ListElement.cs +++ b/src/Symptum.UI/Markdown/TextElements/ListElement.cs @@ -15,10 +15,7 @@ public class ListElement : IAddChild private int _index = 1; private const string _dot = "• "; - public STextElement TextElement - { - get => _container; - } + public STextElement TextElement => _container; public ListElement(ListBlock listBlock, MarkdownConfiguration config, bool isTopLevel = true) { diff --git a/src/Symptum.UI/Markdown/TextElements/ParagraphElement.cs b/src/Symptum.UI/Markdown/TextElements/ParagraphElement.cs index 3ec61ea..92c8b09 100644 --- a/src/Symptum.UI/Markdown/TextElements/ParagraphElement.cs +++ b/src/Symptum.UI/Markdown/TextElements/ParagraphElement.cs @@ -8,10 +8,7 @@ public class ParagraphElement : IAddChild private SParagraph _paragraph; private MarkdownConfiguration _config; - public STextElement TextElement - { - get => _paragraph; - } + public STextElement TextElement => _paragraph; public ParagraphElement(ParagraphBlock paragraphBlock, MarkdownConfiguration config) { diff --git a/src/Symptum.UI/Markdown/TextElements/QuoteElement.cs b/src/Symptum.UI/Markdown/TextElements/QuoteElement.cs index 9dada39..24beb88 100644 --- a/src/Symptum.UI/Markdown/TextElements/QuoteElement.cs +++ b/src/Symptum.UI/Markdown/TextElements/QuoteElement.cs @@ -9,10 +9,7 @@ public class QuoteElement : IAddChild private FlowDocumentElement _flowDocument; private QuoteBlock _quoteBlock; - public STextElement TextElement - { - get => _container; - } + public STextElement TextElement => _container; public QuoteElement(QuoteBlock quoteBlock, MarkdownConfiguration config, StringSlice? kind = null) { diff --git a/src/Symptum.UI/Markdown/TextElements/ReferenceInlineElement.cs b/src/Symptum.UI/Markdown/TextElements/ReferenceInlineElement.cs index f8a9d76..689843e 100644 --- a/src/Symptum.UI/Markdown/TextElements/ReferenceInlineElement.cs +++ b/src/Symptum.UI/Markdown/TextElements/ReferenceInlineElement.cs @@ -9,10 +9,7 @@ public class ReferenceInlineElement : IAddChild private ReferenceInline _referenceInline; private SInline _inline; - public STextElement TextElement - { - get => _inline; - } + public STextElement TextElement => _inline; public ReferenceInlineElement(ReferenceInline referenceInline) { diff --git a/src/Symptum.UI/Markdown/TextElements/STextElements.cs b/src/Symptum.UI/Markdown/TextElements/STextElements.cs index 1fe365d..73e5ff5 100644 --- a/src/Symptum.UI/Markdown/TextElements/STextElements.cs +++ b/src/Symptum.UI/Markdown/TextElements/STextElements.cs @@ -96,7 +96,8 @@ private TextBlock CreateTextBlock(List inlines) TextBlock _textBlock = new() { Style = TextBlockStyle, - TextWrapping = TextWrapping.Wrap + TextWrapping = TextWrapping.Wrap, + TextAlignment = TextAlignment }; foreach (Inline _inline in inlines) diff --git a/src/Symptum.UI/Markdown/TextElements/TableCellElement.cs b/src/Symptum.UI/Markdown/TextElements/TableCellElement.cs index ec289c4..635ff17 100644 --- a/src/Symptum.UI/Markdown/TextElements/TableCellElement.cs +++ b/src/Symptum.UI/Markdown/TextElements/TableCellElement.cs @@ -12,30 +12,17 @@ public class TableCellElement : IAddChild private Grid _grid; private SContainer _container = new(); - public STextElement TextElement - { - get => _container; - } + public STextElement TextElement => _container; - public int ColumnSpan - { - get => _tableCell.ColumnSpan; - } + public int ColumnSpan => _tableCell.ColumnSpan; - public int RowSpan - { - get => _tableCell.RowSpan; - } + public int RowSpan => _tableCell.RowSpan; - public int ColumnIndex - { - get => _columnIndex; - } + public int ColumnIndex => _columnIndex; - public int RowIndex - { - get => _rowIndex; - } + public int RowIndex => _rowIndex; + + public bool IsHeader => _isHeader; public TableCellElement(TableCell tableCell, MarkdownConfiguration config, TextAlignment textAlignment, bool isHeader, int columnIndex, int rowIndex) { diff --git a/src/Symptum.UI/Markdown/TextElements/TableElement.cs b/src/Symptum.UI/Markdown/TextElements/TableElement.cs index c013ae9..d8f094f 100644 --- a/src/Symptum.UI/Markdown/TextElements/TableElement.cs +++ b/src/Symptum.UI/Markdown/TextElements/TableElement.cs @@ -11,10 +11,7 @@ public class TableElement : IAddChild private int _rowCount; private MarkdownConfiguration _config; - public STextElement TextElement - { - get => _container; - } + public STextElement TextElement => _container; public TableElement(Table table, MarkdownConfiguration config) { @@ -53,7 +50,7 @@ public void AddChild(IAddChild child) cell.BorderThickness = GetBorderThickness(cellChild.RowIndex, cellChild.ColumnIndex); cell.CornerRadius = GetCornerRadius(cellChild.RowIndex, cellChild.ColumnIndex, cellChild.RowSpan, cellChild.ColumnSpan); - if (cellChild.RowIndex % 2 == 0 && cellChild.RowIndex != 0) + if (!cellChild.IsHeader && cellChild.RowIndex % 2 == 0) { cell.Style = _config.Themes.AltTableCellGridStyle; } diff --git a/src/Symptum.UI/Markdown/TextElements/TaskListCheckBoxElement.cs b/src/Symptum.UI/Markdown/TextElements/TaskListCheckBoxElement.cs index f7e7cc2..fa3794b 100644 --- a/src/Symptum.UI/Markdown/TextElements/TaskListCheckBoxElement.cs +++ b/src/Symptum.UI/Markdown/TextElements/TaskListCheckBoxElement.cs @@ -7,10 +7,7 @@ public class TaskListCheckBoxElement : IAddChild private TaskList _taskList; private SContainer _container = new(); - public STextElement TextElement - { - get => _container; - } + public STextElement TextElement => _container; public TaskListCheckBoxElement(TaskList taskList) { diff --git a/src/Symptum.UI/Markdown/TextElements/TextInlineElement.cs b/src/Symptum.UI/Markdown/TextElements/TextInlineElement.cs index df58856..2c509ed 100644 --- a/src/Symptum.UI/Markdown/TextElements/TextInlineElement.cs +++ b/src/Symptum.UI/Markdown/TextElements/TextInlineElement.cs @@ -6,10 +6,7 @@ public class TextInlineElement : IAddChild { private SInline inline; - public STextElement TextElement - { - get => inline; - } + public STextElement TextElement => inline; public TextInlineElement(string text) { diff --git a/src/Symptum.UI/Markdown/TextElements/ThematicBreakElement.cs b/src/Symptum.UI/Markdown/TextElements/ThematicBreakElement.cs index 12193f9..b1615b3 100644 --- a/src/Symptum.UI/Markdown/TextElements/ThematicBreakElement.cs +++ b/src/Symptum.UI/Markdown/TextElements/ThematicBreakElement.cs @@ -7,10 +7,7 @@ public class ThematicBreakElement : IAddChild private ThematicBreakBlock _thematicBreakBlock; private SContainer _container = new(); - public STextElement TextElement - { - get => _container; - } + public STextElement TextElement => _container; public ThematicBreakElement(ThematicBreakBlock thematicBreakBlock, MarkdownConfiguration config) { diff --git a/src/Symptum.UI/Symptum.UI.csproj b/src/Symptum.UI/Symptum.UI.csproj index 0a19b69..253bdd2 100644 --- a/src/Symptum.UI/Symptum.UI.csproj +++ b/src/Symptum.UI/Symptum.UI.csproj @@ -43,6 +43,7 @@ + diff --git a/src/Symptum.UI/Themes/Styles.xaml b/src/Symptum.UI/Themes/Styles.xaml index 6e07cec..fcb3772 100644 --- a/src/Symptum.UI/Themes/Styles.xaml +++ b/src/Symptum.UI/Themes/Styles.xaml @@ -95,4 +95,9 @@ + + + From cb2b7706a546f121496141a7f4e20c3ba2288fa8 Mon Sep 17 00:00:00 2001 From: Shankar Date: Sun, 8 Dec 2024 23:13:00 +0530 Subject: [PATCH 5/5] v0.0.4 --- .github/workflows/web-editor-deploy.yml | 4 ++-- global.json | 2 +- src/Symptum.Common/Helpers/PackageHelper.cs | 1 + src/Symptum.Common/Symptum.Common.csproj | 5 ++++- src/Symptum.Core/Serialization/ResourceConverter.cs | 2 +- .../Controls/MarkdownEditorInsertLinkDialog.xaml | 2 +- .../EditorPages/MarkdownEditorPage.xaml.cs | 8 ++++---- src/Symptum.Editor/MainPage.xaml.cs | 2 -- src/Symptum.Editor/Package.appxmanifest | 2 +- src/Symptum.Editor/Symptum.Editor.csproj | 4 ++-- src/Symptum.UI/Markdown/TextElements/ImageElement.cs | 1 - src/Symptum.UI/Symptum.UI.csproj | 9 ++++++--- 12 files changed, 23 insertions(+), 19 deletions(-) diff --git a/.github/workflows/web-editor-deploy.yml b/.github/workflows/web-editor-deploy.yml index 74019c9..19f3020 100644 --- a/.github/workflows/web-editor-deploy.yml +++ b/.github/workflows/web-editor-deploy.yml @@ -24,11 +24,11 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v3 with: - dotnet-version: 8.0.x + dotnet-version: 9.0.x - name: Restore Dependencies run: dotnet restore ./main/src/Symptum.Editor/Symptum.Editor.csproj - name: Publish - run: dotnet publish ./main/src/Symptum.Editor/Symptum.Editor.csproj "-p:WasmShellWebAppBasePath=/editor/" --no-restore -f net8.0-browserwasm -c Release -o out + run: dotnet publish ./main/src/Symptum.Editor/Symptum.Editor.csproj "-p:WasmShellWebAppBasePath=/editor/" --no-restore -f net9.0-browserwasm -c Release -o out - name: Create Editor Folder run: mkdir editor - name: Copy Files to Editor Folder diff --git a/global.json b/global.json index a4a3ec7..0919728 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { // To update the version of Uno please update the version of the Uno.Sdk here. See https://aka.platform.uno/upgrade-uno-packages for more information. "msbuild-sdks": { - "Uno.Sdk": "5.6.0-dev.144" + "Uno.Sdk": "5.6.0-dev.264" } } diff --git a/src/Symptum.Common/Helpers/PackageHelper.cs b/src/Symptum.Common/Helpers/PackageHelper.cs index 156bdfa..e2c34b8 100644 --- a/src/Symptum.Common/Helpers/PackageHelper.cs +++ b/src/Symptum.Common/Helpers/PackageHelper.cs @@ -1,6 +1,7 @@ using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO.Compression; +using System.Runtime.InteropServices.WindowsRuntime; using System.Text.Json; using CsvHelper; using Symptum.Core.Management.Deployment; diff --git a/src/Symptum.Common/Symptum.Common.csproj b/src/Symptum.Common/Symptum.Common.csproj index 15e542c..7f06e79 100644 --- a/src/Symptum.Common/Symptum.Common.csproj +++ b/src/Symptum.Common/Symptum.Common.csproj @@ -1,6 +1,6 @@ - net9.0;net9.0-ios;net9.0-maccatalyst;net9.0-android;net9.0-windows10.0.22621;;net9.0-desktop + net9.0;net9.0-ios;net9.0-maccatalyst;net9.0-android;net9.0-windows10.0.22621;net9.0-browserwasm;net9.0-desktop true Library @@ -8,6 +8,9 @@ enable enable + + false + + net9.0-browserwasm; Exe @@ -17,7 +17,7 @@ com.symptum.editor.dev - 0.0.3 + 0.0.4 1 apk diff --git a/src/Symptum.UI/Markdown/TextElements/ImageElement.cs b/src/Symptum.UI/Markdown/TextElements/ImageElement.cs index 8b96e92..e6b7a0e 100644 --- a/src/Symptum.UI/Markdown/TextElements/ImageElement.cs +++ b/src/Symptum.UI/Markdown/TextElements/ImageElement.cs @@ -95,7 +95,6 @@ private async void LoadImage(object sender, RoutedEventArgs e) string? contentType = response.Content.Headers.ContentType.MediaType; if (contentType == "image/svg+xml") { - string? svgString = await response.Content.ReadAsStringAsync(); ImageSource resImage = await _svgRenderer.SvgToImageSource(svgString); if (resImage != null) diff --git a/src/Symptum.UI/Symptum.UI.csproj b/src/Symptum.UI/Symptum.UI.csproj index 253bdd2..1768fc2 100644 --- a/src/Symptum.UI/Symptum.UI.csproj +++ b/src/Symptum.UI/Symptum.UI.csproj @@ -1,7 +1,7 @@ - net9.0;net9.0-ios;net9.0-maccatalyst;net9.0-android;net9.0-windows10.0.22621;;net9.0-desktop + net9.0;net9.0-ios;net9.0-maccatalyst;net9.0-android;net9.0-windows10.0.22621;net9.0-browserwasm;net9.0-desktop true Library @@ -10,15 +10,18 @@ enable enable + + false + - + Svg; - +