Skip to content

Fix SNaBorderedWindow drag detection and resize coordinate space bugs#21

Closed
Copilot wants to merge 3 commits into
mainfrom
copilot/fix-snaborderedwindow-dragging-issues
Closed

Fix SNaBorderedWindow drag detection and resize coordinate space bugs#21
Copilot wants to merge 3 commits into
mainfrom
copilot/fix-snaborderedwindow-dragging-issues

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Feb 24, 2026

After dragging the window, the hit-detection area for the top border stays at the original position, and the resize delta accumulates incorrectly because mouse coordinates were being compared against inner-canvas layout values without accounting for WindowPosition offset.

Changes

  • OnMouseButtonDown: convert click position to inner-canvas space (LocalPos - WindowPosition) before passing to GetRegionAtPosition, and store the same offset position as DragStartPosition for resizing
  • OnMouseMove (resize): apply the same WindowPosition offset to the current mouse position before computing the resize delta, keeping it consistent with DragStartPosition
  • OnMouseMove (drag): upgrade invalidation from Layout to LayoutAndVolatility so the widget's cached geometry stays in sync with WindowPosition after each update
// OnMouseButtonDown — was passing LocalPos directly
const FVector2D CanvasLocalPos = LocalPos - WindowPosition;
const EWindowRegion Region = GetRegionAtPosition(MyGeometry, CanvasLocalPos);

// OnMouseMove (resize) — was using CurrentPos without offset
const FVector2D CanvasLocalPos = CurrentPos - WindowPosition;
const FVector2D Delta = CanvasLocalPos - DragStartPosition;

// OnMouseMove (drag) — was Layout only
OuterCanvas->Invalidate(EInvalidateWidgetReason::LayoutAndVolatility);
Original prompt

Fix SNaBorderedWindow Dragging Issues

Problem Description

The SNaBorderedWindow widget has two critical bugs with dragging functionality:

Issue 1: Mouse Movement Speed Mismatch

When dragging the top border, the mouse cursor and window move at different speeds, causing relative movement between the cursor and window. The window doesn't follow the mouse cursor properly.

Issue 2: Detection Area Doesn't Move with Window

After dragging the window once, the clickable detection area for the top border remains at the original position. Subsequent drag attempts only work if you click where the top border used to be before the first drag, not where it currently is.

Root Cause Analysis

Issue 1 - Mouse Speed Mismatch

The problem is in OnMouseMove during dragging. The code currently uses screen space coordinates for the delta calculation, but the geometry being invalidated is in local space. When using TAttribute<FVector2D>::CreateSP(this, &SNaBorderedWindow::GetWindowPosition) for the outer canvas slot position, the position is applied directly but the geometry transform isn't being accounted for properly.

Current broken code:

else if (bIsDragging)
{
    const FVector2D Delta = MouseEvent.GetScreenSpacePosition() - DragStartPosition;
    WindowPosition = DragStartWindowPosition + Delta;
    OuterCanvas->Invalidate(EInvalidateWidgetReason::Layout);
    return FReply::Handled();
}

The issue is that WindowPosition is being set in screen space but being used as a local canvas position. The fix is to ensure we're working consistently in the same coordinate space.

Issue 2 - Detection Area Not Moving

The problem is in GetRegionAtPosition(). This function receives LocalPosition which is already in the widget's local space (relative to MyGeometry), but then compares it against absolute pixel coordinates from Params (BorderLeft, BorderTop, BodySize.X, etc.) as if LocalPosition were in the inner canvas's coordinate system.

However, when the window is dragged and WindowPosition changes, the MyGeometry passed to mouse events is still relative to the outer canvas's coordinate system, not the inner canvas. The function should account for the window's position offset.

Current broken logic:

SNaBorderedWindow::EWindowRegion SNaBorderedWindow::GetRegionAtPosition(
    const FGeometry& MyGeometry, const FVector2D& LocalPosition) const
{
    // This checks LocalPosition against Params coordinates directly
    // But LocalPosition is in widget-local space, not inner-canvas space!
    
    if (LocalPosition.Y < Params.BorderTop &&
        LocalPosition.X >= Params.BorderLeft &&
        LocalPosition.X <  Params.BorderLeft + Params.BodySize.X)
    {
        return EWindowRegion::TopBorder;
    }
    // ...
}

Solution

Fix for Issue 1: Consistent Coordinate Space Handling

The dragging logic needs to work in the parent's coordinate space consistently. Since we're using a canvas slot position attribute, we should:

  1. Keep WindowPosition as the offset within the parent container
  2. Ensure the delta calculation matches the coordinate system of WindowPosition

Fixed code:

else if (bIsDragging)
{
    const FVector2D Delta = MouseEvent.GetScreenSpacePosition() - DragStartPosition;
    WindowPosition = DragStartWindowPosition + Delta;
    // No need to manually invalidate - the TAttribute will handle updates
    return FReply::Handled();
}

Actually, the current dragging logic looks correct for screen space. The real issue might be that the TAttribute isn't triggering layout updates properly. We need to verify the outer canvas slot is using the attribute correctly.

Fix for Issue 2: Account for Window Position in Region Detection

The GetRegionAtPosition function needs to work with the geometry that accounts for the window's current position. The LocalPosition parameter is already in the widget's local coordinate system, which should include the window position offset if the geometry is correct.

The issue is that we're comparing LocalPosition (which is relative to the widget's current painted position) against the static layout coordinates. We need to not adjust for WindowPosition because MyGeometry.AbsoluteToLocal() already gives us coordinates relative to the current widget position.

Wait - let me re-analyze. Looking at the code:

FReply SNaBorderedWindow::OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
{
    const FVector2D LocalPos = MyGeometry.AbsoluteToLocal(MouseEvent.GetScreenSpacePosition());
    const EWindowRegion Region = GetRegionAtPosition(MyGeometry, LocalPos);
    // ...
}

The problem is: MyGeometry represents the current widget's geometry, which changes when WindowPosition changes (via the TAttribute). So MyGeometry.AbsoluteToLocal() already accounts for the window's screen position. The LocalPos is correct relative to the ...

This pull request was created from Copilot chat.


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

…validation

Co-authored-by: SodiumZH <46860829+SodiumZH@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix SNaBorderedWindow dragging issues Fix SNaBorderedWindow drag detection and resize coordinate space bugs Feb 24, 2026
@SodiumZH SodiumZH marked this pull request as ready for review February 24, 2026 14:45
@SodiumZH SodiumZH closed this Feb 24, 2026
@SodiumZH SodiumZH deleted the copilot/fix-snaborderedwindow-dragging-issues branch February 24, 2026 15:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants