Implemented prompt component and utilize for gfx change confirmation
This commit is contained in:
parent
bf167f31c7
commit
12f52eab2f
|
@ -10,18 +10,18 @@
|
||||||
<div class="prompt__controls">
|
<div class="prompt__controls">
|
||||||
<button
|
<button
|
||||||
autofocus="true"
|
autofocus="true"
|
||||||
id="prompt-button-left"
|
id="prompt__confirm-button"
|
||||||
class="button button--success"
|
class="button button--success"
|
||||||
style="nav-left: none; nav-right: #prompt-button-right"
|
style="nav-left: none; nav-right: #prompt__cancel-button"
|
||||||
>
|
>
|
||||||
<div class="button__label">{{ promptConfirmLabel }}</div>
|
<div class="button__label" id="prompt__confirm-button-label">{{ promptConfirmLabel }}</div>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
id="prompt-button-right"
|
id="prompt__cancel-button"
|
||||||
class="button button--error"
|
class="button button--error"
|
||||||
style="nav-right: none; nav-left: #prompt-button-left"
|
style="nav-right: none; nav-left: #prompt__confirm-button"
|
||||||
>
|
>
|
||||||
<div class="button__label">{{ promptCancelLabel }}</div>
|
<div class="button__label" id="prompt__cancel-button-label">{{ promptCancelLabel }}</div>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -101,16 +101,19 @@
|
||||||
<label><span style="font-family:promptfont;">↧</span> Accept</label> -->
|
<label><span style="font-family:promptfont;">↧</span> Accept</label> -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div
|
<div
|
||||||
data-if="1==0"
|
id="prompt-root"
|
||||||
data-alias-promptOpen="0"
|
data-model="prompt_model"
|
||||||
data-alias-promptHeader="'test header'"
|
data-if="prompt__open"
|
||||||
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-promptOpen="prompt__open"
|
||||||
data-alias-promptConfirmLabel="'Confirm'"
|
data-alias-promptHeader="prompt__header"
|
||||||
data-alias-promptCancelLabel="'Cancel'"
|
data-alias-promptContent="prompt__content"
|
||||||
|
data-alias-promptConfirmLabel="prompt__confirmLabel"
|
||||||
|
data-alias-promptCancelLabel="prompt__cancelLabel"
|
||||||
|
data-event-click="prompt__on_click"
|
||||||
>
|
>
|
||||||
<template src="prompt"/>
|
<template src="prompt"/>
|
||||||
</div> -->
|
</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> -->
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,7 +1,11 @@
|
||||||
|
@use 'sass:math';
|
||||||
|
|
||||||
|
$prompt-space: 24;
|
||||||
|
|
||||||
.prompt {
|
.prompt {
|
||||||
&__overlay {
|
&__overlay {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
background-color: $color-bg-overlay;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__overlay,
|
&__overlay,
|
||||||
|
@ -30,7 +34,7 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
flex: 1 1 100%;
|
flex: 1 1 100%;
|
||||||
max-width: space(820);
|
max-width: space(700);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: auto;
|
height: auto;
|
||||||
background: $color-modal-overlay;
|
background: $color-modal-overlay;
|
||||||
|
@ -39,19 +43,27 @@
|
||||||
border-width: $border-width-thickness;
|
border-width: $border-width-thickness;
|
||||||
|
|
||||||
h3, p {
|
h3, p {
|
||||||
margin: space(16);
|
margin: space($prompt-space);
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__controls {
|
&__controls {
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: space(16);
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
padding: space($prompt-space) space(math.div($prompt-space, 2));
|
||||||
border-top-color: $color-border-soft;
|
border-top-color: $color-border-soft;
|
||||||
border-top-width: $border-width-thickness;
|
border-top-width: $border-width-thickness;
|
||||||
|
|
||||||
.button {
|
.button {
|
||||||
nav-up: none;
|
nav-up: none;
|
||||||
nav-down: none;
|
nav-down: none;
|
||||||
|
margin: 0 space(math.div($prompt-space, 2));
|
||||||
|
min-width: space(math.div(700, 3));
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "SDL.h"
|
#include "SDL.h"
|
||||||
|
#include "RmlUi/Core.h"
|
||||||
|
|
||||||
namespace Rml {
|
namespace Rml {
|
||||||
class ElementDocument;
|
class ElementDocument;
|
||||||
|
@ -61,6 +62,38 @@ namespace recomp {
|
||||||
void set_cursor_visible(bool visible);
|
void set_cursor_visible(bool visible);
|
||||||
void update_supported_options();
|
void update_supported_options();
|
||||||
void toggle_fullscreen();
|
void toggle_fullscreen();
|
||||||
|
|
||||||
|
struct PromptContext {
|
||||||
|
Rml::DataModelHandle model_handle;
|
||||||
|
std::string header = "";
|
||||||
|
std::string content = "";
|
||||||
|
std::string confirmLabel = "Confirm";
|
||||||
|
std::string cancelLabel = "Cancel";
|
||||||
|
std::function<void()> onConfirm;
|
||||||
|
std::function<void()> onCancel;
|
||||||
|
|
||||||
|
bool open = false;
|
||||||
|
bool shouldFocus = false;
|
||||||
|
bool focusOnCancel = true;
|
||||||
|
|
||||||
|
PromptContext() = default;
|
||||||
|
|
||||||
|
void close_prompt();
|
||||||
|
void open_prompt(
|
||||||
|
const std::string& headerText,
|
||||||
|
const std::string& contentText,
|
||||||
|
const std::string& confirmLabelText,
|
||||||
|
const std::string& cancelLabelText,
|
||||||
|
std::function<void()> confirmCb,
|
||||||
|
std::function<void()> cancelCb,
|
||||||
|
bool shouldFocusOnCancel = true
|
||||||
|
);
|
||||||
|
void on_confirm(void);
|
||||||
|
void on_cancel(void);
|
||||||
|
void on_click(Rml::DataModelHandle model_handle, Rml::Event& event, const Rml::VariantList& inputs);
|
||||||
|
};
|
||||||
|
|
||||||
|
PromptContext *get_prompt_context(void);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -15,6 +15,73 @@ Rml::DataModelHandle general_model_handle;
|
||||||
Rml::DataModelHandle controls_model_handle;
|
Rml::DataModelHandle controls_model_handle;
|
||||||
Rml::DataModelHandle graphics_model_handle;
|
Rml::DataModelHandle graphics_model_handle;
|
||||||
Rml::DataModelHandle sound_options_model_handle;
|
Rml::DataModelHandle sound_options_model_handle;
|
||||||
|
|
||||||
|
recomp::PromptContext prompt_context;
|
||||||
|
|
||||||
|
namespace recomp {
|
||||||
|
void PromptContext::close_prompt() {
|
||||||
|
open = false;
|
||||||
|
model_handle.DirtyVariable("prompt__open");
|
||||||
|
}
|
||||||
|
|
||||||
|
void PromptContext::open_prompt(
|
||||||
|
const std::string& headerText,
|
||||||
|
const std::string& contentText,
|
||||||
|
const std::string& confirmLabelText,
|
||||||
|
const std::string& cancelLabelText,
|
||||||
|
std::function<void()> confirmCb,
|
||||||
|
std::function<void()> cancelCb,
|
||||||
|
bool shouldFocusOnCancel
|
||||||
|
) {
|
||||||
|
open = true;
|
||||||
|
header = headerText;
|
||||||
|
content = contentText;
|
||||||
|
confirmLabel = confirmLabelText;
|
||||||
|
cancelLabel = cancelLabelText;
|
||||||
|
onConfirm = confirmCb;
|
||||||
|
onCancel = cancelCb;
|
||||||
|
focusOnCancel = shouldFocusOnCancel;
|
||||||
|
|
||||||
|
model_handle.DirtyVariable("prompt__open");
|
||||||
|
model_handle.DirtyVariable("prompt__header");
|
||||||
|
model_handle.DirtyVariable("prompt__content");
|
||||||
|
model_handle.DirtyVariable("prompt__confirmLabel");
|
||||||
|
model_handle.DirtyVariable("prompt__cancelLabel");
|
||||||
|
shouldFocus = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PromptContext::on_confirm(void) {
|
||||||
|
onConfirm();
|
||||||
|
open = false;
|
||||||
|
model_handle.DirtyVariable("prompt__open");
|
||||||
|
}
|
||||||
|
|
||||||
|
void PromptContext::on_cancel(void) {
|
||||||
|
onCancel();
|
||||||
|
open = false;
|
||||||
|
model_handle.DirtyVariable("prompt__open");
|
||||||
|
}
|
||||||
|
|
||||||
|
void PromptContext::on_click(Rml::DataModelHandle model_handle, Rml::Event& event, const Rml::VariantList& inputs) {
|
||||||
|
Rml::Element *target = event.GetTargetElement();
|
||||||
|
auto id = target->GetId();
|
||||||
|
if (id == "prompt__confirm-button" || id == "prompt__confirm-button-label") {
|
||||||
|
on_confirm();
|
||||||
|
event.StopPropagation();
|
||||||
|
} else if (id == "prompt__cancel-button" || id == "prompt__cancel-button-label") {
|
||||||
|
on_cancel();
|
||||||
|
event.StopPropagation();
|
||||||
|
}
|
||||||
|
if (event.GetCurrentElement()->GetId() == "prompt-root") {
|
||||||
|
event.StopPropagation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PromptContext *get_prompt_context() {
|
||||||
|
return &prompt_context;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// True if controller config menu is open, false if keyboard config menu is open, undefined otherwise
|
// True if controller config menu is open, false if keyboard config menu is open, undefined otherwise
|
||||||
bool configuring_controller = false;
|
bool configuring_controller = false;
|
||||||
|
|
||||||
|
@ -108,7 +175,7 @@ void recomp::set_cont_or_kb(bool cont_interacted) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void close_config_menu() {
|
void close_config_menu_impl() {
|
||||||
recomp::save_config();
|
recomp::save_config();
|
||||||
|
|
||||||
if (ultramodern::is_game_started()) {
|
if (ultramodern::is_game_started()) {
|
||||||
|
@ -118,6 +185,29 @@ void close_config_menu() {
|
||||||
recomp::set_current_menu(recomp::Menu::Launcher);
|
recomp::set_current_menu(recomp::Menu::Launcher);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
void close_config_menu() {
|
||||||
|
if (ultramodern::get_graphics_config() != new_options) {
|
||||||
|
prompt_context.open_prompt(
|
||||||
|
"Graphics options have changed",
|
||||||
|
"Would you like to apply or discard the changes?",
|
||||||
|
"Apply",
|
||||||
|
"Discard",
|
||||||
|
[]() {
|
||||||
|
ultramodern::set_graphics_config(new_options);
|
||||||
|
graphics_model_handle.DirtyAllVariables();
|
||||||
|
close_config_menu_impl();
|
||||||
|
},
|
||||||
|
[]() {
|
||||||
|
new_options = ultramodern::get_graphics_config();
|
||||||
|
graphics_model_handle.DirtyAllVariables();
|
||||||
|
close_config_menu_impl();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
close_config_menu_impl();
|
||||||
|
}
|
||||||
|
|
||||||
struct ControlOptionsContext {
|
struct ControlOptionsContext {
|
||||||
int rumble_strength = 50; // 0 to 100
|
int rumble_strength = 50; // 0 to 100
|
||||||
|
@ -632,6 +722,24 @@ public:
|
||||||
debug_context.model_handle = constructor.GetModelHandle();
|
debug_context.model_handle = constructor.GetModelHandle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void make_prompt_bindings(Rml::Context* context) {
|
||||||
|
Rml::DataModelConstructor constructor = context->CreateDataModel("prompt_model");
|
||||||
|
if (!constructor) {
|
||||||
|
throw std::runtime_error("Failed to make RmlUi data model for the prompt");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind the debug mode enabled flag.
|
||||||
|
constructor.Bind("prompt__open", &prompt_context.open);
|
||||||
|
constructor.Bind("prompt__header", &prompt_context.header);
|
||||||
|
constructor.Bind("prompt__content", &prompt_context.content);
|
||||||
|
constructor.Bind("prompt__confirmLabel", &prompt_context.confirmLabel);
|
||||||
|
constructor.Bind("prompt__cancelLabel", &prompt_context.cancelLabel);
|
||||||
|
|
||||||
|
constructor.BindEventCallback("prompt__on_click", &recomp::PromptContext::on_click, &prompt_context);
|
||||||
|
|
||||||
|
prompt_context.model_handle = constructor.GetModelHandle();
|
||||||
|
}
|
||||||
|
|
||||||
void make_bindings(Rml::Context* context) override {
|
void make_bindings(Rml::Context* context) override {
|
||||||
make_nav_help_bindings(context);
|
make_nav_help_bindings(context);
|
||||||
make_general_bindings(context);
|
make_general_bindings(context);
|
||||||
|
@ -639,6 +747,7 @@ public:
|
||||||
make_graphics_bindings(context);
|
make_graphics_bindings(context);
|
||||||
make_sound_options_bindings(context);
|
make_sound_options_bindings(context);
|
||||||
make_debug_bindings(context);
|
make_debug_bindings(context);
|
||||||
|
make_prompt_bindings(context);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -985,6 +985,21 @@ struct UIContext {
|
||||||
tab->SetProperty("nav-down", "#" + id);
|
tab->SetProperty("nav-down", "#" + id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void update_prompt_loop(void) {
|
||||||
|
recomp::PromptContext *ctx = recomp::get_prompt_context();
|
||||||
|
if (!ctx->shouldFocus) return;
|
||||||
|
|
||||||
|
Rml::Element* focused = current_document->GetFocusLeafNode();
|
||||||
|
if (focused) focused->Blur();
|
||||||
|
|
||||||
|
Rml::Element *targetButton = current_document->GetElementById(
|
||||||
|
ctx->focusOnCancel ? "prompt__cancel-button" : "prompt__confirm-button");
|
||||||
|
|
||||||
|
targetButton->Focus(true);
|
||||||
|
|
||||||
|
ctx->shouldFocus = false;
|
||||||
|
}
|
||||||
} rml;
|
} rml;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1148,6 +1163,9 @@ void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderFramebuffer* s
|
||||||
if (cur_menu == recomp::Menu::Config) {
|
if (cur_menu == recomp::Menu::Config) {
|
||||||
ui_context->rml.update_config_menu_loop(menu_changed);
|
ui_context->rml.update_config_menu_loop(menu_changed);
|
||||||
}
|
}
|
||||||
|
if (cur_menu != recomp::Menu::None) {
|
||||||
|
ui_context->rml.update_prompt_loop();
|
||||||
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
|
Loading…
Reference in New Issue