Best way to dynamically reposition a Slate widget?

I have a basic Slate button class derived from the SStrategyButtonWidget class in the StrategyGame sample.

I need to position this button directly under the mouse cursor at one point in the game. What’s the best way to do that? Clearly there’s no SetPosition() I can call – should I be using a callback set from my main UI widget’s Construct(), or setting it directly somehow?

Never mind – answered my own question. It seems like the way to do this is to declare it to be in the upper-left

.Padding(TAttribute(this, &MyClass::SomeCallback))

above the widget when you declare it, and then just set the left and top of the margin from this callback.

Hey i end up using this same solution, problem is the widget follow with some sort of lag. It’s not slow but it looks like its getting updated with a delay.
Do you have this same problem and did you find a way to make it better ?

Hi guys,

Slate widgets are aligned/positioned automatically using container widgets like VerticalBox or HorizontalBox. If you want to control exact position of a widget you can for example create a new window and put your widget inside this window. SWindow has MoveWindowTo() method that is what you are looking for.

So here is a very raw code that should help you moving your widget:

// create actual window with helper function (you can actually use `SNew(SWindow)` if you want but its just easier to use CursorDecorator)
TSharedPtr<SWindow.> MyWidgetWindow = SWindow::MakeCursorDecorator();

// Add widget you want to display to the window
MyWidgetWindow->SetContent( _Your_widget_here_);

// Add window to Slate but dont show it yet!
FSlateApplication::Get().AddWindow(MyWidgetWindow.ToSharedRef(), false);

Now you can move/show/hide MyWidgetWindow with functions like MoveWindowTo, ShowWindow, HideWindow

Hope that helps ; )

When i try to use window i end up with a bunch of errors. Window appears but they are not draggable, or take the fullviewport, or crash when you click the close button…, with the example you provide i just succeed to make it appear in the middle and i can’t close them or move them. I have to close the editor to make them disappear.

I would prefer to use SWindow rather than Soverlay or Scanvas but i can’t make them work.
Is it working for you with just that code ?

CursorDecorator is not a regular window, you can actually think of it as a container to other windgets. You wont be able to resize or move it with your mouse inside game- you can only do this via code. I didnt mention that but you also have to clean up after youre done using this window or else SlateApplication will crash. To remove window from memory you just call RequestWindowDestroy on a window in your end game code or in Widget’s destructor. I prepared a sample code presenting usage of this method. This is just a simple widget - SMyOverlay, that extends SOverlay widget and controls behaviour of mentioned window. All you have to do is call GEngine->GameViewport->AddViewportWidgetContent(SAssignNew(OverlayWidget, SMyOverlay)); somewhere in you game begin code and then call OverlayWidget->SetWindowContainerVisibility(true/false) to show or hide window whenever you want.

Code should be easy to understand but if you have any problems let me know.

#pragma once

#include "Slate.h"

class SMyOverlay : public SOverlay
{
public:
	SLATE_BEGIN_ARGS(SMyOverlay)
	{}
		SLATE_SUPPORTS_SLOT(SOverlay::FOverlaySlot)
	SLATE_END_ARGS()

	~SMyOverlay()
	{
		// REMEMBER TO CLEAN UP ! ! !
		if (WindowContainer.IsValid())
		{
			WindowContainer->RequestDestroyWindow();
		}

		WindowContainer.Reset();
	}

	void Construct(const FArguments& InArgs)
	{
		WindowContainer = SWindow::MakeCursorDecorator();

		FSlateApplication::Get().AddWindow(WindowContainer.ToSharedRef(), false);

		WindowContainer->SetContent(
			// Im using Stooltip only because it looks nicer than empty window
			SNew(SToolTip)
			.Content()
			[
				// Add whetever you want here
				SNew(STextBlock)
				.Text(FText::FromString("Window Container Text"))

			]);

		const int32 NumSlots = InArgs.Slots.Num();
		for (int32 SlotIndex = 0; SlotIndex < NumSlots; ++SlotIndex)
		{
			Children.Add(InArgs.Slots[SlotIndex]);
		}

		// uncomment to show window after construction
		//WindowContainer->ShowWindow();

	}

	virtual FReply OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) OVERRIDE
	{
		if (WindowContainer.IsValid())
		{
			// Update WindowContainers location, you can add offsets to mouse position here
			WindowContainer->MoveWindowTo(MouseEvent.GetScreenSpacePosition() + FSlateApplication::Get().GetCursorSize());
		}

		return SOverlay::OnMouseMove(MyGeometry, MouseEvent);
	}

	void SetWindowContainerVisibility(bool NewVis)
	{
		if (WindowContainer.IsValid())
		{
			// change WindowContainer's visibility
			NewVis ? WindowContainer->ShowWindow() : WindowContainer->HideWindow();
		}

	}

private:
	TSharedPtr<SWindow> WindowContainer;

};