From f68cf7d2172e22a02c72f7eef87a904269e216d9 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Mon, 6 Feb 2023 11:53:57 +0100 Subject: [PATCH 1/4] Update to preview 5 --- Directory.Build.props | 2 +- src/AvaloniaEdit.Demo/App.xaml | 5 +- src/AvaloniaEdit/AvaloniaEditCommands.cs | 16 ++--- src/AvaloniaEdit/Editing/AbstractMargin.cs | 1 + src/AvaloniaEdit/Editing/DottedLineMargin.cs | 4 +- src/AvaloniaEdit/Editing/TextArea.cs | 16 ++--- .../Rendering/FormattedTextElement.cs | 2 +- src/AvaloniaEdit/Rendering/InlineObjectRun.cs | 4 +- .../Rendering/PointerHoverLogic.cs | 4 +- .../Rendering/SimpleTextSource.cs | 2 +- .../SingleCharacterElementGenerator.cs | 2 +- src/AvaloniaEdit/Rendering/TextView.cs | 6 +- .../Rendering/VisualLineElement.cs | 4 +- .../Rendering/VisualLineLinkText.cs | 7 ++- src/AvaloniaEdit/Rendering/VisualLineText.cs | 13 ++-- .../Rendering/VisualLineTextSource.cs | 8 +-- src/AvaloniaEdit/RoutedCommand.cs | 29 +++++---- src/AvaloniaEdit/Search/SearchPanel.cs | 1 + src/AvaloniaEdit/Utils/ExtensionMethods.cs | 8 ++- .../Utils/WeakEventManagerBase.cs | 17 +++++- .../AvaloniaMocks/MockFontManagerImpl.cs | 2 +- .../AvaloniaMocks/MockGlyphTypeface.cs | 59 +++++++++++++------ .../MockPlatformRenderInterface.cs | 16 +++++ .../AvaloniaMocks/TestServices.cs | 18 ------ 24 files changed, 143 insertions(+), 103 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index fe803ec9..61e4088f 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -2,7 +2,7 @@ latest true - 11.0.0-preview2 + 11.0.0-preview5 1.0.50 13.0.1 beta diff --git a/src/AvaloniaEdit.Demo/App.xaml b/src/AvaloniaEdit.Demo/App.xaml index 994889b8..e1296d84 100644 --- a/src/AvaloniaEdit.Demo/App.xaml +++ b/src/AvaloniaEdit.Demo/App.xaml @@ -1,9 +1,10 @@ + x:Class="AvaloniaEdit.Demo.App" + RequestedThemeVariant="Default"> - + diff --git a/src/AvaloniaEdit/AvaloniaEditCommands.cs b/src/AvaloniaEdit/AvaloniaEditCommands.cs index b7f0a1c4..dd87f17b 100644 --- a/src/AvaloniaEdit/AvaloniaEditCommands.cs +++ b/src/AvaloniaEdit/AvaloniaEditCommands.cs @@ -17,6 +17,7 @@ // DEALINGS IN THE SOFTWARE. using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; using Avalonia; using Avalonia.Input; using Avalonia.Platform; @@ -112,17 +113,10 @@ public static class ApplicationCommands public static RoutedCommand Redo { get; } = new RoutedCommand(nameof(Redo), new KeyGesture(Key.Y, PlatformCommandKey)); public static RoutedCommand Find { get; } = new RoutedCommand(nameof(Find), new KeyGesture(Key.F, PlatformCommandKey)); public static RoutedCommand Replace { get; } = new RoutedCommand(nameof(Replace), GetReplaceKeyGesture()); - - private static OperatingSystemType GetOperatingSystemType() - { - return AvaloniaLocator.Current.GetService().GetRuntimeInfo().OperatingSystem; - } private static KeyModifiers GetPlatformCommandKey() - { - var os = GetOperatingSystemType(); - - if (os == OperatingSystemType.OSX) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { return KeyModifiers.Meta; } @@ -132,9 +126,7 @@ private static KeyModifiers GetPlatformCommandKey() private static KeyGesture GetReplaceKeyGesture() { - var os = GetOperatingSystemType(); - - if (os == OperatingSystemType.OSX) + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { return new KeyGesture(Key.F, KeyModifiers.Meta | KeyModifiers.Alt); } diff --git a/src/AvaloniaEdit/Editing/AbstractMargin.cs b/src/AvaloniaEdit/Editing/AbstractMargin.cs index c97a2695..79aa85d1 100644 --- a/src/AvaloniaEdit/Editing/AbstractMargin.cs +++ b/src/AvaloniaEdit/Editing/AbstractMargin.cs @@ -22,6 +22,7 @@ using AvaloniaEdit.Document; using AvaloniaEdit.Rendering; using Avalonia.Controls; +using AvaloniaEdit.Utils; namespace AvaloniaEdit.Editing { diff --git a/src/AvaloniaEdit/Editing/DottedLineMargin.cs b/src/AvaloniaEdit/Editing/DottedLineMargin.cs index b55d7579..f8ce173b 100644 --- a/src/AvaloniaEdit/Editing/DottedLineMargin.cs +++ b/src/AvaloniaEdit/Editing/DottedLineMargin.cs @@ -35,7 +35,7 @@ public static class DottedLineMargin /// /// Creates a vertical dotted line to separate the line numbers from the text view. /// - public static IControl Create() + public static Control Create() { var line = new Line { @@ -55,7 +55,7 @@ public static IControl Create() /// /// Gets whether the specified UIElement is the result of a DottedLineMargin.Create call. /// - public static bool IsDottedLineMargin(IControl element) + public static bool IsDottedLineMargin(Control element) { var l = element as Line; return l != null && l.Tag == Tag; diff --git a/src/AvaloniaEdit/Editing/TextArea.cs b/src/AvaloniaEdit/Editing/TextArea.cs index 3e78262f..59416217 100644 --- a/src/AvaloniaEdit/Editing/TextArea.cs +++ b/src/AvaloniaEdit/Editing/TextArea.cs @@ -128,13 +128,13 @@ protected override void OnApplyTemplate(TemplateAppliedEventArgs e) } } - internal void AddChild(IVisual visual) + internal void AddChild(Visual visual) { VisualChildren.Add(visual); InvalidateArrange(); } - internal void RemoveChild(IVisual visual) + internal void RemoveChild(Visual visual) { VisualChildren.Remove(visual); } @@ -686,14 +686,14 @@ private void CaretPositionChanged(object sender, EventArgs e) }); } - public static readonly DirectProperty> LeftMarginsProperty - = AvaloniaProperty.RegisterDirect>(nameof(LeftMargins), + public static readonly DirectProperty> LeftMarginsProperty + = AvaloniaProperty.RegisterDirect>(nameof(LeftMargins), c => c.LeftMargins); /// /// Gets the collection of margins displayed to the left of the text view. /// - public ObservableCollection LeftMargins { get; } = new ObservableCollection(); + public ObservableCollection LeftMargins { get; } = new ObservableCollection(); private void LeftMargins_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { @@ -1122,10 +1122,10 @@ bool ILogicalScrollable.CanVerticallyScroll } } - public bool BringIntoView(IControl target, Rect targetRect) => + public bool BringIntoView(Control target, Rect targetRect) => _logicalScrollable?.BringIntoView(target, targetRect) ?? default(bool); - IControl ILogicalScrollable.GetControlInDirection(NavigationDirection direction, IControl from) + Control ILogicalScrollable.GetControlInDirection(NavigationDirection direction, Control from) => _logicalScrollable?.GetControlInDirection(direction, from); public void RaiseScrollInvalidated(EventArgs e) @@ -1168,7 +1168,7 @@ public Rect CursorRectangle } } - public IVisual TextViewVisual => _textArea; + public Visual TextViewVisual => _textArea; public bool SupportsPreedit => false; diff --git a/src/AvaloniaEdit/Rendering/FormattedTextElement.cs b/src/AvaloniaEdit/Rendering/FormattedTextElement.cs index 91f81aca..4ba63e29 100644 --- a/src/AvaloniaEdit/Rendering/FormattedTextElement.cs +++ b/src/AvaloniaEdit/Rendering/FormattedTextElement.cs @@ -118,7 +118,7 @@ public FormattedTextRun(FormattedTextElement element, TextRunProperties properti /// public FormattedTextElement Element { get; } - public override ReadOnlySlice Text { get; } + public override ReadOnlyMemory Text { get; } /// public override TextRunProperties Properties { get; } diff --git a/src/AvaloniaEdit/Rendering/InlineObjectRun.cs b/src/AvaloniaEdit/Rendering/InlineObjectRun.cs index 79b3ab6d..f4c072cf 100644 --- a/src/AvaloniaEdit/Rendering/InlineObjectRun.cs +++ b/src/AvaloniaEdit/Rendering/InlineObjectRun.cs @@ -75,7 +75,7 @@ public InlineObjectRun(int length, TextRunProperties? properties, Control elemen if (length <= 0) throw new ArgumentOutOfRangeException(nameof(length), length, "Value must be positive"); - TextSourceLength = length; + Length = length; Properties = properties ?? throw new ArgumentNullException(nameof(properties)); Element = element ?? throw new ArgumentNullException(nameof(element)); @@ -95,7 +95,7 @@ public InlineObjectRun(int length, TextRunProperties? properties, Control elemen public override TextRunProperties? Properties { get; } - public override int TextSourceLength { get; } + public override int Length { get; } public override double Baseline { diff --git a/src/AvaloniaEdit/Rendering/PointerHoverLogic.cs b/src/AvaloniaEdit/Rendering/PointerHoverLogic.cs index 89a143c2..5f10799a 100644 --- a/src/AvaloniaEdit/Rendering/PointerHoverLogic.cs +++ b/src/AvaloniaEdit/Rendering/PointerHoverLogic.cs @@ -33,7 +33,7 @@ public class PointerHoverLogic : IDisposable private const double PointerHoverHeight = 2; private static readonly TimeSpan PointerHoverTime = TimeSpan.FromMilliseconds(400); - private readonly IControl _target; + private readonly Control _target; private DispatcherTimer _timer; private Point _hoverStartPoint; @@ -43,7 +43,7 @@ public class PointerHoverLogic : IDisposable /// /// Creates a new instance and attaches itself to the UIElement. /// - public PointerHoverLogic(IControl target) + public PointerHoverLogic(Control target) { _target = target ?? throw new ArgumentNullException(nameof(target)); _target.PointerExited += OnPointerLeave; diff --git a/src/AvaloniaEdit/Rendering/SimpleTextSource.cs b/src/AvaloniaEdit/Rendering/SimpleTextSource.cs index 9c171df7..0a1930d7 100644 --- a/src/AvaloniaEdit/Rendering/SimpleTextSource.cs +++ b/src/AvaloniaEdit/Rendering/SimpleTextSource.cs @@ -37,7 +37,7 @@ public TextRun GetTextRun(int textSourceCharacterIndex) { if (textSourceCharacterIndex < _text.Length) return new TextCharacters( - new ReadOnlySlice(_text.AsMemory(), textSourceCharacterIndex, + _text.AsMemory().Slice(textSourceCharacterIndex, _text.Length - textSourceCharacterIndex), _properties); return new TextEndOfParagraph(1); diff --git a/src/AvaloniaEdit/Rendering/SingleCharacterElementGenerator.cs b/src/AvaloniaEdit/Rendering/SingleCharacterElementGenerator.cs index 19bb749c..4274bb33 100644 --- a/src/AvaloniaEdit/Rendering/SingleCharacterElementGenerator.cs +++ b/src/AvaloniaEdit/Rendering/SingleCharacterElementGenerator.cs @@ -167,7 +167,7 @@ public override TextRun CreateTextRun(int startVisualColumn, ITextRunConstructio if (startVisualColumn == VisualColumn) return new TabGlyphRun(this, TextRunProperties); else if (startVisualColumn == VisualColumn + 1) - return new TextCharacters(new ReadOnlySlice("\t".AsMemory(), VisualColumn + 1, 1), TextRunProperties); + return new TextCharacters("\t".AsMemory(), TextRunProperties); else throw new ArgumentOutOfRangeException(nameof(startVisualColumn)); } diff --git a/src/AvaloniaEdit/Rendering/TextView.cs b/src/AvaloniaEdit/Rendering/TextView.cs index c3361191..fd2e55e1 100644 --- a/src/AvaloniaEdit/Rendering/TextView.cs +++ b/src/AvaloniaEdit/Rendering/TextView.cs @@ -1206,7 +1206,7 @@ protected override Size ArrangeOverride(Size finalSize) Debug.WriteLine(distance); } - offset += span.TextSourceLength; + offset += span.Length; } pos = new Point(pos.X, pos.Y + textLine.Height); } @@ -1950,7 +1950,7 @@ private static ImmutablePen CreateFrozenPen(IBrush brush) return pen; } - bool ILogicalScrollable.BringIntoView(IControl target, Rect rectangle) + bool ILogicalScrollable.BringIntoView(Control target, Rect rectangle) { if (rectangle.IsEmpty || target == null || target == this || !this.IsVisualAncestorOf(target)) { @@ -1967,7 +1967,7 @@ bool ILogicalScrollable.BringIntoView(IControl target, Rect rectangle) return true; } - IControl ILogicalScrollable.GetControlInDirection(NavigationDirection direction, IControl from) + Control ILogicalScrollable.GetControlInDirection(NavigationDirection direction, Control from) { return null; } diff --git a/src/AvaloniaEdit/Rendering/VisualLineElement.cs b/src/AvaloniaEdit/Rendering/VisualLineElement.cs index 1b5babc4..0972fa3e 100644 --- a/src/AvaloniaEdit/Rendering/VisualLineElement.cs +++ b/src/AvaloniaEdit/Rendering/VisualLineElement.cs @@ -106,9 +106,9 @@ internal void SetTextRunProperties(VisualLineElementTextRunProperties p) /// Retrieves the text span immediately before the visual column. /// /// This method is used for word-wrapping in bidirectional text. - public virtual ReadOnlySlice GetPrecedingText(int visualColumnLimit, ITextRunConstructionContext context) + public virtual ReadOnlyMemory GetPrecedingText(int visualColumnLimit, ITextRunConstructionContext context) { - return ReadOnlySlice.Empty; + return ReadOnlyMemory.Empty; } /// diff --git a/src/AvaloniaEdit/Rendering/VisualLineLinkText.cs b/src/AvaloniaEdit/Rendering/VisualLineLinkText.cs index 88f9f68c..dbea7c21 100644 --- a/src/AvaloniaEdit/Rendering/VisualLineLinkText.cs +++ b/src/AvaloniaEdit/Rendering/VisualLineLinkText.cs @@ -110,7 +110,12 @@ protected internal override void OnPointerPressed(PointerPressedEventArgs e) if (!e.Handled && LinkIsClickable(e.KeyModifiers)) { var eventArgs = new OpenUriRoutedEventArgs(NavigateUri) { RoutedEvent = OpenUriEvent }; - e.Source.RaiseEvent(eventArgs); + + if(e.Source is Interactive interactive) + { + interactive.RaiseEvent(eventArgs); + } + e.Handled = true; } } diff --git a/src/AvaloniaEdit/Rendering/VisualLineText.cs b/src/AvaloniaEdit/Rendering/VisualLineText.cs index 8542ec43..cf79e48f 100644 --- a/src/AvaloniaEdit/Rendering/VisualLineText.cs +++ b/src/AvaloniaEdit/Rendering/VisualLineText.cs @@ -68,14 +68,9 @@ public override TextRun CreateTextRun(int startVisualColumn, ITextRunConstructio offset, DocumentLength - relativeOffset); - var bufferOffset = RelativeTextOffset; + //var bufferOffset = RelativeTextOffset; - if (bufferOffset + text.Count > text.Text.Length) - { - bufferOffset = 0; - } - - var textSlice = new ReadOnlySlice(text.Text.AsMemory(), text.Offset, text.Count, bufferOffset); + var textSlice = text.Text.AsMemory().Slice(text.Offset, text.Count); return new TextCharacters(textSlice, TextRunProperties); } @@ -88,7 +83,7 @@ public override bool IsWhitespace(int visualColumn) } /// - public override ReadOnlySlice GetPrecedingText(int visualColumnLimit, ITextRunConstructionContext context) + public override ReadOnlyMemory GetPrecedingText(int visualColumnLimit, ITextRunConstructionContext context) { if (context == null) throw new ArgumentNullException(nameof(context)); @@ -97,7 +92,7 @@ public override ReadOnlySlice GetPrecedingText(int visualColumnLimit, ITex var text = context.GetText(context.VisualLine.FirstDocumentLine.Offset + RelativeTextOffset, relativeOffset); - return new ReadOnlySlice(text.Text.AsMemory(), text.Offset, text.Count); + return text.Text.AsMemory().Slice(text.Offset, text.Count); } /// diff --git a/src/AvaloniaEdit/Rendering/VisualLineTextSource.cs b/src/AvaloniaEdit/Rendering/VisualLineTextSource.cs index 1ba5d125..d61ac619 100644 --- a/src/AvaloniaEdit/Rendering/VisualLineTextSource.cs +++ b/src/AvaloniaEdit/Rendering/VisualLineTextSource.cs @@ -51,9 +51,9 @@ public TextRun GetTextRun(int textSourceCharacterIndex) TextRun run = element.CreateTextRun(textSourceCharacterIndex, this); if (run == null) throw new ArgumentNullException(element.GetType().Name + ".CreateTextRun"); - if (run.TextSourceLength == 0) + if (run.Length == 0) throw new ArgumentException("The returned TextRun must not have length 0.", element.GetType().Name + ".Length"); - if (relativeOffset + run.TextSourceLength > element.VisualLength) + if (relativeOffset + run.Length > element.VisualLength) throw new ArgumentException("The returned TextRun is too long.", element.GetType().Name + ".CreateTextRun"); if (run is InlineObjectRun inlineRun) { inlineRun.VisualLine = VisualLine; @@ -102,7 +102,7 @@ private TextRun CreateTextRunForNewLine() return new FormattedTextRun(textElement, GlobalTextRunProperties); } - public ReadOnlySlice GetPrecedingText(int textSourceCharacterIndexLimit) + public ReadOnlyMemory GetPrecedingText(int textSourceCharacterIndexLimit) { try { foreach (VisualLineElement element in VisualLine.Elements) { @@ -118,7 +118,7 @@ public ReadOnlySlice GetPrecedingText(int textSourceCharacterIndexLimit) } } - return ReadOnlySlice.Empty; + return ReadOnlyMemory.Empty; } catch (Exception ex) { Debug.WriteLine(ex.ToString()); throw; diff --git a/src/AvaloniaEdit/RoutedCommand.cs b/src/AvaloniaEdit/RoutedCommand.cs index 8628aaa9..a570115c 100644 --- a/src/AvaloniaEdit/RoutedCommand.cs +++ b/src/AvaloniaEdit/RoutedCommand.cs @@ -21,22 +21,29 @@ public RoutedCommand(string name, KeyGesture keyGesture = null) static RoutedCommand() { - CanExecuteEvent.AddClassHandler(CanExecuteEventHandler); - ExecutedEvent.AddClassHandler(ExecutedEventHandler); + CanExecuteEvent.AddClassHandler(CanExecuteEventHandler); + ExecutedEvent.AddClassHandler(ExecutedEventHandler); } - private static void CanExecuteEventHandler(IRoutedCommandBindable control, CanExecuteRoutedEventArgs args) + private static void CanExecuteEventHandler(Interactive control, CanExecuteRoutedEventArgs args) { - var binding = control.CommandBindings.Where(c => c != null) - .FirstOrDefault(c => c.Command == args.Command && c.DoCanExecute(control, args)); - args.CanExecute = binding != null; + if (control is IRoutedCommandBindable bindable) + { + var binding = bindable.CommandBindings.Where(c => c != null) + .FirstOrDefault(c => c.Command == args.Command && c.DoCanExecute(control, args)); + args.CanExecute = binding != null; + } } - private static void ExecutedEventHandler(IRoutedCommandBindable control, ExecutedRoutedEventArgs args) + private static void ExecutedEventHandler(Interactive control, ExecutedRoutedEventArgs args) { - // ReSharper disable once UnusedVariable - var binding = control.CommandBindings.Where(c => c != null) - .FirstOrDefault(c => c.Command == args.Command && c.DoExecuted(control, args)); + if (control is IRoutedCommandBindable bindable) + { + // ReSharper disable once UnusedVariable + var binding = bindable.CommandBindings.Where(c => c != null) + .FirstOrDefault(c => c.Command == args.Command && c.DoExecuted(control, args)); + + } } public static RoutedEvent CanExecuteEvent { get; } = RoutedEvent.Register(nameof(CanExecuteEvent), RoutingStrategies.Bubble, typeof(RoutedCommand)); @@ -79,7 +86,7 @@ event EventHandler ICommand.CanExecuteChanged } } - public interface IRoutedCommandBindable : IInteractive + public interface IRoutedCommandBindable { IList CommandBindings { get; } } diff --git a/src/AvaloniaEdit/Search/SearchPanel.cs b/src/AvaloniaEdit/Search/SearchPanel.cs index 9dbe08e9..6eb0b187 100644 --- a/src/AvaloniaEdit/Search/SearchPanel.cs +++ b/src/AvaloniaEdit/Search/SearchPanel.cs @@ -28,6 +28,7 @@ using AvaloniaEdit.Document; using AvaloniaEdit.Editing; using AvaloniaEdit.Rendering; +using AvaloniaEdit.Utils; namespace AvaloniaEdit.Search { diff --git a/src/AvaloniaEdit/Utils/ExtensionMethods.cs b/src/AvaloniaEdit/Utils/ExtensionMethods.cs index fd5b0900..a222ed20 100644 --- a/src/AvaloniaEdit/Utils/ExtensionMethods.cs +++ b/src/AvaloniaEdit/Utils/ExtensionMethods.cs @@ -25,6 +25,7 @@ using Avalonia.Controls.Documents; using Avalonia.Input; using Avalonia.Media; +using Avalonia.Reactive; using Avalonia.VisualTree; namespace AvaloniaEdit.Utils @@ -214,7 +215,7 @@ public static Rect ToAvalonia(this System.Drawing.Rectangle rect) #endregion #region Snap to device pixels - public static Point SnapToDevicePixels(this Point p, IVisual targetVisual) + public static Point SnapToDevicePixels(this Point p, Visual targetVisual) { var root = targetVisual.GetVisualRoot(); @@ -301,5 +302,10 @@ public static T PeekOrDefault(this ImmutableStack stack) { return stack.IsEmpty ? default(T) : stack.Peek(); } + + public static IDisposable Subscribe(this IObservable observable, Action action) + { + return observable.Subscribe(new AnonymousObserver(action)); + } } } diff --git a/src/AvaloniaEdit/Utils/WeakEventManagerBase.cs b/src/AvaloniaEdit/Utils/WeakEventManagerBase.cs index 1cbc7ef8..a8a2252a 100644 --- a/src/AvaloniaEdit/Utils/WeakEventManagerBase.cs +++ b/src/AvaloniaEdit/Utils/WeakEventManagerBase.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Reactive.Disposables; using System.Reflection; using System.Runtime.CompilerServices; using System.Threading; @@ -305,7 +304,7 @@ public IDisposable DeliverActive() { Interlocked.Increment(ref _deliveries); - return Disposable.Create(() => Interlocked.Decrement(ref _deliveries)); + return new Disposable(() => Interlocked.Decrement(ref _deliveries)); } // ReSharper disable once MemberHidesStaticFromOuterClass @@ -339,6 +338,20 @@ public void Purge() } } } + + internal sealed class Disposable : IDisposable + { + private volatile Action _dispose; + public Disposable(Action dispose) + { + _dispose = dispose; + } + public bool IsDisposed => _dispose == null; + public void Dispose() + { + Interlocked.Exchange(ref _dispose, null)?.Invoke(); + } + } } } } \ No newline at end of file diff --git a/test/AvaloniaEdit.Tests/AvaloniaMocks/MockFontManagerImpl.cs b/test/AvaloniaEdit.Tests/AvaloniaMocks/MockFontManagerImpl.cs index be8fdd5c..77965fc5 100644 --- a/test/AvaloniaEdit.Tests/AvaloniaMocks/MockFontManagerImpl.cs +++ b/test/AvaloniaEdit.Tests/AvaloniaMocks/MockFontManagerImpl.cs @@ -37,7 +37,7 @@ public bool TryMatchCharacter(int codepoint, FontStyle fontStyle, FontWeight fon return true; } - public IGlyphTypefaceImpl CreateGlyphTypeface(Typeface typeface) + public IGlyphTypeface CreateGlyphTypeface(Typeface typeface) { return new MockGlyphTypeface(); } diff --git a/test/AvaloniaEdit.Tests/AvaloniaMocks/MockGlyphTypeface.cs b/test/AvaloniaEdit.Tests/AvaloniaMocks/MockGlyphTypeface.cs index 5a416fe5..1ad79fe6 100644 --- a/test/AvaloniaEdit.Tests/AvaloniaMocks/MockGlyphTypeface.cs +++ b/test/AvaloniaEdit.Tests/AvaloniaMocks/MockGlyphTypeface.cs @@ -1,29 +1,26 @@ -using Avalonia.Platform; +using Avalonia.Media; using System; namespace AvaloniaEdit.AvaloniaMocks { - public class MockGlyphTypeface : IGlyphTypefaceImpl + public class MockGlyphTypeface : IGlyphTypeface { - public const int GlyphAdvance = 8; - public const short DefaultFontSize = 10; - public const int GlyphAscent = 2; - public const int GlyphDescent = 10; - - public short DesignEmHeight => DefaultFontSize; - public int Ascent => GlyphAscent; - public int Descent => GlyphDescent; - public int LineGap { get; } - public int UnderlinePosition { get; } - public int UnderlineThickness { get; } - public int StrikethroughPosition { get; } - public int StrikethroughThickness { get; } - public bool IsFixedPitch { get; } + public FontMetrics Metrics => new FontMetrics + { + DesignEmHeight = 10, + Ascent = 2, + Descent = 10, + IsFixedPitch = true + }; + + public int GlyphCount => 1337; + + public FontSimulations FontSimulations => throw new NotImplementedException(); public ushort GetGlyph(uint codepoint) { - return 0; + return (ushort)codepoint; } public ushort[] GetGlyphs(ReadOnlySpan codepoints) @@ -33,7 +30,14 @@ public ushort[] GetGlyphs(ReadOnlySpan codepoints) public int GetGlyphAdvance(ushort glyph) { - return GlyphAdvance; + return 8; + } + + public bool TryGetGlyph(uint codepoint, out ushort glyph) + { + glyph = 8; + + return true; } public int[] GetGlyphAdvances(ReadOnlySpan glyphs) @@ -42,12 +46,29 @@ public int[] GetGlyphAdvances(ReadOnlySpan glyphs) for (var i = 0; i < advances.Length; i++) { - advances[i] = GlyphAdvance; + advances[i] = 8; } return advances; } public void Dispose() { } + + public bool TryGetTable(uint tag, out byte[] table) + { + table = null; + return false; + } + + public bool TryGetGlyphMetrics(ushort glyph, out GlyphMetrics metrics) + { + metrics = new GlyphMetrics + { + Width = 10, + Height = 10 + }; + + return true; + } } } diff --git a/test/AvaloniaEdit.Tests/AvaloniaMocks/MockPlatformRenderInterface.cs b/test/AvaloniaEdit.Tests/AvaloniaMocks/MockPlatformRenderInterface.cs index 0793562c..dc49f194 100644 --- a/test/AvaloniaEdit.Tests/AvaloniaMocks/MockPlatformRenderInterface.cs +++ b/test/AvaloniaEdit.Tests/AvaloniaMocks/MockPlatformRenderInterface.cs @@ -4,6 +4,7 @@ using Avalonia; using Avalonia.Media; using Avalonia.Media.Imaging; +using Avalonia.Media.TextFormatting; using Avalonia.Platform; using Moq; @@ -143,5 +144,20 @@ public IGeometryImpl BuildGlyphRunGeometry(GlyphRun glyphRun) { throw new NotImplementedException(); } + + public IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, IReadOnlyList glyphInfos, Point baselineOrigin) + { + throw new NotImplementedException(); + } + + public IPlatformRenderInterfaceContext CreateBackendContext(IPlatformGraphicsContext graphicsApiContext) + { + throw new NotImplementedException(); + } + + public bool IsSupportedBitmapPixelFormat(PixelFormat format) + { + throw new NotImplementedException(); + } } } \ No newline at end of file diff --git a/test/AvaloniaEdit.Tests/AvaloniaMocks/TestServices.cs b/test/AvaloniaEdit.Tests/AvaloniaMocks/TestServices.cs index 740d2b02..a2916f8d 100644 --- a/test/AvaloniaEdit.Tests/AvaloniaMocks/TestServices.cs +++ b/test/AvaloniaEdit.Tests/AvaloniaMocks/TestServices.cs @@ -1,5 +1,4 @@ using System; -using System.Reactive.Concurrency; using Avalonia; using Avalonia.Input; using Avalonia.Input.Platform; @@ -21,7 +20,6 @@ public class TestServices platform: new AppBuilder().RuntimePlatform, renderInterface: new MockPlatformRenderInterface(), standardCursorFactory: Mock.Of(), - styler: new Styler(), theme: () => CreateDefaultTheme(), threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true), windowingPlatform: new MockWindowingPlatform(), @@ -34,9 +32,6 @@ public class TestServices public static readonly TestServices MockPlatformWrapper = new TestServices( platform: Mock.Of()); - public static readonly TestServices MockStyler = new TestServices( - styler: Mock.Of()); - public static readonly TestServices MockThreadingInterface = new TestServices( threadingInterface: Mock.Of(x => x.CurrentThreadIsLoopThread == true)); @@ -49,9 +44,6 @@ public class TestServices keyboardNavigation: new KeyboardNavigationHandler(), inputManager: new InputManager()); - public static readonly TestServices RealStyler = new TestServices( - styler: new Styler()); - public TestServices( IAssetLoader assetLoader = null, IFocusManager focusManager = null, @@ -63,9 +55,7 @@ public TestServices( IRuntimePlatform platform = null, IPlatformRenderInterface renderInterface = null, IRenderLoop renderLoop = null, - IScheduler scheduler = null, ICursorFactory standardCursorFactory = null, - IStyler styler = null, Func theme = null, IPlatformThreadingInterface threadingInterface = null, IWindowImpl windowImpl = null, @@ -83,9 +73,7 @@ public TestServices( MouseDevice = mouseDevice; Platform = platform; RenderInterface = renderInterface; - Scheduler = scheduler; StandardCursorFactory = standardCursorFactory; - Styler = styler; Theme = theme; ThreadingInterface = threadingInterface; WindowImpl = windowImpl; @@ -104,9 +92,7 @@ public TestServices( public Func MouseDevice { get; } public IRuntimePlatform Platform { get; } public IPlatformRenderInterface RenderInterface { get; } - public IScheduler Scheduler { get; } public ICursorFactory StandardCursorFactory { get; } - public IStyler Styler { get; } public Func Theme { get; } public IPlatformThreadingInterface ThreadingInterface { get; } public IWindowImpl WindowImpl { get; } @@ -127,9 +113,7 @@ public TestServices With( IRuntimePlatform platform = null, IPlatformRenderInterface renderInterface = null, IRenderLoop renderLoop = null, - IScheduler scheduler = null, ICursorFactory standardCursorFactory = null, - IStyler styler = null, Func theme = null, IPlatformThreadingInterface threadingInterface = null, IWindowImpl windowImpl = null, @@ -148,9 +132,7 @@ public TestServices With( mouseDevice: mouseDevice ?? MouseDevice, platform: platform ?? Platform, renderInterface: renderInterface ?? RenderInterface, - scheduler: scheduler ?? Scheduler, standardCursorFactory: standardCursorFactory ?? StandardCursorFactory, - styler: styler ?? Styler, theme: theme ?? Theme, threadingInterface: threadingInterface ?? ThreadingInterface, windowingPlatform: windowingPlatform ?? WindowingPlatform, From 00d004c69f666d8661989cac381f033240ca0d23 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Mon, 6 Feb 2023 13:46:32 +0100 Subject: [PATCH 2/4] Fix tests --- .../Utils/WeakEventManagerBase.cs | 26 +++++++------- .../AvaloniaMocks/MockGlyphRun.cs | 36 +++++++++++++++++++ .../AvaloniaMocks/MockGlyphTypeface.cs | 4 ++- .../MockPlatformRenderInterface.cs | 3 +- .../AvaloniaMocks/MockTextShaperImpl.cs | 9 ++--- .../AvaloniaMocks/UnitTestApplication.cs | 8 ++--- .../Rendering/TextViewTests.cs | 2 +- 7 files changed, 61 insertions(+), 27 deletions(-) create mode 100644 test/AvaloniaEdit.Tests/AvaloniaMocks/MockGlyphRun.cs diff --git a/src/AvaloniaEdit/Utils/WeakEventManagerBase.cs b/src/AvaloniaEdit/Utils/WeakEventManagerBase.cs index a8a2252a..f35e5e7d 100644 --- a/src/AvaloniaEdit/Utils/WeakEventManagerBase.cs +++ b/src/AvaloniaEdit/Utils/WeakEventManagerBase.cs @@ -338,20 +338,20 @@ public void Purge() } } } + } + } - internal sealed class Disposable : IDisposable - { - private volatile Action _dispose; - public Disposable(Action dispose) - { - _dispose = dispose; - } - public bool IsDisposed => _dispose == null; - public void Dispose() - { - Interlocked.Exchange(ref _dispose, null)?.Invoke(); - } - } + internal sealed class Disposable : IDisposable + { + private volatile Action _dispose; + public Disposable(Action dispose) + { + _dispose = dispose; + } + public bool IsDisposed => _dispose == null; + public void Dispose() + { + Interlocked.Exchange(ref _dispose, null)?.Invoke(); } } } \ No newline at end of file diff --git a/test/AvaloniaEdit.Tests/AvaloniaMocks/MockGlyphRun.cs b/test/AvaloniaEdit.Tests/AvaloniaMocks/MockGlyphRun.cs new file mode 100644 index 00000000..f3bb0bad --- /dev/null +++ b/test/AvaloniaEdit.Tests/AvaloniaMocks/MockGlyphRun.cs @@ -0,0 +1,36 @@ +using Avalonia; +using Avalonia.Media.TextFormatting; +using Avalonia.Platform; +using System.Collections.Generic; + +namespace AvaloniaEdit.AvaloniaMocks +{ + internal class MockGlyphRun : IGlyphRunImpl + { + public MockGlyphRun(IReadOnlyList glyphInfos) + { + var width = 0.0; + + for (var i = 0; i < glyphInfos.Count; ++i) + { + width += glyphInfos[i].GlyphAdvance; + } + + Size = new Size(width, 10); + } + + public Size Size { get; } + + public Point BaselineOrigin => new Point(0, 8); + + public void Dispose() + { + + } + + public IReadOnlyList GetIntersections(float lowerBound, float upperBound) + { + return null; + } + } +} diff --git a/test/AvaloniaEdit.Tests/AvaloniaMocks/MockGlyphTypeface.cs b/test/AvaloniaEdit.Tests/AvaloniaMocks/MockGlyphTypeface.cs index 1ad79fe6..436068df 100644 --- a/test/AvaloniaEdit.Tests/AvaloniaMocks/MockGlyphTypeface.cs +++ b/test/AvaloniaEdit.Tests/AvaloniaMocks/MockGlyphTypeface.cs @@ -40,13 +40,15 @@ public bool TryGetGlyph(uint codepoint, out ushort glyph) return true; } + public static int GlyphAdvance => 8; + public int[] GetGlyphAdvances(ReadOnlySpan glyphs) { var advances = new int[glyphs.Length]; for (var i = 0; i < advances.Length; i++) { - advances[i] = 8; + advances[i] = GlyphAdvance; } return advances; diff --git a/test/AvaloniaEdit.Tests/AvaloniaMocks/MockPlatformRenderInterface.cs b/test/AvaloniaEdit.Tests/AvaloniaMocks/MockPlatformRenderInterface.cs index dc49f194..1da9463d 100644 --- a/test/AvaloniaEdit.Tests/AvaloniaMocks/MockPlatformRenderInterface.cs +++ b/test/AvaloniaEdit.Tests/AvaloniaMocks/MockPlatformRenderInterface.cs @@ -6,7 +6,6 @@ using Avalonia.Media.Imaging; using Avalonia.Media.TextFormatting; using Avalonia.Platform; - using Moq; namespace AvaloniaEdit.AvaloniaMocks @@ -147,7 +146,7 @@ public IGeometryImpl BuildGlyphRunGeometry(GlyphRun glyphRun) public IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, IReadOnlyList glyphInfos, Point baselineOrigin) { - throw new NotImplementedException(); + return new MockGlyphRun(glyphInfos); } public IPlatformRenderInterfaceContext CreateBackendContext(IPlatformGraphicsContext graphicsApiContext) diff --git a/test/AvaloniaEdit.Tests/AvaloniaMocks/MockTextShaperImpl.cs b/test/AvaloniaEdit.Tests/AvaloniaMocks/MockTextShaperImpl.cs index 430dbe9b..4c573559 100644 --- a/test/AvaloniaEdit.Tests/AvaloniaMocks/MockTextShaperImpl.cs +++ b/test/AvaloniaEdit.Tests/AvaloniaMocks/MockTextShaperImpl.cs @@ -1,4 +1,5 @@ -using System.Globalization; +using System; +using System.Globalization; using Avalonia.Media; using Avalonia.Media.TextFormatting; using Avalonia.Media.TextFormatting.Unicode; @@ -11,7 +12,7 @@ namespace AvaloniaEdit.AvaloniaMocks; public class MockTextShaperImpl : ITextShaperImpl { - public ShapedBuffer ShapeText(ReadOnlySlice text, TextShaperOptions options) + public ShapedBuffer ShapeText(ReadOnlyMemory text, TextShaperOptions options) { var typeface = options.Typeface; var fontRenderingEmSize = options.FontRenderingEmSize; @@ -21,8 +22,8 @@ public ShapedBuffer ShapeText(ReadOnlySlice text, TextShaperOptions option for (var i = 0; i < shapedBuffer.Length;) { - var glyphCluster = i + text.Start; - var codepoint = Codepoint.ReadAt(text, i, out var count); + var glyphCluster = i; + var codepoint = Codepoint.ReadAt(text.Span, i, out var count); var glyphIndex = typeface.GetGlyph(codepoint); diff --git a/test/AvaloniaEdit.Tests/AvaloniaMocks/UnitTestApplication.cs b/test/AvaloniaEdit.Tests/AvaloniaMocks/UnitTestApplication.cs index 94b49519..a52a3d60 100644 --- a/test/AvaloniaEdit.Tests/AvaloniaMocks/UnitTestApplication.cs +++ b/test/AvaloniaEdit.Tests/AvaloniaMocks/UnitTestApplication.cs @@ -1,15 +1,13 @@ using System; -using System.Reactive.Concurrency; -using System.Reactive.Disposables; using System.Reflection; using Avalonia; -using Avalonia.Controls; using Avalonia.Input; using Avalonia.Input.Platform; using Avalonia.Layout; using Avalonia.Platform; using Avalonia.Styling; using Avalonia.Threading; +using AvaloniaEdit.Utils; namespace AvaloniaEdit.AvaloniaMocks { @@ -30,7 +28,7 @@ public static IDisposable Start(TestServices services = null) AvaloniaLocator.CurrentMutable.BindToSelf(app); var updateServices = Dispatcher.UIThread.GetType().GetMethod("UpdateServices", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); updateServices?.Invoke(Dispatcher.UIThread, null); - return Disposable.Create(() => + return new Disposable(() => { updateServices?.Invoke(Dispatcher.UIThread, null); AvaloniaLocator.CurrentMutable = null; @@ -52,9 +50,7 @@ public override void RegisterServices() .Bind().ToConstant(Services.Platform) .Bind().ToConstant(Services.RenderInterface) .Bind().ToConstant(Services.ThreadingInterface) - .Bind().ToConstant(Services.Scheduler) .Bind().ToConstant(Services.StandardCursorFactory) - .Bind().ToConstant(Services.Styler) .Bind().ToConstant(Services.WindowingPlatform) .Bind().ToConstant(Services.PlatformHotkeyConfiguration) .Bind().ToConstant(Services.FontManagerImpl) diff --git a/test/AvaloniaEdit.Tests/Rendering/TextViewTests.cs b/test/AvaloniaEdit.Tests/Rendering/TextViewTests.cs index 1d4f9382..4a2e1912 100644 --- a/test/AvaloniaEdit.Tests/Rendering/TextViewTests.cs +++ b/test/AvaloniaEdit.Tests/Rendering/TextViewTests.cs @@ -54,7 +54,7 @@ public void Visual_Line_Should_Create_One_Text_Lines_When_Not_Wrapping() VisualLine visualLine = textView.GetOrConstructVisualLine(document.Lines[0]); Assert.AreEqual(1, visualLine.TextLines.Count); - Assert.AreEqual("hello world", new string(visualLine.TextLines[0].TextRuns[0].Text.Buffer.Span)); + Assert.AreEqual("hello world", new string(visualLine.TextLines[0].TextRuns[0].Text.Span)); } } } From 1419fda1f8cc141d8310f00c0cfacb2ec1bfd55e Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 6 Feb 2023 15:02:19 +0100 Subject: [PATCH 3/4] Fix demp app theme --- src/AvaloniaEdit.Demo/App.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AvaloniaEdit.Demo/App.xaml b/src/AvaloniaEdit.Demo/App.xaml index e1296d84..015ce7d6 100644 --- a/src/AvaloniaEdit.Demo/App.xaml +++ b/src/AvaloniaEdit.Demo/App.xaml @@ -2,7 +2,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:cc="clr-namespace:AvaloniaEdit.CodeCompletion;assembly=AvaloniaEdit" x:Class="AvaloniaEdit.Demo.App" - RequestedThemeVariant="Default"> + RequestedThemeVariant="Dark"> From 8a83e8f7c735021be13472034354b9874b81696f Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 6 Feb 2023 15:03:03 +0100 Subject: [PATCH 4/4] Removed commented code --- src/AvaloniaEdit/Rendering/VisualLineText.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/AvaloniaEdit/Rendering/VisualLineText.cs b/src/AvaloniaEdit/Rendering/VisualLineText.cs index cf79e48f..f4625846 100644 --- a/src/AvaloniaEdit/Rendering/VisualLineText.cs +++ b/src/AvaloniaEdit/Rendering/VisualLineText.cs @@ -68,8 +68,6 @@ public override TextRun CreateTextRun(int startVisualColumn, ITextRunConstructio offset, DocumentLength - relativeOffset); - //var bufferOffset = RelativeTextOffset; - var textSlice = text.Text.AsMemory().Slice(text.Offset, text.Count); return new TextCharacters(textSlice, TextRunProperties);