mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-08-20 21:33:28 -04:00
Refactor command console UI lifecycle
This commit is contained in:
@@ -24,6 +24,7 @@ console {
|
||||
font-family: "Noto Mono";
|
||||
font-size: 14dp;
|
||||
color: #FFFFFF;
|
||||
transition: background-color 0.8s linear-in-out;
|
||||
}
|
||||
|
||||
output {
|
||||
@@ -37,32 +38,46 @@ output {
|
||||
output[open] {
|
||||
height: 480dp;
|
||||
max-height: 480dp;
|
||||
overflow-y: scroll;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
console:not([open]) {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
console:not([open])[fading] {
|
||||
background-color: rgba(0, 0, 0, 0%);
|
||||
}
|
||||
|
||||
console[open] {
|
||||
background-color: rgba(0, 0, 0, 60%);
|
||||
transition: none;
|
||||
}
|
||||
|
||||
line {
|
||||
display: block;
|
||||
white-space: nowrap;
|
||||
opacity: 1;
|
||||
transition: opacity 0.8s linear-in-out;
|
||||
}
|
||||
|
||||
output:not([open]) line[fading] {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
output:not([open]) line[expired] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
output[open] line {
|
||||
opacity: 1;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
line.cmd {
|
||||
color: #FFD966;
|
||||
}
|
||||
|
||||
line.copyable {
|
||||
color: #80C8FF;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
line.copyable:hover {
|
||||
color: #FFFFFF;
|
||||
background-color: rgba(255, 255, 255, 12%);
|
||||
}
|
||||
|
||||
console input {
|
||||
display: none;
|
||||
width: 100%;
|
||||
|
||||
@@ -558,7 +558,7 @@ void runCommand(std::string_view cmdLine, CommandState& state, const CommandOutp
|
||||
}
|
||||
dusk::game_clock::set_sim_rate((float)*hz);
|
||||
}
|
||||
output(fmt::format(FMT_STRING("Sim rate: {} hz"), (int)dusk::game_clock::get_sim_rate()));
|
||||
output(fmt::format(FMT_STRING("Sim rate: {:.0f} hz"), dusk::game_clock::get_sim_rate()));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+167
-72
@@ -2,9 +2,7 @@
|
||||
|
||||
#include <RmlUi/Core.h>
|
||||
#include <RmlUi/Core/Elements/ElementFormControlInput.h>
|
||||
#include <SDL3/SDL_keyboard.h>
|
||||
#include <aurora/rmlui.hpp>
|
||||
#include <imgui.h>
|
||||
|
||||
#include "dusk/settings.h"
|
||||
|
||||
@@ -12,13 +10,12 @@
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "fmt/format.h"
|
||||
#include "ui.hpp"
|
||||
|
||||
namespace dusk::ui {
|
||||
namespace {
|
||||
|
||||
static const Rml::String kDocumentSource = R"RML(
|
||||
const Rml::String kDocumentSource = R"RML(
|
||||
<rml>
|
||||
<head>
|
||||
<link type="text/rcss" href="res/rml/command_console.rcss" />
|
||||
@@ -32,13 +29,13 @@ static const Rml::String kDocumentSource = R"RML(
|
||||
</rml>
|
||||
)RML";
|
||||
|
||||
static bool isCommand(std::string_view text) {
|
||||
bool is_command(std::string_view text) {
|
||||
return text.size() >= 2 && text[0] == '>' && text[1] == ' ';
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
CommandConsole::CommandConsole() : Document(kDocumentSource) {
|
||||
CommandConsole::CommandConsole() : Document(kDocumentSource, false, DocumentScope::CommandConsole) {
|
||||
mConsole = mDocument ? mDocument->GetElementById("console") : nullptr;
|
||||
mOutput = mDocument ? mDocument->GetElementById("console-output") : nullptr;
|
||||
auto* rawInput = mDocument ? mDocument->GetElementById("console-input") : nullptr;
|
||||
@@ -50,22 +47,35 @@ CommandConsole::CommandConsole() : Document(kDocumentSource) {
|
||||
if (!mInputActive) {
|
||||
return;
|
||||
}
|
||||
const auto key = static_cast<Rml::Input::KeyIdentifier>(event.GetParameter<int>("key_identifier", Rml::Input::KI_UNKNOWN));
|
||||
const auto key = static_cast<Rml::Input::KeyIdentifier>(
|
||||
event.GetParameter<int>("key_identifier", Rml::Input::KI_UNKNOWN));
|
||||
if (key == Rml::Input::KI_RETURN) {
|
||||
executeFromInput();
|
||||
execute_from_input();
|
||||
event.StopImmediatePropagation();
|
||||
} else if (key == Rml::Input::KI_ESCAPE) {
|
||||
hide(true);
|
||||
hide(false);
|
||||
event.StopImmediatePropagation();
|
||||
} else if (key == Rml::Input::KI_UP) {
|
||||
navigateHistory(-1);
|
||||
navigate_history(-1);
|
||||
event.StopImmediatePropagation();
|
||||
} else if (key == Rml::Input::KI_DOWN) {
|
||||
navigateHistory(+1);
|
||||
navigate_history(1);
|
||||
event.StopImmediatePropagation();
|
||||
} else if (key == Rml::Input::KI_PRIOR) {
|
||||
scroll_messages(-1);
|
||||
event.StopImmediatePropagation();
|
||||
} else if (key == Rml::Input::KI_NEXT) {
|
||||
scroll_messages(1);
|
||||
event.StopImmediatePropagation();
|
||||
}
|
||||
},
|
||||
true);
|
||||
|
||||
listen(mOutput, Rml::EventId::Scroll, [this](Rml::Event&) {
|
||||
const float bottom =
|
||||
std::max(0.0f, mOutput->GetScrollHeight() - mOutput->GetClientHeight());
|
||||
mStickToBottom = mOutput->GetScrollTop() >= bottom - 4.0f;
|
||||
});
|
||||
}
|
||||
|
||||
bool CommandConsole::handle_nav_command(Rml::Event&, NavCommand) {
|
||||
@@ -73,69 +83,48 @@ bool CommandConsole::handle_nav_command(Rml::Event&, NavCommand) {
|
||||
}
|
||||
|
||||
void CommandConsole::update() {
|
||||
if (!getSettings().backend.enableAdvancedSettings) {
|
||||
if (!getSettings().backend.enableAdvancedSettings || is_prelaunch_open()) {
|
||||
close_input();
|
||||
Document::hide(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const float dt = std::max(ImGui::GetIO().DeltaTime, 0.0f);
|
||||
for (auto& line : mOutputLines) {
|
||||
line.remain -= dt;
|
||||
}
|
||||
const auto [first, last] =
|
||||
std::ranges::remove_if(mOutputLines, [](const OutputLine& l) { return l.remain <= 0.0f; });
|
||||
mOutputLines.erase(first, last);
|
||||
|
||||
if (mOutputLines.empty() && !mInputActive) {
|
||||
Document::hide(mPendingClose);
|
||||
update_message_lifetimes();
|
||||
if (!mInputActive && !has_onscreen_messages()) {
|
||||
Document::hide(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mOutput == nullptr) {
|
||||
return;
|
||||
if (!Document::visible()) {
|
||||
Document::show();
|
||||
}
|
||||
|
||||
Rml::String html;
|
||||
if (mInputActive) {
|
||||
for (const auto& msg : mMsgHistory) {
|
||||
html += isCommand(msg) ? "<line class=\"cmd\">" + escape(msg) + "</line>" : "<line>" + escape(msg) + "</line>";
|
||||
}
|
||||
mOutput->SetAttribute("open", "");
|
||||
} else {
|
||||
const int total = (int)mOutputLines.size();
|
||||
const int startIdx = std::max(0, total - kMaxVisibleLines);
|
||||
for (int i = startIdx; i < total; ++i) {
|
||||
const auto& line = mOutputLines[i];
|
||||
const float alpha = line.remain < kFadeSeconds ? line.remain / kFadeSeconds : 1.0f;
|
||||
const Rml::String cls = isCommand(line.text) ? " class=\"cmd\"" : "";
|
||||
html += fmt::format(FMT_STRING("<line{} style=\"opacity: {:.3f}\">{}</line>"), cls,
|
||||
alpha, escape(line.text));
|
||||
}
|
||||
mOutput->RemoveAttribute("open");
|
||||
}
|
||||
|
||||
mOutput->SetInnerRML(html);
|
||||
|
||||
if (mScrollToBottom && mInputActive) {
|
||||
mOutput->SetScrollTop(1e9f);
|
||||
mScrollToBottom = false;
|
||||
if (mInputActive && mStickToBottom && mOutput != nullptr) {
|
||||
mOutput->SetScrollTop(mOutput->GetScrollHeight() - mOutput->GetClientHeight());
|
||||
}
|
||||
}
|
||||
|
||||
void CommandConsole::show() {
|
||||
if (mDocument != nullptr) {
|
||||
mDocument->Show(Rml::ModalFlag::None, Rml::FocusFlag::None, Rml::ScrollFlag::None);
|
||||
if (mInputActive || !getSettings().backend.enableAdvancedSettings || is_prelaunch_open()) {
|
||||
return;
|
||||
}
|
||||
mInputActive = true;
|
||||
mScrollToBottom = true;
|
||||
mHistoryPos = -1;
|
||||
mStickToBottom = true;
|
||||
if (mConsole != nullptr) {
|
||||
mConsole->SetAttribute("open", "");
|
||||
}
|
||||
if (mOutput != nullptr) {
|
||||
mOutput->SetAttribute("open", "");
|
||||
}
|
||||
if (mInput != nullptr) {
|
||||
mInput->SetValue("");
|
||||
}
|
||||
Document::show();
|
||||
focus();
|
||||
}
|
||||
|
||||
bool CommandConsole::focus() {
|
||||
if (mInput != nullptr) {
|
||||
mInput->SetValue("");
|
||||
if (mInputActive && mInput != nullptr) {
|
||||
aurora::rmlui::set_input_type(aurora::rmlui::InputType::Text);
|
||||
return mInput->Focus(true);
|
||||
}
|
||||
@@ -143,6 +132,30 @@ bool CommandConsole::focus() {
|
||||
}
|
||||
|
||||
void CommandConsole::hide(bool close) {
|
||||
close_input();
|
||||
if (close) {
|
||||
Document::hide(true);
|
||||
} else if (!has_onscreen_messages()) {
|
||||
Document::hide(false);
|
||||
}
|
||||
|
||||
if (auto* doc = top_document()) {
|
||||
doc->focus();
|
||||
}
|
||||
}
|
||||
|
||||
bool CommandConsole::visible() const {
|
||||
return mInputActive && Document::visible();
|
||||
}
|
||||
|
||||
bool CommandConsole::active() const {
|
||||
return mInputActive && Document::active();
|
||||
}
|
||||
|
||||
void CommandConsole::close_input() {
|
||||
if (!mInputActive) {
|
||||
return;
|
||||
}
|
||||
mInputActive = false;
|
||||
mHistoryPos = -1;
|
||||
if (mConsole != nullptr) {
|
||||
@@ -150,32 +163,30 @@ void CommandConsole::hide(bool close) {
|
||||
}
|
||||
if (mInput != nullptr) {
|
||||
mInput->SetValue("");
|
||||
mInput->Blur();
|
||||
}
|
||||
mPendingClose = close;
|
||||
// Immediately refocus
|
||||
if (auto* doc = top_document()) {
|
||||
doc->focus();
|
||||
if (mOutput != nullptr) {
|
||||
mOutput->RemoveAttribute("open");
|
||||
}
|
||||
}
|
||||
|
||||
void CommandConsole::executeFromInput() {
|
||||
void CommandConsole::execute_from_input() {
|
||||
if (mInput == nullptr) {
|
||||
return;
|
||||
}
|
||||
const Rml::String value = mInput->GetValue();
|
||||
hide(true);
|
||||
hide(false);
|
||||
if (!value.empty()) {
|
||||
runCommand(value, mState, [this](std::string text) { ConsolePrint(std::move(text)); });
|
||||
runCommand(value, mState, [this](std::string text) { append_message(std::move(text)); });
|
||||
}
|
||||
mScrollToBottom = true;
|
||||
}
|
||||
|
||||
void CommandConsole::navigateHistory(int dir) {
|
||||
void CommandConsole::navigate_history(int direction) {
|
||||
if (mState.history.empty() || mInput == nullptr) {
|
||||
return;
|
||||
}
|
||||
const int prev = mHistoryPos;
|
||||
if (dir < 0) {
|
||||
if (direction < 0) {
|
||||
if (mHistoryPos == -1) {
|
||||
mHistoryPos = (int)mState.history.size() - 1;
|
||||
} else if (mHistoryPos > 0) {
|
||||
@@ -194,16 +205,100 @@ void CommandConsole::navigateHistory(int dir) {
|
||||
}
|
||||
}
|
||||
|
||||
void CommandConsole::ConsolePrint(std::string text) {
|
||||
mMsgHistory.push_back(text);
|
||||
if ((int)mMsgHistory.size() > kMaxMsgHistory) {
|
||||
mMsgHistory.erase(mMsgHistory.begin());
|
||||
void CommandConsole::scroll_messages(int pages) {
|
||||
if (mOutput == nullptr) {
|
||||
return;
|
||||
}
|
||||
mOutputLines.push_back({std::move(text), kDurationNormal});
|
||||
mScrollToBottom = true;
|
||||
if ((int)mOutputLines.size() > kMaxStoredLines) {
|
||||
mOutputLines.erase(mOutputLines.begin());
|
||||
const float bottom = std::max(0.0f, mOutput->GetScrollHeight() - mOutput->GetClientHeight());
|
||||
const float pageHeight = std::max(1.0f, mOutput->GetClientHeight() * 0.9f);
|
||||
const float scrollTop =
|
||||
std::clamp(mOutput->GetScrollTop() + static_cast<float>(pages) * pageHeight, 0.0f, bottom);
|
||||
mOutput->SetScrollTop(scrollTop);
|
||||
mStickToBottom = scrollTop >= bottom - 4.0f;
|
||||
}
|
||||
|
||||
void CommandConsole::append_message(std::string text) {
|
||||
auto* element = append(mOutput, "line");
|
||||
if (element != nullptr) {
|
||||
element->SetClass("cmd", is_command(text));
|
||||
append_text(element, text);
|
||||
}
|
||||
|
||||
const auto now = clock::now();
|
||||
mMessages.push_back({
|
||||
.text = std::move(text),
|
||||
.element = element,
|
||||
.fadeAt = now + kMessageDuration - kFadeDuration,
|
||||
.hideAt = now + kMessageDuration,
|
||||
});
|
||||
|
||||
while (mMessages.size() > kMaxMessageHistory) {
|
||||
if (auto* oldElement = mMessages.front().element; oldElement != nullptr) {
|
||||
if (auto* parent = oldElement->GetParentNode()) {
|
||||
parent->RemoveChild(oldElement);
|
||||
}
|
||||
}
|
||||
mMessages.pop_front();
|
||||
}
|
||||
limit_visible_messages();
|
||||
if (mConsole != nullptr) {
|
||||
mConsole->RemoveAttribute("fading");
|
||||
}
|
||||
|
||||
if (getSettings().backend.enableAdvancedSettings && !is_prelaunch_open() &&
|
||||
!Document::visible())
|
||||
{
|
||||
Document::show();
|
||||
}
|
||||
}
|
||||
|
||||
void CommandConsole::limit_visible_messages() {
|
||||
std::size_t visibleCount = 0;
|
||||
for (auto it = mMessages.rbegin(); it != mMessages.rend(); ++it) {
|
||||
if (it->expired) {
|
||||
continue;
|
||||
}
|
||||
if (++visibleCount <= kMaxVisibleLines) {
|
||||
continue;
|
||||
}
|
||||
it->expired = true;
|
||||
if (it->element != nullptr) {
|
||||
it->element->SetAttribute("expired", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CommandConsole::update_message_lifetimes() {
|
||||
const auto now = clock::now();
|
||||
for (auto& message : mMessages) {
|
||||
if (!message.fading && now >= message.fadeAt) {
|
||||
message.fading = true;
|
||||
if (message.element != nullptr) {
|
||||
message.element->SetAttribute("fading", "");
|
||||
}
|
||||
}
|
||||
if (!message.expired && now >= message.hideAt) {
|
||||
message.expired = true;
|
||||
if (message.element != nullptr) {
|
||||
message.element->SetAttribute("expired", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const bool hasVisibleMessages = has_onscreen_messages();
|
||||
const bool hasOpaqueMessages = std::ranges::any_of(
|
||||
mMessages, [](const auto& message) { return !message.fading && !message.expired; });
|
||||
if (mConsole != nullptr) {
|
||||
if (hasVisibleMessages && !hasOpaqueMessages) {
|
||||
mConsole->SetAttribute("fading", "");
|
||||
} else {
|
||||
mConsole->RemoveAttribute("fading");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CommandConsole::has_onscreen_messages() const {
|
||||
return std::ranges::any_of(mMessages, [](const auto& message) { return !message.expired; });
|
||||
}
|
||||
|
||||
} // namespace dusk::ui
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
#include "document.hpp"
|
||||
#include "dusk/commands.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <deque>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Rml {
|
||||
class ElementFormControlInput;
|
||||
@@ -20,36 +21,48 @@ public:
|
||||
void show() override;
|
||||
bool focus() override;
|
||||
void hide(bool close) override;
|
||||
bool visible() const override;
|
||||
bool active() const override;
|
||||
bool permanent() const override { return true; }
|
||||
|
||||
bool input_active() const { return mInputActive; }
|
||||
|
||||
private:
|
||||
struct OutputLine {
|
||||
struct MessageLine {
|
||||
std::string text;
|
||||
float remain;
|
||||
Rml::Element* element = nullptr;
|
||||
clock::time_point fadeAt;
|
||||
clock::time_point hideAt;
|
||||
bool fading = false;
|
||||
bool expired = false;
|
||||
};
|
||||
|
||||
static constexpr float kDurationNormal = 6.0f;
|
||||
static constexpr float kFadeSeconds = 0.8f;
|
||||
static constexpr int kMaxStoredLines = 64;
|
||||
static constexpr int kMaxVisibleLines = 24;
|
||||
static constexpr int kMaxMsgHistory = 500;
|
||||
static constexpr auto kMessageDuration = std::chrono::seconds{6};
|
||||
static constexpr auto kFadeDuration = std::chrono::milliseconds{800};
|
||||
static constexpr std::size_t kMaxVisibleLines = 24;
|
||||
static constexpr std::size_t kMaxMessageHistory = 500;
|
||||
|
||||
Rml::Element* mConsole = nullptr;
|
||||
Rml::Element* mOutput = nullptr;
|
||||
Rml::ElementFormControlInput* mInput = nullptr;
|
||||
|
||||
std::vector<OutputLine> mOutputLines;
|
||||
std::vector<std::string> mMsgHistory;
|
||||
std::deque<MessageLine> mMessages;
|
||||
int mHistoryPos = -1;
|
||||
bool mInputActive = false;
|
||||
bool mScrollToBottom = false;
|
||||
bool mStickToBottom = true;
|
||||
|
||||
CommandState mState;
|
||||
|
||||
bool handle_nav_command(Rml::Event& event, NavCommand cmd) override;
|
||||
|
||||
void ConsolePrint(std::string text);
|
||||
void executeFromInput();
|
||||
void navigateHistory(int dir);
|
||||
void append_message(std::string text);
|
||||
void close_input();
|
||||
void execute_from_input();
|
||||
bool has_onscreen_messages() const;
|
||||
void limit_visible_messages();
|
||||
void navigate_history(int direction);
|
||||
void scroll_messages(int pages);
|
||||
void update_message_lifetimes();
|
||||
};
|
||||
|
||||
} // namespace dusk::ui
|
||||
|
||||
@@ -23,6 +23,7 @@ public:
|
||||
bool has_focus() const;
|
||||
virtual bool visible() const;
|
||||
virtual bool active() const;
|
||||
virtual bool permanent() const { return false; }
|
||||
virtual bool obscures_game() const { return false; }
|
||||
virtual void cover() {
|
||||
mWasVisible = visible();
|
||||
|
||||
@@ -20,6 +20,7 @@ public:
|
||||
void update() override;
|
||||
bool focus() override;
|
||||
bool visible() const override;
|
||||
bool permanent() const override { return true; }
|
||||
|
||||
static void refresh_tabs();
|
||||
|
||||
|
||||
@@ -1192,10 +1192,7 @@ void Prelaunch::update() {
|
||||
}
|
||||
|
||||
void return_to_prelaunch() noexcept {
|
||||
close_documents_except(DocumentScope::MenuBar);
|
||||
if (auto* menuBar = find_document(DocumentScope::MenuBar)) {
|
||||
menuBar->force_hide(false);
|
||||
}
|
||||
close_all_documents();
|
||||
push_document(std::make_unique<Prelaunch>(), true);
|
||||
}
|
||||
|
||||
|
||||
+36
-14
@@ -69,6 +69,7 @@ void restyle_scope(DocumentScope scope) {
|
||||
|
||||
std::deque<Toast> sToasts;
|
||||
bool sMenuNotificationRequested = false;
|
||||
bool sConsoleShortcutHeld = false;
|
||||
|
||||
// Sometimes gamepads can connect and disconnect quickly, especially during
|
||||
// connection negotiation. In this case, we'll receive an _ADDED event for a
|
||||
@@ -107,6 +108,7 @@ void shutdown() noexcept {
|
||||
sDocumentStack.clear();
|
||||
sPassiveDocuments.clear();
|
||||
sConnectedGamepads.clear();
|
||||
sConsoleShortcutHeld = false;
|
||||
input::reset_input_state();
|
||||
input::release_input_block();
|
||||
sInitialized = false;
|
||||
@@ -216,20 +218,29 @@ void handle_event(const SDL_Event& event) noexcept {
|
||||
}
|
||||
sConnectedGamepads.erase(event.gdevice.which);
|
||||
}
|
||||
input::handle_event(event);
|
||||
// TODO: don't overlap with PAD bindings?
|
||||
if (event.type == SDL_EVENT_KEY_DOWN && event.key.key == SDLK_SLASH) {
|
||||
bool found = false;
|
||||
for (auto& doc : sDocumentStack) {
|
||||
if (auto* console = dynamic_cast<CommandConsole*>(doc.get())) {
|
||||
console->show();
|
||||
found = true;
|
||||
}
|
||||
if (event.type == SDL_EVENT_WINDOW_FOCUS_LOST) {
|
||||
sConsoleShortcutHeld = false;
|
||||
}
|
||||
if (event.type == SDL_EVENT_KEY_UP && event.key.key == SDLK_SLASH && sConsoleShortcutHeld) {
|
||||
sConsoleShortcutHeld = false;
|
||||
return;
|
||||
}
|
||||
if (event.type == SDL_EVENT_KEY_DOWN && event.key.key == SDLK_SLASH &&
|
||||
getSettings().backend.enableAdvancedSettings)
|
||||
{
|
||||
auto* console = static_cast<CommandConsole*>(find_document(DocumentScope::CommandConsole));
|
||||
if (sConsoleShortcutHeld) {
|
||||
return;
|
||||
}
|
||||
if (!found) {
|
||||
push_document(std::make_unique<CommandConsole>(), true, false);
|
||||
if (console != nullptr && !console->input_active() && !event.key.repeat) {
|
||||
sConsoleShortcutHeld = true;
|
||||
bring_document_to_front(*console);
|
||||
console->show();
|
||||
input::sync_input_block();
|
||||
return;
|
||||
}
|
||||
}
|
||||
input::handle_event(event);
|
||||
}
|
||||
|
||||
bool register_scoped_styles(DocumentScope scope, std::string id, const std::string& rcss) noexcept {
|
||||
@@ -274,6 +285,17 @@ Document& push_document(std::unique_ptr<Document> doc, bool show, bool passive)
|
||||
return ret;
|
||||
}
|
||||
|
||||
void bring_document_to_front(Document& doc) noexcept {
|
||||
const auto it = std::ranges::find_if(
|
||||
sDocumentStack, [&doc](const auto& entry) { return entry.get() == &doc; });
|
||||
if (it == sDocumentStack.end() || std::next(it) == sDocumentStack.end()) {
|
||||
return;
|
||||
}
|
||||
auto entry = std::move(*it);
|
||||
sDocumentStack.erase(it);
|
||||
sDocumentStack.push_back(std::move(entry));
|
||||
}
|
||||
|
||||
void uncover_top_document() noexcept {
|
||||
if (auto* doc = top_document()) {
|
||||
doc->uncover();
|
||||
@@ -290,10 +312,10 @@ Document* find_document(DocumentScope scope) noexcept {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void close_documents_except(DocumentScope scope) noexcept {
|
||||
void close_all_documents() noexcept {
|
||||
for (auto& doc : sDocumentStack) {
|
||||
if (!doc->closed() && doc->scope() != scope) {
|
||||
doc->force_hide(true);
|
||||
if (!doc->closed()) {
|
||||
doc->force_hide(!doc->permanent());
|
||||
}
|
||||
}
|
||||
input::sync_input_block();
|
||||
|
||||
+3
-1
@@ -19,6 +19,7 @@ using clock = std::chrono::steady_clock;
|
||||
|
||||
enum class DocumentScope : u8 {
|
||||
None,
|
||||
CommandConsole,
|
||||
Prelaunch,
|
||||
Window,
|
||||
MenuBar,
|
||||
@@ -87,12 +88,13 @@ void update() noexcept;
|
||||
|
||||
Document& push_document(
|
||||
std::unique_ptr<Document> doc, bool show = true, bool passive = false) noexcept;
|
||||
void bring_document_to_front(Document& doc) noexcept;
|
||||
bool register_scoped_styles(DocumentScope scope, std::string id, const std::string& rcss) noexcept;
|
||||
void unregister_scoped_styles(DocumentScope scope, std::string_view id) noexcept;
|
||||
void apply_scoped_styles(Document& doc) noexcept;
|
||||
void uncover_top_document() noexcept;
|
||||
Document* find_document(DocumentScope scope) noexcept;
|
||||
void close_documents_except(DocumentScope scope) noexcept;
|
||||
void close_all_documents() noexcept;
|
||||
bool any_document_visible() noexcept;
|
||||
bool is_prelaunch_open() noexcept;
|
||||
bool game_obscured_below(const Document& doc) noexcept;
|
||||
|
||||
@@ -786,6 +786,7 @@ int game_main(int argc, char* argv[]) {
|
||||
dusk::ui::push_document(std::make_unique<dusk::ui::Overlay>(), true, true);
|
||||
dusk::ui::push_document(std::make_unique<dusk::ui::TouchControls>(), false, true);
|
||||
dusk::ui::push_document(std::make_unique<dusk::ui::MenuBar>(), false);
|
||||
dusk::ui::push_document(std::make_unique<dusk::ui::CommandConsole>(), false);
|
||||
|
||||
// Invalidate a bad saved isoPath so that Dusklight can't get blocked from starting up.
|
||||
// This is only a metadata check; full hash verification is handled by the prelaunch UI.
|
||||
|
||||
Reference in New Issue
Block a user