diff --git a/src/SamplesApp/UITests.Shared/UITests.Shared.projitems b/src/SamplesApp/UITests.Shared/UITests.Shared.projitems index 39b7d55ec8a3..376bb85c8667 100644 --- a/src/SamplesApp/UITests.Shared/UITests.Shared.projitems +++ b/src/SamplesApp/UITests.Shared/UITests.Shared.projitems @@ -1834,6 +1834,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + Designer MSBuild:Compile @@ -6099,6 +6103,9 @@ GridViewMultipleSelectionMode.xaml + + Flyout_OverlayInputPassThroughElement.xaml + IconSourceElementTests.xaml diff --git a/src/Uno.UI/Generated/3.0.0.0/Microsoft.UI.Xaml.Controls.Primitives/FlyoutBase.cs b/src/Uno.UI/Generated/3.0.0.0/Microsoft.UI.Xaml.Controls.Primitives/FlyoutBase.cs index d9ca78ec0101..eb2840da1ca1 100644 --- a/src/Uno.UI/Generated/3.0.0.0/Microsoft.UI.Xaml.Controls.Primitives/FlyoutBase.cs +++ b/src/Uno.UI/Generated/3.0.0.0/Microsoft.UI.Xaml.Controls.Primitives/FlyoutBase.cs @@ -38,7 +38,7 @@ public bool ShouldConstrainToRootBounds } #endif // Skipping already declared property Placement -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ +#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || false || __NETSTD_REFERENCE__ || __MACOS__ [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] public global::Microsoft.UI.Xaml.DependencyObject OverlayInputPassThroughElement { @@ -148,7 +148,7 @@ public bool IsConstrainedToRootBounds #endif // Skipping already declared property IsOpenProperty // Skipping already declared property LightDismissOverlayModeProperty -#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__ +#if __ANDROID__ || __IOS__ || IS_UNIT_TESTS || __WASM__ || false || __NETSTD_REFERENCE__ || __MACOS__ [global::Uno.NotImplemented("__ANDROID__", "__IOS__", "IS_UNIT_TESTS", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")] public static global::Microsoft.UI.Xaml.DependencyProperty OverlayInputPassThroughElementProperty { get; } = Microsoft.UI.Xaml.DependencyProperty.Register( diff --git a/src/Uno.UI/UI/Xaml/Controls/Flyout/FlyoutBase.cs b/src/Uno.UI/UI/Xaml/Controls/Flyout/FlyoutBase.cs index 62975a3f98ad..8c9f33880910 100644 --- a/src/Uno.UI/UI/Xaml/Controls/Flyout/FlyoutBase.cs +++ b/src/Uno.UI/UI/Xaml/Controls/Flyout/FlyoutBase.cs @@ -244,6 +244,15 @@ internal Brush LightDismissOverlayBackground internal static DependencyProperty LightDismissOverlayBackgroundProperty { get; } = DependencyProperty.Register("LightDismissOverlayBackground", typeof(Brush), typeof(FlyoutBase), new FrameworkPropertyMetadata(null)); + + internal static DependencyProperty OverlayInputPassThroughElementProperty { get; } = + DependencyProperty.Register("OverlayInputPassThroughElement", typeof(DependencyObject), typeof(FlyoutBase), new FrameworkPropertyMetadata(null)); + + public DependencyObject OverlayInputPassThroughElement + { + get { return (DependencyObject)GetValue(OverlayInputPassThroughElementProperty); } + set { SetValue(OverlayInputPassThroughElementProperty, value); } + } /// /// Gets or sets whether a disabled control can receive focus. diff --git a/src/Uno.UI/UI/Xaml/Controls/Flyout/FlyoutPopupPanel.cs b/src/Uno.UI/UI/Xaml/Controls/Flyout/FlyoutPopupPanel.cs index b3fae3f3dc51..cdda2f9a57e0 100644 --- a/src/Uno.UI/UI/Xaml/Controls/Flyout/FlyoutPopupPanel.cs +++ b/src/Uno.UI/UI/Xaml/Controls/Flyout/FlyoutPopupPanel.cs @@ -1,11 +1,14 @@ #if !__UWP__ using System; using System.Collections.Generic; +using System.Linq; using System.Text; using Uno.UI; using Windows.Foundation; +using Windows.UI.Core; using Windows.UI.ViewManagement; using Microsoft.UI.Xaml.Controls.Primitives; +using Microsoft.UI.Xaml.Input; using Microsoft.UI.Xaml.Media; namespace Microsoft.UI.Xaml.Controls @@ -35,6 +38,30 @@ public FlyoutBasePopupPanel(FlyoutBase flyout) : base(flyout._popup) internal override FlyoutBase Flyout => _flyout; protected override int PopupPlacementTargetMargin => 5; + + protected override void OnPointerPressed(object sender, PointerRoutedEventArgs args) + { + base.OnPointerPressed(sender, args); + + // Make sure we are the original source. We do not want to handle PointerPressed on the Popup itself. + if (args.OriginalSource == this) + { + if (Flyout.OverlayInputPassThroughElement is UIElement passThroughElement) + { + var (elementToBeHit, _) = VisualTreeHelper.SearchDownForTopMostElementAt( + args.GetCurrentPoint(null).Position, + passThroughElement.XamlRoot.VisualTree.RootElement, + VisualTreeHelper.DefaultGetTestability, + childrenFilter: elements => elements.Where(e => e != this)); + + var eventArgs = new PointerRoutedEventArgs( + new PointerEventArgs(args.GetCurrentPoint(null), args.KeyModifiers), + args.OriginalSource as UIElement); + + elementToBeHit.OnPointerDown(eventArgs); + } + } + } } } #endif diff --git a/src/Uno.UI/UI/Xaml/Controls/Popup/PopupPanel.cs b/src/Uno.UI/UI/Xaml/Controls/Popup/PopupPanel.cs index 70c2903ff583..606afda057e1 100644 --- a/src/Uno.UI/UI/Xaml/Controls/Popup/PopupPanel.cs +++ b/src/Uno.UI/UI/Xaml/Controls/Popup/PopupPanel.cs @@ -243,7 +243,7 @@ private protected override void OnUnloaded() // TODO: pointer handling should really go on PopupRoot. For now it's easier to put here because PopupRoot doesn't track open popups, and also we // need to support native popups on Android that don't use PopupRoot. - private void OnPointerPressed(object sender, PointerRoutedEventArgs args) + protected virtual void OnPointerPressed(object sender, PointerRoutedEventArgs args) { // Make sure we are the original source. We do not want to handle PointerPressed on the Popup itself. if (args.OriginalSource == this && Popup is { } popup) diff --git a/src/Uno.UI/UI/Xaml/Media/VisualTreeHelper.cs b/src/Uno.UI/UI/Xaml/Media/VisualTreeHelper.cs index fb6bc81b9dd7..c94270911230 100644 --- a/src/Uno.UI/UI/Xaml/Media/VisualTreeHelper.cs +++ b/src/Uno.UI/UI/Xaml/Media/VisualTreeHelper.cs @@ -448,7 +448,7 @@ internal static (UIElement? element, Branch? stale) HitTest( /// On skia: The absolute position relative to the window origin. /// Everywhere else: The position relative to the parent (i.e. the position in parent coordinates). /// - private static (UIElement? element, Branch? stale) SearchDownForTopMostElementAt( + internal static (UIElement? element, Branch? stale) SearchDownForTopMostElementAt( Point position, UIElement element, GetHitTestability getVisibility,