Implemented prompt component and utilize for gfx change confirmation

This commit is contained in:
thecozies 2024-04-20 13:25:46 -05:00
parent bf167f31c7
commit 12f52eab2f
7 changed files with 194 additions and 19 deletions

View File

@ -10,18 +10,18 @@
<div class="prompt__controls">
<button
autofocus="true"
id="prompt-button-left"
id="prompt__confirm-button"
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
id="prompt-button-right"
id="prompt__cancel-button"
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>
</div>
</div>

View File

@ -101,16 +101,19 @@
<label><span style="font-family:promptfont;">&#x21A7;</span> Accept</label> -->
</div>
</div>
<!-- <div
data-if="1==0"
data-alias-promptOpen="0"
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'"
<div
id="prompt-root"
data-model="prompt_model"
data-if="prompt__open"
data-alias-promptOpen="prompt__open"
data-alias-promptHeader="prompt__header"
data-alias-promptContent="prompt__content"
data-alias-promptConfirmLabel="prompt__confirmLabel"
data-alias-promptCancelLabel="prompt__cancelLabel"
data-event-click="prompt__on_click"
>
<template src="prompt"/>
</div> -->
</div>
</div>
<!-- </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

View File

@ -1,7 +1,11 @@
@use 'sass:math';
$prompt-space: 24;
.prompt {
&__overlay {
pointer-events: none;
background-color: $color-bg-overlay;
}
&__overlay,
@ -30,7 +34,7 @@
position: relative;
margin: auto;
flex: 1 1 100%;
max-width: space(820);
max-width: space(700);
width: 100%;
height: auto;
background: $color-modal-overlay;
@ -39,19 +43,27 @@
border-width: $border-width-thickness;
h3, p {
margin: space(16);
margin: space($prompt-space);
}
p {
margin-top: 0;
}
}
&__controls {
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-width: $border-width-thickness;
.button {
nav-up: none;
nav-down: none;
margin: 0 space(math.div($prompt-space, 2));
min-width: space(math.div(700, 3));
text-align: center;
}
}
}

View File

@ -5,6 +5,7 @@
#include <string>
#include "SDL.h"
#include "RmlUi/Core.h"
namespace Rml {
class ElementDocument;
@ -61,6 +62,38 @@ namespace recomp {
void set_cursor_visible(bool visible);
void update_supported_options();
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

View File

@ -15,6 +15,73 @@ Rml::DataModelHandle general_model_handle;
Rml::DataModelHandle controls_model_handle;
Rml::DataModelHandle graphics_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
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();
if (ultramodern::is_game_started()) {
@ -118,6 +185,29 @@ void close_config_menu() {
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 {
int rumble_strength = 50; // 0 to 100
@ -632,6 +722,24 @@ public:
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 {
make_nav_help_bindings(context);
make_general_bindings(context);
@ -639,6 +747,7 @@ public:
make_graphics_bindings(context);
make_sound_options_bindings(context);
make_debug_bindings(context);
make_prompt_bindings(context);
}
};

View File

@ -985,6 +985,21 @@ struct UIContext {
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;
};
@ -1148,6 +1163,9 @@ void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderFramebuffer* s
if (cur_menu == recomp::Menu::Config) {
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)) {
bool menu_is_open = cur_menu != recomp::Menu::None;