From b025186af0dd59b867cccbd1c9bb772d561170a8 Mon Sep 17 00:00:00 2001 From: daniel mayost Date: Thu, 17 Mar 2022 09:40:58 +0200 Subject: [PATCH] refactor mirror engine --- src/Avalonia.Controls/Control.cs | 85 ++----------------- src/Avalonia.Visuals/ApiCompatBaseline.txt | 5 +- .../Media/TransformExtensions.cs | 18 ---- src/Avalonia.Visuals/Media/TransformGroup.cs | 18 +--- .../Rendering/SceneGraph/SceneBuilder.cs | 6 ++ src/Avalonia.Visuals/Visual.cs | 23 ++++- src/Avalonia.Visuals/VisualExtensions.cs | 6 ++ src/Avalonia.Visuals/VisualTree/IVisual.cs | 5 ++ 8 files changed, 51 insertions(+), 115 deletions(-) diff --git a/src/Avalonia.Controls/Control.cs b/src/Avalonia.Controls/Control.cs index fd2f28f361..f0d5b342e7 100644 --- a/src/Avalonia.Controls/Control.cs +++ b/src/Avalonia.Controls/Control.cs @@ -67,17 +67,10 @@ namespace Avalonia.Controls /// public static readonly AttachedProperty FlowDirectionProperty = AvaloniaProperty.RegisterAttached(nameof(FlowDirection), inherits: true); - - /// - /// Defines the property. - /// - public static new readonly StyledProperty RenderTransformProperty = - Visual.RenderTransformProperty.AddOwner(); private DataTemplates? _dataTemplates; private IControl? _focusAdorner; private AutomationPeer? _automationPeer; - private bool _hasMirrorTransform; /// /// Gets or sets the control's focus adorner. @@ -133,28 +126,6 @@ namespace Avalonia.Controls set => SetValue(FlowDirectionProperty, value); } - /// - public override ITransform? RenderTransform - { - get => base.RenderTransform; - set - { - if (_hasMirrorTransform) - { - if (value == null) - { - value = MirrorTrasform(); - } - else - { - value = MirrorTrasform().MergeTransforms(value); - } - } - - base.RenderTransform = value; - } - } - /// /// Occurs when the user has completed a context input gesture, such as a right-click. /// @@ -375,13 +346,15 @@ namespace Avalonia.Controls bool shouldApplyMirrorTransform = thisShouldGetMirrored != parentShouldGetMirrored; + if (this is IRenderRoot) shouldApplyMirrorTransform = false; + if (shouldApplyMirrorTransform) { - AddMirrorTransform(); + IsMirrorTransform = true; } else { - RemoveMirrorTransform(); + IsMirrorTransform = false; } } @@ -396,57 +369,15 @@ namespace Avalonia.Controls } } - private void AddMirrorTransform() - { - if (_hasMirrorTransform) - { - return; - } - - var mirrorTransform = MirrorTrasform(); - var renderTransform = RenderTransform; - - ITransform? finalTransform = mirrorTransform; - if (renderTransform != null) - { - finalTransform = mirrorTransform.MergeTransforms(renderTransform); - } - - base.RenderTransform = finalTransform; - _hasMirrorTransform = true; - } - - private void RemoveMirrorTransform() - { - if (!_hasMirrorTransform) - { - return; - } - - ITransform mirrorTransform = MirrorTrasform(); - ITransform renderTransform = RenderTransform!; - - ITransform? finalTransform = mirrorTransform.MergeTransforms(renderTransform); - if (finalTransform!.Value == Matrix.Identity) - { - finalTransform = null; - } - - _hasMirrorTransform = false; - base.RenderTransform = finalTransform; - } - - /// - /// Determines whether the element should be presented mirrored - /// if FlowDirection is RightToLeft + /// Determines whether the element should be presented mirrored, this + /// method related to FlowDirection system and as return true if FlowDirection + /// is RightToLeft. For controls that want to avoid this behavior, it is + /// possible to override this method and return false. /// protected virtual bool ShouldPresentedMirrored() { return FlowDirection == FlowDirection.RightToLeft; } - - static ITransform MirrorTrasform() => - new MatrixTransform(new Avalonia.Matrix(-1, 0, 0, 1, 0.0, 0.0)); } } diff --git a/src/Avalonia.Visuals/ApiCompatBaseline.txt b/src/Avalonia.Visuals/ApiCompatBaseline.txt index b9b3d1906d..300603b297 100644 --- a/src/Avalonia.Visuals/ApiCompatBaseline.txt +++ b/src/Avalonia.Visuals/ApiCompatBaseline.txt @@ -176,5 +176,6 @@ InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Media.GlyphR MembersMustExist : Member 'public Avalonia.Media.GlyphRun Avalonia.Platform.ITextShaperImpl.ShapeText(Avalonia.Utilities.ReadOnlySlice, Avalonia.Media.Typeface, System.Double, System.Globalization.CultureInfo)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'protected void Avalonia.Rendering.RendererBase.RenderFps(Avalonia.Platform.IDrawingContextImpl, Avalonia.Rect, System.Nullable)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public void Avalonia.Utilities.ReadOnlySlice..ctor(System.ReadOnlyMemory, System.Int32, System.Int32)' does not exist in the implementation but it does exist in the contract. -CannotRemoveBaseTypeOrInterface : Type 'Avalonia.Media.Transforms' does not inherit from base type 'Avalonia.Collections.AvaloniaList' in the implementation but it does in the contract. -Total Issues: 178 +InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.VisualTree.IVisual.IsMirrorTransform' is present in the implementation but not in the contract. +InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.VisualTree.IVisual.IsMirrorTransform.get()' is present in the implementation but not in the contract. +Total Issues: 179 diff --git a/src/Avalonia.Visuals/Media/TransformExtensions.cs b/src/Avalonia.Visuals/Media/TransformExtensions.cs index faea406c6d..ccf2231ce2 100644 --- a/src/Avalonia.Visuals/Media/TransformExtensions.cs +++ b/src/Avalonia.Visuals/Media/TransformExtensions.cs @@ -22,23 +22,5 @@ namespace Avalonia.Media return (transform as Transform)?.ToImmutable() ?? new ImmutableTransform(transform.Value); } - - /// - /// Merge two transforms to an one transform. - /// - /// The original transform. - /// The additional transform to be added. - /// - /// One ITransform that includes both. - /// - public static ITransform MergeTransforms(this ITransform srcTransform, ITransform additionalTransform) - { - TransformGroup groupTransform = new TransformGroup(); - - groupTransform.Children.Add(srcTransform); - groupTransform.Children.Add(additionalTransform); - - return groupTransform; - } } } diff --git a/src/Avalonia.Visuals/Media/TransformGroup.cs b/src/Avalonia.Visuals/Media/TransformGroup.cs index 0c31d3fdcb..0465efd5a5 100644 --- a/src/Avalonia.Visuals/Media/TransformGroup.cs +++ b/src/Avalonia.Visuals/Media/TransformGroup.cs @@ -18,20 +18,8 @@ namespace Avalonia.Media Children.CollectionChanged += delegate { Children.ForEachItem( - (tr) => - { - if (tr is IMutableTransform mutTr) - { - mutTr.Changed += ChildTransform_Changed; - } - }, - (tr) => - { - if (tr is IMutableTransform mutTr) - { - mutTr.Changed -= ChildTransform_Changed; - } - }, + (tr) => tr.Changed += ChildTransform_Changed, + (tr) => tr.Changed -= ChildTransform_Changed, () => { }); }; } @@ -73,7 +61,7 @@ namespace Avalonia.Media } } - public sealed class Transforms : AvaloniaList + public sealed class Transforms : AvaloniaList { } } diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs index 63c22efc3f..4eb1c0528f 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs @@ -195,6 +195,12 @@ namespace Avalonia.Rendering.SceneGraph renderTransform = (-offset) * visual.RenderTransform.Value * (offset); } + if (visual.IsMirrorTransform) + { + var mirrorMatrix = new Matrix(-1.0, 0.0, 0.0, 1.0, visual.Bounds.Width, 0); + renderTransform *= mirrorMatrix; + } + m = renderTransform * m; using (contextImpl.BeginUpdate(node)) diff --git a/src/Avalonia.Visuals/Visual.cs b/src/Avalonia.Visuals/Visual.cs index fcb4298895..80adfd31a8 100644 --- a/src/Avalonia.Visuals/Visual.cs +++ b/src/Avalonia.Visuals/Visual.cs @@ -68,6 +68,12 @@ namespace Avalonia public static readonly StyledProperty OpacityMaskProperty = AvaloniaProperty.Register(nameof(OpacityMask)); + /// + /// Defines the property. + /// + public static readonly DirectProperty IsMirrorTransformProperty = + AvaloniaProperty.RegisterDirect(nameof(IsMirrorTransform), o => o.IsMirrorTransform); + /// /// Defines the property. /// @@ -96,6 +102,7 @@ namespace Avalonia private TransformedBounds? _transformedBounds; private IRenderRoot? _visualRoot; private IVisual? _visualParent; + private bool _isMirrorTransform; /// /// Initializes static members of the class. @@ -107,7 +114,8 @@ namespace Avalonia ClipProperty, ClipToBoundsProperty, IsVisibleProperty, - OpacityProperty); + OpacityProperty, + IsMirrorTransformProperty); RenderTransformProperty.Changed.Subscribe(RenderTransformChanged); ZIndexProperty.Changed.Subscribe(ZIndexChanged); } @@ -119,7 +127,7 @@ namespace Avalonia { // Disable transitions until we're added to the visual tree. DisableTransitions(); - + var visualChildren = new AvaloniaList(); visualChildren.ResetBehavior = ResetBehavior.Remove; visualChildren.Validate = visual => ValidateVisualChild(visual); @@ -219,10 +227,19 @@ namespace Avalonia set { SetValue(OpacityMaskProperty, value); } } + /// + /// Gets or sets a value indicating whether this control presented as mirror. + /// + public bool IsMirrorTransform + { + get { return _isMirrorTransform; } + protected set { SetAndRaise(IsMirrorTransformProperty, ref _isMirrorTransform, value); } + } + /// /// Gets or sets the render transform of the control. /// - public virtual ITransform? RenderTransform + public ITransform? RenderTransform { get { return GetValue(RenderTransformProperty); } set { SetValue(RenderTransformProperty, value); } diff --git a/src/Avalonia.Visuals/VisualExtensions.cs b/src/Avalonia.Visuals/VisualExtensions.cs index ff8a515db3..b726c1f547 100644 --- a/src/Avalonia.Visuals/VisualExtensions.cs +++ b/src/Avalonia.Visuals/VisualExtensions.cs @@ -110,6 +110,12 @@ namespace Avalonia result *= renderTransform; } + if (v.IsMirrorTransform) + { + var mirrorMatrix = new Matrix(-1.0, 0.0, 0.0, 1.0, v.Bounds.Width, 0); + result *= mirrorMatrix; + } + var topLeft = v.Bounds.TopLeft; if (topLeft != default) diff --git a/src/Avalonia.Visuals/VisualTree/IVisual.cs b/src/Avalonia.Visuals/VisualTree/IVisual.cs index 97c4554de6..95fe665e89 100644 --- a/src/Avalonia.Visuals/VisualTree/IVisual.cs +++ b/src/Avalonia.Visuals/VisualTree/IVisual.cs @@ -75,6 +75,11 @@ namespace Avalonia.VisualTree /// IBrush? OpacityMask { get; set; } + /// + /// Gets a value indicating whether this control presented as mirror. + /// + bool IsMirrorTransform { get; } + /// /// Gets or sets the render transform of the control. ///