diff --git a/files.cmake b/files.cmake index f0fa42489a..c95bd904bd 100644 --- a/files.cmake +++ b/files.cmake @@ -1466,6 +1466,8 @@ set(DUSK_FILES src/dusk/ui/button.hpp src/dusk/ui/component.cpp src/dusk/ui/component.hpp + src/dusk/ui/document.cpp + src/dusk/ui/document.hpp src/dusk/ui/editor.cpp src/dusk/ui/editor.hpp src/dusk/ui/event.cpp diff --git a/src/dusk/ui/document.cpp b/src/dusk/ui/document.cpp new file mode 100644 index 0000000000..105850673a --- /dev/null +++ b/src/dusk/ui/document.cpp @@ -0,0 +1,71 @@ +#include "document.hpp" + +#include "aurora/rmlui.hpp" +#include "ui.hpp" + +namespace dusk::ui { +namespace { + +Rml::ElementDocument* load_document(const Rml::String& path) { + auto* context = aurora::rmlui::get_context(); + if (context == nullptr) { + return nullptr; + } + return context->LoadDocument(path); +} + +} // namespace + +Document::Document(const Rml::String& path) : mDocument(load_document(path)) { + listen(Rml::EventId::Keydown, [this](Rml::Event& event) { + const auto cmd = map_nav_event(event); + if (cmd != NavCommand::None && handle_nav_command(event, cmd)) { + event.StopPropagation(); + } + }); +} + +Document::~Document() { + mListeners.clear(); + if (mDocument != nullptr) { + mDocument->Close(); + mDocument = nullptr; + } +} + +void Document::show() { + if (mDocument != nullptr) { + mDocument->Show(); + focus(); + } +} + +void Document::hide() { + if (mDocument != nullptr) { + mDocument->Hide(); + } +} + +void Document::update() {} + +bool Document::focus() { + return false; +} + +void Document::listen(Rml::Element* element, Rml::EventId event, + ScopedEventListener::Callback callback, bool capture) { + if (element == nullptr) { + element = mDocument; + } + if (element == nullptr || !callback) { + return; + } + mListeners.emplace_back( + std::make_unique(element, event, std::move(callback), capture)); +} + +bool Document::handle_nav_command(Rml::Event& event, NavCommand cmd) { + return false; +} + +} // namespace dusk::ui diff --git a/src/dusk/ui/document.hpp b/src/dusk/ui/document.hpp new file mode 100644 index 0000000000..26c641ef31 --- /dev/null +++ b/src/dusk/ui/document.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include "component.hpp" +#include "ui.hpp" + +namespace dusk::ui { + +class Document { +public: + Document(const Rml::String& path); + virtual ~Document(); + + Document(const Document&) = delete; + Document& operator=(const Document&) = delete; + + virtual void show(); + virtual void hide(); + virtual void update(); + virtual bool focus(); + + void listen(Rml::Element* element, Rml::EventId event, ScopedEventListener::Callback callback, + bool capture = false); + void listen(Rml::EventId event, ScopedEventListener::Callback callback, bool capture = false) { + listen(mDocument, event, std::move(callback), capture); + } + +protected: + virtual bool handle_nav_command(Rml::Event& event, NavCommand cmd); + + Rml::ElementDocument* mDocument; + std::vector > mListeners; +}; + +} // namespace dusk::ui \ No newline at end of file diff --git a/src/dusk/ui/popup.cpp b/src/dusk/ui/popup.cpp index 103ef79485..e78c615117 100644 --- a/src/dusk/ui/popup.cpp +++ b/src/dusk/ui/popup.cpp @@ -3,6 +3,8 @@ #include #include "aurora/rmlui.hpp" +#include "editor.hpp" +#include "settings.hpp" #include "tab_button.hpp" #include "ui.hpp" #include "window.hpp" @@ -10,22 +12,10 @@ #include #include #include -#include namespace dusk::ui { -Popup::Popup(Window& settingsWindow, Window& editorWindow) - : mSettingsWindow(settingsWindow), mEditorWindow(editorWindow) { - auto* context = aurora::rmlui::get_context(); - if (context == nullptr) { - return; - } - - mDocument = context->LoadDocument("res/rml/popup.rml"); - if (mDocument == nullptr) { - return; - } - +Popup::Popup() : Document("res/rml/popup.rml") { auto* tabBar = mDocument->GetElementById("tab-bar"); if (tabBar == nullptr) { return; @@ -43,72 +33,32 @@ Popup::Popup(Window& settingsWindow, Window& editorWindow) mTabActions = { [this] { hide(); - mSettingsWindow.show(); - mSettingsWindow.focus_for_input(); + // TODO: make this better + auto& settingsWindow = add_document(std::make_unique()); + settingsWindow.show(); set_selected_tab(0); }, - [this] { - set_selected_tab(1); - }, + [this] { set_selected_tab(1); }, [this] { hide(); - mEditorWindow.show(); - mEditorWindow.focus_for_input(); + // TODO: make this better + auto& editorWindow = add_document(std::make_unique()); + editorWindow.show(); set_selected_tab(2); }, - [this] { - set_selected_tab(3); - }, - [this] { - set_selected_tab(4); - }, + [this] { set_selected_tab(3); }, + [this] { set_selected_tab(4); }, }; mTabs.reserve(tabLabels.size()); for (int i = 0; i < static_cast(tabLabels.size()); ++i) { - mTabs.push_back(create_tab_button(tabBar, tabLabels[i], i == mSelectedTabIndex, - [this, i](Rml::Event&) { + mTabs.push_back( + create_tab_button(tabBar, tabLabels[i], i == mSelectedTabIndex, [this, i](Rml::Event&) { if (i >= 0 && i < static_cast(mTabActions.size())) { mTabActions[i](); } })); } - - mKeyListener = std::make_unique( - mDocument, Rml::EventId::Keydown, [this](Rml::Event& event) { - const auto cmd = map_nav_event(event); - if (cmd == NavCommand::None) { - return; - } - if (cmd == NavCommand::Left || cmd == NavCommand::Previous) { - focus_tab(std::max(0, mSelectedTabIndex - 1)); - event.StopPropagation(); - return; - } - if (cmd == NavCommand::Right || cmd == NavCommand::Next) { - focus_tab(std::min(static_cast(mTabs.size()) - 1, mSelectedTabIndex + 1)); - event.StopPropagation(); - return; - } - if (cmd == NavCommand::Confirm && mSelectedTabIndex >= 0 && - mSelectedTabIndex < static_cast(mTabActions.size())) - { - mTabActions[mSelectedTabIndex](); - event.StopPropagation(); - return; - } - if (cmd == NavCommand::Cancel) { - hide(); - event.StopPropagation(); - } - }); -} - -Popup::~Popup() { - auto* context = aurora::rmlui::get_context(); - if (context != nullptr && mDocument != nullptr) { - context->UnloadDocument(mDocument); - } } void Popup::show() { @@ -117,7 +67,7 @@ void Popup::show() { } mHideDeadline.reset(); - mDocument->Show(); + Document::show(); mVisible = true; } @@ -129,9 +79,11 @@ void Popup::hide() { if (auto* popup = mDocument->GetElementById("popup")) { popup->SetClass("popup-hidden", true); - mHideDeadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(500); // Must match the transition duration in popup.rcss + mHideDeadline = + std::chrono::steady_clock::now() + + std::chrono::milliseconds(500); // Must match the transition duration in popup.rcss } else { - mDocument->Hide(); + Document::hide(); } mVisible = false; } @@ -148,7 +100,29 @@ bool Popup::is_visible() const { return mVisible; } -void Popup::update() noexcept { +bool Popup::handle_nav_command(Rml::Event& event, NavCommand cmd) { + if (cmd == NavCommand::Left || cmd == NavCommand::Previous) { + focus_tab(std::max(0, mSelectedTabIndex - 1)); + return true; + } + if (cmd == NavCommand::Right || cmd == NavCommand::Next) { + focus_tab(std::min(static_cast(mTabs.size()) - 1, mSelectedTabIndex + 1)); + return true; + } + if (cmd == NavCommand::Confirm && mSelectedTabIndex >= 0 && + mSelectedTabIndex < static_cast(mTabActions.size())) + { + mTabActions[mSelectedTabIndex](); + return true; + } + if (cmd == NavCommand::Cancel) { + hide(); + return true; + } + return false; +} + +void Popup::update() { if (mDocument == nullptr) { return; } @@ -164,7 +138,7 @@ void Popup::update() noexcept { for (const auto& tab : mTabs) { tabs.push_back(tab.get()); } - dusk::ui::set_selected_tab(tabs, mSelectedTabIndex); + ui::set_selected_tab(tabs, mSelectedTabIndex); } void Popup::set_selected_tab(int index) { @@ -177,7 +151,7 @@ void Popup::set_selected_tab(int index) { for (const auto& tab : mTabs) { tabs.push_back(tab.get()); } - dusk::ui::set_selected_tab(tabs, mSelectedTabIndex); + ui::set_selected_tab(tabs, mSelectedTabIndex); } bool Popup::focus_tab(int index) { diff --git a/src/dusk/ui/popup.hpp b/src/dusk/ui/popup.hpp index 002fffd0ca..7cd437d441 100644 --- a/src/dusk/ui/popup.hpp +++ b/src/dusk/ui/popup.hpp @@ -1,8 +1,7 @@ #pragma once -#include - #include "button.hpp" +#include "document.hpp" #include "event.hpp" #include @@ -12,36 +11,33 @@ namespace dusk::ui { -class Window; - -class Popup { +class Popup : public Document { public: - Popup(Window& settingsWindow, Window& editorWindow); - ~Popup(); + Popup(); Popup(const Popup&) = delete; Popup& operator=(const Popup&) = delete; - void show(); - void hide(); + void show() override; + void hide() override; + void update() override; + void toggle(); bool is_visible() const; - void update() noexcept; + +protected: + bool handle_nav_command(Rml::Event& event, NavCommand cmd) override; private: void set_selected_tab(int index); bool focus_tab(int index); - Window& mSettingsWindow; - Window& mEditorWindow; - Rml::ElementDocument* mDocument = nullptr; std::vector > mTabs; std::vector > mTabActions; std::unique_ptr