added quit button and confirmation prompt

This commit is contained in:
thecozies 2024-04-21 15:55:45 -05:00
parent 17aa09506b
commit c06889c3bb
8 changed files with 141 additions and 19 deletions

View File

@ -72,15 +72,23 @@
<template src="config-menu__debug" /> <template src="config-menu__debug" />
</panel> </panel>
</tabset> </tabset>
<!-- Close button absolutely positioned on top right of modal --> <div class="config__icon-buttons">
<button <button
class="icon-button config__exit-button" class="icon-button"
onclick="open_quit_game_prompt"
id="config__quit-game-button"
>
<svg src="icons/Quit.svg" />
</button>
<button
class="icon-button"
onclick="close_config_menu" onclick="close_config_menu"
style="z-index: 10000;" id="config__close-menu-button"
> >
<svg src="icons/X.svg" /> <svg src="icons/X.svg" />
</button> </button>
</div> </div>
</div>
<div <div
class="centered-page__controls" class="centered-page__controls"
data-model="nav_help_model" data-model="nav_help_model"

11
assets/icons/Quit.svg Normal file
View File

@ -0,0 +1,11 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M29 16L25 21" stroke="#FFFFFF" stroke-width="4" stroke-linecap="round"/>
<path d="M25 11L29 16" stroke="#FFFFFF" stroke-width="4" stroke-linecap="round"/>
<path d="M12 16L27 16" stroke="#FFFFFF" stroke-width="4" stroke-linecap="round"/>
<path d="M7 5L20 5" stroke="#FFFFFF" stroke-width="4" stroke-linecap="round"/>
<path d="M7 27L7 5" stroke="#FFFFFF" stroke-width="4" stroke-linecap="round"/>
<path d="M7 27L20 27" stroke="#FFFFFF" stroke-width="4" stroke-linecap="round"/>
<path d="M20 27V22" stroke="#FFFFFF" stroke-width="4" stroke-linecap="round"/>
<path d="M20 10V5" stroke="#FFFFFF" stroke-width="4" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 781 B

File diff suppressed because one or more lines are too long

View File

