From 421dd69069b7d6548e858adbc607e9455983e728 Mon Sep 17 00:00:00 2001 From: William Bradley Date: Sat, 17 Feb 2018 15:22:58 +1300 Subject: [PATCH] -Built most of the Formatter for RichEditBox. -Removed Write abstract requirement on CodeColorizerBase. -Modified Test/Sample app with navigation and RichEditBox sample. --- ColorCode.Core/CodeColorizerBase.cs | 8 - ColorCode.HTML/HtmlClassFormatter.cs | 2 +- ColorCode.HTML/HtmlFormatter.cs | 2 +- ColorCode.UWP/Common/ExtensionMethods.cs | 11 +- ColorCode.UWP/Common/VisualHelpers.cs | 74 +++++++ ColorCode.UWP/RichEditBoxFormatter.cs | 196 ++++++++++++++++++ ColorCode.UWP/RichTextBlockFormatter.cs | 4 +- .../ColorCode.UWPTests.csproj | 14 ++ Tests/ColorCode.UWPTests/MainPage.xaml | 32 ++- Tests/ColorCode.UWPTests/MainPage.xaml.cs | 84 ++------ Tests/ColorCode.UWPTests/RichEditSample.xaml | 17 ++ .../ColorCode.UWPTests/RichEditSample.xaml.cs | 32 +++ Tests/ColorCode.UWPTests/RichTextSample.xaml | 28 +++ .../ColorCode.UWPTests/RichTextSample.xaml.cs | 96 +++++++++ 14 files changed, 504 insertions(+), 96 deletions(-) create mode 100644 ColorCode.UWP/Common/VisualHelpers.cs create mode 100644 ColorCode.UWP/RichEditBoxFormatter.cs create mode 100644 Tests/ColorCode.UWPTests/RichEditSample.xaml create mode 100644 Tests/ColorCode.UWPTests/RichEditSample.xaml.cs create mode 100644 Tests/ColorCode.UWPTests/RichTextSample.xaml create mode 100644 Tests/ColorCode.UWPTests/RichTextSample.xaml.cs diff --git a/ColorCode.Core/CodeColorizerBase.cs b/ColorCode.Core/CodeColorizerBase.cs index 2618b58..b155600 100644 --- a/ColorCode.Core/CodeColorizerBase.cs +++ b/ColorCode.Core/CodeColorizerBase.cs @@ -1,7 +1,6 @@ using ColorCode.Compilation; using ColorCode.Parsing; using ColorCode.Styling; -using System.Collections.Generic; namespace ColorCode { @@ -20,13 +19,6 @@ public CodeColorizerBase(StyleDictionary Styles, ILanguageParser languageParser) this.Styles = Styles ?? StyleDictionary.DefaultLight; } - /// - /// Writes the parsed source code to the ouput using the specified style sheet. - /// - /// The parsed source code to format and write to the output. - /// The captured scopes for the parsed source code. - protected abstract void Write(string parsedSourceCode, IList scopes); - /// /// The language parser that the instance will use for its lifetime. /// diff --git a/ColorCode.HTML/HtmlClassFormatter.cs b/ColorCode.HTML/HtmlClassFormatter.cs index ac1918d..5b4853c 100644 --- a/ColorCode.HTML/HtmlClassFormatter.cs +++ b/ColorCode.HTML/HtmlClassFormatter.cs @@ -85,7 +85,7 @@ public string GetCSSString() return str.ToString(); } - protected override void Write(string parsedSourceCode, IList scopes) + protected void Write(string parsedSourceCode, IList scopes) { var styleInsertions = new List(); diff --git a/ColorCode.HTML/HtmlFormatter.cs b/ColorCode.HTML/HtmlFormatter.cs index 0970cd0..1f5561f 100644 --- a/ColorCode.HTML/HtmlFormatter.cs +++ b/ColorCode.HTML/HtmlFormatter.cs @@ -52,7 +52,7 @@ public string GetHtmlString(string sourceCode, ILanguage language) return buffer.ToString(); } - protected override void Write(string parsedSourceCode, IList scopes) + protected void Write(string parsedSourceCode, IList scopes) { var styleInsertions = new List(); diff --git a/ColorCode.UWP/Common/ExtensionMethods.cs b/ColorCode.UWP/Common/ExtensionMethods.cs index 0b0eed9..acea1e6 100644 --- a/ColorCode.UWP/Common/ExtensionMethods.cs +++ b/ColorCode.UWP/Common/ExtensionMethods.cs @@ -1,4 +1,5 @@ using System; +using Windows.UI; using Windows.UI.Xaml.Media; namespace ColorCode.UWP.Common @@ -6,6 +7,13 @@ namespace ColorCode.UWP.Common public static class ExtensionMethods { public static SolidColorBrush GetSolidColorBrush(this string hex) + { + var color = GetColor(hex); + SolidColorBrush myBrush = new SolidColorBrush(color); + return myBrush; + } + + public static Color GetColor(this string hex) { hex = hex.Replace("#", string.Empty); @@ -23,8 +31,7 @@ public static SolidColorBrush GetSolidColorBrush(this string hex) 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; + return Color.FromArgb(a, r, g, b); } } } \ No newline at end of file diff --git a/ColorCode.UWP/Common/VisualHelpers.cs b/ColorCode.UWP/Common/VisualHelpers.cs new file mode 100644 index 0000000..643cdf9 --- /dev/null +++ b/ColorCode.UWP/Common/VisualHelpers.cs @@ -0,0 +1,74 @@ +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Media; + +namespace ColorCode.Common +{ + public static class VisualHelpers + { + /// + /// Commandbars causing crashes at the mo + /// + /// + /// + /// + /// + /// + public static T FirstChildofType(DependencyObject parent, bool deepscan = false, bool includeCommandBars = false) + { + if (parent == null) return (T)(object)null; + if (deepscan) + { + if (parent is ContentPresenter presenter) + { + var _Child = presenter.Content as DependencyObject; + if (_Child is T) + { + return (T)(object)_Child; + } + if (!(_Child is CommandBar) || includeCommandBars) + { + var next = (FirstChildofType(_Child, deepscan)); + if (next is T) + { + return ((T)(object)next); + } + } + } + else if (parent is ContentControl control) + { + var _Child = control.Content as DependencyObject; + if (_Child is T) + { + return (T)(object)_Child; + } + if (!(_Child is CommandBar) || includeCommandBars) + { + var next = (FirstChildofType(_Child, deepscan)); + if (next is T) + { + return ((T)(object)next); + } + } + } + } + for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++) + { + var _Child = VisualTreeHelper.GetChild(parent, i); + if (_Child is T) + { + return ((T)(object)_Child); + } + if (!(_Child is CommandBar) || includeCommandBars) + { + var next = (FirstChildofType(_Child, deepscan)); + if (next is T) + { + return ((T)(object)next); + } + } + } + return (T)(object)null; + } + } +} \ No newline at end of file diff --git a/ColorCode.UWP/RichEditBoxFormatter.cs b/ColorCode.UWP/RichEditBoxFormatter.cs new file mode 100644 index 0000000..437d57b --- /dev/null +++ b/ColorCode.UWP/RichEditBoxFormatter.cs @@ -0,0 +1,196 @@ +using System.Collections.Generic; +using ColorCode.Parsing; +using Windows.UI.Xaml.Controls; +using ColorCode.Styling; +using Windows.UI.Text; +using ColorCode.UWP.Common; +using ColorCode.Common; +using Windows.UI.Xaml; +using System.Text.RegularExpressions; + +namespace ColorCode +{ + /// + /// Creates a , for rendering Syntax Highlighted code to a RichTextBlock. + /// + public class RichEditBoxFormatter : 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 RichEditBoxFormatter(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 RichEditBoxFormatter(StyleDictionary Style = null, ILanguageParser languageParser = null) : base(Style, languageParser) + { + } + + private RichEditBox RichEdit { get; set; } + + public ILanguage Language + { + get => _Language; + set + { + _Language = value; + UpdateText(); + } + } + + private ILanguage _Language; + + /// + /// 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 AttachRichEditBox(RichEditBox RichEdit, ILanguage Language) + { + this.RichEdit = RichEdit; + RichEdit.TextChanging += RichEdit_TextChanging; + this.Language = Language; + } + + private void RichEdit_TextChanging(RichEditBox sender, RichEditBoxTextChangingEventArgs args) + { + if (args.IsContentChanging) + { + UpdateText(); + } + } + + public void UpdateText() + { + if (RichEdit != null) + { + // Attempt to get Scrollviewer offsets, to preserve location. + var scroll = VisualHelpers.FirstChildofType(RichEdit); + var vertOffset = scroll?.VerticalOffset; + var horOffset = scroll?.HorizontalOffset; + + var selection = RichEdit.Document.Selection; + var selectionStart = selection.StartPosition; + var selectionEnd = selection.EndPosition; + + RichEdit.Document.GetText(TextGetOptions.UseCrlf, out string raw); + RichEdit.Document.Undo(); + + RichEdit.Document.BeginUndoGroup(); + RichEdit.Document.SetText(TextSetOptions.None, raw); + + var newSelection = RichEdit.Document.Selection; + newSelection.StartPosition = selectionStart; + newSelection.EndPosition = selectionEnd; + + Index = 0; + source = raw; + + languageParser.Parse(raw, Language, (range, captures) => Style(range, captures)); + RichEdit.Document.ApplyDisplayUpdates(); + RichEdit.Document.EndUndoGroup(); + + if (scroll != null) + { + scroll.ChangeView(horOffset, vertOffset, null, true); + } + } + } + + private static int Index = 0; + private static string source = string.Empty; + + protected void Style(string range, IList scopes) + { + var scopeRange = source.Substring(Index); + + var start = Index; + + try + { + var previous = source.Remove(Index); + var crlfCorrection = CountOccurences(previous, "\r\n"); + start -= crlfCorrection; + } + catch + { + } + + var subIndex = scopeRange.IndexOf(range); + if (subIndex != -1) + { + start += subIndex; + } + + foreach (var scope in scopes) + { + StyleFromScope(start, scope); + } + + Index += range.Length; + } + + private int CountOccurences(string str, string match) + { + return Regex.Matches(str, match).Count; + } + + private void StyleFromScope(int Start, Scope Scope) + { + Start += Scope.Index; + var Range = RichEdit.Document.GetRange(Start, Start + Scope.Length); + + 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)) + { + Range.CharacterFormat.ForegroundColor = foreground.GetColor(); + } + + if (!string.IsNullOrWhiteSpace(background)) + { + Range.CharacterFormat.BackgroundColor = background.GetColor(); + } + + if (italic) + Range.CharacterFormat.Italic = FormatEffect.On; + + if (bold) + Range.CharacterFormat.Bold = FormatEffect.On; + + foreach (var subScope in Scope.Children) + { + StyleFromScope(Start, subScope); + } + } + + private void Reset(ITextRange Range) + { + var defaults = RichEdit.Document.GetDefaultCharacterFormat(); + Range.CharacterFormat.Italic = FormatEffect.Off; + Range.CharacterFormat.Bold = FormatEffect.Off; + Range.CharacterFormat.BackgroundColor = defaults.BackgroundColor; + Range.CharacterFormat.ForegroundColor = defaults.ForegroundColor; + } + } +} \ No newline at end of file diff --git a/ColorCode.UWP/RichTextBlockFormatter.cs b/ColorCode.UWP/RichTextBlockFormatter.cs index 16753b9..0de50c4 100644 --- a/ColorCode.UWP/RichTextBlockFormatter.cs +++ b/ColorCode.UWP/RichTextBlockFormatter.cs @@ -54,12 +54,12 @@ public void FormatRichTextBlock(string sourceCode, ILanguage Language, RichTextB public void FormatInlines(string sourceCode, ILanguage Language, InlineCollection InlineCollection) { this.InlineCollection = InlineCollection; - languageParser.Parse(sourceCode, Language, (parsedSourceCode, captures) => Write(parsedSourceCode, captures)); + languageParser.Parse(sourceCode, Language, (parsedSourceCode, captures) => Insert(parsedSourceCode, captures)); } private InlineCollection InlineCollection { get; set; } - protected override void Write(string parsedSourceCode, IList scopes) + protected void Insert(string parsedSourceCode, IList scopes) { var styleInsertions = new List(); diff --git a/Tests/ColorCode.UWPTests/ColorCode.UWPTests.csproj b/Tests/ColorCode.UWPTests/ColorCode.UWPTests.csproj index 14c131f..17caf05 100644 --- a/Tests/ColorCode.UWPTests/ColorCode.UWPTests.csproj +++ b/Tests/ColorCode.UWPTests/ColorCode.UWPTests.csproj @@ -97,6 +97,12 @@ MainPage.xaml + + RichEditSample.xaml + + + RichTextSample.xaml + @@ -123,6 +129,14 @@ MSBuild:Compile Designer + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + diff --git a/Tests/ColorCode.UWPTests/MainPage.xaml b/Tests/ColorCode.UWPTests/MainPage.xaml index 7181342..a7fd21c 100644 --- a/Tests/ColorCode.UWPTests/MainPage.xaml +++ b/Tests/ColorCode.UWPTests/MainPage.xaml @@ -7,22 +7,20 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> - - - - - - - -