mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-06-23 07:19:53 -04:00
Rework Settings components
This commit is contained in:
@@ -1462,6 +1462,8 @@ set(DUSK_FILES
|
||||
src/dusk/imgui/ImGuiStateShare.cpp
|
||||
src/dusk/imgui/ImGuiAchievements.hpp
|
||||
src/dusk/imgui/ImGuiAchievements.cpp
|
||||
src/dusk/ui/bool_button.cpp
|
||||
src/dusk/ui/bool_button.hpp
|
||||
src/dusk/ui/button.cpp
|
||||
src/dusk/ui/button.hpp
|
||||
src/dusk/ui/component.cpp
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
#include "bool_button.hpp"
|
||||
|
||||
namespace dusk::ui {
|
||||
|
||||
BoolButton::BoolButton(Rml::Element* parent, Props props)
|
||||
: BaseControlledSelectButton(parent, {std::move(props.key)}),
|
||||
mGetValue(std::move(props.getValue)), mSetValue(std::move(props.setValue)),
|
||||
mIsDisabled(std::move(props.isDisabled)) {}
|
||||
|
||||
bool BoolButton::disabled() const {
|
||||
if (mIsDisabled) {
|
||||
return mIsDisabled();
|
||||
}
|
||||
return BaseControlledSelectButton::disabled();
|
||||
}
|
||||
|
||||
Rml::String BoolButton::format_value() {
|
||||
return mGetValue() ? "On" : "Off";
|
||||
}
|
||||
|
||||
bool BoolButton::handle_nav_command(NavCommand cmd) {
|
||||
if (cmd == NavCommand::Confirm || cmd == NavCommand::Left || cmd == NavCommand::Right) {
|
||||
mSetValue(!mGetValue());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace dusk::ui
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
#include "select_button.hpp"
|
||||
|
||||
namespace dusk::ui {
|
||||
|
||||
class BoolButton : public BaseControlledSelectButton {
|
||||
public:
|
||||
struct Props {
|
||||
Rml::String key;
|
||||
std::function<bool()> getValue;
|
||||
std::function<void(bool)> setValue;
|
||||
std::function<bool()> isDisabled;
|
||||
};
|
||||
|
||||
BoolButton(Rml::Element* parent, Props props);
|
||||
|
||||
bool disabled() const override;
|
||||
|
||||
protected:
|
||||
Rml::String format_value() override;
|
||||
bool handle_nav_command(NavCommand cmd) override;
|
||||
|
||||
private:
|
||||
std::function<int()> mGetValue;
|
||||
std::function<void(int)> mSetValue;
|
||||
std::function<bool()> mIsDisabled;
|
||||
};
|
||||
|
||||
} // namespace dusk::ui
|
||||
@@ -31,13 +31,13 @@ Button& Button::on_pressed(ButtonCallback callback) {
|
||||
if (!callback) {
|
||||
return *this;
|
||||
}
|
||||
listen(Rml::EventId::Click, [callback](Rml::Event&) { callback(); });
|
||||
listen(Rml::EventId::Keydown, [callback = std::move(callback)](Rml::Event& event) {
|
||||
const auto cmd = map_nav_event(event);
|
||||
// TODO: convert this to a FluentComponent method?
|
||||
on_nav_command([callback = std::move(callback)](Rml::Event&, NavCommand cmd) {
|
||||
if (cmd == NavCommand::Confirm) {
|
||||
callback();
|
||||
event.StopPropagation();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -43,7 +43,6 @@ void Component::set_selected(bool value) {
|
||||
return;
|
||||
}
|
||||
mRoot->SetPseudoClass("selected", value);
|
||||
mRoot->DispatchEvent(Rml::EventId::Change, {{"selected", Rml::Variant{value}}});
|
||||
}
|
||||
|
||||
void Component::set_disabled(bool value) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "event.hpp"
|
||||
#include "ui.hpp"
|
||||
|
||||
#include <RmlUi/Core.h>
|
||||
|
||||
@@ -73,6 +74,24 @@ public:
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Derived& on_nav_command(std::function<bool(Rml::Event&, NavCommand)> callback) {
|
||||
listen(Rml::EventId::Click, [this, callback](Rml::Event& event) {
|
||||
if (!disabled() && callback(event, NavCommand::Confirm)) {
|
||||
event.StopPropagation();
|
||||
}
|
||||
});
|
||||
listen(Rml::EventId::Keydown, [this, callback = std::move(callback)](Rml::Event& event) {
|
||||
if (disabled()) {
|
||||
return;
|
||||
}
|
||||
const auto cmd = map_nav_event(event);
|
||||
if (cmd != NavCommand::None && callback(event, cmd)) {
|
||||
event.StopPropagation();
|
||||
}
|
||||
});
|
||||
return static_cast<Derived&>(*this);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace dusk::ui
|
||||
|
||||
+6
-10
@@ -3,6 +3,7 @@
|
||||
#include <RmlUi/Core.h>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "bool_button.hpp"
|
||||
#include "button.hpp"
|
||||
#include "d/actor/d_a_player.h"
|
||||
#include "d/d_kankyo.h"
|
||||
@@ -1899,17 +1900,12 @@ EditorWindow::EditorWindow() {
|
||||
auto& rightPane = add_child<Pane>(content, Pane::Type::Uncontrolled);
|
||||
|
||||
leftPane.add_section("Options");
|
||||
// TODO: replace with generic bool component based on ConfigBoolSelect
|
||||
leftPane
|
||||
.add_button(
|
||||
{
|
||||
.text = "Enable Vibration",
|
||||
.isSelected = [] { return get_player_config()->getVibration() != 0; },
|
||||
},
|
||||
[] {
|
||||
const bool enabled = get_player_config()->getVibration() != 0;
|
||||
get_player_config()->setVibration(enabled ? 0 : 1);
|
||||
})
|
||||
.add_child<BoolButton>(BoolButton::Props{
|
||||
.key = "Enable Vibration",
|
||||
.getValue = [] { return get_player_config()->getVibration() != 0; },
|
||||
.setValue = [](bool value) { get_player_config()->setVibration(value); },
|
||||
})
|
||||
.on_focus([&rightPane](Rml::Event&) { rightPane.clear(); });
|
||||
leftPane
|
||||
.add_select_button({
|
||||
|
||||
@@ -7,10 +7,22 @@ namespace dusk::ui {
|
||||
|
||||
NumberButton::NumberButton(Rml::Element* parent, Props props)
|
||||
: BaseStringButton(parent, {.key = std::move(props.key), .type = "number"}),
|
||||
mGetValue(std::move(props.getValue)), mSetValue(std::move(props.setValue)), mMin(props.min),
|
||||
mMax(props.max), mStep(props.step) {}
|
||||
mGetValue(std::move(props.getValue)), mSetValue(std::move(props.setValue)),
|
||||
mIsDisabled(std::move(props.isDisabled)), mMin(props.min), mMax(props.max), mStep(props.step),
|
||||
mPrefix(std::move(props.prefix)), mSuffix(std::move(props.suffix)) {}
|
||||
|
||||
bool NumberButton::disabled() const {
|
||||
if (mIsDisabled) {
|
||||
return mIsDisabled();
|
||||
}
|
||||
return BaseStringButton::disabled();
|
||||
}
|
||||
|
||||
Rml::String NumberButton::format_value() {
|
||||
return fmt::format("{}{}{}", mPrefix, mGetValue(), mSuffix);
|
||||
}
|
||||
|
||||
Rml::String NumberButton::input_value() {
|
||||
return fmt::to_string(mGetValue());
|
||||
}
|
||||
|
||||
|
||||
@@ -10,24 +10,33 @@ public:
|
||||
Rml::String key;
|
||||
std::function<int()> getValue;
|
||||
std::function<void(int)> setValue;
|
||||
std::function<bool()> isDisabled;
|
||||
int min = 0;
|
||||
int max = INT_MAX;
|
||||
int step = 1;
|
||||
Rml::String prefix;
|
||||
Rml::String suffix;
|
||||
};
|
||||
|
||||
NumberButton(Rml::Element* parent, Props props);
|
||||
|
||||
bool disabled() const override;
|
||||
|
||||
protected:
|
||||
Rml::String format_value() override;
|
||||
Rml::String input_value() override;
|
||||
void set_value(Rml::String value) override;
|
||||
bool handle_nav_command(NavCommand cmd) override;
|
||||
|
||||
private:
|
||||
std::function<int()> mGetValue;
|
||||
std::function<void(int)> mSetValue;
|
||||
std::function<bool()> mIsDisabled;
|
||||
int mMin;
|
||||
int mMax;
|
||||
int mStep;
|
||||
Rml::String mPrefix;
|
||||
Rml::String mSuffix;
|
||||
};
|
||||
|
||||
} // namespace dusk::ui
|
||||
+15
-29
@@ -64,28 +64,19 @@ Pane::Pane(Rml::Element* parent, Type type) : FluentComponent(createRoot(parent)
|
||||
});
|
||||
|
||||
if (type == Type::Controlled) {
|
||||
// Listen for selection change events
|
||||
listen(Rml::EventId::Change, [this](Rml::Event& event) {
|
||||
const auto it = std::find_if(event.GetParameters().begin(), event.GetParameters().end(),
|
||||
[](const auto& param) { return param.first == "selected"; });
|
||||
if (it != event.GetParameters().end()) {
|
||||
const auto selected = it->second.Get<bool>();
|
||||
int childIndex = -1;
|
||||
for (int i = 0; i < mChildren.size(); ++i) {
|
||||
if (event.GetTargetElement() == mChildren[i]->root()) {
|
||||
childIndex = i;
|
||||
}
|
||||
}
|
||||
if (childIndex != -1) {
|
||||
if (selected) {
|
||||
set_selected_item(childIndex);
|
||||
} else if (childIndex == mSelectedItem) {
|
||||
set_selected_item(-1);
|
||||
}
|
||||
} else {
|
||||
set_selected_item(-1);
|
||||
// For controlled panes, handle SelectButton Submit events for item selection
|
||||
listen(Rml::EventId::Submit, [this](Rml::Event& event) {
|
||||
int childIndex = -1;
|
||||
for (int i = 0; i < mChildren.size(); ++i) {
|
||||
if (event.GetTargetElement() == mChildren[i]->root()) {
|
||||
childIndex = i;
|
||||
}
|
||||
}
|
||||
set_selected_item(childIndex);
|
||||
// If the selection was handled locally, don't allow it to bubble up to window
|
||||
if (event.GetParameter("handled", false)) {
|
||||
event.StopPropagation();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -96,17 +87,11 @@ void Pane::update() {
|
||||
}
|
||||
|
||||
void Pane::set_selected_item(int index) {
|
||||
if (mSelectedItem == index) {
|
||||
if (mType == Type::Uncontrolled) {
|
||||
return;
|
||||
}
|
||||
if (mSelectedItem >= 0 && mSelectedItem < mChildren.size()) {
|
||||
mChildren[mSelectedItem]->set_selected(false);
|
||||
}
|
||||
if (index >= 0 && index < mChildren.size()) {
|
||||
mSelectedItem = index;
|
||||
mChildren[index]->set_selected(true);
|
||||
} else {
|
||||
mSelectedItem = -1;
|
||||
for (int i = 0; i < mChildren.size(); ++i) {
|
||||
mChildren[i]->set_selected(i == index);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,6 +102,7 @@ bool Pane::focus() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Otherwise, focus the first focusable child
|
||||
for (const auto& child : mChildren) {
|
||||
if (child->focus()) {
|
||||
return true;
|
||||
|
||||
@@ -46,7 +46,6 @@ public:
|
||||
private:
|
||||
Type mType;
|
||||
bool finalized = false;
|
||||
int mSelectedItem = -1;
|
||||
};
|
||||
|
||||
} // namespace dusk::ui
|
||||
|
||||
@@ -39,6 +39,7 @@ Popup::Popup() : Document(kDocumentSource), mRoot(mDocument->GetElementById("pop
|
||||
mTabBar->add_tab("Reset", [this] {
|
||||
JUTGamePad::C3ButtonReset::sResetSwitchPushing = true;
|
||||
mTabBar->set_active_tab(-1);
|
||||
hide();
|
||||
});
|
||||
mTabBar->add_tab("Exit", [] { IsRunning = false; });
|
||||
|
||||
|
||||
@@ -20,23 +20,7 @@ SelectButton::SelectButton(Rml::Element* parent, Props props)
|
||||
mKeyElem = append(mRoot, "key");
|
||||
mValueElem = append(mRoot, "value");
|
||||
update_props(std::move(props));
|
||||
listen(Rml::EventId::Click, [this](Rml::Event& event) {
|
||||
if (disabled()) {
|
||||
return;
|
||||
}
|
||||
if (handle_nav_command(NavCommand::Confirm)) {
|
||||
event.StopPropagation();
|
||||
}
|
||||
});
|
||||
listen(Rml::EventId::Keydown, [this](Rml::Event& event) {
|
||||
if (disabled()) {
|
||||
return;
|
||||
}
|
||||
const auto cmd = map_nav_event(event);
|
||||
if (cmd != NavCommand::None && handle_nav_command(cmd)) {
|
||||
event.StopPropagation();
|
||||
}
|
||||
});
|
||||
on_nav_command([this](Rml::Event&, NavCommand cmd) { return handle_nav_command(cmd); });
|
||||
}
|
||||
|
||||
void SelectButton::set_value_label(const Rml::String& value) {
|
||||
@@ -56,7 +40,7 @@ void SelectButton::update_props(Props props) {
|
||||
|
||||
bool SelectButton::handle_nav_command(NavCommand cmd) {
|
||||
if (cmd == NavCommand::Confirm) {
|
||||
set_selected(!selected());
|
||||
mRoot->DispatchEvent(Rml::EventId::Submit, {});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -16,12 +16,6 @@ public:
|
||||
|
||||
void set_value_label(const Rml::String& value);
|
||||
|
||||
SelectButton& on_selected(std::function<void(bool)> callback) {
|
||||
return listen(Rml::EventId::Change, [callback = std::move(callback)](Rml::Event& event) {
|
||||
callback(event.GetParameter("selected", false));
|
||||
});
|
||||
}
|
||||
|
||||
protected:
|
||||
void update_props(Props props);
|
||||
virtual bool handle_nav_command(NavCommand cmd);
|
||||
|
||||
+210
-321
@@ -3,16 +3,16 @@
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "aurora/gfx.h"
|
||||
#include "bool_button.hpp"
|
||||
#include "dusk/audio/DuskAudioSystem.h"
|
||||
#include "dusk/config.hpp"
|
||||
#include "dusk/livesplit.h"
|
||||
#include "m_Do/m_Do_main.h"
|
||||
#include "number_button.hpp"
|
||||
#include "overlay.hpp"
|
||||
#include "pane.hpp"
|
||||
#include "ui.hpp"
|
||||
|
||||
#include <climits>
|
||||
|
||||
namespace dusk::ui {
|
||||
namespace {
|
||||
|
||||
@@ -42,192 +42,46 @@ void reset_for_speedrun_mode() {
|
||||
getSettings().game.enableTurboKeybind.setValue(false);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
const Rml::String kInternalResolutionHelpText =
|
||||
"Configure the resolution used for rendering the game. Higher values are more demanding on "
|
||||
"your graphics hardware.";
|
||||
const Rml::String kShadowResolutionHelpText =
|
||||
"Configure the shadow-map resolution. Higher values improve shadow quality but increase GPU "
|
||||
"and memory usage.";
|
||||
|
||||
template <typename T>
|
||||
struct ConfigProps {
|
||||
struct ConfigBoolProps {
|
||||
Rml::String key;
|
||||
ConfigVar<T>* value;
|
||||
std::function<void(T)> onChange;
|
||||
std::function<bool()> isDisabled;
|
||||
Rml::String helpText;
|
||||
Pane* rightPane = nullptr;
|
||||
std::function<void(bool)> onChange;
|
||||
std::function<bool()> isDisabled;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class ConfigSelect : public SelectButton {
|
||||
public:
|
||||
using Props = ConfigProps<T>;
|
||||
SelectButton& config_bool_select(
|
||||
Pane& leftPane, Pane& rightPane, ConfigVar<bool>& var, ConfigBoolProps props) {
|
||||
return leftPane
|
||||
.add_child<BoolButton>(BoolButton::Props{
|
||||
.key = std::move(props.key),
|
||||
.getValue = [&var] { return var.getValue(); },
|
||||
.setValue =
|
||||
[&var, callback = std::move(props.onChange)](bool value) {
|
||||
if (value == var.getValue()) {
|
||||
return;
|
||||
}
|
||||
var.setValue(value);
|
||||
config::Save();
|
||||
if (callback) {
|
||||
callback(value);
|
||||
}
|
||||
},
|
||||
.isDisabled = std::move(props.isDisabled),
|
||||
})
|
||||
.on_focus([&rightPane, helpText = std::move(props.helpText)](Rml::Event&) {
|
||||
rightPane.clear();
|
||||
rightPane.add_rml(helpText);
|
||||
});
|
||||
}
|
||||
|
||||
ConfigSelect(Rml::Element* parent, Props props)
|
||||
: SelectButton(parent, {std::move(props.key)}), mVar(props.value),
|
||||
mOnChange(std::move(props.onChange)), mIsDisabled(std::move(props.isDisabled)),
|
||||
mHelpText(std::move(props.helpText)), mRightPane(props.rightPane) {
|
||||
if (!mHelpText.empty() && mRightPane != nullptr) {
|
||||
listen(Rml::EventId::Focus, [this](Rml::Event&) {
|
||||
mRightPane->clear();
|
||||
mRightPane->add_rml(mHelpText);
|
||||
});
|
||||
listen(Rml::EventId::Mouseover, [this](Rml::Event&) {
|
||||
mRightPane->clear();
|
||||
mRightPane->add_rml(mHelpText);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void update() override {
|
||||
if (mIsDisabled) {
|
||||
set_disabled(mIsDisabled());
|
||||
}
|
||||
set_value_label(get_value());
|
||||
SelectButton::update();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual Rml::String get_value() = 0;
|
||||
|
||||
void set_value(T newValue) {
|
||||
if (mVar->getValue() == newValue) {
|
||||
return;
|
||||
}
|
||||
mVar->setValue(newValue);
|
||||
if (mOnChange) {
|
||||
mOnChange(newValue);
|
||||
}
|
||||
config::Save();
|
||||
}
|
||||
|
||||
ConfigVar<T>* mVar;
|
||||
std::function<void(T)> mOnChange;
|
||||
std::function<bool()> mIsDisabled;
|
||||
Rml::String mHelpText;
|
||||
Pane* mRightPane;
|
||||
};
|
||||
|
||||
class ConfigBoolSelect : public ConfigSelect<bool> {
|
||||
public:
|
||||
ConfigBoolSelect(Rml::Element* parent, Props props) : ConfigSelect(parent, std::move(props)) {}
|
||||
|
||||
protected:
|
||||
Rml::String get_value() override { return mVar->getValue() ? "On" : "Off"; }
|
||||
bool handle_nav_command(NavCommand cmd) override {
|
||||
if (cmd == NavCommand::Confirm || cmd == NavCommand::Left || cmd == NavCommand::Right) {
|
||||
set_value(!mVar->getValue());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class ConfigIntSelect : public ConfigSelect<int> {
|
||||
public:
|
||||
struct Props {
|
||||
Rml::String key;
|
||||
ConfigVar<int>* value;
|
||||
std::function<void(int)> onChange;
|
||||
std::function<bool()> isDisabled;
|
||||
Rml::String helpText;
|
||||
Pane* rightPane = nullptr;
|
||||
int min = 0;
|
||||
int max = INT_MAX;
|
||||
int step = 1;
|
||||
Rml::String prefix;
|
||||
Rml::String suffix;
|
||||
};
|
||||
|
||||
ConfigIntSelect(Rml::Element* parent, Props props)
|
||||
: ConfigSelect(parent,
|
||||
{
|
||||
.key = std::move(props.key),
|
||||
.value = props.value,
|
||||
.onChange = std::move(props.onChange),
|
||||
.isDisabled = std::move(props.isDisabled),
|
||||
.helpText = std::move(props.helpText),
|
||||
.rightPane = props.rightPane,
|
||||
}),
|
||||
mMin(props.min), mMax(props.max), mStep(props.step), mPrefix(std::move(props.prefix)),
|
||||
mSuffix(std::move(props.suffix)) {}
|
||||
|
||||
protected:
|
||||
Rml::String get_value() override {
|
||||
return fmt::format("{}{}{}", mPrefix, mVar->getValue(), mSuffix);
|
||||
}
|
||||
|
||||
bool handle_nav_command(NavCommand cmd) override {
|
||||
if (cmd == NavCommand::Left) {
|
||||
set_value(std::clamp(mVar->getValue() - mStep, mMin, mMax));
|
||||
return true;
|
||||
} else if (cmd == NavCommand::Right) {
|
||||
set_value(std::clamp(mVar->getValue() + mStep, mMin, mMax));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
int mMin;
|
||||
int mMax;
|
||||
int mStep;
|
||||
Rml::String mPrefix;
|
||||
Rml::String mSuffix;
|
||||
};
|
||||
|
||||
class ConfigOverlaySelect : public ConfigSelect<int> {
|
||||
public:
|
||||
struct Props {
|
||||
Rml::String key;
|
||||
ConfigVar<int>* value;
|
||||
GraphicsOption option;
|
||||
Rml::String title;
|
||||
int min = 0;
|
||||
int max = 0;
|
||||
Rml::String helpText;
|
||||
Pane* rightPane = nullptr;
|
||||
std::function<bool()> isDisabled;
|
||||
};
|
||||
|
||||
ConfigOverlaySelect(Rml::Element* parent, Props props)
|
||||
: ConfigSelect<int>(parent,
|
||||
{
|
||||
.key = std::move(props.key),
|
||||
.value = props.value,
|
||||
.onChange = {},
|
||||
.isDisabled = std::move(props.isDisabled),
|
||||
.helpText = std::move(props.helpText),
|
||||
.rightPane = props.rightPane,
|
||||
}),
|
||||
mOption(props.option), mTitle(std::move(props.title)), mValueMin(props.min),
|
||||
mValueMax(props.max) {}
|
||||
|
||||
protected:
|
||||
Rml::String get_value() override {
|
||||
return format_graphics_setting_value(mOption, mVar->getValue());
|
||||
}
|
||||
|
||||
bool handle_nav_command(NavCommand cmd) override {
|
||||
if (cmd == NavCommand::Confirm) {
|
||||
open_overlay();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
void open_overlay() {
|
||||
push_document(std::make_unique<Overlay>(OverlayProps{
|
||||
.option = mOption,
|
||||
.title = mTitle,
|
||||
.helpText = mHelpText,
|
||||
.valueMin = mValueMin,
|
||||
.valueMax = mValueMax,
|
||||
}));
|
||||
}
|
||||
|
||||
GraphicsOption mOption;
|
||||
Rml::String mTitle;
|
||||
int mValueMin = 0;
|
||||
int mValueMax = 0;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
SettingsWindow::SettingsWindow() {
|
||||
add_tab("Audio", [this](Rml::Element* content) {
|
||||
@@ -235,38 +89,43 @@ SettingsWindow::SettingsWindow() {
|
||||
auto& rightPane = add_child<Pane>(content, Pane::Type::Uncontrolled);
|
||||
|
||||
leftPane.add_section("Volume");
|
||||
leftPane.add_child<ConfigIntSelect>(ConfigIntSelect::Props{
|
||||
.key = "Master Volume",
|
||||
.value = &getSettings().audio.masterVolume,
|
||||
.onChange = [](int value) { audio::SetMasterVolume(value / 100.f); },
|
||||
.helpText = "Adjusts the volume of all sounds in the game.",
|
||||
.rightPane = &rightPane,
|
||||
.max = 100,
|
||||
.suffix = "%",
|
||||
});
|
||||
leftPane
|
||||
.add_child<NumberButton>(NumberButton::Props{
|
||||
.key = "Master Volume",
|
||||
.getValue = [] { return getSettings().audio.masterVolume.getValue(); },
|
||||
.setValue =
|
||||
[](int value) {
|
||||
getSettings().audio.masterVolume.setValue(value);
|
||||
config::Save();
|
||||
audio::SetMasterVolume(value / 100.f);
|
||||
},
|
||||
.max = 100,
|
||||
.suffix = "%",
|
||||
})
|
||||
.on_focus([&rightPane](Rml::Event&) {
|
||||
rightPane.clear();
|
||||
rightPane.add_text("Adjusts the volume of all sounds in the game.");
|
||||
});
|
||||
|
||||
leftPane.add_section("Effects");
|
||||
leftPane.add_child<ConfigBoolSelect>(ConfigBoolSelect::Props{
|
||||
.key = "Enable Reverb",
|
||||
.value = &getSettings().audio.enableReverb,
|
||||
.onChange = [](bool value) { audio::SetEnableReverb(value); },
|
||||
.helpText = "Enables the reverb effect in game audio.",
|
||||
.rightPane = &rightPane,
|
||||
});
|
||||
config_bool_select(leftPane, rightPane, getSettings().audio.enableReverb,
|
||||
{
|
||||
.key = "Enable Reverb",
|
||||
.helpText = "Enables the reverb effect in game audio.",
|
||||
.onChange = [](bool value) { audio::SetEnableReverb(value); },
|
||||
});
|
||||
|
||||
leftPane.add_section("Tweaks");
|
||||
leftPane.add_child<ConfigBoolSelect>(ConfigBoolSelect::Props{
|
||||
.key = "No Low HP Sound",
|
||||
.value = &getSettings().game.noLowHpSound,
|
||||
.helpText = "Disable the beeping sound when having low health.",
|
||||
.rightPane = &rightPane,
|
||||
});
|
||||
leftPane.add_child<ConfigBoolSelect>(ConfigBoolSelect::Props{
|
||||
.key = "Non-Stop Midna's Lament",
|
||||
.value = &getSettings().game.midnasLamentNonStop,
|
||||
.helpText = "Prevents enemy music while Midna's Lament is playing.",
|
||||
.rightPane = &rightPane,
|
||||
});
|
||||
config_bool_select(leftPane, rightPane, getSettings().game.noLowHpSound,
|
||||
{
|
||||
.key = "No Low HP Sound",
|
||||
.helpText = "Disable the beeping sound when having low health.",
|
||||
});
|
||||
config_bool_select(leftPane, rightPane, getSettings().game.midnasLamentNonStop,
|
||||
{
|
||||
.key = "Non-Stop Midna's Lament",
|
||||
.helpText = "Prevents enemy music while Midna's Lament is playing.",
|
||||
});
|
||||
});
|
||||
|
||||
add_tab("Cheats", [this](Rml::Element* content) {
|
||||
@@ -275,13 +134,12 @@ SettingsWindow::SettingsWindow() {
|
||||
|
||||
auto addCheat = [&](const Rml::String& key, ConfigVar<bool>& value,
|
||||
const Rml::String& helpText) {
|
||||
leftPane.add_child<ConfigBoolSelect>(ConfigBoolSelect::Props{
|
||||
.key = key,
|
||||
.value = &value,
|
||||
.isDisabled = [] { return getSettings().game.speedrunMode; },
|
||||
.helpText = helpText,
|
||||
.rightPane = &rightPane,
|
||||
});
|
||||
config_bool_select(leftPane, rightPane, value,
|
||||
{
|
||||
.key = key,
|
||||
.helpText = helpText,
|
||||
.isDisabled = [] { return getSettings().game.speedrunMode; },
|
||||
});
|
||||
};
|
||||
|
||||
leftPane.add_section("Resources");
|
||||
@@ -320,23 +178,20 @@ SettingsWindow::SettingsWindow() {
|
||||
|
||||
auto addOption = [&](const Rml::String& key, ConfigVar<bool>& value,
|
||||
const Rml::String& helpText) {
|
||||
leftPane.add_child<ConfigBoolSelect>(ConfigBoolSelect::Props{
|
||||
.key = key,
|
||||
.value = &value,
|
||||
.helpText = helpText,
|
||||
.rightPane = &rightPane,
|
||||
});
|
||||
config_bool_select(leftPane, rightPane, value,
|
||||
{
|
||||
.key = key,
|
||||
.helpText = helpText,
|
||||
});
|
||||
};
|
||||
|
||||
auto addSpeedrunDisabledOption = [&](const Rml::String& key, ConfigVar<bool>& value,
|
||||
const Rml::String& helpText) {
|
||||
leftPane.add_child<ConfigBoolSelect>(ConfigBoolSelect::Props{
|
||||
.key = key,
|
||||
.value = &value,
|
||||
.isDisabled = [] { return getSettings().game.speedrunMode; },
|
||||
.helpText = helpText,
|
||||
.rightPane = &rightPane,
|
||||
});
|
||||
config_bool_select(leftPane, rightPane, value,
|
||||
{
|
||||
.key = key,
|
||||
.helpText = helpText,
|
||||
.isDisabled = [] { return getSettings().game.speedrunMode; },
|
||||
});
|
||||
};
|
||||
|
||||
leftPane.add_section("General");
|
||||
@@ -351,16 +206,24 @@ SettingsWindow::SettingsWindow() {
|
||||
"Enables rotating Link in the collection menu with the C-Stick.");
|
||||
|
||||
leftPane.add_section("Difficulty");
|
||||
leftPane.add_child<ConfigIntSelect>(ConfigIntSelect::Props{
|
||||
.key = "Damage Multiplier",
|
||||
.value = &getSettings().game.damageMultiplier,
|
||||
.isDisabled = [] { return getSettings().game.speedrunMode; },
|
||||
.helpText = "Multiplies incoming damage.",
|
||||
.rightPane = &rightPane,
|
||||
.min = 1,
|
||||
.max = 8,
|
||||
.prefix = "x",
|
||||
});
|
||||
leftPane
|
||||
.add_child<NumberButton>(NumberButton::Props{
|
||||
.key = "Damage Multiplier",
|
||||
.getValue = [] { return getSettings().game.damageMultiplier.getValue(); },
|
||||
.setValue =
|
||||
[](int value) {
|
||||
getSettings().game.damageMultiplier.setValue(value);
|
||||
config::Save();
|
||||
},
|
||||
.isDisabled = [] { return getSettings().game.speedrunMode; },
|
||||
.min = 1,
|
||||
.max = 8,
|
||||
.prefix = "x",
|
||||
})
|
||||
.on_focus([&rightPane](Rml::Event&) {
|
||||
rightPane.clear();
|
||||
rightPane.add_text("Multiplies incoming damage.");
|
||||
});
|
||||
addSpeedrunDisabledOption(
|
||||
"Instant Death", getSettings().game.instantDeath, "Any hit will instantly kill you.");
|
||||
addSpeedrunDisabledOption("No Heart Drops", getSettings().game.noHeartDrops,
|
||||
@@ -396,29 +259,27 @@ SettingsWindow::SettingsWindow() {
|
||||
"Transform instantly by pressing R and Y simultaneously.");
|
||||
|
||||
leftPane.add_section("Speedrunning");
|
||||
leftPane.add_child<ConfigBoolSelect>(ConfigBoolSelect::Props{
|
||||
.key = "Speedrun Mode",
|
||||
.value = &getSettings().game.speedrunMode,
|
||||
.onChange = [](bool) { reset_for_speedrun_mode(); },
|
||||
.helpText = "Enables speedrunning options while restricting certain "
|
||||
"gameplay modifiers.",
|
||||
.rightPane = &rightPane,
|
||||
});
|
||||
leftPane.add_child<ConfigBoolSelect>(ConfigBoolSelect::Props{
|
||||
.key = "LiveSplit Connection",
|
||||
.value = &getSettings().game.liveSplitEnabled,
|
||||
.onChange =
|
||||
[](bool enabled) {
|
||||
if (enabled) {
|
||||
speedrun::connectLiveSplit();
|
||||
} else {
|
||||
speedrun::disconnectLiveSplit();
|
||||
}
|
||||
},
|
||||
.isDisabled = [] { return !getSettings().game.speedrunMode; },
|
||||
.helpText = "Connect to LiveSplit server on localhost:16834.",
|
||||
.rightPane = &rightPane,
|
||||
});
|
||||
config_bool_select(leftPane, rightPane, getSettings().game.speedrunMode,
|
||||
{
|
||||
.key = "Speedrun Mode",
|
||||
.helpText =
|
||||
"Enables speedrunning options while restricting certain gameplay modifiers.",
|
||||
.onChange = [](bool) { reset_for_speedrun_mode(); },
|
||||
});
|
||||
config_bool_select(leftPane, rightPane, getSettings().game.liveSplitEnabled,
|
||||
{
|
||||
.key = "LiveSplit Connection",
|
||||
.helpText = "Connect to LiveSplit server on localhost:16834.",
|
||||
.onChange =
|
||||
[](bool enabled) {
|
||||
if (enabled) {
|
||||
speedrun::connectLiveSplit();
|
||||
} else {
|
||||
speedrun::disconnectLiveSplit();
|
||||
}
|
||||
},
|
||||
.isDisabled = [] { return !getSettings().game.speedrunMode; },
|
||||
});
|
||||
});
|
||||
|
||||
add_tab("Graphics", [this](Rml::Element* content) {
|
||||
@@ -438,71 +299,99 @@ SettingsWindow::SettingsWindow() {
|
||||
VISetWindowSize(FB_WIDTH * 2, FB_HEIGHT * 2);
|
||||
VICenterWindow();
|
||||
});
|
||||
leftPane.add_child<ConfigBoolSelect>(ConfigBoolSelect::Props{
|
||||
.key = "Enable VSync",
|
||||
.value = &getSettings().video.enableVsync,
|
||||
.onChange = [](bool value) { aurora_enable_vsync(value); },
|
||||
.helpText = "Synchronizes the frame rate to your monitor's refresh rate.",
|
||||
.rightPane = &rightPane,
|
||||
});
|
||||
leftPane.add_child<ConfigBoolSelect>(ConfigBoolSelect::Props{
|
||||
.key = "Lock 4:3 Aspect Ratio",
|
||||
.value = &getSettings().video.lockAspectRatio,
|
||||
.onChange =
|
||||
[](bool value) {
|
||||
AuroraSetViewportPolicy(value ? AURORA_VIEWPORT_FIT : AURORA_VIEWPORT_STRETCH);
|
||||
},
|
||||
.helpText = "Lock the game's aspect ratio to the original.",
|
||||
.rightPane = &rightPane,
|
||||
});
|
||||
config_bool_select(leftPane, rightPane, getSettings().video.enableVsync,
|
||||
{
|
||||
.key = "Enable VSync",
|
||||
.helpText = "Synchronizes the frame rate to your monitor's refresh rate.",
|
||||
.onChange = [](bool value) { aurora_enable_vsync(value); },
|
||||
});
|
||||
config_bool_select(leftPane, rightPane, getSettings().video.lockAspectRatio,
|
||||
{
|
||||
.key = "Lock 4:3 Aspect Ratio",
|
||||
.helpText = "Lock the game's aspect ratio to the original.",
|
||||
.onChange =
|
||||
[](bool value) {
|
||||
AuroraSetViewportPolicy(
|
||||
value ? AURORA_VIEWPORT_FIT : AURORA_VIEWPORT_STRETCH);
|
||||
},
|
||||
});
|
||||
|
||||
leftPane.add_section("Resolution");
|
||||
leftPane.add_child<ConfigOverlaySelect>(ConfigOverlaySelect::Props{
|
||||
.key = "Internal Resolution",
|
||||
.value = &getSettings().game.internalResolutionScale,
|
||||
.option = GraphicsOption::InternalResolution,
|
||||
.title = "Internal Resolution",
|
||||
.min = 0,
|
||||
.max = 12,
|
||||
.helpText =
|
||||
"Configure the resolution used for rendering the game. Higher values are more "
|
||||
"demanding on your graphics hardware.",
|
||||
.rightPane = &rightPane,
|
||||
});
|
||||
leftPane.add_child<ConfigOverlaySelect>(ConfigOverlaySelect::Props{
|
||||
.key = "Shadow Resolution",
|
||||
.value = &getSettings().game.shadowResolutionMultiplier,
|
||||
.option = GraphicsOption::ShadowResolution,
|
||||
.title = "Shadow Resolution",
|
||||
.min = 1,
|
||||
.max = 8,
|
||||
.helpText =
|
||||
"Configure the shadow-map resolution. Higher values improve shadow quality but "
|
||||
"increase GPU and memory usage.",
|
||||
.rightPane = &rightPane,
|
||||
});
|
||||
leftPane
|
||||
.add_select_button({
|
||||
.key = "Internal Resolution",
|
||||
.getValue =
|
||||
[] {
|
||||
return format_graphics_setting_value(GraphicsOption::InternalResolution,
|
||||
getSettings().game.internalResolutionScale.getValue());
|
||||
},
|
||||
})
|
||||
.on_nav_command([](Rml::Event&, NavCommand cmd) {
|
||||
if (cmd == NavCommand::Confirm || cmd == NavCommand::Left ||
|
||||
cmd == NavCommand::Right) {
|
||||
push_document(std::make_unique<Overlay>(OverlayProps{
|
||||
.option = GraphicsOption::InternalResolution,
|
||||
.title = "Internal Resolution",
|
||||
.helpText = kInternalResolutionHelpText,
|
||||
.valueMin = 0,
|
||||
.valueMax = 12,
|
||||
}));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
})
|
||||
.on_focus([&rightPane](Rml::Event&) {
|
||||
rightPane.clear();
|
||||
rightPane.add_text(kInternalResolutionHelpText);
|
||||
});
|
||||
leftPane
|
||||
.add_select_button({
|
||||
.key = "Shadow Resolution",
|
||||
.getValue =
|
||||
[] {
|
||||
return format_graphics_setting_value(GraphicsOption::ShadowResolution,
|
||||
getSettings().game.shadowResolutionMultiplier.getValue());
|
||||
},
|
||||
})
|
||||
.on_nav_command([](Rml::Event&, NavCommand cmd) {
|
||||
if (cmd == NavCommand::Confirm || cmd == NavCommand::Left ||
|
||||
cmd == NavCommand::Right) {
|
||||
push_document(std::make_unique<Overlay>(OverlayProps{
|
||||
.option = GraphicsOption::ShadowResolution,
|
||||
.title = "Shadow Resolution",
|
||||
.helpText = kShadowResolutionHelpText,
|
||||
.valueMin = 0,
|
||||
.valueMax = 8,
|
||||
}));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
})
|
||||
.on_focus([&rightPane](Rml::Event&) {
|
||||
rightPane.clear();
|
||||
rightPane.add_text(kShadowResolutionHelpText);
|
||||
});
|
||||
|
||||
leftPane.add_section("Post-Processing");
|
||||
// TODO: Bloom
|
||||
// TODO: Bloom Brightness
|
||||
|
||||
leftPane.add_section("Rendering");
|
||||
leftPane.add_child<ConfigBoolSelect>(ConfigBoolSelect::Props{
|
||||
.key = "Unlock Framerate",
|
||||
.value = &getSettings().game.enableFrameInterpolation,
|
||||
.helpText =
|
||||
"Uses inter-frame interpolation to enable higher frame rates.<br/><br/>Visual "
|
||||
"artifacts, animation glitches, or instability may occur.",
|
||||
.rightPane = &rightPane,
|
||||
});
|
||||
leftPane.add_child<ConfigBoolSelect>(ConfigBoolSelect::Props{
|
||||
.key = "Enable Depth of Field",
|
||||
.value = &getSettings().game.enableDepthOfField,
|
||||
});
|
||||
leftPane.add_child<ConfigBoolSelect>(ConfigBoolSelect::Props{
|
||||
.key = "Enable Mini-Map Shadows",
|
||||
.value = &getSettings().game.enableMapBackground,
|
||||
});
|
||||
config_bool_select(leftPane, rightPane, getSettings().game.enableFrameInterpolation,
|
||||
{
|
||||
.key = "Unlock Framerate",
|
||||
.helpText =
|
||||
"Uses inter-frame interpolation to enable higher frame rates.<br/><br/>Visual "
|
||||
"artifacts, animation glitches, or instability may occur.",
|
||||
});
|
||||
config_bool_select(leftPane, rightPane, getSettings().game.enableDepthOfField,
|
||||
{
|
||||
.key = "Enable Depth of Field",
|
||||
});
|
||||
config_bool_select(leftPane, rightPane, getSettings().game.enableMapBackground,
|
||||
{
|
||||
.key = "Enable Mini-Map Shadows",
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ void BaseStringButton::start_editing() {
|
||||
return;
|
||||
}
|
||||
mInputElem->SetAttribute("type", mType);
|
||||
mInputElem->SetAttribute("value", format_value());
|
||||
mInputElem->SetAttribute("value", input_value());
|
||||
if (mMaxLength > -1) {
|
||||
mInputElem->SetAttribute("maxlength", mMaxLength);
|
||||
}
|
||||
@@ -49,8 +49,9 @@ void BaseStringButton::start_editing() {
|
||||
// mobile keyboard placement gets a valid caret rectangle.
|
||||
mPendingInputFocusFrames = 2;
|
||||
|
||||
// Mark button as selected to indicate "active"
|
||||
set_selected(true);
|
||||
// Dispatch a submit event so the pane can handle item selection
|
||||
// However, mark it as "handled" to ensure that we don't steal focus away
|
||||
mRoot->DispatchEvent(Rml::EventId::Submit, {{"handled", Rml::Variant{true}}});
|
||||
|
||||
// Register input listeners
|
||||
mInputListeners.emplace_back(std::make_unique<ScopedEventListener>(
|
||||
@@ -85,8 +86,10 @@ bool BaseStringButton::handle_nav_command(NavCommand cmd) {
|
||||
}
|
||||
return true;
|
||||
} else if (cmd == NavCommand::Cancel) {
|
||||
request_stop_editing(false, true);
|
||||
return true;
|
||||
if (mInputElem != nullptr) {
|
||||
request_stop_editing(false, true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ public:
|
||||
protected:
|
||||
bool handle_nav_command(NavCommand cmd) override;
|
||||
virtual void set_value(Rml::String value) = 0;
|
||||
virtual Rml::String input_value() { return format_value(); }
|
||||
|
||||
private:
|
||||
void focus_input();
|
||||
|
||||
+9
-11
@@ -73,19 +73,17 @@ Window::Window() : Document(kDocumentSource), mRoot(mDocument->GetElementById("w
|
||||
});
|
||||
|
||||
// If an item is selected in a pane, focus the next pane in the tree
|
||||
listen(mRoot, Rml::EventId::Change, [this](Rml::Event& event) {
|
||||
if (event.GetParameter("selected", false)) {
|
||||
int paneIndex = -1;
|
||||
for (int i = 0; i < mContentComponents.size(); i++) {
|
||||
if (mContentComponents[i]->contains(event.GetTargetElement())) {
|
||||
paneIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (paneIndex >= 0 && paneIndex < mContentComponents.size() - 1) {
|
||||
mContentComponents[paneIndex + 1]->focus();
|
||||
listen(mRoot, Rml::EventId::Submit, [this](Rml::Event& event) {
|
||||
int paneIndex = -1;
|
||||
for (int i = 0; i < mContentComponents.size(); i++) {
|
||||
if (mContentComponents[i]->contains(event.GetTargetElement())) {
|
||||
paneIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (paneIndex >= 0 && paneIndex < mContentComponents.size() - 1) {
|
||||
mContentComponents[paneIndex + 1]->focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user