#slate
1 messages · Page 15 of 1
Aren't you just trying to stop the underlying Window/Editor from being interacted with while that Modal is open?
If you have encountered that behavior in UE before, I would suggest you check how they did that.
I haven't encountered that yet really
Everything I've seen has a valid Cancel operation basically
mmmmm
Inside the content widgets I've handled most cases I can find of losing focus, submitting text other than pressing Enter, clicking off buttons
Thing is, the Modal function of GEditor says that the function doesn't return until the Modal window gets closed.
So... I could literally just not have that loop potentially? I'll try without
fwiw SlateApps Modal function says the same
Well, yeah.
The while loop is sketchy af :D

this is literally my first

omfg
You know the worst part
I read every single function other than that one
the AddWindowNative
AddWindow
why did I not
omfg
void UEditorEngine::EditorAddModalWindow( TSharedRef<SWindow> InModalWindow ) const
{
// If there is already a window active, parent this new modal window to the existing window so that it doesnt fall behind
TSharedPtr<SWindow> ParentWindow = FSlateApplication::Get().GetActiveTopLevelRegularWindow();
if( !ParentWindow.IsValid() )
{
// Parent to the main frame window
if(FModuleManager::Get().IsModuleLoaded("MainFrame"))
{
IMainFrameModule& MainFrame = FModuleManager::LoadModuleChecked<IMainFrameModule>("MainFrame");
ParentWindow = MainFrame.GetParentWindow();
}
}
FSlateApplication::Get().AddModalWindow( InModalWindow, ParentWindow );
}
void SGameplayTagPicker::OpenRenameGameplayTagDialog(TSharedPtr<FGameplayTagNode> GameplayTagNode) const
{
TSharedRef<SWindow> RenameTagWindow =
SNew(SWindow)
.Title(LOCTEXT("RenameTagWindowTitle", "Rename Gameplay Tag"))
.SizingRule(ESizingRule::Autosized)
.SupportsMaximize(false)
.SupportsMinimize(false);
TSharedRef<SRenameGameplayTagDialog> RenameTagDialog =
SNew(SRenameGameplayTagDialog)
.GameplayTagNode(GameplayTagNode)
.OnGameplayTagRenamed(const_cast<SGameplayTagPicker*>(this), &SGameplayTagPicker::OnGameplayTagRenamed);
RenameTagWindow->SetContent(SNew(SBox)
.MinDesiredWidth(320.0f)
[
RenameTagDialog
]);
TSharedPtr<SWindow> CurrentWindow = FSlateApplication::Get().FindWidgetWindow(AsShared() );
FSlateApplication::Get().AddModalWindow(RenameTagWindow, CurrentWindow);
}
Just as an example.
In my title I use FText::FromString() is there an issue vs using LOCTEXT
Helps just checking how Epic uses it in various places and picking the code that is the simplest.
FText is localizable. FString is not.
If you use FText::FromString, you basically skip the localization feature.
LOCTEXT is used if you want to have your stuff localized.
Ahhhhhhhh
There is a LOCTEXT_NAMESPACE defined in that class that I copied the code from.
Well this is for a plugin I'd like to not assume people don't want localization
That is used hand in hand with the LOCTEXT macro.
The Namespace is grouping the Text Keys.
And the first param of LOCTEXT is the Key.
here "RenameTagWindowTitle".
The second param is the default value that can be localized later.
That's fine for one-off FTexts, if you don't want to define a LOCTEXT_NAMESPACE for just one FText.
If you have multiple, then you should wrap them all with the #define and use LOCTEXT.
Ok that makes sense
I usually argue that it doesn't hurt setting up LOCTEXT properly from the start. One can still just not localize the text, but at least it's possible if it's ever needed.
For things that won't ever get a translation, using FromString is valid though.
I would like to allow for it to be a possibility not a requirement
I'll try the making the define
Then stick to proper LOCTEXT usage. Might also want to read the docs about this a bit, because there is a bit more to it.
But even if you set up the LOCTEXTs wrong initially, having them as LOCTEXT will make it easier to fix them later.
"wrong" here being something like not taking into account that a word could be spelled differently when more than one element is used.
E.g. 1 Task vs 2 Tasks.
Cause FText supports that automatically too.
that would be isnane
You won't die from having 1 Tasks or 2 Task in there for a while
But you can at least search for the LOCTEXT macro in your code base to fix them later.
ye that sounds nice
I'm inside this SGameplayTagPicker I don't see this
You sure, in the .cpp?
I see it right below the includes.
Yeah that's in the .cpp
having it in the .h would be troublesome
under the includes
My problem is I understand that. I should have known that. Why that just never registered in my head.... is... idk how to explain
So it looks like I need to define just the one part of the LOCTEXT and then the rest of it I don't even have to make that's just built in already...
just define the namespace
That's nifty
Yeah, don't forget to undef it at the bottom of the .cpp again

idk how that's Thank You
but it's from this discord so
That's Keanu Reeves saying thank you in some changing room of some evening show.
And no, it's not the "You are breathtaking" scene.
Wow that's so nice with this loctext
... can I use ternary operators inside this macro to set these strings
also no
There is more than LOCTEXT you can use, hence the suggestion to please read the docs.
Ok so I found out this is how you deal with delays properly.... while the top modal window is going, there's a special tick delegate that broadcasts out deltatime, so I can just create a little 1 second delay, call to destroy the ResultWindow modal, which then after that just calls to destroy the original modal window added
ResultWindow->SetContent(Container);
FSlateApplication& SlateApp = FSlateApplication::Get();
float DisplayCounter = 0.f;
FDelegateHandle Handle = SlateApp.GetOnModalLoopTickEvent().AddWeakLambda(this,
[this, &ResultWindow, &DisplayCounter, &Handle] (float DeltaTime)
{
DisplayCounter += DeltaTime;
if (DisplayCounter > 1.f)
{
FSlateApplication::Get().GetOnModalLoopTickEvent().Remove(Handle);
ResultWindow->RequestDestroyWindow();
}
});
SlateApp.AddModalWindow(ResultWindow, Window);
Window->RequestDestroyWindow();
I wanted a pop up that reads out success or not
ok this smooth
Everything is special in slate

