From 397267d1b5b39a11c700003039f3da67025e177f Mon Sep 17 00:00:00 2001 From: Sergey Mikolaytis Date: Mon, 9 Aug 2021 23:46:20 +0300 Subject: [PATCH 01/14] [Menu] [Interaction] Allow end user to change menu show delay globally --- src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs index 984faa4d60..209feb351c 100644 --- a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs +++ b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs @@ -115,7 +115,7 @@ namespace Avalonia.Controls.Platform protected IMenu? Menu { get; private set; } - protected static TimeSpan MenuShowDelay { get; } = TimeSpan.FromMilliseconds(400); + public static TimeSpan MenuShowDelay { get; set; } = TimeSpan.FromMilliseconds(400); protected internal virtual void GotFocus(object sender, GotFocusEventArgs e) { From d082ad91d8c87e0972b5d340df8b4f5d07bb7015 Mon Sep 17 00:00:00 2001 From: Tako <53405089+Takoooooo@users.noreply.github.com> Date: Wed, 25 Aug 2021 19:34:54 +0300 Subject: [PATCH 02/14] Update AvaloniaSynchronizationContext to don't wrap exceptions to AggregateException when invoking from non-UI thread The code suggested by the user makes sense to me. Reproed issue with ` try { var ctx = SynchronizationContext.Current; await Task.Run(() => ctx.Send(state => throw new ArgumentException("hello"), null)); } catch (ArgumentException ex) { Console.WriteLine(ex.Message); } catch (AggregateException ex) { Console.WriteLine(ex.Message); }` this code --- src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs b/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs index 1a78792173..326d1a3f53 100644 --- a/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs +++ b/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs @@ -39,7 +39,7 @@ namespace Avalonia.Threading if (Dispatcher.UIThread.CheckAccess()) d(state); else - Dispatcher.UIThread.InvokeAsync(() => d(state), DispatcherPriority.Send).Wait(); + Dispatcher.UIThread.InvokeAsync(() => d(state), DispatcherPriority.Send).GetAwaiter().GetResult(); } From 9682f014b2e71ded52b82d0e602a56506147b883 Mon Sep 17 00:00:00 2001 From: Sergey Mikolaytis Date: Sat, 28 Aug 2021 15:41:52 +0300 Subject: [PATCH 03/14] [OSX] fix middle button #5784 --- native/Avalonia.Native/src/OSX/window.mm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index 14fe60ab0b..e15f4cc311 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -1672,6 +1672,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent switch(event.buttonNumber) { + case 2: case 3: _isMiddlePressed = true; [self mouseEvent:event withType:MiddleButtonDown]; @@ -1704,6 +1705,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent { switch(event.buttonNumber) { + case 2: case 3: _isMiddlePressed = false; [self mouseEvent:event withType:MiddleButtonUp]; From 56709c9982d78063984c4865278a7afd913e571b Mon Sep 17 00:00:00 2001 From: Max Katz Date: Sun, 29 Aug 2021 14:59:29 -0400 Subject: [PATCH 04/14] Add text box clipboard events --- src/Avalonia.Controls/TextBox.cs | 47 +++++++++++++++---- .../TextBoxClipboardEventArgs.cs | 18 +++++++ 2 files changed, 57 insertions(+), 8 deletions(-) create mode 100644 src/Avalonia.Controls/TextBoxClipboardEventArgs.cs diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 0eade8d6df..657d8fae8f 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -500,6 +500,10 @@ namespace Avalonia.Controls } } + public event EventHandler CopyingToClipboard; + public event EventHandler CuttingToClipboard; + public event EventHandler PastingFromClipboard; + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) { _presenter = e.NameScope.Get("PART_TextPresenter"); @@ -638,27 +642,54 @@ namespace Avalonia.Controls public async void Cut() { var text = GetSelection(); - if (text is null) return; + if (string.IsNullOrEmpty(text)) + { + return; + } - SnapshotUndoRedo(); - Copy(); - DeleteSelection(); + var eventArgs = new TextBoxClipboardEventArgs(); + CuttingToClipboard?.Invoke(this, eventArgs); + if (!eventArgs.Handled) + { + SnapshotUndoRedo(); + await ((IClipboard)AvaloniaLocator.Current.GetService(typeof(IClipboard))) + .SetTextAsync(text); + DeleteSelection(); + } } public async void Copy() { var text = GetSelection(); - if (text is null) return; + if (string.IsNullOrEmpty(text)) + { + return; + } - await ((IClipboard)AvaloniaLocator.Current.GetService(typeof(IClipboard))) - .SetTextAsync(text); + var eventArgs = new TextBoxClipboardEventArgs(); + CopyingToClipboard?.Invoke(this, eventArgs); + if (!eventArgs.Handled) + { + await ((IClipboard)AvaloniaLocator.Current.GetService(typeof(IClipboard))) + .SetTextAsync(text); + } } public async void Paste() { + var eventArgs = new TextBoxClipboardEventArgs(); + PastingFromClipboard?.Invoke(this, eventArgs); + if (eventArgs.Handled) + { + return; + } + var text = await ((IClipboard)AvaloniaLocator.Current.GetService(typeof(IClipboard))).GetTextAsync(); - if (text is null) return; + if (string.IsNullOrEmpty(text)) + { + return; + } SnapshotUndoRedo(); HandleTextInput(text); diff --git a/src/Avalonia.Controls/TextBoxClipboardEventArgs.cs b/src/Avalonia.Controls/TextBoxClipboardEventArgs.cs new file mode 100644 index 0000000000..0fe4cadf39 --- /dev/null +++ b/src/Avalonia.Controls/TextBoxClipboardEventArgs.cs @@ -0,0 +1,18 @@ +using System; + +namespace Avalonia.Controls +{ + /// + /// Provides event data for the , and events. + /// + /// + /// If you perform any action in the handler for a clipboard event, set the Handled property to true; otherwise, the default action is performed. + /// + public class TextBoxClipboardEventArgs : EventArgs + { + /// + /// Gets or sets a value that marks the event as handled. A true value for Handled prevents most handlers along the event from handling the same event again. + /// + public bool Handled { get; set; } + } +} From 825ddc9ccaa2e3d7e002564f96f22c3b1d23a2d8 Mon Sep 17 00:00:00 2001 From: Max Katz Date: Sun, 29 Aug 2021 18:29:56 -0400 Subject: [PATCH 05/14] Use routed events --- src/Avalonia.Controls/TextBox.cs | 44 +++++++++++++++---- .../TextBoxClipboardEventArgs.cs | 18 -------- 2 files changed, 35 insertions(+), 27 deletions(-) delete mode 100644 src/Avalonia.Controls/TextBoxClipboardEventArgs.cs diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 657d8fae8f..9eae928eeb 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -145,6 +145,18 @@ namespace Avalonia.Controls (o, v) => o.UndoLimit = v, unsetValue: -1); + public static readonly RoutedEvent CopyingToClipboardEvent = + RoutedEvent.Register( + "CopyingToClipboard", RoutingStrategies.Bubble); + + public static readonly RoutedEvent CuttingToClipboardEvent = + RoutedEvent.Register( + "CuttingToClipboard", RoutingStrategies.Bubble); + + public static readonly RoutedEvent PastingFromClipboardEvent = + RoutedEvent.Register( + "PastingFromClipboard", RoutingStrategies.Bubble); + readonly struct UndoRedoState : IEquatable { public string Text { get; } @@ -500,9 +512,23 @@ namespace Avalonia.Controls } } - public event EventHandler CopyingToClipboard; - public event EventHandler CuttingToClipboard; - public event EventHandler PastingFromClipboard; + public event EventHandler CopyingToClipboard + { + add => AddHandler(CopyingToClipboardEvent, value); + remove => RemoveHandler(CopyingToClipboardEvent, value); + } + + public event EventHandler CuttingToClipboard + { + add => AddHandler(CuttingToClipboardEvent, value); + remove => RemoveHandler(CuttingToClipboardEvent, value); + } + + public event EventHandler PastingFromClipboard + { + add => AddHandler(PastingFromClipboardEvent, value); + remove => RemoveHandler(PastingFromClipboardEvent, value); + } protected override void OnApplyTemplate(TemplateAppliedEventArgs e) { @@ -647,8 +673,8 @@ namespace Avalonia.Controls return; } - var eventArgs = new TextBoxClipboardEventArgs(); - CuttingToClipboard?.Invoke(this, eventArgs); + var eventArgs = new RoutedEventArgs(CuttingToClipboardEvent); + RaiseEvent(eventArgs); if (!eventArgs.Handled) { SnapshotUndoRedo(); @@ -666,8 +692,8 @@ namespace Avalonia.Controls return; } - var eventArgs = new TextBoxClipboardEventArgs(); - CopyingToClipboard?.Invoke(this, eventArgs); + var eventArgs = new RoutedEventArgs(CopyingToClipboardEvent); + RaiseEvent(eventArgs); if (!eventArgs.Handled) { await ((IClipboard)AvaloniaLocator.Current.GetService(typeof(IClipboard))) @@ -677,8 +703,8 @@ namespace Avalonia.Controls public async void Paste() { - var eventArgs = new TextBoxClipboardEventArgs(); - PastingFromClipboard?.Invoke(this, eventArgs); + var eventArgs = new RoutedEventArgs(PastingFromClipboardEvent); + RaiseEvent(eventArgs); if (eventArgs.Handled) { return; diff --git a/src/Avalonia.Controls/TextBoxClipboardEventArgs.cs b/src/Avalonia.Controls/TextBoxClipboardEventArgs.cs deleted file mode 100644 index 0fe4cadf39..0000000000 --- a/src/Avalonia.Controls/TextBoxClipboardEventArgs.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; - -namespace Avalonia.Controls -{ - /// - /// Provides event data for the , and events. - /// - /// - /// If you perform any action in the handler for a clipboard event, set the Handled property to true; otherwise, the default action is performed. - /// - public class TextBoxClipboardEventArgs : EventArgs - { - /// - /// Gets or sets a value that marks the event as handled. A true value for Handled prevents most handlers along the event from handling the same event again. - /// - public bool Handled { get; set; } - } -} From 3aa38398ff28012d7d296966a6df7d86e42f1424 Mon Sep 17 00:00:00 2001 From: Sergey Mikolaytis Date: Tue, 31 Aug 2021 23:47:25 +0300 Subject: [PATCH 06/14] Fix alt down shortcuts and allow alt down handling for end users (#6491) * [Menu] [Interaction] Allow end user to change menu show delay globally * Fix all alt down handle = true by AccessKeyHandler Co-authored-by: Max Katz --- src/Avalonia.Input/AccessKeyHandler.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Input/AccessKeyHandler.cs b/src/Avalonia.Input/AccessKeyHandler.cs index 5c4af68d79..5082265ea6 100644 --- a/src/Avalonia.Input/AccessKeyHandler.cs +++ b/src/Avalonia.Input/AccessKeyHandler.cs @@ -157,10 +157,9 @@ namespace Avalonia.Input _restoreFocusElement?.Focus(); _restoreFocusElement = null; + + e.Handled = true; } - - // We always handle the Alt key. - e.Handled = true; } else if (_altIsDown) { From f14816144b6e680de653632a8c2ce02d2f4773c2 Mon Sep 17 00:00:00 2001 From: Yoh Deadfall Date: Thu, 2 Sep 2021 13:32:37 +0300 Subject: [PATCH 07/14] Compiler intrinsic based type nullability check --- .../Data/Converters/FuncValueConverter.cs | 2 +- src/Avalonia.Base/Utilities/TypeUtilities.cs | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Base/Data/Converters/FuncValueConverter.cs b/src/Avalonia.Base/Data/Converters/FuncValueConverter.cs index 9ec600d2bc..c0a3c6ad0f 100644 --- a/src/Avalonia.Base/Data/Converters/FuncValueConverter.cs +++ b/src/Avalonia.Base/Data/Converters/FuncValueConverter.cs @@ -26,7 +26,7 @@ namespace Avalonia.Data.Converters /// public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - if (value is TIn || (value == null && TypeUtilities.AcceptsNull(typeof(TIn)))) + if (value is TIn || (value == null && TypeUtilities.AcceptsNull())) { return _convert((TIn)value); } diff --git a/src/Avalonia.Base/Utilities/TypeUtilities.cs b/src/Avalonia.Base/Utilities/TypeUtilities.cs index 179ded3549..0978308ef6 100644 --- a/src/Avalonia.Base/Utilities/TypeUtilities.cs +++ b/src/Avalonia.Base/Utilities/TypeUtilities.cs @@ -3,6 +3,7 @@ using System.ComponentModel; using System.Globalization; using System.Linq; using System.Reflection; +using System.Runtime.CompilerServices; namespace Avalonia.Utilities { @@ -93,6 +94,17 @@ namespace Avalonia.Utilities return !type.IsValueType || IsNullableType(type); } + /// + /// Returns a value indicating whether null can be assigned to the specified type. + /// + /// The type + /// True if the type accepts null values; otherwise false. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool AcceptsNull() + { + return default(T) is null; + } + /// /// Returns a value indicating whether value can be casted to the specified type. /// If value is null, checks if instances of that type can be null. @@ -102,7 +114,7 @@ namespace Avalonia.Utilities /// True if the cast is possible, otherwise false. public static bool CanCast(object value) { - return value is T || (value is null && AcceptsNull(typeof(T))); + return value is T || (value is null && AcceptsNull()); } /// From 0c20e122ba877db3ad702ce235858aef6ec590be Mon Sep 17 00:00:00 2001 From: Yoh Deadfall Date: Thu, 2 Sep 2021 13:36:47 +0300 Subject: [PATCH 08/14] Code deduplication --- src/Avalonia.Base/Data/Converters/FuncValueConverter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Data/Converters/FuncValueConverter.cs b/src/Avalonia.Base/Data/Converters/FuncValueConverter.cs index c0a3c6ad0f..2385d4981c 100644 --- a/src/Avalonia.Base/Data/Converters/FuncValueConverter.cs +++ b/src/Avalonia.Base/Data/Converters/FuncValueConverter.cs @@ -26,7 +26,7 @@ namespace Avalonia.Data.Converters /// public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - if (value is TIn || (value == null && TypeUtilities.AcceptsNull())) + if (TypeUtilities.CanCast(value)) { return _convert((TIn)value); } From 60b3e028b5eca793b146f6bb7fa5c27b79bbb9b6 Mon Sep 17 00:00:00 2001 From: Takoooooo Date: Thu, 2 Sep 2021 16:24:52 +0300 Subject: [PATCH 09/14] fix --- src/Avalonia.Controls/AutoCompleteBox.cs | 16 +++++++++++++++- .../AutoCompleteBoxTests.cs | 10 ++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/AutoCompleteBox.cs b/src/Avalonia.Controls/AutoCompleteBox.cs index 5a6e78f441..805431eeea 100644 --- a/src/Avalonia.Controls/AutoCompleteBox.cs +++ b/src/Avalonia.Controls/AutoCompleteBox.cs @@ -2094,7 +2094,21 @@ namespace Avalonia.Controls bool inResults = !(stringFiltering || objectFiltering); if (!inResults) { - inResults = stringFiltering ? TextFilter(text, FormatValue(item)) : ItemFilter(text, item); + if (stringFiltering) + { + inResults = TextFilter(text, FormatValue(item)); + } + else + { + if (ItemFilter == null) + { + throw new Exception("ItemFilter property can not be unassigned when FilterMode has value AutoCompleteFilterMode.Custom"); + } + else + { + inResults = ItemFilter(text, item); + } + } } if (view_count > view_index && inResults && _view[view_index] == item) diff --git a/tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs b/tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs index b346fca330..c8bd289e54 100644 --- a/tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/AutoCompleteBoxTests.cs @@ -105,6 +105,16 @@ namespace Avalonia.Controls.UnitTests }); } + [Fact] + public void Custom_FilterMode_Without_ItemFilter_Setting_Throws_Exception() + { + RunTest((control, textbox) => + { + control.FilterMode = AutoCompleteFilterMode.Custom; + Assert.Throws(() => { control.Text = "a"; }); + }); + } + [Fact] public void Text_Completion_Via_Text_Property() { From 3c33ee41b1266a70bb624a71d71b9ed33dd921b3 Mon Sep 17 00:00:00 2001 From: Takoooooo Date: Thu, 2 Sep 2021 16:39:48 +0300 Subject: [PATCH 10/14] fix --- src/Avalonia.Controls/AutoCompleteBox.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/AutoCompleteBox.cs b/src/Avalonia.Controls/AutoCompleteBox.cs index 805431eeea..265a354af6 100644 --- a/src/Avalonia.Controls/AutoCompleteBox.cs +++ b/src/Avalonia.Controls/AutoCompleteBox.cs @@ -2102,7 +2102,7 @@ namespace Avalonia.Controls { if (ItemFilter == null) { - throw new Exception("ItemFilter property can not be unassigned when FilterMode has value AutoCompleteFilterMode.Custom"); + throw new Exception("ItemFilter property can not be null when FilterMode has value AutoCompleteFilterMode.Custom"); } else { From b6650a8e4b0acceae33de13a9918760daaaafa84 Mon Sep 17 00:00:00 2001 From: Jumar Macato <16554748+jmacato@users.noreply.github.com> Date: Fri, 3 Sep 2021 18:23:54 +0800 Subject: [PATCH 11/14] Update src/Avalonia.Controls/AutoCompleteBox.cs --- src/Avalonia.Controls/AutoCompleteBox.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/AutoCompleteBox.cs b/src/Avalonia.Controls/AutoCompleteBox.cs index 265a354af6..0e946126ea 100644 --- a/src/Avalonia.Controls/AutoCompleteBox.cs +++ b/src/Avalonia.Controls/AutoCompleteBox.cs @@ -2100,7 +2100,7 @@ namespace Avalonia.Controls } else { - if (ItemFilter == null) + if (ItemFilter is null) { throw new Exception("ItemFilter property can not be null when FilterMode has value AutoCompleteFilterMode.Custom"); } From 846cdb411243d817a451023dbf8c6272b1509b9a Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 6 Sep 2021 15:33:54 +0100 Subject: [PATCH 12/14] correctly implement fullscreen mode so that app ca be started in fullscreen. --- native/Avalonia.Native/src/OSX/window.mm | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index 14fe60ab0b..35c97f1701 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -641,6 +641,7 @@ private: [Window setCanBecomeKeyAndMain]; [Window disableCursorRects]; [Window setTabbingMode:NSWindowTabbingModeDisallowed]; + [Window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; } void HideOrShowTrafficLights () @@ -1091,14 +1092,7 @@ private: { _fullScreenActive = true; - [Window setHasShadow:YES]; - [Window setTitleVisibility:NSWindowTitleVisible]; - [Window setTitlebarAppearsTransparent:NO]; [Window setTitle:_lastTitle]; - - Window.styleMask = Window.styleMask | NSWindowStyleMaskTitled | NSWindowStyleMaskResizable; - Window.styleMask = Window.styleMask & ~NSWindowStyleMaskFullSizeContentView; - [Window toggleFullScreen:nullptr]; } From 69a39974514848da6187672e74e43b5dbe6f58ab Mon Sep 17 00:00:00 2001 From: Takoooooo Date: Tue, 7 Sep 2021 12:11:38 +0300 Subject: [PATCH 13/14] upd --- src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github b/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github index 9e90d34e97..f4ac681b91 160000 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github @@ -1 +1 @@ -Subproject commit 9e90d34e97c766ba8dcb70128147fcded65d195a +Subproject commit f4ac681b91a9dc7a7a095d1050a683de23d86b72 From 35bc310fcdd053f43e754d6d34110cf19989f00c Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 7 Sep 2021 11:17:43 +0100 Subject: [PATCH 14/14] win32 - dont loose window state when hide and show are called programatically. --- src/Windows/Avalonia.Win32/WindowImpl.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 3b7d3efa2f..8fc25f8cfa 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -263,10 +263,8 @@ namespace Avalonia.Win32 { ShowWindow(value, true); } - else - { - _showWindowState = value; - } + + _showWindowState = value; } }