mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-05-29 08:12:52 -04:00
UI: Prelaunch fade + BG persistence
This commit is contained in:
+23
-5
@@ -11,9 +11,27 @@ body {
|
||||
color: #FFFFFF;
|
||||
background-color: #000000;
|
||||
decorator: image(../prelaunch-bg.png cover left center);
|
||||
filter: opacity(0);
|
||||
transition: filter 1s 0.1s linear-in-out;
|
||||
}
|
||||
|
||||
.menu {
|
||||
body[open] {
|
||||
filter: opacity(1);
|
||||
}
|
||||
|
||||
content {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
filter: opacity(0);
|
||||
transition: filter 0.2s linear-in-out;
|
||||
}
|
||||
|
||||
content[open] {
|
||||
filter: opacity(1);
|
||||
}
|
||||
|
||||
menu {
|
||||
position: absolute;
|
||||
left: 96dp;
|
||||
top: 50%;
|
||||
@@ -28,7 +46,7 @@ body {
|
||||
gap: 48dp;
|
||||
}
|
||||
|
||||
.hero {
|
||||
hero {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
@@ -36,7 +54,7 @@ body {
|
||||
gap: 8dp;
|
||||
}
|
||||
|
||||
.hero img {
|
||||
hero img {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -86,7 +104,7 @@ body {
|
||||
decorator: horizontal-gradient(#FEE685FF #FEE68500);
|
||||
}
|
||||
|
||||
.disk-status {
|
||||
disk-status {
|
||||
position: absolute;
|
||||
left: 96dp;
|
||||
bottom: 72dp;
|
||||
@@ -95,7 +113,7 @@ body {
|
||||
gap: 8dp;
|
||||
}
|
||||
|
||||
.version-info {
|
||||
version-info {
|
||||
position: absolute;
|
||||
right: 96dp;
|
||||
bottom: 72dp;
|
||||
|
||||
@@ -368,7 +368,7 @@ namespace dusk {
|
||||
m_toasts.emplace_back(ImGui::GetIO().MouseSource == ImGuiMouseSource_TouchScreen ?
|
||||
"Tap to toggle menu"s :
|
||||
"Press F1 to toggle menu"s,
|
||||
2.5f);
|
||||
4.f);
|
||||
m_isLaunchInitialized = true;
|
||||
if (getSettings().game.liveSplitEnabled) {
|
||||
dusk::speedrun::connectLiveSplit();
|
||||
|
||||
@@ -17,7 +17,7 @@ Rml::ElementDocument* load_document(const Rml::String& source) {
|
||||
} // namespace
|
||||
|
||||
Document::Document(const Rml::String& source) : mDocument(load_document(source)) {
|
||||
// Block keydown events while hidden (except for Menu)
|
||||
// Block events while hidden (except for Menu command)
|
||||
listen(
|
||||
Rml::EventId::Keydown,
|
||||
[this](Rml::Event& event) {
|
||||
@@ -27,6 +27,15 @@ Document::Document(const Rml::String& source) : mDocument(load_document(source))
|
||||
}
|
||||
},
|
||||
true);
|
||||
const auto blockUnlessVisible = [this](Rml::Event& event) {
|
||||
if (!visible()) {
|
||||
event.StopImmediatePropagation();
|
||||
}
|
||||
};
|
||||
listen(Rml::EventId::Mouseover, blockUnlessVisible, true);
|
||||
listen(Rml::EventId::Click, blockUnlessVisible, true);
|
||||
listen(Rml::EventId::Scroll, blockUnlessVisible, true);
|
||||
listen(Rml::EventId::Focus, blockUnlessVisible, true);
|
||||
|
||||
listen(Rml::EventId::Keydown, [this](Rml::Event& event) {
|
||||
const auto cmd = map_nav_event(event);
|
||||
|
||||
+75
-55
@@ -7,9 +7,9 @@
|
||||
#include "dusk/ui/prelaunch_options.hpp"
|
||||
#include "version.h"
|
||||
|
||||
#include <aurora/lib/window.hpp>
|
||||
#include <SDL3/SDL_dialog.h>
|
||||
#include <SDL3/SDL_filesystem.h>
|
||||
#include <aurora/lib/window.hpp>
|
||||
|
||||
namespace dusk::ui {
|
||||
namespace {
|
||||
@@ -20,26 +20,28 @@ const Rml::String kDocumentSource = R"RML(
|
||||
<link type="text/rcss" href="res/rml/prelaunch.rcss" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="menu">
|
||||
<div class="hero intro-item delay-0">
|
||||
<div class="eyebrow"><span>Twilit Realm</span> presents</div>
|
||||
<img src="res/logo-mascot.png" />
|
||||
</div>
|
||||
<div id="menu-list" />
|
||||
</div>
|
||||
<div class="disk-status intro-item delay-4">
|
||||
<span id="status" class="status" />
|
||||
<span id="detail" class="detail" />
|
||||
</div>
|
||||
<div class="version-info intro-item delay-5">
|
||||
<div class="version">Version <span id="version-text"></span></div>
|
||||
<div class="update"><span>Update available!</span> Download</div>
|
||||
</div>
|
||||
<content id="root" open>
|
||||
<menu>
|
||||
<hero class="intro-item delay-0">
|
||||
<div class="eyebrow"><span>Twilit Realm</span> presents</div>
|
||||
<img src="res/logo-mascot.png" />
|
||||
</hero>
|
||||
<div id="menu-list" />
|
||||
</menu>
|
||||
<disk-status class="intro-item delay-4">
|
||||
<span id="status" class="status" />
|
||||
<span id="detail" class="detail" />
|
||||
</disk-status>
|
||||
<version-info class="intro-item delay-5">
|
||||
<div class="version">Version <span id="version-text"></span></div>
|
||||
<div class="update"><span>Update available!</span> Download</div>
|
||||
</version-info>
|
||||
</content>
|
||||
</body>
|
||||
</rml>
|
||||
)RML";
|
||||
|
||||
static constexpr std::array<SDL_DialogFileFilter, 2> kDiscFileFilters{{
|
||||
constexpr std::array<SDL_DialogFileFilter, 2> kDiscFileFilters{{
|
||||
{"Game Disc Images", "iso;gcm;ciso;gcz;nfs;rvz;wbfs;wia;tgc"},
|
||||
{"All Files", "*"},
|
||||
}};
|
||||
@@ -87,7 +89,8 @@ void ensure_initialized() noexcept {
|
||||
}
|
||||
|
||||
bool is_selected_path_valid() noexcept {
|
||||
return !prelaunch_state().selectedIsoPath.empty() && SDL_GetPathInfo(prelaunch_state().selectedIsoPath.c_str(), nullptr);
|
||||
return !prelaunch_state().selectedIsoPath.empty() &&
|
||||
SDL_GetPathInfo(prelaunch_state().selectedIsoPath.c_str(), nullptr);
|
||||
}
|
||||
|
||||
void open_iso_picker() noexcept {
|
||||
@@ -104,24 +107,26 @@ void apply_intro_animation(Rml::Element* element, const char* delay_class) {
|
||||
element->SetClass(delay_class, true);
|
||||
}
|
||||
|
||||
Prelaunch::Prelaunch() : Document(kDocumentSource) {
|
||||
Prelaunch::Prelaunch() : Document(kDocumentSource), mRoot(mDocument->GetElementById("root")) {
|
||||
ensure_initialized();
|
||||
|
||||
if (auto* menuList = mDocument->GetElementById("menu-list")) {
|
||||
const bool hasValidPath = is_selected_path_valid();
|
||||
mMenuButtons.push_back(std::make_unique<Button>(menuList, hasValidPath ? "Start Game" : "Select Disk Image"));
|
||||
mMenuButtons.push_back(
|
||||
std::make_unique<Button>(menuList, hasValidPath ? "Start Game" : "Select Disk Image"));
|
||||
mMenuButtons.back()->on_pressed([] {
|
||||
if (!is_selected_path_valid()) {
|
||||
open_iso_picker();
|
||||
return;
|
||||
}
|
||||
pop_document();
|
||||
IsGameLaunched = true;
|
||||
pop_document();
|
||||
});
|
||||
apply_intro_animation(mMenuButtons.back()->root(), "delay-1");
|
||||
|
||||
mMenuButtons.push_back(std::make_unique<Button>(menuList, "Options"));
|
||||
mMenuButtons.back()->on_pressed([] { push_document(std::make_unique<PrelaunchOptions>());});
|
||||
mMenuButtons.back()->on_pressed(
|
||||
[] { push_document(std::make_unique<PrelaunchOptions>()); });
|
||||
apply_intro_animation(mMenuButtons.back()->root(), "delay-2");
|
||||
|
||||
mMenuButtons.push_back(std::make_unique<Button>(menuList, "Quit To Desktop"));
|
||||
@@ -129,50 +134,37 @@ Prelaunch::Prelaunch() : Document(kDocumentSource) {
|
||||
apply_intro_animation(mMenuButtons.back()->root(), "delay-3");
|
||||
}
|
||||
|
||||
listen(Rml::EventId::Keydown, [this](Rml::Event& event) {
|
||||
const auto cmd = map_nav_event(event);
|
||||
|
||||
int direction = 0;
|
||||
if (cmd == NavCommand::Down) {
|
||||
direction = 1;
|
||||
} else if (cmd == NavCommand::Up) {
|
||||
direction = -1;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
auto* target = event.GetTargetElement();
|
||||
int focusedButton = -1;
|
||||
for (size_t i = 0; i < mMenuButtons.size(); ++i) {
|
||||
if (mMenuButtons[i]->contains(target)) {
|
||||
focusedButton = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (focusedButton == -1) {
|
||||
return;
|
||||
}
|
||||
int i = focusedButton + direction;
|
||||
while (i >= 0 && i < mMenuButtons.size()) {
|
||||
if (mMenuButtons[i]->focus()) {
|
||||
event.StopPropagation();
|
||||
break;
|
||||
}
|
||||
i += direction;
|
||||
}
|
||||
});
|
||||
|
||||
mDiscStatus = mDocument->GetElementById("status");
|
||||
mDiscDetail = mDocument->GetElementById("detail");
|
||||
mVersion = mDocument->GetElementById("version-text");
|
||||
|
||||
listen(mDocument, Rml::EventId::Transitionend, [this](Rml::Event& event) {
|
||||
auto* target = event.GetTargetElement();
|
||||
if (target != nullptr && target->GetTagName() == "button") {
|
||||
if (target == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (target == mDocument && !mDocument->HasAttribute("open")) {
|
||||
Document::hide();
|
||||
} else if (target->GetTagName() == "button") {
|
||||
target->SetClass("anim-done", true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Prelaunch::show() {
|
||||
Document::show();
|
||||
mDocument->SetAttribute("open", "");
|
||||
mRoot->SetAttribute("open", "");
|
||||
}
|
||||
|
||||
void Prelaunch::hide() {
|
||||
if (IsGameLaunched) {
|
||||
mDocument->RemoveAttribute("open");
|
||||
} else {
|
||||
mRoot->RemoveAttribute("open");
|
||||
}
|
||||
}
|
||||
|
||||
void Prelaunch::update() {
|
||||
ensure_initialized();
|
||||
refresh_path_state();
|
||||
@@ -223,7 +215,35 @@ bool Prelaunch::focus() {
|
||||
return mMenuButtons[0]->focus();
|
||||
}
|
||||
|
||||
bool Prelaunch::visible() const {
|
||||
return mDocument->HasAttribute("open") && mRoot->HasAttribute("open");
|
||||
}
|
||||
|
||||
bool Prelaunch::handle_nav_command(Rml::Event& event, NavCommand cmd) {
|
||||
int direction = 0;
|
||||
if (cmd == NavCommand::Down) {
|
||||
direction = 1;
|
||||
} else if (cmd == NavCommand::Up) {
|
||||
direction = -1;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
auto* target = event.GetTargetElement();
|
||||
int focusedButton = -1;
|
||||
for (size_t i = 0; i < mMenuButtons.size(); ++i) {
|
||||
if (mMenuButtons[i]->contains(target)) {
|
||||
focusedButton = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
int i = (focusedButton + direction) % mMenuButtons.size();
|
||||
while (i >= 0 && i < mMenuButtons.size()) {
|
||||
if (mMenuButtons[i]->focus()) {
|
||||
event.StopPropagation();
|
||||
return true;
|
||||
}
|
||||
i += direction;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,8 +13,11 @@ class Prelaunch : public Document {
|
||||
public:
|
||||
Prelaunch();
|
||||
|
||||
void show() override;
|
||||
void hide() override;
|
||||
void update() override;
|
||||
bool focus() override;
|
||||
bool visible() const override;
|
||||
|
||||
protected:
|
||||
bool handle_nav_command(Rml::Event& event, NavCommand cmd) override;
|
||||
@@ -22,6 +25,7 @@ protected:
|
||||
private:
|
||||
bool mEntranceAnimationStarted = false;
|
||||
std::vector<std::unique_ptr<Button>> mMenuButtons;
|
||||
Rml::Element* mRoot = nullptr;
|
||||
Rml::Element* mDiscStatus = nullptr;
|
||||
Rml::Element* mDiscDetail = nullptr;
|
||||
Rml::Element* mVersion = nullptr;
|
||||
|
||||
Reference in New Issue
Block a user