WIP Guide to Slate / UMG + dealing with current issues.

This is a WIP guide about how to correctly implement UI using the Slate and UMG systems. I don’t desire to go into the 30.000 lines of c++ detail but I will write down some of the very common things I bumped into and documented. For some I created detailed topics on the forums before. Compact and to the point solutions for common problems. If you wish a full in depth technical guide, visit these links:

GitHub - YawLighthouse/UMG-Slate-Compendium: A Compendium Information Guide for UMG and Slate in Unreal Engine.

I expect you will be at least somewhat familiar with the editor panel’s properties and the blueprint side of widget implementation.

~~

3.2 Cursors

There are two types of cursor: Software and hardware.

Hardware:

  • Fast.
  • Can’t have multiple cursors.

  • Visuals and input behavior not very customizable.

Software:

  • Very customizable both visually and in input behavior.

  • Works very well in split screen if multiple players use their own cursor.

  • Slightly lags behind a hardware cursor (might matter if using a mouse device).

  • Default implementation has no player owner and is visually the same for all players.

That a software cursor is not assigned to an “owner” but is shared across all players makes a strange implementation. Additionally, event OnInitialized will not execute without a valid player context (owner). If you, like me, decide not to use the default implementation, you can make your own cursor by setting a UserWidget to a player cursor position, note that the cursor can end up behind a MenuAnchor widget, which is the only widget which defers its own rendering process until everything else is processed.

~~

3.3 Input routing / Focus

Normally you have input actions set up for certain keys. A UserWidget holds an input component which can be added and removed to a PlayerController’s input stack. At first it would seem logical to bind a method on the widget to an input action through this component. This is not the case because of how input is routed.

For example, these input components:

  1. Do not prioritize based on what widget is focused and in what order.

  2. Do only respect a manually set priority if it consumes the input.

  3. Do execute when you type in an editable text widget.

  4. End up on a controller.

  5. Do not execute when the playercontroller’s input mode is set to UI.

The input component on the UserWidget should be treated as garbage, with the one exception being when you want to bind an input to get into the UI and UI input mode from within the game. This is usually when you press a button to open a main menu, before letting the UI take over all the input through its own routing.

Processing routed input is done by overriding the UserWidget methods such as “OnKeyDown” and the other relevant methods, which are quite a few. If you implement any form of keybinding system, you will still have to compare a pressed key against actions bound to it.

The widget which is currently focused receives input. Through the mentioned routing methods you can respond to the input and finally respond if the input should be “handled” or “unhandled”. Handled means that nothing else should further process the input. Unhandled means that input can be processed by a parent or ancestor widget. This is a recursive process from parent to parent all the way up through the ancestors on the widget hierarchy. This is called “bubbling”.

There are various ways a widget can lose focus. It is common that when you click on the viewport or remove a focused widget that focus is lost. Problems arise when you are using an input device such as a gamepad or keyboard without a cursor and suddenly lose focus without a way to get it back to a desired widget. Through FSlateApplication you can respond to a change in focus and write your own implementation to attempt restore focus in such case.

For a widget to receive focus there are several ways as well. First off a widget must has set bIsFocusable to true. It is important to realize when this bool must be set to false. When you create a UserWidget with the intention to route all input directly to the UserWidget class, you should set bIsFocusable to true on the UserWidget and ensure that no UWidget on it has bIsFocusable set to true. Else, input is routed to the inner UWidget first. If for example, this inner UWidget happens to be a default button, the slate navigation keys (tab, arrows) and selection key (enter / spacebar) will be processed and handled by the button. In this situation you should set bIsFocusable to false on the inner button and treat it as only visual component of your UserWidget. Note that disabling bIsFocusable on a widget such as a button will still allow you to press the button directly with a cursor. This is very useful because it allows you to process any input on the focused UserWidget while adding additional functionality for cursor users. A button as an “OnPressed” delegate which you can use to bind cursor clicks.

