More work on graphics config menu, improved focus handling in UI, fixed scissor bug in UI rendering

This commit is contained in:
Mr-Wiseguy 2023-12-30 19:03:57 -05:00
parent 525092fd60
commit ac131c7835
3 changed files with 171 additions and 44 deletions

View File

@ -23,7 +23,7 @@
text-align: center; text-align: center;
} }
#tab_graphics:selected { #tab_graphics:selected {
nav-down:#ar_original; nav-down:#res_auto;
} }
</style> </style>
</head> </head>
@ -35,29 +35,74 @@
<tab id="tab_graphics" autofocus>Graphics</tab> <tab id="tab_graphics" autofocus>Graphics</tab>
<panel> <panel>
<form> <form>
<div style="width:700dp; border-radius:0dp; height:auto; padding:16dp; text-align:left; margin-left:auto; margin-right:auto; font-effect: outline(2dp black); font-size:24dp; background:rgba(50,50,50,200);"> <div class="option_container">
<div class="option_row">
<div class="option">
<label style="padding:4dp;">Resolution</label><br/>
<hr/>
<div class="option_list">
<input type="radio" name="resolution" id="res_original"/>
<label for="res_original">Original</label>
<input type="radio" name="resolution" id="res_2x" style="nav-down:#ar_expand"/>
<label for="res_2x">Original 2x</label>
<input type="radio" name="resolution" id="res_auto" style="nav-down:#ar_expand" checked="checked"/>
<label for="res_auto">Auto</label>
</div>
</div>
<div class="option">
<label style="padding:4dp;">Window Mode</label><br/>
<hr/>
<div class="option_list">
<input type="radio" name="windowmode" id="wm_windowed" style="nav-down:#msaa_none" checked="checked"/>
<label for="wm_windowed">Windowed</label>
<input type="radio" name="windowmode" id="wm_fullscreen" style="nav-right:none"/>
<label for="wm_fullscreen">Fullscreen</label>
</div>
</div>
</div>
<div class="option_row">
<div class="option">
<label style="padding:4dp;">Aspect Ratio</label><br/> <label style="padding:4dp;">Aspect Ratio</label><br/>
<hr/> <hr/>
<div style="padding:2dp;"> <div class="option_list">
<input type="radio" name="aspectratio" id="ar_original"/> <input type="radio" name="aspectratio" id="ar_original" style="nav-left:none"/>
<label for="ar_original">Original</label> <label for="ar_original">Original</label>
<input type="radio" name="aspectratio" id="ar_expand" checked="checked" style="nav-right:none"/> <input type="radio" name="aspectratio" id="ar_expand" style="nav-up:#res_2x" checked="checked"/>
<label for="ar_expand">Expand</label> <label for="ar_expand">Expand</label>
</div> </div>
<br/> </div>
<div class="option">
<label style="padding:4dp;">MS Anti-Aliasing</label><br/>
<hr/>
<div class="option_list">
<input type="radio" name="antialiasing" id="msaa_none" checked="checked" style="nav-up:#wm_windowed;nav-left:none"/>
<label for="msaa_none">None</label>
<input type="radio" name="antialiasing" id="msaa_2x"/>
<label for="msaa_2x">2x</label>
<input type="radio" name="antialiasing" id="msaa_4x"/>
<label for="msaa_4x">4x</label>
<input type="radio" name="antialiasing" id="msaa_8x" style="nav-right:none"/>
<label for="msaa_8x">8x</label>
</div>
</div>
</div>
<div class="option_row">
<div class="option">
<label style="padding:4dp;">Refresh Rate</label><br/> <label style="padding:4dp;">Refresh Rate</label><br/>
<hr/> <hr/>
<p style="padding:2dp;"> <div class="option_list" style="flex-wrap: wrap;">
<input type="radio" name="refreshrate" id="rr_original"/> <input type="radio" name="refreshrate" id="rr_original" style="nav-up:#ar_expand"/>
<label for="rr_original">Original</label> <label for="rr_original">Original</label>
<input type="radio" name="refreshrate" id="rr_display"/> <input type="radio" name="refreshrate" id="rr_display" style="nav-up:#ar_expand"/>
<label for="rr_display">Display</label> <label for="rr_display">Display</label>
<input type="radio" name="refreshrate" id="rr_manual" checked="checked" style="nav-up:#ar_expand;nav-right:none"/> <input type="radio" name="refreshrate" id="rr_manual" checked="checked" style="nav-up:#msaa_none;nav-right:none"/>
<label for="rr_manual">Manual</label> <label for="rr_manual">Manual</label>
<br/> <div style="flex-basis:100%;height:0"/>
<input id="rr_manual_input" type="range" min="60" max="1000" style="font:normal;flex:1;nav-up:auto;nav-down:auto;" value="60"/> <input id="rr_manual_input" type="range" min="60" max="1000" style="font:normal;flex:1;nav-up:auto;nav-down:auto;width:100%" value="60"/>
</p> </div>
</div>
</div>
</div> </div>
</form> </form>
</panel> </panel>

