Browse Source

Merge branch 'master' into expander-events

pull/9555/head
robloo 3 years ago
committed by GitHub
parent
commit
dd60ea421b
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      samples/ControlCatalog/ViewModels/TransitioningContentControlPageViewModel.cs
  2. 2
      src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
  3. 2
      src/Avalonia.Base/Animation/PageSlide.cs
  4. 2
      src/Avalonia.Base/AttachedProperty.cs
  5. 2
      src/Avalonia.Base/AvaloniaObject.cs
  6. 100
      src/Avalonia.Base/AvaloniaObjectExtensions.cs
  7. 22
      src/Avalonia.Base/AvaloniaProperty.cs
  8. 6
      src/Avalonia.Base/AvaloniaPropertyChangedEventArgs.cs
  9. 4
      src/Avalonia.Base/AvaloniaPropertyChangedEventArgs`1.cs
  10. 12
      src/Avalonia.Base/AvaloniaPropertyRegistry.cs
  11. 2
      src/Avalonia.Base/AvaloniaProperty`1.cs
  12. 4
      src/Avalonia.Base/ClassBindingManager.cs
  13. 2
      src/Avalonia.Base/Controls/Classes.cs
  14. 2
      src/Avalonia.Base/Controls/ISetInheritanceParent.cs
  15. 3
      src/Avalonia.Base/Controls/ResourceNodeExtensions.cs
  16. 4
      src/Avalonia.Base/Data/BindingOperations.cs
  17. 4
      src/Avalonia.Base/Data/Core/AvaloniaPropertyAccessorNode.cs
  18. 2
      src/Avalonia.Base/Data/IBinding.cs
  19. 6
      src/Avalonia.Base/Data/IndexerBinding.cs
  20. 2
      src/Avalonia.Base/Data/InstancedBinding.cs
  21. 2
      src/Avalonia.Base/Diagnostics/IAvaloniaObjectDebug.cs
  22. 10
      src/Avalonia.Base/DirectProperty.cs
  23. 6
      src/Avalonia.Base/DirectPropertyBase.cs
  24. 79
      src/Avalonia.Base/IAvaloniaObject.cs
  25. 2
      src/Avalonia.Base/IDataContextProvider.cs
  26. 4
      src/Avalonia.Base/IDirectPropertyAccessor.cs
  27. 39
      src/Avalonia.Base/IStyledElement.cs
  28. 4
      src/Avalonia.Base/Input/AccessKeyHandler.cs
  29. 5
      src/Avalonia.Base/Input/DragDropDevice.cs
  30. 2
      src/Avalonia.Base/Input/DragEventArgs.cs
  31. 17
      src/Avalonia.Base/Input/FocusManager.cs
  32. 4
      src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizerCollection.cs
  33. 4
      src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs
  34. 33
      src/Avalonia.Base/Input/Gestures.cs
  35. 39
      src/Avalonia.Base/Input/IInputElement.cs
  36. 2
      src/Avalonia.Base/Input/IMainMenu.cs
  37. 17
      src/Avalonia.Base/Input/InputExtensions.cs
  38. 43
      src/Avalonia.Base/Input/KeyboardDevice.cs
  39. 2
      src/Avalonia.Base/Input/KeyboardNavigation.cs
  40. 6
      src/Avalonia.Base/Input/KeyboardNavigationHandler.cs
  41. 14
      src/Avalonia.Base/Input/MouseDevice.cs
  42. 12
      src/Avalonia.Base/Input/Navigation/FocusExtensions.cs
  43. 18
      src/Avalonia.Base/Input/Navigation/TabNavigation.cs
  44. 6
      src/Avalonia.Base/Input/PenDevice.cs
  45. 20
      src/Avalonia.Base/Input/Pointer.cs
  46. 4
      src/Avalonia.Base/Input/PointerDeltaEventArgs.cs
  47. 30
      src/Avalonia.Base/Input/PointerEventArgs.cs
  48. 48
      src/Avalonia.Base/Input/PointerOverPreProcessor.cs
  49. 2
      src/Avalonia.Base/Input/PointerWheelEventArgs.cs
  50. 2
      src/Avalonia.Base/Input/TappedEventArgs.cs
  51. 2
      src/Avalonia.Base/Input/TextInput/ITextInputMethodClient.cs
  52. 9
      src/Avalonia.Base/Input/TextInput/InputMethodManager.cs
  53. 6
      src/Avalonia.Base/Input/TextInput/TransformTrackingHelper.cs
  54. 6
      src/Avalonia.Base/Input/TouchDevice.cs
  55. 12
      src/Avalonia.Base/Interactivity/EventRoute.cs
  56. 53
      src/Avalonia.Base/Interactivity/IInteractive.cs
  57. 40
      src/Avalonia.Base/Interactivity/Interactive.cs
  58. 8
      src/Avalonia.Base/Interactivity/InteractiveExtensions.cs
  59. 2
      src/Avalonia.Base/Interactivity/RoutedEvent.cs
  60. 4
      src/Avalonia.Base/Interactivity/RoutedEventArgs.cs
  61. 2
      src/Avalonia.Base/Layout/AttachedLayout.cs
  62. 14
      src/Avalonia.Base/Layout/ElementManager.cs
  63. 10
      src/Avalonia.Base/Layout/FlowLayoutAlgorithm.cs
  64. 6
      src/Avalonia.Base/Layout/IFlowLayoutAlgorithmDelegates.cs
  65. 8
      src/Avalonia.Base/Layout/ILayoutManager.cs
  66. 2
      src/Avalonia.Base/Layout/ILayoutRoot.cs
  67. 122
      src/Avalonia.Base/Layout/ILayoutable.cs
  68. 4
      src/Avalonia.Base/Layout/LayoutContextAdapter.cs
  69. 43
      src/Avalonia.Base/Layout/LayoutHelper.cs
  70. 38
      src/Avalonia.Base/Layout/LayoutManager.cs
  71. 35
      src/Avalonia.Base/Layout/Layoutable.cs
  72. 4
      src/Avalonia.Base/Layout/NonVirtualizingLayoutContext.cs
  73. 8
      src/Avalonia.Base/Layout/NonVirtualizingStackLayout.cs
  74. 12
      src/Avalonia.Base/Layout/StackLayout.cs
  75. 6
      src/Avalonia.Base/Layout/UniformGridLayout.cs
  76. 4
      src/Avalonia.Base/Layout/UniformGridLayoutState.cs
  77. 8
      src/Avalonia.Base/Layout/VirtualLayoutContextAdapter.cs
  78. 12
      src/Avalonia.Base/Layout/VirtualizingLayoutContext.cs
  79. 2
      src/Avalonia.Base/Layout/WrapLayout/WrapItem.cs
  80. 4
      src/Avalonia.Base/Media/IVisualBrush.cs
  81. 2
      src/Avalonia.Base/Media/Imaging/RenderTargetBitmap.cs
  82. 4
      src/Avalonia.Base/Media/Immutable/ImmutableVisualBrush.cs
  83. 10
      src/Avalonia.Base/Media/VisualBrush.cs
  84. 2
      src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs
  85. 12
      src/Avalonia.Base/PropertyStore/UntypedValueUtils.cs
  86. 6
      src/Avalonia.Base/Reactive/AvaloniaPropertyBindingObservable.cs
  87. 6
      src/Avalonia.Base/Reactive/AvaloniaPropertyChangedObservable.cs
  88. 36
      src/Avalonia.Base/Reactive/AvaloniaPropertyObservable.cs
  89. 14
      src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs
  90. 22
      src/Avalonia.Base/Rendering/DeferredRenderer.cs
  91. 14
      src/Avalonia.Base/Rendering/DirtyVisuals.cs
  92. 4
      src/Avalonia.Base/Rendering/ICustomSimpleHitTest.cs
  93. 2
      src/Avalonia.Base/Rendering/IRenderRoot.cs
  94. 8
      src/Avalonia.Base/Rendering/IRenderer.cs
  95. 38
      src/Avalonia.Base/Rendering/ImmediateRenderer.cs
  96. 4
      src/Avalonia.Base/Rendering/RenderLayer.cs
  97. 7
      src/Avalonia.Base/Rendering/RenderLayers.cs
  98. 4
      src/Avalonia.Base/Rendering/SceneGraph/ISceneBuilder.cs
  99. 5
      src/Avalonia.Base/Rendering/SceneGraph/IVisualNode.cs
  100. 40
      src/Avalonia.Base/Rendering/SceneGraph/Scene.cs

6
samples/ControlCatalog/ViewModels/TransitioningContentControlPageViewModel.cs

