mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-06-04 18:28:45 -04:00
+49
-1
@@ -110,6 +110,47 @@ toast.achievement heading {
|
||||
color: #C2A42D;
|
||||
}
|
||||
|
||||
toast.controller-warning {
|
||||
top: auto;
|
||||
right: auto;
|
||||
bottom: 40dp;
|
||||
left: 50%;
|
||||
width: 440dp;
|
||||
max-width: 90%;
|
||||
transform: translateX(-50%) scale(0.9);
|
||||
}
|
||||
|
||||
toast.controller-warning[open] {
|
||||
transform: translateX(-50%) scale(1);
|
||||
}
|
||||
|
||||
toast.controller-warning heading {
|
||||
color: #C2A42D;
|
||||
}
|
||||
|
||||
toast.menu-notification {
|
||||
top: 40dp;
|
||||
right: auto;
|
||||
bottom: auto;
|
||||
left: 50%;
|
||||
max-width: 90%;
|
||||
transform: translateX(-50%) scale(0.9);
|
||||
}
|
||||
|
||||
toast.menu-notification[open] {
|
||||
transform: translateX(-50%) scale(1);
|
||||
}
|
||||
|
||||
toast.menu-notification message {
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
toast.menu-notification message row {
|
||||
align-items: center;
|
||||
gap: 6dp;
|
||||
}
|
||||
|
||||
icon {
|
||||
font-family: "Material Symbols Rounded";
|
||||
font-weight: normal;
|
||||
@@ -138,6 +179,13 @@ icon.controller {
|
||||
decorator: text("" center center);
|
||||
}
|
||||
|
||||
icon.warning {
|
||||
width: 24dp;
|
||||
height: 24dp;
|
||||
font-size: 24dp;
|
||||
decorator: text("" center center);
|
||||
}
|
||||
|
||||
logo {
|
||||
position: absolute;
|
||||
width: 100dp;
|
||||
@@ -186,4 +234,4 @@ logo img.outer {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -291,10 +291,6 @@ namespace dusk {
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
if (dusk::IsGameLaunched && !m_isLaunchInitialized) {
|
||||
AddToast(ImGui::GetIO().MouseSource == ImGuiMouseSource_TouchScreen ?
|
||||
"3-finger tap to toggle menu"s :
|
||||
"Press F1 to toggle menu"s,
|
||||
4.f);
|
||||
m_isLaunchInitialized = true;
|
||||
if (getSettings().game.liveSplitEnabled) {
|
||||
dusk::speedrun::connectLiveSplit();
|
||||
|
||||
+133
-6
@@ -1,11 +1,13 @@
|
||||
#include "overlay.hpp"
|
||||
|
||||
#include "aurora/lib/logging.hpp"
|
||||
#include "magic_enum.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "dusk/achievements.h"
|
||||
#include "magic_enum.hpp"
|
||||
#include "window.hpp"
|
||||
|
||||
#include <SDL3/SDL_gamepad.h>
|
||||
#include <algorithm>
|
||||
#include <dolphin/pad.h>
|
||||
|
||||
namespace dusk::ui {
|
||||
namespace {
|
||||
@@ -27,6 +29,8 @@ constexpr std::array<std::pair<const char*, const char*>, 3> kAutoSaveLayers{{
|
||||
{"center", "res/org-icon-center.png"},
|
||||
}};
|
||||
|
||||
constexpr auto kMenuNotificationDuration = std::chrono::milliseconds(2500);
|
||||
|
||||
Rml::Element* create_toast(Rml::Element* parent, const Toast& toast) {
|
||||
if (toast.type == "autosave") {
|
||||
auto* logo = append(parent, "logo");
|
||||
@@ -75,6 +79,78 @@ Rml::Element* create_toast(Rml::Element* parent, const Toast& toast) {
|
||||
return elem;
|
||||
}
|
||||
|
||||
Rml::Element* create_controller_warning(Rml::Element* parent) {
|
||||
auto* elem = append(parent, "toast");
|
||||
elem->SetClass("controller-warning", true);
|
||||
|
||||
auto* heading = append(elem, "heading");
|
||||
auto* title = append(heading, "span");
|
||||
title->SetInnerRML("No controller assigned");
|
||||
auto* icon = append(heading, "icon");
|
||||
icon->SetClass("warning", true);
|
||||
|
||||
auto* message = append(elem, "message");
|
||||
auto* content = append(message, "span");
|
||||
content->SetInnerRML("Configure controller port 1 in Settings.");
|
||||
|
||||
return elem;
|
||||
}
|
||||
|
||||
SDL_Gamepad* gamepad_for_port(u32 port) noexcept {
|
||||
const s32 index = PADGetIndexForPort(port);
|
||||
if (index < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
return PADGetSDLGamepadForIndex(static_cast<u32>(index));
|
||||
}
|
||||
|
||||
Rml::String back_button_name() {
|
||||
if (auto* gamepad = gamepad_for_port(PAD_CHAN0)) {
|
||||
switch (SDL_GetGamepadType(gamepad)) {
|
||||
case SDL_GAMEPAD_TYPE_PS3:
|
||||
return "Select";
|
||||
case SDL_GAMEPAD_TYPE_PS4:
|
||||
return "Share";
|
||||
case SDL_GAMEPAD_TYPE_PS5:
|
||||
return "Create";
|
||||
case SDL_GAMEPAD_TYPE_XBOX360:
|
||||
return "Back";
|
||||
case SDL_GAMEPAD_TYPE_XBOXONE:
|
||||
return "View";
|
||||
case SDL_GAMEPAD_TYPE_GAMECUBE:
|
||||
return "R + Start";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return "Back";
|
||||
}
|
||||
|
||||
Rml::Element* create_menu_notification(Rml::Element* parent) {
|
||||
auto* elem = append(parent, "toast");
|
||||
elem->SetClass("menu-notification", true);
|
||||
|
||||
auto* message = append(elem, "message");
|
||||
auto* row = append(message, "row");
|
||||
append(row, "span")->SetInnerRML("Press F1 or");
|
||||
auto* icon = append(row, "icon");
|
||||
icon->SetClass("controller", true);
|
||||
append(row, "span")->SetInnerRML(escape(back_button_name()));
|
||||
append(row, "span")->SetInnerRML("to open menu");
|
||||
|
||||
return elem;
|
||||
}
|
||||
|
||||
void remove_element(Rml::Element*& elem) noexcept {
|
||||
if (elem == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (auto* parent = elem->GetParentNode()) {
|
||||
parent->RemoveChild(elem);
|
||||
}
|
||||
elem = nullptr;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Overlay::Overlay() : Document(kDocumentSource) {
|
||||
@@ -86,6 +162,15 @@ Overlay::Overlay() : Document(kDocumentSource) {
|
||||
{
|
||||
mCurrentToast->SetPseudoClass("done", true);
|
||||
}
|
||||
} else if (mControllerWarning != nullptr &&
|
||||
event.GetTargetElement() == mControllerWarning &&
|
||||
!mControllerWarning->HasAttribute("open"))
|
||||
{
|
||||
mControllerWarning->SetPseudoClass("done", true);
|
||||
} else if (mMenuNotification != nullptr && event.GetTargetElement() == mMenuNotification &&
|
||||
!mMenuNotification->HasAttribute("open"))
|
||||
{
|
||||
mMenuNotification->SetPseudoClass("done", true);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -98,6 +183,49 @@ void Overlay::show() {
|
||||
|
||||
void Overlay::update() {
|
||||
Document::update();
|
||||
if (mDocument == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool showControllerWarning =
|
||||
PADGetIndexForPort(PAD_CHAN0) < 0 && dynamic_cast<Window*>(top_document()) == nullptr;
|
||||
if (showControllerWarning && mControllerWarning == nullptr) {
|
||||
mControllerWarning = create_controller_warning(mDocument);
|
||||
} else if (showControllerWarning && mControllerWarning != nullptr) {
|
||||
mControllerWarning->SetAttribute("open", "");
|
||||
mControllerWarning->SetPseudoClass("opened", true);
|
||||
mControllerWarning->SetPseudoClass("done", false);
|
||||
} else if (!showControllerWarning && mControllerWarning != nullptr) {
|
||||
if (mControllerWarning->IsPseudoClassSet("done") ||
|
||||
!mControllerWarning->IsPseudoClassSet("opened"))
|
||||
{
|
||||
remove_element(mControllerWarning);
|
||||
} else {
|
||||
mControllerWarning->RemoveAttribute("open");
|
||||
}
|
||||
}
|
||||
|
||||
if (mMenuNotification != nullptr) {
|
||||
if (clock::now() >= mMenuNotificationStartTime + kMenuNotificationDuration) {
|
||||
if (mMenuNotification->IsPseudoClassSet("done") ||
|
||||
!mMenuNotification->IsPseudoClassSet("opened"))
|
||||
{
|
||||
remove_element(mMenuNotification);
|
||||
} else {
|
||||
mMenuNotification->RemoveAttribute("open");
|
||||
}
|
||||
} else {
|
||||
mMenuNotification->SetAttribute("open", "");
|
||||
mMenuNotification->SetPseudoClass("opened", true);
|
||||
mMenuNotification->SetPseudoClass("done", false);
|
||||
}
|
||||
}
|
||||
if (consume_menu_notification_request()) {
|
||||
if (mMenuNotification == nullptr) {
|
||||
mMenuNotification = create_menu_notification(mDocument);
|
||||
}
|
||||
mMenuNotificationStartTime = clock::now();
|
||||
}
|
||||
|
||||
auto& toasts = get_toasts();
|
||||
if (mCurrentToast == nullptr) {
|
||||
@@ -123,8 +251,7 @@ void Overlay::update() {
|
||||
// Fallback for large gaps in time where we never actually opened it
|
||||
!mCurrentToast->IsPseudoClassSet("opened"))
|
||||
{
|
||||
mCurrentToast->GetParentNode()->RemoveChild(mCurrentToast);
|
||||
mCurrentToast = nullptr;
|
||||
remove_element(mCurrentToast);
|
||||
toasts.pop_front();
|
||||
} else {
|
||||
mCurrentToast->RemoveAttribute("open");
|
||||
|
||||
@@ -17,7 +17,10 @@ protected:
|
||||
bool handle_nav_command(Rml::Event& event, NavCommand cmd) override;
|
||||
|
||||
Rml::Element* mCurrentToast = nullptr;
|
||||
Rml::Element* mControllerWarning = nullptr;
|
||||
Rml::Element* mMenuNotification = nullptr;
|
||||
clock::time_point mCurrentToastStartTime;
|
||||
clock::time_point mMenuNotificationStartTime;
|
||||
};
|
||||
|
||||
} // namespace dusk::ui
|
||||
|
||||
@@ -178,6 +178,7 @@ Prelaunch::Prelaunch() : Document(kDocumentSource), mRoot(mDocument->GetElementB
|
||||
}
|
||||
|
||||
mDoAud_seStartMenu(kSoundPlay);
|
||||
show_menu_notification();
|
||||
|
||||
if (getSettings().audio.menuSounds) {
|
||||
JAISoundHandle* handle = g_mEnvSeMgr.field_0x144.getHandle();
|
||||
@@ -199,7 +200,7 @@ Prelaunch::Prelaunch() : Document(kDocumentSource), mRoot(mDocument->GetElementB
|
||||
});
|
||||
apply_intro_animation(mMenuButtons.back()->root(), "delay-1");
|
||||
|
||||
mMenuButtons.push_back(std::make_unique<Button>(menuList, "Options"));
|
||||
mMenuButtons.push_back(std::make_unique<Button>(menuList, "Settings"));
|
||||
mMenuButtons.back()->on_pressed([this] {
|
||||
mRestartSuppressed = false;
|
||||
push(std::make_unique<SettingsWindow>(true));
|
||||
|
||||
@@ -27,6 +27,7 @@ std::vector<std::unique_ptr<Document> > sDocumentStack;
|
||||
// Documents that don't participate in the focus stack
|
||||
std::vector<std::unique_ptr<Document> > sPassiveDocuments;
|
||||
std::deque<Toast> sToasts;
|
||||
bool sMenuNotificationRequested = false;
|
||||
|
||||
// Sometimes gamepads can connect and disconnect quickly, especially during
|
||||
// connection negotiation. In this case, we'll receive an _ADDED event for a
|
||||
@@ -361,4 +362,14 @@ std::deque<Toast>& get_toasts() noexcept {
|
||||
return sToasts;
|
||||
}
|
||||
|
||||
void show_menu_notification() noexcept {
|
||||
sMenuNotificationRequested = true;
|
||||
}
|
||||
|
||||
bool consume_menu_notification_request() noexcept {
|
||||
const bool requested = sMenuNotificationRequested;
|
||||
sMenuNotificationRequested = false;
|
||||
return requested;
|
||||
}
|
||||
|
||||
} // namespace dusk::ui
|
||||
|
||||
@@ -84,6 +84,8 @@ Insets safe_area_insets(Rml::Context* context) noexcept;
|
||||
|
||||
void push_toast(Toast toast) noexcept;
|
||||
std::deque<Toast>& get_toasts() noexcept;
|
||||
void show_menu_notification() noexcept;
|
||||
bool consume_menu_notification_request() noexcept;
|
||||
|
||||
const char* battery_icon(SDL_PowerState state, int level) noexcept;
|
||||
const char* connection_state_icon(SDL_JoystickConnectionState state) noexcept;
|
||||
|
||||
Reference in New Issue
Block a user