This tutorial shows how to handle UI navigation on a screen in a simple and flexible way, using MVVM (the Model View ViewModel pattern) to create a Selection View Model and Common Activatable Widgets to manage focus. The process requires zero coding, and will be focused on UMG and blueprint-based MVVM.
We will implement a focus-based navigation in a simple screen where elements like lists, tiles and buttons communicate with each other using the Selection View Model and with the help of Common Activatable Widgets.
Hi,
I finished the tutorial and when I press the button “Equip” or “Mark as Junk” without triggering the modal, the selected Item loses focus. Or when I click somewhere on the screen the item loses focus too. Is there a way to keep the focus on the item after pressing on the screen/button?
Hello! Great catch.
The whole keyboard-mouse input management in UE isn’t handled the same way it’s handled with gamepad unfortunately, which means:
When you switch between mouse and gamepad, GetDesiredFocusTarget() is called and focus is re-evaluated. This doesn’t happen when you switch between mouse and keyboard. That’s because UE assumes you might still be using mouse-based navigation alongside keyboard shortcuts.
This is annoying when we want to implement a focus-based navigation, because we have no way of automatically refreshing the focus when switching from mouse to keyboard.
The other annoying thing is that when you click on a non-focusable widget (like a background image or border, or even a button that is hit-testable but not focusable, like our Equip and Junk buttons), Unreal sets focus to the viewport instead of maintaining focus within the UI.
Then, when you switch to keyboard, no widget is focused, so navigation doesn’t work, but if you switch to gamepad, Unreal detects an input method change, so it will trigger a focus re-evaluation calling the various GetDesiredFocusTarget() functions.
I can tell you in advance that there is no elegant way of solving this that I know of (and I suppose it’s not a surprise that in many games keyboard navigation is poorly supported), but there are still a couple of workarounds we can implement that use our SelectionVM and GetDesiredFocusTarget().
So there are 2 issues we need to fix:
not lose focus when clicking on a background element, like a background image or the text in the item details panel
not lose focus when clicking on a non-focusable button (the Equip and Junk buttons)
keeping in mind that:
we want to avoid using events and delegates, keeping loose dependencies
we want to avoid calling SetFocus() and instead prefer RequestRefreshFocus() and GetDesiredFocusTarget()
So here’s what we’re going to do:
Add a utility function in the SelectionVM to request a focus refresh (this is so that any widget might request a focus refresh, and not only Common Activatable Widgets)
Add a new FieldNotify property in the SelectionVM so that the widgets that can handle focus can also listen to external refresh focus requests from other widgets
Introduce a FocusCatcher button to catch click events to the inventory background. This way we can catch the focus and redirect it before losing it
Have our non-focusable buttons use the new utility function in the SelectionVM to re-evaluate the focus when they’re clicked, instead of just allowing Slate to panic
So let’s go:
In the SelectionVM view model class, add the new function RequestRefreshFocus() and a new property bool FocusRefreshRequested
We now create a new function in WBP_Inventory to listen to the View Binding of our new property. We call this ForceFocusEvaluation() and it will take the new property as input. If the bool is true, means we have a focus refresh request, so we can now use a widget that is Common Activatable Widget to actually call the proper RequestRefreshFocus() function of activatable widgets.
But there’s a catch! One thing I haven’t mentioned in the tutorial (didn’t want to overload with information) is that RequestRefreshFocus() will trigger a focus re-evaluation ONLY if requested by the leaf-mostactive activatable widget. What does this mean? It means that in our case our activatable widgets are WBP_Inventory and WBP_Category, and the leaf-most active is WBP_Category (because it’s deeper in the hierarchy). So if we call RequestRefreshFocus() from WBP_Inventory, nothing will happen, because WBP_Inventory isn’t our leaf-most active widget. So instead we need to call it from WBP_Category.
So we can either add our ForceFocusEvaluation() function to WBP_Category, or we can just implement the function in a way that it will call RequestRefreshFocus() on WBP_Category instead of self - which is what I chose for the screenshot.
We can now create the view binding that binds the SelectionVM property to the ForceFocusEvaluation() function
So this part takes case of the case when you click somewhere on a screen. You’ll now see that when you click around instead of losing focus, the focus will be refresh to the currently selected item tile.
Next is solving the bug when clicking on the Equip and Junk buttons.
We open WBP_ItemDetails and simply call RequestRefreshFocus() of the SelectionVM on the OnCliked events of both buttons.
And this should fix those issues! As I said, not the most elegant solution, but should do the trick for the moment. At Epic we’re discussing focus issues and how to improve this on a regular basis, so I hope we’ll get to better keyboard support as soon as possible.
Let me know how this goes - I might add this as an appendix to the tutorial.
Hello! I’m glad this was helpful.
I’m considering making a new tutorial using the same project, but instead of showing how to use MVVM to handle navigation, I would show how to use MVVM to populate our widgets. So it would basically be creating view bindings, functions, conversion functions and so on.
Would that be helpful?
Thank you very much for this tutorial! It’s a very good resource!
It would be nice to delve deeper into the topic of resolvers. Specifically, how to use them to retrieve viewmodels in a multiplayer context where I need to display information about other players.
+1 on extra tutorial about how to fill the views with the data. (or how to set this up…I am checking the files with your tutorial, and have the feeling there is still quite some BP graphs to get everything working)
I have a question regarding how unral handles unused ViewModels. For example: When initializing them on a Widget creation each time it loads an inventory UI, The best way i have found so far is to create a viewmodel for ac item when activating the widget. Problem is creating a new viewmodel for each item everytime that ui is activated. I dont see any options to "destroy the viewmodel when you deactivate the widget, so in theory each viewmodel is still somewhere in memory. COuld you elaborate a bit more on how to reference a large amount of viewmodels instead of having to recreate them every time? Or does unreal garbage collect somewhere behind the scenes.
Am the the only one confused by this tutorial? Would have wanted to see how MVVM works in a simple practical example instead of hearing basic explanations of icons you see on the screen?
Personally could not find out what MVVM is used for after watching 3 videos. “We see an rpg fantasy inventory screen” yeah we see, please move on and show how to use MVVM.
Hello, excellent tutorial, this was very helpful for seeing a more practical usecase for setting up and sharing data with viewmodels.
I was wondering, what methods do y’all use for resolving VM methods? You mention an alternative to using global viewmodels (something about a technique someone figured out in Orlando?), but also that on the Fortnite UI team, resolvers are the primary method of setting up and accessing VMs.
I often seem to be getting errors or VMs in a bad state when relying on a global viewmodel collection, and would love to know more about alternatives or intended ways to handle this.
Thanks again, it’s really helpful to have more examples showing how powerful MVVM can be.