mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-06-28 17:20:51 -04:00
Initial attempt at the popup menu
This commit is contained in:
@@ -1470,16 +1470,21 @@ set(DUSK_FILES
|
||||
src/dusk/ui/editor.hpp
|
||||
src/dusk/ui/event.cpp
|
||||
src/dusk/ui/event.hpp
|
||||
src/dusk/ui/nav_types.hpp
|
||||
src/dusk/ui/number_button.cpp
|
||||
src/dusk/ui/number_button.hpp
|
||||
src/dusk/ui/pane.cpp
|
||||
src/dusk/ui/pane.hpp
|
||||
src/dusk/ui/popup.cpp
|
||||
src/dusk/ui/popup.hpp
|
||||
src/dusk/ui/select_button.cpp
|
||||
src/dusk/ui/select_button.hpp
|
||||
src/dusk/ui/settings.cpp
|
||||
src/dusk/ui/settings.hpp
|
||||
src/dusk/ui/string_button.cpp
|
||||
src/dusk/ui/string_button.hpp
|
||||
src/dusk/ui/tab_button.cpp
|
||||
src/dusk/ui/tab_button.hpp
|
||||
src/dusk/ui/ui.hpp
|
||||
src/dusk/ui/ui.cpp
|
||||
src/dusk/ui/window.hpp
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
*, *:before, *:after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
overflow: visible;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: "Fira Sans Condensed";
|
||||
font-weight: bold;
|
||||
font-size: 18dp;
|
||||
color: #E0DBC8;
|
||||
}
|
||||
|
||||
button {
|
||||
cursor: pointer;
|
||||
focus: auto;
|
||||
}
|
||||
|
||||
.popup {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
width: 100%;
|
||||
height: 64dp;
|
||||
background-color: rgba(21, 22, 16, 80%);
|
||||
border-bottom: 2dp #92875B;
|
||||
backdrop-filter: blur(5dp);
|
||||
transform: translateY(0);
|
||||
transition: transform 0.5s cubic-in-out, opacity 0.5s cubic-in-out;
|
||||
}
|
||||
|
||||
.popup.popup-hidden {
|
||||
transform: translateY(-100%);
|
||||
}
|
||||
|
||||
.popup .tab-bar {
|
||||
display: flex;
|
||||
flex: 1 1 0;
|
||||
min-width: 0;
|
||||
overflow: auto hidden;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.popup .tab-bar .tab {
|
||||
flex: 0 0 auto;
|
||||
padding: 0 24dp;
|
||||
line-height: 64dp;
|
||||
opacity: 0.35;
|
||||
white-space: nowrap;
|
||||
color: #E0DBC8;
|
||||
decorator: vertical-gradient(#c2a42d00 #c2a42d00);
|
||||
transition: decorator 0.1s linear-in-out, opacity 0.1s linear-in-out;
|
||||
}
|
||||
|
||||
.popup .tab-bar .tab.selected {
|
||||
opacity: 1;
|
||||
border-bottom: 4dp #C2A42D;
|
||||
font-effect: glow(0dp 4dp 0dp 4dp black);
|
||||
}
|
||||
|
||||
.popup .tab-bar .tab:focus-visible,
|
||||
.popup .tab-bar .tab:hover {
|
||||
opacity: 1;
|
||||
font-effect: glow(0dp 4dp 0dp 4dp black);
|
||||
decorator: vertical-gradient(#c2a42d00 #c2a42d26);
|
||||
}
|
||||
|
||||
.popup .tab-bar .tab:active {
|
||||
decorator: vertical-gradient(#c2a42d10 #c2a42d40);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<rml>
|
||||
<head>
|
||||
<title>Popup</title>
|
||||
<link type="text/rcss" href="popup.rcss" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="popup" class="popup">
|
||||
<div id="tab-bar" class="tab-bar"></div>
|
||||
</div>
|
||||
</body>
|
||||
</rml>
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
namespace dusk::ui {
|
||||
|
||||
enum class NavCommand {
|
||||
None,
|
||||
Up,
|
||||
Down,
|
||||
Left,
|
||||
Right,
|
||||
Next, // R1
|
||||
Previous, // L1
|
||||
Confirm, // A
|
||||
Cancel, // B
|
||||
};
|
||||
|
||||
} // namespace dusk::ui
|
||||
@@ -0,0 +1,191 @@
|
||||
#include "popup.hpp"
|
||||
|
||||
#include <RmlUi/Core.h>
|
||||
|
||||
#include "aurora/rmlui.hpp"
|
||||
#include "tab_button.hpp"
|
||||
#include "ui.hpp"
|
||||
#include "window.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <utility>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
auto* tabBar = mDocument->GetElementById("tab-bar");
|
||||
if (tabBar == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::array<Rml::String, 5> tabLabels = {
|
||||
"Settings",
|
||||
"Warp",
|
||||
"Editor",
|
||||
"Reset",
|
||||
"Exit",
|
||||
};
|
||||
|
||||
// TODO: Make warp, reset, and exit buttons work
|
||||
mTabActions = {
|
||||
[this] {
|
||||
hide();
|
||||
mSettingsWindow.show();
|
||||
mSettingsWindow.focus_for_input();
|
||||
set_selected_tab(0);
|
||||
},
|
||||
[this] {
|
||||
set_selected_tab(1);
|
||||
},
|
||||
[this] {
|
||||
hide();
|
||||
mEditorWindow.show();
|
||||
mEditorWindow.focus_for_input();
|
||||
set_selected_tab(2);
|
||||
},
|
||||
[this] {
|
||||
set_selected_tab(3);
|
||||
},
|
||||
[this] {
|
||||
set_selected_tab(4);
|
||||
},
|
||||
};
|
||||
|
||||
mTabs.reserve(tabLabels.size());
|
||||
for (int i = 0; i < static_cast<int>(tabLabels.size()); ++i) {
|
||||
mTabs.push_back(create_tab_button(tabBar, tabLabels[i], i == mSelectedTabIndex,
|
||||
[this, i](Rml::Event&) {
|
||||
if (i >= 0 && i < static_cast<int>(mTabActions.size())) {
|
||||
mTabActions[i]();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
mKeyListener = std::make_unique<ScopedEventListener>(
|
||||
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<int>(mTabs.size()) - 1, mSelectedTabIndex + 1));
|
||||
event.StopPropagation();
|
||||
return;
|
||||
}
|
||||
if (cmd == NavCommand::Confirm && mSelectedTabIndex >= 0 &&
|
||||
mSelectedTabIndex < static_cast<int>(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() {
|
||||
if (mDocument == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
mHideDeadline.reset();
|
||||
mDocument->Show();
|
||||
mVisible = true;
|
||||
}
|
||||
|
||||
void Popup::hide() {
|
||||
if (mDocument == nullptr) {
|
||||
mVisible = false;
|
||||
return;
|
||||
}
|
||||
|
||||
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
|
||||
} else {
|
||||
mDocument->Hide();
|
||||
}
|
||||
mVisible = false;
|
||||
}
|
||||
|
||||
void Popup::toggle() {
|
||||
if (is_visible()) {
|
||||
hide();
|
||||
} else {
|
||||
show();
|
||||
}
|
||||
}
|
||||
|
||||
bool Popup::is_visible() const {
|
||||
return mVisible;
|
||||
}
|
||||
|
||||
void Popup::update() noexcept {
|
||||
if (mDocument == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (mHideDeadline.has_value() && std::chrono::steady_clock::now() >= *mHideDeadline) {
|
||||
mDocument->Hide();
|
||||
mHideDeadline.reset();
|
||||
}
|
||||
if (mTabs.empty()) {
|
||||
return;
|
||||
}
|
||||
std::vector<Button*> tabs;
|
||||
tabs.reserve(mTabs.size());
|
||||
for (const auto& tab : mTabs) {
|
||||
tabs.push_back(tab.get());
|
||||
}
|
||||
dusk::ui::set_selected_tab(tabs, mSelectedTabIndex);
|
||||
}
|
||||
|
||||
void Popup::set_selected_tab(int index) {
|
||||
if (index < 0 || index >= static_cast<int>(mTabs.size())) {
|
||||
return;
|
||||
}
|
||||
mSelectedTabIndex = index;
|
||||
std::vector<Button*> tabs;
|
||||
tabs.reserve(mTabs.size());
|
||||
for (const auto& tab : mTabs) {
|
||||
tabs.push_back(tab.get());
|
||||
}
|
||||
dusk::ui::set_selected_tab(tabs, mSelectedTabIndex);
|
||||
}
|
||||
|
||||
bool Popup::focus_tab(int index) {
|
||||
if (index < 0 || index >= static_cast<int>(mTabs.size())) {
|
||||
return false;
|
||||
}
|
||||
set_selected_tab(index);
|
||||
return mTabs[index]->focus();
|
||||
}
|
||||
|
||||
} // namespace dusk::ui
|
||||
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include <RmlUi/Core/ElementDocument.h>
|
||||
|
||||
#include "button.hpp"
|
||||
#include "event.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
namespace dusk::ui {
|
||||
|
||||
class Window;
|
||||
|
||||
class Popup {
|
||||
public:
|
||||
Popup(Window& settingsWindow, Window& editorWindow);
|
||||
~Popup();
|
||||
|
||||
Popup(const Popup&) = delete;
|
||||
Popup& operator=(const Popup&) = delete;
|
||||
|
||||
void show();
|
||||
void hide();
|
||||
void toggle();
|
||||
bool is_visible() const;
|
||||
void update() noexcept;
|
||||
|
||||
private:
|
||||
void set_selected_tab(int index);
|
||||
bool focus_tab(int index);
|
||||
|
||||
Window& mSettingsWindow;
|
||||
Window& mEditorWindow;
|
||||
Rml::ElementDocument* mDocument = nullptr;
|
||||
std::vector<std::unique_ptr<Button> > mTabs;
|
||||
std::vector<std::function<void()> > mTabActions;
|
||||
std::unique_ptr<Button> mCloseButton;
|
||||
int mSelectedTabIndex = 0;
|
||||
bool mVisible = false;
|
||||
std::optional<std::chrono::steady_clock::time_point> mHideDeadline;
|
||||
std::unique_ptr<ScopedEventListener> mKeyListener;
|
||||
};
|
||||
|
||||
} // namespace dusk::ui
|
||||
@@ -0,0 +1,24 @@
|
||||
#include "tab_button.hpp"
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace dusk::ui {
|
||||
|
||||
std::unique_ptr<Button> create_tab_button(Rml::Element* tabBar, const Rml::String& title,
|
||||
bool selected, std::function<void(Rml::Event&)> onPressed) {
|
||||
return std::make_unique<Button>(tabBar,
|
||||
Button::Props{
|
||||
.text = title,
|
||||
.onPressed = std::move(onPressed),
|
||||
.selected = selected,
|
||||
},
|
||||
"tab");
|
||||
}
|
||||
|
||||
void set_selected_tab(std::vector<Button*>& tabs, int selectedIndex) {
|
||||
for (int i = 0; i < static_cast<int>(tabs.size()); ++i) {
|
||||
tabs[i]->set_selected(i == selectedIndex);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace dusk::ui
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "button.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace dusk::ui {
|
||||
|
||||
std::unique_ptr<Button> create_tab_button(Rml::Element* tabBar, const Rml::String& title, bool selected, std::function<void(Rml::Event&)> onPressed);
|
||||
void set_selected_tab(std::vector<Button*>& tabs, int selectedIndex);
|
||||
|
||||
} // namespace dusk::ui
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
#include "popup.hpp"
|
||||
#include "window.hpp"
|
||||
|
||||
namespace dusk::ui {
|
||||
@@ -17,6 +18,7 @@ void load_font(const char* filename, bool fallback = false) {
|
||||
|
||||
bool sInitialized = false;
|
||||
std::vector<std::unique_ptr<Window> > sWindows;
|
||||
std::unique_ptr<Popup> sPopup;
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -37,6 +39,7 @@ bool initialize() noexcept {
|
||||
}
|
||||
|
||||
void shutdown() noexcept {
|
||||
sPopup.reset();
|
||||
sWindows.clear();
|
||||
sInitialized = false;
|
||||
}
|
||||
@@ -55,10 +58,18 @@ void remove_window(Window& window) noexcept {
|
||||
// TODO
|
||||
}
|
||||
|
||||
Popup& add_popup(std::unique_ptr<Popup> popupMenu) noexcept {
|
||||
sPopup = std::move(popupMenu);
|
||||
return *sPopup;
|
||||
}
|
||||
|
||||
void update() noexcept {
|
||||
for (const auto& window : sWindows) {
|
||||
window->update();
|
||||
}
|
||||
if (sPopup != nullptr) {
|
||||
sPopup->update();
|
||||
}
|
||||
}
|
||||
|
||||
std::filesystem::path resource_path(const std::filesystem::path& filename) noexcept {
|
||||
|
||||
+5
-12
@@ -4,20 +4,11 @@
|
||||
#include <SDL3/SDL_events.h>
|
||||
#include <filesystem>
|
||||
|
||||
#include "nav_types.hpp"
|
||||
|
||||
namespace dusk::ui {
|
||||
class Window;
|
||||
|
||||
enum class NavCommand {
|
||||
None,
|
||||
Up,
|
||||
Down,
|
||||
Left,
|
||||
Right,
|
||||
Next, // R1
|
||||
Previous, // L1
|
||||
Confirm, // A
|
||||
Cancel, // B
|
||||
};
|
||||
class Popup;
|
||||
|
||||
bool initialize() noexcept;
|
||||
void shutdown() noexcept;
|
||||
@@ -28,6 +19,8 @@ void update() noexcept;
|
||||
Window& add_window(std::unique_ptr<Window> window) noexcept;
|
||||
void remove_window(Window& window) noexcept;
|
||||
|
||||
Popup& add_popup(std::unique_ptr<Popup> popup) noexcept;
|
||||
|
||||
std::filesystem::path resource_path(const std::filesystem::path& filename) noexcept;
|
||||
std::string escape(std::string_view str) noexcept;
|
||||
|
||||
|
||||
+19
-10
@@ -5,8 +5,8 @@
|
||||
|
||||
#include "aurora/lib/window.hpp"
|
||||
#include "aurora/rmlui.hpp"
|
||||
#include "button.hpp"
|
||||
#include "magic_enum.hpp"
|
||||
#include "tab_button.hpp"
|
||||
#include "ui.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
@@ -127,6 +127,15 @@ void Window::hide() {
|
||||
}
|
||||
}
|
||||
|
||||
void Window::focus_for_input() noexcept {
|
||||
if (!mContentComponents.empty()) {
|
||||
if (mContentComponents.front()->focus()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
focus_active_tab();
|
||||
}
|
||||
|
||||
void Window::update() {
|
||||
update_safe_area();
|
||||
for (const auto& component : mContentComponents) {
|
||||
@@ -170,9 +179,12 @@ bool Window::set_active_tab(int index) {
|
||||
const auto& tab = mTabs[index];
|
||||
if (tab.button->focus()) {
|
||||
clear_content();
|
||||
for (int i = 0; i < mTabs.size(); i++) {
|
||||
mTabs[i].button->set_selected(i == index);
|
||||
std::vector<Button*> buttons;
|
||||
buttons.reserve(mTabs.size());
|
||||
for (auto& tab : mTabs) {
|
||||
buttons.push_back(tab.button.get());
|
||||
}
|
||||
set_selected_tab(buttons, index);
|
||||
mSelectedTabIndex = index;
|
||||
if (tab.builder) {
|
||||
tab.builder(mDocument->GetElementById("content"));
|
||||
@@ -190,13 +202,10 @@ void Window::add_tab(const Rml::String& title, TabBuilder builder) {
|
||||
auto* tabBar = mDocument->GetElementById("tab-bar");
|
||||
mTabs.emplace_back(Tab{
|
||||
.title = title,
|
||||
.button = std::make_unique<Button>(tabBar,
|
||||
Button::Props{
|
||||
.text = title,
|
||||
.onPressed = [this, index](Rml::Event&) { set_active_tab(index); },
|
||||
.selected = index == mSelectedTabIndex,
|
||||
},
|
||||
"tab"),
|
||||
.button = create_tab_button(
|
||||
tabBar, title, index == mSelectedTabIndex, [this, index](Rml::Event&) {
|
||||
set_active_tab(index);
|
||||
}),
|
||||
.builder = std::move(builder),
|
||||
});
|
||||
if (index == mSelectedTabIndex) {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
#include "button.hpp"
|
||||
#include "component.hpp"
|
||||
#include "ui.hpp"
|
||||
#include "nav_types.hpp"
|
||||
|
||||
namespace dusk::ui {
|
||||
|
||||
@@ -38,6 +38,8 @@ public:
|
||||
void show();
|
||||
void hide();
|
||||
|
||||
void focus_for_input() noexcept;
|
||||
|
||||
void update();
|
||||
bool set_active_tab(int index);
|
||||
|
||||
|
||||
@@ -58,6 +58,8 @@
|
||||
#include "dusk/imgui/ImGuiConsole.hpp"
|
||||
#include "dusk/ui/ui.hpp"
|
||||
#include "dusk/ui/editor.hpp"
|
||||
#include "dusk/ui/popup.hpp"
|
||||
#include "dusk/ui/settings.hpp"
|
||||
#include "version.h"
|
||||
|
||||
#include <aurora/aurora.h>
|
||||
@@ -79,8 +81,6 @@
|
||||
#include "tracy/Tracy.hpp"
|
||||
#include <RmlUi/Core.h>
|
||||
|
||||
#include "dusk/ui/settings.hpp"
|
||||
|
||||
// --- GLOBALS ---
|
||||
s8 mDoMain::developmentMode = -1;
|
||||
OSTime mDoMain::sPowerOnTime;
|
||||
@@ -590,9 +590,11 @@ int game_main(int argc, char* argv[]) {
|
||||
|
||||
// TODO: just for testing
|
||||
auto& editorWindow = dusk::ui::add_window(std::make_unique<dusk::ui::EditorWindow>());
|
||||
editorWindow.show();
|
||||
// editorWindow.show();
|
||||
auto& settingsWindow = dusk::ui::add_window(std::make_unique<dusk::ui::SettingsWindow>());
|
||||
// settingsWindow.show();
|
||||
auto& popup = dusk::ui::add_popup(std::make_unique<dusk::ui::Popup>(settingsWindow, editorWindow));
|
||||
popup.show();
|
||||
|
||||
std::string dvd_path;
|
||||
bool dvd_opened = false;
|
||||
|
||||
Reference in New Issue
Block a user