New Keybinds for Keyboard Navigation

Hey Developers!

We are excited to announce a series of new keybinds we are adding to the Roblox client to support keyboard navigation, out of the box, in all Experiences, with little to no additional effort required by developers :partying_face:!

keyboardnavdemo

The \ (Backslash) key will now toggle UI Selection

  • If you do not have an element selected, it selects an element in the PlayerGui.

  • If you have one selected, it will unselect it.

This is equivalent to the behavior of a controller’s Select button, and it also respects AutoSelectGuiEnabled. We’ve provided the logic for this in the following code block for reference.

local function EnableKeyboardNavigation(actionName, inputState, inputObject)
  if inputState ~= Enum.UserInputState.Begin then
    return Enum.ContextActionResult.Pass
  end
  -- Respect AutoSelectGuiEnabled

  if not GuiService.AutoSelectGuiEnabled then
    return Enum.ContextActionResult.Pass
  end

  -- There is a selected object, unselect it
  if GuiService.SelectedObject then
    GuiService.SelectedObject = nil
    return Enum.ContextActionResult.Sink
  end

  -- Select an element in the PlayerGui
  GuiService:Select(PlayerGui)
  return Enum.ContextActionResult.Sink
end

ContextActionService:BindAction("EnableKeyboardUINavigation", EnableKeyboardNavigation, false, Enum.KeyCode.BackSlash)

This addition is now shown in the Controls section of the In-Game Menu as UI Selection Toggle under Misc.

In addition, PageUp, PageDown, Home, and End will trigger scrolling if your SelectedObject is a ScrollingFrame, or a descendant of one.

  • PageUp / PageDown = Up / Down

    • Fn+ / Fn+ on keyboards without dedicated keys
  • Home / End = Left / Right

    • Fn+ / Fn+ on keyboards without dedicated keys

This is equivalent to a controller’s joystick behavior when focused on a scrolling frame. The code is available below for reference.

local function ScrollSelectedElement(actionName, inputState, inputObject)
  if inputState ~= Enum.UserInputState.Begin then
    return Enum.ContextActionResult.Pass
  end

  local selectedObject = GuiService.SelectedObject

  -- If no selected object, ignore
  if selectedObject == nil then
    return Enum.ContextActionResult.Pass
  end

  local scrollingFrame = nil
  if selectedObject.ClassName == "ScrollingFrame" then
    scrollingFrame = selectedObject
  else
    local scrollingFrameAncestor = selectedObject:FindFirstAncestorOfClass("ScrollingFrame")

    if scrollingFrameAncestor ~= nil then
      scrollingFrame = scrollingFrameAncestor
    end
  end

  -- If selected object is not a scrolling frame, or a descendant of one, ignore
  if scrollingFrame == nil then
    return Enum.ContextActionResult.Pass
  end

  local scrollDistance = 0
  if inputObject.KeyCode == Enum.KeyCode.PageUp or inputObject.KeyCode == Enum.KeyCode.Home then
    scrollDistance = -100
  elseif inputObject.KeyCode == Enum.KeyCode.PageDown or inputObject.KeyCode == Enum.KeyCode.End then
    scrollDistance = 100
  end

  local x = scrollingFrame.CanvasPosition.X
  local y = scrollingFrame.CanvasPosition.Y
  if inputObject.KeyCode == Enum.KeyCode.PageUp or inputObject.KeyCode == Enum.KeyCode.PageDown then
    -- Scroll vertically
    y = math.max(0, math.min(y + scrollDistance, scrollingFrame.AbsoluteCanvasSize.Y))
  elseif inputObject.KeyCode == Enum.KeyCode.Home or inputObject.KeyCode == Enum.KeyCode.End 
  then
    -- Scroll horizontally
    x = math.max(0, math.min(x + scrollDistance, scrollingFrame.AbsoluteCanvasSize.X))
  end

  scrollingFrame.CanvasPosition = Vector2.new(x, y)
  return Enum.ContextActionResult.Pass
end

ContextActionService:BindAction("ScrollSelectedElement", ScrollSelectedElement, false, Enum.KeyCode.PageUp, Enum.KeyCode.PageDown, Enum.KeyCode.Home, Enum.KeyCode.End)
More Information

