Drawing a slate widget at mouse Location

Hello every one… I want to replace my mouse cursor with a slate widget.

When I am using SCanvas, the location of the slate widget is appearing a lot off. Anyway how to do it?



    IconLocation.Bind(this, &SCursorItemWidget::GetCursorLocation);

    ChildSlot
    
    SNew(SCanvas)
    .Visibility(EVisibility::HitTestInvisible)
    + SCanvas::Slot()
    .Position(IconLocation)
    .Size(FVector2D(64,64))
    .HAlign(HAlign_Center)
    .VAlign(VAlign_Center)
    
    ]
    ];
     

I am getting the mouse position with something like this


    GameHUD->PC->GetMousePosition(MouseLoc.X, MouseLoc.Y);

But the mouse position is a lot off… Something similar to half of what it should be.


Image of what is wrong with .Position of canvas.

I am getting another problem. When I draw the mouse like this, any slate widget present below this is not responding to mouse clicks.

---------- Answers Till Now ----------


		SAssignNew(CursorItemUI, SCursorItemWidget)
			.InvStorage((StoragePtr));

		GEngine->GameViewport->AddViewportWidgetContent(
			SNew(SWeakWidget).PossiblyNullContent(CursorItemUI.ToSharedRef())
			);	

		CursorItemUI.Get()->SetVisibility(EVisibility::HitTestInvisible);

This solves the 2nd problem the 2nd Widget is still above and causes no click problems.

According to my answer Hub post,
Something like this should work


FSlateApplication::Get().GetCursorPos()

But sadly is not working

Cursor position is in absolute coordinates but SCanvas slots expect a position in relative coordinates. This is further compounded by the fact that the entire UI has a scaling factor applied depending on your resolution, so you have not only a translation offset but also a scaling offset.

Converting between the two is most easily done through FGeometry, using AbsoluteToLocal / LocalToAbsolute. In order for AbsoluteToLocal to mean anything to the canvas, though, it has to be the canvas’s geometry. Here’s an excerpt of how I’m doing it in our inventory:


FReply FInventoryPresenter::OnInventoryMouseMove( const FGeometry& InventoryGeometry, const FPointerEvent& MouseEvent )
{
	FVector2D InventoryMovePos = InventoryGeometry.AbsoluteToLocal( MouseEvent.GetScreenSpacePosition() );

	if( IsDraggingItem() )
	{
		InventoryWidget->UpdateItemDragPos( InventoryMovePos );

		return FReply::Handled();
	}
	
	return FReply::Unhandled();
}

My custom inventory SCompoundWidget exposes a delegate for OnMouseMove and my presenter registers for that. The SCanvas used for the drag and drop position is a child of that inventory widget and has the exact same geometry (no scaling, no padding, aligns set to fill), so that AbsoluteToLocal can return the proper coordinates. Then UpdateItemDragPos simply gets the Canvas slot for the drag indicator and manually updates its offsets.

If you can’t have the canvas take up the exact same geometry as a parent widget, you could also subclass SCanvas to override OnMouseMove and handle your positioning logic there.

-----------Edit------------

Well it worked thanks…

Can you show how did you expose a delegate to OnMouse move and register it?

I have not never used any delegates yet.

SWidget only provides function overrides for input events, not delegates, so I simply inspired myself from SBorder’s pointer event delegates. It has a both a OnMouseMove Slate event and a setter for it. Then, to register for that event, either use Slate syntax or a setter:


// Slate syntax
SNew( SInventoryWidget )
.OnMouseMove( this, &FInventoryPresenter::OnInventoryMouseMove )

// Setter
Widget->SetOnMouseMove( FPointerEventHandler::CreateSP( this, &FInventoryPresenter::OnInventoryMouseMove ) );

I use the setter in my case because I only register for MouseMove when a drag operation is in progress.