More work on graphics config menu, improved focus handling in UI, fixed scissor bug in UI rendering
This commit is contained in:
parent
525092fd60
commit
ac131c7835
|
@ -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">
|
||||||
<label style="padding:4dp;">Aspect Ratio</label><br/>
|
<div class="option_row">
|
||||||
<hr/>
|
<div class="option">
|
||||||
<div style="padding:2dp;">
|
<label style="padding:4dp;">Resolution</label><br/>
|
||||||
<input type="radio" name="aspectratio" id="ar_original"/>
|
<hr/>
|
||||||
<label for="ar_original">Original</label>
|
<div class="option_list">
|
||||||
<input type="radio" name="aspectratio" id="ar_expand" checked="checked" style="nav-right:none"/>
|
<input type="radio" name="resolution" id="res_original"/>
|
||||||
<label for="ar_expand">Expand</label>
|
<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/>
|
||||||
|
<hr/>
|
||||||
|
<div class="option_list">
|
||||||
|
<input type="radio" name="aspectratio" id="ar_original" style="nav-left:none"/>
|
||||||
|
<label for="ar_original">Original</label>
|
||||||
|
<input type="radio" name="aspectratio" id="ar_expand" style="nav-up:#res_2x" checked="checked"/>
|
||||||
|
<label for="ar_expand">Expand</label>
|
||||||
|
</div>
|
||||||
|
</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>
|
||||||
<br/>
|
|
||||||
|
|
||||||
<label style="padding:4dp;">Refresh Rate</label><br/>
|
<div class="option_row">
|
||||||
<hr/>
|
<div class="option">
|
||||||
<p style="padding:2dp;">
|
<label style="padding:4dp;">Refresh Rate</label><br/>
|
||||||
<input type="radio" name="refreshrate" id="rr_original"/>
|
<hr/>
|
||||||
<label for="rr_original">Original</label>
|
<div class="option_list" style="flex-wrap: wrap;">
|
||||||
<input type="radio" name="refreshrate" id="rr_display"/>
|
<input type="radio" name="refreshrate" id="rr_original" style="nav-up:#ar_expand"/>
|
||||||
<label for="rr_display">Display</label>
|
<label for="rr_original">Original</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_display" style="nav-up:#ar_expand"/>
|
||||||
<label for="rr_manual">Manual</label>
|
<label for="rr_display">Display</label>
|
||||||
<br/>
|
<input type="radio" name="refreshrate" id="rr_manual" checked="checked" style="nav-up:#msaa_none;nav-right:none"/>
|
||||||
<input id="rr_manual_input" type="range" min="60" max="1000" style="font:normal;flex:1;nav-up:auto;nav-down:auto;" value="60"/>
|
<label for="rr_manual">Manual</label>
|
||||||
</p>
|
<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;width:100%" value="60"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</panel>
|
</panel>
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,10 +757,23 @@ 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;
|
||||||
SDL_GetWindowSizeInPixels(window, &width, &height);
|
SDL_GetWindowSizeInPixels(window, &width, &height);
|
||||||
|
|
Loading…
Reference in New Issue