From bc9f75374968134ce680caa31f60cc53007e7779 Mon Sep 17 00:00:00 2001 From: robloo Date: Sat, 14 Jan 2023 22:31:08 -0500 Subject: [PATCH 1/8] Switch Expander.IsExpanded to a StyledProperty --- src/Avalonia.Controls/Expander.cs | 114 +++++++++++++++++------------- 1 file changed, 64 insertions(+), 50 deletions(-) diff --git a/src/Avalonia.Controls/Expander.cs b/src/Avalonia.Controls/Expander.cs index 65227a826a..93f5e0fc14 100644 --- a/src/Avalonia.Controls/Expander.cs +++ b/src/Avalonia.Controls/Expander.cs @@ -59,12 +59,11 @@ namespace Avalonia.Controls /// /// Defines the property. /// - public static readonly DirectProperty IsExpandedProperty = - AvaloniaProperty.RegisterDirect( + public static readonly StyledProperty IsExpandedProperty = + AvaloniaProperty.Register( nameof(IsExpanded), - o => o.IsExpanded, - (o, v) => o.IsExpanded = v, - defaultBindingMode: Data.BindingMode.TwoWay); + defaultBindingMode: BindingMode.TwoWay, + coerce: CoerceIsExpanded); /// /// Defines the event. @@ -99,7 +98,6 @@ namespace Avalonia.Controls RoutingStrategies.Bubble); private bool _ignorePropertyChanged = false; - private bool _isExpanded; private CancellationTokenSource? _lastTransitionCts; /// @@ -134,50 +132,8 @@ namespace Avalonia.Controls /// public bool IsExpanded { - get => _isExpanded; - set - { - // It is important here that IsExpanded is a direct property so events can be invoked - // BEFORE the property system gets notified of updated values. This is because events - // may be canceled by external code. - if (_isExpanded != value) - { - RoutedEventArgs eventArgs; - - if (value) - { - eventArgs = new RoutedEventArgs(ExpandingEvent, this); - OnExpanding(eventArgs); - } - else - { - eventArgs = new RoutedEventArgs(CollapsingEvent, this); - OnCollapsing(eventArgs); - } - - if (eventArgs.Handled) - { - // If the event was externally handled (canceled) we must still notify the value has changed. - // This property changed notification will update any external code observing this property that itself may have set the new value. - // We are essentially reverted any external state change along with ignoring the IsExpanded property set. - // Remember IsExpanded is usually controlled by a ToggleButton in the control theme. - _ignorePropertyChanged = true; - - RaisePropertyChanged( - IsExpandedProperty, - oldValue: value, - newValue: _isExpanded, - BindingPriority.LocalValue, - isEffectiveValue: true); - - _ignorePropertyChanged = false; - } - else - { - SetAndRaise(IsExpandedProperty, ref _isExpanded, value); - } - } - } + get => GetValue(IsExpandedProperty); + set => SetValue(IsExpandedProperty, value); } /// @@ -332,5 +288,63 @@ namespace Avalonia.Controls PseudoClasses.Set(":expanded", IsExpanded); } + + /// + /// Called when the property has to be coerced. + /// + /// The value to coerce. + protected virtual bool OnCoerceIsExpanded(bool value) + { + RoutedEventArgs eventArgs; + + if (value) + { + eventArgs = new RoutedEventArgs(ExpandingEvent, this); + OnExpanding(eventArgs); + } + else + { + eventArgs = new RoutedEventArgs(CollapsingEvent, this); + OnCollapsing(eventArgs); + } + + if (eventArgs.Handled) + { + // If the event was externally handled (canceled) we must still notify the value has changed. + // This property changed notification will update any external code observing this property that itself may have set the new value. + // We are essentially reverted any external state change along with ignoring the IsExpanded property set. + // Remember IsExpanded is usually controlled by a ToggleButton in the control theme and is also used for animations. + _ignorePropertyChanged = true; + + RaisePropertyChanged( + IsExpandedProperty, + oldValue: value, + newValue: !value, + BindingPriority.LocalValue, + isEffectiveValue: true); + + _ignorePropertyChanged = false; + + return !value; + } + + return value; + } + + /// + /// Coerces/validates the property value. + /// + /// The instance. + /// The value to coerce. + /// The coerced/validated value. + private static bool CoerceIsExpanded(AvaloniaObject instance, bool value) + { + if (instance is Expander expander) + { + return expander.OnCoerceIsExpanded(value); + } + + return value; + } } } From a68cce869f044d726b943388b40d26f3dfe20b2a Mon Sep 17 00:00:00 2001 From: robloo Date: Sun, 15 Jan 2023 19:53:09 -0500 Subject: [PATCH 2/8] Add CancelRoutedEventArgs and use it in Expander --- .../ControlCatalog/Pages/ExpanderPage.xaml.cs | 4 +- .../Interactivity/CancelRoutedEventArgs.cs | 39 +++++++++++++++++++ src/Avalonia.Controls/Expander.cs | 26 ++++++------- 3 files changed, 54 insertions(+), 15 deletions(-) create mode 100644 src/Avalonia.Base/Interactivity/CancelRoutedEventArgs.cs diff --git a/samples/ControlCatalog/Pages/ExpanderPage.xaml.cs b/samples/ControlCatalog/Pages/ExpanderPage.xaml.cs index 98a494e533..c33a0d8bad 100644 --- a/samples/ControlCatalog/Pages/ExpanderPage.xaml.cs +++ b/samples/ControlCatalog/Pages/ExpanderPage.xaml.cs @@ -14,8 +14,8 @@ namespace ControlCatalog.Pages var CollapsingDisabledExpander = this.Get("CollapsingDisabledExpander"); var ExpandingDisabledExpander = this.Get("ExpandingDisabledExpander"); - CollapsingDisabledExpander.Collapsing += (s, e) => { e.Handled = true; }; - ExpandingDisabledExpander.Expanding += (s, e) => { e.Handled = true; }; + CollapsingDisabledExpander.Collapsing += (s, e) => { e.Cancel = true; }; + ExpandingDisabledExpander.Expanding += (s, e) => { e.Cancel = true; }; } private void InitializeComponent() diff --git a/src/Avalonia.Base/Interactivity/CancelRoutedEventArgs.cs b/src/Avalonia.Base/Interactivity/CancelRoutedEventArgs.cs new file mode 100644 index 0000000000..b6913939ab --- /dev/null +++ b/src/Avalonia.Base/Interactivity/CancelRoutedEventArgs.cs @@ -0,0 +1,39 @@ +namespace Avalonia.Interactivity +{ + /// + /// Provides state information and data specific to a cancelable routed event. + /// + public class CancelRoutedEventArgs : RoutedEventArgs + { + /// + /// Initializes a new instance of the class. + /// + public CancelRoutedEventArgs() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The routed event associated with these event args. + public CancelRoutedEventArgs(RoutedEvent? routedEvent) + : base(routedEvent) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The routed event associated with these event args. + /// The source object that raised the routed event. + public CancelRoutedEventArgs(RoutedEvent? routedEvent, object? source) + : base(routedEvent, source) + { + } + + /// + /// Gets or sets a value indicating whether the routed event should be canceled. + /// + public bool Cancel { get; set; } = false; + } +} diff --git a/src/Avalonia.Controls/Expander.cs b/src/Avalonia.Controls/Expander.cs index 93f5e0fc14..c57774c70b 100644 --- a/src/Avalonia.Controls/Expander.cs +++ b/src/Avalonia.Controls/Expander.cs @@ -84,16 +84,16 @@ namespace Avalonia.Controls /// /// Defines the event. /// - public static readonly RoutedEvent ExpandedEvent = - RoutedEvent.Register( + public static readonly RoutedEvent ExpandedEvent = + RoutedEvent.Register( nameof(Expanded), RoutingStrategies.Bubble); /// /// Defines the event. /// - public static readonly RoutedEvent ExpandingEvent = - RoutedEvent.Register( + public static readonly RoutedEvent ExpandingEvent = + RoutedEvent.Register( nameof(Expanding), RoutingStrategies.Bubble); @@ -149,10 +149,10 @@ namespace Avalonia.Controls /// Occurs as the content area is closing. /// /// - /// The event args property may be set to true to cancel the event + /// The event args property may be set to true to cancel the event /// and keep the control open (expanded). /// - public event EventHandler? Collapsing + public event EventHandler? Collapsing { add => AddHandler(CollapsingEvent, value); remove => RemoveHandler(CollapsingEvent, value); @@ -171,10 +171,10 @@ namespace Avalonia.Controls /// Occurs as the content area is opening. /// /// - /// The event args property may be set to true to cancel the event + /// The event args property may be set to true to cancel the event /// and keep the control closed (collapsed). /// - public event EventHandler? Expanding + public event EventHandler? Expanding { add => AddHandler(ExpandingEvent, value); remove => RemoveHandler(ExpandingEvent, value); @@ -295,22 +295,22 @@ namespace Avalonia.Controls /// The value to coerce. protected virtual bool OnCoerceIsExpanded(bool value) { - RoutedEventArgs eventArgs; + CancelRoutedEventArgs eventArgs; if (value) { - eventArgs = new RoutedEventArgs(ExpandingEvent, this); + eventArgs = new CancelRoutedEventArgs(ExpandingEvent, this); OnExpanding(eventArgs); } else { - eventArgs = new RoutedEventArgs(CollapsingEvent, this); + eventArgs = new CancelRoutedEventArgs(CollapsingEvent, this); OnCollapsing(eventArgs); } - if (eventArgs.Handled) + if (eventArgs.Cancel) { - // If the event was externally handled (canceled) we must still notify the value has changed. + // If the event was externally canceled we must still notify the value has changed. // This property changed notification will update any external code observing this property that itself may have set the new value. // We are essentially reverted any external state change along with ignoring the IsExpanded property set. // Remember IsExpanded is usually controlled by a ToggleButton in the control theme and is also used for animations. From cfb90c8201378dab7afc8d75316ea9c069c2c79c Mon Sep 17 00:00:00 2001 From: robloo Date: Mon, 16 Jan 2023 08:18:47 -0500 Subject: [PATCH 3/8] Add Interactive.AddHandler() for CancelRoutedEventArgs This hopefully fixes unit tests due to wrong overload being selected by the compiler. --- .../Interactivity/Interactive.cs | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Base/Interactivity/Interactive.cs b/src/Avalonia.Base/Interactivity/Interactive.cs index 821e00d784..3bdaa60d2e 100644 --- a/src/Avalonia.Base/Interactivity/Interactive.cs +++ b/src/Avalonia.Base/Interactivity/Interactive.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using Avalonia.Layout; -using Avalonia.VisualTree; #nullable enable @@ -67,7 +66,38 @@ namespace Avalonia.Interactivity typedHandler(sender, typedArgs); } - var subscription = new EventSubscription(handler, routes, handledEventsToo, (baseHandler, sender, args) => InvokeAdapter(baseHandler, sender, args)); + var subscription = new EventSubscription(handler, routes, handledEventsToo, InvokeAdapter); + + AddEventSubscription(routedEvent, subscription); + } + + /// + /// Adds a handler for the specified routed event. + /// + /// The routed event. + /// The handler. + /// The routing strategies to listen to. + /// Whether handled events should also be listened for. + public void AddHandler( + RoutedEvent routedEvent, + EventHandler? handler, + RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble, + bool handledEventsToo = false) + { + routedEvent = routedEvent ?? throw new ArgumentNullException(nameof(routedEvent)); + + if (handler is null) + return; + + static void InvokeAdapter(Delegate baseHandler, object sender, RoutedEventArgs args) + { + var typedHandler = (EventHandler)baseHandler; + var typedArgs = (CancelRoutedEventArgs)args; + + typedHandler(sender, typedArgs); + } + + var subscription = new EventSubscription(handler, routes, handledEventsToo, InvokeAdapter); AddEventSubscription(routedEvent, subscription); } From dca768b9a8b3c053bb0b5865772b24ff8e95caa8 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Mon, 16 Jan 2023 17:20:21 +0100 Subject: [PATCH 4/8] feat: Enable Rule CS0649 --- .editorconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.editorconfig b/.editorconfig index 3620896f34..eac5870f96 100644 --- a/.editorconfig +++ b/.editorconfig @@ -137,6 +137,9 @@ space_within_single_line_array_initializer_braces = true #Net Analyzer dotnet_analyzer_diagnostic.category-Performance.severity = none #error - Uncomment when all violations are fixed. +# CS0649: Field 'field' is never assigned to, and will always have its default value 'value' +dotnet_diagnostic.CS0649.severity = error + # CS1591: Missing XML comment for publicly visible type or member dotnet_diagnostic.CS1591.severity = suggestion From 9bb1b2375502d56379d230d8a95ad453ad230058 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Tue, 17 Jan 2023 08:57:24 +0100 Subject: [PATCH 5/8] feat: Address Rule CS0649 https://github.com/AvaloniaUI/Avalonia/discussions/9990#discussioncomment-4701870 --- src/Avalonia.FreeDesktop/DBusIme/DBusTextInputMethodBase.cs | 4 ++-- src/Avalonia.X11/X11Window.Xim.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.FreeDesktop/DBusIme/DBusTextInputMethodBase.cs b/src/Avalonia.FreeDesktop/DBusIme/DBusTextInputMethodBase.cs index 899288b4a8..7d97c7cd36 100644 --- a/src/Avalonia.FreeDesktop/DBusIme/DBusTextInputMethodBase.cs +++ b/src/Avalonia.FreeDesktop/DBusIme/DBusTextInputMethodBase.cs @@ -34,7 +34,7 @@ namespace Avalonia.FreeDesktop.DBusIme private bool _connecting; private string? _currentName; private DBusCallQueue _queue; - private bool _controlActive, _windowActive; + private bool _windowActive; private bool? _imeActive; private Rect _logicalRect; private PixelRect? _lastReportedRect; @@ -189,7 +189,7 @@ namespace Avalonia.FreeDesktop.DBusIme if(!IsConnected) return; - var active = _windowActive && _controlActive; + var active = _windowActive && IsActive; if (active != _imeActive) { _imeActive = active; diff --git a/src/Avalonia.X11/X11Window.Xim.cs b/src/Avalonia.X11/X11Window.Xim.cs index 10aece7f2e..8446d35cc6 100644 --- a/src/Avalonia.X11/X11Window.Xim.cs +++ b/src/Avalonia.X11/X11Window.Xim.cs @@ -13,7 +13,7 @@ namespace Avalonia.X11 class XimInputMethod : ITextInputMethodImpl, IX11InputMethodControl { private readonly X11Window _parent; - private bool _controlActive, _windowActive, _imeActive; + private bool _windowActive, _imeActive; private Rect? _queuedCursorRect; private ITextInputMethodClient? _client; @@ -70,7 +70,7 @@ namespace Avalonia.X11 private void UpdateActive() { - var active = _windowActive && _controlActive; + var active = _windowActive && IsActive; if(_parent._xic == IntPtr.Zero) return; if (active != _imeActive) From ae78a2bc0330d1a3afc8dbb63112230944c7f273 Mon Sep 17 00:00:00 2001 From: robloo Date: Wed, 18 Jan 2023 09:55:30 -0500 Subject: [PATCH 6/8] Revert "Add Interactive.AddHandler() for CancelRoutedEventArgs" This reverts commit cfb90c8201378dab7afc8d75316ea9c069c2c79c. --- .../Interactivity/Interactive.cs | 34 ++----------------- 1 file changed, 2 insertions(+), 32 deletions(-) diff --git a/src/Avalonia.Base/Interactivity/Interactive.cs b/src/Avalonia.Base/Interactivity/Interactive.cs index 3bdaa60d2e..821e00d784 100644 --- a/src/Avalonia.Base/Interactivity/Interactive.cs +++ b/src/Avalonia.Base/Interactivity/Interactive.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using Avalonia.Layout; +using Avalonia.VisualTree; #nullable enable @@ -66,38 +67,7 @@ namespace Avalonia.Interactivity typedHandler(sender, typedArgs); } - var subscription = new EventSubscription(handler, routes, handledEventsToo, InvokeAdapter); - - AddEventSubscription(routedEvent, subscription); - } - - /// - /// Adds a handler for the specified routed event. - /// - /// The routed event. - /// The handler. - /// The routing strategies to listen to. - /// Whether handled events should also be listened for. - public void AddHandler( - RoutedEvent routedEvent, - EventHandler? handler, - RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble, - bool handledEventsToo = false) - { - routedEvent = routedEvent ?? throw new ArgumentNullException(nameof(routedEvent)); - - if (handler is null) - return; - - static void InvokeAdapter(Delegate baseHandler, object sender, RoutedEventArgs args) - { - var typedHandler = (EventHandler)baseHandler; - var typedArgs = (CancelRoutedEventArgs)args; - - typedHandler(sender, typedArgs); - } - - var subscription = new EventSubscription(handler, routes, handledEventsToo, InvokeAdapter); + var subscription = new EventSubscription(handler, routes, handledEventsToo, (baseHandler, sender, args) => InvokeAdapter(baseHandler, sender, args)); AddEventSubscription(routedEvent, subscription); } From c8c3b151b0ffa44d4382e98b5da04572711d9ef8 Mon Sep 17 00:00:00 2001 From: robloo Date: Wed, 18 Jan 2023 09:56:45 -0500 Subject: [PATCH 7/8] Fix expander event args type --- src/Avalonia.Controls/Expander.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Controls/Expander.cs b/src/Avalonia.Controls/Expander.cs index c57774c70b..2ad6a58d38 100644 --- a/src/Avalonia.Controls/Expander.cs +++ b/src/Avalonia.Controls/Expander.cs @@ -76,16 +76,16 @@ namespace Avalonia.Controls /// /// Defines the event. /// - public static readonly RoutedEvent CollapsingEvent = - RoutedEvent.Register( + public static readonly RoutedEvent CollapsingEvent = + RoutedEvent.Register( nameof(Collapsing), RoutingStrategies.Bubble); /// /// Defines the event. /// - public static readonly RoutedEvent ExpandedEvent = - RoutedEvent.Register( + public static readonly RoutedEvent ExpandedEvent = + RoutedEvent.Register( nameof(Expanded), RoutingStrategies.Bubble); From 6189f52c1f5ae6e887b46b9f0a879d7a0bc9a2a2 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Wed, 25 Jan 2023 18:20:55 +0100 Subject: [PATCH 8/8] fix: GpuInterop Build --- samples/GpuInterop/D3DDemo/D3D11DemoControl.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/samples/GpuInterop/D3DDemo/D3D11DemoControl.cs b/samples/GpuInterop/D3DDemo/D3D11DemoControl.cs index c7ee2bb9a4..8b1347e1d8 100644 --- a/samples/GpuInterop/D3DDemo/D3D11DemoControl.cs +++ b/samples/GpuInterop/D3DDemo/D3D11DemoControl.cs @@ -2,21 +2,17 @@ using System; using System.Diagnostics; using System.Linq; using System.Numerics; -using System.Threading.Tasks; using Avalonia; using Avalonia.Platform; using Avalonia.Rendering.Composition; using SharpDX; -using SharpDX.Direct2D1; using SharpDX.Direct3D11; using SharpDX.DXGI; using SharpDX.Mathematics.Interop; using Buffer = SharpDX.Direct3D11.Buffer; -using DeviceContext = SharpDX.Direct2D1.DeviceContext; using DxgiFactory1 = SharpDX.DXGI.Factory1; using Matrix = SharpDX.Matrix; using D3DDevice = SharpDX.Direct3D11.Device; -using DxgiResource = SharpDX.DXGI.Resource; using FeatureLevel = SharpDX.Direct3D.FeatureLevel; using Vector3 = SharpDX.Vector3; @@ -80,7 +76,10 @@ public class D3D11DemoControl : DrawingSurfaceDemoBase if (pixelSize == default) return; if (pixelSize != _lastSize) + { + _lastSize = pixelSize; Resize(pixelSize); + } using (_swapchain.BeginDraw(pixelSize, out var renderView)) {