Unexpected UMG Navigation within a ScrollBox

Hi,

We have a UMG scene that needs some really ugly hacks to work around navigation issues in a scenario that I would just expect to work out of the box.

After digging deeper into the navigation code, it just seems wrong to me.

The issue is that when navigating (using directional inputs) within a scroll box that has some focusable elements within a container (e.g. a vertical box) and some outside of it, focus will jump around depending on the navigation direction and the other content in the widget/scroll box.

The reason seems to be that the boundary widget (scroll box in this case) gets first refusal on the navigation behaviour, regardless of whether the currently focused widget is near the boundary or not.

I’ve reproed this in a blank vanilla project in UE 5.6.1, with controller and with keyboard.

Also with and without using CommonUI for setting the default focus widget.

Attached is a zip with a video of the navigation behaviour (navigating to a widget in the vertical box and then navigating up), and a screenshot of the widget hierarchy.

Code of interest:

  • The call to SWidget::OnNavigation() in FSlateApplication::ProcessReply() is called on the boundary widget of the currently focused element, which is the SScrollBox in this case.
    • This likely returns an FNavigationReply::Explicit that contains one of its direct children that it thinks it should focus.
  • This would be fine, but the subsequent call to AttemptNavigation then prioritises any Explicit boundary NavigationReplys and never gets to the call for NavigationSource.GetDeepestWindow()->GetHittestGrid().FindNextFocusableWidget(), which surely always needs to be queried when navigating in a direction?

Thoughts?

Thanks,

Dave

Steps to Reproduce
Create a new UserWidget and set your level up to display it immediately in the level BP.

In the UserWidget set up the child elements in this structure:

  • CanvasPanel
    • ScrollBox
      • Button
      • Button
      • Button
      • VerticalBox
        • Button
        • Button
        • Button

You can tweak the layout of the scene to make it clearer (adding padding, fill alignment), but don’t modify any of the default navigation properties.

Now run the level in PIE and scroll down the list using cursor keys or gamepad input (you may need to set the initial focus with the mouse).

Selection should work as expected when going down, however when you try to move up after selecting any of the buttons in the VerticalBox you will find that the navigation will jump out of the vertical box to the other widgets directly in the ScrollBox.

I have a simillar issue where I have this hierarchy

  • VerticalBox

    • CommonButton
    • Overlay
      • CommonButton
      • VerticalBox
        • CommonButton
  • CommonButton

  • CommonButton

  • ScrollBox

    • Overlay
      • CommonButton
      • VerticalBox
        • CommonButton
  • CommonButton

  • CommonButton

  • Overlay
    • CommonButton
    • VerticalBox
      • CommonButton
  • CommonButton

  • CommonButton

  • Overlay
    • CommonButton
    • VerticalBox
      • CommonButton
  • CommonButton

  • CommonButton

The navigation inside the ScrollBox is all messed up and when I remove the ScrollBox, everything works as expected.

I’d be curious about your hack to make it work.

That won’t work for me unfortunately… Each

  • Overlay
    • CommonButton
    • VerticalBox
      • CommonButton
      • CommonButton
      • CommonButton

is a widget itself. But I can figure something out. Thank you for your help!

Hi,

This comes up from time to time, it’s a tricky problem since scroll boxes need to be able to navigate to widgets which may be entirely culled by the scrollbox so we can’t rely on the standard hittest grid-based navigation. There’s a bit more background and some potential workarounds [Content removed] though the conclusion isn’t too different from your current workaround.

You should be able to alleviate the issue of having to do bespoke processing on each of your vertical boxes (or other containers) by having the container explicitly manage navigation within itself and then fall back to the scroll box if you hit the boundary. The main way to do this would be to write your own container class and override OnNavigation. Your class could then do some management, tracking which widget is focused and returning the proper navigation target. If you detect the navigation is hitting the boundary (i.e. navigate up while currently focused child is index 0), you return EUINavigationRule::Escape and the scroll box gets it’s turn to handle things. Or, if you don’t want to do the focus state management, you could just have your implementation of OnNavigation return the results for EUINavigation::Next or EUINavigation::Previous to trick the system into using the hit test grid anyway (and maybe still try to track if you’re about to leave the box so you can fall back to Escape and let the scroll box do it’s scrolling).

Best,

Cody

The gross workaround that we have is to set the Navigation direction property (e.g. Up/Down, etc) on the container widget within the scroll box to ‘Custom Boundary’ and then set that to a BP function that returns the next widget to focus in that direction (when leaving the container).

‘Custom Boundary’ must be used (‘Custom’ does not work), but needing to define a function for each of these cases become really painful as the content of the scroll box gets more complex.

Your case is much more complex than my repro example above, but in my example you would set this navigation behaviour on the VerticalBox within the scroll box, and assign it to a function that returns the 3rd button.

Hope this helps!

Thanks, Cody.

I can see this is a complex problem due to the nature of the scrollbox.

Sorry, I didn’t find the duplicate thread!

Thanks for the help.