There are notifications and what not you can spawn
Doesn't have to be a modal window
It must be a special widget type then that I don't know exists
I only read about windows and adding children to them and whatnot
Depends on what exactly you want to do.
and if I just call AddWindow or AddWindowAsNativeChild it won't render
There is, e.g., FMessageDialog::Open(..)
Oh I see, sure
But that doesn't allow me decent formatting
It just Pop up with this FText
Just kidding
Ye it's just this basic stuff
Which also just uses the ModalWindow in the end I guess.
umm how do I find in rider what's bound to that
I can only see where it's declared and where it's executed
FCoreDelegates::ModalMessageDialog.BindUObject(this, &UEditorEngine::OnModalMessageDialog);
Yeah, it comes from Dialogs.h/.cpp
There are some classes you can extend from, however.
SModalEditorDialog also looks interesting
Is templated, so you can define what the return is
Can only find one example, however.
class SControlRigDismissDependencyDialog : public SModalEditorDialog<bool>
man this stuff is wild
ProvideResult(false);
Guess that is also templated and then can be used to return
// Derived classes call this function from their widget events to close the dialog and return the result to the calling context
void ProvideResult(ResultType InResult)
{
Window->RequestDestroyWindow();
Window.Reset();
OnFinished.ExecuteIfBound(MoveTemp(InResult));
OnFinished = {};
}
Ah there is a second usage of it
class SFixupRedirectorsReport : public SModalEditorDialog<bool>
But they are both so simple.
Like, in a good way
How can you say Result.GetValue() if for example I want the result to be a USTRUCT
ResultType ShowModalDialog(const FText& InTitle)
{
Window = UE::Private::CreateModalDialogWindow(InTitle, AsShared(), Sizing, MinDimensions);
Window->SetWidgetToFocusOnActivate(GetWidgetToFocusOnActivate());
TOptional<ResultType> Result;
OnFinished.BindLambda([&Result](ResultType InResult) { Result.Emplace(MoveTemp(InResult)); });
UE::Private::ShowModalDialogWindow(Window.ToSharedRef());
return MoveTemp(Result.GetValue());
}
bool ControlRigDismissDependencyDialog::LaunchDismissDependencyDialog(const URigHierarchy* InHierarchy, const FRigElementKey& InChild, const FRigElementKey& InParent, const FRigHierarchyDependencyChain& InDependencyChain)
{
// todo?: compute a key to remember this setting and don't ask in the future
TSharedPtr<SControlRigDismissDependencyDialog> Dialog = SNew(SControlRigDismissDependencyDialog, InHierarchy, InChild, InParent, InDependencyChain);
const bool bDismissed = Dialog->ShowModalDialog(NSLOCTEXT("ControlRig", "DismissDependencyDialogTitle","Cyclic Dependency Found"));
return bDismissed;
}
Do I have to make sure I write a GetValue() override?
how does that work with a boolean
what boolean can GetValue
You might want to read that function once more
You should be able to do class SMyCustomEditorDialog : public SModaleEditorDialog<FMyStruct>
That's what I thought at first, but then I just missed the TOptional that just includes the template as well
And it's kinda cool it gives you a Lambda as well
And then:
TSharedPtr<SMyCustomEditorDialog> Dialog = SNew(SMyCustomEditorDialog, ..);
const FMyStruct Result = Dialog->ShowModalDialog(...);
Hope that helps, cause you should be quite free in how you can design that dialog due it being its own widget.
Should be able to just implement the Construct method as usual
Easily because it's got this UE::Private:: for putting it and adding it
Not the regular way with the slatapplication
lmao did I not keep going enough
It still creates a Window and tells the SlateApp to add it as modal
But it removes all the management of that for you
void UE::Private::ShowModalDialogWindow(TSharedRef<SWindow> Window)
{
GEditor->EditorAddModalWindow(Window);
}
Just a GEditor now
Yop
void UEditorEngine::EditorAddModalWindow( TSharedRef<SWindow> InModalWindow ) const
{
// If there is already a window active, parent this new modal window to the existing window so that it doesnt fall behind
TSharedPtr<SWindow> ParentWindow = FSlateApplication::Get().GetActiveTopLevelRegularWindow();
if( !ParentWindow.IsValid() )
{
// Parent to the main frame window
if(FModuleManager::Get().IsModuleLoaded("MainFrame"))
{
IMainFrameModule& MainFrame = FModuleManager::LoadModuleChecked<IMainFrameModule>("MainFrame");
ParentWindow = MainFrame.GetParentWindow();
}
}
FSlateApplication::Get().AddModalWindow( InModalWindow, ParentWindow );
}
There it isssssssssssssss
add modal
finally
TSharedRef<SWindow> UE::Private::CreateModalDialogWindow(
const FText& InTitle,
TSharedRef<SWidget> Contents,
ESizingRule Sizing,
FVector2D MinDimensions)
{
// clang-format off
return SNew(SWindow)
.Title(InTitle)
.SizingRule(Sizing)
.MinWidth(MinDimensions.X)
.MinHeight(MinDimensions.Y)
.ClientSize(MinDimensions)
.SupportsMaximize(false)
.SupportsMinimize(false)
.HasCloseButton(false)
[
SNew(SBorder)
.Padding(4.f)
.BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
[
MoveTemp(Contents)
]
];
// clang-format on
}
Ye and then it goes through and adds the TOptional and gives it to the lambda then calls that
Like, you can do all that yourself too
You are already doing that partially anyway
But this would remove the need of manually householding the window etc.
Yeah, cause it handles the actual closing through the content of the window
ye
I think the Delete Redirectors Dialog is using this
What's the difference between MainFrame parent window and the FGlobalTabmanager::Get()->GetRootWindow()
I use the root window which always returns the editor
in all this in the engine already uses this MainFrame
idk it has to load the module every time vs this little static getter
It returns the Editor because you are only one level deep.
And you are calling it from "within" the main editor window
If you were to undock a window and create a dialog from that, then the RootWindow would probably be that new window
Or if you start nesting dialogs.
While the MainFrame should always be the Editor iirc
Actually, maybe that's wrong.
Not 100% sur. MainFrame does set its own Windows as th RootWindow
Th other cases where it's used are for tooling windows.
Like, UE has standalone programs
void FTraceInsightsModule::CreateSessionViewer(bool bAllowDebugTools)
{
RegisterTabSpawners();
#if !WITH_EDITOR || defined(__INTELLISENSE__)
//////////////////////////////////////////////////
// Create the main window.
#if PLATFORM_MAC
const bool bEmbedTitleAreaContent = true;
#else
const bool bEmbedTitleAreaContent = false;
#endif
// Get desktop metrics. It also ensures the correct metrics will be used later in SWindow.
FDisplayMetrics DisplayMetrics;
FSlateApplication::Get().GetDisplayMetrics(DisplayMetrics);
const float DPIScaleFactor = FPlatformApplicationMisc::GetDPIScaleFactorAtPoint(
static_cast<float>(DisplayMetrics.PrimaryDisplayWorkAreaRect.Left),
static_cast<float>(DisplayMetrics.PrimaryDisplayWorkAreaRect.Top));
const FVector2D ClientSize(1280.f * DPIScaleFactor, 720.0f * DPIScaleFactor);
TSharedRef<SWindow> RootWindow = SNew(SWindow)
.Title(NSLOCTEXT("TraceInsightsModule", "UnrealInsightsAppName", "Unreal Insights"))
.CreateTitleBar(!bEmbedTitleAreaContent)
.SupportsMaximize(true)
.SupportsMinimize(true)
.IsInitiallyMaximized(false)
.IsInitiallyMinimized(false)
.SizingRule(ESizingRule::UserSized)
.AutoCenter(EAutoCenter::PreferredWorkArea)
.ClientSize(ClientSize)
.AdjustInitialSizeAndPositionForDPIScale(false);
const bool bShowRootWindowImmediately = false;
FSlateApplication::Get().AddWindow(RootWindow, bShowRootWindowImmediately);
FGlobalTabmanager::Get()->SetRootWindow(RootWindow);
lmao the mainframe sets it as the same
Yeah, I don#t think the MainFrameModule is used within standalone programs.
Or if you were to spawn a fully standalone window
mehhhhh idk if I'm doing all that
You aren't.
Even if you don't use the template dialog class, you should jsut use the GEditor call to add the modal window tbh
instead of through SlateApp
I mean I have to use SlateApp to get the ActiveTopMostWindow to add the primary Modal to it
Yeah, I would try to let UE do as much as possible
Unless that's in GEditor as well
Ye and that calls this
I'm confused what this references tho. What would you say that I should let UE do as much as possible
Boilerplate code in general
Ohhhhhh like isntead of manually managing the popup window
Really extend the MessageDialogue templated
and let that fire
Yeah, cause you might be missing something that they do but don't explain properly
gotcha
Like setting some property, passing some stuff etc.
E.g. Window->SetWidgetToFocusOnActivate(GetWidgetToFocusOnActivate());
(from the templated dialog class)
but isn't for focus only for input routing
It's just an example
oh
I would just use that template class unless it does something you don't want or lacks something you need.
well ye just stuff like that
And if that's the case I would try to just duplicate their stuff and stick to what they do as much as possible
some random edge case ruins the whole thing cuz 1 function or value I didn't set somewhere along this exact input click and results just doesn't handle this 1 specific case
vs using the UE stuff auto and it JustWorks™
Yeah, it can backfire if UE assumes that everyone wants the dialog to work like they think it should.
Which happens more often than not
But if it's doing what you want then use it
Well ye cuz actually
If I did that dialogue with the extended class
It would actually remove a ton of lines inside my lambda
well not a ton but still
this is all it is currently
But you're right I could ez format the extended dialogue widget
Those popups with the results is what I was trying to get properly
Your telling me that when the modal is active that it locks it up was the biggest help of this all because that was my whole concern was that it doesn't execute the next line until the modal is gone
Just this
SlateApp.AddModalWindow(ResultWindow, Window);
Window->RequestDestroyWindow();
Allows that main input window to stay alive until the ResultWindow is gone
which is literally the end of my lambda...
SlateApp.AddModalWindow(ResultWindow, Window);
Window->RequestDestroyWindow();
})));
FSlateApplication& SlateApp = FSlateApplication::Get();
SlateApp.AddModalWindow(Window, FGlobalTabmanager::Get()->GetRootWindow());
return bConfirmedPassword;
So then when that Window is destroyed, it immediately then returns the result
Can I ask why does UE sometimes to namespace enums like this? This is my first one I've made like this, but I've seen them all in my GAS code... like the EAbilityGenericReplicatedEvent::InputPressed for example... why use these over a standard regular enum?
It's old and outdated.
Just use normal enum/enum classes
ahhhhhhhhhhhh
lmao
nice so then I can turn them back into bitflags
Idk why I don't like switch I like Result & Success
@grave hatch hey happy new year! I just wanted to follow up as you mentioned this ages ago now.
Are you still working on a slate WYSIWYG project or is it mothballed? Been thinking about it for a while 😄
Mothballed for now. I've been thinking about it a lot recently, though, and I'd do it completely differently now.
what is the difference between umg and slate?
In fact i notice if i put == i have the possibility to enter the key manually
i will switch on the other #umg
Slate is what all UI in UE is based on.
UMG is the in-editor Blueprint wrapper for Slate.
So if you're doing things with widget blueprints and nodes and things, you're using UMG.
If you're doing things with c++, you're using Slate.
(with some exceptions!)
learn UMG to make a game
learn slate to make editor customizations, or to make special custom things for UMG
Hello Everyone!
I'm trying to set the position of a widget to be the exact same as another widget, and in order to do that, I call Get Cached Geometry, and then call Local to Viewport on it to set the other widget position. Yet the blueprint hover over sign specifically advises against using get cached geometry
And the blueprint hover tip does not say much about how to actually implement the alternative as someone who has not used slate before.
So my question is what is the alternative that solves the mentioned hysteresis problem, and how do I implement it. I have used both BP and C++ before.
Help is greatly appreciated!
You make the widget a child of the other widget.
And this results in?
Yes, but I know from experience that bugs have shown up when I used this in the same frame that the parent widget was instantiated
more context needed... is this a widget that normally floats elsewhere, and now you want it to follow another widget for a bit?
No I have a parent widget. Inside there are a bunch of widgets in a horizontal box. On spawn I want another widget to be positioned in the same position as it. Both are parented to the same canvas
I think I might have confused myself now
Why not just always have the widget there with an overlay and just hide it when it's not needed?
Because I have no idea how many of them will be needed at design time
So how do you know how many widgets there will be in the horizontal box?
you can add or remove things to a horizontal box, to an overlay, etc, whenever you want
I'm mainly also just trying to understand what the tool tip means as it specifically tells me to only use it as a last resort so I'm trying to figure out the intended way to do things
it just means that value will be old when you use it
It means that you don't position a widget programmatically with tickspace geometry, you use the proper hierarchy.
So basically just avoid setting widget positions with code?
feels like a major oversight with the UI tools then
well, you run into this in every walk of game dev... what's the position of something moving? it's always changing, if you aren't careful you might draw something based on last frame's data
putting stuff into a hierarchy lets the program simply update it in an order that (hopefully) works
whether or not that is the correct solution will, naturally, depend on the problem 😄
So does this basically mean that the tooltip overexaggerates the severity of the frame behind problem?
i would happily use cached geometry without any concerns at all if i needed to read the location of a widget that A) i know has existed for a while already and B) i know is not moving
mind you that still can result in visual uggers - e.g. if the user resizes the game exe window, that might cause an unexpected "movement" of widgets
So basically: Cached geometry is fine for widgets that are ticked, and that are still in movement. If a widget is moving, cached can cause it to lag behind.
And the tooltip exaggerates the problem
but everyone has a different threshold for "jank i am willing to accept to get this game shipped"
I don't understand why you can't put it into a proper hierarchy.
i would say the tooltip is perfectly accurate - you should NOT use this unless you have to
and i also still don't know what is stopping you from using hierarchy organization
Instead of putting widgets on a canvas that you need to place things on top of. Place overlays on teh canvas which have the base widget and add an overlayed widget on top when you need.
Alright I looked up information about the overlay widget and I think I know what you mean now.
Thanks @grave hatch and @stuck jewel for the assistance
What is the preferred way of binding enhanced input actions to slate UI navigation?
or triggering slate UI navigation at all besides what is defined in the FNavigationConfig class
I don't have a precise answer here, but are you aware of UCommonUserWidget::RegisterUIActionBinding already? This helped me to bind input actions to my UI, but I am using a combination of UMG and custom slate, so not sure if this would work for you as well.
I will take a look, thanks
Did anyone ever encounter NativePaint being called each tick? I am drawing some lines with slate in NativePaint, but only change what needs to be drawn very infrequently and call Invalidate only on changes. However, NativePaint is still called every tick, while I would have expected it to cache the painted geometry and only call NativePaint again after any Invalidate calls. I also verified that Invalidate is indeed never called and none of my widgets in the widgettree are set to Volatile. What could cause the repainting every tick?
as a quick idea i would look at how retainer boxes handle NativePaint()
quite possible that it's intended to be called each tick and that you have to check if it was invalidated 🤷
When my widget (action menu) is created, I manually set the focus to the first button, and HasFocus returns true on tick, but the 'OnClicked' event won't trigger with the Enter key until I navigate up/down once. Mouse clicks work immediately, but keyboard input seems ignored for the initial focused state. I've already set the Input Mode to UI Only and tried different 'Press Methods' like 'Button Press' in the interaction settings. How can I make the button recognize the Enter/Accept key immediately upon focus without requiring manual navigation first?
My suspicion is, it has something to do with the mouse cursor. I set it to not show, but it still kinda flies around.
This is the coolest slate widget
SNew(SObjectPropertyEntryBox)
.AllowedClass(InArgs._FoundAsset.GetClass())
.DisplayThumbnail(true)
.ThumbnailPool(MakeShareable(new FAssetThumbnailPool(64)))
.DisplayBrowse(true)
.EnableContentPicker(true)
.OnObjectChanged(FOnSetObject::CreateLambda(
[this] (const FAssetData& SelectedAsset)
{
OnNoteSelected.ExecuteIfBound(Cast<UUnrealNote>(SelectedAsset.GetAsset()));
}))```
I just created a target list that is being populatet from an array, and here the Enter Button works instantly. I cannot see any difference, other then the action menu described above is created manually in the designer because I know the items.
in case it helps, this is what the button widget looks like. maybe you see something suspicious.
This is #umg not slate
widget blueprints can't run in slate because the world clock isn't running or rendering
Also how big of a deal is it that I'm not using the construct in the .cpp file with these macros
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
I just use the construct in the .h file
This is slate
TSharedRef<SWindow> Window = SNew(SWindow)
.SupportsMaximize(false)
.SupportsMinimize(false)
.HasCloseButton(false)
.Title(LOCTEXT("NoteActor", "Choose Your Note"))
.SizingRule(ESizingRule::FixedSize)
.AutoCenter(EAutoCenter::PreferredWorkArea)
.ClientSize(FVector2D(250.f, 125.f));
Window->SetContent(
SNew(SBorder)
.VAlign(VAlign_Center)
.HAlign(HAlign_Center)
.Padding(5.f)
[
SNew(SNoteActorNoteSelect)
.NoteSelected(FNoteActorNoteSelected::CreateLambda(
[&] (UUnrealNote* SelectedNote)
{
Note = SelectedNote;
Window->RequestDestroyWindow();
}))
]);
FSlateApplication::Get().AddModalWindow(Window, FGlobalTabmanager::Get()->GetRootWindow());
What is the best way to profile and optimize my Slate widgets? The question probably also applies to UMG the same way and one might use the same tools, I just have no idea where to start really, as Unreal Insights doesn't give sufficient information for UI I feel. I have a Slate widget which gets redrawn constantly and I just can't figure out why and before figuring that out I thought i should check if its even worth optimizing that or if its basically "free" anyway.
Add scoped profiling stuff to your tick and paint functions.
I'm not sure if I should be asking in here or in #animation : I have a curve on my anim montages that changes the playrate dynamically. Is it possible to make an editor extension to modify the playrate of the Montage Preview so it matches the curve?
How can I add rounded corners to my widget? I've tried looking at how buttons/tabs do it but I can't seem to find anything useful
figured it out!
The secret sauce is FSlateRoundedBoxBrush:
ToolkitWidget =
SNew(SOverlay)
+ SOverlay::Slot()
.HAlign(HAlign_Right)
.VAlign(VAlign_Center)
.Padding(FMargin(4.0f))
[
SNew(SBox)
.MinDesiredHeight(600.f)
[
SNew(SBorder)
.BorderImage(new FSlateRoundedBoxBrush(FStyleColors::Panel, 16.f))
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
[
SNew(STextBlock)
.Text(INVTEXT("ToolkitWidget"))
]
]
]
];
An alternative is to use a 9-slice texture with rounded corners. That's basically what FSlateRoundedBoxBrush does.
This tool is part of an extension to the Static mesh editor that works with data in a UAssetUserData instance on the static mesh, and when the mode is entered I would like the details panel to show the details view for the AssetUserData. Is that possible to do without too much fuss?
like if it's possible I'd be fine with just slapping it in with GetInlineContent()
Yeah, it's pretty easy.
Just use the Property Editor Module to create a details panel.
Make sure it doesn't follow selection.
Cool, I'll give it a shot once I'm on lunch
is it just a matter of creating a TSharedPtr<IDetailsView> and slapping some widget it produces into GetInlineContent()?
No. You only need to create an IDetailsView. It will generate the entire details panel for you.
Unless you want to customise a property on your class.
I'm confused, how does the details view know to occupy this area if I'm not inserting it via GetInlineContent?
Well, I'm not sure what GetInlineContent() you're referring to.
If that's what puts a widget in that space... then yes, do that.
FModeToolkit::GetInlineContent(). Right now it's just set to a TextBox that reads "GetInlineContent"
so it looks like IDetailsView is actually a subclass of SCompoundWidget. So would it literally just be like this?
TSharedRef<SWidget> FMyToolkit::GetInlineContent()
{
UObject* MyUserData = /* grab the UAssetUserData */;
return SNew(IDetailsView) //Don't I need a concrete implementation?
.SetObject(MyUserData);
}
no worries!
The property editor module creates the implementation for you.
There's a method on the module.
Search the engine source for PropertyEditorModule.CreateDetailView
Plenty of examples you can just copy+paste.
sweet, thanks!
Np
Ugh, tried to do it on my lunch break but couldn't quite figure out how to get a pointer to the current asset. I'll have to figure it out after my dayjob
@grave hatch hate to poke you again over this but I am slamming my head against trying to get a reference to the current asset inside the static mesh editor. Would you happen to know anything about how to go about it? Clearly things like UV editor and Static Mesh Modeling mode do it
I'm not sure about it being the static mesh editor specifically is relevant. But TL;DR I have a UEdMode that is activated by a button on the top toolbar, and all the UI I've shown is hosted in an FModeToolkit that is created by the EdMode when the EdMode is activated
As part of integrating Jolt Physics into UE I have to make an equivalent of UBodySetup and associate it with a StaticMesh just like you would with a normal UBodySetup. For simplicity I'm just doing that as UAssetUserData that is associated with the static mesh, and I was hoping I could just extend the Static Mesh Editor with my UEdMode in a pretty straightforward way. (and if I could just get a pointer to the damn asset that's open in the hosting FAssetEditor, it would be very straightforward!)
What's the actual class of editor you have access to?
FAssetEditor probably isn't it.
As in, how are you adding your stuff in to the editor?
lemme grab my code
The stuff you have isn't really relevant. It's what it's being added to!
So I register the extensions in StartupModule:
void FJoltPhysicsEditorModule::StartupModule()
{
RegisterDetailsCustomization<FJoltStaticMeshComponentDetails>();
auto& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
PropertyModule.RegisterCustomPropertyTypeLayout(
FJoltBodyCollisionSettings::StaticStruct()->GetFName(),
FOnGetPropertyTypeCustomizationInstance::CreateLambda([](){ return MakeShared<FJoltCollisionLayersCustomization>(); })
);
PropertyModule.NotifyCustomizationModuleChanged();
FJoltStaticMeshCollisionEditorCommands::Register();
UToolMenus::RegisterStartupCallback(
FSimpleMulticastDelegate::FDelegate::CreateRaw(this, &FJoltPhysicsEditorModule::RegisterStaticMeshCollisionMenusAndToolbars)
);
auto& StaticMeshEditorModule = FModuleManager::LoadModuleChecked<IStaticMeshEditorModule>("StaticMeshEditor");
auto& ToolbarExtenders = StaticMeshEditorModule.GetAllStaticMeshEditorToolbarExtenders();
auto ExtendStaticMeshEditorToolbar = [this](const TSharedRef<FUICommandList> InCommandList, TSharedRef<IStaticMeshEditor> Editor) -> TSharedRef<FExtender>
{
TSharedPtr<FExtender> ToolbarExtender = MakeShared<FExtender>();
TWeakPtr<IStaticMeshEditor> WeakEditor { Editor };
// Command to enter/exit collision editing mode
InCommandList->MapAction(
FJoltStaticMeshCollisionEditorCommands::Get().ToggleJoltStaticMeshCollisionEditorMode,
FExecuteAction::CreateStatic(&FJoltPhysicsEditorModule::OnToggleStaticMeshCollisionEditor, WeakEditor),
FCanExecuteAction(),
FIsActionChecked::CreateStatic(&FJoltPhysicsEditorModule::IsStaticMeshCollisionEditorActive, WeakEditor)
);
return ToolbarExtender.ToSharedRef();
};
ToolbarExtenders.Add(IStaticMeshEditorModule::FStaticMeshEditorToolbarExtender::CreateLambda(ExtendStaticMeshEditorToolbar));
}
void FJoltPhysicsEditorModule::OnToggleStaticMeshCollisionEditor(TWeakPtr<IStaticMeshEditor> InEditor)
{
const auto PinnedEditor = InEditor.Pin();
if (PinnedEditor.IsValid())
{
if (!IsStaticMeshCollisionEditorActive(PinnedEditor))
{
PinnedEditor->GetEditorModeManager().ActivateMode(UJoltStaticMeshCollisionEditorMode::Id, true); // This is where my actual UEdMode gets turned on
}
else
{
PinnedEditor->GetEditorModeManager().ActivateDefaultMode();
}
}
}
oh
oh wait
OnToggleStaticMeshCollisionEditor might be the perfect place to try and inject a reference to the actual static mesh editor
and if so IStaticMeshEditor::GetStaticMesh() is exactly the function I've been trying to get my hands on
let me try that
Have you tried subscribing to virtual FStaticMeshEditorOpenedEvent& OnStaticMeshEditorOpened()
and then getting the mesh from that?

that is an idea as well
the only thing is I'm not sure if that would cause an issue if a designer opens two different static mesh editors at once
and actually that makes me wonder if there is a single UEdMode that all static mesh editors share or if each one gets its own copy of my EdMode, which could also throw a wrench into things
let me try some stuff
thanks for being a rubber duck lol
Wait wait wait
waiting
Why aren't you using auto ExtendStaticMeshEditorToolbar = [this](const TSharedRef<FUICommandList> InCommandList, TSharedRef<IStaticMeshEditor> Editor) -> TSharedRef<FExtender> to get the editor and mesh?
I mean, you are.
I don't see the issue!
that doesn't instantiate my UEdMode or or FModeToolkit which is where I need the data
literally all that does is make this button
But that is triggering your second function?
Which also has a ref to the editor.
Can you not get a reference to your uedmode there and set the mesh on it?
oh yeah that's exactly what I'm saying
Ah ha!
lemme go give it a shot
hmm, so I'm having issues with GetStaticMesh() being nullptr, but this is definitely a step in the right direction
void FJoltPhysicsEditorModule::OnToggleStaticMeshCollisionEditor(TSharedRef<IStaticMeshEditor> InEditor)
{
if (!IsStaticMeshCollisionEditorActive(InEditor))
{
InEditor->GetEditorModeManager().ActivateMode(UJoltStaticMeshCollisionEditorMode::Id, true);
auto Foo = InEditor->GetEditorModeManager().GetActiveScriptableMode(UJoltStaticMeshCollisionEditorMode::Id);
UJoltStaticMeshCollisionEditorMode* Bar = Cast<UJoltStaticMeshCollisionEditorMode>(Foo);
checkf(InEditor.ToSharedPtr().IsValid(), TEXT("%hs 1"), __FUNCTION__); // Passes
Bar->ActiveEditor = InEditor.ToSharedPtr();
checkf(Bar->ActiveEditor.IsValid(), TEXT("%hs 2"), __FUNCTION__); // Passes
checkf(Bar->ActiveEditor.Get() == InEditor.ToSharedPtr().Get(), TEXT("%hs 3"), __FUNCTION__); // Passes
}
else
{
auto Foo = InEditor->GetEditorModeManager().GetActiveScriptableMode(UJoltStaticMeshCollisionEditorMode::Id);
UJoltStaticMeshCollisionEditorMode* Bar = Cast<UJoltStaticMeshCollisionEditorMode>(Foo);
Bar->ActiveEditor = nullptr;
InEditor->GetEditorModeManager().ActivateDefaultMode();
}
}
TSharedPtr<SWidget> FJoltStaticMeshEditorModeToolkit::GetInlineContent() const
{
const auto& PropertyEditor = FModuleManager::Get().LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
FDetailsViewArgs DetailsViewArgs;
checkf(OwningMode->ActiveEditor.IsValid(), TEXT("%hs"), __FUNCTION__); // Fails
return SNew(STextBlock).Text(INVTEXT("GetInlineContent"));
}
so for some reason the reference to the editor instance is valid when we activate the EdMode, but becomes invalid by the time we get to GetInlineContent 😓
I'm running on ~4 hours of sleep so this may just have to be something I pick up tomorrow
Got the details panel working! Thanks for your help!
got some sleep and actually thought about the lifecycle of everything with a brain that wasn't completely fried 😅
the problem was I was trying to do this:
InEditor->GetEditorModeManager().ActivateMode(MyMode::Id, true);
MyMode-ActiveEditor = InEditor;
but by the time we get to the second line MyMode has already called Init, Enter etc, and it has to set up the inline content at that point, so of course ActiveEditor was null at that point!
so instead I just made it tolerate not having a valid ActiveEditor and made a setter function that propagates it to the toolkit, sets up the details panel etc and calls FModeToolkit::UpdatePrimaryModePanel()
I also had a weird issue with what I can only guess was a TSharedPtr cycle that was preventing me from opening reopening an asset that was previously open in the static mesh editor
It was probably the UEdMode holding a TSharedPtr to its toolkit and the toolkit holding a TSharedPtr back to the UEdMode
but idk it works now so I'm not touching it 😆
Ah ha. Nice.
That basically describes everything ever made in UE! 😂
hello again slate. i dread coming back here.
does anyone know where the text of a SET key is altered in UInputKeySelector, and its inner widget, the SInputKeySelector?
ive been trying to dive into it, and i dont see a function where the text is actually set. I only see that the FInputChord is sent into a chain of delegates and FieldNotifications and cant find the actual meat and potatoes.
Id appreciate if someone with deeper knowledge would look into it for me.
dont get fooled by the SetKeySelectionText() method, which is for the actual selection system and not where the final selected key is set.
I'd advise putting a breakpoint wherever the delegates start popping off.
See what sets them.
that would make sense, but i was too lazy to dig into it further tbh. i cant find any way to manually set the text, and i dont wanna go through whatever detail customization override i would probably need to do to set the underlying slate text.
for now, i made the font size 1 (so it doesnt impact the sizing), set all its color options to 0 opacity and overlayed it with my own textblock. had to also copy the methods in the FInputChord over to my widget, and now i can manually alter the text however i wish
I made a custom radial panel on Slate
same logic as overlay or canvas with panel + panel slot, but for radial menus
Hi, how i can find is cursor is over UI? I hace a camera system that move camera when cursor is near borders but i need to avoid that if there are UI on borders
For people Goolging this, also consider taking a look at FSlateApplication::LocateWindowUnderMouse void ReportWidgetsUnderMouse() { // Get a reference to the singleton instance of the slate application. FSlateApplication& MySlateApplication = FSlateApplication::Get(); // Find a "widget tree path" of all widgets under the mouse cursor. // T...
Works for any widget. It has to be hit testable I think
Is it hard to make XY plot or bar plots widgets in slate? 🤔
no, but if you don't have too many data points you might aswell do it in UMG
make a blank widget and override the OnPaint function and use DrawLine method to draw the graph
pretty similar in Slate (onPaint/DrawLine(s))
I am getting started with editor extensions and plugins. Right now, I'm trying to figure out how to add a command when right clicking a Sequence Section in the Montage Editor.
How is it best done?
Segments, I think they are called?
is there any other menu entries with clear names to search for in the context menu?
I think I found my problem. I was trying to add to Animation Editor, but I searched for the menu I wanted, AnimSegmentsLabel, and see it's actually in the Persona module instead.
Hmm. It's still not working.
I am trying to add a new option to Here:
{
FPersonaModule& PersonaModule = FModuleManager::Get().LoadModuleChecked<FPersonaModule>(TEXT("Persona"));
TSharedRef<FExtender> SegmentMenuExtender(new FExtender());
SegmentMenuExtender->AddMenuExtension(FName("AnimSegmentsLabel"), EExtensionHook::After, TSharedPtr<FUICommandList>(),
FMenuExtensionDelegate::CreateRaw(this, &FSuperManagerModule::SegmentMenuBuilder));
PersonaModule.GetMenuExtensibilityManager()->AddExtender(SegmentMenuExtender);
}
void FSuperManagerModule::SegmentMenuBuilder(FMenuBuilder& MenuBuilder)
{
MenuBuilder.AddMenuEntry
(
FText::FromString(TEXT("Split")),
FText::FromString(TEXT("Split into two identical sections.")),
FSlateIcon(),
FExecuteAction::CreateRaw(this, &FSuperManagerModule::HandleSplitButtonClicked)
);
}``` Here is my code.
The SegmentMenuBuilder() function never runs, even if I right click the segment.
Maybe I am binding to the wrong thing?
hmm
this menu isn't registered through UToolMenu, it's just manually created on the spot
maybe this system is supposed to work... i'm not sure, i have extended menus differently before than this
It looks like it checks for extensions in SAnimSegmentsPanel.cpp line 308, so there must be some way to get access to the menu builder.
I'll check deeper in the stack to see.
I see you're right, that it just creates a new MenuBuilder within STrack::SummonContextMenu and doesn't seem to have any external calls or delegates.
i looked around for a few minutes to see if i could find any FExtenders that were beign used with menus just push raw to FSlateApplication like this... i didn't find any but i only looked at like three or four, like 1% of the engine's examples
if true could be a case where this would require engine mods 
There is a member variable called EditorActions that might be a way to add new actions? Except it's protected and no accessors anywhere.
That makes me very mad!
@stuck jewel Is there another place I can add a tool to persona to acces the Segments?
afraid i have never even opened persona before let alone looked at its source code 😄
Persona is the standard Unreal animation editor/previewer 😮
I have actually never opened it 😄
I submitted a bug to Epic. Can't do much else at this point. 😠
Wow am I steamed about this.
Are you sure you've got the right hook?
You can turn on a display of all menu hooks in situ.
Yes, I have the right hook. But examining the code shows that the persona module doesn't access external hooks when initializing the context menu,
SAnimSegmentsPanel.cpp line 742
Creates a FMenuBuilder within the function, with no external access
hey, is there quick way to navigate from widget to file in CPP? some quick button, like F12 for functions or classes 😛
I'd generally do ctrl+, type something like "ScatterPlot" and let visual studio tell me where it is.
Oh I figured it out, it's caused by retainer boxes, after disabling them it works now. But they are quite important for the visuals of the game... What am I supposed to do???
@obtuse prairie did you ever solve the issue with tables and slateim?
im getting this issue
and dont know why
ha x)
i mean
even if i just strip it just to call that its happenign
i think
se
even in a very simplistic thing
@drowsy galleon not sure if you are around, sorry for the ping
i put this to a very simple test case
FKaosGameplayDebuggerWidgetTest::FKaosGameplayDebuggerWidgetTest()
: FSlateIMNomadTabBase(TEXT("Kaos Gameplay Debugger Test"), TEXT("KaosDebugger.ShowTest"), TEXT("Opens the Kaos gameplay debugger"))
{
for (int32 i = 0; i < 1000; i++)
{
ReplicatedActors.Add({ TEXT("TestActor"), TEXT("TestActorClass"), TEXT("Dormant"), 1.f, 1.f });
}
}
void FKaosGameplayDebuggerWidgetTest::DrawContent(float DeltaTime)
{
DrawTable(this);
DrawTable(this);
}```
static void DrawTable(FKaosGameplayDebuggerWidgetTest* TestWidget)
{
SlateIM::MaxHeight(200.f);
SlateIM::BeginTable({ .SelectionMode = ESelectionMode::Single });
SlateIM::BeginTableHeader();
SlateIM::NextTableColumn(TEXT("Actor"));
SlateIM::Text(TEXT("Actor"));
SlateIM::Maximize();
SlateIM::Padding(FMargin(0));
SlateIM::InitialTableColumnWidth(80.f);
SlateIM::Maximize();
SlateIM::HAlign(HAlign_Center);
SlateIM::AddTableColumn(TEXT("Class"), TEXT("Class"));
SlateIM::Maximize();
SlateIM::HAlign(HAlign_Center);
SlateIM::AddTableColumn(TEXT("Dormancy"), TEXT("Dormancy"));
SlateIM::Maximize();
SlateIM::HAlign(HAlign_Center);
SlateIM::AddTableColumn(TEXT("Upd. Frequency"), TEXT("Upd. Frequency"));
SlateIM::Maximize();
SlateIM::HAlign(HAlign_Center);
SlateIM::AddTableColumn(TEXT("Upd. Priority"), TEXT("Upd. Priority"));
SlateIM::EndTableHeader();
SlateIM::BeginTableBody();
for (const FKaosGameplayDebuggerWidgetTest::FKaosReplicatedActorInfo& Info : TestWidget->ReplicatedActors)
{
if (SlateIM::NextTableCell())
{
SlateIM::Text(Info.ActorName);
}
if (SlateIM::NextTableCell())
{
SlateIM::Text(Info.ClassName);
}
if (SlateIM::NextTableCell())
{
SlateIM::Text(Info.Dormancy);
}
if (SlateIM::NextTableCell())
{
SlateIM::Text(FString::Printf(TEXT("%.1f"), Info.NetUpdateFreq));
}
if (SlateIM::NextTableCell())
{
SlateIM::Text(FString::Printf(TEXT("%.1f"), Info.NetUpdatePriority));
}
}
SlateIM::EndTableBody();
SlateIM::EndTable();
}```
even happens with a single table
if i just do SlateIM::Fill(); DrawTable(this); it fills and doesnt cause the jumping
SlateIM::BeginVerticalStack();
SlateIM::Fill();
DrawTable(this);
SlateIM::Fill();
DrawTable(this);
SlateIM::EndVerticalStack();``` doing this, the jumping comes back
The vertical stack also needs to fill its container
I'm also doing some work on slate im these days, @mental badge, if you have any issues.
Not sure i understand? Even if i don't specify anything just the table it happens
Thanks I'm just struggling on why this is happening. It only happened since we moved to 5.7. I used a version of slate in before 5.7 and it was fine
You're using the source from git?
it reminds me of an issue but cant remember
So, to be sure, it's trying to draw the same table twice in the video? But it also happens if you just draw it once?
Did you include the vertical stack when drawing the table once?
No but if i draw a single table with fill above it. It does work
If i draw two tables then it bugs again
Have you tried fill, vertical stack, fill, table, fill, table?
Yes. Sec
I think that's what Mr Deez was suggesting.
{
SlateIM::Fill();
SlateIM::BeginVerticalStack();
SlateIM::Fill();
DrawTable(this);
SlateIM::Fill();
DrawTable(this);
SlateIM::EndVerticalStack();
}```
i did this
Alright.
Have you checked that the data is actually the same each time the table is drawn?
Alright
Hmm. I got this issue when I was playing around with something where it wasn't rendering rows (at all) that weren't visible.
but i am showing all rows
Does the example stuff work?
but
which makes me think something weird is going on if even the examples is causing some weirdness
look at the bit towards bottom
That does look weird. I'll have a look tonight / tomorrow and see if I can find any issues.
but i couldnt get any simpler
than my test case window
there nothing else going on
static void DrawTable(FKaosGameplayDebuggerWidgetTest* TestWidget)
{
SlateIM::BeginTable({ .SelectionMode = ESelectionMode::Single });
SlateIM::BeginTableHeader();
SlateIM::NextTableColumn(TEXT("Actor"));
SlateIM::Text(TEXT("Actor"));
SlateIM::Maximize();
SlateIM::Padding(FMargin(0));
SlateIM::AddTableColumn(TEXT("Class"), TEXT("Class"));
SlateIM::AddTableColumn(TEXT("Dormancy"), TEXT("Dormancy"));
SlateIM::AddTableColumn(TEXT("Upd. Frequency"), TEXT("Upd. Frequency"));
SlateIM::AddTableColumn(TEXT("Upd. Priority"), TEXT("Upd. Priority"));
SlateIM::EndTableHeader();
SlateIM::BeginTableBody();
for (const FKaosGameplayDebuggerWidgetTest::FKaosReplicatedActorInfo& Info : TestWidget->ReplicatedActors)
{
if (SlateIM::NextTableCell())
{
SlateIM::Text(Info.ActorName);
}
if (SlateIM::NextTableCell())
{
SlateIM::Text(Info.ClassName);
}
if (SlateIM::NextTableCell())
{
SlateIM::Text(Info.Dormancy);
}
if (SlateIM::NextTableCell())
{
SlateIM::Text(FString::Printf(TEXT("%.1f"), Info.NetUpdateFreq));
}
if (SlateIM::NextTableCell())
{
SlateIM::Text(FString::Printf(TEXT("%.1f"), Info.NetUpdatePriority));
}
}
SlateIM::EndTableBody();
SlateIM::EndTable();
}
FKaosGameplayDebuggerWidgetTest::FKaosGameplayDebuggerWidgetTest()
: FSlateIMNomadTabBase(TEXT("Kaos Gameplay Debugger Test"), TEXT("KaosDebugger.ShowTest"), TEXT("Opens the Kaos gameplay debugger"))
{
for (int32 i = 0; i < 1000; i++)
{
ReplicatedActors.Add({ TEXT("TestActor"), TEXT("TestActorClass"), TEXT("Dormant"), 1.f, 1.f });
}
}
void FKaosGameplayDebuggerWidgetTest::DrawContent(float DeltaTime)
{
SlateIM::Fill();
SlateIM::BeginVerticalStack();
SlateIM::Fill();
DrawTable(this);
SlateIM::Fill();
DrawTable(this);
SlateIM::EndVerticalStack();
}```
its literally this
Ooh that looks like a new bug
Nothing stands out, but the fact that there's an issue in the example makes me think something got introduced in the recent changes to tables, since you're running latest
Oh really... I hadn't seen that
let me boot up 5.7
original, and see
but it didnt happen, when i took slateim
before 5.7
cause i pulled it from main before 5.7 was released
and i remember my tables working
so it doesnt happen in the example on 5.7 release
but i remember my version i pulled from main never had flickering tables
but when we upgraded to 5.7 and i ditched the plugin we pulled from main, it started happening
SlateIM: Fix Viewport roots preventing mouse input for the whole viewport
DoubleDeez
DoubleDeez
committed
on Jul 16, 2025 < this commit was the last we had from UE5-main
cause this fixed a bug i had with viewport widgets
so something past this commit, caused my issues, and something very recently has broken it in main on the examples
the issue doesnt happen if you dont render a whole bunch of items
if i only put 30 items in each table
300 items in each table
@grave hatch ^
maybe that gives some more insight
Yeah. Thank you.
The strange thing is - I unfurled every single thing in the location table.
It didn't flitter about even with a scrollbar.
And it was doing this way back before the recent table changes, so we may need to go back even further to find the cause
@mental badge Do you happen to know the date when you checked out a working version? Like from 5.6 or something?
Even going back to the CL when that dynamic table was added, it was already flickering. 🙁
hmm
it was this one
SlateIM: Fix Viewport roots preventing mouse input for the whole viewport
the last change i pulled in
maybe slate under the hood is broken
they did all that invalidation stuff in 5.7
maybe thats whats causing it(?)
i bet its underlying slate issue
and my money is on invalidation stuff
@grave hatch^
Thanks!
yeah its 100% a slate issue, i think its linked to invalidation stuff
cause it feels like the widgets are being invalidate
strange thing is
the sample works fine
even with 500 items
More and more and more janky behavior with slate as 5 progresses. Opening instanced struct dropdowns is a freaking travesty
The was the one that worked?
Okay. I'll see what I can find out. Thank you!
Any suggestions or anything for SlateIM btw?
This is a fun one. Not even changing anything in the UI and it's flittering about.
it likes those belly rubs
What the hell
after updating an STextBlock do I have to do something so its width changes to fit ?
That'd depend on its container/slot.
It should get a different requested size and it may or may not change size depending on the container.
im trying to use .HAlign(HAlign_Fill) on parents but i cant get the correct behavior
One think I do notice about the above issue, back in the "working" CL, when you regenerate the items, they don't bunch up. But on the later CLs you get 2 lines and they change size when you scroll. Maybe it's the scrollbox not updating correctly?
What do you get?
Have you used the widget debugger to check what the size actually is?
if i add one it spreads the Hbox and doesnt do anything to the textblock
nvm i mixed it with something else, adding a HAlign does nothing
What fixed it?
Oh. Mixed, not fixed!
uh, if i add extra characters it increases in size
feel like it matchs size, but with a few slate units smaller
Were you not doing this before?
i mean that "Hello" with have "llo" cuted, so if i want to show anything i have to add a few empty spaces
(Fishy) are you using the widget debugger at all?
it's going to tell you (well, strongly hint) what is governing size
Desired Size = the size it wants to be.
Actual Size = the size allotted to it by its container/slot.
If AS < DS, the container is the issue.
well text isnt at desired
border isnt to
parent HBox is
border and HBox has HAlign(HAlign_Fill)
Can't say what the issue is, only that it's a container issue.
go up the hierarchy until you find out what is forcing it down to 70 width
usually without any constraints, desired sizes "bubble upwards"
if child's desired size is 69, the container above it probably has a desired size of 69... unless that container is inside a box with a max width
Then it'll be the widget switcher or its container.
its alredy with fill HAlign
try making your two horizontal boxes .Autowidth and not HAlign
(this might not be what you want, but just to learn if nothing else)
Autowidth = make my desired size whatever size my contents want... often for horizontal boxes you want everything autowidth, with one HAlign(Fill) somewhere to stretch
or, everything AutoWidth and then the horizontal box just floats with some extra space left or right in its higher-level container
And same for vertical boxes, you want autoheight.
im more familiar with slate now
but still don't know how to add new category sections 🤔
is it part of IDetailLayoutBuilder?
sometimes if you use too many "fill" settings, slate might have trouble figuring out how big to actually make them. being explicit about some items reduces the amount of crap to solve sizes for
what is a "category section" to you
You'd add a customisation and inside the customisation you get the option to edit/add categories.
Which appears to be that class, yes.
virtual IDetailCategoryBuilder& EditCategory(FName CategoryName, const FText& NewLocalizedDisplayName = FText::GetEmpty(), ECategoryPriority::Type CategoryType = ECategoryPriority::Default) = 0;
I got category, but its just "row"
void FSplineMeshedDetails::CustomizeDetails( IDetailLayoutBuilder& DetailBuilder )
{
DetailBuilder.GetObjectsBeingCustomized(SelectedComponents);
// Gather the selected components
// Create a custom "Spline" category
IDetailCategoryBuilder& SplineCategory = DetailBuilder.EditCategory(
"SplineMeshedComponent",
FText::FromString("SplineMeshedComponent"),
ECategoryPriority::Important
);
auto& grpPoints = SplineCategory.AddGroup("Points", FText::FromString("Points"), false, true);
auto& grpTangs = SplineCategory.AddGroup("Tangents", FText::FromString("Tangents"), false, true);
SplineCategory.SetSortOrder(0);
// adding buttons and members below
I would like button like "General" or "rendering"
At the top? The blue ones?
ye
That's done through details panel sections. You can add your own and specify which categories go into it.
I'm forget if you can do that per details panel (with a custom panel) or jsut globally.
🤔 well kinda important difference, I would like to make it part of plugin ;d
Section->AddCategory("Shape");``` is an example of what we did.
If you want to add them to every type of object, you can make the first parameter "Object"
thanks 🤔
should I add 2 sections for actor component and actor or AC is enough? 🤔
nope, its propagated up :d
thanks ❤️
Adding too many sections is gonna piss people off.
nah, I just wanted to combine them :D, but it works on component
not more then unreal 
😛
hey, quick question,
do I need to store refs to all slate objects? Or is it fine to do operations on copy? 🤔
auto& TangModify = grpTangs.AddGroup("Custom", FText::FromString("Custom"), false);
If it returns by ref, store it by ref.
Unless it's some simple POD class, like FVector2D.
("simple")
well, you should think about it.
this is for a category customization?
if you store a copy of the added group, then when you start editing this new group, will you be editing the actual displayed group, or will you be editing... a local copy that gets thrown away when the function ends
as Daekesh said, look at what it returns. if it's returning a ref, it's (very very very likely) giving you the actual instance you need to work on
so if you decide to make a copy of that, well
Yes I did notice that later. but stupid gpt made all property handles by pointer LOL
I customized everything almost, Component works fine, params and fuctions.
But for actor I can't make it work to cast to actor and trigger function :/
CollisionRow.AddWidgetRow().WholeRowContent()[SNew(SButton).Text(FText::FromString("Overlap test")).OnClicked_Lambda(
[this]()-> FReply {
for ( auto& ob : this->SelectedActors ) {
if ( auto act = Cast<ASplineMeshed>(ob.Get()) ) {
act->OverlapActorAlongSpline();
}
}
return FReply::Handled();
}
)];
@mental badge could I ask you to test something your end, see if it stops the tables going nuts?
what is it (not) doing?
it does not trigger, I would see logs
are you using your debugger
logging is for watching things that progress over time (or consolidating info to compare lots of event data easier), not checking instant "bang" logic
I mean actor function works fine.
But this slate button does not trigger it
it just calls function on component ;d
void ASplineMeshed::OverlapActorAlongSpline()
{
TArray<FSplineMeshedOverlapResult> result{};
SplineMeshedComponent->DetectObjectsAlongSpline(result, CollisionParams);
YASIUSPLINELOG_FNAME(Log, "Manual collision check found: {0} overlap/blocked objects", result.Num());
}
so use your debugger
do you know how to use breakpoints
and stepping over/into/out of
yes, does it work for lambdas?
rider works pretty well, can't remember if VS does
oke gonna see
unless you're on a very old version of rider
that's it, time to initiate r v vs war #82127
battleground: #slate
(i would not enter this fight, rider does a couple of very small but very very annoying things with debugging that it has absolutely no excuse to be doing lol)
array is emptyl ol
debugger wins again!
I forget how strong is this mythical technology
I have no idea, is there problme cause I did register two layouts? 🤔
FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
PropertyModule.RegisterCustomClassLayout(
USplineMeshedComponent::StaticClass()->GetFName(),
FOnGetDetailCustomizationInstance::CreateStatic(&FSplineMeshedDetails::MakeInstance)
);
PropertyModule.RegisterCustomClassLayout(
ASplineMeshed::StaticClass()->GetFName(),
FOnGetDetailCustomizationInstance::CreateStatic(&FSplineMeshedActorDetails::MakeInstance)
);
TSharedRef<FPropertySection> Section = PropertyModule.FindOrCreateSection(
"ActorComponent",
"YasiuSpline",
FText::FromString("YasiuSpline")
);
Section->AddCategory("YasiuSpline");
Is it being registered?
yes, I see actor variables nested properly
i think i would expect the actor one to display when you click on the actor, and i would expect the component one to display when you click on the component...
are you just asking if registering more than one is allowed? yes the engine has a million of these registered at any given time
(what problem are you having)
still the same, empty array for ActorDetailCustomization
class FSplineMeshedDetails : public IDetailCustomization {
public:
static TSharedRef<IDetailCustomization> MakeInstance();
virtual void CustomizeDetails( IDetailLayoutBuilder& DetailBuilder ) override;
protected:
TArray<TWeakObjectPtr<UObject>> SelectedComponents;
};
class FSplineMeshedActorDetails : public IDetailCustomization {
public:
static TSharedRef<IDetailCustomization> MakeInstance();
virtual void CustomizeDetails( IDetailLayoutBuilder& DetailBuilder ) override;
protected:
TArray<TWeakObjectPtr<UObject>> SelectedComponents;
};
so where do you populate it
i think i found mistake
DetailBuilder.GetObjectsBeingCustomized(SelectedComponents);
magic line 🤔
So... you weren't populating the array?
It was mystery haha how it fills that variable
Did you expect it to magically fill itself? 🙂
Ye, I was thinking how does it know how to populate variable with changed name
👏 works
thanks for something
😄
Somehow, My child actors got reversed order of properties 🤔
Component is shown first, then child variable and then parent variables
Ok
Im reading variable from Child class USplineMeshedComponent_Extension but I hold that object as parent pointer 🤔 , any suggestions how to get that property?
UPROPERTY(BlueprintReadWrite, VisibleInstanceOnly, Category="YasiuSpline")
TObjectPtr<USplineMeshedComponent> SplineMeshedComponent;
TSharedRef<IPropertyHandle> ExtendedParams = DetailBuilder.GetProperty(
GET_MEMBER_NAME_CHECKED(USplineMeshedComponent_Extension, ExtendedSpline)
);
if ( ExtendedParams->IsValidHandle() ) {
CmpRow.AddPropertyRow(ExtendedParams);
}
Get the child property handle?
? 🤔 from what
The current property.
You'll need to be more specific about what you mean by "I hold that object as parent pointer"
Youi mean the property is for a parent class?
In actor I got component of type USplineMeshedComponent
but for child actors classes I also put USplineMeshedComponent_Extension into that pointer in CDO
UPROPERTY(BlueprintReadWrite, VisibleInstanceOnly, Category="YasiuSpline")
TObjectPtr<USplineMeshedComponent> SplineMeshedComponent;
Maybe I could loop Array and get properties but im not sure if thats valid way
The property handle has a way to get the object it's a property handle for.
Get that object, try to cast to the subclass you want. If successful, add the subclass property?
TArray<UObject*> ExternalObjects;
for ( TWeakObjectPtr<UObject> ob : SelectedComponents ) {
if ( auto cmp = Cast<USplineMeshedComponent_Extension>(ob.Get()) ) {
ExternalObjects.Add(cmp);
}
};
if ( ExternalObjects.Num() >= 1 ) {
SplineCategory.AddExternalObjectProperty(
ExternalObjects,
GET_MEMBER_NAME_CHECKED(USplineMeshedComponent_Extension, ExtendedSpline)
);
}
External works
yup, works fine for multiple selection
and adding to group works too
FAddPropertyParams params{};
CmpRow.AddExternalObjectProperty(
ExternalObjects,
GET_MEMBER_NAME_CHECKED(USplineMeshedComponent_Extension, ExtendedSpline),
EPropertyLocation::Common,
params
);
👍
sorry didnt see this
Just submitted (about an hour ago) a potential fix, it's pretty simple. It was still misbehaving a little when I was testing last night, but today it's not. So shrug.
Not sure how long these things take to get on git, but the change was moving a couple of lines inside FSlateIMTableRow::UpdateChild
else
{
Children.Add(Child);
// Modifying the child count requires a rebuild to handle the case where we need to create/destroy the expander arrow and child widgets
if (OwningTable)
{
OwningTable->RequestRebuild();
}
}``` is how the function now ends if you wanna test it.
@mental badge
It's only a partial fix, there's still some jittering to solve
did anyone mess with the major pita to have a custom default tooltip widget? patched my engine -_-
thanks ill take a look
void FSlateIMTableRow::UpdateChild(FSlateIMChild Child, int32 Index, const FSlateIMSlotData& AlignmentData)
{
if (TSharedPtr<FSlateIMTableRow> ChildRow = Child.GetChild<FSlateIMTableRow>())
{
ChildRow->UpdateColumnCount(ColumnCount);
ChildRow->SetOwningTable(OwningTable);
}
if (Children.IsValidIndex(Index))
{
Children[Index] = Child;
}
else
{
Children.Add(Child);
}
// Modifying the child count requires a rebuild to handle the case where we need to create/destroy the expander arrow and child widgets
if (OwningTable)
{
OwningTable->RequestRebuild();
}
}
``` what was the change?
oh only on adding
instead of outside
will try it
if it works better than it did, ill be happy and can finish off my changes
that is a lot better @grave hatch
i can actually work with this, thanks
still some little issues
but this at least makes it usable
Great! Are the issues just when scrolling now?
I noticed issues when expanding and collapsing tree rows, too, but not sure how to fix that one.
Will need to prod the tree class a bit I think.
Great!
lol, how do I hide button in BP? 🤨
i did world check and its fine
but hiding button would be better I think
The same code change needs to happen for FSlateIMTableBody's code - I totally forgot about that. It was already fixed on main and should be on git by now, but if you haven't updated already.
you work for Epic ?
Contractor.
Great!
just need to find out why my table is no longer scrollable
something is wonky haha
wish there was an easy way to see all the stuff
like a SlateIM visualizer
or something
Widget reflector works on SlateIM stuff
You have to mentally remap the swidgets to SlateIM calls though
**Question: **Is there a way I can convert
FVector2D MousePos = FSlateApplication::Get().GetCursorPos();
to viewport coordinates?
Background:
Currently I am using this but it fails to take into account my os taskbar and top bar, so its throwing off my calculations for the current mouse position because my coordinate space starts at the top left of the ue application window not the top left of the screen (os window space)
Attached is an annotated screenshot of the issue I am trying to solve
So far I have 2 possible approaches.
- Get slate cursor position in the correct coordinate space
- Cache UMG base mouse position (Viewport + dpi scaled) -> convert it to slate position and paint that (I tried this and so far I have failed.)
WIdget is placed based on umg coordinates retrieved like so :
FVector2D ScaledMousePos = UWidgetLayoutLibrary::GetMousePositionOnViewport(GetWorld());
**
Ideal solution: **
Get the cursor position relative to the correct coordinate system, this makes the widget api a little nicer as I dont have to manually continually update the mouse position externally
first we should ask: what exactly are you drawing - are you drawing stuff "in game"? or is it more like an editor debug overlay, to be drawn over top of the editor viewport?
for the latter, this is some code i am using
if (World->IsPlayInEditor())
{
UGameViewportClient* GameViewportClient = GEditor->GameViewport;
if (!GameViewportClient)
{
return;
}
TSharedPtr<SViewport> ViewportWidget = GameViewportClient->GetGameViewportWidget();
if (!ViewportWidget.IsValid())
{
return;
}
FVector2D GlobalMousePos = FSlateApplication::Get().GetCursorPos();
// Check if mouse is within viewport
const FVector2D AbsoluteMousePos = FSlateApplication::Get().GetCursorPos();
if (!GameViewportClient->GetGameViewportWidget()->GetCachedGeometry().IsUnderLocation(AbsoluteMousePos))
{
return;
}
FVector2f MouseScreenPos = GlobalMousePos - ViewportWidget->GetCachedGeometry().GetAbsolutePosition();
...
```
Lots of debug logic to try to figure out wtf is causing my math to be wrong.
What I actually want to make ultimately is a radial menu like this:
if this is for a normal "game feature" like an in-game radial menu then i think you shouldn't be trying your approach at all
Ingame radial menu. It should be programmable, context sensitive and nestable
you should just e.g. be using player controller to project/deproject world locations to screen locations
This makes sense for spawning the actual widget. I am planning to do this later to seed the button data based on the actor and a trace of the hit target
However I still need to be able to draw a line from the center of the radial to the current mouse pos, which is widget space only and not world based
I will examine the code you posted for a bit, It may be helpful . I appreciate the feedback
Also I am trying to make it umg friendly so my specific use case based on world coordinates in a specific not general case. I inted to package this up in a plugin give it a few skins, and put it up on fab as well so there is that too
you need to take 5 minutes and skim-read the whole header of APlayerController
look at all the functions it has, and stop hurting yourself 😄
WIll do
also unreal has radial menus
exactly like this im sure in commonui or something
i have seen it
I could probably make it work if I wanted to but id rather do my own in slate
I dont know how far cui radial goes but I still want my own custom painting and id rather deal with slate than common ui.
Im not going to go so far as to say the cui radial is a bad solution for my use case, I would rather do slate though. Ive already dealt with common ui plenty and this way is better for me and what I am doing
same, for the slices
Don't forget to set your OS dpi to like 1.5 after you get it working so you can realise that's totally fucked all your calcs.
I can totally see this. An ideal solution will just be me figuring out what magic library function I need to get the position in the correct coordinate system.
That was the solution for me spawning the widget in the correct place.
FVector2D ScaledMousePos = UWidgetLayoutLibrary::GetMousePositionOnViewport(GetWorld());
10 hours of screwing around with math and then I find this function.
Realistically it would be really freaking odd if there weren't a way to go from umg coordinates -> slate coordinates at all in the engine. umg widgets are just wrappers around slate, so there has to be a freaking way to do it easy-peasy. I just havent found it and noones told me. That just means noone knows the answer though, not that there is no answer.
When I call:
FVector2D GlobalMousePos = FSlateApplication::Get().GetCursorPos();
It does:
GetCursorPos() Function description:
Absolute coordinates could be either desktop or window space depending on what space the root of the widget hierarchy is in.
Returns:
Transforms AbsoluteCoordinate into the local space of this Geometry
This means is can return coordinates relative to A or B in the picture
When I call:
FVector2D GlobalMousePos = FSlateApplication::Get().GetCursorPos();
auto LocalPos = AllottedGeometry.AbsoluteToLocal(GlobalMousePos);
AllottedGeometry.AbsoluteToLocal(GlobalMousePos) corrects assuming that GetCursorPos() is returning based on B as the base coordinate space.
For my case GetCursorPos() is returning based on A. This results in my corrected cursor position being off based on the position of the editor window.
This is what is killing my math.
I am quite close to the solution, just not quite there yet. I still need to understand slate root widgets and this GetCursorPos() function. Once I have the understanding the fix will take 1 line I bet.
is there a reason why you don't want to just use the simple tools in player controller? (e.g. for mouse cursor position)
is there a specific reason why you want to do custom drawing in slate?
He's a masochist!
Solution:
TSharedPtr<SWindow> Window = FSlateApplication::Get().FindWidgetWindow(AsShared());
if (Window.IsValid())
{
FVector2D MousePos = FSlateApplication::Get().GetCursorPos();
FVector2D WindowSpace = MousePos - Window->GetPositionInScreen();
FVector2D Local = AllottedGeometry.AbsoluteToLocal(WindowSpace);
}
I have a high tolerance for friction.
I tried this ages ago
PC get position I screwed around with so much yesterday
trying to draw in-game widgets by running your OS's game window position manually is psychotic
Now test that dpi setting.
It should be fine because I already have dpi running. I can tell when I am not taking dpi into account correctly because as I change the position of the mouse on the screen it will skew based on where it is in the viewport
The further I get from the top left the farther it will drift, either to the top left of where it should be or to the bottom right of where it should be.
I will screw around with my dpi settings though to satisfy you ❤️
that's uhh... a description of engine DPI
I also now have a permanent 200% dpi. Helps when looking at these things.
and actually now i realize i didn't test my own plugin's popup menus at other DPIs, away i go 
Hehe. Good luck!
Set application scale to 2.0, it works flawlessly. Away to costco I go!
I'd try OS dpi scaling if. It already.
the OS viewport math position still works fine but other things break, bleh
ok 200% is painful to look at and i am not looking forwards to getting old and needing this sorry lol
Lol
oh man this is gross. GetDesiredSize returns an unscaled value... and GetApplicationScale returns 1.0 when OS is set to 200%
sounds like a problem for future me
Application scale isn't the OS value, tis the one from the editor settings.
The scale on the geometry takes into account OS dpi.
(good stuff, right?)
No, not good stuff lmao
thank heck, pretty easy fix.
TSharedRef<SWidget> MyWidget = ...
MenuWidget->SlatePrepass();
FVector2D WidgetSize = MenuWidget->GetDesiredSize();
// The widget draw size needs to be multiplied by DPI to obtain the size the widget would have on screen.
const TSharedPtr<SWindow> TopLevelWindow = FSlateApplication::Get().GetActiveTopLevelWindow();
const float ScaleDPI = TopLevelWindow ? TopLevelWindow->GetDPIScaleFactor() : 1.f;
WidgetSize *= ScaleDPI;```
I have the same problem too also. it isnt scaling up. I will probably need to fix that. I didnt even notice. My get desired size isnt multiplying in the scaling
Do you have access to layout scale?
virtual FVector2D ComputeDesiredSize(float LayoutScale) const override; < - I have been able to scale it with this.
float ScaledRadius = Radius * LayoutScaleMultiplier;
const float TotalWidth = (ScaledRadius * 2.f) + MaxChildWidth;
const float TotalHeight = (ScaledRadius * 2.f) + MaxChildHeight;
This was simpler for me
ah thanks good to know, in my case no i'm drawing from "higher up"
With a really long pencil.
im in dire need of help on setting up a widget cause its driving me crazy at this point. Is this the right place to ask
ah thanks! i somehow managed to figure it out, it was a UMG related thing!
yep it's local space size. IIRC LocalToAbsolute() should do the trick for sizes too, not only for coordinates (including DPI stuff). You can call it either on AllottedGeometry or CachedGeometry depending on your case.
Here is example, actually it's done via GetRenderBoundingRect():
```
AllottedGeometry.GetRenderBoundingRect(FSlateRect(0.f, 0.f, DesiredSizeLocal.X, DesiredSizeLocal.Y));
Upd: nevermind, it's a bit different, BoundingRect makes a rect big enough to "contain" passed rect after rotation.
Anyway I think LocalToAbsolute() is the key
BoundingRect (red),
InputRect - grey
IIRC you just need to make extra call to LocalToAbsolute() with Rect(0,0) so it would be TopLeft corner, while Desired size is BottomRIght.
Finally to get scaled size just do the Right - Left and Bottom - Top to get rid off the offset part of the Allotted/CachedGeometry internal Transform. 
Btw Absolute DesiredSize doesn't mean actual size on screen, it's indeed desired and may be clipped still
Has anyone ever made a dashed line / dashed box outline in Slate? This is for an editor widget.
there's the built-in marquee brush, what are you doing
@stuck jewel in my plugin I display cards that represent configuration slots.
When one isn't properly configured I wanted to draw a dashed outline around it.
FAppStyle::GetBrush(TEXT("MarqueeSelection"))
```cpp
auto ExtendedHandle = DetailBuilder.GetProperty(
GET_MEMBER_NAME_CHECKED(USplineMeshedComponent_Extension, ExtendedSpline),
USplineMeshedComponent_Extension::StaticClass()
);
if ( ExtendedHandle->IsValidHandle() ) {
CmpRow.AddPropertyRow(ExtendedHandle);
}
Found better way to get properties from child classes.
Previous solution was still showing duplicate properties in detail window :d ( i missed it cuz It was wrong category)
so here i got a button, which is inside a horizontal box that is inside a grid (no the one you can above)
im setting the max width of the button so it matchs the other grid, but i cannot find how to let the button/horizontal box take vertical space to fit the text
end goal is to allow the text to warp and take more space vertically
Have you set the text to allow wrapping?
On the STextBox
its a sbutton
Which contains text?
i was using the SButton Text method
i switch to Content then add a STextblock inside that allows wrapping, but didnt exapanded
it removed the "..." tho
nvm, missed AutoWraping with policy
thanks !
Np
is there a slate widget to have 2 scrollbox so i can move around like in a graph ?
You can put a scorllbox inside another scrollbox.
I would advise using external scrollbars for that.
Something like this.
I don't recall if there's a built-in widget that does it. But you can do this.
what is an "external" scrollbar ?
don't know why but it looks like it should be placed in grid box, would it be better? 🤔
can scrollbox scrol content of other slot ?
A scrollbar can...
Of a scrollbox.
Use a grid if you want. I'd use a set of vertical/horizontal boxes.
Don't use a wrap box?
It'll wrap when it reaches its width limit.
Also #umg
Can someone tell me how to separate the icon & text from the three dots?
The compile button is a good example. Should I use something else and not InitComboButton?
Suggestion: Check teh tooltip for that button, find it in the engine source, find out where either that command or that button is created. See what they do.
yea, use widget explorer to find that widget
it is just two buttons. one with image-text content, another with image
I think it's just a HorizontalBox that contains another Horizontal box for the icon and text, + a button slot.
Damn this tool is cool
That doesn't tell the whole story (it's a horizontal block, etc.)
It's part of a menu which isn't usually created with slate.
(directly)
It's, as you said, create with InitComboButton or some other menu method.
I'm looking to do the following, and having a hard time figuring it out - it seems like it may actually be impossible without engine changes??
I'm looking to -
-
Remove any default call backs for buttons when dealing with widget focus and navigation. By default, arrow keys will call navigation by a certain direction and such. I want to remove this functionality (which I think can be done by overwriting NavigationConfig so that the keys aren't associated with anything).
-
Create a way to do the same navigation logic, but from a function somewhere else. I want to be able to call some function like
Navigate(EDirection::Right)and it tries to move right from the currently focused widget just like the current keypresses do. I don't want to create my own grid / widget tracking / whatever system to call 'set focus' on, I just want to mimic existing functionality.
It seems that slate is super engrained with the input system for whatever silly reason, and that a lot of these functions are not actually exposed publicly, but I may be missing something.
Can anyone advise?
I'm not on my PC but you can overidde a virtual function OnNavigationSomething in c++ when using CommonUI. It allows you to redefine what happen when going left/right
I've used it for example to change slider/enum values in many project settings
You can use interface functions OnNext / On previous called on the widget you have the focus. It will allow to easily define such behavior
(Sorry if I have misunderstandood @halcyon flume )
I'm not sure as your comment is vague, sorry.
I'm not trying to adjust the navigation/ focus logic. I'm just trying to call it from a public function without requiring a key press.
Like when you press the right arrow, slate changes focus to the widget to the right of the current focused widget.
I want to do that, but from a public function
what should i extend to add a new menu and entries in the Content Browser "Create" asset menu ?
It's done through the asset definition.
Iirc.
Check out UAssetDefinition and its subclasses. I may be getting the names slightly wrong...
yeah. you often want to inherit yours from UAssetDefinitionDefault, or look at other existing base classes nearby
you may also still need a UFactory? i can't remember (yeah pretty sure you do)
the AssetDefinition system replaces AssetTypeActions mostly
I think you may need a UFactory too, yeah.
important part for my case is that im not doing that for a custom asset
eh?
and iirc you can inject menu entries to create w/e you want
seen it in the past, but didnt save the blog post
like for e.g i want a menu entry to create a BP subclass and a data asset object instance
have you turned on the menu extension points viewer to see if it gives you any hint
ill check that tmrw back at work
can i tell a container slot to fill space, but inner content like a textblock stays at desired size ?
got a slot i want to fill space, but button fills the space instead of keeping desired size
I would add a dummy SSpacer beside the button, or you might be able to wrap the button in an SBox (with forced width)
button has only one content slot, so you add a SBox/Overlay etc, and add other content to it
Hey everyone 👋
I'm having an issue that I was hoping someone could help me with 🙏
Basically I have a 3D widget (using a Widget Component with a World Space widget) and I'm trying to navigate it using the focus system, but also while listening to input actions from an input context.
After digging through the internals a bit, I realized that the Input Actions callbacks are never called when focus is on a 3D widget because that happens when the focus is on the Game Viewport (which isn't the case when focus goes to a WidgetComponent widget apparently).
Has anyone faced this issue before and, if so, have you found a way to solve it or work around it?
Thanks in advance!
Can anyone point me towards a tutorial for Slate Styles?
Not off hand, but the engine styles are pretty good sources. Just avoid the starship file... there's so much stuff in it you'll drown.
FAvaMediaStyle is one I just randomly looked at that's pretty short and gives you the basics.
Thanks. If I search that name I'll see how it's made and also how it's applied?
I want to be able to change the background color of a property editor based on the content's values.
Styles can't really do that. You'd need something custom for that
Depending on what it is, of course.
Styles are just static values. The background image or colour or something.
They can't, themselves, respond to the dynamic values in a widget.
Gotcha. So I should be working directly with a color attribute?
I found a way to change text color (foreground) but not background color.
You could use a FSlateColorBrush as the background image and then change the colour.
@wispy bramble
Okay. Background image is an existing property of Detail View?
Just one property, for example.
And you want the entire row to change, or just the value part (on the right)?
You'd use a property customisation that uses a whole row widget replacement, not just a value widget replacement.
Or customise the name and value widgets independently.
You'd get a break in between them there, though, where the splitter is.
Understood. If I wanted the entire row I'd need to do a full override and rebuild along with a background?
You can get the default widgets that it would have used and place them on to a widget that you make... but you can't access the splitter location (it's private iirc) so you can't make it work nicely with the rest of the panel.
I'd go with just customising the name and value separately.
I can edit the engine if I need to.
I still need to find how to layer elements on top of eachother. I found the FColorBlock
it would help if you described where you're starting from... a custom class containing these? a struct type you can add to anything else as a property? something else?
a details customization, which can be created for any class, can be used to control the drawing of the whole details panel row by row
a property type customization, which can also be created for any struct or class, can be used to control the drawing of the property itself within a containing row widget
@stuck jewel Let's say I'm making a details customization for a particular class. I have a subset of particular properties that I need to have their background colored differently based on if their value is within a particular range.
so working "from above" (details customization) you would look at each property as you draw it, either drawing it normally, or doing something custom
Yes. How does one properly do "layering" in a slate widget?
are you looking for SOverlay?
If you're looking to put a background behind something, SBorder is also an option.
I'll look at both. Thank you.
I assume that SBorder has a single child slot but allows formatting of the edges and back of the child?
there are UMG equivalents, right? I could try making something in UMG first
hey all, i'm looking at FSlateVertex and can't figure out how to access the SecondaryColor in material editor?
so, looking at SlateVertex.usf and SlateElementPixelShader.usf it seems like SecondaryColor is not passed to the FMaterialPixelParameters(maybe i'm missing it though?) so i noticed in SlateVertex.usf that UVArrays 0's out uv channel 5-7, so i just added the secondary color there so i can access it.
"Border" afaik
TIL that if you make a custom details view widget it inherits the default elements but not the default formatting of the elements. 😠
So I have to make a custom widget, then copy the default row values into the custom widget, then make whatever additions I want.
How is the logical viewport resolution, in the editor preferences for the new window in pie, being calculated?
For example Samsung Galaxy S9 (Adreno) has it set to 624x1280 although the resolution is at 1440x2960. I can't find a single formula or whatever that leads to that result. Additionally, I have tried setting my scale setting on windows to 100% instead of 125% and restarting but that didn't change anything. On top of that, The DPI Curve also doesn't seem to be the missing puzzle piece.
If anyone knows even a tiny thing to guide me into the right direction, I would appreciate it a lot! Thanks in advance
I believe it has something to do with the device profiles, in this case "Android_High", but that doesnt change the fact that these logical resolutions are coming from somewhere
are there any events on FSlateApplication or else to listen to global OnMouseXXX events ?
did you check IInputProcessor ?
no, will look at it
although im not sure its a valid place to change the mouse event data
its for #cpp message
so UGameViewportClient::CreateGameViewport is only called in GameEngine
how can i override the viewport client in editor engine to ?
because it seems like its not possible because SPIEViewport subclasses it
You'll have to find where the viewport is instantiated and backtrack
Hello Everyone!
Quickly would like to ask if anyone knows how to set the font size of a STextBlock
Help is greatly appreciated!
it's part of the FontInfo iirc!?
yeah, you can get base Font from textblock style, set size and assign it to textblock
Wait can I get more details
if you have TSharedPtr<STextBlock> - GetFont - set size - SetFont
if you making new block - SNew(STextBlock).Font( font with size set )
And apparently it is deprecated
do not look at tooltips, look at header source and code you written
in case you used some deprecated constructor
I typed SNew(STextBlock)
.Font(FSlateFontInfo("Roboto", 56)) inside the construct for a SCompoundWidget
so constructor taking font name is deprecated
FSlateFontInfo LayoutFont = FAppStyle::Get().GetFontStyle("NormalFont");
LayoutFont.Size = 56;
now use it
Okay now it works, although FontSize is now known as just Size
Thanks for the help!
Hey, i don't know if this is the best place for this, but I've been losing my sanity to this bug for a few days and it seems to be traceable to slate.
When using both mouse/keyboard and gamepad (at least on Xbox controller) the OnInputModeChanged delegate from CommonInput returns some weird results. First found this issue in my project, then in Lyra too. So you start by using the mouse to look around, and then you open the menu in lyra from the gamepad (tapping the menu button illustrates this; holding it makes the input mode makes it fire 3 times and settles on gamepad). At first it returns Gamepad input method, but when you release the button it returns keyboard again, thus the UI indicators still show keyboard instead of gamepad and doesnt focus the widget for gamepad. Pressing any gamepad button again fixes it for that instance. However, if you use the mouse to look again, the issue returns. At first i thought it was because of the way focus uses the synthetic cursor behind the scenes, but then it should've happened at every focus change.
Ultimately I found someone else on the forums with the same issue here and then tested with printing the cursor position in viewport. It seems that windows moves the cursor in the backend and Unreal detects this as moving back to keyboard in FSlateApplication::OnMouseMove ? I hope this helps somebody not lose 3 days debugging
.
It's actually totally impossible to detect whether it was a gamepad virtual move or a cursor one.
It's really frustrating.
Like I was trying to find a way to completely block certain inputs (actually just limit it to 1 type, e.g. mouse or gamepad) but it was impossible to fix the actual issue being reported. Moving the mouse from the game viewport over to another tab/app (without even alt tabbing) triggered virtual events that you couldn't determine the source of, unless you started capturing raw input.
And even if you did, you'd have to change how the mouse events worked to include the source of the input (gamepad vs mouse) in the mouse events.
And it'd involve a lot of individual widget code to detect whether you cared which input it was.
A whole shitshow.
(i was trying to prevent focus loss from common widgets when you alt-tabbed or were forcibly tabbed out by a debugger etc.)
That's even worse than I imagined
. I started adding gamepad support to our game early on and it kinda worked, but now, when getting into the small details (i.e. UI indicators and navigation paths in the menus) I'm getting second thoughts. In-game it just works, but the navigation and focus path just seem stuck together with hopes and dreams, most of the times it works, and sometimes it just shits the bed
. I honestly expected control mappings for both gamepad and keyboard to be the worst part, but that worked a lot better than I thought.
I also found that it's really complicated to add a modal character window like in diablo 3/4, in which you can use mouse/gamepad to navigate character screen while also moving in-game. And after searching a lot for a solution to that I also found out that not many UE projects use that kind of approach. Instead they clearly delimit between UI only and Game only, with very little hybrid input.
Honestly for that kind of approach, I would treat menu navigation like enhanced input binds and perform manual navigation.
Solves every headache.
We have a similar system with text chat. And a keybind-like menu system where you can quickly go through options like a gamepad radial menu.
For both those system we don't leave game-only mode, we just use an input preprocessor to intercept the valid "temporary ui input" so it never gets to the game inputs.
So you can type 'w' but the action bound to 'w' never occurs if you have text chat open.
The one caveat for that is that virtual mouse movements (from the mouse or gamepad) completely ignore input preprocessors.
THANKS EPIC!
Many things to thank epic for 😂
I still remember when navigation rules in the design view would randomly reset on recompile and I had to manually init all of those rules with the set rule nodes. Fun times
Lol
For a second there I thought I simply forgot to add them
Question: in 5.7, what happens if you recompile a umg widget you're actively using in a PIE session?
Does it just disappear?
As far as I've done that it mostly crashes my engine
I make it a point to stop anything I'm doing in pie before modifying Umg stuff
I've had the reflector crash the engine quite a lot too
🤷♂️ I just work around that now, not much lost but I'd rather the feature didn't exist instead of it being sometimes fine and sometimes not
Oof
Hello everyone!
I'm currently trying to draw a User Widget inside of a editor tab. I want to draw it in a 16x9 aspect ratio, representative of how it would look if drawn in a 1920x1080 resolution, but scaled to fit the tab. However no matter what I do, it seems to always scale to fill the editor tab instead. I have tried using scale boxes but it still fills the entire tab.
Help will be greatly appreciated!
Sounds like you need to set the scale box's min and max aspect ratio to 16/9
Also #umg
I have tried it but the widget is still not representative of how it would look at 1920x1080. As for why I posted it in slate instead of UMG, this is more slate than UMG because I'm using slate to draw a UMG widget, which for these purposes it does not matter much the settings of the UMG widget
if you have umg widget - all you need is call TakeWidget, which will construct and return the SObjectWidget that can be used in slate
there was an article on unreal garden on how to make aspect ratio box - that will constrict content to specific aspect ratio and align it
but isn't the umg preview panel can change the canvas size to specific for preview purposes?
I have looked at the UMG widget preview and its ability to lock the widget size seems to be what I'm looking for. However since I'm trying to show the widget inside a different editor tab, it isn't really a solution and more of a reference for what I'm trying to make
by using https://unreal-garden.com/tutorials/ultrawide-ui/ as reference make custom SBox with customized OnArrangeChildren with ratio constratint, for example my impl was like this:
SNew(SAspectRatioConstraintBox)
.AspectRatio(FIntPoint(16, 9))
.ContentAlign(HAlign_Center, VAlign_Center)
[
MyWidgetPreview->TakeWidget()
]
@kindred nova I have now managed to set it to be 16x9 properly. However the problem now is that the widget shown is always the wrong size, so elements drawn on it will be way bigger than if they are drawn in the player viewport. It also does not change size when the panel size is changed, which is a problem
For context, this is the code that lays out the panel with the UserWidget, located in registerTabSpawners
InTabManager->RegisterTabSpawner("WidgetPreview", FOnSpawnTab::CreateLambda([=, this](const FSpawnTabArgs&)
{
return SNew(SDockTab)
[
SNew(SBox)
.HAlign(HAlign_Fill)
.VAlign(VAlign_Fill)
.MinAspectRatio(1.77f) //AKA 16x9
.MaxAspectRatio(1.77f)
.WidthOverride(1920)
.HeightOverride(1080.0f)
[
SNew(SOverlay)
+SOverlay::Slot()
[
//Background image
SNew(SImage)
]
+SOverlay::Slot()
[
widgetToDisplay->TakeWidget()
]
]
];
}))
Help is again very appreciated
yeah, pretty much you do what i expected.
thats why i said i had own slate widget with layouting children
just using SBox as is won't give result
also you don't want to Fill but Center
because after aspect ratio adjustement you will end up with blank spaces and need to align content
SDockTab
SBox with Width/Height set for dock tab itself or min/max desired size
SOverlay
SImage for background
SAspectConstraintBox or try SBox(for align) + SBox with AspectRatio
Content
there is no need to cheap out on slate widgets when prototyping layout, you can have more nested boxes then compress things
@kindred nova Not sure if this is the correct implementation but here it is, furthermore the problem of the contained widget in the panel being bigger compared to the player viewport is still present, and a critical flaw here
[
SNew(SBox)
.WidthOverride(1920)
.HeightOverride(1080.0f)
[
SNew(SOverlay)
+SOverlay::Slot()
[
//Background image
SNew(SImage)
]
+SOverlay::Slot()
[
SNew(SBox)
.HAlign(HAlign_Center)
.VAlign(VAlign_Center)
.MinAspectRatio(1.77f) //AKA 16x9
.MaxAspectRatio(1.77f)
[
widgetToDisplay->TakeWidget()
]
]
]
use ```cpp ``` to format



