mouse-active/inactive ui modes + focus handling

This commit is contained in:
thecozies 2024-03-03 18:01:33 -06:00
parent c370f9011e
commit 03af2919e9
7 changed files with 87 additions and 45 deletions

File diff suppressed because one or more lines are too long

View File

@ -5,7 +5,7 @@
color: $color-text-dim;
border-color: rgba($base-col, 0.8);
&:focus {
&:focus, &:hover {
color: $color-text;
border-color: $base-col;
background-color: rgba($base-col, 0.3);

View File

@ -115,7 +115,8 @@
}
}
&:focus + .config-option__tab-label {
&:focus + .config-option__tab-label,
&:hover + .config-option__tab-label {
color: $color-text;
}
}

View File

@ -37,12 +37,8 @@
}
}
&:hover {
&:focus, &:hover {
cursor: pointer;
opacity: 1;
}
&:focus {
color: $color-text;
opacity: 1;
}

View File

@ -12,10 +12,6 @@ body
box-sizing: border-box;
color: $color-text;
font-family: $font-stack;
&[disable-mouse] {
pointer-events: none;
}
}
.rmlui-window {
@ -26,6 +22,10 @@ body
opacity: 0;
transition: none;
}
&:not([mouse-active]) {
pointer-events: none;
}
}
*, *:before, *:after {

View File

@ -27,6 +27,7 @@
padding: space(12) space(4);
background-color: rgba(0, 0, 0, 0);
&:focus:not(:disabled, [disabled]),
&:focus-visible:not(:disabled, [disabled]),
&:hover:not(:disabled, [disabled]) {
@include set-color($color-text);
@ -116,11 +117,7 @@
background-color: $color-white-a5;
cursor: pointer;
&:hover:not(:focus) {
@include border($color-white-a80);
}
&:hover:focus, &:focus {
&:hover, &:focus {
@include border($color-white-a80);
background-color: $color-white-a20;
}

View File

@ -770,6 +770,8 @@ struct UIContext {
std::unordered_map<recomp::Menu, Rml::ElementDocument*> documents;
Rml::ElementDocument* current_document;
Rml::Element* prev_focused;
bool mouse_is_active = true;
bool mouse_is_active_changed = false;
public:
std::unique_ptr<SystemInterface_SDL> system_interface;
std::unique_ptr<RmlRenderInterface_RT64> render_interface;
@ -862,43 +864,73 @@ struct UIContext {
}
}
void update_primary_input(bool mouse_moved, bool non_mouse_interacted) {
if (current_document == nullptr) {
return;
}
mouse_is_active_changed = false;
if (non_mouse_interacted) {
// controller newly interacted with
if (mouse_is_active) {
mouse_is_active = false;
mouse_is_active_changed = true;
}
}
else if (mouse_moved) {
// mouse newly interacted with
if (!mouse_is_active) {
mouse_is_active = true;
mouse_is_active_changed = true;
}
}
// TODO: Figure out why this only works if the mouse is moving
SDL_ShowCursor(mouse_is_active ? SDL_ENABLE : SDL_DISABLE);
Rml::Element* window_el = current_document->GetElementById("window");
if (window_el != nullptr) {
if (mouse_is_active) {
if (!window_el->HasAttribute("mouse-active")) {
window_el->SetAttribute("mouse-active", true);
}
}
else if (window_el->HasAttribute("mouse-active")) {
window_el->RemoveAttribute("mouse-active");
}
}
}
void update_focus(bool mouse_moved) {
if (current_document == nullptr) {
return;
}
Rml::Element* focused = current_document->GetFocusLeafNode();
// If there was mouse motion, get the current hovered element (or its target if it points to one) and focus that if applicable.
if (mouse_moved) {
if (mouse_is_active) {
if (mouse_is_active_changed) {
Rml::Element* focused = current_document->GetFocusLeafNode();
if (focused) focused->Blur();
} else if (mouse_moved) {
Rml::Element* hovered = context->GetHoverElement();
if (hovered) {
Rml::Element* hover_target = get_target(current_document, hovered);
if (hover_target && can_focus(hover_target)) {
hover_target->Focus();
// hover_target->Focus();
prev_focused = hover_target;
}
}
}
}
// Revert focus to the previous element if focused on anything without a tab index.
// This should prevent the user from losing focus on something that has no navigation.
if (focused && !can_focus(focused)) {
// If the previously focused element is still accepting focus, return focus to it.
if (prev_focused && can_focus(prev_focused)) {
if (!mouse_is_active) {
if (!prev_focused) {
// get current focus and set to prev
prev_focused = current_document->GetFocusLeafNode();
}
if (mouse_is_active_changed && prev_focused && can_focus(prev_focused)) {
prev_focused->Focus();
}
// Otherwise, check if the currently focused element has a "nav-return" attribute and focus that attribute's value if so.
else {
Rml::Variant* nav_return = focused->GetAttribute("nav-return");
if (nav_return && nav_return->GetType() == Rml::Variant::STRING) {
Rml::Element* return_element = current_document->GetElementById(nav_return->Get<std::string>());
if (return_element) {
return_element->Focus();
}
}
}
}
else {
prev_focused = current_document->GetFocusLeafNode();
}
}
@ -1037,6 +1069,7 @@ void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderFramebuffer* s
SDL_Event cur_event{};
bool mouse_moved = false;
bool non_mouse_interacted = false;
while (recomp::try_deque_event(cur_event)) {
// Scale coordinates for mouse and window events based on the UI scale
@ -1066,14 +1099,28 @@ void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderFramebuffer* s
// Send events to RmlUi if a menu is open.
if (cur_menu != recomp::Menu::None) {
RmlSDL::InputEventHandler(ui_context->rml.context, cur_event);
// Implement some additional behavior for specific events on top of what RmlUi normally does with them.
switch (cur_event.type) {
case SDL_EventType::SDL_MOUSEMOTION:
mouse_moved = true;
break;
case SDL_EventType::SDL_CONTROLLERBUTTONDOWN:
case SDL_EventType::SDL_KEYDOWN:
non_mouse_interacted = true;
break;
case SDL_EventType::SDL_CONTROLLERAXISMOTION:
SDL_ControllerAxisEvent* axis_event = &cur_event.caxis;
float axis_value = axis_event->value * (1 / 32768.0f);
if (fabsf(axis_value) > 0.2f) {
non_mouse_interacted = true;
}
break;
}
RmlSDL::InputEventHandler(ui_context->rml.context, cur_event);
}
// If no menu is open and the game has been started and either the escape key or select button are pressed, open the config menu.
if (cur_menu == recomp::Menu::None && ultramodern::is_game_started()) {
bool open_config = false;
@ -1104,6 +1151,7 @@ void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderFramebuffer* s
recomp::finish_scanning_input(scanned_field);
}
ui_context->rml.update_primary_input(mouse_moved, non_mouse_interacted);
ui_context->rml.update_focus(mouse_moved);
if (cur_menu != recomp::Menu::None) {