You will encounter a lot of situations where navigating widget focus with the navigation keys just doesn’t work as you would expect out of the box, but some can be fixed with the correct settings:

  1. Visually diagonal navigation often fails.

  2. Attempting navigation over a distance often fails.

  3. Navigation within a scrollbox is inconsistent but improved by wrapping any direct entries into a vertical / horizontal box.

  4. If widgets are skipped over but focusable, their visibility is set to “Hit test invisible” instead of “Visible”

  5. Navigation does not respect the widget tree hierarchy, it can pop into a background / foreground widget by visual position if you do not set up the navigation rules for the parent panel. On a panel acting as a menu, you should usually set the navigation rules to “stop” in all directions on the root widget of that menu.

~~

3.4 Localization

The localization topic has been documented quite well on the official pages but people often run into the situation where a string is not localized and instead warns about a missing entry when you are sure the entry is present. Unreal by default only loads and packs assets which are “referenced” in a certain way by something else, to avoid including unused assets. Some blueprint nodes do not make this reference. Especially when you work from c++ and don’t make a direct reference to assets like a datatable, string table or other file you need to manually include these files in the project settings tabs (Asset Manager, Packaging) to be loaded and packed. Personally I got the localization working in a c++ after that by using the FText::FindText method after that.

~~

3.5 Widget bindings

In C++ UserWidgets can use the meta “BindWidget” or “BindWidgetOptional”:


UPROPERTY(meta = (BindWidget))
UImage* KeyImageWidget = nullptr;

When you create a UserWidget in Blueprints which inherits from the c++ UserWidget, a binding will be available as seen on the Bind Widgets panel:

1

To implement the binding, you add a widget to the UserWidget’s widget hierarchy of the same type and name as the c++ property. This widget will then be accessible from c++ through the binding. This also works for widget animations by using the “BindWidgetAnim” or “BindWidgetAnimOptional” meta.

You can make the access specifier of the c++ binding property private. It will be bound but not accessible / visible as a variable in Blueprints unless you implement a protected / public getter for it. You will still be able to modify properties on the editor panel for the widget, using the EditAnywhere specifier.

If you make the property protected / public it can show up as a variable after ticking this checkbox on the “MyBlueprint” tab on the UserWidget’s graph panel:

2

If it is still not visible as a Blueprint variable and not private in c++, tick the following checkbox on the “Details” tab on the designer panel after selecting the widget in the hierarchy:

3

~~

Other still present UI engine problems I discussed:

They will ruin your day (or week, or…) If you have no idea what is going on. Don’t expect patches anytime soon. There are a surprising lot of them so I hope they might reduce your suffering:

[UE5.2.1, Bug report] Right thumbstick reporting inverse axis value while not inversed.

[UE5.2.1, Bug report] EditableTextBox broke further. GetText not returning set value.

[UE5.2, Bug report] Good job, you broke SetValue on the slider this time

[UE5.1.1] UEditableTextBox::SetText reverts to previous text. Can't set text...

[UE5.1.1] Text property on textblock widget fails to unlink properly.

[Bug report] Losing focus / input axis values 0 after releasing mouse button.

Widgets destroyed after moving c++ class from project to plugin

[Bug Report] Adding UWidget causes widget tree corruption

Properties of widget slot not visible on editor?

Refreshing editor after property change?

[BUG ?] UMG animated brush material parameter has the wrong default value

[Bug] Widget animations only play 1 frame in the editor

[Bug report] Bad widget performance : Slate : ProcessMouseMove

Bug? Widget animation event trigger binding does not show all functions. Workaround included

CreateWidget Bug - exposed property pins NULL on OnInitialized, useless.

[UE5.1, Feedback, Feature Request] > UUserWidgetExtension needs way more access to UUserWidget!

[Bugs bugs bugs] Slate widget synchronization

[UE5.1, Bug Report] Widget's IsVisible is funny.

[UE5.1, Bug report] UUserWidget::NativeConstruct "ticks" when the asset is hovered.

[UE5.1, Bug Report] UUserWidgetExtension missing initalization.

Renaming a Named Slot widget breaks widget trees.