You will note via the code blocks that the action for scrolling always passes input, while the toggle UI selection action sinks input when an event occurs (an element is selected or deselected). This is consistent with the implementation of other keybinds we have added (e.g. emotes menu), and allows you as the developer to override this functionality if you choose to by binding an action at a higher priority with BindActionAtPriority. Events bound at the C++ level (e.g. ProximityPrompts with any of these keys as the KeyboardKey) will be completely unaffected.

Now the good stuff :person_cartwheeling:

Players should now be able to navigate UI in experiences using only your keyboard. Once an element is selected, use the arrow keys ( ) or WASD to navigate between elements, and Enter to activate. If you find any gaps in your experience / things you can’t do, please file a bug here on the DevForum - we’ll take a look at it.

Developers - there is very little difference between supporting controllers, and supporting keyboard navigation.

  • For any shortcuts you’ve added for gamepad controllers, consider adding one for keyboard users.

    • E.g. If you allow users to close modals or menus by pressing B (recommended by the Xbox Featured Experience Guidelines), allowing keyboard users to do the same thing by pressing Backspace.
    • Don’t leave users guessing how to do things - show them how to perform an action either in the context of which that action is performed, or in an easily-accessible menu. For more information or best practices, visit the GetStringForKeyCode section on the UserInputService docs page.
  • Use keyboard navigation as an easy way to validate general controller navigation, through your own definitions of selection groups and the gamepad selection algorithm. Read more about this here: Gamepad UI Highlight Improvements

  • If you find your experience working well with keyboard navigation

    • Consider disabling Virtual Cursor either entirely, or conditionally.
      • Navigating UI with buttons is generally a more preferable experience to navigation with joysticks for users with and without disabilities - it requires less fine motor control.
    • Consider enabling for Xbox if you haven’t already, and you abide by other Xbox guidelines.
  • Consider improving other areas of your game for better keyboard support

If you’re currently using the Backslash key for any actions in-experience, please assess any user workflows that might be affected, consider using another key in those cases, or bind your action to a higher priority and allow users to navigate their UI with a keyboard only via other methods. We analyzed the usage of this key and it’s extremely low.There should be very little impact to experiences.

If you have your own implementation of entering / exiting keyboard navigation, you can disable the existing action by adding an action bound to the same key at a higher priority that sinks input, or set AutoSelectGuiEnabled to false. If you do either, consider adding Backslash as a key to whatever action you define for your own keyboard navigation, or ensuring that users are otherwise aware of how to navigate your UI with keyboard navigation.

Here’s an example of how you could customize the \ key to select a specific element on the screen, rather than what GuiService:Select() chooses automatically.

local function EnableKeyboardNavigation(actionName, inputState, inputObject)
  if inputState ~= Enum.UserInputState.Begin then
    return Enum.ContextActionResult.Pass
  end

  -- The selected object is already active, disable
  if GuiService.SelectedObject then
    GuiService.SelectedObject = nil
    return Enum.ContextActionResult.Sink
    -- Returning Pass instead of setting the SelectedObject to nil would also
    -- work, because the existing action would pass in this case as well.
  end

  GuiService.SelectedObject = cactusButton
  return Enum.ContextActionResult.Sink
end

ContextActionService:BindActionAtPriority("CustomEnableKeyboardNavigation", EnableKeyboardNavigation, false, 100, Enum.KeyCode.BackSlash)

Please leave any suggestions or feedback in the comments. We are working to provide better guidelines on controller keyboard navigation with documentation, stay tuned :notes:.

119 Likes

This topic was automatically opened after 10 minutes.

Will this be toggleable?

I currently use backslash as my push to talk key so it will be incredibly annoying while using roblox.
Additionally on British keyboards the backslash is quite close to other commonly used keys in the bottom left so it could be mis pressed often

image

24 Likes

As the engine evolves, we need a way to either override or change the binding of some Roblox context actions (obviously not all of them like the Esc button), but it will allow us to ergonomically design controls, even if it requires moving a few Roblox keybinds around.

