#include "menu_bar.hpp" #include #include "Z2AudioLib/Z2SeMgr.h" #include "m_Do/m_Do_audio.h" #include "achievements.hpp" #include "aurora/rmlui.hpp" #include "dusk/game_mode.hpp" #include "dusk/livesplit.h" #include "dusk/main.h" #include "dusk/mods/svc/ui.hpp" #include "dusk/settings.h" #include "dusk/speedrun.h" #include "dusk/ui/prelaunch.hpp" #include "editor.hpp" #include "f_pc/f_pc_manager.h" #include "f_pc/f_pc_name.h" #include "imgui.h" #include "modal.hpp" #include "mods_window.hpp" #include "settings.hpp" #include "ui.hpp" #include "warp.hpp" #include "window.hpp" #include #include namespace dusk::ui { namespace { const Rml::String kDocumentSource = R"RML( )RML"; } MenuBar::MenuBar() : Document(kDocumentSource, false, DocumentScope::MenuBar), mRoot(mDocument->GetElementById("popup")) { mTabBar = std::make_unique(mRoot, TabBar::Props{ .onClose = [this] { mDoAud_seStartMenu(kSoundMenuClose); hide(false); }, .autoSelect = 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::hide(mPendingClose); } }); build_tabs(); } void MenuBar::build_tabs() { mTabBar->add_tab("Settings", [this] { push(std::make_unique()); }); if (getSettings().backend.enableAdvancedSettings) { mTabBar->add_tab("Warp", [this] { push(std::make_unique()); }); mTabBar->add_tab("Editor", [this] { push(std::make_unique()); }); } // Only allow us to access achievements if we are playing on a game mode that uses them if (dusk::gamemode::getGameModeManager().isCurrentGameMode(dusk::gamemode::kVanillaGameModeId) || dusk::gamemode::getGameModeManager().isCurrentGameMode(dusk::speedrun::kSpeedrunGameModeId)) { mTabBar->add_tab("Achievements", [this] { push(std::make_unique()); }); } mTabBar->add_tab("Mods", [this] { push(std::make_unique()); }); for (auto& tab : mods::svc::ui_mod_menu_tabs()) { mTabBar->add_tab(tab.label, std::move(tab.onSelected)); } mTabBar->add_tab("Reset", [this] { mTabBar->set_active_tab(-1); const auto dismiss = [](Modal& modal) { modal.pop(); }; push(std::make_unique(Modal::Props{ .title = "Reset Game", .bodyRml = "Unsaved progress will be lost.
" "Tip: You can also reset by holding Start+X+B", .actions = { ModalAction{ .label = "Cancel", .onPressed = [this, dismiss](Modal& modal) { mDoAud_seStartMenu(kSoundWindowClose); dismiss(modal); }, }, ModalAction{ .label = "Reset", .onPressed = [this, dismiss](Modal& modal) { mDoAud_seStartMenu(kSoundClick); if (fpcM_SearchByName(fpcNm_LOGO_SCENE_e)) { dismiss(modal); return; } dismiss(modal); if (gamemode::getGameModeManager().getRegisteredGameModes().size() > 1) { // If game modes are registered, return to prelaunch on reset. prelaunch_state().returnToPrelaunchOnReset = true; } hide(false); JUTGamePad::C3ButtonReset::sResetSwitchPushing = true; }, }, }, .onDismiss = dismiss, .icon = "question-mark", })); }); mTabBar->add_tab("Quit", [this] { mTabBar->set_active_tab(-1); const auto dismiss = [](Modal& modal) { modal.pop(); }; push(std::make_unique(Modal::Props{ .title = "Quit Dusklight", .bodyRml = "Unsaved progress will be lost.", .actions = { ModalAction{ .label = "Cancel", .onPressed = [dismiss](Modal& modal) { mDoAud_seStartMenu(kSoundWindowClose); dismiss(modal); }, }, ModalAction{ .label = "Quit", .onPressed = [dismiss](Modal& modal) { mDoAud_seStartMenu(kSoundClick); dismiss(modal); IsRunning = false; }, }, }, .onDismiss = dismiss, .icon = "question-mark", })); }); if (dusk::speedrun::isActive()) { mTabBar->add_tab("Reset Timer", [this] { mTabBar->set_active_tab(-1); mDoAud_seStartMenu(kSoundClick); dusk::speedrun::g_speedrunInfo.reset(); if (getSettings().game.liveSplitEnabled) { dusk::speedrun::reset(); } hide(false); }); } } void MenuBar::show() { Document::show(); mRoot->SetAttribute("open", ""); mTabBar->set_active_tab(-1); if (!mTabBar->focus_tab(mFocusedTabTitle)) { mTabBar->focus(); } } void MenuBar::hide(bool close) { mFocusedTabTitle = mTabBar->focused_tab_title(); mRoot->RemoveAttribute("open"); if (close) { mPendingClose = true; } } void MenuBar::update() { update_safe_area(); Document::update(); } void MenuBar::update_safe_area() noexcept { if (mDocument == nullptr || mTabBar == nullptr) { return; } // Avoid ImGui menu bar if shown if (const auto* viewport = ImGui::GetMainViewport(); viewport != nullptr && mTopMargin != viewport->WorkPos.y) { mTopMargin = viewport->WorkPos.y; mRoot->SetProperty(Rml::PropertyId::MarginTop, Rml::Property(mTopMargin, Rml::Unit::DP)); } Rml::Context* context = mDocument->GetContext(); Insets safeInsets = safe_area_insets(context); safeInsets = { 0.0f, std::round(safeInsets.right), 0.0f, std::round(safeInsets.left), }; if (safeInsets == mTabBarPadding) { return; } mTabBarPadding = safeInsets; auto* tabBar = mTabBar->root(); tabBar->SetProperty( Rml::PropertyId::PaddingRight, Rml::Property(safeInsets.right, Rml::Unit::PX)); tabBar->SetProperty( Rml::PropertyId::PaddingLeft, Rml::Property(safeInsets.left, Rml::Unit::PX)); if (auto* close = tabBar->QuerySelector("close")) { close->SetProperty(Rml::PropertyId::Right, Rml::Property(safeInsets.right + 8.0f * context->GetDensityIndependentPixelRatio(), Rml::Unit::PX)); } } bool MenuBar::visible() const { return mRoot->HasAttribute("open"); } bool MenuBar::handle_nav_command(Rml::Event& event, NavCommand cmd) { if (!getSettings().backend.wasPresetChosen) { return true; } if (cmd == NavCommand::Cancel && visible()) { mDoAud_seStartMenu(kSoundMenuClose); hide(false); return true; } return Document::handle_nav_command(event, cmd); } bool MenuBar::focus() { return mTabBar->focus(); } void MenuBar::refresh_tabs() { auto* menuBar = static_cast(find_document(DocumentScope::MenuBar)); if (menuBar == nullptr) { return; } const auto focusedTitle = menuBar->mTabBar->focused_tab_title(); if (!focusedTitle.empty()) { menuBar->mFocusedTabTitle = focusedTitle; } menuBar->mTabBar->clear_tabs(); menuBar->build_tabs(); if (menuBar->visible() && !menuBar->mTabBar->focus_tab(menuBar->mFocusedTabTitle)) { menuBar->mTabBar->focus(); } } } // namespace dusk::ui