UI: Refactor pane control to allow hover for help text

This commit is contained in:
Luke Street
2026-05-04 14:16:05 -06:00
parent 5f2cf68e80
commit 37b8122962
8 changed files with 517 additions and 563 deletions
-9
View File
@@ -66,15 +66,6 @@ public:
return static_cast<Derived&>(*this);
}
Derived& on_focus(ScopedEventListener::Callback callback) {
return listen(
Rml::EventId::Focus, [this, callback = std::move(callback)](Rml::Event& event) {
if (!disabled()) {
callback(event);
}
});
}
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)) {
+46 -52
View File
@@ -246,18 +246,16 @@ void ControllerConfigWindow::build_port_tab(Rml::Element* content, int port) {
mRightPane = &rightPane;
mActivePort = port;
auto showPage = [this, &rightPane, port](Page page) {
mPage = page;
render_page(rightPane, port, page);
};
auto addPageButton = [&leftPane, showPage](Page page, Rml::String key, auto getValue) {
leftPane
.add_select_button({
.key = std::move(key),
.getValue = std::move(getValue),
})
.on_focus([showPage, page](Rml::Event&) { showPage(page); })
.on_pressed([showPage, page] { showPage(page); });
auto addPageButton = [this, &leftPane, &rightPane, port](
Page page, Rml::String key, auto getValue) {
leftPane.register_control(leftPane.add_select_button({
.key = std::move(key),
.getValue = std::move(getValue),
}),
rightPane, [this, port, page](Pane& pane) {
mPage = page;
render_page(pane, port, page);
});
};
addPageButton(Page::Controller, "Controller", [port] { return current_controller_name(port); });
@@ -266,47 +264,43 @@ void ControllerConfigWindow::build_port_tab(Rml::Element* content, int port) {
addPageButton(Page::Sticks, "Sticks", [] { return Rml::String(">"); });
leftPane.add_section("Options");
leftPane
.add_child<BoolButton>(BoolButton::Props{
.key = "Enable Dead Zones",
.getValue =
[port] {
PADDeadZones* deadZones = PADGetDeadZones(port);
return deadZones != nullptr && deadZones->useDeadzones;
},
.setValue =
[port](bool value) {
if (PADDeadZones* deadZones = PADGetDeadZones(port)) {
deadZones->useDeadzones = value;
PADSerializeMappings();
}
},
.isDisabled = [port] { return PADGetDeadZones(port) == nullptr; },
})
.on_focus([&rightPane](Rml::Event&) {
rightPane.clear();
rightPane.add_text("Apply configured dead zones to the sticks and analog triggers.");
leftPane.register_control(leftPane.add_child<BoolButton>(BoolButton::Props{
.key = "Enable Dead Zones",
.getValue =
[port] {
PADDeadZones* deadZones = PADGetDeadZones(port);
return deadZones != nullptr && deadZones->useDeadzones;
},
.setValue =
[port](bool value) {
if (PADDeadZones* deadZones = PADGetDeadZones(port)) {
deadZones->useDeadzones = value;
PADSerializeMappings();
}
},
.isDisabled = [port] { return PADGetDeadZones(port) == nullptr; },
}),
rightPane, [](Pane& pane) {
pane.add_text("Apply configured dead zones to the sticks and analog triggers.");
});
leftPane
.add_child<BoolButton>(BoolButton::Props{
.key = "Emulate Triggers",
.getValue =
[port] {
PADDeadZones* deadZones = PADGetDeadZones(port);
return deadZones != nullptr && deadZones->emulateTriggers;
},
.setValue =
[port](bool value) {
if (PADDeadZones* deadZones = PADGetDeadZones(port)) {
deadZones->emulateTriggers = value;
PADSerializeMappings();
}
},
.isDisabled = [port] { return PADGetDeadZones(port) == nullptr; },
})
.on_focus([&rightPane](Rml::Event&) {
rightPane.clear();
rightPane.add_text("Treat analog trigger movement as digital L and R button input.");
leftPane.register_control(leftPane.add_child<BoolButton>(BoolButton::Props{
.key = "Emulate Triggers",
.getValue =
[port] {
PADDeadZones* deadZones = PADGetDeadZones(port);
return deadZones != nullptr && deadZones->emulateTriggers;
},
.setValue =
[port](bool value) {
if (PADDeadZones* deadZones = PADGetDeadZones(port)) {
deadZones->emulateTriggers = value;
PADSerializeMappings();
}
},
.isDisabled = [port] { return PADGetDeadZones(port) == nullptr; },
}),
rightPane, [](Pane& pane) {
pane.add_text("Treat analog trigger movement as digital L and R button input.");
});
render_page(rightPane, port, mPage);
+295 -307
View File
@@ -1295,73 +1295,71 @@ EditorWindow::EditorWindow() {
auto& rightPane = add_child<Pane>(content, Pane::Type::Uncontrolled);
leftPane.add_section("Player");
leftPane
.add_child<StringButton>(StringButton::Props{
.key = "Player Name",
.getValue = get_player_name,
.setValue = set_player_name,
.maxLength = 16,
})
.on_focus([&rightPane](Rml::Event&) { rightPane.clear(); });
leftPane
.add_child<StringButton>(StringButton::Props{
.key = "Horse Name",
.getValue = get_horse_name,
.setValue = set_horse_name,
.maxLength = 16,
})
.on_focus([&rightPane](Rml::Event&) { rightPane.clear(); });
leftPane
.add_child<NumberButton>(NumberButton::Props{
leftPane.register_control(leftPane.add_child<StringButton>(StringButton::Props{
.key = "Player Name",
.getValue = get_player_name,
.setValue = set_player_name,
.maxLength = 16,
}),
rightPane, {});
leftPane.register_control(leftPane.add_child<StringButton>(StringButton::Props{
.key = "Horse Name",
.getValue = get_horse_name,
.setValue = set_horse_name,
.maxLength = 16,
}),
rightPane, {});
leftPane.register_control(
leftPane.add_child<NumberButton>(NumberButton::Props{
.key = "Max Health",
.getValue = [] { return get_player_status()->getMaxLife(); },
.setValue = [](int value) { return get_player_status()->setMaxLife(value); },
.max = UINT16_MAX, // TODO: actual max
})
.on_focus([&rightPane](Rml::Event&) { rightPane.clear(); });
leftPane
.add_child<NumberButton>(NumberButton::Props{
}),
rightPane, {});
leftPane.register_control(
leftPane.add_child<NumberButton>(NumberButton::Props{
.key = "Health",
.getValue = [] { return get_player_status()->getLife(); },
.setValue = [](int value) { return get_player_status()->setLife(value); },
.max = UINT16_MAX, // TODO: actual max
})
.on_focus([&rightPane](Rml::Event&) { rightPane.clear(); });
leftPane
.add_child<NumberButton>(NumberButton::Props{
}),
rightPane, {});
leftPane.register_control(
leftPane.add_child<NumberButton>(NumberButton::Props{
.key = "Rupees",
.getValue = [] { return get_player_status()->getRupee(); },
.setValue = [](int value) { return get_player_status()->setRupee(value); },
.max = get_player_status()->getRupeeMax(),
})
.on_focus([&rightPane](Rml::Event&) { rightPane.clear(); });
leftPane
.add_child<NumberButton>(NumberButton::Props{
}),
rightPane, {});
leftPane.register_control(
leftPane.add_child<NumberButton>(NumberButton::Props{
.key = "Max Oil",
.getValue = [] { return get_player_status()->getMaxOil(); },
.setValue = [](int value) { return get_player_status()->setMaxOil(value); },
.max = UINT16_MAX, // TODO: actual max
})
.on_focus([&rightPane](Rml::Event&) { rightPane.clear(); });
leftPane
.add_child<NumberButton>(NumberButton::Props{
}),
rightPane, {});
leftPane.register_control(
leftPane.add_child<NumberButton>(NumberButton::Props{
.key = "Oil",
.getValue = [] { return get_player_status()->getOil(); },
.setValue = [](int value) { return get_player_status()->setOil(value); },
.max = UINT16_MAX, // TODO: actual max
})
.on_focus([&rightPane](Rml::Event&) { rightPane.clear(); });
}),
rightPane, {});
leftPane.add_section("Equipment");
const auto genSelectItemComboBox = [&leftPane, &rightPane](
const Rml::String& label, u8& selectItemData) {
leftPane
.add_select_button({
leftPane.register_control(
leftPane.add_select_button({
.key = label,
.getValue = [&selectItemData] { return item_label_for_slot(selectItemData); },
})
.on_focus([&rightPane, &selectItemData](Rml::Event&) {
populate_select_item_picker(rightPane, selectItemData);
}),
rightPane, [&selectItemData](Pane& pane) {
populate_select_item_picker(pane, selectItemData);
});
};
genSelectItemComboBox("Equip X", get_player_status()->mSelectItem[0]);
@@ -1369,80 +1367,80 @@ EditorWindow::EditorWindow() {
genSelectItemComboBox("Combo Equip X", get_player_status()->mMixItem[0]);
genSelectItemComboBox("Combo Equip Y", get_player_status()->mMixItem[1]);
leftPane
.add_select_button({
leftPane.register_control(
leftPane.add_select_button({
.key = "Clothes",
.getValue = [] { return get_item_name(get_player_status()->mSelectEquip[0]); },
})
.on_focus([&rightPane](Rml::Event&) { populate_select_clothes_picker(rightPane); });
leftPane
.add_select_button({
}),
rightPane, [](Pane& pane) { populate_select_clothes_picker(pane); });
leftPane.register_control(
leftPane.add_select_button({
.key = "Sword",
.getValue = [] { return get_item_name(get_player_status()->mSelectEquip[1]); },
})
.on_focus([&rightPane](Rml::Event&) {
}),
rightPane, [](Pane& pane) {
populate_select_equip_picker(
rightPane, get_player_status()->mSelectEquip[1], swordEntries);
pane, get_player_status()->mSelectEquip[1], swordEntries);
});
leftPane
.add_select_button({
leftPane.register_control(
leftPane.add_select_button({
.key = "Shield",
.getValue = [] { return get_item_name(get_player_status()->mSelectEquip[2]); },
})
.on_focus([&rightPane](Rml::Event&) {
}),
rightPane, [](Pane& pane) {
populate_select_equip_picker(
rightPane, get_player_status()->mSelectEquip[2], shieldEntries);
pane, get_player_status()->mSelectEquip[2], shieldEntries);
});
leftPane
.add_select_button({
leftPane.register_control(
leftPane.add_select_button({
.key = "Scent",
.getValue = [] { return get_item_name(get_player_status()->mSelectEquip[3]); },
})
.on_focus([&rightPane](Rml::Event&) {
}),
rightPane, [](Pane& pane) {
populate_select_equip_picker(
rightPane, get_player_status()->mSelectEquip[3], smellEntries);
pane, get_player_status()->mSelectEquip[3], smellEntries);
});
leftPane
.add_select_button({
leftPane.register_control(
leftPane.add_select_button({
.key = "Wallet Size",
.getValue = [] { return walletSizeNames[get_player_status()->getWalletSize()]; },
})
.on_focus([&rightPane](Rml::Event&) { populate_wallet_picker(rightPane); });
leftPane
.add_select_button({
}),
rightPane, [](Pane& pane) { populate_wallet_picker(pane); });
leftPane.register_control(
leftPane.add_select_button({
.key = "Form",
.getValue = [] { return formNames[get_player_status()->getTransformStatus()]; },
})
.on_focus([&rightPane](Rml::Event&) { populate_form_picker(rightPane); });
}),
rightPane, [](Pane& pane) { populate_form_picker(pane); });
leftPane.add_section("World");
leftPane
.add_child<NumberButton>(NumberButton::Props{
leftPane.register_control(
leftPane.add_child<NumberButton>(NumberButton::Props{
.key = "Day",
.getValue = [] { return get_player_status_b()->getDate(); },
.setValue =
[](int value) { get_player_status_b()->setDate(static_cast<u16>(value)); },
.max = UINT16_MAX,
})
.on_focus([&rightPane](Rml::Event&) { rightPane.clear(); });
leftPane
.add_child<NumberButton>(NumberButton::Props{
}),
rightPane, {});
leftPane.register_control(
leftPane.add_child<NumberButton>(NumberButton::Props{
.key = "Hour",
.getValue = [] { return dKy_getdaytime_hour(); },
.setValue = [](int value) { set_clock_time(value, dKy_getdaytime_minute()); },
.max = 23,
})
.on_focus([&rightPane](Rml::Event&) { rightPane.clear(); });
leftPane
.add_child<NumberButton>(NumberButton::Props{
}),
rightPane, {});
leftPane.register_control(
leftPane.add_child<NumberButton>(NumberButton::Props{
.key = "Minute",
.getValue = [] { return dKy_getdaytime_minute(); },
.setValue = [](int value) { set_clock_time(dKy_getdaytime_hour(), value); },
.max = 59,
})
.on_focus([&rightPane](Rml::Event&) { rightPane.clear(); });
leftPane
.add_child<NumberButton>(NumberButton::Props{
}),
rightPane, {});
leftPane.register_control(
leftPane.add_child<NumberButton>(NumberButton::Props{
.key = "Transform Level",
.getValue =
[] {
@@ -1455,10 +1453,10 @@ EditorWindow::EditorWindow() {
static_cast<u8>((1u << value) - 1u);
},
.max = 3,
})
.on_focus([&rightPane](Rml::Event&) { rightPane.clear(); });
leftPane
.add_child<NumberButton>(NumberButton::Props{
}),
rightPane, {});
leftPane.register_control(
leftPane.add_child<NumberButton>(NumberButton::Props{
.key = "Twilight Clear Level",
.getValue =
[] {
@@ -1471,8 +1469,8 @@ EditorWindow::EditorWindow() {
static_cast<u8>((1u << value) - 1u);
},
.max = 3,
})
.on_focus([&rightPane](Rml::Event&) { rightPane.clear(); });
}),
rightPane, {});
});
add_tab("Location", [this](Rml::Element* content) {
@@ -1481,33 +1479,36 @@ EditorWindow::EditorWindow() {
leftPane.add_section("Save Location");
leftPane
.add_select_button({
.key = "Stage",
.getValue =
[] {
return stage_label_for_file(fixed_string(get_player_return_place()->mName));
},
})
.on_focus([&rightPane](Rml::Event&) {
populate_stage_picker(
rightPane, [] { return fixed_string(get_player_return_place()->mName); },
[](const char* stageFile) {
set_fixed_string(get_player_return_place()->mName, Rml::String(stageFile));
});
})
.register_control(leftPane.add_select_button({
.key = "Stage",
.getValue =
[] {
return stage_label_for_file(
fixed_string(get_player_return_place()->mName));
},
}),
rightPane,
[](Pane& pane) {
populate_stage_picker(
pane, [] { return fixed_string(get_player_return_place()->mName); },
[](const char* stageFile) {
set_fixed_string(
get_player_return_place()->mName, Rml::String(stageFile));
});
})
.set_disabled(true);
leftPane
.add_child<NumberButton>(NumberButton::Props{
leftPane.register_control(
leftPane.add_child<NumberButton>(NumberButton::Props{
.key = "Room",
.getValue = [] { return get_player_return_place()->mRoomNo; },
.setValue =
[](int value) { get_player_return_place()->mRoomNo = static_cast<s8>(value); },
.min = std::numeric_limits<s8>::min(),
.max = std::numeric_limits<s8>::max(),
})
.on_focus([&rightPane](Rml::Event&) { rightPane.clear(); });
leftPane
.add_child<NumberButton>(NumberButton::Props{
}),
rightPane, {});
leftPane.register_control(
leftPane.add_child<NumberButton>(NumberButton::Props{
.key = "Spawn ID",
.getValue = [] { return get_player_return_place()->mPlayerStatus; },
.setValue =
@@ -1515,74 +1516,76 @@ EditorWindow::EditorWindow() {
get_player_return_place()->mPlayerStatus = static_cast<u8>(value);
},
.max = std::numeric_limits<u8>::max(),
})
.on_focus([&rightPane](Rml::Event&) { rightPane.clear(); });
}),
rightPane, {});
leftPane.add_section("Horse Location");
leftPane
.add_child<StringButton>(StringButton::Props{
.key = "Horse Position",
.getValue =
[] {
const auto* horsePlace = get_horse_place();
return fmt::format("{}, {}, {}", static_cast<float>(horsePlace->mPos.x),
static_cast<float>(horsePlace->mPos.y),
static_cast<float>(horsePlace->mPos.z));
},
.setValue =
[](Rml::String value) {
float x = 0.0f;
float y = 0.0f;
float z = 0.0f;
if (parse_vec3(value, x, y, z)) {
auto* horsePlace = get_horse_place();
horsePlace->mPos.x = x;
horsePlace->mPos.y = y;
horsePlace->mPos.z = z;
}
},
})
.on_focus([&rightPane](Rml::Event&) { rightPane.clear(); });
leftPane
.add_child<NumberButton>(NumberButton::Props{
leftPane.register_control(leftPane.add_child<StringButton>(StringButton::Props{
.key = "Horse Position",
.getValue =
[] {
const auto* horsePlace = get_horse_place();
return fmt::format("{}, {}, {}",
static_cast<float>(horsePlace->mPos.x),
static_cast<float>(horsePlace->mPos.y),
static_cast<float>(horsePlace->mPos.z));
},
.setValue =
[](Rml::String value) {
float x = 0.0f;
float y = 0.0f;
float z = 0.0f;
if (parse_vec3(value, x, y, z)) {
auto* horsePlace = get_horse_place();
horsePlace->mPos.x = x;
horsePlace->mPos.y = y;
horsePlace->mPos.z = z;
}
},
}),
rightPane, {});
leftPane.register_control(
leftPane.add_child<NumberButton>(NumberButton::Props{
.key = "Horse Angle",
.getValue = [] { return get_horse_place()->mAngleY; },
.setValue = [](int value) { get_horse_place()->mAngleY = static_cast<s16>(value); },
.min = std::numeric_limits<s16>::min(),
.max = std::numeric_limits<s16>::max(),
})
.on_focus([&rightPane](Rml::Event&) { rightPane.clear(); });
}),
rightPane, {});
leftPane
.add_select_button({
.key = "Horse Stage",
.getValue =
[] { return stage_label_for_file(fixed_string(get_horse_place()->mName)); },
})
.on_focus([&rightPane](Rml::Event&) {
populate_stage_picker(
rightPane, [] { return fixed_string(get_horse_place()->mName); },
[](const char* stageFile) {
set_fixed_string(get_horse_place()->mName, Rml::String(stageFile));
});
})
.register_control(
leftPane.add_select_button({
.key = "Horse Stage",
.getValue =
[] { return stage_label_for_file(fixed_string(get_horse_place()->mName)); },
}),
rightPane,
[](Pane& pane) {
populate_stage_picker(
pane, [] { return fixed_string(get_horse_place()->mName); },
[](const char* stageFile) {
set_fixed_string(get_horse_place()->mName, Rml::String(stageFile));
});
})
.set_disabled(true);
leftPane
.add_child<NumberButton>(NumberButton::Props{
leftPane.register_control(
leftPane.add_child<NumberButton>(NumberButton::Props{
.key = "Horse Room",
.getValue = [] { return get_horse_place()->mRoomNo; },
.setValue = [](int value) { get_horse_place()->mRoomNo = static_cast<s8>(value); },
.min = std::numeric_limits<s8>::min(),
.max = std::numeric_limits<s8>::max(),
})
.on_focus([&rightPane](Rml::Event&) { rightPane.clear(); });
leftPane
.add_child<NumberButton>(NumberButton::Props{
}),
rightPane, {});
leftPane.register_control(
leftPane.add_child<NumberButton>(NumberButton::Props{
.key = "Horse Spawn ID",
.getValue = [] { return get_horse_place()->mSpawnId; },
.setValue = [](int value) { get_horse_place()->mSpawnId = static_cast<u8>(value); },
.max = std::numeric_limits<u8>::max(),
})
.on_focus([&rightPane](Rml::Event&) { rightPane.clear(); });
}),
rightPane, {});
});
add_tab("Inventory", [this](Rml::Element* content) {
@@ -1590,44 +1593,41 @@ EditorWindow::EditorWindow() {
auto& rightPane = add_child<Pane>(content, Pane::Type::Uncontrolled);
leftPane.add_section("Item Wheel");
leftPane.add_button("Default All")
.on_pressed([&rightPane] {
for (int slot = 0; slot < 24; ++slot) {
dComIfGs_setItem(slot, get_slot_default(slot));
}
rightPane.clear();
})
.on_focus([&rightPane](Rml::Event&) { rightPane.clear(); });
leftPane.add_button("Clear All")
.on_pressed([&rightPane] {
for (int slot = 0; slot < 24; ++slot) {
dComIfGs_setItem(slot, dItemNo_NONE_e);
}
rightPane.clear();
})
.on_focus([&rightPane](Rml::Event&) { rightPane.clear(); });
leftPane.register_control(leftPane.add_button("Default All").on_pressed([&rightPane] {
for (int slot = 0; slot < 24; ++slot) {
dComIfGs_setItem(slot, get_slot_default(slot));
}
rightPane.clear();
}),
rightPane, {});
leftPane.register_control(leftPane.add_button("Clear All").on_pressed([&rightPane] {
for (int slot = 0; slot < 24; ++slot) {
dComIfGs_setItem(slot, dItemNo_NONE_e);
}
rightPane.clear();
}),
rightPane, {});
for (int slot = 0; slot < 24; ++slot) {
leftPane
.add_select_button({
leftPane.register_control(
leftPane.add_select_button({
.key = fmt::format("Slot {0:02d}", slot),
.getValue = [slot] { return get_item_name(get_player_item()->mItems[slot]); },
})
.on_focus([&rightPane, slot](
Rml::Event&) { populate_item_slot_picker(rightPane, slot); });
}),
rightPane, [slot](Pane& pane) { populate_item_slot_picker(pane, slot); });
}
leftPane.add_section("Amounts");
leftPane
.add_child<NumberButton>(NumberButton::Props{
leftPane.register_control(
leftPane.add_child<NumberButton>(NumberButton::Props{
.key = "Arrows Amount",
.getValue = [] { return get_player_item_record()->mArrowNum; },
.setValue =
[](int value) { get_player_item_record()->mArrowNum = static_cast<u8>(value); },
.max = std::numeric_limits<u8>::max(),
})
.on_focus([&rightPane](Rml::Event&) { rightPane.clear(); });
leftPane
.add_child<NumberButton>(NumberButton::Props{
}),
rightPane, {});
leftPane.register_control(
leftPane.add_child<NumberButton>(NumberButton::Props{
.key = "Slingshot Amount",
.getValue = [] { return get_player_item_record()->mPachinkoNum; },
.setValue =
@@ -1635,11 +1635,11 @@ EditorWindow::EditorWindow() {
get_player_item_record()->mPachinkoNum = static_cast<u8>(value);
},
.max = std::numeric_limits<u8>::max(),
})
.on_focus([&rightPane](Rml::Event&) { rightPane.clear(); });
}),
rightPane, {});
for (int bag = 0; bag < 3; ++bag) {
leftPane
.add_child<NumberButton>(NumberButton::Props{
leftPane.register_control(
leftPane.add_child<NumberButton>(NumberButton::Props{
.key = fmt::format("Bomb Bag {} Amount", bag + 1),
.getValue = [bag] { return get_player_item_record()->mBombNum[bag]; },
.setValue =
@@ -1647,12 +1647,12 @@ EditorWindow::EditorWindow() {
get_player_item_record()->mBombNum[bag] = static_cast<u8>(value);
},
.max = std::numeric_limits<u8>::max(),
})
.on_focus([&rightPane](Rml::Event&) { rightPane.clear(); });
}),
rightPane, {});
}
for (int bottle = 0; bottle < 4; ++bottle) {
leftPane
.add_child<NumberButton>(NumberButton::Props{
leftPane.register_control(
leftPane.add_child<NumberButton>(NumberButton::Props{
.key = fmt::format("Bottle {} Amount", bottle + 1),
.getValue = [bottle] { return get_player_item_record()->mBottleNum[bottle]; },
.setValue =
@@ -1660,175 +1660,165 @@ EditorWindow::EditorWindow() {
get_player_item_record()->mBottleNum[bottle] = static_cast<u8>(value);
},
.max = std::numeric_limits<u8>::max(),
})
.on_focus([&rightPane](Rml::Event&) { rightPane.clear(); });
}),
rightPane, {});
}
leftPane.add_section("Capacities");
leftPane
.add_child<NumberButton>(NumberButton::Props{
leftPane.register_control(
leftPane.add_child<NumberButton>(NumberButton::Props{
.key = "Arrows Max",
.getValue = [] { return get_player_item_max()->mItemMax[0]; },
.setValue =
[](int value) { get_player_item_max()->mItemMax[0] = static_cast<u8>(value); },
.max = std::numeric_limits<u8>::max(),
})
.on_focus([&rightPane](Rml::Event&) { rightPane.clear(); });
leftPane
.add_child<NumberButton>(NumberButton::Props{
}),
rightPane, {});
leftPane.register_control(
leftPane.add_child<NumberButton>(NumberButton::Props{
.key = "Normal Bombs Max",
.getValue = [] { return get_player_item_max()->mItemMax[1]; },
.setValue =
[](int value) { get_player_item_max()->mItemMax[1] = static_cast<u8>(value); },
.max = std::numeric_limits<u8>::max(),
})
.on_focus([&rightPane](Rml::Event&) { rightPane.clear(); });
leftPane
.add_child<NumberButton>(NumberButton::Props{
}),
rightPane, {});
leftPane.register_control(
leftPane.add_child<NumberButton>(NumberButton::Props{
.key = "Water Bombs Max",
.getValue = [] { return get_player_item_max()->mItemMax[2]; },
.setValue =
[](int value) { get_player_item_max()->mItemMax[2] = static_cast<u8>(value); },
.max = std::numeric_limits<u8>::max(),
})
.on_focus([&rightPane](Rml::Event&) { rightPane.clear(); });
leftPane
.add_child<NumberButton>(NumberButton::Props{
}),
rightPane, {});
leftPane.register_control(
leftPane.add_child<NumberButton>(NumberButton::Props{
.key = "Bomblings Max",
.getValue = [] { return get_player_item_max()->mItemMax[3]; },
.setValue =
[](int value) { get_player_item_max()->mItemMax[3] = static_cast<u8>(value); },
.max = std::numeric_limits<u8>::max(),
})
.on_focus([&rightPane](Rml::Event&) { rightPane.clear(); });
}),
rightPane, {});
leftPane.add_section("Flags");
leftPane
.add_select_button({
.key = "Obtained Items",
.getValue = [] { return "Edit"; },
})
.on_focus([&rightPane](Rml::Event&) { populate_item_flag_picker(rightPane); });
leftPane.register_control(leftPane.add_select_button({
.key = "Obtained Items",
.getValue = [] { return "Edit"; },
}),
rightPane, [](Pane& pane) { populate_item_flag_picker(pane); });
});
add_tab("Collection", [this](Rml::Element* content) {
auto& leftPane = add_child<Pane>(content, Pane::Type::Controlled);
auto& rightPane = add_child<Pane>(content, Pane::Type::Uncontrolled);
leftPane.add_section("Equipment");
leftPane
.add_select_button({
leftPane.register_control(
leftPane.add_select_button({
.key = "Swords",
.getValue =
[] {
return count_label(
count_item_first_bits(swordEntries), swordEntries.size());
},
})
.on_focus([&rightPane](Rml::Event&) {
populate_toggle_group(rightPane, item_toggle_entries(swordEntries));
});
leftPane
.add_select_button({
}),
rightPane,
[](Pane& pane) { populate_toggle_group(pane, item_toggle_entries(swordEntries)); });
leftPane.register_control(
leftPane.add_select_button({
.key = "Shields",
.getValue =
[] {
return count_label(
count_item_first_bits(shieldEntries), shieldEntries.size());
},
})
.on_focus([&rightPane](Rml::Event&) {
populate_toggle_group(rightPane, item_toggle_entries(shieldEntries));
});
leftPane
.add_select_button({
.key = "Clothing",
.getValue = [] { return count_label(count_clothing(), 4); },
})
.on_focus([&rightPane](Rml::Event&) { populate_collect_clothes_picker(rightPane); });
}),
rightPane,
[](Pane& pane) { populate_toggle_group(pane, item_toggle_entries(shieldEntries)); });
leftPane.register_control(leftPane.add_select_button({
.key = "Clothing",
.getValue = [] { return count_label(count_clothing(), 4); },
}),
rightPane, [](Pane& pane) { populate_collect_clothes_picker(pane); });
leftPane.add_section("Key Items");
leftPane
.add_select_button({
leftPane.register_control(
leftPane.add_select_button({
.key = "Fused Shadows",
.getValue =
[] {
return count_label(
count_collect_crystals(fusedShadowEntries), fusedShadowEntries.size());
},
})
.on_focus([&rightPane](Rml::Event&) {
populate_toggle_group(
rightPane, collect_crystal_toggle_entries(fusedShadowEntries));
}),
rightPane, [](Pane& pane) {
populate_toggle_group(pane, collect_crystal_toggle_entries(fusedShadowEntries));
});
leftPane
.add_select_button({
leftPane.register_control(
leftPane.add_select_button({
.key = "Mirror Shards",
.getValue =
[] {
return count_label(
count_collect_mirrors(mirrorShardEntries), mirrorShardEntries.size());
},
})
.on_focus([&rightPane](Rml::Event&) {
populate_toggle_group(rightPane, collect_mirror_toggle_entries(mirrorShardEntries));
}),
rightPane, [](Pane& pane) {
populate_toggle_group(pane, collect_mirror_toggle_entries(mirrorShardEntries));
});
leftPane.add_section("Health & Souls");
leftPane
.add_select_button({
leftPane.register_control(
leftPane.add_select_button({
.key = "Poe Souls",
.getValue = [] { return fmt::format("{} / 60", dComIfGs_getPohSpiritNum()); },
})
.on_focus([&rightPane](Rml::Event&) { populate_poe_souls_picker(rightPane); });
leftPane
.add_select_button({
.key = "Max Life",
.getValue = [] { return max_life_label(); },
})
.on_focus([&rightPane](Rml::Event&) { populate_max_life_picker(rightPane); });
}),
rightPane, [](Pane& pane) { populate_poe_souls_picker(pane); });
leftPane.register_control(leftPane.add_select_button({
.key = "Max Life",
.getValue = [] { return max_life_label(); },
}),
rightPane, [](Pane& pane) { populate_max_life_picker(pane); });
leftPane.add_section("Golden Bugs");
for (const auto& bug : bugSpeciesEntries) {
leftPane
.add_select_button({
.key = bug.name,
.getValue = [bug] { return bug_species_label(bug); },
})
.on_focus([&rightPane, bug](
Rml::Event&) { populate_bug_species_picker(rightPane, bug); });
leftPane.register_control(leftPane.add_select_button({
.key = bug.name,
.getValue = [bug] { return bug_species_label(bug); },
}),
rightPane, [bug](Pane& pane) { populate_bug_species_picker(pane, bug); });
}
leftPane.add_section("Skills");
leftPane
.add_select_button({
leftPane.register_control(
leftPane.add_select_button({
.key = "Hidden Skills",
.getValue =
[] {
return count_label(
count_event_bits(hiddenSkillEntries), hiddenSkillEntries.size());
},
})
.on_focus([&rightPane](Rml::Event&) {
populate_toggle_group(rightPane, event_toggle_entries(hiddenSkillEntries));
}),
rightPane, [](Pane& pane) {
populate_toggle_group(pane, event_toggle_entries(hiddenSkillEntries));
});
leftPane.add_section("Logs");
leftPane
.add_select_button({
leftPane.register_control(
leftPane.add_select_button({
.key = "Postman Letters",
.getValue = [] { return count_label(count_letters(), letterSenders.size()); },
})
.on_focus([&rightPane](Rml::Event&) { populate_letters_picker(rightPane); });
}),
rightPane, [](Pane& pane) { populate_letters_picker(pane); });
leftPane.add_section("Fishing Log");
for (const auto& fish : fishSpeciesEntries) {
leftPane
.add_select_button({
.key = fish.name,
.getValue = [fish] { return fish_species_label(fish); },
})
.on_focus([&rightPane, fish](
Rml::Event&) { populate_fish_species_picker(rightPane, fish); });
leftPane.register_control(leftPane.add_select_button({
.key = fish.name,
.getValue = [fish] { return fish_species_label(fish); },
}),
rightPane, [fish](Pane& pane) { populate_fish_species_picker(pane, fish); });
}
});
@@ -1841,8 +1831,8 @@ EditorWindow::EditorWindow() {
auto& rightPane = add_child<Pane>(content, Pane::Type::Uncontrolled);
leftPane.add_section("Records");
leftPane
.add_child<NumberButton>(NumberButton::Props{
leftPane.register_control(
leftPane.add_child<NumberButton>(NumberButton::Props{
.key = "STAR Game Time (ms)",
.getValue =
[] {
@@ -1854,10 +1844,10 @@ EditorWindow::EditorWindow() {
get_minigame()->setHookGameTime(static_cast<u32>(std::max(0, value)));
},
.max = std::numeric_limits<int>::max(),
})
.on_focus([&rightPane](Rml::Event&) { rightPane.clear(); });
leftPane
.add_child<NumberButton>(NumberButton::Props{
}),
rightPane, {});
leftPane.register_control(
leftPane.add_child<NumberButton>(NumberButton::Props{
.key = "Snowboard Race Time (ms)",
.getValue =
[] {
@@ -1869,10 +1859,10 @@ EditorWindow::EditorWindow() {
get_minigame()->setRaceGameTime(static_cast<u32>(std::max(0, value)));
},
.max = std::numeric_limits<int>::max(),
})
.on_focus([&rightPane](Rml::Event&) { rightPane.clear(); });
leftPane
.add_child<NumberButton>(NumberButton::Props{
}),
rightPane, {});
leftPane.register_control(
leftPane.add_child<NumberButton>(NumberButton::Props{
.key = "Fruit-Pop-Flight Score",
.getValue =
[] {
@@ -1884,8 +1874,8 @@ EditorWindow::EditorWindow() {
get_minigame()->setBalloonScore(static_cast<u32>(std::max(0, value)));
},
.max = std::numeric_limits<int>::max(),
})
.on_focus([&rightPane](Rml::Event&) { rightPane.clear(); });
}),
rightPane, {});
});
add_tab("Config", [this](Rml::Element* content) {
@@ -1893,25 +1883,23 @@ EditorWindow::EditorWindow() {
auto& rightPane = add_child<Pane>(content, Pane::Type::Uncontrolled);
leftPane.add_section("Options");
leftPane
.add_child<BoolButton>(BoolButton::Props{
leftPane.register_control(
leftPane.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({
.key = "Target Type",
.getValue = [] { return target_type_label(); },
})
.on_focus([&rightPane](Rml::Event&) { populate_target_type_picker(rightPane); });
leftPane
.add_select_button({
.key = "Sound",
.getValue = [] { return sound_mode_label(); },
})
.on_focus([&rightPane](Rml::Event&) { populate_sound_mode_picker(rightPane); });
}),
rightPane, {});
leftPane.register_control(leftPane.add_select_button({
.key = "Target Type",
.getValue = [] { return target_type_label(); },
}),
rightPane, [](Pane& pane) { populate_target_type_picker(pane); });
leftPane.register_control(leftPane.add_select_button({
.key = "Sound",
.getValue = [] { return sound_mode_label(); },
}),
rightPane, [](Pane& pane) { populate_sound_mode_picker(pane); });
});
}
+40
View File
@@ -75,6 +75,12 @@ Pane::Pane(Rml::Element* parent, Type type) : FluentComponent(createRoot(parent)
childIndex = i;
}
}
// If item already selected, deselect
if (childIndex >= 0 && childIndex < mChildren.size() &&
mChildren[childIndex]->selected())
{
childIndex = -1;
}
set_selected_item(childIndex);
// If the selection was handled locally, don't allow it to bubble up to window
if (event.GetParameter("handled", false)) {
@@ -98,6 +104,40 @@ void Pane::set_selected_item(int index) {
}
}
Component& Pane::register_control(
Component& component, Pane& nextPane, std::function<void(Pane&)> callback) {
component.listen(component.root(), Rml::EventId::Mouseover,
[this, &component, &nextPane, callback](Rml::Event&) {
if (component.disabled()) {
return;
}
bool anySelected = false;
for (const auto& child : mChildren) {
if (child->selected()) {
anySelected = true;
break;
}
}
if (!anySelected) {
nextPane.clear();
if (callback) {
callback(nextPane);
}
}
});
component.listen(component.root(), Rml::EventId::Focus,
[&component, &nextPane, callback = std::move(callback)](Rml::Event&) {
if (component.disabled()) {
return;
}
nextPane.clear();
if (callback) {
callback(nextPane);
}
});
return component;
}
bool Pane::focus() {
// Focus the first selected child
for (const auto& child : mChildren) {
+2
View File
@@ -19,6 +19,8 @@ public:
void update() override;
void set_selected_item(int index);
Component& register_control(
Component& component, Pane& nextPane, std::function<void(Pane&)> callback);
Rml::Element* add_section(const Rml::String& text);
ControlledButton& add_button(ControlledButton::Props props) {
+1 -1
View File
@@ -84,7 +84,7 @@ void SelectButton::update_props(Props props) {
}
bool SelectButton::handle_nav_command(NavCommand cmd) {
if (cmd == NavCommand::Confirm) {
if (cmd == NavCommand::Confirm && mProps.submit) {
mRoot->DispatchEvent(Rml::EventId::Submit, {});
return true;
}
+7 -1
View File
@@ -17,6 +17,7 @@ public:
Rml::String value;
Rml::String icon;
bool modified = false;
bool submit = true;
};
SelectButton(Rml::Element* parent, Props props);
@@ -54,10 +55,15 @@ public:
std::function<Rml::String()> getValue;
std::function<bool()> isDisabled;
std::function<bool()> isModified;
bool submit = true;
};
ControlledSelectButton(Rml::Element* parent, Props props)
: BaseControlledSelectButton(parent, {std::move(props.key)}),
: BaseControlledSelectButton(parent,
{
.key = std::move(props.key),
.submit = props.submit,
}),
mGetValue(std::move(props.getValue)), mIsDisabled(std::move(props.isDisabled)),
mIsModified(std::move(props.isModified)) {}
+126 -193
View File
@@ -60,11 +60,6 @@ const Rml::String kUnlockFramerateHelpText =
"Uses inter-frame interpolation to enable higher frame rates.<br/><br/>May introduce minor "
"visual artifacts or animation glitches.";
int bloom_multiplier_percent() {
return std::clamp(
static_cast<int>(getSettings().game.bloomMultiplier.getValue() * 100.0f + 0.5f), 0, 100);
}
int float_setting_percent(ConfigVar<float>& var) {
return static_cast<int>(var.getValue() * 100.0f + 0.5f);
}
@@ -83,53 +78,88 @@ struct ConfigBoolProps {
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),
.icon = std::move(props.icon),
.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),
.isModified = [&var] { return var.getValue() != var.getDefaultValue(); },
})
.on_focus([&rightPane, helpText = std::move(props.helpText)](Rml::Event&) {
rightPane.clear();
rightPane.add_rml(helpText);
auto& button = leftPane.add_child<BoolButton>(BoolButton::Props{
.key = std::move(props.key),
.icon = std::move(props.icon),
.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),
.isModified = [&var] { return var.getValue() != var.getDefaultValue(); },
});
leftPane.register_control(
button, rightPane, [helpText = std::move(props.helpText)](Pane& pane) {
pane.clear();
pane.add_rml(helpText);
});
return button;
}
SelectButton& config_percent_select(Pane& leftPane, Pane& rightPane, ConfigVar<float>& var,
Rml::String key, Rml::String helpText, int min, int max, int step = 5,
std::function<bool()> isDisabled = {}) {
return leftPane
.add_child<NumberButton>(NumberButton::Props{
.key = std::move(key),
.getValue = [&var] { return float_setting_percent(var); },
.setValue =
[&var, min, max](int value) {
var.setValue(std::clamp(value, min, max) / 100.0f);
config::Save();
},
.isDisabled = std::move(isDisabled),
.isModified = [&var] { return var.getValue() != var.getDefaultValue(); },
.min = min,
.max = max,
.step = step,
.suffix = "%",
})
.on_focus([&rightPane, helpText = std::move(helpText)](Rml::Event&) {
rightPane.clear();
rightPane.add_text(helpText);
auto& button = leftPane.add_child<NumberButton>(NumberButton::Props{
.key = std::move(key),
.getValue = [&var] { return float_setting_percent(var); },
.setValue =
[&var, min, max](int value) {
var.setValue(std::clamp(value, min, max) / 100.0f);
config::Save();
},
.isDisabled = std::move(isDisabled),
.isModified = [&var] { return var.getValue() != var.getDefaultValue(); },
.min = min,
.max = max,
.step = step,
.suffix = "%",
});
leftPane.register_control(button, rightPane, [helpText = std::move(helpText)](Pane& pane) {
pane.clear();
pane.add_text(helpText);
});
return button;
}
template <typename T>
void overlay_control(
Window& window, Pane& leftPane, Pane& rightPane, ConfigVar<T>& var, const OverlayProps& props) {
leftPane.register_control(
leftPane
.add_select_button({
.key = props.title,
.getValue =
[&var, option = props.option] {
if constexpr (std::is_same_v<T, float>) {
return format_graphics_setting_value(
option, float_setting_percent(var));
} else {
return format_graphics_setting_value(
option, static_cast<int>(var.getValue()));
}
},
.isModified = [&var] { return var.getValue() != var.getDefaultValue(); },
.submit = false,
})
.on_nav_command([&window, props](Rml::Event&, NavCommand cmd) {
if (cmd == NavCommand::Confirm || cmd == NavCommand::Left ||
cmd == NavCommand::Right) {
window.push(std::make_unique<Overlay>(props));
return true;
}
return false;
}),
rightPane, [helpText = props.helpText](Pane& pane) {
pane.clear();
pane.add_text(helpText);
});
}
@@ -176,141 +206,43 @@ SettingsWindow::SettingsWindow() {
});
leftPane.add_section("Resolution");
leftPane
.add_select_button({
.key = "Internal Resolution",
.getValue =
[] {
return format_graphics_setting_value(GraphicsOption::InternalResolution,
getSettings().game.internalResolutionScale.getValue());
},
.isModified =
[] {
return getSettings().game.internalResolutionScale.getValue() !=
getSettings().game.internalResolutionScale.getDefaultValue();
},
})
.on_nav_command([this](Rml::Event&, NavCommand cmd) {
if (cmd == NavCommand::Confirm || cmd == NavCommand::Left ||
cmd == NavCommand::Right) {
push(std::make_unique<Overlay>(OverlayProps{
.option = GraphicsOption::InternalResolution,
.title = "Internal Resolution",
.helpText = kInternalResolutionHelpText,
.valueMin = 0,
.valueMax = 12,
.defaultValue = 0,
}));
return true;
}
return false;
})
.on_focus([&rightPane](Rml::Event&) {
rightPane.clear();
rightPane.add_text(kInternalResolutionHelpText);
overlay_control(*this, leftPane, rightPane, getSettings().game.internalResolutionScale,
OverlayProps{
.option = GraphicsOption::InternalResolution,
.title = "Internal Resolution",
.helpText = kInternalResolutionHelpText,
.valueMin = 0,
.valueMax = 12,
.defaultValue = 0,
});
leftPane
.add_select_button({
.key = "Shadow Resolution",
.getValue =
[] {
return format_graphics_setting_value(GraphicsOption::ShadowResolution,
getSettings().game.shadowResolutionMultiplier.getValue());
},
.isModified =
[] {
return getSettings().game.shadowResolutionMultiplier.getValue() !=
getSettings().game.shadowResolutionMultiplier.getDefaultValue();
},
})
.on_nav_command([this](Rml::Event&, NavCommand cmd) {
if (cmd == NavCommand::Confirm || cmd == NavCommand::Left ||
cmd == NavCommand::Right) {
push(std::make_unique<Overlay>(OverlayProps{
.option = GraphicsOption::ShadowResolution,
.title = "Shadow Resolution",
.helpText = kShadowResolutionHelpText,
.valueMin = 1,
.valueMax = 8,
.defaultValue = 1,
}));
return true;
}
return false;
})
.on_focus([&rightPane](Rml::Event&) {
rightPane.clear();
rightPane.add_text(kShadowResolutionHelpText);
overlay_control(*this, leftPane, rightPane, getSettings().game.shadowResolutionMultiplier,
OverlayProps{
.option = GraphicsOption::ShadowResolution,
.title = "Shadow Resolution",
.helpText = kShadowResolutionHelpText,
.valueMin = 1,
.valueMax = 8,
.defaultValue = 1,
});
leftPane.add_section("Post-Processing");
leftPane
.add_select_button({
.key = "Bloom",
.getValue =
[] {
return format_graphics_setting_value(GraphicsOption::BloomMode,
static_cast<int>(getSettings().game.bloomMode.getValue()));
},
.isModified =
[] {
return getSettings().game.bloomMode.getValue() !=
getSettings().game.bloomMode.getDefaultValue();
},
})
.on_nav_command([this](Rml::Event&, NavCommand cmd) {
if (cmd == NavCommand::Confirm || cmd == NavCommand::Left ||
cmd == NavCommand::Right) {
push(std::make_unique<Overlay>(OverlayProps{
.option = GraphicsOption::BloomMode,
.title = "Bloom",
.helpText = kBloomHelpText,
.valueMin = static_cast<int>(BloomMode::Off),
.valueMax = static_cast<int>(BloomMode::Dusk),
.defaultValue = static_cast<int>(BloomMode::Classic),
}));
return true;
}
return false;
})
.on_focus([&rightPane](Rml::Event&) {
rightPane.clear();
rightPane.add_text(kBloomHelpText);
overlay_control(*this, leftPane, rightPane, getSettings().game.bloomMode,
OverlayProps{
.option = GraphicsOption::BloomMode,
.title = "Bloom",
.helpText = kBloomHelpText,
.valueMin = static_cast<int>(BloomMode::Off),
.valueMax = static_cast<int>(BloomMode::Dusk),
.defaultValue = static_cast<int>(BloomMode::Classic),
});
leftPane
.add_select_button({
.key = "Bloom Brightness",
.getValue =
[] {
return format_graphics_setting_value(
GraphicsOption::BloomMultiplier, bloom_multiplier_percent());
},
.isDisabled =
[] { return getSettings().game.bloomMode.getValue() == BloomMode::Off; },
.isModified =
[] {
return getSettings().game.bloomMultiplier.getValue() !=
getSettings().game.bloomMultiplier.getDefaultValue();
},
})
.on_nav_command([this](Rml::Event&, NavCommand cmd) {
if (cmd == NavCommand::Confirm || cmd == NavCommand::Left ||
cmd == NavCommand::Right) {
push(std::make_unique<Overlay>(OverlayProps{
.option = GraphicsOption::BloomMultiplier,
.title = "Bloom Brightness",
.helpText = kBloomBrightnessHelpText,
.valueMin = 0,
.valueMax = 100,
.defaultValue = 100,
}));
return true;
}
return false;
})
.on_focus([&rightPane](Rml::Event&) {
rightPane.clear();
rightPane.add_text(kBloomBrightnessHelpText);
overlay_control(*this, leftPane, rightPane, getSettings().game.bloomMultiplier,
OverlayProps{
.option = GraphicsOption::BloomMultiplier,
.title = "Bloom Brightness",
.helpText = kBloomBrightnessHelpText,
.valueMin = 0,
.valueMax = 100,
.defaultValue = 100,
});
leftPane.add_section("Rendering");
@@ -344,11 +276,12 @@ SettingsWindow::SettingsWindow() {
};
leftPane.add_section("Controller");
leftPane.add_button("Configure Controller")
.on_pressed([this] { push(std::make_unique<ControllerConfigWindow>()); })
.on_focus([&rightPane](Rml::Event&) {
rightPane.clear();
rightPane.add_text("Open controller binding configuration.");
leftPane.register_control(leftPane.add_button("Configure Controller").on_pressed([this] {
push(std::make_unique<ControllerConfigWindow>());
}),
rightPane, [](Pane& pane) {
pane.clear();
pane.add_text("Open controller binding configuration.");
});
leftPane.add_section("Camera");
@@ -403,8 +336,8 @@ SettingsWindow::SettingsWindow() {
// TODO: Individual sliders for Main Music, Sub Music, Sound Effects, and Fanfare.
leftPane.add_section("Volume");
leftPane
.add_child<NumberButton>(NumberButton::Props{
leftPane.register_control(
leftPane.add_child<NumberButton>(NumberButton::Props{
.key = "Master Volume",
.getValue = [] { return getSettings().audio.masterVolume.getValue(); },
.setValue =
@@ -420,10 +353,10 @@ SettingsWindow::SettingsWindow() {
},
.max = 100,
.suffix = "%",
})
.on_focus([&rightPane](Rml::Event&) {
rightPane.clear();
rightPane.add_text("Adjusts the volume of all sounds in the game.");
}),
rightPane, [](Pane& pane) {
pane.clear();
pane.add_text("Adjusts the volume of all sounds in the game.");
});
leftPane.add_section("Effects");
@@ -493,8 +426,8 @@ SettingsWindow::SettingsWindow() {
"Enables rotating Link in the collection menu with the C-Stick.");
leftPane.add_section("Difficulty");
leftPane
.add_child<NumberButton>(NumberButton::Props{
leftPane.register_control(
leftPane.add_child<NumberButton>(NumberButton::Props{
.key = "Damage Multiplier",
.getValue = [] { return getSettings().game.damageMultiplier.getValue(); },
.setValue =
@@ -511,10 +444,10 @@ SettingsWindow::SettingsWindow() {
.min = 1,
.max = 8,
.suffix = "×",
})
.on_focus([&rightPane](Rml::Event&) {
rightPane.clear();
rightPane.add_text("Multiplies incoming damage.");
}),
rightPane, [](Pane& pane) {
pane.clear();
pane.add_text("Multiplies incoming damage.");
});
addSpeedrunDisabledOption(
"Instant Death", getSettings().game.instantDeath, "Any hit will instantly kill you.");