This also means the Controls section of the Escape Menu can be dynamically updated if Roblox controls are moved by the developer.


ContextActionService:RebindRobloxCoreAction

23 Likes

As mentioned in the post, this can be disabled in a number of ways - by binding an action at a higher priority and sinking input, or setting AutoSelectGuiEnabled to False.

4 Likes

As in can the client disable it via roblox settings not the developer. (Apologies if I didn’t make myself clear)

4 Likes

Having a set of default and expected inputs for users is important for a consistent and good user experience across all of Roblox, so this is likely something we won’t allow developers to do and have it reflected in the player controls menu. You are welcome to override or update the behavior of certain actions like these (as shown in the code snippet, this isn’t a core action, its a plain ol’ action), and you can rebind or rewrite it as you please. But consider users experiences when doing so.

5 Likes

Ah gotcha. We don’t have any plans to allow users to disable any Roblox player controls.

1 Like

for the love of god please make this toggleable or make a popout similar to the one that you get when switching CameraToggle camera control mode or whatever its called

image

i have been switching keyboard navigation numerous times by now without wanting to do so while thinking that this was some sort of issue on my side

4 Likes

There is an issue regarding this UI element:
This UI element will be placed under any in experience UI if there is some.
Here is a video of it happening in my game:

I like the keyboard navigation. It will make things easier as I planned on creating a similar system for an upcoming game.
It also helps when testing UI navigation for console.

6 Likes

Great feedback! This is something we’ve been discussing with the gamepad team. We will consider ways to better distinguish what state a user is in.

7 Likes

How do I press backslash in games that expect me to press backslash?

4 Likes

Oh my god, the day I can finally use arrow keys in scrolling frames. Thank you for this update Roblox, it’ll also be easier to implement game pad support.

5 Likes

Although these changes offer improvements to the user experience, they unfortunately interfere with pre-existing keybinds throughout various Roblox experiences. For example, one of my experiences employs the well-known Kohl’s Admin command module. This module requires the backslash keybind to enable a textbox for inputting admin commands.

Unfortunately, as a result of this update, this feature no longer works and has been overwritten by the keybind’s new functionality. As @metatablecatmaid stated, “As the engine evolves, we need a way to either override or change the binding of some Roblox context actions.”

I believe this update serves as further proof of this necessary change. If developers cannot override Roblox context actions, users should, at the very least, be able to change their keybinds for different actions.

5 Likes

Not sure I agree with this key being the default keybind. Why isn’t it a function key, like F8? Many developers use the backslash key in their experiences already for things like opening custom debugging UI.

5 Likes

Is this functionality a thing for other forced inputs/keybinds? (Escape key and Xbox menu button for opening the Roblox menu, zooming in/out with ‘i’ and ‘o’, etc.) Where is the line drawn for what I can and can’t toggle regarding Roblox’s forced keybinds?

I think this is great, the feature coming to computer and having it be optional. I did want to ask those questions though. What’s on the table, what’s not? Each case would most likely have to be a feature request in of itself with different solutions worked on to problems I have faced regarding keybinds by Roblox, but I wanted to ask nonetheless.


What about the user experience across Roblox games themselves? What if I made a pause menu for example, using the industry standard keybind, the escape button. Now, I understand Roblox’s use-case as they house everything players need, but what if I was able to make my own menu with all that Roblox has and more?

If I could make a pause menu that has everything Roblox has, then there would be no need for Roblox’s menu. Leave button, graphical quality, reporting system, help screen with controls for my game, etc.

I can dream, but the reason why I can’t do this, I believe, is because Roblox doesn’t trust everyone enough to allow developers to use that kind of ‘power’, responsibility, and work.

2 Likes

Pressing a function key often means pressing multiple keys at once - which is inherently more challenging for individuals with motor impairments, for whom keyboard navigation is an important feature.

2 Likes

Ah, so that’s why it kept acting like I had a controller plugged in.

2 Likes

Such an exciting change!

BTW this link is broken, it’s ClickDetector | Roblox Creator Documentation (not plural) :smiley:

1 Like

fixed :slight_smile:

2 Likes