@ -37,9 +37,9 @@
border-radius: $border-radius-md; border-radius: $border-radius-md;
// Setting it by default for convenience // Setting it by default for convenience
// &--primary { &--primary {
// @include create-button-variation($color-primary); @include create-button-variation($color-primary);
// } }
&--large { &--large {
@extend %label-lg; @extend %label-lg;

View File

@ -1,8 +1,17 @@
.config__exit-button { .config__icon-buttons {
display: flex;
position: absolute; position: absolute;
top: space(8); top: space(8);
right: space(8); right: space(0);
flex-direction: row;
align-items: center;
justify-content: flex-end;
width: auto;
.icon-button {
margin: 0 space(8);
}
} }
.config__form { .config__form {

View File

@ -54,6 +54,16 @@ namespace recomp {
Count Count
}; };
enum class ButtonVariant {
Primary,
Secondary,
Tertiary,
Success,
Error,
Warning,
NumVariants,
};
void set_config_submenu(ConfigSubmenu submenu); void set_config_submenu(ConfigSubmenu submenu);
void destroy_ui(); void destroy_ui();
@ -63,15 +73,21 @@ namespace recomp {
void update_supported_options(); void update_supported_options();
void toggle_fullscreen(); void toggle_fullscreen();
extern const std::unordered_map<ButtonVariant, std::string> button_variants;
struct PromptContext { struct PromptContext {
Rml::DataModelHandle model_handle; Rml::DataModelHandle model_handle;
std::string header = ""; std::string header = "";
std::string content = ""; std::string content = "";
std::string confirmLabel = "Confirm"; std::string confirmLabel = "Confirm";
std::string cancelLabel = "Cancel"; std::string cancelLabel = "Cancel";
ButtonVariant confirmVariant = ButtonVariant::Success;
ButtonVariant cancelVariant = ButtonVariant::Error;
std::function<void()> onConfirm; std::function<void()> onConfirm;
std::function<void()> onCancel; std::function<void()> onCancel;
std::string returnElementId = "";
bool open = false; bool open = false;
bool shouldFocus = false; bool shouldFocus = false;
bool focusOnCancel = true; bool focusOnCancel = true;
@ -86,7 +102,10 @@ namespace recomp {
const std::string& cancelLabelText, const std::string& cancelLabelText,
std::function<void()> confirmCb, std::function<void()> confirmCb,
std::function<void()> cancelCb, std::function<void()> cancelCb,
bool shouldFocusOnCancel = true ButtonVariant _confirmVariant = ButtonVariant::Success,
ButtonVariant _cancelVariant = ButtonVariant::Error,
bool _focusOnCancel = true,
const std::string& _returnElementId = ""
); );
void on_confirm(void); void on_confirm(void);
void on_cancel(void); void on_cancel(void);

View File

@ -19,6 +19,15 @@ Rml::DataModelHandle sound_options_model_handle;
recomp::PromptContext prompt_context; recomp::PromptContext prompt_context;
namespace recomp { namespace recomp {
const std::unordered_map<ButtonVariant, std::string> button_variants {
{ButtonVariant::Primary, "primary"},
{ButtonVariant::Secondary, "secondary"},
{ButtonVariant::Tertiary, "tertiary"},
{ButtonVariant::Success, "success"},
{ButtonVariant::Error, "error"},
{ButtonVariant::Warning, "warning"}
};
void PromptContext::close_prompt() { void PromptContext::close_prompt() {
open = false; open = false;
model_handle.DirtyVariable("prompt__open"); model_handle.DirtyVariable("prompt__open");
@ -31,7 +40,10 @@ namespace recomp {
const std::string& cancelLabelText, const std::string& cancelLabelText,
std::function<void()> confirmCb, std::function<void()> confirmCb,
std::function<void()> cancelCb, std::function<void()> cancelCb,
bool shouldFocusOnCancel ButtonVariant _confirmVariant,
ButtonVariant _cancelVariant,
bool _focusOnCancel,
const std::string& _returnElementId
) { ) {
open = true; open = true;
header = headerText; header = headerText;
@ -40,7 +52,10 @@ namespace recomp {
cancelLabel = cancelLabelText; cancelLabel = cancelLabelText;
onConfirm = confirmCb; onConfirm = confirmCb;
onCancel = cancelCb; onCancel = cancelCb;
focusOnCancel = shouldFocusOnCancel; confirmVariant = _confirmVariant;
cancelVariant = _cancelVariant;
focusOnCancel = _focusOnCancel;
returnElementId = _returnElementId;
model_handle.DirtyVariable("prompt__open"); model_handle.DirtyVariable("prompt__open");
model_handle.DirtyVariable("prompt__header"); model_handle.DirtyVariable("prompt__header");
@ -201,7 +216,11 @@ void close_config_menu() {
new_options = ultramodern::get_graphics_config(); new_options = ultramodern::get_graphics_config();
graphics_model_handle.DirtyAllVariables(); graphics_model_handle.DirtyAllVariables();
close_config_menu_impl(); close_config_menu_impl();
} },
recomp::ButtonVariant::Success,
recomp::ButtonVariant::Error,
true,
"config__close-menu-button"
); );
return; return;
} }
@ -209,6 +228,23 @@ void close_config_menu() {
close_config_menu_impl(); close_config_menu_impl();
} }
void open_quit_game_prompt() {
prompt_context.open_prompt(
"Are you sure you want to quit?",
"Any progress since your last save will be lost.",
"Quit",
"Cancel",
[]() {
ultramodern::quit();
},
[]() {},
recomp::ButtonVariant::Error,
recomp::ButtonVariant::Tertiary,
true,
"config__quit-game-button"
);
}
struct ControlOptionsContext { struct ControlOptionsContext {
int rumble_strength = 50; // 0 to 100 int rumble_strength = 50; // 0 to 100
int gyro_sensitivity = 50; // 0 to 200 int gyro_sensitivity = 50; // 0 to 200
@ -344,7 +380,7 @@ public:
}); });
recomp::register_event(listener, "config_keydown", recomp::register_event(listener, "config_keydown",
[](const std::string& param, Rml::Event& event) { [](const std::string& param, Rml::Event& event) {
if (event.GetId() == Rml::EventId::Keydown) { if (!prompt_context.open && event.GetId() == Rml::EventId::Keydown) {
if (event.GetParameter<Rml::Input::KeyIdentifier>("key_identifier", Rml::Input::KeyIdentifier::KI_UNKNOWN) == Rml::Input::KeyIdentifier::KI_ESCAPE) { if (event.GetParameter<Rml::Input::KeyIdentifier>("key_identifier", Rml::Input::KeyIdentifier::KI_UNKNOWN) == Rml::Input::KeyIdentifier::KI_ESCAPE) {
close_config_menu(); close_config_menu();
} }
@ -362,6 +398,11 @@ public:
close_config_menu(); close_config_menu();
}); });
recomp::register_event(listener, "open_quit_game_prompt",
[](const std::string& param, Rml::Event& event) {
open_quit_game_prompt();
});
recomp::register_event(listener, "toggle_input_device", recomp::register_event(listener, "toggle_input_device",
[](const std::string& param, Rml::Event& event) { [](const std::string& param, Rml::Event& event) {
cur_device = cur_device == recomp::InputDevice::Controller cur_device = cur_device == recomp::InputDevice::Controller

View File

@ -987,7 +987,36 @@ struct UIContext {
} }
void update_prompt_loop(void) { void update_prompt_loop(void) {
static bool wasShowingPrompt = false;
recomp::PromptContext *ctx = recomp::get_prompt_context(); recomp::PromptContext *ctx = recomp::get_prompt_context();
if (!ctx->open && wasShowingPrompt) {
Rml::Element* focused = current_document->GetFocusLeafNode();
if (focused) focused->Blur();
bool didFocus = false;
if (ctx->returnElementId.size() > 0) {
Rml::Element *retEl = current_document->GetElementById(ctx->returnElementId);
if (retEl != nullptr && retEl->IsVisible()) {
retEl->Focus(true);
didFocus = true;
}
}
if (!didFocus) {
Rml::ElementList tabs;
current_document->GetElementsByTagName(tabs, "tab");
for (const auto& tab : tabs) {
if (tab->IsVisible()) {
tab->Focus(true);
break;
}
}
}
}
wasShowingPrompt = ctx->open;
if (!ctx->shouldFocus) return; if (!ctx->shouldFocus) return;
Rml::Element* focused = current_document->GetFocusLeafNode(); Rml::Element* focused = current_document->GetFocusLeafNode();
@ -999,6 +1028,11 @@ struct UIContext {
targetButton->Focus(true); targetButton->Focus(true);
ctx->shouldFocus = false; ctx->shouldFocus = false;
Rml::Element *confirmButton = current_document->GetElementById("prompt__confirm-button");
Rml::Element *cancelButton = current_document->GetElementById("prompt__cancel-button");
if (confirmButton != nullptr) confirmButton->SetClassNames("button button--" + recomp::button_variants.at(ctx->confirmVariant));
if (cancelButton != nullptr) cancelButton->SetClassNames( "button button--" + recomp::button_variants.at(ctx->cancelVariant));
} }
} rml; } rml;
}; };