diff --git a/samples/ControlCatalog/Pages/ScreenPage.cs b/samples/ControlCatalog/Pages/ScreenPage.cs index 1fc37d903d..f65566a1e9 100644 --- a/samples/ControlCatalog/Pages/ScreenPage.cs +++ b/samples/ControlCatalog/Pages/ScreenPage.cs @@ -79,6 +79,6 @@ namespace ControlCatalog.Pages Typeface.Default, 12, Brushes.Green); } - protected override bool ShouldBeMirroredIfRightToLeft() => false; + protected override bool ShouldGetMirrored() => false; } } diff --git a/src/Avalonia.Controls/Control.cs b/src/Avalonia.Controls/Control.cs index 16d6c3f4c5..9b41e9ee64 100644 --- a/src/Avalonia.Controls/Control.cs +++ b/src/Avalonia.Controls/Control.cs @@ -67,10 +67,17 @@ 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. @@ -126,6 +133,21 @@ namespace Avalonia.Controls set => SetValue(FlowDirectionProperty, value); } + /// + public override ITransform? RenderTransform + { + get => base.RenderTransform; + set + { + if (_hasMirrorTransform) + { + value = MargeTransforms(MirrorTrasform(), value); + } + + base.RenderTransform = value; + } + } + /// /// Occurs when the user has completed a context input gesture, such as a right-click. /// @@ -312,76 +334,152 @@ namespace Avalonia.Controls static Control() { - AffectsArrange(FlowDirectionProperty); + //var m = new StyledPropertyMetadata(coerce: (s, e) => null); + //RenderTransformProperty.OverrideMetadata(m); + + //AffectsRender(FlowDirectionProperty); + //FlowDirectionProperty.Changed.AddClassHandler((s, e) => + //{ + // s.InvalidateFlowDirection(); + // foreach (var logical in LogicalTree.LogicalExtensions.GetLogicalDescendants(s)) + // { + // if (logical is Control control) + // { + // //if (control) + // //control.InvalidateFlowDirection(); + // } + // } + //}); } - private bool _mirrorApplied; - - protected virtual bool ShouldBeMirroredIfRightToLeft() + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { - if (Parent is Control parent) - { - return parent.ShouldBeMirroredIfRightToLeft(); - } - else + base.OnPropertyChanged(change); + + if (change.Property == FlowDirectionProperty) { - return true; + // Avoid inherit value change to invoke this method + if (!GetBaseValue(FlowDirectionProperty, change.Priority).HasValue) + { + return; + } + + InvalidateFlowDirection(); } } - protected override void ArrangeCore(Rect finalRect) + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) { - base.ArrangeCore(finalRect); + base.OnAttachedToVisualTree(e); + + InvalidateFlowDirection(); + } + protected override void OnAttachedToLogicalTree(LogicalTree.LogicalTreeAttachmentEventArgs e) + { + base.OnAttachedToLogicalTree(e); + //InvalidateFlowDirection(); + } + + protected virtual bool ShouldGetMirrored() => true; + + private void InvalidateFlowDirection() + { FlowDirection parentFD = FlowDirection.LeftToRight; FlowDirection thisFD = FlowDirection; - bool shouldBeMirroredIfRightToLeft = ShouldBeMirroredIfRightToLeft(); - if (Parent is Control control) + bool parentShouldGetMirrored = true; + bool thisShouldGetMirrored = ShouldGetMirrored(); + + if (((Visual)this).GetVisualParent() is Control control) { parentFD = control.FlowDirection; + parentShouldGetMirrored = control.ShouldGetMirrored(); } - - bool shouldMirror; - if (shouldBeMirroredIfRightToLeft) + else if (Parent is Control logicalControl) { - shouldMirror = ShuoldApplyMirrorTransform(parentFD, thisFD); - if (Parent is Popup && thisFD == FlowDirection.RightToLeft) - { - shouldMirror = true; - } + parentFD = logicalControl.FlowDirection; + parentShouldGetMirrored = logicalControl.ShouldGetMirrored(); + } + + bool shouldBeMirrored = thisFD == FlowDirection.RightToLeft && thisShouldGetMirrored; + bool parentMirrored = parentFD == FlowDirection.RightToLeft && parentShouldGetMirrored; + + bool shouldApplyMirrorTransform = shouldBeMirrored != parentMirrored; + + if (shouldApplyMirrorTransform) + { + AddMirrorTransform(); } else { - shouldMirror = ShuoldApplyMirrorTransform(parentFD, FlowDirection.LeftToRight); + RemoveMirrorTransform(); } - if (shouldMirror) + foreach (var visual in VisualChildren) { - ApplyMirrorTransform(); + if (visual is Control child) + { + child.InvalidateFlowDirection(); + } } - else + } + + private void AddMirrorTransform() + { + if (_hasMirrorTransform) { - //RenderTransform = null; + return; } + + var mirrorTransform = MirrorTrasform(); + + ITransform? finalTransform = mirrorTransform; + if (RenderTransform != null) + { + finalTransform = MargeTransforms(RenderTransform, mirrorTransform); + } + + RenderTransform = finalTransform; + _hasMirrorTransform = true; } - private void ApplyMirrorTransform() + private void RemoveMirrorTransform() { - if (_mirrorApplied) + if (!_hasMirrorTransform) { return; } - var transform = new MatrixTransform(new Avalonia.Matrix(-1, 0, 0, 1, 0.0, 0.0)); - RenderTransform = transform; - _mirrorApplied = true; + var mirrorTransform = MirrorTrasform(); + + ITransform? finalTransform = MargeTransforms(RenderTransform, mirrorTransform); + if (finalTransform!.Value == Matrix.Identity) + { + finalTransform = null; + } + + _hasMirrorTransform = false; + RenderTransform = finalTransform; } - internal static bool ShuoldApplyMirrorTransform(FlowDirection parentFD, FlowDirection thisFD) + static ITransform? MargeTransforms(ITransform? iTransform1, ITransform? iTransform2) { - return ((parentFD == FlowDirection.LeftToRight && thisFD == FlowDirection.RightToLeft) || - (parentFD == FlowDirection.RightToLeft && thisFD == FlowDirection.LeftToRight)); + // don't know how to marge ITransform + if (iTransform1 is Transform transform1 && iTransform2 is Transform transform2) + { + TransformGroup groupTransform = new TransformGroup(); + + groupTransform.Children.Add(transform1); + groupTransform.Children.Add(transform2); + + return groupTransform; + } + + return iTransform1; } + + static ITransform MirrorTrasform() => + new MatrixTransform(new Avalonia.Matrix(-1, 0, 0, 1, 0.0, 0.0)); } } diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index 10ce31088a..858544a872 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -798,5 +798,7 @@ namespace Avalonia.Controls.Presenters } } } + + protected override bool ShouldGetMirrored() => false; } } diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs index d29f094c38..a5b1d44dc8 100644 --- a/src/Avalonia.Controls/TextBlock.cs +++ b/src/Avalonia.Controls/TextBlock.cs @@ -613,6 +613,6 @@ namespace Avalonia.Controls InvalidateTextLayout(); } - protected override bool ShouldBeMirroredIfRightToLeft() => false; + protected override bool ShouldGetMirrored() => false; } } diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 76f7a185fe..4d71717776 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -1504,7 +1504,5 @@ namespace Avalonia.Controls } } } - - protected override bool ShouldBeMirroredIfRightToLeft() => false; } } diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index a4fe154515..da85824457 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -529,5 +529,7 @@ namespace Avalonia.Controls ITextInputMethodImpl? ITextInputMethodRoot.InputMethod => (PlatformImpl as ITopLevelImplWithTextInputMethod)?.TextInputMethod; + + protected override bool ShouldGetMirrored() => false; } } diff --git a/src/Avalonia.Themes.Fluent/Controls/CheckBox.xaml b/src/Avalonia.Themes.Fluent/Controls/CheckBox.xaml index ef28593711..66dc17a417 100644 --- a/src/Avalonia.Themes.Fluent/Controls/CheckBox.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/CheckBox.xaml @@ -152,6 +152,7 @@ + diff --git a/src/Avalonia.Visuals/Visual.cs b/src/Avalonia.Visuals/Visual.cs index 324b253a0f..fcb4298895 100644 --- a/src/Avalonia.Visuals/Visual.cs +++ b/src/Avalonia.Visuals/Visual.cs @@ -222,7 +222,7 @@ namespace Avalonia /// /// Gets or sets the render transform of the control. /// - public ITransform? RenderTransform + public virtual ITransform? RenderTransform { get { return GetValue(RenderTransformProperty); } set { SetValue(RenderTransformProperty, value); }