fix and improve kb/cont config tab > panel navigation
prompt component - wip
This commit is contained in:
parent
2b96bb8be9
commit
95ab0074ea
|
@ -143,6 +143,7 @@ set (SOURCES
|
||||||
${CMAKE_SOURCE_DIR}/src/ui/ui_launcher.cpp
|
${CMAKE_SOURCE_DIR}/src/ui/ui_launcher.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/ui/ui_config.cpp
|
${CMAKE_SOURCE_DIR}/src/ui/ui_config.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/ui/ui_color_hack.cpp
|
${CMAKE_SOURCE_DIR}/src/ui/ui_color_hack.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/src/ui/ui_rml_hacks.cpp
|
||||||
|
|
||||||
${CMAKE_SOURCE_DIR}/src/ui/FontEngineScaled/FontEngineInterfaceScaled.cpp
|
${CMAKE_SOURCE_DIR}/src/ui/FontEngineScaled/FontEngineInterfaceScaled.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/ui/FontEngineScaled/FontFace.cpp
|
${CMAKE_SOURCE_DIR}/src/ui/FontEngineScaled/FontFace.cpp
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
<template name="prompt">
|
||||||
|
<head>
|
||||||
|
</head>
|
||||||
|
<body class="prompt">
|
||||||
|
<div class="prompt__overlay" />
|
||||||
|
<div class="prompt__content-wrapper" data-if="promptOpen">
|
||||||
|
<div class="prompt__content">
|
||||||
|
<h3>{{ promptHeader }}</h3>
|
||||||
|
<p>{{ promptContent }}</p>
|
||||||
|
<div class="prompt__controls">
|
||||||
|
<button
|
||||||
|
autofocus="true"
|
||||||
|
id="prompt-button-left"
|
||||||
|
class="button button--success"
|
||||||
|
style="nav-left: none; nav-right: #prompt-button-right"
|
||||||
|
>
|
||||||
|
<div class="button__label">{{ promptConfirmLabel }}</div>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
id="prompt-button-right"
|
||||||
|
class="button button--error"
|
||||||
|
style="nav-right: none; nav-left: #prompt-button-left"
|
||||||
|
>
|
||||||
|
<div class="button__label">{{ promptCancelLabel }}</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</template>
|
|
@ -28,6 +28,7 @@
|
||||||
<link type="text/template" href="config_menu/graphics.rml" />
|
<link type="text/template" href="config_menu/graphics.rml" />
|
||||||
<link type="text/template" href="config_menu/sound.rml" />
|
<link type="text/template" href="config_menu/sound.rml" />
|
||||||
<link type="text/template" href="config_menu/debug.rml" />
|
<link type="text/template" href="config_menu/debug.rml" />
|
||||||
|
<link type="text/template" href="components/prompt.rml" />
|
||||||
</head>
|
</head>
|
||||||
<body class="window">
|
<body class="window">
|
||||||
<!-- <handle move_target="#document"> -->
|
<!-- <handle move_target="#document"> -->
|
||||||
|
@ -42,7 +43,7 @@
|
||||||
<panel class="config" data-model="general_model">
|
<panel class="config" data-model="general_model">
|
||||||
<template src="config-menu__general" />
|
<template src="config-menu__general" />
|
||||||
</panel>
|
</panel>
|
||||||
<tab class="tab">
|
<tab class="tab" id="tab_controls">
|
||||||
<div>Controls</div>
|
<div>Controls</div>
|
||||||
<div class="tab__indicator"></div>
|
<div class="tab__indicator"></div>
|
||||||
</tab>
|
</tab>
|
||||||
|
@ -63,7 +64,7 @@
|
||||||
<panel class="config" data-model="sound_options_model">
|
<panel class="config" data-model="sound_options_model">
|
||||||
<template src="config-menu__sound" />
|
<template src="config-menu__sound" />
|
||||||
</panel>
|
</panel>
|
||||||
<tab class="tab" data-model="debug_model" data-if="debug_enabled">
|
<tab class="tab" data-model="debug_model" data-if="debug_enabled" id="tab_debug">
|
||||||
<div>Debug</div>
|
<div>Debug</div>
|
||||||
<div class="tab__indicator"></div>
|
<div class="tab__indicator"></div>
|
||||||
</tab>
|
</tab>
|
||||||
|
@ -100,6 +101,15 @@
|
||||||
<label><span style="font-family:promptfont;">↧</span> Accept</label> -->
|
<label><span style="font-family:promptfont;">↧</span> Accept</label> -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
data-alias-promptOpen="1"
|
||||||
|
data-alias-promptHeader="'test header'"
|
||||||
|
data-alias-promptContent="'This allows templates to be used as reusable components within data models. By wrapping the inline template in an element that defines variable name aliases, the template can refer to any outside variable by a fixed name.'"
|
||||||
|
data-alias-promptConfirmLabel="'Confirm'"
|
||||||
|
data-alias-promptCancelLabel="'Cancel'"
|
||||||
|
>
|
||||||
|
<template src="prompt"/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- </handle> -->
|
<!-- </handle> -->
|
||||||
<!-- <handle size_target="#document" style="position: absolute; width: 16dp; height: 16dp; bottom: 0px; right: 0px; cursor: resize;"></handle> -->
|
<!-- <handle size_target="#document" style="position: absolute; width: 16dp; height: 16dp; bottom: 0px; right: 0px; cursor: resize;"></handle> -->
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
id="cont_kb_toggle"
|
id="cont_kb_toggle"
|
||||||
data-class-toggle--checked="input_device_is_keyboard"
|
data-class-toggle--checked="input_device_is_keyboard"
|
||||||
onclick="toggle_input_device"
|
onclick="toggle_input_device"
|
||||||
style="nav-down:#input_row_button_0_0"
|
style="nav-down: #input_row_button_0_0; nav-up: #tab_controls"
|
||||||
>
|
>
|
||||||
<div class="toggle__border" />
|
<div class="toggle__border" />
|
||||||
<div class="toggle__floater" />
|
<div class="toggle__floater" />
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
<div class="config-debug__option-controls">
|
<div class="config-debug__option-controls">
|
||||||
<div class="config-debug__select-wrapper">
|
<div class="config-debug__select-wrapper">
|
||||||
<div class="config-debug__select-label"><div>Region</div></div>
|
<div class="config-debug__select-label"><div>Region</div></div>
|
||||||
<select data-value="area_index" onchange="area_index_changed">
|
<select data-value="area_index" onchange="area_index_changed" id="area_index_select" style="nav-up: #tab_debug">
|
||||||
<option data-for="area, i : area_names" data-attr-value="i">{{area}}</option>
|
<option data-for="area, i : area_names" data-attr-value="i">{{area}}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,57 @@
|
||||||
|
|
||||||
|
.prompt {
|
||||||
|
&__overlay {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__overlay,
|
||||||
|
&__content-wrapper {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__content-wrapper {
|
||||||
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
position: relative;
|
||||||
|
margin: auto;
|
||||||
|
flex: 1 1 100%;
|
||||||
|
max-width: space(820);
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
background: $color-modal-overlay;
|
||||||
|
border-radius: $border-radius-modal;
|
||||||
|
border-color: $color-border;
|
||||||
|
border-width: $border-width-thickness;
|
||||||
|
|
||||||
|
h3, p {
|
||||||
|
margin: space(16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__controls {
|
||||||
|
display: flex;
|
||||||
|
padding: space(16);
|
||||||
|
border-top-color: $color-border-soft;
|
||||||
|
border-top-width: $border-width-thickness;
|
||||||
|
|
||||||
|
.button {
|
||||||
|
nav-up: none;
|
||||||
|
nav-down: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,4 +9,5 @@
|
||||||
@import "./MenuListItem";
|
@import "./MenuListItem";
|
||||||
@import "./SubtitleTitle";
|
@import "./SubtitleTitle";
|
||||||
@import "./Toggle";
|
@import "./Toggle";
|
||||||
@import "./BottomLeft"
|
@import "./BottomLeft";
|
||||||
|
@import "./Prompt";
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "recomp_ui.h"
|
#include "recomp_ui.h"
|
||||||
#include "recomp_input.h"
|
#include "recomp_input.h"
|
||||||
#include "recomp_game.h"
|
#include "recomp_game.h"
|
||||||
|
#include "ui_rml_hacks.hpp"
|
||||||
|
|
||||||
#include "concurrentqueue.h"
|
#include "concurrentqueue.h"
|
||||||
|
|
||||||
|
@ -956,6 +957,39 @@ struct UIContext {
|
||||||
void add_menu(recomp::Menu menu, std::unique_ptr<recomp::MenuController>&& controller) {
|
void add_menu(recomp::Menu menu, std::unique_ptr<recomp::MenuController>&& controller) {
|
||||||
menus.emplace(menu, std::move(controller));
|
menus.emplace(menu, std::move(controller));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void update_config_menu_loop(bool menu_changed) {
|
||||||
|
static int prevTab = -1;
|
||||||
|
if (menu_changed) prevTab = -1;
|
||||||
|
|
||||||
|
Rml::ElementTabSet *tabset = (Rml::ElementTabSet *)current_document->GetElementById("config_tabset");
|
||||||
|
if (tabset == nullptr) return;
|
||||||
|
|
||||||
|
int curTab = tabset->GetActiveTab();
|
||||||
|
if (curTab == prevTab) return;
|
||||||
|
prevTab = curTab;
|
||||||
|
|
||||||
|
Rml::ElementList panels;
|
||||||
|
current_document->GetElementsByTagName(panels, "panel");
|
||||||
|
|
||||||
|
Rml::Element *firstFocus = nullptr;
|
||||||
|
for (const auto& panel : panels) {
|
||||||
|
if (panel->IsVisible()) {
|
||||||
|
firstFocus = RecompRml::FindNextTabElement(panel, true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!firstFocus) return;
|
||||||
|
Rml::String id = firstFocus->GetId();
|
||||||
|
if (id.empty()) return;
|
||||||
|
|
||||||
|
Rml::ElementList tabs;
|
||||||
|
current_document->GetElementsByTagName(tabs, "tab");
|
||||||
|
for (const auto& tab : tabs) {
|
||||||
|
tab->SetProperty("nav-down", "#" + id);
|
||||||
|
}
|
||||||
|
}
|
||||||
} rml;
|
} rml;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1079,7 +1113,6 @@ int cont_axis_to_key(SDL_ControllerAxisEvent& axis, float value) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderFramebuffer* swap_chain_framebuffer) {
|
void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderFramebuffer* swap_chain_framebuffer) {
|
||||||
std::lock_guard lock {ui_context_mutex};
|
std::lock_guard lock {ui_context_mutex};
|
||||||
|
|
||||||
|
@ -1104,7 +1137,8 @@ void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderFramebuffer* s
|
||||||
prev_menu = recomp::Menu::None;
|
prev_menu = recomp::Menu::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cur_menu != prev_menu) {
|
bool menu_changed = cur_menu != prev_menu;
|
||||||
|
if (menu_changed) {
|
||||||
ui_context->rml.swap_document(cur_menu);
|
ui_context->rml.swap_document(cur_menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1123,6 +1157,10 @@ void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderFramebuffer* s
|
||||||
bool cont_interacted = false;
|
bool cont_interacted = false;
|
||||||
bool kb_interacted = false;
|
bool kb_interacted = false;
|
||||||
|
|
||||||
|
if (cur_menu == recomp::Menu::Config) {
|
||||||
|
ui_context->rml.update_config_menu_loop(menu_changed);
|
||||||
|
}
|
||||||
|
|
||||||
while (recomp::try_deque_event(cur_event)) {
|
while (recomp::try_deque_event(cur_event)) {
|
||||||
bool menu_is_open = cur_menu != recomp::Menu::None;
|
bool menu_is_open = cur_menu != recomp::Menu::None;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,106 @@
|
||||||
|
#include "recomp_ui.h"
|
||||||
|
#include "RmlUi/Core.h"
|
||||||
|
#include "ui_rml_hacks.hpp"
|
||||||
|
|
||||||
|
//! these are hidden methods not exposed by RmlUi
|
||||||
|
//! they may need to be updated eventually with RmlUi
|
||||||
|
|
||||||
|
enum class CanFocus { Yes, No, NoAndNoChildren };
|
||||||
|
CanFocus CanFocusElement(Rml::Element* element)
|
||||||
|
{
|
||||||
|
if (!element->IsVisible())
|
||||||
|
return CanFocus::NoAndNoChildren;
|
||||||
|
|
||||||
|
const Rml::ComputedValues& computed = element->GetComputedValues();
|
||||||
|
|
||||||
|
if (computed.focus() == Rml::Style::Focus::None)
|
||||||
|
return CanFocus::NoAndNoChildren;
|
||||||
|
|
||||||
|
if (computed.tab_index() == Rml::Style::TabIndex::Auto)
|
||||||
|
return CanFocus::Yes;
|
||||||
|
|
||||||
|
return CanFocus::No;
|
||||||
|
}
|
||||||
|
Rml::Element* SearchFocusSubtree(Rml::Element* element, bool forward)
|
||||||
|
{
|
||||||
|
auto can_focus = CanFocusElement(element);
|
||||||
|
if (can_focus == CanFocus::Yes)
|
||||||
|
return element;
|
||||||
|
else if (can_focus == CanFocus::NoAndNoChildren)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
for (int i = 0; i < element->GetNumChildren(); i++)
|
||||||
|
{
|
||||||
|
int child_index = i;
|
||||||
|
if (!forward)
|
||||||
|
child_index = element->GetNumChildren() - i - 1;
|
||||||
|
if (Rml::Element* result = SearchFocusSubtree(element->GetChild(child_index), forward))
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rml::Element* RecompRml::FindNextTabElement(Rml::Element* current_element, bool forward)
|
||||||
|
{
|
||||||
|
// This algorithm is quite sneaky, I originally thought a depth first search would work, but it appears not. What is
|
||||||
|
// required is to cut the tree in half along the nodes from current_element up the root and then either traverse the
|
||||||
|
// tree in a clockwise or anticlock wise direction depending if you're searching forward or backward respectively.
|
||||||
|
|
||||||
|
// If we're searching forward, check the immediate children of this node first off.
|
||||||
|
if (forward)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < current_element->GetNumChildren(); i++)
|
||||||
|
if (Rml::Element* result = SearchFocusSubtree(current_element->GetChild(i), forward))
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now walk up the tree, testing either the bottom or top
|
||||||
|
// of the tree, depending on whether we're going forward
|
||||||
|
// or backward respectively.
|
||||||
|
bool search_enabled = false;
|
||||||
|
Rml::Element* document = current_element->GetOwnerDocument();
|
||||||
|
Rml::Element* child = current_element;
|
||||||
|
Rml::Element* parent = current_element->GetParentNode();
|
||||||
|
while (child != document)
|
||||||
|
{
|
||||||
|
const int num_children = parent->GetNumChildren();
|
||||||
|
for (int i = 0; i < num_children; i++)
|
||||||
|
{
|
||||||
|
// Calculate index into children
|
||||||
|
const int child_index = forward ? i : (num_children - i - 1);
|
||||||
|
Rml::Element* search_child = parent->GetChild(child_index);
|
||||||
|
|
||||||
|
// Do a search if its enabled
|
||||||
|
if (search_enabled)
|
||||||
|
if (Rml::Element* result = SearchFocusSubtree(search_child, forward))
|
||||||
|
return result;
|
||||||
|
|
||||||
|
// Enable searching when we reach the child.
|
||||||
|
if (search_child == child)
|
||||||
|
search_enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Advance up the tree
|
||||||
|
child = parent;
|
||||||
|
parent = parent->GetParentNode();
|
||||||
|
search_enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We could not find anything to focus along this direction.
|
||||||
|
|
||||||
|
// If we can focus the document, then focus that now.
|
||||||
|
if (current_element != document && CanFocusElement(document) == CanFocus::Yes)
|
||||||
|
return document;
|
||||||
|
|
||||||
|
// Otherwise, search the entire document tree. This way we will wrap around.
|
||||||
|
const int num_children = document->GetNumChildren();
|
||||||
|
for (int i = 0; i < num_children; i++)
|
||||||
|
{
|
||||||
|
const int child_index = forward ? i : (num_children - i - 1);
|
||||||
|
if (Rml::Element* result = SearchFocusSubtree(document->GetChild(child_index), forward))
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
#ifndef UI_RML_HACKS_H
|
||||||
|
#define UI_RML_HACKS_H
|
||||||
|
|
||||||
|
#include "RmlUi/Core.h"
|
||||||
|
namespace RecompRml {
|
||||||
|
Rml::Element* FindNextTabElement(Rml::Element* current_element, bool forward);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue