Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Keyboard / spatial navigation #524

Merged
merged 8 commits into from
Nov 19, 2023
2 changes: 1 addition & 1 deletion Include/RmlUi/Core/Context.h
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ class RMLUICORE_API Context : public ScriptInterface {
// Internal callback for when an element is detached or removed from the hierarchy.
void OnElementDetach(Element* element);
// Internal callback for when a new element gains focus.
bool OnFocusChange(Element* element);
bool OnFocusChange(Element* element, bool focus_visible);

// Generates an event for faking clicks on an element.
void GenerateClickEvent(Element* element);
Expand Down
3 changes: 2 additions & 1 deletion Include/RmlUi/Core/Element.h
Original file line number Diff line number Diff line change
Expand Up @@ -468,8 +468,9 @@ class RMLUICORE_API Element : public ScriptInterface, public EnableObserverPtr<E
//@{

/// Gives focus to the current element.
/// @param[in] focus_visible True to indicate that the focus should be visually indicated by setting the ':focus-visible' pseudo class.
/// @return True if the change focus request was successful
bool Focus();
bool Focus(bool focus_visible = false);
/// Removes focus from from this element.
void Blur();
/// Fakes a mouse click on this element.
Expand Down
3 changes: 3 additions & 0 deletions Include/RmlUi/Core/ElementDocument.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class DocumentHeader;
class ElementText;
class StyleSheet;
class StyleSheetContainer;
enum class NavigationSearchDirection;

/**
ModalFlag used for controlling the modal state of the document.
Expand Down Expand Up @@ -153,6 +154,8 @@ class RMLUICORE_API ElementDocument : public Element {
Element* FindNextTabElement(Element* current_element, bool forward);
/// Searches forwards or backwards for a focusable element in the given substree
Element* SearchFocusSubtree(Element* element, bool forward);
/// Find the next element to navigate to, starting at the current element.
Element* FindNextNavigationElement(Element* current_element, NavigationSearchDirection direction, const Property& property);

/// Sets the dirty flag on the layout so the document will format its children before the next render.
void DirtyLayout() override;
Expand Down
6 changes: 6 additions & 0 deletions Include/RmlUi/Core/ID.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ enum class ShorthandId : uint8_t {
TransformOrigin,
Flex,
FlexFlow,
Nav,

NumDefinedIds,
FirstCustomId = NumDefinedIds,
Expand Down Expand Up @@ -169,6 +170,11 @@ enum class PropertyId : uint8_t {
FlexWrap,
JustifyContent,

NavUp,
NavRight,
NavDown,
NavLeft,

NumDefinedIds,
FirstCustomId = NumDefinedIds,

Expand Down
2 changes: 2 additions & 0 deletions Include/RmlUi/Core/StyleTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ namespace Style {
enum class FlexWrap : uint8_t { Nowrap, Wrap, WrapReverse };
enum class JustifyContent : uint8_t { FlexStart, FlexEnd, Center, SpaceBetween, SpaceAround };

enum class Nav : uint8_t { None, Auto, Horizontal, Vertical };

class ComputedValues;

} // namespace Style
Expand Down
51 changes: 38 additions & 13 deletions Samples/assets/invader.rcss
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,12 @@

text-l: 162px 192px 14px 31px;
text-c: 176px 192px 1px 31px;
text-focus-l: 162px 230px 14px 31px;
text-focus-c: 176px 230px 1px 31px;
textarea: 162px 193px 145px 31px;
textarea-inner: 173px 206px 127px 10px;
textarea-focus: 162px 231px 145px 31px;
textarea-focus-inner: 173px 244px 127px 10px;

selectbox-tl: 281px 275px 11px 9px;
selectbox-t: 292px 275px 1px 9px;
Expand Down Expand Up @@ -113,8 +117,11 @@
sliderarrowinc-hover: 28px 177px 27px 24px;
sliderarrowinc-active: 28px 202px 27px 24px;

range-track: 219px 194px 3px 32px;
range-track-inner: 220px 204px 1px 14px;
range-track: 219px 194px 3px 32px;
range-track-inner: 220px 204px 1px 14px;
range-track-focus: 219px 232px 3px 32px;
range-track-focus-inner: 220px 242px 1px 14px;

range-bar: 127px 191px 34px 32px;
range-dec: 3px 232px 17px 17px;
range-dec-hover: 21px 232px 17px 17px;
Expand All @@ -140,6 +147,7 @@ body
font-style: normal;
font-size: 15dp;
color: white;
nav: auto;
}

body.window
Expand Down Expand Up @@ -252,7 +260,9 @@ input.submit
margin-left: 0;
}


input, button, select {
nav: auto;
}

button,
input.submit
Expand All @@ -276,8 +286,8 @@ input.submit:focus
font-effect: blur(3dp #fff);
}

button:hover,
input.submit:hover
button:hover, button:focus-visible,
input.submit:hover, input.submit:focus-visible
{
decorator: image(button-hover);
}
Expand All @@ -304,6 +314,10 @@ input.text, input.password
cursor: text;
text-align: left;
}
input.text:focus-visible, input.password:focus-visible
{
decorator: tiled-horizontal( text-focus-l, text-focus-c, auto );
}

textarea
{
Expand All @@ -312,6 +326,10 @@ textarea
cursor: text;
text-align: left;
}
textarea:focus-visible
{
decorator: ninepatch( textarea-focus, textarea-focus-inner, 1.0 );
}

input.text,
input.password,
Expand All @@ -336,12 +354,13 @@ table input.text
background-color: white;

font-size: 15dp;

}
table input.text, table input.text:focus-visible
{
decorator: none;
}



select
{
width: 175dp;
Expand All @@ -356,7 +375,10 @@ select selectvalue
height: 25dp;
padding: 12dp 10dp 0dp 10dp;

decorator: image( selectvalue );
decorator: image( selectvalue );
}
select:focus-visible selectvalue {
decorator: image( selectvalue-hover );
}

select selectarrow
Expand All @@ -367,7 +389,7 @@ select selectarrow
decorator: image( selectarrow );
}

select:hover selectarrow
select:hover selectarrow, select:focus-visible selectarrow
{
decorator: image( selectarrow-hover );
}
Expand Down Expand Up @@ -434,7 +456,7 @@ input.radio
decorator: image(radio);
}

input.radio:hover
input.radio:hover, input.radio:focus-visible
{
decorator: image(radio-hover);
}
Expand All @@ -449,7 +471,7 @@ input.radio:checked
decorator: image(radio-checked);
}

input.radio:checked:hover
input.radio:checked:hover, input.radio:checked:focus-visible
{
decorator: image(radio-checked-hover);
}
Expand All @@ -464,7 +486,7 @@ input.checkbox
decorator: image(checkbox);
}

input.checkbox:hover
input.checkbox:hover, input.checkbox:focus-visible
{
decorator: image(checkbox-hover);
}
Expand All @@ -479,7 +501,7 @@ input.checkbox:checked
decorator: image(checkbox-checked);
}

input.checkbox:checked:hover
input.checkbox:checked:hover, input.checkbox:checked:focus-visible
{
decorator: image(checkbox-checked-hover);
}
Expand All @@ -500,6 +522,9 @@ input.range slidertrack {
image-color: #ecc;
decorator: ninepatch( range-track, range-track-inner, 1.0 );
}
input.range:focus-visible slidertrack {
decorator: ninepatch( range-track-focus, range-track-focus-inner, 1.0 );
}
input.range sliderbar {
margin-left: -8dp;
margin-right: -7dp;
Expand Down
16 changes: 12 additions & 4 deletions Samples/basic/demo/data/demo.rml
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,14 @@ progress {
{
height: 12dp;
}
form input, form textarea, form select
{
nav: vertical;
}
form .nav-auto, form input.checkbox, form input.radio
{
nav: auto;
}
</style>
</head>

Expand Down Expand Up @@ -836,21 +844,21 @@ progress {
</div>
<h2>Email and password</h2>
<div>
<input type="text" name="email"/>
<input type="password" name="password"/>
<input type="text" name="email" class="nav-auto"/>
<input type="password" name="password" class="nav-auto"/>
</div>
<h2>Favorite animal</h2>
<div>
<label><input type="radio" name="animal" value="dog" checked/> Dog </label>
<label><input type="radio" name="animal" value="cat"/> Cat </label>
<label><input type="radio" name="animal" value="narwhal"/> Narwhal </label>
<label><input type="radio" name="animal" value="no"/> I don't like animals </label>
<label><input type="radio" name="animal" value="no" style="nav-down: #lasagne"/> I don't like animals </label>
</div>
<h2>Favorite meals</h2>
<div>
<label><input type="checkbox" name="meals" value="pizza" checked/> Pizza </label>
<label><input type="checkbox" name="meals" value="pasta" checked/> Pasta </label>
<label><input type="checkbox" name="meals" value="lasagne" checked/> Lasagne </label>
<label><input type="checkbox" name="meals" value="lasagne" id="lasagne" checked/> Lasagne </label>
</div>
<h2>Rating</h2>
<div>
Expand Down
2 changes: 1 addition & 1 deletion Samples/invaders/data/game.rml
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@
}
</style>
</head>
<body id="game_window">
<body id="game_window" onkeyup="onescape load pause">
<game id="game">
<div id="score_div">
<icon />
Expand Down
2 changes: 1 addition & 1 deletion Samples/invaders/data/help.rml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
}
</style>
</head>
<body template="window">
<body template="window" onkeydown="onescape goto main_menu">
<h1>Story</h1>
<p>
One day, without warning, they came for us; endless waves of the invaders, numbers too vast to count, fresh from the Martian foundries. Earth's orbital defences took a heavy toll, but were inevitably overrun, buying enough time only for a single rmlui ship to launch. The prototype X-42 'Defender'-class, not yet tested but piloted by the finest astronaut the Space Corps had to offer.
Expand Down
2 changes: 1 addition & 1 deletion Samples/invaders/data/high_score.rml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
}
</style>
</head>
<body template="window" onload="add_score">
<body template="window" onload="add_score" onkeydown="onescape goto main_menu">
<table data-model="high_scores">
<thead>
<tr>
Expand Down
2 changes: 1 addition & 1 deletion Samples/invaders/data/options.rml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
}
</style>
</head>
<body template="window" onload="restore">
<body template="window" onload="restore" onkeydown="onescape goto main_menu">
<form onsubmit="store; goto main_menu" onchange="enable_accept">
<div>
<p>
Expand Down
2 changes: 1 addition & 1 deletion Samples/invaders/data/pause.rml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
}
</style>
</head>
<body template="window" onload="pause" onunload="unpause">
<body template="window" onload="pause" onunload="unpause" onkeyup="onescape close">
<br />
<p>Are you sure you want to end this game?</p>
<button onclick="goto high_score; close game_window" autofocus>Yes</button>
Expand Down
2 changes: 1 addition & 1 deletion Samples/invaders/data/start_game.rml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
}
</style>
</head>
<body template="window">
<body template="window" onkeydown="onescape goto main_menu">
<form onsubmit="start">
<div>
<p>
Expand Down
5 changes: 0 additions & 5 deletions Samples/invaders/src/ElementGame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,6 @@ void ElementGame::ProcessEvent(Rml::Event& event)
bool key_down = (event == Rml::EventId::Keydown);
Rml::Input::KeyIdentifier key_identifier = (Rml::Input::KeyIdentifier)event.GetParameter<int>("key_identifier", 0);

if (key_identifier == Rml::Input::KI_ESCAPE && !key_down)
{
EventManager::LoadWindow("pause");
}

// Process left and right keys
if (key_down)
{
Expand Down
11 changes: 8 additions & 3 deletions Samples/invaders/src/EventManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,22 +67,27 @@ void EventManager::ProcessEvent(Rml::Event& event, const Rml::String& value)
Rml::StringUtilities::ExpandString(commands, value, ';');
for (size_t i = 0; i < commands.size(); ++i)
{
// Check for a generic 'load' or 'exit' command.
// Check for custom commands.
Rml::StringList values;
Rml::StringUtilities::ExpandString(values, commands[i], ' ');

if (values.empty())
return;

if (values[0] == "onescape" && values.size() > 1)
{
Rml::Input::KeyIdentifier key_identifier = (Rml::Input::KeyIdentifier)event.GetParameter<int>("key_identifier", 0);
if (key_identifier == Rml::Input::KI_ESCAPE)
values.erase(values.begin());
}

if (values[0] == "goto" && values.size() > 1)
{
// Load the window, and if successful close the old window.
if (LoadWindow(values[1]))
event.GetTargetElement()->GetOwnerDocument()->Close();
}
else if (values[0] == "load" && values.size() > 1)
{
// Load the window.
LoadWindow(values[1]);
}
else if (values[0] == "close")
Expand Down
11 changes: 1 addition & 10 deletions Samples/luainvaders/data/game.rml
Original file line number Diff line number Diff line change
Expand Up @@ -87,17 +87,8 @@
decorator: image( icon-lives );
}
</style>
<script>
Game = Game or {} --namespace for functions

function Game.OnKeyDown(event, document)
if event.parameters['key_identifier'] == rmlui.key_identifier.ESCAPE then
document.context:LoadDocument('luainvaders/data/pause.rml'):Show()
end
end
</script>
</head>
<body id="game_window" onkeydown="Game.OnKeyDown(event, document)" ongameover="Window.LoadMenu('high_score',document)">
<body id="game_window" ongameover="Window.LoadMenu('high_score',document)" onkeydown="if Window.EscapePressed(event) then Window.OpenDocument('pause',document) end">
<game id="game">
<div id="score_div">
<icon />
Expand Down
2 changes: 1 addition & 1 deletion Samples/luainvaders/data/help.rml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
}
</style>
</head>
<body template="luawindow">
<body template="luawindow" onkeydown="if Window.EscapePressed(event) then Window.LoadMenu('main_menu',document) end">
<h1>Story</h1>
<p>
One day, without warning, they came for us; endless waves of the invaders, numbers too vast to count, fresh from the Martian foundries. Earth's orbital defences took a heavy toll, but were inevitably overrun, buying enough time only for a single rmlui ship to launch. The prototype X-42 'Defender'-class, not yet tested but piloted by the finest astronaut the Space Corps had to offer.
Expand Down
Loading