diff --git a/src/ui/elements/ui_button.cpp b/src/ui/elements/ui_button.cpp index 70b0207..6da95d6 100644 --- a/src/ui/elements/ui_button.cpp +++ b/src/ui/elements/ui_button.cpp @@ -53,8 +53,8 @@ namespace recompui { hover_disabled_style.set_border_color({ 196, 196, 196, border_hover_opacity }); hover_disabled_style.set_background_color({ 196, 196, 196, background_hover_opacity }); - add_style(&hover_style, { hover_state }); - add_style(&disabled_style, { disabled_state }); + add_style(&hover_style, hover_state); + add_style(&disabled_style, disabled_state); add_style(&hover_disabled_style, { hover_state, disabled_state }); // transition: color 0.05s linear-in-out, background-color 0.05s linear-in-out; diff --git a/src/ui/elements/ui_element.cpp b/src/ui/elements/ui_element.cpp index 9919a01..477ddba 100644 --- a/src/ui/elements/ui_element.cpp +++ b/src/ui/elements/ui_element.cpp @@ -157,6 +157,10 @@ void Element::clear_children() { children.clear(); } +void Element::add_style(Style *style, const std::string_view style_name) { + add_style(style, { style_name }); +} + void Element::add_style(Style *style, const std::initializer_list &style_names) { for (const std::string_view &style_name : style_names) { style_name_index_map.emplace(style_name, styles.size()); diff --git a/src/ui/elements/ui_element.h b/src/ui/elements/ui_element.h index 336027e..7d774ac 100644 --- a/src/ui/elements/ui_element.h +++ b/src/ui/elements/ui_element.h @@ -44,6 +44,7 @@ public: Element(Element *parent, uint32_t events_enabled = 0, Rml::String base_class = "div"); virtual ~Element(); void clear_children(); + void add_style(Style *style, const std::string_view style_name); void add_style(Style *style, const std::initializer_list &style_names); void set_enabled(bool enabled); void set_text(const std::string &text); diff --git a/src/ui/ui_color_hack.cpp b/src/ui/ui_color_hack.cpp index 9cadbbe..cc9b6fd 100644 --- a/src/ui/ui_color_hack.cpp +++ b/src/ui/ui_color_hack.cpp @@ -7,166 +7,166 @@ using ColourMap = Rml::UnorderedMap; namespace recompui { - class PropertyParserColorHack : public Rml::PropertyParser { - public: - PropertyParserColorHack(); - virtual ~PropertyParserColorHack(); - bool ParseValue(Rml::Property& property, const Rml::String& value, const Rml::ParameterMap& /*parameters*/) const override; - private: - static ColourMap html_colours; - }; - static_assert(sizeof(PropertyParserColorHack) == sizeof(Rml::PropertyParserColour)); - PropertyParserColorHack::PropertyParserColorHack() { - html_colours["black"] = Rml::Colourb(0, 0, 0); - html_colours["silver"] = Rml::Colourb(192, 192, 192); - html_colours["gray"] = Rml::Colourb(128, 128, 128); - html_colours["grey"] = Rml::Colourb(128, 128, 128); - html_colours["white"] = Rml::Colourb(255, 255, 255); - html_colours["maroon"] = Rml::Colourb(128, 0, 0); - html_colours["red"] = Rml::Colourb(255, 0, 0); - html_colours["orange"] = Rml::Colourb(255, 165, 0); - html_colours["purple"] = Rml::Colourb(128, 0, 128); - html_colours["fuchsia"] = Rml::Colourb(255, 0, 255); - html_colours["green"] = Rml::Colourb(0, 128, 0); - html_colours["lime"] = Rml::Colourb(0, 255, 0); - html_colours["olive"] = Rml::Colourb(128, 128, 0); - html_colours["yellow"] = Rml::Colourb(255, 255, 0); - html_colours["navy"] = Rml::Colourb(0, 0, 128); - html_colours["blue"] = Rml::Colourb(0, 0, 255); - html_colours["teal"] = Rml::Colourb(0, 128, 128); - html_colours["aqua"] = Rml::Colourb(0, 255, 255); - html_colours["transparent"] = Rml::Colourb(0, 0, 0, 0); - html_colours["whitesmoke"] = Rml::Colourb(245, 245, 245); - } + class PropertyParserColorHack : public Rml::PropertyParser { + public: + PropertyParserColorHack(); + virtual ~PropertyParserColorHack(); + bool ParseValue(Rml::Property& property, const Rml::String& value, const Rml::ParameterMap& /*parameters*/) const override; + private: + static ColourMap html_colours; + }; + static_assert(sizeof(PropertyParserColorHack) == sizeof(Rml::PropertyParserColour)); + PropertyParserColorHack::PropertyParserColorHack() { + html_colours["black"] = Rml::Colourb(0, 0, 0); + html_colours["silver"] = Rml::Colourb(192, 192, 192); + html_colours["gray"] = Rml::Colourb(128, 128, 128); + html_colours["grey"] = Rml::Colourb(128, 128, 128); + html_colours["white"] = Rml::Colourb(255, 255, 255); + html_colours["maroon"] = Rml::Colourb(128, 0, 0); + html_colours["red"] = Rml::Colourb(255, 0, 0); + html_colours["orange"] = Rml::Colourb(255, 165, 0); + html_colours["purple"] = Rml::Colourb(128, 0, 128); + html_colours["fuchsia"] = Rml::Colourb(255, 0, 255); + html_colours["green"] = Rml::Colourb(0, 128, 0); + html_colours["lime"] = Rml::Colourb(0, 255, 0); + html_colours["olive"] = Rml::Colourb(128, 128, 0); + html_colours["yellow"] = Rml::Colourb(255, 255, 0); + html_colours["navy"] = Rml::Colourb(0, 0, 128); + html_colours["blue"] = Rml::Colourb(0, 0, 255); + html_colours["teal"] = Rml::Colourb(0, 128, 128); + html_colours["aqua"] = Rml::Colourb(0, 255, 255); + html_colours["transparent"] = Rml::Colourb(0, 0, 0, 0); + html_colours["whitesmoke"] = Rml::Colourb(245, 245, 245); + } - PropertyParserColorHack::~PropertyParserColorHack() {} + PropertyParserColorHack::~PropertyParserColorHack() {} - bool PropertyParserColorHack::ParseValue(Rml::Property& property, const Rml::String& value, const Rml::ParameterMap& /*parameters*/) const { - if (value.empty()) - return false; + bool PropertyParserColorHack::ParseValue(Rml::Property& property, const Rml::String& value, const Rml::ParameterMap& /*parameters*/) const { + if (value.empty()) + return false; - Rml::Colourb colour; + Rml::Colourb colour; - // Check for a hex colour. - if (value[0] == '#') - { - char hex_values[4][2] = { {'f', 'f'}, {'f', 'f'}, {'f', 'f'}, {'f', 'f'} }; + // Check for a hex colour. + if (value[0] == '#') + { + char hex_values[4][2] = { {'f', 'f'}, {'f', 'f'}, {'f', 'f'}, {'f', 'f'} }; - switch (value.size()) - { - // Single hex digit per channel, RGB and alpha. - case 5: - hex_values[3][0] = hex_values[3][1] = value[4]; - //-fallthrough - // Single hex digit per channel, RGB only. - case 4: - hex_values[0][0] = hex_values[0][1] = value[1]; - hex_values[1][0] = hex_values[1][1] = value[2]; - hex_values[2][0] = hex_values[2][1] = value[3]; - break; + switch (value.size()) + { + // Single hex digit per channel, RGB and alpha. + case 5: + hex_values[3][0] = hex_values[3][1] = value[4]; + //-fallthrough + // Single hex digit per channel, RGB only. + case 4: + hex_values[0][0] = hex_values[0][1] = value[1]; + hex_values[1][0] = hex_values[1][1] = value[2]; + hex_values[2][0] = hex_values[2][1] = value[3]; + break; - // Two hex digits per channel, RGB and alpha. - case 9: - hex_values[3][0] = value[7]; - hex_values[3][1] = value[8]; - //-fallthrough - // Two hex digits per channel, RGB only. - case 7: memcpy(hex_values, &value.c_str()[1], sizeof(char) * 6); break; + // Two hex digits per channel, RGB and alpha. + case 9: + hex_values[3][0] = value[7]; + hex_values[3][1] = value[8]; + //-fallthrough + // Two hex digits per channel, RGB only. + case 7: memcpy(hex_values, &value.c_str()[1], sizeof(char) * 6); break; - default: return false; - } + default: return false; + } - // Parse each of the colour elements. - for (size_t i = 0; i < 4; i++) - { - int tens = Rml::Math::HexToDecimal(hex_values[i][0]); - int ones = Rml::Math::HexToDecimal(hex_values[i][1]); - if (tens == -1 || ones == -1) - return false; + // Parse each of the colour elements. + for (size_t i = 0; i < 4; i++) + { + int tens = Rml::Math::HexToDecimal(hex_values[i][0]); + int ones = Rml::Math::HexToDecimal(hex_values[i][1]); + if (tens == -1 || ones == -1) + return false; - colour[i] = (Rml::byte)(tens * 16 + ones); - } - } - else if (value.substr(0, 3) == "rgb") - { - Rml::StringList values; - values.reserve(4); + colour[i] = (Rml::byte)(tens * 16 + ones); + } + } + else if (value.substr(0, 3) == "rgb") + { + Rml::StringList values; + values.reserve(4); - size_t find = value.find('('); - if (find == Rml::String::npos) - return false; + size_t find = value.find('('); + if (find == Rml::String::npos) + return false; - size_t begin_values = find + 1; + size_t begin_values = find + 1; - Rml::StringUtilities::ExpandString(values, value.substr(begin_values, value.rfind(')') - begin_values), ','); + Rml::StringUtilities::ExpandString(values, value.substr(begin_values, value.rfind(')') - begin_values), ','); - // Check if we're parsing an 'rgba' or 'rgb' colour declaration. - if (value.size() > 3 && value[3] == 'a') - { - if (values.size() != 4) - return false; - } - else - { - if (values.size() != 3) - return false; + // Check if we're parsing an 'rgba' or 'rgb' colour declaration. + if (value.size() > 3 && value[3] == 'a') + { + if (values.size() != 4) + return false; + } + else + { + if (values.size() != 3) + return false; - values.push_back("255"); - } + values.push_back("255"); + } - // Parse the three RGB values. - for (size_t i = 0; i < 3; ++i) - { - int component; + // Parse the three RGB values. + for (size_t i = 0; i < 3; ++i) + { + int component; - // We're parsing a percentage value. - if (values[i].size() > 0 && values[i][values[i].size() - 1] == '%') - component = int((float)atof(values[i].substr(0, values[i].size() - 1).c_str()) * (255.0f / 100.0f)); - // We're parsing a 0 -> 255 integer value. - else - component = atoi(values[i].c_str()); + // We're parsing a percentage value. + if (values[i].size() > 0 && values[i][values[i].size() - 1] == '%') + component = int((float)atof(values[i].substr(0, values[i].size() - 1).c_str()) * (255.0f / 100.0f)); + // We're parsing a 0 -> 255 integer value. + else + component = atoi(values[i].c_str()); - colour[i] = (Rml::byte)(Rml::Math::Clamp(component, 0, 255)); - } - // Parse the alpha value. Modified from the original RmlUi implementation to use 0-1 instead of 0-255. - { - int component; + colour[i] = (Rml::byte)(Rml::Math::Clamp(component, 0, 255)); + } + // Parse the alpha value. Modified from the original RmlUi implementation to use 0-1 instead of 0-255. + { + int component; - // We're parsing a percentage value. - if (values[3].size() > 0 && values[3][values[3].size() - 1] == '%') - component = ((float)atof(values[3].substr(0, values[3].size() - 1).c_str()) * (255.0f / 100.0f)); - // We're parsing a 0 -> 1 float value. - else - component = atof(values[3].c_str()) * 255.0f; + // We're parsing a percentage value. + if (values[3].size() > 0 && values[3][values[3].size() - 1] == '%') + component = ((float)atof(values[3].substr(0, values[3].size() - 1).c_str()) * (255.0f / 100.0f)); + // We're parsing a 0 -> 1 float value. + else + component = atof(values[3].c_str()) * 255.0f; - colour[3] = (Rml::byte)(Rml::Math::Clamp(component, 0, 255)); - } - } - else - { - // Check for the specification of an HTML colour. - ColourMap::const_iterator iterator = html_colours.find(Rml::StringUtilities::ToLower(value)); - if (iterator == html_colours.end()) - return false; - else - colour = (*iterator).second; - } + colour[3] = (Rml::byte)(Rml::Math::Clamp(component, 0, 255)); + } + } + else + { + // Check for the specification of an HTML colour. + ColourMap::const_iterator iterator = html_colours.find(Rml::StringUtilities::ToLower(value)); + if (iterator == html_colours.end()) + return false; + else + colour = (*iterator).second; + } - property.value = Rml::Variant(colour); - property.unit = Rml::Unit::COLOUR; + property.value = Rml::Variant(colour); + property.unit = Rml::Unit::COLOUR; - return true; - } + return true; + } - // This hack overwrites the contents of a property parser pointer for "color" (which is known to point to a valid Rml::PropertyParserColour) with the contents of a PropertyParserColorHack. - // This overwrites the object's vtable, allowing us to override color parsing behavior to use 0-1 alpha instead of 0-255. - // Ideally we'd just replace the pointer itself, but RmlUi doesn't provide a way to do that currently. - void apply_color_hack() { - // Allocate and leak a parser to act as a vtable source. - PropertyParserColorHack* new_parser = new PropertyParserColorHack(); - // Copy the allocated object into the color parser pointer to overwrite its vtable. - memcpy((void*)Rml::StyleSheetSpecification::GetParser("color"), (void*)new_parser, sizeof(*new_parser)); - } + // This hack overwrites the contents of a property parser pointer for "color" (which is known to point to a valid Rml::PropertyParserColour) with the contents of a PropertyParserColorHack. + // This overwrites the object's vtable, allowing us to override color parsing behavior to use 0-1 alpha instead of 0-255. + // Ideally we'd just replace the pointer itself, but RmlUi doesn't provide a way to do that currently. + void apply_color_hack() { + // Allocate and leak a parser to act as a vtable source. + PropertyParserColorHack* new_parser = new PropertyParserColorHack(); + // Copy the allocated object into the color parser pointer to overwrite its vtable. + memcpy((void*)Rml::StyleSheetSpecification::GetParser("color"), (void*)new_parser, sizeof(*new_parser)); + } - ColourMap PropertyParserColorHack::html_colours{}; + ColourMap PropertyParserColorHack::html_colours{}; } diff --git a/src/ui/ui_config.cpp b/src/ui/ui_config.cpp index 12bb20e..8cc8237 100644 --- a/src/ui/ui_config.cpp +++ b/src/ui/ui_config.cpp @@ -19,82 +19,82 @@ Rml::DataModelHandle sound_options_model_handle; recompui::PromptContext prompt_context; namespace recompui { - const std::unordered_map button_variants { - {ButtonVariant::Primary, "primary"}, - {ButtonVariant::Secondary, "secondary"}, - {ButtonVariant::Tertiary, "tertiary"}, - {ButtonVariant::Success, "success"}, - {ButtonVariant::Error, "error"}, - {ButtonVariant::Warning, "warning"} - }; + const std::unordered_map button_variants { + {ButtonVariant::Primary, "primary"}, + {ButtonVariant::Secondary, "secondary"}, + {ButtonVariant::Tertiary, "tertiary"}, + {ButtonVariant::Success, "success"}, + {ButtonVariant::Error, "error"}, + {ButtonVariant::Warning, "warning"} + }; - void PromptContext::close_prompt() { - open = false; - model_handle.DirtyVariable("prompt__open"); - } + 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 confirmCb, - std::function cancelCb, - ButtonVariant _confirmVariant, - ButtonVariant _cancelVariant, - bool _focusOnCancel, - const std::string& _returnElementId - ) { - open = true; - header = headerText; - content = contentText; - confirmLabel = confirmLabelText; - cancelLabel = cancelLabelText; - onConfirm = confirmCb; - onCancel = cancelCb; - confirmVariant = _confirmVariant; - cancelVariant = _cancelVariant; - focusOnCancel = _focusOnCancel; - returnElementId = _returnElementId; + void PromptContext::open_prompt( + const std::string& headerText, + const std::string& contentText, + const std::string& confirmLabelText, + const std::string& cancelLabelText, + std::function confirmCb, + std::function cancelCb, + ButtonVariant _confirmVariant, + ButtonVariant _cancelVariant, + bool _focusOnCancel, + const std::string& _returnElementId + ) { + open = true; + header = headerText; + content = contentText; + confirmLabel = confirmLabelText; + cancelLabel = cancelLabelText; + onConfirm = confirmCb; + onCancel = cancelCb; + confirmVariant = _confirmVariant; + cancelVariant = _cancelVariant; + focusOnCancel = _focusOnCancel; + returnElementId = _returnElementId; - 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; - } + 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_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_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(); - } - } + 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; - } + PromptContext *get_prompt_context() { + return &prompt_context; + } }; // True if controller config menu is open, false if keyboard config menu is open, undefined otherwise @@ -102,51 +102,51 @@ bool configuring_controller = false; template void get_option(const T& input, Rml::Variant& output) { - std::string value = ""; - to_json(value, input); + std::string value = ""; + to_json(value, input); - if (value.empty()) { - throw std::runtime_error("Invalid value :" + std::to_string(int(input))); - } + if (value.empty()) { + throw std::runtime_error("Invalid value :" + std::to_string(int(input))); + } - output = value; + output = value; } template void set_option(T& output, const Rml::Variant& input) { - T value = T::OptionCount; - from_json(input.Get(), value); + T value = T::OptionCount; + from_json(input.Get(), value); - if (value == T::OptionCount) { - throw std::runtime_error("Invalid value :" + input.Get()); - } + if (value == T::OptionCount) { + throw std::runtime_error("Invalid value :" + input.Get()); + } - output = value; + output = value; } template void bind_option(Rml::DataModelConstructor& constructor, const std::string& name, T* option) { - constructor.BindFunc(name, - [option](Rml::Variant& out) { get_option(*option, out); }, - [option](const Rml::Variant& in) { - set_option(*option, in); - graphics_model_handle.DirtyVariable("options_changed"); - graphics_model_handle.DirtyVariable("ds_info"); - } - ); + constructor.BindFunc(name, + [option](Rml::Variant& out) { get_option(*option, out); }, + [option](const Rml::Variant& in) { + set_option(*option, in); + graphics_model_handle.DirtyVariable("options_changed"); + graphics_model_handle.DirtyVariable("ds_info"); + } + ); }; template void bind_atomic(Rml::DataModelConstructor& constructor, Rml::DataModelHandle handle, const char* name, std::atomic* atomic_val) { - constructor.BindFunc(name, - [atomic_val](Rml::Variant& out) { - out = atomic_val->load(); - }, - [atomic_val, handle, name](const Rml::Variant& in) mutable { - atomic_val->store(in.Get()); - handle.DirtyVariable(name); - } - ); + constructor.BindFunc(name, + [atomic_val](Rml::Variant& out) { + out = atomic_val->load(); + }, + [atomic_val, handle, name](const Rml::Variant& in) mutable { + atomic_val->store(in.Get()); + handle.DirtyVariable(name); + } + ); } static int scanned_binding_index = -1; @@ -164,58 +164,58 @@ static bool cont_active = true; static recomp::InputDevice cur_device = recomp::InputDevice::Controller; int recomp::get_scanned_input_index() { - return scanned_input_index; + return scanned_input_index; } void recomp::finish_scanning_input(recomp::InputField scanned_field) { recomp::set_input_binding(static_cast(scanned_input_index), scanned_binding_index, cur_device, scanned_field); - scanned_input_index = -1; - scanned_binding_index = -1; - controls_model_handle.DirtyVariable("inputs"); - controls_model_handle.DirtyVariable("active_binding_input"); - controls_model_handle.DirtyVariable("active_binding_slot"); - nav_help_model_handle.DirtyVariable("nav_help__accept"); - nav_help_model_handle.DirtyVariable("nav_help__exit"); - graphics_model_handle.DirtyVariable("gfx_help__apply"); + scanned_input_index = -1; + scanned_binding_index = -1; + controls_model_handle.DirtyVariable("inputs"); + controls_model_handle.DirtyVariable("active_binding_input"); + controls_model_handle.DirtyVariable("active_binding_slot"); + nav_help_model_handle.DirtyVariable("nav_help__accept"); + nav_help_model_handle.DirtyVariable("nav_help__exit"); + graphics_model_handle.DirtyVariable("gfx_help__apply"); } void recomp::cancel_scanning_input() { recomp::stop_scanning_input(); - scanned_input_index = -1; - scanned_binding_index = -1; - controls_model_handle.DirtyVariable("inputs"); - controls_model_handle.DirtyVariable("active_binding_input"); - controls_model_handle.DirtyVariable("active_binding_slot"); - nav_help_model_handle.DirtyVariable("nav_help__accept"); - nav_help_model_handle.DirtyVariable("nav_help__exit"); - graphics_model_handle.DirtyVariable("gfx_help__apply"); + scanned_input_index = -1; + scanned_binding_index = -1; + controls_model_handle.DirtyVariable("inputs"); + controls_model_handle.DirtyVariable("active_binding_input"); + controls_model_handle.DirtyVariable("active_binding_slot"); + nav_help_model_handle.DirtyVariable("nav_help__accept"); + nav_help_model_handle.DirtyVariable("nav_help__exit"); + graphics_model_handle.DirtyVariable("gfx_help__apply"); } void recomp::config_menu_set_cont_or_kb(bool cont_interacted) { - if (cont_active != cont_interacted) { - cont_active = cont_interacted; + if (cont_active != cont_interacted) { + cont_active = cont_interacted; - if (nav_help_model_handle) { - nav_help_model_handle.DirtyVariable("nav_help__navigate"); - nav_help_model_handle.DirtyVariable("nav_help__accept"); - nav_help_model_handle.DirtyVariable("nav_help__exit"); - } + if (nav_help_model_handle) { + nav_help_model_handle.DirtyVariable("nav_help__navigate"); + nav_help_model_handle.DirtyVariable("nav_help__accept"); + nav_help_model_handle.DirtyVariable("nav_help__exit"); + } - if (graphics_model_handle) { - graphics_model_handle.DirtyVariable("gfx_help__apply"); - } - } + if (graphics_model_handle) { + graphics_model_handle.DirtyVariable("gfx_help__apply"); + } + } } void close_config_menu_impl() { zelda64::save_config(); - if (ultramodern::is_game_started()) { + if (ultramodern::is_game_started()) { recompui::set_current_menu(recompui::Menu::None); - } - else { + } + else { recompui::set_current_menu(recompui::Menu::Launcher); - } + } } // TODO: Remove once RT64 gets native fullscreen support on Linux @@ -224,232 +224,232 @@ extern SDL_Window* window; #endif void apply_graphics_config(void) { - ultramodern::renderer::set_graphics_config(new_options); + ultramodern::renderer::set_graphics_config(new_options); #if defined(__linux__) // TODO: Remove once RT64 gets native fullscreen support on Linux - if (new_options.wm_option == ultramodern::renderer::WindowMode::Fullscreen) { - SDL_SetWindowFullscreen(window,SDL_WINDOW_FULLSCREEN_DESKTOP); - } else { - SDL_SetWindowFullscreen(window,0); - } + if (new_options.wm_option == ultramodern::renderer::WindowMode::Fullscreen) { + SDL_SetWindowFullscreen(window,SDL_WINDOW_FULLSCREEN_DESKTOP); + } else { + SDL_SetWindowFullscreen(window,0); + } #endif } void close_config_menu() { - if (ultramodern::renderer::get_graphics_config() != new_options) { - prompt_context.open_prompt( - "Graphics options have changed", - "Would you like to apply or discard the changes?", - "Apply", - "Discard", - []() { - apply_graphics_config(); - graphics_model_handle.DirtyAllVariables(); - close_config_menu_impl(); - }, - []() { - new_options = ultramodern::renderer::get_graphics_config(); - graphics_model_handle.DirtyAllVariables(); - close_config_menu_impl(); - }, + if (ultramodern::renderer::get_graphics_config() != new_options) { + prompt_context.open_prompt( + "Graphics options have changed", + "Would you like to apply or discard the changes?", + "Apply", + "Discard", + []() { + apply_graphics_config(); + graphics_model_handle.DirtyAllVariables(); + close_config_menu_impl(); + }, + []() { + new_options = ultramodern::renderer::get_graphics_config(); + graphics_model_handle.DirtyAllVariables(); + close_config_menu_impl(); + }, recompui::ButtonVariant::Success, recompui::ButtonVariant::Error, - true, - "config__close-menu-button" - ); - return; - } + true, + "config__close-menu-button" + ); + return; + } - close_config_menu_impl(); + close_config_menu_impl(); } void zelda64::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(); - }, - []() {}, + prompt_context.open_prompt( + "Are you sure you want to quit?", + "Any progress since your last save will be lost.", + "Quit", + "Cancel", + []() { + ultramodern::quit(); + }, + []() {}, recompui::ButtonVariant::Error, recompui::ButtonVariant::Tertiary, - true, - "config__quit-game-button" - ); + true, + "config__quit-game-button" + ); } // These defaults values don't matter, as the config file handling overrides them. struct ControlOptionsContext { - int rumble_strength; // 0 to 100 - int gyro_sensitivity; // 0 to 100 - int mouse_sensitivity; // 0 to 100 - int joystick_deadzone; // 0 to 100 + int rumble_strength; // 0 to 100 + int gyro_sensitivity; // 0 to 100 + int mouse_sensitivity; // 0 to 100 + int joystick_deadzone; // 0 to 100 zelda64::TargetingMode targeting_mode; - recomp::BackgroundInputMode background_input_mode; - zelda64::AutosaveMode autosave_mode; + recomp::BackgroundInputMode background_input_mode; + zelda64::AutosaveMode autosave_mode; zelda64::CameraInvertMode camera_invert_mode; - zelda64::AnalogCamMode analog_cam_mode; + zelda64::AnalogCamMode analog_cam_mode; zelda64::CameraInvertMode analog_camera_invert_mode; }; ControlOptionsContext control_options_context; int recomp::get_rumble_strength() { - return control_options_context.rumble_strength; + return control_options_context.rumble_strength; } void recomp::set_rumble_strength(int strength) { - control_options_context.rumble_strength = strength; - if (general_model_handle) { - general_model_handle.DirtyVariable("rumble_strength"); - } + control_options_context.rumble_strength = strength; + if (general_model_handle) { + general_model_handle.DirtyVariable("rumble_strength"); + } } int recomp::get_gyro_sensitivity() { - return control_options_context.gyro_sensitivity; + return control_options_context.gyro_sensitivity; } int recomp::get_mouse_sensitivity() { - return control_options_context.mouse_sensitivity; + return control_options_context.mouse_sensitivity; } int recomp::get_joystick_deadzone() { - return control_options_context.joystick_deadzone; + return control_options_context.joystick_deadzone; } void recomp::set_gyro_sensitivity(int sensitivity) { - control_options_context.gyro_sensitivity = sensitivity; - if (general_model_handle) { - general_model_handle.DirtyVariable("gyro_sensitivity"); - } + control_options_context.gyro_sensitivity = sensitivity; + if (general_model_handle) { + general_model_handle.DirtyVariable("gyro_sensitivity"); + } } void recomp::set_mouse_sensitivity(int sensitivity) { - control_options_context.mouse_sensitivity = sensitivity; - if (general_model_handle) { - general_model_handle.DirtyVariable("mouse_sensitivity"); - } + control_options_context.mouse_sensitivity = sensitivity; + if (general_model_handle) { + general_model_handle.DirtyVariable("mouse_sensitivity"); + } } void recomp::set_joystick_deadzone(int deadzone) { - control_options_context.joystick_deadzone = deadzone; - if (general_model_handle) { - general_model_handle.DirtyVariable("joystick_deadzone"); - } + control_options_context.joystick_deadzone = deadzone; + if (general_model_handle) { + general_model_handle.DirtyVariable("joystick_deadzone"); + } } zelda64::TargetingMode zelda64::get_targeting_mode() { - return control_options_context.targeting_mode; + return control_options_context.targeting_mode; } void zelda64::set_targeting_mode(zelda64::TargetingMode mode) { - control_options_context.targeting_mode = mode; - if (general_model_handle) { - general_model_handle.DirtyVariable("targeting_mode"); - } + control_options_context.targeting_mode = mode; + if (general_model_handle) { + general_model_handle.DirtyVariable("targeting_mode"); + } } recomp::BackgroundInputMode recomp::get_background_input_mode() { - return control_options_context.background_input_mode; + return control_options_context.background_input_mode; } void recomp::set_background_input_mode(recomp::BackgroundInputMode mode) { - control_options_context.background_input_mode = mode; - if (general_model_handle) { - general_model_handle.DirtyVariable("background_input_mode"); - } - SDL_SetHint( - SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, - mode == recomp::BackgroundInputMode::On - ? "1" - : "0" - ); + control_options_context.background_input_mode = mode; + if (general_model_handle) { + general_model_handle.DirtyVariable("background_input_mode"); + } + SDL_SetHint( + SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, + mode == recomp::BackgroundInputMode::On + ? "1" + : "0" + ); } zelda64::AutosaveMode zelda64::get_autosave_mode() { - return control_options_context.autosave_mode; + return control_options_context.autosave_mode; } void zelda64::set_autosave_mode(zelda64::AutosaveMode mode) { - control_options_context.autosave_mode = mode; - if (general_model_handle) { - general_model_handle.DirtyVariable("autosave_mode"); - } + control_options_context.autosave_mode = mode; + if (general_model_handle) { + general_model_handle.DirtyVariable("autosave_mode"); + } } zelda64::CameraInvertMode zelda64::get_camera_invert_mode() { - return control_options_context.camera_invert_mode; + return control_options_context.camera_invert_mode; } void zelda64::set_camera_invert_mode(zelda64::CameraInvertMode mode) { - control_options_context.camera_invert_mode = mode; - if (general_model_handle) { - general_model_handle.DirtyVariable("camera_invert_mode"); - } + control_options_context.camera_invert_mode = mode; + if (general_model_handle) { + general_model_handle.DirtyVariable("camera_invert_mode"); + } } zelda64::AnalogCamMode zelda64::get_analog_cam_mode() { - return control_options_context.analog_cam_mode; + return control_options_context.analog_cam_mode; } void zelda64::set_analog_cam_mode(zelda64::AnalogCamMode mode) { - control_options_context.analog_cam_mode = mode; - if (general_model_handle) { - general_model_handle.DirtyVariable("analog_cam_mode"); - } + control_options_context.analog_cam_mode = mode; + if (general_model_handle) { + general_model_handle.DirtyVariable("analog_cam_mode"); + } } zelda64::CameraInvertMode zelda64::get_analog_camera_invert_mode() { - return control_options_context.analog_camera_invert_mode; + return control_options_context.analog_camera_invert_mode; } void zelda64::set_analog_camera_invert_mode(zelda64::CameraInvertMode mode) { - control_options_context.analog_camera_invert_mode = mode; - if (general_model_handle) { - general_model_handle.DirtyVariable("analog_camera_invert_mode"); - } + control_options_context.analog_camera_invert_mode = mode; + if (general_model_handle) { + general_model_handle.DirtyVariable("analog_camera_invert_mode"); + } } struct SoundOptionsContext { - std::atomic main_volume; // Option to control the volume of all sound - std::atomic bgm_volume; - std::atomic low_health_beeps_enabled; // RmlUi doesn't seem to like "true"/"false" strings for setting variants so an int is used here instead. - void reset() { - bgm_volume = 100; - main_volume = 100; - low_health_beeps_enabled = (int)true; - } - SoundOptionsContext() { - reset(); - } + std::atomic main_volume; // Option to control the volume of all sound + std::atomic bgm_volume; + std::atomic low_health_beeps_enabled; // RmlUi doesn't seem to like "true"/"false" strings for setting variants so an int is used here instead. + void reset() { + bgm_volume = 100; + main_volume = 100; + low_health_beeps_enabled = (int)true; + } + SoundOptionsContext() { + reset(); + } }; SoundOptionsContext sound_options_context; void zelda64::reset_sound_settings() { - sound_options_context.reset(); - if (sound_options_model_handle) { - sound_options_model_handle.DirtyAllVariables(); - } + sound_options_context.reset(); + if (sound_options_model_handle) { + sound_options_model_handle.DirtyAllVariables(); + } } void zelda64::set_main_volume(int volume) { - sound_options_context.main_volume.store(volume); - if (sound_options_model_handle) { - sound_options_model_handle.DirtyVariable("main_volume"); - } + sound_options_context.main_volume.store(volume); + if (sound_options_model_handle) { + sound_options_model_handle.DirtyVariable("main_volume"); + } } int zelda64::get_main_volume() { - return sound_options_context.main_volume.load(); + return sound_options_context.main_volume.load(); } void zelda64::set_bgm_volume(int volume) { sound_options_context.bgm_volume.store(volume); - if (sound_options_model_handle) { - sound_options_model_handle.DirtyVariable("bgm_volume"); - } + if (sound_options_model_handle) { + sound_options_model_handle.DirtyVariable("bgm_volume"); + } } int zelda64::get_bgm_volume() { @@ -458,9 +458,9 @@ int zelda64::get_bgm_volume() { void zelda64::set_low_health_beeps_enabled(bool enabled) { sound_options_context.low_health_beeps_enabled.store((int)enabled); - if (sound_options_model_handle) { - sound_options_model_handle.DirtyVariable("low_health_beeps_enabled"); - } + if (sound_options_model_handle) { + sound_options_model_handle.DirtyVariable("low_health_beeps_enabled"); + } } bool zelda64::get_low_health_beeps_enabled() { @@ -468,585 +468,585 @@ bool zelda64::get_low_health_beeps_enabled() { } struct DebugContext { - Rml::DataModelHandle model_handle; - std::vector area_names; - std::vector scene_names; - std::vector entrance_names; - int area_index = 0; - int scene_index = 0; - int entrance_index = 0; - int set_time_day = 1; - int set_time_hour = 12; - int set_time_minute = 0; - bool debug_enabled = false; + Rml::DataModelHandle model_handle; + std::vector area_names; + std::vector scene_names; + std::vector entrance_names; + int area_index = 0; + int scene_index = 0; + int entrance_index = 0; + int set_time_day = 1; + int set_time_hour = 12; + int set_time_minute = 0; + bool debug_enabled = false; - DebugContext() { - for (const auto& area : zelda64::game_warps) { - area_names.emplace_back(area.name); - } - update_warp_names(); - } + DebugContext() { + for (const auto& area : zelda64::game_warps) { + area_names.emplace_back(area.name); + } + update_warp_names(); + } - void update_warp_names() { - scene_names.clear(); - for (const auto& scene : zelda64::game_warps[area_index].scenes) { - scene_names.emplace_back(scene.name); - } - - entrance_names = zelda64::game_warps[area_index].scenes[scene_index].entrances; - } + void update_warp_names() { + scene_names.clear(); + for (const auto& scene : zelda64::game_warps[area_index].scenes) { + scene_names.emplace_back(scene.name); + } + + entrance_names = zelda64::game_warps[area_index].scenes[scene_index].entrances; + } }; void recompui::update_rml_display_refresh_rate() { - static uint32_t lastRate = 0; - if (!graphics_model_handle) return; + static uint32_t lastRate = 0; + if (!graphics_model_handle) return; - uint32_t curRate = ultramodern::get_display_refresh_rate(); - if (curRate != lastRate) { - graphics_model_handle.DirtyVariable("display_refresh_rate"); - } - lastRate = curRate; + uint32_t curRate = ultramodern::get_display_refresh_rate(); + if (curRate != lastRate) { + graphics_model_handle.DirtyVariable("display_refresh_rate"); + } + lastRate = curRate; } DebugContext debug_context; class ConfigMenu : public recompui::MenuController { public: - ConfigMenu() { + ConfigMenu() { - } - ~ConfigMenu() override { + } + ~ConfigMenu() override { - } - Rml::ElementDocument* load_document(Rml::Context* context) override { + } + Rml::ElementDocument* load_document(Rml::Context* context) override { return context->LoadDocument("assets/config_menu.rml"); - } - void register_events(recompui::UiEventListenerInstancer& listener) override { - recompui::register_event(listener, "apply_options", - [](const std::string& param, Rml::Event& event) { - graphics_model_handle.DirtyVariable("options_changed"); - apply_graphics_config(); - }); - recompui::register_event(listener, "config_keydown", - [](const std::string& param, Rml::Event& event) { - if (!prompt_context.open && event.GetId() == Rml::EventId::Keydown) { - auto key = event.GetParameter("key_identifier", Rml::Input::KeyIdentifier::KI_UNKNOWN); - switch (key) { - case Rml::Input::KeyIdentifier::KI_ESCAPE: - close_config_menu(); - break; - case Rml::Input::KeyIdentifier::KI_F: - graphics_model_handle.DirtyVariable("options_changed"); - apply_graphics_config(); - break; - } - } - }); - // This needs to be separate from `close_config_menu` so it ensures that the event is only on the target - recompui::register_event(listener, "close_config_menu_backdrop", - [](const std::string& param, Rml::Event& event) { - if (event.GetPhase() == Rml::EventPhase::Target) { - close_config_menu(); - } - }); - recompui::register_event(listener, "close_config_menu", - [](const std::string& param, Rml::Event& event) { - close_config_menu(); - }); + } + void register_events(recompui::UiEventListenerInstancer& listener) override { + recompui::register_event(listener, "apply_options", + [](const std::string& param, Rml::Event& event) { + graphics_model_handle.DirtyVariable("options_changed"); + apply_graphics_config(); + }); + recompui::register_event(listener, "config_keydown", + [](const std::string& param, Rml::Event& event) { + if (!prompt_context.open && event.GetId() == Rml::EventId::Keydown) { + auto key = event.GetParameter("key_identifier", Rml::Input::KeyIdentifier::KI_UNKNOWN); + switch (key) { + case Rml::Input::KeyIdentifier::KI_ESCAPE: + close_config_menu(); + break; + case Rml::Input::KeyIdentifier::KI_F: + graphics_model_handle.DirtyVariable("options_changed"); + apply_graphics_config(); + break; + } + } + }); + // This needs to be separate from `close_config_menu` so it ensures that the event is only on the target + recompui::register_event(listener, "close_config_menu_backdrop", + [](const std::string& param, Rml::Event& event) { + if (event.GetPhase() == Rml::EventPhase::Target) { + close_config_menu(); + } + }); + recompui::register_event(listener, "close_config_menu", + [](const std::string& param, Rml::Event& event) { + close_config_menu(); + }); - recompui::register_event(listener, "open_quit_game_prompt", - [](const std::string& param, Rml::Event& event) { + recompui::register_event(listener, "open_quit_game_prompt", + [](const std::string& param, Rml::Event& event) { zelda64::open_quit_game_prompt(); - }); + }); - recompui::register_event(listener, "toggle_input_device", - [](const std::string& param, Rml::Event& event) { - cur_device = cur_device == recomp::InputDevice::Controller - ? recomp::InputDevice::Keyboard - : recomp::InputDevice::Controller; - controls_model_handle.DirtyVariable("input_device_is_keyboard"); - controls_model_handle.DirtyVariable("inputs"); - }); - - recompui::register_event(listener, "area_index_changed", - [](const std::string& param, Rml::Event& event) { - debug_context.area_index = event.GetParameter("value", 0); - debug_context.scene_index = 0; - debug_context.entrance_index = 0; - debug_context.update_warp_names(); - debug_context.model_handle.DirtyVariable("scene_index"); - debug_context.model_handle.DirtyVariable("entrance_index"); - debug_context.model_handle.DirtyVariable("scene_names"); - debug_context.model_handle.DirtyVariable("entrance_names"); - }); - - recompui::register_event(listener, "scene_index_changed", - [](const std::string& param, Rml::Event& event) { - debug_context.scene_index = event.GetParameter("value", 0); - debug_context.entrance_index = 0; - debug_context.update_warp_names(); - debug_context.model_handle.DirtyVariable("entrance_index"); - debug_context.model_handle.DirtyVariable("entrance_names"); - }); + recompui::register_event(listener, "toggle_input_device", + [](const std::string& param, Rml::Event& event) { + cur_device = cur_device == recomp::InputDevice::Controller + ? recomp::InputDevice::Keyboard + : recomp::InputDevice::Controller; + controls_model_handle.DirtyVariable("input_device_is_keyboard"); + controls_model_handle.DirtyVariable("inputs"); + }); + + recompui::register_event(listener, "area_index_changed", + [](const std::string& param, Rml::Event& event) { + debug_context.area_index = event.GetParameter("value", 0); + debug_context.scene_index = 0; + debug_context.entrance_index = 0; + debug_context.update_warp_names(); + debug_context.model_handle.DirtyVariable("scene_index"); + debug_context.model_handle.DirtyVariable("entrance_index"); + debug_context.model_handle.DirtyVariable("scene_names"); + debug_context.model_handle.DirtyVariable("entrance_names"); + }); + + recompui::register_event(listener, "scene_index_changed", + [](const std::string& param, Rml::Event& event) { + debug_context.scene_index = event.GetParameter("value", 0); + debug_context.entrance_index = 0; + debug_context.update_warp_names(); + debug_context.model_handle.DirtyVariable("entrance_index"); + debug_context.model_handle.DirtyVariable("entrance_names"); + }); - recompui::register_event(listener, "do_warp", - [](const std::string& param, Rml::Event& event) { + recompui::register_event(listener, "do_warp", + [](const std::string& param, Rml::Event& event) { zelda64::do_warp(debug_context.area_index, debug_context.scene_index, debug_context.entrance_index); - }); + }); - recompui::register_event(listener, "set_time", - [](const std::string& param, Rml::Event& event) { + recompui::register_event(listener, "set_time", + [](const std::string& param, Rml::Event& event) { zelda64::set_time(debug_context.set_time_day, debug_context.set_time_hour, debug_context.set_time_minute); - }); - } + }); + } - void bind_config_list_events(Rml::DataModelConstructor &constructor) { - constructor.BindEventCallback("set_cur_config_index", - [](Rml::DataModelHandle model_handle, Rml::Event& event, const Rml::VariantList& inputs) { - int option_index = inputs.at(0).Get(); - // watch for mouseout being overzealous during event bubbling, only clear if the event's attached element matches the current - if (option_index == -1 && event.GetType() == "mouseout" && event.GetCurrentElement() != event.GetTargetElement()) { - return; - } - focused_config_option_index = option_index; - model_handle.DirtyVariable("cur_config_index"); - }); + void bind_config_list_events(Rml::DataModelConstructor &constructor) { + constructor.BindEventCallback("set_cur_config_index", + [](Rml::DataModelHandle model_handle, Rml::Event& event, const Rml::VariantList& inputs) { + int option_index = inputs.at(0).Get(); + // watch for mouseout being overzealous during event bubbling, only clear if the event's attached element matches the current + if (option_index == -1 && event.GetType() == "mouseout" && event.GetCurrentElement() != event.GetTargetElement()) { + return; + } + focused_config_option_index = option_index; + model_handle.DirtyVariable("cur_config_index"); + }); - constructor.Bind("cur_config_index", &focused_config_option_index); - } + constructor.Bind("cur_config_index", &focused_config_option_index); + } - void make_graphics_bindings(Rml::Context* context) { - Rml::DataModelConstructor constructor = context->CreateDataModel("graphics_model"); - if (!constructor) { - throw std::runtime_error("Failed to make RmlUi data model for the graphics config menu"); - } + void make_graphics_bindings(Rml::Context* context) { + Rml::DataModelConstructor constructor = context->CreateDataModel("graphics_model"); + if (!constructor) { + throw std::runtime_error("Failed to make RmlUi data model for the graphics config menu"); + } - ultramodern::sleep_milliseconds(50); - new_options = ultramodern::renderer::get_graphics_config(); - bind_config_list_events(constructor); + ultramodern::sleep_milliseconds(50); + new_options = ultramodern::renderer::get_graphics_config(); + bind_config_list_events(constructor); - constructor.BindFunc("res_option", - [](Rml::Variant& out) { get_option(new_options.res_option, out); }, - [](const Rml::Variant& in) { - set_option(new_options.res_option, in); - graphics_model_handle.DirtyVariable("options_changed"); - graphics_model_handle.DirtyVariable("ds_info"); - graphics_model_handle.DirtyVariable("ds_option"); - } - ); - bind_option(constructor, "wm_option", &new_options.wm_option); - bind_option(constructor, "ar_option", &new_options.ar_option); - bind_option(constructor, "hr_option", &new_options.hr_option); - bind_option(constructor, "msaa_option", &new_options.msaa_option); - bind_option(constructor, "rr_option", &new_options.rr_option); - constructor.BindFunc("rr_manual_value", - [](Rml::Variant& out) { - out = new_options.rr_manual_value; - }, - [](const Rml::Variant& in) { - new_options.rr_manual_value = in.Get(); - graphics_model_handle.DirtyVariable("options_changed"); - }); - constructor.BindFunc("ds_option", - [](Rml::Variant& out) { - if (new_options.res_option == ultramodern::renderer::Resolution::Auto) { - out = 1; - } else { - out = new_options.ds_option; - } - }, - [](const Rml::Variant& in) { - new_options.ds_option = in.Get(); - graphics_model_handle.DirtyVariable("options_changed"); - graphics_model_handle.DirtyVariable("ds_info"); - }); + constructor.BindFunc("res_option", + [](Rml::Variant& out) { get_option(new_options.res_option, out); }, + [](const Rml::Variant& in) { + set_option(new_options.res_option, in); + graphics_model_handle.DirtyVariable("options_changed"); + graphics_model_handle.DirtyVariable("ds_info"); + graphics_model_handle.DirtyVariable("ds_option"); + } + ); + bind_option(constructor, "wm_option", &new_options.wm_option); + bind_option(constructor, "ar_option", &new_options.ar_option); + bind_option(constructor, "hr_option", &new_options.hr_option); + bind_option(constructor, "msaa_option", &new_options.msaa_option); + bind_option(constructor, "rr_option", &new_options.rr_option); + constructor.BindFunc("rr_manual_value", + [](Rml::Variant& out) { + out = new_options.rr_manual_value; + }, + [](const Rml::Variant& in) { + new_options.rr_manual_value = in.Get(); + graphics_model_handle.DirtyVariable("options_changed"); + }); + constructor.BindFunc("ds_option", + [](Rml::Variant& out) { + if (new_options.res_option == ultramodern::renderer::Resolution::Auto) { + out = 1; + } else { + out = new_options.ds_option; + } + }, + [](const Rml::Variant& in) { + new_options.ds_option = in.Get(); + graphics_model_handle.DirtyVariable("options_changed"); + graphics_model_handle.DirtyVariable("ds_info"); + }); - constructor.BindFunc("display_refresh_rate", - [](Rml::Variant& out) { - out = ultramodern::get_display_refresh_rate(); - }); + constructor.BindFunc("display_refresh_rate", + [](Rml::Variant& out) { + out = ultramodern::get_display_refresh_rate(); + }); - constructor.BindFunc("options_changed", - [](Rml::Variant& out) { - out = (ultramodern::renderer::get_graphics_config() != new_options); - }); - constructor.BindFunc("ds_info", - [](Rml::Variant& out) { - switch (new_options.res_option) { - default: - case ultramodern::renderer::Resolution::Auto: - out = "Downsampling is not available at auto resolution"; - return; - case ultramodern::renderer::Resolution::Original: - if (new_options.ds_option == 2) { - out = "Rendered in 480p and scaled to 240p"; - } else if (new_options.ds_option == 4) { - out = "Rendered in 960p and scaled to 240p"; - } - return; - case ultramodern::renderer::Resolution::Original2x: - if (new_options.ds_option == 2) { - out = "Rendered in 960p and scaled to 480p"; - } else if (new_options.ds_option == 4) { - out = "Rendered in 4K and scaled to 480p"; - } - return; - } - out = ""; - }); - - constructor.BindFunc("gfx_help__apply", [](Rml::Variant& out) { - if (cont_active) { - out = \ - (recomp::get_input_binding(recomp::GameInput::APPLY_MENU, 0, recomp::InputDevice::Controller).to_string() != "" ? - " " + recomp::get_input_binding(recomp::GameInput::APPLY_MENU, 0, recomp::InputDevice::Controller).to_string() : - "" - ) + \ - (recomp::get_input_binding(recomp::GameInput::APPLY_MENU, 1, recomp::InputDevice::Controller).to_string() != "" ? - " " + recomp::get_input_binding(recomp::GameInput::APPLY_MENU, 1, recomp::InputDevice::Controller).to_string() : - "" - ); - } else { - out = " " PF_KEYBOARD_F; - } - }); + constructor.BindFunc("options_changed", + [](Rml::Variant& out) { + out = (ultramodern::renderer::get_graphics_config() != new_options); + }); + constructor.BindFunc("ds_info", + [](Rml::Variant& out) { + switch (new_options.res_option) { + default: + case ultramodern::renderer::Resolution::Auto: + out = "Downsampling is not available at auto resolution"; + return; + case ultramodern::renderer::Resolution::Original: + if (new_options.ds_option == 2) { + out = "Rendered in 480p and scaled to 240p"; + } else if (new_options.ds_option == 4) { + out = "Rendered in 960p and scaled to 240p"; + } + return; + case ultramodern::renderer::Resolution::Original2x: + if (new_options.ds_option == 2) { + out = "Rendered in 960p and scaled to 480p"; + } else if (new_options.ds_option == 4) { + out = "Rendered in 4K and scaled to 480p"; + } + return; + } + out = ""; + }); + + constructor.BindFunc("gfx_help__apply", [](Rml::Variant& out) { + if (cont_active) { + out = \ + (recomp::get_input_binding(recomp::GameInput::APPLY_MENU, 0, recomp::InputDevice::Controller).to_string() != "" ? + " " + recomp::get_input_binding(recomp::GameInput::APPLY_MENU, 0, recomp::InputDevice::Controller).to_string() : + "" + ) + \ + (recomp::get_input_binding(recomp::GameInput::APPLY_MENU, 1, recomp::InputDevice::Controller).to_string() != "" ? + " " + recomp::get_input_binding(recomp::GameInput::APPLY_MENU, 1, recomp::InputDevice::Controller).to_string() : + "" + ); + } else { + out = " " PF_KEYBOARD_F; + } + }); - constructor.Bind("msaa2x_supported", &msaa2x_supported); - constructor.Bind("msaa4x_supported", &msaa4x_supported); - constructor.Bind("msaa8x_supported", &msaa8x_supported); - constructor.Bind("sample_positions_supported", &sample_positions_supported); + constructor.Bind("msaa2x_supported", &msaa2x_supported); + constructor.Bind("msaa4x_supported", &msaa4x_supported); + constructor.Bind("msaa8x_supported", &msaa8x_supported); + constructor.Bind("sample_positions_supported", &sample_positions_supported); - graphics_model_handle = constructor.GetModelHandle(); - } + graphics_model_handle = constructor.GetModelHandle(); + } - void make_controls_bindings(Rml::Context* context) { - Rml::DataModelConstructor constructor = context->CreateDataModel("controls_model"); - if (!constructor) { - throw std::runtime_error("Failed to make RmlUi data model for the controls config menu"); - } + void make_controls_bindings(Rml::Context* context) { + Rml::DataModelConstructor constructor = context->CreateDataModel("controls_model"); + if (!constructor) { + throw std::runtime_error("Failed to make RmlUi data model for the controls config menu"); + } - constructor.BindFunc("input_count", [](Rml::Variant& out) { out = recomp::get_num_inputs(); } ); - constructor.BindFunc("input_device_is_keyboard", [](Rml::Variant& out) { out = cur_device == recomp::InputDevice::Keyboard; } ); + constructor.BindFunc("input_count", [](Rml::Variant& out) { out = recomp::get_num_inputs(); } ); + constructor.BindFunc("input_device_is_keyboard", [](Rml::Variant& out) { out = cur_device == recomp::InputDevice::Keyboard; } ); - constructor.RegisterTransformFunc("get_input_name", [](const Rml::VariantList& inputs) { - return Rml::Variant{recomp::get_input_name(static_cast(inputs.at(0).Get()))}; - }); + constructor.RegisterTransformFunc("get_input_name", [](const Rml::VariantList& inputs) { + return Rml::Variant{recomp::get_input_name(static_cast(inputs.at(0).Get()))}; + }); - constructor.RegisterTransformFunc("get_input_enum_name", [](const Rml::VariantList& inputs) { - return Rml::Variant{recomp::get_input_enum_name(static_cast(inputs.at(0).Get()))}; - }); + constructor.RegisterTransformFunc("get_input_enum_name", [](const Rml::VariantList& inputs) { + return Rml::Variant{recomp::get_input_enum_name(static_cast(inputs.at(0).Get()))}; + }); - constructor.BindEventCallback("set_input_binding", - [](Rml::DataModelHandle model_handle, Rml::Event& event, const Rml::VariantList& inputs) { - scanned_input_index = inputs.at(0).Get(); - scanned_binding_index = inputs.at(1).Get(); + constructor.BindEventCallback("set_input_binding", + [](Rml::DataModelHandle model_handle, Rml::Event& event, const Rml::VariantList& inputs) { + scanned_input_index = inputs.at(0).Get(); + scanned_binding_index = inputs.at(1).Get(); recomp::start_scanning_input(cur_device); - model_handle.DirtyVariable("active_binding_input"); - model_handle.DirtyVariable("active_binding_slot"); - }); + model_handle.DirtyVariable("active_binding_input"); + model_handle.DirtyVariable("active_binding_slot"); + }); - constructor.BindEventCallback("reset_input_bindings_to_defaults", - [](Rml::DataModelHandle model_handle, Rml::Event& event, const Rml::VariantList& inputs) { - if (cur_device == recomp::InputDevice::Controller) { + constructor.BindEventCallback("reset_input_bindings_to_defaults", + [](Rml::DataModelHandle model_handle, Rml::Event& event, const Rml::VariantList& inputs) { + if (cur_device == recomp::InputDevice::Controller) { zelda64::reset_cont_input_bindings(); - } else { + } else { zelda64::reset_kb_input_bindings(); - } - model_handle.DirtyAllVariables(); - nav_help_model_handle.DirtyVariable("nav_help__accept"); - nav_help_model_handle.DirtyVariable("nav_help__exit"); - graphics_model_handle.DirtyVariable("gfx_help__apply"); - }); + } + model_handle.DirtyAllVariables(); + nav_help_model_handle.DirtyVariable("nav_help__accept"); + nav_help_model_handle.DirtyVariable("nav_help__exit"); + graphics_model_handle.DirtyVariable("gfx_help__apply"); + }); - constructor.BindEventCallback("clear_input_bindings", - [](Rml::DataModelHandle model_handle, Rml::Event& event, const Rml::VariantList& inputs) { + constructor.BindEventCallback("clear_input_bindings", + [](Rml::DataModelHandle model_handle, Rml::Event& event, const Rml::VariantList& inputs) { recomp::GameInput input = static_cast(inputs.at(0).Get()); - for (size_t binding_index = 0; binding_index < recomp::bindings_per_input; binding_index++) { + for (size_t binding_index = 0; binding_index < recomp::bindings_per_input; binding_index++) { recomp::set_input_binding(input, binding_index, cur_device, recomp::InputField{}); - } - model_handle.DirtyVariable("inputs"); - graphics_model_handle.DirtyVariable("gfx_help__apply"); - }); + } + model_handle.DirtyVariable("inputs"); + graphics_model_handle.DirtyVariable("gfx_help__apply"); + }); - constructor.BindEventCallback("reset_single_input_binding_to_default", - [](Rml::DataModelHandle model_handle, Rml::Event& event, const Rml::VariantList& inputs) { + constructor.BindEventCallback("reset_single_input_binding_to_default", + [](Rml::DataModelHandle model_handle, Rml::Event& event, const Rml::VariantList& inputs) { recomp::GameInput input = static_cast(inputs.at(0).Get()); - zelda64::reset_single_input_binding(cur_device, input); - model_handle.DirtyVariable("inputs"); - nav_help_model_handle.DirtyVariable("nav_help__accept"); - nav_help_model_handle.DirtyVariable("nav_help__exit"); - }); + zelda64::reset_single_input_binding(cur_device, input); + model_handle.DirtyVariable("inputs"); + nav_help_model_handle.DirtyVariable("nav_help__accept"); + nav_help_model_handle.DirtyVariable("nav_help__exit"); + }); - constructor.BindEventCallback("set_input_row_focus", - [](Rml::DataModelHandle model_handle, Rml::Event& event, const Rml::VariantList& inputs) { - int input_index = inputs.at(0).Get(); - // watch for mouseout being overzealous during event bubbling, only clear if the event's attached element matches the current - if (input_index == -1 && event.GetType() == "mouseout" && event.GetCurrentElement() != event.GetTargetElement()) { - return; - } - focused_input_index = input_index; - model_handle.DirtyVariable("cur_input_row"); - }); + constructor.BindEventCallback("set_input_row_focus", + [](Rml::DataModelHandle model_handle, Rml::Event& event, const Rml::VariantList& inputs) { + int input_index = inputs.at(0).Get(); + // watch for mouseout being overzealous during event bubbling, only clear if the event's attached element matches the current + if (input_index == -1 && event.GetType() == "mouseout" && event.GetCurrentElement() != event.GetTargetElement()) { + return; + } + focused_input_index = input_index; + model_handle.DirtyVariable("cur_input_row"); + }); - // Rml variable definition for an individual InputField. - struct InputFieldVariableDefinition : public Rml::VariableDefinition { - InputFieldVariableDefinition() : Rml::VariableDefinition(Rml::DataVariableType::Scalar) {} + // Rml variable definition for an individual InputField. + struct InputFieldVariableDefinition : public Rml::VariableDefinition { + InputFieldVariableDefinition() : Rml::VariableDefinition(Rml::DataVariableType::Scalar) {} - virtual bool Get(void* ptr, Rml::Variant& variant) override { variant = reinterpret_cast(ptr)->to_string(); return true; } - virtual bool Set(void* ptr, const Rml::Variant& variant) override { return false; } - }; - // Static instance of the InputField variable definition to have a pointer to return to RmlUi. - static InputFieldVariableDefinition input_field_definition_instance{}; + virtual bool Get(void* ptr, Rml::Variant& variant) override { variant = reinterpret_cast(ptr)->to_string(); return true; } + virtual bool Set(void* ptr, const Rml::Variant& variant) override { return false; } + }; + // Static instance of the InputField variable definition to have a pointer to return to RmlUi. + static InputFieldVariableDefinition input_field_definition_instance{}; - // Rml variable definition for an array of InputField values (e.g. all the bindings for a single input). - struct BindingContainerVariableDefinition : public Rml::VariableDefinition { - BindingContainerVariableDefinition() : Rml::VariableDefinition(Rml::DataVariableType::Array) {} + // Rml variable definition for an array of InputField values (e.g. all the bindings for a single input). + struct BindingContainerVariableDefinition : public Rml::VariableDefinition { + BindingContainerVariableDefinition() : Rml::VariableDefinition(Rml::DataVariableType::Array) {} - virtual bool Get(void* ptr, Rml::Variant& variant) override { return false; } - virtual bool Set(void* ptr, const Rml::Variant& variant) override { return false; } + virtual bool Get(void* ptr, Rml::Variant& variant) override { return false; } + virtual bool Set(void* ptr, const Rml::Variant& variant) override { return false; } - virtual int Size(void* ptr) override { return recomp::bindings_per_input; } - virtual Rml::DataVariable Child(void* ptr, const Rml::DataAddressEntry& address) override { + virtual int Size(void* ptr) override { return recomp::bindings_per_input; } + virtual Rml::DataVariable Child(void* ptr, const Rml::DataAddressEntry& address) override { recomp::GameInput input = static_cast((uintptr_t)ptr); - return Rml::DataVariable{&input_field_definition_instance, &recomp::get_input_binding(input, address.index, cur_device)}; - } - }; - // Static instance of the InputField array variable definition to have a fixed pointer to return to RmlUi. - static BindingContainerVariableDefinition binding_container_var_instance{}; + return Rml::DataVariable{&input_field_definition_instance, &recomp::get_input_binding(input, address.index, cur_device)}; + } + }; + // Static instance of the InputField array variable definition to have a fixed pointer to return to RmlUi. + static BindingContainerVariableDefinition binding_container_var_instance{}; - // Rml variable definition for an array of an array of InputField values (e.g. all the bindings for all inputs). - struct BindingArrayContainerVariableDefinition : public Rml::VariableDefinition { - BindingArrayContainerVariableDefinition() : Rml::VariableDefinition(Rml::DataVariableType::Array) {} + // Rml variable definition for an array of an array of InputField values (e.g. all the bindings for all inputs). + struct BindingArrayContainerVariableDefinition : public Rml::VariableDefinition { + BindingArrayContainerVariableDefinition() : Rml::VariableDefinition(Rml::DataVariableType::Array) {} - virtual bool Get(void* ptr, Rml::Variant& variant) override { return false; } - virtual bool Set(void* ptr, const Rml::Variant& variant) override { return false; } + virtual bool Get(void* ptr, Rml::Variant& variant) override { return false; } + virtual bool Set(void* ptr, const Rml::Variant& variant) override { return false; } - virtual int Size(void* ptr) override { return recomp::get_num_inputs(); } - virtual Rml::DataVariable Child(void* ptr, const Rml::DataAddressEntry& address) override { - // Encode the input index as the pointer to avoid needing to do any allocations. - return Rml::DataVariable(&binding_container_var_instance, (void*)(uintptr_t)address.index); - } - }; + virtual int Size(void* ptr) override { return recomp::get_num_inputs(); } + virtual Rml::DataVariable Child(void* ptr, const Rml::DataAddressEntry& address) override { + // Encode the input index as the pointer to avoid needing to do any allocations. + return Rml::DataVariable(&binding_container_var_instance, (void*)(uintptr_t)address.index); + } + }; - // Static instance of the BindingArrayContainerVariableDefinition variable definition to have a fixed pointer to return to RmlUi. - static BindingArrayContainerVariableDefinition binding_array_var_instance{}; + // Static instance of the BindingArrayContainerVariableDefinition variable definition to have a fixed pointer to return to RmlUi. + static BindingArrayContainerVariableDefinition binding_array_var_instance{}; - struct InputContainerVariableDefinition : public Rml::VariableDefinition { - InputContainerVariableDefinition() : Rml::VariableDefinition(Rml::DataVariableType::Struct) {} + struct InputContainerVariableDefinition : public Rml::VariableDefinition { + InputContainerVariableDefinition() : Rml::VariableDefinition(Rml::DataVariableType::Struct) {} - virtual bool Get(void* ptr, Rml::Variant& variant) override { return true; } - virtual bool Set(void* ptr, const Rml::Variant& variant) override { return false; } + virtual bool Get(void* ptr, Rml::Variant& variant) override { return true; } + virtual bool Set(void* ptr, const Rml::Variant& variant) override { return false; } - virtual int Size(void* ptr) override { return recomp::get_num_inputs(); } - virtual Rml::DataVariable Child(void* ptr, const Rml::DataAddressEntry& address) override { - if (address.name == "array") { - return Rml::DataVariable(&binding_array_var_instance, nullptr); - } - else { + virtual int Size(void* ptr) override { return recomp::get_num_inputs(); } + virtual Rml::DataVariable Child(void* ptr, const Rml::DataAddressEntry& address) override { + if (address.name == "array") { + return Rml::DataVariable(&binding_array_var_instance, nullptr); + } + else { recomp::GameInput input = recomp::get_input_from_enum_name(address.name); - if (input != recomp::GameInput::COUNT) { - return Rml::DataVariable(&binding_container_var_instance, (void*)(uintptr_t)input); - } - } - return Rml::DataVariable{}; - } - }; + if (input != recomp::GameInput::COUNT) { + return Rml::DataVariable(&binding_container_var_instance, (void*)(uintptr_t)input); + } + } + return Rml::DataVariable{}; + } + }; - // Dummy type to associate with the variable definition. - struct InputContainer {}; - constructor.RegisterCustomDataVariableDefinition(Rml::MakeUnique()); + // Dummy type to associate with the variable definition. + struct InputContainer {}; + constructor.RegisterCustomDataVariableDefinition(Rml::MakeUnique()); - // Dummy instance of the dummy type to bind to the variable. - static InputContainer dummy_container; - constructor.Bind("inputs", &dummy_container); + // Dummy instance of the dummy type to bind to the variable. + static InputContainer dummy_container; + constructor.Bind("inputs", &dummy_container); - constructor.BindFunc("cur_input_row", [](Rml::Variant& out) { - if (focused_input_index == -1) { - out = "NONE"; - } - else { - out = recomp::get_input_enum_name(static_cast(focused_input_index)); - } - }); + constructor.BindFunc("cur_input_row", [](Rml::Variant& out) { + if (focused_input_index == -1) { + out = "NONE"; + } + else { + out = recomp::get_input_enum_name(static_cast(focused_input_index)); + } + }); - constructor.BindFunc("active_binding_input", [](Rml::Variant& out) { - if (scanned_input_index == -1) { - out = "NONE"; - } - else { - out = recomp::get_input_enum_name(static_cast(scanned_input_index)); - } - }); + constructor.BindFunc("active_binding_input", [](Rml::Variant& out) { + if (scanned_input_index == -1) { + out = "NONE"; + } + else { + out = recomp::get_input_enum_name(static_cast(scanned_input_index)); + } + }); - constructor.Bind("active_binding_slot", &scanned_binding_index); + constructor.Bind("active_binding_slot", &scanned_binding_index); - controls_model_handle = constructor.GetModelHandle(); - } + controls_model_handle = constructor.GetModelHandle(); + } - void make_nav_help_bindings(Rml::Context* context) { - Rml::DataModelConstructor constructor = context->CreateDataModel("nav_help_model"); - if (!constructor) { - throw std::runtime_error("Failed to make RmlUi data model for nav help"); - } + void make_nav_help_bindings(Rml::Context* context) { + Rml::DataModelConstructor constructor = context->CreateDataModel("nav_help_model"); + if (!constructor) { + throw std::runtime_error("Failed to make RmlUi data model for nav help"); + } - constructor.BindFunc("nav_help__navigate", [](Rml::Variant& out) { - if (cont_active) { - out = PF_DPAD; - } else { - out = PF_KEYBOARD_ARROWS PF_KEYBOARD_TAB; - } - }); + constructor.BindFunc("nav_help__navigate", [](Rml::Variant& out) { + if (cont_active) { + out = PF_DPAD; + } else { + out = PF_KEYBOARD_ARROWS PF_KEYBOARD_TAB; + } + }); - constructor.BindFunc("nav_help__accept", [](Rml::Variant& out) { - if (cont_active) { - out = \ - recomp::get_input_binding(recomp::GameInput::ACCEPT_MENU, 0, recomp::InputDevice::Controller).to_string() + \ - recomp::get_input_binding(recomp::GameInput::ACCEPT_MENU, 1, recomp::InputDevice::Controller).to_string(); - } else { - out = PF_KEYBOARD_ENTER; - } - }); + constructor.BindFunc("nav_help__accept", [](Rml::Variant& out) { + if (cont_active) { + out = \ + recomp::get_input_binding(recomp::GameInput::ACCEPT_MENU, 0, recomp::InputDevice::Controller).to_string() + \ + recomp::get_input_binding(recomp::GameInput::ACCEPT_MENU, 1, recomp::InputDevice::Controller).to_string(); + } else { + out = PF_KEYBOARD_ENTER; + } + }); - constructor.BindFunc("nav_help__exit", [](Rml::Variant& out) { - if (cont_active) { - out = \ - recomp::get_input_binding(recomp::GameInput::TOGGLE_MENU, 0, recomp::InputDevice::Controller).to_string() + \ - recomp::get_input_binding(recomp::GameInput::TOGGLE_MENU, 1, recomp::InputDevice::Controller).to_string(); - } else { - out = PF_KEYBOARD_ESCAPE; - } - }); + constructor.BindFunc("nav_help__exit", [](Rml::Variant& out) { + if (cont_active) { + out = \ + recomp::get_input_binding(recomp::GameInput::TOGGLE_MENU, 0, recomp::InputDevice::Controller).to_string() + \ + recomp::get_input_binding(recomp::GameInput::TOGGLE_MENU, 1, recomp::InputDevice::Controller).to_string(); + } else { + out = PF_KEYBOARD_ESCAPE; + } + }); - nav_help_model_handle = constructor.GetModelHandle(); - } + nav_help_model_handle = constructor.GetModelHandle(); + } - void make_general_bindings(Rml::Context* context) { - Rml::DataModelConstructor constructor = context->CreateDataModel("general_model"); - if (!constructor) { - throw std::runtime_error("Failed to make RmlUi data model for the control options menu"); - } + void make_general_bindings(Rml::Context* context) { + Rml::DataModelConstructor constructor = context->CreateDataModel("general_model"); + if (!constructor) { + throw std::runtime_error("Failed to make RmlUi data model for the control options menu"); + } - bind_config_list_events(constructor); - - constructor.Bind("rumble_strength", &control_options_context.rumble_strength); - constructor.Bind("gyro_sensitivity", &control_options_context.gyro_sensitivity); - constructor.Bind("mouse_sensitivity", &control_options_context.mouse_sensitivity); - constructor.Bind("joystick_deadzone", &control_options_context.joystick_deadzone); - bind_option(constructor, "targeting_mode", &control_options_context.targeting_mode); - bind_option(constructor, "background_input_mode", &control_options_context.background_input_mode); - bind_option(constructor, "autosave_mode", &control_options_context.autosave_mode); - bind_option(constructor, "camera_invert_mode", &control_options_context.camera_invert_mode); - bind_option(constructor, "analog_cam_mode", &control_options_context.analog_cam_mode); - bind_option(constructor, "analog_camera_invert_mode", &control_options_context.analog_camera_invert_mode); + bind_config_list_events(constructor); + + constructor.Bind("rumble_strength", &control_options_context.rumble_strength); + constructor.Bind("gyro_sensitivity", &control_options_context.gyro_sensitivity); + constructor.Bind("mouse_sensitivity", &control_options_context.mouse_sensitivity); + constructor.Bind("joystick_deadzone", &control_options_context.joystick_deadzone); + bind_option(constructor, "targeting_mode", &control_options_context.targeting_mode); + bind_option(constructor, "background_input_mode", &control_options_context.background_input_mode); + bind_option(constructor, "autosave_mode", &control_options_context.autosave_mode); + bind_option(constructor, "camera_invert_mode", &control_options_context.camera_invert_mode); + bind_option(constructor, "analog_cam_mode", &control_options_context.analog_cam_mode); + bind_option(constructor, "analog_camera_invert_mode", &control_options_context.analog_camera_invert_mode); - general_model_handle = constructor.GetModelHandle(); - } - - void make_sound_options_bindings(Rml::Context* context) { - Rml::DataModelConstructor constructor = context->CreateDataModel("sound_options_model"); - if (!constructor) { - throw std::runtime_error("Failed to make RmlUi data model for the sound options menu"); - } + general_model_handle = constructor.GetModelHandle(); + } + + void make_sound_options_bindings(Rml::Context* context) { + Rml::DataModelConstructor constructor = context->CreateDataModel("sound_options_model"); + if (!constructor) { + throw std::runtime_error("Failed to make RmlUi data model for the sound options menu"); + } - bind_config_list_events(constructor); - - sound_options_model_handle = constructor.GetModelHandle(); + bind_config_list_events(constructor); + + sound_options_model_handle = constructor.GetModelHandle(); - bind_atomic(constructor, sound_options_model_handle, "main_volume", &sound_options_context.main_volume); - bind_atomic(constructor, sound_options_model_handle, "bgm_volume", &sound_options_context.bgm_volume); - bind_atomic(constructor, sound_options_model_handle, "low_health_beeps_enabled", &sound_options_context.low_health_beeps_enabled); - } + bind_atomic(constructor, sound_options_model_handle, "main_volume", &sound_options_context.main_volume); + bind_atomic(constructor, sound_options_model_handle, "bgm_volume", &sound_options_context.bgm_volume); + bind_atomic(constructor, sound_options_model_handle, "low_health_beeps_enabled", &sound_options_context.low_health_beeps_enabled); + } - void make_debug_bindings(Rml::Context* context) { - Rml::DataModelConstructor constructor = context->CreateDataModel("debug_model"); - if (!constructor) { - throw std::runtime_error("Failed to make RmlUi data model for the debug menu"); - } + void make_debug_bindings(Rml::Context* context) { + Rml::DataModelConstructor constructor = context->CreateDataModel("debug_model"); + if (!constructor) { + throw std::runtime_error("Failed to make RmlUi data model for the debug menu"); + } - bind_config_list_events(constructor); + bind_config_list_events(constructor); - // Bind the debug mode enabled flag. - constructor.Bind("debug_enabled", &debug_context.debug_enabled); - - // Register the array type for string vectors. - constructor.RegisterArray>(); - - // Bind the warp parameter indices - constructor.Bind("area_index", &debug_context.area_index); - constructor.Bind("scene_index", &debug_context.scene_index); - constructor.Bind("entrance_index", &debug_context.entrance_index); + // Bind the debug mode enabled flag. + constructor.Bind("debug_enabled", &debug_context.debug_enabled); + + // Register the array type for string vectors. + constructor.RegisterArray>(); + + // Bind the warp parameter indices + constructor.Bind("area_index", &debug_context.area_index); + constructor.Bind("scene_index", &debug_context.scene_index); + constructor.Bind("entrance_index", &debug_context.entrance_index); - // Bind the vectors for warp names - constructor.Bind("area_names", &debug_context.area_names); - constructor.Bind("scene_names", &debug_context.scene_names); - constructor.Bind("entrance_names", &debug_context.entrance_names); + // Bind the vectors for warp names + constructor.Bind("area_names", &debug_context.area_names); + constructor.Bind("scene_names", &debug_context.scene_names); + constructor.Bind("entrance_names", &debug_context.entrance_names); - constructor.Bind("debug_time_day", &debug_context.set_time_day); - constructor.Bind("debug_time_hour", &debug_context.set_time_hour); - constructor.Bind("debug_time_minute", &debug_context.set_time_minute); + constructor.Bind("debug_time_day", &debug_context.set_time_day); + constructor.Bind("debug_time_hour", &debug_context.set_time_hour); + constructor.Bind("debug_time_minute", &debug_context.set_time_minute); - 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"); - } + 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); + // 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", &recompui::PromptContext::on_click, &prompt_context); + constructor.BindEventCallback("prompt__on_click", &recompui::PromptContext::on_click, &prompt_context); - prompt_context.model_handle = constructor.GetModelHandle(); - } + prompt_context.model_handle = constructor.GetModelHandle(); + } - void make_bindings(Rml::Context* context) override { - // initially set cont state for ui help - recomp::config_menu_set_cont_or_kb(recompui::get_cont_active()); - make_nav_help_bindings(context); - make_general_bindings(context); - make_controls_bindings(context); - make_graphics_bindings(context); - make_sound_options_bindings(context); - make_debug_bindings(context); - make_prompt_bindings(context); - } + void make_bindings(Rml::Context* context) override { + // initially set cont state for ui help + recomp::config_menu_set_cont_or_kb(recompui::get_cont_active()); + make_nav_help_bindings(context); + make_general_bindings(context); + make_controls_bindings(context); + make_graphics_bindings(context); + make_sound_options_bindings(context); + make_debug_bindings(context); + make_prompt_bindings(context); + } }; std::unique_ptr recompui::create_config_menu() { - return std::make_unique(); + return std::make_unique(); } bool zelda64::get_debug_mode_enabled() { - return debug_context.debug_enabled; + return debug_context.debug_enabled; } void zelda64::set_debug_mode_enabled(bool enabled) { - debug_context.debug_enabled = enabled; - if (debug_context.model_handle) { - debug_context.model_handle.DirtyVariable("debug_enabled"); - } + debug_context.debug_enabled = enabled; + if (debug_context.model_handle) { + debug_context.model_handle.DirtyVariable("debug_enabled"); + } } void recompui::update_supported_options() { - msaa2x_supported = zelda64::renderer::RT64MaxMSAA() >= RT64::UserConfiguration::Antialiasing::MSAA2X; - msaa4x_supported = zelda64::renderer::RT64MaxMSAA() >= RT64::UserConfiguration::Antialiasing::MSAA4X; - msaa8x_supported = zelda64::renderer::RT64MaxMSAA() >= RT64::UserConfiguration::Antialiasing::MSAA8X; - sample_positions_supported = zelda64::renderer::RT64SamplePositionsSupported(); - - new_options = ultramodern::renderer::get_graphics_config(); + msaa2x_supported = zelda64::renderer::RT64MaxMSAA() >= RT64::UserConfiguration::Antialiasing::MSAA2X; + msaa4x_supported = zelda64::renderer::RT64MaxMSAA() >= RT64::UserConfiguration::Antialiasing::MSAA4X; + msaa8x_supported = zelda64::renderer::RT64MaxMSAA() >= RT64::UserConfiguration::Antialiasing::MSAA8X; + sample_positions_supported = zelda64::renderer::RT64SamplePositionsSupported(); + + new_options = ultramodern::renderer::get_graphics_config(); - graphics_model_handle.DirtyAllVariables(); + graphics_model_handle.DirtyAllVariables(); } void recompui::toggle_fullscreen() { - new_options.wm_option = (new_options.wm_option == ultramodern::renderer::WindowMode::Windowed) ? ultramodern::renderer::WindowMode::Fullscreen : ultramodern::renderer::WindowMode::Windowed; - apply_graphics_config(); - graphics_model_handle.DirtyVariable("wm_option"); + new_options.wm_option = (new_options.wm_option == ultramodern::renderer::WindowMode::Windowed) ? ultramodern::renderer::WindowMode::Fullscreen : ultramodern::renderer::WindowMode::Windowed; + apply_graphics_config(); + graphics_model_handle.DirtyVariable("wm_option"); } diff --git a/src/ui/ui_launcher.cpp b/src/ui/ui_launcher.cpp index 3733c69..2d03a72 100644 --- a/src/ui/ui_launcher.cpp +++ b/src/ui/ui_launcher.cpp @@ -14,16 +14,16 @@ bool mm_rom_valid = false; extern std::vector supported_games; void select_rom() { - nfdnchar_t* native_path = nullptr; - nfdresult_t result = NFD_OpenDialogN(&native_path, nullptr, 0, nullptr); + nfdnchar_t* native_path = nullptr; + nfdresult_t result = NFD_OpenDialogN(&native_path, nullptr, 0, nullptr); - if (result == NFD_OKAY) { - std::filesystem::path path{native_path}; + if (result == NFD_OKAY) { + std::filesystem::path path{native_path}; - NFD_FreePathN(native_path); - native_path = nullptr; + NFD_FreePathN(native_path); + native_path = nullptr; - recomp::RomValidationError rom_error = recomp::select_rom(path, supported_games[0].game_id); + recomp::RomValidationError rom_error = recomp::select_rom(path, supported_games[0].game_id); switch (rom_error) { case recomp::RomValidationError::Good: mm_rom_valid = true; @@ -55,44 +55,44 @@ void select_rom() { class LauncherMenu : public recompui::MenuController { public: LauncherMenu() { - mm_rom_valid = recomp::is_rom_valid(supported_games[0].game_id); + mm_rom_valid = recomp::is_rom_valid(supported_games[0].game_id); } - ~LauncherMenu() override { + ~LauncherMenu() override { - } - Rml::ElementDocument* load_document(Rml::Context* context) override { + } + Rml::ElementDocument* load_document(Rml::Context* context) override { return context->LoadDocument("assets/launcher.rml"); - } - void register_events(recompui::UiEventListenerInstancer& listener) override { - recompui::register_event(listener, "select_rom", - [](const std::string& param, Rml::Event& event) { - select_rom(); - } - ); - recompui::register_event(listener, "rom_selected", - [](const std::string& param, Rml::Event& event) { - mm_rom_valid = true; - model_handle.DirtyVariable("mm_rom_valid"); - } - ); - recompui::register_event(listener, "start_game", - [](const std::string& param, Rml::Event& event) { - recomp::start_game(supported_games[0].game_id); - recompui::set_current_menu(recompui::Menu::None); - } - ); + } + void register_events(recompui::UiEventListenerInstancer& listener) override { + recompui::register_event(listener, "select_rom", + [](const std::string& param, Rml::Event& event) { + select_rom(); + } + ); + recompui::register_event(listener, "rom_selected", + [](const std::string& param, Rml::Event& event) { + mm_rom_valid = true; + model_handle.DirtyVariable("mm_rom_valid"); + } + ); + recompui::register_event(listener, "start_game", + [](const std::string& param, Rml::Event& event) { + recomp::start_game(supported_games[0].game_id); + recompui::set_current_menu(recompui::Menu::None); + } + ); recompui::register_event(listener, "open_controls", - [](const std::string& param, Rml::Event& event) { + [](const std::string& param, Rml::Event& event) { recompui::set_current_menu(recompui::Menu::Config); - recompui::set_config_submenu(recompui::ConfigSubmenu::Controls); - } - ); + recompui::set_config_submenu(recompui::ConfigSubmenu::Controls); + } + ); recompui::register_event(listener, "open_settings", - [](const std::string& param, Rml::Event& event) { + [](const std::string& param, Rml::Event& event) { recompui::set_current_menu(recompui::Menu::Config); recompui::set_config_submenu(recompui::ConfigSubmenu::General); - } - ); + } + ); recompui::register_event(listener, "open_mods", [](const std::string ¶m, Rml::Event &event) { recompui::set_current_menu(recompui::Menu::Config); @@ -100,21 +100,21 @@ public: } ); recompui::register_event(listener, "exit_game", - [](const std::string& param, Rml::Event& event) { - ultramodern::quit(); - } - ); - } - void make_bindings(Rml::Context* context) override { - Rml::DataModelConstructor constructor = context->CreateDataModel("launcher_model"); + [](const std::string& param, Rml::Event& event) { + ultramodern::quit(); + } + ); + } + void make_bindings(Rml::Context* context) override { + Rml::DataModelConstructor constructor = context->CreateDataModel("launcher_model"); - constructor.Bind("mm_rom_valid", &mm_rom_valid); + constructor.Bind("mm_rom_valid", &mm_rom_valid); - version_string = recomp::get_project_version().to_string(); - constructor.Bind("version_number", &version_string); + version_string = recomp::get_project_version().to_string(); + constructor.Bind("version_number", &version_string); - model_handle = constructor.GetModelHandle(); - } + model_handle = constructor.GetModelHandle(); + } }; std::unique_ptr recompui::create_launcher_menu() { diff --git a/src/ui/ui_mod_details_panel.cpp b/src/ui/ui_mod_details_panel.cpp index c0de193..aff59c0 100644 --- a/src/ui/ui_mod_details_panel.cpp +++ b/src/ui/ui_mod_details_panel.cpp @@ -43,6 +43,7 @@ ModDetailsPanel::ModDetailsPanel(Element *parent) : Element(parent) { buttons_container->set_padding_left(16.0f); { enable_toggle = new Toggle(buttons_container); + enable_toggle->add_checked_callback(std::bind(&ModDetailsPanel::enable_toggle_checked, this, std::placeholders::_1)); configure_button = new Button("Configure", recompui::ButtonStyle::Secondary, buttons_container); erase_button = new Button("Erase", recompui::ButtonStyle::Secondary, buttons_container); } @@ -52,7 +53,7 @@ ModDetailsPanel::ModDetailsPanel(Element *parent) : Element(parent) { ModDetailsPanel::~ModDetailsPanel() { } -void ModDetailsPanel::set_mod_details(const recomp::mods::ModDetails& details) { +void ModDetailsPanel::set_mod_details(const recomp::mods::ModDetails& details, bool enabled) { cur_details = details; title_label->set_text(cur_details.mod_id); @@ -67,6 +68,17 @@ void ModDetailsPanel::set_mod_details(const recomp::mods::ModDetails& details) { authors_label->set_text(authors_str); description_label->set_text("Placeholder description. Some long text to make sure that wrapping is working correctly. Yet more text and so on."); + enable_toggle->set_checked(enabled); +} + +void ModDetailsPanel::set_mod_toggled_callback(std::function callback) { + mod_toggled_callback = callback; +} + +void ModDetailsPanel::enable_toggle_checked(bool checked) { + if (mod_toggled_callback != nullptr) { + mod_toggled_callback(checked); + } } } // namespace recompui diff --git a/src/ui/ui_mod_details_panel.h b/src/ui/ui_mod_details_panel.h index e225de8..47de3f3 100644 --- a/src/ui/ui_mod_details_panel.h +++ b/src/ui/ui_mod_details_panel.h @@ -12,24 +12,28 @@ namespace recompui { class ModDetailsPanel : public Element { public: - ModDetailsPanel(Element *parent); - virtual ~ModDetailsPanel(); - void set_mod_details(const recomp::mods::ModDetails& details); + ModDetailsPanel(Element *parent); + virtual ~ModDetailsPanel(); + void set_mod_details(const recomp::mods::ModDetails& details, bool enabled); + void set_mod_toggled_callback(std::function callback); private: - recomp::mods::ModDetails cur_details; - Container *thumbnail_container = nullptr; - Image *thumbnail_image = nullptr; - Container *header_container = nullptr; - Container *header_details_container = nullptr; - Label *title_label = nullptr; - Label *version_label = nullptr; - Container *body_container = nullptr; - Label *description_label = nullptr; - Label *authors_label = nullptr; - Container *buttons_container = nullptr; - Toggle *enable_toggle = nullptr; - Button *configure_button = nullptr; - Button *erase_button = nullptr; + recomp::mods::ModDetails cur_details; + Container *thumbnail_container = nullptr; + Image *thumbnail_image = nullptr; + Container *header_container = nullptr; + Container *header_details_container = nullptr; + Label *title_label = nullptr; + Label *version_label = nullptr; + Container *body_container = nullptr; + Label *description_label = nullptr; + Label *authors_label = nullptr; + Container *buttons_container = nullptr; + Toggle *enable_toggle = nullptr; + Button *configure_button = nullptr; + Button *erase_button = nullptr; + std::function mod_toggled_callback = {}; + + void enable_toggle_checked(bool checked); }; } // namespace recompui diff --git a/src/ui/ui_mod_menu.cpp b/src/ui/ui_mod_menu.cpp index 15145ce..eeb96a9 100644 --- a/src/ui/ui_mod_menu.cpp +++ b/src/ui/ui_mod_menu.cpp @@ -69,8 +69,12 @@ void ModEntry::process_event(const Event& e) { } } -void ModMenu::set_active_mod(uint32_t mod_index) { - mod_details_panel->set_mod_details(mod_details[mod_index]); +void ModMenu::set_active_mod(int32_t mod_index) { + active_mod_index = mod_index; + if (active_mod_index >= 0) { + bool mod_enabled = recomp::mods::is_mod_enabled(mod_details[mod_index].mod_id); + mod_details_panel->set_mod_details(mod_details[mod_index], mod_enabled); + } } void ModMenu::refresh_mods() { @@ -79,6 +83,12 @@ void ModMenu::refresh_mods() { create_mod_list(); } +void ModMenu::mod_toggled(bool enabled) { + if (active_mod_index >= 0) { + recomp::mods::enable_mod(mod_details[active_mod_index].mod_id, enabled); + } +} + void ModMenu::create_mod_list() { // Clear the contents of the list scroll. list_scroll_container->clear_children(); @@ -121,6 +131,7 @@ ModMenu::ModMenu(Element *parent) : Element(parent) { } // list_container mod_details_panel = new ModDetailsPanel(body_container); + mod_details_panel->set_mod_toggled_callback(std::bind(&ModMenu::mod_toggled, this, std::placeholders::_1)); } // body_container diff --git a/src/ui/ui_mod_menu.h b/src/ui/ui_mod_menu.h index 7afa173..ddd647e 100644 --- a/src/ui/ui_mod_menu.h +++ b/src/ui/ui_mod_menu.h @@ -11,45 +11,47 @@ class ModMenu; class ModEntry : public Element { public: - ModEntry(Element *parent, const recomp::mods::ModDetails &details, uint32_t mod_index, ModMenu *mod_menu); - virtual ~ModEntry(); + ModEntry(Element *parent, const recomp::mods::ModDetails &details, uint32_t mod_index, ModMenu *mod_menu); + virtual ~ModEntry(); protected: - virtual void process_event(const Event &e); + virtual void process_event(const Event &e); private: - uint32_t mod_index = 0; - ModMenu *mod_menu = nullptr; - Image *thumbnail_image = nullptr; - Container *body_container = nullptr; - Label *name_label = nullptr; - Label *description_label = nullptr; + uint32_t mod_index = 0; + ModMenu *mod_menu = nullptr; + Image *thumbnail_image = nullptr; + Container *body_container = nullptr; + Label *name_label = nullptr; + Label *description_label = nullptr; }; class ModMenu : public Element { public: - ModMenu(Element *parent); - virtual ~ModMenu(); - void set_active_mod(uint32_t mod_index); + ModMenu(Element *parent); + virtual ~ModMenu(); + void set_active_mod(int32_t mod_index); private: - void refresh_mods(); - void create_mod_list(); - - Container *body_container = nullptr; - Container *list_container = nullptr; - ScrollContainer *list_scroll_container = nullptr; - ModDetailsPanel *mod_details_panel = nullptr; - Container *footer_container = nullptr; - Button *refresh_button = nullptr; - std::vector mod_entries; - std::vector mod_details{}; - std::string game_mod_id; + void refresh_mods(); + void mod_toggled(bool enabled); + void create_mod_list(); + + Container *body_container = nullptr; + Container *list_container = nullptr; + ScrollContainer *list_scroll_container = nullptr; + ModDetailsPanel *mod_details_panel = nullptr; + Container *footer_container = nullptr; + Button *refresh_button = nullptr; + int32_t active_mod_index = -1; + std::vector mod_entries; + std::vector mod_details{}; + std::string game_mod_id; }; class ElementModMenu : public Rml::Element { public: - ElementModMenu(const Rml::String& tag); - virtual ~ElementModMenu(); + ElementModMenu(const Rml::String& tag); + virtual ~ElementModMenu(); private: - std::unique_ptr mod_menu; + std::unique_ptr mod_menu; }; } // namespace recompui