From 8d2f1475ca329b7c2ffaf6c2c7f4160d755c60ed Mon Sep 17 00:00:00 2001 From: Julien Lebosquain Date: Fri, 23 Jan 2026 07:26:26 +0000 Subject: [PATCH] Remove IInternalScroller and move IScrollable to Avalonia.Base (#20519) * Remove IInternalScroller and move IScrollable to Avalonia.Base * Update API suppressions --- api/Avalonia.Win32.Interoperability.nupkg.xml | 4 +- api/Avalonia.nupkg.xml | 12 ++++++ .../Controls/IInternalScroller.cs | 11 ------ .../Controls/Primitives}/IScrollable.cs | 10 +++++ .../Input/Navigation/XYFocus.FindElements.cs | 4 +- .../Input/Navigation/XYFocus.Impl.cs | 2 +- .../Presenters/ItemsPresenter.cs | 38 +++++++++---------- .../Primitives/ILogicalScrollable.cs | 4 +- src/Avalonia.Controls/ScrollViewer.cs | 6 +-- .../VirtualizingCarouselPanel.cs | 18 ++++++++- 10 files changed, 67 insertions(+), 42 deletions(-) delete mode 100644 src/Avalonia.Base/Controls/IInternalScroller.cs rename src/{Avalonia.Controls => Avalonia.Base/Controls/Primitives}/IScrollable.cs (63%) diff --git a/api/Avalonia.Win32.Interoperability.nupkg.xml b/api/Avalonia.Win32.Interoperability.nupkg.xml index 33fc2ac062..3672bb9b99 100644 --- a/api/Avalonia.Win32.Interoperability.nupkg.xml +++ b/api/Avalonia.Win32.Interoperability.nupkg.xml @@ -1,4 +1,4 @@ - + @@ -37,4 +37,4 @@ baseline/Avalonia.Win32.Interoperability/lib/net8.0-windows7.0/Avalonia.Win32.Interoperability.dll current/Avalonia.Win32.Interoperability/lib/net8.0-windows7.0/Avalonia.Win32.Interoperability.dll - \ No newline at end of file + diff --git a/api/Avalonia.nupkg.xml b/api/Avalonia.nupkg.xml index 9dcc016cbd..dd1ea2ac88 100644 --- a/api/Avalonia.nupkg.xml +++ b/api/Avalonia.nupkg.xml @@ -7,6 +7,12 @@ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll current/Avalonia/lib/net10.0/Avalonia.Base.dll + + CP0001 + T:Avalonia.Controls.Primitives.IScrollable + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll + CP0001 T:Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings.CompiledBindingPath @@ -25,6 +31,12 @@ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll current/Avalonia/lib/net8.0/Avalonia.Base.dll + + CP0001 + T:Avalonia.Controls.Primitives.IScrollable + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll + CP0001 T:Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings.CompiledBindingPath diff --git a/src/Avalonia.Base/Controls/IInternalScroller.cs b/src/Avalonia.Base/Controls/IInternalScroller.cs deleted file mode 100644 index 226626731b..0000000000 --- a/src/Avalonia.Base/Controls/IInternalScroller.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Runtime.CompilerServices; - -namespace Avalonia.Controls.Primitives; - -// TODO12: Integrate with existing IScrollable interface, breaking change -internal interface IInternalScroller -{ - bool CanHorizontallyScroll { get; } - - bool CanVerticallyScroll { get; } -} diff --git a/src/Avalonia.Controls/IScrollable.cs b/src/Avalonia.Base/Controls/Primitives/IScrollable.cs similarity index 63% rename from src/Avalonia.Controls/IScrollable.cs rename to src/Avalonia.Base/Controls/Primitives/IScrollable.cs index 680088290c..ceaaf38c05 100644 --- a/src/Avalonia.Controls/IScrollable.cs +++ b/src/Avalonia.Base/Controls/Primitives/IScrollable.cs @@ -19,5 +19,15 @@ namespace Avalonia.Controls.Primitives /// Gets the size of the viewport, in logical units. /// Size Viewport { get; } + + /// + /// Gets a value indicating whether the content can be scrolled horizontally. + /// + bool CanHorizontallyScroll { get; } + + /// + /// Gets a value indicating whether the content can be scrolled horizontally. + /// + bool CanVerticallyScroll { get; } } } diff --git a/src/Avalonia.Base/Input/Navigation/XYFocus.FindElements.cs b/src/Avalonia.Base/Input/Navigation/XYFocus.FindElements.cs index a4eea7d88b..0f529142ca 100644 --- a/src/Avalonia.Base/Input/Navigation/XYFocus.FindElements.cs +++ b/src/Avalonia.Base/Input/Navigation/XYFocus.FindElements.cs @@ -78,7 +78,7 @@ public partial class XYFocus return false; } - var closestScroller = candidate.FindAncestorOfType(true); + var closestScroller = candidate.FindAncestorOfType(true); return ReferenceEquals(closestScroller, activeScroller); } @@ -93,7 +93,7 @@ public partial class XYFocus var parent = activeScroller.Parent; while (parent != null) { - if (parent is IInternalScroller and Visual visual + if (parent is IScrollable and Visual visual && visual.IsVisualAncestorOf(candidate)) { return true; diff --git a/src/Avalonia.Base/Input/Navigation/XYFocus.Impl.cs b/src/Avalonia.Base/Input/Navigation/XYFocus.Impl.cs index 20267f4c0c..929d92a650 100644 --- a/src/Avalonia.Base/Input/Navigation/XYFocus.Impl.cs +++ b/src/Avalonia.Base/Input/Navigation/XYFocus.Impl.cs @@ -415,7 +415,7 @@ public partial class XYFocus while (parent != null) { var element = parent; - if (element is IInternalScroller scrollable) + if (element is IScrollable scrollable) { var isHorizontallyScrollable = scrollable.CanHorizontallyScroll; var isVerticallyScrollable = scrollable.CanVerticallyScroll; diff --git a/src/Avalonia.Controls/Presenters/ItemsPresenter.cs b/src/Avalonia.Controls/Presenters/ItemsPresenter.cs index 3b9f0b08d3..2d775e2be0 100644 --- a/src/Avalonia.Controls/Presenters/ItemsPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ItemsPresenter.cs @@ -45,35 +45,35 @@ namespace Avalonia.Controls.Presenters /// Gets the owner . /// internal ItemsControl? ItemsControl { get; private set; } + + private bool CanHorizontallyScroll + => _logicalScrollable?.CanHorizontallyScroll ?? false; - bool ILogicalScrollable.CanHorizontallyScroll + bool ILogicalScrollable.CanHorizontallyScroll { - get => _logicalScrollable?.CanHorizontallyScroll ?? false; - set - { - if (_logicalScrollable is not null) - _logicalScrollable.CanHorizontallyScroll = value; - } + get => CanHorizontallyScroll; + set => _logicalScrollable?.CanHorizontallyScroll = value; } - bool ILogicalScrollable.CanVerticallyScroll + bool IScrollable.CanHorizontallyScroll + => CanHorizontallyScroll; + + private bool CanVerticallyScroll + => _logicalScrollable?.CanVerticallyScroll ?? false; + + bool ILogicalScrollable.CanVerticallyScroll { - get => _logicalScrollable?.CanVerticallyScroll ?? false; - set - { - if (_logicalScrollable is not null) - _logicalScrollable.CanVerticallyScroll = value; - } + get => CanVerticallyScroll; + set => _logicalScrollable?.CanVerticallyScroll = value; } + bool IScrollable.CanVerticallyScroll + => CanVerticallyScroll; + Vector IScrollable.Offset { get => _logicalScrollable?.Offset ?? default; - set - { - if (_logicalScrollable is not null) - _logicalScrollable.Offset = value; - } + set => _logicalScrollable?.Offset = value; } bool ILogicalScrollable.IsLogicalScrollEnabled => _logicalScrollable?.IsLogicalScrollEnabled ?? false; diff --git a/src/Avalonia.Controls/Primitives/ILogicalScrollable.cs b/src/Avalonia.Controls/Primitives/ILogicalScrollable.cs index e0c6241620..6c96287c10 100644 --- a/src/Avalonia.Controls/Primitives/ILogicalScrollable.cs +++ b/src/Avalonia.Controls/Primitives/ILogicalScrollable.cs @@ -19,12 +19,12 @@ namespace Avalonia.Controls.Primitives /// /// Gets or sets a value indicating whether the content can be scrolled horizontally. /// - bool CanHorizontallyScroll { get; set; } + new bool CanHorizontallyScroll { get; set; } /// /// Gets or sets a value indicating whether the content can be scrolled horizontally. /// - bool CanVerticallyScroll { get; set; } + new bool CanVerticallyScroll { get; set; } /// /// Gets a value indicating whether logical scrolling is enabled on the control. diff --git a/src/Avalonia.Controls/ScrollViewer.cs b/src/Avalonia.Controls/ScrollViewer.cs index 3152eec2db..e93180fff4 100644 --- a/src/Avalonia.Controls/ScrollViewer.cs +++ b/src/Avalonia.Controls/ScrollViewer.cs @@ -14,7 +14,7 @@ namespace Avalonia.Controls /// [TemplatePart("PART_HorizontalScrollBar", typeof(ScrollBar))] [TemplatePart("PART_VerticalScrollBar", typeof(ScrollBar))] - public class ScrollViewer : ContentControl, IScrollable, IScrollAnchorProvider, IInternalScroller + public class ScrollViewer : ContentControl, IScrollable, IScrollAnchorProvider { /// /// Defines the property. @@ -284,7 +284,7 @@ namespace Avalonia.Controls get => HorizontalScrollBarVisibility != ScrollBarVisibility.Disabled; } - bool IInternalScroller.CanHorizontallyScroll => CanHorizontallyScroll; + bool IScrollable.CanHorizontallyScroll => CanHorizontallyScroll; /// /// Gets a value indicating whether the viewer can scroll vertically. @@ -294,7 +294,7 @@ namespace Avalonia.Controls get => VerticalScrollBarVisibility != ScrollBarVisibility.Disabled; } - bool IInternalScroller.CanVerticallyScroll => CanVerticallyScroll; + bool IScrollable.CanVerticallyScroll => CanVerticallyScroll; /// public Control? CurrentAnchor => (Presenter as IScrollAnchorProvider)?.CurrentAnchor; diff --git a/src/Avalonia.Controls/VirtualizingCarouselPanel.cs b/src/Avalonia.Controls/VirtualizingCarouselPanel.cs index 8b67f01fb1..454069b4b2 100644 --- a/src/Avalonia.Controls/VirtualizingCarouselPanel.cs +++ b/src/Avalonia.Controls/VirtualizingCarouselPanel.cs @@ -29,9 +29,23 @@ namespace Avalonia.Controls private int _transitionFromIndex = -1; private CancellationTokenSource? _transition; private EventHandler? _scrollInvalidated; + private bool _canHorizontallyScroll; + private bool _canVerticallyScroll; - bool ILogicalScrollable.CanHorizontallyScroll { get; set; } - bool ILogicalScrollable.CanVerticallyScroll { get; set; } + bool ILogicalScrollable.CanHorizontallyScroll + { + get => _canHorizontallyScroll; + set => _canHorizontallyScroll = value; + } + + bool ILogicalScrollable.CanVerticallyScroll + { + get => _canVerticallyScroll; + set => _canVerticallyScroll = value; + } + + bool IScrollable.CanHorizontallyScroll => _canHorizontallyScroll; + bool IScrollable.CanVerticallyScroll => _canVerticallyScroll; bool ILogicalScrollable.IsLogicalScrollEnabled => true; Size ILogicalScrollable.ScrollSize => new(1, 1); Size ILogicalScrollable.PageScrollSize => new(1, 1);