View File

@ -22,6 +22,39 @@ body {
tab-index:none; tab-index:none;
} */ } */
div.option_container {
display:flex;
flex-direction: column;
width:850dp;
border-radius:0dp;
height:auto;
padding:16dp;
text-align:left;
margin-left:auto;
margin-right:auto;
font-effect: outline(2dp black);
font-size:24dp;
background:rgba(50,50,50,200);
}
div.option_row {
display:flex;
flex-direction: row;
}
div.option {
flex:1;
padding:4dp;
text-align:center;
}
div.option_list {
padding:2dp;
text-align:center;
display:flex;
justify-content:center;
}
div#title_bar { div#title_bar {
z-index: 1; z-index: 1;
position: absolute; position: absolute;
@ -71,7 +104,7 @@ div#content {
p { p {
text-align: left; text-align: left;
margin-bottom: 4dp; /* margin-bottom: 4dp; */
} }
h1 { h1 {
@ -255,17 +288,18 @@ select selectbox option:hover {
input.radio, input.radio,
input.checkbox { input.checkbox {
/* width: 30dp; */ /* width: 30dp; */
height: 30dp; /* height: 30dp; */
/* vertical-align: -11dp; */ /* vertical-align: -11dp; */
margin-top: 4dp; /* margin-top: 4dp; */
margin-bottom: 4dp; /* margin-bottom: 4dp; */
flex: 1; flex: 0;
tab-index:auto; tab-index:auto;
focus:auto; focus:auto;
nav-up:auto; nav-up:auto;
nav-down:auto; nav-down:auto;
nav-right:auto; nav-right:auto;
nav-left:auto; nav-left:auto;
width:0dp;
} }
input.radio + label { input.radio + label {
@ -276,6 +310,8 @@ input.radio + label {
text-align:center; text-align:center;
tab-index:none; tab-index:none;
focus:auto; focus:auto;
margin-left:10dp;
margin-right:10dp;
/* display:none; */ /* display:none; */
/* decorator: image(radio) */ /* decorator: image(radio) */
} }
@ -295,21 +331,11 @@ input.radio:checked + label {
/* decorator: image(radio-checked) */ /* decorator: image(radio-checked) */
} }
input.radio:hover + label {
color:#329696;
/* background: rgb(150,150,150); */
}
input.radio:focus + label { input.radio:focus + label {
color:#329696; color:#329696;
/* background: rgb(150,150,150); */ /* background: rgb(150,150,150); */
} }
input.radio:checked:hover + label {
border-bottom:1dp;
/* background: rgb(150,150,150); */
}
tabset { tabset {
font:bold; font:bold;
margin:1dp; margin:1dp;
@ -332,10 +358,6 @@ tab {
nav-left:auto; nav-left:auto;
} }
tab:hover {
color:#329696;
}
tab:selected { tab:selected {
border-top: 2dp; border-top: 2dp;
border-color: white; border-color: white;

View File

@ -339,7 +339,7 @@ public:
list_->setViewports(RT64::RenderViewport{ 0, 0, float(window_width_), float(window_height_) }); list_->setViewports(RT64::RenderViewport{ 0, 0, float(window_width_), float(window_height_) });
if (scissor_enabled_) { if (scissor_enabled_) {
list_->setScissors(RT64::RenderRect{ scissor_x_, scissor_y_, scissor_width_, scissor_height_ }); list_->setScissors(RT64::RenderRect{ scissor_x_, scissor_y_, scissor_width_ + scissor_x_, scissor_height_ + scissor_y_ });
} }
else { else {
list_->setScissors(RT64::RenderRect{ 0, 0, window_width_, window_height_ }); list_->setScissors(RT64::RenderRect{ 0, 0, window_width_, window_height_ });
@ -564,11 +564,34 @@ public:
} }
}; };
bool can_focus(Rml::Element* element) {
return element->GetProperty(Rml::PropertyId::TabIndex)->Get<Rml::Style::TabIndex>() != Rml::Property(Rml::Style::TabIndex::None);
}
Rml::Element* get_target(Rml::ElementDocument* document, Rml::Element* element) {
// Labels can have targets, so check if this element is a label.
if (element->GetTagName() == "label") {
// Check if the label has a "for" property.
Rml::String for_value = element->GetAttribute<Rml::String>("for", "");
// If there is a value for the "for" property, find that element and return it if it exists.
if (!for_value.empty()) {
Rml::Element* target_element = document->GetElementById(for_value);
if (target_element) {
return target_element;
}
}
}
// Return the element directly if no target exists.
return element;
}
struct { struct {
struct UIRenderContext render; struct UIRenderContext render;
class { class {
std::unordered_map<recomp::Menu, Rml::ElementDocument*> documents; std::unordered_map<recomp::Menu, Rml::ElementDocument*> documents;
Rml::ElementDocument* current_document; Rml::ElementDocument* current_document;
Rml::Element* prev_focused;
public: public:
SystemInterface_SDL system_interface; SystemInterface_SDL system_interface;
std::unique_ptr<RmlRenderInterface_RT64> render_interface; std::unique_ptr<RmlRenderInterface_RT64> render_interface;
@ -619,6 +642,30 @@ struct {
documents.emplace(recomp::Menu::Launcher, context->LoadDocument("assets/launcher.rml")); documents.emplace(recomp::Menu::Launcher, context->LoadDocument("assets/launcher.rml"));
documents.emplace(recomp::Menu::Config, context->LoadDocument("assets/config_menu.rml")); documents.emplace(recomp::Menu::Config, context->LoadDocument("assets/config_menu.rml"));
} }
void update_focus(bool mouse_moved) {
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) {
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();
}
}
}
// 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)) {
prev_focused->Focus();
}
else {
prev_focused = current_document->GetFocusLeafNode();
}
}
} rml; } rml;
} UIContext; } UIContext;
@ -710,9 +757,22 @@ void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderTexture* swap_
SDL_Event cur_event{}; SDL_Event cur_event{};
bool mouse_moved = false;
while (recomp::try_deque_event(cur_event)) { while (recomp::try_deque_event(cur_event)) {
RmlSDL::InputEventHandler(UIContext.rml.context, cur_event); RmlSDL::InputEventHandler(UIContext.rml.context, cur_event);
// If a menu is open then implement some additional behavior for specific events on top of what RmlUi normally does with them.
if (cur_menu != recomp::Menu::None) {
switch (cur_event.type) {
case SDL_EventType::SDL_MOUSEMOTION:
mouse_moved = true;
break;
} }
}
}
UIContext.rml.update_focus(mouse_moved);
if (cur_menu != recomp::Menu::None) { if (cur_menu != recomp::Menu::None) {
int width, height; int width, height;