Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VisualTreeElement methods need to use DP vs PX #7904

Merged
merged 16 commits into from
Jun 10, 2022
86 changes: 86 additions & 0 deletions src/Controls/tests/DeviceTests/Elements/VisualElementTreeTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
using System.Threading.Tasks;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Controls.Handlers;
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Hosting;
using Microsoft.Maui.Platform;
using Xunit;
#if ANDROID || IOS
using ShellHandler = Microsoft.Maui.Controls.Handlers.Compatibility.ShellRenderer;
#endif

namespace Microsoft.Maui.DeviceTests
{
[Category(TestCategory.VisualElementTree)]
#if ANDROID || IOS
[Collection(HandlerTestBase.RunInNewWindowCollection)]
#endif
public partial class VisualElementTreeTests : HandlerTestBase
{
void SetupBuilder()
{
EnsureHandlerCreated(builder =>
{
builder.ConfigureMauiHandlers(handlers =>
{
handlers.AddHandler(typeof(Controls.Shell), typeof(ShellHandler));
handlers.AddHandler<Layout, LayoutHandler>();
handlers.AddHandler<Image, ImageHandler>();
handlers.AddHandler<Label, LabelHandler>();
handlers.AddHandler<Page, PageHandler>();
handlers.AddHandler<Toolbar, ToolbarHandler>();
handlers.AddHandler<MenuBar, MenuBarHandler>();
handlers.AddHandler<MenuBarItem, MenuBarItemHandler>();
handlers.AddHandler<MenuFlyoutItem, MenuFlyoutItemHandler>();
handlers.AddHandler<MenuFlyoutSubItem, MenuFlyoutSubItemHandler>();
#if WINDOWS
handlers.AddHandler<ShellItem, ShellItemHandler>();
handlers.AddHandler<ShellSection, ShellSectionHandler>();
handlers.AddHandler<ShellContent, ShellContentHandler>();
#endif
});
});
}

[Fact]
public async Task GetVisualTreeElements()
{
SetupBuilder();
var label = new Label() { Text = "Find Me" };
var page = new ContentPage() { Title = "Title Page" };
page.Content = new VerticalStackLayout()
{
label
};

var shell = await InvokeOnMainThreadAsync(() =>
new Shell() { CurrentItem = new FlyoutItem() { Items = { page } } }
);

await CreateHandlerAndAddToWindow<IWindowHandler>(shell, async handler =>
{
await OnFrameSetToNotEmpty(label);
var locationOnScreen = label.GetLocationOnScreen().Value;
var labelFrame = label.Frame;
var window = shell.Window;

// Find label at the top left corner
Assert.Contains(label, window.GetVisualTreeElements(locationOnScreen.X, locationOnScreen.Y));

// find label at the bottom right corner
Assert.Contains(label, window.GetVisualTreeElements(
locationOnScreen.X + labelFrame.Width - 0.1,
locationOnScreen.Y + labelFrame.Height - 0.1
));

// Ensure that the point directly outside the bounds of the label doesn't
// return the label
Assert.DoesNotContain(label, window.GetVisualTreeElements(
locationOnScreen.X + labelFrame.Width + 0.1,
locationOnScreen.Y + labelFrame.Height + 0.1
));

});
}
}
}
14 changes: 11 additions & 3 deletions src/Controls/tests/DeviceTests/HandlerTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Hosting;
using Microsoft.Maui.LifecycleEvents;
using Microsoft.Maui.Platform;
using Microsoft.Maui.TestUtils.DeviceTests.Runners;

namespace Microsoft.Maui.DeviceTests
Expand Down Expand Up @@ -209,6 +210,9 @@ await SetupWindowForTests<THandler>(window, async () =>
content = pc.CurrentPage;

await OnLoadedAsync(content as VisualElement);

if (content is Page page)
await OnNavigatedToAsync(page);
#if WINDOWS

await Task.Delay(10);
Expand Down Expand Up @@ -310,19 +314,23 @@ void NavigatedTo(object sender, NavigatedToEventArgs e)
}
}

protected Task OnFrameSetToNotEmpty(VisualElement frameworkElement, TimeSpan? timeOut = null)
protected async Task OnFrameSetToNotEmpty(VisualElement frameworkElement, TimeSpan? timeOut = null)
{
if (frameworkElement.Frame.Height > 0 &&
frameworkElement.Frame.Width > 0)
{
return Task.CompletedTask;
return;
}

timeOut = timeOut ?? TimeSpan.FromSeconds(2);
TaskCompletionSource<object> taskCompletionSource = new TaskCompletionSource<object>();
frameworkElement.BatchCommitted += OnBatchCommitted;

return taskCompletionSource.Task.WaitAsync(timeOut.Value);
await taskCompletionSource.Task.WaitAsync(timeOut.Value);

// Wait for the layout to propagate to the platform
if (frameworkElement.GetBoundingBox().Equals(new Rect()))
await Task.Delay(10);

void OnBatchCommitted(object sender, Controls.Internals.EventArg<VisualElement> e)
{
Expand Down
1 change: 1 addition & 0 deletions src/Controls/tests/DeviceTests/TestCategory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public static class TestCategory
public const string TabbedPage = "TabbedPage";
public const string TemplatedView = "TemplatedView";
public const string VisualElement = "VisualElement";
public const string VisualElementTree = "VisualElementTree";
public const string Window = "Window";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ static void Impl(IVisualTreeElement visualElement, Predicate<Rect> intersectElem
{
if (visualElement is IView view)
{
Rect bounds = view.GetPlatformViewBounds();
Rect bounds = view.GetBoundingBox();
if (intersectElementBounds(bounds))
elements.Add(visualElement);
}
Expand Down
12 changes: 9 additions & 3 deletions src/Core/src/Platform/Android/ViewExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ public static void UpdateBackground(this AView platformView, Paint? background)
platformView.Background = drawable;
}
}
else if(platformView is LayoutViewGroup)
else if (platformView is LayoutViewGroup)
{
platformView.Background = null;
}
Expand Down Expand Up @@ -408,12 +408,18 @@ internal static Graphics.Rect GetBoundingBox(this IView view)

internal static Graphics.Rect GetBoundingBox(this View? platformView)
{
if (platformView == null)
if (platformView?.Context == null)
return new Rect();

var context = platformView.Context;
var rect = new Android.Graphics.Rect();
platformView.GetGlobalVisibleRect(rect);
return new Rect(rect.ExactCenterX() - (rect.Width() / 2), rect.ExactCenterY() - (rect.Height() / 2), (float)rect.Width(), (float)rect.Height());

return new Rect(
context.FromPixels(rect.ExactCenterX() - (rect.Width() / 2)),
context.FromPixels(rect.ExactCenterY() - (rect.Height() / 2)),
context.FromPixels((float)rect.Width()),
context.FromPixels((float)rect.Height()));
}

internal static bool IsLoaded(this View frameworkElement) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,14 @@ public async Task ReturnsNonEmptyNativeBoundingBounds(int size)

var nativeBoundingBox = await GetValueAsync(view, handler => GetBoundingBox(handler));
Assert.NotEqual(nativeBoundingBox, new Graphics.Rect());

if (!CloseEnough(size, nativeBoundingBox.Size.Height) || !CloseEnough(size, nativeBoundingBox.Size.Width))
Assert.Equal(new Size(size, size), nativeBoundingBox.Size);

bool CloseEnough(double value1, double value2)
{
return System.Math.Abs(value2 - value1) < 0.1;
}
}


Expand Down