From af928316233541c06f60e2e1647ca35500a3b329 Mon Sep 17 00:00:00 2001 From: xiaoy312 Date: Mon, 17 Apr 2023 18:17:26 -0400 Subject: [PATCH] fix(ComboBox): flyout placement in ios SheetPage Co-Authored-By: Andres Pineda --- .../Extensions/UIViewExtensions.iOSmacOS.cs | 2 +- src/Uno.UI/UI/Xaml/UIElement.Android.cs | 4 +-- src/Uno.UI/UI/Xaml/UIElement.cs | 26 ++++++++++++++----- src/Uno.UI/UI/Xaml/UIElement.iOS.cs | 14 ++++++++-- src/Uno.UI/UI/Xaml/UIElement.macOS.cs | 2 +- 5 files changed, 36 insertions(+), 12 deletions(-) diff --git a/src/Uno.UI/Extensions/UIViewExtensions.iOSmacOS.cs b/src/Uno.UI/Extensions/UIViewExtensions.iOSmacOS.cs index 0ab6f21b5283..f3902630e149 100644 --- a/src/Uno.UI/Extensions/UIViewExtensions.iOSmacOS.cs +++ b/src/Uno.UI/Extensions/UIViewExtensions.iOSmacOS.cs @@ -384,7 +384,7 @@ public static _Controller FindViewController(this _View view) // Sometimes, a view is not part of the visual tree (or doesn't have a next responder) but is part of the logical tree. // Here, we substitute the view with the first logical parent that's part of the visual tree (or has a next responder). view = (view as DependencyObject) - .GetParents() + ?.GetParents() .OfType<_View>() .Where(parent => parent.NextResponder != null) .FirstOrDefault(); diff --git a/src/Uno.UI/UI/Xaml/UIElement.Android.cs b/src/Uno.UI/UI/Xaml/UIElement.Android.cs index 7d5cc101edb6..52333140c14b 100644 --- a/src/Uno.UI/UI/Xaml/UIElement.Android.cs +++ b/src/Uno.UI/UI/Xaml/UIElement.Android.cs @@ -237,7 +237,7 @@ internal static GeneralTransform TransformToVisual(View element, View visual) /// Note: Offsets are only an approximation which does not take in consideration possible transformations /// applied by a 'ViewGroup' between this element and its parent UIElement. /// - private bool TryGetParentUIElementForTransformToVisual(out UIElement parentElement, ref double offsetX, ref double offsetY) + private bool TryGetParentUIElementForTransformToVisual(out UIElement parentElement, ref double offsetX, ref double offsetY, ref TransformToVisualContext context) { var parent = this.GetVisualTreeParent(); switch (parent) @@ -271,7 +271,7 @@ private bool TryGetParentUIElementForTransformToVisual(out UIElement parentEleme offsetY += offset.Y; // We return the parent of the ScrollViewer, so we bypass the Offset (and the Scale) handling in shared code. - return sv.TryGetParentUIElementForTransformToVisual(out parentElement, ref offsetX, ref offsetY); + return sv.TryGetParentUIElementForTransformToVisual(out parentElement, ref offsetX, ref offsetY, ref context); case View view: // Android.View and Android.IViewParent var windowToFirstParent = new int[2]; diff --git a/src/Uno.UI/UI/Xaml/UIElement.cs b/src/Uno.UI/UI/Xaml/UIElement.cs index 39b398c6d842..6ab8fe9d5298 100644 --- a/src/Uno.UI/UI/Xaml/UIElement.cs +++ b/src/Uno.UI/UI/Xaml/UIElement.cs @@ -454,7 +454,9 @@ protected virtual void OnVisibilityChanged(Visibility oldValue, Visibility newVi internal AutomationPeer OnCreateAutomationPeerInternal() => OnCreateAutomationPeer(); - internal static Matrix3x2 GetTransform(UIElement from, UIElement to) + internal static Matrix3x2 GetTransform(UIElement from, UIElement to) => GetTransform(from, to, new()); + + private static Matrix3x2 GetTransform(UIElement from, UIElement to, TransformToVisualContext context) { var logInfoString = from.Log().IsEnabled(LogLevel.Debug) ? new StringBuilder() : null; logInfoString?.Append($"{nameof(GetTransform)}(from: {from}, to: {to?.ToString() ?? ""}) Offsets: ["); @@ -525,7 +527,7 @@ internal static Matrix3x2 GetTransform(UIElement from, UIElement to) #endif logInfoString?.Append($"{elt}: ({offsetX}, {offsetY}), "); - } while (elt.TryGetParentUIElementForTransformToVisual(out elt, ref offsetX, ref offsetY) && elt != to); // If possible we stop as soon as we reach 'to' + } while (elt.TryGetParentUIElementForTransformToVisual(out elt, ref offsetX, ref offsetY, ref context) && elt != to); // If possible we stop as soon as we reach 'to' matrix *= Matrix3x2.CreateTranslation((float)offsetX, (float)offsetY); @@ -534,10 +536,20 @@ internal static Matrix3x2 GetTransform(UIElement from, UIElement to) // Unfortunately we didn't find the 'to' in the parent hierarchy, // so matrix == fromToRoot and we now have to compute the transform 'toToRoot'. // Note: We do not propagate the 'intermediatesSelector' as cached transforms would be irrelevant - var toToRoot = GetTransform(to, null); - var rootToTo = toToRoot.Inverse(); + var toContext = new TransformToVisualContext(); + var toToRoot = GetTransform(to, null, toContext); - matrix *= rootToTo; +#if __IOS__ + // On iOS, the `from` and `to` may be coming different ViewController. + // In such case, their coordinates should not be "added" together, since they are from different coordinates space. + if (context?.ViewController is { } vc1 && + toContext?.ViewController is { } vc2 && + vc1 == vc2) +#endif + { + var rootToTo = toToRoot.Inverse(); + matrix *= rootToTo; + } } if (logInfoString != null) @@ -553,7 +565,7 @@ internal static Matrix3x2 GetTransform(UIElement from, UIElement to) /// Note: Offsets are only an approximation which does not take in consideration possible transformations /// applied by a 'UIView' between this element and its parent UIElement. /// - private bool TryGetParentUIElementForTransformToVisual(out UIElement parentElement, ref double offsetX, ref double offsetY) + private bool TryGetParentUIElementForTransformToVisual(out UIElement parentElement, ref double offsetX, ref double offsetY, ref TransformToVisualContext context) { var parent = VisualTreeHelper.GetParent(this); switch (parent) @@ -1132,5 +1144,7 @@ public UI.Input.InputCursor ProtectedCursor set; } #endif + + private partial class TransformToVisualContext { } } } diff --git a/src/Uno.UI/UI/Xaml/UIElement.iOS.cs b/src/Uno.UI/UI/Xaml/UIElement.iOS.cs index dbd2df93a977..8b4bc4dbe9ac 100644 --- a/src/Uno.UI/UI/Xaml/UIElement.iOS.cs +++ b/src/Uno.UI/UI/Xaml/UIElement.iOS.cs @@ -161,7 +161,7 @@ internal Windows.Foundation.Point GetPosition(Point position, global::Windows.UI /// Note: Offsets are only an approximation which does not take in consideration possible transformations /// applied by a 'UIView' between this element and its parent UIElement. /// - private bool TryGetParentUIElementForTransformToVisual(out UIElement parentElement, ref double offsetX, ref double offsetY) + private bool TryGetParentUIElementForTransformToVisual(out UIElement parentElement, ref double offsetX, ref double offsetY, ref TransformToVisualContext context) { var parent = this.GetVisualTreeParent(); switch (parent) @@ -222,18 +222,28 @@ private bool TryGetParentUIElementForTransformToVisual(out UIElement parentEleme case null: // We reached the top of the window without any UIElement in the hierarchy, // so we adjust offsets using the X/Y position of the original 'view' in the window. - offset = view.ConvertRectToView(default, null).Location; parentElement = null; offsetX += offset.X; offsetY += offset.Y; + + if (this.FindViewController() is { } vc) + { + context.ViewController = vc; + } + return false; } } while (true); } } + partial class TransformToVisualContext + { + public UIViewController ViewController { get; set; } + } + #if DEBUG public static Predicate ViewOfInterestSelector { get; set; } = v => (v as FrameworkElement)?.Name == "TargetView"; diff --git a/src/Uno.UI/UI/Xaml/UIElement.macOS.cs b/src/Uno.UI/UI/Xaml/UIElement.macOS.cs index 1c0a108f499f..6127b8904be1 100644 --- a/src/Uno.UI/UI/Xaml/UIElement.macOS.cs +++ b/src/Uno.UI/UI/Xaml/UIElement.macOS.cs @@ -191,7 +191,7 @@ private protected override void OnNativeFlagsChanged(NSEvent evt) private bool CheckFlagKeyDown(NSEventModifierMask flag, NSEventModifierMask newMask) => !_lastFlags.HasFlag(flag) && newMask.HasFlag(flag); - private bool TryGetParentUIElementForTransformToVisual(out UIElement parentElement, ref double offsetX, ref double offsetY) + private bool TryGetParentUIElementForTransformToVisual(out UIElement parentElement, ref double offsetX, ref double offsetY, ref TransformToVisualContext context) { var parent = this.GetVisualTreeParent(); switch (parent)