diff --git a/samples/IntegrationTestApp/MainWindow.axaml b/samples/IntegrationTestApp/MainWindow.axaml index a6f463fdcb..353e01dca7 100644 --- a/samples/IntegrationTestApp/MainWindow.axaml +++ b/samples/IntegrationTestApp/MainWindow.axaml @@ -153,6 +153,9 @@ + + + diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs index 2c9efc7767..d89d6f3690 100644 --- a/src/Avalonia.Base/AvaloniaObject.cs +++ b/src/Avalonia.Base/AvaloniaObject.cs @@ -664,14 +664,12 @@ namespace Avalonia /// The property that has changed. /// The old property value. /// The new property value. - /// The priority of the binding that produced the value. protected void RaisePropertyChanged( DirectPropertyBase property, - Optional oldValue, - BindingValue newValue, - BindingPriority priority = BindingPriority.LocalValue) + T oldValue, + T newValue) { - RaisePropertyChanged(property, oldValue, newValue, priority, true); + RaisePropertyChanged(property, oldValue, newValue, BindingPriority.LocalValue, true); } /// @@ -720,7 +718,7 @@ namespace Avalonia /// /// True if the value changed, otherwise false. /// - protected bool SetAndRaise(AvaloniaProperty property, ref T field, T value) + protected bool SetAndRaise(DirectPropertyBase property, ref T field, T value) { VerifyAccess(); diff --git a/src/Avalonia.Base/Data/Core/IndexerNodeBase.cs b/src/Avalonia.Base/Data/Core/IndexerNodeBase.cs index 9ec256225b..57e4fa4a8e 100644 --- a/src/Avalonia.Base/Data/Core/IndexerNodeBase.cs +++ b/src/Avalonia.Base/Data/Core/IndexerNodeBase.cs @@ -22,7 +22,7 @@ namespace Avalonia.Data.Core if (target is INotifyPropertyChanged inpc) { - WeakEvents.PropertyChanged.Subscribe(inpc, this); + WeakEvents.ThreadSafePropertyChanged.Subscribe(inpc, this); } ValueChanged(GetValue(target)); @@ -39,7 +39,7 @@ namespace Avalonia.Data.Core if (target is INotifyPropertyChanged inpc) { - WeakEvents.PropertyChanged.Unsubscribe(inpc, this); + WeakEvents.ThreadSafePropertyChanged.Unsubscribe(inpc, this); } } } diff --git a/src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs b/src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs index 7c2caf02b4..e8e3e6d509 100644 --- a/src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs +++ b/src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs @@ -160,7 +160,7 @@ namespace Avalonia.Data.Core.Plugins var inpc = GetReferenceTarget() as INotifyPropertyChanged; if (inpc != null) - WeakEvents.PropertyChanged.Unsubscribe(inpc, this); + WeakEvents.ThreadSafePropertyChanged.Unsubscribe(inpc, this); } private object? GetReferenceTarget() @@ -185,7 +185,7 @@ namespace Avalonia.Data.Core.Plugins var inpc = GetReferenceTarget() as INotifyPropertyChanged; if (inpc != null) - WeakEvents.PropertyChanged.Subscribe(inpc, this); + WeakEvents.ThreadSafePropertyChanged.Subscribe(inpc, this); } } } diff --git a/src/Avalonia.Base/Layout/LayoutInformation.cs b/src/Avalonia.Base/Layout/LayoutInformation.cs new file mode 100644 index 0000000000..9b821053a2 --- /dev/null +++ b/src/Avalonia.Base/Layout/LayoutInformation.cs @@ -0,0 +1,27 @@ +namespace Avalonia.Layout; + +/// +/// Provides access to layout information of a control. +/// +public static class LayoutInformation +{ + /// + /// Gets the available size constraint passed in the previous layout pass. + /// + /// The control. + /// Previous control measure constraint, if any. + public static Size? GetPreviousMeasureConstraint(Layoutable control) + { + return control.PreviousMeasure; + } + + /// + /// Gets the control bounds used in the previous layout arrange pass. + /// + /// The control. + /// Previous control arrange bounds, if any. + public static Rect? GetPreviousArrangeBounds(Layoutable control) + { + return control.PreviousArrange; + } +} diff --git a/src/Avalonia.Base/Layout/Layoutable.cs b/src/Avalonia.Base/Layout/Layoutable.cs index 775b8adddd..4a273b0291 100644 --- a/src/Avalonia.Base/Layout/Layoutable.cs +++ b/src/Avalonia.Base/Layout/Layoutable.cs @@ -323,6 +323,9 @@ namespace Avalonia.Layout set { SetValue(UseLayoutRoundingProperty, value); } } + /// + /// Gets the available size passed in the previous layout pass, if any. + /// internal Size? PreviousMeasure => _previousMeasure; /// diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs index e33dc999dc..98be861afa 100644 --- a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs @@ -48,7 +48,8 @@ namespace Avalonia.Rendering.Composition.Server { canvas.PostTransform = Matrix.Identity; canvas.Transform = Matrix.Identity; - canvas.PushClip(AdornedVisual._combinedTransformedClipBounds); + if (AdornerIsClipped) + canvas.PushClip(AdornedVisual._combinedTransformedClipBounds); } var transform = GlobalTransformMatrix; canvas.PostTransform = MatrixUtils.ToMatrix(transform); @@ -74,7 +75,7 @@ namespace Avalonia.Rendering.Composition.Server canvas.PopGeometryClip(); if (ClipToBounds && !HandlesClipToBounds) canvas.PopClip(); - if (AdornedVisual != null) + if (AdornedVisual != null && AdornerIsClipped) canvas.PopClip(); if(Opacity != 1) canvas.PopOpacity(); diff --git a/src/Avalonia.Base/StyledElement.cs b/src/Avalonia.Base/StyledElement.cs index 2cdb973174..5b8dac2f53 100644 --- a/src/Avalonia.Base/StyledElement.cs +++ b/src/Avalonia.Base/StyledElement.cs @@ -524,13 +524,7 @@ namespace Avalonia NotifyResourcesChanged(); } -#nullable disable - RaisePropertyChanged( - ParentProperty, - new Optional(old), - new BindingValue(Parent), - BindingPriority.LocalValue); -#nullable enable + RaisePropertyChanged(ParentProperty, old, Parent); } } diff --git a/src/Avalonia.Base/Utilities/WeakEvents.cs b/src/Avalonia.Base/Utilities/WeakEvents.cs index 6da899bab2..2f62564e0e 100644 --- a/src/Avalonia.Base/Utilities/WeakEvents.cs +++ b/src/Avalonia.Base/Utilities/WeakEvents.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Specialized; using System.ComponentModel; using System.Windows.Input; +using Avalonia.Threading; namespace Avalonia.Utilities; @@ -20,15 +21,30 @@ public class WeakEvents }); /// - /// Represents PropertyChanged event from + /// Represents PropertyChanged event from with auto-dispatching to the UI thread /// public static readonly WeakEvent - PropertyChanged = WeakEvent.Register( + ThreadSafePropertyChanged = WeakEvent.Register( (s, h) => { - PropertyChangedEventHandler handler = (_, e) => h(s, e); + bool unsubscribed = false; + PropertyChangedEventHandler handler = (_, e) => + { + if (Dispatcher.UIThread.CheckAccess()) + h(s, e); + else + Dispatcher.UIThread.Post(() => + { + if (!unsubscribed) + h(s, e); + }); + }; s.PropertyChanged += handler; - return () => s.PropertyChanged -= handler; + return () => + { + unsubscribed = true; + s.PropertyChanged -= handler; + }; }); diff --git a/src/Avalonia.Base/Visual.cs b/src/Avalonia.Base/Visual.cs index 87bb1d3790..8b0cc06136 100644 --- a/src/Avalonia.Base/Visual.cs +++ b/src/Avalonia.Base/Visual.cs @@ -573,7 +573,7 @@ namespace Avalonia /// The new visual parent. protected virtual void OnVisualParentChanged(Visual? oldParent, Visual? newParent) { - RaisePropertyChanged(VisualParentProperty, oldParent, newParent, BindingPriority.LocalValue); + RaisePropertyChanged(VisualParentProperty, oldParent, newParent); } internal override ParametrizedLogger? GetBindingWarningLogger( diff --git a/src/Avalonia.Base/composition-schema.xml b/src/Avalonia.Base/composition-schema.xml index 36fd9fe709..31722974ee 100644 --- a/src/Avalonia.Base/composition-schema.xml +++ b/src/Avalonia.Base/composition-schema.xml @@ -26,6 +26,7 @@ + diff --git a/src/Avalonia.Controls/Automation/Peers/SliderAutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/SliderAutomationPeer.cs new file mode 100644 index 0000000000..42b15eec96 --- /dev/null +++ b/src/Avalonia.Controls/Automation/Peers/SliderAutomationPeer.cs @@ -0,0 +1,22 @@ +using Avalonia.Automation.Peers; + +namespace Avalonia.Controls.Automation.Peers +{ + public class SliderAutomationPeer : RangeBaseAutomationPeer + { + public SliderAutomationPeer(Slider owner) : base(owner) + { + } + + override protected string GetClassNameCore() + { + return "Slider"; + } + + override protected AutomationControlType GetAutomationControlTypeCore() + { + return AutomationControlType.Slider; + } + + } +} diff --git a/src/Avalonia.Controls/Primitives/AdornerLayer.cs b/src/Avalonia.Controls/Primitives/AdornerLayer.cs index 79719912ea..611d57a980 100644 --- a/src/Avalonia.Controls/Primitives/AdornerLayer.cs +++ b/src/Avalonia.Controls/Primitives/AdornerLayer.cs @@ -279,8 +279,11 @@ namespace Avalonia.Controls.Primitives private void UpdateAdornedElement(Visual adorner, Visual? adorned) { if (adorner.CompositionVisual != null) + { adorner.CompositionVisual.AdornedVisual = adorned?.CompositionVisual; - + adorner.CompositionVisual.AdornerIsClipped = GetIsClipEnabled(adorner); + } + var info = adorner.GetValue(s_adornedElementInfoProperty); if (info != null) diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs index 065c4ff2e5..2ee32b0dda 100644 --- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs +++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs @@ -345,10 +345,7 @@ namespace Avalonia.Controls.Primitives if (_oldSelectedItems != SelectedItems) { - RaisePropertyChanged( - SelectedItemsProperty, - new Optional(_oldSelectedItems), - new BindingValue(SelectedItems)); + RaisePropertyChanged(SelectedItemsProperty, _oldSelectedItems, SelectedItems); _oldSelectedItems = SelectedItems; } } @@ -909,10 +906,7 @@ namespace Avalonia.Controls.Primitives else if (e.PropertyName == nameof(InternalSelectionModel.WritableSelectedItems) && _oldSelectedItems != (Selection as InternalSelectionModel)?.SelectedItems) { - RaisePropertyChanged( - SelectedItemsProperty, - new Optional(_oldSelectedItems), - new BindingValue(SelectedItems)); + RaisePropertyChanged(SelectedItemsProperty, _oldSelectedItems, SelectedItems); _oldSelectedItems = SelectedItems; } else if (e.PropertyName == nameof(ISelectionModel.Source)) diff --git a/src/Avalonia.Controls/RelativePanel.AttachedProperties.cs b/src/Avalonia.Controls/RelativePanel.AttachedProperties.cs index 18cf96ddca..d2b91def7d 100644 --- a/src/Avalonia.Controls/RelativePanel.AttachedProperties.cs +++ b/src/Avalonia.Controls/RelativePanel.AttachedProperties.cs @@ -1,37 +1,30 @@ using Avalonia.Layout; +using Avalonia.Threading; namespace Avalonia.Controls { public partial class RelativePanel { - private static void OnAlignPropertiesChanged(AvaloniaObject d, AvaloniaPropertyChangedEventArgs e) - { - if (d is Layoutable layoutable && layoutable.Parent is Layoutable layoutableParent) - { - layoutableParent.InvalidateArrange(); - } - } static RelativePanel() { ClipToBoundsProperty.OverrideDefaultValue(true); - AboveProperty.Changed.AddClassHandler(OnAlignPropertiesChanged); - AlignBottomWithPanelProperty.Changed.AddClassHandler(OnAlignPropertiesChanged); - AlignBottomWithProperty.Changed.AddClassHandler(OnAlignPropertiesChanged); - AlignHorizontalCenterWithPanelProperty.Changed.AddClassHandler(OnAlignPropertiesChanged); - AlignHorizontalCenterWithProperty.Changed.AddClassHandler(OnAlignPropertiesChanged); - AlignLeftWithPanelProperty.Changed.AddClassHandler(OnAlignPropertiesChanged); - AlignLeftWithProperty.Changed.AddClassHandler(OnAlignPropertiesChanged); - AlignRightWithPanelProperty.Changed.AddClassHandler(OnAlignPropertiesChanged); - AlignRightWithProperty.Changed.AddClassHandler(OnAlignPropertiesChanged); - AlignTopWithPanelProperty.Changed.AddClassHandler(OnAlignPropertiesChanged); - AlignTopWithProperty.Changed.AddClassHandler(OnAlignPropertiesChanged); - AlignVerticalCenterWithPanelProperty.Changed.AddClassHandler(OnAlignPropertiesChanged); - AlignVerticalCenterWithProperty.Changed.AddClassHandler(OnAlignPropertiesChanged); - BelowProperty.Changed.AddClassHandler(OnAlignPropertiesChanged); - LeftOfProperty.Changed.AddClassHandler(OnAlignPropertiesChanged); - RightOfProperty.Changed.AddClassHandler(OnAlignPropertiesChanged); + AffectsParentArrange( + AlignLeftWithPanelProperty, AlignLeftWithProperty, LeftOfProperty, + AlignRightWithPanelProperty, AlignRightWithProperty, RightOfProperty, + AlignTopWithPanelProperty, AlignTopWithProperty, AboveProperty, + AlignBottomWithPanelProperty, AlignBottomWithProperty, BelowProperty, + AlignHorizontalCenterWithPanelProperty, AlignHorizontalCenterWithProperty, + AlignVerticalCenterWithPanelProperty, AlignVerticalCenterWithProperty); + + AffectsParentMeasure( + AlignLeftWithPanelProperty, AlignLeftWithProperty, LeftOfProperty, + AlignRightWithPanelProperty, AlignRightWithProperty, RightOfProperty, + AlignTopWithPanelProperty, AlignTopWithProperty, AboveProperty, + AlignBottomWithPanelProperty, AlignBottomWithProperty, BelowProperty, + AlignHorizontalCenterWithPanelProperty, AlignHorizontalCenterWithProperty, + AlignVerticalCenterWithPanelProperty, AlignVerticalCenterWithProperty); } /// diff --git a/src/Avalonia.Controls/Slider.cs b/src/Avalonia.Controls/Slider.cs index 828bf2a1fb..7de726a932 100644 --- a/src/Avalonia.Controls/Slider.cs +++ b/src/Avalonia.Controls/Slider.cs @@ -10,6 +10,7 @@ using Avalonia.Interactivity; using Avalonia.Layout; using Avalonia.Utilities; using Avalonia.Automation; +using Avalonia.Controls.Automation.Peers; namespace Avalonia.Controls { @@ -380,6 +381,11 @@ namespace Avalonia.Controls } } + protected override AutomationPeer OnCreateAutomationPeer() + { + return new SliderAutomationPeer(this); + } + /// protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { diff --git a/src/Avalonia.Diagnostics/Diagnostics/Controls/FilterTextBox.axaml b/src/Avalonia.Diagnostics/Diagnostics/Controls/FilterTextBox.axaml index 3bfe511fbc..1b5f431f36 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/Controls/FilterTextBox.axaml +++ b/src/Avalonia.Diagnostics/Diagnostics/Controls/FilterTextBox.axaml @@ -16,6 +16,8 @@