[UE5.1.1, Feedback] UI artists, where do you get notified of c++ widget bindings?

[UE5.1.1] YAY! #Finally rounded border widgets. OH it's broken. Surprise.

[UE5.1.1] Widget timer / animation supporting pause

[UE5.1.1, Bug report] FAnalogCursor::HandleKeyDownEvent assumes mouse button was used.

You don’t have to make a resolution setting widget if you are rendering with Vulkan, it’s not been implemented in the engine…

[UE5.1.1] Supported Vulkan RHI returns no supported screen resolutions.

[UE5.1.1, Bug report] UserWidget::SetPositionInViewport broken.

[UE5.1.1, Bug report] Widget does not set text unless asset is reloaded.

[Feature or bug?] Navigation rules visible on editor but can't be set for UUserWidget itself.

[UE5.1.1, Bug Report] FText::FindText returns true despite leaving text output empty.

[Bug report, UE5.1.1] blueprint nodes go missing.

Widget navigation (arrow keys) unreliable?

Widget navigation, arrow keys skip widgets?? [43KB project included]

Widget Navigation Does Not Work Diagonally

[UE5.1.1, Bug Report] Slate navigation keys not working after disabling focused button.

Widget input: It SMELLS

[UE5.1.1, Bug Report] Input Key Selector widget ignores modifier keys.

[UE5] Request for multi language support on default font.

[Request] Add soft drop shadows for UMG widgets so it doesn't look like Windows 95

[UE5.1] Sequencer curve editor not showing curves. Awful accessibility on widget animation creation.

[UE5.1] Widget render opacity broken when using border with rounded corners.

[UE5.1] Scrollbar widget image draw margin broken.

[UE5.1.1] UInputKeySelector::SetTextBlockVisibility broken. Does not sync / sync on rebuild.

[UE5.1.1] UInputKeySelector missing styling options for textblock.

[UE5] SizeBox widget Width override ignored.

4 Likes

Up

I’m skeptical EPIC takes a serious look at the older Slate / UMG code but know that anything new released (CommonUI) is just built on top of exactly that. Just a layer on top of the bugs. I’m collecting them here. It’s a request to fix the spaghetti code before putting more sauce on it so we can actually enjoy the widget system.

1 Like

:recycle:

I’m just going to bump this to ?maybe? get any staff to look at the pile of bugs it’s quite jaw dropping honestly. Tired of it being ignored. Hope the links to the “bug reports” do help someone out though.

Hi @SkyeEden is there any way to get the pile of bugs / issues i collected to the right Epic department / team quickly?

Thanks for nothing staff, could have at least posted a reply

I kind of understand it’s a summer time, but I agree that any bugs related to absolute basic UMG functionality should make top priority list for the engineering team. Fixing a spinbox for someone familiar with the engine code should be a matter of an hour max.But even worst case if it’s half day. I mean come on this is such an amazing software engineering product and we are talking about some shity spinbox not working correctly? Kind of funny talking about next gen stuff when the most simple building blocks are not working as expected.

2 Likes

Worst case it is half a day * 6000+ developers running into it who don’t get paid by EPIC to work on it. I gave it months, 0 reply. I wish I knew what is going on, perhaps it’s just going downhill and has been for a long while. A simple honest response in words and actions could absolutely make the difference, but where are they at? I have been running into all these bugs because I want to make the absolute best thing whenever I do something, otherwise it seems pointless to me. There is so much competition with low quality products (sadly EPIC included…) which seem completely pointless to me. I expect cooperation, especially from EPIC. Otherwise we are all wasting time on a massive scale…

Dumpster Fire Sticker - Dumpster Fire Its Stickers|237.85826771653544x256

:see_no_evil: :hear_no_evil: :speak_no_evil: :moneybag: :moneybag: :moneybag:

It might be just me? But I’d rather see an apoligy + 5 more 5.2.X sub versions on only bug fixes + long term support announcement than waking up to see an announcement of what action figures were put in Fortnite last night. Human greed and stupidity seems to have no bounds

1 Like