mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-07-10 12:54:50 -04:00
UI: Rework Document close handling again
This commit is contained in:
@@ -35,7 +35,6 @@ Document::Document(const Rml::String& source) : mDocument(load_document(source))
|
||||
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);
|
||||
@@ -65,10 +64,13 @@ void Document::show() {
|
||||
}
|
||||
}
|
||||
|
||||
void Document::hide() {
|
||||
void Document::hide(bool close) {
|
||||
if (mDocument != nullptr) {
|
||||
mDocument->Hide();
|
||||
}
|
||||
if (close) {
|
||||
mClosed = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Document::update() {}
|
||||
@@ -89,13 +91,6 @@ void Document::listen(Rml::Element* element, Rml::EventId event,
|
||||
std::make_unique<ScopedEventListener>(element, event, std::move(callback), capture));
|
||||
}
|
||||
|
||||
bool Document::can_destroy() const {
|
||||
if (mDocument == nullptr) {
|
||||
return true;
|
||||
}
|
||||
return *mDocument->GetProperty(Rml::PropertyId::Visibility) == Rml::Style::Visibility::Hidden;
|
||||
}
|
||||
|
||||
bool Document::visible() const {
|
||||
if (mDocument == nullptr) {
|
||||
return false;
|
||||
@@ -105,7 +100,7 @@ bool Document::visible() const {
|
||||
|
||||
bool Document::handle_nav_command(Rml::Event& event, NavCommand cmd) {
|
||||
if (cmd == NavCommand::Menu) {
|
||||
toggle_top_document();
|
||||
toggle();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -14,7 +14,7 @@ public:
|
||||
Document& operator=(const Document&) = delete;
|
||||
|
||||
virtual void show();
|
||||
virtual void hide();
|
||||
virtual void hide(bool close);
|
||||
virtual void update();
|
||||
virtual bool focus();
|
||||
virtual bool visible() const;
|
||||
@@ -24,14 +24,28 @@ public:
|
||||
void listen(Rml::EventId event, ScopedEventListener::Callback callback, bool capture = false) {
|
||||
listen(mDocument, event, std::move(callback), capture);
|
||||
}
|
||||
void toggle() {
|
||||
if (visible()) {
|
||||
hide(false);
|
||||
} else {
|
||||
show();
|
||||
}
|
||||
}
|
||||
void pop() {
|
||||
hide(true);
|
||||
show_top_document();
|
||||
}
|
||||
|
||||
bool can_destroy() const;
|
||||
bool pending_close() const { return mPendingClose; }
|
||||
bool closed() const { return mClosed; }
|
||||
|
||||
protected:
|
||||
virtual bool handle_nav_command(Rml::Event& event, NavCommand cmd);
|
||||
|
||||
Rml::ElementDocument* mDocument;
|
||||
std::vector<std::unique_ptr<ScopedEventListener> > mListeners;
|
||||
bool mPendingClose = false;
|
||||
bool mClosed = false;
|
||||
};
|
||||
|
||||
} // namespace dusk::ui
|
||||
|
||||
@@ -208,7 +208,7 @@ Overlay::Overlay(OverlayProps props)
|
||||
|
||||
if (auto* footer = mDocument->GetElementById("footer")) {
|
||||
auto& returnButton = add_component<Button>(footer, "\xE2\x86\x90 Return", "footer-button")
|
||||
.on_pressed(pop_document);
|
||||
.on_pressed([this] { pop(); });
|
||||
returnButton.root()->SetClass("return", true);
|
||||
auto& resetButton =
|
||||
add_component<Button>(footer, "Reset to default", "footer-button").on_pressed([this] {
|
||||
@@ -220,11 +220,10 @@ Overlay::Overlay(OverlayProps props)
|
||||
// Hide document after transition completion
|
||||
mRoot = mDocument->GetElementById("root");
|
||||
listen(mRoot, Rml::EventId::Transitionend, [this](Rml::Event& event) {
|
||||
if (event.GetTargetElement() == mRoot &&
|
||||
*mRoot->GetProperty(Rml::PropertyId::Visibility) == Rml::Style::Visibility::Visible &&
|
||||
!mRoot->HasAttribute("open"))
|
||||
if (event.GetTargetElement() == mRoot && !mRoot->HasAttribute("open") &&
|
||||
Document::visible())
|
||||
{
|
||||
Document::hide();
|
||||
Document::hide(mPendingClose);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -234,8 +233,11 @@ void Overlay::show() {
|
||||
mRoot->SetAttribute("open", "");
|
||||
}
|
||||
|
||||
void Overlay::hide() {
|
||||
void Overlay::hide(bool close) {
|
||||
mRoot->RemoveAttribute("open");
|
||||
if (close) {
|
||||
mPendingClose = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Overlay::update() {
|
||||
@@ -260,7 +262,7 @@ bool Overlay::visible() const {
|
||||
|
||||
bool Overlay::handle_nav_command(Rml::Event& event, NavCommand cmd) {
|
||||
if (cmd == NavCommand::Cancel) {
|
||||
pop_document();
|
||||
pop();
|
||||
return true;
|
||||
}
|
||||
return Document::handle_nav_command(event, cmd);
|
||||
|
||||
@@ -60,7 +60,7 @@ public:
|
||||
explicit Overlay(OverlayProps props);
|
||||
|
||||
void show() override;
|
||||
void hide() override;
|
||||
void hide(bool close) override;
|
||||
void update() override;
|
||||
bool focus() override;
|
||||
bool visible() const override;
|
||||
@@ -85,7 +85,6 @@ private:
|
||||
int mDefaultValue = 0;
|
||||
std::vector<std::unique_ptr<Component> > mComponents;
|
||||
Rml::Element* mRoot;
|
||||
bool mDismissed = false;
|
||||
};
|
||||
|
||||
} // namespace dusk::ui
|
||||
|
||||
+8
-16
@@ -41,17 +41,14 @@ Popup::Popup() : Document(kDocumentSource), mRoot(mDocument->GetElementById("pop
|
||||
mTabBar->add_tab("Reset", [this] {
|
||||
JUTGamePad::C3ButtonReset::sResetSwitchPushing = true;
|
||||
mTabBar->set_active_tab(-1);
|
||||
hide();
|
||||
hide(false);
|
||||
});
|
||||
mTabBar->add_tab("Exit", [] { IsRunning = false; });
|
||||
|
||||
// Hide document after transition completion
|
||||
listen(mRoot, Rml::EventId::Transitionend, [this](Rml::Event& event) {
|
||||
if (event.GetTargetElement() == mRoot &&
|
||||
*mRoot->GetProperty(Rml::PropertyId::Visibility) == Rml::Style::Visibility::Visible &&
|
||||
!mRoot->HasAttribute("open"))
|
||||
{
|
||||
Document::hide();
|
||||
if (event.GetTargetElement() == mRoot && !mRoot->HasAttribute("open") && Document::visible()) {
|
||||
Document::hide(mPendingClose);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -65,8 +62,11 @@ void Popup::show() {
|
||||
mTabBar->set_active_tab(-1);
|
||||
}
|
||||
|
||||
void Popup::hide() {
|
||||
void Popup::hide(bool close) {
|
||||
mRoot->RemoveAttribute("open");
|
||||
if (close) {
|
||||
mPendingClose = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Popup::update() {
|
||||
@@ -99,21 +99,13 @@ void Popup::update_safe_area() noexcept {
|
||||
Rml::PropertyId::PaddingLeft, Rml::Property(safeInsets.left, Rml::Unit::PX));
|
||||
}
|
||||
|
||||
void Popup::toggle() {
|
||||
if (visible()) {
|
||||
hide();
|
||||
} else {
|
||||
show();
|
||||
}
|
||||
}
|
||||
|
||||
bool Popup::visible() const {
|
||||
return mRoot->HasAttribute("open");
|
||||
}
|
||||
|
||||
bool Popup::handle_nav_command(Rml::Event& event, NavCommand cmd) {
|
||||
if (cmd == NavCommand::Cancel) {
|
||||
hide();
|
||||
hide(false);
|
||||
return true;
|
||||
}
|
||||
return Document::handle_nav_command(event, cmd);
|
||||
|
||||
@@ -16,13 +16,11 @@ public:
|
||||
Popup& operator=(const Popup&) = delete;
|
||||
|
||||
void show() override;
|
||||
void hide() override;
|
||||
void hide(bool close) override;
|
||||
void update() override;
|
||||
bool focus() override;
|
||||
bool visible() const override;
|
||||
|
||||
void toggle();
|
||||
|
||||
protected:
|
||||
bool handle_nav_command(Rml::Event& event, NavCommand cmd) override;
|
||||
|
||||
@@ -33,7 +31,6 @@ private:
|
||||
std::unique_ptr<TabBar> mTabBar;
|
||||
std::unique_ptr<Button> mCloseButton;
|
||||
Insets mTabBarPadding;
|
||||
bool mVisible = false;
|
||||
};
|
||||
|
||||
} // namespace dusk::ui
|
||||
|
||||
+15
-11
@@ -114,13 +114,13 @@ Prelaunch::Prelaunch() : Document(kDocumentSource), mRoot(mDocument->GetElementB
|
||||
const bool hasValidPath = is_selected_path_valid();
|
||||
mMenuButtons.push_back(
|
||||
std::make_unique<Button>(menuList, hasValidPath ? "Start Game" : "Select Disk Image"));
|
||||
mMenuButtons.back()->on_pressed([] {
|
||||
mMenuButtons.back()->on_pressed([this] {
|
||||
if (!is_selected_path_valid()) {
|
||||
open_iso_picker();
|
||||
return;
|
||||
}
|
||||
IsGameLaunched = true;
|
||||
pop_document();
|
||||
hide(true);
|
||||
});
|
||||
apply_intro_animation(mMenuButtons.back()->root(), "delay-1");
|
||||
|
||||
@@ -144,7 +144,7 @@ Prelaunch::Prelaunch() : Document(kDocumentSource), mRoot(mDocument->GetElementB
|
||||
return;
|
||||
}
|
||||
if (target == mDocument && !mDocument->HasAttribute("open")) {
|
||||
Document::hide();
|
||||
Document::hide(true);
|
||||
} else if (target->GetTagName() == "button" && !target->IsClassSet("anim-done")) {
|
||||
target->SetClass("anim-done", true);
|
||||
}
|
||||
@@ -157,8 +157,12 @@ void Prelaunch::show() {
|
||||
mRoot->SetAttribute("open", "");
|
||||
}
|
||||
|
||||
void Prelaunch::hide() {
|
||||
if (IsGameLaunched) {
|
||||
void Prelaunch::hide(bool close) {
|
||||
if (close) {
|
||||
if (!mEntranceAnimationStarted) {
|
||||
// Close document immediately
|
||||
Document::hide(true);
|
||||
}
|
||||
mDocument->RemoveAttribute("open");
|
||||
} else {
|
||||
mRoot->RemoveAttribute("open");
|
||||
@@ -169,18 +173,18 @@ void Prelaunch::update() {
|
||||
ensure_initialized();
|
||||
refresh_path_state();
|
||||
|
||||
if (!mEntranceAnimationStarted && mDocument != nullptr) {
|
||||
mDocument->SetClass("animate-in", true);
|
||||
mEntranceAnimationStarted = true;
|
||||
}
|
||||
|
||||
auto& state = prelaunch_state();
|
||||
const bool hasValidPath = is_selected_path_valid();
|
||||
if (hasValidPath && getSettings().backend.skipPreLaunchUI) {
|
||||
pop_document();
|
||||
hide(true);
|
||||
IsGameLaunched = true;
|
||||
}
|
||||
|
||||
if (!mEntranceAnimationStarted && mDocument != nullptr) {
|
||||
mDocument->SetClass("animate-in", true);
|
||||
mEntranceAnimationStarted = true;
|
||||
}
|
||||
|
||||
if (!mMenuButtons.empty()) {
|
||||
mMenuButtons[0]->set_text(hasValidPath ? "Start Game" : "Select Disk Image");
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ public:
|
||||
Prelaunch();
|
||||
|
||||
void show() override;
|
||||
void hide() override;
|
||||
void hide(bool close) override;
|
||||
void update() override;
|
||||
bool focus() override;
|
||||
bool visible() const override;
|
||||
|
||||
+13
-39
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include <ranges>
|
||||
|
||||
#include "aurora/lib/window.hpp"
|
||||
#include "input.hpp"
|
||||
@@ -19,12 +20,7 @@ void load_font(const char* filename, bool fallback = false) {
|
||||
}
|
||||
|
||||
bool sInitialized = false;
|
||||
|
||||
struct OpenDocument {
|
||||
std::unique_ptr<Document> doc;
|
||||
bool pendingDestroy = false;
|
||||
};
|
||||
std::vector<OpenDocument> sDocuments;
|
||||
std::vector<std::unique_ptr<Document> > sDocuments;
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -54,8 +50,8 @@ void shutdown() noexcept {
|
||||
}
|
||||
|
||||
Document& push_document(std::unique_ptr<Document> doc, bool show) noexcept {
|
||||
if (auto* doc = top_document()) {
|
||||
doc->hide();
|
||||
if (auto* top = top_document()) {
|
||||
top->hide(false);
|
||||
}
|
||||
Document& ret = *doc;
|
||||
sDocuments.push_back({std::move(doc)});
|
||||
@@ -66,43 +62,22 @@ Document& push_document(std::unique_ptr<Document> doc, bool show) noexcept {
|
||||
return ret;
|
||||
}
|
||||
|
||||
void pop_document() noexcept {
|
||||
for (auto it = sDocuments.rbegin(); it != sDocuments.rend(); ++it) {
|
||||
if (!it->pendingDestroy) {
|
||||
it->doc->hide();
|
||||
it->pendingDestroy = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
void show_top_document() noexcept {
|
||||
if (auto* doc = top_document()) {
|
||||
doc->show();
|
||||
}
|
||||
sync_input_block();
|
||||
}
|
||||
|
||||
void toggle_top_document() noexcept {
|
||||
auto* doc = top_document();
|
||||
if (doc == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (doc->visible()) {
|
||||
doc->hide();
|
||||
} else {
|
||||
doc->show();
|
||||
}
|
||||
sync_input_block();
|
||||
}
|
||||
|
||||
bool any_document_visible() noexcept {
|
||||
return std::any_of(sDocuments.begin(), sDocuments.end(),
|
||||
[](const OpenDocument& doc) { return doc.doc != nullptr && doc.doc->visible(); });
|
||||
[](const auto& doc) { return doc && doc->visible(); });
|
||||
}
|
||||
|
||||
Document* top_document() noexcept {
|
||||
for (auto it = sDocuments.rbegin(); it != sDocuments.rend(); ++it) {
|
||||
if (!it->pendingDestroy) {
|
||||
return it->doc.get();
|
||||
for (auto& doc : std::views::reverse(sDocuments)) {
|
||||
if (!doc->closed() && !doc->pending_close()) {
|
||||
return doc.get();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
@@ -111,12 +86,11 @@ Document* top_document() noexcept {
|
||||
void update() noexcept {
|
||||
update_input();
|
||||
for (const auto& doc : sDocuments) {
|
||||
doc.doc->update();
|
||||
doc->update();
|
||||
}
|
||||
sDocuments.erase(
|
||||
std::remove_if(sDocuments.begin(), sDocuments.end(),
|
||||
[](const OpenDocument& doc) { return doc.pendingDestroy && doc.doc->can_destroy(); }),
|
||||
sDocuments.end());
|
||||
const auto [first, last] =
|
||||
std::ranges::remove_if(sDocuments, [](const auto& doc) { return doc->closed(); });
|
||||
sDocuments.erase(first, last);
|
||||
sync_input_block();
|
||||
}
|
||||
|
||||
|
||||
+1
-2
@@ -33,8 +33,7 @@ void handle_event(const SDL_Event& event) noexcept;
|
||||
void update() noexcept;
|
||||
|
||||
Document& push_document(std::unique_ptr<Document> doc, bool show = true) noexcept;
|
||||
void pop_document() noexcept;
|
||||
void toggle_top_document() noexcept;
|
||||
void show_top_document() noexcept;
|
||||
bool any_document_visible() noexcept;
|
||||
Document* top_document() noexcept;
|
||||
|
||||
|
||||
@@ -64,11 +64,8 @@ Window::Window() : Document(kDocumentSource), mRoot(mDocument->GetElementById("w
|
||||
|
||||
// Hide document after transition completion
|
||||
listen(mRoot, Rml::EventId::Transitionend, [this](Rml::Event& event) {
|
||||
if (event.GetTargetElement() == mRoot &&
|
||||
*mRoot->GetProperty(Rml::PropertyId::Visibility) == Rml::Style::Visibility::Visible &&
|
||||
!mRoot->HasAttribute("open"))
|
||||
{
|
||||
Document::hide();
|
||||
if (event.GetTargetElement() == mRoot && !mRoot->HasAttribute("open") && Document::visible()) {
|
||||
Document::hide(mPendingClose);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -92,9 +89,9 @@ void Window::show() {
|
||||
mRoot->SetAttribute("open", "");
|
||||
}
|
||||
|
||||
void Window::hide() {
|
||||
void Window::hide(bool close) {
|
||||
mRoot->RemoveAttribute("open");
|
||||
// Document will be hidden after transition
|
||||
mPendingClose = close;
|
||||
}
|
||||
|
||||
void Window::update() {
|
||||
@@ -175,7 +172,7 @@ bool Window::handle_nav_command(Rml::Event& event, NavCommand cmd) {
|
||||
}
|
||||
}
|
||||
if (cmd == NavCommand::Cancel) {
|
||||
pop_document();
|
||||
pop();
|
||||
return true;
|
||||
}
|
||||
if (mTabBar->handle_nav_command(event, cmd)) {
|
||||
|
||||
@@ -24,7 +24,7 @@ public:
|
||||
Window& operator=(const Window&) = delete;
|
||||
|
||||
void show() override;
|
||||
void hide() override;
|
||||
void hide(bool close) override;
|
||||
void update() override;
|
||||
bool focus() override;
|
||||
bool visible() const override;
|
||||
@@ -50,7 +50,6 @@ protected:
|
||||
std::unique_ptr<TabBar> mTabBar;
|
||||
std::vector<std::unique_ptr<Component> > mContentComponents;
|
||||
Insets mBodyPadding;
|
||||
bool mVisible = false;
|
||||
};
|
||||
|
||||
} // namespace dusk::ui
|
||||
|
||||
Reference in New Issue
Block a user