From ab06e45488b94f2d767def229460283a50281e83 Mon Sep 17 00:00:00 2001 From: RMBGAME Date: Fri, 19 Nov 2021 20:08:06 +0800 Subject: [PATCH 001/416] TrayIcon should be re-added when the Explorer is restarted --- .../Interop/UnmanagedMethods.cs | 85 ++++++++++--------- src/Windows/Avalonia.Win32/TrayIconImpl.cs | 12 ++- 2 files changed, 55 insertions(+), 42 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index 938f4222e0..9e514461d7 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -903,9 +903,9 @@ namespace Avalonia.Win32.Interop [DllImport("user32.dll")] public static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lprcClip, MonitorEnumDelegate lpfnEnum, IntPtr dwData); - + public delegate bool MonitorEnumDelegate(IntPtr hMonitor, IntPtr hdcMonitor, ref Rect lprcMonitor, IntPtr dwData); - + [DllImport("user32.dll", SetLastError = true)] public static extern IntPtr GetDC(IntPtr hWnd); @@ -996,7 +996,7 @@ namespace Avalonia.Win32.Interop public static uint GetWindowLong(IntPtr hWnd, int nIndex) { - if(IntPtr.Size == 4) + if (IntPtr.Size == 4) { return GetWindowLong32b(hWnd, nIndex); } @@ -1023,7 +1023,7 @@ namespace Avalonia.Win32.Interop return (uint)SetWindowLong64b(hWnd, nIndex, new IntPtr((uint)value)).ToInt32(); } } - + public static IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr handle) { if (IntPtr.Size == 4) @@ -1057,14 +1057,14 @@ namespace Avalonia.Win32.Interop [DllImport("user32.dll")] public static extern bool InvalidateRect(IntPtr hWnd, RECT* lpRect, bool bErase); - - + + [DllImport("user32.dll")] public static extern bool ValidateRect(IntPtr hWnd, IntPtr lpRect); [DllImport("user32.dll")] public static extern bool IsWindow(IntPtr hWnd); - + [DllImport("user32.dll")] public static extern bool IsWindowEnabled(IntPtr hWnd); @@ -1091,22 +1091,25 @@ namespace Avalonia.Win32.Interop [DllImport("user32")] public static extern IntPtr GetMessageExtraInfo(); - + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "RegisterClassExW")] public static extern ushort RegisterClassEx(ref WNDCLASSEX lpwcx); [DllImport("user32.dll")] public static extern void RegisterTouchWindow(IntPtr hWnd, int flags); - + [DllImport("user32.dll")] public static extern bool ReleaseCapture(); + [DllImport("user32.dll", SetLastError = true)] + public static extern uint RegisterWindowMessage(string lpString); + [DllImport("user32.dll")] public static extern bool ScreenToClient(IntPtr hWnd, ref POINT lpPoint); [DllImport("user32.dll", SetLastError = true)] public static extern IntPtr GetActiveWindow(); - + [DllImport("user32.dll", SetLastError = true)] public static extern IntPtr SetActiveWindow(IntPtr hWnd); @@ -1282,7 +1285,7 @@ namespace Avalonia.Win32.Interop [DllImport("kernel32.dll", SetLastError = true)] public static extern IntPtr LoadLibrary(string fileName); - + [DllImport("kernel32.dll", SetLastError = true)] public static extern IntPtr LoadLibraryEx(string fileName, IntPtr hFile, int flags); @@ -1326,7 +1329,7 @@ namespace Avalonia.Win32.Interop [DllImport("user32.dll")] public static extern IntPtr MonitorFromWindow(IntPtr hwnd, MONITOR dwFlags); - + [DllImport("user32", EntryPoint = "GetMonitorInfoW", ExactSpelling = true, CharSet = CharSet.Unicode)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool GetMonitorInfo([In] IntPtr hMonitor, ref MONITORINFO lpmi); @@ -1334,14 +1337,14 @@ namespace Avalonia.Win32.Interop [DllImport("user32")] public static extern unsafe bool GetTouchInputInfo( IntPtr hTouchInput, - uint cInputs, + uint cInputs, TOUCHINPUT* pInputs, - int cbSize + int cbSize ); - + [DllImport("user32")] public static extern bool CloseTouchInputHandle(IntPtr hTouchInput); - + [return: MarshalAs(UnmanagedType.Bool)] [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "PostMessageW")] public static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); @@ -1350,7 +1353,7 @@ namespace Avalonia.Win32.Interop public static extern int SetDIBitsToDevice(IntPtr hdc, int XDest, int YDest, uint dwWidth, uint dwHeight, int XSrc, int YSrc, uint uStartScan, uint cScanLines, IntPtr lpvBits, [In] ref BITMAPINFOHEADER lpbmi, uint fuColorUse); - + [DllImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool CloseHandle(IntPtr hObject); @@ -1365,27 +1368,27 @@ namespace Avalonia.Win32.Interop [DllImport("gdi32.dll")] public static extern int ChoosePixelFormat(IntPtr hdc, ref PixelFormatDescriptor pfd); - + [DllImport("gdi32.dll")] public static extern int DescribePixelFormat(IntPtr hdc, ref PixelFormatDescriptor pfd); [DllImport("gdi32.dll")] public static extern int SetPixelFormat(IntPtr hdc, int iPixelFormat, ref PixelFormatDescriptor pfd); - - + + [DllImport("gdi32.dll")] public static extern int DescribePixelFormat(IntPtr hdc, int iPixelFormat, int bytes, ref PixelFormatDescriptor pfd); - + [DllImport("gdi32.dll")] public static extern bool SwapBuffers(IntPtr hdc); [DllImport("opengl32.dll")] public static extern IntPtr wglCreateContext(IntPtr hdc); - + [DllImport("opengl32.dll")] public static extern bool wglDeleteContext(IntPtr context); - + [DllImport("opengl32.dll")] public static extern bool wglMakeCurrent(IntPtr hdc, IntPtr context); @@ -1406,9 +1409,9 @@ namespace Avalonia.Win32.Interop uint dwMaximumSizeLow, string lpName); - [DllImport("msvcrt.dll", EntryPoint="memcpy", SetLastError = false, CallingConvention=CallingConvention.Cdecl)] - public static extern IntPtr CopyMemory(IntPtr dest, IntPtr src, UIntPtr count); - + [DllImport("msvcrt.dll", EntryPoint = "memcpy", SetLastError = false, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr CopyMemory(IntPtr dest, IntPtr src, UIntPtr count); + [DllImport("ole32.dll", CharSet = CharSet.Auto, ExactSpelling = true)] public static extern HRESULT RegisterDragDrop(IntPtr hwnd, IDropTarget target); @@ -1447,10 +1450,10 @@ namespace Avalonia.Win32.Interop [DllImport("dwmapi.dll")] public static extern void DwmFlush(); - + [DllImport("dwmapi.dll")] public static extern bool DwmDefWindowProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam, ref IntPtr plResult); - + [DllImport("dwmapi.dll")] public static extern void DwmEnableBlurBehindWindow(IntPtr hwnd, ref DWM_BLURBEHIND blurBehind); @@ -1507,8 +1510,8 @@ namespace Avalonia.Win32.Interop throw new Exception("RtlGetVersion failed!"); } } - - [DllImport("kernel32", EntryPoint="WaitForMultipleObjectsEx", SetLastError = true, CharSet = CharSet.Auto)] + + [DllImport("kernel32", EntryPoint = "WaitForMultipleObjectsEx", SetLastError = true, CharSet = CharSet.Auto)] private static extern int IntWaitForMultipleObjectsEx(int nCount, IntPtr[] pHandles, bool bWaitAll, int dwMilliseconds, bool bAlertable); public const int WAIT_FAILED = unchecked((int)0xFFFFFFFF); @@ -1516,7 +1519,7 @@ namespace Avalonia.Win32.Interop internal static int WaitForMultipleObjectsEx(int nCount, IntPtr[] pHandles, bool bWaitAll, int dwMilliseconds, bool bAlertable) { int result = IntWaitForMultipleObjectsEx(nCount, pHandles, bWaitAll, dwMilliseconds, bAlertable); - if(result == WAIT_FAILED) + if (result == WAIT_FAILED) { throw new Win32Exception(); } @@ -1558,7 +1561,7 @@ namespace Avalonia.Win32.Interop DrawLeftBorder = 0x20, DrawTopBorder = 0x40, DrawRightBorder = 0x80, - DrawBottomBorder = 0x100, + DrawBottomBorder = 0x100, } [StructLayout(LayoutKind.Sequential)] @@ -1626,9 +1629,9 @@ namespace Avalonia.Win32.Interop MDT_ANGULAR_DPI = 1, MDT_RAW_DPI = 2, MDT_DEFAULT = MDT_EFFECTIVE_DPI - } + } - public enum ClipboardFormat + public enum ClipboardFormat { /// /// Text format. Each line ends with a carriage return/linefeed (CR-LF) combination. A null character signals the end of the data. Use this format for ANSI text. @@ -1679,7 +1682,7 @@ namespace Avalonia.Win32.Interop public int X; public int Y; } - + public struct SIZE { public int X; @@ -1880,7 +1883,7 @@ namespace Avalonia.Win32.Interop OFN_NOREADONLYRETURN = 0x00008000, OFN_OVERWRITEPROMPT = 0x00000002 } - + public enum HRESULT : uint { S_FALSE = 0x0001, @@ -2198,13 +2201,13 @@ namespace Avalonia.Win32.Interop internal interface IDropTarget { [PreserveSig] - UnmanagedMethods.HRESULT DragEnter([MarshalAs(UnmanagedType.Interface)] [In] IOleDataObject pDataObj, [MarshalAs(UnmanagedType.U4)] [In] int grfKeyState, [MarshalAs(UnmanagedType.U8)] [In] long pt, [In] [Out] ref DropEffect pdwEffect); + UnmanagedMethods.HRESULT DragEnter([MarshalAs(UnmanagedType.Interface)][In] IOleDataObject pDataObj, [MarshalAs(UnmanagedType.U4)][In] int grfKeyState, [MarshalAs(UnmanagedType.U8)][In] long pt, [In][Out] ref DropEffect pdwEffect); [PreserveSig] - UnmanagedMethods.HRESULT DragOver([MarshalAs(UnmanagedType.U4)] [In] int grfKeyState, [MarshalAs(UnmanagedType.U8)] [In] long pt, [In] [Out] ref DropEffect pdwEffect); + UnmanagedMethods.HRESULT DragOver([MarshalAs(UnmanagedType.U4)][In] int grfKeyState, [MarshalAs(UnmanagedType.U8)][In] long pt, [In][Out] ref DropEffect pdwEffect); [PreserveSig] UnmanagedMethods.HRESULT DragLeave(); [PreserveSig] - UnmanagedMethods.HRESULT Drop([MarshalAs(UnmanagedType.Interface)] [In] IOleDataObject pDataObj, [MarshalAs(UnmanagedType.U4)] [In] int grfKeyState, [MarshalAs(UnmanagedType.U8)] [In] long pt, [In] [Out] ref DropEffect pdwEffect); + UnmanagedMethods.HRESULT Drop([MarshalAs(UnmanagedType.Interface)][In] IOleDataObject pDataObj, [MarshalAs(UnmanagedType.U4)][In] int grfKeyState, [MarshalAs(UnmanagedType.U8)][In] long pt, [In][Out] ref DropEffect pdwEffect); } [ComImport] @@ -2213,9 +2216,9 @@ namespace Avalonia.Win32.Interop internal interface IDropSource { [PreserveSig] - int QueryContinueDrag(int fEscapePressed, [MarshalAs(UnmanagedType.U4)] [In] int grfKeyState); + int QueryContinueDrag(int fEscapePressed, [MarshalAs(UnmanagedType.U4)][In] int grfKeyState); [PreserveSig] - int GiveFeedback([MarshalAs(UnmanagedType.U4)] [In] int dwEffect); + int GiveFeedback([MarshalAs(UnmanagedType.U4)][In] int dwEffect); } diff --git a/src/Windows/Avalonia.Win32/TrayIconImpl.cs b/src/Windows/Avalonia.Win32/TrayIconImpl.cs index 23395dd9b5..c28ec94fe8 100644 --- a/src/Windows/Avalonia.Win32/TrayIconImpl.cs +++ b/src/Windows/Avalonia.Win32/TrayIconImpl.cs @@ -24,6 +24,7 @@ namespace Avalonia.Win32 private readonly Win32NativeToManagedMenuExporter _exporter; private static readonly Dictionary s_trayIcons = new Dictionary(); private bool _disposedValue; + private static readonly uint WM_TASKBARCREATED = UnmanagedMethods.RegisterWindowMessage("TaskbarCreated"); public TrayIconImpl() { @@ -44,6 +45,15 @@ namespace Avalonia.Win32 { s_trayIcons[wParam.ToInt32()].WndProc(hWnd, msg, wParam, lParam); } + + if (msg == WM_TASKBARCREATED) + { + foreach (var tray in s_trayIcons.Values) + { + tray.UpdateIcon(true); + tray.UpdateIcon(); + } + } } public void SetIcon(IWindowIconImpl? icon) @@ -145,7 +155,7 @@ namespace Avalonia.Win32 private enum CustomWindowsMessage : uint { WM_TRAYICON = WindowsMessage.WM_APP + 1024, - WM_TRAYMOUSE = WindowsMessage.WM_USER + 1024 + WM_TRAYMOUSE = WindowsMessage.WM_USER + 1024, } private class TrayIconMenuFlyoutPresenter : MenuFlyoutPresenter, IStyleable From 6bb6c0e5c92716521aac4915904f509450966397 Mon Sep 17 00:00:00 2001 From: RMBGAME Date: Fri, 19 Nov 2021 21:55:35 +0800 Subject: [PATCH 002/416] Only re-add visible TrayIcon --- src/Windows/Avalonia.Win32/TrayIconImpl.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Windows/Avalonia.Win32/TrayIconImpl.cs b/src/Windows/Avalonia.Win32/TrayIconImpl.cs index c28ec94fe8..86732539f1 100644 --- a/src/Windows/Avalonia.Win32/TrayIconImpl.cs +++ b/src/Windows/Avalonia.Win32/TrayIconImpl.cs @@ -50,8 +50,11 @@ namespace Avalonia.Win32 { foreach (var tray in s_trayIcons.Values) { - tray.UpdateIcon(true); - tray.UpdateIcon(); + if (tray._iconAdded) + { + tray.UpdateIcon(true); + tray.UpdateIcon(); + } } } } From 394a5be4026afcf41202d876fb29dcf4beb8ab7c Mon Sep 17 00:00:00 2001 From: Lubomir Tetak Date: Fri, 17 Dec 2021 14:52:22 +0100 Subject: [PATCH 003/416] OSX handle CMD+key up combinations in Avalonia --- native/Avalonia.Native/src/OSX/app.mm | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/native/Avalonia.Native/src/OSX/app.mm b/native/Avalonia.Native/src/OSX/app.mm index 79175d9ff1..05b129baca 100644 --- a/native/Avalonia.Native/src/OSX/app.mm +++ b/native/Avalonia.Native/src/OSX/app.mm @@ -73,6 +73,11 @@ ComPtr _events; _isHandlingSendEvent = true; @try { [super sendEvent: event]; + if ([event type] == NSEventTypeKeyUp && ([event modifierFlags] & NSEventModifierFlagCommand)) + { + [[self keyWindow] sendEvent:event]; + } + } @finally { _isHandlingSendEvent = oldHandling; } From 213cc3429b7d91a2e84c1c2978a28d82ccb2f1ab Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sun, 16 Jan 2022 19:27:34 +0300 Subject: [PATCH 004/416] Visual now uses WeakEvent too --- src/Avalonia.Visuals/Visual.cs | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Visuals/Visual.cs b/src/Avalonia.Visuals/Visual.cs index 324b253a0f..41c9f76dbc 100644 --- a/src/Avalonia.Visuals/Visual.cs +++ b/src/Avalonia.Visuals/Visual.cs @@ -91,11 +91,17 @@ namespace Avalonia /// public static readonly StyledProperty ZIndexProperty = AvaloniaProperty.Register(nameof(ZIndex)); + + private static readonly WeakEvent InvalidatedWeakEvent = + WeakEvent.Register( + (s, h) => s.Invalidated += h, + (s, h) => s.Invalidated -= h); private Rect _bounds; private TransformedBounds? _transformedBounds; private IRenderRoot? _visualRoot; private IVisual? _visualParent; + private WeakEventSubscriber? _affectsRenderWeakSubscriber; /// /// Initializes static members of the class. @@ -352,12 +358,21 @@ namespace Avalonia { if (e.OldValue is IAffectsRender oldValue) { - WeakEventHandlerManager.Unsubscribe(oldValue, nameof(oldValue.Invalidated), sender.AffectsRenderInvalidated); + if (sender._affectsRenderWeakSubscriber != null) + InvalidatedWeakEvent.Unsubscribe(oldValue, sender._affectsRenderWeakSubscriber); } if (e.NewValue is IAffectsRender newValue) { - WeakEventHandlerManager.Subscribe(newValue, nameof(newValue.Invalidated), sender.AffectsRenderInvalidated); + if (sender._affectsRenderWeakSubscriber == null) + { + sender._affectsRenderWeakSubscriber = new WeakEventSubscriber(); + sender._affectsRenderWeakSubscriber.Event += delegate + { + sender.InvalidateVisual(); + }; + } + InvalidatedWeakEvent.Subscribe(newValue, sender._affectsRenderWeakSubscriber); } sender.InvalidateVisual(); @@ -608,8 +623,6 @@ namespace Avalonia OnVisualParentChanged(old, value); } - private void AffectsRenderInvalidated(object? sender, EventArgs e) => InvalidateVisual(); - /// /// Called when the collection changes. /// From 7e1d9dbc72eb9c6371f8bf33800caab19916cf20 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sun, 16 Jan 2022 19:28:49 +0300 Subject: [PATCH 005/416] Optimized WeakEvent (9332ms to 5ms using #6660 bench) --- src/Avalonia.Base/Utilities/WeakEvent.cs | 85 ++++++++++++++++++++---- 1 file changed, 72 insertions(+), 13 deletions(-) diff --git a/src/Avalonia.Base/Utilities/WeakEvent.cs b/src/Avalonia.Base/Utilities/WeakEvent.cs index 0b32015a8a..21c165afae 100644 --- a/src/Avalonia.Base/Utilities/WeakEvent.cs +++ b/src/Avalonia.Base/Utilities/WeakEvent.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; @@ -36,7 +37,7 @@ public class WeakEvent : WeakEvent where TEventArgs : Event { if (!_subscriptions.TryGetValue(target, out var subscription)) _subscriptions.Add(target, subscription = new Subscription(this, target)); - subscription.Add(new WeakReference>(subscriber)); + subscription.Add(subscriber); } public void Unsubscribe(TSender target, IWeakEventSubscriber subscriber) @@ -51,11 +52,61 @@ public class WeakEvent : WeakEvent where TEventArgs : Event private readonly TSender _target; private readonly Action _compact; - private WeakReference>?[] _data = - new WeakReference>[16]; + struct Entry + { + WeakReference>? _reference; + int _hashCode; + + public Entry(IWeakEventSubscriber r) + { + if (r == null) + { + _reference = null; + _hashCode = 0; + return; + } + + _hashCode = r.GetHashCode(); + _reference = new WeakReference>(r); + } + + public bool IsEmpty + { + get + { + if (_reference == null) + return false; + if (_reference.TryGetTarget(out var target)) + return true; + _reference = null; + return false; + } + } + + public bool TryGetTarget([MaybeNullWhen(false)]out IWeakEventSubscriber target) + { + if (_reference == null) + { + target = null!; + return false; + } + return _reference.TryGetTarget(out target); + } + + public bool Equals(IWeakEventSubscriber r) + { + if (_reference == null || r.GetHashCode() != _hashCode) + return false; + return _reference.TryGetTarget(out var target) && target == r; + } + } + + private Entry[] _data = + new Entry[16]; private int _count; private readonly Action _unsubscribe; private bool _compactScheduled; + private int _removedSinceLastCompact; public Subscription(WeakEvent ev, TSender target) { @@ -71,17 +122,17 @@ public class WeakEvent : WeakEvent where TEventArgs : Event _ev._subscriptions.Remove(_target); } - public void Add(WeakReference> s) + public void Add(IWeakEventSubscriber s) { if (_count == _data.Length) { //Extend capacity - var extendedData = new WeakReference>?[_data.Length * 2]; + var extendedData = new Entry[_data.Length * 2]; Array.Copy(_data, extendedData, _data.Length); _data = extendedData; } - _data[_count] = s; + _data[_count] = new(s); _count++; } @@ -93,16 +144,21 @@ public class WeakEvent : WeakEvent where TEventArgs : Event { var reference = _data[c]; - if (reference != null && reference.TryGetTarget(out var instance) && instance == s) + if (reference.Equals(s)) { - _data[c] = null; + _data[c] = default; removed = true; + break; } } if (removed) { + _removedSinceLastCompact++; ScheduleCompact(); + + if (_removedSinceLastCompact > 500) + Compact(); } } @@ -116,18 +172,21 @@ public class WeakEvent : WeakEvent where TEventArgs : Event void Compact() { + if(!_compactScheduled || _removedSinceLastCompact == 0) + return; _compactScheduled = false; + _removedSinceLastCompact = 0; int empty = -1; for (var c = 0; c < _count; c++) { - var r = _data[c]; + ref var r = ref _data[c]; //Mark current index as first empty - if (r == null && empty == -1) + if (r.IsEmpty && empty == -1) empty = c; //If current element isn't null and we have an empty one - if (r != null && empty != -1) + if (!r.IsEmpty && empty != -1) { - _data[c] = null; + _data[c] = default; _data[empty] = r; empty++; } @@ -145,7 +204,7 @@ public class WeakEvent : WeakEvent where TEventArgs : Event for (var c = 0; c < _count; c++) { var r = _data[c]; - if (r?.TryGetTarget(out var sub) == true) + if (r.TryGetTarget(out var sub)) sub!.OnEvent(_target, _ev, eventArgs); else needCompact = true; From dca2ca5940f0c768529f97b89535ed172ef44403 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sun, 16 Jan 2022 19:29:14 +0300 Subject: [PATCH 006/416] Don't implement IWeakEventSubscriber directly on public types --- .../Utilities/IWeakEventSubscriber.cs | 10 +++++++ .../Repeater/ItemsRepeater.cs | 30 ++++++++++--------- src/Avalonia.Controls/TopLevel.cs | 16 +++++----- src/Avalonia.Visuals/Media/Pen.cs | 25 +++++++++------- 4 files changed, 49 insertions(+), 32 deletions(-) diff --git a/src/Avalonia.Base/Utilities/IWeakEventSubscriber.cs b/src/Avalonia.Base/Utilities/IWeakEventSubscriber.cs index e48c0cb111..2a24376592 100644 --- a/src/Avalonia.Base/Utilities/IWeakEventSubscriber.cs +++ b/src/Avalonia.Base/Utilities/IWeakEventSubscriber.cs @@ -9,4 +9,14 @@ namespace Avalonia.Utilities; public interface IWeakEventSubscriber where TEventArgs : EventArgs { void OnEvent(object? sender, WeakEvent ev, TEventArgs e); +} + +public class WeakEventSubscriber : IWeakEventSubscriber where TEventArgs : EventArgs +{ + public event Action? Event; + + void IWeakEventSubscriber.OnEvent(object? sender, WeakEvent ev, TEventArgs e) + { + + } } \ No newline at end of file diff --git a/src/Avalonia.Controls/Repeater/ItemsRepeater.cs b/src/Avalonia.Controls/Repeater/ItemsRepeater.cs index b40cf26df5..6d2f4144eb 100644 --- a/src/Avalonia.Controls/Repeater/ItemsRepeater.cs +++ b/src/Avalonia.Controls/Repeater/ItemsRepeater.cs @@ -20,7 +20,7 @@ namespace Avalonia.Controls /// Represents a data-driven collection control that incorporates a flexible layout system, /// custom views, and virtualization. /// - public class ItemsRepeater : Panel, IChildIndexProvider, IWeakEventSubscriber + public class ItemsRepeater : Panel, IChildIndexProvider { /// /// Defines the property. @@ -68,6 +68,7 @@ namespace Avalonia.Controls private ItemsRepeaterElementPreparedEventArgs _elementPreparedArgs; private ItemsRepeaterElementClearingEventArgs _elementClearingArgs; private ItemsRepeaterElementIndexChangedEventArgs _elementIndexChangedArgs; + private WeakEventSubscriber _layoutWeakSubscriber = new(); /// /// Initializes a new instance of the class. @@ -77,6 +78,15 @@ namespace Avalonia.Controls _viewManager = new ViewManager(this); _viewportManager = new ViewportManager(this); KeyboardNavigation.SetTabNavigation(this, KeyboardNavigationMode.Once); + + _layoutWeakSubscriber.Event += (_, ev, e) => + { + if (ev == AttachedLayout.ArrangeInvalidatedWeakEvent) + InvalidateArrange(); + else if (ev == AttachedLayout.MeasureInvalidatedWeakEvent) + InvalidateMeasure(); + }; + OnLayoutChanged(null, Layout); } @@ -723,8 +733,8 @@ namespace Avalonia.Controls { oldValue.UninitializeForContext(LayoutContext); - AttachedLayout.MeasureInvalidatedWeakEvent.Unsubscribe(oldValue, this); - AttachedLayout.ArrangeInvalidatedWeakEvent.Unsubscribe(oldValue, this); + AttachedLayout.MeasureInvalidatedWeakEvent.Unsubscribe(oldValue, _layoutWeakSubscriber); + AttachedLayout.ArrangeInvalidatedWeakEvent.Unsubscribe(oldValue, _layoutWeakSubscriber); // Walk through all the elements and make sure they are cleared foreach (var element in Children) @@ -742,8 +752,8 @@ namespace Avalonia.Controls { newValue.InitializeForContext(LayoutContext); - AttachedLayout.MeasureInvalidatedWeakEvent.Subscribe(newValue, this); - AttachedLayout.ArrangeInvalidatedWeakEvent.Subscribe(newValue, this); + AttachedLayout.MeasureInvalidatedWeakEvent.Subscribe(newValue, _layoutWeakSubscriber); + AttachedLayout.ArrangeInvalidatedWeakEvent.Subscribe(newValue, _layoutWeakSubscriber); } bool isVirtualizingLayout = newValue != null && newValue is VirtualizingLayout; @@ -793,15 +803,7 @@ namespace Avalonia.Controls { _viewportManager.OnBringIntoViewRequested(e); } - - void IWeakEventSubscriber.OnEvent(object? sender, WeakEvent ev, EventArgs e) - { - if(ev == AttachedLayout.ArrangeInvalidatedWeakEvent) - InvalidateArrange(); - else if (ev == AttachedLayout.MeasureInvalidatedWeakEvent) - InvalidateMeasure(); - } - + private VirtualizingLayoutContext GetLayoutContext() { if (_layoutContext == null) diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index eaee5bdb50..76de113290 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -33,8 +33,7 @@ namespace Avalonia.Controls ICloseable, IStyleHost, ILogicalRoot, - ITextInputMethodRoot, - IWeakEventSubscriber + ITextInputMethodRoot { /// /// Defines the property. @@ -90,6 +89,7 @@ namespace Avalonia.Controls private WindowTransparencyLevel _actualTransparencyLevel; private ILayoutManager _layoutManager; private Border _transparencyFallbackBorder; + private WeakEventSubscriber _resourcesChangesSubscriber; /// /// Initializes static members of the class. @@ -184,7 +184,12 @@ namespace Avalonia.Controls if (((IStyleHost)this).StylingParent is IResourceHost applicationResources) { - ResourcesChangedWeakEvent.Subscribe(applicationResources, this); + _resourcesChangesSubscriber = new(); + _resourcesChangesSubscriber.Event += (_, __, e) => + { + ((ILogical)this).NotifyResourcesChanged(e); + }; + ResourcesChangedWeakEvent.Subscribe(applicationResources, _resourcesChangesSubscriber); } impl.LostFocus += PlatformImpl_LostFocus; @@ -289,11 +294,6 @@ namespace Avalonia.Controls /// IMouseDevice IInputRoot.MouseDevice => PlatformImpl?.MouseDevice; - void IWeakEventSubscriber.OnEvent(object sender, WeakEvent ev, ResourcesChangedEventArgs e) - { - ((ILogical)this).NotifyResourcesChanged(e); - } - /// /// Gets or sets a value indicating whether access keys are shown in the window. /// diff --git a/src/Avalonia.Visuals/Media/Pen.cs b/src/Avalonia.Visuals/Media/Pen.cs index 65ba851100..f0a0d24248 100644 --- a/src/Avalonia.Visuals/Media/Pen.cs +++ b/src/Avalonia.Visuals/Media/Pen.cs @@ -7,7 +7,7 @@ namespace Avalonia.Media /// /// Describes how a stroke is drawn. /// - public sealed class Pen : AvaloniaObject, IPen, IWeakEventSubscriber + public sealed class Pen : AvaloniaObject, IPen { /// /// Defines the property. @@ -48,7 +48,8 @@ namespace Avalonia.Media private EventHandler? _invalidated; private IAffectsRender? _subscribedToBrush; private IAffectsRender? _subscribedToDashes; - + private WeakEventSubscriber? _weakSubscriber; + /// /// Initializes a new instance of the class. /// @@ -207,13 +208,23 @@ namespace Avalonia.Media { if ((_invalidated == null || field != value) && field != null) { - InvalidatedWeakEvent.Unsubscribe(field, this); + if (_weakSubscriber != null) + InvalidatedWeakEvent.Unsubscribe(field, _weakSubscriber); field = null; } if (_invalidated != null && field != value && value is IAffectsRender affectsRender) { - InvalidatedWeakEvent.Subscribe(affectsRender, this); + if (_weakSubscriber == null) + { + _weakSubscriber = new WeakEventSubscriber(); + _weakSubscriber.Event += (_, ev, __) => + { + if (ev == InvalidatedWeakEvent) + _invalidated?.Invoke(this, EventArgs.Empty); + }; + } + InvalidatedWeakEvent.Subscribe(affectsRender, _weakSubscriber); field = affectsRender; } } @@ -223,11 +234,5 @@ namespace Avalonia.Media UpdateSubscription(ref _subscribedToBrush, Brush); UpdateSubscription(ref _subscribedToDashes, DashStyle); } - - void IWeakEventSubscriber.OnEvent(object? sender, WeakEvent ev, EventArgs e) - { - if (ev == InvalidatedWeakEvent) - _invalidated?.Invoke(this, EventArgs.Empty); - } } } From bb9f5e75f33f460dcb729678b2c3a8a41fc81fb0 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sun, 16 Jan 2022 19:55:46 +0300 Subject: [PATCH 007/416] Fixed WeakEvent Compact --- src/Avalonia.Base/Utilities/WeakEvent.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.Base/Utilities/WeakEvent.cs b/src/Avalonia.Base/Utilities/WeakEvent.cs index 21c165afae..5d5a1cb55b 100644 --- a/src/Avalonia.Base/Utilities/WeakEvent.cs +++ b/src/Avalonia.Base/Utilities/WeakEvent.cs @@ -75,7 +75,7 @@ public class WeakEvent : WeakEvent where TEventArgs : Event get { if (_reference == null) - return false; + return true; if (_reference.TryGetTarget(out var target)) return true; _reference = null; @@ -179,15 +179,15 @@ public class WeakEvent : WeakEvent where TEventArgs : Event int empty = -1; for (var c = 0; c < _count; c++) { - ref var r = ref _data[c]; + ref var currentRef = ref _data[c]; //Mark current index as first empty - if (r.IsEmpty && empty == -1) + if (currentRef.IsEmpty && empty == -1) empty = c; //If current element isn't null and we have an empty one - if (!r.IsEmpty && empty != -1) + if (!currentRef.IsEmpty && empty != -1) { - _data[c] = default; - _data[empty] = r; + _data[empty] = currentRef; + currentRef = default; empty++; } } From c73fcd25a9cfcaa17e7295b6b01080c585fe3c78 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sun, 16 Jan 2022 20:09:24 +0300 Subject: [PATCH 008/416] Fixed tests --- src/Avalonia.Base/Utilities/IWeakEventSubscriber.cs | 2 +- src/Avalonia.Base/Utilities/WeakEvent.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Base/Utilities/IWeakEventSubscriber.cs b/src/Avalonia.Base/Utilities/IWeakEventSubscriber.cs index 2a24376592..57060853c8 100644 --- a/src/Avalonia.Base/Utilities/IWeakEventSubscriber.cs +++ b/src/Avalonia.Base/Utilities/IWeakEventSubscriber.cs @@ -17,6 +17,6 @@ public class WeakEventSubscriber : IWeakEventSubscriber void IWeakEventSubscriber.OnEvent(object? sender, WeakEvent ev, TEventArgs e) { - + Event?.Invoke(sender, ev, e); } } \ No newline at end of file diff --git a/src/Avalonia.Base/Utilities/WeakEvent.cs b/src/Avalonia.Base/Utilities/WeakEvent.cs index 5d5a1cb55b..bcee7f89f7 100644 --- a/src/Avalonia.Base/Utilities/WeakEvent.cs +++ b/src/Avalonia.Base/Utilities/WeakEvent.cs @@ -76,10 +76,10 @@ public class WeakEvent : WeakEvent where TEventArgs : Event { if (_reference == null) return true; - if (_reference.TryGetTarget(out var target)) - return true; + if (_reference.TryGetTarget(out _)) + return false; _reference = null; - return false; + return true; } } From a4fa74977f2a9c7c8cb6fc1f8b86c88f00543463 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sun, 16 Jan 2022 20:17:56 +0300 Subject: [PATCH 009/416] Removed immediate compact since it makes things worse --- src/Avalonia.Base/Utilities/WeakEvent.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/Avalonia.Base/Utilities/WeakEvent.cs b/src/Avalonia.Base/Utilities/WeakEvent.cs index bcee7f89f7..b27ea9f455 100644 --- a/src/Avalonia.Base/Utilities/WeakEvent.cs +++ b/src/Avalonia.Base/Utilities/WeakEvent.cs @@ -106,7 +106,6 @@ public class WeakEvent : WeakEvent where TEventArgs : Event private int _count; private readonly Action _unsubscribe; private bool _compactScheduled; - private int _removedSinceLastCompact; public Subscription(WeakEvent ev, TSender target) { @@ -154,11 +153,7 @@ public class WeakEvent : WeakEvent where TEventArgs : Event if (removed) { - _removedSinceLastCompact++; ScheduleCompact(); - - if (_removedSinceLastCompact > 500) - Compact(); } } @@ -172,10 +167,9 @@ public class WeakEvent : WeakEvent where TEventArgs : Event void Compact() { - if(!_compactScheduled || _removedSinceLastCompact == 0) + if(!_compactScheduled) return; _compactScheduled = false; - _removedSinceLastCompact = 0; int empty = -1; for (var c = 0; c < _count; c++) { From 8103f2a0b1c7dbe5f0c620229442ce8a7fe619d3 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sun, 16 Jan 2022 23:47:12 +0300 Subject: [PATCH 010/416] Use Dictionary for more than 8 WeakEvent subscribers --- src/Avalonia.Base/Utilities/WeakEvent.cs | 85 ++----- src/Avalonia.Base/Utilities/WeakHashList.cs | 236 ++++++++++++++++++++ 2 files changed, 258 insertions(+), 63 deletions(-) create mode 100644 src/Avalonia.Base/Utilities/WeakHashList.cs diff --git a/src/Avalonia.Base/Utilities/WeakEvent.cs b/src/Avalonia.Base/Utilities/WeakEvent.cs index b27ea9f455..1335d7e9b8 100644 --- a/src/Avalonia.Base/Utilities/WeakEvent.cs +++ b/src/Avalonia.Base/Utilities/WeakEvent.cs @@ -101,11 +101,10 @@ public class WeakEvent : WeakEvent where TEventArgs : Event } } - private Entry[] _data = - new Entry[16]; - private int _count; private readonly Action _unsubscribe; + private readonly WeakHashList> _list = new(); private bool _compactScheduled; + private bool _destroyed; public Subscription(WeakEvent ev, TSender target) { @@ -117,49 +116,27 @@ public class WeakEvent : WeakEvent where TEventArgs : Event void Destroy() { + if(_destroyed) + return; + _destroyed = true; _unsubscribe(); _ev._subscriptions.Remove(_target); } - public void Add(IWeakEventSubscriber s) - { - if (_count == _data.Length) - { - //Extend capacity - var extendedData = new Entry[_data.Length * 2]; - Array.Copy(_data, extendedData, _data.Length); - _data = extendedData; - } - - _data[_count] = new(s); - _count++; - } + public void Add(IWeakEventSubscriber s) => _list.Add(s); public void Remove(IWeakEventSubscriber s) { - var removed = false; - - for (int c = 0; c < _count; ++c) - { - var reference = _data[c]; - - if (reference.Equals(s)) - { - _data[c] = default; - removed = true; - break; - } - } - - if (removed) - { + _list.Remove(s); + if(_list.IsEmpty) + Destroy(); + else if(_list.NeedCompact && _compactScheduled) ScheduleCompact(); - } } void ScheduleCompact() { - if(_compactScheduled) + if(_compactScheduled || _destroyed) return; _compactScheduled = true; Dispatcher.UIThread.Post(_compact, DispatcherPriority.Background); @@ -170,42 +147,24 @@ public class WeakEvent : WeakEvent where TEventArgs : Event if(!_compactScheduled) return; _compactScheduled = false; - int empty = -1; - for (var c = 0; c < _count; c++) - { - ref var currentRef = ref _data[c]; - //Mark current index as first empty - if (currentRef.IsEmpty && empty == -1) - empty = c; - //If current element isn't null and we have an empty one - if (!currentRef.IsEmpty && empty != -1) - { - _data[empty] = currentRef; - currentRef = default; - empty++; - } - } - - if (empty != -1) - _count = empty; - if (_count == 0) + _list.Compact(); + if (_list.IsEmpty) Destroy(); } void OnEvent(object? sender, TEventArgs eventArgs) { - var needCompact = false; - for (var c = 0; c < _count; c++) + var alive = _list.GetAlive(); + if(alive == null) + Destroy(); + else { - var r = _data[c]; - if (r.TryGetTarget(out var sub)) - sub!.OnEvent(_target, _ev, eventArgs); - else - needCompact = true; + foreach(var item in alive) + item.OnEvent(_target, _ev, eventArgs); + WeakHashList>.ReturnToSharedPool(alive); + if(_list.NeedCompact && !_compactScheduled) + ScheduleCompact(); } - - if (needCompact) - ScheduleCompact(); } } diff --git a/src/Avalonia.Base/Utilities/WeakHashList.cs b/src/Avalonia.Base/Utilities/WeakHashList.cs new file mode 100644 index 0000000000..32668872da --- /dev/null +++ b/src/Avalonia.Base/Utilities/WeakHashList.cs @@ -0,0 +1,236 @@ +using System; +using System.Collections.Generic; +using Avalonia.Collections.Pooled; + +namespace Avalonia.Utilities; + +internal class WeakHashList where T : class +{ + private struct Key + { + public WeakReference? Weak; + public T? Strong; + public int HashCode; + + public static Key MakeStrong(T r) => new() + { + HashCode = r.GetHashCode(), + Strong = r + }; + + public static Key MakeWeak(T r) => new() + { + HashCode = r.GetHashCode(), + Weak = new WeakReference(r) + }; + + public override int GetHashCode() => HashCode; + } + + class KeyComparer : IEqualityComparer + { + public bool Equals(Key x, Key y) + { + if (x.HashCode != y.HashCode) + return false; + if (x.Strong != null) + { + if (y.Strong != null) + return x.Strong == y.Strong; + if (y.Weak == null) + return false; + return y.Weak.TryGetTarget(out var weakTarget) && weakTarget == x.Strong; + } + else if (y.Strong != null) + { + if (x.Weak == null) + return false; + return x.Weak.TryGetTarget(out var weakTarget) && weakTarget == y.Strong; + } + else + { + if (x.Weak == null || x.Weak.TryGetTarget(out var xTarget) == false) + return y.Weak?.TryGetTarget(out _) != true; + return y.Weak?.TryGetTarget(out var yTarget) == true && xTarget == yTarget; + } + } + + public int GetHashCode(Key obj) => obj.HashCode; + public static KeyComparer Instance = new(); + } + + Dictionary? _dic; + WeakReference?[]? _arr; + int _arrCount; + + public bool IsEmpty => _dic == null || _dic.Count == 0; + public bool NeedCompact { get; private set; } + + public void Add(T item) + { + if (_dic != null) + { + var strongKey = Key.MakeStrong(item); + if (_dic.TryGetValue(strongKey, out var cnt)) + _dic[strongKey] = cnt + 1; + else + _dic[Key.MakeWeak(item)] = 1; + return; + } + + if (_arr == null) + _arr = new WeakReference[8]; + + if (_arrCount < _arr.Length) + { + _arr[_arrCount] = new WeakReference(item); + _arrCount++; + return; + } + + // Check if something is dead + for (var c = 0; c < _arrCount; c++) + { + if (_arr[c]!.TryGetTarget(out _) == false) + { + _arr[c] = new WeakReference(item); + return; + } + } + + _dic = new Dictionary(KeyComparer.Instance); + foreach (var existing in _arr) + { + if (existing!.TryGetTarget(out var target)) + Add(target); + } + _arr = null; + + } + + public void Remove(T item) + { + if (_arr != null) + { + for (var c = 0; c < _arr.Length; c++) + { + if (_arr[c]?.TryGetTarget(out var target) == true && target == item) + { + _arr[c] = null; + Compact(); + return; + } + } + } + else if (_dic != null) + { + var strongKey = Key.MakeStrong(item); + + if (_dic.TryGetValue(strongKey, out var cnt)) + { + if (cnt > 1) + { + _dic[strongKey] = cnt - 1; + return; + } + } + + _dic.Remove(strongKey); + } + } + + private void ArrCompact() + { + if (_arr != null) + { + int empty = -1; + for (var c = 0; c < _arrCount; c++) + { + var r = _arr[c]; + //Mark current index as first empty + if (r == null && empty == -1) + empty = c; + //If current element isn't null and we have an empty one + if (r != null && empty != -1) + { + _arr[c] = null; + _arr[empty] = r; + empty++; + } + } + + if (empty != -1) + _arrCount = empty; + } + } + + public void Compact() + { + if (_dic != null) + { + PooledList? toRemove = null; + foreach (var kvp in _dic) + { + if (kvp.Key.Weak?.TryGetTarget(out _) != true) + (toRemove ??= new PooledList()).Add(kvp.Key); + } + + if (toRemove != null) + { + foreach (var k in toRemove) + _dic.Remove(k); + toRemove.Dispose(); + } + } + } + + private static readonly Stack> s_listPool = new(); + + public static void ReturnToSharedPool(PooledList list) + { + list.Clear(); + s_listPool.Push(list); + } + + public PooledList? GetAlive(Func>? factory = null) + { + PooledList? pooled = null; + if (_arr != null) + { + bool needCompact = false; + for (var c = 0; c < _arrCount; c++) + { + if (_arr[c]?.TryGetTarget(out var target) == true) + (pooled ??= factory?.Invoke() + ?? (s_listPool.Count > 0 + ? s_listPool.Pop() + : new PooledList())).Add(target!); + else + { + _arr[c] = null; + needCompact = true; + } + } + if(needCompact) + ArrCompact(); + return pooled; + } + if (_dic != null) + { + + foreach (var kvp in _dic) + { + if (kvp.Key.Weak?.TryGetTarget(out var target) == true) + (pooled ??= factory?.Invoke() + ?? (s_listPool.Count > 0 + ? s_listPool.Pop() + : new PooledList())) + .Add(target!); + else + NeedCompact = true; + } + } + + return pooled; + } +} \ No newline at end of file From 27ecb055086d437516ec6685fc22016d77ebed94 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Mon, 17 Jan 2022 11:38:20 +0300 Subject: [PATCH 011/416] Use PooledList.Span for enumeration --- src/Avalonia.Base/Utilities/WeakEvent.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Utilities/WeakEvent.cs b/src/Avalonia.Base/Utilities/WeakEvent.cs index 1335d7e9b8..e72606bf70 100644 --- a/src/Avalonia.Base/Utilities/WeakEvent.cs +++ b/src/Avalonia.Base/Utilities/WeakEvent.cs @@ -159,7 +159,7 @@ public class WeakEvent : WeakEvent where TEventArgs : Event Destroy(); else { - foreach(var item in alive) + foreach(var item in alive.Span) item.OnEvent(_target, _ev, eventArgs); WeakHashList>.ReturnToSharedPool(alive); if(_list.NeedCompact && !_compactScheduled) From f41a6340f908e189204104385cd73e6e614da125 Mon Sep 17 00:00:00 2001 From: Andreas Schauerte Date: Mon, 5 Jul 2021 19:11:36 +0200 Subject: [PATCH 012/416] Cherry Pick and merge. --- src/Avalonia.Visuals/Matrix.cs | 192 +++++++++++++++++++++++++-------- 1 file changed, 145 insertions(+), 47 deletions(-) diff --git a/src/Avalonia.Visuals/Matrix.cs b/src/Avalonia.Visuals/Matrix.cs index b08a0eb98a..9dd5bced86 100644 --- a/src/Avalonia.Visuals/Matrix.cs +++ b/src/Avalonia.Visuals/Matrix.cs @@ -1,12 +1,21 @@ using System; using System.Globalization; +using System.Linq; using Avalonia.Utilities; namespace Avalonia { /// - /// A 2x3 matrix. + /// A 3x3 matrix. /// + /// Matrix layout: + /// | 1st col | 2nd col | 3r col | + /// 1st row | scaleX | skrewY | persX | + /// 2nd row | skrewX | scaleY | persY | + /// 3rd row | transX | transY | persZ | + /// + /// Note: Skia.SkMatrix uses a transposed layout (where for example skrewX/skrewY and persX/transX are swapped). + /// #if !BUILDTASK public #endif @@ -14,40 +23,77 @@ namespace Avalonia { private readonly double _m11; private readonly double _m12; + private readonly double _m13; private readonly double _m21; private readonly double _m22; + private readonly double _m23; private readonly double _m31; private readonly double _m32; + private readonly double _m33; + + + /// + /// Initializes a new instance of the struct (equivalent to a 2x3 Matrix without perspective). + /// + /// The first element of the first row. + /// The second element of the first row. + /// The first element of the second row. + /// The second element of the second row. + /// The first element of the third row. + /// The second element of the third row. + public Matrix( + double scaleX, + double skrewY, + double skrewX, + double scaleY, + double offsetX, + double offsetY) : this( scaleX, skrewY, 0, skrewX, scaleY, 0, offsetX, offsetY, 1) + { + } + + /// /// Initializes a new instance of the struct. /// - /// The first element of the first row. - /// The second element of the first row. - /// The first element of the second row. - /// The second element of the second row. + /// The first element of the first row. + /// The second element of the first row. + /// The third element of the first row. + /// The first element of the second row. + /// The second element of the second row. + /// The third element of the second row. /// The first element of the third row. /// The second element of the third row. + /// The third element of the third row. public Matrix( - double m11, - double m12, - double m21, - double m22, + double scaleX, + double skrewY, + double persX, + double skrewX, + double scaleY, + double persY, double offsetX, - double offsetY) + double offsetY, + double persZ) { - _m11 = m11; - _m12 = m12; - _m21 = m21; - _m22 = m22; + _m11 = scaleX; + _m12 = skrewY; + _m13 = persX; + _m21 = skrewX; + _m22 = scaleY; + _m23 = persY; _m31 = offsetX; _m32 = offsetY; + _m33 = persZ; } /// /// Returns the multiplicative identity matrix. /// - public static Matrix Identity { get; } = new Matrix(1.0, 0.0, 0.0, 1.0, 0.0, 0.0); + public static Matrix Identity { get; } = new Matrix( + 1.0, 0.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0); /// /// Returns whether the matrix is the identity matrix. @@ -60,35 +106,50 @@ namespace Avalonia public bool HasInverse => !MathUtilities.IsZero(GetDeterminant()); /// - /// The first element of the first row + /// The first element of the first row (scaleX). /// public double M11 => _m11; /// - /// The second element of the first row + /// The second element of the first row (skrewY). /// public double M12 => _m12; /// - /// The first element of the second row + /// The third element of the first row (persX: input x-axis perspective factor). + /// + public double M13 => _m13; + + /// + /// The first element of the second row (skrewX). /// public double M21 => _m21; /// - /// The second element of the second row + /// The second element of the second row (scaleY). /// public double M22 => _m22; /// - /// The first element of the third row + /// The third element of the second row (persY: input y-axis perspective factor). + /// + public double M23 => _m23; + + /// + /// The first element of the third row (offsetX/translateX). /// public double M31 => _m31; /// - /// The second element of the third row + /// The second element of the third row (offsetY/translateY). /// public double M32 => _m32; + /// + /// The third element of the third row (persZ: perspective scale factor). + /// + public double M33 => _m33; + /// /// Multiplies two matrices together and returns the resulting matrix. /// @@ -103,7 +164,7 @@ namespace Avalonia (value1.M21 * value2.M11) + (value1.M22 * value2.M21), (value1.M21 * value2.M12) + (value1.M22 * value2.M22), (value1._m31 * value2.M11) + (value1._m32 * value2.M21) + value2._m31, - (value1._m31 * value2.M12) + (value1._m32 * value2.M22) + value2._m32); + (value1._m31 * value2.M12) + (value1._m32 * value2.M22) + value2._m32); //TODO: include perspective } /// @@ -171,7 +232,7 @@ namespace Avalonia /// A scaling matrix. public static Matrix CreateScale(double xScale, double yScale) { - return CreateScale(new Vector(xScale, yScale)); + return new Matrix(xScale, 0, 0, yScale, 0, 0); } /// @@ -181,7 +242,7 @@ namespace Avalonia /// A scaling matrix. public static Matrix CreateScale(Vector scales) { - return new Matrix(scales.X, 0, 0, scales.Y, 0, 0); + return CreateScale(scales.X, scales.Y); } /// @@ -247,7 +308,12 @@ namespace Avalonia /// public double GetDeterminant() { - return (_m11 * _m22) - (_m12 * _m21); + //return (_m11 * _m22) - (_m12 * _m21); //TODO: ensure new implementation yields the same result as before, when pers is 0,0,1 + + // implemented using "Laplace expansion": + return _m11 * (_m22 * _m33 - _m23 * _m32) + - _m12 * (_m21 * _m33 - _m23 * _m31) + + _m13 * (_m21 * _m32 - _m22 * _m31); } /// @@ -260,10 +326,13 @@ namespace Avalonia // ReSharper disable CompareOfFloatsByEqualityOperator return _m11 == other.M11 && _m12 == other.M12 && + _m13 == other.M13 && _m21 == other.M21 && _m22 == other.M22 && + _m23 == other.M23 && _m31 == other.M31 && - _m32 == other.M32; + _m32 == other.M32 && + _m33 == other.M33; // ReSharper restore CompareOfFloatsByEqualityOperator } @@ -280,9 +349,18 @@ namespace Avalonia /// The hash code. public override int GetHashCode() { - return M11.GetHashCode() + M12.GetHashCode() + - M21.GetHashCode() + M22.GetHashCode() + - M31.GetHashCode() + M32.GetHashCode(); + return (_m11, _m12, _m13, _m21, _m22, _m23, _m31, _m32, _m33).GetHashCode(); + } + + /// + /// Determines if the current matrix contains perspective (non-affine) transforms (true) or only (affine) transforms that could be mapped into an 2x3 matrix (false). + /// + private bool ContainsPerspective() + { + + // ReSharper disable CompareOfFloatsByEqualityOperator + return _m13 != 0 || _m23 != 0 || _m33 != 1; + // ReSharper restore CompareOfFloatsByEqualityOperator } /// @@ -292,15 +370,25 @@ namespace Avalonia public override string ToString() { CultureInfo ci = CultureInfo.CurrentCulture; + + string msg; + double[] values; + + if (ContainsPerspective()) + { + msg = "{{ {{M11:{0} M12:{1} M13:{2}}} {{M21:{3} M22:{4} M23:{5}}} {{M31:{6} M32:{7} M33:{8}}} }}"; + values = new[] { M11, M12, M13, M21, M22, M23, M31, M32, M33 }; + } + else + { + msg = "{{ {{M11:{0} M12:{1}}} {{M21:{2} M22:{3}}} {{M31:{4} M32:{5}}} }}"; + values = new[] { M11, M12, M21, M22, M31, M32 }; + } + return string.Format( ci, - "{{ {{M11:{0} M12:{1}}} {{M21:{2} M22:{3}}} {{M31:{4} M32:{5}}} }}", - M11.ToString(ci), - M12.ToString(ci), - M21.ToString(ci), - M22.ToString(ci), - M31.ToString(ci), - M32.ToString(ci)); + msg, + values.Select((v) => v.ToString(ci)).ToArray()); } /// @@ -311,7 +399,7 @@ namespace Avalonia { double d = GetDeterminant(); - if (MathUtilities.IsZero(d)) + if (MathUtilities.IsZero(d)) //TODO: decide if special handling is required for perspective { inverted = default; @@ -347,20 +435,30 @@ namespace Avalonia /// /// Parses a string. /// - /// Six comma-delimited double values (m11, m12, m21, m22, offsetX, offsetY) that describe the new + /// Six or nine comma-delimited double values (m11, m12, m21, m22, offsetX, offsetY[, persX, persY, persZ]) that describe the new /// The . public static Matrix Parse(string s) { + // initialize to satisfy compiler - only used when retrieved from string. + double v8 = 0; + double v9 = 0; + using (var tokenizer = new StringTokenizer(s, CultureInfo.InvariantCulture, exceptionMessage: "Invalid Matrix.")) { - return new Matrix( - tokenizer.ReadDouble(), - tokenizer.ReadDouble(), - tokenizer.ReadDouble(), - tokenizer.ReadDouble(), - tokenizer.ReadDouble(), - tokenizer.ReadDouble() - ); + var v1 = tokenizer.ReadDouble(); + var v2 = tokenizer.ReadDouble(); + var v3 = tokenizer.ReadDouble(); + var v4 = tokenizer.ReadDouble(); + var v5 = tokenizer.ReadDouble(); + var v6 = tokenizer.ReadDouble(); + var pers = tokenizer.TryReadDouble(out var v7); + pers = pers && tokenizer.TryReadDouble(out v8); + pers = pers && tokenizer.TryReadDouble(out v9); + + if (pers) + return new Matrix(v1, v2, v7, v3, v4, v8, v5, v6, v9); + else + return new Matrix(v1, v2, v3, v4, v5, v6); } } @@ -376,7 +474,7 @@ namespace Avalonia var determinant = matrix.GetDeterminant(); - if (MathUtilities.IsZero(determinant)) + if (MathUtilities.IsZero(determinant) || matrix.ContainsPerspective()) { return false; } From c40410cdb371f37071fb5df91367c60f057ea317 Mon Sep 17 00:00:00 2001 From: Andreas Schauerte Date: Mon, 5 Jul 2021 18:29:12 +0200 Subject: [PATCH 013/416] Add basic 3d transformation. --- .../Resources/Resource.Designer.cs | 2 +- samples/RenderDemo/MainWindow.xaml | 3 + .../RenderDemo/Pages/Transform3DPage.axaml | 81 ++++++++ .../RenderDemo/Pages/Transform3DPage.axaml.cs | 21 ++ .../ViewModels/Transform3DPageViewModel.cs | 61 ++++++ .../Resources/Resource.Designer.cs | 2 +- .../Animation/Animators/TransformAnimator.cs | 1 + src/Avalonia.Visuals/Matrix.cs | 50 +++-- src/Avalonia.Visuals/Media/Transform3D.cs | 192 ++++++++++++++++++ src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs | 6 +- 10 files changed, 393 insertions(+), 26 deletions(-) create mode 100644 samples/RenderDemo/Pages/Transform3DPage.axaml create mode 100644 samples/RenderDemo/Pages/Transform3DPage.axaml.cs create mode 100644 samples/RenderDemo/ViewModels/Transform3DPageViewModel.cs create mode 100644 src/Avalonia.Visuals/Media/Transform3D.cs diff --git a/samples/ControlCatalog.Android/Resources/Resource.Designer.cs b/samples/ControlCatalog.Android/Resources/Resource.Designer.cs index dccc3f7159..b1ca548e2c 100644 --- a/samples/ControlCatalog.Android/Resources/Resource.Designer.cs +++ b/samples/ControlCatalog.Android/Resources/Resource.Designer.cs @@ -14,7 +14,7 @@ namespace ControlCatalog.Android { - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "12.1.99.62")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")] public partial class Resource { diff --git a/samples/RenderDemo/MainWindow.xaml b/samples/RenderDemo/MainWindow.xaml index a4c6299278..2b5bba928a 100644 --- a/samples/RenderDemo/MainWindow.xaml +++ b/samples/RenderDemo/MainWindow.xaml @@ -63,5 +63,8 @@ + + + diff --git a/samples/RenderDemo/Pages/Transform3DPage.axaml b/samples/RenderDemo/Pages/Transform3DPage.axaml new file mode 100644 index 0000000000..ebd838eb67 --- /dev/null +++ b/samples/RenderDemo/Pages/Transform3DPage.axaml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + X-Rotation: + + Y-Rotation: + + Z-Rotation: + + X: + + Y: + + Z: + + + + + + diff --git a/samples/RenderDemo/Pages/Transform3DPage.axaml.cs b/samples/RenderDemo/Pages/Transform3DPage.axaml.cs new file mode 100644 index 0000000000..5083189c4c --- /dev/null +++ b/samples/RenderDemo/Pages/Transform3DPage.axaml.cs @@ -0,0 +1,21 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using RenderDemo.ViewModels; + +namespace RenderDemo.Pages; + +public class Transform3DPage : UserControl +{ + public Transform3DPage() + { + InitializeComponent(); + this.DataContext = new Transform3DPageViewModel(); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } +} + diff --git a/samples/RenderDemo/ViewModels/Transform3DPageViewModel.cs b/samples/RenderDemo/ViewModels/Transform3DPageViewModel.cs new file mode 100644 index 0000000000..7e7849bd01 --- /dev/null +++ b/samples/RenderDemo/ViewModels/Transform3DPageViewModel.cs @@ -0,0 +1,61 @@ +using System; +using MiniMvvm; +using Avalonia.Animation; + +namespace RenderDemo.ViewModels +{ + public class Transform3DPageViewModel : ViewModelBase + { + private double _roationX = 0; + private double _rotationY = 0; + private double _rotationZ = 0; + + private double _x = 0; + private double _y = 0; + private double _z = 0; + + private double _depth = 200; + + public double RoationX + { + get => _roationX; + set => RaiseAndSetIfChanged(ref _roationX, value); + } + + public double RotationY + { + get => _rotationY; + set => RaiseAndSetIfChanged(ref _rotationY, value); + } + + public double RotationZ + { + get => _rotationZ; + set => RaiseAndSetIfChanged(ref _rotationZ, value); + } + + public double Depth + { + get => _depth; + set => RaiseAndSetIfChanged(ref _depth, value); + } + + public double X + { + get => _x; + set => RaiseAndSetIfChanged(ref _x, value); + } + + public double Y + { + get => _y; + set => RaiseAndSetIfChanged(ref _y, value); + } + + public double Z + { + get => _z; + set => RaiseAndSetIfChanged(ref _z, value); + } + } +} diff --git a/src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs b/src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs index 87fd47df25..83db67fcee 100644 --- a/src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs +++ b/src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs @@ -14,7 +14,7 @@ namespace Avalonia.AndroidTestApplication { - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "12.1.99.62")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")] public partial class Resource { diff --git a/src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs index 34ec8ac503..a98f2ec65e 100644 --- a/src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs +++ b/src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs @@ -43,6 +43,7 @@ namespace Avalonia.Animation.Animators normalTransform.Children.Add(new SkewTransform()); normalTransform.Children.Add(new RotateTransform()); normalTransform.Children.Add(new TranslateTransform()); + normalTransform.Children.Add(new Transform3D()); ctrl.RenderTransform = normalTransform; } diff --git a/src/Avalonia.Visuals/Matrix.cs b/src/Avalonia.Visuals/Matrix.cs index 9dd5bced86..e949d728d0 100644 --- a/src/Avalonia.Visuals/Matrix.cs +++ b/src/Avalonia.Visuals/Matrix.cs @@ -14,7 +14,7 @@ namespace Avalonia /// 2nd row | skrewX | scaleY | persY | /// 3rd row | transX | transY | persZ | /// - /// Note: Skia.SkMatrix uses a transposed layout (where for example skrewX/skrewY and persX/transX are swapped). + /// Note: Skia.SkMatrix uses a transposed layout (where for example skrewX/skrewY and perspp0/tranX are swapped). /// #if !BUILDTASK public @@ -30,8 +30,7 @@ namespace Avalonia private readonly double _m31; private readonly double _m32; private readonly double _m33; - - + /// /// Initializes a new instance of the struct (equivalent to a 2x3 Matrix without perspective). /// @@ -159,12 +158,15 @@ namespace Avalonia public static Matrix operator *(Matrix value1, Matrix value2) { return new Matrix( - (value1.M11 * value2.M11) + (value1.M12 * value2.M21), - (value1.M11 * value2.M12) + (value1.M12 * value2.M22), - (value1.M21 * value2.M11) + (value1.M22 * value2.M21), - (value1.M21 * value2.M12) + (value1.M22 * value2.M22), - (value1._m31 * value2.M11) + (value1._m32 * value2.M21) + value2._m31, - (value1._m31 * value2.M12) + (value1._m32 * value2.M22) + value2._m32); //TODO: include perspective + (value1.M11 * value2.M11) + (value1.M12 * value2.M21) + (value1.M13 * value2.M31), + (value1.M11 * value2.M12) + (value1.M12 * value2.M22) + (value1.M13 * value2.M32), + (value1.M11 * value2.M13) + (value1.M12 * value2.M23) + (value1.M13 * value2.M33), + (value1.M21 * value2.M11) + (value1.M22 * value2.M21) + (value1.M23 * value2.M31), + (value1.M21 * value2.M12) + (value1.M22 * value2.M22) + (value1.M23 * value2.M32), + (value1.M21 * value2.M13) + (value1.M22 * value2.M23) + (value1.M23 * value2.M33), + (value1.M31 * value2.M11) + (value1.M32 * value2.M21) + (value1.M33 * value2.M31), + (value1.M31 * value2.M12) + (value1.M32 * value2.M22) + (value1.M33 * value2.M32), + (value1.M31 * value2.M13) + (value1.M32 * value2.M23) + (value1.M33 * value2.M33)); } /// @@ -288,7 +290,7 @@ namespace Avalonia } /// - /// Prpends another matrix as pre-multiplication operation. + /// Prepends another matrix as pre-multiplication operation. /// Equivalent to value * this; /// /// A matrix. @@ -359,7 +361,7 @@ namespace Avalonia { // ReSharper disable CompareOfFloatsByEqualityOperator - return _m13 != 0 || _m23 != 0 || _m33 != 1; + return _m31 != 0 || _m32 != 0 || _m33 != 1; // ReSharper restore CompareOfFloatsByEqualityOperator } @@ -399,21 +401,27 @@ namespace Avalonia { double d = GetDeterminant(); - if (MathUtilities.IsZero(d)) //TODO: decide if special handling is required for perspective + if (MathUtilities.IsZero(d)) { inverted = default; return false; } + var invdet = 1 / d; + inverted = new Matrix( - _m22 / d, - -_m12 / d, - -_m21 / d, - _m11 / d, - ((_m21 * _m32) - (_m22 * _m31)) / d, - ((_m12 * _m31) - (_m11 * _m32)) / d); - + (_m22 * _m33 - _m32 * _m23) * invdet, + (_m13 * _m31 - _m12 * _m33) * invdet, + (_m12 * _m23 - _m13 * _m22) * invdet, + (_m23 * _m31 - _m21 * _m33) * invdet, + (_m11 * _m33 - _m13 * _m31) * invdet, + (_m21 * _m13 - _m11 * _m23) * invdet, + (_m21 * _m32 - _m31 * _m22) * invdet, + (_m21 * _m12 - _m11 * _m32) * invdet, + (_m11 * _m22 - _m21 * _m12) * invdet + ); + return true; } @@ -424,7 +432,7 @@ namespace Avalonia /// The inverted matrix. public Matrix Invert() { - if (!TryInvert(out Matrix inverted)) + if (!TryInvert(out var inverted)) { throw new InvalidOperationException("Transform is not invertible."); } @@ -467,7 +475,7 @@ namespace Avalonia /// /// Matrix to decompose. /// Decomposed matrix. - /// The status of the operation. + /// The status of the operation. public static bool TryDecomposeTransform(Matrix matrix, out Decomposed decomposed) { decomposed = default; diff --git a/src/Avalonia.Visuals/Media/Transform3D.cs b/src/Avalonia.Visuals/Media/Transform3D.cs new file mode 100644 index 0000000000..e063f76556 --- /dev/null +++ b/src/Avalonia.Visuals/Media/Transform3D.cs @@ -0,0 +1,192 @@ +using System; +using System.Numerics; + +namespace Avalonia.Media; + +public class Transform3D : Transform +{ + /// + /// Defines the property. + /// + public static readonly StyledProperty s_rotationXProperty = + AvaloniaProperty.Register(nameof(RotationX)); + + /// + /// Defines the property. + /// + public static readonly StyledProperty s_rotationYProperty = + AvaloniaProperty.Register(nameof(RotationY)); + + /// + /// Defines the property. + /// + public static readonly StyledProperty s_rotationZProperty = + AvaloniaProperty.Register(nameof(RotationZ)); + + /// + /// Defines the property. + /// + public static readonly StyledProperty s_depthProperty = + AvaloniaProperty.Register(nameof(Depth)); + + /// + /// Defines the property. + /// + public static readonly StyledProperty s_centerXProperty = + AvaloniaProperty.Register(nameof(CenterX)); + + /// + /// Defines the property. + /// + public static readonly StyledProperty s_centerYProperty = + AvaloniaProperty.Register(nameof(CenterY)); + + /// + /// Defines the property. + /// + public static readonly StyledProperty s_xProperty = + AvaloniaProperty.Register(nameof(X)); + + + /// + /// Defines the property. + /// + public static readonly StyledProperty s_yProperty = + AvaloniaProperty.Register(nameof(Y)); + + + /// + /// Defines the property. + /// + public static readonly StyledProperty s_zProperty = + AvaloniaProperty.Register(nameof(Z)); + + + /// + /// Initializes a new instance of the class. + /// + public Transform3D() + { + this.GetObservable(s_rotationXProperty).Subscribe(_ => RaiseChanged()); + this.GetObservable(s_rotationYProperty).Subscribe(_ => RaiseChanged()); + this.GetObservable(s_rotationZProperty).Subscribe(_ => RaiseChanged()); + this.GetObservable(s_depthProperty).Subscribe(_ => RaiseChanged()); + this.GetObservable(s_centerXProperty).Subscribe(_ => RaiseChanged()); + this.GetObservable(s_centerYProperty).Subscribe(_ => RaiseChanged()); + this.GetObservable(s_xProperty).Subscribe(_ => RaiseChanged()); + this.GetObservable(s_yProperty).Subscribe(_ => RaiseChanged()); + this.GetObservable(s_zProperty).Subscribe(_ => RaiseChanged()); + } + + /// + /// Initializes a new instance of the class. + /// + /// The skew angle of X-axis, in degrees. + /// The skew angle of Y-axis, in degrees. + /// + public Transform3D( + double rotationX, + double rotationY, + double rotationZ, + double depth, + double centerX, + double centerY) : this() + { + RotationX = rotationX; + RotationY = rotationY; + RotationZ = rotationZ; + Depth = depth; + CenterX = centerX; + CenterY = centerY; + } + + /// + /// Gets or sets the X property. + /// + public double RotationX + { + get => GetValue(s_rotationXProperty); + set => SetValue(s_rotationXProperty, value); + } + + /// + /// Gets or sets the Y property. + /// + public double RotationY + { + get => GetValue(s_rotationYProperty); + set => SetValue(s_rotationYProperty, value); + } + + public double RotationZ + { + get => GetValue(s_rotationZProperty); + set => SetValue(s_rotationZProperty, value); + } + + public double Depth + { + get => GetValue(s_depthProperty); + set => SetValue(s_depthProperty, value); + } + + public double CenterX + { + get => GetValue(s_centerXProperty); + set => SetValue(s_centerXProperty, value); + } + + public double CenterY + { + get => GetValue(s_centerYProperty); + set => SetValue(s_centerYProperty, value); + } + + public double X + { + get => GetValue(s_xProperty); + set => SetValue(s_xProperty, value); + } + + public double Y + { + get => GetValue(s_yProperty); + set => SetValue(s_yProperty, value); + } + + public double Z + { + get => GetValue(s_zProperty); + set => SetValue(s_zProperty, value); + } + + /// + /// Gets the transform's . + /// + public override Matrix Value + { + get + { + var matrix44 = Matrix4x4.Identity; + + matrix44 *= Matrix4x4.CreateTranslation((float)X, (float)Y, (float)Z); + + matrix44 *= Matrix4x4.CreateRotationX((float)Matrix.ToRadians(RotationX)); + matrix44 *= Matrix4x4.CreateRotationY((float)Matrix.ToRadians(RotationY)); + matrix44 *= Matrix4x4.CreateRotationZ((float)Matrix.ToRadians(RotationZ)); + + var matrix = new Matrix( + matrix44.M11, + matrix44.M12, + matrix44.M14, + matrix44.M21, + matrix44.M22, + matrix44.M24, + matrix44.M41, + matrix44.M42, + matrix44.M44); + + return matrix; + } + } +} diff --git a/src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs b/src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs index 75b4231640..c0afc8bd9c 100644 --- a/src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs +++ b/src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs @@ -103,9 +103,9 @@ namespace Avalonia.Skia SkewY = (float)m.M12, ScaleY = (float)m.M22, TransY = (float)m.M32, - Persp0 = 0, - Persp1 = 0, - Persp2 = 1 + Persp0 = (float)m.M13, + Persp1 = (float)m.M23, + Persp2 = (float)m.M33 }; return sm; From 0a831b978483699b1dfa75ca177307aebef98e6e Mon Sep 17 00:00:00 2001 From: Jan-Peter Zurek Date: Wed, 19 Jan 2022 11:16:33 +0100 Subject: [PATCH 014/416] Refactor names. --- .../RenderDemo/Pages/Transform3DPage.axaml | 7 +- .../ViewModels/Transform3DPageViewModel.cs | 8 +- src/Avalonia.Visuals/Media/Transform3D.cs | 135 ++++++++---------- 3 files changed, 64 insertions(+), 86 deletions(-) diff --git a/samples/RenderDemo/Pages/Transform3DPage.axaml b/samples/RenderDemo/Pages/Transform3DPage.axaml index ebd838eb67..e66c498bbc 100644 --- a/samples/RenderDemo/Pages/Transform3DPage.axaml +++ b/samples/RenderDemo/Pages/Transform3DPage.axaml @@ -54,12 +54,11 @@ + CenterX="{Binding X}" + CenterY="{Binding Y}" + CenterZ="{Binding Z}" /> X-Rotation: diff --git a/samples/RenderDemo/ViewModels/Transform3DPageViewModel.cs b/samples/RenderDemo/ViewModels/Transform3DPageViewModel.cs index 7e7849bd01..d28705fc0d 100644 --- a/samples/RenderDemo/ViewModels/Transform3DPageViewModel.cs +++ b/samples/RenderDemo/ViewModels/Transform3DPageViewModel.cs @@ -6,7 +6,7 @@ namespace RenderDemo.ViewModels { public class Transform3DPageViewModel : ViewModelBase { - private double _roationX = 0; + private double _rotationX = 0; private double _rotationY = 0; private double _rotationZ = 0; @@ -16,10 +16,10 @@ namespace RenderDemo.ViewModels private double _depth = 200; - public double RoationX + public double RotationX { - get => _roationX; - set => RaiseAndSetIfChanged(ref _roationX, value); + get => _rotationX; + set => RaiseAndSetIfChanged(ref _rotationX, value); } public double RotationY diff --git a/src/Avalonia.Visuals/Media/Transform3D.cs b/src/Avalonia.Visuals/Media/Transform3D.cs index e063f76556..cfbde3204a 100644 --- a/src/Avalonia.Visuals/Media/Transform3D.cs +++ b/src/Avalonia.Visuals/Media/Transform3D.cs @@ -8,58 +8,41 @@ public class Transform3D : Transform /// /// Defines the property. /// - public static readonly StyledProperty s_rotationXProperty = + public static readonly StyledProperty RotationXProperty = AvaloniaProperty.Register(nameof(RotationX)); /// /// Defines the property. /// - public static readonly StyledProperty s_rotationYProperty = + public static readonly StyledProperty RotationYProperty = AvaloniaProperty.Register(nameof(RotationY)); /// /// Defines the property. /// - public static readonly StyledProperty s_rotationZProperty = + public static readonly StyledProperty RotationZProperty = AvaloniaProperty.Register(nameof(RotationZ)); - /// - /// Defines the property. - /// - public static readonly StyledProperty s_depthProperty = - AvaloniaProperty.Register(nameof(Depth)); /// /// Defines the property. /// - public static readonly StyledProperty s_centerXProperty = + public static readonly StyledProperty CenterXProperty = AvaloniaProperty.Register(nameof(CenterX)); - - /// - /// Defines the property. - /// - public static readonly StyledProperty s_centerYProperty = - AvaloniaProperty.Register(nameof(CenterY)); - - /// - /// Defines the property. - /// - public static readonly StyledProperty s_xProperty = - AvaloniaProperty.Register(nameof(X)); /// /// Defines the property. /// - public static readonly StyledProperty s_yProperty = - AvaloniaProperty.Register(nameof(Y)); + public static readonly StyledProperty CenterYProperty = + AvaloniaProperty.Register(nameof(CenterY)); /// - /// Defines the property. + /// Defines the property. /// - public static readonly StyledProperty s_zProperty = - AvaloniaProperty.Register(nameof(Z)); + public static readonly StyledProperty CenterZProperty = + AvaloniaProperty.Register(nameof(CenterZ)); /// @@ -67,97 +50,93 @@ public class Transform3D : Transform /// public Transform3D() { - this.GetObservable(s_rotationXProperty).Subscribe(_ => RaiseChanged()); - this.GetObservable(s_rotationYProperty).Subscribe(_ => RaiseChanged()); - this.GetObservable(s_rotationZProperty).Subscribe(_ => RaiseChanged()); - this.GetObservable(s_depthProperty).Subscribe(_ => RaiseChanged()); - this.GetObservable(s_centerXProperty).Subscribe(_ => RaiseChanged()); - this.GetObservable(s_centerYProperty).Subscribe(_ => RaiseChanged()); - this.GetObservable(s_xProperty).Subscribe(_ => RaiseChanged()); - this.GetObservable(s_yProperty).Subscribe(_ => RaiseChanged()); - this.GetObservable(s_zProperty).Subscribe(_ => RaiseChanged()); + this.GetObservable(RotationXProperty).Subscribe(_ => RaiseChanged()); + this.GetObservable(RotationYProperty).Subscribe(_ => RaiseChanged()); + this.GetObservable(RotationZProperty).Subscribe(_ => RaiseChanged()); + this.GetObservable(CenterXProperty).Subscribe(_ => RaiseChanged()); + this.GetObservable(CenterYProperty).Subscribe(_ => RaiseChanged()); + this.GetObservable(CenterXProperty).Subscribe(_ => RaiseChanged()); + this.GetObservable(CenterYProperty).Subscribe(_ => RaiseChanged()); + this.GetObservable(CenterZProperty).Subscribe(_ => RaiseChanged()); } /// /// Initializes a new instance of the class. /// - /// The skew angle of X-axis, in degrees. - /// The skew angle of Y-axis, in degrees. - /// + /// The rotation around the X-Axis + /// The rotation around the Y-Axis + /// The rotation around the Z-Axis + /// The origin of the X-Axis + /// The origin of the Y-Axis + /// The origin of the Z-Axis public Transform3D( double rotationX, double rotationY, double rotationZ, - double depth, - double centerX, - double centerY) : this() + double originCenterX, + double originCenterY, + double originCenterZ) : this() { RotationX = rotationX; RotationY = rotationY; RotationZ = rotationZ; - Depth = depth; - CenterX = centerX; - CenterY = centerY; + CenterX = originCenterX; + CenterY = originCenterY; + CenterZ = originCenterZ; } /// - /// Gets or sets the X property. + /// Sets the rotation around the X-Axis /// public double RotationX { - get => GetValue(s_rotationXProperty); - set => SetValue(s_rotationXProperty, value); + get => GetValue(RotationXProperty); + set => SetValue(RotationXProperty, value); } /// - /// Gets or sets the Y property. + /// Sets the rotation around the Y-Axis /// public double RotationY { - get => GetValue(s_rotationYProperty); - set => SetValue(s_rotationYProperty, value); + get => GetValue(RotationYProperty); + set => SetValue(RotationYProperty, value); } + /// + /// Sets the rotation around the Z-Axis + /// public double RotationZ { - get => GetValue(s_rotationZProperty); - set => SetValue(s_rotationZProperty, value); - } - - public double Depth - { - get => GetValue(s_depthProperty); - set => SetValue(s_depthProperty, value); + get => GetValue(RotationZProperty); + set => SetValue(RotationZProperty, value); } + /// + /// Moves the origin of the X-Axis + /// public double CenterX { - get => GetValue(s_centerXProperty); - set => SetValue(s_centerXProperty, value); + get => GetValue(CenterXProperty); + set => SetValue(CenterXProperty, value); } + /// + /// Moves the origin of the Y-Axis + /// public double CenterY { - get => GetValue(s_centerYProperty); - set => SetValue(s_centerYProperty, value); + get => GetValue(CenterYProperty); + set => SetValue(CenterYProperty, value); } - public double X - { - get => GetValue(s_xProperty); - set => SetValue(s_xProperty, value); - } - - public double Y - { - get => GetValue(s_yProperty); - set => SetValue(s_yProperty, value); - } - - public double Z + /// + /// Moves the origin of the Z-Axis + /// + public double CenterZ { - get => GetValue(s_zProperty); - set => SetValue(s_zProperty, value); + get => GetValue(CenterZProperty); + set => SetValue(CenterZProperty, value); } /// @@ -169,7 +148,7 @@ public class Transform3D : Transform { var matrix44 = Matrix4x4.Identity; - matrix44 *= Matrix4x4.CreateTranslation((float)X, (float)Y, (float)Z); + matrix44 *= Matrix4x4.CreateTranslation(-(float)CenterX, -(float)CenterY, -(float)CenterZ); matrix44 *= Matrix4x4.CreateRotationX((float)Matrix.ToRadians(RotationX)); matrix44 *= Matrix4x4.CreateRotationY((float)Matrix.ToRadians(RotationY)); From 9a01f13114f77a889d68af2e06fbfebd0aa87882 Mon Sep 17 00:00:00 2001 From: Jan-Peter Zurek Date: Wed, 19 Jan 2022 13:39:57 +0100 Subject: [PATCH 015/416] Fix order of transforms. --- samples/RenderDemo/Pages/Transform3DPage.axaml | 2 +- src/Avalonia.Visuals/Media/Transform3D.cs | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/samples/RenderDemo/Pages/Transform3DPage.axaml b/samples/RenderDemo/Pages/Transform3DPage.axaml index e66c498bbc..0bf2279dd3 100644 --- a/samples/RenderDemo/Pages/Transform3DPage.axaml +++ b/samples/RenderDemo/Pages/Transform3DPage.axaml @@ -2,7 +2,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" + mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="650" x:Class="RenderDemo.Pages.Transform3DPage"> diff --git a/src/Avalonia.Visuals/Media/Transform3D.cs b/src/Avalonia.Visuals/Media/Transform3D.cs index cfbde3204a..cbc1fc958d 100644 --- a/src/Avalonia.Visuals/Media/Transform3D.cs +++ b/src/Avalonia.Visuals/Media/Transform3D.cs @@ -154,6 +154,13 @@ public class Transform3D : Transform matrix44 *= Matrix4x4.CreateRotationY((float)Matrix.ToRadians(RotationY)); matrix44 *= Matrix4x4.CreateRotationZ((float)Matrix.ToRadians(RotationZ)); + matrix44 *= Matrix4x4.CreateTranslation((float)CenterX, (float)CenterY, (float)CenterZ); + + var perspectiveMatrix = Matrix4x4.Identity; + perspectiveMatrix.M34 = -1 / (float)50; + + matrix44 *= perspectiveMatrix; + var matrix = new Matrix( matrix44.M11, matrix44.M12, From 07f45cf1d724535d483f2c9b8acdfe6d393630cf Mon Sep 17 00:00:00 2001 From: Jan-Peter Zurek Date: Wed, 19 Jan 2022 15:15:56 +0100 Subject: [PATCH 016/416] Add animation. Add controls instead of Vector to prof usability. --- .../RenderDemo/Pages/Transform3DPage.axaml | 252 +++++++++++++----- 1 file changed, 179 insertions(+), 73 deletions(-) diff --git a/samples/RenderDemo/Pages/Transform3DPage.axaml b/samples/RenderDemo/Pages/Transform3DPage.axaml index 0bf2279dd3..438611976a 100644 --- a/samples/RenderDemo/Pages/Transform3DPage.axaml +++ b/samples/RenderDemo/Pages/Transform3DPage.axaml @@ -2,79 +2,185 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="650" + mc:Ignorable="d" d:DesignWidth="200" d:DesignHeight="200" x:Class="RenderDemo.Pages.Transform3DPage"> - - - - - - - - - - - - - - - - - - - - X-Rotation: - - Y-Rotation: - - Z-Rotation: - - X: - - Y: - - Z: - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - From 9fdb48d0c4328ea6332851ed0758bd24ca692ee8 Mon Sep 17 00:00:00 2001 From: Jan-Peter Zurek Date: Wed, 19 Jan 2022 17:29:31 +0100 Subject: [PATCH 017/416] Rename transform. Re-add depth. Add slider to preview for showing effect of depth. --- .../RenderDemo/Pages/Transform3DPage.axaml | 89 ++++---- .../ViewModels/Transform3DPageViewModel.cs | 44 ---- .../Animation/Animators/TransformAnimator.cs | 2 +- .../Media/Rotate3DTransform.cs | 195 ++++++++++++++++++ src/Avalonia.Visuals/Media/Transform3D.cs | 178 ---------------- 5 files changed, 235 insertions(+), 273 deletions(-) create mode 100644 src/Avalonia.Visuals/Media/Rotate3DTransform.cs delete mode 100644 src/Avalonia.Visuals/Media/Transform3D.cs diff --git a/samples/RenderDemo/Pages/Transform3DPage.axaml b/samples/RenderDemo/Pages/Transform3DPage.axaml index 438611976a..1093399c0c 100644 --- a/samples/RenderDemo/Pages/Transform3DPage.axaml +++ b/samples/RenderDemo/Pages/Transform3DPage.axaml @@ -2,7 +2,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - mc:Ignorable="d" d:DesignWidth="200" d:DesignHeight="200" + mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="400" x:Class="RenderDemo.Pages.Transform3DPage"> @@ -16,10 +16,10 @@ - + @@ -142,45 +142,34 @@ - + - + - + - + + + Depth: + + + diff --git a/samples/RenderDemo/ViewModels/Transform3DPageViewModel.cs b/samples/RenderDemo/ViewModels/Transform3DPageViewModel.cs index d28705fc0d..0c1caac8ea 100644 --- a/samples/RenderDemo/ViewModels/Transform3DPageViewModel.cs +++ b/samples/RenderDemo/ViewModels/Transform3DPageViewModel.cs @@ -6,56 +6,12 @@ namespace RenderDemo.ViewModels { public class Transform3DPageViewModel : ViewModelBase { - private double _rotationX = 0; - private double _rotationY = 0; - private double _rotationZ = 0; - - private double _x = 0; - private double _y = 0; - private double _z = 0; - private double _depth = 200; - public double RotationX - { - get => _rotationX; - set => RaiseAndSetIfChanged(ref _rotationX, value); - } - - public double RotationY - { - get => _rotationY; - set => RaiseAndSetIfChanged(ref _rotationY, value); - } - - public double RotationZ - { - get => _rotationZ; - set => RaiseAndSetIfChanged(ref _rotationZ, value); - } - public double Depth { get => _depth; set => RaiseAndSetIfChanged(ref _depth, value); } - - public double X - { - get => _x; - set => RaiseAndSetIfChanged(ref _x, value); - } - - public double Y - { - get => _y; - set => RaiseAndSetIfChanged(ref _y, value); - } - - public double Z - { - get => _z; - set => RaiseAndSetIfChanged(ref _z, value); - } } } diff --git a/src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs b/src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs index a98f2ec65e..e12ca722f9 100644 --- a/src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs +++ b/src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs @@ -43,7 +43,7 @@ namespace Avalonia.Animation.Animators normalTransform.Children.Add(new SkewTransform()); normalTransform.Children.Add(new RotateTransform()); normalTransform.Children.Add(new TranslateTransform()); - normalTransform.Children.Add(new Transform3D()); + normalTransform.Children.Add(new Rotate3DTransform()); ctrl.RenderTransform = normalTransform; } diff --git a/src/Avalonia.Visuals/Media/Rotate3DTransform.cs b/src/Avalonia.Visuals/Media/Rotate3DTransform.cs new file mode 100644 index 0000000000..9a81ae306c --- /dev/null +++ b/src/Avalonia.Visuals/Media/Rotate3DTransform.cs @@ -0,0 +1,195 @@ +using System; +using System.Numerics; + +namespace Avalonia.Media; + +/// +/// Non-Affine 3D transformation for rotating an visual around a definable axis +/// +public class Rotate3DTransform : Transform +{ + /// + /// Defines the property. + /// + public static readonly StyledProperty RotationXProperty = + AvaloniaProperty.Register(nameof(RotationX)); + + /// + /// Defines the property. + /// + public static readonly StyledProperty RotationYProperty = + AvaloniaProperty.Register(nameof(RotationY)); + + /// + /// Defines the property. + /// + public static readonly StyledProperty RotationZProperty = + AvaloniaProperty.Register(nameof(RotationZ)); + + + /// + /// Defines the property. + /// + public static readonly StyledProperty CenterXProperty = + AvaloniaProperty.Register(nameof(CenterX)); + + + /// + /// Defines the property. + /// + public static readonly StyledProperty CenterYProperty = + AvaloniaProperty.Register(nameof(CenterY)); + + + /// + /// Defines the property. + /// + public static readonly StyledProperty CenterZProperty = + AvaloniaProperty.Register(nameof(CenterZ)); + + /// + /// Defines the property. + /// + public static readonly StyledProperty DepthProperty = + AvaloniaProperty.Register(nameof(Depth)); + + /// + /// Initializes a new instance of the class. + /// + public Rotate3DTransform() + { + this.GetObservable(RotationXProperty).Subscribe(_ => RaiseChanged()); + this.GetObservable(RotationYProperty).Subscribe(_ => RaiseChanged()); + this.GetObservable(RotationZProperty).Subscribe(_ => RaiseChanged()); + this.GetObservable(CenterXProperty).Subscribe(_ => RaiseChanged()); + this.GetObservable(CenterYProperty).Subscribe(_ => RaiseChanged()); + this.GetObservable(CenterZProperty).Subscribe(_ => RaiseChanged()); + } + + /// + /// Initializes a new instance of the class. + /// + /// The rotation around the X-Axis + /// The rotation around the Y-Axis + /// The rotation around the Z-Axis + /// The origin of the X-Axis + /// The origin of the Y-Axis + /// The origin of the Z-Axis + public Rotate3DTransform( + double rotationX, + double rotationY, + double rotationZ, + double centerX, + double centerY, + double centerZ) : this() + { + RotationX = rotationX; + RotationY = rotationY; + RotationZ = rotationZ; + CenterX = centerX; + CenterY = centerY; + CenterZ = centerZ; + } + + /// + /// Sets the rotation around the X-Axis + /// + public double RotationX + { + get => GetValue(RotationXProperty); + set => SetValue(RotationXProperty, value); + } + + /// + /// Sets the rotation around the Y-Axis + /// + public double RotationY + { + get => GetValue(RotationYProperty); + set => SetValue(RotationYProperty, value); + } + + /// + /// Sets the rotation around the Z-Axis + /// + public double RotationZ + { + get => GetValue(RotationZProperty); + set => SetValue(RotationZProperty, value); + } + + /// + /// Moves the origin of the X-Axis + /// + public double CenterX + { + get => GetValue(CenterXProperty); + set => SetValue(CenterXProperty, value); + } + + /// + /// Moves the origin of the Y-Axis + /// + public double CenterY + { + get => GetValue(CenterYProperty); + set => SetValue(CenterYProperty, value); + } + + /// + /// Moves the origin of the Z-Axis + /// + public double CenterZ + { + get => GetValue(CenterZProperty); + set => SetValue(CenterZProperty, value); + } + + /// + /// Affects the depth of the rotation effect + /// + public double Depth + { + get => GetValue(DepthProperty); + set => SetValue(DepthProperty, value); + } + + /// + /// Gets the transform's . + /// + public override Matrix Value + { + get + { + var matrix44 = Matrix4x4.Identity; + + matrix44 *= Matrix4x4.CreateTranslation(-(float)CenterX, -(float)CenterY, -(float)CenterZ); + + matrix44 *= Matrix4x4.CreateRotationX((float)Matrix.ToRadians(RotationX)); + matrix44 *= Matrix4x4.CreateRotationY((float)Matrix.ToRadians(RotationY)); + matrix44 *= Matrix4x4.CreateRotationZ((float)Matrix.ToRadians(RotationZ)); + + matrix44 *= Matrix4x4.CreateTranslation((float)CenterX, (float)CenterY, (float)CenterZ); + + if (Depth != 0) + { + var perspectiveMatrix = Matrix4x4.Identity; + perspectiveMatrix.M34 = -1 / (float)Depth; + matrix44 *= perspectiveMatrix; + } + + var matrix = new Matrix( + matrix44.M11, + matrix44.M12, + matrix44.M14, + matrix44.M21, + matrix44.M22, + matrix44.M24, + matrix44.M41, + matrix44.M42, + matrix44.M44); + + return matrix; + } + } +} diff --git a/src/Avalonia.Visuals/Media/Transform3D.cs b/src/Avalonia.Visuals/Media/Transform3D.cs deleted file mode 100644 index cbc1fc958d..0000000000 --- a/src/Avalonia.Visuals/Media/Transform3D.cs +++ /dev/null @@ -1,178 +0,0 @@ -using System; -using System.Numerics; - -namespace Avalonia.Media; - -public class Transform3D : Transform -{ - /// - /// Defines the property. - /// - public static readonly StyledProperty RotationXProperty = - AvaloniaProperty.Register(nameof(RotationX)); - - /// - /// Defines the property. - /// - public static readonly StyledProperty RotationYProperty = - AvaloniaProperty.Register(nameof(RotationY)); - - /// - /// Defines the property. - /// - public static readonly StyledProperty RotationZProperty = - AvaloniaProperty.Register(nameof(RotationZ)); - - - /// - /// Defines the property. - /// - public static readonly StyledProperty CenterXProperty = - AvaloniaProperty.Register(nameof(CenterX)); - - - /// - /// Defines the property. - /// - public static readonly StyledProperty CenterYProperty = - AvaloniaProperty.Register(nameof(CenterY)); - - - /// - /// Defines the property. - /// - public static readonly StyledProperty CenterZProperty = - AvaloniaProperty.Register(nameof(CenterZ)); - - - /// - /// Initializes a new instance of the class. - /// - public Transform3D() - { - this.GetObservable(RotationXProperty).Subscribe(_ => RaiseChanged()); - this.GetObservable(RotationYProperty).Subscribe(_ => RaiseChanged()); - this.GetObservable(RotationZProperty).Subscribe(_ => RaiseChanged()); - this.GetObservable(CenterXProperty).Subscribe(_ => RaiseChanged()); - this.GetObservable(CenterYProperty).Subscribe(_ => RaiseChanged()); - this.GetObservable(CenterXProperty).Subscribe(_ => RaiseChanged()); - this.GetObservable(CenterYProperty).Subscribe(_ => RaiseChanged()); - this.GetObservable(CenterZProperty).Subscribe(_ => RaiseChanged()); - } - - /// - /// Initializes a new instance of the class. - /// - /// The rotation around the X-Axis - /// The rotation around the Y-Axis - /// The rotation around the Z-Axis - /// The origin of the X-Axis - /// The origin of the Y-Axis - /// The origin of the Z-Axis - public Transform3D( - double rotationX, - double rotationY, - double rotationZ, - double originCenterX, - double originCenterY, - double originCenterZ) : this() - { - RotationX = rotationX; - RotationY = rotationY; - RotationZ = rotationZ; - CenterX = originCenterX; - CenterY = originCenterY; - CenterZ = originCenterZ; - } - - /// - /// Sets the rotation around the X-Axis - /// - public double RotationX - { - get => GetValue(RotationXProperty); - set => SetValue(RotationXProperty, value); - } - - /// - /// Sets the rotation around the Y-Axis - /// - public double RotationY - { - get => GetValue(RotationYProperty); - set => SetValue(RotationYProperty, value); - } - - /// - /// Sets the rotation around the Z-Axis - /// - public double RotationZ - { - get => GetValue(RotationZProperty); - set => SetValue(RotationZProperty, value); - } - - /// - /// Moves the origin of the X-Axis - /// - public double CenterX - { - get => GetValue(CenterXProperty); - set => SetValue(CenterXProperty, value); - } - - /// - /// Moves the origin of the Y-Axis - /// - public double CenterY - { - get => GetValue(CenterYProperty); - set => SetValue(CenterYProperty, value); - } - - /// - /// Moves the origin of the Z-Axis - /// - public double CenterZ - { - get => GetValue(CenterZProperty); - set => SetValue(CenterZProperty, value); - } - - /// - /// Gets the transform's . - /// - public override Matrix Value - { - get - { - var matrix44 = Matrix4x4.Identity; - - matrix44 *= Matrix4x4.CreateTranslation(-(float)CenterX, -(float)CenterY, -(float)CenterZ); - - matrix44 *= Matrix4x4.CreateRotationX((float)Matrix.ToRadians(RotationX)); - matrix44 *= Matrix4x4.CreateRotationY((float)Matrix.ToRadians(RotationY)); - matrix44 *= Matrix4x4.CreateRotationZ((float)Matrix.ToRadians(RotationZ)); - - matrix44 *= Matrix4x4.CreateTranslation((float)CenterX, (float)CenterY, (float)CenterZ); - - var perspectiveMatrix = Matrix4x4.Identity; - perspectiveMatrix.M34 = -1 / (float)50; - - matrix44 *= perspectiveMatrix; - - var matrix = new Matrix( - matrix44.M11, - matrix44.M12, - matrix44.M14, - matrix44.M21, - matrix44.M22, - matrix44.M24, - matrix44.M41, - matrix44.M42, - matrix44.M44); - - return matrix; - } - } -} From 8c131ba100ac3890595216c4936feb54840a2688 Mon Sep 17 00:00:00 2001 From: Jan-Peter Zurek Date: Thu, 20 Jan 2022 06:13:00 +0100 Subject: [PATCH 018/416] Improve readability in RenderDemo, slow down animation. --- .../RenderDemo/Pages/Transform3DPage.axaml | 40 +++++++++++++------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/samples/RenderDemo/Pages/Transform3DPage.axaml b/samples/RenderDemo/Pages/Transform3DPage.axaml index 1093399c0c..8646dc6870 100644 --- a/samples/RenderDemo/Pages/Transform3DPage.axaml +++ b/samples/RenderDemo/Pages/Transform3DPage.axaml @@ -8,10 +8,15 @@ @@ -24,9 +29,16 @@ + + @@ -140,25 +156,25 @@ - + - + - + - + From e6d86fb898ce92569cb590bc2466d8ec7542627c Mon Sep 17 00:00:00 2001 From: Jan-Peter Zurek Date: Thu, 20 Jan 2022 06:54:00 +0100 Subject: [PATCH 019/416] Fix ContainsPerspective method. --- src/Avalonia.Visuals/Matrix.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Visuals/Matrix.cs b/src/Avalonia.Visuals/Matrix.cs index e949d728d0..4326dc3670 100644 --- a/src/Avalonia.Visuals/Matrix.cs +++ b/src/Avalonia.Visuals/Matrix.cs @@ -361,7 +361,7 @@ namespace Avalonia { // ReSharper disable CompareOfFloatsByEqualityOperator - return _m31 != 0 || _m32 != 0 || _m33 != 1; + return _m13 != 0 || _m23 != 0 || _m33 != 1; // ReSharper restore CompareOfFloatsByEqualityOperator } From bde9c078c56eb3f7864cac17a46315af783fa123 Mon Sep 17 00:00:00 2001 From: Jan-Peter Zurek Date: Thu, 20 Jan 2022 13:58:34 +0100 Subject: [PATCH 020/416] Add observable. Remove borders from sample to better visualize error. --- samples/RenderDemo/Pages/Transform3DPage.axaml | 8 ++++---- src/Avalonia.Visuals/Media/Rotate3DTransform.cs | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/samples/RenderDemo/Pages/Transform3DPage.axaml b/samples/RenderDemo/Pages/Transform3DPage.axaml index 8646dc6870..9f369dae98 100644 --- a/samples/RenderDemo/Pages/Transform3DPage.axaml +++ b/samples/RenderDemo/Pages/Transform3DPage.axaml @@ -162,24 +162,24 @@ Depth="{Binding Depth}"/> - + - + diff --git a/src/Avalonia.Visuals/Media/Rotate3DTransform.cs b/src/Avalonia.Visuals/Media/Rotate3DTransform.cs index 9a81ae306c..a3af6b8c66 100644 --- a/src/Avalonia.Visuals/Media/Rotate3DTransform.cs +++ b/src/Avalonia.Visuals/Media/Rotate3DTransform.cs @@ -64,6 +64,7 @@ public class Rotate3DTransform : Transform this.GetObservable(CenterXProperty).Subscribe(_ => RaiseChanged()); this.GetObservable(CenterYProperty).Subscribe(_ => RaiseChanged()); this.GetObservable(CenterZProperty).Subscribe(_ => RaiseChanged()); + this.GetObservable(DepthProperty).Subscribe(_ => RaiseChanged()); } /// From 9078ca7dc8b2512322e5a2b26e91c2ca8348f9e1 Mon Sep 17 00:00:00 2001 From: Jan-Peter Zurek Date: Wed, 26 Jan 2022 14:46:54 +0100 Subject: [PATCH 021/416] Fix Rotate3DTransform for DeferredRenderer. --- .../Avalonia.Build.Tasks.csproj | 1 + src/Avalonia.Visuals/Matrix.cs | 2 +- src/Avalonia.Visuals/Point.cs | 17 +++++++++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj index e864ea2007..7e4272f5d2 100644 --- a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj +++ b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj @@ -94,5 +94,6 @@ + diff --git a/src/Avalonia.Visuals/Matrix.cs b/src/Avalonia.Visuals/Matrix.cs index 4326dc3670..38d0508ffb 100644 --- a/src/Avalonia.Visuals/Matrix.cs +++ b/src/Avalonia.Visuals/Matrix.cs @@ -357,7 +357,7 @@ namespace Avalonia /// /// Determines if the current matrix contains perspective (non-affine) transforms (true) or only (affine) transforms that could be mapped into an 2x3 matrix (false). /// - private bool ContainsPerspective() + public bool ContainsPerspective() { // ReSharper disable CompareOfFloatsByEqualityOperator diff --git a/src/Avalonia.Visuals/Point.cs b/src/Avalonia.Visuals/Point.cs index 67e7d71fbc..473de8e501 100644 --- a/src/Avalonia.Visuals/Point.cs +++ b/src/Avalonia.Visuals/Point.cs @@ -1,5 +1,6 @@ using System; using System.Globalization; +using System.Numerics; #if !BUILDTASK using Avalonia.Animation.Animators; #endif @@ -244,6 +245,22 @@ namespace Avalonia /// The transformed point. public Point Transform(Matrix transform) { + if (transform.ContainsPerspective()) + { + var m44 = new Matrix4x4( + (float)transform.M11, (float)transform.M12, (float)transform.M13, 0, + (float)transform.M21, (float)transform.M22, (float)transform.M23, 0, + (float)transform.M31, (float)transform.M32, (float)transform.M33, 0, + 0, 0, 0, 1 + ); + + var vector = new Vector3((float)X, (float)Y, 1); + var transformedVector = Vector3.Transform(vector, m44); + var z = 1 / transformedVector.Z; + + return new Point(transformedVector.X * z, transformedVector.Y * z); + } + var x = X; var y = Y; var xadd = y * transform.M21 + transform.M31; From e8f2d3e3f3372fa226a1cb57b447974fe1da8f79 Mon Sep 17 00:00:00 2001 From: Jan-Peter Zurek Date: Wed, 26 Jan 2022 16:25:18 +0100 Subject: [PATCH 022/416] Add comments, remove todos, extend example. --- .../RenderDemo/Pages/Transform3DPage.axaml | 162 ++++++++++++------ .../ViewModels/Transform3DPageViewModel.cs | 44 +++++ src/Avalonia.Visuals/Matrix.cs | 50 +++++- .../Media/Rotate3DTransform.cs | 73 ++++---- src/Avalonia.Visuals/Point.cs | 29 +--- 5 files changed, 235 insertions(+), 123 deletions(-) diff --git a/samples/RenderDemo/Pages/Transform3DPage.axaml b/samples/RenderDemo/Pages/Transform3DPage.axaml index 9f369dae98..bf63b6d1c6 100644 --- a/samples/RenderDemo/Pages/Transform3DPage.axaml +++ b/samples/RenderDemo/Pages/Transform3DPage.axaml @@ -2,12 +2,12 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="400" + mc:Ignorable="d" d:DesignWidth="600" d:DesignHeight="700" x:Class="RenderDemo.Pages.Transform3DPage"> - + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/RenderDemo/ViewModels/Transform3DPageViewModel.cs b/samples/RenderDemo/ViewModels/Transform3DPageViewModel.cs index 2a6bdcb434..c8d1d40e3a 100644 --- a/samples/RenderDemo/ViewModels/Transform3DPageViewModel.cs +++ b/samples/RenderDemo/ViewModels/Transform3DPageViewModel.cs @@ -8,7 +8,6 @@ namespace RenderDemo.ViewModels { private double _depth = 200; - private double _depth2 = 200; private double _centerX = 0; private double _centerY = 0; private double _centerZ = 0; @@ -22,11 +21,6 @@ namespace RenderDemo.ViewModels set => RaiseAndSetIfChanged(ref _depth, value); } - public double Depth2 - { - get => _depth2; - set => RaiseAndSetIfChanged(ref _depth2, value); - } public double CenterX { get => _centerX; From 3e7f190ff9881fc0785e4698aca96099212e5b71 Mon Sep 17 00:00:00 2001 From: Jan-Peter Zurek Date: Thu, 27 Jan 2022 13:39:27 +0100 Subject: [PATCH 027/416] Add code review changes: Fix typos. Simplify code. Generalize code. Remove duplications. Improve performance. --- .../RenderDemo/Pages/Transform3DPage.axaml | 234 +++++++++--------- .../Resources/Resource.Designer.cs | 2 +- .../Transitions/Rotate3DTransition.cs | 124 ++++------ src/Avalonia.Visuals/Matrix.cs | 12 +- .../Media/Rotate3DTransform.cs | 2 +- src/Avalonia.Visuals/Point.cs | 7 +- 6 files changed, 163 insertions(+), 218 deletions(-) diff --git a/samples/RenderDemo/Pages/Transform3DPage.axaml b/samples/RenderDemo/Pages/Transform3DPage.axaml index fa03c7ba49..30ed35bbac 100644 --- a/samples/RenderDemo/Pages/Transform3DPage.axaml +++ b/samples/RenderDemo/Pages/Transform3DPage.axaml @@ -21,149 +21,139 @@ + + + + + + + + + - - - - - - - - - - - + - - - - + - - - - + - - - - + diff --git a/src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs b/src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs index 83db67fcee..87fd47df25 100644 --- a/src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs +++ b/src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs @@ -14,7 +14,7 @@ namespace Avalonia.AndroidTestApplication { - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "12.1.99.62")] public partial class Resource { diff --git a/src/Avalonia.Visuals/Animation/Transitions/Rotate3DTransition.cs b/src/Avalonia.Visuals/Animation/Transitions/Rotate3DTransition.cs index b603fe5f7b..56567f5fea 100644 --- a/src/Avalonia.Visuals/Animation/Transitions/Rotate3DTransition.cs +++ b/src/Avalonia.Visuals/Animation/Transitions/Rotate3DTransition.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using Avalonia.Collections; using Avalonia.Media; using Avalonia.Styling; @@ -9,18 +10,26 @@ namespace Avalonia.Animation; public class Rotate3DTransition: PageSlide { - + /// - /// Creates a new instance if the + /// Creates a new instance of the /// /// How long the rotation should take place /// The orientation of the rotation - public Rotate3DTransition(TimeSpan duration, SlideAxis orientation = SlideAxis.Horizontal) + public Rotate3DTransition(TimeSpan duration, SlideAxis orientation = SlideAxis.Horizontal, double? depth = null) : base(duration, orientation) - {} + { + Depth = depth; + } + + /// + /// Defines the depth of the 3D Effect. If null, depth will be calculated automatically from the width or height + /// of the common parent of the visual being rotated. + /// + public double? Depth { get; set; } /// - /// Creates a new instance if the + /// Creates a new instance of the /// public Rotate3DTransition() { } @@ -41,49 +50,32 @@ public class Rotate3DTransition: PageSlide _ => throw new ArgumentOutOfRangeException() }; - var depthSetter = new Setter {Property = Rotate3DTransform.DepthProperty, Value = center}; + var depthSetter = new Setter {Property = Rotate3DTransform.DepthProperty, Value = Depth ?? center}; var centerZSetter = new Setter {Property = Rotate3DTransform.CenterZProperty, Value = -center / 2}; + KeyFrame CreateKeyFrame(double cue, double rotation, int zIndex) => + new() { + Setters = + { + new Setter { Property = rotateProperty, Value = rotation }, + new Setter { Property = Visual.ZIndexProperty, Value = zIndex }, + centerZSetter, + depthSetter + }, + Cue = new Cue(cue) + }; + if (from != null) { var animation = new Animation { + Easing = SlideOutEasing, Duration = Duration, Children = { - new KeyFrame - { - Setters = - { - new Setter { Property = rotateProperty, Value = 0d }, - new Setter { Property = Visual.ZIndexProperty, Value = 2 }, - centerZSetter, - depthSetter, - }, - Cue = new Cue(0d) - }, - new KeyFrame - { - Setters = - { - new Setter { Property = rotateProperty, Value = 45d * (forward ? -1 : 1) }, - new Setter { Property = Visual.ZIndexProperty, Value = 1 }, - centerZSetter, - depthSetter - }, - Cue = new Cue(0.5d) - }, - new KeyFrame - { - Setters = - { - new Setter { Property = rotateProperty, Value = 90d * (forward ? -1 : 1) }, - new Setter { Property = Visual.ZIndexProperty, Value = 1 }, - centerZSetter, - depthSetter - }, - Cue = new Cue(1d) - } + CreateKeyFrame(0d, 0d, 2), + CreateKeyFrame(0.5d, 45d * (forward ? -1 : 1), 1), + CreateKeyFrame(1d, 90d * (forward ? -1 : 1), 1) } }; @@ -95,42 +87,13 @@ public class Rotate3DTransition: PageSlide to.IsVisible = true; var animation = new Animation { + Easing = SlideInEasing, Duration = Duration, Children = { - new KeyFrame - { - Setters = - { - new Setter { Property = rotateProperty, Value = 90d * (forward ? 1 : -1) }, - new Setter { Property = Visual.ZIndexProperty, Value = 1 }, - centerZSetter, - depthSetter - }, - Cue = new Cue(0d) - }, - new KeyFrame - { - Setters = - { - new Setter { Property = Visual.ZIndexProperty, Value = 1 }, - new Setter { Property = rotateProperty, Value = 45d * (forward ? 1 : -1) }, - centerZSetter, - depthSetter - }, - Cue = new Cue(0.5d) - }, - new KeyFrame - { - Setters = - { - new Setter { Property = rotateProperty, Value = 0d }, - new Setter { Property = Visual.ZIndexProperty, Value = 2 }, - centerZSetter, - depthSetter, - }, - Cue = new Cue(1d) - } + CreateKeyFrame(0d, 90d * (forward ? 1 : -1), 1), + CreateKeyFrame(0.5d, 45d * (forward ? 1 : -1), 1), + CreateKeyFrame(1d, 0d, 2) } }; @@ -139,15 +102,18 @@ public class Rotate3DTransition: PageSlide await Task.WhenAll(tasks); - if (from != null && !cancellationToken.IsCancellationRequested) + if (!cancellationToken.IsCancellationRequested) { - from.IsVisible = false; - from.ZIndex = 1; - } + if (from != null) + { + from.IsVisible = false; + from.ZIndex = 1; + } - if (to != null && !cancellationToken.IsCancellationRequested) - { - to.ZIndex = 2; + if (to != null) + { + to.ZIndex = 2; + } } } } diff --git a/src/Avalonia.Visuals/Matrix.cs b/src/Avalonia.Visuals/Matrix.cs index 5c7d42676a..5bbc657385 100644 --- a/src/Avalonia.Visuals/Matrix.cs +++ b/src/Avalonia.Visuals/Matrix.cs @@ -348,15 +348,9 @@ namespace Avalonia } else { - var x = p.X; - var y = p.Y; - var xAdd = y * M21 + M31; - var yAdd = x * M12 + M32; - x *= M11; - x += xAdd; - y *= M22; - y += yAdd; - transformedResult = new Point(x, y); + return new Point( + (p.X * M11) + (p.Y * M21) + M31, + (p.X * M12) + (p.Y * M22) + M32); } return transformedResult; diff --git a/src/Avalonia.Visuals/Media/Rotate3DTransform.cs b/src/Avalonia.Visuals/Media/Rotate3DTransform.cs index 0fea9d73a0..4feca7acd8 100644 --- a/src/Avalonia.Visuals/Media/Rotate3DTransform.cs +++ b/src/Avalonia.Visuals/Media/Rotate3DTransform.cs @@ -5,7 +5,7 @@ using Avalonia.Animation.Animators; namespace Avalonia.Media; /// -/// Non-Affine 3D transformation for rotating an visual around a definable axis +/// Non-Affine 3D transformation for rotating a visual around a definable axis /// public class Rotate3DTransform : Transform { diff --git a/src/Avalonia.Visuals/Point.cs b/src/Avalonia.Visuals/Point.cs index 8e01ea47c5..fbdf0db800 100644 --- a/src/Avalonia.Visuals/Point.cs +++ b/src/Avalonia.Visuals/Point.cs @@ -169,12 +169,7 @@ namespace Avalonia /// The point. /// The matrix. /// The resulting point. - public static Point operator *(Point point, Matrix matrix) - { - return new Point( - (point.X * matrix.M11) + (point.Y * matrix.M21) + matrix.M31, - (point.X * matrix.M12) + (point.Y * matrix.M22) + matrix.M32); - } + public static Point operator *(Point point, Matrix matrix) => matrix.Transform(point); /// /// Parses a string. From 43de185f2fb181faa01d244d5312283ffd385ed3 Mon Sep 17 00:00:00 2001 From: Jan-Peter Zurek Date: Thu, 27 Jan 2022 16:52:35 +0100 Subject: [PATCH 028/416] Add unit tests. Add param to constructor. --- .../Media/Rotate3DTransform.cs | 5 +- .../Avalonia.Visuals.UnitTests/MatrixTests.cs | 95 +++++++++++++++++++ 2 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 tests/Avalonia.Visuals.UnitTests/MatrixTests.cs diff --git a/src/Avalonia.Visuals/Media/Rotate3DTransform.cs b/src/Avalonia.Visuals/Media/Rotate3DTransform.cs index 4feca7acd8..dbcf749789 100644 --- a/src/Avalonia.Visuals/Media/Rotate3DTransform.cs +++ b/src/Avalonia.Visuals/Media/Rotate3DTransform.cs @@ -70,13 +70,15 @@ public class Rotate3DTransform : Transform /// The origin of the X-Axis /// The origin of the Y-Axis /// The origin of the Z-Axis + /// The depth of the 3D effect public Rotate3DTransform( double angleX, double angleY, double angleZ, double centerX, double centerY, - double centerZ) : this() + double centerZ, + double depth) : this() { _isInitializing = true; AngleX = angleX; @@ -85,6 +87,7 @@ public class Rotate3DTransform : Transform CenterX = centerX; CenterY = centerY; CenterZ = centerZ; + Depth = depth; _isInitializing = false; } diff --git a/tests/Avalonia.Visuals.UnitTests/MatrixTests.cs b/tests/Avalonia.Visuals.UnitTests/MatrixTests.cs new file mode 100644 index 0000000000..cb5e2390c7 --- /dev/null +++ b/tests/Avalonia.Visuals.UnitTests/MatrixTests.cs @@ -0,0 +1,95 @@ +using System; +using System.Numerics; +using System.Runtime.InteropServices; +using Avalonia.Media; +using Xunit; + +namespace Avalonia.Visuals.UnitTests; + +/// +/// These tests use the "official" Matrix4x4 and Matrix3x2 from the System.Numerics namespace, to validate +/// that Avalonias own implementation of a 3x3 Matrix works correctly. +/// +public class MatrixTests +{ + private double ReducePrecision(double input) + { + return Math.Truncate(input * 10000); + } + + /// + /// Because Avalonia is working internally with doubles, but System.Numerics Vector and Matrix implementations + /// only make use of floats, we need to reduce precision, comparing them. It should be sufficient to compare + /// 5 fractional digits to ensure, that the result is correct. + /// + /// The expected vector + /// The actual transformed point + private void AssertCoordinatesEqualWithReducedPrecision(Vector2 expected, Point actual) + { + var expectedX = ReducePrecision(expected.X); + var expectedY = ReducePrecision(expected.Y); + + var actualX = ReducePrecision(actual.X); + var actualY = ReducePrecision(actual.Y); + + Assert.Equal(expectedX, actualX); + Assert.Equal(expectedY, actualY); + } + + [Fact] + public void Transform_Point_Should_Return_Correct_Value_For_Translated_Matrix() + { + var vector2 = Vector2.Transform( + new Vector2(1, 1), + Matrix3x2.CreateTranslation(2, 2)); + var expected = new Point(vector2.X, vector2.Y); + + var matrix = Matrix.CreateTranslation(2, 2); + var point = new Point(1, 1); + var transformedPoint = matrix.Transform(point); + + Assert.Equal(expected, transformedPoint); + } + + [Fact] + public void Transform_Point_Should_Return_Correct_Value_For_Rotated_Matrix() + { + var expected = Vector2.Transform( + new Vector2(0, 10), + Matrix3x2.CreateRotation((float)Matrix.ToRadians(45))); + + var matrix = Matrix.CreateRotation(Matrix.ToRadians(45)); + var point = new Point(0, 10); + var actual = matrix.Transform(point); + + AssertCoordinatesEqualWithReducedPrecision(expected, actual); + } + + [Fact] + public void Transform_Point_Should_Return_Correct_Value_For_Scaled_Matrix() + { + var vector2 = Vector2.Transform( + new Vector2(1, 1), + Matrix3x2.CreateScale(2, 2)); + var expected = new Point(vector2.X, vector2.Y); + var matrix = Matrix.CreateScale(2, 2); + var point = new Point(1, 1); + var actual = matrix.Transform(point); + + Assert.Equal(expected, actual); + } + + [Fact] + public void Transform_Point_Should_Return_Correct_Value_For_Skewed_Matrix() + { + var expected = Vector2.Transform( + new Vector2(1, 1), + Matrix3x2.CreateSkew(30, 20)); + + var matrix = Matrix.CreateSkew(30, 20); + var point = new Point(1, 1); + var actual = matrix.Transform(point); + + AssertCoordinatesEqualWithReducedPrecision(expected, actual); + } +} From 30f99c847a767d2ff7776964ed209df6a55ddd0d Mon Sep 17 00:00:00 2001 From: Jan-Peter Zurek Date: Thu, 27 Jan 2022 16:54:26 +0100 Subject: [PATCH 029/416] Refactor test. --- tests/Avalonia.Visuals.UnitTests/MatrixTests.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/Avalonia.Visuals.UnitTests/MatrixTests.cs b/tests/Avalonia.Visuals.UnitTests/MatrixTests.cs index cb5e2390c7..99fe4685d7 100644 --- a/tests/Avalonia.Visuals.UnitTests/MatrixTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/MatrixTests.cs @@ -12,11 +12,6 @@ namespace Avalonia.Visuals.UnitTests; /// public class MatrixTests { - private double ReducePrecision(double input) - { - return Math.Truncate(input * 10000); - } - /// /// Because Avalonia is working internally with doubles, but System.Numerics Vector and Matrix implementations /// only make use of floats, we need to reduce precision, comparing them. It should be sufficient to compare @@ -26,6 +21,8 @@ public class MatrixTests /// The actual transformed point private void AssertCoordinatesEqualWithReducedPrecision(Vector2 expected, Point actual) { + double ReducePrecision(double input) => Math.Truncate(input * 10000); + var expectedX = ReducePrecision(expected.X); var expectedY = ReducePrecision(expected.Y); From 69d65f6c95715934ff295780c05fe846d22052b7 Mon Sep 17 00:00:00 2001 From: Jan-Peter Zurek Date: Thu, 27 Jan 2022 20:40:47 +0100 Subject: [PATCH 030/416] Revert unwanted change. --- samples/ControlCatalog.Android/Resources/Resource.Designer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/ControlCatalog.Android/Resources/Resource.Designer.cs b/samples/ControlCatalog.Android/Resources/Resource.Designer.cs index b1ca548e2c..dccc3f7159 100644 --- a/samples/ControlCatalog.Android/Resources/Resource.Designer.cs +++ b/samples/ControlCatalog.Android/Resources/Resource.Designer.cs @@ -14,7 +14,7 @@ namespace ControlCatalog.Android { - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "12.1.99.62")] public partial class Resource { From b07a1b0222e194b559445a0a8fac0c7f903b8512 Mon Sep 17 00:00:00 2001 From: Jan-Peter Zurek Date: Fri, 28 Jan 2022 13:53:03 +0100 Subject: [PATCH 031/416] Add double.Epsilon check instead of 0. --- src/Avalonia.Visuals/Media/Rotate3DTransform.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Visuals/Media/Rotate3DTransform.cs b/src/Avalonia.Visuals/Media/Rotate3DTransform.cs index dbcf749789..e899f6b49b 100644 --- a/src/Avalonia.Visuals/Media/Rotate3DTransform.cs +++ b/src/Avalonia.Visuals/Media/Rotate3DTransform.cs @@ -164,13 +164,13 @@ public class Rotate3DTransform : Transform var matrix44 = Matrix4x4.Identity; var centerSum = CenterX + CenterY + CenterZ; - if (centerSum != 0) matrix44 *= Matrix4x4.CreateTranslation(-(float)CenterX, -(float)CenterY, -(float)CenterZ); + if (Math.Abs(centerSum) > double.Epsilon) matrix44 *= Matrix4x4.CreateTranslation(-(float)CenterX, -(float)CenterY, -(float)CenterZ); if (AngleX != 0) matrix44 *= Matrix4x4.CreateRotationX((float)Matrix.ToRadians(AngleX)); if (AngleY != 0) matrix44 *= Matrix4x4.CreateRotationY((float)Matrix.ToRadians(AngleY)); if (AngleZ != 0) matrix44 *= Matrix4x4.CreateRotationZ((float)Matrix.ToRadians(AngleZ)); - if (centerSum != 0) matrix44 *= Matrix4x4.CreateTranslation((float)CenterX, (float)CenterY, (float)CenterZ); + if (Math.Abs(centerSum) > double.Epsilon) matrix44 *= Matrix4x4.CreateTranslation((float)CenterX, (float)CenterY, (float)CenterZ); if (Depth != 0) { From 75788233170777fe8305bb57df8a72e11b0fd564 Mon Sep 17 00:00:00 2001 From: Jan-Peter Zurek Date: Fri, 28 Jan 2022 15:36:25 +0100 Subject: [PATCH 032/416] Add copy of values as basis for matrix transform. It is not guaranteed, that values will not change during calculation. --- .../Media/Rotate3DTransform.cs | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/Avalonia.Visuals/Media/Rotate3DTransform.cs b/src/Avalonia.Visuals/Media/Rotate3DTransform.cs index e899f6b49b..a24d6b26c3 100644 --- a/src/Avalonia.Visuals/Media/Rotate3DTransform.cs +++ b/src/Avalonia.Visuals/Media/Rotate3DTransform.cs @@ -162,20 +162,29 @@ public class Rotate3DTransform : Transform get { var matrix44 = Matrix4x4.Identity; - var centerSum = CenterX + CenterY + CenterZ; + //Copy values first, because it's not guaranteed, that values will not change during calculation + var (copyCenterX, + copyCenterY, + copyCenterZ, + copyAngleX, + copyAngleY, + copyAngleZ, + copyDepth) = (CenterX, CenterY, CenterZ, AngleX, AngleY, AngleZ, Depth); - if (Math.Abs(centerSum) > double.Epsilon) matrix44 *= Matrix4x4.CreateTranslation(-(float)CenterX, -(float)CenterY, -(float)CenterZ); + var centerSum = copyCenterX + copyCenterY + copyCenterZ; + + if (Math.Abs(centerSum) > double.Epsilon) matrix44 *= Matrix4x4.CreateTranslation(-(float)copyCenterX, -(float)copyCenterY, -(float)copyCenterZ); - if (AngleX != 0) matrix44 *= Matrix4x4.CreateRotationX((float)Matrix.ToRadians(AngleX)); - if (AngleY != 0) matrix44 *= Matrix4x4.CreateRotationY((float)Matrix.ToRadians(AngleY)); - if (AngleZ != 0) matrix44 *= Matrix4x4.CreateRotationZ((float)Matrix.ToRadians(AngleZ)); + if (AngleX != 0) matrix44 *= Matrix4x4.CreateRotationX((float)Matrix.ToRadians(copyAngleX)); + if (AngleY != 0) matrix44 *= Matrix4x4.CreateRotationY((float)Matrix.ToRadians(copyAngleY)); + if (AngleZ != 0) matrix44 *= Matrix4x4.CreateRotationZ((float)Matrix.ToRadians(copyAngleZ)); - if (Math.Abs(centerSum) > double.Epsilon) matrix44 *= Matrix4x4.CreateTranslation((float)CenterX, (float)CenterY, (float)CenterZ); + if (Math.Abs(centerSum) > double.Epsilon) matrix44 *= Matrix4x4.CreateTranslation((float)copyCenterX, (float)copyCenterY, (float)copyCenterZ); - if (Depth != 0) + if (copyDepth != 0) { var perspectiveMatrix = Matrix4x4.Identity; - perspectiveMatrix.M34 = -1 / (float)Depth; + perspectiveMatrix.M34 = -1 / (float)copyDepth; matrix44 *= perspectiveMatrix; } From e571559b64ca6c6c832aa6bf90e5db2743f3f74f Mon Sep 17 00:00:00 2001 From: Jan-Peter Zurek Date: Fri, 28 Jan 2022 16:47:22 +0100 Subject: [PATCH 033/416] Fix missing use of local copies. --- src/Avalonia.Visuals/Media/Rotate3DTransform.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Visuals/Media/Rotate3DTransform.cs b/src/Avalonia.Visuals/Media/Rotate3DTransform.cs index a24d6b26c3..306363ec39 100644 --- a/src/Avalonia.Visuals/Media/Rotate3DTransform.cs +++ b/src/Avalonia.Visuals/Media/Rotate3DTransform.cs @@ -175,9 +175,9 @@ public class Rotate3DTransform : Transform if (Math.Abs(centerSum) > double.Epsilon) matrix44 *= Matrix4x4.CreateTranslation(-(float)copyCenterX, -(float)copyCenterY, -(float)copyCenterZ); - if (AngleX != 0) matrix44 *= Matrix4x4.CreateRotationX((float)Matrix.ToRadians(copyAngleX)); - if (AngleY != 0) matrix44 *= Matrix4x4.CreateRotationY((float)Matrix.ToRadians(copyAngleY)); - if (AngleZ != 0) matrix44 *= Matrix4x4.CreateRotationZ((float)Matrix.ToRadians(copyAngleZ)); + if (copyAngleX != 0) matrix44 *= Matrix4x4.CreateRotationX((float)Matrix.ToRadians(copyAngleX)); + if (copyAngleY != 0) matrix44 *= Matrix4x4.CreateRotationY((float)Matrix.ToRadians(copyAngleY)); + if (copyAngleZ != 0) matrix44 *= Matrix4x4.CreateRotationZ((float)Matrix.ToRadians(copyAngleZ)); if (Math.Abs(centerSum) > double.Epsilon) matrix44 *= Matrix4x4.CreateTranslation((float)copyCenterX, (float)copyCenterY, (float)copyCenterZ); From b2b4014b1c6e5805e7c2330582124aa2f73f34ca Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Thu, 3 Feb 2022 14:58:28 +0300 Subject: [PATCH 034/416] Make DispatcherPriority to be a struct with static readonly field values In future versions this will allow us to extend priority lists without breaking binary compatibility --- src/Avalonia.Base/ApiCompatBaseline.txt | 3 +- .../Threading/AvaloniaScheduler.cs | 2 +- src/Avalonia.Base/Threading/Dispatcher.cs | 12 +-- .../Threading/DispatcherPriority.cs | 97 ++++++++++++++----- .../Threading/DispatcherTimer.cs | 4 +- src/Avalonia.Base/Threading/IDispatcher.cs | 12 +-- .../Diagnostics/ViewModels/MainViewModel.cs | 3 +- .../Avalonia.UnitTests/ImmediateDispatcher.cs | 12 +-- 8 files changed, 96 insertions(+), 49 deletions(-) diff --git a/src/Avalonia.Base/ApiCompatBaseline.txt b/src/Avalonia.Base/ApiCompatBaseline.txt index 4701a83175..7f378d2f65 100644 --- a/src/Avalonia.Base/ApiCompatBaseline.txt +++ b/src/Avalonia.Base/ApiCompatBaseline.txt @@ -1,3 +1,4 @@ Compat issues with assembly Avalonia.Base: +MembersMustExist : Member 'public System.Int32 System.Int32 Avalonia.Threading.DispatcherPriority.value__' does not exist in the implementation but it does exist in the contract. InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Threading.IDispatcher.Post(System.Action, T, Avalonia.Threading.DispatcherPriority)' is present in the implementation but not in the contract. -Total Issues: 1 +Total Issues: 2 diff --git a/src/Avalonia.Base/Threading/AvaloniaScheduler.cs b/src/Avalonia.Base/Threading/AvaloniaScheduler.cs index 397826df53..6423d86e7c 100644 --- a/src/Avalonia.Base/Threading/AvaloniaScheduler.cs +++ b/src/Avalonia.Base/Threading/AvaloniaScheduler.cs @@ -46,7 +46,7 @@ namespace Avalonia.Threading { composite.Add(action(this, state)); } - }, DispatcherPriority.DataBind); + }, DispatcherPriority.Background); composite.Add(cancellation); diff --git a/src/Avalonia.Base/Threading/Dispatcher.cs b/src/Avalonia.Base/Threading/Dispatcher.cs index 49cee441d0..2eb2e7c01f 100644 --- a/src/Avalonia.Base/Threading/Dispatcher.cs +++ b/src/Avalonia.Base/Threading/Dispatcher.cs @@ -83,42 +83,42 @@ namespace Avalonia.Threading _jobRunner.HasJobsWithPriority(minimumPriority); /// - public Task InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal) + public Task InvokeAsync(Action action, DispatcherPriority priority = default) { _ = action ?? throw new ArgumentNullException(nameof(action)); return _jobRunner.InvokeAsync(action, priority); } /// - public Task InvokeAsync(Func function, DispatcherPriority priority = DispatcherPriority.Normal) + public Task InvokeAsync(Func function, DispatcherPriority priority = default) { _ = function ?? throw new ArgumentNullException(nameof(function)); return _jobRunner.InvokeAsync(function, priority); } /// - public Task InvokeAsync(Func function, DispatcherPriority priority = DispatcherPriority.Normal) + public Task InvokeAsync(Func function, DispatcherPriority priority = default) { _ = function ?? throw new ArgumentNullException(nameof(function)); return _jobRunner.InvokeAsync(function, priority).Unwrap(); } /// - public Task InvokeAsync(Func> function, DispatcherPriority priority = DispatcherPriority.Normal) + public Task InvokeAsync(Func> function, DispatcherPriority priority = default) { _ = function ?? throw new ArgumentNullException(nameof(function)); return _jobRunner.InvokeAsync(function, priority).Unwrap(); } /// - public void Post(Action action, DispatcherPriority priority = DispatcherPriority.Normal) + public void Post(Action action, DispatcherPriority priority = default) { _ = action ?? throw new ArgumentNullException(nameof(action)); _jobRunner.Post(action, priority); } /// - public void Post(Action action, T arg, DispatcherPriority priority = DispatcherPriority.Normal) + public void Post(Action action, T arg, DispatcherPriority priority = default) { _ = action ?? throw new ArgumentNullException(nameof(action)); _jobRunner.Post(action, arg, priority); diff --git a/src/Avalonia.Base/Threading/DispatcherPriority.cs b/src/Avalonia.Base/Threading/DispatcherPriority.cs index a2b4b86bac..a93e4f406d 100644 --- a/src/Avalonia.Base/Threading/DispatcherPriority.cs +++ b/src/Avalonia.Base/Threading/DispatcherPriority.cs @@ -1,74 +1,121 @@ +using System; + namespace Avalonia.Threading { /// /// Defines the priorities with which jobs can be invoked on a . /// - // TODO: These are copied from WPF - many won't apply to Avalonia. - public enum DispatcherPriority + public readonly struct DispatcherPriority : IEquatable, IComparable { + /// + /// The integer value of the priority + /// + public int Value { get; } + + private DispatcherPriority(int value) + { + Value = value; + } + /// /// Minimum possible priority /// - MinValue = 1, - + public static readonly DispatcherPriority MinValue = new(0); + /// /// The job will be processed when the system is idle. /// - SystemIdle = 1, + [Obsolete("WPF compatibility")] public static readonly DispatcherPriority SystemIdle = MinValue; /// /// The job will be processed when the application is idle. /// - ApplicationIdle = 2, + [Obsolete("WPF compatibility")] public static readonly DispatcherPriority ApplicationIdle = MinValue; /// /// The job will be processed after background operations have completed. /// - ContextIdle = 3, + [Obsolete("WPF compatibility")] public static readonly DispatcherPriority ContextIdle = MinValue; /// - /// The job will be processed after other non-idle operations have completed. + /// The job will be processed with normal priority. /// - Background = 4, + public static readonly DispatcherPriority Normal = MinValue; /// - /// The job will be processed with the same priority as input. + /// The job will be processed after other non-idle operations have completed. /// - Input = 5, + public static readonly DispatcherPriority Background = new(1); /// - /// The job will be processed after layout and render but before input. + /// The job will be processed with the same priority as input. /// - Loaded = 6, + public static readonly DispatcherPriority Input = new(2); /// - /// The job will be processed with the same priority as render. + /// The job will be processed after layout and render but before input. /// - Render = 7, + public static readonly DispatcherPriority Loaded = new(3); /// /// The job will be processed with the same priority as render. /// - Layout = 8, - + public static readonly DispatcherPriority Render = new(5); + /// - /// The job will be processed with the same priority as data binding. + /// The job will be processed with the same priority as render. /// - DataBind = 9, + public static readonly DispatcherPriority Layout = new(6); /// - /// The job will be processed with normal priority. + /// The job will be processed with the same priority as data binding. /// - Normal = 10, + [Obsolete("WPF compatibility")] public static readonly DispatcherPriority DataBind = MinValue; /// /// The job will be processed before other asynchronous operations. /// - Send = 11, - + public static readonly DispatcherPriority Send = new(7); + /// /// Maximum possible priority /// - MaxValue = 11 + public static readonly DispatcherPriority MaxValue = Send; + + // Note: unlike ctor this one is validating + public static DispatcherPriority FromValue(int value) + { + if (value < MinValue.Value || value > MaxValue.Value) + throw new ArgumentOutOfRangeException(nameof(value)); + return new DispatcherPriority(value); + } + + public static implicit operator int(DispatcherPriority priority) => priority.Value; + + public static implicit operator DispatcherPriority(int value) => FromValue(value); + + /// + public bool Equals(DispatcherPriority other) => Value == other.Value; + + /// + public override bool Equals(object? obj) => obj is DispatcherPriority other && Equals(other); + + /// + public override int GetHashCode() => Value.GetHashCode(); + + public static bool operator ==(DispatcherPriority left, DispatcherPriority right) => left.Value == right.Value; + + public static bool operator !=(DispatcherPriority left, DispatcherPriority right) => left.Value != right.Value; + + public static bool operator <(DispatcherPriority left, DispatcherPriority right) => left.Value < right.Value; + + public static bool operator >(DispatcherPriority left, DispatcherPriority right) => left.Value > right.Value; + + public static bool operator <=(DispatcherPriority left, DispatcherPriority right) => left.Value <= right.Value; + + public static bool operator >=(DispatcherPriority left, DispatcherPriority right) => left.Value >= right.Value; + + /// + public int CompareTo(DispatcherPriority other) => Value.CompareTo(other.Value); } -} +} \ No newline at end of file diff --git a/src/Avalonia.Base/Threading/DispatcherTimer.cs b/src/Avalonia.Base/Threading/DispatcherTimer.cs index 93023b90a5..0c25d89722 100644 --- a/src/Avalonia.Base/Threading/DispatcherTimer.cs +++ b/src/Avalonia.Base/Threading/DispatcherTimer.cs @@ -123,7 +123,7 @@ namespace Avalonia.Threading /// The interval at which to tick. /// The priority to use. /// An used to cancel the timer. - public static IDisposable Run(Func action, TimeSpan interval, DispatcherPriority priority = DispatcherPriority.Normal) + public static IDisposable Run(Func action, TimeSpan interval, DispatcherPriority priority = default) { var timer = new DispatcherTimer(priority) { Interval = interval }; @@ -152,7 +152,7 @@ namespace Avalonia.Threading public static IDisposable RunOnce( Action action, TimeSpan interval, - DispatcherPriority priority = DispatcherPriority.Normal) + DispatcherPriority priority = default) { interval = (interval != TimeSpan.Zero) ? interval : TimeSpan.FromTicks(1); diff --git a/src/Avalonia.Base/Threading/IDispatcher.cs b/src/Avalonia.Base/Threading/IDispatcher.cs index cd5add70d4..eccd42bd4e 100644 --- a/src/Avalonia.Base/Threading/IDispatcher.cs +++ b/src/Avalonia.Base/Threading/IDispatcher.cs @@ -24,7 +24,7 @@ namespace Avalonia.Threading /// /// The method. /// The priority with which to invoke the method. - void Post(Action action, DispatcherPriority priority = DispatcherPriority.Normal); + void Post(Action action, DispatcherPriority priority = default); /// /// Posts an action that will be invoked on the dispatcher thread. @@ -33,7 +33,7 @@ namespace Avalonia.Threading /// The method to call. /// The argument of method to call. /// The priority with which to invoke the method. - void Post(Action action, T arg, DispatcherPriority priority = DispatcherPriority.Normal); + void Post(Action action, T arg, DispatcherPriority priority = default); /// /// Invokes a action on the dispatcher thread. @@ -41,7 +41,7 @@ namespace Avalonia.Threading /// The method. /// The priority with which to invoke the method. /// A task that can be used to track the method's execution. - Task InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal); + Task InvokeAsync(Action action, DispatcherPriority priority = default); /// /// Invokes a method on the dispatcher thread. @@ -49,7 +49,7 @@ namespace Avalonia.Threading /// The method. /// The priority with which to invoke the method. /// A task that can be used to track the method's execution. - Task InvokeAsync(Func function, DispatcherPriority priority = DispatcherPriority.Normal); + Task InvokeAsync(Func function, DispatcherPriority priority = default); /// /// Queues the specified work to run on the dispatcher thread and returns a proxy for the @@ -58,7 +58,7 @@ namespace Avalonia.Threading /// The work to execute asynchronously. /// The priority with which to invoke the method. /// A task that represents a proxy for the task returned by . - Task InvokeAsync(Func function, DispatcherPriority priority = DispatcherPriority.Normal); + Task InvokeAsync(Func function, DispatcherPriority priority = default); /// /// Queues the specified work to run on the dispatcher thread and returns a proxy for the @@ -67,6 +67,6 @@ namespace Avalonia.Threading /// The work to execute asynchronously. /// The priority with which to invoke the method. /// A task that represents a proxy for the task returned by . - Task InvokeAsync(Func> function, DispatcherPriority priority = DispatcherPriority.Normal); + Task InvokeAsync(Func> function, DispatcherPriority priority = default); } } diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs index e08c5bc8dd..140515eb40 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs @@ -163,8 +163,7 @@ namespace Avalonia.Diagnostics.ViewModels } catch { } }, - TimeSpan.FromMilliseconds(0), - DispatcherPriority.ApplicationIdle); + TimeSpan.FromMilliseconds(0)); } RaiseAndSetIfChanged(ref _content, value); diff --git a/tests/Avalonia.UnitTests/ImmediateDispatcher.cs b/tests/Avalonia.UnitTests/ImmediateDispatcher.cs index 5f0d41590f..03c89732f3 100644 --- a/tests/Avalonia.UnitTests/ImmediateDispatcher.cs +++ b/tests/Avalonia.UnitTests/ImmediateDispatcher.cs @@ -16,39 +16,39 @@ namespace Avalonia.UnitTests } /// - public void Post(Action action, DispatcherPriority priority = DispatcherPriority.Normal) + public void Post(Action action, DispatcherPriority priority) { action(); } /// - public void Post(Action action, T arg, DispatcherPriority priority = DispatcherPriority.Normal) + public void Post(Action action, T arg, DispatcherPriority priority) { action(arg); } /// - public Task InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal) + public Task InvokeAsync(Action action, DispatcherPriority priority) { action(); return Task.CompletedTask; } /// - public Task InvokeAsync(Func function, DispatcherPriority priority = DispatcherPriority.Normal) + public Task InvokeAsync(Func function, DispatcherPriority priority) { var result = function(); return Task.FromResult(result); } /// - public Task InvokeAsync(Func function, DispatcherPriority priority = DispatcherPriority.Normal) + public Task InvokeAsync(Func function, DispatcherPriority priority) { return function(); } /// - public Task InvokeAsync(Func> function, DispatcherPriority priority = DispatcherPriority.Normal) + public Task InvokeAsync(Func> function, DispatcherPriority priority) { return function(); } From 9318ce1612579cb5d1f7b36c33012137f955e9ac Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 7 Feb 2022 16:28:40 +0100 Subject: [PATCH 035/416] Added failing tests for #7552. --- .../LayoutableTests.cs | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/tests/Avalonia.Layout.UnitTests/LayoutableTests.cs b/tests/Avalonia.Layout.UnitTests/LayoutableTests.cs index a8aa0bbf0e..514eae12c0 100644 --- a/tests/Avalonia.Layout.UnitTests/LayoutableTests.cs +++ b/tests/Avalonia.Layout.UnitTests/LayoutableTests.cs @@ -320,6 +320,71 @@ namespace Avalonia.Layout.UnitTests Times.Once); } + [Fact] + public void Making_Control_Invisible_Should_Invalidate_Parent_Measure() + { + Border child; + var target = new StackPanel + { + Children = + { + (child = new Border + { + Width = 100, + }), + } + }; + + target.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); + target.Arrange(new Rect(target.DesiredSize)); + + Assert.True(target.IsMeasureValid); + Assert.True(target.IsArrangeValid); + Assert.True(child.IsMeasureValid); + Assert.True(child.IsArrangeValid); + + child.IsVisible = false; + + Assert.False(target.IsMeasureValid); + Assert.False(target.IsArrangeValid); + Assert.True(child.IsMeasureValid); + Assert.True(child.IsArrangeValid); + } + + [Fact] + public void Measuring_Invisible_Control_Should_Not_Invalidate_Parent_Measure() + { + Border child; + var target = new StackPanel + { + Children = + { + (child = new Border + { + Width = 100, + }), + } + }; + + target.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); + target.Arrange(new Rect(target.DesiredSize)); + + Assert.True(target.IsMeasureValid); + Assert.True(target.IsArrangeValid); + Assert.Equal(new Size(100, 0), child.DesiredSize); + + child.IsVisible = false; + Assert.Equal(default, child.DesiredSize); + + target.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); + target.Arrange(new Rect(target.DesiredSize)); + child.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); + + Assert.True(target.IsMeasureValid); + Assert.True(target.IsArrangeValid); + Assert.Equal(default, child.DesiredSize); + } + private class TestLayoutable : Layoutable { public Size ArrangeSize { get; private set; } From 1f3cb4fa00dfc6e443d92a1658517653c039066b Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 7 Feb 2022 16:29:39 +0100 Subject: [PATCH 036/416] Invalidate parent measure when child visibility changes. Fixes #7552 but breaks `ListBoxTests.LayoutManager_Should_Measure_Arrange_All`. --- src/Avalonia.Layout/Layoutable.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Layout/Layoutable.cs b/src/Avalonia.Layout/Layoutable.cs index 09e0c4263a..516a70c3c9 100644 --- a/src/Avalonia.Layout/Layoutable.cs +++ b/src/Avalonia.Layout/Layoutable.cs @@ -141,7 +141,6 @@ namespace Avalonia.Layout static Layoutable() { AffectsMeasure( - IsVisibleProperty, WidthProperty, HeightProperty, MinWidthProperty, @@ -781,6 +780,17 @@ namespace Avalonia.Layout { } + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == IsVisibleProperty) + { + DesiredSize = default; + this.GetVisualParent()?.ChildDesiredSizeChanged(this); + } + } + /// protected sealed override void OnVisualParentChanged(IVisual? oldParent, IVisual? newParent) { From 662dbf78649c60fbdee853bb4076f5ac3836bf7e Mon Sep 17 00:00:00 2001 From: Jan-Peter Zurek Date: Wed, 9 Feb 2022 19:18:25 +0100 Subject: [PATCH 037/416] Switch from list to array. Fix flickering. --- .../Transitions/Rotate3DTransition.cs | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/Avalonia.Visuals/Animation/Transitions/Rotate3DTransition.cs b/src/Avalonia.Visuals/Animation/Transitions/Rotate3DTransition.cs index 56567f5fea..ebc8f44278 100644 --- a/src/Avalonia.Visuals/Animation/Transitions/Rotate3DTransition.cs +++ b/src/Avalonia.Visuals/Animation/Transitions/Rotate3DTransition.cs @@ -1,8 +1,6 @@ using System; -using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using Avalonia.Collections; using Avalonia.Media; using Avalonia.Styling; @@ -41,7 +39,7 @@ public class Rotate3DTransition: PageSlide return; } - var tasks = new List(); + var tasks = new Task[2]; var parent = GetVisualParent(from, to); var (rotateProperty, center) = Orientation switch { @@ -53,12 +51,13 @@ public class Rotate3DTransition: PageSlide var depthSetter = new Setter {Property = Rotate3DTransform.DepthProperty, Value = Depth ?? center}; var centerZSetter = new Setter {Property = Rotate3DTransform.CenterZProperty, Value = -center / 2}; - KeyFrame CreateKeyFrame(double cue, double rotation, int zIndex) => + KeyFrame CreateKeyFrame(double cue, double rotation, int zIndex, bool isVisible = true) => new() { Setters = { new Setter { Property = rotateProperty, Value = rotation }, new Setter { Property = Visual.ZIndexProperty, Value = zIndex }, + new Setter { Property = Visual.IsVisibleProperty, Value = isVisible }, centerZSetter, depthSetter }, @@ -71,15 +70,16 @@ public class Rotate3DTransition: PageSlide { Easing = SlideOutEasing, Duration = Duration, + FillMode = FillMode.Forward, Children = { CreateKeyFrame(0d, 0d, 2), CreateKeyFrame(0.5d, 45d * (forward ? -1 : 1), 1), - CreateKeyFrame(1d, 90d * (forward ? -1 : 1), 1) + CreateKeyFrame(1d, 90d * (forward ? -1 : 1), 1, isVisible: false) } }; - tasks.Add(animation.RunAsync(from, null, cancellationToken)); + tasks[0] = animation.RunAsync(from, null, cancellationToken); } if (to != null) @@ -89,6 +89,7 @@ public class Rotate3DTransition: PageSlide { Easing = SlideInEasing, Duration = Duration, + FillMode = FillMode.Forward, Children = { CreateKeyFrame(0d, 90d * (forward ? 1 : -1), 1), @@ -97,23 +98,23 @@ public class Rotate3DTransition: PageSlide } }; - tasks.Add(animation.RunAsync(to, null, cancellationToken)); + tasks[1] = animation.RunAsync(to, null, cancellationToken); } await Task.WhenAll(tasks); if (!cancellationToken.IsCancellationRequested) { + if (to != null) + { + to.ZIndex = 2; + } + if (from != null) { from.IsVisible = false; from.ZIndex = 1; } - - if (to != null) - { - to.ZIndex = 2; - } } } } From 9c5b3ab4d8c7b454e5a33ae65a2d8d501fbf4ca7 Mon Sep 17 00:00:00 2001 From: Jan-Peter Zurek Date: Thu, 10 Feb 2022 13:22:33 +0100 Subject: [PATCH 038/416] Fix null reference exception for Tasks.WhenAll. --- .../Animation/Transitions/Rotate3DTransition.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Visuals/Animation/Transitions/Rotate3DTransition.cs b/src/Avalonia.Visuals/Animation/Transitions/Rotate3DTransition.cs index ebc8f44278..01d04dff6b 100644 --- a/src/Avalonia.Visuals/Animation/Transitions/Rotate3DTransition.cs +++ b/src/Avalonia.Visuals/Animation/Transitions/Rotate3DTransition.cs @@ -39,7 +39,7 @@ public class Rotate3DTransition: PageSlide return; } - var tasks = new Task[2]; + var tasks = new Task[from != null && to != null ? 2 : 1]; var parent = GetVisualParent(from, to); var (rotateProperty, center) = Orientation switch { From 20c28efd90a7f4a6a636a7bb70745fcbbab8d473 Mon Sep 17 00:00:00 2001 From: Jan-Peter Zurek Date: Thu, 10 Feb 2022 13:24:33 +0100 Subject: [PATCH 039/416] Improve previous fix. --- .../Animation/Transitions/Rotate3DTransition.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Visuals/Animation/Transitions/Rotate3DTransition.cs b/src/Avalonia.Visuals/Animation/Transitions/Rotate3DTransition.cs index 01d04dff6b..60c7a97ced 100644 --- a/src/Avalonia.Visuals/Animation/Transitions/Rotate3DTransition.cs +++ b/src/Avalonia.Visuals/Animation/Transitions/Rotate3DTransition.cs @@ -98,7 +98,7 @@ public class Rotate3DTransition: PageSlide } }; - tasks[1] = animation.RunAsync(to, null, cancellationToken); + tasks[from != null ? 1 : 0] = animation.RunAsync(to, null, cancellationToken); } await Task.WhenAll(tasks); From e1e6789f7824c6a21f4d8c7288cf2cffe672ee00 Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 20 Mar 2022 23:05:29 -0400 Subject: [PATCH 040/416] Add ColorPicker base enums --- .../ColorPicker/ColorPickerHsvChannel.cs | 33 +++++++++++++ .../ColorSpectrum/IncrementAmount.cs | 23 ++++++++++ .../ColorSpectrum/IncrementDirection.cs | 23 ++++++++++ .../ColorPicker/ColorSpectrumChannels.cs | 46 +++++++++++++++++++ .../ColorPicker/ColorSpectrumShape.cs | 26 +++++++++++ 5 files changed, 151 insertions(+) create mode 100644 src/Avalonia.Controls/ColorPicker/ColorPickerHsvChannel.cs create mode 100644 src/Avalonia.Controls/ColorPicker/ColorSpectrum/IncrementAmount.cs create mode 100644 src/Avalonia.Controls/ColorPicker/ColorSpectrum/IncrementDirection.cs create mode 100644 src/Avalonia.Controls/ColorPicker/ColorSpectrumChannels.cs create mode 100644 src/Avalonia.Controls/ColorPicker/ColorSpectrumShape.cs diff --git a/src/Avalonia.Controls/ColorPicker/ColorPickerHsvChannel.cs b/src/Avalonia.Controls/ColorPicker/ColorPickerHsvChannel.cs new file mode 100644 index 0000000000..9e6e7cb89e --- /dev/null +++ b/src/Avalonia.Controls/ColorPicker/ColorPickerHsvChannel.cs @@ -0,0 +1,33 @@ +// This source file is adapted from the WinUI project. +// (https://github.com/microsoft/microsoft-ui-xaml) +// +// Licensed to The Avalonia Project under the MIT License. + +namespace Avalonia.Controls +{ + /// + /// Defines a specific HSV color model channel. + /// + public enum ColorPickerHsvChannel + { + /// + /// The Hue channel. + /// + Hue, + + /// + /// The Saturation channel. + /// + Saturation, + + /// + /// The Value channel. + /// + Value, + + /// + /// The Alpha channel. + /// + Alpha + }; +} diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/IncrementAmount.cs b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/IncrementAmount.cs new file mode 100644 index 0000000000..fd749cd7dc --- /dev/null +++ b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/IncrementAmount.cs @@ -0,0 +1,23 @@ +// This source file is adapted from the WinUI project. +// (https://github.com/microsoft/microsoft-ui-xaml) +// +// Licensed to The Avalonia Project under the MIT License. + +namespace Avalonia.Controls.Primitives +{ + /// + /// Defines a relative amount that a color channel should be incremented. + /// + internal enum IncrementAmount + { + /// + /// A smaller change in value. + /// + Small, + + /// + /// A larger change in value. + /// + Large, + }; +} diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/IncrementDirection.cs b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/IncrementDirection.cs new file mode 100644 index 0000000000..8002a44fc9 --- /dev/null +++ b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/IncrementDirection.cs @@ -0,0 +1,23 @@ +// This source file is adapted from the WinUI project. +// (https://github.com/microsoft/microsoft-ui-xaml) +// +// Licensed to The Avalonia Project under the MIT License. + +namespace Avalonia.Controls.Primitives +{ + /// + /// Defines the direction a color channel should be incremented. + /// + internal enum IncrementDirection + { + /// + /// Decreasing in value towards zero. + /// + Lower, + + /// + /// Increasing in value towards positive infinity. + /// + Higher, + }; +} diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrumChannels.cs b/src/Avalonia.Controls/ColorPicker/ColorSpectrumChannels.cs new file mode 100644 index 0000000000..2b3f711634 --- /dev/null +++ b/src/Avalonia.Controls/ColorPicker/ColorSpectrumChannels.cs @@ -0,0 +1,46 @@ +// This source file is adapted from the WinUI project. +// (https://github.com/microsoft/microsoft-ui-xaml) +// +// Licensed to The Avalonia Project under the MIT License. + +using Avalonia.Controls.Primitives; + +namespace Avalonia.Controls +{ + /// + /// Defines the two HSV color channels displayed by a . + /// Order of the color channels is important. + /// + public enum ColorSpectrumChannels + { + /// + /// The Hue and Value channels. + /// + HueValue, + + /// + /// The Value and Hue channels. + /// + ValueHue, + + /// + /// The Hue and Saturation channels. + /// + HueSaturation, + + /// + /// The Saturation and Hue channels. + /// + SaturationHue, + + /// + /// The Saturation and Value channels. + /// + SaturationValue, + + /// + /// The Value and Saturation channels. + /// + ValueSaturation, + }; +} diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrumShape.cs b/src/Avalonia.Controls/ColorPicker/ColorSpectrumShape.cs new file mode 100644 index 0000000000..0319d4a6c8 --- /dev/null +++ b/src/Avalonia.Controls/ColorPicker/ColorSpectrumShape.cs @@ -0,0 +1,26 @@ +// This source file is adapted from the WinUI project. +// (https://github.com/microsoft/microsoft-ui-xaml) +// +// Licensed to The Avalonia Project under the MIT License. + +using Avalonia.Controls.Primitives; + +namespace Avalonia.Controls +{ + /// + /// Defines the shape of a . + /// + public enum ColorSpectrumShape + { + /// + /// The spectrum is in the shape of a rectangular or square box. + /// Note that more colors are visible to the user in Box shape. + /// + Box, + + /// + /// The spectrum is in the shape of an ellipse or circle. + /// + Ring, + }; +} From bf9b356a29655e74b3a8fd7ade8a094f34952054 Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 20 Mar 2022 23:06:27 -0400 Subject: [PATCH 041/416] Add HsvColor.FromRgb() overload that works with normalized double values --- src/Avalonia.Visuals/Media/HsvColor.cs | 42 ++++++++++++++++++++------ 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/src/Avalonia.Visuals/Media/HsvColor.cs b/src/Avalonia.Visuals/Media/HsvColor.cs index 8b2f10d088..a18f1032f2 100644 --- a/src/Avalonia.Visuals/Media/HsvColor.cs +++ b/src/Avalonia.Visuals/Media/HsvColor.cs @@ -173,7 +173,7 @@ namespace Avalonia.Media } /// - /// Converts the given HSV color to it's RGB color equivalent. + /// Converts the given HSV color to its RGB color equivalent. /// /// The color in the HSV color model. /// A new RGB equivalent to the given HSVA values. @@ -183,7 +183,7 @@ namespace Avalonia.Media } /// - /// Converts the given HSVA color channel values to it's RGB color equivalent. + /// Converts the given HSVA color channel values to its RGB color equivalent. /// /// The hue channel value in the HSV color model in the range from 0..360. /// The saturation channel value in the HSV color model in the range from 0..1. @@ -321,7 +321,7 @@ namespace Avalonia.Media } /// - /// Converts the given RGB color to it's HSV color equivalent. + /// Converts the given RGB color to its HSV color equivalent. /// /// The color in the RGB color model. /// A new equivalent to the given RGBA values. @@ -331,7 +331,7 @@ namespace Avalonia.Media } /// - /// Converts the given RGBA color channel values to it's HSV color equivalent. + /// Converts the given RGBA color channel values to its HSV color equivalent. /// /// The red channel value in the RGB color model. /// The green channel value in the RGB color model. @@ -343,18 +343,40 @@ namespace Avalonia.Media byte green, byte blue, byte alpha = 0xFF) + { + // Normalize RGBA channel values into the 0..1 range + return HsvColor.FromRgb( + (red / 255.0), + (green / 255.0), + (blue / 255.0), + (alpha / 255.0)); + } + + // TODO: Mark the below method Internal and make Internals visible to Avalonia.Controls... + + /// + /// Converts the given RGBA color channel values to its HSV color equivalent. + /// + /// + /// Warning: No bounds checks or clamping is done on the input channel values. + /// This method is for internal-use only and the caller must ensure bounds. + /// + /// The red channel value in the RGB color model within the range 0..1. + /// The green channel value in the RGB color model within the range 0..1. + /// The blue channel value in the RGB color model within the range 0..1. + /// The alpha channel value in the RGB color model within the range 0..1. + /// A new equivalent to the given RGBA values. + public static HsvColor FromRgb( + double r, + double g, + double b, + double a = 1.0) { // Note: Conversion code is originally based on the C++ in WinUI (licensed MIT) // https://github.com/microsoft/microsoft-ui-xaml/blob/main/dev/Common/ColorConversion.cpp // This was used because it is the best documented and likely most optimized for performance // Alpha channel support was added - // Normalize RGBA channel values into the 0..1 range used by this algorithm - double r = red / 255.0; - double g = green / 255.0; - double b = blue / 255.0; - double a = alpha / 255.0; - double hue; double saturation; double value; From 653abb3b3187e2b6f8753ad3b97616d590aa483f Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 20 Mar 2022 23:07:23 -0400 Subject: [PATCH 042/416] Add higher performance, mutable RGB and HSV structs These are only for internal use with the ColorSpectrum --- .../ColorPicker/ColorSpectrum/Hsv.cs | 87 +++++++++++++++++ .../ColorPicker/ColorSpectrum/Rgb.cs | 94 +++++++++++++++++++ 2 files changed, 181 insertions(+) create mode 100644 src/Avalonia.Controls/ColorPicker/ColorSpectrum/Hsv.cs create mode 100644 src/Avalonia.Controls/ColorPicker/ColorSpectrum/Rgb.cs diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/Hsv.cs b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/Hsv.cs new file mode 100644 index 0000000000..6d9f8440bf --- /dev/null +++ b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/Hsv.cs @@ -0,0 +1,87 @@ +// Portions of this source file are adapted from the WinUI project. +// (https://github.com/microsoft/microsoft-ui-xaml) +// +// Licensed to The Avalonia Project under the MIT License. + +using System.Numerics; +using Avalonia.Media; + +namespace Avalonia.Controls.Primitives +{ + /// + /// Contains and allows modification of Hue, Saturation and Value channel values. + /// + /// + /// The is a specialized struct optimized for permanence and memory: + /// + /// This is not a read-only struct like and allows editing the fields + /// Removes the alpha component unnecessary in core calculations + /// No channel value bounds checks or clamping is done. + /// + /// + internal struct Hsv + { + /// + /// The Hue channel value in the range from 0..359. + /// + public double H; + + /// + /// The Saturation channel value in the range from 0..1. + /// + public double S; + + /// + /// The Value channel value in the range from 0..1. + /// + public double V; + + /// + /// Initializes a new instance of the struct. + /// + /// The Hue channel value in the range from 0..360. + /// The Saturation channel value in the range from 0..1. + /// The Value channel value in the range from 0..1. + public Hsv(double h, double s, double v) + { + H = h; + S = s; + V = v; + } + + /// + /// Initializes a new instance of the struct. + /// + /// An existing to convert to . + public Hsv(HsvColor hsvColor) + { + H = hsvColor.H; + S = hsvColor.S; + V = hsvColor.V; + } + + /// + /// Converts this struct into a standard . + /// + /// The Alpha channel value in the range from 0..1. + /// A new representing this struct. + public HsvColor ToHsvColor(double alpha = 1.0) + { + // Clamping is done automatically in the constructor + return HsvColor.FromAhsv(alpha, H, S, V); + } + + /// + /// Returns the color model equivalent of this color. + /// + /// The equivalent color. + public Rgb ToRgb() + { + // Instantiating a Color is unfortunately necessary to use existing conversions + // Clamping is done internally in the conversion method + Color color = HsvColor.ToRgb(H, S, V); + + return new Rgb(color); + } + } +} diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/Rgb.cs b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/Rgb.cs new file mode 100644 index 0000000000..c2358b7bee --- /dev/null +++ b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/Rgb.cs @@ -0,0 +1,94 @@ +// Portions of this source file are adapted from the WinUI project. +// (https://github.com/microsoft/microsoft-ui-xaml) +// +// Licensed to The Avalonia Project under the MIT License. + +using System; +using Avalonia.Media; +using Avalonia.Utilities; + +namespace Avalonia.Controls.Primitives +{ + /// + /// Contains and allows modification of Red, Green and Blue channel values. + /// + /// + /// The is a specialized struct optimized for permanence and memory: + /// + /// This is not a read-only struct like and allows editing the fields + /// Removes the alpha component unnecessary in core calculations + /// Normalizes RGB channel values in the range of 0..1 to simplify calculations. + /// No channel value bounds checks or clamping is done. + /// + /// + internal struct Rgb + { + /// + /// The Red channel value in the range from 0..1. + /// + public double R; + + /// + /// The Green channel value in the range from 0..1. + /// + public double G; + + /// + /// The Blue channel value in the range from 0..1. + /// + public double B; + + /// + /// Initializes a new instance of the struct. + /// + /// The Red channel value in the range from 0..1. + /// The Green channel value in the range from 0..1. + /// The Blue channel value in the range from 0..1. + public Rgb(double r, double g, double b) + { + R = r; + G = g; + B = b; + } + + /// + /// Initializes a new instance of the struct. + /// + /// An existing to convert to . + public Rgb(Color color) + { + R = color.R / 255.0; + G = color.G / 255.0; + B = color.B / 255.0; + } + + /// + /// Converts this struct into a standard . + /// + /// The Alpha channel value in the range from 0..1. + /// A new representing this struct. + public Color ToColor(double alpha = 1.0) + { + return Color.FromArgb( + (byte)MathUtilities.Clamp(alpha * 255.0, 0x00, 0xFF), + (byte)MathUtilities.Clamp(R * 255.0, 0x00, 0xFF), + (byte)MathUtilities.Clamp(G * 255.0, 0x00, 0xFF), + (byte)MathUtilities.Clamp(B * 255.0, 0x00, 0xFF)); + } + + /// + /// Returns the color model equivalent of this color. + /// + /// The equivalent color. + public Hsv ToHsv() + { + // Instantiating an HsvColor is unfortunately necessary to use existing conversions + HsvColor hsvColor = HsvColor.FromRgb( + MathUtilities.Clamp(R, 0.0, 1.0), + MathUtilities.Clamp(G, 0.0, 1.0), + MathUtilities.Clamp(B, 0.0, 1.0)); + + return new Hsv(hsvColor); + } + } +} From 2e9d392ef91ca27bd94567dd30926a7ec9ce3321 Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 20 Mar 2022 23:07:45 -0400 Subject: [PATCH 043/416] Add new ColorChangedEventArgs --- .../ColorPicker/ColorChangedEventArgs.cs | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 src/Avalonia.Controls/ColorPicker/ColorChangedEventArgs.cs diff --git a/src/Avalonia.Controls/ColorPicker/ColorChangedEventArgs.cs b/src/Avalonia.Controls/ColorPicker/ColorChangedEventArgs.cs new file mode 100644 index 0000000000..93ba1a4db1 --- /dev/null +++ b/src/Avalonia.Controls/ColorPicker/ColorChangedEventArgs.cs @@ -0,0 +1,59 @@ +// Portions of this source file are adapted from the WinUI project. +// (https://github.com/microsoft/microsoft-ui-xaml) +// +// Licensed to The Avalonia Project under the MIT License. + +using System; +using Avalonia.Media; + +namespace Avalonia.Controls +{ + /// + /// Holds the details of a ColorChanged event. + /// + /// + /// HSV color information is intentionally not provided. + /// Use to obtain it. + /// + public class ColorChangedEventArgs : EventArgs + { + private Color _OldColor; + private Color _NewColor; + + /// + /// Initializes a new instance of the class. + /// + public ColorChangedEventArgs() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The old/original color from before the change event. + /// The new/updated color that triggered the change event. + public ColorChangedEventArgs(Color oldColor, Color newColor) + { + _OldColor = oldColor; + _NewColor = newColor; + } + + /// + /// Gets the old/original color from before the change event. + /// + public Color OldColor + { + get => _OldColor; + internal set => _OldColor = value; + } + + /// + /// Gets the new/updated color that triggered the change event. + /// + public Color NewColor + { + get => _NewColor; + internal set => _NewColor = value; + } + } +} From 0f3409064864cffc65996d2ac8a29ce4a4d225f5 Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 20 Mar 2022 23:08:17 -0400 Subject: [PATCH 044/416] Add base ColorHelpers porting from WinUI --- .../ColorPicker/ColorSpectrum/ColorHelpers.cs | 388 ++++++++++++++++++ 1 file changed, 388 insertions(+) create mode 100644 src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorHelpers.cs diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorHelpers.cs b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorHelpers.cs new file mode 100644 index 0000000000..3d380392aa --- /dev/null +++ b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorHelpers.cs @@ -0,0 +1,388 @@ +// This source file is adapted from the WinUI project. +// (https://github.com/microsoft/microsoft-ui-xaml) +// +// Licensed to The Avalonia Project under the MIT License. + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using Avalonia.Media; +using Avalonia.Media.Imaging; +using Avalonia.Platform; + +namespace Avalonia.Controls.Primitives +{ + internal static class ColorHelpers + { + public const int CheckerSize = 4; + + public static bool ToDisplayNameExists + { + get => false; + } + + public static string ToDisplayName(Color color) + { + return string.Empty; + } + + public static Hsv IncrementColorChannel( + Hsv originalHsv, + ColorPickerHsvChannel channel, + IncrementDirection direction, + IncrementAmount amount, + bool shouldWrap, + double minBound, + double maxBound) + { + Hsv newHsv = originalHsv; + + if (amount == IncrementAmount.Small || !ToDisplayNameExists) + { + // In order to avoid working with small values that can incur rounding issues, + // we'll multiple saturation and value by 100 to put them in the range of 0-100 instead of 0-1. + newHsv.S *= 100; + newHsv.V *= 100; + + // Note: *valueToIncrement replaced with ref local variable for C#, must be initialized + ref double valueToIncrement = ref newHsv.H; + double incrementAmount = 0.0; + + // If we're adding a small increment, then we'll just add or subtract 1. + // If we're adding a large increment, then we want to snap to the next + // or previous major value - for hue, this is every increment of 30; + // for saturation and value, this is every increment of 10. + switch (channel) + { + case ColorPickerHsvChannel.Hue: + valueToIncrement = ref newHsv.H; + incrementAmount = amount == IncrementAmount.Small ? 1 : 30; + break; + + case ColorPickerHsvChannel.Saturation: + valueToIncrement = ref newHsv.S; + incrementAmount = amount == IncrementAmount.Small ? 1 : 10; + break; + + case ColorPickerHsvChannel.Value: + valueToIncrement = ref newHsv.V; + incrementAmount = amount == IncrementAmount.Small ? 1 : 10; + break; + + default: + throw new InvalidOperationException("Invalid ColorPickerHsvChannel."); + } + + double previousValue = valueToIncrement; + + valueToIncrement += (direction == IncrementDirection.Lower ? -incrementAmount : incrementAmount); + + // If the value has reached outside the bounds, we were previous at the boundary, and we should wrap, + // then we'll place the selection on the other side of the spectrum. + // Otherwise, we'll place it on the boundary that was exceeded. + if (valueToIncrement < minBound) + { + valueToIncrement = (shouldWrap && previousValue == minBound) ? maxBound : minBound; + } + + if (valueToIncrement > maxBound) + { + valueToIncrement = (shouldWrap && previousValue == maxBound) ? minBound : maxBound; + } + + // We multiplied saturation and value by 100 previously, so now we want to put them back in the 0-1 range. + newHsv.S /= 100; + newHsv.V /= 100; + } + else + { + // While working with named colors, we're going to need to be working in actual HSV units, + // so we'll divide the min bound and max bound by 100 in the case of saturation or value, + // since we'll have received units between 0-100 and we need them within 0-1. + if (channel == ColorPickerHsvChannel.Saturation || + channel == ColorPickerHsvChannel.Value) + { + minBound /= 100; + maxBound /= 100; + } + + newHsv = FindNextNamedColor(originalHsv, channel, direction, shouldWrap, minBound, maxBound); + } + + return newHsv; + } + + public static Hsv FindNextNamedColor( + Hsv originalHsv, + ColorPickerHsvChannel channel, + IncrementDirection direction, + bool shouldWrap, + double minBound, + double maxBound) + { + // There's no easy way to directly get the next named color, so what we'll do + // is just iterate in the direction that we want to find it until we find a color + // in that direction that has a color name different than our current color name. + // Once we find a new color name, then we'll iterate across that color name until + // we find its bounds on the other side, and then select the color that is exactly + // in the middle of that color's bounds. + Hsv newHsv = originalHsv; + + string originalColorName = ColorHelpers.ToDisplayName(originalHsv.ToRgb().ToColor()); + string newColorName = originalColorName; + + // Note: *newValue replaced with ref local variable for C#, must be initialized + double originalValue = 0.0; + ref double newValue = ref newHsv.H; + double incrementAmount = 0.0; + + switch (channel) + { + case ColorPickerHsvChannel.Hue: + originalValue = originalHsv.H; + newValue = ref newHsv.H; + incrementAmount = 1; + break; + + case ColorPickerHsvChannel.Saturation: + originalValue = originalHsv.S; + newValue = ref newHsv.S; + incrementAmount = 0.01; + break; + + case ColorPickerHsvChannel.Value: + originalValue = originalHsv.V; + newValue = ref newHsv.V; + incrementAmount = 0.01; + break; + + default: + throw new InvalidOperationException("Invalid ColorPickerHsvChannel."); + } + + bool shouldFindMidPoint = true; + + while (newColorName == originalColorName) + { + double previousValue = newValue; + newValue += (direction == IncrementDirection.Lower ? -1 : 1) * incrementAmount; + + bool justWrapped = false; + + // If we've hit a boundary, then either we should wrap or we shouldn't. + // If we should, then we'll perform that wrapping if we were previously up against + // the boundary that we've now hit. Otherwise, we'll stop at that boundary. + if (newValue > maxBound) + { + if (shouldWrap) + { + newValue = minBound; + justWrapped = true; + } + else + { + newValue = maxBound; + shouldFindMidPoint = false; + newColorName = ColorHelpers.ToDisplayName(newHsv.ToRgb().ToColor()); + break; + } + } + else if (newValue < minBound) + { + if (shouldWrap) + { + newValue = maxBound; + justWrapped = true; + } + else + { + newValue = minBound; + shouldFindMidPoint = false; + newColorName = ColorHelpers.ToDisplayName(newHsv.ToRgb().ToColor()); + break; + } + } + + if (!justWrapped && + previousValue != originalValue && + Math.Sign(newValue - originalValue) != Math.Sign(previousValue - originalValue)) + { + // If we've wrapped all the way back to the start and have failed to find a new color name, + // then we'll just quit - there isn't a new color name that we're going to find. + shouldFindMidPoint = false; + break; + } + + newColorName = ColorHelpers.ToDisplayName(newHsv.ToRgb().ToColor()); + } + + if (shouldFindMidPoint) + { + Hsv startHsv = newHsv; + Hsv currentHsv = startHsv; + double startEndOffset = 0; + string currentColorName = newColorName; + + // Note: *startValue/*currentValue replaced with ref local variables for C#, must be initialized + ref double startValue = ref startHsv.H; + ref double currentValue = ref currentHsv.H; + double wrapIncrement = 0; + + switch (channel) + { + case ColorPickerHsvChannel.Hue: + startValue = ref startHsv.H; + currentValue = ref currentHsv.H; + wrapIncrement = 360.0; + break; + + case ColorPickerHsvChannel.Saturation: + startValue = ref startHsv.S; + currentValue = ref currentHsv.S; + wrapIncrement = 1.0; + break; + + case ColorPickerHsvChannel.Value: + startValue = ref startHsv.V; + currentValue = ref currentHsv.V; + wrapIncrement = 1.0; + break; + + default: + throw new InvalidOperationException("Invalid ColorPickerHsvChannel."); + } + + while (newColorName == currentColorName) + { + currentValue += (direction == IncrementDirection.Lower ? -1 : 1) * incrementAmount; + + // If we've hit a boundary, then either we should wrap or we shouldn't. + // If we should, then we'll perform that wrapping if we were previously up against + // the boundary that we've now hit. Otherwise, we'll stop at that boundary. + if (currentValue > maxBound) + { + if (shouldWrap) + { + currentValue = minBound; + startEndOffset = maxBound - minBound; + } + else + { + currentValue = maxBound; + break; + } + } + else if (currentValue < minBound) + { + if (shouldWrap) + { + currentValue = maxBound; + startEndOffset = minBound - maxBound; + } + else + { + currentValue = minBound; + break; + } + } + + currentColorName = ColorHelpers.ToDisplayName(currentHsv.ToRgb().ToColor()); + } + + newValue = (startValue + currentValue + startEndOffset) / 2; + + // Dividing by 2 may have gotten us halfway through a single step, so we'll + // remove that half-step if it exists. + double leftoverValue = Math.Abs(newValue); + + while (leftoverValue > incrementAmount) + { + leftoverValue -= incrementAmount; + } + + newValue -= leftoverValue; + + while (newValue < minBound) + { + newValue += wrapIncrement; + } + + while (newValue > maxBound) + { + newValue -= wrapIncrement; + } + } + + return newHsv; + } + + public static double IncrementAlphaChannel( + double originalAlpha, + IncrementDirection direction, + IncrementAmount amount, + bool shouldWrap, + double minBound, + double maxBound) + { + // In order to avoid working with small values that can incur rounding issues, + // we'll multiple alpha by 100 to put it in the range of 0-100 instead of 0-1. + originalAlpha *= 100; + + const double smallIncrementAmount = 1; + const double largeIncrementAmount = 10; + + if (amount == IncrementAmount.Small) + { + originalAlpha += (direction == IncrementDirection.Lower ? -1 : 1) * smallIncrementAmount; + } + else + { + if (direction == IncrementDirection.Lower) + { + originalAlpha = Math.Ceiling((originalAlpha - largeIncrementAmount) / largeIncrementAmount) * largeIncrementAmount; + } + else + { + originalAlpha = Math.Floor((originalAlpha + largeIncrementAmount) / largeIncrementAmount) * largeIncrementAmount; + } + } + + // If the value has reached outside the bounds and we should wrap, then we'll place the selection + // on the other side of the spectrum. Otherwise, we'll place it on the boundary that was exceeded. + if (originalAlpha < minBound) + { + originalAlpha = shouldWrap ? maxBound : minBound; + } + + if (originalAlpha > maxBound) + { + originalAlpha = shouldWrap ? minBound : maxBound; + } + + // We multiplied alpha by 100 previously, so now we want to put it back in the 0-1 range. + return originalAlpha / 100; + } + + public static WriteableBitmap CreateBitmapFromPixelData( + int pixelWidth, + int pixelHeight, + List bgraPixelData) + { + Vector dpi = new Vector(96, 96); // Standard may need to change on some devices + + WriteableBitmap bitmap = new WriteableBitmap( + new PixelSize(pixelWidth, pixelHeight), + dpi, + PixelFormat.Bgra8888, + AlphaFormat.Premul); + + // Warning: This is highly questionable + using (var frameBuffer = bitmap.Lock()) + { + Marshal.Copy(bgraPixelData.ToArray(), 0, frameBuffer.Address, bgraPixelData.Count); + } + + return bitmap; + } + } +} From fb63032d40bea28ef5703a05814c230d6025a093 Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 20 Mar 2022 23:08:42 -0400 Subject: [PATCH 045/416] Add ColorSpectrum porting from WinUI --- .../ColorSpectrum/ColorSpectrum.Properties.cs | 205 +++ .../ColorSpectrum/ColorSpectrum.cs | 1615 +++++++++++++++++ 2 files changed, 1820 insertions(+) create mode 100644 src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs create mode 100644 src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs new file mode 100644 index 0000000000..e95c63da33 --- /dev/null +++ b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs @@ -0,0 +1,205 @@ +// This source file is adapted from the WinUI project. +// (https://github.com/microsoft/microsoft-ui-xaml) +// +// Licensed to The Avalonia Project under the MIT License. + +using Avalonia.Media; + +namespace Avalonia.Controls.Primitives +{ + /// + public partial class ColorSpectrum + { + /// + /// Gets or sets the currently selected color in the RGB color model. + /// + /// + /// For control authors use instead to avoid loss + /// of precision and color drifting. + /// + public Color Color + { + get => GetValue(ColorProperty); + set => SetValue(ColorProperty, value); + } + + /// + /// Defines the property. + /// + public static readonly StyledProperty ColorProperty = + AvaloniaProperty.Register( + nameof(Color), + Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF)); + + /// + /// Gets or sets the two HSV color channels displayed by the spectrum. + /// + /// + /// Internally, the uses the HSV color model. + /// + public ColorSpectrumChannels Channels + { + get => GetValue(ChannelsProperty); + set => SetValue(ChannelsProperty, value); + } + + /// + /// Defines the property. + /// + public static readonly StyledProperty ChannelsProperty = + AvaloniaProperty.Register( + nameof(Channels), + ColorSpectrumChannels.HueSaturation); + + /// + /// Gets or sets the currently selected color in the HSV color model. + /// + /// + /// This should be used in all cases instead of the property. + /// Internally, the uses the HSV color model and using + /// this property will avoid loss of precision and color drifting. + /// + public HsvColor HsvColor + { + get => GetValue(HsvColorProperty); + set => SetValue(HsvColorProperty, value); + } + + /// + /// Defines the property. + /// + public static readonly StyledProperty HsvColorProperty = + AvaloniaProperty.Register(nameof(HsvColor), new HsvColor(1, 0, 0, 1)); + + /// + /// Gets or sets the maximum value of the Hue channel in the range from 0..359. + /// This property must be greater than . + /// + /// + /// Internally, the uses the HSV color model. + /// + public int MaxHue + { + get => GetValue(MaxHueProperty); + set => SetValue(MaxHueProperty, value); + } + + /// + /// Defines the property. + /// + public static readonly StyledProperty MaxHueProperty = + AvaloniaProperty.Register(nameof(MaxHue), 359); + + /// + /// Gets or sets the maximum value of the Saturation channel in the range from 0..100. + /// This property must be greater than . + /// + /// + /// Internally, the uses the HSV color model. + /// + public int MaxSaturation + { + get => GetValue(MaxSaturationProperty); + set => SetValue(MaxSaturationProperty, value); + } + + /// + /// Defines the property. + /// + public static readonly StyledProperty MaxSaturationProperty = + AvaloniaProperty.Register(nameof(MaxSaturation), 100); + + /// + /// Gets or sets the maximum value of the Value channel in the range from 0..100. + /// This property must be greater than . + /// + /// + /// Internally, the uses the HSV color model. + /// + public int MaxValue + { + get => GetValue(MaxValueProperty); + set => SetValue(MaxValueProperty, value); + } + + /// + /// Defines the property. + /// + public static readonly StyledProperty MaxValueProperty = + AvaloniaProperty.Register(nameof(MaxValue), 100); + + /// + /// Gets or sets the minimum value of the Hue channel in the range from 0..359. + /// This property must be less than . + /// + /// + /// Internally, the uses the HSV color model. + /// + public int MinHue + { + get => GetValue(MinHueProperty); + set => SetValue(MinHueProperty, value); + } + + /// + /// Defines the property. + /// + public static readonly StyledProperty MinHueProperty = + AvaloniaProperty.Register(nameof(MinHue), 0); + + /// + /// Gets or sets the minimum value of the Saturation channel in the range from 0..100. + /// This property must be less than . + /// + /// + /// Internally, the uses the HSV color model. + /// + public int MinSaturation + { + get => GetValue(MinSaturationProperty); + set => SetValue(MinSaturationProperty, value); + } + + /// + /// Defines the property. + /// + public static readonly StyledProperty MinSaturationProperty = + AvaloniaProperty.Register(nameof(MinSaturation), 0); + + /// + /// Gets or sets the minimum value of the Value channel in the range from 0..100. + /// This property must be less than . + /// + /// + /// Internally, the uses the HSV color model. + /// + public int MinValue + { + get => GetValue(MinValueProperty); + set => SetValue(MinValueProperty, value); + } + + /// + /// Defines the property. + /// + public static readonly StyledProperty MinValueProperty = + AvaloniaProperty.Register(nameof(MinValue), 0); + + /// + /// Gets or sets the displayed shape of the spectrum. + /// + public ColorSpectrumShape Shape + { + get => GetValue(ShapeProperty); + set => SetValue(ShapeProperty, value); + } + + /// + /// Defines the property. + /// + public static readonly StyledProperty ShapeProperty = + AvaloniaProperty.Register( + nameof(Shape), + ColorSpectrumShape.Box); + } +} diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs new file mode 100644 index 0000000000..e4096bcfda --- /dev/null +++ b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -0,0 +1,1615 @@ +// This source file is adapted from the WinUI project. +// (https://github.com/microsoft/microsoft-ui-xaml) +// +// Licensed to The Avalonia Project under the MIT License. + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Avalonia.Controls.Shapes; +using Avalonia.Input; +using Avalonia.Interactivity; +using Avalonia.Media; +using Avalonia.Media.Imaging; +using Avalonia.Threading; +using Avalonia.Utilities; + +namespace Avalonia.Controls.Primitives +{ + /// + /// A two dimensional spectrum for color selection. + /// + public partial class ColorSpectrum : TemplatedControl + { + /// + /// Event for when the selected color changes within the spectrum. + /// + public event EventHandler? ColorChanged; + + private bool _updatingColor = false; + private bool _updatingHsvColor = false; + private bool _isPointerOver = false; + private bool _isPointerPressed = false; + private bool _shouldShowLargeSelection = false; + private List _hsvValues = new List(); + + private IDisposable? _layoutRootDisposable; + + // XAML template parts + private Grid? _layoutRoot; + private Grid? _sizingGrid; + + private Rectangle? _spectrumRectangle; + private Ellipse? _spectrumEllipse; + private Rectangle? _spectrumOverlayRectangle; + private Ellipse? _spectrumOverlayEllipse; + + private Canvas? _inputTarget; + private Panel? _selectionEllipsePanel; + + private ToolTip? _colorNameToolTip; + + // Put the spectrum images in a bitmap, which is then given to an ImageBrush. + private WriteableBitmap? _hueRedBitmap; + private WriteableBitmap? _hueYellowBitmap; + private WriteableBitmap? _hueGreenBitmap; + private WriteableBitmap? _hueCyanBitmap; + private WriteableBitmap? _hueBlueBitmap; + private WriteableBitmap? _huePurpleBitmap; + + private WriteableBitmap? _saturationMinimumBitmap; + private WriteableBitmap? _saturationMaximumBitmap; + + private WriteableBitmap? _valueBitmap; + + // Fields used by UpdateEllipse() to ensure that it's using the data + // associated with the last call to CreateBitmapsAndColorMap(), + // in order to function properly while the asynchronous bitmap creation + // is in progress. + private ColorSpectrumShape _shapeFromLastBitmapCreation = ColorSpectrumShape.Box; + private ColorSpectrumChannels _componentsFromLastBitmapCreation = ColorSpectrumChannels.HueSaturation; + private double _imageWidthFromLastBitmapCreation = 0.0; + private double _imageHeightFromLastBitmapCreation = 0.0; + private int _minHueFromLastBitmapCreation = 0; + private int _maxHueFromLastBitmapCreation = 0; + private int _minSaturationFromLastBitmapCreation = 0; + private int _maxSaturationFromLastBitmapCreation = 0; + private int _minValueFromLastBitmapCreation = 0; + private int _maxValueFromLastBitmapCreation = 0; + + private Color _oldColor = Color.FromArgb(255, 255, 255, 255); + private HsvColor _oldHsvColor = HsvColor.FromAhsv(0.0f, 0.0f, 1.0f, 1.0f); + + /// + /// Initializes a new instance of the class. + /// + public ColorSpectrum() + { + _shapeFromLastBitmapCreation = Shape; + _componentsFromLastBitmapCreation = Channels; + _imageWidthFromLastBitmapCreation = 0; + _imageHeightFromLastBitmapCreation = 0; + _minHueFromLastBitmapCreation = MinHue; + _maxHueFromLastBitmapCreation = MaxHue; + _minSaturationFromLastBitmapCreation = MinSaturation; + _maxSaturationFromLastBitmapCreation = MaxSaturation; + _minValueFromLastBitmapCreation = MinValue; + _maxValueFromLastBitmapCreation = MaxValue; + } + + /// + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) + { + base.OnApplyTemplate(e); + + UnregisterEvents(); + + _layoutRoot = e.NameScope.Find("LayoutRoot"); + _sizingGrid = e.NameScope.Find("SizingGrid"); + _spectrumRectangle = e.NameScope.Find("SpectrumRectangle"); + _spectrumEllipse = e.NameScope.Find("SpectrumEllipse"); + _spectrumOverlayRectangle = e.NameScope.Find("SpectrumOverlayRectangle"); + _spectrumOverlayEllipse = e.NameScope.Find("SpectrumOverlayEllipse"); + _inputTarget = e.NameScope.Find("InputTarget"); + _selectionEllipsePanel = e.NameScope.Find("SelectionEllipsePanel"); + _colorNameToolTip = e.NameScope.Find("ColorNameToolTip"); + + if (_layoutRoot != null) + { + _layoutRootDisposable = _layoutRoot.GetObservable(BoundsProperty).Subscribe(_ => OnLayoutRootSizeChanged()); + } + + if (_inputTarget != null) + { + _inputTarget.PointerEnter += OnInputTargetPointerEnter; + _inputTarget.PointerLeave += OnInputTargetPointerLeave; + _inputTarget.PointerPressed += OnInputTargetPointerPressed; + _inputTarget.PointerMoved += OnInputTargetPointerMoved; + _inputTarget.PointerReleased += OnInputTargetPointerReleased; + } + + if (ColorHelpers.ToDisplayNameExists) + { + if (_colorNameToolTip != null) + { + _colorNameToolTip.Content = ColorHelpers.ToDisplayName(Color); + } + } + + if (_selectionEllipsePanel != null) + { + // TODO: After FlowDirection PR is merged: https://github.com/AvaloniaUI/Avalonia/pull/7810 + //m_selectionEllipsePanel.RegisterPropertyChangedCallback(FrameworkElement.FlowDirectionProperty, OnSelectionEllipseFlowDirectionChanged); + } + + // If we haven't yet created our bitmaps, do so now. + if (_hsvValues.Count == 0) + { + CreateBitmapsAndColorMap(); + } + + UpdateEllipse(); + UpdateVisualState(useTransitions: false); + } + + /// + /// Explicitly unregisters all events connected in OnApplyTemplate(). + /// + private void UnregisterEvents() + { + _layoutRootDisposable?.Dispose(); + _layoutRootDisposable = null; + + if (_inputTarget != null) + { + _inputTarget.PointerEnter -= OnInputTargetPointerEnter; + _inputTarget.PointerLeave -= OnInputTargetPointerLeave; + _inputTarget.PointerPressed -= OnInputTargetPointerPressed; + _inputTarget.PointerMoved -= OnInputTargetPointerMoved; + _inputTarget.PointerReleased -= OnInputTargetPointerReleased; + } + } + + /// + protected override void OnKeyDown(KeyEventArgs e) + { + var key = e.Key; + + if (key != Key.Left && + key != Key.Right && + key != Key.Up && + key != Key.Down) + { + base.OnKeyDown(e); + return; + } + + bool isControlDown = e.KeyModifiers.HasFlag(KeyModifiers.Control); + + ColorPickerHsvChannel incrementChannel = ColorPickerHsvChannel.Hue; + + if (key == Key.Left || + key == Key.Right) + { + switch (Channels) + { + case ColorSpectrumChannels.HueSaturation: + case ColorSpectrumChannels.HueValue: + incrementChannel = ColorPickerHsvChannel.Hue; + break; + + case ColorSpectrumChannels.SaturationHue: + case ColorSpectrumChannels.SaturationValue: + incrementChannel = ColorPickerHsvChannel.Saturation; + break; + + case ColorSpectrumChannels.ValueHue: + case ColorSpectrumChannels.ValueSaturation: + incrementChannel = ColorPickerHsvChannel.Value; + break; + } + } + else if (key == Key.Up || + key == Key.Down) + { + switch (Channels) + { + case ColorSpectrumChannels.SaturationHue: + case ColorSpectrumChannels.ValueHue: + incrementChannel = ColorPickerHsvChannel.Hue; + break; + + case ColorSpectrumChannels.HueSaturation: + case ColorSpectrumChannels.ValueSaturation: + incrementChannel = ColorPickerHsvChannel.Saturation; + break; + + case ColorSpectrumChannels.HueValue: + case ColorSpectrumChannels.SaturationValue: + incrementChannel = ColorPickerHsvChannel.Value; + break; + } + } + + double minBound = 0.0; + double maxBound = 0.0; + + switch (incrementChannel) + { + case ColorPickerHsvChannel.Hue: + minBound = MinHue; + maxBound = MaxHue; + break; + + case ColorPickerHsvChannel.Saturation: + minBound = MinSaturation; + maxBound = MaxSaturation; + break; + + case ColorPickerHsvChannel.Value: + minBound = MinValue; + maxBound = MaxValue; + break; + } + + // The order of saturation and value in the spectrum is reversed - the max value is at the bottom while the min value is at the top - + // so we want left and up to be lower for hue, but higher for saturation and value. + // This will ensure that the icon always moves in the direction of the key press. + IncrementDirection direction = + (incrementChannel == ColorPickerHsvChannel.Hue && (key == Key.Left || key == Key.Up)) || + (incrementChannel != ColorPickerHsvChannel.Hue && (key == Key.Right || key == Key.Down)) ? + IncrementDirection.Lower : + IncrementDirection.Higher; + + IncrementAmount amount = isControlDown ? IncrementAmount.Large : IncrementAmount.Small; + + HsvColor hsvColor = HsvColor; + UpdateColor(ColorHelpers.IncrementColorChannel( + new Hsv(hsvColor), + incrementChannel, + direction, + amount, + shouldWrap: true, + minBound, + maxBound)); + + e.Handled = true; + + return; + } + + /// + protected override void OnGotFocus(GotFocusEventArgs e) + { + // We only want to bother with the color name tool tip if we can provide color names. + if (_colorNameToolTip is ToolTip colorNameToolTip) + { + if (ColorHelpers.ToDisplayNameExists) + { + //colorNameToolTip.IsOpen = true; + } + } + + UpdateVisualState(useTransitions: true); + } + + /// + protected override void OnLostFocus(RoutedEventArgs e) + { + // We only want to bother with the color name tool tip if we can provide color names. + if (_colorNameToolTip is ToolTip colorNameToolTip) + { + if (ColorHelpers.ToDisplayNameExists) + { + //colorNameToolTip.IsOpen = false; + } + } + + UpdateVisualState(useTransitions: true); + } + + /// + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + if (change.Property == ColorProperty) + { + OnColorChanged(change); + } + else if (change.Property == HsvColorProperty) + { + OnHsvColorChanged(change); + } + else if ( + change.Property == MinHueProperty || + change.Property == MaxHueProperty) + { + OnMinMaxHueChanged(); + } + else if ( + change.Property == MinSaturationProperty || + change.Property == MaxSaturationProperty) + { + OnMinMaxSaturationChanged(); + } + else if ( + change.Property == MinValueProperty || + change.Property == MaxValueProperty) + { + OnMinMaxValueChanged(); + } + else if (change.Property == ShapeProperty) + { + OnShapeChanged(); + } + else if (change.Property == ChannelsProperty) + { + OnComponentsChanged(); + } + + base.OnPropertyChanged(change); + } + + private void OnColorChanged(AvaloniaPropertyChangedEventArgs change) + { + // If we're in the process of internally updating the color, + // then we don't want to respond to the Color property changing. + if (!_updatingColor) + { + Color color = Color; + + _updatingHsvColor = true; + Hsv newHsv = (new Rgb(color)).ToHsv(); + HsvColor = newHsv.ToHsvColor(color.A / 255.0); + _updatingHsvColor = false; + + UpdateEllipse(); + UpdateBitmapSources(); + } + + _oldColor = change.OldValue.GetValueOrDefault(); + } + + private void OnHsvColorChanged(AvaloniaPropertyChangedEventArgs change) + { + // If we're in the process of internally updating the HSV color, + // then we don't want to respond to the HsvColor property changing. + if (!_updatingHsvColor) + { + SetColor(); + } + + _oldHsvColor = change.OldValue.GetValueOrDefault(); + } + + private void SetColor() + { + HsvColor hsvColor = HsvColor; + + _updatingColor = true; + Rgb newRgb = (new Hsv(hsvColor)).ToRgb(); + + Color = newRgb.ToColor(hsvColor.A); + + _updatingColor = false; + + UpdateEllipse(); + UpdateBitmapSources(); + RaiseColorChanged(); + } + + public void RaiseColorChanged() + { + Color newColor = Color; + + if (_oldColor.A != newColor.A || + _oldColor.R != newColor.R || + _oldColor.G != newColor.G || + _oldColor.B != newColor.B) + { + var colorChangedEventArgs = new ColorChangedEventArgs(); + + colorChangedEventArgs.OldColor = _oldColor; + colorChangedEventArgs.NewColor = newColor; + + ColorChanged?.Invoke(this, colorChangedEventArgs); + + if (ColorHelpers.ToDisplayNameExists) + { + if (_colorNameToolTip is ToolTip colorNameToolTip) + { + colorNameToolTip.Content = ColorHelpers.ToDisplayName(newColor); + } + } + } + } + + protected void OnMinMaxHueChanged() + { + int minHue = MinHue; + int maxHue = MaxHue; + + if (minHue < 0 || minHue > 359) + { + throw new ArgumentException("MinHue must be between 0 and 359."); + } + else if (maxHue < 0 || maxHue > 359) + { + throw new ArgumentException("MaxHue must be between 0 and 359."); + } + + ColorSpectrumChannels channels = Channels; + + // If hue is one of the axes in the spectrum bitmap, then we'll need to regenerate it + // if the maximum or minimum value has changed. + if (channels != ColorSpectrumChannels.SaturationValue && + channels != ColorSpectrumChannels.ValueSaturation) + { + CreateBitmapsAndColorMap(); + } + } + + protected void OnMinMaxSaturationChanged() + { + int minSaturation = MinSaturation; + int maxSaturation = MaxSaturation; + + if (minSaturation < 0 || minSaturation > 100) + { + throw new ArgumentException("MinSaturation must be between 0 and 100."); + } + else if (maxSaturation < 0 || maxSaturation > 100) + { + throw new ArgumentException("MaxSaturation must be between 0 and 100."); + } + + ColorSpectrumChannels channels = Channels; + + // If value is one of the axes in the spectrum bitmap, then we'll need to regenerate it + // if the maximum or minimum value has changed. + if (channels != ColorSpectrumChannels.HueValue && + channels != ColorSpectrumChannels.ValueHue) + { + CreateBitmapsAndColorMap(); + } + } + + private void OnMinMaxValueChanged() + { + int minValue = MinValue; + int maxValue = MaxValue; + + if (minValue < 0 || minValue > 100) + { + throw new ArgumentException("MinValue must be between 0 and 100."); + } + else if (maxValue < 0 || maxValue > 100) + { + throw new ArgumentException("MaxValue must be between 0 and 100."); + } + + ColorSpectrumChannels channels = Channels; + + // If value is one of the axes in the spectrum bitmap, then we'll need to regenerate it + // if the maximum or minimum value has changed. + if (channels != ColorSpectrumChannels.HueSaturation && + channels != ColorSpectrumChannels.SaturationHue) + { + CreateBitmapsAndColorMap(); + } + } + + private void OnShapeChanged() + { + CreateBitmapsAndColorMap(); + } + + private void OnComponentsChanged() + { + CreateBitmapsAndColorMap(); + } + + private void UpdateVisualState(bool useTransitions) + { + //if (m_isPointerPressed) + //{ + // VisualStateManager.GoToState(this, m_shouldShowLargeSelection ? "PressedLarge" : "Pressed", useTransitions); + //} + //else if (m_isPointerOver) + //{ + // VisualStateManager.GoToState(this, "PointerOver", useTransitions); + //} + //else + //{ + // VisualStateManager.GoToState(this, "Normal", useTransitions); + //} + + //VisualStateManager.GoToState(this, m_shapeFromLastBitmapCreation == ColorSpectrumShape.Box ? "BoxSelected" : "RingSelected", useTransitions); + //VisualStateManager.GoToState(this, SelectionEllipseShouldBeLight() ? "SelectionEllipseLight" : "SelectionEllipseDark", useTransitions); + + //if (IsEnabled && FocusState != FocusState.Unfocused) + //{ + // if (FocusState == FocusState.Pointer) + // { + // VisualStateManager.GoToState(this, "PointerFocused", useTransitions); + // } + // else + // { + // VisualStateManager.GoToState(this, "Focused", useTransitions); + // } + //} + //else + //{ + // VisualStateManager.GoToState(this, "Unfocused", useTransitions); + //} + } + + private void UpdateColor(Hsv newHsv) + { + _updatingColor = true; + _updatingHsvColor = true; + + Rgb newRgb = newHsv.ToRgb(); + double alpha = HsvColor.A; + + Color = newRgb.ToColor(alpha); + HsvColor = newHsv.ToHsvColor(alpha); + + UpdateEllipse(); + UpdateVisualState(useTransitions: true); + + _updatingHsvColor = false; + _updatingColor = false; + + RaiseColorChanged(); + } + + private void UpdateColorFromPoint(PointerPoint point) + { + // If we haven't initialized our HSV value array yet, then we should just ignore any user input - + // we don't yet know what to do with it. + if (_hsvValues.Count == 0) + { + return; + } + + double xPosition = point.Position.X; + double yPosition = point.Position.Y; + double radius = Math.Min(_imageWidthFromLastBitmapCreation, _imageHeightFromLastBitmapCreation) / 2; + double distanceFromRadius = Math.Sqrt(Math.Pow(xPosition - radius, 2) + Math.Pow(yPosition - radius, 2)); + + var shape = Shape; + + // If the point is outside the circle, we should bring it back into the circle. + if (distanceFromRadius > radius && shape == ColorSpectrumShape.Ring) + { + xPosition = (radius / distanceFromRadius) * (xPosition - radius) + radius; + yPosition = (radius / distanceFromRadius) * (yPosition - radius) + radius; + } + + // Now we need to find the index into the array of HSL values at each point in the spectrum m_image. + int x = (int)Math.Round(xPosition); + int y = (int)Math.Round(yPosition); + int width = (int)Math.Round(_imageWidthFromLastBitmapCreation); + + if (x < 0) + { + x = 0; + } + else if (x >= _imageWidthFromLastBitmapCreation) + { + x = (int)Math.Round(_imageWidthFromLastBitmapCreation) - 1; + } + + if (y < 0) + { + y = 0; + } + else if (y >= _imageHeightFromLastBitmapCreation) + { + y = (int)Math.Round(_imageHeightFromLastBitmapCreation) - 1; + } + + // The gradient image contains two dimensions of HSL information, but not the third. + // We should keep the third where it already was. + // Note: This can sometimes cause a crash -- possibly due to differences in c# rounding. Therefore, index is now clamped. + Hsv hsvAtPoint = _hsvValues[MathUtilities.Clamp((y * width + x), 0, _hsvValues.Count - 1)]; + + var channels = Channels; + var hsvColor = HsvColor; + + switch (channels) + { + case ColorSpectrumChannels.HueValue: + case ColorSpectrumChannels.ValueHue: + hsvAtPoint.S = hsvColor.S; + break; + + case ColorSpectrumChannels.HueSaturation: + case ColorSpectrumChannels.SaturationHue: + hsvAtPoint.V = hsvColor.V; + break; + + case ColorSpectrumChannels.ValueSaturation: + case ColorSpectrumChannels.SaturationValue: + hsvAtPoint.H = hsvColor.H; + break; + } + + UpdateColor(hsvAtPoint); + } + + private void UpdateEllipse() + { + var selectionEllipsePanel = _selectionEllipsePanel; + + if (selectionEllipsePanel == null) + { + return; + } + + // If we don't have an image size yet, we shouldn't be showing the ellipse. + if (_imageWidthFromLastBitmapCreation == 0 || + _imageHeightFromLastBitmapCreation == 0) + { + selectionEllipsePanel.IsVisible = false; + return; + } + else + { + selectionEllipsePanel.IsVisible = true; + } + + double xPosition; + double yPosition; + + Hsv hsvColor = new Hsv(HsvColor); + + hsvColor.H = MathUtilities.Clamp(hsvColor.H, (double)_minHueFromLastBitmapCreation, (double)_maxHueFromLastBitmapCreation); + hsvColor.S = MathUtilities.Clamp(hsvColor.S, _minSaturationFromLastBitmapCreation / 100.0, _maxSaturationFromLastBitmapCreation / 100.0); + hsvColor.V = MathUtilities.Clamp(hsvColor.V, _minValueFromLastBitmapCreation / 100.0, _maxValueFromLastBitmapCreation / 100.0); + + if (_shapeFromLastBitmapCreation == ColorSpectrumShape.Box) + { + double xPercent = 0; + double yPercent = 0; + + double hPercent = (hsvColor.H - _minHueFromLastBitmapCreation) / (_maxHueFromLastBitmapCreation - _minHueFromLastBitmapCreation); + double sPercent = (hsvColor.S * 100.0 - _minSaturationFromLastBitmapCreation) / (_maxSaturationFromLastBitmapCreation - _minSaturationFromLastBitmapCreation); + double vPercent = (hsvColor.V * 100.0 - _minValueFromLastBitmapCreation) / (_maxValueFromLastBitmapCreation - _minValueFromLastBitmapCreation); + + // In the case where saturation was an axis in the spectrum with hue, or value is an axis, full stop, + // we inverted the direction of that axis in order to put more hue on the outside of the ring, + // so we need to do similarly here when positioning the ellipse. + if (_componentsFromLastBitmapCreation == ColorSpectrumChannels.HueSaturation || + _componentsFromLastBitmapCreation == ColorSpectrumChannels.SaturationHue) + { + sPercent = 1 - sPercent; + } + else + { + vPercent = 1 - vPercent; + } + + switch (_componentsFromLastBitmapCreation) + { + case ColorSpectrumChannels.HueValue: + xPercent = hPercent; + yPercent = vPercent; + break; + + case ColorSpectrumChannels.HueSaturation: + xPercent = hPercent; + yPercent = sPercent; + break; + + case ColorSpectrumChannels.ValueHue: + xPercent = vPercent; + yPercent = hPercent; + break; + + case ColorSpectrumChannels.ValueSaturation: + xPercent = vPercent; + yPercent = sPercent; + break; + + case ColorSpectrumChannels.SaturationHue: + xPercent = sPercent; + yPercent = hPercent; + break; + + case ColorSpectrumChannels.SaturationValue: + xPercent = sPercent; + yPercent = vPercent; + break; + } + + xPosition = _imageWidthFromLastBitmapCreation * xPercent; + yPosition = _imageHeightFromLastBitmapCreation * yPercent; + } + else + { + double thetaValue = 0; + double rValue = 0; + + double hThetaValue = + _maxHueFromLastBitmapCreation != _minHueFromLastBitmapCreation ? + 360 * (hsvColor.H - _minHueFromLastBitmapCreation) / (_maxHueFromLastBitmapCreation - _minHueFromLastBitmapCreation) : + 0; + double sThetaValue = + _maxSaturationFromLastBitmapCreation != _minSaturationFromLastBitmapCreation ? + 360 * (hsvColor.S * 100.0 - _minSaturationFromLastBitmapCreation) / (_maxSaturationFromLastBitmapCreation - _minSaturationFromLastBitmapCreation) : + 0; + double vThetaValue = + _maxValueFromLastBitmapCreation != _minValueFromLastBitmapCreation ? + 360 * (hsvColor.V * 100.0 - _minValueFromLastBitmapCreation) / (_maxValueFromLastBitmapCreation - _minValueFromLastBitmapCreation) : + 0; + double hRValue = _maxHueFromLastBitmapCreation != _minHueFromLastBitmapCreation ? + (hsvColor.H - _minHueFromLastBitmapCreation) / (_maxHueFromLastBitmapCreation - _minHueFromLastBitmapCreation) - 1 : + 0; + double sRValue = _maxSaturationFromLastBitmapCreation != _minSaturationFromLastBitmapCreation ? + (hsvColor.S * 100.0 - _minSaturationFromLastBitmapCreation) / (_maxSaturationFromLastBitmapCreation - _minSaturationFromLastBitmapCreation) - 1 : + 0; + double vRValue = _maxValueFromLastBitmapCreation != _minValueFromLastBitmapCreation ? + (hsvColor.V * 100.0 - _minValueFromLastBitmapCreation) / (_maxValueFromLastBitmapCreation - _minValueFromLastBitmapCreation) - 1 : + 0; + + // In the case where saturation was an axis in the spectrum with hue, or value is an axis, full stop, + // we inverted the direction of that axis in order to put more hue on the outside of the ring, + // so we need to do similarly here when positioning the ellipse. + if (_componentsFromLastBitmapCreation == ColorSpectrumChannels.HueSaturation || + _componentsFromLastBitmapCreation == ColorSpectrumChannels.ValueHue) + { + sThetaValue = 360 - sThetaValue; + sRValue = -sRValue - 1; + } + else + { + vThetaValue = 360 - vThetaValue; + vRValue = -vRValue - 1; + } + + switch (_componentsFromLastBitmapCreation) + { + case ColorSpectrumChannels.HueValue: + thetaValue = hThetaValue; + rValue = vRValue; + break; + + case ColorSpectrumChannels.HueSaturation: + thetaValue = hThetaValue; + rValue = sRValue; + break; + + case ColorSpectrumChannels.ValueHue: + thetaValue = vThetaValue; + rValue = hRValue; + break; + + case ColorSpectrumChannels.ValueSaturation: + thetaValue = vThetaValue; + rValue = sRValue; + break; + + case ColorSpectrumChannels.SaturationHue: + thetaValue = sThetaValue; + rValue = hRValue; + break; + + case ColorSpectrumChannels.SaturationValue: + thetaValue = sThetaValue; + rValue = vRValue; + break; + } + + double radius = Math.Min(_imageWidthFromLastBitmapCreation, _imageHeightFromLastBitmapCreation) / 2; + + xPosition = (Math.Cos((thetaValue * Math.PI / 180.0) + Math.PI) * radius * rValue) + radius; + yPosition = (Math.Sin((thetaValue * Math.PI / 180.0) + Math.PI) * radius * rValue) + radius; + } + + Canvas.SetLeft(selectionEllipsePanel, xPosition - (selectionEllipsePanel.Width / 2)); + Canvas.SetTop(selectionEllipsePanel, yPosition - (selectionEllipsePanel.Height / 2)); + + // We only want to bother with the color name tool tip if we can provide color names. + if (ColorHelpers.ToDisplayNameExists) + { + if (_colorNameToolTip is ToolTip colorNameToolTip) + { + // ToolTip doesn't currently provide any way to re-run its placement logic if its placement target moves, + // so toggling IsEnabled induces it to do that without incurring any visual glitches. + colorNameToolTip.IsEnabled = false; + colorNameToolTip.IsEnabled = true; + } + } + + UpdateVisualState(useTransitions: true); + } + + private void OnLayoutRootSizeChanged() + { + CreateBitmapsAndColorMap(); + } + + private void OnInputTargetPointerEnter(object? sender, PointerEventArgs args) + { + _isPointerOver = true; + UpdateVisualState(useTransitions: true); + args.Handled = true; + } + + private void OnInputTargetPointerLeave(object? sender, PointerEventArgs args) + { + _isPointerOver = false; + UpdateVisualState(useTransitions: true); + args.Handled = true; + } + + private void OnInputTargetPointerPressed(object? sender, PointerPressedEventArgs args) + { + var inputTarget = _inputTarget; + + Focus(); + + _isPointerPressed = true; + _shouldShowLargeSelection = + // TODO: After Pen PR is merged: https://github.com/AvaloniaUI/Avalonia/pull/7412 + // args.Pointer.Type == PointerType.Pen || + args.Pointer.Type == PointerType.Touch; + + args.Pointer.Capture(inputTarget); + UpdateColorFromPoint(args.GetCurrentPoint(inputTarget)); + UpdateVisualState(useTransitions: true); + UpdateEllipse(); + + args.Handled = true; + } + + private void OnInputTargetPointerMoved(object? sender, PointerEventArgs args) + { + if (!_isPointerPressed) + { + return; + } + + UpdateColorFromPoint(args.GetCurrentPoint(_inputTarget)); + args.Handled = true; + } + + private void OnInputTargetPointerReleased(object? sender, PointerReleasedEventArgs args) + { + _isPointerPressed = false; + _shouldShowLargeSelection = false; + + args.Pointer.Capture(null); + UpdateVisualState(useTransitions: true); + UpdateEllipse(); + + args.Handled = true; + } + + // TODO: After FlowDirection PR is merged: https://github.com/AvaloniaUI/Avalonia/pull/7810 + //private void OnSelectionEllipseFlowDirectionChanged(DependencyObject o, DependencyProperty p) + //{ + // UpdateEllipse(); + //} + + private async void CreateBitmapsAndColorMap() + { + if (_layoutRoot == null || + _sizingGrid == null || + _inputTarget == null || + _spectrumRectangle == null || + _spectrumEllipse == null || + _spectrumOverlayRectangle == null || + _spectrumOverlayEllipse == null + /*|| SharedHelpers.IsInDesignMode*/) + { + return; + } + + var layoutRoot = _layoutRoot; + var sizingGrid = _sizingGrid; + var inputTarget = _inputTarget; + var spectrumRectangle = _spectrumRectangle; + var spectrumEllipse = _spectrumEllipse; + var spectrumOverlayRectangle = _spectrumOverlayRectangle; + var spectrumOverlayEllipse = _spectrumOverlayEllipse; + + // We want ColorSpectrum to always be a square, so we'll take the smaller of the dimensions + // and size the sizing grid to that. + double minDimension = Math.Min(layoutRoot.Bounds.Width, layoutRoot.Bounds.Height); + + if (minDimension == 0) + { + return; + } + + sizingGrid.Width = minDimension; + sizingGrid.Height = minDimension; + + if (sizingGrid.Clip is RectangleGeometry clip) + { + clip.Rect = new Rect(0, 0, minDimension, minDimension); + } + + inputTarget.Width = minDimension; + inputTarget.Height = minDimension; + spectrumRectangle.Width = minDimension; + spectrumRectangle.Height = minDimension; + spectrumEllipse.Width = minDimension; + spectrumEllipse.Height = minDimension; + spectrumOverlayRectangle.Width = minDimension; + spectrumOverlayRectangle.Height = minDimension; + spectrumOverlayEllipse.Width = minDimension; + spectrumOverlayEllipse.Height = minDimension; + + HsvColor hsvColor = HsvColor; + int minHue = MinHue; + int maxHue = MaxHue; + int minSaturation = MinSaturation; + int maxSaturation = MaxSaturation; + int minValue = MinValue; + int maxValue = MaxValue; + ColorSpectrumShape shape = Shape; + ColorSpectrumChannels channels = Channels; + + // If min >= max, then by convention, min is the only number that a property can have. + if (minHue >= maxHue) + { + maxHue = minHue; + } + + if (minSaturation >= maxSaturation) + { + maxSaturation = minSaturation; + } + + if (minValue >= maxValue) + { + maxValue = minValue; + } + + Hsv hsv = new Hsv(hsvColor); + + // The middle 4 are only needed and used in the case of hue as the third dimension. + // Saturation and luminosity need only a min and max. + List bgraMinPixelData = new List(); + List bgraMiddle1PixelData = new List(); + List bgraMiddle2PixelData = new List(); + List bgraMiddle3PixelData = new List(); + List bgraMiddle4PixelData = new List(); + List bgraMaxPixelData = new List(); + List newHsvValues = new List(); + + var pixelCount = (int)(Math.Round(minDimension) * Math.Round(minDimension)); + var pixelDataSize = pixelCount * 4; + bgraMinPixelData.Capacity = pixelDataSize; + + // We'll only save pixel data for the middle bitmaps if our third dimension is hue. + if (channels == ColorSpectrumChannels.ValueSaturation || + channels == ColorSpectrumChannels.SaturationValue) + { + bgraMiddle1PixelData.Capacity = pixelDataSize; + bgraMiddle2PixelData.Capacity = pixelDataSize; + bgraMiddle3PixelData.Capacity = pixelDataSize; + bgraMiddle4PixelData.Capacity = pixelDataSize; + } + + bgraMaxPixelData.Capacity = pixelDataSize; + newHsvValues.Capacity = pixelCount; + + int minDimensionInt = (int)Math.Round(minDimension); + + await Task.Run(() => + { + // As the user perceives it, every time the third dimension not represented in the ColorSpectrum changes, + // the ColorSpectrum will visually change to accommodate that value. For example, if the ColorSpectrum handles hue and luminosity, + // and the saturation externally goes from 1.0 to 0.5, then the ColorSpectrum will visually change to look more washed out + // to represent that third dimension's new value. + // Internally, however, we don't want to regenerate the ColorSpectrum bitmap every single time this happens, since that's very expensive. + // In order to make it so that we don't have to, we implement an optimization where, rather than having only one bitmap, + // we instead have multiple that we blend together using opacity to create the effect that we want. + // In the case where the third dimension is saturation or luminosity, we only need two: one bitmap at the minimum value + // of the third dimension, and one bitmap at the maximum. Then we set the second's opacity at whatever the value of + // the third dimension is - e.g., a saturation of 0.5 implies an opacity of 50%. + // In the case where the third dimension is hue, we need six: one bitmap corresponding to red, yellow, green, cyan, blue, and purple. + // We'll then blend between whichever colors our hue exists between - e.g., an orange color would use red and yellow with an opacity of 50%. + // This optimization does incur slightly more startup time initially since we have to generate multiple bitmaps at once instead of only one, + // but the running time savings after that are *huge* when we can just set an opacity instead of generating a brand new bitmap. + if (shape == ColorSpectrumShape.Box) + { + for (int x = minDimensionInt - 1; x >= 0; --x) + { + for (int y = minDimensionInt - 1; y >= 0; --y) + { + FillPixelForBox( + x, y, hsv, minDimensionInt, channels, minHue, maxHue, minSaturation, maxSaturation, minValue, maxValue, + bgraMinPixelData, bgraMiddle1PixelData, bgraMiddle2PixelData, bgraMiddle3PixelData, bgraMiddle4PixelData, bgraMaxPixelData, + newHsvValues); + } + } + } + else + { + for (int y = 0; y < minDimensionInt; ++y) + { + for (int x = 0; x < minDimensionInt; ++x) + { + FillPixelForRing( + x, y, minDimensionInt / 2.0, hsv, channels, minHue, maxHue, minSaturation, maxSaturation, minValue, maxValue, + bgraMinPixelData, bgraMiddle1PixelData, bgraMiddle2PixelData, bgraMiddle3PixelData, bgraMiddle4PixelData, bgraMaxPixelData, + newHsvValues); + } + } + } + }); + + Dispatcher.UIThread.Post(() => + { + int pixelWidth = (int)Math.Round(minDimension); + int pixelHeight = (int)Math.Round(minDimension); + + ColorSpectrumChannels channels2 = Channels; + + WriteableBitmap minBitmap = ColorHelpers.CreateBitmapFromPixelData(pixelWidth, pixelHeight, bgraMinPixelData); + WriteableBitmap maxBitmap = ColorHelpers.CreateBitmapFromPixelData(pixelWidth, pixelHeight, bgraMaxPixelData); + + switch (channels2) + { + case ColorSpectrumChannels.HueValue: + case ColorSpectrumChannels.ValueHue: + _saturationMinimumBitmap = minBitmap; + _saturationMaximumBitmap = maxBitmap; + break; + case ColorSpectrumChannels.HueSaturation: + case ColorSpectrumChannels.SaturationHue: + _valueBitmap = maxBitmap; + break; + case ColorSpectrumChannels.ValueSaturation: + case ColorSpectrumChannels.SaturationValue: + _hueRedBitmap = minBitmap; + _hueYellowBitmap = ColorHelpers.CreateBitmapFromPixelData(pixelWidth, pixelHeight, bgraMiddle1PixelData); + _hueGreenBitmap = ColorHelpers.CreateBitmapFromPixelData(pixelWidth, pixelHeight, bgraMiddle2PixelData); + _hueCyanBitmap = ColorHelpers.CreateBitmapFromPixelData(pixelWidth, pixelHeight, bgraMiddle3PixelData); + _hueBlueBitmap = ColorHelpers.CreateBitmapFromPixelData(pixelWidth, pixelHeight, bgraMiddle4PixelData); + _huePurpleBitmap = maxBitmap; + break; + } + + _shapeFromLastBitmapCreation = Shape; + _componentsFromLastBitmapCreation = Channels; + _imageWidthFromLastBitmapCreation = minDimension; + _imageHeightFromLastBitmapCreation = minDimension; + _minHueFromLastBitmapCreation = MinHue; + _maxHueFromLastBitmapCreation = MaxHue; + _minSaturationFromLastBitmapCreation = MinSaturation; + _maxSaturationFromLastBitmapCreation = MaxSaturation; + _minValueFromLastBitmapCreation = MinValue; + _maxValueFromLastBitmapCreation = MaxValue; + + _hsvValues = newHsvValues; + + UpdateBitmapSources(); + UpdateEllipse(); + }); + } + + private void FillPixelForBox( + double x, + double y, + Hsv baseHsv, + double minDimension, + ColorSpectrumChannels channels, + double minHue, + double maxHue, + double minSaturation, + double maxSaturation, + double minValue, + double maxValue, + List bgraMinPixelData, + List bgraMiddle1PixelData, + List bgraMiddle2PixelData, + List bgraMiddle3PixelData, + List bgraMiddle4PixelData, + List bgraMaxPixelData, + List newHsvValues) + { + double hMin = minHue; + double hMax = maxHue; + double sMin = minSaturation / 100.0; + double sMax = maxSaturation / 100.0; + double vMin = minValue / 100.0; + double vMax = maxValue / 100.0; + + Hsv hsvMin = baseHsv; + Hsv hsvMiddle1 = baseHsv; + Hsv hsvMiddle2 = baseHsv; + Hsv hsvMiddle3 = baseHsv; + Hsv hsvMiddle4 = baseHsv; + Hsv hsvMax = baseHsv; + + double xPercent = (minDimension - 1 - x) / (minDimension - 1); + double yPercent = (minDimension - 1 - y) / (minDimension - 1); + + switch (channels) + { + case ColorSpectrumChannels.HueValue: + hsvMin.H = hsvMiddle1.H = hsvMiddle2.H = hsvMiddle3.H = hsvMiddle4.H = hsvMax.H = hMin + yPercent * (hMax - hMin); + hsvMin.V = hsvMiddle1.V = hsvMiddle2.V = hsvMiddle3.V = hsvMiddle4.V = hsvMax.V = vMin + xPercent * (vMax - vMin); + hsvMin.S = 0; + hsvMax.S = 1; + break; + + case ColorSpectrumChannels.HueSaturation: + hsvMin.H = hsvMiddle1.H = hsvMiddle2.H = hsvMiddle3.H = hsvMiddle4.H = hsvMax.H = hMin + yPercent * (hMax - hMin); + hsvMin.S = hsvMiddle1.S = hsvMiddle2.S = hsvMiddle3.S = hsvMiddle4.S = hsvMax.S = sMin + xPercent * (sMax - sMin); + hsvMin.V = 0; + hsvMax.V = 1; + break; + + case ColorSpectrumChannels.ValueHue: + hsvMin.V = hsvMiddle1.V = hsvMiddle2.V = hsvMiddle3.V = hsvMiddle4.V = hsvMax.V = vMin + yPercent * (vMax - vMin); + hsvMin.H = hsvMiddle1.H = hsvMiddle2.H = hsvMiddle3.H = hsvMiddle4.H = hsvMax.H = hMin + xPercent * (hMax - hMin); + hsvMin.S = 0; + hsvMax.S = 1; + break; + + case ColorSpectrumChannels.ValueSaturation: + hsvMin.V = hsvMiddle1.V = hsvMiddle2.V = hsvMiddle3.V = hsvMiddle4.V = hsvMax.V = vMin + yPercent * (vMax - vMin); + hsvMin.S = hsvMiddle1.S = hsvMiddle2.S = hsvMiddle3.S = hsvMiddle4.S = hsvMax.S = sMin + xPercent * (sMax - sMin); + hsvMin.H = 0; + hsvMiddle1.H = 60; + hsvMiddle2.H = 120; + hsvMiddle3.H = 180; + hsvMiddle4.H = 240; + hsvMax.H = 300; + break; + + case ColorSpectrumChannels.SaturationHue: + hsvMin.S = hsvMiddle1.S = hsvMiddle2.S = hsvMiddle3.S = hsvMiddle4.S = hsvMax.S = sMin + yPercent * (sMax - sMin); + hsvMin.H = hsvMiddle1.H = hsvMiddle2.H = hsvMiddle3.H = hsvMiddle4.H = hsvMax.H = hMin + xPercent * (hMax - hMin); + hsvMin.V = 0; + hsvMax.V = 1; + break; + + case ColorSpectrumChannels.SaturationValue: + hsvMin.S = hsvMiddle1.S = hsvMiddle2.S = hsvMiddle3.S = hsvMiddle4.S = hsvMax.S = sMin + yPercent * (sMax - sMin); + hsvMin.V = hsvMiddle1.V = hsvMiddle2.V = hsvMiddle3.V = hsvMiddle4.V = hsvMax.V = vMin + xPercent * (vMax - vMin); + hsvMin.H = 0; + hsvMiddle1.H = 60; + hsvMiddle2.H = 120; + hsvMiddle3.H = 180; + hsvMiddle4.H = 240; + hsvMax.H = 300; + break; + } + + // If saturation is an axis in the spectrum with hue, or value is an axis, then we want + // that axis to go from maximum at the top to minimum at the bottom, + // or maximum at the outside to minimum at the inside in the case of the ring configuration, + // so we'll invert the number before assigning the HSL value to the array. + // Otherwise, we'll have a very narrow section in the middle that actually has meaningful hue + // in the case of the ring configuration. + if (channels == ColorSpectrumChannels.HueSaturation || + channels == ColorSpectrumChannels.SaturationHue) + { + hsvMin.S = sMax - hsvMin.S + sMin; + hsvMiddle1.S = sMax - hsvMiddle1.S + sMin; + hsvMiddle2.S = sMax - hsvMiddle2.S + sMin; + hsvMiddle3.S = sMax - hsvMiddle3.S + sMin; + hsvMiddle4.S = sMax - hsvMiddle4.S + sMin; + hsvMax.S = sMax - hsvMax.S + sMin; + } + else + { + hsvMin.V = vMax - hsvMin.V + vMin; + hsvMiddle1.V = vMax - hsvMiddle1.V + vMin; + hsvMiddle2.V = vMax - hsvMiddle2.V + vMin; + hsvMiddle3.V = vMax - hsvMiddle3.V + vMin; + hsvMiddle4.V = vMax - hsvMiddle4.V + vMin; + hsvMax.V = vMax - hsvMax.V + vMin; + } + + newHsvValues.Add(hsvMin); + + Rgb rgbMin = hsvMin.ToRgb(); + bgraMinPixelData.Add((byte)Math.Round(rgbMin.B * 255.0)); // b + bgraMinPixelData.Add((byte)Math.Round(rgbMin.G * 255.0)); // g + bgraMinPixelData.Add((byte)Math.Round(rgbMin.R * 255.0)); // r + bgraMinPixelData.Add(255); // a - ignored + + // We'll only save pixel data for the middle bitmaps if our third dimension is hue. + if (channels == ColorSpectrumChannels.ValueSaturation || + channels == ColorSpectrumChannels.SaturationValue) + { + Rgb rgbMiddle1 = hsvMiddle1.ToRgb(); + bgraMiddle1PixelData.Add((byte)Math.Round(rgbMiddle1.B * 255.0)); // b + bgraMiddle1PixelData.Add((byte)Math.Round(rgbMiddle1.G * 255.0)); // g + bgraMiddle1PixelData.Add((byte)Math.Round(rgbMiddle1.R * 255.0)); // r + bgraMiddle1PixelData.Add(255); // a - ignored + + Rgb rgbMiddle2 = hsvMiddle2.ToRgb(); + bgraMiddle2PixelData.Add((byte)Math.Round(rgbMiddle2.B * 255.0)); // b + bgraMiddle2PixelData.Add((byte)Math.Round(rgbMiddle2.G * 255.0)); // g + bgraMiddle2PixelData.Add((byte)Math.Round(rgbMiddle2.R * 255.0)); // r + bgraMiddle2PixelData.Add(255); // a - ignored + + Rgb rgbMiddle3 = hsvMiddle3.ToRgb(); + bgraMiddle3PixelData.Add((byte)Math.Round(rgbMiddle3.B * 255.0)); // b + bgraMiddle3PixelData.Add((byte)Math.Round(rgbMiddle3.G * 255.0)); // g + bgraMiddle3PixelData.Add((byte)Math.Round(rgbMiddle3.R * 255.0)); // r + bgraMiddle3PixelData.Add(255); // a - ignored + + Rgb rgbMiddle4 = hsvMiddle4.ToRgb(); + bgraMiddle4PixelData.Add((byte)Math.Round(rgbMiddle4.B * 255.0)); // b + bgraMiddle4PixelData.Add((byte)Math.Round(rgbMiddle4.G * 255.0)); // g + bgraMiddle4PixelData.Add((byte)Math.Round(rgbMiddle4.R * 255.0)); // r + bgraMiddle4PixelData.Add(255); // a - ignored + } + + Rgb rgbMax = hsvMax.ToRgb(); + bgraMaxPixelData.Add((byte)Math.Round(rgbMax.B * 255.0)); // b + bgraMaxPixelData.Add((byte)Math.Round(rgbMax.G * 255.0)); // g + bgraMaxPixelData.Add((byte)Math.Round(rgbMax.R * 255.0)); // r + bgraMaxPixelData.Add(255); // a - ignored + } + + private void FillPixelForRing( + double x, + double y, + double radius, + Hsv baseHsv, + ColorSpectrumChannels channels, + double minHue, + double maxHue, + double minSaturation, + double maxSaturation, + double minValue, + double maxValue, + List bgraMinPixelData, + List bgraMiddle1PixelData, + List bgraMiddle2PixelData, + List bgraMiddle3PixelData, + List bgraMiddle4PixelData, + List bgraMaxPixelData, + List newHsvValues) + { + double hMin = minHue; + double hMax = maxHue; + double sMin = minSaturation / 100.0; + double sMax = maxSaturation / 100.0; + double vMin = minValue / 100.0; + double vMax = maxValue / 100.0; + + double distanceFromRadius = Math.Sqrt(Math.Pow(x - radius, 2) + Math.Pow(y - radius, 2)); + + double xToUse = x; + double yToUse = y; + + // If we're outside the ring, then we want the pixel to appear as blank. + // However, to avoid issues with rounding errors, we'll act as though this point + // is on the edge of the ring for the purposes of returning an HSL value. + // That way, hit testing on the edges will always return the correct value. + if (distanceFromRadius > radius) + { + xToUse = (radius / distanceFromRadius) * (x - radius) + radius; + yToUse = (radius / distanceFromRadius) * (y - radius) + radius; + distanceFromRadius = radius; + } + + Hsv hsvMin = baseHsv; + Hsv hsvMiddle1 = baseHsv; + Hsv hsvMiddle2 = baseHsv; + Hsv hsvMiddle3 = baseHsv; + Hsv hsvMiddle4 = baseHsv; + Hsv hsvMax = baseHsv; + + double r = 1 - distanceFromRadius / radius; + + double theta = Math.Atan2((radius - yToUse), (radius - xToUse)) * 180.0 / Math.PI; + theta += 180.0; + theta = Math.Floor(theta); + + while (theta > 360) + { + theta -= 360; + } + + double thetaPercent = theta / 360; + + switch (channels) + { + case ColorSpectrumChannels.HueValue: + hsvMin.H = hsvMiddle1.H = hsvMiddle2.H = hsvMiddle3.H = hsvMiddle4.H = hsvMax.H = hMin + thetaPercent * (hMax - hMin); + hsvMin.V = hsvMiddle1.V = hsvMiddle2.V = hsvMiddle3.V = hsvMiddle4.V = hsvMax.V = vMin + r * (vMax - vMin); + hsvMin.S = 0; + hsvMax.S = 1; + break; + + case ColorSpectrumChannels.HueSaturation: + hsvMin.H = hsvMiddle1.H = hsvMiddle2.H = hsvMiddle3.H = hsvMiddle4.H = hsvMax.H = hMin + thetaPercent * (hMax - hMin); + hsvMin.S = hsvMiddle1.S = hsvMiddle2.S = hsvMiddle3.S = hsvMiddle4.S = hsvMax.S = sMin + r * (sMax - sMin); + hsvMin.V = 0; + hsvMax.V = 1; + break; + + case ColorSpectrumChannels.ValueHue: + hsvMin.V = hsvMiddle1.V = hsvMiddle2.V = hsvMiddle3.V = hsvMiddle4.V = hsvMax.V = vMin + thetaPercent * (vMax - vMin); + hsvMin.H = hsvMiddle1.H = hsvMiddle2.H = hsvMiddle3.H = hsvMiddle4.H = hsvMax.H = hMin + r * (hMax - hMin); + hsvMin.S = 0; + hsvMax.S = 1; + break; + + case ColorSpectrumChannels.ValueSaturation: + hsvMin.V = hsvMiddle1.V = hsvMiddle2.V = hsvMiddle3.V = hsvMiddle4.V = hsvMax.V = vMin + thetaPercent * (vMax - vMin); + hsvMin.S = hsvMiddle1.S = hsvMiddle2.S = hsvMiddle3.S = hsvMiddle4.S = hsvMax.S = sMin + r * (sMax - sMin); + hsvMin.H = 0; + hsvMiddle1.H = 60; + hsvMiddle2.H = 120; + hsvMiddle3.H = 180; + hsvMiddle4.H = 240; + hsvMax.H = 300; + break; + + case ColorSpectrumChannels.SaturationHue: + hsvMin.S = hsvMiddle1.S = hsvMiddle2.S = hsvMiddle3.S = hsvMiddle4.S = hsvMax.S = sMin + thetaPercent * (sMax - sMin); + hsvMin.H = hsvMiddle1.H = hsvMiddle2.H = hsvMiddle3.H = hsvMiddle4.H = hsvMax.H = hMin + r * (hMax - hMin); + hsvMin.V = 0; + hsvMax.V = 1; + break; + + case ColorSpectrumChannels.SaturationValue: + hsvMin.S = hsvMiddle1.S = hsvMiddle2.S = hsvMiddle3.S = hsvMiddle4.S = hsvMax.S = sMin + thetaPercent * (sMax - sMin); + hsvMin.V = hsvMiddle1.V = hsvMiddle2.V = hsvMiddle3.V = hsvMiddle4.V = hsvMax.V = vMin + r * (vMax - vMin); + hsvMin.H = 0; + hsvMiddle1.H = 60; + hsvMiddle2.H = 120; + hsvMiddle3.H = 180; + hsvMiddle4.H = 240; + hsvMax.H = 300; + break; + } + + // If saturation is an axis in the spectrum with hue, or value is an axis, then we want + // that axis to go from maximum at the top to minimum at the bottom, + // or maximum at the outside to minimum at the inside in the case of the ring configuration, + // so we'll invert the number before assigning the HSL value to the array. + // Otherwise, we'll have a very narrow section in the middle that actually has meaningful hue + // in the case of the ring configuration. + if (channels == ColorSpectrumChannels.HueSaturation || + channels == ColorSpectrumChannels.SaturationHue) + { + hsvMin.S = sMax - hsvMin.S + sMin; + hsvMiddle1.S = sMax - hsvMiddle1.S + sMin; + hsvMiddle2.S = sMax - hsvMiddle2.S + sMin; + hsvMiddle3.S = sMax - hsvMiddle3.S + sMin; + hsvMiddle4.S = sMax - hsvMiddle4.S + sMin; + hsvMax.S = sMax - hsvMax.S + sMin; + } + else + { + hsvMin.V = vMax - hsvMin.V + vMin; + hsvMiddle1.V = vMax - hsvMiddle1.V + vMin; + hsvMiddle2.V = vMax - hsvMiddle2.V + vMin; + hsvMiddle3.V = vMax - hsvMiddle3.V + vMin; + hsvMiddle4.V = vMax - hsvMiddle4.V + vMin; + hsvMax.V = vMax - hsvMax.V + vMin; + } + + newHsvValues.Add(hsvMin); + + Rgb rgbMin = hsvMin.ToRgb(); + bgraMinPixelData.Add((byte)Math.Round(rgbMin.B * 255)); // b + bgraMinPixelData.Add((byte)Math.Round(rgbMin.G * 255)); // g + bgraMinPixelData.Add((byte)Math.Round(rgbMin.R * 255)); // r + bgraMinPixelData.Add(255); // a + + // We'll only save pixel data for the middle bitmaps if our third dimension is hue. + if (channels == ColorSpectrumChannels.ValueSaturation || + channels == ColorSpectrumChannels.SaturationValue) + { + Rgb rgbMiddle1 = hsvMiddle1.ToRgb(); + bgraMiddle1PixelData.Add((byte)Math.Round(rgbMiddle1.B * 255)); // b + bgraMiddle1PixelData.Add((byte)Math.Round(rgbMiddle1.G * 255)); // g + bgraMiddle1PixelData.Add((byte)Math.Round(rgbMiddle1.R * 255)); // r + bgraMiddle1PixelData.Add(255); // a + + Rgb rgbMiddle2 = hsvMiddle2.ToRgb(); + bgraMiddle2PixelData.Add((byte)Math.Round(rgbMiddle2.B * 255)); // b + bgraMiddle2PixelData.Add((byte)Math.Round(rgbMiddle2.G * 255)); // g + bgraMiddle2PixelData.Add((byte)Math.Round(rgbMiddle2.R * 255)); // r + bgraMiddle2PixelData.Add(255); // a + + Rgb rgbMiddle3 = hsvMiddle3.ToRgb(); + bgraMiddle3PixelData.Add((byte)Math.Round(rgbMiddle3.B * 255)); // b + bgraMiddle3PixelData.Add((byte)Math.Round(rgbMiddle3.G * 255)); // g + bgraMiddle3PixelData.Add((byte)Math.Round(rgbMiddle3.R * 255)); // r + bgraMiddle3PixelData.Add(255); // a + + Rgb rgbMiddle4 = hsvMiddle4.ToRgb(); + bgraMiddle4PixelData.Add((byte)Math.Round(rgbMiddle4.B * 255)); // b + bgraMiddle4PixelData.Add((byte)Math.Round(rgbMiddle4.G * 255)); // g + bgraMiddle4PixelData.Add((byte)Math.Round(rgbMiddle4.R * 255)); // r + bgraMiddle4PixelData.Add(255); // a + } + + Rgb rgbMax = hsvMax.ToRgb(); + bgraMaxPixelData.Add((byte)Math.Round(rgbMax.B * 255)); // b + bgraMaxPixelData.Add((byte)Math.Round(rgbMax.G * 255)); // g + bgraMaxPixelData.Add((byte)Math.Round(rgbMax.R * 255)); // r + bgraMaxPixelData.Add(255); // a + } + + private void UpdateBitmapSources() + { + var spectrumOverlayRectangle = _spectrumOverlayRectangle; + var spectrumOverlayEllipse = _spectrumOverlayEllipse; + + var spectrumRectangle = _spectrumRectangle; + var spectrumEllipse = _spectrumEllipse; + + if (spectrumOverlayRectangle == null || + spectrumOverlayEllipse == null || + spectrumRectangle == null || + spectrumEllipse == null) + { + return; + } + + HsvColor hsvColor = HsvColor; + ColorSpectrumChannels channels = Channels; + + // We'll set the base image and the overlay image based on which component is our third dimension. + // If it's saturation or luminosity, then the base image is that dimension at its minimum value, + // while the overlay image is that dimension at its maximum value. + // If it's hue, then we'll figure out where in the color wheel we are, and then use the two + // colors on either side of our position as our base image and overlay image. + // For example, if our hue is orange, then the base image would be red and the overlay image yellow. + switch (channels) + { + case ColorSpectrumChannels.HueValue: + case ColorSpectrumChannels.ValueHue: + { + if (_saturationMinimumBitmap == null || + _saturationMaximumBitmap == null) + { + return; + } + + ImageBrush spectrumBrush; + ImageBrush spectrumOverlayBrush; + + spectrumBrush = new ImageBrush(_saturationMinimumBitmap); + spectrumOverlayBrush = new ImageBrush(_saturationMaximumBitmap); + spectrumOverlayRectangle.Opacity = hsvColor.S; + spectrumOverlayEllipse.Opacity = hsvColor.S; + spectrumRectangle.Fill = spectrumBrush; + spectrumEllipse.Fill = spectrumBrush; + spectrumOverlayRectangle.Fill = spectrumOverlayBrush; + spectrumOverlayRectangle.Fill = spectrumOverlayBrush; + } + break; + + case ColorSpectrumChannels.HueSaturation: + case ColorSpectrumChannels.SaturationHue: + { + if (_valueBitmap == null) + { + return; + } + + ImageBrush spectrumBrush; + ImageBrush spectrumOverlayBrush; + + spectrumBrush = new ImageBrush(_valueBitmap); + spectrumOverlayBrush = new ImageBrush(_valueBitmap); + spectrumOverlayRectangle.Opacity = 1.0; + spectrumOverlayEllipse.Opacity = 1.0; + spectrumRectangle.Fill = spectrumBrush; + spectrumEllipse.Fill = spectrumBrush; + spectrumOverlayRectangle.Fill = spectrumOverlayBrush; + spectrumOverlayRectangle.Fill = spectrumOverlayBrush; + } + break; + + case ColorSpectrumChannels.ValueSaturation: + case ColorSpectrumChannels.SaturationValue: + { + if (_hueRedBitmap == null || + _hueYellowBitmap == null || + _hueGreenBitmap == null || + _hueCyanBitmap == null || + _hueBlueBitmap == null || + _huePurpleBitmap == null) + { + return; + } + + ImageBrush spectrumBrush; + ImageBrush spectrumOverlayBrush; + + double sextant = hsvColor.H / 60.0; + + if (sextant < 1) + { + spectrumBrush = new ImageBrush(_hueRedBitmap); + spectrumOverlayBrush = new ImageBrush(_hueYellowBitmap); + } + else if (sextant >= 1 && sextant < 2) + { + spectrumBrush = new ImageBrush(_hueYellowBitmap); + spectrumOverlayBrush = new ImageBrush(_hueGreenBitmap); + } + else if (sextant >= 2 && sextant < 3) + { + spectrumBrush = new ImageBrush(_hueGreenBitmap); + spectrumOverlayBrush = new ImageBrush(_hueCyanBitmap); + } + else if (sextant >= 3 && sextant < 4) + { + spectrumBrush = new ImageBrush(_hueCyanBitmap); + spectrumOverlayBrush = new ImageBrush(_hueBlueBitmap); + } + else if (sextant >= 4 && sextant < 5) + { + spectrumBrush = new ImageBrush(_hueBlueBitmap); + spectrumOverlayBrush = new ImageBrush(_huePurpleBitmap); + } + else + { + spectrumBrush = new ImageBrush(_huePurpleBitmap); + spectrumOverlayBrush = new ImageBrush(_hueRedBitmap); + } + + spectrumOverlayRectangle.Opacity = sextant - (int)sextant; + spectrumOverlayEllipse.Opacity = sextant - (int)sextant; + spectrumRectangle.Fill = spectrumBrush; + spectrumEllipse.Fill = spectrumBrush; + spectrumOverlayRectangle.Fill = spectrumOverlayBrush; + spectrumOverlayRectangle.Fill = spectrumOverlayBrush; + } + break; + } + } + + private bool SelectionEllipseShouldBeLight() + { + // The selection ellipse should be light if and only if the chosen color + // contrasts more with black than it does with white. + // To find how much something contrasts with white, we use the equation + // for relative luminance, which is given by + // + // L = 0.2126 * Rg + 0.7152 * Gg + 0.0722 * Bg + // + // where Xg = { X/3294 if X <= 10, (R/269 + 0.0513)^2.4 otherwise } + // + // If L is closer to 1, then the color is closer to white; if it is closer to 0, + // then the color is closer to black. This is based on the fact that the human + // eye perceives green to be much brighter than red, which in turn is perceived to be + // brighter than blue. + // + // If the third dimension is value, then we won't be updating the spectrum's displayed colors, + // so in that case we should use a value of 1 when considering the backdrop + // for the selection ellipse. + Color displayedColor; + + if (Channels == ColorSpectrumChannels.HueSaturation || + Channels == ColorSpectrumChannels.SaturationHue) + { + HsvColor hsvColor = HsvColor; + Rgb color = (new Hsv(hsvColor.H, hsvColor.S, 1.0)).ToRgb(); + displayedColor = color.ToColor(hsvColor.A); + } + else + { + displayedColor = Color; + } + + double rg = displayedColor.R <= 10 ? displayedColor.R / 3294.0 : Math.Pow(displayedColor.R / 269.0 + 0.0513, 2.4); + double gg = displayedColor.G <= 10 ? displayedColor.G / 3294.0 : Math.Pow(displayedColor.G / 269.0 + 0.0513, 2.4); + double bg = displayedColor.B <= 10 ? displayedColor.B / 3294.0 : Math.Pow(displayedColor.B / 269.0 + 0.0513, 2.4); + + return ((0.2126 * rg + 0.7152 * gg + 0.0722 * bg) <= 0.5); + } + } +} From d12ee97e78f9ec1f1a15d0d61906d70317d62f94 Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 20 Mar 2022 23:09:03 -0400 Subject: [PATCH 046/416] Fix missing base.OnApplyTemplate() in SplitButton --- src/Avalonia.Controls/SplitButton/SplitButton.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Avalonia.Controls/SplitButton/SplitButton.cs b/src/Avalonia.Controls/SplitButton/SplitButton.cs index f1d07b2679..c21b70645b 100644 --- a/src/Avalonia.Controls/SplitButton/SplitButton.cs +++ b/src/Avalonia.Controls/SplitButton/SplitButton.cs @@ -227,6 +227,8 @@ namespace Avalonia.Controls /// protected override void OnApplyTemplate(TemplateAppliedEventArgs e) { + base.OnApplyTemplate(e); + UnregisterEvents(); UnregisterFlyoutEvents(Flyout); From 2edd7e4157ae3929fb3341f766785664395f4044 Mon Sep 17 00:00:00 2001 From: robloo Date: Mon, 21 Mar 2022 00:49:13 -0400 Subject: [PATCH 047/416] Add initial ColorSpectrum Fluent style/template --- .../ColorSpectrum/ColorSpectrum.cs | 41 ++-- .../Controls/ColorSpectrum.xaml | 183 ++++++++++++++++++ .../Controls/FluentControls.xaml | 5 +- 3 files changed, 208 insertions(+), 21 deletions(-) create mode 100644 src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs index e4096bcfda..47b113b0de 100644 --- a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -104,15 +104,15 @@ namespace Avalonia.Controls.Primitives UnregisterEvents(); - _layoutRoot = e.NameScope.Find("LayoutRoot"); - _sizingGrid = e.NameScope.Find("SizingGrid"); - _spectrumRectangle = e.NameScope.Find("SpectrumRectangle"); - _spectrumEllipse = e.NameScope.Find("SpectrumEllipse"); - _spectrumOverlayRectangle = e.NameScope.Find("SpectrumOverlayRectangle"); - _spectrumOverlayEllipse = e.NameScope.Find("SpectrumOverlayEllipse"); - _inputTarget = e.NameScope.Find("InputTarget"); - _selectionEllipsePanel = e.NameScope.Find("SelectionEllipsePanel"); - _colorNameToolTip = e.NameScope.Find("ColorNameToolTip"); + _layoutRoot = e.NameScope.Find("PART_LayoutRoot"); + _sizingGrid = e.NameScope.Find("PART_SizingGrid"); + _spectrumRectangle = e.NameScope.Find("PART_SpectrumRectangle"); + _spectrumEllipse = e.NameScope.Find("PART_SpectrumEllipse"); + _spectrumOverlayRectangle = e.NameScope.Find("PART_SpectrumOverlayRectangle"); + _spectrumOverlayEllipse = e.NameScope.Find("PART_SpectrumOverlayEllipse"); + _inputTarget = e.NameScope.Find("PART_InputTarget"); + _selectionEllipsePanel = e.NameScope.Find("PART_SelectionEllipsePanel"); + _colorNameToolTip = e.NameScope.Find("PART_ColorNameToolTip"); if (_layoutRoot != null) { @@ -149,7 +149,7 @@ namespace Avalonia.Controls.Primitives } UpdateEllipse(); - UpdateVisualState(useTransitions: false); + UpdatePseudoClasses(); } /// @@ -290,7 +290,7 @@ namespace Avalonia.Controls.Primitives } } - UpdateVisualState(useTransitions: true); + UpdatePseudoClasses(); } /// @@ -305,7 +305,7 @@ namespace Avalonia.Controls.Primitives } } - UpdateVisualState(useTransitions: true); + UpdatePseudoClasses(); } /// @@ -508,7 +508,10 @@ namespace Avalonia.Controls.Primitives CreateBitmapsAndColorMap(); } - private void UpdateVisualState(bool useTransitions) + /// + /// Updates the visual state of the control by applying latest PseudoClasses. + /// + private void UpdatePseudoClasses() { //if (m_isPointerPressed) //{ @@ -555,7 +558,7 @@ namespace Avalonia.Controls.Primitives HsvColor = newHsv.ToHsvColor(alpha); UpdateEllipse(); - UpdateVisualState(useTransitions: true); + UpdatePseudoClasses(); _updatingHsvColor = false; _updatingColor = false; @@ -822,7 +825,7 @@ namespace Avalonia.Controls.Primitives } } - UpdateVisualState(useTransitions: true); + UpdatePseudoClasses(); } private void OnLayoutRootSizeChanged() @@ -833,14 +836,14 @@ namespace Avalonia.Controls.Primitives private void OnInputTargetPointerEnter(object? sender, PointerEventArgs args) { _isPointerOver = true; - UpdateVisualState(useTransitions: true); + UpdatePseudoClasses(); args.Handled = true; } private void OnInputTargetPointerLeave(object? sender, PointerEventArgs args) { _isPointerOver = false; - UpdateVisualState(useTransitions: true); + UpdatePseudoClasses(); args.Handled = true; } @@ -858,7 +861,7 @@ namespace Avalonia.Controls.Primitives args.Pointer.Capture(inputTarget); UpdateColorFromPoint(args.GetCurrentPoint(inputTarget)); - UpdateVisualState(useTransitions: true); + UpdatePseudoClasses(); UpdateEllipse(); args.Handled = true; @@ -881,7 +884,7 @@ namespace Avalonia.Controls.Primitives _shouldShowLargeSelection = false; args.Pointer.Capture(null); - UpdateVisualState(useTransitions: true); + UpdatePseudoClasses(); UpdateEllipse(); args.Handled = true; diff --git a/src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml b/src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml new file mode 100644 index 0000000000..c4ca09d439 --- /dev/null +++ b/src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml @@ -0,0 +1,183 @@ + + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml b/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml index 16a6cc9c14..921826bab1 100644 --- a/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml @@ -9,6 +9,7 @@ + @@ -59,8 +60,8 @@ - - + + From edbd3de9a550673b6c20cbccc57a9cbfbb9225be Mon Sep 17 00:00:00 2001 From: robloo Date: Mon, 21 Mar 2022 00:52:35 -0400 Subject: [PATCH 048/416] Add initial ColorPickerPage to ControlCatalog --- samples/ControlCatalog/MainView.xaml | 3 +++ .../ControlCatalog/Pages/ColorPickerPage.xaml | 23 +++++++++++++++++++ .../Pages/ColorPickerPage.xaml.cs | 19 +++++++++++++++ 3 files changed, 45 insertions(+) create mode 100644 samples/ControlCatalog/Pages/ColorPickerPage.xaml create mode 100644 samples/ControlCatalog/Pages/ColorPickerPage.xaml.cs diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml index facce2aa82..b6f118a721 100644 --- a/samples/ControlCatalog/MainView.xaml +++ b/samples/ControlCatalog/MainView.xaml @@ -43,6 +43,9 @@ + + + diff --git a/samples/ControlCatalog/Pages/ColorPickerPage.xaml b/samples/ControlCatalog/Pages/ColorPickerPage.xaml new file mode 100644 index 0000000000..31faf5ff09 --- /dev/null +++ b/samples/ControlCatalog/Pages/ColorPickerPage.xaml @@ -0,0 +1,23 @@ + + + + + + + diff --git a/samples/ControlCatalog/Pages/ColorPickerPage.xaml.cs b/samples/ControlCatalog/Pages/ColorPickerPage.xaml.cs new file mode 100644 index 0000000000..6e017e381f --- /dev/null +++ b/samples/ControlCatalog/Pages/ColorPickerPage.xaml.cs @@ -0,0 +1,19 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; + +namespace ControlCatalog.Pages +{ + public partial class ColorPickerPage : UserControl + { + public ColorPickerPage() + { + InitializeComponent(); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + } +} From 5ac00ef54d981a5e5dc23aca5ca0153c29001ee7 Mon Sep 17 00:00:00 2001 From: robloo Date: Mon, 21 Mar 2022 10:46:15 -0400 Subject: [PATCH 049/416] Add new EnumValueEqualsConverter --- .../Converters/EnumValueEqualsConverter.cs | 54 +++++++++++++++++++ .../Converters/MarginMultiplierConverter.cs | 1 - 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 src/Avalonia.Controls/Converters/EnumValueEqualsConverter.cs diff --git a/src/Avalonia.Controls/Converters/EnumValueEqualsConverter.cs b/src/Avalonia.Controls/Converters/EnumValueEqualsConverter.cs new file mode 100644 index 0000000000..1a33a82ca4 --- /dev/null +++ b/src/Avalonia.Controls/Converters/EnumValueEqualsConverter.cs @@ -0,0 +1,54 @@ +using System; +using System.Globalization; +using Avalonia.Data.Converters; + +namespace Avalonia.Controls.Converters +{ + /// + /// Converter that checks if an enum value is equal to the given parameter enum value. + /// + public class EnumValueEqualsConverter : IValueConverter + { + /// + public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) + { + // Note: Unlike string comparisons, null/empty is not supported + // Both 'value' and 'parameter' must exist and if both are missing they are not considered equal + if (value != null && + parameter != null) + { + Type type = value.GetType(); + + if (type.IsEnum) + { + var valueStr = value?.ToString(); + var paramStr = parameter?.ToString(); + + if (string.Equals(valueStr, paramStr, StringComparison.OrdinalIgnoreCase)) + { + return true; + } + } + + /* + // TODO: When .net Standard 2.0 is no longer supported the code can be changed to below + // This is a little more type safe + if (type.IsEnum && + Enum.TryParse(type, value?.ToString(), true, out object? valueEnum) && + Enum.TryParse(type, parameter?.ToString(), true, out object? paramEnum)) + { + return valueEnum == paramEnum; + } + */ + } + + return false; + } + + /// + public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) + { + throw new System.NotImplementedException(); + } + } +} diff --git a/src/Avalonia.Controls/Converters/MarginMultiplierConverter.cs b/src/Avalonia.Controls/Converters/MarginMultiplierConverter.cs index b0c30ea11f..7931b63d8e 100644 --- a/src/Avalonia.Controls/Converters/MarginMultiplierConverter.cs +++ b/src/Avalonia.Controls/Converters/MarginMultiplierConverter.cs @@ -35,7 +35,6 @@ namespace Avalonia.Controls.Converters Bottom ? Indent * thicknessDepth.Bottom : 0); } return new Thickness(0); - } public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) From e828d42952825975b9ab48e03b7c2d5cbace17b9 Mon Sep 17 00:00:00 2001 From: robloo Date: Mon, 21 Mar 2022 10:49:23 -0400 Subject: [PATCH 050/416] Implement Box/Ring shape states in the template --- .../ColorSpectrum/ColorSpectrum.cs | 1 - .../Controls/ColorSpectrum.xaml | 49 +++++-------------- 2 files changed, 13 insertions(+), 37 deletions(-) diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs index 47b113b0de..ef07e56f10 100644 --- a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -526,7 +526,6 @@ namespace Avalonia.Controls.Primitives // VisualStateManager.GoToState(this, "Normal", useTransitions); //} - //VisualStateManager.GoToState(this, m_shapeFromLastBitmapCreation == ColorSpectrumShape.Box ? "BoxSelected" : "RingSelected", useTransitions); //VisualStateManager.GoToState(this, SelectionEllipseShouldBeLight() ? "SelectionEllipseLight" : "SelectionEllipseDark", useTransitions); //if (IsEnabled && FocusState != FocusState.Unfocused) diff --git a/src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml b/src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml index c4ca09d439..0d6ed59c68 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml @@ -1,11 +1,13 @@  + x:CompileBindings="True" + xmlns:converters="using:Avalonia.Controls.Converters"> + + + + + + + + @@ -112,11 +110,19 @@ + + + + + + + - - From 7fb48a8a2578d65c5757cdd4108fe96b2c09d8a0 Mon Sep 17 00:00:00 2001 From: robloo Date: Mon, 21 Mar 2022 13:41:20 -0400 Subject: [PATCH 061/416] Sync with WinUI at 09267531b807707138addc9bef3c8e9fa340615a --- .../ColorSpectrum/ColorSpectrum.cs | 41 ++++++++++++++++--- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs index 775a85dfec..3b8d781275 100644 --- a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -191,6 +191,8 @@ namespace Avalonia.Controls.Primitives HsvChannel incrementChannel = HsvChannel.Hue; + bool isSaturationValue = false; + if (key == Key.Left || key == Key.Right) { @@ -201,8 +203,10 @@ namespace Avalonia.Controls.Primitives incrementChannel = HsvChannel.Hue; break; - case ColorSpectrumChannels.SaturationHue: case ColorSpectrumChannels.SaturationValue: + isSaturationValue = true; + goto case ColorSpectrumChannels.SaturationHue; + case ColorSpectrumChannels.SaturationHue: incrementChannel = HsvChannel.Saturation; break; @@ -227,8 +231,10 @@ namespace Avalonia.Controls.Primitives incrementChannel = HsvChannel.Saturation; break; - case ColorSpectrumChannels.HueValue: case ColorSpectrumChannels.SaturationValue: + isSaturationValue = true; + goto case ColorSpectrumChannels.HueValue; + case ColorSpectrumChannels.HueValue: incrementChannel = HsvChannel.Value; break; } @@ -264,6 +270,23 @@ namespace Avalonia.Controls.Primitives IncrementDirection.Lower : IncrementDirection.Higher; + // Image is flipped in RightToLeft, so we need to invert direction in that case. + // The combination saturation and value is also flipped, so we need to invert in that case too. + // If both are false, we don't need to invert. + // If both are true, we would invert twice, so not invert at all. + if ((FlowDirection == FlowDirection.RightToLeft) != isSaturationValue && + (key == Key.Left || key == Key.Right)) + { + if (direction == IncrementDirection.Higher) + { + direction = IncrementDirection.Lower; + } + else + { + direction = IncrementDirection.Higher; + } + } + IncrementAmount amount = isControlDown ? IncrementAmount.Large : IncrementAmount.Small; HsvColor hsvColor = HsvColor; @@ -404,10 +427,18 @@ namespace Avalonia.Controls.Primitives { Color newColor = Color; - if (_oldColor.A != newColor.A || + bool colorChanged = + _oldColor.A != newColor.A || _oldColor.R != newColor.R || _oldColor.G != newColor.G || - _oldColor.B != newColor.B) + _oldColor.B != newColor.B; + + bool areBothColorsBlack = + (_oldColor.R == newColor.R && newColor.R == 0) || + (_oldColor.G == newColor.G && newColor.G == 0) || + (_oldColor.B == newColor.B && newColor.B == 0); + + if (colorChanged || areBothColorsBlack) { var colorChangedEventArgs = new ColorChangedEventArgs(); @@ -745,7 +776,7 @@ namespace Avalonia.Controls.Primitives // we inverted the direction of that axis in order to put more hue on the outside of the ring, // so we need to do similarly here when positioning the ellipse. if (_componentsFromLastBitmapCreation == ColorSpectrumChannels.HueSaturation || - _componentsFromLastBitmapCreation == ColorSpectrumChannels.ValueHue) + _componentsFromLastBitmapCreation == ColorSpectrumChannels.SaturationHue) { sThetaValue = 360 - sThetaValue; sRValue = -sRValue - 1; From baec9c52fc03d9d41a26d5cbe48b050831809166 Mon Sep 17 00:00:00 2001 From: robloo Date: Mon, 21 Mar 2022 18:31:52 -0400 Subject: [PATCH 062/416] Fix ToolTip handling --- .../ColorSpectrum/ColorSpectrum.cs | 26 ++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs index 3b8d781275..42e45c61eb 100644 --- a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -308,12 +308,10 @@ namespace Avalonia.Controls.Primitives protected override void OnGotFocus(GotFocusEventArgs e) { // We only want to bother with the color name tool tip if we can provide color names. - if (_colorNameToolTip is ToolTip colorNameToolTip) + if (_colorNameToolTip != null && + ColorHelpers.ToDisplayNameExists) { - if (ColorHelpers.ToDisplayNameExists) - { - //colorNameToolTip.IsOpen = true; - } + ToolTip.SetIsOpen(_colorNameToolTip, true); } UpdatePseudoClasses(); @@ -323,12 +321,10 @@ namespace Avalonia.Controls.Primitives protected override void OnLostFocus(RoutedEventArgs e) { // We only want to bother with the color name tool tip if we can provide color names. - if (_colorNameToolTip is ToolTip colorNameToolTip) + if (_colorNameToolTip != null && + ColorHelpers.ToDisplayNameExists) { - if (ColorHelpers.ToDisplayNameExists) - { - //colorNameToolTip.IsOpen = false; - } + ToolTip.SetIsOpen(_colorNameToolTip, false); } UpdatePseudoClasses(); @@ -449,9 +445,9 @@ namespace Avalonia.Controls.Primitives if (ColorHelpers.ToDisplayNameExists) { - if (_colorNameToolTip is ToolTip colorNameToolTip) + if (_colorNameToolTip != null) { - colorNameToolTip.Content = ColorHelpers.ToDisplayName(newColor); + _colorNameToolTip.Content = ColorHelpers.ToDisplayName(newColor); } } } @@ -832,12 +828,12 @@ namespace Avalonia.Controls.Primitives // We only want to bother with the color name tool tip if we can provide color names. if (ColorHelpers.ToDisplayNameExists) { - if (_colorNameToolTip is ToolTip colorNameToolTip) + if (_colorNameToolTip != null) { // ToolTip doesn't currently provide any way to re-run its placement logic if its placement target moves, // so toggling IsEnabled induces it to do that without incurring any visual glitches. - colorNameToolTip.IsEnabled = false; - colorNameToolTip.IsEnabled = true; + _colorNameToolTip.IsEnabled = false; + _colorNameToolTip.IsEnabled = true; } } From 9f132d4ac0d7980cd98a0a2b9cc971ed0b376a04 Mon Sep 17 00:00:00 2001 From: robloo Date: Mon, 21 Mar 2022 18:44:17 -0400 Subject: [PATCH 063/416] Simplify by removing unnecessary single-use methods --- .../ColorSpectrum/ColorSpectrum.cs | 238 ++++++++---------- 1 file changed, 99 insertions(+), 139 deletions(-) diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs index 42e45c61eb..892fa76fc9 100644 --- a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -119,7 +119,10 @@ namespace Avalonia.Controls.Primitives if (_layoutRoot != null) { - _layoutRootDisposable = _layoutRoot.GetObservable(BoundsProperty).Subscribe(_ => OnLayoutRootSizeChanged()); + _layoutRootDisposable = _layoutRoot.GetObservable(BoundsProperty).Subscribe(_ => + { + CreateBitmapsAndColorMap(); + }); } if (_inputTarget != null) @@ -335,74 +338,121 @@ namespace Avalonia.Controls.Primitives { if (change.Property == ColorProperty) { - OnColorChanged(change); + // If we're in the process of internally updating the color, + // then we don't want to respond to the Color property changing. + if (!_updatingColor) + { + Color color = Color; + + _updatingHsvColor = true; + Hsv newHsv = (new Rgb(color)).ToHsv(); + HsvColor = newHsv.ToHsvColor(color.A / 255.0); + _updatingHsvColor = false; + + UpdateEllipse(); + UpdateBitmapSources(); + } + + _oldColor = change.OldValue.GetValueOrDefault(); } else if (change.Property == HsvColorProperty) { - OnHsvColorChanged(change); + // If we're in the process of internally updating the HSV color, + // then we don't want to respond to the HsvColor property changing. + if (!_updatingHsvColor) + { + SetColor(); + } + + _oldHsvColor = change.OldValue.GetValueOrDefault(); } - else if ( - change.Property == MinHueProperty || - change.Property == MaxHueProperty) + else if (change.Property == MinHueProperty || + change.Property == MaxHueProperty) { - OnMinMaxHueChanged(); + int minHue = MinHue; + int maxHue = MaxHue; + + if (minHue < 0 || minHue > 359) + { + throw new ArgumentException("MinHue must be between 0 and 359."); + } + else if (maxHue < 0 || maxHue > 359) + { + throw new ArgumentException("MaxHue must be between 0 and 359."); + } + + ColorSpectrumChannels channels = Channels; + + // If hue is one of the axes in the spectrum bitmap, then we'll need to regenerate it + // if the maximum or minimum value has changed. + if (channels != ColorSpectrumChannels.SaturationValue && + channels != ColorSpectrumChannels.ValueSaturation) + { + CreateBitmapsAndColorMap(); + } } - else if ( - change.Property == MinSaturationProperty || - change.Property == MaxSaturationProperty) + else if (change.Property == MinSaturationProperty || + change.Property == MaxSaturationProperty) { - OnMinMaxSaturationChanged(); + int minSaturation = MinSaturation; + int maxSaturation = MaxSaturation; + + if (minSaturation < 0 || minSaturation > 100) + { + throw new ArgumentException("MinSaturation must be between 0 and 100."); + } + else if (maxSaturation < 0 || maxSaturation > 100) + { + throw new ArgumentException("MaxSaturation must be between 0 and 100."); + } + + ColorSpectrumChannels channels = Channels; + + // If value is one of the axes in the spectrum bitmap, then we'll need to regenerate it + // if the maximum or minimum value has changed. + if (channels != ColorSpectrumChannels.HueValue && + channels != ColorSpectrumChannels.ValueHue) + { + CreateBitmapsAndColorMap(); + } } - else if ( - change.Property == MinValueProperty || - change.Property == MaxValueProperty) + else if (change.Property == MinValueProperty || + change.Property == MaxValueProperty) { - OnMinMaxValueChanged(); + int minValue = MinValue; + int maxValue = MaxValue; + + if (minValue < 0 || minValue > 100) + { + throw new ArgumentException("MinValue must be between 0 and 100."); + } + else if (maxValue < 0 || maxValue > 100) + { + throw new ArgumentException("MaxValue must be between 0 and 100."); + } + + ColorSpectrumChannels channels = Channels; + + // If value is one of the axes in the spectrum bitmap, then we'll need to regenerate it + // if the maximum or minimum value has changed. + if (channels != ColorSpectrumChannels.HueSaturation && + channels != ColorSpectrumChannels.SaturationHue) + { + CreateBitmapsAndColorMap(); + } } else if (change.Property == ShapeProperty) { - OnShapeChanged(); + CreateBitmapsAndColorMap(); } else if (change.Property == ChannelsProperty) { - OnComponentsChanged(); + CreateBitmapsAndColorMap(); } base.OnPropertyChanged(change); } - private void OnColorChanged(AvaloniaPropertyChangedEventArgs change) - { - // If we're in the process of internally updating the color, - // then we don't want to respond to the Color property changing. - if (!_updatingColor) - { - Color color = Color; - - _updatingHsvColor = true; - Hsv newHsv = (new Rgb(color)).ToHsv(); - HsvColor = newHsv.ToHsvColor(color.A / 255.0); - _updatingHsvColor = false; - - UpdateEllipse(); - UpdateBitmapSources(); - } - - _oldColor = change.OldValue.GetValueOrDefault(); - } - - private void OnHsvColorChanged(AvaloniaPropertyChangedEventArgs change) - { - // If we're in the process of internally updating the HSV color, - // then we don't want to respond to the HsvColor property changing. - if (!_updatingHsvColor) - { - SetColor(); - } - - _oldHsvColor = change.OldValue.GetValueOrDefault(); - } - private void SetColor() { HsvColor hsvColor = HsvColor; @@ -453,91 +503,6 @@ namespace Avalonia.Controls.Primitives } } - protected void OnMinMaxHueChanged() - { - int minHue = MinHue; - int maxHue = MaxHue; - - if (minHue < 0 || minHue > 359) - { - throw new ArgumentException("MinHue must be between 0 and 359."); - } - else if (maxHue < 0 || maxHue > 359) - { - throw new ArgumentException("MaxHue must be between 0 and 359."); - } - - ColorSpectrumChannels channels = Channels; - - // If hue is one of the axes in the spectrum bitmap, then we'll need to regenerate it - // if the maximum or minimum value has changed. - if (channels != ColorSpectrumChannels.SaturationValue && - channels != ColorSpectrumChannels.ValueSaturation) - { - CreateBitmapsAndColorMap(); - } - } - - protected void OnMinMaxSaturationChanged() - { - int minSaturation = MinSaturation; - int maxSaturation = MaxSaturation; - - if (minSaturation < 0 || minSaturation > 100) - { - throw new ArgumentException("MinSaturation must be between 0 and 100."); - } - else if (maxSaturation < 0 || maxSaturation > 100) - { - throw new ArgumentException("MaxSaturation must be between 0 and 100."); - } - - ColorSpectrumChannels channels = Channels; - - // If value is one of the axes in the spectrum bitmap, then we'll need to regenerate it - // if the maximum or minimum value has changed. - if (channels != ColorSpectrumChannels.HueValue && - channels != ColorSpectrumChannels.ValueHue) - { - CreateBitmapsAndColorMap(); - } - } - - private void OnMinMaxValueChanged() - { - int minValue = MinValue; - int maxValue = MaxValue; - - if (minValue < 0 || minValue > 100) - { - throw new ArgumentException("MinValue must be between 0 and 100."); - } - else if (maxValue < 0 || maxValue > 100) - { - throw new ArgumentException("MaxValue must be between 0 and 100."); - } - - ColorSpectrumChannels channels = Channels; - - // If value is one of the axes in the spectrum bitmap, then we'll need to regenerate it - // if the maximum or minimum value has changed. - if (channels != ColorSpectrumChannels.HueSaturation && - channels != ColorSpectrumChannels.SaturationHue) - { - CreateBitmapsAndColorMap(); - } - } - - private void OnShapeChanged() - { - CreateBitmapsAndColorMap(); - } - - private void OnComponentsChanged() - { - CreateBitmapsAndColorMap(); - } - /// /// Updates the visual state of the control by applying latest PseudoClasses. /// @@ -840,11 +805,6 @@ namespace Avalonia.Controls.Primitives UpdatePseudoClasses(); } - private void OnLayoutRootSizeChanged() - { - CreateBitmapsAndColorMap(); - } - private void OnInputTargetPointerEnter(object? sender, PointerEventArgs args) { _isPointerOver = true; From d4dbd92ca882f9e76dd1729cfe2b242eef7e745a Mon Sep 17 00:00:00 2001 From: robloo Date: Tue, 22 Mar 2022 18:09:20 -0400 Subject: [PATCH 064/416] Support FlowDirection and other simplifications --- .../ColorSpectrum/ColorSpectrum.Properties.cs | 4 +++- .../ColorSpectrum/ColorSpectrum.cs | 24 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs index e95c63da33..71fc887bee 100644 --- a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs +++ b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs @@ -69,7 +69,9 @@ namespace Avalonia.Controls.Primitives /// Defines the property. /// public static readonly StyledProperty HsvColorProperty = - AvaloniaProperty.Register(nameof(HsvColor), new HsvColor(1, 0, 0, 1)); + AvaloniaProperty.Register( + nameof(HsvColor), + new HsvColor(1, 0, 0, 1)); /// /// Gets or sets the maximum value of the Hue channel in the range from 0..359. diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs index 892fa76fc9..a9497b98f1 100644 --- a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -40,6 +40,7 @@ namespace Avalonia.Controls.Primitives private List _hsvValues = new List(); private IDisposable? _layoutRootDisposable; + private IDisposable? _selectionEllipsePanelDisposable; // XAML template parts private Grid? _layoutRoot; @@ -134,18 +135,18 @@ namespace Avalonia.Controls.Primitives _inputTarget.PointerReleased += OnInputTargetPointerReleased; } - if (ColorHelpers.ToDisplayNameExists) + if (ColorHelpers.ToDisplayNameExists && + _colorNameToolTip != null) { - if (_colorNameToolTip != null) - { - _colorNameToolTip.Content = ColorHelpers.ToDisplayName(Color); - } + _colorNameToolTip.Content = ColorHelpers.ToDisplayName(Color); } if (_selectionEllipsePanel != null) { - // TODO: After FlowDirection PR is merged: https://github.com/AvaloniaUI/Avalonia/pull/7810 - //m_selectionEllipsePanel.RegisterPropertyChangedCallback(FrameworkElement.FlowDirectionProperty, OnSelectionEllipseFlowDirectionChanged); + _selectionEllipsePanelDisposable = _selectionEllipsePanel.GetObservable(FlowDirectionProperty).Subscribe(_ => + { + UpdateEllipse(); + }); } // If we haven't yet created our bitmaps, do so now. @@ -166,6 +167,9 @@ namespace Avalonia.Controls.Primitives _layoutRootDisposable?.Dispose(); _layoutRootDisposable = null; + _selectionEllipsePanelDisposable?.Dispose(); + _selectionEllipsePanelDisposable = null; + if (_inputTarget != null) { _inputTarget.PointerEnter -= OnInputTargetPointerEnter; @@ -862,12 +866,6 @@ namespace Avalonia.Controls.Primitives args.Handled = true; } - // TODO: After FlowDirection PR is merged: https://github.com/AvaloniaUI/Avalonia/pull/7810 - //private void OnSelectionEllipseFlowDirectionChanged(DependencyObject o, DependencyProperty p) - //{ - // UpdateEllipse(); - //} - private async void CreateBitmapsAndColorMap() { if (_layoutRoot == null || From 41d2b0dd1cd92e7b1528f5410560d5b6b5c031db Mon Sep 17 00:00:00 2001 From: robloo Date: Tue, 22 Mar 2022 21:37:33 -0400 Subject: [PATCH 065/416] Remove ContrastBrushConverter for now (part of ColorSlider) --- .../Converters/ContrastBrushConverter.cs | 88 ------------------- 1 file changed, 88 deletions(-) delete mode 100644 src/Avalonia.Controls/ColorPicker/Converters/ContrastBrushConverter.cs diff --git a/src/Avalonia.Controls/ColorPicker/Converters/ContrastBrushConverter.cs b/src/Avalonia.Controls/ColorPicker/Converters/ContrastBrushConverter.cs deleted file mode 100644 index 79c38ea53f..0000000000 --- a/src/Avalonia.Controls/ColorPicker/Converters/ContrastBrushConverter.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System; -using System.Globalization; -using Avalonia; -using Avalonia.Controls.Primitives; -using Avalonia.Data.Converters; -using Avalonia.Media; - -namespace FinancialManager.UWP -{ - /// - /// Gets a color, either black or white, depending on the perceived brightness of the supplied color. - /// - public class ContrastBrushConverter : IValueConverter - { - /// - /// Gets or sets the alpha channel threshold below which a default color is used instead of black/white. - /// - public byte AlphaThreshold { get; set; } = 128; - - /// - public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) - { - Color comparisonColor; - Color? defaultColor = null; - - // Get the changing color to compare against - if (value is Color valueColor) - { - comparisonColor = valueColor; - } - else if (value is HsvColor valueHsvColor) - { - comparisonColor = valueHsvColor.ToRgb(); - } - else if (value is SolidColorBrush valueBrush) - { - comparisonColor = valueBrush.Color; - } - else - { - // Invalid color value provided - return AvaloniaProperty.UnsetValue; - } - - // Get the default color when transparency is high - if (parameter is Color parameterColor) - { - defaultColor = parameterColor; - } - else if (parameter is HsvColor parameterHsvColor) - { - defaultColor = parameterHsvColor.ToRgb(); - } - else if (parameter is SolidColorBrush parameterBrush) - { - defaultColor = parameterBrush.Color; - } - - if (comparisonColor.A < AlphaThreshold && - defaultColor.HasValue) - { - // If the transparency is less than the threshold just use the default brush - // This can commonly be something like the TextControlForeground brush - return new SolidColorBrush(defaultColor.Value); - } - else - { - // Chose a white/black brush based on contrast to the base color - if (ColorHelpers.GetRelativeLuminance(comparisonColor) > 0.5) - { - // Bright color, use a dark for contrast - return new SolidColorBrush(Colors.Black); - } - else - { - // Dark color, use a light for contrast - return new SolidColorBrush(Colors.White); - } - } - } - - /// - public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) - { - throw new NotImplementedException(); - } - } -} From 9ad87658f194757a66b62251f2ebb02308035dd9 Mon Sep 17 00:00:00 2001 From: Andrej Bunjac Date: Thu, 24 Mar 2022 14:16:18 +0100 Subject: [PATCH 066/416] Added fixes for Margin, Padding and Thickness properties with UseLayoutRounding = true. --- src/Avalonia.Controls/Border.cs | 25 +++++++- .../Presenters/ContentPresenter.cs | 40 +++++++++++-- src/Avalonia.Layout/LayoutHelper.cs | 59 ++++++++++++++++++- src/Avalonia.Layout/Layoutable.cs | 14 ++++- 4 files changed, 128 insertions(+), 10 deletions(-) diff --git a/src/Avalonia.Controls/Border.cs b/src/Avalonia.Controls/Border.cs index ee3be1d5b3..ce64570dc8 100644 --- a/src/Avalonia.Controls/Border.cs +++ b/src/Avalonia.Controls/Border.cs @@ -169,13 +169,36 @@ namespace Avalonia.Controls set => SetValue(BoxShadowProperty, value); } + private Thickness _layoutThickness = default; + + private Thickness LayoutThickness + { + get + { + if (_layoutThickness == default) + { + var borderThickness = BorderThickness; + + if (UseLayoutRounding) + { + var scale = LayoutHelper.GetLayoutScale(this); + borderThickness = LayoutHelper.RoundLayoutThickness(BorderThickness, scale, scale); + } + + _layoutThickness = borderThickness; + } + + return _layoutThickness; + } + } + /// /// Renders the control. /// /// The drawing context. public override void Render(DrawingContext context) { - _borderRenderHelper.Render(context, Bounds.Size, BorderThickness, CornerRadius, Background, BorderBrush, + _borderRenderHelper.Render(context, Bounds.Size, LayoutThickness, CornerRadius, Background, BorderBrush, BoxShadow, BorderDashOffset, BorderLineCap, BorderLineJoin, BorderDashArray); } diff --git a/src/Avalonia.Controls/Presenters/ContentPresenter.cs b/src/Avalonia.Controls/Presenters/ContentPresenter.cs index 93acd88fb1..bbb772a4ce 100644 --- a/src/Avalonia.Controls/Presenters/ContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ContentPresenter.cs @@ -329,10 +329,33 @@ namespace Avalonia.Controls.Presenters InvalidateMeasure(); } + private Thickness _layoutThickness = default; + + private Thickness LayoutThickness + { + get + { + if (_layoutThickness == default) + { + var borderThickness = BorderThickness; + + if (UseLayoutRounding) + { + var scale = LayoutHelper.GetLayoutScale(this); + borderThickness = LayoutHelper.RoundLayoutThickness(BorderThickness, scale, scale); + } + + _layoutThickness = borderThickness; + } + + return _layoutThickness; + } + } + /// public override void Render(DrawingContext context) { - _borderRenderer.Render(context, Bounds.Size, BorderThickness, CornerRadius, Background, BorderBrush, + _borderRenderer.Render(context, Bounds.Size, LayoutThickness, CornerRadius, Background, BorderBrush, BoxShadow); } @@ -400,13 +423,22 @@ namespace Avalonia.Controls.Presenters { if (Child == null) return finalSize; - var padding = Padding + BorderThickness; + var useLayoutRounding = UseLayoutRounding; + var scale = LayoutHelper.GetLayoutScale(this); + var padding = Padding; + var borderThickness = BorderThickness; + + if (useLayoutRounding) + { + padding = LayoutHelper.RoundLayoutThickness(padding, scale, scale); + borderThickness = LayoutHelper.RoundLayoutThickness(borderThickness, scale, scale); + } + + padding += borderThickness; var horizontalContentAlignment = HorizontalContentAlignment; var verticalContentAlignment = VerticalContentAlignment; - var useLayoutRounding = UseLayoutRounding; var availableSize = finalSize; var sizeForChild = availableSize; - var scale = LayoutHelper.GetLayoutScale(this); var originX = offset.X; var originY = offset.Y; diff --git a/src/Avalonia.Layout/LayoutHelper.cs b/src/Avalonia.Layout/LayoutHelper.cs index d4154a6d0c..d24be57d2b 100644 --- a/src/Avalonia.Layout/LayoutHelper.cs +++ b/src/Avalonia.Layout/LayoutHelper.cs @@ -52,16 +52,44 @@ namespace Avalonia.Layout public static Size ArrangeChild(ILayoutable? child, Size availableSize, Thickness padding, Thickness borderThickness) { - return ArrangeChild(child, availableSize, padding + borderThickness); + if (IsParentLayoutRounded(child, out double scale)) + { + padding = RoundLayoutThickness(padding, scale, scale); + borderThickness = RoundLayoutThickness(borderThickness, scale, scale); + } + + return ArrangeChildInternal(child, availableSize, padding + borderThickness); } public static Size ArrangeChild(ILayoutable? child, Size availableSize, Thickness padding) + { + if(IsParentLayoutRounded(child, out double scale)) + padding = RoundLayoutThickness(padding, scale, scale); + + return ArrangeChildInternal(child, availableSize, padding); + } + + private static Size ArrangeChildInternal(ILayoutable? child, Size availableSize, Thickness padding) { child?.Arrange(new Rect(availableSize).Deflate(padding)); return availableSize; } + private static bool IsParentLayoutRounded(ILayoutable? child, out double scale) + { + var layoutableParent = (ILayoutable?)child?.GetVisualParent(); + + if (layoutableParent == null || !((Layoutable)layoutableParent).UseLayoutRounding) + { + scale = 1.0; + return false; + } + + scale = GetLayoutScale(layoutableParent); + return true; + } + /// /// Invalidates measure for given control and all visual children recursively. /// @@ -126,6 +154,32 @@ namespace Avalonia.Layout return new Size(RoundLayoutValue(size.Width, dpiScaleX), RoundLayoutValue(size.Height, dpiScaleY)); } + /// + /// Rounds a thickness to integer values for layout purposes, compensating for high DPI screen + /// coordinates. + /// + /// Input thickness. + /// DPI along x-dimension. + /// DPI along y-dimension. + /// Value of thickness that will be rounded under screen DPI. + /// + /// This is a layout helper method. It takes DPI into account and also does not return + /// the rounded value if it is unacceptable for layout, e.g. Infinity or NaN. It's a helper + /// associated with the UseLayoutRounding property and should not be used as a general rounding + /// utility. + /// + public static Thickness RoundLayoutThickness(Thickness thickness, double dpiScaleX, double dpiScaleY) + { + return new Thickness( + RoundLayoutValue(thickness.Left, dpiScaleX), + RoundLayoutValue(thickness.Top, dpiScaleY), + RoundLayoutValue(thickness.Right, dpiScaleX), + RoundLayoutValue(thickness.Bottom, dpiScaleY) + ); + } + + + /// /// Calculates the value to be used for layout rounding at high DPI. /// @@ -163,8 +217,7 @@ namespace Avalonia.Layout return newValue; } - - + /// /// Calculates the min and max height for a control. Ported from WPF. /// diff --git a/src/Avalonia.Layout/Layoutable.cs b/src/Avalonia.Layout/Layoutable.cs index 09e0c4263a..23a76f6ee2 100644 --- a/src/Avalonia.Layout/Layoutable.cs +++ b/src/Avalonia.Layout/Layoutable.cs @@ -643,17 +643,27 @@ namespace Avalonia.Layout { if (IsVisible) { + var useLayoutRounding = UseLayoutRounding; + var scale = LayoutHelper.GetLayoutScale(this); + var margin = Margin; var originX = finalRect.X + margin.Left; var originY = finalRect.Y + margin.Top; + + // Margin has to be treated separately because the layout rounding function is not linear + // f(a + b) != f(a) + f(b) + // If the margin isn't pre-rounded some sizes will be offset by 1 pixel in certain scales. + if (UseLayoutRounding) + { + margin = LayoutHelper.RoundLayoutThickness(margin, scale, scale); + } + var availableSizeMinusMargins = new Size( Math.Max(0, finalRect.Width - margin.Left - margin.Right), Math.Max(0, finalRect.Height - margin.Top - margin.Bottom)); var horizontalAlignment = HorizontalAlignment; var verticalAlignment = VerticalAlignment; var size = availableSizeMinusMargins; - var scale = LayoutHelper.GetLayoutScale(this); - var useLayoutRounding = UseLayoutRounding; if (horizontalAlignment != HorizontalAlignment.Stretch) { From d3551ce99fa850c840f8730fafcfab256632db01 Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 27 Mar 2022 11:59:33 -0400 Subject: [PATCH 067/416] Use the new TemplatePartAttribute --- .../ColorSpectrum/ColorSpectrum.cs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs index a9497b98f1..9d04d1385b 100644 --- a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -20,6 +20,15 @@ namespace Avalonia.Controls.Primitives /// /// A two dimensional spectrum for color selection. /// + [TemplatePart(Name = "PART_ColorNameToolTip", Type = typeof(ToolTip))] + [TemplatePart(Name = "PART_InputTarget", Type = typeof(Canvas))] + [TemplatePart(Name = "PART_LayoutRoot", Type = typeof(Grid))] + [TemplatePart(Name = "PART_SelectionEllipsePanel", Type = typeof(Panel))] + [TemplatePart(Name = "PART_SizingGrid", Type = typeof(Grid))] + [TemplatePart(Name = "PART_SpectrumEllipse", Type = typeof(Ellipse))] + [TemplatePart(Name = "PART_SpectrumRectangle", Type = typeof(Rectangle))] + [TemplatePart(Name = "PART_SpectrumOverlayEllipse", Type = typeof(Ellipse))] + [TemplatePart(Name = "PART_SpectrumOverlayRectangle", Type = typeof(Rectangle))] [PseudoClasses(pcPressed, pcLargeSelector, pcLightSelector)] public partial class ColorSpectrum : TemplatedControl { @@ -108,15 +117,15 @@ namespace Avalonia.Controls.Primitives UnregisterEvents(); + _colorNameToolTip = e.NameScope.Find("PART_ColorNameToolTip"); + _inputTarget = e.NameScope.Find("PART_InputTarget"); _layoutRoot = e.NameScope.Find("PART_LayoutRoot"); + _selectionEllipsePanel = e.NameScope.Find("PART_SelectionEllipsePanel"); _sizingGrid = e.NameScope.Find("PART_SizingGrid"); - _spectrumRectangle = e.NameScope.Find("PART_SpectrumRectangle"); _spectrumEllipse = e.NameScope.Find("PART_SpectrumEllipse"); - _spectrumOverlayRectangle = e.NameScope.Find("PART_SpectrumOverlayRectangle"); + _spectrumRectangle = e.NameScope.Find("PART_SpectrumRectangle"); _spectrumOverlayEllipse = e.NameScope.Find("PART_SpectrumOverlayEllipse"); - _inputTarget = e.NameScope.Find("PART_InputTarget"); - _selectionEllipsePanel = e.NameScope.Find("PART_SelectionEllipsePanel"); - _colorNameToolTip = e.NameScope.Find("PART_ColorNameToolTip"); + _spectrumOverlayRectangle = e.NameScope.Find("PART_SpectrumOverlayRectangle"); if (_layoutRoot != null) { From 32f8b63764fcbfeda894415e706b3686fe07997b Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 27 Mar 2022 12:01:17 -0400 Subject: [PATCH 068/416] Remove extra variables leftover from C# conversion --- .../ColorSpectrum/ColorSpectrum.cs | 36 ++++++++----------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs index 9d04d1385b..abd84cc452 100644 --- a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -889,41 +889,33 @@ namespace Avalonia.Controls.Primitives return; } - var layoutRoot = _layoutRoot; - var sizingGrid = _sizingGrid; - var inputTarget = _inputTarget; - var spectrumRectangle = _spectrumRectangle; - var spectrumEllipse = _spectrumEllipse; - var spectrumOverlayRectangle = _spectrumOverlayRectangle; - var spectrumOverlayEllipse = _spectrumOverlayEllipse; - // We want ColorSpectrum to always be a square, so we'll take the smaller of the dimensions // and size the sizing grid to that. - double minDimension = Math.Min(layoutRoot.Bounds.Width, layoutRoot.Bounds.Height); + double minDimension = Math.Min(_layoutRoot.Bounds.Width, _layoutRoot.Bounds.Height); if (minDimension == 0) { return; } - sizingGrid.Width = minDimension; - sizingGrid.Height = minDimension; + _sizingGrid.Width = minDimension; + _sizingGrid.Height = minDimension; - if (sizingGrid.Clip is RectangleGeometry clip) + if (_sizingGrid.Clip is RectangleGeometry clip) { clip.Rect = new Rect(0, 0, minDimension, minDimension); } - inputTarget.Width = minDimension; - inputTarget.Height = minDimension; - spectrumRectangle.Width = minDimension; - spectrumRectangle.Height = minDimension; - spectrumEllipse.Width = minDimension; - spectrumEllipse.Height = minDimension; - spectrumOverlayRectangle.Width = minDimension; - spectrumOverlayRectangle.Height = minDimension; - spectrumOverlayEllipse.Width = minDimension; - spectrumOverlayEllipse.Height = minDimension; + _inputTarget.Width = minDimension; + _inputTarget.Height = minDimension; + _spectrumRectangle.Width = minDimension; + _spectrumRectangle.Height = minDimension; + _spectrumEllipse.Width = minDimension; + _spectrumEllipse.Height = minDimension; + _spectrumOverlayRectangle.Width = minDimension; + _spectrumOverlayRectangle.Height = minDimension; + _spectrumOverlayEllipse.Width = minDimension; + _spectrumOverlayEllipse.Height = minDimension; HsvColor hsvColor = HsvColor; int minHue = MinHue; From a47d9b6487dad0303eec8728f30bc14c25d911c8 Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 27 Mar 2022 12:14:09 -0400 Subject: [PATCH 069/416] Improve comments ColorSpectrumChannels clarifying axis mappings --- .../ColorPicker/ColorSpectrumChannels.cs | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrumChannels.cs b/src/Avalonia.Controls/ColorPicker/ColorSpectrumChannels.cs index 2b3f711634..a31586d175 100644 --- a/src/Avalonia.Controls/ColorPicker/ColorSpectrumChannels.cs +++ b/src/Avalonia.Controls/ColorPicker/ColorSpectrumChannels.cs @@ -9,38 +9,65 @@ namespace Avalonia.Controls { /// /// Defines the two HSV color channels displayed by a . - /// Order of the color channels is important. /// + /// + /// Order of the color channels is important and correspond with an X/Y axis in Box + /// shape or a degree/radius in Ring shape. + /// public enum ColorSpectrumChannels { /// /// The Hue and Value channels. /// + /// + /// In Box shape, Hue is mapped to the X-axis and Value is mapped to the Y-axis. + /// In Ring shape, Hue is mapped to degrees and Value is mapped to radius. + /// HueValue, /// /// The Value and Hue channels. /// + /// + /// In Box shape, Value is mapped to the X-axis and Hue is mapped to the Y-axis. + /// In Ring shape, Value is mapped to degrees and Hue is mapped to radius. + /// ValueHue, /// /// The Hue and Saturation channels. /// + /// + /// In Box shape, Hue is mapped to the X-axis and Saturation is mapped to the Y-axis. + /// In Ring shape, Hue is mapped to degrees and Saturation is mapped to radius. + /// HueSaturation, /// /// The Saturation and Hue channels. /// + /// + /// In Box shape, Saturation is mapped to the X-axis and Hue is mapped to the Y-axis. + /// In Ring shape, Saturation is mapped to degrees and Hue is mapped to radius. + /// SaturationHue, /// /// The Saturation and Value channels. /// + /// + /// In Box shape, Saturation is mapped to the X-axis and Value is mapped to the Y-axis. + /// In Ring shape, Saturation is mapped to degrees and Value is mapped to radius. + /// SaturationValue, /// /// The Value and Saturation channels. /// + /// + /// In Box shape, Value is mapped to the X-axis and Saturation is mapped to the Y-axis. + /// In Ring shape, Value is mapped to degrees and Saturation is mapped to radius. + /// ValueSaturation, }; } From 5904057bd200fb8b933eb8ad516d3db0c25532f6 Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 27 Mar 2022 12:41:59 -0400 Subject: [PATCH 070/416] Separate border style setters for easier customization --- .../Controls/ColorSpectrum.xaml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml b/src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml index 6a958809a0..8657a072a4 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml @@ -73,8 +73,6 @@ --> + + + + From 88370ce91bbbe1d1b786c578a22a3286f6329851 Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 27 Mar 2022 12:49:10 -0400 Subject: [PATCH 072/416] Add Default theme for ColorSpectrum --- .../Controls/ColorSpectrum.xaml | 138 ++++++++++++++++++ src/Avalonia.Themes.Default/DefaultTheme.xaml | 1 + 2 files changed, 139 insertions(+) create mode 100644 src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml diff --git a/src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml b/src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml new file mode 100644 index 0000000000..128f7038eb --- /dev/null +++ b/src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Default/DefaultTheme.xaml b/src/Avalonia.Themes.Default/DefaultTheme.xaml index 846d45b839..eea5af5932 100644 --- a/src/Avalonia.Themes.Default/DefaultTheme.xaml +++ b/src/Avalonia.Themes.Default/DefaultTheme.xaml @@ -11,6 +11,7 @@ + From 5669a640d019ab5425775a80339aa73a32662dcd Mon Sep 17 00:00:00 2001 From: Kibnet Philosoff Date: Wed, 9 Feb 2022 13:13:27 +0300 Subject: [PATCH 073/416] Fix #7567 - TreeView crashes the app when trying to display the same viewmodel twice --- .../Generators/TreeContainerIndex.cs | 54 +++++++++++++++++-- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/src/Avalonia.Controls/Generators/TreeContainerIndex.cs b/src/Avalonia.Controls/Generators/TreeContainerIndex.cs index da13416700..eb60fca367 100644 --- a/src/Avalonia.Controls/Generators/TreeContainerIndex.cs +++ b/src/Avalonia.Controls/Generators/TreeContainerIndex.cs @@ -15,6 +15,7 @@ namespace Avalonia.Controls.Generators /// public class TreeContainerIndex { + private readonly Dictionary> _itemToContainerSet = new Dictionary>(); private readonly Dictionary _itemToContainer = new Dictionary(); private readonly Dictionary _containerToItem = new Dictionary(); @@ -45,14 +46,45 @@ namespace Avalonia.Controls.Generators /// The item container. public void Add(object item, IControl container) { - _itemToContainer.Add(item, container); + _itemToContainer[item] = container; + if (_itemToContainerSet.TryGetValue(item, out var set)) + { + set.Add(container); + } + else + { + _itemToContainerSet.Add(item, new HashSet { container }); + } + _containerToItem.Add(container, item); Materialized?.Invoke( - this, + this, new ItemContainerEventArgs(new ItemContainerInfo(container, item, 0))); } + /// + /// Removes a container from private collections. + /// + /// The item container. + /// The DataContext object + private void RemoveContainer(IControl container, object item) + { + if (_itemToContainerSet.TryGetValue(item, out var set)) + { + set.Remove(container); + if (set.Count == 0) + { + _itemToContainerSet.Remove(item); + _itemToContainer.Remove(item); + } + else + { + _itemToContainer[item] = set.First(); + } + } + } + /// /// Removes a container from the index. /// @@ -61,10 +93,10 @@ namespace Avalonia.Controls.Generators { var item = _containerToItem[container]; _containerToItem.Remove(container); - _itemToContainer.Remove(item); + RemoveContainer(container, item); Dematerialized?.Invoke( - this, + this, new ItemContainerEventArgs(new ItemContainerInfo(container, item, 0))); } @@ -79,7 +111,7 @@ namespace Avalonia.Controls.Generators { var item = _containerToItem[container.ContainerControl]; _containerToItem.Remove(container.ContainerControl); - _itemToContainer.Remove(item); + RemoveContainer(container.ContainerControl, item); } Dematerialized?.Invoke( @@ -97,6 +129,14 @@ namespace Avalonia.Controls.Generators if (item != null) { _itemToContainer.TryGetValue(item, out var result); + if (result == null) + { + _itemToContainerSet.TryGetValue(item, out var set); + if (set?.Count > 0) + { + return set.FirstOrDefault(); + } + } return result; } @@ -113,6 +153,10 @@ namespace Avalonia.Controls.Generators if (container != null) { _containerToItem.TryGetValue(container, out var result); + if (result != null) + { + _itemToContainer[result] = container; + } return result; } From 5a76c63c478ec21a6b2f8b960f6e6e7623904a56 Mon Sep 17 00:00:00 2001 From: robloo Date: Mon, 28 Mar 2022 11:36:37 -0400 Subject: [PATCH 074/416] Simplify TemplatePartAttribute usage --- .../ColorPicker/ColorSpectrum/ColorSpectrum.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs index abd84cc452..62d3b4ce88 100644 --- a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -20,15 +20,15 @@ namespace Avalonia.Controls.Primitives /// /// A two dimensional spectrum for color selection. /// - [TemplatePart(Name = "PART_ColorNameToolTip", Type = typeof(ToolTip))] - [TemplatePart(Name = "PART_InputTarget", Type = typeof(Canvas))] - [TemplatePart(Name = "PART_LayoutRoot", Type = typeof(Grid))] - [TemplatePart(Name = "PART_SelectionEllipsePanel", Type = typeof(Panel))] - [TemplatePart(Name = "PART_SizingGrid", Type = typeof(Grid))] - [TemplatePart(Name = "PART_SpectrumEllipse", Type = typeof(Ellipse))] - [TemplatePart(Name = "PART_SpectrumRectangle", Type = typeof(Rectangle))] - [TemplatePart(Name = "PART_SpectrumOverlayEllipse", Type = typeof(Ellipse))] - [TemplatePart(Name = "PART_SpectrumOverlayRectangle", Type = typeof(Rectangle))] + [TemplatePart("PART_ColorNameToolTip", typeof(ToolTip))] + [TemplatePart("PART_InputTarget", typeof(Canvas))] + [TemplatePart("PART_LayoutRoot", typeof(Grid))] + [TemplatePart("PART_SelectionEllipsePanel", typeof(Panel))] + [TemplatePart("PART_SizingGrid", typeof(Grid))] + [TemplatePart("PART_SpectrumEllipse", typeof(Ellipse))] + [TemplatePart("PART_SpectrumRectangle", typeof(Rectangle))] + [TemplatePart("PART_SpectrumOverlayEllipse", typeof(Ellipse))] + [TemplatePart("PART_SpectrumOverlayRectangle", typeof(Rectangle))] [PseudoClasses(pcPressed, pcLargeSelector, pcLightSelector)] public partial class ColorSpectrum : TemplatedControl { From 4fe7fb71e67264d8635c2fa5383138b2eab7ff12 Mon Sep 17 00:00:00 2001 From: robloo Date: Mon, 28 Mar 2022 11:44:32 -0400 Subject: [PATCH 075/416] Simplify ColorChangedEventArgs --- .../ColorPicker/ColorChangedEventArgs.cs | 26 +++---------------- .../ColorSpectrum/ColorSpectrum.cs | 6 +---- 2 files changed, 5 insertions(+), 27 deletions(-) diff --git a/src/Avalonia.Controls/ColorPicker/ColorChangedEventArgs.cs b/src/Avalonia.Controls/ColorPicker/ColorChangedEventArgs.cs index 93ba1a4db1..b1d15d6b17 100644 --- a/src/Avalonia.Controls/ColorPicker/ColorChangedEventArgs.cs +++ b/src/Avalonia.Controls/ColorPicker/ColorChangedEventArgs.cs @@ -17,16 +17,6 @@ namespace Avalonia.Controls /// public class ColorChangedEventArgs : EventArgs { - private Color _OldColor; - private Color _NewColor; - - /// - /// Initializes a new instance of the class. - /// - public ColorChangedEventArgs() - { - } - /// /// Initializes a new instance of the class. /// @@ -34,26 +24,18 @@ namespace Avalonia.Controls /// The new/updated color that triggered the change event. public ColorChangedEventArgs(Color oldColor, Color newColor) { - _OldColor = oldColor; - _NewColor = newColor; + OldColor = oldColor; + NewColor = newColor; } /// /// Gets the old/original color from before the change event. /// - public Color OldColor - { - get => _OldColor; - internal set => _OldColor = value; - } + public Color OldColor { get; private set; } /// /// Gets the new/updated color that triggered the change event. /// - public Color NewColor - { - get => _NewColor; - internal set => _NewColor = value; - } + public Color NewColor { get; private set; } } } diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs index 62d3b4ce88..11e1437f81 100644 --- a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -499,11 +499,7 @@ namespace Avalonia.Controls.Primitives if (colorChanged || areBothColorsBlack) { - var colorChangedEventArgs = new ColorChangedEventArgs(); - - colorChangedEventArgs.OldColor = _oldColor; - colorChangedEventArgs.NewColor = newColor; - + var colorChangedEventArgs = new ColorChangedEventArgs(_oldColor, newColor); ColorChanged?.Invoke(this, colorChangedEventArgs); if (ColorHelpers.ToDisplayNameExists) From 073b3e4d34706a1198fc7fea3f9d02dc747dcb50 Mon Sep 17 00:00:00 2001 From: robloo Date: Mon, 28 Mar 2022 12:05:14 -0400 Subject: [PATCH 076/416] Slightly rework events using OnAttached/DetachedToVisualTree --- .../ColorSpectrum/ColorSpectrum.cs | 40 +++++++++++++------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs index 11e1437f81..dfead58c6a 100644 --- a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -115,7 +115,7 @@ namespace Avalonia.Controls.Primitives { base.OnApplyTemplate(e); - UnregisterEvents(); + UnregisterEvents(); // Failsafe _colorNameToolTip = e.NameScope.Find("PART_ColorNameToolTip"); _inputTarget = e.NameScope.Find("PART_InputTarget"); @@ -127,14 +127,6 @@ namespace Avalonia.Controls.Primitives _spectrumOverlayEllipse = e.NameScope.Find("PART_SpectrumOverlayEllipse"); _spectrumOverlayRectangle = e.NameScope.Find("PART_SpectrumOverlayRectangle"); - if (_layoutRoot != null) - { - _layoutRootDisposable = _layoutRoot.GetObservable(BoundsProperty).Subscribe(_ => - { - CreateBitmapsAndColorMap(); - }); - } - if (_inputTarget != null) { _inputTarget.PointerEnter += OnInputTargetPointerEnter; @@ -144,10 +136,12 @@ namespace Avalonia.Controls.Primitives _inputTarget.PointerReleased += OnInputTargetPointerReleased; } - if (ColorHelpers.ToDisplayNameExists && - _colorNameToolTip != null) + if (_layoutRoot != null) { - _colorNameToolTip.Content = ColorHelpers.ToDisplayName(Color); + _layoutRootDisposable = _layoutRoot.GetObservable(BoundsProperty).Subscribe(_ => + { + CreateBitmapsAndColorMap(); + }); } if (_selectionEllipsePanel != null) @@ -158,6 +152,12 @@ namespace Avalonia.Controls.Primitives }); } + if (ColorHelpers.ToDisplayNameExists && + _colorNameToolTip != null) + { + _colorNameToolTip.Content = ColorHelpers.ToDisplayName(Color); + } + // If we haven't yet created our bitmaps, do so now. if (_hsvValues.Count == 0) { @@ -168,6 +168,22 @@ namespace Avalonia.Controls.Primitives UpdatePseudoClasses(); } + /// + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnAttachedToVisualTree(e); + + // OnAttachedToVisualTree is called after OnApplyTemplate so events cannot be connected here + } + + /// + protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnDetachedFromVisualTree(e); + + UnregisterEvents(); + } + /// /// Explicitly unregisters all events connected in OnApplyTemplate(). /// From 7e3cf42c00f6dfe6ea1a3a16af4f2906224cbff6 Mon Sep 17 00:00:00 2001 From: robloo Date: Mon, 28 Mar 2022 12:08:05 -0400 Subject: [PATCH 077/416] Rename event handlers that are not overridable methods --- .../ColorSpectrum/ColorSpectrum.cs | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs index dfead58c6a..c63bcb225b 100644 --- a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -129,11 +129,11 @@ namespace Avalonia.Controls.Primitives if (_inputTarget != null) { - _inputTarget.PointerEnter += OnInputTargetPointerEnter; - _inputTarget.PointerLeave += OnInputTargetPointerLeave; - _inputTarget.PointerPressed += OnInputTargetPointerPressed; - _inputTarget.PointerMoved += OnInputTargetPointerMoved; - _inputTarget.PointerReleased += OnInputTargetPointerReleased; + _inputTarget.PointerEnter += InputTarget_PointerEnter; + _inputTarget.PointerLeave += InputTarget_PointerLeave; + _inputTarget.PointerPressed += InputTarget_PointerPressed; + _inputTarget.PointerMoved += InputTarget_PointerMoved; + _inputTarget.PointerReleased += InputTarget_PointerReleased; } if (_layoutRoot != null) @@ -197,11 +197,11 @@ namespace Avalonia.Controls.Primitives if (_inputTarget != null) { - _inputTarget.PointerEnter -= OnInputTargetPointerEnter; - _inputTarget.PointerLeave -= OnInputTargetPointerLeave; - _inputTarget.PointerPressed -= OnInputTargetPointerPressed; - _inputTarget.PointerMoved -= OnInputTargetPointerMoved; - _inputTarget.PointerReleased -= OnInputTargetPointerReleased; + _inputTarget.PointerEnter -= InputTarget_PointerEnter; + _inputTarget.PointerLeave -= InputTarget_PointerLeave; + _inputTarget.PointerPressed -= InputTarget_PointerPressed; + _inputTarget.PointerMoved -= InputTarget_PointerMoved; + _inputTarget.PointerReleased -= InputTarget_PointerReleased; } } @@ -830,21 +830,24 @@ namespace Avalonia.Controls.Primitives UpdatePseudoClasses(); } - private void OnInputTargetPointerEnter(object? sender, PointerEventArgs args) + /// + private void InputTarget_PointerEnter(object? sender, PointerEventArgs args) { _isPointerOver = true; UpdatePseudoClasses(); args.Handled = true; } - private void OnInputTargetPointerLeave(object? sender, PointerEventArgs args) + /// + private void InputTarget_PointerLeave(object? sender, PointerEventArgs args) { _isPointerOver = false; UpdatePseudoClasses(); args.Handled = true; } - private void OnInputTargetPointerPressed(object? sender, PointerPressedEventArgs args) + /// + private void InputTarget_PointerPressed(object? sender, PointerPressedEventArgs args) { var inputTarget = _inputTarget; @@ -864,7 +867,8 @@ namespace Avalonia.Controls.Primitives args.Handled = true; } - private void OnInputTargetPointerMoved(object? sender, PointerEventArgs args) + /// + private void InputTarget_PointerMoved(object? sender, PointerEventArgs args) { if (!_isPointerPressed) { @@ -875,7 +879,8 @@ namespace Avalonia.Controls.Primitives args.Handled = true; } - private void OnInputTargetPointerReleased(object? sender, PointerReleasedEventArgs args) + /// + private void InputTarget_PointerReleased(object? sender, PointerReleasedEventArgs args) { _isPointerPressed = false; _shouldShowLargeSelection = false; From 89b6e4e9d05f95d6ef73f30a777387e72f4d3ff2 Mon Sep 17 00:00:00 2001 From: robloo Date: Mon, 28 Mar 2022 12:10:39 -0400 Subject: [PATCH 078/416] If we can't connect in OnAttachedToVisualTree we also can't disconnect --- .../ColorPicker/ColorSpectrum/ColorSpectrum.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs index c63bcb225b..2ff264baf9 100644 --- a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -180,8 +180,6 @@ namespace Avalonia.Controls.Primitives protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) { base.OnDetachedFromVisualTree(e); - - UnregisterEvents(); } /// From 088d63b9128e153ef91c7cfb4015718eb0f309ed Mon Sep 17 00:00:00 2001 From: robloo Date: Mon, 28 Mar 2022 12:17:15 -0400 Subject: [PATCH 079/416] Remove Clip geometry which seems to be unnecessary --- .../ColorPicker/ColorSpectrum/ColorSpectrum.cs | 6 ------ src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml | 6 ++---- src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml | 6 ++---- 3 files changed, 4 insertions(+), 14 deletions(-) diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs index 2ff264baf9..45b95ce981 100644 --- a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -915,12 +915,6 @@ namespace Avalonia.Controls.Primitives _sizingGrid.Width = minDimension; _sizingGrid.Height = minDimension; - - if (_sizingGrid.Clip is RectangleGeometry clip) - { - clip.Rect = new Rect(0, 0, minDimension, minDimension); - } - _inputTarget.Width = minDimension; _inputTarget.Height = minDimension; _spectrumRectangle.Width = minDimension; diff --git a/src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml b/src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml index 128f7038eb..c5ea317ce3 100644 --- a/src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml +++ b/src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml @@ -20,10 +20,8 @@ - - - + VerticalAlignment="Center" + ClipToBounds="True"> - - - + VerticalAlignment="Center" + ClipToBounds="True"> Date: Mon, 28 Mar 2022 12:39:38 -0400 Subject: [PATCH 080/416] Switch from Grid to Panel which is more lightweight --- .../ColorPicker/ColorSpectrum/ColorSpectrum.cs | 18 +++++++++--------- .../Controls/ColorSpectrum.xaml | 16 +++++++++------- .../Controls/ColorSpectrum.xaml | 16 +++++++++------- 3 files changed, 27 insertions(+), 23 deletions(-) diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs index 45b95ce981..1b04607ef0 100644 --- a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -22,9 +22,9 @@ namespace Avalonia.Controls.Primitives /// [TemplatePart("PART_ColorNameToolTip", typeof(ToolTip))] [TemplatePart("PART_InputTarget", typeof(Canvas))] - [TemplatePart("PART_LayoutRoot", typeof(Grid))] + [TemplatePart("PART_LayoutRoot", typeof(Panel))] [TemplatePart("PART_SelectionEllipsePanel", typeof(Panel))] - [TemplatePart("PART_SizingGrid", typeof(Grid))] + [TemplatePart("PART_SizingPanel", typeof(Panel))] [TemplatePart("PART_SpectrumEllipse", typeof(Ellipse))] [TemplatePart("PART_SpectrumRectangle", typeof(Rectangle))] [TemplatePart("PART_SpectrumOverlayEllipse", typeof(Ellipse))] @@ -52,8 +52,8 @@ namespace Avalonia.Controls.Primitives private IDisposable? _selectionEllipsePanelDisposable; // XAML template parts - private Grid? _layoutRoot; - private Grid? _sizingGrid; + private Panel? _layoutRoot; + private Panel? _sizingPanel; private Rectangle? _spectrumRectangle; private Ellipse? _spectrumEllipse; private Rectangle? _spectrumOverlayRectangle; @@ -119,9 +119,9 @@ namespace Avalonia.Controls.Primitives _colorNameToolTip = e.NameScope.Find("PART_ColorNameToolTip"); _inputTarget = e.NameScope.Find("PART_InputTarget"); - _layoutRoot = e.NameScope.Find("PART_LayoutRoot"); + _layoutRoot = e.NameScope.Find("PART_LayoutRoot"); _selectionEllipsePanel = e.NameScope.Find("PART_SelectionEllipsePanel"); - _sizingGrid = e.NameScope.Find("PART_SizingGrid"); + _sizingPanel = e.NameScope.Find("PART_SizingPanel"); _spectrumEllipse = e.NameScope.Find("PART_SpectrumEllipse"); _spectrumRectangle = e.NameScope.Find("PART_SpectrumRectangle"); _spectrumOverlayEllipse = e.NameScope.Find("PART_SpectrumOverlayEllipse"); @@ -893,7 +893,7 @@ namespace Avalonia.Controls.Primitives private async void CreateBitmapsAndColorMap() { if (_layoutRoot == null || - _sizingGrid == null || + _sizingPanel == null || _inputTarget == null || _spectrumRectangle == null || _spectrumEllipse == null || @@ -913,8 +913,8 @@ namespace Avalonia.Controls.Primitives return; } - _sizingGrid.Width = minDimension; - _sizingGrid.Height = minDimension; + _sizingPanel.Width = minDimension; + _sizingPanel.Height = minDimension; _inputTarget.Width = minDimension; _inputTarget.Height = minDimension; _spectrumRectangle.Width = minDimension; diff --git a/src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml b/src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml index c5ea317ce3..7fa703ed18 100644 --- a/src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml +++ b/src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml @@ -17,11 +17,13 @@ - - + + - - + + diff --git a/src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml b/src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml index 0b68aa3744..b138a4ad63 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml @@ -17,11 +17,13 @@ - - + + - - + + From e607f8cce8ccced935eded7450751c4973ace6ea Mon Sep 17 00:00:00 2001 From: robloo Date: Mon, 28 Mar 2022 12:55:30 -0400 Subject: [PATCH 081/416] Watch for Bounds and FlowDirection property changes on the control itself --- .../ColorSpectrum/ColorSpectrum.cs | 35 +++++-------------- 1 file changed, 9 insertions(+), 26 deletions(-) diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs index 1b04607ef0..1b68c686fd 100644 --- a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -48,9 +48,6 @@ namespace Avalonia.Controls.Primitives private bool _shouldShowLargeSelection = false; private List _hsvValues = new List(); - private IDisposable? _layoutRootDisposable; - private IDisposable? _selectionEllipsePanelDisposable; - // XAML template parts private Panel? _layoutRoot; private Panel? _sizingPanel; @@ -136,22 +133,6 @@ namespace Avalonia.Controls.Primitives _inputTarget.PointerReleased += InputTarget_PointerReleased; } - if (_layoutRoot != null) - { - _layoutRootDisposable = _layoutRoot.GetObservable(BoundsProperty).Subscribe(_ => - { - CreateBitmapsAndColorMap(); - }); - } - - if (_selectionEllipsePanel != null) - { - _selectionEllipsePanelDisposable = _selectionEllipsePanel.GetObservable(FlowDirectionProperty).Subscribe(_ => - { - UpdateEllipse(); - }); - } - if (ColorHelpers.ToDisplayNameExists && _colorNameToolTip != null) { @@ -187,12 +168,6 @@ namespace Avalonia.Controls.Primitives /// private void UnregisterEvents() { - _layoutRootDisposable?.Dispose(); - _layoutRootDisposable = null; - - _selectionEllipsePanelDisposable?.Dispose(); - _selectionEllipsePanelDisposable = null; - if (_inputTarget != null) { _inputTarget.PointerEnter -= InputTarget_PointerEnter; @@ -363,7 +338,11 @@ namespace Avalonia.Controls.Primitives /// protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { - if (change.Property == ColorProperty) + if (change.Property == BoundsProperty) + { + CreateBitmapsAndColorMap(); + } + else if (change.Property == ColorProperty) { // If we're in the process of internally updating the color, // then we don't want to respond to the Color property changing. @@ -382,6 +361,10 @@ namespace Avalonia.Controls.Primitives _oldColor = change.OldValue.GetValueOrDefault(); } + else if (change.Property == FlowDirectionProperty) + { + UpdateEllipse(); + } else if (change.Property == HsvColorProperty) { // If we're in the process of internally updating the HSV color, From 680d3084aa5aaf068326137f54075f8259a4f8ee Mon Sep 17 00:00:00 2001 From: robloo Date: Mon, 28 Mar 2022 12:58:01 -0400 Subject: [PATCH 082/416] Rename border controls in template to match convention --- src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml | 8 ++++---- src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml b/src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml index 7fa703ed18..1d127ec115 100644 --- a/src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml +++ b/src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml @@ -72,14 +72,14 @@ - - - diff --git a/src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml b/src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml index b138a4ad63..c8267b57d9 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml @@ -72,14 +72,14 @@ - - - From bcc2be8d645275a41cb83c0f8d15955b56894af4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro?= Date: Tue, 29 Mar 2022 01:09:47 +0100 Subject: [PATCH 083/416] Fixed ScrollViewer Padding. --- src/Avalonia.Themes.Default/Controls/ScrollViewer.xaml | 2 +- src/Avalonia.Themes.Fluent/Controls/ScrollViewer.xaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Themes.Default/Controls/ScrollViewer.xaml b/src/Avalonia.Themes.Default/Controls/ScrollViewer.xaml index b357446bfa..aab1b76259 100644 --- a/src/Avalonia.Themes.Default/Controls/ScrollViewer.xaml +++ b/src/Avalonia.Themes.Default/Controls/ScrollViewer.xaml @@ -13,7 +13,7 @@ CanVerticallyScroll="{TemplateBinding CanVerticallyScroll}" Content="{TemplateBinding Content}" Extent="{TemplateBinding Extent, Mode=TwoWay}" - Margin="{TemplateBinding Padding}" + Padding="{TemplateBinding Padding}" Offset="{TemplateBinding Offset, Mode=TwoWay}" Viewport="{TemplateBinding Viewport, Mode=TwoWay}" IsScrollChainingEnabled="{TemplateBinding IsScrollChainingEnabled}"> diff --git a/src/Avalonia.Themes.Fluent/Controls/ScrollViewer.xaml b/src/Avalonia.Themes.Fluent/Controls/ScrollViewer.xaml index 53a1b721d1..b7addcb61e 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ScrollViewer.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ScrollViewer.xaml @@ -32,7 +32,7 @@ CanVerticallyScroll="{TemplateBinding CanVerticallyScroll}" Content="{TemplateBinding Content}" Extent="{TemplateBinding Extent, Mode=TwoWay}" - Margin="{TemplateBinding Padding}" + Padding="{TemplateBinding Padding}" Offset="{TemplateBinding Offset, Mode=TwoWay}" Viewport="{TemplateBinding Viewport, Mode=TwoWay}" IsScrollChainingEnabled="{TemplateBinding IsScrollChainingEnabled}"> From 847e20150c993c7431b8a145a161c82107d6297c Mon Sep 17 00:00:00 2001 From: Jade Macho Date: Tue, 29 Mar 2022 21:08:47 +0200 Subject: [PATCH 084/416] Fix IsRegisteredAsAnchorCandidate desync, mainly affecting recycled anchors --- .../Repeater/ItemsRepeater.cs | 8 ++--- .../Repeater/ViewportManager.cs | 29 +++++++++++++++---- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/src/Avalonia.Controls/Repeater/ItemsRepeater.cs b/src/Avalonia.Controls/Repeater/ItemsRepeater.cs index 09c0e58332..21b677af66 100644 --- a/src/Avalonia.Controls/Repeater/ItemsRepeater.cs +++ b/src/Avalonia.Controls/Repeater/ItemsRepeater.cs @@ -391,11 +391,7 @@ namespace Avalonia.Controls var newBounds = element.Bounds; virtInfo.ArrangeBounds = newBounds; - if (!virtInfo.IsRegisteredAsAnchorCandidate) - { - _viewportManager.RegisterScrollAnchorCandidate(element); - virtInfo.IsRegisteredAsAnchorCandidate = true; - } + _viewportManager.RegisterScrollAnchorCandidate(element, virtInfo); } } @@ -480,7 +476,7 @@ namespace Avalonia.Controls _processingItemsSourceChange.Action == NotifyCollectionChangedAction.Reset); _viewManager.ClearElement(element, isClearedDueToCollectionChange); - _viewportManager.OnElementCleared(element); + _viewportManager.OnElementCleared(element, GetVirtualizationInfo(element)); } private int GetElementIndexImpl(IControl element) diff --git a/src/Avalonia.Controls/Repeater/ViewportManager.cs b/src/Avalonia.Controls/Repeater/ViewportManager.cs index ec25fcb265..7f03cc575e 100644 --- a/src/Avalonia.Controls/Repeater/ViewportManager.cs +++ b/src/Avalonia.Controls/Repeater/ViewportManager.cs @@ -249,9 +249,10 @@ namespace Avalonia.Controls virtInfo.IsRegisteredAsAnchorCandidate = false; } - public void OnElementCleared(IControl element) + public void OnElementCleared(IControl element, VirtualizationInfo virtInfo) { _scroller?.UnregisterAnchorCandidate(element); + virtInfo.IsRegisteredAsAnchorCandidate = false; } public void OnOwnerMeasuring() @@ -358,9 +359,12 @@ namespace Avalonia.Controls { foreach (var child in _owner.Children) { - if (child != targetChild) + var info = ItemsRepeater.GetVirtualizationInfo(child); + + if (child != targetChild && info.IsRegisteredAsAnchorCandidate) { _scroller.UnregisterAnchorCandidate(child); + info.IsRegisteredAsAnchorCandidate = false; } } } @@ -377,9 +381,13 @@ namespace Avalonia.Controls } } - public void RegisterScrollAnchorCandidate(IControl element) + public void RegisterScrollAnchorCandidate(IControl element, VirtualizationInfo virtInfo) { - _scroller?.RegisterAnchorCandidate(element); + if (!virtInfo.IsRegisteredAsAnchorCandidate) + { + _scroller?.RegisterAnchorCandidate(element); + virtInfo.IsRegisteredAsAnchorCandidate = true; + } } private IControl? GetImmediateChildOfRepeater(IControl descendant) @@ -405,15 +413,18 @@ namespace Avalonia.Controls _isBringIntoViewInProgress = false; _makeAnchorElement = null; + // Undo the anchor deregistrations done by OnBringIntoViewRequested. if (_scroller is object) { foreach (var child in _owner.Children) { var info = ItemsRepeater.GetVirtualizationInfo(child); - if (info.IsRealized && info.IsHeldByLayout) + // The item brought into view is still registered - don't register it more than once. + if (info.IsRealized && info.IsHeldByLayout && !info.IsRegisteredAsAnchorCandidate) { _scroller.RegisterAnchorCandidate(child); + info.IsRegisteredAsAnchorCandidate = true; } } } @@ -430,7 +441,13 @@ namespace Avalonia.Controls { foreach (var child in _owner.Children) { - _scroller.UnregisterAnchorCandidate(child); + var info = ItemsRepeater.GetVirtualizationInfo(child); + + if (info.IsRegisteredAsAnchorCandidate) + { + _scroller.UnregisterAnchorCandidate(child); + info.IsRegisteredAsAnchorCandidate = false; + } } _scroller = null; From c8f2548f058841e27ab49431a847e07056dbb437 Mon Sep 17 00:00:00 2001 From: Jade Macho Date: Tue, 29 Mar 2022 22:44:32 +0200 Subject: [PATCH 085/416] Add "Scroll to Selected" and "Remove Item" to control catalog items repeater --- samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml | 2 ++ .../ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs | 10 ++++++++++ .../ViewModels/ItemsRepeaterPageViewModel.cs | 13 +++++++++++++ 3 files changed, 25 insertions(+) diff --git a/samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml b/samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml index 8305d72d1f..1d42b92096 100644 --- a/samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml +++ b/samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml @@ -53,10 +53,12 @@ UniformGrid - Horizontal + + ("scroller"); _scrollToLast = this.FindControl private void UnregisterEvents() { + _layoutRootDisposable?.Dispose(); + _layoutRootDisposable = null; + + _selectionEllipsePanelDisposable?.Dispose(); + _selectionEllipsePanelDisposable = null; + if (_inputTarget != null) { _inputTarget.PointerEnter -= InputTarget_PointerEnter; @@ -338,11 +363,7 @@ namespace Avalonia.Controls.Primitives /// protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { - if (change.Property == BoundsProperty) - { - CreateBitmapsAndColorMap(); - } - else if (change.Property == ColorProperty) + if (change.Property == ColorProperty) { // If we're in the process of internally updating the color, // then we don't want to respond to the Color property changing. @@ -361,10 +382,6 @@ namespace Avalonia.Controls.Primitives _oldColor = change.OldValue.GetValueOrDefault(); } - else if (change.Property == FlowDirectionProperty) - { - UpdateEllipse(); - } else if (change.Property == HsvColorProperty) { // If we're in the process of internally updating the HSV color, From f46ed4d922faa02eaa8d658fcb88e467290d3005 Mon Sep 17 00:00:00 2001 From: robloo Date: Tue, 29 Mar 2022 20:21:07 -0400 Subject: [PATCH 087/416] Switch last Grid entirely to Panel --- .../ColorPicker/ColorSpectrum/ColorSpectrum.cs | 14 ++++++-------- .../Controls/ColorSpectrum.xaml | 8 ++++---- .../Controls/ColorSpectrum.xaml | 8 ++++---- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs index 1b04607ef0..6779c54a1e 100644 --- a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -643,9 +643,7 @@ namespace Avalonia.Controls.Primitives private void UpdateEllipse() { - var selectionEllipsePanel = _selectionEllipsePanel; - - if (selectionEllipsePanel == null) + if (_selectionEllipsePanel == null) { return; } @@ -654,12 +652,12 @@ namespace Avalonia.Controls.Primitives if (_imageWidthFromLastBitmapCreation == 0 || _imageHeightFromLastBitmapCreation == 0) { - selectionEllipsePanel.IsVisible = false; + _selectionEllipsePanel.IsVisible = false; return; } else { - selectionEllipsePanel.IsVisible = true; + _selectionEllipsePanel.IsVisible = true; } double xPosition; @@ -810,8 +808,8 @@ namespace Avalonia.Controls.Primitives yPosition = (Math.Sin((thetaValue * Math.PI / 180.0) + Math.PI) * radius * rValue) + radius; } - Canvas.SetLeft(selectionEllipsePanel, xPosition - (selectionEllipsePanel.Width / 2)); - Canvas.SetTop(selectionEllipsePanel, yPosition - (selectionEllipsePanel.Height / 2)); + Canvas.SetLeft(_selectionEllipsePanel, xPosition - (_selectionEllipsePanel.Width / 2)); + Canvas.SetTop(_selectionEllipsePanel, yPosition - (_selectionEllipsePanel.Height / 2)); // We only want to bother with the color name tool tip if we can provide color names. if (ColorHelpers.ToDisplayNameExists) @@ -905,7 +903,7 @@ namespace Avalonia.Controls.Primitives } // We want ColorSpectrum to always be a square, so we'll take the smaller of the dimensions - // and size the sizing grid to that. + // and size the sizing panel to that. double minDimension = Math.Min(_layoutRoot.Bounds.Width, _layoutRoot.Bounds.Height); if (minDimension == 0) diff --git a/src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml b/src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml index 1d127ec115..7065ba30f6 100644 --- a/src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml +++ b/src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml @@ -52,7 +52,7 @@ Background="Transparent" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> - + - + - - diff --git a/src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml b/src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml index c8267b57d9..4208d9665f 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml @@ -52,7 +52,7 @@ Background="Transparent" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> - + - + - - From 1a8435c04db7a8c7ae14b8949da87cad925dcf22 Mon Sep 17 00:00:00 2001 From: robloo Date: Mon, 4 Apr 2022 21:35:46 -0400 Subject: [PATCH 088/416] Add CornerRadiusToDoubleConverter --- ...iusFilterKind.cs => CornerRadiusCorner.cs} | 18 +++++--- .../Converters/CornerRadiusFilterConverter.cs | 17 +++---- .../CornerRadiusToDoubleConverter.cs | 46 +++++++++++++++++++ 3 files changed, 66 insertions(+), 15 deletions(-) rename src/Avalonia.Controls/Converters/{CornerRadiusFilterKind.cs => CornerRadiusCorner.cs} (57%) create mode 100644 src/Avalonia.Controls/Converters/CornerRadiusToDoubleConverter.cs diff --git a/src/Avalonia.Controls/Converters/CornerRadiusFilterKind.cs b/src/Avalonia.Controls/Converters/CornerRadiusCorner.cs similarity index 57% rename from src/Avalonia.Controls/Converters/CornerRadiusFilterKind.cs rename to src/Avalonia.Controls/Converters/CornerRadiusCorner.cs index 6a9d0596be..205dd12f48 100644 --- a/src/Avalonia.Controls/Converters/CornerRadiusFilterKind.cs +++ b/src/Avalonia.Controls/Converters/CornerRadiusCorner.cs @@ -3,29 +3,33 @@ namespace Avalonia.Controls.Converters { /// - /// Defines constants that specify the filter type for a instance. + /// Defines constants that specify the corner of a . /// [Flags] - public enum CornerRadiusFilterKinds + public enum CornerRadiusCorner { /// - /// No filter applied. + /// No corner. /// None, + /// - /// Filters TopLeft value. + /// The TopLeft corner. /// TopLeft = 1, + /// - /// Filters TopRight value. + /// The TopRight corner. /// TopRight = 2, + /// - /// Filters BottomLeft value. + /// The BottomLeft corner. /// BottomLeft = 4, + /// - /// Filters BottomRight value. + /// The BottomRight corner. /// BottomRight = 8 } diff --git a/src/Avalonia.Controls/Converters/CornerRadiusFilterConverter.cs b/src/Avalonia.Controls/Converters/CornerRadiusFilterConverter.cs index 643c30178e..b298fbf2e1 100644 --- a/src/Avalonia.Controls/Converters/CornerRadiusFilterConverter.cs +++ b/src/Avalonia.Controls/Converters/CornerRadiusFilterConverter.cs @@ -7,17 +7,18 @@ namespace Avalonia.Controls.Converters { /// /// Converts an existing CornerRadius struct to a new CornerRadius struct, - /// with filters applied to extract only the specified fields, leaving the others set to 0. + /// with filters applied to extract only the specified corners, leaving the others set to 0. /// public class CornerRadiusFilterConverter : IValueConverter { /// - /// Gets or sets the type of the filter applied to the . + /// Gets or sets the corners to filter by. + /// Only the specified corners will be included in the converted . /// - public CornerRadiusFilterKinds Filter { get; set; } + public CornerRadiusCorner Filter { get; set; } /// - /// Gets or sets the scale multiplier applied to the . + /// Gets or sets the scale multiplier applied uniformly to each corner. /// public double Scale { get; set; } = 1; @@ -29,10 +30,10 @@ namespace Avalonia.Controls.Converters } return new CornerRadius( - Filter.HasAllFlags(CornerRadiusFilterKinds.TopLeft) ? radius.TopLeft * Scale : 0, - Filter.HasAllFlags(CornerRadiusFilterKinds.TopRight) ? radius.TopRight * Scale : 0, - Filter.HasAllFlags(CornerRadiusFilterKinds.BottomRight) ? radius.BottomRight * Scale : 0, - Filter.HasAllFlags(CornerRadiusFilterKinds.BottomLeft) ? radius.BottomLeft * Scale : 0); + Filter.HasAllFlags(CornerRadiusCorner.TopLeft) ? radius.TopLeft * Scale : 0, + Filter.HasAllFlags(CornerRadiusCorner.TopRight) ? radius.TopRight * Scale : 0, + Filter.HasAllFlags(CornerRadiusCorner.BottomRight) ? radius.BottomRight * Scale : 0, + Filter.HasAllFlags(CornerRadiusCorner.BottomLeft) ? radius.BottomLeft * Scale : 0); } public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) diff --git a/src/Avalonia.Controls/Converters/CornerRadiusToDoubleConverter.cs b/src/Avalonia.Controls/Converters/CornerRadiusToDoubleConverter.cs new file mode 100644 index 0000000000..2d549504ff --- /dev/null +++ b/src/Avalonia.Controls/Converters/CornerRadiusToDoubleConverter.cs @@ -0,0 +1,46 @@ +using System; +using System.Globalization; +using Avalonia.Data.Converters; + +namespace Avalonia.Controls.Converters +{ + /// + /// Converts one corner of a to its double value. + /// + public class CornerRadiusToDoubleConverter : IValueConverter + { + /// + /// Gets or sets the specific corner of the to convert to double. + /// + public CornerRadiusCorner Corner { get; set; } + + /// + public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) + { + if (!(value is CornerRadius cornerRadius)) + { + return AvaloniaProperty.UnsetValue; + } + + switch (Corner) + { + case CornerRadiusCorner.TopLeft: + return cornerRadius.TopLeft; + case CornerRadiusCorner.TopRight: + return cornerRadius.TopRight; + case CornerRadiusCorner.BottomRight: + return cornerRadius.BottomRight; + case CornerRadiusCorner.BottomLeft: + return cornerRadius.BottomLeft; + default: + return 0.0; + } + } + + /// + public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} From 30f713746a399883d59446b3a2d5307aaaf014b6 Mon Sep 17 00:00:00 2001 From: robloo Date: Mon, 4 Apr 2022 21:39:11 -0400 Subject: [PATCH 089/416] Support CornerRadius in ColorSpectrum --- .../ControlCatalog/Pages/ColorPickerPage.xaml | 6 ++++++ .../Controls/ColorSpectrum.xaml | 20 ++++++++++--------- .../Controls/ColorSpectrum.xaml | 20 ++++++++++--------- 3 files changed, 28 insertions(+), 18 deletions(-) diff --git a/samples/ControlCatalog/Pages/ColorPickerPage.xaml b/samples/ControlCatalog/Pages/ColorPickerPage.xaml index eee041e5c5..ec34193f8c 100644 --- a/samples/ControlCatalog/Pages/ColorPickerPage.xaml +++ b/samples/ControlCatalog/Pages/ColorPickerPage.xaml @@ -19,5 +19,11 @@ Shape="Ring" Height="256" Width="256" /> + diff --git a/src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml b/src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml index 7065ba30f6..338ea69dac 100644 --- a/src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml +++ b/src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml @@ -11,6 +11,8 @@ + + - - - - - - - - - - - /// - /// The date to display. The default - /// . + /// The date to display. The default is . /// - /// + /// /// The specified date is not in the range defined by - /// + /// /// and - /// . + /// . /// public DateTime DisplayDate { @@ -215,11 +214,11 @@ namespace Avalonia.Controls /// /// /// The specified date is not in the range defined by - /// + /// /// and - /// , + /// , /// or the specified date is in the - /// + /// /// collection. /// public DateTime? SelectedDate @@ -233,9 +232,9 @@ namespace Avalonia.Controls /// /// /// The format that is used to display the selected date. The default is - /// . + /// . /// - /// + /// /// An specified format is not valid. /// public CalendarDatePickerFormat SelectedDateFormat @@ -251,18 +250,16 @@ namespace Avalonia.Controls } /// - /// Gets or sets the text that is displayed by the - /// . + /// Gets or sets the text that is displayed by the . /// /// - /// The text displayed by the - /// . + /// The text displayed by the . /// - /// + /// /// The text entered cannot be parsed to a valid date, and the exception /// is not suppressed. /// - /// + /// /// The text entered parses to a date that is not selectable. /// public string? Text From b0246965b8a72cc33403093919acfb449bc21c38 Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 10 Apr 2022 16:00:08 -0400 Subject: [PATCH 105/416] Modernize TextBox property accessor code --- src/Avalonia.Controls/TextBox.cs | 94 ++++++++++++++------------------ 1 file changed, 41 insertions(+), 53 deletions(-) diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index ce7d2f7e5f..82346599a4 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -239,23 +239,19 @@ namespace Avalonia.Controls public bool AcceptsReturn { - get { return GetValue(AcceptsReturnProperty); } - set { SetValue(AcceptsReturnProperty, value); } + get => GetValue(AcceptsReturnProperty); + set => SetValue(AcceptsReturnProperty, value); } public bool AcceptsTab { - get { return GetValue(AcceptsTabProperty); } - set { SetValue(AcceptsTabProperty, value); } + get => GetValue(AcceptsTabProperty); + set => SetValue(AcceptsTabProperty, value); } public int CaretIndex { - get - { - return _caretIndex; - } - + get => _caretIndex; set { value = CoerceCaretIndex(value); @@ -271,8 +267,8 @@ namespace Avalonia.Controls public bool IsReadOnly { - get { return GetValue(IsReadOnlyProperty); } - set { SetValue(IsReadOnlyProperty, value); } + get => GetValue(IsReadOnlyProperty); + set => SetValue(IsReadOnlyProperty, value); } public char PasswordChar @@ -301,11 +297,7 @@ namespace Avalonia.Controls public int SelectionStart { - get - { - return _selectionStart; - } - + get => _selectionStart; set { value = CoerceCaretIndex(value); @@ -325,11 +317,7 @@ namespace Avalonia.Controls public int SelectionEnd { - get - { - return _selectionEnd; - } - + get => _selectionEnd; set { value = CoerceCaretIndex(value); @@ -349,20 +337,20 @@ namespace Avalonia.Controls public int MaxLength { - get { return GetValue(MaxLengthProperty); } - set { SetValue(MaxLengthProperty, value); } + get => GetValue(MaxLengthProperty); + set => SetValue(MaxLengthProperty, value); } public int MaxLines { - get { return GetValue(MaxLinesProperty); } - set { SetValue(MaxLinesProperty, value); } + get => GetValue(MaxLinesProperty); + set => SetValue(MaxLinesProperty, value); } [Content] public string? Text { - get { return _text; } + get => _text; set { if (!_ignoreTextChanges) @@ -386,7 +374,7 @@ namespace Avalonia.Controls public string SelectedText { - get { return GetSelection(); } + get => GetSelection(); set { if (string.IsNullOrEmpty(value)) @@ -407,8 +395,8 @@ namespace Avalonia.Controls /// public HorizontalAlignment HorizontalContentAlignment { - get { return GetValue(HorizontalContentAlignmentProperty); } - set { SetValue(HorizontalContentAlignmentProperty, value); } + get => GetValue(HorizontalContentAlignmentProperty); + set => SetValue(HorizontalContentAlignmentProperty, value); } /// @@ -416,14 +404,14 @@ namespace Avalonia.Controls /// public VerticalAlignment VerticalContentAlignment { - get { return GetValue(VerticalContentAlignmentProperty); } - set { SetValue(VerticalContentAlignmentProperty, value); } + get => GetValue(VerticalContentAlignmentProperty); + set => SetValue(VerticalContentAlignmentProperty, value); } public TextAlignment TextAlignment { - get { return GetValue(TextAlignmentProperty); } - set { SetValue(TextAlignmentProperty, value); } + get => GetValue(TextAlignmentProperty); + set => SetValue(TextAlignmentProperty, value); } /// @@ -448,26 +436,26 @@ namespace Avalonia.Controls public object InnerLeftContent { - get { return GetValue(InnerLeftContentProperty); } - set { SetValue(InnerLeftContentProperty, value); } + get => GetValue(InnerLeftContentProperty); + set => SetValue(InnerLeftContentProperty, value); } public object InnerRightContent { - get { return GetValue(InnerRightContentProperty); } - set { SetValue(InnerRightContentProperty, value); } + get => GetValue(InnerRightContentProperty); + set => SetValue(InnerRightContentProperty, value); } public bool RevealPassword { - get { return GetValue(RevealPasswordProperty); } - set { SetValue(RevealPasswordProperty, value); } + get => GetValue(RevealPasswordProperty); + set => SetValue(RevealPasswordProperty, value); } public TextWrapping TextWrapping { - get { return GetValue(TextWrappingProperty); } - set { SetValue(TextWrappingProperty, value); } + get => GetValue(TextWrappingProperty); + set => SetValue(TextWrappingProperty, value); } /// @@ -475,8 +463,8 @@ namespace Avalonia.Controls /// public string NewLine { - get { return _newLine; } - set { SetAndRaise(NewLineProperty, ref _newLine, value); } + get => _newLine; + set => SetAndRaise(NewLineProperty, ref _newLine, value); } /// @@ -492,8 +480,8 @@ namespace Avalonia.Controls /// public bool CanCut { - get { return _canCut; } - private set { SetAndRaise(CanCutProperty, ref _canCut, value); } + get => _canCut; + private set => SetAndRaise(CanCutProperty, ref _canCut, value); } /// @@ -501,8 +489,8 @@ namespace Avalonia.Controls /// public bool CanCopy { - get { return _canCopy; } - private set { SetAndRaise(CanCopyProperty, ref _canCopy, value); } + get => _canCopy; + private set => SetAndRaise(CanCopyProperty, ref _canCopy, value); } /// @@ -510,8 +498,8 @@ namespace Avalonia.Controls /// public bool CanPaste { - get { return _canPaste; } - private set { SetAndRaise(CanPasteProperty, ref _canPaste, value); } + get => _canPaste; + private set => SetAndRaise(CanPasteProperty, ref _canPaste, value); } /// @@ -519,13 +507,13 @@ namespace Avalonia.Controls /// public bool IsUndoEnabled { - get { return GetValue(IsUndoEnabledProperty); } - set { SetValue(IsUndoEnabledProperty, value); } + get => GetValue(IsUndoEnabledProperty); + set => SetValue(IsUndoEnabledProperty, value); } public int UndoLimit { - get { return _undoRedoHelper.Limit; } + get => _undoRedoHelper.Limit; set { if (_undoRedoHelper.Limit != value) @@ -1544,7 +1532,7 @@ namespace Avalonia.Controls UndoRedoState UndoRedoHelper.IUndoRedoHost.UndoRedoState { - get { return new UndoRedoState(Text, CaretIndex); } + get => new UndoRedoState(Text, CaretIndex); set { Text = value.Text; From b043db169cc02d1018ed02c95ea41231acf4a010 Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 10 Apr 2022 16:15:34 -0400 Subject: [PATCH 106/416] Make sure to call base methods in Button --- src/Avalonia.Controls/Button.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Avalonia.Controls/Button.cs b/src/Avalonia.Controls/Button.cs index a4d15bab8d..9a7853cd4b 100644 --- a/src/Avalonia.Controls/Button.cs +++ b/src/Avalonia.Controls/Button.cs @@ -309,6 +309,8 @@ namespace Avalonia.Controls IsPressed = false; e.Handled = true; } + + base.OnKeyUp(e); } /// @@ -393,6 +395,8 @@ namespace Avalonia.Controls /// protected override void OnPointerCaptureLost(PointerCaptureLostEventArgs e) { + base.OnPointerCaptureLost(e); + IsPressed = false; } @@ -407,6 +411,8 @@ namespace Avalonia.Controls /// protected override void OnApplyTemplate(TemplateAppliedEventArgs e) { + base.OnApplyTemplate(e); + UnregisterFlyoutEvents(Flyout); RegisterFlyoutEvents(Flyout); UpdatePseudoClasses(); From 0329a8daa1ceef647d89f3ccece0ca1e94e544ec Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 10 Apr 2022 16:18:34 -0400 Subject: [PATCH 107/416] Support opening the calendar flyout when pressing outside the button --- .../Calendar/CalendarDatePicker.cs | 56 ++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs b/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs index fbd3f36aea..1f9b457c42 100644 --- a/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs +++ b/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs @@ -331,6 +331,7 @@ namespace Avalonia.Controls base.OnPropertyChanged(change); } + /// protected override void UpdateDataValidation(AvaloniaProperty property, BindingValue value) { if (property == SelectedDateProperty) @@ -339,6 +340,55 @@ namespace Avalonia.Controls } } + /// + protected override void OnPointerPressed(PointerPressedEventArgs e) + { + base.OnPointerPressed(e); + + if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) + { + e.Handled = true; + + _ignoreButtonClick = _isPopupClosing; + + _isPressed = true; + UpdatePseudoClasses(); + } + } + + /// + protected override void OnPointerReleased(PointerReleasedEventArgs e) + { + base.OnPointerReleased(e); + + if (_isPressed && e.InitialPressMouseButton == MouseButton.Left) + { + e.Handled = true; + + if (!_ignoreButtonClick) + { + HandlePopUp(); + } + else + { + _ignoreButtonClick = false; + } + + _isPressed = false; + UpdatePseudoClasses(); + } + } + + /// + protected override void OnPointerCaptureLost(PointerCaptureLostEventArgs e) + { + base.OnPointerCaptureLost(e); + + _isPressed = false; + UpdatePseudoClasses(); + } + + /// protected override void OnPointerWheelChanged(PointerWheelEventArgs e) { base.OnPointerWheelChanged(e); @@ -354,6 +404,7 @@ namespace Avalonia.Controls } } + /// protected override void OnGotFocus(GotFocusEventArgs e) { base.OnGotFocus(e); @@ -369,10 +420,14 @@ namespace Avalonia.Controls } } + /// protected override void OnLostFocus(RoutedEventArgs e) { base.OnLostFocus(e); + _isPressed = false; + UpdatePseudoClasses(); + SetSelectedDate(); } @@ -671,7 +726,6 @@ namespace Avalonia.Controls private bool ProcessDatePickerKey(KeyEventArgs e) { - switch (e.Key) { case Key.Enter: From 24a3dc560aaed3f7208bce05b190f4b79a3695fd Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 10 Apr 2022 16:23:39 -0400 Subject: [PATCH 108/416] Better support MinHeight --- src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml b/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml index c5a219540b..00f53a563d 100644 --- a/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml @@ -19,6 +19,7 @@ 12 + 32 - + + + + + + From dbe46697c9e75b3d77afed712321695e679588d0 Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 10 Apr 2022 20:21:53 -0400 Subject: [PATCH 110/416] Improve CalendarDatePicker Fluent style --- .../Controls/CalendarDatePicker.xaml | 38 ++++++++----------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml b/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml index e030e14243..592581e975 100644 --- a/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml @@ -23,7 +23,6 @@ - - - + - + + + + + @@ -31,7 +44,9 @@ - + diff --git a/src/Avalonia.Controls.DataGrid/DataGrid.cs b/src/Avalonia.Controls.DataGrid/DataGrid.cs index 9dfb34517b..9b67c9b096 100644 --- a/src/Avalonia.Controls.DataGrid/DataGrid.cs +++ b/src/Avalonia.Controls.DataGrid/DataGrid.cs @@ -669,8 +669,6 @@ namespace Avalonia.Controls ItemsProperty.Changed.AddClassHandler((x, e) => x.OnItemsPropertyChanged(e)); CanUserResizeColumnsProperty.Changed.AddClassHandler((x, e) => x.OnCanUserResizeColumnsChanged(e)); ColumnWidthProperty.Changed.AddClassHandler((x, e) => x.OnColumnWidthChanged(e)); - RowBackgroundProperty.Changed.AddClassHandler((x, e) => x.OnRowBackgroundChanged(e)); - AlternatingRowBackgroundProperty.Changed.AddClassHandler((x, e) => x.OnRowBackgroundChanged(e)); FrozenColumnCountProperty.Changed.AddClassHandler((x, e) => x.OnFrozenColumnCountChanged(e)); GridLinesVisibilityProperty.Changed.AddClassHandler((x, e) => x.OnGridLinesVisibilityChanged(e)); HeadersVisibilityProperty.Changed.AddClassHandler((x, e) => x.OnHeadersVisibilityChanged(e)); @@ -1144,14 +1142,6 @@ namespace Avalonia.Controls InvalidateCellsArrange(); } - private void OnRowBackgroundChanged(AvaloniaPropertyChangedEventArgs e) - { - foreach (DataGridRow row in GetAllRows()) - { - row.EnsureBackground(); - } - } - private void OnColumnWidthChanged(AvaloniaPropertyChangedEventArgs e) { var value = (DataGridLength)e.NewValue; diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumn.cs b/src/Avalonia.Controls.DataGrid/DataGridColumn.cs index a77b482436..4a7137dcda 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridColumn.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridColumn.cs @@ -199,7 +199,7 @@ namespace Avalonia.Controls if (change.Property == IsVisibleProperty) { OwningGrid?.OnColumnVisibleStateChanging(this); - var isVisible = (change as AvaloniaPropertyChangedEventArgs).NewValue.Value; + var isVisible = change.NewValue.GetValueOrDefault(); if (_headerCell != null) { diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumnCollection.cs b/src/Avalonia.Controls.DataGrid/DataGridColumnCollection.cs index 922b1d9c08..e7f9a9a6c4 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridColumnCollection.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridColumnCollection.cs @@ -12,7 +12,8 @@ namespace Avalonia.Controls { internal class DataGridColumnCollection : ObservableCollection { - private DataGrid _owningGrid; + private readonly Dictionary _columnsMap = new Dictionary(); + private readonly DataGrid _owningGrid; public DataGridColumnCollection(DataGrid owningGrid) { @@ -124,18 +125,8 @@ namespace Avalonia.Controls internal int VisibleColumnCount { - get - { - int visibleColumnCount = 0; - for (int columnIndex = 0; columnIndex < ItemsInternal.Count; columnIndex++) - { - if (ItemsInternal[columnIndex].IsVisible) - { - visibleColumnCount++; - } - } - return visibleColumnCount; - } + get; + private set; } internal double VisibleEdgedColumnsWidth @@ -287,20 +278,31 @@ namespace Avalonia.Controls { VisibleStarColumnCount = 0; VisibleEdgedColumnsWidth = 0; + VisibleColumnCount = 0; + _columnsMap.Clear(); + for (int columnIndex = 0; columnIndex < ItemsInternal.Count; columnIndex++) { - if (ItemsInternal[columnIndex].IsVisible) + var item = ItemsInternal[columnIndex]; + _columnsMap[columnIndex] = item.DisplayIndex; + if (item.IsVisible) { - ItemsInternal[columnIndex].EnsureWidth(); - if (ItemsInternal[columnIndex].Width.IsStar) + VisibleColumnCount++; + item.EnsureWidth(); + if (item.Width.IsStar) { VisibleStarColumnCount++; } - VisibleEdgedColumnsWidth += ItemsInternal[columnIndex].ActualWidth; + VisibleEdgedColumnsWidth += item.ActualWidth; } } } + internal int GetColumnDisplayIndex(int columnIndex) + { + return _columnsMap.TryGetValue(columnIndex, out var displayIndex) ? displayIndex : -1; + } + internal DataGridColumn GetColumnAtDisplayIndex(int displayIndex) { if (displayIndex < 0 || displayIndex >= ItemsInternal.Count || displayIndex >= DisplayIndexMap.Count) diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumns.cs b/src/Avalonia.Controls.DataGrid/DataGridColumns.cs index a4577ee952..ea8a121d29 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridColumns.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridColumns.cs @@ -444,12 +444,11 @@ namespace Avalonia.Controls // We need to explicitly collapse the cells of the invisible column because layout only goes through // visible ones - if (!updatedColumn.IsVisible) + ColumnHeaders.InvalidateChildIndex(); + foreach (var row in GetAllRows()) { - foreach (DataGridRow row in GetAllRows()) - { - row.Cells[updatedColumn.Index].IsVisible = false; - } + row.Cells[updatedColumn.Index].IsVisible = updatedColumn.IsVisible; + row.InvalidateCellsIndex(); } } diff --git a/src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs b/src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs index fade597ca1..a3095ad214 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs @@ -77,7 +77,7 @@ namespace Avalonia.Controls private set; } - public int Count => GetCount(true); + public int Count => TryGetCount(true, false, out var count) ? count : 0; public bool DataIsPrimitive { @@ -193,22 +193,25 @@ namespace Avalonia.Controls } } - internal bool Any() - { - return GetCount(false) > 0; - } - /// When "allowSlow" is false, method will not use Linq.Count() method and will return 0 or 1 instead. - private int GetCount(bool allowSlow) + /// If "getAny" is true, method can use Linq.Any() method to speedup. + internal bool TryGetCount(bool allowSlow, bool getAny, out int count) { - return DataSource switch + bool result; + (result, count) = DataSource switch { - ICollection collection => collection.Count, - DataGridCollectionView cv => cv.Count, - IEnumerable enumerable when allowSlow => enumerable.Cast().Count(), - IEnumerable enumerable when !allowSlow => enumerable.Cast().Any() ? 1 : 0, - _ => 0 + ICollection collection => (true, collection.Count), + DataGridCollectionView cv => (true, cv.Count), + IEnumerable enumerable when allowSlow && !getAny => (true, enumerable.Cast().Count()), + IEnumerable enumerable when getAny => (true, enumerable.Cast().Any() ? 1 : 0), + _ => (false, 0) }; + return result; + } + + internal bool Any() + { + return TryGetCount(false, true, out var count) && count > 0; } /// @@ -383,7 +386,7 @@ namespace Avalonia.Controls List propertyNames = TypeHelper.SplitPropertyPath(propertyName); for (int i = 0; i < propertyNames.Count; i++) { - propertyInfo = propertyType.GetPropertyOrIndexer(propertyNames[i], out object[] index); + propertyInfo = propertyType.GetPropertyOrIndexer(propertyNames[i], out _); if (propertyInfo == null || propertyType.GetIsReadOnly() || propertyInfo.GetIsReadOnly()) { // Either the data type is read-only, the property doesn't exist, or it does exist but is read-only @@ -391,11 +394,10 @@ namespace Avalonia.Controls } // Check if EditableAttribute is defined on the property and if it indicates uneditable - object[] attributes = propertyInfo.GetCustomAttributes(typeof(EditableAttribute), true); + var attributes = propertyInfo.GetCustomAttributes(typeof(EditableAttribute), true); if (attributes != null && attributes.Length > 0) { - EditableAttribute editableAttribute = attributes[0] as EditableAttribute; - Debug.Assert(editableAttribute != null); + var editableAttribute = (EditableAttribute)attributes[0]; if (!editableAttribute.AllowEdit) { return true; diff --git a/src/Avalonia.Controls.DataGrid/DataGridRow.cs b/src/Avalonia.Controls.DataGrid/DataGridRow.cs index 1efce7c0b8..a6faec752d 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridRow.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridRow.cs @@ -543,7 +543,6 @@ namespace Avalonia.Controls RootElement = e.NameScope.Find(DATAGRIDROW_elementRoot); if (RootElement != null) { - EnsureBackground(); UpdatePseudoClasses(); } @@ -668,43 +667,9 @@ namespace Avalonia.Controls Slot = -1; } - // Make sure the row's background is set to its correct value. It could be explicity set or inherit - // DataGrid.RowBackground or DataGrid.AlternatingRowBackground - internal void EnsureBackground() + internal void InvalidateCellsIndex() { - // Inherit the DataGrid's RowBackground properties only if this row doesn't explicity have a background set - if (RootElement != null && OwningGrid != null) - { - IBrush newBackground = null; - if (Background == null) - { - if (Index % 2 == 0 || OwningGrid.AlternatingRowBackground == null) - { - // Use OwningGrid.RowBackground if the index is even or if the OwningGrid.AlternatingRowBackground is null - if (OwningGrid.RowBackground != null) - { - newBackground = OwningGrid.RowBackground; - } - } - else - { - // Alternate row - if (OwningGrid.AlternatingRowBackground != null) - { - newBackground = OwningGrid.AlternatingRowBackground; - } - } - } - else - { - newBackground = Background; - } - - if (RootElement.Background != newBackground) - { - RootElement.Background = newBackground; - } - } + _cellsElement?.InvalidateChildIndex(); } internal void EnsureFillerVisibility() diff --git a/src/Avalonia.Controls.DataGrid/DataGridRows.cs b/src/Avalonia.Controls.DataGrid/DataGridRows.cs index 1d5c899993..52b67b8921 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridRows.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridRows.cs @@ -5,6 +5,7 @@ using Avalonia.Collections; using Avalonia.Controls.Utils; +using Avalonia.LogicalTree; using Avalonia.Media; using Avalonia.Utilities; using System; @@ -16,7 +17,7 @@ using System.Linq; namespace Avalonia.Controls { - public partial class DataGrid + public partial class DataGrid : IChildIndexProvider { internal bool AreRowBottomGridLinesRequired @@ -123,6 +124,26 @@ namespace Avalonia.Controls } } + internal EventHandler _childIndexChanged; + + event EventHandler IChildIndexProvider.ChildIndexChanged + { + add => _childIndexChanged += value; + remove => _childIndexChanged -= value; + } + + int IChildIndexProvider.GetChildIndex(ILogical child) + { + return child is DataGridRow row + ? row.Index + : throw new InvalidOperationException("Invalid DataGrid child"); + } + + bool IChildIndexProvider.TryGetTotalCount(out int count) + { + return DataConnection.TryGetCount(false, true, out count); + } + /// /// Clears the entire selection. Displayed rows are deselected explicitly to visualize /// potential transition effects @@ -811,7 +832,7 @@ namespace Avalonia.Controls if (row.Slot > slotDeleted) { CorrectRowAfterDeletion(row, wasRow); - row.EnsureBackground(); + _childIndexChanged?.Invoke(this, new ChildIndexChangedEventArgs(row)); } } @@ -867,7 +888,7 @@ namespace Avalonia.Controls if (row.Slot >= slotInserted) { DataGrid.CorrectRowAfterInsertion(row, rowInserted); - row.EnsureBackground(); + _childIndexChanged?.Invoke(this, new ChildIndexChangedEventArgs(row)); } } @@ -1485,8 +1506,8 @@ namespace Avalonia.Controls // If the row has been recycled, reapply the BackgroundBrush if (row.IsRecycled) { - row.EnsureBackground(); row.ApplyCellsState(); + _childIndexChanged?.Invoke(this, new ChildIndexChangedEventArgs(row)); } else if (row == EditingRow) { diff --git a/src/Avalonia.Controls.DataGrid/Primitives/DataGridCellsPresenter.cs b/src/Avalonia.Controls.DataGrid/Primitives/DataGridCellsPresenter.cs index c5fe9f0cb2..e07c933039 100644 --- a/src/Avalonia.Controls.DataGrid/Primitives/DataGridCellsPresenter.cs +++ b/src/Avalonia.Controls.DataGrid/Primitives/DataGridCellsPresenter.cs @@ -3,12 +3,13 @@ // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. +using Avalonia.LogicalTree; using Avalonia.Media; using Avalonia.Utilities; using System; +using System.Collections.Generic; +using System.Collections.Specialized; using System.Diagnostics; -using Avalonia.Controls; -using Avalonia.Controls.Utils; namespace Avalonia.Controls.Primitives { @@ -16,9 +17,10 @@ namespace Avalonia.Controls.Primitives /// Used within the template of a /// to specify the location in the control's visual tree where the cells are to be added. /// - public sealed class DataGridCellsPresenter : Panel + public sealed class DataGridCellsPresenter : Panel, IChildIndexProvider { private double _fillerLeftEdge; + private EventHandler _childIndexChanged; // The desired height needs to be cached due to column virtualization; otherwise, the cells // would grow and shrink as the DataGrid scrolls horizontally @@ -42,6 +44,25 @@ namespace Avalonia.Controls.Primitives set; } + event EventHandler IChildIndexProvider.ChildIndexChanged + { + add => _childIndexChanged += value; + remove => _childIndexChanged -= value; + } + + int IChildIndexProvider.GetChildIndex(ILogical child) + { + return child is DataGridCell cell + ? OwningGrid.ColumnsInternal.GetColumnDisplayIndex(cell.ColumnIndex) + : throw new InvalidOperationException("Invalid cell type"); + } + + bool IChildIndexProvider.TryGetTotalCount(out int count) + { + count = OwningGrid.ColumnsInternal.VisibleColumnCount; + return true; + } + /// /// Arranges the content of the . /// @@ -120,6 +141,13 @@ namespace Avalonia.Controls.Primitives } } + protected override void ChildrenChanged(object sender, NotifyCollectionChangedEventArgs e) + { + base.ChildrenChanged(sender, e); + + InvalidateChildIndex(); + } + private static void EnsureCellDisplay(DataGridCell cell, bool displayColumn) { if (cell.IsCurrent) @@ -304,6 +332,11 @@ namespace Avalonia.Controls.Primitives DesiredHeight = 0; } + internal void InvalidateChildIndex() + { + _childIndexChanged?.Invoke(this, ChildIndexChangedEventArgs.Empty); + } + private bool ShouldDisplayCell(DataGridColumn column, double frozenLeftEdge, double scrollingLeftEdge) { if (!column.IsVisible) diff --git a/src/Avalonia.Controls.DataGrid/Primitives/DataGridColumnHeadersPresenter.cs b/src/Avalonia.Controls.DataGrid/Primitives/DataGridColumnHeadersPresenter.cs index 4eed119240..108dc8ded7 100644 --- a/src/Avalonia.Controls.DataGrid/Primitives/DataGridColumnHeadersPresenter.cs +++ b/src/Avalonia.Controls.DataGrid/Primitives/DataGridColumnHeadersPresenter.cs @@ -3,8 +3,10 @@ // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. +using Avalonia.LogicalTree; using Avalonia.Media; using System; +using System.Collections.Specialized; using System.Diagnostics; namespace Avalonia.Controls.Primitives @@ -13,10 +15,11 @@ namespace Avalonia.Controls.Primitives /// Used within the template of a to specify the /// location in the control's visual tree where the column headers are to be added. /// - public sealed class DataGridColumnHeadersPresenter : Panel + public sealed class DataGridColumnHeadersPresenter : Panel, IChildIndexProvider { private Control _dragIndicator; private IControl _dropLocationIndicator; + private EventHandler _childIndexChanged; /// /// Tracks which column is currently being dragged. @@ -104,6 +107,25 @@ namespace Avalonia.Controls.Primitives set; } + event EventHandler IChildIndexProvider.ChildIndexChanged + { + add => _childIndexChanged += value; + remove => _childIndexChanged -= value; + } + + int IChildIndexProvider.GetChildIndex(ILogical child) + { + return child is DataGridColumnHeader header + ? OwningGrid.ColumnsInternal.GetColumnDisplayIndex(header.ColumnIndex) + : throw new InvalidOperationException("Invalid cell type"); + } + + bool IChildIndexProvider.TryGetTotalCount(out int count) + { + count = OwningGrid.ColumnsInternal.VisibleColumnCount; + return true; + } + /// /// Arranges the content of the . /// @@ -391,5 +413,17 @@ namespace Avalonia.Controls.Primitives OwningGrid.ColumnsInternal.EnsureVisibleEdgedColumnsWidth(); return new Size(OwningGrid.ColumnsInternal.VisibleEdgedColumnsWidth, height); } + + protected override void ChildrenChanged(object sender, NotifyCollectionChangedEventArgs e) + { + base.ChildrenChanged(sender, e); + + InvalidateChildIndex(); + } + + internal void InvalidateChildIndex() + { + _childIndexChanged?.Invoke(this, ChildIndexChangedEventArgs.Empty); + } } } diff --git a/src/Avalonia.Controls.DataGrid/Primitives/DataGridRowsPresenter.cs b/src/Avalonia.Controls.DataGrid/Primitives/DataGridRowsPresenter.cs index 308ebc69d4..0b9bd0a564 100644 --- a/src/Avalonia.Controls.DataGrid/Primitives/DataGridRowsPresenter.cs +++ b/src/Avalonia.Controls.DataGrid/Primitives/DataGridRowsPresenter.cs @@ -7,8 +7,8 @@ using System; using System.Diagnostics; using Avalonia.Input; -using Avalonia.Input.GestureRecognizers; using Avalonia.Layout; +using Avalonia.LogicalTree; using Avalonia.Media; namespace Avalonia.Controls.Primitives @@ -17,7 +17,7 @@ namespace Avalonia.Controls.Primitives /// Used within the template of a to specify the /// location in the control's visual tree where the rows are to be added. /// - public sealed class DataGridRowsPresenter : Panel + public sealed class DataGridRowsPresenter : Panel, IChildIndexProvider { public DataGridRowsPresenter() { @@ -44,6 +44,22 @@ namespace Avalonia.Controls.Primitives } } + event EventHandler IChildIndexProvider.ChildIndexChanged + { + add => OwningGrid._childIndexChanged += value; + remove => OwningGrid._childIndexChanged -= value; + } + + bool IChildIndexProvider.TryGetTotalCount(out int count) + { + return ((IChildIndexProvider)OwningGrid).TryGetTotalCount(out count); + } + + int IChildIndexProvider.GetChildIndex(ILogical child) + { + return ((IChildIndexProvider)OwningGrid).GetChildIndex(child); + } + /// /// Arranges the content of the . /// diff --git a/src/Avalonia.Controls.DataGrid/Themes/Default.xaml b/src/Avalonia.Controls.DataGrid/Themes/Default.xaml index 8d4e327f3e..05c3d2dd19 100644 --- a/src/Avalonia.Controls.DataGrid/Themes/Default.xaml +++ b/src/Avalonia.Controls.DataGrid/Themes/Default.xaml @@ -103,8 +103,9 @@ + Background="{TemplateBinding Background}" + RowDefinitions="*,Auto,Auto" + ColumnDefinitions="Auto,*"> @@ -118,6 +119,13 @@ + + + + + + diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs index 0cd72dc91c..114aa9727d 100644 --- a/src/Avalonia.Controls/ItemsControl.cs +++ b/src/Avalonia.Controls/ItemsControl.cs @@ -166,7 +166,7 @@ namespace Avalonia.Controls if (Presenter is IChildIndexProvider innerProvider) { innerProvider.ChildIndexChanged += PresenterChildIndexChanged; - _childIndexChanged?.Invoke(this, new ChildIndexChangedEventArgs()); + _childIndexChanged?.Invoke(this, ChildIndexChangedEventArgs.Empty); } } diff --git a/src/Avalonia.Controls/Panel.cs b/src/Avalonia.Controls/Panel.cs index 482a7fab84..2230b4b0d2 100644 --- a/src/Avalonia.Controls/Panel.cs +++ b/src/Avalonia.Controls/Panel.cs @@ -147,7 +147,7 @@ namespace Avalonia.Controls throw new NotSupportedException(); } - _childIndexChanged?.Invoke(this, new ChildIndexChangedEventArgs()); + _childIndexChanged?.Invoke(this, ChildIndexChangedEventArgs.Empty); InvalidateMeasureOnChildrenChanged(); } diff --git a/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs b/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs index f938c8d437..2821fa8cf0 100644 --- a/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs +++ b/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs @@ -158,7 +158,7 @@ namespace Avalonia.Controls.Presenters { ItemsChanged(e); - _childIndexChanged?.Invoke(this, new ChildIndexChangedEventArgs()); + _childIndexChanged?.Invoke(this, ChildIndexChangedEventArgs.Empty); } } diff --git a/src/Avalonia.Styling/LogicalTree/ChildIndexChangedEventArgs.cs b/src/Avalonia.Styling/LogicalTree/ChildIndexChangedEventArgs.cs index de41f5292c..afc6c1f5fc 100644 --- a/src/Avalonia.Styling/LogicalTree/ChildIndexChangedEventArgs.cs +++ b/src/Avalonia.Styling/LogicalTree/ChildIndexChangedEventArgs.cs @@ -8,6 +8,8 @@ namespace Avalonia.LogicalTree /// public class ChildIndexChangedEventArgs : EventArgs { + public static new ChildIndexChangedEventArgs Empty { get; } = new ChildIndexChangedEventArgs(); + public ChildIndexChangedEventArgs() { } From f7e8b7658fbc68e127f7dc508e386459bf763637 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 31 Dec 2021 03:43:23 -0500 Subject: [PATCH 120/416] Do not hardcode Margin and VerticalAlignment on data grid text column --- src/Avalonia.Controls.DataGrid/DataGridTextColumn.cs | 5 +---- src/Avalonia.Controls.DataGrid/Themes/Default.xaml | 4 ++++ src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml | 5 +++++ 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Controls.DataGrid/DataGridTextColumn.cs b/src/Avalonia.Controls.DataGrid/DataGridTextColumn.cs index 863910c226..68736dee7f 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridTextColumn.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridTextColumn.cs @@ -19,8 +19,6 @@ namespace Avalonia.Controls /// public class DataGridTextColumn : DataGridBoundColumn { - private const string DATAGRID_TextColumnCellTextBlockMarginKey = "DataGridTextColumnCellTextBlockMargin"; - /// /// Initializes a new instance of the class. /// @@ -178,8 +176,7 @@ namespace Avalonia.Controls { TextBlock textBlockElement = new TextBlock { - [!Layoutable.MarginProperty] = new DynamicResourceExtension(DATAGRID_TextColumnCellTextBlockMarginKey), - VerticalAlignment = VerticalAlignment.Center + Name = "CellTextBlock" }; SyncProperties(textBlockElement); diff --git a/src/Avalonia.Controls.DataGrid/Themes/Default.xaml b/src/Avalonia.Controls.DataGrid/Themes/Default.xaml index 05c3d2dd19..b066702212 100644 --- a/src/Avalonia.Controls.DataGrid/Themes/Default.xaml +++ b/src/Avalonia.Controls.DataGrid/Themes/Default.xaml @@ -29,6 +29,10 @@ + + + From 5019ee09853f91b695563b3ea330dcc0f04673a4 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 31 Dec 2021 03:46:59 -0500 Subject: [PATCH 121/416] Add missing support of Border properties to the DataGrid templates --- .../Themes/Default.xaml | 135 ++++++---- .../Themes/Fluent.xaml | 255 +++++++++--------- 2 files changed, 213 insertions(+), 177 deletions(-) diff --git a/src/Avalonia.Controls.DataGrid/Themes/Default.xaml b/src/Avalonia.Controls.DataGrid/Themes/Default.xaml index b066702212..0d1fe43eb6 100644 --- a/src/Avalonia.Controls.DataGrid/Themes/Default.xaml +++ b/src/Avalonia.Controls.DataGrid/Themes/Default.xaml @@ -12,20 +12,25 @@ - - - - - + + + + + + + @@ -44,35 +49,40 @@ - - - - - - + + + + + + + - + - + - + + @@ -106,19 +116,24 @@ @@ -192,12 +207,18 @@ + ColumnDefinitions="Auto,Auto,Auto,Auto" + RowDefinitions="Auto,*,Auto"> - + @@ -250,9 +271,11 @@ - + CornerRadius="{TemplateBinding CornerRadius}"> diff --git a/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml b/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml index 6a6ab283ab..8243d9c22d 100644 --- a/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml +++ b/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml @@ -93,54 +93,57 @@ - + + - - - - - + + + + - + - + - - + + + @@ -185,57 +188,58 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + - - - + Fill="{TemplateBinding SeparatorBrush}" + IsVisible="{TemplateBinding AreSeparatorsVisible}" /> + + + + + - + @@ -276,35 +280,40 @@ - - - - - - - - + + + + + + + - + DataGridFrozenGrid.IsFrozen="True" /> + + - + + @@ -315,7 +324,7 @@ - + @@ -443,9 +452,12 @@ Width="12" Height="12" Margin="12,0,0,0" + BorderBrush="{TemplateBinding BorderBrush}" + BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" - Foreground="{TemplateBinding Foreground}" - Focusable="False" /> + CornerRadius="{TemplateBinding CornerRadius}" + Focusable="False" + Foreground="{TemplateBinding Foreground}" /> - - + CornerRadius="{TemplateBinding CornerRadius}"> + From af9f27677d0289b5c45f366f2594f393d2782eb1 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 31 Dec 2021 04:21:11 -0500 Subject: [PATCH 122/416] Fix NRE in DataGrid --- .../DataGridColumns.cs | 2 +- .../DataGridRows.cs | 28 +++---------------- .../Primitives/DataGridRowsPresenter.cs | 19 +++++++++---- 3 files changed, 19 insertions(+), 30 deletions(-) diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumns.cs b/src/Avalonia.Controls.DataGrid/DataGridColumns.cs index ea8a121d29..52f0ad7537 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridColumns.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridColumns.cs @@ -444,7 +444,7 @@ namespace Avalonia.Controls // We need to explicitly collapse the cells of the invisible column because layout only goes through // visible ones - ColumnHeaders.InvalidateChildIndex(); + ColumnHeaders?.InvalidateChildIndex(); foreach (var row in GetAllRows()) { row.Cells[updatedColumn.Index].IsVisible = updatedColumn.IsVisible; diff --git a/src/Avalonia.Controls.DataGrid/DataGridRows.cs b/src/Avalonia.Controls.DataGrid/DataGridRows.cs index 52b67b8921..f3afe2c42d 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridRows.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridRows.cs @@ -17,7 +17,7 @@ using System.Linq; namespace Avalonia.Controls { - public partial class DataGrid : IChildIndexProvider + public partial class DataGrid { internal bool AreRowBottomGridLinesRequired @@ -124,26 +124,6 @@ namespace Avalonia.Controls } } - internal EventHandler _childIndexChanged; - - event EventHandler IChildIndexProvider.ChildIndexChanged - { - add => _childIndexChanged += value; - remove => _childIndexChanged -= value; - } - - int IChildIndexProvider.GetChildIndex(ILogical child) - { - return child is DataGridRow row - ? row.Index - : throw new InvalidOperationException("Invalid DataGrid child"); - } - - bool IChildIndexProvider.TryGetTotalCount(out int count) - { - return DataConnection.TryGetCount(false, true, out count); - } - /// /// Clears the entire selection. Displayed rows are deselected explicitly to visualize /// potential transition effects @@ -832,7 +812,7 @@ namespace Avalonia.Controls if (row.Slot > slotDeleted) { CorrectRowAfterDeletion(row, wasRow); - _childIndexChanged?.Invoke(this, new ChildIndexChangedEventArgs(row)); + _rowsPresenter?.InvalidateChildIndex(row); } } @@ -888,7 +868,7 @@ namespace Avalonia.Controls if (row.Slot >= slotInserted) { DataGrid.CorrectRowAfterInsertion(row, rowInserted); - _childIndexChanged?.Invoke(this, new ChildIndexChangedEventArgs(row)); + _rowsPresenter?.InvalidateChildIndex(row); } } @@ -1507,7 +1487,7 @@ namespace Avalonia.Controls if (row.IsRecycled) { row.ApplyCellsState(); - _childIndexChanged?.Invoke(this, new ChildIndexChangedEventArgs(row)); + _rowsPresenter?.InvalidateChildIndex(row); } else if (row == EditingRow) { diff --git a/src/Avalonia.Controls.DataGrid/Primitives/DataGridRowsPresenter.cs b/src/Avalonia.Controls.DataGrid/Primitives/DataGridRowsPresenter.cs index 0b9bd0a564..5d82689eff 100644 --- a/src/Avalonia.Controls.DataGrid/Primitives/DataGridRowsPresenter.cs +++ b/src/Avalonia.Controls.DataGrid/Primitives/DataGridRowsPresenter.cs @@ -19,6 +19,8 @@ namespace Avalonia.Controls.Primitives /// public sealed class DataGridRowsPresenter : Panel, IChildIndexProvider { + private EventHandler _childIndexChanged; + public DataGridRowsPresenter() { AddHandler(Gestures.ScrollGestureEvent, OnScrollGesture); @@ -46,18 +48,25 @@ namespace Avalonia.Controls.Primitives event EventHandler IChildIndexProvider.ChildIndexChanged { - add => OwningGrid._childIndexChanged += value; - remove => OwningGrid._childIndexChanged -= value; + add => _childIndexChanged += value; + remove => _childIndexChanged -= value; + } + + int IChildIndexProvider.GetChildIndex(ILogical child) + { + return child is DataGridRow row + ? row.Index + : throw new InvalidOperationException("Invalid DataGrid child"); } bool IChildIndexProvider.TryGetTotalCount(out int count) { - return ((IChildIndexProvider)OwningGrid).TryGetTotalCount(out count); + return OwningGrid.DataConnection.TryGetCount(false, true, out count); } - int IChildIndexProvider.GetChildIndex(ILogical child) + internal void InvalidateChildIndex(DataGridRow row) { - return ((IChildIndexProvider)OwningGrid).GetChildIndex(child); + _childIndexChanged?.Invoke(this, new ChildIndexChangedEventArgs(row)); } /// From 64cce62e7b34f2b843d3258de640858d6bfe94f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Komosi=C5=84ski?= Date: Tue, 12 Apr 2022 23:09:46 +0200 Subject: [PATCH 123/416] Benchmark for finding resources. --- .../Styling/ResourceBenchmarks.cs | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs diff --git a/tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs b/tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs new file mode 100644 index 0000000000..1445fcba05 --- /dev/null +++ b/tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs @@ -0,0 +1,91 @@ +using System; +using Avalonia.Controls; +using Avalonia.Platform; +using Avalonia.PlatformSupport; +using Avalonia.Styling; +using Avalonia.Themes.Fluent; +using Avalonia.UnitTests; +using BenchmarkDotNet.Attributes; +using Moq; + +namespace Avalonia.Benchmarks.Styling +{ + [MemoryDiagnoser] + public class ResourceBenchmarks : IDisposable + { + private readonly Control _searchStart; + private readonly IDisposable _app; + + private static IDisposable CreateApp() + { + var services = new TestServices( + assetLoader: new AssetLoader(), + globalClock: new MockGlobalClock(), + platform: new AppBuilder().RuntimePlatform, + renderInterface: new MockPlatformRenderInterface(), + standardCursorFactory: Mock.Of(), + styler: new Styler(), + theme: () => LoadTheme(), + threadingInterface: new NullThreadingPlatform(), + fontManagerImpl: new MockFontManagerImpl(), + textShaperImpl: new MockTextShaperImpl(), + windowingPlatform: new MockWindowingPlatform()); + + return UnitTestApplication.Start(services); + } + + private static Styles LoadTheme() + { + AssetLoader.RegisterResUriParsers(); + + var hostStyle = new Style(); + + hostStyle.Resources.Add("test", null); + + return new Styles + { + hostStyle, + new FluentTheme(new Uri("avares://Avalonia.Benchmarks")) + }; + } + + public void Dispose() + { + _app.Dispose(); + } + + public ResourceBenchmarks() + { + _searchStart = new Button(); + + _app = CreateApp(); + + Decorator root = new TestRoot(true, null) + { + Renderer = new NullRenderer() + }; + + var current = root; + + for (int i = 0; i < 10; i++) + { + var child = new Decorator(); + + current.Child = child; + + current = child; + } + + current.Child = _searchStart; + } + + [Benchmark(Baseline = true)] + public void FindResource() + { + for (int i = 0; i < 100; ++i) + { + _searchStart.FindResource("test"); + } + } + } +} From 55e681a326673f680f8324fa1bb365be4305f86e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Komosi=C5=84ski?= Date: Tue, 12 Apr 2022 23:15:55 +0200 Subject: [PATCH 124/416] Add more benchmark cases. --- .../Styling/ResourceBenchmarks.cs | 35 +++++++++++++++---- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs b/tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs index 1445fcba05..babfaef6ab 100644 --- a/tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs +++ b/tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs @@ -38,14 +38,17 @@ namespace Avalonia.Benchmarks.Styling { AssetLoader.RegisterResUriParsers(); - var hostStyle = new Style(); + var preHost = new Style(); + preHost.Resources.Add("preTheme", null); - hostStyle.Resources.Add("test", null); + var postHost = new Style(); + postHost.Resources.Add("postTheme", null); return new Styles { - hostStyle, - new FluentTheme(new Uri("avares://Avalonia.Benchmarks")) + preHost, + new FluentTheme(new Uri("avares://Avalonia.Benchmarks")), + postHost }; } @@ -79,12 +82,30 @@ namespace Avalonia.Benchmarks.Styling current.Child = _searchStart; } - [Benchmark(Baseline = true)] - public void FindResource() + [Benchmark] + public void FindPreResource() { for (int i = 0; i < 100; ++i) { - _searchStart.FindResource("test"); + _searchStart.FindResource("preTheme"); + } + } + + [Benchmark] + public void FindPostResource() + { + for (int i = 0; i < 100; ++i) + { + _searchStart.FindResource("postTheme"); + } + } + + [Benchmark] + public void FindNotExistingResource() + { + for (int i = 0; i < 100; ++i) + { + _searchStart.FindResource("notPresent"); } } } From fb0da85ad3a998bdf711b42e7b619550e6ff9de3 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 14 Apr 2022 14:39:44 +0200 Subject: [PATCH 125/416] Merge core libraries. (#5831) * Merge core libraries. Everything below `Avalonia.Controls` into `Avalonia.Base`. * Move new files to correct place. * Removed unused dirs/projects, * Removed outdated references from theme assemblies. * Merge unit tests to match new assembly layout. * Fixup test namespaces. * Make directory match namespace. * Move files to match namespace. * Move files to match namespace. * Fix up incorrect namespace. `Avalonia.Visuals.Media.Imaging` -> `Avalonia.Media.Imaging`. * Fix resource URL. * Removed outdated dependencies. * Added missing project reference. * Update test namespaces. * Fix merge error. * Fix merge errors. * Fix bad merge in WindowsInteropTest.csproj. * Fix up merge errors in csprojs. * Remove merged tests from nuke. * Fix up namespace. * Fix compile error. * Fix failing tests. Now that more unit tests are present in Avalonia.Base.UnitTests, general `AvaloniaObject` properties are getting registered. Ignore those. Co-authored-by: Jumar Macato <16554748+jmacato@users.noreply.github.com> --- Avalonia.sln | 318 ------------------ Directory.Build.props | 1 + build/CoreLibraries.props | 6 - nukebuild/Build.cs | 6 - .../ControlCatalog.Android.csproj | 40 +++ .../ControlCatalog.iOS.csproj | 49 +++ .../RenderDemo/Pages/PathMeasurementPage.cs | 6 - .../Pages/RenderTargetBitmapPage.cs | 1 - samples/RenderDemo/RenderDemo.csproj | 2 +- .../Avalonia.AndroidTestApplication.csproj | 36 ++ src/Avalonia.Animation/ApiCompatBaseline.txt | 5 - .../Avalonia.Animation.csproj | 15 - .../Properties/AssemblyInfo.cs | 9 - .../Animation}/Animatable.cs | 0 .../Animation}/Animation.cs | 0 .../Animation}/AnimationInstance`1.cs | 0 .../Animation}/AnimatorDrivenTransition.cs | 0 .../Animation}/AnimatorKeyFrame.cs | 0 .../AnimatorTransitionObservable.cs | 0 .../Animation}/Animators/Animator`1.cs | 0 .../Animation/Animators/BaseBrushAnimator.cs | 0 .../Animation}/Animators/BoolAnimator.cs | 0 .../Animation/Animators/BoxShadowAnimator.cs | 0 .../Animation/Animators/BoxShadowsAnimator.cs | 0 .../Animation}/Animators/ByteAnimator.cs | 0 .../Animation/Animators/ColorAnimator.cs | 0 .../Animators/CornerRadiusAnimator.cs | 0 .../Animation}/Animators/DecimalAnimator.cs | 0 .../Animation}/Animators/DoubleAnimator.cs | 0 .../Animation}/Animators/FloatAnimator.cs | 0 .../Animators/GradientBrushAnimator.cs | 0 .../Animation}/Animators/Int16Animator.cs | 0 .../Animation}/Animators/Int32Animator.cs | 0 .../Animation}/Animators/Int64Animator.cs | 0 .../Animation/Animators/PointAnimator.cs | 0 .../Animation/Animators/RectAnimator.cs | 0 .../Animators/RelativePointAnimator.cs | 0 .../Animation/Animators/SizeAnimator.cs | 0 .../Animators/SolidColorBrushAnimator.cs | 0 .../Animation/Animators/ThicknessAnimator.cs | 0 .../Animation/Animators/TransformAnimator.cs | 0 .../Animators/TransformOperationsAnimator.cs | 0 .../Animation}/Animators/UInt16Animator.cs | 0 .../Animation}/Animators/UInt32Animator.cs | 0 .../Animation}/Animators/UInt64Animator.cs | 0 .../Animation/Animators/VectorAnimator.cs | 0 .../Animation}/Clock.cs | 0 .../Animation}/ClockBase.cs | 0 .../Animation/CompositePageTransition.cs | 0 .../Animation/CrossFade.cs | 0 .../Animation}/Cue.cs | 0 .../DisposeAnimationInstanceSubject.cs | 0 .../Animation/Easings}/BackEaseIn.cs | 0 .../Animation/Easings}/BackEaseInOut.cs | 0 .../Animation/Easings}/BackEaseOut.cs | 0 .../Animation/Easings}/BounceEaseIn.cs | 0 .../Animation/Easings}/BounceEaseInOut.cs | 0 .../Animation/Easings}/BounceEaseOut.cs | 0 .../Animation/Easings}/CircularEaseIn.cs | 0 .../Animation/Easings}/CircularEaseInOut.cs | 0 .../Animation/Easings}/CircularEaseOut.cs | 0 .../Animation/Easings}/CubicEaseIn.cs | 0 .../Animation/Easings}/CubicEaseInOut.cs | 0 .../Animation/Easings}/CubicEaseOut.cs | 0 .../Animation/Easings}/Easing.cs | 0 .../Animation/Easings}/EasingTypeConverter.cs | 0 .../Animation/Easings}/ElasticEaseIn.cs | 0 .../Animation/Easings}/ElasticEaseInOut.cs | 0 .../Animation/Easings}/ElasticEaseOut.cs | 0 .../Animation/Easings}/ExponentialEaseIn.cs | 0 .../Easings}/ExponentialEaseInOut.cs | 0 .../Animation/Easings}/ExponentialEaseOut.cs | 0 .../Animation/Easings}/IEasing.cs | 0 .../Animation/Easings}/LinearEasing.cs | 0 .../Animation/Easings}/QuadraticEaseIn.cs | 0 .../Animation/Easings}/QuadraticEaseInOut.cs | 0 .../Animation/Easings}/QuadraticEaseOut.cs | 0 .../Animation/Easings}/QuarticEaseIn.cs | 0 .../Animation/Easings}/QuarticEaseInOut.cs | 0 .../Animation/Easings}/QuarticEaseOut.cs | 0 .../Animation/Easings}/QuinticEaseIn.cs | 0 .../Animation/Easings}/QuinticEaseInOut.cs | 0 .../Animation/Easings}/QuinticEaseOut.cs | 0 .../Animation/Easings}/SineEaseIn.cs | 0 .../Animation/Easings}/SineEaseInOut.cs | 0 .../Animation/Easings}/SineEaseOut.cs | 0 .../Animation/Easings}/SplineEasing.cs | 0 .../Animation}/FillMode.cs | 0 .../Animation}/IAnimation.cs | 0 .../Animation}/IAnimationSetter.cs | 0 .../Animation}/IAnimator.cs | 0 .../Animation}/IClock.cs | 0 .../Animation}/IGlobalClock.cs | 0 .../Animation/IPageTransition.cs | 0 .../Animation}/ITransition.cs | 0 .../Animation}/IterationCount.cs | 0 .../Animation}/IterationCountTypeConverter.cs | 0 .../Animation}/KeyFrame.cs | 0 .../Animation}/KeyFrames.cs | 0 .../Animation}/KeySpline.cs | 0 .../Animation}/KeySplineTypeConverter.cs | 0 .../Animation/PageSlide.cs | 0 .../Animation}/PlayState.cs | 0 .../Animation}/PlaybackDirection.cs | 0 .../Animation/RenderLoopClock.cs | 0 .../Animation}/Transition.cs | 0 .../Animation}/TransitionInstance.cs | 0 .../Animation}/TransitionObservableBase.cs | 0 .../Animation}/Transitions.cs | 0 .../Transitions/BoxShadowsTransition.cs | 0 .../Animation/Transitions/BrushTransition.cs | 0 .../Animation/Transitions/ColorTransition.cs | 0 .../Transitions/CornerRadiusTransition.cs | 0 .../Transitions/DoubleTransition.cs | 0 .../Animation}/Transitions/FloatTransition.cs | 0 .../Transitions/IntegerTransition.cs | 0 .../Animation/Transitions/PointTransition.cs | 0 .../Transitions/RelativePointTransition.cs | 0 .../Animation/Transitions/SizeTransition.cs | 0 .../Transitions/ThicknessTransition.cs | 0 .../TransformOperationsTransition.cs | 0 .../Animation/Transitions/VectorTransition.cs | 0 .../Animation}/Utils/BounceEaseUtils.cs | 0 .../Animation}/Utils/EasingUtils.cs | 0 .../Assets/BiDi.trie | Bin .../Assets/GraphemeBreak.trie | Bin .../Assets/UnicodeData.trie | Bin src/Avalonia.Base/Avalonia.Base.csproj | 3 + .../AvaloniaPropertyExtensions.cs | 0 .../ClassBindingManager.cs | 0 .../CombinedGeometry.cs | 0 .../Controls/ChildNameScope.cs | 2 +- .../Controls/Classes.cs | 0 .../Controls/INameScope.cs | 0 .../Controls/IPseudoClasses.cs | 0 .../Controls/IResourceDictionary.cs | 0 .../Controls/IResourceHost.cs | 0 .../Controls/IResourceNode.cs | 0 .../Controls/IResourceProvider.cs | 0 .../Controls/ISetInheritanceParent.cs | 0 .../Controls/ISetLogicalParent.cs | 0 .../Metadata/PseudoClassesAttribute.cs | 0 .../Metadata/TemplatePartAttribute.cs | 0 .../Controls/NameScope.cs | 2 +- .../Controls/NameScopeEventArgs.cs | 0 .../Controls/NameScopeExtensions.cs | 0 .../Controls/NameScopeLocator.cs | 0 .../Controls/PseudoClassesExtensions.cs | 0 .../Controls/ResourceDictionary.cs | 0 .../Controls/ResourceNodeExtensions.cs | 0 .../Controls/ResourcesChangedEventArgs.cs | 0 .../CornerRadius.cs | 0 .../Diagnostics/StyleDiagnostics.cs | 0 .../Diagnostics/StyledElementExtensions.cs | 0 .../GeometryCollection.cs | 0 .../Media => Avalonia.Base}/GeometryGroup.cs | 0 .../IDataContextProvider.cs | 0 .../INamed.cs | 0 .../IStyledElement.cs | 0 .../Input}/AccessKeyHandler.cs | 0 .../Input}/Cursor.cs | 0 .../Input}/DataFormats.cs | 0 .../Input}/DataObject.cs | 0 .../Input}/DragDrop.cs | 0 .../Input}/DragDropDevice.cs | 0 .../Input}/DragDropEffects.cs | 0 .../Input}/DragEventArgs.cs | 0 .../Input}/FocusManager.cs | 0 .../GestureRecognizerCollection.cs | 0 .../GestureRecognizers/IGestureRecognizer.cs | 0 .../ScrollGestureRecognizer.cs | 0 .../Input}/Gestures.cs | 0 .../Input}/GotFocusEventArgs.cs | 0 .../Input}/IAccessKeyHandler.cs | 0 .../Input}/IClickableControl.cs | 0 .../Input}/ICloseable.cs | 0 .../Input}/ICommandSource.cs | 0 .../Input}/ICustomKeyboardNavigation.cs | 0 .../Input}/IDataObject.cs | 0 .../Input}/IFocusManager.cs | 0 .../Input}/IFocusScope.cs | 0 .../Input}/IInputDevice.cs | 0 .../Input}/IInputElement.cs | 0 .../Input}/IInputManager.cs | 0 .../Input}/IInputRoot.cs | 0 .../Input}/IKeyboardDevice.cs | 0 .../Input}/IKeyboardNavigationHandler.cs | 0 .../Input}/IMainMenu.cs | 0 .../Input}/IMouseDevice.cs | 0 .../Input}/INavigableContainer.cs | 0 .../Input}/IPointer.cs | 0 .../Input}/IPointerDevice.cs | 0 .../Input}/InputElement.cs | 0 .../Input}/InputExtensions.cs | 0 .../Input}/InputManager.cs | 0 .../Input}/InputMethod.cs | 0 .../Input}/Key.cs | 0 .../Input}/KeyBinding.cs | 0 .../Input}/KeyEventArgs.cs | 0 .../Input}/KeyGesture.cs | 0 .../Input}/KeyboardDevice.cs | 0 .../Input}/KeyboardNavigation.cs | 0 .../Input}/KeyboardNavigationHandler.cs | 0 .../Input}/KeyboardNavigationMode.cs | 0 .../Input}/MouseDevice.cs | 0 .../Input}/Navigation/FocusExtensions.cs | 0 .../Input}/Navigation/TabNavigation.cs | 0 .../Input}/NavigationDirection.cs | 0 .../Input}/NavigationMethod.cs | 0 .../Input}/Platform/IClipboard.cs | 0 .../Input}/Platform/IPlatformDragSource.cs | 0 .../Platform/PlatformHotkeyConfiguration.cs | 0 .../Input}/Pointer.cs | 0 .../Input}/PointerDeltaEventArgs.cs | 0 .../Input}/PointerEventArgs.cs | 0 .../Input}/PointerPoint.cs | 0 .../Input}/PointerWheelEventArgs.cs | 0 .../Input}/Raw/IDragDropDevice.cs | 0 .../Input}/Raw/RawDragEvent.cs | 0 .../Input}/Raw/RawDragEventType.cs | 0 .../Input}/Raw/RawInputEventArgs.cs | 0 .../Input}/Raw/RawKeyEventArgs.cs | 0 .../Input}/Raw/RawMouseWheelEventArgs.cs | 0 .../Input}/Raw/RawPointerEventArgs.cs | 0 .../Input}/Raw/RawPointerGestureEventArgs.cs | 0 .../Input}/Raw/RawSizeEventArgs.cs | 0 .../Input}/Raw/RawTextInputEventArgs.cs | 0 .../Input}/Raw/RawTouchEventArgs.cs | 0 .../Input}/ScrollGestureEventArgs.cs | 0 .../Input}/TappedEventArgs.cs | 0 .../TextInput/ITextInputMethodClient.cs | 0 .../Input}/TextInput/ITextInputMethodImpl.cs | 0 .../Input}/TextInput/InputMethodManager.cs | 0 .../Input}/TextInput/TextInputContentType.cs | 0 ...TextInputMethodClientRequestedEventArgs.cs | 0 .../Input}/TextInput/TextInputOptions.cs | 0 .../TextInput/TransformTrackingHelper.cs | 0 .../Input}/TextInputEventArgs.cs | 0 .../Input}/TouchDevice.cs | 0 .../Input}/VectorEventArgs.cs | 0 .../Interactivity}/EventRoute.cs | 0 .../Interactivity}/IInteractive.cs | 0 .../Interactivity}/Interactive.cs | 0 .../Interactivity}/InteractiveExtensions.cs | 0 .../Interactivity}/RoutedEvent.cs | 0 .../Interactivity}/RoutedEventArgs.cs | 0 .../Interactivity}/RoutedEventRegistry.cs | 0 .../Layout}/AttachedLayout.cs | 0 .../EffectiveViewportChangedEventArgs.cs | 0 .../Layout}/ElementManager.cs | 0 .../Layout}/FlowLayoutAlgorithm.cs | 0 .../Layout}/IEmbeddedLayoutRoot.cs | 0 .../Layout}/IFlowLayoutAlgorithmDelegates.cs | 0 .../Layout}/ILayoutManager.cs | 0 .../Layout}/ILayoutRoot.cs | 0 .../Layout}/ILayoutable.cs | 0 .../Layout}/LayoutContext.cs | 0 .../Layout}/LayoutContextAdapter.cs | 0 .../Layout}/LayoutExtensions.cs | 0 .../Layout}/LayoutHelper.cs | 0 .../Layout}/LayoutManager.cs | 0 .../Layout}/LayoutQueue.cs | 0 .../Layout}/Layoutable.cs | 0 .../Layout}/NonVirtualizingLayout.cs | 0 .../Layout}/NonVirtualizingLayoutContext.cs | 0 .../Layout}/NonVirtualizingStackLayout.cs | 0 .../Layout}/Orientation.cs | 0 .../Layout}/OrientationBasedMeasures.cs | 0 .../Layout}/StackLayout.cs | 0 .../Layout}/StackLayoutState.cs | 0 .../Layout}/UniformGridLayout.cs | 0 .../Layout}/UniformGridLayoutState.cs | 0 .../Layout}/Utils/ListUtils.cs | 0 .../Layout}/VirtualLayoutContextAdapter.cs | 0 .../Layout}/VirtualizingLayout.cs | 0 .../Layout}/VirtualizingLayoutContext.cs | 0 .../Layout}/WrapLayout/UvBounds.cs | 0 .../Layout}/WrapLayout/UvMeasure.cs | 0 .../Layout}/WrapLayout/WrapItem.cs | 0 .../Layout}/WrapLayout/WrapLayout.cs | 0 .../Layout}/WrapLayout/WrapLayoutState.cs | 0 .../LogicalTree/ChildIndexChangedEventArgs.cs | 0 .../LogicalTree/ControlLocator.cs | 0 .../LogicalTree/IChildIndexProvider.cs | 0 .../LogicalTree/ILogical.cs | 0 .../LogicalTree/ILogicalRoot.cs | 0 .../LogicalTree/LogicalExtensions.cs | 0 .../LogicalTreeAttachmentEventArgs.cs | 0 .../Matrix.cs | 0 .../Media/AcrylicBackgroundSource.cs | 0 .../Media/AlignmentX.cs | 0 .../Media/AlignmentY.cs | 0 .../Media/ArcSegment.cs | 0 .../Media/BaselineAlignment.cs | 0 .../Media/BezierSegment .cs | 0 .../Media/BoxShadow.cs | 0 .../Media/BoxShadows.cs | 0 .../Media/Brush.cs | 0 .../Media/BrushConverter.cs | 0 .../Media/BrushExtensions.cs | 0 .../Media/BrushMappingMode.cs | 0 .../Media/Brushes.cs | 0 .../Media/CharacterHit.cs | 0 .../Media/Color.cs | 0 .../Media/Colors.cs | 0 .../Media/ConicGradientBrush.cs | 0 .../Media/DashStyle.cs | 0 .../Media/Drawing.cs | 0 .../Media/DrawingContext.cs | 2 +- .../Media/DrawingGroup.cs | 0 .../Media/DrawingImage.cs | 4 +- .../Media/EllipseGeometry.cs | 0 .../Media/ExperimentalAcrylicMaterial.cs | 0 .../Media/FillRule.cs | 0 .../Media/FlowDirection.cs | 0 .../Media/FontFallback.cs | 0 .../Media/FontFamily.cs | 0 .../Media/FontManager.cs | 0 .../Media/FontManagerOptions.cs | 0 .../Media/FontStretch.cs | 0 .../Media/FontStyle.cs | 0 .../Media/FontWeight.cs | 0 .../Media/Fonts/FamilyNameCollection.cs | 0 .../Media/Fonts/FontFamilyKey.cs | 0 .../Media/Fonts/FontFamilyLoader.cs | 0 .../Media/FormattedText.cs | 0 .../Media/Geometry.cs | 0 .../Media/GeometryDrawing.cs | 0 .../Media/GlyphRun.cs | 0 .../Media/GlyphRunDrawing.cs | 0 .../Media/GlyphRunMetrics.cs | 0 .../Media/GlyphTypeface.cs | 0 .../Media/GradientBrush.cs | 0 .../Media/GradientSpreadMethod.cs | 0 .../Media/GradientStop.cs | 0 .../Media/GradientStops.cs | 0 .../Media/HslColor.cs | 0 .../Media/HsvColor.cs | 0 .../Media/IAffectsRender.cs | 0 .../Media/IBrush.cs | 0 .../Media/IConicGradientBrush.cs | 0 .../Media/IDashStyle.cs | 0 .../Media/IExperimentalAcrylicMaterial.cs | 0 .../Media/IGradientBrush.cs | 0 .../Media/IGradientStop.cs | 0 .../Media/IImage.cs | 3 +- .../Media/IImageBrush.cs | 0 .../Media/ILinearGradientBrush.cs | 0 .../Media/IMutableBrush.cs | 0 .../IMutableExperimentalAcrylicMaterial.cs | 0 .../Media/IMutableTransform.cs | 0 .../Media/IPen.cs | 0 .../Media/IRadialGradientBrush.cs | 0 .../Media/ISolidColorBrush.cs | 0 .../Media/ITileBrush.cs | 2 +- .../Media/ITransform.cs | 0 .../Media/IVisualBrush.cs | 0 .../Media/ImageBrush.cs | 0 .../Media/ImageDrawing.cs | 0 .../Media/Imaging/Bitmap.cs | 1 - .../Media/Imaging/BitmapBlendingMode.cs | 2 +- .../Media/Imaging/BitmapInterpolationMode.cs | 2 +- .../Media/Imaging/CroppedBitmap.cs | 2 +- .../Media/Imaging/IBitmap.cs | 0 .../Media/Imaging/RenderTargetBitmap.cs | 0 .../Media/Imaging/WriteableBitmap.cs | 2 - .../Immutable/ImmutableConicGradientBrush.cs | 0 .../Media/Immutable/ImmutableDashStyle.cs | 0 .../Media/Immutable/ImmutableGradientBrush.cs | 0 .../Media/Immutable/ImmutableGradientStop.cs | 0 .../Media/Immutable/ImmutableImageBrush.cs | 1 - .../Immutable/ImmutableLinearGradientBrush.cs | 0 .../Media/Immutable/ImmutablePen.cs | 0 .../Immutable/ImmutableRadialGradientBrush.cs | 0 .../Immutable/ImmutableSolidColorBrush.cs | 0 .../Immutable/ImmutableTextDecoration.cs | 0 .../Media/Immutable/ImmutableTileBrush.cs | 2 +- .../Media/Immutable/ImmutableTransform.cs | 0 .../Media/Immutable/ImmutableVisualBrush.cs | 4 +- .../ImmutableExperimentalAcrylicMaterial.cs | 0 .../Media/KnownColors.cs | 0 .../Media/LineGeometry.cs | 0 .../Media/LineSegment.cs | 0 .../Media/LinearGradientBrush.cs | 0 .../Media/MaterialExtensions.cs | 0 .../Media/MatrixTransform.cs | 0 .../Media/MediaExtensions.cs | 0 .../Media/PathFigure.cs | 0 .../Media/PathGeometry.cs | 0 .../Media/PathGeometryCollections.cs | 0 .../Media/PathMarkupParser.cs | 0 .../Media/PathSegment.cs | 0 .../Media/Pen.cs | 0 .../Media/PenLineCap.cs | 0 .../Media/PenLineJoin.cs | 0 .../Media/PolyLineSegment.cs | 0 .../Media/PolylineGeometry.cs | 0 .../Media/PreciseEllipticArcHelper.cs | 0 .../Media/QuadraticBezierSegment .cs | 0 .../Media/RadialGradientBrush.cs | 0 .../Media/RectangleGeometry.cs | 0 .../Media/RenderOptions.cs | 2 +- .../Media/RotateTransform.cs | 0 .../Media/ScaleTransform.cs | 0 .../Media/SkewTransform.cs | 0 .../Media/SolidColorBrush.cs | 0 .../Media/StreamGeometry.cs | 0 .../Media/StreamGeometryContext.cs | 0 .../Media/Stretch.cs | 0 .../Media/StretchDirection.cs | 0 .../Media/SweepDirection.cs | 0 .../Media/TextAlignment.cs | 0 .../Media/TextCollapsingCreateInfo.cs | 0 .../Media/TextDecoration.cs | 0 .../Media/TextDecorationCollection.cs | 0 .../Media/TextDecorationLocation.cs | 0 .../Media/TextDecorationUnit.cs | 0 .../Media/TextDecorations.cs | 0 .../Media/TextFormatting/DrawableTextRun.cs | 0 .../Media/TextFormatting/FontMetrics.cs | 0 .../TextFormatting/FormattedTextSource.cs | 0 .../GenericTextParagraphProperties.cs | 0 .../GenericTextRunProperties.cs | 0 .../Media/TextFormatting/ITextSource.cs | 0 .../Media/TextFormatting/LogicalDirection.cs | 0 .../TextFormatting/ShapeableTextCharacters.cs | 0 .../Media/TextFormatting/ShapedBuffer.cs | 0 .../TextFormatting/ShapedTextCharacters.cs | 0 .../Media/TextFormatting/SplitResult.cs | 0 .../Media/TextFormatting/TextBounds.cs | 0 .../Media/TextFormatting/TextCharacters.cs | 0 .../TextCollapsingProperties.cs | 0 .../TextFormatting/TextEllipsisHelper.cs | 0 .../Media/TextFormatting/TextEndOfLine.cs | 0 .../TextFormatting/TextEndOfParagraph.cs | 0 .../Media/TextFormatting/TextFormatter.cs | 0 .../Media/TextFormatting/TextFormatterImpl.cs | 0 .../Media/TextFormatting/TextLayout.cs | 0 .../TextLeadingPrefixCharacterEllipsis.cs | 0 .../Media/TextFormatting/TextLine.cs | 0 .../Media/TextFormatting/TextLineBreak.cs | 0 .../Media/TextFormatting/TextLineImpl.cs | 0 .../Media/TextFormatting/TextLineMetrics.cs | 0 .../TextFormatting/TextParagraphProperties.cs | 0 .../Media/TextFormatting/TextRange.cs | 0 .../Media/TextFormatting/TextRun.cs | 0 .../Media/TextFormatting/TextRunProperties.cs | 0 .../Media/TextFormatting/TextShaper.cs | 0 .../Media/TextFormatting/TextShaperOptions.cs | 0 .../TextTrailingCharacterEllipsis.cs | 0 .../TextTrailingWordEllipsis.cs | 0 .../TextFormatting/Unicode/BiDiAlgorithm.cs | 0 .../Media/TextFormatting/Unicode/BiDiClass.cs | 0 .../Media/TextFormatting/Unicode/BiDiData.cs | 0 .../Unicode/BiDiPairedBracketType.cs | 0 .../Unicode/BinaryReaderExtensions.cs | 0 .../Media/TextFormatting/Unicode/Codepoint.cs | 0 .../Unicode/CodepointEnumerator.cs | 0 .../TextFormatting/Unicode/GeneralCategory.cs | 0 .../Media/TextFormatting/Unicode/Grapheme.cs | 0 .../Unicode/GraphemeBreakClass.cs | 0 .../Unicode/GraphemeEnumerator.cs | 0 .../Media/TextFormatting/Unicode/LineBreak.cs | 0 .../TextFormatting/Unicode/LineBreakClass.cs | 0 .../Unicode/LineBreakEnumerator.cs | 0 .../Unicode/LineBreakPairTable.cs | 0 .../Unicode/PropertyValueAliasHelper.cs | 0 .../Media/TextFormatting/Unicode/Script.cs | 0 .../TextFormatting/Unicode/UnicodeData.cs | 0 .../TextFormatting/Unicode/UnicodeTrie.cs | 0 .../Unicode/UnicodeTrieBuilder.Constants.cs | 0 .../Unicode/UnicodeTrieBuilder.cs | 0 .../Media/TextHitTestResult.cs | 0 .../Media/TextLeadingPrefixTrimming.cs | 0 .../Media/TextNoneTrimming.cs | 0 .../Media/TextTrailingTrimming.cs | 0 .../Media/TextTrimming.cs | 0 .../Media/TextWrapping.cs | 0 .../Media/TileBrush.cs | 2 +- .../Media/Transform.cs | 0 .../Media/TransformConverter.cs | 0 .../Media/TransformExtensions.cs | 0 .../Media/TransformGroup.cs | 0 .../Transformation/InterpolationUtilities.cs | 0 .../Transformation/TransformOperation.cs | 0 .../Transformation/TransformOperations.cs | 0 .../Media/Transformation/TransformParser.cs | 0 .../Media/TranslateTransform.cs | 0 .../Media/Typeface.cs | 0 .../Media/UnicodeRange.cs | 0 .../Media/VisualBrush.cs | 0 .../Media => Avalonia.Base}/PixelPoint.cs | 0 .../Media => Avalonia.Base}/PixelRect.cs | 0 .../Media => Avalonia.Base}/PixelSize.cs | 0 .../Media => Avalonia.Base}/PixelVector.cs | 0 .../Platform/AlphaFormat.cs | 0 .../Platform/IBitmapImpl.cs | 0 .../Platform/ICursorFactory.cs | 0 .../Platform/ICursorImpl.cs | 0 .../Platform/IDrawingContextImpl.cs | 2 +- .../IDrawingContextWithAcrylicLikeSupport.cs | 0 .../Platform/IFontManagerImpl.cs | 0 .../Platform/IGeometryContext.cs | 0 .../Platform/IGeometryImpl.cs | 0 .../Platform/IGlyphRunImpl.cs | 0 .../Platform/IGlyphTypefaceImpl.cs | 0 .../Platform/ILockedFramebuffer.cs | 0 .../Platform/IModuleEnvironmentChecker.cs | 0 .../Platform/IPlatformRenderInterface.cs | 2 +- .../Platform/IPlatformSettings.cs | 0 .../Platform/IRenderTarget.cs | 0 .../Platform/IRenderTargetBitmapImpl.cs | 0 .../Platform/IStreamGeometryContextImpl.cs | 0 .../Platform/IStreamGeometryImpl.cs | 0 .../Platform/ITextShaperImpl.cs | 0 .../Platform/ITransformedGeometryImpl.cs | 0 .../Platform/IWriteableBitmapImpl.cs | 0 .../Platform/LockedFramebuffer.cs | 0 .../Platform/PathGeometryContext.cs | 0 .../Platform/PixelFormat.cs | 0 .../Point.cs | 0 .../Points.cs | 0 src/Avalonia.Base/Properties/AssemblyInfo.cs | 29 +- .../Rect.cs | 0 .../RelativePoint.cs | 0 .../RelativeRect.cs | 0 .../RenderTargetCorruptedException.cs | 0 .../Rendering/DefaultRenderTimer.cs | 0 .../Rendering/DeferredRenderer.cs | 0 .../Rendering/DirtyRects.cs | 0 .../Rendering/DirtyVisuals.cs | 0 .../Rendering/DisplayDirtyRect.cs | 0 .../Rendering/DisplayDirtyRects.cs | 0 .../Rendering/ICustomSimpleHitTest.cs | 0 .../Rendering/IDeferredRendererLock.cs | 0 .../Rendering/IRenderLoop.cs | 0 .../Rendering/IRenderLoopTask.cs | 0 .../Rendering/IRenderRoot.cs | 0 .../Rendering/IRenderTimer.cs | 0 .../Rendering/IRenderer.cs | 0 .../Rendering/IRendererFactory.cs | 0 .../Rendering/IVisualBrushInitialize.cs | 0 .../Rendering/IVisualBrushRenderer.cs | 0 .../Rendering/ImmediateRenderer.cs | 0 .../Rendering/ManagedDeferredRendererLock.cs | 0 .../Rendering/RenderLayer.cs | 0 .../Rendering/RenderLayers.cs | 0 .../Rendering/RenderLoop.cs | 0 .../Rendering/RendererBase.cs | 0 .../SceneGraph/BitmapBlendModeNode.cs | 2 +- .../SceneGraph/BrushDrawOperation.cs | 0 .../Rendering/SceneGraph/ClipNode.cs | 0 .../SceneGraph/CustomDrawOperation.cs | 0 .../SceneGraph/DeferredDrawingContextImpl.cs | 2 +- .../Rendering/SceneGraph/DrawOperation.cs | 0 .../Rendering/SceneGraph/EllipseNode.cs | 0 .../SceneGraph/ExperimentalAcrylicNode.cs | 0 .../SceneGraph/GeometryBoundsHelper.cs | 0 .../Rendering/SceneGraph/GeometryClipNode.cs | 0 .../Rendering/SceneGraph/GeometryNode.cs | 0 .../Rendering/SceneGraph/GlyphRunNode.cs | 0 .../Rendering/SceneGraph/IDrawOperation.cs | 0 .../Rendering/SceneGraph/ISceneBuilder.cs | 0 .../Rendering/SceneGraph/IVisualNode.cs | 0 .../Rendering/SceneGraph/ImageNode.cs | 2 +- .../Rendering/SceneGraph/LineBoundsHelper.cs | 0 .../Rendering/SceneGraph/LineNode.cs | 0 .../Rendering/SceneGraph/OpacityMaskNode.cs | 0 .../Rendering/SceneGraph/OpacityNode.cs | 0 .../Rendering/SceneGraph/RectangleNode.cs | 0 .../Rendering/SceneGraph/Scene.cs | 0 .../Rendering/SceneGraph/SceneBuilder.cs | 0 .../Rendering/SceneGraph/SceneLayer.cs | 0 .../Rendering/SceneGraph/SceneLayers.cs | 0 .../Rendering/SceneGraph/VisualNode.cs | 0 .../Rendering/SceneInvalidatedEventArgs.cs | 0 .../Rendering/SleepLoopRenderTimer.cs | 0 .../Rendering/UiThreadRenderTimer.cs | 0 .../Utilities/TileBrushCalculator.cs | 0 .../Rendering/ZIndexComparer.cs | 0 .../RoundedRect.cs | 0 src/Avalonia.Base/Settings.StyleCop | 1 - .../Size.cs | 0 .../StyledElement.cs | 0 .../StyledElementExtensions.cs | 0 .../Styling/Activators/AndActivator.cs | 0 .../Styling/Activators/AndActivatorBuilder.cs | 0 .../Styling/Activators/IStyleActivator.cs | 0 .../Styling/Activators/IStyleActivatorSink.cs | 0 .../Styling/Activators/NotActivator.cs | 0 .../Styling/Activators/NthChildActivator.cs | 0 .../Styling/Activators/OrActivator.cs | 0 .../Styling/Activators/OrActivatorBuilder.cs | 0 .../Activators/PropertyEqualsActivator.cs | 0 .../Styling/Activators/StyleActivatorBase.cs | 0 .../Styling/Activators/StyleClassActivator.cs | 0 .../Styling/ChildSelector.cs | 0 .../Styling/DescendentSelector.cs | 0 .../Styling/IGlobalStyles.cs | 0 .../Styling/ISetter.cs | 0 .../Styling/ISetterInstance.cs | 0 .../Styling/ISetterValue.cs | 0 .../Styling/IStyle.cs | 0 .../Styling/IStyleHost.cs | 0 .../Styling/IStyleInstance.cs | 0 .../Styling/IStyleable.cs | 0 .../Styling/IStyler.cs | 0 .../Styling/ITemplate.cs | 0 .../Styling/ITemplatedControl.cs | 0 .../Styling/NotSelector.cs | 0 .../Styling/NthChildSelector.cs | 0 .../Styling/NthLastChildSelector.cs | 0 .../Styling/OrSelector.cs | 0 .../Styling/PropertyEqualsSelector.cs | 0 .../Styling/PropertySetterBindingInstance.cs | 0 .../Styling/PropertySetterInstance.cs | 0 .../Styling/PropertySetterLazyInstance.cs | 0 .../Styling/Selector.cs | 0 .../Styling/SelectorMatch.cs | 0 .../Styling/Selectors.cs | 0 .../Styling/Setter.cs | 0 .../Styling/Style.cs | 0 .../Styling/StyleInstance.cs | 0 .../Styling/Styler.cs | 0 .../Styling/Styles.cs | 0 .../Styling/TemplateSelector.cs | 0 .../Styling/TypeNameAndClassSelector.cs | 0 .../Thickness.cs | 0 .../Utilities/ArrayBuilder.cs | 0 .../Utilities/ArraySlice.cs | 0 .../Utilities/BinarySearchExtension.cs | 0 .../Utilities/FrugalList.cs | 0 .../Utilities/MappedArraySlice.cs | 0 .../Utilities/ReadOnlySlice.cs | 0 .../Utilities/Span.cs | 0 .../Utilities/ValueSpan.cs | 0 .../Vector.cs | 0 .../Visual.cs | 0 .../VisualExtensions.cs | 0 .../VisualTree/IHostedVisualTreeRoot.cs | 0 .../VisualTree/IVisual.cs | 0 .../VisualTree/IVisualTreeHost.cs | 0 .../VisualTree/IVisualWithRoundRectClip.cs | 0 .../VisualTree/TransformedBounds.cs | 0 .../VisualTree/VisualExtensions.cs | 0 .../VisualTree/VisualLocator.cs | 0 .../VisualTreeAttachmentEventArgs.cs | 0 .../Avalonia.Build.Tasks.csproj | 20 +- .../Avalonia.Controls.DataGrid.csproj | 6 - .../Avalonia.Controls.csproj | 6 - .../Avalonia.DesignerSupport.csproj | 6 - .../Avalonia.Diagnostics.csproj | 6 - .../HeadlessPlatformRenderInterface.cs | 2 +- src/Avalonia.Input/ApiCompatBaseline.txt | 30 -- src/Avalonia.Input/Avalonia.Input.csproj | 19 -- src/Avalonia.Input/Properties/AssemblyInfo.cs | 10 - .../ApiCompatBaseline.txt | 1 - .../Avalonia.Interactivity.csproj | 15 - src/Avalonia.Layout/ApiCompatBaseline.txt | 1 - src/Avalonia.Layout/Avalonia.Layout.csproj | 14 - .../Properties/AssemblyInfo.cs | 7 - src/Avalonia.OpenGL/Avalonia.OpenGL.csproj | 1 - .../Avalonia.Remote.Protocol.csproj | 2 +- src/Avalonia.Styling/ApiCompatBaseline.txt | 4 - src/Avalonia.Styling/Avalonia.Styling.csproj | 14 - .../Properties/AssemblyInfo.cs | 9 - .../Avalonia.Themes.Default.csproj | 8 +- .../Avalonia.Themes.Fluent.csproj | 8 +- src/Avalonia.Visuals/ApiCompatBaseline.txt | 188 ----------- src/Avalonia.Visuals/Avalonia.Visuals.csproj | 23 -- .../Properties/AssemblyInfo.cs | 15 - src/Avalonia.X11/X11IconLoader.cs | 5 +- .../Avalonia.Markup.Xaml.csproj | 6 - .../Avalonia.Markup/Avalonia.Markup.csproj | 2 - src/Skia/Avalonia.Skia/DrawingContextImpl.cs | 2 +- src/Skia/Avalonia.Skia/ImmutableBitmap.cs | 2 +- .../Avalonia.Skia/PlatformRenderInterface.cs | 2 +- src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs | 2 +- src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs | 2 +- .../Avalonia.Direct2D1/Direct2D1Platform.cs | 2 +- .../Media/DrawingContextImpl.cs | 4 +- .../Media/ImageBrushImpl.cs | 2 +- .../Media/Imaging/WicBitmapImpl.cs | 12 +- .../Media/Imaging/WriteableWicBitmapImpl.cs | 2 +- .../Avalonia.Designer.HostApp.csproj | 6 - .../Avalonia.Animation.UnitTests.csproj | 28 -- .../Properties/AssemblyInfo.cs | 7 - .../Animation}/AnimatableTests.cs | 6 +- .../Animation}/AnimationIterationTests.cs | 10 +- .../Animation}/BrushTransitionTests.cs | 7 +- .../Animation}/KeySplineTests.cs | 7 +- .../Animation}/TestClock.cs | 6 +- .../Animation}/TransitionsTests.cs | 4 +- .../Avalonia.Base.UnitTests.csproj | 5 + .../AvaloniaPropertyRegistryTests.cs | 5 +- .../CornerRadiusTests.cs | 5 +- .../DataAnnotationsValidationPluginTests.cs | 2 +- .../Data/DefaultValueConverterTests.cs | 2 +- .../Input}/GesturesTests.cs | 3 +- .../Input}/InputElement_Enabled.cs | 2 +- .../Input}/InputElement_Focus.cs | 3 +- .../Input}/KeyGestureTests.cs | 3 +- .../Input}/KeyboardDeviceTests.cs | 3 +- .../Input}/KeyboardNavigationTests_Custom.cs | 3 +- .../Input}/KeyboardNavigationTests_Tab.cs | 3 +- .../Input}/MouseDeviceTests.cs | 10 +- .../Input}/PointerTests.cs | 3 +- .../Input}/TouchDeviceTests.cs | 0 .../Interactivity}/InteractiveTests.cs | 3 +- .../RoutedEventRegistryTests.cs | 3 +- .../Layout}/ArrangeTests.cs | 3 +- .../Layout}/FullLayoutTests.cs | 4 +- .../Layout}/LayoutHelperTests.cs | 3 +- .../Layout}/LayoutManagerTests.cs | 3 +- .../Layout}/LayoutQueueTests.cs | 3 +- .../Layout}/LayoutTestControl.cs | 3 +- .../Layout}/LayoutTestRoot.cs | 3 +- .../Layout}/LayoutableTests.cs | 3 +- ...ayoutableTests_EffectiveViewportChanged.cs | 3 +- .../Layout}/MeasureTests.cs | 2 +- .../NonVirtualizingStackLayoutTests.cs | 3 +- .../Media/BoxShadowTests.cs | 2 +- .../Media/BrushTests.cs | 2 +- .../Media/ColorTests.cs | 2 +- .../Media/FontFamilyTests.cs | 2 +- .../Media/FontManagerTests.cs | 2 +- .../Media/Fonts/FamilyNameCollectionTests.cs | 2 +- .../Media/Fonts/FontFamilyKeyTests.cs | 2 +- .../Media/Fonts/FontFamilyLoaderTests.cs | 7 +- .../Media/GeometryGroupTests.cs | 0 .../Media/GeometryTests.cs | 2 +- .../Media/GlyphRunTests.cs | 2 +- .../Media/ImageBrushTests.cs | 7 +- .../Media/LinearGradientBrushTests.cs | 5 +- .../Media/MatrixTests.cs | 2 +- .../Media/PathMarkupParserTests.cs | 11 +- .../Media/PathSegmentTests.cs | 2 +- .../Media/PenTests.cs | 2 +- .../Media/PixelRectTests.cs | 7 +- .../Media/RectTests.cs | 7 +- .../Media/RectangleGeometryTests.cs | 5 +- .../Media/SolidColorBrushTests.cs | 5 +- .../Media/TextDecorationTests.cs | 2 +- .../TextFormatting/BiDiAlgorithmTests.cs | 0 .../BiDiClassTestDataGenerator.cs | 1 + .../Media/TextFormatting/BiDiClassTests.cs | 0 .../TextFormatting/BiDiTestDataGenerator.cs | 1 + .../Media/TextFormatting/BreakPairTable.txt | 0 .../GraphemeBreakClassTrieGenerator.cs | 2 +- .../GraphemeBreakClassTrieGeneratorTests.cs | 2 +- .../LineBreakEnumuratorTests.cs | 2 +- .../Media/TextFormatting/TestDataGenerator.cs | 2 +- .../TextFormatting/UnicodeDataGenerator.cs | 2 +- .../UnicodeDataGeneratorTests.cs | 2 +- .../TextFormatting/UnicodeEnumsGenerator.cs | 2 +- .../Media/TransformOperationsTests.cs | 2 +- .../Media/TypefaceTests.cs | 2 +- .../Media/UnicodeRangeSegmentTests.cs | 0 .../Media/UnicodeRangeTests.cs | 0 .../RectTests.cs | 2 +- .../RelativePointTests.cs | 3 +- .../RelativeRectComparer.cs | 2 +- .../RelativeRectTests.cs | 3 +- .../RenderTests_Culling.cs | 8 +- .../Rendering/CustomHitTestBorder.cs | 3 +- .../Rendering/DeferredRendererTests.cs | 7 +- .../DeferredRendererTests_HitTesting.cs | 3 +- .../Rendering/ImmediateRendererTests.cs | 2 +- .../ImmediateRendererTests_HitTesting.cs | 2 +- .../Rendering/RenderLoopTests.cs | 4 +- .../DeferredDrawingContextImplTests.cs | 6 +- .../SceneGraph/DrawOperationTests.cs | 4 +- .../Rendering/SceneGraph/EllipseNodeTests.cs | 0 .../Rendering/SceneGraph/LineNodeTests.cs | 0 .../Rendering/SceneGraph/SceneBuilderTests.cs | 19 +- .../SceneGraph/SceneBuilderTests_Layers.cs | 10 +- .../Rendering/SceneGraph/SceneLayersTests.cs | 5 +- .../Rendering/SceneGraph/SceneTests.cs | 5 +- .../Rendering/SceneGraph/VisualNodeTests.cs | 5 +- .../Utilities/TileBrushCalculatorTests.cs | 5 +- .../SizeTests.cs | 2 +- .../Styling}/ResourceDictionaryTests.cs | 2 +- .../Styling}/SelectorTests_Child.cs | 4 +- .../Styling}/SelectorTests_Class.cs | 8 +- .../Styling}/SelectorTests_Descendent.cs | 4 +- .../Styling}/SelectorTests_Multiple.cs | 4 +- .../Styling}/SelectorTests_Name.cs | 3 +- .../Styling}/SelectorTests_Not.cs | 4 +- .../Styling}/SelectorTests_NthChild.cs | 3 +- .../Styling}/SelectorTests_NthLastChild.cs | 3 +- .../Styling}/SelectorTests_OfType.cs | 3 +- .../Styling}/SelectorTests_Or.cs | 3 +- .../Styling}/SelectorTests_PropertyEquals.cs | 4 +- .../Styling}/SelectorTests_Template.cs | 11 +- .../Styling}/SetterTests.cs | 3 +- .../Styling}/StyleActivatorExtensions.cs | 2 +- .../Styling}/StyleTests.cs | 3 +- .../Styling}/StyledElementTests.cs | 16 +- .../Styling}/StyledElementTests_Resources.cs | 4 +- .../Styling}/StylesTests.cs | 4 +- .../Styling}/TestObservable.cs | 2 +- .../Styling}/TestObserver.cs | 2 +- .../Styling}/TestSelectors.cs | 2 +- .../Styling}/TestSubject.cs | 2 +- .../TestVisual.cs | 3 +- .../ThicknessTests.cs | 5 +- .../Utilities/ReadOnlySpanTests.cs | 2 +- .../VectorTests.cs | 5 +- .../VisualExtensionsTests.cs | 2 +- .../VisualTests.cs | 6 +- .../VisualTree/MockRenderInterface.cs | 4 +- .../VisualTree/TransformedBoundsTests.cs | 14 +- .../VisualExtensions_GetVisualsAt.cs | 2 +- .../Avalonia.Benchmarks.csproj | 6 - .../NullDrawingContextImpl.cs | 2 +- .../NullRenderingPlatform.cs | 2 +- .../Avalonia.Controls.UnitTests.csproj | 6 - .../Avalonia.DesignerSupport.TestApp.csproj | 6 - .../Avalonia.Direct2D1.RenderTests.csproj | 6 - .../Avalonia.Direct2D1.UnitTests.csproj | 6 - .../Avalonia.Input.UnitTests.csproj | 27 -- .../Properties/AssemblyInfo.cs | 7 - .../Avalonia.Interactivity.UnitTests.csproj | 27 -- .../Properties/AssemblyInfo.cs | 7 - .../Avalonia.Layout.UnitTests.csproj | 29 -- .../Properties/AssemblyInfo.cs | 7 - .../Avalonia.LeakTests.csproj | 6 - .../Avalonia.Markup.UnitTests.csproj | 6 - .../Avalonia.Markup.Xaml.UnitTests.csproj | 6 - .../Avalonia.Skia.RenderTests.csproj | 6 - .../Avalonia.Skia.UnitTests.csproj | 6 - .../Avalonia.Styling.UnitTests.csproj | 30 -- .../Properties/AssemblyInfo.cs | 7 - .../Avalonia.UnitTests.csproj | 6 - .../MockPlatformRenderInterface.cs | 2 +- .../Avalonia.Visuals.UnitTests.csproj | 40 --- .../Properties/AssemblyInfo.cs | 7 - 837 files changed, 437 insertions(+), 1366 deletions(-) delete mode 100644 src/Avalonia.Animation/ApiCompatBaseline.txt delete mode 100644 src/Avalonia.Animation/Avalonia.Animation.csproj delete mode 100644 src/Avalonia.Animation/Properties/AssemblyInfo.cs rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Animatable.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Animation.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/AnimationInstance`1.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/AnimatorDrivenTransition.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/AnimatorKeyFrame.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/AnimatorTransitionObservable.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Animators/Animator`1.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Animators/BaseBrushAnimator.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Animators/BoolAnimator.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Animators/BoxShadowAnimator.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Animators/BoxShadowsAnimator.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Animators/ByteAnimator.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Animators/ColorAnimator.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Animators/CornerRadiusAnimator.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Animators/DecimalAnimator.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Animators/DoubleAnimator.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Animators/FloatAnimator.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Animators/GradientBrushAnimator.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Animators/Int16Animator.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Animators/Int32Animator.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Animators/Int64Animator.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Animators/PointAnimator.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Animators/RectAnimator.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Animators/RelativePointAnimator.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Animators/SizeAnimator.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Animators/SolidColorBrushAnimator.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Animators/ThicknessAnimator.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Animators/TransformAnimator.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Animators/TransformOperationsAnimator.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Animators/UInt16Animator.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Animators/UInt32Animator.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Animators/UInt64Animator.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Animators/VectorAnimator.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Clock.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/ClockBase.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/CompositePageTransition.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/CrossFade.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Cue.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/DisposeAnimationInstanceSubject.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/BackEaseIn.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/BackEaseInOut.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/BackEaseOut.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/BounceEaseIn.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/BounceEaseInOut.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/BounceEaseOut.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/CircularEaseIn.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/CircularEaseInOut.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/CircularEaseOut.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/CubicEaseIn.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/CubicEaseInOut.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/CubicEaseOut.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/Easing.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/EasingTypeConverter.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/ElasticEaseIn.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/ElasticEaseInOut.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/ElasticEaseOut.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/ExponentialEaseIn.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/ExponentialEaseInOut.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/ExponentialEaseOut.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/IEasing.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/LinearEasing.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/QuadraticEaseIn.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/QuadraticEaseInOut.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/QuadraticEaseOut.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/QuarticEaseIn.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/QuarticEaseInOut.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/QuarticEaseOut.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/QuinticEaseIn.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/QuinticEaseInOut.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/QuinticEaseOut.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/SineEaseIn.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/SineEaseInOut.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/SineEaseOut.cs (100%) rename src/{Avalonia.Animation/Easing => Avalonia.Base/Animation/Easings}/SplineEasing.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/FillMode.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/IAnimation.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/IAnimationSetter.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/IAnimator.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/IClock.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/IGlobalClock.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/IPageTransition.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/ITransition.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/IterationCount.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/IterationCountTypeConverter.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/KeyFrame.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/KeyFrames.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/KeySpline.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/KeySplineTypeConverter.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/PageSlide.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/PlayState.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/PlaybackDirection.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/RenderLoopClock.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Transition.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/TransitionInstance.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/TransitionObservableBase.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Transitions.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Transitions/BoxShadowsTransition.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Transitions/BrushTransition.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Transitions/ColorTransition.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Transitions/CornerRadiusTransition.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Transitions/DoubleTransition.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Transitions/FloatTransition.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Transitions/IntegerTransition.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Transitions/PointTransition.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Transitions/RelativePointTransition.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Transitions/SizeTransition.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Transitions/ThicknessTransition.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Transitions/TransformOperationsTransition.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Animation/Transitions/VectorTransition.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Utils/BounceEaseUtils.cs (100%) rename src/{Avalonia.Animation => Avalonia.Base/Animation}/Utils/EasingUtils.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Assets/BiDi.trie (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Assets/GraphemeBreak.trie (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Assets/UnicodeData.trie (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/AvaloniaPropertyExtensions.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/ClassBindingManager.cs (100%) rename src/{Avalonia.Visuals/Media => Avalonia.Base}/CombinedGeometry.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Controls/ChildNameScope.cs (99%) rename src/{Avalonia.Styling => Avalonia.Base}/Controls/Classes.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Controls/INameScope.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Controls/IPseudoClasses.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Controls/IResourceDictionary.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Controls/IResourceHost.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Controls/IResourceNode.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Controls/IResourceProvider.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Controls/ISetInheritanceParent.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Controls/ISetLogicalParent.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Controls/Metadata/PseudoClassesAttribute.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Controls/Metadata/TemplatePartAttribute.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Controls/NameScope.cs (99%) rename src/{Avalonia.Styling => Avalonia.Base}/Controls/NameScopeEventArgs.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Controls/NameScopeExtensions.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Controls/NameScopeLocator.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Controls/PseudoClassesExtensions.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Controls/ResourceDictionary.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Controls/ResourceNodeExtensions.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Controls/ResourcesChangedEventArgs.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/CornerRadius.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Diagnostics/StyleDiagnostics.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Diagnostics/StyledElementExtensions.cs (100%) rename src/{Avalonia.Visuals/Media => Avalonia.Base}/GeometryCollection.cs (100%) rename src/{Avalonia.Visuals/Media => Avalonia.Base}/GeometryGroup.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/IDataContextProvider.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/INamed.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/IStyledElement.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/AccessKeyHandler.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/Cursor.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/DataFormats.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/DataObject.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/DragDrop.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/DragDropDevice.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/DragDropEffects.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/DragEventArgs.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/FocusManager.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/GestureRecognizers/GestureRecognizerCollection.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/GestureRecognizers/IGestureRecognizer.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/GestureRecognizers/ScrollGestureRecognizer.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/Gestures.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/GotFocusEventArgs.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/IAccessKeyHandler.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/IClickableControl.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/ICloseable.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/ICommandSource.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/ICustomKeyboardNavigation.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/IDataObject.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/IFocusManager.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/IFocusScope.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/IInputDevice.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/IInputElement.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/IInputManager.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/IInputRoot.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/IKeyboardDevice.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/IKeyboardNavigationHandler.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/IMainMenu.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/IMouseDevice.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/INavigableContainer.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/IPointer.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/IPointerDevice.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/InputElement.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/InputExtensions.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/InputManager.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/InputMethod.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/Key.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/KeyBinding.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/KeyEventArgs.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/KeyGesture.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/KeyboardDevice.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/KeyboardNavigation.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/KeyboardNavigationHandler.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/KeyboardNavigationMode.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/MouseDevice.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/Navigation/FocusExtensions.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/Navigation/TabNavigation.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/NavigationDirection.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/NavigationMethod.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/Platform/IClipboard.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/Platform/IPlatformDragSource.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/Platform/PlatformHotkeyConfiguration.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/Pointer.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/PointerDeltaEventArgs.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/PointerEventArgs.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/PointerPoint.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/PointerWheelEventArgs.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/Raw/IDragDropDevice.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/Raw/RawDragEvent.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/Raw/RawDragEventType.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/Raw/RawInputEventArgs.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/Raw/RawKeyEventArgs.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/Raw/RawMouseWheelEventArgs.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/Raw/RawPointerEventArgs.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/Raw/RawPointerGestureEventArgs.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/Raw/RawSizeEventArgs.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/Raw/RawTextInputEventArgs.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/Raw/RawTouchEventArgs.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/ScrollGestureEventArgs.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/TappedEventArgs.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/TextInput/ITextInputMethodClient.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/TextInput/ITextInputMethodImpl.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/TextInput/InputMethodManager.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/TextInput/TextInputContentType.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/TextInput/TextInputMethodClientRequestedEventArgs.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/TextInput/TextInputOptions.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/TextInput/TransformTrackingHelper.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/TextInputEventArgs.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/TouchDevice.cs (100%) rename src/{Avalonia.Input => Avalonia.Base/Input}/VectorEventArgs.cs (100%) rename src/{Avalonia.Interactivity => Avalonia.Base/Interactivity}/EventRoute.cs (100%) rename src/{Avalonia.Interactivity => Avalonia.Base/Interactivity}/IInteractive.cs (100%) rename src/{Avalonia.Interactivity => Avalonia.Base/Interactivity}/Interactive.cs (100%) rename src/{Avalonia.Interactivity => Avalonia.Base/Interactivity}/InteractiveExtensions.cs (100%) rename src/{Avalonia.Interactivity => Avalonia.Base/Interactivity}/RoutedEvent.cs (100%) rename src/{Avalonia.Interactivity => Avalonia.Base/Interactivity}/RoutedEventArgs.cs (100%) rename src/{Avalonia.Interactivity => Avalonia.Base/Interactivity}/RoutedEventRegistry.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/AttachedLayout.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/EffectiveViewportChangedEventArgs.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/ElementManager.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/FlowLayoutAlgorithm.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/IEmbeddedLayoutRoot.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/IFlowLayoutAlgorithmDelegates.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/ILayoutManager.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/ILayoutRoot.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/ILayoutable.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/LayoutContext.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/LayoutContextAdapter.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/LayoutExtensions.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/LayoutHelper.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/LayoutManager.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/LayoutQueue.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/Layoutable.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/NonVirtualizingLayout.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/NonVirtualizingLayoutContext.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/NonVirtualizingStackLayout.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/Orientation.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/OrientationBasedMeasures.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/StackLayout.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/StackLayoutState.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/UniformGridLayout.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/UniformGridLayoutState.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/Utils/ListUtils.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/VirtualLayoutContextAdapter.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/VirtualizingLayout.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/VirtualizingLayoutContext.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/WrapLayout/UvBounds.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/WrapLayout/UvMeasure.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/WrapLayout/WrapItem.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/WrapLayout/WrapLayout.cs (100%) rename src/{Avalonia.Layout => Avalonia.Base/Layout}/WrapLayout/WrapLayoutState.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/LogicalTree/ChildIndexChangedEventArgs.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/LogicalTree/ControlLocator.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/LogicalTree/IChildIndexProvider.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/LogicalTree/ILogical.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/LogicalTree/ILogicalRoot.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/LogicalTree/LogicalExtensions.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/LogicalTree/LogicalTreeAttachmentEventArgs.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Matrix.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/AcrylicBackgroundSource.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/AlignmentX.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/AlignmentY.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/ArcSegment.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/BaselineAlignment.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/BezierSegment .cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/BoxShadow.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/BoxShadows.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Brush.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/BrushConverter.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/BrushExtensions.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/BrushMappingMode.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Brushes.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/CharacterHit.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Color.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Colors.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/ConicGradientBrush.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/DashStyle.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Drawing.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/DrawingContext.cs (99%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/DrawingGroup.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/DrawingImage.cs (96%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/EllipseGeometry.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/ExperimentalAcrylicMaterial.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/FillRule.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/FlowDirection.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/FontFallback.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/FontFamily.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/FontManager.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/FontManagerOptions.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/FontStretch.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/FontStyle.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/FontWeight.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Fonts/FamilyNameCollection.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Fonts/FontFamilyKey.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Fonts/FontFamilyLoader.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/FormattedText.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Geometry.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/GeometryDrawing.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/GlyphRun.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/GlyphRunDrawing.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/GlyphRunMetrics.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/GlyphTypeface.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/GradientBrush.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/GradientSpreadMethod.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/GradientStop.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/GradientStops.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/HslColor.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/HsvColor.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/IAffectsRender.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/IBrush.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/IConicGradientBrush.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/IDashStyle.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/IExperimentalAcrylicMaterial.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/IGradientBrush.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/IGradientStop.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/IImage.cs (93%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/IImageBrush.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/ILinearGradientBrush.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/IMutableBrush.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/IMutableExperimentalAcrylicMaterial.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/IMutableTransform.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/IPen.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/IRadialGradientBrush.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/ISolidColorBrush.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/ITileBrush.cs (97%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/ITransform.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/IVisualBrush.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/ImageBrush.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/ImageDrawing.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Imaging/Bitmap.cs (99%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Imaging/BitmapBlendingMode.cs (97%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Imaging/BitmapInterpolationMode.cs (93%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Imaging/CroppedBitmap.cs (98%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Imaging/IBitmap.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Imaging/RenderTargetBitmap.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Imaging/WriteableBitmap.cs (98%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Immutable/ImmutableConicGradientBrush.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Immutable/ImmutableDashStyle.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Immutable/ImmutableGradientBrush.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Immutable/ImmutableGradientStop.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Immutable/ImmutableImageBrush.cs (98%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Immutable/ImmutableLinearGradientBrush.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Immutable/ImmutablePen.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Immutable/ImmutableRadialGradientBrush.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Immutable/ImmutableSolidColorBrush.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Immutable/ImmutableTextDecoration.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Immutable/ImmutableTileBrush.cs (98%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Immutable/ImmutableTransform.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Immutable/ImmutableVisualBrush.cs (94%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/ImmutableExperimentalAcrylicMaterial.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/KnownColors.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/LineGeometry.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/LineSegment.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/LinearGradientBrush.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/MaterialExtensions.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/MatrixTransform.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/MediaExtensions.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/PathFigure.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/PathGeometry.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/PathGeometryCollections.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/PathMarkupParser.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/PathSegment.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Pen.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/PenLineCap.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/PenLineJoin.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/PolyLineSegment.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/PolylineGeometry.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/PreciseEllipticArcHelper.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/QuadraticBezierSegment .cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/RadialGradientBrush.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/RectangleGeometry.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/RenderOptions.cs (97%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/RotateTransform.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/ScaleTransform.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/SkewTransform.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/SolidColorBrush.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/StreamGeometry.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/StreamGeometryContext.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Stretch.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/StretchDirection.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/SweepDirection.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextAlignment.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextCollapsingCreateInfo.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextDecoration.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextDecorationCollection.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextDecorationLocation.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextDecorationUnit.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextDecorations.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/DrawableTextRun.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/FontMetrics.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/FormattedTextSource.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/GenericTextParagraphProperties.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/GenericTextRunProperties.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/ITextSource.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/LogicalDirection.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/ShapeableTextCharacters.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/ShapedBuffer.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/ShapedTextCharacters.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/SplitResult.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/TextBounds.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/TextCharacters.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/TextCollapsingProperties.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/TextEllipsisHelper.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/TextEndOfLine.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/TextEndOfParagraph.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/TextFormatter.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/TextFormatterImpl.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/TextLayout.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/TextLine.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/TextLineBreak.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/TextLineImpl.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/TextLineMetrics.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/TextParagraphProperties.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/TextRange.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/TextRun.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/TextRunProperties.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/TextShaper.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/TextShaperOptions.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/TextTrailingCharacterEllipsis.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/TextTrailingWordEllipsis.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/Unicode/BiDiAlgorithm.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/Unicode/BiDiClass.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/Unicode/BiDiData.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/Unicode/BiDiPairedBracketType.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/Unicode/BinaryReaderExtensions.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/Unicode/Codepoint.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/Unicode/CodepointEnumerator.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/Unicode/GeneralCategory.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/Unicode/Grapheme.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/Unicode/GraphemeBreakClass.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/Unicode/GraphemeEnumerator.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/Unicode/LineBreak.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/Unicode/LineBreakClass.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/Unicode/LineBreakEnumerator.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/Unicode/LineBreakPairTable.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/Unicode/PropertyValueAliasHelper.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/Unicode/Script.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/Unicode/UnicodeData.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/Unicode/UnicodeTrie.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/Unicode/UnicodeTrieBuilder.Constants.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextFormatting/Unicode/UnicodeTrieBuilder.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextHitTestResult.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextLeadingPrefixTrimming.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextNoneTrimming.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextTrailingTrimming.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextTrimming.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TextWrapping.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TileBrush.cs (99%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Transform.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TransformConverter.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TransformExtensions.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TransformGroup.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Transformation/InterpolationUtilities.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Transformation/TransformOperation.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Transformation/TransformOperations.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Transformation/TransformParser.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/TranslateTransform.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/Typeface.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/UnicodeRange.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Media/VisualBrush.cs (100%) rename src/{Avalonia.Visuals/Media => Avalonia.Base}/PixelPoint.cs (100%) rename src/{Avalonia.Visuals/Media => Avalonia.Base}/PixelRect.cs (100%) rename src/{Avalonia.Visuals/Media => Avalonia.Base}/PixelSize.cs (100%) rename src/{Avalonia.Visuals/Media => Avalonia.Base}/PixelVector.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/AlphaFormat.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/IBitmapImpl.cs (100%) rename src/{Avalonia.Input => Avalonia.Base}/Platform/ICursorFactory.cs (100%) rename src/{Avalonia.Input => Avalonia.Base}/Platform/ICursorImpl.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/IDrawingContextImpl.cs (99%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/IDrawingContextWithAcrylicLikeSupport.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/IFontManagerImpl.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/IGeometryContext.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/IGeometryImpl.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/IGlyphRunImpl.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/IGlyphTypefaceImpl.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/ILockedFramebuffer.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/IModuleEnvironmentChecker.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/IPlatformRenderInterface.cs (99%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/IPlatformSettings.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/IRenderTarget.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/IRenderTargetBitmapImpl.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/IStreamGeometryContextImpl.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/IStreamGeometryImpl.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/ITextShaperImpl.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/ITransformedGeometryImpl.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/IWriteableBitmapImpl.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/LockedFramebuffer.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/PathGeometryContext.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Platform/PixelFormat.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Point.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Points.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rect.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/RelativePoint.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/RelativeRect.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/RenderTargetCorruptedException.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/DefaultRenderTimer.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/DeferredRenderer.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/DirtyRects.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/DirtyVisuals.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/DisplayDirtyRect.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/DisplayDirtyRects.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/ICustomSimpleHitTest.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/IDeferredRendererLock.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/IRenderLoop.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/IRenderLoopTask.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/IRenderRoot.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/IRenderTimer.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/IRenderer.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/IRendererFactory.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/IVisualBrushInitialize.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/IVisualBrushRenderer.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/ImmediateRenderer.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/ManagedDeferredRendererLock.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/RenderLayer.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/RenderLayers.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/RenderLoop.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/RendererBase.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/BitmapBlendModeNode.cs (98%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/BrushDrawOperation.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/ClipNode.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/CustomDrawOperation.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/DeferredDrawingContextImpl.cs (99%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/DrawOperation.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/EllipseNode.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/ExperimentalAcrylicNode.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/GeometryBoundsHelper.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/GeometryClipNode.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/GeometryNode.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/GlyphRunNode.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/IDrawOperation.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/ISceneBuilder.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/IVisualNode.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/ImageNode.cs (99%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/LineBoundsHelper.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/LineNode.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/OpacityMaskNode.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/OpacityNode.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/RectangleNode.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/Scene.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/SceneBuilder.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/SceneLayer.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/SceneLayers.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneGraph/VisualNode.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SceneInvalidatedEventArgs.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/SleepLoopRenderTimer.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/UiThreadRenderTimer.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/Utilities/TileBrushCalculator.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Rendering/ZIndexComparer.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/RoundedRect.cs (100%) delete mode 100644 src/Avalonia.Base/Settings.StyleCop rename src/{Avalonia.Visuals => Avalonia.Base}/Size.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/StyledElement.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/StyledElementExtensions.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/Activators/AndActivator.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/Activators/AndActivatorBuilder.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/Activators/IStyleActivator.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/Activators/IStyleActivatorSink.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/Activators/NotActivator.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/Activators/NthChildActivator.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/Activators/OrActivator.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/Activators/OrActivatorBuilder.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/Activators/PropertyEqualsActivator.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/Activators/StyleActivatorBase.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/Activators/StyleClassActivator.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/ChildSelector.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/DescendentSelector.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/IGlobalStyles.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/ISetter.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/ISetterInstance.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/ISetterValue.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/IStyle.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/IStyleHost.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/IStyleInstance.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/IStyleable.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/IStyler.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/ITemplate.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/ITemplatedControl.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/NotSelector.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/NthChildSelector.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/NthLastChildSelector.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/OrSelector.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/PropertyEqualsSelector.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/PropertySetterBindingInstance.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/PropertySetterInstance.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/PropertySetterLazyInstance.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/Selector.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/SelectorMatch.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/Selectors.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/Setter.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/Style.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/StyleInstance.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/Styler.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/Styles.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/TemplateSelector.cs (100%) rename src/{Avalonia.Styling => Avalonia.Base}/Styling/TypeNameAndClassSelector.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Thickness.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Utilities/ArrayBuilder.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Utilities/ArraySlice.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Utilities/BinarySearchExtension.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Utilities/FrugalList.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Utilities/MappedArraySlice.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Utilities/ReadOnlySlice.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Utilities/Span.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Utilities/ValueSpan.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Vector.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/Visual.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/VisualExtensions.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/VisualTree/IHostedVisualTreeRoot.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/VisualTree/IVisual.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/VisualTree/IVisualTreeHost.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/VisualTree/IVisualWithRoundRectClip.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/VisualTree/TransformedBounds.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/VisualTree/VisualExtensions.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/VisualTree/VisualLocator.cs (100%) rename src/{Avalonia.Visuals => Avalonia.Base}/VisualTreeAttachmentEventArgs.cs (100%) delete mode 100644 src/Avalonia.Input/ApiCompatBaseline.txt delete mode 100644 src/Avalonia.Input/Avalonia.Input.csproj delete mode 100644 src/Avalonia.Input/Properties/AssemblyInfo.cs delete mode 100644 src/Avalonia.Interactivity/ApiCompatBaseline.txt delete mode 100644 src/Avalonia.Interactivity/Avalonia.Interactivity.csproj delete mode 100644 src/Avalonia.Layout/ApiCompatBaseline.txt delete mode 100644 src/Avalonia.Layout/Avalonia.Layout.csproj delete mode 100644 src/Avalonia.Layout/Properties/AssemblyInfo.cs delete mode 100644 src/Avalonia.Styling/ApiCompatBaseline.txt delete mode 100644 src/Avalonia.Styling/Avalonia.Styling.csproj delete mode 100644 src/Avalonia.Styling/Properties/AssemblyInfo.cs delete mode 100644 src/Avalonia.Visuals/ApiCompatBaseline.txt delete mode 100644 src/Avalonia.Visuals/Avalonia.Visuals.csproj delete mode 100644 src/Avalonia.Visuals/Properties/AssemblyInfo.cs delete mode 100644 tests/Avalonia.Animation.UnitTests/Avalonia.Animation.UnitTests.csproj delete mode 100644 tests/Avalonia.Animation.UnitTests/Properties/AssemblyInfo.cs rename tests/{Avalonia.Animation.UnitTests => Avalonia.Base.UnitTests/Animation}/AnimatableTests.cs (99%) rename tests/{Avalonia.Animation.UnitTests => Avalonia.Base.UnitTests/Animation}/AnimationIterationTests.cs (97%) rename tests/{Avalonia.Animation.UnitTests => Avalonia.Base.UnitTests/Animation}/BrushTransitionTests.cs (93%) rename tests/{Avalonia.Animation.UnitTests => Avalonia.Base.UnitTests/Animation}/KeySplineTests.cs (97%) rename tests/{Avalonia.Animation.UnitTests => Avalonia.Base.UnitTests/Animation}/TestClock.cs (90%) rename tests/{Avalonia.Animation.UnitTests => Avalonia.Base.UnitTests/Animation}/TransitionsTests.cs (98%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/CornerRadiusTests.cs (93%) rename tests/{Avalonia.Input.UnitTests => Avalonia.Base.UnitTests/Input}/GesturesTests.cs (98%) rename tests/{Avalonia.Input.UnitTests => Avalonia.Base.UnitTests/Input}/InputElement_Enabled.cs (98%) rename tests/{Avalonia.Input.UnitTests => Avalonia.Base.UnitTests/Input}/InputElement_Focus.cs (99%) rename tests/{Avalonia.Input.UnitTests => Avalonia.Base.UnitTests/Input}/KeyGestureTests.cs (97%) rename tests/{Avalonia.Input.UnitTests => Avalonia.Base.UnitTests/Input}/KeyboardDeviceTests.cs (98%) rename tests/{Avalonia.Input.UnitTests => Avalonia.Base.UnitTests/Input}/KeyboardNavigationTests_Custom.cs (99%) rename tests/{Avalonia.Input.UnitTests => Avalonia.Base.UnitTests/Input}/KeyboardNavigationTests_Tab.cs (99%) rename tests/{Avalonia.Input.UnitTests => Avalonia.Base.UnitTests/Input}/MouseDeviceTests.cs (98%) rename tests/{Avalonia.Input.UnitTests => Avalonia.Base.UnitTests/Input}/PointerTests.cs (95%) rename tests/{Avalonia.Input.UnitTests => Avalonia.Base.UnitTests/Input}/TouchDeviceTests.cs (100%) rename tests/{Avalonia.Interactivity.UnitTests => Avalonia.Base.UnitTests/Interactivity}/InteractiveTests.cs (99%) rename tests/{Avalonia.Interactivity.UnitTests => Avalonia.Base.UnitTests/Interactivity}/RoutedEventRegistryTests.cs (95%) rename tests/{Avalonia.Layout.UnitTests => Avalonia.Base.UnitTests/Layout}/ArrangeTests.cs (98%) rename tests/{Avalonia.Layout.UnitTests => Avalonia.Base.UnitTests/Layout}/FullLayoutTests.cs (98%) rename tests/{Avalonia.Layout.UnitTests => Avalonia.Base.UnitTests/Layout}/LayoutHelperTests.cs (92%) rename tests/{Avalonia.Layout.UnitTests => Avalonia.Base.UnitTests/Layout}/LayoutManagerTests.cs (99%) rename tests/{Avalonia.Layout.UnitTests => Avalonia.Base.UnitTests/Layout}/LayoutQueueTests.cs (98%) rename tests/{Avalonia.Layout.UnitTests => Avalonia.Base.UnitTests/Layout}/LayoutTestControl.cs (93%) rename tests/{Avalonia.Layout.UnitTests => Avalonia.Base.UnitTests/Layout}/LayoutTestRoot.cs (94%) rename tests/{Avalonia.Layout.UnitTests => Avalonia.Base.UnitTests/Layout}/LayoutableTests.cs (99%) rename tests/{Avalonia.Layout.UnitTests => Avalonia.Base.UnitTests/Layout}/LayoutableTests_EffectiveViewportChanged.cs (99%) rename tests/{Avalonia.Layout.UnitTests => Avalonia.Base.UnitTests/Layout}/MeasureTests.cs (99%) rename tests/{Avalonia.Layout.UnitTests => Avalonia.Base.UnitTests/Layout}/NonVirtualizingStackLayoutTests.cs (99%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/BoxShadowTests.cs (96%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/BrushTests.cs (98%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/ColorTests.cs (99%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/FontFamilyTests.cs (99%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/FontManagerTests.cs (98%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/Fonts/FamilyNameCollectionTests.cs (91%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/Fonts/FontFamilyKeyTests.cs (95%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/Fonts/FontFamilyLoaderTests.cs (94%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/GeometryGroupTests.cs (100%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/GeometryTests.cs (98%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/GlyphRunTests.cs (99%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/ImageBrushTests.cs (78%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/LinearGradientBrushTests.cs (97%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/MatrixTests.cs (98%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/PathMarkupParserTests.cs (98%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/PathSegmentTests.cs (94%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/PenTests.cs (98%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/PixelRectTests.cs (90%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/RectTests.cs (74%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/RectangleGeometryTests.cs (93%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/SolidColorBrushTests.cs (82%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/TextDecorationTests.cs (95%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/TextFormatting/BiDiAlgorithmTests.cs (100%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/TextFormatting/BiDiClassTestDataGenerator.cs (98%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/TextFormatting/BiDiClassTests.cs (100%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/TextFormatting/BiDiTestDataGenerator.cs (98%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/TextFormatting/BreakPairTable.txt (100%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/TextFormatting/GraphemeBreakClassTrieGenerator.cs (98%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/TextFormatting/GraphemeBreakClassTrieGeneratorTests.cs (96%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/TextFormatting/LineBreakEnumuratorTests.cs (99%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/TextFormatting/TestDataGenerator.cs (97%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/TextFormatting/UnicodeDataGenerator.cs (99%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/TextFormatting/UnicodeDataGeneratorTests.cs (97%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/TextFormatting/UnicodeEnumsGenerator.cs (99%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/TransformOperationsTests.cs (99%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/TypefaceTests.cs (93%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/UnicodeRangeSegmentTests.cs (100%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Media/UnicodeRangeTests.cs (100%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/RectTests.cs (97%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/RelativePointTests.cs (89%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/RelativeRectComparer.cs (94%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/RelativeRectTests.cs (93%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/RenderTests_Culling.cs (99%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Rendering/CustomHitTestBorder.cs (84%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Rendering/DeferredRendererTests.cs (99%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Rendering/DeferredRendererTests_HitTesting.cs (99%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Rendering/ImmediateRendererTests.cs (99%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Rendering/ImmediateRendererTests_HitTesting.cs (99%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Rendering/RenderLoopTests.cs (97%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Rendering/SceneGraph/DeferredDrawingContextImplTests.cs (98%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Rendering/SceneGraph/DrawOperationTests.cs (96%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Rendering/SceneGraph/EllipseNodeTests.cs (100%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Rendering/SceneGraph/LineNodeTests.cs (100%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Rendering/SceneGraph/SceneBuilderTests.cs (99%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Rendering/SceneGraph/SceneBuilderTests_Layers.cs (98%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Rendering/SceneGraph/SceneLayersTests.cs (89%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Rendering/SceneGraph/SceneTests.cs (90%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Rendering/SceneGraph/VisualNodeTests.cs (97%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Rendering/Utilities/TileBrushCalculatorTests.cs (98%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/SizeTests.cs (92%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/ResourceDictionaryTests.cs (99%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/SelectorTests_Child.cs (97%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/SelectorTests_Class.cs (98%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/SelectorTests_Descendent.cs (98%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/SelectorTests_Multiple.cs (99%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/SelectorTests_Name.cs (96%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/SelectorTests_Not.cs (97%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/SelectorTests_NthChild.cs (99%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/SelectorTests_NthLastChild.cs (99%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/SelectorTests_OfType.cs (95%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/SelectorTests_Or.cs (98%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/SelectorTests_PropertyEquals.cs (98%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/SelectorTests_Template.cs (98%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/SetterTests.cs (98%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/StyleActivatorExtensions.cs (97%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/StyleTests.cs (99%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/StyledElementTests.cs (99%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/StyledElementTests_Resources.cs (98%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/StylesTests.cs (98%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/TestObservable.cs (90%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/TestObserver.cs (96%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/TestSelectors.cs (90%) rename tests/{Avalonia.Styling.UnitTests => Avalonia.Base.UnitTests/Styling}/TestSubject.cs (96%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/TestVisual.cs (94%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/ThicknessTests.cs (93%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/Utilities/ReadOnlySpanTests.cs (94%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/VectorTests.cs (98%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/VisualExtensionsTests.cs (99%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/VisualTests.cs (98%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/VisualTree/MockRenderInterface.cs (98%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/VisualTree/TransformedBoundsTests.cs (94%) rename tests/{Avalonia.Visuals.UnitTests => Avalonia.Base.UnitTests}/VisualTree/VisualExtensions_GetVisualsAt.cs (98%) delete mode 100644 tests/Avalonia.Input.UnitTests/Avalonia.Input.UnitTests.csproj delete mode 100644 tests/Avalonia.Input.UnitTests/Properties/AssemblyInfo.cs delete mode 100644 tests/Avalonia.Interactivity.UnitTests/Avalonia.Interactivity.UnitTests.csproj delete mode 100644 tests/Avalonia.Interactivity.UnitTests/Properties/AssemblyInfo.cs delete mode 100644 tests/Avalonia.Layout.UnitTests/Avalonia.Layout.UnitTests.csproj delete mode 100644 tests/Avalonia.Layout.UnitTests/Properties/AssemblyInfo.cs delete mode 100644 tests/Avalonia.Styling.UnitTests/Avalonia.Styling.UnitTests.csproj delete mode 100644 tests/Avalonia.Styling.UnitTests/Properties/AssemblyInfo.cs delete mode 100644 tests/Avalonia.Visuals.UnitTests/Avalonia.Visuals.UnitTests.csproj delete mode 100644 tests/Avalonia.Visuals.UnitTests/Properties/AssemblyInfo.cs diff --git a/Avalonia.sln b/Avalonia.sln index 5043f474c2..a216229a5b 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -5,48 +5,26 @@ VisualStudioVersion = 17.0.31903.59 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Base", "src\Avalonia.Base\Avalonia.Base.csproj", "{B09B78D8-9B26-48B0-9149-D64A2F120F3F}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Visuals", "src\Avalonia.Visuals\Avalonia.Visuals.csproj", "{EB582467-6ABB-43A1-B052-E981BA910E3A}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Layout", "src\Avalonia.Layout\Avalonia.Layout.csproj", "{42472427-4774-4C81-8AFF-9F27B8E31721}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Windows", "Windows", "{B39A8919-9F95-48FE-AD7B-76E08B509888}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Win32", "src\Windows\Avalonia.Win32\Avalonia.Win32.csproj", "{811A76CF-1CF6-440F-963B-BBE31BD72A82}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Direct2D1", "src\Windows\Avalonia.Direct2D1\Avalonia.Direct2D1.csproj", "{3E908F67-5543-4879-A1DC-08EACE79B3CD}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Input", "src\Avalonia.Input\Avalonia.Input.csproj", "{62024B2D-53EB-4638-B26B-85EEAA54866E}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Interactivity", "src\Avalonia.Interactivity\Avalonia.Interactivity.csproj", "{6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls", "src\Avalonia.Controls\Avalonia.Controls.csproj", "{D2221C82-4A25-4583-9B43-D791E3F6820C}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Styling", "src\Avalonia.Styling\Avalonia.Styling.csproj", "{F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Themes.Default", "src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj", "{3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Diagnostics", "src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj", "{7062AE20-5DCC-4442-9645-8195BDECE63E}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Animation", "src\Avalonia.Animation\Avalonia.Animation.csproj", "{D211E587-D8BC-45B9-95A4-F297C8FA5200}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Styling.UnitTests", "tests\Avalonia.Styling.UnitTests\Avalonia.Styling.UnitTests.csproj", "{47ECDF59-DEF8-4C53-87B1-2098A3429059}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls.UnitTests", "tests\Avalonia.Controls.UnitTests\Avalonia.Controls.UnitTests.csproj", "{5CCB5571-7C30-4E7D-967D-0E2158EBD91F}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Visuals.UnitTests", "tests\Avalonia.Visuals.UnitTests\Avalonia.Visuals.UnitTests.csproj", "{76716382-3159-460E-BDA6-C5715CF606D7}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Base.UnitTests", "tests\Avalonia.Base.UnitTests\Avalonia.Base.UnitTests.csproj", "{2905FF23-53FB-45E6-AA49-6AF47A172056}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Layout.UnitTests", "tests\Avalonia.Layout.UnitTests\Avalonia.Layout.UnitTests.csproj", "{DB070A10-BF39-4752-8456-86E9D5928478}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Interactivity.UnitTests", "tests\Avalonia.Interactivity.UnitTests\Avalonia.Interactivity.UnitTests.csproj", "{08478EF5-44E8-42E9-92D6-15E00EC038D8}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Direct2D1.RenderTests", "tests\Avalonia.Direct2D1.RenderTests\Avalonia.Direct2D1.RenderTests.csproj", "{DABFD304-D6A4-4752-8123-C2CCF7AC7831}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Input.UnitTests", "tests\Avalonia.Input.UnitTests\Avalonia.Input.UnitTests.csproj", "{AC18926A-E784-40FE-B09D-BB0FE2B599F0}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Direct2D1.UnitTests", "tests\Avalonia.Direct2D1.UnitTests\Avalonia.Direct2D1.UnitTests.csproj", "{EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Markup.Xaml.UnitTests", "tests\Avalonia.Markup.Xaml.UnitTests\Avalonia.Markup.Xaml.UnitTests.csproj", "{99135EAB-653D-47E4-A378-C96E1278CA44}" @@ -185,8 +163,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Build.Tasks", "src EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "_build", "nukebuild\_build.csproj", "{3F00BC43-5095-477F-93D8-E65B08179A00}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Animation.UnitTests", "tests\Avalonia.Animation.UnitTests\Avalonia.Animation.UnitTests.csproj", "{AF227847-E65C-4BE9-BCE9-B551357788E0}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.X11", "src\Avalonia.X11\Avalonia.X11.csproj", "{41B02319-965D-4945-8005-C1A3D1224165}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlatformSanityChecks", "samples\PlatformSanityChecks\PlatformSanityChecks.csproj", "{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B}" @@ -279,54 +255,6 @@ Global {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Release|iPhone.Build.0 = Release|Any CPU {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {B09B78D8-9B26-48B0-9149-D64A2F120F3F}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.AppStore|Any CPU.Build.0 = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.AppStore|iPhone.Build.0 = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Debug|iPhone.Build.0 = Debug|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Release|Any CPU.Build.0 = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Release|iPhone.ActiveCfg = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Release|iPhone.Build.0 = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {EB582467-6ABB-43A1-B052-E981BA910E3A}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.AppStore|Any CPU.Build.0 = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.AppStore|iPhone.Build.0 = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Debug|Any CPU.Build.0 = Debug|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Debug|iPhone.Build.0 = Debug|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Release|Any CPU.ActiveCfg = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Release|Any CPU.Build.0 = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Release|iPhone.ActiveCfg = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Release|iPhone.Build.0 = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {42472427-4774-4C81-8AFF-9F27B8E31721}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {811A76CF-1CF6-440F-963B-BBE31BD72A82}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU {811A76CF-1CF6-440F-963B-BBE31BD72A82}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU {811A76CF-1CF6-440F-963B-BBE31BD72A82}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU @@ -375,54 +303,6 @@ Global {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Release|iPhone.Build.0 = Release|Any CPU {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {3E908F67-5543-4879-A1DC-08EACE79B3CD}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.AppStore|Any CPU.Build.0 = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.AppStore|iPhone.Build.0 = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Debug|iPhone.Build.0 = Debug|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Release|Any CPU.Build.0 = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Release|iPhone.ActiveCfg = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Release|iPhone.Build.0 = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {62024B2D-53EB-4638-B26B-85EEAA54866E}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.AppStore|Any CPU.Build.0 = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.AppStore|iPhone.Build.0 = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Debug|iPhone.Build.0 = Debug|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Release|Any CPU.Build.0 = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Release|iPhone.ActiveCfg = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Release|iPhone.Build.0 = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {D2221C82-4A25-4583-9B43-D791E3F6820C}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU {D2221C82-4A25-4583-9B43-D791E3F6820C}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU {D2221C82-4A25-4583-9B43-D791E3F6820C}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU @@ -447,30 +327,6 @@ Global {D2221C82-4A25-4583-9B43-D791E3F6820C}.Release|iPhone.Build.0 = Release|Any CPU {D2221C82-4A25-4583-9B43-D791E3F6820C}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {D2221C82-4A25-4583-9B43-D791E3F6820C}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.AppStore|Any CPU.Build.0 = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.AppStore|iPhone.Build.0 = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Debug|iPhone.Build.0 = Debug|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Release|Any CPU.Build.0 = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Release|iPhone.ActiveCfg = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Release|iPhone.Build.0 = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU @@ -519,54 +375,6 @@ Global {7062AE20-5DCC-4442-9645-8195BDECE63E}.Release|iPhone.Build.0 = Release|Any CPU {7062AE20-5DCC-4442-9645-8195BDECE63E}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {7062AE20-5DCC-4442-9645-8195BDECE63E}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.AppStore|Any CPU.Build.0 = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.AppStore|iPhone.Build.0 = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Debug|iPhone.Build.0 = Debug|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Release|Any CPU.Build.0 = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Release|iPhone.ActiveCfg = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Release|iPhone.Build.0 = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.AppStore|Any CPU.Build.0 = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.AppStore|iPhone.Build.0 = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Debug|Any CPU.Build.0 = Debug|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Debug|iPhone.Build.0 = Debug|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Release|Any CPU.ActiveCfg = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Release|Any CPU.Build.0 = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Release|iPhone.ActiveCfg = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Release|iPhone.Build.0 = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {47ECDF59-DEF8-4C53-87B1-2098A3429059}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU @@ -591,30 +399,6 @@ Global {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Release|iPhone.Build.0 = Release|Any CPU {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {5CCB5571-7C30-4E7D-967D-0E2158EBD91F}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.AppStore|Any CPU.Build.0 = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.AppStore|iPhone.Build.0 = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Debug|iPhone.Build.0 = Debug|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Release|Any CPU.Build.0 = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Release|iPhone.ActiveCfg = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Release|iPhone.Build.0 = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {76716382-3159-460E-BDA6-C5715CF606D7}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {2905FF23-53FB-45E6-AA49-6AF47A172056}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU {2905FF23-53FB-45E6-AA49-6AF47A172056}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU {2905FF23-53FB-45E6-AA49-6AF47A172056}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU @@ -639,54 +423,6 @@ Global {2905FF23-53FB-45E6-AA49-6AF47A172056}.Release|iPhone.Build.0 = Release|Any CPU {2905FF23-53FB-45E6-AA49-6AF47A172056}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {2905FF23-53FB-45E6-AA49-6AF47A172056}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.AppStore|Any CPU.Build.0 = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.AppStore|iPhone.Build.0 = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Debug|iPhone.Build.0 = Debug|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Release|Any CPU.Build.0 = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Release|iPhone.ActiveCfg = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Release|iPhone.Build.0 = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {DB070A10-BF39-4752-8456-86E9D5928478}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.AppStore|Any CPU.Build.0 = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.AppStore|iPhone.Build.0 = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Debug|iPhone.Build.0 = Debug|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Release|Any CPU.Build.0 = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Release|iPhone.ActiveCfg = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Release|iPhone.Build.0 = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {08478EF5-44E8-42E9-92D6-15E00EC038D8}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU @@ -711,30 +447,6 @@ Global {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Release|iPhone.Build.0 = Release|Any CPU {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {DABFD304-D6A4-4752-8123-C2CCF7AC7831}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.AppStore|Any CPU.Build.0 = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.AppStore|iPhone.Build.0 = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Debug|iPhone.Build.0 = Debug|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Release|Any CPU.Build.0 = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Release|iPhone.ActiveCfg = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Release|iPhone.Build.0 = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {AC18926A-E784-40FE-B09D-BB0FE2B599F0}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU @@ -1641,30 +1353,6 @@ Global {3F00BC43-5095-477F-93D8-E65B08179A00}.Release|iPhone.Build.0 = Release|Any CPU {3F00BC43-5095-477F-93D8-E65B08179A00}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {3F00BC43-5095-477F-93D8-E65B08179A00}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.AppStore|iPhone.Build.0 = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Debug|iPhone.Build.0 = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Release|Any CPU.Build.0 = Release|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Release|iPhone.ActiveCfg = Release|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Release|iPhone.Build.0 = Release|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {AF227847-E65C-4BE9-BCE9-B551357788E0}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {41B02319-965D-4945-8005-C1A3D1224165}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU {41B02319-965D-4945-8005-C1A3D1224165}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU {41B02319-965D-4945-8005-C1A3D1224165}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU @@ -2272,14 +1960,9 @@ Global GlobalSection(NestedProjects) = preSolution {811A76CF-1CF6-440F-963B-BBE31BD72A82} = {B39A8919-9F95-48FE-AD7B-76E08B509888} {3E908F67-5543-4879-A1DC-08EACE79B3CD} = {B39A8919-9F95-48FE-AD7B-76E08B509888} - {47ECDF59-DEF8-4C53-87B1-2098A3429059} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {5CCB5571-7C30-4E7D-967D-0E2158EBD91F} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} - {76716382-3159-460E-BDA6-C5715CF606D7} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {2905FF23-53FB-45E6-AA49-6AF47A172056} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} - {DB070A10-BF39-4752-8456-86E9D5928478} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} - {08478EF5-44E8-42E9-92D6-15E00EC038D8} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {DABFD304-D6A4-4752-8123-C2CCF7AC7831} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} - {AC18926A-E784-40FE-B09D-BB0FE2B599F0} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {99135EAB-653D-47E4-A378-C96E1278CA44} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {3E53A01A-B331-47F3-B828-4A5717E77A24} = {8B6A8209-894F-4BA1-B880-965FD453982C} @@ -2313,7 +1996,6 @@ Global {E1240B49-7B4B-4371-A00E-068778C5CF0B} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {D49233F8-F29C-47DD-9975-C4C9E4502720} = {E870DCD7-F46A-498D-83FC-D0FD13E0A11C} {3C471044-3640-45E3-B1B2-16D2FF8399EE} = {E870DCD7-F46A-498D-83FC-D0FD13E0A11C} - {AF227847-E65C-4BE9-BCE9-B551357788E0} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {41B02319-965D-4945-8005-C1A3D1224165} = {86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B} {D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B} = {9B9E3891-2366-4253-A952-D08BCEB71098} {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} diff --git a/Directory.Build.props b/Directory.Build.props index 835decc672..97781b7517 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -5,5 +5,6 @@ false false + False diff --git a/build/CoreLibraries.props b/build/CoreLibraries.props index 6bf69603c0..314d38190a 100644 --- a/build/CoreLibraries.props +++ b/build/CoreLibraries.props @@ -1,14 +1,8 @@ - - - - - - diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs index 72d90abbf3..9fcb9d6b7f 100644 --- a/nukebuild/Build.cs +++ b/nukebuild/Build.cs @@ -214,17 +214,11 @@ partial class Build : NukeBuild .DependsOn(Compile) .Executes(() => { - RunCoreTest("Avalonia.Animation.UnitTests"); RunCoreTest("Avalonia.Base.UnitTests"); RunCoreTest("Avalonia.Controls.UnitTests"); RunCoreTest("Avalonia.Controls.DataGrid.UnitTests"); - RunCoreTest("Avalonia.Input.UnitTests"); - RunCoreTest("Avalonia.Interactivity.UnitTests"); - RunCoreTest("Avalonia.Layout.UnitTests"); RunCoreTest("Avalonia.Markup.UnitTests"); RunCoreTest("Avalonia.Markup.Xaml.UnitTests"); - RunCoreTest("Avalonia.Styling.UnitTests"); - RunCoreTest("Avalonia.Visuals.UnitTests"); RunCoreTest("Avalonia.Skia.UnitTests"); RunCoreTest("Avalonia.ReactiveUI.UnitTests"); }); diff --git a/samples/ControlCatalog.Android/ControlCatalog.Android.csproj b/samples/ControlCatalog.Android/ControlCatalog.Android.csproj index 9777bb46c3..8d2ead054c 100644 --- a/samples/ControlCatalog.Android/ControlCatalog.Android.csproj +++ b/samples/ControlCatalog.Android/ControlCatalog.Android.csproj @@ -41,7 +41,47 @@ + + {7b92af71-6287-4693-9dcb-bd5b6e927e23} + Avalonia.Android + + + {b09b78d8-9b26-48b0-9149-d64a2f120f3f} + Avalonia.Base + + + {d2221c82-4a25-4583-9b43-d791e3f6820c} + Avalonia.Controls + + + {7062ae20-5dcc-4442-9645-8195bdece63e} + Avalonia.Diagnostics + + + {3e10a5fa-e8da-48b1-ad44-6a5b6cb7750f} + Avalonia.Themes.Default + + + {3e53a01a-b331-47f3-b828-4a5717e77a24} + Avalonia.Markup.Xaml + + + {6417e941-21bc-467b-a771-0de389353ce6} + Avalonia.Markup + + + {7d2d3083-71dd-4cc9-8907-39a0d86fb322} + Avalonia.Skia + + + {d0a739b9-3c68-4ba6-a328-41606954b6bd} + ControlCatalog + + + + + diff --git a/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj b/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj index 12d1d5645e..d4f10b3580 100644 --- a/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj +++ b/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj @@ -13,4 +13,53 @@ + + + + + + + + + {4488ad85-1495-4809-9aa4-ddfe0a48527e} + Avalonia.iOS + + + {3E53A01A-B331-47F3-B828-4A5717E77A24} + Avalonia.Markup.Xaml + + + {6417E941-21BC-467B-A771-0DE389353CE6} + Avalonia.Markup + + + {B09B78D8-9B26-48B0-9149-D64A2F120F3F} + Avalonia.Base + + + {D2221C82-4A25-4583-9B43-D791E3F6820C} + Avalonia.Controls + + + {7062AE20-5DCC-4442-9645-8195BDECE63E} + Avalonia.Diagnostics + + + {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F} + Avalonia.Themes.Default + + + {7d2d3083-71dd-4cc9-8907-39a0d86fb322} + Avalonia.Skia + + + {d0a739b9-3c68-4ba6-a328-41606954b6bd} + ControlCatalog + + + + + + + diff --git a/samples/RenderDemo/Pages/PathMeasurementPage.cs b/samples/RenderDemo/Pages/PathMeasurementPage.cs index 212377deae..cc5125609c 100644 --- a/samples/RenderDemo/Pages/PathMeasurementPage.cs +++ b/samples/RenderDemo/Pages/PathMeasurementPage.cs @@ -1,15 +1,9 @@ -using System; -using System.Diagnostics; -using System.Drawing.Drawing2D; -using System.Security.Cryptography; using Avalonia; using Avalonia.Controls; using Avalonia.LogicalTree; using Avalonia.Media; using Avalonia.Media.Imaging; using Avalonia.Media.Immutable; -using Avalonia.Threading; -using Avalonia.Visuals.Media.Imaging; namespace RenderDemo.Pages { diff --git a/samples/RenderDemo/Pages/RenderTargetBitmapPage.cs b/samples/RenderDemo/Pages/RenderTargetBitmapPage.cs index f263786ab7..f365b59c20 100644 --- a/samples/RenderDemo/Pages/RenderTargetBitmapPage.cs +++ b/samples/RenderDemo/Pages/RenderTargetBitmapPage.cs @@ -5,7 +5,6 @@ using Avalonia.LogicalTree; using Avalonia.Media; using Avalonia.Media.Imaging; using Avalonia.Threading; -using Avalonia.Visuals.Media.Imaging; namespace RenderDemo.Pages { diff --git a/samples/RenderDemo/RenderDemo.csproj b/samples/RenderDemo/RenderDemo.csproj index 3d5aee49e9..18a4ee5662 100644 --- a/samples/RenderDemo/RenderDemo.csproj +++ b/samples/RenderDemo/RenderDemo.csproj @@ -4,7 +4,7 @@ net6.0 - + TextFormatterPage.axaml Code diff --git a/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj b/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj index db0bb01410..ee41b9f354 100644 --- a/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj +++ b/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj @@ -40,4 +40,40 @@ + + + {7b92af71-6287-4693-9dcb-bd5b6e927e23} + Avalonia.Android + + + {3e53a01a-b331-47f3-b828-4a5717e77a24} + Avalonia.Markup.Xaml + + + {b09b78d8-9b26-48b0-9149-d64a2f120f3f} + Avalonia.Base + + + {d2221c82-4a25-4583-9b43-d791e3f6820c} + Avalonia.Controls + + + {7062ae20-5dcc-4442-9645-8195bdece63e} + Avalonia.Diagnostics + + + {3e10a5fa-e8da-48b1-ad44-6a5b6cb7750f} + Avalonia.Themes.Default + + + {7d2d3083-71dd-4cc9-8907-39a0d86fb322} + Avalonia.Skia + + + + + + + + diff --git a/src/Avalonia.Animation/ApiCompatBaseline.txt b/src/Avalonia.Animation/ApiCompatBaseline.txt deleted file mode 100644 index 973698f872..0000000000 --- a/src/Avalonia.Animation/ApiCompatBaseline.txt +++ /dev/null @@ -1,5 +0,0 @@ -Compat issues with assembly Avalonia.Animation: -InterfacesShouldHaveSameMembers : Interface member 'public System.Threading.Tasks.Task Avalonia.Animation.IAnimation.RunAsync(Avalonia.Animation.Animatable, Avalonia.Animation.IClock)' is present in the contract but not in the implementation. -MembersMustExist : Member 'public System.Threading.Tasks.Task Avalonia.Animation.IAnimation.RunAsync(Avalonia.Animation.Animatable, Avalonia.Animation.IClock)' does not exist in the implementation but it does exist in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public System.Threading.Tasks.Task Avalonia.Animation.IAnimation.RunAsync(Avalonia.Animation.Animatable, Avalonia.Animation.IClock, System.Threading.CancellationToken)' is present in the implementation but not in the contract. -Total Issues: 3 diff --git a/src/Avalonia.Animation/Avalonia.Animation.csproj b/src/Avalonia.Animation/Avalonia.Animation.csproj deleted file mode 100644 index 80726f2884..0000000000 --- a/src/Avalonia.Animation/Avalonia.Animation.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - net6.0;netstandard2.0 - - - - - - - - - - - - diff --git a/src/Avalonia.Animation/Properties/AssemblyInfo.cs b/src/Avalonia.Animation/Properties/AssemblyInfo.cs deleted file mode 100644 index 6b539b075b..0000000000 --- a/src/Avalonia.Animation/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Avalonia.Metadata; -using System.Runtime.CompilerServices; - -[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Animation")] -[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Animation.Easings")] -[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Animation.Animators")] - -[assembly: InternalsVisibleTo("Avalonia.LeakTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.Animation.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] diff --git a/src/Avalonia.Animation/Animatable.cs b/src/Avalonia.Base/Animation/Animatable.cs similarity index 100% rename from src/Avalonia.Animation/Animatable.cs rename to src/Avalonia.Base/Animation/Animatable.cs diff --git a/src/Avalonia.Animation/Animation.cs b/src/Avalonia.Base/Animation/Animation.cs similarity index 100% rename from src/Avalonia.Animation/Animation.cs rename to src/Avalonia.Base/Animation/Animation.cs diff --git a/src/Avalonia.Animation/AnimationInstance`1.cs b/src/Avalonia.Base/Animation/AnimationInstance`1.cs similarity index 100% rename from src/Avalonia.Animation/AnimationInstance`1.cs rename to src/Avalonia.Base/Animation/AnimationInstance`1.cs diff --git a/src/Avalonia.Animation/AnimatorDrivenTransition.cs b/src/Avalonia.Base/Animation/AnimatorDrivenTransition.cs similarity index 100% rename from src/Avalonia.Animation/AnimatorDrivenTransition.cs rename to src/Avalonia.Base/Animation/AnimatorDrivenTransition.cs diff --git a/src/Avalonia.Animation/AnimatorKeyFrame.cs b/src/Avalonia.Base/Animation/AnimatorKeyFrame.cs similarity index 100% rename from src/Avalonia.Animation/AnimatorKeyFrame.cs rename to src/Avalonia.Base/Animation/AnimatorKeyFrame.cs diff --git a/src/Avalonia.Animation/AnimatorTransitionObservable.cs b/src/Avalonia.Base/Animation/AnimatorTransitionObservable.cs similarity index 100% rename from src/Avalonia.Animation/AnimatorTransitionObservable.cs rename to src/Avalonia.Base/Animation/AnimatorTransitionObservable.cs diff --git a/src/Avalonia.Animation/Animators/Animator`1.cs b/src/Avalonia.Base/Animation/Animators/Animator`1.cs similarity index 100% rename from src/Avalonia.Animation/Animators/Animator`1.cs rename to src/Avalonia.Base/Animation/Animators/Animator`1.cs diff --git a/src/Avalonia.Visuals/Animation/Animators/BaseBrushAnimator.cs b/src/Avalonia.Base/Animation/Animators/BaseBrushAnimator.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Animators/BaseBrushAnimator.cs rename to src/Avalonia.Base/Animation/Animators/BaseBrushAnimator.cs diff --git a/src/Avalonia.Animation/Animators/BoolAnimator.cs b/src/Avalonia.Base/Animation/Animators/BoolAnimator.cs similarity index 100% rename from src/Avalonia.Animation/Animators/BoolAnimator.cs rename to src/Avalonia.Base/Animation/Animators/BoolAnimator.cs diff --git a/src/Avalonia.Visuals/Animation/Animators/BoxShadowAnimator.cs b/src/Avalonia.Base/Animation/Animators/BoxShadowAnimator.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Animators/BoxShadowAnimator.cs rename to src/Avalonia.Base/Animation/Animators/BoxShadowAnimator.cs diff --git a/src/Avalonia.Visuals/Animation/Animators/BoxShadowsAnimator.cs b/src/Avalonia.Base/Animation/Animators/BoxShadowsAnimator.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Animators/BoxShadowsAnimator.cs rename to src/Avalonia.Base/Animation/Animators/BoxShadowsAnimator.cs diff --git a/src/Avalonia.Animation/Animators/ByteAnimator.cs b/src/Avalonia.Base/Animation/Animators/ByteAnimator.cs similarity index 100% rename from src/Avalonia.Animation/Animators/ByteAnimator.cs rename to src/Avalonia.Base/Animation/Animators/ByteAnimator.cs diff --git a/src/Avalonia.Visuals/Animation/Animators/ColorAnimator.cs b/src/Avalonia.Base/Animation/Animators/ColorAnimator.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Animators/ColorAnimator.cs rename to src/Avalonia.Base/Animation/Animators/ColorAnimator.cs diff --git a/src/Avalonia.Visuals/Animation/Animators/CornerRadiusAnimator.cs b/src/Avalonia.Base/Animation/Animators/CornerRadiusAnimator.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Animators/CornerRadiusAnimator.cs rename to src/Avalonia.Base/Animation/Animators/CornerRadiusAnimator.cs diff --git a/src/Avalonia.Animation/Animators/DecimalAnimator.cs b/src/Avalonia.Base/Animation/Animators/DecimalAnimator.cs similarity index 100% rename from src/Avalonia.Animation/Animators/DecimalAnimator.cs rename to src/Avalonia.Base/Animation/Animators/DecimalAnimator.cs diff --git a/src/Avalonia.Animation/Animators/DoubleAnimator.cs b/src/Avalonia.Base/Animation/Animators/DoubleAnimator.cs similarity index 100% rename from src/Avalonia.Animation/Animators/DoubleAnimator.cs rename to src/Avalonia.Base/Animation/Animators/DoubleAnimator.cs diff --git a/src/Avalonia.Animation/Animators/FloatAnimator.cs b/src/Avalonia.Base/Animation/Animators/FloatAnimator.cs similarity index 100% rename from src/Avalonia.Animation/Animators/FloatAnimator.cs rename to src/Avalonia.Base/Animation/Animators/FloatAnimator.cs diff --git a/src/Avalonia.Visuals/Animation/Animators/GradientBrushAnimator.cs b/src/Avalonia.Base/Animation/Animators/GradientBrushAnimator.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Animators/GradientBrushAnimator.cs rename to src/Avalonia.Base/Animation/Animators/GradientBrushAnimator.cs diff --git a/src/Avalonia.Animation/Animators/Int16Animator.cs b/src/Avalonia.Base/Animation/Animators/Int16Animator.cs similarity index 100% rename from src/Avalonia.Animation/Animators/Int16Animator.cs rename to src/Avalonia.Base/Animation/Animators/Int16Animator.cs diff --git a/src/Avalonia.Animation/Animators/Int32Animator.cs b/src/Avalonia.Base/Animation/Animators/Int32Animator.cs similarity index 100% rename from src/Avalonia.Animation/Animators/Int32Animator.cs rename to src/Avalonia.Base/Animation/Animators/Int32Animator.cs diff --git a/src/Avalonia.Animation/Animators/Int64Animator.cs b/src/Avalonia.Base/Animation/Animators/Int64Animator.cs similarity index 100% rename from src/Avalonia.Animation/Animators/Int64Animator.cs rename to src/Avalonia.Base/Animation/Animators/Int64Animator.cs diff --git a/src/Avalonia.Visuals/Animation/Animators/PointAnimator.cs b/src/Avalonia.Base/Animation/Animators/PointAnimator.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Animators/PointAnimator.cs rename to src/Avalonia.Base/Animation/Animators/PointAnimator.cs diff --git a/src/Avalonia.Visuals/Animation/Animators/RectAnimator.cs b/src/Avalonia.Base/Animation/Animators/RectAnimator.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Animators/RectAnimator.cs rename to src/Avalonia.Base/Animation/Animators/RectAnimator.cs diff --git a/src/Avalonia.Visuals/Animation/Animators/RelativePointAnimator.cs b/src/Avalonia.Base/Animation/Animators/RelativePointAnimator.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Animators/RelativePointAnimator.cs rename to src/Avalonia.Base/Animation/Animators/RelativePointAnimator.cs diff --git a/src/Avalonia.Visuals/Animation/Animators/SizeAnimator.cs b/src/Avalonia.Base/Animation/Animators/SizeAnimator.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Animators/SizeAnimator.cs rename to src/Avalonia.Base/Animation/Animators/SizeAnimator.cs diff --git a/src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs b/src/Avalonia.Base/Animation/Animators/SolidColorBrushAnimator.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs rename to src/Avalonia.Base/Animation/Animators/SolidColorBrushAnimator.cs diff --git a/src/Avalonia.Visuals/Animation/Animators/ThicknessAnimator.cs b/src/Avalonia.Base/Animation/Animators/ThicknessAnimator.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Animators/ThicknessAnimator.cs rename to src/Avalonia.Base/Animation/Animators/ThicknessAnimator.cs diff --git a/src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs b/src/Avalonia.Base/Animation/Animators/TransformAnimator.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Animators/TransformAnimator.cs rename to src/Avalonia.Base/Animation/Animators/TransformAnimator.cs diff --git a/src/Avalonia.Visuals/Animation/Animators/TransformOperationsAnimator.cs b/src/Avalonia.Base/Animation/Animators/TransformOperationsAnimator.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Animators/TransformOperationsAnimator.cs rename to src/Avalonia.Base/Animation/Animators/TransformOperationsAnimator.cs diff --git a/src/Avalonia.Animation/Animators/UInt16Animator.cs b/src/Avalonia.Base/Animation/Animators/UInt16Animator.cs similarity index 100% rename from src/Avalonia.Animation/Animators/UInt16Animator.cs rename to src/Avalonia.Base/Animation/Animators/UInt16Animator.cs diff --git a/src/Avalonia.Animation/Animators/UInt32Animator.cs b/src/Avalonia.Base/Animation/Animators/UInt32Animator.cs similarity index 100% rename from src/Avalonia.Animation/Animators/UInt32Animator.cs rename to src/Avalonia.Base/Animation/Animators/UInt32Animator.cs diff --git a/src/Avalonia.Animation/Animators/UInt64Animator.cs b/src/Avalonia.Base/Animation/Animators/UInt64Animator.cs similarity index 100% rename from src/Avalonia.Animation/Animators/UInt64Animator.cs rename to src/Avalonia.Base/Animation/Animators/UInt64Animator.cs diff --git a/src/Avalonia.Visuals/Animation/Animators/VectorAnimator.cs b/src/Avalonia.Base/Animation/Animators/VectorAnimator.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Animators/VectorAnimator.cs rename to src/Avalonia.Base/Animation/Animators/VectorAnimator.cs diff --git a/src/Avalonia.Animation/Clock.cs b/src/Avalonia.Base/Animation/Clock.cs similarity index 100% rename from src/Avalonia.Animation/Clock.cs rename to src/Avalonia.Base/Animation/Clock.cs diff --git a/src/Avalonia.Animation/ClockBase.cs b/src/Avalonia.Base/Animation/ClockBase.cs similarity index 100% rename from src/Avalonia.Animation/ClockBase.cs rename to src/Avalonia.Base/Animation/ClockBase.cs diff --git a/src/Avalonia.Visuals/Animation/CompositePageTransition.cs b/src/Avalonia.Base/Animation/CompositePageTransition.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/CompositePageTransition.cs rename to src/Avalonia.Base/Animation/CompositePageTransition.cs diff --git a/src/Avalonia.Visuals/Animation/CrossFade.cs b/src/Avalonia.Base/Animation/CrossFade.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/CrossFade.cs rename to src/Avalonia.Base/Animation/CrossFade.cs diff --git a/src/Avalonia.Animation/Cue.cs b/src/Avalonia.Base/Animation/Cue.cs similarity index 100% rename from src/Avalonia.Animation/Cue.cs rename to src/Avalonia.Base/Animation/Cue.cs diff --git a/src/Avalonia.Animation/DisposeAnimationInstanceSubject.cs b/src/Avalonia.Base/Animation/DisposeAnimationInstanceSubject.cs similarity index 100% rename from src/Avalonia.Animation/DisposeAnimationInstanceSubject.cs rename to src/Avalonia.Base/Animation/DisposeAnimationInstanceSubject.cs diff --git a/src/Avalonia.Animation/Easing/BackEaseIn.cs b/src/Avalonia.Base/Animation/Easings/BackEaseIn.cs similarity index 100% rename from src/Avalonia.Animation/Easing/BackEaseIn.cs rename to src/Avalonia.Base/Animation/Easings/BackEaseIn.cs diff --git a/src/Avalonia.Animation/Easing/BackEaseInOut.cs b/src/Avalonia.Base/Animation/Easings/BackEaseInOut.cs similarity index 100% rename from src/Avalonia.Animation/Easing/BackEaseInOut.cs rename to src/Avalonia.Base/Animation/Easings/BackEaseInOut.cs diff --git a/src/Avalonia.Animation/Easing/BackEaseOut.cs b/src/Avalonia.Base/Animation/Easings/BackEaseOut.cs similarity index 100% rename from src/Avalonia.Animation/Easing/BackEaseOut.cs rename to src/Avalonia.Base/Animation/Easings/BackEaseOut.cs diff --git a/src/Avalonia.Animation/Easing/BounceEaseIn.cs b/src/Avalonia.Base/Animation/Easings/BounceEaseIn.cs similarity index 100% rename from src/Avalonia.Animation/Easing/BounceEaseIn.cs rename to src/Avalonia.Base/Animation/Easings/BounceEaseIn.cs diff --git a/src/Avalonia.Animation/Easing/BounceEaseInOut.cs b/src/Avalonia.Base/Animation/Easings/BounceEaseInOut.cs similarity index 100% rename from src/Avalonia.Animation/Easing/BounceEaseInOut.cs rename to src/Avalonia.Base/Animation/Easings/BounceEaseInOut.cs diff --git a/src/Avalonia.Animation/Easing/BounceEaseOut.cs b/src/Avalonia.Base/Animation/Easings/BounceEaseOut.cs similarity index 100% rename from src/Avalonia.Animation/Easing/BounceEaseOut.cs rename to src/Avalonia.Base/Animation/Easings/BounceEaseOut.cs diff --git a/src/Avalonia.Animation/Easing/CircularEaseIn.cs b/src/Avalonia.Base/Animation/Easings/CircularEaseIn.cs similarity index 100% rename from src/Avalonia.Animation/Easing/CircularEaseIn.cs rename to src/Avalonia.Base/Animation/Easings/CircularEaseIn.cs diff --git a/src/Avalonia.Animation/Easing/CircularEaseInOut.cs b/src/Avalonia.Base/Animation/Easings/CircularEaseInOut.cs similarity index 100% rename from src/Avalonia.Animation/Easing/CircularEaseInOut.cs rename to src/Avalonia.Base/Animation/Easings/CircularEaseInOut.cs diff --git a/src/Avalonia.Animation/Easing/CircularEaseOut.cs b/src/Avalonia.Base/Animation/Easings/CircularEaseOut.cs similarity index 100% rename from src/Avalonia.Animation/Easing/CircularEaseOut.cs rename to src/Avalonia.Base/Animation/Easings/CircularEaseOut.cs diff --git a/src/Avalonia.Animation/Easing/CubicEaseIn.cs b/src/Avalonia.Base/Animation/Easings/CubicEaseIn.cs similarity index 100% rename from src/Avalonia.Animation/Easing/CubicEaseIn.cs rename to src/Avalonia.Base/Animation/Easings/CubicEaseIn.cs diff --git a/src/Avalonia.Animation/Easing/CubicEaseInOut.cs b/src/Avalonia.Base/Animation/Easings/CubicEaseInOut.cs similarity index 100% rename from src/Avalonia.Animation/Easing/CubicEaseInOut.cs rename to src/Avalonia.Base/Animation/Easings/CubicEaseInOut.cs diff --git a/src/Avalonia.Animation/Easing/CubicEaseOut.cs b/src/Avalonia.Base/Animation/Easings/CubicEaseOut.cs similarity index 100% rename from src/Avalonia.Animation/Easing/CubicEaseOut.cs rename to src/Avalonia.Base/Animation/Easings/CubicEaseOut.cs diff --git a/src/Avalonia.Animation/Easing/Easing.cs b/src/Avalonia.Base/Animation/Easings/Easing.cs similarity index 100% rename from src/Avalonia.Animation/Easing/Easing.cs rename to src/Avalonia.Base/Animation/Easings/Easing.cs diff --git a/src/Avalonia.Animation/Easing/EasingTypeConverter.cs b/src/Avalonia.Base/Animation/Easings/EasingTypeConverter.cs similarity index 100% rename from src/Avalonia.Animation/Easing/EasingTypeConverter.cs rename to src/Avalonia.Base/Animation/Easings/EasingTypeConverter.cs diff --git a/src/Avalonia.Animation/Easing/ElasticEaseIn.cs b/src/Avalonia.Base/Animation/Easings/ElasticEaseIn.cs similarity index 100% rename from src/Avalonia.Animation/Easing/ElasticEaseIn.cs rename to src/Avalonia.Base/Animation/Easings/ElasticEaseIn.cs diff --git a/src/Avalonia.Animation/Easing/ElasticEaseInOut.cs b/src/Avalonia.Base/Animation/Easings/ElasticEaseInOut.cs similarity index 100% rename from src/Avalonia.Animation/Easing/ElasticEaseInOut.cs rename to src/Avalonia.Base/Animation/Easings/ElasticEaseInOut.cs diff --git a/src/Avalonia.Animation/Easing/ElasticEaseOut.cs b/src/Avalonia.Base/Animation/Easings/ElasticEaseOut.cs similarity index 100% rename from src/Avalonia.Animation/Easing/ElasticEaseOut.cs rename to src/Avalonia.Base/Animation/Easings/ElasticEaseOut.cs diff --git a/src/Avalonia.Animation/Easing/ExponentialEaseIn.cs b/src/Avalonia.Base/Animation/Easings/ExponentialEaseIn.cs similarity index 100% rename from src/Avalonia.Animation/Easing/ExponentialEaseIn.cs rename to src/Avalonia.Base/Animation/Easings/ExponentialEaseIn.cs diff --git a/src/Avalonia.Animation/Easing/ExponentialEaseInOut.cs b/src/Avalonia.Base/Animation/Easings/ExponentialEaseInOut.cs similarity index 100% rename from src/Avalonia.Animation/Easing/ExponentialEaseInOut.cs rename to src/Avalonia.Base/Animation/Easings/ExponentialEaseInOut.cs diff --git a/src/Avalonia.Animation/Easing/ExponentialEaseOut.cs b/src/Avalonia.Base/Animation/Easings/ExponentialEaseOut.cs similarity index 100% rename from src/Avalonia.Animation/Easing/ExponentialEaseOut.cs rename to src/Avalonia.Base/Animation/Easings/ExponentialEaseOut.cs diff --git a/src/Avalonia.Animation/Easing/IEasing.cs b/src/Avalonia.Base/Animation/Easings/IEasing.cs similarity index 100% rename from src/Avalonia.Animation/Easing/IEasing.cs rename to src/Avalonia.Base/Animation/Easings/IEasing.cs diff --git a/src/Avalonia.Animation/Easing/LinearEasing.cs b/src/Avalonia.Base/Animation/Easings/LinearEasing.cs similarity index 100% rename from src/Avalonia.Animation/Easing/LinearEasing.cs rename to src/Avalonia.Base/Animation/Easings/LinearEasing.cs diff --git a/src/Avalonia.Animation/Easing/QuadraticEaseIn.cs b/src/Avalonia.Base/Animation/Easings/QuadraticEaseIn.cs similarity index 100% rename from src/Avalonia.Animation/Easing/QuadraticEaseIn.cs rename to src/Avalonia.Base/Animation/Easings/QuadraticEaseIn.cs diff --git a/src/Avalonia.Animation/Easing/QuadraticEaseInOut.cs b/src/Avalonia.Base/Animation/Easings/QuadraticEaseInOut.cs similarity index 100% rename from src/Avalonia.Animation/Easing/QuadraticEaseInOut.cs rename to src/Avalonia.Base/Animation/Easings/QuadraticEaseInOut.cs diff --git a/src/Avalonia.Animation/Easing/QuadraticEaseOut.cs b/src/Avalonia.Base/Animation/Easings/QuadraticEaseOut.cs similarity index 100% rename from src/Avalonia.Animation/Easing/QuadraticEaseOut.cs rename to src/Avalonia.Base/Animation/Easings/QuadraticEaseOut.cs diff --git a/src/Avalonia.Animation/Easing/QuarticEaseIn.cs b/src/Avalonia.Base/Animation/Easings/QuarticEaseIn.cs similarity index 100% rename from src/Avalonia.Animation/Easing/QuarticEaseIn.cs rename to src/Avalonia.Base/Animation/Easings/QuarticEaseIn.cs diff --git a/src/Avalonia.Animation/Easing/QuarticEaseInOut.cs b/src/Avalonia.Base/Animation/Easings/QuarticEaseInOut.cs similarity index 100% rename from src/Avalonia.Animation/Easing/QuarticEaseInOut.cs rename to src/Avalonia.Base/Animation/Easings/QuarticEaseInOut.cs diff --git a/src/Avalonia.Animation/Easing/QuarticEaseOut.cs b/src/Avalonia.Base/Animation/Easings/QuarticEaseOut.cs similarity index 100% rename from src/Avalonia.Animation/Easing/QuarticEaseOut.cs rename to src/Avalonia.Base/Animation/Easings/QuarticEaseOut.cs diff --git a/src/Avalonia.Animation/Easing/QuinticEaseIn.cs b/src/Avalonia.Base/Animation/Easings/QuinticEaseIn.cs similarity index 100% rename from src/Avalonia.Animation/Easing/QuinticEaseIn.cs rename to src/Avalonia.Base/Animation/Easings/QuinticEaseIn.cs diff --git a/src/Avalonia.Animation/Easing/QuinticEaseInOut.cs b/src/Avalonia.Base/Animation/Easings/QuinticEaseInOut.cs similarity index 100% rename from src/Avalonia.Animation/Easing/QuinticEaseInOut.cs rename to src/Avalonia.Base/Animation/Easings/QuinticEaseInOut.cs diff --git a/src/Avalonia.Animation/Easing/QuinticEaseOut.cs b/src/Avalonia.Base/Animation/Easings/QuinticEaseOut.cs similarity index 100% rename from src/Avalonia.Animation/Easing/QuinticEaseOut.cs rename to src/Avalonia.Base/Animation/Easings/QuinticEaseOut.cs diff --git a/src/Avalonia.Animation/Easing/SineEaseIn.cs b/src/Avalonia.Base/Animation/Easings/SineEaseIn.cs similarity index 100% rename from src/Avalonia.Animation/Easing/SineEaseIn.cs rename to src/Avalonia.Base/Animation/Easings/SineEaseIn.cs diff --git a/src/Avalonia.Animation/Easing/SineEaseInOut.cs b/src/Avalonia.Base/Animation/Easings/SineEaseInOut.cs similarity index 100% rename from src/Avalonia.Animation/Easing/SineEaseInOut.cs rename to src/Avalonia.Base/Animation/Easings/SineEaseInOut.cs diff --git a/src/Avalonia.Animation/Easing/SineEaseOut.cs b/src/Avalonia.Base/Animation/Easings/SineEaseOut.cs similarity index 100% rename from src/Avalonia.Animation/Easing/SineEaseOut.cs rename to src/Avalonia.Base/Animation/Easings/SineEaseOut.cs diff --git a/src/Avalonia.Animation/Easing/SplineEasing.cs b/src/Avalonia.Base/Animation/Easings/SplineEasing.cs similarity index 100% rename from src/Avalonia.Animation/Easing/SplineEasing.cs rename to src/Avalonia.Base/Animation/Easings/SplineEasing.cs diff --git a/src/Avalonia.Animation/FillMode.cs b/src/Avalonia.Base/Animation/FillMode.cs similarity index 100% rename from src/Avalonia.Animation/FillMode.cs rename to src/Avalonia.Base/Animation/FillMode.cs diff --git a/src/Avalonia.Animation/IAnimation.cs b/src/Avalonia.Base/Animation/IAnimation.cs similarity index 100% rename from src/Avalonia.Animation/IAnimation.cs rename to src/Avalonia.Base/Animation/IAnimation.cs diff --git a/src/Avalonia.Animation/IAnimationSetter.cs b/src/Avalonia.Base/Animation/IAnimationSetter.cs similarity index 100% rename from src/Avalonia.Animation/IAnimationSetter.cs rename to src/Avalonia.Base/Animation/IAnimationSetter.cs diff --git a/src/Avalonia.Animation/IAnimator.cs b/src/Avalonia.Base/Animation/IAnimator.cs similarity index 100% rename from src/Avalonia.Animation/IAnimator.cs rename to src/Avalonia.Base/Animation/IAnimator.cs diff --git a/src/Avalonia.Animation/IClock.cs b/src/Avalonia.Base/Animation/IClock.cs similarity index 100% rename from src/Avalonia.Animation/IClock.cs rename to src/Avalonia.Base/Animation/IClock.cs diff --git a/src/Avalonia.Animation/IGlobalClock.cs b/src/Avalonia.Base/Animation/IGlobalClock.cs similarity index 100% rename from src/Avalonia.Animation/IGlobalClock.cs rename to src/Avalonia.Base/Animation/IGlobalClock.cs diff --git a/src/Avalonia.Visuals/Animation/IPageTransition.cs b/src/Avalonia.Base/Animation/IPageTransition.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/IPageTransition.cs rename to src/Avalonia.Base/Animation/IPageTransition.cs diff --git a/src/Avalonia.Animation/ITransition.cs b/src/Avalonia.Base/Animation/ITransition.cs similarity index 100% rename from src/Avalonia.Animation/ITransition.cs rename to src/Avalonia.Base/Animation/ITransition.cs diff --git a/src/Avalonia.Animation/IterationCount.cs b/src/Avalonia.Base/Animation/IterationCount.cs similarity index 100% rename from src/Avalonia.Animation/IterationCount.cs rename to src/Avalonia.Base/Animation/IterationCount.cs diff --git a/src/Avalonia.Animation/IterationCountTypeConverter.cs b/src/Avalonia.Base/Animation/IterationCountTypeConverter.cs similarity index 100% rename from src/Avalonia.Animation/IterationCountTypeConverter.cs rename to src/Avalonia.Base/Animation/IterationCountTypeConverter.cs diff --git a/src/Avalonia.Animation/KeyFrame.cs b/src/Avalonia.Base/Animation/KeyFrame.cs similarity index 100% rename from src/Avalonia.Animation/KeyFrame.cs rename to src/Avalonia.Base/Animation/KeyFrame.cs diff --git a/src/Avalonia.Animation/KeyFrames.cs b/src/Avalonia.Base/Animation/KeyFrames.cs similarity index 100% rename from src/Avalonia.Animation/KeyFrames.cs rename to src/Avalonia.Base/Animation/KeyFrames.cs diff --git a/src/Avalonia.Animation/KeySpline.cs b/src/Avalonia.Base/Animation/KeySpline.cs similarity index 100% rename from src/Avalonia.Animation/KeySpline.cs rename to src/Avalonia.Base/Animation/KeySpline.cs diff --git a/src/Avalonia.Animation/KeySplineTypeConverter.cs b/src/Avalonia.Base/Animation/KeySplineTypeConverter.cs similarity index 100% rename from src/Avalonia.Animation/KeySplineTypeConverter.cs rename to src/Avalonia.Base/Animation/KeySplineTypeConverter.cs diff --git a/src/Avalonia.Visuals/Animation/PageSlide.cs b/src/Avalonia.Base/Animation/PageSlide.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/PageSlide.cs rename to src/Avalonia.Base/Animation/PageSlide.cs diff --git a/src/Avalonia.Animation/PlayState.cs b/src/Avalonia.Base/Animation/PlayState.cs similarity index 100% rename from src/Avalonia.Animation/PlayState.cs rename to src/Avalonia.Base/Animation/PlayState.cs diff --git a/src/Avalonia.Animation/PlaybackDirection.cs b/src/Avalonia.Base/Animation/PlaybackDirection.cs similarity index 100% rename from src/Avalonia.Animation/PlaybackDirection.cs rename to src/Avalonia.Base/Animation/PlaybackDirection.cs diff --git a/src/Avalonia.Visuals/Animation/RenderLoopClock.cs b/src/Avalonia.Base/Animation/RenderLoopClock.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/RenderLoopClock.cs rename to src/Avalonia.Base/Animation/RenderLoopClock.cs diff --git a/src/Avalonia.Animation/Transition.cs b/src/Avalonia.Base/Animation/Transition.cs similarity index 100% rename from src/Avalonia.Animation/Transition.cs rename to src/Avalonia.Base/Animation/Transition.cs diff --git a/src/Avalonia.Animation/TransitionInstance.cs b/src/Avalonia.Base/Animation/TransitionInstance.cs similarity index 100% rename from src/Avalonia.Animation/TransitionInstance.cs rename to src/Avalonia.Base/Animation/TransitionInstance.cs diff --git a/src/Avalonia.Animation/TransitionObservableBase.cs b/src/Avalonia.Base/Animation/TransitionObservableBase.cs similarity index 100% rename from src/Avalonia.Animation/TransitionObservableBase.cs rename to src/Avalonia.Base/Animation/TransitionObservableBase.cs diff --git a/src/Avalonia.Animation/Transitions.cs b/src/Avalonia.Base/Animation/Transitions.cs similarity index 100% rename from src/Avalonia.Animation/Transitions.cs rename to src/Avalonia.Base/Animation/Transitions.cs diff --git a/src/Avalonia.Visuals/Animation/Transitions/BoxShadowsTransition.cs b/src/Avalonia.Base/Animation/Transitions/BoxShadowsTransition.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Transitions/BoxShadowsTransition.cs rename to src/Avalonia.Base/Animation/Transitions/BoxShadowsTransition.cs diff --git a/src/Avalonia.Visuals/Animation/Transitions/BrushTransition.cs b/src/Avalonia.Base/Animation/Transitions/BrushTransition.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Transitions/BrushTransition.cs rename to src/Avalonia.Base/Animation/Transitions/BrushTransition.cs diff --git a/src/Avalonia.Visuals/Animation/Transitions/ColorTransition.cs b/src/Avalonia.Base/Animation/Transitions/ColorTransition.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Transitions/ColorTransition.cs rename to src/Avalonia.Base/Animation/Transitions/ColorTransition.cs diff --git a/src/Avalonia.Visuals/Animation/Transitions/CornerRadiusTransition.cs b/src/Avalonia.Base/Animation/Transitions/CornerRadiusTransition.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Transitions/CornerRadiusTransition.cs rename to src/Avalonia.Base/Animation/Transitions/CornerRadiusTransition.cs diff --git a/src/Avalonia.Animation/Transitions/DoubleTransition.cs b/src/Avalonia.Base/Animation/Transitions/DoubleTransition.cs similarity index 100% rename from src/Avalonia.Animation/Transitions/DoubleTransition.cs rename to src/Avalonia.Base/Animation/Transitions/DoubleTransition.cs diff --git a/src/Avalonia.Animation/Transitions/FloatTransition.cs b/src/Avalonia.Base/Animation/Transitions/FloatTransition.cs similarity index 100% rename from src/Avalonia.Animation/Transitions/FloatTransition.cs rename to src/Avalonia.Base/Animation/Transitions/FloatTransition.cs diff --git a/src/Avalonia.Animation/Transitions/IntegerTransition.cs b/src/Avalonia.Base/Animation/Transitions/IntegerTransition.cs similarity index 100% rename from src/Avalonia.Animation/Transitions/IntegerTransition.cs rename to src/Avalonia.Base/Animation/Transitions/IntegerTransition.cs diff --git a/src/Avalonia.Visuals/Animation/Transitions/PointTransition.cs b/src/Avalonia.Base/Animation/Transitions/PointTransition.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Transitions/PointTransition.cs rename to src/Avalonia.Base/Animation/Transitions/PointTransition.cs diff --git a/src/Avalonia.Visuals/Animation/Transitions/RelativePointTransition.cs b/src/Avalonia.Base/Animation/Transitions/RelativePointTransition.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Transitions/RelativePointTransition.cs rename to src/Avalonia.Base/Animation/Transitions/RelativePointTransition.cs diff --git a/src/Avalonia.Visuals/Animation/Transitions/SizeTransition.cs b/src/Avalonia.Base/Animation/Transitions/SizeTransition.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Transitions/SizeTransition.cs rename to src/Avalonia.Base/Animation/Transitions/SizeTransition.cs diff --git a/src/Avalonia.Visuals/Animation/Transitions/ThicknessTransition.cs b/src/Avalonia.Base/Animation/Transitions/ThicknessTransition.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Transitions/ThicknessTransition.cs rename to src/Avalonia.Base/Animation/Transitions/ThicknessTransition.cs diff --git a/src/Avalonia.Visuals/Animation/Transitions/TransformOperationsTransition.cs b/src/Avalonia.Base/Animation/Transitions/TransformOperationsTransition.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Transitions/TransformOperationsTransition.cs rename to src/Avalonia.Base/Animation/Transitions/TransformOperationsTransition.cs diff --git a/src/Avalonia.Visuals/Animation/Transitions/VectorTransition.cs b/src/Avalonia.Base/Animation/Transitions/VectorTransition.cs similarity index 100% rename from src/Avalonia.Visuals/Animation/Transitions/VectorTransition.cs rename to src/Avalonia.Base/Animation/Transitions/VectorTransition.cs diff --git a/src/Avalonia.Animation/Utils/BounceEaseUtils.cs b/src/Avalonia.Base/Animation/Utils/BounceEaseUtils.cs similarity index 100% rename from src/Avalonia.Animation/Utils/BounceEaseUtils.cs rename to src/Avalonia.Base/Animation/Utils/BounceEaseUtils.cs diff --git a/src/Avalonia.Animation/Utils/EasingUtils.cs b/src/Avalonia.Base/Animation/Utils/EasingUtils.cs similarity index 100% rename from src/Avalonia.Animation/Utils/EasingUtils.cs rename to src/Avalonia.Base/Animation/Utils/EasingUtils.cs diff --git a/src/Avalonia.Visuals/Assets/BiDi.trie b/src/Avalonia.Base/Assets/BiDi.trie similarity index 100% rename from src/Avalonia.Visuals/Assets/BiDi.trie rename to src/Avalonia.Base/Assets/BiDi.trie diff --git a/src/Avalonia.Visuals/Assets/GraphemeBreak.trie b/src/Avalonia.Base/Assets/GraphemeBreak.trie similarity index 100% rename from src/Avalonia.Visuals/Assets/GraphemeBreak.trie rename to src/Avalonia.Base/Assets/GraphemeBreak.trie diff --git a/src/Avalonia.Visuals/Assets/UnicodeData.trie b/src/Avalonia.Base/Assets/UnicodeData.trie similarity index 100% rename from src/Avalonia.Visuals/Assets/UnicodeData.trie rename to src/Avalonia.Base/Assets/UnicodeData.trie diff --git a/src/Avalonia.Base/Avalonia.Base.csproj b/src/Avalonia.Base/Avalonia.Base.csproj index 637b86d759..5b79d3cd7b 100644 --- a/src/Avalonia.Base/Avalonia.Base.csproj +++ b/src/Avalonia.Base/Avalonia.Base.csproj @@ -5,6 +5,9 @@ Avalonia True + + + diff --git a/src/Avalonia.Visuals/AvaloniaPropertyExtensions.cs b/src/Avalonia.Base/AvaloniaPropertyExtensions.cs similarity index 100% rename from src/Avalonia.Visuals/AvaloniaPropertyExtensions.cs rename to src/Avalonia.Base/AvaloniaPropertyExtensions.cs diff --git a/src/Avalonia.Styling/ClassBindingManager.cs b/src/Avalonia.Base/ClassBindingManager.cs similarity index 100% rename from src/Avalonia.Styling/ClassBindingManager.cs rename to src/Avalonia.Base/ClassBindingManager.cs diff --git a/src/Avalonia.Visuals/Media/CombinedGeometry.cs b/src/Avalonia.Base/CombinedGeometry.cs similarity index 100% rename from src/Avalonia.Visuals/Media/CombinedGeometry.cs rename to src/Avalonia.Base/CombinedGeometry.cs diff --git a/src/Avalonia.Styling/Controls/ChildNameScope.cs b/src/Avalonia.Base/Controls/ChildNameScope.cs similarity index 99% rename from src/Avalonia.Styling/Controls/ChildNameScope.cs rename to src/Avalonia.Base/Controls/ChildNameScope.cs index 58114a57fd..1ceaff924c 100644 --- a/src/Avalonia.Styling/Controls/ChildNameScope.cs +++ b/src/Avalonia.Base/Controls/ChildNameScope.cs @@ -22,7 +22,7 @@ namespace Avalonia.Controls return new SynchronousCompletionAsyncResult(found); // Not found and both current and parent scope are in completed state if(IsCompleted) - return new SynchronousCompletionAsyncResult(null); + return new SynchronousCompletionAsyncResult((object?)null); return DoFindAsync(name); } diff --git a/src/Avalonia.Styling/Controls/Classes.cs b/src/Avalonia.Base/Controls/Classes.cs similarity index 100% rename from src/Avalonia.Styling/Controls/Classes.cs rename to src/Avalonia.Base/Controls/Classes.cs diff --git a/src/Avalonia.Styling/Controls/INameScope.cs b/src/Avalonia.Base/Controls/INameScope.cs similarity index 100% rename from src/Avalonia.Styling/Controls/INameScope.cs rename to src/Avalonia.Base/Controls/INameScope.cs diff --git a/src/Avalonia.Styling/Controls/IPseudoClasses.cs b/src/Avalonia.Base/Controls/IPseudoClasses.cs similarity index 100% rename from src/Avalonia.Styling/Controls/IPseudoClasses.cs rename to src/Avalonia.Base/Controls/IPseudoClasses.cs diff --git a/src/Avalonia.Styling/Controls/IResourceDictionary.cs b/src/Avalonia.Base/Controls/IResourceDictionary.cs similarity index 100% rename from src/Avalonia.Styling/Controls/IResourceDictionary.cs rename to src/Avalonia.Base/Controls/IResourceDictionary.cs diff --git a/src/Avalonia.Styling/Controls/IResourceHost.cs b/src/Avalonia.Base/Controls/IResourceHost.cs similarity index 100% rename from src/Avalonia.Styling/Controls/IResourceHost.cs rename to src/Avalonia.Base/Controls/IResourceHost.cs diff --git a/src/Avalonia.Styling/Controls/IResourceNode.cs b/src/Avalonia.Base/Controls/IResourceNode.cs similarity index 100% rename from src/Avalonia.Styling/Controls/IResourceNode.cs rename to src/Avalonia.Base/Controls/IResourceNode.cs diff --git a/src/Avalonia.Styling/Controls/IResourceProvider.cs b/src/Avalonia.Base/Controls/IResourceProvider.cs similarity index 100% rename from src/Avalonia.Styling/Controls/IResourceProvider.cs rename to src/Avalonia.Base/Controls/IResourceProvider.cs diff --git a/src/Avalonia.Styling/Controls/ISetInheritanceParent.cs b/src/Avalonia.Base/Controls/ISetInheritanceParent.cs similarity index 100% rename from src/Avalonia.Styling/Controls/ISetInheritanceParent.cs rename to src/Avalonia.Base/Controls/ISetInheritanceParent.cs diff --git a/src/Avalonia.Styling/Controls/ISetLogicalParent.cs b/src/Avalonia.Base/Controls/ISetLogicalParent.cs similarity index 100% rename from src/Avalonia.Styling/Controls/ISetLogicalParent.cs rename to src/Avalonia.Base/Controls/ISetLogicalParent.cs diff --git a/src/Avalonia.Styling/Controls/Metadata/PseudoClassesAttribute.cs b/src/Avalonia.Base/Controls/Metadata/PseudoClassesAttribute.cs similarity index 100% rename from src/Avalonia.Styling/Controls/Metadata/PseudoClassesAttribute.cs rename to src/Avalonia.Base/Controls/Metadata/PseudoClassesAttribute.cs diff --git a/src/Avalonia.Styling/Controls/Metadata/TemplatePartAttribute.cs b/src/Avalonia.Base/Controls/Metadata/TemplatePartAttribute.cs similarity index 100% rename from src/Avalonia.Styling/Controls/Metadata/TemplatePartAttribute.cs rename to src/Avalonia.Base/Controls/Metadata/TemplatePartAttribute.cs diff --git a/src/Avalonia.Styling/Controls/NameScope.cs b/src/Avalonia.Base/Controls/NameScope.cs similarity index 99% rename from src/Avalonia.Styling/Controls/NameScope.cs rename to src/Avalonia.Base/Controls/NameScope.cs index 77f98f85c4..952401c00c 100644 --- a/src/Avalonia.Styling/Controls/NameScope.cs +++ b/src/Avalonia.Base/Controls/NameScope.cs @@ -82,7 +82,7 @@ namespace Avalonia.Controls if (found != null) return new SynchronousCompletionAsyncResult(found); if (IsCompleted) - return new SynchronousCompletionAsyncResult(null); + return new SynchronousCompletionAsyncResult((object?)null); if (!_pendingSearches.TryGetValue(name, out var tcs)) // We are intentionally running continuations synchronously here _pendingSearches[name] = tcs = new SynchronousCompletionAsyncResultSource(); diff --git a/src/Avalonia.Styling/Controls/NameScopeEventArgs.cs b/src/Avalonia.Base/Controls/NameScopeEventArgs.cs similarity index 100% rename from src/Avalonia.Styling/Controls/NameScopeEventArgs.cs rename to src/Avalonia.Base/Controls/NameScopeEventArgs.cs diff --git a/src/Avalonia.Styling/Controls/NameScopeExtensions.cs b/src/Avalonia.Base/Controls/NameScopeExtensions.cs similarity index 100% rename from src/Avalonia.Styling/Controls/NameScopeExtensions.cs rename to src/Avalonia.Base/Controls/NameScopeExtensions.cs diff --git a/src/Avalonia.Styling/Controls/NameScopeLocator.cs b/src/Avalonia.Base/Controls/NameScopeLocator.cs similarity index 100% rename from src/Avalonia.Styling/Controls/NameScopeLocator.cs rename to src/Avalonia.Base/Controls/NameScopeLocator.cs diff --git a/src/Avalonia.Styling/Controls/PseudoClassesExtensions.cs b/src/Avalonia.Base/Controls/PseudoClassesExtensions.cs similarity index 100% rename from src/Avalonia.Styling/Controls/PseudoClassesExtensions.cs rename to src/Avalonia.Base/Controls/PseudoClassesExtensions.cs diff --git a/src/Avalonia.Styling/Controls/ResourceDictionary.cs b/src/Avalonia.Base/Controls/ResourceDictionary.cs similarity index 100% rename from src/Avalonia.Styling/Controls/ResourceDictionary.cs rename to src/Avalonia.Base/Controls/ResourceDictionary.cs diff --git a/src/Avalonia.Styling/Controls/ResourceNodeExtensions.cs b/src/Avalonia.Base/Controls/ResourceNodeExtensions.cs similarity index 100% rename from src/Avalonia.Styling/Controls/ResourceNodeExtensions.cs rename to src/Avalonia.Base/Controls/ResourceNodeExtensions.cs diff --git a/src/Avalonia.Styling/Controls/ResourcesChangedEventArgs.cs b/src/Avalonia.Base/Controls/ResourcesChangedEventArgs.cs similarity index 100% rename from src/Avalonia.Styling/Controls/ResourcesChangedEventArgs.cs rename to src/Avalonia.Base/Controls/ResourcesChangedEventArgs.cs diff --git a/src/Avalonia.Visuals/CornerRadius.cs b/src/Avalonia.Base/CornerRadius.cs similarity index 100% rename from src/Avalonia.Visuals/CornerRadius.cs rename to src/Avalonia.Base/CornerRadius.cs diff --git a/src/Avalonia.Styling/Diagnostics/StyleDiagnostics.cs b/src/Avalonia.Base/Diagnostics/StyleDiagnostics.cs similarity index 100% rename from src/Avalonia.Styling/Diagnostics/StyleDiagnostics.cs rename to src/Avalonia.Base/Diagnostics/StyleDiagnostics.cs diff --git a/src/Avalonia.Styling/Diagnostics/StyledElementExtensions.cs b/src/Avalonia.Base/Diagnostics/StyledElementExtensions.cs similarity index 100% rename from src/Avalonia.Styling/Diagnostics/StyledElementExtensions.cs rename to src/Avalonia.Base/Diagnostics/StyledElementExtensions.cs diff --git a/src/Avalonia.Visuals/Media/GeometryCollection.cs b/src/Avalonia.Base/GeometryCollection.cs similarity index 100% rename from src/Avalonia.Visuals/Media/GeometryCollection.cs rename to src/Avalonia.Base/GeometryCollection.cs diff --git a/src/Avalonia.Visuals/Media/GeometryGroup.cs b/src/Avalonia.Base/GeometryGroup.cs similarity index 100% rename from src/Avalonia.Visuals/Media/GeometryGroup.cs rename to src/Avalonia.Base/GeometryGroup.cs diff --git a/src/Avalonia.Styling/IDataContextProvider.cs b/src/Avalonia.Base/IDataContextProvider.cs similarity index 100% rename from src/Avalonia.Styling/IDataContextProvider.cs rename to src/Avalonia.Base/IDataContextProvider.cs diff --git a/src/Avalonia.Styling/INamed.cs b/src/Avalonia.Base/INamed.cs similarity index 100% rename from src/Avalonia.Styling/INamed.cs rename to src/Avalonia.Base/INamed.cs diff --git a/src/Avalonia.Styling/IStyledElement.cs b/src/Avalonia.Base/IStyledElement.cs similarity index 100% rename from src/Avalonia.Styling/IStyledElement.cs rename to src/Avalonia.Base/IStyledElement.cs diff --git a/src/Avalonia.Input/AccessKeyHandler.cs b/src/Avalonia.Base/Input/AccessKeyHandler.cs similarity index 100% rename from src/Avalonia.Input/AccessKeyHandler.cs rename to src/Avalonia.Base/Input/AccessKeyHandler.cs diff --git a/src/Avalonia.Input/Cursor.cs b/src/Avalonia.Base/Input/Cursor.cs similarity index 100% rename from src/Avalonia.Input/Cursor.cs rename to src/Avalonia.Base/Input/Cursor.cs diff --git a/src/Avalonia.Input/DataFormats.cs b/src/Avalonia.Base/Input/DataFormats.cs similarity index 100% rename from src/Avalonia.Input/DataFormats.cs rename to src/Avalonia.Base/Input/DataFormats.cs diff --git a/src/Avalonia.Input/DataObject.cs b/src/Avalonia.Base/Input/DataObject.cs similarity index 100% rename from src/Avalonia.Input/DataObject.cs rename to src/Avalonia.Base/Input/DataObject.cs diff --git a/src/Avalonia.Input/DragDrop.cs b/src/Avalonia.Base/Input/DragDrop.cs similarity index 100% rename from src/Avalonia.Input/DragDrop.cs rename to src/Avalonia.Base/Input/DragDrop.cs diff --git a/src/Avalonia.Input/DragDropDevice.cs b/src/Avalonia.Base/Input/DragDropDevice.cs similarity index 100% rename from src/Avalonia.Input/DragDropDevice.cs rename to src/Avalonia.Base/Input/DragDropDevice.cs diff --git a/src/Avalonia.Input/DragDropEffects.cs b/src/Avalonia.Base/Input/DragDropEffects.cs similarity index 100% rename from src/Avalonia.Input/DragDropEffects.cs rename to src/Avalonia.Base/Input/DragDropEffects.cs diff --git a/src/Avalonia.Input/DragEventArgs.cs b/src/Avalonia.Base/Input/DragEventArgs.cs similarity index 100% rename from src/Avalonia.Input/DragEventArgs.cs rename to src/Avalonia.Base/Input/DragEventArgs.cs diff --git a/src/Avalonia.Input/FocusManager.cs b/src/Avalonia.Base/Input/FocusManager.cs similarity index 100% rename from src/Avalonia.Input/FocusManager.cs rename to src/Avalonia.Base/Input/FocusManager.cs diff --git a/src/Avalonia.Input/GestureRecognizers/GestureRecognizerCollection.cs b/src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizerCollection.cs similarity index 100% rename from src/Avalonia.Input/GestureRecognizers/GestureRecognizerCollection.cs rename to src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizerCollection.cs diff --git a/src/Avalonia.Input/GestureRecognizers/IGestureRecognizer.cs b/src/Avalonia.Base/Input/GestureRecognizers/IGestureRecognizer.cs similarity index 100% rename from src/Avalonia.Input/GestureRecognizers/IGestureRecognizer.cs rename to src/Avalonia.Base/Input/GestureRecognizers/IGestureRecognizer.cs diff --git a/src/Avalonia.Input/GestureRecognizers/ScrollGestureRecognizer.cs b/src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs similarity index 100% rename from src/Avalonia.Input/GestureRecognizers/ScrollGestureRecognizer.cs rename to src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs diff --git a/src/Avalonia.Input/Gestures.cs b/src/Avalonia.Base/Input/Gestures.cs similarity index 100% rename from src/Avalonia.Input/Gestures.cs rename to src/Avalonia.Base/Input/Gestures.cs diff --git a/src/Avalonia.Input/GotFocusEventArgs.cs b/src/Avalonia.Base/Input/GotFocusEventArgs.cs similarity index 100% rename from src/Avalonia.Input/GotFocusEventArgs.cs rename to src/Avalonia.Base/Input/GotFocusEventArgs.cs diff --git a/src/Avalonia.Input/IAccessKeyHandler.cs b/src/Avalonia.Base/Input/IAccessKeyHandler.cs similarity index 100% rename from src/Avalonia.Input/IAccessKeyHandler.cs rename to src/Avalonia.Base/Input/IAccessKeyHandler.cs diff --git a/src/Avalonia.Input/IClickableControl.cs b/src/Avalonia.Base/Input/IClickableControl.cs similarity index 100% rename from src/Avalonia.Input/IClickableControl.cs rename to src/Avalonia.Base/Input/IClickableControl.cs diff --git a/src/Avalonia.Input/ICloseable.cs b/src/Avalonia.Base/Input/ICloseable.cs similarity index 100% rename from src/Avalonia.Input/ICloseable.cs rename to src/Avalonia.Base/Input/ICloseable.cs diff --git a/src/Avalonia.Input/ICommandSource.cs b/src/Avalonia.Base/Input/ICommandSource.cs similarity index 100% rename from src/Avalonia.Input/ICommandSource.cs rename to src/Avalonia.Base/Input/ICommandSource.cs diff --git a/src/Avalonia.Input/ICustomKeyboardNavigation.cs b/src/Avalonia.Base/Input/ICustomKeyboardNavigation.cs similarity index 100% rename from src/Avalonia.Input/ICustomKeyboardNavigation.cs rename to src/Avalonia.Base/Input/ICustomKeyboardNavigation.cs diff --git a/src/Avalonia.Input/IDataObject.cs b/src/Avalonia.Base/Input/IDataObject.cs similarity index 100% rename from src/Avalonia.Input/IDataObject.cs rename to src/Avalonia.Base/Input/IDataObject.cs diff --git a/src/Avalonia.Input/IFocusManager.cs b/src/Avalonia.Base/Input/IFocusManager.cs similarity index 100% rename from src/Avalonia.Input/IFocusManager.cs rename to src/Avalonia.Base/Input/IFocusManager.cs diff --git a/src/Avalonia.Input/IFocusScope.cs b/src/Avalonia.Base/Input/IFocusScope.cs similarity index 100% rename from src/Avalonia.Input/IFocusScope.cs rename to src/Avalonia.Base/Input/IFocusScope.cs diff --git a/src/Avalonia.Input/IInputDevice.cs b/src/Avalonia.Base/Input/IInputDevice.cs similarity index 100% rename from src/Avalonia.Input/IInputDevice.cs rename to src/Avalonia.Base/Input/IInputDevice.cs diff --git a/src/Avalonia.Input/IInputElement.cs b/src/Avalonia.Base/Input/IInputElement.cs similarity index 100% rename from src/Avalonia.Input/IInputElement.cs rename to src/Avalonia.Base/Input/IInputElement.cs diff --git a/src/Avalonia.Input/IInputManager.cs b/src/Avalonia.Base/Input/IInputManager.cs similarity index 100% rename from src/Avalonia.Input/IInputManager.cs rename to src/Avalonia.Base/Input/IInputManager.cs diff --git a/src/Avalonia.Input/IInputRoot.cs b/src/Avalonia.Base/Input/IInputRoot.cs similarity index 100% rename from src/Avalonia.Input/IInputRoot.cs rename to src/Avalonia.Base/Input/IInputRoot.cs diff --git a/src/Avalonia.Input/IKeyboardDevice.cs b/src/Avalonia.Base/Input/IKeyboardDevice.cs similarity index 100% rename from src/Avalonia.Input/IKeyboardDevice.cs rename to src/Avalonia.Base/Input/IKeyboardDevice.cs diff --git a/src/Avalonia.Input/IKeyboardNavigationHandler.cs b/src/Avalonia.Base/Input/IKeyboardNavigationHandler.cs similarity index 100% rename from src/Avalonia.Input/IKeyboardNavigationHandler.cs rename to src/Avalonia.Base/Input/IKeyboardNavigationHandler.cs diff --git a/src/Avalonia.Input/IMainMenu.cs b/src/Avalonia.Base/Input/IMainMenu.cs similarity index 100% rename from src/Avalonia.Input/IMainMenu.cs rename to src/Avalonia.Base/Input/IMainMenu.cs diff --git a/src/Avalonia.Input/IMouseDevice.cs b/src/Avalonia.Base/Input/IMouseDevice.cs similarity index 100% rename from src/Avalonia.Input/IMouseDevice.cs rename to src/Avalonia.Base/Input/IMouseDevice.cs diff --git a/src/Avalonia.Input/INavigableContainer.cs b/src/Avalonia.Base/Input/INavigableContainer.cs similarity index 100% rename from src/Avalonia.Input/INavigableContainer.cs rename to src/Avalonia.Base/Input/INavigableContainer.cs diff --git a/src/Avalonia.Input/IPointer.cs b/src/Avalonia.Base/Input/IPointer.cs similarity index 100% rename from src/Avalonia.Input/IPointer.cs rename to src/Avalonia.Base/Input/IPointer.cs diff --git a/src/Avalonia.Input/IPointerDevice.cs b/src/Avalonia.Base/Input/IPointerDevice.cs similarity index 100% rename from src/Avalonia.Input/IPointerDevice.cs rename to src/Avalonia.Base/Input/IPointerDevice.cs diff --git a/src/Avalonia.Input/InputElement.cs b/src/Avalonia.Base/Input/InputElement.cs similarity index 100% rename from src/Avalonia.Input/InputElement.cs rename to src/Avalonia.Base/Input/InputElement.cs diff --git a/src/Avalonia.Input/InputExtensions.cs b/src/Avalonia.Base/Input/InputExtensions.cs similarity index 100% rename from src/Avalonia.Input/InputExtensions.cs rename to src/Avalonia.Base/Input/InputExtensions.cs diff --git a/src/Avalonia.Input/InputManager.cs b/src/Avalonia.Base/Input/InputManager.cs similarity index 100% rename from src/Avalonia.Input/InputManager.cs rename to src/Avalonia.Base/Input/InputManager.cs diff --git a/src/Avalonia.Input/InputMethod.cs b/src/Avalonia.Base/Input/InputMethod.cs similarity index 100% rename from src/Avalonia.Input/InputMethod.cs rename to src/Avalonia.Base/Input/InputMethod.cs diff --git a/src/Avalonia.Input/Key.cs b/src/Avalonia.Base/Input/Key.cs similarity index 100% rename from src/Avalonia.Input/Key.cs rename to src/Avalonia.Base/Input/Key.cs diff --git a/src/Avalonia.Input/KeyBinding.cs b/src/Avalonia.Base/Input/KeyBinding.cs similarity index 100% rename from src/Avalonia.Input/KeyBinding.cs rename to src/Avalonia.Base/Input/KeyBinding.cs diff --git a/src/Avalonia.Input/KeyEventArgs.cs b/src/Avalonia.Base/Input/KeyEventArgs.cs similarity index 100% rename from src/Avalonia.Input/KeyEventArgs.cs rename to src/Avalonia.Base/Input/KeyEventArgs.cs diff --git a/src/Avalonia.Input/KeyGesture.cs b/src/Avalonia.Base/Input/KeyGesture.cs similarity index 100% rename from src/Avalonia.Input/KeyGesture.cs rename to src/Avalonia.Base/Input/KeyGesture.cs diff --git a/src/Avalonia.Input/KeyboardDevice.cs b/src/Avalonia.Base/Input/KeyboardDevice.cs similarity index 100% rename from src/Avalonia.Input/KeyboardDevice.cs rename to src/Avalonia.Base/Input/KeyboardDevice.cs diff --git a/src/Avalonia.Input/KeyboardNavigation.cs b/src/Avalonia.Base/Input/KeyboardNavigation.cs similarity index 100% rename from src/Avalonia.Input/KeyboardNavigation.cs rename to src/Avalonia.Base/Input/KeyboardNavigation.cs diff --git a/src/Avalonia.Input/KeyboardNavigationHandler.cs b/src/Avalonia.Base/Input/KeyboardNavigationHandler.cs similarity index 100% rename from src/Avalonia.Input/KeyboardNavigationHandler.cs rename to src/Avalonia.Base/Input/KeyboardNavigationHandler.cs diff --git a/src/Avalonia.Input/KeyboardNavigationMode.cs b/src/Avalonia.Base/Input/KeyboardNavigationMode.cs similarity index 100% rename from src/Avalonia.Input/KeyboardNavigationMode.cs rename to src/Avalonia.Base/Input/KeyboardNavigationMode.cs diff --git a/src/Avalonia.Input/MouseDevice.cs b/src/Avalonia.Base/Input/MouseDevice.cs similarity index 100% rename from src/Avalonia.Input/MouseDevice.cs rename to src/Avalonia.Base/Input/MouseDevice.cs diff --git a/src/Avalonia.Input/Navigation/FocusExtensions.cs b/src/Avalonia.Base/Input/Navigation/FocusExtensions.cs similarity index 100% rename from src/Avalonia.Input/Navigation/FocusExtensions.cs rename to src/Avalonia.Base/Input/Navigation/FocusExtensions.cs diff --git a/src/Avalonia.Input/Navigation/TabNavigation.cs b/src/Avalonia.Base/Input/Navigation/TabNavigation.cs similarity index 100% rename from src/Avalonia.Input/Navigation/TabNavigation.cs rename to src/Avalonia.Base/Input/Navigation/TabNavigation.cs diff --git a/src/Avalonia.Input/NavigationDirection.cs b/src/Avalonia.Base/Input/NavigationDirection.cs similarity index 100% rename from src/Avalonia.Input/NavigationDirection.cs rename to src/Avalonia.Base/Input/NavigationDirection.cs diff --git a/src/Avalonia.Input/NavigationMethod.cs b/src/Avalonia.Base/Input/NavigationMethod.cs similarity index 100% rename from src/Avalonia.Input/NavigationMethod.cs rename to src/Avalonia.Base/Input/NavigationMethod.cs diff --git a/src/Avalonia.Input/Platform/IClipboard.cs b/src/Avalonia.Base/Input/Platform/IClipboard.cs similarity index 100% rename from src/Avalonia.Input/Platform/IClipboard.cs rename to src/Avalonia.Base/Input/Platform/IClipboard.cs diff --git a/src/Avalonia.Input/Platform/IPlatformDragSource.cs b/src/Avalonia.Base/Input/Platform/IPlatformDragSource.cs similarity index 100% rename from src/Avalonia.Input/Platform/IPlatformDragSource.cs rename to src/Avalonia.Base/Input/Platform/IPlatformDragSource.cs diff --git a/src/Avalonia.Input/Platform/PlatformHotkeyConfiguration.cs b/src/Avalonia.Base/Input/Platform/PlatformHotkeyConfiguration.cs similarity index 100% rename from src/Avalonia.Input/Platform/PlatformHotkeyConfiguration.cs rename to src/Avalonia.Base/Input/Platform/PlatformHotkeyConfiguration.cs diff --git a/src/Avalonia.Input/Pointer.cs b/src/Avalonia.Base/Input/Pointer.cs similarity index 100% rename from src/Avalonia.Input/Pointer.cs rename to src/Avalonia.Base/Input/Pointer.cs diff --git a/src/Avalonia.Input/PointerDeltaEventArgs.cs b/src/Avalonia.Base/Input/PointerDeltaEventArgs.cs similarity index 100% rename from src/Avalonia.Input/PointerDeltaEventArgs.cs rename to src/Avalonia.Base/Input/PointerDeltaEventArgs.cs diff --git a/src/Avalonia.Input/PointerEventArgs.cs b/src/Avalonia.Base/Input/PointerEventArgs.cs similarity index 100% rename from src/Avalonia.Input/PointerEventArgs.cs rename to src/Avalonia.Base/Input/PointerEventArgs.cs diff --git a/src/Avalonia.Input/PointerPoint.cs b/src/Avalonia.Base/Input/PointerPoint.cs similarity index 100% rename from src/Avalonia.Input/PointerPoint.cs rename to src/Avalonia.Base/Input/PointerPoint.cs diff --git a/src/Avalonia.Input/PointerWheelEventArgs.cs b/src/Avalonia.Base/Input/PointerWheelEventArgs.cs similarity index 100% rename from src/Avalonia.Input/PointerWheelEventArgs.cs rename to src/Avalonia.Base/Input/PointerWheelEventArgs.cs diff --git a/src/Avalonia.Input/Raw/IDragDropDevice.cs b/src/Avalonia.Base/Input/Raw/IDragDropDevice.cs similarity index 100% rename from src/Avalonia.Input/Raw/IDragDropDevice.cs rename to src/Avalonia.Base/Input/Raw/IDragDropDevice.cs diff --git a/src/Avalonia.Input/Raw/RawDragEvent.cs b/src/Avalonia.Base/Input/Raw/RawDragEvent.cs similarity index 100% rename from src/Avalonia.Input/Raw/RawDragEvent.cs rename to src/Avalonia.Base/Input/Raw/RawDragEvent.cs diff --git a/src/Avalonia.Input/Raw/RawDragEventType.cs b/src/Avalonia.Base/Input/Raw/RawDragEventType.cs similarity index 100% rename from src/Avalonia.Input/Raw/RawDragEventType.cs rename to src/Avalonia.Base/Input/Raw/RawDragEventType.cs diff --git a/src/Avalonia.Input/Raw/RawInputEventArgs.cs b/src/Avalonia.Base/Input/Raw/RawInputEventArgs.cs similarity index 100% rename from src/Avalonia.Input/Raw/RawInputEventArgs.cs rename to src/Avalonia.Base/Input/Raw/RawInputEventArgs.cs diff --git a/src/Avalonia.Input/Raw/RawKeyEventArgs.cs b/src/Avalonia.Base/Input/Raw/RawKeyEventArgs.cs similarity index 100% rename from src/Avalonia.Input/Raw/RawKeyEventArgs.cs rename to src/Avalonia.Base/Input/Raw/RawKeyEventArgs.cs diff --git a/src/Avalonia.Input/Raw/RawMouseWheelEventArgs.cs b/src/Avalonia.Base/Input/Raw/RawMouseWheelEventArgs.cs similarity index 100% rename from src/Avalonia.Input/Raw/RawMouseWheelEventArgs.cs rename to src/Avalonia.Base/Input/Raw/RawMouseWheelEventArgs.cs diff --git a/src/Avalonia.Input/Raw/RawPointerEventArgs.cs b/src/Avalonia.Base/Input/Raw/RawPointerEventArgs.cs similarity index 100% rename from src/Avalonia.Input/Raw/RawPointerEventArgs.cs rename to src/Avalonia.Base/Input/Raw/RawPointerEventArgs.cs diff --git a/src/Avalonia.Input/Raw/RawPointerGestureEventArgs.cs b/src/Avalonia.Base/Input/Raw/RawPointerGestureEventArgs.cs similarity index 100% rename from src/Avalonia.Input/Raw/RawPointerGestureEventArgs.cs rename to src/Avalonia.Base/Input/Raw/RawPointerGestureEventArgs.cs diff --git a/src/Avalonia.Input/Raw/RawSizeEventArgs.cs b/src/Avalonia.Base/Input/Raw/RawSizeEventArgs.cs similarity index 100% rename from src/Avalonia.Input/Raw/RawSizeEventArgs.cs rename to src/Avalonia.Base/Input/Raw/RawSizeEventArgs.cs diff --git a/src/Avalonia.Input/Raw/RawTextInputEventArgs.cs b/src/Avalonia.Base/Input/Raw/RawTextInputEventArgs.cs similarity index 100% rename from src/Avalonia.Input/Raw/RawTextInputEventArgs.cs rename to src/Avalonia.Base/Input/Raw/RawTextInputEventArgs.cs diff --git a/src/Avalonia.Input/Raw/RawTouchEventArgs.cs b/src/Avalonia.Base/Input/Raw/RawTouchEventArgs.cs similarity index 100% rename from src/Avalonia.Input/Raw/RawTouchEventArgs.cs rename to src/Avalonia.Base/Input/Raw/RawTouchEventArgs.cs diff --git a/src/Avalonia.Input/ScrollGestureEventArgs.cs b/src/Avalonia.Base/Input/ScrollGestureEventArgs.cs similarity index 100% rename from src/Avalonia.Input/ScrollGestureEventArgs.cs rename to src/Avalonia.Base/Input/ScrollGestureEventArgs.cs diff --git a/src/Avalonia.Input/TappedEventArgs.cs b/src/Avalonia.Base/Input/TappedEventArgs.cs similarity index 100% rename from src/Avalonia.Input/TappedEventArgs.cs rename to src/Avalonia.Base/Input/TappedEventArgs.cs diff --git a/src/Avalonia.Input/TextInput/ITextInputMethodClient.cs b/src/Avalonia.Base/Input/TextInput/ITextInputMethodClient.cs similarity index 100% rename from src/Avalonia.Input/TextInput/ITextInputMethodClient.cs rename to src/Avalonia.Base/Input/TextInput/ITextInputMethodClient.cs diff --git a/src/Avalonia.Input/TextInput/ITextInputMethodImpl.cs b/src/Avalonia.Base/Input/TextInput/ITextInputMethodImpl.cs similarity index 100% rename from src/Avalonia.Input/TextInput/ITextInputMethodImpl.cs rename to src/Avalonia.Base/Input/TextInput/ITextInputMethodImpl.cs diff --git a/src/Avalonia.Input/TextInput/InputMethodManager.cs b/src/Avalonia.Base/Input/TextInput/InputMethodManager.cs similarity index 100% rename from src/Avalonia.Input/TextInput/InputMethodManager.cs rename to src/Avalonia.Base/Input/TextInput/InputMethodManager.cs diff --git a/src/Avalonia.Input/TextInput/TextInputContentType.cs b/src/Avalonia.Base/Input/TextInput/TextInputContentType.cs similarity index 100% rename from src/Avalonia.Input/TextInput/TextInputContentType.cs rename to src/Avalonia.Base/Input/TextInput/TextInputContentType.cs diff --git a/src/Avalonia.Input/TextInput/TextInputMethodClientRequestedEventArgs.cs b/src/Avalonia.Base/Input/TextInput/TextInputMethodClientRequestedEventArgs.cs similarity index 100% rename from src/Avalonia.Input/TextInput/TextInputMethodClientRequestedEventArgs.cs rename to src/Avalonia.Base/Input/TextInput/TextInputMethodClientRequestedEventArgs.cs diff --git a/src/Avalonia.Input/TextInput/TextInputOptions.cs b/src/Avalonia.Base/Input/TextInput/TextInputOptions.cs similarity index 100% rename from src/Avalonia.Input/TextInput/TextInputOptions.cs rename to src/Avalonia.Base/Input/TextInput/TextInputOptions.cs diff --git a/src/Avalonia.Input/TextInput/TransformTrackingHelper.cs b/src/Avalonia.Base/Input/TextInput/TransformTrackingHelper.cs similarity index 100% rename from src/Avalonia.Input/TextInput/TransformTrackingHelper.cs rename to src/Avalonia.Base/Input/TextInput/TransformTrackingHelper.cs diff --git a/src/Avalonia.Input/TextInputEventArgs.cs b/src/Avalonia.Base/Input/TextInputEventArgs.cs similarity index 100% rename from src/Avalonia.Input/TextInputEventArgs.cs rename to src/Avalonia.Base/Input/TextInputEventArgs.cs diff --git a/src/Avalonia.Input/TouchDevice.cs b/src/Avalonia.Base/Input/TouchDevice.cs similarity index 100% rename from src/Avalonia.Input/TouchDevice.cs rename to src/Avalonia.Base/Input/TouchDevice.cs diff --git a/src/Avalonia.Input/VectorEventArgs.cs b/src/Avalonia.Base/Input/VectorEventArgs.cs similarity index 100% rename from src/Avalonia.Input/VectorEventArgs.cs rename to src/Avalonia.Base/Input/VectorEventArgs.cs diff --git a/src/Avalonia.Interactivity/EventRoute.cs b/src/Avalonia.Base/Interactivity/EventRoute.cs similarity index 100% rename from src/Avalonia.Interactivity/EventRoute.cs rename to src/Avalonia.Base/Interactivity/EventRoute.cs diff --git a/src/Avalonia.Interactivity/IInteractive.cs b/src/Avalonia.Base/Interactivity/IInteractive.cs similarity index 100% rename from src/Avalonia.Interactivity/IInteractive.cs rename to src/Avalonia.Base/Interactivity/IInteractive.cs diff --git a/src/Avalonia.Interactivity/Interactive.cs b/src/Avalonia.Base/Interactivity/Interactive.cs similarity index 100% rename from src/Avalonia.Interactivity/Interactive.cs rename to src/Avalonia.Base/Interactivity/Interactive.cs diff --git a/src/Avalonia.Interactivity/InteractiveExtensions.cs b/src/Avalonia.Base/Interactivity/InteractiveExtensions.cs similarity index 100% rename from src/Avalonia.Interactivity/InteractiveExtensions.cs rename to src/Avalonia.Base/Interactivity/InteractiveExtensions.cs diff --git a/src/Avalonia.Interactivity/RoutedEvent.cs b/src/Avalonia.Base/Interactivity/RoutedEvent.cs similarity index 100% rename from src/Avalonia.Interactivity/RoutedEvent.cs rename to src/Avalonia.Base/Interactivity/RoutedEvent.cs diff --git a/src/Avalonia.Interactivity/RoutedEventArgs.cs b/src/Avalonia.Base/Interactivity/RoutedEventArgs.cs similarity index 100% rename from src/Avalonia.Interactivity/RoutedEventArgs.cs rename to src/Avalonia.Base/Interactivity/RoutedEventArgs.cs diff --git a/src/Avalonia.Interactivity/RoutedEventRegistry.cs b/src/Avalonia.Base/Interactivity/RoutedEventRegistry.cs similarity index 100% rename from src/Avalonia.Interactivity/RoutedEventRegistry.cs rename to src/Avalonia.Base/Interactivity/RoutedEventRegistry.cs diff --git a/src/Avalonia.Layout/AttachedLayout.cs b/src/Avalonia.Base/Layout/AttachedLayout.cs similarity index 100% rename from src/Avalonia.Layout/AttachedLayout.cs rename to src/Avalonia.Base/Layout/AttachedLayout.cs diff --git a/src/Avalonia.Layout/EffectiveViewportChangedEventArgs.cs b/src/Avalonia.Base/Layout/EffectiveViewportChangedEventArgs.cs similarity index 100% rename from src/Avalonia.Layout/EffectiveViewportChangedEventArgs.cs rename to src/Avalonia.Base/Layout/EffectiveViewportChangedEventArgs.cs diff --git a/src/Avalonia.Layout/ElementManager.cs b/src/Avalonia.Base/Layout/ElementManager.cs similarity index 100% rename from src/Avalonia.Layout/ElementManager.cs rename to src/Avalonia.Base/Layout/ElementManager.cs diff --git a/src/Avalonia.Layout/FlowLayoutAlgorithm.cs b/src/Avalonia.Base/Layout/FlowLayoutAlgorithm.cs similarity index 100% rename from src/Avalonia.Layout/FlowLayoutAlgorithm.cs rename to src/Avalonia.Base/Layout/FlowLayoutAlgorithm.cs diff --git a/src/Avalonia.Layout/IEmbeddedLayoutRoot.cs b/src/Avalonia.Base/Layout/IEmbeddedLayoutRoot.cs similarity index 100% rename from src/Avalonia.Layout/IEmbeddedLayoutRoot.cs rename to src/Avalonia.Base/Layout/IEmbeddedLayoutRoot.cs diff --git a/src/Avalonia.Layout/IFlowLayoutAlgorithmDelegates.cs b/src/Avalonia.Base/Layout/IFlowLayoutAlgorithmDelegates.cs similarity index 100% rename from src/Avalonia.Layout/IFlowLayoutAlgorithmDelegates.cs rename to src/Avalonia.Base/Layout/IFlowLayoutAlgorithmDelegates.cs diff --git a/src/Avalonia.Layout/ILayoutManager.cs b/src/Avalonia.Base/Layout/ILayoutManager.cs similarity index 100% rename from src/Avalonia.Layout/ILayoutManager.cs rename to src/Avalonia.Base/Layout/ILayoutManager.cs diff --git a/src/Avalonia.Layout/ILayoutRoot.cs b/src/Avalonia.Base/Layout/ILayoutRoot.cs similarity index 100% rename from src/Avalonia.Layout/ILayoutRoot.cs rename to src/Avalonia.Base/Layout/ILayoutRoot.cs diff --git a/src/Avalonia.Layout/ILayoutable.cs b/src/Avalonia.Base/Layout/ILayoutable.cs similarity index 100% rename from src/Avalonia.Layout/ILayoutable.cs rename to src/Avalonia.Base/Layout/ILayoutable.cs diff --git a/src/Avalonia.Layout/LayoutContext.cs b/src/Avalonia.Base/Layout/LayoutContext.cs similarity index 100% rename from src/Avalonia.Layout/LayoutContext.cs rename to src/Avalonia.Base/Layout/LayoutContext.cs diff --git a/src/Avalonia.Layout/LayoutContextAdapter.cs b/src/Avalonia.Base/Layout/LayoutContextAdapter.cs similarity index 100% rename from src/Avalonia.Layout/LayoutContextAdapter.cs rename to src/Avalonia.Base/Layout/LayoutContextAdapter.cs diff --git a/src/Avalonia.Layout/LayoutExtensions.cs b/src/Avalonia.Base/Layout/LayoutExtensions.cs similarity index 100% rename from src/Avalonia.Layout/LayoutExtensions.cs rename to src/Avalonia.Base/Layout/LayoutExtensions.cs diff --git a/src/Avalonia.Layout/LayoutHelper.cs b/src/Avalonia.Base/Layout/LayoutHelper.cs similarity index 100% rename from src/Avalonia.Layout/LayoutHelper.cs rename to src/Avalonia.Base/Layout/LayoutHelper.cs diff --git a/src/Avalonia.Layout/LayoutManager.cs b/src/Avalonia.Base/Layout/LayoutManager.cs similarity index 100% rename from src/Avalonia.Layout/LayoutManager.cs rename to src/Avalonia.Base/Layout/LayoutManager.cs diff --git a/src/Avalonia.Layout/LayoutQueue.cs b/src/Avalonia.Base/Layout/LayoutQueue.cs similarity index 100% rename from src/Avalonia.Layout/LayoutQueue.cs rename to src/Avalonia.Base/Layout/LayoutQueue.cs diff --git a/src/Avalonia.Layout/Layoutable.cs b/src/Avalonia.Base/Layout/Layoutable.cs similarity index 100% rename from src/Avalonia.Layout/Layoutable.cs rename to src/Avalonia.Base/Layout/Layoutable.cs diff --git a/src/Avalonia.Layout/NonVirtualizingLayout.cs b/src/Avalonia.Base/Layout/NonVirtualizingLayout.cs similarity index 100% rename from src/Avalonia.Layout/NonVirtualizingLayout.cs rename to src/Avalonia.Base/Layout/NonVirtualizingLayout.cs diff --git a/src/Avalonia.Layout/NonVirtualizingLayoutContext.cs b/src/Avalonia.Base/Layout/NonVirtualizingLayoutContext.cs similarity index 100% rename from src/Avalonia.Layout/NonVirtualizingLayoutContext.cs rename to src/Avalonia.Base/Layout/NonVirtualizingLayoutContext.cs diff --git a/src/Avalonia.Layout/NonVirtualizingStackLayout.cs b/src/Avalonia.Base/Layout/NonVirtualizingStackLayout.cs similarity index 100% rename from src/Avalonia.Layout/NonVirtualizingStackLayout.cs rename to src/Avalonia.Base/Layout/NonVirtualizingStackLayout.cs diff --git a/src/Avalonia.Layout/Orientation.cs b/src/Avalonia.Base/Layout/Orientation.cs similarity index 100% rename from src/Avalonia.Layout/Orientation.cs rename to src/Avalonia.Base/Layout/Orientation.cs diff --git a/src/Avalonia.Layout/OrientationBasedMeasures.cs b/src/Avalonia.Base/Layout/OrientationBasedMeasures.cs similarity index 100% rename from src/Avalonia.Layout/OrientationBasedMeasures.cs rename to src/Avalonia.Base/Layout/OrientationBasedMeasures.cs diff --git a/src/Avalonia.Layout/StackLayout.cs b/src/Avalonia.Base/Layout/StackLayout.cs similarity index 100% rename from src/Avalonia.Layout/StackLayout.cs rename to src/Avalonia.Base/Layout/StackLayout.cs diff --git a/src/Avalonia.Layout/StackLayoutState.cs b/src/Avalonia.Base/Layout/StackLayoutState.cs similarity index 100% rename from src/Avalonia.Layout/StackLayoutState.cs rename to src/Avalonia.Base/Layout/StackLayoutState.cs diff --git a/src/Avalonia.Layout/UniformGridLayout.cs b/src/Avalonia.Base/Layout/UniformGridLayout.cs similarity index 100% rename from src/Avalonia.Layout/UniformGridLayout.cs rename to src/Avalonia.Base/Layout/UniformGridLayout.cs diff --git a/src/Avalonia.Layout/UniformGridLayoutState.cs b/src/Avalonia.Base/Layout/UniformGridLayoutState.cs similarity index 100% rename from src/Avalonia.Layout/UniformGridLayoutState.cs rename to src/Avalonia.Base/Layout/UniformGridLayoutState.cs diff --git a/src/Avalonia.Layout/Utils/ListUtils.cs b/src/Avalonia.Base/Layout/Utils/ListUtils.cs similarity index 100% rename from src/Avalonia.Layout/Utils/ListUtils.cs rename to src/Avalonia.Base/Layout/Utils/ListUtils.cs diff --git a/src/Avalonia.Layout/VirtualLayoutContextAdapter.cs b/src/Avalonia.Base/Layout/VirtualLayoutContextAdapter.cs similarity index 100% rename from src/Avalonia.Layout/VirtualLayoutContextAdapter.cs rename to src/Avalonia.Base/Layout/VirtualLayoutContextAdapter.cs diff --git a/src/Avalonia.Layout/VirtualizingLayout.cs b/src/Avalonia.Base/Layout/VirtualizingLayout.cs similarity index 100% rename from src/Avalonia.Layout/VirtualizingLayout.cs rename to src/Avalonia.Base/Layout/VirtualizingLayout.cs diff --git a/src/Avalonia.Layout/VirtualizingLayoutContext.cs b/src/Avalonia.Base/Layout/VirtualizingLayoutContext.cs similarity index 100% rename from src/Avalonia.Layout/VirtualizingLayoutContext.cs rename to src/Avalonia.Base/Layout/VirtualizingLayoutContext.cs diff --git a/src/Avalonia.Layout/WrapLayout/UvBounds.cs b/src/Avalonia.Base/Layout/WrapLayout/UvBounds.cs similarity index 100% rename from src/Avalonia.Layout/WrapLayout/UvBounds.cs rename to src/Avalonia.Base/Layout/WrapLayout/UvBounds.cs diff --git a/src/Avalonia.Layout/WrapLayout/UvMeasure.cs b/src/Avalonia.Base/Layout/WrapLayout/UvMeasure.cs similarity index 100% rename from src/Avalonia.Layout/WrapLayout/UvMeasure.cs rename to src/Avalonia.Base/Layout/WrapLayout/UvMeasure.cs diff --git a/src/Avalonia.Layout/WrapLayout/WrapItem.cs b/src/Avalonia.Base/Layout/WrapLayout/WrapItem.cs similarity index 100% rename from src/Avalonia.Layout/WrapLayout/WrapItem.cs rename to src/Avalonia.Base/Layout/WrapLayout/WrapItem.cs diff --git a/src/Avalonia.Layout/WrapLayout/WrapLayout.cs b/src/Avalonia.Base/Layout/WrapLayout/WrapLayout.cs similarity index 100% rename from src/Avalonia.Layout/WrapLayout/WrapLayout.cs rename to src/Avalonia.Base/Layout/WrapLayout/WrapLayout.cs diff --git a/src/Avalonia.Layout/WrapLayout/WrapLayoutState.cs b/src/Avalonia.Base/Layout/WrapLayout/WrapLayoutState.cs similarity index 100% rename from src/Avalonia.Layout/WrapLayout/WrapLayoutState.cs rename to src/Avalonia.Base/Layout/WrapLayout/WrapLayoutState.cs diff --git a/src/Avalonia.Styling/LogicalTree/ChildIndexChangedEventArgs.cs b/src/Avalonia.Base/LogicalTree/ChildIndexChangedEventArgs.cs similarity index 100% rename from src/Avalonia.Styling/LogicalTree/ChildIndexChangedEventArgs.cs rename to src/Avalonia.Base/LogicalTree/ChildIndexChangedEventArgs.cs diff --git a/src/Avalonia.Styling/LogicalTree/ControlLocator.cs b/src/Avalonia.Base/LogicalTree/ControlLocator.cs similarity index 100% rename from src/Avalonia.Styling/LogicalTree/ControlLocator.cs rename to src/Avalonia.Base/LogicalTree/ControlLocator.cs diff --git a/src/Avalonia.Styling/LogicalTree/IChildIndexProvider.cs b/src/Avalonia.Base/LogicalTree/IChildIndexProvider.cs similarity index 100% rename from src/Avalonia.Styling/LogicalTree/IChildIndexProvider.cs rename to src/Avalonia.Base/LogicalTree/IChildIndexProvider.cs diff --git a/src/Avalonia.Styling/LogicalTree/ILogical.cs b/src/Avalonia.Base/LogicalTree/ILogical.cs similarity index 100% rename from src/Avalonia.Styling/LogicalTree/ILogical.cs rename to src/Avalonia.Base/LogicalTree/ILogical.cs diff --git a/src/Avalonia.Styling/LogicalTree/ILogicalRoot.cs b/src/Avalonia.Base/LogicalTree/ILogicalRoot.cs similarity index 100% rename from src/Avalonia.Styling/LogicalTree/ILogicalRoot.cs rename to src/Avalonia.Base/LogicalTree/ILogicalRoot.cs diff --git a/src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs b/src/Avalonia.Base/LogicalTree/LogicalExtensions.cs similarity index 100% rename from src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs rename to src/Avalonia.Base/LogicalTree/LogicalExtensions.cs diff --git a/src/Avalonia.Styling/LogicalTree/LogicalTreeAttachmentEventArgs.cs b/src/Avalonia.Base/LogicalTree/LogicalTreeAttachmentEventArgs.cs similarity index 100% rename from src/Avalonia.Styling/LogicalTree/LogicalTreeAttachmentEventArgs.cs rename to src/Avalonia.Base/LogicalTree/LogicalTreeAttachmentEventArgs.cs diff --git a/src/Avalonia.Visuals/Matrix.cs b/src/Avalonia.Base/Matrix.cs similarity index 100% rename from src/Avalonia.Visuals/Matrix.cs rename to src/Avalonia.Base/Matrix.cs diff --git a/src/Avalonia.Visuals/Media/AcrylicBackgroundSource.cs b/src/Avalonia.Base/Media/AcrylicBackgroundSource.cs similarity index 100% rename from src/Avalonia.Visuals/Media/AcrylicBackgroundSource.cs rename to src/Avalonia.Base/Media/AcrylicBackgroundSource.cs diff --git a/src/Avalonia.Visuals/Media/AlignmentX.cs b/src/Avalonia.Base/Media/AlignmentX.cs similarity index 100% rename from src/Avalonia.Visuals/Media/AlignmentX.cs rename to src/Avalonia.Base/Media/AlignmentX.cs diff --git a/src/Avalonia.Visuals/Media/AlignmentY.cs b/src/Avalonia.Base/Media/AlignmentY.cs similarity index 100% rename from src/Avalonia.Visuals/Media/AlignmentY.cs rename to src/Avalonia.Base/Media/AlignmentY.cs diff --git a/src/Avalonia.Visuals/Media/ArcSegment.cs b/src/Avalonia.Base/Media/ArcSegment.cs similarity index 100% rename from src/Avalonia.Visuals/Media/ArcSegment.cs rename to src/Avalonia.Base/Media/ArcSegment.cs diff --git a/src/Avalonia.Visuals/Media/BaselineAlignment.cs b/src/Avalonia.Base/Media/BaselineAlignment.cs similarity index 100% rename from src/Avalonia.Visuals/Media/BaselineAlignment.cs rename to src/Avalonia.Base/Media/BaselineAlignment.cs diff --git a/src/Avalonia.Visuals/Media/BezierSegment .cs b/src/Avalonia.Base/Media/BezierSegment .cs similarity index 100% rename from src/Avalonia.Visuals/Media/BezierSegment .cs rename to src/Avalonia.Base/Media/BezierSegment .cs diff --git a/src/Avalonia.Visuals/Media/BoxShadow.cs b/src/Avalonia.Base/Media/BoxShadow.cs similarity index 100% rename from src/Avalonia.Visuals/Media/BoxShadow.cs rename to src/Avalonia.Base/Media/BoxShadow.cs diff --git a/src/Avalonia.Visuals/Media/BoxShadows.cs b/src/Avalonia.Base/Media/BoxShadows.cs similarity index 100% rename from src/Avalonia.Visuals/Media/BoxShadows.cs rename to src/Avalonia.Base/Media/BoxShadows.cs diff --git a/src/Avalonia.Visuals/Media/Brush.cs b/src/Avalonia.Base/Media/Brush.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Brush.cs rename to src/Avalonia.Base/Media/Brush.cs diff --git a/src/Avalonia.Visuals/Media/BrushConverter.cs b/src/Avalonia.Base/Media/BrushConverter.cs similarity index 100% rename from src/Avalonia.Visuals/Media/BrushConverter.cs rename to src/Avalonia.Base/Media/BrushConverter.cs diff --git a/src/Avalonia.Visuals/Media/BrushExtensions.cs b/src/Avalonia.Base/Media/BrushExtensions.cs similarity index 100% rename from src/Avalonia.Visuals/Media/BrushExtensions.cs rename to src/Avalonia.Base/Media/BrushExtensions.cs diff --git a/src/Avalonia.Visuals/Media/BrushMappingMode.cs b/src/Avalonia.Base/Media/BrushMappingMode.cs similarity index 100% rename from src/Avalonia.Visuals/Media/BrushMappingMode.cs rename to src/Avalonia.Base/Media/BrushMappingMode.cs diff --git a/src/Avalonia.Visuals/Media/Brushes.cs b/src/Avalonia.Base/Media/Brushes.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Brushes.cs rename to src/Avalonia.Base/Media/Brushes.cs diff --git a/src/Avalonia.Visuals/Media/CharacterHit.cs b/src/Avalonia.Base/Media/CharacterHit.cs similarity index 100% rename from src/Avalonia.Visuals/Media/CharacterHit.cs rename to src/Avalonia.Base/Media/CharacterHit.cs diff --git a/src/Avalonia.Visuals/Media/Color.cs b/src/Avalonia.Base/Media/Color.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Color.cs rename to src/Avalonia.Base/Media/Color.cs diff --git a/src/Avalonia.Visuals/Media/Colors.cs b/src/Avalonia.Base/Media/Colors.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Colors.cs rename to src/Avalonia.Base/Media/Colors.cs diff --git a/src/Avalonia.Visuals/Media/ConicGradientBrush.cs b/src/Avalonia.Base/Media/ConicGradientBrush.cs similarity index 100% rename from src/Avalonia.Visuals/Media/ConicGradientBrush.cs rename to src/Avalonia.Base/Media/ConicGradientBrush.cs diff --git a/src/Avalonia.Visuals/Media/DashStyle.cs b/src/Avalonia.Base/Media/DashStyle.cs similarity index 100% rename from src/Avalonia.Visuals/Media/DashStyle.cs rename to src/Avalonia.Base/Media/DashStyle.cs diff --git a/src/Avalonia.Visuals/Media/Drawing.cs b/src/Avalonia.Base/Media/Drawing.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Drawing.cs rename to src/Avalonia.Base/Media/Drawing.cs diff --git a/src/Avalonia.Visuals/Media/DrawingContext.cs b/src/Avalonia.Base/Media/DrawingContext.cs similarity index 99% rename from src/Avalonia.Visuals/Media/DrawingContext.cs rename to src/Avalonia.Base/Media/DrawingContext.cs index d0a73ca9a8..d0f3b9e21a 100644 --- a/src/Avalonia.Visuals/Media/DrawingContext.cs +++ b/src/Avalonia.Base/Media/DrawingContext.cs @@ -4,7 +4,7 @@ using Avalonia.Platform; using Avalonia.Rendering.SceneGraph; using Avalonia.Threading; using Avalonia.Utilities; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; namespace Avalonia.Media { diff --git a/src/Avalonia.Visuals/Media/DrawingGroup.cs b/src/Avalonia.Base/Media/DrawingGroup.cs similarity index 100% rename from src/Avalonia.Visuals/Media/DrawingGroup.cs rename to src/Avalonia.Base/Media/DrawingGroup.cs diff --git a/src/Avalonia.Visuals/Media/DrawingImage.cs b/src/Avalonia.Base/Media/DrawingImage.cs similarity index 96% rename from src/Avalonia.Visuals/Media/DrawingImage.cs rename to src/Avalonia.Base/Media/DrawingImage.cs index 488822b693..1663e384b5 100644 --- a/src/Avalonia.Visuals/Media/DrawingImage.cs +++ b/src/Avalonia.Base/Media/DrawingImage.cs @@ -1,8 +1,6 @@ using System; -using Avalonia.Data; using Avalonia.Metadata; -using Avalonia.Platform; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; namespace Avalonia.Media { diff --git a/src/Avalonia.Visuals/Media/EllipseGeometry.cs b/src/Avalonia.Base/Media/EllipseGeometry.cs similarity index 100% rename from src/Avalonia.Visuals/Media/EllipseGeometry.cs rename to src/Avalonia.Base/Media/EllipseGeometry.cs diff --git a/src/Avalonia.Visuals/Media/ExperimentalAcrylicMaterial.cs b/src/Avalonia.Base/Media/ExperimentalAcrylicMaterial.cs similarity index 100% rename from src/Avalonia.Visuals/Media/ExperimentalAcrylicMaterial.cs rename to src/Avalonia.Base/Media/ExperimentalAcrylicMaterial.cs diff --git a/src/Avalonia.Visuals/Media/FillRule.cs b/src/Avalonia.Base/Media/FillRule.cs similarity index 100% rename from src/Avalonia.Visuals/Media/FillRule.cs rename to src/Avalonia.Base/Media/FillRule.cs diff --git a/src/Avalonia.Visuals/Media/FlowDirection.cs b/src/Avalonia.Base/Media/FlowDirection.cs similarity index 100% rename from src/Avalonia.Visuals/Media/FlowDirection.cs rename to src/Avalonia.Base/Media/FlowDirection.cs diff --git a/src/Avalonia.Visuals/Media/FontFallback.cs b/src/Avalonia.Base/Media/FontFallback.cs similarity index 100% rename from src/Avalonia.Visuals/Media/FontFallback.cs rename to src/Avalonia.Base/Media/FontFallback.cs diff --git a/src/Avalonia.Visuals/Media/FontFamily.cs b/src/Avalonia.Base/Media/FontFamily.cs similarity index 100% rename from src/Avalonia.Visuals/Media/FontFamily.cs rename to src/Avalonia.Base/Media/FontFamily.cs diff --git a/src/Avalonia.Visuals/Media/FontManager.cs b/src/Avalonia.Base/Media/FontManager.cs similarity index 100% rename from src/Avalonia.Visuals/Media/FontManager.cs rename to src/Avalonia.Base/Media/FontManager.cs diff --git a/src/Avalonia.Visuals/Media/FontManagerOptions.cs b/src/Avalonia.Base/Media/FontManagerOptions.cs similarity index 100% rename from src/Avalonia.Visuals/Media/FontManagerOptions.cs rename to src/Avalonia.Base/Media/FontManagerOptions.cs diff --git a/src/Avalonia.Visuals/Media/FontStretch.cs b/src/Avalonia.Base/Media/FontStretch.cs similarity index 100% rename from src/Avalonia.Visuals/Media/FontStretch.cs rename to src/Avalonia.Base/Media/FontStretch.cs diff --git a/src/Avalonia.Visuals/Media/FontStyle.cs b/src/Avalonia.Base/Media/FontStyle.cs similarity index 100% rename from src/Avalonia.Visuals/Media/FontStyle.cs rename to src/Avalonia.Base/Media/FontStyle.cs diff --git a/src/Avalonia.Visuals/Media/FontWeight.cs b/src/Avalonia.Base/Media/FontWeight.cs similarity index 100% rename from src/Avalonia.Visuals/Media/FontWeight.cs rename to src/Avalonia.Base/Media/FontWeight.cs diff --git a/src/Avalonia.Visuals/Media/Fonts/FamilyNameCollection.cs b/src/Avalonia.Base/Media/Fonts/FamilyNameCollection.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Fonts/FamilyNameCollection.cs rename to src/Avalonia.Base/Media/Fonts/FamilyNameCollection.cs diff --git a/src/Avalonia.Visuals/Media/Fonts/FontFamilyKey.cs b/src/Avalonia.Base/Media/Fonts/FontFamilyKey.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Fonts/FontFamilyKey.cs rename to src/Avalonia.Base/Media/Fonts/FontFamilyKey.cs diff --git a/src/Avalonia.Visuals/Media/Fonts/FontFamilyLoader.cs b/src/Avalonia.Base/Media/Fonts/FontFamilyLoader.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Fonts/FontFamilyLoader.cs rename to src/Avalonia.Base/Media/Fonts/FontFamilyLoader.cs diff --git a/src/Avalonia.Visuals/Media/FormattedText.cs b/src/Avalonia.Base/Media/FormattedText.cs similarity index 100% rename from src/Avalonia.Visuals/Media/FormattedText.cs rename to src/Avalonia.Base/Media/FormattedText.cs diff --git a/src/Avalonia.Visuals/Media/Geometry.cs b/src/Avalonia.Base/Media/Geometry.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Geometry.cs rename to src/Avalonia.Base/Media/Geometry.cs diff --git a/src/Avalonia.Visuals/Media/GeometryDrawing.cs b/src/Avalonia.Base/Media/GeometryDrawing.cs similarity index 100% rename from src/Avalonia.Visuals/Media/GeometryDrawing.cs rename to src/Avalonia.Base/Media/GeometryDrawing.cs diff --git a/src/Avalonia.Visuals/Media/GlyphRun.cs b/src/Avalonia.Base/Media/GlyphRun.cs similarity index 100% rename from src/Avalonia.Visuals/Media/GlyphRun.cs rename to src/Avalonia.Base/Media/GlyphRun.cs diff --git a/src/Avalonia.Visuals/Media/GlyphRunDrawing.cs b/src/Avalonia.Base/Media/GlyphRunDrawing.cs similarity index 100% rename from src/Avalonia.Visuals/Media/GlyphRunDrawing.cs rename to src/Avalonia.Base/Media/GlyphRunDrawing.cs diff --git a/src/Avalonia.Visuals/Media/GlyphRunMetrics.cs b/src/Avalonia.Base/Media/GlyphRunMetrics.cs similarity index 100% rename from src/Avalonia.Visuals/Media/GlyphRunMetrics.cs rename to src/Avalonia.Base/Media/GlyphRunMetrics.cs diff --git a/src/Avalonia.Visuals/Media/GlyphTypeface.cs b/src/Avalonia.Base/Media/GlyphTypeface.cs similarity index 100% rename from src/Avalonia.Visuals/Media/GlyphTypeface.cs rename to src/Avalonia.Base/Media/GlyphTypeface.cs diff --git a/src/Avalonia.Visuals/Media/GradientBrush.cs b/src/Avalonia.Base/Media/GradientBrush.cs similarity index 100% rename from src/Avalonia.Visuals/Media/GradientBrush.cs rename to src/Avalonia.Base/Media/GradientBrush.cs diff --git a/src/Avalonia.Visuals/Media/GradientSpreadMethod.cs b/src/Avalonia.Base/Media/GradientSpreadMethod.cs similarity index 100% rename from src/Avalonia.Visuals/Media/GradientSpreadMethod.cs rename to src/Avalonia.Base/Media/GradientSpreadMethod.cs diff --git a/src/Avalonia.Visuals/Media/GradientStop.cs b/src/Avalonia.Base/Media/GradientStop.cs similarity index 100% rename from src/Avalonia.Visuals/Media/GradientStop.cs rename to src/Avalonia.Base/Media/GradientStop.cs diff --git a/src/Avalonia.Visuals/Media/GradientStops.cs b/src/Avalonia.Base/Media/GradientStops.cs similarity index 100% rename from src/Avalonia.Visuals/Media/GradientStops.cs rename to src/Avalonia.Base/Media/GradientStops.cs diff --git a/src/Avalonia.Visuals/Media/HslColor.cs b/src/Avalonia.Base/Media/HslColor.cs similarity index 100% rename from src/Avalonia.Visuals/Media/HslColor.cs rename to src/Avalonia.Base/Media/HslColor.cs diff --git a/src/Avalonia.Visuals/Media/HsvColor.cs b/src/Avalonia.Base/Media/HsvColor.cs similarity index 100% rename from src/Avalonia.Visuals/Media/HsvColor.cs rename to src/Avalonia.Base/Media/HsvColor.cs diff --git a/src/Avalonia.Visuals/Media/IAffectsRender.cs b/src/Avalonia.Base/Media/IAffectsRender.cs similarity index 100% rename from src/Avalonia.Visuals/Media/IAffectsRender.cs rename to src/Avalonia.Base/Media/IAffectsRender.cs diff --git a/src/Avalonia.Visuals/Media/IBrush.cs b/src/Avalonia.Base/Media/IBrush.cs similarity index 100% rename from src/Avalonia.Visuals/Media/IBrush.cs rename to src/Avalonia.Base/Media/IBrush.cs diff --git a/src/Avalonia.Visuals/Media/IConicGradientBrush.cs b/src/Avalonia.Base/Media/IConicGradientBrush.cs similarity index 100% rename from src/Avalonia.Visuals/Media/IConicGradientBrush.cs rename to src/Avalonia.Base/Media/IConicGradientBrush.cs diff --git a/src/Avalonia.Visuals/Media/IDashStyle.cs b/src/Avalonia.Base/Media/IDashStyle.cs similarity index 100% rename from src/Avalonia.Visuals/Media/IDashStyle.cs rename to src/Avalonia.Base/Media/IDashStyle.cs diff --git a/src/Avalonia.Visuals/Media/IExperimentalAcrylicMaterial.cs b/src/Avalonia.Base/Media/IExperimentalAcrylicMaterial.cs similarity index 100% rename from src/Avalonia.Visuals/Media/IExperimentalAcrylicMaterial.cs rename to src/Avalonia.Base/Media/IExperimentalAcrylicMaterial.cs diff --git a/src/Avalonia.Visuals/Media/IGradientBrush.cs b/src/Avalonia.Base/Media/IGradientBrush.cs similarity index 100% rename from src/Avalonia.Visuals/Media/IGradientBrush.cs rename to src/Avalonia.Base/Media/IGradientBrush.cs diff --git a/src/Avalonia.Visuals/Media/IGradientStop.cs b/src/Avalonia.Base/Media/IGradientStop.cs similarity index 100% rename from src/Avalonia.Visuals/Media/IGradientStop.cs rename to src/Avalonia.Base/Media/IGradientStop.cs diff --git a/src/Avalonia.Visuals/Media/IImage.cs b/src/Avalonia.Base/Media/IImage.cs similarity index 93% rename from src/Avalonia.Visuals/Media/IImage.cs rename to src/Avalonia.Base/Media/IImage.cs index aff2a9ddf9..cbe25b7b58 100644 --- a/src/Avalonia.Visuals/Media/IImage.cs +++ b/src/Avalonia.Base/Media/IImage.cs @@ -1,5 +1,4 @@ -using Avalonia.Platform; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; namespace Avalonia.Media { diff --git a/src/Avalonia.Visuals/Media/IImageBrush.cs b/src/Avalonia.Base/Media/IImageBrush.cs similarity index 100% rename from src/Avalonia.Visuals/Media/IImageBrush.cs rename to src/Avalonia.Base/Media/IImageBrush.cs diff --git a/src/Avalonia.Visuals/Media/ILinearGradientBrush.cs b/src/Avalonia.Base/Media/ILinearGradientBrush.cs similarity index 100% rename from src/Avalonia.Visuals/Media/ILinearGradientBrush.cs rename to src/Avalonia.Base/Media/ILinearGradientBrush.cs diff --git a/src/Avalonia.Visuals/Media/IMutableBrush.cs b/src/Avalonia.Base/Media/IMutableBrush.cs similarity index 100% rename from src/Avalonia.Visuals/Media/IMutableBrush.cs rename to src/Avalonia.Base/Media/IMutableBrush.cs diff --git a/src/Avalonia.Visuals/Media/IMutableExperimentalAcrylicMaterial.cs b/src/Avalonia.Base/Media/IMutableExperimentalAcrylicMaterial.cs similarity index 100% rename from src/Avalonia.Visuals/Media/IMutableExperimentalAcrylicMaterial.cs rename to src/Avalonia.Base/Media/IMutableExperimentalAcrylicMaterial.cs diff --git a/src/Avalonia.Visuals/Media/IMutableTransform.cs b/src/Avalonia.Base/Media/IMutableTransform.cs similarity index 100% rename from src/Avalonia.Visuals/Media/IMutableTransform.cs rename to src/Avalonia.Base/Media/IMutableTransform.cs diff --git a/src/Avalonia.Visuals/Media/IPen.cs b/src/Avalonia.Base/Media/IPen.cs similarity index 100% rename from src/Avalonia.Visuals/Media/IPen.cs rename to src/Avalonia.Base/Media/IPen.cs diff --git a/src/Avalonia.Visuals/Media/IRadialGradientBrush.cs b/src/Avalonia.Base/Media/IRadialGradientBrush.cs similarity index 100% rename from src/Avalonia.Visuals/Media/IRadialGradientBrush.cs rename to src/Avalonia.Base/Media/IRadialGradientBrush.cs diff --git a/src/Avalonia.Visuals/Media/ISolidColorBrush.cs b/src/Avalonia.Base/Media/ISolidColorBrush.cs similarity index 100% rename from src/Avalonia.Visuals/Media/ISolidColorBrush.cs rename to src/Avalonia.Base/Media/ISolidColorBrush.cs diff --git a/src/Avalonia.Visuals/Media/ITileBrush.cs b/src/Avalonia.Base/Media/ITileBrush.cs similarity index 97% rename from src/Avalonia.Visuals/Media/ITileBrush.cs rename to src/Avalonia.Base/Media/ITileBrush.cs index 12fb221a89..991857eec9 100644 --- a/src/Avalonia.Visuals/Media/ITileBrush.cs +++ b/src/Avalonia.Base/Media/ITileBrush.cs @@ -1,4 +1,4 @@ -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; namespace Avalonia.Media { diff --git a/src/Avalonia.Visuals/Media/ITransform.cs b/src/Avalonia.Base/Media/ITransform.cs similarity index 100% rename from src/Avalonia.Visuals/Media/ITransform.cs rename to src/Avalonia.Base/Media/ITransform.cs diff --git a/src/Avalonia.Visuals/Media/IVisualBrush.cs b/src/Avalonia.Base/Media/IVisualBrush.cs similarity index 100% rename from src/Avalonia.Visuals/Media/IVisualBrush.cs rename to src/Avalonia.Base/Media/IVisualBrush.cs diff --git a/src/Avalonia.Visuals/Media/ImageBrush.cs b/src/Avalonia.Base/Media/ImageBrush.cs similarity index 100% rename from src/Avalonia.Visuals/Media/ImageBrush.cs rename to src/Avalonia.Base/Media/ImageBrush.cs diff --git a/src/Avalonia.Visuals/Media/ImageDrawing.cs b/src/Avalonia.Base/Media/ImageDrawing.cs similarity index 100% rename from src/Avalonia.Visuals/Media/ImageDrawing.cs rename to src/Avalonia.Base/Media/ImageDrawing.cs diff --git a/src/Avalonia.Visuals/Media/Imaging/Bitmap.cs b/src/Avalonia.Base/Media/Imaging/Bitmap.cs similarity index 99% rename from src/Avalonia.Visuals/Media/Imaging/Bitmap.cs rename to src/Avalonia.Base/Media/Imaging/Bitmap.cs index 647ce13528..5f1617d778 100644 --- a/src/Avalonia.Visuals/Media/Imaging/Bitmap.cs +++ b/src/Avalonia.Base/Media/Imaging/Bitmap.cs @@ -2,7 +2,6 @@ using System; using System.IO; using Avalonia.Platform; using Avalonia.Utilities; -using Avalonia.Visuals.Media.Imaging; namespace Avalonia.Media.Imaging { diff --git a/src/Avalonia.Visuals/Media/Imaging/BitmapBlendingMode.cs b/src/Avalonia.Base/Media/Imaging/BitmapBlendingMode.cs similarity index 97% rename from src/Avalonia.Visuals/Media/Imaging/BitmapBlendingMode.cs rename to src/Avalonia.Base/Media/Imaging/BitmapBlendingMode.cs index 473b43dab3..eb39020939 100644 --- a/src/Avalonia.Visuals/Media/Imaging/BitmapBlendingMode.cs +++ b/src/Avalonia.Base/Media/Imaging/BitmapBlendingMode.cs @@ -1,4 +1,4 @@ -namespace Avalonia.Visuals.Media.Imaging +namespace Avalonia.Media.Imaging { /// /// Controls the way the bitmaps are drawn together. diff --git a/src/Avalonia.Visuals/Media/Imaging/BitmapInterpolationMode.cs b/src/Avalonia.Base/Media/Imaging/BitmapInterpolationMode.cs similarity index 93% rename from src/Avalonia.Visuals/Media/Imaging/BitmapInterpolationMode.cs rename to src/Avalonia.Base/Media/Imaging/BitmapInterpolationMode.cs index 5fa48a5ec8..7cdb5d8b9f 100644 --- a/src/Avalonia.Visuals/Media/Imaging/BitmapInterpolationMode.cs +++ b/src/Avalonia.Base/Media/Imaging/BitmapInterpolationMode.cs @@ -1,4 +1,4 @@ -namespace Avalonia.Visuals.Media.Imaging +namespace Avalonia.Media.Imaging { /// /// Controls the performance and quality of bitmap scaling. diff --git a/src/Avalonia.Visuals/Media/Imaging/CroppedBitmap.cs b/src/Avalonia.Base/Media/Imaging/CroppedBitmap.cs similarity index 98% rename from src/Avalonia.Visuals/Media/Imaging/CroppedBitmap.cs rename to src/Avalonia.Base/Media/Imaging/CroppedBitmap.cs index be1f6db561..70f9fbf567 100644 --- a/src/Avalonia.Visuals/Media/Imaging/CroppedBitmap.cs +++ b/src/Avalonia.Base/Media/Imaging/CroppedBitmap.cs @@ -1,5 +1,5 @@ using System; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; namespace Avalonia.Media.Imaging { diff --git a/src/Avalonia.Visuals/Media/Imaging/IBitmap.cs b/src/Avalonia.Base/Media/Imaging/IBitmap.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Imaging/IBitmap.cs rename to src/Avalonia.Base/Media/Imaging/IBitmap.cs diff --git a/src/Avalonia.Visuals/Media/Imaging/RenderTargetBitmap.cs b/src/Avalonia.Base/Media/Imaging/RenderTargetBitmap.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Imaging/RenderTargetBitmap.cs rename to src/Avalonia.Base/Media/Imaging/RenderTargetBitmap.cs diff --git a/src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs b/src/Avalonia.Base/Media/Imaging/WriteableBitmap.cs similarity index 98% rename from src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs rename to src/Avalonia.Base/Media/Imaging/WriteableBitmap.cs index 40a24db338..1f39b1344d 100644 --- a/src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs +++ b/src/Avalonia.Base/Media/Imaging/WriteableBitmap.cs @@ -1,8 +1,6 @@ using System; using System.IO; -using System.Threading.Tasks; using Avalonia.Platform; -using Avalonia.Visuals.Media.Imaging; namespace Avalonia.Media.Imaging { diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableConicGradientBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableConicGradientBrush.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Immutable/ImmutableConicGradientBrush.cs rename to src/Avalonia.Base/Media/Immutable/ImmutableConicGradientBrush.cs diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableDashStyle.cs b/src/Avalonia.Base/Media/Immutable/ImmutableDashStyle.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Immutable/ImmutableDashStyle.cs rename to src/Avalonia.Base/Media/Immutable/ImmutableDashStyle.cs diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableGradientBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableGradientBrush.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Immutable/ImmutableGradientBrush.cs rename to src/Avalonia.Base/Media/Immutable/ImmutableGradientBrush.cs diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableGradientStop.cs b/src/Avalonia.Base/Media/Immutable/ImmutableGradientStop.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Immutable/ImmutableGradientStop.cs rename to src/Avalonia.Base/Media/Immutable/ImmutableGradientStop.cs diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableImageBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableImageBrush.cs similarity index 98% rename from src/Avalonia.Visuals/Media/Immutable/ImmutableImageBrush.cs rename to src/Avalonia.Base/Media/Immutable/ImmutableImageBrush.cs index 2d4fe45c8e..c36e82eacb 100644 --- a/src/Avalonia.Visuals/Media/Immutable/ImmutableImageBrush.cs +++ b/src/Avalonia.Base/Media/Immutable/ImmutableImageBrush.cs @@ -1,5 +1,4 @@ using Avalonia.Media.Imaging; -using Avalonia.Visuals.Media.Imaging; namespace Avalonia.Media.Immutable { diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableLinearGradientBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableLinearGradientBrush.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Immutable/ImmutableLinearGradientBrush.cs rename to src/Avalonia.Base/Media/Immutable/ImmutableLinearGradientBrush.cs diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutablePen.cs b/src/Avalonia.Base/Media/Immutable/ImmutablePen.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Immutable/ImmutablePen.cs rename to src/Avalonia.Base/Media/Immutable/ImmutablePen.cs diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableRadialGradientBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableRadialGradientBrush.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Immutable/ImmutableRadialGradientBrush.cs rename to src/Avalonia.Base/Media/Immutable/ImmutableRadialGradientBrush.cs diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableSolidColorBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableSolidColorBrush.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Immutable/ImmutableSolidColorBrush.cs rename to src/Avalonia.Base/Media/Immutable/ImmutableSolidColorBrush.cs diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableTextDecoration.cs b/src/Avalonia.Base/Media/Immutable/ImmutableTextDecoration.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Immutable/ImmutableTextDecoration.cs rename to src/Avalonia.Base/Media/Immutable/ImmutableTextDecoration.cs diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableTileBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableTileBrush.cs similarity index 98% rename from src/Avalonia.Visuals/Media/Immutable/ImmutableTileBrush.cs rename to src/Avalonia.Base/Media/Immutable/ImmutableTileBrush.cs index 1019751733..2c1844a2c2 100644 --- a/src/Avalonia.Visuals/Media/Immutable/ImmutableTileBrush.cs +++ b/src/Avalonia.Base/Media/Immutable/ImmutableTileBrush.cs @@ -1,4 +1,4 @@ -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; namespace Avalonia.Media.Immutable { diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableTransform.cs b/src/Avalonia.Base/Media/Immutable/ImmutableTransform.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Immutable/ImmutableTransform.cs rename to src/Avalonia.Base/Media/Immutable/ImmutableTransform.cs diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableVisualBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableVisualBrush.cs similarity index 94% rename from src/Avalonia.Visuals/Media/Immutable/ImmutableVisualBrush.cs rename to src/Avalonia.Base/Media/Immutable/ImmutableVisualBrush.cs index 0fd2905660..8ecef63237 100644 --- a/src/Avalonia.Visuals/Media/Immutable/ImmutableVisualBrush.cs +++ b/src/Avalonia.Base/Media/Immutable/ImmutableVisualBrush.cs @@ -1,4 +1,4 @@ -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; using Avalonia.VisualTree; namespace Avalonia.Media.Immutable @@ -33,7 +33,7 @@ namespace Avalonia.Media.Immutable RelativeRect? sourceRect = null, Stretch stretch = Stretch.Uniform, TileMode tileMode = TileMode.None, - BitmapInterpolationMode bitmapInterpolationMode = BitmapInterpolationMode.Default) + Imaging.BitmapInterpolationMode bitmapInterpolationMode = Imaging.BitmapInterpolationMode.Default) : base( alignmentX, alignmentY, diff --git a/src/Avalonia.Visuals/Media/ImmutableExperimentalAcrylicMaterial.cs b/src/Avalonia.Base/Media/ImmutableExperimentalAcrylicMaterial.cs similarity index 100% rename from src/Avalonia.Visuals/Media/ImmutableExperimentalAcrylicMaterial.cs rename to src/Avalonia.Base/Media/ImmutableExperimentalAcrylicMaterial.cs diff --git a/src/Avalonia.Visuals/Media/KnownColors.cs b/src/Avalonia.Base/Media/KnownColors.cs similarity index 100% rename from src/Avalonia.Visuals/Media/KnownColors.cs rename to src/Avalonia.Base/Media/KnownColors.cs diff --git a/src/Avalonia.Visuals/Media/LineGeometry.cs b/src/Avalonia.Base/Media/LineGeometry.cs similarity index 100% rename from src/Avalonia.Visuals/Media/LineGeometry.cs rename to src/Avalonia.Base/Media/LineGeometry.cs diff --git a/src/Avalonia.Visuals/Media/LineSegment.cs b/src/Avalonia.Base/Media/LineSegment.cs similarity index 100% rename from src/Avalonia.Visuals/Media/LineSegment.cs rename to src/Avalonia.Base/Media/LineSegment.cs diff --git a/src/Avalonia.Visuals/Media/LinearGradientBrush.cs b/src/Avalonia.Base/Media/LinearGradientBrush.cs similarity index 100% rename from src/Avalonia.Visuals/Media/LinearGradientBrush.cs rename to src/Avalonia.Base/Media/LinearGradientBrush.cs diff --git a/src/Avalonia.Visuals/Media/MaterialExtensions.cs b/src/Avalonia.Base/Media/MaterialExtensions.cs similarity index 100% rename from src/Avalonia.Visuals/Media/MaterialExtensions.cs rename to src/Avalonia.Base/Media/MaterialExtensions.cs diff --git a/src/Avalonia.Visuals/Media/MatrixTransform.cs b/src/Avalonia.Base/Media/MatrixTransform.cs similarity index 100% rename from src/Avalonia.Visuals/Media/MatrixTransform.cs rename to src/Avalonia.Base/Media/MatrixTransform.cs diff --git a/src/Avalonia.Visuals/Media/MediaExtensions.cs b/src/Avalonia.Base/Media/MediaExtensions.cs similarity index 100% rename from src/Avalonia.Visuals/Media/MediaExtensions.cs rename to src/Avalonia.Base/Media/MediaExtensions.cs diff --git a/src/Avalonia.Visuals/Media/PathFigure.cs b/src/Avalonia.Base/Media/PathFigure.cs similarity index 100% rename from src/Avalonia.Visuals/Media/PathFigure.cs rename to src/Avalonia.Base/Media/PathFigure.cs diff --git a/src/Avalonia.Visuals/Media/PathGeometry.cs b/src/Avalonia.Base/Media/PathGeometry.cs similarity index 100% rename from src/Avalonia.Visuals/Media/PathGeometry.cs rename to src/Avalonia.Base/Media/PathGeometry.cs diff --git a/src/Avalonia.Visuals/Media/PathGeometryCollections.cs b/src/Avalonia.Base/Media/PathGeometryCollections.cs similarity index 100% rename from src/Avalonia.Visuals/Media/PathGeometryCollections.cs rename to src/Avalonia.Base/Media/PathGeometryCollections.cs diff --git a/src/Avalonia.Visuals/Media/PathMarkupParser.cs b/src/Avalonia.Base/Media/PathMarkupParser.cs similarity index 100% rename from src/Avalonia.Visuals/Media/PathMarkupParser.cs rename to src/Avalonia.Base/Media/PathMarkupParser.cs diff --git a/src/Avalonia.Visuals/Media/PathSegment.cs b/src/Avalonia.Base/Media/PathSegment.cs similarity index 100% rename from src/Avalonia.Visuals/Media/PathSegment.cs rename to src/Avalonia.Base/Media/PathSegment.cs diff --git a/src/Avalonia.Visuals/Media/Pen.cs b/src/Avalonia.Base/Media/Pen.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Pen.cs rename to src/Avalonia.Base/Media/Pen.cs diff --git a/src/Avalonia.Visuals/Media/PenLineCap.cs b/src/Avalonia.Base/Media/PenLineCap.cs similarity index 100% rename from src/Avalonia.Visuals/Media/PenLineCap.cs rename to src/Avalonia.Base/Media/PenLineCap.cs diff --git a/src/Avalonia.Visuals/Media/PenLineJoin.cs b/src/Avalonia.Base/Media/PenLineJoin.cs similarity index 100% rename from src/Avalonia.Visuals/Media/PenLineJoin.cs rename to src/Avalonia.Base/Media/PenLineJoin.cs diff --git a/src/Avalonia.Visuals/Media/PolyLineSegment.cs b/src/Avalonia.Base/Media/PolyLineSegment.cs similarity index 100% rename from src/Avalonia.Visuals/Media/PolyLineSegment.cs rename to src/Avalonia.Base/Media/PolyLineSegment.cs diff --git a/src/Avalonia.Visuals/Media/PolylineGeometry.cs b/src/Avalonia.Base/Media/PolylineGeometry.cs similarity index 100% rename from src/Avalonia.Visuals/Media/PolylineGeometry.cs rename to src/Avalonia.Base/Media/PolylineGeometry.cs diff --git a/src/Avalonia.Visuals/Media/PreciseEllipticArcHelper.cs b/src/Avalonia.Base/Media/PreciseEllipticArcHelper.cs similarity index 100% rename from src/Avalonia.Visuals/Media/PreciseEllipticArcHelper.cs rename to src/Avalonia.Base/Media/PreciseEllipticArcHelper.cs diff --git a/src/Avalonia.Visuals/Media/QuadraticBezierSegment .cs b/src/Avalonia.Base/Media/QuadraticBezierSegment .cs similarity index 100% rename from src/Avalonia.Visuals/Media/QuadraticBezierSegment .cs rename to src/Avalonia.Base/Media/QuadraticBezierSegment .cs diff --git a/src/Avalonia.Visuals/Media/RadialGradientBrush.cs b/src/Avalonia.Base/Media/RadialGradientBrush.cs similarity index 100% rename from src/Avalonia.Visuals/Media/RadialGradientBrush.cs rename to src/Avalonia.Base/Media/RadialGradientBrush.cs diff --git a/src/Avalonia.Visuals/Media/RectangleGeometry.cs b/src/Avalonia.Base/Media/RectangleGeometry.cs similarity index 100% rename from src/Avalonia.Visuals/Media/RectangleGeometry.cs rename to src/Avalonia.Base/Media/RectangleGeometry.cs diff --git a/src/Avalonia.Visuals/Media/RenderOptions.cs b/src/Avalonia.Base/Media/RenderOptions.cs similarity index 97% rename from src/Avalonia.Visuals/Media/RenderOptions.cs rename to src/Avalonia.Base/Media/RenderOptions.cs index 7abbc8a656..5863d0ac58 100644 --- a/src/Avalonia.Visuals/Media/RenderOptions.cs +++ b/src/Avalonia.Base/Media/RenderOptions.cs @@ -1,4 +1,4 @@ -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; namespace Avalonia.Media { diff --git a/src/Avalonia.Visuals/Media/RotateTransform.cs b/src/Avalonia.Base/Media/RotateTransform.cs similarity index 100% rename from src/Avalonia.Visuals/Media/RotateTransform.cs rename to src/Avalonia.Base/Media/RotateTransform.cs diff --git a/src/Avalonia.Visuals/Media/ScaleTransform.cs b/src/Avalonia.Base/Media/ScaleTransform.cs similarity index 100% rename from src/Avalonia.Visuals/Media/ScaleTransform.cs rename to src/Avalonia.Base/Media/ScaleTransform.cs diff --git a/src/Avalonia.Visuals/Media/SkewTransform.cs b/src/Avalonia.Base/Media/SkewTransform.cs similarity index 100% rename from src/Avalonia.Visuals/Media/SkewTransform.cs rename to src/Avalonia.Base/Media/SkewTransform.cs diff --git a/src/Avalonia.Visuals/Media/SolidColorBrush.cs b/src/Avalonia.Base/Media/SolidColorBrush.cs similarity index 100% rename from src/Avalonia.Visuals/Media/SolidColorBrush.cs rename to src/Avalonia.Base/Media/SolidColorBrush.cs diff --git a/src/Avalonia.Visuals/Media/StreamGeometry.cs b/src/Avalonia.Base/Media/StreamGeometry.cs similarity index 100% rename from src/Avalonia.Visuals/Media/StreamGeometry.cs rename to src/Avalonia.Base/Media/StreamGeometry.cs diff --git a/src/Avalonia.Visuals/Media/StreamGeometryContext.cs b/src/Avalonia.Base/Media/StreamGeometryContext.cs similarity index 100% rename from src/Avalonia.Visuals/Media/StreamGeometryContext.cs rename to src/Avalonia.Base/Media/StreamGeometryContext.cs diff --git a/src/Avalonia.Visuals/Media/Stretch.cs b/src/Avalonia.Base/Media/Stretch.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Stretch.cs rename to src/Avalonia.Base/Media/Stretch.cs diff --git a/src/Avalonia.Visuals/Media/StretchDirection.cs b/src/Avalonia.Base/Media/StretchDirection.cs similarity index 100% rename from src/Avalonia.Visuals/Media/StretchDirection.cs rename to src/Avalonia.Base/Media/StretchDirection.cs diff --git a/src/Avalonia.Visuals/Media/SweepDirection.cs b/src/Avalonia.Base/Media/SweepDirection.cs similarity index 100% rename from src/Avalonia.Visuals/Media/SweepDirection.cs rename to src/Avalonia.Base/Media/SweepDirection.cs diff --git a/src/Avalonia.Visuals/Media/TextAlignment.cs b/src/Avalonia.Base/Media/TextAlignment.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextAlignment.cs rename to src/Avalonia.Base/Media/TextAlignment.cs diff --git a/src/Avalonia.Visuals/Media/TextCollapsingCreateInfo.cs b/src/Avalonia.Base/Media/TextCollapsingCreateInfo.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextCollapsingCreateInfo.cs rename to src/Avalonia.Base/Media/TextCollapsingCreateInfo.cs diff --git a/src/Avalonia.Visuals/Media/TextDecoration.cs b/src/Avalonia.Base/Media/TextDecoration.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextDecoration.cs rename to src/Avalonia.Base/Media/TextDecoration.cs diff --git a/src/Avalonia.Visuals/Media/TextDecorationCollection.cs b/src/Avalonia.Base/Media/TextDecorationCollection.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextDecorationCollection.cs rename to src/Avalonia.Base/Media/TextDecorationCollection.cs diff --git a/src/Avalonia.Visuals/Media/TextDecorationLocation.cs b/src/Avalonia.Base/Media/TextDecorationLocation.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextDecorationLocation.cs rename to src/Avalonia.Base/Media/TextDecorationLocation.cs diff --git a/src/Avalonia.Visuals/Media/TextDecorationUnit.cs b/src/Avalonia.Base/Media/TextDecorationUnit.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextDecorationUnit.cs rename to src/Avalonia.Base/Media/TextDecorationUnit.cs diff --git a/src/Avalonia.Visuals/Media/TextDecorations.cs b/src/Avalonia.Base/Media/TextDecorations.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextDecorations.cs rename to src/Avalonia.Base/Media/TextDecorations.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/DrawableTextRun.cs b/src/Avalonia.Base/Media/TextFormatting/DrawableTextRun.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/DrawableTextRun.cs rename to src/Avalonia.Base/Media/TextFormatting/DrawableTextRun.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/FontMetrics.cs b/src/Avalonia.Base/Media/TextFormatting/FontMetrics.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/FontMetrics.cs rename to src/Avalonia.Base/Media/TextFormatting/FontMetrics.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/FormattedTextSource.cs b/src/Avalonia.Base/Media/TextFormatting/FormattedTextSource.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/FormattedTextSource.cs rename to src/Avalonia.Base/Media/TextFormatting/FormattedTextSource.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/GenericTextParagraphProperties.cs b/src/Avalonia.Base/Media/TextFormatting/GenericTextParagraphProperties.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/GenericTextParagraphProperties.cs rename to src/Avalonia.Base/Media/TextFormatting/GenericTextParagraphProperties.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/GenericTextRunProperties.cs b/src/Avalonia.Base/Media/TextFormatting/GenericTextRunProperties.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/GenericTextRunProperties.cs rename to src/Avalonia.Base/Media/TextFormatting/GenericTextRunProperties.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/ITextSource.cs b/src/Avalonia.Base/Media/TextFormatting/ITextSource.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/ITextSource.cs rename to src/Avalonia.Base/Media/TextFormatting/ITextSource.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/LogicalDirection.cs b/src/Avalonia.Base/Media/TextFormatting/LogicalDirection.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/LogicalDirection.cs rename to src/Avalonia.Base/Media/TextFormatting/LogicalDirection.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/ShapeableTextCharacters.cs b/src/Avalonia.Base/Media/TextFormatting/ShapeableTextCharacters.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/ShapeableTextCharacters.cs rename to src/Avalonia.Base/Media/TextFormatting/ShapeableTextCharacters.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/ShapedBuffer.cs b/src/Avalonia.Base/Media/TextFormatting/ShapedBuffer.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/ShapedBuffer.cs rename to src/Avalonia.Base/Media/TextFormatting/ShapedBuffer.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/ShapedTextCharacters.cs b/src/Avalonia.Base/Media/TextFormatting/ShapedTextCharacters.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/ShapedTextCharacters.cs rename to src/Avalonia.Base/Media/TextFormatting/ShapedTextCharacters.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/SplitResult.cs b/src/Avalonia.Base/Media/TextFormatting/SplitResult.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/SplitResult.cs rename to src/Avalonia.Base/Media/TextFormatting/SplitResult.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextBounds.cs b/src/Avalonia.Base/Media/TextFormatting/TextBounds.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/TextBounds.cs rename to src/Avalonia.Base/Media/TextFormatting/TextBounds.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextCharacters.cs b/src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/TextCharacters.cs rename to src/Avalonia.Base/Media/TextFormatting/TextCharacters.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextCollapsingProperties.cs b/src/Avalonia.Base/Media/TextFormatting/TextCollapsingProperties.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/TextCollapsingProperties.cs rename to src/Avalonia.Base/Media/TextFormatting/TextCollapsingProperties.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextEllipsisHelper.cs b/src/Avalonia.Base/Media/TextFormatting/TextEllipsisHelper.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/TextEllipsisHelper.cs rename to src/Avalonia.Base/Media/TextFormatting/TextEllipsisHelper.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextEndOfLine.cs b/src/Avalonia.Base/Media/TextFormatting/TextEndOfLine.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/TextEndOfLine.cs rename to src/Avalonia.Base/Media/TextFormatting/TextEndOfLine.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextEndOfParagraph.cs b/src/Avalonia.Base/Media/TextFormatting/TextEndOfParagraph.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/TextEndOfParagraph.cs rename to src/Avalonia.Base/Media/TextFormatting/TextEndOfParagraph.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextFormatter.cs b/src/Avalonia.Base/Media/TextFormatting/TextFormatter.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/TextFormatter.cs rename to src/Avalonia.Base/Media/TextFormatting/TextFormatter.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs rename to src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs b/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs rename to src/Avalonia.Base/Media/TextFormatting/TextLayout.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs b/src/Avalonia.Base/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs rename to src/Avalonia.Base/Media/TextFormatting/TextLeadingPrefixCharacterEllipsis.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLine.cs b/src/Avalonia.Base/Media/TextFormatting/TextLine.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/TextLine.cs rename to src/Avalonia.Base/Media/TextFormatting/TextLine.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLineBreak.cs b/src/Avalonia.Base/Media/TextFormatting/TextLineBreak.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/TextLineBreak.cs rename to src/Avalonia.Base/Media/TextFormatting/TextLineBreak.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs rename to src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLineMetrics.cs b/src/Avalonia.Base/Media/TextFormatting/TextLineMetrics.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/TextLineMetrics.cs rename to src/Avalonia.Base/Media/TextFormatting/TextLineMetrics.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextParagraphProperties.cs b/src/Avalonia.Base/Media/TextFormatting/TextParagraphProperties.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/TextParagraphProperties.cs rename to src/Avalonia.Base/Media/TextFormatting/TextParagraphProperties.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextRange.cs b/src/Avalonia.Base/Media/TextFormatting/TextRange.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/TextRange.cs rename to src/Avalonia.Base/Media/TextFormatting/TextRange.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextRun.cs b/src/Avalonia.Base/Media/TextFormatting/TextRun.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/TextRun.cs rename to src/Avalonia.Base/Media/TextFormatting/TextRun.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextRunProperties.cs b/src/Avalonia.Base/Media/TextFormatting/TextRunProperties.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/TextRunProperties.cs rename to src/Avalonia.Base/Media/TextFormatting/TextRunProperties.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextShaper.cs b/src/Avalonia.Base/Media/TextFormatting/TextShaper.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/TextShaper.cs rename to src/Avalonia.Base/Media/TextFormatting/TextShaper.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextShaperOptions.cs b/src/Avalonia.Base/Media/TextFormatting/TextShaperOptions.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/TextShaperOptions.cs rename to src/Avalonia.Base/Media/TextFormatting/TextShaperOptions.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingCharacterEllipsis.cs b/src/Avalonia.Base/Media/TextFormatting/TextTrailingCharacterEllipsis.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/TextTrailingCharacterEllipsis.cs rename to src/Avalonia.Base/Media/TextFormatting/TextTrailingCharacterEllipsis.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextTrailingWordEllipsis.cs b/src/Avalonia.Base/Media/TextFormatting/TextTrailingWordEllipsis.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/TextTrailingWordEllipsis.cs rename to src/Avalonia.Base/Media/TextFormatting/TextTrailingWordEllipsis.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/BiDiAlgorithm.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/BiDiAlgorithm.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/Unicode/BiDiAlgorithm.cs rename to src/Avalonia.Base/Media/TextFormatting/Unicode/BiDiAlgorithm.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/BiDiClass.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/BiDiClass.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/Unicode/BiDiClass.cs rename to src/Avalonia.Base/Media/TextFormatting/Unicode/BiDiClass.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/BiDiData.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/BiDiData.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/Unicode/BiDiData.cs rename to src/Avalonia.Base/Media/TextFormatting/Unicode/BiDiData.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/BiDiPairedBracketType.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/BiDiPairedBracketType.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/Unicode/BiDiPairedBracketType.cs rename to src/Avalonia.Base/Media/TextFormatting/Unicode/BiDiPairedBracketType.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/BinaryReaderExtensions.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/BinaryReaderExtensions.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/Unicode/BinaryReaderExtensions.cs rename to src/Avalonia.Base/Media/TextFormatting/Unicode/BinaryReaderExtensions.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/Codepoint.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/Codepoint.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/Unicode/Codepoint.cs rename to src/Avalonia.Base/Media/TextFormatting/Unicode/Codepoint.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/CodepointEnumerator.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/CodepointEnumerator.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/Unicode/CodepointEnumerator.cs rename to src/Avalonia.Base/Media/TextFormatting/Unicode/CodepointEnumerator.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/GeneralCategory.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/GeneralCategory.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/Unicode/GeneralCategory.cs rename to src/Avalonia.Base/Media/TextFormatting/Unicode/GeneralCategory.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/Grapheme.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/Grapheme.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/Unicode/Grapheme.cs rename to src/Avalonia.Base/Media/TextFormatting/Unicode/Grapheme.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/GraphemeBreakClass.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/GraphemeBreakClass.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/Unicode/GraphemeBreakClass.cs rename to src/Avalonia.Base/Media/TextFormatting/Unicode/GraphemeBreakClass.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/GraphemeEnumerator.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/GraphemeEnumerator.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/Unicode/GraphemeEnumerator.cs rename to src/Avalonia.Base/Media/TextFormatting/Unicode/GraphemeEnumerator.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/LineBreak.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/LineBreak.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/Unicode/LineBreak.cs rename to src/Avalonia.Base/Media/TextFormatting/Unicode/LineBreak.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/LineBreakClass.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/LineBreakClass.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/Unicode/LineBreakClass.cs rename to src/Avalonia.Base/Media/TextFormatting/Unicode/LineBreakClass.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/LineBreakEnumerator.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/LineBreakEnumerator.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/Unicode/LineBreakEnumerator.cs rename to src/Avalonia.Base/Media/TextFormatting/Unicode/LineBreakEnumerator.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/LineBreakPairTable.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/LineBreakPairTable.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/Unicode/LineBreakPairTable.cs rename to src/Avalonia.Base/Media/TextFormatting/Unicode/LineBreakPairTable.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/PropertyValueAliasHelper.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/PropertyValueAliasHelper.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/Unicode/PropertyValueAliasHelper.cs rename to src/Avalonia.Base/Media/TextFormatting/Unicode/PropertyValueAliasHelper.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/Script.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/Script.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/Unicode/Script.cs rename to src/Avalonia.Base/Media/TextFormatting/Unicode/Script.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/UnicodeData.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeData.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/Unicode/UnicodeData.cs rename to src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeData.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/UnicodeTrie.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeTrie.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/Unicode/UnicodeTrie.cs rename to src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeTrie.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/UnicodeTrieBuilder.Constants.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeTrieBuilder.Constants.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/Unicode/UnicodeTrieBuilder.Constants.cs rename to src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeTrieBuilder.Constants.cs diff --git a/src/Avalonia.Visuals/Media/TextFormatting/Unicode/UnicodeTrieBuilder.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeTrieBuilder.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextFormatting/Unicode/UnicodeTrieBuilder.cs rename to src/Avalonia.Base/Media/TextFormatting/Unicode/UnicodeTrieBuilder.cs diff --git a/src/Avalonia.Visuals/Media/TextHitTestResult.cs b/src/Avalonia.Base/Media/TextHitTestResult.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextHitTestResult.cs rename to src/Avalonia.Base/Media/TextHitTestResult.cs diff --git a/src/Avalonia.Visuals/Media/TextLeadingPrefixTrimming.cs b/src/Avalonia.Base/Media/TextLeadingPrefixTrimming.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextLeadingPrefixTrimming.cs rename to src/Avalonia.Base/Media/TextLeadingPrefixTrimming.cs diff --git a/src/Avalonia.Visuals/Media/TextNoneTrimming.cs b/src/Avalonia.Base/Media/TextNoneTrimming.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextNoneTrimming.cs rename to src/Avalonia.Base/Media/TextNoneTrimming.cs diff --git a/src/Avalonia.Visuals/Media/TextTrailingTrimming.cs b/src/Avalonia.Base/Media/TextTrailingTrimming.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextTrailingTrimming.cs rename to src/Avalonia.Base/Media/TextTrailingTrimming.cs diff --git a/src/Avalonia.Visuals/Media/TextTrimming.cs b/src/Avalonia.Base/Media/TextTrimming.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextTrimming.cs rename to src/Avalonia.Base/Media/TextTrimming.cs diff --git a/src/Avalonia.Visuals/Media/TextWrapping.cs b/src/Avalonia.Base/Media/TextWrapping.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TextWrapping.cs rename to src/Avalonia.Base/Media/TextWrapping.cs diff --git a/src/Avalonia.Visuals/Media/TileBrush.cs b/src/Avalonia.Base/Media/TileBrush.cs similarity index 99% rename from src/Avalonia.Visuals/Media/TileBrush.cs rename to src/Avalonia.Base/Media/TileBrush.cs index 0a4bc0e56d..ab1ee2d604 100644 --- a/src/Avalonia.Visuals/Media/TileBrush.cs +++ b/src/Avalonia.Base/Media/TileBrush.cs @@ -1,4 +1,4 @@ -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; namespace Avalonia.Media { diff --git a/src/Avalonia.Visuals/Media/Transform.cs b/src/Avalonia.Base/Media/Transform.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Transform.cs rename to src/Avalonia.Base/Media/Transform.cs diff --git a/src/Avalonia.Visuals/Media/TransformConverter.cs b/src/Avalonia.Base/Media/TransformConverter.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TransformConverter.cs rename to src/Avalonia.Base/Media/TransformConverter.cs diff --git a/src/Avalonia.Visuals/Media/TransformExtensions.cs b/src/Avalonia.Base/Media/TransformExtensions.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TransformExtensions.cs rename to src/Avalonia.Base/Media/TransformExtensions.cs diff --git a/src/Avalonia.Visuals/Media/TransformGroup.cs b/src/Avalonia.Base/Media/TransformGroup.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TransformGroup.cs rename to src/Avalonia.Base/Media/TransformGroup.cs diff --git a/src/Avalonia.Visuals/Media/Transformation/InterpolationUtilities.cs b/src/Avalonia.Base/Media/Transformation/InterpolationUtilities.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Transformation/InterpolationUtilities.cs rename to src/Avalonia.Base/Media/Transformation/InterpolationUtilities.cs diff --git a/src/Avalonia.Visuals/Media/Transformation/TransformOperation.cs b/src/Avalonia.Base/Media/Transformation/TransformOperation.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Transformation/TransformOperation.cs rename to src/Avalonia.Base/Media/Transformation/TransformOperation.cs diff --git a/src/Avalonia.Visuals/Media/Transformation/TransformOperations.cs b/src/Avalonia.Base/Media/Transformation/TransformOperations.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Transformation/TransformOperations.cs rename to src/Avalonia.Base/Media/Transformation/TransformOperations.cs diff --git a/src/Avalonia.Visuals/Media/Transformation/TransformParser.cs b/src/Avalonia.Base/Media/Transformation/TransformParser.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Transformation/TransformParser.cs rename to src/Avalonia.Base/Media/Transformation/TransformParser.cs diff --git a/src/Avalonia.Visuals/Media/TranslateTransform.cs b/src/Avalonia.Base/Media/TranslateTransform.cs similarity index 100% rename from src/Avalonia.Visuals/Media/TranslateTransform.cs rename to src/Avalonia.Base/Media/TranslateTransform.cs diff --git a/src/Avalonia.Visuals/Media/Typeface.cs b/src/Avalonia.Base/Media/Typeface.cs similarity index 100% rename from src/Avalonia.Visuals/Media/Typeface.cs rename to src/Avalonia.Base/Media/Typeface.cs diff --git a/src/Avalonia.Visuals/Media/UnicodeRange.cs b/src/Avalonia.Base/Media/UnicodeRange.cs similarity index 100% rename from src/Avalonia.Visuals/Media/UnicodeRange.cs rename to src/Avalonia.Base/Media/UnicodeRange.cs diff --git a/src/Avalonia.Visuals/Media/VisualBrush.cs b/src/Avalonia.Base/Media/VisualBrush.cs similarity index 100% rename from src/Avalonia.Visuals/Media/VisualBrush.cs rename to src/Avalonia.Base/Media/VisualBrush.cs diff --git a/src/Avalonia.Visuals/Media/PixelPoint.cs b/src/Avalonia.Base/PixelPoint.cs similarity index 100% rename from src/Avalonia.Visuals/Media/PixelPoint.cs rename to src/Avalonia.Base/PixelPoint.cs diff --git a/src/Avalonia.Visuals/Media/PixelRect.cs b/src/Avalonia.Base/PixelRect.cs similarity index 100% rename from src/Avalonia.Visuals/Media/PixelRect.cs rename to src/Avalonia.Base/PixelRect.cs diff --git a/src/Avalonia.Visuals/Media/PixelSize.cs b/src/Avalonia.Base/PixelSize.cs similarity index 100% rename from src/Avalonia.Visuals/Media/PixelSize.cs rename to src/Avalonia.Base/PixelSize.cs diff --git a/src/Avalonia.Visuals/Media/PixelVector.cs b/src/Avalonia.Base/PixelVector.cs similarity index 100% rename from src/Avalonia.Visuals/Media/PixelVector.cs rename to src/Avalonia.Base/PixelVector.cs diff --git a/src/Avalonia.Visuals/Platform/AlphaFormat.cs b/src/Avalonia.Base/Platform/AlphaFormat.cs similarity index 100% rename from src/Avalonia.Visuals/Platform/AlphaFormat.cs rename to src/Avalonia.Base/Platform/AlphaFormat.cs diff --git a/src/Avalonia.Visuals/Platform/IBitmapImpl.cs b/src/Avalonia.Base/Platform/IBitmapImpl.cs similarity index 100% rename from src/Avalonia.Visuals/Platform/IBitmapImpl.cs rename to src/Avalonia.Base/Platform/IBitmapImpl.cs diff --git a/src/Avalonia.Input/Platform/ICursorFactory.cs b/src/Avalonia.Base/Platform/ICursorFactory.cs similarity index 100% rename from src/Avalonia.Input/Platform/ICursorFactory.cs rename to src/Avalonia.Base/Platform/ICursorFactory.cs diff --git a/src/Avalonia.Input/Platform/ICursorImpl.cs b/src/Avalonia.Base/Platform/ICursorImpl.cs similarity index 100% rename from src/Avalonia.Input/Platform/ICursorImpl.cs rename to src/Avalonia.Base/Platform/ICursorImpl.cs diff --git a/src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs b/src/Avalonia.Base/Platform/IDrawingContextImpl.cs similarity index 99% rename from src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs rename to src/Avalonia.Base/Platform/IDrawingContextImpl.cs index 82af6ff0b6..4e6612e908 100644 --- a/src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs +++ b/src/Avalonia.Base/Platform/IDrawingContextImpl.cs @@ -2,7 +2,7 @@ using System; using Avalonia.Media; using Avalonia.Rendering.SceneGraph; using Avalonia.Utilities; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; namespace Avalonia.Platform { diff --git a/src/Avalonia.Visuals/Platform/IDrawingContextWithAcrylicLikeSupport.cs b/src/Avalonia.Base/Platform/IDrawingContextWithAcrylicLikeSupport.cs similarity index 100% rename from src/Avalonia.Visuals/Platform/IDrawingContextWithAcrylicLikeSupport.cs rename to src/Avalonia.Base/Platform/IDrawingContextWithAcrylicLikeSupport.cs diff --git a/src/Avalonia.Visuals/Platform/IFontManagerImpl.cs b/src/Avalonia.Base/Platform/IFontManagerImpl.cs similarity index 100% rename from src/Avalonia.Visuals/Platform/IFontManagerImpl.cs rename to src/Avalonia.Base/Platform/IFontManagerImpl.cs diff --git a/src/Avalonia.Visuals/Platform/IGeometryContext.cs b/src/Avalonia.Base/Platform/IGeometryContext.cs similarity index 100% rename from src/Avalonia.Visuals/Platform/IGeometryContext.cs rename to src/Avalonia.Base/Platform/IGeometryContext.cs diff --git a/src/Avalonia.Visuals/Platform/IGeometryImpl.cs b/src/Avalonia.Base/Platform/IGeometryImpl.cs similarity index 100% rename from src/Avalonia.Visuals/Platform/IGeometryImpl.cs rename to src/Avalonia.Base/Platform/IGeometryImpl.cs diff --git a/src/Avalonia.Visuals/Platform/IGlyphRunImpl.cs b/src/Avalonia.Base/Platform/IGlyphRunImpl.cs similarity index 100% rename from src/Avalonia.Visuals/Platform/IGlyphRunImpl.cs rename to src/Avalonia.Base/Platform/IGlyphRunImpl.cs diff --git a/src/Avalonia.Visuals/Platform/IGlyphTypefaceImpl.cs b/src/Avalonia.Base/Platform/IGlyphTypefaceImpl.cs similarity index 100% rename from src/Avalonia.Visuals/Platform/IGlyphTypefaceImpl.cs rename to src/Avalonia.Base/Platform/IGlyphTypefaceImpl.cs diff --git a/src/Avalonia.Visuals/Platform/ILockedFramebuffer.cs b/src/Avalonia.Base/Platform/ILockedFramebuffer.cs similarity index 100% rename from src/Avalonia.Visuals/Platform/ILockedFramebuffer.cs rename to src/Avalonia.Base/Platform/ILockedFramebuffer.cs diff --git a/src/Avalonia.Visuals/Platform/IModuleEnvironmentChecker.cs b/src/Avalonia.Base/Platform/IModuleEnvironmentChecker.cs similarity index 100% rename from src/Avalonia.Visuals/Platform/IModuleEnvironmentChecker.cs rename to src/Avalonia.Base/Platform/IModuleEnvironmentChecker.cs diff --git a/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs b/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs similarity index 99% rename from src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs rename to src/Avalonia.Base/Platform/IPlatformRenderInterface.cs index 444cc2eb8c..c46efd46c3 100644 --- a/src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs +++ b/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using System.IO; using Avalonia.Media; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; namespace Avalonia.Platform { diff --git a/src/Avalonia.Visuals/Platform/IPlatformSettings.cs b/src/Avalonia.Base/Platform/IPlatformSettings.cs similarity index 100% rename from src/Avalonia.Visuals/Platform/IPlatformSettings.cs rename to src/Avalonia.Base/Platform/IPlatformSettings.cs diff --git a/src/Avalonia.Visuals/Platform/IRenderTarget.cs b/src/Avalonia.Base/Platform/IRenderTarget.cs similarity index 100% rename from src/Avalonia.Visuals/Platform/IRenderTarget.cs rename to src/Avalonia.Base/Platform/IRenderTarget.cs diff --git a/src/Avalonia.Visuals/Platform/IRenderTargetBitmapImpl.cs b/src/Avalonia.Base/Platform/IRenderTargetBitmapImpl.cs similarity index 100% rename from src/Avalonia.Visuals/Platform/IRenderTargetBitmapImpl.cs rename to src/Avalonia.Base/Platform/IRenderTargetBitmapImpl.cs diff --git a/src/Avalonia.Visuals/Platform/IStreamGeometryContextImpl.cs b/src/Avalonia.Base/Platform/IStreamGeometryContextImpl.cs similarity index 100% rename from src/Avalonia.Visuals/Platform/IStreamGeometryContextImpl.cs rename to src/Avalonia.Base/Platform/IStreamGeometryContextImpl.cs diff --git a/src/Avalonia.Visuals/Platform/IStreamGeometryImpl.cs b/src/Avalonia.Base/Platform/IStreamGeometryImpl.cs similarity index 100% rename from src/Avalonia.Visuals/Platform/IStreamGeometryImpl.cs rename to src/Avalonia.Base/Platform/IStreamGeometryImpl.cs diff --git a/src/Avalonia.Visuals/Platform/ITextShaperImpl.cs b/src/Avalonia.Base/Platform/ITextShaperImpl.cs similarity index 100% rename from src/Avalonia.Visuals/Platform/ITextShaperImpl.cs rename to src/Avalonia.Base/Platform/ITextShaperImpl.cs diff --git a/src/Avalonia.Visuals/Platform/ITransformedGeometryImpl.cs b/src/Avalonia.Base/Platform/ITransformedGeometryImpl.cs similarity index 100% rename from src/Avalonia.Visuals/Platform/ITransformedGeometryImpl.cs rename to src/Avalonia.Base/Platform/ITransformedGeometryImpl.cs diff --git a/src/Avalonia.Visuals/Platform/IWriteableBitmapImpl.cs b/src/Avalonia.Base/Platform/IWriteableBitmapImpl.cs similarity index 100% rename from src/Avalonia.Visuals/Platform/IWriteableBitmapImpl.cs rename to src/Avalonia.Base/Platform/IWriteableBitmapImpl.cs diff --git a/src/Avalonia.Visuals/Platform/LockedFramebuffer.cs b/src/Avalonia.Base/Platform/LockedFramebuffer.cs similarity index 100% rename from src/Avalonia.Visuals/Platform/LockedFramebuffer.cs rename to src/Avalonia.Base/Platform/LockedFramebuffer.cs diff --git a/src/Avalonia.Visuals/Platform/PathGeometryContext.cs b/src/Avalonia.Base/Platform/PathGeometryContext.cs similarity index 100% rename from src/Avalonia.Visuals/Platform/PathGeometryContext.cs rename to src/Avalonia.Base/Platform/PathGeometryContext.cs diff --git a/src/Avalonia.Visuals/Platform/PixelFormat.cs b/src/Avalonia.Base/Platform/PixelFormat.cs similarity index 100% rename from src/Avalonia.Visuals/Platform/PixelFormat.cs rename to src/Avalonia.Base/Platform/PixelFormat.cs diff --git a/src/Avalonia.Visuals/Point.cs b/src/Avalonia.Base/Point.cs similarity index 100% rename from src/Avalonia.Visuals/Point.cs rename to src/Avalonia.Base/Point.cs diff --git a/src/Avalonia.Visuals/Points.cs b/src/Avalonia.Base/Points.cs similarity index 100% rename from src/Avalonia.Visuals/Points.cs rename to src/Avalonia.Base/Points.cs diff --git a/src/Avalonia.Base/Properties/AssemblyInfo.cs b/src/Avalonia.Base/Properties/AssemblyInfo.cs index 9ffb5872f0..4d7f487e00 100644 --- a/src/Avalonia.Base/Properties/AssemblyInfo.cs +++ b/src/Avalonia.Base/Properties/AssemblyInfo.cs @@ -1,15 +1,32 @@ -// Licensed under the MIT license. See licence.md file in the project root for full license information. - using System.Reflection; using System.Runtime.CompilerServices; using Avalonia.Metadata; +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia")] +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Animation")] +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Animation.Easings")] +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Data.Converters")] +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Input")] +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Input.GestureRecognizers")] +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Input.TextInput")] +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Layout")] +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.LogicalTree")] +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Media")] +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Media.Imaging")] +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Media.Transformation")] +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Styling")] + [assembly: InternalsVisibleTo("Avalonia.Base.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] +[assembly: InternalsVisibleTo("Avalonia.Controls, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] [assembly: InternalsVisibleTo("Avalonia.Controls.DataGrid, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] +[assembly: InternalsVisibleTo("Avalonia.Controls.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] +[assembly: InternalsVisibleTo("Avalonia.Direct2D1.RenderTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] +[assembly: InternalsVisibleTo("Avalonia.LeakTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] [assembly: InternalsVisibleTo("Avalonia.Markup.Xaml.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.Visuals, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] [assembly: InternalsVisibleTo("Avalonia.PlatformSupport, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] - +[assembly: InternalsVisibleTo("Avalonia.Skia.RenderTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] +[assembly: InternalsVisibleTo("Avalonia.Skia.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] +[assembly: InternalsVisibleTo("Avalonia.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] +[assembly: InternalsVisibleTo("Avalonia.Web.Blazor, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] +[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] diff --git a/src/Avalonia.Visuals/Rect.cs b/src/Avalonia.Base/Rect.cs similarity index 100% rename from src/Avalonia.Visuals/Rect.cs rename to src/Avalonia.Base/Rect.cs diff --git a/src/Avalonia.Visuals/RelativePoint.cs b/src/Avalonia.Base/RelativePoint.cs similarity index 100% rename from src/Avalonia.Visuals/RelativePoint.cs rename to src/Avalonia.Base/RelativePoint.cs diff --git a/src/Avalonia.Visuals/RelativeRect.cs b/src/Avalonia.Base/RelativeRect.cs similarity index 100% rename from src/Avalonia.Visuals/RelativeRect.cs rename to src/Avalonia.Base/RelativeRect.cs diff --git a/src/Avalonia.Visuals/RenderTargetCorruptedException.cs b/src/Avalonia.Base/RenderTargetCorruptedException.cs similarity index 100% rename from src/Avalonia.Visuals/RenderTargetCorruptedException.cs rename to src/Avalonia.Base/RenderTargetCorruptedException.cs diff --git a/src/Avalonia.Visuals/Rendering/DefaultRenderTimer.cs b/src/Avalonia.Base/Rendering/DefaultRenderTimer.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/DefaultRenderTimer.cs rename to src/Avalonia.Base/Rendering/DefaultRenderTimer.cs diff --git a/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs b/src/Avalonia.Base/Rendering/DeferredRenderer.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/DeferredRenderer.cs rename to src/Avalonia.Base/Rendering/DeferredRenderer.cs diff --git a/src/Avalonia.Visuals/Rendering/DirtyRects.cs b/src/Avalonia.Base/Rendering/DirtyRects.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/DirtyRects.cs rename to src/Avalonia.Base/Rendering/DirtyRects.cs diff --git a/src/Avalonia.Visuals/Rendering/DirtyVisuals.cs b/src/Avalonia.Base/Rendering/DirtyVisuals.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/DirtyVisuals.cs rename to src/Avalonia.Base/Rendering/DirtyVisuals.cs diff --git a/src/Avalonia.Visuals/Rendering/DisplayDirtyRect.cs b/src/Avalonia.Base/Rendering/DisplayDirtyRect.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/DisplayDirtyRect.cs rename to src/Avalonia.Base/Rendering/DisplayDirtyRect.cs diff --git a/src/Avalonia.Visuals/Rendering/DisplayDirtyRects.cs b/src/Avalonia.Base/Rendering/DisplayDirtyRects.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/DisplayDirtyRects.cs rename to src/Avalonia.Base/Rendering/DisplayDirtyRects.cs diff --git a/src/Avalonia.Visuals/Rendering/ICustomSimpleHitTest.cs b/src/Avalonia.Base/Rendering/ICustomSimpleHitTest.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/ICustomSimpleHitTest.cs rename to src/Avalonia.Base/Rendering/ICustomSimpleHitTest.cs diff --git a/src/Avalonia.Visuals/Rendering/IDeferredRendererLock.cs b/src/Avalonia.Base/Rendering/IDeferredRendererLock.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/IDeferredRendererLock.cs rename to src/Avalonia.Base/Rendering/IDeferredRendererLock.cs diff --git a/src/Avalonia.Visuals/Rendering/IRenderLoop.cs b/src/Avalonia.Base/Rendering/IRenderLoop.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/IRenderLoop.cs rename to src/Avalonia.Base/Rendering/IRenderLoop.cs diff --git a/src/Avalonia.Visuals/Rendering/IRenderLoopTask.cs b/src/Avalonia.Base/Rendering/IRenderLoopTask.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/IRenderLoopTask.cs rename to src/Avalonia.Base/Rendering/IRenderLoopTask.cs diff --git a/src/Avalonia.Visuals/Rendering/IRenderRoot.cs b/src/Avalonia.Base/Rendering/IRenderRoot.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/IRenderRoot.cs rename to src/Avalonia.Base/Rendering/IRenderRoot.cs diff --git a/src/Avalonia.Visuals/Rendering/IRenderTimer.cs b/src/Avalonia.Base/Rendering/IRenderTimer.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/IRenderTimer.cs rename to src/Avalonia.Base/Rendering/IRenderTimer.cs diff --git a/src/Avalonia.Visuals/Rendering/IRenderer.cs b/src/Avalonia.Base/Rendering/IRenderer.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/IRenderer.cs rename to src/Avalonia.Base/Rendering/IRenderer.cs diff --git a/src/Avalonia.Visuals/Rendering/IRendererFactory.cs b/src/Avalonia.Base/Rendering/IRendererFactory.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/IRendererFactory.cs rename to src/Avalonia.Base/Rendering/IRendererFactory.cs diff --git a/src/Avalonia.Visuals/Rendering/IVisualBrushInitialize.cs b/src/Avalonia.Base/Rendering/IVisualBrushInitialize.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/IVisualBrushInitialize.cs rename to src/Avalonia.Base/Rendering/IVisualBrushInitialize.cs diff --git a/src/Avalonia.Visuals/Rendering/IVisualBrushRenderer.cs b/src/Avalonia.Base/Rendering/IVisualBrushRenderer.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/IVisualBrushRenderer.cs rename to src/Avalonia.Base/Rendering/IVisualBrushRenderer.cs diff --git a/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs b/src/Avalonia.Base/Rendering/ImmediateRenderer.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs rename to src/Avalonia.Base/Rendering/ImmediateRenderer.cs diff --git a/src/Avalonia.Visuals/Rendering/ManagedDeferredRendererLock.cs b/src/Avalonia.Base/Rendering/ManagedDeferredRendererLock.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/ManagedDeferredRendererLock.cs rename to src/Avalonia.Base/Rendering/ManagedDeferredRendererLock.cs diff --git a/src/Avalonia.Visuals/Rendering/RenderLayer.cs b/src/Avalonia.Base/Rendering/RenderLayer.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/RenderLayer.cs rename to src/Avalonia.Base/Rendering/RenderLayer.cs diff --git a/src/Avalonia.Visuals/Rendering/RenderLayers.cs b/src/Avalonia.Base/Rendering/RenderLayers.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/RenderLayers.cs rename to src/Avalonia.Base/Rendering/RenderLayers.cs diff --git a/src/Avalonia.Visuals/Rendering/RenderLoop.cs b/src/Avalonia.Base/Rendering/RenderLoop.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/RenderLoop.cs rename to src/Avalonia.Base/Rendering/RenderLoop.cs diff --git a/src/Avalonia.Visuals/Rendering/RendererBase.cs b/src/Avalonia.Base/Rendering/RendererBase.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/RendererBase.cs rename to src/Avalonia.Base/Rendering/RendererBase.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/BitmapBlendModeNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/BitmapBlendModeNode.cs similarity index 98% rename from src/Avalonia.Visuals/Rendering/SceneGraph/BitmapBlendModeNode.cs rename to src/Avalonia.Base/Rendering/SceneGraph/BitmapBlendModeNode.cs index 45b62b843b..98e89f6549 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/BitmapBlendModeNode.cs +++ b/src/Avalonia.Base/Rendering/SceneGraph/BitmapBlendModeNode.cs @@ -1,5 +1,5 @@ using Avalonia.Platform; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; namespace Avalonia.Rendering.SceneGraph { diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/BrushDrawOperation.cs b/src/Avalonia.Base/Rendering/SceneGraph/BrushDrawOperation.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/BrushDrawOperation.cs rename to src/Avalonia.Base/Rendering/SceneGraph/BrushDrawOperation.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/ClipNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/ClipNode.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/ClipNode.cs rename to src/Avalonia.Base/Rendering/SceneGraph/ClipNode.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/CustomDrawOperation.cs b/src/Avalonia.Base/Rendering/SceneGraph/CustomDrawOperation.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/CustomDrawOperation.cs rename to src/Avalonia.Base/Rendering/SceneGraph/CustomDrawOperation.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs b/src/Avalonia.Base/Rendering/SceneGraph/DeferredDrawingContextImpl.cs similarity index 99% rename from src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs rename to src/Avalonia.Base/Rendering/SceneGraph/DeferredDrawingContextImpl.cs index 9710ca6c3c..5225b85020 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs +++ b/src/Avalonia.Base/Rendering/SceneGraph/DeferredDrawingContextImpl.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using Avalonia.Media; using Avalonia.Platform; using Avalonia.Utilities; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; using Avalonia.VisualTree; namespace Avalonia.Rendering.SceneGraph diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/DrawOperation.cs b/src/Avalonia.Base/Rendering/SceneGraph/DrawOperation.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/DrawOperation.cs rename to src/Avalonia.Base/Rendering/SceneGraph/DrawOperation.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/EllipseNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/EllipseNode.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/EllipseNode.cs rename to src/Avalonia.Base/Rendering/SceneGraph/EllipseNode.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/ExperimentalAcrylicNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/ExperimentalAcrylicNode.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/ExperimentalAcrylicNode.cs rename to src/Avalonia.Base/Rendering/SceneGraph/ExperimentalAcrylicNode.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryBoundsHelper.cs b/src/Avalonia.Base/Rendering/SceneGraph/GeometryBoundsHelper.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/GeometryBoundsHelper.cs rename to src/Avalonia.Base/Rendering/SceneGraph/GeometryBoundsHelper.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryClipNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/GeometryClipNode.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/GeometryClipNode.cs rename to src/Avalonia.Base/Rendering/SceneGraph/GeometryClipNode.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/GeometryNode.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs rename to src/Avalonia.Base/Rendering/SceneGraph/GeometryNode.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/GlyphRunNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/GlyphRunNode.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/GlyphRunNode.cs rename to src/Avalonia.Base/Rendering/SceneGraph/GlyphRunNode.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/IDrawOperation.cs b/src/Avalonia.Base/Rendering/SceneGraph/IDrawOperation.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/IDrawOperation.cs rename to src/Avalonia.Base/Rendering/SceneGraph/IDrawOperation.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/ISceneBuilder.cs b/src/Avalonia.Base/Rendering/SceneGraph/ISceneBuilder.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/ISceneBuilder.cs rename to src/Avalonia.Base/Rendering/SceneGraph/ISceneBuilder.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/IVisualNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/IVisualNode.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/IVisualNode.cs rename to src/Avalonia.Base/Rendering/SceneGraph/IVisualNode.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/ImageNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/ImageNode.cs similarity index 99% rename from src/Avalonia.Visuals/Rendering/SceneGraph/ImageNode.cs rename to src/Avalonia.Base/Rendering/SceneGraph/ImageNode.cs index d3da19d8c9..23267166a5 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/ImageNode.cs +++ b/src/Avalonia.Base/Rendering/SceneGraph/ImageNode.cs @@ -1,6 +1,6 @@ using Avalonia.Platform; using Avalonia.Utilities; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; namespace Avalonia.Rendering.SceneGraph { diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/LineBoundsHelper.cs b/src/Avalonia.Base/Rendering/SceneGraph/LineBoundsHelper.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/LineBoundsHelper.cs rename to src/Avalonia.Base/Rendering/SceneGraph/LineBoundsHelper.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/LineNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/LineNode.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/LineNode.cs rename to src/Avalonia.Base/Rendering/SceneGraph/LineNode.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/OpacityMaskNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/OpacityMaskNode.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/OpacityMaskNode.cs rename to src/Avalonia.Base/Rendering/SceneGraph/OpacityMaskNode.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/OpacityNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/OpacityNode.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/OpacityNode.cs rename to src/Avalonia.Base/Rendering/SceneGraph/OpacityNode.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/RectangleNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/RectangleNode.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/RectangleNode.cs rename to src/Avalonia.Base/Rendering/SceneGraph/RectangleNode.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs b/src/Avalonia.Base/Rendering/SceneGraph/Scene.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs rename to src/Avalonia.Base/Rendering/SceneGraph/Scene.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs b/src/Avalonia.Base/Rendering/SceneGraph/SceneBuilder.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs rename to src/Avalonia.Base/Rendering/SceneGraph/SceneBuilder.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneLayer.cs b/src/Avalonia.Base/Rendering/SceneGraph/SceneLayer.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/SceneLayer.cs rename to src/Avalonia.Base/Rendering/SceneGraph/SceneLayer.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneLayers.cs b/src/Avalonia.Base/Rendering/SceneGraph/SceneLayers.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/SceneLayers.cs rename to src/Avalonia.Base/Rendering/SceneGraph/SceneLayers.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs b/src/Avalonia.Base/Rendering/SceneGraph/VisualNode.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs rename to src/Avalonia.Base/Rendering/SceneGraph/VisualNode.cs diff --git a/src/Avalonia.Visuals/Rendering/SceneInvalidatedEventArgs.cs b/src/Avalonia.Base/Rendering/SceneInvalidatedEventArgs.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SceneInvalidatedEventArgs.cs rename to src/Avalonia.Base/Rendering/SceneInvalidatedEventArgs.cs diff --git a/src/Avalonia.Visuals/Rendering/SleepLoopRenderTimer.cs b/src/Avalonia.Base/Rendering/SleepLoopRenderTimer.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/SleepLoopRenderTimer.cs rename to src/Avalonia.Base/Rendering/SleepLoopRenderTimer.cs diff --git a/src/Avalonia.Visuals/Rendering/UiThreadRenderTimer.cs b/src/Avalonia.Base/Rendering/UiThreadRenderTimer.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/UiThreadRenderTimer.cs rename to src/Avalonia.Base/Rendering/UiThreadRenderTimer.cs diff --git a/src/Avalonia.Visuals/Rendering/Utilities/TileBrushCalculator.cs b/src/Avalonia.Base/Rendering/Utilities/TileBrushCalculator.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/Utilities/TileBrushCalculator.cs rename to src/Avalonia.Base/Rendering/Utilities/TileBrushCalculator.cs diff --git a/src/Avalonia.Visuals/Rendering/ZIndexComparer.cs b/src/Avalonia.Base/Rendering/ZIndexComparer.cs similarity index 100% rename from src/Avalonia.Visuals/Rendering/ZIndexComparer.cs rename to src/Avalonia.Base/Rendering/ZIndexComparer.cs diff --git a/src/Avalonia.Visuals/RoundedRect.cs b/src/Avalonia.Base/RoundedRect.cs similarity index 100% rename from src/Avalonia.Visuals/RoundedRect.cs rename to src/Avalonia.Base/RoundedRect.cs diff --git a/src/Avalonia.Base/Settings.StyleCop b/src/Avalonia.Base/Settings.StyleCop deleted file mode 100644 index bb05f99bc1..0000000000 --- a/src/Avalonia.Base/Settings.StyleCop +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/Avalonia.Visuals/Size.cs b/src/Avalonia.Base/Size.cs similarity index 100% rename from src/Avalonia.Visuals/Size.cs rename to src/Avalonia.Base/Size.cs diff --git a/src/Avalonia.Styling/StyledElement.cs b/src/Avalonia.Base/StyledElement.cs similarity index 100% rename from src/Avalonia.Styling/StyledElement.cs rename to src/Avalonia.Base/StyledElement.cs diff --git a/src/Avalonia.Styling/StyledElementExtensions.cs b/src/Avalonia.Base/StyledElementExtensions.cs similarity index 100% rename from src/Avalonia.Styling/StyledElementExtensions.cs rename to src/Avalonia.Base/StyledElementExtensions.cs diff --git a/src/Avalonia.Styling/Styling/Activators/AndActivator.cs b/src/Avalonia.Base/Styling/Activators/AndActivator.cs similarity index 100% rename from src/Avalonia.Styling/Styling/Activators/AndActivator.cs rename to src/Avalonia.Base/Styling/Activators/AndActivator.cs diff --git a/src/Avalonia.Styling/Styling/Activators/AndActivatorBuilder.cs b/src/Avalonia.Base/Styling/Activators/AndActivatorBuilder.cs similarity index 100% rename from src/Avalonia.Styling/Styling/Activators/AndActivatorBuilder.cs rename to src/Avalonia.Base/Styling/Activators/AndActivatorBuilder.cs diff --git a/src/Avalonia.Styling/Styling/Activators/IStyleActivator.cs b/src/Avalonia.Base/Styling/Activators/IStyleActivator.cs similarity index 100% rename from src/Avalonia.Styling/Styling/Activators/IStyleActivator.cs rename to src/Avalonia.Base/Styling/Activators/IStyleActivator.cs diff --git a/src/Avalonia.Styling/Styling/Activators/IStyleActivatorSink.cs b/src/Avalonia.Base/Styling/Activators/IStyleActivatorSink.cs similarity index 100% rename from src/Avalonia.Styling/Styling/Activators/IStyleActivatorSink.cs rename to src/Avalonia.Base/Styling/Activators/IStyleActivatorSink.cs diff --git a/src/Avalonia.Styling/Styling/Activators/NotActivator.cs b/src/Avalonia.Base/Styling/Activators/NotActivator.cs similarity index 100% rename from src/Avalonia.Styling/Styling/Activators/NotActivator.cs rename to src/Avalonia.Base/Styling/Activators/NotActivator.cs diff --git a/src/Avalonia.Styling/Styling/Activators/NthChildActivator.cs b/src/Avalonia.Base/Styling/Activators/NthChildActivator.cs similarity index 100% rename from src/Avalonia.Styling/Styling/Activators/NthChildActivator.cs rename to src/Avalonia.Base/Styling/Activators/NthChildActivator.cs diff --git a/src/Avalonia.Styling/Styling/Activators/OrActivator.cs b/src/Avalonia.Base/Styling/Activators/OrActivator.cs similarity index 100% rename from src/Avalonia.Styling/Styling/Activators/OrActivator.cs rename to src/Avalonia.Base/Styling/Activators/OrActivator.cs diff --git a/src/Avalonia.Styling/Styling/Activators/OrActivatorBuilder.cs b/src/Avalonia.Base/Styling/Activators/OrActivatorBuilder.cs similarity index 100% rename from src/Avalonia.Styling/Styling/Activators/OrActivatorBuilder.cs rename to src/Avalonia.Base/Styling/Activators/OrActivatorBuilder.cs diff --git a/src/Avalonia.Styling/Styling/Activators/PropertyEqualsActivator.cs b/src/Avalonia.Base/Styling/Activators/PropertyEqualsActivator.cs similarity index 100% rename from src/Avalonia.Styling/Styling/Activators/PropertyEqualsActivator.cs rename to src/Avalonia.Base/Styling/Activators/PropertyEqualsActivator.cs diff --git a/src/Avalonia.Styling/Styling/Activators/StyleActivatorBase.cs b/src/Avalonia.Base/Styling/Activators/StyleActivatorBase.cs similarity index 100% rename from src/Avalonia.Styling/Styling/Activators/StyleActivatorBase.cs rename to src/Avalonia.Base/Styling/Activators/StyleActivatorBase.cs diff --git a/src/Avalonia.Styling/Styling/Activators/StyleClassActivator.cs b/src/Avalonia.Base/Styling/Activators/StyleClassActivator.cs similarity index 100% rename from src/Avalonia.Styling/Styling/Activators/StyleClassActivator.cs rename to src/Avalonia.Base/Styling/Activators/StyleClassActivator.cs diff --git a/src/Avalonia.Styling/Styling/ChildSelector.cs b/src/Avalonia.Base/Styling/ChildSelector.cs similarity index 100% rename from src/Avalonia.Styling/Styling/ChildSelector.cs rename to src/Avalonia.Base/Styling/ChildSelector.cs diff --git a/src/Avalonia.Styling/Styling/DescendentSelector.cs b/src/Avalonia.Base/Styling/DescendentSelector.cs similarity index 100% rename from src/Avalonia.Styling/Styling/DescendentSelector.cs rename to src/Avalonia.Base/Styling/DescendentSelector.cs diff --git a/src/Avalonia.Styling/Styling/IGlobalStyles.cs b/src/Avalonia.Base/Styling/IGlobalStyles.cs similarity index 100% rename from src/Avalonia.Styling/Styling/IGlobalStyles.cs rename to src/Avalonia.Base/Styling/IGlobalStyles.cs diff --git a/src/Avalonia.Styling/Styling/ISetter.cs b/src/Avalonia.Base/Styling/ISetter.cs similarity index 100% rename from src/Avalonia.Styling/Styling/ISetter.cs rename to src/Avalonia.Base/Styling/ISetter.cs diff --git a/src/Avalonia.Styling/Styling/ISetterInstance.cs b/src/Avalonia.Base/Styling/ISetterInstance.cs similarity index 100% rename from src/Avalonia.Styling/Styling/ISetterInstance.cs rename to src/Avalonia.Base/Styling/ISetterInstance.cs diff --git a/src/Avalonia.Styling/Styling/ISetterValue.cs b/src/Avalonia.Base/Styling/ISetterValue.cs similarity index 100% rename from src/Avalonia.Styling/Styling/ISetterValue.cs rename to src/Avalonia.Base/Styling/ISetterValue.cs diff --git a/src/Avalonia.Styling/Styling/IStyle.cs b/src/Avalonia.Base/Styling/IStyle.cs similarity index 100% rename from src/Avalonia.Styling/Styling/IStyle.cs rename to src/Avalonia.Base/Styling/IStyle.cs diff --git a/src/Avalonia.Styling/Styling/IStyleHost.cs b/src/Avalonia.Base/Styling/IStyleHost.cs similarity index 100% rename from src/Avalonia.Styling/Styling/IStyleHost.cs rename to src/Avalonia.Base/Styling/IStyleHost.cs diff --git a/src/Avalonia.Styling/Styling/IStyleInstance.cs b/src/Avalonia.Base/Styling/IStyleInstance.cs similarity index 100% rename from src/Avalonia.Styling/Styling/IStyleInstance.cs rename to src/Avalonia.Base/Styling/IStyleInstance.cs diff --git a/src/Avalonia.Styling/Styling/IStyleable.cs b/src/Avalonia.Base/Styling/IStyleable.cs similarity index 100% rename from src/Avalonia.Styling/Styling/IStyleable.cs rename to src/Avalonia.Base/Styling/IStyleable.cs diff --git a/src/Avalonia.Styling/Styling/IStyler.cs b/src/Avalonia.Base/Styling/IStyler.cs similarity index 100% rename from src/Avalonia.Styling/Styling/IStyler.cs rename to src/Avalonia.Base/Styling/IStyler.cs diff --git a/src/Avalonia.Styling/Styling/ITemplate.cs b/src/Avalonia.Base/Styling/ITemplate.cs similarity index 100% rename from src/Avalonia.Styling/Styling/ITemplate.cs rename to src/Avalonia.Base/Styling/ITemplate.cs diff --git a/src/Avalonia.Styling/Styling/ITemplatedControl.cs b/src/Avalonia.Base/Styling/ITemplatedControl.cs similarity index 100% rename from src/Avalonia.Styling/Styling/ITemplatedControl.cs rename to src/Avalonia.Base/Styling/ITemplatedControl.cs diff --git a/src/Avalonia.Styling/Styling/NotSelector.cs b/src/Avalonia.Base/Styling/NotSelector.cs similarity index 100% rename from src/Avalonia.Styling/Styling/NotSelector.cs rename to src/Avalonia.Base/Styling/NotSelector.cs diff --git a/src/Avalonia.Styling/Styling/NthChildSelector.cs b/src/Avalonia.Base/Styling/NthChildSelector.cs similarity index 100% rename from src/Avalonia.Styling/Styling/NthChildSelector.cs rename to src/Avalonia.Base/Styling/NthChildSelector.cs diff --git a/src/Avalonia.Styling/Styling/NthLastChildSelector.cs b/src/Avalonia.Base/Styling/NthLastChildSelector.cs similarity index 100% rename from src/Avalonia.Styling/Styling/NthLastChildSelector.cs rename to src/Avalonia.Base/Styling/NthLastChildSelector.cs diff --git a/src/Avalonia.Styling/Styling/OrSelector.cs b/src/Avalonia.Base/Styling/OrSelector.cs similarity index 100% rename from src/Avalonia.Styling/Styling/OrSelector.cs rename to src/Avalonia.Base/Styling/OrSelector.cs diff --git a/src/Avalonia.Styling/Styling/PropertyEqualsSelector.cs b/src/Avalonia.Base/Styling/PropertyEqualsSelector.cs similarity index 100% rename from src/Avalonia.Styling/Styling/PropertyEqualsSelector.cs rename to src/Avalonia.Base/Styling/PropertyEqualsSelector.cs diff --git a/src/Avalonia.Styling/Styling/PropertySetterBindingInstance.cs b/src/Avalonia.Base/Styling/PropertySetterBindingInstance.cs similarity index 100% rename from src/Avalonia.Styling/Styling/PropertySetterBindingInstance.cs rename to src/Avalonia.Base/Styling/PropertySetterBindingInstance.cs diff --git a/src/Avalonia.Styling/Styling/PropertySetterInstance.cs b/src/Avalonia.Base/Styling/PropertySetterInstance.cs similarity index 100% rename from src/Avalonia.Styling/Styling/PropertySetterInstance.cs rename to src/Avalonia.Base/Styling/PropertySetterInstance.cs diff --git a/src/Avalonia.Styling/Styling/PropertySetterLazyInstance.cs b/src/Avalonia.Base/Styling/PropertySetterLazyInstance.cs similarity index 100% rename from src/Avalonia.Styling/Styling/PropertySetterLazyInstance.cs rename to src/Avalonia.Base/Styling/PropertySetterLazyInstance.cs diff --git a/src/Avalonia.Styling/Styling/Selector.cs b/src/Avalonia.Base/Styling/Selector.cs similarity index 100% rename from src/Avalonia.Styling/Styling/Selector.cs rename to src/Avalonia.Base/Styling/Selector.cs diff --git a/src/Avalonia.Styling/Styling/SelectorMatch.cs b/src/Avalonia.Base/Styling/SelectorMatch.cs similarity index 100% rename from src/Avalonia.Styling/Styling/SelectorMatch.cs rename to src/Avalonia.Base/Styling/SelectorMatch.cs diff --git a/src/Avalonia.Styling/Styling/Selectors.cs b/src/Avalonia.Base/Styling/Selectors.cs similarity index 100% rename from src/Avalonia.Styling/Styling/Selectors.cs rename to src/Avalonia.Base/Styling/Selectors.cs diff --git a/src/Avalonia.Styling/Styling/Setter.cs b/src/Avalonia.Base/Styling/Setter.cs similarity index 100% rename from src/Avalonia.Styling/Styling/Setter.cs rename to src/Avalonia.Base/Styling/Setter.cs diff --git a/src/Avalonia.Styling/Styling/Style.cs b/src/Avalonia.Base/Styling/Style.cs similarity index 100% rename from src/Avalonia.Styling/Styling/Style.cs rename to src/Avalonia.Base/Styling/Style.cs diff --git a/src/Avalonia.Styling/Styling/StyleInstance.cs b/src/Avalonia.Base/Styling/StyleInstance.cs similarity index 100% rename from src/Avalonia.Styling/Styling/StyleInstance.cs rename to src/Avalonia.Base/Styling/StyleInstance.cs diff --git a/src/Avalonia.Styling/Styling/Styler.cs b/src/Avalonia.Base/Styling/Styler.cs similarity index 100% rename from src/Avalonia.Styling/Styling/Styler.cs rename to src/Avalonia.Base/Styling/Styler.cs diff --git a/src/Avalonia.Styling/Styling/Styles.cs b/src/Avalonia.Base/Styling/Styles.cs similarity index 100% rename from src/Avalonia.Styling/Styling/Styles.cs rename to src/Avalonia.Base/Styling/Styles.cs diff --git a/src/Avalonia.Styling/Styling/TemplateSelector.cs b/src/Avalonia.Base/Styling/TemplateSelector.cs similarity index 100% rename from src/Avalonia.Styling/Styling/TemplateSelector.cs rename to src/Avalonia.Base/Styling/TemplateSelector.cs diff --git a/src/Avalonia.Styling/Styling/TypeNameAndClassSelector.cs b/src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs similarity index 100% rename from src/Avalonia.Styling/Styling/TypeNameAndClassSelector.cs rename to src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs diff --git a/src/Avalonia.Visuals/Thickness.cs b/src/Avalonia.Base/Thickness.cs similarity index 100% rename from src/Avalonia.Visuals/Thickness.cs rename to src/Avalonia.Base/Thickness.cs diff --git a/src/Avalonia.Visuals/Utilities/ArrayBuilder.cs b/src/Avalonia.Base/Utilities/ArrayBuilder.cs similarity index 100% rename from src/Avalonia.Visuals/Utilities/ArrayBuilder.cs rename to src/Avalonia.Base/Utilities/ArrayBuilder.cs diff --git a/src/Avalonia.Visuals/Utilities/ArraySlice.cs b/src/Avalonia.Base/Utilities/ArraySlice.cs similarity index 100% rename from src/Avalonia.Visuals/Utilities/ArraySlice.cs rename to src/Avalonia.Base/Utilities/ArraySlice.cs diff --git a/src/Avalonia.Visuals/Utilities/BinarySearchExtension.cs b/src/Avalonia.Base/Utilities/BinarySearchExtension.cs similarity index 100% rename from src/Avalonia.Visuals/Utilities/BinarySearchExtension.cs rename to src/Avalonia.Base/Utilities/BinarySearchExtension.cs diff --git a/src/Avalonia.Visuals/Utilities/FrugalList.cs b/src/Avalonia.Base/Utilities/FrugalList.cs similarity index 100% rename from src/Avalonia.Visuals/Utilities/FrugalList.cs rename to src/Avalonia.Base/Utilities/FrugalList.cs diff --git a/src/Avalonia.Visuals/Utilities/MappedArraySlice.cs b/src/Avalonia.Base/Utilities/MappedArraySlice.cs similarity index 100% rename from src/Avalonia.Visuals/Utilities/MappedArraySlice.cs rename to src/Avalonia.Base/Utilities/MappedArraySlice.cs diff --git a/src/Avalonia.Visuals/Utilities/ReadOnlySlice.cs b/src/Avalonia.Base/Utilities/ReadOnlySlice.cs similarity index 100% rename from src/Avalonia.Visuals/Utilities/ReadOnlySlice.cs rename to src/Avalonia.Base/Utilities/ReadOnlySlice.cs diff --git a/src/Avalonia.Visuals/Utilities/Span.cs b/src/Avalonia.Base/Utilities/Span.cs similarity index 100% rename from src/Avalonia.Visuals/Utilities/Span.cs rename to src/Avalonia.Base/Utilities/Span.cs diff --git a/src/Avalonia.Visuals/Utilities/ValueSpan.cs b/src/Avalonia.Base/Utilities/ValueSpan.cs similarity index 100% rename from src/Avalonia.Visuals/Utilities/ValueSpan.cs rename to src/Avalonia.Base/Utilities/ValueSpan.cs diff --git a/src/Avalonia.Visuals/Vector.cs b/src/Avalonia.Base/Vector.cs similarity index 100% rename from src/Avalonia.Visuals/Vector.cs rename to src/Avalonia.Base/Vector.cs diff --git a/src/Avalonia.Visuals/Visual.cs b/src/Avalonia.Base/Visual.cs similarity index 100% rename from src/Avalonia.Visuals/Visual.cs rename to src/Avalonia.Base/Visual.cs diff --git a/src/Avalonia.Visuals/VisualExtensions.cs b/src/Avalonia.Base/VisualExtensions.cs similarity index 100% rename from src/Avalonia.Visuals/VisualExtensions.cs rename to src/Avalonia.Base/VisualExtensions.cs diff --git a/src/Avalonia.Visuals/VisualTree/IHostedVisualTreeRoot.cs b/src/Avalonia.Base/VisualTree/IHostedVisualTreeRoot.cs similarity index 100% rename from src/Avalonia.Visuals/VisualTree/IHostedVisualTreeRoot.cs rename to src/Avalonia.Base/VisualTree/IHostedVisualTreeRoot.cs diff --git a/src/Avalonia.Visuals/VisualTree/IVisual.cs b/src/Avalonia.Base/VisualTree/IVisual.cs similarity index 100% rename from src/Avalonia.Visuals/VisualTree/IVisual.cs rename to src/Avalonia.Base/VisualTree/IVisual.cs diff --git a/src/Avalonia.Visuals/VisualTree/IVisualTreeHost.cs b/src/Avalonia.Base/VisualTree/IVisualTreeHost.cs similarity index 100% rename from src/Avalonia.Visuals/VisualTree/IVisualTreeHost.cs rename to src/Avalonia.Base/VisualTree/IVisualTreeHost.cs diff --git a/src/Avalonia.Visuals/VisualTree/IVisualWithRoundRectClip.cs b/src/Avalonia.Base/VisualTree/IVisualWithRoundRectClip.cs similarity index 100% rename from src/Avalonia.Visuals/VisualTree/IVisualWithRoundRectClip.cs rename to src/Avalonia.Base/VisualTree/IVisualWithRoundRectClip.cs diff --git a/src/Avalonia.Visuals/VisualTree/TransformedBounds.cs b/src/Avalonia.Base/VisualTree/TransformedBounds.cs similarity index 100% rename from src/Avalonia.Visuals/VisualTree/TransformedBounds.cs rename to src/Avalonia.Base/VisualTree/TransformedBounds.cs diff --git a/src/Avalonia.Visuals/VisualTree/VisualExtensions.cs b/src/Avalonia.Base/VisualTree/VisualExtensions.cs similarity index 100% rename from src/Avalonia.Visuals/VisualTree/VisualExtensions.cs rename to src/Avalonia.Base/VisualTree/VisualExtensions.cs diff --git a/src/Avalonia.Visuals/VisualTree/VisualLocator.cs b/src/Avalonia.Base/VisualTree/VisualLocator.cs similarity index 100% rename from src/Avalonia.Visuals/VisualTree/VisualLocator.cs rename to src/Avalonia.Base/VisualTree/VisualLocator.cs diff --git a/src/Avalonia.Visuals/VisualTreeAttachmentEventArgs.cs b/src/Avalonia.Base/VisualTreeAttachmentEventArgs.cs similarity index 100% rename from src/Avalonia.Visuals/VisualTreeAttachmentEventArgs.cs rename to src/Avalonia.Base/VisualTreeAttachmentEventArgs.cs diff --git a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj index 9629324c8d..6267c74df9 100644 --- a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj +++ b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj @@ -62,34 +62,34 @@ Markup/%(RecursiveDir)%(FileName)%(Extension) - + Markup/%(RecursiveDir)%(FileName)%(Extension) - + Markup/%(RecursiveDir)%(FileName)%(Extension) - + Markup/%(RecursiveDir)%(FileName)%(Extension) - + Markup/%(RecursiveDir)%(FileName)%(Extension) - + Markup/%(RecursiveDir)%(FileName)%(Extension) - + Markup/%(RecursiveDir)%(FileName)%(Extension) - + Markup/%(RecursiveDir)%(FileName)%(Extension) - + Markup/%(RecursiveDir)%(FileName)%(Extension) - + Markup/%(RecursiveDir)%(FileName)%(Extension) - + Markup/%(RecursiveDir)%(FileName)%(Extension) diff --git a/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj b/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj index 0438e4e0e8..410cac72fc 100644 --- a/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj +++ b/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj @@ -4,14 +4,8 @@ Avalonia.Controls.DataGrid - - - - - - diff --git a/src/Avalonia.Controls/Avalonia.Controls.csproj b/src/Avalonia.Controls/Avalonia.Controls.csproj index 5d34261bdf..4d239e69f4 100644 --- a/src/Avalonia.Controls/Avalonia.Controls.csproj +++ b/src/Avalonia.Controls/Avalonia.Controls.csproj @@ -6,14 +6,8 @@ - - - - - - diff --git a/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj b/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj index f8a7cdc690..0270000d8c 100644 --- a/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj +++ b/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj @@ -16,14 +16,8 @@ - - - - - - diff --git a/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj b/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj index bb908e07dc..2fb7c07b6f 100644 --- a/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj +++ b/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj @@ -16,14 +16,8 @@ - - - - - - diff --git a/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs b/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs index 90221bb922..addc248d58 100644 --- a/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs +++ b/src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs @@ -7,7 +7,7 @@ using Avalonia.Platform; using Avalonia.Rendering; using Avalonia.Rendering.SceneGraph; using Avalonia.Utilities; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; namespace Avalonia.Headless { diff --git a/src/Avalonia.Input/ApiCompatBaseline.txt b/src/Avalonia.Input/ApiCompatBaseline.txt deleted file mode 100644 index 93c4cba5a6..0000000000 --- a/src/Avalonia.Input/ApiCompatBaseline.txt +++ /dev/null @@ -1,30 +0,0 @@ -Compat issues with assembly Avalonia.Input: -MembersMustExist : Member 'public Avalonia.Platform.IPlatformHandle Avalonia.Input.Cursor.PlatformCursor.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Interactivity.RoutedEvent Avalonia.Interactivity.RoutedEvent Avalonia.Input.Gestures.ScrollGestureEndedEvent' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Interactivity.RoutedEvent Avalonia.Interactivity.RoutedEvent Avalonia.Input.Gestures.DoubleTappedEvent' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Interactivity.RoutedEvent Avalonia.Interactivity.RoutedEvent Avalonia.Input.Gestures.RightTappedEvent' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Interactivity.RoutedEvent Avalonia.Interactivity.RoutedEvent Avalonia.Input.Gestures.TappedEvent' does not exist in the implementation but it does exist in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Input.IFocusManager.RemoveFocusScope(Avalonia.Input.IFocusScope)' is present in the implementation but not in the contract. -MembersMustExist : Member 'public Avalonia.Interactivity.RoutedEvent Avalonia.Interactivity.RoutedEvent Avalonia.Input.InputElement.TextInputOptionsQueryEvent' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Interactivity.RoutedEvent Avalonia.Interactivity.RoutedEvent Avalonia.Input.InputElement.DoubleTappedEvent' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Interactivity.RoutedEvent Avalonia.Interactivity.RoutedEvent Avalonia.Input.InputElement.TappedEvent' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Input.InputElement.add_DoubleTapped(System.EventHandler)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Input.InputElement.add_Tapped(System.EventHandler)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Input.InputElement.add_TextInputOptionsQuery(System.EventHandler)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Input.InputElement.remove_DoubleTapped(System.EventHandler)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Input.InputElement.remove_Tapped(System.EventHandler)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Input.InputElement.remove_TextInputOptionsQuery(System.EventHandler)' does not exist in the implementation but it does exist in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Input.TextInput.ITextInputMethodImpl.SetActive(System.Boolean)' is present in the contract but not in the implementation. -MembersMustExist : Member 'public void Avalonia.Input.TextInput.ITextInputMethodImpl.SetActive(System.Boolean)' does not exist in the implementation but it does exist in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Input.TextInput.ITextInputMethodImpl.SetClient(Avalonia.Input.TextInput.ITextInputMethodClient)' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Input.TextInput.ITextInputMethodImpl.SetOptions(Avalonia.Input.TextInput.TextInputOptions)' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Input.TextInput.ITextInputMethodImpl.SetOptions(Avalonia.Input.TextInput.TextInputOptionsQueryEventArgs)' is present in the contract but not in the implementation. -MembersMustExist : Member 'public void Avalonia.Input.TextInput.ITextInputMethodImpl.SetOptions(Avalonia.Input.TextInput.TextInputOptionsQueryEventArgs)' does not exist in the implementation but it does exist in the contract. -EnumValuesMustMatch : Enum value 'Avalonia.Input.TextInput.TextInputContentType Avalonia.Input.TextInput.TextInputContentType.Email' is (System.Int32)5 in the implementation but (System.Int32)1 in the contract. -EnumValuesMustMatch : Enum value 'Avalonia.Input.TextInput.TextInputContentType Avalonia.Input.TextInput.TextInputContentType.Number' is (System.Int32)4 in the implementation but (System.Int32)3 in the contract. -EnumValuesMustMatch : Enum value 'Avalonia.Input.TextInput.TextInputContentType Avalonia.Input.TextInput.TextInputContentType.Password' is (System.Int32)8 in the implementation but (System.Int32)5 in the contract. -MembersMustExist : Member 'public Avalonia.Input.TextInput.TextInputContentType Avalonia.Input.TextInput.TextInputContentType Avalonia.Input.TextInput.TextInputContentType.Phone' does not exist in the implementation but it does exist in the contract. -EnumValuesMustMatch : Enum value 'Avalonia.Input.TextInput.TextInputContentType Avalonia.Input.TextInput.TextInputContentType.Url' is (System.Int32)6 in the implementation but (System.Int32)4 in the contract. -TypesMustExist : Type 'Avalonia.Input.TextInput.TextInputOptionsQueryEventArgs' does not exist in the implementation but it does exist in the contract. -TypesMustExist : Type 'Avalonia.Platform.IStandardCursorFactory' does not exist in the implementation but it does exist in the contract. -Total Issues: 28 diff --git a/src/Avalonia.Input/Avalonia.Input.csproj b/src/Avalonia.Input/Avalonia.Input.csproj deleted file mode 100644 index 6e0321b3aa..0000000000 --- a/src/Avalonia.Input/Avalonia.Input.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - net6.0;netstandard2.0 - - - - - - - - - - - - - - - - diff --git a/src/Avalonia.Input/Properties/AssemblyInfo.cs b/src/Avalonia.Input/Properties/AssemblyInfo.cs deleted file mode 100644 index a7126d6ba9..0000000000 --- a/src/Avalonia.Input/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using Avalonia.Metadata; - -[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Input")] -[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Input.TextInput")] -[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Input.GestureRecognizers")] - -[assembly: InternalsVisibleTo("Avalonia.Controls, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.Controls.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] diff --git a/src/Avalonia.Interactivity/ApiCompatBaseline.txt b/src/Avalonia.Interactivity/ApiCompatBaseline.txt deleted file mode 100644 index fcc74cf864..0000000000 --- a/src/Avalonia.Interactivity/ApiCompatBaseline.txt +++ /dev/null @@ -1 +0,0 @@ -Total Issues: 0 diff --git a/src/Avalonia.Interactivity/Avalonia.Interactivity.csproj b/src/Avalonia.Interactivity/Avalonia.Interactivity.csproj deleted file mode 100644 index fa115ed980..0000000000 --- a/src/Avalonia.Interactivity/Avalonia.Interactivity.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - net6.0;netstandard2.0 - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Avalonia.Layout/ApiCompatBaseline.txt b/src/Avalonia.Layout/ApiCompatBaseline.txt deleted file mode 100644 index fcc74cf864..0000000000 --- a/src/Avalonia.Layout/ApiCompatBaseline.txt +++ /dev/null @@ -1 +0,0 @@ -Total Issues: 0 diff --git a/src/Avalonia.Layout/Avalonia.Layout.csproj b/src/Avalonia.Layout/Avalonia.Layout.csproj deleted file mode 100644 index d94c3eb101..0000000000 --- a/src/Avalonia.Layout/Avalonia.Layout.csproj +++ /dev/null @@ -1,14 +0,0 @@ - - - net6.0;netstandard2.0 - - - - - - - - - - - diff --git a/src/Avalonia.Layout/Properties/AssemblyInfo.cs b/src/Avalonia.Layout/Properties/AssemblyInfo.cs deleted file mode 100644 index efcbf184b5..0000000000 --- a/src/Avalonia.Layout/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,7 +0,0 @@ -using System.Runtime.CompilerServices; -using Avalonia.Metadata; - -[assembly: InternalsVisibleTo("Avalonia.Layout.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] - -[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Layout")] - diff --git a/src/Avalonia.OpenGL/Avalonia.OpenGL.csproj b/src/Avalonia.OpenGL/Avalonia.OpenGL.csproj index 07cd295711..76924d060f 100644 --- a/src/Avalonia.OpenGL/Avalonia.OpenGL.csproj +++ b/src/Avalonia.OpenGL/Avalonia.OpenGL.csproj @@ -8,7 +8,6 @@ - diff --git a/src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj b/src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj index 5a8301a2e9..d746c6db7e 100644 --- a/src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj +++ b/src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj @@ -7,7 +7,7 @@ Avalonia.Remote.Protocol - + \ No newline at end of file diff --git a/src/Avalonia.Styling/ApiCompatBaseline.txt b/src/Avalonia.Styling/ApiCompatBaseline.txt deleted file mode 100644 index 0eedc3e360..0000000000 --- a/src/Avalonia.Styling/ApiCompatBaseline.txt +++ /dev/null @@ -1,4 +0,0 @@ -Compat issues with assembly Avalonia.Styling: -InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Styling.IStyleInstance.IsActive' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Styling.IStyleInstance.IsActive.get()' is present in the implementation but not in the contract. -Total Issues: 2 diff --git a/src/Avalonia.Styling/Avalonia.Styling.csproj b/src/Avalonia.Styling/Avalonia.Styling.csproj deleted file mode 100644 index 260cfb0516..0000000000 --- a/src/Avalonia.Styling/Avalonia.Styling.csproj +++ /dev/null @@ -1,14 +0,0 @@ - - - net6.0;netstandard2.0 - Avalonia.Styling - Avalonia - - - - - - - - - diff --git a/src/Avalonia.Styling/Properties/AssemblyInfo.cs b/src/Avalonia.Styling/Properties/AssemblyInfo.cs deleted file mode 100644 index ab034740ed..0000000000 --- a/src/Avalonia.Styling/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Runtime.CompilerServices; -using Avalonia.Metadata; - -[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls")] -[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.LogicalTree")] -[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Styling")] - -[assembly: InternalsVisibleTo("Avalonia.Styling.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] - diff --git a/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj b/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj index ef200b5532..40ed4a0f87 100644 --- a/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj +++ b/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj @@ -3,16 +3,10 @@ net6.0;netstandard2.0 - - - - - - - + diff --git a/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj b/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj index 885661536c..35603fe216 100644 --- a/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj +++ b/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj @@ -3,16 +3,10 @@ net6.0;netstandard2.0 - - - - - - - + diff --git a/src/Avalonia.Visuals/ApiCompatBaseline.txt b/src/Avalonia.Visuals/ApiCompatBaseline.txt deleted file mode 100644 index 0e82ef7367..0000000000 --- a/src/Avalonia.Visuals/ApiCompatBaseline.txt +++ /dev/null @@ -1,188 +0,0 @@ -Compat issues with assembly Avalonia.Visuals: -MembersMustExist : Member 'public System.Threading.Tasks.Task Avalonia.Animation.CompositePageTransition.Start(Avalonia.Visual, Avalonia.Visual, System.Boolean)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public System.Threading.Tasks.Task Avalonia.Animation.CrossFade.Start(Avalonia.Visual, Avalonia.Visual)' does not exist in the implementation but it does exist in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public System.Threading.Tasks.Task Avalonia.Animation.IPageTransition.Start(Avalonia.Visual, Avalonia.Visual, System.Boolean)' is present in the contract but not in the implementation. -MembersMustExist : Member 'public System.Threading.Tasks.Task Avalonia.Animation.IPageTransition.Start(Avalonia.Visual, Avalonia.Visual, System.Boolean)' does not exist in the implementation but it does exist in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public System.Threading.Tasks.Task Avalonia.Animation.IPageTransition.Start(Avalonia.Visual, Avalonia.Visual, System.Boolean, System.Threading.CancellationToken)' is present in the implementation but not in the contract. -MembersMustExist : Member 'public System.Threading.Tasks.Task Avalonia.Animation.PageSlide.Start(Avalonia.Visual, Avalonia.Visual, System.Boolean)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.DrawingContext.DrawText(Avalonia.Media.IBrush, Avalonia.Point, Avalonia.Media.FormattedText)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public System.Boolean Avalonia.Media.FontManager.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Typeface)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.FormattedText..ctor()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.FormattedText..ctor(Avalonia.Platform.IPlatformRenderInterface)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.FormattedText..ctor(System.String, Avalonia.Media.Typeface, System.Double, Avalonia.Media.TextAlignment, Avalonia.Media.TextWrapping, Avalonia.Size)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Rect Avalonia.Media.FormattedText.Bounds.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Size Avalonia.Media.FormattedText.Constraint.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.FormattedText.Constraint.set(Avalonia.Size)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public System.Double Avalonia.Media.FormattedText.FontSize.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.FormattedText.FontSize.set(System.Double)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public System.Collections.Generic.IEnumerable Avalonia.Media.FormattedText.GetLines()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Media.TextHitTestResult Avalonia.Media.FormattedText.HitTestPoint(Avalonia.Point)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Rect Avalonia.Media.FormattedText.HitTestTextPosition(System.Int32)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public System.Collections.Generic.IEnumerable Avalonia.Media.FormattedText.HitTestTextRange(System.Int32, System.Int32)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Platform.IFormattedTextImpl Avalonia.Media.FormattedText.PlatformImpl.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public System.Collections.Generic.IReadOnlyList Avalonia.Media.FormattedText.Spans.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.FormattedText.Spans.set(System.Collections.Generic.IReadOnlyList)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public System.String Avalonia.Media.FormattedText.Text.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.FormattedText.Text.set(System.String)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Media.TextWrapping Avalonia.Media.FormattedText.TextWrapping.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.FormattedText.TextWrapping.set(Avalonia.Media.TextWrapping)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Media.Typeface Avalonia.Media.FormattedText.Typeface.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.FormattedText.Typeface.set(Avalonia.Media.Typeface)' does not exist in the implementation but it does exist in the contract. -TypesMustExist : Type 'Avalonia.Media.FormattedTextLine' does not exist in the implementation but it does exist in the contract. -TypesMustExist : Type 'Avalonia.Media.FormattedTextStyleSpan' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.GlyphRun..ctor()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.GlyphRun..ctor(Avalonia.Media.GlyphTypeface, System.Double, Avalonia.Utilities.ReadOnlySlice, Avalonia.Utilities.ReadOnlySlice, Avalonia.Utilities.ReadOnlySlice, Avalonia.Utilities.ReadOnlySlice, Avalonia.Utilities.ReadOnlySlice, System.Int32)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Utilities.ReadOnlySlice Avalonia.Media.GlyphRun.GlyphAdvances.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.GlyphRun.GlyphAdvances.set(Avalonia.Utilities.ReadOnlySlice)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Utilities.ReadOnlySlice Avalonia.Media.GlyphRun.GlyphClusters.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.GlyphRun.GlyphClusters.set(Avalonia.Utilities.ReadOnlySlice)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Utilities.ReadOnlySlice Avalonia.Media.GlyphRun.GlyphIndices.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.GlyphRun.GlyphIndices.set(Avalonia.Utilities.ReadOnlySlice)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Utilities.ReadOnlySlice Avalonia.Media.GlyphRun.GlyphOffsets.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.GlyphRun.GlyphOffsets.set(Avalonia.Utilities.ReadOnlySlice)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.GlyphRun.GlyphTypeface.set(Avalonia.Media.GlyphTypeface)' does not exist in the implementation but it does exist in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Media.ITransform Avalonia.Media.IBrush.Transform' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Media.ITransform Avalonia.Media.IBrush.Transform.get()' is present in the implementation but not in the contract. -CannotSealType : Type 'Avalonia.Media.Pen' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract. -MembersMustExist : Member 'protected void Avalonia.Media.Pen.AffectsRender(Avalonia.AvaloniaProperty[])' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'protected void Avalonia.Media.Pen.RaiseInvalidated(System.EventArgs)' does not exist in the implementation but it does exist in the contract. -CannotSealType : Type 'Avalonia.Media.TextHitTestResult' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract. -TypeCannotChangeClassification : Type 'Avalonia.Media.TextHitTestResult' is a 'struct' in the implementation but is a 'class' in the contract. -MembersMustExist : Member 'public void Avalonia.Media.TextHitTestResult..ctor()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.TextHitTestResult.IsInside.set(System.Boolean)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.TextHitTestResult.IsTrailing.set(System.Boolean)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.TextHitTestResult.TextPosition.set(System.Int32)' does not exist in the implementation but it does exist in the contract. -CannotMakeTypeAbstract : Type 'Avalonia.Media.TextTrimming' is abstract in the implementation but is not abstract in the contract. -TypeCannotChangeClassification : Type 'Avalonia.Media.TextTrimming' is a 'class' in the implementation but is a 'struct' in the contract. -MembersMustExist : Member 'public Avalonia.Media.TextTrimming Avalonia.Media.TextTrimming Avalonia.Media.TextTrimming.CharacterEllipsis' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Media.TextTrimming Avalonia.Media.TextTrimming Avalonia.Media.TextTrimming.None' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Media.TextTrimming Avalonia.Media.TextTrimming Avalonia.Media.TextTrimming.WordEllipsis' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public System.Int32 System.Int32 Avalonia.Media.TextTrimming.value__' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.Typeface..ctor(Avalonia.Media.FontFamily, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.Typeface..ctor(System.String, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.Immutable.ImmutableConicGradientBrush..ctor(System.Collections.Generic.IReadOnlyList, System.Double, Avalonia.Media.GradientSpreadMethod, System.Nullable, System.Double)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'protected void Avalonia.Media.Immutable.ImmutableGradientBrush..ctor(System.Collections.Generic.IReadOnlyList, System.Double, Avalonia.Media.GradientSpreadMethod)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.Immutable.ImmutableLinearGradientBrush..ctor(System.Collections.Generic.IReadOnlyList, System.Double, Avalonia.Media.GradientSpreadMethod, System.Nullable, System.Nullable)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.Immutable.ImmutableRadialGradientBrush..ctor(System.Collections.Generic.IReadOnlyList, System.Double, Avalonia.Media.GradientSpreadMethod, System.Nullable, System.Nullable, System.Double)' does not exist in the implementation but it does exist in the contract. -TypeCannotChangeClassification : Type 'Avalonia.Media.Immutable.ImmutableSolidColorBrush' is a 'class' in the implementation but is a 'struct' in the contract. -MembersMustExist : Member 'public void Avalonia.Media.Immutable.ImmutableSolidColorBrush..ctor(Avalonia.Media.Color, System.Double)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'protected void Avalonia.Media.Immutable.ImmutableTileBrush..ctor(Avalonia.Media.AlignmentX, Avalonia.Media.AlignmentY, Avalonia.RelativeRect, System.Double, Avalonia.RelativeRect, Avalonia.Media.Stretch, Avalonia.Media.TileMode, Avalonia.Visuals.Media.Imaging.BitmapInterpolationMode)' does not exist in the implementation but it does exist in the contract. -CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.DrawableTextRun.Baseline' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.DrawableTextRun.Baseline.get()' is abstract in the implementation but is missing in the contract. -MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext)' does not exist in the implementation but it does exist in the contract. -CannotAddAbstractMembers : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' is abstract in the implementation but is missing in the contract. -CannotSealType : Type 'Avalonia.Media.TextFormatting.GenericTextParagraphProperties' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract. -CannotMakeMemberNonVirtual : Member 'public Avalonia.Media.TextFormatting.TextRunProperties Avalonia.Media.TextFormatting.GenericTextParagraphProperties.DefaultTextRunProperties' is non-virtual in the implementation but is virtual in the contract. -CannotMakeMemberNonVirtual : Member 'public System.Double Avalonia.Media.TextFormatting.GenericTextParagraphProperties.LineHeight' is non-virtual in the implementation but is virtual in the contract. -CannotMakeMemberNonVirtual : Member 'public Avalonia.Media.TextAlignment Avalonia.Media.TextFormatting.GenericTextParagraphProperties.TextAlignment' is non-virtual in the implementation but is virtual in the contract. -CannotMakeMemberNonVirtual : Member 'public Avalonia.Media.TextWrapping Avalonia.Media.TextFormatting.GenericTextParagraphProperties.TextWrapping' is non-virtual in the implementation but is virtual in the contract. -CannotMakeMemberNonVirtual : Member 'public Avalonia.Media.TextFormatting.TextRunProperties Avalonia.Media.TextFormatting.GenericTextParagraphProperties.DefaultTextRunProperties.get()' is non-virtual in the implementation but is virtual in the contract. -CannotMakeMemberNonVirtual : Member 'public System.Double Avalonia.Media.TextFormatting.GenericTextParagraphProperties.LineHeight.get()' is non-virtual in the implementation but is virtual in the contract. -CannotMakeMemberNonVirtual : Member 'public Avalonia.Media.TextAlignment Avalonia.Media.TextFormatting.GenericTextParagraphProperties.TextAlignment.get()' is non-virtual in the implementation but is virtual in the contract. -CannotMakeMemberNonVirtual : Member 'public Avalonia.Media.TextWrapping Avalonia.Media.TextFormatting.GenericTextParagraphProperties.TextWrapping.get()' is non-virtual in the implementation but is virtual in the contract. -MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.GenericTextRunProperties..ctor(Avalonia.Media.Typeface, System.Double, Avalonia.Media.TextDecorationCollection, Avalonia.Media.IBrush, Avalonia.Media.IBrush, System.Globalization.CultureInfo)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.ShapeableTextCharacters..ctor(Avalonia.Utilities.ReadOnlySlice, Avalonia.Media.TextFormatting.TextRunProperties)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.ShapedTextCharacters..ctor(Avalonia.Media.GlyphRun, Avalonia.Media.TextFormatting.TextRunProperties)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.ShapedTextCharacters.Draw(Avalonia.Media.DrawingContext)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Media.TextFormatting.ShapedTextCharacters.SplitTextCharactersResult Avalonia.Media.TextFormatting.ShapedTextCharacters.Split(System.Int32)' does not exist in the implementation but it does exist in the contract. -TypesMustExist : Type 'Avalonia.Media.TextFormatting.ShapedTextCharacters.SplitTextCharactersResult' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'protected System.Boolean Avalonia.Media.TextFormatting.TextCharacters.TryGetRunProperties(Avalonia.Utilities.ReadOnlySlice, Avalonia.Media.Typeface, Avalonia.Media.Typeface, System.Int32)' does not exist in the implementation but it does exist in the contract. -CannotAddAbstractMembers : Member 'public System.Collections.Generic.List Avalonia.Media.TextFormatting.TextCollapsingProperties.Collapse(Avalonia.Media.TextFormatting.TextLine)' is abstract in the implementation but is missing in the contract. -MembersMustExist : Member 'public Avalonia.Media.TextFormatting.TextCollapsingStyle Avalonia.Media.TextFormatting.TextCollapsingProperties.Style.get()' does not exist in the implementation but it does exist in the contract. -TypesMustExist : Type 'Avalonia.Media.TextFormatting.TextCollapsingStyle' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextEndOfLine..ctor()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextLayout..ctor(System.String, Avalonia.Media.Typeface, System.Double, Avalonia.Media.IBrush, Avalonia.Media.TextAlignment, Avalonia.Media.TextWrapping, Avalonia.Media.TextTrimming, Avalonia.Media.TextDecorationCollection, System.Double, System.Double, System.Double, System.Int32, System.Collections.Generic.IReadOnlyList>)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextLayout.Draw(Avalonia.Media.DrawingContext)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Size Avalonia.Media.TextFormatting.TextLayout.Size.get()' does not exist in the implementation but it does exist in the contract. -CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.Baseline' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.Extent' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Int32 Avalonia.Media.TextFormatting.TextLine.FirstTextSourceIndex' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Boolean Avalonia.Media.TextFormatting.TextLine.HasOverflowed' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.Height' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Int32 Avalonia.Media.TextFormatting.TextLine.Length' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Int32 Avalonia.Media.TextFormatting.TextLine.NewLineLength' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.OverhangAfter' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.OverhangLeading' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.OverhangTrailing' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.Start' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Int32 Avalonia.Media.TextFormatting.TextLine.TrailingWhitespaceLength' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.Width' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.WidthIncludingTrailingWhitespace' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.Baseline.get()' is abstract in the implementation but is missing in the contract. -MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextLine.Draw(Avalonia.Media.DrawingContext)' does not exist in the implementation but it does exist in the contract. -CannotAddAbstractMembers : Member 'public void Avalonia.Media.TextFormatting.TextLine.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.Extent.get()' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Int32 Avalonia.Media.TextFormatting.TextLine.FirstTextSourceIndex.get()' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Collections.Generic.IReadOnlyList Avalonia.Media.TextFormatting.TextLine.GetTextBounds(System.Int32, System.Int32)' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Boolean Avalonia.Media.TextFormatting.TextLine.HasOverflowed.get()' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.Height.get()' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Int32 Avalonia.Media.TextFormatting.TextLine.Length.get()' is abstract in the implementation but is missing in the contract. -MembersMustExist : Member 'public Avalonia.Media.TextFormatting.TextLineMetrics Avalonia.Media.TextFormatting.TextLine.LineMetrics.get()' does not exist in the implementation but it does exist in the contract. -CannotAddAbstractMembers : Member 'public System.Int32 Avalonia.Media.TextFormatting.TextLine.NewLineLength.get()' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.OverhangAfter.get()' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.OverhangLeading.get()' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.OverhangTrailing.get()' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.Start.get()' is abstract in the implementation but is missing in the contract. -MembersMustExist : Member 'public Avalonia.Media.TextFormatting.TextRange Avalonia.Media.TextFormatting.TextLine.TextRange.get()' does not exist in the implementation but it does exist in the contract. -CannotAddAbstractMembers : Member 'public System.Int32 Avalonia.Media.TextFormatting.TextLine.TrailingWhitespaceLength.get()' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.Width.get()' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextLine.WidthIncludingTrailingWhitespace.get()' is abstract in the implementation but is missing in the contract. -MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextLineBreak..ctor(System.Collections.Generic.IReadOnlyList)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public System.Collections.Generic.IReadOnlyList Avalonia.Media.TextFormatting.TextLineBreak.RemainingCharacters.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextLineMetrics..ctor(Avalonia.Size, System.Double, Avalonia.Media.TextFormatting.TextRange, System.Boolean)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Media.TextFormatting.TextLineMetrics Avalonia.Media.TextFormatting.TextLineMetrics.Create(System.Collections.Generic.IEnumerable, Avalonia.Media.TextFormatting.TextRange, System.Double, Avalonia.Media.TextFormatting.TextParagraphProperties)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Size Avalonia.Media.TextFormatting.TextLineMetrics.Size.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Media.TextFormatting.TextRange Avalonia.Media.TextFormatting.TextLineMetrics.TextRange.get()' does not exist in the implementation but it does exist in the contract. -CannotAddAbstractMembers : Member 'public System.Boolean Avalonia.Media.TextFormatting.TextParagraphProperties.FirstLineInParagraph' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public Avalonia.Media.FlowDirection Avalonia.Media.TextFormatting.TextParagraphProperties.FlowDirection' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextParagraphProperties.Indent' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Boolean Avalonia.Media.TextFormatting.TextParagraphProperties.FirstLineInParagraph.get()' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public Avalonia.Media.FlowDirection Avalonia.Media.TextFormatting.TextParagraphProperties.FlowDirection.get()' is abstract in the implementation but is missing in the contract. -CannotAddAbstractMembers : Member 'public System.Double Avalonia.Media.TextFormatting.TextParagraphProperties.Indent.get()' is abstract in the implementation but is missing in the contract. -MembersMustExist : Member 'public Avalonia.Media.GlyphRun Avalonia.Media.TextFormatting.TextShaper.ShapeText(Avalonia.Utilities.ReadOnlySlice, Avalonia.Media.Typeface, System.Double, System.Globalization.CultureInfo)' does not exist in the implementation but it does exist in the contract. -CannotSealType : Type 'Avalonia.Media.TextFormatting.TextTrailingCharacterEllipsis' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract. -MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextTrailingCharacterEllipsis..ctor(System.Double, Avalonia.Media.TextFormatting.TextRunProperties)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Media.TextFormatting.TextCollapsingStyle Avalonia.Media.TextFormatting.TextTrailingCharacterEllipsis.Style.get()' does not exist in the implementation but it does exist in the contract. -CannotSealType : Type 'Avalonia.Media.TextFormatting.TextTrailingWordEllipsis' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract. -MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextTrailingWordEllipsis..ctor(System.Double, Avalonia.Media.TextFormatting.TextRunProperties)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Media.TextFormatting.TextCollapsingStyle Avalonia.Media.TextFormatting.TextTrailingWordEllipsis.Style.get()' does not exist in the implementation but it does exist in the contract. -TypesMustExist : Type 'Avalonia.Media.TextFormatting.Unicode.BiDiClass' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public Avalonia.Media.TextFormatting.Unicode.BiDiClass Avalonia.Media.TextFormatting.Unicode.Codepoint.BiDiClass.get()' does not exist in the implementation but it does exist in the contract. -TypesMustExist : Type 'Avalonia.Platform.ExportRenderingSubsystemAttribute' does not exist in the implementation but it does exist in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IDrawingContextImpl.DrawEllipse(Avalonia.Media.IBrush, Avalonia.Media.IPen, Avalonia.Rect)' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IDrawingContextImpl.DrawText(Avalonia.Media.IBrush, Avalonia.Point, Avalonia.Platform.IFormattedTextImpl)' is present in the contract but not in the implementation. -MembersMustExist : Member 'public void Avalonia.Platform.IDrawingContextImpl.DrawText(Avalonia.Media.IBrush, Avalonia.Point, Avalonia.Platform.IFormattedTextImpl)' does not exist in the implementation but it does exist in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IDrawingContextImpl.PopBitmapBlendMode()' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IDrawingContextImpl.PushBitmapBlendMode(Avalonia.Visuals.Media.Imaging.BitmapBlendingMode)' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Typeface)' is present in the contract but not in the implementation. -MembersMustExist : Member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Typeface)' does not exist in the implementation but it does exist in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontStretch, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Typeface)' is present in the implementation but not in the contract. -TypesMustExist : Type 'Avalonia.Platform.IFormattedTextImpl' does not exist in the implementation but it does exist in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public System.Double Avalonia.Platform.IGeometryImpl.ContourLength' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public System.Double Avalonia.Platform.IGeometryImpl.ContourLength.get()' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Platform.IGeometryImpl.TryGetPointAndTangentAtDistance(System.Double, Avalonia.Point, Avalonia.Point)' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Platform.IGeometryImpl.TryGetPointAtDistance(System.Double, Avalonia.Point)' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Platform.IGeometryImpl.TryGetSegment(System.Double, System.Double, System.Boolean, Avalonia.Platform.IGeometryImpl)' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IGeometryImpl Avalonia.Platform.IPlatformRenderInterface.CreateCombinedGeometry(Avalonia.Media.GeometryCombineMode, Avalonia.Media.Geometry, Avalonia.Media.Geometry)' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IFormattedTextImpl Avalonia.Platform.IPlatformRenderInterface.CreateFormattedText(System.String, Avalonia.Media.Typeface, System.Double, Avalonia.Media.TextAlignment, Avalonia.Media.TextWrapping, Avalonia.Size, System.Collections.Generic.IReadOnlyList)' is present in the contract but not in the implementation. -MembersMustExist : Member 'public Avalonia.Platform.IFormattedTextImpl Avalonia.Platform.IPlatformRenderInterface.CreateFormattedText(System.String, Avalonia.Media.Typeface, System.Double, Avalonia.Media.TextAlignment, Avalonia.Media.TextWrapping, Avalonia.Size, System.Collections.Generic.IReadOnlyList)' does not exist in the implementation but it does exist in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IGeometryImpl Avalonia.Platform.IPlatformRenderInterface.CreateGeometryGroup(Avalonia.Media.FillRule, System.Collections.Generic.IReadOnlyList)' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IGlyphRunImpl Avalonia.Platform.IPlatformRenderInterface.CreateGlyphRun(Avalonia.Media.GlyphRun)' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IGlyphRunImpl Avalonia.Platform.IPlatformRenderInterface.CreateGlyphRun(Avalonia.Media.GlyphRun, System.Double)' is present in the contract but not in the implementation. -MembersMustExist : Member 'public Avalonia.Platform.IGlyphRunImpl Avalonia.Platform.IPlatformRenderInterface.CreateGlyphRun(Avalonia.Media.GlyphRun, System.Double)' does not exist in the implementation but it does exist in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWriteableBitmapImpl Avalonia.Platform.IPlatformRenderInterface.LoadWriteableBitmap(System.IO.Stream)' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWriteableBitmapImpl Avalonia.Platform.IPlatformRenderInterface.LoadWriteableBitmap(System.String)' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWriteableBitmapImpl Avalonia.Platform.IPlatformRenderInterface.LoadWriteableBitmapToHeight(System.IO.Stream, System.Int32, Avalonia.Visuals.Media.Imaging.BitmapInterpolationMode)' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IWriteableBitmapImpl Avalonia.Platform.IPlatformRenderInterface.LoadWriteableBitmapToWidth(System.IO.Stream, System.Int32, Avalonia.Visuals.Media.Imaging.BitmapInterpolationMode)' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Size Avalonia.Platform.IPlatformSettings.TouchDoubleClickSize' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public System.TimeSpan Avalonia.Platform.IPlatformSettings.TouchDoubleClickTime' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Size Avalonia.Platform.IPlatformSettings.TouchDoubleClickSize.get()' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public System.TimeSpan Avalonia.Platform.IPlatformSettings.TouchDoubleClickTime.get()' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Media.TextFormatting.ShapedBuffer Avalonia.Platform.ITextShaperImpl.ShapeText(Avalonia.Utilities.ReadOnlySlice, Avalonia.Media.TextFormatting.TextShaperOptions)' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Media.GlyphRun Avalonia.Platform.ITextShaperImpl.ShapeText(Avalonia.Utilities.ReadOnlySlice, Avalonia.Media.Typeface, System.Double, System.Globalization.CultureInfo)' is present in the contract but not in the implementation. -MembersMustExist : Member 'public Avalonia.Media.GlyphRun Avalonia.Platform.ITextShaperImpl.ShapeText(Avalonia.Utilities.ReadOnlySlice, Avalonia.Media.Typeface, System.Double, System.Globalization.CultureInfo)' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'protected void Avalonia.Rendering.RendererBase.RenderFps(Avalonia.Platform.IDrawingContextImpl, Avalonia.Rect, System.Nullable)' does not exist in the implementation but it does exist in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.VisualTree.IVisual.HasMirrorTransform' is present in the implementation but not in the contract. -InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.VisualTree.IVisual.HasMirrorTransform.get()' is present in the implementation but not in the contract. -MembersMustExist : Member 'public void Avalonia.Utilities.ReadOnlySlice..ctor(System.ReadOnlyMemory, System.Int32, System.Int32)' does not exist in the implementation but it does exist in the contract. -Total Issues: 186 diff --git a/src/Avalonia.Visuals/Avalonia.Visuals.csproj b/src/Avalonia.Visuals/Avalonia.Visuals.csproj deleted file mode 100644 index 7e9947812b..0000000000 --- a/src/Avalonia.Visuals/Avalonia.Visuals.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - net6.0;netstandard2.0 - Avalonia - true - - - - - - - - - - - - - - - - - - diff --git a/src/Avalonia.Visuals/Properties/AssemblyInfo.cs b/src/Avalonia.Visuals/Properties/AssemblyInfo.cs deleted file mode 100644 index ebff097199..0000000000 --- a/src/Avalonia.Visuals/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Runtime.CompilerServices; -using Avalonia.Metadata; - -[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Animation")] -[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Media")] -[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Media.Imaging")] -[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Media.Transformation")] -[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia")] - -[assembly: InternalsVisibleTo("Avalonia.Visuals.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.Direct2D1.RenderTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.Skia.RenderTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.Skia.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.Web.Blazor, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] - diff --git a/src/Avalonia.X11/X11IconLoader.cs b/src/Avalonia.X11/X11IconLoader.cs index 6632573ba2..36939f4103 100644 --- a/src/Avalonia.X11/X11IconLoader.cs +++ b/src/Avalonia.X11/X11IconLoader.cs @@ -2,12 +2,9 @@ using System; using System.IO; using System.Runtime.InteropServices; using Avalonia.Controls.Platform.Surfaces; -using Avalonia.Media; using Avalonia.Media.Imaging; using Avalonia.Platform; -using Avalonia.Utilities; -using Avalonia.Visuals.Media.Imaging; -using static Avalonia.X11.XLib; + namespace Avalonia.X11 { class X11IconLoader : IPlatformIconLoader diff --git a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj index 6624392258..622ec416e8 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj +++ b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj @@ -56,14 +56,8 @@ - - - - - - diff --git a/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj b/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj index 119f57973c..f7f1a111ba 100644 --- a/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj +++ b/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj @@ -11,8 +11,6 @@ - - diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs index 642808eacb..2548b9f5aa 100644 --- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs +++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs @@ -9,7 +9,7 @@ using Avalonia.Rendering; using Avalonia.Rendering.SceneGraph; using Avalonia.Rendering.Utilities; using Avalonia.Utilities; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; using SkiaSharp; namespace Avalonia.Skia diff --git a/src/Skia/Avalonia.Skia/ImmutableBitmap.cs b/src/Skia/Avalonia.Skia/ImmutableBitmap.cs index 3010f49420..5628ae177c 100644 --- a/src/Skia/Avalonia.Skia/ImmutableBitmap.cs +++ b/src/Skia/Avalonia.Skia/ImmutableBitmap.cs @@ -5,7 +5,7 @@ using System.Security.Cryptography; using Avalonia.Media.Imaging; using Avalonia.Platform; using Avalonia.Skia.Helpers; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; using SkiaSharp; namespace Avalonia.Skia diff --git a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs index 1c92c7d193..d3c3585cd0 100644 --- a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs +++ b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs @@ -10,7 +10,7 @@ using Avalonia.Media; using Avalonia.OpenGL; using Avalonia.OpenGL.Imaging; using Avalonia.Platform; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; using SkiaSharp; namespace Avalonia.Skia diff --git a/src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs b/src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs index 75b4231640..166f2e8f98 100644 --- a/src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs +++ b/src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs @@ -1,7 +1,7 @@ using System; using Avalonia.Media; using Avalonia.Platform; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; using SkiaSharp; namespace Avalonia.Skia diff --git a/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs b/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs index 63a1e8f869..506edf0627 100644 --- a/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs +++ b/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs @@ -4,7 +4,7 @@ using System.Threading; using Avalonia.Media.Imaging; using Avalonia.Platform; using Avalonia.Skia.Helpers; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; using SkiaSharp; namespace Avalonia.Skia diff --git a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs index c32c58605f..d9e992bb80 100644 --- a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs +++ b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs @@ -9,7 +9,7 @@ using Avalonia.Direct2D1.Media.Imaging; using Avalonia.Media; using Avalonia.Media.Imaging; using Avalonia.Platform; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; using SharpDX.DirectWrite; using GlyphRun = Avalonia.Media.GlyphRun; using TextAlignment = Avalonia.Media.TextAlignment; diff --git a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs index b62a6fa5a6..a259d8fab9 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs @@ -5,11 +5,11 @@ using Avalonia.Platform; using Avalonia.Rendering; using Avalonia.Rendering.SceneGraph; using Avalonia.Utilities; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; using SharpDX; using SharpDX.Direct2D1; using SharpDX.Mathematics.Interop; -using BitmapInterpolationMode = Avalonia.Visuals.Media.Imaging.BitmapInterpolationMode; +using BitmapInterpolationMode = Avalonia.Media.Imaging.BitmapInterpolationMode; namespace Avalonia.Direct2D1.Media { diff --git a/src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs index 09b900b0c2..296cefad4e 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs @@ -9,7 +9,7 @@ namespace Avalonia.Direct2D1.Media { private readonly OptionalDispose _bitmap; - private readonly Visuals.Media.Imaging.BitmapInterpolationMode _bitmapInterpolationMode; + private readonly Avalonia.Media.Imaging.BitmapInterpolationMode _bitmapInterpolationMode; public ImageBrushImpl( ITileBrush brush, diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs index cac35aa4ae..df07f7f39c 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs @@ -15,21 +15,21 @@ namespace Avalonia.Direct2D1.Media { private readonly BitmapDecoder _decoder; - private static BitmapInterpolationMode ConvertInterpolationMode(Avalonia.Visuals.Media.Imaging.BitmapInterpolationMode interpolationMode) + private static BitmapInterpolationMode ConvertInterpolationMode(Avalonia.Media.Imaging.BitmapInterpolationMode interpolationMode) { switch (interpolationMode) { - case Visuals.Media.Imaging.BitmapInterpolationMode.Default: + case Avalonia.Media.Imaging.BitmapInterpolationMode.Default: return BitmapInterpolationMode.Fant; - case Visuals.Media.Imaging.BitmapInterpolationMode.LowQuality: + case Avalonia.Media.Imaging.BitmapInterpolationMode.LowQuality: return BitmapInterpolationMode.NearestNeighbor; - case Visuals.Media.Imaging.BitmapInterpolationMode.MediumQuality: + case Avalonia.Media.Imaging.BitmapInterpolationMode.MediumQuality: return BitmapInterpolationMode.Fant; default: - case Visuals.Media.Imaging.BitmapInterpolationMode.HighQuality: + case Avalonia.Media.Imaging.BitmapInterpolationMode.HighQuality: return BitmapInterpolationMode.HighQualityCubic; } @@ -118,7 +118,7 @@ namespace Avalonia.Direct2D1.Media } } - public WicBitmapImpl(Stream stream, int decodeSize, bool horizontal, Avalonia.Visuals.Media.Imaging.BitmapInterpolationMode interpolationMode) + public WicBitmapImpl(Stream stream, int decodeSize, bool horizontal, Avalonia.Media.Imaging.BitmapInterpolationMode interpolationMode) { _decoder = new BitmapDecoder(Direct2D1Platform.ImagingFactory, stream, DecodeOptions.CacheOnLoad); diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WriteableWicBitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WriteableWicBitmapImpl.cs index fef6a9aa82..885be132a4 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WriteableWicBitmapImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WriteableWicBitmapImpl.cs @@ -9,7 +9,7 @@ namespace Avalonia.Direct2D1.Media.Imaging class WriteableWicBitmapImpl : WicBitmapImpl, IWriteableBitmapImpl { public WriteableWicBitmapImpl(Stream stream, int decodeSize, bool horizontal, - Avalonia.Visuals.Media.Imaging.BitmapInterpolationMode interpolationMode) + Avalonia.Media.Imaging.BitmapInterpolationMode interpolationMode) : base(stream, decodeSize, horizontal, interpolationMode) { } diff --git a/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj b/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj index a2d27fd579..1cf68c1605 100644 --- a/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj +++ b/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj @@ -7,15 +7,9 @@ - - - - - - diff --git a/tests/Avalonia.Animation.UnitTests/Avalonia.Animation.UnitTests.csproj b/tests/Avalonia.Animation.UnitTests/Avalonia.Animation.UnitTests.csproj deleted file mode 100644 index e07ece5460..0000000000 --- a/tests/Avalonia.Animation.UnitTests/Avalonia.Animation.UnitTests.csproj +++ /dev/null @@ -1,28 +0,0 @@ - - - net6.0 - Library - true - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tests/Avalonia.Animation.UnitTests/Properties/AssemblyInfo.cs b/tests/Avalonia.Animation.UnitTests/Properties/AssemblyInfo.cs deleted file mode 100644 index b57cd55868..0000000000 --- a/tests/Avalonia.Animation.UnitTests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,7 +0,0 @@ -using System.Reflection; -using Xunit; - -[assembly: AssemblyTitle("Avalonia.Animation.UnitTests")] - -// Don't run tests in parallel. -[assembly: CollectionBehavior(DisableTestParallelization = true)] \ No newline at end of file diff --git a/tests/Avalonia.Animation.UnitTests/AnimatableTests.cs b/tests/Avalonia.Base.UnitTests/Animation/AnimatableTests.cs similarity index 99% rename from tests/Avalonia.Animation.UnitTests/AnimatableTests.cs rename to tests/Avalonia.Base.UnitTests/Animation/AnimatableTests.cs index 1d5296bebd..f9ff8caab1 100644 --- a/tests/Avalonia.Animation.UnitTests/AnimatableTests.cs +++ b/tests/Avalonia.Base.UnitTests/Animation/AnimatableTests.cs @@ -1,5 +1,5 @@ using System; -using Avalonia.Animation.Animators; +using Avalonia.Animation; using Avalonia.Controls; using Avalonia.Controls.Shapes; using Avalonia.Data; @@ -10,7 +10,7 @@ using Avalonia.UnitTests; using Moq; using Xunit; -namespace Avalonia.Animation.UnitTests +namespace Avalonia.Base.UnitTests.Animation { public class AnimatableTests { @@ -136,7 +136,7 @@ namespace Avalonia.Animation.UnitTests KeyTime = TimeSpan.FromSeconds(3), }; - var animation = new Animation() + var animation = new Avalonia.Animation.Animation() { Duration = TimeSpan.FromSeconds(3), Children = diff --git a/tests/Avalonia.Animation.UnitTests/AnimationIterationTests.cs b/tests/Avalonia.Base.UnitTests/Animation/AnimationIterationTests.cs similarity index 97% rename from tests/Avalonia.Animation.UnitTests/AnimationIterationTests.cs rename to tests/Avalonia.Base.UnitTests/Animation/AnimationIterationTests.cs index 58bd7a42c3..ca24b95e65 100644 --- a/tests/Avalonia.Animation.UnitTests/AnimationIterationTests.cs +++ b/tests/Avalonia.Base.UnitTests/Animation/AnimationIterationTests.cs @@ -12,8 +12,10 @@ using Avalonia.Animation.Easings; using System.Threading; using System.Reactive.Linq; -namespace Avalonia.Animation.UnitTests +namespace Avalonia.Base.UnitTests.Animation { + using Animation = Avalonia.Animation.Animation; + public class AnimationIterationTests { [Fact] @@ -37,7 +39,7 @@ namespace Avalonia.Animation.UnitTests KeyTime = TimeSpan.FromSeconds(0) }; - var animation = new Animation() + var animation = new Avalonia.Animation.Animation() { Duration = TimeSpan.FromSeconds(1), Children = @@ -86,7 +88,7 @@ namespace Avalonia.Animation.UnitTests Cue = new Cue(0d) }; - var animation = new Animation() + var animation = new Avalonia.Animation.Animation() { Duration = TimeSpan.FromSeconds(3), Delay = TimeSpan.FromSeconds(3), @@ -147,7 +149,7 @@ namespace Avalonia.Animation.UnitTests Cue = new Cue(1.0d) }; - var animation = new Animation() + var animation = new Avalonia.Animation.Animation() { Duration = TimeSpan.FromSeconds(0.05d), Delay = TimeSpan.FromSeconds(0.05d), diff --git a/tests/Avalonia.Animation.UnitTests/BrushTransitionTests.cs b/tests/Avalonia.Base.UnitTests/Animation/BrushTransitionTests.cs similarity index 93% rename from tests/Avalonia.Animation.UnitTests/BrushTransitionTests.cs rename to tests/Avalonia.Base.UnitTests/Animation/BrushTransitionTests.cs index 1986bd2ee3..acc32fd6a2 100644 --- a/tests/Avalonia.Animation.UnitTests/BrushTransitionTests.cs +++ b/tests/Avalonia.Base.UnitTests/Animation/BrushTransitionTests.cs @@ -1,13 +1,10 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using Avalonia.Animation; using Avalonia.Controls; using Avalonia.Media; using Xunit; -namespace Avalonia.Animation.UnitTests +namespace Avalonia.Base.UnitTests.Animation { public class BrushTransitionTests { diff --git a/tests/Avalonia.Animation.UnitTests/KeySplineTests.cs b/tests/Avalonia.Base.UnitTests/Animation/KeySplineTests.cs similarity index 97% rename from tests/Avalonia.Animation.UnitTests/KeySplineTests.cs rename to tests/Avalonia.Base.UnitTests/Animation/KeySplineTests.cs index fa2ed61e65..3cd12d92ec 100644 --- a/tests/Avalonia.Animation.UnitTests/KeySplineTests.cs +++ b/tests/Avalonia.Base.UnitTests/Animation/KeySplineTests.cs @@ -1,11 +1,12 @@ using System; +using Avalonia.Animation; using Avalonia.Animation.Easings; using Avalonia.Controls.Shapes; using Avalonia.Media; using Avalonia.Styling; using Xunit; -namespace Avalonia.Animation.UnitTests +namespace Avalonia.Base.UnitTests.Animation { public class KeySplineTests { @@ -123,7 +124,7 @@ namespace Avalonia.Animation.UnitTests 0.499999999999999999) }; - var animation = new Animation() + var animation = new Avalonia.Animation.Animation() { Duration = TimeSpan.FromSeconds(5), Children = @@ -190,7 +191,7 @@ namespace Avalonia.Animation.UnitTests KeyTime = TimeSpan.FromSeconds(5), }; - var animation = new Animation() + var animation = new Avalonia.Animation.Animation() { Duration = TimeSpan.FromSeconds(5), Children = diff --git a/tests/Avalonia.Animation.UnitTests/TestClock.cs b/tests/Avalonia.Base.UnitTests/Animation/TestClock.cs similarity index 90% rename from tests/Avalonia.Animation.UnitTests/TestClock.cs rename to tests/Avalonia.Base.UnitTests/Animation/TestClock.cs index 4812880c03..6604bd29a4 100644 --- a/tests/Avalonia.Animation.UnitTests/TestClock.cs +++ b/tests/Avalonia.Base.UnitTests/Animation/TestClock.cs @@ -1,7 +1,7 @@ using System; -using System.Collections.Generic; +using Avalonia.Animation; -namespace Avalonia.Animation.UnitTests +namespace Avalonia.Base.UnitTests.Animation { internal class TestClock : IClock, IDisposable { @@ -33,4 +33,4 @@ namespace Avalonia.Animation.UnitTests return this; } } -} \ No newline at end of file +} diff --git a/tests/Avalonia.Animation.UnitTests/TransitionsTests.cs b/tests/Avalonia.Base.UnitTests/Animation/TransitionsTests.cs similarity index 98% rename from tests/Avalonia.Animation.UnitTests/TransitionsTests.cs rename to tests/Avalonia.Base.UnitTests/Animation/TransitionsTests.cs index 640013dedd..65cf90b642 100644 --- a/tests/Avalonia.Animation.UnitTests/TransitionsTests.cs +++ b/tests/Avalonia.Base.UnitTests/Animation/TransitionsTests.cs @@ -1,9 +1,9 @@ using System; +using Avalonia.Animation; using Avalonia.Controls; -using Avalonia.UnitTests; using Xunit; -namespace Avalonia.Animation.UnitTests +namespace Avalonia.Base.UnitTests.Animation { public class TransitionsTests { diff --git a/tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj b/tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj index 9628d16a6c..602e5e4496 100644 --- a/tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj +++ b/tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj @@ -12,6 +12,11 @@ + + + + + diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaPropertyRegistryTests.cs b/tests/Avalonia.Base.UnitTests/AvaloniaPropertyRegistryTests.cs index 7f4db23268..0d62f20f7e 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaPropertyRegistryTests.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaPropertyRegistryTests.cs @@ -1,3 +1,4 @@ +using System; using System.Linq; using System.Reactive.Linq; using Xunit; @@ -56,7 +57,7 @@ namespace Avalonia.Base.UnitTests .Select(x => x.Name) .ToArray(); - Assert.Equal(new[] { "Attached" }, names); + Assert.Contains("Attached", names); } [Fact] @@ -66,7 +67,7 @@ namespace Avalonia.Base.UnitTests .Select(x => x.Name) .ToArray(); - Assert.Equal(new[] { "Attached" }, names); + Assert.Contains("Attached", names); } [Fact] diff --git a/tests/Avalonia.Visuals.UnitTests/CornerRadiusTests.cs b/tests/Avalonia.Base.UnitTests/CornerRadiusTests.cs similarity index 93% rename from tests/Avalonia.Visuals.UnitTests/CornerRadiusTests.cs rename to tests/Avalonia.Base.UnitTests/CornerRadiusTests.cs index 21633b1cec..edfbd5acd9 100644 --- a/tests/Avalonia.Visuals.UnitTests/CornerRadiusTests.cs +++ b/tests/Avalonia.Base.UnitTests/CornerRadiusTests.cs @@ -1,7 +1,6 @@ -using System.Globalization; using Xunit; -namespace Avalonia.Visuals.UnitTests +namespace Avalonia.Base.UnitTests { public class CornerRadiusTests { @@ -37,4 +36,4 @@ namespace Avalonia.Visuals.UnitTests Assert.Equal(new CornerRadius(1.1, 2.2, 3.3, 4.4), result); } } -} \ No newline at end of file +} diff --git a/tests/Avalonia.Base.UnitTests/Data/Core/Plugins/DataAnnotationsValidationPluginTests.cs b/tests/Avalonia.Base.UnitTests/Data/Core/Plugins/DataAnnotationsValidationPluginTests.cs index 1d9524a191..574ecd2ca4 100644 --- a/tests/Avalonia.Base.UnitTests/Data/Core/Plugins/DataAnnotationsValidationPluginTests.cs +++ b/tests/Avalonia.Base.UnitTests/Data/Core/Plugins/DataAnnotationsValidationPluginTests.cs @@ -7,7 +7,7 @@ using Avalonia.Data.Core.Plugins; using Avalonia.UnitTests; using Xunit; -namespace Avalonia.Markup.UnitTests.Data.Plugins +namespace Avalonia.Base.UnitTests.Data.Core.Plugins { public class DataAnnotationsValidationPluginTests : IClassFixture { diff --git a/tests/Avalonia.Base.UnitTests/Data/DefaultValueConverterTests.cs b/tests/Avalonia.Base.UnitTests/Data/DefaultValueConverterTests.cs index f522acf9ce..7cf84d767a 100644 --- a/tests/Avalonia.Base.UnitTests/Data/DefaultValueConverterTests.cs +++ b/tests/Avalonia.Base.UnitTests/Data/DefaultValueConverterTests.cs @@ -7,7 +7,7 @@ using Avalonia.Data.Converters; using Avalonia.Layout; using Xunit; -namespace Avalonia.Base.UnitTests.Data.Converters +namespace Avalonia.Base.UnitTests.Data { public class DefaultValueConverterTests { diff --git a/tests/Avalonia.Input.UnitTests/GesturesTests.cs b/tests/Avalonia.Base.UnitTests/Input/GesturesTests.cs similarity index 98% rename from tests/Avalonia.Input.UnitTests/GesturesTests.cs rename to tests/Avalonia.Base.UnitTests/Input/GesturesTests.cs index b2687c2555..57ec00cb58 100644 --- a/tests/Avalonia.Input.UnitTests/GesturesTests.cs +++ b/tests/Avalonia.Base.UnitTests/Input/GesturesTests.cs @@ -1,9 +1,10 @@ using System.Collections.Generic; using Avalonia.Controls; +using Avalonia.Input; using Avalonia.UnitTests; using Xunit; -namespace Avalonia.Input.UnitTests +namespace Avalonia.Base.UnitTests.Input { public class GesturesTests { diff --git a/tests/Avalonia.Input.UnitTests/InputElement_Enabled.cs b/tests/Avalonia.Base.UnitTests/Input/InputElement_Enabled.cs similarity index 98% rename from tests/Avalonia.Input.UnitTests/InputElement_Enabled.cs rename to tests/Avalonia.Base.UnitTests/Input/InputElement_Enabled.cs index 11d68d307e..1c001d4b3a 100644 --- a/tests/Avalonia.Input.UnitTests/InputElement_Enabled.cs +++ b/tests/Avalonia.Base.UnitTests/Input/InputElement_Enabled.cs @@ -1,7 +1,7 @@ using Avalonia.Controls; using Xunit; -namespace Avalonia.Input.UnitTests +namespace Avalonia.Base.UnitTests.Input { public class InputElement_Enabled { diff --git a/tests/Avalonia.Input.UnitTests/InputElement_Focus.cs b/tests/Avalonia.Base.UnitTests/Input/InputElement_Focus.cs similarity index 99% rename from tests/Avalonia.Input.UnitTests/InputElement_Focus.cs rename to tests/Avalonia.Base.UnitTests/Input/InputElement_Focus.cs index 602455ba36..2d8ee62ef2 100644 --- a/tests/Avalonia.Input.UnitTests/InputElement_Focus.cs +++ b/tests/Avalonia.Base.UnitTests/Input/InputElement_Focus.cs @@ -1,8 +1,9 @@ using Avalonia.Controls; +using Avalonia.Input; using Avalonia.UnitTests; using Xunit; -namespace Avalonia.Input.UnitTests +namespace Avalonia.Base.UnitTests.Input { public class InputElement_Focus { diff --git a/tests/Avalonia.Input.UnitTests/KeyGestureTests.cs b/tests/Avalonia.Base.UnitTests/Input/KeyGestureTests.cs similarity index 97% rename from tests/Avalonia.Input.UnitTests/KeyGestureTests.cs rename to tests/Avalonia.Base.UnitTests/Input/KeyGestureTests.cs index 90f9a0a985..33cd94ec98 100644 --- a/tests/Avalonia.Input.UnitTests/KeyGestureTests.cs +++ b/tests/Avalonia.Base.UnitTests/Input/KeyGestureTests.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; +using Avalonia.Input; using Xunit; -namespace Avalonia.Input.UnitTests +namespace Avalonia.Base.UnitTests.Input { public class KeyGestureTests { diff --git a/tests/Avalonia.Input.UnitTests/KeyboardDeviceTests.cs b/tests/Avalonia.Base.UnitTests/Input/KeyboardDeviceTests.cs similarity index 98% rename from tests/Avalonia.Input.UnitTests/KeyboardDeviceTests.cs rename to tests/Avalonia.Base.UnitTests/Input/KeyboardDeviceTests.cs index 32ba14d337..07bfa0d2e6 100644 --- a/tests/Avalonia.Input.UnitTests/KeyboardDeviceTests.cs +++ b/tests/Avalonia.Base.UnitTests/Input/KeyboardDeviceTests.cs @@ -1,12 +1,13 @@ using System; using System.Windows.Input; using Avalonia.Controls; +using Avalonia.Input; using Avalonia.Input.Raw; using Avalonia.UnitTests; using Moq; using Xunit; -namespace Avalonia.Input.UnitTests +namespace Avalonia.Base.UnitTests.Input { public class KeyboardDeviceTests { diff --git a/tests/Avalonia.Input.UnitTests/KeyboardNavigationTests_Custom.cs b/tests/Avalonia.Base.UnitTests/Input/KeyboardNavigationTests_Custom.cs similarity index 99% rename from tests/Avalonia.Input.UnitTests/KeyboardNavigationTests_Custom.cs rename to tests/Avalonia.Base.UnitTests/Input/KeyboardNavigationTests_Custom.cs index f9c85ee4ca..830f7223ec 100644 --- a/tests/Avalonia.Input.UnitTests/KeyboardNavigationTests_Custom.cs +++ b/tests/Avalonia.Base.UnitTests/Input/KeyboardNavigationTests_Custom.cs @@ -1,7 +1,8 @@ using Avalonia.Controls; +using Avalonia.Input; using Xunit; -namespace Avalonia.Input.UnitTests +namespace Avalonia.Base.UnitTests.Input { public class KeyboardNavigationTests_Custom { diff --git a/tests/Avalonia.Input.UnitTests/KeyboardNavigationTests_Tab.cs b/tests/Avalonia.Base.UnitTests/Input/KeyboardNavigationTests_Tab.cs similarity index 99% rename from tests/Avalonia.Input.UnitTests/KeyboardNavigationTests_Tab.cs rename to tests/Avalonia.Base.UnitTests/Input/KeyboardNavigationTests_Tab.cs index 9a117bb71d..7fd8cb345d 100644 --- a/tests/Avalonia.Input.UnitTests/KeyboardNavigationTests_Tab.cs +++ b/tests/Avalonia.Base.UnitTests/Input/KeyboardNavigationTests_Tab.cs @@ -1,8 +1,9 @@ using System.Collections.Generic; using Avalonia.Controls; +using Avalonia.Input; using Xunit; -namespace Avalonia.Input.UnitTests +namespace Avalonia.Base.UnitTests.Input { public class KeyboardNavigationTests_Tab { diff --git a/tests/Avalonia.Input.UnitTests/MouseDeviceTests.cs b/tests/Avalonia.Base.UnitTests/Input/MouseDeviceTests.cs similarity index 98% rename from tests/Avalonia.Input.UnitTests/MouseDeviceTests.cs rename to tests/Avalonia.Base.UnitTests/Input/MouseDeviceTests.cs index 223f458f25..758d501cc1 100644 --- a/tests/Avalonia.Input.UnitTests/MouseDeviceTests.cs +++ b/tests/Avalonia.Base.UnitTests/Input/MouseDeviceTests.cs @@ -1,16 +1,16 @@ -using Avalonia.Controls; +using System; +using System.Collections.Generic; +using Avalonia.Controls; +using Avalonia.Input; using Avalonia.Input.Raw; -using Avalonia.Interactivity; using Avalonia.Media; using Avalonia.Rendering; using Avalonia.UnitTests; using Avalonia.VisualTree; using Moq; -using System; -using System.Collections.Generic; using Xunit; -namespace Avalonia.Input.UnitTests +namespace Avalonia.Base.UnitTests.Input { public class MouseDeviceTests { diff --git a/tests/Avalonia.Input.UnitTests/PointerTests.cs b/tests/Avalonia.Base.UnitTests/Input/PointerTests.cs similarity index 95% rename from tests/Avalonia.Input.UnitTests/PointerTests.cs rename to tests/Avalonia.Base.UnitTests/Input/PointerTests.cs index e639726dbd..e25ef0a9b8 100644 --- a/tests/Avalonia.Input.UnitTests/PointerTests.cs +++ b/tests/Avalonia.Base.UnitTests/Input/PointerTests.cs @@ -1,11 +1,12 @@ using System.Collections.Generic; using System.Linq; using Avalonia.Controls; +using Avalonia.Input; using Avalonia.UnitTests; using Avalonia.VisualTree; using Xunit; -namespace Avalonia.Input.UnitTests +namespace Avalonia.Base.UnitTests.Input { public class PointerTests { diff --git a/tests/Avalonia.Input.UnitTests/TouchDeviceTests.cs b/tests/Avalonia.Base.UnitTests/Input/TouchDeviceTests.cs similarity index 100% rename from tests/Avalonia.Input.UnitTests/TouchDeviceTests.cs rename to tests/Avalonia.Base.UnitTests/Input/TouchDeviceTests.cs diff --git a/tests/Avalonia.Interactivity.UnitTests/InteractiveTests.cs b/tests/Avalonia.Base.UnitTests/Interactivity/InteractiveTests.cs similarity index 99% rename from tests/Avalonia.Interactivity.UnitTests/InteractiveTests.cs rename to tests/Avalonia.Base.UnitTests/Interactivity/InteractiveTests.cs index b997ffbbd9..1ca7e9bd49 100644 --- a/tests/Avalonia.Interactivity.UnitTests/InteractiveTests.cs +++ b/tests/Avalonia.Base.UnitTests/Interactivity/InteractiveTests.cs @@ -1,10 +1,11 @@ using System; using System.Collections.Generic; using System.Linq; +using Avalonia.Interactivity; using Avalonia.VisualTree; using Xunit; -namespace Avalonia.Interactivity.UnitTests +namespace Avalonia.Base.UnitTests.Interactivity { public class InteractiveTests { diff --git a/tests/Avalonia.Interactivity.UnitTests/RoutedEventRegistryTests.cs b/tests/Avalonia.Base.UnitTests/Interactivity/RoutedEventRegistryTests.cs similarity index 95% rename from tests/Avalonia.Interactivity.UnitTests/RoutedEventRegistryTests.cs rename to tests/Avalonia.Base.UnitTests/Interactivity/RoutedEventRegistryTests.cs index 8276039d09..05b2187df4 100644 --- a/tests/Avalonia.Interactivity.UnitTests/RoutedEventRegistryTests.cs +++ b/tests/Avalonia.Base.UnitTests/Interactivity/RoutedEventRegistryTests.cs @@ -1,9 +1,10 @@ using System.Collections.Generic; using Avalonia.Controls; using Avalonia.Input; +using Avalonia.Interactivity; using Xunit; -namespace Avalonia.Interactivity.UnitTests +namespace Avalonia.Base.UnitTests.Interactivity { public class RoutedEventRegistryTests { diff --git a/tests/Avalonia.Layout.UnitTests/ArrangeTests.cs b/tests/Avalonia.Base.UnitTests/Layout/ArrangeTests.cs similarity index 98% rename from tests/Avalonia.Layout.UnitTests/ArrangeTests.cs rename to tests/Avalonia.Base.UnitTests/Layout/ArrangeTests.cs index db0ead1b00..b771cf2d24 100644 --- a/tests/Avalonia.Layout.UnitTests/ArrangeTests.cs +++ b/tests/Avalonia.Base.UnitTests/Layout/ArrangeTests.cs @@ -1,7 +1,8 @@ using Avalonia.Controls; +using Avalonia.Layout; using Xunit; -namespace Avalonia.Layout.UnitTests +namespace Avalonia.Base.UnitTests.Layout { public class ArrangeTests { diff --git a/tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs b/tests/Avalonia.Base.UnitTests/Layout/FullLayoutTests.cs similarity index 98% rename from tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs rename to tests/Avalonia.Base.UnitTests/Layout/FullLayoutTests.cs index dcc29a9716..1d6eead6dd 100644 --- a/tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs +++ b/tests/Avalonia.Base.UnitTests/Layout/FullLayoutTests.cs @@ -3,12 +3,12 @@ using Avalonia.Controls; using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives; using Avalonia.Controls.Templates; -using Avalonia.Styling; +using Avalonia.Layout; using Avalonia.UnitTests; using Avalonia.VisualTree; using Xunit; -namespace Avalonia.Layout.UnitTests +namespace Avalonia.Base.UnitTests.Layout { public class FullLayoutTests { diff --git a/tests/Avalonia.Layout.UnitTests/LayoutHelperTests.cs b/tests/Avalonia.Base.UnitTests/Layout/LayoutHelperTests.cs similarity index 92% rename from tests/Avalonia.Layout.UnitTests/LayoutHelperTests.cs rename to tests/Avalonia.Base.UnitTests/Layout/LayoutHelperTests.cs index f9187edc42..3df31939d1 100644 --- a/tests/Avalonia.Layout.UnitTests/LayoutHelperTests.cs +++ b/tests/Avalonia.Base.UnitTests/Layout/LayoutHelperTests.cs @@ -1,7 +1,8 @@ using System; +using Avalonia.Layout; using Xunit; -namespace Avalonia.Layout.UnitTests +namespace Avalonia.Base.UnitTests.Layout { public class LayoutHelperTests { diff --git a/tests/Avalonia.Layout.UnitTests/LayoutManagerTests.cs b/tests/Avalonia.Base.UnitTests/Layout/LayoutManagerTests.cs similarity index 99% rename from tests/Avalonia.Layout.UnitTests/LayoutManagerTests.cs rename to tests/Avalonia.Base.UnitTests/Layout/LayoutManagerTests.cs index e429adce85..f0e8e1cd11 100644 --- a/tests/Avalonia.Layout.UnitTests/LayoutManagerTests.cs +++ b/tests/Avalonia.Base.UnitTests/Layout/LayoutManagerTests.cs @@ -1,9 +1,10 @@ using System.Collections.Generic; using System.Linq; using Avalonia.Controls; +using Avalonia.Layout; using Xunit; -namespace Avalonia.Layout.UnitTests +namespace Avalonia.Base.UnitTests.Layout { public class LayoutManagerTests { diff --git a/tests/Avalonia.Layout.UnitTests/LayoutQueueTests.cs b/tests/Avalonia.Base.UnitTests/Layout/LayoutQueueTests.cs similarity index 98% rename from tests/Avalonia.Layout.UnitTests/LayoutQueueTests.cs rename to tests/Avalonia.Base.UnitTests/Layout/LayoutQueueTests.cs index f047677a8a..fc3b5c3d43 100644 --- a/tests/Avalonia.Layout.UnitTests/LayoutQueueTests.cs +++ b/tests/Avalonia.Base.UnitTests/Layout/LayoutQueueTests.cs @@ -1,8 +1,9 @@ using System.Collections.Generic; using System.Linq; +using Avalonia.Layout; using Xunit; -namespace Avalonia.Layout.UnitTests +namespace Avalonia.Base.UnitTests.Layout { public class LayoutQueueTests { diff --git a/tests/Avalonia.Layout.UnitTests/LayoutTestControl.cs b/tests/Avalonia.Base.UnitTests/Layout/LayoutTestControl.cs similarity index 93% rename from tests/Avalonia.Layout.UnitTests/LayoutTestControl.cs rename to tests/Avalonia.Base.UnitTests/Layout/LayoutTestControl.cs index f7f072eb1e..ca593cc665 100644 --- a/tests/Avalonia.Layout.UnitTests/LayoutTestControl.cs +++ b/tests/Avalonia.Base.UnitTests/Layout/LayoutTestControl.cs @@ -1,7 +1,8 @@ using System; using Avalonia.Controls; +using Avalonia.Layout; -namespace Avalonia.Layout.UnitTests +namespace Avalonia.Base.UnitTests.Layout { internal class LayoutTestControl : Decorator { diff --git a/tests/Avalonia.Layout.UnitTests/LayoutTestRoot.cs b/tests/Avalonia.Base.UnitTests/Layout/LayoutTestRoot.cs similarity index 94% rename from tests/Avalonia.Layout.UnitTests/LayoutTestRoot.cs rename to tests/Avalonia.Base.UnitTests/Layout/LayoutTestRoot.cs index a94459df96..19ab54a339 100644 --- a/tests/Avalonia.Layout.UnitTests/LayoutTestRoot.cs +++ b/tests/Avalonia.Base.UnitTests/Layout/LayoutTestRoot.cs @@ -1,7 +1,8 @@ using System; +using Avalonia.Layout; using Avalonia.UnitTests; -namespace Avalonia.Layout.UnitTests +namespace Avalonia.Base.UnitTests.Layout { internal class LayoutTestRoot : TestRoot, ILayoutable { diff --git a/tests/Avalonia.Layout.UnitTests/LayoutableTests.cs b/tests/Avalonia.Base.UnitTests/Layout/LayoutableTests.cs similarity index 99% rename from tests/Avalonia.Layout.UnitTests/LayoutableTests.cs rename to tests/Avalonia.Base.UnitTests/Layout/LayoutableTests.cs index a8aa0bbf0e..87fa8cf1f3 100644 --- a/tests/Avalonia.Layout.UnitTests/LayoutableTests.cs +++ b/tests/Avalonia.Base.UnitTests/Layout/LayoutableTests.cs @@ -1,10 +1,11 @@ using System; using Avalonia.Controls; +using Avalonia.Layout; using Avalonia.UnitTests; using Moq; using Xunit; -namespace Avalonia.Layout.UnitTests +namespace Avalonia.Base.UnitTests.Layout { public class LayoutableTests { diff --git a/tests/Avalonia.Layout.UnitTests/LayoutableTests_EffectiveViewportChanged.cs b/tests/Avalonia.Base.UnitTests/Layout/LayoutableTests_EffectiveViewportChanged.cs similarity index 99% rename from tests/Avalonia.Layout.UnitTests/LayoutableTests_EffectiveViewportChanged.cs rename to tests/Avalonia.Base.UnitTests/Layout/LayoutableTests_EffectiveViewportChanged.cs index 178855d71c..7c1a525002 100644 --- a/tests/Avalonia.Layout.UnitTests/LayoutableTests_EffectiveViewportChanged.cs +++ b/tests/Avalonia.Base.UnitTests/Layout/LayoutableTests_EffectiveViewportChanged.cs @@ -4,11 +4,12 @@ using Avalonia.Controls; using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives; using Avalonia.Controls.Templates; +using Avalonia.Layout; using Avalonia.Media; using Avalonia.UnitTests; using Xunit; -namespace Avalonia.Layout.UnitTests +namespace Avalonia.Base.UnitTests.Layout { public class LayoutableTests_EffectiveViewportChanged { diff --git a/tests/Avalonia.Layout.UnitTests/MeasureTests.cs b/tests/Avalonia.Base.UnitTests/Layout/MeasureTests.cs similarity index 99% rename from tests/Avalonia.Layout.UnitTests/MeasureTests.cs rename to tests/Avalonia.Base.UnitTests/Layout/MeasureTests.cs index 140ebede15..32c3e89b74 100644 --- a/tests/Avalonia.Layout.UnitTests/MeasureTests.cs +++ b/tests/Avalonia.Base.UnitTests/Layout/MeasureTests.cs @@ -1,7 +1,7 @@ using Avalonia.Controls; using Xunit; -namespace Avalonia.Layout.UnitTests +namespace Avalonia.Base.UnitTests.Layout { public class MeasureTests { diff --git a/tests/Avalonia.Layout.UnitTests/NonVirtualizingStackLayoutTests.cs b/tests/Avalonia.Base.UnitTests/Layout/NonVirtualizingStackLayoutTests.cs similarity index 99% rename from tests/Avalonia.Layout.UnitTests/NonVirtualizingStackLayoutTests.cs rename to tests/Avalonia.Base.UnitTests/Layout/NonVirtualizingStackLayoutTests.cs index a7b378c322..78cf63ed3f 100644 --- a/tests/Avalonia.Layout.UnitTests/NonVirtualizingStackLayoutTests.cs +++ b/tests/Avalonia.Base.UnitTests/Layout/NonVirtualizingStackLayoutTests.cs @@ -1,9 +1,10 @@ using System.Collections.Generic; using System.Linq; using Avalonia.Controls; +using Avalonia.Layout; using Xunit; -namespace Avalonia.Layout.UnitTests +namespace Avalonia.Base.UnitTests.Layout { public class NonVirtualizingStackLayoutTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/BoxShadowTests.cs b/tests/Avalonia.Base.UnitTests/Media/BoxShadowTests.cs similarity index 96% rename from tests/Avalonia.Visuals.UnitTests/Media/BoxShadowTests.cs rename to tests/Avalonia.Base.UnitTests/Media/BoxShadowTests.cs index e3b703baf2..5f1860d177 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/BoxShadowTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/BoxShadowTests.cs @@ -1,7 +1,7 @@ using Avalonia.Media; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media +namespace Avalonia.Base.UnitTests.Media { public class BoxShadowTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/BrushTests.cs b/tests/Avalonia.Base.UnitTests/Media/BrushTests.cs similarity index 98% rename from tests/Avalonia.Visuals.UnitTests/Media/BrushTests.cs rename to tests/Avalonia.Base.UnitTests/Media/BrushTests.cs index 1a2f7732dd..6edce79de1 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/BrushTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/BrushTests.cs @@ -2,7 +2,7 @@ using System; using Avalonia.Media; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media +namespace Avalonia.Base.UnitTests.Media { public class BrushTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/ColorTests.cs b/tests/Avalonia.Base.UnitTests/Media/ColorTests.cs similarity index 99% rename from tests/Avalonia.Visuals.UnitTests/Media/ColorTests.cs rename to tests/Avalonia.Base.UnitTests/Media/ColorTests.cs index 352e58abd1..1392635b32 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/ColorTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/ColorTests.cs @@ -2,7 +2,7 @@ using System; using Avalonia.Media; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media +namespace Avalonia.Base.UnitTests.Media { public class ColorTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/FontFamilyTests.cs b/tests/Avalonia.Base.UnitTests/Media/FontFamilyTests.cs similarity index 99% rename from tests/Avalonia.Visuals.UnitTests/Media/FontFamilyTests.cs rename to tests/Avalonia.Base.UnitTests/Media/FontFamilyTests.cs index 8ed5e408fa..1f8ae9bd8b 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/FontFamilyTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/FontFamilyTests.cs @@ -4,7 +4,7 @@ using Avalonia.Media; using Avalonia.Media.Fonts; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media +namespace Avalonia.Base.UnitTests.Media { public class FontFamilyTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/FontManagerTests.cs b/tests/Avalonia.Base.UnitTests/Media/FontManagerTests.cs similarity index 98% rename from tests/Avalonia.Visuals.UnitTests/Media/FontManagerTests.cs rename to tests/Avalonia.Base.UnitTests/Media/FontManagerTests.cs index 2be0209c85..11ecac0039 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/FontManagerTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/FontManagerTests.cs @@ -3,7 +3,7 @@ using Avalonia.Media; using Avalonia.UnitTests; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media +namespace Avalonia.Base.UnitTests.Media { public class FontManagerTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/Fonts/FamilyNameCollectionTests.cs b/tests/Avalonia.Base.UnitTests/Media/Fonts/FamilyNameCollectionTests.cs similarity index 91% rename from tests/Avalonia.Visuals.UnitTests/Media/Fonts/FamilyNameCollectionTests.cs rename to tests/Avalonia.Base.UnitTests/Media/Fonts/FamilyNameCollectionTests.cs index f2fcd51629..07415ac2d6 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/Fonts/FamilyNameCollectionTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/Fonts/FamilyNameCollectionTests.cs @@ -2,7 +2,7 @@ using Avalonia.Media.Fonts; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media.Fonts +namespace Avalonia.Base.UnitTests.Media.Fonts { public class FamilyNameCollectionTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/Fonts/FontFamilyKeyTests.cs b/tests/Avalonia.Base.UnitTests/Media/Fonts/FontFamilyKeyTests.cs similarity index 95% rename from tests/Avalonia.Visuals.UnitTests/Media/Fonts/FontFamilyKeyTests.cs rename to tests/Avalonia.Base.UnitTests/Media/Fonts/FontFamilyKeyTests.cs index d99301ea6c..0cb70ea016 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/Fonts/FontFamilyKeyTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/Fonts/FontFamilyKeyTests.cs @@ -2,7 +2,7 @@ using Avalonia.Media.Fonts; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media.Fonts +namespace Avalonia.Base.UnitTests.Media.Fonts { public class FontFamilyKeyTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/Fonts/FontFamilyLoaderTests.cs b/tests/Avalonia.Base.UnitTests/Media/Fonts/FontFamilyLoaderTests.cs similarity index 94% rename from tests/Avalonia.Visuals.UnitTests/Media/Fonts/FontFamilyLoaderTests.cs rename to tests/Avalonia.Base.UnitTests/Media/Fonts/FontFamilyLoaderTests.cs index 7e1cc4cd96..1bb2300724 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/Fonts/FontFamilyLoaderTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/Fonts/FontFamilyLoaderTests.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.Linq; using Avalonia.Media; using Avalonia.Media.Fonts; @@ -6,10 +7,8 @@ using Avalonia.Platform; using Avalonia.UnitTests; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media.Fonts +namespace Avalonia.Base.UnitTests.Media.Fonts { - using System.Diagnostics; - public class FontFamilyLoaderTests : IDisposable { private const string AssetMyFontRegular = AssetLocation + ".MyFont Regular.ttf" + Assembly + FontName; @@ -100,7 +99,7 @@ namespace Avalonia.Visuals.UnitTests.Media.Fonts { var assetLoader = AvaloniaLocator.Current.GetService(); - var fontFamily = new FontFamily("resm:Avalonia.Visuals.UnitTests.Assets?assembly=Avalonia.Visuals.UnitTests#Noto Mono"); + var fontFamily = new FontFamily("resm:Avalonia.Base.UnitTests.Assets?assembly=Avalonia.Base.UnitTests#Noto Mono"); var fontAssets = FontFamilyLoader.LoadFontAssets(fontFamily.Key).ToArray(); diff --git a/tests/Avalonia.Visuals.UnitTests/Media/GeometryGroupTests.cs b/tests/Avalonia.Base.UnitTests/Media/GeometryGroupTests.cs similarity index 100% rename from tests/Avalonia.Visuals.UnitTests/Media/GeometryGroupTests.cs rename to tests/Avalonia.Base.UnitTests/Media/GeometryGroupTests.cs diff --git a/tests/Avalonia.Visuals.UnitTests/Media/GeometryTests.cs b/tests/Avalonia.Base.UnitTests/Media/GeometryTests.cs similarity index 98% rename from tests/Avalonia.Visuals.UnitTests/Media/GeometryTests.cs rename to tests/Avalonia.Base.UnitTests/Media/GeometryTests.cs index 7b1ed7f977..ae8dc9dad2 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/GeometryTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/GeometryTests.cs @@ -4,7 +4,7 @@ using Avalonia.Platform; using Moq; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media +namespace Avalonia.Base.UnitTests.Media { public class GeometryTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/GlyphRunTests.cs b/tests/Avalonia.Base.UnitTests/Media/GlyphRunTests.cs similarity index 99% rename from tests/Avalonia.Visuals.UnitTests/Media/GlyphRunTests.cs rename to tests/Avalonia.Base.UnitTests/Media/GlyphRunTests.cs index ba334fdb74..cd0586233f 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/GlyphRunTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/GlyphRunTests.cs @@ -5,7 +5,7 @@ using Avalonia.UnitTests; using Avalonia.Utilities; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media +namespace Avalonia.Base.UnitTests.Media { public class GlyphRunTests : TestWithServicesBase { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/ImageBrushTests.cs b/tests/Avalonia.Base.UnitTests/Media/ImageBrushTests.cs similarity index 78% rename from tests/Avalonia.Visuals.UnitTests/Media/ImageBrushTests.cs rename to tests/Avalonia.Base.UnitTests/Media/ImageBrushTests.cs index ff7b94105a..2e872e7f7f 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/ImageBrushTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/ImageBrushTests.cs @@ -1,12 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Text; -using Avalonia.Media; +using Avalonia.Media; using Avalonia.Media.Imaging; using Moq; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media +namespace Avalonia.Base.UnitTests.Media { public class ImageBrushTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/LinearGradientBrushTests.cs b/tests/Avalonia.Base.UnitTests/Media/LinearGradientBrushTests.cs similarity index 97% rename from tests/Avalonia.Visuals.UnitTests/Media/LinearGradientBrushTests.cs rename to tests/Avalonia.Base.UnitTests/Media/LinearGradientBrushTests.cs index b3f78dfe8f..736bafba66 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/LinearGradientBrushTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/LinearGradientBrushTests.cs @@ -1,10 +1,9 @@ -using System; -using Avalonia.Media; +using Avalonia.Media; using Avalonia.Media.Imaging; using Moq; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media +namespace Avalonia.Base.UnitTests.Media { public class LinearGradientBrushTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/MatrixTests.cs b/tests/Avalonia.Base.UnitTests/Media/MatrixTests.cs similarity index 98% rename from tests/Avalonia.Visuals.UnitTests/Media/MatrixTests.cs rename to tests/Avalonia.Base.UnitTests/Media/MatrixTests.cs index 6ef48b6161..98b1ff0e28 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/MatrixTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/MatrixTests.cs @@ -2,7 +2,7 @@ using Avalonia.Utilities; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media +namespace Avalonia.Base.UnitTests.Media { public class MatrixTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/PathMarkupParserTests.cs b/tests/Avalonia.Base.UnitTests/Media/PathMarkupParserTests.cs similarity index 98% rename from tests/Avalonia.Visuals.UnitTests/Media/PathMarkupParserTests.cs rename to tests/Avalonia.Base.UnitTests/Media/PathMarkupParserTests.cs index ba8c490829..c829690eb4 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/PathMarkupParserTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/PathMarkupParserTests.cs @@ -1,14 +1,13 @@ +using System.Globalization; +using System.IO; using Avalonia.Media; +using Avalonia.Platform; using Avalonia.Visuals.Platform; +using Moq; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media +namespace Avalonia.Base.UnitTests.Media { - using System.Globalization; - using System.IO; - using Avalonia.Platform; - using Moq; - public class PathMarkupParserTests { [Fact] diff --git a/tests/Avalonia.Visuals.UnitTests/Media/PathSegmentTests.cs b/tests/Avalonia.Base.UnitTests/Media/PathSegmentTests.cs similarity index 94% rename from tests/Avalonia.Visuals.UnitTests/Media/PathSegmentTests.cs rename to tests/Avalonia.Base.UnitTests/Media/PathSegmentTests.cs index 0737b4dc88..41d2fdb349 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/PathSegmentTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/PathSegmentTests.cs @@ -1,7 +1,7 @@ using Avalonia.Media; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media +namespace Avalonia.Base.UnitTests.Media { public class PathSegmentTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/PenTests.cs b/tests/Avalonia.Base.UnitTests/Media/PenTests.cs similarity index 98% rename from tests/Avalonia.Visuals.UnitTests/Media/PenTests.cs rename to tests/Avalonia.Base.UnitTests/Media/PenTests.cs index 8c25019606..fecdd30834 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/PenTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/PenTests.cs @@ -3,7 +3,7 @@ using Avalonia.Media; using Avalonia.Media.Immutable; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media +namespace Avalonia.Base.UnitTests.Media { public class PenTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/PixelRectTests.cs b/tests/Avalonia.Base.UnitTests/Media/PixelRectTests.cs similarity index 90% rename from tests/Avalonia.Visuals.UnitTests/Media/PixelRectTests.cs rename to tests/Avalonia.Base.UnitTests/Media/PixelRectTests.cs index c6da9b77e9..61c410ca8e 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/PixelRectTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/PixelRectTests.cs @@ -1,9 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Text; -using Xunit; +using Xunit; -namespace Avalonia.Visuals.UnitTests.Media +namespace Avalonia.Base.UnitTests.Media { public class PixelRectTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/RectTests.cs b/tests/Avalonia.Base.UnitTests/Media/RectTests.cs similarity index 74% rename from tests/Avalonia.Visuals.UnitTests/Media/RectTests.cs rename to tests/Avalonia.Base.UnitTests/Media/RectTests.cs index cd0c7e1ace..96473348cd 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/RectTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/RectTests.cs @@ -1,7 +1,6 @@ -using System.Globalization; -using Xunit; +using Xunit; -namespace Avalonia.Visuals.UnitTests.Media +namespace Avalonia.Base.UnitTests.Media { public class RectTests { @@ -13,4 +12,4 @@ namespace Avalonia.Visuals.UnitTests.Media Assert.Equal(expected, rect); } } -} \ No newline at end of file +} diff --git a/tests/Avalonia.Visuals.UnitTests/Media/RectangleGeometryTests.cs b/tests/Avalonia.Base.UnitTests/Media/RectangleGeometryTests.cs similarity index 93% rename from tests/Avalonia.Visuals.UnitTests/Media/RectangleGeometryTests.cs rename to tests/Avalonia.Base.UnitTests/Media/RectangleGeometryTests.cs index 5af1bb572e..387b2f4a19 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/RectangleGeometryTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/RectangleGeometryTests.cs @@ -1,11 +1,10 @@ -using System; -using Avalonia.Media; +using Avalonia.Media; using Avalonia.Platform; using Avalonia.UnitTests; using Moq; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media +namespace Avalonia.Base.UnitTests.Media { public class RectangleGeometryTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/SolidColorBrushTests.cs b/tests/Avalonia.Base.UnitTests/Media/SolidColorBrushTests.cs similarity index 82% rename from tests/Avalonia.Visuals.UnitTests/Media/SolidColorBrushTests.cs rename to tests/Avalonia.Base.UnitTests/Media/SolidColorBrushTests.cs index bcf63f9660..c982d8c1c8 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/SolidColorBrushTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/SolidColorBrushTests.cs @@ -1,8 +1,7 @@ -using System; -using Avalonia.Media; +using Avalonia.Media; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media +namespace Avalonia.Base.UnitTests.Media { public class SolidColorBrushTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/TextDecorationTests.cs b/tests/Avalonia.Base.UnitTests/Media/TextDecorationTests.cs similarity index 95% rename from tests/Avalonia.Visuals.UnitTests/Media/TextDecorationTests.cs rename to tests/Avalonia.Base.UnitTests/Media/TextDecorationTests.cs index 60a41baaa7..c4537ea2f8 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/TextDecorationTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/TextDecorationTests.cs @@ -1,7 +1,7 @@ using Avalonia.Media; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media +namespace Avalonia.Base.UnitTests.Media { public class TextDecorationTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/BiDiAlgorithmTests.cs b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/BiDiAlgorithmTests.cs similarity index 100% rename from tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/BiDiAlgorithmTests.cs rename to tests/Avalonia.Base.UnitTests/Media/TextFormatting/BiDiAlgorithmTests.cs diff --git a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/BiDiClassTestDataGenerator.cs b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/BiDiClassTestDataGenerator.cs similarity index 98% rename from tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/BiDiClassTestDataGenerator.cs rename to tests/Avalonia.Base.UnitTests/Media/TextFormatting/BiDiClassTestDataGenerator.cs index 2f1064be21..f6b01d737c 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/BiDiClassTestDataGenerator.cs +++ b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/BiDiClassTestDataGenerator.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Http; +using Avalonia.Base.UnitTests.Media.TextFormatting; namespace Avalonia.Visuals.UnitTests.Media.TextFormatting { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/BiDiClassTests.cs b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/BiDiClassTests.cs similarity index 100% rename from tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/BiDiClassTests.cs rename to tests/Avalonia.Base.UnitTests/Media/TextFormatting/BiDiClassTests.cs diff --git a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/BiDiTestDataGenerator.cs b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/BiDiTestDataGenerator.cs similarity index 98% rename from tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/BiDiTestDataGenerator.cs rename to tests/Avalonia.Base.UnitTests/Media/TextFormatting/BiDiTestDataGenerator.cs index 4102de10ec..033e0e2242 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/BiDiTestDataGenerator.cs +++ b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/BiDiTestDataGenerator.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Http; +using Avalonia.Base.UnitTests.Media.TextFormatting; using Avalonia.Media.TextFormatting.Unicode; namespace Avalonia.Visuals.UnitTests.Media.TextFormatting diff --git a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/BreakPairTable.txt b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/BreakPairTable.txt similarity index 100% rename from tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/BreakPairTable.txt rename to tests/Avalonia.Base.UnitTests/Media/TextFormatting/BreakPairTable.txt diff --git a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGenerator.cs b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGenerator.cs similarity index 98% rename from tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGenerator.cs rename to tests/Avalonia.Base.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGenerator.cs index c074e5a1f1..7ad2e71d77 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGenerator.cs +++ b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGenerator.cs @@ -5,7 +5,7 @@ using System.Net.Http; using System.Text.RegularExpressions; using Avalonia.Media.TextFormatting.Unicode; -namespace Avalonia.Visuals.UnitTests.Media.TextFormatting +namespace Avalonia.Base.UnitTests.Media.TextFormatting { public static class GraphemeBreakClassTrieGenerator { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGeneratorTests.cs b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGeneratorTests.cs similarity index 96% rename from tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGeneratorTests.cs rename to tests/Avalonia.Base.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGeneratorTests.cs index 848b60b341..e1e3e14ea7 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGeneratorTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/GraphemeBreakClassTrieGeneratorTests.cs @@ -2,7 +2,7 @@ using Avalonia.Media.TextFormatting.Unicode; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media.TextFormatting +namespace Avalonia.Base.UnitTests.Media.TextFormatting { /// /// This class is intended for use when the Unicode spec changes. Otherwise the containing tests are redundant. diff --git a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/LineBreakEnumuratorTests.cs b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/LineBreakEnumuratorTests.cs similarity index 99% rename from tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/LineBreakEnumuratorTests.cs rename to tests/Avalonia.Base.UnitTests/Media/TextFormatting/LineBreakEnumuratorTests.cs index a90be6d519..1dbfbe4d24 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/LineBreakEnumuratorTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/LineBreakEnumuratorTests.cs @@ -8,7 +8,7 @@ using Avalonia.Media.TextFormatting.Unicode; using Xunit; using Xunit.Abstractions; -namespace Avalonia.Visuals.UnitTests.Media.TextFormatting +namespace Avalonia.Base.UnitTests.Media.TextFormatting { public class LineBreakEnumeratorTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/TestDataGenerator.cs b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/TestDataGenerator.cs similarity index 97% rename from tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/TestDataGenerator.cs rename to tests/Avalonia.Base.UnitTests/Media/TextFormatting/TestDataGenerator.cs index 058de909df..33b4c8a269 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/TestDataGenerator.cs +++ b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/TestDataGenerator.cs @@ -5,7 +5,7 @@ using System.IO; using System.Linq; using System.Net.Http; -namespace Avalonia.Visuals.UnitTests.Media.TextFormatting +namespace Avalonia.Base.UnitTests.Media.TextFormatting { public abstract class TestDataGenerator : IEnumerable { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/UnicodeDataGenerator.cs b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/UnicodeDataGenerator.cs similarity index 99% rename from tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/UnicodeDataGenerator.cs rename to tests/Avalonia.Base.UnitTests/Media/TextFormatting/UnicodeDataGenerator.cs index da9e494405..e2877abd83 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/UnicodeDataGenerator.cs +++ b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/UnicodeDataGenerator.cs @@ -6,7 +6,7 @@ using System.Text.RegularExpressions; using Avalonia.Media.TextFormatting.Unicode; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media.TextFormatting +namespace Avalonia.Base.UnitTests.Media.TextFormatting { internal static class UnicodeDataGenerator { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/UnicodeDataGeneratorTests.cs b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/UnicodeDataGeneratorTests.cs similarity index 97% rename from tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/UnicodeDataGeneratorTests.cs rename to tests/Avalonia.Base.UnitTests/Media/TextFormatting/UnicodeDataGeneratorTests.cs index f122876322..f3aea83316 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/UnicodeDataGeneratorTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/UnicodeDataGeneratorTests.cs @@ -2,7 +2,7 @@ using Avalonia.Media.TextFormatting.Unicode; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media.TextFormatting +namespace Avalonia.Base.UnitTests.Media.TextFormatting { public class UnicodeDataGeneratorTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/UnicodeEnumsGenerator.cs b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/UnicodeEnumsGenerator.cs similarity index 99% rename from tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/UnicodeEnumsGenerator.cs rename to tests/Avalonia.Base.UnitTests/Media/TextFormatting/UnicodeEnumsGenerator.cs index e483728423..1323ddfbd1 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/TextFormatting/UnicodeEnumsGenerator.cs +++ b/tests/Avalonia.Base.UnitTests/Media/TextFormatting/UnicodeEnumsGenerator.cs @@ -4,7 +4,7 @@ using System.IO; using System.Linq; using System.Net.Http; -namespace Avalonia.Visuals.UnitTests.Media.TextFormatting +namespace Avalonia.Base.UnitTests.Media.TextFormatting { internal static class UnicodeEnumsGenerator { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/TransformOperationsTests.cs b/tests/Avalonia.Base.UnitTests/Media/TransformOperationsTests.cs similarity index 99% rename from tests/Avalonia.Visuals.UnitTests/Media/TransformOperationsTests.cs rename to tests/Avalonia.Base.UnitTests/Media/TransformOperationsTests.cs index e4f88706d8..0d84ba1700 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/TransformOperationsTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/TransformOperationsTests.cs @@ -2,7 +2,7 @@ using Avalonia.Media.Transformation; using Avalonia.Utilities; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media +namespace Avalonia.Base.UnitTests.Media { public class TransformOperationsTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/TypefaceTests.cs b/tests/Avalonia.Base.UnitTests/Media/TypefaceTests.cs similarity index 93% rename from tests/Avalonia.Visuals.UnitTests/Media/TypefaceTests.cs rename to tests/Avalonia.Base.UnitTests/Media/TypefaceTests.cs index e526172622..826c1f292b 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/TypefaceTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/TypefaceTests.cs @@ -2,7 +2,7 @@ using Avalonia.Media; using Xunit; -namespace Avalonia.Visuals.UnitTests.Media +namespace Avalonia.Base.UnitTests.Media { public class TypefaceTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/UnicodeRangeSegmentTests.cs b/tests/Avalonia.Base.UnitTests/Media/UnicodeRangeSegmentTests.cs similarity index 100% rename from tests/Avalonia.Visuals.UnitTests/Media/UnicodeRangeSegmentTests.cs rename to tests/Avalonia.Base.UnitTests/Media/UnicodeRangeSegmentTests.cs diff --git a/tests/Avalonia.Visuals.UnitTests/Media/UnicodeRangeTests.cs b/tests/Avalonia.Base.UnitTests/Media/UnicodeRangeTests.cs similarity index 100% rename from tests/Avalonia.Visuals.UnitTests/Media/UnicodeRangeTests.cs rename to tests/Avalonia.Base.UnitTests/Media/UnicodeRangeTests.cs diff --git a/tests/Avalonia.Visuals.UnitTests/RectTests.cs b/tests/Avalonia.Base.UnitTests/RectTests.cs similarity index 97% rename from tests/Avalonia.Visuals.UnitTests/RectTests.cs rename to tests/Avalonia.Base.UnitTests/RectTests.cs index a2b0569949..95a438b287 100644 --- a/tests/Avalonia.Visuals.UnitTests/RectTests.cs +++ b/tests/Avalonia.Base.UnitTests/RectTests.cs @@ -1,6 +1,6 @@ using Xunit; -namespace Avalonia.Visuals.UnitTests +namespace Avalonia.Base.UnitTests { public class RectTests { diff --git a/tests/Avalonia.Visuals.UnitTests/RelativePointTests.cs b/tests/Avalonia.Base.UnitTests/RelativePointTests.cs similarity index 89% rename from tests/Avalonia.Visuals.UnitTests/RelativePointTests.cs rename to tests/Avalonia.Base.UnitTests/RelativePointTests.cs index c871b8ee87..c242a504b9 100644 --- a/tests/Avalonia.Visuals.UnitTests/RelativePointTests.cs +++ b/tests/Avalonia.Base.UnitTests/RelativePointTests.cs @@ -1,7 +1,6 @@ -using System.Globalization; using Xunit; -namespace Avalonia.Visuals.UnitTests +namespace Avalonia.Base.UnitTests { public class RelativePointTests { diff --git a/tests/Avalonia.Visuals.UnitTests/RelativeRectComparer.cs b/tests/Avalonia.Base.UnitTests/RelativeRectComparer.cs similarity index 94% rename from tests/Avalonia.Visuals.UnitTests/RelativeRectComparer.cs rename to tests/Avalonia.Base.UnitTests/RelativeRectComparer.cs index bf3bf39367..3e0acddb02 100644 --- a/tests/Avalonia.Visuals.UnitTests/RelativeRectComparer.cs +++ b/tests/Avalonia.Base.UnitTests/RelativeRectComparer.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -namespace Avalonia.Visuals.UnitTests +namespace Avalonia.Base.UnitTests { public class RelativeRectComparer : IEqualityComparer { diff --git a/tests/Avalonia.Visuals.UnitTests/RelativeRectTests.cs b/tests/Avalonia.Base.UnitTests/RelativeRectTests.cs similarity index 93% rename from tests/Avalonia.Visuals.UnitTests/RelativeRectTests.cs rename to tests/Avalonia.Base.UnitTests/RelativeRectTests.cs index 9753e0779a..ff53419d5c 100644 --- a/tests/Avalonia.Visuals.UnitTests/RelativeRectTests.cs +++ b/tests/Avalonia.Base.UnitTests/RelativeRectTests.cs @@ -1,8 +1,7 @@ using System; -using System.Globalization; using Xunit; -namespace Avalonia.Visuals.UnitTests +namespace Avalonia.Base.UnitTests { public class RelativeRectTests { diff --git a/tests/Avalonia.Visuals.UnitTests/RenderTests_Culling.cs b/tests/Avalonia.Base.UnitTests/RenderTests_Culling.cs similarity index 99% rename from tests/Avalonia.Visuals.UnitTests/RenderTests_Culling.cs rename to tests/Avalonia.Base.UnitTests/RenderTests_Culling.cs index ba632b791a..c7421c5827 100644 --- a/tests/Avalonia.Visuals.UnitTests/RenderTests_Culling.cs +++ b/tests/Avalonia.Base.UnitTests/RenderTests_Culling.cs @@ -1,12 +1,12 @@ -using Moq; using Avalonia.Controls; using Avalonia.Media; -using Avalonia.Rendering; -using Xunit; using Avalonia.Platform; +using Avalonia.Rendering; using Avalonia.UnitTests; +using Moq; +using Xunit; -namespace Avalonia.Visuals.UnitTests +namespace Avalonia.Base.UnitTests { public class RenderTests_Culling { diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/CustomHitTestBorder.cs b/tests/Avalonia.Base.UnitTests/Rendering/CustomHitTestBorder.cs similarity index 84% rename from tests/Avalonia.Visuals.UnitTests/Rendering/CustomHitTestBorder.cs rename to tests/Avalonia.Base.UnitTests/Rendering/CustomHitTestBorder.cs index 3221f097ad..fece152713 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/CustomHitTestBorder.cs +++ b/tests/Avalonia.Base.UnitTests/Rendering/CustomHitTestBorder.cs @@ -1,8 +1,7 @@ using Avalonia.Controls; -using Avalonia.Media; using Avalonia.Rendering; -namespace Avalonia.Visuals.UnitTests.Rendering +namespace Avalonia.Base.UnitTests.Rendering { internal class CustomHitTestBorder : Border, ICustomHitTest { diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs b/tests/Avalonia.Base.UnitTests/Rendering/DeferredRendererTests.cs similarity index 99% rename from tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs rename to tests/Avalonia.Base.UnitTests/Rendering/DeferredRendererTests.cs index e58eea42d8..c345b9a7ea 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs +++ b/tests/Avalonia.Base.UnitTests/Rendering/DeferredRendererTests.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Reactive.Subjects; -using System.Threading.Tasks; using Avalonia.Controls; using Avalonia.Data; using Avalonia.Media; @@ -11,14 +10,12 @@ using Avalonia.Rendering; using Avalonia.Rendering.SceneGraph; using Avalonia.Threading; using Avalonia.UnitTests; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; using Avalonia.VisualTree; - using Moq; - using Xunit; -namespace Avalonia.Visuals.UnitTests.Rendering +namespace Avalonia.Base.UnitTests.Rendering { public class DeferredRendererTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests_HitTesting.cs b/tests/Avalonia.Base.UnitTests/Rendering/DeferredRendererTests_HitTesting.cs similarity index 99% rename from tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests_HitTesting.cs rename to tests/Avalonia.Base.UnitTests/Rendering/DeferredRendererTests_HitTesting.cs index d24d183709..33c7b3e0f8 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests_HitTesting.cs +++ b/tests/Avalonia.Base.UnitTests/Rendering/DeferredRendererTests_HitTesting.cs @@ -3,7 +3,6 @@ using System.Linq; using Avalonia.Controls; using Avalonia.Controls.Presenters; using Avalonia.Controls.Shapes; -using Avalonia.Input; using Avalonia.Layout; using Avalonia.Media; using Avalonia.Platform; @@ -13,7 +12,7 @@ using Avalonia.VisualTree; using Moq; using Xunit; -namespace Avalonia.Visuals.UnitTests.Rendering +namespace Avalonia.Base.UnitTests.Rendering { public class DeferredRendererTests_HitTesting { diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/ImmediateRendererTests.cs b/tests/Avalonia.Base.UnitTests/Rendering/ImmediateRendererTests.cs similarity index 99% rename from tests/Avalonia.Visuals.UnitTests/Rendering/ImmediateRendererTests.cs rename to tests/Avalonia.Base.UnitTests/Rendering/ImmediateRendererTests.cs index 6859b2b139..84bb9b00cd 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/ImmediateRendererTests.cs +++ b/tests/Avalonia.Base.UnitTests/Rendering/ImmediateRendererTests.cs @@ -10,7 +10,7 @@ using Avalonia.VisualTree; using Moq; using Xunit; -namespace Avalonia.Visuals.UnitTests.Rendering +namespace Avalonia.Base.UnitTests.Rendering { public class ImmediateRendererTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/ImmediateRendererTests_HitTesting.cs b/tests/Avalonia.Base.UnitTests/Rendering/ImmediateRendererTests_HitTesting.cs similarity index 99% rename from tests/Avalonia.Visuals.UnitTests/Rendering/ImmediateRendererTests_HitTesting.cs rename to tests/Avalonia.Base.UnitTests/Rendering/ImmediateRendererTests_HitTesting.cs index a3b0a0cdd5..1a9822e259 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/ImmediateRendererTests_HitTesting.cs +++ b/tests/Avalonia.Base.UnitTests/Rendering/ImmediateRendererTests_HitTesting.cs @@ -9,7 +9,7 @@ using Avalonia.UnitTests; using Avalonia.VisualTree; using Xunit; -namespace Avalonia.Visuals.UnitTests.Rendering +namespace Avalonia.Base.UnitTests.Rendering { public class ImmediateRendererTests_HitTesting { diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/RenderLoopTests.cs b/tests/Avalonia.Base.UnitTests/Rendering/RenderLoopTests.cs similarity index 97% rename from tests/Avalonia.Visuals.UnitTests/Rendering/RenderLoopTests.cs rename to tests/Avalonia.Base.UnitTests/Rendering/RenderLoopTests.cs index e9aefc46e6..6855a378b4 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/RenderLoopTests.cs +++ b/tests/Avalonia.Base.UnitTests/Rendering/RenderLoopTests.cs @@ -1,13 +1,11 @@ using System; -using System.Collections.Generic; -using System.Text; using System.Threading.Tasks; using Avalonia.Rendering; using Avalonia.Threading; using Moq; using Xunit; -namespace Avalonia.Visuals.UnitTests.Rendering +namespace Avalonia.Base.UnitTests.Rendering { public class RenderLoopTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DeferredDrawingContextImplTests.cs b/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/DeferredDrawingContextImplTests.cs similarity index 98% rename from tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DeferredDrawingContextImplTests.cs rename to tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/DeferredDrawingContextImplTests.cs index 27aa0c55b5..8f307978f2 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DeferredDrawingContextImplTests.cs +++ b/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/DeferredDrawingContextImplTests.cs @@ -1,7 +1,5 @@ -using System; -using System.Linq; +using System.Linq; using Avalonia.Media; -using Avalonia.Platform; using Avalonia.Rendering.SceneGraph; using Avalonia.UnitTests; using Avalonia.Utilities; @@ -9,7 +7,7 @@ using Avalonia.VisualTree; using Moq; using Xunit; -namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph +namespace Avalonia.Base.UnitTests.Rendering.SceneGraph { public class DeferredDrawingContextImplTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DrawOperationTests.cs b/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/DrawOperationTests.cs similarity index 96% rename from tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DrawOperationTests.cs rename to tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/DrawOperationTests.cs index 0cdc8827ad..2fac968206 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/DrawOperationTests.cs +++ b/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/DrawOperationTests.cs @@ -2,11 +2,11 @@ using Avalonia.Platform; using Avalonia.Rendering.SceneGraph; using Avalonia.Utilities; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; using Moq; using Xunit; -namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph +namespace Avalonia.Base.UnitTests.Rendering.SceneGraph { public class DrawOperationTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/EllipseNodeTests.cs b/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/EllipseNodeTests.cs similarity index 100% rename from tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/EllipseNodeTests.cs rename to tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/EllipseNodeTests.cs diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/LineNodeTests.cs b/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/LineNodeTests.cs similarity index 100% rename from tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/LineNodeTests.cs rename to tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/LineNodeTests.cs diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs b/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs similarity index 99% rename from tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs rename to tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs index e317b43efc..01afe85b8b 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs +++ b/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs @@ -1,20 +1,19 @@ -using System; -using System.Linq; +using System.Linq; +using System.Reactive.Subjects; using Avalonia.Controls; +using Avalonia.Data; +using Avalonia.Layout; using Avalonia.Media; +using Avalonia.Media.Imaging; +using Avalonia.Platform; using Avalonia.Rendering.SceneGraph; using Avalonia.UnitTests; +using Avalonia.Utilities; using Avalonia.VisualTree; -using Xunit; -using Avalonia.Layout; using Moq; -using Avalonia.Platform; -using System.Reactive.Subjects; -using Avalonia.Data; -using Avalonia.Utilities; -using Avalonia.Media.Imaging; +using Xunit; -namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph +namespace Avalonia.Base.UnitTests.Rendering.SceneGraph { public partial class SceneBuilderTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests_Layers.cs b/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/SceneBuilderTests_Layers.cs similarity index 98% rename from tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests_Layers.cs rename to tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/SceneBuilderTests_Layers.cs index 19b4c7e606..9f5a0363ed 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests_Layers.cs +++ b/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/SceneBuilderTests_Layers.cs @@ -1,16 +1,14 @@ -using System; -using System.Linq; +using System.Linq; +using System.Reactive.Subjects; using Avalonia.Controls; +using Avalonia.Data; using Avalonia.Media; using Avalonia.Rendering.SceneGraph; using Avalonia.UnitTests; using Avalonia.VisualTree; using Xunit; -using Avalonia.Layout; -using System.Reactive.Subjects; -using Avalonia.Data; -namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph +namespace Avalonia.Base.UnitTests.Rendering.SceneGraph { public partial class SceneBuilderTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneLayersTests.cs b/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/SceneLayersTests.cs similarity index 89% rename from tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneLayersTests.cs rename to tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/SceneLayersTests.cs index fac6e1ceb6..9d25a3aa44 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneLayersTests.cs +++ b/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/SceneLayersTests.cs @@ -1,12 +1,11 @@ -using System; -using System.Linq; +using System.Linq; using Avalonia.Controls; using Avalonia.Rendering.SceneGraph; using Avalonia.UnitTests; using Avalonia.VisualTree; using Xunit; -namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph +namespace Avalonia.Base.UnitTests.Rendering.SceneGraph { public class SceneLayersTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneTests.cs b/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/SceneTests.cs similarity index 90% rename from tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneTests.cs rename to tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/SceneTests.cs index bba0c8b8f3..18ff31f676 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneTests.cs +++ b/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/SceneTests.cs @@ -1,11 +1,10 @@ -using System; -using System.Linq; +using System.Linq; using Avalonia.Controls; using Avalonia.Rendering.SceneGraph; using Avalonia.UnitTests; using Xunit; -namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph +namespace Avalonia.Base.UnitTests.Rendering.SceneGraph { public class SceneTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/VisualNodeTests.cs b/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/VisualNodeTests.cs similarity index 97% rename from tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/VisualNodeTests.cs rename to tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/VisualNodeTests.cs index 4ec3630053..93266c428c 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/VisualNodeTests.cs +++ b/tests/Avalonia.Base.UnitTests/Rendering/SceneGraph/VisualNodeTests.cs @@ -1,11 +1,10 @@ -using System; -using Avalonia.Rendering.SceneGraph; +using Avalonia.Rendering.SceneGraph; using Avalonia.Utilities; using Avalonia.VisualTree; using Moq; using Xunit; -namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph +namespace Avalonia.Base.UnitTests.Rendering.SceneGraph { public class VisualNodeTests { diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/Utilities/TileBrushCalculatorTests.cs b/tests/Avalonia.Base.UnitTests/Rendering/Utilities/TileBrushCalculatorTests.cs similarity index 98% rename from tests/Avalonia.Visuals.UnitTests/Rendering/Utilities/TileBrushCalculatorTests.cs rename to tests/Avalonia.Base.UnitTests/Rendering/Utilities/TileBrushCalculatorTests.cs index 8d70b45842..349fd0a6d0 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/Utilities/TileBrushCalculatorTests.cs +++ b/tests/Avalonia.Base.UnitTests/Rendering/Utilities/TileBrushCalculatorTests.cs @@ -1,10 +1,9 @@ -using System; -using Avalonia.Controls; +using Avalonia.Controls; using Avalonia.Media; using Avalonia.Rendering.Utilities; using Xunit; -namespace Avalonia.Visuals.UnitTests.Rendering.Utilities +namespace Avalonia.Base.UnitTests.Rendering.Utilities { public class TileBrushCalculatorTests { diff --git a/tests/Avalonia.Visuals.UnitTests/SizeTests.cs b/tests/Avalonia.Base.UnitTests/SizeTests.cs similarity index 92% rename from tests/Avalonia.Visuals.UnitTests/SizeTests.cs rename to tests/Avalonia.Base.UnitTests/SizeTests.cs index 96197785b9..9b8125748a 100644 --- a/tests/Avalonia.Visuals.UnitTests/SizeTests.cs +++ b/tests/Avalonia.Base.UnitTests/SizeTests.cs @@ -1,6 +1,6 @@ using Xunit; -namespace Avalonia.Visuals.UnitTests +namespace Avalonia.Base.UnitTests { public class SizeTests { diff --git a/tests/Avalonia.Styling.UnitTests/ResourceDictionaryTests.cs b/tests/Avalonia.Base.UnitTests/Styling/ResourceDictionaryTests.cs similarity index 99% rename from tests/Avalonia.Styling.UnitTests/ResourceDictionaryTests.cs rename to tests/Avalonia.Base.UnitTests/Styling/ResourceDictionaryTests.cs index d91d1fab9e..86b1b897d4 100644 --- a/tests/Avalonia.Styling.UnitTests/ResourceDictionaryTests.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/ResourceDictionaryTests.cs @@ -3,7 +3,7 @@ using Avalonia.Controls; using Moq; using Xunit; -namespace Avalonia.Styling.UnitTests +namespace Avalonia.Base.UnitTests.Styling { public class ResourceDictionaryTests { diff --git a/tests/Avalonia.Styling.UnitTests/SelectorTests_Child.cs b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Child.cs similarity index 97% rename from tests/Avalonia.Styling.UnitTests/SelectorTests_Child.cs rename to tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Child.cs index d011c16ebb..66efe2cc7b 100644 --- a/tests/Avalonia.Styling.UnitTests/SelectorTests_Child.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Child.cs @@ -1,11 +1,11 @@ using System.Collections.Generic; -using System.Reactive.Linq; using System.Threading.Tasks; using Avalonia.Controls; using Avalonia.LogicalTree; +using Avalonia.Styling; using Xunit; -namespace Avalonia.Styling.UnitTests +namespace Avalonia.Base.UnitTests.Styling { public class SelectorTests_Child { diff --git a/tests/Avalonia.Styling.UnitTests/SelectorTests_Class.cs b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Class.cs similarity index 98% rename from tests/Avalonia.Styling.UnitTests/SelectorTests_Class.cs rename to tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Class.cs index 72383501be..376e2b23d0 100644 --- a/tests/Avalonia.Styling.UnitTests/SelectorTests_Class.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Class.cs @@ -1,14 +1,12 @@ -using System; -using System.Linq; +using System.Collections.Generic; using System.Reactive.Linq; using System.Threading.Tasks; -using Moq; using Avalonia.Controls; using Avalonia.Styling; +using Moq; using Xunit; -using System.Collections.Generic; -namespace Avalonia.Styling.UnitTests +namespace Avalonia.Base.UnitTests.Styling { public class SelectorTests_Class { diff --git a/tests/Avalonia.Styling.UnitTests/SelectorTests_Descendent.cs b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Descendent.cs similarity index 98% rename from tests/Avalonia.Styling.UnitTests/SelectorTests_Descendent.cs rename to tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Descendent.cs index aacf2ce223..6b52936ec5 100644 --- a/tests/Avalonia.Styling.UnitTests/SelectorTests_Descendent.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Descendent.cs @@ -1,11 +1,11 @@ -using System.Linq; using System.Reactive.Linq; using System.Threading.Tasks; using Avalonia.Controls; using Avalonia.LogicalTree; +using Avalonia.Styling; using Xunit; -namespace Avalonia.Styling.UnitTests +namespace Avalonia.Base.UnitTests.Styling { public class SelectorTests_Descendant { diff --git a/tests/Avalonia.Styling.UnitTests/SelectorTests_Multiple.cs b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Multiple.cs similarity index 99% rename from tests/Avalonia.Styling.UnitTests/SelectorTests_Multiple.cs rename to tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Multiple.cs index 4436f02429..258a4a44a1 100644 --- a/tests/Avalonia.Styling.UnitTests/SelectorTests_Multiple.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Multiple.cs @@ -1,13 +1,13 @@ -using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Avalonia.Controls; using Avalonia.Controls.Templates; +using Avalonia.Styling; using Avalonia.VisualTree; using Xunit; -namespace Avalonia.Styling.UnitTests +namespace Avalonia.Base.UnitTests.Styling { public class SelectorTests_Multiple { diff --git a/tests/Avalonia.Styling.UnitTests/SelectorTests_Name.cs b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Name.cs similarity index 96% rename from tests/Avalonia.Styling.UnitTests/SelectorTests_Name.cs rename to tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Name.cs index 308070c52f..46b97cf384 100644 --- a/tests/Avalonia.Styling.UnitTests/SelectorTests_Name.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Name.cs @@ -1,8 +1,9 @@ using Avalonia.Controls; +using Avalonia.Styling; using Moq; using Xunit; -namespace Avalonia.Styling.UnitTests +namespace Avalonia.Base.UnitTests.Styling { public class SelectorTests_Name { diff --git a/tests/Avalonia.Styling.UnitTests/SelectorTests_Not.cs b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Not.cs similarity index 97% rename from tests/Avalonia.Styling.UnitTests/SelectorTests_Not.cs rename to tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Not.cs index 23b711725a..fd1385ac47 100644 --- a/tests/Avalonia.Styling.UnitTests/SelectorTests_Not.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Not.cs @@ -1,9 +1,9 @@ -using System.Reactive.Linq; using System.Threading.Tasks; using Avalonia.Controls; +using Avalonia.Styling; using Xunit; -namespace Avalonia.Styling.UnitTests +namespace Avalonia.Base.UnitTests.Styling { public class SelectorTests_Not { diff --git a/tests/Avalonia.Styling.UnitTests/SelectorTests_NthChild.cs b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_NthChild.cs similarity index 99% rename from tests/Avalonia.Styling.UnitTests/SelectorTests_NthChild.cs rename to tests/Avalonia.Base.UnitTests/Styling/SelectorTests_NthChild.cs index e1507be110..7f645be4a9 100644 --- a/tests/Avalonia.Styling.UnitTests/SelectorTests_NthChild.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_NthChild.cs @@ -1,9 +1,10 @@ using System.Linq; using System.Threading.Tasks; using Avalonia.Controls; +using Avalonia.Styling; using Xunit; -namespace Avalonia.Styling.UnitTests +namespace Avalonia.Base.UnitTests.Styling { public class SelectorTests_NthChild { diff --git a/tests/Avalonia.Styling.UnitTests/SelectorTests_NthLastChild.cs b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_NthLastChild.cs similarity index 99% rename from tests/Avalonia.Styling.UnitTests/SelectorTests_NthLastChild.cs rename to tests/Avalonia.Base.UnitTests/Styling/SelectorTests_NthLastChild.cs index 00a99523c7..821ed3d0ab 100644 --- a/tests/Avalonia.Styling.UnitTests/SelectorTests_NthLastChild.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_NthLastChild.cs @@ -1,8 +1,9 @@ using System.Threading.Tasks; using Avalonia.Controls; +using Avalonia.Styling; using Xunit; -namespace Avalonia.Styling.UnitTests +namespace Avalonia.Base.UnitTests.Styling { public class SelectorTests_NthLastChild { diff --git a/tests/Avalonia.Styling.UnitTests/SelectorTests_OfType.cs b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_OfType.cs similarity index 95% rename from tests/Avalonia.Styling.UnitTests/SelectorTests_OfType.cs rename to tests/Avalonia.Base.UnitTests/Styling/SelectorTests_OfType.cs index 2aef46575d..e86a323c71 100644 --- a/tests/Avalonia.Styling.UnitTests/SelectorTests_OfType.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_OfType.cs @@ -1,8 +1,9 @@ using Avalonia.Controls; +using Avalonia.Styling; using Moq; using Xunit; -namespace Avalonia.Styling.UnitTests +namespace Avalonia.Base.UnitTests.Styling { public class SelectorTests_OfType { diff --git a/tests/Avalonia.Styling.UnitTests/SelectorTests_Or.cs b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Or.cs similarity index 98% rename from tests/Avalonia.Styling.UnitTests/SelectorTests_Or.cs rename to tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Or.cs index 2b55482593..fb5d54bd1f 100644 --- a/tests/Avalonia.Styling.UnitTests/SelectorTests_Or.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Or.cs @@ -1,7 +1,8 @@ using Avalonia.Controls; +using Avalonia.Styling; using Xunit; -namespace Avalonia.Styling.UnitTests +namespace Avalonia.Base.UnitTests.Styling { public class SelectorTests_Or { diff --git a/tests/Avalonia.Styling.UnitTests/SelectorTests_PropertyEquals.cs b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_PropertyEquals.cs similarity index 98% rename from tests/Avalonia.Styling.UnitTests/SelectorTests_PropertyEquals.cs rename to tests/Avalonia.Base.UnitTests/Styling/SelectorTests_PropertyEquals.cs index 729b42196b..ee1b654ba8 100644 --- a/tests/Avalonia.Styling.UnitTests/SelectorTests_PropertyEquals.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_PropertyEquals.cs @@ -1,10 +1,10 @@ -using System.Linq; using System.Reactive.Linq; using System.Threading.Tasks; using Avalonia.Controls; +using Avalonia.Styling; using Xunit; -namespace Avalonia.Styling.UnitTests +namespace Avalonia.Base.UnitTests.Styling { public class SelectorTests_PropertyEquals { diff --git a/tests/Avalonia.Styling.UnitTests/SelectorTests_Template.cs b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Template.cs similarity index 98% rename from tests/Avalonia.Styling.UnitTests/SelectorTests_Template.cs rename to tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Template.cs index 13cb2578cf..8b4c988037 100644 --- a/tests/Avalonia.Styling.UnitTests/SelectorTests_Template.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Template.cs @@ -1,14 +1,13 @@ -using System; using System.Linq; -using System.Reactive.Linq; -using Moq; +using System.Threading.Tasks; using Avalonia.Controls; -using Avalonia.VisualTree; using Avalonia.Diagnostics; +using Avalonia.Styling; +using Avalonia.VisualTree; +using Moq; using Xunit; -using System.Threading.Tasks; -namespace Avalonia.Styling.UnitTests +namespace Avalonia.Base.UnitTests.Styling { public class SelectorTests_Template { diff --git a/tests/Avalonia.Styling.UnitTests/SetterTests.cs b/tests/Avalonia.Base.UnitTests/Styling/SetterTests.cs similarity index 98% rename from tests/Avalonia.Styling.UnitTests/SetterTests.cs rename to tests/Avalonia.Base.UnitTests/Styling/SetterTests.cs index 901233801e..40d2cab4a3 100644 --- a/tests/Avalonia.Styling.UnitTests/SetterTests.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/SetterTests.cs @@ -5,10 +5,11 @@ using Avalonia.Controls; using Avalonia.Controls.Templates; using Avalonia.Data; using Avalonia.Data.Converters; +using Avalonia.Styling; using Moq; using Xunit; -namespace Avalonia.Styling.UnitTests +namespace Avalonia.Base.UnitTests.Styling { public class SetterTests { diff --git a/tests/Avalonia.Styling.UnitTests/StyleActivatorExtensions.cs b/tests/Avalonia.Base.UnitTests/Styling/StyleActivatorExtensions.cs similarity index 97% rename from tests/Avalonia.Styling.UnitTests/StyleActivatorExtensions.cs rename to tests/Avalonia.Base.UnitTests/Styling/StyleActivatorExtensions.cs index 22f4db79d1..f5b3bb40a3 100644 --- a/tests/Avalonia.Styling.UnitTests/StyleActivatorExtensions.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/StyleActivatorExtensions.cs @@ -4,7 +4,7 @@ using System.Threading.Tasks; using Avalonia.Reactive; using Avalonia.Styling.Activators; -namespace Avalonia.Styling.UnitTests +namespace Avalonia.Base.UnitTests.Styling { internal static class StyleActivatorExtensions { diff --git a/tests/Avalonia.Styling.UnitTests/StyleTests.cs b/tests/Avalonia.Base.UnitTests/Styling/StyleTests.cs similarity index 99% rename from tests/Avalonia.Styling.UnitTests/StyleTests.cs rename to tests/Avalonia.Base.UnitTests/Styling/StyleTests.cs index 14710f44ba..8dedf3471f 100644 --- a/tests/Avalonia.Styling.UnitTests/StyleTests.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/StyleTests.cs @@ -3,11 +3,12 @@ using System.Collections.Generic; using Avalonia.Controls; using Avalonia.Controls.Templates; using Avalonia.Data; +using Avalonia.Styling; using Avalonia.UnitTests; using Moq; using Xunit; -namespace Avalonia.Styling.UnitTests +namespace Avalonia.Base.UnitTests.Styling { public class StyleTests { diff --git a/tests/Avalonia.Styling.UnitTests/StyledElementTests.cs b/tests/Avalonia.Base.UnitTests/Styling/StyledElementTests.cs similarity index 99% rename from tests/Avalonia.Styling.UnitTests/StyledElementTests.cs rename to tests/Avalonia.Base.UnitTests/Styling/StyledElementTests.cs index fcbccba695..bb4d590060 100644 --- a/tests/Avalonia.Styling.UnitTests/StyledElementTests.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/StyledElementTests.cs @@ -1,19 +1,17 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Reactive.Linq; -using Moq; -using Avalonia.Styling; -using Avalonia.UnitTests; -using Xunit; -using Avalonia.LogicalTree; using Avalonia.Controls; -using System.ComponentModel; -using Avalonia.Controls.Primitives; -using Avalonia.Markup.Xaml.Templates; using Avalonia.Controls.Templates; using Avalonia.Data; +using Avalonia.LogicalTree; +using Avalonia.Styling; +using Avalonia.UnitTests; +using Moq; +using Xunit; -namespace Avalonia.Styling.UnitTests +namespace Avalonia.Base.UnitTests.Styling { public class StyledElementTests { diff --git a/tests/Avalonia.Styling.UnitTests/StyledElementTests_Resources.cs b/tests/Avalonia.Base.UnitTests/Styling/StyledElementTests_Resources.cs similarity index 98% rename from tests/Avalonia.Styling.UnitTests/StyledElementTests_Resources.cs rename to tests/Avalonia.Base.UnitTests/Styling/StyledElementTests_Resources.cs index 05315380fd..69d37d1c54 100644 --- a/tests/Avalonia.Styling.UnitTests/StyledElementTests_Resources.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/StyledElementTests_Resources.cs @@ -1,11 +1,11 @@ -using System; +using Avalonia.Controls; using Avalonia.Controls.Presenters; using Avalonia.Controls.Templates; using Avalonia.Styling; using Avalonia.UnitTests; using Xunit; -namespace Avalonia.Controls.UnitTests +namespace Avalonia.Base.UnitTests.Styling { public class StyledElementTests_Resources { diff --git a/tests/Avalonia.Styling.UnitTests/StylesTests.cs b/tests/Avalonia.Base.UnitTests/Styling/StylesTests.cs similarity index 98% rename from tests/Avalonia.Styling.UnitTests/StylesTests.cs rename to tests/Avalonia.Base.UnitTests/Styling/StylesTests.cs index 88ab124e01..a6777c9466 100644 --- a/tests/Avalonia.Styling.UnitTests/StylesTests.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/StylesTests.cs @@ -1,9 +1,9 @@ -using System; using Avalonia.Controls; +using Avalonia.Styling; using Moq; using Xunit; -namespace Avalonia.Styling.UnitTests +namespace Avalonia.Base.UnitTests.Styling { public class StylesTests { diff --git a/tests/Avalonia.Styling.UnitTests/TestObservable.cs b/tests/Avalonia.Base.UnitTests/Styling/TestObservable.cs similarity index 90% rename from tests/Avalonia.Styling.UnitTests/TestObservable.cs rename to tests/Avalonia.Base.UnitTests/Styling/TestObservable.cs index 7a07f12883..05cf7ef3e1 100644 --- a/tests/Avalonia.Styling.UnitTests/TestObservable.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/TestObservable.cs @@ -1,7 +1,7 @@ using System; using System.Reactive.Disposables; -namespace Avalonia.Styling.UnitTests +namespace Avalonia.Base.UnitTests.Styling { public class TestObservable : IObservable { diff --git a/tests/Avalonia.Styling.UnitTests/TestObserver.cs b/tests/Avalonia.Base.UnitTests/Styling/TestObserver.cs similarity index 96% rename from tests/Avalonia.Styling.UnitTests/TestObserver.cs rename to tests/Avalonia.Base.UnitTests/Styling/TestObserver.cs index c18fa8618f..46f26804f1 100644 --- a/tests/Avalonia.Styling.UnitTests/TestObserver.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/TestObserver.cs @@ -1,6 +1,6 @@ using System; -namespace Avalonia.Styling.UnitTests +namespace Avalonia.Base.UnitTests.Styling { internal class TestObserver : IObserver { diff --git a/tests/Avalonia.Styling.UnitTests/TestSelectors.cs b/tests/Avalonia.Base.UnitTests/Styling/TestSelectors.cs similarity index 90% rename from tests/Avalonia.Styling.UnitTests/TestSelectors.cs rename to tests/Avalonia.Base.UnitTests/Styling/TestSelectors.cs index 44a087af19..bd60275d50 100644 --- a/tests/Avalonia.Styling.UnitTests/TestSelectors.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/TestSelectors.cs @@ -1,7 +1,7 @@ using System; using Avalonia.Styling; -namespace Avalonia.Styling.UnitTests +namespace Avalonia.Base.UnitTests.Styling { public static class TestSelectors { diff --git a/tests/Avalonia.Styling.UnitTests/TestSubject.cs b/tests/Avalonia.Base.UnitTests/Styling/TestSubject.cs similarity index 96% rename from tests/Avalonia.Styling.UnitTests/TestSubject.cs rename to tests/Avalonia.Base.UnitTests/Styling/TestSubject.cs index 7550b545b6..c162f24d59 100644 --- a/tests/Avalonia.Styling.UnitTests/TestSubject.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/TestSubject.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using System.Reactive.Disposables; -namespace Avalonia.Styling.UnitTests +namespace Avalonia.Base.UnitTests.Styling { internal class TestSubject : IObserver, IObservable { diff --git a/tests/Avalonia.Visuals.UnitTests/TestVisual.cs b/tests/Avalonia.Base.UnitTests/TestVisual.cs similarity index 94% rename from tests/Avalonia.Visuals.UnitTests/TestVisual.cs rename to tests/Avalonia.Base.UnitTests/TestVisual.cs index 29d61ddefc..fc9b9e22fb 100644 --- a/tests/Avalonia.Visuals.UnitTests/TestVisual.cs +++ b/tests/Avalonia.Base.UnitTests/TestVisual.cs @@ -1,10 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; -using Avalonia.Rendering; using Avalonia.VisualTree; -namespace Avalonia.Visuals.UnitTests +namespace Avalonia.Base.UnitTests { public class ParamEventArgs : EventArgs { diff --git a/tests/Avalonia.Visuals.UnitTests/ThicknessTests.cs b/tests/Avalonia.Base.UnitTests/ThicknessTests.cs similarity index 93% rename from tests/Avalonia.Visuals.UnitTests/ThicknessTests.cs rename to tests/Avalonia.Base.UnitTests/ThicknessTests.cs index e85a9d5b85..094acada26 100644 --- a/tests/Avalonia.Visuals.UnitTests/ThicknessTests.cs +++ b/tests/Avalonia.Base.UnitTests/ThicknessTests.cs @@ -1,7 +1,6 @@ -using System.Globalization; using Xunit; -namespace Avalonia.Visuals.UnitTests +namespace Avalonia.Base.UnitTests { public class ThicknessTests { @@ -37,4 +36,4 @@ namespace Avalonia.Visuals.UnitTests Assert.Equal(new Thickness(1.2, 3.4, 5, 6), result); } } -} \ No newline at end of file +} diff --git a/tests/Avalonia.Visuals.UnitTests/Utilities/ReadOnlySpanTests.cs b/tests/Avalonia.Base.UnitTests/Utilities/ReadOnlySpanTests.cs similarity index 94% rename from tests/Avalonia.Visuals.UnitTests/Utilities/ReadOnlySpanTests.cs rename to tests/Avalonia.Base.UnitTests/Utilities/ReadOnlySpanTests.cs index 1afd84e546..da30ee9d02 100644 --- a/tests/Avalonia.Visuals.UnitTests/Utilities/ReadOnlySpanTests.cs +++ b/tests/Avalonia.Base.UnitTests/Utilities/ReadOnlySpanTests.cs @@ -2,7 +2,7 @@ using Avalonia.Utilities; using Xunit; -namespace Avalonia.Visuals.UnitTests.Utilities +namespace Avalonia.Base.UnitTests.Utilities { public class ReadOnlySpanTests { diff --git a/tests/Avalonia.Visuals.UnitTests/VectorTests.cs b/tests/Avalonia.Base.UnitTests/VectorTests.cs similarity index 98% rename from tests/Avalonia.Visuals.UnitTests/VectorTests.cs rename to tests/Avalonia.Base.UnitTests/VectorTests.cs index f9a9e59436..154b4a8e6c 100644 --- a/tests/Avalonia.Visuals.UnitTests/VectorTests.cs +++ b/tests/Avalonia.Base.UnitTests/VectorTests.cs @@ -1,8 +1,7 @@ -using Xunit; -using Avalonia; using System; +using Xunit; -namespace Avalonia.Visuals.UnitTests +namespace Avalonia.Base.UnitTests { public class VectorTests { diff --git a/tests/Avalonia.Visuals.UnitTests/VisualExtensionsTests.cs b/tests/Avalonia.Base.UnitTests/VisualExtensionsTests.cs similarity index 99% rename from tests/Avalonia.Visuals.UnitTests/VisualExtensionsTests.cs rename to tests/Avalonia.Base.UnitTests/VisualExtensionsTests.cs index 781169cfa6..4863c0fa61 100644 --- a/tests/Avalonia.Visuals.UnitTests/VisualExtensionsTests.cs +++ b/tests/Avalonia.Base.UnitTests/VisualExtensionsTests.cs @@ -5,7 +5,7 @@ using Avalonia.UnitTests; using Avalonia.VisualTree; using Xunit; -namespace Avalonia.Visuals.UnitTests +namespace Avalonia.Base.UnitTests { public class VisualExtensionsTests { diff --git a/tests/Avalonia.Visuals.UnitTests/VisualTests.cs b/tests/Avalonia.Base.UnitTests/VisualTests.cs similarity index 98% rename from tests/Avalonia.Visuals.UnitTests/VisualTests.cs rename to tests/Avalonia.Base.UnitTests/VisualTests.cs index 38131fbfca..382001229b 100644 --- a/tests/Avalonia.Visuals.UnitTests/VisualTests.cs +++ b/tests/Avalonia.Base.UnitTests/VisualTests.cs @@ -10,7 +10,7 @@ using Avalonia.VisualTree; using Moq; using Xunit; -namespace Avalonia.Visuals.UnitTests +namespace Avalonia.Base.UnitTests { public class VisualTests { @@ -262,7 +262,7 @@ namespace Avalonia.Visuals.UnitTests LogCallback checkLogMessage = (level, area, src, mt, pv) => { - if (level >= Logging.LogEventLevel.Warning) + if (level >= Avalonia.Logging.LogEventLevel.Warning) { called = true; } @@ -285,7 +285,7 @@ namespace Avalonia.Visuals.UnitTests LogCallback checkLogMessage = (level, area, src, mt, pv) => { - if (level >= Logging.LogEventLevel.Warning) + if (level >= Avalonia.Logging.LogEventLevel.Warning) { called = true; } diff --git a/tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs b/tests/Avalonia.Base.UnitTests/VisualTree/MockRenderInterface.cs similarity index 98% rename from tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs rename to tests/Avalonia.Base.UnitTests/VisualTree/MockRenderInterface.cs index 6100f2cb74..add8f7fd73 100644 --- a/tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs +++ b/tests/Avalonia.Base.UnitTests/VisualTree/MockRenderInterface.cs @@ -4,9 +4,9 @@ using System.IO; using Avalonia.Media; using Avalonia.Platform; using Avalonia.UnitTests; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; -namespace Avalonia.Visuals.UnitTests.VisualTree +namespace Avalonia.Base.UnitTests.VisualTree { class MockRenderInterface : IPlatformRenderInterface { diff --git a/tests/Avalonia.Visuals.UnitTests/VisualTree/TransformedBoundsTests.cs b/tests/Avalonia.Base.UnitTests/VisualTree/TransformedBoundsTests.cs similarity index 94% rename from tests/Avalonia.Visuals.UnitTests/VisualTree/TransformedBoundsTests.cs rename to tests/Avalonia.Base.UnitTests/VisualTree/TransformedBoundsTests.cs index 54d9ce40f0..d8158fd673 100644 --- a/tests/Avalonia.Visuals.UnitTests/VisualTree/TransformedBoundsTests.cs +++ b/tests/Avalonia.Base.UnitTests/VisualTree/TransformedBoundsTests.cs @@ -1,18 +1,16 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Reactive.Linq; using Avalonia.Controls; using Avalonia.Controls.Shapes; -using Avalonia.VisualTree; -using Avalonia.Rendering; -using Xunit; using Avalonia.Media; -using Moq; -using Avalonia.UnitTests; using Avalonia.Platform; +using Avalonia.Rendering; +using Avalonia.UnitTests; +using Avalonia.VisualTree; +using Moq; +using Xunit; -namespace Avalonia.Visuals.UnitTests.VisualTree +namespace Avalonia.Base.UnitTests.VisualTree { public class TransformedBoundsTests { diff --git a/tests/Avalonia.Visuals.UnitTests/VisualTree/VisualExtensions_GetVisualsAt.cs b/tests/Avalonia.Base.UnitTests/VisualTree/VisualExtensions_GetVisualsAt.cs similarity index 98% rename from tests/Avalonia.Visuals.UnitTests/VisualTree/VisualExtensions_GetVisualsAt.cs rename to tests/Avalonia.Base.UnitTests/VisualTree/VisualExtensions_GetVisualsAt.cs index 139a7925b1..1020a3a4d6 100644 --- a/tests/Avalonia.Visuals.UnitTests/VisualTree/VisualExtensions_GetVisualsAt.cs +++ b/tests/Avalonia.Base.UnitTests/VisualTree/VisualExtensions_GetVisualsAt.cs @@ -8,7 +8,7 @@ using Avalonia.UnitTests; using Avalonia.VisualTree; using Xunit; -namespace Avalonia.Visuals.UnitTests.VisualTree +namespace Avalonia.Base.UnitTests.VisualTree { public class VisualExtensions_GetVisualsAt { diff --git a/tests/Avalonia.Benchmarks/Avalonia.Benchmarks.csproj b/tests/Avalonia.Benchmarks/Avalonia.Benchmarks.csproj index 2dbb054221..5d17808e0c 100644 --- a/tests/Avalonia.Benchmarks/Avalonia.Benchmarks.csproj +++ b/tests/Avalonia.Benchmarks/Avalonia.Benchmarks.csproj @@ -6,15 +6,9 @@ false - - - - - - diff --git a/tests/Avalonia.Benchmarks/NullDrawingContextImpl.cs b/tests/Avalonia.Benchmarks/NullDrawingContextImpl.cs index 59067e642f..6fb2a5ac24 100644 --- a/tests/Avalonia.Benchmarks/NullDrawingContextImpl.cs +++ b/tests/Avalonia.Benchmarks/NullDrawingContextImpl.cs @@ -2,7 +2,7 @@ using Avalonia.Platform; using Avalonia.Rendering.SceneGraph; using Avalonia.Utilities; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; namespace Avalonia.Benchmarks { diff --git a/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs b/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs index ccdb0eed2f..5cbb3b2c49 100644 --- a/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs +++ b/tests/Avalonia.Benchmarks/NullRenderingPlatform.cs @@ -4,7 +4,7 @@ using System.IO; using Avalonia.Media; using Avalonia.Platform; using Avalonia.UnitTests; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; namespace Avalonia.Benchmarks { diff --git a/tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj b/tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj index 4aed8c60c3..12eb290fde 100644 --- a/tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj +++ b/tests/Avalonia.Controls.UnitTests/Avalonia.Controls.UnitTests.csproj @@ -18,14 +18,8 @@ - - - - - - diff --git a/tests/Avalonia.DesignerSupport.TestApp/Avalonia.DesignerSupport.TestApp.csproj b/tests/Avalonia.DesignerSupport.TestApp/Avalonia.DesignerSupport.TestApp.csproj index f4c6434a68..f66b2b0457 100644 --- a/tests/Avalonia.DesignerSupport.TestApp/Avalonia.DesignerSupport.TestApp.csproj +++ b/tests/Avalonia.DesignerSupport.TestApp/Avalonia.DesignerSupport.TestApp.csproj @@ -18,15 +18,9 @@ - - - - - - diff --git a/tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj b/tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj index cdadc3dcba..e7f1552370 100644 --- a/tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj +++ b/tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj @@ -11,14 +11,8 @@ - - - - - - diff --git a/tests/Avalonia.Direct2D1.UnitTests/Avalonia.Direct2D1.UnitTests.csproj b/tests/Avalonia.Direct2D1.UnitTests/Avalonia.Direct2D1.UnitTests.csproj index 636643d9e4..ad7e3ad504 100644 --- a/tests/Avalonia.Direct2D1.UnitTests/Avalonia.Direct2D1.UnitTests.csproj +++ b/tests/Avalonia.Direct2D1.UnitTests/Avalonia.Direct2D1.UnitTests.csproj @@ -12,14 +12,8 @@ - - - - - - diff --git a/tests/Avalonia.Input.UnitTests/Avalonia.Input.UnitTests.csproj b/tests/Avalonia.Input.UnitTests/Avalonia.Input.UnitTests.csproj deleted file mode 100644 index 3b0b082093..0000000000 --- a/tests/Avalonia.Input.UnitTests/Avalonia.Input.UnitTests.csproj +++ /dev/null @@ -1,27 +0,0 @@ - - - net6.0 - Library - true - - - - - - - - - - - - - - - - - - - - - - diff --git a/tests/Avalonia.Input.UnitTests/Properties/AssemblyInfo.cs b/tests/Avalonia.Input.UnitTests/Properties/AssemblyInfo.cs deleted file mode 100644 index 3974559efc..0000000000 --- a/tests/Avalonia.Input.UnitTests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,7 +0,0 @@ -using System.Reflection; -using Xunit; - -[assembly: AssemblyTitle("Avalonia.Input.UnitTests")] - -// Don't run tests in parallel. -[assembly: CollectionBehavior(DisableTestParallelization = true)] \ No newline at end of file diff --git a/tests/Avalonia.Interactivity.UnitTests/Avalonia.Interactivity.UnitTests.csproj b/tests/Avalonia.Interactivity.UnitTests/Avalonia.Interactivity.UnitTests.csproj deleted file mode 100644 index f6cfe51db4..0000000000 --- a/tests/Avalonia.Interactivity.UnitTests/Avalonia.Interactivity.UnitTests.csproj +++ /dev/null @@ -1,27 +0,0 @@ - - - net6.0 - Library - true - latest - - - - - - - - - - - - - - - - - - - - - diff --git a/tests/Avalonia.Interactivity.UnitTests/Properties/AssemblyInfo.cs b/tests/Avalonia.Interactivity.UnitTests/Properties/AssemblyInfo.cs deleted file mode 100644 index c5bf2d8657..0000000000 --- a/tests/Avalonia.Interactivity.UnitTests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,7 +0,0 @@ -using System.Reflection; -using Xunit; - -[assembly: AssemblyTitle("Avalonia.Interactivity.UnitTests")] - -// Don't run tests in parallel. -[assembly: CollectionBehavior(DisableTestParallelization = true)] \ No newline at end of file diff --git a/tests/Avalonia.Layout.UnitTests/Avalonia.Layout.UnitTests.csproj b/tests/Avalonia.Layout.UnitTests/Avalonia.Layout.UnitTests.csproj deleted file mode 100644 index a3c85c72b5..0000000000 --- a/tests/Avalonia.Layout.UnitTests/Avalonia.Layout.UnitTests.csproj +++ /dev/null @@ -1,29 +0,0 @@ - - - net6.0 - Library - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tests/Avalonia.Layout.UnitTests/Properties/AssemblyInfo.cs b/tests/Avalonia.Layout.UnitTests/Properties/AssemblyInfo.cs deleted file mode 100644 index 6f62e38bda..0000000000 --- a/tests/Avalonia.Layout.UnitTests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,7 +0,0 @@ -using System.Reflection; -using Xunit; - -[assembly: AssemblyTitle("Avalonia.Layout.UnitTests")] - -// Don't run tests in parallel. -[assembly: CollectionBehavior(DisableTestParallelization = true)] \ No newline at end of file diff --git a/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj b/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj index 7e569f2eac..0c663e1a8f 100644 --- a/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj +++ b/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj @@ -12,14 +12,8 @@ - - - - - - diff --git a/tests/Avalonia.Markup.UnitTests/Avalonia.Markup.UnitTests.csproj b/tests/Avalonia.Markup.UnitTests/Avalonia.Markup.UnitTests.csproj index fceea56273..fc15ad8230 100644 --- a/tests/Avalonia.Markup.UnitTests/Avalonia.Markup.UnitTests.csproj +++ b/tests/Avalonia.Markup.UnitTests/Avalonia.Markup.UnitTests.csproj @@ -13,14 +13,8 @@ - - - - - - diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj b/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj index 14f6525dbe..a0efa7bdeb 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj @@ -16,14 +16,8 @@ - - - - - - diff --git a/tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj b/tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj index 5189e9335a..ffbcdd9e7c 100644 --- a/tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj +++ b/tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj @@ -12,14 +12,8 @@ - - - - - - diff --git a/tests/Avalonia.Skia.UnitTests/Avalonia.Skia.UnitTests.csproj b/tests/Avalonia.Skia.UnitTests/Avalonia.Skia.UnitTests.csproj index e37cd25009..ccde66a50e 100644 --- a/tests/Avalonia.Skia.UnitTests/Avalonia.Skia.UnitTests.csproj +++ b/tests/Avalonia.Skia.UnitTests/Avalonia.Skia.UnitTests.csproj @@ -13,14 +13,8 @@ - - - - - - diff --git a/tests/Avalonia.Styling.UnitTests/Avalonia.Styling.UnitTests.csproj b/tests/Avalonia.Styling.UnitTests/Avalonia.Styling.UnitTests.csproj deleted file mode 100644 index c5eb636a9a..0000000000 --- a/tests/Avalonia.Styling.UnitTests/Avalonia.Styling.UnitTests.csproj +++ /dev/null @@ -1,30 +0,0 @@ - - - net6.0 - Library - CS0067 - true - latest - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tests/Avalonia.Styling.UnitTests/Properties/AssemblyInfo.cs b/tests/Avalonia.Styling.UnitTests/Properties/AssemblyInfo.cs deleted file mode 100644 index 3bdb8a2f2e..0000000000 --- a/tests/Avalonia.Styling.UnitTests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,7 +0,0 @@ -using System.Reflection; -using Xunit; - -[assembly: AssemblyTitle("Avalonia.Styling.UnitTests")] - -// Don't run tests in parallel. -[assembly: CollectionBehavior(DisableTestParallelization = true)] \ No newline at end of file diff --git a/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj b/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj index c3cf6cd8d3..f54ccaa857 100644 --- a/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj +++ b/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj @@ -17,14 +17,8 @@ - - - - - - diff --git a/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs b/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs index 2858a9f228..376121c269 100644 --- a/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs +++ b/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.IO; using Avalonia.Media; using Avalonia.Platform; -using Avalonia.Visuals.Media.Imaging; +using Avalonia.Media.Imaging; using Moq; namespace Avalonia.UnitTests diff --git a/tests/Avalonia.Visuals.UnitTests/Avalonia.Visuals.UnitTests.csproj b/tests/Avalonia.Visuals.UnitTests/Avalonia.Visuals.UnitTests.csproj deleted file mode 100644 index 57de90114e..0000000000 --- a/tests/Avalonia.Visuals.UnitTests/Avalonia.Visuals.UnitTests.csproj +++ /dev/null @@ -1,40 +0,0 @@ - - - net6.0 - Library - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tests/Avalonia.Visuals.UnitTests/Properties/AssemblyInfo.cs b/tests/Avalonia.Visuals.UnitTests/Properties/AssemblyInfo.cs deleted file mode 100644 index 2251cc6e92..0000000000 --- a/tests/Avalonia.Visuals.UnitTests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,7 +0,0 @@ -using System.Reflection; -using Xunit; - -[assembly: AssemblyTitle("Avalonia.Visuals.UnitTests")] - -// Don't run tests in parallel. -[assembly: CollectionBehavior(DisableTestParallelization = true)] \ No newline at end of file From a2bcd8a9c95b2fc854939b8ce1ef8f854dfc0e38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Komosi=C5=84ski?= Date: Thu, 14 Apr 2022 14:39:56 +0200 Subject: [PATCH 126/416] Remove dependency on fluent theme. --- .../Styling/ResourceBenchmarks.cs | 17 ++++++----- tests/Avalonia.Benchmarks/TestStyles.cs | 29 +++++++++++++++++++ 2 files changed, 38 insertions(+), 8 deletions(-) create mode 100644 tests/Avalonia.Benchmarks/TestStyles.cs diff --git a/tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs b/tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs index babfaef6ab..2a048beefa 100644 --- a/tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs +++ b/tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs @@ -3,7 +3,6 @@ using Avalonia.Controls; using Avalonia.Platform; using Avalonia.PlatformSupport; using Avalonia.Styling; -using Avalonia.Themes.Fluent; using Avalonia.UnitTests; using BenchmarkDotNet.Attributes; using Moq; @@ -25,7 +24,7 @@ namespace Avalonia.Benchmarks.Styling renderInterface: new MockPlatformRenderInterface(), standardCursorFactory: Mock.Of(), styler: new Styler(), - theme: () => LoadTheme(), + theme: () => CreateTheme(), threadingInterface: new NullThreadingPlatform(), fontManagerImpl: new MockFontManagerImpl(), textShaperImpl: new MockTextShaperImpl(), @@ -34,7 +33,7 @@ namespace Avalonia.Benchmarks.Styling return UnitTestApplication.Start(services); } - private static Styles LoadTheme() + private static Styles CreateTheme() { AssetLoader.RegisterResUriParsers(); @@ -43,11 +42,11 @@ namespace Avalonia.Benchmarks.Styling var postHost = new Style(); postHost.Resources.Add("postTheme", null); - + return new Styles { preHost, - new FluentTheme(new Uri("avares://Avalonia.Benchmarks")), + new TestStyles(50, 3, 5), postHost }; } @@ -81,11 +80,13 @@ namespace Avalonia.Benchmarks.Styling current.Child = _searchStart; } + + private const int LookupCount = 100; [Benchmark] public void FindPreResource() { - for (int i = 0; i < 100; ++i) + for (int i = 0; i < LookupCount; ++i) { _searchStart.FindResource("preTheme"); } @@ -94,7 +95,7 @@ namespace Avalonia.Benchmarks.Styling [Benchmark] public void FindPostResource() { - for (int i = 0; i < 100; ++i) + for (int i = 0; i < LookupCount; ++i) { _searchStart.FindResource("postTheme"); } @@ -103,7 +104,7 @@ namespace Avalonia.Benchmarks.Styling [Benchmark] public void FindNotExistingResource() { - for (int i = 0; i < 100; ++i) + for (int i = 0; i < LookupCount; ++i) { _searchStart.FindResource("notPresent"); } diff --git a/tests/Avalonia.Benchmarks/TestStyles.cs b/tests/Avalonia.Benchmarks/TestStyles.cs new file mode 100644 index 0000000000..be2ad7d072 --- /dev/null +++ b/tests/Avalonia.Benchmarks/TestStyles.cs @@ -0,0 +1,29 @@ +using Avalonia.Styling; + +namespace Avalonia.Benchmarks +{ + public class TestStyles : Styles + { + public TestStyles(int childStylesCount, int childInnerStyleCount, int childResourceCount) + { + for (int i = 0; i < childStylesCount; i++) + { + var childStyles = new Styles(); + + for (int j = 0; j < childInnerStyleCount; j++) + { + var childStyle = new Style(); + + for (int k = 0; k < childResourceCount; k++) + { + childStyle.Resources.Add($"resource.{i}.{j}.{k}", null); + } + + childStyles.Add(childStyle); + } + + Add(childStyles); + } + } + } +} From 0ec26bcdc3e9b9f841993242ca662f2ea194aa4e Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 14 Apr 2022 11:40:38 -0400 Subject: [PATCH 127/416] Make empty ChildIndexChangedEventArgs ctor private --- src/Avalonia.Base/LogicalTree/ChildIndexChangedEventArgs.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Base/LogicalTree/ChildIndexChangedEventArgs.cs b/src/Avalonia.Base/LogicalTree/ChildIndexChangedEventArgs.cs index afc6c1f5fc..497596fcc1 100644 --- a/src/Avalonia.Base/LogicalTree/ChildIndexChangedEventArgs.cs +++ b/src/Avalonia.Base/LogicalTree/ChildIndexChangedEventArgs.cs @@ -10,7 +10,7 @@ namespace Avalonia.LogicalTree { public static new ChildIndexChangedEventArgs Empty { get; } = new ChildIndexChangedEventArgs(); - public ChildIndexChangedEventArgs() + private ChildIndexChangedEventArgs() { } From 9bef2acafe18551bd0d717c316864677e6400814 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 14 Apr 2022 11:47:25 -0400 Subject: [PATCH 128/416] Move pointerover logic out of input device layer --- src/Avalonia.Controls/TopLevel.cs | 25 +- src/Avalonia.Input/IInputRoot.cs | 3 - src/Avalonia.Input/IKeyboardDevice.cs | 6 - src/Avalonia.Input/IMouseDevice.cs | 2 + src/Avalonia.Input/IPointerDevice.cs | 16 +- src/Avalonia.Input/KeyboardDevice.cs | 2 +- src/Avalonia.Input/MouseDevice.cs | 328 +++--------------- src/Avalonia.Input/PointerEventArgs.cs | 2 + src/Avalonia.Input/PointerOverPreProcessor.cs | 210 +++++++++++ src/Avalonia.Input/Raw/RawDragEvent.cs | 2 +- src/Avalonia.Input/Raw/RawInputHelpers.cs | 27 ++ src/Avalonia.Input/Raw/RawPointerEventArgs.cs | 2 + src/Avalonia.Input/TouchDevice.cs | 47 ++- 13 files changed, 358 insertions(+), 314 deletions(-) create mode 100644 src/Avalonia.Input/PointerOverPreProcessor.cs create mode 100644 src/Avalonia.Input/Raw/RawInputHelpers.cs diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index 55202dd20d..75a34659a2 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -15,7 +15,6 @@ using Avalonia.Rendering; using Avalonia.Styling; using Avalonia.Utilities; using Avalonia.VisualTree; -using JetBrains.Annotations; namespace Avalonia.Controls { @@ -87,6 +86,8 @@ namespace Avalonia.Controls private readonly IKeyboardNavigationHandler? _keyboardNavigationHandler; private readonly IPlatformRenderInterface? _renderInterface; private readonly IGlobalStyles? _globalStyles; + private readonly PointerOverPreProcessor? _pointerOverPreProcessor; + private readonly IDisposable? _pointerOverPreProcessorSubscription; private Size _clientSize; private Size? _frameSize; private WindowTransparencyLevel _actualTransparencyLevel; @@ -195,6 +196,9 @@ namespace Avalonia.Controls } impl.LostFocus += PlatformImpl_LostFocus; + + _pointerOverPreProcessor = new PointerOverPreProcessor(this); + _pointerOverPreProcessorSubscription = _inputManager?.PreProcess.Subscribe(_pointerOverPreProcessor); } /// @@ -283,9 +287,7 @@ namespace Avalonia.Controls /// IKeyboardNavigationHandler IInputRoot.KeyboardNavigationHandler => _keyboardNavigationHandler!; - /// - /// Gets or sets the input element that the pointer is currently over. - /// + /// IInputElement? IInputRoot.PointerOverElement { get { return GetValue(PointerOverElementProperty); } @@ -378,10 +380,12 @@ namespace Avalonia.Controls Renderer?.Dispose(); Renderer = null!; - - (this as IInputRoot).MouseDevice?.TopLevelClosed(this); + + _pointerOverPreProcessor?.OnCompleted(); + _pointerOverPreProcessorSubscription?.Dispose(); + PlatformImpl = null; - + var logicalArgs = new LogicalTreeAttachmentEventArgs(this, this, null); ((ILogical)this).NotifyDetachedFromLogicalTree(logicalArgs); @@ -515,12 +519,17 @@ namespace Avalonia.Controls /// The event args. private void HandleInput(RawInputEventArgs e) { + if (e is RawPointerEventArgs pointerArgs) + { + pointerArgs.InputHitTestResult = this.InputHitTest(pointerArgs.Position); + } + _inputManager?.ProcessInput(e); } private void SceneInvalidated(object? sender, SceneInvalidatedEventArgs e) { - (this as IInputRoot).MouseDevice?.SceneInvalidated(this, e.DirtyRect); + _pointerOverPreProcessor?.SceneInvalidated(e.DirtyRect); } void PlatformImpl_LostFocus() diff --git a/src/Avalonia.Input/IInputRoot.cs b/src/Avalonia.Input/IInputRoot.cs index 3e2b8cc477..98e8699573 100644 --- a/src/Avalonia.Input/IInputRoot.cs +++ b/src/Avalonia.Input/IInputRoot.cs @@ -1,5 +1,3 @@ -using JetBrains.Annotations; - namespace Avalonia.Input { /// @@ -30,7 +28,6 @@ namespace Avalonia.Input /// /// Gets associated mouse device /// - [CanBeNull] IMouseDevice? MouseDevice { get; } } } diff --git a/src/Avalonia.Input/IKeyboardDevice.cs b/src/Avalonia.Input/IKeyboardDevice.cs index 9506dc36fb..d0e84e5ad0 100644 --- a/src/Avalonia.Input/IKeyboardDevice.cs +++ b/src/Avalonia.Input/IKeyboardDevice.cs @@ -50,12 +50,6 @@ namespace Avalonia.Input KeyboardMask = Alt | Control | Shift | Meta } - internal static class KeyModifiersUtils - { - public static KeyModifiers ConvertToKey(RawInputModifiers modifiers) => - (KeyModifiers)(modifiers & RawInputModifiers.KeyboardMask); - } - public interface IKeyboardDevice : IInputDevice, INotifyPropertyChanged { IInputElement? FocusedElement { get; } diff --git a/src/Avalonia.Input/IMouseDevice.cs b/src/Avalonia.Input/IMouseDevice.cs index 272d1eb8d7..6b7f0e76e5 100644 --- a/src/Avalonia.Input/IMouseDevice.cs +++ b/src/Avalonia.Input/IMouseDevice.cs @@ -13,8 +13,10 @@ namespace Avalonia.Input [Obsolete("Use PointerEventArgs.GetPosition")] PixelPoint Position { get; } + [Obsolete] void TopLevelClosed(IInputRoot root); + [Obsolete] void SceneInvalidated(IInputRoot root, Rect rect); } } diff --git a/src/Avalonia.Input/IPointerDevice.cs b/src/Avalonia.Input/IPointerDevice.cs index 1f82cb1ed7..0096bb77bf 100644 --- a/src/Avalonia.Input/IPointerDevice.cs +++ b/src/Avalonia.Input/IPointerDevice.cs @@ -1,17 +1,31 @@ using System; using Avalonia.VisualTree; +using Avalonia.Input.Raw; namespace Avalonia.Input { public interface IPointerDevice : IInputDevice { + /// [Obsolete("Use IPointer")] IInputElement? Captured { get; } - + + /// [Obsolete("Use IPointer")] void Capture(IInputElement? control); + /// [Obsolete("Use PointerEventArgs.GetPosition")] Point GetPosition(IVisual relativeTo); + + /// + /// Gets a pointer for specific event args. + /// + /// + /// If pointer doesn't exist or wasn't yet created this method will return null. + /// + /// Raw pointer event args associated with the pointer. + /// The pointer. + IPointer? TryGetPointer(RawPointerEventArgs ev); } } diff --git a/src/Avalonia.Input/KeyboardDevice.cs b/src/Avalonia.Input/KeyboardDevice.cs index 3df717b8c4..0600b54618 100644 --- a/src/Avalonia.Input/KeyboardDevice.cs +++ b/src/Avalonia.Input/KeyboardDevice.cs @@ -188,7 +188,7 @@ namespace Avalonia.Input RoutedEvent = routedEvent, Device = this, Key = keyInput.Key, - KeyModifiers = KeyModifiersUtils.ConvertToKey(keyInput.Modifiers), + KeyModifiers = keyInput.Modifiers.ToKeyModifiers(), Source = element, }; diff --git a/src/Avalonia.Input/MouseDevice.cs b/src/Avalonia.Input/MouseDevice.cs index a5d54bb047..5f8ab24b79 100644 --- a/src/Avalonia.Input/MouseDevice.cs +++ b/src/Avalonia.Input/MouseDevice.cs @@ -21,27 +21,17 @@ namespace Avalonia.Input private readonly Pointer _pointer; private bool _disposed; - private PixelPoint? _position; + private PixelPoint? _position; + private MouseButton _lastMouseDownButton; public MouseDevice(Pointer? pointer = null) { _pointer = pointer ?? new Pointer(Pointer.GetNextFreeId(), PointerType.Mouse, true); } - - /// - /// Gets the control that is currently capturing by the mouse, if any. - /// - /// - /// When an element captures the mouse, it receives mouse input whether the cursor is - /// within the control's bounds or not. To set the mouse capture, call the - /// method. - /// + [Obsolete("Use IPointer instead")] public IInputElement? Captured => _pointer.Captured; - /// - /// Gets the mouse position, in screen coordinates. - /// [Obsolete("Use events instead")] public PixelPoint Position { @@ -49,15 +39,7 @@ namespace Avalonia.Input protected set => _position = value; } - /// - /// Captures mouse input to the specified control. - /// - /// The control. - /// - /// When an element captures the mouse, it receives mouse input whether the cursor is - /// within the control's bounds or not. The current mouse capture control is exposed - /// by the property. - /// + [Obsolete("Use IPointer instead")] public void Capture(IInputElement? control) { _pointer.Capture(control); @@ -90,39 +72,6 @@ namespace Avalonia.Input ProcessRawEvent(margs); } - public void TopLevelClosed(IInputRoot root) - { - ClearPointerOver(this, 0, root, PointerPointProperties.None, KeyModifiers.None); - } - - public void SceneInvalidated(IInputRoot root, Rect rect) - { - // Pointer is outside of the target area - if (_position == null ) - { - if (root.PointerOverElement != null) - ClearPointerOver(this, 0, root, PointerPointProperties.None, KeyModifiers.None); - return; - } - - - var clientPoint = root.PointToClient(_position.Value); - - if (rect.Contains(clientPoint)) - { - if (_pointer.Captured == null) - { - SetPointerOver(this, 0 /* TODO: proper timestamp */, root, clientPoint, - PointerPointProperties.None, KeyModifiers.None); - } - else - { - SetPointerOver(this, 0 /* TODO: proper timestamp */, root, _pointer.Captured, - PointerPointProperties.None, KeyModifiers.None); - } - } - } - int ButtonCount(PointerPointProperties props) { var rv = 0; @@ -138,7 +87,7 @@ namespace Avalonia.Input rv++; return rv; } - + private void ProcessRawEvent(RawPointerEventArgs e) { e = e ?? throw new ArgumentNullException(nameof(e)); @@ -147,15 +96,14 @@ namespace Avalonia.Input if(mouse._disposed) return; - if (e.Type == RawPointerEventType.NonClientLeftButtonDown) return; - _position = e.Root.PointToScreen(e.Position); var props = CreateProperties(e); - var keyModifiers = KeyModifiersUtils.ConvertToKey(e.InputModifiers); + var keyModifiers = e.InputModifiers.ToKeyModifiers(); switch (e.Type) { case RawPointerEventType.LeaveWindow: - LeaveWindow(mouse, e.Timestamp, e.Root, props, keyModifiers); + case RawPointerEventType.NonClientLeftButtonDown: + LeaveWindow(); break; case RawPointerEventType.LeftButtonDown: case RawPointerEventType.RightButtonDown: @@ -163,10 +111,9 @@ namespace Avalonia.Input case RawPointerEventType.XButton1Down: case RawPointerEventType.XButton2Down: if (ButtonCount(props) > 1) - e.Handled = MouseMove(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers, e.IntermediatePoints); + e.Handled = MouseMove(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers, e.IntermediatePoints, e.InputHitTestResult); else - e.Handled = MouseDown(mouse, e.Timestamp, e.Root, e.Position, - props, keyModifiers); + e.Handled = MouseDown(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers, e.InputHitTestResult); break; case RawPointerEventType.LeftButtonUp: case RawPointerEventType.RightButtonUp: @@ -174,82 +121,50 @@ namespace Avalonia.Input case RawPointerEventType.XButton1Up: case RawPointerEventType.XButton2Up: if (ButtonCount(props) != 0) - e.Handled = MouseMove(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers, e.IntermediatePoints); + e.Handled = MouseMove(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers, e.IntermediatePoints, e.InputHitTestResult); else - e.Handled = MouseUp(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers); + e.Handled = MouseUp(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers, e.InputHitTestResult); break; case RawPointerEventType.Move: - e.Handled = MouseMove(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers, e.IntermediatePoints); + e.Handled = MouseMove(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers, e.IntermediatePoints, e.InputHitTestResult); break; case RawPointerEventType.Wheel: - e.Handled = MouseWheel(mouse, e.Timestamp, e.Root, e.Position, props, ((RawMouseWheelEventArgs)e).Delta, keyModifiers); + e.Handled = MouseWheel(mouse, e.Timestamp, e.Root, e.Position, props, ((RawMouseWheelEventArgs)e).Delta, keyModifiers, e.InputHitTestResult); break; case RawPointerEventType.Magnify: - e.Handled = GestureMagnify(mouse, e.Timestamp, e.Root, e.Position, props, ((RawPointerGestureEventArgs)e).Delta, keyModifiers); + e.Handled = GestureMagnify(mouse, e.Timestamp, e.Root, e.Position, props, ((RawPointerGestureEventArgs)e).Delta, keyModifiers, e.InputHitTestResult); break; case RawPointerEventType.Rotate: - e.Handled = GestureRotate(mouse, e.Timestamp, e.Root, e.Position, props, ((RawPointerGestureEventArgs)e).Delta, keyModifiers); + e.Handled = GestureRotate(mouse, e.Timestamp, e.Root, e.Position, props, ((RawPointerGestureEventArgs)e).Delta, keyModifiers, e.InputHitTestResult); break; case RawPointerEventType.Swipe: - e.Handled = GestureSwipe(mouse, e.Timestamp, e.Root, e.Position, props, ((RawPointerGestureEventArgs)e).Delta, keyModifiers); + e.Handled = GestureSwipe(mouse, e.Timestamp, e.Root, e.Position, props, ((RawPointerGestureEventArgs)e).Delta, keyModifiers, e.InputHitTestResult); break; } } - private void LeaveWindow(IMouseDevice device, ulong timestamp, IInputRoot root, PointerPointProperties properties, - KeyModifiers inputModifiers) + private void LeaveWindow() { - device = device ?? throw new ArgumentNullException(nameof(device)); - root = root ?? throw new ArgumentNullException(nameof(root)); - _position = null; - ClearPointerOver(this, timestamp, root, properties, inputModifiers); } - PointerPointProperties CreateProperties(RawPointerEventArgs args) { - - var kind = PointerUpdateKind.Other; - - if (args.Type == RawPointerEventType.LeftButtonDown) - kind = PointerUpdateKind.LeftButtonPressed; - if (args.Type == RawPointerEventType.MiddleButtonDown) - kind = PointerUpdateKind.MiddleButtonPressed; - if (args.Type == RawPointerEventType.RightButtonDown) - kind = PointerUpdateKind.RightButtonPressed; - if (args.Type == RawPointerEventType.XButton1Down) - kind = PointerUpdateKind.XButton1Pressed; - if (args.Type == RawPointerEventType.XButton2Down) - kind = PointerUpdateKind.XButton2Pressed; - if (args.Type == RawPointerEventType.LeftButtonUp) - kind = PointerUpdateKind.LeftButtonReleased; - if (args.Type == RawPointerEventType.MiddleButtonUp) - kind = PointerUpdateKind.MiddleButtonReleased; - if (args.Type == RawPointerEventType.RightButtonUp) - kind = PointerUpdateKind.RightButtonReleased; - if (args.Type == RawPointerEventType.XButton1Up) - kind = PointerUpdateKind.XButton1Released; - if (args.Type == RawPointerEventType.XButton2Up) - kind = PointerUpdateKind.XButton2Released; - - return new PointerPointProperties(args.InputModifiers, kind); + return new PointerPointProperties(args.InputModifiers, args.Type.ToUpdateKind()); } - private MouseButton _lastMouseDownButton; private bool MouseDown(IMouseDevice device, ulong timestamp, IInputElement root, Point p, PointerPointProperties properties, - KeyModifiers inputModifiers) + KeyModifiers inputModifiers, IInputElement? hitTest) { device = device ?? throw new ArgumentNullException(nameof(device)); root = root ?? throw new ArgumentNullException(nameof(root)); - var hit = HitTest(root, p); + var source = _pointer.Captured ?? root.InputHitTest(p); - if (hit != null) + if (source != null) { - _pointer.Capture(hit); - var source = GetSource(hit); + _pointer.Capture(source); if (source != null) { var settings = AvaloniaLocator.Current.GetService(); @@ -275,23 +190,14 @@ namespace Avalonia.Input return false; } - private bool MouseMove(IMouseDevice device, ulong timestamp, IInputRoot root, Point p, PointerPointProperties properties, - KeyModifiers inputModifiers, Lazy?>? intermediatePoints) + private bool MouseMove(IMouseDevice device, ulong timestamp, IInputRoot root, Point p, + PointerPointProperties properties, KeyModifiers inputModifiers, Lazy?>? intermediatePoints, + IInputElement? hitTest) { device = device ?? throw new ArgumentNullException(nameof(device)); root = root ?? throw new ArgumentNullException(nameof(root)); - IInputElement? source; - - if (_pointer.Captured == null) - { - source = SetPointerOver(this, timestamp, root, p, properties, inputModifiers); - } - else - { - SetPointerOver(this, timestamp, root, _pointer.Captured, properties, inputModifiers); - source = _pointer.Captured; - } + var source = _pointer.Captured ?? hitTest; if (source is object) { @@ -306,13 +212,12 @@ namespace Avalonia.Input } private bool MouseUp(IMouseDevice device, ulong timestamp, IInputRoot root, Point p, PointerPointProperties props, - KeyModifiers inputModifiers) + KeyModifiers inputModifiers, IInputElement? hitTest) { device = device ?? throw new ArgumentNullException(nameof(device)); root = root ?? throw new ArgumentNullException(nameof(root)); - var hit = HitTest(root, p); - var source = GetSource(hit); + var source = _pointer.Captured ?? hitTest; if (source is not null) { @@ -329,13 +234,12 @@ namespace Avalonia.Input private bool MouseWheel(IMouseDevice device, ulong timestamp, IInputRoot root, Point p, PointerPointProperties props, - Vector delta, KeyModifiers inputModifiers) + Vector delta, KeyModifiers inputModifiers, IInputElement? hitTest) { device = device ?? throw new ArgumentNullException(nameof(device)); root = root ?? throw new ArgumentNullException(nameof(root)); - var hit = HitTest(root, p); - var source = GetSource(hit); + var source = _pointer.Captured ?? hitTest; // KeyModifiers.Shift should scroll in horizontal direction. This does not work on every platform. // If Shift-Key is pressed and X is close to 0 we swap the Vector. @@ -356,16 +260,15 @@ namespace Avalonia.Input } private bool GestureMagnify(IMouseDevice device, ulong timestamp, IInputRoot root, Point p, - PointerPointProperties props, Vector delta, KeyModifiers inputModifiers) + PointerPointProperties props, Vector delta, KeyModifiers inputModifiers, IInputElement? hitTest) { device = device ?? throw new ArgumentNullException(nameof(device)); root = root ?? throw new ArgumentNullException(nameof(root)); - var hit = HitTest(root, p); + var source = _pointer.Captured ?? hitTest; - if (hit != null) + if (source != null) { - var source = GetSource(hit); var e = new PointerDeltaEventArgs(Gestures.PointerTouchPadGestureMagnifyEvent, source, _pointer, root, p, timestamp, props, inputModifiers, delta); @@ -377,16 +280,15 @@ namespace Avalonia.Input } private bool GestureRotate(IMouseDevice device, ulong timestamp, IInputRoot root, Point p, - PointerPointProperties props, Vector delta, KeyModifiers inputModifiers) + PointerPointProperties props, Vector delta, KeyModifiers inputModifiers, IInputElement? hitTest) { device = device ?? throw new ArgumentNullException(nameof(device)); root = root ?? throw new ArgumentNullException(nameof(root)); - var hit = HitTest(root, p); + var source = _pointer.Captured ?? hitTest; - if (hit != null) + if (source != null) { - var source = GetSource(hit); var e = new PointerDeltaEventArgs(Gestures.PointerTouchPadGestureRotateEvent, source, _pointer, root, p, timestamp, props, inputModifiers, delta); @@ -398,16 +300,15 @@ namespace Avalonia.Input } private bool GestureSwipe(IMouseDevice device, ulong timestamp, IInputRoot root, Point p, - PointerPointProperties props, Vector delta, KeyModifiers inputModifiers) + PointerPointProperties props, Vector delta, KeyModifiers inputModifiers, IInputElement? hitTest) { device = device ?? throw new ArgumentNullException(nameof(device)); root = root ?? throw new ArgumentNullException(nameof(root)); - var hit = HitTest(root, p); + var source = _pointer.Captured ?? hitTest; - if (hit != null) + if (source != null) { - var source = GetSource(hit); var e = new PointerDeltaEventArgs(Gestures.PointerTouchPadGestureSwipeEvent, source, _pointer, root, p, timestamp, props, inputModifiers, delta); @@ -418,154 +319,27 @@ namespace Avalonia.Input return false; } - private IInteractive? GetSource(IVisual? hit) - { - if (hit is null) - return null; - - return _pointer.Captured ?? - (hit as IInteractive) ?? - hit.GetSelfAndVisualAncestors().OfType().FirstOrDefault(); - } - - private IInputElement? HitTest(IInputElement root, Point p) - { - root = root ?? throw new ArgumentNullException(nameof(root)); - - return _pointer.Captured ?? root.InputHitTest(p); - } - - PointerEventArgs CreateSimpleEvent(RoutedEvent ev, ulong timestamp, IInteractive? source, - PointerPointProperties properties, - KeyModifiers inputModifiers) - { - return new PointerEventArgs(ev, source, _pointer, null, default, - timestamp, properties, inputModifiers); - } - - private void ClearPointerOver(IPointerDevice device, ulong timestamp, IInputRoot root, - PointerPointProperties properties, - KeyModifiers inputModifiers) - { - device = device ?? throw new ArgumentNullException(nameof(device)); - root = root ?? throw new ArgumentNullException(nameof(root)); - - var element = root.PointerOverElement; - var e = CreateSimpleEvent(InputElement.PointerLeaveEvent, timestamp, element, properties, inputModifiers); - - if (element!=null && !element.IsAttachedToVisualTree) - { - // element has been removed from visual tree so do top down cleanup - if (root.IsPointerOver) - ClearChildrenPointerOver(e, root,true); - } - while (element != null) - { - e.Source = element; - e.Handled = false; - element.RaiseEvent(e); - element = (IInputElement?)element.VisualParent; - } - - root.PointerOverElement = null; - } - - private void ClearChildrenPointerOver(PointerEventArgs e, IInputElement element,bool clearRoot) + public void Dispose() { - foreach (IInputElement el in element.VisualChildren) - { - if (el.IsPointerOver) - { - ClearChildrenPointerOver(e, el, true); - break; - } - } - if(clearRoot) - { - e.Source = element; - e.Handled = false; - element.RaiseEvent(e); - } + _disposed = true; + _pointer?.Dispose(); } - private IInputElement? SetPointerOver(IPointerDevice device, ulong timestamp, IInputRoot root, Point p, - PointerPointProperties properties, - KeyModifiers inputModifiers) + [Obsolete] + public void TopLevelClosed(IInputRoot root) { - device = device ?? throw new ArgumentNullException(nameof(device)); - root = root ?? throw new ArgumentNullException(nameof(root)); - - var element = root.InputHitTest(p); - - if (element != root.PointerOverElement) - { - if (element != null) - { - SetPointerOver(device, timestamp, root, element, properties, inputModifiers); - } - else - { - ClearPointerOver(device, timestamp, root, properties, inputModifiers); - } - } - - return element; + // no-op } - private void SetPointerOver(IPointerDevice device, ulong timestamp, IInputRoot root, IInputElement element, - PointerPointProperties properties, - KeyModifiers inputModifiers) + [Obsolete] + public void SceneInvalidated(IInputRoot root, Rect rect) { - device = device ?? throw new ArgumentNullException(nameof(device)); - root = root ?? throw new ArgumentNullException(nameof(root)); - element = element ?? throw new ArgumentNullException(nameof(element)); - - IInputElement? branch = null; - - IInputElement? el = element; - - while (el != null) - { - if (el.IsPointerOver) - { - branch = el; - break; - } - el = (IInputElement?)el.VisualParent; - } - - el = root.PointerOverElement; - - var e = CreateSimpleEvent(InputElement.PointerLeaveEvent, timestamp, el, properties, inputModifiers); - if (el!=null && branch!=null && !el.IsAttachedToVisualTree) - { - ClearChildrenPointerOver(e,branch,false); - } - - while (el != null && el != branch) - { - e.Source = el; - e.Handled = false; - el.RaiseEvent(e); - el = (IInputElement?)el.VisualParent; - } - - el = root.PointerOverElement = element; - e.RoutedEvent = InputElement.PointerEnterEvent; - - while (el != null && el != branch) - { - e.Source = el; - e.Handled = false; - el.RaiseEvent(e); - el = (IInputElement?)el.VisualParent; - } + // no-op } - public void Dispose() + public IPointer? TryGetPointer(RawPointerEventArgs ev) { - _disposed = true; - _pointer?.Dispose(); + return _pointer; } } } diff --git a/src/Avalonia.Input/PointerEventArgs.cs b/src/Avalonia.Input/PointerEventArgs.cs index 0604d09dc4..5495802920 100644 --- a/src/Avalonia.Input/PointerEventArgs.cs +++ b/src/Avalonia.Input/PointerEventArgs.cs @@ -63,6 +63,8 @@ namespace Avalonia.Input } public Point GetPosition(IVisual relativeTo) => _ev.GetPosition(relativeTo); + + public IPointer? TryGetPointer(RawPointerEventArgs ev) => _ev.Pointer; } public IPointer Pointer { get; } diff --git a/src/Avalonia.Input/PointerOverPreProcessor.cs b/src/Avalonia.Input/PointerOverPreProcessor.cs new file mode 100644 index 0000000000..a38364197d --- /dev/null +++ b/src/Avalonia.Input/PointerOverPreProcessor.cs @@ -0,0 +1,210 @@ +using System; +using Avalonia.Input.Raw; + +namespace Avalonia.Input +{ + internal class PointerOverPreProcessor : IObserver + { + private IPointerDevice? _lastActivePointerDevice; + private (IPointer pointer, PixelPoint position)? _lastPointer; + + private readonly IInputRoot _inputRoot; + + public PointerOverPreProcessor(IInputRoot inputRoot) + { + _inputRoot = inputRoot ?? throw new ArgumentNullException(nameof(inputRoot)); + } + + public void OnCompleted() + { + ClearPointerOver(); + } + + public void OnError(Exception error) + { + } + + public void OnNext(RawInputEventArgs value) + { + if (value is RawPointerEventArgs args + && args.Root == _inputRoot + && value.Device is IPointerDevice pointerDevice) + { + if (pointerDevice != _lastActivePointerDevice) + { + ClearPointerOver(); + + // Set last active device before processing input, because ClearPointerOver might be called and clear last device. + _lastActivePointerDevice = pointerDevice; + } + + if (args.Type is RawPointerEventType.LeaveWindow or RawPointerEventType.NonClientLeftButtonDown + && _lastPointer is (var lastPointer, var lastPosition)) + { + _lastPointer = null; + ClearPointerOver(lastPointer, args.Root, 0, args.Root.PointToClient(lastPosition), + new PointerPointProperties(args.InputModifiers, args.Type.ToUpdateKind()), + args.InputModifiers.ToKeyModifiers()); + } + else if (pointerDevice.TryGetPointer(args) is IPointer pointer + && pointer.Type != PointerType.Touch) + { + var element = pointer.Captured ?? args.InputHitTestResult; + + SetPointerOver(pointer, args.Root, element, args.Timestamp, args.Position, + new PointerPointProperties(args.InputModifiers, args.Type.ToUpdateKind()), + args.InputModifiers.ToKeyModifiers()); + } + } + } + + public void SceneInvalidated(Rect dirtyRect) + { + // Pointer is outside of the target area + if (_lastPointer is (var pointer, var position)) + { + var clientPoint = _inputRoot.PointToClient(position); + + if (dirtyRect.Contains(clientPoint)) + { + SetPointerOver(pointer, _inputRoot, _inputRoot.InputHitTest(clientPoint), 0, clientPoint, PointerPointProperties.None, KeyModifiers.None); + } + else if (!_inputRoot.Bounds.Contains(clientPoint)) + { + ClearPointerOver(pointer, _inputRoot, 0, new Point(-1, -1), PointerPointProperties.None, KeyModifiers.None); + } + } + } + + private void ClearPointerOver() + { + if (_lastPointer is (var pointer, var _)) + { + ClearPointerOver(pointer, _inputRoot, 0, new Point(-1, -1), PointerPointProperties.None, KeyModifiers.None); + } + _lastPointer = null; + _lastActivePointerDevice = null; + } + + private void ClearPointerOver(IPointer pointer, IInputRoot root, + ulong timestamp, Point position, PointerPointProperties properties, KeyModifiers inputModifiers) + { + var element = root.PointerOverElement; + if (element is null) + { + return; + } + + // Do not pass rootVisual, when we have unknown (negative) position, + // so GetPosition won't return invalid values. + var hasPosition = position.X >= 0 && position.Y >= 0; + var e = new PointerEventArgs(InputElement.PointerLeaveEvent, element, pointer, + hasPosition ? root : null, hasPosition ? position : default, + timestamp, properties, inputModifiers); + + if (element != null && !element.IsAttachedToVisualTree) + { + // element has been removed from visual tree so do top down cleanup + if (root.IsPointerOver) + { + ClearChildrenPointerOver(e, root, true); + } + } + while (element != null) + { + e.Source = element; + e.Handled = false; + element.RaiseEvent(e); + element = (IInputElement?)element.VisualParent; + } + + root.PointerOverElement = null; + _lastActivePointerDevice = null; + _lastPointer = null; + } + + private void ClearChildrenPointerOver(PointerEventArgs e, IInputElement element, bool clearRoot) + { + foreach (IInputElement el in element.VisualChildren) + { + if (el.IsPointerOver) + { + ClearChildrenPointerOver(e, el, true); + break; + } + } + if (clearRoot) + { + e.Source = element; + e.Handled = false; + element.RaiseEvent(e); + } + } + + private void SetPointerOver(IPointer pointer, IInputRoot root, IInputElement? element, + ulong timestamp, Point position, PointerPointProperties properties, KeyModifiers inputModifiers) + { + var pointerOverElement = root.PointerOverElement; + + if (element != pointerOverElement) + { + if (element != null) + { + SetPointerOverToElement(pointer, root, element, timestamp, position, properties, inputModifiers); + } + else + { + ClearPointerOver(pointer, root, timestamp, position, properties, inputModifiers); + } + } + } + + private void SetPointerOverToElement(IPointer pointer, IInputRoot root, IInputElement element, + ulong timestamp, Point position, PointerPointProperties properties, KeyModifiers inputModifiers) + { + IInputElement? branch = null; + + IInputElement? el = element; + + while (el != null) + { + if (el.IsPointerOver) + { + branch = el; + break; + } + el = (IInputElement?)el.VisualParent; + } + + el = root.PointerOverElement; + + var e = new PointerEventArgs(InputElement.PointerLeaveEvent, el, pointer, root, position, + timestamp, properties, inputModifiers); + if (el != null && branch != null && !el.IsAttachedToVisualTree) + { + ClearChildrenPointerOver(e, branch, false); + } + + while (el != null && el != branch) + { + e.Source = el; + e.Handled = false; + el.RaiseEvent(e); + el = (IInputElement?)el.VisualParent; + } + + el = root.PointerOverElement = element; + _lastPointer = (pointer, root.PointToScreen(position)); + + e.RoutedEvent = InputElement.PointerEnterEvent; + + while (el != null && el != branch) + { + e.Source = el; + e.Handled = false; + el.RaiseEvent(e); + el = (IInputElement?)el.VisualParent; + } + } + } +} diff --git a/src/Avalonia.Input/Raw/RawDragEvent.cs b/src/Avalonia.Input/Raw/RawDragEvent.cs index 6e9ce20ff1..652bad7115 100644 --- a/src/Avalonia.Input/Raw/RawDragEvent.cs +++ b/src/Avalonia.Input/Raw/RawDragEvent.cs @@ -20,7 +20,7 @@ namespace Avalonia.Input.Raw Location = location; Data = data; Effects = effects; - KeyModifiers = KeyModifiersUtils.ConvertToKey(modifiers); + KeyModifiers = modifiers.ToKeyModifiers(); #pragma warning disable CS0618 // Type or member is obsolete Modifiers = (InputModifiers)modifiers; #pragma warning restore CS0618 // Type or member is obsolete diff --git a/src/Avalonia.Input/Raw/RawInputHelpers.cs b/src/Avalonia.Input/Raw/RawInputHelpers.cs new file mode 100644 index 0000000000..9d329bae59 --- /dev/null +++ b/src/Avalonia.Input/Raw/RawInputHelpers.cs @@ -0,0 +1,27 @@ +using Avalonia.Input.Raw; + +namespace Avalonia.Input +{ + internal static class RawInputHelpers + { + public static KeyModifiers ToKeyModifiers(this RawInputModifiers modifiers) => + (KeyModifiers)(modifiers & RawInputModifiers.KeyboardMask); + + public static PointerUpdateKind ToUpdateKind(this RawPointerEventType type) => type switch + { + RawPointerEventType.LeftButtonDown => PointerUpdateKind.LeftButtonPressed, + RawPointerEventType.LeftButtonUp => PointerUpdateKind.LeftButtonReleased, + RawPointerEventType.RightButtonDown => PointerUpdateKind.RightButtonPressed, + RawPointerEventType.RightButtonUp => PointerUpdateKind.RightButtonReleased, + RawPointerEventType.MiddleButtonDown => PointerUpdateKind.MiddleButtonPressed, + RawPointerEventType.MiddleButtonUp => PointerUpdateKind.MiddleButtonReleased, + RawPointerEventType.XButton1Down => PointerUpdateKind.XButton1Pressed, + RawPointerEventType.XButton1Up => PointerUpdateKind.XButton1Released, + RawPointerEventType.XButton2Down => PointerUpdateKind.XButton2Pressed, + RawPointerEventType.XButton2Up => PointerUpdateKind.XButton2Released, + RawPointerEventType.TouchBegin => PointerUpdateKind.LeftButtonPressed, + RawPointerEventType.TouchEnd => PointerUpdateKind.LeftButtonReleased, + _ => PointerUpdateKind.Other + }; + } +} diff --git a/src/Avalonia.Input/Raw/RawPointerEventArgs.cs b/src/Avalonia.Input/Raw/RawPointerEventArgs.cs index c157fa059c..8b9d7c161d 100644 --- a/src/Avalonia.Input/Raw/RawPointerEventArgs.cs +++ b/src/Avalonia.Input/Raw/RawPointerEventArgs.cs @@ -120,6 +120,8 @@ namespace Avalonia.Input.Raw /// only valid for Move and TouchUpdate /// public Lazy?>? IntermediatePoints { get; set; } + + internal IInputElement? InputHitTestResult { get; set; } } public struct RawPointerPoint diff --git a/src/Avalonia.Input/TouchDevice.cs b/src/Avalonia.Input/TouchDevice.cs index 20cafb9e8e..54dcc4051e 100644 --- a/src/Avalonia.Input/TouchDevice.cs +++ b/src/Avalonia.Input/TouchDevice.cs @@ -3,24 +3,26 @@ using System.Collections.Generic; using System.Linq; using Avalonia.Input.Raw; using Avalonia.Platform; +using Avalonia.VisualTree; namespace Avalonia.Input { /// /// Handles raw touch events + /// /// /// This class is supposed to be used on per-toplevel basis, don't use a shared one /// - /// - public class TouchDevice : IInputDevice, IDisposable + public class TouchDevice : IPointerDevice, IDisposable { private readonly Dictionary _pointers = new Dictionary(); private bool _disposed; private int _clickCount; private Rect _lastClickRect; private ulong _lastClickTime; - KeyModifiers GetKeyModifiers(RawInputModifiers modifiers) => - (KeyModifiers)(modifiers & RawInputModifiers.KeyboardMask); + private Pointer? _lastPointer; + + IInputElement? IPointerDevice.Captured => _lastPointer?.Captured; RawInputModifiers GetModifiers(RawInputModifiers modifiers, bool isLeftButtonDown) { @@ -30,6 +32,10 @@ namespace Avalonia.Input return rv; } + void IPointerDevice.Capture(IInputElement? control) => _lastPointer?.Capture(control); + + Point IPointerDevice.GetPosition(IVisual relativeTo) => default; + public void ProcessRawEvent(RawInputEventArgs ev) { if (ev.Handled || _disposed) @@ -39,15 +45,18 @@ namespace Avalonia.Input { if (args.Type == RawPointerEventType.TouchEnd) return; - var hit = args.Root.InputHitTest(args.Position); + var hit = args.InputHitTestResult; _pointers[args.TouchPointId] = pointer = new Pointer(Pointer.GetNextFreeId(), PointerType.Touch, _pointers.Count == 0); pointer.Capture(hit); } - + _lastPointer = pointer; var target = pointer.Captured ?? args.Root; + var updateKind = args.Type.ToUpdateKind(); + var keyModifier = args.InputModifiers.ToKeyModifiers(); + if (args.Type == RawPointerEventType.TouchBegin) { if (_pointers.Count > 1) @@ -73,9 +82,8 @@ namespace Avalonia.Input target.RaiseEvent(new PointerPressedEventArgs(target, pointer, args.Root, args.Position, ev.Timestamp, - new PointerPointProperties(GetModifiers(args.InputModifiers, true), - PointerUpdateKind.LeftButtonPressed), - GetKeyModifiers(args.InputModifiers), _clickCount)); + new PointerPointProperties(GetModifiers(args.InputModifiers, true), updateKind), + keyModifier, _clickCount)); } if (args.Type == RawPointerEventType.TouchEnd) @@ -85,10 +93,10 @@ namespace Avalonia.Input { target.RaiseEvent(new PointerReleasedEventArgs(target, pointer, args.Root, args.Position, ev.Timestamp, - new PointerPointProperties(GetModifiers(args.InputModifiers, false), - PointerUpdateKind.LeftButtonReleased), - GetKeyModifiers(args.InputModifiers), MouseButton.Left)); + new PointerPointProperties(GetModifiers(args.InputModifiers, false), updateKind), + keyModifier, MouseButton.Left)); } + _lastPointer = null; } if (args.Type == RawPointerEventType.TouchCancel) @@ -96,18 +104,16 @@ namespace Avalonia.Input _pointers.Remove(args.TouchPointId); using (pointer) pointer.Capture(null); + _lastPointer = null; } if (args.Type == RawPointerEventType.TouchUpdate) { - var modifiers = GetModifiers(args.InputModifiers, pointer.IsPrimary); target.RaiseEvent(new PointerEventArgs(InputElement.PointerMovedEvent, target, pointer, args.Root, args.Position, ev.Timestamp, - new PointerPointProperties(GetModifiers(args.InputModifiers, true), PointerUpdateKind.Other), - GetKeyModifiers(args.InputModifiers), args.IntermediatePoints)); + new PointerPointProperties(GetModifiers(args.InputModifiers, true), updateKind), + keyModifier, args.IntermediatePoints)); } - - } public void Dispose() @@ -121,5 +127,12 @@ namespace Avalonia.Input p.Dispose(); } + public IPointer? TryGetPointer(RawPointerEventArgs ev) + { + return ev is RawTouchEventArgs args + && _pointers.TryGetValue(args.TouchPointId, out var pointer) + ? pointer + : null; + } } } From 8fba608156ecc275f90a44d6b7af1aaa2a7e15f5 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 14 Apr 2022 11:47:35 -0400 Subject: [PATCH 129/416] Add more tests for pointerover --- .../TopLevelTests.cs | 21 +- .../MouseDeviceTests.cs | 177 ------ .../PointerOverTests.cs | 533 ++++++++++++++++++ 3 files changed, 536 insertions(+), 195 deletions(-) create mode 100644 tests/Avalonia.Input.UnitTests/PointerOverTests.cs diff --git a/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs b/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs index 9c2d760733..db6349cc5a 100644 --- a/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs @@ -193,6 +193,9 @@ namespace Avalonia.Controls.UnitTests public void Impl_Input_Should_Pass_Input_To_InputManager() { var inputManagerMock = new Mock(); + inputManagerMock.DefaultValue = DefaultValue.Mock; + inputManagerMock.SetupAllProperties(); + var services = TestServices.StyledWindow.With(inputManager: inputManagerMock.Object); using (UnitTestApplication.Start(services)) @@ -249,24 +252,6 @@ namespace Avalonia.Controls.UnitTests } } - [Fact] - public void Close_Should_Notify_MouseDevice() - { - using (UnitTestApplication.Start(TestServices.StyledWindow)) - { - var impl = new Mock(); - var mouseDevice = new Mock(); - impl.SetupAllProperties(); - impl.Setup(x => x.MouseDevice).Returns(mouseDevice.Object); - - var target = new TestTopLevel(impl.Object); - - impl.Object.Closed(); - - mouseDevice.Verify(x => x.TopLevelClosed(target)); - } - } - [Fact] public void Close_Should_Dispose_LayoutManager() { diff --git a/tests/Avalonia.Input.UnitTests/MouseDeviceTests.cs b/tests/Avalonia.Input.UnitTests/MouseDeviceTests.cs index 223f458f25..e8f8244d83 100644 --- a/tests/Avalonia.Input.UnitTests/MouseDeviceTests.cs +++ b/tests/Avalonia.Input.UnitTests/MouseDeviceTests.cs @@ -1,13 +1,10 @@ using Avalonia.Controls; using Avalonia.Input.Raw; -using Avalonia.Interactivity; using Avalonia.Media; using Avalonia.Rendering; using Avalonia.UnitTests; -using Avalonia.VisualTree; using Moq; using System; -using System.Collections.Generic; using Xunit; namespace Avalonia.Input.UnitTests @@ -34,160 +31,6 @@ namespace Avalonia.Input.UnitTests } #pragma warning restore CS0618 // Type or member is obsolete - [Fact] - public void MouseMove_Should_Update_IsPointerOver() - { - var renderer = new Mock(); - - using (TestApplication(renderer.Object)) - { - var inputManager = InputManager.Instance; - - Canvas canvas; - Border border; - Decorator decorator; - - var root = new TestRoot - { - MouseDevice = new MouseDevice(), - Renderer = renderer.Object, - Child = new Panel - { - Children = - { - (canvas = new Canvas()), - (border = new Border - { - Child = decorator = new Decorator(), - }) - } - } - }; - - SetHit(renderer, decorator); - SendMouseMove(inputManager, root); - - Assert.True(decorator.IsPointerOver); - Assert.True(border.IsPointerOver); - Assert.False(canvas.IsPointerOver); - Assert.True(root.IsPointerOver); - - SetHit(renderer, canvas); - SendMouseMove(inputManager, root); - - Assert.False(decorator.IsPointerOver); - Assert.False(border.IsPointerOver); - Assert.True(canvas.IsPointerOver); - Assert.True(root.IsPointerOver); - } - } - - [Fact] - public void IsPointerOver_Should_Be_Updated_When_Child_Sets_Handled_True() - { - var renderer = new Mock(); - - using (TestApplication(renderer.Object)) - { - var inputManager = InputManager.Instance; - - Canvas canvas; - Border border; - Decorator decorator; - - var root = new TestRoot - { - MouseDevice = new MouseDevice(), - Renderer = renderer.Object, - Child = new Panel - { - Children = - { - (canvas = new Canvas()), - (border = new Border - { - Child = decorator = new Decorator(), - }) - } - } - }; - - SetHit(renderer, canvas); - SendMouseMove(inputManager, root); - - Assert.False(decorator.IsPointerOver); - Assert.False(border.IsPointerOver); - Assert.True(canvas.IsPointerOver); - Assert.True(root.IsPointerOver); - - // Ensure that e.Handled is reset between controls. - decorator.PointerEnter += (s, e) => e.Handled = true; - - SetHit(renderer, decorator); - SendMouseMove(inputManager, root); - - Assert.True(decorator.IsPointerOver); - Assert.True(border.IsPointerOver); - Assert.False(canvas.IsPointerOver); - Assert.True(root.IsPointerOver); - } - } - - [Fact] - public void PointerEnter_Leave_Should_Be_Raised_In_Correct_Order() - { - var renderer = new Mock(); - var result = new List<(object, string)>(); - - void HandleEvent(object sender, PointerEventArgs e) - { - result.Add((sender, e.RoutedEvent.Name)); - } - - using (TestApplication(renderer.Object)) - { - var inputManager = InputManager.Instance; - - Canvas canvas; - Border border; - Decorator decorator; - - var root = new TestRoot - { - MouseDevice = new MouseDevice(), - Renderer = renderer.Object, - Child = new Panel - { - Children = - { - (canvas = new Canvas()), - (border = new Border - { - Child = decorator = new Decorator(), - }) - } - } - }; - - SetHit(renderer, canvas); - SendMouseMove(inputManager, root); - - AddEnterLeaveHandlers(HandleEvent, root, canvas, border, decorator); - SetHit(renderer, decorator); - SendMouseMove(inputManager, root); - - Assert.Equal( - new[] - { - ((object)canvas, "PointerLeave"), - ((object)decorator, "PointerEnter"), - ((object)border, "PointerEnter"), - }, - result); - } - } - - [Fact] public void GetPosition_Should_Respect_Control_RenderTransform() { @@ -216,17 +59,6 @@ namespace Avalonia.Input.UnitTests } } - private void AddEnterLeaveHandlers( - EventHandler handler, - params IControl[] controls) - { - foreach (var c in controls) - { - c.PointerEnter += handler; - c.PointerLeave += handler; - } - } - private void SendMouseMove(IInputManager inputManager, TestRoot root, Point p = new Point()) { inputManager.ProcessInput(new RawPointerEventArgs( @@ -238,15 +70,6 @@ namespace Avalonia.Input.UnitTests RawInputModifiers.None)); } - private void SetHit(Mock renderer, IControl hit) - { - renderer.Setup(x => x.HitTest(It.IsAny(), It.IsAny(), It.IsAny>())) - .Returns(new[] { hit }); - - renderer.Setup(x => x.HitTestFirst(It.IsAny(), It.IsAny(), It.IsAny>())) - .Returns(hit); - } - private IDisposable TestApplication(IRenderer renderer) { return UnitTestApplication.Start( diff --git a/tests/Avalonia.Input.UnitTests/PointerOverTests.cs b/tests/Avalonia.Input.UnitTests/PointerOverTests.cs new file mode 100644 index 0000000000..14acf9f946 --- /dev/null +++ b/tests/Avalonia.Input.UnitTests/PointerOverTests.cs @@ -0,0 +1,533 @@ +#nullable enable +using System; +using System.Collections.Generic; + +using Avalonia.Controls; +using Avalonia.Controls.Presenters; +using Avalonia.Controls.Templates; +using Avalonia.Input.Raw; +using Avalonia.Platform; +using Avalonia.Rendering; +using Avalonia.UnitTests; +using Avalonia.VisualTree; + +using Moq; + +using Xunit; + +namespace Avalonia.Input.UnitTests +{ + public class PointerOverTests + { + // https://github.com/AvaloniaUI/Avalonia/issues/2821 + [Fact] + public void Close_Should_Remove_PointerOver() + { + using var app = UnitTestApplication.Start(new TestServices(inputManager: new InputManager())); + + var renderer = new Mock(); + var device = CreatePointerDeviceMock().Object; + var impl = CreateTopLevelImplMock(renderer.Object); + + Canvas canvas; + var root = CreateInputRoot(impl.Object, new Panel + { + Children = + { + (canvas = new Canvas()) + } + }); + + SetHit(renderer, canvas); + impl.Object.Input!(CreateRawPointerMovedArgs(device, root)); + + Assert.True(canvas.IsPointerOver); + + impl.Object.Closed!(); + + Assert.False(canvas.IsPointerOver); + } + + [Fact] + public void MouseMove_Should_Update_IsPointerOver() + { + using var app = UnitTestApplication.Start(new TestServices(inputManager: new InputManager())); + + var renderer = new Mock(); + var device = CreatePointerDeviceMock().Object; + var impl = CreateTopLevelImplMock(renderer.Object); + + Canvas canvas; + Border border; + Decorator decorator; + + var root = CreateInputRoot(impl.Object, new Panel + { + Children = + { + (canvas = new Canvas()), + (border = new Border + { + Child = decorator = new Decorator(), + }) + } + }); + + SetHit(renderer, decorator); + impl.Object.Input!(CreateRawPointerMovedArgs(device, root)); + + Assert.True(decorator.IsPointerOver); + Assert.True(border.IsPointerOver); + Assert.False(canvas.IsPointerOver); + Assert.True(root.IsPointerOver); + + SetHit(renderer, canvas); + impl.Object.Input!(CreateRawPointerMovedArgs(device, root)); + + Assert.False(decorator.IsPointerOver); + Assert.False(border.IsPointerOver); + Assert.True(canvas.IsPointerOver); + Assert.True(root.IsPointerOver); + } + + + [Fact] + public void TouchMove_Should_Not_Set_IsPointerOver() + { + using var app = UnitTestApplication.Start(new TestServices(inputManager: new InputManager())); + + var renderer = new Mock(); + var device = CreatePointerDeviceMock(pointerType: PointerType.Touch).Object; + var impl = CreateTopLevelImplMock(renderer.Object); + + Canvas canvas; + + var root = CreateInputRoot(impl.Object, new Panel + { + Children = + { + (canvas = new Canvas()) + } + }); + + SetHit(renderer, canvas); + impl.Object.Input!(CreateRawPointerMovedArgs(device, root)); + + Assert.False(canvas.IsPointerOver); + Assert.False(root.IsPointerOver); + } + + [Fact] + public void HitTest_Should_Be_Ignored_If_Element_Captured() + { + using var app = UnitTestApplication.Start(new TestServices(inputManager: new InputManager())); + + var renderer = new Mock(); + var pointer = new Mock(); + var device = CreatePointerDeviceMock(pointer.Object).Object; + var impl = CreateTopLevelImplMock(renderer.Object); + + Canvas canvas; + Border border; + Decorator decorator; + + var root = CreateInputRoot(impl.Object, new Panel + { + Children = + { + (canvas = new Canvas()), + (border = new Border + { + Child = decorator = new Decorator(), + }) + } + }); + + SetHit(renderer, canvas); + pointer.SetupGet(p => p.Captured).Returns(decorator); + impl.Object.Input!(CreateRawPointerMovedArgs(device, root)); + + Assert.True(decorator.IsPointerOver); + Assert.True(border.IsPointerOver); + Assert.False(canvas.IsPointerOver); + Assert.True(root.IsPointerOver); + } + + [Fact] + public void IsPointerOver_Should_Be_Updated_When_Child_Sets_Handled_True() + { + using var app = UnitTestApplication.Start(new TestServices(inputManager: new InputManager())); + + var renderer = new Mock(); + var device = CreatePointerDeviceMock().Object; + var impl = CreateTopLevelImplMock(renderer.Object); + + Canvas canvas; + Border border; + Decorator decorator; + + var root = CreateInputRoot(impl.Object, new Panel + { + Children = + { + (canvas = new Canvas()), + (border = new Border + { + Child = decorator = new Decorator(), + }) + } + }); + + SetHit(renderer, canvas); + impl.Object.Input!(CreateRawPointerMovedArgs(device, root)); + + Assert.False(decorator.IsPointerOver); + Assert.False(border.IsPointerOver); + Assert.True(canvas.IsPointerOver); + Assert.True(root.IsPointerOver); + + // Ensure that e.Handled is reset between controls. + root.PointerMoved += (s, e) => e.Handled = true; + decorator.PointerEnter += (s, e) => e.Handled = true; + + SetHit(renderer, decorator); + impl.Object.Input!(CreateRawPointerMovedArgs(device, root)); + + Assert.True(decorator.IsPointerOver); + Assert.True(border.IsPointerOver); + Assert.False(canvas.IsPointerOver); + Assert.True(root.IsPointerOver); + } + + [Fact] + public void Pointer_Enter_Move_Leave_Should_Be_Followed() + { + using var app = UnitTestApplication.Start(new TestServices(inputManager: new InputManager())); + + var renderer = new Mock(); + var deviceMock = CreatePointerDeviceMock(); + var impl = CreateTopLevelImplMock(renderer.Object); + var result = new List<(object?, string)>(); + + void HandleEvent(object? sender, PointerEventArgs e) + { + result.Add((sender, e.RoutedEvent!.Name)); + } + + Canvas canvas; + Border border; + Decorator decorator; + + var root = CreateInputRoot(impl.Object, new Panel + { + Children = + { + (canvas = new Canvas()), + (border = new Border + { + Child = decorator = new Decorator(), + }) + } + }); + + AddEnterLeaveHandlers(HandleEvent, canvas, decorator); + + // Enter decorator + SetHit(renderer, decorator); + SetMove(deviceMock, root, decorator); + impl.Object.Input!(CreateRawPointerMovedArgs(deviceMock.Object, root)); + + // Leave decorator + SetHit(renderer, canvas); + SetMove(deviceMock, root, canvas); + impl.Object.Input!(CreateRawPointerMovedArgs(deviceMock.Object, root)); + + Assert.Equal( + new[] + { + ((object?)decorator, "PointerEnter"), + (decorator, "PointerMove"), + (decorator, "PointerLeave"), + (canvas, "PointerEnter"), + (canvas, "PointerMove") + }, + result); + } + + [Fact] + public void PointerEnter_Leave_Should_Be_Raised_In_Correct_Order() + { + using var app = UnitTestApplication.Start(new TestServices(inputManager: new InputManager())); + + var renderer = new Mock(); + var deviceMock = CreatePointerDeviceMock(); + var impl = CreateTopLevelImplMock(renderer.Object); + var result = new List<(object?, string)>(); + + void HandleEvent(object? sender, PointerEventArgs e) + { + result.Add((sender, e.RoutedEvent!.Name)); + } + + Canvas canvas; + Border border; + Decorator decorator; + + var root = CreateInputRoot(impl.Object, new Panel + { + Children = + { + (canvas = new Canvas()), + (border = new Border + { + Child = decorator = new Decorator(), + }) + } + }); + + SetHit(renderer, canvas); + impl.Object.Input!(CreateRawPointerMovedArgs(deviceMock.Object, root)); + + AddEnterLeaveHandlers(HandleEvent, root, canvas, border, decorator); + + SetHit(renderer, decorator); + impl.Object.Input!(CreateRawPointerMovedArgs(deviceMock.Object, root)); + + Assert.Equal( + new[] + { + ((object?)canvas, "PointerLeave"), + (decorator, "PointerEnter"), + (border, "PointerEnter"), + }, + result); + } + + // https://github.com/AvaloniaUI/Avalonia/issues/7896 + [Fact] + public void PointerEnter_Leave_Should_Set_Correct_Position() + { + using var app = UnitTestApplication.Start(new TestServices(inputManager: new InputManager())); + + var expectedPosition = new Point(15, 15); + var renderer = new Mock(); + var deviceMock = CreatePointerDeviceMock(); + var impl = CreateTopLevelImplMock(renderer.Object); + var result = new List<(object?, string, Point)>(); + + void HandleEvent(object? sender, PointerEventArgs e) + { + result.Add((sender, e.RoutedEvent!.Name, e.GetPosition(null))); + } + + Canvas canvas; + + var root = CreateInputRoot(impl.Object, new Panel + { + Children = + { + (canvas = new Canvas()) + } + }); + + AddEnterLeaveHandlers(HandleEvent, root, canvas); + + SetHit(renderer, canvas); + impl.Object.Input!(CreateRawPointerMovedArgs(deviceMock.Object, root, expectedPosition)); + + SetHit(renderer, null); + impl.Object.Input!(CreateRawPointerMovedArgs(deviceMock.Object, root, expectedPosition)); + + Assert.Equal( + new[] + { + ((object?)canvas, "PointerEnter", expectedPosition), + (root, "PointerEnter", expectedPosition), + (canvas, "PointerLeave", expectedPosition), + (root, "PointerLeave", expectedPosition) + }, + result); + } + + [Fact] + public void Render_Invalidation_Should_Affect_PointerOver() + { + using var app = UnitTestApplication.Start(new TestServices(inputManager: new InputManager())); + + var renderer = new Mock(); + var deviceMock = CreatePointerDeviceMock(); + var impl = CreateTopLevelImplMock(renderer.Object); + + var invalidateRect = new Rect(0, 0, 15, 15); + + Canvas canvas; + + var root = CreateInputRoot(impl.Object, new Panel + { + Children = + { + (canvas = new Canvas()) + } + }); + + // Let input know about latest device. + SetHit(renderer, canvas); + impl.Object.Input!(CreateRawPointerMovedArgs(deviceMock.Object, root)); + Assert.True(canvas.IsPointerOver); + + SetHit(renderer, canvas); + renderer.Raise(r => r.SceneInvalidated += null, new SceneInvalidatedEventArgs((IRenderRoot)root, invalidateRect)); + Assert.True(canvas.IsPointerOver); + + // Raise SceneInvalidated again, but now hide element from the hittest. + SetHit(renderer, null); + renderer.Raise(r => r.SceneInvalidated += null, new SceneInvalidatedEventArgs((IRenderRoot)root, invalidateRect)); + Assert.False(canvas.IsPointerOver); + } + + // https://github.com/AvaloniaUI/Avalonia/issues/7748 + [Fact] + public void LeaveWindow_Should_Reset_PointerOver() + { + using var app = UnitTestApplication.Start(new TestServices(inputManager: new InputManager())); + + var renderer = new Mock(); + var deviceMock = CreatePointerDeviceMock(); + var impl = CreateTopLevelImplMock(renderer.Object); + + var lastClientPosition = new Point(1, 5); + var invalidateRect = new Rect(0, 0, 15, 15); + var result = new List<(object?, string, Point)>(); + + void HandleEvent(object? sender, PointerEventArgs e) + { + result.Add((sender, e.RoutedEvent!.Name, e.GetPosition(null))); + } + + Canvas canvas; + + var root = CreateInputRoot(impl.Object, new Panel + { + Children = + { + (canvas = new Canvas()) + } + }); + + AddEnterLeaveHandlers(HandleEvent, root, canvas); + + // Init pointer over. + SetHit(renderer, canvas); + impl.Object.Input!(CreateRawPointerMovedArgs(deviceMock.Object, root, lastClientPosition)); + Assert.True(canvas.IsPointerOver); + + // Send LeaveWindow. + impl.Object.Input!(new RawPointerEventArgs(deviceMock.Object, 0, root, RawPointerEventType.LeaveWindow, new Point(), default)); + Assert.False(canvas.IsPointerOver); + + Assert.Equal( + new[] + { + ((object?)canvas, "PointerEnter", lastClientPosition), + (root, "PointerEnter", lastClientPosition), + (canvas, "PointerLeave", lastClientPosition), + (root, "PointerLeave", lastClientPosition), + }, + result); + } + + private static void AddEnterLeaveHandlers( + EventHandler handler, + params IInputElement[] controls) + { + foreach (var c in controls) + { + c.PointerEnter += handler; + c.PointerLeave += handler; + c.PointerMoved += handler; + } + } + + private static void SetHit(Mock renderer, IControl? hit) + { + renderer.Setup(x => x.HitTest(It.IsAny(), It.IsAny(), It.IsAny>())) + .Returns(hit is null ? Array.Empty() : new[] { hit }); + + renderer.Setup(x => x.HitTestFirst(It.IsAny(), It.IsAny(), It.IsAny>())) + .Returns(hit); + } + + private static void SetMove(Mock deviceMock, IInputRoot root, IInputElement element) + { + deviceMock.Setup(d => d.ProcessRawEvent(It.IsAny())) + .Callback(() => element.RaiseEvent(CreatePointerMovedArgs(root, element))); + } + + private static Mock CreateTopLevelImplMock(IRenderer renderer) + { + var impl = new Mock(); + impl.DefaultValue = DefaultValue.Mock; + impl.SetupAllProperties(); + impl.SetupGet(r => r.RenderScaling).Returns(1); + impl.Setup(r => r.CreateRenderer(It.IsAny())).Returns(renderer); + impl.Setup(r => r.PointToScreen(It.IsAny())).Returns(p => new PixelPoint((int)p.X, (int)p.Y)); + impl.Setup(r => r.PointToClient(It.IsAny())).Returns(p => new Point(p.X, p.Y)); + return impl; + } + + private static IInputRoot CreateInputRoot(IWindowImpl impl, IControl child) + { + var root = new Window(impl) + { + Width = 100, + Height = 100, + Content = child, + Template = new FuncControlTemplate((w, _) => new ContentPresenter + { + Content = w.Content + }) + }; + root.Show(); + return root; + } + + private static IInputRoot CreateInputRoot(IRenderer renderer, IControl child) + { + return CreateInputRoot(CreateTopLevelImplMock(renderer).Object, child); + } + + private static RawPointerEventArgs CreateRawPointerMovedArgs( + IPointerDevice pointerDevice, + IInputRoot root, + Point? positition = null) + { + return new RawPointerEventArgs(pointerDevice, 0, root, RawPointerEventType.Move, + positition ?? default, default); + } + + private static PointerEventArgs CreatePointerMovedArgs( + IInputRoot root, IInputElement? source, Point? positition = null) + { + return new PointerEventArgs(InputElement.PointerMovedEvent, source, new Mock().Object, root, + positition ?? default, default, PointerPointProperties.None, KeyModifiers.None); + } + + private static Mock CreatePointerDeviceMock( + IPointer? pointer = null, + PointerType pointerType = PointerType.Mouse) + { + if (pointer is null) + { + var pointerMock = new Mock(); + pointerMock.SetupGet(p => p.Type).Returns(pointerType); + pointer = pointerMock.Object; + } + + var pointerDevice = new Mock(); + pointerDevice.Setup(d => d.TryGetPointer(It.IsAny())) + .Returns(pointer); + + return pointerDevice; + } + } +} From 45916251515cbe354833366fb91c43decf1541c6 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 14 Apr 2022 11:47:38 -0400 Subject: [PATCH 130/416] Api compat --- src/Avalonia.Input/ApiCompatBaseline.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Avalonia.Input/ApiCompatBaseline.txt b/src/Avalonia.Input/ApiCompatBaseline.txt index 93c4cba5a6..6e68e7aed9 100644 --- a/src/Avalonia.Input/ApiCompatBaseline.txt +++ b/src/Avalonia.Input/ApiCompatBaseline.txt @@ -14,6 +14,7 @@ MembersMustExist : Member 'public void Avalonia.Input.InputElement.add_TextInput MembersMustExist : Member 'public void Avalonia.Input.InputElement.remove_DoubleTapped(System.EventHandler)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public void Avalonia.Input.InputElement.remove_Tapped(System.EventHandler)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public void Avalonia.Input.InputElement.remove_TextInputOptionsQuery(System.EventHandler)' does not exist in the implementation but it does exist in the contract. +InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Input.IPointer Avalonia.Input.IPointerDevice.TryGetPointer(Avalonia.Input.Raw.RawPointerEventArgs)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Input.TextInput.ITextInputMethodImpl.SetActive(System.Boolean)' is present in the contract but not in the implementation. MembersMustExist : Member 'public void Avalonia.Input.TextInput.ITextInputMethodImpl.SetActive(System.Boolean)' does not exist in the implementation but it does exist in the contract. InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Input.TextInput.ITextInputMethodImpl.SetClient(Avalonia.Input.TextInput.ITextInputMethodClient)' is present in the implementation but not in the contract. From 3c8cc3610f5559f8b8f2610faf8ca8ab0fdb3cb6 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 14 Apr 2022 11:47:56 -0400 Subject: [PATCH 131/416] Use InputHitTest instead of GetInputElementsAt+Linq in dev tools --- .../Diagnostics/ViewModels/MainViewModel.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs index e08c5bc8dd..d92bbb742b 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs @@ -7,7 +7,6 @@ using Avalonia.Diagnostics.Models; using Avalonia.Input; using Avalonia.Metadata; using Avalonia.Threading; -using System.Reactive.Linq; using System.Linq; namespace Avalonia.Diagnostics.ViewModels @@ -59,8 +58,8 @@ namespace Avalonia.Diagnostics.ViewModels .Subscribe(e => { PointerOverRoot = e.Root; - PointerOverElement = e.Root.GetInputElementsAt(e.Position).FirstOrDefault(); - }); + PointerOverElement = e.Root.InputHitTest(e.Position); + }); #nullable restore } Console = new ConsoleViewModel(UpdateConsoleContext); From 0bb22cff12d804b5a7993ea2e9ef6f0876060b3b Mon Sep 17 00:00:00 2001 From: Steven He Date: Fri, 15 Apr 2022 01:07:11 +0800 Subject: [PATCH 132/416] SubtypesFactory generator --- Avalonia.sln | 29 ++- build/SourceGenerators.props | 6 + src/Avalonia.Base/Animation/Easings/Easing.cs | 35 ++-- src/Avalonia.Base/Avalonia.Base.csproj | 1 + src/Avalonia.Base/Input/KeyGesture.cs | 8 + .../Remote/HtmlTransport/HtmlTransport.cs | 8 + .../Avalonia.SourceGenerator.csproj | 17 ++ .../SubtypesFactoryGenerator.cs | 167 ++++++++++++++++++ src/Shared/SourceGeneratorAttributes.cs | 17 ++ 9 files changed, 265 insertions(+), 23 deletions(-) create mode 100644 build/SourceGenerators.props create mode 100644 src/Avalonia.SourceGenerator/Avalonia.SourceGenerator.csproj create mode 100644 src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs create mode 100644 src/Shared/SourceGeneratorAttributes.cs diff --git a/Avalonia.sln b/Avalonia.sln index a216229a5b..39788160b2 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -39,6 +39,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{A689DE ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig src\Shared\ModuleInitializer.cs = src\Shared\ModuleInitializer.cs + src\Shared\RawEventGrouping.cs = src\Shared\RawEventGrouping.cs + src\Shared\SourceGeneratorAttributes.cs = src\Shared\SourceGeneratorAttributes.cs EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.ReactiveUI", "src\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj", "{6417B24E-49C2-4985-8DB2-3AB9D898EC91}" @@ -209,10 +211,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlSamples", "samples\S EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.PlatformSupport", "src\Avalonia.PlatformSupport\Avalonia.PlatformSupport.csproj", "{E8A597F0-2AB5-4BDA-A235-41162DAF53CF}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.iOS", "samples\ControlCatalog.iOS\ControlCatalog.iOS.csproj", "{70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.iOS", "samples\ControlCatalog.iOS\ControlCatalog.iOS.csproj", "{70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.PlatformSupport.UnitTests", "tests\Avalonia.PlatformSupport.UnitTests\Avalonia.PlatformSupport.UnitTests.csproj", "{CE910927-CE5A-456F-BC92-E4C757354A5C}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.SourceGenerator", "src\Avalonia.SourceGenerator\Avalonia.SourceGenerator.csproj", "{CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevAnalyzers", "src\tools\DevAnalyzers\DevAnalyzers.csproj", "{2B390431-288C-435C-BB6B-A374033BD8D1}" EndProject Global @@ -1929,6 +1932,30 @@ Global {CE910927-CE5A-456F-BC92-E4C757354A5C}.Release|iPhone.Build.0 = Release|Any CPU {CE910927-CE5A-456F-BC92-E4C757354A5C}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {CE910927-CE5A-456F-BC92-E4C757354A5C}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.AppStore|iPhone.Build.0 = Debug|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Debug|iPhone.Build.0 = Debug|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Release|Any CPU.Build.0 = Release|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Release|iPhone.ActiveCfg = Release|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Release|iPhone.Build.0 = Release|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {2B390431-288C-435C-BB6B-A374033BD8D1}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU {2B390431-288C-435C-BB6B-A374033BD8D1}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU {2B390431-288C-435C-BB6B-A374033BD8D1}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU diff --git a/build/SourceGenerators.props b/build/SourceGenerators.props new file mode 100644 index 0000000000..f9eab24d08 --- /dev/null +++ b/build/SourceGenerators.props @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/Avalonia.Base/Animation/Easings/Easing.cs b/src/Avalonia.Base/Animation/Easings/Easing.cs index c721772f3e..6dfc4c86b3 100644 --- a/src/Avalonia.Base/Animation/Easings/Easing.cs +++ b/src/Avalonia.Base/Animation/Easings/Easing.cs @@ -1,8 +1,9 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using System.Globalization; -using System.Linq; +using Avalonia.SourceGenerator; namespace Avalonia.Animation.Easings { @@ -10,14 +11,15 @@ namespace Avalonia.Animation.Easings /// Base class for all Easing classes. /// [TypeConverter(typeof(EasingTypeConverter))] - public abstract class Easing : IEasing + public abstract partial class Easing : IEasing { /// public abstract double Ease(double progress); - static Dictionary? _easingTypes; + private const string Namespace = "Avalonia.Animation.Easings"; - static readonly Type s_thisType = typeof(Easing); + [SubtypesFactory(typeof(Easing), Namespace)] + private static partial bool TryCreateEasingInstance(string type, [NotNullWhen(true)] out Easing? instance); /// /// Parses a Easing type string. @@ -26,33 +28,22 @@ namespace Avalonia.Animation.Easings /// Returns the instance of the parsed type. public static Easing Parse(string e) { +#if NETSTANDARD2_0 + if (e.Contains(",")) +#else if (e.Contains(',')) +#endif { return new SplineEasing(KeySpline.Parse(e, CultureInfo.InvariantCulture)); } - if (_easingTypes == null) + if (TryCreateEasingInstance(e, out var easing)) { - _easingTypes = new Dictionary(); - - // Fetch the built-in easings. - var derivedTypes = typeof(Easing).Assembly.GetTypes() - .Where(p => p.Namespace == s_thisType.Namespace) - .Where(p => p.IsSubclassOf(s_thisType)) - .Select(p => p); - - foreach (var easingType in derivedTypes) - _easingTypes.Add(easingType.Name, easingType); - } - - if (_easingTypes.ContainsKey(e)) - { - var type = _easingTypes[e]; - return (Easing)Activator.CreateInstance(type)!; + return easing; } else { - throw new FormatException($"Easing \"{e}\" was not found in {s_thisType.Namespace} namespace."); + throw new FormatException($"Easing \"{e}\" was not found in {Namespace} namespace."); } } } diff --git a/src/Avalonia.Base/Avalonia.Base.csproj b/src/Avalonia.Base/Avalonia.Base.csproj index 5b79d3cd7b..8e4755b4b7 100644 --- a/src/Avalonia.Base/Avalonia.Base.csproj +++ b/src/Avalonia.Base/Avalonia.Base.csproj @@ -16,4 +16,5 @@ + diff --git a/src/Avalonia.Base/Input/KeyGesture.cs b/src/Avalonia.Base/Input/KeyGesture.cs index 0adbe73263..6f302a3ae7 100644 --- a/src/Avalonia.Base/Input/KeyGesture.cs +++ b/src/Avalonia.Base/Input/KeyGesture.cs @@ -155,7 +155,11 @@ namespace Avalonia.Input if (s_keySynonyms.TryGetValue(key.ToLower(), out Key rv)) return rv; +#if NETSTANDARD2_0 return (Key)Enum.Parse(typeof(Key), key, true); +#else + return Enum.Parse(key, true); +#endif } private static KeyModifiers ParseModifier(ReadOnlySpan modifier) @@ -172,7 +176,11 @@ namespace Avalonia.Input return KeyModifiers.Meta; } +#if NETSTANDARD2_0 return (KeyModifiers)Enum.Parse(typeof(KeyModifiers), modifier.ToString(), true); +#else + return Enum.Parse(modifier.ToString(), true); +#endif } private Key ResolveNumPadOperationKey(Key key) diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs index 6b1934ed06..7965e5d0d8 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs @@ -320,15 +320,23 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport ? null : modifiersText .Split(',') +#if NETSTANDARD2_0 .Select(x => (InputProtocol.InputModifiers)Enum.Parse( typeof(InputProtocol.InputModifiers), x, true)) +#else + .Select(x => Enum.Parse(x, true)) +#endif .ToArray(); private static InputProtocol.MouseButton ParseMouseButton(string buttonText) => string.IsNullOrWhiteSpace(buttonText) ? InputProtocol.MouseButton.None +#if NETSTANDARD2_0 : (InputProtocol.MouseButton)Enum.Parse( typeof(InputProtocol.MouseButton), buttonText, true); +#else + : Enum.Parse(buttonText, true); +#endif private static double ParseDouble(string text) => double.Parse(text, NumberStyles.Float, CultureInfo.InvariantCulture); diff --git a/src/Avalonia.SourceGenerator/Avalonia.SourceGenerator.csproj b/src/Avalonia.SourceGenerator/Avalonia.SourceGenerator.csproj new file mode 100644 index 0000000000..97e58f8a64 --- /dev/null +++ b/src/Avalonia.SourceGenerator/Avalonia.SourceGenerator.csproj @@ -0,0 +1,17 @@ + + + + netstandard2.0 + enable + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + diff --git a/src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs b/src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs new file mode 100644 index 0000000000..7bc240853e --- /dev/null +++ b/src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs @@ -0,0 +1,167 @@ +using System.Collections.Generic; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Avalonia.SourceGenerator +{ + internal class GenerateSubtypesSyntaxReceiver : ISyntaxReceiver + { + public List<(MethodDeclarationSyntax, AttributeSyntax)> CandidateMethods { get; } = new(); + public List Types { get; } = new(); + + public void OnVisitSyntaxNode(SyntaxNode syntaxNode) + { + if (syntaxNode is MethodDeclarationSyntax declarationSyntax) + { + foreach (var attribute in declarationSyntax.AttributeLists.SelectMany(i => i.Attributes)) + { + CandidateMethods.Add((declarationSyntax, attribute)); + } + } + + if (syntaxNode is ClassDeclarationSyntax or StructDeclarationSyntax) + { + Types.Add(syntaxNode); + } + } + } + + [Generator] + internal class SubtypesFactoryGenerator : ISourceGenerator + { + private readonly GenerateSubtypesSyntaxReceiver _receiver = new(); + private static readonly string s_attributeName = typeof(SubtypesFactoryAttribute).FullName; + + public void Execute(GeneratorExecutionContext context) + { + var methods = new List<(IMethodSymbol, ITypeSymbol, string)>(); + + foreach (var (method, attribute) in _receiver.CandidateMethods) + { + var semanticModel = context.Compilation.GetSemanticModel(method.SyntaxTree); + var attributeTypeInfo = semanticModel.GetTypeInfo(attribute); + if (attributeTypeInfo.Type is null || + attributeTypeInfo.Type.ToString() != s_attributeName || + attribute.ArgumentList is null) + { + continue; + } + + var arguments = attribute.ArgumentList.Arguments; + if (arguments.Count != 2) + { + continue; + } + + if (arguments[0].Expression is not TypeOfExpressionSyntax typeOfExpr || + arguments[1].Expression is not LiteralExpressionSyntax and not IdentifierNameSyntax) + { + continue; + } + + var type = semanticModel.GetTypeInfo(typeOfExpr.Type); + var ns = semanticModel.GetConstantValue(arguments[1].Expression); + var methodDeclInfo = semanticModel.GetDeclaredSymbol(method); + + if (type.Type is not ITypeSymbol baseType || + ns.HasValue is false || + ns.Value is not string nsValue || + methodDeclInfo is not IMethodSymbol methodSymbol || + methodSymbol.Parameters.Length != 2 || + methodSymbol.Parameters[1].RefKind != RefKind.Out) + { + continue; + } + + methods.Add((methodSymbol, baseType, nsValue)); + } + + var types = new List(); + foreach (var type in _receiver.Types) + { + var semanticModel = context.Compilation.GetSemanticModel(type.SyntaxTree); + var decl = semanticModel.GetDeclaredSymbol(type); + if (decl is ITypeSymbol typeSymbol) + { + types.Add(typeSymbol); + } + } + + GenerateSubTypes(context, methods, types); + } + + private bool IsSubtypeOf(ITypeSymbol type, ITypeSymbol baseType) + { + if (type.BaseType is null) + { + return false; + } + + if (SymbolEqualityComparer.Default.Equals(type.BaseType, baseType)) + { + return true; + } + + return IsSubtypeOf(type.BaseType, baseType); + } + + private void GenerateSubTypes( + GeneratorExecutionContext context, + List<(IMethodSymbol Method, ITypeSymbol BaseType, string Namespace)> methods, + List types) + { + foreach (var (method, baseType, @namespace) in methods) + { + var candidateTypes = types.Where(i => IsSubtypeOf(i, baseType)).Where(i => $"{i.ContainingNamespace}.".StartsWith($"{@namespace}.")).ToArray(); + var type = method.ContainingType; + var isGeneric = type.TypeParameters.Length > 0; + var isClass = type.TypeKind == TypeKind.Class; + + if (method.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax() is not MethodDeclarationSyntax methodDecl) + { + continue; + } + + var parameters = new SeparatedSyntaxList().AddRange(methodDecl.ParameterList.Parameters.Select(i => i.WithAttributeLists(new SyntaxList()))); + + var methodDeclText = methodDecl + .WithAttributeLists(new SyntaxList()) + .WithParameterList(methodDecl.ParameterList.WithParameters(parameters)) + .WithBody(null) + .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.None)) + .WithoutTrivia().ToString(); + + var typeDecl = $"partial {(isClass ? "class" : "struct")} {type.Name}{(isGeneric ? $"<{string.Join(", ", type.TypeParameters)}>" : "")}"; + var source = $@"using System; +using System.Collections.Generic; + +namespace {method.ContainingNamespace} +{{ + {typeDecl} + {{ + {methodDeclText} + {{ + var hasMatch = false; + (hasMatch, {method.Parameters[1].Name}) = {method.Parameters[0].Name} switch + {{ +{string.Join("\n", candidateTypes.Select(i => $" \"{i.Name}\" => (true, ({method.Parameters[1].Type})new {i}()),"))} + _ => (false, default({method.Parameters[1].Type})) + }}; + + return hasMatch; + }} + }} +}}"; + + context.AddSource($"{type}.{method.MetadataName}.gen.cs", source); + } + } + + public void Initialize(GeneratorInitializationContext context) + { + context.RegisterForSyntaxNotifications(() => _receiver); + } + } +} diff --git a/src/Shared/SourceGeneratorAttributes.cs b/src/Shared/SourceGeneratorAttributes.cs new file mode 100644 index 0000000000..fdb5977d23 --- /dev/null +++ b/src/Shared/SourceGeneratorAttributes.cs @@ -0,0 +1,17 @@ +using System; + +namespace Avalonia.SourceGenerator +{ + [AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = true)] + internal sealed class SubtypesFactoryAttribute : Attribute + { + public SubtypesFactoryAttribute(Type baseType, string @namespace) + { + BaseType = baseType; + Namespace = @namespace; + } + + public string Namespace { get; } + public Type BaseType { get; } + } +} From 9296ada3432010edb0201f99b81daeb498f89809 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Komosi=C5=84ski?= Date: Thu, 14 Apr 2022 19:12:02 +0200 Subject: [PATCH 133/416] Post merge fixup. --- src/Avalonia.Controls/TopLevel.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index 271129f3ac..d27a020300 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -91,7 +91,7 @@ namespace Avalonia.Controls private WindowTransparencyLevel _actualTransparencyLevel; private ILayoutManager? _layoutManager; private Border? _transparencyFallbackBorder; - private WeakEventSubscriber _resourcesChangesSubscriber; + private WeakEventSubscriber? _resourcesChangesSubscriber; /// /// Initializes static members of the class. @@ -300,11 +300,6 @@ namespace Avalonia.Controls /// IMouseDevice? IInputRoot.MouseDevice => PlatformImpl?.MouseDevice; - void IWeakEventSubscriber.OnEvent(object? sender, WeakEvent ev, ResourcesChangedEventArgs e) - { - ((ILogical)this).NotifyResourcesChanged(e); - } - /// /// Gets or sets a value indicating whether access keys are shown in the window. /// From 6700fd851b8196b34d053bd4956906a6ee6f6c7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Komosi=C5=84ski?= Date: Thu, 14 Apr 2022 19:12:16 +0200 Subject: [PATCH 134/416] Add a benchmark case for affects render. --- .../Visuals/VisualAffectsRenderBenchmarks.cs | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 tests/Avalonia.Benchmarks/Visuals/VisualAffectsRenderBenchmarks.cs diff --git a/tests/Avalonia.Benchmarks/Visuals/VisualAffectsRenderBenchmarks.cs b/tests/Avalonia.Benchmarks/Visuals/VisualAffectsRenderBenchmarks.cs new file mode 100644 index 0000000000..4a7c842249 --- /dev/null +++ b/tests/Avalonia.Benchmarks/Visuals/VisualAffectsRenderBenchmarks.cs @@ -0,0 +1,45 @@ +using Avalonia.Controls; +using Avalonia.Media; +using BenchmarkDotNet.Attributes; + +namespace Avalonia.Benchmarks.Visuals; + +[MemoryDiagnoser] +public class VisualAffectsRenderBenchmarks +{ + private readonly TestVisual _target; + private readonly IPen _pen; + + public VisualAffectsRenderBenchmarks() + { + _target = new TestVisual(); + _pen = new Pen(Brushes.Black); + } + + [Benchmark] + public void SetPropertyThatAffectsRender() + { + _target.Pen = _pen; + _target.Pen = null; + } + + private class TestVisual : Visual + { + /// + /// Defines the property. + /// + public static readonly StyledProperty PenProperty = + AvaloniaProperty.Register(nameof(Pen)); + + public IPen Pen + { + get { return GetValue(PenProperty); } + set { SetValue(PenProperty, value); } + } + + static TestVisual() + { + AffectsRender(PenProperty); + } + } +} From 276afe4271cc7e95bf182bd0589eaa0bd27709f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Komosi=C5=84ski?= Date: Thu, 14 Apr 2022 19:12:44 +0200 Subject: [PATCH 135/416] Reduce allocations when creating weak event subscribers. --- .../Utilities/IWeakEventSubscriber.cs | 21 +++++++++++++++++-- src/Avalonia.Base/Visual.cs | 12 +++++------ 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/Avalonia.Base/Utilities/IWeakEventSubscriber.cs b/src/Avalonia.Base/Utilities/IWeakEventSubscriber.cs index 57060853c8..6cf8c605a7 100644 --- a/src/Avalonia.Base/Utilities/IWeakEventSubscriber.cs +++ b/src/Avalonia.Base/Utilities/IWeakEventSubscriber.cs @@ -11,7 +11,7 @@ public interface IWeakEventSubscriber where TEventArgs : EventArg void OnEvent(object? sender, WeakEvent ev, TEventArgs e); } -public class WeakEventSubscriber : IWeakEventSubscriber where TEventArgs : EventArgs +public sealed class WeakEventSubscriber : IWeakEventSubscriber where TEventArgs : EventArgs { public event Action? Event; @@ -19,4 +19,21 @@ public class WeakEventSubscriber : IWeakEventSubscriber { Event?.Invoke(sender, ev, e); } -} \ No newline at end of file +} + +public sealed class TargetWeakEventSubscriber : IWeakEventSubscriber where TEventArgs : EventArgs +{ + private readonly TTarget _target; + private readonly Action _dispatchFunc; + + public TargetWeakEventSubscriber(TTarget target, Action dispatchFunc) + { + _target = target; + _dispatchFunc = dispatchFunc; + } + + void IWeakEventSubscriber.OnEvent(object? sender, WeakEvent ev, TEventArgs e) + { + _dispatchFunc(_target, sender, ev, e); + } +} diff --git a/src/Avalonia.Base/Visual.cs b/src/Avalonia.Base/Visual.cs index e25cb854cf..4fd21f02f9 100644 --- a/src/Avalonia.Base/Visual.cs +++ b/src/Avalonia.Base/Visual.cs @@ -108,7 +108,7 @@ namespace Avalonia private IRenderRoot? _visualRoot; private IVisual? _visualParent; private bool _hasMirrorTransform; - private WeakEventSubscriber? _affectsRenderWeakSubscriber; + private TargetWeakEventSubscriber? _affectsRenderWeakSubscriber; /// /// Initializes static members of the class. @@ -383,11 +383,11 @@ namespace Avalonia { if (sender._affectsRenderWeakSubscriber == null) { - sender._affectsRenderWeakSubscriber = new WeakEventSubscriber(); - sender._affectsRenderWeakSubscriber.Event += delegate - { - sender.InvalidateVisual(); - }; + sender._affectsRenderWeakSubscriber = new TargetWeakEventSubscriber( + sender, static (target, _, _, _) => + { + target.InvalidateVisual(); + }); } InvalidatedWeakEvent.Subscribe(newValue, sender._affectsRenderWeakSubscriber); } From 832afd2b46be359b9ea25e550f39902439d584a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Komosi=C5=84ski?= Date: Thu, 14 Apr 2022 22:38:51 +0200 Subject: [PATCH 136/416] Apply optimization to Pen as well. --- src/Avalonia.Base/Media/Pen.cs | 15 ++++++++------- .../Visuals/VisualAffectsRenderBenchmarks.cs | 4 ++-- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/Avalonia.Base/Media/Pen.cs b/src/Avalonia.Base/Media/Pen.cs index f0a0d24248..bda0e5cb99 100644 --- a/src/Avalonia.Base/Media/Pen.cs +++ b/src/Avalonia.Base/Media/Pen.cs @@ -48,7 +48,7 @@ namespace Avalonia.Media private EventHandler? _invalidated; private IAffectsRender? _subscribedToBrush; private IAffectsRender? _subscribedToDashes; - private WeakEventSubscriber? _weakSubscriber; + private TargetWeakEventSubscriber? _weakSubscriber; /// /// Initializes a new instance of the class. @@ -217,13 +217,14 @@ namespace Avalonia.Media { if (_weakSubscriber == null) { - _weakSubscriber = new WeakEventSubscriber(); - _weakSubscriber.Event += (_, ev, __) => - { - if (ev == InvalidatedWeakEvent) - _invalidated?.Invoke(this, EventArgs.Empty); - }; + _weakSubscriber = new TargetWeakEventSubscriber( + this, static (target, _, ev, _) => + { + if (ev == InvalidatedWeakEvent) + target._invalidated?.Invoke(target, EventArgs.Empty); + }); } + InvalidatedWeakEvent.Subscribe(affectsRender, _weakSubscriber); field = affectsRender; } diff --git a/tests/Avalonia.Benchmarks/Visuals/VisualAffectsRenderBenchmarks.cs b/tests/Avalonia.Benchmarks/Visuals/VisualAffectsRenderBenchmarks.cs index 4a7c842249..5b85a068d6 100644 --- a/tests/Avalonia.Benchmarks/Visuals/VisualAffectsRenderBenchmarks.cs +++ b/tests/Avalonia.Benchmarks/Visuals/VisualAffectsRenderBenchmarks.cs @@ -33,8 +33,8 @@ public class VisualAffectsRenderBenchmarks public IPen Pen { - get { return GetValue(PenProperty); } - set { SetValue(PenProperty, value); } + get => GetValue(PenProperty); + set => SetValue(PenProperty, value); } static TestVisual() From 7beafdc1d84f453211a537a614c66924bf2a098d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Komosi=C5=84ski?= Date: Thu, 14 Apr 2022 22:50:30 +0200 Subject: [PATCH 137/416] Refactor TopLevel and ItemsRepeater handling. --- .../Repeater/ItemsRepeater.cs | 21 ++++++++++--------- src/Avalonia.Controls/TopLevel.cs | 13 ++++++------ 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/Avalonia.Controls/Repeater/ItemsRepeater.cs b/src/Avalonia.Controls/Repeater/ItemsRepeater.cs index 97129f887d..c59037a30b 100644 --- a/src/Avalonia.Controls/Repeater/ItemsRepeater.cs +++ b/src/Avalonia.Controls/Repeater/ItemsRepeater.cs @@ -60,6 +60,7 @@ namespace Avalonia.Controls private readonly ViewManager _viewManager; private readonly ViewportManager _viewportManager; + private readonly TargetWeakEventSubscriber _layoutWeakSubscriber; private IEnumerable? _items; private VirtualizingLayoutContext? _layoutContext; private EventHandler? _childIndexChanged; @@ -68,25 +69,25 @@ namespace Avalonia.Controls private ItemsRepeaterElementPreparedEventArgs? _elementPreparedArgs; private ItemsRepeaterElementClearingEventArgs? _elementClearingArgs; private ItemsRepeaterElementIndexChangedEventArgs? _elementIndexChangedArgs; - private WeakEventSubscriber _layoutWeakSubscriber = new(); /// /// Initializes a new instance of the class. /// public ItemsRepeater() { + _layoutWeakSubscriber = new TargetWeakEventSubscriber( + this, static (target, _, ev, _) => + { + if (ev == AttachedLayout.ArrangeInvalidatedWeakEvent) + target.InvalidateArrange(); + else if (ev == AttachedLayout.MeasureInvalidatedWeakEvent) + target.InvalidateMeasure(); + }); + _viewManager = new ViewManager(this); _viewportManager = new ViewportManager(this); KeyboardNavigation.SetTabNavigation(this, KeyboardNavigationMode.Once); - - _layoutWeakSubscriber.Event += (_, ev, e) => - { - if (ev == AttachedLayout.ArrangeInvalidatedWeakEvent) - InvalidateArrange(); - else if (ev == AttachedLayout.MeasureInvalidatedWeakEvent) - InvalidateMeasure(); - }; - + OnLayoutChanged(null, Layout); } diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index d27a020300..34e87e18cc 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -91,7 +91,7 @@ namespace Avalonia.Controls private WindowTransparencyLevel _actualTransparencyLevel; private ILayoutManager? _layoutManager; private Border? _transparencyFallbackBorder; - private WeakEventSubscriber? _resourcesChangesSubscriber; + private TargetWeakEventSubscriber? _resourcesChangesSubscriber; /// /// Initializes static members of the class. @@ -191,11 +191,12 @@ namespace Avalonia.Controls if (((IStyleHost)this).StylingParent is IResourceHost applicationResources) { - _resourcesChangesSubscriber = new(); - _resourcesChangesSubscriber.Event += (_, __, e) => - { - ((ILogical)this).NotifyResourcesChanged(e); - }; + _resourcesChangesSubscriber = new TargetWeakEventSubscriber( + this, static (target, _, _, e) => + { + ((ILogical)target).NotifyResourcesChanged(e); + }); + ResourcesChangedWeakEvent.Subscribe(applicationResources, _resourcesChangesSubscriber); } From 705a2813ab7b4a676a35eeca0cdb1f0822c87be8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Komosi=C5=84ski?= Date: Thu, 14 Apr 2022 22:53:22 +0200 Subject: [PATCH 138/416] Undo formatting change. --- src/Avalonia.Controls/Repeater/ItemsRepeater.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Avalonia.Controls/Repeater/ItemsRepeater.cs b/src/Avalonia.Controls/Repeater/ItemsRepeater.cs index c59037a30b..c63adb0bf6 100644 --- a/src/Avalonia.Controls/Repeater/ItemsRepeater.cs +++ b/src/Avalonia.Controls/Repeater/ItemsRepeater.cs @@ -87,7 +87,6 @@ namespace Avalonia.Controls _viewManager = new ViewManager(this); _viewportManager = new ViewportManager(this); KeyboardNavigation.SetTabNavigation(this, KeyboardNavigationMode.Once); - OnLayoutChanged(null, Layout); } From e6044ea33069d994b65aa05aad7c775e7686d8d9 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 14 Apr 2022 23:49:52 +0200 Subject: [PATCH 139/416] Remove Avalonia.AndroidTestApplication. It's no longer needed. --- Avalonia.sln | 21 +--- .../Avalonia.AndroidTestApplication.csproj | 79 ------------- .../MainActivity.cs | 104 ------------------ .../Properties/AndroidManifest.xml | 4 - .../Resources/AboutResources.txt | 50 --------- .../Resources/drawable/Icon.png | Bin 4147 -> 0 bytes .../Resources/values/Strings.xml | 6 - 7 files changed, 1 insertion(+), 263 deletions(-) delete mode 100644 src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj delete mode 100644 src/Android/Avalonia.AndroidTestApplication/MainActivity.cs delete mode 100644 src/Android/Avalonia.AndroidTestApplication/Properties/AndroidManifest.xml delete mode 100644 src/Android/Avalonia.AndroidTestApplication/Resources/AboutResources.txt delete mode 100644 src/Android/Avalonia.AndroidTestApplication/Resources/drawable/Icon.png delete mode 100644 src/Android/Avalonia.AndroidTestApplication/Resources/values/Strings.xml diff --git a/Avalonia.sln b/Avalonia.sln index a216229a5b..64ed214e1f 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -55,8 +55,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Android", "Android", "{7CF9 EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Android", "src\Android\Avalonia.Android\Avalonia.Android.csproj", "{7B92AF71-6287-4693-9DCB-BD5B6E927E23}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.AndroidTestApplication", "src\Android\Avalonia.AndroidTestApplication\Avalonia.AndroidTestApplication.csproj", "{FF69B927-C545-49AE-8E16-3D14D621AA12}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "iOS", "iOS", "{0CB0B92E-6CFF-4240-80A5-CCAFE75D91E1}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.iOS", "src\iOS\Avalonia.iOS\Avalonia.iOS.csproj", "{4488AD85-1495-4809-9AA4-DDFE0A48527E}" @@ -209,7 +207,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlSamples", "samples\S EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.PlatformSupport", "src\Avalonia.PlatformSupport\Avalonia.PlatformSupport.csproj", "{E8A597F0-2AB5-4BDA-A235-41162DAF53CF}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.iOS", "samples\ControlCatalog.iOS\ControlCatalog.iOS.csproj", "{70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.iOS", "samples\ControlCatalog.iOS\ControlCatalog.iOS.csproj", "{70B9F5CC-E2F9-4314-9514-EDE762ACCC4B}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.PlatformSupport.UnitTests", "tests\Avalonia.PlatformSupport.UnitTests\Avalonia.PlatformSupport.UnitTests.csproj", "{CE910927-CE5A-456F-BC92-E4C757354A5C}" EndProject @@ -629,22 +627,6 @@ Global {7B92AF71-6287-4693-9DCB-BD5B6E927E23}.Release|Any CPU.Build.0 = Release|Any CPU {7B92AF71-6287-4693-9DCB-BD5B6E927E23}.Release|iPhone.ActiveCfg = Release|Any CPU {7B92AF71-6287-4693-9DCB-BD5B6E927E23}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.AppStore|Any CPU.ActiveCfg = Release|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.AppStore|iPhone.ActiveCfg = Release|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.Debug|Any CPU.Deploy.0 = Debug|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.Release|Any CPU.Build.0 = Release|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.Release|Any CPU.Deploy.0 = Release|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.Release|iPhone.ActiveCfg = Release|Any CPU - {FF69B927-C545-49AE-8E16-3D14D621AA12}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {4488AD85-1495-4809-9AA4-DDFE0A48527E}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU {4488AD85-1495-4809-9AA4-DDFE0A48527E}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU {4488AD85-1495-4809-9AA4-DDFE0A48527E}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU @@ -1970,7 +1952,6 @@ Global {8EF392D5-1416-45AA-9956-7CBBC3229E8A} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {08B3E6B9-1CD5-443C-9F61-6D49D1C5F162} = {9B9E3891-2366-4253-A952-D08BCEB71098} {7B92AF71-6287-4693-9DCB-BD5B6E927E23} = {7CF9789C-F1D3-4D0E-90E5-F1DF67A2753F} - {FF69B927-C545-49AE-8E16-3D14D621AA12} = {7CF9789C-F1D3-4D0E-90E5-F1DF67A2753F} {4488AD85-1495-4809-9AA4-DDFE0A48527E} = {0CB0B92E-6CFF-4240-80A5-CCAFE75D91E1} {E1AA3DBF-9056-4530-9376-18119A7A3FFE} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {88060192-33D5-4932-B0F9-8BD2763E857D} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} diff --git a/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj b/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj deleted file mode 100644 index ee41b9f354..0000000000 --- a/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj +++ /dev/null @@ -1,79 +0,0 @@ - - - net6.0-android - 21 - Exe - enable - com.Avalonia.AndroidTestApplication - 1 - 1.0 - apk - true - portable - - - - - Resources\drawable\Icon.png - - - - - True - True - True - True - - - - - - - True - - - - True - - - - - - - - - {7b92af71-6287-4693-9dcb-bd5b6e927e23} - Avalonia.Android - - - {3e53a01a-b331-47f3-b828-4a5717e77a24} - Avalonia.Markup.Xaml - - - {b09b78d8-9b26-48b0-9149-d64a2f120f3f} - Avalonia.Base - - - {d2221c82-4a25-4583-9b43-d791e3f6820c} - Avalonia.Controls - - - {7062ae20-5dcc-4442-9645-8195bdece63e} - Avalonia.Diagnostics - - - {3e10a5fa-e8da-48b1-ad44-6a5b6cb7750f} - Avalonia.Themes.Default - - - {7d2d3083-71dd-4cc9-8907-39a0d86fb322} - Avalonia.Skia - - - - - - - - - diff --git a/src/Android/Avalonia.AndroidTestApplication/MainActivity.cs b/src/Android/Avalonia.AndroidTestApplication/MainActivity.cs deleted file mode 100644 index 8f4beb2737..0000000000 --- a/src/Android/Avalonia.AndroidTestApplication/MainActivity.cs +++ /dev/null @@ -1,104 +0,0 @@ -using System; -using Android.App; -using Android.Content.PM; -using Avalonia.Android; -using Avalonia.Controls; -using Avalonia.Controls.ApplicationLifetimes; -using Avalonia.Input.TextInput; -using Avalonia.Markup.Xaml; -using Avalonia.Media; -using Avalonia.Styling; -using Avalonia.Themes.Default; - -namespace Avalonia.AndroidTestApplication -{ - [Activity(Label = "Main", - MainLauncher = true, - Icon = "@drawable/icon", - Theme = "@style/Theme.AppCompat.NoActionBar", - ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, - LaunchMode = LaunchMode.SingleInstance/*, - ScreenOrientation = ScreenOrientation.Landscape*/)] - public class MainActivity : AvaloniaActivity - { - protected override AppBuilder CustomizeAppBuilder(AppBuilder builder) - { - return base.CustomizeAppBuilder(builder); - } - } - - public class App : Application - { - public override void Initialize() - { - Styles.Add(new SimpleTheme(new Uri("avares://Avalonia.AndroidTestApplication"))); - } - - public override void OnFrameworkInitializationCompleted() - { - if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewLifetime) - { - singleViewLifetime.MainView = CreateSimpleWindow(); - } - - base.OnFrameworkInitializationCompleted(); - } - - // This provides a simple UI tree for testing input handling, drawing, etc - public static ContentControl CreateSimpleWindow() - { - ContentControl window = new ContentControl() - { - Background = Brushes.Red, - Content = new StackPanel - { - Margin = new Thickness(30), - Background = Brushes.Yellow, - Children = - { - new TextBlock - { - Text = "TEXT BLOCK", - Width = 300, - Height = 40, - Background = Brushes.White, - Foreground = Brushes.Black - }, - - new Button - { - Content = "BUTTON", - Width = 150, - Height = 40, - Background = Brushes.LightGreen, - Foreground = Brushes.Black - }, - - CreateTextBox(TextInputContentType.Normal), - CreateTextBox(TextInputContentType.Password), - CreateTextBox(TextInputContentType.Email), - CreateTextBox(TextInputContentType.Url), - CreateTextBox(TextInputContentType.Digits), - CreateTextBox(TextInputContentType.Number), - } - } - }; - - return window; - } - - private static TextBox CreateTextBox(TextInputContentType contentType) - { - var textBox = new TextBox() - { - Margin = new Thickness(20, 10), - Watermark = contentType.ToString(), - BorderThickness = new Thickness(3), - FontSize = 20, - [TextInputOptions.ContentTypeProperty] = contentType - }; - - return textBox; - } - } -} diff --git a/src/Android/Avalonia.AndroidTestApplication/Properties/AndroidManifest.xml b/src/Android/Avalonia.AndroidTestApplication/Properties/AndroidManifest.xml deleted file mode 100644 index ad8134f628..0000000000 --- a/src/Android/Avalonia.AndroidTestApplication/Properties/AndroidManifest.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/Android/Avalonia.AndroidTestApplication/Resources/AboutResources.txt b/src/Android/Avalonia.AndroidTestApplication/Resources/AboutResources.txt deleted file mode 100644 index 194ae28a59..0000000000 --- a/src/Android/Avalonia.AndroidTestApplication/Resources/AboutResources.txt +++ /dev/null @@ -1,50 +0,0 @@ -Images, layout descriptions, binary blobs and string dictionaries can be included -in your application as resource files. Various Android APIs are designed to -operate on the resource IDs instead of dealing with images, strings or binary blobs -directly. - -For example, a sample Android app that contains a user interface layout (main.xml), -an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png) -would keep its resources in the "Resources" directory of the application: - -Resources/ - drawable-hdpi/ - icon.png - - drawable-ldpi/ - icon.png - - drawable-mdpi/ - icon.png - - layout/ - main.xml - - values/ - strings.xml - -In order to get the build system to recognize Android resources, set the build action to -"AndroidResource". The native Android APIs do not operate directly with filenames, but -instead operate on resource IDs. When you compile an Android application that uses resources, -the build system will package the resources for distribution and generate a class called -"Resource" that contains the tokens for each one of the resources included. For example, -for the above Resources layout, this is what the Resource class would expose: - -public class Resource { - public class drawable { - public const int icon = 0x123; - } - - public class layout { - public const int main = 0x456; - } - - public class strings { - public const int first_string = 0xabc; - public const int second_string = 0xbcd; - } -} - -You would then use R.drawable.icon to reference the drawable/icon.png file, or Resource.layout.main -to reference the layout/main.xml file, or Resource.strings.first_string to reference the first -string in the dictionary file values/strings.xml. \ No newline at end of file diff --git a/src/Android/Avalonia.AndroidTestApplication/Resources/drawable/Icon.png b/src/Android/Avalonia.AndroidTestApplication/Resources/drawable/Icon.png deleted file mode 100644 index 8074c4c571b8cd19e27f4ee5545df367420686d7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4147 zcmV-35X|q1P)OwvMs$Q8_8nISM!^>PxsujeDCl4&hPxrxkp%Qc^^|l zp6LqAcf3zf1H4aA1Gv-O6ha)ktct9Y+VA@N^9i;p0H%6v>ZJZYQ`zEa396z-gi{r_ zDz)D=vgRv62GCVeRjK{15j7V@v6|2nafFX6W7z2j1_T0a zLyT3pGTubf1lB5)32>bl0*BflrA!$|_(WD2)iJIfV}37=ZKAC zSe3boYtQ=;o0i>)RtBvsI#iT{0!oF1VFeW`jDjF2Q4aE?{pGCAd>o8Kg#neIh*AMY zLl{;F!vLiem7s*x0<9FKAd6LoPz3~G32P+F+cuGOJ5gcC@pU_?C2fmix7g2)SUaQO$NS07~H)#fn!Q<}KQWtX}wW`g2>cMld+`7Rxgq zChaey66SG560JhO66zA!;sK1cWa2AG$9k~VQY??6bOmJsw9@3uL*z;WWa7(Nm{^TA zilc?y#N9O3LcTo2c)6d}SQl-v-pE4^#wb=s(RxaE28f3FQW(yp$ulG9{KcQ7r>7mQ zE!HYxUYex~*7IinL+l*>HR*UaD;HkQhkL(5I@UwN%Wz504M^d!ylo>ANvKPF_TvA< zkugG5;F6x}$s~J8cnev->_(Ic7%lGQgUi3n#XVo36lUpcS9s z)ympRr7}@|6WF)Ae;D{owN1;aZSR50al9h~?-WhbtKK%bDd zhML131oi1Bu1&Qb$Cp199LJ#;j5d|FhW8_i4KO1OI>}J^p2DfreMSVGY9aFlr&90t zyI2FvxQiKMFviSQeP$Ixh#70qj5O%I+O_I2t2XHWqmh2!1~tHpN3kA4n=1iHj?`@c<~3q^X6_Q$AqTDjBU`|!y<&lkqL|m5tG(b z8a!z&j^m(|;?SW(l*?tZ*{m2H9d&3jqBtXh>O-5e4Qp-W*a5=2NL&Oi62BUM)>zE3 zbSHb>aU3d@3cGggA`C-PsT9^)oy}%dHCaO~nwOrm5E54=aDg(&HR4S23Oa#-a^=}w%g?ZP-1iq8PSjE8jYaGZu z$I)?YN8he?F9>)2d$G6a*zm0XB*Rf&gZAjq(8l@CUDSY1tB#!i> zW$VfG%#SYSiZ};)>pHA`qlfDTEYQEwN6>NNEp+uxuqx({Fgr zjI@!4xRc?vk^9+~eU|mzH__dCDI=xb{Cd}4bELS9xRaS!*FXMwtMR-RR%SLMh0Cjl zencr8#Su<4(%}$yGVBU-HX{18v=yPH*+%^Vtknc>2A;%-~DrYFx^3XfuVgvZ{#1tA== zm3>IzAM2{3Iv_d1XG{P6^tN3|PkJMnjs&CWN7%7_CmjoVakUhsa&dMv==2~^ri?&x zVdv*rnfVyM+I1^Kg*S=23mR@+0T9BWFZUu~@toA8d)fw6be=`Yb6DSX6D?jB%2YT~ z*aHjtIOozfMhA!Jd*?u5_n!SnX>vX`=Ti-1HA4RiE>eI3vTn zz+>Ccf0HX6Ans-ebOB>RJST-Cyr#4XAk+mAlJgdQnoE{^iIN)OcYFSpgJUmXtl@tT z-^ZuUeSj5hSFrQwqX>~EtZ*{>Gi8Bu9_|o06oNtaXP?E936!a@DsvS*tsB@fa6kEA z5GkjwmH?EgpiG&itsB_Tb1NxtFnvxh_s@9KYX1Sttf?AlI~)z zT=6Y7ulx=}<8Scr_UqU-_z)5gPo%050PsbM*ZLno;_-ow&k?FZJtYmb2hPA$LkP)8 z=^d0Q6PImh6Y|QT?{grxj)S=uBKvY2EQUbm@ns9^yKiP~$DcD)c$5Em`zDSScH%iH zVov&m=cMo`1tYwA=!a}vb_ef_{)Q2?FUqn>BR$6phXQRv^1%=YfyE-F$AR4Q?9D!f zCzB^^#td~4u&l~l#rp2QLfe3+_ub9@+|x+m;=2(sQ`s%gO|j$XBb>A7Q(UydipiMw%igcweV#Cr~SP);q>w`bxts_4} znKHg?X==JDkQl3Y>Ckt%`s{n?Nq-1Fw5~%Mq$CAsi-`yu_bKm zxs#QdE7&vgJD%M84f4SNzSDv)S|V?|$!d5a#lhT5>>YWE4NGqa9-fbmV$=)@k&32kdEYetna>=j@0>V8+wRsL;po!3ivVwh<9tn z2S<1u9DAAQ>x1Sn=fk`)At|quvleV($B|#Kap_lB-F^*yV=wZ{9baUu(uXfokr95^ zA*!*W=5a>$2Ps`-F^+qRQT^{*cN>vipT*4!r#p%{(#I7s z0NN94*q?ib$KJjfDI_sjHNdmEVp5wB&j54O#VoFqBwy)gfA$%)4d_X4q${L9Xom2R3xy&ZBSNgt4a1d7K^CDWa9r zVb-_52m}Vp)`9;ZSKd#|U4ZYj5}Gp49{4utST|=c`~(#>KHF6}CCov1iHYw zt{bWo)A@yF2$~c(nR$rSAaFQ$(Wh{vkG1AlutDMw=mM`C`T=X&|Ad9fb5Od}ROt1z zOpczHqrb4Jo^rSCiW#&o(m7jFamnrsTpQb;*h4o8r#$aZ}2RaT-x2u^^ z%u@YyIv$U^u~@9(XGbSwU@fk6SikH>j+D1jQrYTKGJpW%vUT{!d}7THI5&Sa?~MKy zS0-mvMl+BOcroEJ@hN!2H_?coTEJ5Q<;Nd?yx;eIj4{$$E2?YUO|NtNPJ-PdDf;s} zab;}Mz0kbOI}5*w@3gROcnl#5)wQnEhDBfn!Xhy`u>C}*E~vWpO^HS)FC>8^umI=+ z&H;LW6w#;EF`}vQd_9Muru`KnQVPI9U?(sD)&Dg-0j3#(!fNKVZ_GoYH{la~d*1Yh$TI-TL>mI4vpNb@sU2=IZ8vL%AXUx0 zz{K0|nK(yizLHaeW#ZhRfQXoK^}1$=$#1{Yn002ovPDHLkV1n#w+^+xt diff --git a/src/Android/Avalonia.AndroidTestApplication/Resources/values/Strings.xml b/src/Android/Avalonia.AndroidTestApplication/Resources/values/Strings.xml deleted file mode 100644 index c8dca13c35..0000000000 --- a/src/Android/Avalonia.AndroidTestApplication/Resources/values/Strings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - Hello World, Click Me! - Avalonia.AndroidTestApplication - \ No newline at end of file From a71ddcc8700cdd9a589b2385c0e801d55df0928c Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 14 Apr 2022 23:50:16 +0200 Subject: [PATCH 140/416] Fix project files broken by #5831. --- .../ControlCatalog.Android.csproj | 44 +-------------- .../ControlCatalog.iOS.csproj | 53 +------------------ 2 files changed, 4 insertions(+), 93 deletions(-) diff --git a/samples/ControlCatalog.Android/ControlCatalog.Android.csproj b/samples/ControlCatalog.Android/ControlCatalog.Android.csproj index 8d2ead054c..04c67e84e8 100644 --- a/samples/ControlCatalog.Android/ControlCatalog.Android.csproj +++ b/samples/ControlCatalog.Android/ControlCatalog.Android.csproj @@ -39,49 +39,9 @@ - + - - {7b92af71-6287-4693-9dcb-bd5b6e927e23} - Avalonia.Android - - - {b09b78d8-9b26-48b0-9149-d64a2f120f3f} - Avalonia.Base - - - {d2221c82-4a25-4583-9b43-d791e3f6820c} - Avalonia.Controls - - - {7062ae20-5dcc-4442-9645-8195bdece63e} - Avalonia.Diagnostics - - - {3e10a5fa-e8da-48b1-ad44-6a5b6cb7750f} - Avalonia.Themes.Default - - - {3e53a01a-b331-47f3-b828-4a5717e77a24} - Avalonia.Markup.Xaml - - - {6417e941-21bc-467b-a771-0de389353ce6} - Avalonia.Markup - - - {7d2d3083-71dd-4cc9-8907-39a0d86fb322} - Avalonia.Skia - - - {d0a739b9-3c68-4ba6-a328-41606954b6bd} - ControlCatalog - - - - - - + \ No newline at end of file diff --git a/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj b/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj index d4f10b3580..513ac44f83 100644 --- a/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj +++ b/samples/ControlCatalog.iOS/ControlCatalog.iOS.csproj @@ -7,59 +7,10 @@ True iossimulator-x64 - + - - - - - - - - - {4488ad85-1495-4809-9aa4-ddfe0a48527e} - Avalonia.iOS - - - {3E53A01A-B331-47F3-B828-4A5717E77A24} - Avalonia.Markup.Xaml - - - {6417E941-21BC-467B-A771-0DE389353CE6} - Avalonia.Markup - - - {B09B78D8-9B26-48B0-9149-D64A2F120F3F} - Avalonia.Base - - - {D2221C82-4A25-4583-9B43-D791E3F6820C} - Avalonia.Controls - - - {7062AE20-5DCC-4442-9645-8195BDECE63E} - Avalonia.Diagnostics - - - {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F} - Avalonia.Themes.Default - - - {7d2d3083-71dd-4cc9-8907-39a0d86fb322} - Avalonia.Skia - - - {d0a739b9-3c68-4ba6-a328-41606954b6bd} - ControlCatalog - - - - - - - - + \ No newline at end of file From c1644eb21455bb2658ed67c52c5425139c0cb672 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Apr 2022 22:11:48 +0000 Subject: [PATCH 141/416] Bump async in /src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp Bumps [async](https://github.com/caolan/async) from 2.6.3 to 2.6.4. - [Release notes](https://github.com/caolan/async/releases) - [Changelog](https://github.com/caolan/async/blob/v2.6.4/CHANGELOG.md) - [Commits](https://github.com/caolan/async/compare/v2.6.3...v2.6.4) --- updated-dependencies: - dependency-name: async dependency-type: indirect ... Signed-off-by: dependabot[bot] --- .../Remote/HtmlTransport/webapp/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/package-lock.json b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/package-lock.json index 08ede477c3..403bb5a59a 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/package-lock.json +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/package-lock.json @@ -377,9 +377,9 @@ "dev": true }, "async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", "dev": true, "requires": { "lodash": "^4.17.14" From 98ba0f529bcbf708276b6fda48e4130e10a76751 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 15 Apr 2022 10:44:54 +0200 Subject: [PATCH 142/416] Initial implementation of nested styles. No XAML/parser support yet. --- src/Avalonia.Base/Styling/ChildSelector.cs | 4 +- .../Styling/DescendentSelector.cs | 4 +- src/Avalonia.Base/Styling/NestingSelector.cs | 29 ++++ src/Avalonia.Base/Styling/NotSelector.cs | 4 +- src/Avalonia.Base/Styling/NthChildSelector.cs | 2 +- src/Avalonia.Base/Styling/OrSelector.cs | 4 +- .../Styling/PropertyEqualsSelector.cs | 2 +- src/Avalonia.Base/Styling/Selector.cs | 31 ++-- src/Avalonia.Base/Styling/Selectors.cs | 9 ++ src/Avalonia.Base/Styling/Style.cs | 22 ++- src/Avalonia.Base/Styling/StyleChildren.cs | 29 ++++ src/Avalonia.Base/Styling/TemplateSelector.cs | 4 +- .../Styling/TypeNameAndClassSelector.cs | 2 +- .../Styling/SelectorTests_Nesting.cs | 132 ++++++++++++++++++ 14 files changed, 253 insertions(+), 25 deletions(-) create mode 100644 src/Avalonia.Base/Styling/NestingSelector.cs create mode 100644 src/Avalonia.Base/Styling/StyleChildren.cs create mode 100644 tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Nesting.cs diff --git a/src/Avalonia.Base/Styling/ChildSelector.cs b/src/Avalonia.Base/Styling/ChildSelector.cs index 5c92182b80..8675ca8820 100644 --- a/src/Avalonia.Base/Styling/ChildSelector.cs +++ b/src/Avalonia.Base/Styling/ChildSelector.cs @@ -37,13 +37,13 @@ namespace Avalonia.Styling return _selectorString; } - protected override SelectorMatch Evaluate(IStyleable control, bool subscribe) + protected override SelectorMatch Evaluate(IStyleable control, IStyle? parent, bool subscribe) { var controlParent = ((ILogical)control).LogicalParent; if (controlParent != null) { - var parentMatch = _parent.Match((IStyleable)controlParent, subscribe); + var parentMatch = _parent.Match((IStyleable)controlParent, parent, subscribe); if (parentMatch.Result == SelectorMatchResult.Sometimes) { diff --git a/src/Avalonia.Base/Styling/DescendentSelector.cs b/src/Avalonia.Base/Styling/DescendentSelector.cs index dde88b3436..b164ad1c69 100644 --- a/src/Avalonia.Base/Styling/DescendentSelector.cs +++ b/src/Avalonia.Base/Styling/DescendentSelector.cs @@ -35,7 +35,7 @@ namespace Avalonia.Styling return _selectorString; } - protected override SelectorMatch Evaluate(IStyleable control, bool subscribe) + protected override SelectorMatch Evaluate(IStyleable control, IStyle? parent, bool subscribe) { var c = (ILogical)control; var descendantMatches = new OrActivatorBuilder(); @@ -46,7 +46,7 @@ namespace Avalonia.Styling if (c is IStyleable) { - var match = _parent.Match((IStyleable)c, subscribe); + var match = _parent.Match((IStyleable)c, parent, subscribe); if (match.Result == SelectorMatchResult.Sometimes) { diff --git a/src/Avalonia.Base/Styling/NestingSelector.cs b/src/Avalonia.Base/Styling/NestingSelector.cs new file mode 100644 index 0000000000..1be54dea3c --- /dev/null +++ b/src/Avalonia.Base/Styling/NestingSelector.cs @@ -0,0 +1,29 @@ +using System; + +namespace Avalonia.Styling +{ + /// + /// The `&` nesting style selector. + /// + internal class NestingSelector : Selector + { + public override bool InTemplate => false; + public override bool IsCombinator => false; + public override Type? TargetType => null; + + public override string ToString() => "&"; + + protected override SelectorMatch Evaluate(IStyleable control, IStyle? parent, bool subscribe) + { + if (parent is Style s && s.Selector is Selector selector) + { + return selector.Match(control, null, subscribe); + } + + throw new InvalidOperationException( + "Nesting selector was specified but cannot determine parent selector."); + } + + protected override Selector? MovePrevious() => null; + } +} diff --git a/src/Avalonia.Base/Styling/NotSelector.cs b/src/Avalonia.Base/Styling/NotSelector.cs index ab4e9d5d7f..c4beca6b9e 100644 --- a/src/Avalonia.Base/Styling/NotSelector.cs +++ b/src/Avalonia.Base/Styling/NotSelector.cs @@ -45,9 +45,9 @@ namespace Avalonia.Styling return _selectorString; } - protected override SelectorMatch Evaluate(IStyleable control, bool subscribe) + protected override SelectorMatch Evaluate(IStyleable control, IStyle? parent, bool subscribe) { - var innerResult = _argument.Match(control, subscribe); + var innerResult = _argument.Match(control, parent, subscribe); switch (innerResult.Result) { diff --git a/src/Avalonia.Base/Styling/NthChildSelector.cs b/src/Avalonia.Base/Styling/NthChildSelector.cs index aff34ea17c..cbb5e64772 100644 --- a/src/Avalonia.Base/Styling/NthChildSelector.cs +++ b/src/Avalonia.Base/Styling/NthChildSelector.cs @@ -48,7 +48,7 @@ namespace Avalonia.Styling public int Step { get; } public int Offset { get; } - protected override SelectorMatch Evaluate(IStyleable control, bool subscribe) + protected override SelectorMatch Evaluate(IStyleable control, IStyle? parent, bool subscribe) { if (!(control is ILogical logical)) { diff --git a/src/Avalonia.Base/Styling/OrSelector.cs b/src/Avalonia.Base/Styling/OrSelector.cs index 3d6db9b01e..a34712caaf 100644 --- a/src/Avalonia.Base/Styling/OrSelector.cs +++ b/src/Avalonia.Base/Styling/OrSelector.cs @@ -65,14 +65,14 @@ namespace Avalonia.Styling return _selectorString; } - protected override SelectorMatch Evaluate(IStyleable control, bool subscribe) + protected override SelectorMatch Evaluate(IStyleable control, IStyle? parent, bool subscribe) { var activators = new OrActivatorBuilder(); var neverThisInstance = false; foreach (var selector in _selectors) { - var match = selector.Match(control, subscribe); + var match = selector.Match(control, parent, subscribe); switch (match.Result) { diff --git a/src/Avalonia.Base/Styling/PropertyEqualsSelector.cs b/src/Avalonia.Base/Styling/PropertyEqualsSelector.cs index 1cd1a650ef..34474fb7ab 100644 --- a/src/Avalonia.Base/Styling/PropertyEqualsSelector.cs +++ b/src/Avalonia.Base/Styling/PropertyEqualsSelector.cs @@ -74,7 +74,7 @@ namespace Avalonia.Styling } /// - protected override SelectorMatch Evaluate(IStyleable control, bool subscribe) + protected override SelectorMatch Evaluate(IStyleable control, IStyle? parent, bool subscribe) { if (subscribe) { diff --git a/src/Avalonia.Base/Styling/Selector.cs b/src/Avalonia.Base/Styling/Selector.cs index 0740e0f891..008ce7f3a5 100644 --- a/src/Avalonia.Base/Styling/Selector.cs +++ b/src/Avalonia.Base/Styling/Selector.cs @@ -33,22 +33,25 @@ namespace Avalonia.Styling /// Tries to match the selector with a control. /// /// The control. + /// + /// The parent style, if the style containing the selector is a nested style. + /// /// /// Whether the match should subscribe to changes in order to track the match over time, /// or simply return an immediate result. /// /// A . - public SelectorMatch Match(IStyleable control, bool subscribe = true) + public SelectorMatch Match(IStyleable control, IStyle? parent = null, bool subscribe = true) { // First match the selector until a combinator is found. Selectors are stored from // right-to-left, so MatchUntilCombinator reverses this order because the type selector // will be on the left. - var match = MatchUntilCombinator(control, this, subscribe, out var combinator); + var match = MatchUntilCombinator(control, this, parent, subscribe, out var combinator); // If the pre-combinator selector matches, we can now match the combinator, if any. if (match.IsMatch && combinator is object) { - match = match.And(combinator.Match(control, subscribe)); + match = match.And(combinator.Match(control, parent, subscribe)); // If we have a combinator then we can never say that we always match a control of // this type, because by definition the combinator matches on things outside of the @@ -68,12 +71,15 @@ namespace Avalonia.Styling /// Evaluates the selector for a match. /// /// The control. + /// + /// The parent style, if the style containing the selector is a nested style. + /// /// /// Whether the match should subscribe to changes in order to track the match over time, /// or simply return an immediate result. /// /// A . - protected abstract SelectorMatch Evaluate(IStyleable control, bool subscribe); + protected abstract SelectorMatch Evaluate(IStyleable control, IStyle? parent, bool subscribe); /// /// Moves to the previous selector. @@ -83,13 +89,18 @@ namespace Avalonia.Styling private static SelectorMatch MatchUntilCombinator( IStyleable control, Selector start, + IStyle? parent, bool subscribe, out Selector? combinator) { combinator = null; var activators = new AndActivatorBuilder(); - var result = Match(control, start, subscribe, ref activators, ref combinator); + var foundNested = false; + var result = Match(control, start, parent, subscribe, ref activators, ref combinator, ref foundNested); + + if (parent is not null && !foundNested) + throw new InvalidOperationException("Nesting selector '&' must appear in child selector."); return result == SelectorMatchResult.Sometimes ? new SelectorMatch(activators.Get()) : @@ -99,9 +110,11 @@ namespace Avalonia.Styling private static SelectorMatchResult Match( IStyleable control, Selector selector, + IStyle? parent, bool subscribe, ref AndActivatorBuilder activators, - ref Selector? combinator) + ref Selector? combinator, + ref bool foundNested) { var previous = selector.MovePrevious(); @@ -110,7 +123,7 @@ namespace Avalonia.Styling // opportunity to exit early. if (previous != null && !previous.IsCombinator) { - var previousMatch = Match(control, previous, subscribe, ref activators, ref combinator); + var previousMatch = Match(control, previous, parent, subscribe, ref activators, ref combinator, ref foundNested); if (previousMatch < SelectorMatchResult.Sometimes) { @@ -118,8 +131,10 @@ namespace Avalonia.Styling } } + foundNested |= selector is NestingSelector; + // Match this selector. - var match = selector.Evaluate(control, subscribe); + var match = selector.Evaluate(control, parent, subscribe); if (!match.IsMatch) { diff --git a/src/Avalonia.Base/Styling/Selectors.cs b/src/Avalonia.Base/Styling/Selectors.cs index 7c66469cf1..a036c140c2 100644 --- a/src/Avalonia.Base/Styling/Selectors.cs +++ b/src/Avalonia.Base/Styling/Selectors.cs @@ -109,6 +109,15 @@ namespace Avalonia.Styling } } + public static Selector Nesting(this Selector? previous) + { + if (previous is not null) + throw new InvalidOperationException( + "Nesting selector '&' must appear at the start of the style selector."); + + return new NestingSelector(); + } + /// /// Returns a selector which inverts the results of selector argument. /// diff --git a/src/Avalonia.Base/Styling/Style.cs b/src/Avalonia.Base/Styling/Style.cs index 00819ef7be..a3a7ea29d3 100644 --- a/src/Avalonia.Base/Styling/Style.cs +++ b/src/Avalonia.Base/Styling/Style.cs @@ -4,8 +4,6 @@ using Avalonia.Animation; using Avalonia.Controls; using Avalonia.Metadata; -#nullable enable - namespace Avalonia.Styling { /// @@ -14,6 +12,7 @@ namespace Avalonia.Styling public class Style : AvaloniaObject, IStyle, IResourceProvider { private IResourceHost? _owner; + private StyleChildren? _children; private IResourceDictionary? _resources; private List? _setters; private List? _animations; @@ -34,6 +33,14 @@ namespace Avalonia.Styling Selector = selector(null); } + /// + /// Gets the children of the style. + /// + public IList Children => _children ??= new(this); + + /// + /// Gets the or Application that hosts the style. + /// public IResourceHost? Owner { get => _owner; @@ -47,6 +54,11 @@ namespace Avalonia.Styling } } + /// + /// Gets the parent style if this style is hosted in a collection. + /// + public Style? Parent { get; private set; } + /// /// Gets or sets a dictionary of style resources. /// @@ -90,7 +102,7 @@ namespace Avalonia.Styling public IList Animations => _animations ??= new List(); bool IResourceNode.HasResources => _resources?.Count > 0; - IReadOnlyList IStyle.Children => Array.Empty(); + IReadOnlyList IStyle.Children => (IReadOnlyList?)_children ?? Array.Empty(); public event EventHandler? OwnerChanged; @@ -98,7 +110,7 @@ namespace Avalonia.Styling { target = target ?? throw new ArgumentNullException(nameof(target)); - var match = Selector is object ? Selector.Match(target) : + var match = Selector is object ? Selector.Match(target, Parent) : target == host ? SelectorMatch.AlwaysThisInstance : SelectorMatch.NeverThisInstance; if (match.IsMatch && (_setters is object || _animations is object)) @@ -156,5 +168,7 @@ namespace Avalonia.Styling _resources?.RemoveOwner(owner); } } + + internal void SetParent(Style? parent) => Parent = parent; } } diff --git a/src/Avalonia.Base/Styling/StyleChildren.cs b/src/Avalonia.Base/Styling/StyleChildren.cs new file mode 100644 index 0000000000..21ac3c4072 --- /dev/null +++ b/src/Avalonia.Base/Styling/StyleChildren.cs @@ -0,0 +1,29 @@ +using System.Collections.ObjectModel; + +namespace Avalonia.Styling +{ + internal class StyleChildren : Collection + { + private readonly Style _owner; + + public StyleChildren(Style owner) => _owner = owner; + + protected override void InsertItem(int index, IStyle item) + { + base.InsertItem(index, item); + (item as Style)?.SetParent(_owner); + } + + protected override void RemoveItem(int index) + { + (Items[index] as Style)?.SetParent(null); + base.RemoveItem(index); + } + + protected override void SetItem(int index, IStyle item) + { + base.SetItem(index, item); + (item as Style)?.SetParent(_owner); + } + } +} diff --git a/src/Avalonia.Base/Styling/TemplateSelector.cs b/src/Avalonia.Base/Styling/TemplateSelector.cs index e8051efa6d..6f0f9e0900 100644 --- a/src/Avalonia.Base/Styling/TemplateSelector.cs +++ b/src/Avalonia.Base/Styling/TemplateSelector.cs @@ -36,7 +36,7 @@ namespace Avalonia.Styling return _selectorString; } - protected override SelectorMatch Evaluate(IStyleable control, bool subscribe) + protected override SelectorMatch Evaluate(IStyleable control, IStyle? parent, bool subscribe) { var templatedParent = control.TemplatedParent as IStyleable; @@ -45,7 +45,7 @@ namespace Avalonia.Styling return SelectorMatch.NeverThisInstance; } - return _parent.Match(templatedParent, subscribe); + return _parent.Match(templatedParent, parent, subscribe); } protected override Selector? MovePrevious() => null; diff --git a/src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs b/src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs index ef48c4a8cd..ae9b14635f 100644 --- a/src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs +++ b/src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs @@ -94,7 +94,7 @@ namespace Avalonia.Styling } /// - protected override SelectorMatch Evaluate(IStyleable control, bool subscribe) + protected override SelectorMatch Evaluate(IStyleable control, IStyle? parent, bool subscribe) { if (TargetType != null) { diff --git a/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Nesting.cs b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Nesting.cs new file mode 100644 index 0000000000..cee7e748ba --- /dev/null +++ b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Nesting.cs @@ -0,0 +1,132 @@ +using System; +using Avalonia.Controls; +using Avalonia.Styling; +using Avalonia.Styling.Activators; +using Xunit; + +namespace Avalonia.Base.UnitTests.Styling +{ + public class SelectorTests_Nesting + { + [Fact] + public void Parent_Selector_Doesnt_Match_OfType() + { + var control = new Control2(); + Style nested; + var parent = new Style(x => x.OfType()) + { + Children = + { + (nested = new Style(x => x.Nesting().Class("foo"))), + } + }; + + var match = nested.Selector.Match(control, parent); + Assert.Equal(SelectorMatchResult.NeverThisType, match.Result); + } + + [Fact] + public void Nested_Class_Selector() + { + var control = new Control1 { Classes = { "foo" } }; + Style nested; + var parent = new Style(x => x.OfType()) + { + Children = + { + (nested = new Style(x => x.Nesting().Class("foo"))), + } + }; + + var match = nested.Selector.Match(control, parent); + Assert.Equal(SelectorMatchResult.Sometimes, match.Result); + + var sink = new ActivatorSink(match.Activator); + + Assert.True(sink.Active); + control.Classes.Clear(); + Assert.False(sink.Active); + } + + [Fact] + public void Nesting_With_No_Parent_Style_Fails() + { + var control = new Control1(); + var style = new Style(x => x.Nesting().OfType()); + + Assert.Throws(() => style.Selector.Match(control, null)); + } + + [Fact] + public void Nesting_With_No_Parent_Selector_Fails() + { + var control = new Control1(); + Style nested; + var parent = new Style + { + Children = + { + (nested = new Style(x => x.Nesting().Class("foo"))), + } + }; + + Assert.Throws(() => nested.Selector.Match(control, parent)); + } + + [Fact] + public void Nesting_Must_Appear_At_Start_Of_Selector() + { + var control = new Control1(); + Assert.Throws(() => new Style(x => x.OfType().Nesting())); + } + + [Fact] + public void Nesting_Must_Appear() + { + var control = new Control1(); + Style nested; + var parent = new Style + { + Children = + { + (nested = new Style(x => x.OfType().Class("foo"))), + } + }; + + Assert.Throws(() => nested.Selector.Match(control, parent)); + } + + [Fact] + public void Nesting_Must_Appear_In_All_Or_Arguments() + { + var control = new Control1(); + Style nested; + var parent = new Style(x => x.OfType()) + { + Children = + { + (nested = new Style(x => Selectors.Or( + x.Nesting().Class("foo"), + x.Class("bar")))) + } + }; + + Assert.Throws(() => nested.Selector.Match(control, parent)); + } + + public class Control1 : Control + { + } + + public class Control2 : Control + { + } + + private class ActivatorSink : IStyleActivatorSink + { + public ActivatorSink(IStyleActivator source) => source.Subscribe(this); + public bool Active { get; private set; } + public void OnNext(bool value, int tag) => Active = value; + } + } +} From a506737a0f72ee1e56face79bfdbdd6e1bb0ffdd Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 15 Apr 2022 11:16:12 +0200 Subject: [PATCH 143/416] Parse nesting selector. --- .../Markup/Parsers/SelectorGrammar.cs | 30 +++- .../Parsers/SelectorGrammarTests.cs | 139 ++++++++++++++++++ 2 files changed, 162 insertions(+), 7 deletions(-) diff --git a/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs b/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs index a9fc18474c..6562afc7ff 100644 --- a/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs +++ b/src/Markup/Avalonia.Markup/Markup/Parsers/SelectorGrammar.cs @@ -46,7 +46,7 @@ namespace Avalonia.Markup.Parsers switch (state) { case State.Start: - state = ParseStart(ref r); + (state, syntax) = ParseStart(ref r); break; case State.Middle: (state, syntax) = ParseMiddle(ref r, end); @@ -93,27 +93,31 @@ namespace Avalonia.Markup.Parsers return selector; } - private static State ParseStart(ref CharacterReader r) + private static (State, ISyntax?) ParseStart(ref CharacterReader r) { r.SkipWhitespace(); if (r.End) { - return State.End; + return (State.End, null); } if (r.TakeIf(':')) { - return State.Colon; + return (State.Colon, null); } else if (r.TakeIf('.')) { - return State.Class; + return (State.Class, null); } else if (r.TakeIf('#')) { - return State.Name; + return (State.Name, null); + } + else if (r.TakeIf('&')) + { + return (State.CanHaveType, new NestingSyntax()); } - return State.TypeName; + return (State.TypeName, null); } private static (State, ISyntax?) ParseMiddle(ref CharacterReader r, char? end) @@ -142,6 +146,10 @@ namespace Avalonia.Markup.Parsers { return (State.Start, new CommaSyntax()); } + else if (r.TakeIf('&')) + { + return (State.CanHaveType, new NestingSyntax()); + } else if (end.HasValue && !r.End && r.Peek == end.Value) { return (State.End, null); @@ -635,5 +643,13 @@ namespace Avalonia.Markup.Parsers return obj is CommaSyntax or; } } + + public class NestingSyntax : ISyntax + { + public override bool Equals(object? obj) + { + return obj is NestingSyntax; + } + } } } diff --git a/tests/Avalonia.Markup.UnitTests/Parsers/SelectorGrammarTests.cs b/tests/Avalonia.Markup.UnitTests/Parsers/SelectorGrammarTests.cs index 6fbf024ff1..5c59101307 100644 --- a/tests/Avalonia.Markup.UnitTests/Parsers/SelectorGrammarTests.cs +++ b/tests/Avalonia.Markup.UnitTests/Parsers/SelectorGrammarTests.cs @@ -469,6 +469,145 @@ namespace Avalonia.Markup.UnitTests.Parsers result); } + [Fact] + public void Nesting_Class() + { + var result = SelectorGrammar.Parse("&.foo"); + + Assert.Equal( + new SelectorGrammar.ISyntax[] + { + new SelectorGrammar.NestingSyntax(), + new SelectorGrammar.ClassSyntax { Class = "foo" }, + }, + result); + } + + [Fact] + public void Nesting_Child_Class() + { + var result = SelectorGrammar.Parse("& > .foo"); + + Assert.Equal( + new SelectorGrammar.ISyntax[] + { + new SelectorGrammar.NestingSyntax(), + new SelectorGrammar.ChildSyntax { }, + new SelectorGrammar.ClassSyntax { Class = "foo" }, + }, + result); + } + + [Fact] + public void Nesting_Descendant_Class() + { + var result = SelectorGrammar.Parse("& .foo"); + + Assert.Equal( + new SelectorGrammar.ISyntax[] + { + new SelectorGrammar.NestingSyntax(), + new SelectorGrammar.DescendantSyntax { }, + new SelectorGrammar.ClassSyntax { Class = "foo" }, + }, + result); + } + + [Fact] + public void Nesting_Template_Class() + { + var result = SelectorGrammar.Parse("& /template/ .foo"); + + Assert.Equal( + new SelectorGrammar.ISyntax[] + { + new SelectorGrammar.NestingSyntax(), + new SelectorGrammar.TemplateSyntax { }, + new SelectorGrammar.ClassSyntax { Class = "foo" }, + }, + result); + } + + [Fact] + public void OfType_Template_Nesting() + { + var result = SelectorGrammar.Parse("Button /template/ &"); + + Assert.Equal( + new SelectorGrammar.ISyntax[] + { + new SelectorGrammar.OfTypeSyntax { TypeName = "Button" }, + new SelectorGrammar.TemplateSyntax { }, + new SelectorGrammar.NestingSyntax(), + }, + result); + } + + [Fact] + public void Nesting_Property() + { + var result = SelectorGrammar.Parse("&[Foo=bar]"); + + Assert.Equal( + new SelectorGrammar.ISyntax[] + { + new SelectorGrammar.NestingSyntax(), + new SelectorGrammar.PropertySyntax { Property = "Foo", Value = "bar" }, + }, + result); + } + + [Fact] + public void Not_Nesting() + { + var result = SelectorGrammar.Parse(":not(&)"); + + Assert.Equal( + new SelectorGrammar.ISyntax[] + { + new SelectorGrammar.NotSyntax + { + Argument = new[] { new SelectorGrammar.NestingSyntax() }, + } + }, + result); + } + + + [Fact] + public void Nesting_NthChild() + { + var result = SelectorGrammar.Parse("&:nth-child(2n+1)"); + + Assert.Equal( + new SelectorGrammar.ISyntax[] + { + new SelectorGrammar.NestingSyntax(), + new SelectorGrammar.NthChildSyntax() + { + Step = 2, + Offset = 1 + } + }, + result); + } + + [Fact] + public void Nesting_Comma_Nesting_Class() + { + var result = SelectorGrammar.Parse("&, &.foo"); + + Assert.Equal( + new SelectorGrammar.ISyntax[] + { + new SelectorGrammar.NestingSyntax(), + new SelectorGrammar.CommaSyntax(), + new SelectorGrammar.NestingSyntax(), + new SelectorGrammar.ClassSyntax { Class = "foo" }, + }, + result); + } + [Fact] public void Namespace_Alone_Fails() { From 3b24669d084c1801c3f94eb294d7e95ba2194bfc Mon Sep 17 00:00:00 2001 From: Steven He Date: Fri, 15 Apr 2022 17:27:39 +0800 Subject: [PATCH 144/416] Add missing props reference to sln --- Avalonia.sln | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Avalonia.sln b/Avalonia.sln index e5baee7911..8e94eb4e63 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -113,6 +113,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1 build\SharedVersion.props = build\SharedVersion.props build\SharpDX.props = build\SharpDX.props build\SkiaSharp.props = build\SkiaSharp.props + build\SourceGenerators.props = build\SourceGenerators.props build\SourceLink.props = build\SourceLink.props build\System.Drawing.Common.props = build\System.Drawing.Common.props build\System.Memory.props = build\System.Memory.props @@ -214,6 +215,7 @@ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.PlatformSupport.UnitTests", "tests\Avalonia.PlatformSupport.UnitTests\Avalonia.PlatformSupport.UnitTests.csproj", "{CE910927-CE5A-456F-BC92-E4C757354A5C}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.SourceGenerator", "src\Avalonia.SourceGenerator\Avalonia.SourceGenerator.csproj", "{CA932DF3-2616-4BF6-8F28-1AD0EC40F1FF}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevAnalyzers", "src\tools\DevAnalyzers\DevAnalyzers.csproj", "{2B390431-288C-435C-BB6B-A374033BD8D1}" EndProject Global From 7418fd7dc39aa91ea70dc4dcc2a4c5246ec71993 Mon Sep 17 00:00:00 2001 From: Steven He Date: Fri, 15 Apr 2022 18:19:07 +0800 Subject: [PATCH 145/416] Apply code review feedbacks --- Avalonia.sln | 2 +- src/Avalonia.Base/Avalonia.Base.csproj | 1 + src/Avalonia.Base/Input/KeyGesture.cs | 12 ++---------- .../Avalonia.DesignerSupport.csproj | 1 + .../Remote/HtmlTransport/HtmlTransport.cs | 14 ++------------ src/Shared/EnumParserHelper.cs | 19 +++++++++++++++++++ 6 files changed, 26 insertions(+), 23 deletions(-) create mode 100644 src/Shared/EnumParserHelper.cs diff --git a/Avalonia.sln b/Avalonia.sln index 8e94eb4e63..1f59a94e2e 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -38,8 +38,8 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{A689DEF5-D50F-4975-8B72-124C9EB54066}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig + src\Shared\EnumParserHelper.cs = src\Shared\EnumParserHelper.cs src\Shared\ModuleInitializer.cs = src\Shared\ModuleInitializer.cs - src\Shared\RawEventGrouping.cs = src\Shared\RawEventGrouping.cs src\Shared\SourceGeneratorAttributes.cs = src\Shared\SourceGeneratorAttributes.cs EndProjectSection EndProject diff --git a/src/Avalonia.Base/Avalonia.Base.csproj b/src/Avalonia.Base/Avalonia.Base.csproj index 8e4755b4b7..f0d54d831a 100644 --- a/src/Avalonia.Base/Avalonia.Base.csproj +++ b/src/Avalonia.Base/Avalonia.Base.csproj @@ -7,6 +7,7 @@ + diff --git a/src/Avalonia.Base/Input/KeyGesture.cs b/src/Avalonia.Base/Input/KeyGesture.cs index 6f302a3ae7..d7fe89afda 100644 --- a/src/Avalonia.Base/Input/KeyGesture.cs +++ b/src/Avalonia.Base/Input/KeyGesture.cs @@ -155,11 +155,7 @@ namespace Avalonia.Input if (s_keySynonyms.TryGetValue(key.ToLower(), out Key rv)) return rv; -#if NETSTANDARD2_0 - return (Key)Enum.Parse(typeof(Key), key, true); -#else - return Enum.Parse(key, true); -#endif + return EnumParserHelper.ParseEnum(key, true); } private static KeyModifiers ParseModifier(ReadOnlySpan modifier) @@ -176,11 +172,7 @@ namespace Avalonia.Input return KeyModifiers.Meta; } -#if NETSTANDARD2_0 - return (KeyModifiers)Enum.Parse(typeof(KeyModifiers), modifier.ToString(), true); -#else - return Enum.Parse(modifier.ToString(), true); -#endif + return EnumParserHelper.ParseEnum(modifier.ToString(), true); } private Key ResolveNumPadOperationKey(Key key) diff --git a/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj b/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj index 0270000d8c..66a7a6389e 100644 --- a/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj +++ b/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj @@ -19,6 +19,7 @@ + diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs index 7965e5d0d8..11a1efb9c7 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs @@ -320,23 +320,13 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport ? null : modifiersText .Split(',') -#if NETSTANDARD2_0 - .Select(x => (InputProtocol.InputModifiers)Enum.Parse( - typeof(InputProtocol.InputModifiers), x, true)) -#else - .Select(x => Enum.Parse(x, true)) -#endif + .Select(x => EnumParserHelper.ParseEnum(x, true)) .ToArray(); private static InputProtocol.MouseButton ParseMouseButton(string buttonText) => string.IsNullOrWhiteSpace(buttonText) ? InputProtocol.MouseButton.None -#if NETSTANDARD2_0 - : (InputProtocol.MouseButton)Enum.Parse( - typeof(InputProtocol.MouseButton), buttonText, true); -#else - : Enum.Parse(buttonText, true); -#endif + : EnumParserHelper.ParseEnum(buttonText, true); private static double ParseDouble(string text) => double.Parse(text, NumberStyles.Float, CultureInfo.InvariantCulture); diff --git a/src/Shared/EnumParserHelper.cs b/src/Shared/EnumParserHelper.cs new file mode 100644 index 0000000000..09d6a0cb7d --- /dev/null +++ b/src/Shared/EnumParserHelper.cs @@ -0,0 +1,19 @@ +using System; + +namespace Avalonia +{ + internal class EnumParserHelper + { +#if NET6_0 + public static T ParseEnum(ReadOnlySpan key, bool ignoreCase) where T : struct + { + return Enum.Parse(key, ignoreCase); + } +#else + public static T ParseEnum(string key, bool ignoreCase) where T : struct + { + return (T)Enum.Parse(typeof(T), key, ignoreCase); + } +#endif + } +} From 67b18b8e3fdb7ed507dbae6f6834faff50a7d4ba Mon Sep 17 00:00:00 2001 From: Steven He Date: Fri, 15 Apr 2022 18:22:10 +0800 Subject: [PATCH 146/416] Rename EnumParserHelper --- Avalonia.sln | 2 +- src/Avalonia.Base/Avalonia.Base.csproj | 2 +- src/Avalonia.Base/Input/KeyGesture.cs | 4 ++-- .../Avalonia.DesignerSupport.csproj | 2 +- .../Remote/HtmlTransport/HtmlTransport.cs | 4 ++-- src/Shared/{EnumParserHelper.cs => EnumHelper.cs} | 6 +++--- 6 files changed, 10 insertions(+), 10 deletions(-) rename src/Shared/{EnumParserHelper.cs => EnumHelper.cs} (51%) diff --git a/Avalonia.sln b/Avalonia.sln index 1f59a94e2e..8a48566653 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -38,7 +38,7 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{A689DEF5-D50F-4975-8B72-124C9EB54066}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig - src\Shared\EnumParserHelper.cs = src\Shared\EnumParserHelper.cs + src\Shared\EnumHelper.cs = src\Shared\EnumHelper.cs src\Shared\ModuleInitializer.cs = src\Shared\ModuleInitializer.cs src\Shared\SourceGeneratorAttributes.cs = src\Shared\SourceGeneratorAttributes.cs EndProjectSection diff --git a/src/Avalonia.Base/Avalonia.Base.csproj b/src/Avalonia.Base/Avalonia.Base.csproj index f0d54d831a..a9a606de8d 100644 --- a/src/Avalonia.Base/Avalonia.Base.csproj +++ b/src/Avalonia.Base/Avalonia.Base.csproj @@ -7,7 +7,7 @@ - + diff --git a/src/Avalonia.Base/Input/KeyGesture.cs b/src/Avalonia.Base/Input/KeyGesture.cs index d7fe89afda..cb6d12ac0d 100644 --- a/src/Avalonia.Base/Input/KeyGesture.cs +++ b/src/Avalonia.Base/Input/KeyGesture.cs @@ -155,7 +155,7 @@ namespace Avalonia.Input if (s_keySynonyms.TryGetValue(key.ToLower(), out Key rv)) return rv; - return EnumParserHelper.ParseEnum(key, true); + return EnumHelper.Parse(key, true); } private static KeyModifiers ParseModifier(ReadOnlySpan modifier) @@ -172,7 +172,7 @@ namespace Avalonia.Input return KeyModifiers.Meta; } - return EnumParserHelper.ParseEnum(modifier.ToString(), true); + return EnumHelper.Parse(modifier.ToString(), true); } private Key ResolveNumPadOperationKey(Key key) diff --git a/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj b/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj index 66a7a6389e..586d13f179 100644 --- a/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj +++ b/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj @@ -19,7 +19,7 @@ - + diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs index 11a1efb9c7..794bc94256 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs @@ -320,13 +320,13 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport ? null : modifiersText .Split(',') - .Select(x => EnumParserHelper.ParseEnum(x, true)) + .Select(x => EnumHelper.Parse(x, true)) .ToArray(); private static InputProtocol.MouseButton ParseMouseButton(string buttonText) => string.IsNullOrWhiteSpace(buttonText) ? InputProtocol.MouseButton.None - : EnumParserHelper.ParseEnum(buttonText, true); + : EnumHelper.Parse(buttonText, true); private static double ParseDouble(string text) => double.Parse(text, NumberStyles.Float, CultureInfo.InvariantCulture); diff --git a/src/Shared/EnumParserHelper.cs b/src/Shared/EnumHelper.cs similarity index 51% rename from src/Shared/EnumParserHelper.cs rename to src/Shared/EnumHelper.cs index 09d6a0cb7d..8511e712fa 100644 --- a/src/Shared/EnumParserHelper.cs +++ b/src/Shared/EnumHelper.cs @@ -2,15 +2,15 @@ namespace Avalonia { - internal class EnumParserHelper + internal class EnumHelper { #if NET6_0 - public static T ParseEnum(ReadOnlySpan key, bool ignoreCase) where T : struct + public static T Parse(ReadOnlySpan key, bool ignoreCase) where T : struct { return Enum.Parse(key, ignoreCase); } #else - public static T ParseEnum(string key, bool ignoreCase) where T : struct + public static T Parse(string key, bool ignoreCase) where T : struct { return (T)Enum.Parse(typeof(T), key, ignoreCase); } From 4f6da322fa4ac370d320f50a84aa9bcb5e5dd405 Mon Sep 17 00:00:00 2001 From: Steven He Date: Fri, 15 Apr 2022 18:23:18 +0800 Subject: [PATCH 147/416] PrivateAssets --- build/SourceGenerators.props | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/build/SourceGenerators.props b/build/SourceGenerators.props index f9eab24d08..d000af1bf6 100644 --- a/build/SourceGenerators.props +++ b/build/SourceGenerators.props @@ -1,6 +1,10 @@ - + From d416b4de45413987330ef856035a02d07e177ad1 Mon Sep 17 00:00:00 2001 From: Steven He Date: Fri, 15 Apr 2022 18:30:08 +0800 Subject: [PATCH 148/416] NET6_0 -> NET6_0_OR_GREATER --- src/Shared/EnumHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Shared/EnumHelper.cs b/src/Shared/EnumHelper.cs index 8511e712fa..fddcf7ebc5 100644 --- a/src/Shared/EnumHelper.cs +++ b/src/Shared/EnumHelper.cs @@ -4,7 +4,7 @@ namespace Avalonia { internal class EnumHelper { -#if NET6_0 +#if NET6_0_OR_GREATER public static T Parse(ReadOnlySpan key, bool ignoreCase) where T : struct { return Enum.Parse(key, ignoreCase); From 578f5fc1cbd2abb42f85fb3bf798395721de6c57 Mon Sep 17 00:00:00 2001 From: Rustam Sayfutdinov Date: Fri, 15 Apr 2022 13:31:37 +0300 Subject: [PATCH 149/416] Fix PlatformSupport.UnitTests --- nukebuild/Build.cs | 1 + src/Avalonia.PlatformSupport/AssetLoader.cs | 8 ++++---- .../Avalonia.PlatformSupport.csproj | 2 +- .../Internal/AssemblyDescriptor.cs | 10 +++++++++- .../Internal/AssemblyDescriptorResolver.cs | 11 ++++++++--- .../AssetLoaderTests.cs | 6 +++--- 6 files changed, 26 insertions(+), 12 deletions(-) diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs index 9fcb9d6b7f..f0f677b844 100644 --- a/nukebuild/Build.cs +++ b/nukebuild/Build.cs @@ -221,6 +221,7 @@ partial class Build : NukeBuild RunCoreTest("Avalonia.Markup.Xaml.UnitTests"); RunCoreTest("Avalonia.Skia.UnitTests"); RunCoreTest("Avalonia.ReactiveUI.UnitTests"); + RunCoreTest("Avalonia.PlatformSupport.UnitTests"); }); Target RunRenderTests => _ => _ diff --git a/src/Avalonia.PlatformSupport/AssetLoader.cs b/src/Avalonia.PlatformSupport/AssetLoader.cs index fb03ec2f6e..0e33c3d4c7 100644 --- a/src/Avalonia.PlatformSupport/AssetLoader.cs +++ b/src/Avalonia.PlatformSupport/AssetLoader.cs @@ -14,14 +14,14 @@ namespace Avalonia.PlatformSupport /// public class AssetLoader : IAssetLoader { - private static AssemblyDescriptorResolver s_assemblyDescriptorResolver = new(); + private static IAssemblyDescriptorResolver s_assemblyDescriptorResolver = new AssemblyDescriptorResolver(); private AssemblyDescriptor? _defaultResmAssembly; /// /// Introduced for tests. /// - internal static void SetAssemblyDescriptorResolver(AssemblyDescriptorResolver resolver) => + internal static void SetAssemblyDescriptorResolver(IAssemblyDescriptorResolver resolver) => s_assemblyDescriptorResolver = resolver; /// @@ -182,13 +182,13 @@ namespace Avalonia.PlatformSupport throw new ArgumentException($"Unsupported url type: " + uri.Scheme, nameof(uri)); } - private (AssemblyDescriptor asm, string path) GetResAsmAndPath(Uri uri) + private (IAssemblyDescriptor asm, string path) GetResAsmAndPath(Uri uri) { var asm = s_assemblyDescriptorResolver.GetAssembly(uri.Authority); return (asm, uri.GetUnescapeAbsolutePath()); } - private AssemblyDescriptor? GetAssembly(Uri? uri) + private IAssemblyDescriptor? GetAssembly(Uri? uri) { if (uri != null) { diff --git a/src/Avalonia.PlatformSupport/Avalonia.PlatformSupport.csproj b/src/Avalonia.PlatformSupport/Avalonia.PlatformSupport.csproj index 420ac0796c..5336f1e630 100644 --- a/src/Avalonia.PlatformSupport/Avalonia.PlatformSupport.csproj +++ b/src/Avalonia.PlatformSupport/Avalonia.PlatformSupport.csproj @@ -19,6 +19,6 @@ - + diff --git a/src/Avalonia.PlatformSupport/Internal/AssemblyDescriptor.cs b/src/Avalonia.PlatformSupport/Internal/AssemblyDescriptor.cs index a3de7f2b8a..64ffec8482 100644 --- a/src/Avalonia.PlatformSupport/Internal/AssemblyDescriptor.cs +++ b/src/Avalonia.PlatformSupport/Internal/AssemblyDescriptor.cs @@ -6,7 +6,15 @@ using Avalonia.Utilities; namespace Avalonia.PlatformSupport.Internal; -internal class AssemblyDescriptor +internal interface IAssemblyDescriptor +{ + Assembly Assembly { get; } + Dictionary? Resources { get; } + Dictionary? AvaloniaResources { get; } + string? Name { get; } +} + +internal class AssemblyDescriptor : IAssemblyDescriptor { public AssemblyDescriptor(Assembly assembly) { diff --git a/src/Avalonia.PlatformSupport/Internal/AssemblyDescriptorResolver.cs b/src/Avalonia.PlatformSupport/Internal/AssemblyDescriptorResolver.cs index a78051a9c4..28ae35d57d 100644 --- a/src/Avalonia.PlatformSupport/Internal/AssemblyDescriptorResolver.cs +++ b/src/Avalonia.PlatformSupport/Internal/AssemblyDescriptorResolver.cs @@ -5,11 +5,16 @@ using System.Reflection; namespace Avalonia.PlatformSupport.Internal; -internal class AssemblyDescriptorResolver +internal interface IAssemblyDescriptorResolver { - private readonly Dictionary _assemblyNameCache = new(); + IAssemblyDescriptor GetAssembly(string name); +} + +internal class AssemblyDescriptorResolver: IAssemblyDescriptorResolver +{ + private readonly Dictionary _assemblyNameCache = new(); - public AssemblyDescriptor GetAssembly(string name) + public IAssemblyDescriptor GetAssembly(string name) { if (name == null) throw new ArgumentNullException(nameof(name)); diff --git a/tests/Avalonia.PlatformSupport.UnitTests/AssetLoaderTests.cs b/tests/Avalonia.PlatformSupport.UnitTests/AssetLoaderTests.cs index f950fb7e99..dfd195073b 100644 --- a/tests/Avalonia.PlatformSupport.UnitTests/AssetLoaderTests.cs +++ b/tests/Avalonia.PlatformSupport.UnitTests/AssetLoaderTests.cs @@ -16,7 +16,7 @@ public class AssetLoaderTests static AssetLoaderTests() { - var resolver = Mock.Of(); + var resolver = Mock.Of(); var descriptor = CreateAssemblyDescriptor(AssemblyNameWithWhitespace); Mock.Get(resolver).Setup(x => x.GetAssembly(AssemblyNameWithWhitespace)).Returns(descriptor); @@ -49,13 +49,13 @@ public class AssetLoaderTests Assert.Equal(AssemblyNameWithNonAscii, assemblyActual?.FullName); } - private static AssemblyDescriptor CreateAssemblyDescriptor(string assemblyName) + private static IAssemblyDescriptor CreateAssemblyDescriptor(string assemblyName) { var assembly = Mock.Of(); Mock.Get(assembly).Setup(x => x.GetName()).Returns(new AssemblyName(assemblyName)); Mock.Get(assembly).Setup(x => x.FullName).Returns(assemblyName); - var descriptor = Mock.Of(); + var descriptor = Mock.Of(); Mock.Get(descriptor).Setup(x => x.Assembly).Returns(assembly); return descriptor; } From a91bad4d3b7a9047c544876c8a4c8899980989a1 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 15 Apr 2022 13:55:57 +0200 Subject: [PATCH 150/416] Remove nesting selector validation. Needs to be added later. --- src/Avalonia.Base/Styling/Selector.cs | 13 +-- src/Avalonia.Base/Styling/Selectors.cs | 4 - src/Avalonia.Base/Styling/Style.cs | 12 +- .../Styling/SelectorTests_Nesting.cs | 105 ++++++++++++------ .../Styling/StyleTests.cs | 42 +++++++ 5 files changed, 130 insertions(+), 46 deletions(-) diff --git a/src/Avalonia.Base/Styling/Selector.cs b/src/Avalonia.Base/Styling/Selector.cs index 008ce7f3a5..6f7f754b7b 100644 --- a/src/Avalonia.Base/Styling/Selector.cs +++ b/src/Avalonia.Base/Styling/Selector.cs @@ -96,11 +96,7 @@ namespace Avalonia.Styling combinator = null; var activators = new AndActivatorBuilder(); - var foundNested = false; - var result = Match(control, start, parent, subscribe, ref activators, ref combinator, ref foundNested); - - if (parent is not null && !foundNested) - throw new InvalidOperationException("Nesting selector '&' must appear in child selector."); + var result = Match(control, start, parent, subscribe, ref activators, ref combinator); return result == SelectorMatchResult.Sometimes ? new SelectorMatch(activators.Get()) : @@ -113,8 +109,7 @@ namespace Avalonia.Styling IStyle? parent, bool subscribe, ref AndActivatorBuilder activators, - ref Selector? combinator, - ref bool foundNested) + ref Selector? combinator) { var previous = selector.MovePrevious(); @@ -123,7 +118,7 @@ namespace Avalonia.Styling // opportunity to exit early. if (previous != null && !previous.IsCombinator) { - var previousMatch = Match(control, previous, parent, subscribe, ref activators, ref combinator, ref foundNested); + var previousMatch = Match(control, previous, parent, subscribe, ref activators, ref combinator); if (previousMatch < SelectorMatchResult.Sometimes) { @@ -131,8 +126,6 @@ namespace Avalonia.Styling } } - foundNested |= selector is NestingSelector; - // Match this selector. var match = selector.Evaluate(control, parent, subscribe); diff --git a/src/Avalonia.Base/Styling/Selectors.cs b/src/Avalonia.Base/Styling/Selectors.cs index a036c140c2..476d86cd11 100644 --- a/src/Avalonia.Base/Styling/Selectors.cs +++ b/src/Avalonia.Base/Styling/Selectors.cs @@ -111,10 +111,6 @@ namespace Avalonia.Styling public static Selector Nesting(this Selector? previous) { - if (previous is not null) - throw new InvalidOperationException( - "Nesting selector '&' must appear at the start of the style selector."); - return new NestingSelector(); } diff --git a/src/Avalonia.Base/Styling/Style.cs b/src/Avalonia.Base/Styling/Style.cs index a3a7ea29d3..e79678b20b 100644 --- a/src/Avalonia.Base/Styling/Style.cs +++ b/src/Avalonia.Base/Styling/Style.cs @@ -169,6 +169,16 @@ namespace Avalonia.Styling } } - internal void SetParent(Style? parent) => Parent = parent; + internal void SetParent(Style? parent) + { + if (parent?.Selector is not null) + { + if (Selector is null) + throw new InvalidOperationException("Nested styles must have a selector."); + // TODO: Validate that selector contains & in the right place. + } + + Parent = parent; + } } } diff --git a/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Nesting.cs b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Nesting.cs index cee7e748ba..eeb2fad996 100644 --- a/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Nesting.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Nesting.cs @@ -9,7 +9,7 @@ namespace Avalonia.Base.UnitTests.Styling public class SelectorTests_Nesting { [Fact] - public void Parent_Selector_Doesnt_Match_OfType() + public void Nesting_Class_Doesnt_Match_Parent_Selector() { var control = new Control2(); Style nested; @@ -26,43 +26,50 @@ namespace Avalonia.Base.UnitTests.Styling } [Fact] - public void Nested_Class_Selector() + public void Or_Nesting_Class_Doesnt_Match_Parent_Selector() { - var control = new Control1 { Classes = { "foo" } }; + var control = new Control2(); Style nested; var parent = new Style(x => x.OfType()) { Children = { - (nested = new Style(x => x.Nesting().Class("foo"))), + (nested = new Style(x => Selectors.Or( + x.Nesting().Class("foo"), + x.Nesting().Class("bar")))), } }; var match = nested.Selector.Match(control, parent); - Assert.Equal(SelectorMatchResult.Sometimes, match.Result); - - var sink = new ActivatorSink(match.Activator); - - Assert.True(sink.Active); - control.Classes.Clear(); - Assert.False(sink.Active); + Assert.Equal(SelectorMatchResult.NeverThisType, match.Result); } [Fact] - public void Nesting_With_No_Parent_Style_Fails() + public void Or_Nesting_Child_OfType_Does_Not_Match_Parent_Selector() { var control = new Control1(); - var style = new Style(x => x.Nesting().OfType()); + var panel = new DockPanel { Children = { control } }; + Style nested; + var parent = new Style(x => x.OfType()) + { + Children = + { + (nested = new Style(x => Selectors.Or( + x.Nesting().Child().OfType(), + x.Nesting().Child().OfType()))), + } + }; - Assert.Throws(() => style.Selector.Match(control, null)); + var match = nested.Selector.Match(control, parent); + Assert.Equal(SelectorMatchResult.NeverThisInstance, match.Result); } [Fact] - public void Nesting_With_No_Parent_Selector_Fails() + public void Nesting_Class_Matches() { - var control = new Control1(); + var control = new Control1 { Classes = { "foo" } }; Style nested; - var parent = new Style + var parent = new Style(x => x.OfType()) { Children = { @@ -70,44 +77,80 @@ namespace Avalonia.Base.UnitTests.Styling } }; - Assert.Throws(() => nested.Selector.Match(control, parent)); + var match = nested.Selector.Match(control, parent); + Assert.Equal(SelectorMatchResult.Sometimes, match.Result); + + var sink = new ActivatorSink(match.Activator); + + Assert.True(sink.Active); + control.Classes.Clear(); + Assert.False(sink.Active); } [Fact] - public void Nesting_Must_Appear_At_Start_Of_Selector() + public void Or_Nesting_Class_Matches() { - var control = new Control1(); - Assert.Throws(() => new Style(x => x.OfType().Nesting())); + var control = new Control1 { Classes = { "foo" } }; + Style nested; + var parent = new Style(x => x.OfType()) + { + Children = + { + (nested = new Style(x => Selectors.Or( + x.Nesting().Class("foo"), + x.Nesting().Class("bar")))), + } + }; + + var match = nested.Selector.Match(control, parent); + Assert.Equal(SelectorMatchResult.Sometimes, match.Result); + + var sink = new ActivatorSink(match.Activator); + + Assert.True(sink.Active); + control.Classes.Clear(); + Assert.False(sink.Active); } [Fact] - public void Nesting_Must_Appear() + public void Or_Nesting_Child_OfType_Matches() { - var control = new Control1(); + var control = new Control1 { Classes = { "foo" } }; + var panel = new Panel { Children = { control } }; Style nested; - var parent = new Style + var parent = new Style(x => x.OfType()) { Children = { - (nested = new Style(x => x.OfType().Class("foo"))), + (nested = new Style(x => Selectors.Or( + x.Nesting().Child().OfType(), + x.Nesting().Child().OfType()))), } }; - Assert.Throws(() => nested.Selector.Match(control, parent)); + var match = nested.Selector.Match(control, parent); + Assert.Equal(SelectorMatchResult.AlwaysThisInstance, match.Result); } [Fact] - public void Nesting_Must_Appear_In_All_Or_Arguments() + public void Nesting_With_No_Parent_Style_Fails() + { + var control = new Control1(); + var style = new Style(x => x.Nesting().OfType()); + + Assert.Throws(() => style.Selector.Match(control, null)); + } + + [Fact] + public void Nesting_With_No_Parent_Selector_Fails() { var control = new Control1(); Style nested; - var parent = new Style(x => x.OfType()) + var parent = new Style { Children = { - (nested = new Style(x => Selectors.Or( - x.Nesting().Class("foo"), - x.Class("bar")))) + (nested = new Style(x => x.Nesting().Class("foo"))), } }; diff --git a/tests/Avalonia.Base.UnitTests/Styling/StyleTests.cs b/tests/Avalonia.Base.UnitTests/Styling/StyleTests.cs index 8dedf3471f..7aa86a1328 100644 --- a/tests/Avalonia.Base.UnitTests/Styling/StyleTests.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/StyleTests.cs @@ -722,6 +722,48 @@ namespace Avalonia.Base.UnitTests.Styling resources.Verify(x => x.AddOwner(host.Object), Times.Once); } + [Fact] + public void Nested_Style_Can_Be_Added() + { + var parent = new Style(x => x.OfType()); + var nested = new Style(x => x.Nesting().Class("foo")); + + parent.Children.Add(nested); + + Assert.Same(parent, nested.Parent); + } + + [Fact] + public void Nested_Or_Style_Can_Be_Added() + { + var parent = new Style(x => x.OfType()); + var nested = new Style(x => Selectors.Or( + x.Nesting().Class("foo"), + x.Nesting().Class("bar"))); + + parent.Children.Add(nested); + + Assert.Same(parent, nested.Parent); + } + + [Fact] + public void Nested_Style_Without_Selector_Throws() + { + var parent = new Style(x => x.OfType()); + var nested = new Style(); + + Assert.Throws(() => parent.Children.Add(nested)); + } + + [Fact(Skip = "TODO")] + public void Nested_Style_Without_Nesting_Operator_Throws() + { + var parent = new Style(x => x.OfType()); + var nested = new Style(x => x.Class("foo")); + + Assert.Throws(() => parent.Children.Add(nested)); + } + private class Class1 : Control { public static readonly StyledProperty FooProperty = From fd5e2169c143fb38b32edb1f2e2825bb2a35a513 Mon Sep 17 00:00:00 2001 From: Tako Date: Fri, 15 Apr 2022 14:56:02 +0300 Subject: [PATCH 151/416] reimplementation and some tests --- src/Avalonia.Controls/ComboBox.cs | 19 +---- src/Avalonia.Controls/ItemsControl.cs | 1 - src/Avalonia.Controls/StackPanel.cs | 2 +- .../ComboBoxTests.cs | 75 +++++++++++++++++++ 4 files changed, 77 insertions(+), 20 deletions(-) diff --git a/src/Avalonia.Controls/ComboBox.cs b/src/Avalonia.Controls/ComboBox.cs index 582f90e472..cbf9b35a05 100644 --- a/src/Avalonia.Controls/ComboBox.cs +++ b/src/Avalonia.Controls/ComboBox.cs @@ -455,24 +455,7 @@ namespace Avalonia.Controls { if (ItemCount >= 1) { - if (SelectedIndex == -1) - { - if (MoveSelection(NavigationDirection.First, WrapSelection) == false) - { - // MoveSelection works only with indexes starting from 0 - // so to make it search further than the first item we need to set SelectedIndex to 0. - SelectedIndex = 0; - var isSelectionMoved = MoveSelection(NavigationDirection.Next, WrapSelection); - if (isSelectionMoved == false) - { - SelectedIndex = -1; - } - } - } - else - { - MoveSelection(NavigationDirection.Next, WrapSelection); - } + MoveSelection(NavigationDirection.Next, WrapSelection); } } diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs index 0cd72dc91c..afe4a08446 100644 --- a/src/Avalonia.Controls/ItemsControl.cs +++ b/src/Avalonia.Controls/ItemsControl.cs @@ -508,7 +508,6 @@ namespace Avalonia.Controls do { result = container.GetControl(direction, c, wrap); - from = from ?? result; if (result != null && result.Focusable && diff --git a/src/Avalonia.Controls/StackPanel.cs b/src/Avalonia.Controls/StackPanel.cs index feb425a9c3..50c48d2bb0 100644 --- a/src/Avalonia.Controls/StackPanel.cs +++ b/src/Avalonia.Controls/StackPanel.cs @@ -123,7 +123,7 @@ namespace Avalonia.Controls index = Children.Count - 1; break; case NavigationDirection.Next: - if (index != -1) ++index; + ++index; break; case NavigationDirection.Previous: if (index != -1) --index; diff --git a/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs b/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs index cb2fd11175..98695fe88e 100644 --- a/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs @@ -36,6 +36,81 @@ namespace Avalonia.Controls.UnitTests Assert.False(target.IsDropDownOpen); } + [Fact] + public void WrapSelection_Should_Work() + { + using (UnitTestApplication.Start(TestServices.RealFocus)) + { + var items = new[] + { + new ComboBoxItem() { Content = "bla" }, + new ComboBoxItem() { Content = "dd" }, + new ComboBoxItem() { Content = "sdf", IsEnabled = false } + }; + var target = new ComboBox + { + Items = items, + Template = GetTemplate(), + WrapSelection = true + }; + var root = new TestRoot(target); + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); + target.Focus(); + Assert.Equal(target.SelectedIndex, -1); + Assert.True(target.IsFocused); + target.RaiseEvent(new KeyEventArgs + { + RoutedEvent = InputElement.KeyDownEvent, + Key = Key.Up, + }); + Assert.Equal(target.SelectedIndex, 1); + target.RaiseEvent(new KeyEventArgs + { + RoutedEvent = InputElement.KeyDownEvent, + Key = Key.Down, + }); + Assert.Equal(target.SelectedIndex, 0); + } + } + + [Fact] + public void Focuses_Next_Item_On_Key_Down() + { + using (UnitTestApplication.Start(TestServices.RealFocus)) + { + var items = new[] + { + new ComboBoxItem() { Content = "bla" }, + new ComboBoxItem() { Content = "dd", IsEnabled = false }, + new ComboBoxItem() { Content = "sdf" } + }; + var target = new ComboBox + { + Items = items, + Template = GetTemplate() + }; + var root = new TestRoot(target); + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); + target.Focus(); + Assert.Equal(target.SelectedIndex, -1); + Assert.True(target.IsFocused); + target.RaiseEvent(new KeyEventArgs + { + RoutedEvent = InputElement.KeyDownEvent, + Key = Key.Down, + }); + Assert.Equal(target.SelectedIndex, 0); + target.RaiseEvent(new KeyEventArgs + { + RoutedEvent = InputElement.KeyDownEvent, + Key = Key.Down, + }); + Assert.Equal(target.SelectedIndex, 2); + } + } + [Fact] public void SelectionBoxItem_Is_Rectangle_With_VisualBrush_When_Selection_Is_Control() { From 646ce23bb4bdfdce57f9d9052e5904440e094dfa Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 15 Apr 2022 14:29:00 +0200 Subject: [PATCH 152/416] Initial XAML implementation of nested styles. --- src/Avalonia.Base/Styling/Style.cs | 8 +++++ .../AvaloniaXamlIlSelectorTransformer.cs | 24 ++++++++++++++ .../Parsers/SelectorGrammarTests.cs | 1 - .../Xaml/StyleTests.cs | 32 +++++++++++++++++++ 4 files changed, 64 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Styling/Style.cs b/src/Avalonia.Base/Styling/Style.cs index e79678b20b..6020dfe25f 100644 --- a/src/Avalonia.Base/Styling/Style.cs +++ b/src/Avalonia.Base/Styling/Style.cs @@ -120,6 +120,14 @@ namespace Avalonia.Styling instance.Start(); } + if (_children is not null) + { + foreach (var child in _children) + { + child.TryAttach(target, host); + } + } + return match.Result; } diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs index 4c4df1f53a..70209fb3ad 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs @@ -151,6 +151,14 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers results.Add(result); result = initialNode; break; + case SelectorGrammar.NestingSyntax: + var parentTargetType = context.ParentNodes().OfType().FirstOrDefault(); + + if (parentTargetType is null) + throw new XamlParseException($"Cannot find parent style for nested selector.", node); + + result = new XamlIlNestingSelector(result, parentTargetType.TargetType.GetClrType()); + break; default: throw new XamlParseException($"Unsupported selector grammar '{i.GetType()}'.", node); } @@ -474,4 +482,20 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers m => m.Name == "Or" && m.Parameters.Count == 1 && m.Parameters[0].Name.StartsWith("IReadOnlyList")); } } + + class XamlIlNestingSelector : XamlIlSelectorNode + { + public XamlIlNestingSelector(XamlIlSelectorNode previous, IXamlType targetType) + : base(previous) + { + TargetType = targetType; + } + + public override IXamlType TargetType { get; } + protected override void DoEmit(XamlEmitContext context, IXamlILEmitter codeGen) + { + EmitCall(context, codeGen, + m => m.Name == "Nesting" && m.Parameters.Count == 1); + } + } } diff --git a/tests/Avalonia.Markup.UnitTests/Parsers/SelectorGrammarTests.cs b/tests/Avalonia.Markup.UnitTests/Parsers/SelectorGrammarTests.cs index 5c59101307..685f9eab6f 100644 --- a/tests/Avalonia.Markup.UnitTests/Parsers/SelectorGrammarTests.cs +++ b/tests/Avalonia.Markup.UnitTests/Parsers/SelectorGrammarTests.cs @@ -573,7 +573,6 @@ namespace Avalonia.Markup.UnitTests.Parsers result); } - [Fact] public void Nesting_NthChild() { diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleTests.cs index 022ff0c3a4..9301647411 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleTests.cs @@ -617,5 +617,37 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml Assert.Equal(Colors.Red, ((ISolidColorBrush)foo.Background).Color); } } + + [Fact] + public void Can_Use_Nested_Styles() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + + + + + + + + +"; + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + var foo = window.FindControl("foo"); + + Assert.Null(foo.Background); + + foo.Classes.Add("foo"); + + Assert.Equal(Colors.Red, ((ISolidColorBrush)foo.Background).Color); + } + } } } From 2a2e4ca6db9651a334de740033eb507aea6f3c3b Mon Sep 17 00:00:00 2001 From: Steven He Date: Fri, 15 Apr 2022 23:53:10 +0800 Subject: [PATCH 153/416] Apply code review feedbacks --- Avalonia.sln | 1 - 1 file changed, 1 deletion(-) diff --git a/Avalonia.sln b/Avalonia.sln index 8a48566653..1e2a3c6027 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -38,7 +38,6 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{A689DEF5-D50F-4975-8B72-124C9EB54066}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig - src\Shared\EnumHelper.cs = src\Shared\EnumHelper.cs src\Shared\ModuleInitializer.cs = src\Shared\ModuleInitializer.cs src\Shared\SourceGeneratorAttributes.cs = src\Shared\SourceGeneratorAttributes.cs EndProjectSection From be8bcd2be7bb31321af83a6c0ba99f87a07d67cc Mon Sep 17 00:00:00 2001 From: Max Katz Date: Fri, 15 Apr 2022 12:06:45 -0400 Subject: [PATCH 154/416] Update src/Avalonia.Base/Input/PointerOverPreProcessor.cs --- src/Avalonia.Base/Input/PointerOverPreProcessor.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Avalonia.Base/Input/PointerOverPreProcessor.cs b/src/Avalonia.Base/Input/PointerOverPreProcessor.cs index a38364197d..d22252893d 100644 --- a/src/Avalonia.Base/Input/PointerOverPreProcessor.cs +++ b/src/Avalonia.Base/Input/PointerOverPreProcessor.cs @@ -60,7 +60,6 @@ namespace Avalonia.Input public void SceneInvalidated(Rect dirtyRect) { - // Pointer is outside of the target area if (_lastPointer is (var pointer, var position)) { var clientPoint = _inputRoot.PointToClient(position); From 7530e20be04366fd621bca9650faba41294cdbb7 Mon Sep 17 00:00:00 2001 From: Steven He Date: Sat, 16 Apr 2022 00:21:42 +0800 Subject: [PATCH 155/416] Move EnumHelper to Avalonia.Base --- src/Avalonia.Base/Avalonia.Base.csproj | 1 - src/Avalonia.Base/Input/KeyGesture.cs | 1 + src/Avalonia.Base/Properties/AssemblyInfo.cs | 1 + src/{Shared => Avalonia.Base/Utilities}/EnumHelper.cs | 2 +- src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj | 1 - .../Remote/HtmlTransport/HtmlTransport.cs | 1 + 6 files changed, 4 insertions(+), 3 deletions(-) rename src/{Shared => Avalonia.Base/Utilities}/EnumHelper.cs (93%) diff --git a/src/Avalonia.Base/Avalonia.Base.csproj b/src/Avalonia.Base/Avalonia.Base.csproj index a9a606de8d..8e4755b4b7 100644 --- a/src/Avalonia.Base/Avalonia.Base.csproj +++ b/src/Avalonia.Base/Avalonia.Base.csproj @@ -7,7 +7,6 @@ - diff --git a/src/Avalonia.Base/Input/KeyGesture.cs b/src/Avalonia.Base/Input/KeyGesture.cs index cb6d12ac0d..3b7a828b86 100644 --- a/src/Avalonia.Base/Input/KeyGesture.cs +++ b/src/Avalonia.Base/Input/KeyGesture.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Text; +using Avalonia.Utilities; namespace Avalonia.Input { diff --git a/src/Avalonia.Base/Properties/AssemblyInfo.cs b/src/Avalonia.Base/Properties/AssemblyInfo.cs index 4d7f487e00..a0560924e7 100644 --- a/src/Avalonia.Base/Properties/AssemblyInfo.cs +++ b/src/Avalonia.Base/Properties/AssemblyInfo.cs @@ -21,6 +21,7 @@ using Avalonia.Metadata; [assembly: InternalsVisibleTo("Avalonia.Controls, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] [assembly: InternalsVisibleTo("Avalonia.Controls.DataGrid, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] [assembly: InternalsVisibleTo("Avalonia.Controls.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] +[assembly: InternalsVisibleTo("Avalonia.DesignerSupport, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] [assembly: InternalsVisibleTo("Avalonia.Direct2D1.RenderTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] [assembly: InternalsVisibleTo("Avalonia.LeakTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] [assembly: InternalsVisibleTo("Avalonia.Markup.Xaml.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] diff --git a/src/Shared/EnumHelper.cs b/src/Avalonia.Base/Utilities/EnumHelper.cs similarity index 93% rename from src/Shared/EnumHelper.cs rename to src/Avalonia.Base/Utilities/EnumHelper.cs index fddcf7ebc5..c857033ef1 100644 --- a/src/Shared/EnumHelper.cs +++ b/src/Avalonia.Base/Utilities/EnumHelper.cs @@ -1,6 +1,6 @@ using System; -namespace Avalonia +namespace Avalonia.Utilities { internal class EnumHelper { diff --git a/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj b/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj index 586d13f179..0270000d8c 100644 --- a/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj +++ b/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj @@ -19,7 +19,6 @@ - diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs index 794bc94256..f100be5d5b 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs @@ -10,6 +10,7 @@ using System.Threading; using System.Threading.Tasks; using Avalonia.Remote.Protocol; using Avalonia.Remote.Protocol.Viewport; +using Avalonia.Utilities; using InputProtocol = Avalonia.Remote.Protocol.Input; namespace Avalonia.DesignerSupport.Remote.HtmlTransport From 43a3841dcbdc224c528f95c9f25eb585b141c8fc Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 15 Apr 2022 18:02:43 +0200 Subject: [PATCH 156/416] Add/remove resource owner. --- src/Avalonia.Base/Styling/StyleChildren.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Styling/StyleChildren.cs b/src/Avalonia.Base/Styling/StyleChildren.cs index 21ac3c4072..64d838e4ce 100644 --- a/src/Avalonia.Base/Styling/StyleChildren.cs +++ b/src/Avalonia.Base/Styling/StyleChildren.cs @@ -1,4 +1,5 @@ using System.Collections.ObjectModel; +using Avalonia.Controls; namespace Avalonia.Styling { @@ -16,7 +17,10 @@ namespace Avalonia.Styling protected override void RemoveItem(int index) { - (Items[index] as Style)?.SetParent(null); + var item = Items[index]; + if (_owner.Owner is IResourceHost host) + (item as IResourceProvider)?.RemoveOwner(host); + (item as Style)?.SetParent(null); base.RemoveItem(index); } @@ -24,6 +28,8 @@ namespace Avalonia.Styling { base.SetItem(index, item); (item as Style)?.SetParent(_owner); + if (_owner.Owner is IResourceHost host) + (item as IResourceProvider)?.AddOwner(host); } } } From 37f678e96a7b35c2d0c79d6871d5e06fd540948b Mon Sep 17 00:00:00 2001 From: robloo Date: Fri, 15 Apr 2022 19:05:10 -0400 Subject: [PATCH 157/416] Fix missing color alpha rounding --- src/Avalonia.Base/Media/Color.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Media/Color.cs b/src/Avalonia.Base/Media/Color.cs index eaa886ccbd..9ad75b3290 100644 --- a/src/Avalonia.Base/Media/Color.cs +++ b/src/Avalonia.Base/Media/Color.cs @@ -375,7 +375,7 @@ namespace Avalonia.Media byte.TryParse(components[2], NumberStyles.Number, CultureInfo.InvariantCulture, out byte blue) && TryInternalParse(components[3], out double alpha)) { - color = new Color((byte)(alpha * 255), red, green, blue); + color = new Color((byte)Math.Round(alpha * 255.0), red, green, blue); return true; } } From c7e0a68f2779361ac23bf13003860ed5634c8278 Mon Sep 17 00:00:00 2001 From: robloo Date: Fri, 15 Apr 2022 19:07:01 -0400 Subject: [PATCH 158/416] Support RGB component percentages in CSS format parsing --- src/Avalonia.Base/Media/Color.cs | 43 +++++++++++++++---- .../Media/ColorTests.cs | 24 ++++++++--- 2 files changed, 53 insertions(+), 14 deletions(-) diff --git a/src/Avalonia.Base/Media/Color.cs b/src/Avalonia.Base/Media/Color.cs index 9ad75b3290..2f0def58a4 100644 --- a/src/Avalonia.Base/Media/Color.cs +++ b/src/Avalonia.Base/Media/Color.cs @@ -360,9 +360,9 @@ namespace Avalonia.Media if (components.Length == 3) // RGB { - if (byte.TryParse(components[0], NumberStyles.Number, CultureInfo.InvariantCulture, out byte red) && - byte.TryParse(components[1], NumberStyles.Number, CultureInfo.InvariantCulture, out byte green) && - byte.TryParse(components[2], NumberStyles.Number, CultureInfo.InvariantCulture, out byte blue)) + if (InternalTryParseByte(components[0], out byte red) && + InternalTryParseByte(components[1], out byte green) && + InternalTryParseByte(components[2], out byte blue)) { color = new Color(0xFF, red, green, blue); return true; @@ -370,18 +370,45 @@ namespace Avalonia.Media } else if (components.Length == 4) // RGBA { - if (byte.TryParse(components[0], NumberStyles.Number, CultureInfo.InvariantCulture, out byte red) && - byte.TryParse(components[1], NumberStyles.Number, CultureInfo.InvariantCulture, out byte green) && - byte.TryParse(components[2], NumberStyles.Number, CultureInfo.InvariantCulture, out byte blue) && - TryInternalParse(components[3], out double alpha)) + if (InternalTryParseByte(components[0], out byte red) && + InternalTryParseByte(components[1], out byte green) && + InternalTryParseByte(components[2], out byte blue) && + InternalTryParseDouble(components[3], out double alpha)) { color = new Color((byte)Math.Round(alpha * 255.0), red, green, blue); return true; } } + // Local function to specially parse a byte value with an optional percentage sign + bool InternalTryParseByte(string inString, out byte outByte) + { + // The percent sign, if it exists, must be at the end of the number + int percentIndex = inString.IndexOf("%", StringComparison.Ordinal); + + if (percentIndex >= 0) + { + var result = double.TryParse( + inString.Substring(0, percentIndex), + NumberStyles.Number, + CultureInfo.InvariantCulture, + out double percentage); + + outByte = (byte)Math.Round((percentage / 100.0) * 255.0); + return result; + } + else + { + return byte.TryParse( + inString, + NumberStyles.Number, + CultureInfo.InvariantCulture, + out outByte); + } + } + // Local function to specially parse a double value with an optional percentage sign - bool TryInternalParse(string inString, out double outDouble) + bool InternalTryParseDouble(string inString, out double outDouble) { // The percent sign, if it exists, must be at the end of the number int percentIndex = inString.IndexOf("%", StringComparison.Ordinal); diff --git a/tests/Avalonia.Base.UnitTests/Media/ColorTests.cs b/tests/Avalonia.Base.UnitTests/Media/ColorTests.cs index 1392635b32..36929d5e95 100644 --- a/tests/Avalonia.Base.UnitTests/Media/ColorTests.cs +++ b/tests/Avalonia.Base.UnitTests/Media/ColorTests.cs @@ -216,8 +216,8 @@ namespace Avalonia.Base.UnitTests.Media Tuple.Create("hsl(-1000, -1000, -1000)", new HslColor(1, 0, 0, 0)), // Clamps to min Tuple.Create("hsl(-1000, -1000%, -1000%)", new HslColor(1, 0, 0, 0)), // Clamps to min - Tuple.Create("hsl(1000, 1000, 1000)", new HslColor(1, 0, 1, 1)), // Clamps to max - Tuple.Create("hsl(1000, 1000%, 1000%)", new HslColor(1, 0, 1, 1)), // Clamps to max + Tuple.Create("hsl(1000, 1000, 1000)", new HslColor(1, 0, 1, 1)), // Clamps to max (Hue wraps to zero) + Tuple.Create("hsl(1000, 1000%, 1000%)", new HslColor(1, 0, 1, 1)), // Clamps to max (Hue wraps to zero) Tuple.Create("hsl(300, 0.8, 0.2)", new HslColor(1.0, 300, 0.8, 0.2)), Tuple.Create("hsl(300, 80%, 20%)", new HslColor(1.0, 300, 0.8, 0.2)), @@ -262,8 +262,8 @@ namespace Avalonia.Base.UnitTests.Media Tuple.Create("hsv(-1000, -1000, -1000)", new HsvColor(1, 0, 0, 0)), // Clamps to min Tuple.Create("hsv(-1000, -1000%, -1000%)", new HsvColor(1, 0, 0, 0)), // Clamps to min - Tuple.Create("hsv(1000, 1000, 1000)", new HsvColor(1, 0, 1, 1)), // Clamps to max - Tuple.Create("hsv(1000, 1000%, 1000%)", new HsvColor(1, 0, 1, 1)), // Clamps to max + Tuple.Create("hsv(1000, 1000, 1000)", new HsvColor(1, 0, 1, 1)), // Clamps to max (Hue wraps to zero) + Tuple.Create("hsv(1000, 1000%, 1000%)", new HsvColor(1, 0, 1, 1)), // Clamps to max (Hue wraps to zero) Tuple.Create("hsv(300, 0.8, 0.2)", new HsvColor(1.0, 300, 0.8, 0.2)), Tuple.Create("hsv(300, 80%, 20%)", new HsvColor(1.0, 300, 0.8, 0.2)), @@ -303,8 +303,20 @@ namespace Avalonia.Base.UnitTests.Media Tuple.Create("#123456", new Color(0xff, 0x12, 0x34, 0x56)), Tuple.Create("rgb(100, 30, 45)", new Color(255, 100, 30, 45)), - Tuple.Create("rgba(100, 30, 45, 0.9)", new Color(229, 100, 30, 45)), - Tuple.Create("rgba(100, 30, 45, 90%)", new Color(229, 100, 30, 45)), + Tuple.Create("rgba(100, 30, 45, 0.9)", new Color(230, 100, 30, 45)), + Tuple.Create("rgba(100, 30, 45, 90%)", new Color(230, 100, 30, 45)), + + Tuple.Create("rgb(255,0,0)", new Color(255, 255, 0, 0)), + Tuple.Create("rgb(0,255,0)", new Color(255, 0, 255, 0)), + Tuple.Create("rgb(0,0,255)", new Color(255, 0, 0, 255)), + + Tuple.Create("rgb(100%, 0, 0)", new Color(255, 255, 0, 0)), + Tuple.Create("rgb(0, 100%, 0)", new Color(255, 0, 255, 0)), + Tuple.Create("rgb(0, 0, 100%)", new Color(255, 0, 0, 255)), + + Tuple.Create("rgba(0, 0, 100%, 50%)", new Color(128, 0, 0, 255)), + Tuple.Create("rgba(50%, 10%, 80%, 50%)", new Color(128, 128, 26, 204)), + Tuple.Create("rgba(50%, 10%, 80%, 0.5)", new Color(128, 128, 26, 204)), // HSL Tuple.Create("hsl(296, 85%, 12%)", new Color(255, 53, 5, 57)), From 467f78b58d3ee2554a79df40693e260cd736d3ca Mon Sep 17 00:00:00 2001 From: robloo Date: Fri, 15 Apr 2022 19:16:17 -0400 Subject: [PATCH 159/416] Improve color format length checking for performance --- src/Avalonia.Base/Media/Color.cs | 22 ++++++++++++++-------- src/Avalonia.Base/Media/HslColor.cs | 11 +++++++++-- src/Avalonia.Base/Media/HsvColor.cs | 11 +++++++++-- 3 files changed, 32 insertions(+), 12 deletions(-) diff --git a/src/Avalonia.Base/Media/Color.cs b/src/Avalonia.Base/Media/Color.cs index 2f0def58a4..dc22f87d49 100644 --- a/src/Avalonia.Base/Media/Color.cs +++ b/src/Avalonia.Base/Media/Color.cs @@ -166,7 +166,10 @@ namespace Avalonia.Media return true; } - if (s.Length > 5 && + // Note: The length checks are also an important optimization. + // The shortest possible CSS format is "rbg(0,0,0)", Length = 10. + + if (s.Length >= 10 && (s[0] == 'r' || s[0] == 'R') && (s[1] == 'g' || s[1] == 'G') && (s[2] == 'b' || s[2] == 'B') && @@ -175,7 +178,7 @@ namespace Avalonia.Media return true; } - if (s.Length > 5 && + if (s.Length >= 10 && (s[0] == 'h' || s[0] == 'H') && (s[1] == 's' || s[1] == 'S') && (s[2] == 'l' || s[2] == 'L') && @@ -185,7 +188,7 @@ namespace Avalonia.Media return true; } - if (s.Length > 5 && + if (s.Length >= 10 && (s[0] == 'h' || s[0] == 'H') && (s[1] == 's' || s[1] == 'S') && (s[2] == 'v' || s[2] == 'V') && @@ -229,7 +232,10 @@ namespace Avalonia.Media // At this point all parsing uses strings var str = s.ToString(); - if (s.Length > 5 && + // Note: The length checks are also an important optimization. + // The shortest possible CSS format is "rbg(0,0,0)", Length = 10. + + if (s.Length >= 10 && (s[0] == 'r' || s[0] == 'R') && (s[1] == 'g' || s[1] == 'G') && (s[2] == 'b' || s[2] == 'B') && @@ -238,7 +244,7 @@ namespace Avalonia.Media return true; } - if (s.Length > 5 && + if (s.Length >= 10 && (s[0] == 'h' || s[0] == 'H') && (s[1] == 's' || s[1] == 'S') && (s[2] == 'l' || s[2] == 'L') && @@ -248,7 +254,7 @@ namespace Avalonia.Media return true; } - if (s.Length > 5 && + if (s.Length >= 10 && (s[0] == 'h' || s[0] == 'H') && (s[1] == 's' || s[1] == 'S') && (s[2] == 'v' || s[2] == 'V') && @@ -342,14 +348,14 @@ namespace Avalonia.Media return false; } - if (workingString.Length > 6 && + if (workingString.Length >= 11 && workingString.StartsWith("rgba(", StringComparison.OrdinalIgnoreCase) && workingString.EndsWith(")", StringComparison.Ordinal)) { workingString = workingString.Substring(5, workingString.Length - 6); } - if (workingString.Length > 5 && + if (workingString.Length >= 10 && workingString.StartsWith("rgb(", StringComparison.OrdinalIgnoreCase) && workingString.EndsWith(")", StringComparison.Ordinal)) { diff --git a/src/Avalonia.Base/Media/HslColor.cs b/src/Avalonia.Base/Media/HslColor.cs index e27a4f3106..622d3ac2b7 100644 --- a/src/Avalonia.Base/Media/HslColor.cs +++ b/src/Avalonia.Base/Media/HslColor.cs @@ -241,19 +241,26 @@ namespace Avalonia.Media return false; } - if (workingString.Length > 6 && + // Note: The length checks are also an important optimization. + // The shortest possible format is "hsl(0,0,0)", Length = 10. + + if (workingString.Length >= 11 && workingString.StartsWith("hsla(", StringComparison.OrdinalIgnoreCase) && workingString.EndsWith(")", StringComparison.Ordinal)) { workingString = workingString.Substring(5, workingString.Length - 6); } - if (workingString.Length > 5 && + if (workingString.Length >= 10 && workingString.StartsWith("hsl(", StringComparison.OrdinalIgnoreCase) && workingString.EndsWith(")", StringComparison.Ordinal)) { workingString = workingString.Substring(4, workingString.Length - 5); } + else + { + return false; + } string[] components = workingString.Split(','); diff --git a/src/Avalonia.Base/Media/HsvColor.cs b/src/Avalonia.Base/Media/HsvColor.cs index 164aeb1df1..1e718b5739 100644 --- a/src/Avalonia.Base/Media/HsvColor.cs +++ b/src/Avalonia.Base/Media/HsvColor.cs @@ -241,19 +241,26 @@ namespace Avalonia.Media return false; } - if (workingString.Length > 6 && + // Note: The length checks are also an important optimization. + // The shortest possible format is "hsv(0,0,0)", Length = 10. + + if (workingString.Length >= 11 && workingString.StartsWith("hsva(", StringComparison.OrdinalIgnoreCase) && workingString.EndsWith(")", StringComparison.Ordinal)) { workingString = workingString.Substring(5, workingString.Length - 6); } - if (workingString.Length > 5 && + if (workingString.Length >= 10 && workingString.StartsWith("hsv(", StringComparison.OrdinalIgnoreCase) && workingString.EndsWith(")", StringComparison.Ordinal)) { workingString = workingString.Substring(4, workingString.Length - 5); } + else + { + return false; + } string[] components = workingString.Split(','); From 02e7e8702ed3e9866701755edb0d4814da7ac88d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Komosi=C5=84ski?= Date: Sat, 16 Apr 2022 01:24:32 +0200 Subject: [PATCH 160/416] Allow for parsing relative points during compile time. (#7987) * Allow for parsing relative points during compile time. * Revert extra target framework. --- src/Avalonia.Base/RelativePoint.cs | 15 +++++++++--- .../Avalonia.Build.Tasks.csproj | 3 +++ .../Properties/launchSettings.json | 10 ++++++++ .../AvaloniaXamlIlLanguageParseIntrinsics.cs | 23 +++++++++++++++++++ .../AvaloniaXamlIlWellKnownTypes.cs | 7 ++++++ 5 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 src/Avalonia.Build.Tasks/Properties/launchSettings.json diff --git a/src/Avalonia.Base/RelativePoint.cs b/src/Avalonia.Base/RelativePoint.cs index 4550dbd54b..e1fd0093b6 100644 --- a/src/Avalonia.Base/RelativePoint.cs +++ b/src/Avalonia.Base/RelativePoint.cs @@ -1,7 +1,8 @@ using System; using System.Globalization; - +#if !BUILDTASK using Avalonia.Animation.Animators; +#endif using Avalonia.Utilities; namespace Avalonia @@ -10,7 +11,10 @@ namespace Avalonia /// Defines the reference point units of an or /// . /// - public enum RelativeUnit +#if !BUILDTASK + public +#endif + enum RelativeUnit { /// /// The point is expressed as a fraction of the containing element's size. @@ -26,7 +30,10 @@ namespace Avalonia /// /// Defines a point that may be defined relative to a containing element. /// - public readonly struct RelativePoint : IEquatable +#if !BUILDTASK + public +#endif + readonly struct RelativePoint : IEquatable { /// /// A point at the top left of the containing element. @@ -49,7 +56,9 @@ namespace Avalonia static RelativePoint() { +#if !BUILDTASK Animation.Animation.RegisterAnimator(prop => typeof(RelativePoint).IsAssignableFrom(prop.PropertyType)); +#endif } /// diff --git a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj index 6267c74df9..e9b99c9aa8 100644 --- a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj +++ b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj @@ -95,6 +95,9 @@ Markup/%(RecursiveDir)%(FileName)%(Extension) + + Markup/%(RecursiveDir)%(FileName)%(Extension) + diff --git a/src/Avalonia.Build.Tasks/Properties/launchSettings.json b/src/Avalonia.Build.Tasks/Properties/launchSettings.json new file mode 100644 index 0000000000..e9f5af46d6 --- /dev/null +++ b/src/Avalonia.Build.Tasks/Properties/launchSettings.json @@ -0,0 +1,10 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "profiles": { + "Compile Sandbox": { + "commandName": "Project", + "executablePath": "$(SolutionDir)\\src\\Avalonia.Build.Tasks\\bin\\Debug\\net6.0\\Avalonia.Build.Tasks.exe", + "commandLineArgs": "$(SolutionDir)\\samples\\Sandbox\\obj\\Debug\\net6.0\\Avalonia\\original.dll $(SolutionDir)\\samples\\Sandbox\\bin\\Debug\\net6.0\\Sandbox.dll.refs $(SolutionDir)\\out.dll" + } + } +} diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs index 88529ae3a0..d907bcbef9 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs @@ -160,6 +160,29 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions return true; } + if (type.Equals(types.RelativePoint)) + { + try + { + var relativePoint = RelativePoint.Parse(text); + + var relativePointTypeRef = new XamlAstClrTypeReference(node, types.RelativePoint, false); + + result = new XamlAstNewClrObjectNode(node, relativePointTypeRef, types.RelativePointFullConstructor, new List + { + new XamlConstantNode(node, types.XamlIlTypes.Double, relativePoint.Point.X), + new XamlConstantNode(node, types.XamlIlTypes.Double, relativePoint.Point.Y), + new XamlConstantNode(node, types.RelativeUnit, (int) relativePoint.Unit), + }); + + return true; + } + catch + { + throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a relative point", node); + } + } + if (type.Equals(types.GridLength)) { try diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs index 99072ace02..76f3cc071f 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs @@ -71,6 +71,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers public IXamlConstructor MatrixFullConstructor { get; } public IXamlType CornerRadius { get; } public IXamlConstructor CornerRadiusFullConstructor { get; } + public IXamlType RelativeUnit { get; } + public IXamlType RelativePoint { get; } + public IXamlConstructor RelativePointFullConstructor { get; } public IXamlType GridLength { get; } public IXamlConstructor GridLengthConstructorValueType { get; } public IXamlType Color { get; } @@ -175,6 +178,10 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers (Matrix, MatrixFullConstructor) = GetNumericTypeInfo("Avalonia.Matrix", XamlIlTypes.Double, 6); (CornerRadius, CornerRadiusFullConstructor) = GetNumericTypeInfo("Avalonia.CornerRadius", XamlIlTypes.Double, 4); + RelativeUnit = cfg.TypeSystem.GetType("Avalonia.RelativeUnit"); + RelativePoint = cfg.TypeSystem.GetType("Avalonia.RelativePoint"); + RelativePointFullConstructor = RelativePoint.GetConstructor(new List { XamlIlTypes.Double, XamlIlTypes.Double, RelativeUnit }); + GridLength = cfg.TypeSystem.GetType("Avalonia.Controls.GridLength"); GridLengthConstructorValueType = GridLength.GetConstructor(new List { XamlIlTypes.Double, cfg.TypeSystem.GetType("Avalonia.Controls.GridUnitType") }); Color = cfg.TypeSystem.GetType("Avalonia.Media.Color"); From 024dd47943e2b3c2461be965e8c102ff7fa87339 Mon Sep 17 00:00:00 2001 From: robloo Date: Fri, 15 Apr 2022 19:39:05 -0400 Subject: [PATCH 161/416] Improve color comments --- src/Avalonia.Base/Media/Color.cs | 6 +++++ src/Avalonia.Base/Media/HslColor.cs | 38 ++++++++++++++++++++++++++--- src/Avalonia.Base/Media/HsvColor.cs | 38 ++++++++++++++++++++++++++--- 3 files changed, 74 insertions(+), 8 deletions(-) diff --git a/src/Avalonia.Base/Media/Color.cs b/src/Avalonia.Base/Media/Color.cs index dc22f87d49..208e359f80 100644 --- a/src/Avalonia.Base/Media/Color.cs +++ b/src/Avalonia.Base/Media/Color.cs @@ -277,6 +277,9 @@ namespace Avalonia.Media return false; } + /// + /// Parses the given span of characters representing a hex color value into a new . + /// private static bool TryParseHexFormat(ReadOnlySpan s, out Color color) { static bool TryParseCore(ReadOnlySpan input, ref Color color) @@ -331,6 +334,9 @@ namespace Avalonia.Media return TryParseCore(input, ref color); } + /// + /// Parses the given string representing a CSS color value into a new . + /// private static bool TryParseCssFormat(string s, out Color color) { color = default; diff --git a/src/Avalonia.Base/Media/HslColor.cs b/src/Avalonia.Base/Media/HslColor.cs index 622d3ac2b7..cd98c72e3f 100644 --- a/src/Avalonia.Base/Media/HslColor.cs +++ b/src/Avalonia.Base/Media/HslColor.cs @@ -12,6 +12,7 @@ namespace Avalonia.Media { /// /// Defines a color using the hue/saturation/lightness (HSL) model. + /// This uses a cylindrical-coordinate representation of a color. /// #if !BUILDTASK public @@ -98,24 +99,53 @@ namespace Avalonia.Media } /// - /// Gets the Alpha (transparency) component in the range from 0..1. + /// Gets the Alpha (transparency) component in the range from 0..1 (percentage). /// + /// + /// + /// 0 is fully transparent. + /// 1 is fully opaque. + /// + /// public double A { get; } /// - /// Gets the Hue component in the range from 0..360. + /// Gets the Hue component in the range from 0..360 (degrees). + /// This is the color's location, in degrees, on a color wheel/circle from 0 to 360. /// Note that 360 is equivalent to 0 and will be adjusted automatically. /// + /// + /// + /// 0/360 degrees is Red. + /// 60 degrees is Yellow. + /// 120 degrees is Green. + /// 180 degrees is Cyan. + /// 240 degrees is Blue. + /// 300 degrees is Magenta. + /// + /// public double H { get; } /// - /// Gets the Saturation component in the range from 0..1. + /// Gets the Saturation component in the range from 0..1 (percentage). /// + /// + /// + /// 0 is a shade of gray (no color). + /// 1 is the full color. + /// + /// public double S { get; } /// - /// Gets the Lightness component in the range from 0..1. + /// Gets the Lightness component in the range from 0..1 (percentage). /// + /// + /// + /// 0 is fully black. + /// 1 is fully white. + /// + /// public double L { get; } /// diff --git a/src/Avalonia.Base/Media/HsvColor.cs b/src/Avalonia.Base/Media/HsvColor.cs index 1e718b5739..1ef0bcc742 100644 --- a/src/Avalonia.Base/Media/HsvColor.cs +++ b/src/Avalonia.Base/Media/HsvColor.cs @@ -12,6 +12,7 @@ namespace Avalonia.Media { /// /// Defines a color using the hue/saturation/value (HSV) model. + /// This uses a cylindrical-coordinate representation of a color. /// #if !BUILDTASK public @@ -98,24 +99,53 @@ namespace Avalonia.Media } /// - /// Gets the Alpha (transparency) component in the range from 0..1. + /// Gets the Alpha (transparency) component in the range from 0..1 (percentage). /// + /// + /// + /// 0 is fully transparent. + /// 1 is fully opaque. + /// + /// public double A { get; } /// - /// Gets the Hue component in the range from 0..360. + /// Gets the Hue component in the range from 0..360 (degrees). + /// This is the color's location, in degrees, on a color wheel/circle from 0 to 360. /// Note that 360 is equivalent to 0 and will be adjusted automatically. /// + /// + /// + /// 0/360 degrees is Red. + /// 60 degrees is Yellow. + /// 120 degrees is Green. + /// 180 degrees is Cyan. + /// 240 degrees is Blue. + /// 300 degrees is Magenta. + /// + /// public double H { get; } /// - /// Gets the Saturation component in the range from 0..1. + /// Gets the Saturation component in the range from 0..1 (percentage). /// + /// + /// + /// 0 is a shade of gray (no color). + /// 1 is the full color. + /// + /// public double S { get; } /// - /// Gets the Value component in the range from 0..1. + /// Gets the Value (or Brightness/Intensity) component in the range from 0..1 (percentage). /// + /// + /// + /// 0 is fully black and shows no color. + /// 1 is the brightest and shows full color. + /// + /// public double V { get; } /// From a7dc94b3cde14fa5faa8b5d38f2a07341a7702c3 Mon Sep 17 00:00:00 2001 From: robloo Date: Fri, 15 Apr 2022 19:54:11 -0400 Subject: [PATCH 162/416] More strictly match color prefixes --- src/Avalonia.Base/Media/Color.cs | 12 +++++++++++- src/Avalonia.Base/Media/HslColor.cs | 10 ++++++++-- src/Avalonia.Base/Media/HsvColor.cs | 10 ++++++++-- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/Avalonia.Base/Media/Color.cs b/src/Avalonia.Base/Media/Color.cs index 208e359f80..cb90404f6d 100644 --- a/src/Avalonia.Base/Media/Color.cs +++ b/src/Avalonia.Base/Media/Color.cs @@ -339,6 +339,8 @@ namespace Avalonia.Media /// private static bool TryParseCssFormat(string s, out Color color) { + bool prefixMatched = false; + color = default; if (s is null) @@ -359,13 +361,21 @@ namespace Avalonia.Media workingString.EndsWith(")", StringComparison.Ordinal)) { workingString = workingString.Substring(5, workingString.Length - 6); + prefixMatched = true; } - if (workingString.Length >= 10 && + if (prefixMatched == false && + workingString.Length >= 10 && workingString.StartsWith("rgb(", StringComparison.OrdinalIgnoreCase) && workingString.EndsWith(")", StringComparison.Ordinal)) { workingString = workingString.Substring(4, workingString.Length - 5); + prefixMatched = true; + } + + if (prefixMatched == false) + { + return false; } string[] components = workingString.Split(','); diff --git a/src/Avalonia.Base/Media/HslColor.cs b/src/Avalonia.Base/Media/HslColor.cs index cd98c72e3f..e8a4d6f94f 100644 --- a/src/Avalonia.Base/Media/HslColor.cs +++ b/src/Avalonia.Base/Media/HslColor.cs @@ -256,6 +256,8 @@ namespace Avalonia.Media /// True if parsing was successful; otherwise, false. public static bool TryParse(string s, out HslColor hslColor) { + bool prefixMatched = false; + hslColor = default; if (s is null) @@ -279,15 +281,19 @@ namespace Avalonia.Media workingString.EndsWith(")", StringComparison.Ordinal)) { workingString = workingString.Substring(5, workingString.Length - 6); + prefixMatched = true; } - if (workingString.Length >= 10 && + if (prefixMatched == false && + workingString.Length >= 10 && workingString.StartsWith("hsl(", StringComparison.OrdinalIgnoreCase) && workingString.EndsWith(")", StringComparison.Ordinal)) { workingString = workingString.Substring(4, workingString.Length - 5); + prefixMatched = true; } - else + + if (prefixMatched == false) { return false; } diff --git a/src/Avalonia.Base/Media/HsvColor.cs b/src/Avalonia.Base/Media/HsvColor.cs index 1ef0bcc742..924ef4778b 100644 --- a/src/Avalonia.Base/Media/HsvColor.cs +++ b/src/Avalonia.Base/Media/HsvColor.cs @@ -256,6 +256,8 @@ namespace Avalonia.Media /// True if parsing was successful; otherwise, false. public static bool TryParse(string s, out HsvColor hsvColor) { + bool prefixMatched = false; + hsvColor = default; if (s is null) @@ -279,15 +281,19 @@ namespace Avalonia.Media workingString.EndsWith(")", StringComparison.Ordinal)) { workingString = workingString.Substring(5, workingString.Length - 6); + prefixMatched = true; } - if (workingString.Length >= 10 && + if (prefixMatched == false && + workingString.Length >= 10 && workingString.StartsWith("hsv(", StringComparison.OrdinalIgnoreCase) && workingString.EndsWith(")", StringComparison.Ordinal)) { workingString = workingString.Substring(4, workingString.Length - 5); + prefixMatched = true; } - else + + if (prefixMatched == false) { return false; } From 248b3c87c34fb5679a8cb120211ed94066081bfa Mon Sep 17 00:00:00 2001 From: robloo Date: Fri, 15 Apr 2022 21:15:08 -0400 Subject: [PATCH 163/416] Use more standard x:Double resource --- src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml b/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml index 592581e975..6f7b14ce25 100644 --- a/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml @@ -18,7 +18,7 @@ - 12 + 12 32 From 53fc90a108ef8216e3b54d50f289e9dc3fe3f9c9 Mon Sep 17 00:00:00 2001 From: robloo Date: Fri, 15 Apr 2022 21:16:26 -0400 Subject: [PATCH 164/416] Remove Microsoft copyright An audit of git blame shows the style has now been fully rewritten for Avalonia --- .../Controls/CalendarDatePicker.xaml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml b/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml index 6f7b14ce25..c6f70d05e1 100644 --- a/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml @@ -1,10 +1,3 @@ - - Date: Fri, 15 Apr 2022 21:37:23 -0400 Subject: [PATCH 165/416] Add TemplatePart attributes for DataGrid controls --- src/Avalonia.Controls.DataGrid/DataGrid.cs | 8 ++++++++ src/Avalonia.Controls.DataGrid/DataGridCell.cs | 1 + src/Avalonia.Controls.DataGrid/DataGridRow.cs | 5 +++++ src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs | 6 ++++++ src/Avalonia.Controls.DataGrid/DataGridRowHeader.cs | 1 + 5 files changed, 21 insertions(+) diff --git a/src/Avalonia.Controls.DataGrid/DataGrid.cs b/src/Avalonia.Controls.DataGrid/DataGrid.cs index 9b67c9b096..aaac3f8f9c 100644 --- a/src/Avalonia.Controls.DataGrid/DataGrid.cs +++ b/src/Avalonia.Controls.DataGrid/DataGrid.cs @@ -32,6 +32,14 @@ namespace Avalonia.Controls /// /// Displays data in a customizable grid. /// + [TemplatePart(DATAGRID_elementBottomRightCornerHeaderName, typeof(IVisual))] + [TemplatePart(DATAGRID_elementColumnHeadersPresenterName, typeof(DataGridColumnHeadersPresenter))] + [TemplatePart(DATAGRID_elementFrozenColumnScrollBarSpacerName, typeof(Control))] + [TemplatePart(DATAGRID_elementHorizontalScrollbarName, typeof(ScrollBar))] + [TemplatePart(DATAGRID_elementRowsPresenterName, typeof(DataGridRowsPresenter))] + [TemplatePart(DATAGRID_elementTopLeftCornerHeaderName, typeof(ContentControl))] + [TemplatePart(DATAGRID_elementTopRightCornerHeaderName, typeof(ContentControl))] + [TemplatePart(DATAGRID_elementVerticalScrollbarName, typeof(ScrollBar))] [PseudoClasses(":invalid", ":empty-rows", ":empty-columns")] public partial class DataGrid : TemplatedControl { diff --git a/src/Avalonia.Controls.DataGrid/DataGridCell.cs b/src/Avalonia.Controls.DataGrid/DataGridCell.cs index e3f150f5c4..67183781d3 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridCell.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridCell.cs @@ -13,6 +13,7 @@ namespace Avalonia.Controls /// /// Represents an individual cell. /// + [TemplatePart(DATAGRIDCELL_elementRightGridLine, typeof(Rectangle))] [PseudoClasses(":selected", ":current", ":edited", ":invalid")] public class DataGridCell : ContentControl { diff --git a/src/Avalonia.Controls.DataGrid/DataGridRow.cs b/src/Avalonia.Controls.DataGrid/DataGridRow.cs index a6faec752d..db5d428942 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridRow.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridRow.cs @@ -21,6 +21,11 @@ namespace Avalonia.Controls /// /// Represents a row. /// + [TemplatePart(DATAGRIDROW_elementBottomGridLine, typeof(Rectangle))] + [TemplatePart(DATAGRIDROW_elementCells, typeof(DataGridCellsPresenter))] + [TemplatePart(DATAGRIDROW_elementDetails, typeof(DataGridDetailsPresenter))] + [TemplatePart(DATAGRIDROW_elementRoot, typeof(Panel))] + [TemplatePart(DATAGRIDROW_elementRowHeader, typeof(DataGridRowHeader))] [PseudoClasses(":selected", ":editing", ":invalid")] public class DataGridRow : TemplatedControl { diff --git a/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs b/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs index 49ca23d34c..a3dfa44fc9 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs @@ -14,6 +14,12 @@ using System.Reactive.Linq; namespace Avalonia.Controls { + [TemplatePart(DATAGRIDROWGROUPHEADER_expanderButton, typeof(ToggleButton))] + [TemplatePart(DATAGRIDROWGROUPHEADER_indentSpacer, typeof(Control))] + [TemplatePart(DATAGRIDROWGROUPHEADER_itemCountElement, typeof(TextBlock))] + [TemplatePart(DATAGRIDROWGROUPHEADER_propertyNameElement, typeof(TextBlock))] + [TemplatePart(DataGridRow.DATAGRIDROW_elementRoot, typeof(Panel))] + [TemplatePart(DataGridRow.DATAGRIDROW_elementRowHeader, typeof(DataGridRowHeader))] [PseudoClasses(":pressed", ":current", ":expanded")] public class DataGridRowGroupHeader : TemplatedControl { diff --git a/src/Avalonia.Controls.DataGrid/DataGridRowHeader.cs b/src/Avalonia.Controls.DataGrid/DataGridRowHeader.cs index 510072174f..03299bbf35 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridRowHeader.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridRowHeader.cs @@ -13,6 +13,7 @@ namespace Avalonia.Controls.Primitives /// /// Represents an individual row header. /// + [TemplatePart(DATAGRIDROWHEADER_elementRootName, typeof(Control))] [PseudoClasses(":invalid", ":selected", ":editing", ":current")] public class DataGridRowHeader : ContentControl { From 18e7a11a023b5283373303030b353a2fed8bb1c9 Mon Sep 17 00:00:00 2001 From: Steven He Date: Sat, 16 Apr 2022 15:41:16 +0800 Subject: [PATCH 166/416] Implement IncrementalGenerator --- src/Avalonia.Base/Animation/Easings/Easing.cs | 11 +- .../IsExternalInit.cs | 14 ++ .../SubtypesFactoryGenerator.cs | 234 +++++++++--------- 3 files changed, 131 insertions(+), 128 deletions(-) create mode 100644 src/Avalonia.SourceGenerator/IsExternalInit.cs diff --git a/src/Avalonia.Base/Animation/Easings/Easing.cs b/src/Avalonia.Base/Animation/Easings/Easing.cs index 6dfc4c86b3..d4f817ccc3 100644 --- a/src/Avalonia.Base/Animation/Easings/Easing.cs +++ b/src/Avalonia.Base/Animation/Easings/Easing.cs @@ -37,14 +37,9 @@ namespace Avalonia.Animation.Easings return new SplineEasing(KeySpline.Parse(e, CultureInfo.InvariantCulture)); } - if (TryCreateEasingInstance(e, out var easing)) - { - return easing; - } - else - { - throw new FormatException($"Easing \"{e}\" was not found in {Namespace} namespace."); - } + return TryCreateEasingInstance(e, out var easing) + ? easing + : throw new FormatException($"Easing \"{e}\" was not found in {Namespace} namespace."); } } } diff --git a/src/Avalonia.SourceGenerator/IsExternalInit.cs b/src/Avalonia.SourceGenerator/IsExternalInit.cs new file mode 100644 index 0000000000..c6ddf762ad --- /dev/null +++ b/src/Avalonia.SourceGenerator/IsExternalInit.cs @@ -0,0 +1,14 @@ +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; + +namespace System.Runtime.CompilerServices +{ + /// + /// Reserved to be used by the compiler for tracking metadata. + /// This class should not be used by developers in source code. + /// + [ExcludeFromCodeCoverage, DebuggerNonUserCode] + internal static class IsExternalInit + { + } +} diff --git a/src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs b/src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs index 7bc240853e..6ba1f6ac18 100644 --- a/src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs +++ b/src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs @@ -1,140 +1,48 @@ -using System.Collections.Generic; +using System; +using System.Collections.Immutable; using System.Linq; +using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Avalonia.SourceGenerator { - internal class GenerateSubtypesSyntaxReceiver : ISyntaxReceiver + [Generator(LanguageNames.CSharp)] + public class SubtypesFactoryGenerator : IIncrementalGenerator { - public List<(MethodDeclarationSyntax, AttributeSyntax)> CandidateMethods { get; } = new(); - public List Types { get; } = new(); - - public void OnVisitSyntaxNode(SyntaxNode syntaxNode) - { - if (syntaxNode is MethodDeclarationSyntax declarationSyntax) - { - foreach (var attribute in declarationSyntax.AttributeLists.SelectMany(i => i.Attributes)) - { - CandidateMethods.Add((declarationSyntax, attribute)); - } - } - - if (syntaxNode is ClassDeclarationSyntax or StructDeclarationSyntax) - { - Types.Add(syntaxNode); - } - } - } - - [Generator] - internal class SubtypesFactoryGenerator : ISourceGenerator - { - private readonly GenerateSubtypesSyntaxReceiver _receiver = new(); + private record MethodTarget(IMethodSymbol Method, ITypeSymbol BaseType, string Namespace); private static readonly string s_attributeName = typeof(SubtypesFactoryAttribute).FullName; - public void Execute(GeneratorExecutionContext context) + private static bool IsSubtypeOf(ITypeSymbol type, ITypeSymbol baseType) { - var methods = new List<(IMethodSymbol, ITypeSymbol, string)>(); - - foreach (var (method, attribute) in _receiver.CandidateMethods) - { - var semanticModel = context.Compilation.GetSemanticModel(method.SyntaxTree); - var attributeTypeInfo = semanticModel.GetTypeInfo(attribute); - if (attributeTypeInfo.Type is null || - attributeTypeInfo.Type.ToString() != s_attributeName || - attribute.ArgumentList is null) - { - continue; - } - - var arguments = attribute.ArgumentList.Arguments; - if (arguments.Count != 2) - { - continue; - } - - if (arguments[0].Expression is not TypeOfExpressionSyntax typeOfExpr || - arguments[1].Expression is not LiteralExpressionSyntax and not IdentifierNameSyntax) - { - continue; - } - - var type = semanticModel.GetTypeInfo(typeOfExpr.Type); - var ns = semanticModel.GetConstantValue(arguments[1].Expression); - var methodDeclInfo = semanticModel.GetDeclaredSymbol(method); - - if (type.Type is not ITypeSymbol baseType || - ns.HasValue is false || - ns.Value is not string nsValue || - methodDeclInfo is not IMethodSymbol methodSymbol || - methodSymbol.Parameters.Length != 2 || - methodSymbol.Parameters[1].RefKind != RefKind.Out) - { - continue; - } - - methods.Add((methodSymbol, baseType, nsValue)); - } - - var types = new List(); - foreach (var type in _receiver.Types) - { - var semanticModel = context.Compilation.GetSemanticModel(type.SyntaxTree); - var decl = semanticModel.GetDeclaredSymbol(type); - if (decl is ITypeSymbol typeSymbol) - { - types.Add(typeSymbol); - } - } - - GenerateSubTypes(context, methods, types); + return type.BaseType is not null && (SymbolEqualityComparer.Default.Equals(type.BaseType, baseType) || IsSubtypeOf(type.BaseType, baseType)); } - private bool IsSubtypeOf(ITypeSymbol type, ITypeSymbol baseType) + private static void GenerateSubTypes(SourceProductionContext context, MethodTarget methodTarget, ImmutableArray types) { - if (type.BaseType is null) - { - return false; - } + var (method, baseType, @namespace) = methodTarget; + var candidateTypes = types.Where(i => IsSubtypeOf(i, baseType)).Where(i => $"{i.ContainingNamespace}.".StartsWith($"{@namespace}.")).ToArray(); + var type = method.ContainingType; + var isGeneric = type.TypeParameters.Length > 0; + var isClass = type.TypeKind == TypeKind.Class; - if (SymbolEqualityComparer.Default.Equals(type.BaseType, baseType)) + if (method.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax() is not MethodDeclarationSyntax methodDecl) { - return true; + return; } - return IsSubtypeOf(type.BaseType, baseType); - } + var parameters = new SeparatedSyntaxList().AddRange(methodDecl.ParameterList.Parameters.Select(i => i.WithAttributeLists(new SyntaxList()))); - private void GenerateSubTypes( - GeneratorExecutionContext context, - List<(IMethodSymbol Method, ITypeSymbol BaseType, string Namespace)> methods, - List types) - { - foreach (var (method, baseType, @namespace) in methods) - { - var candidateTypes = types.Where(i => IsSubtypeOf(i, baseType)).Where(i => $"{i.ContainingNamespace}.".StartsWith($"{@namespace}.")).ToArray(); - var type = method.ContainingType; - var isGeneric = type.TypeParameters.Length > 0; - var isClass = type.TypeKind == TypeKind.Class; - - if (method.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax() is not MethodDeclarationSyntax methodDecl) - { - continue; - } + var methodDeclText = methodDecl + .WithAttributeLists(new SyntaxList()) + .WithParameterList(methodDecl.ParameterList.WithParameters(parameters)) + .WithBody(null) + .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.None)) + .WithoutTrivia().ToString(); - var parameters = new SeparatedSyntaxList().AddRange(methodDecl.ParameterList.Parameters.Select(i => i.WithAttributeLists(new SyntaxList()))); - - var methodDeclText = methodDecl - .WithAttributeLists(new SyntaxList()) - .WithParameterList(methodDecl.ParameterList.WithParameters(parameters)) - .WithBody(null) - .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.None)) - .WithoutTrivia().ToString(); - - var typeDecl = $"partial {(isClass ? "class" : "struct")} {type.Name}{(isGeneric ? $"<{string.Join(", ", type.TypeParameters)}>" : "")}"; - var source = $@"using System; + var typeDecl = $"partial {(isClass ? "class" : "struct")} {type.Name}{(isGeneric ? $"<{string.Join(", ", type.TypeParameters)}>" : "")}"; + var source = $@"using System; using System.Collections.Generic; namespace {method.ContainingNamespace} @@ -155,13 +63,99 @@ namespace {method.ContainingNamespace} }} }}"; - context.AddSource($"{type}.{method.MetadataName}.gen.cs", source); + context.AddSource($"{type}.{method.MetadataName}.gen.cs", source); + } + + private static MethodTarget? PopulateMethodTargets(GeneratorSyntaxContext context, CancellationToken token) + { + token.ThrowIfCancellationRequested(); + if (context.Node is MethodDeclarationSyntax method) + { + var attributes = method.AttributeLists.SelectMany(i => i.Attributes); + var semanticModel = context.SemanticModel; + foreach (var attribute in attributes) + { + var attributeTypeInfo = semanticModel.GetTypeInfo(attribute); + if (attributeTypeInfo.Type is null || + attributeTypeInfo.Type.ToString() != s_attributeName || + attribute.ArgumentList is null) + { + continue; + } + + var arguments = attribute.ArgumentList.Arguments; + if (arguments.Count != 2) + { + continue; + } + + if (arguments[0].Expression is not TypeOfExpressionSyntax typeOfExpr || + arguments[1].Expression is not LiteralExpressionSyntax and not IdentifierNameSyntax) + { + continue; + } + + var type = semanticModel.GetTypeInfo(typeOfExpr.Type); + var ns = semanticModel.GetConstantValue(arguments[1].Expression); + var methodDeclInfo = semanticModel.GetDeclaredSymbol(method); + + if (type.Type is not ITypeSymbol baseType || + ns.HasValue is false || + ns.Value is not string nsValue || + methodDeclInfo is not IMethodSymbol methodSymbol || + methodSymbol.Parameters.Length != 2 || + methodSymbol.Parameters[1].RefKind != RefKind.Out) + { + continue; + } + + return new MethodTarget(methodSymbol, baseType, nsValue); + } } + + return null; } - public void Initialize(GeneratorInitializationContext context) + public void Initialize(IncrementalGeneratorInitializationContext context) { - context.RegisterForSyntaxNotifications(() => _receiver); + var typesProvider = context.SyntaxProvider.CreateSyntaxProvider( + static (syntaxNode, token) => + { + token.ThrowIfCancellationRequested(); + return syntaxNode is ClassDeclarationSyntax or StructDeclarationSyntax; + }, + static (syntaxContext, token) => + { + token.ThrowIfCancellationRequested(); + return syntaxContext.Node is ClassDeclarationSyntax or StructDeclarationSyntax && + syntaxContext.SemanticModel.GetDeclaredSymbol(syntaxContext.Node) is ITypeSymbol typeSymbol + ? typeSymbol : null; + }) + .SelectMany((type, token) => + { + token.ThrowIfCancellationRequested(); + return type is null ? Array.Empty() : new ITypeSymbol[] { type }; + }); + + var methodsProvider = context.SyntaxProvider.CreateSyntaxProvider( + static (syntaxNode, token) => + { + token.ThrowIfCancellationRequested(); + return syntaxNode is ClassDeclarationSyntax or StructDeclarationSyntax or MethodDeclarationSyntax { AttributeLists.Count: > 0 }; + }, PopulateMethodTargets) + .SelectMany((method, token) => + { + token.ThrowIfCancellationRequested(); + return method is null ? Array.Empty() : new MethodTarget[] { method }; + }); + + var generateContext = methodsProvider.Combine(typesProvider.Collect()); + + context.RegisterSourceOutput(generateContext, static (sourceContext, source) => + { + sourceContext.CancellationToken.ThrowIfCancellationRequested(); + GenerateSubTypes(sourceContext, source.Left, source.Right); + }); } } } From b4a5d2e88a51e52b9fadb3f1bfe1969d73ea8826 Mon Sep 17 00:00:00 2001 From: Steven He Date: Sat, 16 Apr 2022 15:45:25 +0800 Subject: [PATCH 167/416] Use record struct --- src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs b/src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs index 6ba1f6ac18..dbdef5e089 100644 --- a/src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs +++ b/src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs @@ -11,7 +11,7 @@ namespace Avalonia.SourceGenerator [Generator(LanguageNames.CSharp)] public class SubtypesFactoryGenerator : IIncrementalGenerator { - private record MethodTarget(IMethodSymbol Method, ITypeSymbol BaseType, string Namespace); + private record struct MethodTarget(IMethodSymbol Method, ITypeSymbol BaseType, string Namespace); private static readonly string s_attributeName = typeof(SubtypesFactoryAttribute).FullName; private static bool IsSubtypeOf(ITypeSymbol type, ITypeSymbol baseType) @@ -146,7 +146,7 @@ namespace {method.ContainingNamespace} .SelectMany((method, token) => { token.ThrowIfCancellationRequested(); - return method is null ? Array.Empty() : new MethodTarget[] { method }; + return method is null ? Array.Empty() : new MethodTarget[] { method.Value }; }); var generateContext = methodsProvider.Combine(typesProvider.Collect()); From de771d6fc49cdd1b8d5043e4a6b0730ff5350698 Mon Sep 17 00:00:00 2001 From: Steven He Date: Sat, 16 Apr 2022 15:46:33 +0800 Subject: [PATCH 168/416] Remove unnecessary nodes in MethodTargets --- src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs b/src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs index dbdef5e089..10c18580b3 100644 --- a/src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs +++ b/src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs @@ -141,7 +141,7 @@ namespace {method.ContainingNamespace} static (syntaxNode, token) => { token.ThrowIfCancellationRequested(); - return syntaxNode is ClassDeclarationSyntax or StructDeclarationSyntax or MethodDeclarationSyntax { AttributeLists.Count: > 0 }; + return syntaxNode is MethodDeclarationSyntax { AttributeLists.Count: > 0 }; }, PopulateMethodTargets) .SelectMany((method, token) => { From d7a098b75e39c84db552aa620e4bd34d2d448758 Mon Sep 17 00:00:00 2001 From: Steven He Date: Sat, 16 Apr 2022 15:51:05 +0800 Subject: [PATCH 169/416] Avoid reparse source in codegen stage --- .../SubtypesFactoryGenerator.cs | 30 ++++++++----------- 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs b/src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs index 10c18580b3..4fc9397e7a 100644 --- a/src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs +++ b/src/Avalonia.SourceGenerator/SubtypesFactoryGenerator.cs @@ -11,7 +11,7 @@ namespace Avalonia.SourceGenerator [Generator(LanguageNames.CSharp)] public class SubtypesFactoryGenerator : IIncrementalGenerator { - private record struct MethodTarget(IMethodSymbol Method, ITypeSymbol BaseType, string Namespace); + private record struct MethodTarget(IMethodSymbol Method, string MethodDecl, ITypeSymbol BaseType, string Namespace); private static readonly string s_attributeName = typeof(SubtypesFactoryAttribute).FullName; private static bool IsSubtypeOf(ITypeSymbol type, ITypeSymbol baseType) @@ -21,26 +21,12 @@ namespace Avalonia.SourceGenerator private static void GenerateSubTypes(SourceProductionContext context, MethodTarget methodTarget, ImmutableArray types) { - var (method, baseType, @namespace) = methodTarget; + var (method, methodDecl, baseType, @namespace) = methodTarget; var candidateTypes = types.Where(i => IsSubtypeOf(i, baseType)).Where(i => $"{i.ContainingNamespace}.".StartsWith($"{@namespace}.")).ToArray(); var type = method.ContainingType; var isGeneric = type.TypeParameters.Length > 0; var isClass = type.TypeKind == TypeKind.Class; - if (method.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax() is not MethodDeclarationSyntax methodDecl) - { - return; - } - - var parameters = new SeparatedSyntaxList().AddRange(methodDecl.ParameterList.Parameters.Select(i => i.WithAttributeLists(new SyntaxList()))); - - var methodDeclText = methodDecl - .WithAttributeLists(new SyntaxList()) - .WithParameterList(methodDecl.ParameterList.WithParameters(parameters)) - .WithBody(null) - .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.None)) - .WithoutTrivia().ToString(); - var typeDecl = $"partial {(isClass ? "class" : "struct")} {type.Name}{(isGeneric ? $"<{string.Join(", ", type.TypeParameters)}>" : "")}"; var source = $@"using System; using System.Collections.Generic; @@ -49,7 +35,7 @@ namespace {method.ContainingNamespace} {{ {typeDecl} {{ - {methodDeclText} + {methodDecl} {{ var hasMatch = false; (hasMatch, {method.Parameters[1].Name}) = {method.Parameters[0].Name} switch @@ -109,7 +95,15 @@ namespace {method.ContainingNamespace} continue; } - return new MethodTarget(methodSymbol, baseType, nsValue); + var parameters = new SeparatedSyntaxList().AddRange(method.ParameterList.Parameters.Select(i => i.WithAttributeLists(new SyntaxList()))); + var methodDecl = method + .WithAttributeLists(new SyntaxList()) + .WithParameterList(method.ParameterList.WithParameters(parameters)) + .WithBody(null) + .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.None)) + .WithoutTrivia().ToString(); + + return new MethodTarget(methodSymbol, methodDecl, baseType, nsValue); } } From d79854ec54df61a7377cd475e012771aa49a55f2 Mon Sep 17 00:00:00 2001 From: Steven He Date: Sat, 16 Apr 2022 21:38:51 +0800 Subject: [PATCH 170/416] Remove rd.xml --- .../ControlCatalog.NetCore/ControlCatalog.NetCore.csproj | 5 ++--- samples/ControlCatalog.NetCore/rd.xml | 7 ------- 2 files changed, 2 insertions(+), 10 deletions(-) delete mode 100644 samples/ControlCatalog.NetCore/rd.xml diff --git a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj index d1b657722c..2b45ac1508 100644 --- a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj +++ b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj @@ -6,7 +6,7 @@ true - + true https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet7/nuget/v3/index.json 7.0.0-* @@ -22,12 +22,11 @@ - + - diff --git a/samples/ControlCatalog.NetCore/rd.xml b/samples/ControlCatalog.NetCore/rd.xml deleted file mode 100644 index 27db7f34ca..0000000000 --- a/samples/ControlCatalog.NetCore/rd.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file From d5e049e70a6f9d8a3c6c0fdbda8686dfa82456ee Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sat, 16 Apr 2022 17:41:38 +0300 Subject: [PATCH 171/416] Fixed path to DevAnalyzers.csproj Linux uses case-sensetive file systems, so MSBuild was unable to find the file --- build/DevAnalyzers.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/DevAnalyzers.props b/build/DevAnalyzers.props index 28959dbd47..14e4f6a563 100644 --- a/build/DevAnalyzers.props +++ b/build/DevAnalyzers.props @@ -1,6 +1,6 @@ - Date: Sat, 16 Apr 2022 22:17:12 -0400 Subject: [PATCH 172/416] Move ColorPicker source into Avalonia.Controls.ColorPicker --- .../ColorChangedEventArgs.cs | 0 .../ColorSpectrum/ColorHelpers.cs | 0 .../ColorSpectrum/ColorSpectrum.Properties.cs | 0 .../ColorSpectrum/ColorSpectrum.cs | 0 .../ColorSpectrum/Hsv.cs | 0 .../ColorSpectrum/IncrementAmount.cs | 0 .../ColorSpectrum/IncrementDirection.cs | 0 .../ColorSpectrum/Rgb.cs | 0 .../ColorSpectrumChannels.cs | 0 .../ColorSpectrumShape.cs | 0 .../HsvChannel.cs | 0 .../Themes/Default.xaml} | 10 ++-------- .../Themes/Fluent.xaml} | 10 ++-------- src/Avalonia.Themes.Default/DefaultTheme.xaml | 1 - .../Controls/FluentControls.xaml | 1 - 15 files changed, 4 insertions(+), 18 deletions(-) rename src/{Avalonia.Controls/ColorPicker => Avalonia.Controls.ColorPicker}/ColorChangedEventArgs.cs (100%) rename src/{Avalonia.Controls/ColorPicker => Avalonia.Controls.ColorPicker}/ColorSpectrum/ColorHelpers.cs (100%) rename src/{Avalonia.Controls/ColorPicker => Avalonia.Controls.ColorPicker}/ColorSpectrum/ColorSpectrum.Properties.cs (100%) rename src/{Avalonia.Controls/ColorPicker => Avalonia.Controls.ColorPicker}/ColorSpectrum/ColorSpectrum.cs (100%) rename src/{Avalonia.Controls/ColorPicker => Avalonia.Controls.ColorPicker}/ColorSpectrum/Hsv.cs (100%) rename src/{Avalonia.Controls/ColorPicker => Avalonia.Controls.ColorPicker}/ColorSpectrum/IncrementAmount.cs (100%) rename src/{Avalonia.Controls/ColorPicker => Avalonia.Controls.ColorPicker}/ColorSpectrum/IncrementDirection.cs (100%) rename src/{Avalonia.Controls/ColorPicker => Avalonia.Controls.ColorPicker}/ColorSpectrum/Rgb.cs (100%) rename src/{Avalonia.Controls/ColorPicker => Avalonia.Controls.ColorPicker}/ColorSpectrumChannels.cs (100%) rename src/{Avalonia.Controls/ColorPicker => Avalonia.Controls.ColorPicker}/ColorSpectrumShape.cs (100%) rename src/{Avalonia.Controls/ColorPicker => Avalonia.Controls.ColorPicker}/HsvChannel.cs (100%) rename src/{Avalonia.Themes.Default/Controls/ColorSpectrum.xaml => Avalonia.Controls.ColorPicker/Themes/Default.xaml} (97%) rename src/{Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml => Avalonia.Controls.ColorPicker/Themes/Fluent.xaml} (97%) diff --git a/src/Avalonia.Controls/ColorPicker/ColorChangedEventArgs.cs b/src/Avalonia.Controls.ColorPicker/ColorChangedEventArgs.cs similarity index 100% rename from src/Avalonia.Controls/ColorPicker/ColorChangedEventArgs.cs rename to src/Avalonia.Controls.ColorPicker/ColorChangedEventArgs.cs diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorHelpers.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorHelpers.cs similarity index 100% rename from src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorHelpers.cs rename to src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorHelpers.cs diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs similarity index 100% rename from src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs rename to src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs similarity index 100% rename from src/Avalonia.Controls/ColorPicker/ColorSpectrum/ColorSpectrum.cs rename to src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/Hsv.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/Hsv.cs similarity index 100% rename from src/Avalonia.Controls/ColorPicker/ColorSpectrum/Hsv.cs rename to src/Avalonia.Controls.ColorPicker/ColorSpectrum/Hsv.cs diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/IncrementAmount.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/IncrementAmount.cs similarity index 100% rename from src/Avalonia.Controls/ColorPicker/ColorSpectrum/IncrementAmount.cs rename to src/Avalonia.Controls.ColorPicker/ColorSpectrum/IncrementAmount.cs diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/IncrementDirection.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/IncrementDirection.cs similarity index 100% rename from src/Avalonia.Controls/ColorPicker/ColorSpectrum/IncrementDirection.cs rename to src/Avalonia.Controls.ColorPicker/ColorSpectrum/IncrementDirection.cs diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrum/Rgb.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/Rgb.cs similarity index 100% rename from src/Avalonia.Controls/ColorPicker/ColorSpectrum/Rgb.cs rename to src/Avalonia.Controls.ColorPicker/ColorSpectrum/Rgb.cs diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrumChannels.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrumChannels.cs similarity index 100% rename from src/Avalonia.Controls/ColorPicker/ColorSpectrumChannels.cs rename to src/Avalonia.Controls.ColorPicker/ColorSpectrumChannels.cs diff --git a/src/Avalonia.Controls/ColorPicker/ColorSpectrumShape.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrumShape.cs similarity index 100% rename from src/Avalonia.Controls/ColorPicker/ColorSpectrumShape.cs rename to src/Avalonia.Controls.ColorPicker/ColorSpectrumShape.cs diff --git a/src/Avalonia.Controls/ColorPicker/HsvChannel.cs b/src/Avalonia.Controls.ColorPicker/HsvChannel.cs similarity index 100% rename from src/Avalonia.Controls/ColorPicker/HsvChannel.cs rename to src/Avalonia.Controls.ColorPicker/HsvChannel.cs diff --git a/src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Default.xaml similarity index 97% rename from src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml rename to src/Avalonia.Controls.ColorPicker/Themes/Default.xaml index 338ea69dac..832daf8853 100644 --- a/src/Avalonia.Themes.Default/Controls/ColorSpectrum.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Default.xaml @@ -1,14 +1,8 @@ - - - - - - - @@ -136,5 +130,5 @@ - + diff --git a/src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml similarity index 97% rename from src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml rename to src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml index 5de6daea11..545702ea84 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ColorSpectrum.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml @@ -1,14 +1,8 @@ - - - - - - - @@ -136,5 +130,5 @@ - + diff --git a/src/Avalonia.Themes.Default/DefaultTheme.xaml b/src/Avalonia.Themes.Default/DefaultTheme.xaml index 682f395e5b..468b723f5b 100644 --- a/src/Avalonia.Themes.Default/DefaultTheme.xaml +++ b/src/Avalonia.Themes.Default/DefaultTheme.xaml @@ -11,7 +11,6 @@ - diff --git a/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml b/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml index b7d35ad5cc..5b217e4764 100644 --- a/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml @@ -11,7 +11,6 @@ - From cf654da43f1d4a8dae5f1e37347c724221eb4ecc Mon Sep 17 00:00:00 2001 From: robloo Date: Sat, 16 Apr 2022 22:38:49 -0400 Subject: [PATCH 173/416] Add separate Avalonia.Controls.ColorPicker project --- Avalonia.sln | 26 +++++++++++++++++++ samples/ControlCatalog/App.xaml.cs | 13 +++++++++- samples/ControlCatalog/ControlCatalog.csproj | 1 + samples/ControlCatalog/MainView.xaml.cs | 4 +++ samples/Sandbox/Sandbox.csproj | 1 + src/Avalonia.Base/Properties/AssemblyInfo.cs | 1 + .../Avalonia.Controls.ColorPicker.csproj | 21 +++++++++++++++ .../Avalonia.Diagnostics.csproj | 1 + .../Avalonia.LeakTests.csproj | 1 + 9 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 src/Avalonia.Controls.ColorPicker/Avalonia.Controls.ColorPicker.csproj diff --git a/Avalonia.sln b/Avalonia.sln index 1e2a3c6027..ea30514c3e 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -169,6 +169,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlatformSanityChecks", "sam EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.ReactiveUI.UnitTests", "tests\Avalonia.ReactiveUI.UnitTests\Avalonia.ReactiveUI.UnitTests.csproj", "{AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls.ColorPicker", "src\Avalonia.Controls.ColorPicker\Avalonia.Controls.ColorPicker.csproj", "{1ECC012A-8837-4AE2-9BDA-3E2857898727}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls.DataGrid", "src\Avalonia.Controls.DataGrid\Avalonia.Controls.DataGrid.csproj", "{3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Dialogs", "src\Avalonia.Dialogs\Avalonia.Dialogs.csproj", "{4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}" @@ -1963,6 +1965,30 @@ Global {2B390431-288C-435C-BB6B-A374033BD8D1}.Release|iPhone.Build.0 = Release|Any CPU {2B390431-288C-435C-BB6B-A374033BD8D1}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {2B390431-288C-435C-BB6B-A374033BD8D1}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|iPhone.Build.0 = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|iPhone.Build.0 = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|Any CPU.Build.0 = Release|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|iPhone.ActiveCfg = Release|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|iPhone.Build.0 = Release|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {1ECC012A-8837-4AE2-9BDA-3E2857898727}.Release|iPhoneSimulator.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/samples/ControlCatalog/App.xaml.cs b/samples/ControlCatalog/App.xaml.cs index 866fb8632a..6539cdaee6 100644 --- a/samples/ControlCatalog/App.xaml.cs +++ b/samples/ControlCatalog/App.xaml.cs @@ -18,6 +18,16 @@ namespace ControlCatalog DataContext = new ApplicationViewModel(); } + public static readonly StyleInclude ColorPickerFluent = new StyleInclude(new Uri("avares://ControlCatalog/Styles")) + { + Source = new Uri("avares://Avalonia.Controls.ColorPicker/Themes/Fluent.xaml") + }; + + public static readonly StyleInclude ColorPickerDefault = new StyleInclude(new Uri("avares://ControlCatalog/Styles")) + { + Source = new Uri("avares://Avalonia.Controls.ColorPicker/Themes/Default.xaml") + }; + public static readonly StyleInclude DataGridFluent = new StyleInclude(new Uri("avares://ControlCatalog/Styles")) { Source = new Uri("avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml") @@ -69,7 +79,8 @@ namespace ControlCatalog public override void Initialize() { Styles.Insert(0, Fluent); - Styles.Insert(1, DataGridFluent); + Styles.Insert(1, ColorPickerFluent); + Styles.Insert(2, DataGridFluent); AvaloniaXamlLoader.Load(this); } diff --git a/samples/ControlCatalog/ControlCatalog.csproj b/samples/ControlCatalog/ControlCatalog.csproj index c0e24357ca..7cbd8a3f9c 100644 --- a/samples/ControlCatalog/ControlCatalog.csproj +++ b/samples/ControlCatalog/ControlCatalog.csproj @@ -23,6 +23,7 @@ + diff --git a/samples/ControlCatalog/MainView.xaml.cs b/samples/ControlCatalog/MainView.xaml.cs index e8ea39abbb..f2d89a1325 100644 --- a/samples/ControlCatalog/MainView.xaml.cs +++ b/samples/ControlCatalog/MainView.xaml.cs @@ -50,6 +50,7 @@ namespace ControlCatalog } Application.Current.Styles[0] = App.Fluent; Application.Current.Styles[1] = App.DataGridFluent; + Application.Current.Styles.Add(App.ColorPickerFluent); } else if (theme == CatalogTheme.FluentDark) { @@ -60,18 +61,21 @@ namespace ControlCatalog } Application.Current.Styles[0] = App.Fluent; Application.Current.Styles[1] = App.DataGridFluent; + Application.Current.Styles.Add(App.ColorPickerFluent); } else if (theme == CatalogTheme.DefaultLight) { App.Default.Mode = Avalonia.Themes.Default.SimpleThemeMode.Light; Application.Current.Styles[0] = App.DefaultLight; Application.Current.Styles[1] = App.DataGridDefault; + Application.Current.Styles.Add(App.ColorPickerDefault); } else if (theme == CatalogTheme.DefaultDark) { App.Default.Mode = Avalonia.Themes.Default.SimpleThemeMode.Dark; Application.Current.Styles[0] = App.DefaultDark; Application.Current.Styles[1] = App.DataGridDefault; + Application.Current.Styles.Add(App.ColorPickerDefault); } } }; diff --git a/samples/Sandbox/Sandbox.csproj b/samples/Sandbox/Sandbox.csproj index 8f2812e048..f3c38cd96e 100644 --- a/samples/Sandbox/Sandbox.csproj +++ b/samples/Sandbox/Sandbox.csproj @@ -8,6 +8,7 @@ + diff --git a/src/Avalonia.Base/Properties/AssemblyInfo.cs b/src/Avalonia.Base/Properties/AssemblyInfo.cs index a0560924e7..2c40c768f5 100644 --- a/src/Avalonia.Base/Properties/AssemblyInfo.cs +++ b/src/Avalonia.Base/Properties/AssemblyInfo.cs @@ -19,6 +19,7 @@ using Avalonia.Metadata; [assembly: InternalsVisibleTo("Avalonia.Base.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] [assembly: InternalsVisibleTo("Avalonia.Controls, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] +[assembly: InternalsVisibleTo("Avalonia.Controls.ColorPicker, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] [assembly: InternalsVisibleTo("Avalonia.Controls.DataGrid, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] [assembly: InternalsVisibleTo("Avalonia.Controls.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] [assembly: InternalsVisibleTo("Avalonia.DesignerSupport, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] diff --git a/src/Avalonia.Controls.ColorPicker/Avalonia.Controls.ColorPicker.csproj b/src/Avalonia.Controls.ColorPicker/Avalonia.Controls.ColorPicker.csproj new file mode 100644 index 0000000000..21218fc771 --- /dev/null +++ b/src/Avalonia.Controls.ColorPicker/Avalonia.Controls.ColorPicker.csproj @@ -0,0 +1,21 @@ + + + net6.0;netstandard2.0 + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj b/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj index 2fb7c07b6f..a83e8be475 100644 --- a/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj +++ b/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj @@ -13,6 +13,7 @@ + diff --git a/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj b/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj index 0c663e1a8f..a308e1c3ed 100644 --- a/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj +++ b/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj @@ -9,6 +9,7 @@ + From 83402cb285712d5d9a5d3563f9aa21fc87bae2cc Mon Sep 17 00:00:00 2001 From: robloo Date: Sat, 16 Apr 2022 22:50:28 -0400 Subject: [PATCH 174/416] Make Color.ToHsv() internal again --- src/Avalonia.Base/Media/Color.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Avalonia.Base/Media/Color.cs b/src/Avalonia.Base/Media/Color.cs index db18ec7c6e..cb90404f6d 100644 --- a/src/Avalonia.Base/Media/Color.cs +++ b/src/Avalonia.Base/Media/Color.cs @@ -653,9 +653,6 @@ namespace Avalonia.Media (byteToDouble * alpha)); } - // TODO: Mark the below .ToHsv(double...) method internal and make internals visible to Avalonia.Controls - // It is needed for the ColorPicker which works in normalized values directly - /// /// Converts the given RGBA color component values to their HSV color equivalent. /// @@ -668,7 +665,7 @@ namespace Avalonia.Media /// The Blue component in the RGB color model within the range 0..1. /// The Alpha component in the RGB color model within the range 0..1. /// A new equivalent to the given RGBA values. - public static HsvColor ToHsv( + internal static HsvColor ToHsv( double r, double g, double b, From 9f1833030dec741137058631dcfc1e67f63a5e23 Mon Sep 17 00:00:00 2001 From: robloo Date: Sat, 16 Apr 2022 23:29:46 -0400 Subject: [PATCH 175/416] Use namespaces in XAML --- .../ControlCatalog/Pages/ColorPickerPage.xaml | 36 +++++++++---------- .../Themes/Default.xaml | 27 +++++++------- .../Themes/Fluent.xaml | 27 +++++++------- 3 files changed, 46 insertions(+), 44 deletions(-) diff --git a/samples/ControlCatalog/Pages/ColorPickerPage.xaml b/samples/ControlCatalog/Pages/ColorPickerPage.xaml index ec34193f8c..08f56be8e3 100644 --- a/samples/ControlCatalog/Pages/ColorPickerPage.xaml +++ b/samples/ControlCatalog/Pages/ColorPickerPage.xaml @@ -2,28 +2,28 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:primitives="clr-namespace:Avalonia.Controls.Primitives;assembly=Avalonia.Controls" + xmlns:cpp="clr-namespace:Avalonia.Controls.Primitives;assembly=Avalonia.Controls.ColorPicker" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="ControlCatalog.Pages.ColorPickerPage"> - - - + + + diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Default.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Default.xaml index 832daf8853..8d9b49b9b1 100644 --- a/src/Avalonia.Controls.ColorPicker/Themes/Default.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Default.xaml @@ -1,7 +1,8 @@ + xmlns:converters="using:Avalonia.Controls.Converters" + xmlns:primitive="using:Avalonia.Controls.Primitives"> @@ -9,7 +10,7 @@ - - - - - - - - - - diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml index 545702ea84..9c3a74b1fa 100644 --- a/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml @@ -1,7 +1,8 @@ + xmlns:converters="using:Avalonia.Controls.Converters" + xmlns:primitive="using:Avalonia.Controls.Primitives"> @@ -9,7 +10,7 @@ - - - - - - - - - - From f13398506d96b964fb96f834063053bdaef11f23 Mon Sep 17 00:00:00 2001 From: robloo Date: Sat, 16 Apr 2022 23:30:22 -0400 Subject: [PATCH 176/416] Fix Avalonia.Controls.ColorPicker.csproj - Disable ApiDiff tool - Enable nullable reference types --- .../Avalonia.Controls.ColorPicker.csproj | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls.ColorPicker/Avalonia.Controls.ColorPicker.csproj b/src/Avalonia.Controls.ColorPicker/Avalonia.Controls.ColorPicker.csproj index 21218fc771..03950fb168 100644 --- a/src/Avalonia.Controls.ColorPicker/Avalonia.Controls.ColorPicker.csproj +++ b/src/Avalonia.Controls.ColorPicker/Avalonia.Controls.ColorPicker.csproj @@ -3,6 +3,9 @@ net6.0;netstandard2.0 + + + @@ -16,6 +19,7 @@ - + + From 8da297b30073c05142ad3e31fb7710553db6e824 Mon Sep 17 00:00:00 2001 From: robloo Date: Sat, 16 Apr 2022 23:36:09 -0400 Subject: [PATCH 177/416] Correct ColorPicker style loading in ControlCatalog --- samples/ControlCatalog/MainView.xaml.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/samples/ControlCatalog/MainView.xaml.cs b/samples/ControlCatalog/MainView.xaml.cs index f2d89a1325..0326946c08 100644 --- a/samples/ControlCatalog/MainView.xaml.cs +++ b/samples/ControlCatalog/MainView.xaml.cs @@ -49,8 +49,8 @@ namespace ControlCatalog App.Fluent.Mode = FluentThemeMode.Light; } Application.Current.Styles[0] = App.Fluent; - Application.Current.Styles[1] = App.DataGridFluent; - Application.Current.Styles.Add(App.ColorPickerFluent); + Application.Current.Styles[1] = App.ColorPickerFluent; + Application.Current.Styles[2] = App.DataGridFluent; } else if (theme == CatalogTheme.FluentDark) { @@ -60,22 +60,22 @@ namespace ControlCatalog App.Fluent.Mode = FluentThemeMode.Dark; } Application.Current.Styles[0] = App.Fluent; - Application.Current.Styles[1] = App.DataGridFluent; - Application.Current.Styles.Add(App.ColorPickerFluent); + Application.Current.Styles[1] = App.ColorPickerFluent; + Application.Current.Styles[2] = App.DataGridFluent; } else if (theme == CatalogTheme.DefaultLight) { App.Default.Mode = Avalonia.Themes.Default.SimpleThemeMode.Light; Application.Current.Styles[0] = App.DefaultLight; - Application.Current.Styles[1] = App.DataGridDefault; - Application.Current.Styles.Add(App.ColorPickerDefault); + Application.Current.Styles[1] = App.ColorPickerDefault; + Application.Current.Styles[2] = App.DataGridDefault; } else if (theme == CatalogTheme.DefaultDark) { App.Default.Mode = Avalonia.Themes.Default.SimpleThemeMode.Dark; Application.Current.Styles[0] = App.DefaultDark; - Application.Current.Styles[1] = App.DataGridDefault; - Application.Current.Styles.Add(App.ColorPickerDefault); + Application.Current.Styles[1] = App.ColorPickerDefault; + Application.Current.Styles[2] = App.DataGridDefault; } } }; From a389f7b0d8aa82db0d04401887f72f0bca3c2e3a Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 17 Apr 2022 22:02:18 -0400 Subject: [PATCH 178/416] Fix ColorPicker project references --- src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj | 2 +- tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj b/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj index a83e8be475..adddf3f57b 100644 --- a/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj +++ b/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj @@ -13,7 +13,7 @@ - + diff --git a/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj b/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj index a308e1c3ed..a00b24bdd7 100644 --- a/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj +++ b/tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj @@ -9,7 +9,7 @@ - + From 60e0d12543aac9b8948f8657f0f64292cc219b50 Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 17 Apr 2022 22:04:56 -0400 Subject: [PATCH 179/416] Revert "Use namespaces in XAML" This reverts commit 9f1833030dec741137058631dcfc1e67f63a5e23. --- .../ControlCatalog/Pages/ColorPickerPage.xaml | 36 +++++++++---------- .../Themes/Default.xaml | 27 +++++++------- .../Themes/Fluent.xaml | 27 +++++++------- 3 files changed, 44 insertions(+), 46 deletions(-) diff --git a/samples/ControlCatalog/Pages/ColorPickerPage.xaml b/samples/ControlCatalog/Pages/ColorPickerPage.xaml index 08f56be8e3..ec34193f8c 100644 --- a/samples/ControlCatalog/Pages/ColorPickerPage.xaml +++ b/samples/ControlCatalog/Pages/ColorPickerPage.xaml @@ -2,28 +2,28 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:cpp="clr-namespace:Avalonia.Controls.Primitives;assembly=Avalonia.Controls.ColorPicker" + xmlns:primitives="clr-namespace:Avalonia.Controls.Primitives;assembly=Avalonia.Controls" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="ControlCatalog.Pages.ColorPickerPage"> - - - + + + diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Default.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Default.xaml index 8d9b49b9b1..832daf8853 100644 --- a/src/Avalonia.Controls.ColorPicker/Themes/Default.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Default.xaml @@ -1,8 +1,7 @@ + xmlns:converters="using:Avalonia.Controls.Converters"> @@ -10,7 +9,7 @@ - - - - - - - - - - diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml index 9c3a74b1fa..545702ea84 100644 --- a/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml @@ -1,8 +1,7 @@ + xmlns:converters="using:Avalonia.Controls.Converters"> @@ -10,7 +9,7 @@ - - - - - - - - - - From 8453b4a79ac85fd338f23f1941005ac0ad788ae1 Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 17 Apr 2022 22:11:23 -0400 Subject: [PATCH 180/416] Add AssemblyInfo for ColorPicker.csproj --- .../Properties/AssemblyInfo.cs | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/Avalonia.Controls.ColorPicker/Properties/AssemblyInfo.cs diff --git a/src/Avalonia.Controls.ColorPicker/Properties/AssemblyInfo.cs b/src/Avalonia.Controls.ColorPicker/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..0135541349 --- /dev/null +++ b/src/Avalonia.Controls.ColorPicker/Properties/AssemblyInfo.cs @@ -0,0 +1,8 @@ +using System.Runtime.CompilerServices; +using Avalonia.Metadata; + +[assembly: InternalsVisibleTo("Avalonia.DesignerSupport, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] + +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls")] +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Collections")] +[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Primitives")] From a66e15e8a2a3f2b8d2f9d0ae14eaaf8509436260 Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 17 Apr 2022 22:11:41 -0400 Subject: [PATCH 181/416] Add back PackageId for ColorPicker.csproj --- .../Avalonia.Controls.ColorPicker.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls.ColorPicker/Avalonia.Controls.ColorPicker.csproj b/src/Avalonia.Controls.ColorPicker/Avalonia.Controls.ColorPicker.csproj index 03950fb168..0952c899d4 100644 --- a/src/Avalonia.Controls.ColorPicker/Avalonia.Controls.ColorPicker.csproj +++ b/src/Avalonia.Controls.ColorPicker/Avalonia.Controls.ColorPicker.csproj @@ -1,7 +1,7 @@  net6.0;netstandard2.0 - + Avalonia.Controls.ColorPicker From 397fd5068fc26e7093e0316d1cfb0beb28aa1543 Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 17 Apr 2022 22:31:23 -0400 Subject: [PATCH 182/416] Rename 'channel' to 'component' --- .../ColorSpectrum/ColorHelpers.cs | 44 ++-- .../ColorSpectrum/ColorSpectrum.Properties.cs | 30 +-- .../ColorSpectrum/ColorSpectrum.cs | 231 +++++++++--------- .../ColorSpectrum/Hsv.cs | 18 +- .../ColorSpectrum/IncrementAmount.cs | 2 +- .../ColorSpectrum/IncrementDirection.cs | 2 +- .../ColorSpectrum/Rgb.cs | 20 +- ...Channels.cs => ColorSpectrumComponents.cs} | 18 +- .../HsvChannel.cs | 33 --- .../HsvComponent.cs | 47 ++++ 10 files changed, 229 insertions(+), 216 deletions(-) rename src/Avalonia.Controls.ColorPicker/{ColorSpectrumChannels.cs => ColorSpectrumComponents.cs} (81%) delete mode 100644 src/Avalonia.Controls.ColorPicker/HsvChannel.cs create mode 100644 src/Avalonia.Controls.ColorPicker/HsvComponent.cs diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorHelpers.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorHelpers.cs index cd2decf0a7..b912d39aba 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorHelpers.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorHelpers.cs @@ -26,9 +26,9 @@ namespace Avalonia.Controls.Primitives return string.Empty; } - public static Hsv IncrementColorChannel( + public static Hsv IncrementColorComponent( Hsv originalHsv, - HsvChannel channel, + HsvComponent component, IncrementDirection direction, IncrementAmount amount, bool shouldWrap, @@ -52,25 +52,25 @@ namespace Avalonia.Controls.Primitives // If we're adding a large increment, then we want to snap to the next // or previous major value - for hue, this is every increment of 30; // for saturation and value, this is every increment of 10. - switch (channel) + switch (component) { - case HsvChannel.Hue: + case HsvComponent.Hue: valueToIncrement = ref newHsv.H; incrementAmount = amount == IncrementAmount.Small ? 1 : 30; break; - case HsvChannel.Saturation: + case HsvComponent.Saturation: valueToIncrement = ref newHsv.S; incrementAmount = amount == IncrementAmount.Small ? 1 : 10; break; - case HsvChannel.Value: + case HsvComponent.Value: valueToIncrement = ref newHsv.V; incrementAmount = amount == IncrementAmount.Small ? 1 : 10; break; default: - throw new InvalidOperationException("Invalid HsvChannel."); + throw new InvalidOperationException("Invalid HsvComponent."); } double previousValue = valueToIncrement; @@ -99,14 +99,14 @@ namespace Avalonia.Controls.Primitives // While working with named colors, we're going to need to be working in actual HSV units, // so we'll divide the min bound and max bound by 100 in the case of saturation or value, // since we'll have received units between 0-100 and we need them within 0-1. - if (channel == HsvChannel.Saturation || - channel == HsvChannel.Value) + if (component == HsvComponent.Saturation || + component == HsvComponent.Value) { minBound /= 100; maxBound /= 100; } - newHsv = FindNextNamedColor(originalHsv, channel, direction, shouldWrap, minBound, maxBound); + newHsv = FindNextNamedColor(originalHsv, component, direction, shouldWrap, minBound, maxBound); } return newHsv; @@ -114,7 +114,7 @@ namespace Avalonia.Controls.Primitives public static Hsv FindNextNamedColor( Hsv originalHsv, - HsvChannel channel, + HsvComponent component, IncrementDirection direction, bool shouldWrap, double minBound, @@ -136,28 +136,28 @@ namespace Avalonia.Controls.Primitives ref double newValue = ref newHsv.H; double incrementAmount = 0.0; - switch (channel) + switch (component) { - case HsvChannel.Hue: + case HsvComponent.Hue: originalValue = originalHsv.H; newValue = ref newHsv.H; incrementAmount = 1; break; - case HsvChannel.Saturation: + case HsvComponent.Saturation: originalValue = originalHsv.S; newValue = ref newHsv.S; incrementAmount = 0.01; break; - case HsvChannel.Value: + case HsvComponent.Value: originalValue = originalHsv.V; newValue = ref newHsv.V; incrementAmount = 0.01; break; default: - throw new InvalidOperationException("Invalid HsvChannel."); + throw new InvalidOperationException("Invalid HsvComponent."); } bool shouldFindMidPoint = true; @@ -228,28 +228,28 @@ namespace Avalonia.Controls.Primitives ref double currentValue = ref currentHsv.H; double wrapIncrement = 0; - switch (channel) + switch (component) { - case HsvChannel.Hue: + case HsvComponent.Hue: startValue = ref startHsv.H; currentValue = ref currentHsv.H; wrapIncrement = 360.0; break; - case HsvChannel.Saturation: + case HsvComponent.Saturation: startValue = ref startHsv.S; currentValue = ref currentHsv.S; wrapIncrement = 1.0; break; - case HsvChannel.Value: + case HsvComponent.Value: startValue = ref startHsv.V; currentValue = ref currentHsv.V; wrapIncrement = 1.0; break; default: - throw new InvalidOperationException("Invalid HsvChannel."); + throw new InvalidOperationException("Invalid HsvComponent."); } while (newColorName == currentColorName) @@ -316,7 +316,7 @@ namespace Avalonia.Controls.Primitives return newHsv; } - public static double IncrementAlphaChannel( + public static double IncrementAlphaComponent( double originalAlpha, IncrementDirection direction, IncrementAmount amount, diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs index 71fc887bee..824bf9ab05 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs @@ -32,24 +32,24 @@ namespace Avalonia.Controls.Primitives Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF)); /// - /// Gets or sets the two HSV color channels displayed by the spectrum. + /// Gets or sets the two HSV color components displayed by the spectrum. /// /// /// Internally, the uses the HSV color model. /// - public ColorSpectrumChannels Channels + public ColorSpectrumComponents Components { - get => GetValue(ChannelsProperty); - set => SetValue(ChannelsProperty, value); + get => GetValue(ComponentsProperty); + set => SetValue(ComponentsProperty, value); } /// - /// Defines the property. + /// Defines the property. /// - public static readonly StyledProperty ChannelsProperty = - AvaloniaProperty.Register( - nameof(Channels), - ColorSpectrumChannels.HueSaturation); + public static readonly StyledProperty ComponentsProperty = + AvaloniaProperty.Register( + nameof(Components), + ColorSpectrumComponents.HueSaturation); /// /// Gets or sets the currently selected color in the HSV color model. @@ -74,7 +74,7 @@ namespace Avalonia.Controls.Primitives new HsvColor(1, 0, 0, 1)); /// - /// Gets or sets the maximum value of the Hue channel in the range from 0..359. + /// Gets or sets the maximum value of the Hue component in the range from 0..359. /// This property must be greater than . /// /// @@ -93,7 +93,7 @@ namespace Avalonia.Controls.Primitives AvaloniaProperty.Register(nameof(MaxHue), 359); /// - /// Gets or sets the maximum value of the Saturation channel in the range from 0..100. + /// Gets or sets the maximum value of the Saturation component in the range from 0..100. /// This property must be greater than . /// /// @@ -112,7 +112,7 @@ namespace Avalonia.Controls.Primitives AvaloniaProperty.Register(nameof(MaxSaturation), 100); /// - /// Gets or sets the maximum value of the Value channel in the range from 0..100. + /// Gets or sets the maximum value of the Value component in the range from 0..100. /// This property must be greater than . /// /// @@ -131,7 +131,7 @@ namespace Avalonia.Controls.Primitives AvaloniaProperty.Register(nameof(MaxValue), 100); /// - /// Gets or sets the minimum value of the Hue channel in the range from 0..359. + /// Gets or sets the minimum value of the Hue component in the range from 0..359. /// This property must be less than . /// /// @@ -150,7 +150,7 @@ namespace Avalonia.Controls.Primitives AvaloniaProperty.Register(nameof(MinHue), 0); /// - /// Gets or sets the minimum value of the Saturation channel in the range from 0..100. + /// Gets or sets the minimum value of the Saturation component in the range from 0..100. /// This property must be less than . /// /// @@ -169,7 +169,7 @@ namespace Avalonia.Controls.Primitives AvaloniaProperty.Register(nameof(MinSaturation), 0); /// - /// Gets or sets the minimum value of the Value channel in the range from 0..100. + /// Gets or sets the minimum value of the Value component in the range from 0..100. /// This property must be less than . /// /// diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs index 6779c54a1e..aecaa88f36 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -80,7 +80,7 @@ namespace Avalonia.Controls.Primitives // in order to function properly while the asynchronous bitmap creation // is in progress. private ColorSpectrumShape _shapeFromLastBitmapCreation = ColorSpectrumShape.Box; - private ColorSpectrumChannels _componentsFromLastBitmapCreation = ColorSpectrumChannels.HueSaturation; + private ColorSpectrumComponents _componentsFromLastBitmapCreation = ColorSpectrumComponents.HueSaturation; private double _imageWidthFromLastBitmapCreation = 0.0; private double _imageHeightFromLastBitmapCreation = 0.0; private int _minHueFromLastBitmapCreation = 0; @@ -99,7 +99,7 @@ namespace Avalonia.Controls.Primitives public ColorSpectrum() { _shapeFromLastBitmapCreation = Shape; - _componentsFromLastBitmapCreation = Channels; + _componentsFromLastBitmapCreation = Components; _imageWidthFromLastBitmapCreation = 0; _imageHeightFromLastBitmapCreation = 0; _minHueFromLastBitmapCreation = MinHue; @@ -219,53 +219,53 @@ namespace Avalonia.Controls.Primitives bool isControlDown = e.KeyModifiers.HasFlag(KeyModifiers.Control); - HsvChannel incrementChannel = HsvChannel.Hue; + HsvComponent incrementComponent = HsvComponent.Hue; bool isSaturationValue = false; if (key == Key.Left || key == Key.Right) { - switch (Channels) + switch (Components) { - case ColorSpectrumChannels.HueSaturation: - case ColorSpectrumChannels.HueValue: - incrementChannel = HsvChannel.Hue; + case ColorSpectrumComponents.HueSaturation: + case ColorSpectrumComponents.HueValue: + incrementComponent = HsvComponent.Hue; break; - case ColorSpectrumChannels.SaturationValue: + case ColorSpectrumComponents.SaturationValue: isSaturationValue = true; - goto case ColorSpectrumChannels.SaturationHue; - case ColorSpectrumChannels.SaturationHue: - incrementChannel = HsvChannel.Saturation; + goto case ColorSpectrumComponents.SaturationHue; + case ColorSpectrumComponents.SaturationHue: + incrementComponent = HsvComponent.Saturation; break; - case ColorSpectrumChannels.ValueHue: - case ColorSpectrumChannels.ValueSaturation: - incrementChannel = HsvChannel.Value; + case ColorSpectrumComponents.ValueHue: + case ColorSpectrumComponents.ValueSaturation: + incrementComponent = HsvComponent.Value; break; } } else if (key == Key.Up || key == Key.Down) { - switch (Channels) + switch (Components) { - case ColorSpectrumChannels.SaturationHue: - case ColorSpectrumChannels.ValueHue: - incrementChannel = HsvChannel.Hue; + case ColorSpectrumComponents.SaturationHue: + case ColorSpectrumComponents.ValueHue: + incrementComponent = HsvComponent.Hue; break; - case ColorSpectrumChannels.HueSaturation: - case ColorSpectrumChannels.ValueSaturation: - incrementChannel = HsvChannel.Saturation; + case ColorSpectrumComponents.HueSaturation: + case ColorSpectrumComponents.ValueSaturation: + incrementComponent = HsvComponent.Saturation; break; - case ColorSpectrumChannels.SaturationValue: + case ColorSpectrumComponents.SaturationValue: isSaturationValue = true; - goto case ColorSpectrumChannels.HueValue; - case ColorSpectrumChannels.HueValue: - incrementChannel = HsvChannel.Value; + goto case ColorSpectrumComponents.HueValue; + case ColorSpectrumComponents.HueValue: + incrementComponent = HsvComponent.Value; break; } } @@ -273,19 +273,19 @@ namespace Avalonia.Controls.Primitives double minBound = 0.0; double maxBound = 0.0; - switch (incrementChannel) + switch (incrementComponent) { - case HsvChannel.Hue: + case HsvComponent.Hue: minBound = MinHue; maxBound = MaxHue; break; - case HsvChannel.Saturation: + case HsvComponent.Saturation: minBound = MinSaturation; maxBound = MaxSaturation; break; - case HsvChannel.Value: + case HsvComponent.Value: minBound = MinValue; maxBound = MaxValue; break; @@ -295,8 +295,8 @@ namespace Avalonia.Controls.Primitives // so we want left and up to be lower for hue, but higher for saturation and value. // This will ensure that the icon always moves in the direction of the key press. IncrementDirection direction = - (incrementChannel == HsvChannel.Hue && (key == Key.Left || key == Key.Up)) || - (incrementChannel != HsvChannel.Hue && (key == Key.Right || key == Key.Down)) ? + (incrementComponent == HsvComponent.Hue && (key == Key.Left || key == Key.Up)) || + (incrementComponent != HsvComponent.Hue && (key == Key.Right || key == Key.Down)) ? IncrementDirection.Lower : IncrementDirection.Higher; @@ -320,9 +320,9 @@ namespace Avalonia.Controls.Primitives IncrementAmount amount = isControlDown ? IncrementAmount.Large : IncrementAmount.Small; HsvColor hsvColor = HsvColor; - UpdateColor(ColorHelpers.IncrementColorChannel( + UpdateColor(ColorHelpers.IncrementColorComponent( new Hsv(hsvColor), - incrementChannel, + incrementComponent, direction, amount, shouldWrap: true, @@ -408,12 +408,12 @@ namespace Avalonia.Controls.Primitives throw new ArgumentException("MaxHue must be between 0 and 359."); } - ColorSpectrumChannels channels = Channels; + ColorSpectrumComponents components = Components; // If hue is one of the axes in the spectrum bitmap, then we'll need to regenerate it // if the maximum or minimum value has changed. - if (channels != ColorSpectrumChannels.SaturationValue && - channels != ColorSpectrumChannels.ValueSaturation) + if (components != ColorSpectrumComponents.SaturationValue && + components != ColorSpectrumComponents.ValueSaturation) { CreateBitmapsAndColorMap(); } @@ -433,12 +433,12 @@ namespace Avalonia.Controls.Primitives throw new ArgumentException("MaxSaturation must be between 0 and 100."); } - ColorSpectrumChannels channels = Channels; + ColorSpectrumComponents components = Components; // If value is one of the axes in the spectrum bitmap, then we'll need to regenerate it // if the maximum or minimum value has changed. - if (channels != ColorSpectrumChannels.HueValue && - channels != ColorSpectrumChannels.ValueHue) + if (components != ColorSpectrumComponents.HueValue && + components != ColorSpectrumComponents.ValueHue) { CreateBitmapsAndColorMap(); } @@ -458,12 +458,12 @@ namespace Avalonia.Controls.Primitives throw new ArgumentException("MaxValue must be between 0 and 100."); } - ColorSpectrumChannels channels = Channels; + ColorSpectrumComponents components = Components; // If value is one of the axes in the spectrum bitmap, then we'll need to regenerate it // if the maximum or minimum value has changed. - if (channels != ColorSpectrumChannels.HueSaturation && - channels != ColorSpectrumChannels.SaturationHue) + if (components != ColorSpectrumComponents.HueSaturation && + components != ColorSpectrumComponents.SaturationHue) { CreateBitmapsAndColorMap(); } @@ -472,7 +472,7 @@ namespace Avalonia.Controls.Primitives { CreateBitmapsAndColorMap(); } - else if (change.Property == ChannelsProperty) + else if (change.Property == ComponentsProperty) { CreateBitmapsAndColorMap(); } @@ -617,23 +617,22 @@ namespace Avalonia.Controls.Primitives // Note: This can sometimes cause a crash -- possibly due to differences in c# rounding. Therefore, index is now clamped. Hsv hsvAtPoint = _hsvValues[MathUtilities.Clamp((y * width + x), 0, _hsvValues.Count - 1)]; - var channels = Channels; var hsvColor = HsvColor; - switch (channels) + switch (Components) { - case ColorSpectrumChannels.HueValue: - case ColorSpectrumChannels.ValueHue: + case ColorSpectrumComponents.HueValue: + case ColorSpectrumComponents.ValueHue: hsvAtPoint.S = hsvColor.S; break; - case ColorSpectrumChannels.HueSaturation: - case ColorSpectrumChannels.SaturationHue: + case ColorSpectrumComponents.HueSaturation: + case ColorSpectrumComponents.SaturationHue: hsvAtPoint.V = hsvColor.V; break; - case ColorSpectrumChannels.ValueSaturation: - case ColorSpectrumChannels.SaturationValue: + case ColorSpectrumComponents.ValueSaturation: + case ColorSpectrumComponents.SaturationValue: hsvAtPoint.H = hsvColor.H; break; } @@ -681,8 +680,8 @@ namespace Avalonia.Controls.Primitives // In the case where saturation was an axis in the spectrum with hue, or value is an axis, full stop, // we inverted the direction of that axis in order to put more hue on the outside of the ring, // so we need to do similarly here when positioning the ellipse. - if (_componentsFromLastBitmapCreation == ColorSpectrumChannels.HueSaturation || - _componentsFromLastBitmapCreation == ColorSpectrumChannels.SaturationHue) + if (_componentsFromLastBitmapCreation == ColorSpectrumComponents.HueSaturation || + _componentsFromLastBitmapCreation == ColorSpectrumComponents.SaturationHue) { sPercent = 1 - sPercent; } @@ -693,32 +692,32 @@ namespace Avalonia.Controls.Primitives switch (_componentsFromLastBitmapCreation) { - case ColorSpectrumChannels.HueValue: + case ColorSpectrumComponents.HueValue: xPercent = hPercent; yPercent = vPercent; break; - case ColorSpectrumChannels.HueSaturation: + case ColorSpectrumComponents.HueSaturation: xPercent = hPercent; yPercent = sPercent; break; - case ColorSpectrumChannels.ValueHue: + case ColorSpectrumComponents.ValueHue: xPercent = vPercent; yPercent = hPercent; break; - case ColorSpectrumChannels.ValueSaturation: + case ColorSpectrumComponents.ValueSaturation: xPercent = vPercent; yPercent = sPercent; break; - case ColorSpectrumChannels.SaturationHue: + case ColorSpectrumComponents.SaturationHue: xPercent = sPercent; yPercent = hPercent; break; - case ColorSpectrumChannels.SaturationValue: + case ColorSpectrumComponents.SaturationValue: xPercent = sPercent; yPercent = vPercent; break; @@ -757,8 +756,8 @@ namespace Avalonia.Controls.Primitives // In the case where saturation was an axis in the spectrum with hue, or value is an axis, full stop, // we inverted the direction of that axis in order to put more hue on the outside of the ring, // so we need to do similarly here when positioning the ellipse. - if (_componentsFromLastBitmapCreation == ColorSpectrumChannels.HueSaturation || - _componentsFromLastBitmapCreation == ColorSpectrumChannels.SaturationHue) + if (_componentsFromLastBitmapCreation == ColorSpectrumComponents.HueSaturation || + _componentsFromLastBitmapCreation == ColorSpectrumComponents.SaturationHue) { sThetaValue = 360 - sThetaValue; sRValue = -sRValue - 1; @@ -771,32 +770,32 @@ namespace Avalonia.Controls.Primitives switch (_componentsFromLastBitmapCreation) { - case ColorSpectrumChannels.HueValue: + case ColorSpectrumComponents.HueValue: thetaValue = hThetaValue; rValue = vRValue; break; - case ColorSpectrumChannels.HueSaturation: + case ColorSpectrumComponents.HueSaturation: thetaValue = hThetaValue; rValue = sRValue; break; - case ColorSpectrumChannels.ValueHue: + case ColorSpectrumComponents.ValueHue: thetaValue = vThetaValue; rValue = hRValue; break; - case ColorSpectrumChannels.ValueSaturation: + case ColorSpectrumComponents.ValueSaturation: thetaValue = vThetaValue; rValue = sRValue; break; - case ColorSpectrumChannels.SaturationHue: + case ColorSpectrumComponents.SaturationHue: thetaValue = sThetaValue; rValue = hRValue; break; - case ColorSpectrumChannels.SaturationValue: + case ColorSpectrumComponents.SaturationValue: thetaValue = sThetaValue; rValue = vRValue; break; @@ -932,7 +931,7 @@ namespace Avalonia.Controls.Primitives int minValue = MinValue; int maxValue = MaxValue; ColorSpectrumShape shape = Shape; - ColorSpectrumChannels channels = Channels; + ColorSpectrumComponents components = Components; // If min >= max, then by convention, min is the only number that a property can have. if (minHue >= maxHue) @@ -967,8 +966,8 @@ namespace Avalonia.Controls.Primitives bgraMinPixelData.Capacity = pixelDataSize; // We'll only save pixel data for the middle bitmaps if our third dimension is hue. - if (channels == ColorSpectrumChannels.ValueSaturation || - channels == ColorSpectrumChannels.SaturationValue) + if (components == ColorSpectrumComponents.ValueSaturation || + components == ColorSpectrumComponents.SaturationValue) { bgraMiddle1PixelData.Capacity = pixelDataSize; bgraMiddle2PixelData.Capacity = pixelDataSize; @@ -1004,7 +1003,7 @@ namespace Avalonia.Controls.Primitives for (int y = minDimensionInt - 1; y >= 0; --y) { FillPixelForBox( - x, y, hsv, minDimensionInt, channels, minHue, maxHue, minSaturation, maxSaturation, minValue, maxValue, + x, y, hsv, minDimensionInt, components, minHue, maxHue, minSaturation, maxSaturation, minValue, maxValue, bgraMinPixelData, bgraMiddle1PixelData, bgraMiddle2PixelData, bgraMiddle3PixelData, bgraMiddle4PixelData, bgraMaxPixelData, newHsvValues); } @@ -1017,7 +1016,7 @@ namespace Avalonia.Controls.Primitives for (int x = 0; x < minDimensionInt; ++x) { FillPixelForRing( - x, y, minDimensionInt / 2.0, hsv, channels, minHue, maxHue, minSaturation, maxSaturation, minValue, maxValue, + x, y, minDimensionInt / 2.0, hsv, components, minHue, maxHue, minSaturation, maxSaturation, minValue, maxValue, bgraMinPixelData, bgraMiddle1PixelData, bgraMiddle2PixelData, bgraMiddle3PixelData, bgraMiddle4PixelData, bgraMaxPixelData, newHsvValues); } @@ -1030,24 +1029,24 @@ namespace Avalonia.Controls.Primitives int pixelWidth = (int)Math.Round(minDimension); int pixelHeight = (int)Math.Round(minDimension); - ColorSpectrumChannels channels2 = Channels; + ColorSpectrumComponents components2 = Components; WriteableBitmap minBitmap = ColorHelpers.CreateBitmapFromPixelData(pixelWidth, pixelHeight, bgraMinPixelData); WriteableBitmap maxBitmap = ColorHelpers.CreateBitmapFromPixelData(pixelWidth, pixelHeight, bgraMaxPixelData); - switch (channels2) + switch (components2) { - case ColorSpectrumChannels.HueValue: - case ColorSpectrumChannels.ValueHue: + case ColorSpectrumComponents.HueValue: + case ColorSpectrumComponents.ValueHue: _saturationMinimumBitmap = minBitmap; _saturationMaximumBitmap = maxBitmap; break; - case ColorSpectrumChannels.HueSaturation: - case ColorSpectrumChannels.SaturationHue: + case ColorSpectrumComponents.HueSaturation: + case ColorSpectrumComponents.SaturationHue: _valueBitmap = maxBitmap; break; - case ColorSpectrumChannels.ValueSaturation: - case ColorSpectrumChannels.SaturationValue: + case ColorSpectrumComponents.ValueSaturation: + case ColorSpectrumComponents.SaturationValue: _hueRedBitmap = minBitmap; _hueYellowBitmap = ColorHelpers.CreateBitmapFromPixelData(pixelWidth, pixelHeight, bgraMiddle1PixelData); _hueGreenBitmap = ColorHelpers.CreateBitmapFromPixelData(pixelWidth, pixelHeight, bgraMiddle2PixelData); @@ -1058,7 +1057,7 @@ namespace Avalonia.Controls.Primitives } _shapeFromLastBitmapCreation = Shape; - _componentsFromLastBitmapCreation = Channels; + _componentsFromLastBitmapCreation = Components; _imageWidthFromLastBitmapCreation = minDimension; _imageHeightFromLastBitmapCreation = minDimension; _minHueFromLastBitmapCreation = MinHue; @@ -1080,7 +1079,7 @@ namespace Avalonia.Controls.Primitives double y, Hsv baseHsv, double minDimension, - ColorSpectrumChannels channels, + ColorSpectrumComponents components, double minHue, double maxHue, double minSaturation, @@ -1112,30 +1111,30 @@ namespace Avalonia.Controls.Primitives double xPercent = (minDimension - 1 - x) / (minDimension - 1); double yPercent = (minDimension - 1 - y) / (minDimension - 1); - switch (channels) + switch (components) { - case ColorSpectrumChannels.HueValue: + case ColorSpectrumComponents.HueValue: hsvMin.H = hsvMiddle1.H = hsvMiddle2.H = hsvMiddle3.H = hsvMiddle4.H = hsvMax.H = hMin + yPercent * (hMax - hMin); hsvMin.V = hsvMiddle1.V = hsvMiddle2.V = hsvMiddle3.V = hsvMiddle4.V = hsvMax.V = vMin + xPercent * (vMax - vMin); hsvMin.S = 0; hsvMax.S = 1; break; - case ColorSpectrumChannels.HueSaturation: + case ColorSpectrumComponents.HueSaturation: hsvMin.H = hsvMiddle1.H = hsvMiddle2.H = hsvMiddle3.H = hsvMiddle4.H = hsvMax.H = hMin + yPercent * (hMax - hMin); hsvMin.S = hsvMiddle1.S = hsvMiddle2.S = hsvMiddle3.S = hsvMiddle4.S = hsvMax.S = sMin + xPercent * (sMax - sMin); hsvMin.V = 0; hsvMax.V = 1; break; - case ColorSpectrumChannels.ValueHue: + case ColorSpectrumComponents.ValueHue: hsvMin.V = hsvMiddle1.V = hsvMiddle2.V = hsvMiddle3.V = hsvMiddle4.V = hsvMax.V = vMin + yPercent * (vMax - vMin); hsvMin.H = hsvMiddle1.H = hsvMiddle2.H = hsvMiddle3.H = hsvMiddle4.H = hsvMax.H = hMin + xPercent * (hMax - hMin); hsvMin.S = 0; hsvMax.S = 1; break; - case ColorSpectrumChannels.ValueSaturation: + case ColorSpectrumComponents.ValueSaturation: hsvMin.V = hsvMiddle1.V = hsvMiddle2.V = hsvMiddle3.V = hsvMiddle4.V = hsvMax.V = vMin + yPercent * (vMax - vMin); hsvMin.S = hsvMiddle1.S = hsvMiddle2.S = hsvMiddle3.S = hsvMiddle4.S = hsvMax.S = sMin + xPercent * (sMax - sMin); hsvMin.H = 0; @@ -1146,14 +1145,14 @@ namespace Avalonia.Controls.Primitives hsvMax.H = 300; break; - case ColorSpectrumChannels.SaturationHue: + case ColorSpectrumComponents.SaturationHue: hsvMin.S = hsvMiddle1.S = hsvMiddle2.S = hsvMiddle3.S = hsvMiddle4.S = hsvMax.S = sMin + yPercent * (sMax - sMin); hsvMin.H = hsvMiddle1.H = hsvMiddle2.H = hsvMiddle3.H = hsvMiddle4.H = hsvMax.H = hMin + xPercent * (hMax - hMin); hsvMin.V = 0; hsvMax.V = 1; break; - case ColorSpectrumChannels.SaturationValue: + case ColorSpectrumComponents.SaturationValue: hsvMin.S = hsvMiddle1.S = hsvMiddle2.S = hsvMiddle3.S = hsvMiddle4.S = hsvMax.S = sMin + yPercent * (sMax - sMin); hsvMin.V = hsvMiddle1.V = hsvMiddle2.V = hsvMiddle3.V = hsvMiddle4.V = hsvMax.V = vMin + xPercent * (vMax - vMin); hsvMin.H = 0; @@ -1171,8 +1170,8 @@ namespace Avalonia.Controls.Primitives // so we'll invert the number before assigning the HSL value to the array. // Otherwise, we'll have a very narrow section in the middle that actually has meaningful hue // in the case of the ring configuration. - if (channels == ColorSpectrumChannels.HueSaturation || - channels == ColorSpectrumChannels.SaturationHue) + if (components == ColorSpectrumComponents.HueSaturation || + components == ColorSpectrumComponents.SaturationHue) { hsvMin.S = sMax - hsvMin.S + sMin; hsvMiddle1.S = sMax - hsvMiddle1.S + sMin; @@ -1200,8 +1199,8 @@ namespace Avalonia.Controls.Primitives bgraMinPixelData.Add(255); // a - ignored // We'll only save pixel data for the middle bitmaps if our third dimension is hue. - if (channels == ColorSpectrumChannels.ValueSaturation || - channels == ColorSpectrumChannels.SaturationValue) + if (components == ColorSpectrumComponents.ValueSaturation || + components == ColorSpectrumComponents.SaturationValue) { Rgb rgbMiddle1 = hsvMiddle1.ToRgb(); bgraMiddle1PixelData.Add((byte)Math.Round(rgbMiddle1.B * 255.0)); // b @@ -1240,7 +1239,7 @@ namespace Avalonia.Controls.Primitives double y, double radius, Hsv baseHsv, - ColorSpectrumChannels channels, + ColorSpectrumComponents components, double minHue, double maxHue, double minSaturation, @@ -1298,30 +1297,30 @@ namespace Avalonia.Controls.Primitives double thetaPercent = theta / 360; - switch (channels) + switch (components) { - case ColorSpectrumChannels.HueValue: + case ColorSpectrumComponents.HueValue: hsvMin.H = hsvMiddle1.H = hsvMiddle2.H = hsvMiddle3.H = hsvMiddle4.H = hsvMax.H = hMin + thetaPercent * (hMax - hMin); hsvMin.V = hsvMiddle1.V = hsvMiddle2.V = hsvMiddle3.V = hsvMiddle4.V = hsvMax.V = vMin + r * (vMax - vMin); hsvMin.S = 0; hsvMax.S = 1; break; - case ColorSpectrumChannels.HueSaturation: + case ColorSpectrumComponents.HueSaturation: hsvMin.H = hsvMiddle1.H = hsvMiddle2.H = hsvMiddle3.H = hsvMiddle4.H = hsvMax.H = hMin + thetaPercent * (hMax - hMin); hsvMin.S = hsvMiddle1.S = hsvMiddle2.S = hsvMiddle3.S = hsvMiddle4.S = hsvMax.S = sMin + r * (sMax - sMin); hsvMin.V = 0; hsvMax.V = 1; break; - case ColorSpectrumChannels.ValueHue: + case ColorSpectrumComponents.ValueHue: hsvMin.V = hsvMiddle1.V = hsvMiddle2.V = hsvMiddle3.V = hsvMiddle4.V = hsvMax.V = vMin + thetaPercent * (vMax - vMin); hsvMin.H = hsvMiddle1.H = hsvMiddle2.H = hsvMiddle3.H = hsvMiddle4.H = hsvMax.H = hMin + r * (hMax - hMin); hsvMin.S = 0; hsvMax.S = 1; break; - case ColorSpectrumChannels.ValueSaturation: + case ColorSpectrumComponents.ValueSaturation: hsvMin.V = hsvMiddle1.V = hsvMiddle2.V = hsvMiddle3.V = hsvMiddle4.V = hsvMax.V = vMin + thetaPercent * (vMax - vMin); hsvMin.S = hsvMiddle1.S = hsvMiddle2.S = hsvMiddle3.S = hsvMiddle4.S = hsvMax.S = sMin + r * (sMax - sMin); hsvMin.H = 0; @@ -1332,14 +1331,14 @@ namespace Avalonia.Controls.Primitives hsvMax.H = 300; break; - case ColorSpectrumChannels.SaturationHue: + case ColorSpectrumComponents.SaturationHue: hsvMin.S = hsvMiddle1.S = hsvMiddle2.S = hsvMiddle3.S = hsvMiddle4.S = hsvMax.S = sMin + thetaPercent * (sMax - sMin); hsvMin.H = hsvMiddle1.H = hsvMiddle2.H = hsvMiddle3.H = hsvMiddle4.H = hsvMax.H = hMin + r * (hMax - hMin); hsvMin.V = 0; hsvMax.V = 1; break; - case ColorSpectrumChannels.SaturationValue: + case ColorSpectrumComponents.SaturationValue: hsvMin.S = hsvMiddle1.S = hsvMiddle2.S = hsvMiddle3.S = hsvMiddle4.S = hsvMax.S = sMin + thetaPercent * (sMax - sMin); hsvMin.V = hsvMiddle1.V = hsvMiddle2.V = hsvMiddle3.V = hsvMiddle4.V = hsvMax.V = vMin + r * (vMax - vMin); hsvMin.H = 0; @@ -1357,8 +1356,8 @@ namespace Avalonia.Controls.Primitives // so we'll invert the number before assigning the HSL value to the array. // Otherwise, we'll have a very narrow section in the middle that actually has meaningful hue // in the case of the ring configuration. - if (channels == ColorSpectrumChannels.HueSaturation || - channels == ColorSpectrumChannels.SaturationHue) + if (components == ColorSpectrumComponents.HueSaturation || + components == ColorSpectrumComponents.SaturationHue) { hsvMin.S = sMax - hsvMin.S + sMin; hsvMiddle1.S = sMax - hsvMiddle1.S + sMin; @@ -1386,8 +1385,8 @@ namespace Avalonia.Controls.Primitives bgraMinPixelData.Add(255); // a // We'll only save pixel data for the middle bitmaps if our third dimension is hue. - if (channels == ColorSpectrumChannels.ValueSaturation || - channels == ColorSpectrumChannels.SaturationValue) + if (components == ColorSpectrumComponents.ValueSaturation || + components == ColorSpectrumComponents.SaturationValue) { Rgb rgbMiddle1 = hsvMiddle1.ToRgb(); bgraMiddle1PixelData.Add((byte)Math.Round(rgbMiddle1.B * 255)); // b @@ -1432,7 +1431,7 @@ namespace Avalonia.Controls.Primitives } HsvColor hsvColor = HsvColor; - ColorSpectrumChannels channels = Channels; + ColorSpectrumComponents components = Components; // We'll set the base image and the overlay image based on which component is our third dimension. // If it's saturation or luminosity, then the base image is that dimension at its minimum value, @@ -1440,10 +1439,10 @@ namespace Avalonia.Controls.Primitives // If it's hue, then we'll figure out where in the color wheel we are, and then use the two // colors on either side of our position as our base image and overlay image. // For example, if our hue is orange, then the base image would be red and the overlay image yellow. - switch (channels) + switch (components) { - case ColorSpectrumChannels.HueValue: - case ColorSpectrumChannels.ValueHue: + case ColorSpectrumComponents.HueValue: + case ColorSpectrumComponents.ValueHue: { if (_saturationMinimumBitmap == null || _saturationMaximumBitmap == null) @@ -1463,8 +1462,8 @@ namespace Avalonia.Controls.Primitives } break; - case ColorSpectrumChannels.HueSaturation: - case ColorSpectrumChannels.SaturationHue: + case ColorSpectrumComponents.HueSaturation: + case ColorSpectrumComponents.SaturationHue: { if (_valueBitmap == null) { @@ -1483,8 +1482,8 @@ namespace Avalonia.Controls.Primitives } break; - case ColorSpectrumChannels.ValueSaturation: - case ColorSpectrumChannels.SaturationValue: + case ColorSpectrumComponents.ValueSaturation: + case ColorSpectrumComponents.SaturationValue: { if (_hueRedBitmap == null || _hueYellowBitmap == null || @@ -1554,13 +1553,13 @@ namespace Avalonia.Controls.Primitives // To find how much something contrasts with white, we use the equation // for relative luminance. // - // If the third channel is value, then we won't be updating the spectrum's displayed colors, + // If the third component is value, then we won't be updating the spectrum's displayed colors, // so in that case we should use a value of 1 when considering the backdrop // for the selection ellipse. Color displayedColor; - if (Channels == ColorSpectrumChannels.HueSaturation || - Channels == ColorSpectrumChannels.SaturationHue) + if (Components == ColorSpectrumComponents.HueSaturation || + Components == ColorSpectrumComponents.SaturationHue) { HsvColor hsvColor = HsvColor; Rgb color = (new Hsv(hsvColor.H, hsvColor.S, 1.0)).ToRgb(); diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/Hsv.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/Hsv.cs index d44e6b3a9f..8a425b9581 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/Hsv.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/Hsv.cs @@ -8,39 +8,39 @@ using Avalonia.Media; namespace Avalonia.Controls.Primitives { /// - /// Contains and allows modification of Hue, Saturation and Value channel values. + /// Contains and allows modification of Hue, Saturation and Value components. /// /// /// The is a specialized struct optimized for permanence and memory: /// /// This is not a read-only struct like and allows editing the fields /// Removes the alpha component unnecessary in core calculations - /// No channel value bounds checks or clamping is done. + /// No component bounds checks or clamping is done. /// /// internal struct Hsv { /// - /// The Hue channel value in the range from 0..359. + /// The Hue component in the range from 0..359. /// public double H; /// - /// The Saturation channel value in the range from 0..1. + /// The Saturation component in the range from 0..1. /// public double S; /// - /// The Value channel value in the range from 0..1. + /// The Value component in the range from 0..1. /// public double V; /// /// Initializes a new instance of the struct. /// - /// The Hue channel value in the range from 0..360. - /// The Saturation channel value in the range from 0..1. - /// The Value channel value in the range from 0..1. + /// The Hue component in the range from 0..360. + /// The Saturation component in the range from 0..1. + /// The Value component in the range from 0..1. public Hsv(double h, double s, double v) { H = h; @@ -62,7 +62,7 @@ namespace Avalonia.Controls.Primitives /// /// Converts this struct into a standard . /// - /// The Alpha channel value in the range from 0..1. + /// The Alpha component in the range from 0..1. /// A new representing this struct. public HsvColor ToHsvColor(double alpha = 1.0) { diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/IncrementAmount.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/IncrementAmount.cs index fd749cd7dc..12aca593d5 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/IncrementAmount.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/IncrementAmount.cs @@ -6,7 +6,7 @@ namespace Avalonia.Controls.Primitives { /// - /// Defines a relative amount that a color channel should be incremented. + /// Defines a relative amount that a color component should be incremented. /// internal enum IncrementAmount { diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/IncrementDirection.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/IncrementDirection.cs index 8002a44fc9..df9c1e3350 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/IncrementDirection.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/IncrementDirection.cs @@ -6,7 +6,7 @@ namespace Avalonia.Controls.Primitives { /// - /// Defines the direction a color channel should be incremented. + /// Defines the direction a color component should be incremented. /// internal enum IncrementDirection { diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/Rgb.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/Rgb.cs index 616ab9e9ac..72e3821c2b 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/Rgb.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/Rgb.cs @@ -9,40 +9,40 @@ using Avalonia.Utilities; namespace Avalonia.Controls.Primitives { /// - /// Contains and allows modification of Red, Green and Blue channel values. + /// Contains and allows modification of Red, Green and Blue components. /// /// /// The is a specialized struct optimized for permanence and memory: /// /// This is not a read-only struct like and allows editing the fields /// Removes the alpha component unnecessary in core calculations - /// Normalizes RGB channel values in the range of 0..1 to simplify calculations. - /// No channel value bounds checks or clamping is done. + /// Normalizes RGB components in the range of 0..1 to simplify calculations. + /// No component bounds checks or clamping is done. /// /// internal struct Rgb { /// - /// The Red channel value in the range from 0..1. + /// The Red component in the range from 0..1. /// public double R; /// - /// The Green channel value in the range from 0..1. + /// The Green component in the range from 0..1. /// public double G; /// - /// The Blue channel value in the range from 0..1. + /// The Blue component in the range from 0..1. /// public double B; /// /// Initializes a new instance of the struct. /// - /// The Red channel value in the range from 0..1. - /// The Green channel value in the range from 0..1. - /// The Blue channel value in the range from 0..1. + /// The Red component in the range from 0..1. + /// The Green component in the range from 0..1. + /// The Blue component in the range from 0..1. public Rgb(double r, double g, double b) { R = r; @@ -64,7 +64,7 @@ namespace Avalonia.Controls.Primitives /// /// Converts this struct into a standard . /// - /// The Alpha channel value in the range from 0..1. + /// The Alpha component in the range from 0..1. /// A new representing this struct. public Color ToColor(double alpha = 1.0) { diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrumChannels.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrumComponents.cs similarity index 81% rename from src/Avalonia.Controls.ColorPicker/ColorSpectrumChannels.cs rename to src/Avalonia.Controls.ColorPicker/ColorSpectrumComponents.cs index a31586d175..164089096e 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSpectrumChannels.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSpectrumComponents.cs @@ -8,16 +8,16 @@ using Avalonia.Controls.Primitives; namespace Avalonia.Controls { /// - /// Defines the two HSV color channels displayed by a . + /// Defines the two HSV color components displayed by a . /// /// - /// Order of the color channels is important and correspond with an X/Y axis in Box + /// Order of the color components is important and correspond with an X/Y axis in Box /// shape or a degree/radius in Ring shape. /// - public enum ColorSpectrumChannels + public enum ColorSpectrumComponents { /// - /// The Hue and Value channels. + /// The Hue and Value components. /// /// /// In Box shape, Hue is mapped to the X-axis and Value is mapped to the Y-axis. @@ -26,7 +26,7 @@ namespace Avalonia.Controls HueValue, /// - /// The Value and Hue channels. + /// The Value and Hue components. /// /// /// In Box shape, Value is mapped to the X-axis and Hue is mapped to the Y-axis. @@ -35,7 +35,7 @@ namespace Avalonia.Controls ValueHue, /// - /// The Hue and Saturation channels. + /// The Hue and Saturation components. /// /// /// In Box shape, Hue is mapped to the X-axis and Saturation is mapped to the Y-axis. @@ -44,7 +44,7 @@ namespace Avalonia.Controls HueSaturation, /// - /// The Saturation and Hue channels. + /// The Saturation and Hue components. /// /// /// In Box shape, Saturation is mapped to the X-axis and Hue is mapped to the Y-axis. @@ -53,7 +53,7 @@ namespace Avalonia.Controls SaturationHue, /// - /// The Saturation and Value channels. + /// The Saturation and Value components. /// /// /// In Box shape, Saturation is mapped to the X-axis and Value is mapped to the Y-axis. @@ -62,7 +62,7 @@ namespace Avalonia.Controls SaturationValue, /// - /// The Value and Saturation channels. + /// The Value and Saturation components. /// /// /// In Box shape, Value is mapped to the X-axis and Saturation is mapped to the Y-axis. diff --git a/src/Avalonia.Controls.ColorPicker/HsvChannel.cs b/src/Avalonia.Controls.ColorPicker/HsvChannel.cs deleted file mode 100644 index 18f96fe70b..0000000000 --- a/src/Avalonia.Controls.ColorPicker/HsvChannel.cs +++ /dev/null @@ -1,33 +0,0 @@ -// This source file is adapted from the WinUI project. -// (https://github.com/microsoft/microsoft-ui-xaml) -// -// Licensed to The Avalonia Project under the MIT License. - -namespace Avalonia.Controls -{ - /// - /// Defines a specific HSV color model channel. - /// - public enum HsvChannel - { - /// - /// The Hue channel. - /// - Hue, - - /// - /// The Saturation channel. - /// - Saturation, - - /// - /// The Value channel. - /// - Value, - - /// - /// The Alpha channel. - /// - Alpha - }; -} diff --git a/src/Avalonia.Controls.ColorPicker/HsvComponent.cs b/src/Avalonia.Controls.ColorPicker/HsvComponent.cs new file mode 100644 index 0000000000..1132bd7bbb --- /dev/null +++ b/src/Avalonia.Controls.ColorPicker/HsvComponent.cs @@ -0,0 +1,47 @@ +// This source file is adapted from the WinUI project. +// (https://github.com/microsoft/microsoft-ui-xaml) +// +// Licensed to The Avalonia Project under the MIT License. + +using Avalonia.Media; + +namespace Avalonia.Controls +{ + /// + /// Defines a specific component in the HSV color model. + /// + public enum HsvComponent + { + /// + /// The Hue component. + /// + /// + /// Also see: + /// + Hue, + + /// + /// The Saturation component. + /// + /// + /// Also see: + /// + Saturation, + + /// + /// The Value component. + /// + /// + /// Also see: + /// + Value, + + /// + /// The Alpha component. + /// + /// + /// Also see: + /// + Alpha + }; +} From a39de875aa00ab18fdd2db74121b7071c9ee2a8b Mon Sep 17 00:00:00 2001 From: AndrejBunjac Date: Mon, 18 Apr 2022 17:34:31 +0200 Subject: [PATCH 183/416] Added invalidation to LayoutThickness property in Border and ContentPresenter and implemented minor review fixes. --- src/Avalonia.Controls/Border.cs | 38 +++++++++++++++---- .../Presenters/ContentPresenter.cs | 29 ++++++++++---- src/Avalonia.Layout/Layoutable.cs | 2 +- 3 files changed, 54 insertions(+), 15 deletions(-) diff --git a/src/Avalonia.Controls/Border.cs b/src/Avalonia.Controls/Border.cs index ce64570dc8..53de95ac41 100644 --- a/src/Avalonia.Controls/Border.cs +++ b/src/Avalonia.Controls/Border.cs @@ -1,8 +1,10 @@ +using System; using Avalonia.Collections; using Avalonia.Controls.Shapes; using Avalonia.Controls.Utils; using Avalonia.Layout; using Avalonia.Media; +using Avalonia.Utilities; using Avalonia.VisualTree; namespace Avalonia.Controls @@ -88,6 +90,18 @@ namespace Avalonia.Controls AffectsMeasure(BorderThicknessProperty); } + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + switch (change.Property.Name) + { + case nameof(UseLayoutRounding): + case nameof(BorderThickness): + _layoutThickness = null; + break; + } + } + /// /// Gets or sets a brush with which to paint the background. /// @@ -169,29 +183,39 @@ namespace Avalonia.Controls set => SetValue(BoxShadowProperty, value); } - private Thickness _layoutThickness = default; + private Thickness? _layoutThickness; + private double _scale; private Thickness LayoutThickness { get { - if (_layoutThickness == default) + VerifyScale(); + + if (_layoutThickness == null) { var borderThickness = BorderThickness; if (UseLayoutRounding) - { - var scale = LayoutHelper.GetLayoutScale(this); - borderThickness = LayoutHelper.RoundLayoutThickness(BorderThickness, scale, scale); - } + borderThickness = LayoutHelper.RoundLayoutThickness(BorderThickness, _scale, _scale); _layoutThickness = borderThickness; } - return _layoutThickness; + return _layoutThickness.Value; } } + private void VerifyScale() + { + var currentScale = LayoutHelper.GetLayoutScale(this); + if (MathUtilities.AreClose(currentScale, _scale)) + return; + + _scale = currentScale; + _layoutThickness = null; + } + /// /// Renders the control. /// diff --git a/src/Avalonia.Controls/Presenters/ContentPresenter.cs b/src/Avalonia.Controls/Presenters/ContentPresenter.cs index bbb772a4ce..ae08b4a452 100644 --- a/src/Avalonia.Controls/Presenters/ContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ContentPresenter.cs @@ -8,6 +8,7 @@ using Avalonia.Layout; using Avalonia.LogicalTree; using Avalonia.Media; using Avalonia.Metadata; +using Avalonia.Utilities; namespace Avalonia.Controls.Presenters { @@ -249,6 +250,10 @@ namespace Avalonia.Controls.Presenters case nameof(TemplatedParent): TemplatedParentChanged(change); break; + case nameof(UseLayoutRounding): + case nameof(BorderThickness): + _layoutThickness = null; + break; } } @@ -329,29 +334,39 @@ namespace Avalonia.Controls.Presenters InvalidateMeasure(); } - private Thickness _layoutThickness = default; + private Thickness? _layoutThickness; + private double _scale; private Thickness LayoutThickness { get { - if (_layoutThickness == default) + VerifyScale(); + + if (_layoutThickness == null) { var borderThickness = BorderThickness; if (UseLayoutRounding) - { - var scale = LayoutHelper.GetLayoutScale(this); - borderThickness = LayoutHelper.RoundLayoutThickness(BorderThickness, scale, scale); - } + borderThickness = LayoutHelper.RoundLayoutThickness(BorderThickness, _scale, _scale); _layoutThickness = borderThickness; } - return _layoutThickness; + return _layoutThickness.Value; } } + private void VerifyScale() + { + var currentScale = LayoutHelper.GetLayoutScale(this); + if (MathUtilities.AreClose(currentScale, _scale)) + return; + + _scale = currentScale; + _layoutThickness = null; + } + /// public override void Render(DrawingContext context) { diff --git a/src/Avalonia.Layout/Layoutable.cs b/src/Avalonia.Layout/Layoutable.cs index 23a76f6ee2..df7aa937a0 100644 --- a/src/Avalonia.Layout/Layoutable.cs +++ b/src/Avalonia.Layout/Layoutable.cs @@ -653,7 +653,7 @@ namespace Avalonia.Layout // Margin has to be treated separately because the layout rounding function is not linear // f(a + b) != f(a) + f(b) // If the margin isn't pre-rounded some sizes will be offset by 1 pixel in certain scales. - if (UseLayoutRounding) + if (useLayoutRounding) { margin = LayoutHelper.RoundLayoutThickness(margin, scale, scale); } From 2902e3d24a9736484da225ec7ec15fa24b523437 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Tue, 19 Apr 2022 17:02:20 +0200 Subject: [PATCH 184/416] Rework Inlines invalidation --- .../TextFormatting/Unicode/BiDiAlgorithm.cs | 5 ++ .../Documents/IInlineHost.cs | 11 +++++ src/Avalonia.Controls/Documents/Inline.cs | 12 ++--- .../Documents/InlineCollection.cs | 48 ++++++++++++------- .../Documents/InlineUIContainer.cs | 12 +++-- src/Avalonia.Controls/Documents/LineBreak.cs | 2 +- src/Avalonia.Controls/Documents/Run.cs | 5 +- src/Avalonia.Controls/Documents/Span.cs | 9 ++-- .../Documents/TextElement.cs | 15 ++---- src/Avalonia.Controls/TextBlock.cs | 19 ++++---- 10 files changed, 77 insertions(+), 61 deletions(-) create mode 100644 src/Avalonia.Controls/Documents/IInlineHost.cs diff --git a/src/Avalonia.Base/Media/TextFormatting/Unicode/BiDiAlgorithm.cs b/src/Avalonia.Base/Media/TextFormatting/Unicode/BiDiAlgorithm.cs index d18a4b2a87..2511807d9c 100644 --- a/src/Avalonia.Base/Media/TextFormatting/Unicode/BiDiAlgorithm.cs +++ b/src/Avalonia.Base/Media/TextFormatting/Unicode/BiDiAlgorithm.cs @@ -238,6 +238,11 @@ namespace Avalonia.Media.TextFormatting.Unicode _levelRuns.Clear(); _resolvedLevelsBuffer.Clear(); + if (types.IsEmpty) + { + return; + } + // Setup original types and working types _originalClasses = types; _workingClasses = _workingClassesBuffer.Add(types); diff --git a/src/Avalonia.Controls/Documents/IInlineHost.cs b/src/Avalonia.Controls/Documents/IInlineHost.cs new file mode 100644 index 0000000000..da72c207be --- /dev/null +++ b/src/Avalonia.Controls/Documents/IInlineHost.cs @@ -0,0 +1,11 @@ +using Avalonia.LogicalTree; + +namespace Avalonia.Controls.Documents +{ + internal interface IInlineHost : ILogical + { + void AddVisualChild(IControl child); + + void Invalidate(); + } +} diff --git a/src/Avalonia.Controls/Documents/Inline.cs b/src/Avalonia.Controls/Documents/Inline.cs index 445a48ecf4..a657d754b3 100644 --- a/src/Avalonia.Controls/Documents/Inline.cs +++ b/src/Avalonia.Controls/Documents/Inline.cs @@ -1,10 +1,9 @@ using System.Collections.Generic; using System.Text; -using Avalonia.LogicalTree; using Avalonia.Media; using Avalonia.Media.TextFormatting; -namespace Avalonia.Controls.Documents +namespace Avalonia.Controls.Documents { /// /// Inline element. @@ -45,7 +44,7 @@ namespace Avalonia.Controls.Documents set { SetValue(BaselineAlignmentProperty, value); } } - internal abstract void BuildTextRun(IList textRuns, IInlinesHost parent); + internal abstract void BuildTextRun(IList textRuns); internal abstract void AppendText(StringBuilder stringBuilder); @@ -63,14 +62,9 @@ namespace Avalonia.Controls.Documents { case nameof(TextDecorations): case nameof(BaselineAlignment): - Invalidate(); + InlineHost?.Invalidate(); break; } } } - - public interface IInlinesHost : ILogical - { - void AddVisualChild(IControl child); - } } diff --git a/src/Avalonia.Controls/Documents/InlineCollection.cs b/src/Avalonia.Controls/Documents/InlineCollection.cs index abe8f2cd4d..a76222385e 100644 --- a/src/Avalonia.Controls/Documents/InlineCollection.cs +++ b/src/Avalonia.Controls/Documents/InlineCollection.cs @@ -12,29 +12,37 @@ namespace Avalonia.Controls.Documents [WhitespaceSignificantCollection] public class InlineCollection : AvaloniaList { + private readonly IInlineHost? _host; private string? _text = string.Empty; /// /// Initializes a new instance of the class. /// - public InlineCollection(ILogical parent) : base(0) + public InlineCollection(ILogical parent) : this(parent, null) { } + + /// + /// Initializes a new instance of the class. + /// + internal InlineCollection(ILogical parent, IInlineHost? host = null) : base(0) { + _host = host; + ResetBehavior = ResetBehavior.Remove; this.ForEachItem( x => { ((ISetLogicalParent)x).SetParent(parent); - x.Invalidated += Invalidate; - Invalidate(); + x.InlineHost = host; + host?.Invalidate(); }, x => { ((ISetLogicalParent)x).SetParent(null); - x.Invalidated -= Invalidate; - Invalidate(); + x.InlineHost = host; + host?.Invalidate(); }, - () => throw new NotSupportedException()); + () => throw new NotSupportedException()); } public bool HasComplexContent => Count > 0; @@ -98,22 +106,20 @@ namespace Avalonia.Controls.Documents public void Add(IControl child) { - if (!HasComplexContent && !string.IsNullOrEmpty(_text)) - { - base.Add(new Run(_text)); - - _text = string.Empty; - } + var implicitRun = new InlineUIContainer(child); - base.Add(new InlineUIContainer(child)); + Add(implicitRun); } public override void Add(Inline item) { if (!HasComplexContent) { - base.Add(new Run(_text)); - + if (!string.IsNullOrEmpty(_text)) + { + base.Add(new Run(_text)); + } + _text = string.Empty; } @@ -124,11 +130,19 @@ namespace Avalonia.Controls.Documents /// Raised when an inline in the collection changes. /// public event EventHandler? Invalidated; - + /// /// Raises the event. /// - protected void Invalidate() => Invalidated?.Invoke(this, EventArgs.Empty); + protected void Invalidate() + { + if(_host != null) + { + _host.Invalidate(); + } + + Invalidated?.Invoke(this, EventArgs.Empty); + } private void Invalidate(object? sender, EventArgs e) => Invalidate(); } diff --git a/src/Avalonia.Controls/Documents/InlineUIContainer.cs b/src/Avalonia.Controls/Documents/InlineUIContainer.cs index 47851903dd..eb12092bb8 100644 --- a/src/Avalonia.Controls/Documents/InlineUIContainer.cs +++ b/src/Avalonia.Controls/Documents/InlineUIContainer.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Text; -using Avalonia.LogicalTree; using Avalonia.Media; using Avalonia.Media.TextFormatting; using Avalonia.Metadata; @@ -58,11 +57,16 @@ namespace Avalonia.Controls.Documents set => SetValue(ChildProperty, value); } - internal override void BuildTextRun(IList textRuns, IInlinesHost parent) + internal override void BuildTextRun(IList textRuns) { - ((ISetLogicalParent)Child).SetParent(parent); + if(InlineHost == null) + { + return; + } + + ((ISetLogicalParent)Child).SetParent(InlineHost); - parent.AddVisualChild(Child); + InlineHost.AddVisualChild(Child); textRuns.Add(new InlineRun(Child, CreateTextRunProperties())); } diff --git a/src/Avalonia.Controls/Documents/LineBreak.cs b/src/Avalonia.Controls/Documents/LineBreak.cs index 00fad491d3..aeb81f7313 100644 --- a/src/Avalonia.Controls/Documents/LineBreak.cs +++ b/src/Avalonia.Controls/Documents/LineBreak.cs @@ -20,7 +20,7 @@ namespace Avalonia.Controls.Documents { } - internal override void BuildTextRun(IList textRuns, IInlinesHost parent) + internal override void BuildTextRun(IList textRuns) { textRuns.Add(new TextEndOfLine()); } diff --git a/src/Avalonia.Controls/Documents/Run.cs b/src/Avalonia.Controls/Documents/Run.cs index 884718c28b..2c6482b586 100644 --- a/src/Avalonia.Controls/Documents/Run.cs +++ b/src/Avalonia.Controls/Documents/Run.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.Text; using Avalonia.Data; -using Avalonia.LogicalTree; using Avalonia.Media.TextFormatting; using Avalonia.Metadata; @@ -51,7 +50,7 @@ namespace Avalonia.Controls.Documents set { SetValue (TextProperty, value); } } - internal override void BuildTextRun(IList textRuns, IInlinesHost parent) + internal override void BuildTextRun(IList textRuns) { var text = (Text ?? "").AsMemory(); @@ -76,7 +75,7 @@ namespace Avalonia.Controls.Documents switch (change.Property.Name) { case nameof(Text): - Invalidate(); + InlineHost?.Invalidate(); break; } } diff --git a/src/Avalonia.Controls/Documents/Span.cs b/src/Avalonia.Controls/Documents/Span.cs index 32e19d4153..bd1b4fc5e1 100644 --- a/src/Avalonia.Controls/Documents/Span.cs +++ b/src/Avalonia.Controls/Documents/Span.cs @@ -1,10 +1,8 @@ using System; using System.Collections.Generic; using System.Text; -using Avalonia.LogicalTree; using Avalonia.Media.TextFormatting; using Avalonia.Metadata; -using Avalonia.Utilities; namespace Avalonia.Controls.Documents { @@ -27,8 +25,7 @@ namespace Avalonia.Controls.Documents public Span() { Inlines = new InlineCollection(this); - - Inlines.Invalidated += (s, e) => Invalidate(); + Inlines.Invalidated += (s, e) => InlineHost?.Invalidate(); } /// @@ -37,13 +34,13 @@ namespace Avalonia.Controls.Documents [Content] public InlineCollection Inlines { get; } - internal override void BuildTextRun(IList textRuns, IInlinesHost parent) + internal override void BuildTextRun(IList textRuns) { if (Inlines.HasComplexContent) { foreach (var inline in Inlines) { - inline.BuildTextRun(textRuns, parent); + inline.BuildTextRun(textRuns); } } else diff --git a/src/Avalonia.Controls/Documents/TextElement.cs b/src/Avalonia.Controls/Documents/TextElement.cs index d8e13554b5..faf869cce6 100644 --- a/src/Avalonia.Controls/Documents/TextElement.cs +++ b/src/Avalonia.Controls/Documents/TextElement.cs @@ -1,5 +1,4 @@ -using System; -using Avalonia.Media; +using Avalonia.Media; namespace Avalonia.Controls.Documents { @@ -251,10 +250,7 @@ namespace Avalonia.Controls.Documents control.SetValue(ForegroundProperty, value); } - /// - /// Raised when the visual representation of the text element changes. - /// - public event EventHandler? Invalidated; + internal IInlineHost? InlineHost { get; set; } protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { @@ -269,14 +265,9 @@ namespace Avalonia.Controls.Documents case nameof(FontWeight): case nameof(FontStretch): case nameof(Foreground): - Invalidate(); + InlineHost?.Invalidate(); break; } } - - /// - /// Raises the event. - /// - protected void Invalidate() => Invalidated?.Invoke(this, EventArgs.Empty); } } diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs index c698f0ff3b..3bcb74eee6 100644 --- a/src/Avalonia.Controls/TextBlock.cs +++ b/src/Avalonia.Controls/TextBlock.cs @@ -14,7 +14,7 @@ namespace Avalonia.Controls /// /// A control that displays a block of text. /// - public class TextBlock : Control, IInlinesHost + public class TextBlock : Control, IInlineHost { /// /// Defines the property. @@ -155,9 +155,7 @@ namespace Avalonia.Controls /// public TextBlock() { - Inlines = new InlineCollection(this); - - Inlines.Invalidated += InlinesChanged; + Inlines = new InlineCollection(this, this); } /// @@ -211,7 +209,7 @@ namespace Avalonia.Controls } /// - /// Gets or sets the inlines. + /// Gets the inlines. /// [Content] public InlineCollection Inlines { get; } @@ -569,7 +567,7 @@ namespace Avalonia.Controls foreach (var inline in Inlines) { - inline.BuildTextRun(textRuns, this); + inline.BuildTextRun(textRuns); } textSource = new InlinesTextSource(textRuns); @@ -667,8 +665,6 @@ namespace Avalonia.Controls case nameof (Padding): case nameof (LineHeight): case nameof (MaxLines): - - case nameof (InlinesProperty): case nameof (Text): case nameof (TextDecorations): @@ -685,7 +681,7 @@ namespace Avalonia.Controls InvalidateTextLayout(); } - void IInlinesHost.AddVisualChild(IControl child) + void IInlineHost.AddVisualChild(IControl child) { if (child.VisualParent == null) { @@ -693,6 +689,11 @@ namespace Avalonia.Controls } } + void IInlineHost.Invalidate() + { + InvalidateTextLayout(); + } + private readonly struct InlinesTextSource : ITextSource { private readonly IReadOnlyList _textRuns; From 628ae788e4ce422e4576b0c6a2d49bed0ef9977d Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Tue, 19 Apr 2022 19:22:58 +0200 Subject: [PATCH 185/416] Fix PointToClient not working on macOS. --- native/Avalonia.Native/src/OSX/window.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index d16c466fe6..4426e7fdff 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -457,7 +457,7 @@ public: } point = ConvertPointY(point); - NSRect convertRect = [Window convertRectToScreen:NSMakeRect(point.X, point.Y, 0.0, 0.0)]; + NSRect convertRect = [Window convertRectFromScreen:NSMakeRect(point.X, point.Y, 0.0, 0.0)]; auto viewPoint = NSMakePoint(convertRect.origin.x, convertRect.origin.y); *ret = [View translateLocalPoint:ToAvnPoint(viewPoint)]; From 5bacd9144383ac6c43226b974f9bbeea16c6b51d Mon Sep 17 00:00:00 2001 From: Kevin Ivarsen Date: Tue, 19 Apr 2022 22:16:38 -0700 Subject: [PATCH 186/416] Fix #6603: PInvokeStackImbalance error caused by incorrect signature for WindowsDeleteString function. When PreserveSig=false, a function that natively returns an HRESULT and has no final [out] parameter should instead be marked as void. --- src/Windows/Avalonia.Win32/WinRT/NativeWinRTMethods.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Windows/Avalonia.Win32/WinRT/NativeWinRTMethods.cs b/src/Windows/Avalonia.Win32/WinRT/NativeWinRTMethods.cs index 5026fbaaba..89cde01ff4 100644 --- a/src/Windows/Avalonia.Win32/WinRT/NativeWinRTMethods.cs +++ b/src/Windows/Avalonia.Win32/WinRT/NativeWinRTMethods.cs @@ -23,7 +23,7 @@ namespace Avalonia.Win32.WinRT [DllImport("api-ms-win-core-winrt-string-l1-1-0.dll", CallingConvention = CallingConvention.StdCall, PreserveSig = false)] - internal static extern unsafe IntPtr WindowsDeleteString(IntPtr hString); + internal static extern unsafe void WindowsDeleteString(IntPtr hString); [DllImport("Windows.UI.Composition", EntryPoint = "DllGetActivationFactory", CallingConvention = CallingConvention.StdCall, PreserveSig = false)] From 050ac5fbba104fa7606cc5c3d3618f837f158731 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Wed, 20 Apr 2022 13:53:38 +0200 Subject: [PATCH 187/416] Fix line metrics for empty lines that are processed by TextWrapping --- .../Media/TextFormatting/TextFormatterImpl.cs | 26 +++++++++++++++-- .../Media/TextFormatting/TextLayout.cs | 29 ++----------------- 2 files changed, 27 insertions(+), 28 deletions(-) diff --git a/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs index 7241c62472..be07745d89 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs @@ -8,6 +8,8 @@ namespace Avalonia.Media.TextFormatting { internal class TextFormatterImpl : TextFormatter { + private static readonly char[] s_empty = { ' ' }; + /// public override TextLine FormatLine(ITextSource textSource, int firstTextSourceIndex, double paragraphWidth, TextParagraphProperties paragraphProperties, TextLineBreak? previousLineBreak = null) @@ -524,6 +526,27 @@ namespace Avalonia.Media.TextFormatting return measuredLength != 0; } + /// + /// Creates an empty text line. + /// + /// The empty text line. + public static TextLineImpl CreateEmptyTextLine(int firstTextSourceIndex, TextParagraphProperties paragraphProperties) + { + var flowDirection = paragraphProperties.FlowDirection; + var properties = paragraphProperties.DefaultTextRunProperties; + var glyphTypeface = properties.Typeface.GlyphTypeface; + var text = new ReadOnlySlice(s_empty, firstTextSourceIndex, 1); + var glyph = glyphTypeface.GetGlyph(s_empty[0]); + var glyphInfos = new[] { new GlyphInfo(glyph, firstTextSourceIndex) }; + + var shapedBuffer = new ShapedBuffer(text, glyphInfos, glyphTypeface, properties.FontRenderingEmSize, + (sbyte)flowDirection); + + var textRuns = new List { new ShapedTextCharacters(shapedBuffer, properties) }; + + return new TextLineImpl(textRuns, firstTextSourceIndex, 1, double.PositiveInfinity, paragraphProperties, flowDirection).FinalizeLine(); + } + /// /// Performs text wrapping returns a list of text lines. /// @@ -540,8 +563,7 @@ namespace Avalonia.Media.TextFormatting { if(textRuns.Count == 0) { - return new TextLineImpl(textRuns, firstTextSourceIndex, 0, paragraphWidth, paragraphProperties, flowDirection); - + return CreateEmptyTextLine(firstTextSourceIndex, paragraphProperties); } if (!TryMeasureLength(textRuns, paragraphWidth, out var measuredLength)) diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs b/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs index c6692b6203..0df608cb34 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextLayout.cs @@ -10,8 +10,6 @@ namespace Avalonia.Media.TextFormatting /// public class TextLayout { - private static readonly char[] s_empty = { ' ' }; - private readonly ITextSource _textSource; private readonly TextParagraphProperties _paragraphProperties; private readonly TextTrimming _textTrimming; @@ -408,32 +406,11 @@ namespace Avalonia.Media.TextFormatting height += textLine.Height; } - /// - /// Creates an empty text line. - /// - /// The empty text line. - private TextLine CreateEmptyTextLine(int firstTextSourceIndex) - { - var flowDirection = _paragraphProperties.FlowDirection; - var properties = _paragraphProperties.DefaultTextRunProperties; - var glyphTypeface = properties.Typeface.GlyphTypeface; - var text = new ReadOnlySlice(s_empty, firstTextSourceIndex, 1); - var glyph = glyphTypeface.GetGlyph(s_empty[0]); - var glyphInfos = new[] { new GlyphInfo(glyph, firstTextSourceIndex) }; - - var shapedBuffer = new ShapedBuffer(text, glyphInfos, glyphTypeface, properties.FontRenderingEmSize, - (sbyte)flowDirection); - - var textRuns = new List { new ShapedTextCharacters(shapedBuffer, properties) }; - - return new TextLineImpl(textRuns, firstTextSourceIndex, 1, MaxWidth, _paragraphProperties, flowDirection).FinalizeLine(); - } - private IReadOnlyList CreateTextLines() { if (MathUtilities.IsZero(MaxWidth) || MathUtilities.IsZero(MaxHeight)) { - var textLine = CreateEmptyTextLine(0); + var textLine = TextFormatterImpl.CreateEmptyTextLine(0, _paragraphProperties); Bounds = new Rect(0,0,0, textLine.Height); @@ -457,7 +434,7 @@ namespace Avalonia.Media.TextFormatting { if(previousLine != null && previousLine.NewLineLength > 0) { - var emptyTextLine = CreateEmptyTextLine(_textSourceLength); + var emptyTextLine = TextFormatterImpl.CreateEmptyTextLine(_textSourceLength, _paragraphProperties); textLines.Add(emptyTextLine); @@ -506,7 +483,7 @@ namespace Avalonia.Media.TextFormatting //Make sure the TextLayout always contains at least on empty line if(textLines.Count == 0) { - var textLine = CreateEmptyTextLine(0); + var textLine = TextFormatterImpl.CreateEmptyTextLine(0, _paragraphProperties); textLines.Add(textLine); From f9dbbb3da1d42b6450ac10b6485d118fcc5fa0aa Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 20 Apr 2022 16:35:53 +0200 Subject: [PATCH 188/416] Make UpdateDataValidation non-generic. --- src/Avalonia.Base/AvaloniaObject.cs | 12 ++++---- src/Avalonia.Controls/AutoCompleteBox.cs | 10 +++++-- src/Avalonia.Controls/Button.cs | 9 ++++-- .../Calendar/CalendarDatePicker.cs | 4 +-- src/Avalonia.Controls/MenuItem.cs | 9 ++++-- .../NumericUpDown/NumericUpDown.cs | 10 +++++-- .../Primitives/SelectingItemsControl.cs | 10 +++++-- src/Avalonia.Controls/Slider.cs | 7 +++-- src/Avalonia.Controls/TextBox.cs | 7 +++-- .../AvaloniaObjectTests_DataValidation.cs | 28 +++++++++---------- 10 files changed, 66 insertions(+), 40 deletions(-) diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs index bc1e95805f..1f14ddede4 100644 --- a/src/Avalonia.Base/AvaloniaObject.cs +++ b/src/Avalonia.Base/AvaloniaObject.cs @@ -646,10 +646,12 @@ namespace Avalonia /// enabled. /// /// The property. - /// The new binding value for the property. - protected virtual void UpdateDataValidation( - AvaloniaProperty property, - BindingValue value) + /// The current data binding state. + /// The current data binding error, if any. + protected virtual void UpdateDataValidation( + AvaloniaProperty property, + BindingValueType state, + Exception? error) { } @@ -860,7 +862,7 @@ namespace Avalonia if (metadata.EnableDataValidation == true) { - UpdateDataValidation(property, value); + UpdateDataValidation(property, value.Type, value.Error); } } diff --git a/src/Avalonia.Controls/AutoCompleteBox.cs b/src/Avalonia.Controls/AutoCompleteBox.cs index 3316c06bf5..5c95932c1f 100644 --- a/src/Avalonia.Controls/AutoCompleteBox.cs +++ b/src/Avalonia.Controls/AutoCompleteBox.cs @@ -1346,12 +1346,16 @@ namespace Avalonia.Controls /// enabled. /// /// The property. - /// The new binding value for the property. - protected override void UpdateDataValidation(AvaloniaProperty property, BindingValue value) + /// The current data binding state. + /// The current data binding error, if any. + protected override void UpdateDataValidation( + AvaloniaProperty property, + BindingValueType state, + Exception? error) { if (property == TextProperty || property == SelectedItemProperty) { - DataValidationErrors.SetError(this, value.Error); + DataValidationErrors.SetError(this, error); } } diff --git a/src/Avalonia.Controls/Button.cs b/src/Avalonia.Controls/Button.cs index a4a147e0f3..0ef1ba4c8c 100644 --- a/src/Avalonia.Controls/Button.cs +++ b/src/Avalonia.Controls/Button.cs @@ -498,12 +498,15 @@ namespace Avalonia.Controls protected override AutomationPeer OnCreateAutomationPeer() => new ButtonAutomationPeer(this); /// - protected override void UpdateDataValidation(AvaloniaProperty property, BindingValue value) + protected override void UpdateDataValidation( + AvaloniaProperty property, + BindingValueType state, + Exception? error) { - base.UpdateDataValidation(property, value); + base.UpdateDataValidation(property, state, error); if (property == CommandProperty) { - if (value.Type == BindingValueType.BindingError) + if (state == BindingValueType.BindingError) { if (_commandCanExecute) { diff --git a/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs b/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs index 0ac2056ed1..0409eb30aa 100644 --- a/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs +++ b/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs @@ -540,11 +540,11 @@ namespace Avalonia.Controls } } - protected override void UpdateDataValidation(AvaloniaProperty property, BindingValue value) + protected override void UpdateDataValidation(AvaloniaProperty property, BindingValueType state, Exception? error) { if (property == SelectedDateProperty) { - DataValidationErrors.SetError(this, value.Error); + DataValidationErrors.SetError(this, error); } } diff --git a/src/Avalonia.Controls/MenuItem.cs b/src/Avalonia.Controls/MenuItem.cs index 955af8888b..619eafb71b 100644 --- a/src/Avalonia.Controls/MenuItem.cs +++ b/src/Avalonia.Controls/MenuItem.cs @@ -501,12 +501,15 @@ namespace Avalonia.Controls return new MenuItemAutomationPeer(this); } - protected override void UpdateDataValidation(AvaloniaProperty property, BindingValue value) + protected override void UpdateDataValidation( + AvaloniaProperty property, + BindingValueType state, + Exception? error) { - base.UpdateDataValidation(property, value); + base.UpdateDataValidation(property, state, error); if (property == CommandProperty) { - _commandBindingError = value.Type == BindingValueType.BindingError; + _commandBindingError = state == BindingValueType.BindingError; if (_commandBindingError && _commandCanExecute) { _commandCanExecute = false; diff --git a/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs b/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs index fbbaab6182..4d86a0f17c 100644 --- a/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs +++ b/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs @@ -403,12 +403,16 @@ namespace Avalonia.Controls /// enabled. /// /// The property. - /// The new binding value for the property. - protected override void UpdateDataValidation(AvaloniaProperty property, BindingValue value) + /// The current data binding state. + /// The current data binding error, if any. + protected override void UpdateDataValidation( + AvaloniaProperty property, + BindingValueType state, + Exception? error) { if (property == TextProperty || property == ValueProperty) { - DataValidationErrors.SetError(this, value.Error); + DataValidationErrors.SetError(this, error); } } diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs index 6f2554bef3..bff6799792 100644 --- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs +++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs @@ -501,12 +501,16 @@ namespace Avalonia.Controls.Primitives /// enabled. /// /// The property. - /// The new binding value for the property. - protected override void UpdateDataValidation(AvaloniaProperty property, BindingValue value) + /// The current data binding state. + /// The current data binding error, if any. + protected override void UpdateDataValidation( + AvaloniaProperty property, + BindingValueType state, + Exception? error) { if (property == SelectedItemProperty) { - DataValidationErrors.SetError(this, value.Error); + DataValidationErrors.SetError(this, error); } } diff --git a/src/Avalonia.Controls/Slider.cs b/src/Avalonia.Controls/Slider.cs index f0a0fba1af..64dfce22d4 100644 --- a/src/Avalonia.Controls/Slider.cs +++ b/src/Avalonia.Controls/Slider.cs @@ -361,11 +361,14 @@ namespace Avalonia.Controls Value = IsSnapToTickEnabled ? SnapToTick(finalValue) : finalValue; } - protected override void UpdateDataValidation(AvaloniaProperty property, BindingValue value) + protected override void UpdateDataValidation( + AvaloniaProperty property, + BindingValueType state, + Exception? error) { if (property == ValueProperty) { - DataValidationErrors.SetError(this, value.Error); + DataValidationErrors.SetError(this, error); } } diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 1f3dbc87db..0be58e7fcc 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -1262,11 +1262,14 @@ namespace Avalonia.Controls return new TextBoxAutomationPeer(this); } - protected override void UpdateDataValidation(AvaloniaProperty property, BindingValue value) + protected override void UpdateDataValidation( + AvaloniaProperty property, + BindingValueType state, + Exception? error) { if (property == TextProperty) { - DataValidationErrors.SetError(this, value.Error); + DataValidationErrors.SetError(this, error); } } diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_DataValidation.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_DataValidation.cs index 65f03b3eca..d48e58136a 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_DataValidation.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_DataValidation.cs @@ -52,14 +52,14 @@ namespace Avalonia.Base.UnitTests source.OnNext(BindingValue.DataValidationError(new Exception())); source.OnNext(7); - var result = target.Notifications.Cast>().ToList(); + var result = target.Notifications; Assert.Equal(4, result.Count); - Assert.Equal(BindingValueType.Value, result[0].Type); - Assert.Equal(6, result[0].Value); - Assert.Equal(BindingValueType.BindingError, result[1].Type); - Assert.Equal(BindingValueType.DataValidationError, result[2].Type); - Assert.Equal(BindingValueType.Value, result[3].Type); - Assert.Equal(7, result[3].Value); + Assert.Equal(BindingValueType.Value, result[0].type); + Assert.Equal(6, result[0].value); + Assert.Equal(BindingValueType.BindingError, result[1].type); + Assert.Equal(BindingValueType.DataValidationError, result[2].type); + Assert.Equal(BindingValueType.Value, result[3].type); + Assert.Equal(7, result[3].value); } [Fact] @@ -72,8 +72,7 @@ namespace Avalonia.Base.UnitTests target.Bind(Class1.NonValidatedDirectProperty, source); source.OnNext(1); - var result = target.Notifications.Cast>().ToList(); - Assert.Equal(1, result.Count); + Assert.Equal(1, target.Notifications.Count); } [Fact] @@ -154,13 +153,14 @@ namespace Avalonia.Base.UnitTests set { SetAndRaise(ValidatedDirectStringProperty, ref _directString, value); } } - public IList Notifications { get; } = new List(); + public List<(BindingValueType type, object value)> Notifications { get; } = new(); - protected override void UpdateDataValidation( - AvaloniaProperty property, - BindingValue value) + protected override void UpdateDataValidation( + AvaloniaProperty property, + BindingValueType state, + Exception error) { - Notifications.Add(value); + Notifications.Add((state, GetValue(property))); } } From 7f469752d55097d4dcf014e23fd2f0798f199e68 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 20 Apr 2022 16:43:20 +0200 Subject: [PATCH 189/416] Remove generic methods from IInteractive. --- .../Interactivity/IInteractive.cs | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/src/Avalonia.Base/Interactivity/IInteractive.cs b/src/Avalonia.Base/Interactivity/IInteractive.cs index afda29e329..6d7dcd64f4 100644 --- a/src/Avalonia.Base/Interactivity/IInteractive.cs +++ b/src/Avalonia.Base/Interactivity/IInteractive.cs @@ -28,21 +28,6 @@ namespace Avalonia.Interactivity RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble, bool handledEventsToo = false); - /// - /// Adds a handler for the specified routed event. - /// - /// The type of the event's args. - /// The routed event. - /// The handler. - /// The routing strategies to listen to. - /// Whether handled events should also be listened for. - /// A disposable that terminates the event subscription. - void AddHandler( - RoutedEvent routedEvent, - EventHandler handler, - RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble, - bool handledEventsToo = false) where TEventArgs : RoutedEventArgs; - /// /// Removes a handler for the specified routed event. /// @@ -50,15 +35,6 @@ namespace Avalonia.Interactivity /// The handler. void RemoveHandler(RoutedEvent routedEvent, Delegate handler); - /// - /// Removes a handler for the specified routed event. - /// - /// The type of the event's args. - /// The routed event. - /// The handler. - void RemoveHandler(RoutedEvent routedEvent, EventHandler handler) - where TEventArgs : RoutedEventArgs; - /// /// Adds the object's handlers for a routed event to an event route. /// From 4ec36c97e2810c8e1bb48f85339d76e96e502b78 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 20 Apr 2022 16:47:54 +0200 Subject: [PATCH 190/416] Remove generic methods from IDispatcher. --- src/Avalonia.Base/Threading/IDispatcher.cs | 26 ---------------------- 1 file changed, 26 deletions(-) diff --git a/src/Avalonia.Base/Threading/IDispatcher.cs b/src/Avalonia.Base/Threading/IDispatcher.cs index eccd42bd4e..713a7ac4d7 100644 --- a/src/Avalonia.Base/Threading/IDispatcher.cs +++ b/src/Avalonia.Base/Threading/IDispatcher.cs @@ -26,15 +26,6 @@ namespace Avalonia.Threading /// The priority with which to invoke the method. void Post(Action action, DispatcherPriority priority = default); - /// - /// Posts an action that will be invoked on the dispatcher thread. - /// - /// type of argument - /// The method to call. - /// The argument of method to call. - /// The priority with which to invoke the method. - void Post(Action action, T arg, DispatcherPriority priority = default); - /// /// Invokes a action on the dispatcher thread. /// @@ -43,14 +34,6 @@ namespace Avalonia.Threading /// A task that can be used to track the method's execution. Task InvokeAsync(Action action, DispatcherPriority priority = default); - /// - /// Invokes a method on the dispatcher thread. - /// - /// The method. - /// The priority with which to invoke the method. - /// A task that can be used to track the method's execution. - Task InvokeAsync(Func function, DispatcherPriority priority = default); - /// /// Queues the specified work to run on the dispatcher thread and returns a proxy for the /// task returned by . @@ -59,14 +42,5 @@ namespace Avalonia.Threading /// The priority with which to invoke the method. /// A task that represents a proxy for the task returned by . Task InvokeAsync(Func function, DispatcherPriority priority = default); - - /// - /// Queues the specified work to run on the dispatcher thread and returns a proxy for the - /// task returned by . - /// - /// The work to execute asynchronously. - /// The priority with which to invoke the method. - /// A task that represents a proxy for the task returned by . - Task InvokeAsync(Func> function, DispatcherPriority priority = default); } } From fa44075d26873d80b6fba5df8492d774fadec589 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 20 Apr 2022 17:19:02 +0200 Subject: [PATCH 191/416] Make utility methods non-virtual. Not sure why these method were virtual anyway: any customization should be done by overriding the non-generic `CreateItemContainerGenerator` method. --- src/Avalonia.Controls/TreeView.cs | 2 +- src/Avalonia.Controls/TreeViewItem.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/TreeView.cs b/src/Avalonia.Controls/TreeView.cs index 1d806913dd..b2a188a2ea 100644 --- a/src/Avalonia.Controls/TreeView.cs +++ b/src/Avalonia.Controls/TreeView.cs @@ -401,7 +401,7 @@ namespace Avalonia.Controls protected virtual ITreeItemContainerGenerator CreateTreeItemContainerGenerator() => CreateTreeItemContainerGenerator(); - protected virtual ITreeItemContainerGenerator CreateTreeItemContainerGenerator() where TVItem: TreeViewItem, new() + protected ITreeItemContainerGenerator CreateTreeItemContainerGenerator() where TVItem: TreeViewItem, new() { return new TreeItemContainerGenerator( this, diff --git a/src/Avalonia.Controls/TreeViewItem.cs b/src/Avalonia.Controls/TreeViewItem.cs index a0a3c09942..490b0b3ce3 100644 --- a/src/Avalonia.Controls/TreeViewItem.cs +++ b/src/Avalonia.Controls/TreeViewItem.cs @@ -96,7 +96,7 @@ namespace Avalonia.Controls protected override IItemContainerGenerator CreateItemContainerGenerator() => CreateTreeItemContainerGenerator(); /// - protected virtual ITreeItemContainerGenerator CreateTreeItemContainerGenerator() + protected ITreeItemContainerGenerator CreateTreeItemContainerGenerator() where TVItem: TreeViewItem, new() { return new TreeItemContainerGenerator( From 6df672e4c078bae8dd0259825032af68824f3f08 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 20 Apr 2022 22:30:45 +0200 Subject: [PATCH 192/416] Remove IAvaloniaPropertyVisitor. It was only used internally in creating `ISetterInstance`s so add a virtual method to `AvaloniaProperty` to do this explicitly without generic virtual methods. --- src/Avalonia.Base/AvaloniaProperty.cs | 11 +--- src/Avalonia.Base/DirectPropertyBase.cs | 32 +++++++-- src/Avalonia.Base/StyledPropertyBase.cs | 32 +++++++-- src/Avalonia.Base/Styling/Setter.cs | 65 +------------------ .../Utilities/IAvaloniaPropertyVisitor.cs | 32 --------- .../AvaloniaPropertyTests.cs | 11 ++-- 6 files changed, 62 insertions(+), 121 deletions(-) delete mode 100644 src/Avalonia.Base/Utilities/IAvaloniaPropertyVisitor.cs diff --git a/src/Avalonia.Base/AvaloniaProperty.cs b/src/Avalonia.Base/AvaloniaProperty.cs index 62ca971412..fd43ced196 100644 --- a/src/Avalonia.Base/AvaloniaProperty.cs +++ b/src/Avalonia.Base/AvaloniaProperty.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using Avalonia.Data; using Avalonia.Data.Core; +using Avalonia.Styling; using Avalonia.Utilities; namespace Avalonia @@ -454,15 +455,6 @@ namespace Avalonia return Name; } - /// - /// Uses the visitor pattern to resolve an untyped property to a typed property. - /// - /// The type of user data passed. - /// The visitor which will accept the typed property. - /// The user data to pass. - public abstract void Accept(IAvaloniaPropertyVisitor visitor, ref TData data) - where TData : struct; - /// /// Routes an untyped ClearValue call to a typed call. /// @@ -508,6 +500,7 @@ namespace Avalonia BindingPriority priority); internal abstract void RouteInheritanceParentChanged(AvaloniaObject o, AvaloniaObject? oldParent); + internal abstract ISetterInstance CreateSetterInstance(IStyleable target, object? value); /// /// Overrides the metadata for the property on the specified type. diff --git a/src/Avalonia.Base/DirectPropertyBase.cs b/src/Avalonia.Base/DirectPropertyBase.cs index 6e3baea99b..9c1ffce24c 100644 --- a/src/Avalonia.Base/DirectPropertyBase.cs +++ b/src/Avalonia.Base/DirectPropertyBase.cs @@ -1,6 +1,7 @@ using System; using Avalonia.Data; using Avalonia.Reactive; +using Avalonia.Styling; using Avalonia.Utilities; namespace Avalonia @@ -120,12 +121,6 @@ namespace Avalonia base.OverrideMetadata(type, metadata); } - /// - public override void Accept(IAvaloniaPropertyVisitor visitor, ref TData data) - { - visitor.Visit(this, ref data); - } - /// internal override void RouteClearValue(AvaloniaObject o) { @@ -181,5 +176,30 @@ namespace Avalonia { throw new NotSupportedException("Direct properties do not support inheritance."); } + + internal override ISetterInstance CreateSetterInstance(IStyleable target, object? value) + { + if (value is IBinding binding) + { + return new PropertySetterBindingInstance( + target, + this, + binding); + } + else if (value is ITemplate template && !typeof(ITemplate).IsAssignableFrom(PropertyType)) + { + return new PropertySetterLazyInstance( + target, + this, + () => (TValue)template.Build()); + } + else + { + return new PropertySetterInstance( + target, + this, + (TValue)value!); + } + } } } diff --git a/src/Avalonia.Base/StyledPropertyBase.cs b/src/Avalonia.Base/StyledPropertyBase.cs index f94723866a..dd5eb703ea 100644 --- a/src/Avalonia.Base/StyledPropertyBase.cs +++ b/src/Avalonia.Base/StyledPropertyBase.cs @@ -2,6 +2,7 @@ using System; using System.Diagnostics; using Avalonia.Data; using Avalonia.Reactive; +using Avalonia.Styling; using Avalonia.Utilities; namespace Avalonia @@ -158,12 +159,6 @@ namespace Avalonia base.OverrideMetadata(type, metadata); } - /// - public override void Accept(IAvaloniaPropertyVisitor visitor, ref TData data) - { - visitor.Visit(this, ref data); - } - /// /// Gets the string representation of the property. /// @@ -237,6 +232,31 @@ namespace Avalonia o.InheritanceParentChanged(this, oldParent); } + internal override ISetterInstance CreateSetterInstance(IStyleable target, object? value) + { + if (value is IBinding binding) + { + return new PropertySetterBindingInstance( + target, + this, + binding); + } + else if (value is ITemplate template && !typeof(ITemplate).IsAssignableFrom(PropertyType)) + { + return new PropertySetterLazyInstance( + target, + this, + () => (TValue)template.Build()); + } + else + { + return new PropertySetterInstance( + target, + this, + (TValue)value!); + } + } + private object? GetDefaultBoxedValue(Type type) { _ = type ?? throw new ArgumentNullException(nameof(type)); diff --git a/src/Avalonia.Base/Styling/Setter.cs b/src/Avalonia.Base/Styling/Setter.cs index 168a882499..b4b3399022 100644 --- a/src/Avalonia.Base/Styling/Setter.cs +++ b/src/Avalonia.Base/Styling/Setter.cs @@ -16,7 +16,7 @@ namespace Avalonia.Styling /// A is used to set a value on a /// depending on a condition. /// - public class Setter : ISetter, IAnimationSetter, IAvaloniaPropertyVisitor + public class Setter : ISetter, IAnimationSetter { private object? _value; @@ -68,68 +68,7 @@ namespace Avalonia.Styling throw new InvalidOperationException("Setter.Property must be set."); } - var data = new SetterVisitorData - { - target = target, - value = Value, - }; - - Property.Accept(this, ref data); - return data.result!; - } - - void IAvaloniaPropertyVisitor.Visit( - StyledPropertyBase property, - ref SetterVisitorData data) - { - if (data.value is IBinding binding) - { - data.result = new PropertySetterBindingInstance( - data.target, - property, - binding); - } - else if (data.value is ITemplate template && !typeof(ITemplate).IsAssignableFrom(property.PropertyType)) - { - data.result = new PropertySetterLazyInstance( - data.target, - property, - () => (T)template.Build()); - } - else - { - data.result = new PropertySetterInstance( - data.target, - property, - (T)data.value!); - } - } - - void IAvaloniaPropertyVisitor.Visit( - DirectPropertyBase property, - ref SetterVisitorData data) - { - if (data.value is IBinding binding) - { - data.result = new PropertySetterBindingInstance( - data.target, - property, - binding); - } - else if (data.value is ITemplate template && !typeof(ITemplate).IsAssignableFrom(property.PropertyType)) - { - data.result = new PropertySetterLazyInstance( - data.target, - property, - () => (T)template.Build()); - } - else - { - data.result = new PropertySetterInstance( - data.target, - property, - (T)data.value!); - } + return Property.CreateSetterInstance(target, Value); } private struct SetterVisitorData diff --git a/src/Avalonia.Base/Utilities/IAvaloniaPropertyVisitor.cs b/src/Avalonia.Base/Utilities/IAvaloniaPropertyVisitor.cs deleted file mode 100644 index 6a8df91b81..0000000000 --- a/src/Avalonia.Base/Utilities/IAvaloniaPropertyVisitor.cs +++ /dev/null @@ -1,32 +0,0 @@ -namespace Avalonia.Utilities -{ - /// - /// A visitor to resolve an untyped to a typed property. - /// - /// The type of user data passed. - /// - /// Pass an instance that implements this interface to - /// - /// in order to resolve un untyped to a typed - /// or . - /// - public interface IAvaloniaPropertyVisitor - where TData : struct - { - /// - /// Called when the property is a styled property. - /// - /// The property value type. - /// The property. - /// The user data. - void Visit(StyledPropertyBase property, ref TData data); - - /// - /// Called when the property is a direct property. - /// - /// The property value type. - /// The property. - /// The user data. - void Visit(DirectPropertyBase property, ref TData data); - } -} diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaPropertyTests.cs b/tests/Avalonia.Base.UnitTests/AvaloniaPropertyTests.cs index ee29c82345..f3f39b465b 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaPropertyTests.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaPropertyTests.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using Avalonia.Data; +using Avalonia.Styling; using Avalonia.Utilities; using Xunit; @@ -146,11 +147,6 @@ namespace Avalonia.Base.UnitTests OverrideMetadata(typeof(T), metadata); } - public override void Accept(IAvaloniaPropertyVisitor vistor, ref TData data) - { - throw new NotImplementedException(); - } - internal override IDisposable RouteBind( AvaloniaObject o, IObservable> source, @@ -186,6 +182,11 @@ namespace Avalonia.Base.UnitTests { throw new NotImplementedException(); } + + internal override ISetterInstance CreateSetterInstance(IStyleable target, object value) + { + throw new NotImplementedException(); + } } private class Class1 : AvaloniaObject From c53e5307a02e37a5781429514ef5f0fa935ed5f8 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Wed, 20 Apr 2022 23:30:23 +0200 Subject: [PATCH 193/416] Fix WeakHashList losing one item when upgrading storage. --- src/Avalonia.Base/Utilities/WeakHashList.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Utilities/WeakHashList.cs b/src/Avalonia.Base/Utilities/WeakHashList.cs index 32668872da..51aa24dd6c 100644 --- a/src/Avalonia.Base/Utilities/WeakHashList.cs +++ b/src/Avalonia.Base/Utilities/WeakHashList.cs @@ -104,8 +104,10 @@ internal class WeakHashList where T : class if (existing!.TryGetTarget(out var target)) Add(target); } - _arr = null; + Add(item); + + _arr = null; } public void Remove(T item) From 82835ef95708edaf80a4ac2406179192d2db3d59 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 21 Apr 2022 09:53:28 +0200 Subject: [PATCH 194/416] Use nested styles in Button template. And related fixes to make this work. --- src/Avalonia.Base/Styling/NestingSelector.cs | 2 +- src/Avalonia.Base/Styling/Style.cs | 8 +- .../Controls/Button.xaml | 80 ++++++++++--------- .../Styling/SelectorTests_Nesting.cs | 68 +++++++++++++++- 4 files changed, 116 insertions(+), 42 deletions(-) diff --git a/src/Avalonia.Base/Styling/NestingSelector.cs b/src/Avalonia.Base/Styling/NestingSelector.cs index 1be54dea3c..741eb7e9ca 100644 --- a/src/Avalonia.Base/Styling/NestingSelector.cs +++ b/src/Avalonia.Base/Styling/NestingSelector.cs @@ -17,7 +17,7 @@ namespace Avalonia.Styling { if (parent is Style s && s.Selector is Selector selector) { - return selector.Match(control, null, subscribe); + return selector.Match(control, (parent as Style)?.Parent, subscribe); } throw new InvalidOperationException( diff --git a/src/Avalonia.Base/Styling/Style.cs b/src/Avalonia.Base/Styling/Style.cs index 6020dfe25f..7a83322355 100644 --- a/src/Avalonia.Base/Styling/Style.cs +++ b/src/Avalonia.Base/Styling/Style.cs @@ -120,15 +120,19 @@ namespace Avalonia.Styling instance.Start(); } + var result = match.Result; + if (_children is not null) { foreach (var child in _children) { - child.TryAttach(target, host); + var childResult = child.TryAttach(target, host); + if (childResult > result) + result = childResult; } } - return match.Result; + return result; } public bool TryGetResource(object key, out object? result) diff --git a/src/Avalonia.Themes.Fluent/Controls/Button.xaml b/src/Avalonia.Themes.Fluent/Controls/Button.xaml index f545206a2f..282f575605 100644 --- a/src/Avalonia.Themes.Fluent/Controls/Button.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/Button.xaml @@ -7,9 +7,11 @@ + 8,5,8,6 + - - + + - + - + - + - + - + + + + + - diff --git a/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Nesting.cs b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Nesting.cs index eeb2fad996..f7e8793ede 100644 --- a/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Nesting.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Nesting.cs @@ -9,7 +9,7 @@ namespace Avalonia.Base.UnitTests.Styling public class SelectorTests_Nesting { [Fact] - public void Nesting_Class_Doesnt_Match_Parent_Selector() + public void Nesting_Class_Doesnt_Match_Parent_OfType_Selector() { var control = new Control2(); Style nested; @@ -26,7 +26,7 @@ namespace Avalonia.Base.UnitTests.Styling } [Fact] - public void Or_Nesting_Class_Doesnt_Match_Parent_Selector() + public void Or_Nesting_Class_Doesnt_Match_Parent_OfType_Selector() { var control = new Control2(); Style nested; @@ -45,7 +45,7 @@ namespace Avalonia.Base.UnitTests.Styling } [Fact] - public void Or_Nesting_Child_OfType_Does_Not_Match_Parent_Selector() + public void Or_Nesting_Child_OfType_Doesnt_Match_Parent_OfType_Selector() { var control = new Control1(); var panel = new DockPanel { Children = { control } }; @@ -64,6 +64,34 @@ namespace Avalonia.Base.UnitTests.Styling Assert.Equal(SelectorMatchResult.NeverThisInstance, match.Result); } + [Fact] + public void Double_Nesting_Class_Doesnt_Match_Grandparent_OfType_Selector() + { + var control = new Control2 + { + Classes = { "foo", "bar" }, + }; + + Style parent; + Style nested; + var grandparent = new Style(x => x.OfType()) + { + Children = + { + (parent = new Style(x => x.Nesting().Class("foo")) + { + Children = + { + (nested = new Style(x => x.Nesting().Class("bar"))) + } + }) + } + }; + + var match = nested.Selector.Match(control, parent); + Assert.Equal(SelectorMatchResult.NeverThisType, match.Result); + } + [Fact] public void Nesting_Class_Matches() { @@ -87,6 +115,40 @@ namespace Avalonia.Base.UnitTests.Styling Assert.False(sink.Active); } + [Fact] + public void Double_Nesting_Class_Matches() + { + var control = new Control1 + { + Classes = { "foo", "bar" }, + }; + + Style parent; + Style nested; + var grandparent = new Style(x => x.OfType()) + { + Children = + { + (parent = new Style(x => x.Nesting().Class("foo")) + { + Children = + { + (nested = new Style(x => x.Nesting().Class("bar"))) + } + }) + } + }; + + var match = nested.Selector.Match(control, parent); + Assert.Equal(SelectorMatchResult.Sometimes, match.Result); + + var sink = new ActivatorSink(match.Activator); + + Assert.True(sink.Active); + control.Classes.Remove("foo"); + Assert.False(sink.Active); + } + [Fact] public void Or_Nesting_Class_Matches() { From 30531df3273f576a11ed2ca03c07a07d740a5eea Mon Sep 17 00:00:00 2001 From: Kevin Ivarsen Date: Thu, 21 Apr 2022 01:43:29 -0700 Subject: [PATCH 195/416] Disable clipping in DevTools content/padding/margin adorner so that the margin part is visible (fixes #8025) --- src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs b/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs index 2553ae90ce..58807b489e 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml.cs @@ -36,6 +36,7 @@ namespace Avalonia.Diagnostics.Views new Border { BorderBrush = new SolidColorBrush(Colors.Yellow, 0.5) } }, }; + AdornerLayer.SetIsClipEnabled(_adorner, false); } protected void AddAdorner(object? sender, PointerEventArgs e) From 066c81b1ac3784b36a151bcf3b90d8cba7f5254c Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Thu, 21 Apr 2022 11:35:30 +0200 Subject: [PATCH 196/416] Another fix for WeakHashList. Added basic unit tests. --- src/Avalonia.Base/Utilities/WeakHashList.cs | 13 ++-- .../Utilities/WeakHashListTests.cs | 66 +++++++++++++++++++ 2 files changed, 74 insertions(+), 5 deletions(-) create mode 100644 tests/Avalonia.Base.UnitTests/Utilities/WeakHashListTests.cs diff --git a/src/Avalonia.Base/Utilities/WeakHashList.cs b/src/Avalonia.Base/Utilities/WeakHashList.cs index 51aa24dd6c..df480aa062 100644 --- a/src/Avalonia.Base/Utilities/WeakHashList.cs +++ b/src/Avalonia.Base/Utilities/WeakHashList.cs @@ -6,6 +6,8 @@ namespace Avalonia.Utilities; internal class WeakHashList where T : class { + public const int DefaultArraySize = 8; + private struct Key { public WeakReference? Weak; @@ -63,7 +65,8 @@ internal class WeakHashList where T : class WeakReference?[]? _arr; int _arrCount; - public bool IsEmpty => _dic == null || _dic.Count == 0; + public bool IsEmpty => _dic is not null ? _dic.Count == 0 : _arrCount == 0; + public bool NeedCompact { get; private set; } public void Add(T item) @@ -79,7 +82,7 @@ internal class WeakHashList where T : class } if (_arr == null) - _arr = new WeakReference[8]; + _arr = new WeakReference[DefaultArraySize]; if (_arrCount < _arr.Length) { @@ -108,6 +111,7 @@ internal class WeakHashList where T : class Add(item); _arr = null; + _arrCount = 0; } public void Remove(T item) @@ -119,7 +123,7 @@ internal class WeakHashList where T : class if (_arr[c]?.TryGetTarget(out var target) == true && target == item) { _arr[c] = null; - Compact(); + ArrCompact(); return; } } @@ -219,7 +223,6 @@ internal class WeakHashList where T : class } if (_dic != null) { - foreach (var kvp in _dic) { if (kvp.Key.Weak?.TryGetTarget(out var target) == true) @@ -235,4 +238,4 @@ internal class WeakHashList where T : class return pooled; } -} \ No newline at end of file +} diff --git a/tests/Avalonia.Base.UnitTests/Utilities/WeakHashListTests.cs b/tests/Avalonia.Base.UnitTests/Utilities/WeakHashListTests.cs new file mode 100644 index 0000000000..9ae82e2be0 --- /dev/null +++ b/tests/Avalonia.Base.UnitTests/Utilities/WeakHashListTests.cs @@ -0,0 +1,66 @@ +using Avalonia.Utilities; +using Xunit; + +namespace Avalonia.Base.UnitTests.Utilities; + +public class WeakHashListTests +{ + [Fact] + public void Is_Empty_Works() + { + var target = new WeakHashList(); + + Assert.True(target.IsEmpty); + + target.Add("1"); + + Assert.False(target.IsEmpty); + + target.Remove("1"); + + Assert.True(target.IsEmpty); + + // Fill array storage. + var arrMaxSize = WeakHashList.DefaultArraySize; + + for (int i = 0; i < arrMaxSize; i++) + { + target.Add(i.ToString()); + } + + Assert.False(target.IsEmpty); + + // This goes above array storage and upgrades to a dictionary. + target.Add(arrMaxSize.ToString()); + + Assert.False(target.IsEmpty); + + // Remove everything, this should still keep an empty dictionary. + for (int i = 0; i < arrMaxSize + 1; i++) + { + target.Remove(i.ToString()); + } + + Assert.True(target.IsEmpty); + } + + [Fact] + public void Array_Compact_After_Remove_Works() + { + var target = new WeakHashList(); + + // Use all slots in array storage. + var arrMaxSize = WeakHashList.DefaultArraySize; + + for (int i = 0; i < arrMaxSize; i++) + { + target.Add(i.ToString()); + } + + // This should compact the array. + target.Remove("3"); + + // And new value should fill empty space. + target.Add("42"); + } +} From 3e6bc0b48d4ea1e32a7b552b5c3c54c093a6592a Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Thu, 21 Apr 2022 14:17:26 +0200 Subject: [PATCH 197/416] Some more hit testing fixes --- src/Avalonia.Base/Media/GlyphRun.cs | 44 +++++---- .../Media/TextFormatting/TextFormatterImpl.cs | 2 +- .../Media/TextFormatting/TextLineImpl.cs | 74 +++++++------- .../Media/TextFormatting/TextLineTests.cs | 97 +++++++++++++------ 4 files changed, 133 insertions(+), 84 deletions(-) diff --git a/src/Avalonia.Base/Media/GlyphRun.cs b/src/Avalonia.Base/Media/GlyphRun.cs index ec270d796a..9a2645f03d 100644 --- a/src/Avalonia.Base/Media/GlyphRun.cs +++ b/src/Avalonia.Base/Media/GlyphRun.cs @@ -49,7 +49,7 @@ namespace Avalonia.Media IReadOnlyList? glyphClusters = null, int biDiLevel = 0) { - _glyphTypeface = glyphTypeface; + _glyphTypeface = glyphTypeface; FontRenderingEmSize = fontRenderingEmSize; @@ -204,7 +204,7 @@ namespace Avalonia.Media public double GetDistanceFromCharacterHit(CharacterHit characterHit) { var characterIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength; - + var distance = 0.0; if (IsLeftToRight) @@ -223,7 +223,7 @@ namespace Avalonia.Media } var glyphIndex = FindGlyphIndex(characterIndex); - + if (GlyphClusters != null) { var currentCluster = GlyphClusters[glyphIndex]; @@ -249,7 +249,7 @@ namespace Avalonia.Media { //RightToLeft var glyphIndex = FindGlyphIndex(characterIndex); - + if (GlyphClusters != null) { if (characterIndex > GlyphClusters[0]) @@ -284,13 +284,13 @@ namespace Avalonia.Media public CharacterHit GetCharacterHitFromDistance(double distance, out bool isInside) { var characterIndex = 0; - + // Before if (distance <= 0) { isInside = false; - if(GlyphClusters != null) + if (GlyphClusters != null) { characterIndex = GlyphClusters[characterIndex]; } @@ -307,11 +307,11 @@ namespace Avalonia.Media characterIndex = GlyphIndices.Count - 1; - if(GlyphClusters != null) + if (GlyphClusters != null) { characterIndex = GlyphClusters[characterIndex]; } - + var lastCharacterHit = FindNearestCharacterHit(characterIndex, out _); return IsLeftToRight ? lastCharacterHit : new CharacterHit(lastCharacterHit.FirstCharacterIndex); @@ -327,7 +327,7 @@ namespace Avalonia.Media var advance = GetGlyphAdvance(index, out var cluster); characterIndex = cluster; - + if (distance > currentX && distance <= currentX + advance) { break; @@ -345,7 +345,7 @@ namespace Avalonia.Media var advance = GetGlyphAdvance(index, out var cluster); characterIndex = cluster; - + if (currentX - advance < distance) { break; @@ -554,6 +554,16 @@ namespace Avalonia.Media nextCluster = GlyphClusters[currentIndex]; } + if (nextCluster < Characters.Start) + { + nextCluster = Characters.Start; + } + + if (cluster < Characters.Start) + { + cluster = Characters.Start; + } + int trailingLength; if (nextCluster == cluster) @@ -577,7 +587,7 @@ namespace Avalonia.Media private double GetGlyphAdvance(int index, out int cluster) { cluster = GlyphClusters != null ? GlyphClusters[index] : index; - + if (GlyphAdvances != null) { return GlyphAdvances[index]; @@ -603,7 +613,7 @@ namespace Avalonia.Media var widthIncludingTrailingWhitespace = 0d; var trailingWhitespaceLength = GetTrailingWhitespaceLength(out var newLineLength, out var glyphCount); - + for (var index = 0; index < GlyphIndices.Count; index++) { var advance = GetGlyphAdvance(index, out _); @@ -615,7 +625,7 @@ namespace Avalonia.Media if (IsLeftToRight) { - for (var index = GlyphIndices.Count - glyphCount; index { new ShapedTextCharacters(shapedBuffer, properties) }; - return new TextLineImpl(textRuns, firstTextSourceIndex, 1, double.PositiveInfinity, paragraphProperties, flowDirection).FinalizeLine(); + return new TextLineImpl(textRuns, firstTextSourceIndex, 0, double.PositiveInfinity, paragraphProperties, flowDirection).FinalizeLine(); } /// diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs index 30e3728d1f..b480774d1d 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs @@ -183,6 +183,7 @@ namespace Avalonia.Media.TextFormatting case ShapedTextCharacters shapedRun: { characterHit = shapedRun.GlyphRun.GetCharacterHitFromDistance(distance, out _); + break; } default: @@ -426,31 +427,42 @@ namespace Avalonia.Media.TextFormatting if (nextRun != null) { - if (nextRun.Text.Start < currentRun.Text.Start && firstTextSourceCharacterIndex + textLength < currentRun.Text.End) + switch (nextRun) { - goto skip; - } + case ShapedTextCharacters when currentRun is ShapedTextCharacters: + { + if (nextRun.Text.Start < currentRun.Text.Start && firstTextSourceCharacterIndex + textLength < currentRun.Text.End) + { + goto skip; + } - if (currentRun.Text.Start >= firstTextSourceCharacterIndex + textLength) - { - goto skip; - } + if (currentRun.Text.Start >= firstTextSourceCharacterIndex + textLength) + { + goto skip; + } - if (currentRun.Text.Start > nextRun.Text.Start && currentRun.Text.Start < firstTextSourceCharacterIndex) - { - goto skip; - } + if (currentRun.Text.Start > nextRun.Text.Start && currentRun.Text.Start < firstTextSourceCharacterIndex) + { + goto skip; + } - if (currentRun.Text.End < firstTextSourceCharacterIndex) - { - goto skip; - } + if (currentRun.Text.End < firstTextSourceCharacterIndex) + { + goto skip; + } - goto noop; + goto noop; + } + default: + { + goto noop; + } + } skip: { startX += currentRun.Size.Width; + currentPosition += currentRun.TextSourceLength; } continue; @@ -460,7 +472,6 @@ namespace Avalonia.Media.TextFormatting } } - var endX = startX; var endOffset = 0d; @@ -520,11 +531,13 @@ namespace Avalonia.Media.TextFormatting } default: { - if (firstTextSourceCharacterIndex + textLength >= currentRun.Text.Start + currentRun.Text.Length) + if (currentPosition + currentRun.TextSourceLength <= firstTextSourceCharacterIndex + textLength) { endX += currentRun.Size.Width; } + currentPosition += currentRun.TextSourceLength; + break; } } @@ -538,7 +551,9 @@ namespace Avalonia.Media.TextFormatting if (lastDirection == currentDirection && result.Count > 0 && MathUtilities.AreClose(currentRect.Right, startX)) { - var textBounds = new TextBounds(currentRect.WithWidth(currentRect.Width + width), currentDirection); + currentRect = currentRect.WithWidth(currentRect.Width + width); + + var textBounds = new TextBounds(currentRect, currentDirection); result[result.Count - 1] = textBounds; } @@ -551,21 +566,9 @@ namespace Avalonia.Media.TextFormatting if (currentDirection == FlowDirection.LeftToRight) { - if (nextRun != null) - { - if (nextRun.Text.Start > currentRun.Text.Start && nextRun.Text.Start >= firstTextSourceCharacterIndex + textLength) - { - break; - } - - currentPosition = nextRun.Text.End; - } - else + if (currentPosition >= firstTextSourceCharacterIndex + textLength) { - if (currentPosition >= firstTextSourceCharacterIndex + textLength) - { - break; - } + break; } } else @@ -575,10 +578,7 @@ namespace Avalonia.Media.TextFormatting break; } - if (currentPosition != currentRun.Text.Start) - { - endX += currentRun.Size.Width - endOffset; - } + endX += currentRun.Size.Width - endOffset; } lastDirection = currentDirection; diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs index e9bc792be3..b58d9051f3 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs @@ -70,12 +70,12 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting } } } - + [Fact] public void Should_Get_Next_Caret_CharacterHit_Bidi() { const string text = "אבג 1 ABC"; - + using (Start()) { var defaultProperties = new GenericTextRunProperties(Typeface.Default); @@ -90,7 +90,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting var clusters = new List(); - foreach (var textRun in textLine.TextRuns.OrderBy(x=> x.Text.Start)) + foreach (var textRun in textLine.TextRuns.OrderBy(x => x.Text.Start)) { var shapedRun = (ShapedTextCharacters)textRun; @@ -98,7 +98,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting shapedRun.ShapedBuffer.GlyphClusters.Reverse() : shapedRun.ShapedBuffer.GlyphClusters); } - + var nextCharacterHit = new CharacterHit(0, clusters[1] - clusters[0]); foreach (var cluster in clusters) @@ -122,7 +122,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting public void Should_Get_Previous_Caret_CharacterHit_Bidi() { const string text = "אבג 1 ABC"; - + using (Start()) { var defaultProperties = new GenericTextRunProperties(Typeface.Default); @@ -137,7 +137,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting var clusters = new List(); - foreach (var textRun in textLine.TextRuns.OrderBy(x=> x.Text.Start)) + foreach (var textRun in textLine.TextRuns.OrderBy(x => x.Text.Start)) { var shapedRun = (ShapedTextCharacters)textRun; @@ -147,13 +147,13 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting } clusters.Reverse(); - + var nextCharacterHit = new CharacterHit(text.Length - 1); foreach (var cluster in clusters) { var currentCaretIndex = nextCharacterHit.FirstCharacterIndex + nextCharacterHit.TrailingLength; - + Assert.Equal(cluster, currentCaretIndex); nextCharacterHit = textLine.GetPreviousCaretCharacterHit(nextCharacterHit); @@ -168,7 +168,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting Assert.Equal(lastCharacterHit.TrailingLength, nextCharacterHit.TrailingLength); } } - + [InlineData("𐐷𐐷𐐷𐐷𐐷")] [InlineData("01234567🎉\n")] [InlineData("𐐷1234")] @@ -324,7 +324,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting } } - Assert.Equal(currentDistance,textLine.GetDistanceFromCharacterHit(new CharacterHit(s_multiLineText.Length))); + Assert.Equal(currentDistance, textLine.GetDistanceFromCharacterHit(new CharacterHit(s_multiLineText.Length))); } } @@ -371,7 +371,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting yield return CreateData("01234 01234", 58, TextTrimming.WordEllipsis, "01234\u2026"); yield return CreateData("01234", 9, TextTrimming.CharacterEllipsis, "\u2026"); yield return CreateData("01234", 2, TextTrimming.CharacterEllipsis, ""); - + object[] CreateData(string text, double width, TextTrimming mode, string expected) { return new object[] @@ -424,7 +424,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting { var defaultProperties = new GenericTextRunProperties(Typeface.Default); var textSource = new DrawableRunTextSource(); - + var formatter = new TextFormatterImpl(); var textLine = @@ -471,7 +471,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting Assert.Equal(4, textLine.TextRuns.Count); - var currentHit = textLine.GetPreviousCaretCharacterHit(new CharacterHit(3,1)); + var currentHit = textLine.GetPreviousCaretCharacterHit(new CharacterHit(3, 1)); Assert.Equal(3, currentHit.FirstCharacterIndex); Assert.Equal(0, currentHit.TrailingLength); @@ -552,11 +552,11 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting switch (textSourceIndex) { case 0: - return new CustomDrawableRun(); + return new CustomDrawableRun(); case 1: return new TextCharacters(new ReadOnlySlice(Text.AsMemory(), 1, 1, 1), new GenericTextRunProperties(Typeface.Default)); case 2: - return new CustomDrawableRun(); + return new CustomDrawableRun(); case 3: return new TextCharacters(new ReadOnlySlice(Text.AsMemory(), 3, 1, 3), new GenericTextRunProperties(Typeface.Default)); default: @@ -564,14 +564,14 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting } } } - + private class CustomDrawableRun : DrawableTextRun { public override Size Size => new(14, 14); public override double Baseline => 14; public override void Draw(DrawingContext drawingContext, Point origin) { - + } } @@ -587,29 +587,29 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting var shapedTextRuns = textLine.TextRuns.Cast().ToList(); var lastCluster = -1; - + foreach (var textRun in shapedTextRuns) { var shapedBuffer = textRun.ShapedBuffer; var currentClusters = shapedBuffer.GlyphClusters.ToList(); - foreach (var currentCluster in currentClusters) + foreach (var currentCluster in currentClusters) { if (lastCluster == currentCluster) { continue; } - + glyphClusters.Add(currentCluster); lastCluster = currentCluster; } } - + return glyphClusters; } - + private static List BuildRects(TextLine textLine) { var rects = new List(); @@ -624,11 +624,11 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting foreach (var textRun in shapedTextRuns) { var shapedBuffer = textRun.ShapedBuffer; - + for (var index = 0; index < shapedBuffer.GlyphAdvances.Count; index++) { var currentCluster = shapedBuffer.GlyphClusters[index]; - + var advance = shapedBuffer.GlyphAdvances[index]; if (lastCluster != currentCluster) @@ -642,10 +642,10 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting rects.Remove(rect); rect = rect.WithWidth(rect.Width + advance); - + rects.Add(rect); } - + currentX += advance; lastCluster = currentCluster; @@ -655,8 +655,43 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting return rects; } + [Fact] - public void Should_Get_TextBounds() + public void Should_Get_TextBounds_Mixed() + { + using (Start()) + { + var defaultProperties = new GenericTextRunProperties(Typeface.Default); + var text = "0123".AsMemory(); + var shaperOption = new TextShaperOptions(Typeface.Default.GlyphTypeface, 10, 0, CultureInfo.CurrentCulture); + + var textRuns = new List + { + new ShapedTextCharacters(TextShaper.Current.ShapeText(new ReadOnlySlice(text), shaperOption), defaultProperties), + new CustomDrawableRun(), + new ShapedTextCharacters(TextShaper.Current.ShapeText(new ReadOnlySlice(text, text.Length + 1, text.Length), shaperOption), defaultProperties), + new CustomDrawableRun(), + new ShapedTextCharacters(TextShaper.Current.ShapeText(new ReadOnlySlice(text, text.Length * 2 + 2, text.Length), shaperOption), defaultProperties), + new CustomDrawableRun(), + }; + + var textSource = new FixedRunsTextSource(textRuns); + + var formatter = new TextFormatterImpl(); + + var textLine = + formatter.FormatLine(textSource, 0, double.PositiveInfinity, + new GenericTextParagraphProperties(defaultProperties)); + + var textBounds = textLine.GetTextBounds(0, text.Length * 3 + 3); + + Assert.Equal(1, textBounds.Count); + Assert.Equal(textLine.WidthIncludingTrailingWhitespace, textBounds.Sum(x => x.Rectangle.Width)); + } + } + + [Fact] + public void Should_Get_TextBounds_BiDi() { using (Start()) { @@ -673,7 +708,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting new ShapedTextCharacters(TextShaper.Current.ShapeText(new ReadOnlySlice(text, text.Length * 3, text.Length), ltrOptions), defaultProperties) }; - + var textSource = new FixedRunsTextSource(textRuns); var formatter = new TextFormatterImpl(); @@ -700,12 +735,16 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting public TextRun? GetTextRun(int textSourceIndex) { + var currentPosition = 0; + foreach (var textRun in _textRuns) { - if(textRun.Text.Start == textSourceIndex) + if (currentPosition == textSourceIndex) { return textRun; } + + currentPosition += textRun.TextSourceLength; } return null; From 32d72930972ce5586caaf5259909c4b932945b2f Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Thu, 21 Apr 2022 15:33:01 +0200 Subject: [PATCH 198/416] Fix GetNextCharacterHit for trailing whitespace --- src/Avalonia.Controls/Presenters/TextPresenter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index 7f2dde7c1e..9ac4b71a12 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -662,7 +662,7 @@ namespace Avalonia.Controls.Presenters caretIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength; - if (textLine.NewLineLength > 0 && caretIndex == textLine.FirstTextSourceIndex + textLine.Length) + if (textLine.TrailingWhitespaceLength > 0 && caretIndex == textLine.FirstTextSourceIndex + textLine.Length) { characterHit = new CharacterHit(caretIndex); } From aaf04a38dab6bb8d1dfc263e6fc922a3ce111b10 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Thu, 21 Apr 2022 15:39:09 +0200 Subject: [PATCH 199/416] Fix property GetValue --- src/Avalonia.Controls/Documents/InlineUIContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Documents/InlineUIContainer.cs b/src/Avalonia.Controls/Documents/InlineUIContainer.cs index eb12092bb8..5f08c23099 100644 --- a/src/Avalonia.Controls/Documents/InlineUIContainer.cs +++ b/src/Avalonia.Controls/Documents/InlineUIContainer.cs @@ -105,7 +105,7 @@ namespace Avalonia.Controls.Documents get { double baseline = Size.Height; - double baselineOffsetValue = (double)Control.GetValue(TextBlock.BaselineOffsetProperty); + double baselineOffsetValue = Control.GetValue(TextBlock.BaselineOffsetProperty); if (!MathUtilities.IsZero(baselineOffsetValue)) { From 982e2d5db090305b3f57fc46e226da26839061fa Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 21 Apr 2022 16:15:36 +0200 Subject: [PATCH 200/416] Fix merge error due to changed API. And moved fields to live with other field. --- src/Avalonia.Controls/Border.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Controls/Border.cs b/src/Avalonia.Controls/Border.cs index 53de95ac41..06368eb5c6 100644 --- a/src/Avalonia.Controls/Border.cs +++ b/src/Avalonia.Controls/Border.cs @@ -71,6 +71,8 @@ namespace Avalonia.Controls AvaloniaProperty.Register(nameof(BorderLineJoin), PenLineJoin.Miter); private readonly BorderRenderHelper _borderRenderHelper = new BorderRenderHelper(); + private Thickness? _layoutThickness; + private double _scale; /// /// Initializes static members of the class. @@ -90,7 +92,7 @@ namespace Avalonia.Controls AffectsMeasure(BorderThicknessProperty); } - protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { base.OnPropertyChanged(change); switch (change.Property.Name) @@ -183,9 +185,6 @@ namespace Avalonia.Controls set => SetValue(BoxShadowProperty, value); } - private Thickness? _layoutThickness; - private double _scale; - private Thickness LayoutThickness { get From f33fe3b708f1d6a5966b4ba71447d35165a4aeaf Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Thu, 21 Apr 2022 20:25:16 +0200 Subject: [PATCH 201/416] Avoid checking all array values. --- src/Avalonia.Base/Utilities/WeakHashList.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Utilities/WeakHashList.cs b/src/Avalonia.Base/Utilities/WeakHashList.cs index df480aa062..fe582e8a78 100644 --- a/src/Avalonia.Base/Utilities/WeakHashList.cs +++ b/src/Avalonia.Base/Utilities/WeakHashList.cs @@ -118,7 +118,7 @@ internal class WeakHashList where T : class { if (_arr != null) { - for (var c = 0; c < _arr.Length; c++) + for (var c = 0; c < _arrCount; c++) { if (_arr[c]?.TryGetTarget(out var target) == true && target == item) { From a81a2d908ab71bfca837902290affe9628e4fccb Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Thu, 21 Apr 2022 20:57:46 +0200 Subject: [PATCH 202/416] Fix double property reads. --- src/Avalonia.Controls/Border.cs | 2 +- src/Avalonia.Controls/Presenters/ContentPresenter.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/Border.cs b/src/Avalonia.Controls/Border.cs index 06368eb5c6..bc740c133a 100644 --- a/src/Avalonia.Controls/Border.cs +++ b/src/Avalonia.Controls/Border.cs @@ -196,7 +196,7 @@ namespace Avalonia.Controls var borderThickness = BorderThickness; if (UseLayoutRounding) - borderThickness = LayoutHelper.RoundLayoutThickness(BorderThickness, _scale, _scale); + borderThickness = LayoutHelper.RoundLayoutThickness(borderThickness, _scale, _scale); _layoutThickness = borderThickness; } diff --git a/src/Avalonia.Controls/Presenters/ContentPresenter.cs b/src/Avalonia.Controls/Presenters/ContentPresenter.cs index e70a3bc1c9..996cb29534 100644 --- a/src/Avalonia.Controls/Presenters/ContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ContentPresenter.cs @@ -514,7 +514,7 @@ namespace Avalonia.Controls.Presenters var borderThickness = BorderThickness; if (UseLayoutRounding) - borderThickness = LayoutHelper.RoundLayoutThickness(BorderThickness, _scale, _scale); + borderThickness = LayoutHelper.RoundLayoutThickness(borderThickness, _scale, _scale); _layoutThickness = borderThickness; } From 2a5b2cf90f16418806170a18ed5692600784c7a5 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 21 Apr 2022 22:24:32 -0400 Subject: [PATCH 203/416] Use newer GetOldValue method --- .../ColorSpectrum/ColorSpectrum.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs index aecaa88f36..fe9a2fac43 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -361,7 +361,7 @@ namespace Avalonia.Controls.Primitives } /// - protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { if (change.Property == ColorProperty) { @@ -380,7 +380,7 @@ namespace Avalonia.Controls.Primitives UpdateBitmapSources(); } - _oldColor = change.OldValue.GetValueOrDefault(); + _oldColor = change.GetOldValue(); } else if (change.Property == HsvColorProperty) { @@ -391,7 +391,7 @@ namespace Avalonia.Controls.Primitives SetColor(); } - _oldHsvColor = change.OldValue.GetValueOrDefault(); + _oldHsvColor = change.GetOldValue(); } else if (change.Property == MinHueProperty || change.Property == MaxHueProperty) From fd96e6483d1ec66cc3afefd5e5250f79c4f8e474 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 22 Apr 2022 21:59:08 +0200 Subject: [PATCH 204/416] Add scaled ComboBox to ControlCatalog. To test #7147. --- samples/ControlCatalog/Pages/ComboBoxPage.xaml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/samples/ControlCatalog/Pages/ComboBoxPage.xaml b/samples/ControlCatalog/Pages/ComboBoxPage.xaml index 64e80a8e11..9f2fbb88f3 100644 --- a/samples/ControlCatalog/Pages/ComboBoxPage.xaml +++ b/samples/ControlCatalog/Pages/ComboBoxPage.xaml @@ -86,6 +86,16 @@ + + + + + + Inline Items + Inline Item 2 + Inline Item 3 + Inline Item 4 + WrapSelection From d1956d18b4ee8f3112855e59f89cdb5d82960430 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 22 Apr 2022 21:59:57 +0200 Subject: [PATCH 205/416] Scale ComboBox popup according to RenderTransform. --- .../Primitives/IPopupHost.cs | 56 +++++-- .../Primitives/OverlayPopupHost.cs | 42 +++-- src/Avalonia.Controls/Primitives/Popup.cs | 150 ++++++++++++++++-- src/Avalonia.Controls/Primitives/PopupRoot.cs | 39 ++--- .../Controls/OverlayPopupHost.xaml | 18 ++- .../Controls/PopupRoot.xaml | 22 +-- .../Controls/ComboBox.xaml | 3 +- .../Controls/OverlayPopupHost.xaml | 16 +- .../Controls/PopupRoot.xaml | 14 +- .../Primitives/PopupTests.cs | 4 + 10 files changed, 257 insertions(+), 107 deletions(-) diff --git a/src/Avalonia.Controls/Primitives/IPopupHost.cs b/src/Avalonia.Controls/Primitives/IPopupHost.cs index 36d2ae9230..8652924f90 100644 --- a/src/Avalonia.Controls/Primitives/IPopupHost.cs +++ b/src/Avalonia.Controls/Primitives/IPopupHost.cs @@ -2,6 +2,7 @@ using System; using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives.PopupPositioning; using Avalonia.Input; +using Avalonia.Media; using Avalonia.VisualTree; namespace Avalonia.Controls.Primitives @@ -17,16 +18,50 @@ namespace Avalonia.Controls.Primitives public interface IPopupHost : IDisposable, IFocusScope { /// - /// Sets the control to display in the popup. + /// Gets or sets the fixed width of the popup. /// - /// - void SetChild(IControl? control); + double Width { get; set; } + + /// + /// Gets or sets the minimum width of the popup. + /// + double MinWidth { get; set; } + + /// + /// Gets or sets the maximum width of the popup. + /// + double MaxWidth { get; set; } + + /// + /// Gets or sets the fixed height of the popup. + /// + double Height { get; set; } + + /// + /// Gets or sets the minimum height of the popup. + /// + double MinHeight { get; set; } + + /// + /// Gets or sets the maximum height of the popup. + /// + double MaxHeight { get; set; } /// /// Gets the presenter from the control's template. /// IContentPresenter? Presenter { get; } + /// + /// Gets or sets whether the popup appears on top of all other windows. + /// + bool Topmost { get; set; } + + /// + /// Gets or sets a transform that will be applied to the popup. + /// + Transform? Transform { get; set; } + /// /// Gets the root of the visual tree in the case where the popup is presented using a /// separate visual tree. @@ -57,6 +92,12 @@ namespace Avalonia.Controls.Primitives PopupPositionerConstraintAdjustment constraintAdjustment = PopupPositionerConstraintAdjustment.All, Rect? rect = null); + /// + /// Sets the control to display in the popup. + /// + /// + void SetChild(IControl? control); + /// /// Shows the popup. /// @@ -66,14 +107,5 @@ namespace Avalonia.Controls.Primitives /// Hides the popup. /// void Hide(); - - /// - /// Binds the constraints of the popup host to a set of properties, usally those present on - /// . - /// - IDisposable BindConstraints(AvaloniaObject popup, StyledProperty widthProperty, - StyledProperty minWidthProperty, StyledProperty maxWidthProperty, - StyledProperty heightProperty, StyledProperty minHeightProperty, - StyledProperty maxHeightProperty, StyledProperty topmostProperty); } } diff --git a/src/Avalonia.Controls/Primitives/OverlayPopupHost.cs b/src/Avalonia.Controls/Primitives/OverlayPopupHost.cs index 6ac544e0fe..4765718c3b 100644 --- a/src/Avalonia.Controls/Primitives/OverlayPopupHost.cs +++ b/src/Avalonia.Controls/Primitives/OverlayPopupHost.cs @@ -11,6 +11,12 @@ namespace Avalonia.Controls.Primitives { public class OverlayPopupHost : ContentControl, IPopupHost, IInteractive, IManagedPopupPositionerPopup { + /// + /// Defines the property. + /// + public static readonly StyledProperty TransformProperty = + PopupRoot.TransformProperty.AddOwner(); + private readonly OverlayLayer _overlayLayer; private PopupPositionerParameters _positionerParameters = new PopupPositionerParameters(); private ManagedPopupPositioner _positioner; @@ -29,10 +35,22 @@ namespace Avalonia.Controls.Primitives } public IVisual? HostedVisualTreeRoot => null; - + + public Transform? Transform + { + get => GetValue(TransformProperty); + set => SetValue(TransformProperty, value); + } + /// IInteractive? IInteractive.InteractiveParent => Parent; + bool IPopupHost.Topmost + { + get => false; + set { /* Not currently supported in overlay popups */ } + } + public void Dispose() => Hide(); @@ -48,28 +66,6 @@ namespace Avalonia.Controls.Primitives _shown = false; } - public IDisposable BindConstraints(AvaloniaObject popup, StyledProperty widthProperty, StyledProperty minWidthProperty, - StyledProperty maxWidthProperty, StyledProperty heightProperty, StyledProperty minHeightProperty, - StyledProperty maxHeightProperty, StyledProperty topmostProperty) - { - // Topmost property is not supported - var bindings = new List(); - - void Bind(AvaloniaProperty what, AvaloniaProperty to) => bindings.Add(this.Bind(what, popup[~to])); - Bind(WidthProperty, widthProperty); - Bind(MinWidthProperty, minWidthProperty); - Bind(MaxWidthProperty, maxWidthProperty); - Bind(HeightProperty, heightProperty); - Bind(MinHeightProperty, minHeightProperty); - Bind(MaxHeightProperty, maxHeightProperty); - - return Disposable.Create(() => - { - foreach (var x in bindings) - x.Dispose(); - }); - } - public void ConfigurePosition(IVisual target, PlacementMode placement, Point offset, PopupAnchor anchor = PopupAnchor.None, PopupGravity gravity = PopupGravity.None, PopupPositionerConstraintAdjustment constraintAdjustment = PopupPositionerConstraintAdjustment.All, diff --git a/src/Avalonia.Controls/Primitives/Popup.cs b/src/Avalonia.Controls/Primitives/Popup.cs index bb546107e0..7a7e41b029 100644 --- a/src/Avalonia.Controls/Primitives/Popup.cs +++ b/src/Avalonia.Controls/Primitives/Popup.cs @@ -14,6 +14,8 @@ using Avalonia.LogicalTree; using Avalonia.Metadata; using Avalonia.Platform; using Avalonia.VisualTree; +using Avalonia.Media; +using Avalonia.Utilities; namespace Avalonia.Controls.Primitives { @@ -33,6 +35,12 @@ namespace Avalonia.Controls.Primitives public static readonly StyledProperty ChildProperty = AvaloniaProperty.Register(nameof(Child)); + /// + /// Defines the property. + /// + public static readonly StyledProperty InheritsTransformProperty = + AvaloniaProperty.Register(nameof(InheritsTransform)); + /// /// Defines the property. /// @@ -196,6 +204,16 @@ namespace Avalonia.Controls.Primitives set; } + /// + /// Gets or sets a value that determines whether the popup inherits the render transform + /// from its . Defaults to false. + /// + public bool InheritsTransform + { + get => GetValue(InheritsTransformProperty); + set => SetValue(InheritsTransformProperty, value); + } + /// /// Gets or sets a value that determines how the can be dismissed. /// @@ -395,24 +413,29 @@ namespace Avalonia.Controls.Primitives } _isOpenRequested = false; - var popupHost = OverlayPopupHost.CreatePopupHost(placementTarget, DependencyResolver); + var popupHost = OverlayPopupHost.CreatePopupHost(placementTarget, DependencyResolver); var handlerCleanup = new CompositeDisposable(7); - popupHost.BindConstraints(this, WidthProperty, MinWidthProperty, MaxWidthProperty, - HeightProperty, MinHeightProperty, MaxHeightProperty, TopmostProperty).DisposeWith(handlerCleanup); - + UpdateHostSizing(popupHost, topLevel, placementTarget); + popupHost.Topmost = Topmost; popupHost.SetChild(Child); ((ISetLogicalParent)popupHost).SetParent(this); - popupHost.ConfigurePosition( - placementTarget, - PlacementMode, - new Point(HorizontalOffset, VerticalOffset), - PlacementAnchor, - PlacementGravity, - PlacementConstraintAdjustment, - PlacementRect); + if (InheritsTransform && placementTarget is Control c) + { + SubscribeToEventHandler>( + c, + PlacementTargetPropertyChanged, + (x, handler) => x.PropertyChanged += handler, + (x, handler) => x.PropertyChanged -= handler).DisposeWith(handlerCleanup); + } + else + { + popupHost.Transform = null; + } + + UpdateHostPosition(popupHost, placementTarget); SubscribeToEventHandler>(popupHost, RootTemplateApplied, (x, handler) => x.TemplateApplied += handler, @@ -494,7 +517,7 @@ namespace Avalonia.Controls.Primitives } } - _openState = new PopupOpenState(topLevel, popupHost, cleanupPopup); + _openState = new PopupOpenState(placementTarget, topLevel, popupHost, cleanupPopup); WindowManagerAddShadowHintChanged(popupHost, WindowManagerAddShadowHint); @@ -542,7 +565,93 @@ namespace Avalonia.Controls.Primitives base.OnDetachedFromLogicalTree(e); Close(); } - + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + if (_openState is not null) + { + if (change.Property == WidthProperty || + change.Property == MinWidthProperty || + change.Property == MaxWidthProperty || + change.Property == HeightProperty || + change.Property == MinHeightProperty || + change.Property == MaxHeightProperty) + { + UpdateHostSizing(_openState.PopupHost, _openState.TopLevel, _openState.PlacementTarget); + } + else if (change.Property == PlacementTargetProperty || + change.Property == PlacementModeProperty || + change.Property == HorizontalOffsetProperty || + change.Property == VerticalOffsetProperty || + change.Property == PlacementAnchorProperty || + change.Property == PlacementConstraintAdjustmentProperty || + change.Property == PlacementRectProperty) + { + if (change.Property == PlacementTargetProperty) + { + var newTarget = change.GetNewValue() ?? this.FindLogicalAncestorOfType(); + + if (newTarget is null || newTarget.GetVisualRoot() != _openState.TopLevel) + { + Close(); + return; + } + + _openState.PlacementTarget = newTarget; + } + + UpdateHostPosition(_openState.PopupHost, _openState.PlacementTarget); + } + else if (change.Property == TopmostProperty) + { + _openState.PopupHost.Topmost = change.GetNewValue(); + } + } + } + + private void UpdateHostPosition(IPopupHost popupHost, IControl placementTarget) + { + popupHost.ConfigurePosition( + placementTarget, + PlacementMode, + new Point(HorizontalOffset, VerticalOffset), + PlacementAnchor, + PlacementGravity, + PlacementConstraintAdjustment, + PlacementRect ?? new Rect(default, placementTarget.Bounds.Size)); + } + + private void UpdateHostSizing(IPopupHost popupHost, TopLevel topLevel, IControl placementTarget) + { + var scaleX = 1.0; + var scaleY = 1.0; + + if (InheritsTransform && placementTarget.TransformToVisual(topLevel) is Matrix m) + { + scaleX = Math.Sqrt(m.M11 * m.M11 + m.M12 * m.M12); + scaleY = Math.Sqrt(m.M11 * m.M11 + m.M12 * m.M12); + + // Ideally we'd only assign a ScaleTransform here when the scale != 1, but there's + // an issue with LayoutTransformControl in that it sets its LayoutTransform property + // with LocalValue priority in ArrangeOverride in certain cases when LayoutTransform + // is null, which breaks TemplateBindings to this property. Offending commit/line: + // + // https://github.com/AvaloniaUI/Avalonia/commit/6fbe1c2180ef45a940e193f1b4637e64eaab80ed#diff-5344e793df13f462126a8153ef46c44194f244b6890f25501709bae51df97f82R54 + popupHost.Transform = new ScaleTransform(scaleX, scaleY); + } + else + { + popupHost.Transform = null; + } + + popupHost.Width = Width * scaleX; + popupHost.MinWidth = MinWidth * scaleX; + popupHost.MaxWidth = MaxWidth * scaleX; + popupHost.Height = Height * scaleY; + popupHost.MinHeight = MinHeight * scaleY; + popupHost.MaxHeight = MaxHeight * scaleY; + } + private void HandlePositionChange() { if (_openState != null) @@ -824,6 +933,14 @@ namespace Avalonia.Controls.Primitives } } + private void PlacementTargetPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e) + { + if (_openState is not null && e.Property == Visual.TransformedBoundsProperty) + { + UpdateHostSizing(_openState.PopupHost, _openState.TopLevel, _openState.PlacementTarget); + } + } + private void WindowLostFocus() { if (IsLightDismissEnabled) @@ -862,15 +979,16 @@ namespace Avalonia.Controls.Primitives private readonly IDisposable _cleanup; private IDisposable? _presenterCleanup; - public PopupOpenState(TopLevel topLevel, IPopupHost popupHost, IDisposable cleanup) + public PopupOpenState(IControl placementTarget, TopLevel topLevel, IPopupHost popupHost, IDisposable cleanup) { + PlacementTarget = placementTarget; TopLevel = topLevel; PopupHost = popupHost; _cleanup = cleanup; } public TopLevel TopLevel { get; } - + public IControl PlacementTarget { get; set; } public IPopupHost PopupHost { get; } public void SetPresenterSubscription(IDisposable? presenterCleanup) diff --git a/src/Avalonia.Controls/Primitives/PopupRoot.cs b/src/Avalonia.Controls/Primitives/PopupRoot.cs index abf56e5420..f7bf7c1a27 100644 --- a/src/Avalonia.Controls/Primitives/PopupRoot.cs +++ b/src/Avalonia.Controls/Primitives/PopupRoot.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Reactive.Disposables; using Avalonia.Automation.Peers; using Avalonia.Controls.Primitives.PopupPositioning; using Avalonia.Interactivity; @@ -8,7 +6,6 @@ using Avalonia.Media; using Avalonia.Platform; using Avalonia.Styling; using Avalonia.VisualTree; -using JetBrains.Annotations; namespace Avalonia.Controls.Primitives { @@ -17,6 +14,12 @@ namespace Avalonia.Controls.Primitives /// public sealed class PopupRoot : WindowBase, IInteractive, IHostedVisualTreeRoot, IDisposable, IStyleHost, IPopupHost { + /// + /// Defines the property. + /// + public static readonly StyledProperty TransformProperty = + AvaloniaProperty.Register(nameof(Transform)); + private PopupPositionerParameters _positionerParameters; /// @@ -54,6 +57,15 @@ namespace Avalonia.Controls.Primitives /// public new IPopupImpl? PlatformImpl => (IPopupImpl?)base.PlatformImpl; + /// + /// Gets or sets a transform that will be applied to the popup. + /// + public Transform? Transform + { + get => GetValue(TransformProperty); + set => SetValue(TransformProperty, value); + } + /// /// Gets the parent control in the event route. /// @@ -103,27 +115,6 @@ namespace Avalonia.Controls.Primitives IVisual IPopupHost.HostedVisualTreeRoot => this; - public IDisposable BindConstraints(AvaloniaObject popup, StyledProperty widthProperty, StyledProperty minWidthProperty, - StyledProperty maxWidthProperty, StyledProperty heightProperty, StyledProperty minHeightProperty, - StyledProperty maxHeightProperty, StyledProperty topmostProperty) - { - var bindings = new List(); - - void Bind(AvaloniaProperty what, AvaloniaProperty to) => bindings.Add(this.Bind(what, popup[~to])); - Bind(WidthProperty, widthProperty); - Bind(MinWidthProperty, minWidthProperty); - Bind(MaxWidthProperty, maxWidthProperty); - Bind(HeightProperty, heightProperty); - Bind(MinHeightProperty, minHeightProperty); - Bind(MaxHeightProperty, maxHeightProperty); - Bind(TopmostProperty, topmostProperty); - return Disposable.Create(() => - { - foreach (var x in bindings) - x.Dispose(); - }); - } - protected override Size MeasureOverride(Size availableSize) { var maxAutoSize = PlatformImpl?.MaxAutoSizeHint ?? Size.Infinity; diff --git a/src/Avalonia.Themes.Default/Controls/OverlayPopupHost.xaml b/src/Avalonia.Themes.Default/Controls/OverlayPopupHost.xaml index 301e19d208..07d905ea1d 100644 --- a/src/Avalonia.Themes.Default/Controls/OverlayPopupHost.xaml +++ b/src/Avalonia.Themes.Default/Controls/OverlayPopupHost.xaml @@ -1,4 +1,4 @@ - diff --git a/src/Avalonia.Themes.Default/Controls/PopupRoot.xaml b/src/Avalonia.Themes.Default/Controls/PopupRoot.xaml index 9468cc5535..5e8f3337ee 100644 --- a/src/Avalonia.Themes.Default/Controls/PopupRoot.xaml +++ b/src/Avalonia.Themes.Default/Controls/PopupRoot.xaml @@ -10,16 +10,18 @@ - - - - - - + + + + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/ComboBox.xaml b/src/Avalonia.Themes.Fluent/Controls/ComboBox.xaml index e35093d2f1..93ecc438eb 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ComboBox.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ComboBox.xaml @@ -119,7 +119,8 @@ MinWidth="{Binding Bounds.Width, RelativeSource={RelativeSource TemplatedParent}}" MaxHeight="{TemplateBinding MaxDropDownHeight}" PlacementTarget="Background" - IsLightDismissEnabled="True"> + IsLightDismissEnabled="True" + InheritsTransform="True"> - - - + + + + + diff --git a/src/Avalonia.Themes.Fluent/Controls/PopupRoot.xaml b/src/Avalonia.Themes.Fluent/Controls/PopupRoot.xaml index 1e573913b9..f608cf55f5 100644 --- a/src/Avalonia.Themes.Fluent/Controls/PopupRoot.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/PopupRoot.xaml @@ -10,16 +10,18 @@ - - - - + + + + - - + + + diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs index cac8ca885d..d9cb40d1cc 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs @@ -325,6 +325,7 @@ namespace Avalonia.Controls.UnitTests.Primitives Assert.Equal( new[] { + "LayoutTransformControl", "VisualLayerManager", "ContentPresenter", "ContentPresenter", @@ -337,6 +338,7 @@ namespace Avalonia.Controls.UnitTests.Primitives Assert.Equal( new[] { + "LayoutTransformControl", "Panel", "Border", "VisualLayerManager", @@ -356,6 +358,7 @@ namespace Avalonia.Controls.UnitTests.Primitives Assert.Equal( new object[] { + popupRoot, popupRoot, popupRoot, target, @@ -372,6 +375,7 @@ namespace Avalonia.Controls.UnitTests.Primitives popupRoot, popupRoot, popupRoot, + popupRoot, target, null, }, From 7e2c1ad165b28a3918ec22278e2716254bcc1e45 Mon Sep 17 00:00:00 2001 From: amwx <40413319+amwx@users.noreply.github.com> Date: Fri, 22 Apr 2022 21:33:55 -0400 Subject: [PATCH 206/416] Implement TransformOrigin for brushes --- .../Animation/Animators/GradientBrushAnimator.cs | 6 ++++++ src/Avalonia.Base/Media/Brush.cs | 15 +++++++++++++++ src/Avalonia.Base/Media/IBrush.cs | 5 +++++ .../Immutable/ImmutableConicGradientBrush.cs | 4 +++- .../Media/Immutable/ImmutableGradientBrush.cs | 11 ++++++++++- .../Media/Immutable/ImmutableImageBrush.cs | 3 +++ .../Immutable/ImmutableLinearGradientBrush.cs | 4 +++- .../Immutable/ImmutableRadialGradientBrush.cs | 4 +++- .../Media/Immutable/ImmutableSolidColorBrush.cs | 5 +++++ .../Media/Immutable/ImmutableTileBrush.cs | 9 +++++++++ .../Media/Immutable/ImmutableVisualBrush.cs | 3 +++ 11 files changed, 65 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Base/Animation/Animators/GradientBrushAnimator.cs b/src/Avalonia.Base/Animation/Animators/GradientBrushAnimator.cs index 5e97635c9a..4727ea1bfb 100644 --- a/src/Avalonia.Base/Animation/Animators/GradientBrushAnimator.cs +++ b/src/Avalonia.Base/Animation/Animators/GradientBrushAnimator.cs @@ -31,6 +31,7 @@ namespace Avalonia.Animation.Animators InterpolateStops(progress, oldValue.GradientStops, newValue.GradientStops), s_doubleAnimator.Interpolate(progress, oldValue.Opacity, newValue.Opacity), oldValue.Transform is { } ? new ImmutableTransform(oldValue.Transform.Value) : null, + s_relativePointAnimator.Interpolate(progress, oldValue.TransformOrigin, newValue.TransformOrigin), oldValue.SpreadMethod, s_relativePointAnimator.Interpolate(progress, oldRadial.Center, newRadial.Center), s_relativePointAnimator.Interpolate(progress, oldRadial.GradientOrigin, newRadial.GradientOrigin), @@ -41,6 +42,7 @@ namespace Avalonia.Animation.Animators InterpolateStops(progress, oldValue.GradientStops, newValue.GradientStops), s_doubleAnimator.Interpolate(progress, oldValue.Opacity, newValue.Opacity), oldValue.Transform is { } ? new ImmutableTransform(oldValue.Transform.Value) : null, + s_relativePointAnimator.Interpolate(progress, oldValue.TransformOrigin, newValue.TransformOrigin), oldValue.SpreadMethod, s_relativePointAnimator.Interpolate(progress, oldConic.Center, newConic.Center), s_doubleAnimator.Interpolate(progress, oldConic.Angle, newConic.Angle)); @@ -50,6 +52,7 @@ namespace Avalonia.Animation.Animators InterpolateStops(progress, oldValue.GradientStops, newValue.GradientStops), s_doubleAnimator.Interpolate(progress, oldValue.Opacity, newValue.Opacity), oldValue.Transform is { } ? new ImmutableTransform(oldValue.Transform.Value) : null, + s_relativePointAnimator.Interpolate(progress, oldValue.TransformOrigin, newValue.TransformOrigin), oldValue.SpreadMethod, s_relativePointAnimator.Interpolate(progress, oldLinear.StartPoint, newLinear.StartPoint), s_relativePointAnimator.Interpolate(progress, oldLinear.EndPoint, newLinear.EndPoint)); @@ -102,18 +105,21 @@ namespace Avalonia.Animation.Animators return new ImmutableRadialGradientBrush( CreateStopsFromSolidColorBrush(solidColorBrush, oldRadial.GradientStops), solidColorBrush.Opacity, oldRadial.Transform is { } ? new ImmutableTransform(oldRadial.Transform.Value) : null, + oldRadial.TransformOrigin, oldRadial.SpreadMethod, oldRadial.Center, oldRadial.GradientOrigin, oldRadial.Radius); case IConicGradientBrush oldConic: return new ImmutableConicGradientBrush( CreateStopsFromSolidColorBrush(solidColorBrush, oldConic.GradientStops), solidColorBrush.Opacity, oldConic.Transform is { } ? new ImmutableTransform(oldConic.Transform.Value) : null, + oldConic.TransformOrigin, oldConic.SpreadMethod, oldConic.Center, oldConic.Angle); case ILinearGradientBrush oldLinear: return new ImmutableLinearGradientBrush( CreateStopsFromSolidColorBrush(solidColorBrush, oldLinear.GradientStops), solidColorBrush.Opacity, oldLinear.Transform is { } ? new ImmutableTransform(oldLinear.Transform.Value) : null, + oldLinear.TransformOrigin, oldLinear.SpreadMethod, oldLinear.StartPoint, oldLinear.EndPoint); default: diff --git a/src/Avalonia.Base/Media/Brush.cs b/src/Avalonia.Base/Media/Brush.cs index 9d989979a7..8d531e9394 100644 --- a/src/Avalonia.Base/Media/Brush.cs +++ b/src/Avalonia.Base/Media/Brush.cs @@ -24,6 +24,12 @@ namespace Avalonia.Media public static readonly StyledProperty TransformProperty = AvaloniaProperty.Register(nameof(Transform)); + /// + /// Defines the property + /// + public static readonly StyledProperty TransformOriginProperty = + AvaloniaProperty.Register(nameof(TransformOrigin)); + /// public event EventHandler? Invalidated; @@ -51,6 +57,15 @@ namespace Avalonia.Media set { SetValue(TransformProperty, value); } } + /// + /// Gets or sets the origin of the brush + /// + public RelativePoint TransformOrigin + { + get => GetValue(TransformOriginProperty); + set => SetValue(TransformOriginProperty, value); + } + /// /// Parses a brush string. /// diff --git a/src/Avalonia.Base/Media/IBrush.cs b/src/Avalonia.Base/Media/IBrush.cs index 830c066182..14ccf12b8f 100644 --- a/src/Avalonia.Base/Media/IBrush.cs +++ b/src/Avalonia.Base/Media/IBrush.cs @@ -17,5 +17,10 @@ namespace Avalonia.Media /// Gets the transform of the brush. /// ITransform? Transform { get; } + + /// + /// Gets the origin of the brushes + /// + RelativePoint TransformOrigin { get; } } } diff --git a/src/Avalonia.Base/Media/Immutable/ImmutableConicGradientBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableConicGradientBrush.cs index 4b97615c4c..70232f0a63 100644 --- a/src/Avalonia.Base/Media/Immutable/ImmutableConicGradientBrush.cs +++ b/src/Avalonia.Base/Media/Immutable/ImmutableConicGradientBrush.cs @@ -13,6 +13,7 @@ namespace Avalonia.Media.Immutable /// The gradient stops. /// The opacity of the brush. /// The transform of the brush. + /// The transform origin of the brush /// The spread method. /// The center point for the gradient. /// The starting angle for the gradient. @@ -20,10 +21,11 @@ namespace Avalonia.Media.Immutable IReadOnlyList gradientStops, double opacity = 1, ImmutableTransform? transform = null, + RelativePoint? transformOrigin = null, GradientSpreadMethod spreadMethod = GradientSpreadMethod.Pad, RelativePoint? center = null, double angle = 0) - : base(gradientStops, opacity, transform, spreadMethod) + : base(gradientStops, opacity, transform, transformOrigin, spreadMethod) { Center = center ?? RelativePoint.Center; Angle = angle; diff --git a/src/Avalonia.Base/Media/Immutable/ImmutableGradientBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableGradientBrush.cs index f1e51687d0..1e95acbf22 100644 --- a/src/Avalonia.Base/Media/Immutable/ImmutableGradientBrush.cs +++ b/src/Avalonia.Base/Media/Immutable/ImmutableGradientBrush.cs @@ -13,16 +13,19 @@ namespace Avalonia.Media.Immutable /// The gradient stops. /// The opacity of the brush. /// The transform of the brush. + /// The transform origin of the brush /// The spread method. protected ImmutableGradientBrush( IReadOnlyList gradientStops, double opacity, ImmutableTransform? transform, + RelativePoint? transformOrigin, GradientSpreadMethod spreadMethod) { GradientStops = gradientStops; Opacity = opacity; Transform = transform; + TransformOrigin = transformOrigin.HasValue ? transformOrigin.Value : RelativePoint.TopLeft; SpreadMethod = spreadMethod; } @@ -31,7 +34,8 @@ namespace Avalonia.Media.Immutable /// /// The brush from which this brush's properties should be copied. protected ImmutableGradientBrush(GradientBrush source) - : this(source.GradientStops.ToImmutable(), source.Opacity, source.Transform?.ToImmutable(), source.SpreadMethod) + : this(source.GradientStops.ToImmutable(), source.Opacity, source.Transform?.ToImmutable(), + source.TransformOrigin, source.SpreadMethod) { } @@ -47,6 +51,11 @@ namespace Avalonia.Media.Immutable /// public ITransform? Transform { get; } + /// + /// Gets the transform origin of the brush + /// + public RelativePoint TransformOrigin { get; } + /// public GradientSpreadMethod SpreadMethod { get; } } diff --git a/src/Avalonia.Base/Media/Immutable/ImmutableImageBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableImageBrush.cs index c36e82eacb..f9892bf60c 100644 --- a/src/Avalonia.Base/Media/Immutable/ImmutableImageBrush.cs +++ b/src/Avalonia.Base/Media/Immutable/ImmutableImageBrush.cs @@ -16,6 +16,7 @@ namespace Avalonia.Media.Immutable /// The rectangle on the destination in which to paint a tile. /// The opacity of the brush. /// The transform of the brush. + /// The transform origin of the brush /// The rectangle of the source image that will be displayed. /// /// How the source rectangle will be stretched to fill the destination rect. @@ -29,6 +30,7 @@ namespace Avalonia.Media.Immutable RelativeRect? destinationRect = null, double opacity = 1, ImmutableTransform? transform = null, + RelativePoint transformOrigin = new RelativePoint(), RelativeRect? sourceRect = null, Stretch stretch = Stretch.Uniform, TileMode tileMode = TileMode.None, @@ -39,6 +41,7 @@ namespace Avalonia.Media.Immutable destinationRect ?? RelativeRect.Fill, opacity, transform, + transformOrigin, sourceRect ?? RelativeRect.Fill, stretch, tileMode, diff --git a/src/Avalonia.Base/Media/Immutable/ImmutableLinearGradientBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableLinearGradientBrush.cs index 64c0f9b44e..3c26b5c009 100644 --- a/src/Avalonia.Base/Media/Immutable/ImmutableLinearGradientBrush.cs +++ b/src/Avalonia.Base/Media/Immutable/ImmutableLinearGradientBrush.cs @@ -13,6 +13,7 @@ namespace Avalonia.Media.Immutable /// The gradient stops. /// The opacity of the brush. /// The transform of the brush. + /// The transform origin of the brush /// The spread method. /// The start point for the gradient. /// The end point for the gradient. @@ -20,10 +21,11 @@ namespace Avalonia.Media.Immutable IReadOnlyList gradientStops, double opacity = 1, ImmutableTransform? transform = null, + RelativePoint? transformOrigin = null, GradientSpreadMethod spreadMethod = GradientSpreadMethod.Pad, RelativePoint? startPoint = null, RelativePoint? endPoint = null) - : base(gradientStops, opacity, transform, spreadMethod) + : base(gradientStops, opacity, transform, transformOrigin, spreadMethod) { StartPoint = startPoint ?? RelativePoint.TopLeft; EndPoint = endPoint ?? RelativePoint.BottomRight; diff --git a/src/Avalonia.Base/Media/Immutable/ImmutableRadialGradientBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableRadialGradientBrush.cs index 3da4bdd8e9..e08d2810f8 100644 --- a/src/Avalonia.Base/Media/Immutable/ImmutableRadialGradientBrush.cs +++ b/src/Avalonia.Base/Media/Immutable/ImmutableRadialGradientBrush.cs @@ -13,6 +13,7 @@ namespace Avalonia.Media.Immutable /// The gradient stops. /// The opacity of the brush. /// The transform of the brush. + /// The transform origin of the brush /// The spread method. /// The start point for the gradient. /// @@ -25,11 +26,12 @@ namespace Avalonia.Media.Immutable IReadOnlyList gradientStops, double opacity = 1, ImmutableTransform? transform = null, + RelativePoint? transformOrigin = null, GradientSpreadMethod spreadMethod = GradientSpreadMethod.Pad, RelativePoint? center = null, RelativePoint? gradientOrigin = null, double radius = 0.5) - : base(gradientStops, opacity, transform, spreadMethod) + : base(gradientStops, opacity, transform, transformOrigin, spreadMethod) { Center = center ?? RelativePoint.Center; GradientOrigin = gradientOrigin ?? RelativePoint.Center; diff --git a/src/Avalonia.Base/Media/Immutable/ImmutableSolidColorBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableSolidColorBrush.cs index 9b1b2500ef..6755dfd236 100644 --- a/src/Avalonia.Base/Media/Immutable/ImmutableSolidColorBrush.cs +++ b/src/Avalonia.Base/Media/Immutable/ImmutableSolidColorBrush.cs @@ -53,6 +53,11 @@ namespace Avalonia.Media.Immutable /// public ITransform? Transform { get; } + /// + /// Gets the transform origin of the brush + /// + public RelativePoint TransformOrigin { get; } + public bool Equals(ImmutableSolidColorBrush? other) { if (ReferenceEquals(null, other)) return false; diff --git a/src/Avalonia.Base/Media/Immutable/ImmutableTileBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableTileBrush.cs index 2c1844a2c2..6df27872fc 100644 --- a/src/Avalonia.Base/Media/Immutable/ImmutableTileBrush.cs +++ b/src/Avalonia.Base/Media/Immutable/ImmutableTileBrush.cs @@ -15,6 +15,7 @@ namespace Avalonia.Media.Immutable /// The rectangle on the destination in which to paint a tile. /// The opacity of the brush. /// The transform of the brush. + /// The transform origin of the brush /// The rectangle of the source image that will be displayed. /// /// How the source rectangle will be stretched to fill the destination rect. @@ -27,6 +28,7 @@ namespace Avalonia.Media.Immutable RelativeRect destinationRect, double opacity, ImmutableTransform? transform, + RelativePoint transformOrigin, RelativeRect sourceRect, Stretch stretch, TileMode tileMode, @@ -37,6 +39,7 @@ namespace Avalonia.Media.Immutable DestinationRect = destinationRect; Opacity = opacity; Transform = transform; + TransformOrigin = transformOrigin; SourceRect = sourceRect; Stretch = stretch; TileMode = tileMode; @@ -54,6 +57,7 @@ namespace Avalonia.Media.Immutable source.DestinationRect, source.Opacity, source.Transform?.ToImmutable(), + source.TransformOrigin, source.SourceRect, source.Stretch, source.TileMode, @@ -78,6 +82,11 @@ namespace Avalonia.Media.Immutable /// public ITransform? Transform { get; } + /// + /// Gets the transform origin of the brush + /// + public RelativePoint TransformOrigin { get; } + /// public RelativeRect SourceRect { get; } diff --git a/src/Avalonia.Base/Media/Immutable/ImmutableVisualBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableVisualBrush.cs index 8ecef63237..b436dcdb5e 100644 --- a/src/Avalonia.Base/Media/Immutable/ImmutableVisualBrush.cs +++ b/src/Avalonia.Base/Media/Immutable/ImmutableVisualBrush.cs @@ -17,6 +17,7 @@ namespace Avalonia.Media.Immutable /// The rectangle on the destination in which to paint a tile. /// The opacity of the brush. /// The transform of the brush. + /// The transform origin of the brush /// The rectangle of the source image that will be displayed. /// /// How the source rectangle will be stretched to fill the destination rect. @@ -30,6 +31,7 @@ namespace Avalonia.Media.Immutable RelativeRect? destinationRect = null, double opacity = 1, ImmutableTransform? transform = null, + RelativePoint transformOrigin = new RelativePoint(), RelativeRect? sourceRect = null, Stretch stretch = Stretch.Uniform, TileMode tileMode = TileMode.None, @@ -40,6 +42,7 @@ namespace Avalonia.Media.Immutable destinationRect ?? RelativeRect.Fill, opacity, transform, + transformOrigin, sourceRect ?? RelativeRect.Fill, stretch, tileMode, From 488108aab24c59547e4cac443e28bd41d2f7f0bc Mon Sep 17 00:00:00 2001 From: amwx <40413319+amwx@users.noreply.github.com> Date: Fri, 22 Apr 2022 21:34:27 -0400 Subject: [PATCH 207/416] Support for TransformOrigin in Skia DC --- src/Skia/Avalonia.Skia/DrawingContextImpl.cs | 39 ++++++++++++++++---- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs index 2548b9f5aa..7e0360d691 100644 --- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs +++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs @@ -180,7 +180,8 @@ namespace Avalonia.Skia var size = geometry.Bounds.Size; using (var fill = brush != null ? CreatePaint(_fillPaint, brush, size) : default(PaintWrapper)) - using (var stroke = pen?.Brush != null ? CreatePaint(_strokePaint, pen, size) : default(PaintWrapper)) + using (var stroke = pen?.Brush != null ? CreatePaint(_strokePaint, pen, + size.Inflate(new Thickness(pen?.Thickness / 2 ?? 0))) : default(PaintWrapper)) { if (fill.Paint != null) { @@ -397,7 +398,7 @@ namespace Avalonia.Skia if (pen?.Brush != null) { - using (var paint = CreatePaint(_strokePaint, pen, rect.Rect.Size)) + using (var paint = CreatePaint(_strokePaint, pen, rect.Rect.Size.Inflate(new Thickness(pen?.Thickness / 2 ?? 0)))) { if (paint.Paint is object) { @@ -432,7 +433,7 @@ namespace Avalonia.Skia if (pen?.Brush != null) { - using (var paint = CreatePaint(_strokePaint, pen, rect.Size)) + using (var paint = CreatePaint(_strokePaint, pen, rect.Size.Inflate(new Thickness(pen?.Thickness / 2 ?? 0)))) { if (paint.Paint is object) { @@ -624,8 +625,12 @@ namespace Avalonia.Skia } else { + var transformOrigin = linearGradient.TransformOrigin.ToPixels(targetSize); + var offset = Matrix.CreateTranslation(transformOrigin); + var transform = (-offset) * linearGradient.Transform.Value * (offset); + using (var shader = - SKShader.CreateLinearGradient(start, end, stopColors, stopOffsets, tileMode, linearGradient.Transform.Value.ToSKMatrix())) + SKShader.CreateLinearGradient(start, end, stopColors, stopOffsets, tileMode, transform.ToSKMatrix())) { paintWrapper.Paint.Shader = shader; } @@ -653,8 +658,12 @@ namespace Avalonia.Skia } else { + var transformOrigin = radialGradient.TransformOrigin.ToPixels(targetSize); + var offset = Matrix.CreateTranslation(transformOrigin); + var transform = (-offset) * radialGradient.Transform.Value * (offset); + using (var shader = - SKShader.CreateRadialGradient(center, radius, stopColors, stopOffsets, tileMode, radialGradient.Transform.Value.ToSKMatrix())) + SKShader.CreateRadialGradient(center, radius, stopColors, stopOffsets, tileMode, transform.ToSKMatrix())) { paintWrapper.Paint.Shader = shader; } @@ -693,9 +702,14 @@ namespace Avalonia.Skia } else { + + var transformOrigin = radialGradient.TransformOrigin.ToPixels(targetSize); + var offset = Matrix.CreateTranslation(transformOrigin); + var transform = (-offset) * radialGradient.Transform.Value * (offset); + using (var shader = SKShader.CreateCompose( SKShader.CreateColor(reversedColors[0]), - SKShader.CreateTwoPointConicalGradient(center, radius, origin, 0, reversedColors, reversedStops, tileMode, radialGradient.Transform.Value.ToSKMatrix()) + SKShader.CreateTwoPointConicalGradient(center, radius, origin, 0, reversedColors, reversedStops, tileMode, transform.ToSKMatrix()) )) { paintWrapper.Paint.Shader = shader; @@ -716,7 +730,12 @@ namespace Avalonia.Skia if (conicGradient.Transform is { }) { - rotation = rotation.PreConcat(conicGradient.Transform.Value.ToSKMatrix()); + + var transformOrigin = conicGradient.TransformOrigin.ToPixels(targetSize); + var offset = Matrix.CreateTranslation(transformOrigin); + var transform = (-offset) * conicGradient.Transform.Value * (offset); + + rotation = rotation.PreConcat(transform.ToSKMatrix()); } using (var shader = @@ -793,7 +812,11 @@ namespace Avalonia.Skia if (tileBrush.Transform is { }) { - paintTransform = paintTransform.PreConcat(tileBrush.Transform.Value.ToSKMatrix()); + var origin = tileBrush.TransformOrigin.ToPixels(targetSize); + var offset = Matrix.CreateTranslation(origin); + var transform = (-offset) * tileBrush.Transform.Value * (offset); + + paintTransform = paintTransform.PreConcat(transform.ToSKMatrix()); } using (var shader = image.ToShader(tileX, tileY, paintTransform)) From 6b2cedfcd1b763691373ba0b6ffb5f0cb6c9f445 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 23 Apr 2022 10:15:46 +0200 Subject: [PATCH 208/416] Fix failing test. It was probably not a good idea to test the contents of the template explicitly in unit tests, but here we are... --- .../Primitives/PopupRootTests.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs index f27ff3928c..6d3351d2b2 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/PopupRootTests.cs @@ -78,9 +78,13 @@ namespace Avalonia.Controls.UnitTests.Primitives var templatedChild = ((Visual)target.Host).GetVisualChildren().Single(); - Assert.IsType(templatedChild); + Assert.IsType(templatedChild); - var visualLayerManager = templatedChild.GetVisualChildren().Skip(1).Single(); + var panel = templatedChild.GetVisualChildren().Single(); + + Assert.IsType(panel); + + var visualLayerManager = panel.GetVisualChildren().Skip(1).Single(); Assert.IsType(visualLayerManager); From 0cdbd53bc312d2711461552c88a5250c1c1d3c1a Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Mon, 25 Apr 2022 15:21:26 +0200 Subject: [PATCH 209/416] Rewrite TextBounds test --- .../Media/TextFormatting/TextLineTests.cs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs index b58d9051f3..e3b9e5a8b1 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs @@ -665,14 +665,17 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting var text = "0123".AsMemory(); var shaperOption = new TextShaperOptions(Typeface.Default.GlyphTypeface, 10, 0, CultureInfo.CurrentCulture); + var shapedBuffer = TextShaper.Current.ShapeText(new ReadOnlySlice(text), shaperOption); + var firstRun = new ShapedTextCharacters(shapedBuffer, defaultProperties); + var textRuns = new List { - new ShapedTextCharacters(TextShaper.Current.ShapeText(new ReadOnlySlice(text), shaperOption), defaultProperties), new CustomDrawableRun(), - new ShapedTextCharacters(TextShaper.Current.ShapeText(new ReadOnlySlice(text, text.Length + 1, text.Length), shaperOption), defaultProperties), + firstRun, new CustomDrawableRun(), - new ShapedTextCharacters(TextShaper.Current.ShapeText(new ReadOnlySlice(text, text.Length * 2 + 2, text.Length), shaperOption), defaultProperties), + new ShapedTextCharacters(shapedBuffer, defaultProperties), new CustomDrawableRun(), + new ShapedTextCharacters(shapedBuffer, defaultProperties) }; var textSource = new FixedRunsTextSource(textRuns); @@ -687,6 +690,16 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting Assert.Equal(1, textBounds.Count); Assert.Equal(textLine.WidthIncludingTrailingWhitespace, textBounds.Sum(x => x.Rectangle.Width)); + + textBounds = textLine.GetTextBounds(0, firstRun.Text.Length); + + Assert.Equal(1, textBounds.Count); + Assert.Equal(firstRun.Size.Width, textBounds[0].Rectangle.Width); + + textBounds = textLine.GetTextBounds(0, firstRun.Text.Length + 1); + + Assert.Equal(1, textBounds.Count); + Assert.Equal(firstRun.Size.Width + 14, textBounds[0].Rectangle.Width); } } From 7c63e1a60b9b20ee5bec85c26a21a15f801c1600 Mon Sep 17 00:00:00 2001 From: Tako Date: Mon, 25 Apr 2022 17:42:52 +0300 Subject: [PATCH 210/416] Fix ContextMenu freeze. --- src/Avalonia.Controls/ItemsControl.cs | 2 +- .../Platform/DefaultMenuInteractionHandler.cs | 2 +- .../Primitives/SelectingItemsControlTests.cs | 45 +++++++++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs index 256160a116..56b0014c05 100644 --- a/src/Avalonia.Controls/ItemsControl.cs +++ b/src/Avalonia.Controls/ItemsControl.cs @@ -518,7 +518,7 @@ namespace Avalonia.Controls } c = result; - } while (c != null && c != from); + } while (c != null && c != from && direction != NavigationDirection.First && direction != NavigationDirection.Last); return null; } diff --git a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs index 6e9ac537f1..2f9bf0ac06 100644 --- a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs +++ b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs @@ -417,7 +417,7 @@ namespace Avalonia.Controls.Platform protected internal virtual void MenuOpened(object? sender, RoutedEventArgs e) { - if (e.Source == Menu) + if (e.Source is Menu) { Menu?.MoveSelection(NavigationDirection.First, true); } diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs index 0e0ca7cd25..3d36395c3a 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs @@ -6,6 +6,7 @@ using System.Collections.Specialized; using System.ComponentModel; using System.Linq; using System.Reactive.Disposables; +using System.Threading.Tasks; using Avalonia.Collections; using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives; @@ -1614,6 +1615,50 @@ namespace Avalonia.Controls.UnitTests.Primitives target.MoveSelection(NavigationDirection.Next, true); } + [Fact(Timeout = 2000)] + public async Task MoveSelection_Does_Not_Hang_With_No_Focusable_Controls_And_Moving_Selection_To_The_First_Item() + { + var target = new TestSelector + { + Template = Template(), + Items = new[] + { + new ListBoxItem { Focusable = false }, + new ListBoxItem(), + } + }; + + target.Measure(new Size(100, 100)); + target.Arrange(new Rect(0, 0, 100, 100)); + + // Timeout in xUnit doesen't work with synchronous methods so we need to apply hack below. + // https://github.com/xunit/xunit/issues/2222 + await Task.Run(() => target.MoveSelection(NavigationDirection.First, true)); + Assert.Equal(-1, target.SelectedIndex); + } + + [Fact(Timeout = 2000)] + public async Task MoveSelection_Does_Not_Hang_With_No_Focusable_Controls_And_Moving_Selection_To_The_Last_Item() + { + var target = new TestSelector + { + Template = Template(), + Items = new[] + { + new ListBoxItem(), + new ListBoxItem { Focusable = false }, + } + }; + + target.Measure(new Size(100, 100)); + target.Arrange(new Rect(0, 0, 100, 100)); + + // Timeout in xUnit doesen't work with synchronous methods so we need to apply hack below. + // https://github.com/xunit/xunit/issues/2222 + await Task.Run(() => target.MoveSelection(NavigationDirection.Last, true)); + Assert.Equal(-1, target.SelectedIndex); + } + [Fact] public void MoveSelection_Does_Select_Disabled_Controls() { From d75732699cb4ad2ed53ae441fe83ebbe0a27a758 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 26 Apr 2022 16:59:58 +0200 Subject: [PATCH 211/416] Added attributes describing API stability. Adds two attributes which describe the stability of an API: - `UnstableAttribute` indicates that we provide no API stability guarantees for the class between minor/patch versions. This is mostly applied to `*Impl` interfaces which describe the interface between core Avalonia and platform implementations - `NotClientImplementableAttribute` indicates that an interface is stable for consumption by a client, but should not be implemented outside of Avalonia itself (either because custom implementations are not supported, or because as members may be added to its API) --- build/ApiCompatAttributeExcludeList.txt | 2 ++ src/Avalonia.Base/Animation/IAnimation.cs | 2 ++ src/Avalonia.Base/Animation/IAnimationSetter.cs | 3 +++ src/Avalonia.Base/Animation/IAnimator.cs | 2 ++ src/Avalonia.Base/Animation/IClock.cs | 4 ++-- src/Avalonia.Base/Animation/IGlobalClock.cs | 5 ++--- src/Avalonia.Base/Animation/ITransition.cs | 2 ++ src/Avalonia.Base/Controls/INameScope.cs | 4 ++-- src/Avalonia.Base/Controls/IPseudoClasses.cs | 2 ++ src/Avalonia.Base/Controls/IResourceHost.cs | 4 ++-- src/Avalonia.Base/Controls/IResourceNode.cs | 4 ++-- .../Controls/ISetInheritanceParent.cs | 3 ++- src/Avalonia.Base/Controls/ISetLogicalParent.cs | 4 ++-- src/Avalonia.Base/Data/Core/IPropertyInfo.cs | 2 ++ src/Avalonia.Base/Data/IBinding.cs | 3 +++ src/Avalonia.Base/IAvaloniaObject.cs | 2 ++ src/Avalonia.Base/IDataContextProvider.cs | 3 ++- src/Avalonia.Base/IDirectPropertyAccessor.cs | 1 + src/Avalonia.Base/IDirectPropertyMetadata.cs | 3 +++ src/Avalonia.Base/IStyledElement.cs | 4 ++-- src/Avalonia.Base/IStyledPropertyMetadata.cs | 3 +++ src/Avalonia.Base/Input/IAccessKeyHandler.cs | 3 +++ src/Avalonia.Base/Input/IFocusManager.cs | 3 +++ src/Avalonia.Base/Input/IInputDevice.cs | 2 ++ src/Avalonia.Base/Input/IInputElement.cs | 2 ++ src/Avalonia.Base/Input/IInputManager.cs | 2 ++ src/Avalonia.Base/Input/IInputRoot.cs | 3 +++ src/Avalonia.Base/Input/IKeyboardDevice.cs | 2 ++ .../Input/IKeyboardNavigationHandler.cs | 3 +++ src/Avalonia.Base/Input/IMainMenu.cs | 2 ++ src/Avalonia.Base/Input/IMouseDevice.cs | 2 ++ src/Avalonia.Base/Input/IPointer.cs | 3 +++ src/Avalonia.Base/Input/IPointerDevice.cs | 2 ++ src/Avalonia.Base/Input/Platform/IClipboard.cs | 2 ++ .../Input/Platform/IPlatformDragSource.cs | 2 ++ src/Avalonia.Base/Input/Raw/IDragDropDevice.cs | 5 ++++- .../Input/TextInput/ITextInputMethodImpl.cs | 4 ++++ src/Avalonia.Base/Interactivity/IInteractive.cs | 2 ++ src/Avalonia.Base/Layout/ILayoutManager.cs | 4 ++-- src/Avalonia.Base/Layout/ILayoutRoot.cs | 3 +++ src/Avalonia.Base/Layout/ILayoutable.cs | 4 ++-- src/Avalonia.Base/LogicalTree/ILogical.cs | 2 ++ src/Avalonia.Base/LogicalTree/ILogicalRoot.cs | 5 ++++- src/Avalonia.Base/Media/IBrush.cs | 2 ++ src/Avalonia.Base/Media/IConicGradientBrush.cs | 5 ++++- src/Avalonia.Base/Media/IDashStyle.cs | 2 ++ .../Media/IExperimentalAcrylicMaterial.cs | 5 ++++- src/Avalonia.Base/Media/IGradientBrush.cs | 2 ++ src/Avalonia.Base/Media/IGradientStop.cs | 5 ++++- src/Avalonia.Base/Media/IImageBrush.cs | 4 +++- src/Avalonia.Base/Media/ILinearGradientBrush.cs | 7 +++++-- src/Avalonia.Base/Media/IMutableBrush.cs | 2 ++ .../IMutableExperimentalAcrylicMaterial.cs | 5 ++++- src/Avalonia.Base/Media/IMutableTransform.cs | 1 + src/Avalonia.Base/Media/IPen.cs | 5 ++++- src/Avalonia.Base/Media/IRadialGradientBrush.cs | 7 +++++-- src/Avalonia.Base/Media/ISolidColorBrush.cs | 5 ++++- src/Avalonia.Base/Media/ITileBrush.cs | 4 +++- src/Avalonia.Base/Media/IVisualBrush.cs | 6 ++++-- src/Avalonia.Base/Media/Imaging/IBitmap.cs | 2 ++ .../Media/TextFormatting/ITextSource.cs | 5 ++++- .../Metadata/NotClientImplementableAttribute.cs | 17 +++++++++++++++++ src/Avalonia.Base/Metadata/UnstableAttribute.cs | 12 ++++++++++++ src/Avalonia.Base/Platform/IAssetLoader.cs | 2 ++ src/Avalonia.Base/Platform/IBitmapImpl.cs | 2 ++ src/Avalonia.Base/Platform/ICursorImpl.cs | 2 ++ .../Platform/IDrawingContextImpl.cs | 2 ++ src/Avalonia.Base/Platform/IFontManagerImpl.cs | 2 ++ src/Avalonia.Base/Platform/IGeometryImpl.cs | 2 ++ src/Avalonia.Base/Platform/IGlyphRunImpl.cs | 2 ++ .../Platform/IGlyphTypefaceImpl.cs | 2 ++ .../Platform/IMacOSTopLevelPlatformHandle.cs | 2 ++ .../Platform/IPlatformRenderInterface.cs | 2 ++ src/Avalonia.Base/Platform/IPlatformSettings.cs | 2 ++ .../Platform/IPlatformThreadingInterface.cs | 2 ++ .../Platform/IRenderTargetBitmapImpl.cs | 2 ++ src/Avalonia.Base/Platform/IRuntimePlatform.cs | 5 +++++ .../Platform/IStreamGeometryContextImpl.cs | 3 +++ .../Platform/IStreamGeometryImpl.cs | 3 +++ src/Avalonia.Base/Platform/ITextShaperImpl.cs | 2 ++ .../Platform/ITransformedGeometryImpl.cs | 5 ++++- .../Platform/IWriteableBitmapImpl.cs | 5 ++++- .../Platform/Interop/IDynamicLibraryLoader.cs | 2 ++ .../Rendering/IDeferredRendererLock.cs | 2 ++ src/Avalonia.Base/Rendering/IRenderLoop.cs | 5 ++++- src/Avalonia.Base/Rendering/IRenderRoot.cs | 2 ++ src/Avalonia.Base/Rendering/IRenderTimer.cs | 2 ++ .../Rendering/IVisualBrushInitialize.cs | 2 ++ .../Rendering/IVisualBrushRenderer.cs | 2 ++ .../Styling/Activators/IStyleActivator.cs | 6 +++--- .../Styling/Activators/IStyleActivatorSink.cs | 3 ++- src/Avalonia.Base/Styling/IGlobalStyles.cs | 4 ++-- src/Avalonia.Base/Styling/ISetter.cs | 4 ++-- src/Avalonia.Base/Styling/ISetterInstance.cs | 6 +++--- src/Avalonia.Base/Styling/IStyle.cs | 5 ++--- src/Avalonia.Base/Styling/IStyleHost.cs | 2 ++ src/Avalonia.Base/Styling/IStyleInstance.cs | 4 ++-- src/Avalonia.Base/Styling/IStyleable.cs | 2 ++ src/Avalonia.Base/Styling/ITemplatedControl.cs | 2 ++ src/Avalonia.Base/VisualTree/IVisual.cs | 4 ++-- .../IApplicationLifetime.cs | 3 +++ .../IClassicDesktopStyleApplicationLifetime.cs | 2 ++ .../IControlledApplicationLifetime.cs | 2 ++ .../ISingleViewApplicationLifetime.cs | 3 +++ .../Diagnostics/IPopupHostProvider.cs | 2 ++ .../Offscreen/OffscreenTopLevelImpl.cs | 2 ++ src/Avalonia.Controls/IContentControl.cs | 2 ++ src/Avalonia.Controls/IControl.cs | 2 ++ src/Avalonia.Controls/IGlobalDataTemplates.cs | 2 ++ src/Avalonia.Controls/IMenu.cs | 2 ++ src/Avalonia.Controls/IMenuElement.cs | 2 ++ src/Avalonia.Controls/IMenuItem.cs | 5 ++++- .../INativeMenuExporterEventsImplBridge.cs | 3 +++ .../INativeMenuItemExporterEventsImplBridge.cs | 3 +++ src/Avalonia.Controls/IPanel.cs | 5 ++++- src/Avalonia.Controls/IScrollable.cs | 1 - .../IManagedNotificationManager.cs | 5 ++++- .../Notifications/INotification.cs | 2 ++ .../Notifications/INotificationManager.cs | 5 ++++- .../Platform/DefaultMenuInteractionHandler.cs | 2 ++ .../Platform/IApplicationPlatformEvents.cs | 3 +++ .../Platform/IMenuInteractionHandler.cs | 5 ++++- .../Platform/IMountedVolumeInfoProvider.cs | 4 ++-- .../Platform/INativeControlHostImpl.cs | 6 +++++- .../Platform/IPlatformIconLoader.cs | 2 ++ .../Platform/IPlatformLifetimeEventsImpl.cs | 3 ++- .../Platform/IPlatformNativeSurfaceHandle.cs | 2 ++ src/Avalonia.Controls/Platform/IPopupImpl.cs | 2 ++ src/Avalonia.Controls/Platform/IScreenImpl.cs | 4 ++-- .../Platform/ISystemDialogImpl.cs | 2 ++ src/Avalonia.Controls/Platform/ITopLevelImpl.cs | 2 ++ .../ITopLevelImplWithTextInputMethod.cs | 2 ++ .../Platform/ITopLevelNativeMenuExporter.cs | 7 ++++++- src/Avalonia.Controls/Platform/ITrayIconImpl.cs | 2 ++ .../Platform/IWindowBaseImpl.cs | 2 ++ .../Platform/IWindowIconImpl.cs | 2 ++ src/Avalonia.Controls/Platform/IWindowImpl.cs | 2 ++ .../Platform/IWindowingPlatform.cs | 3 +++ .../InternalPlatformThreadingInterface.cs | 5 ++--- .../Platform/MountedDriveInfo.cs | 2 ++ .../Platform/PlatformManager.cs | 2 ++ .../Surfaces/IFramebufferPlatformSurface.cs | 4 +++- .../Presenters/IContentPresenter.cs | 2 ++ .../Presenters/IContentPresenterHost.cs | 2 ++ .../Presenters/IItemsPresenter.cs | 2 ++ .../Presenters/IItemsPresenterHost.cs | 2 ++ src/Avalonia.Controls/Presenters/IPresenter.cs | 2 ++ src/Avalonia.Controls/Primitives/IPopupHost.cs | 2 ++ .../PopupPositioning/IPopupPositioner.cs | 4 ++++ src/Avalonia.Controls/Remote/RemoteServer.cs | 3 ++- .../Remote/Server/RemoteServerTopLevelImpl.cs | 2 ++ .../Templates/IDataTemplateHost.cs | 4 +++- .../Imaging/IOpenGlBitmapImpl.cs | 4 +++- src/Skia/Avalonia.Skia/GlyphRunImpl.cs | 2 ++ src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs | 2 ++ .../Avalonia.Skia/ISkiaDrawingContextImpl.cs | 2 ++ .../Avalonia.Direct2D1/Media/BrushImpl.cs | 2 ++ .../Media/DrawingContextImpl.cs | 2 ++ .../Avalonia.Direct2D1/Media/GeometryImpl.cs | 2 ++ .../Media/GlyphTypefaceImpl.cs | 2 ++ .../Avalonia.Direct2D1/Media/ImageBrushImpl.cs | 2 ++ .../Media/Imaging/BitmapImpl.cs | 2 ++ .../Media/Imaging/D2DBitmapImpl.cs | 2 ++ .../Media/Imaging/D2DRenderTargetBitmapImpl.cs | 2 ++ .../Media/Imaging/WicBitmapImpl.cs | 2 ++ .../Media/Imaging/WicRenderTargetBitmapImpl.cs | 2 ++ .../Media/LinearGradientBrushImpl.cs | 2 ++ .../Media/RadialGradientBrushImpl.cs | 2 ++ .../Media/SolidColorBrushImpl.cs | 2 ++ .../Media/StreamGeometryContextImpl.cs | 2 ++ .../Media/StreamGeometryImpl.cs | 2 ++ .../Media/TransformedGeometryImpl.cs | 2 ++ src/Windows/Avalonia.Win32/ScreenImpl.cs | 2 ++ src/Windows/Avalonia.Win32/TrayIconImpl.cs | 2 ++ src/Windows/Avalonia.Win32/WindowImpl.cs | 2 ++ 175 files changed, 452 insertions(+), 79 deletions(-) create mode 100644 build/ApiCompatAttributeExcludeList.txt create mode 100644 src/Avalonia.Base/Metadata/NotClientImplementableAttribute.cs create mode 100644 src/Avalonia.Base/Metadata/UnstableAttribute.cs diff --git a/build/ApiCompatAttributeExcludeList.txt b/build/ApiCompatAttributeExcludeList.txt new file mode 100644 index 0000000000..1df5a30ec3 --- /dev/null +++ b/build/ApiCompatAttributeExcludeList.txt @@ -0,0 +1,2 @@ +T:Avalonia.Metadata.NotClientImplementableAttribute +T:Avalonia.Metadata.UnstableAttribute diff --git a/src/Avalonia.Base/Animation/IAnimation.cs b/src/Avalonia.Base/Animation/IAnimation.cs index 436a765d27..eda86ed106 100644 --- a/src/Avalonia.Base/Animation/IAnimation.cs +++ b/src/Avalonia.Base/Animation/IAnimation.cs @@ -1,12 +1,14 @@ using System; using System.Threading; using System.Threading.Tasks; +using Avalonia.Metadata; namespace Avalonia.Animation { /// /// Interface for Animation objects /// + [NotClientImplementable] public interface IAnimation { /// diff --git a/src/Avalonia.Base/Animation/IAnimationSetter.cs b/src/Avalonia.Base/Animation/IAnimationSetter.cs index 6a1d3539e2..8c4ba95517 100644 --- a/src/Avalonia.Base/Animation/IAnimationSetter.cs +++ b/src/Avalonia.Base/Animation/IAnimationSetter.cs @@ -1,5 +1,8 @@ +using Avalonia.Metadata; + namespace Avalonia.Animation { + [NotClientImplementable] public interface IAnimationSetter { AvaloniaProperty? Property { get; set; } diff --git a/src/Avalonia.Base/Animation/IAnimator.cs b/src/Avalonia.Base/Animation/IAnimator.cs index f64ac9f913..06ba8dc329 100644 --- a/src/Avalonia.Base/Animation/IAnimator.cs +++ b/src/Avalonia.Base/Animation/IAnimator.cs @@ -1,11 +1,13 @@ using System; using System.Collections.Generic; +using Avalonia.Metadata; namespace Avalonia.Animation { /// /// Interface for Animator objects /// + [NotClientImplementable] public interface IAnimator : IList { /// diff --git a/src/Avalonia.Base/Animation/IClock.cs b/src/Avalonia.Base/Animation/IClock.cs index ae44102077..7b3b7b3924 100644 --- a/src/Avalonia.Base/Animation/IClock.cs +++ b/src/Avalonia.Base/Animation/IClock.cs @@ -1,9 +1,9 @@ using System; -using System.Collections.Generic; -using System.Text; +using Avalonia.Metadata; namespace Avalonia.Animation { + [NotClientImplementable] public interface IClock : IObservable { PlayState PlayState { get; set; } diff --git a/src/Avalonia.Base/Animation/IGlobalClock.cs b/src/Avalonia.Base/Animation/IGlobalClock.cs index b0455e2c80..138dc07539 100644 --- a/src/Avalonia.Base/Animation/IGlobalClock.cs +++ b/src/Avalonia.Base/Animation/IGlobalClock.cs @@ -1,9 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Text; +using Avalonia.Metadata; namespace Avalonia.Animation { + [NotClientImplementable] public interface IGlobalClock : IClock { } diff --git a/src/Avalonia.Base/Animation/ITransition.cs b/src/Avalonia.Base/Animation/ITransition.cs index 241ca208d1..639b92ce35 100644 --- a/src/Avalonia.Base/Animation/ITransition.cs +++ b/src/Avalonia.Base/Animation/ITransition.cs @@ -1,10 +1,12 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Animation { /// /// Interface for Transition objects. /// + [NotClientImplementable] public interface ITransition { /// diff --git a/src/Avalonia.Base/Controls/INameScope.cs b/src/Avalonia.Base/Controls/INameScope.cs index 1ca7db2f37..a12e88c055 100644 --- a/src/Avalonia.Base/Controls/INameScope.cs +++ b/src/Avalonia.Base/Controls/INameScope.cs @@ -1,5 +1,4 @@ -using System; -using System.Threading.Tasks; +using Avalonia.Metadata; using Avalonia.Utilities; namespace Avalonia.Controls @@ -7,6 +6,7 @@ namespace Avalonia.Controls /// /// Defines a name scope. /// + [NotClientImplementable] public interface INameScope { /// diff --git a/src/Avalonia.Base/Controls/IPseudoClasses.cs b/src/Avalonia.Base/Controls/IPseudoClasses.cs index 27290716d0..eda521727f 100644 --- a/src/Avalonia.Base/Controls/IPseudoClasses.cs +++ b/src/Avalonia.Base/Controls/IPseudoClasses.cs @@ -1,9 +1,11 @@ +using Avalonia.Metadata; namespace Avalonia.Controls { /// /// Exposes an interface for setting pseudoclasses on a collection. /// + [NotClientImplementable] public interface IPseudoClasses { /// diff --git a/src/Avalonia.Base/Controls/IResourceHost.cs b/src/Avalonia.Base/Controls/IResourceHost.cs index ea34a8b39a..286f0e36ef 100644 --- a/src/Avalonia.Base/Controls/IResourceHost.cs +++ b/src/Avalonia.Base/Controls/IResourceHost.cs @@ -1,6 +1,5 @@ using System; - -#nullable enable +using Avalonia.Metadata; namespace Avalonia.Controls { @@ -10,6 +9,7 @@ namespace Avalonia.Controls /// /// This interface is implemented by and `Application`. /// + [NotClientImplementable] public interface IResourceHost : IResourceNode { /// diff --git a/src/Avalonia.Base/Controls/IResourceNode.cs b/src/Avalonia.Base/Controls/IResourceNode.cs index 73bfeaf161..d6c900f97f 100644 --- a/src/Avalonia.Base/Controls/IResourceNode.cs +++ b/src/Avalonia.Base/Controls/IResourceNode.cs @@ -1,6 +1,5 @@ using System; - -#nullable enable +using Avalonia.Metadata; namespace Avalonia.Controls { @@ -12,6 +11,7 @@ namespace Avalonia.Controls /// () and resource providers such as /// (see ). /// + [NotClientImplementable] public interface IResourceNode { /// diff --git a/src/Avalonia.Base/Controls/ISetInheritanceParent.cs b/src/Avalonia.Base/Controls/ISetInheritanceParent.cs index dbf8c68892..e85e025005 100644 --- a/src/Avalonia.Base/Controls/ISetInheritanceParent.cs +++ b/src/Avalonia.Base/Controls/ISetInheritanceParent.cs @@ -1,4 +1,4 @@ -#nullable enable +using Avalonia.Metadata; namespace Avalonia.Controls { @@ -10,6 +10,7 @@ namespace Avalonia.Controls /// Additionally, also sets the inheritance parent; this /// interface is only needed where the logical and inheritance parents differ. /// + [NotClientImplementable] public interface ISetInheritanceParent { /// diff --git a/src/Avalonia.Base/Controls/ISetLogicalParent.cs b/src/Avalonia.Base/Controls/ISetLogicalParent.cs index 85bda05961..7f1b4b5d87 100644 --- a/src/Avalonia.Base/Controls/ISetLogicalParent.cs +++ b/src/Avalonia.Base/Controls/ISetLogicalParent.cs @@ -1,6 +1,5 @@ using Avalonia.LogicalTree; - -#nullable enable +using Avalonia.Metadata; namespace Avalonia.Controls { @@ -10,6 +9,7 @@ namespace Avalonia.Controls /// /// You should not usually need to use this interface - it is for advanced scenarios only. /// + [NotClientImplementable] public interface ISetLogicalParent { /// diff --git a/src/Avalonia.Base/Data/Core/IPropertyInfo.cs b/src/Avalonia.Base/Data/Core/IPropertyInfo.cs index 4d80feb4ba..0cb8a937cc 100644 --- a/src/Avalonia.Base/Data/Core/IPropertyInfo.cs +++ b/src/Avalonia.Base/Data/Core/IPropertyInfo.cs @@ -1,7 +1,9 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Data.Core { + [NotClientImplementable] public interface IPropertyInfo { string Name { get; } diff --git a/src/Avalonia.Base/Data/IBinding.cs b/src/Avalonia.Base/Data/IBinding.cs index 9535cf608e..7d44bf09b5 100644 --- a/src/Avalonia.Base/Data/IBinding.cs +++ b/src/Avalonia.Base/Data/IBinding.cs @@ -1,8 +1,11 @@ +using Avalonia.Metadata; + namespace Avalonia.Data { /// /// Holds a binding that can be applied to a property on an object. /// + [NotClientImplementable] public interface IBinding { /// diff --git a/src/Avalonia.Base/IAvaloniaObject.cs b/src/Avalonia.Base/IAvaloniaObject.cs index 00f5062f9e..3b0016903b 100644 --- a/src/Avalonia.Base/IAvaloniaObject.cs +++ b/src/Avalonia.Base/IAvaloniaObject.cs @@ -1,11 +1,13 @@ using System; using Avalonia.Data; +using Avalonia.Metadata; namespace Avalonia { /// /// Interface for getting/setting values on an object. /// + [NotClientImplementable] public interface IAvaloniaObject { /// diff --git a/src/Avalonia.Base/IDataContextProvider.cs b/src/Avalonia.Base/IDataContextProvider.cs index 1172adcaa4..1d381cf4a5 100644 --- a/src/Avalonia.Base/IDataContextProvider.cs +++ b/src/Avalonia.Base/IDataContextProvider.cs @@ -1,10 +1,11 @@ -#nullable enable +using Avalonia.Metadata; namespace Avalonia { /// /// Defines an element with a data context that can be used for binding. /// + [NotClientImplementable] public interface IDataContextProvider : IAvaloniaObject { /// diff --git a/src/Avalonia.Base/IDirectPropertyAccessor.cs b/src/Avalonia.Base/IDirectPropertyAccessor.cs index e34483fa7b..476a36f372 100644 --- a/src/Avalonia.Base/IDirectPropertyAccessor.cs +++ b/src/Avalonia.Base/IDirectPropertyAccessor.cs @@ -1,4 +1,5 @@ using System; +using Avalonia.Metadata; namespace Avalonia { diff --git a/src/Avalonia.Base/IDirectPropertyMetadata.cs b/src/Avalonia.Base/IDirectPropertyMetadata.cs index 4fd4a3a6b7..7d74470a13 100644 --- a/src/Avalonia.Base/IDirectPropertyMetadata.cs +++ b/src/Avalonia.Base/IDirectPropertyMetadata.cs @@ -1,8 +1,11 @@ +using Avalonia.Metadata; + namespace Avalonia { /// /// Untyped interface to /// + [NotClientImplementable] public interface IDirectPropertyMetadata { /// diff --git a/src/Avalonia.Base/IStyledElement.cs b/src/Avalonia.Base/IStyledElement.cs index a068d4a5bf..4eed54de45 100644 --- a/src/Avalonia.Base/IStyledElement.cs +++ b/src/Avalonia.Base/IStyledElement.cs @@ -2,12 +2,12 @@ using System.ComponentModel; using Avalonia.Controls; using Avalonia.LogicalTree; +using Avalonia.Metadata; using Avalonia.Styling; -#nullable enable - namespace Avalonia { + [NotClientImplementable] public interface IStyledElement : IStyleable, IStyleHost, diff --git a/src/Avalonia.Base/IStyledPropertyMetadata.cs b/src/Avalonia.Base/IStyledPropertyMetadata.cs index 6b29b5f977..89990338e0 100644 --- a/src/Avalonia.Base/IStyledPropertyMetadata.cs +++ b/src/Avalonia.Base/IStyledPropertyMetadata.cs @@ -1,8 +1,11 @@ +using Avalonia.Metadata; + namespace Avalonia { /// /// Untyped interface to /// + [NotClientImplementable] public interface IStyledPropertyMetadata { /// diff --git a/src/Avalonia.Base/Input/IAccessKeyHandler.cs b/src/Avalonia.Base/Input/IAccessKeyHandler.cs index e484d003c7..93a50968e2 100644 --- a/src/Avalonia.Base/Input/IAccessKeyHandler.cs +++ b/src/Avalonia.Base/Input/IAccessKeyHandler.cs @@ -1,8 +1,11 @@ +using Avalonia.Metadata; + namespace Avalonia.Input { /// /// Defines the interface for classes that handle access keys for a window. /// + [Unstable] public interface IAccessKeyHandler { /// diff --git a/src/Avalonia.Base/Input/IFocusManager.cs b/src/Avalonia.Base/Input/IFocusManager.cs index 2510479a8e..0c85cad2f7 100644 --- a/src/Avalonia.Base/Input/IFocusManager.cs +++ b/src/Avalonia.Base/Input/IFocusManager.cs @@ -1,8 +1,11 @@ +using Avalonia.Metadata; + namespace Avalonia.Input { /// /// Manages focus for the application. /// + [NotClientImplementable] public interface IFocusManager { /// diff --git a/src/Avalonia.Base/Input/IInputDevice.cs b/src/Avalonia.Base/Input/IInputDevice.cs index ab0fae65df..fe4f584f18 100644 --- a/src/Avalonia.Base/Input/IInputDevice.cs +++ b/src/Avalonia.Base/Input/IInputDevice.cs @@ -1,7 +1,9 @@ using Avalonia.Input.Raw; +using Avalonia.Metadata; namespace Avalonia.Input { + [NotClientImplementable] public interface IInputDevice { /// diff --git a/src/Avalonia.Base/Input/IInputElement.cs b/src/Avalonia.Base/Input/IInputElement.cs index d1552a3a2a..78001143d7 100644 --- a/src/Avalonia.Base/Input/IInputElement.cs +++ b/src/Avalonia.Base/Input/IInputElement.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using Avalonia.Interactivity; +using Avalonia.Metadata; using Avalonia.VisualTree; #nullable enable @@ -10,6 +11,7 @@ namespace Avalonia.Input /// /// Defines input-related functionality for a control. /// + [NotClientImplementable] public interface IInputElement : IInteractive, IVisual { /// diff --git a/src/Avalonia.Base/Input/IInputManager.cs b/src/Avalonia.Base/Input/IInputManager.cs index 80b71d3e47..658226a519 100644 --- a/src/Avalonia.Base/Input/IInputManager.cs +++ b/src/Avalonia.Base/Input/IInputManager.cs @@ -1,5 +1,6 @@ using System; using Avalonia.Input.Raw; +using Avalonia.Metadata; namespace Avalonia.Input { @@ -7,6 +8,7 @@ namespace Avalonia.Input /// Receives input from the windowing subsystem and dispatches it to interested parties /// for processing. /// + [NotClientImplementable] public interface IInputManager { /// diff --git a/src/Avalonia.Base/Input/IInputRoot.cs b/src/Avalonia.Base/Input/IInputRoot.cs index 98e8699573..7edc69df52 100644 --- a/src/Avalonia.Base/Input/IInputRoot.cs +++ b/src/Avalonia.Base/Input/IInputRoot.cs @@ -1,8 +1,11 @@ +using Avalonia.Metadata; + namespace Avalonia.Input { /// /// Defines the interface for top-level input elements. /// + [NotClientImplementable] public interface IInputRoot : IInputElement { /// diff --git a/src/Avalonia.Base/Input/IKeyboardDevice.cs b/src/Avalonia.Base/Input/IKeyboardDevice.cs index d0e84e5ad0..c8db8bf16f 100644 --- a/src/Avalonia.Base/Input/IKeyboardDevice.cs +++ b/src/Avalonia.Base/Input/IKeyboardDevice.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel; +using Avalonia.Metadata; namespace Avalonia.Input { @@ -50,6 +51,7 @@ namespace Avalonia.Input KeyboardMask = Alt | Control | Shift | Meta } + [NotClientImplementable] public interface IKeyboardDevice : IInputDevice, INotifyPropertyChanged { IInputElement? FocusedElement { get; } diff --git a/src/Avalonia.Base/Input/IKeyboardNavigationHandler.cs b/src/Avalonia.Base/Input/IKeyboardNavigationHandler.cs index 88d00b3b50..3bcd6d6206 100644 --- a/src/Avalonia.Base/Input/IKeyboardNavigationHandler.cs +++ b/src/Avalonia.Base/Input/IKeyboardNavigationHandler.cs @@ -1,8 +1,11 @@ +using Avalonia.Metadata; + namespace Avalonia.Input { /// /// Defines the interface for classes that handle keyboard navigation for a window. /// + [Unstable] public interface IKeyboardNavigationHandler { /// diff --git a/src/Avalonia.Base/Input/IMainMenu.cs b/src/Avalonia.Base/Input/IMainMenu.cs index 67b58c0ffc..213a979c28 100644 --- a/src/Avalonia.Base/Input/IMainMenu.cs +++ b/src/Avalonia.Base/Input/IMainMenu.cs @@ -1,5 +1,6 @@ using System; using Avalonia.Interactivity; +using Avalonia.Metadata; using Avalonia.VisualTree; namespace Avalonia.Input @@ -7,6 +8,7 @@ namespace Avalonia.Input /// /// Defines the interface for a window's main menu. /// + [NotClientImplementable] public interface IMainMenu : IVisual { /// diff --git a/src/Avalonia.Base/Input/IMouseDevice.cs b/src/Avalonia.Base/Input/IMouseDevice.cs index 6b7f0e76e5..2d66397d63 100644 --- a/src/Avalonia.Base/Input/IMouseDevice.cs +++ b/src/Avalonia.Base/Input/IMouseDevice.cs @@ -1,10 +1,12 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Input { /// /// Represents a mouse device. /// + [NotClientImplementable] public interface IMouseDevice : IPointerDevice { /// diff --git a/src/Avalonia.Base/Input/IPointer.cs b/src/Avalonia.Base/Input/IPointer.cs index 7af48cef82..66aeacadc9 100644 --- a/src/Avalonia.Base/Input/IPointer.cs +++ b/src/Avalonia.Base/Input/IPointer.cs @@ -1,5 +1,8 @@ +using Avalonia.Metadata; + namespace Avalonia.Input { + [NotClientImplementable] public interface IPointer { int Id { get; } diff --git a/src/Avalonia.Base/Input/IPointerDevice.cs b/src/Avalonia.Base/Input/IPointerDevice.cs index 0096bb77bf..0993835feb 100644 --- a/src/Avalonia.Base/Input/IPointerDevice.cs +++ b/src/Avalonia.Base/Input/IPointerDevice.cs @@ -1,9 +1,11 @@ using System; using Avalonia.VisualTree; using Avalonia.Input.Raw; +using Avalonia.Metadata; namespace Avalonia.Input { + [NotClientImplementable] public interface IPointerDevice : IInputDevice { /// diff --git a/src/Avalonia.Base/Input/Platform/IClipboard.cs b/src/Avalonia.Base/Input/Platform/IClipboard.cs index eb880904eb..bf2a5a8602 100644 --- a/src/Avalonia.Base/Input/Platform/IClipboard.cs +++ b/src/Avalonia.Base/Input/Platform/IClipboard.cs @@ -1,7 +1,9 @@ using System.Threading.Tasks; +using Avalonia.Metadata; namespace Avalonia.Input.Platform { + [NotClientImplementable] public interface IClipboard { Task GetTextAsync(); diff --git a/src/Avalonia.Base/Input/Platform/IPlatformDragSource.cs b/src/Avalonia.Base/Input/Platform/IPlatformDragSource.cs index 30d8ee5337..8d1b93087e 100644 --- a/src/Avalonia.Base/Input/Platform/IPlatformDragSource.cs +++ b/src/Avalonia.Base/Input/Platform/IPlatformDragSource.cs @@ -1,7 +1,9 @@ using System.Threading.Tasks; +using Avalonia.Metadata; namespace Avalonia.Input.Platform { + [Unstable] public interface IPlatformDragSource { Task DoDragDrop(PointerEventArgs triggerEvent, IDataObject data, DragDropEffects allowedEffects); diff --git a/src/Avalonia.Base/Input/Raw/IDragDropDevice.cs b/src/Avalonia.Base/Input/Raw/IDragDropDevice.cs index f7b7914bd1..3bcc9fadd3 100644 --- a/src/Avalonia.Base/Input/Raw/IDragDropDevice.cs +++ b/src/Avalonia.Base/Input/Raw/IDragDropDevice.cs @@ -1,5 +1,8 @@ -namespace Avalonia.Input.Raw +using Avalonia.Metadata; + +namespace Avalonia.Input.Raw { + [NotClientImplementable] public interface IDragDropDevice : IInputDevice { } diff --git a/src/Avalonia.Base/Input/TextInput/ITextInputMethodImpl.cs b/src/Avalonia.Base/Input/TextInput/ITextInputMethodImpl.cs index 4404c903b7..be7ad81f81 100644 --- a/src/Avalonia.Base/Input/TextInput/ITextInputMethodImpl.cs +++ b/src/Avalonia.Base/Input/TextInput/ITextInputMethodImpl.cs @@ -1,5 +1,8 @@ +using Avalonia.Metadata; + namespace Avalonia.Input.TextInput { + [Unstable] public interface ITextInputMethodImpl { void SetClient(ITextInputMethodClient? client); @@ -8,6 +11,7 @@ namespace Avalonia.Input.TextInput void Reset(); } + [NotClientImplementable] public interface ITextInputMethodRoot : IInputRoot { ITextInputMethodImpl? InputMethod { get; } diff --git a/src/Avalonia.Base/Interactivity/IInteractive.cs b/src/Avalonia.Base/Interactivity/IInteractive.cs index 6d7dcd64f4..980bf54f1f 100644 --- a/src/Avalonia.Base/Interactivity/IInteractive.cs +++ b/src/Avalonia.Base/Interactivity/IInteractive.cs @@ -1,4 +1,5 @@ using System; +using Avalonia.Metadata; #nullable enable @@ -7,6 +8,7 @@ namespace Avalonia.Interactivity /// /// Interface for objects that raise routed events. /// + [NotClientImplementable] public interface IInteractive { /// diff --git a/src/Avalonia.Base/Layout/ILayoutManager.cs b/src/Avalonia.Base/Layout/ILayoutManager.cs index 614670a53b..143ce13a1b 100644 --- a/src/Avalonia.Base/Layout/ILayoutManager.cs +++ b/src/Avalonia.Base/Layout/ILayoutManager.cs @@ -1,12 +1,12 @@ using System; - -#nullable enable +using Avalonia.Metadata; namespace Avalonia.Layout { /// /// Manages measuring and arranging of controls. /// + [NotClientImplementable] public interface ILayoutManager : IDisposable { /// diff --git a/src/Avalonia.Base/Layout/ILayoutRoot.cs b/src/Avalonia.Base/Layout/ILayoutRoot.cs index e2f16b338a..df15fc1a1d 100644 --- a/src/Avalonia.Base/Layout/ILayoutRoot.cs +++ b/src/Avalonia.Base/Layout/ILayoutRoot.cs @@ -1,8 +1,11 @@ +using Avalonia.Metadata; + namespace Avalonia.Layout { /// /// Defines the root of a layoutable tree. /// + [NotClientImplementable] public interface ILayoutRoot : ILayoutable { /// diff --git a/src/Avalonia.Base/Layout/ILayoutable.cs b/src/Avalonia.Base/Layout/ILayoutable.cs index 54d3ba6a11..d8b546b04a 100644 --- a/src/Avalonia.Base/Layout/ILayoutable.cs +++ b/src/Avalonia.Base/Layout/ILayoutable.cs @@ -1,12 +1,12 @@ +using Avalonia.Metadata; using Avalonia.VisualTree; -#nullable enable - namespace Avalonia.Layout { /// /// Defines layout-related functionality for a control. /// + [NotClientImplementable] public interface ILayoutable : IVisual { /// diff --git a/src/Avalonia.Base/LogicalTree/ILogical.cs b/src/Avalonia.Base/LogicalTree/ILogical.cs index caff3d8150..9cc2edff86 100644 --- a/src/Avalonia.Base/LogicalTree/ILogical.cs +++ b/src/Avalonia.Base/LogicalTree/ILogical.cs @@ -1,12 +1,14 @@ using System; using Avalonia.Collections; using Avalonia.Controls; +using Avalonia.Metadata; namespace Avalonia.LogicalTree { /// /// Represents a node in the logical tree. /// + [NotClientImplementable] public interface ILogical { /// diff --git a/src/Avalonia.Base/LogicalTree/ILogicalRoot.cs b/src/Avalonia.Base/LogicalTree/ILogicalRoot.cs index 4a61544a6f..ea0f554e96 100644 --- a/src/Avalonia.Base/LogicalTree/ILogicalRoot.cs +++ b/src/Avalonia.Base/LogicalTree/ILogicalRoot.cs @@ -1,8 +1,11 @@ -namespace Avalonia.LogicalTree +using Avalonia.Metadata; + +namespace Avalonia.LogicalTree { /// /// Represents a root of a logical tree. /// + [NotClientImplementable] public interface ILogicalRoot : ILogical { } diff --git a/src/Avalonia.Base/Media/IBrush.cs b/src/Avalonia.Base/Media/IBrush.cs index 830c066182..10700492d1 100644 --- a/src/Avalonia.Base/Media/IBrush.cs +++ b/src/Avalonia.Base/Media/IBrush.cs @@ -1,4 +1,5 @@ using System.ComponentModel; +using Avalonia.Metadata; namespace Avalonia.Media { @@ -6,6 +7,7 @@ namespace Avalonia.Media /// Describes how an area is painted. /// [TypeConverter(typeof(BrushConverter))] + [NotClientImplementable] public interface IBrush { /// diff --git a/src/Avalonia.Base/Media/IConicGradientBrush.cs b/src/Avalonia.Base/Media/IConicGradientBrush.cs index 5368dd1851..6a397b86d4 100644 --- a/src/Avalonia.Base/Media/IConicGradientBrush.cs +++ b/src/Avalonia.Base/Media/IConicGradientBrush.cs @@ -1,8 +1,11 @@ -namespace Avalonia.Media +using Avalonia.Metadata; + +namespace Avalonia.Media { /// /// Paints an area with a conic gradient. /// + [NotClientImplementable] public interface IConicGradientBrush : IGradientBrush { /// diff --git a/src/Avalonia.Base/Media/IDashStyle.cs b/src/Avalonia.Base/Media/IDashStyle.cs index 7835c7a1e9..7208216603 100644 --- a/src/Avalonia.Base/Media/IDashStyle.cs +++ b/src/Avalonia.Base/Media/IDashStyle.cs @@ -1,10 +1,12 @@ using System.Collections.Generic; +using Avalonia.Metadata; namespace Avalonia.Media { /// /// Represents the sequence of dashes and gaps that will be applied by a . /// + [NotClientImplementable] public interface IDashStyle { /// diff --git a/src/Avalonia.Base/Media/IExperimentalAcrylicMaterial.cs b/src/Avalonia.Base/Media/IExperimentalAcrylicMaterial.cs index e71584258a..38048fb255 100644 --- a/src/Avalonia.Base/Media/IExperimentalAcrylicMaterial.cs +++ b/src/Avalonia.Base/Media/IExperimentalAcrylicMaterial.cs @@ -1,8 +1,11 @@ -namespace Avalonia.Media +using Avalonia.Metadata; + +namespace Avalonia.Media { /// /// Experimental Interface for producing Acrylic-like materials. /// + [NotClientImplementable] public interface IExperimentalAcrylicMaterial { /// diff --git a/src/Avalonia.Base/Media/IGradientBrush.cs b/src/Avalonia.Base/Media/IGradientBrush.cs index 18db0af660..9b78a4af78 100644 --- a/src/Avalonia.Base/Media/IGradientBrush.cs +++ b/src/Avalonia.Base/Media/IGradientBrush.cs @@ -1,10 +1,12 @@ using System.Collections.Generic; +using Avalonia.Metadata; namespace Avalonia.Media { /// /// A brush that draws with a gradient. /// + [NotClientImplementable] public interface IGradientBrush : IBrush { /// diff --git a/src/Avalonia.Base/Media/IGradientStop.cs b/src/Avalonia.Base/Media/IGradientStop.cs index 22eb9df60d..64b952f3bc 100644 --- a/src/Avalonia.Base/Media/IGradientStop.cs +++ b/src/Avalonia.Base/Media/IGradientStop.cs @@ -1,8 +1,11 @@ -namespace Avalonia.Media +using Avalonia.Metadata; + +namespace Avalonia.Media { /// /// Describes the location and color of a transition point in a gradient. /// + [NotClientImplementable] public interface IGradientStop { /// diff --git a/src/Avalonia.Base/Media/IImageBrush.cs b/src/Avalonia.Base/Media/IImageBrush.cs index aaa481bd28..732f1957d0 100644 --- a/src/Avalonia.Base/Media/IImageBrush.cs +++ b/src/Avalonia.Base/Media/IImageBrush.cs @@ -1,10 +1,12 @@ using Avalonia.Media.Imaging; +using Avalonia.Metadata; namespace Avalonia.Media { /// /// Paints an area with an . /// + [NotClientImplementable] public interface IImageBrush : ITileBrush { /// @@ -12,4 +14,4 @@ namespace Avalonia.Media /// IBitmap Source { get; } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Base/Media/ILinearGradientBrush.cs b/src/Avalonia.Base/Media/ILinearGradientBrush.cs index 3e2a5a0e22..4f4a55db36 100644 --- a/src/Avalonia.Base/Media/ILinearGradientBrush.cs +++ b/src/Avalonia.Base/Media/ILinearGradientBrush.cs @@ -1,8 +1,11 @@ -namespace Avalonia.Media +using Avalonia.Metadata; + +namespace Avalonia.Media { /// /// A brush that draws with a linear gradient. /// + [NotClientImplementable] public interface ILinearGradientBrush : IGradientBrush { /// @@ -15,4 +18,4 @@ /// RelativePoint EndPoint { get; } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Base/Media/IMutableBrush.cs b/src/Avalonia.Base/Media/IMutableBrush.cs index 415db61d68..fef124ba36 100644 --- a/src/Avalonia.Base/Media/IMutableBrush.cs +++ b/src/Avalonia.Base/Media/IMutableBrush.cs @@ -1,10 +1,12 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Media { /// /// Represents a mutable brush which can return an immutable clone of itself. /// + [NotClientImplementable] public interface IMutableBrush : IBrush, IAffectsRender { /// diff --git a/src/Avalonia.Base/Media/IMutableExperimentalAcrylicMaterial.cs b/src/Avalonia.Base/Media/IMutableExperimentalAcrylicMaterial.cs index fcfe4631a6..f954a8c52a 100644 --- a/src/Avalonia.Base/Media/IMutableExperimentalAcrylicMaterial.cs +++ b/src/Avalonia.Base/Media/IMutableExperimentalAcrylicMaterial.cs @@ -1,8 +1,11 @@ -namespace Avalonia.Media +using Avalonia.Metadata; + +namespace Avalonia.Media { /// /// Represents a mutable brush which can return an immutable clone of itself. /// + [NotClientImplementable] public interface IMutableExperimentalAcrylicMaterial : IExperimentalAcrylicMaterial, IAffectsRender { /// diff --git a/src/Avalonia.Base/Media/IMutableTransform.cs b/src/Avalonia.Base/Media/IMutableTransform.cs index 2033c434c0..22526eed17 100644 --- a/src/Avalonia.Base/Media/IMutableTransform.cs +++ b/src/Avalonia.Base/Media/IMutableTransform.cs @@ -1,4 +1,5 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Media { diff --git a/src/Avalonia.Base/Media/IPen.cs b/src/Avalonia.Base/Media/IPen.cs index 1cad9948b4..eca1cb507a 100644 --- a/src/Avalonia.Base/Media/IPen.cs +++ b/src/Avalonia.Base/Media/IPen.cs @@ -1,8 +1,11 @@ -namespace Avalonia.Media +using Avalonia.Metadata; + +namespace Avalonia.Media { /// /// Describes how a stroke is drawn. /// + [NotClientImplementable] public interface IPen { /// diff --git a/src/Avalonia.Base/Media/IRadialGradientBrush.cs b/src/Avalonia.Base/Media/IRadialGradientBrush.cs index cadf53cc18..0f025e98bc 100644 --- a/src/Avalonia.Base/Media/IRadialGradientBrush.cs +++ b/src/Avalonia.Base/Media/IRadialGradientBrush.cs @@ -1,8 +1,11 @@ -namespace Avalonia.Media +using Avalonia.Metadata; + +namespace Avalonia.Media { /// /// Paints an area with a radial gradient. /// + [NotClientImplementable] public interface IRadialGradientBrush : IGradientBrush { /// @@ -21,4 +24,4 @@ /// double Radius { get; } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Base/Media/ISolidColorBrush.cs b/src/Avalonia.Base/Media/ISolidColorBrush.cs index d0ab00599b..29e11210f1 100644 --- a/src/Avalonia.Base/Media/ISolidColorBrush.cs +++ b/src/Avalonia.Base/Media/ISolidColorBrush.cs @@ -1,8 +1,11 @@ +using Avalonia.Metadata; + namespace Avalonia.Media { /// /// Fills an area with a solid color. /// + [NotClientImplementable] public interface ISolidColorBrush : IBrush { /// @@ -10,4 +13,4 @@ namespace Avalonia.Media /// Color Color { get; } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Base/Media/ITileBrush.cs b/src/Avalonia.Base/Media/ITileBrush.cs index 991857eec9..cb5a591003 100644 --- a/src/Avalonia.Base/Media/ITileBrush.cs +++ b/src/Avalonia.Base/Media/ITileBrush.cs @@ -1,10 +1,12 @@ using Avalonia.Media.Imaging; +using Avalonia.Metadata; namespace Avalonia.Media -{ +{ /// /// A brush which displays a repeating image. /// + [NotClientImplementable] public interface ITileBrush : IBrush { /// diff --git a/src/Avalonia.Base/Media/IVisualBrush.cs b/src/Avalonia.Base/Media/IVisualBrush.cs index e74892b218..b8900bbd73 100644 --- a/src/Avalonia.Base/Media/IVisualBrush.cs +++ b/src/Avalonia.Base/Media/IVisualBrush.cs @@ -1,10 +1,12 @@ -using Avalonia.VisualTree; +using Avalonia.Metadata; +using Avalonia.VisualTree; namespace Avalonia.Media { /// /// Paints an area with an . /// + [NotClientImplementable] public interface IVisualBrush : ITileBrush { /// @@ -12,4 +14,4 @@ namespace Avalonia.Media /// IVisual Visual { get; } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Base/Media/Imaging/IBitmap.cs b/src/Avalonia.Base/Media/Imaging/IBitmap.cs index 134bebc002..bd04d5ce86 100644 --- a/src/Avalonia.Base/Media/Imaging/IBitmap.cs +++ b/src/Avalonia.Base/Media/Imaging/IBitmap.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using Avalonia.Metadata; using Avalonia.Platform; using Avalonia.Utilities; @@ -8,6 +9,7 @@ namespace Avalonia.Media.Imaging /// /// Represents a bitmap image. /// + [NotClientImplementable] public interface IBitmap : IImage, IDisposable { /// diff --git a/src/Avalonia.Base/Media/TextFormatting/ITextSource.cs b/src/Avalonia.Base/Media/TextFormatting/ITextSource.cs index 32012ab8e9..85641977e6 100644 --- a/src/Avalonia.Base/Media/TextFormatting/ITextSource.cs +++ b/src/Avalonia.Base/Media/TextFormatting/ITextSource.cs @@ -1,8 +1,11 @@ -namespace Avalonia.Media.TextFormatting +using Avalonia.Metadata; + +namespace Avalonia.Media.TextFormatting { /// /// Produces objects that are used by the . /// + [NotClientImplementable] public interface ITextSource { /// diff --git a/src/Avalonia.Base/Metadata/NotClientImplementableAttribute.cs b/src/Avalonia.Base/Metadata/NotClientImplementableAttribute.cs new file mode 100644 index 0000000000..348c983c03 --- /dev/null +++ b/src/Avalonia.Base/Metadata/NotClientImplementableAttribute.cs @@ -0,0 +1,17 @@ +using System; + +namespace Avalonia.Metadata +{ + /// + /// This interface is not intended to be implemented outside of the core Avalonia framework as + /// its API may change without warning. + /// + /// + /// This interface is stable for consumption by a client, but should not be implemented as members + /// may be added to its API. + /// + [AttributeUsage(AttributeTargets.Interface)] + public class NotClientImplementableAttribute : Attribute + { + } +} diff --git a/src/Avalonia.Base/Metadata/UnstableAttribute.cs b/src/Avalonia.Base/Metadata/UnstableAttribute.cs new file mode 100644 index 0000000000..3b6fa5168a --- /dev/null +++ b/src/Avalonia.Base/Metadata/UnstableAttribute.cs @@ -0,0 +1,12 @@ +using System; + +namespace Avalonia.Metadata +{ + /// + /// This API is unstable and is not covered by API compatibility guarantees between minor and + /// patch releases. + /// + public class UnstableAttribute : Attribute + { + } +} diff --git a/src/Avalonia.Base/Platform/IAssetLoader.cs b/src/Avalonia.Base/Platform/IAssetLoader.cs index e3899784ad..b65d61803f 100644 --- a/src/Avalonia.Base/Platform/IAssetLoader.cs +++ b/src/Avalonia.Base/Platform/IAssetLoader.cs @@ -2,12 +2,14 @@ using System; using System.Collections.Generic; using System.IO; using System.Reflection; +using Avalonia.Metadata; namespace Avalonia.Platform { /// /// Loads assets compiled into the application binary. /// + [Unstable] public interface IAssetLoader { /// diff --git a/src/Avalonia.Base/Platform/IBitmapImpl.cs b/src/Avalonia.Base/Platform/IBitmapImpl.cs index 1e68bc477d..8f11f68e7c 100644 --- a/src/Avalonia.Base/Platform/IBitmapImpl.cs +++ b/src/Avalonia.Base/Platform/IBitmapImpl.cs @@ -1,11 +1,13 @@ using System; using System.IO; +using Avalonia.Metadata; namespace Avalonia.Platform { /// /// Defines the platform-specific interface for a . /// + [Unstable] public interface IBitmapImpl : IDisposable { /// diff --git a/src/Avalonia.Base/Platform/ICursorImpl.cs b/src/Avalonia.Base/Platform/ICursorImpl.cs index 14235869f7..74e0ba2e5c 100644 --- a/src/Avalonia.Base/Platform/ICursorImpl.cs +++ b/src/Avalonia.Base/Platform/ICursorImpl.cs @@ -1,5 +1,6 @@ using System; using Avalonia.Input; +using Avalonia.Metadata; #nullable enable @@ -8,6 +9,7 @@ namespace Avalonia.Platform /// /// Represents a platform implementation of a . /// + [Unstable] public interface ICursorImpl : IDisposable { } diff --git a/src/Avalonia.Base/Platform/IDrawingContextImpl.cs b/src/Avalonia.Base/Platform/IDrawingContextImpl.cs index 4e6612e908..d84a509234 100644 --- a/src/Avalonia.Base/Platform/IDrawingContextImpl.cs +++ b/src/Avalonia.Base/Platform/IDrawingContextImpl.cs @@ -3,12 +3,14 @@ using Avalonia.Media; using Avalonia.Rendering.SceneGraph; using Avalonia.Utilities; using Avalonia.Media.Imaging; +using Avalonia.Metadata; namespace Avalonia.Platform { /// /// Defines the interface through which drawing occurs. /// + [Unstable] public interface IDrawingContextImpl : IDisposable { /// diff --git a/src/Avalonia.Base/Platform/IFontManagerImpl.cs b/src/Avalonia.Base/Platform/IFontManagerImpl.cs index 0110287afd..932249bd52 100644 --- a/src/Avalonia.Base/Platform/IFontManagerImpl.cs +++ b/src/Avalonia.Base/Platform/IFontManagerImpl.cs @@ -1,9 +1,11 @@ using System.Collections.Generic; using System.Globalization; using Avalonia.Media; +using Avalonia.Metadata; namespace Avalonia.Platform { + [Unstable] public interface IFontManagerImpl { /// diff --git a/src/Avalonia.Base/Platform/IGeometryImpl.cs b/src/Avalonia.Base/Platform/IGeometryImpl.cs index ed6de1b5c7..c80f8923ef 100644 --- a/src/Avalonia.Base/Platform/IGeometryImpl.cs +++ b/src/Avalonia.Base/Platform/IGeometryImpl.cs @@ -1,10 +1,12 @@ using Avalonia.Media; +using Avalonia.Metadata; namespace Avalonia.Platform { /// /// Defines the platform-specific interface for a . /// + [Unstable] public interface IGeometryImpl { /// diff --git a/src/Avalonia.Base/Platform/IGlyphRunImpl.cs b/src/Avalonia.Base/Platform/IGlyphRunImpl.cs index 08786d9689..7801bdd50f 100644 --- a/src/Avalonia.Base/Platform/IGlyphRunImpl.cs +++ b/src/Avalonia.Base/Platform/IGlyphRunImpl.cs @@ -1,9 +1,11 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Platform { /// /// Actual implementation of a glyph run that stores platform dependent resources. /// + [Unstable] public interface IGlyphRunImpl : IDisposable { } } diff --git a/src/Avalonia.Base/Platform/IGlyphTypefaceImpl.cs b/src/Avalonia.Base/Platform/IGlyphTypefaceImpl.cs index 6afd79d29c..415f34fb29 100644 --- a/src/Avalonia.Base/Platform/IGlyphTypefaceImpl.cs +++ b/src/Avalonia.Base/Platform/IGlyphTypefaceImpl.cs @@ -1,7 +1,9 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Platform { + [Unstable] public interface IGlyphTypefaceImpl : IDisposable { /// diff --git a/src/Avalonia.Base/Platform/IMacOSTopLevelPlatformHandle.cs b/src/Avalonia.Base/Platform/IMacOSTopLevelPlatformHandle.cs index e399976bbe..b087724079 100644 --- a/src/Avalonia.Base/Platform/IMacOSTopLevelPlatformHandle.cs +++ b/src/Avalonia.Base/Platform/IMacOSTopLevelPlatformHandle.cs @@ -1,7 +1,9 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Platform { + [Unstable] public interface IMacOSTopLevelPlatformHandle { IntPtr NSView { get; } diff --git a/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs b/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs index c46efd46c3..0eeefddf0b 100644 --- a/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs +++ b/src/Avalonia.Base/Platform/IPlatformRenderInterface.cs @@ -3,12 +3,14 @@ using System.Collections.Generic; using System.IO; using Avalonia.Media; using Avalonia.Media.Imaging; +using Avalonia.Metadata; namespace Avalonia.Platform { /// /// Defines the main platform-specific interface for the rendering subsystem. /// + [Unstable] public interface IPlatformRenderInterface { /// diff --git a/src/Avalonia.Base/Platform/IPlatformSettings.cs b/src/Avalonia.Base/Platform/IPlatformSettings.cs index e4b28e6575..78d1817312 100644 --- a/src/Avalonia.Base/Platform/IPlatformSettings.cs +++ b/src/Avalonia.Base/Platform/IPlatformSettings.cs @@ -1,7 +1,9 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Platform { + [Unstable] public interface IPlatformSettings { Size DoubleClickSize { get; } diff --git a/src/Avalonia.Base/Platform/IPlatformThreadingInterface.cs b/src/Avalonia.Base/Platform/IPlatformThreadingInterface.cs index 2137f965cc..bf18a7da5b 100644 --- a/src/Avalonia.Base/Platform/IPlatformThreadingInterface.cs +++ b/src/Avalonia.Base/Platform/IPlatformThreadingInterface.cs @@ -1,5 +1,6 @@ using System; using System.Threading; +using Avalonia.Metadata; using Avalonia.Threading; namespace Avalonia.Platform @@ -7,6 +8,7 @@ namespace Avalonia.Platform /// /// Provides platform-specific services relating to threading. /// + [Unstable] public interface IPlatformThreadingInterface { void RunLoop(CancellationToken cancellationToken); diff --git a/src/Avalonia.Base/Platform/IRenderTargetBitmapImpl.cs b/src/Avalonia.Base/Platform/IRenderTargetBitmapImpl.cs index 9add07afe3..d33c503650 100644 --- a/src/Avalonia.Base/Platform/IRenderTargetBitmapImpl.cs +++ b/src/Avalonia.Base/Platform/IRenderTargetBitmapImpl.cs @@ -1,3 +1,4 @@ +using Avalonia.Metadata; namespace Avalonia.Platform { @@ -5,6 +6,7 @@ namespace Avalonia.Platform /// Defines the platform-specific interface for a /// . /// + [Unstable] public interface IRenderTargetBitmapImpl : IBitmapImpl, IRenderTarget { } diff --git a/src/Avalonia.Base/Platform/IRuntimePlatform.cs b/src/Avalonia.Base/Platform/IRuntimePlatform.cs index 850757a1ee..8ab04f5995 100644 --- a/src/Avalonia.Base/Platform/IRuntimePlatform.cs +++ b/src/Avalonia.Base/Platform/IRuntimePlatform.cs @@ -1,7 +1,9 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Platform { + [Unstable] public interface IRuntimePlatform { IDisposable StartSystemTimer(TimeSpan interval, Action tick); @@ -9,6 +11,7 @@ namespace Avalonia.Platform IUnmanagedBlob AllocBlob(int size); } + [Unstable] public interface IUnmanagedBlob : IDisposable { IntPtr Address { get; } @@ -17,6 +20,7 @@ namespace Avalonia.Platform } + [Unstable] public struct RuntimePlatformInfo { public OperatingSystemType OperatingSystem { get; set; } @@ -29,6 +33,7 @@ namespace Avalonia.Platform public bool IsUnix { get; set; } } + [Unstable] public enum OperatingSystemType { Unknown, diff --git a/src/Avalonia.Base/Platform/IStreamGeometryContextImpl.cs b/src/Avalonia.Base/Platform/IStreamGeometryContextImpl.cs index 4587979308..3d5ee36608 100644 --- a/src/Avalonia.Base/Platform/IStreamGeometryContextImpl.cs +++ b/src/Avalonia.Base/Platform/IStreamGeometryContextImpl.cs @@ -1,8 +1,11 @@ +using Avalonia.Metadata; + namespace Avalonia.Platform { /// /// Describes a geometry using drawing commands. /// + [Unstable] public interface IStreamGeometryContextImpl : IGeometryContext { } diff --git a/src/Avalonia.Base/Platform/IStreamGeometryImpl.cs b/src/Avalonia.Base/Platform/IStreamGeometryImpl.cs index 5b070fde02..bd4411f3a4 100644 --- a/src/Avalonia.Base/Platform/IStreamGeometryImpl.cs +++ b/src/Avalonia.Base/Platform/IStreamGeometryImpl.cs @@ -1,8 +1,11 @@ +using Avalonia.Metadata; + namespace Avalonia.Platform { /// /// Defines the platform-specific interface for a . /// + [Unstable] public interface IStreamGeometryImpl : IGeometryImpl { /// diff --git a/src/Avalonia.Base/Platform/ITextShaperImpl.cs b/src/Avalonia.Base/Platform/ITextShaperImpl.cs index 11be9e3f09..10e58b7d0b 100644 --- a/src/Avalonia.Base/Platform/ITextShaperImpl.cs +++ b/src/Avalonia.Base/Platform/ITextShaperImpl.cs @@ -1,4 +1,5 @@ using Avalonia.Media.TextFormatting; +using Avalonia.Metadata; using Avalonia.Utilities; namespace Avalonia.Platform @@ -6,6 +7,7 @@ namespace Avalonia.Platform /// /// An abstraction that is used produce shaped text. /// + [Unstable] public interface ITextShaperImpl { /// diff --git a/src/Avalonia.Base/Platform/ITransformedGeometryImpl.cs b/src/Avalonia.Base/Platform/ITransformedGeometryImpl.cs index 1ed025b571..2754414cd1 100644 --- a/src/Avalonia.Base/Platform/ITransformedGeometryImpl.cs +++ b/src/Avalonia.Base/Platform/ITransformedGeometryImpl.cs @@ -1,4 +1,6 @@ -namespace Avalonia.Platform +using Avalonia.Metadata; + +namespace Avalonia.Platform { /// /// Represents a geometry with a transform applied. @@ -7,6 +9,7 @@ /// An transforms a geometry without transforming its /// stroke thickness. /// + [Unstable] public interface ITransformedGeometryImpl : IGeometryImpl { /// diff --git a/src/Avalonia.Base/Platform/IWriteableBitmapImpl.cs b/src/Avalonia.Base/Platform/IWriteableBitmapImpl.cs index c4e2e4915f..fa1e1862b7 100644 --- a/src/Avalonia.Base/Platform/IWriteableBitmapImpl.cs +++ b/src/Avalonia.Base/Platform/IWriteableBitmapImpl.cs @@ -1,8 +1,11 @@ -namespace Avalonia.Platform +using Avalonia.Metadata; + +namespace Avalonia.Platform { /// /// Defines the platform-specific interface for a . /// + [Unstable] public interface IWriteableBitmapImpl : IBitmapImpl { ILockedFramebuffer Lock(); diff --git a/src/Avalonia.Base/Platform/Interop/IDynamicLibraryLoader.cs b/src/Avalonia.Base/Platform/Interop/IDynamicLibraryLoader.cs index 8124ce6bc4..9389ebc703 100644 --- a/src/Avalonia.Base/Platform/Interop/IDynamicLibraryLoader.cs +++ b/src/Avalonia.Base/Platform/Interop/IDynamicLibraryLoader.cs @@ -1,7 +1,9 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Platform.Interop { + [Unstable] public interface IDynamicLibraryLoader { IntPtr LoadLibrary(string dll); diff --git a/src/Avalonia.Base/Rendering/IDeferredRendererLock.cs b/src/Avalonia.Base/Rendering/IDeferredRendererLock.cs index eab3dca58e..1c6bd69158 100644 --- a/src/Avalonia.Base/Rendering/IDeferredRendererLock.cs +++ b/src/Avalonia.Base/Rendering/IDeferredRendererLock.cs @@ -1,7 +1,9 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Rendering { + [Unstable] public interface IDeferredRendererLock { IDisposable? TryLock(); diff --git a/src/Avalonia.Base/Rendering/IRenderLoop.cs b/src/Avalonia.Base/Rendering/IRenderLoop.cs index dd7442e7f8..9838967261 100644 --- a/src/Avalonia.Base/Rendering/IRenderLoop.cs +++ b/src/Avalonia.Base/Rendering/IRenderLoop.cs @@ -1,4 +1,6 @@ -namespace Avalonia.Rendering +using Avalonia.Metadata; + +namespace Avalonia.Rendering { /// /// The application render loop. @@ -7,6 +9,7 @@ /// The render loop is responsible for advancing the animation timer and updating the scene /// graph for visible windows. /// + [NotClientImplementable] public interface IRenderLoop { /// diff --git a/src/Avalonia.Base/Rendering/IRenderRoot.cs b/src/Avalonia.Base/Rendering/IRenderRoot.cs index 54e58bf39c..1aa44158b2 100644 --- a/src/Avalonia.Base/Rendering/IRenderRoot.cs +++ b/src/Avalonia.Base/Rendering/IRenderRoot.cs @@ -1,3 +1,4 @@ +using Avalonia.Metadata; using Avalonia.Platform; using Avalonia.VisualTree; @@ -6,6 +7,7 @@ namespace Avalonia.Rendering /// /// Represents the root of a renderable tree. /// + [NotClientImplementable] public interface IRenderRoot : IVisual { /// diff --git a/src/Avalonia.Base/Rendering/IRenderTimer.cs b/src/Avalonia.Base/Rendering/IRenderTimer.cs index d333e928a0..ee74c345be 100644 --- a/src/Avalonia.Base/Rendering/IRenderTimer.cs +++ b/src/Avalonia.Base/Rendering/IRenderTimer.cs @@ -1,11 +1,13 @@ using System; using System.Threading.Tasks; +using Avalonia.Metadata; namespace Avalonia.Rendering { /// /// Defines the interface implemented by an application render timer. /// + [NotClientImplementable] public interface IRenderTimer { /// diff --git a/src/Avalonia.Base/Rendering/IVisualBrushInitialize.cs b/src/Avalonia.Base/Rendering/IVisualBrushInitialize.cs index 00449c5344..b5ab8ed0bd 100644 --- a/src/Avalonia.Base/Rendering/IVisualBrushInitialize.cs +++ b/src/Avalonia.Base/Rendering/IVisualBrushInitialize.cs @@ -1,4 +1,5 @@ using Avalonia.Media; +using Avalonia.Metadata; namespace Avalonia.Rendering { @@ -6,6 +7,7 @@ namespace Avalonia.Rendering /// Internal interface for initializing controls that are to be used as the visual in a /// . /// + [Unstable] public interface IVisualBrushInitialize { /// diff --git a/src/Avalonia.Base/Rendering/IVisualBrushRenderer.cs b/src/Avalonia.Base/Rendering/IVisualBrushRenderer.cs index 1cd6515635..f5312ad39b 100644 --- a/src/Avalonia.Base/Rendering/IVisualBrushRenderer.cs +++ b/src/Avalonia.Base/Rendering/IVisualBrushRenderer.cs @@ -1,4 +1,5 @@ using Avalonia.Media; +using Avalonia.Metadata; using Avalonia.Platform; namespace Avalonia.Rendering @@ -6,6 +7,7 @@ namespace Avalonia.Rendering /// /// Defines a renderer used to render a visual brush to a bitmap. /// + [Unstable] public interface IVisualBrushRenderer { /// diff --git a/src/Avalonia.Base/Styling/Activators/IStyleActivator.cs b/src/Avalonia.Base/Styling/Activators/IStyleActivator.cs index 479100ed8a..ac7b8b3ef1 100644 --- a/src/Avalonia.Base/Styling/Activators/IStyleActivator.cs +++ b/src/Avalonia.Base/Styling/Activators/IStyleActivator.cs @@ -1,6 +1,5 @@ -#nullable enable - -using System; +using System; +using Avalonia.Metadata; namespace Avalonia.Styling.Activators { @@ -16,6 +15,7 @@ namespace Avalonia.Styling.Activators /// - The subscription can have a tag associated with it, allowing a subscriber to index /// into a list of subscriptions without having to allocate additional objects. /// + [Unstable] public interface IStyleActivator : IDisposable { /// diff --git a/src/Avalonia.Base/Styling/Activators/IStyleActivatorSink.cs b/src/Avalonia.Base/Styling/Activators/IStyleActivatorSink.cs index a1a6ef5c28..fbb18dc304 100644 --- a/src/Avalonia.Base/Styling/Activators/IStyleActivatorSink.cs +++ b/src/Avalonia.Base/Styling/Activators/IStyleActivatorSink.cs @@ -1,10 +1,11 @@ -#nullable enable +using Avalonia.Metadata; namespace Avalonia.Styling.Activators { /// /// Receives notifications from an . /// + [Unstable] public interface IStyleActivatorSink { /// diff --git a/src/Avalonia.Base/Styling/IGlobalStyles.cs b/src/Avalonia.Base/Styling/IGlobalStyles.cs index ab24e3138c..f9667a8895 100644 --- a/src/Avalonia.Base/Styling/IGlobalStyles.cs +++ b/src/Avalonia.Base/Styling/IGlobalStyles.cs @@ -1,13 +1,13 @@ using System; using System.Collections.Generic; - -#nullable enable +using Avalonia.Metadata; namespace Avalonia.Styling { /// /// Defines the style host that provides styles global to the application. /// + [NotClientImplementable] public interface IGlobalStyles : IStyleHost { /// diff --git a/src/Avalonia.Base/Styling/ISetter.cs b/src/Avalonia.Base/Styling/ISetter.cs index d588817be8..71ae5d84c0 100644 --- a/src/Avalonia.Base/Styling/ISetter.cs +++ b/src/Avalonia.Base/Styling/ISetter.cs @@ -1,12 +1,12 @@ using System; - -#nullable enable +using Avalonia.Metadata; namespace Avalonia.Styling { /// /// Represents a setter for a . /// + [NotClientImplementable] public interface ISetter { /// diff --git a/src/Avalonia.Base/Styling/ISetterInstance.cs b/src/Avalonia.Base/Styling/ISetterInstance.cs index a299a87b64..e0d3137619 100644 --- a/src/Avalonia.Base/Styling/ISetterInstance.cs +++ b/src/Avalonia.Base/Styling/ISetterInstance.cs @@ -1,12 +1,12 @@ -#nullable enable - -using System; +using System; +using Avalonia.Metadata; namespace Avalonia.Styling { /// /// Represents a setter that has been instanced on a control. /// + [Unstable] public interface ISetterInstance : IDisposable { /// diff --git a/src/Avalonia.Base/Styling/IStyle.cs b/src/Avalonia.Base/Styling/IStyle.cs index 78fbe0f2b5..fe6e8319ad 100644 --- a/src/Avalonia.Base/Styling/IStyle.cs +++ b/src/Avalonia.Base/Styling/IStyle.cs @@ -1,13 +1,12 @@ using System.Collections.Generic; -using Avalonia.Controls; - -#nullable enable +using Avalonia.Metadata; namespace Avalonia.Styling { /// /// Defines the interface for styles. /// + [NotClientImplementable] public interface IStyle { /// diff --git a/src/Avalonia.Base/Styling/IStyleHost.cs b/src/Avalonia.Base/Styling/IStyleHost.cs index 360b40d9a1..2ae488825c 100644 --- a/src/Avalonia.Base/Styling/IStyleHost.cs +++ b/src/Avalonia.Base/Styling/IStyleHost.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; +using Avalonia.Metadata; #nullable enable @@ -8,6 +9,7 @@ namespace Avalonia.Styling /// /// Defines an element that has a collection. /// + [NotClientImplementable] public interface IStyleHost { /// diff --git a/src/Avalonia.Base/Styling/IStyleInstance.cs b/src/Avalonia.Base/Styling/IStyleInstance.cs index 8ddb989bc0..5b5b99097c 100644 --- a/src/Avalonia.Base/Styling/IStyleInstance.cs +++ b/src/Avalonia.Base/Styling/IStyleInstance.cs @@ -1,12 +1,12 @@ using System; - -#nullable enable +using Avalonia.Metadata; namespace Avalonia.Styling { /// /// Represents a style that has been instanced on a control. /// + [Unstable] public interface IStyleInstance : IDisposable { /// diff --git a/src/Avalonia.Base/Styling/IStyleable.cs b/src/Avalonia.Base/Styling/IStyleable.cs index a3df779057..5bc972e7ab 100644 --- a/src/Avalonia.Base/Styling/IStyleable.cs +++ b/src/Avalonia.Base/Styling/IStyleable.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using Avalonia.Collections; +using Avalonia.Metadata; #nullable enable @@ -9,6 +10,7 @@ namespace Avalonia.Styling /// /// Interface for styleable elements. /// + [NotClientImplementable] public interface IStyleable : IAvaloniaObject, INamed { /// diff --git a/src/Avalonia.Base/Styling/ITemplatedControl.cs b/src/Avalonia.Base/Styling/ITemplatedControl.cs index 5485babb62..68989c8a5e 100644 --- a/src/Avalonia.Base/Styling/ITemplatedControl.cs +++ b/src/Avalonia.Base/Styling/ITemplatedControl.cs @@ -1,6 +1,8 @@ +using Avalonia.Metadata; namespace Avalonia.Styling { + [NotClientImplementable] public interface ITemplatedControl : IAvaloniaObject { } diff --git a/src/Avalonia.Base/VisualTree/IVisual.cs b/src/Avalonia.Base/VisualTree/IVisual.cs index b1251618c4..3b053fab38 100644 --- a/src/Avalonia.Base/VisualTree/IVisual.cs +++ b/src/Avalonia.Base/VisualTree/IVisual.cs @@ -1,10 +1,9 @@ using System; using Avalonia.Collections; using Avalonia.Media; +using Avalonia.Metadata; using Avalonia.Rendering; -#nullable enable - namespace Avalonia.VisualTree { /// @@ -18,6 +17,7 @@ namespace Avalonia.VisualTree /// implemented by . It should not be necessary to implement it /// anywhere else. /// + [NotClientImplementable] public interface IVisual { /// diff --git a/src/Avalonia.Controls/ApplicationLifetimes/IApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/IApplicationLifetime.cs index 9860d0cb38..b38a539d4a 100644 --- a/src/Avalonia.Controls/ApplicationLifetimes/IApplicationLifetime.cs +++ b/src/Avalonia.Controls/ApplicationLifetimes/IApplicationLifetime.cs @@ -1,5 +1,8 @@ +using Avalonia.Metadata; + namespace Avalonia.Controls.ApplicationLifetimes { + [NotClientImplementable] public interface IApplicationLifetime { diff --git a/src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs index 2bd5c1238d..4b88f6b537 100644 --- a/src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs +++ b/src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs @@ -1,12 +1,14 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using Avalonia.Metadata; namespace Avalonia.Controls.ApplicationLifetimes { /// /// Controls application lifetime in classic desktop style /// + [NotClientImplementable] public interface IClassicDesktopStyleApplicationLifetime : IControlledApplicationLifetime { /// diff --git a/src/Avalonia.Controls/ApplicationLifetimes/IControlledApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/IControlledApplicationLifetime.cs index 3f61aeb536..d7eda790df 100644 --- a/src/Avalonia.Controls/ApplicationLifetimes/IControlledApplicationLifetime.cs +++ b/src/Avalonia.Controls/ApplicationLifetimes/IControlledApplicationLifetime.cs @@ -1,7 +1,9 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Controls.ApplicationLifetimes { + [NotClientImplementable] public interface IControlledApplicationLifetime : IApplicationLifetime { /// diff --git a/src/Avalonia.Controls/ApplicationLifetimes/ISingleViewApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/ISingleViewApplicationLifetime.cs index e25815602e..480c65e5ad 100644 --- a/src/Avalonia.Controls/ApplicationLifetimes/ISingleViewApplicationLifetime.cs +++ b/src/Avalonia.Controls/ApplicationLifetimes/ISingleViewApplicationLifetime.cs @@ -1,5 +1,8 @@ +using Avalonia.Metadata; + namespace Avalonia.Controls.ApplicationLifetimes { + [NotClientImplementable] public interface ISingleViewApplicationLifetime : IApplicationLifetime { Control? MainView { get; set; } diff --git a/src/Avalonia.Controls/Diagnostics/IPopupHostProvider.cs b/src/Avalonia.Controls/Diagnostics/IPopupHostProvider.cs index 45cd1d727e..64978248e5 100644 --- a/src/Avalonia.Controls/Diagnostics/IPopupHostProvider.cs +++ b/src/Avalonia.Controls/Diagnostics/IPopupHostProvider.cs @@ -1,11 +1,13 @@ using System; using Avalonia.Controls.Primitives; +using Avalonia.Metadata; namespace Avalonia.Controls.Diagnostics { /// /// Diagnostics interface to retrieve an associated . /// + [NotClientImplementable] public interface IPopupHostProvider { /// diff --git a/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs b/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs index 08d559a5c1..1c7bdb9b37 100644 --- a/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs +++ b/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs @@ -2,11 +2,13 @@ using System.Collections.Generic; using Avalonia.Input; using Avalonia.Input.Raw; +using Avalonia.Metadata; using Avalonia.Platform; using Avalonia.Rendering; namespace Avalonia.Controls.Embedding.Offscreen { + [Unstable] public abstract class OffscreenTopLevelImplBase : ITopLevelImpl { private double _scaling = 1; diff --git a/src/Avalonia.Controls/IContentControl.cs b/src/Avalonia.Controls/IContentControl.cs index d28b0afb25..b4d8d0f574 100644 --- a/src/Avalonia.Controls/IContentControl.cs +++ b/src/Avalonia.Controls/IContentControl.cs @@ -1,5 +1,6 @@ using Avalonia.Controls.Templates; using Avalonia.Layout; +using Avalonia.Metadata; namespace Avalonia.Controls { @@ -7,6 +8,7 @@ namespace Avalonia.Controls /// Defines a control that displays according to a /// . /// + [NotClientImplementable] public interface IContentControl : IControl { /// diff --git a/src/Avalonia.Controls/IControl.cs b/src/Avalonia.Controls/IControl.cs index b501bc15a7..3395fc1059 100644 --- a/src/Avalonia.Controls/IControl.cs +++ b/src/Avalonia.Controls/IControl.cs @@ -1,6 +1,7 @@ using Avalonia.Controls.Templates; using Avalonia.Input; using Avalonia.Layout; +using Avalonia.Metadata; using Avalonia.VisualTree; namespace Avalonia.Controls @@ -8,6 +9,7 @@ namespace Avalonia.Controls /// /// Interface for Avalonia controls. /// + [NotClientImplementable] public interface IControl : IVisual, IDataTemplateHost, ILayoutable, diff --git a/src/Avalonia.Controls/IGlobalDataTemplates.cs b/src/Avalonia.Controls/IGlobalDataTemplates.cs index 92dcd2c189..f4499ddb5e 100644 --- a/src/Avalonia.Controls/IGlobalDataTemplates.cs +++ b/src/Avalonia.Controls/IGlobalDataTemplates.cs @@ -1,10 +1,12 @@ using Avalonia.Controls.Templates; +using Avalonia.Metadata; namespace Avalonia.Controls { /// /// Defines the application-global data templates. /// + [NotClientImplementable] public interface IGlobalDataTemplates : IDataTemplateHost { } diff --git a/src/Avalonia.Controls/IMenu.cs b/src/Avalonia.Controls/IMenu.cs index 0722a22f08..d90c5ea7a8 100644 --- a/src/Avalonia.Controls/IMenu.cs +++ b/src/Avalonia.Controls/IMenu.cs @@ -1,10 +1,12 @@ using Avalonia.Controls.Platform; +using Avalonia.Metadata; namespace Avalonia.Controls { /// /// Represents a or . /// + [NotClientImplementable] public interface IMenu : IMenuElement { /// diff --git a/src/Avalonia.Controls/IMenuElement.cs b/src/Avalonia.Controls/IMenuElement.cs index a3200d2b1b..c13c20b639 100644 --- a/src/Avalonia.Controls/IMenuElement.cs +++ b/src/Avalonia.Controls/IMenuElement.cs @@ -1,11 +1,13 @@ using System.Collections.Generic; using Avalonia.Input; +using Avalonia.Metadata; namespace Avalonia.Controls { /// /// Represents an or . /// + [NotClientImplementable] public interface IMenuElement : IControl { /// diff --git a/src/Avalonia.Controls/IMenuItem.cs b/src/Avalonia.Controls/IMenuItem.cs index 35e36eb0f4..9d7ef3c18d 100644 --- a/src/Avalonia.Controls/IMenuItem.cs +++ b/src/Avalonia.Controls/IMenuItem.cs @@ -1,8 +1,11 @@ -namespace Avalonia.Controls +using Avalonia.Metadata; + +namespace Avalonia.Controls { /// /// Represents a . /// + [NotClientImplementable] public interface IMenuItem : IMenuElement { /// diff --git a/src/Avalonia.Controls/INativeMenuExporterEventsImplBridge.cs b/src/Avalonia.Controls/INativeMenuExporterEventsImplBridge.cs index f492e6ca0f..29963e4821 100644 --- a/src/Avalonia.Controls/INativeMenuExporterEventsImplBridge.cs +++ b/src/Avalonia.Controls/INativeMenuExporterEventsImplBridge.cs @@ -1,5 +1,8 @@ +using Avalonia.Metadata; + namespace Avalonia.Controls { + [Unstable] public interface INativeMenuExporterEventsImplBridge { void RaiseNeedsUpdate (); diff --git a/src/Avalonia.Controls/INativeMenuItemExporterEventsImplBridge.cs b/src/Avalonia.Controls/INativeMenuItemExporterEventsImplBridge.cs index 6cb68d8ddd..a6c7489971 100644 --- a/src/Avalonia.Controls/INativeMenuItemExporterEventsImplBridge.cs +++ b/src/Avalonia.Controls/INativeMenuItemExporterEventsImplBridge.cs @@ -1,5 +1,8 @@ +using Avalonia.Metadata; + namespace Avalonia.Controls { + [Unstable] public interface INativeMenuItemExporterEventsImplBridge { void RaiseClicked (); diff --git a/src/Avalonia.Controls/IPanel.cs b/src/Avalonia.Controls/IPanel.cs index 7b9e2c2074..8f2564ec74 100644 --- a/src/Avalonia.Controls/IPanel.cs +++ b/src/Avalonia.Controls/IPanel.cs @@ -1,8 +1,11 @@ +using Avalonia.Metadata; + namespace Avalonia.Controls { /// /// Interface for controls that can contain multiple children. /// + [NotClientImplementable] public interface IPanel : IControl { /// @@ -10,4 +13,4 @@ namespace Avalonia.Controls /// Controls Children { get; } } -} \ No newline at end of file +} diff --git a/src/Avalonia.Controls/IScrollable.cs b/src/Avalonia.Controls/IScrollable.cs index 2a98b3910a..680088290c 100644 --- a/src/Avalonia.Controls/IScrollable.cs +++ b/src/Avalonia.Controls/IScrollable.cs @@ -1,4 +1,3 @@ - namespace Avalonia.Controls.Primitives { /// diff --git a/src/Avalonia.Controls/Notifications/IManagedNotificationManager.cs b/src/Avalonia.Controls/Notifications/IManagedNotificationManager.cs index 977544674d..b2e6e9e80b 100644 --- a/src/Avalonia.Controls/Notifications/IManagedNotificationManager.cs +++ b/src/Avalonia.Controls/Notifications/IManagedNotificationManager.cs @@ -1,4 +1,6 @@ -namespace Avalonia.Controls.Notifications +using Avalonia.Metadata; + +namespace Avalonia.Controls.Notifications { /// /// Represents a notification manager that can show arbitrary content. @@ -9,6 +11,7 @@ /// can display arbitrary content, as opposed to notification managers which display notifications /// using the host operating system's notification mechanism. /// + [NotClientImplementable] public interface IManagedNotificationManager : INotificationManager { /// diff --git a/src/Avalonia.Controls/Notifications/INotification.cs b/src/Avalonia.Controls/Notifications/INotification.cs index fa08233097..9ccce5b2c4 100644 --- a/src/Avalonia.Controls/Notifications/INotification.cs +++ b/src/Avalonia.Controls/Notifications/INotification.cs @@ -1,10 +1,12 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Controls.Notifications { /// /// Represents a notification that can be shown in a window or by the host operating system. /// + [NotClientImplementable] public interface INotification { /// diff --git a/src/Avalonia.Controls/Notifications/INotificationManager.cs b/src/Avalonia.Controls/Notifications/INotificationManager.cs index 72fb8e6c08..5fa479f2c3 100644 --- a/src/Avalonia.Controls/Notifications/INotificationManager.cs +++ b/src/Avalonia.Controls/Notifications/INotificationManager.cs @@ -1,9 +1,12 @@ -namespace Avalonia.Controls.Notifications +using Avalonia.Metadata; + +namespace Avalonia.Controls.Notifications { /// /// Represents a notification manager that can be used to show notifications in a window or using /// the host operating system. /// + [NotClientImplementable] public interface INotificationManager { /// diff --git a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs index 6e9ac537f1..abef29bbd9 100644 --- a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs +++ b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs @@ -4,6 +4,7 @@ using Avalonia.Input; using Avalonia.Input.Raw; using Avalonia.Interactivity; using Avalonia.LogicalTree; +using Avalonia.Metadata; using Avalonia.Platform; using Avalonia.Rendering; using Avalonia.Threading; @@ -14,6 +15,7 @@ namespace Avalonia.Controls.Platform /// /// Provides the default keyboard and pointer interaction for menus. /// + [Unstable] public class DefaultMenuInteractionHandler : IMenuInteractionHandler { private readonly bool _isContextMenu; diff --git a/src/Avalonia.Controls/Platform/IApplicationPlatformEvents.cs b/src/Avalonia.Controls/Platform/IApplicationPlatformEvents.cs index a8d3a3b3ac..99bbb8b56d 100644 --- a/src/Avalonia.Controls/Platform/IApplicationPlatformEvents.cs +++ b/src/Avalonia.Controls/Platform/IApplicationPlatformEvents.cs @@ -1,5 +1,8 @@ +using Avalonia.Metadata; + namespace Avalonia.Platform { + [Unstable] public interface IApplicationPlatformEvents { void RaiseUrlsOpened(string[] urls); diff --git a/src/Avalonia.Controls/Platform/IMenuInteractionHandler.cs b/src/Avalonia.Controls/Platform/IMenuInteractionHandler.cs index dd8503f768..47b5a048b0 100644 --- a/src/Avalonia.Controls/Platform/IMenuInteractionHandler.cs +++ b/src/Avalonia.Controls/Platform/IMenuInteractionHandler.cs @@ -1,8 +1,11 @@ -namespace Avalonia.Controls.Platform +using Avalonia.Metadata; + +namespace Avalonia.Controls.Platform { /// /// Handles user interaction for menus. /// + [Unstable] public interface IMenuInteractionHandler { /// diff --git a/src/Avalonia.Controls/Platform/IMountedVolumeInfoProvider.cs b/src/Avalonia.Controls/Platform/IMountedVolumeInfoProvider.cs index 6e10163175..daeb9076e6 100644 --- a/src/Avalonia.Controls/Platform/IMountedVolumeInfoProvider.cs +++ b/src/Avalonia.Controls/Platform/IMountedVolumeInfoProvider.cs @@ -1,13 +1,13 @@ using System; using System.Collections.ObjectModel; -using System.Threading.Tasks; -using Avalonia.Platform; +using Avalonia.Metadata; namespace Avalonia.Controls.Platform { /// /// Defines a platform-specific mount volumes info provider implementation. /// + [Unstable] public interface IMountedVolumeInfoProvider { /// diff --git a/src/Avalonia.Controls/Platform/INativeControlHostImpl.cs b/src/Avalonia.Controls/Platform/INativeControlHostImpl.cs index df13613848..ffa79aa8d6 100644 --- a/src/Avalonia.Controls/Platform/INativeControlHostImpl.cs +++ b/src/Avalonia.Controls/Platform/INativeControlHostImpl.cs @@ -1,10 +1,11 @@ using System; using System.Diagnostics.CodeAnalysis; +using Avalonia.Metadata; using Avalonia.Platform; -using Avalonia.VisualTree; namespace Avalonia.Controls.Platform { + [Unstable] public interface INativeControlHostImpl { INativeControlHostDestroyableControlHandle CreateDefaultChild(IPlatformHandle parent); @@ -13,11 +14,13 @@ namespace Avalonia.Controls.Platform bool IsCompatibleWith(IPlatformHandle handle); } + [Unstable] public interface INativeControlHostDestroyableControlHandle : IPlatformHandle { void Destroy(); } + [Unstable] public interface INativeControlHostControlTopLevelAttachment : IDisposable { INativeControlHostImpl? AttachedTo { get; set; } @@ -27,6 +30,7 @@ namespace Avalonia.Controls.Platform void ShowInBounds(Rect rect); } + [Unstable] public interface ITopLevelImplWithNativeControlHost { INativeControlHostImpl? NativeControlHost { get; } diff --git a/src/Avalonia.Controls/Platform/IPlatformIconLoader.cs b/src/Avalonia.Controls/Platform/IPlatformIconLoader.cs index ecbc6d2234..4c844ce30f 100644 --- a/src/Avalonia.Controls/Platform/IPlatformIconLoader.cs +++ b/src/Avalonia.Controls/Platform/IPlatformIconLoader.cs @@ -1,7 +1,9 @@ using System.IO; +using Avalonia.Metadata; namespace Avalonia.Platform { + [Unstable] public interface IPlatformIconLoader { IWindowIconImpl LoadIcon(string fileName); diff --git a/src/Avalonia.Controls/Platform/IPlatformLifetimeEventsImpl.cs b/src/Avalonia.Controls/Platform/IPlatformLifetimeEventsImpl.cs index 4cd6640453..0658f9211c 100644 --- a/src/Avalonia.Controls/Platform/IPlatformLifetimeEventsImpl.cs +++ b/src/Avalonia.Controls/Platform/IPlatformLifetimeEventsImpl.cs @@ -1,9 +1,10 @@ using System; -using System.ComponentModel; using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Metadata; namespace Avalonia.Platform { + [Unstable] public interface IPlatformLifetimeEventsImpl { /// diff --git a/src/Avalonia.Controls/Platform/IPlatformNativeSurfaceHandle.cs b/src/Avalonia.Controls/Platform/IPlatformNativeSurfaceHandle.cs index 264f5e4667..6ad07b1b13 100644 --- a/src/Avalonia.Controls/Platform/IPlatformNativeSurfaceHandle.cs +++ b/src/Avalonia.Controls/Platform/IPlatformNativeSurfaceHandle.cs @@ -1,7 +1,9 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Platform { + [Unstable] public interface IPlatformNativeSurfaceHandle : IPlatformHandle { PixelSize Size { get; } diff --git a/src/Avalonia.Controls/Platform/IPopupImpl.cs b/src/Avalonia.Controls/Platform/IPopupImpl.cs index 477d5fab43..cd86045dee 100644 --- a/src/Avalonia.Controls/Platform/IPopupImpl.cs +++ b/src/Avalonia.Controls/Platform/IPopupImpl.cs @@ -1,10 +1,12 @@ using Avalonia.Controls.Primitives.PopupPositioning; +using Avalonia.Metadata; namespace Avalonia.Platform { /// /// Defines a platform-specific popup window implementation. /// + [Unstable] public interface IPopupImpl : IWindowBaseImpl { IPopupPositioner PopupPositioner { get; } diff --git a/src/Avalonia.Controls/Platform/IScreenImpl.cs b/src/Avalonia.Controls/Platform/IScreenImpl.cs index b68391aa52..fcae3b6493 100644 --- a/src/Avalonia.Controls/Platform/IScreenImpl.cs +++ b/src/Avalonia.Controls/Platform/IScreenImpl.cs @@ -1,9 +1,9 @@ using System.Collections.Generic; - -#nullable enable +using Avalonia.Metadata; namespace Avalonia.Platform { + [Unstable] public interface IScreenImpl { int ScreenCount { get; } diff --git a/src/Avalonia.Controls/Platform/ISystemDialogImpl.cs b/src/Avalonia.Controls/Platform/ISystemDialogImpl.cs index 1685a6a38c..715eda5cfa 100644 --- a/src/Avalonia.Controls/Platform/ISystemDialogImpl.cs +++ b/src/Avalonia.Controls/Platform/ISystemDialogImpl.cs @@ -1,10 +1,12 @@ using System.Threading.Tasks; +using Avalonia.Metadata; namespace Avalonia.Controls.Platform { /// /// Defines a platform-specific system dialog implementation. /// + [Unstable] public interface ISystemDialogImpl { /// diff --git a/src/Avalonia.Controls/Platform/ITopLevelImpl.cs b/src/Avalonia.Controls/Platform/ITopLevelImpl.cs index 80434882f7..bd0339f525 100644 --- a/src/Avalonia.Controls/Platform/ITopLevelImpl.cs +++ b/src/Avalonia.Controls/Platform/ITopLevelImpl.cs @@ -4,6 +4,7 @@ using Avalonia.Controls; using Avalonia.Input; using Avalonia.Input.Raw; using Avalonia.Layout; +using Avalonia.Metadata; using Avalonia.Rendering; using JetBrains.Annotations; @@ -50,6 +51,7 @@ namespace Avalonia.Platform /// This interface is the common interface to and /// . /// + [Unstable] public interface ITopLevelImpl : IDisposable { /// diff --git a/src/Avalonia.Controls/Platform/ITopLevelImplWithTextInputMethod.cs b/src/Avalonia.Controls/Platform/ITopLevelImplWithTextInputMethod.cs index bafb973765..a2e426ca08 100644 --- a/src/Avalonia.Controls/Platform/ITopLevelImplWithTextInputMethod.cs +++ b/src/Avalonia.Controls/Platform/ITopLevelImplWithTextInputMethod.cs @@ -1,9 +1,11 @@ using Avalonia.Input; using Avalonia.Input.TextInput; +using Avalonia.Metadata; using Avalonia.Platform; namespace Avalonia.Controls.Platform { + [Unstable] public interface ITopLevelImplWithTextInputMethod : ITopLevelImpl { public ITextInputMethodImpl? TextInputMethod { get; } diff --git a/src/Avalonia.Controls/Platform/ITopLevelNativeMenuExporter.cs b/src/Avalonia.Controls/Platform/ITopLevelNativeMenuExporter.cs index 9e72a40439..149a978c54 100644 --- a/src/Avalonia.Controls/Platform/ITopLevelNativeMenuExporter.cs +++ b/src/Avalonia.Controls/Platform/ITopLevelNativeMenuExporter.cs @@ -1,13 +1,16 @@ using System; +using Avalonia.Metadata; using Avalonia.Platform; namespace Avalonia.Controls.Platform { + [Unstable] public interface INativeMenuExporter { void SetNativeMenu(NativeMenu? menu); } + [Unstable] public interface ITopLevelNativeMenuExporter : INativeMenuExporter { bool IsNativeMenuExported { get; } @@ -15,11 +18,13 @@ namespace Avalonia.Controls.Platform event EventHandler OnIsNativeMenuExportedChanged; } + [Unstable] public interface INativeMenuExporterProvider { INativeMenuExporter? NativeMenuExporter { get; } } - + + [Unstable] public interface ITopLevelImplWithNativeMenuExporter : ITopLevelImpl { ITopLevelNativeMenuExporter? NativeMenuExporter { get; } diff --git a/src/Avalonia.Controls/Platform/ITrayIconImpl.cs b/src/Avalonia.Controls/Platform/ITrayIconImpl.cs index 289b569645..4ef9397d04 100644 --- a/src/Avalonia.Controls/Platform/ITrayIconImpl.cs +++ b/src/Avalonia.Controls/Platform/ITrayIconImpl.cs @@ -1,8 +1,10 @@ using System; using Avalonia.Controls.Platform; +using Avalonia.Metadata; namespace Avalonia.Platform { + [Unstable] public interface ITrayIconImpl : IDisposable { /// diff --git a/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs b/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs index 066f4579c0..512fad6dfc 100644 --- a/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs +++ b/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs @@ -1,8 +1,10 @@ using System; using Avalonia.Automation.Peers; +using Avalonia.Metadata; namespace Avalonia.Platform { + [Unstable] public interface IWindowBaseImpl : ITopLevelImpl { /// diff --git a/src/Avalonia.Controls/Platform/IWindowIconImpl.cs b/src/Avalonia.Controls/Platform/IWindowIconImpl.cs index 7086b7651c..4bb8844d97 100644 --- a/src/Avalonia.Controls/Platform/IWindowIconImpl.cs +++ b/src/Avalonia.Controls/Platform/IWindowIconImpl.cs @@ -1,7 +1,9 @@ using System.IO; +using Avalonia.Metadata; namespace Avalonia.Platform { + [Unstable] public interface IWindowIconImpl { void Save(Stream outputStream); diff --git a/src/Avalonia.Controls/Platform/IWindowImpl.cs b/src/Avalonia.Controls/Platform/IWindowImpl.cs index d4be4f9f45..af9392d440 100644 --- a/src/Avalonia.Controls/Platform/IWindowImpl.cs +++ b/src/Avalonia.Controls/Platform/IWindowImpl.cs @@ -1,12 +1,14 @@ using System; using Avalonia.Controls; using Avalonia.Input; +using Avalonia.Metadata; namespace Avalonia.Platform { /// /// Defines a platform-specific window implementation. /// + [Unstable] public interface IWindowImpl : IWindowBaseImpl { /// diff --git a/src/Avalonia.Controls/Platform/IWindowingPlatform.cs b/src/Avalonia.Controls/Platform/IWindowingPlatform.cs index fa26fe8fdd..5acc5adccd 100644 --- a/src/Avalonia.Controls/Platform/IWindowingPlatform.cs +++ b/src/Avalonia.Controls/Platform/IWindowingPlatform.cs @@ -1,5 +1,8 @@ +using Avalonia.Metadata; + namespace Avalonia.Platform { + [Unstable] public interface IWindowingPlatform { IWindowImpl CreateWindow(); diff --git a/src/Avalonia.Controls/Platform/InternalPlatformThreadingInterface.cs b/src/Avalonia.Controls/Platform/InternalPlatformThreadingInterface.cs index 4e5908456e..630d2d8efb 100644 --- a/src/Avalonia.Controls/Platform/InternalPlatformThreadingInterface.cs +++ b/src/Avalonia.Controls/Platform/InternalPlatformThreadingInterface.cs @@ -1,14 +1,13 @@ using System; -using System.Collections.Generic; using System.Runtime.InteropServices; using System.Threading; -using System.Threading.Tasks; +using Avalonia.Metadata; using Avalonia.Platform; -using Avalonia.Rendering; using Avalonia.Threading; namespace Avalonia.Controls.Platform { + [Unstable] public class InternalPlatformThreadingInterface : IPlatformThreadingInterface { public InternalPlatformThreadingInterface() diff --git a/src/Avalonia.Controls/Platform/MountedDriveInfo.cs b/src/Avalonia.Controls/Platform/MountedDriveInfo.cs index f3104e4360..620ac9303f 100644 --- a/src/Avalonia.Controls/Platform/MountedDriveInfo.cs +++ b/src/Avalonia.Controls/Platform/MountedDriveInfo.cs @@ -1,10 +1,12 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Controls.Platform { /// /// Describes a Drive's properties. /// + [Unstable] public class MountedVolumeInfo : IEquatable { public string? VolumeLabel { get; set; } diff --git a/src/Avalonia.Controls/Platform/PlatformManager.cs b/src/Avalonia.Controls/Platform/PlatformManager.cs index ee62316922..92f6f1cb52 100644 --- a/src/Avalonia.Controls/Platform/PlatformManager.cs +++ b/src/Avalonia.Controls/Platform/PlatformManager.cs @@ -1,9 +1,11 @@ using System; using System.Reactive.Disposables; +using Avalonia.Metadata; using Avalonia.Platform; namespace Avalonia.Controls.Platform { + [Unstable] public static partial class PlatformManager { static bool s_designerMode; diff --git a/src/Avalonia.Controls/Platform/Surfaces/IFramebufferPlatformSurface.cs b/src/Avalonia.Controls/Platform/Surfaces/IFramebufferPlatformSurface.cs index 62cd012d51..0a7daeaa24 100644 --- a/src/Avalonia.Controls/Platform/Surfaces/IFramebufferPlatformSurface.cs +++ b/src/Avalonia.Controls/Platform/Surfaces/IFramebufferPlatformSurface.cs @@ -1,7 +1,9 @@ -using Avalonia.Platform; +using Avalonia.Metadata; +using Avalonia.Platform; namespace Avalonia.Controls.Platform.Surfaces { + [Unstable] public interface IFramebufferPlatformSurface { /// diff --git a/src/Avalonia.Controls/Presenters/IContentPresenter.cs b/src/Avalonia.Controls/Presenters/IContentPresenter.cs index ab4d61e3bd..673de4700b 100644 --- a/src/Avalonia.Controls/Presenters/IContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/IContentPresenter.cs @@ -1,4 +1,5 @@ using Avalonia.Controls.Primitives; +using Avalonia.Metadata; namespace Avalonia.Controls.Presenters { @@ -6,6 +7,7 @@ namespace Avalonia.Controls.Presenters /// Interface for controls that present a single item of data inside a /// template. /// + [NotClientImplementable] public interface IContentPresenter : IPresenter { /// diff --git a/src/Avalonia.Controls/Presenters/IContentPresenterHost.cs b/src/Avalonia.Controls/Presenters/IContentPresenterHost.cs index 78c4affe44..562638e94a 100644 --- a/src/Avalonia.Controls/Presenters/IContentPresenterHost.cs +++ b/src/Avalonia.Controls/Presenters/IContentPresenterHost.cs @@ -1,5 +1,6 @@ using Avalonia.Collections; using Avalonia.LogicalTree; +using Avalonia.Metadata; using Avalonia.Styling; namespace Avalonia.Controls.Presenters @@ -15,6 +16,7 @@ namespace Avalonia.Controls.Presenters /// parent control's template is instantiated so they register themselves using this /// interface. /// + [NotClientImplementable] public interface IContentPresenterHost : ITemplatedControl { /// diff --git a/src/Avalonia.Controls/Presenters/IItemsPresenter.cs b/src/Avalonia.Controls/Presenters/IItemsPresenter.cs index e7da3d4618..7cc72ef0a7 100644 --- a/src/Avalonia.Controls/Presenters/IItemsPresenter.cs +++ b/src/Avalonia.Controls/Presenters/IItemsPresenter.cs @@ -1,8 +1,10 @@ using System.Collections; using System.Collections.Specialized; +using Avalonia.Metadata; namespace Avalonia.Controls.Presenters { + [NotClientImplementable] public interface IItemsPresenter : IPresenter { IEnumerable? Items { get; set; } diff --git a/src/Avalonia.Controls/Presenters/IItemsPresenterHost.cs b/src/Avalonia.Controls/Presenters/IItemsPresenterHost.cs index ba9ee0fe31..db11474871 100644 --- a/src/Avalonia.Controls/Presenters/IItemsPresenterHost.cs +++ b/src/Avalonia.Controls/Presenters/IItemsPresenterHost.cs @@ -1,3 +1,4 @@ +using Avalonia.Metadata; using Avalonia.Styling; namespace Avalonia.Controls.Presenters @@ -13,6 +14,7 @@ namespace Avalonia.Controls.Presenters /// parent control's template is instantiated so they register themselves using this /// interface. /// + [NotClientImplementable] public interface IItemsPresenterHost : ITemplatedControl { /// diff --git a/src/Avalonia.Controls/Presenters/IPresenter.cs b/src/Avalonia.Controls/Presenters/IPresenter.cs index 5318ea2757..0399983189 100644 --- a/src/Avalonia.Controls/Presenters/IPresenter.cs +++ b/src/Avalonia.Controls/Presenters/IPresenter.cs @@ -1,4 +1,5 @@ using Avalonia.Controls.Primitives; +using Avalonia.Metadata; namespace Avalonia.Controls.Presenters { @@ -12,6 +13,7 @@ namespace Avalonia.Controls.Presenters /// of a then that signals that the visual child /// of the presenter is not a part of the template. /// + [NotClientImplementable] public interface IPresenter : IControl, INamed { } diff --git a/src/Avalonia.Controls/Primitives/IPopupHost.cs b/src/Avalonia.Controls/Primitives/IPopupHost.cs index 36d2ae9230..99b7be6385 100644 --- a/src/Avalonia.Controls/Primitives/IPopupHost.cs +++ b/src/Avalonia.Controls/Primitives/IPopupHost.cs @@ -2,6 +2,7 @@ using System; using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives.PopupPositioning; using Avalonia.Input; +using Avalonia.Metadata; using Avalonia.VisualTree; namespace Avalonia.Controls.Primitives @@ -14,6 +15,7 @@ namespace Avalonia.Controls.Primitives /// () or an which is created /// on an . /// + [NotClientImplementable] public interface IPopupHost : IDisposable, IFocusScope { /// diff --git a/src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs b/src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs index 8daf1ac68a..8d35a91e00 100644 --- a/src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs +++ b/src/Avalonia.Controls/Primitives/PopupPositioning/IPopupPositioner.cs @@ -45,6 +45,7 @@ Copyright © 2019 Nikita Tsukanov */ using System; +using Avalonia.Metadata; using Avalonia.VisualTree; using Avalonia.Media; @@ -61,6 +62,7 @@ namespace Avalonia.Controls.Primitives.PopupPositioning /// requirement that a popup must intersect with or be at least partially adjacent to its parent /// surface. /// + [Unstable] public struct PopupPositionerParameters { private PopupGravity _gravity; @@ -429,6 +431,7 @@ namespace Avalonia.Controls.Primitives.PopupPositioning /// managed implementation is provided in for platforms /// on which popups can be arbitrarily positioned. /// + [NotClientImplementable] public interface IPopupPositioner { /// @@ -439,6 +442,7 @@ namespace Avalonia.Controls.Primitives.PopupPositioning void Update(PopupPositionerParameters parameters); } + [Unstable] static class PopupPositionerExtensions { public static void ConfigurePosition(ref this PopupPositionerParameters positionerParameters, diff --git a/src/Avalonia.Controls/Remote/RemoteServer.cs b/src/Avalonia.Controls/Remote/RemoteServer.cs index f4cc91a0e6..2cf2d2b97d 100644 --- a/src/Avalonia.Controls/Remote/RemoteServer.cs +++ b/src/Avalonia.Controls/Remote/RemoteServer.cs @@ -1,11 +1,12 @@ using System; using Avalonia.Controls.Embedding; using Avalonia.Controls.Remote.Server; -using Avalonia.Platform; +using Avalonia.Metadata; using Avalonia.Remote.Protocol; namespace Avalonia.Controls.Remote { + [Unstable] public class RemoteServer { private EmbeddableControlRoot _topLevel; diff --git a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs index c9fd1dc3b8..e800f2f4b0 100644 --- a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs +++ b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs @@ -6,6 +6,7 @@ using Avalonia.Controls.Platform.Surfaces; using Avalonia.Input; using Avalonia.Input.Raw; using Avalonia.Layout; +using Avalonia.Metadata; using Avalonia.Platform; using Avalonia.Remote.Protocol; using Avalonia.Remote.Protocol.Input; @@ -18,6 +19,7 @@ using ProtocolPixelFormat = Avalonia.Remote.Protocol.Viewport.PixelFormat; namespace Avalonia.Controls.Remote.Server { + [Unstable] public class RemoteServerTopLevelImpl : OffscreenTopLevelImplBase, IFramebufferPlatformSurface { private readonly IAvaloniaRemoteTransportConnection _transport; diff --git a/src/Avalonia.Controls/Templates/IDataTemplateHost.cs b/src/Avalonia.Controls/Templates/IDataTemplateHost.cs index 61986a0661..ce763c3336 100644 --- a/src/Avalonia.Controls/Templates/IDataTemplateHost.cs +++ b/src/Avalonia.Controls/Templates/IDataTemplateHost.cs @@ -1,9 +1,11 @@ - +using Avalonia.Metadata; + namespace Avalonia.Controls.Templates { /// /// Defines an element that has a collection. /// + [NotClientImplementable] public interface IDataTemplateHost { /// diff --git a/src/Avalonia.OpenGL/Imaging/IOpenGlBitmapImpl.cs b/src/Avalonia.OpenGL/Imaging/IOpenGlBitmapImpl.cs index aef4f601be..22f0cebf57 100644 --- a/src/Avalonia.OpenGL/Imaging/IOpenGlBitmapImpl.cs +++ b/src/Avalonia.OpenGL/Imaging/IOpenGlBitmapImpl.cs @@ -1,15 +1,17 @@ using System; -using Avalonia.Media.Imaging; +using Avalonia.Metadata; using Avalonia.Platform; namespace Avalonia.OpenGL.Imaging { + [Unstable] public interface IOpenGlBitmapImpl : IBitmapImpl { IOpenGlBitmapAttachment CreateFramebufferAttachment(IGlContext context, Action presentCallback); bool SupportsContext(IGlContext context); } + [Unstable] public interface IOpenGlBitmapAttachment : IDisposable { void Present(); diff --git a/src/Skia/Avalonia.Skia/GlyphRunImpl.cs b/src/Skia/Avalonia.Skia/GlyphRunImpl.cs index f59a0a32c2..bdc3d075cf 100644 --- a/src/Skia/Avalonia.Skia/GlyphRunImpl.cs +++ b/src/Skia/Avalonia.Skia/GlyphRunImpl.cs @@ -1,4 +1,5 @@ using System; +using Avalonia.Metadata; using Avalonia.Platform; using JetBrains.Annotations; using SkiaSharp; @@ -6,6 +7,7 @@ using SkiaSharp; namespace Avalonia.Skia { /// + [Unstable] public class GlyphRunImpl : IGlyphRunImpl { public GlyphRunImpl([NotNull] SKTextBlob textBlob) diff --git a/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs b/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs index 5b6e5af60f..dcb4eac7ca 100644 --- a/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs +++ b/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs @@ -1,11 +1,13 @@ using System; using System.Runtime.InteropServices; +using Avalonia.Metadata; using Avalonia.Platform; using HarfBuzzSharp; using SkiaSharp; namespace Avalonia.Skia { + [Unstable] public class GlyphTypefaceImpl : IGlyphTypefaceImpl { private bool _isDisposed; diff --git a/src/Skia/Avalonia.Skia/ISkiaDrawingContextImpl.cs b/src/Skia/Avalonia.Skia/ISkiaDrawingContextImpl.cs index 38fa5a5253..1391a8f195 100644 --- a/src/Skia/Avalonia.Skia/ISkiaDrawingContextImpl.cs +++ b/src/Skia/Avalonia.Skia/ISkiaDrawingContextImpl.cs @@ -1,8 +1,10 @@ +using Avalonia.Metadata; using Avalonia.Platform; using SkiaSharp; namespace Avalonia.Skia { + [Unstable] public interface ISkiaDrawingContextImpl : IDrawingContextImpl { SKCanvas SkCanvas { get; } diff --git a/src/Windows/Avalonia.Direct2D1/Media/BrushImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/BrushImpl.cs index ad609a0810..602ea9b568 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/BrushImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/BrushImpl.cs @@ -1,7 +1,9 @@ using System; +using Avalonia.Metadata; namespace Avalonia.Direct2D1.Media { + [Unstable] public abstract class BrushImpl : IDisposable { public SharpDX.Direct2D1.Brush PlatformBrush { get; set; } diff --git a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs index a259d8fab9..a0f98bbbc9 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs @@ -10,12 +10,14 @@ using SharpDX; using SharpDX.Direct2D1; using SharpDX.Mathematics.Interop; using BitmapInterpolationMode = Avalonia.Media.Imaging.BitmapInterpolationMode; +using Avalonia.Metadata; namespace Avalonia.Direct2D1.Media { /// /// Draws using Direct2D1. /// + [Unstable] public class DrawingContextImpl : IDrawingContextImpl { private readonly IVisualBrushRenderer _visualBrushRenderer; diff --git a/src/Windows/Avalonia.Direct2D1/Media/GeometryImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/GeometryImpl.cs index ec88347a17..c84c14daac 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/GeometryImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/GeometryImpl.cs @@ -1,4 +1,5 @@ using Avalonia.Logging; +using Avalonia.Metadata; using Avalonia.Platform; using SharpDX.Direct2D1; @@ -7,6 +8,7 @@ namespace Avalonia.Direct2D1.Media /// /// The platform-specific interface for . /// + [Unstable] public abstract class GeometryImpl : IGeometryImpl { private const float ContourApproximation = 0.0001f; diff --git a/src/Windows/Avalonia.Direct2D1/Media/GlyphTypefaceImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/GlyphTypefaceImpl.cs index 4f2ed22a25..4154b44702 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/GlyphTypefaceImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/GlyphTypefaceImpl.cs @@ -1,11 +1,13 @@ using System; using Avalonia.Media; +using Avalonia.Metadata; using Avalonia.Platform; using HarfBuzzSharp; using SharpDX.DirectWrite; namespace Avalonia.Direct2D1.Media { + [Unstable] public class GlyphTypefaceImpl : IGlyphTypefaceImpl { private bool _isDisposed; diff --git a/src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs index 296cefad4e..17dc359ed7 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs @@ -1,10 +1,12 @@ using Avalonia.Media; +using Avalonia.Metadata; using Avalonia.Rendering.Utilities; using Avalonia.Utilities; using SharpDX.Direct2D1; namespace Avalonia.Direct2D1.Media { + [Unstable] public sealed class ImageBrushImpl : BrushImpl { private readonly OptionalDispose _bitmap; diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/BitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/BitmapImpl.cs index af6d5c5e7b..843efe2cc4 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/BitmapImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/BitmapImpl.cs @@ -1,10 +1,12 @@ using System; using System.IO; +using Avalonia.Metadata; using Avalonia.Platform; using D2DBitmap = SharpDX.Direct2D1.Bitmap; namespace Avalonia.Direct2D1.Media { + [Unstable] public abstract class BitmapImpl : IBitmapImpl, IDisposable { public abstract Vector Dpi { get; } diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DBitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DBitmapImpl.cs index 63676e30b5..2656ab4c58 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DBitmapImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DBitmapImpl.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using Avalonia.Metadata; using SharpDX.WIC; using Bitmap = SharpDX.Direct2D1.Bitmap; @@ -8,6 +9,7 @@ namespace Avalonia.Direct2D1.Media /// /// A Direct2D Bitmap implementation that uses a GPU memory bitmap as its image. /// + [Unstable] public class D2DBitmapImpl : BitmapImpl { private readonly Bitmap _direct2DBitmap; diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs index 9a0e2ec00c..357e472d34 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using Avalonia.Metadata; using Avalonia.Platform; using Avalonia.Rendering; using Avalonia.Utilities; @@ -9,6 +10,7 @@ using D2DBitmap = SharpDX.Direct2D1.Bitmap; namespace Avalonia.Direct2D1.Media.Imaging { + [Unstable] public class D2DRenderTargetBitmapImpl : D2DBitmapImpl, IDrawingContextLayerImpl, ILayerFactory { private readonly BitmapRenderTarget _renderTarget; diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs index df07f7f39c..1156246b29 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs @@ -5,12 +5,14 @@ using SharpDX.WIC; using APixelFormat = Avalonia.Platform.PixelFormat; using AlphaFormat = Avalonia.Platform.AlphaFormat; using D2DBitmap = SharpDX.Direct2D1.Bitmap; +using Avalonia.Metadata; namespace Avalonia.Direct2D1.Media { /// /// A WIC implementation of a . /// + [Unstable] public class WicBitmapImpl : BitmapImpl { private readonly BitmapDecoder _decoder; diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicRenderTargetBitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicRenderTargetBitmapImpl.cs index 1265a7bdf0..8c9d01f37d 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicRenderTargetBitmapImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicRenderTargetBitmapImpl.cs @@ -1,10 +1,12 @@ using System; +using Avalonia.Metadata; using Avalonia.Platform; using Avalonia.Rendering; using SharpDX.Direct2D1; namespace Avalonia.Direct2D1.Media { + [Unstable] public class WicRenderTargetBitmapImpl : WicBitmapImpl, IDrawingContextLayerImpl { private readonly WicRenderTarget _renderTarget; diff --git a/src/Windows/Avalonia.Direct2D1/Media/LinearGradientBrushImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/LinearGradientBrushImpl.cs index 0e63d4cc03..5dfe683f59 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/LinearGradientBrushImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/LinearGradientBrushImpl.cs @@ -1,8 +1,10 @@ using System.Linq; using Avalonia.Media; +using Avalonia.Metadata; namespace Avalonia.Direct2D1.Media { + [Unstable] public class LinearGradientBrushImpl : BrushImpl { public LinearGradientBrushImpl( diff --git a/src/Windows/Avalonia.Direct2D1/Media/RadialGradientBrushImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/RadialGradientBrushImpl.cs index 1fca6d4e33..0069e47001 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/RadialGradientBrushImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/RadialGradientBrushImpl.cs @@ -1,8 +1,10 @@ using System.Linq; using Avalonia.Media; +using Avalonia.Metadata; namespace Avalonia.Direct2D1.Media { + [Unstable] public class RadialGradientBrushImpl : BrushImpl { public RadialGradientBrushImpl( diff --git a/src/Windows/Avalonia.Direct2D1/Media/SolidColorBrushImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/SolidColorBrushImpl.cs index fea1ca9157..b85494e2c1 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/SolidColorBrushImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/SolidColorBrushImpl.cs @@ -1,7 +1,9 @@ using Avalonia.Media; +using Avalonia.Metadata; namespace Avalonia.Direct2D1.Media { + [Unstable] public class SolidColorBrushImpl : BrushImpl { public SolidColorBrushImpl(ISolidColorBrush brush, SharpDX.Direct2D1.RenderTarget target) diff --git a/src/Windows/Avalonia.Direct2D1/Media/StreamGeometryContextImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/StreamGeometryContextImpl.cs index e1f7aad1b2..ec8f82556d 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/StreamGeometryContextImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/StreamGeometryContextImpl.cs @@ -1,6 +1,7 @@ using System; using Avalonia.Logging; using Avalonia.Media; +using Avalonia.Metadata; using Avalonia.Platform; using SharpDX.Direct2D1; using D2D = SharpDX.Direct2D1; @@ -8,6 +9,7 @@ using SweepDirection = SharpDX.Direct2D1.SweepDirection; namespace Avalonia.Direct2D1.Media { + [Unstable] public class StreamGeometryContextImpl : IStreamGeometryContextImpl { private readonly GeometrySink _sink; diff --git a/src/Windows/Avalonia.Direct2D1/Media/StreamGeometryImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/StreamGeometryImpl.cs index 2bc2b2db71..e1677c0ed1 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/StreamGeometryImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/StreamGeometryImpl.cs @@ -1,3 +1,4 @@ +using Avalonia.Metadata; using Avalonia.Platform; using SharpDX.Direct2D1; @@ -6,6 +7,7 @@ namespace Avalonia.Direct2D1.Media /// /// A Direct2D implementation of a . /// + [Unstable] public class StreamGeometryImpl : GeometryImpl, IStreamGeometryImpl { /// diff --git a/src/Windows/Avalonia.Direct2D1/Media/TransformedGeometryImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/TransformedGeometryImpl.cs index fe274701bf..3ecdb49e46 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/TransformedGeometryImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/TransformedGeometryImpl.cs @@ -1,8 +1,10 @@ +using Avalonia.Metadata; using Avalonia.Platform; using SharpDX.Direct2D1; namespace Avalonia.Direct2D1.Media { + [Unstable] public class TransformedGeometryImpl : GeometryImpl, ITransformedGeometryImpl { /// diff --git a/src/Windows/Avalonia.Win32/ScreenImpl.cs b/src/Windows/Avalonia.Win32/ScreenImpl.cs index 96e45927da..f3754cd58f 100644 --- a/src/Windows/Avalonia.Win32/ScreenImpl.cs +++ b/src/Windows/Avalonia.Win32/ScreenImpl.cs @@ -1,12 +1,14 @@ using System; using System.Collections.Generic; using System.Linq; +using Avalonia.Metadata; using Avalonia.Platform; using Avalonia.Win32.Interop; using static Avalonia.Win32.Interop.UnmanagedMethods; namespace Avalonia.Win32 { + [Unstable] public class ScreenImpl : IScreenImpl { public int ScreenCount diff --git a/src/Windows/Avalonia.Win32/TrayIconImpl.cs b/src/Windows/Avalonia.Win32/TrayIconImpl.cs index 6484ae6c54..7c3b8cf2d2 100644 --- a/src/Windows/Avalonia.Win32/TrayIconImpl.cs +++ b/src/Windows/Avalonia.Win32/TrayIconImpl.cs @@ -5,6 +5,7 @@ using Avalonia.Controls; using Avalonia.Controls.Platform; using Avalonia.Controls.Primitives.PopupPositioning; using Avalonia.LogicalTree; +using Avalonia.Metadata; using Avalonia.Platform; using Avalonia.Styling; using Avalonia.Win32.Interop; @@ -14,6 +15,7 @@ using static Avalonia.Win32.Interop.UnmanagedMethods; namespace Avalonia.Win32 { + [Unstable] public class TrayIconImpl : ITrayIconImpl { private readonly int _uniqueId; diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index f0036236ec..8d836ef452 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -21,12 +21,14 @@ using Avalonia.Win32.OpenGl; using Avalonia.Win32.WinRT; using Avalonia.Win32.WinRT.Composition; using static Avalonia.Win32.Interop.UnmanagedMethods; +using Avalonia.Metadata; namespace Avalonia.Win32 { /// /// Window implementation for Win32 platform. /// + [Unstable] public partial class WindowImpl : IWindowImpl, EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo, ITopLevelImplWithNativeControlHost, ITopLevelImplWithTextInputMethod From f63ec5f5a915789a0dff264585ec895bd1b3cf61 Mon Sep 17 00:00:00 2001 From: robloo Date: Tue, 26 Apr 2022 18:47:51 -0400 Subject: [PATCH 212/416] Separate color picker control styles --- .../Themes/Default.xaml | 133 +--------------- .../Themes/Default/ColorSpectrum.xaml | 134 ++++++++++++++++ .../Themes/Fluent.xaml | 146 +++--------------- .../Themes/Fluent/ColorSpectrum.xaml | 134 ++++++++++++++++ 4 files changed, 290 insertions(+), 257 deletions(-) create mode 100644 src/Avalonia.Controls.ColorPicker/Themes/Default/ColorSpectrum.xaml create mode 100644 src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSpectrum.xaml diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Default.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Default.xaml index 832daf8853..528eed9969 100644 --- a/src/Avalonia.Controls.ColorPicker/Themes/Default.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Default.xaml @@ -1,134 +1,7 @@ + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> - - - - - - - - - - - - - - - - - - - - - - - - - - - + + diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Default/ColorSpectrum.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Default/ColorSpectrum.xaml new file mode 100644 index 0000000000..9596ca9653 --- /dev/null +++ b/src/Avalonia.Controls.ColorPicker/Themes/Default/ColorSpectrum.xaml @@ -0,0 +1,134 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml index 545702ea84..fb656ce964 100644 --- a/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml @@ -1,134 +1,26 @@ + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> - - - + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSpectrum.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSpectrum.xaml new file mode 100644 index 0000000000..b209fe75b3 --- /dev/null +++ b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSpectrum.xaml @@ -0,0 +1,134 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 21dd8392bf95045d404dbb67a4e5c589d691eb27 Mon Sep 17 00:00:00 2001 From: robloo Date: Tue, 26 Apr 2022 18:48:39 -0400 Subject: [PATCH 213/416] Add initial ColorPreviewer primitive --- .../ColorPreviewer/AccentColorConverter.cs | 112 ++++++++++++++ .../ColorPreviewer.Properties.cs | 69 +++++++++ .../ColorPreviewer/ColorPreviewer.cs | 138 ++++++++++++++++++ .../Themes/Fluent.xaml | 1 + .../Themes/Fluent/ColorPreviewer.xaml | 90 ++++++++++++ 5 files changed, 410 insertions(+) create mode 100644 src/Avalonia.Controls.ColorPicker/ColorPreviewer/AccentColorConverter.cs create mode 100644 src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.Properties.cs create mode 100644 src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.cs create mode 100644 src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorPreviewer.xaml diff --git a/src/Avalonia.Controls.ColorPicker/ColorPreviewer/AccentColorConverter.cs b/src/Avalonia.Controls.ColorPicker/ColorPreviewer/AccentColorConverter.cs new file mode 100644 index 0000000000..ad8f66251a --- /dev/null +++ b/src/Avalonia.Controls.ColorPicker/ColorPreviewer/AccentColorConverter.cs @@ -0,0 +1,112 @@ +using System; +using System.Globalization; +using Avalonia.Data.Converters; +using Avalonia.Media; + +namespace Avalonia.Controls.Primitives +{ + /// + /// Creates an accent color for a given base color value and step parameter. + /// + public class AccentColorConverter : IValueConverter + { + /// + /// The amount to change the Value channel for each accent color step. + /// + public const double ValueDelta = 0.1; + + /// + public object? Convert( + object? value, + Type targetType, + object? parameter, + CultureInfo culture) + { + int accentStep; + Color? rgbColor = null; + HsvColor? hsvColor = null; + + // Get the current color in HSV + if (value is Color valueColor) + { + rgbColor = valueColor; + } + else if (value is HsvColor valueHsvColor) + { + hsvColor = valueHsvColor; + } + else if (value is SolidColorBrush valueBrush) + { + rgbColor = valueBrush.Color; + } + else + { + // Invalid color value provided + return AvaloniaProperty.UnsetValue; + } + + // Get the value component delta + try + { + accentStep = int.Parse(parameter?.ToString() ?? "", CultureInfo.InvariantCulture); + } + catch + { + // Invalid parameter provided, unable to convert to integer + return AvaloniaProperty.UnsetValue; + } + + if (hsvColor == null && + rgbColor != null) + { + hsvColor = rgbColor.Value.ToHsv(); + } + + if (hsvColor != null) + { + return new SolidColorBrush(GetAccent(hsvColor.Value, accentStep).ToRgb()); + } + else + { + return AvaloniaProperty.UnsetValue; + } + } + + /// + public object? ConvertBack( + object? value, + Type targetType, + object? parameter, + CultureInfo culture) + { + return AvaloniaProperty.UnsetValue; + } + + /// + /// This does not account for perceptual differences and also does not match with + /// system accent color calculation. + /// + /// + /// Use the HSV representation as it's more perceptual. + /// In most cases only the value is changed by a fixed percentage so the algorithm is reproducible. + /// + /// The base color to calculate the accent from. + /// The number of accent color steps to move. + /// The new accent color. + public static HsvColor GetAccent(HsvColor hsvColor, int accentStep) + { + if (accentStep != 0) + { + double colorValue = hsvColor.V; + colorValue += (accentStep * AccentColorConverter.ValueDelta); + colorValue = Math.Round(colorValue, 2); + + return new HsvColor(hsvColor.A, hsvColor.H, hsvColor.S, colorValue); + } + else + { + return hsvColor; + } + } + } +} diff --git a/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.Properties.cs b/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.Properties.cs new file mode 100644 index 0000000000..74c0943919 --- /dev/null +++ b/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.Properties.cs @@ -0,0 +1,69 @@ +using Avalonia.Media; + +namespace Avalonia.Controls.Primitives +{ + /// + public partial class ColorPreviewer + { + /// + /// Defines the property. + /// + public static readonly StyledProperty ColorProperty = + AvaloniaProperty.Register( + nameof(Color), + Colors.White); + + /// + /// Gets or sets the currently previewed color in the RGB color model. + /// + /// + /// For control authors use instead to avoid loss + /// of precision and color drifting. + /// + public Color Color + { + get => GetValue(ColorProperty); + set => SetValue(ColorProperty, value); + } + + /// + /// Defines the property. + /// + public static readonly StyledProperty HsvColorProperty = + AvaloniaProperty.Register( + nameof(HsvColor), + Colors.Transparent.ToHsv()); + + /// + /// Gets or sets the currently previewed color in the HSV color model. + /// + /// + /// This should be used in all cases instead of the property. + /// Internally, the uses the HSV color model and using + /// this property will avoid loss of precision and color drifting. + /// + public HsvColor HsvColor + { + get => GetValue(HsvColorProperty); + set => SetValue(HsvColorProperty, value); + } + + /// + /// Defines the property. + /// + public static readonly StyledProperty ShowAccentColorsProperty = + AvaloniaProperty.Register( + nameof(ShowAccentColors), + true); + + /// + /// Gets or sets a value indicating whether accent colors are shown along + /// with the preview color. + /// + public bool ShowAccentColors + { + get => (bool)this.GetValue(ShowAccentColorsProperty); + set => SetValue(ShowAccentColorsProperty, value); + } + } +} diff --git a/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.cs b/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.cs new file mode 100644 index 0000000000..35bd62601f --- /dev/null +++ b/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.cs @@ -0,0 +1,138 @@ +using System; +using System.Globalization; +using Avalonia.Controls.Metadata; +using Avalonia.Input; +using Avalonia.Media; + +namespace Avalonia.Controls.Primitives +{ + /// + /// Presents a preview color with optional accent colors. + /// + [TemplatePart(Name = nameof(AccentDec1Border), Type = typeof(Border))] + [TemplatePart(Name = nameof(AccentDec2Border), Type = typeof(Border))] + [TemplatePart(Name = nameof(AccentInc1Border), Type = typeof(Border))] + [TemplatePart(Name = nameof(AccentInc2Border), Type = typeof(Border))] + public partial class ColorPreviewer : TemplatedControl + { + /// + /// Event for when the selected color changes within the previewer. + /// This happens when an accent color is pressed. + /// + public event EventHandler? ColorChanged; + + private bool eventsConnected = false; + + private Border? AccentDec1Border; + private Border? AccentDec2Border; + private Border? AccentInc1Border; + private Border? AccentInc2Border; + + /// + /// Initializes a new instance of the class. + /// + public ColorPreviewer() : base() + { + } + + /// + /// Connects or disconnects all control event handlers. + /// + /// True to connect event handlers, otherwise false. + private void ConnectEvents(bool connected) + { + if (connected == true && eventsConnected == false) + { + // Add all events + if (AccentDec1Border != null) { AccentDec1Border.PointerPressed += AccentBorder_PointerPressed; } + if (AccentDec2Border != null) { AccentDec2Border.PointerPressed += AccentBorder_PointerPressed; } + if (AccentInc1Border != null) { AccentInc1Border.PointerPressed += AccentBorder_PointerPressed; } + if (AccentInc2Border != null) { AccentInc2Border.PointerPressed += AccentBorder_PointerPressed; } + + eventsConnected = true; + } + else if (connected == false && eventsConnected == true) + { + // Remove all events + if (AccentDec1Border != null) { AccentDec1Border.PointerPressed -= AccentBorder_PointerPressed; } + if (AccentDec2Border != null) { AccentDec2Border.PointerPressed -= AccentBorder_PointerPressed; } + if (AccentInc1Border != null) { AccentInc1Border.PointerPressed -= AccentBorder_PointerPressed; } + if (AccentInc2Border != null) { AccentInc2Border.PointerPressed -= AccentBorder_PointerPressed; } + + eventsConnected = false; + } + + return; + } + + /// + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) + { + // Remove any existing events present if the control was previously loaded then unloaded + ConnectEvents(false); + + AccentDec1Border = e.NameScope.Find(nameof(AccentDec1Border)); + AccentDec2Border = e.NameScope.Find(nameof(AccentDec2Border)); + AccentInc1Border = e.NameScope.Find(nameof(AccentInc1Border)); + AccentInc2Border = e.NameScope.Find(nameof(AccentInc2Border)); + + // Must connect after controls are found + ConnectEvents(true); + + base.OnApplyTemplate(e); + } + + /// + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + // Always keep the two color properties in sync + if (change.Property == ColorProperty) + { + HsvColor = Color.ToHsv(); + } + else if (change.Property == HsvColorProperty) + { + Color = HsvColor.ToRgb(); + } + + base.OnPropertyChanged(change); + } + + /// + /// Called before the event occurs. + /// + /// The newly selected color. + protected virtual void OnColorChanged(HsvColor newColor) + { + var oldColor = HsvColor; + HsvColor = newColor; + + ColorChanged?.Invoke(this, new ColorChangedEventArgs(oldColor.ToRgb(), newColor.ToRgb())); + + return; + } + + /// + /// Event handler for when an accent color border is pressed. + /// This will update the color to the background of the pressed panel. + /// + private void AccentBorder_PointerPressed(object? sender, PointerPressedEventArgs e) + { + Border? border = sender as Border; + int accentStep = 0; + HsvColor hsvColor = HsvColor; + + // Get the value component delta + try + { + accentStep = int.Parse(border?.Tag?.ToString() ?? "", CultureInfo.InvariantCulture); + } + catch { } + + HsvColor newHsvColor = AccentColorConverter.GetAccent(hsvColor, accentStep); + OnColorChanged(newHsvColor); + + return; + } + } +} diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml index fb656ce964..d96066e56b 100644 --- a/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml @@ -21,6 +21,7 @@ + diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorPreviewer.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorPreviewer.xaml new file mode 100644 index 0000000000..a91da45578 --- /dev/null +++ b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorPreviewer.xaml @@ -0,0 +1,90 @@ + + + + + + + + + From ad5249992df8667f2785cf2504b2ddcc62a730c8 Mon Sep 17 00:00:00 2001 From: robloo Date: Tue, 26 Apr 2022 18:49:24 -0400 Subject: [PATCH 214/416] Add initial ColorSlider primitive --- .../ColorComponent.cs | 28 ++ .../{ColorSpectrum => }/ColorHelpers.cs | 344 +++++++++++++++++- .../ColorModel.cs | 18 + .../ColorSlider/ColorSlider.Properties.cs | 143 ++++++++ .../ColorSlider/ColorSlider.cs | 221 +++++++++++ .../Themes/Fluent.xaml | 1 + .../Themes/Fluent/ColorSlider.xaml | 172 +++++++++ 7 files changed, 925 insertions(+), 2 deletions(-) create mode 100644 src/Avalonia.Controls.ColorPicker/ColorComponent.cs rename src/Avalonia.Controls.ColorPicker/{ColorSpectrum => }/ColorHelpers.cs (50%) create mode 100644 src/Avalonia.Controls.ColorPicker/ColorModel.cs create mode 100644 src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.Properties.cs create mode 100644 src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs create mode 100644 src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSlider.xaml diff --git a/src/Avalonia.Controls.ColorPicker/ColorComponent.cs b/src/Avalonia.Controls.ColorPicker/ColorComponent.cs new file mode 100644 index 0000000000..a0385c03b4 --- /dev/null +++ b/src/Avalonia.Controls.ColorPicker/ColorComponent.cs @@ -0,0 +1,28 @@ +namespace Avalonia.Controls +{ + /// + /// Defines a specific component within a color model. + /// + public enum ColorComponent + { + /// + /// Represents the alpha component. + /// + Alpha, + + /// + /// Represents the first color component which is Red when RGB or Hue when HSV. + /// + Component1, + + /// + /// Represents the second color component which is Green when RGB or Saturation when HSV. + /// + Component2, + + /// + /// Represents the third color component which is Blue when RGB or Value when HSV. + /// + Component3 + } +} diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorHelpers.cs b/src/Avalonia.Controls.ColorPicker/ColorHelpers.cs similarity index 50% rename from src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorHelpers.cs rename to src/Avalonia.Controls.ColorPicker/ColorHelpers.cs index b912d39aba..37c6f552d6 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorHelpers.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorHelpers.cs @@ -6,9 +6,12 @@ using System; using System.Collections.Generic; using System.Runtime.InteropServices; +using System.Threading.Tasks; +using Avalonia.Layout; using Avalonia.Media; using Avalonia.Media.Imaging; using Avalonia.Platform; +using Avalonia.Utilities; namespace Avalonia.Controls.Primitives { @@ -26,6 +29,291 @@ namespace Avalonia.Controls.Primitives return string.Empty; } + /// + /// Generates a new bitmap of the specified size by changing a specific color component. + /// This will produce a gradient representing a sweep of all possible values of the color component. + /// + /// The pixel width (X, horizontal) of the resulting bitmap. + /// The pixel height (Y, vertical) of the resulting bitmap. + /// The orientation of the resulting bitmap (gradient direction). + /// The color model being used: RGBA or HSVA. + /// The specific color component to sweep. + /// The base HSV color used for components not being changed. + /// Fix the alpha component value to maximum during calculation. + /// This will remove any alpha/transparency from the other component backgrounds. + /// Fix the saturation and value components to maximum + /// during calculation with the HSVA color model. + /// This will ensure colors are always discernible regardless of saturation/value. + /// A new bitmap representing a gradient of color component values. + internal static async Task CreateComponentBitmapAsync( + int width, + int height, + Orientation orientation, + ColorModel colorModel, + ColorComponent component, + HsvColor baseHsvColor, + bool isAlphaMaxForced, + bool isSaturationValueMaxForced) + { + if (width == 0 || height == 0) + { + return new byte[0]; + } + + var bitmap = await Task.Run(() => + { + int pixelDataIndex = 0; + double componentStep; + byte[] bgraPixelData; + Color baseRgbColor = Colors.White; + Color rgbColor; + int bgraPixelDataHeight; + int bgraPixelDataWidth; + + // Allocate the buffer + // BGRA formatted color components 1 byte each (4 bytes in a pixel) + bgraPixelData = new byte[width * height * 4]; + bgraPixelDataHeight = height * 4; + bgraPixelDataWidth = width * 4; + + // Maximize alpha component value + if (isAlphaMaxForced && + component != ColorComponent.Alpha) + { + baseHsvColor = new HsvColor(1.0, baseHsvColor.H, baseHsvColor.S, baseHsvColor.V); + } + + // Convert HSV to RGB once + if (colorModel == ColorModel.Rgba) + { + baseRgbColor = baseHsvColor.ToRgb(); + } + + // Maximize Saturation and Value components when in HSVA mode + if (isSaturationValueMaxForced && + colorModel == ColorModel.Hsva && + component != ColorComponent.Alpha) + { + switch (component) + { + case ColorComponent.Component1: + baseHsvColor = new HsvColor(baseHsvColor.A, baseHsvColor.H, 1.0, 1.0); + break; + case ColorComponent.Component2: + baseHsvColor = new HsvColor(baseHsvColor.A, baseHsvColor.H, baseHsvColor.S, 1.0); + break; + case ColorComponent.Component3: + baseHsvColor = new HsvColor(baseHsvColor.A, baseHsvColor.H, 1.0, baseHsvColor.V); + break; + } + } + + // Create the color component gradient + if (orientation == Orientation.Horizontal) + { + // Determine the numerical increment of the color steps within the component + if (colorModel == ColorModel.Hsva) + { + if (component == ColorComponent.Component1) + { + componentStep = 360.0 / width; + } + else + { + componentStep = 1.0 / width; + } + } + else + { + componentStep = 255.0 / width; + } + + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + if (y == 0) + { + rgbColor = GetColor(x * componentStep); + + // Get a new color + bgraPixelData[pixelDataIndex + 0] = Convert.ToByte(rgbColor.B * rgbColor.A / 255); + bgraPixelData[pixelDataIndex + 1] = Convert.ToByte(rgbColor.G * rgbColor.A / 255); + bgraPixelData[pixelDataIndex + 2] = Convert.ToByte(rgbColor.R * rgbColor.A / 255); + bgraPixelData[pixelDataIndex + 3] = rgbColor.A; + } + else + { + // Use the color in the row above + // Remember the pixel data is 1 dimensional instead of 2 + bgraPixelData[pixelDataIndex + 0] = bgraPixelData[pixelDataIndex + 0 - bgraPixelDataWidth]; + bgraPixelData[pixelDataIndex + 1] = bgraPixelData[pixelDataIndex + 1 - bgraPixelDataWidth]; + bgraPixelData[pixelDataIndex + 2] = bgraPixelData[pixelDataIndex + 2 - bgraPixelDataWidth]; + bgraPixelData[pixelDataIndex + 3] = bgraPixelData[pixelDataIndex + 3 - bgraPixelDataWidth]; + } + + pixelDataIndex += 4; + } + } + } + else + { + // Determine the numerical increment of the color steps within the component + if (colorModel == ColorModel.Hsva) + { + if (component == ColorComponent.Component1) + { + componentStep = 360.0 / height; + } + else + { + componentStep = 1.0 / height; + } + } + else + { + componentStep = 255.0 / height; + } + + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + if (x == 0) + { + // The lowest component value should be at the 'bottom' of the bitmap + rgbColor = GetColor((height - 1 - y) * componentStep); + + // Get a new color + bgraPixelData[pixelDataIndex + 0] = Convert.ToByte(rgbColor.B * rgbColor.A / 255); + bgraPixelData[pixelDataIndex + 1] = Convert.ToByte(rgbColor.G * rgbColor.A / 255); + bgraPixelData[pixelDataIndex + 2] = Convert.ToByte(rgbColor.R * rgbColor.A / 255); + bgraPixelData[pixelDataIndex + 3] = rgbColor.A; + } + else + { + // Use the color in the column to the left + // Remember the pixel data is 1 dimensional instead of 2 + bgraPixelData[pixelDataIndex + 0] = bgraPixelData[pixelDataIndex - 4]; + bgraPixelData[pixelDataIndex + 1] = bgraPixelData[pixelDataIndex - 3]; + bgraPixelData[pixelDataIndex + 2] = bgraPixelData[pixelDataIndex - 2]; + bgraPixelData[pixelDataIndex + 3] = bgraPixelData[pixelDataIndex - 1]; + } + + pixelDataIndex += 4; + } + } + } + + Color GetColor(double componentValue) + { + Color newRgbColor = Colors.White; + + switch (component) + { + case ColorComponent.Component1: + { + if (colorModel == ColorModel.Hsva) + { + // Sweep hue + newRgbColor = HsvColor.ToRgb( + MathUtilities.Clamp(componentValue, 0.0, 360.0), + baseHsvColor.S, + baseHsvColor.V, + baseHsvColor.A); + } + else + { + // Sweep red + newRgbColor = new Color( + baseRgbColor.A, + Convert.ToByte(MathUtilities.Clamp(componentValue, 0.0, 255.0)), + baseRgbColor.G, + baseRgbColor.B); + } + + break; + } + case ColorComponent.Component2: + { + if (colorModel == ColorModel.Hsva) + { + // Sweep saturation + newRgbColor = HsvColor.ToRgb( + baseHsvColor.H, + MathUtilities.Clamp(componentValue, 0.0, 1.0), + baseHsvColor.V, + baseHsvColor.A); + } + else + { + // Sweep green + newRgbColor = new Color( + baseRgbColor.A, + baseRgbColor.R, + Convert.ToByte(MathUtilities.Clamp(componentValue, 0.0, 255.0)), + baseRgbColor.B); + } + + break; + } + case ColorComponent.Component3: + { + if (colorModel == ColorModel.Hsva) + { + // Sweep value + newRgbColor = HsvColor.ToRgb( + baseHsvColor.H, + baseHsvColor.S, + MathUtilities.Clamp(componentValue, 0.0, 1.0), + baseHsvColor.A); + } + else + { + // Sweep blue + newRgbColor = new Color( + baseRgbColor.A, + baseRgbColor.R, + baseRgbColor.G, + Convert.ToByte(MathUtilities.Clamp(componentValue, 0.0, 255.0))); + } + + break; + } + case ColorComponent.Alpha: + { + if (colorModel == ColorModel.Hsva) + { + // Sweep alpha + newRgbColor = HsvColor.ToRgb( + baseHsvColor.H, + baseHsvColor.S, + baseHsvColor.V, + MathUtilities.Clamp(componentValue, 0.0, 1.0)); + } + else + { + // Sweep alpha + newRgbColor = new Color( + Convert.ToByte(MathUtilities.Clamp(componentValue, 0.0, 255.0)), + baseRgbColor.R, + baseRgbColor.G, + baseRgbColor.B); + } + + break; + } + } + + return newRgbColor; + } + + return bgraPixelData; + }); + + return bitmap; + } + public static Hsv IncrementColorComponent( Hsv originalHsv, HsvComponent component, @@ -363,14 +651,22 @@ namespace Avalonia.Controls.Primitives return originalAlpha / 100; } + /// + /// + /// + /// The pixel width of the bitmap. + /// The pixel height of the bitmap. + /// + /// public static WriteableBitmap CreateBitmapFromPixelData( int pixelWidth, int pixelHeight, List bgraPixelData) { - Vector dpi = new Vector(96, 96); // Standard may need to change on some devices + // Standard may need to change on some devices + Vector dpi = new Vector(96, 96); - WriteableBitmap bitmap = new WriteableBitmap( + var bitmap = new WriteableBitmap( new PixelSize(pixelWidth, pixelHeight), dpi, PixelFormat.Bgra8888, @@ -385,6 +681,50 @@ namespace Avalonia.Controls.Primitives return bitmap; } + /// + /// Converts the given bitmap (in raw BGRA pre-multiplied alpha pixels) into an image brush + /// that can be used in the UI. + /// + /// The bitmap (in raw BGRA pre-multiplied alpha pixels) + /// to convert to a brush. + /// The pixel width of the bitmap. + /// The pixel height of the bitmap. + /// A new . + public static IBrush? BitmapToBrushAsync( + byte[] bgraPixelData, + int pixelWidth, + int pixelHeight) + { + if (bgraPixelData.Length == 0 || + (pixelWidth == 0 && + pixelHeight == 0)) + { + return null; + } + + // Standard may need to change on some devices + Vector dpi = new Vector(96, 96); + + var bitmap = new WriteableBitmap( + new PixelSize(pixelWidth, pixelHeight), + dpi, + PixelFormat.Bgra8888, + AlphaFormat.Premul); + + // Warning: This is highly questionable + using (var frameBuffer = bitmap.Lock()) + { + Marshal.Copy(bgraPixelData, 0, frameBuffer.Address, bgraPixelData.Length); + } + + var brush = new ImageBrush(bitmap) + { + Stretch = Stretch.Fill + }; + + return brush; + } + /// /// Gets the relative (perceptual) luminance/brightness of the given color. /// 1 is closer to white while 0 is closer to black. diff --git a/src/Avalonia.Controls.ColorPicker/ColorModel.cs b/src/Avalonia.Controls.ColorPicker/ColorModel.cs new file mode 100644 index 0000000000..f11b514706 --- /dev/null +++ b/src/Avalonia.Controls.ColorPicker/ColorModel.cs @@ -0,0 +1,18 @@ +namespace Avalonia.Controls +{ + /// + /// Defines the model used to represent colors. + /// + public enum ColorModel + { + /// + /// Color is represented by hue, saturation, value and alpha components. + /// + Hsva, + + /// + /// Color is represented by red, green, blue and alpha components. + /// + Rgba + } +} diff --git a/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.Properties.cs b/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.Properties.cs new file mode 100644 index 0000000000..3aa3e3a789 --- /dev/null +++ b/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.Properties.cs @@ -0,0 +1,143 @@ +using Avalonia.Media; + +namespace Avalonia.Controls.Primitives +{ + /// + public partial class ColorSlider + { + /// + /// Defines the property. + /// + public static readonly StyledProperty ColorProperty = + AvaloniaProperty.Register( + nameof(Color), + Colors.White); + + /// + /// Gets or sets the currently selected color in the RGB color model. + /// + /// + /// Use this property instead of when in + /// to avoid loss of precision and color drifting. + /// + public Color Color + { + get => GetValue(ColorProperty); + set => SetValue(ColorProperty, value); + } + + /// + /// Defines the property. + /// + public static readonly StyledProperty ColorComponentProperty = + AvaloniaProperty.Register( + nameof(ColorComponent), + ColorComponent.Component1); + + /// + /// Gets or sets the color component represented by the slider. + /// + public ColorComponent ColorComponent + { + get => GetValue(ColorComponentProperty); + set => SetValue(ColorComponentProperty, value); + } + + /// + /// Defines the property. + /// + public static readonly StyledProperty ColorModelProperty = + AvaloniaProperty.Register( + nameof(ColorModel), + ColorModel.Rgba); + + /// + /// Gets or sets the active color model used by the slider. + /// + public ColorModel ColorModel + { + get => GetValue(ColorModelProperty); + set => SetValue(ColorModelProperty, value); + } + + /// + /// Defines the property. + /// + public static readonly StyledProperty HsvColorProperty = + AvaloniaProperty.Register( + nameof(HsvColor), + Colors.White.ToHsv()); + + /// + /// Gets or sets the currently selected color in the HSV color model. + /// + /// + /// Use this property instead of when in + /// to avoid loss of precision and color drifting. + /// + public HsvColor HsvColor + { + get => GetValue(HsvColorProperty); + set => SetValue(HsvColorProperty, value); + } + + /// + /// Defines the property. + /// + public static readonly StyledProperty IsAlphaMaxForcedProperty = + AvaloniaProperty.Register( + nameof(IsAlphaMaxForced), + true); + + /// + /// Gets or sets a value indicating whether the alpha component is always forced to maximum for components + /// other than . + /// This ensures that the background is always visible and never transparent regardless of the actual color. + /// + public bool IsAlphaMaxForced + { + get => GetValue(IsAlphaMaxForcedProperty); + set => SetValue(IsAlphaMaxForcedProperty, value); + } + + /// + /// Defines the property. + /// + public static readonly StyledProperty IsAutoUpdatingEnabledProperty = + AvaloniaProperty.Register( + nameof(IsAutoUpdatingEnabled), + true); + + /// + /// Gets or sets a value indicating whether automatic background and foreground updates will be + /// calculated when the set color changes. + /// + /// + /// This can be disabled for performance reasons when working with multiple sliders. + /// + public bool IsAutoUpdatingEnabled + { + get => GetValue(IsAutoUpdatingEnabledProperty); + set => SetValue(IsAutoUpdatingEnabledProperty, value); + } + + /// + /// Defines the property. + /// + public static readonly StyledProperty IsSaturationValueMaxForcedProperty = + AvaloniaProperty.Register( + nameof(IsSaturationValueMaxForced), + true); + + /// + /// Gets or sets a value indicating whether the saturation and value components are always forced to maximum values + /// when using the HSVA color model. Only component values other than will be changed. + /// This ensures, for example, that the Hue background is always visible and never washed out regardless of the actual color. + /// + public bool IsSaturationValueMaxForced + { + get => GetValue(IsSaturationValueMaxForcedProperty); + set => SetValue(IsSaturationValueMaxForcedProperty, value); + } + } +} diff --git a/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs b/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs new file mode 100644 index 0000000000..e3f2dc3555 --- /dev/null +++ b/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs @@ -0,0 +1,221 @@ +using System; +using Avalonia.Media; +using Avalonia.Utilities; + +namespace Avalonia.Controls.Primitives +{ + /// + /// A slider with a background that represents a single color component. + /// + public partial class ColorSlider : Slider + { + private Size cachedSize = Size.Empty; + + /// + /// Initializes a new instance of the class. + /// + public ColorSlider() : base() + { + } + + /// + /// Update the slider's Foreground and Background brushes based on the current slider state and color. + /// + /// + /// Manually refreshes the background gradient of the slider. + /// This is callable separately for performance reasons. + /// + public void UpdateColors() + { + HsvColor hsvColor = HsvColor; + + // Calculate and set the background + UpdateBackground(hsvColor); + + // Calculate and set the foreground ensuring contrast with the background + Color rgbColor = hsvColor.ToRgb(); + Color selectedRgbColor; + double sliderPercent = Value / (Maximum - Minimum); + + var component = ColorComponent; + + if (ColorModel == ColorModel.Hsva) + { + if (IsAlphaMaxForced && + component != ColorComponent.Alpha) + { + hsvColor = new HsvColor(1.0, hsvColor.H, hsvColor.S, hsvColor.V); + } + + switch (component) + { + case ColorComponent.Component1: + { + var componentValue = MathUtilities.Clamp(sliderPercent * 360.0, 0.0, 360.0); + + hsvColor = new HsvColor( + hsvColor.A, + componentValue, + IsSaturationValueMaxForced ? 1.0 : hsvColor.S, + IsSaturationValueMaxForced ? 1.0 : hsvColor.V); + + break; + } + + case ColorComponent.Component2: + { + var componentValue = MathUtilities.Clamp(sliderPercent * 1.0, 0.0, 1.0); + + hsvColor = new HsvColor( + hsvColor.A, + hsvColor.H, + componentValue, + IsSaturationValueMaxForced ? 1.0 : hsvColor.V); + + break; + } + + case ColorComponent.Component3: + { + var componentValue = MathUtilities.Clamp(sliderPercent * 1.0, 0.0, 1.0); + + hsvColor = new HsvColor( + hsvColor.A, + hsvColor.H, + IsSaturationValueMaxForced ? 1.0 : hsvColor.S, + componentValue); + + break; + } + } + + selectedRgbColor = hsvColor.ToRgb(); + } + else + { + if (IsAlphaMaxForced && + component != ColorComponent.Alpha) + { + rgbColor = new Color(255, rgbColor.R, rgbColor.G, rgbColor.B); + } + + byte componentValue = Convert.ToByte(MathUtilities.Clamp(sliderPercent * 255, 0, 255)); + + switch (component) + { + case ColorComponent.Component1: + rgbColor = new Color(rgbColor.A, componentValue, rgbColor.G, rgbColor.B); + break; + case ColorComponent.Component2: + rgbColor = new Color(rgbColor.A, rgbColor.R, componentValue, rgbColor.B); + break; + case ColorComponent.Component3: + rgbColor = new Color(rgbColor.A, rgbColor.R, rgbColor.G, componentValue); + break; + } + + selectedRgbColor = rgbColor; + } + + //var converter = new ContrastBrushConverter(); + //this.Foreground = converter.Convert(selectedRgbColor, typeof(Brush), this.DefaultForeground, null) as Brush; + + return; + } + + /// + /// Generates a new background image for the color slider and applies it. + /// + private async void UpdateBackground(HsvColor color) + { + // Updates may be requested when sliders are not in the visual tree. + // For first-time load this is handled by the Loaded event. + // However, after that problems may arise, consider the following case: + // + // (1) Backgrounds are drawn normally the first time on Loaded. + // Actual height/width are available. + // (2) The palette tab is selected which has no sliders + // (3) The picker flyout is closed + // (4) Externally the color is changed + // The color change will trigger slider background updates but + // with the flyout closed, actual height/width are zero. + // No zero size bitmap can be generated. + // (5) The picker flyout is re-opened by the user and the default + // last-opened tab will be viewed: palette. + // No loaded events will be fired for sliders. The color change + // event was already handled in (4). The sliders will never + // be updated. + // + // In this case the sliders become out of sync with the Color because there is no way + // to tell when they actually come into view. To work around this, force a re-render of + // the background with the last size of the slider. This last size will be when it was + // last loaded or updated. + // + // In the future additional consideration may be required for SizeChanged of the control. + // This work-around will also cause issues if display scaling changes in the special + // case where cached sizes are required. + + var width = Convert.ToInt32(Bounds.Width); + var height = Convert.ToInt32(Bounds.Height); + + if (width == 0 || height == 0) + { + // Attempt to use the last size if it was available + if (cachedSize.IsDefault == false) + { + width = Convert.ToInt32(cachedSize.Width); + height = Convert.ToInt32(cachedSize.Height); + } + } + else + { + cachedSize = new Size(width, height); + } + + var bitmap = await ColorHelpers.CreateComponentBitmapAsync( + width, + height, + Orientation, + ColorModel, + ColorComponent, + color, + IsAlphaMaxForced, + IsSaturationValueMaxForced); + + if (bitmap != null) + { + Background = ColorHelpers.BitmapToBrushAsync(bitmap, width, height); + } + + return; + } + + /// + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + bool update = false; + + if (change.Property == ColorProperty) + { + // Sync with HSV (which is primary) + HsvColor = Color.ToHsv(); + update = true; + } + else if (change.Property == HsvColorProperty) + { + update = true; + } + else if (change.Property == BoundsProperty) + { + update = true; + } + + if (update && IsAutoUpdatingEnabled) + { + UpdateColors(); + } + + base.OnPropertyChanged(change); + } + } +} diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml index d96066e56b..c25d79727f 100644 --- a/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml @@ -22,6 +22,7 @@ + diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSlider.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSlider.xaml new file mode 100644 index 0000000000..1ca9b12ffe --- /dev/null +++ b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSlider.xaml @@ -0,0 +1,172 @@ + + + + + + + + + + + + + + + + + + + + + From a5b3e85dfc519bee6a762e2d5c45869002e0d135 Mon Sep 17 00:00:00 2001 From: robloo Date: Tue, 26 Apr 2022 18:50:00 -0400 Subject: [PATCH 215/416] Simplify default color property values --- .../ColorSpectrum/ColorSpectrum.Properties.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs index 824bf9ab05..ab5b83afcb 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs @@ -29,7 +29,7 @@ namespace Avalonia.Controls.Primitives public static readonly StyledProperty ColorProperty = AvaloniaProperty.Register( nameof(Color), - Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF)); + Colors.White); /// /// Gets or sets the two HSV color components displayed by the spectrum. @@ -71,7 +71,7 @@ namespace Avalonia.Controls.Primitives public static readonly StyledProperty HsvColorProperty = AvaloniaProperty.Register( nameof(HsvColor), - new HsvColor(1, 0, 0, 1)); + Colors.White.ToHsv()); /// /// Gets or sets the maximum value of the Hue component in the range from 0..359. From e9cb628f820ba7226cce379a601b9867053eef76 Mon Sep 17 00:00:00 2001 From: robloo Date: Tue, 26 Apr 2022 18:50:22 -0400 Subject: [PATCH 216/416] Improve formatting --- .../Converters/CornerRadiusFilterConverter.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/Converters/CornerRadiusFilterConverter.cs b/src/Avalonia.Controls/Converters/CornerRadiusFilterConverter.cs index b2433bfd97..a91f143019 100644 --- a/src/Avalonia.Controls/Converters/CornerRadiusFilterConverter.cs +++ b/src/Avalonia.Controls/Converters/CornerRadiusFilterConverter.cs @@ -1,6 +1,5 @@ using System; using System.Globalization; - using Avalonia.Data.Converters; namespace Avalonia.Controls.Converters @@ -22,7 +21,12 @@ namespace Avalonia.Controls.Converters /// public double Scale { get; set; } = 1; - public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) + /// + public object? Convert( + object? value, + Type targetType, + object? parameter, + CultureInfo culture) { if (!(value is CornerRadius radius)) { @@ -36,7 +40,12 @@ namespace Avalonia.Controls.Converters Filter.HasAllFlags(Corners.BottomLeft) ? radius.BottomLeft * Scale : 0); } - public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) + /// + public object? ConvertBack( + object? value, + Type targetType, + object? parameter, + CultureInfo culture) { throw new NotImplementedException(); } From c068adb60944b8e6a2ba1bfc1a3666c8e0caf1fd Mon Sep 17 00:00:00 2001 From: robloo Date: Tue, 26 Apr 2022 18:51:01 -0400 Subject: [PATCH 217/416] Update ColorPickerPage in ControlCatalog with ColorSlider and ColorPreviewer --- .../ControlCatalog/Pages/ColorPickerPage.xaml | 37 +++++++++++++++---- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/samples/ControlCatalog/Pages/ColorPickerPage.xaml b/samples/ControlCatalog/Pages/ColorPickerPage.xaml index ec34193f8c..f343fd8f59 100644 --- a/samples/ControlCatalog/Pages/ColorPickerPage.xaml +++ b/samples/ControlCatalog/Pages/ColorPickerPage.xaml @@ -3,14 +3,18 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:primitives="clr-namespace:Avalonia.Controls.Primitives;assembly=Avalonia.Controls" - mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" + mc:Ignorable="d" + d:DesignWidth="800" + d:DesignHeight="450" x:Class="ControlCatalog.Pages.ColorPickerPage"> - - + + + + + + + From b6b8e96fd97a5d77d7e26ab2e189ab9d84849e48 Mon Sep 17 00:00:00 2001 From: robloo Date: Tue, 26 Apr 2022 18:56:49 -0400 Subject: [PATCH 218/416] Move spectrum enums into ColorSpectrum directory --- .../{ => ColorSpectrum}/ColorSpectrumComponents.cs | 0 .../{ => ColorSpectrum}/ColorSpectrumShape.cs | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/Avalonia.Controls.ColorPicker/{ => ColorSpectrum}/ColorSpectrumComponents.cs (100%) rename src/Avalonia.Controls.ColorPicker/{ => ColorSpectrum}/ColorSpectrumShape.cs (100%) diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrumComponents.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrumComponents.cs similarity index 100% rename from src/Avalonia.Controls.ColorPicker/ColorSpectrumComponents.cs rename to src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrumComponents.cs diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrumShape.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrumShape.cs similarity index 100% rename from src/Avalonia.Controls.ColorPicker/ColorSpectrumShape.cs rename to src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrumShape.cs From 1f5e3c0d9dcfa2f95ad2d217521f075d33364d5c Mon Sep 17 00:00:00 2001 From: robloo Date: Tue, 26 Apr 2022 19:28:21 -0400 Subject: [PATCH 219/416] Move helpers into separate directory --- src/Avalonia.Controls.ColorPicker/{ => Helpers}/ColorHelpers.cs | 0 .../{ColorSpectrum => Helpers}/Hsv.cs | 0 .../{ColorSpectrum => Helpers}/IncrementAmount.cs | 0 .../{ColorSpectrum => Helpers}/IncrementDirection.cs | 0 .../{ColorSpectrum => Helpers}/Rgb.cs | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename src/Avalonia.Controls.ColorPicker/{ => Helpers}/ColorHelpers.cs (100%) rename src/Avalonia.Controls.ColorPicker/{ColorSpectrum => Helpers}/Hsv.cs (100%) rename src/Avalonia.Controls.ColorPicker/{ColorSpectrum => Helpers}/IncrementAmount.cs (100%) rename src/Avalonia.Controls.ColorPicker/{ColorSpectrum => Helpers}/IncrementDirection.cs (100%) rename src/Avalonia.Controls.ColorPicker/{ColorSpectrum => Helpers}/Rgb.cs (100%) diff --git a/src/Avalonia.Controls.ColorPicker/ColorHelpers.cs b/src/Avalonia.Controls.ColorPicker/Helpers/ColorHelpers.cs similarity index 100% rename from src/Avalonia.Controls.ColorPicker/ColorHelpers.cs rename to src/Avalonia.Controls.ColorPicker/Helpers/ColorHelpers.cs diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/Hsv.cs b/src/Avalonia.Controls.ColorPicker/Helpers/Hsv.cs similarity index 100% rename from src/Avalonia.Controls.ColorPicker/ColorSpectrum/Hsv.cs rename to src/Avalonia.Controls.ColorPicker/Helpers/Hsv.cs diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/IncrementAmount.cs b/src/Avalonia.Controls.ColorPicker/Helpers/IncrementAmount.cs similarity index 100% rename from src/Avalonia.Controls.ColorPicker/ColorSpectrum/IncrementAmount.cs rename to src/Avalonia.Controls.ColorPicker/Helpers/IncrementAmount.cs diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/IncrementDirection.cs b/src/Avalonia.Controls.ColorPicker/Helpers/IncrementDirection.cs similarity index 100% rename from src/Avalonia.Controls.ColorPicker/ColorSpectrum/IncrementDirection.cs rename to src/Avalonia.Controls.ColorPicker/Helpers/IncrementDirection.cs diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/Rgb.cs b/src/Avalonia.Controls.ColorPicker/Helpers/Rgb.cs similarity index 100% rename from src/Avalonia.Controls.ColorPicker/ColorSpectrum/Rgb.cs rename to src/Avalonia.Controls.ColorPicker/Helpers/Rgb.cs From 65e5e580acd359c0103e7b9bb3b0397d7676f1e1 Mon Sep 17 00:00:00 2001 From: robloo Date: Tue, 26 Apr 2022 21:57:19 -0400 Subject: [PATCH 220/416] Make ColorSlider fully functional --- .../ControlCatalog/Pages/ColorPickerPage.xaml | 3 +- .../ColorSlider/ColorSlider.cs | 367 ++++++++++++------ .../Helpers/ColorHelpers.cs | 2 +- 3 files changed, 241 insertions(+), 131 deletions(-) diff --git a/samples/ControlCatalog/Pages/ColorPickerPage.xaml b/samples/ControlCatalog/Pages/ColorPickerPage.xaml index f343fd8f59..09ec15ad15 100644 --- a/samples/ControlCatalog/Pages/ColorPickerPage.xaml +++ b/samples/ControlCatalog/Pages/ColorPickerPage.xaml @@ -25,7 +25,8 @@ Width="256" /> + RowDefinitions="Auto,Auto,Auto,Auto,Auto" + Margin="0,10,0,0"> public partial class ColorSlider : Slider { - private Size cachedSize = Size.Empty; + private bool disableUpdates = false; /// /// Initializes a new instance of the class. @@ -19,200 +20,308 @@ namespace Avalonia.Controls.Primitives } /// - /// Update the slider's Foreground and Background brushes based on the current slider state and color. + /// Generates a new background image for the color slider and applies it. /// - /// - /// Manually refreshes the background gradient of the slider. - /// This is callable separately for performance reasons. - /// - public void UpdateColors() + private async void UpdateBackground() { - HsvColor hsvColor = HsvColor; + // In Avalonia, Bounds returns the actual device-independent pixel size of a control. + // However, this is not necessarily the size of the control rendered on a display. + // A desktop or application scaling factor may be applied which must be accounted for here. + // Remember bitmaps in Avalonia are rendered mapping to actual device pixels, not the device- + // independent pixels of controls. - // Calculate and set the background - UpdateBackground(hsvColor); + var scale = LayoutHelper.GetLayoutScale(this); + var pixelWidth = Convert.ToInt32(Bounds.Width * scale); + var pixelHeight = Convert.ToInt32(Bounds.Height * scale); - // Calculate and set the foreground ensuring contrast with the background - Color rgbColor = hsvColor.ToRgb(); - Color selectedRgbColor; - double sliderPercent = Value / (Maximum - Minimum); + if (pixelWidth != 0 && pixelHeight != 0) + { + var bitmap = await ColorHelpers.CreateComponentBitmapAsync( + pixelWidth, + pixelHeight, + Orientation, + ColorModel, + ColorComponent, + HsvColor, + IsAlphaMaxForced, + IsSaturationValueMaxForced); + + if (bitmap != null) + { + Background = ColorHelpers.BitmapToBrushAsync(bitmap, pixelWidth, pixelHeight); + } + } + + return; + } + /// + /// Updates the slider property values by applying the current color. + /// + /// + /// Warning: This will trigger property changed updates. + /// Consider using externally. + /// + private void SetColorToSliderValues() + { + var hsvColor = HsvColor; + var rgbColor = Color; var component = ColorComponent; if (ColorModel == ColorModel.Hsva) { - if (IsAlphaMaxForced && - component != ColorComponent.Alpha) + // Note: Components converted into a usable range for the user + switch (component) { - hsvColor = new HsvColor(1.0, hsvColor.H, hsvColor.S, hsvColor.V); + case ColorComponent.Alpha: + Minimum = 0; + Maximum = 100; + Value = hsvColor.A * 100; + break; + case ColorComponent.Component1: // Hue + Minimum = 0; + Maximum = 359; + Value = hsvColor.H; + break; + case ColorComponent.Component2: // Saturation + Minimum = 0; + Maximum = 100; + Value = hsvColor.S * 100; + break; + case ColorComponent.Component3: // Value + Minimum = 0; + Maximum = 100; + Value = hsvColor.V * 100; + break; } - + } + else + { switch (component) { - case ColorComponent.Component1: - { - var componentValue = MathUtilities.Clamp(sliderPercent * 360.0, 0.0, 360.0); - - hsvColor = new HsvColor( - hsvColor.A, - componentValue, - IsSaturationValueMaxForced ? 1.0 : hsvColor.S, - IsSaturationValueMaxForced ? 1.0 : hsvColor.V); - - break; - } + case ColorComponent.Alpha: + Minimum = 0; + Maximum = 255; + Value = Convert.ToDouble(rgbColor.A); + break; + case ColorComponent.Component1: // Red + Minimum = 0; + Maximum = 255; + Value = Convert.ToDouble(rgbColor.R); + break; + case ColorComponent.Component2: // Green + Minimum = 0; + Maximum = 255; + Value = Convert.ToDouble(rgbColor.G); + break; + case ColorComponent.Component3: // Blue + Minimum = 0; + Maximum = 255; + Value = Convert.ToDouble(rgbColor.B); + break; + } + } - case ColorComponent.Component2: - { - var componentValue = MathUtilities.Clamp(sliderPercent * 1.0, 0.0, 1.0); + return; + } - hsvColor = new HsvColor( - hsvColor.A, - hsvColor.H, - componentValue, - IsSaturationValueMaxForced ? 1.0 : hsvColor.V); + /// + /// Gets the current color determined by the slider values. + /// + private (Color, HsvColor) GetColorFromSliderValues() + { + HsvColor hsvColor = new HsvColor(); + Color rgbColor = new Color(); + double sliderPercent = Value / (Maximum - Minimum); - break; - } + var baseHsvColor = HsvColor; + var baseRgbColor = Color; + var component = ColorComponent; + if (ColorModel == ColorModel.Hsva) + { + switch (component) + { + case ColorComponent.Alpha: + { + var componentValue = MathUtilities.Clamp(sliderPercent * 1.0, 0.0, 1.0); + hsvColor = new HsvColor(componentValue, baseHsvColor.H, baseHsvColor.S, baseHsvColor.V); + break; + } + case ColorComponent.Component1: + { + var componentValue = MathUtilities.Clamp(sliderPercent * 360.0, 0.0, 360.0); + hsvColor = new HsvColor(baseHsvColor.A, componentValue, baseHsvColor.S, baseHsvColor.V); + break; + } + case ColorComponent.Component2: + { + var componentValue = MathUtilities.Clamp(sliderPercent * 1.0, 0.0, 1.0); + hsvColor = new HsvColor(baseHsvColor.A, baseHsvColor.H, componentValue, baseHsvColor.V); + break; + } case ColorComponent.Component3: - { - var componentValue = MathUtilities.Clamp(sliderPercent * 1.0, 0.0, 1.0); - - hsvColor = new HsvColor( - hsvColor.A, - hsvColor.H, - IsSaturationValueMaxForced ? 1.0 : hsvColor.S, - componentValue); - - break; - } + { + var componentValue = MathUtilities.Clamp(sliderPercent * 1.0, 0.0, 1.0); + hsvColor = new HsvColor(baseHsvColor.A, baseHsvColor.H, baseHsvColor.S, componentValue); + break; + } } - selectedRgbColor = hsvColor.ToRgb(); + return (hsvColor.ToRgb(), hsvColor); } else { - if (IsAlphaMaxForced && - component != ColorComponent.Alpha) - { - rgbColor = new Color(255, rgbColor.R, rgbColor.G, rgbColor.B); - } - byte componentValue = Convert.ToByte(MathUtilities.Clamp(sliderPercent * 255, 0, 255)); switch (component) { + case ColorComponent.Alpha: + rgbColor = new Color(componentValue, baseRgbColor.R, baseRgbColor.G, baseRgbColor.B); + break; case ColorComponent.Component1: - rgbColor = new Color(rgbColor.A, componentValue, rgbColor.G, rgbColor.B); + rgbColor = new Color(baseRgbColor.A, componentValue, baseRgbColor.G, baseRgbColor.B); break; case ColorComponent.Component2: - rgbColor = new Color(rgbColor.A, rgbColor.R, componentValue, rgbColor.B); + rgbColor = new Color(baseRgbColor.A, baseRgbColor.R, componentValue, baseRgbColor.B); break; case ColorComponent.Component3: - rgbColor = new Color(rgbColor.A, rgbColor.R, rgbColor.G, componentValue); + rgbColor = new Color(baseRgbColor.A, baseRgbColor.R, baseRgbColor.G, componentValue); break; } - selectedRgbColor = rgbColor; + return (rgbColor, rgbColor.ToHsv()); } - - //var converter = new ContrastBrushConverter(); - //this.Foreground = converter.Convert(selectedRgbColor, typeof(Brush), this.DefaultForeground, null) as Brush; - - return; } /// - /// Generates a new background image for the color slider and applies it. + /// Gets the actual background color displayed for the given HSV color. + /// This can differ due to the effects of certain properties intended to improve perception. /// - private async void UpdateBackground(HsvColor color) + /// The actual color to get the equivalent background color for. + /// The equivalent, perceived background color. + private HsvColor GetEquivalentBackgroundColor(HsvColor hsvColor) { - // Updates may be requested when sliders are not in the visual tree. - // For first-time load this is handled by the Loaded event. - // However, after that problems may arise, consider the following case: - // - // (1) Backgrounds are drawn normally the first time on Loaded. - // Actual height/width are available. - // (2) The palette tab is selected which has no sliders - // (3) The picker flyout is closed - // (4) Externally the color is changed - // The color change will trigger slider background updates but - // with the flyout closed, actual height/width are zero. - // No zero size bitmap can be generated. - // (5) The picker flyout is re-opened by the user and the default - // last-opened tab will be viewed: palette. - // No loaded events will be fired for sliders. The color change - // event was already handled in (4). The sliders will never - // be updated. - // - // In this case the sliders become out of sync with the Color because there is no way - // to tell when they actually come into view. To work around this, force a re-render of - // the background with the last size of the slider. This last size will be when it was - // last loaded or updated. - // - // In the future additional consideration may be required for SizeChanged of the control. - // This work-around will also cause issues if display scaling changes in the special - // case where cached sizes are required. - - var width = Convert.ToInt32(Bounds.Width); - var height = Convert.ToInt32(Bounds.Height); - - if (width == 0 || height == 0) + var component = ColorComponent; + var isAlphaMaxForced = IsAlphaMaxForced; + var isSaturationValueMaxForced = IsSaturationValueMaxForced; + + if (isAlphaMaxForced && + component != ColorComponent.Alpha) { - // Attempt to use the last size if it was available - if (cachedSize.IsDefault == false) - { - width = Convert.ToInt32(cachedSize.Width); - height = Convert.ToInt32(cachedSize.Height); - } + hsvColor = new HsvColor(1.0, hsvColor.H, hsvColor.S, hsvColor.V); } - else + + switch (component) { - cachedSize = new Size(width, height); + case ColorComponent.Component1: + return new HsvColor( + hsvColor.A, + hsvColor.H, + isSaturationValueMaxForced ? 1.0 : hsvColor.S, + isSaturationValueMaxForced ? 1.0 : hsvColor.V); + case ColorComponent.Component2: + return new HsvColor( + hsvColor.A, + hsvColor.H, + hsvColor.S, + isSaturationValueMaxForced ? 1.0 : hsvColor.V); + case ColorComponent.Component3: + return new HsvColor( + hsvColor.A, + hsvColor.H, + isSaturationValueMaxForced ? 1.0 : hsvColor.S, + hsvColor.V); + default: + return hsvColor; } + } + + /// + /// Gets the actual background color displayed for the given RGB color. + /// This can differ due to the effects of certain properties intended to improve perception. + /// + /// The actual color to get the equivalent background color for. + /// The equivalent, perceived background color. + private Color GetEquivalentBackgroundColor(Color rgbColor) + { + var component = ColorComponent; + var isAlphaMaxForced = IsAlphaMaxForced; - var bitmap = await ColorHelpers.CreateComponentBitmapAsync( - width, - height, - Orientation, - ColorModel, - ColorComponent, - color, - IsAlphaMaxForced, - IsSaturationValueMaxForced); - - if (bitmap != null) + if (isAlphaMaxForced && + component != ColorComponent.Alpha) { - Background = ColorHelpers.BitmapToBrushAsync(bitmap, width, height); + rgbColor = new Color(255, rgbColor.R, rgbColor.G, rgbColor.B); } - return; + return rgbColor; } /// protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { - bool update = false; + if (disableUpdates) + { + base.OnPropertyChanged(change); + return; + } + // Always keep the two color properties in sync if (change.Property == ColorProperty) { - // Sync with HSV (which is primary) + disableUpdates = true; + HsvColor = Color.ToHsv(); - update = true; + + if (IsAutoUpdatingEnabled) + { + SetColorToSliderValues(); + UpdateBackground(); + } + + disableUpdates = false; } else if (change.Property == HsvColorProperty) { - update = true; + disableUpdates = true; + + Color = HsvColor.ToRgb(); + + if (IsAutoUpdatingEnabled) + { + SetColorToSliderValues(); + UpdateBackground(); + } + + disableUpdates = false; } else if (change.Property == BoundsProperty) { - update = true; + if (IsAutoUpdatingEnabled) + { + UpdateBackground(); + } } - - if (update && IsAutoUpdatingEnabled) + else if (change.Property == ValueProperty || + change.Property == MinimumProperty || + change.Property == MaximumProperty) { - UpdateColors(); + disableUpdates = true; + + (var color, var hsvColor) = GetColorFromSliderValues(); + + if (ColorModel == ColorModel.Hsva) + { + HsvColor = hsvColor; + Color = hsvColor.ToRgb(); + } + else + { + Color = color; + HsvColor = color.ToHsv(); + } + + disableUpdates = false; } base.OnPropertyChanged(change); diff --git a/src/Avalonia.Controls.ColorPicker/Helpers/ColorHelpers.cs b/src/Avalonia.Controls.ColorPicker/Helpers/ColorHelpers.cs index 37c6f552d6..6500d10fe9 100644 --- a/src/Avalonia.Controls.ColorPicker/Helpers/ColorHelpers.cs +++ b/src/Avalonia.Controls.ColorPicker/Helpers/ColorHelpers.cs @@ -719,7 +719,7 @@ namespace Avalonia.Controls.Primitives var brush = new ImageBrush(bitmap) { - Stretch = Stretch.Fill + Stretch = Stretch.None }; return brush; From aae4b6708d15343f5f962e0c7a7c63d626e44064 Mon Sep 17 00:00:00 2001 From: robloo Date: Tue, 26 Apr 2022 21:57:55 -0400 Subject: [PATCH 221/416] Remove Color property from ColorPreviewer --- .../ColorPreviewer.Properties.cs | 27 +++---------------- .../ColorPreviewer/ColorPreviewer.cs | 18 +------------ 2 files changed, 4 insertions(+), 41 deletions(-) diff --git a/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.Properties.cs b/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.Properties.cs index 74c0943919..903b5fb52b 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.Properties.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.Properties.cs @@ -5,27 +5,6 @@ namespace Avalonia.Controls.Primitives /// public partial class ColorPreviewer { - /// - /// Defines the property. - /// - public static readonly StyledProperty ColorProperty = - AvaloniaProperty.Register( - nameof(Color), - Colors.White); - - /// - /// Gets or sets the currently previewed color in the RGB color model. - /// - /// - /// For control authors use instead to avoid loss - /// of precision and color drifting. - /// - public Color Color - { - get => GetValue(ColorProperty); - set => SetValue(ColorProperty, value); - } - /// /// Defines the property. /// @@ -38,9 +17,9 @@ namespace Avalonia.Controls.Primitives /// Gets or sets the currently previewed color in the HSV color model. /// /// - /// This should be used in all cases instead of the property. - /// Internally, the uses the HSV color model and using - /// this property will avoid loss of precision and color drifting. + /// Only an HSV color is supported in this control to ensure there is never any + /// loss of precision or color information. Accent colors, like the color spectrum, + /// only operate with the HSV color model. /// public HsvColor HsvColor { diff --git a/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.cs b/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.cs index 35bd62601f..1c0dd2154a 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.cs @@ -17,7 +17,7 @@ namespace Avalonia.Controls.Primitives { /// /// Event for when the selected color changes within the previewer. - /// This happens when an accent color is pressed. + /// This occurs when an accent color is pressed. /// public event EventHandler? ColorChanged; @@ -82,22 +82,6 @@ namespace Avalonia.Controls.Primitives base.OnApplyTemplate(e); } - /// - protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) - { - // Always keep the two color properties in sync - if (change.Property == ColorProperty) - { - HsvColor = Color.ToHsv(); - } - else if (change.Property == HsvColorProperty) - { - Color = HsvColor.ToRgb(); - } - - base.OnPropertyChanged(change); - } - /// /// Called before the event occurs. /// From 357eddf5e933f4797e21d2235e19936c66878b21 Mon Sep 17 00:00:00 2001 From: robloo Date: Tue, 26 Apr 2022 22:52:00 -0400 Subject: [PATCH 222/416] Implement ColorSlider PseudoClasses --- .../ColorSlider/ColorSlider.cs | 66 ++++++++++++++++--- .../Themes/Fluent/ColorSlider.xaml | 20 ++++-- .../Themes/Fluent/ColorSpectrum.xaml | 3 + 3 files changed, 76 insertions(+), 13 deletions(-) diff --git a/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs b/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs index 61df36c806..9d9e9f393e 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs @@ -1,4 +1,5 @@ using System; +using Avalonia.Controls.Metadata; using Avalonia.Layout; using Avalonia.Media; using Avalonia.Utilities; @@ -8,8 +9,13 @@ namespace Avalonia.Controls.Primitives /// /// A slider with a background that represents a single color component. /// + [PseudoClasses(pcDarkSelector, pcLightSelector)] public partial class ColorSlider : Slider { + protected const string pcDarkSelector = ":dark-selector"; + protected const string pcLightSelector = ":light-selector"; + + private const double MaxHue = 359.99999999999999999; private bool disableUpdates = false; /// @@ -19,6 +25,49 @@ namespace Avalonia.Controls.Primitives { } + /// + /// Updates the visual state of the control by applying latest PseudoClasses. + /// + private void UpdatePseudoClasses() + { + // The slider itself can be transparent for certain color values. + // This causes an issue where a white selector thumb over a light window background or + // a black selector thumb over a dark window background is not visible. + // This means under a certain alpha threshold, neither a white or black selector thumb + // should be shown and instead the default slider thumb color should be used instead. + if (Color.A < 128 && + (IsAlphaMaxForced == false || + ColorComponent == ColorComponent.Alpha)) + { + PseudoClasses.Set(pcDarkSelector, false); + PseudoClasses.Set(pcLightSelector, false); + } + else + { + Color perceivedColor; + + if (ColorModel == ColorModel.Hsva) + { + perceivedColor = GetEquivalentBackgroundColor(HsvColor).ToRgb(); + } + else + { + perceivedColor = GetEquivalentBackgroundColor(Color); + } + + if (ColorHelpers.GetRelativeLuminance(perceivedColor) <= 0.5) + { + PseudoClasses.Set(pcDarkSelector, false); + PseudoClasses.Set(pcLightSelector, true); + } + else + { + PseudoClasses.Set(pcDarkSelector, true); + PseudoClasses.Set(pcLightSelector, false); + } + } + } + /// /// Generates a new background image for the color slider and applies it. /// @@ -80,7 +129,7 @@ namespace Avalonia.Controls.Primitives break; case ColorComponent.Component1: // Hue Minimum = 0; - Maximum = 359; + Maximum = MaxHue; Value = hsvColor.H; break; case ColorComponent.Component2: // Saturation @@ -144,26 +193,22 @@ namespace Avalonia.Controls.Primitives { case ColorComponent.Alpha: { - var componentValue = MathUtilities.Clamp(sliderPercent * 1.0, 0.0, 1.0); - hsvColor = new HsvColor(componentValue, baseHsvColor.H, baseHsvColor.S, baseHsvColor.V); + hsvColor = new HsvColor(sliderPercent, baseHsvColor.H, baseHsvColor.S, baseHsvColor.V); break; } case ColorComponent.Component1: { - var componentValue = MathUtilities.Clamp(sliderPercent * 360.0, 0.0, 360.0); - hsvColor = new HsvColor(baseHsvColor.A, componentValue, baseHsvColor.S, baseHsvColor.V); + hsvColor = new HsvColor(baseHsvColor.A, sliderPercent * MaxHue, baseHsvColor.S, baseHsvColor.V); break; } case ColorComponent.Component2: { - var componentValue = MathUtilities.Clamp(sliderPercent * 1.0, 0.0, 1.0); - hsvColor = new HsvColor(baseHsvColor.A, baseHsvColor.H, componentValue, baseHsvColor.V); + hsvColor = new HsvColor(baseHsvColor.A, baseHsvColor.H, sliderPercent, baseHsvColor.V); break; } case ColorComponent.Component3: { - var componentValue = MathUtilities.Clamp(sliderPercent * 1.0, 0.0, 1.0); - hsvColor = new HsvColor(baseHsvColor.A, baseHsvColor.H, baseHsvColor.S, componentValue); + hsvColor = new HsvColor(baseHsvColor.A, baseHsvColor.H, baseHsvColor.S, sliderPercent); break; } } @@ -279,6 +324,7 @@ namespace Avalonia.Controls.Primitives UpdateBackground(); } + UpdatePseudoClasses(); disableUpdates = false; } else if (change.Property == HsvColorProperty) @@ -293,6 +339,7 @@ namespace Avalonia.Controls.Primitives UpdateBackground(); } + UpdatePseudoClasses(); disableUpdates = false; } else if (change.Property == BoundsProperty) @@ -321,6 +368,7 @@ namespace Avalonia.Controls.Primitives HsvColor = color.ToHsv(); } + UpdatePseudoClasses(); disableUpdates = false; } diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSlider.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSlider.xaml index 1ca9b12ffe..620e9f658d 100644 --- a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSlider.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSlider.xaml @@ -159,14 +159,26 @@ - + + + + + - - + diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSpectrum.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSpectrum.xaml index b209fe75b3..8e5139975a 100644 --- a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSpectrum.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSpectrum.xaml @@ -118,6 +118,9 @@ + From 005907a93bf5f594694007de8b1fd91fda0cc466 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Wed, 27 Apr 2022 16:53:18 +0200 Subject: [PATCH 223/416] More hit testing fixes for embedded content runs --- src/Avalonia.Base/Media/GlyphRun.cs | 61 ++++++++++--------- .../Media/TextFormatting/TextFormatterImpl.cs | 24 ++++---- .../Media/TextFormatting/TextLineImpl.cs | 44 +++++++------ src/Skia/Avalonia.Skia/TextShaperImpl.cs | 3 +- .../Media/TextFormatting/TextLineTests.cs | 21 +++++-- 5 files changed, 81 insertions(+), 72 deletions(-) diff --git a/src/Avalonia.Base/Media/GlyphRun.cs b/src/Avalonia.Base/Media/GlyphRun.cs index 9a2645f03d..22be8d8865 100644 --- a/src/Avalonia.Base/Media/GlyphRun.cs +++ b/src/Avalonia.Base/Media/GlyphRun.cs @@ -28,6 +28,8 @@ namespace Avalonia.Media private IReadOnlyList? _glyphOffsets; private IReadOnlyList? _glyphClusters; + private int _offsetToFirstCharacter; + /// /// Initializes a new instance of the class by specifying properties of the class. /// @@ -203,7 +205,7 @@ namespace Avalonia.Media /// public double GetDistanceFromCharacterHit(CharacterHit characterHit) { - var characterIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength; + var characterIndex = characterHit.FirstCharacterIndex + characterHit.TrailingLength - _offsetToFirstCharacter; var distance = 0.0; @@ -552,30 +554,20 @@ namespace Avalonia.Media } nextCluster = GlyphClusters[currentIndex]; - } - - if (nextCluster < Characters.Start) - { - nextCluster = Characters.Start; - } - - if (cluster < Characters.Start) - { - cluster = Characters.Start; - } + } int trailingLength; if (nextCluster == cluster) { - trailingLength = Characters.Start + Characters.Length - cluster; + trailingLength = Characters.Start + Characters.Length - _offsetToFirstCharacter - cluster; } else { trailingLength = nextCluster - cluster; } - return new CharacterHit(cluster, trailingLength); + return new CharacterHit(_offsetToFirstCharacter + cluster, trailingLength); } /// @@ -609,6 +601,13 @@ namespace Avalonia.Media private GlyphRunMetrics CreateGlyphRunMetrics() { + if (GlyphClusters != null && GlyphClusters.Count > 0) + { + var firstCluster = GlyphClusters[0]; + + _offsetToFirstCharacter = Math.Max(0, Characters.Start - firstCluster); + } + var height = (GlyphTypeface.Descent - GlyphTypeface.Ascent + GlyphTypeface.LineGap) * Scale; var widthIncludingTrailingWhitespace = 0d; @@ -680,34 +679,40 @@ namespace Avalonia.Media { for (var i = GlyphClusters.Count - 1; i >= 0; i--) { - var cluster = GlyphClusters[i]; - - var codepointIndex = IsLeftToRight ? cluster - _characters.Start : _characters.End - cluster; + var currentCluster = GlyphClusters[i]; + var characterIndex = Math.Max(0, currentCluster - _characters.BufferOffset); + var codepoint = Codepoint.ReadAt(_characters, characterIndex, out _); - if (codepointIndex < 0) + if (!codepoint.IsWhiteSpace) { - trailingWhitespaceLength = _characters.Length; - - glyphCount = GlyphClusters.Count; - break; } - var codepoint = Codepoint.ReadAt(_characters, codepointIndex, out _); + var clusterLength = 1; - if (!codepoint.IsWhiteSpace) + while(i - 1 >= 0) { + var nextCluster = GlyphClusters[i - 1]; + + if(currentCluster == nextCluster) + { + clusterLength++; + i--; + + continue; + } + break; } if (codepoint.IsBreakChar) { - newLineLength++; + newLineLength += clusterLength; } - trailingWhitespaceLength++; - - glyphCount++; + trailingWhitespaceLength += clusterLength; + + glyphCount++; } } diff --git a/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs index 67fba00ee8..7f0f204886 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs @@ -79,14 +79,14 @@ namespace Avalonia.Media.TextFormatting { var currentRun = textRuns[i]; - if (currentLength + currentRun.Text.Length < length) + if (currentLength + currentRun.TextSourceLength < length) { currentLength += currentRun.TextSourceLength; continue; } - var firstCount = currentRun.Text.Length >= 1 ? i + 1 : i; + var firstCount = currentRun.TextSourceLength >= 1 ? i + 1 : i; var first = new List(firstCount); @@ -100,13 +100,13 @@ namespace Avalonia.Media.TextFormatting var secondCount = textRuns.Count - firstCount; - if (currentLength + currentRun.Text.Length == length) + if (currentLength + currentRun.TextSourceLength == length) { var second = secondCount > 0 ? new List(secondCount) : null; if (second != null) { - var offset = currentRun.Text.Length >= 1 ? 1 : 0; + var offset = currentRun.TextSourceLength >= 1 ? 1 : 0; for (var j = 0; j < secondCount; j++) { @@ -124,16 +124,14 @@ namespace Avalonia.Media.TextFormatting var second = new List(secondCount); - if (currentRun is not ShapedTextCharacters shapedTextCharacters) + if (currentRun is ShapedTextCharacters shapedTextCharacters) { - throw new NotSupportedException("Only shaped runs can be split in between."); - } - - var split = shapedTextCharacters.Split(length - currentLength); + var split = shapedTextCharacters.Split(length - currentLength); - first.Add(split.First); + first.Add(split.First); - second.Add(split.Second!); + second.Add(split.Second!); + } for (var j = 1; j < secondCount; j++) { @@ -483,7 +481,7 @@ namespace Avalonia.Media.TextFormatting { case ShapedTextCharacters shapedTextCharacters: { - var firstCluster = shapedTextCharacters.Text.Start; + var firstCluster = shapedTextCharacters.ShapedBuffer.GlyphClusters[0]; var lastCluster = firstCluster; for (var i = 0; i < shapedTextCharacters.ShapedBuffer.Length; i++) @@ -492,7 +490,7 @@ namespace Avalonia.Media.TextFormatting if (currentWidth + glyphInfo.GlyphAdvance > paragraphWidth) { - measuredLength += Math.Max(0, lastCluster - firstCluster + 1); + measuredLength += Math.Max(0, lastCluster - firstCluster); goto found; } diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs index b480774d1d..6a704f6f3e 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs @@ -404,7 +404,7 @@ namespace Avalonia.Media.TextFormatting var result = new List(TextRuns.Count); var lastDirection = _flowDirection; var currentDirection = lastDirection; - var currentPosition = 0; + var currentPosition = FirstTextSourceIndex; var currentRect = Rect.Empty; var startX = Start; @@ -418,6 +418,11 @@ namespace Avalonia.Media.TextFormatting continue; } + if(currentPosition + currentRun.TextSourceLength <= firstTextSourceCharacterIndex) + { + continue; + } + TextRun? nextRun = null; if (index + 1 < TextRuns.Count) @@ -1018,31 +1023,21 @@ namespace Avalonia.Media.TextFormatting private TextLineMetrics CreateLineMetrics() { - var start = 0d; - var height = 0d; + var glyphTypeface = _paragraphProperties.DefaultTextRunProperties.Typeface.GlyphTypeface; + var fontRenderingEmSize = _paragraphProperties.DefaultTextRunProperties.FontRenderingEmSize; + var scale = fontRenderingEmSize / glyphTypeface.DesignEmHeight; + var width = 0d; var widthIncludingWhitespace = 0d; var trailingWhitespaceLength = 0; var newLineLength = 0; - var ascent = 0d; - var descent = 0d; - var lineGap = 0d; - var fontRenderingEmSize = 0d; + var ascent = glyphTypeface.Ascent * scale; + var descent = glyphTypeface.Descent * scale; + var lineGap = glyphTypeface.LineGap * scale; - var lineHeight = _paragraphProperties.LineHeight; - - if (_textRuns.Count == 0) - { - var glyphTypeface = _paragraphProperties.DefaultTextRunProperties.Typeface.GlyphTypeface; - fontRenderingEmSize = _paragraphProperties.DefaultTextRunProperties.FontRenderingEmSize; - var scale = fontRenderingEmSize / glyphTypeface.DesignEmHeight; - ascent = glyphTypeface.Ascent * scale; - height = double.IsNaN(lineHeight) || MathUtilities.IsZero(lineHeight) ? - descent - ascent + lineGap : - lineHeight; - - return new TextLineMetrics(false, height, 0, start, -ascent, 0, 0, 0); - } + var height = descent - ascent + lineGap; + + var lineHeight = _paragraphProperties.LineHeight; for (var index = 0; index < _textRuns.Count; index++) { @@ -1166,12 +1161,15 @@ namespace Avalonia.Media.TextFormatting } } - start = GetParagraphOffsetX(width, widthIncludingWhitespace, _paragraphWidth, + var start = GetParagraphOffsetX(width, widthIncludingWhitespace, _paragraphWidth, _paragraphProperties.TextAlignment, _paragraphProperties.FlowDirection); if (!double.IsNaN(lineHeight) && !MathUtilities.IsZero(lineHeight)) { - height = lineHeight; + if(lineHeight > height) + { + height = lineHeight; + } } return new TextLineMetrics(widthIncludingWhitespace > _paragraphWidth, height, newLineLength, start, diff --git a/src/Skia/Avalonia.Skia/TextShaperImpl.cs b/src/Skia/Avalonia.Skia/TextShaperImpl.cs index a0890262e7..ebaa247da8 100644 --- a/src/Skia/Avalonia.Skia/TextShaperImpl.cs +++ b/src/Skia/Avalonia.Skia/TextShaperImpl.cs @@ -1,6 +1,5 @@ using System; using System.Globalization; -using Avalonia.Media; using Avalonia.Media.TextFormatting; using Avalonia.Media.TextFormatting.Unicode; using Avalonia.Platform; @@ -59,7 +58,7 @@ namespace Avalonia.Skia var glyphIndex = (ushort)sourceInfo.Codepoint; - var glyphCluster = (int)sourceInfo.Cluster; + var glyphCluster = (int)(sourceInfo.Cluster); var glyphAdvance = GetGlyphAdvance(glyphPositions, i, textScale); diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs index e3b9e5a8b1..a47638d2ec 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLineTests.cs @@ -665,17 +665,16 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting var text = "0123".AsMemory(); var shaperOption = new TextShaperOptions(Typeface.Default.GlyphTypeface, 10, 0, CultureInfo.CurrentCulture); - var shapedBuffer = TextShaper.Current.ShapeText(new ReadOnlySlice(text), shaperOption); - var firstRun = new ShapedTextCharacters(shapedBuffer, defaultProperties); + var firstRun = new ShapedTextCharacters(TextShaper.Current.ShapeText(new ReadOnlySlice(text, 1, text.Length), shaperOption), defaultProperties); var textRuns = new List { new CustomDrawableRun(), firstRun, new CustomDrawableRun(), - new ShapedTextCharacters(shapedBuffer, defaultProperties), + new ShapedTextCharacters(TextShaper.Current.ShapeText(new ReadOnlySlice(text, text.Length + 2, text.Length), shaperOption), defaultProperties), new CustomDrawableRun(), - new ShapedTextCharacters(shapedBuffer, defaultProperties) + new ShapedTextCharacters(TextShaper.Current.ShapeText(new ReadOnlySlice(text, text.Length * 2 + 3, text.Length), shaperOption), defaultProperties) }; var textSource = new FixedRunsTextSource(textRuns); @@ -691,15 +690,25 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting Assert.Equal(1, textBounds.Count); Assert.Equal(textLine.WidthIncludingTrailingWhitespace, textBounds.Sum(x => x.Rectangle.Width)); - textBounds = textLine.GetTextBounds(0, firstRun.Text.Length); + textBounds = textLine.GetTextBounds(0, 1); Assert.Equal(1, textBounds.Count); - Assert.Equal(firstRun.Size.Width, textBounds[0].Rectangle.Width); + Assert.Equal(14, textBounds[0].Rectangle.Width); textBounds = textLine.GetTextBounds(0, firstRun.Text.Length + 1); Assert.Equal(1, textBounds.Count); Assert.Equal(firstRun.Size.Width + 14, textBounds[0].Rectangle.Width); + + textBounds = textLine.GetTextBounds(1, firstRun.Text.Length); + + Assert.Equal(1, textBounds.Count); + Assert.Equal(firstRun.Size.Width, textBounds[0].Rectangle.Width); + + textBounds = textLine.GetTextBounds(1, firstRun.Text.Length + 1); + + Assert.Equal(1, textBounds.Count); + Assert.Equal(firstRun.Size.Width + 14, textBounds[0].Rectangle.Width); } } From a34e0f50e2367a45da815ea8dd36b0de4dca167c Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Wed, 27 Apr 2022 17:55:00 +0200 Subject: [PATCH 224/416] Bump --- .../Media/TextFormatting/TextLayoutTests.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/Avalonia.RenderTests/Media/TextFormatting/TextLayoutTests.cs b/tests/Avalonia.RenderTests/Media/TextFormatting/TextLayoutTests.cs index 6ed4ba0d4a..b668f4d39e 100644 --- a/tests/Avalonia.RenderTests/Media/TextFormatting/TextLayoutTests.cs +++ b/tests/Avalonia.RenderTests/Media/TextFormatting/TextLayoutTests.cs @@ -1,11 +1,8 @@ using Avalonia.Media; -using Avalonia.Platform; using System; -using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Runtime.InteropServices; -using System.Text; using System.Threading.Tasks; using Avalonia.Controls; using Avalonia.Media.TextFormatting; From 25a34efd9a01cfb86698038cc54c4292ec1947ff Mon Sep 17 00:00:00 2001 From: robloo Date: Wed, 27 Apr 2022 22:08:33 -0400 Subject: [PATCH 225/416] Implement color display name in ColorSpectrum --- .../ColorPreviewer.Properties.cs | 2 +- .../ColorSpectrum/ColorSpectrum.cs | 60 +++++---- .../Helpers/ColorHelpers.cs | 24 +--- .../Helpers/ColorNameHelpers.cs | 116 ++++++++++++++++++ .../Themes/Default/ColorSpectrum.xaml | 16 +-- .../Themes/Fluent/ColorSpectrum.xaml | 16 +-- 6 files changed, 174 insertions(+), 60 deletions(-) create mode 100644 src/Avalonia.Controls.ColorPicker/Helpers/ColorNameHelpers.cs diff --git a/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.Properties.cs b/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.Properties.cs index 903b5fb52b..f90f02551d 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.Properties.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.Properties.cs @@ -41,7 +41,7 @@ namespace Avalonia.Controls.Primitives /// public bool ShowAccentColors { - get => (bool)this.GetValue(ShowAccentColorsProperty); + get => GetValue(ShowAccentColorsProperty); set => SetValue(ShowAccentColorsProperty, value); } } diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs index fe9a2fac43..9b2459265d 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -20,7 +20,6 @@ namespace Avalonia.Controls.Primitives /// /// A two dimensional spectrum for color selection. /// - [TemplatePart("PART_ColorNameToolTip", typeof(ToolTip))] [TemplatePart("PART_InputTarget", typeof(Canvas))] [TemplatePart("PART_LayoutRoot", typeof(Panel))] [TemplatePart("PART_SelectionEllipsePanel", typeof(Panel))] @@ -60,7 +59,6 @@ namespace Avalonia.Controls.Primitives private Ellipse? _spectrumOverlayEllipse; private Canvas? _inputTarget; private Panel? _selectionEllipsePanel; - private ToolTip? _colorNameToolTip; // Put the spectrum images in a bitmap, which is then given to an ImageBrush. private WriteableBitmap? _hueRedBitmap; @@ -117,7 +115,6 @@ namespace Avalonia.Controls.Primitives UnregisterEvents(); // Failsafe - _colorNameToolTip = e.NameScope.Find("PART_ColorNameToolTip"); _inputTarget = e.NameScope.Find("PART_InputTarget"); _layoutRoot = e.NameScope.Find("PART_LayoutRoot"); _selectionEllipsePanel = e.NameScope.Find("PART_SelectionEllipsePanel"); @@ -152,10 +149,10 @@ namespace Avalonia.Controls.Primitives }); } - if (ColorHelpers.ToDisplayNameExists && - _colorNameToolTip != null) + if (_selectionEllipsePanel != null && + ColorNameHelpers.ToDisplayNameExists) { - _colorNameToolTip.Content = ColorHelpers.ToDisplayName(Color); + ToolTip.SetTip(_selectionEllipsePanel, ColorNameHelpers.ToDisplayName(Color)); } // If we haven't yet created our bitmaps, do so now. @@ -338,26 +335,45 @@ namespace Avalonia.Controls.Primitives protected override void OnGotFocus(GotFocusEventArgs e) { // We only want to bother with the color name tool tip if we can provide color names. - if (_colorNameToolTip != null && - ColorHelpers.ToDisplayNameExists) + if (_selectionEllipsePanel != null && + ColorNameHelpers.ToDisplayNameExists) { - ToolTip.SetIsOpen(_colorNameToolTip, true); + ToolTip.SetIsOpen(_selectionEllipsePanel, true); } UpdatePseudoClasses(); + + base.OnGotFocus(e); } /// protected override void OnLostFocus(RoutedEventArgs e) { // We only want to bother with the color name tool tip if we can provide color names. - if (_colorNameToolTip != null && - ColorHelpers.ToDisplayNameExists) + if (_selectionEllipsePanel != null && + ColorNameHelpers.ToDisplayNameExists) + { + ToolTip.SetIsOpen(_selectionEllipsePanel, false); + } + + UpdatePseudoClasses(); + + base.OnLostFocus(e); + } + + /// + protected override void OnPointerLeave(PointerEventArgs e) + { + // We only want to bother with the color name tool tip if we can provide color names. + if (_selectionEllipsePanel != null && + ColorNameHelpers.ToDisplayNameExists) { - ToolTip.SetIsOpen(_colorNameToolTip, false); + ToolTip.SetIsOpen(_selectionEllipsePanel, false); } UpdatePseudoClasses(); + + base.OnPointerLeave(e); } /// @@ -516,12 +532,10 @@ namespace Avalonia.Controls.Primitives var colorChangedEventArgs = new ColorChangedEventArgs(_oldColor, newColor); ColorChanged?.Invoke(this, colorChangedEventArgs); - if (ColorHelpers.ToDisplayNameExists) + if (_selectionEllipsePanel != null && + ColorNameHelpers.ToDisplayNameExists) { - if (_colorNameToolTip != null) - { - _colorNameToolTip.Content = ColorHelpers.ToDisplayName(newColor); - } + ToolTip.SetTip(_selectionEllipsePanel, ColorNameHelpers.ToDisplayName(Color)); } } } @@ -811,15 +825,11 @@ namespace Avalonia.Controls.Primitives Canvas.SetTop(_selectionEllipsePanel, yPosition - (_selectionEllipsePanel.Height / 2)); // We only want to bother with the color name tool tip if we can provide color names. - if (ColorHelpers.ToDisplayNameExists) + if (IsFocused && + _selectionEllipsePanel != null && + ColorNameHelpers.ToDisplayNameExists) { - if (_colorNameToolTip != null) - { - // ToolTip doesn't currently provide any way to re-run its placement logic if its placement target moves, - // so toggling IsEnabled induces it to do that without incurring any visual glitches. - _colorNameToolTip.IsEnabled = false; - _colorNameToolTip.IsEnabled = true; - } + ToolTip.SetIsOpen(_selectionEllipsePanel, true); } UpdatePseudoClasses(); diff --git a/src/Avalonia.Controls.ColorPicker/Helpers/ColorHelpers.cs b/src/Avalonia.Controls.ColorPicker/Helpers/ColorHelpers.cs index 6500d10fe9..36ee478c7b 100644 --- a/src/Avalonia.Controls.ColorPicker/Helpers/ColorHelpers.cs +++ b/src/Avalonia.Controls.ColorPicker/Helpers/ColorHelpers.cs @@ -17,18 +17,6 @@ namespace Avalonia.Controls.Primitives { internal static class ColorHelpers { - public const int CheckerSize = 4; - - public static bool ToDisplayNameExists - { - get => false; - } - - public static string ToDisplayName(Color color) - { - return string.Empty; - } - /// /// Generates a new bitmap of the specified size by changing a specific color component. /// This will produce a gradient representing a sweep of all possible values of the color component. @@ -325,7 +313,7 @@ namespace Avalonia.Controls.Primitives { Hsv newHsv = originalHsv; - if (amount == IncrementAmount.Small || !ToDisplayNameExists) + if (amount == IncrementAmount.Small || !ColorNameHelpers.ToDisplayNameExists) { // In order to avoid working with small values that can incur rounding issues, // we'll multiple saturation and value by 100 to put them in the range of 0-100 instead of 0-1. @@ -416,7 +404,7 @@ namespace Avalonia.Controls.Primitives // in the middle of that color's bounds. Hsv newHsv = originalHsv; - string originalColorName = ColorHelpers.ToDisplayName(originalHsv.ToRgb().ToColor()); + string originalColorName = ColorNameHelpers.ToDisplayName(originalHsv.ToRgb().ToColor()); string newColorName = originalColorName; // Note: *newValue replaced with ref local variable for C#, must be initialized @@ -471,7 +459,7 @@ namespace Avalonia.Controls.Primitives { newValue = maxBound; shouldFindMidPoint = false; - newColorName = ColorHelpers.ToDisplayName(newHsv.ToRgb().ToColor()); + newColorName = ColorNameHelpers.ToDisplayName(newHsv.ToRgb().ToColor()); break; } } @@ -486,7 +474,7 @@ namespace Avalonia.Controls.Primitives { newValue = minBound; shouldFindMidPoint = false; - newColorName = ColorHelpers.ToDisplayName(newHsv.ToRgb().ToColor()); + newColorName = ColorNameHelpers.ToDisplayName(newHsv.ToRgb().ToColor()); break; } } @@ -501,7 +489,7 @@ namespace Avalonia.Controls.Primitives break; } - newColorName = ColorHelpers.ToDisplayName(newHsv.ToRgb().ToColor()); + newColorName = ColorNameHelpers.ToDisplayName(newHsv.ToRgb().ToColor()); } if (shouldFindMidPoint) @@ -574,7 +562,7 @@ namespace Avalonia.Controls.Primitives } } - currentColorName = ColorHelpers.ToDisplayName(currentHsv.ToRgb().ToColor()); + currentColorName = ColorNameHelpers.ToDisplayName(currentHsv.ToRgb().ToColor()); } newValue = (startValue + currentValue + startEndOffset) / 2; diff --git a/src/Avalonia.Controls.ColorPicker/Helpers/ColorNameHelpers.cs b/src/Avalonia.Controls.ColorPicker/Helpers/ColorNameHelpers.cs new file mode 100644 index 0000000000..303a52f00b --- /dev/null +++ b/src/Avalonia.Controls.ColorPicker/Helpers/ColorNameHelpers.cs @@ -0,0 +1,116 @@ +using System; +using System.Globalization; +using System.Collections.Generic; +using Avalonia.Media; +using System.Text; + +namespace Avalonia.Controls.Primitives +{ + /// + /// Contains helpers useful when working with color names. + /// + public static class ColorNameHelpers + { + private static readonly Dictionary cachedDisplayNames = new Dictionary(); + private static readonly object cacheMutex = new object(); + + /// + /// Determines if color display names are supported based on the current thread culture. + /// + /// + /// Only English names are currently supported following known color names. + /// In the future known color names could be localized. + /// + public static bool ToDisplayNameExists + { + get => CultureInfo.CurrentUICulture.Name.StartsWith("EN", StringComparison.OrdinalIgnoreCase); + } + + /// + /// Determines an approximate display name for the given color. + /// + /// The color to get the display name for. + /// The approximate color display name. + public static string ToDisplayName(Color color) + { + // Without rounding, there are 16,777,216 possible RGB colors (without alpha). + // This is too many to cache and search through for performance reasons. + // It is also needlessly large as there are only ~140 known/named colors. + // Therefore, rounding of the input color's component values is done to + // reduce the color space into something more useful. + double rounding = 5; + var roundedColor = new Color( + 0xFF, + Convert.ToByte(Math.Round(color.R / rounding) * rounding), + Convert.ToByte(Math.Round(color.G / rounding) * rounding), + Convert.ToByte(Math.Round(color.B / rounding) * rounding)); + + // Attempt to use a previously cached display name + lock (cacheMutex) + { + if (cachedDisplayNames.TryGetValue(roundedColor, out var displayName)) + { + return displayName; + } + } + + // Find the closest known color by measuring 3D Euclidean distance (ignore alpha) + var closestKnownColor = KnownColor.None; + var closestKnownColorDistance = double.PositiveInfinity; + var knownColors = (KnownColor[])Enum.GetValues(typeof(KnownColor)); + + for (int i = 1; i < knownColors.Length; i++) // Skip 'None' + { + // Transparent is skipped since alpha is ignored making it equivalent to White + if (knownColors[i] != KnownColor.Transparent) + { + Color knownColor = KnownColors.ToColor(knownColors[i]); + + double distance = Math.Sqrt( + Math.Pow((double)(roundedColor.R - knownColor.R), 2.0) + + Math.Pow((double)(roundedColor.G - knownColor.G), 2.0) + + Math.Pow((double)(roundedColor.B - knownColor.B), 2.0)); + + if (distance < closestKnownColorDistance) + { + closestKnownColor = knownColors[i]; + closestKnownColorDistance = distance; + } + } + } + + // Return the closest known color as the display name + // Cache results for next time as well + if (closestKnownColor != KnownColor.None) + { + StringBuilder sb = new StringBuilder(); + string name = closestKnownColor.ToString(); + + // Add spaces converting PascalCase to human-readable names + for (int i = 0; i < name.Length; i++) + { + if (i != 0 && + char.IsUpper(name[i])) + { + sb.Append(' '); + } + + sb.Append(name[i]); + } + + string displayName = sb.ToString(); + + lock (cacheMutex) + { + cachedDisplayNames.Add(roundedColor, displayName); + } + + return displayName; + } + else + { + return string.Empty; + } + } + } +} diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Default/ColorSpectrum.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Default/ColorSpectrum.xaml index 9596ca9653..78e6da8aa3 100644 --- a/src/Avalonia.Controls.ColorPicker/Themes/Default/ColorSpectrum.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Default/ColorSpectrum.xaml @@ -48,7 +48,10 @@ Background="Transparent" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> - + + - - - - + VerticalAlignment="Stretch" /> + + + - + + - - - - + VerticalAlignment="Stretch" /> + + + Date: Wed, 27 Apr 2022 22:37:36 -0400 Subject: [PATCH 226/416] Follow Avalonia convention --- .../ColorPreviewer/ColorPreviewer.cs | 6 ------ .../ColorSlider/ColorSlider.cs | 4 ---- .../ColorSpectrum/ColorSpectrum.cs | 2 -- 3 files changed, 12 deletions(-) diff --git a/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.cs b/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.cs index 1c0dd2154a..35072d6a42 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.cs @@ -61,8 +61,6 @@ namespace Avalonia.Controls.Primitives eventsConnected = false; } - - return; } /// @@ -92,8 +90,6 @@ namespace Avalonia.Controls.Primitives HsvColor = newColor; ColorChanged?.Invoke(this, new ColorChangedEventArgs(oldColor.ToRgb(), newColor.ToRgb())); - - return; } /// @@ -115,8 +111,6 @@ namespace Avalonia.Controls.Primitives HsvColor newHsvColor = AccentColorConverter.GetAccent(hsvColor, accentStep); OnColorChanged(newHsvColor); - - return; } } } diff --git a/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs b/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs index 9d9e9f393e..c73f2b1cea 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.cs @@ -100,8 +100,6 @@ namespace Avalonia.Controls.Primitives Background = ColorHelpers.BitmapToBrushAsync(bitmap, pixelWidth, pixelHeight); } } - - return; } /// @@ -170,8 +168,6 @@ namespace Avalonia.Controls.Primitives break; } } - - return; } /// diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs index 9b2459265d..7b68068d46 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -327,8 +327,6 @@ namespace Avalonia.Controls.Primitives maxBound)); e.Handled = true; - - return; } /// From d9ef01acfcd9d7d504558ca7ac92941351a94cb3 Mon Sep 17 00:00:00 2001 From: robloo Date: Wed, 27 Apr 2022 22:40:26 -0400 Subject: [PATCH 227/416] Render the ColorSpectrum to physical device pixel resolution --- .../ColorSpectrum/ColorSpectrum.cs | 46 +++++++++++-------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs index 7b68068d46..4cabb5c5b7 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -10,6 +10,7 @@ using Avalonia.Controls.Metadata; using Avalonia.Controls.Shapes; using Avalonia.Input; using Avalonia.Interactivity; +using Avalonia.Layout; using Avalonia.Media; using Avalonia.Media.Imaging; using Avalonia.Threading; @@ -587,8 +588,10 @@ namespace Avalonia.Controls.Primitives return; } - double xPosition = point.Position.X; - double yPosition = point.Position.Y; + // Remember the bitmap size follows physical device pixels + var scale = LayoutHelper.GetLayoutScale(this); + double xPosition = point.Position.X * scale; + double yPosition = point.Position.Y * scale; double radius = Math.Min(_imageWidthFromLastBitmapCreation, _imageHeightFromLastBitmapCreation) / 2; double distanceFromRadius = Math.Sqrt(Math.Pow(xPosition - radius, 2) + Math.Pow(yPosition - radius, 2)); @@ -819,8 +822,10 @@ namespace Avalonia.Controls.Primitives yPosition = (Math.Sin((thetaValue * Math.PI / 180.0) + Math.PI) * radius * rValue) + radius; } - Canvas.SetLeft(_selectionEllipsePanel, xPosition - (_selectionEllipsePanel.Width / 2)); - Canvas.SetTop(_selectionEllipsePanel, yPosition - (_selectionEllipsePanel.Height / 2)); + // Remember the bitmap size follows physical device pixels + var scale = LayoutHelper.GetLayoutScale(this); + Canvas.SetLeft(_selectionEllipsePanel, (xPosition / scale) - (_selectionEllipsePanel.Width / 2)); + Canvas.SetTop(_selectionEllipsePanel, (yPosition / scale) - (_selectionEllipsePanel.Height / 2)); // We only want to bother with the color name tool tip if we can provide color names. if (IsFocused && @@ -969,7 +974,14 @@ namespace Avalonia.Controls.Primitives List bgraMaxPixelData = new List(); List newHsvValues = new List(); - var pixelCount = (int)(Math.Round(minDimension) * Math.Round(minDimension)); + // In Avalonia, Bounds returns the actual device-independent pixel size of a control. + // However, this is not necessarily the size of the control rendered on a display. + // A desktop or application scaling factor may be applied which must be accounted for here. + // Remember bitmaps in Avalonia are rendered mapping to actual device pixels, not the device- + // independent pixels of controls. + var scale = LayoutHelper.GetLayoutScale(this); + int pixelDimension = (int)Math.Round(minDimension * scale); + var pixelCount = pixelDimension * pixelDimension; var pixelDataSize = pixelCount * 4; bgraMinPixelData.Capacity = pixelDataSize; @@ -986,8 +998,6 @@ namespace Avalonia.Controls.Primitives bgraMaxPixelData.Capacity = pixelDataSize; newHsvValues.Capacity = pixelCount; - int minDimensionInt = (int)Math.Round(minDimension); - await Task.Run(() => { // As the user perceives it, every time the third dimension not represented in the ColorSpectrum changes, @@ -1006,12 +1016,12 @@ namespace Avalonia.Controls.Primitives // but the running time savings after that are *huge* when we can just set an opacity instead of generating a brand new bitmap. if (shape == ColorSpectrumShape.Box) { - for (int x = minDimensionInt - 1; x >= 0; --x) + for (int x = pixelDimension - 1; x >= 0; --x) { - for (int y = minDimensionInt - 1; y >= 0; --y) + for (int y = pixelDimension - 1; y >= 0; --y) { FillPixelForBox( - x, y, hsv, minDimensionInt, components, minHue, maxHue, minSaturation, maxSaturation, minValue, maxValue, + x, y, hsv, pixelDimension, components, minHue, maxHue, minSaturation, maxSaturation, minValue, maxValue, bgraMinPixelData, bgraMiddle1PixelData, bgraMiddle2PixelData, bgraMiddle3PixelData, bgraMiddle4PixelData, bgraMaxPixelData, newHsvValues); } @@ -1019,12 +1029,12 @@ namespace Avalonia.Controls.Primitives } else { - for (int y = 0; y < minDimensionInt; ++y) + for (int y = 0; y < pixelDimension; ++y) { - for (int x = 0; x < minDimensionInt; ++x) + for (int x = 0; x < pixelDimension; ++x) { FillPixelForRing( - x, y, minDimensionInt / 2.0, hsv, components, minHue, maxHue, minSaturation, maxSaturation, minValue, maxValue, + x, y, pixelDimension / 2.0, hsv, components, minHue, maxHue, minSaturation, maxSaturation, minValue, maxValue, bgraMinPixelData, bgraMiddle1PixelData, bgraMiddle2PixelData, bgraMiddle3PixelData, bgraMiddle4PixelData, bgraMaxPixelData, newHsvValues); } @@ -1034,8 +1044,8 @@ namespace Avalonia.Controls.Primitives Dispatcher.UIThread.Post(() => { - int pixelWidth = (int)Math.Round(minDimension); - int pixelHeight = (int)Math.Round(minDimension); + int pixelWidth = pixelDimension; + int pixelHeight = pixelDimension; ColorSpectrumComponents components2 = Components; @@ -1066,8 +1076,8 @@ namespace Avalonia.Controls.Primitives _shapeFromLastBitmapCreation = Shape; _componentsFromLastBitmapCreation = Components; - _imageWidthFromLastBitmapCreation = minDimension; - _imageHeightFromLastBitmapCreation = minDimension; + _imageWidthFromLastBitmapCreation = pixelDimension; + _imageHeightFromLastBitmapCreation = pixelDimension; _minHueFromLastBitmapCreation = MinHue; _maxHueFromLastBitmapCreation = MaxHue; _minSaturationFromLastBitmapCreation = MinSaturation; @@ -1086,7 +1096,7 @@ namespace Avalonia.Controls.Primitives double x, double y, Hsv baseHsv, - double minDimension, + int minDimension, ColorSpectrumComponents components, double minHue, double maxHue, From aef0d012258da94978d96ff38ce19af1c6554090 Mon Sep 17 00:00:00 2001 From: robloo Date: Wed, 27 Apr 2022 22:47:29 -0400 Subject: [PATCH 228/416] Reorder properties following Avalonia convention --- .../ColorPreviewer.Properties.cs | 16 +-- .../ColorSlider/ColorSlider.Properties.cs | 96 ++++++------- .../ColorSpectrum/ColorSpectrum.Properties.cs | 136 +++++++++--------- 3 files changed, 124 insertions(+), 124 deletions(-) diff --git a/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.Properties.cs b/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.Properties.cs index f90f02551d..c545f25298 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.Properties.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.Properties.cs @@ -13,6 +13,14 @@ namespace Avalonia.Controls.Primitives nameof(HsvColor), Colors.Transparent.ToHsv()); + /// + /// Defines the property. + /// + public static readonly StyledProperty ShowAccentColorsProperty = + AvaloniaProperty.Register( + nameof(ShowAccentColors), + true); + /// /// Gets or sets the currently previewed color in the HSV color model. /// @@ -27,14 +35,6 @@ namespace Avalonia.Controls.Primitives set => SetValue(HsvColorProperty, value); } - /// - /// Defines the property. - /// - public static readonly StyledProperty ShowAccentColorsProperty = - AvaloniaProperty.Register( - nameof(ShowAccentColors), - true); - /// /// Gets or sets a value indicating whether accent colors are shown along /// with the preview color. diff --git a/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.Properties.cs b/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.Properties.cs index 3aa3e3a789..12dce0b03e 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.Properties.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSlider/ColorSlider.Properties.cs @@ -13,6 +13,54 @@ namespace Avalonia.Controls.Primitives nameof(Color), Colors.White); + /// + /// Defines the property. + /// + public static readonly StyledProperty ColorComponentProperty = + AvaloniaProperty.Register( + nameof(ColorComponent), + ColorComponent.Component1); + + /// + /// Defines the property. + /// + public static readonly StyledProperty ColorModelProperty = + AvaloniaProperty.Register( + nameof(ColorModel), + ColorModel.Rgba); + + /// + /// Defines the property. + /// + public static readonly StyledProperty HsvColorProperty = + AvaloniaProperty.Register( + nameof(HsvColor), + Colors.White.ToHsv()); + + /// + /// Defines the property. + /// + public static readonly StyledProperty IsAlphaMaxForcedProperty = + AvaloniaProperty.Register( + nameof(IsAlphaMaxForced), + true); + + /// + /// Defines the property. + /// + public static readonly StyledProperty IsAutoUpdatingEnabledProperty = + AvaloniaProperty.Register( + nameof(IsAutoUpdatingEnabled), + true); + + /// + /// Defines the property. + /// + public static readonly StyledProperty IsSaturationValueMaxForcedProperty = + AvaloniaProperty.Register( + nameof(IsSaturationValueMaxForced), + true); + /// /// Gets or sets the currently selected color in the RGB color model. /// @@ -26,14 +74,6 @@ namespace Avalonia.Controls.Primitives set => SetValue(ColorProperty, value); } - /// - /// Defines the property. - /// - public static readonly StyledProperty ColorComponentProperty = - AvaloniaProperty.Register( - nameof(ColorComponent), - ColorComponent.Component1); - /// /// Gets or sets the color component represented by the slider. /// @@ -43,14 +83,6 @@ namespace Avalonia.Controls.Primitives set => SetValue(ColorComponentProperty, value); } - /// - /// Defines the property. - /// - public static readonly StyledProperty ColorModelProperty = - AvaloniaProperty.Register( - nameof(ColorModel), - ColorModel.Rgba); - /// /// Gets or sets the active color model used by the slider. /// @@ -60,14 +92,6 @@ namespace Avalonia.Controls.Primitives set => SetValue(ColorModelProperty, value); } - /// - /// Defines the property. - /// - public static readonly StyledProperty HsvColorProperty = - AvaloniaProperty.Register( - nameof(HsvColor), - Colors.White.ToHsv()); - /// /// Gets or sets the currently selected color in the HSV color model. /// @@ -81,14 +105,6 @@ namespace Avalonia.Controls.Primitives set => SetValue(HsvColorProperty, value); } - /// - /// Defines the property. - /// - public static readonly StyledProperty IsAlphaMaxForcedProperty = - AvaloniaProperty.Register( - nameof(IsAlphaMaxForced), - true); - /// /// Gets or sets a value indicating whether the alpha component is always forced to maximum for components /// other than . @@ -100,14 +116,6 @@ namespace Avalonia.Controls.Primitives set => SetValue(IsAlphaMaxForcedProperty, value); } - /// - /// Defines the property. - /// - public static readonly StyledProperty IsAutoUpdatingEnabledProperty = - AvaloniaProperty.Register( - nameof(IsAutoUpdatingEnabled), - true); - /// /// Gets or sets a value indicating whether automatic background and foreground updates will be /// calculated when the set color changes. @@ -121,14 +129,6 @@ namespace Avalonia.Controls.Primitives set => SetValue(IsAutoUpdatingEnabledProperty, value); } - /// - /// Defines the property. - /// - public static readonly StyledProperty IsSaturationValueMaxForcedProperty = - AvaloniaProperty.Register( - nameof(IsSaturationValueMaxForced), - true); - /// /// Gets or sets a value indicating whether the saturation and value components are always forced to maximum values /// when using the HSVA color model. Only component values other than will be changed. diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs index ab5b83afcb..a1cb43a95a 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.Properties.cs @@ -10,6 +10,74 @@ namespace Avalonia.Controls.Primitives /// public partial class ColorSpectrum { + /// + /// Defines the property. + /// + public static readonly StyledProperty ColorProperty = + AvaloniaProperty.Register( + nameof(Color), + Colors.White); + + /// + /// Defines the property. + /// + public static readonly StyledProperty ComponentsProperty = + AvaloniaProperty.Register( + nameof(Components), + ColorSpectrumComponents.HueSaturation); + + /// + /// Defines the property. + /// + public static readonly StyledProperty HsvColorProperty = + AvaloniaProperty.Register( + nameof(HsvColor), + Colors.White.ToHsv()); + + /// + /// Defines the property. + /// + public static readonly StyledProperty MaxHueProperty = + AvaloniaProperty.Register(nameof(MaxHue), 359); + + /// + /// Defines the property. + /// + public static readonly StyledProperty MaxSaturationProperty = + AvaloniaProperty.Register(nameof(MaxSaturation), 100); + + /// + /// Defines the property. + /// + public static readonly StyledProperty MaxValueProperty = + AvaloniaProperty.Register(nameof(MaxValue), 100); + + /// + /// Defines the property. + /// + public static readonly StyledProperty MinHueProperty = + AvaloniaProperty.Register(nameof(MinHue), 0); + + /// + /// Defines the property. + /// + public static readonly StyledProperty MinSaturationProperty = + AvaloniaProperty.Register(nameof(MinSaturation), 0); + + /// + /// Defines the property. + /// + public static readonly StyledProperty MinValueProperty = + AvaloniaProperty.Register(nameof(MinValue), 0); + + /// + /// Defines the property. + /// + public static readonly StyledProperty ShapeProperty = + AvaloniaProperty.Register( + nameof(Shape), + ColorSpectrumShape.Box); + /// /// Gets or sets the currently selected color in the RGB color model. /// @@ -23,14 +91,6 @@ namespace Avalonia.Controls.Primitives set => SetValue(ColorProperty, value); } - /// - /// Defines the property. - /// - public static readonly StyledProperty ColorProperty = - AvaloniaProperty.Register( - nameof(Color), - Colors.White); - /// /// Gets or sets the two HSV color components displayed by the spectrum. /// @@ -43,14 +103,6 @@ namespace Avalonia.Controls.Primitives set => SetValue(ComponentsProperty, value); } - /// - /// Defines the property. - /// - public static readonly StyledProperty ComponentsProperty = - AvaloniaProperty.Register( - nameof(Components), - ColorSpectrumComponents.HueSaturation); - /// /// Gets or sets the currently selected color in the HSV color model. /// @@ -65,14 +117,6 @@ namespace Avalonia.Controls.Primitives set => SetValue(HsvColorProperty, value); } - /// - /// Defines the property. - /// - public static readonly StyledProperty HsvColorProperty = - AvaloniaProperty.Register( - nameof(HsvColor), - Colors.White.ToHsv()); - /// /// Gets or sets the maximum value of the Hue component in the range from 0..359. /// This property must be greater than . @@ -86,12 +130,6 @@ namespace Avalonia.Controls.Primitives set => SetValue(MaxHueProperty, value); } - /// - /// Defines the property. - /// - public static readonly StyledProperty MaxHueProperty = - AvaloniaProperty.Register(nameof(MaxHue), 359); - /// /// Gets or sets the maximum value of the Saturation component in the range from 0..100. /// This property must be greater than . @@ -105,12 +143,6 @@ namespace Avalonia.Controls.Primitives set => SetValue(MaxSaturationProperty, value); } - /// - /// Defines the property. - /// - public static readonly StyledProperty MaxSaturationProperty = - AvaloniaProperty.Register(nameof(MaxSaturation), 100); - /// /// Gets or sets the maximum value of the Value component in the range from 0..100. /// This property must be greater than . @@ -124,12 +156,6 @@ namespace Avalonia.Controls.Primitives set => SetValue(MaxValueProperty, value); } - /// - /// Defines the property. - /// - public static readonly StyledProperty MaxValueProperty = - AvaloniaProperty.Register(nameof(MaxValue), 100); - /// /// Gets or sets the minimum value of the Hue component in the range from 0..359. /// This property must be less than . @@ -143,12 +169,6 @@ namespace Avalonia.Controls.Primitives set => SetValue(MinHueProperty, value); } - /// - /// Defines the property. - /// - public static readonly StyledProperty MinHueProperty = - AvaloniaProperty.Register(nameof(MinHue), 0); - /// /// Gets or sets the minimum value of the Saturation component in the range from 0..100. /// This property must be less than . @@ -162,12 +182,6 @@ namespace Avalonia.Controls.Primitives set => SetValue(MinSaturationProperty, value); } - /// - /// Defines the property. - /// - public static readonly StyledProperty MinSaturationProperty = - AvaloniaProperty.Register(nameof(MinSaturation), 0); - /// /// Gets or sets the minimum value of the Value component in the range from 0..100. /// This property must be less than . @@ -181,12 +195,6 @@ namespace Avalonia.Controls.Primitives set => SetValue(MinValueProperty, value); } - /// - /// Defines the property. - /// - public static readonly StyledProperty MinValueProperty = - AvaloniaProperty.Register(nameof(MinValue), 0); - /// /// Gets or sets the displayed shape of the spectrum. /// @@ -195,13 +203,5 @@ namespace Avalonia.Controls.Primitives get => GetValue(ShapeProperty); set => SetValue(ShapeProperty, value); } - - /// - /// Defines the property. - /// - public static readonly StyledProperty ShapeProperty = - AvaloniaProperty.Register( - nameof(Shape), - ColorSpectrumShape.Box); } } From 21190ff39bba3fcb242cf3225247b36d74b3d082 Mon Sep 17 00:00:00 2001 From: robloo Date: Wed, 27 Apr 2022 22:53:16 -0400 Subject: [PATCH 229/416] Add :dark-selector PseudoClass to ColorSpectrum This standardizes with ColorSlider (which requires three states) but so far isn't needed in the templates. --- .../ColorSpectrum/ColorSpectrum.cs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs index 4cabb5c5b7..563fa24c08 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorSpectrum/ColorSpectrum.cs @@ -29,10 +29,11 @@ namespace Avalonia.Controls.Primitives [TemplatePart("PART_SpectrumRectangle", typeof(Rectangle))] [TemplatePart("PART_SpectrumOverlayEllipse", typeof(Ellipse))] [TemplatePart("PART_SpectrumOverlayRectangle", typeof(Rectangle))] - [PseudoClasses(pcPressed, pcLargeSelector, pcLightSelector)] + [PseudoClasses(pcPressed, pcLargeSelector, pcDarkSelector, pcLightSelector)] public partial class ColorSpectrum : TemplatedControl { protected const string pcPressed = ":pressed"; + protected const string pcDarkSelector = ":dark-selector"; protected const string pcLargeSelector = ":large-selector"; protected const string pcLightSelector = ":light-selector"; @@ -556,7 +557,16 @@ namespace Avalonia.Controls.Primitives PseudoClasses.Set(pcLargeSelector, false); } - PseudoClasses.Set(pcLightSelector, SelectionEllipseShouldBeLight()); + if (SelectionEllipseShouldBeLight()) + { + PseudoClasses.Set(pcDarkSelector, false); + PseudoClasses.Set(pcLightSelector, true); + } + else + { + PseudoClasses.Set(pcDarkSelector, true); + PseudoClasses.Set(pcLightSelector, false); + } } private void UpdateColor(Hsv newHsv) From c3cdf856a3345f74413aed31febb61d4f214eb21 Mon Sep 17 00:00:00 2001 From: robloo Date: Wed, 27 Apr 2022 23:14:29 -0400 Subject: [PATCH 230/416] Add default themes for ColorSlider and ColorPreviewer --- .../Themes/Default.xaml | 21 ++ .../Themes/Default/ColorPreviewer.xaml | 90 ++++++++ .../Themes/Default/ColorSlider.xaml | 198 ++++++++++++++++++ .../Themes/Fluent/ColorSlider.xaml | 32 ++- 4 files changed, 332 insertions(+), 9 deletions(-) create mode 100644 src/Avalonia.Controls.ColorPicker/Themes/Default/ColorPreviewer.xaml create mode 100644 src/Avalonia.Controls.ColorPicker/Themes/Default/ColorSlider.xaml diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Default.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Default.xaml index 528eed9969..6d2f979f6e 100644 --- a/src/Avalonia.Controls.ColorPicker/Themes/Default.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Default.xaml @@ -1,7 +1,28 @@ + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Default/ColorPreviewer.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Default/ColorPreviewer.xaml new file mode 100644 index 0000000000..fe35dbd587 --- /dev/null +++ b/src/Avalonia.Controls.ColorPicker/Themes/Default/ColorPreviewer.xaml @@ -0,0 +1,90 @@ + + + + + + + + + diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Default/ColorSlider.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Default/ColorSlider.xaml new file mode 100644 index 0000000000..c0b78d628a --- /dev/null +++ b/src/Avalonia.Controls.ColorPicker/Themes/Default/ColorSlider.xaml @@ -0,0 +1,198 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSlider.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSlider.xaml index 620e9f658d..54f58d2a8f 100644 --- a/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSlider.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/ColorSlider.xaml @@ -8,6 +8,20 @@ + + - - - - - - - From a86e0cc64e18787cae2ebb7b531966ee8b3ec0dd Mon Sep 17 00:00:00 2001 From: robloo Date: Wed, 27 Apr 2022 23:19:02 -0400 Subject: [PATCH 231/416] Move ColorPicker theme definitions into theme folders --- samples/ControlCatalog/App.xaml.cs | 4 ++-- .../Themes/{ => Default}/Default.xaml | 0 .../Themes/{ => Fluent}/Fluent.xaml | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename src/Avalonia.Controls.ColorPicker/Themes/{ => Default}/Default.xaml (100%) rename src/Avalonia.Controls.ColorPicker/Themes/{ => Fluent}/Fluent.xaml (100%) diff --git a/samples/ControlCatalog/App.xaml.cs b/samples/ControlCatalog/App.xaml.cs index 6539cdaee6..7ebb87094a 100644 --- a/samples/ControlCatalog/App.xaml.cs +++ b/samples/ControlCatalog/App.xaml.cs @@ -20,12 +20,12 @@ namespace ControlCatalog public static readonly StyleInclude ColorPickerFluent = new StyleInclude(new Uri("avares://ControlCatalog/Styles")) { - Source = new Uri("avares://Avalonia.Controls.ColorPicker/Themes/Fluent.xaml") + Source = new Uri("avares://Avalonia.Controls.ColorPicker/Themes/Fluent/Fluent.xaml") }; public static readonly StyleInclude ColorPickerDefault = new StyleInclude(new Uri("avares://ControlCatalog/Styles")) { - Source = new Uri("avares://Avalonia.Controls.ColorPicker/Themes/Default.xaml") + Source = new Uri("avares://Avalonia.Controls.ColorPicker/Themes/Default/Default.xaml") }; public static readonly StyleInclude DataGridFluent = new StyleInclude(new Uri("avares://ControlCatalog/Styles")) diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Default.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Default/Default.xaml similarity index 100% rename from src/Avalonia.Controls.ColorPicker/Themes/Default.xaml rename to src/Avalonia.Controls.ColorPicker/Themes/Default/Default.xaml diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Fluent/Fluent.xaml similarity index 100% rename from src/Avalonia.Controls.ColorPicker/Themes/Fluent.xaml rename to src/Avalonia.Controls.ColorPicker/Themes/Fluent/Fluent.xaml From e0bc2e35c50e96563e9dd0f24cc33160cf8765be Mon Sep 17 00:00:00 2001 From: robloo Date: Wed, 27 Apr 2022 23:25:11 -0400 Subject: [PATCH 232/416] Add RgbComponent enum and support direct casting with all component enums --- .../ColorComponent.cs | 8 ++-- .../HsvComponent.cs | 22 +++++----- .../RgbComponent.cs | 42 +++++++++++++++++++ 3 files changed, 57 insertions(+), 15 deletions(-) create mode 100644 src/Avalonia.Controls.ColorPicker/RgbComponent.cs diff --git a/src/Avalonia.Controls.ColorPicker/ColorComponent.cs b/src/Avalonia.Controls.ColorPicker/ColorComponent.cs index a0385c03b4..71725056cf 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorComponent.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorComponent.cs @@ -8,21 +8,21 @@ /// /// Represents the alpha component. /// - Alpha, + Alpha = 0, /// /// Represents the first color component which is Red when RGB or Hue when HSV. /// - Component1, + Component1 = 1, /// /// Represents the second color component which is Green when RGB or Saturation when HSV. /// - Component2, + Component2 = 2, /// /// Represents the third color component which is Blue when RGB or Value when HSV. /// - Component3 + Component3 = 3 } } diff --git a/src/Avalonia.Controls.ColorPicker/HsvComponent.cs b/src/Avalonia.Controls.ColorPicker/HsvComponent.cs index 1132bd7bbb..1a7a13166a 100644 --- a/src/Avalonia.Controls.ColorPicker/HsvComponent.cs +++ b/src/Avalonia.Controls.ColorPicker/HsvComponent.cs @@ -12,13 +12,21 @@ namespace Avalonia.Controls /// public enum HsvComponent { + /// + /// The Alpha component. + /// + /// + /// Also see: + /// + Alpha = 0, + /// /// The Hue component. /// /// /// Also see: /// - Hue, + Hue = 1, /// /// The Saturation component. @@ -26,7 +34,7 @@ namespace Avalonia.Controls /// /// Also see: /// - Saturation, + Saturation = 2, /// /// The Value component. @@ -34,14 +42,6 @@ namespace Avalonia.Controls /// /// Also see: /// - Value, - - /// - /// The Alpha component. - /// - /// - /// Also see: - /// - Alpha + Value = 3 }; } diff --git a/src/Avalonia.Controls.ColorPicker/RgbComponent.cs b/src/Avalonia.Controls.ColorPicker/RgbComponent.cs new file mode 100644 index 0000000000..c3591573bb --- /dev/null +++ b/src/Avalonia.Controls.ColorPicker/RgbComponent.cs @@ -0,0 +1,42 @@ +using Avalonia.Media; + +namespace Avalonia.Controls +{ + /// + /// Defines a specific component in the RGB color model. + /// + public enum RgbComponent + { + /// + /// The Alpha component. + /// + /// + /// Also see: + /// + Alpha = 0, + + /// + /// The Red component. + /// + /// + /// Also see: + /// + Red = 1, + + /// + /// The Green component. + /// + /// + /// Also see: + /// + Green = 2, + + /// + /// The Blue component. + /// + /// + /// Also see: + /// + Blue = 3 + }; +} From c3ce137bda4483c51d2cca1b79c761ca326f6be8 Mon Sep 17 00:00:00 2001 From: robloo Date: Wed, 27 Apr 2022 23:27:05 -0400 Subject: [PATCH 233/416] Move AccentColorConverter in Converters directory --- .../{ColorPreviewer => Converters}/AccentColorConverter.cs | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/Avalonia.Controls.ColorPicker/{ColorPreviewer => Converters}/AccentColorConverter.cs (100%) diff --git a/src/Avalonia.Controls.ColorPicker/ColorPreviewer/AccentColorConverter.cs b/src/Avalonia.Controls.ColorPicker/Converters/AccentColorConverter.cs similarity index 100% rename from src/Avalonia.Controls.ColorPicker/ColorPreviewer/AccentColorConverter.cs rename to src/Avalonia.Controls.ColorPicker/Converters/AccentColorConverter.cs From f550b8f9e82fa0f02490316a2e0d07840d73b0b4 Mon Sep 17 00:00:00 2001 From: robloo Date: Wed, 27 Apr 2022 23:30:22 -0400 Subject: [PATCH 234/416] Move AccentColorConverter in Avalonia.Controls.Primitives.Converters namespace This better hides these special-purpose converters --- .../ColorPreviewer/ColorPreviewer.cs | 1 + .../Converters/AccentColorConverter.cs | 2 +- .../Themes/Default/ColorPreviewer.xaml | 4 ++-- .../Themes/Fluent/ColorPreviewer.xaml | 4 ++-- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.cs b/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.cs index 35072d6a42..3c429783d5 100644 --- a/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.cs +++ b/src/Avalonia.Controls.ColorPicker/ColorPreviewer/ColorPreviewer.cs @@ -1,6 +1,7 @@ using System; using System.Globalization; using Avalonia.Controls.Metadata; +using Avalonia.Controls.Primitives.Converters; using Avalonia.Input; using Avalonia.Media; diff --git a/src/Avalonia.Controls.ColorPicker/Converters/AccentColorConverter.cs b/src/Avalonia.Controls.ColorPicker/Converters/AccentColorConverter.cs index ad8f66251a..07ebc899db 100644 --- a/src/Avalonia.Controls.ColorPicker/Converters/AccentColorConverter.cs +++ b/src/Avalonia.Controls.ColorPicker/Converters/AccentColorConverter.cs @@ -3,7 +3,7 @@ using System.Globalization; using Avalonia.Data.Converters; using Avalonia.Media; -namespace Avalonia.Controls.Primitives +namespace Avalonia.Controls.Primitives.Converters { /// /// Creates an accent color for a given base color value and step parameter. diff --git a/src/Avalonia.Controls.ColorPicker/Themes/Default/ColorPreviewer.xaml b/src/Avalonia.Controls.ColorPicker/Themes/Default/ColorPreviewer.xaml index fe35dbd587..9100bf0440 100644 --- a/src/Avalonia.Controls.ColorPicker/Themes/Default/ColorPreviewer.xaml +++ b/src/Avalonia.Controls.ColorPicker/Themes/Default/ColorPreviewer.xaml @@ -1,10 +1,10 @@  - + - - - - diff --git a/src/Avalonia.Themes.Default/IBitmapToImageConverter.cs b/src/Avalonia.Themes.Default/IBitmapToImageConverter.cs new file mode 100644 index 0000000000..9b7fcecf45 --- /dev/null +++ b/src/Avalonia.Themes.Default/IBitmapToImageConverter.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Avalonia.Controls; +using Avalonia.Data.Converters; +using Avalonia.Media.Imaging; + +namespace Avalonia.Themes.Default +{ + internal class IBitmapToImageConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value != null && value is IBitmap bm) + return new Image { Source=bm }; + + return null; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj b/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj index 35603fe216..ede0791438 100644 --- a/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj +++ b/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj @@ -10,6 +10,15 @@ + + + + + + + MSBuild:Compile + + diff --git a/src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml b/src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml index 7860e08ef5..6251c86720 100644 --- a/src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml @@ -4,12 +4,13 @@ x:CompileBindings="True" Selector="NativeMenuBar"> - + + diff --git a/src/Avalonia.Themes.Fluent/IBitmapToImageConverter.cs b/src/Avalonia.Themes.Fluent/IBitmapToImageConverter.cs new file mode 100644 index 0000000000..34670882f8 --- /dev/null +++ b/src/Avalonia.Themes.Fluent/IBitmapToImageConverter.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Avalonia.Controls; +using Avalonia.Data.Converters; +using Avalonia.Media.Imaging; + +namespace Avalonia.Themes.Fluent +{ + internal class IBitmapToImageConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value != null && value is IBitmap bm) + return new Image { Source=bm }; + + return null; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} From 8772a46bbaab12d3d77b76fd97eed4cffd11e365 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Fri, 29 Apr 2022 14:26:28 +0200 Subject: [PATCH 253/416] Fix scaled text measurements --- src/Avalonia.Controls/Presenters/TextPresenter.cs | 4 +++- src/Avalonia.Controls/TextBlock.cs | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index db1bbdbc6c..c3912077ee 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -532,7 +532,9 @@ namespace Avalonia.Controls.Presenters return finalSize; } - _constraint = new Size(finalSize.Width, Math.Ceiling(finalSize.Height)); + var textSize = PixelSize.FromSize(finalSize, 1); + + _constraint = new Size(textSize.Width, textSize.Height); _textLayout = null; diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs index c04a62008b..7427f21134 100644 --- a/src/Avalonia.Controls/TextBlock.cs +++ b/src/Avalonia.Controls/TextBlock.cs @@ -626,9 +626,9 @@ namespace Avalonia.Controls var padding = Padding; - var textSize = finalSize.Deflate(padding); + var textSize = PixelSize.FromSize(finalSize.Deflate(padding), 1); - _constraint = new Size(textSize.Width, Math.Ceiling(textSize.Height)); + _constraint = new Size(textSize.Width, textSize.Height); _textLayout = null; From 2425cbf7aa2504b96b6fe441ef9522de92bbc98b Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Fri, 29 Apr 2022 17:03:05 +0200 Subject: [PATCH 254/416] Use RenderScaling in the ArrangeOverride --- src/Avalonia.Controls/Presenters/TextPresenter.cs | 2 +- src/Avalonia.Controls/TextBlock.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index c3912077ee..eecf0d9101 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -532,7 +532,7 @@ namespace Avalonia.Controls.Presenters return finalSize; } - var textSize = PixelSize.FromSize(finalSize, 1); + var textSize = PixelSize.FromSize(finalSize, VisualRoot?.RenderScaling ?? 1); _constraint = new Size(textSize.Width, textSize.Height); diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs index 7427f21134..1087310841 100644 --- a/src/Avalonia.Controls/TextBlock.cs +++ b/src/Avalonia.Controls/TextBlock.cs @@ -626,7 +626,7 @@ namespace Avalonia.Controls var padding = Padding; - var textSize = PixelSize.FromSize(finalSize.Deflate(padding), 1); + var textSize = PixelSize.FromSize(finalSize.Deflate(padding), VisualRoot?.RenderScaling ?? 1); _constraint = new Size(textSize.Width, textSize.Height); From bf61dd9a1ed4237ac611c2d986b12a7e64f33216 Mon Sep 17 00:00:00 2001 From: Lubomir Tetak Date: Mon, 2 May 2022 10:17:00 +0200 Subject: [PATCH 255/416] macos - disable native menus completely --- native/Avalonia.Native/src/OSX/window.mm | 4 ---- 1 file changed, 4 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index d16c466fe6..6adb177ae9 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -2233,10 +2233,6 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent [NSApp setMenu:nativeAppMenu->GetNative()]; } - else - { - [NSApp setMenu:nullptr]; - } } -(void) applyMenu:(AvnMenu *)menu From a056f0b654ae220ddbb58a2648fd286676769fef Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Mon, 2 May 2022 11:39:08 +0200 Subject: [PATCH 256/416] Second attempt to fix text measurements with scaling --- .../Presenters/TextPresenter.cs | 27 ++++++++++++------- src/Avalonia.Controls/TextBlock.cs | 19 +++++++------ 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index eecf0d9101..0785149a73 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -511,30 +511,39 @@ namespace Avalonia.Controls.Presenters InvalidateMeasure(); } - + + protected override Size MeasureOverride(Size availableSize) { + if (string.IsNullOrEmpty(Text)) + { + return new Size(); + } + _constraint = availableSize; - + _textLayout = null; - + InvalidateArrange(); - var measuredSize = PixelSize.FromSize(TextLayout.Bounds.Size, 1); - - return new Size(measuredSize.Width, measuredSize.Height); + var measuredSize = TextLayout.Bounds.Size; + + return measuredSize; } protected override Size ArrangeOverride(Size finalSize) { + if (finalSize.Width < TextLayout.Bounds.Width) + { + finalSize = finalSize.WithWidth(TextLayout.Bounds.Width); + } + if (MathUtilities.AreClose(_constraint.Width, finalSize.Width)) { return finalSize; } - var textSize = PixelSize.FromSize(finalSize, VisualRoot?.RenderScaling ?? 1); - - _constraint = new Size(textSize.Width, textSize.Height); + _constraint = new Size(finalSize.Width, double.PositiveInfinity); _textLayout = null; diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs index 1087310841..bbe6aeb7ee 100644 --- a/src/Avalonia.Controls/TextBlock.cs +++ b/src/Avalonia.Controls/TextBlock.cs @@ -604,7 +604,9 @@ namespace Avalonia.Controls return new Size(); } - var padding = Padding; + var scale = LayoutHelper.GetLayoutScale(this); + + var padding = LayoutHelper.RoundLayoutThickness(Padding, scale, scale); _constraint = availableSize.Deflate(padding); @@ -612,23 +614,24 @@ namespace Avalonia.Controls InvalidateArrange(); - var measuredSize = PixelSize.FromSize(TextLayout.Bounds.Size, 1); + var measuredSize = TextLayout.Bounds.Size.Inflate(padding); - return new Size(measuredSize.Width, measuredSize.Height).Inflate(padding); + return measuredSize; } protected override Size ArrangeOverride(Size finalSize) { + if(finalSize.Width < TextLayout.Bounds.Width) + { + finalSize = finalSize.WithWidth(TextLayout.Bounds.Width); + } + if (MathUtilities.AreClose(_constraint.Width, finalSize.Width)) { return finalSize; } - var padding = Padding; - - var textSize = PixelSize.FromSize(finalSize.Deflate(padding), VisualRoot?.RenderScaling ?? 1); - - _constraint = new Size(textSize.Width, textSize.Height); + _constraint = new Size(finalSize.Width, double.PositiveInfinity); _textLayout = null; From bb8aaee1e0da5116d06febbe8aa512add3be0ce0 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Mon, 2 May 2022 23:09:13 +0200 Subject: [PATCH 257/416] Optimizing resource related code. --- .../Controls/ResourceNodeExtensions.cs | 11 ++++------- src/Avalonia.Base/Styling/IStyle.cs | 2 +- src/Avalonia.Base/Styling/Styles.cs | 2 +- .../Controls/DataValidationErrors.xaml | 10 +++++++--- .../MarkupExtensions/StaticResourceExtension.cs | 17 +++++++---------- .../Styling/StyleInclude.cs | 6 +++--- 6 files changed, 23 insertions(+), 25 deletions(-) diff --git a/src/Avalonia.Base/Controls/ResourceNodeExtensions.cs b/src/Avalonia.Base/Controls/ResourceNodeExtensions.cs index 513b3f2424..1758c45650 100644 --- a/src/Avalonia.Base/Controls/ResourceNodeExtensions.cs +++ b/src/Avalonia.Base/Controls/ResourceNodeExtensions.cs @@ -40,19 +40,16 @@ namespace Avalonia.Controls control = control ?? throw new ArgumentNullException(nameof(control)); key = key ?? throw new ArgumentNullException(nameof(key)); - IResourceHost? current = control; + IResourceNode? current = control; while (current != null) { - if (current is IResourceHost host) + if (current.TryGetResource(key, out value)) { - if (host.TryGetResource(key, out value)) - { - return true; - } + return true; } - current = (current as IStyledElement)?.StylingParent as IResourceHost; + current = (current as IStyledElement)?.StylingParent as IResourceNode; } value = null; diff --git a/src/Avalonia.Base/Styling/IStyle.cs b/src/Avalonia.Base/Styling/IStyle.cs index 78fbe0f2b5..738a69cb88 100644 --- a/src/Avalonia.Base/Styling/IStyle.cs +++ b/src/Avalonia.Base/Styling/IStyle.cs @@ -8,7 +8,7 @@ namespace Avalonia.Styling /// /// Defines the interface for styles. /// - public interface IStyle + public interface IStyle : IResourceNode { /// /// Gets a collection of child styles. diff --git a/src/Avalonia.Base/Styling/Styles.cs b/src/Avalonia.Base/Styling/Styles.cs index 81502f1570..d79081152e 100644 --- a/src/Avalonia.Base/Styling/Styles.cs +++ b/src/Avalonia.Base/Styling/Styles.cs @@ -160,7 +160,7 @@ namespace Avalonia.Styling for (var i = Count - 1; i >= 0; --i) { - if (this[i] is IResourceProvider p && p.TryGetResource(key, out value)) + if (this[i].TryGetResource(key, out value)) { return true; } diff --git a/src/Avalonia.Themes.Default/Controls/DataValidationErrors.xaml b/src/Avalonia.Themes.Default/Controls/DataValidationErrors.xaml index a3a4cf4662..d7bf4bbbf1 100644 --- a/src/Avalonia.Themes.Default/Controls/DataValidationErrors.xaml +++ b/src/Avalonia.Themes.Default/Controls/DataValidationErrors.xaml @@ -1,9 +1,13 @@ - + diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs index db33b88cc3..f865f87220 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs @@ -39,17 +39,11 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions targetType = setter.Property.PropertyType; } - // Look upwards though the ambient context for IResourceHosts and IResourceProviders + // Look upwards though the ambient context for IResourceNodes // which might be able to give us the resource. - foreach (var e in stack.Parents) + foreach (var parent in stack.Parents) { - object value; - - if (e is IResourceHost host && host.TryGetResource(ResourceKey, out value)) - { - return ColorToBrushConverter.Convert(value, targetType); - } - else if (e is IResourceProvider provider && provider.TryGetResource(ResourceKey, out value)) + if (parent is IResourceNode node && node.TryGetResource(ResourceKey, out var value)) { return ColorToBrushConverter.Convert(value, targetType); } @@ -58,7 +52,10 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions if (provideTarget.TargetObject is IControl target && provideTarget.TargetProperty is PropertyInfo property) { - DelayedBinding.Add(target, property, x => GetValue(x, targetType)); + var localTargetType = targetType; + var localInstance = this; + + DelayedBinding.Add(target, property, x => localInstance.GetValue(x, localTargetType)); return AvaloniaProperty.UnsetValue; } diff --git a/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs b/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs index 607b552c28..fa4a27fc50 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs @@ -60,7 +60,7 @@ namespace Avalonia.Markup.Xaml.Styling } } - bool IResourceNode.HasResources => (Loaded as IResourceProvider)?.HasResources ?? false; + bool IResourceNode.HasResources => Loaded?.HasResources ?? false; IReadOnlyList IStyle.Children => _loaded ?? Array.Empty(); @@ -86,9 +86,9 @@ namespace Avalonia.Markup.Xaml.Styling public bool TryGetResource(object key, out object? value) { - if (!_isLoading && Loaded is IResourceProvider p) + if (!_isLoading) { - return p.TryGetResource(key, out value); + return Loaded.TryGetResource(key, out value); } value = null; From 4aa0f878c2890dca2ade446832192dafae5d0675 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Mon, 2 May 2022 23:25:18 +0200 Subject: [PATCH 258/416] Add an explanation why certain locals are copied. --- .../MarkupExtensions/StaticResourceExtension.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs index f865f87220..add97a660b 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs @@ -52,6 +52,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions if (provideTarget.TargetObject is IControl target && provideTarget.TargetProperty is PropertyInfo property) { + // This is stored locally to avoid allocating closure in the outer scope. var localTargetType = targetType; var localInstance = this; From 0a8d679ab29e3364cce0b716d7c9bc9f55380441 Mon Sep 17 00:00:00 2001 From: Tim Date: Tue, 3 May 2022 11:54:35 +0200 Subject: [PATCH 259/416] Register StretchProperty for Viewbox instead of Image --- src/Avalonia.Controls/Viewbox.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Viewbox.cs b/src/Avalonia.Controls/Viewbox.cs index dd74d549bd..33a05f126d 100644 --- a/src/Avalonia.Controls/Viewbox.cs +++ b/src/Avalonia.Controls/Viewbox.cs @@ -14,7 +14,7 @@ namespace Avalonia.Controls /// Defines the property. /// public static readonly StyledProperty StretchProperty = - AvaloniaProperty.Register(nameof(Stretch), Stretch.Uniform); + AvaloniaProperty.Register(nameof(Stretch), Stretch.Uniform); /// /// Defines the property. From bdadb6a35111899747c353aa01a1ab334805a384 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Tue, 3 May 2022 12:04:26 +0200 Subject: [PATCH 260/416] Fix GetTextBounds for mixed runs --- .../Media/TextFormatting/TextLineImpl.cs | 47 ++++-------------- src/Skia/Avalonia.Skia/TextShaperImpl.cs | 7 +-- .../Media/TextShaperImpl.cs | 33 +++++------- ...estrictedHeight_VerticalAlign.expected.png | Bin 768 -> 752 bytes ...estrictedHeight_VerticalAlign.expected.png | Bin 532 -> 557 bytes 5 files changed, 24 insertions(+), 63 deletions(-) diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs index 35ada3c7ee..a518e2ffb8 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs @@ -536,6 +536,11 @@ namespace Avalonia.Media.TextFormatting endX += currentRun.Size.Width; } + if(currentPosition < firstTextSourceCharacterIndex) + { + startX += currentRun.Size.Width; + } + currentPosition += currentRun.TextSourceLength; break; @@ -590,10 +595,10 @@ namespace Avalonia.Media.TextFormatting public TextLineImpl FinalizeLine() { - BidiReorder(); - _textLineMetrics = CreateLineMetrics(); + BidiReorder(); + return this; } @@ -1068,41 +1073,11 @@ namespace Avalonia.Media.TextFormatting } } - switch (_paragraphProperties.FlowDirection) + if (index == _textRuns.Count - 1) { - case FlowDirection.LeftToRight: - { - if (index == _textRuns.Count - 1) - { - width = widthIncludingWhitespace + textRun.GlyphRun.Metrics.Width; - trailingWhitespaceLength = textRun.GlyphRun.Metrics.TrailingWhitespaceLength; - newLineLength = textRun.GlyphRun.Metrics.NewlineLength; - } - - break; - } - - case FlowDirection.RightToLeft: - { - if (index == _textRuns.Count - 1) - { - var firstRun = _textRuns[0]; - - if (firstRun is ShapedTextCharacters shapedTextCharacters) - { - var offset = shapedTextCharacters.GlyphRun.Metrics.WidthIncludingTrailingWhitespace - - shapedTextCharacters.GlyphRun.Metrics.Width; - - width = widthIncludingWhitespace + - textRun.GlyphRun.Metrics.WidthIncludingTrailingWhitespace - offset; - - trailingWhitespaceLength = shapedTextCharacters.GlyphRun.Metrics.TrailingWhitespaceLength; - newLineLength = shapedTextCharacters.GlyphRun.Metrics.NewlineLength; - } - } - - break; - } + width = widthIncludingWhitespace + textRun.GlyphRun.Metrics.Width; + trailingWhitespaceLength = textRun.GlyphRun.Metrics.TrailingWhitespaceLength; + newLineLength = textRun.GlyphRun.Metrics.NewlineLength; } widthIncludingWhitespace += textRun.GlyphRun.Metrics.WidthIncludingTrailingWhitespace; diff --git a/src/Skia/Avalonia.Skia/TextShaperImpl.cs b/src/Skia/Avalonia.Skia/TextShaperImpl.cs index ebaa247da8..908b0ffa47 100644 --- a/src/Skia/Avalonia.Skia/TextShaperImpl.cs +++ b/src/Skia/Avalonia.Skia/TextShaperImpl.cs @@ -27,7 +27,7 @@ namespace Avalonia.Skia buffer.GuessSegmentProperties(); - buffer.Direction = (bidiLevel & 1) == 0 ? Direction.LeftToRight : Direction.RightToLeft; + buffer.Direction = Direction.LeftToRight; //Always shape LeftToRight buffer.Language = new Language(culture ?? CultureInfo.CurrentCulture); @@ -35,11 +35,6 @@ namespace Avalonia.Skia font.Shape(buffer); - if (buffer.Direction == Direction.RightToLeft) - { - buffer.Reverse(); - } - font.GetScale(out var scaleX, out _); var textScale = fontRenderingEmSize / scaleX; diff --git a/src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs index 59027a663f..f4e4b00147 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs @@ -1,6 +1,5 @@ using System; using System.Globalization; -using Avalonia.Media; using Avalonia.Media.TextFormatting; using Avalonia.Media.TextFormatting.Unicode; using Avalonia.Platform; @@ -11,8 +10,7 @@ using GlyphInfo = HarfBuzzSharp.GlyphInfo; namespace Avalonia.Direct2D1.Media { - -internal class TextShaperImpl : ITextShaperImpl + internal class TextShaperImpl : ITextShaperImpl { public ShapedBuffer ShapeText(ReadOnlySlice text, TextShaperOptions options) { @@ -23,25 +21,20 @@ internal class TextShaperImpl : ITextShaperImpl using (var buffer = new Buffer()) { - buffer.AddUtf16(text.Buffer.Span, text.Start, text.Length); + buffer.AddUtf16(text.Buffer.Span, text.BufferOffset, text.Length); MergeBreakPair(buffer); - + buffer.GuessSegmentProperties(); - buffer.Direction = (bidiLevel & 1) == 0 ? Direction.LeftToRight : Direction.RightToLeft; + buffer.Direction = Direction.LeftToRight; //Always shape LeftToRight - buffer.Language = new Language(culture ?? CultureInfo.CurrentCulture); + buffer.Language = new Language(culture ?? CultureInfo.CurrentCulture); var font = ((GlyphTypefaceImpl)typeface.PlatformImpl).Font; font.Shape(buffer); - if (buffer.Direction == Direction.RightToLeft) - { - buffer.Reverse(); - } - font.GetScale(out var scaleX, out _); var textScale = fontRenderingEmSize / scaleX; @@ -60,13 +53,13 @@ internal class TextShaperImpl : ITextShaperImpl var glyphIndex = (ushort)sourceInfo.Codepoint; - var glyphCluster = (int)sourceInfo.Cluster; + var glyphCluster = (int)(sourceInfo.Cluster); var glyphAdvance = GetGlyphAdvance(glyphPositions, i, textScale); var glyphOffset = GetGlyphOffset(glyphPositions, i, textScale); - if (glyphIndex == 0 && text[glyphCluster] == '\t') + if (glyphIndex == 0 && text.Buffer.Span[glyphCluster] == '\t') { glyphIndex = typeface.GetGlyph(' '); @@ -75,9 +68,7 @@ internal class TextShaperImpl : ITextShaperImpl 4 * typeface.GetGlyphAdvance(glyphIndex) * textScale; } - var targetInfo = - new Avalonia.Media.TextFormatting.GlyphInfo(glyphIndex, glyphCluster, glyphAdvance, - glyphOffset); + var targetInfo = new Avalonia.Media.TextFormatting.GlyphInfo(glyphIndex, glyphCluster, glyphAdvance, glyphOffset); shapedBuffer[i] = targetInfo; } @@ -91,7 +82,7 @@ internal class TextShaperImpl : ITextShaperImpl var length = buffer.Length; var glyphInfos = buffer.GetGlyphInfoSpan(); - + var second = glyphInfos[length - 1]; if (!new Codepoint((int)second.Codepoint).IsBreakChar) @@ -102,7 +93,7 @@ internal class TextShaperImpl : ITextShaperImpl if (length > 1 && glyphInfos[length - 2].Codepoint == '\r' && second.Codepoint == '\n') { var first = glyphInfos[length - 2]; - + first.Codepoint = '\u200C'; second.Codepoint = '\u200C'; second.Cluster = first.Cluster; @@ -113,7 +104,7 @@ internal class TextShaperImpl : ITextShaperImpl { *p = first; } - + fixed (GlyphInfo* p = &glyphInfos[length - 1]) { *p = second; @@ -148,7 +139,7 @@ internal class TextShaperImpl : ITextShaperImpl private static double GetGlyphAdvance(ReadOnlySpan glyphPositions, int index, double textScale) { // Depends on direction of layout - // advanceBuffer[index] = buffer.GlyphPositions[index].YAdvance * textScale; + // glyphPositions[index].YAdvance * textScale; return glyphPositions[index].XAdvance * textScale; } } diff --git a/tests/TestFiles/Direct2D1/Controls/TextBlock/RestrictedHeight_VerticalAlign.expected.png b/tests/TestFiles/Direct2D1/Controls/TextBlock/RestrictedHeight_VerticalAlign.expected.png index edd4dfd263d0aeddca97eb4f38b4a5310ef6441f..e8624fa457f37565fdc483c474424991e7b696d3 100644 GIT binary patch delta 663 zcmV;I0%-k!2Ji)tK~kDYL_t(|UhUgIa??N*$8nhhgpv-9Kst&PbZ~-{bWo>HI%eP$ zbliY0bTFi#WQHbHCZVECE@0mCYC&Ev)~@tc(Vy>U=9gVr&L&sF5? z2a=zgt*ZJWIdk3Ow4gWZj_kMw*zr#C&2{Yze1z^>+>Eo|l*ZA6u^yCDYTA#}F7b|b z1-+DUQlBp7XTR-n+9lr6_JMtu-Y0j%e~v?mF0?|+c*2;U(Dpd(9@K4;4-y?YmRFK4 z^g(@eT|_WHMd&L`zBFy;-KXYy|(KU{b8zDnNKxsvFW zH-0;ezHL90^u_#aD^}3IBtx9|jEVjQPyh0jnrD(F8Df4|Lt|DbXw1sJ6tu3b_2LwN zfr_3W`0X%Otk(2a znEu5s`c3DXRyVzJM1r%nkRJ0aMM(FjDeD5@R(^af#<|TF^PCdjI796AKN{2Wx1| x3I&Z>NnUc)osL*?lhFbeli&gb8GzoZ>JL{KmA-v_Vov}7002ovPDHLkV1g47Q*QtO delta 680 zcmV;Z0$2U;1%L*SK~kzoL_t(|UhUg4a??N*#&MYggpv-903AgNIyeCxbfl&a9W!tW zI&MH0Iv6M@nW3S|1S(3nfO#)Z%WR}I+O^(-y!!uUzG!9H8)yBZ?8;6DlfeQOe*gdg zXxpaWPS|ey#tfUx?*;5HyP&tZcTL!I`ujjZqdp26OL9YU+nOU6lCRd>je^FKJd`}O zW_MF^B>8f=*LCkC {7WCyrdau|9DDpz`!J7I0!x`G6xaq6RA@-#wV?8P7*0lB2 zcR0njpx4+}$;-od?LS_b&&a;ve=BL7*bO6(B(JSGl6vx=r1i#b7`Y?S$yCbPULeM6 zTHMz){gUg6L?5a-l^jR=yNWr7KJht^UmM12HvJ8fFHxkRw^Gof+f%VkP`9s=*e0mk z7vps`wh8=x#-cMc>d`3$^6eTg3VIo*Bf3vR=Kl|;! z(DHaSQP7@?)mjVRC24uQm~x;g)e2hQi1%9P=%2M8);uWsoOPb3`6l@l?GNK=&Er+A zpubDJ=7suZ&Sz^Ll=qV7ea`gjQ**ChgxBkvx2!pv=VH~fuM&*cZ0&rgMIUtbr>&cyZeJy_O~80f#WsdHG}g%XEHOVp z>R4%l)Unb8sbi%HQpZXYq>i0Rjt95J(bw-R-tWct3GwW!G(|iMC>jgkxg4sswAxF~ z1qq=Aoq5-UtN(5T)WaMaO`)LClwW{S>gC-`V7Q6^ O0000Wy2)Tif!}f*BA9YQBr<>Yv<0+ed$LhGhl$6qoFtRV$DAqv>tEFoHy;= zjj8AUHLmvgqjP}4f0`Ym-l@|ncRSzK*B<`-V%Iyxa-Mt3j*FJP@}0!LwOcIxMdrlE z`EBc_9=&?AZmz%QzU4W~PG3(r%l<3q)s%g)(bchAU-i5`|6g~~!uOv(cvj{7-f@I; zZO^|R^Na=Arn~kV?eHzSTz#h~bwk)M4ct2k#G&)%(W*Dsx)C6j$|eyBU2 z#3_f(+TH&xR=QqWGxcS(?x8Q~dmJTy>788NygzpSjqgWi)>y6GoVM2Iepc=R`^UZi z3We58xyrynyb(IzS%pj{N(kv(qntpW!+KP9r*m+Cc8U!>XmlI yb8njNTQ2ZS)p@fv+pJj4eL2tLs~9ojqI);H&U7`F+XGKFP=N&RV07srr_IdAW5%)1;Q!uDXlX#W9ggKvom4qEyL4~4P0&0{{ck2_jY zz?fNE$h%Z>(W=lXQ?j31ZCk(J|C5mA@slS#KgDbNF)^S4hwFN;%CwgrT&UI0`gre( zyBn{Z`^PAr`Jt|XYkILgL&iym%_{3BPpMbF-QIRwbLpF=FDusivZW`ln|z07Z}8Ve zAHMMX-Jp@P^FYYz`%&?e|M>j%d7pLG=tA@w(@X6Ni@OMP+s%{|8UZRef2 zOxJB?{S~_W*Ou5%Gj2?N7|#)W?H1GDRgZPpV+}8q9Cgq?Fkj{U;Tz2DaF{%vno$(gm^S9kO54e|f1`v3Z(cq0>#J*i*%C*Mn*p3gV8zGmi= zcRgFP)>dv@6<_N1-{NIR@src0`e9#J_nmMI=P`-C@@dwg Date: Tue, 3 May 2022 14:38:34 +0200 Subject: [PATCH 261/416] Fix crash when property getter throws --- .../ViewModels/AvaloniaPropertyViewModel.cs | 64 +++++++++++++------ .../ViewModels/ClrPropertyViewModel.cs | 41 ++++++++---- 2 files changed, 73 insertions(+), 32 deletions(-) diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs index aa03330cc5..0a2a6cac25 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs @@ -1,13 +1,17 @@ +using System; +using Avalonia.Data; +using Avalonia.Media; + namespace Avalonia.Diagnostics.ViewModels { internal class AvaloniaPropertyViewModel : PropertyViewModel { private readonly AvaloniaObject _target; - private System.Type _assignedType; + private Type _assignedType; private object? _value; private string _priority; private string _group; - private readonly System.Type _propertyType; + private readonly Type _propertyType; #nullable disable // Remove "nullable disable" after MemberNotNull will work on our CI. @@ -28,13 +32,9 @@ namespace Avalonia.Diagnostics.ViewModels public AvaloniaProperty Property { get; } public override object Key => Property; public override string Name { get; } - public override bool? IsAttached => - Property.IsAttached; - - public override string Priority => - _priority; - - public override System.Type AssignedType => _assignedType; + public override bool? IsAttached => Property.IsAttached; + public override string Priority => _priority; + public override Type AssignedType => _assignedType; public override string? Value { @@ -53,30 +53,58 @@ namespace Avalonia.Diagnostics.ViewModels public override string Group => _group; - public override System.Type? DeclaringType { get; } - public override System.Type PropertyType => _propertyType; + public override Type? DeclaringType { get; } + public override Type PropertyType => _propertyType; // [MemberNotNull(nameof(_type), nameof(_group), nameof(_priority))] public override void Update() { if (Property.IsDirect) { - RaiseAndSetIfChanged(ref _value, _target.GetValue(Property), nameof(Value)); - RaiseAndSetIfChanged(ref _assignedType,_value?.GetType() ?? Property.PropertyType, nameof(AssignedType)); + object? value; + Type? valueType = null; + + try + { + value = _target.GetValue(Property); + valueType = value?.GetType(); + } + catch (Exception e) + { + value = e; + } + + RaiseAndSetIfChanged(ref _value, value, nameof(Value)); + RaiseAndSetIfChanged(ref _assignedType, valueType ?? Property.PropertyType, nameof(AssignedType)); RaiseAndSetIfChanged(ref _priority, "Direct", nameof(Priority)); _group = "Properties"; } else { - var val = _target.GetDiagnostic(Property); + object? value; + Type? valueType = null; + BindingPriority? priority = null; + + try + { + var diag = _target.GetDiagnostic(Property); + + value = diag.Value; + valueType = value?.GetType(); + priority = diag.Priority; + } + catch (Exception e) + { + value = e; + } - RaiseAndSetIfChanged(ref _value, val?.Value, nameof(Value)); - RaiseAndSetIfChanged(ref _assignedType, _value?.GetType() ?? Property.PropertyType, nameof(AssignedType)); + RaiseAndSetIfChanged(ref _value, value, nameof(Value)); + RaiseAndSetIfChanged(ref _assignedType, valueType ?? Property.PropertyType, nameof(AssignedType)); - if (val != null) + if (priority != null) { - RaiseAndSetIfChanged(ref _priority, val.Priority.ToString(), nameof(Priority)); + RaiseAndSetIfChanged(ref _priority, priority.ToString()!, nameof(Priority)); RaiseAndSetIfChanged(ref _group, IsAttached == true ? "Attached Properties" : "Properties", nameof(Group)); } else diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs index e2d8a30c8a..296413de78 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs @@ -1,13 +1,15 @@ -using System.Reflection; +using System; +using System.Reflection; +using Avalonia.Media; namespace Avalonia.Diagnostics.ViewModels { internal class ClrPropertyViewModel : PropertyViewModel { private readonly object _target; - private System.Type _assignedType; + private Type _assignedType; private object? _value; - private readonly System.Type _propertyType; + private readonly Type _propertyType; #nullable disable // Remove "nullable disable" after MemberNotNull will work on our CI. @@ -25,6 +27,7 @@ namespace Avalonia.Diagnostics.ViewModels { Name = property.DeclaringType.Name + '.' + property.Name; } + DeclaringType = property.DeclaringType; _propertyType = property.PropertyType; @@ -36,10 +39,10 @@ namespace Avalonia.Diagnostics.ViewModels public override string Name { get; } public override string Group => "CLR Properties"; - public override System.Type AssignedType => _assignedType; - public override System.Type PropertyType => _propertyType; + public override Type AssignedType => _assignedType; + public override Type PropertyType => _propertyType; - public override string? Value + public override string? Value { get => ConvertToString(_value); set @@ -54,20 +57,30 @@ namespace Avalonia.Diagnostics.ViewModels } } - public override string Priority => - string.Empty; + public override string Priority => string.Empty; - public override bool? IsAttached => - default; + public override bool? IsAttached => default; - public override System.Type? DeclaringType { get; } + public override Type? DeclaringType { get; } // [MemberNotNull(nameof(_type))] public override void Update() { - var val = Property.GetValue(_target); - RaiseAndSetIfChanged(ref _value, val, nameof(Value)); - RaiseAndSetIfChanged(ref _assignedType, _value?.GetType() ?? Property.PropertyType, nameof(AssignedType)); + object? value; + Type? valueType = null; + + try + { + value = Property.GetValue(_target); + valueType = value?.GetType(); + } + catch (Exception e) + { + value = e; + } + + RaiseAndSetIfChanged(ref _value, value, nameof(Value)); + RaiseAndSetIfChanged(ref _assignedType, valueType ?? Property.PropertyType, nameof(AssignedType)); RaisePropertyChanged(nameof(Type)); } } From b5391f9419bd32f6410dc8c378ee3db3a9ad2e54 Mon Sep 17 00:00:00 2001 From: Benedikt Stebner Date: Tue, 3 May 2022 16:34:15 +0200 Subject: [PATCH 262/416] Fix GetTextBounds for some Bidi scenarios --- .../Media/TextFormatting/TextLineImpl.cs | 41 +++++++++++-------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs index a518e2ffb8..73ec055bbe 100644 --- a/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs +++ b/src/Avalonia.Base/Media/TextFormatting/TextLineImpl.cs @@ -252,7 +252,7 @@ namespace Avalonia.Media.TextFormatting //Look at the left and right edge of the current run if (currentRun.IsLeftToRight) { - if (lastRun == null || lastRun.IsLeftToRight) + if (_flowDirection == FlowDirection.LeftToRight && (lastRun == null || lastRun.IsLeftToRight)) { if (characterIndex <= textRun.Text.Start) { @@ -455,7 +455,7 @@ namespace Avalonia.Media.TextFormatting } default: { - goto noop; + goto noop; } } @@ -536,7 +536,7 @@ namespace Avalonia.Media.TextFormatting endX += currentRun.Size.Width; } - if(currentPosition < firstTextSourceCharacterIndex) + if (currentPosition < firstTextSourceCharacterIndex) { startX += currentRun.Size.Width; } @@ -554,24 +554,29 @@ namespace Avalonia.Media.TextFormatting var width = endX - startX; - if (lastDirection == currentDirection && result.Count > 0 && MathUtilities.AreClose(currentRect.Right, startX)) + if (!MathUtilities.IsZero(width)) { - currentRect = currentRect.WithWidth(currentRect.Width + width); + if (lastDirection == currentDirection && result.Count > 0 && MathUtilities.AreClose(currentRect.Right, startX)) + { + currentRect = currentRect.WithWidth(currentRect.Width + width); - var textBounds = new TextBounds(currentRect, currentDirection); + var textBounds = new TextBounds(currentRect, currentDirection); - result[result.Count - 1] = textBounds; - } - else - { - currentRect = new Rect(startX, 0, width, Height); + result[result.Count - 1] = textBounds; + } + else + { + + currentRect = new Rect(startX, 0, width, Height); - result.Add(new TextBounds(currentRect, currentDirection)); + result.Add(new TextBounds(currentRect, currentDirection)); + + } } if (currentDirection == FlowDirection.LeftToRight) { - if (currentPosition >= firstTextSourceCharacterIndex + textLength) + if (currentPosition > firstTextSourceCharacterIndex + textLength) { break; } @@ -1026,7 +1031,7 @@ namespace Avalonia.Media.TextFormatting var glyphTypeface = _paragraphProperties.DefaultTextRunProperties.Typeface.GlyphTypeface; var fontRenderingEmSize = _paragraphProperties.DefaultTextRunProperties.FontRenderingEmSize; var scale = fontRenderingEmSize / glyphTypeface.DesignEmHeight; - + var width = 0d; var widthIncludingWhitespace = 0d; var trailingWhitespaceLength = 0; @@ -1036,8 +1041,8 @@ namespace Avalonia.Media.TextFormatting var lineGap = glyphTypeface.LineGap * scale; var height = descent - ascent + lineGap; - - var lineHeight = _paragraphProperties.LineHeight; + + var lineHeight = _paragraphProperties.LineHeight; for (var index = 0; index < _textRuns.Count; index++) { @@ -1136,10 +1141,10 @@ namespace Avalonia.Media.TextFormatting if (!double.IsNaN(lineHeight) && !MathUtilities.IsZero(lineHeight)) { - if(lineHeight > height) + if (lineHeight > height) { height = lineHeight; - } + } } return new TextLineMetrics(widthIncludingWhitespace > _paragraphWidth, height, newLineLength, start, From 6a8eb5a1cf551503c2090e3a0b8dfdc4eec8497c Mon Sep 17 00:00:00 2001 From: peter kuhn Date: Wed, 4 May 2022 06:58:39 +0200 Subject: [PATCH 263/416] Fix Typo --- src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj | 9 --------- src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml | 4 ++-- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj b/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj index ede0791438..35603fe216 100644 --- a/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj +++ b/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj @@ -10,15 +10,6 @@ - - - - - - - MSBuild:Compile - - diff --git a/src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml b/src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml index 6251c86720..d40ba0cc1d 100644 --- a/src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml @@ -5,7 +5,7 @@ Selector="NativeMenuBar"> - + @@ -21,7 +21,7 @@ - + From c34b7f2d31531c0f1210d2ffd8adf132e574e3dd Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 4 May 2022 13:35:34 +0200 Subject: [PATCH 264/416] Remove generic methods from ILogSink. Part of the push to remove generic virtual methods from Avalonia for performance reasons. Generic methods were added to this interface in #3055 to prevent boxing before we added `Logger.TryGet` (#4135). Given that since we added `Logger.TryGet`, only enabled logging levels will result in a call to the logger, we can move back to using `params object[]` and boxing; removing the generic interface methods. --- src/Avalonia.Base/Logging/ILogSink.cs | 51 ----------------------- src/Avalonia.Base/Logging/TraceLogSink.cs | 38 ++--------------- tests/Avalonia.UnitTests/TestLogSink.cs | 17 -------- 3 files changed, 3 insertions(+), 103 deletions(-) diff --git a/src/Avalonia.Base/Logging/ILogSink.cs b/src/Avalonia.Base/Logging/ILogSink.cs index 27558ba0ee..60709776c6 100644 --- a/src/Avalonia.Base/Logging/ILogSink.cs +++ b/src/Avalonia.Base/Logging/ILogSink.cs @@ -26,57 +26,6 @@ namespace Avalonia.Logging object? source, string messageTemplate); - /// - /// Logs an event. - /// - /// The log event level. - /// The area that the event originates. - /// The object from which the event originates. - /// The message template. - /// Message property value. - void Log( - LogEventLevel level, - string area, - object? source, - string messageTemplate, - T0 propertyValue0); - - /// - /// Logs an event. - /// - /// The log event level. - /// The area that the event originates. - /// The object from which the event originates. - /// The message template. - /// Message property value. - /// Message property value. - void Log( - LogEventLevel level, - string area, - object? source, - string messageTemplate, - T0 propertyValue0, - T1 propertyValue1); - - /// - /// Logs an event. - /// - /// The log event level. - /// The area that the event originates. - /// The object from which the event originates. - /// The message template. - /// Message property value. - /// Message property value. - /// Message property value. - void Log( - LogEventLevel level, - string area, - object? source, - string messageTemplate, - T0 propertyValue0, - T1 propertyValue1, - T2 propertyValue2); - /// /// Logs a new event. /// diff --git a/src/Avalonia.Base/Logging/TraceLogSink.cs b/src/Avalonia.Base/Logging/TraceLogSink.cs index 02ed191d2c..05e4b8bc5a 100644 --- a/src/Avalonia.Base/Logging/TraceLogSink.cs +++ b/src/Avalonia.Base/Logging/TraceLogSink.cs @@ -28,31 +28,7 @@ namespace Avalonia.Logging { if (IsEnabled(level, area)) { - Trace.WriteLine(Format(area, messageTemplate, source)); - } - } - - public void Log(LogEventLevel level, string area, object? source, string messageTemplate, T0 propertyValue0) - { - if (IsEnabled(level, area)) - { - Trace.WriteLine(Format(area, messageTemplate, source, propertyValue0)); - } - } - - public void Log(LogEventLevel level, string area, object? source, string messageTemplate, T0 propertyValue0, T1 propertyValue1) - { - if (IsEnabled(level, area)) - { - Trace.WriteLine(Format(area, messageTemplate, source, propertyValue0, propertyValue1)); - } - } - - public void Log(LogEventLevel level, string area, object? source, string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) - { - if (IsEnabled(level, area)) - { - Trace.WriteLine(Format(area, messageTemplate, source, propertyValue0, propertyValue1, propertyValue2)); + Trace.WriteLine(Format(area, messageTemplate, source, null)); } } @@ -68,9 +44,7 @@ namespace Avalonia.Logging string area, string template, object? source, - T0? v0 = default, - T1? v1 = default, - T2? v2 = default) + object?[]? values) { var result = new StringBuilder(template.Length); var r = new CharacterReader(template.AsSpan()); @@ -93,13 +67,7 @@ namespace Avalonia.Logging if (r.Peek != '{') { result.Append('\''); - result.Append(i++ switch - { - 0 => v0, - 1 => v1, - 2 => v2, - _ => null - }); + result.Append(values?[i++]); result.Append('\''); r.TakeUntil('}'); r.Take(); diff --git a/tests/Avalonia.UnitTests/TestLogSink.cs b/tests/Avalonia.UnitTests/TestLogSink.cs index e10292a59b..8b8084ca17 100644 --- a/tests/Avalonia.UnitTests/TestLogSink.cs +++ b/tests/Avalonia.UnitTests/TestLogSink.cs @@ -37,23 +37,6 @@ namespace Avalonia.UnitTests _callback(level, area, source, messageTemplate); } - public void Log(LogEventLevel level, string area, object source, string messageTemplate, T0 propertyValue0) - { - _callback(level, area, source, messageTemplate, propertyValue0); - } - - public void Log(LogEventLevel level, string area, object source, string messageTemplate, - T0 propertyValue0, T1 propertyValue1) - { - _callback(level, area, source, messageTemplate, propertyValue0, propertyValue1); - } - - public void Log(LogEventLevel level, string area, object source, string messageTemplate, - T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) - { - _callback(level, area, source, messageTemplate, propertyValue0, propertyValue1, propertyValue2); - } - public void Log(LogEventLevel level, string area, object source, string messageTemplate, params object[] propertyValues) { From a3112b49e5c310a685d49876352257d85cac22f5 Mon Sep 17 00:00:00 2001 From: Luis von der Eltz Date: Wed, 4 May 2022 14:02:21 +0200 Subject: [PATCH 265/416] Unwrap exceptions --- .../Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs | 4 ++-- .../Diagnostics/ViewModels/ClrPropertyViewModel.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs index 0a2a6cac25..7384daae30 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs @@ -71,7 +71,7 @@ namespace Avalonia.Diagnostics.ViewModels } catch (Exception e) { - value = e; + value = e.GetBaseException(); } RaiseAndSetIfChanged(ref _value, value, nameof(Value)); @@ -96,7 +96,7 @@ namespace Avalonia.Diagnostics.ViewModels } catch (Exception e) { - value = e; + value = e.GetBaseException(); } RaiseAndSetIfChanged(ref _value, value, nameof(Value)); diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs index 296413de78..73fb615b32 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs @@ -76,7 +76,7 @@ namespace Avalonia.Diagnostics.ViewModels } catch (Exception e) { - value = e; + value = e.GetBaseException(); } RaiseAndSetIfChanged(ref _value, value, nameof(Value)); From b3af7f0c6a012512520a09ac06a39fa5fc1b24c0 Mon Sep 17 00:00:00 2001 From: Luis von der Eltz Date: Wed, 4 May 2022 14:45:02 +0200 Subject: [PATCH 266/416] Proper Canvas positioning for adorner layer --- src/Avalonia.Controls/Canvas.cs | 75 +++++++++++-------- .../Primitives/AdornerLayer.cs | 2 +- 2 files changed, 44 insertions(+), 33 deletions(-) diff --git a/src/Avalonia.Controls/Canvas.cs b/src/Avalonia.Controls/Canvas.cs index fabf8978c7..adee7d4d90 100644 --- a/src/Avalonia.Controls/Canvas.cs +++ b/src/Avalonia.Controls/Canvas.cs @@ -1,4 +1,5 @@ using System; +using System.Reactive.Concurrency; using Avalonia.Input; using Avalonia.Layout; @@ -159,47 +160,57 @@ namespace Avalonia.Controls } /// - /// Arranges the control's children. + /// Arranges a single child. /// - /// The size allocated to the control. - /// The space taken. - protected override Size ArrangeOverride(Size finalSize) + /// The child to arrange. + /// The size allocated to the canvas. + protected virtual void ArrangeChild(Control child, Size finalSize) { - foreach (Control child in Children) - { - double x = 0.0; - double y = 0.0; - double elementLeft = GetLeft(child); + double x = 0.0; + double y = 0.0; + double elementLeft = GetLeft(child); - if (!double.IsNaN(elementLeft)) - { - x = elementLeft; - } - else + if (!double.IsNaN(elementLeft)) + { + x = elementLeft; + } + else + { + // Arrange with right. + double elementRight = GetRight(child); + if (!double.IsNaN(elementRight)) { - // Arrange with right. - double elementRight = GetRight(child); - if (!double.IsNaN(elementRight)) - { - x = finalSize.Width - child.DesiredSize.Width - elementRight; - } + x = finalSize.Width - child.DesiredSize.Width - elementRight; } + } - double elementTop = GetTop(child); - if (!double.IsNaN(elementTop) ) - { - y = elementTop; - } - else + double elementTop = GetTop(child); + if (!double.IsNaN(elementTop)) + { + y = elementTop; + } + else + { + double elementBottom = GetBottom(child); + if (!double.IsNaN(elementBottom)) { - double elementBottom = GetBottom(child); - if (!double.IsNaN(elementBottom)) - { - y = finalSize.Height - child.DesiredSize.Height - elementBottom; - } + y = finalSize.Height - child.DesiredSize.Height - elementBottom; } + } - child.Arrange(new Rect(new Point(x, y), child.DesiredSize)); + child.Arrange(new Rect(new Point(x, y), child.DesiredSize)); + } + + /// + /// Arranges the control's children. + /// + /// The size allocated to the control. + /// The space taken. + protected override Size ArrangeOverride(Size finalSize) + { + foreach (Control child in Children) + { + ArrangeChild(child, finalSize); } return finalSize; diff --git a/src/Avalonia.Controls/Primitives/AdornerLayer.cs b/src/Avalonia.Controls/Primitives/AdornerLayer.cs index fb047d93df..5ad4e39baf 100644 --- a/src/Avalonia.Controls/Primitives/AdornerLayer.cs +++ b/src/Avalonia.Controls/Primitives/AdornerLayer.cs @@ -105,7 +105,7 @@ namespace Avalonia.Controls.Primitives } else { - child.Arrange(new Rect(finalSize)); + ArrangeChild((Control) child, finalSize); } } } From 2cf78ac11c7badc0f9ddb015809266d052a810f6 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 4 May 2022 17:38:03 +0100 Subject: [PATCH 267/416] move IWindowBaseImpl to its own file. --- .../Avalonia.Native/src/OSX/INSWindowHolder.h | 15 + .../Avalonia.Native/src/OSX/WindowBaseImpl.h | 121 ++++ .../Avalonia.Native/src/OSX/WindowBaseImpl.mm | 511 ++++++++++++++ native/Avalonia.Native/src/OSX/window.h | 7 +- native/Avalonia.Native/src/OSX/window.mm | 621 +----------------- 5 files changed, 650 insertions(+), 625 deletions(-) create mode 100644 native/Avalonia.Native/src/OSX/INSWindowHolder.h create mode 100644 native/Avalonia.Native/src/OSX/WindowBaseImpl.h create mode 100644 native/Avalonia.Native/src/OSX/WindowBaseImpl.mm diff --git a/native/Avalonia.Native/src/OSX/INSWindowHolder.h b/native/Avalonia.Native/src/OSX/INSWindowHolder.h new file mode 100644 index 0000000000..aa8c34ef00 --- /dev/null +++ b/native/Avalonia.Native/src/OSX/INSWindowHolder.h @@ -0,0 +1,15 @@ +// +// Created by Dan Walmsley on 04/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#ifndef AVALONIA_NATIVE_OSX_INSWINDOWHOLDER_H +#define AVALONIA_NATIVE_OSX_INSWINDOWHOLDER_H + +struct INSWindowHolder +{ + virtual AvnWindow* _Nonnull GetNSWindow () = 0; + virtual AvnView* _Nonnull GetNSView () = 0; +}; + +#endif //AVALONIA_NATIVE_OSX_INSWINDOWHOLDER_H diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h new file mode 100644 index 0000000000..2f9b05988f --- /dev/null +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -0,0 +1,121 @@ +// +// Created by Dan Walmsley on 04/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#ifndef AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H +#define AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H + +#include "INSWindowHolder.h" + +class WindowBaseImpl : public virtual ComObject, + public virtual IAvnWindowBase, + public INSWindowHolder { +private: + NSCursor *cursor; + +public: + FORWARD_IUNKNOWN() + +BEGIN_INTERFACE_MAP() + INTERFACE_MAP_ENTRY(IAvnWindowBase, IID_IAvnWindowBase) + END_INTERFACE_MAP() + + virtual ~WindowBaseImpl() { + View = NULL; + Window = NULL; + } + + AutoFitContentView *StandardContainer; + AvnView *View; + AvnWindow *Window; + ComPtr BaseEvents; + ComPtr _glContext; + NSObject *renderTarget; + AvnPoint lastPositionSet; + NSString *_lastTitle; + IAvnMenu *_mainMenu; + + bool _shown; + bool _inResize; + + WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl); + + virtual HRESULT ObtainNSWindowHandle(void **ret) override; + + virtual HRESULT ObtainNSWindowHandleRetained(void **ret) override; + + virtual HRESULT ObtainNSViewHandle(void **ret) override; + + virtual HRESULT ObtainNSViewHandleRetained(void **ret) override; + + virtual AvnWindow *GetNSWindow() override; + + virtual AvnView *GetNSView() override; + + virtual HRESULT Show(bool activate, bool isDialog) override; + + virtual bool ShouldTakeFocusOnShow(); + + virtual HRESULT Hide() override; + + virtual HRESULT Activate() override; + + virtual HRESULT SetTopMost(bool value) override; + + virtual HRESULT Close() override; + + virtual HRESULT GetClientSize(AvnSize *ret) override; + + virtual HRESULT GetFrameSize(AvnSize *ret) override; + + virtual HRESULT GetScaling(double *ret) override; + + virtual HRESULT SetMinMaxSize(AvnSize minSize, AvnSize maxSize) override; + + virtual HRESULT Resize(double x, double y, AvnPlatformResizeReason reason) override; + + virtual HRESULT Invalidate(AvnRect rect) override; + + virtual HRESULT SetMainMenu(IAvnMenu *menu) override; + + virtual HRESULT BeginMoveDrag() override; + + virtual HRESULT BeginResizeDrag(AvnWindowEdge edge) override; + + virtual HRESULT GetPosition(AvnPoint *ret) override; + + virtual HRESULT SetPosition(AvnPoint point) override; + + virtual HRESULT PointToClient(AvnPoint point, AvnPoint *ret) override; + + virtual HRESULT PointToScreen(AvnPoint point, AvnPoint *ret) override; + + virtual HRESULT ThreadSafeSetSwRenderedFrame(AvnFramebuffer *fb, IUnknown *dispose) override; + + virtual HRESULT SetCursor(IAvnCursor *cursor) override; + + virtual void UpdateCursor(); + + virtual HRESULT CreateGlRenderTarget(IAvnGlSurfaceRenderTarget **ppv) override; + + virtual HRESULT CreateNativeControlHost(IAvnNativeControlHost **retOut) override; + + virtual HRESULT SetBlurEnabled(bool enable) override; + + virtual HRESULT BeginDragAndDropOperation(AvnDragDropEffects effects, AvnPoint point, + IAvnClipboard *clipboard, IAvnDndResultCallback *cb, + void *sourceHandle) override; + + virtual bool IsDialog(); + +protected: + virtual NSWindowStyleMask GetStyle(); + + void UpdateStyle(); + +public: + virtual void OnResized(); +}; + +#endif //AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm new file mode 100644 index 0000000000..482b6172af --- /dev/null +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -0,0 +1,511 @@ +// +// Created by Dan Walmsley on 04/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#import +#include "common.h" +#import "window.h" +#include "menu.h" +#include "rendertarget.h" +#include "automation.h" +#import "WindowBaseImpl.h" +#import "cursor.h" + +WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl) { + _shown = false; + _inResize = false; + _mainMenu = nullptr; + BaseEvents = events; + _glContext = gl; + renderTarget = [[IOSurfaceRenderTarget alloc] initWithOpenGlContext:gl]; + View = [[AvnView alloc] initWithParent:this]; + StandardContainer = [[AutoFitContentView new] initWithContent:View]; + + Window = [[AvnWindow alloc] initWithParent:this]; + [Window setContentView:StandardContainer]; + + lastPositionSet.X = 100; + lastPositionSet.Y = 100; + _lastTitle = @""; + + [Window setStyleMask:NSWindowStyleMaskBorderless]; + [Window setBackingType:NSBackingStoreBuffered]; + + [Window setOpaque:false]; +} + +HRESULT WindowBaseImpl::ObtainNSViewHandle(void **ret) { + START_COM_CALL; + + if (ret == nullptr) { + return E_POINTER; + } + + *ret = (__bridge void *) View; + + return S_OK; +} + +HRESULT WindowBaseImpl::ObtainNSViewHandleRetained(void **ret) { + START_COM_CALL; + + if (ret == nullptr) { + return E_POINTER; + } + + *ret = (__bridge_retained void *) View; + + return S_OK; +} + +AvnWindow *WindowBaseImpl::GetNSWindow() { + return Window; +} + +AvnView *WindowBaseImpl::GetNSView() { + return View; +} + +HRESULT WindowBaseImpl::ObtainNSWindowHandleRetained(void **ret) { + START_COM_CALL; + + if (ret == nullptr) { + return E_POINTER; + } + + *ret = (__bridge_retained void *) Window; + + return S_OK; +} + +HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) { + START_COM_CALL; + + @autoreleasepool { + SetPosition(lastPositionSet); + UpdateStyle(); + + [Window setTitle:_lastTitle]; + + if (ShouldTakeFocusOnShow() && activate) { + [Window orderFront:Window]; + [Window makeKeyAndOrderFront:Window]; + [Window makeFirstResponder:View]; + [NSApp activateIgnoringOtherApps:YES]; + } else { + [Window orderFront:Window]; + } + + _shown = true; + + return S_OK; + } +} + +bool WindowBaseImpl::ShouldTakeFocusOnShow() { + return true; +} + +HRESULT WindowBaseImpl::ObtainNSWindowHandle(void **ret) { + START_COM_CALL; + + if (ret == nullptr) { + return E_POINTER; + } + + *ret = (__bridge void *) Window; + + return S_OK; +} + +HRESULT WindowBaseImpl::Hide() { + START_COM_CALL; + + @autoreleasepool { + if (Window != nullptr) { + [Window orderOut:Window]; + [Window restoreParentWindow]; + } + + return S_OK; + } +} + +HRESULT WindowBaseImpl::Activate() { + START_COM_CALL; + + @autoreleasepool { + if (Window != nullptr) { + [Window makeKeyAndOrderFront:nil]; + [NSApp activateIgnoringOtherApps:YES]; + } + } + + return S_OK; +} + +HRESULT WindowBaseImpl::SetTopMost(bool value) { + START_COM_CALL; + + @autoreleasepool { + [Window setLevel:value ? NSFloatingWindowLevel : NSNormalWindowLevel]; + + return S_OK; + } +} + +HRESULT WindowBaseImpl::Close() { + START_COM_CALL; + + @autoreleasepool { + if (Window != nullptr) { + auto window = Window; + Window = nullptr; + + try { + // Seems to throw sometimes on application exit. + [window close]; + } + catch (NSException *) {} + } + + return S_OK; + } +} + +HRESULT WindowBaseImpl::GetClientSize(AvnSize *ret) { + START_COM_CALL; + + @autoreleasepool { + if (ret == nullptr) + return E_POINTER; + + auto frame = [View frame]; + ret->Width = frame.size.width; + ret->Height = frame.size.height; + + return S_OK; + } +} + +HRESULT WindowBaseImpl::GetFrameSize(AvnSize *ret) { + START_COM_CALL; + + @autoreleasepool { + if (ret == nullptr) + return E_POINTER; + + auto frame = [Window frame]; + ret->Width = frame.size.width; + ret->Height = frame.size.height; + + return S_OK; + } +} + +HRESULT WindowBaseImpl::GetScaling(double *ret) { + START_COM_CALL; + + @autoreleasepool { + if (ret == nullptr) + return E_POINTER; + + if (Window == nullptr) { + *ret = 1; + return S_OK; + } + + *ret = [Window backingScaleFactor]; + return S_OK; + } +} + +HRESULT WindowBaseImpl::SetMinMaxSize(AvnSize minSize, AvnSize maxSize) { + START_COM_CALL; + + @autoreleasepool { + [Window setMinSize:ToNSSize(minSize)]; + [Window setMaxSize:ToNSSize(maxSize)]; + + return S_OK; + } +} + +HRESULT WindowBaseImpl::Resize(double x, double y, AvnPlatformResizeReason reason) { + if (_inResize) { + return S_OK; + } + + _inResize = true; + + START_COM_CALL; + auto resizeBlock = ResizeScope(View, reason); + + @autoreleasepool { + auto maxSize = [Window maxSize]; + auto minSize = [Window minSize]; + + if (x < minSize.width) { + x = minSize.width; + } + + if (y < minSize.height) { + y = minSize.height; + } + + if (x > maxSize.width) { + x = maxSize.width; + } + + if (y > maxSize.height) { + y = maxSize.height; + } + + @try { + if (!_shown) { + BaseEvents->Resized(AvnSize{x, y}, reason); + } + + [Window setContentSize:NSSize{x, y}]; + [Window invalidateShadow]; + } + @finally { + _inResize = false; + } + + return S_OK; + } +} + +HRESULT WindowBaseImpl::Invalidate(AvnRect rect) { + START_COM_CALL; + + @autoreleasepool { + [View setNeedsDisplayInRect:[View frame]]; + + return S_OK; + } +} + +HRESULT WindowBaseImpl::SetMainMenu(IAvnMenu *menu) { + START_COM_CALL; + + _mainMenu = menu; + + auto nativeMenu = dynamic_cast(menu); + + auto nsmenu = nativeMenu->GetNative(); + + [Window applyMenu:nsmenu]; + + if ([Window isKeyWindow]) { + [Window showWindowMenuWithAppMenu]; + } + + return S_OK; +} + +HRESULT WindowBaseImpl::BeginMoveDrag() { + START_COM_CALL; + + @autoreleasepool { + auto lastEvent = [View lastMouseDownEvent]; + + if (lastEvent == nullptr) { + return S_OK; + } + + [Window performWindowDragWithEvent:lastEvent]; + + return S_OK; + } +} + +HRESULT WindowBaseImpl::BeginResizeDrag(AvnWindowEdge edge) { + START_COM_CALL; + + return S_OK; +} + +HRESULT WindowBaseImpl::GetPosition(AvnPoint *ret) { + START_COM_CALL; + + @autoreleasepool { + if (ret == nullptr) { + return E_POINTER; + } + + auto frame = [Window frame]; + + ret->X = frame.origin.x; + ret->Y = frame.origin.y + frame.size.height; + + *ret = ConvertPointY(*ret); + + return S_OK; + } +} + +HRESULT WindowBaseImpl::SetPosition(AvnPoint point) { + START_COM_CALL; + + @autoreleasepool { + lastPositionSet = point; + [Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(point))]; + + return S_OK; + } +} + +HRESULT WindowBaseImpl::PointToClient(AvnPoint point, AvnPoint *ret) { + START_COM_CALL; + + @autoreleasepool { + if (ret == nullptr) { + return E_POINTER; + } + + point = ConvertPointY(point); + NSRect convertRect = [Window convertRectFromScreen:NSMakeRect(point.X, point.Y, 0.0, 0.0)]; + auto viewPoint = NSMakePoint(convertRect.origin.x, convertRect.origin.y); + + *ret = [View translateLocalPoint:ToAvnPoint(viewPoint)]; + + return S_OK; + } +} + +HRESULT WindowBaseImpl::PointToScreen(AvnPoint point, AvnPoint *ret) { + START_COM_CALL; + + @autoreleasepool { + if (ret == nullptr) { + return E_POINTER; + } + + auto cocoaViewPoint = ToNSPoint([View translateLocalPoint:point]); + NSRect convertRect = [Window convertRectToScreen:NSMakeRect(cocoaViewPoint.x, cocoaViewPoint.y, 0.0, 0.0)]; + auto cocoaScreenPoint = NSPointFromCGPoint(NSMakePoint(convertRect.origin.x, convertRect.origin.y)); + *ret = ConvertPointY(ToAvnPoint(cocoaScreenPoint)); + + return S_OK; + } +} + +HRESULT WindowBaseImpl::ThreadSafeSetSwRenderedFrame(AvnFramebuffer *fb, IUnknown *dispose) { + START_COM_CALL; + + [View setSwRenderedFrame:fb dispose:dispose]; + return S_OK; +} + +HRESULT WindowBaseImpl::SetCursor(IAvnCursor *cursor) { + START_COM_CALL; + + @autoreleasepool { + Cursor *avnCursor = dynamic_cast(cursor); + this->cursor = avnCursor->GetNative(); + UpdateCursor(); + + if (avnCursor->IsHidden()) { + [NSCursor hide]; + } else { + [NSCursor unhide]; + } + + return S_OK; + } +} + +void WindowBaseImpl::UpdateCursor() { + if (cursor != nil) { + [cursor set]; + } +} + +HRESULT WindowBaseImpl::CreateGlRenderTarget(IAvnGlSurfaceRenderTarget **ppv) { + START_COM_CALL; + + if (View == NULL) + return E_FAIL; + *ppv = [renderTarget createSurfaceRenderTarget]; + return *ppv == nil ? E_FAIL : S_OK; +} + +HRESULT WindowBaseImpl::CreateNativeControlHost(IAvnNativeControlHost **retOut) { + START_COM_CALL; + + if (View == NULL) + return E_FAIL; + *retOut = ::CreateNativeControlHost(View); + return S_OK; +} + +HRESULT WindowBaseImpl::SetBlurEnabled(bool enable) { + START_COM_CALL; + + [StandardContainer ShowBlur:enable]; + + return S_OK; +} + +HRESULT WindowBaseImpl::BeginDragAndDropOperation(AvnDragDropEffects effects, AvnPoint point, IAvnClipboard *clipboard, IAvnDndResultCallback *cb, void *sourceHandle) { + START_COM_CALL; + + auto item = TryGetPasteboardItem(clipboard); + [item setString:@"" forType:GetAvnCustomDataType()]; + if (item == nil) + return E_INVALIDARG; + if (View == NULL) + return E_FAIL; + + auto nsevent = [NSApp currentEvent]; + auto nseventType = [nsevent type]; + + // If current event isn't a mouse one (probably due to malfunctioning user app) + // attempt to forge a new one + if (!((nseventType >= NSEventTypeLeftMouseDown && nseventType <= NSEventTypeMouseExited) + || (nseventType >= NSEventTypeOtherMouseDown && nseventType <= NSEventTypeOtherMouseDragged))) { + NSRect convertRect = [Window convertRectToScreen:NSMakeRect(point.X, point.Y, 0.0, 0.0)]; + auto nspoint = NSMakePoint(convertRect.origin.x, convertRect.origin.y); + CGPoint cgpoint = NSPointToCGPoint(nspoint); + auto cgevent = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseDown, cgpoint, kCGMouseButtonLeft); + nsevent = [NSEvent eventWithCGEvent:cgevent]; + CFRelease(cgevent); + } + + auto dragItem = [[NSDraggingItem alloc] initWithPasteboardWriter:item]; + + auto dragItemImage = [NSImage imageNamed:NSImageNameMultipleDocuments]; + NSRect dragItemRect = {(float) point.X, (float) point.Y, [dragItemImage size].width, [dragItemImage size].height}; + [dragItem setDraggingFrame:dragItemRect contents:dragItemImage]; + + int op = 0; + int ieffects = (int) effects; + if ((ieffects & (int) AvnDragDropEffects::Copy) != 0) + op |= NSDragOperationCopy; + if ((ieffects & (int) AvnDragDropEffects::Link) != 0) + op |= NSDragOperationLink; + if ((ieffects & (int) AvnDragDropEffects::Move) != 0) + op |= NSDragOperationMove; + [View beginDraggingSessionWithItems:@[dragItem] event:nsevent + source:CreateDraggingSource((NSDragOperation) op, cb, sourceHandle)]; + return S_OK; +} + +bool WindowBaseImpl::IsDialog() { + return false; +} + +NSWindowStyleMask WindowBaseImpl::GetStyle() { + return NSWindowStyleMaskBorderless; +} + +void WindowBaseImpl::UpdateStyle() { + [Window setStyleMask:GetStyle()]; +} + +void WindowBaseImpl::OnResized() { + +} diff --git a/native/Avalonia.Native/src/OSX/window.h b/native/Avalonia.Native/src/OSX/window.h index 1369ceaea0..68dc673917 100644 --- a/native/Avalonia.Native/src/OSX/window.h +++ b/native/Avalonia.Native/src/OSX/window.h @@ -1,6 +1,7 @@ #ifndef window_h #define window_h + class WindowBaseImpl; @interface AvnView : NSView @@ -40,12 +41,6 @@ class WindowBaseImpl; -(bool) isDialog; @end -struct INSWindowHolder -{ - virtual AvnWindow* _Nonnull GetNSWindow () = 0; - virtual AvnView* _Nonnull GetNSView () = 0; -}; - struct IWindowStateChanged { virtual void WindowStateChanged () = 0; diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index 4426e7fdff..9676515b16 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -1,628 +1,11 @@ #include "common.h" -#include "window.h" +#import "window.h" #include "KeyTransform.h" -#include "cursor.h" #include "menu.h" -#include #include "rendertarget.h" -#include "AvnString.h" #include "automation.h" +#import "WindowBaseImpl.h" -class WindowBaseImpl : public virtual ComObject, - public virtual IAvnWindowBase, - public INSWindowHolder -{ -private: - NSCursor* cursor; - -public: - FORWARD_IUNKNOWN() - BEGIN_INTERFACE_MAP() - INTERFACE_MAP_ENTRY(IAvnWindowBase, IID_IAvnWindowBase) - END_INTERFACE_MAP() - - virtual ~WindowBaseImpl() - { - View = NULL; - Window = NULL; - } - AutoFitContentView* StandardContainer; - AvnView* View; - AvnWindow* Window; - ComPtr BaseEvents; - ComPtr _glContext; - NSObject* renderTarget; - AvnPoint lastPositionSet; - NSString* _lastTitle; - IAvnMenu* _mainMenu; - - bool _shown; - bool _inResize; - - WindowBaseImpl(IAvnWindowBaseEvents* events, IAvnGlContext* gl) - { - _shown = false; - _inResize = false; - _mainMenu = nullptr; - BaseEvents = events; - _glContext = gl; - renderTarget = [[IOSurfaceRenderTarget alloc] initWithOpenGlContext: gl]; - View = [[AvnView alloc] initWithParent:this]; - StandardContainer = [[AutoFitContentView new] initWithContent:View]; - - Window = [[AvnWindow alloc] initWithParent:this]; - [Window setContentView: StandardContainer]; - - lastPositionSet.X = 100; - lastPositionSet.Y = 100; - _lastTitle = @""; - - [Window setStyleMask:NSWindowStyleMaskBorderless]; - [Window setBackingType:NSBackingStoreBuffered]; - - [Window setOpaque:false]; - } - - virtual HRESULT ObtainNSWindowHandle(void** ret) override - { - START_COM_CALL; - - if (ret == nullptr) - { - return E_POINTER; - } - - *ret = (__bridge void*)Window; - - return S_OK; - } - - virtual HRESULT ObtainNSWindowHandleRetained(void** ret) override - { - START_COM_CALL; - - if (ret == nullptr) - { - return E_POINTER; - } - - *ret = (__bridge_retained void*)Window; - - return S_OK; - } - - virtual HRESULT ObtainNSViewHandle(void** ret) override - { - START_COM_CALL; - - if (ret == nullptr) - { - return E_POINTER; - } - - *ret = (__bridge void*)View; - - return S_OK; - } - - virtual HRESULT ObtainNSViewHandleRetained(void** ret) override - { - START_COM_CALL; - - if (ret == nullptr) - { - return E_POINTER; - } - - *ret = (__bridge_retained void*)View; - - return S_OK; - } - - virtual AvnWindow* GetNSWindow() override - { - return Window; - } - - virtual AvnView* GetNSView() override - { - return View; - } - - virtual HRESULT Show(bool activate, bool isDialog) override - { - START_COM_CALL; - - @autoreleasepool - { - SetPosition(lastPositionSet); - UpdateStyle(); - - [Window setTitle:_lastTitle]; - - if(ShouldTakeFocusOnShow() && activate) - { - [Window orderFront: Window]; - [Window makeKeyAndOrderFront:Window]; - [Window makeFirstResponder:View]; - [NSApp activateIgnoringOtherApps:YES]; - } - else - { - [Window orderFront: Window]; - } - - _shown = true; - - return S_OK; - } - } - - virtual bool ShouldTakeFocusOnShow() - { - return true; - } - - virtual HRESULT Hide () override - { - START_COM_CALL; - - @autoreleasepool - { - if(Window != nullptr) - { - [Window orderOut:Window]; - [Window restoreParentWindow]; - } - - return S_OK; - } - } - - virtual HRESULT Activate () override - { - START_COM_CALL; - - @autoreleasepool - { - if(Window != nullptr) - { - [Window makeKeyAndOrderFront:nil]; - [NSApp activateIgnoringOtherApps:YES]; - } - } - - return S_OK; - } - - virtual HRESULT SetTopMost (bool value) override - { - START_COM_CALL; - - @autoreleasepool - { - [Window setLevel: value ? NSFloatingWindowLevel : NSNormalWindowLevel]; - - return S_OK; - } - } - - virtual HRESULT Close() override - { - START_COM_CALL; - - @autoreleasepool - { - if (Window != nullptr) - { - auto window = Window; - Window = nullptr; - - try{ - // Seems to throw sometimes on application exit. - [window close]; - } - catch(NSException*){} - } - - return S_OK; - } - } - - virtual HRESULT GetClientSize(AvnSize* ret) override - { - START_COM_CALL; - - @autoreleasepool - { - if(ret == nullptr) - return E_POINTER; - - auto frame = [View frame]; - ret->Width = frame.size.width; - ret->Height = frame.size.height; - - return S_OK; - } - } - - virtual HRESULT GetFrameSize(AvnSize* ret) override - { - START_COM_CALL; - - @autoreleasepool - { - if(ret == nullptr) - return E_POINTER; - - auto frame = [Window frame]; - ret->Width = frame.size.width; - ret->Height = frame.size.height; - - return S_OK; - } - } - - virtual HRESULT GetScaling (double* ret) override - { - START_COM_CALL; - - @autoreleasepool - { - if(ret == nullptr) - return E_POINTER; - - if(Window == nullptr) - { - *ret = 1; - return S_OK; - } - - *ret = [Window backingScaleFactor]; - return S_OK; - } - } - - virtual HRESULT SetMinMaxSize (AvnSize minSize, AvnSize maxSize) override - { - START_COM_CALL; - - @autoreleasepool - { - [Window setMinSize: ToNSSize(minSize)]; - [Window setMaxSize: ToNSSize(maxSize)]; - - return S_OK; - } - } - - virtual HRESULT Resize(double x, double y, AvnPlatformResizeReason reason) override - { - if(_inResize) - { - return S_OK; - } - - _inResize = true; - - START_COM_CALL; - auto resizeBlock = ResizeScope(View, reason); - - @autoreleasepool - { - auto maxSize = [Window maxSize]; - auto minSize = [Window minSize]; - - if (x < minSize.width) - { - x = minSize.width; - } - - if (y < minSize.height) - { - y = minSize.height; - } - - if (x > maxSize.width) - { - x = maxSize.width; - } - - if (y > maxSize.height) - { - y = maxSize.height; - } - - @try - { - if(!_shown) - { - BaseEvents->Resized(AvnSize{x,y}, reason); - } - - [Window setContentSize:NSSize{x,y}]; - [Window invalidateShadow]; - } - @finally - { - _inResize = false; - } - - return S_OK; - } - } - - virtual HRESULT Invalidate (AvnRect rect) override - { - START_COM_CALL; - - @autoreleasepool - { - [View setNeedsDisplayInRect:[View frame]]; - - return S_OK; - } - } - - virtual HRESULT SetMainMenu(IAvnMenu* menu) override - { - START_COM_CALL; - - _mainMenu = menu; - - auto nativeMenu = dynamic_cast(menu); - - auto nsmenu = nativeMenu->GetNative(); - - [Window applyMenu:nsmenu]; - - if ([Window isKeyWindow]) - { - [Window showWindowMenuWithAppMenu]; - } - - return S_OK; - } - - virtual HRESULT BeginMoveDrag () override - { - START_COM_CALL; - - @autoreleasepool - { - auto lastEvent = [View lastMouseDownEvent]; - - if(lastEvent == nullptr) - { - return S_OK; - } - - [Window performWindowDragWithEvent:lastEvent]; - - return S_OK; - } - } - - virtual HRESULT BeginResizeDrag (AvnWindowEdge edge) override - { - START_COM_CALL; - - return S_OK; - } - - virtual HRESULT GetPosition (AvnPoint* ret) override - { - START_COM_CALL; - - @autoreleasepool - { - if(ret == nullptr) - { - return E_POINTER; - } - - auto frame = [Window frame]; - - ret->X = frame.origin.x; - ret->Y = frame.origin.y + frame.size.height; - - *ret = ConvertPointY(*ret); - - return S_OK; - } - } - - virtual HRESULT SetPosition (AvnPoint point) override - { - START_COM_CALL; - - @autoreleasepool - { - lastPositionSet = point; - [Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(point))]; - - return S_OK; - } - } - - virtual HRESULT PointToClient (AvnPoint point, AvnPoint* ret) override - { - START_COM_CALL; - - @autoreleasepool - { - if(ret == nullptr) - { - return E_POINTER; - } - - point = ConvertPointY(point); - NSRect convertRect = [Window convertRectFromScreen:NSMakeRect(point.X, point.Y, 0.0, 0.0)]; - auto viewPoint = NSMakePoint(convertRect.origin.x, convertRect.origin.y); - - *ret = [View translateLocalPoint:ToAvnPoint(viewPoint)]; - - return S_OK; - } - } - - virtual HRESULT PointToScreen (AvnPoint point, AvnPoint* ret) override - { - START_COM_CALL; - - @autoreleasepool - { - if(ret == nullptr) - { - return E_POINTER; - } - - auto cocoaViewPoint = ToNSPoint([View translateLocalPoint:point]); - NSRect convertRect = [Window convertRectToScreen:NSMakeRect(cocoaViewPoint.x, cocoaViewPoint.y, 0.0, 0.0)]; - auto cocoaScreenPoint = NSPointFromCGPoint(NSMakePoint(convertRect.origin.x, convertRect.origin.y)); - *ret = ConvertPointY(ToAvnPoint(cocoaScreenPoint)); - - return S_OK; - } - } - - virtual HRESULT ThreadSafeSetSwRenderedFrame(AvnFramebuffer* fb, IUnknown* dispose) override - { - START_COM_CALL; - - [View setSwRenderedFrame: fb dispose: dispose]; - return S_OK; - } - - virtual HRESULT SetCursor(IAvnCursor* cursor) override - { - START_COM_CALL; - - @autoreleasepool - { - Cursor* avnCursor = dynamic_cast(cursor); - this->cursor = avnCursor->GetNative(); - UpdateCursor(); - - if(avnCursor->IsHidden()) - { - [NSCursor hide]; - } - else - { - [NSCursor unhide]; - } - - return S_OK; - } - } - - virtual void UpdateCursor() - { - if (cursor != nil) - { - [cursor set]; - } - } - - virtual HRESULT CreateGlRenderTarget(IAvnGlSurfaceRenderTarget** ppv) override - { - START_COM_CALL; - - if(View == NULL) - return E_FAIL; - *ppv = [renderTarget createSurfaceRenderTarget]; - return *ppv == nil ? E_FAIL : S_OK; - } - - virtual HRESULT CreateNativeControlHost(IAvnNativeControlHost** retOut) override - { - START_COM_CALL; - - if(View == NULL) - return E_FAIL; - *retOut = ::CreateNativeControlHost(View); - return S_OK; - } - - virtual HRESULT SetBlurEnabled (bool enable) override - { - START_COM_CALL; - - [StandardContainer ShowBlur:enable]; - - return S_OK; - } - - virtual HRESULT BeginDragAndDropOperation(AvnDragDropEffects effects, AvnPoint point, - IAvnClipboard* clipboard, IAvnDndResultCallback* cb, - void* sourceHandle) override - { - START_COM_CALL; - - auto item = TryGetPasteboardItem(clipboard); - [item setString:@"" forType:GetAvnCustomDataType()]; - if(item == nil) - return E_INVALIDARG; - if(View == NULL) - return E_FAIL; - - auto nsevent = [NSApp currentEvent]; - auto nseventType = [nsevent type]; - - // If current event isn't a mouse one (probably due to malfunctioning user app) - // attempt to forge a new one - if(!((nseventType >= NSEventTypeLeftMouseDown && nseventType <= NSEventTypeMouseExited) - || (nseventType >= NSEventTypeOtherMouseDown && nseventType <= NSEventTypeOtherMouseDragged))) - { - NSRect convertRect = [Window convertRectToScreen:NSMakeRect(point.X, point.Y, 0.0, 0.0)]; - auto nspoint = NSMakePoint(convertRect.origin.x, convertRect.origin.y); - CGPoint cgpoint = NSPointToCGPoint(nspoint); - auto cgevent = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseDown, cgpoint, kCGMouseButtonLeft); - nsevent = [NSEvent eventWithCGEvent: cgevent]; - CFRelease(cgevent); - } - - auto dragItem = [[NSDraggingItem alloc] initWithPasteboardWriter: item]; - - auto dragItemImage = [NSImage imageNamed:NSImageNameMultipleDocuments]; - NSRect dragItemRect = {(float)point.X, (float)point.Y, [dragItemImage size].width, [dragItemImage size].height}; - [dragItem setDraggingFrame: dragItemRect contents: dragItemImage]; - - int op = 0; int ieffects = (int)effects; - if((ieffects & (int)AvnDragDropEffects::Copy) != 0) - op |= NSDragOperationCopy; - if((ieffects & (int)AvnDragDropEffects::Link) != 0) - op |= NSDragOperationLink; - if((ieffects & (int)AvnDragDropEffects::Move) != 0) - op |= NSDragOperationMove; - [View beginDraggingSessionWithItems: @[dragItem] event: nsevent - source: CreateDraggingSource((NSDragOperation) op, cb, sourceHandle)]; - return S_OK; - } - - virtual bool IsDialog() - { - return false; - } - -protected: - virtual NSWindowStyleMask GetStyle() - { - return NSWindowStyleMaskBorderless; - } - - void UpdateStyle() - { - [Window setStyleMask: GetStyle()]; - } - -public: - virtual void OnResized () - { - - } -}; class WindowImpl : public virtual WindowBaseImpl, public virtual IAvnWindow, public IWindowStateChanged { From 6eb40ac09d1b38fd80b2ca4be7799f9d048dc42c Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 4 May 2022 17:40:33 +0100 Subject: [PATCH 268/416] make avalonia-native compile again with import instead of include. --- native/Avalonia.Native/src/OSX/SystemDialogs.mm | 1 + native/Avalonia.Native/src/OSX/automation.h | 2 +- native/Avalonia.Native/src/OSX/automation.mm | 4 ++-- native/Avalonia.Native/src/OSX/menu.mm | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/SystemDialogs.mm b/native/Avalonia.Native/src/OSX/SystemDialogs.mm index a47221056b..21ad9cfa7c 100644 --- a/native/Avalonia.Native/src/OSX/SystemDialogs.mm +++ b/native/Avalonia.Native/src/OSX/SystemDialogs.mm @@ -1,5 +1,6 @@ #include "common.h" #include "window.h" +#include "INSWindowHolder.h" class SystemDialogs : public ComSingleObject { diff --git a/native/Avalonia.Native/src/OSX/automation.h b/native/Avalonia.Native/src/OSX/automation.h index 4a12a965fd..79ccfd0eaa 100644 --- a/native/Avalonia.Native/src/OSX/automation.h +++ b/native/Avalonia.Native/src/OSX/automation.h @@ -1,5 +1,5 @@ #import -#include "window.h" +#import "window.h" NS_ASSUME_NONNULL_BEGIN diff --git a/native/Avalonia.Native/src/OSX/automation.mm b/native/Avalonia.Native/src/OSX/automation.mm index 7d697140c2..226e8810c7 100644 --- a/native/Avalonia.Native/src/OSX/automation.mm +++ b/native/Avalonia.Native/src/OSX/automation.mm @@ -1,7 +1,7 @@ #include "common.h" -#include "automation.h" +#import "automation.h" #include "AvnString.h" -#include "window.h" +#import "INSWindowHolder.h" @interface AvnAccessibilityElement (Events) - (void) raiseChildrenChanged; diff --git a/native/Avalonia.Native/src/OSX/menu.mm b/native/Avalonia.Native/src/OSX/menu.mm index 2dbe76bc6d..726e58478b 100644 --- a/native/Avalonia.Native/src/OSX/menu.mm +++ b/native/Avalonia.Native/src/OSX/menu.mm @@ -1,7 +1,7 @@ #include "common.h" #include "menu.h" -#include "window.h" +#import "window.h" #include "KeyTransform.h" #include #include /* For kVK_ constants, and TIS functions. */ From e2b76313b76fb261a7b92542660561ccfebd983d Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 4 May 2022 18:01:31 +0100 Subject: [PATCH 269/416] further seperate out files. --- .../project.pbxproj | 32 + .../src/OSX/IWindowStateChanged.h | 18 + native/Avalonia.Native/src/OSX/ResizeScope.h | 23 + native/Avalonia.Native/src/OSX/ResizeScope.mm | 17 + .../Avalonia.Native/src/OSX/WindowBaseImpl.h | 1 + .../Avalonia.Native/src/OSX/WindowBaseImpl.mm | 1 + native/Avalonia.Native/src/OSX/WindowImpl.h | 99 +++ native/Avalonia.Native/src/OSX/WindowImpl.mm | 546 ++++++++++++++ native/Avalonia.Native/src/OSX/automation.h | 1 - native/Avalonia.Native/src/OSX/automation.mm | 1 + native/Avalonia.Native/src/OSX/window.h | 30 +- native/Avalonia.Native/src/OSX/window.mm | 667 +----------------- 12 files changed, 741 insertions(+), 695 deletions(-) create mode 100644 native/Avalonia.Native/src/OSX/IWindowStateChanged.h create mode 100644 native/Avalonia.Native/src/OSX/ResizeScope.h create mode 100644 native/Avalonia.Native/src/OSX/ResizeScope.mm create mode 100644 native/Avalonia.Native/src/OSX/WindowImpl.h create mode 100644 native/Avalonia.Native/src/OSX/WindowImpl.mm diff --git a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj index 7571d51c9f..2a206b0692 100644 --- a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj +++ b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj @@ -7,6 +7,14 @@ objects = { /* Begin PBXBuildFile section */ + 18391068E48EF96E3DB5FDAB /* ResizeScope.mm in Sources */ = {isa = PBXBuildFile; fileRef = 18391E45702740FE9DD69695 /* ResizeScope.mm */; }; + 1839125F057B0A4EB1760058 /* WindowImpl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 183919BF108EB72A029F7671 /* WindowImpl.mm */; }; + 183916173528EC2737DBE5E1 /* WindowBaseImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 183915BFF0E234CD3604A7CD /* WindowBaseImpl.h */; }; + 1839171DCC651B0638603AC4 /* INSWindowHolder.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391BBB7782C296D424071F /* INSWindowHolder.h */; }; + 1839179A55FC1421BEE83330 /* WindowBaseImpl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 18391676ECF0E983F4964357 /* WindowBaseImpl.mm */; }; + 183919D91DB9AAB5D700C2EA /* WindowImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391CD090AA776E7E841AC9 /* WindowImpl.h */; }; + 18391C28BF1823B5464FDD36 /* ResizeScope.h in Headers */ = {isa = PBXBuildFile; fileRef = 1839171D898F9BFC1373631A /* ResizeScope.h */; }; + 18391CF07316F819E76B617C /* IWindowStateChanged.h in Headers */ = {isa = PBXBuildFile; fileRef = 183913C6BFD6856BD42D19FD /* IWindowStateChanged.h */; }; 1A002B9E232135EE00021753 /* app.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A002B9D232135EE00021753 /* app.mm */; }; 1A1852DC23E05814008F0DED /* deadlock.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A1852DB23E05814008F0DED /* deadlock.mm */; }; 1A3E5EA823E9E83B00EDE661 /* rendertarget.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A3E5EA723E9E83B00EDE661 /* rendertarget.mm */; }; @@ -35,6 +43,14 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 183913C6BFD6856BD42D19FD /* IWindowStateChanged.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IWindowStateChanged.h; sourceTree = ""; }; + 183915BFF0E234CD3604A7CD /* WindowBaseImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowBaseImpl.h; sourceTree = ""; }; + 18391676ECF0E983F4964357 /* WindowBaseImpl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WindowBaseImpl.mm; sourceTree = ""; }; + 1839171D898F9BFC1373631A /* ResizeScope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ResizeScope.h; sourceTree = ""; }; + 183919BF108EB72A029F7671 /* WindowImpl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WindowImpl.mm; sourceTree = ""; }; + 18391BBB7782C296D424071F /* INSWindowHolder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = INSWindowHolder.h; sourceTree = ""; }; + 18391CD090AA776E7E841AC9 /* WindowImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowImpl.h; sourceTree = ""; }; + 18391E45702740FE9DD69695 /* ResizeScope.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ResizeScope.mm; sourceTree = ""; }; 1A002B9D232135EE00021753 /* app.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = app.mm; sourceTree = ""; }; 1A1852DB23E05814008F0DED /* deadlock.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = deadlock.mm; sourceTree = ""; }; 1A3E5EA723E9E83B00EDE661 /* rendertarget.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = rendertarget.mm; sourceTree = ""; }; @@ -130,6 +146,14 @@ 37C09D8721580FE4006A6758 /* SystemDialogs.mm */, AB7A61F02147C815003C5833 /* Products */, AB661C1C2148230E00291242 /* Frameworks */, + 18391676ECF0E983F4964357 /* WindowBaseImpl.mm */, + 183915BFF0E234CD3604A7CD /* WindowBaseImpl.h */, + 18391BBB7782C296D424071F /* INSWindowHolder.h */, + 183919BF108EB72A029F7671 /* WindowImpl.mm */, + 18391CD090AA776E7E841AC9 /* WindowImpl.h */, + 183913C6BFD6856BD42D19FD /* IWindowStateChanged.h */, + 18391E45702740FE9DD69695 /* ResizeScope.mm */, + 1839171D898F9BFC1373631A /* ResizeScope.h */, ); sourceTree = ""; }; @@ -150,6 +174,11 @@ files = ( 37155CE4233C00EB0034DCE9 /* menu.h in Headers */, BC11A5BE2608D58F0017BAD0 /* automation.h in Headers */, + 183916173528EC2737DBE5E1 /* WindowBaseImpl.h in Headers */, + 1839171DCC651B0638603AC4 /* INSWindowHolder.h in Headers */, + 183919D91DB9AAB5D700C2EA /* WindowImpl.h in Headers */, + 18391CF07316F819E76B617C /* IWindowStateChanged.h in Headers */, + 18391C28BF1823B5464FDD36 /* ResizeScope.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -229,6 +258,9 @@ AB00E4F72147CA920032A60A /* main.mm in Sources */, 37C09D8821580FE4006A6758 /* SystemDialogs.mm in Sources */, AB661C202148286E00291242 /* window.mm in Sources */, + 1839179A55FC1421BEE83330 /* WindowBaseImpl.mm in Sources */, + 1839125F057B0A4EB1760058 /* WindowImpl.mm in Sources */, + 18391068E48EF96E3DB5FDAB /* ResizeScope.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/native/Avalonia.Native/src/OSX/IWindowStateChanged.h b/native/Avalonia.Native/src/OSX/IWindowStateChanged.h new file mode 100644 index 0000000000..f0905da3ac --- /dev/null +++ b/native/Avalonia.Native/src/OSX/IWindowStateChanged.h @@ -0,0 +1,18 @@ +// +// Created by Dan Walmsley on 04/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#ifndef AVALONIA_NATIVE_OSX_IWINDOWSTATECHANGED_H +#define AVALONIA_NATIVE_OSX_IWINDOWSTATECHANGED_H + +struct IWindowStateChanged +{ + virtual void WindowStateChanged () = 0; + virtual void StartStateTransition () = 0; + virtual void EndStateTransition () = 0; + virtual SystemDecorations Decorations () = 0; + virtual AvnWindowState WindowState () = 0; +}; + +#endif //AVALONIA_NATIVE_OSX_IWINDOWSTATECHANGED_H diff --git a/native/Avalonia.Native/src/OSX/ResizeScope.h b/native/Avalonia.Native/src/OSX/ResizeScope.h new file mode 100644 index 0000000000..c57dc96690 --- /dev/null +++ b/native/Avalonia.Native/src/OSX/ResizeScope.h @@ -0,0 +1,23 @@ +// +// Created by Dan Walmsley on 04/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#ifndef AVALONIA_NATIVE_OSX_RESIZESCOPE_H +#define AVALONIA_NATIVE_OSX_RESIZESCOPE_H + +#include "window.h" +#include "avalonia-native.h" + +class ResizeScope +{ +public: + ResizeScope(AvnView* _Nonnull view, AvnPlatformResizeReason reason); + + ~ResizeScope(); +private: + AvnView* _Nonnull _view; + AvnPlatformResizeReason _restore; +}; + +#endif //AVALONIA_NATIVE_OSX_RESIZESCOPE_H diff --git a/native/Avalonia.Native/src/OSX/ResizeScope.mm b/native/Avalonia.Native/src/OSX/ResizeScope.mm new file mode 100644 index 0000000000..8644b41fba --- /dev/null +++ b/native/Avalonia.Native/src/OSX/ResizeScope.mm @@ -0,0 +1,17 @@ +// +// Created by Dan Walmsley on 04/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#import +#include "ResizeScope.h" + +ResizeScope::ResizeScope(AvnView *view, AvnPlatformResizeReason reason) { + _view = view; + _restore = [view getResizeReason]; + [view setResizeReason:reason]; +} + +ResizeScope::~ResizeScope() { + [_view setResizeReason:_restore]; +} diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index 2f9b05988f..ec013657dc 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -6,6 +6,7 @@ #ifndef AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H #define AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H +#import "rendertarget.h" #include "INSWindowHolder.h" class WindowBaseImpl : public virtual ComObject, diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index 482b6172af..bb40cd2a7e 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -11,6 +11,7 @@ #include "automation.h" #import "WindowBaseImpl.h" #import "cursor.h" +#include "ResizeScope.h" WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl) { _shown = false; diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.h b/native/Avalonia.Native/src/OSX/WindowImpl.h new file mode 100644 index 0000000000..b16e72fb32 --- /dev/null +++ b/native/Avalonia.Native/src/OSX/WindowImpl.h @@ -0,0 +1,99 @@ +// +// Created by Dan Walmsley on 04/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#ifndef AVALONIA_NATIVE_OSX_WINDOWIMPL_H +#define AVALONIA_NATIVE_OSX_WINDOWIMPL_H + + +#import "WindowBaseImpl.h" +#include "IWindowStateChanged.h" + +class WindowImpl : public virtual WindowBaseImpl, public virtual IAvnWindow, public IWindowStateChanged +{ +private: + bool _canResize; + bool _fullScreenActive; + SystemDecorations _decorations; + AvnWindowState _lastWindowState; + AvnWindowState _actualWindowState; + bool _inSetWindowState; + NSRect _preZoomSize; + bool _transitioningWindowState; + bool _isClientAreaExtended; + bool _isDialog; + AvnExtendClientAreaChromeHints _extendClientHints; + + FORWARD_IUNKNOWN() +BEGIN_INTERFACE_MAP() + INHERIT_INTERFACE_MAP(WindowBaseImpl) + INTERFACE_MAP_ENTRY(IAvnWindow, IID_IAvnWindow) + END_INTERFACE_MAP() + virtual ~WindowImpl() + { + } + + ComPtr WindowEvents; + + WindowImpl(IAvnWindowEvents* events, IAvnGlContext* gl); + + void HideOrShowTrafficLights (); + + virtual HRESULT Show (bool activate, bool isDialog) override; + + virtual HRESULT SetEnabled (bool enable) override; + + virtual HRESULT SetParent (IAvnWindow* parent) override; + + void StartStateTransition (); + + void EndStateTransition (); + + SystemDecorations Decorations (); + + AvnWindowState WindowState (); + + void WindowStateChanged (); + + bool UndecoratedIsMaximized (); + + bool IsZoomed (); + + void DoZoom(); + + virtual HRESULT SetCanResize(bool value) override; + + virtual HRESULT SetDecorations(SystemDecorations value) override; + + virtual HRESULT SetTitle (char* utf8title) override; + + virtual HRESULT SetTitleBarColor(AvnColor color) override; + + virtual HRESULT GetWindowState (AvnWindowState*ret) override; + + virtual HRESULT TakeFocusFromChildren () override; + + virtual HRESULT SetExtendClientArea (bool enable) override; + + virtual HRESULT SetExtendClientAreaHints (AvnExtendClientAreaChromeHints hints) override; + + virtual HRESULT GetExtendTitleBarHeight (double*ret) override; + + virtual HRESULT SetExtendTitleBarHeight (double value) override; + + void EnterFullScreenMode (); + + void ExitFullScreenMode (); + + virtual HRESULT SetWindowState (AvnWindowState state) override; + + virtual void OnResized () override; + + virtual bool IsDialog() override; + +protected: + virtual NSWindowStyleMask GetStyle() override; +}; + +#endif //AVALONIA_NATIVE_OSX_WINDOWIMPL_H diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm new file mode 100644 index 0000000000..0b7f9e0a13 --- /dev/null +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -0,0 +1,546 @@ +// +// Created by Dan Walmsley on 04/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#import +#import "window.h" +#include "automation.h" +#include "menu.h" +#import "WindowImpl.h" + + +WindowImpl::WindowImpl(IAvnWindowEvents *events, IAvnGlContext *gl) : WindowBaseImpl(events, gl) { + _isClientAreaExtended = false; + _extendClientHints = AvnDefaultChrome; + _fullScreenActive = false; + _canResize = true; + _decorations = SystemDecorationsFull; + _transitioningWindowState = false; + _inSetWindowState = false; + _lastWindowState = Normal; + _actualWindowState = Normal; + WindowEvents = events; + [Window setCanBecomeKeyAndMain]; + [Window disableCursorRects]; + [Window setTabbingMode:NSWindowTabbingModeDisallowed]; + [Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; +} + +void WindowImpl::HideOrShowTrafficLights() { + if (Window == nil) { + return; + } + + for (id subview in Window.contentView.superview.subviews) { + if ([subview isKindOfClass:NSClassFromString(@"NSTitlebarContainerView")]) { + NSView *titlebarView = [subview subviews][0]; + for (id button in titlebarView.subviews) { + if ([button isKindOfClass:[NSButton class]]) { + if (_isClientAreaExtended) { + auto wantsChrome = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome); + + [button setHidden:!wantsChrome]; + } else { + [button setHidden:(_decorations != SystemDecorationsFull)]; + } + + [button setWantsLayer:true]; + } + } + } + } +} + +HRESULT WindowImpl::Show(bool activate, bool isDialog) { + START_COM_CALL; + + @autoreleasepool { + _isDialog = isDialog; + WindowBaseImpl::Show(activate, isDialog); + + HideOrShowTrafficLights(); + + return SetWindowState(_lastWindowState); + } +} + +HRESULT WindowImpl::SetEnabled(bool enable) { + START_COM_CALL; + + @autoreleasepool { + [Window setEnabled:enable]; + return S_OK; + } +} + +HRESULT WindowImpl::SetParent(IAvnWindow *parent) { + START_COM_CALL; + + @autoreleasepool { + if (parent == nullptr) + return E_POINTER; + + auto cparent = dynamic_cast(parent); + if (cparent == nullptr) + return E_INVALIDARG; + + // If one tries to show a child window with a minimized parent window, then the parent window will be + // restored but macOS isn't kind enough to *tell* us that, so the window will be left in a non-interactive + // state. Detect this and explicitly restore the parent window ourselves to avoid this situation. + if (cparent->WindowState() == Minimized) + cparent->SetWindowState(Normal); + + [Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary]; + [cparent->Window addChildWindow:Window ordered:NSWindowAbove]; + + UpdateStyle(); + + return S_OK; + } +} + +void WindowImpl::StartStateTransition() { + _transitioningWindowState = true; +} + +void WindowImpl::EndStateTransition() { + _transitioningWindowState = false; +} + +SystemDecorations WindowImpl::Decorations() { + return _decorations; +} + +AvnWindowState WindowImpl::WindowState() { + return _lastWindowState; +} + +void WindowImpl::WindowStateChanged() { + if (_shown && !_inSetWindowState && !_transitioningWindowState) { + AvnWindowState state; + GetWindowState(&state); + + if (_lastWindowState != state) { + if (_isClientAreaExtended) { + if (_lastWindowState == FullScreen) { + // we exited fs. + if (_extendClientHints & AvnOSXThickTitleBar) { + Window.toolbar = [NSToolbar new]; + Window.toolbar.showsBaselineSeparator = false; + } + + [Window setTitlebarAppearsTransparent:true]; + + [StandardContainer setFrameSize:StandardContainer.frame.size]; + } else if (state == FullScreen) { + // we entered fs. + if (_extendClientHints & AvnOSXThickTitleBar) { + Window.toolbar = nullptr; + } + + [Window setTitlebarAppearsTransparent:false]; + + [StandardContainer setFrameSize:StandardContainer.frame.size]; + } + } + + _lastWindowState = state; + _actualWindowState = state; + WindowEvents->WindowStateChanged(state); + } + } +} + +bool WindowImpl::UndecoratedIsMaximized() { + auto windowSize = [Window frame]; + auto available = [Window screen].visibleFrame; + return CGRectEqualToRect(windowSize, available); +} + +bool WindowImpl::IsZoomed() { + return _decorations == SystemDecorationsFull ? [Window isZoomed] : UndecoratedIsMaximized(); +} + +void WindowImpl::DoZoom() { + switch (_decorations) { + case SystemDecorationsNone: + case SystemDecorationsBorderOnly: + [Window setFrame:[Window screen].visibleFrame display:true]; + break; + + + case SystemDecorationsFull: + [Window performZoom:Window]; + break; + } +} + +HRESULT WindowImpl::SetCanResize(bool value) { + START_COM_CALL; + + @autoreleasepool { + _canResize = value; + UpdateStyle(); + return S_OK; + } +} + +HRESULT WindowImpl::SetDecorations(SystemDecorations value) { + START_COM_CALL; + + @autoreleasepool { + auto currentWindowState = _lastWindowState; + _decorations = value; + + if (_fullScreenActive) { + return S_OK; + } + + UpdateStyle(); + + HideOrShowTrafficLights(); + + switch (_decorations) { + case SystemDecorationsNone: + [Window setHasShadow:NO]; + [Window setTitleVisibility:NSWindowTitleHidden]; + [Window setTitlebarAppearsTransparent:YES]; + + if (currentWindowState == Maximized) { + if (!UndecoratedIsMaximized()) { + DoZoom(); + } + } + break; + + case SystemDecorationsBorderOnly: + [Window setHasShadow:YES]; + [Window setTitleVisibility:NSWindowTitleHidden]; + [Window setTitlebarAppearsTransparent:YES]; + + if (currentWindowState == Maximized) { + if (!UndecoratedIsMaximized()) { + DoZoom(); + } + } + break; + + case SystemDecorationsFull: + [Window setHasShadow:YES]; + [Window setTitleVisibility:NSWindowTitleVisible]; + [Window setTitlebarAppearsTransparent:NO]; + [Window setTitle:_lastTitle]; + + if (currentWindowState == Maximized) { + auto newFrame = [Window contentRectForFrameRect:[Window frame]].size; + + [View setFrameSize:newFrame]; + } + break; + } + + return S_OK; + } +} + +HRESULT WindowImpl::SetTitle(char *utf8title) { + START_COM_CALL; + + @autoreleasepool { + _lastTitle = [NSString stringWithUTF8String:(const char *) utf8title]; + [Window setTitle:_lastTitle]; + + return S_OK; + } +} + +HRESULT WindowImpl::SetTitleBarColor(AvnColor color) { + START_COM_CALL; + + @autoreleasepool { + float a = (float) color.Alpha / 255.0f; + float r = (float) color.Red / 255.0f; + float g = (float) color.Green / 255.0f; + float b = (float) color.Blue / 255.0f; + + auto nscolor = [NSColor colorWithSRGBRed:r green:g blue:b alpha:a]; + + // Based on the titlebar color we have to choose either light or dark + // OSX doesnt let you set a foreground color for titlebar. + if ((r * 0.299 + g * 0.587 + b * 0.114) > 186.0f / 255.0f) { + [Window setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameVibrantLight]]; + } else { + [Window setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameVibrantDark]]; + } + + [Window setTitlebarAppearsTransparent:true]; + [Window setBackgroundColor:nscolor]; + } + + return S_OK; +} + +HRESULT WindowImpl::GetWindowState(AvnWindowState *ret) { + START_COM_CALL; + + @autoreleasepool { + if (ret == nullptr) { + return E_POINTER; + } + + if (([Window styleMask] & NSWindowStyleMaskFullScreen) == NSWindowStyleMaskFullScreen) { + *ret = FullScreen; + return S_OK; + } + + if ([Window isMiniaturized]) { + *ret = Minimized; + return S_OK; + } + + if (IsZoomed()) { + *ret = Maximized; + return S_OK; + } + + *ret = Normal; + + return S_OK; + } +} + +HRESULT WindowImpl::TakeFocusFromChildren() { + START_COM_CALL; + + @autoreleasepool { + if (Window == nil) + return S_OK; + if ([Window isKeyWindow]) + [Window makeFirstResponder:View]; + + return S_OK; + } +} + +HRESULT WindowImpl::SetExtendClientArea(bool enable) { + START_COM_CALL; + + @autoreleasepool { + _isClientAreaExtended = enable; + + if (enable) { + Window.titleVisibility = NSWindowTitleHidden; + + [Window setTitlebarAppearsTransparent:true]; + + auto wantsTitleBar = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome); + + if (wantsTitleBar) { + [StandardContainer ShowTitleBar:true]; + } else { + [StandardContainer ShowTitleBar:false]; + } + + if (_extendClientHints & AvnOSXThickTitleBar) { + Window.toolbar = [NSToolbar new]; + Window.toolbar.showsBaselineSeparator = false; + } else { + Window.toolbar = nullptr; + } + } else { + Window.titleVisibility = NSWindowTitleVisible; + Window.toolbar = nullptr; + [Window setTitlebarAppearsTransparent:false]; + View.layer.zPosition = 0; + } + + [Window setIsExtended:enable]; + + HideOrShowTrafficLights(); + + UpdateStyle(); + + return S_OK; + } +} + +HRESULT WindowImpl::SetExtendClientAreaHints(AvnExtendClientAreaChromeHints hints) { + START_COM_CALL; + + @autoreleasepool { + _extendClientHints = hints; + + SetExtendClientArea(_isClientAreaExtended); + return S_OK; + } +} + +HRESULT WindowImpl::GetExtendTitleBarHeight(double *ret) { + START_COM_CALL; + + @autoreleasepool { + if (ret == nullptr) { + return E_POINTER; + } + + *ret = [Window getExtendedTitleBarHeight]; + + return S_OK; + } +} + +HRESULT WindowImpl::SetExtendTitleBarHeight(double value) { + START_COM_CALL; + + @autoreleasepool { + [StandardContainer SetTitleBarHeightHint:value]; + return S_OK; + } +} + +void WindowImpl::EnterFullScreenMode() { + _fullScreenActive = true; + + [Window setTitle:_lastTitle]; + [Window toggleFullScreen:nullptr]; +} + +void WindowImpl::ExitFullScreenMode() { + [Window toggleFullScreen:nullptr]; + + _fullScreenActive = false; + + SetDecorations(_decorations); +} + +HRESULT WindowImpl::SetWindowState(AvnWindowState state) { + START_COM_CALL; + + @autoreleasepool { + if (Window == nullptr) { + return S_OK; + } + + if (_actualWindowState == state) { + return S_OK; + } + + _inSetWindowState = true; + + auto currentState = _actualWindowState; + _lastWindowState = state; + + if (currentState == Normal) { + _preZoomSize = [Window frame]; + } + + if (_shown) { + switch (state) { + case Maximized: + if (currentState == FullScreen) { + ExitFullScreenMode(); + } + + lastPositionSet.X = 0; + lastPositionSet.Y = 0; + + if ([Window isMiniaturized]) { + [Window deminiaturize:Window]; + } + + if (!IsZoomed()) { + DoZoom(); + } + break; + + case Minimized: + if (currentState == FullScreen) { + ExitFullScreenMode(); + } else { + [Window miniaturize:Window]; + } + break; + + case FullScreen: + if ([Window isMiniaturized]) { + [Window deminiaturize:Window]; + } + + EnterFullScreenMode(); + break; + + case Normal: + if ([Window isMiniaturized]) { + [Window deminiaturize:Window]; + } + + if (currentState == FullScreen) { + ExitFullScreenMode(); + } + + if (IsZoomed()) { + if (_decorations == SystemDecorationsFull) { + DoZoom(); + } else { + [Window setFrame:_preZoomSize display:true]; + auto newFrame = [Window contentRectForFrameRect:[Window frame]].size; + + [View setFrameSize:newFrame]; + } + + } + break; + } + + _actualWindowState = _lastWindowState; + WindowEvents->WindowStateChanged(_actualWindowState); + } + + + _inSetWindowState = false; + + return S_OK; + } +} + +void WindowImpl::OnResized() { + if (_shown && !_inSetWindowState && !_transitioningWindowState) { + WindowStateChanged(); + } +} + +bool WindowImpl::IsDialog() { + return _isDialog; +} + +NSWindowStyleMask WindowImpl::GetStyle() { + unsigned long s = NSWindowStyleMaskBorderless; + + switch (_decorations) { + case SystemDecorationsNone: + s = s | NSWindowStyleMaskFullSizeContentView; + break; + + case SystemDecorationsBorderOnly: + s = s | NSWindowStyleMaskTitled | NSWindowStyleMaskFullSizeContentView; + break; + + case SystemDecorationsFull: + s = s | NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskBorderless; + + if (_canResize) { + s = s | NSWindowStyleMaskResizable; + } + break; + } + + if ([Window parentWindow] == nullptr) { + s |= NSWindowStyleMaskMiniaturizable; + } + + if (_isClientAreaExtended) { + s |= NSWindowStyleMaskFullSizeContentView | NSWindowStyleMaskTexturedBackground; + } + return s; +} diff --git a/native/Avalonia.Native/src/OSX/automation.h b/native/Avalonia.Native/src/OSX/automation.h index 79ccfd0eaa..1727d21f27 100644 --- a/native/Avalonia.Native/src/OSX/automation.h +++ b/native/Avalonia.Native/src/OSX/automation.h @@ -1,5 +1,4 @@ #import -#import "window.h" NS_ASSUME_NONNULL_BEGIN diff --git a/native/Avalonia.Native/src/OSX/automation.mm b/native/Avalonia.Native/src/OSX/automation.mm index 226e8810c7..087d15a248 100644 --- a/native/Avalonia.Native/src/OSX/automation.mm +++ b/native/Avalonia.Native/src/OSX/automation.mm @@ -1,5 +1,6 @@ #include "common.h" #import "automation.h" +#import "window.h" #include "AvnString.h" #import "INSWindowHolder.h" diff --git a/native/Avalonia.Native/src/OSX/window.h b/native/Avalonia.Native/src/OSX/window.h index 68dc673917..365172622f 100644 --- a/native/Avalonia.Native/src/OSX/window.h +++ b/native/Avalonia.Native/src/OSX/window.h @@ -1,7 +1,7 @@ #ifndef window_h #define window_h - +#import "avalonia-native.h" class WindowBaseImpl; @interface AvnView : NSView @@ -41,32 +41,4 @@ class WindowBaseImpl; -(bool) isDialog; @end -struct IWindowStateChanged -{ - virtual void WindowStateChanged () = 0; - virtual void StartStateTransition () = 0; - virtual void EndStateTransition () = 0; - virtual SystemDecorations Decorations () = 0; - virtual AvnWindowState WindowState () = 0; -}; - -class ResizeScope -{ -public: - ResizeScope(AvnView* _Nonnull view, AvnPlatformResizeReason reason) - { - _view = view; - _restore = [view getResizeReason]; - [view setResizeReason:reason]; - } - - ~ResizeScope() - { - [_view setResizeReason:_restore]; - } -private: - AvnView* _Nonnull _view; - AvnPlatformResizeReason _restore; -}; - #endif /* window_h */ diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index 9676515b16..a1d231f30a 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -5,673 +5,10 @@ #include "rendertarget.h" #include "automation.h" #import "WindowBaseImpl.h" +#include "WindowImpl.h" +#include "IWindowStateChanged.h" -class WindowImpl : public virtual WindowBaseImpl, public virtual IAvnWindow, public IWindowStateChanged -{ -private: - bool _canResize; - bool _fullScreenActive; - SystemDecorations _decorations; - AvnWindowState _lastWindowState; - AvnWindowState _actualWindowState; - bool _inSetWindowState; - NSRect _preZoomSize; - bool _transitioningWindowState; - bool _isClientAreaExtended; - bool _isDialog; - AvnExtendClientAreaChromeHints _extendClientHints; - - FORWARD_IUNKNOWN() - BEGIN_INTERFACE_MAP() - INHERIT_INTERFACE_MAP(WindowBaseImpl) - INTERFACE_MAP_ENTRY(IAvnWindow, IID_IAvnWindow) - END_INTERFACE_MAP() - virtual ~WindowImpl() - { - } - - ComPtr WindowEvents; - WindowImpl(IAvnWindowEvents* events, IAvnGlContext* gl) : WindowBaseImpl(events, gl) - { - _isClientAreaExtended = false; - _extendClientHints = AvnDefaultChrome; - _fullScreenActive = false; - _canResize = true; - _decorations = SystemDecorationsFull; - _transitioningWindowState = false; - _inSetWindowState = false; - _lastWindowState = Normal; - _actualWindowState = Normal; - WindowEvents = events; - [Window setCanBecomeKeyAndMain]; - [Window disableCursorRects]; - [Window setTabbingMode:NSWindowTabbingModeDisallowed]; - [Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; - } - - void HideOrShowTrafficLights () - { - if (Window == nil) - { - return; - } - - for (id subview in Window.contentView.superview.subviews) { - if ([subview isKindOfClass:NSClassFromString(@"NSTitlebarContainerView")]) { - NSView *titlebarView = [subview subviews][0]; - for (id button in titlebarView.subviews) { - if ([button isKindOfClass:[NSButton class]]) - { - if(_isClientAreaExtended) - { - auto wantsChrome = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome); - - [button setHidden: !wantsChrome]; - } - else - { - [button setHidden: (_decorations != SystemDecorationsFull)]; - } - - [button setWantsLayer:true]; - } - } - } - } - } - - virtual HRESULT Show (bool activate, bool isDialog) override - { - START_COM_CALL; - - @autoreleasepool - { - _isDialog = isDialog; - WindowBaseImpl::Show(activate, isDialog); - - HideOrShowTrafficLights(); - - return SetWindowState(_lastWindowState); - } - } - - virtual HRESULT SetEnabled (bool enable) override - { - START_COM_CALL; - - @autoreleasepool - { - [Window setEnabled:enable]; - return S_OK; - } - } - - virtual HRESULT SetParent (IAvnWindow* parent) override - { - START_COM_CALL; - - @autoreleasepool - { - if(parent == nullptr) - return E_POINTER; - - auto cparent = dynamic_cast(parent); - if(cparent == nullptr) - return E_INVALIDARG; - - // If one tries to show a child window with a minimized parent window, then the parent window will be - // restored but macOS isn't kind enough to *tell* us that, so the window will be left in a non-interactive - // state. Detect this and explicitly restore the parent window ourselves to avoid this situation. - if (cparent->WindowState() == Minimized) - cparent->SetWindowState(Normal); - - [Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary]; - [cparent->Window addChildWindow:Window ordered:NSWindowAbove]; - - UpdateStyle(); - - return S_OK; - } - } - - void StartStateTransition () override - { - _transitioningWindowState = true; - } - - void EndStateTransition () override - { - _transitioningWindowState = false; - } - - SystemDecorations Decorations () override - { - return _decorations; - } - - AvnWindowState WindowState () override - { - return _lastWindowState; - } - - void WindowStateChanged () override - { - if(_shown && !_inSetWindowState && !_transitioningWindowState) - { - AvnWindowState state; - GetWindowState(&state); - - if(_lastWindowState != state) - { - if(_isClientAreaExtended) - { - if(_lastWindowState == FullScreen) - { - // we exited fs. - if(_extendClientHints & AvnOSXThickTitleBar) - { - Window.toolbar = [NSToolbar new]; - Window.toolbar.showsBaselineSeparator = false; - } - - [Window setTitlebarAppearsTransparent:true]; - - [StandardContainer setFrameSize: StandardContainer.frame.size]; - } - else if(state == FullScreen) - { - // we entered fs. - if(_extendClientHints & AvnOSXThickTitleBar) - { - Window.toolbar = nullptr; - } - - [Window setTitlebarAppearsTransparent:false]; - - [StandardContainer setFrameSize: StandardContainer.frame.size]; - } - } - - _lastWindowState = state; - _actualWindowState = state; - WindowEvents->WindowStateChanged(state); - } - } - } - - bool UndecoratedIsMaximized () - { - auto windowSize = [Window frame]; - auto available = [Window screen].visibleFrame; - return CGRectEqualToRect(windowSize, available); - } - - bool IsZoomed () - { - return _decorations == SystemDecorationsFull ? [Window isZoomed] : UndecoratedIsMaximized(); - } - - void DoZoom() - { - switch (_decorations) - { - case SystemDecorationsNone: - case SystemDecorationsBorderOnly: - [Window setFrame:[Window screen].visibleFrame display:true]; - break; - - - case SystemDecorationsFull: - [Window performZoom:Window]; - break; - } - } - - virtual HRESULT SetCanResize(bool value) override - { - START_COM_CALL; - - @autoreleasepool - { - _canResize = value; - UpdateStyle(); - return S_OK; - } - } - - virtual HRESULT SetDecorations(SystemDecorations value) override - { - START_COM_CALL; - - @autoreleasepool - { - auto currentWindowState = _lastWindowState; - _decorations = value; - - if(_fullScreenActive) - { - return S_OK; - } - - UpdateStyle(); - - HideOrShowTrafficLights(); - - switch (_decorations) - { - case SystemDecorationsNone: - [Window setHasShadow:NO]; - [Window setTitleVisibility:NSWindowTitleHidden]; - [Window setTitlebarAppearsTransparent:YES]; - - if(currentWindowState == Maximized) - { - if(!UndecoratedIsMaximized()) - { - DoZoom(); - } - } - break; - - case SystemDecorationsBorderOnly: - [Window setHasShadow:YES]; - [Window setTitleVisibility:NSWindowTitleHidden]; - [Window setTitlebarAppearsTransparent:YES]; - - if(currentWindowState == Maximized) - { - if(!UndecoratedIsMaximized()) - { - DoZoom(); - } - } - break; - - case SystemDecorationsFull: - [Window setHasShadow:YES]; - [Window setTitleVisibility:NSWindowTitleVisible]; - [Window setTitlebarAppearsTransparent:NO]; - [Window setTitle:_lastTitle]; - - if(currentWindowState == Maximized) - { - auto newFrame = [Window contentRectForFrameRect:[Window frame]].size; - - [View setFrameSize:newFrame]; - } - break; - } - - return S_OK; - } - } - - virtual HRESULT SetTitle (char* utf8title) override - { - START_COM_CALL; - - @autoreleasepool - { - _lastTitle = [NSString stringWithUTF8String:(const char*)utf8title]; - [Window setTitle:_lastTitle]; - - return S_OK; - } - } - - virtual HRESULT SetTitleBarColor(AvnColor color) override - { - START_COM_CALL; - - @autoreleasepool - { - float a = (float)color.Alpha / 255.0f; - float r = (float)color.Red / 255.0f; - float g = (float)color.Green / 255.0f; - float b = (float)color.Blue / 255.0f; - - auto nscolor = [NSColor colorWithSRGBRed:r green:g blue:b alpha:a]; - - // Based on the titlebar color we have to choose either light or dark - // OSX doesnt let you set a foreground color for titlebar. - if ((r*0.299 + g*0.587 + b*0.114) > 186.0f / 255.0f) - { - [Window setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameVibrantLight]]; - } - else - { - [Window setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameVibrantDark]]; - } - - [Window setTitlebarAppearsTransparent:true]; - [Window setBackgroundColor:nscolor]; - } - - return S_OK; - } - - virtual HRESULT GetWindowState (AvnWindowState*ret) override - { - START_COM_CALL; - - @autoreleasepool - { - if(ret == nullptr) - { - return E_POINTER; - } - - if(([Window styleMask] & NSWindowStyleMaskFullScreen) == NSWindowStyleMaskFullScreen) - { - *ret = FullScreen; - return S_OK; - } - - if([Window isMiniaturized]) - { - *ret = Minimized; - return S_OK; - } - - if(IsZoomed()) - { - *ret = Maximized; - return S_OK; - } - - *ret = Normal; - - return S_OK; - } - } - - virtual HRESULT TakeFocusFromChildren () override - { - START_COM_CALL; - - @autoreleasepool - { - if(Window == nil) - return S_OK; - if([Window isKeyWindow]) - [Window makeFirstResponder: View]; - - return S_OK; - } - } - - virtual HRESULT SetExtendClientArea (bool enable) override - { - START_COM_CALL; - - @autoreleasepool - { - _isClientAreaExtended = enable; - - if(enable) - { - Window.titleVisibility = NSWindowTitleHidden; - - [Window setTitlebarAppearsTransparent:true]; - - auto wantsTitleBar = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome); - - if (wantsTitleBar) - { - [StandardContainer ShowTitleBar:true]; - } - else - { - [StandardContainer ShowTitleBar:false]; - } - - if(_extendClientHints & AvnOSXThickTitleBar) - { - Window.toolbar = [NSToolbar new]; - Window.toolbar.showsBaselineSeparator = false; - } - else - { - Window.toolbar = nullptr; - } - } - else - { - Window.titleVisibility = NSWindowTitleVisible; - Window.toolbar = nullptr; - [Window setTitlebarAppearsTransparent:false]; - View.layer.zPosition = 0; - } - - [Window setIsExtended:enable]; - - HideOrShowTrafficLights(); - - UpdateStyle(); - - return S_OK; - } - } - - virtual HRESULT SetExtendClientAreaHints (AvnExtendClientAreaChromeHints hints) override - { - START_COM_CALL; - - @autoreleasepool - { - _extendClientHints = hints; - - SetExtendClientArea(_isClientAreaExtended); - return S_OK; - } - } - - virtual HRESULT GetExtendTitleBarHeight (double*ret) override - { - START_COM_CALL; - - @autoreleasepool - { - if(ret == nullptr) - { - return E_POINTER; - } - - *ret = [Window getExtendedTitleBarHeight]; - - return S_OK; - } - } - - virtual HRESULT SetExtendTitleBarHeight (double value) override - { - START_COM_CALL; - - @autoreleasepool - { - [StandardContainer SetTitleBarHeightHint:value]; - return S_OK; - } - } - - void EnterFullScreenMode () - { - _fullScreenActive = true; - - [Window setTitle:_lastTitle]; - [Window toggleFullScreen:nullptr]; - } - - void ExitFullScreenMode () - { - [Window toggleFullScreen:nullptr]; - - _fullScreenActive = false; - - SetDecorations(_decorations); - } - - virtual HRESULT SetWindowState (AvnWindowState state) override - { - START_COM_CALL; - - @autoreleasepool - { - if(Window == nullptr) - { - return S_OK; - } - - if(_actualWindowState == state) - { - return S_OK; - } - - _inSetWindowState = true; - - auto currentState = _actualWindowState; - _lastWindowState = state; - - if(currentState == Normal) - { - _preZoomSize = [Window frame]; - } - - if(_shown) - { - switch (state) { - case Maximized: - if(currentState == FullScreen) - { - ExitFullScreenMode(); - } - - lastPositionSet.X = 0; - lastPositionSet.Y = 0; - - if([Window isMiniaturized]) - { - [Window deminiaturize:Window]; - } - - if(!IsZoomed()) - { - DoZoom(); - } - break; - - case Minimized: - if(currentState == FullScreen) - { - ExitFullScreenMode(); - } - else - { - [Window miniaturize:Window]; - } - break; - - case FullScreen: - if([Window isMiniaturized]) - { - [Window deminiaturize:Window]; - } - - EnterFullScreenMode(); - break; - - case Normal: - if([Window isMiniaturized]) - { - [Window deminiaturize:Window]; - } - - if(currentState == FullScreen) - { - ExitFullScreenMode(); - } - - if(IsZoomed()) - { - if(_decorations == SystemDecorationsFull) - { - DoZoom(); - } - else - { - [Window setFrame:_preZoomSize display:true]; - auto newFrame = [Window contentRectForFrameRect:[Window frame]].size; - - [View setFrameSize:newFrame]; - } - - } - break; - } - - _actualWindowState = _lastWindowState; - WindowEvents->WindowStateChanged(_actualWindowState); - } - - - _inSetWindowState = false; - - return S_OK; - } - } - - virtual void OnResized () override - { - if(_shown && !_inSetWindowState && !_transitioningWindowState) - { - WindowStateChanged(); - } - } - - virtual bool IsDialog() override - { - return _isDialog; - } - -protected: - virtual NSWindowStyleMask GetStyle() override - { - unsigned long s = NSWindowStyleMaskBorderless; - - switch (_decorations) - { - case SystemDecorationsNone: - s = s | NSWindowStyleMaskFullSizeContentView; - break; - - case SystemDecorationsBorderOnly: - s = s | NSWindowStyleMaskTitled | NSWindowStyleMaskFullSizeContentView; - break; - - case SystemDecorationsFull: - s = s | NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskBorderless; - - if(_canResize) - { - s = s | NSWindowStyleMaskResizable; - } - break; - } - - if([Window parentWindow] == nullptr) - { - s |= NSWindowStyleMaskMiniaturizable; - } - - if(_isClientAreaExtended) - { - s |= NSWindowStyleMaskFullSizeContentView | NSWindowStyleMaskTexturedBackground; - } - return s; - } -}; - NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEventTrackingRunLoopMode, NSModalPanelRunLoopMode, NSRunLoopCommonModes, NSConnectionReplyMode, nil]; @implementation AutoFitContentView From 947590d453ee9a3490088d09f4cbca3edebee74f Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 4 May 2022 18:10:07 +0100 Subject: [PATCH 270/416] fix warnings --- native/Avalonia.Native/src/OSX/WindowImpl.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.h b/native/Avalonia.Native/src/OSX/WindowImpl.h index b16e72fb32..fae53acc22 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowImpl.h @@ -46,15 +46,15 @@ BEGIN_INTERFACE_MAP() virtual HRESULT SetParent (IAvnWindow* parent) override; - void StartStateTransition (); + void StartStateTransition () override ; - void EndStateTransition (); + void EndStateTransition () override ; - SystemDecorations Decorations (); + SystemDecorations Decorations () override ; - AvnWindowState WindowState (); + AvnWindowState WindowState () override ; - void WindowStateChanged (); + void WindowStateChanged () override ; bool UndecoratedIsMaximized (); From 68b4173743353b9901fbca06d4a41add3adbb5ae Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 4 May 2022 18:39:30 +0100 Subject: [PATCH 271/416] remove unused window code. --- .../Avalonia.Native/src/OSX/WindowBaseImpl.h | 7 +- .../Avalonia.Native/src/OSX/WindowBaseImpl.mm | 15 ++-- native/Avalonia.Native/src/OSX/WindowImpl.h | 2 - native/Avalonia.Native/src/OSX/WindowImpl.mm | 6 -- native/Avalonia.Native/src/OSX/window.h | 12 ++-- native/Avalonia.Native/src/OSX/window.mm | 68 ++++++------------- 6 files changed, 32 insertions(+), 78 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index ec013657dc..94175b8187 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -35,7 +35,6 @@ BEGIN_INTERFACE_MAP() NSObject *renderTarget; AvnPoint lastPositionSet; NSString *_lastTitle; - IAvnMenu *_mainMenu; bool _shown; bool _inResize; @@ -76,13 +75,13 @@ BEGIN_INTERFACE_MAP() virtual HRESULT Resize(double x, double y, AvnPlatformResizeReason reason) override; - virtual HRESULT Invalidate(AvnRect rect) override; + virtual HRESULT Invalidate(__attribute__((unused)) AvnRect rect) override; virtual HRESULT SetMainMenu(IAvnMenu *menu) override; virtual HRESULT BeginMoveDrag() override; - virtual HRESULT BeginResizeDrag(AvnWindowEdge edge) override; + virtual HRESULT BeginResizeDrag(__attribute__((unused)) AvnWindowEdge edge) override; virtual HRESULT GetPosition(AvnPoint *ret) override; @@ -115,8 +114,6 @@ protected: void UpdateStyle(); -public: - virtual void OnResized(); }; #endif //AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index bb40cd2a7e..9959d6a034 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -16,7 +16,6 @@ WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl) { _shown = false; _inResize = false; - _mainMenu = nullptr; BaseEvents = events; _glContext = gl; renderTarget = [[IOSurfaceRenderTarget alloc] initWithOpenGlContext:gl]; @@ -279,7 +278,7 @@ HRESULT WindowBaseImpl::Resize(double x, double y, AvnPlatformResizeReason reaso } } -HRESULT WindowBaseImpl::Invalidate(AvnRect rect) { +HRESULT WindowBaseImpl::Invalidate(__attribute__((unused)) AvnRect rect) { START_COM_CALL; @autoreleasepool { @@ -292,8 +291,6 @@ HRESULT WindowBaseImpl::Invalidate(AvnRect rect) { HRESULT WindowBaseImpl::SetMainMenu(IAvnMenu *menu) { START_COM_CALL; - _mainMenu = menu; - auto nativeMenu = dynamic_cast(menu); auto nsmenu = nativeMenu->GetNative(); @@ -323,7 +320,7 @@ HRESULT WindowBaseImpl::BeginMoveDrag() { } } -HRESULT WindowBaseImpl::BeginResizeDrag(AvnWindowEdge edge) { +HRESULT WindowBaseImpl::BeginResizeDrag(__attribute__((unused)) AvnWindowEdge edge) { START_COM_CALL; return S_OK; @@ -431,7 +428,7 @@ HRESULT WindowBaseImpl::CreateGlRenderTarget(IAvnGlSurfaceRenderTarget **ppv) { if (View == NULL) return E_FAIL; *ppv = [renderTarget createSurfaceRenderTarget]; - return *ppv == nil ? E_FAIL : S_OK; + return static_cast(*ppv == nil ? E_FAIL : S_OK); } HRESULT WindowBaseImpl::CreateNativeControlHost(IAvnNativeControlHost **retOut) { @@ -505,8 +502,4 @@ NSWindowStyleMask WindowBaseImpl::GetStyle() { void WindowBaseImpl::UpdateStyle() { [Window setStyleMask:GetStyle()]; -} - -void WindowBaseImpl::OnResized() { - -} +} \ No newline at end of file diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.h b/native/Avalonia.Native/src/OSX/WindowImpl.h index fae53acc22..b229921baa 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowImpl.h @@ -88,8 +88,6 @@ BEGIN_INTERFACE_MAP() virtual HRESULT SetWindowState (AvnWindowState state) override; - virtual void OnResized () override; - virtual bool IsDialog() override; protected: diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index 0b7f9e0a13..9cf5160c97 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -504,12 +504,6 @@ HRESULT WindowImpl::SetWindowState(AvnWindowState state) { } } -void WindowImpl::OnResized() { - if (_shown && !_inSetWindowState && !_transitioningWindowState) { - WindowStateChanged(); - } -} - bool WindowImpl::IsDialog() { return _isDialog; } diff --git a/native/Avalonia.Native/src/OSX/window.h b/native/Avalonia.Native/src/OSX/window.h index 365172622f..271dd2534e 100644 --- a/native/Avalonia.Native/src/OSX/window.h +++ b/native/Avalonia.Native/src/OSX/window.h @@ -2,6 +2,9 @@ #define window_h #import "avalonia-native.h" + +@class AvnMenu; + class WindowBaseImpl; @interface AvnView : NSView @@ -10,7 +13,7 @@ class WindowBaseImpl; -(AvnPoint) translateLocalPoint:(AvnPoint)pt; -(void) setSwRenderedFrame: (AvnFramebuffer* _Nonnull) fb dispose: (IUnknown* _Nonnull) dispose; -(void) onClosed; --(AvnPixelSize) getPixelSize; + -(AvnPlatformResizeReason) getResizeReason; -(void) setResizeReason:(AvnPlatformResizeReason)reason; + (AvnPoint)toAvnPoint:(CGPoint)p; @@ -20,12 +23,11 @@ class WindowBaseImpl; -(AutoFitContentView* _Nonnull) initWithContent: (NSView* _Nonnull) content; -(void) ShowTitleBar: (bool) show; -(void) SetTitleBarHeightHint: (double) height; --(void) SetContent: (NSView* _Nonnull) content; + -(void) ShowBlur: (bool) show; @end @interface AvnWindow : NSWindow -+(void) closeAll; -(AvnWindow* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent; -(void) setCanBecomeKeyAndMain; -(void) pollModalSession: (NSModalSession _Nonnull) session; @@ -34,8 +36,8 @@ class WindowBaseImpl; -(void) setEnabled: (bool) enable; -(void) showAppMenuOnly; -(void) showWindowMenuWithAppMenu; --(void) applyMenu:(NSMenu* _Nullable)menu; --(double) getScaling; +-(void) applyMenu:(AvnMenu* _Nullable)menu; + -(double) getExtendedTitleBarHeight; -(void) setIsExtended:(bool)value; -(bool) isDialog; diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index a1d231f30a..fe80142f1a 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -1,3 +1,4 @@ +#import #include "common.h" #import "window.h" #include "KeyTransform.h" @@ -6,10 +7,6 @@ #include "automation.h" #import "WindowBaseImpl.h" #include "WindowImpl.h" -#include "IWindowStateChanged.h" - - -NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEventTrackingRunLoopMode, NSModalPanelRunLoopMode, NSRunLoopCommonModes, NSConnectionReplyMode, nil]; @implementation AutoFitContentView { @@ -106,26 +103,13 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent _settingSize = false; } - --(void) SetContent: (NSView* _Nonnull) content -{ - if(content != nullptr) - { - [content removeFromSuperview]; - [self addSubview:content]; - _content = content; - } -} @end @implementation AvnView { ComPtr _parent; - ComPtr _swRenderedFrame; - AvnFramebuffer _swRenderedFrameBuffer; - bool _queuedDisplayFromThread; NSTrackingArea* _area; - bool _isLeftPressed, _isMiddlePressed, _isRightPressed, _isXButton1Pressed, _isXButton2Pressed, _isMouseOver; + bool _isLeftPressed, _isMiddlePressed, _isRightPressed, _isXButton1Pressed, _isXButton2Pressed; AvnInputModifiers _modifierState; NSEvent* _lastMouseDownEvent; bool _lastKeyHandled; @@ -143,11 +127,6 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent } } --(AvnPixelSize) getPixelSize -{ - return _lastPixelSize; -} - - (NSEvent*) lastMouseDownEvent { return _lastMouseDownEvent; @@ -155,7 +134,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent - (void) updateRenderTarget { - [_renderTarget resize:_lastPixelSize withScale: [[self window] backingScaleFactor]]; + [_renderTarget resize:_lastPixelSize withScale:static_cast([[self window] backingScaleFactor])]; [self setNeedsDisplayInRect:[self frame]]; } @@ -345,7 +324,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent auto localPoint = [self convertPoint:[event locationInWindow] toView:self]; auto avnPoint = [AvnView toAvnPoint:localPoint]; auto point = [self translateLocalPoint:avnPoint]; - AvnVector delta; + AvnVector delta = { 0, 0}; if(type == Wheel) { @@ -378,7 +357,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent delta.Y = [event deltaY]; } - auto timestamp = [event timestamp] * 1000; + uint32 timestamp = static_cast([event timestamp] * 1000); auto modifiers = [self getModifiers:[event modifierFlags]]; if(type != AvnRawMouseEventType::Move || @@ -437,6 +416,9 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent _isXButton2Pressed = true; [self mouseEvent:event withType:XButton2Down]; break; + + default: + break; } } @@ -470,6 +452,9 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent _isXButton2Pressed = false; [self mouseEvent:event withType:XButton2Up]; break; + + default: + break; } } @@ -523,13 +508,11 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent - (void)mouseEntered:(NSEvent *)event { - _isMouseOver = true; [super mouseEntered:event]; } - (void)mouseExited:(NSEvent *)event { - _isMouseOver = false; [self mouseEvent:event withType:LeaveWindow]; [super mouseExited:event]; } @@ -543,7 +526,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent auto key = s_KeyMap[[event keyCode]]; - auto timestamp = [event timestamp] * 1000; + uint32_t timestamp = static_cast([event timestamp] * 1000); auto modifiers = [self getModifiers:[event modifierFlags]]; if(_parent != nullptr) @@ -711,7 +694,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent - (NSRect)firstRectForCharacterRange:(NSRange)range actualRange:(NSRangePointer)actualRange { - CGRect result; + CGRect result = { 0 }; return result; } @@ -730,10 +713,10 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent CreateClipboard([info draggingPasteboard], nil), GetAvnDataObjectHandleFromDraggingInfo(info)); - NSDragOperation ret = 0; + NSDragOperation ret = static_cast(0); // Ensure that the managed part didn't add any new effects - reffects = (int)effects & (int)reffects; + reffects = (int)effects & reffects; // OSX requires exactly one operation if((reffects & (int)AvnDragDropEffects::Copy) != 0) @@ -829,9 +812,6 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent bool _isEnabled; bool _isExtended; AvnMenu* _menu; - double _lastScaling; - IAvnAutomationPeer* _automationPeer; - NSMutableArray* _automationChildren; } -(void) setIsExtended:(bool)value; @@ -844,11 +824,6 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent return _parent->IsDialog(); } --(double) getScaling -{ - return _lastScaling; -} - -(double) getExtendedTitleBarHeight { if(_isExtended) @@ -871,11 +846,6 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent } } -+(void)closeAll -{ - [[NSApplication sharedApplication] terminate:self]; -} - - (void)performClose:(id)sender { if([[self delegate] respondsToSelector:@selector(windowShouldClose:)]) @@ -983,7 +953,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent _closed = false; _isEnabled = true; - _lastScaling = [self backingScaleFactor]; + [self backingScaleFactor]; [self setOpaque:NO]; [self setBackgroundColor: [NSColor clearColor]]; _isExtended = false; @@ -1004,7 +974,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent - (void)windowDidChangeBackingProperties:(NSNotification *)notification { - _lastScaling = [self backingScaleFactor]; + [self backingScaleFactor]; } - (void)windowWillClose:(NSNotification *)notification @@ -1221,9 +1191,9 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent { auto avnPoint = [AvnView toAvnPoint:windowPoint]; auto point = [self translateLocalPoint:avnPoint]; - AvnVector delta; + AvnVector delta = { 0, 0 }; - _parent->BaseEvents->RawMouseEvent(NonClientLeftButtonDown, [event timestamp] * 1000, AvnInputModifiersNone, point, delta); + _parent->BaseEvents->RawMouseEvent(NonClientLeftButtonDown, static_cast([event timestamp] * 1000), AvnInputModifiersNone, point, delta); } } break; From 68af00ef0e7f7eee34ee1f5dfc0e113e04a4128e Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 4 May 2022 18:40:08 +0100 Subject: [PATCH 272/416] remove unused code in other classes. --- native/Avalonia.Native/src/OSX/app.mm | 12 ------------ native/Avalonia.Native/src/OSX/common.h | 3 +-- native/Avalonia.Native/src/OSX/cursor.mm | 1 - native/Avalonia.Native/src/OSX/main.mm | 6 +----- native/Avalonia.Native/src/OSX/menu.h | 1 - native/Avalonia.Native/src/OSX/menu.mm | 5 ++--- native/Avalonia.Native/src/OSX/rendertarget.mm | 4 ---- 7 files changed, 4 insertions(+), 28 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/app.mm b/native/Avalonia.Native/src/OSX/app.mm index 05b129baca..14f1f6888c 100644 --- a/native/Avalonia.Native/src/OSX/app.mm +++ b/native/Avalonia.Native/src/OSX/app.mm @@ -82,18 +82,6 @@ ComPtr _events; _isHandlingSendEvent = oldHandling; } } - -// This is needed for certain embedded controls -- (BOOL) isHandlingSendEvent -{ - return _isHandlingSendEvent; -} - -- (void)setHandlingSendEvent:(BOOL)handlingSendEvent -{ - _isHandlingSendEvent = handlingSendEvent; -} - @end extern void InitializeAvnApp(IAvnApplicationEvents* events) diff --git a/native/Avalonia.Native/src/OSX/common.h b/native/Avalonia.Native/src/OSX/common.h index 9186d9e15a..a90a235b9d 100644 --- a/native/Avalonia.Native/src/OSX/common.h +++ b/native/Avalonia.Native/src/OSX/common.h @@ -27,7 +27,7 @@ extern IAvnMenuItem* CreateAppMenuItem(); extern IAvnMenuItem* CreateAppMenuItemSeparator(); extern IAvnApplicationCommands* CreateApplicationCommands(); extern IAvnNativeControlHost* CreateNativeControlHost(NSView* parent); -extern void SetAppMenu (NSString* appName, IAvnMenu* appMenu); +extern void SetAppMenu(IAvnMenu *menu); extern void SetServicesMenu (IAvnMenu* menu); extern IAvnMenu* GetAppMenu (); extern NSMenuItem* GetAppMenuItem (); @@ -38,7 +38,6 @@ extern NSPoint ToNSPoint (AvnPoint p); extern NSRect ToNSRect (AvnRect r); extern AvnPoint ToAvnPoint (NSPoint p); extern AvnPoint ConvertPointY (AvnPoint p); -extern CGFloat PrimaryDisplayHeight(); extern NSSize ToNSSize (AvnSize s); #ifdef DEBUG #define NSDebugLog(...) NSLog(__VA_ARGS__) diff --git a/native/Avalonia.Native/src/OSX/cursor.mm b/native/Avalonia.Native/src/OSX/cursor.mm index dc38294a18..8638a03531 100644 --- a/native/Avalonia.Native/src/OSX/cursor.mm +++ b/native/Avalonia.Native/src/OSX/cursor.mm @@ -1,6 +1,5 @@ #include "common.h" #include "cursor.h" -#include class CursorFactory : public ComSingleObject { diff --git a/native/Avalonia.Native/src/OSX/main.mm b/native/Avalonia.Native/src/OSX/main.mm index ea79c494d7..011f881e94 100644 --- a/native/Avalonia.Native/src/OSX/main.mm +++ b/native/Avalonia.Native/src/OSX/main.mm @@ -343,7 +343,7 @@ public: @autoreleasepool { - ::SetAppMenu(s_appTitle, appMenu); + ::SetAppMenu(appMenu); return S_OK; } } @@ -428,7 +428,3 @@ AvnPoint ConvertPointY (AvnPoint p) return p; } -CGFloat PrimaryDisplayHeight() -{ - return NSMaxY([[[NSScreen screens] firstObject] frame]); -} diff --git a/native/Avalonia.Native/src/OSX/menu.h b/native/Avalonia.Native/src/OSX/menu.h index 186fcf255b..ce46ac11e0 100644 --- a/native/Avalonia.Native/src/OSX/menu.h +++ b/native/Avalonia.Native/src/OSX/menu.h @@ -31,7 +31,6 @@ private: NSMenuItem* _native; // here we hold a pointer to an AvnMenuItem IAvnActionCallback* _callback; IAvnPredicateCallback* _predicate; - bool _isSeparator; bool _isCheckable; public: diff --git a/native/Avalonia.Native/src/OSX/menu.mm b/native/Avalonia.Native/src/OSX/menu.mm index 726e58478b..fd74edd772 100644 --- a/native/Avalonia.Native/src/OSX/menu.mm +++ b/native/Avalonia.Native/src/OSX/menu.mm @@ -74,8 +74,7 @@ AvnAppMenuItem::AvnAppMenuItem(bool isSeparator) { _isCheckable = false; - _isSeparator = isSeparator; - + if(isSeparator) { _native = [NSMenuItem separatorItem]; @@ -460,7 +459,7 @@ extern IAvnMenuItem* CreateAppMenuItemSeparator() static IAvnMenu* s_appMenu = nullptr; static NSMenuItem* s_appMenuItem = nullptr; -extern void SetAppMenu (NSString* appName, IAvnMenu* menu) +extern void SetAppMenu(IAvnMenu *menu) { s_appMenu = menu; diff --git a/native/Avalonia.Native/src/OSX/rendertarget.mm b/native/Avalonia.Native/src/OSX/rendertarget.mm index dc5c24e41e..266d0345d1 100644 --- a/native/Avalonia.Native/src/OSX/rendertarget.mm +++ b/native/Avalonia.Native/src/OSX/rendertarget.mm @@ -1,14 +1,10 @@ #include "common.h" #include "rendertarget.h" -#import #import #import -#include -#include #include #include -#include @interface IOSurfaceHolder : NSObject @end From 849754e424b6ac10cb60a486c7812e28247574ab Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 5 May 2022 16:51:44 +0200 Subject: [PATCH 273/416] Expose CurrentOpacity on ISkiaDrawingContextImpl. --- src/Skia/Avalonia.Skia/DrawingContextImpl.cs | 1 + src/Skia/Avalonia.Skia/ISkiaDrawingContextImpl.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs index 2548b9f5aa..b45968ec27 100644 --- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs +++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs @@ -126,6 +126,7 @@ namespace Avalonia.Skia SKCanvas ISkiaDrawingContextImpl.SkCanvas => Canvas; SKSurface ISkiaDrawingContextImpl.SkSurface => Surface; GRContext ISkiaDrawingContextImpl.GrContext => _grContext; + double ISkiaDrawingContextImpl.CurrentOpacity => _currentOpacity; /// public void Clear(Color color) diff --git a/src/Skia/Avalonia.Skia/ISkiaDrawingContextImpl.cs b/src/Skia/Avalonia.Skia/ISkiaDrawingContextImpl.cs index 38fa5a5253..87c89beb7e 100644 --- a/src/Skia/Avalonia.Skia/ISkiaDrawingContextImpl.cs +++ b/src/Skia/Avalonia.Skia/ISkiaDrawingContextImpl.cs @@ -8,5 +8,6 @@ namespace Avalonia.Skia SKCanvas SkCanvas { get; } GRContext GrContext { get; } SKSurface SkSurface { get; } + double CurrentOpacity { get; } } } From e0ec6a4ba148c9016d4f438da1de43b0b30604a3 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 5 May 2022 12:16:02 -0400 Subject: [PATCH 274/416] Use top level to inform about pointer input --- src/iOS/Avalonia.iOS/TouchHandler.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/iOS/Avalonia.iOS/TouchHandler.cs b/src/iOS/Avalonia.iOS/TouchHandler.cs index 43b19c85af..959a660d8a 100644 --- a/src/iOS/Avalonia.iOS/TouchHandler.cs +++ b/src/iOS/Avalonia.iOS/TouchHandler.cs @@ -41,7 +41,7 @@ namespace Avalonia.iOS _ => RawPointerEventType.TouchUpdate }, pt, RawInputModifiers.None, id); - _device.ProcessRawEvent(ev); + _tl.Input?.Invoke(ev); if (t.Phase == UITouchPhase.Cancelled || t.Phase == UITouchPhase.Ended) _knownTouches.Remove(t); @@ -49,4 +49,4 @@ namespace Avalonia.iOS } } -} \ No newline at end of file +} From c029ddfb32d253094478ac07c308a54f1afec67b Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 5 May 2022 18:13:39 +0100 Subject: [PATCH 275/416] fix project config. --- .../xcshareddata/xcschemes/Avalonia.Native.OSX.xcscheme | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/xcshareddata/xcschemes/Avalonia.Native.OSX.xcscheme b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/xcshareddata/xcschemes/Avalonia.Native.OSX.xcscheme index 5d20a135b9..87a8312c38 100644 --- a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/xcshareddata/xcschemes/Avalonia.Native.OSX.xcscheme +++ b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/xcshareddata/xcschemes/Avalonia.Native.OSX.xcscheme @@ -56,10 +56,14 @@ + + Date: Thu, 5 May 2022 18:14:06 +0100 Subject: [PATCH 276/416] move avnview to its own file. --- .../project.pbxproj | 8 + native/Avalonia.Native/src/OSX/AvnView.h | 29 + native/Avalonia.Native/src/OSX/AvnView.mm | 706 ++++++++++++++++++ .../Avalonia.Native/src/OSX/INSWindowHolder.h | 2 + native/Avalonia.Native/src/OSX/ResizeScope.h | 2 + native/Avalonia.Native/src/OSX/ResizeScope.mm | 1 + .../Avalonia.Native/src/OSX/WindowBaseImpl.mm | 1 + native/Avalonia.Native/src/OSX/WindowImpl.mm | 1 + native/Avalonia.Native/src/OSX/automation.mm | 1 + native/Avalonia.Native/src/OSX/window.h | 12 - native/Avalonia.Native/src/OSX/window.mm | 699 +---------------- 11 files changed, 752 insertions(+), 710 deletions(-) create mode 100644 native/Avalonia.Native/src/OSX/AvnView.h create mode 100644 native/Avalonia.Native/src/OSX/AvnView.mm diff --git a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj index 2a206b0692..2d9dcbf9c8 100644 --- a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj +++ b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj @@ -15,6 +15,8 @@ 183919D91DB9AAB5D700C2EA /* WindowImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391CD090AA776E7E841AC9 /* WindowImpl.h */; }; 18391C28BF1823B5464FDD36 /* ResizeScope.h in Headers */ = {isa = PBXBuildFile; fileRef = 1839171D898F9BFC1373631A /* ResizeScope.h */; }; 18391CF07316F819E76B617C /* IWindowStateChanged.h in Headers */ = {isa = PBXBuildFile; fileRef = 183913C6BFD6856BD42D19FD /* IWindowStateChanged.h */; }; + 18391D4EB311BC7EF8B8C0A6 /* AvnView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1839132D0E2454D911F1D1F9 /* AvnView.mm */; }; + 18391ED5F611FF62C45F196D /* AvnView.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391D1669284AD2EC9E866A /* AvnView.h */; }; 1A002B9E232135EE00021753 /* app.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A002B9D232135EE00021753 /* app.mm */; }; 1A1852DC23E05814008F0DED /* deadlock.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A1852DB23E05814008F0DED /* deadlock.mm */; }; 1A3E5EA823E9E83B00EDE661 /* rendertarget.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A3E5EA723E9E83B00EDE661 /* rendertarget.mm */; }; @@ -43,6 +45,7 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 1839132D0E2454D911F1D1F9 /* AvnView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AvnView.mm; sourceTree = ""; }; 183913C6BFD6856BD42D19FD /* IWindowStateChanged.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IWindowStateChanged.h; sourceTree = ""; }; 183915BFF0E234CD3604A7CD /* WindowBaseImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowBaseImpl.h; sourceTree = ""; }; 18391676ECF0E983F4964357 /* WindowBaseImpl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WindowBaseImpl.mm; sourceTree = ""; }; @@ -50,6 +53,7 @@ 183919BF108EB72A029F7671 /* WindowImpl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WindowImpl.mm; sourceTree = ""; }; 18391BBB7782C296D424071F /* INSWindowHolder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = INSWindowHolder.h; sourceTree = ""; }; 18391CD090AA776E7E841AC9 /* WindowImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowImpl.h; sourceTree = ""; }; + 18391D1669284AD2EC9E866A /* AvnView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AvnView.h; sourceTree = ""; }; 18391E45702740FE9DD69695 /* ResizeScope.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ResizeScope.mm; sourceTree = ""; }; 1A002B9D232135EE00021753 /* app.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = app.mm; sourceTree = ""; }; 1A1852DB23E05814008F0DED /* deadlock.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = deadlock.mm; sourceTree = ""; }; @@ -154,6 +158,8 @@ 183913C6BFD6856BD42D19FD /* IWindowStateChanged.h */, 18391E45702740FE9DD69695 /* ResizeScope.mm */, 1839171D898F9BFC1373631A /* ResizeScope.h */, + 1839132D0E2454D911F1D1F9 /* AvnView.mm */, + 18391D1669284AD2EC9E866A /* AvnView.h */, ); sourceTree = ""; }; @@ -179,6 +185,7 @@ 183919D91DB9AAB5D700C2EA /* WindowImpl.h in Headers */, 18391CF07316F819E76B617C /* IWindowStateChanged.h in Headers */, 18391C28BF1823B5464FDD36 /* ResizeScope.h in Headers */, + 18391ED5F611FF62C45F196D /* AvnView.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -261,6 +268,7 @@ 1839179A55FC1421BEE83330 /* WindowBaseImpl.mm in Sources */, 1839125F057B0A4EB1760058 /* WindowImpl.mm in Sources */, 18391068E48EF96E3DB5FDAB /* ResizeScope.mm in Sources */, + 18391D4EB311BC7EF8B8C0A6 /* AvnView.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/native/Avalonia.Native/src/OSX/AvnView.h b/native/Avalonia.Native/src/OSX/AvnView.h new file mode 100644 index 0000000000..c6dd90150f --- /dev/null +++ b/native/Avalonia.Native/src/OSX/AvnView.h @@ -0,0 +1,29 @@ +// +// Created by Dan Walmsley on 05/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#import + + +#import +#import +#include "window.h" +#import "comimpl.h" +#import "common.h" +#import "WindowImpl.h" +#import "KeyTransform.h" + +@class AvnAccessibilityElement; + +@interface AvnView : NSView +-(AvnView* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent; +-(NSEvent* _Nonnull) lastMouseDownEvent; +-(AvnPoint) translateLocalPoint:(AvnPoint)pt; +-(void) setSwRenderedFrame: (AvnFramebuffer* _Nonnull) fb dispose: (IUnknown* _Nonnull) dispose; +-(void) onClosed; + +-(AvnPlatformResizeReason) getResizeReason; +-(void) setResizeReason:(AvnPlatformResizeReason)reason; ++ (AvnPoint)toAvnPoint:(CGPoint)p; +@end \ No newline at end of file diff --git a/native/Avalonia.Native/src/OSX/AvnView.mm b/native/Avalonia.Native/src/OSX/AvnView.mm new file mode 100644 index 0000000000..24cbc25502 --- /dev/null +++ b/native/Avalonia.Native/src/OSX/AvnView.mm @@ -0,0 +1,706 @@ +// +// Created by Dan Walmsley on 05/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#import +#import "AvnView.h" +#include "automation.h" + +@implementation AvnView +{ + ComPtr _parent; + NSTrackingArea* _area; + bool _isLeftPressed, _isMiddlePressed, _isRightPressed, _isXButton1Pressed, _isXButton2Pressed; + AvnInputModifiers _modifierState; + NSEvent* _lastMouseDownEvent; + bool _lastKeyHandled; + AvnPixelSize _lastPixelSize; + NSObject* _renderTarget; + AvnPlatformResizeReason _resizeReason; + AvnAccessibilityElement* _accessibilityChild; +} + +- (void)onClosed +{ + @synchronized (self) + { + _parent = nullptr; + } +} + +- (NSEvent*) lastMouseDownEvent +{ + return _lastMouseDownEvent; +} + +- (void) updateRenderTarget +{ + [_renderTarget resize:_lastPixelSize withScale:static_cast([[self window] backingScaleFactor])]; + [self setNeedsDisplayInRect:[self frame]]; +} + +-(AvnView*) initWithParent: (WindowBaseImpl*) parent +{ + self = [super init]; + _renderTarget = parent->renderTarget; + [self setWantsLayer:YES]; + [self setLayerContentsRedrawPolicy: NSViewLayerContentsRedrawDuringViewResize]; + + _parent = parent; + _area = nullptr; + _lastPixelSize.Height = 100; + _lastPixelSize.Width = 100; + [self registerForDraggedTypes: @[@"public.data", GetAvnCustomDataType()]]; + + _modifierState = AvnInputModifiersNone; + return self; +} + +- (BOOL)isFlipped +{ + return YES; +} + +- (BOOL)wantsUpdateLayer +{ + return YES; +} + +- (void)setLayer:(CALayer *)layer +{ + [_renderTarget setNewLayer: layer]; + [super setLayer: layer]; +} + +- (BOOL)isOpaque +{ + return YES; +} + +- (BOOL)acceptsFirstResponder +{ + return true; +} + +- (BOOL)acceptsFirstMouse:(NSEvent *)event +{ + return true; +} + +- (BOOL)canBecomeKeyView +{ + return true; +} + +-(void)setFrameSize:(NSSize)newSize +{ + [super setFrameSize:newSize]; + + if(_area != nullptr) + { + [self removeTrackingArea:_area]; + _area = nullptr; + } + + if (_parent == nullptr) + { + return; + } + + NSRect rect = NSZeroRect; + rect.size = newSize; + + NSTrackingAreaOptions options = NSTrackingActiveAlways | NSTrackingMouseMoved | NSTrackingMouseEnteredAndExited | NSTrackingEnabledDuringMouseDrag; + _area = [[NSTrackingArea alloc] initWithRect:rect options:options owner:self userInfo:nullptr]; + [self addTrackingArea:_area]; + + _parent->UpdateCursor(); + + auto fsize = [self convertSizeToBacking: [self frame].size]; + + if(_lastPixelSize.Width != (int)fsize.width || _lastPixelSize.Height != (int)fsize.height) + { + _lastPixelSize.Width = (int)fsize.width; + _lastPixelSize.Height = (int)fsize.height; + [self updateRenderTarget]; + + auto reason = [self inLiveResize] ? ResizeUser : _resizeReason; + _parent->BaseEvents->Resized(AvnSize{newSize.width, newSize.height}, reason); + } +} + +- (void)updateLayer +{ + AvnInsidePotentialDeadlock deadlock; + if (_parent == nullptr) + { + return; + } + + _parent->BaseEvents->RunRenderPriorityJobs(); + + if (_parent == nullptr) + { + return; + } + + _parent->BaseEvents->Paint(); +} + +- (void)drawRect:(NSRect)dirtyRect +{ + return; +} + +-(void) setSwRenderedFrame: (AvnFramebuffer*) fb dispose: (IUnknown*) dispose +{ + @autoreleasepool { + [_renderTarget setSwFrame:fb]; + dispose->Release(); + } +} + +- (AvnPoint) translateLocalPoint:(AvnPoint)pt +{ + pt.Y = [self bounds].size.height - pt.Y; + return pt; +} + ++ (AvnPoint)toAvnPoint:(CGPoint)p +{ + AvnPoint result; + + result.X = p.x; + result.Y = p.y; + + return result; +} + +- (void) viewDidChangeBackingProperties +{ + auto fsize = [self convertSizeToBacking: [self frame].size]; + _lastPixelSize.Width = (int)fsize.width; + _lastPixelSize.Height = (int)fsize.height; + [self updateRenderTarget]; + + if(_parent != nullptr) + { + _parent->BaseEvents->ScalingChanged([_parent->Window backingScaleFactor]); + } + + [super viewDidChangeBackingProperties]; +} + +- (bool) ignoreUserInput:(bool)trigerInputWhenDisabled +{ + auto parentWindow = objc_cast([self window]); + + if(parentWindow == nil || ![parentWindow shouldTryToHandleEvents]) + { + if(trigerInputWhenDisabled) + { + auto window = dynamic_cast(_parent.getRaw()); + + if(window != nullptr) + { + window->WindowEvents->GotInputWhenDisabled(); + } + } + + return TRUE; + } + + return FALSE; +} + +- (void)mouseEvent:(NSEvent *)event withType:(AvnRawMouseEventType) type +{ + bool triggerInputWhenDisabled = type != Move; + + if([self ignoreUserInput: triggerInputWhenDisabled]) + { + return; + } + + auto localPoint = [self convertPoint:[event locationInWindow] toView:self]; + auto avnPoint = [AvnView toAvnPoint:localPoint]; + auto point = [self translateLocalPoint:avnPoint]; + AvnVector delta = { 0, 0}; + + if(type == Wheel) + { + auto speed = 5; + + if([event hasPreciseScrollingDeltas]) + { + speed = 50; + } + + delta.X = [event scrollingDeltaX] / speed; + delta.Y = [event scrollingDeltaY] / speed; + + if(delta.X == 0 && delta.Y == 0) + { + return; + } + } + else if (type == Magnify) + { + delta.X = delta.Y = [event magnification]; + } + else if (type == Rotate) + { + delta.X = delta.Y = [event rotation]; + } + else if (type == Swipe) + { + delta.X = [event deltaX]; + delta.Y = [event deltaY]; + } + + uint32 timestamp = static_cast([event timestamp] * 1000); + auto modifiers = [self getModifiers:[event modifierFlags]]; + + if(type != Move || + ( + [self window] != nil && + ( + [[self window] firstResponder] == nil + || ![[[self window] firstResponder] isKindOfClass: [NSView class]] + ) + ) + ) + [self becomeFirstResponder]; + + if(_parent != nullptr) + { + _parent->BaseEvents->RawMouseEvent(type, timestamp, modifiers, point, delta); + } + + [super mouseMoved:event]; +} + +- (BOOL) resignFirstResponder +{ + _parent->BaseEvents->LostFocus(); + return YES; +} + +- (void)mouseMoved:(NSEvent *)event +{ + [self mouseEvent:event withType:Move]; +} + +- (void)mouseDown:(NSEvent *)event +{ + _isLeftPressed = true; + _lastMouseDownEvent = event; + [self mouseEvent:event withType:LeftButtonDown]; +} + +- (void)otherMouseDown:(NSEvent *)event +{ + _lastMouseDownEvent = event; + + switch(event.buttonNumber) + { + case 2: + case 3: + _isMiddlePressed = true; + [self mouseEvent:event withType:MiddleButtonDown]; + break; + case 4: + _isXButton1Pressed = true; + [self mouseEvent:event withType:XButton1Down]; + break; + case 5: + _isXButton2Pressed = true; + [self mouseEvent:event withType:XButton2Down]; + break; + + default: + break; + } +} + +- (void)rightMouseDown:(NSEvent *)event +{ + _isRightPressed = true; + _lastMouseDownEvent = event; + [self mouseEvent:event withType:RightButtonDown]; +} + +- (void)mouseUp:(NSEvent *)event +{ + _isLeftPressed = false; + [self mouseEvent:event withType:LeftButtonUp]; +} + +- (void)otherMouseUp:(NSEvent *)event +{ + switch(event.buttonNumber) + { + case 2: + case 3: + _isMiddlePressed = false; + [self mouseEvent:event withType:MiddleButtonUp]; + break; + case 4: + _isXButton1Pressed = false; + [self mouseEvent:event withType:XButton1Up]; + break; + case 5: + _isXButton2Pressed = false; + [self mouseEvent:event withType:XButton2Up]; + break; + + default: + break; + } +} + +- (void)rightMouseUp:(NSEvent *)event +{ + _isRightPressed = false; + [self mouseEvent:event withType:RightButtonUp]; +} + +- (void)mouseDragged:(NSEvent *)event +{ + [self mouseEvent:event withType:Move]; + [super mouseDragged:event]; +} + +- (void)otherMouseDragged:(NSEvent *)event +{ + [self mouseEvent:event withType:Move]; + [super otherMouseDragged:event]; +} + +- (void)rightMouseDragged:(NSEvent *)event +{ + [self mouseEvent:event withType:Move]; + [super rightMouseDragged:event]; +} + +- (void)scrollWheel:(NSEvent *)event +{ + [self mouseEvent:event withType:Wheel]; + [super scrollWheel:event]; +} + +- (void)magnifyWithEvent:(NSEvent *)event +{ + [self mouseEvent:event withType:Magnify]; + [super magnifyWithEvent:event]; +} + +- (void)rotateWithEvent:(NSEvent *)event +{ + [self mouseEvent:event withType:Rotate]; + [super rotateWithEvent:event]; +} + +- (void)swipeWithEvent:(NSEvent *)event +{ + [self mouseEvent:event withType:Swipe]; + [super swipeWithEvent:event]; +} + +- (void)mouseEntered:(NSEvent *)event +{ + [super mouseEntered:event]; +} + +- (void)mouseExited:(NSEvent *)event +{ + [self mouseEvent:event withType:LeaveWindow]; + [super mouseExited:event]; +} + +- (void) keyboardEvent: (NSEvent *) event withType: (AvnRawKeyEventType)type +{ + if([self ignoreUserInput: false]) + { + return; + } + + auto key = s_KeyMap[[event keyCode]]; + + uint32_t timestamp = static_cast([event timestamp] * 1000); + auto modifiers = [self getModifiers:[event modifierFlags]]; + + if(_parent != nullptr) + { + _lastKeyHandled = _parent->BaseEvents->RawKeyEvent(type, timestamp, modifiers, key); + } +} + +- (BOOL)performKeyEquivalent:(NSEvent *)event +{ + bool result = _lastKeyHandled; + + _lastKeyHandled = false; + + return result; +} + +- (void)flagsChanged:(NSEvent *)event +{ + auto newModifierState = [self getModifiers:[event modifierFlags]]; + + bool isAltCurrentlyPressed = (_modifierState & Alt) == Alt; + bool isControlCurrentlyPressed = (_modifierState & Control) == Control; + bool isShiftCurrentlyPressed = (_modifierState & Shift) == Shift; + bool isCommandCurrentlyPressed = (_modifierState & Windows) == Windows; + + bool isAltPressed = (newModifierState & Alt) == Alt; + bool isControlPressed = (newModifierState & Control) == Control; + bool isShiftPressed = (newModifierState & Shift) == Shift; + bool isCommandPressed = (newModifierState & Windows) == Windows; + + + if (isAltPressed && !isAltCurrentlyPressed) + { + [self keyboardEvent:event withType:KeyDown]; + } + else if (isAltCurrentlyPressed && !isAltPressed) + { + [self keyboardEvent:event withType:KeyUp]; + } + + if (isControlPressed && !isControlCurrentlyPressed) + { + [self keyboardEvent:event withType:KeyDown]; + } + else if (isControlCurrentlyPressed && !isControlPressed) + { + [self keyboardEvent:event withType:KeyUp]; + } + + if (isShiftPressed && !isShiftCurrentlyPressed) + { + [self keyboardEvent:event withType:KeyDown]; + } + else if(isShiftCurrentlyPressed && !isShiftPressed) + { + [self keyboardEvent:event withType:KeyUp]; + } + + if(isCommandPressed && !isCommandCurrentlyPressed) + { + [self keyboardEvent:event withType:KeyDown]; + } + else if(isCommandCurrentlyPressed && ! isCommandPressed) + { + [self keyboardEvent:event withType:KeyUp]; + } + + _modifierState = newModifierState; + + [[self inputContext] handleEvent:event]; + [super flagsChanged:event]; +} + +- (void)keyDown:(NSEvent *)event +{ + [self keyboardEvent:event withType:KeyDown]; + [[self inputContext] handleEvent:event]; + [super keyDown:event]; +} + +- (void)keyUp:(NSEvent *)event +{ + [self keyboardEvent:event withType:KeyUp]; + [super keyUp:event]; +} + +- (AvnInputModifiers)getModifiers:(NSEventModifierFlags)mod +{ + unsigned int rv = 0; + + if (mod & NSEventModifierFlagControl) + rv |= Control; + if (mod & NSEventModifierFlagShift) + rv |= Shift; + if (mod & NSEventModifierFlagOption) + rv |= Alt; + if (mod & NSEventModifierFlagCommand) + rv |= Windows; + + if (_isLeftPressed) + rv |= LeftMouseButton; + if (_isMiddlePressed) + rv |= MiddleMouseButton; + if (_isRightPressed) + rv |= RightMouseButton; + if (_isXButton1Pressed) + rv |= XButton1MouseButton; + if (_isXButton2Pressed) + rv |= XButton2MouseButton; + + return (AvnInputModifiers)rv; +} + +- (BOOL)hasMarkedText +{ + return _lastKeyHandled; +} + +- (NSRange)markedRange +{ + return NSMakeRange(NSNotFound, 0); +} + +- (NSRange)selectedRange +{ + return NSMakeRange(NSNotFound, 0); +} + +- (void)setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange +{ + +} + +- (void)unmarkText +{ + +} + +- (NSArray *)validAttributesForMarkedText +{ + return [NSArray new]; +} + +- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)range actualRange:(NSRangePointer)actualRange +{ + return [NSAttributedString new]; +} + +- (void)insertText:(id)string replacementRange:(NSRange)replacementRange +{ + if(!_lastKeyHandled) + { + if(_parent != nullptr) + { + _lastKeyHandled = _parent->BaseEvents->RawTextInputEvent(0, [string UTF8String]); + } + } +} + +- (NSUInteger)characterIndexForPoint:(NSPoint)point +{ + return 0; +} + +- (NSRect)firstRectForCharacterRange:(NSRange)range actualRange:(NSRangePointer)actualRange +{ + CGRect result = { 0 }; + + return result; +} + +- (NSDragOperation)triggerAvnDragEvent: (AvnDragEventType) type info: (id )info +{ + auto localPoint = [self convertPoint:[info draggingLocation] toView:self]; + auto avnPoint = [AvnView toAvnPoint:localPoint]; + auto point = [self translateLocalPoint:avnPoint]; + auto modifiers = [self getModifiers:[[NSApp currentEvent] modifierFlags]]; + NSDragOperation nsop = [info draggingSourceOperationMask]; + + auto effects = ConvertDragDropEffects(nsop); + int reffects = (int)_parent->BaseEvents + ->DragEvent(type, point, modifiers, effects, + CreateClipboard([info draggingPasteboard], nil), + GetAvnDataObjectHandleFromDraggingInfo(info)); + + NSDragOperation ret = static_cast(0); + + // Ensure that the managed part didn't add any new effects + reffects = (int)effects & reffects; + + // OSX requires exactly one operation + if((reffects & (int)AvnDragDropEffects::Copy) != 0) + ret = NSDragOperationCopy; + else if((reffects & (int)AvnDragDropEffects::Move) != 0) + ret = NSDragOperationMove; + else if((reffects & (int)AvnDragDropEffects::Link) != 0) + ret = NSDragOperationLink; + if(ret == 0) + ret = NSDragOperationNone; + return ret; +} + +- (NSDragOperation)draggingEntered:(id )sender +{ + return [self triggerAvnDragEvent: AvnDragEventType::Enter info:sender]; +} + +- (NSDragOperation)draggingUpdated:(id )sender +{ + return [self triggerAvnDragEvent: AvnDragEventType::Over info:sender]; +} + +- (void)draggingExited:(id )sender +{ + [self triggerAvnDragEvent: AvnDragEventType::Leave info:sender]; +} + +- (BOOL)prepareForDragOperation:(id )sender +{ + return [self triggerAvnDragEvent: AvnDragEventType::Over info:sender] != NSDragOperationNone; +} + +- (BOOL)performDragOperation:(id )sender +{ + return [self triggerAvnDragEvent: AvnDragEventType::Drop info:sender] != NSDragOperationNone; +} + +- (void)concludeDragOperation:(nullable id )sender +{ + +} + +- (AvnPlatformResizeReason)getResizeReason +{ + return _resizeReason; +} + +- (void)setResizeReason:(AvnPlatformResizeReason)reason +{ + _resizeReason = reason; +} + +- (AvnAccessibilityElement *) accessibilityChild +{ + if (_accessibilityChild == nil) + { + auto peer = _parent->BaseEvents->GetAutomationPeer(); + + if (peer == nil) + return nil; + + _accessibilityChild = [AvnAccessibilityElement acquire:peer]; + } + + return _accessibilityChild; +} + +- (NSArray *)accessibilityChildren +{ + auto child = [self accessibilityChild]; + return NSAccessibilityUnignoredChildrenForOnlyChild(child); +} + +- (id)accessibilityHitTest:(NSPoint)point +{ + return [[self accessibilityChild] accessibilityHitTest:point]; +} + +- (id)accessibilityFocusedUIElement +{ + return [[self accessibilityChild] accessibilityFocusedUIElement]; +} + +@end \ No newline at end of file diff --git a/native/Avalonia.Native/src/OSX/INSWindowHolder.h b/native/Avalonia.Native/src/OSX/INSWindowHolder.h index aa8c34ef00..adc0dd1990 100644 --- a/native/Avalonia.Native/src/OSX/INSWindowHolder.h +++ b/native/Avalonia.Native/src/OSX/INSWindowHolder.h @@ -6,6 +6,8 @@ #ifndef AVALONIA_NATIVE_OSX_INSWINDOWHOLDER_H #define AVALONIA_NATIVE_OSX_INSWINDOWHOLDER_H +@class AvnView; + struct INSWindowHolder { virtual AvnWindow* _Nonnull GetNSWindow () = 0; diff --git a/native/Avalonia.Native/src/OSX/ResizeScope.h b/native/Avalonia.Native/src/OSX/ResizeScope.h index c57dc96690..7509f93c01 100644 --- a/native/Avalonia.Native/src/OSX/ResizeScope.h +++ b/native/Avalonia.Native/src/OSX/ResizeScope.h @@ -9,6 +9,8 @@ #include "window.h" #include "avalonia-native.h" +@class AvnView; + class ResizeScope { public: diff --git a/native/Avalonia.Native/src/OSX/ResizeScope.mm b/native/Avalonia.Native/src/OSX/ResizeScope.mm index 8644b41fba..90e7f5cf15 100644 --- a/native/Avalonia.Native/src/OSX/ResizeScope.mm +++ b/native/Avalonia.Native/src/OSX/ResizeScope.mm @@ -5,6 +5,7 @@ #import #include "ResizeScope.h" +#import "AvnView.h" ResizeScope::ResizeScope(AvnView *view, AvnPlatformResizeReason reason) { _view = view; diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index 9959d6a034..8f409fa84b 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -6,6 +6,7 @@ #import #include "common.h" #import "window.h" +#import "AvnView.h" #include "menu.h" #include "rendertarget.h" #include "automation.h" diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index 9cf5160c97..ef8be4be9c 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -5,6 +5,7 @@ #import #import "window.h" +#import "AvnView.h" #include "automation.h" #include "menu.h" #import "WindowImpl.h" diff --git a/native/Avalonia.Native/src/OSX/automation.mm b/native/Avalonia.Native/src/OSX/automation.mm index 087d15a248..7a0b3b8127 100644 --- a/native/Avalonia.Native/src/OSX/automation.mm +++ b/native/Avalonia.Native/src/OSX/automation.mm @@ -3,6 +3,7 @@ #import "window.h" #include "AvnString.h" #import "INSWindowHolder.h" +#import "AvnView.h" @interface AvnAccessibilityElement (Events) - (void) raiseChildrenChanged; diff --git a/native/Avalonia.Native/src/OSX/window.h b/native/Avalonia.Native/src/OSX/window.h index 271dd2534e..8caceb5bc1 100644 --- a/native/Avalonia.Native/src/OSX/window.h +++ b/native/Avalonia.Native/src/OSX/window.h @@ -7,18 +7,6 @@ class WindowBaseImpl; -@interface AvnView : NSView --(AvnView* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent; --(NSEvent* _Nonnull) lastMouseDownEvent; --(AvnPoint) translateLocalPoint:(AvnPoint)pt; --(void) setSwRenderedFrame: (AvnFramebuffer* _Nonnull) fb dispose: (IUnknown* _Nonnull) dispose; --(void) onClosed; - --(AvnPlatformResizeReason) getResizeReason; --(void) setResizeReason:(AvnPlatformResizeReason)reason; -+ (AvnPoint)toAvnPoint:(CGPoint)p; -@end - @interface AutoFitContentView : NSView -(AutoFitContentView* _Nonnull) initWithContent: (NSView* _Nonnull) content; -(void) ShowTitleBar: (bool) show; diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index fe80142f1a..a66ff94c8a 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -7,6 +7,7 @@ #include "automation.h" #import "WindowBaseImpl.h" #include "WindowImpl.h" +#include "AvnView.h" @implementation AutoFitContentView { @@ -105,704 +106,6 @@ } @end -@implementation AvnView -{ - ComPtr _parent; - NSTrackingArea* _area; - bool _isLeftPressed, _isMiddlePressed, _isRightPressed, _isXButton1Pressed, _isXButton2Pressed; - AvnInputModifiers _modifierState; - NSEvent* _lastMouseDownEvent; - bool _lastKeyHandled; - AvnPixelSize _lastPixelSize; - NSObject* _renderTarget; - AvnPlatformResizeReason _resizeReason; - AvnAccessibilityElement* _accessibilityChild; -} - -- (void)onClosed -{ - @synchronized (self) - { - _parent = nullptr; - } -} - -- (NSEvent*) lastMouseDownEvent -{ - return _lastMouseDownEvent; -} - -- (void) updateRenderTarget -{ - [_renderTarget resize:_lastPixelSize withScale:static_cast([[self window] backingScaleFactor])]; - [self setNeedsDisplayInRect:[self frame]]; -} - --(AvnView*) initWithParent: (WindowBaseImpl*) parent -{ - self = [super init]; - _renderTarget = parent->renderTarget; - [self setWantsLayer:YES]; - [self setLayerContentsRedrawPolicy: NSViewLayerContentsRedrawDuringViewResize]; - - _parent = parent; - _area = nullptr; - _lastPixelSize.Height = 100; - _lastPixelSize.Width = 100; - [self registerForDraggedTypes: @[@"public.data", GetAvnCustomDataType()]]; - - _modifierState = AvnInputModifiersNone; - return self; -} - -- (BOOL)isFlipped -{ - return YES; -} - -- (BOOL)wantsUpdateLayer -{ - return YES; -} - -- (void)setLayer:(CALayer *)layer -{ - [_renderTarget setNewLayer: layer]; - [super setLayer: layer]; -} - -- (BOOL)isOpaque -{ - return YES; -} - -- (BOOL)acceptsFirstResponder -{ - return true; -} - -- (BOOL)acceptsFirstMouse:(NSEvent *)event -{ - return true; -} - -- (BOOL)canBecomeKeyView -{ - return true; -} - --(void)setFrameSize:(NSSize)newSize -{ - [super setFrameSize:newSize]; - - if(_area != nullptr) - { - [self removeTrackingArea:_area]; - _area = nullptr; - } - - if (_parent == nullptr) - { - return; - } - - NSRect rect = NSZeroRect; - rect.size = newSize; - - NSTrackingAreaOptions options = NSTrackingActiveAlways | NSTrackingMouseMoved | NSTrackingMouseEnteredAndExited | NSTrackingEnabledDuringMouseDrag; - _area = [[NSTrackingArea alloc] initWithRect:rect options:options owner:self userInfo:nullptr]; - [self addTrackingArea:_area]; - - _parent->UpdateCursor(); - - auto fsize = [self convertSizeToBacking: [self frame].size]; - - if(_lastPixelSize.Width != (int)fsize.width || _lastPixelSize.Height != (int)fsize.height) - { - _lastPixelSize.Width = (int)fsize.width; - _lastPixelSize.Height = (int)fsize.height; - [self updateRenderTarget]; - - auto reason = [self inLiveResize] ? ResizeUser : _resizeReason; - _parent->BaseEvents->Resized(AvnSize{newSize.width, newSize.height}, reason); - } -} - -- (void)updateLayer -{ - AvnInsidePotentialDeadlock deadlock; - if (_parent == nullptr) - { - return; - } - - _parent->BaseEvents->RunRenderPriorityJobs(); - - if (_parent == nullptr) - { - return; - } - - _parent->BaseEvents->Paint(); -} - -- (void)drawRect:(NSRect)dirtyRect -{ - return; -} - --(void) setSwRenderedFrame: (AvnFramebuffer*) fb dispose: (IUnknown*) dispose -{ - @autoreleasepool { - [_renderTarget setSwFrame:fb]; - dispose->Release(); - } -} - -- (AvnPoint) translateLocalPoint:(AvnPoint)pt -{ - pt.Y = [self bounds].size.height - pt.Y; - return pt; -} - -+ (AvnPoint)toAvnPoint:(CGPoint)p -{ - AvnPoint result; - - result.X = p.x; - result.Y = p.y; - - return result; -} - -- (void) viewDidChangeBackingProperties -{ - auto fsize = [self convertSizeToBacking: [self frame].size]; - _lastPixelSize.Width = (int)fsize.width; - _lastPixelSize.Height = (int)fsize.height; - [self updateRenderTarget]; - - if(_parent != nullptr) - { - _parent->BaseEvents->ScalingChanged([_parent->Window backingScaleFactor]); - } - - [super viewDidChangeBackingProperties]; -} - -- (bool) ignoreUserInput:(bool)trigerInputWhenDisabled -{ - auto parentWindow = objc_cast([self window]); - - if(parentWindow == nil || ![parentWindow shouldTryToHandleEvents]) - { - if(trigerInputWhenDisabled) - { - auto window = dynamic_cast(_parent.getRaw()); - - if(window != nullptr) - { - window->WindowEvents->GotInputWhenDisabled(); - } - } - - return TRUE; - } - - return FALSE; -} - -- (void)mouseEvent:(NSEvent *)event withType:(AvnRawMouseEventType) type -{ - bool triggerInputWhenDisabled = type != Move; - - if([self ignoreUserInput: triggerInputWhenDisabled]) - { - return; - } - - auto localPoint = [self convertPoint:[event locationInWindow] toView:self]; - auto avnPoint = [AvnView toAvnPoint:localPoint]; - auto point = [self translateLocalPoint:avnPoint]; - AvnVector delta = { 0, 0}; - - if(type == Wheel) - { - auto speed = 5; - - if([event hasPreciseScrollingDeltas]) - { - speed = 50; - } - - delta.X = [event scrollingDeltaX] / speed; - delta.Y = [event scrollingDeltaY] / speed; - - if(delta.X == 0 && delta.Y == 0) - { - return; - } - } - else if (type == Magnify) - { - delta.X = delta.Y = [event magnification]; - } - else if (type == Rotate) - { - delta.X = delta.Y = [event rotation]; - } - else if (type == Swipe) - { - delta.X = [event deltaX]; - delta.Y = [event deltaY]; - } - - uint32 timestamp = static_cast([event timestamp] * 1000); - auto modifiers = [self getModifiers:[event modifierFlags]]; - - if(type != AvnRawMouseEventType::Move || - ( - [self window] != nil && - ( - [[self window] firstResponder] == nil - || ![[[self window] firstResponder] isKindOfClass: [NSView class]] - ) - ) - ) - [self becomeFirstResponder]; - - if(_parent != nullptr) - { - _parent->BaseEvents->RawMouseEvent(type, timestamp, modifiers, point, delta); - } - - [super mouseMoved:event]; -} - -- (BOOL) resignFirstResponder -{ - _parent->BaseEvents->LostFocus(); - return YES; -} - -- (void)mouseMoved:(NSEvent *)event -{ - [self mouseEvent:event withType:Move]; -} - -- (void)mouseDown:(NSEvent *)event -{ - _isLeftPressed = true; - _lastMouseDownEvent = event; - [self mouseEvent:event withType:LeftButtonDown]; -} - -- (void)otherMouseDown:(NSEvent *)event -{ - _lastMouseDownEvent = event; - - switch(event.buttonNumber) - { - case 2: - case 3: - _isMiddlePressed = true; - [self mouseEvent:event withType:MiddleButtonDown]; - break; - case 4: - _isXButton1Pressed = true; - [self mouseEvent:event withType:XButton1Down]; - break; - case 5: - _isXButton2Pressed = true; - [self mouseEvent:event withType:XButton2Down]; - break; - - default: - break; - } -} - -- (void)rightMouseDown:(NSEvent *)event -{ - _isRightPressed = true; - _lastMouseDownEvent = event; - [self mouseEvent:event withType:RightButtonDown]; -} - -- (void)mouseUp:(NSEvent *)event -{ - _isLeftPressed = false; - [self mouseEvent:event withType:LeftButtonUp]; -} - -- (void)otherMouseUp:(NSEvent *)event -{ - switch(event.buttonNumber) - { - case 2: - case 3: - _isMiddlePressed = false; - [self mouseEvent:event withType:MiddleButtonUp]; - break; - case 4: - _isXButton1Pressed = false; - [self mouseEvent:event withType:XButton1Up]; - break; - case 5: - _isXButton2Pressed = false; - [self mouseEvent:event withType:XButton2Up]; - break; - - default: - break; - } -} - -- (void)rightMouseUp:(NSEvent *)event -{ - _isRightPressed = false; - [self mouseEvent:event withType:RightButtonUp]; -} - -- (void)mouseDragged:(NSEvent *)event -{ - [self mouseEvent:event withType:Move]; - [super mouseDragged:event]; -} - -- (void)otherMouseDragged:(NSEvent *)event -{ - [self mouseEvent:event withType:Move]; - [super otherMouseDragged:event]; -} - -- (void)rightMouseDragged:(NSEvent *)event -{ - [self mouseEvent:event withType:Move]; - [super rightMouseDragged:event]; -} - -- (void)scrollWheel:(NSEvent *)event -{ - [self mouseEvent:event withType:Wheel]; - [super scrollWheel:event]; -} - -- (void)magnifyWithEvent:(NSEvent *)event -{ - [self mouseEvent:event withType:Magnify]; - [super magnifyWithEvent:event]; -} - -- (void)rotateWithEvent:(NSEvent *)event -{ - [self mouseEvent:event withType:Rotate]; - [super rotateWithEvent:event]; -} - -- (void)swipeWithEvent:(NSEvent *)event -{ - [self mouseEvent:event withType:Swipe]; - [super swipeWithEvent:event]; -} - -- (void)mouseEntered:(NSEvent *)event -{ - [super mouseEntered:event]; -} - -- (void)mouseExited:(NSEvent *)event -{ - [self mouseEvent:event withType:LeaveWindow]; - [super mouseExited:event]; -} - -- (void) keyboardEvent: (NSEvent *) event withType: (AvnRawKeyEventType)type -{ - if([self ignoreUserInput: false]) - { - return; - } - - auto key = s_KeyMap[[event keyCode]]; - - uint32_t timestamp = static_cast([event timestamp] * 1000); - auto modifiers = [self getModifiers:[event modifierFlags]]; - - if(_parent != nullptr) - { - _lastKeyHandled = _parent->BaseEvents->RawKeyEvent(type, timestamp, modifiers, key); - } -} - -- (BOOL)performKeyEquivalent:(NSEvent *)event -{ - bool result = _lastKeyHandled; - - _lastKeyHandled = false; - - return result; -} - -- (void)flagsChanged:(NSEvent *)event -{ - auto newModifierState = [self getModifiers:[event modifierFlags]]; - - bool isAltCurrentlyPressed = (_modifierState & Alt) == Alt; - bool isControlCurrentlyPressed = (_modifierState & Control) == Control; - bool isShiftCurrentlyPressed = (_modifierState & Shift) == Shift; - bool isCommandCurrentlyPressed = (_modifierState & Windows) == Windows; - - bool isAltPressed = (newModifierState & Alt) == Alt; - bool isControlPressed = (newModifierState & Control) == Control; - bool isShiftPressed = (newModifierState & Shift) == Shift; - bool isCommandPressed = (newModifierState & Windows) == Windows; - - - if (isAltPressed && !isAltCurrentlyPressed) - { - [self keyboardEvent:event withType:KeyDown]; - } - else if (isAltCurrentlyPressed && !isAltPressed) - { - [self keyboardEvent:event withType:KeyUp]; - } - - if (isControlPressed && !isControlCurrentlyPressed) - { - [self keyboardEvent:event withType:KeyDown]; - } - else if (isControlCurrentlyPressed && !isControlPressed) - { - [self keyboardEvent:event withType:KeyUp]; - } - - if (isShiftPressed && !isShiftCurrentlyPressed) - { - [self keyboardEvent:event withType:KeyDown]; - } - else if(isShiftCurrentlyPressed && !isShiftPressed) - { - [self keyboardEvent:event withType:KeyUp]; - } - - if(isCommandPressed && !isCommandCurrentlyPressed) - { - [self keyboardEvent:event withType:KeyDown]; - } - else if(isCommandCurrentlyPressed && ! isCommandPressed) - { - [self keyboardEvent:event withType:KeyUp]; - } - - _modifierState = newModifierState; - - [[self inputContext] handleEvent:event]; - [super flagsChanged:event]; -} - -- (void)keyDown:(NSEvent *)event -{ - [self keyboardEvent:event withType:KeyDown]; - [[self inputContext] handleEvent:event]; - [super keyDown:event]; -} - -- (void)keyUp:(NSEvent *)event -{ - [self keyboardEvent:event withType:KeyUp]; - [super keyUp:event]; -} - -- (AvnInputModifiers)getModifiers:(NSEventModifierFlags)mod -{ - unsigned int rv = 0; - - if (mod & NSEventModifierFlagControl) - rv |= Control; - if (mod & NSEventModifierFlagShift) - rv |= Shift; - if (mod & NSEventModifierFlagOption) - rv |= Alt; - if (mod & NSEventModifierFlagCommand) - rv |= Windows; - - if (_isLeftPressed) - rv |= LeftMouseButton; - if (_isMiddlePressed) - rv |= MiddleMouseButton; - if (_isRightPressed) - rv |= RightMouseButton; - if (_isXButton1Pressed) - rv |= XButton1MouseButton; - if (_isXButton2Pressed) - rv |= XButton2MouseButton; - - return (AvnInputModifiers)rv; -} - -- (BOOL)hasMarkedText -{ - return _lastKeyHandled; -} - -- (NSRange)markedRange -{ - return NSMakeRange(NSNotFound, 0); -} - -- (NSRange)selectedRange -{ - return NSMakeRange(NSNotFound, 0); -} - -- (void)setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange -{ - -} - -- (void)unmarkText -{ - -} - -- (NSArray *)validAttributesForMarkedText -{ - return [NSArray new]; -} - -- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)range actualRange:(NSRangePointer)actualRange -{ - return [NSAttributedString new]; -} - -- (void)insertText:(id)string replacementRange:(NSRange)replacementRange -{ - if(!_lastKeyHandled) - { - if(_parent != nullptr) - { - _lastKeyHandled = _parent->BaseEvents->RawTextInputEvent(0, [string UTF8String]); - } - } -} - -- (NSUInteger)characterIndexForPoint:(NSPoint)point -{ - return 0; -} - -- (NSRect)firstRectForCharacterRange:(NSRange)range actualRange:(NSRangePointer)actualRange -{ - CGRect result = { 0 }; - - return result; -} - -- (NSDragOperation)triggerAvnDragEvent: (AvnDragEventType) type info: (id )info -{ - auto localPoint = [self convertPoint:[info draggingLocation] toView:self]; - auto avnPoint = [AvnView toAvnPoint:localPoint]; - auto point = [self translateLocalPoint:avnPoint]; - auto modifiers = [self getModifiers:[[NSApp currentEvent] modifierFlags]]; - NSDragOperation nsop = [info draggingSourceOperationMask]; - - auto effects = ConvertDragDropEffects(nsop); - int reffects = (int)_parent->BaseEvents - ->DragEvent(type, point, modifiers, effects, - CreateClipboard([info draggingPasteboard], nil), - GetAvnDataObjectHandleFromDraggingInfo(info)); - - NSDragOperation ret = static_cast(0); - - // Ensure that the managed part didn't add any new effects - reffects = (int)effects & reffects; - - // OSX requires exactly one operation - if((reffects & (int)AvnDragDropEffects::Copy) != 0) - ret = NSDragOperationCopy; - else if((reffects & (int)AvnDragDropEffects::Move) != 0) - ret = NSDragOperationMove; - else if((reffects & (int)AvnDragDropEffects::Link) != 0) - ret = NSDragOperationLink; - if(ret == 0) - ret = NSDragOperationNone; - return ret; -} - -- (NSDragOperation)draggingEntered:(id )sender -{ - return [self triggerAvnDragEvent: AvnDragEventType::Enter info:sender]; -} - -- (NSDragOperation)draggingUpdated:(id )sender -{ - return [self triggerAvnDragEvent: AvnDragEventType::Over info:sender]; -} - -- (void)draggingExited:(id )sender -{ - [self triggerAvnDragEvent: AvnDragEventType::Leave info:sender]; -} - -- (BOOL)prepareForDragOperation:(id )sender -{ - return [self triggerAvnDragEvent: AvnDragEventType::Over info:sender] != NSDragOperationNone; -} - -- (BOOL)performDragOperation:(id )sender -{ - return [self triggerAvnDragEvent: AvnDragEventType::Drop info:sender] != NSDragOperationNone; -} - -- (void)concludeDragOperation:(nullable id )sender -{ - -} - -- (AvnPlatformResizeReason)getResizeReason -{ - return _resizeReason; -} - -- (void)setResizeReason:(AvnPlatformResizeReason)reason -{ - _resizeReason = reason; -} - -- (AvnAccessibilityElement *) accessibilityChild -{ - if (_accessibilityChild == nil) - { - auto peer = _parent->BaseEvents->GetAutomationPeer(); - - if (peer == nil) - return nil; - - _accessibilityChild = [AvnAccessibilityElement acquire:peer]; - } - - return _accessibilityChild; -} - -- (NSArray *)accessibilityChildren -{ - auto child = [self accessibilityChild]; - return NSAccessibilityUnignoredChildrenForOnlyChild(child); -} - -- (id)accessibilityHitTest:(NSPoint)point -{ - return [[self accessibilityChild] accessibilityHitTest:point]; -} - -- (id)accessibilityFocusedUIElement -{ - return [[self accessibilityChild] accessibilityFocusedUIElement]; -} - -@end - @implementation AvnWindow { From f19639684d4fd7ac1b497a27f874475c6888447d Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 5 May 2022 18:17:58 +0100 Subject: [PATCH 277/416] move AutoFitContentView to its own file. --- .../src/OSX/AutoFitContentView.h | 15 +++ .../src/OSX/AutoFitContentView.mm | 104 ++++++++++++++++++ .../project.pbxproj | 8 ++ .../Avalonia.Native/src/OSX/WindowBaseImpl.h | 2 + .../Avalonia.Native/src/OSX/WindowBaseImpl.mm | 1 + native/Avalonia.Native/src/OSX/WindowImpl.mm | 3 +- native/Avalonia.Native/src/OSX/window.h | 8 -- native/Avalonia.Native/src/OSX/window.mm | 98 +---------------- 8 files changed, 132 insertions(+), 107 deletions(-) create mode 100644 native/Avalonia.Native/src/OSX/AutoFitContentView.h create mode 100644 native/Avalonia.Native/src/OSX/AutoFitContentView.mm diff --git a/native/Avalonia.Native/src/OSX/AutoFitContentView.h b/native/Avalonia.Native/src/OSX/AutoFitContentView.h new file mode 100644 index 0000000000..68c9fb8dc8 --- /dev/null +++ b/native/Avalonia.Native/src/OSX/AutoFitContentView.h @@ -0,0 +1,15 @@ +// +// Created by Dan Walmsley on 05/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#import +#import "avalonia-native.h" + +@interface AutoFitContentView : NSView +-(AutoFitContentView* _Nonnull) initWithContent: (NSView* _Nonnull) content; +-(void) ShowTitleBar: (bool) show; +-(void) SetTitleBarHeightHint: (double) height; + +-(void) ShowBlur: (bool) show; +@end \ No newline at end of file diff --git a/native/Avalonia.Native/src/OSX/AutoFitContentView.mm b/native/Avalonia.Native/src/OSX/AutoFitContentView.mm new file mode 100644 index 0000000000..92d6f67a91 --- /dev/null +++ b/native/Avalonia.Native/src/OSX/AutoFitContentView.mm @@ -0,0 +1,104 @@ +// +// Created by Dan Walmsley on 05/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#include "AvnView.h" +#import "AutoFitContentView.h" + +@implementation AutoFitContentView +{ + NSVisualEffectView* _titleBarMaterial; + NSBox* _titleBarUnderline; + NSView* _content; + NSVisualEffectView* _blurBehind; + double _titleBarHeightHint; + bool _settingSize; +} + +-(AutoFitContentView* _Nonnull) initWithContent:(NSView *)content +{ + _titleBarHeightHint = -1; + _content = content; + _settingSize = false; + + [self setAutoresizesSubviews:true]; + [self setWantsLayer:true]; + + _titleBarMaterial = [NSVisualEffectView new]; + [_titleBarMaterial setBlendingMode:NSVisualEffectBlendingModeWithinWindow]; + [_titleBarMaterial setMaterial:NSVisualEffectMaterialTitlebar]; + [_titleBarMaterial setWantsLayer:true]; + _titleBarMaterial.hidden = true; + + _titleBarUnderline = [NSBox new]; + _titleBarUnderline.boxType = NSBoxSeparator; + _titleBarUnderline.fillColor = [NSColor underPageBackgroundColor]; + _titleBarUnderline.hidden = true; + + [self addSubview:_titleBarMaterial]; + [self addSubview:_titleBarUnderline]; + + _blurBehind = [NSVisualEffectView new]; + [_blurBehind setBlendingMode:NSVisualEffectBlendingModeBehindWindow]; + [_blurBehind setMaterial:NSVisualEffectMaterialLight]; + [_blurBehind setWantsLayer:true]; + _blurBehind.hidden = true; + + [_blurBehind setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + [_content setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + + [self addSubview:_blurBehind]; + [self addSubview:_content]; + + [self setWantsLayer:true]; + return self; +} + +-(void) ShowBlur:(bool)show +{ + _blurBehind.hidden = !show; +} + +-(void) ShowTitleBar: (bool) show +{ + _titleBarMaterial.hidden = !show; + _titleBarUnderline.hidden = !show; +} + +-(void) SetTitleBarHeightHint: (double) height +{ + _titleBarHeightHint = height; + + [self setFrameSize:self.frame.size]; +} + +-(void)setFrameSize:(NSSize)newSize +{ + if(_settingSize) + { + return; + } + + _settingSize = true; + [super setFrameSize:newSize]; + + auto window = objc_cast([self window]); + + // TODO get actual titlebar size + + double height = _titleBarHeightHint == -1 ? [window getExtendedTitleBarHeight] : _titleBarHeightHint; + + NSRect tbar; + tbar.origin.x = 0; + tbar.origin.y = newSize.height - height; + tbar.size.width = newSize.width; + tbar.size.height = height; + + [_titleBarMaterial setFrame:tbar]; + tbar.size.height = height < 1 ? 0 : 1; + [_titleBarUnderline setFrame:tbar]; + + _settingSize = false; +} +@end \ No newline at end of file diff --git a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj index 2d9dcbf9c8..88e4c0c682 100644 --- a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj +++ b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj @@ -13,9 +13,11 @@ 1839171DCC651B0638603AC4 /* INSWindowHolder.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391BBB7782C296D424071F /* INSWindowHolder.h */; }; 1839179A55FC1421BEE83330 /* WindowBaseImpl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 18391676ECF0E983F4964357 /* WindowBaseImpl.mm */; }; 183919D91DB9AAB5D700C2EA /* WindowImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391CD090AA776E7E841AC9 /* WindowImpl.h */; }; + 18391AA7E0BBA74D184C5734 /* AutoFitContentView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1839166350F32661F3ABD70F /* AutoFitContentView.mm */; }; 18391C28BF1823B5464FDD36 /* ResizeScope.h in Headers */ = {isa = PBXBuildFile; fileRef = 1839171D898F9BFC1373631A /* ResizeScope.h */; }; 18391CF07316F819E76B617C /* IWindowStateChanged.h in Headers */ = {isa = PBXBuildFile; fileRef = 183913C6BFD6856BD42D19FD /* IWindowStateChanged.h */; }; 18391D4EB311BC7EF8B8C0A6 /* AvnView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1839132D0E2454D911F1D1F9 /* AvnView.mm */; }; + 18391E1381E2D5BFD60265A9 /* AutoFitContentView.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391654EF0E7AB3D3AB4071 /* AutoFitContentView.h */; }; 18391ED5F611FF62C45F196D /* AvnView.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391D1669284AD2EC9E866A /* AvnView.h */; }; 1A002B9E232135EE00021753 /* app.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A002B9D232135EE00021753 /* app.mm */; }; 1A1852DC23E05814008F0DED /* deadlock.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A1852DB23E05814008F0DED /* deadlock.mm */; }; @@ -48,6 +50,8 @@ 1839132D0E2454D911F1D1F9 /* AvnView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AvnView.mm; sourceTree = ""; }; 183913C6BFD6856BD42D19FD /* IWindowStateChanged.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IWindowStateChanged.h; sourceTree = ""; }; 183915BFF0E234CD3604A7CD /* WindowBaseImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowBaseImpl.h; sourceTree = ""; }; + 18391654EF0E7AB3D3AB4071 /* AutoFitContentView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AutoFitContentView.h; sourceTree = ""; }; + 1839166350F32661F3ABD70F /* AutoFitContentView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AutoFitContentView.mm; sourceTree = ""; }; 18391676ECF0E983F4964357 /* WindowBaseImpl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WindowBaseImpl.mm; sourceTree = ""; }; 1839171D898F9BFC1373631A /* ResizeScope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ResizeScope.h; sourceTree = ""; }; 183919BF108EB72A029F7671 /* WindowImpl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WindowImpl.mm; sourceTree = ""; }; @@ -160,6 +164,8 @@ 1839171D898F9BFC1373631A /* ResizeScope.h */, 1839132D0E2454D911F1D1F9 /* AvnView.mm */, 18391D1669284AD2EC9E866A /* AvnView.h */, + 1839166350F32661F3ABD70F /* AutoFitContentView.mm */, + 18391654EF0E7AB3D3AB4071 /* AutoFitContentView.h */, ); sourceTree = ""; }; @@ -186,6 +192,7 @@ 18391CF07316F819E76B617C /* IWindowStateChanged.h in Headers */, 18391C28BF1823B5464FDD36 /* ResizeScope.h in Headers */, 18391ED5F611FF62C45F196D /* AvnView.h in Headers */, + 18391E1381E2D5BFD60265A9 /* AutoFitContentView.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -269,6 +276,7 @@ 1839125F057B0A4EB1760058 /* WindowImpl.mm in Sources */, 18391068E48EF96E3DB5FDAB /* ResizeScope.mm in Sources */, 18391D4EB311BC7EF8B8C0A6 /* AvnView.mm in Sources */, + 18391AA7E0BBA74D184C5734 /* AutoFitContentView.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index 94175b8187..d2690cee41 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -9,6 +9,8 @@ #import "rendertarget.h" #include "INSWindowHolder.h" +@class AutoFitContentView; + class WindowBaseImpl : public virtual ComObject, public virtual IAvnWindowBase, public INSWindowHolder { diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index 8f409fa84b..ca610d3ce6 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -13,6 +13,7 @@ #import "WindowBaseImpl.h" #import "cursor.h" #include "ResizeScope.h" +#import "AutoFitContentView.h" WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl) { _shown = false; diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index ef8be4be9c..954d27ab98 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -5,10 +5,9 @@ #import #import "window.h" +#import "AutoFitContentView.h" #import "AvnView.h" #include "automation.h" -#include "menu.h" -#import "WindowImpl.h" WindowImpl::WindowImpl(IAvnWindowEvents *events, IAvnGlContext *gl) : WindowBaseImpl(events, gl) { diff --git a/native/Avalonia.Native/src/OSX/window.h b/native/Avalonia.Native/src/OSX/window.h index 8caceb5bc1..e9b98ce9ae 100644 --- a/native/Avalonia.Native/src/OSX/window.h +++ b/native/Avalonia.Native/src/OSX/window.h @@ -7,14 +7,6 @@ class WindowBaseImpl; -@interface AutoFitContentView : NSView --(AutoFitContentView* _Nonnull) initWithContent: (NSView* _Nonnull) content; --(void) ShowTitleBar: (bool) show; --(void) SetTitleBarHeightHint: (double) height; - --(void) ShowBlur: (bool) show; -@end - @interface AvnWindow : NSWindow -(AvnWindow* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent; -(void) setCanBecomeKeyAndMain; diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index a66ff94c8a..5bd3d8ddac 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -8,103 +8,7 @@ #import "WindowBaseImpl.h" #include "WindowImpl.h" #include "AvnView.h" - -@implementation AutoFitContentView -{ - NSVisualEffectView* _titleBarMaterial; - NSBox* _titleBarUnderline; - NSView* _content; - NSVisualEffectView* _blurBehind; - double _titleBarHeightHint; - bool _settingSize; -} - --(AutoFitContentView* _Nonnull) initWithContent:(NSView *)content -{ - _titleBarHeightHint = -1; - _content = content; - _settingSize = false; - - [self setAutoresizesSubviews:true]; - [self setWantsLayer:true]; - - _titleBarMaterial = [NSVisualEffectView new]; - [_titleBarMaterial setBlendingMode:NSVisualEffectBlendingModeWithinWindow]; - [_titleBarMaterial setMaterial:NSVisualEffectMaterialTitlebar]; - [_titleBarMaterial setWantsLayer:true]; - _titleBarMaterial.hidden = true; - - _titleBarUnderline = [NSBox new]; - _titleBarUnderline.boxType = NSBoxSeparator; - _titleBarUnderline.fillColor = [NSColor underPageBackgroundColor]; - _titleBarUnderline.hidden = true; - - [self addSubview:_titleBarMaterial]; - [self addSubview:_titleBarUnderline]; - - _blurBehind = [NSVisualEffectView new]; - [_blurBehind setBlendingMode:NSVisualEffectBlendingModeBehindWindow]; - [_blurBehind setMaterial:NSVisualEffectMaterialLight]; - [_blurBehind setWantsLayer:true]; - _blurBehind.hidden = true; - - [_blurBehind setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; - [_content setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; - - [self addSubview:_blurBehind]; - [self addSubview:_content]; - - [self setWantsLayer:true]; - return self; -} - --(void) ShowBlur:(bool)show -{ - _blurBehind.hidden = !show; -} - --(void) ShowTitleBar: (bool) show -{ - _titleBarMaterial.hidden = !show; - _titleBarUnderline.hidden = !show; -} - --(void) SetTitleBarHeightHint: (double) height -{ - _titleBarHeightHint = height; - - [self setFrameSize:self.frame.size]; -} - --(void)setFrameSize:(NSSize)newSize -{ - if(_settingSize) - { - return; - } - - _settingSize = true; - [super setFrameSize:newSize]; - - auto window = objc_cast([self window]); - - // TODO get actual titlebar size - - double height = _titleBarHeightHint == -1 ? [window getExtendedTitleBarHeight] : _titleBarHeightHint; - - NSRect tbar; - tbar.origin.x = 0; - tbar.origin.y = newSize.height - height; - tbar.size.width = newSize.width; - tbar.size.height = height; - - [_titleBarMaterial setFrame:tbar]; - tbar.size.height = height < 1 ? 0 : 1; - [_titleBarUnderline setFrame:tbar]; - - _settingSize = false; -} -@end +#include "AutoFitContentView.h" @implementation AvnWindow From 3aab84d8daca2575897ca435af49f073f0a8aeb2 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 5 May 2022 18:21:58 +0100 Subject: [PATCH 278/416] remove some imports no longer needed. --- native/Avalonia.Native/src/OSX/WindowBaseImpl.mm | 2 -- native/Avalonia.Native/src/OSX/WindowImpl.mm | 1 - native/Avalonia.Native/src/OSX/window.mm | 4 ---- 3 files changed, 7 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index ca610d3ce6..fb67addce1 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -8,9 +8,7 @@ #import "window.h" #import "AvnView.h" #include "menu.h" -#include "rendertarget.h" #include "automation.h" -#import "WindowBaseImpl.h" #import "cursor.h" #include "ResizeScope.h" #import "AutoFitContentView.h" diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index 954d27ab98..0d325c4490 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -9,7 +9,6 @@ #import "AvnView.h" #include "automation.h" - WindowImpl::WindowImpl(IAvnWindowEvents *events, IAvnGlContext *gl) : WindowBaseImpl(events, gl) { _isClientAreaExtended = false; _extendClientHints = AvnDefaultChrome; diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index bf2cbdbf19..68a459d088 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -1,15 +1,11 @@ #import #include "common.h" #import "window.h" -#include "KeyTransform.h" #include "menu.h" -#include "rendertarget.h" #include "automation.h" #import "WindowBaseImpl.h" #include "WindowImpl.h" #include "AvnView.h" -#include "AutoFitContentView.h" - @implementation AvnWindow { From 174b94f3b4c822018dcc7daaccc9f1420ef8c172 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Fri, 6 May 2022 09:28:45 +0200 Subject: [PATCH 279/416] Replace ImageMagick with ImageSharp for render test comparisons. --- Avalonia.sln | 2 +- ....NET-Q16-AnyCPU.props => ImageSharp.props} | 2 +- .../Avalonia.Direct2D1.RenderTests.csproj | 2 +- tests/Avalonia.RenderTests/TestBase.cs | 68 ++++++++++++++++--- .../Avalonia.Skia.RenderTests.csproj | 2 +- 5 files changed, 62 insertions(+), 14 deletions(-) rename build/{Magick.NET-Q16-AnyCPU.props => ImageSharp.props} (63%) diff --git a/Avalonia.sln b/Avalonia.sln index ea30514c3e..c8e513f94c 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -99,7 +99,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1 build\HarfBuzzSharp.props = build\HarfBuzzSharp.props build\JetBrains.Annotations.props = build\JetBrains.Annotations.props build\JetBrains.dotMemoryUnit.props = build\JetBrains.dotMemoryUnit.props - build\Magick.NET-Q16-AnyCPU.props = build\Magick.NET-Q16-AnyCPU.props build\Microsoft.CSharp.props = build\Microsoft.CSharp.props build\Microsoft.Reactive.Testing.props = build\Microsoft.Reactive.Testing.props build\Moq.props = build\Moq.props @@ -118,6 +117,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1 build\System.Memory.props = build\System.Memory.props build\UnitTests.NetFX.props = build\UnitTests.NetFX.props build\XUnit.props = build\XUnit.props + build\ImageSharp.props = build\ImageSharp.props EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Targets", "Targets", "{4D6FAF79-58B4-482F-9122-0668C346364C}" diff --git a/build/Magick.NET-Q16-AnyCPU.props b/build/ImageSharp.props similarity index 63% rename from build/Magick.NET-Q16-AnyCPU.props rename to build/ImageSharp.props index 21d9cdcb1f..178c274ac9 100644 --- a/build/Magick.NET-Q16-AnyCPU.props +++ b/build/ImageSharp.props @@ -1,5 +1,5 @@ - + diff --git a/tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj b/tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj index e7f1552370..52acc78db1 100644 --- a/tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj +++ b/tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj @@ -19,6 +19,6 @@ - + diff --git a/tests/Avalonia.RenderTests/TestBase.cs b/tests/Avalonia.RenderTests/TestBase.cs index b70c721085..523876500f 100644 --- a/tests/Avalonia.RenderTests/TestBase.cs +++ b/tests/Avalonia.RenderTests/TestBase.cs @@ -1,10 +1,9 @@ using System.IO; using System.Runtime.CompilerServices; -using ImageMagick; using Avalonia.Controls; using Avalonia.Media.Imaging; using Avalonia.Rendering; - +using SixLabors.ImageSharp; using Xunit; using Avalonia.Platform; using System.Threading.Tasks; @@ -12,6 +11,8 @@ using System; using System.Threading; using Avalonia.Media; using Avalonia.Threading; +using SixLabors.ImageSharp.PixelFormats; +using Image = SixLabors.ImageSharp.Image; #if AVALONIA_SKIA using Avalonia.Skia; #else @@ -119,12 +120,12 @@ namespace Avalonia.Direct2D1.RenderTests var immediatePath = Path.Combine(OutputPath, testName + ".immediate.out.png"); var deferredPath = Path.Combine(OutputPath, testName + ".deferred.out.png"); - using (var expected = new MagickImage(expectedPath)) - using (var immediate = new MagickImage(immediatePath)) - using (var deferred = new MagickImage(deferredPath)) + using (var expected = Image.Load(expectedPath)) + using (var immediate = Image.Load(immediatePath)) + using (var deferred = Image.Load(deferredPath)) { - double immediateError = expected.Compare(immediate, ErrorMetric.RootMeanSquared); - double deferredError = expected.Compare(deferred, ErrorMetric.RootMeanSquared); + var immediateError = CompareImages(immediate, expected); + var deferredError = CompareImages(deferred, expected); if (immediateError > 0.022) { @@ -143,10 +144,10 @@ namespace Avalonia.Direct2D1.RenderTests var expectedPath = Path.Combine(OutputPath, testName + ".expected.png"); var actualPath = Path.Combine(OutputPath, testName + ".out.png"); - using (var expected = new MagickImage(expectedPath)) - using (var actual = new MagickImage(actualPath)) + using (var expected = Image.Load(expectedPath)) + using (var actual = Image.Load(actualPath)) { - double immediateError = expected.Compare(actual, ErrorMetric.RootMeanSquared); + double immediateError = CompareImages(actual, expected); if (immediateError > 0.022) { @@ -154,6 +155,53 @@ namespace Avalonia.Direct2D1.RenderTests } } } + + /// + /// Calculates root mean square error for given two images. + /// Based roughly on ImageMagick implementation to ensure consistency. + /// + private static double CompareImages(Image actual, Image expected) + { + if (actual.Width != expected.Width || actual.Height != expected.Height) + { + throw new ArgumentException("Images have different resolutions"); + } + + var quantity = actual.Width * actual.Height; + double squaresError = 0; + + const double scale = 1 / 255d; + + for (var x = 0; x < actual.Width; x++) + { + double localError = 0; + + for (var y = 0; y < actual.Height; y++) + { + var expectedAlpha = expected[x, y].A * scale; + var actualAlpha = actual[x, y].A * scale; + + var r = scale * (expectedAlpha * expected[x, y].R - actualAlpha * actual[x, y].R); + var g = scale * (expectedAlpha * expected[x, y].G - actualAlpha * actual[x, y].G); + var b = scale * (expectedAlpha * expected[x, y].B - actualAlpha * actual[x, y].B); + var a = expectedAlpha - actualAlpha; + + var error = r * r + g * g + b * b + a * a; + + localError += error; + } + + squaresError += localError; + } + + var meanSquaresError = squaresError / quantity; + + const int channelCount = 4; + + meanSquaresError = meanSquaresError / channelCount; + + return Math.Sqrt(meanSquaresError); + } private string GetTestsDirectory() { diff --git a/tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj b/tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj index ffbcdd9e7c..d3f2b44968 100644 --- a/tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj +++ b/tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj @@ -20,7 +20,7 @@ - + From 66d7ffe2e99d8809bf1801bd7df20397db4260e0 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 6 May 2022 11:23:52 +0100 Subject: [PATCH 280/416] use content min/max size for minsize. --- native/Avalonia.Native/src/OSX/WindowBaseImpl.mm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index fb67addce1..43a27a34b2 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -225,8 +225,8 @@ HRESULT WindowBaseImpl::SetMinMaxSize(AvnSize minSize, AvnSize maxSize) { START_COM_CALL; @autoreleasepool { - [Window setMinSize:ToNSSize(minSize)]; - [Window setMaxSize:ToNSSize(maxSize)]; + [Window setContentMinSize:ToNSSize(minSize)]; + [Window setContentMaxSize:ToNSSize(maxSize)]; return S_OK; } @@ -243,8 +243,8 @@ HRESULT WindowBaseImpl::Resize(double x, double y, AvnPlatformResizeReason reaso auto resizeBlock = ResizeScope(View, reason); @autoreleasepool { - auto maxSize = [Window maxSize]; - auto minSize = [Window minSize]; + auto maxSize = [Window contentMaxSize]; + auto minSize = [Window contentMinSize]; if (x < minSize.width) { x = minSize.width; From 90f6143c58d9641fca2a19734fbeb9462c30639c Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 6 May 2022 12:40:33 +0200 Subject: [PATCH 281/416] Don't use managed dialogs by default in ControlCatalog. It means that system dialogs never get properly tested. --- samples/ControlCatalog.NetCore/Program.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/samples/ControlCatalog.NetCore/Program.cs b/samples/ControlCatalog.NetCore/Program.cs index 4b81935452..4464413e63 100644 --- a/samples/ControlCatalog.NetCore/Program.cs +++ b/samples/ControlCatalog.NetCore/Program.cs @@ -117,7 +117,6 @@ namespace ControlCatalog.NetCore EnableMultitouch = true }) .UseSkia() - .UseManagedSystemDialogs() .AfterSetup(builder => { builder.Instance!.AttachDevTools(new Avalonia.Diagnostics.DevToolsOptions() From 4cba4519f3e76d368384a40a9e655a76eedcf7f9 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 6 May 2022 11:45:47 +0100 Subject: [PATCH 282/416] only create the NSWindow when show is called. Cache sizes until needed. --- .../Avalonia.Native/src/OSX/WindowBaseImpl.h | 11 ++-- .../Avalonia.Native/src/OSX/WindowBaseImpl.mm | 53 ++++++++++++++----- native/Avalonia.Native/src/OSX/window.h | 2 +- native/Avalonia.Native/src/OSX/window.mm | 6 ++- 4 files changed, 51 insertions(+), 21 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index d2690cee41..19c443806c 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -24,10 +24,7 @@ BEGIN_INTERFACE_MAP() INTERFACE_MAP_ENTRY(IAvnWindowBase, IID_IAvnWindowBase) END_INTERFACE_MAP() - virtual ~WindowBaseImpl() { - View = NULL; - Window = NULL; - } + virtual ~WindowBaseImpl(); AutoFitContentView *StandardContainer; AvnView *View; @@ -36,6 +33,9 @@ BEGIN_INTERFACE_MAP() ComPtr _glContext; NSObject *renderTarget; AvnPoint lastPositionSet; + NSSize lastSize; + NSSize lastMinSize; + NSSize lastMaxSize; NSString *_lastTitle; bool _shown; @@ -116,6 +116,9 @@ protected: void UpdateStyle(); +private: + void InitialiseNSWindow (); + }; #endif //AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index fb67addce1..fe3a209d3e 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -12,6 +12,13 @@ #import "cursor.h" #include "ResizeScope.h" #import "AutoFitContentView.h" +#include "WindowBaseImpl.h" + + +WindowBaseImpl::~WindowBaseImpl() { + View = nullptr; + Window = nullptr; +} WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl) { _shown = false; @@ -22,17 +29,11 @@ WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl) View = [[AvnView alloc] initWithParent:this]; StandardContainer = [[AutoFitContentView new] initWithContent:View]; - Window = [[AvnWindow alloc] initWithParent:this]; - [Window setContentView:StandardContainer]; - lastPositionSet.X = 100; lastPositionSet.Y = 100; _lastTitle = @""; - [Window setStyleMask:NSWindowStyleMaskBorderless]; - [Window setBackingType:NSBackingStoreBuffered]; - - [Window setOpaque:false]; + Window = nullptr; } HRESULT WindowBaseImpl::ObtainNSViewHandle(void **ret) { @@ -83,6 +84,8 @@ HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) { START_COM_CALL; @autoreleasepool { + InitialiseNSWindow(); + SetPosition(lastPositionSet); UpdateStyle(); @@ -97,6 +100,10 @@ HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) { [Window orderFront:Window]; } + [Window setContentMinSize:lastMinSize]; + [Window setContentMaxSize:lastMaxSize]; + [Window setContentSize: lastSize]; + _shown = true; return S_OK; @@ -225,8 +232,13 @@ HRESULT WindowBaseImpl::SetMinMaxSize(AvnSize minSize, AvnSize maxSize) { START_COM_CALL; @autoreleasepool { - [Window setMinSize:ToNSSize(minSize)]; - [Window setMaxSize:ToNSSize(maxSize)]; + lastMinSize = ToNSSize(minSize); + lastMaxSize = ToNSSize(maxSize); + + if(Window != nullptr) { + [Window setContentMinSize:lastMinSize]; + [Window setContentMaxSize:lastMaxSize]; + } return S_OK; } @@ -243,8 +255,8 @@ HRESULT WindowBaseImpl::Resize(double x, double y, AvnPlatformResizeReason reaso auto resizeBlock = ResizeScope(View, reason); @autoreleasepool { - auto maxSize = [Window maxSize]; - auto minSize = [Window minSize]; + auto maxSize = lastMaxSize; + auto minSize = lastMinSize; if (x < minSize.width) { x = minSize.width; @@ -267,8 +279,12 @@ HRESULT WindowBaseImpl::Resize(double x, double y, AvnPlatformResizeReason reaso BaseEvents->Resized(AvnSize{x, y}, reason); } - [Window setContentSize:NSSize{x, y}]; - [Window invalidateShadow]; + lastSize = NSSize {x, y}; + + if(Window != nullptr) { + [Window setContentSize:lastSize]; + [Window invalidateShadow]; + } } @finally { _inResize = false; @@ -502,4 +518,13 @@ NSWindowStyleMask WindowBaseImpl::GetStyle() { void WindowBaseImpl::UpdateStyle() { [Window setStyleMask:GetStyle()]; -} \ No newline at end of file +} + +void WindowBaseImpl::InitialiseNSWindow() { + Window = [[AvnWindow alloc] initWithParent:this contentRect:NSRect{ 0, 0, lastSize } styleMask:GetStyle()]; + [Window setContentView:StandardContainer]; + [Window setStyleMask:NSWindowStyleMaskBorderless]; + [Window setBackingType:NSBackingStoreBuffered]; + + [Window setOpaque:false]; +} diff --git a/native/Avalonia.Native/src/OSX/window.h b/native/Avalonia.Native/src/OSX/window.h index e9b98ce9ae..a8c31e7b60 100644 --- a/native/Avalonia.Native/src/OSX/window.h +++ b/native/Avalonia.Native/src/OSX/window.h @@ -8,7 +8,7 @@ class WindowBaseImpl; @interface AvnWindow : NSWindow --(AvnWindow* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent; +-(AvnWindow* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask; -(void) setCanBecomeKeyAndMain; -(void) pollModalSession: (NSModalSession _Nonnull) session; -(void) restoreParentWindow; diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index 68a459d088..965b4a849b 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -143,9 +143,10 @@ _canBecomeKeyAndMain = true; } --(AvnWindow*) initWithParent: (WindowBaseImpl*) parent +-(AvnWindow*) initWithParent: (WindowBaseImpl*) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask; { - self = [super init]; + self = [super initWithContentRect:contentRect styleMask: styleMask backing:NSBackingStoreBuffered defer:false]; + [self setReleasedWhenClosed:false]; _parent = parent; [self setDelegate:self]; @@ -155,6 +156,7 @@ [self backingScaleFactor]; [self setOpaque:NO]; [self setBackgroundColor: [NSColor clearColor]]; + _isExtended = false; return self; } From 0ee1a7e3910525a069230d713c93ed47d3528b4d Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 6 May 2022 11:50:42 +0100 Subject: [PATCH 283/416] add explanation for init with content size. --- native/Avalonia.Native/src/OSX/window.mm | 3 +++ 1 file changed, 3 insertions(+) diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index 965b4a849b..68f8f76d75 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -145,6 +145,9 @@ -(AvnWindow*) initWithParent: (WindowBaseImpl*) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask; { + // https://jameshfisher.com/2020/07/10/why-is-the-contentrect-of-my-nswindow-ignored/ + // create nswindow with specific contentRect, otherwise we wont be able to resize the window + // until several ms after the window is physically on the screen. self = [super initWithContentRect:contentRect styleMask: styleMask backing:NSBackingStoreBuffered defer:false]; [self setReleasedWhenClosed:false]; From 2372155995ec6680c5ec27570da95a23b9e09cc4 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 6 May 2022 13:05:40 +0100 Subject: [PATCH 284/416] only create the window if reference is null --- native/Avalonia.Native/src/OSX/WindowBaseImpl.mm | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index fe3a209d3e..0c85dcc303 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -521,10 +521,12 @@ void WindowBaseImpl::UpdateStyle() { } void WindowBaseImpl::InitialiseNSWindow() { - Window = [[AvnWindow alloc] initWithParent:this contentRect:NSRect{ 0, 0, lastSize } styleMask:GetStyle()]; - [Window setContentView:StandardContainer]; - [Window setStyleMask:NSWindowStyleMaskBorderless]; - [Window setBackingType:NSBackingStoreBuffered]; + if(Window == nullptr) { + Window = [[AvnWindow alloc] initWithParent:this contentRect:NSRect{0, 0, lastSize} styleMask:GetStyle()]; + [Window setContentView:StandardContainer]; + [Window setStyleMask:NSWindowStyleMaskBorderless]; + [Window setBackingType:NSBackingStoreBuffered]; - [Window setOpaque:false]; + [Window setOpaque:false]; + } } From e76887fe25830404a0d6afd97782e0f8629c22b0 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 6 May 2022 14:14:08 +0100 Subject: [PATCH 285/416] INSWindowHolder uses NSView and NSWindow types. --- native/Avalonia.Native/src/OSX/INSWindowHolder.h | 4 ++-- native/Avalonia.Native/src/OSX/WindowBaseImpl.h | 4 ++-- native/Avalonia.Native/src/OSX/WindowBaseImpl.mm | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/INSWindowHolder.h b/native/Avalonia.Native/src/OSX/INSWindowHolder.h index adc0dd1990..ae64a53e7d 100644 --- a/native/Avalonia.Native/src/OSX/INSWindowHolder.h +++ b/native/Avalonia.Native/src/OSX/INSWindowHolder.h @@ -10,8 +10,8 @@ struct INSWindowHolder { - virtual AvnWindow* _Nonnull GetNSWindow () = 0; - virtual AvnView* _Nonnull GetNSView () = 0; + virtual NSWindow* _Nonnull GetNSWindow () = 0; + virtual NSView* _Nonnull GetNSView () = 0; }; #endif //AVALONIA_NATIVE_OSX_INSWINDOWHOLDER_H diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index 19c443806c..fd079b1427 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -51,9 +51,9 @@ BEGIN_INTERFACE_MAP() virtual HRESULT ObtainNSViewHandleRetained(void **ret) override; - virtual AvnWindow *GetNSWindow() override; + virtual NSWindow *GetNSWindow() override; - virtual AvnView *GetNSView() override; + virtual NSView *GetNSView() override; virtual HRESULT Show(bool activate, bool isDialog) override; diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index 0c85dcc303..6ec026ed10 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -60,11 +60,11 @@ HRESULT WindowBaseImpl::ObtainNSViewHandleRetained(void **ret) { return S_OK; } -AvnWindow *WindowBaseImpl::GetNSWindow() { +NSWindow *WindowBaseImpl::GetNSWindow() { return Window; } -AvnView *WindowBaseImpl::GetNSView() { +NSView *WindowBaseImpl::GetNSView() { return View; } From 0ac5a1c808f192b17afcbb9acc8a9d387432fab8 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 6 May 2022 14:16:11 +0100 Subject: [PATCH 286/416] set min and max when creating window. --- native/Avalonia.Native/src/OSX/WindowBaseImpl.mm | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index 6ec026ed10..826486ae31 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -527,6 +527,10 @@ void WindowBaseImpl::InitialiseNSWindow() { [Window setStyleMask:NSWindowStyleMaskBorderless]; [Window setBackingType:NSBackingStoreBuffered]; + [Window setContentSize: lastSize]; + [Window setContentMinSize:lastMinSize]; + [Window setContentMaxSize:lastMaxSize]; + [Window setOpaque:false]; } } From 8be332ab1d1504ff12950821ce8136e1a8647603 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 6 May 2022 14:20:49 +0100 Subject: [PATCH 287/416] make AvnWindowProtocol --- .../project.pbxproj | 12 ++++++++++ .../Avalonia.Native/src/OSX/AvnPanelWindow.h | 9 ++++++++ .../Avalonia.Native/src/OSX/AvnPanelWindow.mm | 8 +++++++ .../Avalonia.Native/src/OSX/WindowBaseImpl.h | 6 +++-- .../Avalonia.Native/src/OSX/WindowBaseImpl.mm | 18 +++++++++------ native/Avalonia.Native/src/OSX/WindowImpl.mm | 8 +++---- .../Avalonia.Native/src/OSX/WindowProtocol.h | 23 +++++++++++++++++++ native/Avalonia.Native/src/OSX/window.h | 15 ++---------- native/Avalonia.Native/src/OSX/window.mm | 10 ++++---- 9 files changed, 78 insertions(+), 31 deletions(-) create mode 100644 native/Avalonia.Native/src/OSX/AvnPanelWindow.h create mode 100644 native/Avalonia.Native/src/OSX/AvnPanelWindow.mm create mode 100644 native/Avalonia.Native/src/OSX/WindowProtocol.h diff --git a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj index 88e4c0c682..8ee2a61741 100644 --- a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj +++ b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj @@ -9,9 +9,11 @@ /* Begin PBXBuildFile section */ 18391068E48EF96E3DB5FDAB /* ResizeScope.mm in Sources */ = {isa = PBXBuildFile; fileRef = 18391E45702740FE9DD69695 /* ResizeScope.mm */; }; 1839125F057B0A4EB1760058 /* WindowImpl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 183919BF108EB72A029F7671 /* WindowImpl.mm */; }; + 1839151F32D1BB1AB51A7BB6 /* AvnPanelWindow.mm in Sources */ = {isa = PBXBuildFile; fileRef = 18391884C7476DA4E53A492D /* AvnPanelWindow.mm */; }; 183916173528EC2737DBE5E1 /* WindowBaseImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 183915BFF0E234CD3604A7CD /* WindowBaseImpl.h */; }; 1839171DCC651B0638603AC4 /* INSWindowHolder.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391BBB7782C296D424071F /* INSWindowHolder.h */; }; 1839179A55FC1421BEE83330 /* WindowBaseImpl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 18391676ECF0E983F4964357 /* WindowBaseImpl.mm */; }; + 1839196EC57BBC5C8BE1C735 /* AvnPanelWindow.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391DC046BC700D56DF0B82 /* AvnPanelWindow.h */; }; 183919D91DB9AAB5D700C2EA /* WindowImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391CD090AA776E7E841AC9 /* WindowImpl.h */; }; 18391AA7E0BBA74D184C5734 /* AutoFitContentView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1839166350F32661F3ABD70F /* AutoFitContentView.mm */; }; 18391C28BF1823B5464FDD36 /* ResizeScope.h in Headers */ = {isa = PBXBuildFile; fileRef = 1839171D898F9BFC1373631A /* ResizeScope.h */; }; @@ -19,6 +21,7 @@ 18391D4EB311BC7EF8B8C0A6 /* AvnView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1839132D0E2454D911F1D1F9 /* AvnView.mm */; }; 18391E1381E2D5BFD60265A9 /* AutoFitContentView.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391654EF0E7AB3D3AB4071 /* AutoFitContentView.h */; }; 18391ED5F611FF62C45F196D /* AvnView.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391D1669284AD2EC9E866A /* AvnView.h */; }; + 18391F1E2411C79405A9943A /* WindowProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 1839122E037567BDD1D09DEB /* WindowProtocol.h */; }; 1A002B9E232135EE00021753 /* app.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A002B9D232135EE00021753 /* app.mm */; }; 1A1852DC23E05814008F0DED /* deadlock.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A1852DB23E05814008F0DED /* deadlock.mm */; }; 1A3E5EA823E9E83B00EDE661 /* rendertarget.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A3E5EA723E9E83B00EDE661 /* rendertarget.mm */; }; @@ -47,6 +50,7 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 1839122E037567BDD1D09DEB /* WindowProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowProtocol.h; sourceTree = ""; }; 1839132D0E2454D911F1D1F9 /* AvnView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AvnView.mm; sourceTree = ""; }; 183913C6BFD6856BD42D19FD /* IWindowStateChanged.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IWindowStateChanged.h; sourceTree = ""; }; 183915BFF0E234CD3604A7CD /* WindowBaseImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowBaseImpl.h; sourceTree = ""; }; @@ -54,10 +58,12 @@ 1839166350F32661F3ABD70F /* AutoFitContentView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AutoFitContentView.mm; sourceTree = ""; }; 18391676ECF0E983F4964357 /* WindowBaseImpl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WindowBaseImpl.mm; sourceTree = ""; }; 1839171D898F9BFC1373631A /* ResizeScope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ResizeScope.h; sourceTree = ""; }; + 18391884C7476DA4E53A492D /* AvnPanelWindow.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AvnPanelWindow.mm; sourceTree = ""; }; 183919BF108EB72A029F7671 /* WindowImpl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WindowImpl.mm; sourceTree = ""; }; 18391BBB7782C296D424071F /* INSWindowHolder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = INSWindowHolder.h; sourceTree = ""; }; 18391CD090AA776E7E841AC9 /* WindowImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowImpl.h; sourceTree = ""; }; 18391D1669284AD2EC9E866A /* AvnView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AvnView.h; sourceTree = ""; }; + 18391DC046BC700D56DF0B82 /* AvnPanelWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AvnPanelWindow.h; sourceTree = ""; }; 18391E45702740FE9DD69695 /* ResizeScope.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ResizeScope.mm; sourceTree = ""; }; 1A002B9D232135EE00021753 /* app.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = app.mm; sourceTree = ""; }; 1A1852DB23E05814008F0DED /* deadlock.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = deadlock.mm; sourceTree = ""; }; @@ -166,6 +172,9 @@ 18391D1669284AD2EC9E866A /* AvnView.h */, 1839166350F32661F3ABD70F /* AutoFitContentView.mm */, 18391654EF0E7AB3D3AB4071 /* AutoFitContentView.h */, + 18391884C7476DA4E53A492D /* AvnPanelWindow.mm */, + 18391DC046BC700D56DF0B82 /* AvnPanelWindow.h */, + 1839122E037567BDD1D09DEB /* WindowProtocol.h */, ); sourceTree = ""; }; @@ -193,6 +202,8 @@ 18391C28BF1823B5464FDD36 /* ResizeScope.h in Headers */, 18391ED5F611FF62C45F196D /* AvnView.h in Headers */, 18391E1381E2D5BFD60265A9 /* AutoFitContentView.h in Headers */, + 1839196EC57BBC5C8BE1C735 /* AvnPanelWindow.h in Headers */, + 18391F1E2411C79405A9943A /* WindowProtocol.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -277,6 +288,7 @@ 18391068E48EF96E3DB5FDAB /* ResizeScope.mm in Sources */, 18391D4EB311BC7EF8B8C0A6 /* AvnView.mm in Sources */, 18391AA7E0BBA74D184C5734 /* AutoFitContentView.mm in Sources */, + 1839151F32D1BB1AB51A7BB6 /* AvnPanelWindow.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/native/Avalonia.Native/src/OSX/AvnPanelWindow.h b/native/Avalonia.Native/src/OSX/AvnPanelWindow.h new file mode 100644 index 0000000000..0fe95d044d --- /dev/null +++ b/native/Avalonia.Native/src/OSX/AvnPanelWindow.h @@ -0,0 +1,9 @@ +// +// Created by Dan Walmsley on 06/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#define NSWindow NSPanel +#define AvnWindow AvnPanelWindow + +//#include "window.h" \ No newline at end of file diff --git a/native/Avalonia.Native/src/OSX/AvnPanelWindow.mm b/native/Avalonia.Native/src/OSX/AvnPanelWindow.mm new file mode 100644 index 0000000000..8b79e28ca1 --- /dev/null +++ b/native/Avalonia.Native/src/OSX/AvnPanelWindow.mm @@ -0,0 +1,8 @@ +// +// Created by Dan Walmsley on 06/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#define AvnWindow AvnPanelWindow + +//#include "window.mm" \ No newline at end of file diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index fd079b1427..19065097fb 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -7,6 +7,7 @@ #define AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H #import "rendertarget.h" +#import "WindowProtocol.h" #include "INSWindowHolder.h" @class AutoFitContentView; @@ -28,7 +29,7 @@ BEGIN_INTERFACE_MAP() AutoFitContentView *StandardContainer; AvnView *View; - AvnWindow *Window; + NSWindow * Window; ComPtr BaseEvents; ComPtr _glContext; NSObject *renderTarget; @@ -116,9 +117,10 @@ protected: void UpdateStyle(); + id GetWindowProtocol (); + private: void InitialiseNSWindow (); - }; #endif //AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index 826486ae31..82c634dc4f 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -100,10 +100,6 @@ HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) { [Window orderFront:Window]; } - [Window setContentMinSize:lastMinSize]; - [Window setContentMaxSize:lastMaxSize]; - [Window setContentSize: lastSize]; - _shown = true; return S_OK; @@ -132,7 +128,8 @@ HRESULT WindowBaseImpl::Hide() { @autoreleasepool { if (Window != nullptr) { [Window orderOut:Window]; - [Window restoreParentWindow]; + + [GetWindowProtocol() restoreParentWindow]; } return S_OK; @@ -311,10 +308,10 @@ HRESULT WindowBaseImpl::SetMainMenu(IAvnMenu *menu) { auto nsmenu = nativeMenu->GetNative(); - [Window applyMenu:nsmenu]; + [GetWindowProtocol() applyMenu:nsmenu]; if ([Window isKeyWindow]) { - [Window showWindowMenuWithAppMenu]; + [GetWindowProtocol() showWindowMenuWithAppMenu]; } return S_OK; @@ -532,5 +529,12 @@ void WindowBaseImpl::InitialiseNSWindow() { [Window setContentMaxSize:lastMaxSize]; [Window setOpaque:false]; + + [Window setContentMinSize: lastMinSize]; + [Window setContentMaxSize: lastMaxSize]; } } + +id WindowBaseImpl::GetWindowProtocol() { + return static_cast>(Window); +} diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index 0d325c4490..3de4f5d5a8 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -20,7 +20,7 @@ WindowImpl::WindowImpl(IAvnWindowEvents *events, IAvnGlContext *gl) : WindowBase _lastWindowState = Normal; _actualWindowState = Normal; WindowEvents = events; - [Window setCanBecomeKeyAndMain]; + [GetWindowProtocol() setCanBecomeKeyAndMain]; [Window disableCursorRects]; [Window setTabbingMode:NSWindowTabbingModeDisallowed]; [Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; @@ -68,7 +68,7 @@ HRESULT WindowImpl::SetEnabled(bool enable) { START_COM_CALL; @autoreleasepool { - [Window setEnabled:enable]; + [GetWindowProtocol() setEnabled:enable]; return S_OK; } } @@ -354,7 +354,7 @@ HRESULT WindowImpl::SetExtendClientArea(bool enable) { View.layer.zPosition = 0; } - [Window setIsExtended:enable]; + [GetWindowProtocol() setIsExtended:enable]; HideOrShowTrafficLights(); @@ -383,7 +383,7 @@ HRESULT WindowImpl::GetExtendTitleBarHeight(double *ret) { return E_POINTER; } - *ret = [Window getExtendedTitleBarHeight]; + *ret = [GetWindowProtocol() getExtendedTitleBarHeight]; return S_OK; } diff --git a/native/Avalonia.Native/src/OSX/WindowProtocol.h b/native/Avalonia.Native/src/OSX/WindowProtocol.h new file mode 100644 index 0000000000..ac20fe8eb1 --- /dev/null +++ b/native/Avalonia.Native/src/OSX/WindowProtocol.h @@ -0,0 +1,23 @@ +// +// Created by Dan Walmsley on 06/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#import + +@class AvnMenu; + +@protocol AvnWindowProtocol +-(void) setCanBecomeKeyAndMain; +-(void) pollModalSession: (NSModalSession _Nonnull) session; +-(void) restoreParentWindow; +-(bool) shouldTryToHandleEvents; +-(void) setEnabled: (bool) enable; +-(void) showAppMenuOnly; +-(void) showWindowMenuWithAppMenu; +-(void) applyMenu:(AvnMenu* _Nullable)menu; + +-(double) getExtendedTitleBarHeight; +-(void) setIsExtended:(bool)value; +-(bool) isDialog; +@end \ No newline at end of file diff --git a/native/Avalonia.Native/src/OSX/window.h b/native/Avalonia.Native/src/OSX/window.h index a8c31e7b60..1a001dd6e4 100644 --- a/native/Avalonia.Native/src/OSX/window.h +++ b/native/Avalonia.Native/src/OSX/window.h @@ -2,25 +2,14 @@ #define window_h #import "avalonia-native.h" +#import "WindowProtocol.h" @class AvnMenu; class WindowBaseImpl; -@interface AvnWindow : NSWindow +@interface AvnWindow : NSWindow -(AvnWindow* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask; --(void) setCanBecomeKeyAndMain; --(void) pollModalSession: (NSModalSession _Nonnull) session; --(void) restoreParentWindow; --(bool) shouldTryToHandleEvents; --(void) setEnabled: (bool) enable; --(void) showAppMenuOnly; --(void) showWindowMenuWithAppMenu; --(void) applyMenu:(AvnMenu* _Nullable)menu; - --(double) getExtendedTitleBarHeight; --(void) setIsExtended:(bool)value; --(bool) isDialog; @end #endif /* window_h */ diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index 68f8f76d75..e4bfedaba4 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -1,11 +1,11 @@ #import -#include "common.h" +#import "common.h" #import "window.h" -#include "menu.h" -#include "automation.h" +#import "menu.h" +#import "automation.h" #import "WindowBaseImpl.h" -#include "WindowImpl.h" -#include "AvnView.h" +#import "WindowImpl.h" +#import "AvnView.h" @implementation AvnWindow { From cd9be07ced12909309b68a68934ba4371855a3c3 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 6 May 2022 14:30:36 +0100 Subject: [PATCH 288/416] ensure menu gets applied. --- .../Avalonia.Native/src/OSX/WindowBaseImpl.h | 1 + .../Avalonia.Native/src/OSX/WindowBaseImpl.mm | 36 ++++++++++++++----- native/Avalonia.Native/src/OSX/WindowImpl.mm | 1 - 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index 19065097fb..d52518820c 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -37,6 +37,7 @@ BEGIN_INTERFACE_MAP() NSSize lastSize; NSSize lastMinSize; NSSize lastMaxSize; + AvnMenu* lastMenu; NSString *_lastTitle; bool _shown; diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index 82c634dc4f..bd763ce2fd 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -34,6 +34,7 @@ WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl) _lastTitle = @""; Window = nullptr; + lastMenu = nullptr; } HRESULT WindowBaseImpl::ObtainNSViewHandle(void **ret) { @@ -91,6 +92,10 @@ HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) { [Window setTitle:_lastTitle]; + if(!isDialog) { + [GetWindowProtocol() setCanBecomeKeyAndMain]; + } + if (ShouldTakeFocusOnShow() && activate) { [Window orderFront:Window]; [Window makeKeyAndOrderFront:Window]; @@ -306,12 +311,14 @@ HRESULT WindowBaseImpl::SetMainMenu(IAvnMenu *menu) { auto nativeMenu = dynamic_cast(menu); - auto nsmenu = nativeMenu->GetNative(); + lastMenu = nativeMenu->GetNative(); - [GetWindowProtocol() applyMenu:nsmenu]; + if(Window != nullptr) { + [GetWindowProtocol() applyMenu:lastMenu]; - if ([Window isKeyWindow]) { - [GetWindowProtocol() showWindowMenuWithAppMenu]; + if ([Window isKeyWindow]) { + [GetWindowProtocol() showWindowMenuWithAppMenu]; + } } return S_OK; @@ -524,17 +531,30 @@ void WindowBaseImpl::InitialiseNSWindow() { [Window setStyleMask:NSWindowStyleMaskBorderless]; [Window setBackingType:NSBackingStoreBuffered]; - [Window setContentSize: lastSize]; + [Window setContentSize:lastSize]; [Window setContentMinSize:lastMinSize]; [Window setContentMaxSize:lastMaxSize]; [Window setOpaque:false]; - [Window setContentMinSize: lastMinSize]; - [Window setContentMaxSize: lastMaxSize]; + [Window setContentMinSize:lastMinSize]; + [Window setContentMaxSize:lastMaxSize]; + + if (lastMenu != nullptr) { + [GetWindowProtocol() applyMenu:lastMenu]; + + if ([Window isKeyWindow]) { + [GetWindowProtocol() showWindowMenuWithAppMenu]; + } + } } } id WindowBaseImpl::GetWindowProtocol() { - return static_cast>(Window); + id instance; + if ([Window conformsToProtocol:@protocol(AvnWindowProtocol)]) { + instance = Window; + } + + return instance; } diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index 3de4f5d5a8..2a25cc69da 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -20,7 +20,6 @@ WindowImpl::WindowImpl(IAvnWindowEvents *events, IAvnGlContext *gl) : WindowBase _lastWindowState = Normal; _actualWindowState = Normal; WindowEvents = events; - [GetWindowProtocol() setCanBecomeKeyAndMain]; [Window disableCursorRects]; [Window setTabbingMode:NSWindowTabbingModeDisallowed]; [Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; From f008e403cf1e1ef18fa1d6fba631eb3048a18059 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 6 May 2022 16:02:50 +0100 Subject: [PATCH 289/416] make it compile 2 versions is AvnWindow (NSWindow / NSPanel version) fix include mess, and pragma once. --- native/Avalonia.Native/inc/rendertarget.h | 5 + .../src/OSX/AutoFitContentView.h | 4 +- .../src/OSX/AutoFitContentView.mm | 3 +- .../project.pbxproj | 26 ++- .../Avalonia.Native/src/OSX/AvnPanelWindow.h | 9 - .../Avalonia.Native/src/OSX/AvnPanelWindow.mm | 7 +- native/Avalonia.Native/src/OSX/AvnView.h | 10 +- native/Avalonia.Native/src/OSX/AvnView.mm | 3 +- .../src/OSX/{window.mm => AvnWindow.mm} | 189 +++++++----------- native/Avalonia.Native/src/OSX/PopupImpl.h | 9 + native/Avalonia.Native/src/OSX/PopupImpl.mm | 68 +++++++ native/Avalonia.Native/src/OSX/ResizeScope.h | 1 - native/Avalonia.Native/src/OSX/ResizeScope.mm | 2 +- .../Avalonia.Native/src/OSX/SystemDialogs.mm | 1 - .../Avalonia.Native/src/OSX/WindowBaseImpl.h | 5 +- .../Avalonia.Native/src/OSX/WindowBaseImpl.mm | 19 +- native/Avalonia.Native/src/OSX/WindowImpl.h | 1 - native/Avalonia.Native/src/OSX/WindowImpl.mm | 6 +- .../src/OSX/WindowInterfaces.h | 17 ++ .../Avalonia.Native/src/OSX/WindowProtocol.h | 2 + native/Avalonia.Native/src/OSX/automation.h | 3 +- native/Avalonia.Native/src/OSX/automation.mm | 7 +- native/Avalonia.Native/src/OSX/main.mm | 1 - native/Avalonia.Native/src/OSX/menu.mm | 1 - native/Avalonia.Native/src/OSX/window.h | 15 -- src/Avalonia.Native/avn.idl | 1 + 26 files changed, 235 insertions(+), 180 deletions(-) delete mode 100644 native/Avalonia.Native/src/OSX/AvnPanelWindow.h rename native/Avalonia.Native/src/OSX/{window.mm => AvnWindow.mm} (79%) create mode 100644 native/Avalonia.Native/src/OSX/PopupImpl.h create mode 100644 native/Avalonia.Native/src/OSX/PopupImpl.mm create mode 100644 native/Avalonia.Native/src/OSX/WindowInterfaces.h delete mode 100644 native/Avalonia.Native/src/OSX/window.h diff --git a/native/Avalonia.Native/inc/rendertarget.h b/native/Avalonia.Native/inc/rendertarget.h index 2b0338d099..a59915777f 100644 --- a/native/Avalonia.Native/inc/rendertarget.h +++ b/native/Avalonia.Native/inc/rendertarget.h @@ -1,3 +1,8 @@ +#pragma once + +#include "com.h" +#include "comimpl.h" +#include "avalonia-native.h" @protocol IRenderTarget -(void) setNewLayer: (CALayer*) layer; diff --git a/native/Avalonia.Native/src/OSX/AutoFitContentView.h b/native/Avalonia.Native/src/OSX/AutoFitContentView.h index 68c9fb8dc8..7f1f764141 100644 --- a/native/Avalonia.Native/src/OSX/AutoFitContentView.h +++ b/native/Avalonia.Native/src/OSX/AutoFitContentView.h @@ -3,8 +3,10 @@ // Copyright (c) 2022 Avalonia. All rights reserved. // +#pragma once + #import -#import "avalonia-native.h" +#include "avalonia-native.h" @interface AutoFitContentView : NSView -(AutoFitContentView* _Nonnull) initWithContent: (NSView* _Nonnull) content; diff --git a/native/Avalonia.Native/src/OSX/AutoFitContentView.mm b/native/Avalonia.Native/src/OSX/AutoFitContentView.mm index 92d6f67a91..4eaa08cbe2 100644 --- a/native/Avalonia.Native/src/OSX/AutoFitContentView.mm +++ b/native/Avalonia.Native/src/OSX/AutoFitContentView.mm @@ -4,7 +4,8 @@ // #include "AvnView.h" -#import "AutoFitContentView.h" +#include "AutoFitContentView.h" +#import "WindowInterfaces.h" @implementation AutoFitContentView { diff --git a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj index 8ee2a61741..6fc3977d4e 100644 --- a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj +++ b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj @@ -9,16 +9,19 @@ /* Begin PBXBuildFile section */ 18391068E48EF96E3DB5FDAB /* ResizeScope.mm in Sources */ = {isa = PBXBuildFile; fileRef = 18391E45702740FE9DD69695 /* ResizeScope.mm */; }; 1839125F057B0A4EB1760058 /* WindowImpl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 183919BF108EB72A029F7671 /* WindowImpl.mm */; }; + 183914E50CF6D2EFC1667F7C /* WindowInterfaces.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391DB45C7D892E61BF388C /* WindowInterfaces.h */; }; 1839151F32D1BB1AB51A7BB6 /* AvnPanelWindow.mm in Sources */ = {isa = PBXBuildFile; fileRef = 18391884C7476DA4E53A492D /* AvnPanelWindow.mm */; }; 183916173528EC2737DBE5E1 /* WindowBaseImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 183915BFF0E234CD3604A7CD /* WindowBaseImpl.h */; }; 1839171DCC651B0638603AC4 /* INSWindowHolder.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391BBB7782C296D424071F /* INSWindowHolder.h */; }; 1839179A55FC1421BEE83330 /* WindowBaseImpl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 18391676ECF0E983F4964357 /* WindowBaseImpl.mm */; }; - 1839196EC57BBC5C8BE1C735 /* AvnPanelWindow.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391DC046BC700D56DF0B82 /* AvnPanelWindow.h */; }; 183919D91DB9AAB5D700C2EA /* WindowImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391CD090AA776E7E841AC9 /* WindowImpl.h */; }; 18391AA7E0BBA74D184C5734 /* AutoFitContentView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1839166350F32661F3ABD70F /* AutoFitContentView.mm */; }; + 18391AC16726CBC45856233B /* AvnWindow.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1839155B28B20FFB672D29C6 /* AvnWindow.mm */; }; + 18391AC65ADD7DDD33FBE737 /* PopupImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = 183910513F396141938832B5 /* PopupImpl.h */; }; 18391C28BF1823B5464FDD36 /* ResizeScope.h in Headers */ = {isa = PBXBuildFile; fileRef = 1839171D898F9BFC1373631A /* ResizeScope.h */; }; 18391CF07316F819E76B617C /* IWindowStateChanged.h in Headers */ = {isa = PBXBuildFile; fileRef = 183913C6BFD6856BD42D19FD /* IWindowStateChanged.h */; }; 18391D4EB311BC7EF8B8C0A6 /* AvnView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1839132D0E2454D911F1D1F9 /* AvnView.mm */; }; + 18391D8CD1756DC858DC1A09 /* PopupImpl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 18391BB698579F40F1783F31 /* PopupImpl.mm */; }; 18391E1381E2D5BFD60265A9 /* AutoFitContentView.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391654EF0E7AB3D3AB4071 /* AutoFitContentView.h */; }; 18391ED5F611FF62C45F196D /* AvnView.h in Headers */ = {isa = PBXBuildFile; fileRef = 18391D1669284AD2EC9E866A /* AvnView.h */; }; 18391F1E2411C79405A9943A /* WindowProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 1839122E037567BDD1D09DEB /* WindowProtocol.h */; }; @@ -43,16 +46,17 @@ AB00E4F72147CA920032A60A /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB00E4F62147CA920032A60A /* main.mm */; }; AB1E522C217613570091CD71 /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB1E522B217613570091CD71 /* OpenGL.framework */; }; AB661C1E2148230F00291242 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB661C1D2148230F00291242 /* AppKit.framework */; }; - AB661C202148286E00291242 /* window.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB661C1F2148286E00291242 /* window.mm */; }; AB8F7D6B21482D7F0057DBA5 /* platformthreading.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB8F7D6A21482D7F0057DBA5 /* platformthreading.mm */; }; BC11A5BE2608D58F0017BAD0 /* automation.h in Headers */ = {isa = PBXBuildFile; fileRef = BC11A5BC2608D58F0017BAD0 /* automation.h */; }; BC11A5BF2608D58F0017BAD0 /* automation.mm in Sources */ = {isa = PBXBuildFile; fileRef = BC11A5BD2608D58F0017BAD0 /* automation.mm */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 183910513F396141938832B5 /* PopupImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PopupImpl.h; sourceTree = ""; }; 1839122E037567BDD1D09DEB /* WindowProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowProtocol.h; sourceTree = ""; }; 1839132D0E2454D911F1D1F9 /* AvnView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AvnView.mm; sourceTree = ""; }; 183913C6BFD6856BD42D19FD /* IWindowStateChanged.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IWindowStateChanged.h; sourceTree = ""; }; + 1839155B28B20FFB672D29C6 /* AvnWindow.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AvnWindow.mm; sourceTree = ""; }; 183915BFF0E234CD3604A7CD /* WindowBaseImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowBaseImpl.h; sourceTree = ""; }; 18391654EF0E7AB3D3AB4071 /* AutoFitContentView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AutoFitContentView.h; sourceTree = ""; }; 1839166350F32661F3ABD70F /* AutoFitContentView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AutoFitContentView.mm; sourceTree = ""; }; @@ -60,10 +64,11 @@ 1839171D898F9BFC1373631A /* ResizeScope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ResizeScope.h; sourceTree = ""; }; 18391884C7476DA4E53A492D /* AvnPanelWindow.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AvnPanelWindow.mm; sourceTree = ""; }; 183919BF108EB72A029F7671 /* WindowImpl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WindowImpl.mm; sourceTree = ""; }; + 18391BB698579F40F1783F31 /* PopupImpl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PopupImpl.mm; sourceTree = ""; }; 18391BBB7782C296D424071F /* INSWindowHolder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = INSWindowHolder.h; sourceTree = ""; }; 18391CD090AA776E7E841AC9 /* WindowImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowImpl.h; sourceTree = ""; }; 18391D1669284AD2EC9E866A /* AvnView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AvnView.h; sourceTree = ""; }; - 18391DC046BC700D56DF0B82 /* AvnPanelWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AvnPanelWindow.h; sourceTree = ""; }; + 18391DB45C7D892E61BF388C /* WindowInterfaces.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowInterfaces.h; sourceTree = ""; }; 18391E45702740FE9DD69695 /* ResizeScope.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ResizeScope.mm; sourceTree = ""; }; 1A002B9D232135EE00021753 /* app.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = app.mm; sourceTree = ""; }; 1A1852DB23E05814008F0DED /* deadlock.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = deadlock.mm; sourceTree = ""; }; @@ -78,7 +83,6 @@ 37A4E71A2178846A00EACBCD /* headers */ = {isa = PBXFileReference; lastKnownFileType = folder; name = headers; path = ../../inc; sourceTree = ""; }; 37A517B22159597E00FBA241 /* Screens.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = Screens.mm; sourceTree = ""; }; 37C09D8721580FE4006A6758 /* SystemDialogs.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SystemDialogs.mm; sourceTree = ""; }; - 37C09D8A21581EF2006A6758 /* window.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = window.h; sourceTree = ""; }; 37DDA9AF219330F8002E132B /* AvnString.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = AvnString.mm; sourceTree = ""; }; 37DDA9B121933371002E132B /* AvnString.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AvnString.h; sourceTree = ""; }; 37E2330E21583241000CB7E2 /* KeyTransform.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = KeyTransform.mm; sourceTree = ""; }; @@ -92,7 +96,6 @@ AB00E4F62147CA920032A60A /* main.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = ""; }; AB1E522B217613570091CD71 /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = System/Library/Frameworks/OpenGL.framework; sourceTree = SDKROOT; }; AB661C1D2148230F00291242 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; - AB661C1F2148286E00291242 /* window.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = window.mm; sourceTree = ""; }; AB661C212148288600291242 /* common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = common.h; sourceTree = ""; }; AB7A61EF2147C815003C5833 /* libAvalonia.Native.OSX.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libAvalonia.Native.OSX.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; AB8F7D6A21482D7F0057DBA5 /* platformthreading.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = platformthreading.mm; sourceTree = ""; }; @@ -148,8 +151,6 @@ AB661C212148288600291242 /* common.h */, 379860FE214DA0C000CD0246 /* KeyTransform.h */, 37E2330E21583241000CB7E2 /* KeyTransform.mm */, - AB661C1F2148286E00291242 /* window.mm */, - 37C09D8A21581EF2006A6758 /* window.h */, AB00E4F62147CA920032A60A /* main.mm */, 37155CE3233C00EB0034DCE9 /* menu.h */, 520624B222973F4100C4DCEF /* menu.mm */, @@ -173,8 +174,11 @@ 1839166350F32661F3ABD70F /* AutoFitContentView.mm */, 18391654EF0E7AB3D3AB4071 /* AutoFitContentView.h */, 18391884C7476DA4E53A492D /* AvnPanelWindow.mm */, - 18391DC046BC700D56DF0B82 /* AvnPanelWindow.h */, 1839122E037567BDD1D09DEB /* WindowProtocol.h */, + 1839155B28B20FFB672D29C6 /* AvnWindow.mm */, + 18391DB45C7D892E61BF388C /* WindowInterfaces.h */, + 18391BB698579F40F1783F31 /* PopupImpl.mm */, + 183910513F396141938832B5 /* PopupImpl.h */, ); sourceTree = ""; }; @@ -202,8 +206,9 @@ 18391C28BF1823B5464FDD36 /* ResizeScope.h in Headers */, 18391ED5F611FF62C45F196D /* AvnView.h in Headers */, 18391E1381E2D5BFD60265A9 /* AutoFitContentView.h in Headers */, - 1839196EC57BBC5C8BE1C735 /* AvnPanelWindow.h in Headers */, 18391F1E2411C79405A9943A /* WindowProtocol.h in Headers */, + 183914E50CF6D2EFC1667F7C /* WindowInterfaces.h in Headers */, + 18391AC65ADD7DDD33FBE737 /* PopupImpl.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -282,13 +287,14 @@ 1A465D10246AB61600C5858B /* dnd.mm in Sources */, AB00E4F72147CA920032A60A /* main.mm in Sources */, 37C09D8821580FE4006A6758 /* SystemDialogs.mm in Sources */, - AB661C202148286E00291242 /* window.mm in Sources */, 1839179A55FC1421BEE83330 /* WindowBaseImpl.mm in Sources */, 1839125F057B0A4EB1760058 /* WindowImpl.mm in Sources */, 18391068E48EF96E3DB5FDAB /* ResizeScope.mm in Sources */, 18391D4EB311BC7EF8B8C0A6 /* AvnView.mm in Sources */, 18391AA7E0BBA74D184C5734 /* AutoFitContentView.mm in Sources */, 1839151F32D1BB1AB51A7BB6 /* AvnPanelWindow.mm in Sources */, + 18391AC16726CBC45856233B /* AvnWindow.mm in Sources */, + 18391D8CD1756DC858DC1A09 /* PopupImpl.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/native/Avalonia.Native/src/OSX/AvnPanelWindow.h b/native/Avalonia.Native/src/OSX/AvnPanelWindow.h deleted file mode 100644 index 0fe95d044d..0000000000 --- a/native/Avalonia.Native/src/OSX/AvnPanelWindow.h +++ /dev/null @@ -1,9 +0,0 @@ -// -// Created by Dan Walmsley on 06/05/2022. -// Copyright (c) 2022 Avalonia. All rights reserved. -// - -#define NSWindow NSPanel -#define AvnWindow AvnPanelWindow - -//#include "window.h" \ No newline at end of file diff --git a/native/Avalonia.Native/src/OSX/AvnPanelWindow.mm b/native/Avalonia.Native/src/OSX/AvnPanelWindow.mm index 8b79e28ca1..2365189010 100644 --- a/native/Avalonia.Native/src/OSX/AvnPanelWindow.mm +++ b/native/Avalonia.Native/src/OSX/AvnPanelWindow.mm @@ -3,6 +3,9 @@ // Copyright (c) 2022 Avalonia. All rights reserved. // -#define AvnWindow AvnPanelWindow +#pragma once + +#define IS_NSPANEL + +#include "AvnWindow.mm" -//#include "window.mm" \ No newline at end of file diff --git a/native/Avalonia.Native/src/OSX/AvnView.h b/native/Avalonia.Native/src/OSX/AvnView.h index c6dd90150f..86a68d34c5 100644 --- a/native/Avalonia.Native/src/OSX/AvnView.h +++ b/native/Avalonia.Native/src/OSX/AvnView.h @@ -2,17 +2,15 @@ // Created by Dan Walmsley on 05/05/2022. // Copyright (c) 2022 Avalonia. All rights reserved. // - +#pragma once #import #import #import -#include "window.h" -#import "comimpl.h" -#import "common.h" -#import "WindowImpl.h" -#import "KeyTransform.h" +#include "common.h" +#include "WindowImpl.h" +#include "KeyTransform.h" @class AvnAccessibilityElement; diff --git a/native/Avalonia.Native/src/OSX/AvnView.mm b/native/Avalonia.Native/src/OSX/AvnView.mm index 24cbc25502..e6cf73755b 100644 --- a/native/Avalonia.Native/src/OSX/AvnView.mm +++ b/native/Avalonia.Native/src/OSX/AvnView.mm @@ -4,8 +4,9 @@ // #import -#import "AvnView.h" +#include "AvnView.h" #include "automation.h" +#import "WindowInterfaces.h" @implementation AvnView { diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/AvnWindow.mm similarity index 79% rename from native/Avalonia.Native/src/OSX/window.mm rename to native/Avalonia.Native/src/OSX/AvnWindow.mm index e4bfedaba4..09534a0a4b 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/AvnWindow.mm @@ -1,13 +1,32 @@ +// +// Created by Dan Walmsley on 06/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + + #import -#import "common.h" -#import "window.h" -#import "menu.h" -#import "automation.h" +#import "WindowProtocol.h" #import "WindowBaseImpl.h" -#import "WindowImpl.h" -#import "AvnView.h" -@implementation AvnWindow +#ifdef IS_NSPANEL +#define BASE_CLASS NSPanel +#define CLASS_NAME AvnPanel +#else +#define BASE_CLASS NSWindow +#define CLASS_NAME AvnWindow +#endif + +#import +#include "common.h" +#include "menu.h" +#include "automation.h" +#include "WindowBaseImpl.h" +#include "WindowImpl.h" +#include "AvnView.h" +#include "WindowInterfaces.h" +#include "PopupImpl.h" + +@implementation CLASS_NAME { ComPtr _parent; bool _canBecomeKeyAndMain; @@ -66,7 +85,7 @@ - (void)pollModalSession:(nonnull NSModalSession)session { auto response = [NSApp runModalSession:session]; - + if(response == NSModalResponseContinue) { dispatch_async(dispatch_get_main_queue(), ^{ @@ -85,18 +104,18 @@ if(_menu != nullptr) { auto appMenuItem = ::GetAppMenuItem(); - + if(appMenuItem != nullptr) { auto appMenu = [appMenuItem menu]; - + [appMenu removeItem:appMenuItem]; - + [_menu insertItem:appMenuItem atIndex:0]; - + [_menu setHasGlobalMenuItem:true]; } - + [NSApp setMenu:_menu]; } else @@ -108,22 +127,22 @@ -(void) showAppMenuOnly { auto appMenuItem = ::GetAppMenuItem(); - + if(appMenuItem != nullptr) { auto appMenu = ::GetAppMenu(); - + auto nativeAppMenu = dynamic_cast(appMenu); - + [[appMenuItem menu] removeItem:appMenuItem]; - + if(_menu != nullptr) { [_menu setHasGlobalMenuItem:false]; } - + [nativeAppMenu->GetNative() addItem:appMenuItem]; - + [NSApp setMenu:nativeAppMenu->GetNative()]; } } @@ -134,7 +153,7 @@ { menu = [AvnMenu new]; } - + _menu = menu; } @@ -143,19 +162,19 @@ _canBecomeKeyAndMain = true; } --(AvnWindow*) initWithParent: (WindowBaseImpl*) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask; +-(CLASS_NAME*) initWithParent: (WindowBaseImpl*) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask; { // https://jameshfisher.com/2020/07/10/why-is-the-contentrect-of-my-nswindow-ignored/ // create nswindow with specific contentRect, otherwise we wont be able to resize the window // until several ms after the window is physically on the screen. self = [super initWithContentRect:contentRect styleMask: styleMask backing:NSBackingStoreBuffered defer:false]; - + [self setReleasedWhenClosed:false]; _parent = parent; [self setDelegate:self]; _closed = false; _isEnabled = true; - + [self backingScaleFactor]; [self setOpaque:NO]; [self setBackgroundColor: [NSColor clearColor]]; @@ -167,12 +186,12 @@ - (BOOL)windowShouldClose:(NSWindow *)sender { auto window = dynamic_cast(_parent.getRaw()); - + if(window != nullptr) { return !window->WindowEvents->Closing(); } - + return true; } @@ -201,16 +220,17 @@ // If the window has a child window being shown as a dialog then don't allow it to become the key window. for(NSWindow* uch in [self childWindows]) { - auto ch = objc_cast(uch); + // TODO protocol + auto ch = objc_cast(uch); if(ch == nil) continue; if (ch.isDialog) return false; } - + return true; } - + return false; } @@ -232,7 +252,7 @@ -(void)becomeKeyWindow { [self showWindowMenuWithAppMenu]; - + if(_parent != nullptr) { _parent->BaseEvents->Activated(); @@ -243,7 +263,8 @@ -(void) restoreParentWindow; { - auto parent = objc_cast([self parentWindow]); + // TODO protocol + auto parent = objc_cast([self parentWindow]); if(parent != nil) { [parent removeChildWindow:self]; @@ -253,7 +274,7 @@ - (void)windowDidMiniaturize:(NSNotification *)notification { auto parent = dynamic_cast(_parent.operator->()); - + if(parent != nullptr) { parent->WindowStateChanged(); @@ -263,7 +284,7 @@ - (void)windowDidDeminiaturize:(NSNotification *)notification { auto parent = dynamic_cast(_parent.operator->()); - + if(parent != nullptr) { parent->WindowStateChanged(); @@ -273,7 +294,7 @@ - (void)windowDidResize:(NSNotification *)notification { auto parent = dynamic_cast(_parent.operator->()); - + if(parent != nullptr) { parent->WindowStateChanged(); @@ -283,7 +304,7 @@ - (void)windowWillExitFullScreen:(NSNotification *)notification { auto parent = dynamic_cast(_parent.operator->()); - + if(parent != nullptr) { parent->StartStateTransition(); @@ -293,22 +314,22 @@ - (void)windowDidExitFullScreen:(NSNotification *)notification { auto parent = dynamic_cast(_parent.operator->()); - + if(parent != nullptr) { parent->EndStateTransition(); - + if(parent->Decorations() != SystemDecorationsFull && parent->WindowState() == Maximized) { NSRect screenRect = [[self screen] visibleFrame]; [self setFrame:screenRect display:YES]; } - + if(parent->WindowState() == Minimized) { [self miniaturize:nullptr]; } - + parent->WindowStateChanged(); } } @@ -316,7 +337,7 @@ - (void)windowWillEnterFullScreen:(NSNotification *)notification { auto parent = dynamic_cast(_parent.operator->()); - + if(parent != nullptr) { parent->StartStateTransition(); @@ -326,7 +347,7 @@ - (void)windowDidEnterFullScreen:(NSNotification *)notification { auto parent = dynamic_cast(_parent.operator->()); - + if(parent != nullptr) { parent->EndStateTransition(); @@ -343,20 +364,20 @@ { if(_parent) _parent->BaseEvents->Deactivated(); - + [self showAppMenuOnly]; - + [super resignKeyWindow]; } - (void)windowDidMove:(NSNotification *)notification { AvnPoint position; - + if(_parent != nullptr) { auto cparent = dynamic_cast(_parent.getRaw()); - + if(cparent != nullptr) { if(cparent->WindowState() == Maximized) @@ -364,7 +385,7 @@ cparent->SetWindowState(Normal); } } - + _parent->GetPosition(&position); _parent->BaseEvents->PositionChanged(position); } @@ -379,7 +400,7 @@ - (void)sendEvent:(NSEvent *)event { [super sendEvent:event]; - + /// This is to detect non-client clicks. This can only be done on Windows... not popups, hence the dynamic_cast. if(_parent != nullptr && dynamic_cast(_parent.getRaw()) != nullptr) { @@ -390,30 +411,30 @@ AvnView* view = _parent->View; NSPoint windowPoint = [event locationInWindow]; NSPoint viewPoint = [view convertPoint:windowPoint fromView:nil]; - + if (!NSPointInRect(viewPoint, view.bounds)) { auto avnPoint = [AvnView toAvnPoint:windowPoint]; auto point = [self translateLocalPoint:avnPoint]; AvnVector delta = { 0, 0 }; - + _parent->BaseEvents->RawMouseEvent(NonClientLeftButtonDown, static_cast([event timestamp] * 1000), AvnInputModifiersNone, point, delta); } } - break; - + break; + case NSEventTypeMouseEntered: { _parent->UpdateCursor(); } - break; - + break; + case NSEventTypeMouseExited: { [[NSCursor arrowCursor] set]; } - break; - + break; + default: break; } @@ -422,63 +443,3 @@ @end -class PopupImpl : public virtual WindowBaseImpl, public IAvnPopup -{ -private: - BEGIN_INTERFACE_MAP() - INHERIT_INTERFACE_MAP(WindowBaseImpl) - INTERFACE_MAP_ENTRY(IAvnPopup, IID_IAvnPopup) - END_INTERFACE_MAP() - virtual ~PopupImpl(){} - ComPtr WindowEvents; - PopupImpl(IAvnWindowEvents* events, IAvnGlContext* gl) : WindowBaseImpl(events, gl) - { - WindowEvents = events; - [Window setLevel:NSPopUpMenuWindowLevel]; - } -protected: - virtual NSWindowStyleMask GetStyle() override - { - return NSWindowStyleMaskBorderless; - } - - virtual HRESULT Resize(double x, double y, AvnPlatformResizeReason reason) override - { - START_COM_CALL; - - @autoreleasepool - { - if (Window != nullptr) - { - [Window setContentSize:NSSize{x, y}]; - - [Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(lastPositionSet))]; - } - - return S_OK; - } - } -public: - virtual bool ShouldTakeFocusOnShow() override - { - return false; - } -}; - -extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events, IAvnGlContext* gl) -{ - @autoreleasepool - { - IAvnPopup* ptr = dynamic_cast(new PopupImpl(events, gl)); - return ptr; - } -} - -extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events, IAvnGlContext* gl) -{ - @autoreleasepool - { - IAvnWindow* ptr = (IAvnWindow*)new WindowImpl(events, gl); - return ptr; - } -} diff --git a/native/Avalonia.Native/src/OSX/PopupImpl.h b/native/Avalonia.Native/src/OSX/PopupImpl.h new file mode 100644 index 0000000000..451019a6a4 --- /dev/null +++ b/native/Avalonia.Native/src/OSX/PopupImpl.h @@ -0,0 +1,9 @@ +// +// Created by Dan Walmsley on 06/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#ifndef AVALONIA_NATIVE_OSX_POPUPIMPL_H +#define AVALONIA_NATIVE_OSX_POPUPIMPL_H + +#endif //AVALONIA_NATIVE_OSX_POPUPIMPL_H diff --git a/native/Avalonia.Native/src/OSX/PopupImpl.mm b/native/Avalonia.Native/src/OSX/PopupImpl.mm new file mode 100644 index 0000000000..64a8780158 --- /dev/null +++ b/native/Avalonia.Native/src/OSX/PopupImpl.mm @@ -0,0 +1,68 @@ +// +// Created by Dan Walmsley on 06/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#include "WindowInterfaces.h" +#include "AvnView.h" +#include "WindowImpl.h" +#include "automation.h" +#include "menu.h" +#include "common.h" +#import "WindowBaseImpl.h" +#import "WindowProtocol.h" +#import +#include "PopupImpl.h" + +class PopupImpl : public virtual WindowBaseImpl, public IAvnPopup +{ +private: + BEGIN_INTERFACE_MAP() + INHERIT_INTERFACE_MAP(WindowBaseImpl) + INTERFACE_MAP_ENTRY(IAvnPopup, IID_IAvnPopup) + END_INTERFACE_MAP() + virtual ~PopupImpl(){} + ComPtr WindowEvents; + PopupImpl(IAvnWindowEvents* events, IAvnGlContext* gl) : WindowBaseImpl(events, gl) + { + WindowEvents = events; + [Window setLevel:NSPopUpMenuWindowLevel]; + } +protected: + virtual NSWindowStyleMask GetStyle() override + { + return NSWindowStyleMaskBorderless; + } + + virtual HRESULT Resize(double x, double y, AvnPlatformResizeReason reason) override + { + START_COM_CALL; + + @autoreleasepool + { + if (Window != nullptr) + { + [Window setContentSize:NSSize{x, y}]; + + [Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(lastPositionSet))]; + } + + return S_OK; + } + } +public: + virtual bool ShouldTakeFocusOnShow() override + { + return false; + } +}; + + +extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events, IAvnGlContext* gl) +{ + @autoreleasepool + { + IAvnPopup* ptr = dynamic_cast(new PopupImpl(events, gl)); + return ptr; + } +} \ No newline at end of file diff --git a/native/Avalonia.Native/src/OSX/ResizeScope.h b/native/Avalonia.Native/src/OSX/ResizeScope.h index 7509f93c01..9a43c158fe 100644 --- a/native/Avalonia.Native/src/OSX/ResizeScope.h +++ b/native/Avalonia.Native/src/OSX/ResizeScope.h @@ -6,7 +6,6 @@ #ifndef AVALONIA_NATIVE_OSX_RESIZESCOPE_H #define AVALONIA_NATIVE_OSX_RESIZESCOPE_H -#include "window.h" #include "avalonia-native.h" @class AvnView; diff --git a/native/Avalonia.Native/src/OSX/ResizeScope.mm b/native/Avalonia.Native/src/OSX/ResizeScope.mm index 90e7f5cf15..9f1177af8b 100644 --- a/native/Avalonia.Native/src/OSX/ResizeScope.mm +++ b/native/Avalonia.Native/src/OSX/ResizeScope.mm @@ -5,7 +5,7 @@ #import #include "ResizeScope.h" -#import "AvnView.h" +#include "AvnView.h" ResizeScope::ResizeScope(AvnView *view, AvnPlatformResizeReason reason) { _view = view; diff --git a/native/Avalonia.Native/src/OSX/SystemDialogs.mm b/native/Avalonia.Native/src/OSX/SystemDialogs.mm index 21ad9cfa7c..535b6c3b66 100644 --- a/native/Avalonia.Native/src/OSX/SystemDialogs.mm +++ b/native/Avalonia.Native/src/OSX/SystemDialogs.mm @@ -1,5 +1,4 @@ #include "common.h" -#include "window.h" #include "INSWindowHolder.h" class SystemDialogs : public ComSingleObject diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index d52518820c..a8f549b3c6 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -6,11 +6,12 @@ #ifndef AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H #define AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H -#import "rendertarget.h" -#import "WindowProtocol.h" +#include "rendertarget.h" #include "INSWindowHolder.h" @class AutoFitContentView; +@class AvnMenu; +@protocol AvnWindowProtocol; class WindowBaseImpl : public virtual ComObject, public virtual IAvnWindowBase, diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index bd763ce2fd..a58d0bb8be 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -5,14 +5,14 @@ #import #include "common.h" -#import "window.h" -#import "AvnView.h" +#include "AvnView.h" #include "menu.h" #include "automation.h" -#import "cursor.h" +#include "cursor.h" #include "ResizeScope.h" -#import "AutoFitContentView.h" -#include "WindowBaseImpl.h" +#include "AutoFitContentView.h" +#import "WindowProtocol.h" +#import "WindowInterfaces.h" WindowBaseImpl::~WindowBaseImpl() { @@ -558,3 +558,12 @@ id WindowBaseImpl::GetWindowProtocol() { return instance; } + +extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events, IAvnGlContext* gl) +{ + @autoreleasepool + { + IAvnWindow* ptr = (IAvnWindow*)new WindowImpl(events, gl); + return ptr; + } +} diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.h b/native/Avalonia.Native/src/OSX/WindowImpl.h index b229921baa..a4ee4f447c 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowImpl.h @@ -6,7 +6,6 @@ #ifndef AVALONIA_NATIVE_OSX_WINDOWIMPL_H #define AVALONIA_NATIVE_OSX_WINDOWIMPL_H - #import "WindowBaseImpl.h" #include "IWindowStateChanged.h" diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index 2a25cc69da..9992d64b47 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -4,10 +4,10 @@ // #import -#import "window.h" -#import "AutoFitContentView.h" -#import "AvnView.h" +#include "AutoFitContentView.h" +#include "AvnView.h" #include "automation.h" +#include "WindowProtocol.h" WindowImpl::WindowImpl(IAvnWindowEvents *events, IAvnGlContext *gl) : WindowBaseImpl(events, gl) { _isClientAreaExtended = false; diff --git a/native/Avalonia.Native/src/OSX/WindowInterfaces.h b/native/Avalonia.Native/src/OSX/WindowInterfaces.h new file mode 100644 index 0000000000..6e6d62e85e --- /dev/null +++ b/native/Avalonia.Native/src/OSX/WindowInterfaces.h @@ -0,0 +1,17 @@ +// +// Created by Dan Walmsley on 06/05/2022. +// Copyright (c) 2022 Avalonia. All rights reserved. +// + +#import +#import +#include "WindowProtocol.h" +#include "WindowBaseImpl.h" + +@interface AvnWindow : NSWindow +-(AvnWindow* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask; +@end + +@interface AvnPanel : NSPanel +-(AvnPanel* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask; +@end \ No newline at end of file diff --git a/native/Avalonia.Native/src/OSX/WindowProtocol.h b/native/Avalonia.Native/src/OSX/WindowProtocol.h index ac20fe8eb1..d81d2f1ed1 100644 --- a/native/Avalonia.Native/src/OSX/WindowProtocol.h +++ b/native/Avalonia.Native/src/OSX/WindowProtocol.h @@ -3,6 +3,8 @@ // Copyright (c) 2022 Avalonia. All rights reserved. // +#pragma once + #import @class AvnMenu; diff --git a/native/Avalonia.Native/src/OSX/automation.h b/native/Avalonia.Native/src/OSX/automation.h index 1727d21f27..367df3619d 100644 --- a/native/Avalonia.Native/src/OSX/automation.h +++ b/native/Avalonia.Native/src/OSX/automation.h @@ -1,5 +1,6 @@ -#import +#pragma once +#import NS_ASSUME_NONNULL_BEGIN class IAvnAutomationPeer; diff --git a/native/Avalonia.Native/src/OSX/automation.mm b/native/Avalonia.Native/src/OSX/automation.mm index 7a0b3b8127..d0c8d7a9db 100644 --- a/native/Avalonia.Native/src/OSX/automation.mm +++ b/native/Avalonia.Native/src/OSX/automation.mm @@ -1,9 +1,8 @@ #include "common.h" -#import "automation.h" -#import "window.h" +#include "automation.h" #include "AvnString.h" -#import "INSWindowHolder.h" -#import "AvnView.h" +#include "INSWindowHolder.h" +#include "AvnView.h" @interface AvnAccessibilityElement (Events) - (void) raiseChildrenChanged; diff --git a/native/Avalonia.Native/src/OSX/main.mm b/native/Avalonia.Native/src/OSX/main.mm index 011f881e94..6ee86b21ae 100644 --- a/native/Avalonia.Native/src/OSX/main.mm +++ b/native/Avalonia.Native/src/OSX/main.mm @@ -1,7 +1,6 @@ //This file will contain actual IID structures #define COM_GUIDS_MATERIALIZE #include "common.h" -#include "window.h" static NSString* s_appTitle = @"Avalonia"; diff --git a/native/Avalonia.Native/src/OSX/menu.mm b/native/Avalonia.Native/src/OSX/menu.mm index fd74edd772..b05588a441 100644 --- a/native/Avalonia.Native/src/OSX/menu.mm +++ b/native/Avalonia.Native/src/OSX/menu.mm @@ -1,7 +1,6 @@ #include "common.h" #include "menu.h" -#import "window.h" #include "KeyTransform.h" #include #include /* For kVK_ constants, and TIS functions. */ diff --git a/native/Avalonia.Native/src/OSX/window.h b/native/Avalonia.Native/src/OSX/window.h deleted file mode 100644 index 1a001dd6e4..0000000000 --- a/native/Avalonia.Native/src/OSX/window.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef window_h -#define window_h - -#import "avalonia-native.h" -#import "WindowProtocol.h" - -@class AvnMenu; - -class WindowBaseImpl; - -@interface AvnWindow : NSWindow --(AvnWindow* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask; -@end - -#endif /* window_h */ diff --git a/src/Avalonia.Native/avn.idl b/src/Avalonia.Native/avn.idl index 8092004989..d6ef0f8918 100644 --- a/src/Avalonia.Native/avn.idl +++ b/src/Avalonia.Native/avn.idl @@ -2,6 +2,7 @@ @clr-access internal @clr-map bool int @cpp-preamble @@ +#pragma once #include "com.h" #include "stddef.h" @@ From 1cca34f56ede6b1c4b2169b64a74a9832331e536 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 6 May 2022 17:30:03 +0100 Subject: [PATCH 290/416] actually create nspanels for dialogs. --- native/Avalonia.Native/src/OSX/AvnWindow.mm | 35 ++++++++----------- .../Avalonia.Native/src/OSX/WindowBaseImpl.h | 1 + .../Avalonia.Native/src/OSX/WindowBaseImpl.mm | 28 ++++++++++----- native/Avalonia.Native/src/OSX/WindowImpl.mm | 2 +- .../Avalonia.Native/src/OSX/WindowProtocol.h | 1 - 5 files changed, 35 insertions(+), 32 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/AvnWindow.mm b/native/Avalonia.Native/src/OSX/AvnWindow.mm index 09534a0a4b..d0b23540f9 100644 --- a/native/Avalonia.Native/src/OSX/AvnWindow.mm +++ b/native/Avalonia.Native/src/OSX/AvnWindow.mm @@ -29,7 +29,6 @@ @implementation CLASS_NAME { ComPtr _parent; - bool _canBecomeKeyAndMain; bool _closed; bool _isEnabled; bool _isExtended; @@ -157,11 +156,6 @@ _menu = menu; } --(void) setCanBecomeKeyAndMain -{ - _canBecomeKeyAndMain = true; -} - -(CLASS_NAME*) initWithParent: (WindowBaseImpl*) parent contentRect: (NSRect)contentRect styleMask: (NSWindowStyleMask)styleMask; { // https://jameshfisher.com/2020/07/10/why-is-the-contentrect-of-my-nswindow-ignored/ @@ -215,28 +209,27 @@ -(BOOL)canBecomeKeyWindow { - if (_canBecomeKeyAndMain) + // If the window has a child window being shown as a dialog then don't allow it to become the key window. + for(NSWindow* uch in [self childWindows]) { - // If the window has a child window being shown as a dialog then don't allow it to become the key window. - for(NSWindow* uch in [self childWindows]) - { - // TODO protocol - auto ch = objc_cast(uch); - if(ch == nil) - continue; - if (ch.isDialog) - return false; - } - - return true; + // TODO protocol + auto ch = objc_cast(uch); + if(ch == nil) + continue; + if (ch.isDialog) + return false; } - return false; + return true; } -(BOOL)canBecomeMainWindow { - return _canBecomeKeyAndMain; +#ifdef IS_NSPANEL + return false; +#else + return true; +#endif } -(bool)shouldTryToHandleEvents diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index a8f549b3c6..ae1e6a7016 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -122,6 +122,7 @@ protected: id GetWindowProtocol (); private: + void CreateNSWindow (bool isDialog); void InitialiseNSWindow (); }; diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index a58d0bb8be..414632770f 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -13,6 +13,7 @@ #include "AutoFitContentView.h" #import "WindowProtocol.h" #import "WindowInterfaces.h" +#include "WindowBaseImpl.h" WindowBaseImpl::~WindowBaseImpl() { @@ -31,6 +32,9 @@ WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl) lastPositionSet.X = 100; lastPositionSet.Y = 100; + lastSize = NSSize { 100, 100 }; + lastMaxSize = NSSize { CGFLOAT_MAX, CGFLOAT_MAX}; + lastMinSize = NSSize { 0, 0 }; _lastTitle = @""; Window = nullptr; @@ -85,6 +89,7 @@ HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) { START_COM_CALL; @autoreleasepool { + CreateNSWindow(isDialog); InitialiseNSWindow(); SetPosition(lastPositionSet); @@ -92,10 +97,6 @@ HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) { [Window setTitle:_lastTitle]; - if(!isDialog) { - [GetWindowProtocol() setCanBecomeKeyAndMain]; - } - if (ShouldTakeFocusOnShow() && activate) { [Window orderFront:Window]; [Window makeKeyAndOrderFront:Window]; @@ -524,9 +525,21 @@ void WindowBaseImpl::UpdateStyle() { [Window setStyleMask:GetStyle()]; } -void WindowBaseImpl::InitialiseNSWindow() { +void WindowBaseImpl::CreateNSWindow(bool isDialog) { if(Window == nullptr) { - Window = [[AvnWindow alloc] initWithParent:this contentRect:NSRect{0, 0, lastSize} styleMask:GetStyle()]; + if(isDialog) + { + Window = [[AvnPanel alloc] initWithParent:this contentRect:NSRect{0, 0, lastSize} styleMask:GetStyle()]; + } + else + { + Window = [[AvnWindow alloc] initWithParent:this contentRect:NSRect{0, 0, lastSize} styleMask:GetStyle()]; + } + } +} + +void WindowBaseImpl::InitialiseNSWindow() { + if(Window != nullptr) { [Window setContentView:StandardContainer]; [Window setStyleMask:NSWindowStyleMaskBorderless]; [Window setBackingType:NSBackingStoreBuffered]; @@ -537,9 +550,6 @@ void WindowBaseImpl::InitialiseNSWindow() { [Window setOpaque:false]; - [Window setContentMinSize:lastMinSize]; - [Window setContentMaxSize:lastMaxSize]; - if (lastMenu != nullptr) { [GetWindowProtocol() applyMenu:lastMenu]; diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index 9992d64b47..63a38f0c22 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -507,7 +507,7 @@ bool WindowImpl::IsDialog() { } NSWindowStyleMask WindowImpl::GetStyle() { - unsigned long s = NSWindowStyleMaskBorderless; + unsigned long s = this->_isDialog ? NSWindowStyleMaskUtilityWindow : NSWindowStyleMaskBorderless; switch (_decorations) { case SystemDecorationsNone: diff --git a/native/Avalonia.Native/src/OSX/WindowProtocol.h b/native/Avalonia.Native/src/OSX/WindowProtocol.h index d81d2f1ed1..1c97d89f39 100644 --- a/native/Avalonia.Native/src/OSX/WindowProtocol.h +++ b/native/Avalonia.Native/src/OSX/WindowProtocol.h @@ -10,7 +10,6 @@ @class AvnMenu; @protocol AvnWindowProtocol --(void) setCanBecomeKeyAndMain; -(void) pollModalSession: (NSModalSession _Nonnull) session; -(void) restoreParentWindow; -(bool) shouldTryToHandleEvents; From d6a4a6c901fec82f9675c3f70e72a7e01c008fb5 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 6 May 2022 18:04:57 +0100 Subject: [PATCH 291/416] ensure cast to protocol instead of concrete types. --- native/Avalonia.Native/src/OSX/AutoFitContentView.mm | 5 +++-- native/Avalonia.Native/src/OSX/AvnView.mm | 7 ++++++- native/Avalonia.Native/src/OSX/AvnWindow.mm | 6 ++---- native/Avalonia.Native/src/OSX/WindowBaseImpl.h | 4 ++-- native/Avalonia.Native/src/OSX/WindowBaseImpl.mm | 8 ++++---- 5 files changed, 17 insertions(+), 13 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/AutoFitContentView.mm b/native/Avalonia.Native/src/OSX/AutoFitContentView.mm index 4eaa08cbe2..314c579b76 100644 --- a/native/Avalonia.Native/src/OSX/AutoFitContentView.mm +++ b/native/Avalonia.Native/src/OSX/AutoFitContentView.mm @@ -5,7 +5,8 @@ #include "AvnView.h" #include "AutoFitContentView.h" -#import "WindowInterfaces.h" +#include "WindowInterfaces.h" +#include "WindowProtocol.h" @implementation AutoFitContentView { @@ -84,7 +85,7 @@ _settingSize = true; [super setFrameSize:newSize]; - auto window = objc_cast([self window]); + auto window = static_cast>([self window]); // TODO get actual titlebar size diff --git a/native/Avalonia.Native/src/OSX/AvnView.mm b/native/Avalonia.Native/src/OSX/AvnView.mm index e6cf73755b..02526afbcb 100644 --- a/native/Avalonia.Native/src/OSX/AvnView.mm +++ b/native/Avalonia.Native/src/OSX/AvnView.mm @@ -195,7 +195,12 @@ - (bool) ignoreUserInput:(bool)trigerInputWhenDisabled { - auto parentWindow = objc_cast([self window]); + if(_parent == nullptr) + { + return TRUE; + } + + auto parentWindow = _parent->GetWindowProtocol(); if(parentWindow == nil || ![parentWindow shouldTryToHandleEvents]) { diff --git a/native/Avalonia.Native/src/OSX/AvnWindow.mm b/native/Avalonia.Native/src/OSX/AvnWindow.mm index d0b23540f9..ef4dcc3df4 100644 --- a/native/Avalonia.Native/src/OSX/AvnWindow.mm +++ b/native/Avalonia.Native/src/OSX/AvnWindow.mm @@ -212,8 +212,7 @@ // If the window has a child window being shown as a dialog then don't allow it to become the key window. for(NSWindow* uch in [self childWindows]) { - // TODO protocol - auto ch = objc_cast(uch); + auto ch = static_cast>(uch); if(ch == nil) continue; if (ch.isDialog) @@ -256,8 +255,7 @@ -(void) restoreParentWindow; { - // TODO protocol - auto parent = objc_cast([self parentWindow]); + auto parent = static_cast>([self parentWindow]); if(parent != nil) { [parent removeChildWindow:self]; diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index ae1e6a7016..8c82bba98c 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -114,13 +114,13 @@ BEGIN_INTERFACE_MAP() virtual bool IsDialog(); + id GetWindowProtocol (); + protected: virtual NSWindowStyleMask GetStyle(); void UpdateStyle(); - id GetWindowProtocol (); - private: void CreateNSWindow (bool isDialog); void InitialiseNSWindow (); diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index 414632770f..227f348333 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -561,12 +561,12 @@ void WindowBaseImpl::InitialiseNSWindow() { } id WindowBaseImpl::GetWindowProtocol() { - id instance; - if ([Window conformsToProtocol:@protocol(AvnWindowProtocol)]) { - instance = Window; + if(Window == nullptr) + { + return nullptr; } - return instance; + return static_cast>(Window); } extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events, IAvnGlContext* gl) From 99e07e68d592771273aa4b795682603d2507d973 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 6 May 2022 18:07:01 +0100 Subject: [PATCH 292/416] remove unnecessary cast. --- native/Avalonia.Native/src/OSX/AvnWindow.mm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/native/Avalonia.Native/src/OSX/AvnWindow.mm b/native/Avalonia.Native/src/OSX/AvnWindow.mm index ef4dcc3df4..33f7f75314 100644 --- a/native/Avalonia.Native/src/OSX/AvnWindow.mm +++ b/native/Avalonia.Native/src/OSX/AvnWindow.mm @@ -255,7 +255,8 @@ -(void) restoreParentWindow; { - auto parent = static_cast>([self parentWindow]); + auto parent = [self parentWindow]; + if(parent != nil) { [parent removeChildWindow:self]; From 45ed0194254fa28b7a82b25de5f381da6be147a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiago=20Concei=C3=A7=C3=A3o?= Date: Sat, 7 May 2022 13:54:55 +0100 Subject: [PATCH 293/416] Respect Window MaxWidth and MaxHeight when using any SizeToContent to Auto --- src/Avalonia.Controls/Window.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index a5f99918b2..d0162bb0de 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -917,6 +917,15 @@ namespace Avalonia.Controls var constraint = clientSize; var maxAutoSize = PlatformImpl?.MaxAutoSizeHint ?? Size.Infinity; + if (MaxWidth > 0 && MaxWidth < maxAutoSize.Width) + { + maxAutoSize = maxAutoSize.WithWidth(MaxWidth); + } + if (MaxHeight > 0 && MaxHeight < maxAutoSize.Height) + { + maxAutoSize = maxAutoSize.WithHeight(MaxHeight); + } + if (sizeToContent.HasAllFlags(SizeToContent.Width)) { constraint = constraint.WithWidth(maxAutoSize.Width); From 3600639d39482ef60efbb573f4586d8019da745a Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Sun, 8 May 2022 19:17:08 +0100 Subject: [PATCH 294/416] re-create NSWindow if we call show and need to swap out for a dialog. --- native/Avalonia.Native/src/OSX/AvnWindow.mm | 4 ++++ .../Avalonia.Native/src/OSX/WindowBaseImpl.h | 1 + .../Avalonia.Native/src/OSX/WindowBaseImpl.mm | 21 ++++++++++++++----- .../Avalonia.Native/src/OSX/WindowProtocol.h | 1 + 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/AvnWindow.mm b/native/Avalonia.Native/src/OSX/AvnWindow.mm index 33f7f75314..6ff19ead68 100644 --- a/native/Avalonia.Native/src/OSX/AvnWindow.mm +++ b/native/Avalonia.Native/src/OSX/AvnWindow.mm @@ -433,5 +433,9 @@ } } +- (void)disconnectParent { + _parent = nullptr; +} + @end diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index 8c82bba98c..eff13bcb23 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -123,6 +123,7 @@ protected: private: void CreateNSWindow (bool isDialog); + void CleanNSWindow (); void InitialiseNSWindow (); }; diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index 227f348333..a7c38bf652 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -525,14 +525,25 @@ void WindowBaseImpl::UpdateStyle() { [Window setStyleMask:GetStyle()]; } +void WindowBaseImpl::CleanNSWindow() { + if(Window != nullptr) { + [GetWindowProtocol() disconnectParent]; + [Window close]; + Window = nullptr; + } +} + void WindowBaseImpl::CreateNSWindow(bool isDialog) { - if(Window == nullptr) { - if(isDialog) - { + if (isDialog) { + if (![Window isKindOfClass:[AvnPanel class]]) { + CleanNSWindow(); + Window = [[AvnPanel alloc] initWithParent:this contentRect:NSRect{0, 0, lastSize} styleMask:GetStyle()]; } - else - { + } else { + if (![Window isKindOfClass:[AvnWindow class]]) { + CleanNSWindow(); + Window = [[AvnWindow alloc] initWithParent:this contentRect:NSRect{0, 0, lastSize} styleMask:GetStyle()]; } } diff --git a/native/Avalonia.Native/src/OSX/WindowProtocol.h b/native/Avalonia.Native/src/OSX/WindowProtocol.h index 1c97d89f39..92194706de 100644 --- a/native/Avalonia.Native/src/OSX/WindowProtocol.h +++ b/native/Avalonia.Native/src/OSX/WindowProtocol.h @@ -20,5 +20,6 @@ -(double) getExtendedTitleBarHeight; -(void) setIsExtended:(bool)value; +-(void) disconnectParent; -(bool) isDialog; @end \ No newline at end of file From c756f812ec887cbdf77fd2d3d420a7164f25ee0f Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 9 May 2022 11:19:20 +0100 Subject: [PATCH 295/416] remove shadow invalidation hack. --- native/Avalonia.Native/src/OSX/WindowBaseImpl.mm | 1 - 1 file changed, 1 deletion(-) diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index a7c38bf652..db5eb54e3f 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -286,7 +286,6 @@ HRESULT WindowBaseImpl::Resize(double x, double y, AvnPlatformResizeReason reaso if(Window != nullptr) { [Window setContentSize:lastSize]; - [Window invalidateShadow]; } } @finally { From 5a027c585ab9f827bea077cc0a23f9f0579f9bce Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 9 May 2022 12:44:07 +0100 Subject: [PATCH 296/416] Merge pull request #8103 from AvaloniaUI/fixes/deterministic-builds Fixes/deterministic builds --- .../CompilerExtensions/XamlIlClrPropertyInfoHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlClrPropertyInfoHelper.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlClrPropertyInfoHelper.cs index 871a2a2045..e76e2cd46e 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlClrPropertyInfoHelper.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlClrPropertyInfoHelper.cs @@ -55,7 +55,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions return cached.get; } - var name = lst.Count == 0 ? key : key + "_" + Guid.NewGuid().ToString("N"); + var name = lst.Count == 0 ? key : key + "_" + context.Configuration.IdentifierGenerator.GenerateIdentifierPart(); var field = _builder.DefineField(types.IPropertyInfo, name + "!Field", false, true); From a92a49b04fb44103c0ec5506564ee7f0ab87dbfa Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Mon, 9 May 2022 22:14:35 +0300 Subject: [PATCH 297/416] Updated Rotate3DTransform to the new OnPropertyChanged --- src/Avalonia.Base/Rotate3DTransform.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Rotate3DTransform.cs b/src/Avalonia.Base/Rotate3DTransform.cs index 306363ec39..2c4e515861 100644 --- a/src/Avalonia.Base/Rotate3DTransform.cs +++ b/src/Avalonia.Base/Rotate3DTransform.cs @@ -203,7 +203,7 @@ public class Rotate3DTransform : Transform } } - protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { if (!_isInitializing) RaiseChanged(); } From 2f1ffbd81e6d573a6d5dfa6b06647a0d071d3903 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Mon, 9 May 2022 23:52:15 +0300 Subject: [PATCH 298/416] Make ThreadSafeObjectPool actually thread safe (#8106) * Make ThreadSafeObjectPool actually thread safe --- src/Avalonia.Base/Threading/ThreadSafeObjectPool.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Avalonia.Base/Threading/ThreadSafeObjectPool.cs b/src/Avalonia.Base/Threading/ThreadSafeObjectPool.cs index c6845485dc..827a02334a 100644 --- a/src/Avalonia.Base/Threading/ThreadSafeObjectPool.cs +++ b/src/Avalonia.Base/Threading/ThreadSafeObjectPool.cs @@ -5,12 +5,11 @@ namespace Avalonia.Threading public class ThreadSafeObjectPool where T : class, new() { private Stack _stack = new Stack(); - private object _lock = new object(); public static ThreadSafeObjectPool Default { get; } = new ThreadSafeObjectPool(); public T Get() { - lock (_lock) + lock (_stack) { if(_stack.Count == 0) return new T(); From 08487446d98311daa2d6d2304fc4647f984ea8b9 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 10 May 2022 14:19:35 +0100 Subject: [PATCH 299/416] [OSX] cache IsClientAreaExtendedToDecorations, and apply it when NSPanel / NSWindow is created and Shown. --- native/Avalonia.Native/src/OSX/WindowImpl.mm | 56 ++++++++++++------- .../ViewModels/MainWindowViewModel.cs | 2 +- src/Avalonia.Native/WindowImpl.cs | 7 +++ 3 files changed, 43 insertions(+), 22 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index 63a38f0c22..7ab2b2b5fc 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -55,8 +55,20 @@ HRESULT WindowImpl::Show(bool activate, bool isDialog) { @autoreleasepool { _isDialog = isDialog; + + bool created = Window == nullptr; + WindowBaseImpl::Show(activate, isDialog); + if(created) + { + if(_isClientAreaExtended) + { + [GetWindowProtocol() setIsExtended:true]; + SetExtendClientArea(true); + } + } + HideOrShowTrafficLights(); return SetWindowState(_lastWindowState); @@ -327,37 +339,39 @@ HRESULT WindowImpl::SetExtendClientArea(bool enable) { @autoreleasepool { _isClientAreaExtended = enable; - if (enable) { - Window.titleVisibility = NSWindowTitleHidden; + if(Window != nullptr) { + if (enable) { + Window.titleVisibility = NSWindowTitleHidden; - [Window setTitlebarAppearsTransparent:true]; + [Window setTitlebarAppearsTransparent:true]; - auto wantsTitleBar = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome); + auto wantsTitleBar = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome); - if (wantsTitleBar) { - [StandardContainer ShowTitleBar:true]; - } else { - [StandardContainer ShowTitleBar:false]; - } + if (wantsTitleBar) { + [StandardContainer ShowTitleBar:true]; + } else { + [StandardContainer ShowTitleBar:false]; + } - if (_extendClientHints & AvnOSXThickTitleBar) { - Window.toolbar = [NSToolbar new]; - Window.toolbar.showsBaselineSeparator = false; + if (_extendClientHints & AvnOSXThickTitleBar) { + Window.toolbar = [NSToolbar new]; + Window.toolbar.showsBaselineSeparator = false; + } else { + Window.toolbar = nullptr; + } } else { + Window.titleVisibility = NSWindowTitleVisible; Window.toolbar = nullptr; + [Window setTitlebarAppearsTransparent:false]; + View.layer.zPosition = 0; } - } else { - Window.titleVisibility = NSWindowTitleVisible; - Window.toolbar = nullptr; - [Window setTitlebarAppearsTransparent:false]; - View.layer.zPosition = 0; - } - [GetWindowProtocol() setIsExtended:enable]; + [GetWindowProtocol() setIsExtended:enable]; - HideOrShowTrafficLights(); + HideOrShowTrafficLights(); - UpdateStyle(); + UpdateStyle(); + } return S_OK; } diff --git a/samples/ControlCatalog/ViewModels/MainWindowViewModel.cs b/samples/ControlCatalog/ViewModels/MainWindowViewModel.cs index 2b0c30f311..c44024d952 100644 --- a/samples/ControlCatalog/ViewModels/MainWindowViewModel.cs +++ b/samples/ControlCatalog/ViewModels/MainWindowViewModel.cs @@ -18,7 +18,7 @@ namespace ControlCatalog.ViewModels private WindowState _windowState; private WindowState[] _windowStates; private int _transparencyLevel; - private ExtendClientAreaChromeHints _chromeHints; + private ExtendClientAreaChromeHints _chromeHints = ExtendClientAreaChromeHints.PreferSystemChrome; private bool _extendClientAreaEnabled; private bool _systemTitleBarEnabled; private bool _preferSystemChromeEnabled; diff --git a/src/Avalonia.Native/WindowImpl.cs b/src/Avalonia.Native/WindowImpl.cs index c082bdb1b8..b5af927ea0 100644 --- a/src/Avalonia.Native/WindowImpl.cs +++ b/src/Avalonia.Native/WindowImpl.cs @@ -107,6 +107,13 @@ namespace Avalonia.Native private bool _isExtended; public bool IsClientAreaExtendedToDecorations => _isExtended; + public override void Show(bool activate, bool isDialog) + { + base.Show(activate, isDialog); + + InvalidateExtendedMargins(); + } + protected override bool ChromeHitTest (RawPointerEventArgs e) { if(_isExtended) From 65ed75970dd54e9c68d37c2bf67440c754018d82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Tue, 10 May 2022 19:25:15 +0200 Subject: [PATCH 300/416] Fix TextBox property name registrations --- src/Avalonia.Controls/TextBox.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 0be58e7fcc..45e66828c1 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -55,13 +55,13 @@ namespace Avalonia.Controls AvaloniaProperty.Register(nameof(PasswordChar)); public static readonly StyledProperty SelectionBrushProperty = - AvaloniaProperty.Register(nameof(SelectionBrushProperty)); + AvaloniaProperty.Register(nameof(SelectionBrush)); public static readonly StyledProperty SelectionForegroundBrushProperty = - AvaloniaProperty.Register(nameof(SelectionForegroundBrushProperty)); + AvaloniaProperty.Register(nameof(SelectionForegroundBrush)); public static readonly StyledProperty CaretBrushProperty = - AvaloniaProperty.Register(nameof(CaretBrushProperty)); + AvaloniaProperty.Register(nameof(CaretBrush)); public static readonly DirectProperty SelectionStartProperty = AvaloniaProperty.RegisterDirect( From 665b0fa3b6db07ef85463453a5e0a0da2a99d065 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Tue, 10 May 2022 19:25:26 +0200 Subject: [PATCH 301/416] Fix TextPresenter property name registrations --- src/Avalonia.Controls/Presenters/TextPresenter.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index 0785149a73..07061940b5 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -26,13 +26,13 @@ namespace Avalonia.Controls.Presenters AvaloniaProperty.Register(nameof(PasswordChar)); public static readonly StyledProperty SelectionBrushProperty = - AvaloniaProperty.Register(nameof(SelectionBrushProperty)); + AvaloniaProperty.Register(nameof(SelectionBrush)); public static readonly StyledProperty SelectionForegroundBrushProperty = - AvaloniaProperty.Register(nameof(SelectionForegroundBrushProperty)); + AvaloniaProperty.Register(nameof(SelectionForegroundBrush)); public static readonly StyledProperty CaretBrushProperty = - AvaloniaProperty.Register(nameof(CaretBrushProperty)); + AvaloniaProperty.Register(nameof(CaretBrush)); public static readonly DirectProperty SelectionStartProperty = TextBox.SelectionStartProperty.AddOwner( From d8e01f0e1a67f72f71a81a271c000042934a6196 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Tue, 10 May 2022 19:25:35 +0200 Subject: [PATCH 302/416] Fix DockPanel property name registrations --- src/Avalonia.Controls/DockPanel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/DockPanel.cs b/src/Avalonia.Controls/DockPanel.cs index 8e23555c2d..3e3ed509b5 100644 --- a/src/Avalonia.Controls/DockPanel.cs +++ b/src/Avalonia.Controls/DockPanel.cs @@ -34,7 +34,7 @@ namespace Avalonia.Controls /// public static readonly StyledProperty LastChildFillProperty = AvaloniaProperty.Register( - nameof(LastChildFillProperty), + nameof(LastChildFill), defaultValue: true); /// From d2475dd53b4830ef5332a9de79f8ac9214bd8c8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Tue, 10 May 2022 19:28:44 +0200 Subject: [PATCH 303/416] Fix owner of IsTextSearchEnabled property --- src/Avalonia.Controls/Primitives/SelectingItemsControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs index bff6799792..164aab32db 100644 --- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs +++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs @@ -94,7 +94,7 @@ namespace Avalonia.Controls.Primitives /// Defines the property. /// public static readonly StyledProperty IsTextSearchEnabledProperty = - AvaloniaProperty.Register(nameof(IsTextSearchEnabled), false); + AvaloniaProperty.Register(nameof(IsTextSearchEnabled), false); /// /// Event that should be raised by items that implement to From 3880f1abf41e4494c84af8fc52492f5adfbb4127 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Tue, 10 May 2022 19:30:19 +0200 Subject: [PATCH 304/416] Fix PasswordCharProperty owner --- src/Avalonia.Controls/MaskedTextBox.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/MaskedTextBox.cs b/src/Avalonia.Controls/MaskedTextBox.cs index 933788f9ea..080326606e 100644 --- a/src/Avalonia.Controls/MaskedTextBox.cs +++ b/src/Avalonia.Controls/MaskedTextBox.cs @@ -32,7 +32,7 @@ namespace Avalonia.Controls AvaloniaProperty.Register(nameof(Mask), string.Empty); public static new readonly StyledProperty PasswordCharProperty = - AvaloniaProperty.Register(nameof(PasswordChar), '\0'); + AvaloniaProperty.Register(nameof(PasswordChar), '\0'); public static readonly StyledProperty PromptCharProperty = AvaloniaProperty.Register(nameof(PromptChar), '_'); From ee33319be119129cef8ca1fe0a243b4d976ee109 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Tue, 10 May 2022 19:31:25 +0200 Subject: [PATCH 305/416] Fix TickPlacementProperty property owner --- src/Avalonia.Controls/Slider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Slider.cs b/src/Avalonia.Controls/Slider.cs index 64dfce22d4..b6a6f3d02c 100644 --- a/src/Avalonia.Controls/Slider.cs +++ b/src/Avalonia.Controls/Slider.cs @@ -76,7 +76,7 @@ namespace Avalonia.Controls /// Defines the property. /// public static readonly StyledProperty TickPlacementProperty = - AvaloniaProperty.Register(nameof(TickPlacement), 0d); + AvaloniaProperty.Register(nameof(TickPlacement), 0d); /// /// Defines the property. From da640adb0cec38e95d2910bf54371ac6f973fa9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Tue, 10 May 2022 19:40:00 +0200 Subject: [PATCH 306/416] Fix PaneTemplateProperty property owner --- src/Avalonia.Controls/SplitView.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/SplitView.cs b/src/Avalonia.Controls/SplitView.cs index ae1605a985..532cb1d329 100644 --- a/src/Avalonia.Controls/SplitView.cs +++ b/src/Avalonia.Controls/SplitView.cs @@ -138,7 +138,7 @@ namespace Avalonia.Controls /// Defines the property. /// public static readonly StyledProperty PaneTemplateProperty = - AvaloniaProperty.Register(nameof(PaneTemplate)); + AvaloniaProperty.Register(nameof(PaneTemplate)); /// /// Defines the property From c6eb4138447fd2e9758a8d893fff6b60150268df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Tue, 10 May 2022 19:42:10 +0200 Subject: [PATCH 307/416] Use AddOwner for PlacementRectProperty --- src/Avalonia.Controls/ContextMenu.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/ContextMenu.cs b/src/Avalonia.Controls/ContextMenu.cs index ee2378101a..2b122d4174 100644 --- a/src/Avalonia.Controls/ContextMenu.cs +++ b/src/Avalonia.Controls/ContextMenu.cs @@ -63,7 +63,7 @@ namespace Avalonia.Controls /// Defines the property. /// public static readonly StyledProperty PlacementRectProperty = - AvaloniaProperty.Register(nameof(PlacementRect)); + Popup.PlacementRectProperty.AddOwner(); /// /// Defines the property. From 05fe5fb4ebfc164723ddbe476049499fae675098 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Tue, 10 May 2022 19:43:15 +0200 Subject: [PATCH 308/416] Fix owner for WindowManagerAddShadowHintProperty property --- src/Avalonia.Controls/Primitives/Popup.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Primitives/Popup.cs b/src/Avalonia.Controls/Primitives/Popup.cs index 7a7e41b029..95e5e25c42 100644 --- a/src/Avalonia.Controls/Primitives/Popup.cs +++ b/src/Avalonia.Controls/Primitives/Popup.cs @@ -27,7 +27,7 @@ namespace Avalonia.Controls.Primitives #pragma warning restore CS0612 // Type or member is obsolete { public static readonly StyledProperty WindowManagerAddShadowHintProperty = - AvaloniaProperty.Register(nameof(WindowManagerAddShadowHint), false); + AvaloniaProperty.Register(nameof(WindowManagerAddShadowHint), false); /// /// Defines the property. From 2d6bd60f691434e667cbbbdff933dfda9d4c0f69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Tue, 10 May 2022 19:46:41 +0200 Subject: [PATCH 309/416] Rename --- src/Avalonia.Controls/Primitives/Track.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/Primitives/Track.cs b/src/Avalonia.Controls/Primitives/Track.cs index 49f0cda982..85343b8fbb 100644 --- a/src/Avalonia.Controls/Primitives/Track.cs +++ b/src/Avalonia.Controls/Primitives/Track.cs @@ -44,7 +44,7 @@ namespace Avalonia.Controls.Primitives AvaloniaProperty.Register(nameof(IsDirectionReversed)); public static readonly StyledProperty IgnoreThumbDragProperty = - AvaloniaProperty.Register(nameof(IsThumbDragHandled)); + AvaloniaProperty.Register(nameof(IgnoreThumbDrag)); private double _minimum; private double _maximum = 100.0; @@ -118,7 +118,7 @@ namespace Avalonia.Controls.Primitives set { SetValue(IsDirectionReversedProperty, value); } } - public bool IsThumbDragHandled + public bool IgnoreThumbDrag { get { return GetValue(IgnoreThumbDragProperty); } set { SetValue(IgnoreThumbDragProperty, value); } From da7eecec0788d086fe6a02138755c770509ffe47 Mon Sep 17 00:00:00 2001 From: Kaktusbot Date: Tue, 10 May 2022 22:38:03 +0400 Subject: [PATCH 310/416] Fix missing NotifyCountChanged in AvaloniaList.AddRange --- src/Avalonia.Base/Collections/AvaloniaList.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Avalonia.Base/Collections/AvaloniaList.cs b/src/Avalonia.Base/Collections/AvaloniaList.cs index 9972e72dd4..a05c9c5da3 100644 --- a/src/Avalonia.Base/Collections/AvaloniaList.cs +++ b/src/Avalonia.Base/Collections/AvaloniaList.cs @@ -394,7 +394,13 @@ namespace Avalonia.Collections } while (en.MoveNext()); if (notificationItems is not null) + { NotifyAdd(notificationItems, index); + } + else + { + NotifyCountChanged(); + } } } } From 5eca6cc83e3cbb9cbef2e5b0c48959eee32e78b7 Mon Sep 17 00:00:00 2001 From: Kaktusbot Date: Tue, 10 May 2022 22:57:55 +0400 Subject: [PATCH 311/416] Add test to AvaloniaList.AddRange to notify Count property changed --- .../Collections/AvaloniaListTests.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/Avalonia.Base.UnitTests/Collections/AvaloniaListTests.cs b/tests/Avalonia.Base.UnitTests/Collections/AvaloniaListTests.cs index f5ae4cc1e0..b59f272b0c 100644 --- a/tests/Avalonia.Base.UnitTests/Collections/AvaloniaListTests.cs +++ b/tests/Avalonia.Base.UnitTests/Collections/AvaloniaListTests.cs @@ -146,6 +146,23 @@ namespace Avalonia.Base.UnitTests.Collections Assert.True(raised); } + [Fact] + public void AddRange_IEnumerable_Should_Raise_Count_PropertyChanged() + { + var target = new AvaloniaList(new[] { 1, 2, 3, 4, 5 }); + var raised = false; + + target.PropertyChanged += (s, e) => { + Assert.Equal(e.PropertyName, nameof(target.Count)); + Assert.Equal(target.Count, 7); + raised = true; + }; + + target.AddRange(Enumerable.Range(6, 2)); + + Assert.True(raised); + } + [Fact] public void AddRange_Items_Should_Raise_Correct_CollectionChanged() { From a90c4d4f22d737ee4231bacaf9f1763019822687 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Tue, 10 May 2022 22:47:24 +0200 Subject: [PATCH 312/416] Use correct property name --- src/Avalonia.Controls/Primitives/Track.cs | 2 +- src/Avalonia.Controls/Slider.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/Primitives/Track.cs b/src/Avalonia.Controls/Primitives/Track.cs index 85343b8fbb..14ec7a2849 100644 --- a/src/Avalonia.Controls/Primitives/Track.cs +++ b/src/Avalonia.Controls/Primitives/Track.cs @@ -442,7 +442,7 @@ namespace Avalonia.Controls.Primitives private void ThumbDragged(object? sender, VectorEventArgs e) { - if (IsThumbDragHandled) + if (IgnoreThumbDrag) return; Value = MathUtilities.Clamp( diff --git a/src/Avalonia.Controls/Slider.cs b/src/Avalonia.Controls/Slider.cs index b6a6f3d02c..be87705b54 100644 --- a/src/Avalonia.Controls/Slider.cs +++ b/src/Avalonia.Controls/Slider.cs @@ -197,7 +197,7 @@ namespace Avalonia.Controls if (_track != null) { - _track.IsThumbDragHandled = true; + _track.IgnoreThumbDrag = true; } if (_decreaseButton != null) From 409d673215ba683559d062b406ea928303d4bf5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Tue, 10 May 2022 22:48:54 +0200 Subject: [PATCH 313/416] Fix MaximumRowsOrColumnsProperty registration name --- src/Avalonia.Base/Layout/UniformGridLayout.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Layout/UniformGridLayout.cs b/src/Avalonia.Base/Layout/UniformGridLayout.cs index 418cd55e41..47c994a350 100644 --- a/src/Avalonia.Base/Layout/UniformGridLayout.cs +++ b/src/Avalonia.Base/Layout/UniformGridLayout.cs @@ -116,7 +116,7 @@ namespace Avalonia.Layout /// Defines the property. /// public static readonly StyledProperty MaximumRowsOrColumnsProperty = - AvaloniaProperty.Register(nameof(MinItemWidth)); + AvaloniaProperty.Register(nameof(MaximumRowsOrColumns)); /// /// Defines the property. From 1e3c5642a3a014fc255d0e11ef1ca7842a7d9f44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Tue, 10 May 2022 22:52:58 +0200 Subject: [PATCH 314/416] Fix ConicGradientBrush property registrations --- src/Avalonia.Base/Media/ConicGradientBrush.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Base/Media/ConicGradientBrush.cs b/src/Avalonia.Base/Media/ConicGradientBrush.cs index 7c1266fa17..4b50019ddc 100644 --- a/src/Avalonia.Base/Media/ConicGradientBrush.cs +++ b/src/Avalonia.Base/Media/ConicGradientBrush.cs @@ -11,7 +11,7 @@ namespace Avalonia.Media /// Defines the property. /// public static readonly StyledProperty CenterProperty = - AvaloniaProperty.Register( + AvaloniaProperty.Register( nameof(Center), RelativePoint.Center); @@ -19,7 +19,7 @@ namespace Avalonia.Media /// Defines the property. /// public static readonly StyledProperty AngleProperty = - AvaloniaProperty.Register( + AvaloniaProperty.Register( nameof(Angle), 0); From a5b2eb08d4000d7bb4c8c4d1f21afe51bb2031b9 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 11 May 2022 21:12:25 +0300 Subject: [PATCH 315/416] Added support for IReflectableType in InpcPropertyAccessorPlugin --- .../Plugins/InpcPropertyAccessorPlugin.cs | 19 +- .../Data/BindingTests.cs | 19 ++ .../Data/DynamicReflectableType.cs | 221 ++++++++++++++++++ 3 files changed, 252 insertions(+), 7 deletions(-) create mode 100644 tests/Avalonia.Markup.UnitTests/Data/DynamicReflectableType.cs diff --git a/src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs index 33cecd10a7..b93bf87fdf 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs @@ -17,7 +17,7 @@ namespace Avalonia.Data.Core.Plugins new Dictionary<(Type, string), PropertyInfo?>(); /// - public bool Match(object obj, string propertyName) => GetFirstPropertyWithName(obj.GetType(), propertyName) != null; + public bool Match(object obj, string propertyName) => GetFirstPropertyWithName(obj, propertyName) != null; /// /// Starts monitoring the value of a property on an object. @@ -36,7 +36,7 @@ namespace Avalonia.Data.Core.Plugins if (!reference.TryGetTarget(out var instance) || instance is null) return null; - var p = GetFirstPropertyWithName(instance.GetType(), propertyName); + var p = GetFirstPropertyWithName(instance, propertyName); if (p != null) { @@ -50,8 +50,16 @@ namespace Avalonia.Data.Core.Plugins } } - private PropertyInfo? GetFirstPropertyWithName(Type type, string propertyName) + private const BindingFlags PropertyBindingFlags = + BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance; + + private PropertyInfo? GetFirstPropertyWithName(object instance, string propertyName) { + if (instance is IReflectableType reflectableType) + return reflectableType.GetTypeInfo().GetProperty(propertyName, PropertyBindingFlags); + + var type = instance.GetType(); + var key = (type, propertyName); if (!_propertyLookup.TryGetValue(key, out var propertyInfo)) @@ -66,10 +74,7 @@ namespace Avalonia.Data.Core.Plugins { PropertyInfo? found = null; - const BindingFlags bindingFlags = - BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance; - - var properties = type.GetProperties(bindingFlags); + var properties = type.GetProperties(PropertyBindingFlags); foreach (PropertyInfo propertyInfo in properties) { diff --git a/tests/Avalonia.Markup.UnitTests/Data/BindingTests.cs b/tests/Avalonia.Markup.UnitTests/Data/BindingTests.cs index 055de999e2..11a22f0dec 100644 --- a/tests/Avalonia.Markup.UnitTests/Data/BindingTests.cs +++ b/tests/Avalonia.Markup.UnitTests/Data/BindingTests.cs @@ -617,6 +617,25 @@ namespace Avalonia.Markup.UnitTests.Data Assert.Equal(0, source.SubscriberCount); } + + [Fact] + public void Binding_Can_Resolve_Property_From_IReflectableType_Type() + { + var source = new DynamicReflectableType { ["Foo"] = "foo" }; + var target = new TwoWayBindingTest { DataContext = source }; + var binding = new Binding + { + Path = "Foo", + }; + + target.Bind(TwoWayBindingTest.TwoWayProperty, binding); + + Assert.Equal("foo", target.TwoWay); + source["Foo"] = "bar"; + Assert.Equal("bar", target.TwoWay); + target.TwoWay = "baz"; + Assert.Equal("baz", source["Foo"]); + } private class StyledPropertyClass : AvaloniaObject { diff --git a/tests/Avalonia.Markup.UnitTests/Data/DynamicReflectableType.cs b/tests/Avalonia.Markup.UnitTests/Data/DynamicReflectableType.cs new file mode 100644 index 0000000000..3c57bd38cf --- /dev/null +++ b/tests/Avalonia.Markup.UnitTests/Data/DynamicReflectableType.cs @@ -0,0 +1,221 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Globalization; +using System.Reflection; +using Moq; + +namespace Avalonia.Markup.UnitTests.Data; + +class DynamicReflectableType : IReflectableType, INotifyPropertyChanged, IEnumerable> +{ + private Dictionary _dic = new(); + + public TypeInfo GetTypeInfo() + { + return new FakeTypeInfo(); + } + + public void Add(string key, object value) + { + _dic.Add(key, value); + + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(key)); + } + + public object this[string key] + { + get => _dic[key]; + set + { + _dic[key] = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(key)); + } + } + + public event PropertyChangedEventHandler PropertyChanged; + public IEnumerator> GetEnumerator() + { + return _dic.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable)_dic).GetEnumerator(); + } + + + class FakeTypeInfo : TypeInfo + { + protected override PropertyInfo GetPropertyImpl(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, + ParameterModifier[] modifiers) + { + var propInfo = new Mock(); + propInfo.SetupGet(x => x.Name).Returns(name); + propInfo.SetupGet(x => x.PropertyType).Returns(typeof(object)); + propInfo.SetupGet(x => x.CanWrite).Returns(true); + propInfo.Setup(x => x.GetValue(It.IsAny(), It.IsAny())) + .Returns((object target, object [] _) => ((DynamicReflectableType)target)._dic.GetValueOrDefault(name)); + propInfo.Setup(x => x.SetValue(It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((object target, object value, object [] _) => + { + ((DynamicReflectableType)target)._dic[name] = value; + }); + return propInfo.Object; + } + + #region NotSupported + + + public override object[] GetCustomAttributes(bool inherit) + { + throw new NotSupportedException(); + } + + public override object[] GetCustomAttributes(Type attributeType, bool inherit) + { + throw new NotSupportedException(); + } + + public override bool IsDefined(Type attributeType, bool inherit) + { + throw new NotSupportedException(); + } + + public override Module Module { get; } + public override string Namespace { get; } + public override string Name { get; } + protected override TypeAttributes GetAttributeFlagsImpl() + { + throw new NotSupportedException(); + } + + protected override ConstructorInfo GetConstructorImpl(BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, + Type[] types, ParameterModifier[] modifiers) + { + throw new NotSupportedException(); + } + + public override ConstructorInfo[] GetConstructors(BindingFlags bindingAttr) + { + throw new NotSupportedException(); + } + + public override Type GetElementType() + { + throw new NotSupportedException(); + } + + public override EventInfo GetEvent(string name, BindingFlags bindingAttr) + { + throw new NotSupportedException(); + } + + public override EventInfo[] GetEvents(BindingFlags bindingAttr) + { + throw new NotSupportedException(); + } + + public override FieldInfo GetField(string name, BindingFlags bindingAttr) + { + throw new NotSupportedException(); + } + + public override FieldInfo[] GetFields(BindingFlags bindingAttr) + { + throw new NotSupportedException(); + } + + public override MemberInfo[] GetMembers(BindingFlags bindingAttr) + { + throw new NotSupportedException(); + } + + protected override MethodInfo GetMethodImpl(string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, + Type[] types, ParameterModifier[] modifiers) + { + throw new NotSupportedException(); + } + + public override MethodInfo[] GetMethods(BindingFlags bindingAttr) + { + throw new NotSupportedException(); + } + + public override PropertyInfo[] GetProperties(BindingFlags bindingAttr) + { + throw new NotSupportedException(); + } + + public override object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, + ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters) + { + throw new NotSupportedException(); + } + + public override Type UnderlyingSystemType { get; } + + protected override bool IsArrayImpl() + { + throw new NotSupportedException(); + } + + protected override bool IsByRefImpl() + { + throw new NotSupportedException(); + } + + protected override bool IsCOMObjectImpl() + { + throw new NotSupportedException(); + } + + protected override bool IsPointerImpl() + { + throw new NotSupportedException(); + } + + protected override bool IsPrimitiveImpl() + { + throw new NotSupportedException(); + } + + public override Assembly Assembly { get; } + public override string AssemblyQualifiedName { get; } + public override Type BaseType { get; } + public override string FullName { get; } + public override Guid GUID { get; } + + + + protected override bool HasElementTypeImpl() + { + throw new NotSupportedException(); + } + + public override Type GetNestedType(string name, BindingFlags bindingAttr) + { + throw new NotSupportedException(); + } + + public override Type[] GetNestedTypes(BindingFlags bindingAttr) + { + throw new NotSupportedException(); + } + + public override Type GetInterface(string name, bool ignoreCase) + { + throw new NotSupportedException(); + } + + public override Type[] GetInterfaces() + { + throw new NotSupportedException(); + } + + + #endregion + + } +} \ No newline at end of file From 35db70c8d4905eba47f0fd73e2de0830d5e8e2fe Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 12 May 2022 14:37:23 +0200 Subject: [PATCH 316/416] Don't expose viewbox container as logical child. #7735 introduced an internal container control which hosts the child, but it exposed this child in the logical tree, breaking any styles which relied on the `Viewbox.Child` being the logical child of the `Viewbox`. Fix this by introducing a simple internal `ViewboxContainer` control as the container. --- src/Avalonia.Controls/Viewbox.cs | 43 +++++++++++++++++-- .../ViewboxTests.cs | 20 +++++++++ 2 files changed, 60 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/Viewbox.cs b/src/Avalonia.Controls/Viewbox.cs index 33a05f126d..f3ec53ed2d 100644 --- a/src/Avalonia.Controls/Viewbox.cs +++ b/src/Avalonia.Controls/Viewbox.cs @@ -8,7 +8,7 @@ namespace Avalonia.Controls /// public class Viewbox : Control { - private Decorator _containerVisual; + private ViewboxContainer _containerVisual; /// /// Defines the property. @@ -37,9 +37,8 @@ namespace Avalonia.Controls public Viewbox() { - _containerVisual = new Decorator(); + _containerVisual = new ViewboxContainer(); _containerVisual.RenderTransformOrigin = RelativePoint.TopLeft; - LogicalChildren.Add(_containerVisual); VisualChildren.Add(_containerVisual); } @@ -88,7 +87,22 @@ namespace Avalonia.Controls if (change.Property == ChildProperty) { + var (oldChild, newChild) = change.GetOldAndNewValue(); + + if (oldChild is not null) + { + ((ISetLogicalParent)oldChild).SetParent(null); + LogicalChildren.Remove(oldChild); + } + _containerVisual.Child = change.GetNewValue(); + + if (newChild is not null) + { + ((ISetLogicalParent)newChild).SetParent(this); + LogicalChildren.Add(newChild); + } + InvalidateMeasure(); } } @@ -129,5 +143,28 @@ namespace Avalonia.Controls return finalSize; } + + private class ViewboxContainer : Control + { + private IControl? _child; + + public IControl? Child + { + get => _child; + set + { + if (_child != value) + { + if (_child is not null) + VisualChildren.Remove(_child); + + _child = value; + + if (_child is not null) + VisualChildren.Add(_child); + } + } + } + } } } diff --git a/tests/Avalonia.Controls.UnitTests/ViewboxTests.cs b/tests/Avalonia.Controls.UnitTests/ViewboxTests.cs index d33e55341b..39a14a6a7e 100644 --- a/tests/Avalonia.Controls.UnitTests/ViewboxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ViewboxTests.cs @@ -1,4 +1,5 @@ using Avalonia.Controls.Shapes; +using Avalonia.LogicalTree; using Avalonia.Media; using Avalonia.UnitTests; using Xunit; @@ -170,5 +171,24 @@ namespace Avalonia.Controls.UnitTests Assert.Equal(expectedScale, scaleTransform.ScaleX); Assert.Equal(expectedScale, scaleTransform.ScaleY); } + + [Fact] + public void Child_Should_Be_Logical_Child_Of_Viewbox() + { + var target = new Viewbox(); + + Assert.Empty(target.GetLogicalChildren()); + + var child = new Canvas(); + target.Child = child; + + Assert.Single(target.GetLogicalChildren(), child); + Assert.Same(child.GetLogicalParent(), target); + + target.Child = null; + + Assert.Empty(target.GetLogicalChildren()); + Assert.Null(child.GetLogicalParent()); + } } } From 36fefd871704a48ef6fd553845ad6c03e70ea9ad Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 12 May 2022 22:44:29 +0200 Subject: [PATCH 317/416] Fix copypasta. Co-authored-by: Max Katz --- src/Avalonia.Controls/Viewbox.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Viewbox.cs b/src/Avalonia.Controls/Viewbox.cs index f3ec53ed2d..55c52d8ed9 100644 --- a/src/Avalonia.Controls/Viewbox.cs +++ b/src/Avalonia.Controls/Viewbox.cs @@ -95,7 +95,7 @@ namespace Avalonia.Controls LogicalChildren.Remove(oldChild); } - _containerVisual.Child = change.GetNewValue(); + _containerVisual.Child = newChild; if (newChild is not null) { From e4f4837a8d3ac9e8a30cb84c03c6dbcb2da39d0b Mon Sep 17 00:00:00 2001 From: robloo Date: Thu, 12 May 2022 21:52:36 -0400 Subject: [PATCH 318/416] Simplify getting changed property values Co-authored-by: Max Katz --- src/Avalonia.Controls/Calendar/CalendarDatePicker.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs b/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs index 0005e07536..8dffc43d91 100644 --- a/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs +++ b/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs @@ -291,8 +291,7 @@ namespace Avalonia.Controls // Text else if (change.Property == TextProperty) { - var oldValue = change.GetOldValue(); - var value = change.GetNewValue(); + var (oldValue, value) = change.GetOldAndNewValue(); if (!_suspendTextChangeHandler) { From ccce304c69385fc3f5c41a3646084792c0f37f6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Fri, 13 May 2022 14:13:20 +0200 Subject: [PATCH 319/416] Fix TimePicker property registrations --- src/Avalonia.Controls/DateTimePickers/TimePicker.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/DateTimePickers/TimePicker.cs b/src/Avalonia.Controls/DateTimePickers/TimePicker.cs index f04c79505e..047667567d 100644 --- a/src/Avalonia.Controls/DateTimePickers/TimePicker.cs +++ b/src/Avalonia.Controls/DateTimePickers/TimePicker.cs @@ -38,13 +38,13 @@ namespace Avalonia.Controls /// Defines the property /// public static readonly StyledProperty HeaderProperty = - AvaloniaProperty.Register(nameof(Header)); + AvaloniaProperty.Register(nameof(Header)); /// /// Defines the property /// public static readonly StyledProperty HeaderTemplateProperty = - AvaloniaProperty.Register(nameof(HeaderTemplate)); + AvaloniaProperty.Register(nameof(HeaderTemplate)); /// /// Defines the property From 2ee94118305364b72aee6865d427f1330314bc27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Fri, 13 May 2022 14:16:05 +0200 Subject: [PATCH 320/416] Fix SelectingItemsControl WrapSelection property owner --- src/Avalonia.Controls/Primitives/SelectingItemsControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs index 164aab32db..a730659330 100644 --- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs +++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs @@ -118,7 +118,7 @@ namespace Avalonia.Controls.Primitives /// Defines the property. /// public static readonly StyledProperty WrapSelectionProperty = - AvaloniaProperty.Register(nameof(WrapSelection), defaultValue: false); + AvaloniaProperty.Register(nameof(WrapSelection), defaultValue: false); private static readonly IList Empty = Array.Empty(); private string _textSearchTerm = string.Empty; From 08cbec6f23848dab0680531a059eb8c92babb290 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Fri, 13 May 2022 14:28:42 +0200 Subject: [PATCH 321/416] Fix ContentPresenter property field types --- .../Presenters/ContentPresenter.cs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Avalonia.Controls/Presenters/ContentPresenter.cs b/src/Avalonia.Controls/Presenters/ContentPresenter.cs index 996cb29534..12a8dd747d 100644 --- a/src/Avalonia.Controls/Presenters/ContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ContentPresenter.cs @@ -52,67 +52,67 @@ namespace Avalonia.Controls.Presenters /// /// Defines the property. /// - public static readonly AttachedProperty ForegroundProperty = + public static readonly StyledProperty ForegroundProperty = TextElement.ForegroundProperty.AddOwner(); /// /// Defines the property. /// - public static readonly AttachedProperty FontFamilyProperty = + public static readonly StyledProperty FontFamilyProperty = TextElement.FontFamilyProperty.AddOwner(); /// /// Defines the property. /// - public static readonly AttachedProperty FontSizeProperty = + public static readonly StyledProperty FontSizeProperty = TextElement.FontSizeProperty.AddOwner(); /// /// Defines the property. /// - public static readonly AttachedProperty FontStyleProperty = + public static readonly StyledProperty FontStyleProperty = TextElement.FontStyleProperty.AddOwner(); /// /// Defines the property. /// - public static readonly AttachedProperty FontWeightProperty = + public static readonly StyledProperty FontWeightProperty = TextElement.FontWeightProperty.AddOwner(); /// /// Defines the property. /// - public static readonly AttachedProperty FontStretchProperty = + public static readonly StyledProperty FontStretchProperty = TextElement.FontStretchProperty.AddOwner(); /// /// Defines the property /// - public static readonly AttachedProperty TextAlignmentProperty = + public static readonly StyledProperty TextAlignmentProperty = TextBlock.TextAlignmentProperty.AddOwner(); /// /// Defines the property /// - public static readonly AttachedProperty TextWrappingProperty = + public static readonly StyledProperty TextWrappingProperty = TextBlock.TextWrappingProperty.AddOwner(); /// /// Defines the property /// - public static readonly AttachedProperty TextTrimmingProperty = + public static readonly StyledProperty TextTrimmingProperty = TextBlock.TextTrimmingProperty.AddOwner(); /// /// Defines the property /// - public static readonly AttachedProperty LineHeightProperty = + public static readonly StyledProperty LineHeightProperty = TextBlock.LineHeightProperty.AddOwner(); /// /// Defines the property /// - public static readonly AttachedProperty MaxLinesProperty = + public static readonly StyledProperty MaxLinesProperty = TextBlock.MaxLinesProperty.AddOwner(); /// From b34095fda37618cde431112f0abc3be3056f5ac2 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Fri, 13 May 2022 23:53:26 +0200 Subject: [PATCH 322/416] Optimize memory usage of styles and fix ordering issue in devtools. --- src/Avalonia.Base/Styling/IStyleInstance.cs | 5 +++ .../Styling/PropertySetterInstance.cs | 10 +++--- ...e.cs => PropertySetterTemplateInstance.cs} | 31 ++++++++-------- src/Avalonia.Base/Styling/Setter.cs | 9 ----- src/Avalonia.Base/Styling/StyleInstance.cs | 35 ++++++++++--------- .../ViewModels/ControlDetailsViewModel.cs | 3 +- 6 files changed, 46 insertions(+), 47 deletions(-) rename src/Avalonia.Base/Styling/{PropertySetterLazyInstance.cs => PropertySetterTemplateInstance.cs} (79%) diff --git a/src/Avalonia.Base/Styling/IStyleInstance.cs b/src/Avalonia.Base/Styling/IStyleInstance.cs index 8ddb989bc0..d4f7510eb3 100644 --- a/src/Avalonia.Base/Styling/IStyleInstance.cs +++ b/src/Avalonia.Base/Styling/IStyleInstance.cs @@ -14,6 +14,11 @@ namespace Avalonia.Styling /// IStyle Source { get; } + /// + /// Gets a value indicating whether this style has an activator. + /// + bool HasActivator { get; } + /// /// Gets a value indicating whether this style is active. /// diff --git a/src/Avalonia.Base/Styling/PropertySetterInstance.cs b/src/Avalonia.Base/Styling/PropertySetterInstance.cs index 48f462d006..c4e8f47e67 100644 --- a/src/Avalonia.Base/Styling/PropertySetterInstance.cs +++ b/src/Avalonia.Base/Styling/PropertySetterInstance.cs @@ -44,7 +44,7 @@ namespace Avalonia.Styling { if (hasActivator) { - if (_styledProperty is object) + if (_styledProperty is not null) { _subscription = _target.Bind(_styledProperty, this, BindingPriority.StyleTrigger); } @@ -55,13 +55,15 @@ namespace Avalonia.Styling } else { - if (_styledProperty is object) + var target = (AvaloniaObject) _target; + + if (_styledProperty is not null) { - _subscription = _target.SetValue(_styledProperty!, _value, BindingPriority.Style); + _subscription = target.SetValue(_styledProperty!, _value, BindingPriority.Style); } else { - _target.SetValue(_directProperty!, _value); + target.SetValue(_directProperty!, _value); } } } diff --git a/src/Avalonia.Base/Styling/PropertySetterLazyInstance.cs b/src/Avalonia.Base/Styling/PropertySetterTemplateInstance.cs similarity index 79% rename from src/Avalonia.Base/Styling/PropertySetterLazyInstance.cs rename to src/Avalonia.Base/Styling/PropertySetterTemplateInstance.cs index 92653d0064..0f6efef1be 100644 --- a/src/Avalonia.Base/Styling/PropertySetterLazyInstance.cs +++ b/src/Avalonia.Base/Styling/PropertySetterTemplateInstance.cs @@ -11,42 +11,42 @@ namespace Avalonia.Styling /// evaluated. /// /// The target property type. - internal class PropertySetterLazyInstance : SingleSubscriberObservableBase>, + internal class PropertySetterTemplateInstance : SingleSubscriberObservableBase>, ISetterInstance { private readonly IStyleable _target; private readonly StyledPropertyBase? _styledProperty; private readonly DirectPropertyBase? _directProperty; - private readonly Func _valueFactory; + private readonly ITemplate _template; private BindingValue _value; private IDisposable? _subscription; private bool _isActive; - public PropertySetterLazyInstance( + public PropertySetterTemplateInstance( IStyleable target, StyledPropertyBase property, - Func valueFactory) + ITemplate template) { _target = target; _styledProperty = property; - _valueFactory = valueFactory; + _template = template; } - public PropertySetterLazyInstance( + public PropertySetterTemplateInstance( IStyleable target, DirectPropertyBase property, - Func valueFactory) + ITemplate template) { _target = target; _directProperty = property; - _valueFactory = valueFactory; + _template = template; } public void Start(bool hasActivator) { _isActive = !hasActivator; - if (_styledProperty is object) + if (_styledProperty is not null) { var priority = hasActivator ? BindingPriority.StyleTrigger : BindingPriority.Style; _subscription = _target.Bind(_styledProperty, this, priority); @@ -77,7 +77,7 @@ namespace Avalonia.Styling public override void Dispose() { - if (_subscription is object) + if (_subscription is not null) { var sub = _subscription; _subscription = null; @@ -85,7 +85,7 @@ namespace Avalonia.Styling } else if (_isActive) { - if (_styledProperty is object) + if (_styledProperty is not null) { _target.ClearValue(_styledProperty); } @@ -101,22 +101,21 @@ namespace Avalonia.Styling protected override void Subscribed() => PublishNext(); protected override void Unsubscribed() { } - private T GetValue() + private void EnsureTemplate() { if (_value.HasValue) { - return _value.Value; + return; } - _value = _valueFactory(); - return _value.Value; + _value = (T) _template.Build(); } private void PublishNext() { if (_isActive) { - GetValue(); + EnsureTemplate(); PublishNext(_value); } else diff --git a/src/Avalonia.Base/Styling/Setter.cs b/src/Avalonia.Base/Styling/Setter.cs index b4b3399022..d989bb0706 100644 --- a/src/Avalonia.Base/Styling/Setter.cs +++ b/src/Avalonia.Base/Styling/Setter.cs @@ -1,9 +1,7 @@ using System; using Avalonia.Animation; using Avalonia.Data; -using Avalonia.Data.Core; using Avalonia.Metadata; -using Avalonia.Utilities; #nullable enable @@ -70,12 +68,5 @@ namespace Avalonia.Styling return Property.CreateSetterInstance(target, Value); } - - private struct SetterVisitorData - { - public IStyleable target; - public object? value; - public ISetterInstance? result; - } } } diff --git a/src/Avalonia.Base/Styling/StyleInstance.cs b/src/Avalonia.Base/Styling/StyleInstance.cs index 830cf49a0d..db96da6821 100644 --- a/src/Avalonia.Base/Styling/StyleInstance.cs +++ b/src/Avalonia.Base/Styling/StyleInstance.cs @@ -11,10 +11,10 @@ namespace Avalonia.Styling /// /// A which has been instanced on a control. /// - internal class StyleInstance : IStyleInstance, IStyleActivatorSink + internal sealed class StyleInstance : IStyleInstance, IStyleActivatorSink { - private readonly List? _setters; - private readonly List? _animations; + private readonly ISetterInstance[]? _setters; + private readonly IDisposable[]? _animations; private readonly IStyleActivator? _activator; private readonly Subject? _animationTrigger; @@ -30,41 +30,42 @@ namespace Avalonia.Styling _activator = activator; IsActive = _activator is null; - if (setters is object) + if (setters is not null) { var setterCount = setters.Count; - _setters = new List(setterCount); + _setters = new ISetterInstance[setterCount]; for (var i = 0; i < setterCount; ++i) { - _setters.Add(setters[i].Instance(Target)); + _setters[i] = setters[i].Instance(Target); } } - if (animations is object && target is Animatable animatable) + if (animations is not null && target is Animatable animatable) { var animationsCount = animations.Count; - _animations = new List(animationsCount); + _animations = new IDisposable[animationsCount]; _animationTrigger = new Subject(); for (var i = 0; i < animationsCount; ++i) { - _animations.Add(animations[i].Apply(animatable, null, _animationTrigger)); + _animations[i] = animations[i].Apply(animatable, null, _animationTrigger); } } } + public bool HasActivator => _activator is not null; public bool IsActive { get; private set; } public IStyle Source { get; } public IStyleable Target { get; } public void Start() { - var hasActivator = _activator is object; + var hasActivator = HasActivator; - if (_setters is object) + if (_setters is not null) { foreach (var setter in _setters) { @@ -76,7 +77,7 @@ namespace Avalonia.Styling { _activator!.Subscribe(this, 0); } - else if (_animationTrigger != null) + else if (_animationTrigger is not null) { _animationTrigger.OnNext(true); } @@ -84,7 +85,7 @@ namespace Avalonia.Styling public void Dispose() { - if (_setters is object) + if (_setters is not null) { foreach (var setter in _setters) { @@ -92,11 +93,11 @@ namespace Avalonia.Styling } } - if (_animations is object) + if (_animations is not null) { - foreach (var subscripion in _animations) + foreach (var subscription in _animations) { - subscripion.Dispose(); + subscription.Dispose(); } } @@ -111,7 +112,7 @@ namespace Avalonia.Styling _animationTrigger?.OnNext(value); - if (_setters is object) + if (_setters is not null) { if (IsActive) { diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs index a1fd425571..e383c160e3 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs @@ -60,7 +60,8 @@ namespace Avalonia.Diagnostics.ViewModels var styleDiagnostics = styledElement.GetStyleDiagnostics(); - foreach (var appliedStyle in styleDiagnostics.AppliedStyles) + // We need to place styles without activator first, such styles will be overwritten by ones with activators. + foreach (var appliedStyle in styleDiagnostics.AppliedStyles.OrderBy(s => s.HasActivator)) { var styleSource = appliedStyle.Source; From 6c7dc426fcec124fcb2edb75be0ec1fa0bdb0afc Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Fri, 13 May 2022 23:59:09 +0200 Subject: [PATCH 323/416] Missing property changes. --- src/Avalonia.Base/DirectPropertyBase.cs | 5 ++--- src/Avalonia.Base/StyledPropertyBase.cs | 8 +++----- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/Avalonia.Base/DirectPropertyBase.cs b/src/Avalonia.Base/DirectPropertyBase.cs index 9c1ffce24c..efcb7dfecb 100644 --- a/src/Avalonia.Base/DirectPropertyBase.cs +++ b/src/Avalonia.Base/DirectPropertyBase.cs @@ -2,7 +2,6 @@ using Avalonia.Data; using Avalonia.Reactive; using Avalonia.Styling; -using Avalonia.Utilities; namespace Avalonia { @@ -188,10 +187,10 @@ namespace Avalonia } else if (value is ITemplate template && !typeof(ITemplate).IsAssignableFrom(PropertyType)) { - return new PropertySetterLazyInstance( + return new PropertySetterTemplateInstance( target, this, - () => (TValue)template.Build()); + template); } else { diff --git a/src/Avalonia.Base/StyledPropertyBase.cs b/src/Avalonia.Base/StyledPropertyBase.cs index dd5eb703ea..da607720ff 100644 --- a/src/Avalonia.Base/StyledPropertyBase.cs +++ b/src/Avalonia.Base/StyledPropertyBase.cs @@ -1,9 +1,7 @@ using System; -using System.Diagnostics; using Avalonia.Data; using Avalonia.Reactive; using Avalonia.Styling; -using Avalonia.Utilities; namespace Avalonia { @@ -12,7 +10,7 @@ namespace Avalonia /// public abstract class StyledPropertyBase : AvaloniaProperty, IStyledPropertyAccessor { - private bool _inherits; + private readonly bool _inherits; /// /// Initializes a new instance of the class. @@ -243,10 +241,10 @@ namespace Avalonia } else if (value is ITemplate template && !typeof(ITemplate).IsAssignableFrom(PropertyType)) { - return new PropertySetterLazyInstance( + return new PropertySetterTemplateInstance( target, this, - () => (TValue)template.Build()); + template); } else { From 90b67c3b9f73f2dfc6698f5f43486b0367c986cf Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Sat, 14 May 2022 01:22:34 +0200 Subject: [PATCH 324/416] Modify test setup so actual framework classes are used. --- tests/Avalonia.Base.UnitTests/Styling/SetterTests.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/tests/Avalonia.Base.UnitTests/Styling/SetterTests.cs b/tests/Avalonia.Base.UnitTests/Styling/SetterTests.cs index ef2586fcb7..ed4c78aa3e 100644 --- a/tests/Avalonia.Base.UnitTests/Styling/SetterTests.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/SetterTests.cs @@ -91,16 +91,13 @@ namespace Avalonia.Base.UnitTests.Styling [Fact] public void Setter_Should_Apply_Value_Without_Activator_With_Style_Priority() { - var control = new Mock(); - var style = Mock.Of - - - - - - From 3fccb1417459a5cb96fedb156895af11ec460a0c Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 19 May 2022 16:43:58 +0200 Subject: [PATCH 381/416] Added style caching to Style children. Extracted out the caching code from `Styles` and reuse for `Style.Children`. --- src/Avalonia.Base/Styling/Style.cs | 11 +++-- src/Avalonia.Base/Styling/StyleCache.cs | 58 +++++++++++++++++++++++++ src/Avalonia.Base/Styling/Styles.cs | 41 ++--------------- 3 files changed, 66 insertions(+), 44 deletions(-) create mode 100644 src/Avalonia.Base/Styling/StyleCache.cs diff --git a/src/Avalonia.Base/Styling/Style.cs b/src/Avalonia.Base/Styling/Style.cs index a8707e00c0..8fcf5eec8a 100644 --- a/src/Avalonia.Base/Styling/Style.cs +++ b/src/Avalonia.Base/Styling/Style.cs @@ -16,6 +16,7 @@ namespace Avalonia.Styling private IResourceDictionary? _resources; private List? _setters; private List? _animations; + private StyleCache? _childCache; /// /// Initializes a new instance of the class. @@ -124,12 +125,10 @@ namespace Avalonia.Styling if (_children is not null) { - foreach (var child in _children) - { - var childResult = child.TryAttach(target, host); - if (childResult > result) - result = childResult; - } + _childCache ??= new StyleCache(); + var childResult = _childCache.TryAttach(_children, target, host); + if (childResult > result) + result = childResult; } return result; diff --git a/src/Avalonia.Base/Styling/StyleCache.cs b/src/Avalonia.Base/Styling/StyleCache.cs new file mode 100644 index 0000000000..3285476880 --- /dev/null +++ b/src/Avalonia.Base/Styling/StyleCache.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; + +namespace Avalonia.Styling +{ + /// + /// Simple cache for improving performance of applying styles. + /// + /// + /// Maps to a list of styles that are known be be possible + /// matches. + /// + internal class StyleCache : Dictionary?> + { + public SelectorMatchResult TryAttach(IList styles, IStyleable target, IStyleHost? host) + { + if (TryGetValue(target.StyleKey, out var cached)) + { + if (cached is object) + { + var result = SelectorMatchResult.NeverThisType; + + foreach (var style in cached) + { + var childResult = style.TryAttach(target, host); + if (childResult > result) + result = childResult; + } + + return result; + } + else + { + return SelectorMatchResult.NeverThisType; + } + } + else + { + List? matches = null; + + foreach (var child in styles) + { + if (child.TryAttach(target, host) != SelectorMatchResult.NeverThisType) + { + matches ??= new List(); + matches.Add(child); + } + } + + Add(target.StyleKey, matches); + + return matches is null ? + SelectorMatchResult.NeverThisType : + SelectorMatchResult.AlwaysThisType; + } + } + } +} diff --git a/src/Avalonia.Base/Styling/Styles.cs b/src/Avalonia.Base/Styling/Styles.cs index d79081152e..7c0bc4ad7f 100644 --- a/src/Avalonia.Base/Styling/Styles.cs +++ b/src/Avalonia.Base/Styling/Styles.cs @@ -20,7 +20,7 @@ namespace Avalonia.Styling private readonly AvaloniaList _styles = new AvaloniaList(); private IResourceHost? _owner; private IResourceDictionary? _resources; - private Dictionary?>? _cache; + private StyleCache? _cache; public Styles() { @@ -111,43 +111,8 @@ namespace Avalonia.Styling public SelectorMatchResult TryAttach(IStyleable target, IStyleHost? host) { - _cache ??= new Dictionary?>(); - - if (_cache.TryGetValue(target.StyleKey, out var cached)) - { - if (cached is object) - { - foreach (var style in cached) - { - style.TryAttach(target, host); - } - - return SelectorMatchResult.AlwaysThisType; - } - else - { - return SelectorMatchResult.NeverThisType; - } - } - else - { - List? matches = null; - - foreach (var child in this) - { - if (child.TryAttach(target, host) != SelectorMatchResult.NeverThisType) - { - matches ??= new List(); - matches.Add(child); - } - } - - _cache.Add(target.StyleKey, matches); - - return matches is null ? - SelectorMatchResult.NeverThisType : - SelectorMatchResult.AlwaysThisType; - } + _cache ??= new StyleCache(); + return _cache.TryAttach(this, target, host); } /// From 0a7f34f4c681d0e445d91637cfc54108006c56d9 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 19 May 2022 20:25:14 +0200 Subject: [PATCH 382/416] Use nested styles in CheckBox template. --- .../Controls/CheckBox.xaml | 603 +++++++++--------- 1 file changed, 315 insertions(+), 288 deletions(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/CheckBox.xaml b/src/Avalonia.Themes.Fluent/Controls/CheckBox.xaml index f16e1ed99f..2d70a35b13 100644 --- a/src/Avalonia.Themes.Fluent/Controls/CheckBox.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/CheckBox.xaml @@ -1,294 +1,321 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 75a10efcf10860c3644d6a2639f2b70355bd7617 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 19 May 2022 23:55:48 -0400 Subject: [PATCH 383/416] Avoid static references in DataGrid fluent theme --- .../Themes/Fluent.xaml | 141 +++++++----------- 1 file changed, 53 insertions(+), 88 deletions(-) diff --git a/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml b/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml index 8243d9c22d..2baa8c88c9 100644 --- a/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml +++ b/src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml @@ -1,87 +1,51 @@ - + - 12,0,12,0 - 0.6 0.8 + 12,0,12,0 M1875 1011l-787 787v-1798h-128v1798l-787 -787l-90 90l941 941l941 -941z M1965 947l-941 -941l-941 941l90 90l787 -787v1798h128v-1798l787 787z M515 93l930 931l-930 931l90 90l1022 -1021l-1022 -1021z M1939 1581l90 -90l-1005 -1005l-1005 1005l90 90l915 -915z + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Opacity="0.4" + Color="{DynamicResource SystemBaseMediumLowColor}" /> + + + From 55b19b445f210d579fad8105baaa9acbc3a12f9e Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 20 May 2022 16:59:53 +0200 Subject: [PATCH 384/416] Remove unused vars. --- .../Avalonia.Base.UnitTests/Styling/SelectorTests_Nesting.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Nesting.cs b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Nesting.cs index c5f779cbbb..d49fcf03a2 100644 --- a/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Nesting.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Nesting.cs @@ -222,7 +222,6 @@ namespace Avalonia.Base.UnitTests.Styling [Fact] public void Adding_Child_With_No_Nesting_Selector_Fails() { - var control = new Control1(); var parent = new Style(x => x.OfType()); var child = new Style(x => x.Class("foo")); @@ -232,7 +231,6 @@ namespace Avalonia.Base.UnitTests.Styling [Fact] public void Adding_Combinator_Selector_Child_With_No_Nesting_Selector_Fails() { - var control = new Control1(); var parent = new Style(x => x.OfType()); var child = new Style(x => x.Class("foo").Descendant().Class("bar")); @@ -242,7 +240,6 @@ namespace Avalonia.Base.UnitTests.Styling [Fact] public void Adding_Or_Selector_Child_With_No_Nesting_Selector_Fails() { - var control = new Control1(); var parent = new Style(x => x.OfType()); var child = new Style(x => Selectors.Or( x.Nesting().Class("foo"), @@ -254,7 +251,6 @@ namespace Avalonia.Base.UnitTests.Styling [Fact] public void Can_Add_Child_Without_Nesting_Selector_To_Style_Without_Selector() { - var control = new Control1(); var parent = new Style(); var child = new Style(x => x.Class("foo")); From 060ec9694b62d74a21ccef2420feb53c3ccb78cf Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 20 May 2022 19:50:41 +0100 Subject: [PATCH 385/416] Merge pull request #8165 from AvaloniaUI/fixes/position-osx Fix more OSX regressions --- native/Avalonia.Native/src/OSX/PopupImpl.mm | 17 ----------------- native/Avalonia.Native/src/OSX/WindowBaseImpl.h | 1 + .../Avalonia.Native/src/OSX/WindowBaseImpl.mm | 15 +++++++++------ 3 files changed, 10 insertions(+), 23 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/PopupImpl.mm b/native/Avalonia.Native/src/OSX/PopupImpl.mm index cf3ecefb3c..cb52047148 100644 --- a/native/Avalonia.Native/src/OSX/PopupImpl.mm +++ b/native/Avalonia.Native/src/OSX/PopupImpl.mm @@ -34,23 +34,6 @@ protected: return NSWindowStyleMaskBorderless; } - virtual HRESULT Resize(double x, double y, AvnPlatformResizeReason reason) override - { - START_COM_CALL; - - @autoreleasepool - { - if (Window != nullptr) - { - [Window setContentSize:NSSize{x, y}]; - - [Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(lastPositionSet))]; - } - - return S_OK; - } - } - public: virtual bool ShouldTakeFocusOnShow() override { diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index 0e482f9f30..e3e646ff2a 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -35,6 +35,7 @@ BEGIN_INTERFACE_MAP() ComPtr _glContext; NSObject *renderTarget; AvnPoint lastPositionSet; + bool hasPosition; NSSize lastSize; NSSize lastMinSize; NSSize lastMaxSize; diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index b84e999825..bc512f80d8 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -30,8 +30,8 @@ WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl) View = [[AvnView alloc] initWithParent:this]; StandardContainer = [[AutoFitContentView new] initWithContent:View]; - lastPositionSet.X = -1; - lastPositionSet.Y = -1; + lastPositionSet = { 0, 0 }; + hasPosition = false; lastSize = NSSize { 100, 100 }; lastMaxSize = NSSize { CGFLOAT_MAX, CGFLOAT_MAX}; lastMinSize = NSSize { 0, 0 }; @@ -92,9 +92,12 @@ HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) { CreateNSWindow(isDialog); InitialiseNSWindow(); - if(lastPositionSet.X >= 0 && lastPositionSet.Y >= 0) + if(hasPosition) { SetPosition(lastPositionSet); + } else + { + [Window center]; } UpdateStyle(); @@ -288,12 +291,12 @@ HRESULT WindowBaseImpl::Resize(double x, double y, AvnPlatformResizeReason reaso } @try { + lastSize = NSSize {x, y}; + if (!_shown) { BaseEvents->Resized(AvnSize{x, y}, reason); } - lastSize = NSSize {x, y}; - if(Window != nullptr) { [Window setContentSize:lastSize]; } @@ -385,6 +388,7 @@ HRESULT WindowBaseImpl::SetPosition(AvnPoint point) { @autoreleasepool { lastPositionSet = point; + hasPosition = true; if(Window != nullptr) { [Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(point))]; @@ -577,7 +581,6 @@ void WindowBaseImpl::InitialiseNSWindow() { [Window setContentMaxSize:lastMaxSize]; [Window setOpaque:false]; - [Window center]; if (lastMenu != nullptr) { [GetWindowProtocol() applyMenu:lastMenu]; From 4845c10d881d7aa3bdf10e4e417f0755a6b0c2c4 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Sat, 21 May 2022 11:36:57 +0200 Subject: [PATCH 386/416] fix(ExpressionNode): ensure _subscriber do not change during ValueChanged --- src/Avalonia.Base/Data/Core/ExpressionNode.cs | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/Avalonia.Base/Data/Core/ExpressionNode.cs b/src/Avalonia.Base/Data/Core/ExpressionNode.cs index 54785f18e8..4f755ff140 100644 --- a/src/Avalonia.Base/Data/Core/ExpressionNode.cs +++ b/src/Avalonia.Base/Data/Core/ExpressionNode.cs @@ -98,36 +98,36 @@ namespace Avalonia.Data.Core private void ValueChanged(object? value, bool notify) { - if (_subscriber is null) - return; - - var notification = value as BindingNotification; - - if (notification == null) + if (_subscriber is { } subscriber) { - LastValue = value != null ? new WeakReference(value) : NullReference; + var notification = value as BindingNotification; + var next = Next; - if (Next != null) + if (notification == null) { - Next.Target = LastValue; + LastValue = value != null ? new WeakReference(value) : NullReference; + if (next != null) + { + next.Target = LastValue; + } + else if (notify) + { + subscriber(value); + } } - else if (notify) + else { - _subscriber(value); - } - } - else - { - LastValue = notification.Value != null ? new WeakReference(notification.Value) : NullReference; + LastValue = notification.Value != null ? new WeakReference(notification.Value) : NullReference; - if (Next != null) - { - Next.Target = LastValue; - } + if (next != null) + { + next.Target = LastValue; + } - if (Next == null || notification.Error != null) - { - _subscriber(value); + if (next == null || notification.Error != null) + { + subscriber(value); + } } } } From 81eb964ae6082bd4aac045000146f6d756fb8683 Mon Sep 17 00:00:00 2001 From: Takoooooo Date: Mon, 23 May 2022 19:19:19 +0300 Subject: [PATCH 387/416] Fix Menu selection to match UWP. --- .../Platform/DefaultMenuInteractionHandler.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs index 3b38750de7..66b2a84e6b 100644 --- a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs +++ b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs @@ -56,6 +56,7 @@ namespace Avalonia.Controls.Platform Menu.AddHandler(Avalonia.Controls.Menu.MenuOpenedEvent, this.MenuOpened); Menu.AddHandler(MenuItem.PointerEnterItemEvent, PointerEnter); Menu.AddHandler(MenuItem.PointerLeaveItemEvent, PointerLeave); + Menu.AddHandler(InputElement.PointerMovedEvent, PointerMoved); _root = Menu.VisualRoot; @@ -91,6 +92,7 @@ namespace Avalonia.Controls.Platform Menu.RemoveHandler(Avalonia.Controls.Menu.MenuOpenedEvent, this.MenuOpened); Menu.RemoveHandler(MenuItem.PointerEnterItemEvent, PointerEnter); Menu.RemoveHandler(MenuItem.PointerLeaveItemEvent, PointerLeave); + Menu.RemoveHandler(InputElement.PointerMovedEvent, PointerMoved); if (_root is InputElement inputRoot) { @@ -340,6 +342,21 @@ namespace Avalonia.Controls.Platform } } + protected internal virtual void PointerMoved(object? sender, PointerEventArgs e) + { + var item = GetMenuItem(e.Source as IControl) as MenuItem; + if (item?.TransformedBounds == null) + { + return; + } + var point = e.GetCurrentPoint(null); + + if (point.Properties.IsLeftButtonPressed && item.TransformedBounds.Value.Contains(point.Position) == false) + { + e.Pointer.Capture(null); + } + } + protected internal virtual void PointerLeave(object? sender, PointerEventArgs e) { var item = GetMenuItem(e.Source as IControl); From ac24ee467ad565accf6bcd47f6901d6db07c0696 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Tue, 24 May 2022 01:35:08 -0400 Subject: [PATCH 388/416] Update TopLevelImpl.cs --- .../Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs index 8a475676a5..65a9adc937 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs @@ -40,7 +40,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform _gl = GlPlatformSurface.TryCreate(this); _framebuffer = new FramebufferManager(this); - RenderScaling = (int)_view.Scaling; + RenderScaling = _view.Scaling; MaxClientSize = new PixelSize(_view.Resources.DisplayMetrics.WidthPixels, _view.Resources.DisplayMetrics.HeightPixels).ToSize(RenderScaling); From 1c2da897e5bdea98100ef1f0c4f6ce0364010d76 Mon Sep 17 00:00:00 2001 From: Takoooooo Date: Tue, 24 May 2022 15:18:56 +0300 Subject: [PATCH 389/416] Add comment. --- src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs index 66b2a84e6b..3a6d06f150 100644 --- a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs +++ b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs @@ -344,6 +344,7 @@ namespace Avalonia.Controls.Platform protected internal virtual void PointerMoved(object? sender, PointerEventArgs e) { + // HACK: #8179 needs to be addressed to correctly implement it in the PointerPressed method. var item = GetMenuItem(e.Source as IControl) as MenuItem; if (item?.TransformedBounds == null) { From 0654d636932c863897121e3d66c6226c7a1cdd94 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 24 May 2022 15:13:19 +0200 Subject: [PATCH 390/416] Added failing tests for #8178. --- tests/Avalonia.LeakTests/ControlTests.cs | 66 ++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/tests/Avalonia.LeakTests/ControlTests.cs b/tests/Avalonia.LeakTests/ControlTests.cs index 3a5a8f1474..09d6764bae 100644 --- a/tests/Avalonia.LeakTests/ControlTests.cs +++ b/tests/Avalonia.LeakTests/ControlTests.cs @@ -6,6 +6,7 @@ using System.Runtime.Remoting.Contexts; using Avalonia.Controls; using Avalonia.Controls.Shapes; using Avalonia.Controls.Templates; +using Avalonia.Data; using Avalonia.Diagnostics; using Avalonia.Input; using Avalonia.Layout; @@ -661,6 +662,70 @@ namespace Avalonia.LeakTests } } + [Fact] + public void ElementName_Binding_In_DataTemplate_Is_Freed() + { + using (Start()) + { + var items = new ObservableCollection(Enumerable.Range(0, 10)); + NameScope ns; + TextBox tb; + ListBox lb; + var window = new Window + { + [NameScope.NameScopeProperty] = ns = new NameScope(), + Width = 100, + Height = 100, + Content = new StackPanel + { + Children = + { + (tb = new TextBox + { + Name = "tb", + Text = "foo", + }), + (lb = new ListBox + { + Items = items, + ItemTemplate = new FuncDataTemplate((_, _) => + new Canvas + { + Width = 10, + Height = 10, + [!Control.TagProperty] = new Binding + { + ElementName = "tb", + Path = "Text", + NameScope = new WeakReference(ns), + } + }) + }), + } + } + }; + + tb.RegisterInNameScope(ns); + + window.Show(); + window.LayoutManager.ExecuteInitialLayoutPass(); + + Assert.Equal(10, lb.ItemContainerGenerator.Containers.Count()); + + var item0 = (ListBoxItem)lb.ItemContainerGenerator.Containers.First().ContainerControl; + var canvas0 = (Canvas)item0.Presenter.Child; + Assert.Equal("foo", canvas0.Tag); + + items.Clear(); + window.LayoutManager.ExecuteLayoutPass(); + + Assert.Empty(lb.ItemContainerGenerator.Containers); + + dotMemory.Check(memory => + Assert.Equal(0, memory.GetObjects(where => where.Type.Is()).ObjectsCount)); + } + } + private IDisposable Start() { return UnitTestApplication.Start(TestServices.StyledWindow.With( @@ -669,6 +734,7 @@ namespace Avalonia.LeakTests inputManager: new InputManager())); } + private class Node { public string Name { get; set; } From 69f5f7a48e695bf54d33ba49d5ae74b7d93be22a Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 24 May 2022 15:17:52 +0200 Subject: [PATCH 391/416] Use weak events in AvaloniaProperty plugin. Fixes #8178. --- .../Plugins/AvaloniaPropertyAccessorPlugin.cs | 40 +++++++++++++------ src/Avalonia.Base/Utilities/WeakEvents.cs | 15 ++++++- 2 files changed, 42 insertions(+), 13 deletions(-) diff --git a/src/Avalonia.Base/Data/Core/Plugins/AvaloniaPropertyAccessorPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/AvaloniaPropertyAccessorPlugin.cs index 3c4120ad0b..cc6d92ceb7 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/AvaloniaPropertyAccessorPlugin.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/AvaloniaPropertyAccessorPlugin.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.ExceptionServices; +using Avalonia.Utilities; namespace Avalonia.Data.Core.Plugins { @@ -60,11 +61,10 @@ namespace Avalonia.Data.Core.Plugins return AvaloniaPropertyRegistry.Instance.FindRegistered(o, propertyName); } - private class Accessor : PropertyAccessorBase, IObserver + private class Accessor : PropertyAccessorBase, IWeakEventSubscriber { private readonly WeakReference _reference; private readonly AvaloniaProperty _property; - private IDisposable? _subscription; public Accessor(WeakReference reference, AvaloniaProperty property) { @@ -95,29 +95,45 @@ namespace Avalonia.Data.Core.Plugins return false; } - protected override void SubscribeCore() + void IWeakEventSubscriber. + OnEvent(object? notifyPropertyChanged, WeakEvent ev, AvaloniaPropertyChangedEventArgs e) { - _subscription = Instance?.GetObservable(_property).Subscribe(this); + if (e.Property == _property) + { + SendCurrentValue(); + } } - protected override void UnsubscribeCore() + protected override void SubscribeCore() { - _subscription?.Dispose(); - _subscription = null; + SubscribeToChanges(); + SendCurrentValue(); } - void IObserver.OnCompleted() + protected override void UnsubscribeCore() { + var instance = Instance; + + if (instance != null) + WeakEvents.AvaloniaPropertyChanged.Unsubscribe(instance, this); } - void IObserver.OnError(Exception error) + private void SendCurrentValue() { - ExceptionDispatchInfo.Capture(error).Throw(); + try + { + var value = Value; + PublishValue(value); + } + catch { } } - void IObserver.OnNext(object? value) + private void SubscribeToChanges() { - PublishValue(value); + var instance = Instance; + + if (instance != null) + WeakEvents.AvaloniaPropertyChanged.Subscribe(instance, this); } } } diff --git a/src/Avalonia.Base/Utilities/WeakEvents.cs b/src/Avalonia.Base/Utilities/WeakEvents.cs index d1b5e7f12d..6da899bab2 100644 --- a/src/Avalonia.Base/Utilities/WeakEvents.cs +++ b/src/Avalonia.Base/Utilities/WeakEvents.cs @@ -31,10 +31,23 @@ public class WeakEvents return () => s.PropertyChanged -= handler; }); + + /// + /// Represents PropertyChanged event from + /// + public static readonly WeakEvent + AvaloniaPropertyChanged = WeakEvent.Register( + (s, h) => + { + EventHandler handler = (_, e) => h(s, e); + s.PropertyChanged += handler; + return () => s.PropertyChanged -= handler; + }); + /// /// Represents CanExecuteChanged event from /// public static readonly WeakEvent CommandCanExecuteChanged = WeakEvent.Register((s, h) => s.CanExecuteChanged += h, (s, h) => s.CanExecuteChanged -= h); -} \ No newline at end of file +} From e4bbebac7d0e1f97f9864439f67e50e797110a85 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 24 May 2022 20:48:19 +0100 Subject: [PATCH 392/416] restore _canBecomeKeyWindow flag, important to distinguish behavior of a window and a popup. --- native/Avalonia.Native/src/OSX/AvnWindow.mm | 34 +++++++++++++-------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/AvnWindow.mm b/native/Avalonia.Native/src/OSX/AvnWindow.mm index 54bfe6e38a..7c1f548786 100644 --- a/native/Avalonia.Native/src/OSX/AvnWindow.mm +++ b/native/Avalonia.Native/src/OSX/AvnWindow.mm @@ -31,6 +31,7 @@ ComPtr _parent; bool _closed; bool _isEnabled; + bool _canBecomeKeyWindow; bool _isExtended; AvnMenu* _menu; } @@ -216,29 +217,38 @@ -(BOOL)canBecomeKeyWindow { - // If the window has a child window being shown as a dialog then don't allow it to become the key window. - for(NSWindow* uch in [self childWindows]) + if(_canBecomeKeyWindow) { - if (![uch conformsToProtocol:@protocol(AvnWindowProtocol)]) + // If the window has a child window being shown as a dialog then don't allow it to become the key window. + for(NSWindow* uch in [self childWindows]) { - continue; - } + if (![uch conformsToProtocol:@protocol(AvnWindowProtocol)]) + { + continue; + } - id ch = (id ) uch; + id ch = (id ) uch; - return !ch.isDialog; - } + if(ch.isDialog) + return true; + } - return true; + return true; + } + + return false; } +#ifndef IS_NSPANEL -(BOOL)canBecomeMainWindow { -#ifdef IS_NSPANEL - return false; -#else return true; +} #endif + +-(void)setCanBecomeKeyWindow:(bool)value +{ + _canBecomeKeyWindow = value; } -(bool)shouldTryToHandleEvents From aa06e02c6d8bdd775350926da702d9fb29206c22 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 24 May 2022 20:48:41 +0100 Subject: [PATCH 393/416] arrange fields nicely. --- .../Avalonia.Native/src/OSX/WindowBaseImpl.h | 39 ++++++++++--------- .../Avalonia.Native/src/OSX/WindowBaseImpl.mm | 3 -- native/Avalonia.Native/src/OSX/WindowImpl.h | 3 ++ native/Avalonia.Native/src/OSX/WindowImpl.mm | 1 + 4 files changed, 24 insertions(+), 22 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index e3e646ff2a..a879fecc21 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -16,8 +16,6 @@ class WindowBaseImpl : public virtual ComObject, public virtual IAvnWindowBase, public INSWindowHolder { -private: - NSCursor *cursor; public: FORWARD_IUNKNOWN() @@ -28,23 +26,6 @@ BEGIN_INTERFACE_MAP() virtual ~WindowBaseImpl(); - AutoFitContentView *StandardContainer; - AvnView *View; - NSWindow * Window; - ComPtr BaseEvents; - ComPtr _glContext; - NSObject *renderTarget; - AvnPoint lastPositionSet; - bool hasPosition; - NSSize lastSize; - NSSize lastMinSize; - NSSize lastMaxSize; - AvnMenu* lastMenu; - NSString *_lastTitle; - - bool _shown; - bool _inResize; - WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl); virtual HRESULT ObtainNSWindowHandle(void **ret) override; @@ -128,6 +109,26 @@ private: void CreateNSWindow (bool isDialog); void CleanNSWindow (); void InitialiseNSWindow (); + + NSCursor *cursor; + ComPtr _glContext; + bool hasPosition; + NSSize lastSize; + NSSize lastMinSize; + NSSize lastMaxSize; + AvnMenu* lastMenu; + bool _inResize; + +protected: + AvnPoint lastPositionSet; + AutoFitContentView *StandardContainer; + bool _shown; + +public: + NSObject *renderTarget; + NSWindow * Window; + ComPtr BaseEvents; + AvnView *View; }; #endif //AVALONIA_NATIVE_OSX_WINDOWBASEIMPL_H diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index bc512f80d8..c97989556f 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -35,7 +35,6 @@ WindowBaseImpl::WindowBaseImpl(IAvnWindowBaseEvents *events, IAvnGlContext *gl) lastSize = NSSize { 100, 100 }; lastMaxSize = NSSize { CGFLOAT_MAX, CGFLOAT_MAX}; lastMinSize = NSSize { 0, 0 }; - _lastTitle = @""; Window = nullptr; lastMenu = nullptr; @@ -102,8 +101,6 @@ HRESULT WindowBaseImpl::Show(bool activate, bool isDialog) { UpdateStyle(); - [Window setTitle:_lastTitle]; - if (ShouldTakeFocusOnShow() && activate) { [Window orderFront:Window]; [Window makeKeyAndOrderFront:Window]; diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.h b/native/Avalonia.Native/src/OSX/WindowImpl.h index a4ee4f447c..dc82ee59c2 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowImpl.h @@ -91,6 +91,9 @@ BEGIN_INTERFACE_MAP() protected: virtual NSWindowStyleMask GetStyle() override; + +private: + NSString *_lastTitle; }; #endif //AVALONIA_NATIVE_OSX_WINDOWIMPL_H diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index 5b15b4cdfc..5bb739d369 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -19,6 +19,7 @@ WindowImpl::WindowImpl(IAvnWindowEvents *events, IAvnGlContext *gl) : WindowBase _inSetWindowState = false; _lastWindowState = Normal; _actualWindowState = Normal; + _lastTitle = @""; WindowEvents = events; [Window disableCursorRects]; [Window setTabbingMode:NSWindowTabbingModeDisallowed]; From a77f9d3e54d580a41290654b4872064b053bf1f6 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 24 May 2022 20:49:42 +0100 Subject: [PATCH 394/416] add setCanBecomeKeyWindow to protocol --- native/Avalonia.Native/src/OSX/WindowProtocol.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/native/Avalonia.Native/src/OSX/WindowProtocol.h b/native/Avalonia.Native/src/OSX/WindowProtocol.h index 92194706de..0e5c5869e7 100644 --- a/native/Avalonia.Native/src/OSX/WindowProtocol.h +++ b/native/Avalonia.Native/src/OSX/WindowProtocol.h @@ -22,4 +22,6 @@ -(void) setIsExtended:(bool)value; -(void) disconnectParent; -(bool) isDialog; -@end \ No newline at end of file + +-(void) setCanBecomeKeyWindow:(bool)value; +@end From 02b3bf253af52c3c08ade06fba827dce7d3cf93b Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 24 May 2022 20:50:39 +0100 Subject: [PATCH 395/416] add OnInitialiseNSWindow to allow inheritors to hook into NSWindow/Panel setup before being shown. --- native/Avalonia.Native/src/OSX/PopupImpl.mm | 8 +++-- .../Avalonia.Native/src/OSX/WindowBaseImpl.h | 2 ++ .../Avalonia.Native/src/OSX/WindowBaseImpl.mm | 7 +++++ native/Avalonia.Native/src/OSX/WindowImpl.h | 2 ++ native/Avalonia.Native/src/OSX/WindowImpl.mm | 29 ++++++++++--------- 5 files changed, 32 insertions(+), 16 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/PopupImpl.mm b/native/Avalonia.Native/src/OSX/PopupImpl.mm index cb52047148..3c5afd9424 100644 --- a/native/Avalonia.Native/src/OSX/PopupImpl.mm +++ b/native/Avalonia.Native/src/OSX/PopupImpl.mm @@ -26,13 +26,17 @@ private: PopupImpl(IAvnWindowEvents* events, IAvnGlContext* gl) : WindowBaseImpl(events, gl) { WindowEvents = events; - [Window setLevel:NSPopUpMenuWindowLevel]; } protected: virtual NSWindowStyleMask GetStyle() override { return NSWindowStyleMaskBorderless; } + + virtual void OnInitialiseNSWindow () override + { + [Window setLevel:NSPopUpMenuWindowLevel]; + } public: virtual bool ShouldTakeFocusOnShow() override @@ -54,4 +58,4 @@ extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events, IAvnGlContext* gl) IAvnPopup* ptr = dynamic_cast(new PopupImpl(events, gl)); return ptr; } -} \ No newline at end of file +} diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h index a879fecc21..83850e780c 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.h @@ -104,6 +104,8 @@ protected: virtual NSWindowStyleMask GetStyle(); void UpdateStyle(); + + virtual void OnInitialiseNSWindow (); private: void CreateNSWindow (bool isDialog); diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index c97989556f..022769bad0 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -567,6 +567,11 @@ void WindowBaseImpl::CreateNSWindow(bool isDialog) { } } +void WindowBaseImpl::OnInitialiseNSWindow() +{ + +} + void WindowBaseImpl::InitialiseNSWindow() { if(Window != nullptr) { [Window setContentView:StandardContainer]; @@ -586,6 +591,8 @@ void WindowBaseImpl::InitialiseNSWindow() { [GetWindowProtocol() showWindowMenuWithAppMenu]; } } + + OnInitialiseNSWindow(); } } diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.h b/native/Avalonia.Native/src/OSX/WindowImpl.h index dc82ee59c2..db19497b29 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.h +++ b/native/Avalonia.Native/src/OSX/WindowImpl.h @@ -88,6 +88,8 @@ BEGIN_INTERFACE_MAP() virtual HRESULT SetWindowState (AvnWindowState state) override; virtual bool IsDialog() override; + + virtual void OnInitialiseNSWindow() override; protected: virtual NSWindowStyleMask GetStyle() override; diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index 5bb739d369..d43a8beee4 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -21,9 +21,6 @@ WindowImpl::WindowImpl(IAvnWindowEvents *events, IAvnGlContext *gl) : WindowBase _actualWindowState = Normal; _lastTitle = @""; WindowEvents = events; - [Window disableCursorRects]; - [Window setTabbingMode:NSWindowTabbingModeDisallowed]; - [Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; } void WindowImpl::HideOrShowTrafficLights() { @@ -51,25 +48,29 @@ void WindowImpl::HideOrShowTrafficLights() { } } +void WindowImpl::OnInitialiseNSWindow(){ + [GetWindowProtocol() setCanBecomeKeyWindow:true]; + [Window disableCursorRects]; + [Window setTabbingMode:NSWindowTabbingModeDisallowed]; + [Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; + + [Window setTitle:_lastTitle]; + + if(_isClientAreaExtended) + { + [GetWindowProtocol() setIsExtended:true]; + SetExtendClientArea(true); + } +} + HRESULT WindowImpl::Show(bool activate, bool isDialog) { START_COM_CALL; @autoreleasepool { _isDialog = isDialog; - bool created = Window == nullptr; - WindowBaseImpl::Show(activate, isDialog); - if(created) - { - if(_isClientAreaExtended) - { - [GetWindowProtocol() setIsExtended:true]; - SetExtendClientArea(true); - } - } - HideOrShowTrafficLights(); return SetWindowState(_lastWindowState); From 30377966e6b0623df63b7f620cd5dcc39de7ac90 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 24 May 2022 21:02:52 +0100 Subject: [PATCH 396/416] fix incorrect return value. --- native/Avalonia.Native/src/OSX/AvnWindow.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native/Avalonia.Native/src/OSX/AvnWindow.mm b/native/Avalonia.Native/src/OSX/AvnWindow.mm index 7c1f548786..f51c693777 100644 --- a/native/Avalonia.Native/src/OSX/AvnWindow.mm +++ b/native/Avalonia.Native/src/OSX/AvnWindow.mm @@ -230,7 +230,7 @@ id ch = (id ) uch; if(ch.isDialog) - return true; + return false; } return true; From 25efb6bec6a1b2a72ecc245346279e4583f7f421 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 25 May 2022 11:07:09 +0200 Subject: [PATCH 397/416] Fix pooled list bug. Ported from upstream: https://github.com/jtmueller/Collections.Pooled/pull/44 --- src/Avalonia.Base/Collections/Pooled/PooledList.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Collections/Pooled/PooledList.cs b/src/Avalonia.Base/Collections/Pooled/PooledList.cs index 200a52fb0d..803b8d60dc 100644 --- a/src/Avalonia.Base/Collections/Pooled/PooledList.cs +++ b/src/Avalonia.Base/Collections/Pooled/PooledList.cs @@ -587,7 +587,7 @@ namespace Avalonia.Collections.Pooled if (size > 0 && _clearOnFree) { // Clear the elements so that the gc can reclaim the references. - Array.Clear(_items, 0, _size); + Array.Clear(_items, 0, size); } } From 61ee2d18e9186cdd76fad89217581cb281880bc6 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 25 May 2022 11:11:49 +0200 Subject: [PATCH 398/416] Move assert of initial item state into method. So that we don't keep a reference to the item. --- tests/Avalonia.LeakTests/ControlTests.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/tests/Avalonia.LeakTests/ControlTests.cs b/tests/Avalonia.LeakTests/ControlTests.cs index 09d6764bae..3971cddecf 100644 --- a/tests/Avalonia.LeakTests/ControlTests.cs +++ b/tests/Avalonia.LeakTests/ControlTests.cs @@ -2,14 +2,12 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; -using System.Runtime.Remoting.Contexts; using Avalonia.Controls; using Avalonia.Controls.Shapes; using Avalonia.Controls.Templates; using Avalonia.Data; using Avalonia.Diagnostics; using Avalonia.Input; -using Avalonia.Layout; using Avalonia.Media; using Avalonia.Platform; using Avalonia.Rendering; @@ -710,11 +708,15 @@ namespace Avalonia.LeakTests window.Show(); window.LayoutManager.ExecuteInitialLayoutPass(); + void AssertInitialItemState() + { + var item0 = (ListBoxItem)lb.ItemContainerGenerator.Containers.First().ContainerControl; + var canvas0 = (Canvas)item0.Presenter.Child; + Assert.Equal("foo", canvas0.Tag); + } + Assert.Equal(10, lb.ItemContainerGenerator.Containers.Count()); - - var item0 = (ListBoxItem)lb.ItemContainerGenerator.Containers.First().ContainerControl; - var canvas0 = (Canvas)item0.Presenter.Child; - Assert.Equal("foo", canvas0.Tag); + AssertInitialItemState(); items.Clear(); window.LayoutManager.ExecuteLayoutPass(); From 5aa7de52e787787ba702e3c614b370b587af2e8c Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 25 May 2022 12:17:17 +0200 Subject: [PATCH 399/416] Add cleanup step to leak tests. Because previously failures could occur depending on the order that leak tests were run in. --- tests/Avalonia.LeakTests/ControlTests.cs | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/tests/Avalonia.LeakTests/ControlTests.cs b/tests/Avalonia.LeakTests/ControlTests.cs index 3971cddecf..bb520c16aa 100644 --- a/tests/Avalonia.LeakTests/ControlTests.cs +++ b/tests/Avalonia.LeakTests/ControlTests.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; +using System.Reactive.Disposables; using Avalonia.Controls; using Avalonia.Controls.Shapes; using Avalonia.Controls.Templates; @@ -12,6 +13,7 @@ using Avalonia.Media; using Avalonia.Platform; using Avalonia.Rendering; using Avalonia.Styling; +using Avalonia.Threading; using Avalonia.UnitTests; using Avalonia.VisualTree; using JetBrains.dotMemoryUnit; @@ -730,10 +732,23 @@ namespace Avalonia.LeakTests private IDisposable Start() { - return UnitTestApplication.Start(TestServices.StyledWindow.With( - focusManager: new FocusManager(), - keyboardDevice: () => new KeyboardDevice(), - inputManager: new InputManager())); + void Cleanup() + { + // KeyboardDevice holds a reference to the focused item. + KeyboardDevice.Instance.SetFocusedElement(null, NavigationMethod.Unspecified, KeyModifiers.None); + + // Empty the dispatcher queue. + Dispatcher.UIThread.RunJobs(); + } + + return new CompositeDisposable + { + Disposable.Create(Cleanup), + UnitTestApplication.Start(TestServices.StyledWindow.With( + focusManager: new FocusManager(), + keyboardDevice: () => new KeyboardDevice(), + inputManager: new InputManager())) + }; } From af237c6dd7067b7b5f43961f4fd64b635a2d20fc Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 25 May 2022 11:24:53 +0100 Subject: [PATCH 400/416] fix file dialog filter nullable annotation, and osx platform. --- samples/ControlCatalog/ControlCatalog.csproj | 3 ++- samples/ControlCatalog/Pages/DialogsPage.xaml.cs | 3 +-- src/Avalonia.Controls/SystemDialog.cs | 2 +- src/Avalonia.Native/SystemDialogs.cs | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/samples/ControlCatalog/ControlCatalog.csproj b/samples/ControlCatalog/ControlCatalog.csproj index e5f07c90c3..bce924a3f2 100644 --- a/samples/ControlCatalog/ControlCatalog.csproj +++ b/samples/ControlCatalog/ControlCatalog.csproj @@ -1,7 +1,8 @@  netstandard2.0 - true + true + enable diff --git a/samples/ControlCatalog/Pages/DialogsPage.xaml.cs b/samples/ControlCatalog/Pages/DialogsPage.xaml.cs index 3cadc7243a..fd908a33b6 100644 --- a/samples/ControlCatalog/Pages/DialogsPage.xaml.cs +++ b/samples/ControlCatalog/Pages/DialogsPage.xaml.cs @@ -8,7 +8,6 @@ using Avalonia.Dialogs; using Avalonia.Layout; using Avalonia.Markup.Xaml; #pragma warning disable 4014 - namespace ControlCatalog.Pages { public class DialogsPage : UserControl @@ -22,7 +21,7 @@ namespace ControlCatalog.Pages string lastSelectedDirectory = null; - List GetFilters() + List? GetFilters() { if (this.FindControl("UseFilters").IsChecked != true) return null; diff --git a/src/Avalonia.Controls/SystemDialog.cs b/src/Avalonia.Controls/SystemDialog.cs index 4a9e745e30..093f10be51 100644 --- a/src/Avalonia.Controls/SystemDialog.cs +++ b/src/Avalonia.Controls/SystemDialog.cs @@ -15,7 +15,7 @@ namespace Avalonia.Controls /// Gets or sets a collection of filters which determine the types of files displayed in an /// or an . /// - public List Filters { get; set; } = new List(); + public List? Filters { get; set; } = new List(); /// /// Gets or sets initial file name that is displayed when the dialog is opened. diff --git a/src/Avalonia.Native/SystemDialogs.cs b/src/Avalonia.Native/SystemDialogs.cs index 4372829df1..d1d9c17ae3 100644 --- a/src/Avalonia.Native/SystemDialogs.cs +++ b/src/Avalonia.Native/SystemDialogs.cs @@ -30,7 +30,7 @@ namespace Avalonia.Native ofd.Title ?? "", ofd.Directory ?? "", ofd.InitialFileName ?? "", - string.Join(";", dialog.Filters.SelectMany(f => f.Extensions))); + string.Join(";", dialog.Filters?.SelectMany(f => f.Extensions) ?? Array.Empty())); } else { @@ -39,7 +39,7 @@ namespace Avalonia.Native dialog.Title ?? "", dialog.Directory ?? "", dialog.InitialFileName ?? "", - string.Join(";", dialog.Filters.SelectMany(f => f.Extensions))); + string.Join(";", dialog.Filters?.SelectMany(f => f.Extensions) ?? Array.Empty())); } return events.Task.ContinueWith(t => { events.Dispose(); return t.Result; }); From 5b66f260657190e09698254cf5059b3efa8ef6df Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 25 May 2022 11:38:50 +0100 Subject: [PATCH 401/416] Fix osx dialog style mask. makes the titlebar look like normal window. --- native/Avalonia.Native/src/OSX/WindowImpl.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native/Avalonia.Native/src/OSX/WindowImpl.mm b/native/Avalonia.Native/src/OSX/WindowImpl.mm index d43a8beee4..d96fe717ab 100644 --- a/native/Avalonia.Native/src/OSX/WindowImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowImpl.mm @@ -523,7 +523,7 @@ bool WindowImpl::IsDialog() { } NSWindowStyleMask WindowImpl::GetStyle() { - unsigned long s = this->_isDialog ? NSWindowStyleMaskUtilityWindow : NSWindowStyleMaskBorderless; + unsigned long s = this->_isDialog ? NSWindowStyleMaskDocModalWindow : NSWindowStyleMaskBorderless; switch (_decorations) { case SystemDecorationsNone: From 8aa64bf839d26e3971456157038a717fc1a0562f Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Wed, 25 May 2022 12:48:22 +0200 Subject: [PATCH 402/416] Update SkiaSharp. --- build/SkiaSharp.props | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/SkiaSharp.props b/build/SkiaSharp.props index a217a8272d..1ee4aa56a2 100644 --- a/build/SkiaSharp.props +++ b/build/SkiaSharp.props @@ -1,7 +1,7 @@  - - - + + + From ea9bd40dbd4c3f576f8e6a4efa14987983983fdd Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Wed, 25 May 2022 12:57:02 +0200 Subject: [PATCH 403/416] Update HarfBuzzSharp. --- build/HarfBuzzSharp.props | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/HarfBuzzSharp.props b/build/HarfBuzzSharp.props index e10de93530..85e7a1f34d 100644 --- a/build/HarfBuzzSharp.props +++ b/build/HarfBuzzSharp.props @@ -1,7 +1,7 @@  - - - + + + From ba7fd6c9e8be304db7bdc22900f22ead2efcd799 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Wed, 25 May 2022 15:30:24 +0200 Subject: [PATCH 404/416] fix: some xml comment --- src/Avalonia.Base/Animation/Transitions/Rotate3DTransition.cs | 1 + src/Avalonia.Base/AvaloniaPropertyChangedEventArgs.cs | 2 +- src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs | 3 +++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Animation/Transitions/Rotate3DTransition.cs b/src/Avalonia.Base/Animation/Transitions/Rotate3DTransition.cs index 60c7a97ced..239f3aea08 100644 --- a/src/Avalonia.Base/Animation/Transitions/Rotate3DTransition.cs +++ b/src/Avalonia.Base/Animation/Transitions/Rotate3DTransition.cs @@ -14,6 +14,7 @@ public class Rotate3DTransition: PageSlide /// /// How long the rotation should take place /// The orientation of the rotation + /// Defines the depth of the 3D Effect. If null, depth will be calculated automatically from the width or height of the common parent of the visual being rotated public Rotate3DTransition(TimeSpan duration, SlideAxis orientation = SlideAxis.Horizontal, double? depth = null) : base(duration, orientation) { diff --git a/src/Avalonia.Base/AvaloniaPropertyChangedEventArgs.cs b/src/Avalonia.Base/AvaloniaPropertyChangedEventArgs.cs index 445f35aad2..45c67b9f48 100644 --- a/src/Avalonia.Base/AvaloniaPropertyChangedEventArgs.cs +++ b/src/Avalonia.Base/AvaloniaPropertyChangedEventArgs.cs @@ -55,7 +55,7 @@ namespace Avalonia /// /// /// This will usually be true, except in - /// + /// /// which receives notifications for all changes to property values, whether a value with a higher /// priority is present or not. When this property is false, the change that is being signaled /// has not resulted in a change to the property value on the object. diff --git a/src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs b/src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs index a3095ad214..ae52e5f970 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs @@ -193,8 +193,11 @@ namespace Avalonia.Controls } } + /// Try get number of DataSource itmes. /// When "allowSlow" is false, method will not use Linq.Count() method and will return 0 or 1 instead. /// If "getAny" is true, method can use Linq.Any() method to speedup. + /// number of DataSource itmes. + /// true if able to retrieve number of DataSource itmes; otherwise, false. internal bool TryGetCount(bool allowSlow, bool getAny, out int count) { bool result; From f1a2716858fc90067ffe6fa0651ee2a04f43c36a Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 26 May 2022 03:36:49 -0400 Subject: [PATCH 405/416] Inherit DataType from template without hardcoding on specific DataTemplate implementation --- .../Metadata/TemplateDataTypeAttribute.cs | 9 ++ ...valoniaXamlIlDataContextTypeTransformer.cs | 5 +- .../AvaloniaXamlIlWellKnownTypes.cs | 2 + .../Templates/DataTemplate.cs | 1 + .../Templates/TreeDataTemplate.cs | 1 + .../CompiledBindingExtensionTests.cs | 122 +++++++++++++++++- 6 files changed, 137 insertions(+), 3 deletions(-) create mode 100644 src/Avalonia.Base/Metadata/TemplateDataTypeAttribute.cs diff --git a/src/Avalonia.Base/Metadata/TemplateDataTypeAttribute.cs b/src/Avalonia.Base/Metadata/TemplateDataTypeAttribute.cs new file mode 100644 index 0000000000..29e8cf4a06 --- /dev/null +++ b/src/Avalonia.Base/Metadata/TemplateDataTypeAttribute.cs @@ -0,0 +1,9 @@ +using System; + +namespace Avalonia.Metadata; + +[AttributeUsage(AttributeTargets.Property)] +public class TemplateDataTypeAttribute : Attribute +{ + +} diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs index cf691db860..75859a0a18 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs @@ -49,6 +49,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers } else if (child is XamlPropertyAssignmentNode pa) { + var templateDataTypeAttribute = context.GetAvaloniaTypes().TemplateDataTypeAttribute; + if (pa.Property.Name == "DataContext" && pa.Property.DeclaringType.Equals(context.GetAvaloniaTypes().StyledElement) && pa.Values[0] is XamlMarkupExtensionNode ext @@ -56,8 +58,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers { inferredDataContextTypeNode = ParseDataContext(context, on, obj); } - else if(context.GetAvaloniaTypes().DataTemplate.IsAssignableFrom(on.Type.GetClrType()) - && pa.Property.Name == "DataType" + else if(pa.Property.CustomAttributes.Any(a => a.Type == templateDataTypeAttribute) && pa.Values[0] is XamlTypeExtensionNode dataTypeNode) { inferredDataContextTypeNode = new AvaloniaXamlIlDataContextTypeMetadataNode(on, dataTypeNode.Value.GetClrType()); diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs index 5da40035d2..da28891968 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs @@ -26,6 +26,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers public IXamlType Transitions { get; } public IXamlType AssignBindingAttribute { get; } public IXamlType DependsOnAttribute { get; } + public IXamlType TemplateDataTypeAttribute { get; } public IXamlType UnsetValueType { get; } public IXamlType StyledElement { get; } public IXamlType IStyledElement { get; } @@ -112,6 +113,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers Transitions = cfg.TypeSystem.GetType("Avalonia.Animation.Transitions"); AssignBindingAttribute = cfg.TypeSystem.GetType("Avalonia.Data.AssignBindingAttribute"); DependsOnAttribute = cfg.TypeSystem.GetType("Avalonia.Metadata.DependsOnAttribute"); + TemplateDataTypeAttribute = cfg.TypeSystem.GetType("Avalonia.Metadata.TemplateDataTypeAttribute"); AvaloniaObjectBindMethod = AvaloniaObjectExtensions.FindMethod("Bind", IDisposable, false, IAvaloniaObject, AvaloniaProperty, IBinding, cfg.WellKnownTypes.Object); diff --git a/src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs b/src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs index b7db1a3fbb..1dd3cddf9a 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs @@ -7,6 +7,7 @@ namespace Avalonia.Markup.Xaml.Templates { public class DataTemplate : IRecyclingDataTemplate { + [TemplateDataType] public Type DataType { get; set; } [Content] diff --git a/src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs b/src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs index 7b065c7f47..b07e501fde 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs @@ -11,6 +11,7 @@ namespace Avalonia.Markup.Xaml.Templates { public class TreeDataTemplate : ITreeDataTemplate { + [TemplateDataType] public Type DataType { get; set; } [Content] diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs index c1c2284372..fe3df7d189 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs @@ -8,11 +8,14 @@ using System.Text; using System.Threading.Tasks; using Avalonia.Controls; using Avalonia.Controls.Presenters; +using Avalonia.Controls.Templates; using Avalonia.Data.Converters; using Avalonia.Data.Core; using Avalonia.Input; using Avalonia.Markup.Data; +using Avalonia.Markup.Xaml.Templates; using Avalonia.Media; +using Avalonia.Metadata; using Avalonia.UnitTests; using XamlX; using Xunit; @@ -455,7 +458,106 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions ThrowsXamlTransformException(() => AvaloniaRuntimeXamlLoader.Load(xaml)); } } + + [Fact] + public void IgnoresDataTemplateTypeFromDataTypePropertyIfXDataTypeDefined() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + + + + + + +"; + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + var target = window.FindControl("target"); + + var dataContext = new TestDataContext(); + + dataContext.StringProperty = "Initial Value"; + + window.DataContext = dataContext; + window.ApplyTemplate(); + target.ApplyTemplate(); + ((ContentPresenter)target.Presenter).UpdateChild(); + + Assert.Equal(dataContext.StringProperty, ((TextBlock)target.Presenter.Child).Text); + } + } + + [Fact] + public void InfersCustomDataTemplateBasedOnAttribute() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + + + + + + +"; + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + var target = window.FindControl("target"); + + var dataContext = new TestDataContext(); + + dataContext.StringProperty = "Initial Value"; + + window.DataContext = dataContext; + + window.ApplyTemplate(); + target.ApplyTemplate(); + ((ContentPresenter)target.Presenter).UpdateChild(); + + Assert.Equal(dataContext.StringProperty, ((TextBlock)target.Presenter.Child).Text); + } + } + + [Fact] + public void InfersCustomDataTemplateBasedOnAttributeFromBaseClass() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + + + + + + +"; + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); + var target = window.FindControl("target"); + + var dataContext = new TestDataContext(); + + dataContext.StringProperty = "Initial Value"; + + window.DataContext = dataContext; + + window.ApplyTemplate(); + target.ApplyTemplate(); + ((ContentPresenter)target.Presenter).UpdateChild(); + + Assert.Equal(dataContext.StringProperty, ((TextBlock)target.Presenter.Child).Text); + } + } + [Fact] public void ResolvesElementNameBinding() { @@ -1324,7 +1426,9 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions public string StringProperty { get; set; } } - public class TestDataContext : IHasPropertyDerived + public class TestDataContextBaseClass {} + + public class TestDataContext : TestDataContextBaseClass, IHasPropertyDerived { public string StringProperty { get; set; } @@ -1413,4 +1517,20 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions return ReferenceEquals(null, parameter) == false; } } + + public class CustomDataTemplate : IDataTemplate + { + [TemplateDataType] + public Type FancyDataType { get; set; } + + [Content] + [TemplateContent] + public object Content { get; set; } + + public bool Match(object data) => FancyDataType.IsInstanceOfType(data); + + public IControl Build(object data) => TemplateContent.Load(Content)?.Control; + } + + public class CustomDataTemplateInherit : CustomDataTemplate { } } From 9126edca7003c698f4f8a60e0ee10cd5c54605ec Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 26 May 2022 03:40:05 -0400 Subject: [PATCH 406/416] Add documentation --- src/Avalonia.Base/Metadata/TemplateDataTypeAttribute.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Avalonia.Base/Metadata/TemplateDataTypeAttribute.cs b/src/Avalonia.Base/Metadata/TemplateDataTypeAttribute.cs index 29e8cf4a06..51899bd03c 100644 --- a/src/Avalonia.Base/Metadata/TemplateDataTypeAttribute.cs +++ b/src/Avalonia.Base/Metadata/TemplateDataTypeAttribute.cs @@ -2,6 +2,9 @@ namespace Avalonia.Metadata; +/// +/// Defines the property that contains type of the data passed to the implementation. +/// [AttributeUsage(AttributeTargets.Property)] public class TemplateDataTypeAttribute : Attribute { From b567fcebf58a3afc8b08f31996110f31fdb139e6 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Thu, 26 May 2022 04:15:29 -0400 Subject: [PATCH 407/416] Rename TemplateDataTypeAttribute to DataTypeAttribute --- src/Avalonia.Base/Metadata/DataTypeAttribute.cs | 15 +++++++++++++++ .../Metadata/TemplateDataTypeAttribute.cs | 12 ------------ .../AvaloniaXamlIlDataContextTypeTransformer.cs | 2 +- .../Transformers/AvaloniaXamlIlWellKnownTypes.cs | 4 ++-- .../Templates/DataTemplate.cs | 2 +- .../Templates/TreeDataTemplate.cs | 2 +- .../CompiledBindingExtensionTests.cs | 2 +- 7 files changed, 21 insertions(+), 18 deletions(-) create mode 100644 src/Avalonia.Base/Metadata/DataTypeAttribute.cs delete mode 100644 src/Avalonia.Base/Metadata/TemplateDataTypeAttribute.cs diff --git a/src/Avalonia.Base/Metadata/DataTypeAttribute.cs b/src/Avalonia.Base/Metadata/DataTypeAttribute.cs new file mode 100644 index 0000000000..ac46a0d30a --- /dev/null +++ b/src/Avalonia.Base/Metadata/DataTypeAttribute.cs @@ -0,0 +1,15 @@ +using System; + +namespace Avalonia.Metadata; + +/// +/// Defines the property that contains type that should be used as a type information for compiled bindings. +/// +/// +/// Used on DataTemplate.DataType property so it can be inherited in compiled bindings inside of the template. +/// +[AttributeUsage(AttributeTargets.Property)] +public class DataTypeAttribute : Attribute +{ + +} diff --git a/src/Avalonia.Base/Metadata/TemplateDataTypeAttribute.cs b/src/Avalonia.Base/Metadata/TemplateDataTypeAttribute.cs deleted file mode 100644 index 51899bd03c..0000000000 --- a/src/Avalonia.Base/Metadata/TemplateDataTypeAttribute.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace Avalonia.Metadata; - -/// -/// Defines the property that contains type of the data passed to the implementation. -/// -[AttributeUsage(AttributeTargets.Property)] -public class TemplateDataTypeAttribute : Attribute -{ - -} diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs index 75859a0a18..d4dfcf9c4b 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDataContextTypeTransformer.cs @@ -49,7 +49,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers } else if (child is XamlPropertyAssignmentNode pa) { - var templateDataTypeAttribute = context.GetAvaloniaTypes().TemplateDataTypeAttribute; + var templateDataTypeAttribute = context.GetAvaloniaTypes().DataTypeAttribute; if (pa.Property.Name == "DataContext" && pa.Property.DeclaringType.Equals(context.GetAvaloniaTypes().StyledElement) diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs index da28891968..28787d9b84 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs @@ -26,7 +26,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers public IXamlType Transitions { get; } public IXamlType AssignBindingAttribute { get; } public IXamlType DependsOnAttribute { get; } - public IXamlType TemplateDataTypeAttribute { get; } + public IXamlType DataTypeAttribute { get; } public IXamlType UnsetValueType { get; } public IXamlType StyledElement { get; } public IXamlType IStyledElement { get; } @@ -113,7 +113,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers Transitions = cfg.TypeSystem.GetType("Avalonia.Animation.Transitions"); AssignBindingAttribute = cfg.TypeSystem.GetType("Avalonia.Data.AssignBindingAttribute"); DependsOnAttribute = cfg.TypeSystem.GetType("Avalonia.Metadata.DependsOnAttribute"); - TemplateDataTypeAttribute = cfg.TypeSystem.GetType("Avalonia.Metadata.TemplateDataTypeAttribute"); + DataTypeAttribute = cfg.TypeSystem.GetType("Avalonia.Metadata.DataTypeAttribute"); AvaloniaObjectBindMethod = AvaloniaObjectExtensions.FindMethod("Bind", IDisposable, false, IAvaloniaObject, AvaloniaProperty, IBinding, cfg.WellKnownTypes.Object); diff --git a/src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs b/src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs index 1dd3cddf9a..d2b24979cc 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs @@ -7,7 +7,7 @@ namespace Avalonia.Markup.Xaml.Templates { public class DataTemplate : IRecyclingDataTemplate { - [TemplateDataType] + [DataType] public Type DataType { get; set; } [Content] diff --git a/src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs b/src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs index b07e501fde..10061c3d48 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs @@ -11,7 +11,7 @@ namespace Avalonia.Markup.Xaml.Templates { public class TreeDataTemplate : ITreeDataTemplate { - [TemplateDataType] + [DataType] public Type DataType { get; set; } [Content] diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs index fe3df7d189..7e721fd7b2 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs @@ -1520,7 +1520,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions public class CustomDataTemplate : IDataTemplate { - [TemplateDataType] + [DataType] public Type FancyDataType { get; set; } [Content] From 60d0e2f1c383159288f6fbb243bfc8d078f129cb Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Wed, 25 May 2022 17:30:25 +0200 Subject: [PATCH 408/416] fix: Consolidate JetBrains.DotMemoryUnit --- build/JetBrains.dotMemoryUnit.props | 2 +- nukebuild/_build.csproj | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/build/JetBrains.dotMemoryUnit.props b/build/JetBrains.dotMemoryUnit.props index eb4e2b6f15..5d74d474cf 100644 --- a/build/JetBrains.dotMemoryUnit.props +++ b/build/JetBrains.dotMemoryUnit.props @@ -1,5 +1,5 @@ - + diff --git a/nukebuild/_build.csproj b/nukebuild/_build.csproj index 52b60b7d0f..b2c58e2292 100644 --- a/nukebuild/_build.csproj +++ b/nukebuild/_build.csproj @@ -8,11 +8,10 @@ False CS0649;CS0169 - + - From d3b09a7d741bd33b7c615e54d4792c02aa6a8d20 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Wed, 25 May 2022 17:42:03 +0200 Subject: [PATCH 409/416] fix: Update SourceLink --- build/SourceLink.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/SourceLink.props b/build/SourceLink.props index 9f05848881..dd7ecc8d2a 100644 --- a/build/SourceLink.props +++ b/build/SourceLink.props @@ -19,7 +19,7 @@ - + From 1d8e3cabe7476c1c82e5b3ba60735a975bdb68cb Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Thu, 26 May 2022 11:53:38 +0200 Subject: [PATCH 410/416] fix: update Microsoft.NETFramework.ReferenceAssemblies --- build/NetFX.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/NetFX.props b/build/NetFX.props index 8ffc9ec561..27bac831b8 100644 --- a/build/NetFX.props +++ b/build/NetFX.props @@ -1,7 +1,7 @@  - + From a25ce2fb08c0bcfe4eb05c04418f7df73770a2cc Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Thu, 26 May 2022 12:43:52 +0200 Subject: [PATCH 411/416] feat: move InternalsVisibleTo in csproj --- Directory.Build.props | 1 + build/AvaloniaPrublicKey.props | 5 +++++ src/Avalonia.Base/Avalonia.Base.csproj | 17 +++++++++++++++++ src/Avalonia.Base/Properties/AssemblyInfo.cs | 14 -------------- .../Avalonia.Controls.ColorPicker.csproj | 5 +++++ .../Properties/AssemblyInfo.cs | 3 --- .../Avalonia.Controls.DataGrid.csproj | 5 +++++ .../Properties/AssemblyInfo.cs | 4 ---- src/Avalonia.Controls/Avalonia.Controls.csproj | 6 ++++++ .../Properties/AssemblyInfo.cs | 5 ----- .../Avalonia.FreeDesktop.csproj | 4 +++- src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs | 4 ---- .../Avalonia.PlatformSupport.csproj | 2 +- .../Avalonia.Markup.Xaml.csproj | 4 ++++ .../Properties/AssemblyInfo.cs | 3 --- .../Avalonia.Markup/Avalonia.Markup.csproj | 4 ++++ .../Avalonia.Markup/Properties/AssemblyInfo.cs | 3 --- src/Skia/Avalonia.Skia/Avalonia.Skia.csproj | 9 +++++++++ .../Avalonia.Skia/Properties/AssemblyInfo.cs | 5 ----- .../Avalonia.Direct2D1.csproj | 10 ++++++++++ .../Properties/AssemblyInfo.cs | 4 ---- 21 files changed, 70 insertions(+), 47 deletions(-) create mode 100644 build/AvaloniaPrublicKey.props delete mode 100644 src/Skia/Avalonia.Skia/Properties/AssemblyInfo.cs delete mode 100644 src/Windows/Avalonia.Direct2D1/Properties/AssemblyInfo.cs diff --git a/Directory.Build.props b/Directory.Build.props index 97781b7517..adfb985d2b 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,4 +1,5 @@ + $(MSBuildThisFileDirectory)build-intermediate/nuget $(MSBuildThisFileDirectory)\src\tools\Avalonia.Designer.HostApp\bin\$(Configuration)\netcoreapp2.0\Avalonia.Designer.HostApp.dll diff --git a/build/AvaloniaPrublicKey.props b/build/AvaloniaPrublicKey.props new file mode 100644 index 0000000000..89215635c0 --- /dev/null +++ b/build/AvaloniaPrublicKey.props @@ -0,0 +1,5 @@ + + + 0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87 + + diff --git a/src/Avalonia.Base/Avalonia.Base.csproj b/src/Avalonia.Base/Avalonia.Base.csproj index 8e4755b4b7..5bd733a17e 100644 --- a/src/Avalonia.Base/Avalonia.Base.csproj +++ b/src/Avalonia.Base/Avalonia.Base.csproj @@ -17,4 +17,21 @@ + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Base/Properties/AssemblyInfo.cs b/src/Avalonia.Base/Properties/AssemblyInfo.cs index 2c40c768f5..3753588fce 100644 --- a/src/Avalonia.Base/Properties/AssemblyInfo.cs +++ b/src/Avalonia.Base/Properties/AssemblyInfo.cs @@ -17,18 +17,4 @@ using Avalonia.Metadata; [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Media.Transformation")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Styling")] -[assembly: InternalsVisibleTo("Avalonia.Base.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.Controls, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.Controls.ColorPicker, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.Controls.DataGrid, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.Controls.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.DesignerSupport, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.Direct2D1.RenderTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.LeakTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.Markup.Xaml.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.PlatformSupport, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.Skia.RenderTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.Skia.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.Web.Blazor, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] diff --git a/src/Avalonia.Controls.ColorPicker/Avalonia.Controls.ColorPicker.csproj b/src/Avalonia.Controls.ColorPicker/Avalonia.Controls.ColorPicker.csproj index 0952c899d4..e0790795c5 100644 --- a/src/Avalonia.Controls.ColorPicker/Avalonia.Controls.ColorPicker.csproj +++ b/src/Avalonia.Controls.ColorPicker/Avalonia.Controls.ColorPicker.csproj @@ -22,4 +22,9 @@ + + + + + diff --git a/src/Avalonia.Controls.ColorPicker/Properties/AssemblyInfo.cs b/src/Avalonia.Controls.ColorPicker/Properties/AssemblyInfo.cs index 0135541349..64769303d6 100644 --- a/src/Avalonia.Controls.ColorPicker/Properties/AssemblyInfo.cs +++ b/src/Avalonia.Controls.ColorPicker/Properties/AssemblyInfo.cs @@ -1,8 +1,5 @@ -using System.Runtime.CompilerServices; using Avalonia.Metadata; -[assembly: InternalsVisibleTo("Avalonia.DesignerSupport, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] - [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Collections")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Primitives")] diff --git a/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj b/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj index 410cac72fc..6369961f0f 100644 --- a/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj +++ b/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj @@ -18,4 +18,9 @@ + + + + + diff --git a/src/Avalonia.Controls.DataGrid/Properties/AssemblyInfo.cs b/src/Avalonia.Controls.DataGrid/Properties/AssemblyInfo.cs index d61c05ab6e..64769303d6 100644 --- a/src/Avalonia.Controls.DataGrid/Properties/AssemblyInfo.cs +++ b/src/Avalonia.Controls.DataGrid/Properties/AssemblyInfo.cs @@ -1,9 +1,5 @@ -using System.Runtime.CompilerServices; using Avalonia.Metadata; -[assembly: InternalsVisibleTo("Avalonia.Controls.DataGrid.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.DesignerSupport, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] - [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Collections")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Primitives")] diff --git a/src/Avalonia.Controls/Avalonia.Controls.csproj b/src/Avalonia.Controls/Avalonia.Controls.csproj index 4d239e69f4..00f094f508 100644 --- a/src/Avalonia.Controls/Avalonia.Controls.csproj +++ b/src/Avalonia.Controls/Avalonia.Controls.csproj @@ -14,4 +14,10 @@ + + + + + + diff --git a/src/Avalonia.Controls/Properties/AssemblyInfo.cs b/src/Avalonia.Controls/Properties/AssemblyInfo.cs index 25330614cf..0f3da91107 100644 --- a/src/Avalonia.Controls/Properties/AssemblyInfo.cs +++ b/src/Avalonia.Controls/Properties/AssemblyInfo.cs @@ -1,10 +1,5 @@ -using System.Runtime.CompilerServices; using Avalonia.Metadata; -[assembly: InternalsVisibleTo("Avalonia.Controls.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.DesignerSupport, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.LeakTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] - [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Automation")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls")] diff --git a/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj b/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj index bcb63783a4..e9d6394aa5 100644 --- a/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj +++ b/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj @@ -8,5 +8,7 @@ - + + + diff --git a/src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs b/src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs index a7cc4f4cc2..4a55212de3 100644 --- a/src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs +++ b/src/Avalonia.FreeDesktop/DBusTrayIconImpl.cs @@ -12,10 +12,6 @@ using Tmds.DBus; [assembly: InternalsVisibleTo(Connection.DynamicAssemblyName)] -[assembly: - InternalsVisibleTo( - "Avalonia.X11, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] - namespace Avalonia.FreeDesktop { internal class DBusTrayIconImpl : ITrayIconImpl diff --git a/src/Avalonia.PlatformSupport/Avalonia.PlatformSupport.csproj b/src/Avalonia.PlatformSupport/Avalonia.PlatformSupport.csproj index 5336f1e630..98839b7af3 100644 --- a/src/Avalonia.PlatformSupport/Avalonia.PlatformSupport.csproj +++ b/src/Avalonia.PlatformSupport/Avalonia.PlatformSupport.csproj @@ -18,7 +18,7 @@ - + diff --git a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj index 622ec416e8..30d321426f 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj +++ b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj @@ -63,4 +63,8 @@ + + + + diff --git a/src/Markup/Avalonia.Markup.Xaml/Properties/AssemblyInfo.cs b/src/Markup/Avalonia.Markup.Xaml/Properties/AssemblyInfo.cs index cee4d90917..7e5b3159d4 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Properties/AssemblyInfo.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Properties/AssemblyInfo.cs @@ -1,9 +1,6 @@ using Avalonia.Metadata; -using System.Runtime.CompilerServices; [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Markup.Xaml.MarkupExtensions")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Markup.Xaml.Styling")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Markup.Xaml.Templates")] -[assembly: InternalsVisibleTo("Avalonia.Markup.Xaml.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] - diff --git a/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj b/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj index f7f1a111ba..6711c3dd3d 100644 --- a/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj +++ b/src/Markup/Avalonia.Markup/Avalonia.Markup.csproj @@ -17,4 +17,8 @@ + + + + diff --git a/src/Markup/Avalonia.Markup/Properties/AssemblyInfo.cs b/src/Markup/Avalonia.Markup/Properties/AssemblyInfo.cs index 46e2925f92..36ca2784da 100644 --- a/src/Markup/Avalonia.Markup/Properties/AssemblyInfo.cs +++ b/src/Markup/Avalonia.Markup/Properties/AssemblyInfo.cs @@ -1,8 +1,5 @@ using Avalonia.Metadata; -using System.Runtime.CompilerServices; [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Data")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Markup.Data")] -[assembly: InternalsVisibleTo("Avalonia.Markup.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] - diff --git a/src/Skia/Avalonia.Skia/Avalonia.Skia.csproj b/src/Skia/Avalonia.Skia/Avalonia.Skia.csproj index 26c0fb756e..413c2ba4d4 100644 --- a/src/Skia/Avalonia.Skia/Avalonia.Skia.csproj +++ b/src/Skia/Avalonia.Skia/Avalonia.Skia.csproj @@ -18,4 +18,13 @@ + + + + + + + + + diff --git a/src/Skia/Avalonia.Skia/Properties/AssemblyInfo.cs b/src/Skia/Avalonia.Skia/Properties/AssemblyInfo.cs deleted file mode 100644 index a7e556ee84..0000000000 --- a/src/Skia/Avalonia.Skia/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,5 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Avalonia.Skia.RenderTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.Skia.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] - diff --git a/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj b/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj index bd1f05b30f..03b3ebec0d 100644 --- a/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj +++ b/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj @@ -19,4 +19,14 @@ + + + + + + + + + + diff --git a/src/Windows/Avalonia.Direct2D1/Properties/AssemblyInfo.cs b/src/Windows/Avalonia.Direct2D1/Properties/AssemblyInfo.cs deleted file mode 100644 index 89fd6c2dd8..0000000000 --- a/src/Windows/Avalonia.Direct2D1/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,4 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Avalonia.Direct2D1.RenderTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.Direct2D1.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] From ea4d92ddafcb74e58357729383376e7b5037e950 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Thu, 26 May 2022 23:12:21 +0200 Subject: [PATCH 412/416] Clear IMM state when window is closed. --- .../Avalonia.Win32/Input/Imm32InputMethod.cs | 18 ++++++++++++++++++ .../Avalonia.Win32/WindowImpl.AppWndProc.cs | 6 ++++++ 2 files changed, 24 insertions(+) diff --git a/src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs b/src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs index 9ff6f76ac4..3adefd965f 100644 --- a/src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs +++ b/src/Windows/Avalonia.Win32/Input/Imm32InputMethod.cs @@ -37,6 +37,24 @@ namespace Avalonia.Win32.Input IsComposing = false; } + public void ClearLanguageAndWindow() + { + if (HWND != IntPtr.Zero && _defaultImc != IntPtr.Zero) + { + ImmReleaseContext(HWND, _defaultImc); + } + + _defaultImc = IntPtr.Zero; + HWND = IntPtr.Zero; + _parent = null; + _active = false; + _langId = 0; + _showCompositionWindow = false; + _showCandidateList = false; + + IsComposing = false; + } + //Dependant on CurrentThread. When Avalonia will support Multiple Dispatchers - //every Dispatcher should have their own InputMethod. public static Imm32InputMethod Current { get; } = new Imm32InputMethod(); diff --git a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs index 64ab15bc30..cae8834550 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs @@ -82,6 +82,12 @@ namespace Avalonia.Win32 case WindowsMessage.WM_DESTROY: { UiaCoreProviderApi.UiaReturnRawElementProvider(_hwnd, IntPtr.Zero, IntPtr.Zero, null); + + // We need to release IMM context and state to avoid leaks. + if (Imm32InputMethod.Current.HWND == _hwnd) + { + Imm32InputMethod.Current.ClearLanguageAndWindow(); + } //Window doesn't exist anymore _hwnd = IntPtr.Zero; From 74d951235fad07ae507c4c870c4b69e55578a15e Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 27 May 2022 09:09:58 +0200 Subject: [PATCH 413/416] fix: move DynamicProxyGenAssembly2 InternalsVisibleTo to csproj --- src/Avalonia.Base/Avalonia.Base.csproj | 1 + src/Avalonia.Base/Properties/AssemblyInfo.cs | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Avalonia.Base.csproj b/src/Avalonia.Base/Avalonia.Base.csproj index 5bd733a17e..16a91b10d6 100644 --- a/src/Avalonia.Base/Avalonia.Base.csproj +++ b/src/Avalonia.Base/Avalonia.Base.csproj @@ -33,5 +33,6 @@ + diff --git a/src/Avalonia.Base/Properties/AssemblyInfo.cs b/src/Avalonia.Base/Properties/AssemblyInfo.cs index 3753588fce..9a62f94e35 100644 --- a/src/Avalonia.Base/Properties/AssemblyInfo.cs +++ b/src/Avalonia.Base/Properties/AssemblyInfo.cs @@ -17,4 +17,3 @@ using Avalonia.Metadata; [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Media.Transformation")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Styling")] -[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] From cc91c3002fea1c50893d2c7e2b5e36637ab1922a Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 27 May 2022 09:12:56 +0200 Subject: [PATCH 414/416] fix: AvaloniaPublicKey.props file name typo --- Directory.Build.props | 2 +- build/{AvaloniaPrublicKey.props => AvaloniaPublicKey.props} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename build/{AvaloniaPrublicKey.props => AvaloniaPublicKey.props} (100%) diff --git a/Directory.Build.props b/Directory.Build.props index adfb985d2b..42daa2df7f 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,5 +1,5 @@ - + $(MSBuildThisFileDirectory)build-intermediate/nuget $(MSBuildThisFileDirectory)\src\tools\Avalonia.Designer.HostApp\bin\$(Configuration)\netcoreapp2.0\Avalonia.Designer.HostApp.dll diff --git a/build/AvaloniaPrublicKey.props b/build/AvaloniaPublicKey.props similarity index 100% rename from build/AvaloniaPrublicKey.props rename to build/AvaloniaPublicKey.props From b2714472fea7c4f329b8f1c53ee28a9ee73356d0 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Fri, 27 May 2022 09:28:52 +0200 Subject: [PATCH 415/416] fix: remove Microsoft.NETFramework.ReferenceAssemblies package --- build/NetFX.props | 1 - 1 file changed, 1 deletion(-) diff --git a/build/NetFX.props b/build/NetFX.props index 27bac831b8..14adb54035 100644 --- a/build/NetFX.props +++ b/build/NetFX.props @@ -1,7 +1,6 @@  - From b605c5c4527013f568d37b85975103059933b9b3 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 27 May 2022 10:51:37 +0100 Subject: [PATCH 416/416] [OSX] fix border shadow invalidation. --- native/Avalonia.Native/src/OSX/WindowBaseImpl.mm | 3 +++ 1 file changed, 3 insertions(+) diff --git a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm index 022769bad0..6dc59ae4d8 100644 --- a/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm +++ b/native/Avalonia.Native/src/OSX/WindowBaseImpl.mm @@ -296,6 +296,7 @@ HRESULT WindowBaseImpl::Resize(double x, double y, AvnPlatformResizeReason reaso if(Window != nullptr) { [Window setContentSize:lastSize]; + [Window invalidateShadow]; } } @finally { @@ -583,6 +584,8 @@ void WindowBaseImpl::InitialiseNSWindow() { [Window setContentMaxSize:lastMaxSize]; [Window setOpaque:false]; + + [Window invalidateShadow]; if (lastMenu != nullptr) { [GetWindowProtocol() applyMenu:lastMenu];