#slate
1 messages · Page 13 of 1
maybe if i start over and use lfs from the beginning,,
idk what to do. youre gonna have to help me some more. all the asset files seem to be pointers and i cant figure out how to get the real ones. ive been googling and nothing is helpful
Probably Splines or something. Look up Beziar curves or whatever it's called
I need precise numbers tho... Normally you could specify rounding amount, and it would just work, but it doesn't allow negative values
Do you mean like in material?
if you need negatives, you can just turn it upside down. but yes, also in materials. or just on a draw
wdym "turn it upside down" ?
like....rotation? theres nothing you cant rotate
Yeah, but how would that help?
you said it only does positive values
So normally rounding deletes corners of the image, I basically wanna add extra corners which is round
Like extrude the corner, if you will
So there is normally a gap between buttons and the bottom panel. I'm already filling that gap with another image (to make it look like it's connected to the bottom panel)
So I wanna give that extra rounding to the background image
I don't wanna modify or use a custom button
TL;DR there is a rectangle image in the background and I wanna make it "inverse round"
(it's called "concave" I think)
Note: This is from UX design, it's not current state of the UI (Basically I wanna reach that)
well im not sure how to make this yourself. my best guess is with materials.
and the spline as i said
❓I am using FSlateDrawElement::MakeCustomVerts() (used in SMeshWidget) to draw some mesh which has Vertexes positioned in exact World positions I want them to be drawn on some map widget. To transform world position to Geometry I am using
WorldToMap.InverseTransformPosition(WorldPosition)
which works perfectly fine to draw those vertexes using e.g. FSlateDrawElement::MakeLines() or any other FSlateDrawElement
but MakeCustomVerts() makes those vertexes to "hover" way above those drawn via MakeLines(). What I am missing ❓
Way to go! You'll use what you learned again, guarantee it.
im gonna forget how i did it in a day. the next time i use git lfs a year from now ill have to relearn it lol
Hello guys, i am gonna make a plugin basicly half of the window should have viewport. I am gonna an actor in there . So How can i create that viewport in horizontal box. I couldnt find any example so if you help that would be great
without a plugin you'll have to figure it out for yourself in CPP
There's no vanilla solution...
it's also that git-lfs with github for desktop seems kinda broken when submodules are involved. kinda confused as to why the heartgraph demo project is set up like this tbh.
this dude is wild. he made his own input system
i rly wish this demo was in cpp actually. blueprints are more complicated to make heads and tails of
Really general question: are people using Slate for game UI? Or is that crazy talk? I'm coming from a web dev place so UMG feels like a step back technologically, with everything being raster and WYSIWYG. I miss CSS.
UMG is a layer on top of slate, so all games that use UMG still use slate; by exposing slate elements to UMG you make them editable and controllable via the UMG editor and blueprints. It'll probably start being inconvenient to use pure slate when you want your UI to use art assets, materials, the such; it is faster to iterate over changes when they're exposed to editor reflection.
You can use html renderers.
Interesting idea. Like a sandboxed browser plugin for the game engine?
This is always ass
Probably. 😛
Something like that. I'm not sure what people use.
I doubt it's the bundled chromium.
Browsers have the benefit of being able to use all their resources to render the UI (webpage).
I tried to do this in a game once but a bunch of the gameplay programmers came and complained that "the game was just one huge menu".
if you're more comfortable with web stuff you can check out Rive.app
@mild oracle that looks pretty awesome, I will have a dig in.
Does anyone have any resources for creating a widget that's pretty much an overlay with elements (UWidgets) added to it dynamically based on a variable? I'm a little lost with the information I have been able to find.
I assume I need to create a Slate widget with a UserWidget wrapper that I can then use to create a widget but beyond that I'm not finding all the pieces to understand the procress.
Hmm I would also like to know this. I would start by looking at how the Panel widgets are implemented and go from there. Is there a reason you want to do this in Slate and not just regular C++ and User widgets?
What do you mean elements added based on a variable? Like some TArray that is used to populate a container with UMG widgets?
I'm not sure, I might not need to but I'm confident I don't actually know enough to make that decision. 😅 From what I've read it seems that if you want to create UWidgets (Overlays, Horizontal Box etc..) dynamically then you'd use slate. Of course I could be completely wrong.
I guess I think about it like this:
a) If you want to create sets of UMG widgets that are defined in Blueprints (so BP subclasses of UserWidget), I would do that in BP/C++ from another UserWidget that is the container.
b) Drawing something complex, e.g. loop through an array and draw a bunch of brushes, I'd do it from Slate in a new custom widget.
c) You want to dynamically add UMG widgets (vanilla UImage, vanilla UHorizontalBox), those still need to be added to something, so I would put the logic inside a UserWidget and do it in C++. But usually there's enough design set-up to make it worth doing a) instead of c).
And either way with a or c you'll want to do pooling for efficiency.
It sounds like I'm wanting option C. So no need to use slate specifically?
afaik no, you could probably do what you need to do in RebuildWidget of a new UMG class?
does anyone know how to properly draw / configure a brush in slate to preserve the corner of the image when resizing ? I have an oval shape and Id like to stretch it to become a long bar without the stretch affecting the corners .. I could use 3 images but it feels like a feature that should be there and I just don't know where to look at
The SBorder widget has a 9-image thing that stretches the middle part, I think?
Maybe.
```
This seems to do the trick, still unclear why the margin can't be pixel based .. but for now Im ok XD
if you look in engine code you'll see they do like FMargin(8.0/32.0) for a 32 px image with a 8 px border
kinda silly but yah
@sinful remnant im finally getting somewhere. been doing a lot of procrastination, but ive got progress!
Awesome! 😎
Damn, well done!
❤️ i even managed to recreate spawning it properly. tho im trying to fix a bug now. connections arent showing.
honestly, i feel kinda bad, im just mostly copying his implementation. i dont feel like i know enough to rly modify it to my needs yet, and what he has works already
Yeah the connections are done in an on paint function somewhere.
yeah. the problem is that its not seeing my nodes somehow. im trying to debug it currently.
the nodes are created properly, and the graph properly lists displayed nodes, but trying to access that node gets null
i think i found the problem. the guid for the EdNode isnt matching up with the guid of the visualizer node
and im not sure why
the first picture is the demo, second is mine. why is the guid not showing? and it always seems to be invalid in code. i cant find what im missing
these are invalid i think, and i cant figure out how to fix them
anyone has an idea why those wouldn't wrap correct? (both small widgets have the correct size, so it should fit. First screenshot shows the bounding box of the wrap box
that's the wrapbox: SNew(SWrapBox).Orientation(Orient_Horizontal);
that's how i'm adding content:
Content_WrapBox->AddSlot()
//.HAlign(HAlign_Left)
//.VAlign(VAlign_Top)
[
ItemAssetView_WrapBoxContent
];
(same issue with the valign/halign options enabled, but those should be default anyways)
nvm, had to set UseAllottedSize to true
FINALLY!!!
goddang this was so cancer
Bing bong so simple!
maybe if youre a genius lol. frankly i still dont get how it works. im just not gonna touch it anymore
tho now that the basics are setup i can do some more complicated splines
do you know why the background doesnt move with the nodes? and how to make it?
well nvm im figuring it out
idk how to make the math better than this. took me forever to even get this far. the code is pretty confusing in the cpp
Can you create a custom editor that derives from another editor? I basically need a custom asset that uses an editor similar to the animation blueprint editor, but, with some very minor differences. i still need to be able to create states that play animations on a skeletal mesh and all that. Are there any examples in source code of deriving from an editor like that?
Do you want to do that for personal use? For some sort or company use? To sell?
So, how do y'all go about extending slate widgets..? For context, I'm hoping to make an extension/child of SFixedSampledSequenceRuler, but I don't understand how I'm supposed to set the SLATE_ARGUMENTS or running Construct() of the base class (SFixedSampledSequenceRuler) from my child class.
Subclassing does not really work well with Slate widgets.
Previously I have just copy-pasted the entire base class and created my own. e.g. SButton => SMyButton

Something something composition over extension something something. 🤷
I appreciate the input nonetheless
Slate does support inheritance and you can pass args to the parent class via parentargs (search this channel for posts from Daekesh on this topic). I agree it’s a bit unwieldy but if you want other classes to recognize your custom button as a button you may need to inherit from SButton, and the such.
I'll take a look for that. Thanks.
It depends, Daekesh’s question is important cause it’s usually easiest to modify the editor source, but some editors like the sequencer are implemented via an interface which is implemented in various different ways in the editor itself (the standard level sequence sequence editor vs the motion design sequence editor, those have completely different implementations).
The sequencer is also an interesting bit of engine code because it is in itself extendable and you can register custom tracks and sections for dealing with objects, etc.
So it really depends on which editor you’re looking into, what you’re trying to do with it, etc.
@grave hatch Yea its for company use. Just in the research phase right now to see what the proccess would look like. For more info, I am trying to see what an ideal tool might look like to animate widget components. In a VR game, there are several widget components that are being moved around in 3d space but also need to have UMG animations playing at the same time. Sometimes the 3d animation needs to wait for the 2d animation to finish, sometimes vice versa, etc. Our best approach so far has been to use a skeletal mesh where all the widget components are attached to the bones, and we handle the 3d animations in an animation blueprint. That way proper states/transitions can be setup. But that only covers the 3d side of things. Trying to come up with a tool that covers both 3d and 2d
Hmm.
As long as you don't distribute it, it's fine... but you can't really extend the animation editor. You can make your own. All the base classes are right there.
im having a problem im wondering if you know the answer for.
when i make a change to anything connected to the Heart EdGraph asset, the pin connections disappear and the editor crashes if i interact with the asset at all.
the pins themselves still show that the pins should have a connection, but the connection data is lost somehow.
it used to just let me delete the nodes and remake them, but now it crashes if i do anything and i get errors like this:
Assertion failed: false [File:G:\UnrealEngine-Angelscript\Engine\Source\Runtime\Engine\Private\EdGraph\EdGraphPin.cpp] [Line: 1980] Pin named then was serialized while trashed - requesting pin named Input owned by node HeartEdGraphNode_1
something seems to corrupt the savestate of the asset and i dont know what to do to fix it
i could theoretically comment out the check that crashes the editor at this line, but i dont really want to mess with the source code of the engine, and it doesnt fix the fundamental issue that the pins lose their data
ok so the problem doesnt appear when i change something, it only seems to appear when i restart the editor. i was working for hours just fine, and on a restart all the nodes got bricked
i have no idea what i did....i remade a couple things and now it seems to work. but also, now an output cant connect to 2 other nodes at the same time. not sure why.
Hey slate fam! does anyone know how to visualize the coordinates within a slate EdGraph?
this graph uses the node's position on the EdGraph to place them in a UMG canvas in the appropriate position. however, i have no idea where that actually is.
there is no ruler along the edges of the graph to give me an idea of the actual node position and i cant find a way to enable one. there is nothing in settings. im assuming its just not a default thing these graphs have.
However, UMG graphs have a ruler along the side and top, and keep track of the mouse position on the graph. Is there a way to implement that in another type of graph?
Check the umg editor, there’s either some helper class that paints the ruler or some code invoked in the paint event that does so.
If you can’t find the code or it isn’t transferable for some reason, basically just make your own, use the paint event to draw markers, subtract the window’s offset, multiply by the zoom. And so on.
I’m pretty sure that the UMG editor graph is a child of UEdGraph and that it uses some ruler class you’ll be able to re-appropriate
i think this is the class
i actually dont think umg inherits from EdGraph at all
or i just cant find it
but this designer view seems to have some relevant stuff. its definitely where the mouse position is determined
aha.
so maybe i can inherit a class from this and implement it on my EdGraph
or i can just take the ruler parts and recreate them
thanks for your help. at least gave me a place to start looking
What I did was take the entire ruler class, copy it somewhere else and re-use it.
That's a good idea too.
Spent a few hours trying to figure out how to weave it into the plugin's existing systems and I'm still not quite sure how to. Then I got tired and had to stop.
I'll try more tomorrow.
Sad thing is, I did that to another in-engine plugin. 😂
Got any tips for how to overlay an existing EdGraph class with the ruler?
..Nope
well. it would take too much work to try to get these rulers in.
If anyone has a lot of experience creating tools/plugins in UE, hit me up, I've got some issues related to Slate
How high up should we hit you
my first time being hit by the dontasktoask link was a formative experience. i do think ive become a better person since learning how to ask properly on the internet
and ive hit others with it many times now
Any way to have a SWindow to be created and be in the background and not come into foreground with fslateapplication::Get().AddWindow or something similar (don't want it minimized, it needs to be active in order to record from it)? IsTopmostWindow is set to false too
Or even keep main viewport always in the foreground? Tried BringToFront with force true and the HACK_ForceToFront and neither worked to make the main viewport be on top of the new one
Hey! I was wondering about how someone would pseudo create the tilting effect from balatro in unreal's widgets. The problem I'm facing is skewing and rotation angle doesn't seem to be enough to properly re-create it or I'm just bad at math (Which is probably accurate LOL) But would really appreciate any insight into the best way to make this work. Thank you!
Doing it in 3D is probably easiest to be honest
Assuming you are building a card game anyways
Slate does have render transforms.
You can't do rotations unless you construct them specifically, though.
Thanks for the answers yall! Can you elaborate on construction them specifically?
depending on what you need to do, slate rotations might be fucking ass because the actual render bounds stays the same after you rotate the content. i think there might be a way to nest widgets under a whole canvas and maybe rotating the canvas works though? never tried it
and i think (not sure) daekesh meant to apply a rotation you have to build the rotation matrix (not like from totally raw numbers, but there just isn't a super easy convenient .SetAngle function for example)
this is what i'm talking about with render bounds and... ass #slate message
So I was able to get it to work for the window to be created in the background and not get sent to the front, but only for Windows as I used the winapi functions that could be used:
FSlateApplication::Get().AddWindow(CustomWindow, false);
FSlateApplication::Get().GetRenderer()->CreateViewport(CustomWindow); // Need this from the slate window ShowWindow()
HWND Handle = (HWND) CustomWindow->GetNativeWindow()->GetOSWindowHandle();
SetWindowPos(Handle, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); // Puts window position to bottom and doesn't activate it
CustomWindow->GetNativeWindow()->Show(); // Just need native Show() for window to display to use for recording
#endif
#if !PLATFORM_WINDOWS
FSlateApplication::Get().AddWindow(CustomWindow);
#endif```
Also needed this in header:
```#if PLATFORM_WINDOWS
#include "AllowWindowsPlatformTypes.h"
#endif ```
However, if anyone knows any native UE way to do it across all platforms, please let me know
Does anyone know whether its efficient to render a lot of text (think hundreds of lines max), which come and go with different fonts (~3 fonts max) and font sizes (~3 different sizes), with FSlateDrawElement::MakeText in C++ widget's NativePaint? It's needed for combat text.
Following reference in #cpp message
I did indeed find some examples in there, but nothing using icons for rows.
Tho, I have figured something out myself, however, I cant seem to get the SImage to behave as I want...
How would I create a FSlateImageBrush from a UTexture2D* ?
i'm using something like this in one place
TSharedPtr<FSlateImageBrush> NewImageBrush = MakeShared<FSlateImageBrush>((UObject*)Texture2DPtr, FVector2D(128, 128));
I'll try it. Trying to make a combo-box with icons instead of text :3
Do I have to clean that brush up later?
in my case i'm storing the texture in a project UPROPERTY which never goes away; and as for the slate brush, it's a smart pointer
Same for me
if you don't know how refcounted smart/shared pointers and refs work you can try doing some reading first, start a discussion either here (this channel is slow enough who cares), or #programmer-hangout if you want, to clear things up
10>Attribute.h(46): Error C2440 : 'static_cast': cannot convert from 'const OtherType' to 'ObjectType'
with
[
OtherType=TSharedPtr<FSlateImageBrush,ESPMode::ThreadSafe>
]
and
[
ObjectType=const FSlateBrush *
]
Edit: Oh, forgot .get()
There's constructors for translate, scale, translate and scale ans rotation I think. Not one that does all of them. If you wanna combine them you gotta multiply them. Something like that.
It's been ages...
What do I do to solve "AsShared not defined in someclass" error when I do
OnGenerateWidget(this, &someclass::methodref)?
Even if you fix this, you need to construct the object using makeshared/makeshareable or it will juts crash.
Solved by making method static and using the _static variant.
The _Raw won't need any TSharedFromThis/MakedShared shenanigans.
How do you make a TArray for the SComboBox? eg. How do I crate TSharedRef/Ptr from a custom USTRUCT?
You can't make sharedptrs uproperties, if you want to do that.
However, let's say you have your struct, FMyStruct
You define the array: TArray<TSharedRef<FMyStruct>> ListSource;
Then you can add to it with ListSource.Add(MakeShared<FMyStruct>(...ctor params...));
Ty
Lots of people use arrays of TSharedPtr and I still don't know why.
I can use whichever, no prob 🙂
I'm assuming this item has something to do with references?
I reckon you've probably made a local tarray and then passed a pointer to it as the source list to your list view.
You have to provide the "initial value" widget.
It's stupid.
I forget what it's called. Just look at the list view.
"InitiallySelectedItem" does not accept a Widget, and the "Item" from the options list does not work either 😢
The initial selection works at least, the item is "Circled"
SAssignNew(CboDownArrow_Outer, SComboBox<FFINArrowOptionType>)
.OptionsSource(&TraceOuter)
.OnGenerateWidget_Static(CreateItem)
.OnSelectionChanged_Lambda([this](const TSharedPtr<FFINIconTextIntegerOption>& Value, ESelectInfo::Type) {
SetTraceOuter(3, static_cast<EFINPanelTraceEndTypes>(Value.Get()->Value));
})
.InitiallySelectedItem(GetTraceOuterItem(3, TraceOuter))
There is no methods for any other widget than OnGenerateWidget, nor for initial except InitiallySelectedItem
Yeah...
SLATE_DEFAULT_SLOT( FArguments, Content )
Just do SNew(SComboxBox) ... stuff here ... [ Default widget here ]
Same as you do ever other widget.
Should the "initial widget" have the same content as the initialselected?
No.
It's literally a widget that it will stick where "No Content Provided" will go.
You could do [ GenerateRow(InitialItem) ] or whatever
(depending on your setup)
Or you could have something completely unrelated like "Pick an option..."
Oh that sucks... Oh well... Guess I will have to make a ton of temp variables 😛
Why?
But in the above image, even if I select another item, the "No Content Provided" never changes
To store the initial value so I can pass it to two place instead of one
Right. And why is that an issue?
This is an example of a combo box I did: cpp return SNew(SComboBox<TSharedPtr<FDMObjectMaterialProperty>>) .InitiallySelectedItem(InitiallySelectedItem) .OptionsSource(&ActorMaterialProperties) .OnGenerateWidget(this, &SDMToolBar::GenerateSelectedMaterialSlotRow) .OnSelectionChanged(this, &SDMToolBar::OnMaterialSlotChanged) [ SNew(STextBlock) .MinDesiredWidth(100.0f) .TextStyle(FDynamicMaterialEditorStyle::Get(), "RegularFont") .Text(this, &SDMToolBar::GetSelectedMaterialSlotName) ];
Is it normal that the combo box does not change the value when I change the list item? It keeps sayin the initial value
I managed to get it to display the "initial" item as you said, but it still does not update with the selection
Yeah. I may have been mistaken about that. I tho ught you were using a list view!
Basically what the code there does ^ is use a text block with a dynamic text source to show the selected value.
You could also use OnSelectionChanged to update content of the combo box.
Must be the most worthlessly coded combo-box in history.... 😄
It gives you... options.
It should be named dropdown list or something instead, cause it is not a combo box 😛
The Blueprint Editor version behaves as I expect one to behave...
UMG or whatever its called
That's what a combo box is? shrug
UMG will use the slate one under the hood, but with a bit of extra coding - probably to give it the "expected" default.
There is no self reference in the lambda either, so this became iffy... Need to create fields for everything
You can create a self reference by just creating a local varaible (tsharedref) to the combo box and assigning the events afterwards.
Then pass in the sharedref as a weakptr to your lambda.
Or maybe create an SP Lambda.
(which does that for you)
CboUpArrow_Outer->SetContent(CreateItem(static_cast<FFINArrowOptionType>(Value)));
Apparently does not work... gives compile errors 😢
FINArrowModuleHolo.cpp(181): [C2248] 'TSharedRef<FFINIconTextIntegerOption,ESPMode::ThreadSafe>::TSharedRef': cannot access private member declared in class 'TSharedRef<FFINIconTextIntegerOption,ESPMode::ThreadSafe>'
Oh you want to do it on the content. Yeah...
I just want it to display the current value, like a combobox should 😢
Wait, what is the type of CboUpArrow_Outer>?.
You don't need to
TSharedRef overrides -> to give you a pointer to the inner object.
But, yes, Slate often requires you to save references to widgets you've added to a hierarchy to do operations on them later.
Its this part that fails: FFINArrowOptionType FfinIconTextIntegerOption = static_cast<FFINArrowOptionType>(Value);
The reason you needed a member earlier (for the tarray) is because it doesn't store the array, only a pointer to it, so your local var was deleted and then the widget caused a memory error.
Well, none of that is related to Slate?
Nope. Working on it...
But non the less.. I firmly believe updating the current value should be part of the combobox itself. Else its not a combobox imo.
Cant do much about it tho. Just not very clear at all.
Also, I had to change to SharedPtr instead of SharedRef in the lists, Ref ended up problematic 😛
SetContent does not work... it removes all the controls and border 😄
God I hate this so called ComboBox 😛
What was the problem with sharedref?
I made my CreateItem take the typedefed item type, and the lambda callback parameter has to be sharedptr
Why?
Dunno, compile error otherwise
Btw, SharedRef converts implicitly to SharedPtr...
Something something overload
(Inside of SCombobox)
I wonder if my update issues is because the row widget is not a textblock
You could put an SBox in the content slot, save a reference to it and set its content.
E.g. [ SAssignNew(MyComboConent, SBox) ]
Well... 1.
I would not have a single widget with 9 different combo boxes in it that had repeating functonality and style. I'd create a combo box wrapper that managed this for you and just re-use it 9 times.
https://forums.unrealengine.com/t/slate-scombobox-example/349369
This uses a callback for the text value
You mean like the one I posted earlier? 😛
Yeah
Unfortunately, the callback is not aware of which combobox...
So... 9 new callbacks...
And, I have to expand the creation, so 9 more repeated code blocks \o/
Worst Combobox ever 😄
I'll just ditch the idea of ever having icons in them. Cant be arsed to deal with this non working backbone 😛
I refer you to my earlier statement!
It would take maybe 5 lines of code (including fields) to implement mirroing of selected items in the content area.
Including any icons you wish to add.
I have 0 clue of how to write a new wrapper for slate
It's just a class that inherits from SCompoundWidget (99.99% of cases) and does whatever functionality you want.
Like adding a single combo box and managing it.
Then you add that class to your main widget instead of the combo box directly.
It's 6. And 2 of them are { and }.
This is the second time I work with slate.... I have no clue 😄
I'm working on the code expansion. 600 lines for 8 combo boxes
if you build these skills just think of how many hours of your life you could spend writing 3000 line slate widgets
Or, I finish this, and never look at slate again, cause I hate it 😄
Well, I think my biggest issue is, I hate C++
what function should I override to prepare data for OnPaint() and later to check if the cursor is hovering over it (the data)?
9 exactly identical functions except the field they use is different 😛
Stick to umg? 🙂
Don't want to sound harsh, but if tihs is what you're doing, it's not slate or c++ that's the issue!
If you really wanted to do it all in a single widget, at least use an array.
Or an array of structs, each of which deals with 1 isntance of your combo box.
(or completely separate widgets)
I really do not want to create classes in Unreal C++, I have to restart rider like 8 times before all the errors goes away and the compiler is satisfied
Use live coding for slate.
Thats not the issue, its the precompiled headers. Before everything is set up and all wiggly lines is fixed, its like 3 regenerations of Unreal Source Files, several recompiels and a couple of Rider restarts.
Live coding fails 99% of the time cause of Enum reload errors
Me neither. But I'm pretty sure most of the issues is because I suck at C++ and because its a mod-environment for a pre-compiled game 😛
Absolutely.
From my initial 20 lines to 200 lines
It's gonna keep growing and get unmaintainable.
Absolutely. Hopefully, I do not have to maintain it 😄
0 comments too
\o/ it works!
The horror!
Had I been in Java, I'd never done that 😄
...where's the indentation? : (
Discord wankery, view whole file
Ty for all the help! 
Np
Can I assume that the Slate-creation is evaluated "forward". Ie, if I inline-assign a value to a variable, and use it in a child-declaration, is that safe?
Like cpp SAssignNew(SomeWidget, SSomeWidget) [ SNew(SSomeChildWidget, SomeWidget) ]; ?
This would absolutely not work.
Maybe?
Not really, however, I realized I cant do it anyway, cause it will be changed several times and used in lambda functions 😛
I would advise not just using lambdas for everything.
😄
Just for sanity: If I put an SLeafWidget across the whole Editor, mark it as Visible and return Unhandled in its Input function overrides, will the underlying Editor be blocked?
The SLeafWidget would specifically sit in the Main Windows Overlay.
Hm, I think my Clip Rect is f*cked.
You could mark is as HitDetectInvisible instead of overriding input functions?
Yeah I didn't explain properly I guess. The Widget has to handle Inputs, but only on some parts of what it draws.
HitTestInvisible doesn't work for the functions to trigger and Visible currently causes problems, but I debugged a bit more and it must be the Clipping stuff that is not doing what I need it to do.
It's not the clipping stuff. I probably don't know what it is actually. :D
New to Slate: is there an in-built widget to allow users to select one from an enum in a dropdown? I assume there's some clean way to go about it but I'm unfamiliar
SComboBox
Or, if you want to get a bit more complicated, you can use the property editor module's methods to create single property (UPROPERTY) widgets.
Does the property editor work on structs?
Probably, but I figure I should ask, might make life easier
USTRUCTS, sure
Oh sick, any good examples of it in use? Been searching a bit, couldn't find any succinct ones.
There is a built in editor widget which is an enum combo box
EditorWidgets/SEnumCombo.h
Is there a version of SlotAsCanvasSlot for Slate Widgets?
All of the UMG code deals with custom UPanelSlot objects and I can't seem to find a method in an SWidget that would return information about the slot the widget is in.
I could pass the SlotIndex down to the Child and then get the FChildren of the parent, but if there is already such information, then I wouldn't want to duplicate the logic.
I'm not really sure what's the exact syntax to do it but I think that all Slots have an Expose method that allows you to pin a pointer to the slot itself
I shall have a look at that, thanks!
Apparently via .Expose(Slot) when creating the Widget itself.
Although no, that's more for getting a ptr to the slot to later modify it inside the parent.
I think you'd have to just enumerate the slots to find the right one.
Yeah I passed the index along for now. Might have to iterate though if the order can change. Will see.
Honestly if there aren't many slots, it's not going to affect much if you do just enumerate.
how can i access slate event?
in TSharedPtr<SSubobjectEditorDragDropTree> TreeWidget
I want to modify OnGenerateRow inside SSubobjectEditorDragDropTree from another class like TreeWidget->OnGenerateRow
the class definition is inside SSubobjectEditor
the problem i am having is that when i try to access it for some reason, it seems to be shadowing OnGenerateRow from the parent class and it's protected there
1> with
1> [
1> ItemType=FSubobjectEditorTreeNodePtrType
1> ]
1> treeWidget->OnGenerateRow.AddUniqueDynamic(this, &SCustomSubobjectEditor::MakeTableRowWidgetTest);
arguments exist only during construction (Snew/Construct), later they're assigned to private variables
you could try via private access hook get the private member
also those are neither multicast nor dynamic delegates, that adduniquedynamic is invalid
I'm trying to override the ConstructTreeWidget during construction like this
{
SSubobjectEditor::ConstructTreeWidget();
// I have access to the treeWidget and
TreeWidget->OnGenerateRow.....
}```
I don't understand what you mean when you said`later they're assigned to private variables `
to my knowledge I'm still in construction, am i not?!
`adduniquedynamic is invalid` I can reinitialize it but I cannot access OnGenerateRow to begin with
I'm trying to avoid intrusive modification to the code if possible, so I am leaving that at as a final option,
What I don't understand is why I cannot access OnGenerateRow even though it's under a public access modifier
TSharedPtr<SSubobjectEditorDragDropTree> TreeWidget;
class SUBOBJECTEDITOR_API SSubobjectEditorDragDropTree
{
public:
SLATE_BEGIN_ARGS(SSubobjectEditorDragDropTree)
: _SubobjectEditor(nullptr)
, _OnGenerateRow();
SLATE_EVENT(FOnGenerateRow, OnGenerateRow)```
SLATE_BEGIN_ARGS is essentually " struct FArguments { FArguments () "
SLATE_END_ARGS is " } "
OnGenerateRow is a public function of FArguments struct
"_OnGenerateRow" is a private member of FArguments struct,
it is not a SListView member OnGenerateRow that you trying to access
I see,
_OnGenerateRow" is a private member of FArguments struct
in the SLATE_EVENT definition at 546
I saw this DelegateName _##EventName; \
I thought there might be a public variable definition
FOnGenerateRow _OnGenerateRow;
it is not a SListView member OnGenerateRow that you trying to access
I see but I need to mention that i dont want to access the SListView one, and i get the same error whether I tried to access it as a function or a variable like this TreeWidget->OnGenerateRow(this, &SCustomSubobjectEditor::MakeTableRowWidgetTest);
but i get what you said
it is like that
class SListView
{
struct FArguments
{
void OnGenerateRow(DelegateType Value);
DelegateType _OnGenerateRow;
}
void Construct(FArguments InArgs)
{
OnGenerateRow = InArgs._OnGenerateRow;
}
private:
DelegateType OnGenerateRow;
}
Yeah, I missed the SLATE_END_ARGS, so i thought it was defined in the class
that's what i was trying to say in my last comment
thats why the only way to hook to it is to use private access, get old value, replace with new delegate that will call original then patch it
or reinitialize the TreeWidget with a new SNew(SSubobjectEditorDragDropTree) in the child class will do the trick
If the thing you want to change isn't exposed by a slate construct argument/attribute/event or a function on the widget class, you shouldn't really be changing it unless you really know what you're doing.
It might help to know what you actually want to achieve.
I.e. I want to change how the look of the rows generated by widget X.
Yes, btu that isn't what I asked.
He said "i want to modify this thing" but not why.
oh, he wanted to add buttons to SCS Editor table items on components in list. i think that was yesterday or few days before
thats why my answer was "only with black magic"
Yeah. Pretty much.
If it's not exposed, you use a private access hack to change it or you accept it won't happen.
yep
luckily it is public header, not a private one. and some things are dllexported so less pain
If it's an editor-used class that is. If it's soemthing you want to add to your own widgets, you can either copy+change the class or subclass it maybe.
Either way you still need access to the widget, which seems hard enough as it is if you're trying to edit the bp editor or details panel ro something.
that's what i did copy and paste the construct and modified the function there
getting access to widget instance is easy- recursive search checking type, it is unique within SSCSEditor
hard part is patching and keeping changes in sync
now i'm just experimenting with it, i'm using SSubobjectEditor because it says SSCSEditor is deprecated in 5.0
Just the construct? 😛
construct & ConstructTreeWidget, i'm looking at SSubobjectBlueprintEditor & SSubobjectInstanceEditor for references
Is there a way to manually tick the drawing of Slate Widgets without it being an overhead of sorts?
I can see multiple parts in the Engine calling FSlateApplication::Get().Tick(), so it should in theory be fine to do that. Seems to have FSlateApplication::Get().GetRenderer()->Sync() called afterwards, but I wonder what the overhead for that is, if I call that at the end of the engine loop tick.
Doesn't seem like I can tell it to redraw a specific Widget itself, only a whole SWindow, which would be the Editor Window in this case so that's not helpful.
And I'm somewhat sure calling Tick at the end of the Engine loop will cause the Editor to be drawn twice per frame. There at least doesn't seem to be any protection against that.
I could turn my Widget into a Window and manually re-draw just that, but that comes with the trouble of it being an additional window and not part of the Editor.
sad beep
honestly i don't think i've ever used them so not sure if i should be saying much but... are you maybe looking for SInvalidationPanel ?
Dynamic components can’t be deleted from the blueprint component tab, Is there a way to create a component similar to SSubobjectEditor using C++ from an actor class? or delete-able component at least?
I can see the code is protected, but I have seen classes like component editor util, so I wonder if there’s an accessible way to replicated the behaviour of SSubobjectEditor or a utility function!!!!
When I said dynamic I meant component made with C++
native components created in code - can be removed only in code or toggled on/off (if optional) only in code
blueprint components - created&defined in UBlueprint asset/SimpleConstructionScript
simple: no, you can't change it.
When teh state changes, remove the current widget and add the new one.
I'm creating a EditorWindow widget with slate.
Now I'm looping through some Actors in the World and want to provide quick edit access to UPROPERTY.
Can I get the PropertyHandle of the property and just use the SObjectPropertyEntryBox for drawing the selection dropdown?
TSharedRef<SWidget> UDukesWorldManagerEditorTab::RebuildWorldAreaOverviewWidget(AAbstractWorldArea* WorldArea)
{
TSharedPtr<IPropertyHandle> OwningDynastyPropertyHandle = // ????
return SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
[
// SNew(SObjectPropertyEntryBox).PropertyHandle(OwningDynastyPropertyHandle)
];
}
For property:
UPROPERTY(Replicated, EditInstanceOnly, BlueprintReadOnly, Category = "Dukes")
ADukesDynasty* OwningDynasty;
You have to use an FPropertyRowGenerator
You can create them via the FPropertyEditorModule
(FPropertyEditorModule& PropertyEditorModule = FModuleManager::Get().GetModuleChecked("PropertyEditor");)
Thank you! There's a lot of useful stuff in there 🙏 . Just so hard to find the correct classes and methods .
Do anyone have any examples of using the SComboPanel? I've not found any...
I dont have any of those
They should be on the UE git at least.
Found something, ty
Oh... Not included in shipping
Trying to create a simple chart using slate, but what about the jaggy borders of the chart slices?
Notice te purple and light blue border is jaggy the most
How are you drawing it?
Am creating arches with FSlateDrawElement::MakeCustomVerts
and just like a loop of slices
And what parameters are you proviing to MakeCustomVerts?
Code to draw a pie slice is like this.. I doubt if there is any aniallising thing i could enable?
You could try rendering it to a render target first (at a higher res) and then drawing that render target
A bit of a workaround, but yeah.
Epic draw circular widgets using materials, the audio radial slider for instance.
Hard to create dynamic pie charts with materials, though.
So yeah rendertargets was my first try but found it a bit bad on performance
So why would the code result in jaggy edges?
Because it's not anti-aliased?
Also render targets are really speedy. If you're having issue, it may be because you've done some stuff wrong.
Hopefully you aren't drawing the texture by hand?
Pie charts are actually pretty similar to radial sliders
Hey guys, I have a weird issue where if I click on a commonui button, the click event fires fine, but if I focus it with a gamepad and try to use it then it doesnt work. Its a button on a dynamically created widget, and there are multiple copies of it (server list item). If I cycle between them the button doenst work, but if I focus outside of them, like a refresh button or checkbox, the first time switching from and outside widget to the button works. I checked the widget reflector, and it looks like when it works, it focuses the InternalButtonBase, but when it doesnt work, it focuses the buttonitself. Has anyone tackled this issue before?
When clicking with mouse, it also focuses on the internalbuttonbase
Ah yep must be, but i don't see a antialiasing option for FSlateDrawElement::MakeCustomVerts while FSlateDrawElement::MakeLines has one 😕
The ItemHeight is only used for Tile. See ShouldArrangeAsTiles Please update your code to the new API before upgrading to the next release, otherwise your project will no longer compile.
Necro this discussion a long time ago, seems like the right thing to do is to just removeItemHeight, after digging into the original CL in GitHub
Code that utilizesItemHeighthas been removed and replaced with0.0f
Hope this helps anyone who is searching for answers
Hello everyone
In UE5 if I set bUseMouseForTouch=True scroll of ListView works fine mouse but hovering of everything stops.
I want bUseMouseForTouch=True and hovering to keep working like UE4 any suggestions.
Hovering of what exactly? Showing the mouse cursor shouldn't stop this. Maybe you added a bigger widget that is blocking everything below it at the same time?
That's not longer issue as in UE5 there is an if check to filter FakeTouch for mouse
Oh I misread the Boolean name. My bad
I'm trying to implement left mouse button drag scrolling for a ListView.
If I click on the empty space between items, scrolling works fine.
However, if I click on a ListView item, the item captures the mouse, and scrolling doesn't happen.
If I remove mouse capture from the item, scrolling works—but then the item becomes unclickable.
Is there a way to make both item clicking and drag scrolling work?
Code of ListView item OnMouseButtonDown in SObjectTableRow.h:
Reply = FReply::Handled()
.DetectDrag(SharedThis(this), EKeys::LeftMouseButton)
.CaptureMouse(SharedThis(this));
"clickable" meaning you can press buttons on it, for instance?
Or that you can drag the item?
It means ListView item does not respond to click
Respond in what way?
Meaning upon clicking (after mouse button up) it is supposed to do something (like a button) which it does not do
So any solution, can listview and listview item both have capture or something?
Just solved my problem few moments ago, OnDrag I check if LeftClickScroll is enabled then release mouse capture
Hello, I have a problem that I don't know how to fix, I made my game menu but the head movement that the character has stays there when you are in the menu and I don't know how to fix it, does anyone know?
So.... you want to disable head movement when you're in a menu?
Like the head moving about on screen? Or you want the mouse to stop changing the view (like in an fps) ?
The head movement that the character has when he doesn't move bone you can't move the camera there but the camera moves by itself, because of the movement I don't know if I explained it well
I mean the movement of when the character runs or walks the camera shakes
Right. Do you want them to stop moving when the menu's open?
Yes, I want the camera to stop moving when you are in the main menu
Just the cámara
I fix it, but thank you
is there a way to draw a line with a start end point like FSlateDrawElement::MakeLines , but with a material?
FSlateDrawElement::MakeRotatedBox() with a box 1 pixel wide
Hey Everyone,
I am dealing with a issue with Stuck Touches.
Its seems that in some cases OnTouchEnded does not get called which leaves the touch there on the screen. When I show touches Input.Debug.ShowTouches 1
This mostly happens when UMG is there and you are clicking a button, The button consumes the touch but the OnTouchStarted still fires.
How can this be resolved or how can I clear the stuck Touches
I asked this in the CPP but they directed me here
Anyone here work with level editor toolbar that could assist me?
My goal is to have this simple slider EUW I created up onto the toolbar, with logic I already have in the widget to control the time of day in my game. It would be most preferable in my case to have it like this.
I've tried researching into this specifically for getting widgets onto the toolbar meeting only dead ends, as well as testing out c++ methods to no avail...
If you go into the editor options and enable the ui something points, you can see what the menu is "called" and then extend it with the UToolMenus system. Most likely.
If you can't extend it with UToolMenus, then the legacy system will probably work. Just check the name thingy and see which it uses.
I have slate custom editor viewport in my custom editor window and I want to put widget into it and then update. At the moment I am using MakeViewportToolbar for add widget but I can't update that widget after it visible. Any bestpractice how to put slate UI into editor viewport with C++?
How exactly do you want to update it?
What I'd probably do is wrap the widget you want to change in an SBox, hold a weak reference to it and then use the SetContent of the sbox to replace the widget.
If you want to only update parts of the widget, like text or a colour or something, then you can use lambdas/events and make it dynamic.
I'm attempting to make a custom asset that is accessible in the Content Browser's right-click menu, has full Blueprint capability, and inherits from my custom UATBaseOperation class (which inherits from UObject). So far, I seem to have achieved those three requirements, at least with regard to functionality. However, the appearances of the thumbnail, color bar, displayed class name, and tab icon are not as desired.
From what I understand, I should not use FAssetTypeActions-related classes anymore due to functionality being moved into the UAssetDefinition-related classes. Therefore, I have created the UATOperationFactory and UATOperationAssetDefinition, which inherit from UFactory and UAssetDefinitionDefault, respectively. With those two alone, I could not find a way to get the proper thumbnail, color, class name, and tab icon. So, to address the thumbnail issue, I hackily wrote a custom UATOperationThumbnailRenderer that reads my png (in the Resources folder) and turns it into a texture. This semi-works, as can be seen in the below pic, but Unreal still realizes something is amiss, as is also indicated in the below pic.
I’m not sure how to properly set the asset’s thumbnail, color bar, class type name, and tab icon. Can anyone provide some direction? Any help is appreciated.
Override GetAssetColor in the asset definition to change the color.
You can override the 2 GetAssetDisplayName methods to get custom names.
The icon you can probably do by defining a ClassIcon.YourClassNameHere in a the editor's styleset
(or your own styelset)
The thumbnail... well. A custom thumbnail renderer is probably teh correct approach.
@solemn saffron
Thanks for the response, @grave hatch. I have already overridden the GetAssetColor and two GetAssetDisplayName functions, as shown below.
FLinearColor UATOperationAssetDefinition::GetAssetColor() const
{
return ATSlateUtils::Colors::AssetTypeColor;
}
FText UATOperationAssetDefinition::GetAssetDisplayName() const
{
return NSLOCTEXT("ATOperationAssetDefinition", "DisplayName_AnimToolsOperation", "Anim Tools Operation");
}
FText UATOperationAssetDefinition::GetAssetDisplayName(const FAssetData& AssetData) const
{
return GetAssetDisplayName();
}
Blueprint Class is still what is displayed as the asset display name. And I have verified my ATSlateUtils::Colors::AssetTypeColor returns correct as FLinearColor(0.0f, 0.56f, 1.0f), which is not the default Blueprint class color being currently displayed. I'm thinking there must be some sort of conflict somewhere I'm not aware of. I've never tried to create a custom asset that behaves as a Blueprint before; hence, my ignorance.
As for the class icon, I will look into that right now.
For the thumbnail, I'm still uncertain how to get rid of the default cube at the bottom right. From what I have seen, that cube is present when no thumbnail is detected, yet I have a thumbnail, so... 🤷🏻♂️
Did you override the GetAssetClass function to specify that it's meant for your class?
Have you tried breakpointing the function to see if it's called (to get the colour you want)
Well that's silly
Did you override the GetAssetClass function to specify that it's meant for your class?
@grave hatch, yup, I have, as shown below.
virtual TSoftClassPtr<UObject> GetAssetClass() const override { return UATBaseOperation::StaticClass(); }
Have you tried breakpointing the function to see if it's called (to get the colour you want)
Hmm. Actually, unless Rider is messing with me again, theGetAssetColorfunction doesn't seem to be getting called.
Blueprints are a bitch to do this for... You need to make your own
UBlueprintsubclass just to change the color/icon
@trail zenith, dang, that is quite annoying. Well, at least I now know why I couldn't figure out the issues after hours of frustration yesterday. Thanks for the specific reference toUGameplayAbilityBlueprint. I will look at that class.
...if you use the New Blueprint menu to make the BP instead of your custom right click menu entry, it will show up the default blue with the default icon
Is there a way to prevent my asset from showing up in the New Blueprint menu?
You're probably right. I guess I got carried away with the pretty customization. I'm hoping this product I'm making now will help me finally land a job as an Unreal developer, so I'm trying to make things as pretty as possible.
Then you should go the extra mile and use a custom BP class, really.
Yeah, that's what I'll be trying. I've already invested significant time, so I might as well make the result worth the time investment.
The tab "NotesTab_RT_Test" attempted to spawn in layout 'Standalone_TextureEditor_Layout_v5' but failed for some reason. It will not be displayed.
am i registering my tab spawner too late? i'm registering my tab spawner in a delegate on
AssetEditorSubsystem->OnAssetOpenedInEditor()
to the LocalTabManager of that window
tabname is also persistent, so shouldn't be an issue
NotifyEditorOpeningPreWidgets() seems to be too early, since the tabmanager is null then
nvm, the later works
but for some things it seems to be too early... (e.g. PhysicsControlAssetEditor seems to reset the spawners), i'll post follow up #editor-scripting
After the help I received yesterday, I was able to create a custom Blueprint asset with custom default Events that automatically display, as shown in the attached image, but now I have two more questions.
How can I change the graph label? Currently, it has the default "Editor Utility." I was hoping overriding the GetGraphDisplayInformation in my custom Graph Schema would do the trick, but I guess not. I've looked for a few hours, but I'm guessing I'm missing something very small (again).
void UATOperationGraphSchema::GetGraphDisplayInformation(const UEdGraph& Graph, FGraphDisplayInfo& DisplayInfo) const
{
Super::GetGraphDisplayInformation(Graph, DisplayInfo);
DisplayInfo.PlainName = LOCTEXT("GraphDisplayName_Operation", "Operation");
DisplayInfo.DisplayName = LOCTEXT("GraphDisplayName_Operation", "Operation");
DisplayInfo.DocExcerptName = TEXT("Operation");
}
Why are there two asterisks displayed when the asset has changed? Usually, when as asset changes, a single asterisk is displayed, indicating the asset needs to be saved. My asset shows two.
Thanks for the detailed response.
For the first point, I looked into the FGraphAppearanceInfo, which led me to override the GetGraphAppearance function, as shown below. (I'm posting it for future users who need similar help.)
FGraphAppearanceInfo FATOperationBlueprintEditor::GetGraphAppearance(UEdGraph* InGraph) const
{
FGraphAppearanceInfo AppearanceInfo = FBlueprintEditor::GetGraphAppearance(InGraph);
if (GetBlueprintObj())
{
AppearanceInfo.CornerText = LOCTEXT("AppearanceCornerText_AnimTools", "ANIM TOOLS");
if (const UClass* GraphClass = InGraph ? InGraph->GetClass() : nullptr)
{
if (GraphClass->IsChildOf<UATOperationGraph>())
{
AppearanceInfo.CornerText = LOCTEXT("AppearanceCornerText_Operation", "OPERATION");
}
// else if (GraphClass->IsChildOf<UATSomeOtherGraph>())
// {
// AppearanceInfo.CornerText = LOCTEXT("AppearanceCornerText_SomeOtherGraph", "SOME OTHER");
// }
//
//
//
}
}
return AppearanceInfo;
}
For the second point, you caught me red-handed. 😅 I copied from the FGameplayAbilityBlueprintEditor, and I didn't even notice the additional line: Args.Add(TEXT("DirtyState"), bDirtyState ? FText::FromString(TEXT("*")) : FText::GetEmpty());. I simply removed that, and now the second asterisk is gone.
.
I have another question. I'm still having trouble getting the icon to display properly in the main tab, as shown in the below picture. I have my brush set in my Style class:
StyleSet->Set(ATSlateUtils::Names::IconName_OperationThumbnail, new IMAGE_BRUSH(TEXT("AnimTools_OperationThumbnail"), FVector2D(64.0f, 64.0f)));
StyleSet->Set(ATSlateUtils::Names::IconName_OperationIcon, new IMAGE_BRUSH(TEXT("AnimTools_OperationThumbnail"), FVector2D(16.0f, 16.0f))); //size change only
And I call the below in the Asset Type Actions class. (I couldn't get things to work with just the Asset Definition.)
const FSlateBrush* FATOperationAssetTypeActions::GetIconBrush(const FAssetData& InAssetData,
const FName InClassName) const
{
return FATStyle::Get().GetBrush(ATSlateUtils::Names::IconName_OperationIcon);
}
const FSlateBrush* FATOperationAssetTypeActions::GetThumbnailBrush(const FAssetData& InAssetData,
const FName InClassName) const
{
return FATStyle::Get().GetBrush(ATSlateUtils::Names::IconName_OperationThumbnail);
}
The correct icon is displayed in the Content Browser menu, and the correct thumbnail is displayed, but the tab's icon is unchanged. What am I missing?
EDIT: I have realized the Asset Type Actions class only affects the Content Browser, menus, and thumbnails, but not editor tabs. Doing the below changed the icon appropriately.
const FSlateBrush* FATOperationBlueprintEditor::GetDefaultTabIcon() const
{
return FATStyle::Get().GetBrush(ATSlateUtils::Names::IconName_OperationIcon);
}
And I also realized the tab icon's color can be changed by overriding GetDefaultTabColor in the Editor class too.
I have a SNew(SObjectPropertyEntryBox)
I want to set the value through the PropertyHandle
when I set the value of the PropertyHandle it doesn't seem to invalidate the SObjectPropertyEntryBox
wdym, it doesn't show the new asset in the picker? (how) did you set up ObjectPath attribute?
SNew(SFontRefWidget)
.ValueHandle(FontObjectProperty)
.RefHandle(FontObjectRefProperty)
.CustomValueWidget(
SNew(SObjectPropertyEntryBox)
.PropertyHandle(FontObjectProperty)
.AllowedClass(UFont::StaticClass())
.OnShouldFilterAsset(FOnShouldFilterAsset::CreateStatic(&FSlateFontInfoStructCustomization::OnFilterFontAsset))
.OnObjectChanged(this, &FSlateFontInfoStructCustomization::OnFontChanged)
.DisplayUseSelected(true)
.DisplayBrowse(true)
)
I am using PropertyHandle instead of ObjectPath, not sure if that is causing issues
the parent widget SFontRefWidget mutates FontObjectProperty by calling ->SetValue( ... )
Edit: this actually works as expected, turns out the property was getting re-edited further down the callstack in the specific UMG I was testing it on in a way that made it appear to not work
forgot about the other option, sorry
That icon is set via slate styleset. Use
FSlateStyleSet::Set. Name needs to beClassIcon.ClassNameandClassThumbnail.ClassName
@trail zenith, okay, this must be what Daekesh was referring to yesterday. I'll look for an example of that. For some reason, I'm having difficulty understanding. It should probably be the easiest things I've done so far, but... 🤷🏻♂️
Use AssetDefinitions.
I'm actually switching over as we speak. I wanted to get something working first. Most things in the engine seem to still use AssetTypeActions, so that seemed like a good reference. But now that most things are working, I'm switching over to AssetDefinitions.
i feel like i couldn't get it to work and just used the stupid path option. might have been because i'm not in a details customization for mine though
Okay, thanks. 👌🏻
Oh, I guess I assumed because of what I saw with the Gameplay Ability System. Well, shame on me. 😅 I'll take a look around in the engine again.
After attempting to switch from the Asset Type Actions to Asset Definitions, I have had only partial success.
The simple (non-Blueprint-based) asset was easily switched, in that it shows up on the Content Browser's menu and works as it did before; however, my Blueprint-based Operation asset does not seem to be showing up in the Content Browser's menu. I verified it is getting registered. I even manually used UAssetDefinitionRegistry::Get()->RegisterAssetDefinition(NewObject<UATOperationAssetDefinition>()), but that did nothing beyond what was already internally being done. I have set the category as shown below.
TConstArrayView<FAssetCategoryPath> UATOperationAssetDefinition::GetAssetCategories() const
{
static const FAssetCategoryPath Categories[] = { ATSlateUtils::Categories::AnimToolsCategory };
return Categories;
}
And that is the same as what I did for the non-Blueprint-based asset, so I know the category itself works.
Below is my header for the Blueprint-based Operation class. I have overriden what I believe are all the necessary functions.
UCLASS()
class ANIMTOOLS_API UATOperationAssetDefinition : public UAssetDefinition_Blueprint
{
GENERATED_BODY()
#pragma region UAssetDefinition
virtual FText GetAssetDisplayName() const override;
virtual FText GetAssetDisplayName(const FAssetData& AssetData) const override;
virtual FText GetAssetDescription(const FAssetData& AssetData) const override;
virtual FLinearColor GetAssetColor() const override;
virtual TSoftClassPtr<UObject> GetAssetClass() const override;
virtual TConstArrayView<FAssetCategoryPath> GetAssetCategories() const override;
virtual UThumbnailInfo* LoadThumbnailInfo(const FAssetData& InAssetData) const override;
virtual EAssetCommandResult OpenAssets(const FAssetOpenArgs& OpenArgs) const override;
#pragma endregion
#pragma region UAssetDefinition_ClassTypeBase
virtual UFactory* GetFactoryForBlueprintType(UBlueprint* InBlueprint) const;
#pragma endregion
/** Returns true if the blueprint is data only. */
bool ShouldUseDataOnlyEditor(const UBlueprint* Blueprint) const;
};
Does anyone have any ideas as to what I may have overlooked when switching from the Asset Type Action to the Asset Definition for my Blueprint-based class?
EDIT: I found the issue. I was having a conflict between the GetMenuCategories function in my Factory class and the GetAssetCategories function in my Asset Definition (AD) class, so I removed GetAssetCategories from the AD class. Now everything works as expected.
you could set breakpoint in GetAssetClass and see what is happening during its registration
I did happen to check that yesterday, but at your suggestion I verified again that the GetAssetClass function is returning the ATOperationBlueprint class, which is expected and correct, I believe.
And it's properly being added to the registered Asset Definitions.
after constructing this slate object, how can i add more verticalbox slots ? (basically edit/add something to something already built
HeaderRow
.NameContent()
[
...
]
.ValueContent()
[
SNew(SVerticalBox)
+SVerticalBox::Slot()
[
SNew(STextBlock).Text(INVTEXT("Hello"))
]
];
// ...
// add a +SVerticalBox::Slot() back to HeaderRow ValueContent
im brand new to slate 😅
I’m not seeing where you’re creating the actual vertical box but if you’d create it with SAssignNew instead of SNew and assign it to a TSharedPtr you’ll be able to add more slots whenever you want
i deleted by mistake, code snippet edited
how do i use assign new ?
idk what ExposeAs is
welp its as simple as SAssignNew(HorizontalBox, SHorizontalBox)
Yup
this is how the socket browser is filled in engine code
FSubobjectData* Data = CachedNodePtr->GetDataSource();
if (Data && Data->HasValidSocket())
{
TSharedPtr<SSubobjectEditor> Editor = BlueprintEditorPtr.Pin()->GetSubobjectEditor();
check(Editor.IsValid());
FSubobjectEditorTreeNodePtrType ParentFNode = CachedNodePtr->GetParent();
FSubobjectData* ParentData = ParentFNode.IsValid() ? ParentFNode->GetDataSource() : nullptr;
if (ParentData)
{
// #TODO_BH Remove const cast
if (USceneComponent* ParentSceneComponent = const_cast<USceneComponent*>(ParentData->GetObjectForBlueprint<USceneComponent>(Editor->GetBlueprint())))
{
if (ParentSceneComponent->HasAnySockets())
{
// Pop up a combo box to pick socket from mesh
FSlateApplication::Get().PushMenu(
BlueprintEditorPtr.Pin()->GetToolkitHost()->GetParentWidget(),
FWidgetPath(),
SNew(SSocketChooserPopup)
.SceneComponent( ParentSceneComponent )
.OnSocketChosen( this, &FBlueprintComponentDetails::OnSocketSelection ),
FSlateApplication::Get().GetCursorPos(),
FPopupTransitionEffect( FPopupTransitionEffect::TypeInPopup )
);
}
}
}
}
the important thing is FSlateApplication::Get().PushMenu, the issue is that i dont know what parent widget to pass
this is my current test code
+SHorizontalBox::Slot()
.AutoWidth()
.HAlign(HAlign_Center)
.VAlign(VAlign_Center)
.Padding(2.0f, 1.0f)
[
PropertyCustomizationHelpers::MakeBrowseButton(
FSimpleDelegate::CreateSP(this, &FFUPickerSocketCustomization::OnBrowseSocket), // what do i run in OnBrowseSocket to have the same combobox behavior ?
FText::FromString("Select"),
TAttribute<bool>(ComponentName.IsValid())
)
]
Did you try using the widget reflector? SComboBox is a widget, fwiw.
i dont see how this would help me
but maybe i can do differently than the engine, by adding directing a combobox (not dynamicly on click)
I mean, I don't really know what you're trying to do here so you know, it usually takes some reverse engineering to extend an editor toolkit, but what about this ' SNew(SSocketChooserPopup)'
It seems that's the widget being spawned for that push menu method
Im struggling to get the owning actor (to iterate comps afterwards)
Im testing with my property placed on a actor component
check what i used to determine actor context. it depends what you have as edited object
thanks!
im wondering if i can get the Slate widget of how the engine would generate a property so i can append it to my vertical box
for example, get how it renders your comp ref struct, and append your cool widget in mine
Exi suggested something yesterday but i didnt work
you can just save the SVertBox as a member of customization and add/remove slots on it
popup?
it is a SComboButton, which has GenerateMenu parameter - which gives you ability to make any slate popup
so thats not possible ?
like PropertyHandle->GetGeneratedWidget()
like you would do to get the default widget of any engine type
you don't need it
default widget would be just text box if you have fname or fstring property
check CustomizeHeader of BCR
so default widget only works for "default" props ? you cant query other dynamic customize header result ?
"default widget" is what engine normally generates for property - text boxes, combox
while you customizing and using own value widget with things you want that do not match the engine defaults
i'd recommend drawing a preview in paint of how you expect your details to look like and go from that. it helps a lot
i understand that, im just wondering if i can get the result of a customize header
you mean the widget you generated?
just save it in a member variable of customization if you need accessing widget
if i have
MyStruct { MyVar, AquaBCRVar }
and MyStruct has a detailscustomization, will AquaBCRVar try to create its own detailscustomization ?
oh, i wouldn't recommend using BCR in your case
why ? it has everything i would like to have
you don't need to create customization, it will be picked by default
i need tho
after selecting the component from your custom details, ill run some code to fill my socket picker
you will have to rely on property handle of BCRVar and its OnPropertyChange event
yeah
but i dont know how to get your details customization result so its not overriden by mine
black: engine details panel
green: my details customization
red: my socket picker thing
blue: your details customization of BCR
what you do in CustomizeChildren ? you should add props to the builder
nothing, i dont know yet the diff between header and child
i assume header is for the title and child for a per-struct property widget ?
header is the first top level row, children is the nested items
yeah im dumb i had everything in header for now
so in CustomizeChildren i need to know when its your BCR property so i get "your default" widget ?
void xxxxx::CustomizeChildren(TSharedRef<IPropertyHandle> InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils)
{
uint32 NumberOfChild = 0;
if (InStructPropertyHandle->GetNumChildren(NumberOfChild) == FPropertyAccess::Success)
{
for (uint32 Index = 0; Index < NumberOfChild; ++Index)
{
TSharedRef<IPropertyHandle> ChildPropertyHandle = InStructPropertyHandle->GetChildHandle(Index).ToSharedRef();
StructBuilder.AddProperty(ChildPropertyHandle);
}
}
}
that will construct default child members for the member props of your type details customization
alternative
void xxxxx::CustomizeChildren(TSharedRef<IPropertyHandle> InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils)
{
// add what would display in nested rows
TSharedPtr<IPropertyHandle> Handle = InPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FYuorStruct, BCRVar));
IDetailPropertyRow& PropertyRow = StructBuilder.AddProperty(Handle);
// here you can customize row if you want
}
i would again recommend starting from simple thing first with combobox and comobutton menu for two fnames, instead of integrating other plugins
since you're new to slate and editor customization
that was my first iteration yesterday
im getting a infinite loop when trying your code
i guess im doing something wrong
is the IPropertyHandle passed to customize child the a struct property (the child) ?, or the struct (the parent) ?
seems like i was giving the parent in a recursive way
it is always the root - the FYourStruct
Sorry to intervene, but just looked at your plugin. Always wanted a component picker on the actor. Nice job 🙂
What I noticed was this pure node with execute pins for 5.5? What is this?
you can make any pure function use Exec pins in 5.5. its really useful for nodes with multiple outputs
This is awesome.
tx
so finally map find only runs once, not twice for bool to check if it's in and the query again for the value
my Slate Frame View from unreal insights, showing massive # for updated widgets in a silly stress test scenario. how should i attempt to lower this number?
- i have added LOD switchers which helped performance a lot; hiding little widgets when i zoom out a bit
- i have tried naively wrapping the whole widget in an SInvalidationPanel... it made a huge difference but it crashes, on a graph pin widget, when i change zoom 🙃 do i need to try wrapping dozens of lower-level individual widgets in these manually instead, so that i avoid wrapping the troublesome crash causers?
- i have tried naively wrapping the whole widget in an SRetainerWidget, it made a small difference but also crashed the same way
- my widget relies almost entirely on function binds (99% TAttribute); i've been planning on converting it to event-based updates for performance, but i've tried unbinding a few of these to see what it might gain me... and it makes absolutely no difference (i get the exact same numbers whether i'm querying a few hundred things on tick or just using a static constant value). does this make sense?
that is UE 5.5 core feature, exec pins on pure nodes
how slate tree of a single node looks like? could it be minimized?
also could widgets overlapping causing invalidation / redraw of others (since they all seem to have P/Paint invalidation reason)
can also try override ComputeVolatility() and manually control it ( return Super::CompulteVolatility() || bNeedsRedraw; )
the tree is... fairly deep 😄 but it's all primitive widgets. actually, i might have just had a "oh that's how i use this" moment, in most frames the upper Slate Frame View is empty. so... maybe invalidation isn't my biggest problem here?
more typical frame:
i am tempted to try something super wacky and replace complex widgets with just a single text/icon, and spawn the complex widget dynamically on hover
(no invalidation events on most frames - yet invalidation has to be at least partly my problem, since wrapping the majority of the node's body in SInvalidationPanel gave me a big FPS boost!?)
Any thoughts on what would be the simplest way to create a wrap box that lets the user rearrange elements by dragging them?
😦
Hey guys! Having fun with optimizing UI 😄
I saw that CanvasPanel was a container to avoid as much as possible because they are not that cheap.
So I wondered when they would be absolutely necessary. For instance, I have a compass on top of my game that show some icons which move on a tick rate. Would there be a way to avoid the canvas using overlays? Or is it a usecase that make sense?
I use what many would consider an unhealthy amount of canvas panels. and performance is still fine.
Using 1 Canvas panel won't tank or cripple your performance... The Canvas Panel=terrible performance is just a myth because the claim is something along the lines of it ticks 5 times per tick or something like that...
Just use it if it works for you... If in doubt measure the performance... saying something is "bad or terrible" without actually measuring is something i don't believe in
If it was terrible they would simply deprecate it and phase it out... its not terrible, it depends on what you're doing on it , and if its the only obvious solution for your project just use it...
measure if in doubt, sometime its not the canvas, but a function you wrote that runs on the canvas...
the profiler will show you what and where in any case
Is there a way to hook into an event that will tell me when the IProperyHandle I'm working with is getting invalidated and that my custom property details widget will be rebuilt?
details rebuild recreates everything from scratch
startingfrom CustomizeHeader/Children
I have a details class that opens a window. I'm trying to find a more elegant way to deal with the entire tree being invalidated whenever I perform certain actions.
Because, when that happens, I need to store some data so I can re-open the window and re-display the same data I was looking at before the rebuild
close window in c++ destructor of details
There seem to be some nuiances. Sometimes I remove an element in the middle of an array and the entire details rebuilds. Sometimes it doesn;t.
Ok
I was thinking about that, but wasn't sure it I would run into edge case crashes
Plus on destruct how do I know that the user didn't just close the asset, in which case, I don't want to try to store data / re-open the window on the next time the asset is open
it is similar with popups- you close the when creator destroys
I guess rely on checking if the window is still valid? I could do that I suppose
you usually save pointer to created window in details class
Yes I am doing that
and in details destructor check and close it
Yeah, I can check if the window has been closed during destruct
I guess I will try that
So, you're talking about during ~IPropertyTypeCustomization?
Call my storing data logic within there?
yes, you can do that too but not advised
/* A handle to a property which is used to read and write the value without needing to handle Pre/PostEditChange, transactions, package modification*/
Wait... I shouldn't be using FTransaction and calling the events? Is this what is causing my widget to be rebuilt at seemingly random times? What about for adding to IPropertyHandleArray?
Thanks for your answer 🙂
In my case, I do have performance issues from a bunch of widgets that use canvas panels, hence my question
Good to know though that I should not consider it as evil and I can live with a few of them
Since ->AsArray won't work with TSet<>, and there isn't an equiv. ->AsTSet(), how do you modify a TSet? Do you have to modify the raw data or something?
I just saw on BenUI website that calling SetVisibility could be expensive on a Tick call.
Is anybody able to confirm that? I see that we do a lot of things even though I would have expected that we early return if the given visibilty is already set
depends why would you want to do that and how often you do that.
changing visibility will invalidate/repaint widget
if you invalidate widget every tick frame you will obviously end up repainting it every draw frame
I have a minimap that is a canvaspanel with a lot of images. For each of them I update the position and the visibility. Not a big opti here but it won't hurt to not call that function if it's not needed.
then it doesn't matter. you will redraw each frame anyway then
Speaking of invalidation - I'm a novice on that
I got 2 questions
1 - Changing the position also invalidate the widget, but does it also has an impact on its parent?
2 -Do you use the global invalidation for your game? Asking because I wonder if I do have to make my whole minimap volatile, since everything will be changed on tick rate?
If you're updating every frame anyway, it might be worth not even use slate elements and just paint them yourself.
Then you don't have to worry about state changes and events triggered useless code.
If you have a lot of elements.
Hey hey :) So im not really familiar with engine stuffs- im working on a school project rn where i have to make a main menu.
yesterday i was able to open the main menu when i pressed play and stuffs- but today its only opening the 3D template? I literally cant find where i switch to the main menu :,D pls help
i figured it out :,D 👍
May I ask what you mean by painting them myself instead of using slate elements?
Atm my minimap actually display userwidgets which is basically an image. The image itself never change, only the position and visibility.
Do you mean that I should call the OnPaint function ?
yead, like overriding OnPaint of base widget and drawing it at location you need
Alright I'll give a try then
With my current implementation, I'll have to pass the positions from the minimap to the images, but I guess I can cache the value and let the image paint function handle the rest
So I'll have to make the whole minimap volatile since I don't want to cache anything, right?
It doesn't seem to matter what I do, I can't get the W / E buttons to show the literal text W or E without making the buttons really wide:
N/ S show up no problem
SNew(SOverlay)
+ SOverlay::Slot()
.HAlign(HAlign_Center)
.VAlign(VAlign_Center)
[
SNew(SScaleBox)
.Stretch(EStretch::ScaleToFit)
.StretchDirection(EStretchDirection::Both)
[
SNew(STextBlock)
.Justification(ETextJustify::Center)
.Font(FCoreStyle::GetDefaultFontStyle("Regular", 8))
.Text(FText::FromString(*DirectionStr))
]
]
TSharedRef<SBox> SizeBox = SNew(SBox)
.HAlign(HAlign_Fill)
.VAlign(VAlign_Fill);
float WallThicknessPx = 22.f; // 1-row / 1-col bar
/* make it look like a thin bar */
if (Dir == EDungeonPointDirection::North ||
Dir == EDungeonPointDirection::South)
{
/* horizontal bar: full width, small height */
SizeBox->SetHeightOverride(WallThicknessPx);
}
else
{
/* vertical bar: full height, small width */
SizeBox->SetWidthOverride(WallThicknessPx);
}
SizeBox->SetContent(CellWidget);
Is the above not right?
what's your hope for that scalebox to be doing?
wonder if it's doing something
I'm just trying to throw crap at the wall and see what sticks
I'm just trying to get the E / W to show up
Also constexpr float WallThicknessPx
I didn't make it constexpr because I wasn't sure how live coding would handle that and I wanted to play with changing the values
put the W / E in and use the widget reflector to see what widget is driving the size of the box
oK
every widget will have "desired size" and "actual size" or something like that
Yep
text block's actual size is 0
scalebox's actual width is 0
desired size is 9
So what am I looking for? The furthest parent up the chain that doesn't match its desired?
like if the box's width is 57 and you think it should be 23... then maybe see if you can find the last widget that seems to be causing that 57 width (plus/minus any padding)
ideally, W text widget should have a width of something like 16, and then you could wrap that in an SBox with a manual override width of e.g. 24 to actually force the width of that button, and that 24 value would bubble upwards until a larger container
that's weird lol
the slot it's inside of might be forcing it down
Ok
i would normally AutoWidth your small buttons and the big green one would not be AutoWidth, so it stretches to fill.... and each of the 9 cells would be an SBox with a manual override width. i think?
Ok, let me try that
kind of looks like you might want to have an SGridPanel inside of an SGridPanel... each one is a 9x9 grid...
that might let you chop out the corners so the N / S buttons aren't wider than the X button... i also haven't used SGridPanel really much yet though
so now I have this:
return SNew(SVerticalBox)
+ SVerticalBox::Slot()
.AutoHeight()
.HAlign(HAlign_Fill)
[
BuildEdgeButton(Offset, EDungeonPointDirection::North, true, bHasNorthEdgeRule)
]
/* 0) centre-big “prop on/off” button ------------------------- */
+ SVerticalBox::Slot()
.FillHeight(1.0f)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot().AutoWidth()
[
BuildEdgeButton(Offset, EDungeonPointDirection::West, true, bHasWestEdgeRule)
]
+ SHorizontalBox::Slot()
/*.FillWidth(1.0f)*/
[
BuildOffsetCellWidget(Offset, bHasRule, false) // existing green / gray button
]
+ SHorizontalBox::Slot().AutoWidth()
[
BuildEdgeButton(Offset, EDungeonPointDirection::East, true, bHasEastEdgeRule)
]
]
+ SVerticalBox::Slot()
.AutoHeight()
.HAlign(HAlign_Fill)
[
BuildEdgeButton(Offset, EDungeonPointDirection::South, true, bHasSouthEdgeRule)
];
and this is BuildEdgeButton:
TSharedRef<SWidget> CellWidget = SNew(SBorder)
.BorderImage(FCoreStyle::Get().GetBrush("NoBorder"))
.ToolTipText(FText::Format(LOCTEXT("OffsetCellButton", "{0} Edge"), FText::FromString(*DirectionStr)))
.OnMouseButtonDown_Lambda([this, Cell](const FGeometry&, const FPointerEvent& Ev)
{
if (Ev.GetEffectingButton() == EKeys::RightMouseButton)
{
OpenCellContextMenu(Cell);
return FReply::Handled();
}
return FReply::Unhandled();
})
[
SNew(SButton)
.ButtonColorAndOpacity(bHasRule
? FLinearColor(0.f, 0.5f, 1.f)
: FLinearColor(0.15f, 0.15f, 0.15f, 0.4f)) // empty
.OnClicked_Lambda([this, Cell, bHasRule]()
{
/* ALT-click -> remove the entire offset entry (if one exists) */
const bool bAlt = FSlateApplication::Get().GetModifierKeys().IsAltDown();
if (bAlt && bHasRule)
{
/* ---------------- REMOVE ---------------- */
FScopedTransaction Tx(LOCTEXT("RemovePointOffset", "Remove Point Offset"));
PointOffsetsHandle->NotifyPreChange();
/* delete from underlying array */
TArray<void*> Raw;
PointOffsetsHandle->AccessRawData(Raw);
if (Raw.Num())
{
TArray<FPointOffset>* Arr = static_cast<TArray<FPointOffset>*>(Raw[0]);
Arr->RemoveAll([&](const FPointOffset& P) { return P.Offset == Cell; });
}
PointOffsetsHandle->NotifyPostChange(EPropertyChangeType::ValueSet);
PopulatePointOffsetsGrid(); // redraw grid
}
else
{
/* ---------------- EDIT (or create-then-edit) ---------- */
OnPointOffsetCellClicked(Cell);
}
return FReply::Handled();
})
.Content()
[
/*SNew(SOverlay)
+ SOverlay::Slot()
.HAlign(HAlign_Center)
.VAlign(VAlign_Center)
[*/
/* SNew(SScaleBox)
.Stretch(Dir == EDungeonPointDirection::North || Dir == EDungeonPointDirection::South ? EStretch::ScaleToFit : EStretch::ScaleToFitX)
.StretchDirection(EStretchDirection::Both)
[*/
SNew(STextBlock)
.Justification(Dir == EDungeonPointDirection::North || Dir == EDungeonPointDirection::South ? ETextJustify::Center : ETextJustify::Left)
.Font(FCoreStyle::GetDefaultFontStyle("Regular", 8))
.Text(FText::FromString(*DirectionStr))
/*]*/
/*]*/
]
];
TSharedRef<SBox> SizeBox = SNew(SBox)
.HAlign(Dir == EDungeonPointDirection::North || Dir == EDungeonPointDirection::South ? HAlign_Fill : HAlign_Left)
.VAlign(VAlign_Fill);
float WallThicknessPx = 22.f; // 1-row / 1-col bar
/* make it look like a thin bar */
if (Dir == EDungeonPointDirection::North ||
Dir == EDungeonPointDirection::South)
{
/* horizontal bar: full width, small height */
SizeBox->SetHeightOverride(WallThicknessPx);
}
else
{
/* vertical bar: full height, small width */
SizeBox->SetWidthOverride(WallThicknessPx);
}
SizeBox->SetContent(CellWidget);
This is now the result:
If I make the width override very high then it works:
float WallThicknessPx = 22.f; // 1-row / 1-col bar
/* make it look like a thin bar */
if (Dir == EDungeonPointDirection::North ||
Dir == EDungeonPointDirection::South)
{
/* horizontal bar: full width, small height */
SizeBox->SetHeightOverride(WallThicknessPx);
}
else
{
/* vertical bar: full height, small width */
SizeBox->SetWidthOverride(WallThicknessPx * 4.5);
}
SizeBox->SetContent(CellWidget);
Setting the size box to fill for horizontal makes the button at least stretch to fill the empty space
Getting closer
Hey guys!
I have enabled global invalidation which works great overall except with my widget components set on Screen Space.
These widget components seem to be rebuilt in the Slate::Prepass each frame even if I mark the widget (and all the children) volatile.
I suspect the SScreenManager that stores and handles the widgets to trigger an invalidatation no matter what happens.
Has anybody a workaround to have the widget components and/or their slate container to ignore the global invalidation?
I saw that @fervent plume got the same problem 3 years ago. I hope there has been a solution since then 😄
What is the best way to debug why setting a mask handle's value fails? I can see a valid property path. The property handle is valid and the backing property doesn't say it has been deleted. I copied the code from another widget I made and everything looks ok, but for some reason, setting the mask's value fails.
SharedRef<IPropertyHandle> PairHandle = Map->GetElement(UpdatedIndex - 1);
TSharedPtr<IPropertyHandle> KeyStruct = PairHandle->GetKeyHandle();
TSharedPtr<IPropertyHandle> MaskHandle =
KeyStruct->GetChildHandle(GET_MEMBER_NAME_CHECKED(FDungeonPointFlagMaskKey, Mask));
if (MaskHandle->SetValue(Mask) == FPropertyAccess::Fail)
{
UE_LOG(LogTemp, Warning, TEXT("Failed to set value on KeyHandle!"));
PointRuleSetMapHandle->NotifyFinishedChangingProperties();
return nullptr;
}
I'm using Debug Editor, but pressing F11 just skips stepping into the MaskHandle->SetValue invocation
Well, that's not 100% true. It steps into this:
Then I can't step into the actual method
you could see that all of those just unconditonally return Fail and do nothing
just look slightly above at macro body
usually you switch to assembly view and step for the calls if some debugging is needed
I saw that method body and was wondering if I was hitting that block. I then searched for uint8 and found only one instance, which dealt with enums. That ultimately led me to trying int32 (which worked)
Never switched to something called assembly view
if you dealing with uenum you have to know what underlying type it is ( bit, byte or uint32)
uint8 (BP exposed)
Which apparently is not supported by IPropertyHandle
You must first cast to int32
from just this code block there is no way to guess what type Mask is nor what kind uproperty under MaskHandle / FDungeonPointFlagMaskKey is
I was trying to find the code. My adderall hasnt kicked in yet, so I get easily confused. I will find it in just a moment,.
It should be uint8
Oh, lol. It's not uint8. It is int32.
I thought I had changed it. What did I change to uint8 the other day then o.O
Now the question is... will it stop working if I change it to uint8
Ultimately mask is being converted to a bitmask enum later (using uint8 since it is BP exposed)
Ok, here is a question for you, do you know why initializing an array element to a valid FInstancedStruct type forces the entire property details editor to be rebuilt? Is there a way to stop that behavior?
no, that is just how containers work
That's unfortunate. That is such a terrible design.
It's like someone thought "how can I make the most painful system possible, just for kicks?"
just because of how wild possible customizations can be it is easier to just trigger refresh than try guess what change would affect
reevaluating conditions, customizations etc
Ok, well I'll take your word for it. It would be nice to have an override though. Maybe that is an oversimplification wish though
Just makes it very painful to build interactive and rich experiences
If you want good experiences, the details panel is not your friend. 😛
Yeah, this is crazy. Any form of automation seems impossible.
I want to click a button and have it modify two elements in an array. It seems that sometimes doing that causes a crash.
Do the IPropertyHandles become all deleted before even exiting your code? I thought it happened after leaving my method
It can happen during your code if you don't hold a strong pointer to them and you call some engine method that deletes them.
I'm trying to make it to where, when you click one of those buttons, the second array element gets added and the first one gets modified. Holding a strong pointer to them... ok I will look into that.
Adding the second element there and initializing the FInstancedStruct will trigger a rebuild, but that happens after I have left my method. Which is fine, as I've already started building my code to work with that. But if I can't even finish my current operation on the IProperties I already have, all within the same loop body, then that's freaking nuts.
Would changing the TSharedPtr<IPropertyHandle>s I'm using to build up my loop of operations to TSharedRef<> be sufficient?
Or would I need to actually hold on to the IProperty* instead of the IPropertyHandle?
I don't really get what you're trying to do but can't you just make your Instanced struct, set its members and only then add it to the array once it's all good and done?
Well thankfully I think my recent change fixed it, but thank you.
Now I need to make the narrow buttons disappear by default unless either the state has been set or the user hovers over the separation between buttons 🙂 so I am working on that atm.
Ok, maybe you all could help me with this - I am trying to inherit from SBorder so I can more easily hook into mouse enter / leave events. This is normally a straight forward type deal, except for some reason, my inherited version doesn't seem to allow for the same arguments (such as BorderImage) - how can I have my inherited type forward all arguments? If it were a normal class I would just call the base constructor, but this is slate, so.
Would I just call SBorder::Construct?
(within my inherited type's construct)
Well, that didn't work. I got a compiler error:
22>G:\src\SpaceScrap\Source\LyraEditor\Utilities\DungeonRoomPropertyEditor.cpp(551): error C2676: binary '[': 'SHoverBorder::FArguments::WidgetArgsType' does not define this operator or a conversion to a type acceptable to the predefined operator
class SHoverBorder : public SBorder
{
SLATE_DECLARE_WIDGET_API(SHoverBorder, SBorder, SLATE_API)
public:
SLATE_BEGIN_ARGS(SHoverBorder) {}
SLATE_ARGUMENT(FMargin, Padding)
SLATE_EVENT(FSimpleDelegate, OnEnter)
SLATE_EVENT(FSimpleDelegate, OnLeave)
SLATE_EVENT(FPointerEventHandler, OnMouseButtonDown)
SLATE_END_ARGS()
I'm trying to inherit SBorder like above
Search this channel for 'parent arguments'
Ok, I did that, but using SLATE_ARGUMENT(SBorder::FArguments, ParentArgs) does not fix the compiler error
TSharedRef<SWidget> CellWidget = SNew(SHoverBorder)
.ParentArgs(SBorder::FArguments().Padding(0.0f)
.OnMouseButtonDown_Lambda([this, Cell](const FGeometry&, const FPointerEvent& Ev)
{
if (Ev.GetEffectingButton() == EKeys::RightMouseButton)
{
OpenCellContextMenu(Cell);
return FReply::Handled();
}
return FReply::Unhandled();
}))
It's complaining about my SHoverBorder::WidgetArgsType is not subscriptable
Asking ChatGPT feels like it got my closer. I added SLATE_DEFAULT_SLOT(FArguments, Content) and then that compiler error went away. Then I got the linker error regarding PrivateRegisterAttributes. So that led to me adding SLATE_IMPLEMENT_WIDGET(SHoverBorder) to my cpp (that was missing but I do have the SLATE_DECLARE_WIDGET_API(SHoverBorder, SBorder, LYRAEDITOR_API) defined) and now I am getting 4>G:\src\SpaceScrap\Source\LyraEditor\Utilities\DungeonRoomPropertyEditor.h(14): error C2487: 'PrivateRegisterAttributes': member of dll interface class may not be declared with dll interface
It complains the macro doesn't have enough arguments if I try to drop the LYRAEDITOR_API from the arguments passed in
I may have gotten it
It's compiling at least. Whether it runs without crashing is another story
Shouldn't setting padding to 0 cause the space between the green bar and the button to disappear? This is an SVerticalBox / Slot inside of a grid slot
There is just so much spacing between the buttons. I want them to be closer together
Are you using the widget reflector?
Yes, but that shows very little information; unless I am using it wrong.
The green box is actually drawn from me using widget reflector
you might be setting hte padding one level too high or low
like a child slot vs a parent slot
I've been trying to set it to 0 everywhere to get the gap to disappear
kek. so what is that, an SButton? is that screenshot five SVerticalBoes each with 3 slots or something?
It's ok though. I am moving on for now. I've spent so long on this property details panel. I've spent more time just trying to build a simple helper, that I would have spent less time just manually adding everything for 100+ rooms. Whoever built this system did not build it to work well with interactive experiences
Just when I think I got it down - CRASH - your IProperty has been deleted!
LIke gee thanks! I just wanted to automate three levels of properties, like it's the year 2025! That would've been great! Not have my stuff deleted out from under me because I just deleted one element in a completely different level!
Maybe it would've been solved by just accessing the raw data and doing everything that way, idk
Just ridiculous. It's the crappiest system I've ever seen in unreal to date
Every single widget built in unreal uses slate, ultimately you can get it to look and behave pretty much any way you'd like, it has some quirks but it's like, the cornerstone of the entire editor.
I guess then I picked the one thing with the most quirks. I wanted to have a helper widget where I could click buttons on a grid and have it automatically setup my nested TMap
because its an eye sore to work with via the regular editor
Like, that's a lot to have to click and manage for each cell
But, it's not worth it. As soon as you try to automate deleting one of the rules, or if you initialize the FInstancedStruct (PointOffsetRule) - BAM! you have a 50%+ chance to crash now as you continue to to modify the other rules
I should at least be able to make all of my changes before handing control back to the engine's code without my IProperty handles becoming invalid, short of doing something where that obviously doesn't make sense, like if I delete a parent handle
But, it doesn't work that way apparently.
There's also the possibility that you're overcomplicating things and are trying to do something in some specific way that causes your issues even though some simpler alternatives exist.
Looking at the things you posted, PropertyHandles and the such, it seems to me you're trying to directly manipulate memory and variables unreal is already looking at, which kinda sounds like a premature optimization, I don't know exactly what you're trying to achieve but idk, you don't really need to make things super optimized for editor use cases, making a copy of the data, manipulating it and copying it back to the right place when you're done is perfectly fine, it's not a runtime game scenario.
That picture I last posted. In summary, I am trying to automate changes to every property in that picture. I have an array of structures, which in turn contain nested structures, shown in the picture above. I was trying to build a custom property details panel to help automate modifying that data. My latest efforts were focused on creating the grid UI (SGrid). This grid, which would be a grid of buttons, would allow the user to click on it, and, depending on which keys were held during the click (ctrl, alt, ctrl+alt, etc.), that mouse click would trigger adding, modifying, removing, or a combination of those, new array elements, configuring the properties contained in the element when applicable.
The problem I constantly was running into was when I would try to either add a new rule or remove an existing one, if I had multiple rules to modify, upon moving to the second rule, sometimes it seemed the IProperty would no longer be valid. I hadn't even left the loop yet that had modified the first rule
So I don't know how you are supposed to manage multiple levels of properties in one single action using slate. Apparently the way I was doing it was wrong.
My comment about using the raw data was meaning, that, instead of calling things such as MyArrayHandle->RemoveElement(Index) I maybe should have first collected all of the raw data, modified it, and then just called MyRootLevelHandle->NotifyChangingProperties(). The hope here being it would be one "change" and then I wouldn't have to worry about anything being deleted out from under me.
i don't have time to carefully parse this: are you saying that you are modifying a parent array (adding elements) and then trying to modify child properties of that array's elements? if so then yes everything would get invalidated sometimes, that's how arrays work (they move to a larger/smaller home when they grow/shrink)
you always have to find the child properties after modifying the array. this can be "fixed" by not storing pointers to elements at all, and always looking into containers via an index int, or map keys, every time
I would expect deleting a parent would invalidate a child. I'm not in front of the code. Maybe I'll take another stab at it in a few weeks and try a different approach.
I seemed to have issues when deleting an element in a child array and then when going to a different parent arrays children, I'd get the crash, even though I hadn't deleted anything from that particular parent or its parents
Calling access raw data and then calling .Num() to check if there were any elements returned would sometimes cause the crash
In summary, the handles seem to be unstable as soon as you touch a container anywhere m
Sure fair enough, in your earlier messages you said something about when adding a rule (to an array??) And then accessing the next rule (by... a cached property handle maybe??) it would crash. That crash would make sense, adding the new rule to an array of rules would invalidate anything pointing to anything of that rules array
You've given me a great deal of help and I appreciate it. Yes the crash would happen when modifying the TMap and underlying array elements. Essentially I have an array of a custom structure which holds an FIntPoint and a TMap property, and that TMap's value is a custom struct which holds a TArray of a custom FInstancedStruct. Actions from the user will trigger a whole range of actions; adding, deleting, merging values from one TMaps value with another (which would trigger a delete of the merging from tmap), etc.
Sometimes I would be able to repeat the actions in order and be ok and sometimes it would crash, seemingly always on the .Num() of the TArray<void*> that gets populated by calling access raw data, which I found odd, as it's an array. I wouldn't think calling .Num of it would cause a crash ever
i don't want to explain something if you already know how it works, do you know how arrays, maps, any container types periodically resize their memory layouts?
I know that unless you set a size, each add will cause a relocation to make the container fit
And the size just works past that because it allocates the memory up front
yeah, each add can cause a relocation - it will typically add two to ... many new slots. same with removing elements. which is a likely explanation why it would "sometimes" crash
So two questions come to mind from this:
#1 (most important): why would calling .Num() on an array ever cause a crash? I'm in bed now, so I can't easily verify this, but I thought .Num() would just return an internal counter. Meaning every pointer it holds could be invalid and it still wouldn't matter.
#2: then I believe my idea of accessing the raw data for the root array element and then making all changes to the underlying data, finally calling the appropriate notify properly change calls after doing all the adds, merges, and deletes, was on the right track then?
Side note: I'm fixating on the .Num() only because that's where the stack trace would lead me in my code once I hit a read access violation. Not sure if DebugGame Editor is good enough to trust that, but that was consistent with the crash. Each time I could follow the callstack back to my user code calling Num() on the TArray I passed in to IPropertyHandle->AccessRawData(SomeArrayHere)
if .Num() was being called on the parent-most container, then i'm not sure. that should never get moved around.
if .Num() was on any of the child containers, and it was being called after any parent container got modified... then that'd be a reliable crash
Ok, then I guess I need to study TArray's source a bit. I'm not sure why it'd ever cause an issue, because it's a value type. So that means Num is doing more than I am assuming
well it (potentially) all has to do with using "old" pointers to data inside containers
Right, so of it involves, internally, accessing those old pointers for some reason, then that definitely makes sense.
I just wasn't expecting that as the behavior
TMap<Key, TArray<Stuff>>
you start by finding a pointer to one of the arrays (aka an IPropertyHandle in this case)
then you do something to this map... that pointer to one of this map's array is no longer pointing to that array. it's pointing to illegal memory. sometimes
When calling .Num()
Ok, so then would #2 be the right approach for applying cascading changes?
Or multiple levels of changes would be a better way to say it
maybe, hard to say without seeing some pseudocode or something
It sounds like I need to make all of the changes, maybe even to a copy, and then just overwrite the root TMap's value
Ok. That's fair. We probably have gone as far as possible with me being in bed on my phone lol. Thank you for taking the time to read all of my frantic writing 😀 I was getting frustrated and had to switch to something else for a few. I'll give it another try soon with a more clear head.
Still opened to anysuggestion on how to make the widgets in a widget component volatile
I suspect that the issue is not the widget itself but a box/container wrapping the widget that always invalidates but is not volatile
I’m creating a class detail customization called FATOperationDetailCustomization inheriting from IDetailCustomization. As far as I can tell, my code is working for non-struct types, as shown in the first picture attached below.
However, for struct types, the struct widget itself is missing, and I can’t figure out why. (See the second picture attached below.) I’ve looked through chats on here in an attempt to find answers. Some people seem to delete their questions after receiving answers, making it difficult for me to determine whether the answers given are exactly relevant to my current problem.
The issue seems to be the CreatePropertyValueWidget() function in my below code when applied to struct property types.
/** Create the custom row. */
CategoryBuilder.AddCustomRow(FText::FromString(Property->GetName()))
.NameContent()
[
PropertyHandle->CreatePropertyNameWidget()
]
.ValueContent()
[
SNew(SHorizontalBox)
/** Standard property value. */
+ SHorizontalBox::Slot()
.FillWidth(1.0f)
.Padding(0, 0, 4, 0)
[
PropertyHandle->CreatePropertyValueWidget()
]
/** Per-animation toggle. */
+ SHorizontalBox::Slot()
.AutoWidth()
.VAlign(VAlign_Center)
.Padding(4, 0)
[
SNew(SCheckBox)
// stuff
]
];
Does anyone have any suggestions? I honesty don't know where to even look right now.
yeah, CreatePropertyValueWidget is dumb and breaks if the property itself has a property type customization. trying to find the other way to do it
Ah, so you're working on the same thing?
IDetailPropertyRow& Row = Group.AddPropertyRow(Property.ToSharedRef());
TSharedPtr<SWidget> NameWidget;
TSharedPtr<SWidget> ValueWidget;
FDetailWidgetRow WidgetRow;
Row.GetDefaultWidgets(NameWidget, ValueWidget, WidgetRow);
Row.CustomWidget(true)
.NameContent()
[
NameWidget.ToSharedRef()
]
.ValueContent()
[
ValueWidget.ToSharedRef()
];
try this as a starting point for drawing a property row (add your horizontal box / checkbox back into value content similar to how you already did it)
Thanks! I'll try that and see how it goes.
That approach at least shows something correctly, but it's stuck inside a group. I'll keep looking and see what kind of hackery is needed to get the display be fully as I'm hoping it to be.
What're you trying to do with the custom checkbox?
should be able to do the same thing using Category->AddProperty where Category is IDetailCategoryBuilder ... if that helps give you a breadcrumb
i'm using both methods in this (this file is not really cleaned up, it's first iteration so might be hard to follow) https://github.com/HomerJohnston/UE-Yap/blob/main/Source/YapEditor/Private/YapEditor/Customizations/PropertyCustomization_YapGroupSettings.cpp
I'm using it to enable/disable another set of widgets for what I'm calling "per-animation" control for my plugin. Essentially, when the checkbox is enabled, the user will have control over how a particular property (defined in a specific class) is applied to each individual animation during the modification process.
The concept is simple, but I'm probably not doing the best job explaining it without providing a better explanation of my plugin.
I was actually attempting to do something with the Category.AddProperty(PropertyHandle), but I was stuck trying to figure out how to customize the PropertyHandle before passing it into the AddProperty function.
Thanks for the repo link. I'll take a look at your code and hopefully pull some useful nuggets from it.
what would you hope to achieve by customizing the property handle?
or maybe i should say this: do the same thing i did with the group code above
AddPropertyRow and AddProperty are close cousins
I'm not sure. 😅 Maybe I shouldn't be messing with the Property Handle. This area of modification is very new to me. I have a goal output, but I don't yet know how to achieve it.
Okay, yeah, I'll look into doing that.
I guess since CategoryBuilder.AddProperty(PropertyHandle) works for the unmodified (default Slate) property, I figured I needed to modify the PropertyHandle in some way to get my custom appearance before then feeding it into the AddProperty(PropertyHandle) function.
Use a lot of toptionals and save yourself a lot of work.
Or edit condition / Edit condition hides.
I need something fairly generic and automatic, since users can define their own properties via C++ and BP (when inheriting from a certain class in my plugin), and certain properties, such as float, bool, int32, and some struct types (FVector, FVector2D, FVector4, and FRotator), will have the custom checkbox associated with them. I don't see another "easy" way aside from modifying the Details View. I'm almost there. The only thing I can't get to work right now are the struct types.
If I wasn't giving users control to make their own stuff, I'd do as you said, which is actually what I did in my first version of my plugin.
Of course, maybe I could just make custom widgets for specifically the struct anyway, if that's what you're saying.
So you want somebody to be able to jsut define a property, let's say a fvector, and it automatically gets this extra option and saves some meta data internally?
And you customise the details panel to show the option and that editable metadata?
I haven't worked out all the details, but, yes, your description of what I'm aiming to achieve seems accurate to what I have in mind.
Just to clarify, let's say the user creates an FVector in my special class (in either C++ or BP), there would be the checkbox option that automatically is created in the Details View (of that class and my custom editor) that, when enabled, creates something like the below (in another spot I will choose), where there are FVector widgets for each animation. The checkbox for structs in the Details View is what I'm stuck on. The generation of the other widgets in response, as shown below, I believe I already have down (though I often overestimate what I understand until I get kicked in the face).
First off, I wouldn't put the checkbox in the value widget area.
I would create an extension... extension.
The extension buttons are like reset to default and add key, etc.
You can register your own hook there without changing individual rows.
Secondly I wouldn't change the actual row for the widget, but simply add another row below it if the checkbox was enabled.
So you don't actually edit any rows.
This should stop anything failing.
It'd be the least intrusive way to do it, at least.
Things like vectors, say, don't produce single rows in details panels. They actually produce multiple rows. That's probably why your structure customisation is failing for fvector.
Oh, wow. Okay. Thanks for the detailed suggestions. I will try those and see if they solve some issues.
for academics: yes and no... this is because of a semi-annoying fact that the CreateValueWidget (sp?) function, and the other CreateNameWidget (sp?) function, don't look up whether the property type has a propertytypecustomization or not. you can do the lookup and draw manually which is kind of painful to figure out, or you can use the category/group property functions which already do it
It's not really even a customisation issue. It's just that CreateValueWidget doesn't do shit for structures 😛
yeah it's why i said both yes and no - if the system would draw the type's property customization then it wouldn't be an issue. otherwise, yeah if using the default row drawers then just creating the one row "value widget" won't draw all the children which is what you're talking about
either way those other functions do all the secret sauce (for cases where you actually do want to do everything fairly custom)
The below function seems to solve the immediate problem I was having with regard to row construction. The custom checkboxes seem to at least visually appear. I have yet to implement their full functionality. Maybe someone else will find the below useful.
void FATOperationDetailCustomization::CreateCustomPropertyRow(IDetailLayoutBuilder& DetailLayoutBuilder, TSharedPtr<IPropertyHandle> PropertyHandle)
{
if (!PropertyHandle.IsValid()) return;
FProperty* Property = PropertyHandle->GetProperty();
if (!Property) return;
uint32 PropertyHash = GetPropertyHash(PropertyHandle);
/** Create a custom category. */
IDetailCategoryBuilder& CategoryBuilder = DetailLayoutBuilder.EditCategory(PropertyHandle->GetDefaultCategoryName(),
FText::FromName(PropertyHandle->GetDefaultCategoryName()), ECategoryPriority::Important);
IDetailPropertyRow& Row = CategoryBuilder.AddProperty(PropertyHandle);
TSharedPtr<SWidget> NameWidget;
TSharedPtr<SWidget> ValueWidget;
FDetailWidgetRow WidgetRow;
Row.GetDefaultWidgets(NameWidget, ValueWidget, WidgetRow);
float FillWidth = Property->IsA<FBoolProperty>() ? 1.0f : 10.0f;
Row.CustomWidget(true)
.NameContent()
[
NameWidget.ToSharedRef()
]
.ValueContent()
[
SNew(SHorizontalBox)
/** Standard content. */
+ SHorizontalBox::Slot()
.FillWidth(FillWidth)
.HAlign(HAlign_Left)
[
SNew(SBox)
.WidthOverride(this, &FATOperationDetailCustomization::GetWidthOverride_StandardContent, Property)
[
ValueWidget.ToSharedRef()
]
]
/** Checkbox. */
+ SHorizontalBox::Slot()
.Padding(10, 0, 0, 0)
[
CreatePerAnimationToggle(PropertyHash)
]
];
DetailLayoutBuilder.HideProperty(PropertyHandle);
}
Wow. I finally discovered the ExtensionContent mentioned by Daekesh. 🤦🏻♂️ (It wasn't super clear to me before because I had never seen it in the engine.) As expected, that approach is much simpler and provides visual consistency. (No hackery needed.) I'll put the code below for anyone who, like me, was/is having trouble.
IDetailPropertyRow& DetailPropertyRow = CategoryBuilder.AddProperty(PropertyHandle);
/** Get the default widgets first */
TSharedPtr<SWidget> NameWidget;
TSharedPtr<SWidget> ValueWidget;
FDetailWidgetRow DefaultRow;
DetailPropertyRow.GetDefaultWidgets(NameWidget, ValueWidget, DefaultRow);
/** Construct the row with the extension. */
DetailPropertyRow.CustomWidget(true)
.NameContent()
[
NameWidget.ToSharedRef()
]
.ValueContent()
[
ValueWidget.ToSharedRef()
]
.ExtensionContent()
[
SNew(SBox)
.WidthOverride(22.0f)
.HeightOverride(22.0f)
.HAlign(HAlign_Center)
.VAlign(VAlign_Center)
[
SNew(SCheckBox)
// checkbox details go here
]
];
This is the result. All the custom checkboxes are aligned to the right.
I don't think you need to even do that.
There's an event that's broadcast to fill the extension buttons!
Oh, really? Ugh. Another thing to find. 😅
PropertyEditorModule.GetGlobalRowExtensionDelegate()
FPropertyEditorModule& PropertyEditorModule = FModuleManager::Get().GetModuleChecked<FPropertyEditorModule>("PropertyEditor");
FOnGenerateGlobalRowExtensionArgs RowExtensionArgs;
RowExtensionArgs.OwnerTreeNode = DetailTreeNodeWeak;
RowExtensionArgs.PropertyHandle = PropertyHandle;
PropertyEditorModule.GetGlobalRowExtensionDelegate().Broadcast(RowExtensionArgs, OutExtensionButtons);``` this is how we call it somewhere.
That's for getting the buttons, not adding to them.
But you get the idea of what info is available.
Awesome. Thanks! I'll look into that. I've spent days on this, so I'm going to laugh when my final result is only a few lines of code.
Hehe. Yeah.
The shit you learn when you use the engine for long enough is... useful.
I can see that. 😅
I'm doing some custom drawing in Slate OnPaint override, such as FSlateDrawElement::MakeBox() etc. But I want to also draw some circles, do I need to create the verts myself and use something like MakeLines() or is there a better way?
May the verts be with you.
Hey, how do you get or bind data in a C++ Slate Widget? For example binding a simple GetText Function when pressing a button to retreive some text from a different class. I have tried including some custom classes but i could not get it to work.
you need to have pointer to object you want read data from.
if you want to bind slate attribute - you just need to construct one to read data from appropriate source
show code
The data is in a custom helper class but i cant seem to include it in the Slate code
yep I did it with verts and it works fine, it's only for editor anyway
wdym by include? if it is just attribute - you can use lambda or static as data source for attribute
my dream has finally come tru
lol, a nightmare coming out for ya
Yea thank you, but i cant get the reference to the Helper class
Is it because it is an UClass?
define "can't get reference"
can't find the type, can't include header. where the header
cant include header
are they in same module ?
where is the header, where is the type you use it.
what does compiler says
so the helper class containing the data is in /Source/xxxx/public
the slate widget file is in /plugins
plugin module can not reference/have dependency on a game module
oh okay
So is there even a way to have a seperate editor module window containing Game/Runtime data?
editor module should not have game runtime data
normal setup usually like this
/Project/Source
/PrimaryGameModule type=runtime.
/EditorGameModule type=editor or uncookedonly. has dependency of primarygm
now i am lost on how to set this whole thing up
Usually you can make a runtime module in your plugin, then you can use the code from this runtime module in the game module and the editor module with no problem.
i now got a runtime module aswell as the editor module, then copied over the blueprint library i created to use within the Slate UI Window to the plugin folder from the Project Source. I still cant include the blueprint library header in the Runtime Module. Am i missing something here?
you'll have to share a bit of actual info for anyone to help you
e.g. build output log
No such file or directory
if you don't want to share anything, then good luck
i now got a runtime module aswell as the editor module
in the plugin?
/RuntimeModule type=runtime.
/SlateWindow type=editor
/RuntimeUtility -> Blueprint Library```
what about dependencies ? runtime depends on utility / editor depends on runtime?
i got "RuntimeUtility" in my RuntimeModule dependencies and "RuntimeModule" as a dependency in my EditorModule
the file Source/RuntimeUtility/Public/Header.h will be accessible via #include Header.h. search about general modules in UE
so, what is the error message?
also this is not slate, more like general #cpp or #plugin-dev
I'm having an issue I can't seem to resolve. I'm creating rows of SNumericVectorInputBox widgets (see picture), but for the individual entry boxes there appears to be some sort of equal-width entry box widgets behind them that are the height of the row. I'm having trouble determining how to get those "behind" ones to go away.
I construct the row entry using the below function.
TSharedPtr<SWidget> SATOperationAnimGrid::Construct_ParameterRowEntry(int32 AnimIndex, float RowHeight)
{
TSharedPtr<SWidget> ParameterRow = Construct_Parameters(AnimIndex);
return SNew(SBox)
.MinDesiredHeight(RowHeight)
.MaxDesiredHeight(RowHeight)
[
SNew(SBorder)
.BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
.BorderBackgroundColor(this, &SATOperationAnimGrid::GetBorderBackgroundColor_AnimRow, AnimIndex)
.Padding(FMargin(2.0f, 1.0f))
[
ParameterRow.ToSharedRef()
]
];
}
The above function, of course, calls the Construct_Parameters, which is shown below.
TSharedPtr<SHorizontalBox> SATOperationAnimGrid::Construct_Parameters(int32 MappedAnimIndex)
{
TSharedPtr<SHorizontalBox> ParametersHorizontalBox;
SAssignNew(ParametersHorizontalBox, SHorizontalBox);
// some more code...
int32 ColumnIndex = 0;
for (TFieldIterator<FProperty> PropIt(Operation->GetClass()); PropIt; ++PropIt)
{
FProperty* Property = *PropIt;
FName PropertyName = Property->GetFName();
// more code
/** Struct (FVector2D, FVector, FVector4 etc.) */
else if (FStructProperty* StructProperty = CastField<FStructProperty>(Property))
{
// some struct types....
/** FVector */
else if (StructProperty->Struct == TBaseStructure<FVector>::Get())
{
TSharedPtr<FPropertyVectorHandler<FVector>> VectorHandler = MakeShared<FPropertyVectorHandler<FVector>>(StructProperty, Operation);
using SNumericVector3DInputBox = SNumericVectorInputBox<double, FVector, 3>;
ParameterWidget = SNew(SNumericVector3DInputBox)
.Vector(VectorHandler.ToSharedRef(), &FPropertyVectorHandler<FVector>::GetVector)
.IsEnabled(bIsEnabled)
.OnVectorChanged_Lambda([VectorHandler](const FVector& NewValue)
{
VectorHandler->OnVectorChanged(NewValue);
})
.OnVectorCommitted_Lambda([VectorHandler](const FVector& NewValue, ETextCommit::Type CommitType)
{
VectorHandler->OnVectorCommitted(NewValue, CommitType);
})
.AllowSpin(true)
.ToolTipText(FText::FromName(PropertyName))
.bColorAxisLabels(true);
}
// more struct types....
}
//...
//...continue previous post.
/** Wrap the parameter widget in a box with consistent width. */
if (ParameterWidget.IsValid())
{
ParametersHorizontalBox->AddSlot()
.AutoWidth()
[
SNew(SBox)
.WidthOverride(ColumnWidth) // Use the cached column width
.Padding(FMargin(2.0f, 0.0f))
.HAlign(HAlign_Center)
[
ParameterWidget.ToSharedRef()
]
];
}
ColumnIndex++;
}
return ParametersHorizontalBox;
widget reflector?
I tried that, and it points to an SBorder [SNumericEntryBix.cpp ], if I remember correctly. (I'm on my phone right now.) But I haven't figured out what is actually causing the issue. I'm not seeing how SBorder could be causing what I'm seeing.
outer border from Construct_ParameterRowEntry 2px/1px
inner one from SNumericVectorInputBox inner Construct?
view the tree in widget reflector. by selecting elements you will have it highlighted so you will see which border is where.
Hmm. Apparently, setting AllowSpin(false) rather than AllowSpin(true) removes the inner box. Weird. I guess the SNumericEntryBox creates both a SSpinBox and a SEditableText and overlays them when AllowSpin(true). But why would the SSpinBox be affected differently than the SEditableText box when it comes to vertical size? Makes no sense.
This is a problem because I actually want the spin.
With AllowSpin(false) I have the below. No inner box is present.
You'll probably find that it isn't "fixed" by disabling that, but the boxes just overlap 100% instead.
Yeah, that's true. I guess I don't know how to resolve the issue. I've tried modifying all sort of things: height override, min/max height, alignment, etc.
i dont fully understand the difference between CustomizeHeader and CustomizeChildren
for IPropertyTypeCustomization
The header is the header row
The children are the children rows
Idk?
Children of what?
Because in the header row you are just making a full row (left and right content)
okay nvm, the header replaces the name of struct
The header is like the first row of a struct and the children and sub-rows (individual properties)
SOmething like that?
is there a slate widget that fill its dropdown from given enum ?
SEnumComboBox exists I think, but it's an editor only widget
If you need something for runtime look for the methods in the UEnum namespace cause it's possible to get the values out of a UENUM and use them to populate a regular combobox
can i find somewhere in the engine a png or w/e of a slate icon ?
i also wonder if i can use it IF it stays in UE
i already have a slate icon browser
i want the source file .png/... location
Find the string associated with the icon.
Look for that in the source code.
Find what it references.
if plugin lets you copy the FSlateBrush to clipboard - you could paste it into text editor and see the Resource property
i don't remember which of SlateStyleBrowser or SlateIconBrowser plugins had that function
from a FToolMenuEntry / FToolMenuSection how would you get the FBlueprintEditor (which "contains" this (i know the real container is a SStandaloneAssetEditorToolkitHost)) ?
do you walk through the chain of parent Slate widgets until the cats is successful ?
(im also wondering what is a "dynamic" tool menu entry)
so dynamic entries with Context was what i needed
im curious of the lifetime of the context objects
for example, does a UBlueprintEditorToolMenuContext lives as long as the BP editor is alive ?
Anyone knows if it's possible to make UCommonInputPlatformSettings::TryGetInputBrush asynchronous? Under the hood it just tries to get the FSlateBrush directly and if you see the widget for the very first time in the game you get a little loading hitch. Problem is that FSlateBrush::GetResourceObject returns a perfectly fine and valid pointer, however the image still needs to load for some reason and I can't for the life of me find out how to check if the image texture is loaded or not
Create a soft pointer to it and see if it's null would be a stupid hacky way. There's probalby an asset manager method somewhere.
I swear I tried everything lmao, I did convert it to a TSoftObjectPtr and checked whether it's valid, and it is!
To be honest it seems like a different sort of issue, I can see a small message top left of the screen saying that it's compiling resources when you see that little widget the very first time. The widget itself does load asynchronously, but obviously some of the elements in it (like CommonActionWidget) resolves FSlateBrush at runtime based on the passed in UInputAction variable. I am currently only testing in the editor, and it happens every time I close the editor -> start playing the level right away and invoking that widget.
Maybe it's not even going to be an issue in a fully cooked and packaged build, I should really test this I guess...
And yeah I'd async load it with the AssetManager like I do with everything else but the problem is that the UObject pointer that gets returned by FSlateBrush::GetResourceObject does not need any loading, so yeah...
It may not be loading the texture...
any guides on how to properly register a tab spawner ?
i found scattered info across forums posts, source code is a bit shady because of composition
It depends how you want the tab to function.
Like a major tab (asset editor) or like a level editor tab (details panel).
yea, depends what kind of tab you want. you can check my example here for SB
https://github.com/aquanox/SubsystemBrowserPlugin/blob/main/Source/SubsystemBrowser/SubsystemBrowserModule.cpp#L49-L76
thanks!
whats a "nomad" tab ? something that can exist whatever is active ?
in my case its a tab that only makes sense to exists while a actor BP is open
normal/global tab that you can drag to make a separate window
while level editor - locked within level editor tab
check BlueprintEditor if it exposes its tab manager if you want locked one
I highly doubt it.
yeah i tried getting how "My Blueprint", sub object editor etc was registered but i guess i was to tired back then to understand anything
honestly extending the editor in UE is kinda simple
you just have to know the code the first time 😆
copy ID, boom a new entry
it seems like it has tab manager exposed
TSharedPtr<IBlueprintEditor> BlueprintEditor = FKismetEditorUtilities::GetIBlueprintEditorForObject(Blueprint, true);
BlueprintEditor->GetTabManager()
FBlueprintEditorModule& BlueprintEditorModule = FModuleManager::LoadModuleChecked<FBlueprintEditorModule>("Kismet");
for (const TSharedRef<IBlueprintEditor>& BlueprintEditor : BlueprintEditorModule.GetBlueprintEditors())
{}
but that is looks like per-editor, not global. so you would need to track asset opening -> registering spawner
or dig more around
oh, there is a FBlueprintEditorModule BlueprintEditorOpened.Broadcast(BPType); can hook to that to register spawner
what special setup is needed for these ?
When you register the tab spawner (in the level editor) you can set a window menu thing.
idk if its for other cases but thsi is the actual code thats called when you open a BP
useful blog post talking about all the mess of FTabManager/FWorkflowTabFactory/FAssetEditorToolkit/... https://zhuanlan.zhihu.com/p/628655599
Having a slate crash from a displaying properties in one of our UAsset types. We have an asset type for designers to place POIs in our game. In it there is a drop down to choose whether the POI's placement is driven by PCG or a custom written function. If they choose PCG they then get to choose what's essentially our own version of a PCG instance (a PCG graph + a bunch of Uprop variables). If designers choose one of the more complex PCG bundles we have (which essentially is a TArray of these bundles) the editor crashes with a call stack that is all slate and none of our code. Essentially it looks like in the tick of some property the property is blank or something else is a bad ref. I get a pure-virutal function call error. Trying to debug i see different properties where the crash happens. Sometimes its a simple bool, sometimes its a FRotator, etc.
The full stack isn't here, but it essentially just keeps going up (all in engine code) to the windows loop.
So certainly something could be wrong with the UClass i have, but we have been using that class for months in other asset types. The difference here is the level of nesting that happens in this particular resource (this uclass type being a sub-property of a property and it having sub-properties of its own). Curious if there is some known limits i have to watch out for or something malformed about my class that maybe only broke now. Sorry this a vague one, i don't deal with slate or interal editor mechanics much.
The only other thing i can think of is that the UClass in quesiton has a bunch of UProp meta conditional edit/display logic bits on properties.
I cant figure out whats clipping the left side of this text block
note: the TextBlock that is cut off should say "Rank" but the R is cut off even thought it's owning HorizontalBox has plenty of room
...
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.HAlign(HAlign_Right)
.VAlign(VAlign_Center)
.FillWidth(1.f)
.Padding(24, 0)
[
SNew(STextBlock)
.Text(RankColumnDesc.DisplayText)
.TextStyle(&HeaderRowTextStyle)
.Justification(ETextJustify::Right)
]
]
...
Screenshot if it would help.
Looks like it's too wide enough to fit in the space?
whats weird tho is the horizontal box is definitely wide enough when looking at the widget reflector
oh...
will give it a shot 👍
curious if GetDesiredSize doesnt account for the padding... but Ill tinker around
padding was definitely the issue btw. ty for pointing me in the right direction
It won't. For the text.
The padding is decided by the wrapping box, not the text.
Just so you know, your problem was that the horizontal box decided that you only had, let's say, 50 pixels for the word. And it put it in the centre.
Your text saw it only had 50 pixels and was set to justify right, so it aligned the end of the word to the right.
But the word was liek 55 pixels long, so the left side got cut off.
any resources (o example classes) for how to implement an window like the reference viewer ?
The reference viewer is an asset?
basically spawning a new tab with some inputs, and spawn nodes (display name + tooltip + color + icons + ...) and their relations
didnt meant asset, more like a window tool
There's been some discussion recently about that in here.
Scroll up a bit!
...by you!
it wasnt for the same thing ?
Do you still need to spawn a tab? Or are you trying to populate it?
Same shit, man.
thing 1: BP tab (what i asked a few days before)
thing 2: content browser -> right click -> show my cool node window
the "special" part of the "thing 1" is to support the "checkmark" thingy (because if i used a classic button i would have to handle the spawn manually on each BP opened + handle the toggle)
If you want to add an option to the content browser right click, check the content browser module and the event you can subscribe to about selected assets.
yeah i know how to do that part
the 2 foggy things is the BP tab summoner thing and the slate widget or w/e to spawn a window like the reference viewer
i guess i'll never know 😆
I'm making my own icon creator that uses a custom viewport, scene capture, and render target. How can I use the render target to draw a preview texture that dynamically updates? I currently just have an empty SImage since I believe that's what I should use.
Create an FSlateImageBrush with the render target as the object.
Set that in the SImage.
Thanks, that worked. Now, would you know why the capture is so dark and not picking up the lights?
I have no idea what that is.
i would like to do a custom BP class picker, BP needs to implement an interface and a actor component
I thought about using a ipropertycustomization, and doing a custom slate class picker, but i wonder if there is any already made slate class picker that i can fill with an array (or apply some filters?)
Shrug
I don't think there's an easy way to specify whether a blueprint contains a certain component.
ill dig into the class picker slate class
do i have to forward keydown events from UMG to my slate widget? otherwise my keyboard inputs don't seem to work
it just feels a bit off, since i thought that input is handled rather in slate than in UMG 🤔
i just did what ChatGPT told me 😛
my SlateWidget is a SBox child
my UMG Widget is a UUserWidget and in RebuildWidget() it creates the Slate widget and returns it
what am i supposed to do?
If what you're doing is working, do that?
yea but it seems wrong (regarding the input event forwarding from UMG => Slate)
ok, got another "working" way
bool SSnekSlateWidget::SupportsKeyboardFocus() const
{
return true;
}
had to override that
and grab focus like this FSlateApplication::Get().SetKeyboardFocus(AsShared());
which also isn't ideal as it seems like i can't set focus from BP/UMG once it lost it
ok, works when forwarding the FocusReceived event... so there's 2 working solutions, and idk which one is to be favored
reparented my UMG widget to UWidget, and now it works without that override...
UUserWidget has some extra shit you don't need, eh? 🙂
apparently, haven't checked what exactly. Hoped someone here could give me a bump in the right direction 😄
sorry i was just crapping on the chatgpt remark 