mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-06-15 13:41:31 -04:00
RmlUi improvements (#663)
* rmlui audio * fix menu select sound * Fixes #662 * fix reset logic and fix popup getting stuck closed * fix X button on menu popup * rmlui achievements, and fix open/close bug * presets, achievements css, and menu sounds toggle * forgor * fix b button causing audio when menu not visible
This commit is contained in:
+4
-4
@@ -1449,8 +1449,6 @@ set(DUSK_FILES
|
||||
src/dusk/imgui/ImGuiMenuTools.hpp
|
||||
src/dusk/imgui/ImGuiPreLaunchWindow.cpp
|
||||
src/dusk/imgui/ImGuiPreLaunchWindow.hpp
|
||||
src/dusk/imgui/ImGuiFirstRunPreset.hpp
|
||||
src/dusk/imgui/ImGuiFirstRunPreset.cpp
|
||||
src/dusk/imgui/ImGuiProcessOverlay.cpp
|
||||
src/dusk/imgui/ImGuiCameraOverlay.cpp
|
||||
src/dusk/imgui/ImGuiHeapOverlay.cpp
|
||||
@@ -1461,8 +1459,6 @@ set(DUSK_FILES
|
||||
src/dusk/imgui/ImGuiSaveEditor.cpp
|
||||
src/dusk/imgui/ImGuiStateShare.hpp
|
||||
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
|
||||
@@ -1473,6 +1469,10 @@ set(DUSK_FILES
|
||||
src/dusk/ui/controller_config.hpp
|
||||
src/dusk/ui/document.cpp
|
||||
src/dusk/ui/document.hpp
|
||||
src/dusk/ui/achievements.cpp
|
||||
src/dusk/ui/achievements.hpp
|
||||
src/dusk/ui/preset.cpp
|
||||
src/dusk/ui/preset.hpp
|
||||
src/dusk/ui/editor.cpp
|
||||
src/dusk/ui/editor.hpp
|
||||
src/dusk/ui/event.cpp
|
||||
|
||||
@@ -56,6 +56,7 @@ struct UserSettings {
|
||||
ConfigVar<int> fanfareVolume;
|
||||
ConfigVar<bool> enableReverb;
|
||||
ConfigVar<bool> enableHrtf;
|
||||
ConfigVar<bool> menuSounds;
|
||||
} audio;
|
||||
|
||||
// Game settings
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "Z2AudioLib/Z2EnvSeMgr.h"
|
||||
#include "Z2AudioLib/Z2LinkMgr.h"
|
||||
#include "dusk/audio.h"
|
||||
#include "dusk/settings.h"
|
||||
|
||||
class mDoAud_zelAudio_c : public Z2AudioMgr {
|
||||
public:
|
||||
@@ -132,6 +133,18 @@ inline void mDoAud_seStart(u32 i_sfxID, const Vec* i_sePos, u32 param_2, s8 i_re
|
||||
-1.0f, -1.0f, 0);
|
||||
}
|
||||
|
||||
#if TARGET_PC
|
||||
inline void mDoAud_seStartMenu(u32 i_sfxID) {
|
||||
if (!mDoAud_zelAudio_c::isInitFlag()) {
|
||||
return;
|
||||
}
|
||||
if (!dusk::getSettings().audio.menuSounds.getValue()) {
|
||||
return;
|
||||
}
|
||||
mDoAud_seStart(i_sfxID, nullptr, 0, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
inline void mDoAud_seStartLevel(u32 i_sfxID, const Vec* i_sePos, u32 param_2, s8 i_reverb) {
|
||||
DUSK_AUDIO_SKIP()
|
||||
Z2AudioMgr::getInterface()->seStartLevel(i_sfxID, i_sePos, param_2, i_reverb, 1.0f, 1.0f,
|
||||
|
||||
@@ -256,3 +256,136 @@ icon.warning {
|
||||
decorator: text("" center center);
|
||||
color: #ffcc00;
|
||||
}
|
||||
|
||||
.achievement-row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 10dp;
|
||||
padding: 12dp 0;
|
||||
border-bottom: 1dp rgba(146, 135, 91, 30%);
|
||||
}
|
||||
|
||||
.achievement-info {
|
||||
display: block;
|
||||
flex: 1 1 0;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.achievement-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.achievement-name {
|
||||
flex: 1;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.achievement-name.unlocked {
|
||||
color: #ffa826;
|
||||
}
|
||||
|
||||
.achievement-badge {
|
||||
font-size: 14dp;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.achievement-badge.unlocked {
|
||||
color: #44cc55;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.achievement-badge.locked {
|
||||
color: #cc4444;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.achievement-desc {
|
||||
display: block;
|
||||
color: rgba(224, 219, 200, 55%);
|
||||
font-size: 16dp;
|
||||
margin: 4dp 0 0 0;
|
||||
}
|
||||
|
||||
.achievement-progress {
|
||||
display: block;
|
||||
font-size: 13dp;
|
||||
color: rgba(224, 219, 200, 45%);
|
||||
}
|
||||
|
||||
progressbar {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 6dp;
|
||||
border-radius: 3dp;
|
||||
background-color: rgba(255, 255, 255, 10%);
|
||||
margin: 6dp 0 2dp 0;
|
||||
}
|
||||
|
||||
progressbar.progress-done fill {
|
||||
background-color: #44aa22;
|
||||
border-radius: 3dp;
|
||||
}
|
||||
|
||||
progressbar.progress-ongoing fill {
|
||||
background-color: #2255bb;
|
||||
border-radius: 3dp;
|
||||
}
|
||||
|
||||
button.achievement-clear {
|
||||
flex: 0 0 auto;
|
||||
align-self: center;
|
||||
font-size: 14dp;
|
||||
padding: 2dp 8dp;
|
||||
opacity: 0.45;
|
||||
}
|
||||
|
||||
.preset-dialog {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
padding: 32dp;
|
||||
gap: 20dp;
|
||||
flex: 1 1 0;
|
||||
}
|
||||
|
||||
.preset-title {
|
||||
display: block;
|
||||
font-family: "Fira Sans Condensed";
|
||||
font-weight: bold;
|
||||
font-size: 30dp;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.preset-intro {
|
||||
display: block;
|
||||
font-size: 16dp;
|
||||
text-align: center;
|
||||
color: rgba(224, 219, 200, 65%);
|
||||
}
|
||||
|
||||
.preset-grid {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 20dp;
|
||||
flex: 1 1 0;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.preset-col {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
gap: 12dp;
|
||||
flex: 1 1 0;
|
||||
}
|
||||
|
||||
button.preset-btn {
|
||||
font-size: 22dp;
|
||||
padding: 20dp 16dp;
|
||||
}
|
||||
|
||||
.preset-desc {
|
||||
display: block;
|
||||
font-size: 15dp;
|
||||
color: rgba(224, 219, 200, 55%);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@@ -1,245 +0,0 @@
|
||||
#include "ImGuiAchievements.hpp"
|
||||
#include "ImGuiConfig.hpp"
|
||||
#include "dusk/achievements.h"
|
||||
#include "dusk/settings.h"
|
||||
#include "fmt/format.h"
|
||||
#include "imgui.h"
|
||||
|
||||
namespace dusk {
|
||||
|
||||
void ImGuiAchievements::notify(std::string name) {
|
||||
if (m_notifyTimer <= 0.f) {
|
||||
m_notifyName = std::move(name);
|
||||
m_notifyTimer = NOTIFY_DURATION;
|
||||
} else {
|
||||
m_notifyQueue.push(std::move(name));
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiAchievements::showNotification() {
|
||||
if (!getSettings().game.enableAchievementNotifications.getValue()) {
|
||||
return;
|
||||
}
|
||||
if (m_notifyTimer <= 0.f) {
|
||||
if (m_notifyQueue.empty()) {
|
||||
return;
|
||||
}
|
||||
m_notifyName = std::move(m_notifyQueue.front());
|
||||
m_notifyQueue.pop();
|
||||
m_notifyTimer = NOTIFY_DURATION;
|
||||
}
|
||||
|
||||
m_notifyTimer -= ImGui::GetIO().DeltaTime;
|
||||
|
||||
const float alpha = std::min({
|
||||
m_notifyTimer / NOTIFY_FADE_TIME,
|
||||
(NOTIFY_DURATION - m_notifyTimer) / NOTIFY_FADE_TIME,
|
||||
1.0f
|
||||
});
|
||||
|
||||
const ImGuiViewport* viewport = ImGui::GetMainViewport();
|
||||
const float padding = 12.0f;
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2(viewport->WorkPos.x + viewport->WorkSize.x - padding, viewport->WorkPos.y + padding),
|
||||
ImGuiCond_Always, ImVec2(1.0f, 0.0f)
|
||||
);
|
||||
|
||||
ImGui::SetNextWindowBgAlpha(alpha * 0.92f);
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.08f, 0.06f, 0.01f, alpha * 0.92f));
|
||||
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(1.0f, 0.8f, 0.1f, alpha));
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 1.0f, 1.0f, alpha));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 2.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(14.0f, 10.0f));
|
||||
|
||||
constexpr ImGuiWindowFlags flags =
|
||||
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize |
|
||||
ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing |
|
||||
ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs;
|
||||
|
||||
if (ImGui::Begin("##achievement_notify", nullptr, flags)) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.82f, 0.1f, alpha));
|
||||
ImGui::TextUnformatted("Achievement Unlocked!");
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::Spacing();
|
||||
ImGui::TextUnformatted(m_notifyName.c_str());
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
ImGui::PopStyleVar(2);
|
||||
ImGui::PopStyleColor(3);
|
||||
}
|
||||
|
||||
void ImGuiAchievements::draw(bool& open) {
|
||||
showNotification();
|
||||
|
||||
if (!open) {
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::SetNextWindowSizeConstraints(ImVec2(800, 200), ImVec2(1280, 900));
|
||||
ImGui::SetNextWindowSize(ImVec2(800, 480), ImGuiCond_FirstUseEver);
|
||||
|
||||
if (!ImGui::Begin(
|
||||
"Achievements", &open,
|
||||
ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav)
|
||||
)
|
||||
{
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
const auto achievements = AchievementSystem::get().getAchievements();
|
||||
|
||||
int unlocked = 0;
|
||||
for (const auto& a : achievements) {
|
||||
if (a.unlocked) {
|
||||
++unlocked;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Text("%d / %d achievements unlocked", unlocked, (int)achievements.size());
|
||||
ImGui::SameLine();
|
||||
config::ImGuiCheckbox("Notifications", getSettings().game.enableAchievementNotifications);
|
||||
ImGui::Separator();
|
||||
|
||||
static const struct {
|
||||
AchievementCategory cat;
|
||||
const char* label;
|
||||
ImVec4 color;
|
||||
} ACHIEVEMENT_CATEGORIES[] = {
|
||||
{AchievementCategory::Story, "Story", ImVec4(1.0f, 0.82f, 0.1f, 1.0f)},
|
||||
{AchievementCategory::Collection, "Collection", ImVec4(0.3f, 0.85f, 0.4f, 1.0f)},
|
||||
{AchievementCategory::Challenge, "Challenge", ImVec4(1.0f, 0.65f, 0.15f, 1.0f)},
|
||||
{AchievementCategory::Minigame, "Minigame", ImVec4(0.5f, 0.85f, 1.0f, 1.0f)},
|
||||
{AchievementCategory::Misc, "Misc", ImVec4(0.65f, 0.65f, 0.65f, 1.0f)},
|
||||
{AchievementCategory::Glitched, "Glitched", ImVec4(0.75f, 0.4f, 1.0f, 1.0f)},
|
||||
};
|
||||
|
||||
const float footerHeight = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing();
|
||||
|
||||
if (ImGui::BeginTabBar("##achievement_tabs", ImGuiTabBarFlags_FittingPolicyScroll)) {
|
||||
for (const auto& catInfo : ACHIEVEMENT_CATEGORIES) {
|
||||
int catTotal = 0, catUnlocked = 0;
|
||||
for (const auto& a : achievements) {
|
||||
if (a.category == catInfo.cat) {
|
||||
++catTotal;
|
||||
if (a.unlocked) {
|
||||
++catUnlocked;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (catTotal == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::string tabLabel = fmt::format("{} ({}/{})###{}", catInfo.label, catUnlocked, catTotal, catInfo.label);
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, catInfo.color);
|
||||
const bool tabOpen = ImGui::BeginTabItem(tabLabel.c_str());
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
if (tabOpen) {
|
||||
ImGui::BeginChild(
|
||||
"##cat_list",
|
||||
ImVec2(0, -footerHeight),
|
||||
ImGuiChildFlags_None,
|
||||
ImGuiWindowFlags_NoBackground
|
||||
);
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
for (const auto& a : achievements) {
|
||||
if (a.category != catInfo.cat) {
|
||||
continue;
|
||||
}
|
||||
ImGui::PushID(a.key);
|
||||
ImGui::BeginGroup();
|
||||
|
||||
ImGui::PushStyleColor(
|
||||
ImGuiCol_Text,
|
||||
a.unlocked ?
|
||||
ImVec4(1.0f, 0.65f, 0.15f, 1.0f) :
|
||||
ImGui::GetStyleColorVec4(ImGuiCol_Text)
|
||||
);
|
||||
|
||||
ImGui::TextUnformatted(a.name);
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
const char* statusLabel = a.unlocked ? "[Unlocked]" : "[Locked]";
|
||||
ImGui::SameLine(
|
||||
ImGui::GetContentRegionMax().x -
|
||||
ImGui::CalcTextSize(statusLabel).x
|
||||
);
|
||||
|
||||
if (a.unlocked) {
|
||||
ImGui::TextColored(ImVec4(0.2f, 0.8f, 0.2f, 1.0f), "%s", statusLabel);
|
||||
} else {
|
||||
ImGui::TextColored(ImVec4(0.8f, 0.2f, 0.2f, 1.0f), "%s", statusLabel);
|
||||
}
|
||||
|
||||
ImGui::TextDisabled("%s", a.description);
|
||||
|
||||
if (a.isCounter) {
|
||||
const float fraction = a.goal > 0 ? (float)(a.progress) / (float)(a.goal) : 1.0f;
|
||||
const std::string overlay = fmt::format("{} / {}", a.progress, a.goal);
|
||||
ImGui::PushStyleColor(
|
||||
ImGuiCol_PlotHistogram,
|
||||
a.unlocked ?
|
||||
ImVec4(0.4f, 0.7f, 0.1f, 1.0f) :
|
||||
ImVec4(0.2f, 0.45f, 0.8f, 1.0f)
|
||||
);
|
||||
ImGui::ProgressBar(fraction, ImVec2(-1.0f, 0.0f), overlay.c_str());
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
ImGui::EndGroup();
|
||||
|
||||
if (ImGui::IsItemHovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
|
||||
ImGui::OpenPopup("##ctx");
|
||||
}
|
||||
|
||||
if (ImGui::BeginPopup("##ctx")) {
|
||||
ImGui::TextDisabled("%s", a.name);
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("Clear Achievement")) {
|
||||
AchievementSystem::get().clearOne(a.key);
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
}
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
if (ImGui::Button("Clear All Achievements")) {
|
||||
ImGui::OpenPopup("##confirm_clear");
|
||||
}
|
||||
|
||||
if (ImGui::BeginPopup("##confirm_clear")) {
|
||||
ImGui::Text("Reset all achievement progress?");
|
||||
ImGui::Spacing();
|
||||
if (ImGui::Button("Yes, reset all")) {
|
||||
AchievementSystem::get().clearAll();
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Cancel")) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
} // namespace dusk
|
||||
@@ -1,23 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <queue>
|
||||
#include <string>
|
||||
|
||||
namespace dusk {
|
||||
|
||||
class ImGuiAchievements {
|
||||
public:
|
||||
void draw(bool& open);
|
||||
void notify(std::string name);
|
||||
|
||||
private:
|
||||
void showNotification();
|
||||
|
||||
std::string m_notifyName;
|
||||
float m_notifyTimer = 0.f;
|
||||
std::queue<std::string> m_notifyQueue;
|
||||
static constexpr float NOTIFY_DURATION = 4.0f;
|
||||
static constexpr float NOTIFY_FADE_TIME = 0.5f;
|
||||
};
|
||||
|
||||
} // namespace dusk
|
||||
@@ -10,6 +10,8 @@
|
||||
|
||||
#include "fmt/format.h"
|
||||
#include "ImGuiConsole.hpp"
|
||||
#include "dusk/ui/preset.hpp"
|
||||
#include "dusk/ui/ui.hpp"
|
||||
#include "JSystem/JUtility/JUTGamePad.h"
|
||||
#include "SDL3/SDL_mouse.h"
|
||||
#include "dusk/achievements.h"
|
||||
@@ -20,6 +22,8 @@
|
||||
#include "dusk/livesplit.h"
|
||||
#include "dusk/main.h"
|
||||
#include "dusk/settings.h"
|
||||
#include "f_pc/f_pc_manager.h"
|
||||
#include "f_pc/f_pc_name.h"
|
||||
#include "m_Do/m_Do_controller_pad.h"
|
||||
#include "m_Do/m_Do_main.h"
|
||||
#include "tracy/Tracy.hpp"
|
||||
@@ -259,7 +263,8 @@ namespace dusk {
|
||||
}
|
||||
}
|
||||
|
||||
if ((ImGui::IsKeyDown(ImGuiKey_LeftCtrl) || ImGui::IsKeyDown(ImGuiKey_RightCtrl)) &&
|
||||
if (!fpcM_SearchByName(fpcNm_LOGO_SCENE_e) &&
|
||||
(ImGui::IsKeyDown(ImGuiKey_LeftCtrl) || ImGui::IsKeyDown(ImGuiKey_RightCtrl)) &&
|
||||
ImGui::IsKeyPressed(ImGuiKey_R))
|
||||
{
|
||||
JUTGamePad::C3ButtonReset::sResetSwitchPushing = true;
|
||||
@@ -300,7 +305,10 @@ namespace dusk {
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
if (!getSettings().backend.wasPresetChosen) {
|
||||
m_firstRunPreset.draw();
|
||||
if (!m_presetShown) {
|
||||
m_presetShown = true;
|
||||
dusk::ui::push_document(std::make_unique<dusk::ui::PresetWindow>());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -342,7 +350,7 @@ namespace dusk {
|
||||
m_menuTools.ShowSaveEditor();
|
||||
m_menuTools.ShowStateShare();
|
||||
}
|
||||
m_menuTools.ShowAchievements();
|
||||
m_menuTools.showAchievementNotification();
|
||||
DuskDebugPad(); // temporary, remove later
|
||||
|
||||
// Hide mouse cursor if the F1 menu is not open and the cursor is idle for 3 seconds.
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
#include <aurora/aurora.h>
|
||||
|
||||
#include "ImGuiFirstRunPreset.hpp"
|
||||
#include "ImGuiMenuGame.hpp"
|
||||
#include "ImGuiMenuTools.hpp"
|
||||
#include "ImGuiPreLaunchWindow.hpp"
|
||||
@@ -45,7 +44,7 @@ private:
|
||||
ImVec2 m_dragScrollLastMousePos = {};
|
||||
std::deque<Toast> m_toasts;
|
||||
|
||||
ImGuiFirstRunPreset m_firstRunPreset;
|
||||
bool m_presetShown = false;
|
||||
ImGuiMenuGame m_menuGame;
|
||||
ImGuiPreLaunchWindow m_preLaunchWindow;
|
||||
|
||||
|
||||
@@ -1,141 +0,0 @@
|
||||
#include "ImGuiFirstRunPreset.hpp"
|
||||
|
||||
#include "imgui.h"
|
||||
#include "ImGuiConsole.hpp"
|
||||
#include "ImGuiEngine.hpp"
|
||||
#include "dusk/settings.h"
|
||||
#include "dusk/config.hpp"
|
||||
#include <dusk/dusk.h>
|
||||
|
||||
namespace dusk {
|
||||
|
||||
static void ApplyPresetClassic() {
|
||||
auto& s = getSettings();
|
||||
s.video.lockAspectRatio.setValue(true);
|
||||
s.game.bloomMode.setValue(BloomMode::Classic);
|
||||
AuroraSetViewportPolicy(AURORA_VIEWPORT_FIT);
|
||||
}
|
||||
|
||||
static void ApplyPresetHD() {
|
||||
auto& s = getSettings();
|
||||
s.game.bloomMode.setValue(BloomMode::Classic);
|
||||
s.game.hideTvSettingsScreen.setValue(true);
|
||||
s.game.skipWarningScreen.setValue(true);
|
||||
s.game.noReturnRupees.setValue(true);
|
||||
s.game.disableRupeeCutscenes.setValue(true);
|
||||
s.game.noSwordRecoil.setValue(true);
|
||||
s.game.fastClimbing.setValue(true);
|
||||
s.game.noMissClimbing.setValue(true);
|
||||
s.game.fastTears.setValue(true);
|
||||
s.game.biggerWallets.setValue(true);
|
||||
s.game.invertCameraXAxis.setValue(true);
|
||||
s.game.freeCamera.setValue(true);
|
||||
s.game.no2ndFishForCat.setValue(true);
|
||||
}
|
||||
|
||||
static void ApplyPresetDusk() {
|
||||
ApplyPresetHD();
|
||||
|
||||
auto& s = getSettings();
|
||||
s.game.enableAchievementNotifications.setValue(true);
|
||||
s.game.enableQuickTransform.setValue(true);
|
||||
s.game.instantSaves.setValue(true);
|
||||
s.game.midnasLamentNonStop.setValue(true);
|
||||
s.game.enableFrameInterpolation.setValue(true);
|
||||
s.game.sunsSong.setValue(true);
|
||||
s.game.bloomMode.setValue(BloomMode::Dusk);
|
||||
s.game.autoSave.setValue(true);
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
|
||||
void ImGuiFirstRunPreset::draw() {
|
||||
const char* modalTitle = "Welcome to Dusk!";
|
||||
|
||||
if (m_done) return;
|
||||
|
||||
if (!m_opened) {
|
||||
ImGui::OpenPopup(modalTitle);
|
||||
m_opened = true;
|
||||
}
|
||||
|
||||
const ImGuiViewport* viewport = ImGui::GetMainViewport();
|
||||
ImGui::SetNextWindowPos(viewport->GetCenter(), ImGuiCond_Always, ImVec2(0.5f, 0.5f));
|
||||
ImGui::SetNextWindowSize(ImVec2(800.0f * ImGuiScale(), 0.0f), ImGuiCond_Always);
|
||||
|
||||
if (!ImGui::BeginPopupModal(modalTitle, nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove)) {
|
||||
// force the user to actually pick one, and not just hit escape to skip the dialog
|
||||
m_opened = false;
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::TextWrapped("Choose a preset to get started. You can change any setting later from the Enhancements menu.");
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
int chosen = -1;
|
||||
|
||||
if (ImGui::BeginTable("##presets", 5, ImGuiTableFlags_None)) {
|
||||
ImGui::TableSetupColumn(nullptr, ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableSetupColumn(nullptr, ImGuiTableColumnFlags_WidthFixed, 16.0f * ImGuiScale());
|
||||
ImGui::TableSetupColumn(nullptr, ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableSetupColumn(nullptr, ImGuiTableColumnFlags_WidthFixed, 16.0f * ImGuiScale());
|
||||
ImGui::TableSetupColumn(nullptr, ImGuiTableColumnFlags_WidthStretch);
|
||||
|
||||
ImGui::TableNextRow();
|
||||
|
||||
ImGui::PushFont(ImGuiEngine::fontLarge);
|
||||
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
if (ImGui::Button("Classic##btn", ImVec2(ImGui::GetContentRegionAvail().x, 80.0f * ImGuiScale()))) {
|
||||
chosen = 0;
|
||||
}
|
||||
|
||||
ImGui::TableSetColumnIndex(2);
|
||||
if (ImGui::Button("HD##btn", ImVec2(ImGui::GetContentRegionAvail().x, 80.0f * ImGuiScale()))) {
|
||||
chosen = 1;
|
||||
}
|
||||
|
||||
ImGui::TableSetColumnIndex(4);
|
||||
if (ImGui::Button("Dusk##btn", ImVec2(ImGui::GetContentRegionAvail().x, 80.0f * ImGuiScale())))
|
||||
{
|
||||
chosen = 2;
|
||||
}
|
||||
|
||||
ImGui::PopFont();
|
||||
|
||||
ImGui::TableNextRow();
|
||||
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::Spacing();
|
||||
ImGui::TextWrapped("All enhancements disabled to match the GameCube version. Good for speedrunning or simple nostalgia!");
|
||||
|
||||
ImGui::TableSetColumnIndex(2);
|
||||
ImGui::Spacing();
|
||||
ImGui::TextWrapped("Some enhancements enabled to match the HD version. A good starting point for most players!");
|
||||
|
||||
ImGui::TableSetColumnIndex(4);
|
||||
ImGui::Spacing();
|
||||
ImGui::TextWrapped("More enhancements enabled than the HD preset. Veteran players will appreciate the additional tweaks!");
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
if (chosen >= 0) {
|
||||
if (chosen == 0) ApplyPresetClassic();
|
||||
if (chosen == 1) ApplyPresetHD();
|
||||
if (chosen == 2) ApplyPresetDusk();
|
||||
|
||||
getSettings().backend.wasPresetChosen.setValue(true);
|
||||
config::Save();
|
||||
|
||||
m_done = true;
|
||||
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
} // namespace dusk
|
||||
@@ -1,14 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
namespace dusk {
|
||||
|
||||
class ImGuiFirstRunPreset {
|
||||
public:
|
||||
void draw();
|
||||
|
||||
private:
|
||||
bool m_opened = false;
|
||||
bool m_done = false;
|
||||
};
|
||||
|
||||
} // namespace dusk
|
||||
@@ -66,7 +66,6 @@ namespace dusk {
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
|
||||
ImGui::MenuItem("Achievements", nullptr, &m_showAchievements);
|
||||
|
||||
#if DUSK_CAN_OPEN_DATA_FOLDER
|
||||
ImGui::Separator();
|
||||
@@ -269,11 +268,65 @@ namespace dusk {
|
||||
ImGui::PopFont();
|
||||
}
|
||||
|
||||
void ImGuiMenuTools::ShowAchievements() {
|
||||
m_achievementsWindow.draw(m_showAchievements);
|
||||
void ImGuiMenuTools::notifyAchievement(std::string name) {
|
||||
if (m_notifyTimer <= 0.f) {
|
||||
m_notifyName = std::move(name);
|
||||
m_notifyTimer = NOTIFY_DURATION;
|
||||
} else {
|
||||
m_notifyQueue.push(std::move(name));
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiMenuTools::notifyAchievement(std::string name) {
|
||||
m_achievementsWindow.notify(std::move(name));
|
||||
void ImGuiMenuTools::showAchievementNotification() {
|
||||
if (!getSettings().game.enableAchievementNotifications.getValue()) {
|
||||
return;
|
||||
}
|
||||
if (m_notifyTimer <= 0.f) {
|
||||
if (m_notifyQueue.empty()) {
|
||||
return;
|
||||
}
|
||||
m_notifyName = std::move(m_notifyQueue.front());
|
||||
m_notifyQueue.pop();
|
||||
m_notifyTimer = NOTIFY_DURATION;
|
||||
}
|
||||
|
||||
m_notifyTimer -= ImGui::GetIO().DeltaTime;
|
||||
|
||||
const float alpha = std::min({
|
||||
m_notifyTimer / NOTIFY_FADE_TIME,
|
||||
(NOTIFY_DURATION - m_notifyTimer) / NOTIFY_FADE_TIME,
|
||||
1.0f
|
||||
});
|
||||
|
||||
const ImGuiViewport* viewport = ImGui::GetMainViewport();
|
||||
const float padding = 12.0f;
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2(viewport->WorkPos.x + viewport->WorkSize.x - padding, viewport->WorkPos.y + padding),
|
||||
ImGuiCond_Always, ImVec2(1.0f, 0.0f)
|
||||
);
|
||||
|
||||
ImGui::SetNextWindowBgAlpha(alpha * 0.92f);
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.08f, 0.06f, 0.01f, alpha * 0.92f));
|
||||
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(1.0f, 0.8f, 0.1f, alpha));
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 1.0f, 1.0f, alpha));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 2.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(14.0f, 10.0f));
|
||||
|
||||
constexpr ImGuiWindowFlags flags =
|
||||
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize |
|
||||
ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing |
|
||||
ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs;
|
||||
|
||||
if (ImGui::Begin("##achievement_notify", nullptr, flags)) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.82f, 0.1f, alpha));
|
||||
ImGui::TextUnformatted("Achievement Unlocked!");
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::Spacing();
|
||||
ImGui::TextUnformatted(m_notifyName.c_str());
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
ImGui::PopStyleVar(2);
|
||||
ImGui::PopStyleColor(3);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
#define DUSK_IMGUI_MENUTOOLS_HPP
|
||||
|
||||
#include <aurora/aurora.h>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
|
||||
#include "imgui.h"
|
||||
#include "ImGuiAchievements.hpp"
|
||||
#include "ImGuiSaveEditor.hpp"
|
||||
#include "ImGuiStateShare.hpp"
|
||||
|
||||
@@ -27,8 +27,8 @@ namespace dusk {
|
||||
void ShowAudioDebug();
|
||||
void ShowSaveEditor();
|
||||
void ShowStateShare();
|
||||
void ShowAchievements();
|
||||
void notifyAchievement(std::string name);
|
||||
void showAchievementNotification();
|
||||
|
||||
private:
|
||||
bool m_showDebugOverlay = false;
|
||||
@@ -69,8 +69,11 @@ namespace dusk {
|
||||
bool m_showStateShare = false;
|
||||
ImGuiStateShare m_stateShare;
|
||||
|
||||
bool m_showAchievements = false;
|
||||
ImGuiAchievements m_achievementsWindow;
|
||||
std::string m_notifyName;
|
||||
float m_notifyTimer = 0.f;
|
||||
std::queue<std::string> m_notifyQueue;
|
||||
static constexpr float NOTIFY_DURATION = 4.0f;
|
||||
static constexpr float NOTIFY_FADE_TIME = 0.5f;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ UserSettings g_userSettings = {
|
||||
.fanfareVolume {"audio.fanfareVolume", 100},
|
||||
.enableReverb {"audio.enableReverb", true},
|
||||
.enableHrtf {"audio.enableHrtf", false},
|
||||
.menuSounds {"audio.menuSounds", true},
|
||||
},
|
||||
|
||||
.game = {
|
||||
@@ -135,6 +136,7 @@ void registerSettings() {
|
||||
Register(g_userSettings.audio.fanfareVolume);
|
||||
Register(g_userSettings.audio.enableReverb);
|
||||
Register(g_userSettings.audio.enableHrtf);
|
||||
Register(g_userSettings.audio.menuSounds);
|
||||
|
||||
// Game
|
||||
Register(g_userSettings.game.language);
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
#include "achievements.hpp"
|
||||
|
||||
#include "Z2AudioLib/Z2SeMgr.h"
|
||||
#include "dusk/achievements.h"
|
||||
#include "fmt/format.h"
|
||||
#include "m_Do/m_Do_audio.h"
|
||||
#include "nav_types.hpp"
|
||||
#include "pane.hpp"
|
||||
|
||||
namespace dusk::ui {
|
||||
namespace {
|
||||
|
||||
struct CategoryInfo {
|
||||
AchievementCategory cat;
|
||||
const char* label;
|
||||
};
|
||||
|
||||
constexpr CategoryInfo kCategories[] = {
|
||||
{AchievementCategory::Story, "Story"},
|
||||
{AchievementCategory::Collection, "Collection"},
|
||||
{AchievementCategory::Challenge, "Challenge"},
|
||||
{AchievementCategory::Minigame, "Minigame"},
|
||||
{AchievementCategory::Misc, "Misc"},
|
||||
{AchievementCategory::Glitched, "Glitched"},
|
||||
};
|
||||
|
||||
Rml::String build_achievement_info_rml(const Achievement& a) {
|
||||
Rml::String s = fmt::format(
|
||||
R"(<div class="achievement-header">)"
|
||||
R"(<span class="achievement-name{}">{}</span>)"
|
||||
R"(<span class="achievement-badge{}">{}</span>)"
|
||||
R"(</div>)"
|
||||
R"(<p class="achievement-desc">{}</p>)",
|
||||
a.unlocked ? " unlocked" : "",
|
||||
a.name,
|
||||
a.unlocked ? " unlocked" : " locked",
|
||||
a.unlocked ? "Unlocked" : "Locked",
|
||||
a.description
|
||||
);
|
||||
|
||||
if (a.isCounter) {
|
||||
float fraction = a.goal > 0 ? float(a.progress) / float(a.goal) : 1.0f;
|
||||
s += fmt::format(
|
||||
R"(<progressbar value="{:.3f}" class="{}"/>)"
|
||||
R"(<span class="achievement-progress">{} / {}</span>)",
|
||||
fraction,
|
||||
a.unlocked ? "progress-done" : "progress-ongoing",
|
||||
a.progress,
|
||||
a.goal
|
||||
);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
class AchievementRow : public FluentComponent<AchievementRow> {
|
||||
public:
|
||||
AchievementRow(Rml::Element* parent, const Achievement& a)
|
||||
: FluentComponent(createRowRoot(parent))
|
||||
{
|
||||
auto& btn = add_child<Button>(Button::Props{"×"});
|
||||
mClearButton = &btn;
|
||||
btn.root()->SetClass("achievement-clear", true);
|
||||
|
||||
btn.on_nav_command([this, key = std::string(a.key)](Rml::Event&, NavCommand cmd) {
|
||||
if (cmd == NavCommand::Confirm) {
|
||||
if (mConfirming) {
|
||||
mDoAud_seStartMenu(Z2SE_SY_CURSOR_OK);
|
||||
AchievementSystem::get().clearOne(key.c_str());
|
||||
resetConfirm();
|
||||
} else {
|
||||
mConfirming = true;
|
||||
mClearButton->set_text("Clear?");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (cmd == NavCommand::Cancel && mConfirming) {
|
||||
resetConfirm();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
Component::listen(btn.root(), Rml::EventId::Blur, [this](Rml::Event&) {
|
||||
resetConfirm();
|
||||
});
|
||||
|
||||
auto* infoDiv = append(mRoot, "div");
|
||||
infoDiv->SetClass("achievement-info", true);
|
||||
infoDiv->SetInnerRML(build_achievement_info_rml(a));
|
||||
}
|
||||
|
||||
bool focus() override { return mClearButton->focus(); }
|
||||
|
||||
private:
|
||||
static Rml::Element* createRowRoot(Rml::Element* parent) {
|
||||
auto* doc = parent->GetOwnerDocument();
|
||||
auto elem = doc->CreateElement("div");
|
||||
elem->SetClass("achievement-row", true);
|
||||
return parent->AppendChild(std::move(elem));
|
||||
}
|
||||
|
||||
void resetConfirm() {
|
||||
mConfirming = false;
|
||||
mClearButton->set_text("×");
|
||||
}
|
||||
|
||||
Button* mClearButton = nullptr;
|
||||
bool mConfirming = false;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
AchievementsWindow::AchievementsWindow() {
|
||||
const auto all = AchievementSystem::get().getAchievements();
|
||||
|
||||
for (const auto& catInfo : kCategories) {
|
||||
int catTotal = 0;
|
||||
for (const auto& a : all) {
|
||||
if (a.category == catInfo.cat) {
|
||||
++catTotal;
|
||||
}
|
||||
}
|
||||
if (catTotal == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
add_tab(catInfo.label, [this, cat = catInfo.cat](Rml::Element* content) {
|
||||
const auto achievements = AchievementSystem::get().getAchievements();
|
||||
|
||||
int total = 0, unlocked = 0;
|
||||
for (const auto& a : achievements) {
|
||||
if (a.category == cat) {
|
||||
++total;
|
||||
if (a.unlocked) {
|
||||
++unlocked;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto& pane = add_child<Pane>(content, Pane::Type::Controlled);
|
||||
|
||||
pane.add_section(fmt::format("{} / {} unlocked", unlocked, total));
|
||||
|
||||
for (const auto& a : achievements) {
|
||||
if (a.category != cat) {
|
||||
continue;
|
||||
}
|
||||
pane.add_child<AchievementRow>(a);
|
||||
}
|
||||
|
||||
pane.add_section("Actions");
|
||||
|
||||
auto& clearAllBtn = pane.add_button("Clear All Achievements");
|
||||
auto* clearAllPtr = &clearAllBtn;
|
||||
auto confirmingAll = std::make_shared<bool>(false);
|
||||
|
||||
clearAllBtn.on_nav_command([clearAllPtr, confirmingAll](Rml::Event&, NavCommand cmd) {
|
||||
if (cmd == NavCommand::Confirm) {
|
||||
if (*confirmingAll) {
|
||||
mDoAud_seStartMenu(Z2SE_SY_CURSOR_OK);
|
||||
AchievementSystem::get().clearAll();
|
||||
*confirmingAll = false;
|
||||
clearAllPtr->set_text("Clear All Achievements");
|
||||
} else {
|
||||
*confirmingAll = true;
|
||||
clearAllPtr->set_text("Are you sure?");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (cmd == NavCommand::Cancel && *confirmingAll) {
|
||||
*confirmingAll = false;
|
||||
clearAllPtr->set_text("Clear All Achievements");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
clearAllBtn.listen(Rml::EventId::Blur, [clearAllPtr, confirmingAll](Rml::Event&) {
|
||||
*confirmingAll = false;
|
||||
clearAllPtr->set_text("Clear All Achievements");
|
||||
});
|
||||
|
||||
pane.finalize();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void AchievementsWindow::update() {
|
||||
const auto current = AchievementSystem::get().getAchievements();
|
||||
bool dirty = current.size() != mSnapshot.size();
|
||||
if (!dirty) {
|
||||
for (size_t i = 0; i < current.size(); ++i) {
|
||||
if (current[i].progress != mSnapshot[i].progress ||
|
||||
current[i].unlocked != mSnapshot[i].unlocked)
|
||||
{
|
||||
dirty = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dirty) {
|
||||
mSnapshot = current;
|
||||
refresh_active_tab();
|
||||
}
|
||||
Window::update();
|
||||
}
|
||||
|
||||
} // namespace dusk::ui
|
||||
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "dusk/achievements.h"
|
||||
#include "window.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace dusk::ui {
|
||||
|
||||
class AchievementsWindow : public Window {
|
||||
public:
|
||||
AchievementsWindow();
|
||||
void update() override;
|
||||
|
||||
private:
|
||||
std::vector<Achievement> mSnapshot;
|
||||
};
|
||||
|
||||
} // namespace dusk::ui
|
||||
@@ -1,5 +1,8 @@
|
||||
#include "bool_button.hpp"
|
||||
|
||||
#include "Z2AudioLib/Z2SeMgr.h"
|
||||
#include "m_Do/m_Do_audio.h"
|
||||
|
||||
namespace dusk::ui {
|
||||
|
||||
BoolButton::BoolButton(Rml::Element* parent, Props props)
|
||||
@@ -31,7 +34,9 @@ Rml::String BoolButton::format_value() {
|
||||
|
||||
bool BoolButton::handle_nav_command(NavCommand cmd) {
|
||||
if (cmd == NavCommand::Confirm || cmd == NavCommand::Left || cmd == NavCommand::Right) {
|
||||
mSetValue(!mGetValue());
|
||||
const bool newValue = !mGetValue();
|
||||
mSetValue(newValue);
|
||||
mDoAud_seStartMenu(newValue ? Z2SE_SY_CURSOR_OK : Z2SE_SY_CURSOR_CANCEL);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
#include "ui.hpp"
|
||||
|
||||
#include "Z2AudioLib/Z2SeMgr.h"
|
||||
#include "m_Do/m_Do_audio.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace dusk::ui {
|
||||
@@ -34,6 +37,7 @@ Button& Button::on_pressed(ButtonCallback callback) {
|
||||
// TODO: convert this to a FluentComponent method?
|
||||
on_nav_command([callback = std::move(callback)](Rml::Event&, NavCommand cmd) {
|
||||
if (cmd == NavCommand::Confirm) {
|
||||
mDoAud_seStartMenu(Z2SE_SY_CURSOR_OK);
|
||||
callback();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
#include "aurora/rmlui.hpp"
|
||||
#include "ui.hpp"
|
||||
|
||||
#include "Z2AudioLib/Z2SeMgr.h"
|
||||
#include "m_Do/m_Do_audio.h"
|
||||
|
||||
namespace dusk::ui {
|
||||
namespace {
|
||||
|
||||
@@ -17,7 +20,7 @@ Rml::ElementDocument* load_document(const Rml::String& source) {
|
||||
} // namespace
|
||||
|
||||
Document::Document(const Rml::String& source) : mDocument(load_document(source)) {
|
||||
// Block events while hidden (except for Menu command)
|
||||
// Block events while hidden (except for Menu command); play nav sounds when visible
|
||||
listen(
|
||||
Rml::EventId::Keydown,
|
||||
[this](Rml::Event& event) {
|
||||
@@ -38,8 +41,18 @@ Document::Document(const Rml::String& source) : mDocument(load_document(source))
|
||||
|
||||
listen(Rml::EventId::Keydown, [this](Rml::Event& event) {
|
||||
const auto cmd = map_nav_event(event);
|
||||
if (cmd != NavCommand::None && handle_nav_command(event, cmd)) {
|
||||
if (cmd == NavCommand::None) {
|
||||
return;
|
||||
}
|
||||
auto* prevFocused = mDocument->GetFocusLeafNode();
|
||||
if (handle_nav_command(event, cmd)) {
|
||||
event.StopPropagation();
|
||||
return;
|
||||
}
|
||||
if ((cmd == NavCommand::Up || cmd == NavCommand::Down) &&
|
||||
mDocument->GetFocusLeafNode() != prevFocused)
|
||||
{
|
||||
mDoAud_seStartMenu(Z2SE_SY_NAME_CURSOR);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -100,6 +113,7 @@ bool Document::visible() const {
|
||||
|
||||
bool Document::handle_nav_command(Rml::Event& event, NavCommand cmd) {
|
||||
if (cmd == NavCommand::Menu) {
|
||||
mDoAud_seStartMenu(visible() ? Z2SE_SY_MENU_OUT : Z2SE_SY_MENU_IN);
|
||||
toggle();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -635,7 +635,8 @@ void process_axis_direction(
|
||||
}
|
||||
|
||||
set_pad_button_held(port, heldPadButton, true);
|
||||
const bool chorded = heldPadButton == PAD_TRIGGER_R && is_menu_chord(port);
|
||||
const bool chorded = heldPadButton == PAD_TRIGGER_R && is_menu_chord(port) &&
|
||||
(port >= sMenuChordConsumed.size() || !sMenuChordConsumed[port]);
|
||||
if (chorded) {
|
||||
consume_menu_chord(port, context);
|
||||
}
|
||||
@@ -657,7 +658,7 @@ void process_axis_direction(
|
||||
} // namespace
|
||||
|
||||
void sync_input_block() noexcept {
|
||||
const bool shouldBlock = any_document_visible() || should_block_pad_for_menu_chord();
|
||||
const bool shouldBlock = any_document_visible();
|
||||
if (sPadInputBlocked == shouldBlock) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#include "number_button.hpp"
|
||||
|
||||
#include "Z2AudioLib/Z2SeMgr.h"
|
||||
#include "m_Do/m_Do_audio.h"
|
||||
|
||||
#include <charconv>
|
||||
#include <fmt/format.h>
|
||||
|
||||
@@ -51,11 +54,13 @@ void NumberButton::set_value(Rml::String value) {
|
||||
}
|
||||
|
||||
bool NumberButton::handle_nav_command(NavCommand cmd) {
|
||||
if (cmd == NavCommand::Left) {
|
||||
mSetValue(std::clamp(mGetValue() - mStep, mMin, mMax));
|
||||
return true;
|
||||
} else if (cmd == NavCommand::Right) {
|
||||
mSetValue(std::clamp(mGetValue() + mStep, mMin, mMax));
|
||||
if (cmd == NavCommand::Left || cmd == NavCommand::Right) {
|
||||
const int newValue = std::clamp(
|
||||
mGetValue() + (cmd == NavCommand::Right ? mStep : -mStep), mMin, mMax);
|
||||
if (newValue != mGetValue()) {
|
||||
mSetValue(newValue);
|
||||
mDoAud_seStartMenu(Z2SE_SY_NAME_CURSOR);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return BaseStringButton::handle_nav_command(cmd);
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#include "overlay.hpp"
|
||||
|
||||
#include "Z2AudioLib/Z2SeMgr.h"
|
||||
#include "m_Do/m_Do_audio.h"
|
||||
|
||||
#include <dolphin/gx/GXAurora.h>
|
||||
#include <dolphin/vi.h>
|
||||
#include <fmt/format.h>
|
||||
@@ -146,6 +149,7 @@ void SteppedCarousel::apply(int value) {
|
||||
if (nextValue == currentValue) {
|
||||
return;
|
||||
}
|
||||
mDoAud_seStartMenu(Z2SE_SY_NAME_CURSOR);
|
||||
if (mProps.onChange) {
|
||||
mProps.onChange(nextValue);
|
||||
}
|
||||
@@ -229,6 +233,7 @@ Overlay::Overlay(OverlayProps props)
|
||||
}
|
||||
|
||||
void Overlay::show() {
|
||||
mDoAud_seStartMenu(Z2SE_SY_CURSOR_OK);
|
||||
Document::show();
|
||||
mRoot->SetAttribute("open", "");
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "pane.hpp"
|
||||
|
||||
#include "Z2AudioLib/Z2SeMgr.h"
|
||||
#include "m_Do/m_Do_audio.h"
|
||||
#include "ui.hpp"
|
||||
|
||||
namespace dusk::ui {
|
||||
@@ -56,6 +58,7 @@ Pane::Pane(Rml::Element* parent, Type type) : FluentComponent(createRoot(parent)
|
||||
int i = focusedChild + direction;
|
||||
while (i >= 0 && i < mChildren.size()) {
|
||||
if (mChildren[i]->focus()) {
|
||||
mDoAud_seStartMenu(Z2SE_SY_NAME_CURSOR);
|
||||
event.StopPropagation();
|
||||
break;
|
||||
}
|
||||
|
||||
+24
-5
@@ -2,8 +2,15 @@
|
||||
|
||||
#include <RmlUi/Core.h>
|
||||
|
||||
#include "Z2AudioLib/Z2SeMgr.h"
|
||||
#include "m_Do/m_Do_audio.h"
|
||||
|
||||
#include "aurora/rmlui.hpp"
|
||||
#include "dusk/main.h"
|
||||
#include "dusk/settings.h"
|
||||
#include "f_pc/f_pc_manager.h"
|
||||
#include "f_pc/f_pc_name.h"
|
||||
#include "achievements.hpp"
|
||||
#include "editor.hpp"
|
||||
#include "imgui.h"
|
||||
#include "settings.hpp"
|
||||
@@ -40,19 +47,23 @@ Popup::Popup() : Document(kDocumentSource), mRoot(mDocument->GetElementById("pop
|
||||
// // TODO
|
||||
// });
|
||||
mTabBar->add_tab("Editor", [this] { push(std::make_unique<EditorWindow>()); });
|
||||
mTabBar->add_tab("Achievements", [this] { push(std::make_unique<AchievementsWindow>()); });
|
||||
mTabBar->add_tab("Reset", [this] {
|
||||
JUTGamePad::C3ButtonReset::sResetSwitchPushing = true;
|
||||
mTabBar->set_active_tab(-1);
|
||||
if (fpcM_SearchByName(fpcNm_LOGO_SCENE_e)) {
|
||||
return;
|
||||
}
|
||||
JUTGamePad::C3ButtonReset::sResetSwitchPushing = true;
|
||||
hide(false);
|
||||
});
|
||||
mTabBar->add_tab("Quit", [] { IsRunning = false; });
|
||||
|
||||
// Hide document after transition completion
|
||||
|
||||
listen(mRoot, Rml::EventId::Transitionend, [this](Rml::Event& event) {
|
||||
if (event.GetTargetElement() == mRoot && !mRoot->HasAttribute("open") &&
|
||||
Document::visible())
|
||||
Document::visible() && mPendingClose)
|
||||
{
|
||||
Document::hide(mPendingClose);
|
||||
Document::hide(true);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -64,9 +75,13 @@ void Popup::show() {
|
||||
Document::show();
|
||||
mRoot->SetAttribute("open", "");
|
||||
mTabBar->set_active_tab(-1);
|
||||
if (!mTabBar->focus_tab(mFocusedTabIndex)) {
|
||||
mTabBar->focus();
|
||||
}
|
||||
}
|
||||
|
||||
void Popup::hide(bool close) {
|
||||
mFocusedTabIndex = mTabBar->focused_tab_index();
|
||||
mRoot->RemoveAttribute("open");
|
||||
if (close) {
|
||||
mPendingClose = true;
|
||||
@@ -121,7 +136,11 @@ bool Popup::visible() const {
|
||||
}
|
||||
|
||||
bool Popup::handle_nav_command(Rml::Event& event, NavCommand cmd) {
|
||||
if (cmd == NavCommand::Cancel) {
|
||||
if (!getSettings().backend.wasPresetChosen) {
|
||||
return true;
|
||||
}
|
||||
if (cmd == NavCommand::Cancel && visible()) {
|
||||
mDoAud_seStartMenu(Z2SE_SY_MENU_OUT);
|
||||
hide(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ private:
|
||||
std::unique_ptr<Button> mCloseButton;
|
||||
Insets mTabBarPadding;
|
||||
float mTopMargin = 0.f;
|
||||
int mFocusedTabIndex = -1;
|
||||
};
|
||||
|
||||
} // namespace dusk::ui
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
#include "preset.hpp"
|
||||
|
||||
#include "Z2AudioLib/Z2SeMgr.h"
|
||||
#include "button.hpp"
|
||||
#include "dusk/config.hpp"
|
||||
#include "dusk/settings.h"
|
||||
#include "m_Do/m_Do_audio.h"
|
||||
#include "ui.hpp"
|
||||
|
||||
#include <dolphin/gx/GXAurora.h>
|
||||
|
||||
namespace dusk::ui {
|
||||
namespace {
|
||||
|
||||
void applyPresetClassic() {
|
||||
auto& s = getSettings();
|
||||
s.video.lockAspectRatio.setValue(true);
|
||||
s.game.bloomMode.setValue(BloomMode::Classic);
|
||||
AuroraSetViewportPolicy(AURORA_VIEWPORT_FIT);
|
||||
}
|
||||
|
||||
void applyPresetHD() {
|
||||
auto& s = getSettings();
|
||||
s.game.bloomMode.setValue(BloomMode::Classic);
|
||||
s.game.hideTvSettingsScreen.setValue(true);
|
||||
s.game.skipWarningScreen.setValue(true);
|
||||
s.game.noReturnRupees.setValue(true);
|
||||
s.game.disableRupeeCutscenes.setValue(true);
|
||||
s.game.noSwordRecoil.setValue(true);
|
||||
s.game.fastClimbing.setValue(true);
|
||||
s.game.noMissClimbing.setValue(true);
|
||||
s.game.fastTears.setValue(true);
|
||||
s.game.biggerWallets.setValue(true);
|
||||
s.game.invertCameraXAxis.setValue(true);
|
||||
s.game.freeCamera.setValue(true);
|
||||
s.game.no2ndFishForCat.setValue(true);
|
||||
}
|
||||
|
||||
void applyPresetDusk() {
|
||||
applyPresetHD();
|
||||
auto& s = getSettings();
|
||||
s.game.enableAchievementNotifications.setValue(true);
|
||||
s.game.enableQuickTransform.setValue(true);
|
||||
s.game.instantSaves.setValue(true);
|
||||
s.game.midnasLamentNonStop.setValue(true);
|
||||
s.game.enableFrameInterpolation.setValue(true);
|
||||
s.game.sunsSong.setValue(true);
|
||||
s.game.bloomMode.setValue(BloomMode::Dusk);
|
||||
s.game.autoSave.setValue(true);
|
||||
}
|
||||
|
||||
Rml::Element* createElement(Rml::Element* parent, const Rml::String& tag) {
|
||||
auto* doc = parent->GetOwnerDocument();
|
||||
auto elem = doc->CreateElement(tag);
|
||||
return parent->AppendChild(std::move(elem));
|
||||
}
|
||||
|
||||
const Rml::String kDocumentSource = R"RML(
|
||||
<rml>
|
||||
<head>
|
||||
<link type="text/rcss" href="res/rml/window.rcss" />
|
||||
</head>
|
||||
<body>
|
||||
<window id="window">
|
||||
<div id="preset-dialog" class="preset-dialog"></div>
|
||||
</window>
|
||||
</body>
|
||||
</rml>
|
||||
)RML";
|
||||
|
||||
} // namespace
|
||||
|
||||
PresetWindow::PresetWindow()
|
||||
: Document(kDocumentSource), mRoot(mDocument->GetElementById("window")) {
|
||||
listen(mRoot, Rml::EventId::Transitionend, [this](Rml::Event& event) {
|
||||
if (event.GetTargetElement() == mRoot && !mRoot->HasAttribute("open") &&
|
||||
Document::visible()) {
|
||||
Document::hide(mPendingClose);
|
||||
}
|
||||
});
|
||||
|
||||
auto* dialog = mDocument->GetElementById("preset-dialog");
|
||||
|
||||
auto* title = createElement(dialog, "div");
|
||||
title->SetClass("preset-title", true);
|
||||
title->SetInnerRML("Welcome to Dusk!");
|
||||
|
||||
auto* intro = createElement(dialog, "div");
|
||||
intro->SetClass("preset-intro", true);
|
||||
intro->SetInnerRML(
|
||||
"Choose a preset to get started. "
|
||||
"You can change any setting later from the Enhancements menu.");
|
||||
|
||||
auto* grid = createElement(dialog, "div");
|
||||
grid->SetClass("preset-grid", true);
|
||||
|
||||
struct PresetInfo {
|
||||
const char* name;
|
||||
const char* desc;
|
||||
void (*apply)();
|
||||
};
|
||||
|
||||
static constexpr PresetInfo kPresets[] = {
|
||||
{"Classic",
|
||||
"All enhancements disabled to match the GameCube version. "
|
||||
"Good for speedrunning or simple nostalgia!",
|
||||
applyPresetClassic},
|
||||
{"HD",
|
||||
"Some enhancements enabled to match the HD version. "
|
||||
"A good starting point for most players!",
|
||||
applyPresetHD},
|
||||
{"Dusk",
|
||||
"More enhancements enabled than the HD preset. "
|
||||
"Veteran players will appreciate the additional tweaks!",
|
||||
applyPresetDusk},
|
||||
};
|
||||
|
||||
for (const auto& preset : kPresets) {
|
||||
auto* col = createElement(grid, "div");
|
||||
col->SetClass("preset-col", true);
|
||||
|
||||
auto btn = std::make_unique<Button>(col, Rml::String(preset.name));
|
||||
btn->root()->SetClass("preset-btn", true);
|
||||
btn->on_nav_command([this, apply = preset.apply](Rml::Event&, NavCommand cmd) {
|
||||
if (cmd == NavCommand::Confirm) {
|
||||
apply();
|
||||
getSettings().backend.wasPresetChosen.setValue(true);
|
||||
config::Save();
|
||||
hide(true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
mButtons.push_back(std::move(btn));
|
||||
|
||||
auto* desc = createElement(col, "div");
|
||||
desc->SetClass("preset-desc", true);
|
||||
desc->SetInnerRML(escape(preset.desc));
|
||||
}
|
||||
}
|
||||
|
||||
void PresetWindow::show() {
|
||||
Document::show();
|
||||
mRoot->SetAttribute("open", "");
|
||||
}
|
||||
|
||||
void PresetWindow::hide(bool close) {
|
||||
mRoot->RemoveAttribute("open");
|
||||
mPendingClose = close;
|
||||
}
|
||||
|
||||
bool PresetWindow::visible() const {
|
||||
return mRoot->HasAttribute("open");
|
||||
}
|
||||
|
||||
bool PresetWindow::focus() {
|
||||
if (!mButtons.empty()) {
|
||||
return mButtons.front()->focus();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PresetWindow::handle_nav_command(Rml::Event& event, NavCommand cmd) {
|
||||
if (cmd == NavCommand::Cancel || cmd == NavCommand::Menu) {
|
||||
return true;
|
||||
}
|
||||
int direction = 0;
|
||||
if (cmd == NavCommand::Left) {
|
||||
direction = -1;
|
||||
} else if (cmd == NavCommand::Right) {
|
||||
direction = 1;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
auto* target = event.GetTargetElement();
|
||||
for (int i = 0; i < static_cast<int>(mButtons.size()); ++i) {
|
||||
if (mButtons[i]->contains(target)) {
|
||||
const int next = i + direction;
|
||||
if (next >= 0 && next < static_cast<int>(mButtons.size())) {
|
||||
if (mButtons[next]->focus()) {
|
||||
mDoAud_seStartMenu(Z2SE_SY_NAME_CURSOR);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace dusk::ui
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
#include "component.hpp"
|
||||
#include "document.hpp"
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace dusk::ui {
|
||||
|
||||
class PresetWindow : public Document {
|
||||
public:
|
||||
PresetWindow();
|
||||
|
||||
void show() override;
|
||||
void hide(bool close) override;
|
||||
bool visible() const override;
|
||||
bool focus() override;
|
||||
|
||||
protected:
|
||||
bool handle_nav_command(Rml::Event& event, NavCommand cmd) override;
|
||||
|
||||
private:
|
||||
Rml::Element* mRoot = nullptr;
|
||||
std::vector<std::unique_ptr<Component>> mButtons;
|
||||
};
|
||||
|
||||
} // namespace dusk::ui
|
||||
@@ -179,6 +179,11 @@ SettingsWindow::SettingsWindow() {
|
||||
.helpText = "Emulate surround sound via HRTF. Recommended only for use with headphones!",
|
||||
.onChange = [](bool value) { audio::EnableHrtf = value; },
|
||||
});
|
||||
config_bool_select(leftPane, rightPane, getSettings().audio.menuSounds,
|
||||
{
|
||||
.key = "Dusk Menu Sounds",
|
||||
.helpText = "Play sound effects when navigating the Dusk menu.",
|
||||
});
|
||||
|
||||
leftPane.add_section("Tweaks");
|
||||
config_bool_select(leftPane, rightPane, getSettings().game.noLowHpSound,
|
||||
|
||||
+55
-11
@@ -1,5 +1,8 @@
|
||||
#include "tab_bar.hpp"
|
||||
|
||||
#include "Z2AudioLib/Z2SeMgr.h"
|
||||
#include "m_Do/m_Do_audio.h"
|
||||
|
||||
namespace dusk::ui {
|
||||
namespace {
|
||||
|
||||
@@ -41,7 +44,14 @@ TabBar::TabBar(Rml::Element* parent, Props props)
|
||||
: FluentComponent(createRoot(parent)), mProps(std::move(props)) {
|
||||
if (mProps.onClose) {
|
||||
mRoot->SetAttribute("closable", "");
|
||||
add_child<Button>(Button::Props{}, "close").on_pressed([this] { mProps.onClose(); });
|
||||
add_child<Button>(Button::Props{}, "close").on_nav_command([this](Rml::Event&, NavCommand cmd) {
|
||||
if (cmd == NavCommand::Confirm) {
|
||||
mDoAud_seStartMenu(Z2SE_SY_CURSOR_CANCEL);
|
||||
mProps.onClose();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
mEndSpacer = append(mRoot, "tab-end-spacer");
|
||||
}
|
||||
|
||||
@@ -85,12 +95,14 @@ bool TabBar::focus() {
|
||||
if (mProps.selectedTabIndex >= 0 && mProps.selectedTabIndex < mTabs.size()) {
|
||||
// Try to focus the currently selected tab
|
||||
if (mTabs[mProps.selectedTabIndex].button.focus()) {
|
||||
mLastFocusedTabIndex = mProps.selectedTabIndex;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Otherwise, focus the first enabled tab
|
||||
for (const auto& tab : mTabs) {
|
||||
if (tab.button.focus()) {
|
||||
for (int i = 0; i < static_cast<int>(mTabs.size()); ++i) {
|
||||
if (mTabs[i].button.focus()) {
|
||||
mLastFocusedTabIndex = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -104,7 +116,14 @@ void TabBar::add_tab(const Rml::String& title, TabCallback callback) {
|
||||
callback();
|
||||
}
|
||||
auto& button = add_child<Button>(Button::Props{title}, "tab");
|
||||
button.on_pressed([this, index] { set_active_tab(index); });
|
||||
button.on_nav_command([this, index](Rml::Event&, NavCommand cmd) {
|
||||
if (cmd == NavCommand::Confirm) {
|
||||
mDoAud_seStartMenu(mProps.autoSelect ? Z2SE_SY_NAME_CURSOR : Z2SE_SY_CURSOR_OK);
|
||||
set_active_tab(index);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if (selected) {
|
||||
button.set_selected(true);
|
||||
}
|
||||
@@ -134,6 +153,7 @@ bool TabBar::set_active_tab(int index) {
|
||||
}
|
||||
const auto& tab = mTabs[index];
|
||||
if (tab.button.focus()) {
|
||||
mLastFocusedTabIndex = index;
|
||||
for (int i = 0; i < static_cast<int>(mTabs.size()); ++i) {
|
||||
mTabs[i].button.set_selected(i == index);
|
||||
}
|
||||
@@ -146,11 +166,30 @@ bool TabBar::set_active_tab(int index) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void TabBar::refresh_active_tab() {
|
||||
if (mProps.selectedTabIndex >= 0 &&
|
||||
mProps.selectedTabIndex < static_cast<int>(mTabs.size()))
|
||||
{
|
||||
const auto& tab = mTabs[mProps.selectedTabIndex];
|
||||
if (tab.callback) {
|
||||
tab.callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int TabBar::focused_tab_index() const {
|
||||
return mLastFocusedTabIndex;
|
||||
}
|
||||
|
||||
bool TabBar::focus_tab(int index) {
|
||||
if (index < 0 || index >= mTabs.size() || index == mProps.selectedTabIndex) {
|
||||
return false;
|
||||
}
|
||||
return mTabs[index].button.focus();
|
||||
if (mTabs[index].button.focus()) {
|
||||
mLastFocusedTabIndex = index;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int TabBar::tab_containing(Rml::Element* element) const {
|
||||
@@ -172,18 +211,23 @@ bool TabBar::handle_nav_command(Rml::Event& event, NavCommand cmd) {
|
||||
currentComponent = tab_containing(event.GetTargetElement());
|
||||
}
|
||||
int direction = isNext ? 1 : -1;
|
||||
int i = currentComponent + direction;
|
||||
if (currentComponent == -1) {
|
||||
// If the container itself is focused and right is pressed, focus the first element
|
||||
if (isNext) {
|
||||
i = 0;
|
||||
if (cmd == NavCommand::Left || cmd == NavCommand::Right) {
|
||||
// If the container itself is focused and right is pressed, focus the first element
|
||||
if (!isNext) {
|
||||
return false;
|
||||
}
|
||||
currentComponent = -1;
|
||||
} else {
|
||||
// Otherwise, allow event to bubble
|
||||
// Next/Previous require a currently selected tab to navigate from
|
||||
return false;
|
||||
}
|
||||
}
|
||||
int i = currentComponent + direction;
|
||||
while (i >= 0 && i < mTabs.size()) {
|
||||
if (mProps.autoSelect ? set_active_tab(i) : focus_tab(i)) {
|
||||
const bool changed = mProps.autoSelect ? set_active_tab(i) : focus_tab(i);
|
||||
if (changed) {
|
||||
mDoAud_seStartMenu(Z2SE_SY_NAME_CURSOR);
|
||||
return true;
|
||||
}
|
||||
i += direction;
|
||||
|
||||
@@ -28,7 +28,9 @@ public:
|
||||
|
||||
void add_tab(const Rml::String& title, TabCallback callback);
|
||||
bool set_active_tab(int index);
|
||||
void refresh_active_tab();
|
||||
bool focus_tab(int index);
|
||||
int focused_tab_index() const;
|
||||
bool handle_nav_command(Rml::Event& event, NavCommand cmd);
|
||||
|
||||
private:
|
||||
@@ -38,6 +40,7 @@ private:
|
||||
std::vector<Tab> mTabs;
|
||||
Rml::Element* mEndSpacer = nullptr;
|
||||
bool mRedirectingScroll = false;
|
||||
int mLastFocusedTabIndex = -1;
|
||||
};
|
||||
|
||||
} // namespace dusk::ui
|
||||
|
||||
+16
-3
@@ -6,6 +6,9 @@
|
||||
#include "pane.hpp"
|
||||
#include "ui.hpp"
|
||||
|
||||
#include "Z2AudioLib/Z2SeMgr.h"
|
||||
#include "m_Do/m_Do_audio.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
@@ -136,6 +139,10 @@ bool Window::set_active_tab(int index) {
|
||||
return mTabBar->set_active_tab(index);
|
||||
}
|
||||
|
||||
void Window::refresh_active_tab() {
|
||||
mTabBar->refresh_active_tab();
|
||||
}
|
||||
|
||||
void Window::add_tab(const Rml::String& title, TabBuilder builder) {
|
||||
mTabBar->add_tab(title, [this, builder = std::move(builder)] {
|
||||
clear_content();
|
||||
@@ -168,11 +175,13 @@ bool Window::handle_nav_command(Rml::Event& event, NavCommand cmd) {
|
||||
}
|
||||
}
|
||||
if (cmd == NavCommand::Confirm || cmd == NavCommand::Down) {
|
||||
if (!mContentComponents.empty()) {
|
||||
return mContentComponents.front()->focus();
|
||||
if (!mContentComponents.empty() && mContentComponents.front()->focus()) {
|
||||
mDoAud_seStartMenu(Z2SE_SY_NAME_CURSOR);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (cmd == NavCommand::Cancel) {
|
||||
mDoAud_seStartMenu(Z2SE_SY_CURSOR_CANCEL);
|
||||
pop();
|
||||
return true;
|
||||
}
|
||||
@@ -184,7 +193,11 @@ bool Window::handle_nav_command(Rml::Event& event, NavCommand cmd) {
|
||||
|
||||
bool Window::handle_content_nav(Rml::Event& event, NavCommand cmd) noexcept {
|
||||
if (cmd == NavCommand::Up) {
|
||||
return focus();
|
||||
if (focus()) {
|
||||
mDoAud_seStartMenu(Z2SE_SY_NAME_CURSOR);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} else if (cmd == NavCommand::Cancel) {
|
||||
int currentComponent = -1;
|
||||
for (int i = 0; i < mContentComponents.size(); ++i) {
|
||||
|
||||
@@ -32,6 +32,7 @@ public:
|
||||
|
||||
protected:
|
||||
void add_tab(const Rml::String& title, TabBuilder builder);
|
||||
void refresh_active_tab();
|
||||
void update_safe_area() noexcept;
|
||||
void clear_content() noexcept;
|
||||
bool handle_nav_command(Rml::Event& event, NavCommand cmd) override;
|
||||
|
||||
Reference in New Issue
Block a user