@ -293,10 +293,10 @@ namespace ControlCatalog.ViewModels
/// <remarks>
/// Any one of the parameters may be null, but not both.
/// </remarks>
private static IVisual GetVisualParent(IVisual? from, IVisual? to)
private static Visual GetVisualParent(Visual? from, Visual? to)
{
var p1 = (from ?? to)!.VisualParent;
var p2 = (to ?? from)!.VisualParent;
var p1 = (from ?? to)!.GetVisualParent();
var p2 = (to ?? from)!.GetVisualParent();
if (p1 != null && p2 != null && p1 != p2)
{

2
src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs

@ -109,7 +109,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
? new CompositingRenderer(root, AndroidPlatform.Compositor)
: AndroidPlatform.Options.UseDeferredRendering
? new DeferredRenderer(root, AvaloniaLocator.Current.GetRequiredService<IRenderLoop>()) { RenderOnlyOnRenderThread = true }
: new ImmediateRenderer(root);
: new ImmediateRenderer((Visual)root);
public virtual void Hide()
{

2
src/Avalonia.Base/Animation/PageSlide.cs

@ -157,7 +157,7 @@ namespace Avalonia.Animation
/// <remarks>
/// Any one of the parameters may be null, but not both.
/// </remarks>
protected static IVisual GetVisualParent(IVisual? from, IVisual? to)
protected static Visual GetVisualParent(Visual? from, Visual? to)
{
var p1 = (from ?? to)!.VisualParent;
var p2 = (to ?? from)!.VisualParent;

2
src/Avalonia.Base/AttachedProperty.cs

@ -34,7 +34,7 @@ namespace Avalonia
/// </summary>
/// <typeparam name="TOwner">The owner type.</typeparam>
/// <returns>The property.</returns>
public new AttachedProperty<TValue> AddOwner<TOwner>() where TOwner : IAvaloniaObject
public new AttachedProperty<TValue> AddOwner<TOwner>() where TOwner : AvaloniaObject
{
AvaloniaPropertyRegistry.Instance.Register(typeof(TOwner), this);
return this;

2
src/Avalonia.Base/AvaloniaObject.cs

@ -16,7 +16,7 @@ namespace Avalonia
/// <remarks>
/// This class is analogous to DependencyObject in WPF.
/// </remarks>
public class AvaloniaObject : IAvaloniaObject, IAvaloniaObjectDebug, INotifyPropertyChanged
public class AvaloniaObject : IAvaloniaObjectDebug, INotifyPropertyChanged
{
private readonly ValueStore _values;
private AvaloniaObject? _inheritanceParent;

100
src/Avalonia.Base/AvaloniaObjectExtensions.cs

@ -36,7 +36,7 @@ namespace Avalonia
/// <remarks>
/// The subscription to <paramref name="o"/> is created using a weak reference.
/// </remarks>
public static IObservable<object?> GetObservable(this IAvaloniaObject o, AvaloniaProperty property)
public static IObservable<object?> GetObservable(this AvaloniaObject o, AvaloniaProperty property)
{
return new AvaloniaPropertyObservable<object?>(
o ?? throw new ArgumentNullException(nameof(o)),
@ -56,7 +56,7 @@ namespace Avalonia
/// <remarks>
/// The subscription to <paramref name="o"/> is created using a weak reference.
/// </remarks>
public static IObservable<T> GetObservable<T>(this IAvaloniaObject o, AvaloniaProperty<T> property)
public static IObservable<T> GetObservable<T>(this AvaloniaObject o, AvaloniaProperty<T> property)
{
return new AvaloniaPropertyObservable<T>(
o ?? throw new ArgumentNullException(nameof(o)),
@ -76,7 +76,7 @@ namespace Avalonia
/// The subscription to <paramref name="o"/> is created using a weak reference.
/// </remarks>
public static IObservable<BindingValue<object?>> GetBindingObservable(
this IAvaloniaObject o,
this AvaloniaObject o,
AvaloniaProperty property)
{
return new AvaloniaPropertyBindingObservable<object?>(
@ -98,7 +98,7 @@ namespace Avalonia
/// The subscription to <paramref name="o"/> is created using a weak reference.
/// </remarks>
public static IObservable<BindingValue<T>> GetBindingObservable<T>(
this IAvaloniaObject o,
this AvaloniaObject o,
AvaloniaProperty<T> property)
{
return new AvaloniaPropertyBindingObservable<T>(
@ -115,11 +115,11 @@ namespace Avalonia
/// <param name="property">The property.</param>
/// <returns>
/// An observable which when subscribed pushes the property changed event args
/// each time a <see cref="IAvaloniaObject.PropertyChanged"/> event is raised
/// each time a <see cref="AvaloniaObject.PropertyChanged"/> event is raised
/// for the specified property.
/// </returns>
public static IObservable<AvaloniaPropertyChangedEventArgs> GetPropertyChangedObservable(
this IAvaloniaObject o,
this AvaloniaObject o,
AvaloniaProperty property)
{
return new AvaloniaPropertyChangedObservable(
@ -140,7 +140,7 @@ namespace Avalonia
/// property.
/// </returns>
public static ISubject<object?> GetSubject(
this IAvaloniaObject o,
this AvaloniaObject o,
AvaloniaProperty property,
BindingPriority priority = BindingPriority.LocalValue)
{
@ -163,7 +163,7 @@ namespace Avalonia
/// property.
/// </returns>
public static ISubject<T> GetSubject<T>(
this IAvaloniaObject o,
this AvaloniaObject o,
AvaloniaProperty<T> property,
BindingPriority priority = BindingPriority.LocalValue)
{
@ -185,7 +185,7 @@ namespace Avalonia
/// property.
/// </returns>
public static ISubject<BindingValue<object?>> GetBindingSubject(
this IAvaloniaObject o,
this AvaloniaObject o,
AvaloniaProperty property,
BindingPriority priority = BindingPriority.LocalValue)
{
@ -214,7 +214,7 @@ namespace Avalonia
/// property.
/// </returns>
public static ISubject<BindingValue<T>> GetBindingSubject<T>(
this IAvaloniaObject o,
this AvaloniaObject o,
AvaloniaProperty<T> property,
BindingPriority priority = BindingPriority.LocalValue)
{
@ -241,7 +241,7 @@ namespace Avalonia
/// A disposable which can be used to terminate the binding.
/// </returns>
public static IDisposable Bind<T>(
this IAvaloniaObject target,
this AvaloniaObject target,
AvaloniaProperty<T> property,
IObservable<BindingValue<T>> source,
BindingPriority priority = BindingPriority.LocalValue)
@ -250,17 +250,12 @@ namespace Avalonia
property = property ?? throw new ArgumentNullException(nameof(property));
source = source ?? throw new ArgumentNullException(nameof(source));
if (target is AvaloniaObject ao)
return property switch
{
return property switch
{
StyledPropertyBase<T> styled => ao.Bind(styled, source, priority),
DirectPropertyBase<T> direct => ao.Bind(direct, source),
_ => throw new NotSupportedException("Unsupported AvaloniaProperty type."),
};
}
throw new NotSupportedException("Custom implementations of IAvaloniaObject not supported.");
StyledPropertyBase<T> styled => target.Bind(styled, source, priority),
DirectPropertyBase<T> direct => target.Bind(direct, source),
_ => throw new NotSupportedException("Unsupported AvaloniaProperty type."),
};
}
/// <summary>
@ -274,22 +269,17 @@ namespace Avalonia
/// A disposable which can be used to terminate the binding.
/// </returns>
public static IDisposable Bind<T>(
this IAvaloniaObject target,
this AvaloniaObject target,
AvaloniaProperty<T> property,
IObservable<T> source,
BindingPriority priority = BindingPriority.LocalValue)
{
if (target is AvaloniaObject ao)
return property switch
{
return property switch
{
StyledPropertyBase<T> styled => ao.Bind(styled, source, priority),
DirectPropertyBase<T> direct => ao.Bind(direct, source),
_ => throw new NotSupportedException("Unsupported AvaloniaProperty type."),
};
}
throw new NotSupportedException("Custom implementations of IAvaloniaObject not supported.");
StyledPropertyBase<T> styled => target.Bind(styled, source, priority),
DirectPropertyBase<T> direct => target.Bind(direct, source),
_ => throw new NotSupportedException("Unsupported AvaloniaProperty type."),
};
}
/// <summary>
@ -306,7 +296,7 @@ namespace Avalonia
/// </param>
/// <returns>An <see cref="IDisposable"/> which can be used to cancel the binding.</returns>
public static IDisposable Bind(
this IAvaloniaObject target,
this AvaloniaObject target,
AvaloniaProperty property,
IBinding binding,
object? anchor = null)
@ -340,23 +330,17 @@ namespace Avalonia
/// <param name="target">The object.</param>
/// <param name="property">The property.</param>
/// <returns>The value.</returns>
public static T GetValue<T>(this IAvaloniaObject target, AvaloniaProperty<T> property)
public static T GetValue<T>(this AvaloniaObject target, AvaloniaProperty<T> property)
{
target = target ?? throw new ArgumentNullException(nameof(target));
property = property ?? throw new ArgumentNullException(nameof(property));
if (target is AvaloniaObject ao)
return property switch
{
return property switch
{
StyledPropertyBase<T> styled => ao.GetValue(styled),
DirectPropertyBase<T> direct => ao.GetValue(direct),
_ => throw new NotSupportedException("Unsupported AvaloniaProperty type.")
};
}
throw new NotSupportedException("Custom implementations of IAvaloniaObject not supported.");
StyledPropertyBase<T> styled => target.GetValue(styled),
DirectPropertyBase<T> direct => target.GetValue(direct),
_ => throw new NotSupportedException("Unsupported AvaloniaProperty type.")
};
}
/// <summary>
@ -372,15 +356,13 @@ namespace Avalonia
/// For direct properties returns the current value of the property.
/// </remarks>
public static object? GetBaseValue(
this IAvaloniaObject target,
this AvaloniaObject target,
AvaloniaProperty property)
{
target = target ?? throw new ArgumentNullException(nameof(target));
property = property ?? throw new ArgumentNullException(nameof(property));
if (target is AvaloniaObject ao)
return property.RouteGetBaseValue(ao);
throw new NotSupportedException("Custom implementations of IAvaloniaObject not supported.");
return property.RouteGetBaseValue(target);
}
/// <summary>
@ -396,24 +378,18 @@ namespace Avalonia
/// For direct properties returns the current value of the property.
/// </remarks>
public static Optional<T> GetBaseValue<T>(
this IAvaloniaObject target,
this AvaloniaObject target,
AvaloniaProperty<T> property)
{
target = target ?? throw new ArgumentNullException(nameof(target));
property = property ?? throw new ArgumentNullException(nameof(property));
if (target is AvaloniaObject ao)
return property switch
{
return property switch
{
StyledPropertyBase<T> styled => ao.GetBaseValue(styled),
DirectPropertyBase<T> direct => ao.GetValue(direct),
_ => throw new NotSupportedException("Unsupported AvaloniaProperty type.")
};
}
throw new NotSupportedException("Custom implementations of IAvaloniaObject not supported.");
StyledPropertyBase<T> styled => target.GetBaseValue(styled),
DirectPropertyBase<T> direct => target.GetValue(direct),
_ => throw new NotSupportedException("Unsupported AvaloniaProperty type.")
};
}
/// <summary>
@ -474,7 +450,7 @@ namespace Avalonia
}
public InstancedBinding? Initiate(
IAvaloniaObject target,
AvaloniaObject target,
AvaloniaProperty? targetProperty,
object? anchor = null,
bool enableDataValidation = false)

22
src/Avalonia.Base/AvaloniaProperty.cs

@ -38,7 +38,7 @@ namespace Avalonia
Type valueType,
Type ownerType,
AvaloniaPropertyMetadata metadata,
Action<IAvaloniaObject, bool>? notifying = null)
Action<AvaloniaObject, bool>? notifying = null)
{
_ = name ?? throw new ArgumentNullException(nameof(name));
@ -145,7 +145,7 @@ namespace Avalonia
/// will be true before the property change notifications are sent and false afterwards. This
/// callback is intended to support Control.IsDataContextChanging.
/// </remarks>
public Action<IAvaloniaObject, bool>? Notifying { get; }
public Action<AvaloniaObject, bool>? Notifying { get; }
/// <summary>
/// Gets the integer ID that represents this property.
@ -238,9 +238,9 @@ namespace Avalonia
bool inherits = false,
BindingMode defaultBindingMode = BindingMode.OneWay,
Func<TValue, bool>? validate = null,
Func<IAvaloniaObject, TValue, TValue>? coerce = null,
Action<IAvaloniaObject, bool>? notifying = null)
where TOwner : IAvaloniaObject
Func<AvaloniaObject, TValue, TValue>? coerce = null,
Action<AvaloniaObject, bool>? notifying = null)
where TOwner : AvaloniaObject
{
_ = name ?? throw new ArgumentNullException(nameof(name));
@ -279,8 +279,8 @@ namespace Avalonia
bool inherits = false,
BindingMode defaultBindingMode = BindingMode.OneWay,
Func<TValue, bool>? validate = null,
Func<IAvaloniaObject, TValue, TValue>? coerce = null)
where THost : IAvaloniaObject
Func<AvaloniaObject, TValue, TValue>? coerce = null)
where THost : AvaloniaObject
{
_ = name ?? throw new ArgumentNullException(nameof(name));
@ -316,8 +316,8 @@ namespace Avalonia
bool inherits = false,
BindingMode defaultBindingMode = BindingMode.OneWay,
Func<TValue, bool>? validate = null,
Func<IAvaloniaObject, TValue, TValue>? coerce = null)
where THost : IAvaloniaObject
Func<AvaloniaObject, TValue, TValue>? coerce = null)
where THost : AvaloniaObject
{
_ = name ?? throw new ArgumentNullException(nameof(name));
@ -354,7 +354,7 @@ namespace Avalonia
TValue unsetValue = default!,
BindingMode defaultBindingMode = BindingMode.OneWay,
bool enableDataValidation = false)
where TOwner : IAvaloniaObject
where TOwner : AvaloniaObject
{
_ = name ?? throw new ArgumentNullException(nameof(name));
_ = getter ?? throw new ArgumentNullException(nameof(getter));
@ -415,7 +415,7 @@ namespace Avalonia
/// <returns>
/// The property metadata.
/// </returns>
public AvaloniaPropertyMetadata GetMetadata<T>() where T : IAvaloniaObject
public AvaloniaPropertyMetadata GetMetadata<T>() where T : AvaloniaObject
{
return GetMetadata(typeof(T));
}

6
src/Avalonia.Base/AvaloniaPropertyChangedEventArgs.cs

@ -9,7 +9,7 @@ namespace Avalonia
public abstract class AvaloniaPropertyChangedEventArgs : EventArgs
{
public AvaloniaPropertyChangedEventArgs(
IAvaloniaObject sender,
AvaloniaObject sender,
BindingPriority priority)
{
Sender = sender;
@ -18,7 +18,7 @@ namespace Avalonia
}
internal AvaloniaPropertyChangedEventArgs(
IAvaloniaObject sender,
AvaloniaObject sender,
BindingPriority priority,
bool isEffectiveValueChange)
{
@ -31,7 +31,7 @@ namespace Avalonia
/// Gets the <see cref="AvaloniaObject"/> that the property changed on.
/// </summary>
/// <value>The sender object.</value>
public IAvaloniaObject Sender { get; }
public AvaloniaObject Sender { get; }
/// <summary>
/// Gets the property that changed.

4
src/Avalonia.Base/AvaloniaPropertyChangedEventArgs`1.cs

@ -16,7 +16,7 @@ namespace Avalonia
/// <param name="newValue">The new value of the property.</param>
/// <param name="priority">The priority of the binding that produced the value.</param>
public AvaloniaPropertyChangedEventArgs(
IAvaloniaObject sender,
AvaloniaObject sender,
AvaloniaProperty<T> property,
Optional<T> oldValue,
BindingValue<T> newValue,
@ -26,7 +26,7 @@ namespace Avalonia
}
internal AvaloniaPropertyChangedEventArgs(
IAvaloniaObject sender,
AvaloniaObject sender,
AvaloniaProperty<T> property,
Optional<T> oldValue,
BindingValue<T> newValue,

12
src/Avalonia.Base/AvaloniaPropertyRegistry.cs

@ -189,7 +189,7 @@ namespace Avalonia
/// </summary>
/// <param name="o">The object.</param>
/// <returns>A collection of <see cref="AvaloniaProperty"/> definitions.</returns>
public IReadOnlyList<AvaloniaProperty> GetRegistered(IAvaloniaObject o)
public IReadOnlyList<AvaloniaProperty> GetRegistered(AvaloniaObject o)
{
_ = o ?? throw new ArgumentNullException(nameof(o));
@ -205,7 +205,7 @@ namespace Avalonia
/// The registered.
/// </returns>
public DirectPropertyBase<T> GetRegisteredDirect<T>(
IAvaloniaObject o,
AvaloniaObject o,
DirectPropertyBase<T> property)
{
return FindRegisteredDirect(o, property) ??
@ -260,7 +260,7 @@ namespace Avalonia
/// <exception cref="InvalidOperationException">
/// The property name contains a '.'.
/// </exception>
public AvaloniaProperty? FindRegistered(IAvaloniaObject o, string name)
public AvaloniaProperty? FindRegistered(AvaloniaObject o, string name)
{
_ = o ?? throw new ArgumentNullException(nameof(o));
_ = name ?? throw new ArgumentNullException(nameof(name));
@ -277,7 +277,7 @@ namespace Avalonia
/// The registered property or null if no matching property found.
/// </returns>
public DirectPropertyBase<T>? FindRegisteredDirect<T>(
IAvaloniaObject o,
AvaloniaObject o,
DirectPropertyBase<T> property)
{
if (property.Owner == o.GetType())
@ -362,7 +362,7 @@ namespace Avalonia
/// <param name="property">The property.</param>
/// <remarks>
/// You won't usually want to call this method directly, instead use the
/// <see cref="AvaloniaProperty.Register{TOwner, TValue}(string, TValue, bool, Data.BindingMode, Func{TValue, bool}, Func{IAvaloniaObject, TValue, TValue}, Action{IAvaloniaObject, bool})"/>
/// <see cref="AvaloniaProperty.Register{TOwner, TValue}(string, TValue, bool, Data.BindingMode, Func{TValue, bool}, Func{AvaloniaObject, TValue, TValue}, Action{AvaloniaObject, bool})"/>
/// method.
/// </remarks>
public void Register(Type type, AvaloniaProperty property)
@ -413,7 +413,7 @@ namespace Avalonia
/// <param name="property">The property.</param>
/// <remarks>
/// You won't usually want to call this method directly, instead use the
/// <see cref="AvaloniaProperty.RegisterAttached{THost, TValue}(string, Type, TValue, bool, Data.BindingMode, Func{TValue, bool}, Func{IAvaloniaObject, TValue, TValue})"/>
/// <see cref="AvaloniaProperty.RegisterAttached{THost, TValue}(string, Type, TValue, bool, Data.BindingMode, Func{TValue, bool}, Func{AvaloniaObject, TValue, TValue})"/>
/// method.
/// </remarks>
public void RegisterAttached(Type type, AvaloniaProperty property)

2
src/Avalonia.Base/AvaloniaProperty`1.cs

@ -24,7 +24,7 @@ namespace Avalonia
string name,
Type ownerType,
AvaloniaPropertyMetadata metadata,
Action<IAvaloniaObject, bool>? notifying = null)
Action<AvaloniaObject, bool>? notifying = null)
: base(name, typeof(TValue), ownerType, metadata, notifying)
{
_changed = new Subject<AvaloniaPropertyChangedEventArgs<TValue>>();

4
src/Avalonia.Base/ClassBindingManager.cs

@ -9,7 +9,7 @@ namespace Avalonia
private static readonly Dictionary<string, AvaloniaProperty> s_RegisteredProperties =
new Dictionary<string, AvaloniaProperty>();
public static IDisposable Bind(IStyledElement target, string className, IBinding source, object anchor)
public static IDisposable Bind(StyledElement target, string className, IBinding source, object anchor)
{
if (!s_RegisteredProperties.TryGetValue(className, out var prop))
s_RegisteredProperties[className] = prop = RegisterClassProxyProperty(className);
@ -21,7 +21,7 @@ namespace Avalonia
var prop = AvaloniaProperty.Register<StyledElement, bool>("__AvaloniaReserved::Classes::" + className);
prop.Changed.Subscribe(args =>
{
var classes = ((IStyledElement)args.Sender).Classes;
var classes = ((StyledElement)args.Sender).Classes;
classes.Set(className, args.NewValue.GetValueOrDefault());
});

2
src/Avalonia.Base/Controls/Classes.cs

@ -6,7 +6,7 @@ using Avalonia.Utilities;
namespace Avalonia.Controls
{
/// <summary>
/// Holds a collection of style classes for an <see cref="IStyledElement"/>.
/// Holds a collection of style classes for an <see cref="StyledElement"/>.
/// </summary>
/// <remarks>
/// Similar to CSS, each control may have any number of styling classes applied.

2
src/Avalonia.Base/Controls/ISetInheritanceParent.cs

@ -17,6 +17,6 @@ namespace Avalonia.Controls
/// Sets the control's inheritance parent.
/// </summary>
/// <param name="parent">The parent.</param>
void SetParent(IAvaloniaObject? parent);
void SetParent(AvaloniaObject? parent);
}
}

3
src/Avalonia.Base/Controls/ResourceNodeExtensions.cs

@ -2,6 +2,7 @@
using Avalonia.Data.Converters;
using Avalonia.LogicalTree;
using Avalonia.Reactive;
using Avalonia.Styling;
#nullable enable
@ -49,7 +50,7 @@ namespace Avalonia.Controls
return true;
}
current = (current as IStyledElement)?.StylingParent as IResourceNode;
current = (current as IStyleHost)?.StylingParent as IResourceNode;
}
value = null;

4
src/Avalonia.Base/Data/BindingOperations.cs

@ -9,7 +9,7 @@ namespace Avalonia.Data
public static readonly object DoNothing = new DoNothingType();
/// <summary>
/// Applies an <see cref="InstancedBinding"/> a property on an <see cref="IAvaloniaObject"/>.
/// Applies an <see cref="InstancedBinding"/> a property on an <see cref="AvaloniaObject"/>.
/// </summary>
/// <param name="target">The target object.</param>
/// <param name="property">The property to bind.</param>
@ -22,7 +22,7 @@ namespace Avalonia.Data
/// </param>
/// <returns>An <see cref="IDisposable"/> which can be used to cancel the binding.</returns>
public static IDisposable Apply(
IAvaloniaObject target,
AvaloniaObject target,
AvaloniaProperty property,
InstancedBinding binding,
object? anchor)

4
src/Avalonia.Base/Data/Core/AvaloniaPropertyAccessorNode.cs

@ -24,7 +24,7 @@ namespace Avalonia.Data.Core
{
try
{
if (Target.TryGetTarget(out var target) && target is IAvaloniaObject obj)
if (Target.TryGetTarget(out var target) && target is AvaloniaObject obj)
{
obj.SetValue(_property, value, priority);
return true;
@ -39,7 +39,7 @@ namespace Avalonia.Data.Core
protected override void StartListeningCore(WeakReference<object?> reference)
{
if (reference.TryGetTarget(out var target) && target is IAvaloniaObject obj)
if (reference.TryGetTarget(out var target) && target is AvaloniaObject obj)
{
_subscription = new AvaloniaPropertyObservable<object?>(obj, _property).Subscribe(ValueChanged);
}

2
src/Avalonia.Base/Data/IBinding.cs

@ -24,7 +24,7 @@ namespace Avalonia.Data
/// A <see cref="InstancedBinding"/> or null if the binding could not be resolved.
/// </returns>
InstancedBinding? Initiate(
IAvaloniaObject target,
AvaloniaObject target,
AvaloniaProperty? targetProperty,
object? anchor = null,
bool enableDataValidation = false);

6
src/Avalonia.Base/Data/IndexerBinding.cs

@ -3,7 +3,7 @@
public class IndexerBinding : IBinding
{
public IndexerBinding(
IAvaloniaObject source,
AvaloniaObject source,
AvaloniaProperty property,
BindingMode mode)
{
@ -12,12 +12,12 @@
Mode = mode;
}
private IAvaloniaObject Source { get; }
private AvaloniaObject Source { get; }
public AvaloniaProperty Property { get; }
private BindingMode Mode { get; }
public InstancedBinding? Initiate(
IAvaloniaObject target,
AvaloniaObject target,
AvaloniaProperty? targetProperty,
object? anchor = null,
bool enableDataValidation = false)

2
src/Avalonia.Base/Data/InstancedBinding.cs

@ -9,7 +9,7 @@ namespace Avalonia.Data
/// <remarks>
/// Whereas an <see cref="IBinding"/> holds a description of a binding such as "Bind to the X
/// property on a control's DataContext"; this class represents a binding that has been
/// *instanced* by calling <see cref="IBinding.Initiate(IAvaloniaObject, AvaloniaProperty, object, bool)"/>
/// *instanced* by calling <see cref="IBinding.Initiate(AvaloniaObject, AvaloniaProperty, object, bool)"/>
/// on a target object.
/// </remarks>
public class InstancedBinding

2
src/Avalonia.Base/Diagnostics/IAvaloniaObjectDebug.cs

@ -8,7 +8,7 @@ namespace Avalonia.Diagnostics
public interface IAvaloniaObjectDebug
{
/// <summary>
/// Gets the subscriber list for the <see cref="IAvaloniaObject.PropertyChanged"/>
/// Gets the subscriber list for the <see cref="AvaloniaObject.PropertyChanged"/>
/// event.
/// </summary>
/// <returns>

10
src/Avalonia.Base/DirectProperty.cs

@ -15,7 +15,7 @@ namespace Avalonia
/// allows the avalonia property system to read and write the current value.
/// </remarks>
public class DirectProperty<TOwner, TValue> : DirectPropertyBase<TValue>, IDirectPropertyAccessor
where TOwner : IAvaloniaObject
where TOwner : AvaloniaObject
{
/// <summary>
/// Initializes a new instance of the <see cref="DirectProperty{TOwner, TValue}"/> class.
@ -151,13 +151,13 @@ namespace Avalonia
}
/// <inheritdoc/>
internal override TValue InvokeGetter(IAvaloniaObject instance)
internal override TValue InvokeGetter(AvaloniaObject instance)
{
return Getter((TOwner)instance);
}
/// <inheritdoc/>
internal override void InvokeSetter(IAvaloniaObject instance, BindingValue<TValue> value)
internal override void InvokeSetter(AvaloniaObject instance, BindingValue<TValue> value)
{
if (Setter == null)
{
@ -171,13 +171,13 @@ namespace Avalonia
}
/// <inheritdoc/>
object? IDirectPropertyAccessor.GetValue(IAvaloniaObject instance)
object? IDirectPropertyAccessor.GetValue(AvaloniaObject instance)
{
return Getter((TOwner)instance);
}
/// <inheritdoc/>
void IDirectPropertyAccessor.SetValue(IAvaloniaObject instance, object? value)
void IDirectPropertyAccessor.SetValue(AvaloniaObject instance, object? value)
{
if (Setter == null)
{

6
src/Avalonia.Base/DirectPropertyBase.cs

@ -54,14 +54,14 @@ namespace Avalonia
/// </summary>
/// <param name="instance">The instance.</param>
/// <returns>The property value.</returns>
internal abstract TValue InvokeGetter(IAvaloniaObject instance);
internal abstract TValue InvokeGetter(AvaloniaObject instance);
/// <summary>
/// Sets the value of the property on the instance.
/// </summary>
/// <param name="instance">The instance.</param>
/// <param name="value">The value.</param>
internal abstract void InvokeSetter(IAvaloniaObject instance, BindingValue<TValue> value);
internal abstract void InvokeSetter(AvaloniaObject instance, BindingValue<TValue> value);
/// <summary>
/// Gets the unset value for the property on the specified type.
@ -91,7 +91,7 @@ namespace Avalonia
/// </summary>
/// <typeparam name="T">The type.</typeparam>
/// <param name="metadata">The metadata.</param>
public void OverrideMetadata<T>(DirectPropertyMetadata<TValue> metadata) where T : IAvaloniaObject
public void OverrideMetadata<T>(DirectPropertyMetadata<TValue> metadata) where T : AvaloniaObject
{
base.OverrideMetadata(typeof(T), metadata);
}

79
src/Avalonia.Base/IAvaloniaObject.cs

@ -1,79 +0,0 @@
using System;
using Avalonia.Data;
using Avalonia.Metadata;
namespace Avalonia
{
/// <summary>
/// Interface for getting/setting <see cref="AvaloniaProperty"/> values on an object.
/// </summary>
[NotClientImplementable]
public interface IAvaloniaObject
{
/// <summary>
/// Raised when a <see cref="AvaloniaProperty"/> value changes on this object.
/// </summary>
event EventHandler<AvaloniaPropertyChangedEventArgs>? PropertyChanged;
/// <summary>
/// Clears an <see cref="AvaloniaProperty"/>'s local value.
/// </summary>
/// <param name="property">The property.</param>
void ClearValue(AvaloniaProperty property);
/// <summary>
/// Gets a <see cref="AvaloniaProperty"/> value.
/// </summary>
/// <param name="property">The property.</param>
/// <returns>The value.</returns>
object? GetValue(AvaloniaProperty property);
/// <summary>
/// Checks whether a <see cref="AvaloniaProperty"/> is animating.
/// </summary>
/// <param name="property">The property.</param>
/// <returns>True if the property is animating, otherwise false.</returns>
bool IsAnimating(AvaloniaProperty property);
/// <summary>
/// Checks whether a <see cref="AvaloniaProperty"/> is set on this object.
/// </summary>
/// <param name="property">The property.</param>
/// <returns>True if the property is set, otherwise false.</returns>
bool IsSet(AvaloniaProperty property);
/// <summary>
/// Sets a <see cref="AvaloniaProperty"/> value.
/// </summary>
/// <param name="property">The property.</param>
/// <param name="value">The value.</param>
/// <param name="priority">The priority of the value.</param>
/// <returns>
/// An <see cref="IDisposable"/> if setting the property can be undone, otherwise null.
/// </returns>
IDisposable? SetValue(
AvaloniaProperty property,
object? value,
BindingPriority priority = BindingPriority.LocalValue);
/// <summary>
/// Binds a <see cref="AvaloniaProperty"/> to an observable.
/// </summary>
/// <param name="property">The property.</param>
/// <param name="source">The observable.</param>
/// <param name="priority">The priority of the binding.</param>
/// <returns>
/// A disposable which can be used to terminate the binding.
/// </returns>
IDisposable Bind(
AvaloniaProperty property,
IObservable<object?> source,
BindingPriority priority = BindingPriority.LocalValue);
/// <summary>
/// Coerces the specified <see cref="AvaloniaProperty"/>.
/// </summary>
/// <param name="property">The property.</param>
void CoerceValue(AvaloniaProperty property);
}
}

2
src/Avalonia.Base/IDataContextProvider.cs

@ -6,7 +6,7 @@ namespace Avalonia
/// Defines an element with a data context that can be used for binding.
/// </summary>
[NotClientImplementable]
public interface IDataContextProvider : IAvaloniaObject
public interface IDataContextProvider
{
/// <summary>
/// Gets or sets the element's data context.

4
src/Avalonia.Base/IDirectPropertyAccessor.cs

@ -24,13 +24,13 @@ namespace Avalonia
/// </summary>
/// <param name="instance">The instance.</param>
/// <returns>The property value.</returns>
object? GetValue(IAvaloniaObject instance);
object? GetValue(AvaloniaObject instance);
/// <summary>
/// Sets the value of the property on the instance.
/// </summary>
/// <param name="instance">The instance.</param>
/// <param name="value">The value.</param>
void SetValue(IAvaloniaObject instance, object? value);
void SetValue(AvaloniaObject instance, object? value);
}
}

39
src/Avalonia.Base/IStyledElement.cs

@ -1,39 +0,0 @@
using System;
using System.ComponentModel;
using Avalonia.Controls;
using Avalonia.LogicalTree;
using Avalonia.Metadata;
using Avalonia.Styling;
namespace Avalonia
{
[NotClientImplementable]
public interface IStyledElement :
IStyleable,
IStyleHost,
ILogical,
IResourceHost,
IDataContextProvider,
ISupportInitialize
{
/// <summary>
/// Occurs when the control has finished initialization.
/// </summary>
event EventHandler? Initialized;
/// <summary>
/// Gets a value that indicates whether the element has finished initialization.
/// </summary>
bool IsInitialized { get; }
/// <summary>
/// Gets or sets the control's styling classes.
/// </summary>
new Classes Classes { get; set; }
/// <summary>
/// Gets the control's logical parent.
/// </summary>
IStyledElement? Parent { get; }
}
}

4
src/Avalonia.Base/Input/AccessKeyHandler.cs

@ -182,13 +182,13 @@ namespace Avalonia.Input
// find all controls who have registered that access key.
var text = e.Key.ToString().ToUpper();
var matches = _registered
.Where(x => x.Item1 == text && x.Item2.IsEffectivelyVisible)
.Where(x => x.Item1 == text && ((Visual)x.Item2).IsEffectivelyVisible)
.Select(x => x.Item2);
// If the menu is open, only match controls in the menu's visual tree.
if (menuIsOpen)
{
matches = matches.Where(x => x is not null && MainMenu!.IsVisualAncestorOf(x));
matches = matches.Where(x => x is not null && ((Visual)MainMenu!).IsVisualAncestorOf((Visual)x));
}
var match = matches.FirstOrDefault();

5
src/Avalonia.Base/Input/DragDropDevice.cs

@ -13,7 +13,8 @@ namespace Avalonia.Input
private Interactive? GetTarget(IInputRoot root, Point local)
{
var target = root.InputHitTest(local)?.GetSelfAndVisualAncestors()?.OfType<Interactive>()?.FirstOrDefault();
var hit = root.InputHitTest(local) as Visual;
var target = hit?.GetSelfAndVisualAncestors()?.OfType<Interactive>()?.FirstOrDefault();
if (target != null && DragDrop.GetAllowDrop(target))
return target;
return null;
@ -24,7 +25,7 @@ namespace Avalonia.Input
if (target == null)
return DragDropEffects.None;
var p = inputRoot.TranslatePoint(point, target);
var p = ((Visual)inputRoot).TranslatePoint(point, target);
if (!p.HasValue)
return DragDropEffects.None;

2
src/Avalonia.Base/Input/DragEventArgs.cs

@ -15,7 +15,7 @@ namespace Avalonia.Input
public KeyModifiers KeyModifiers { get; private set; }
public Point GetPosition(IVisual relativeTo)
public Point GetPosition(Visual relativeTo)
{
var point = new Point(0, 0);

17
src/Avalonia.Base/Input/FocusManager.cs

@ -185,7 +185,7 @@ namespace Avalonia.Input
/// </summary>
/// <param name="e">The element.</param>
/// <returns>True if the element can be focused.</returns>
private static bool CanFocus(IInputElement e) => e.Focusable && e.IsEffectivelyEnabled && e.IsVisible;
private static bool CanFocus(IInputElement e) => e.Focusable && e.IsEffectivelyEnabled && IsVisible(e);
/// <summary>
/// Gets the focus scope ancestors of the specified control, traversing popups.
@ -198,14 +198,15 @@ namespace Avalonia.Input
while (c != null)
{
var scope = c as IFocusScope;
if (scope != null && c.VisualRoot?.IsVisible == true)
if (c is IFocusScope scope &&
c is Visual v &&
v.VisualRoot is Visual root &&
root.IsVisible)
{
yield return scope;
}
c = c.GetVisualParent<IInputElement>() ??
c = (c as Visual)?.GetVisualParent<IInputElement>() ??
((c as IHostedVisualTreeRoot)?.Host as IInputElement);
}
}
@ -221,11 +222,11 @@ namespace Avalonia.Input
return;
var ev = (PointerPressedEventArgs)e;
var visual = (IVisual)sender;
var visual = (Visual)sender;
if (sender == e.Source && ev.GetCurrentPoint(visual).Properties.IsLeftButtonPressed)
{
IVisual? element = ev.Pointer?.Captured ?? e.Source as IInputElement;
Visual? element = ev.Pointer?.Captured as Visual ?? e.Source as Visual;
while (element != null)
{
@ -240,5 +241,7 @@ namespace Avalonia.Input
}
}
}
private static bool IsVisible(IInputElement e) => (e as Visual)?.IsVisible ?? true;
}
}

4
src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizerCollection.cs

@ -35,8 +35,8 @@ namespace Avalonia.Input.GestureRecognizers
if (_inputElement is ILogical logicalParent && recognizer is ISetLogicalParent logical)
{
logical.SetParent(logicalParent);
if (recognizer is IStyleable styleableRecognizer
&& _inputElement is IStyleable styleableParent)
if (recognizer is StyledElement styleableRecognizer
&& _inputElement is StyledElement styleableParent)
styleableRecognizer.Bind(StyledElement.TemplatedParentProperty,
styleableParent.GetObservable(StyledElement.TemplatedParentProperty));
}

4
src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs

@ -72,7 +72,7 @@ namespace Avalonia.Input.GestureRecognizers
EndGesture();
_tracking = e.Pointer;
_gestureId = ScrollGestureEventArgs.GetNextFreeId();
_trackedRootPoint = e.GetPosition(_target);
_trackedRootPoint = e.GetPosition((Visual?)_target);
}
}
@ -86,7 +86,7 @@ namespace Avalonia.Input.GestureRecognizers
{
if (e.Pointer == _tracking)
{
var rootPoint = e.GetPosition(_target);
var rootPoint = e.GetPosition((Visual?)_target);
if (!_scrolling)
{
if (CanHorizontallyScroll && Math.Abs(_trackedRootPoint.X - rootPoint.X) > ScrollStartDistance)

33
src/Avalonia.Base/Input/Gestures.cs

@ -43,7 +43,7 @@ namespace Avalonia.Input
RoutedEvent.Register<PointerDeltaEventArgs>(
"PointerSwipeGesture", RoutingStrategies.Bubble, typeof(Gestures));
private static readonly WeakReference<IInteractive?> s_lastPress = new WeakReference<IInteractive?>(null);
private static readonly WeakReference<object?> s_lastPress = new WeakReference<object?>(null);
private static Point s_lastPressPoint;
static Gestures()
@ -52,32 +52,32 @@ namespace Avalonia.Input
InputElement.PointerReleasedEvent.RouteFinished.Subscribe(PointerReleased);
}
public static void AddTappedHandler(IInteractive element, EventHandler<RoutedEventArgs> handler)
public static void AddTappedHandler(Interactive element, EventHandler<RoutedEventArgs> handler)
{
element.AddHandler(TappedEvent, handler);
}
public static void AddDoubleTappedHandler(IInteractive element, EventHandler<RoutedEventArgs> handler)
public static void AddDoubleTappedHandler(Interactive element, EventHandler<RoutedEventArgs> handler)
{
element.AddHandler(DoubleTappedEvent, handler);
}
public static void AddRightTappedHandler(IInteractive element, EventHandler<RoutedEventArgs> handler)
public static void AddRightTappedHandler(Interactive element, EventHandler<RoutedEventArgs> handler)
{
element.AddHandler(RightTappedEvent, handler);
}
public static void RemoveTappedHandler(IInteractive element, EventHandler<RoutedEventArgs> handler)
public static void RemoveTappedHandler(Interactive element, EventHandler<RoutedEventArgs> handler)
{
element.RemoveHandler(TappedEvent, handler);
}
public static void RemoveDoubleTappedHandler(IInteractive element, EventHandler<RoutedEventArgs> handler)
public static void RemoveDoubleTappedHandler(Interactive element, EventHandler<RoutedEventArgs> handler)
{
element.RemoveHandler(DoubleTappedEvent, handler);
}
public static void RemoveRightTappedHandler(IInteractive element, EventHandler<RoutedEventArgs> handler)
public static void RemoveRightTappedHandler(Interactive element, EventHandler<RoutedEventArgs> handler)
{
element.RemoveHandler(RightTappedEvent, handler);
}
@ -92,20 +92,22 @@ namespace Avalonia.Input
if (ev.Route == RoutingStrategies.Bubble)
{
var e = (PointerPressedEventArgs)ev;
var visual = (IVisual)ev.Source;
var visual = (Visual)ev.Source;
if (e.ClickCount % 2 == 1)
{
s_isDoubleTapped = false;
s_lastPress.SetTarget(ev.Source);
s_lastPressPoint = e.GetPosition((IVisual)ev.Source);
s_lastPressPoint = e.GetPosition((Visual)ev.Source);
}
else if (e.ClickCount % 2 == 0 && e.GetCurrentPoint(visual).Properties.IsLeftButtonPressed)
{
if (s_lastPress.TryGetTarget(out var target) && target == e.Source)
if (s_lastPress.TryGetTarget(out var target) &&
target == e.Source &&
e.Source is Interactive i)
{
s_isDoubleTapped = true;
e.Source.RaiseEvent(new TappedEventArgs(DoubleTappedEvent, e));
i.RaiseEvent(new TappedEventArgs(DoubleTappedEvent, e));
}
}
}
@ -119,9 +121,10 @@ namespace Avalonia.Input
if (s_lastPress.TryGetTarget(out var target) &&
target == e.Source &&
e.InitialPressMouseButton is MouseButton.Left or MouseButton.Right)
e.InitialPressMouseButton is MouseButton.Left or MouseButton.Right &&
e.Source is Interactive i)
{
var point = e.GetCurrentPoint((IVisual)target);
var point = e.GetCurrentPoint((Visual)target);
var settings = AvaloniaLocator.Current.GetService<IPlatformSettings>();
var tapSize = settings?.GetTapSize(point.Pointer.Type) ?? new Size(4, 4);
var tapRect = new Rect(s_lastPressPoint, new Size())
@ -131,13 +134,13 @@ namespace Avalonia.Input
{
if (e.InitialPressMouseButton == MouseButton.Right)
{
e.Source.RaiseEvent(new TappedEventArgs(RightTappedEvent, e));
i.RaiseEvent(new TappedEventArgs(RightTappedEvent, e));
}
//s_isDoubleTapped needed here to prevent invoking Tapped event when DoubleTapped is called.
//This behaviour matches UWP behaviour.
else if (s_isDoubleTapped == false)
{
e.Source.RaiseEvent(new TappedEventArgs(TappedEvent, e));
i.RaiseEvent(new TappedEventArgs(TappedEvent, e));
}
}
}

39
src/Avalonia.Base/Input/IInputElement.cs

@ -2,9 +2,6 @@ using System;
using System.Collections.Generic;
using Avalonia.Interactivity;
using Avalonia.Metadata;
using Avalonia.VisualTree;
#nullable enable
namespace Avalonia.Input
{
@ -12,7 +9,7 @@ namespace Avalonia.Input
/// Defines input-related functionality for a control.
/// </summary>
[NotClientImplementable]
public interface IInputElement : IInteractive, IVisual
public interface IInputElement
{
/// <summary>
/// Occurs when the control receives focus.
@ -93,7 +90,12 @@ namespace Avalonia.Input
/// <see cref="IsEnabled"/> value of this control and its parent controls.
/// </remarks>
bool IsEffectivelyEnabled { get; }
/// <summary>
/// Gets a value indicating whether this control and all its parents are visible.
/// </summary>
bool IsEffectivelyVisible { get; }
/// <summary>
/// Gets a value indicating whether keyboard focus is anywhere within the element or its visual tree child elements.
/// </summary>
@ -123,5 +125,32 @@ namespace Avalonia.Input
/// Gets the key bindings for the element.
/// </summary>
List<KeyBinding> KeyBindings { get; }
/// <summary>
/// Adds a handler for the specified routed event.
/// </summary>
/// <param name="routedEvent">The routed event.</param>
/// <param name="handler">The handler.</param>
/// <param name="routes">The routing strategies to listen to.</param>
/// <param name="handledEventsToo">Whether handled events should also be listened for.</param>
/// <returns>A disposable that terminates the event subscription.</returns>
void AddHandler(
RoutedEvent routedEvent,
Delegate handler,
RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble,
bool handledEventsToo = false);
/// <summary>
/// Removes a handler for the specified routed event.
/// </summary>
/// <param name="routedEvent">The routed event.</param>
/// <param name="handler">The handler.</param>
void RemoveHandler(RoutedEvent routedEvent, Delegate handler);
/// <summary>
/// Raises a routed event.
/// </summary>
/// <param name="e">The event args.</param>
void RaiseEvent(RoutedEventArgs e);
}
}

2
src/Avalonia.Base/Input/IMainMenu.cs

@ -9,7 +9,7 @@ namespace Avalonia.Input
/// Defines the interface for a window's main menu.
/// </summary>
[NotClientImplementable]
public interface IMainMenu : IVisual
public interface IMainMenu
{
/// <summary>
/// Gets a value indicating whether the menu is open.

17
src/Avalonia.Base/Input/InputExtensions.cs

@ -12,7 +12,7 @@ namespace Avalonia.Input
/// </summary>
public static class InputExtensions
{
private static readonly Func<IVisual, bool> s_hitTestDelegate = IsHitTestVisible;
private static readonly Func<Visual, bool> s_hitTestDelegate = IsHitTestVisible;
/// <summary>
/// Returns the active input elements at a point on an <see cref="IInputElement"/>.
@ -26,7 +26,8 @@ namespace Avalonia.Input
{
element = element ?? throw new ArgumentNullException(nameof(element));
return element.GetVisualsAt(p, s_hitTestDelegate).Cast<IInputElement>();
return (element as Visual)?.GetVisualsAt(p, s_hitTestDelegate).Cast<IInputElement>() ??
Enumerable.Empty<IInputElement>();
}
/// <summary>
@ -39,7 +40,7 @@ namespace Avalonia.Input
{
element = element ?? throw new ArgumentNullException(nameof(element));
return element.GetVisualAt(p, s_hitTestDelegate) as IInputElement;
return (element as Visual)?.GetVisualAt(p, s_hitTestDelegate) as IInputElement;
}
/// <summary>
@ -55,22 +56,22 @@ namespace Avalonia.Input
public static IInputElement? InputHitTest(
this IInputElement element,
Point p,
Func<IVisual, bool> filter)
Func<Visual, bool> filter)
{
element = element ?? throw new ArgumentNullException(nameof(element));
filter = filter ?? throw new ArgumentNullException(nameof(filter));
return element.GetVisualAt(p, x => s_hitTestDelegate(x) && filter(x)) as IInputElement;
return (element as Visual)?.GetVisualAt(p, x => s_hitTestDelegate(x) && filter(x)) as IInputElement;
}
private static bool IsHitTestVisible(IVisual visual)
private static bool IsHitTestVisible(Visual visual)
{
var element = visual as IInputElement;
return element != null &&
element.IsVisible &&
visual.IsVisible &&
element.IsHitTestVisible &&
element.IsEffectivelyEnabled &&
element.IsAttachedToVisualTree;
visual.IsAttachedToVisualTree;
}
}
}

43
src/Avalonia.Base/Input/KeyboardDevice.cs

@ -3,7 +3,6 @@ using System.Runtime.CompilerServices;
using Avalonia.Input.Raw;
using Avalonia.Input.TextInput;
using Avalonia.Interactivity;
using Avalonia.VisualTree;
namespace Avalonia.Input
{
@ -37,18 +36,21 @@ namespace Avalonia.Input
ie.IsKeyboardFocusWithin = false;
}
el = (IInputElement?)el.VisualParent;
el = (IInputElement?)(el as Visual)?.VisualParent;
}
}
private void ClearFocusWithin(IInputElement element, bool clearRoot)
{
foreach (var visual in element.VisualChildren)
if (element is Visual v)
{
if (visual is IInputElement el && el.IsKeyboardFocusWithin)
foreach (var visual in v.VisualChildren)
{
ClearFocusWithin(el, true);
break;
if (visual is IInputElement el && el.IsKeyboardFocusWithin)
{
ClearFocusWithin(el, true);
break;
}
}
}
@ -81,7 +83,7 @@ namespace Avalonia.Input
break;
}
el = el.VisualParent as IInputElement;
el = (el as Visual)?.VisualParent as IInputElement;
}
el = oldElement;
@ -100,18 +102,21 @@ namespace Avalonia.Input
ie.IsKeyboardFocusWithin = true;
}
el = el.VisualParent as IInputElement;
el = (el as Visual)?.VisualParent as IInputElement;
}
}
private void ClearChildrenFocusWithin(IInputElement element, bool clearRoot)
{
foreach (var visual in element.VisualChildren)
if (element is Visual v)
{
if (visual is IInputElement el && el.IsKeyboardFocusWithin)
foreach (var visual in v.VisualChildren)
{
ClearChildrenFocusWithin(el, true);
break;
if (visual is IInputElement el && el.IsKeyboardFocusWithin)
{
ClearChildrenFocusWithin(el, true);
break;
}
}
}
@ -128,11 +133,11 @@ namespace Avalonia.Input
{
if (element != FocusedElement)
{
var interactive = FocusedElement as IInteractive;
var interactive = FocusedElement as Interactive;
if (FocusedElement != null &&
(!FocusedElement.IsAttachedToVisualTree ||
_focusedRoot != element?.VisualRoot as IInputRoot) &&
(!((Visual)FocusedElement).IsAttachedToVisualTree ||
_focusedRoot != ((Visual?)element)?.VisualRoot as IInputRoot) &&
_focusedRoot != null)
{
ClearChildrenFocusWithin(_focusedRoot, true);
@ -140,14 +145,14 @@ namespace Avalonia.Input
SetIsFocusWithin(FocusedElement, element);
_focusedElement = element;
_focusedRoot = _focusedElement?.VisualRoot as IInputRoot;
_focusedRoot = ((Visual?)_focusedElement)?.VisualRoot as IInputRoot;
interactive?.RaiseEvent(new RoutedEventArgs
{
RoutedEvent = InputElement.LostFocusEvent,
});
interactive = element as IInteractive;
interactive = element as Interactive;
interactive?.RaiseEvent(new GotFocusEventArgs
{
@ -191,8 +196,8 @@ namespace Avalonia.Input
KeyModifiers = keyInput.Modifiers.ToKeyModifiers(),
Source = element,
};
IVisual? currentHandler = element;
var currentHandler = element as Visual;
while (currentHandler != null && !ev.Handled && keyInput.Type == RawKeyEventType.KeyDown)
{
var bindings = (currentHandler as IInputElement)?.KeyBindings;

2
src/Avalonia.Base/Input/KeyboardNavigation.cs

@ -68,7 +68,7 @@ namespace Avalonia.Input
/// <param name="value">The tab index.</param>
public static void SetTabIndex(IInputElement element, int value)
{
((IAvaloniaObject)element).SetValue(TabIndexProperty, value);
((AvaloniaObject)element).SetValue(TabIndexProperty, value);
}
/// <summary>

6
src/Avalonia.Base/Input/KeyboardNavigationHandler.cs

@ -50,7 +50,7 @@ namespace Avalonia.Input
element = element ?? throw new ArgumentNullException(nameof(element));
// If there's a custom keyboard navigation handler as an ancestor, use that.
var custom = element.FindAncestorOfType<ICustomKeyboardNavigation>(true);
var custom = (element as Visual)?.FindAncestorOfType<ICustomKeyboardNavigation>(true);
if (custom is object && HandlePreCustomNavigation(custom, element, direction, out var ce))
return ce;
@ -156,9 +156,9 @@ namespace Avalonia.Input
NavigationDirection direction,
[NotNullWhen(true)] out IInputElement? result)
{
if (newElement is object)
if (newElement is Visual v)
{
var customHandler = newElement.FindAncestorOfType<ICustomKeyboardNavigation>(true);
var customHandler = v.FindAncestorOfType<ICustomKeyboardNavigation>(true);
if (customHandler is object)
{

14
src/Avalonia.Base/Input/MouseDevice.cs

@ -141,7 +141,7 @@ namespace Avalonia.Input
_lastClickRect = new Rect(p, new Size())
.Inflate(new Thickness(doubleClickSize.Width / 2, doubleClickSize.Height / 2));
_lastMouseDownButton = properties.PointerUpdateKind.GetMouseButton();
var e = new PointerPressedEventArgs(source, _pointer, root, p, timestamp, properties, inputModifiers, _clickCount);
var e = new PointerPressedEventArgs(source, _pointer, (Visual)root, p, timestamp, properties, inputModifiers, _clickCount);
source.RaiseEvent(e);
return e.Handled;
}
@ -161,7 +161,7 @@ namespace Avalonia.Input
if (source is object)
{
var e = new PointerEventArgs(InputElement.PointerMovedEvent, source, _pointer, root,
var e = new PointerEventArgs(InputElement.PointerMovedEvent, source, _pointer, (Visual)root,
p, timestamp, properties, inputModifiers, intermediatePoints);
source.RaiseEvent(e);
@ -181,7 +181,7 @@ namespace Avalonia.Input
if (source is not null)
{
var e = new PointerReleasedEventArgs(source, _pointer, root, p, timestamp, props, inputModifiers,
var e = new PointerReleasedEventArgs(source, _pointer, (Visual)root, p, timestamp, props, inputModifiers,
_lastMouseDownButton);
source?.RaiseEvent(e);
@ -204,7 +204,7 @@ namespace Avalonia.Input
if (source is not null)
{
var e = new PointerWheelEventArgs(source, _pointer, root, p, timestamp, props, inputModifiers, delta);
var e = new PointerWheelEventArgs(source, _pointer, (Visual)root, p, timestamp, props, inputModifiers, delta);
source?.RaiseEvent(e);
return e.Handled;
@ -224,7 +224,7 @@ namespace Avalonia.Input
if (source != null)
{
var e = new PointerDeltaEventArgs(Gestures.PointerTouchPadGestureMagnifyEvent, source,
_pointer, root, p, timestamp, props, inputModifiers, delta);
_pointer, (Visual)root, p, timestamp, props, inputModifiers, delta);
source?.RaiseEvent(e);
return e.Handled;
@ -244,7 +244,7 @@ namespace Avalonia.Input
if (source != null)
{
var e = new PointerDeltaEventArgs(Gestures.PointerTouchPadGestureRotateEvent, source,
_pointer, root, p, timestamp, props, inputModifiers, delta);
_pointer, (Visual)root, p, timestamp, props, inputModifiers, delta);
source?.RaiseEvent(e);
return e.Handled;
@ -264,7 +264,7 @@ namespace Avalonia.Input
if (source != null)
{
var e = new PointerDeltaEventArgs(Gestures.PointerTouchPadGestureSwipeEvent, source,
_pointer, root, p, timestamp, props, inputModifiers, delta);
_pointer, (Visual)root, p, timestamp, props, inputModifiers, delta);
source?.RaiseEvent(e);
return e.Handled;

12
src/Avalonia.Base/Input/Navigation/FocusExtensions.cs

@ -10,13 +10,21 @@ namespace Avalonia.Input.Navigation
/// </summary>
/// <param name="e">The element.</param>
/// <returns>True if the element can be focused.</returns>
public static bool CanFocus(this IInputElement e) => e.Focusable && e.IsEffectivelyEnabled && e.IsVisible;
public static bool CanFocus(this IInputElement e)
{
var visible = (e as Visual)?.IsVisible ?? true;
return e.Focusable && e.IsEffectivelyEnabled && visible;
}
/// <summary>
/// Checks if descendants of the specified element can be focused.
/// </summary>
/// <param name="e">The element.</param>
/// <returns>True if descendants of the element can be focused.</returns>
public static bool CanFocusDescendants(this IInputElement e) => e.IsEffectivelyEnabled && e.IsVisible;
public static bool CanFocusDescendants(this IInputElement e)
{
var visible = (e as Visual)?.IsVisible ?? true;
return e.IsEffectivelyEnabled && visible;
}
}
}

18
src/Avalonia.Base/Input/Navigation/TabNavigation.cs

@ -212,9 +212,10 @@ namespace Avalonia.Input.Navigation
if (!IsFocusScope(e))
{
// Verify if focusedElement is a visual descendant of e
if (focusedElement is IVisual visualFocusedElement &&
if (focusedElement is Visual visualFocusedElement &&
e is Visual v &&
visualFocusedElement != e &&
e.IsVisualAncestorOf(visualFocusedElement))
v.IsVisualAncestorOf(visualFocusedElement))
{
return focusedElement;
}
@ -236,7 +237,7 @@ namespace Avalonia.Input.Navigation
if (uiElement is null || IsVisibleAndEnabled(uiElement))
{
if (e is IVisual elementAsVisual)
if (e is Visual elementAsVisual)
{
var children = elementAsVisual.VisualChildren;
var count = children.Count;
@ -272,7 +273,7 @@ namespace Avalonia.Input.Navigation
if (uiElement == null || IsVisibleAndEnabled(uiElement))
{
var elementAsVisual = e as IVisual;
var elementAsVisual = e as Visual;
if (elementAsVisual != null)
{
@ -385,7 +386,7 @@ namespace Avalonia.Input.Navigation
private static IInputElement? GetNextSibling(IInputElement e)
{
if (GetParent(e) is IVisual parentAsVisual && e is IVisual elementAsVisual)
if (GetParent(e) is Visual parentAsVisual && e is Visual elementAsVisual)
{
var children = parentAsVisual.VisualChildren;
var count = children.Count;
@ -589,7 +590,7 @@ namespace Avalonia.Input.Navigation
private static IInputElement? GetPreviousSibling(IInputElement e)
{
if (GetParent(e) is IVisual parentAsVisual && e is IVisual elementAsVisual)
if (GetParent(e) is Visual parentAsVisual && e is Visual elementAsVisual)
{
var children = parentAsVisual.VisualChildren;
var count = children.Count;
@ -646,7 +647,7 @@ namespace Avalonia.Input.Navigation
private static IInputElement? GetParent(IInputElement e)
{
// For Visual - go up the visual parent chain until we find Visual.
if (e is IVisual v)
if (e is Visual v)
return v.FindAncestorOfType<IInputElement>();
// This will need to be implemented when we have non-visual input elements.
@ -669,6 +670,7 @@ namespace Avalonia.Input.Navigation
}
private static bool IsTabStopOrGroup(IInputElement e) => IsTabStop(e) || IsGroup(e);
private static bool IsVisibleAndEnabled(IInputElement e) => e.IsVisible && e.IsEnabled;
private static bool IsVisible(IInputElement e) => (e as Visual)?.IsVisible ?? true;
private static bool IsVisibleAndEnabled(IInputElement e) => IsVisible(e) && e.IsEnabled;
}
}

6
src/Avalonia.Base/Input/PenDevice.cs

@ -91,7 +91,7 @@ namespace Avalonia.Input
_lastClickRect = new Rect(p, new Size())
.Inflate(new Thickness(doubleClickSize.Width / 2, doubleClickSize.Height / 2));
_lastMouseDownButton = properties.PointerUpdateKind.GetMouseButton();
var e = new PointerPressedEventArgs(source, pointer, root, p, timestamp, properties, inputModifiers, _clickCount);
var e = new PointerPressedEventArgs(source, pointer, (Visual)root, p, timestamp, properties, inputModifiers, _clickCount);
source.RaiseEvent(e);
return e.Handled;
}
@ -108,7 +108,7 @@ namespace Avalonia.Input
if (source is not null)
{
var e = new PointerEventArgs(InputElement.PointerMovedEvent, source, pointer, root,
var e = new PointerEventArgs(InputElement.PointerMovedEvent, source, pointer, (Visual)root,
p, timestamp, properties, inputModifiers, intermediatePoints);
source.RaiseEvent(e);
@ -126,7 +126,7 @@ namespace Avalonia.Input
if (source is not null)
{
var e = new PointerReleasedEventArgs(source, pointer, root, p, timestamp, properties, inputModifiers,
var e = new PointerReleasedEventArgs(source, pointer, (Visual)root, p, timestamp, properties, inputModifiers,
_lastMouseDownButton);
source?.RaiseEvent(e);

20
src/Avalonia.Base/Input/Pointer.cs

@ -21,10 +21,10 @@ namespace Avalonia.Input
IInputElement? FindCommonParent(IInputElement? control1, IInputElement? control2)
{
if (control1 == null || control2 == null)
if (control1 is not Visual c1 || control2 is not Visual c2)
return null;
var seen = new HashSet<IInputElement>(control1.GetSelfAndVisualAncestors().OfType<IInputElement>());
return control2.GetSelfAndVisualAncestors().OfType<IInputElement>().FirstOrDefault(seen.Contains);
var seen = new HashSet<IInputElement>(c1.GetSelfAndVisualAncestors().OfType<IInputElement>());
return c2.GetSelfAndVisualAncestors().OfType<IInputElement>().FirstOrDefault(seen.Contains);
}
protected virtual void PlatformCapture(IInputElement? element)
@ -34,15 +34,15 @@ namespace Avalonia.Input
public void Capture(IInputElement? control)
{
if (Captured != null)
Captured.DetachedFromVisualTree -= OnCaptureDetached;
if (Captured is Visual v1)
v1.DetachedFromVisualTree -= OnCaptureDetached;
var oldCapture = Captured;
Captured = control;
PlatformCapture(control);
if (oldCapture != null)
if (oldCapture is Visual v2)
{
var commonParent = FindCommonParent(control, oldCapture);
foreach (var notifyTarget in oldCapture.GetSelfAndVisualAncestors().OfType<IInputElement>())
foreach (var notifyTarget in v2.GetSelfAndVisualAncestors().OfType<IInputElement>())
{
if (notifyTarget == commonParent)
break;
@ -50,11 +50,11 @@ namespace Avalonia.Input
}
}
if (Captured != null)
Captured.DetachedFromVisualTree += OnCaptureDetached;
if (Captured is Visual v3)
v3.DetachedFromVisualTree += OnCaptureDetached;
}
IInputElement? GetNextCapture(IVisual parent)
IInputElement? GetNextCapture(Visual parent)
{
return parent as IInputElement ?? parent.FindAncestorOfType<IInputElement>();
}

4
src/Avalonia.Base/Input/PointerDeltaEventArgs.cs

@ -7,8 +7,8 @@ namespace Avalonia.Input
{
public Vector Delta { get; set; }
internal PointerDeltaEventArgs(RoutedEvent routedEvent, IInteractive? source,
IPointer pointer, IVisual rootVisual, Point rootVisualPosition, ulong timestamp,
internal PointerDeltaEventArgs(RoutedEvent routedEvent, object? source,
IPointer pointer, Visual rootVisual, Point rootVisualPosition, ulong timestamp,
PointerPointProperties properties, KeyModifiers modifiers, Vector delta)
: base(routedEvent, source, pointer, rootVisual, rootVisualPosition,
timestamp, properties, modifiers)

30
src/Avalonia.Base/Input/PointerEventArgs.cs

@ -8,15 +8,15 @@ namespace Avalonia.Input
{
public class PointerEventArgs : RoutedEventArgs
{
private readonly IVisual? _rootVisual;
private readonly Visual? _rootVisual;
private readonly Point _rootVisualPosition;
private readonly PointerPointProperties _properties;
private readonly Lazy<IReadOnlyList<RawPointerPoint>?>? _previousPoints;
internal PointerEventArgs(RoutedEvent routedEvent,
IInteractive? source,
object? source,
IPointer pointer,
IVisual? rootVisual, Point rootVisualPosition,
Visual? rootVisual, Point rootVisualPosition,
ulong timestamp,
PointerPointProperties properties,
KeyModifiers modifiers)
@ -30,11 +30,11 @@ namespace Avalonia.Input
Timestamp = timestamp;
KeyModifiers = modifiers;
}
internal PointerEventArgs(RoutedEvent routedEvent,
IInteractive? source,
object? source,
IPointer pointer,
IVisual? rootVisual, Point rootVisualPosition,
Visual? rootVisual, Point rootVisualPosition,
ulong timestamp,
PointerPointProperties properties,
KeyModifiers modifiers,
@ -59,7 +59,7 @@ namespace Avalonia.Input
/// </summary>
public KeyModifiers KeyModifiers { get; }
private Point GetPosition(Point pt, IVisual? relativeTo)
private Point GetPosition(Point pt, Visual? relativeTo)
{
if (_rootVisual == null)
return default;
@ -74,14 +74,14 @@ namespace Avalonia.Input
/// </summary>
/// <param name="relativeTo">The control.</param>
/// <returns>The pointer position in the control's coordinates.</returns>
public Point GetPosition(IVisual? relativeTo) => GetPosition(_rootVisualPosition, relativeTo);
public Point GetPosition(Visual? relativeTo) => GetPosition(_rootVisualPosition, relativeTo);
/// <summary>
/// Returns the PointerPoint associated with the current event
/// </summary>
/// <param name="relativeTo">The visual which coordinate system to use. Pass null for toplevel coordinate system</param>
/// <returns></returns>
public PointerPoint GetCurrentPoint(IVisual? relativeTo)
public PointerPoint GetCurrentPoint(Visual? relativeTo)
=> new PointerPoint(Pointer, GetPosition(relativeTo), _properties);
/// <summary>
@ -89,7 +89,7 @@ namespace Avalonia.Input
/// </summary>
/// <param name="relativeTo">The visual which coordinate system to use. Pass null for toplevel coordinate system</param>
/// <returns></returns>
public IReadOnlyList<PointerPoint> GetIntermediatePoints(IVisual? relativeTo)
public IReadOnlyList<PointerPoint> GetIntermediatePoints(Visual? relativeTo)
{
var previousPoints = _previousPoints?.Value;
if (previousPoints == null || previousPoints.Count == 0)
@ -125,9 +125,9 @@ namespace Avalonia.Input
public class PointerPressedEventArgs : PointerEventArgs
{
internal PointerPressedEventArgs(
IInteractive source,
object source,
IPointer pointer,
IVisual rootVisual, Point rootVisualPosition,
Visual rootVisual, Point rootVisualPosition,
ulong timestamp,
PointerPointProperties properties,
KeyModifiers modifiers,
@ -144,8 +144,8 @@ namespace Avalonia.Input
public class PointerReleasedEventArgs : PointerEventArgs
{
internal PointerReleasedEventArgs(
IInteractive source, IPointer pointer,
IVisual rootVisual, Point rootVisualPosition, ulong timestamp,
object source, IPointer pointer,
Visual rootVisual, Point rootVisualPosition, ulong timestamp,
PointerPointProperties properties, KeyModifiers modifiers,
MouseButton initialPressMouseButton)
: base(InputElement.PointerReleasedEvent, source, pointer, rootVisual, rootVisualPosition,
@ -164,7 +164,7 @@ namespace Avalonia.Input
{
public IPointer Pointer { get; }
internal PointerCaptureLostEventArgs(IInteractive source, IPointer pointer) : base(InputElement.PointerCaptureLostEvent)
internal PointerCaptureLostEventArgs(object source, IPointer pointer) : base(InputElement.PointerCaptureLostEvent)
{
Pointer = pointer;
Source = source;

48
src/Avalonia.Base/Input/PointerOverPreProcessor.cs

@ -44,7 +44,7 @@ namespace Avalonia.Input
&& _lastPointer is (var lastPointer, var lastPosition))
{
_lastPointer = null;
ClearPointerOver(lastPointer, args.Root, 0, args.Root.PointToClient(lastPosition),
ClearPointerOver(lastPointer, args.Root, 0, PointToClient(args.Root, lastPosition),
new PointerPointProperties(args.InputModifiers, args.Type.ToUpdateKind()),
args.InputModifiers.ToKeyModifiers());
}
@ -64,14 +64,14 @@ namespace Avalonia.Input
{
if (_lastPointer is (var pointer, var position))
{
var clientPoint = _inputRoot.PointToClient(position);
var clientPoint = PointToClient(_inputRoot, position);
if (dirtyRect.Contains(clientPoint))
{
var element = pointer.Captured ?? _inputRoot.InputHitTest(clientPoint);
SetPointerOver(pointer, _inputRoot, element, 0, clientPoint, PointerPointProperties.None, KeyModifiers.None);
}
else if (!_inputRoot.Bounds.Contains(clientPoint))
else if (!((Visual)_inputRoot).Bounds.Contains(clientPoint))
{
ClearPointerOver(pointer, _inputRoot, 0, clientPoint, PointerPointProperties.None, KeyModifiers.None);
}
@ -82,7 +82,7 @@ namespace Avalonia.Input
{
if (_lastPointer is (var pointer, var position))
{
var clientPoint = _inputRoot.PointToClient(position);
var clientPoint = PointToClient(_inputRoot, position);
ClearPointerOver(pointer, _inputRoot, 0, clientPoint, PointerPointProperties.None, KeyModifiers.None);
}
_lastPointer = null;
@ -101,10 +101,10 @@ namespace Avalonia.Input
// Do not pass rootVisual, when we have unknown position,
// so GetPosition won't return invalid values.
var e = new PointerEventArgs(InputElement.PointerExitedEvent, element, pointer,
position.HasValue ? root : null, position.HasValue ? position.Value : default,
position.HasValue ? root as Visual : null, position.HasValue ? position.Value : default,
timestamp, properties, inputModifiers);
if (element != null && !element.IsAttachedToVisualTree)
if (element is Visual v && !v.IsAttachedToVisualTree)
{
// element has been removed from visual tree so do top down cleanup
if (root.IsPointerOver)
@ -117,7 +117,7 @@ namespace Avalonia.Input
e.Source = element;
e.Handled = false;
element.RaiseEvent(e);
element = (IInputElement?)element.VisualParent;
element = GetVisualParent(element);
}
root.PointerOverElement = null;
@ -127,14 +127,18 @@ namespace Avalonia.Input
private void ClearChildrenPointerOver(PointerEventArgs e, IInputElement element, bool clearRoot)
{
foreach (IInputElement el in element.VisualChildren)
if (element is Visual v)
{
if (el.IsPointerOver)
foreach (IInputElement el in v.VisualChildren)
{
ClearChildrenPointerOver(e, el, true);
break;
if (el.IsPointerOver)
{
ClearChildrenPointerOver(e, el, true);
break;
}
}
}
if (clearRoot)
{
e.Source = element;
@ -160,7 +164,7 @@ namespace Avalonia.Input
}
}
_lastPointer = (pointer, root.PointToScreen(position));
_lastPointer = (pointer, ((Visual)root).PointToScreen(position));
}
private void SetPointerOverToElement(IPointer pointer, IInputRoot root, IInputElement element,
@ -177,14 +181,14 @@ namespace Avalonia.Input
branch = el;
break;
}
el = (IInputElement?)el.VisualParent;
el = GetVisualParent(el);
}
el = root.PointerOverElement;
var e = new PointerEventArgs(InputElement.PointerExitedEvent, el, pointer, root, position,
var e = new PointerEventArgs(InputElement.PointerExitedEvent, el, pointer, (Visual)root, position,
timestamp, properties, inputModifiers);
if (el != null && branch != null && !el.IsAttachedToVisualTree)
if (el is Visual v && branch != null && !v.IsAttachedToVisualTree)
{
ClearChildrenPointerOver(e, branch, false);
}
@ -194,7 +198,7 @@ namespace Avalonia.Input
e.Source = el;
e.Handled = false;
el.RaiseEvent(e);
el = (IInputElement?)el.VisualParent;
el = GetVisualParent(el);
}
el = root.PointerOverElement = element;
@ -206,8 +210,18 @@ namespace Avalonia.Input
e.Source = el;
e.Handled = false;
el.RaiseEvent(e);
el = (IInputElement?)el.VisualParent;
el = GetVisualParent(el);
}
}
private static IInputElement? GetVisualParent(IInputElement e)
{
return (e as Visual)?.VisualParent as IInputElement;
}
private static Point PointToClient(IInputRoot root, PixelPoint p)
{
return ((Visual)root).PointToClient(p);
}
}
}

2
src/Avalonia.Base/Input/PointerWheelEventArgs.cs

@ -7,7 +7,7 @@ namespace Avalonia.Input
{
public Vector Delta { get; set; }
internal PointerWheelEventArgs(IInteractive source, IPointer pointer, IVisual rootVisual,
internal PointerWheelEventArgs(object source, IPointer pointer, Visual rootVisual,
Point rootVisualPosition, ulong timestamp,
PointerPointProperties properties, KeyModifiers modifiers, Vector delta)
: base(InputElement.PointerWheelChangedEvent, source, pointer, rootVisual, rootVisualPosition,

2
src/Avalonia.Base/Input/TappedEventArgs.cs

@ -17,6 +17,6 @@ namespace Avalonia.Input
public KeyModifiers KeyModifiers => lastPointerEventArgs.KeyModifiers;
public ulong Timestamp => lastPointerEventArgs.Timestamp;
public Point GetPosition(IVisual? relativeTo) => lastPointerEventArgs.GetPosition(relativeTo);
public Point GetPosition(Visual? relativeTo) => lastPointerEventArgs.GetPosition(relativeTo);
}
}

2
src/Avalonia.Base/Input/TextInput/ITextInputMethodClient.cs

@ -16,7 +16,7 @@ namespace Avalonia.Input.TextInput
/// <summary>
/// The visual that's showing the text
/// </summary>
IVisual TextViewVisual { get; }
Visual TextViewVisual { get; }
/// <summary>
/// Should be fired when text-hosting visual is changed
/// </summary>

9
src/Avalonia.Base/Input/TextInput/InputMethodManager.cs

@ -73,10 +73,13 @@ namespace Avalonia.Input.TextInput
private void UpdateCursorRect()
{
if (_im == null || _client == null || _focusedElement?.VisualRoot == null)
if (_im == null ||
_client == null ||
_focusedElement is not Visual v ||
v.VisualRoot is not Visual root)
return;
var transform = _focusedElement.TransformToVisual(_focusedElement.VisualRoot);
var transform = v.TransformToVisual(root);
if (transform == null)
_im.SetCursorRect(default);
else
@ -95,7 +98,7 @@ namespace Avalonia.Input.TextInput
return;
_focusedElement = element;
var inputMethod = (element?.VisualRoot as ITextInputMethodRoot)?.InputMethod;
var inputMethod = ((element as Visual)?.VisualRoot as ITextInputMethodRoot)?.InputMethod;
if (_im != inputMethod)
{

6
src/Avalonia.Base/Input/TextInput/TransformTrackingHelper.cs

@ -7,7 +7,7 @@ namespace Avalonia.Input.TextInput
{
class TransformTrackingHelper : IDisposable
{
private IVisual? _visual;
private Visual? _visual;
private bool _queuedForUpdate;
private readonly EventHandler<AvaloniaPropertyChangedEventArgs> _propertyChangedHandler;
private readonly List<Visual> _propertyChangedSubscriptions = new List<Visual>();
@ -17,7 +17,7 @@ namespace Avalonia.Input.TextInput
_propertyChangedHandler = PropertyChangedHandler;
}
public void SetVisual(IVisual? visual)
public void SetVisual(Visual? visual)
{
Dispose();
_visual = visual;
@ -72,7 +72,7 @@ namespace Avalonia.Input.TextInput
{
Matrix? matrix = null;
if (_visual != null && _visual.VisualRoot != null)
matrix = _visual.TransformToVisual(_visual.VisualRoot);
matrix = _visual.TransformToVisual((Visual)_visual.VisualRoot);
if (Matrix != matrix)
{
Matrix = matrix;

6
src/Avalonia.Base/Input/TouchDevice.cs

@ -75,7 +75,7 @@ namespace Avalonia.Input
}
target.RaiseEvent(new PointerPressedEventArgs(target, pointer,
args.Root, args.Position, ev.Timestamp,
(Visual)args.Root, args.Position, ev.Timestamp,
new PointerPointProperties(GetModifiers(args.InputModifiers, true), updateKind),
keyModifier, _clickCount));
}
@ -86,7 +86,7 @@ namespace Avalonia.Input
using (pointer)
{
target.RaiseEvent(new PointerReleasedEventArgs(target, pointer,
args.Root, args.Position, ev.Timestamp,
(Visual)args.Root, args.Position, ev.Timestamp,
new PointerPointProperties(GetModifiers(args.InputModifiers, false), updateKind),
keyModifier, MouseButton.Left));
}
@ -101,7 +101,7 @@ namespace Avalonia.Input
if (args.Type == RawPointerEventType.TouchUpdate)
{
target.RaiseEvent(new PointerEventArgs(InputElement.PointerMovedEvent, target, pointer, args.Root,
target.RaiseEvent(new PointerEventArgs(InputElement.PointerMovedEvent, target, pointer, (Visual)args.Root,
args.Position, ev.Timestamp,
new PointerPointProperties(GetModifiers(args.InputModifiers, true), updateKind),
keyModifier, args.IntermediatePoints));

12
src/Avalonia.Base/Interactivity/EventRoute.cs

@ -43,7 +43,7 @@ namespace Avalonia.Interactivity
/// `DynamicInvoke` on the handler.
/// </param>
public void Add(
IInteractive target,
Interactive target,
Delegate handler,
RoutingStrategies routes,
bool handledEventsToo = false,
@ -60,7 +60,7 @@ namespace Avalonia.Interactivity
/// Adds a class handler to the route.
/// </summary>
/// <param name="target">The target on which the event should be raised.</param>
public void AddClassHandler(IInteractive target)
public void AddClassHandler(Interactive target)
{
target = target ?? throw new ArgumentNullException(nameof(target));
@ -73,7 +73,7 @@ namespace Avalonia.Interactivity
/// </summary>
/// <param name="source">The event source.</param>
/// <param name="e">The event args.</param>
public void RaiseEvent(IInteractive source, RoutedEventArgs e)
public void RaiseEvent(Interactive source, RoutedEventArgs e)
{
source = source ?? throw new ArgumentNullException(nameof(source));
e = e ?? throw new ArgumentNullException(nameof(e));
@ -125,7 +125,7 @@ namespace Avalonia.Interactivity
throw new ArgumentException("Event source may not be null", nameof(e));
}
IInteractive? lastTarget = null;
Interactive? lastTarget = null;
var start = 0;
var end = _route.Count;
var step = 1;
@ -177,7 +177,7 @@ namespace Avalonia.Interactivity
private readonly struct RouteItem
{
public RouteItem(
IInteractive target,
Interactive target,
Delegate? handler,
Action<Delegate, object, RoutedEventArgs>? adapter,
RoutingStrategies routes,
@ -190,7 +190,7 @@ namespace Avalonia.Interactivity
HandledEventsToo = handledEventsToo;
}
public IInteractive Target { get; }
public Interactive Target { get; }
public Delegate? Handler { get; }
public Action<Delegate, object, RoutedEventArgs>? Adapter { get; }
public RoutingStrategies Routes { get; }

53
src/Avalonia.Base/Interactivity/IInteractive.cs

@ -1,53 +0,0 @@
using System;
using Avalonia.Metadata;
#nullable enable
namespace Avalonia.Interactivity
{
/// <summary>
/// Interface for objects that raise routed events.
/// </summary>
[NotClientImplementable]
public interface IInteractive
{
/// <summary>
/// Gets the interactive parent of the object for bubbling and tunneling events.
/// </summary>
IInteractive? InteractiveParent { get; }
/// <summary>
/// Adds a handler for the specified routed event.
/// </summary>
/// <param name="routedEvent">The routed event.</param>
/// <param name="handler">The handler.</param>
/// <param name="routes">The routing strategies to listen to.</param>
/// <param name="handledEventsToo">Whether handled events should also be listened for.</param>
/// <returns>A disposable that terminates the event subscription.</returns>
void AddHandler(
RoutedEvent routedEvent,
Delegate handler,
RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble,
bool handledEventsToo = false);
/// <summary>
/// Removes a handler for the specified routed event.
/// </summary>
/// <param name="routedEvent">The routed event.</param>
/// <param name="handler">The handler.</param>
void RemoveHandler(RoutedEvent routedEvent, Delegate handler);
/// <summary>
/// Adds the object's handlers for a routed event to an event route.
/// </summary>
/// <param name="routedEvent">The event.</param>
/// <param name="route">The event route.</param>
void AddToEventRoute(RoutedEvent routedEvent, EventRoute route);
/// <summary>
/// Raises a routed event.
/// </summary>
/// <param name="e">The event args.</param>
void RaiseEvent(RoutedEventArgs e);
}
}

40
src/Avalonia.Base/Interactivity/Interactive.cs

@ -10,14 +10,14 @@ namespace Avalonia.Interactivity
/// <summary>
/// Base class for objects that raise routed events.
/// </summary>
public class Interactive : Layoutable, IInteractive
public class Interactive : Layoutable
{
private Dictionary<RoutedEvent, List<EventSubscription>>? _eventHandlers;
/// <summary>
/// Gets the interactive parent of the object for bubbling and tunneling events.
/// </summary>
IInteractive? IInteractive.InteractiveParent => ((IVisual)this).VisualParent as IInteractive;
protected internal virtual Interactive? InteractiveParent => VisualParent as Interactive;
/// <summary>
/// Adds a handler for the specified routed event.
@ -125,21 +125,6 @@ namespace Avalonia.Interactivity
route.RaiseEvent(this, e);
}
void IInteractive.AddToEventRoute(RoutedEvent routedEvent, EventRoute route)
{
routedEvent = routedEvent ?? throw new ArgumentNullException(nameof(routedEvent));
route = route ?? throw new ArgumentNullException(nameof(route));
if (_eventHandlers != null &&
_eventHandlers.TryGetValue(routedEvent, out var subscriptions))
{
foreach (var sub in subscriptions)
{
route.Add(this, sub.Handler, sub.Routes, sub.HandledEventsToo, sub.InvokeAdapter);
}
}
}
/// <summary>
/// Builds an event route for a routed event.
/// </summary>
@ -151,7 +136,7 @@ namespace Avalonia.Interactivity
/// and should be avoided if there are no handlers for an event. In these cases you can call
/// this method to build the event route and check the <see cref="EventRoute.HasHandlers"/>
/// property to see if there are any handlers registered on the route. If there are, call
/// <see cref="EventRoute.RaiseEvent(IInteractive, RoutedEventArgs)"/> to raise the event.
/// <see cref="EventRoute.RaiseEvent(Interactive, RoutedEventArgs)"/> to raise the event.
/// </remarks>
protected EventRoute BuildEventRoute(RoutedEvent e)
{
@ -163,7 +148,7 @@ namespace Avalonia.Interactivity
if (e.RoutingStrategies.HasAllFlags(RoutingStrategies.Bubble) ||
e.RoutingStrategies.HasAllFlags(RoutingStrategies.Tunnel))
{
IInteractive? element = this;
Interactive? element = this;
while (element != null)
{
@ -183,7 +168,7 @@ namespace Avalonia.Interactivity
result.AddClassHandler(this);
}
((IInteractive)this).AddToEventRoute(e, result);
AddToEventRoute(e, result);
}
return result;
@ -202,6 +187,21 @@ namespace Avalonia.Interactivity
subscriptions.Add(subscription);
}
private void AddToEventRoute(RoutedEvent routedEvent, EventRoute route)
{
routedEvent = routedEvent ?? throw new ArgumentNullException(nameof(routedEvent));
route = route ?? throw new ArgumentNullException(nameof(route));
if (_eventHandlers != null &&
_eventHandlers.TryGetValue(routedEvent, out var subscriptions))
{
foreach (var sub in subscriptions)
{
route.Add(this, sub.Handler, sub.Routes, sub.HandledEventsToo, sub.InvokeAdapter);
}
}
}
private readonly struct EventSubscription
{
public EventSubscription(

8
src/Avalonia.Base/Interactivity/InteractiveExtensions.cs

@ -5,7 +5,7 @@ using System.Reactive.Linq;
namespace Avalonia.Interactivity
{
/// <summary>
/// Provides extension methods for the <see cref="IInteractive"/> interface.
/// Provides extension methods for the <see cref="Interactive"/> interface.
/// </summary>
public static class InteractiveExtensions
{
@ -19,7 +19,7 @@ namespace Avalonia.Interactivity
/// <param name="routes">The routing strategies to listen to.</param>
/// <param name="handledEventsToo">Whether handled events should also be listened for.</param>
/// <returns>A disposable that terminates the event subscription.</returns>
public static IDisposable AddDisposableHandler<TEventArgs>(this IInteractive o, RoutedEvent<TEventArgs> routedEvent,
public static IDisposable AddDisposableHandler<TEventArgs>(this Interactive o, RoutedEvent<TEventArgs> routedEvent,
EventHandler<TEventArgs> handler,
RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble,
bool handledEventsToo = false) where TEventArgs : RoutedEventArgs
@ -31,6 +31,8 @@ namespace Avalonia.Interactivity
state => state.instance.RemoveHandler(state.routedEvent, state.handler));
}
public static Interactive? GetInteractiveParent(this Interactive o) => o.InteractiveParent;
/// <summary>
/// Gets an observable for a <see cref="RoutedEvent{TEventArgs}"/>.
/// </summary>
@ -42,7 +44,7 @@ namespace Avalonia.Interactivity
/// An observable which fires each time the event is raised.
/// </returns>
public static IObservable<TEventArgs> GetObservable<TEventArgs>(
this IInteractive o,
this Interactive o,
RoutedEvent<TEventArgs> routedEvent,
RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble,
bool handledEventsToo = false)

2
src/Avalonia.Base/Interactivity/RoutedEvent.cs

@ -116,7 +116,7 @@ namespace Avalonia.Interactivity
public IDisposable AddClassHandler<TTarget>(
Action<TTarget, TEventArgs> handler,
RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble,
bool handledEventsToo = false) where TTarget : class, IInteractive
bool handledEventsToo = false) where TTarget : Interactive
{
void Adapter(object? sender, RoutedEventArgs e)
{

4
src/Avalonia.Base/Interactivity/RoutedEventArgs.cs

@ -28,7 +28,7 @@ namespace Avalonia.Interactivity
/// </summary>
/// <param name="routedEvent">The routed event associated with these event args.</param>
/// <param name="source">The source object that raised the routed event.</param>
public RoutedEventArgs(RoutedEvent? routedEvent, IInteractive? source)
public RoutedEventArgs(RoutedEvent? routedEvent, object? source)
{
RoutedEvent = routedEvent;
Source = source;
@ -55,6 +55,6 @@ namespace Avalonia.Interactivity
/// <summary>
/// Gets or sets the source object that raised the routed event.
/// </summary>
public IInteractive? Source { get; set; }
public object? Source { get; set; }
}
}

2
src/Avalonia.Base/Layout/AttachedLayout.cs

@ -43,7 +43,7 @@ namespace Avalonia.Layout
/// <summary>
/// Initializes any per-container state the layout requires when it is attached to an
/// <see cref="ILayoutable"/> container.
/// <see cref="Layoutable"/> container.
/// </summary>
/// <param name="context">
/// The context object that facilitates communication between the layout and its host

14
src/Avalonia.Base/Layout/ElementManager.cs

@ -13,7 +13,7 @@ namespace Avalonia.Layout
{
internal class ElementManager
{
private readonly List<ILayoutable?> _realizedElements = new List<ILayoutable?>();
private readonly List<Layoutable?> _realizedElements = new List<Layoutable?>();
private readonly List<Rect> _realizedElementLayoutBounds = new List<Rect>();
private int _firstRealizedDataIndex;
private VirtualizingLayoutContext? _context;
@ -69,9 +69,9 @@ namespace Avalonia.Layout
return IsVirtualizingContext ? _realizedElements.Count : _context!.ItemCount;
}
public ILayoutable GetAt(int realizedIndex)
public Layoutable GetAt(int realizedIndex)
{
ILayoutable? element;
Layoutable? element;
if (IsVirtualizingContext)
{
@ -99,7 +99,7 @@ namespace Avalonia.Layout
return element;
}
public void Add(ILayoutable element, int dataIndex)
public void Add(Layoutable element, int dataIndex)
{
if (_realizedElements.Count == 0)
{
@ -110,7 +110,7 @@ namespace Avalonia.Layout
_realizedElementLayoutBounds.Add(default);
}
public void Insert(int realizedIndex, int dataIndex, ILayoutable? element)
public void Insert(int realizedIndex, int dataIndex, Layoutable? element)
{
if (realizedIndex == 0)
{
@ -207,7 +207,7 @@ namespace Avalonia.Layout
public bool IsIndexValidInData(int currentIndex) => (uint)currentIndex < _context!.ItemCount;
public ILayoutable? GetRealizedElement(int dataIndex)
public Layoutable? GetRealizedElement(int dataIndex)
{
return IsVirtualizingContext ?
GetAt(GetRealizedRangeIndexFromDataIndex(dataIndex)) :
@ -330,7 +330,7 @@ namespace Avalonia.Layout
}
}
public int GetElementDataIndex(ILayoutable suggestedAnchor)
public int GetElementDataIndex(Layoutable suggestedAnchor)
{
var it = _realizedElements.IndexOf(suggestedAnchor);
return it != -1 ? GetDataIndexFromRealizedRangeIndex(it) : -1;

10
src/Avalonia.Base/Layout/FlowLayoutAlgorithm.cs

@ -144,7 +144,7 @@ namespace Avalonia.Layout
}
public Size MeasureElement(
ILayoutable element,
Layoutable element,
int index,
Size availableSize,
VirtualizingLayoutContext context)
@ -512,9 +512,9 @@ namespace Avalonia.Layout
private Rect EstimateExtent(Size availableSize, string? layoutId)
{
ILayoutable? firstRealizedElement = null;
Layoutable? firstRealizedElement = null;
Rect firstBounds = new Rect();
ILayoutable? lastRealizedElement = null;
Layoutable? lastRealizedElement = null;
Rect lastBounds = new Rect();
int firstDataIndex = -1;
int lastDataIndex = -1;
@ -727,7 +727,7 @@ namespace Avalonia.Layout
}
}
public ILayoutable? GetElementIfRealized(int dataIndex)
public Layoutable? GetElementIfRealized(int dataIndex)
{
if (_elementManager.IsDataIndexRealized(dataIndex))
{
@ -737,7 +737,7 @@ namespace Avalonia.Layout
return null;
}
public bool TryAddElement0(ILayoutable element)
public bool TryAddElement0(Layoutable element)
{
if (_elementManager.GetRealizedElementCount() == 0)
{

6
src/Avalonia.Base/Layout/IFlowLayoutAlgorithmDelegates.cs

@ -21,14 +21,14 @@ namespace Avalonia.Layout
Rect Algorithm_GetExtent(
Size availableSize,
VirtualizingLayoutContext context,
ILayoutable? firstRealized,
Layoutable? firstRealized,
int firstRealizedItemIndex,
Rect firstRealizedLayoutBounds,
ILayoutable? lastRealized,
Layoutable? lastRealized,
int lastRealizedItemIndex,
Rect lastRealizedLayoutBounds);
void Algorithm_OnElementMeasured(
ILayoutable element,
Layoutable element,
int index,
Size availableSize,
Size measureSize,

8
src/Avalonia.Base/Layout/ILayoutManager.cs

@ -18,13 +18,13 @@ namespace Avalonia.Layout
/// Notifies the layout manager that a control requires a measure.
/// </summary>
/// <param name="control">The control.</param>
void InvalidateMeasure(ILayoutable control);
void InvalidateMeasure(Layoutable control);
/// <summary>
/// Notifies the layout manager that a control requires an arrange.
/// </summary>
/// <param name="control">The control.</param>
void InvalidateArrange(ILayoutable control);
void InvalidateArrange(Layoutable control);
/// <summary>
/// Executes a layout pass.
@ -48,12 +48,12 @@ namespace Avalonia.Layout
/// Registers a control as wanting to receive effective viewport notifications.
/// </summary>
/// <param name="control">The control.</param>
void RegisterEffectiveViewportListener(ILayoutable control);
void RegisterEffectiveViewportListener(Layoutable control);
/// <summary>
/// Registers a control as no longer wanting to receive effective viewport notifications.
/// </summary>
/// <param name="control">The control.</param>
void UnregisterEffectiveViewportListener(ILayoutable control);
void UnregisterEffectiveViewportListener(Layoutable control);
}
}

2
src/Avalonia.Base/Layout/ILayoutRoot.cs

@ -6,7 +6,7 @@ namespace Avalonia.Layout
/// Defines the root of a layoutable tree.
/// </summary>
[NotClientImplementable]
public interface ILayoutRoot : ILayoutable
public interface ILayoutRoot
{
/// <summary>
/// The size available to lay out the controls.

122
src/Avalonia.Base/Layout/ILayoutable.cs

@ -1,122 +0,0 @@
using Avalonia.Metadata;
using Avalonia.VisualTree;
namespace Avalonia.Layout
{
/// <summary>
/// Defines layout-related functionality for a control.
/// </summary>
[NotClientImplementable]
public interface ILayoutable : IVisual
{
/// <summary>
/// Gets the size that this element computed during the measure pass of the layout process.
/// </summary>
Size DesiredSize { get; }
/// <summary>
/// Gets the width of the element.
/// </summary>
double Width { get; }
/// <summary>
/// Gets the height of the element.
/// </summary>
double Height { get; }
/// <summary>
/// Gets the minimum width of the element.
/// </summary>
double MinWidth { get; }
/// <summary>
/// Gets the maximum width of the element.
/// </summary>
double MaxWidth { get; }
/// <summary>
/// Gets the minimum height of the element.
/// </summary>
double MinHeight { get; }
/// <summary>
/// Gets the maximum height of the element.
/// </summary>
double MaxHeight { get; }
/// <summary>
/// Gets the margin around the element.
/// </summary>
Thickness Margin { get; }
/// <summary>
/// Gets the element's preferred horizontal alignment in its parent.
/// </summary>
HorizontalAlignment HorizontalAlignment { get; }
/// <summary>
/// Gets the element's preferred vertical alignment in its parent.
/// </summary>
VerticalAlignment VerticalAlignment { get; }
/// <summary>
/// Gets a value indicating whether the control's layout measure is valid.
/// </summary>
bool IsMeasureValid { get; }
/// <summary>
/// Gets a value indicating whether the control's layouts arrange is valid.
/// </summary>
bool IsArrangeValid { get; }
/// <summary>
/// Gets the available size passed in the previous layout pass, if any.
/// </summary>
Size? PreviousMeasure { get; }
/// <summary>
/// Gets the layout rect passed in the previous layout pass, if any.
/// </summary>
Rect? PreviousArrange { get; }
/// <summary>
/// Creates the visual children of the control, if necessary
/// </summary>
void ApplyTemplate();
/// <summary>
/// Carries out a measure of the control.
/// </summary>
/// <param name="availableSize">The available size for the control.</param>
void Measure(Size availableSize);
/// <summary>
/// Arranges the control and its children.
/// </summary>
/// <param name="rect">The control's new bounds.</param>
void Arrange(Rect rect);
/// <summary>
/// Invalidates the measurement of the control and queues a new layout pass.
/// </summary>
void InvalidateMeasure();
/// <summary>
/// Invalidates the arrangement of the control and queues a new layout pass.
/// </summary>
void InvalidateArrange();
/// <summary>
/// Called when a child control's desired size changes.
/// </summary>
/// <param name="control">The child control.</param>
void ChildDesiredSizeChanged(ILayoutable control);
/// <summary>
/// Used by the <see cref="LayoutManager"/> to notify the control that its effective
/// viewport is changed.
/// </summary>
/// <param name="e">The viewport information.</param>
void EffectiveViewportChanged(EffectiveViewportChangedEventArgs e);
}
}

4
src/Avalonia.Base/Layout/LayoutContextAdapter.cs

@ -38,8 +38,8 @@ namespace Avalonia.Layout
protected override int ItemCountCore() => _nonVirtualizingContext.Children.Count;
protected override object GetItemAtCore(int index) => _nonVirtualizingContext.Children[index];
protected override ILayoutable GetOrCreateElementAtCore(int index, ElementRealizationOptions options) =>
protected override Layoutable GetOrCreateElementAtCore(int index, ElementRealizationOptions options) =>
_nonVirtualizingContext.Children[index];
protected override void RecycleElementCore(ILayoutable element) { }
protected override void RecycleElementCore(Layoutable element) { }
}
}

43
src/Avalonia.Base/Layout/LayoutHelper.cs

@ -16,15 +16,15 @@ namespace Avalonia.Layout
public static double LayoutEpsilon { get; } = 0.00000153;
/// <summary>
/// Calculates a control's size based on its <see cref="ILayoutable.Width"/>,
/// <see cref="ILayoutable.Height"/>, <see cref="ILayoutable.MinWidth"/>,
/// <see cref="ILayoutable.MaxWidth"/>, <see cref="ILayoutable.MinHeight"/> and
/// <see cref="ILayoutable.MaxHeight"/>.
/// Calculates a control's size based on its <see cref="Layoutable.Width"/>,
/// <see cref="Layoutable.Height"/>, <see cref="Layoutable.MinWidth"/>,
/// <see cref="Layoutable.MaxWidth"/>, <see cref="Layoutable.MinHeight"/> and
/// <see cref="Layoutable.MaxHeight"/>.
/// </summary>
/// <param name="control">The control.</param>
/// <param name="constraints">The space available for the control.</param>
/// <returns>The control's size.</returns>
public static Size ApplyLayoutConstraints(ILayoutable control, Size constraints)
public static Size ApplyLayoutConstraints(Layoutable control, Size constraints)
{
var minmax = new MinMax(control);
@ -33,7 +33,7 @@ namespace Avalonia.Layout
MathUtilities.Clamp(constraints.Height, minmax.MinHeight, minmax.MaxHeight));
}
public static Size MeasureChild(ILayoutable? control, Size availableSize, Thickness padding,
public static Size MeasureChild(Layoutable? control, Size availableSize, Thickness padding,
Thickness borderThickness)
{
if (IsParentLayoutRounded(control, out double scale))
@ -51,7 +51,7 @@ namespace Avalonia.Layout
return new Size().Inflate(padding + borderThickness);
}
public static Size MeasureChild(ILayoutable? control, Size availableSize, Thickness padding)
public static Size MeasureChild(Layoutable? control, Size availableSize, Thickness padding)
{
if (IsParentLayoutRounded(control, out double scale))
{
@ -67,7 +67,7 @@ namespace Avalonia.Layout
return new Size(padding.Left + padding.Right, padding.Bottom + padding.Top);
}
public static Size ArrangeChild(ILayoutable? child, Size availableSize, Thickness padding, Thickness borderThickness)
public static Size ArrangeChild(Layoutable? child, Size availableSize, Thickness padding, Thickness borderThickness)
{
if (IsParentLayoutRounded(child, out double scale))
{
@ -78,7 +78,7 @@ namespace Avalonia.Layout
return ArrangeChildInternal(child, availableSize, padding + borderThickness);
}
public static Size ArrangeChild(ILayoutable? child, Size availableSize, Thickness padding)
public static Size ArrangeChild(Layoutable? child, Size availableSize, Thickness padding)
{
if(IsParentLayoutRounded(child, out double scale))
padding = RoundLayoutThickness(padding, scale, scale);
@ -86,18 +86,18 @@ namespace Avalonia.Layout
return ArrangeChildInternal(child, availableSize, padding);
}
private static Size ArrangeChildInternal(ILayoutable? child, Size availableSize, Thickness padding)
private static Size ArrangeChildInternal(Layoutable? child, Size availableSize, Thickness padding)
{
child?.Arrange(new Rect(availableSize).Deflate(padding));
return availableSize;
}
private static bool IsParentLayoutRounded(ILayoutable? child, out double scale)
private static bool IsParentLayoutRounded(Layoutable? child, out double scale)
{
var layoutableParent = (ILayoutable?)child?.GetVisualParent();
var layoutableParent = (child as Visual)?.GetVisualParent() as Layoutable;
if (layoutableParent == null || !((Layoutable)layoutableParent).UseLayoutRounding)
if (layoutableParent == null || !layoutableParent.UseLayoutRounding)
{
scale = 1.0;
return false;
@ -110,11 +110,11 @@ namespace Avalonia.Layout
/// <summary>
/// Invalidates measure for given control and all visual children recursively.
/// </summary>
public static void InvalidateSelfAndChildrenMeasure(ILayoutable control)
public static void InvalidateSelfAndChildrenMeasure(Layoutable control)
{
void InnerInvalidateMeasure(IVisual target)
void InnerInvalidateMeasure(Visual target)
{
if (target is ILayoutable targetLayoutable)
if (target is Layoutable targetLayoutable)
{
targetLayoutable.InvalidateMeasure();
}
@ -124,13 +124,14 @@ namespace Avalonia.Layout
for (int i = 0; i < visualChildrenCount; i++)
{
IVisual child = visualChildren[i];
Visual child = visualChildren[i];
InnerInvalidateMeasure(child);
}
}
InnerInvalidateMeasure(control);
if (control is Visual v)
InnerInvalidateMeasure(v);
}
/// <summary>
@ -138,9 +139,9 @@ namespace Avalonia.Layout
/// </summary>
/// <param name="control">The control.</param>
/// <exception cref="Exception">Thrown when control has no root or returned layout scaling is invalid.</exception>
public static double GetLayoutScale(ILayoutable control)
public static double GetLayoutScale(Layoutable control)
{
var visualRoot = control.VisualRoot;
var visualRoot = (control as Visual)?.VisualRoot;
var result = (visualRoot as ILayoutRoot)?.LayoutScaling ?? 1.0;
@ -289,7 +290,7 @@ namespace Avalonia.Layout
/// </summary>
private readonly struct MinMax
{
public MinMax(ILayoutable e)
public MinMax(Layoutable e)
{
MaxHeight = e.MaxHeight;
MinHeight = e.MinHeight;

38
src/Avalonia.Base/Layout/LayoutManager.cs

@ -16,9 +16,9 @@ namespace Avalonia.Layout
public class LayoutManager : ILayoutManager, IDisposable
{
private const int MaxPasses = 3;
private readonly ILayoutRoot _owner;
private readonly LayoutQueue<ILayoutable> _toMeasure = new LayoutQueue<ILayoutable>(v => !v.IsMeasureValid);
private readonly LayoutQueue<ILayoutable> _toArrange = new LayoutQueue<ILayoutable>(v => !v.IsArrangeValid);
private readonly Layoutable _owner;
private readonly LayoutQueue<Layoutable> _toMeasure = new LayoutQueue<Layoutable>(v => !v.IsMeasureValid);
private readonly LayoutQueue<Layoutable> _toArrange = new LayoutQueue<Layoutable>(v => !v.IsArrangeValid);
private readonly Action _executeLayoutPass;
private List<EffectiveViewportChangedListener>? _effectiveViewportChangedListeners;
private bool _disposed;
@ -27,14 +27,14 @@ namespace Avalonia.Layout
public LayoutManager(ILayoutRoot owner)
{
_owner = owner ?? throw new ArgumentNullException(nameof(owner));
_owner = owner as Layoutable ?? throw new ArgumentNullException(nameof(owner));
_executeLayoutPass = ExecuteQueuedLayoutPass;
}
public virtual event EventHandler? LayoutUpdated;
/// <inheritdoc/>
public virtual void InvalidateMeasure(ILayoutable control)
public virtual void InvalidateMeasure(Layoutable control)
{
control = control ?? throw new ArgumentNullException(nameof(control));
Dispatcher.UIThread.VerifyAccess();
@ -65,7 +65,7 @@ namespace Avalonia.Layout
}
/// <inheritdoc/>
public virtual void InvalidateArrange(ILayoutable control)
public virtual void InvalidateArrange(Layoutable control)
{
control = control ?? throw new ArgumentNullException(nameof(control));
Dispatcher.UIThread.VerifyAccess();
@ -203,7 +203,7 @@ namespace Avalonia.Layout
_toArrange.Dispose();
}
void ILayoutManager.RegisterEffectiveViewportListener(ILayoutable control)
void ILayoutManager.RegisterEffectiveViewportListener(Layoutable control)
{
_effectiveViewportChangedListeners ??= new List<EffectiveViewportChangedListener>();
_effectiveViewportChangedListeners.Add(new EffectiveViewportChangedListener(
@ -211,7 +211,7 @@ namespace Avalonia.Layout
CalculateEffectiveViewport(control)));
}
void ILayoutManager.UnregisterEffectiveViewportListener(ILayoutable control)
void ILayoutManager.UnregisterEffectiveViewportListener(Layoutable control)
{
if (_effectiveViewportChangedListeners is object)
{
@ -265,13 +265,13 @@ namespace Avalonia.Layout
}
}
private void Measure(ILayoutable control)
private void Measure(Layoutable control)
{
// Controls closest to the visual root need to be arranged first. We don't try to store
// ordered invalidation lists, instead we traverse the tree upwards, measuring the
// controls closest to the root first. This has been shown by benchmarks to be the
// fastest and most memory-efficient algorithm.
if (control.VisualParent is ILayoutable parent)
if (control.VisualParent is Layoutable parent)
{
Measure(parent);
}
@ -283,7 +283,7 @@ namespace Avalonia.Layout
{
if (control is ILayoutRoot root)
{
root.Measure(Size.Infinity);
control.Measure(Size.Infinity);
}
else if (control.PreviousMeasure.HasValue)
{
@ -292,9 +292,9 @@ namespace Avalonia.Layout
}
}
private void Arrange(ILayoutable control)
private void Arrange(Layoutable control)
{
if (control.VisualParent is ILayoutable parent)
if (control.VisualParent is Layoutable parent)
{
Arrange(parent);
}
@ -304,7 +304,7 @@ namespace Avalonia.Layout
if (control is IEmbeddedLayoutRoot embeddedRoot)
control.Arrange(new Rect(embeddedRoot.AllocatedSize));
else if (control is ILayoutRoot root)
control.Arrange(new Rect(root.DesiredSize));
control.Arrange(new Rect(control.DesiredSize));
else if (control.PreviousArrange != null)
{
// Has been observed that PreviousArrange sometimes is null, probably a bug somewhere else.
@ -350,7 +350,7 @@ namespace Avalonia.Layout
if (viewport != l.Viewport)
{
l.Listener.EffectiveViewportChanged(new EffectiveViewportChangedEventArgs(viewport));
l.Listener.RaiseEffectiveViewportChanged(new EffectiveViewportChangedEventArgs(viewport));
l.Viewport = viewport;
}
}
@ -364,14 +364,14 @@ namespace Avalonia.Layout
return startCount != _toMeasure.Count + _toArrange.Count;
}
private Rect CalculateEffectiveViewport(IVisual control)
private Rect CalculateEffectiveViewport(Visual control)
{
var viewport = new Rect(0, 0, double.PositiveInfinity, double.PositiveInfinity);
CalculateEffectiveViewport(control, control, ref viewport);
return viewport;
}
private void CalculateEffectiveViewport(IVisual target, IVisual control, ref Rect viewport)
private void CalculateEffectiveViewport(Visual target, Visual control, ref Rect viewport)
{
// Recurse until the top level control.
if (control.VisualParent is object)
@ -405,13 +405,13 @@ namespace Avalonia.Layout
private class EffectiveViewportChangedListener
{
public EffectiveViewportChangedListener(ILayoutable listener, Rect viewport)
public EffectiveViewportChangedListener(Layoutable listener, Rect viewport)
{
Listener = listener;
Viewport = viewport;
}
public ILayoutable Listener { get; }
public Layoutable Listener { get; }
public Rect Viewport { get; set; }
}
}

35
src/Avalonia.Base/Layout/Layoutable.cs

@ -62,7 +62,7 @@ namespace Avalonia.Layout
/// <summary>
/// Implements layout-related functionality for a control.
/// </summary>
public class Layoutable : Visual, ILayoutable
public class Layoutable : Visual
{
/// <summary>
/// Defines the <see cref="DesiredSize"/> property.
@ -323,15 +323,12 @@ namespace Avalonia.Layout
set { SetValue(UseLayoutRoundingProperty, value); }
}
/// <summary>
/// Gets the available size passed in the previous layout pass, if any.
/// </summary>
Size? ILayoutable.PreviousMeasure => _previousMeasure;
internal Size? PreviousMeasure => _previousMeasure;
/// <summary>
/// Gets the layout rect passed in the previous layout pass, if any.
/// </summary>
Rect? ILayoutable.PreviousArrange => _previousArrange;
internal Rect? PreviousArrange => _previousArrange;
/// <summary>
/// Creates the visual children of the control, if necessary
@ -380,7 +377,7 @@ namespace Avalonia.Layout
if (DesiredSize != previousDesiredSize)
{
this.GetVisualParent<ILayoutable>()?.ChildDesiredSizeChanged(this);
this.GetVisualParent<Layoutable>()?.ChildDesiredSizeChanged(this);
}
}
}
@ -423,7 +420,7 @@ namespace Avalonia.Layout
IsMeasureValid = false;
IsArrangeValid = false;
if (((ILayoutable)this).IsAttachedToVisualTree)
if (IsAttachedToVisualTree)
{
(VisualRoot as ILayoutRoot)?.LayoutManager.InvalidateMeasure(this);
InvalidateVisual();
@ -448,7 +445,7 @@ namespace Avalonia.Layout
}
/// <inheritdoc/>
void ILayoutable.ChildDesiredSizeChanged(ILayoutable control)
internal void ChildDesiredSizeChanged(Layoutable control)
{
if (!_measuring)
{
@ -456,11 +453,11 @@ namespace Avalonia.Layout
}
}
void ILayoutable.EffectiveViewportChanged(EffectiveViewportChangedEventArgs e)
internal void RaiseEffectiveViewportChanged(EffectiveViewportChangedEventArgs e)
{
_effectiveViewportChanged?.Invoke(this, e);
}
/// <summary>
/// Marks a property as affecting the control's measurement.
/// </summary>
@ -471,7 +468,7 @@ namespace Avalonia.Layout
/// property will cause <see cref="InvalidateMeasure"/> to be called on the element.
/// </remarks>
protected static void AffectsMeasure<T>(params AvaloniaProperty[] properties)
where T : class, ILayoutable
where T : Layoutable
{
void Invalidate(AvaloniaPropertyChangedEventArgs e)
{
@ -494,7 +491,7 @@ namespace Avalonia.Layout
/// property will cause <see cref="InvalidateArrange"/> to be called on the element.
/// </remarks>
protected static void AffectsArrange<T>(params AvaloniaProperty[] properties)
where T : class, ILayoutable
where T : Layoutable
{
void Invalidate(AvaloniaPropertyChangedEventArgs e)
{
@ -596,9 +593,9 @@ namespace Avalonia.Layout
for (var i = 0; i < visualCount; i++)
{
IVisual visual = visualChildren[i];
Visual visual = visualChildren[i];
if (visual is ILayoutable layoutable)
if (visual is Layoutable layoutable)
{
layoutable.Measure(availableSize);
width = Math.Max(width, layoutable.DesiredSize.Width);
@ -709,9 +706,9 @@ namespace Avalonia.Layout
for (var i = 0; i < visualCount; i++)
{
IVisual visual = visualChildren[i];
Visual visual = visualChildren[i];
if (visual is ILayoutable layoutable)
if (visual is Layoutable layoutable)
{
layoutable.Arrange(arrangeRect);
}
@ -778,7 +775,7 @@ namespace Avalonia.Layout
DesiredSize = default;
// All changes to visibility cause the parent element to be notified.
this.GetVisualParent<ILayoutable>()?.ChildDesiredSizeChanged(this);
this.GetVisualParent<Layoutable>()?.ChildDesiredSizeChanged(this);
// We only invalidate outselves when visibility is changed to true.
if (change.GetNewValue<bool>())
@ -789,7 +786,7 @@ namespace Avalonia.Layout
}
/// <inheritdoc/>
protected sealed override void OnVisualParentChanged(IVisual? oldParent, IVisual? newParent)
protected sealed override void OnVisualParentChanged(Visual? oldParent, Visual? newParent)
{
LayoutHelper.InvalidateSelfAndChildrenMeasure(this);

4
src/Avalonia.Base/Layout/NonVirtualizingLayoutContext.cs

@ -17,13 +17,13 @@ namespace Avalonia.Layout
/// <summary>
/// Gets the collection of child controls from the container that provides the context.
/// </summary>
public IReadOnlyList<ILayoutable> Children => ChildrenCore;
public IReadOnlyList<Layoutable> Children => ChildrenCore;
/// <summary>
/// Implements the behavior for getting the return value of <see cref="Children"/> in a
/// derived or custom <see cref="NonVirtualizingLayoutContext"/>.
/// </summary>
protected abstract IReadOnlyList<ILayoutable> ChildrenCore { get; }
protected abstract IReadOnlyList<Layoutable> ChildrenCore { get; }
internal VirtualizingLayoutContext GetVirtualizingContextAdapter() =>
_contextAdapter ??= new LayoutContextAdapter(this);

8
src/Avalonia.Base/Layout/NonVirtualizingStackLayout.cs

@ -59,7 +59,7 @@ namespace Avalonia.Layout
{
var element = context.Children[i];
if (!element.IsVisible)
if ((element as Visual)?.IsVisible == false)
{
continue;
}
@ -100,7 +100,7 @@ namespace Avalonia.Layout
{
var element = context.Children[i];
if (!element.IsVisible)
if ((element as Visual)?.IsVisible == false)
{
continue;
}
@ -117,7 +117,7 @@ namespace Avalonia.Layout
Math.Max(finalSize.Height, bounds.Height));
}
private static Rect LayoutVertical(ILayoutable element, double y, Size constraint)
private static Rect LayoutVertical(Layoutable element, double y, Size constraint)
{
var x = 0.0;
var width = element.DesiredSize.Width;
@ -138,7 +138,7 @@ namespace Avalonia.Layout
return new Rect(x, y, width, element.DesiredSize.Height);
}
private static Rect LayoutHorizontal(ILayoutable element, double x, Size constraint)
private static Rect LayoutHorizontal(Layoutable element, double x, Size constraint)
{
var y = 0.0;
var height = element.DesiredSize.Height;

12
src/Avalonia.Base/Layout/StackLayout.cs

@ -78,10 +78,10 @@ namespace Avalonia.Layout
internal Rect GetExtent(
Size availableSize,
VirtualizingLayoutContext context,
ILayoutable? firstRealized,
Layoutable? firstRealized,
int firstRealizedItemIndex,
Rect firstRealizedLayoutBounds,
ILayoutable? lastRealized,
Layoutable? lastRealized,
int lastRealizedItemIndex,
Rect lastRealizedLayoutBounds)
{
@ -121,7 +121,7 @@ namespace Avalonia.Layout
}
internal void OnElementMeasured(
ILayoutable element,
Layoutable element,
int index,
Size availableSize,
Size measureSize,
@ -188,10 +188,10 @@ namespace Avalonia.Layout
Rect IFlowLayoutAlgorithmDelegates.Algorithm_GetExtent(
Size availableSize,
VirtualizingLayoutContext context,
ILayoutable? firstRealized,
Layoutable? firstRealized,
int firstRealizedItemIndex,
Rect firstRealizedLayoutBounds,
ILayoutable? lastRealized,
Layoutable? lastRealized,
int lastRealizedItemIndex,
Rect lastRealizedLayoutBounds)
{
@ -206,7 +206,7 @@ namespace Avalonia.Layout
lastRealizedLayoutBounds);
}
void IFlowLayoutAlgorithmDelegates.Algorithm_OnElementMeasured(ILayoutable element, int index, Size availableSize, Size measureSize, Size desiredSize, Size provisionalArrangeSize, VirtualizingLayoutContext context)
void IFlowLayoutAlgorithmDelegates.Algorithm_OnElementMeasured(Layoutable element, int index, Size availableSize, Size measureSize, Size desiredSize, Size provisionalArrangeSize, VirtualizingLayoutContext context)
{
OnElementMeasured(
element,

6
src/Avalonia.Base/Layout/UniformGridLayout.cs

@ -338,10 +338,10 @@ namespace Avalonia.Layout
Rect IFlowLayoutAlgorithmDelegates.Algorithm_GetExtent(
Size availableSize,
VirtualizingLayoutContext context,
ILayoutable? firstRealized,
Layoutable? firstRealized,
int firstRealizedItemIndex,
Rect firstRealizedLayoutBounds,
ILayoutable? lastRealized,
Layoutable? lastRealized,
int lastRealizedItemIndex,
Rect lastRealizedLayoutBounds)
{
@ -391,7 +391,7 @@ namespace Avalonia.Layout
return extent;
}
void IFlowLayoutAlgorithmDelegates.Algorithm_OnElementMeasured(ILayoutable element, int index, Size availableSize, Size measureSize, Size desiredSize, Size provisionalArrangeSize, VirtualizingLayoutContext context)
void IFlowLayoutAlgorithmDelegates.Algorithm_OnElementMeasured(Layoutable element, int index, Size availableSize, Size measureSize, Size desiredSize, Size provisionalArrangeSize, VirtualizingLayoutContext context)
{
}

4
src/Avalonia.Base/Layout/UniformGridLayoutState.cs

@ -18,7 +18,7 @@ namespace Avalonia.Layout
// If it does not, then we need to do context.GetElement(0) at which point we have requested an element and are on point to clear it.
// If we are responsible for clearing element 0 we keep m_cachedFirstElement valid.
// If we are not (because FlowLayoutAlgorithm is holding it for us) then we just null out this field and use the one from FlowLayoutAlgorithm.
private ILayoutable? _cachedFirstElement;
private Layoutable? _cachedFirstElement;
internal FlowLayoutAlgorithm FlowAlgorithm { get; } = new FlowLayoutAlgorithm();
internal double EffectiveItemWidth { get; private set; }
@ -91,7 +91,7 @@ namespace Avalonia.Layout
}
private void SetSize(
ILayoutable element,
Layoutable element,
double layoutItemWidth,
double layoutItemHeight,
Size availableSize,

8
src/Avalonia.Base/Layout/VirtualLayoutContextAdapter.cs

@ -19,18 +19,18 @@ namespace Avalonia.Layout
set => _virtualizingContext.LayoutState = value;
}
protected override IReadOnlyList<ILayoutable> ChildrenCore =>
protected override IReadOnlyList<Layoutable> ChildrenCore =>
_children ?? (_children = new ChildrenCollection(_virtualizingContext));
private class ChildrenCollection : IReadOnlyList<ILayoutable>
private class ChildrenCollection : IReadOnlyList<Layoutable>
{
private readonly VirtualizingLayoutContext _context;
public ChildrenCollection(VirtualizingLayoutContext context) => _context = context;
public ILayoutable this[int index] => _context.GetOrCreateElementAt(index);
public Layoutable this[int index] => _context.GetOrCreateElementAt(index);
public int Count => _context.ItemCount;
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerator<ILayoutable> GetEnumerator()
public IEnumerator<Layoutable> GetEnumerator()
{
for (var i = 0; i < Count; ++i)
{

12
src/Avalonia.Base/Layout/VirtualizingLayoutContext.cs

@ -116,7 +116,7 @@ namespace Avalonia.Layout
/// This method calls <see cref="GetOrCreateElementAtCore(int, ElementRealizationOptions)"/>
/// with options set to None. GetElementAtCore must be implemented in a derived class.
/// </remarks>
public ILayoutable GetOrCreateElementAt(int index)
public Layoutable GetOrCreateElementAt(int index)
=> GetOrCreateElementAtCore(index, ElementRealizationOptions.None);
/// <summary>
@ -140,7 +140,7 @@ namespace Avalonia.Layout
/// advanced layouts that choose to explicitly manage the realization and recycling of
/// elements as a performance optimization.
/// </remarks>
public ILayoutable GetOrCreateElementAt(int index, ElementRealizationOptions options)
public Layoutable GetOrCreateElementAt(int index, ElementRealizationOptions options)
=> GetOrCreateElementAtCore(index, options);
/// <summary>
@ -148,10 +148,10 @@ namespace Avalonia.Layout
/// </summary>
/// <param name="element">The element to clear.</param>
/// <remarks>
/// This method calls <see cref="RecycleElementCore(ILayoutable)"/>, which must be implemented
/// This method calls <see cref="RecycleElementCore(Layoutable)"/>, which must be implemented
/// in a derived class.
/// </remarks>
public void RecycleElement(ILayoutable element) => RecycleElementCore(element);
public void RecycleElement(Layoutable element) => RecycleElementCore(element);
/// <summary>
/// When implemented in a derived class, retrieves the number of items in the data.
@ -180,14 +180,14 @@ namespace Avalonia.Layout
/// A value of <see cref="ElementRealizationOptions"/> that specifies whether to suppress
/// automatic recycling of the retrieved element or force creation of a new element.
/// </param>
protected abstract ILayoutable GetOrCreateElementAtCore(int index, ElementRealizationOptions options);
protected abstract Layoutable GetOrCreateElementAtCore(int index, ElementRealizationOptions options);
/// <summary>
/// When implemented in a derived class, clears the specified UIElement and allows it to be
/// either re-used or released.
/// </summary>
/// <param name="element">The element to clear.</param>
protected abstract void RecycleElementCore(ILayoutable element);
protected abstract void RecycleElementCore(Layoutable element);
internal NonVirtualizingLayoutContext GetNonVirtualizingContextAdapter() =>
_contextAdapter ?? (_contextAdapter = new VirtualLayoutContextAdapter(this));

2
src/Avalonia.Base/Layout/WrapLayout/WrapItem.cs

@ -20,6 +20,6 @@ namespace Avalonia.Layout
public UvMeasure? Position { get; internal set; }
public ILayoutable? Element { get; internal set; }
public Layoutable? Element { get; internal set; }
}
}

4
src/Avalonia.Base/Media/IVisualBrush.cs

@ -4,7 +4,7 @@ using Avalonia.VisualTree;
namespace Avalonia.Media
{
/// <summary>
/// Paints an area with an <see cref="IVisual"/>.
/// Paints an area with an <see cref="Visual"/>.
/// </summary>
[NotClientImplementable]
public interface IVisualBrush : ITileBrush
@ -12,6 +12,6 @@ namespace Avalonia.Media
/// <summary>
/// Gets the visual to draw.
/// </summary>
IVisual Visual { get; }
Visual Visual { get; }
}
}

2
src/Avalonia.Base/Media/Imaging/RenderTargetBitmap.cs

@ -44,7 +44,7 @@ namespace Avalonia.Media.Imaging
/// Renders a visual to the <see cref="RenderTargetBitmap"/>.
/// </summary>
/// <param name="visual">The visual to render.</param>
public void Render(IVisual visual) => ImmediateRenderer.Render(visual, this);
public void Render(Visual visual) => ImmediateRenderer.Render(visual, this);
/// <summary>
/// Creates a platform-specific implementation for a <see cref="RenderTargetBitmap"/>.

4
src/Avalonia.Base/Media/Immutable/ImmutableVisualBrush.cs

@ -25,7 +25,7 @@ namespace Avalonia.Media.Immutable
/// <param name="tileMode">The tile mode.</param>
/// <param name="bitmapInterpolationMode">Controls the quality of interpolation.</param>
public ImmutableVisualBrush(
IVisual visual,
Visual visual,
AlignmentX alignmentX = AlignmentX.Center,
AlignmentY alignmentY = AlignmentY.Center,
RelativeRect? destinationRect = null,
@ -62,6 +62,6 @@ namespace Avalonia.Media.Immutable
}
/// <inheritdoc/>
public IVisual Visual { get; }
public Visual Visual { get; }
}
}

10
src/Avalonia.Base/Media/VisualBrush.cs

@ -4,15 +4,15 @@ using Avalonia.VisualTree;
namespace Avalonia.Media
{
/// <summary>
/// Paints an area with an <see cref="IVisual"/>.
/// Paints an area with an <see cref="Visual"/>.
/// </summary>
public class VisualBrush : TileBrush, IVisualBrush
{
/// <summary>
/// Defines the <see cref="Visual"/> property.
/// </summary>
public static readonly StyledProperty<IVisual> VisualProperty =
AvaloniaProperty.Register<VisualBrush, IVisual>(nameof(Visual));
public static readonly StyledProperty<Visual> VisualProperty =
AvaloniaProperty.Register<VisualBrush, Visual>(nameof(Visual));
static VisualBrush()
{
@ -30,7 +30,7 @@ namespace Avalonia.Media
/// Initializes a new instance of the <see cref="VisualBrush"/> class.
/// </summary>
/// <param name="visual">The visual to draw.</param>
public VisualBrush(IVisual visual)
public VisualBrush(Visual visual)
{
Visual = visual;
}
@ -38,7 +38,7 @@ namespace Avalonia.Media
/// <summary>
/// Gets or sets the visual to draw.
/// </summary>
public IVisual Visual
public Visual Visual
{
get { return GetValue(VisualProperty); }
set { SetValue(VisualProperty, value); }

2
src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs

@ -262,7 +262,7 @@ namespace Avalonia.PropertyStore
private class UncommonFields
{
public Func<IAvaloniaObject, T, T>? _coerce;
public Func<AvaloniaObject, T, T>? _coerce;
public T? _uncoercedValue;
public T? _uncoercedBaseValue;
}

12
src/Avalonia.Base/PropertyStore/UntypedValueUtils.cs

@ -23,18 +23,6 @@ namespace Avalonia.PropertyStore
return v;
}
public static bool TryConvertAndValidate(
AvaloniaProperty property,
object? value,
out object? result)
{
if (TypeUtilities.TryConvertImplicit(property.PropertyType, value, out result))
return ((IStyledPropertyAccessor)property).ValidateValue(result);
result = default;
return false;
}
public static bool TryConvertAndValidate<T>(
StyledPropertyBase<T> property,
object? value,

6
src/Avalonia.Base/Reactive/AvaloniaPropertyBindingObservable.cs

@ -6,15 +6,15 @@ namespace Avalonia.Reactive
{
internal class AvaloniaPropertyBindingObservable<T> : LightweightObservableBase<BindingValue<T>>, IDescription
{
private readonly WeakReference<IAvaloniaObject> _target;
private readonly WeakReference<AvaloniaObject> _target;
private readonly AvaloniaProperty _property;
private BindingValue<T> _value = BindingValue<T>.Unset;
public AvaloniaPropertyBindingObservable(
IAvaloniaObject target,
AvaloniaObject target,
AvaloniaProperty property)
{
_target = new WeakReference<IAvaloniaObject>(target);
_target = new WeakReference<AvaloniaObject>(target);
_property = property;
}

6
src/Avalonia.Base/Reactive/AvaloniaPropertyChangedObservable.cs

@ -6,14 +6,14 @@ namespace Avalonia.Reactive
LightweightObservableBase<AvaloniaPropertyChangedEventArgs>,
IDescription
{
private readonly WeakReference<IAvaloniaObject> _target;
private readonly WeakReference<AvaloniaObject> _target;
private readonly AvaloniaProperty _property;
public AvaloniaPropertyChangedObservable(
IAvaloniaObject target,
AvaloniaObject target,
AvaloniaProperty property)
{
_target = new WeakReference<IAvaloniaObject>(target);
_target = new WeakReference<AvaloniaObject>(target);
_property = property;
}

36
src/Avalonia.Base/Reactive/AvaloniaPropertyObservable.cs

@ -6,15 +6,15 @@ namespace Avalonia.Reactive
{
internal class AvaloniaPropertyObservable<T> : LightweightObservableBase<T>, IDescription
{
private readonly WeakReference<IAvaloniaObject> _target;
private readonly WeakReference<AvaloniaObject> _target;
private readonly AvaloniaProperty _property;
private Optional<T> _value;
public AvaloniaPropertyObservable(
IAvaloniaObject target,
AvaloniaObject target,
AvaloniaProperty property)
{
_target = new WeakReference<IAvaloniaObject>(target);
_target = new WeakReference<AvaloniaObject>(target);
_property = property;
}
@ -49,31 +49,23 @@ namespace Avalonia.Reactive
{
if (e.Property == _property)
{
if (e.Sender is AvaloniaObject ao)
{
T newValue;
if (e is AvaloniaPropertyChangedEventArgs<T> typed)
{
newValue = AvaloniaObjectExtensions.GetValue(ao, typed.Property);
}
else
{
newValue = (T)e.Sender.GetValue(e.Property)!;
}
T newValue;
if (!_value.HasValue ||
!EqualityComparer<T>.Default.Equals(newValue, _value.Value))
{
_value = newValue;
PublishNext(_value.Value!);
}
if (e is AvaloniaPropertyChangedEventArgs<T> typed)
{
newValue = AvaloniaObjectExtensions.GetValue(e.Sender, typed.Property);
}
else
{
throw new NotSupportedException("Custom implementations of IAvaloniaObject not supported.");
newValue = (T)e.Sender.GetValue(e.Property)!;
}
if (!_value.HasValue ||
!EqualityComparer<T>.Default.Equals(newValue, _value.Value))
{
_value = newValue;
PublishNext(_value.Value!);
}
}
}
}

14
src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs

@ -45,7 +45,7 @@ public class CompositingRenderer : IRendererWithCompositor
_compositor = compositor;
_recordingContext = new DrawingContext(_recorder);
CompositionTarget = compositor.CreateCompositionTarget(root.CreateRenderTarget);
CompositionTarget.Root = ((Visual)root!.VisualRoot!).AttachToCompositor(compositor);
CompositionTarget.Root = ((Visual)root).AttachToCompositor(compositor);
_update = Update;
}
@ -75,7 +75,7 @@ public class CompositingRenderer : IRendererWithCompositor
}
/// <inheritdoc/>
public void AddDirty(IVisual visual)
public void AddDirty(Visual visual)
{
if (_updating)
throw new InvalidOperationException("Visual was invalidated during the render pass");
@ -84,7 +84,7 @@ public class CompositingRenderer : IRendererWithCompositor
}
/// <inheritdoc/>
public IEnumerable<IVisual> HitTest(Point p, IVisual root, Func<IVisual, bool>? filter)
public IEnumerable<Visual> HitTest(Point p, Visual root, Func<Visual, bool>? filter)
{
Func<CompositionVisual, bool>? f = null;
if (filter != null)
@ -109,14 +109,14 @@ public class CompositingRenderer : IRendererWithCompositor
}
/// <inheritdoc/>
public IVisual? HitTestFirst(Point p, IVisual root, Func<IVisual, bool>? filter)
public Visual? HitTestFirst(Point p, Visual root, Func<Visual, bool>? filter)
{
// TODO: Optimize
return HitTest(p, root, filter).FirstOrDefault();
}
/// <inheritdoc/>
public void RecalculateChildren(IVisual visual)
public void RecalculateChildren(Visual visual)
{
if (_updating)
throw new InvalidOperationException("Visual was invalidated during the render pass");
@ -130,9 +130,9 @@ public class CompositingRenderer : IRendererWithCompositor
if(v.CompositionVisual == null)
return;
var compositionChildren = v.CompositionVisual.Children;
var visualChildren = (AvaloniaList<IVisual>)v.GetVisualChildren();
var visualChildren = (AvaloniaList<Visual>)v.GetVisualChildren();
PooledList<(IVisual visual, int index)>? sortedChildren = null;
PooledList<(Visual visual, int index)>? sortedChildren = null;
if (v.HasNonUniformZIndexChildren && visualChildren.Count > 1)
{
sortedChildren = new (visualChildren.Count);

22
src/Avalonia.Base/Rendering/DeferredRenderer.cs

@ -22,14 +22,14 @@ namespace Avalonia.Rendering
{
private readonly IDispatcher? _dispatcher;
private readonly IRenderLoop? _renderLoop;
private readonly IVisual _root;
private readonly Visual _root;
private readonly ISceneBuilder _sceneBuilder;
private bool _running;
private bool _disposed;
private volatile IRef<Scene>? _scene;
private DirtyVisuals? _dirty;
private HashSet<IVisual>? _recalculateChildren;
private HashSet<Visual>? _recalculateChildren;
private IRef<IRenderTargetBitmapImpl>? _overlay;
private int _lastSceneId = -1;
private DisplayDirtyRects _dirtyRectsDisplay = new DisplayDirtyRects();
@ -56,7 +56,7 @@ namespace Avalonia.Rendering
IDeferredRendererLock? rendererLock = null) : base(true)
{
_dispatcher = dispatcher ?? Dispatcher.UIThread;
_root = root ?? throw new ArgumentNullException(nameof(root));
_root = root as Visual ?? throw new ArgumentNullException(nameof(root));
_sceneBuilder = sceneBuilder ?? new SceneBuilder();
Layers = new RenderLayers();
_renderLoop = renderLoop;
@ -74,7 +74,7 @@ namespace Avalonia.Rendering
/// This constructor is intended to be used for unit testing.
/// </remarks>
public DeferredRenderer(
IVisual root,
Visual root,
IRenderTarget renderTarget,
ISceneBuilder? sceneBuilder = null) : base(true)
{
@ -116,7 +116,7 @@ namespace Avalonia.Rendering
internal IRenderTarget? RenderTarget { get; private set; }
/// <inheritdoc/>
public void AddDirty(IVisual visual)
public void AddDirty(Visual visual)
{
_dirty?.Add(visual);
}
@ -142,7 +142,7 @@ namespace Avalonia.Rendering
DisposeRenderTarget();
}
public void RecalculateChildren(IVisual visual) => _recalculateChildren?.Add(visual);
public void RecalculateChildren(Visual visual) => _recalculateChildren?.Add(visual);
void DisposeRenderTarget()
{
@ -163,17 +163,17 @@ namespace Avalonia.Rendering
}
/// <inheritdoc/>
public IEnumerable<IVisual> HitTest(Point p, IVisual root, Func<IVisual, bool>? filter)
public IEnumerable<Visual> HitTest(Point p, Visual root, Func<Visual, bool>? filter)
{
EnsureCanHitTest();
//It's safe to access _scene here without a lock since
//it's only changed from UI thread which we are currently on
return _scene?.Item.HitTest(p, root, filter) ?? Enumerable.Empty<IVisual>();
return _scene?.Item.HitTest(p, root, filter) ?? Enumerable.Empty<Visual>();
}
/// <inheritdoc/>
public IVisual? HitTestFirst(Point p, IVisual root, Func<IVisual, bool>? filter)
public Visual? HitTestFirst(Point p, Visual root, Func<Visual, bool>? filter)
{
EnsureCanHitTest();
@ -414,7 +414,7 @@ namespace Avalonia.Rendering
}
private void Render(IDrawingContextImpl context, VisualNode node, IVisual? layer, Rect clipBounds)
private void Render(IDrawingContextImpl context, VisualNode node, Visual? layer, Rect clipBounds)
{
if (layer == null || node.LayerRoot == layer)
{
@ -643,7 +643,7 @@ namespace Avalonia.Rendering
if (_dirty == null)
{
_dirty = new DirtyVisuals();
_recalculateChildren = new HashSet<IVisual>();
_recalculateChildren = new HashSet<Visual>();
_sceneBuilder.UpdateAll(scene);
}
else

14
src/Avalonia.Base/Rendering/DirtyVisuals.cs

@ -13,10 +13,10 @@ namespace Avalonia.Rendering
/// visual. TODO: We probably want to put an upper limit on the number of visuals that can be
/// stored and if we reach that limit, assume all visuals are dirty.
/// </remarks>
internal class DirtyVisuals : IEnumerable<IVisual>
internal class DirtyVisuals : IEnumerable<Visual>
{
private SortedDictionary<int, List<IVisual>> _inner = new SortedDictionary<int, List<IVisual>>();
private Dictionary<IVisual, int> _index = new Dictionary<IVisual, int>();
private SortedDictionary<int, List<Visual>> _inner = new SortedDictionary<int, List<Visual>>();
private Dictionary<Visual, int> _index = new Dictionary<Visual, int>();
private int _enumerating;
/// <summary>
@ -28,14 +28,14 @@ namespace Avalonia.Rendering
/// Adds a visual to the dirty list.
/// </summary>
/// <param name="visual">The dirty visual.</param>
public void Add(IVisual visual)
public void Add(Visual visual)
{
if (_enumerating > 0)
{
throw new InvalidOperationException("Visual was invalidated during a render pass");
}
var distance = visual.CalculateDistanceFromAncestor(visual.VisualRoot);
var distance = visual.CalculateDistanceFromAncestor((Visual?)visual.GetVisualRoot());
if (_index.TryGetValue(visual, out var existingDistance))
{
@ -50,7 +50,7 @@ namespace Avalonia.Rendering
if (!_inner.TryGetValue(distance, out var list))
{
list = new List<IVisual>();
list = new List<Visual>();
_inner.Add(distance, list);
}
@ -76,7 +76,7 @@ namespace Avalonia.Rendering
/// Gets the dirty visuals, in ascending order of distance to their root.
/// </summary>
/// <returns>A collection of visuals.</returns>
public IEnumerator<IVisual> GetEnumerator()
public IEnumerator<Visual> GetEnumerator()
{
_enumerating++;
try

4
src/Avalonia.Base/Rendering/ICustomSimpleHitTest.cs

@ -24,10 +24,10 @@ namespace Avalonia.Rendering
public static class CustomSimpleHitTestExtensions
{
public static bool HitTestCustom(this IVisual visual, Point point)
public static bool HitTestCustom(this Visual visual, Point point)
=> (visual as ICustomSimpleHitTest)?.HitTest(point) ?? visual.TransformedBounds?.Contains(point) == true;
public static bool HitTestCustom(this IEnumerable<IVisual> children, Point point)
public static bool HitTestCustom(this IEnumerable<Visual> children, Point point)
=> children.Any(ctrl => ctrl.HitTestCustom(point));
}
}

2
src/Avalonia.Base/Rendering/IRenderRoot.cs

@ -8,7 +8,7 @@ namespace Avalonia.Rendering
/// Represents the root of a renderable tree.
/// </summary>
[NotClientImplementable]
public interface IRenderRoot : IVisual
public interface IRenderRoot
{
/// <summary>
/// Gets the client size of the window.

8
src/Avalonia.Base/Rendering/IRenderer.cs

@ -34,7 +34,7 @@ namespace Avalonia.Rendering
/// Mark a visual as dirty and needing re-rendering.
/// </summary>
/// <param name="visual">The visual.</param>
void AddDirty(IVisual visual);
void AddDirty(Visual visual);
/// <summary>
/// Hit tests a location to find the visuals at the specified point.
@ -46,7 +46,7 @@ namespace Avalonia.Rendering
/// children will be excluded from the results.
/// </param>
/// <returns>The visuals at the specified point, topmost first.</returns>
IEnumerable<IVisual> HitTest(Point p, IVisual root, Func<IVisual, bool> filter);
IEnumerable<Visual> HitTest(Point p, Visual root, Func<Visual, bool> filter);
/// <summary>
/// Hit tests a location to find first visual at the specified point.
@ -58,13 +58,13 @@ namespace Avalonia.Rendering
/// children will be excluded from the results.
/// </param>
/// <returns>The visual at the specified point, topmost first.</returns>
IVisual? HitTestFirst(Point p, IVisual root, Func<IVisual, bool> filter);
Visual? HitTestFirst(Point p, Visual root, Func<Visual, bool> filter);
/// <summary>
/// Informs the renderer that the z-ordering of a visual's children has changed.
/// </summary>
/// <param name="visual">The visual.</param>
void RecalculateChildren(IVisual visual);
void RecalculateChildren(Visual visual);
/// <summary>
/// Called when a resize notification is received by the control being rendered.

38
src/Avalonia.Base/Rendering/ImmediateRenderer.cs

@ -18,7 +18,7 @@ namespace Avalonia.Rendering
/// </remarks>
public class ImmediateRenderer : RendererBase, IRenderer, IVisualBrushRenderer
{
private readonly IVisual _root;
private readonly Visual _root;
private readonly IRenderRoot? _renderRoot;
private bool _updateTransformedBounds = true;
private IRenderTarget? _renderTarget;
@ -27,13 +27,13 @@ namespace Avalonia.Rendering
/// Initializes a new instance of the <see cref="ImmediateRenderer"/> class.
/// </summary>
/// <param name="root">The control to render.</param>
public ImmediateRenderer(IVisual root)
public ImmediateRenderer(Visual root)
{
_root = root ?? throw new ArgumentNullException(nameof(root));
_renderRoot = root as IRenderRoot;
}
private ImmediateRenderer(IVisual root, bool updateTransformedBounds)
private ImmediateRenderer(Visual root, bool updateTransformedBounds)
{
_root = root ?? throw new ArgumentNullException(nameof(root));
_renderRoot = root as IRenderRoot;
@ -102,7 +102,7 @@ namespace Avalonia.Rendering
/// </summary>
/// <param name="visual">The visual.</param>
/// <param name="target">The render target.</param>
public static void Render(IVisual visual, IRenderTarget target)
public static void Render(Visual visual, IRenderTarget target)
{
using (var renderer = new ImmediateRenderer(visual, updateTransformedBounds: false))
using (var context = new DrawingContext(target.CreateDrawingContext(renderer)))
@ -116,7 +116,7 @@ namespace Avalonia.Rendering
/// </summary>
/// <param name="visual">The visual.</param>
/// <param name="context">The drawing context.</param>
public static void Render(IVisual visual, DrawingContext context)
public static void Render(Visual visual, DrawingContext context)
{
using (var renderer = new ImmediateRenderer(visual, updateTransformedBounds: false))
{
@ -125,7 +125,7 @@ namespace Avalonia.Rendering
}
/// <inheritdoc/>
public void AddDirty(IVisual visual)
public void AddDirty(Visual visual)
{
if (visual.Bounds != Rect.Empty)
{
@ -162,18 +162,18 @@ namespace Avalonia.Rendering
}
/// <inheritdoc/>
public IEnumerable<IVisual> HitTest(Point p, IVisual root, Func<IVisual, bool> filter)
public IEnumerable<Visual> HitTest(Point p, Visual root, Func<Visual, bool> filter)
{
return HitTest(root, p, filter);
}
public IVisual? HitTestFirst(Point p, IVisual root, Func<IVisual, bool> filter)
public Visual? HitTestFirst(Point p, Visual root, Func<Visual, bool> filter)
{
return HitTest(root, p, filter).FirstOrDefault();
}
/// <inheritdoc/>
public void RecalculateChildren(IVisual visual) => AddDirty(visual);
public void RecalculateChildren(Visual visual) => AddDirty(visual);
/// <inheritdoc/>
public void Start()
@ -199,21 +199,21 @@ namespace Avalonia.Rendering
Render(new DrawingContext(context), visual, visual.Bounds);
}
internal static void Render(IVisual visual, DrawingContext context, bool updateTransformedBounds)
internal static void Render(Visual visual, DrawingContext context, bool updateTransformedBounds)
{
using var renderer = new ImmediateRenderer(visual, updateTransformedBounds);
renderer.Render(context, visual, visual.Bounds);
}
private static void ClearTransformedBounds(IVisual visual)
private static void ClearTransformedBounds(Visual visual)
{
foreach (var e in visual.GetSelfAndVisualDescendants())
{
visual.TransformedBounds = null;
visual.SetTransformedBounds(null);
}
}
private static Rect GetTransformedBounds(IVisual visual)
private static Rect GetTransformedBounds(Visual visual)
{
if (visual.RenderTransform == null)
{
@ -228,10 +228,10 @@ namespace Avalonia.Rendering
}
}
private static IEnumerable<IVisual> HitTest(
IVisual visual,
private static IEnumerable<Visual> HitTest(
Visual visual,
Point p,
Func<IVisual, bool>? filter)
Func<Visual, bool>? filter)
{
_ = visual ?? throw new ArgumentNullException(nameof(visual));
@ -266,7 +266,7 @@ namespace Avalonia.Rendering
}
}
private void Render(DrawingContext context, IVisual visual, Rect clipRect)
private void Render(DrawingContext context, Visual visual, Rect clipRect)
{
var opacity = visual.Opacity;
var clipToBounds = visual.ClipToBounds;
@ -329,11 +329,11 @@ namespace Avalonia.Rendering
#pragma warning restore 0618
if (_updateTransformedBounds)
visual.TransformedBounds = transformed;
visual.SetTransformedBounds(transformed);
var childrenEnumerable = visual.HasNonUniformZIndexChildren
? visual.VisualChildren.OrderBy(x => x, ZIndexComparer.Instance)
: (IEnumerable<IVisual>)visual.VisualChildren;
: (IEnumerable<Visual>)visual.VisualChildren;
foreach (var child in childrenEnumerable)
{

4
src/Avalonia.Base/Rendering/RenderLayer.cs

@ -11,7 +11,7 @@ namespace Avalonia.Rendering
IDrawingContextImpl drawingContext,
Size size,
double scaling,
IVisual layerRoot)
Visual layerRoot)
{
Bitmap = RefCountable.Create(drawingContext.CreateLayer(size));
Size = size;
@ -24,7 +24,7 @@ namespace Avalonia.Rendering
public bool IsEmpty { get; set; }
public double Scaling { get; private set; }
public Size Size { get; private set; }
public IVisual LayerRoot { get; }
public Visual LayerRoot { get; }
public void RecreateBitmap(IDrawingContextImpl drawingContext, Size size, double scaling)
{

7
src/Avalonia.Base/Rendering/RenderLayers.cs

@ -3,17 +3,16 @@ using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Avalonia.Platform;
using Avalonia.Rendering.SceneGraph;
using Avalonia.VisualTree;
namespace Avalonia.Rendering
{
public class RenderLayers : IEnumerable<RenderLayer>
{
private readonly List<RenderLayer> _inner = new List<RenderLayer>();
private readonly Dictionary<IVisual, RenderLayer> _index = new Dictionary<IVisual, RenderLayer>();
private readonly Dictionary<Visual, RenderLayer> _index = new Dictionary<Visual, RenderLayer>();
public int Count => _inner.Count;
public RenderLayer this[IVisual layerRoot] => _index[layerRoot];
public RenderLayer this[Visual layerRoot] => _index[layerRoot];
public void Update(Scene scene, IDrawingContextImpl context)
{
@ -59,7 +58,7 @@ namespace Avalonia.Rendering
_inner.Clear();
}
public bool TryGetValue(IVisual layerRoot, [NotNullWhen(true)] out RenderLayer? value)
public bool TryGetValue(Visual layerRoot, [NotNullWhen(true)] out RenderLayer? value)
{
return _index.TryGetValue(layerRoot, out value);
}

4
src/Avalonia.Base/Rendering/SceneGraph/ISceneBuilder.cs

@ -19,6 +19,6 @@ namespace Avalonia.Rendering.SceneGraph
/// <param name="scene">The scene.</param>
/// <param name="visual">The visual to update.</param>
/// <returns>True if changes were made, otherwise false.</returns>
bool Update(Scene scene, IVisual visual);
bool Update(Scene scene, Visual visual);
}
}
}

5
src/Avalonia.Base/Rendering/SceneGraph/IVisualNode.cs

@ -2,19 +2,18 @@
using System.Collections.Generic;
using Avalonia.Platform;
using Avalonia.Utilities;
using Avalonia.VisualTree;
namespace Avalonia.Rendering.SceneGraph
{
/// <summary>
/// Represents a node in the low-level scene graph representing an <see cref="IVisual"/>.
/// Represents a node in the low-level scene graph representing a <see cref="Visual"/>.
/// </summary>
public interface IVisualNode : IDisposable
{
/// <summary>
/// Gets the visual to which the node relates.
/// </summary>
IVisual Visual { get; }
Visual Visual { get; }
/// <summary>
/// Gets the parent scene graph node.

40
src/Avalonia.Base/Rendering/SceneGraph/Scene.cs

@ -13,24 +13,24 @@ namespace Avalonia.Rendering.SceneGraph
/// </summary>
public class Scene : IDisposable
{
private readonly Dictionary<IVisual, IVisualNode> _index;
private readonly Dictionary<Visual, IVisualNode> _index;
private readonly TaskCompletionSource<bool> _rendered = new TaskCompletionSource<bool>();
/// <summary>
/// Initializes a new instance of the <see cref="Scene"/> class.
/// </summary>
/// <param name="rootVisual">The root visual to draw.</param>
public Scene(IVisual rootVisual)
public Scene(Visual rootVisual)
: this(
new VisualNode(rootVisual, null),
new Dictionary<IVisual, IVisualNode>(),
new Dictionary<Visual, IVisualNode>(),
new SceneLayers(rootVisual),
0)
{
_index.Add(rootVisual, Root);
}
private Scene(VisualNode root, Dictionary<IVisual, IVisualNode> index, SceneLayers layers, int generation)
private Scene(VisualNode root, Dictionary<Visual, IVisualNode> index, SceneLayers layers, int generation)
{
_ = root ?? throw new ArgumentNullException(nameof(root));
@ -87,7 +87,7 @@ namespace Avalonia.Rendering.SceneGraph
/// <returns>The cloned scene.</returns>
public Scene CloneScene()
{
var index = new Dictionary<IVisual, IVisualNode>(_index.Count);
var index = new Dictionary<Visual, IVisualNode>(_index.Count);
var root = Clone((VisualNode)Root, null, index);
var result = new Scene(root, index, Layers.Clone(), Generation + 1)
@ -115,7 +115,7 @@ namespace Avalonia.Rendering.SceneGraph
/// <returns>
/// The node representing the visual or null if it could not be found.
/// </returns>
public IVisualNode? FindNode(IVisual visual)
public IVisualNode? FindNode(Visual visual)
{
_index.TryGetValue(visual, out var node);
return node;
@ -128,10 +128,10 @@ namespace Avalonia.Rendering.SceneGraph
/// <param name="root">The root of the subtree to search.</param>
/// <param name="filter">A filter. May be null.</param>
/// <returns>The visuals at the specified point.</returns>
public IEnumerable<IVisual> HitTest(Point p, IVisual root, Func<IVisual, bool>? filter)
public IEnumerable<Visual> HitTest(Point p, Visual root, Func<Visual, bool>? filter)
{
var node = FindNode(root);
return (node != null) ? new HitTestEnumerable(node, filter, p, Root) : Enumerable.Empty<IVisual>();
return (node != null) ? new HitTestEnumerable(node, filter, p, Root) : Enumerable.Empty<Visual>();
}
/// <summary>
@ -141,7 +141,7 @@ namespace Avalonia.Rendering.SceneGraph
/// <param name="root">The root of the subtree to search.</param>
/// <param name="filter">A filter. May be null.</param>
/// <returns>The visual at the specified point.</returns>
public IVisual? HitTestFirst(Point p, IVisual root, Func<IVisual, bool>? filter)
public Visual? HitTestFirst(Point p, Visual root, Func<Visual, bool>? filter)
{
var node = FindNode(root);
return (node != null) ? HitTestFirst(node, p, filter) : null;
@ -160,7 +160,7 @@ namespace Avalonia.Rendering.SceneGraph
node.Dispose();
}
private VisualNode Clone(VisualNode source, IVisualNode? parent, Dictionary<IVisual, IVisualNode> index)
private VisualNode Clone(VisualNode source, IVisualNode? parent, Dictionary<Visual, IVisualNode> index)
{
var result = source.Clone(parent);
@ -184,7 +184,7 @@ namespace Avalonia.Rendering.SceneGraph
return result;
}
private IVisual HitTestFirst(IVisualNode root, Point p, Func<IVisual, bool>? filter)
private Visual HitTestFirst(IVisualNode root, Point p, Func<Visual, bool>? filter)
{
using var enumerator = new HitTestEnumerator(root, filter, p, Root);
@ -193,14 +193,14 @@ namespace Avalonia.Rendering.SceneGraph
return enumerator.Current;
}
private class HitTestEnumerable : IEnumerable<IVisual>
private class HitTestEnumerable : IEnumerable<Visual>
{
private readonly IVisualNode _root;
private readonly Func<IVisual, bool>? _filter;
private readonly Func<Visual, bool>? _filter;
private readonly IVisualNode _sceneRoot;
private readonly Point _point;
public HitTestEnumerable(IVisualNode root, Func<IVisual, bool>? filter, Point point, IVisualNode sceneRoot)
public HitTestEnumerable(IVisualNode root, Func<Visual, bool>? filter, Point point, IVisualNode sceneRoot)
{
_root = root;
_filter = filter;
@ -208,7 +208,7 @@ namespace Avalonia.Rendering.SceneGraph
_sceneRoot = sceneRoot;
}
public IEnumerator<IVisual> GetEnumerator()
public IEnumerator<Visual> GetEnumerator()
{
return new HitTestEnumerator(_root, _filter, _point, _sceneRoot);
}
@ -219,15 +219,15 @@ namespace Avalonia.Rendering.SceneGraph
}
}
private struct HitTestEnumerator : IEnumerator<IVisual>
private struct HitTestEnumerator : IEnumerator<Visual>
{
private readonly PooledStack<Entry> _nodeStack;
private readonly Func<IVisual, bool>? _filter;
private readonly Func<Visual, bool>? _filter;
private readonly IVisualNode _sceneRoot;
private IVisual? _current;
private Visual? _current;
private readonly Point _point;
public HitTestEnumerator(IVisualNode root, Func<IVisual, bool>? filter, Point point, IVisualNode sceneRoot)
public HitTestEnumerator(IVisualNode root, Func<Visual, bool>? filter, Point point, IVisualNode sceneRoot)
{
_nodeStack = new PooledStack<Entry>();
_nodeStack.Push(new Entry(root, false, null, true));
@ -282,7 +282,7 @@ namespace Avalonia.Rendering.SceneGraph
throw new NotSupportedException();
}
public IVisual Current => _current!;
public Visual Current => _current!;
object IEnumerator.Current => Current;

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save