Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions Cursorial.UI.Tests/Windowing/WindowPopupTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,41 @@ public void PlacementBottom_PositionsBelowTarget()
Assert.Equal(ty + target.Bounds.Rows, popup.PopupSurface!.Top);
}

[Fact] // inside a popup placed AWAY from the screen origin, screen↔element translation round-trips and
// MouseEventArgs.GetPosition reports element-local cells (the popup surface's offset is applied both ways)
public void PlacedPopup_TranslateFromScreen_AndGetPosition_AreElementLocal()
{
var host = NewHost();
using var _ = host;
var spacer = new UIControls.Border { Width = 30, Height = 5 };
var target = new UIControls.Button { Width = 10, Height = 1, Content = "open", Margin = new(7, 0, 0, 0) };
var inner = new UIControls.Button { Width = 8, Height = 3, Content = "menu" };
var popup = new Popup { Child = inner, PlacementTarget = target };
var root = new UIControls.StackPanel();
root.Children.Add(spacer);
root.Children.Add(target);
root.Children.Add(popup);
host.ShowRoot(root);
Assert.True(host.RunUntilIdle());

popup.Open();
Assert.True(host.RunUntilIdle());
var surface = popup.PopupSurface!;
Assert.True(surface.Left > 0 && surface.Top > 0, $"the popup should sit away from the origin, was ({surface.Left},{surface.Top})");

// Round trip: element → screen → element.
var (sx, sy) = inner.TranslateToScreen(2, 1);
Assert.Equal((surface.Left + 2, surface.Top + 1), (sx, sy));
Assert.Equal((2, 1), inner.TranslateFromScreen(sx, sy));

// The dispatched press reports the same element-local cell.
CellPosition? seen = null;
inner.AddHandler(UIElement.MouseDownEvent, (_, e) => seen = e.GetPosition(inner), handledEventsToo: true);
host.SendMouseDown(sx, sy);
Assert.True(host.RunUntilIdle());
Assert.Equal(new CellPosition(2, 1), seen);
}

[Fact] // an uncaptured press outside the popup light-dismisses it (Closed fires with LightDismiss)
public void OutsidePress_LightDismisses()
{
Expand Down
11 changes: 7 additions & 4 deletions Cursorial.UI/UIElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -896,12 +896,15 @@ public bool IsEffectivelyVisible
{
(column, row) = TranslateFromWindow(column, row);

// The surface offset is resolved the same way TranslateToScreen resolves it — through the window manager,
// for ANY surface root. A popup's hosted child is its own surface root (the Popup element itself stays in
// the owner's tree), so matching on `root is Popup` never fired for popup content: inside a popup placed
// away from the screen origin, MouseEventArgs.GetPosition came back offset by the popup's position.
if (_visualRoot is {} root)
{
if (root is Window { HostSurface: {} windowHost})
(column, row) = (column - windowHost.Left, row - windowHost.Top);
else if (root is Popup { PopupSurface: {} popupHost})
(column, row) = (column - popupHost.Left, row - popupHost.Top);
var surface = UIApplication.Current?.WindowManager?.SurfaceForElement(root);
if (surface is not null)
(column, row) = (column - surface.Left, row - surface.Top);
}

return (column, row);
Expand Down
Loading