diff --git a/extern/aurora b/extern/aurora index 8a2b80ecb1..1fed316829 160000 --- a/extern/aurora +++ b/extern/aurora @@ -1 +1 @@ -Subproject commit 8a2b80ecb104625319c2818fd6d752bab8617679 +Subproject commit 1fed316829004084aeae75e8a4c4315bd33e4890 diff --git a/files.cmake b/files.cmake index 387913013a..13fd5c9629 100644 --- a/files.cmake +++ b/files.cmake @@ -1462,28 +1462,8 @@ set(DUSK_FILES src/dusk/imgui/ImGuiStateShare.cpp src/dusk/imgui/ImGuiAchievements.hpp src/dusk/imgui/ImGuiAchievements.cpp - src/dusk/ui/button.hpp - src/dusk/ui/button.cpp - src/dusk/ui/control_surface.hpp - src/dusk/ui/control_surface.cpp - src/dusk/ui/disc_state.hpp - src/dusk/ui/disc_state.cpp - src/dusk/ui/element.hpp - src/dusk/ui/element.cpp - src/dusk/ui/focus_border.hpp - src/dusk/ui/focus_border.cpp - src/dusk/ui/game_menu.hpp - src/dusk/ui/game_menu.cpp - src/dusk/ui/game_option.hpp - src/dusk/ui/game_option.cpp - src/dusk/ui/label.hpp - src/dusk/ui/label.cpp - src/dusk/ui/prelaunch_layout.hpp - src/dusk/ui/prelaunch_layout.cpp - src/dusk/ui/prelaunch_screen.hpp - src/dusk/ui/prelaunch_screen.cpp - src/dusk/ui/theme.hpp - src/dusk/ui/theme.cpp + src/dusk/ui/editor.cpp + src/dusk/ui/editor.hpp src/dusk/ui/ui.hpp src/dusk/ui/ui.cpp src/dusk/ui/window.hpp diff --git a/res/FiraSans-Regular.ttf b/res/FiraSans-Regular.ttf new file mode 100644 index 0000000000..6f80647494 Binary files /dev/null and b/res/FiraSans-Regular.ttf differ diff --git a/res/FiraSansCondensed-Bold.ttf b/res/FiraSansCondensed-Bold.ttf new file mode 100644 index 0000000000..ec7e841549 Binary files /dev/null and b/res/FiraSansCondensed-Bold.ttf differ diff --git a/res/FiraSansCondensed-Regular.ttf b/res/FiraSansCondensed-Regular.ttf new file mode 100644 index 0000000000..6e1a192127 Binary files /dev/null and b/res/FiraSansCondensed-Regular.ttf differ diff --git a/src/dusk/imgui/ImGuiConsole.cpp b/src/dusk/imgui/ImGuiConsole.cpp index 078ed3f8fb..a94c7d1f5b 100644 --- a/src/dusk/imgui/ImGuiConsole.cpp +++ b/src/dusk/imgui/ImGuiConsole.cpp @@ -22,8 +22,6 @@ #include "dusk/livesplit.h" #include "dusk/main.h" #include "dusk/settings.h" -#include "dusk/ui/game_menu.hpp" -#include "dusk/ui/prelaunch_screen.hpp" #include "m_Do/m_Do_controller_pad.h" #include "m_Do/m_Do_main.h" #include "tracy/Tracy.hpp" @@ -341,14 +339,14 @@ namespace dusk { ImGuiMenuGame::ToggleFullscreen(); } - if (!dusk::IsGameLaunched && !dusk::ui::prelaunch::is_active()) { + if (!dusk::IsGameLaunched) { m_preLaunchWindow.draw(); } m_isHidden = !getSettings().backend.duskMenuOpen; if (dusk::IsGameLaunched) { if (ImGui::IsKeyPressed(ImGuiKey_F1)) { - dusk::ui::game_menu::toggle(); + m_isHidden = !m_isHidden; } if (ImGui::IsKeyPressed(ImGuiKey_GamepadBack)) { m_isHidden = !m_isHidden; diff --git a/src/dusk/ui/button.cpp b/src/dusk/ui/button.cpp deleted file mode 100644 index b2c4eae6e8..0000000000 --- a/src/dusk/ui/button.cpp +++ /dev/null @@ -1,152 +0,0 @@ -#include "button.hpp" - -#include "control_surface.hpp" -#include "element.hpp" -#include "focus_border.hpp" -#include "label.hpp" -#include "theme.hpp" - -#include - -#include - -namespace dusk::ui { -namespace { - -ControlSurfaceTone control_surface_tone(ButtonVariant variant) { - switch (variant) { - case ButtonVariant::Primary: - return ControlSurfaceTone::Primary; - case ButtonVariant::Secondary: - return ControlSurfaceTone::Secondary; - case ButtonVariant::Quiet: - default: - return ControlSurfaceTone::Quiet; - } -} - -} // namespace - -Button::Button(Rml::Element* parent, std::string_view id, std::string_view text, - ButtonVariant variant, std::function pressedCallback) - : m_variant(variant), m_pressedCallback(std::move(pressedCallback)) { - using namespace theme; - - m_element = append(parent, "button", id, - { - {Rml::PropertyId::Display, Rml::Style::Display::Flex}, - {Rml::PropertyId::Position, Rml::Style::Position::Relative}, - {Rml::PropertyId::FlexDirection, Rml::Style::FlexDirection::Row}, - {Rml::PropertyId::AlignItems, Rml::Style::AlignItems::Center}, - {Rml::PropertyId::JustifyContent, Rml::Style::JustifyContent::Center}, - {Rml::PropertyId::BoxSizing, Rml::Style::BoxSizing::BorderBox}, - {Rml::PropertyId::Width, rml_percent(100.0f)}, - {Rml::PropertyId::Height, rml_dp(68.0f)}, - {Rml::PropertyId::MinHeight, rml_dp(68.0f)}, - {Rml::PropertyId::MaxHeight, rml_dp(68.0f)}, - {Rml::PropertyId::PaddingLeft, rml_dp(22.0f)}, - {Rml::PropertyId::PaddingRight, rml_dp(22.0f)}, - {Rml::PropertyId::BorderTopWidth, rml_dp(BorderWidth)}, - {Rml::PropertyId::BorderRightWidth, rml_dp(BorderWidth)}, - {Rml::PropertyId::BorderBottomWidth, rml_dp(BorderWidth)}, - {Rml::PropertyId::BorderLeftWidth, rml_dp(BorderWidth)}, - {Rml::PropertyId::BorderTopLeftRadius, rml_dp(BorderRadiusMedium)}, - {Rml::PropertyId::BorderTopRightRadius, rml_dp(BorderRadiusMedium)}, - {Rml::PropertyId::BorderBottomRightRadius, rml_dp(BorderRadiusMedium)}, - {Rml::PropertyId::BorderBottomLeftRadius, rml_dp(BorderRadiusMedium)}, - {Rml::PropertyId::Cursor, rml_string("pointer")}, - {Rml::PropertyId::TabIndex, Rml::Style::TabIndex::Auto}, - {Rml::PropertyId::NavUp, Rml::Style::Nav::Auto}, - {Rml::PropertyId::NavDown, Rml::Style::Nav::Auto}, - {Rml::PropertyId::NavLeft, Rml::Style::Nav::Auto}, - {Rml::PropertyId::NavRight, Rml::Style::Nav::Auto}, - {Rml::PropertyId::Opacity, rml_number(1.0f)}, - {Rml::PropertyId::FontFamily, rml_string("Inter")}, - {Rml::PropertyId::Color, rml_color(Text)}, - }); - - add_focus_border(m_element, BorderRadiusMedium); - m_label = append_text(m_element, "span", text); - apply_label_style(m_label, LabelStyle::Medium); - set_props(m_label, { - {Rml::PropertyId::PointerEvents, Rml::Style::PointerEvents::None}, - {Rml::PropertyId::TextAlign, Rml::Style::TextAlign::Center}, - }); - - m_element->AddEventListener(Rml::EventId::Click, this); - m_element->AddEventListener(Rml::EventId::Focus, this); - m_element->AddEventListener(Rml::EventId::Blur, this); - m_element->AddEventListener(Rml::EventId::Mouseover, this); - m_element->AddEventListener(Rml::EventId::Mouseout, this); - apply_style(); -} - -Button::~Button() { - if (m_element == nullptr) { - return; - } - - m_element->RemoveEventListener(Rml::EventId::Click, this); - m_element->RemoveEventListener(Rml::EventId::Focus, this); - m_element->RemoveEventListener(Rml::EventId::Blur, this); - m_element->RemoveEventListener(Rml::EventId::Mouseover, this); - m_element->RemoveEventListener(Rml::EventId::Mouseout, this); - m_element = nullptr; -} - -void Button::ProcessEvent(Rml::Event& event) { - switch (event.GetId()) { - case Rml::EventId::Click: - if (m_pressedCallback) { - m_pressedCallback(); - } - break; - case Rml::EventId::Focus: - m_focused = true; - apply_style(); - break; - case Rml::EventId::Blur: - m_focused = false; - apply_style(); - break; - case Rml::EventId::Mouseover: - m_hovered = true; - apply_style(); - break; - case Rml::EventId::Mouseout: - m_hovered = false; - apply_style(); - break; - default: - break; - } -} - -Rml::Element* Button::element() const { - return m_element; -} - -std::string Button::id() const { - return m_element == nullptr ? std::string{} : m_element->GetId(); -} - -void Button::set_text(std::string_view text) { - ui::set_text(m_label, text); -} - -void Button::apply_style() { - using namespace theme; - - if (m_element == nullptr) { - return; - } - - const bool active = m_hovered || m_focused; - apply_control_surface_style( - m_element, control_surface_style(control_surface_tone(m_variant)), active); - m_element->SetProperty(Rml::PropertyId::Color, rml_color(active ? TextActive : Text)); - m_label->SetProperty(Rml::PropertyId::Color, rml_color(active ? TextActive : Text)); - set_focus_border_visible(m_element, m_focused); -} - -} // namespace dusk::ui diff --git a/src/dusk/ui/button.hpp b/src/dusk/ui/button.hpp deleted file mode 100644 index 7123914bbb..0000000000 --- a/src/dusk/ui/button.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include - -#include -#include -#include - -namespace Rml { -class Element; -} - -namespace dusk::ui { - -enum class ButtonVariant { - Primary, - Secondary, - Quiet, -}; - -class Button : public Rml::EventListener { -public: - Button(Rml::Element* parent, std::string_view id, std::string_view text, ButtonVariant variant, - std::function pressedCallback); - ~Button() override; - - Button(const Button&) = delete; - Button& operator=(const Button&) = delete; - - void ProcessEvent(Rml::Event& event) override; - - Rml::Element* element() const; - std::string id() const; - void set_text(std::string_view text); - -private: - Rml::Element* m_element = nullptr; - Rml::Element* m_label = nullptr; - ButtonVariant m_variant = ButtonVariant::Secondary; - std::function m_pressedCallback; - bool m_hovered = false; - bool m_focused = false; - - void apply_style(); -}; - -} // namespace dusk::ui diff --git a/src/dusk/ui/control_surface.cpp b/src/dusk/ui/control_surface.cpp deleted file mode 100644 index 8d78f9053b..0000000000 --- a/src/dusk/ui/control_surface.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#include "control_surface.hpp" - -#include - -namespace dusk::ui { -ControlSurfaceStyle control_surface_style(ControlSurfaceTone tone) { - switch (tone) { - case ControlSurfaceTone::Primary: - return { - .accent = theme::Primary, - .inactiveBorderOpacity = 112, - .inactiveBackgroundOpacity = 28, - .activeBorderOpacity = 255, - .activeBackgroundOpacity = 116, - }; - case ControlSurfaceTone::Secondary: - return { - .accent = theme::Secondary, - .inactiveBorderOpacity = 112, - .inactiveBackgroundOpacity = 28, - .activeBorderOpacity = 255, - .activeBackgroundOpacity = 116, - }; - case ControlSurfaceTone::Window: - return { - .accent = theme::WindowAccent, - .inactiveBorderOpacity = 0, - .inactiveBackgroundOpacity = 26, - .activeBorderOpacity = 200, - .activeBackgroundOpacity = 76, - }; - case ControlSurfaceTone::Quiet: - default: - return { - .accent = theme::Elevated, - .inactiveBorderOpacity = 86, - .inactiveBackgroundOpacity = 0, - .activeBorderOpacity = 150, - .activeBackgroundOpacity = 68, - }; - } -} - -void apply_control_surface_style( - Rml::Element* element, const ControlSurfaceStyle& style, bool active) { - if (element == nullptr) { - return; - } - - const auto borderColor = active ? rml_color(style.accent, style.activeBorderOpacity) : - rml_color(theme::ElevatedBorder, style.inactiveBorderOpacity); - element->SetProperty(Rml::PropertyId::BorderLeftColor, borderColor); - element->SetProperty(Rml::PropertyId::BorderRightColor, borderColor); - element->SetProperty(Rml::PropertyId::BorderTopColor, borderColor); - element->SetProperty(Rml::PropertyId::BorderBottomColor, borderColor); - element->SetProperty(Rml::PropertyId::BackgroundColor, - rml_color(style.accent, - active ? style.activeBackgroundOpacity : style.inactiveBackgroundOpacity)); -} -} // namespace dusk::ui diff --git a/src/dusk/ui/control_surface.hpp b/src/dusk/ui/control_surface.hpp deleted file mode 100644 index 2d68441c9f..0000000000 --- a/src/dusk/ui/control_surface.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include "theme.hpp" - -namespace Rml { -class Element; -} - -namespace dusk::ui { -enum class ControlSurfaceTone { - Primary, - Secondary, - Quiet, - Window, -}; - -struct ControlSurfaceStyle { - theme::Color accent = theme::Primary; - int inactiveBorderOpacity = 86; - int inactiveBackgroundOpacity = 0; - int activeBorderOpacity = 150; - int activeBackgroundOpacity = 68; -}; - -ControlSurfaceStyle control_surface_style(ControlSurfaceTone tone); -void apply_control_surface_style( - Rml::Element* element, const ControlSurfaceStyle& style, bool active); -} // namespace dusk::ui diff --git a/src/dusk/ui/disc_state.cpp b/src/dusk/ui/disc_state.cpp deleted file mode 100644 index 23c102f610..0000000000 --- a/src/dusk/ui/disc_state.cpp +++ /dev/null @@ -1,153 +0,0 @@ -#include "disc_state.hpp" - -#include "element.hpp" -#include "focus_border.hpp" -#include "label.hpp" -#include "theme.hpp" - -#include - -#include - -namespace dusk::ui { - -DiscState::DiscState(Rml::Element* parent, std::string_view id, std::string_view text, - std::string_view statusText, bool statusIsError, std::function pressedCallback) - : m_pressedCallback(std::move(pressedCallback)), m_statusIsError(statusIsError) { - using namespace theme; - - m_element = append(parent, "button", id, - { - {Rml::PropertyId::Display, Rml::Style::Display::Flex}, - {Rml::PropertyId::Position, Rml::Style::Position::Relative}, - {Rml::PropertyId::FlexDirection, Rml::Style::FlexDirection::Column}, - {Rml::PropertyId::AlignItems, Rml::Style::AlignItems::Stretch}, - {Rml::PropertyId::RowGap, rml_dp(6.0f)}, - {Rml::PropertyId::ColumnGap, rml_dp(6.0f)}, - {Rml::PropertyId::Width, rml_percent(100.0f)}, - {Rml::PropertyId::BoxSizing, Rml::Style::BoxSizing::BorderBox}, - {Rml::PropertyId::PaddingTop, rml_dp(14.0f)}, - {Rml::PropertyId::PaddingRight, rml_dp(16.0f)}, - {Rml::PropertyId::PaddingBottom, rml_dp(14.0f)}, - {Rml::PropertyId::PaddingLeft, rml_dp(16.0f)}, - {Rml::PropertyId::BorderTopWidth, rml_dp(BorderWidth)}, - {Rml::PropertyId::BorderRightWidth, rml_dp(BorderWidth)}, - {Rml::PropertyId::BorderBottomWidth, rml_dp(BorderWidth)}, - {Rml::PropertyId::BorderLeftWidth, rml_dp(BorderWidth)}, - {Rml::PropertyId::BorderTopLeftRadius, rml_dp(BorderRadiusSmall)}, - {Rml::PropertyId::BorderTopRightRadius, rml_dp(BorderRadiusSmall)}, - {Rml::PropertyId::BorderBottomRightRadius, rml_dp(BorderRadiusSmall)}, - {Rml::PropertyId::BorderBottomLeftRadius, rml_dp(BorderRadiusSmall)}, - {Rml::PropertyId::Cursor, rml_string("pointer")}, - {Rml::PropertyId::TabIndex, Rml::Style::TabIndex::Auto}, - {Rml::PropertyId::NavUp, Rml::Style::Nav::Auto}, - {Rml::PropertyId::NavDown, Rml::Style::Nav::Auto}, - {Rml::PropertyId::NavLeft, Rml::Style::Nav::Auto}, - {Rml::PropertyId::NavRight, Rml::Style::Nav::Auto}, - {Rml::PropertyId::FontFamily, rml_string("Inter")}, - }); - - add_focus_border(m_element, BorderRadiusSmall); - - m_value = add_label(m_element, text, LabelStyle::Body); - set_props(m_value, { - {Rml::PropertyId::OverflowX, Rml::Style::Overflow::Hidden}, - {Rml::PropertyId::OverflowY, Rml::Style::Overflow::Hidden}, - {Rml::PropertyId::TextOverflow, Rml::Style::TextOverflow::Ellipsis}, - {Rml::PropertyId::WhiteSpace, Rml::Style::WhiteSpace::Nowrap}, - {Rml::PropertyId::PointerEvents, Rml::Style::PointerEvents::None}, - }); - - if (!statusText.empty()) { - m_status = add_label(m_element, statusText, LabelStyle::Annotation); - set_props(m_status, { - {Rml::PropertyId::PointerEvents, Rml::Style::PointerEvents::None}, - {Rml::PropertyId::WhiteSpace, Rml::Style::WhiteSpace::Normal}, - }); - } - - m_element->AddEventListener(Rml::EventId::Click, this); - m_element->AddEventListener(Rml::EventId::Focus, this); - m_element->AddEventListener(Rml::EventId::Blur, this); - m_element->AddEventListener(Rml::EventId::Mouseover, this); - m_element->AddEventListener(Rml::EventId::Mouseout, this); - apply_style(); -} - -DiscState::~DiscState() { - if (m_element == nullptr) { - return; - } - - m_element->RemoveEventListener(Rml::EventId::Click, this); - m_element->RemoveEventListener(Rml::EventId::Focus, this); - m_element->RemoveEventListener(Rml::EventId::Blur, this); - m_element->RemoveEventListener(Rml::EventId::Mouseover, this); - m_element->RemoveEventListener(Rml::EventId::Mouseout, this); - m_element = nullptr; -} - -void DiscState::ProcessEvent(Rml::Event& event) { - switch (event.GetId()) { - case Rml::EventId::Click: - if (m_pressedCallback) { - m_pressedCallback(); - } - break; - case Rml::EventId::Focus: - m_focused = true; - apply_style(); - break; - case Rml::EventId::Blur: - m_focused = false; - apply_style(); - break; - case Rml::EventId::Mouseover: - m_hovered = true; - apply_style(); - break; - case Rml::EventId::Mouseout: - m_hovered = false; - apply_style(); - break; - default: - break; - } -} - -Rml::Element* DiscState::element() const { - return m_element; -} - -std::string DiscState::id() const { - return m_element == nullptr ? std::string{} : m_element->GetId(); -} - -void DiscState::apply_style() { - using namespace theme; - - if (m_element == nullptr) { - return; - } - - const bool active = m_hovered || m_focused; - const Color accent = m_statusIsError ? Danger : Primary; - - m_element->SetProperty(Rml::PropertyId::BackgroundColor, - rml_color(accent, active ? 52 : (m_statusIsError ? 32 : 20))); - const auto borderColor = rml_color(accent, active ? 220 : (m_statusIsError ? 190 : 120)); - m_element->SetProperty(Rml::PropertyId::BorderTopColor, borderColor); - m_element->SetProperty(Rml::PropertyId::BorderRightColor, borderColor); - m_element->SetProperty(Rml::PropertyId::BorderBottomColor, borderColor); - m_element->SetProperty(Rml::PropertyId::BorderLeftColor, borderColor); - m_element->SetProperty(Rml::PropertyId::Color, rml_color(active ? TextActive : Text)); - - m_value->SetProperty(Rml::PropertyId::Color, rml_color(active ? TextActive : Text)); - if (m_status != nullptr) { - m_status->SetProperty( - Rml::PropertyId::Color, rml_color(m_statusIsError ? Danger : TextDim)); - } - set_focus_border_visible(m_element, m_focused); -} - -} // namespace dusk::ui diff --git a/src/dusk/ui/disc_state.hpp b/src/dusk/ui/disc_state.hpp deleted file mode 100644 index c0e00097a3..0000000000 --- a/src/dusk/ui/disc_state.hpp +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include - -#include -#include -#include - -namespace Rml { -class Element; -} - -namespace dusk::ui { - -class DiscState : public Rml::EventListener { -public: - DiscState(Rml::Element* parent, std::string_view id, std::string_view text, - std::string_view statusText, bool statusIsError, std::function pressedCallback); - ~DiscState() override; - - DiscState(const DiscState&) = delete; - DiscState& operator=(const DiscState&) = delete; - - void ProcessEvent(Rml::Event& event) override; - - Rml::Element* element() const; - std::string id() const; - -private: - Rml::Element* m_element = nullptr; - Rml::Element* m_value = nullptr; - Rml::Element* m_status = nullptr; - std::function m_pressedCallback; - bool m_statusIsError = false; - bool m_hovered = false; - bool m_focused = false; - - void apply_style(); -}; - -} // namespace dusk::ui diff --git a/src/dusk/ui/editor.cpp b/src/dusk/ui/editor.cpp new file mode 100644 index 0000000000..4b7a05dd2d --- /dev/null +++ b/src/dusk/ui/editor.cpp @@ -0,0 +1,112 @@ +#include "editor.hpp" + +#include + +namespace dusk::ui { +namespace { + +const Rml::String kPlayerStatusContent = R"RML( +
+
Player
+ + + + + + +
Equipment
+ + + + + +
+ +
+ + + + + + + + + +
+)RML"; + +const Rml::String kLocationContent = R"RML( +
+
Save Location
+ + + +
Horse Location
+ +
+
+)RML"; + +} // namespace + +EditorWindow::EditorWindow() + : Window({.tabs = { + {"Player Status", + [](Rml::Element* content) { + // TODO: actually bind values and events. wonder if we should have + // a SettingsPane element or something for sharing? + Rml::Factory::InstanceElementText(content, kPlayerStatusContent); + }}, + {"Location", + [](Rml::Element* content) { + Rml::Factory::InstanceElementText(content, kLocationContent); + }}, + {"Inventory"}, + }}) {} + +} // namespace dusk::ui \ No newline at end of file diff --git a/src/dusk/ui/editor.hpp b/src/dusk/ui/editor.hpp new file mode 100644 index 0000000000..cec381e277 --- /dev/null +++ b/src/dusk/ui/editor.hpp @@ -0,0 +1,11 @@ +#pragma once +#include "window.hpp" + +namespace dusk::ui { + +class EditorWindow : public Window { +public: + EditorWindow(); +}; + +} // namespace dusk::ui diff --git a/src/dusk/ui/element.cpp b/src/dusk/ui/element.cpp deleted file mode 100644 index 87a113486c..0000000000 --- a/src/dusk/ui/element.cpp +++ /dev/null @@ -1,94 +0,0 @@ -#include "element.hpp" - -#include - -#include - -namespace dusk::ui { - -std::string escape(std::string_view text) { - std::string result; - result.reserve(text.size()); - for (char c : text) { - switch (c) { - case '&': - result += "&"; - break; - case '<': - result += "<"; - break; - case '>': - result += ">"; - break; - case '"': - result += """; - break; - case '\'': - result += "'"; - break; - default: - result += c; - break; - } - } - return result; -} - -Rml::Element* append(Rml::Element* parent, std::string_view tag, std::string_view id, - std::initializer_list > properties) { - if (parent == nullptr) { - return nullptr; - } - - Rml::ElementDocument* document = parent->GetOwnerDocument(); - if (document == nullptr) { - document = dynamic_cast(parent); - } - if (document == nullptr) { - return nullptr; - } - - Rml::ElementPtr child = document->CreateElement(std::string(tag)); - Rml::Element* rawChild = child.get(); - if (!id.empty()) { - rawChild->SetId(std::string(id)); - } - Rml::Element* appended = parent->AppendChild(std::move(child)); - set_props(appended, properties); - return appended; -} - -Rml::Element* append_text( - Rml::Element* parent, std::string_view tag, std::string_view text, std::string_view id) { - Rml::Element* element = append(parent, tag, id); - set_text(element, text); - return element; -} - -void set_text(Rml::Element* element, std::string_view text) { - if (element != nullptr) { - element->SetInnerRML(escape(text)); - } -} - -void set_props(Rml::Element* element, - std::initializer_list > properties) { - if (element == nullptr) { - return; - } - for (const auto& [name, value] : properties) { - element->SetProperty(std::string(name), std::string(value)); - } -} - -void set_props(Rml::Element* element, - std::initializer_list > properties) { - if (element == nullptr) { - return; - } - for (const auto& [name, value] : properties) { - element->SetProperty(name, value); - } -} - -} // namespace dusk::ui diff --git a/src/dusk/ui/element.hpp b/src/dusk/ui/element.hpp deleted file mode 100644 index a5f7586b32..0000000000 --- a/src/dusk/ui/element.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include - -#include -#include - -namespace dusk::ui { - -std::string escape(std::string_view text); -Rml::Element* append(Rml::Element* parent, std::string_view tag, std::string_view id = {}, - std::initializer_list > properties = {}); -Rml::Element* append_text( - Rml::Element* parent, std::string_view tag, std::string_view text, std::string_view id = {}); -void set_text(Rml::Element* element, std::string_view text); -void set_props(Rml::Element* element, - std::initializer_list > properties); -void set_props(Rml::Element* element, - std::initializer_list > properties); - -} // namespace dusk::ui diff --git a/src/dusk/ui/focus_border.cpp b/src/dusk/ui/focus_border.cpp deleted file mode 100644 index 32f872f5d1..0000000000 --- a/src/dusk/ui/focus_border.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "focus_border.hpp" - -#include "element.hpp" -#include "theme.hpp" - -#include - -namespace dusk::ui { - -Rml::Element* add_focus_border(Rml::Element* parent, float radius) { - using namespace theme; - - const auto borderColor = rml_color(PrimaryLight, 0); - return append(parent, "div", {}, - { - {Rml::PropertyId::Position, Rml::Style::Position::Absolute}, - {Rml::PropertyId::PointerEvents, Rml::Style::PointerEvents::None}, - {Rml::PropertyId::Left, rml_dp(-(BorderWidth * 3.0f))}, - {Rml::PropertyId::Top, rml_dp(-(BorderWidth * 3.0f))}, - {Rml::PropertyId::Right, rml_dp(-(BorderWidth * 3.0f))}, - {Rml::PropertyId::Bottom, rml_dp(-(BorderWidth * 3.0f))}, - {Rml::PropertyId::BorderTopWidth, rml_dp(BorderWidth * 2.0f)}, - {Rml::PropertyId::BorderRightWidth, rml_dp(BorderWidth * 2.0f)}, - {Rml::PropertyId::BorderBottomWidth, rml_dp(BorderWidth * 2.0f)}, - {Rml::PropertyId::BorderLeftWidth, rml_dp(BorderWidth * 2.0f)}, - {Rml::PropertyId::BorderTopLeftRadius, rml_dp(radius + BorderWidth * 4.0f)}, - {Rml::PropertyId::BorderTopRightRadius, rml_dp(radius + BorderWidth * 4.0f)}, - {Rml::PropertyId::BorderBottomRightRadius, rml_dp(radius + BorderWidth * 4.0f)}, - {Rml::PropertyId::BorderBottomLeftRadius, rml_dp(radius + BorderWidth * 4.0f)}, - {Rml::PropertyId::BorderTopColor, borderColor}, - {Rml::PropertyId::BorderRightColor, borderColor}, - {Rml::PropertyId::BorderBottomColor, borderColor}, - {Rml::PropertyId::BorderLeftColor, borderColor}, - }); -} - -void set_focus_border_visible(Rml::Element* parent, bool visible) { - if (parent == nullptr || parent->GetNumChildren() == 0) { - return; - } - - const auto borderColor = rml_color(theme::PrimaryLight, visible ? 255 : 0); - set_props(parent->GetChild(0), { - {Rml::PropertyId::BorderTopColor, borderColor}, - {Rml::PropertyId::BorderRightColor, borderColor}, - {Rml::PropertyId::BorderBottomColor, borderColor}, - {Rml::PropertyId::BorderLeftColor, borderColor}, - }); -} - -} // namespace dusk::ui diff --git a/src/dusk/ui/focus_border.hpp b/src/dusk/ui/focus_border.hpp deleted file mode 100644 index 79bc315bbd..0000000000 --- a/src/dusk/ui/focus_border.hpp +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#include - -namespace dusk::ui { - -Rml::Element* add_focus_border(Rml::Element* parent, float radius); -void set_focus_border_visible(Rml::Element* parent, bool visible); - -} // namespace dusk::ui diff --git a/src/dusk/ui/game_menu.cpp b/src/dusk/ui/game_menu.cpp deleted file mode 100644 index a05e20acb5..0000000000 --- a/src/dusk/ui/game_menu.cpp +++ /dev/null @@ -1,808 +0,0 @@ -#include "game_menu.hpp" - -#include "element.hpp" -#include "game_option.hpp" -#include "label.hpp" -#include "theme.hpp" -#include "ui.hpp" -#include "window.hpp" - -#include "dusk/config.hpp" -#include "dusk/imgui/ImGuiEngine.hpp" -#include "dusk/main.h" -#include "dusk/settings.h" -#include "m_Do/m_Do_graphic.h" - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace aurora::gx { -extern bool enableLodBias; -} - -namespace dusk::ui::game_menu { -namespace { - -enum class Tab : int { - Audio = 0, - Cheats, - Gameplay, - Graphics, - Input, - Interface, - Count, -}; - -struct TabDef { - const char* id; - const char* label; -}; - -constexpr std::array(Tab::Count)> kTabs{{ - {"audio", "Audio"}, - {"cheats", "Cheats"}, - {"gameplay", "Gameplay"}, - {"graphics", "Graphics"}, - {"input", "Input"}, - {"interface", "Interface"}, -}}; - -constexpr int kInternalResolutionScaleMax = 12; -constexpr int kShadowResolutionMax = 8; - -constexpr std::array kBloomMultiplierStops{0.0f, 0.25f, 0.50f, 0.75f, 1.00f}; -constexpr std::array kBloomModeNames{"Off", "Classic", "Dusk"}; - -// TODO: Needs more spacing for newlines -static const char* get_description_for_item(std::string_view id) { - if (id == "internal-resolution") { - return "Auto renders at the native window resolution.\nHigher values scale the internal " - "framebuffer."; - } - if (id == "shadow-resolution") { - return "Improves the shadow resolution, making them higher quality."; - } - if (id == "frame-interp") { - return "Uses inter-frame interpolation to enable higher frame rates.\nVisual artifacts, " - "animation glitches, or instability may occur."; - } - - return "No description found."; -} - -struct Row { - std::string id; - std::function activate; - std::function cycle; -}; - -class Screen : public Rml::EventListener { -public: - bool initialize() { - if (m_initialized) { - return true; - } - if (!ui::initialize()) { - return false; - } - m_initialized = true; - return true; - } - - void shutdown() { - close_document(); - if (ui::is_active()) { - ui::set_active(false); - } - m_initialized = false; - m_focusIds.clear(); - m_rows.clear(); - } - - bool is_open() const { return m_open; } - - void set_open(bool open) { - if (open == m_open) { - return; - } - if (open && !initialize()) { - return; - } - m_open = open; - if (open) { - ui::set_active(true); - rebuild(); - } else { - close_document(); - ui::set_active(false); - } - } - - void toggle() { set_open(!m_open); } - - void handle_event(const SDL_Event& event) { - if (!m_open) { - return; - } - ui::handle_event(event); - } - - void update() { - if (!m_open) { - return; - } - - if (m_requestClose) { - m_requestClose = false; - set_open(false); - return; - } - - if (!m_pendingTabId.empty()) { - const std::string tabId = std::move(m_pendingTabId); - m_pendingTabId.clear(); - apply_tab_selection(tabId); - if (!m_open) { - return; - } - } - - if (!m_requestedActivation.empty()) { - const std::string id = std::move(m_requestedActivation); - m_requestedActivation.clear(); - invoke_activate(id); - if (!m_open) { - return; - } - } - - if (m_requestedCycleDirection != 0) { - const std::string id = std::move(m_requestedCycleId); - const int direction = m_requestedCycleDirection; - m_requestedCycleId.clear(); - m_requestedCycleDirection = 0; - invoke_cycle(id, direction); - if (!m_open) { - return; - } - } - - if (m_needsRebuild) { - m_needsRebuild = false; - rebuild(); - } - - ui::update(); - sync_description_pane(); - } - - void ProcessEvent(Rml::Event& event) override { - if (event.GetId() != Rml::EventId::Keydown) { - return; - } - const auto key = static_cast( - event.GetParameter("key_identifier", Rml::Input::KI_UNKNOWN)); - if (handle_key(key)) { - event.StopImmediatePropagation(); - } - } - -private: - bool m_initialized = false; - bool m_open = false; - Tab m_tab = Tab::Graphics; - Rml::ElementDocument* m_document = nullptr; - std::unique_ptr m_window; - std::vector > m_options; - std::vector m_rows; - std::vector m_focusIds; - std::string m_pendingFocusId; - std::string m_requestedActivation; - std::string m_requestedCycleId; - int m_requestedCycleDirection = 0; - bool m_requestClose = false; - bool m_needsRebuild = false; - std::string m_pendingTabId; - Rml::Element* m_descriptionElement = nullptr; - Rml::Element* m_lastDescriptionSyncFocus = nullptr; - - Row* find_row(std::string_view id) { - for (auto& row : m_rows) { - if (row.id == id) { - return &row; - } - } - return nullptr; - } - - void invoke_activate(const std::string& id) { - if (Row* row = find_row(id); row && row->activate) { - row->activate(); - } - } - - void invoke_cycle(const std::string& id, int direction) { - if (Row* row = find_row(id); row && row->cycle) { - row->cycle(direction); - } - } - - void close_document() { - if (m_document == nullptr) { - return; - } - m_document->RemoveEventListener(Rml::EventId::Keydown, this); - m_options.clear(); - m_rows.clear(); - m_focusIds.clear(); - m_descriptionElement = nullptr; - m_lastDescriptionSyncFocus = nullptr; - m_window.reset(); - m_document->Close(); - m_document = nullptr; - } - - void style_document(Rml::ElementDocument* document) { - using namespace theme; - set_props(document, { - {Rml::PropertyId::Width, rml_percent(100.0f)}, - {Rml::PropertyId::Height, rml_percent(100.0f)}, - {Rml::PropertyId::MarginTop, rml_px(0.0f)}, - {Rml::PropertyId::MarginRight, rml_px(0.0f)}, - {Rml::PropertyId::MarginBottom, rml_px(0.0f)}, - {Rml::PropertyId::MarginLeft, rml_px(0.0f)}, - {Rml::PropertyId::PaddingTop, rml_px(0.0f)}, - {Rml::PropertyId::PaddingRight, rml_px(0.0f)}, - {Rml::PropertyId::PaddingBottom, rml_px(0.0f)}, - {Rml::PropertyId::PaddingLeft, rml_px(0.0f)}, - {Rml::PropertyId::FontFamily, rml_string("Inter")}, - {Rml::PropertyId::Color, rml_color(Text)}, - }); - } - - Rml::Element* add_screen() { - using namespace theme; - return append(m_document, "div", "game-menu-screen", - { - {Rml::PropertyId::Display, Rml::Style::Display::Flex}, - {Rml::PropertyId::Position, Rml::Style::Position::Absolute}, - {Rml::PropertyId::Left, rml_px(0.0f)}, - {Rml::PropertyId::Top, rml_px(0.0f)}, - {Rml::PropertyId::Right, rml_px(0.0f)}, - {Rml::PropertyId::Bottom, rml_px(0.0f)}, - {Rml::PropertyId::FlexDirection, Rml::Style::FlexDirection::Column}, - {Rml::PropertyId::AlignItems, Rml::Style::AlignItems::Center}, - {Rml::PropertyId::JustifyContent, Rml::Style::JustifyContent::Center}, - {Rml::PropertyId::BoxSizing, Rml::Style::BoxSizing::BorderBox}, - {Rml::PropertyId::PaddingTop, rml_dp(32.0f)}, - {Rml::PropertyId::PaddingRight, rml_dp(32.0f)}, - {Rml::PropertyId::PaddingBottom, rml_dp(32.0f)}, - {Rml::PropertyId::PaddingLeft, rml_dp(32.0f)}, - }); - } - - Rml::Element* add_section_header(Rml::Element* parent, std::string_view title) { - auto* row = append(parent, "div", {}, - { - {Rml::PropertyId::Display, Rml::Style::Display::Flex}, - {Rml::PropertyId::FlexDirection, Rml::Style::FlexDirection::Row}, - {Rml::PropertyId::AlignItems, Rml::Style::AlignItems::Center}, - {Rml::PropertyId::Width, rml_percent(100.0f)}, - {Rml::PropertyId::PaddingTop, rml_dp(8.0f)}, - {Rml::PropertyId::PaddingBottom, rml_dp(4.0f)}, - }); - auto* label = add_label(row, title, LabelStyle::Annotation); - set_props(label, { - {Rml::PropertyId::FontSize, rml_dp(14.0f)}, - {Rml::PropertyId::LetterSpacing, rml_dp(3.0f)}, - {Rml::PropertyId::Color, rml_color(theme::WindowAccentSoft)}, - {Rml::PropertyId::FlexShrink, rml_number(0.0f)}, - }); - return row; - } - - Rml::Element* add_scroll_body(Rml::Element* parent) { - return append(parent, "div", {}, - { - {Rml::PropertyId::Display, Rml::Style::Display::Flex}, - {Rml::PropertyId::FlexDirection, Rml::Style::FlexDirection::Column}, - {Rml::PropertyId::Width, rml_percent(100.0f)}, - {Rml::PropertyId::FlexGrow, rml_number(1.0f)}, - {Rml::PropertyId::MinHeight, rml_px(0.0f)}, - {Rml::PropertyId::RowGap, rml_dp(8.0f)}, - {Rml::PropertyId::ColumnGap, rml_dp(8.0f)}, - {Rml::PropertyId::OverflowY, Rml::Style::Overflow::Auto}, - }); - } - - std::function queue_activate(std::string id) { - return [this, id = std::move(id)] { m_requestedActivation = id; }; - } - - void register_row(Row row, std::unique_ptr option) { - m_focusIds.push_back(row.id); - m_rows.push_back(std::move(row)); - m_options.push_back(std::move(option)); - } - - void add_toggle(Rml::Element* parent, std::string id, std::string_view title, - config::ConfigVar& var, std::function sideEffect = {}, - std::string_view detail = {}) { - auto mutate = [this, &var, sideEffect = std::move(sideEffect)] { - const bool next = !var.getValue(); - var.setValue(next); - Save(); - if (sideEffect) { - sideEffect(next); - } - m_needsRebuild = true; - }; - const std::string_view valueText = var.getValue() ? "On" : "Off"; - auto option = - std::make_unique(parent, id, title, valueText, detail, queue_activate(id)); - register_row(Row{id, mutate, [mutate](int) { mutate(); }}, std::move(option)); - } - - void add_action(Rml::Element* parent, std::string id, std::string_view title, - std::function action, std::string_view valueText = ">", - std::string_view detail = {}) { - auto mutate = [this, action = std::move(action)] { - action(); - m_needsRebuild = true; - }; - auto option = - std::make_unique(parent, id, title, valueText, detail, queue_activate(id)); - register_row(Row{id, mutate, {}}, std::move(option)); - } - - template - void add_cycle_row(Rml::Element* parent, std::string id, std::string_view title, - std::string_view valueText, std::string_view detail, std::function cycle) { - auto mutate = [this, cycle] { - cycle(1); - m_needsRebuild = true; - }; - auto cycleWithRebuild = [this, cycle](int direction) { - cycle(direction); - m_needsRebuild = true; - }; - auto option = - std::make_unique(parent, id, title, valueText, detail, queue_activate(id)); - register_row(Row{id, mutate, cycleWithRebuild}, std::move(option)); - } - - void add_int_cycle(Rml::Element* parent, std::string id, std::string_view title, - config::ConfigVar& var, int minValue, int maxValue, - std::function formatter, std::function sideEffect = {}) { - const int current = std::clamp(var.getValue(), minValue, maxValue); - const std::string valueText = formatter(current); - auto cycle = [&var, minValue, maxValue, sideEffect = std::move(sideEffect)](int dir) { - int next = std::clamp(var.getValue(), minValue, maxValue) + dir; - if (next < minValue) { - next = maxValue; - } else if (next > maxValue) { - next = minValue; - } - var.setValue(next); - Save(); - if (sideEffect) { - sideEffect(next); - } - }; - add_cycle_row(parent, std::move(id), title, valueText, {}, std::move(cycle)); - } - - void add_bloom_mode_row(Rml::Element* parent) { - auto& var = getSettings().game.bloomMode; - const int current = std::clamp( - static_cast(var.getValue()), 0, static_cast(kBloomModeNames.size() - 1)); - const std::string_view valueText = kBloomModeNames[static_cast(current)]; - auto cycle = [&var](int dir) { - const int count = kBloomModeNames.size(); - int next = static_cast(var.getValue()) + dir; - next = (next % count + count) % count; - var.setValue(static_cast(next)); - Save(); - }; - add_cycle_row(parent, "bloom-mode", "Bloom", valueText, {}, std::move(cycle)); - } - - void add_bloom_brightness_row(Rml::Element* parent) { - auto& var = getSettings().game.bloomMultiplier; - const std::string valueText = fmt::format("{:.2f}", var.getValue()); - auto cycle = [&var](int dir) { - const float currentValue = var.getValue(); - int closest = 0; - float bestDelta = std::abs(currentValue - kBloomMultiplierStops[0]); - for (int i = 1; i < static_cast(kBloomMultiplierStops.size()); ++i) { - const float delta = std::abs(currentValue - kBloomMultiplierStops[i]); - if (delta < bestDelta) { - bestDelta = delta; - closest = i; - } - } - const int count = kBloomMultiplierStops.size(); - const int next = (closest + dir + count) % count; - var.setValue(kBloomMultiplierStops[next]); - Save(); - }; - const std::string_view detail = - getSettings().game.bloomMode.getValue() == BloomMode::Off ? "Bloom is disabled" : ""; - add_cycle_row( - parent, "bloom-brightness", "Bloom Brightness", valueText, detail, std::move(cycle)); - } - - void build_description_pane() { - m_descriptionElement = nullptr; - m_lastDescriptionSyncFocus = nullptr; - Rml::Element* right = m_window->right_pane(); - if (right == nullptr) { - return; - } - m_descriptionElement = append_text(right, "p", " ", "option-description"); - set_props(m_descriptionElement, - { - {Rml::PropertyId::Color, rml_color(theme::TextActive)}, - {Rml::PropertyId::FontSize, rml_dp(20.0f)}, - {Rml::PropertyId::LineHeight, Rml::Property(1.45f, Rml::Unit::EM)}, - {Rml::PropertyId::TextAlign, Rml::Style::TextAlign::Left}, - {Rml::PropertyId::AlignSelf, Rml::Style::AlignSelf::Stretch}, - {Rml::PropertyId::Width, rml_percent(100.0f)}, - {Rml::PropertyId::BoxSizing, Rml::Style::BoxSizing::BorderBox}, - {Rml::PropertyId::WhiteSpace, Rml::Style::WhiteSpace::Preline}, - }); - } - - void sync_description_pane() { - if (m_descriptionElement == nullptr) { - return; - } - if (m_tab != Tab::Graphics) { - return; - } - Rml::Context* context = aurora::rmlui::get_context(); - if (context == nullptr) { - return; - } - Rml::Element* const focused = context->GetFocusElement(); - if (focused == m_lastDescriptionSyncFocus) { - return; - } - m_lastDescriptionSyncFocus = focused; - if (focused == nullptr) { - set_text(m_descriptionElement, get_description_for_item({})); - } else { - set_text(m_descriptionElement, get_description_for_item(focused->GetId())); - } - } - - void build_graphics_tab(Rml::Element* body) { - auto* scroll = add_scroll_body(body); - - add_section_header(scroll, "Display"); - - // TODO: Replace this with a Display Mode toggle. - add_toggle(scroll, "fullscreen", "Toggle Fullscreen", getSettings().video.enableFullscreen, - [](bool enabled) { VISetWindowFullscreen(enabled); }); - - u32 internalWidth = 0; - u32 internalHeight = 0; - AuroraGetRenderSize(&internalWidth, &internalHeight); - const std::string detail = fmt::format("Current: {}x{}", internalWidth, internalHeight); - - const int currentScale = std::clamp( - getSettings().game.internalResolutionScale.getValue(), 0, kInternalResolutionScaleMax); - const std::string scaleValue = - currentScale == 0 ? std::string("Auto") : fmt::format("{}x", currentScale); - - auto scaleCycle = [](int dir) { - int next = std::clamp(getSettings().game.internalResolutionScale.getValue(), 0, - kInternalResolutionScaleMax) + - dir; - if (next < 0) { - next = kInternalResolutionScaleMax; - } else if (next > kInternalResolutionScaleMax) { - next = 0; - } - getSettings().game.internalResolutionScale.setValue(next); - VISetFrameBufferScale(static_cast(next)); - Save(); - }; - - add_cycle_row(scroll, "internal-resolution", "Internal Resolution", scaleValue, detail, - std::move(scaleCycle)); - - add_int_cycle(scroll, "shadow-resolution", "Shadow Resolution", - getSettings().game.shadowResolutionMultiplier, 1, kShadowResolutionMax, - [](int v) { return fmt::format("x{}", v); }); - - add_toggle(scroll, "lock-aspect", "Force 4:3 Aspect Ratio", - getSettings().video.lockAspectRatio, [](bool enabled) { - AuroraSetViewportPolicy(enabled ? AURORA_VIEWPORT_FIT : AURORA_VIEWPORT_STRETCH); - }); - - add_toggle(scroll, "vsync", "VSync", getSettings().video.enableVsync, - [](bool enabled) { aurora_enable_vsync(enabled); }); - - add_toggle(scroll, "frame-interp", "Unlock Framerate", - getSettings().game.enableFrameInterpolation, {}, "Experimental"); - - add_section_header(scroll, "Post-Processing"); - - add_bloom_mode_row(scroll); - if (getSettings().game.bloomMode.getValue() != BloomMode::Off) { - add_bloom_brightness_row(scroll); - } - - add_toggle( - scroll, "depth-of-field", "Depth of Field", getSettings().game.enableDepthOfField); - - add_section_header(scroll, "Developer Options"); - - const std::string lodValue = aurora::gx::enableLodBias ? "On" : "Off"; - auto lodMutate = [this] { - aurora::gx::enableLodBias = !aurora::gx::enableLodBias; - m_needsRebuild = true; - }; - auto lodOption = std::make_unique(scroll, "lod-bias", "LOD Bias", lodValue, - std::string_view{}, queue_activate("lod-bias")); - register_row( - Row{"lod-bias", lodMutate, [lodMutate](int) { lodMutate(); }}, std::move(lodOption)); - - add_toggle( - scroll, "minimap-shadows", "Mini-Map Shadows", getSettings().game.enableMapBackground); - } - - void build_placeholder_tab(Rml::Element* body, std::string_view tabLabel) { - auto* wrap = append(body, "div", {}, - { - {Rml::PropertyId::Display, Rml::Style::Display::Flex}, - {Rml::PropertyId::FlexDirection, Rml::Style::FlexDirection::Column}, - {Rml::PropertyId::AlignItems, Rml::Style::AlignItems::Center}, - {Rml::PropertyId::JustifyContent, Rml::Style::JustifyContent::Center}, - {Rml::PropertyId::Width, rml_percent(100.0f)}, - {Rml::PropertyId::FlexGrow, rml_number(1.0f)}, - {Rml::PropertyId::RowGap, rml_dp(12.0f)}, - {Rml::PropertyId::ColumnGap, rml_dp(12.0f)}, - }); - auto* heading = add_label(wrap, tabLabel, LabelStyle::Large); - set_props(heading, {{Rml::PropertyId::TextAlign, Rml::Style::TextAlign::Center}}); - auto* sub = add_label(wrap, "Not yet ported.", LabelStyle::Body); - set_props(sub, { - {Rml::PropertyId::TextAlign, Rml::Style::TextAlign::Center}, - {Rml::PropertyId::Color, rml_color(theme::TextDim)}, - }); - } - - void build_body() { - m_window->set_right_pane_visible(m_tab == Tab::Graphics); - Rml::Element* body = m_window->body(); - switch (m_tab) { - case Tab::Graphics: - build_graphics_tab(body); - build_description_pane(); - break; - default: - build_placeholder_tab(body, kTabs[static_cast(m_tab)].label); - break; - } - } - - void rebuild() { - if (!m_open) { - return; - } - - Rml::Context* context = aurora::rmlui::get_context(); - if (context == nullptr) { - return; - } - - const std::string preferredFocus = - m_pendingFocusId.empty() ? current_focus_id() : m_pendingFocusId; - m_pendingFocusId.clear(); - - close_document(); - - m_document = context->CreateDocument(); - if (m_document == nullptr) { - return; - } - - style_document(m_document); - Rml::Element* screen = add_screen(); - - m_window = std::make_unique(screen, "game-menu", [this] { request_close(); }); - - for (const TabDef& tab : kTabs) { - const std::string tabId = tab.id; - m_window->add_tab(tabId, tab.label, [this, tabId] { m_pendingTabId = tabId; }); - } - m_window->set_selected_tab(kTabs[static_cast(m_tab)].id); - - build_body(); - - m_document->AddEventListener(Rml::EventId::Keydown, this); - m_document->Show(); - - focus_id(preferredFocus.empty() ? first_focus_id() : preferredFocus); - sync_description_pane(); - } - - void request_close() { m_requestClose = true; } - - void switch_tab(int direction) { - const int count = kTabs.size(); - const int next = (static_cast(m_tab) + direction + count) % count; - m_pendingTabId = kTabs[static_cast(next)].id; - } - - void apply_tab_selection(std::string_view tabId) { - for (size_t i = 0; i < kTabs.size(); ++i) { - if (tabId == kTabs[i].id) { - if (m_tab == static_cast(i)) { - return; - } - m_tab = static_cast(i); - m_pendingFocusId.clear(); - rebuild(); - return; - } - } - } - - std::string current_focus_id() const { - if (Rml::Context* context = aurora::rmlui::get_context()) { - if (Rml::Element* focused = context->GetFocusElement()) { - return focused->GetId(); - } - } - return {}; - } - - std::string first_focus_id() const { - return m_focusIds.empty() ? std::string{} : m_focusIds.front(); - } - - int focus_index() const { - const std::string id = current_focus_id(); - if (id.empty()) { - return -1; - } - for (int i = 0; i < static_cast(m_focusIds.size()); ++i) { - if (m_focusIds[i] == id) { - return i; - } - } - return -1; - } - - void focus_id(std::string_view id) { - if (m_document == nullptr) { - return; - } - if (!id.empty()) { - if (Rml::Element* element = m_document->GetElementById(std::string(id))) { - element->Focus(true); - return; - } - } - const std::string fallback = first_focus_id(); - if (!fallback.empty()) { - if (Rml::Element* element = m_document->GetElementById(fallback)) { - element->Focus(true); - } - } - } - - void move_focus(int direction) { - if (m_focusIds.empty()) { - return; - } - const int index = focus_index(); - if (index < 0) { - focus_id(m_focusIds.front()); - return; - } - const int next = index + direction; - if (next < 0 || next >= static_cast(m_focusIds.size())) { - return; - } - focus_id(m_focusIds[static_cast(next)]); - } - - void queue_activate_focused() { - const std::string id = current_focus_id(); - if (!id.empty()) { - m_requestedActivation = id; - } - } - - void queue_cycle_focused(int direction) { - const std::string id = current_focus_id(); - if (!id.empty()) { - m_requestedCycleId = id; - m_requestedCycleDirection = direction; - } - } - - bool handle_key(Rml::Input::KeyIdentifier key) { - switch (key) { - case Rml::Input::KI_UP: - move_focus(-1); - return true; - case Rml::Input::KI_DOWN: - move_focus(1); - return true; - case Rml::Input::KI_LEFT: - queue_cycle_focused(-1); - return true; - case Rml::Input::KI_RIGHT: - queue_cycle_focused(1); - return true; - case Rml::Input::KI_F16: - switch_tab(-1); - return true; - case Rml::Input::KI_F17: - switch_tab(1); - return true; - case Rml::Input::KI_RETURN: - queue_activate_focused(); - return true; - case Rml::Input::KI_ESCAPE: - case Rml::Input::KI_F15: - request_close(); - return true; - default: - return false; - } - } -}; - -Screen s_screen; - -} // namespace - -bool initialize() { - return s_screen.initialize(); -} - -void shutdown() { - s_screen.shutdown(); -} - -bool is_active() { - return s_screen.is_open(); -} - -void toggle() { - s_screen.toggle(); -} - -void set_active(bool active) { - s_screen.set_open(active); -} - -void handle_event(const SDL_Event& event) { - s_screen.handle_event(event); -} - -void update() { - s_screen.update(); -} - -} // namespace dusk::ui::game_menu diff --git a/src/dusk/ui/game_menu.hpp b/src/dusk/ui/game_menu.hpp deleted file mode 100644 index bff59a0f50..0000000000 --- a/src/dusk/ui/game_menu.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include - -namespace dusk::ui::game_menu { - -bool initialize(); -void shutdown(); - -bool is_active(); -void toggle(); -void set_active(bool active); - -void handle_event(const SDL_Event& event); -void update(); - -} // namespace dusk::ui::game_menu diff --git a/src/dusk/ui/game_option.cpp b/src/dusk/ui/game_option.cpp deleted file mode 100644 index 66540c967c..0000000000 --- a/src/dusk/ui/game_option.cpp +++ /dev/null @@ -1,201 +0,0 @@ -#include "game_option.hpp" - -#include "control_surface.hpp" -#include "element.hpp" -#include "focus_border.hpp" -#include "label.hpp" -#include "theme.hpp" - -#include - -#include - -namespace dusk::ui { - -GameOption::GameOption(Rml::Element* parent, std::string_view id, std::string_view title, - std::string_view value, std::string_view detail, std::function pressedCallback) - : m_pressedCallback(std::move(pressedCallback)) { - using namespace theme; - - m_element = append(parent, "button", id, - { - {Rml::PropertyId::Display, Rml::Style::Display::Flex}, - {Rml::PropertyId::Position, Rml::Style::Position::Relative}, - {Rml::PropertyId::FlexDirection, Rml::Style::FlexDirection::Row}, - {Rml::PropertyId::AlignItems, Rml::Style::AlignItems::Center}, - {Rml::PropertyId::JustifyContent, Rml::Style::JustifyContent::SpaceBetween}, - {Rml::PropertyId::BoxSizing, Rml::Style::BoxSizing::BorderBox}, - {Rml::PropertyId::RowGap, rml_dp(16.0f)}, - {Rml::PropertyId::ColumnGap, rml_dp(16.0f)}, - {Rml::PropertyId::Width, rml_percent(100.0f)}, - {Rml::PropertyId::PaddingTop, rml_dp(16.0f)}, - {Rml::PropertyId::PaddingRight, rml_dp(16.0f)}, - {Rml::PropertyId::PaddingBottom, rml_dp(16.0f)}, - {Rml::PropertyId::PaddingLeft, rml_dp(16.0f)}, - {Rml::PropertyId::BorderTopWidth, rml_dp(BorderWidth)}, - {Rml::PropertyId::BorderRightWidth, rml_dp(BorderWidth)}, - {Rml::PropertyId::BorderBottomWidth, rml_dp(BorderWidth)}, - {Rml::PropertyId::BorderLeftWidth, rml_dp(BorderWidth)}, - {Rml::PropertyId::BorderTopLeftRadius, rml_dp(BorderRadiusSmall)}, - {Rml::PropertyId::BorderTopRightRadius, rml_dp(BorderRadiusSmall)}, - {Rml::PropertyId::BorderBottomRightRadius, rml_dp(BorderRadiusSmall)}, - {Rml::PropertyId::BorderBottomLeftRadius, rml_dp(BorderRadiusSmall)}, - {Rml::PropertyId::BackgroundColor, rml_color(Transparent)}, - {Rml::PropertyId::BorderTopColor, rml_color(ElevatedBorder, 0)}, - {Rml::PropertyId::BorderRightColor, rml_color(ElevatedBorder, 0)}, - {Rml::PropertyId::BorderBottomColor, rml_color(ElevatedBorder, 0)}, - {Rml::PropertyId::BorderLeftColor, rml_color(ElevatedBorder, 0)}, - {Rml::PropertyId::Color, rml_color(TextDim)}, - {Rml::PropertyId::Cursor, rml_string("pointer")}, - {Rml::PropertyId::TabIndex, Rml::Style::TabIndex::Auto}, - {Rml::PropertyId::NavUp, Rml::Style::Nav::Auto}, - {Rml::PropertyId::NavDown, Rml::Style::Nav::Auto}, - {Rml::PropertyId::NavLeft, Rml::Style::Nav::Auto}, - {Rml::PropertyId::NavRight, Rml::Style::Nav::Auto}, - {Rml::PropertyId::Opacity, rml_number(1.0f)}, - {Rml::PropertyId::FontFamily, rml_string("Inter")}, - }); - - add_focus_border(m_element, BorderRadiusSmall); - - auto* left = append(m_element, "div", {}, - { - {Rml::PropertyId::Display, Rml::Style::Display::Flex}, - {Rml::PropertyId::FlexDirection, Rml::Style::FlexDirection::Column}, - {Rml::PropertyId::RowGap, rml_dp(4.0f)}, - {Rml::PropertyId::ColumnGap, rml_dp(4.0f)}, - {Rml::PropertyId::MinWidth, rml_px(0.0f)}, - {Rml::PropertyId::Width, rml_px(0.0f)}, - {Rml::PropertyId::FlexGrow, rml_number(1.0f)}, - {Rml::PropertyId::FlexShrink, rml_number(1.0f)}, - {Rml::PropertyId::PointerEvents, Rml::Style::PointerEvents::None}, - }); - - m_title = add_label(left, title, LabelStyle::Large); - set_props(m_title, { - {Rml::PropertyId::Color, rml_color(TextDim)}, - {Rml::PropertyId::FontSize, rml_dp(28.0f)}, - {Rml::PropertyId::LetterSpacing, rml_dp(1.0f)}, - }); - - if (!value.empty() || !detail.empty()) { - auto* right = append(m_element, "div", {}, - { - {Rml::PropertyId::Display, Rml::Style::Display::Flex}, - {Rml::PropertyId::FlexDirection, Rml::Style::FlexDirection::Column}, - {Rml::PropertyId::AlignItems, Rml::Style::AlignItems::FlexEnd}, - {Rml::PropertyId::JustifyContent, Rml::Style::JustifyContent::Center}, - {Rml::PropertyId::RowGap, rml_dp(4.0f)}, - {Rml::PropertyId::ColumnGap, rml_dp(4.0f)}, - {Rml::PropertyId::MinWidth, rml_dp(170.0f)}, - {Rml::PropertyId::MaxWidth, rml_percent(48.0f)}, - {Rml::PropertyId::FlexShrink, rml_number(0.0f)}, - {Rml::PropertyId::PointerEvents, Rml::Style::PointerEvents::None}, - }); - - if (!value.empty()) { - m_value = add_label(right, value, LabelStyle::Body); - set_props( - m_value, { - {Rml::PropertyId::Color, rml_color(TextDim)}, - {Rml::PropertyId::TextAlign, Rml::Style::TextAlign::Right}, - {Rml::PropertyId::OverflowX, Rml::Style::Overflow::Hidden}, - {Rml::PropertyId::OverflowY, Rml::Style::Overflow::Hidden}, - {Rml::PropertyId::TextOverflow, Rml::Style::TextOverflow::Ellipsis}, - {Rml::PropertyId::WhiteSpace, Rml::Style::WhiteSpace::Nowrap}, - }); - } - - if (!detail.empty()) { - m_detail = add_label(right, detail, LabelStyle::Annotation); - set_props(m_detail, { - {Rml::PropertyId::Color, rml_color(TextDim)}, - {Rml::PropertyId::TextAlign, Rml::Style::TextAlign::Right}, - }); - } - } - - m_element->AddEventListener(Rml::EventId::Click, this); - m_element->AddEventListener(Rml::EventId::Focus, this); - m_element->AddEventListener(Rml::EventId::Blur, this); - m_element->AddEventListener(Rml::EventId::Mouseover, this); - m_element->AddEventListener(Rml::EventId::Mouseout, this); - apply_style(); -} - -GameOption::~GameOption() { - if (m_element == nullptr) { - return; - } - - m_element->RemoveEventListener(Rml::EventId::Click, this); - m_element->RemoveEventListener(Rml::EventId::Focus, this); - m_element->RemoveEventListener(Rml::EventId::Blur, this); - m_element->RemoveEventListener(Rml::EventId::Mouseover, this); - m_element->RemoveEventListener(Rml::EventId::Mouseout, this); - m_element = nullptr; -} - -void GameOption::ProcessEvent(Rml::Event& event) { - switch (event.GetId()) { - case Rml::EventId::Click: - if (m_pressedCallback) { - m_pressedCallback(); - } - break; - case Rml::EventId::Focus: - m_focused = true; - apply_style(); - break; - case Rml::EventId::Blur: - m_focused = false; - apply_style(); - break; - case Rml::EventId::Mouseover: - m_hovered = true; - apply_style(); - break; - case Rml::EventId::Mouseout: - m_hovered = false; - apply_style(); - break; - default: - break; - } -} - -Rml::Element* GameOption::element() const { - return m_element; -} - -std::string GameOption::id() const { - return m_element == nullptr ? std::string{} : m_element->GetId(); -} - -void GameOption::set_value(std::string_view value) { - set_text(m_value, value); -} - -void GameOption::apply_style() { - if (m_element == nullptr) { - return; - } - - const bool active = m_hovered || m_focused; - apply_control_surface_style( - m_element, control_surface_style(ControlSurfaceTone::Quiet), active); - m_element->SetProperty( - Rml::PropertyId::Color, rml_color(active ? theme::TextActive : theme::TextDim)); - m_title->SetProperty( - Rml::PropertyId::Color, rml_color(active ? theme::TextActive : theme::TextDim)); - if (m_value != nullptr) { - m_value->SetProperty( - Rml::PropertyId::Color, rml_color(active ? theme::TextActive : theme::TextDim)); - } - if (m_detail != nullptr) { - m_detail->SetProperty(Rml::PropertyId::Color, rml_color(theme::TextDim)); - } - set_focus_border_visible(m_element, m_focused); -} - -} // namespace dusk::ui diff --git a/src/dusk/ui/game_option.hpp b/src/dusk/ui/game_option.hpp deleted file mode 100644 index c66e3ddd2a..0000000000 --- a/src/dusk/ui/game_option.hpp +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include - -#include -#include -#include - -namespace Rml { -class Element; -} - -namespace dusk::ui { - -class GameOption : public Rml::EventListener { -public: - GameOption(Rml::Element* parent, std::string_view id, std::string_view title, - std::string_view value, std::string_view detail, std::function pressedCallback); - ~GameOption() override; - - GameOption(const GameOption&) = delete; - GameOption& operator=(const GameOption&) = delete; - - void ProcessEvent(Rml::Event& event) override; - - Rml::Element* element() const; - std::string id() const; - void set_value(std::string_view value); - -private: - Rml::Element* m_element = nullptr; - Rml::Element* m_title = nullptr; - Rml::Element* m_value = nullptr; - Rml::Element* m_detail = nullptr; - std::function m_pressedCallback; - bool m_hovered = false; - bool m_focused = false; - - void apply_style(); -}; - -} // namespace dusk::ui diff --git a/src/dusk/ui/label.cpp b/src/dusk/ui/label.cpp deleted file mode 100644 index 1077a9fcba..0000000000 --- a/src/dusk/ui/label.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include "label.hpp" - -#include "element.hpp" -#include "theme.hpp" - -namespace dusk::ui { - -void apply_label_style(Rml::Element* element, LabelStyle style) { - using namespace theme; - - switch (style) { - case LabelStyle::Annotation: - set_props(element, { - {Rml::PropertyId::FontSize, rml_dp(18.0f)}, - {Rml::PropertyId::LetterSpacing, rml_dp(2.0f)}, - {Rml::PropertyId::FontWeight, Rml::Style::FontWeight::Normal}, - {Rml::PropertyId::Color, rml_color(TextDim)}, - }); - break; - case LabelStyle::Body: - set_props(element, { - {Rml::PropertyId::FontSize, rml_dp(20.0f)}, - {Rml::PropertyId::LetterSpacing, rml_px(0.0f)}, - {Rml::PropertyId::FontWeight, Rml::Style::FontWeight::Normal}, - {Rml::PropertyId::Color, rml_color(Text)}, - }); - break; - case LabelStyle::Medium: - set_props(element, { - {Rml::PropertyId::FontSize, rml_dp(28.0f)}, - {Rml::PropertyId::LetterSpacing, rml_dp(3.0f)}, - {Rml::PropertyId::FontWeight, Rml::Style::FontWeight::Bold}, - {Rml::PropertyId::Color, rml_color(Text)}, - }); - break; - case LabelStyle::Large: - set_props(element, { - {Rml::PropertyId::FontSize, rml_dp(36.0f)}, - {Rml::PropertyId::LetterSpacing, rml_dp(4.0f)}, - {Rml::PropertyId::FontWeight, Rml::Style::FontWeight::Bold}, - {Rml::PropertyId::Color, rml_color(Text)}, - }); - break; - } -} - -Rml::Element* add_label(Rml::Element* parent, std::string_view text, LabelStyle style) { - Rml::Element* label = append_text(parent, "div", text); - apply_label_style(label, style); - return label; -} - -} // namespace dusk::ui diff --git a/src/dusk/ui/label.hpp b/src/dusk/ui/label.hpp deleted file mode 100644 index fb23ad408f..0000000000 --- a/src/dusk/ui/label.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include - -#include - -namespace dusk::ui { - -enum class LabelStyle { - Annotation, - Body, - Medium, - Large, -}; - -Rml::Element* add_label(Rml::Element* parent, std::string_view text, LabelStyle style); -void apply_label_style(Rml::Element* element, LabelStyle style); - -} // namespace dusk::ui diff --git a/src/dusk/ui/prelaunch_layout.cpp b/src/dusk/ui/prelaunch_layout.cpp deleted file mode 100644 index a1916cc89e..0000000000 --- a/src/dusk/ui/prelaunch_layout.cpp +++ /dev/null @@ -1,138 +0,0 @@ -#include "prelaunch_layout.hpp" - -#include "element.hpp" -#include "label.hpp" -#include "theme.hpp" - -#include - -namespace dusk::ui::prelaunch::layout { - -void style_document(Rml::ElementDocument* document) { - using namespace theme; - - set_props(document, { - {Rml::PropertyId::Width, rml_percent(100.0f)}, - {Rml::PropertyId::Height, rml_percent(100.0f)}, - {Rml::PropertyId::MarginTop, rml_px(0.0f)}, - {Rml::PropertyId::MarginRight, rml_px(0.0f)}, - {Rml::PropertyId::MarginBottom, rml_px(0.0f)}, - {Rml::PropertyId::MarginLeft, rml_px(0.0f)}, - {Rml::PropertyId::PaddingTop, rml_px(0.0f)}, - {Rml::PropertyId::PaddingRight, rml_px(0.0f)}, - {Rml::PropertyId::PaddingBottom, rml_px(0.0f)}, - {Rml::PropertyId::PaddingLeft, rml_px(0.0f)}, - {Rml::PropertyId::FontFamily, rml_string("Inter")}, - {Rml::PropertyId::BackgroundColor, rml_color(Background1)}, - {Rml::PropertyId::Color, rml_color(Text)}, - }); -} - -Rml::Element* add_screen(Rml::ElementDocument* document, ScreenLayout layout) { - using namespace theme; - - const bool compact = layout == ScreenLayout::CompactSplit; - return append(document, "div", "prelaunch-screen", - { - {Rml::PropertyId::Display, Rml::Style::Display::Flex}, - {Rml::PropertyId::Position, Rml::Style::Position::Absolute}, - {Rml::PropertyId::Left, rml_px(0.0f)}, - {Rml::PropertyId::Top, rml_px(0.0f)}, - {Rml::PropertyId::Right, rml_px(0.0f)}, - {Rml::PropertyId::Bottom, rml_px(0.0f)}, - {Rml::PropertyId::FlexDirection, - compact ? Rml::Style::FlexDirection::Row : Rml::Style::FlexDirection::Column}, - {Rml::PropertyId::AlignItems, Rml::Style::AlignItems::Center}, - {Rml::PropertyId::JustifyContent, Rml::Style::JustifyContent::Center}, - {Rml::PropertyId::RowGap, rml_dp(compact ? 28.0f : 24.0f)}, - {Rml::PropertyId::ColumnGap, rml_dp(compact ? 28.0f : 24.0f)}, - {Rml::PropertyId::BoxSizing, Rml::Style::BoxSizing::BorderBox}, - {Rml::PropertyId::PaddingTop, rml_dp(compact ? 24.0f : 48.0f)}, - {Rml::PropertyId::PaddingRight, rml_dp(compact ? 24.0f : 28.0f)}, - {Rml::PropertyId::PaddingBottom, rml_dp(compact ? 24.0f : 48.0f)}, - {Rml::PropertyId::PaddingLeft, rml_dp(compact ? 24.0f : 28.0f)}, - {Rml::PropertyId::BackgroundColor, rml_color(Background1)}, - }); -} - -Rml::Element* add_brand(Rml::Element* parent, std::string_view logoPath, bool compact) { - auto* brand = append(parent, "div", {}, - { - {Rml::PropertyId::Display, Rml::Style::Display::Flex}, - {Rml::PropertyId::FlexDirection, Rml::Style::FlexDirection::Column}, - {Rml::PropertyId::AlignItems, Rml::Style::AlignItems::Center}, - {Rml::PropertyId::JustifyContent, Rml::Style::JustifyContent::Center}, - {Rml::PropertyId::RowGap, rml_dp(compact ? 8.0f : 12.0f)}, - {Rml::PropertyId::ColumnGap, rml_dp(compact ? 8.0f : 12.0f)}, - {Rml::PropertyId::Width, compact ? rml_dp(260.0f) : rml_percent(100.0f)}, - {Rml::PropertyId::MaxWidth, compact ? rml_percent(32.0f) : rml_dp(720.0f)}, - {Rml::PropertyId::FlexShrink, rml_number(compact ? 0.0f : 1.0f)}, - }); - - auto* subtitle = add_label(brand, "Twilit Realm presents", LabelStyle::Annotation); - set_props(subtitle, { - {Rml::PropertyId::TextAlign, Rml::Style::TextAlign::Center}, - {Rml::PropertyId::FontSize, rml_dp(compact ? 14.0f : 18.0f)}, - }); - - if (!logoPath.empty()) { - auto* logo = append(brand, "img"); - logo->SetAttribute("src", std::string(logoPath)); - set_props(logo, { - {Rml::PropertyId::Width, rml_dp(compact ? 220.0f : 360.0f)}, - {Rml::PropertyId::MaxWidth, rml_percent(compact ? 100.0f : 70.0f)}, - {Rml::PropertyId::Height, Rml::Style::Height::Auto}, - }); - } else { - auto* title = add_label(brand, "Dusk", LabelStyle::Large); - set_props(title, { - {Rml::PropertyId::FontSize, rml_dp(compact ? 42.0f : 54.0f)}, - {Rml::PropertyId::LetterSpacing, rml_dp(compact ? 3.0f : 4.0f)}, - }); - } - return brand; -} - -Rml::Element* add_heading(Rml::Element* parent, std::string_view title) { - auto* heading = add_label(parent, title, LabelStyle::Large); - set_props(heading, { - {Rml::PropertyId::Width, rml_percent(100.0f)}, - {Rml::PropertyId::MaxWidth, rml_dp(840.0f)}, - {Rml::PropertyId::TextAlign, Rml::Style::TextAlign::Left}, - }); - return heading; -} - -Rml::Element* add_panel(Rml::Element* parent, bool wide, bool compact) { - using namespace theme; - - return append(parent, "div", {}, - { - {Rml::PropertyId::Display, Rml::Style::Display::Flex}, - {Rml::PropertyId::FlexDirection, Rml::Style::FlexDirection::Column}, - {Rml::PropertyId::RowGap, rml_dp(12.0f)}, - {Rml::PropertyId::ColumnGap, rml_dp(12.0f)}, - {Rml::PropertyId::Width, rml_dp(wide ? 840.0f : 520.0f)}, - {Rml::PropertyId::MaxWidth, rml_percent(compact ? 62.0f : 100.0f)}, - {Rml::PropertyId::BoxSizing, Rml::Style::BoxSizing::BorderBox}, - {Rml::PropertyId::PaddingTop, rml_dp(compact ? 16.0f : 20.0f)}, - {Rml::PropertyId::PaddingRight, rml_dp(compact ? 16.0f : 20.0f)}, - {Rml::PropertyId::PaddingBottom, rml_dp(compact ? 16.0f : 20.0f)}, - {Rml::PropertyId::PaddingLeft, rml_dp(compact ? 16.0f : 20.0f)}, - {Rml::PropertyId::BorderTopWidth, rml_dp(BorderWidth)}, - {Rml::PropertyId::BorderRightWidth, rml_dp(BorderWidth)}, - {Rml::PropertyId::BorderBottomWidth, rml_dp(BorderWidth)}, - {Rml::PropertyId::BorderLeftWidth, rml_dp(BorderWidth)}, - {Rml::PropertyId::BorderTopLeftRadius, rml_dp(BorderRadiusMedium)}, - {Rml::PropertyId::BorderTopRightRadius, rml_dp(BorderRadiusMedium)}, - {Rml::PropertyId::BorderBottomRightRadius, rml_dp(BorderRadiusMedium)}, - {Rml::PropertyId::BorderBottomLeftRadius, rml_dp(BorderRadiusMedium)}, - {Rml::PropertyId::BorderTopColor, rml_color(ElevatedBorder)}, - {Rml::PropertyId::BorderRightColor, rml_color(ElevatedBorder)}, - {Rml::PropertyId::BorderBottomColor, rml_color(ElevatedBorder)}, - {Rml::PropertyId::BorderLeftColor, rml_color(ElevatedBorder)}, - {Rml::PropertyId::BackgroundColor, rml_color(ElevatedSoft)}, - }); -} - -} // namespace dusk::ui::prelaunch::layout diff --git a/src/dusk/ui/prelaunch_layout.hpp b/src/dusk/ui/prelaunch_layout.hpp deleted file mode 100644 index 11efea9b4f..0000000000 --- a/src/dusk/ui/prelaunch_layout.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include -#include - -#include - -namespace dusk::ui::prelaunch::layout { - -enum class ScreenLayout { - Standard, - CompactSplit, -}; - -void style_document(Rml::ElementDocument* document); -Rml::Element* add_screen( - Rml::ElementDocument* document, ScreenLayout layout = ScreenLayout::Standard); -Rml::Element* add_brand(Rml::Element* parent, std::string_view logoPath, bool compact = false); -Rml::Element* add_heading(Rml::Element* parent, std::string_view title); -Rml::Element* add_panel(Rml::Element* parent, bool wide, bool compact = false); - -} // namespace dusk::ui::prelaunch::layout diff --git a/src/dusk/ui/prelaunch_screen.cpp b/src/dusk/ui/prelaunch_screen.cpp deleted file mode 100644 index 45525c6af7..0000000000 --- a/src/dusk/ui/prelaunch_screen.cpp +++ /dev/null @@ -1,933 +0,0 @@ -#include "prelaunch_screen.hpp" - -#include "button.hpp" -#include "disc_state.hpp" -#include "game_option.hpp" -#include "prelaunch_layout.hpp" -#include "ui.hpp" - -#include "../file_select.hpp" -#include "../iso_validate.hpp" -#include "dusk/config.hpp" -#include "dusk/main.h" -#include "dusk/settings.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "aurora/lib/window.hpp" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace dusk::ui::prelaunch { -namespace { - -enum class View { - Main, - Options, - LanguageSelect, - GraphicsBackendSelect, - SaveFileTypeSelect, -}; - -struct BackendChoice { - AuroraBackend backend = BACKEND_AUTO; - std::string id; - std::string name; -}; - -constexpr std::array kLanguageNames = { - "English", - "German", - "French", - "Spanish", - "Italian", -}; - -constexpr std::array kGameDiscFileFilters{{ - {"Game Disc Images", "iso;gcm;ciso;gcz;nfs;rvz;wbfs;wia;tgc"}, - {"All Files", "*"}, -}}; - -std::string iso_validation_error_message(iso::ValidationError code) { - switch (code) { - case iso::ValidationError::IOError: - return "Unable to read selected disc image"; - case iso::ValidationError::InvalidImage: - return "Unable to interpret selected file as a disc image"; - case iso::ValidationError::WrongGame: - return "Disc is for a different game"; - case iso::ValidationError::WrongVersion: - return "Disc is for an unsupported version. Only NTSC & PAL GameCube are " - "supported at this time"; - case iso::ValidationError::ExecutableMismatch: - return "Disc contains modified executable files"; - case iso::ValidationError::Success: - return {}; - case iso::ValidationError::Unknown: - default: - return "Unknown disc validation error"; - } -} - -std::string_view backend_name(AuroraBackend backend) { - switch (backend) { - default: - return "Auto"; - case BACKEND_D3D12: - return "D3D12"; - case BACKEND_D3D11: - return "D3D11"; - case BACKEND_METAL: - return "Metal"; - case BACKEND_VULKAN: - return "Vulkan"; - case BACKEND_OPENGL: - return "OpenGL"; - case BACKEND_OPENGLES: - return "OpenGL ES"; - case BACKEND_WEBGPU: - return "WebGPU"; - case BACKEND_NULL: - return "Null"; - } -} - -std::string_view backend_id(AuroraBackend backend) { - switch (backend) { - default: - return "auto"; - case BACKEND_D3D12: - return "d3d12"; - case BACKEND_D3D11: - return "d3d11"; - case BACKEND_METAL: - return "metal"; - case BACKEND_VULKAN: - return "vulkan"; - case BACKEND_OPENGL: - return "opengl"; - case BACKEND_OPENGLES: - return "opengles"; - case BACKEND_WEBGPU: - return "webgpu"; - case BACKEND_NULL: - return "null"; - } -} - -bool try_parse_backend(std::string_view backend, AuroraBackend& outBackend) { - if (backend == "auto") { - outBackend = BACKEND_AUTO; - return true; - } - if (backend == "d3d11") { - outBackend = BACKEND_D3D11; - return true; - } - if (backend == "d3d12") { - outBackend = BACKEND_D3D12; - return true; - } - if (backend == "metal") { - outBackend = BACKEND_METAL; - return true; - } - if (backend == "vulkan") { - outBackend = BACKEND_VULKAN; - return true; - } - if (backend == "opengl") { - outBackend = BACKEND_OPENGL; - return true; - } - if (backend == "opengles") { - outBackend = BACKEND_OPENGLES; - return true; - } - if (backend == "webgpu") { - outBackend = BACKEND_WEBGPU; - return true; - } - if (backend == "null") { - outBackend = BACKEND_NULL; - return true; - } - return false; -} - -std::string_view card_type_name(CARDFileType type) { - switch (type) { - case CARD_GCIFOLDER: - return "GCI Folder"; - case CARD_RAWIMAGE: - return "Card Image"; - default: - return ""; - } -} - -std::filesystem::path resource_path(const char* filename) { - const char* basePath = SDL_GetBasePath(); - if (basePath == nullptr) { - return std::filesystem::path("res") / filename; - } - return std::filesystem::path(basePath) / "res" / filename; -} - -std::string display_path(std::string_view path) { - const char* home = SDL_GetUserFolder(SDL_FOLDER_HOME); - if (home == nullptr || home[0] == '\0') { - home = std::getenv("HOME"); - } - if (home == nullptr || home[0] == '\0') { - return std::string(path); - } - - std::string homePath(home); - while (homePath.size() > 1 && homePath.back() == '/') { - homePath.pop_back(); - } - - if (path == homePath) { - return "~"; - } - - if (path.size() > homePath.size() && path.substr(0, homePath.size()) == homePath && - path[homePath.size()] == '/') - { - return "~" + std::string(path.substr(homePath.size())); - } - - return std::string(path); -} - -std::vector backend_choices() { - std::vector choices; - choices.push_back({BACKEND_AUTO, std::string(backend_id(BACKEND_AUTO)), - std::string(backend_name(BACKEND_AUTO))}); - - size_t backendCount = 0; - const AuroraBackend* availableBackends = aurora_get_available_backends(&backendCount); - for (size_t i = 0; i < backendCount; ++i) { - const AuroraBackend backend = availableBackends[i]; - choices.push_back( - {backend, std::string(backend_id(backend)), std::string(backend_name(backend))}); - } - - return choices; -} - -class Screen : public Rml::EventListener { -public: - bool initialize() { - if (m_initialized) { - return true; - } - if (!ui::initialize()) { - return false; - } - - m_selectedIsoPath = getSettings().backend.isoPath.getValue(); - validate_selected_iso(false); - m_initialGraphicsBackend = getSettings().backend.graphicsBackend.getValue(); - m_initialized = true; - - if (is_selected_path_valid() && getSettings().backend.skipPreLaunchUI.getValue()) { - IsGameLaunched = true; - return true; - } - - set_active(true); - rebuild(); - if (m_document == nullptr) { - shutdown(); - return false; - } - return true; - } - - void shutdown() { - close_document(); - set_active(false); - m_initialized = false; - m_focusIds.clear(); - } - - bool is_active() const { return m_initialized && ui::is_active(); } - - void handle_event(const SDL_Event& event) { ui::handle_event(event); } - - void update() { - if (m_requestedBack) { - m_requestedBack = false; - back(); - if (!is_active()) { - return; - } - } - - if (!m_requestedActivation.empty()) { - const std::string requestedActivation = m_requestedActivation; - m_requestedActivation.clear(); - activate(requestedActivation); - if (!is_active()) { - return; - } - } - - if (m_requestedCycleDirection != 0) { - const std::string requestedCycleId = m_requestedCycleId; - const int requestedCycleDirection = m_requestedCycleDirection; - m_requestedCycleId.clear(); - m_requestedCycleDirection = 0; - cycle_option(requestedCycleId, requestedCycleDirection); - if (!is_active()) { - return; - } - } - - if (is_selected_path_valid() && getSettings().backend.skipPreLaunchUI.getValue()) { - IsGameLaunched = true; - shutdown(); - return; - } - - ui::update(); - rebuild_if_layout_changed(); - } - - void ProcessEvent(Rml::Event& event) override { - if (event.GetId() == Rml::EventId::Keydown) { - const auto key = static_cast( - event.GetParameter("key_identifier", Rml::Input::KI_UNKNOWN)); - if (handle_key(key)) { - event.StopImmediatePropagation(); - } - } - } - -private: - bool m_initialized = false; - View m_view = View::Main; - Rml::ElementDocument* m_document = nullptr; - std::vector m_focusIds; - std::vector > m_buttons; - std::unique_ptr m_discState; - std::vector > m_options; - std::string m_pendingFocusId; - std::string m_requestedActivation; - std::string m_requestedCycleId; - int m_requestedCycleDirection = 0; - bool m_requestedBack = false; - std::string m_selectedIsoPath; - std::string m_errorString; - std::string m_initialGraphicsBackend; - bool m_compactLayout = false; - bool m_selectedIsoValid = false; - bool m_isPal = false; - - bool selected_path_exists() const { -#if TARGET_ANDROID - return !m_selectedIsoPath.empty(); -#else - return !m_selectedIsoPath.empty() && SDL_GetPathInfo(m_selectedIsoPath.c_str(), nullptr); -#endif - } - - bool is_selected_path_valid() const { return m_selectedIsoValid; } - - void validate_selected_iso(bool save_valid_path) { - m_errorString.clear(); - m_selectedIsoValid = false; - m_isPal = false; - - if (m_selectedIsoPath.empty()) { - return; - } - - if (!selected_path_exists()) { - m_errorString = "Selected disc image does not exist"; - return; - } - - const iso::ValidationError validationResult = iso::validate(m_selectedIsoPath.c_str()); - if (validationResult != iso::ValidationError::Success) { - m_errorString = iso_validation_error_message(validationResult); - return; - } - - m_selectedIsoValid = true; - m_isPal = iso::isPal(m_selectedIsoPath.c_str()); - if (save_valid_path) { - getSettings().backend.isoPath.setValue(m_selectedIsoPath); - Save(); - } - } - - void close_document() { - if (m_document == nullptr) { - return; - } - - m_document->RemoveEventListener(Rml::EventId::Keydown, this); - m_buttons.clear(); - m_discState.reset(); - m_options.clear(); - m_focusIds.clear(); - m_document->Close(); - m_document = nullptr; - } - - std::string logo_path() const { - const auto logo_path = resource_path("logo-mascot.png"); - if (std::filesystem::exists(logo_path)) { - return logo_path.string(); - } - - return {}; - } - - std::string selected_disc_text() const { - if (!m_selectedIsoPath.empty()) { - return display_path(m_selectedIsoPath); - } - - return "Select a disc..."; - } - - std::string disc_status_text() const { - if (!m_errorString.empty()) { - return m_errorString; - } - if (is_selected_path_valid()) { - return fmt::format("Disc region: {}", m_isPal ? "PAL" : "NTSC"); - } - return {}; - } - - bool should_use_compact_layout() const { - Rml::Context* context = aurora::rmlui::get_context(); - if (context == nullptr) { - return false; - } - - const Rml::Vector2i dimensions = context->GetDimensions(); - float dp_ratio = context->GetDensityIndependentPixelRatio(); - if (dp_ratio <= 0.0f) { - dp_ratio = 1.0f; - } - - const float width = static_cast(dimensions.x) / dp_ratio; - const float height = static_cast(dimensions.y) / dp_ratio; - return height < 680.0f && width >= 720.0f; - } - - void rebuild_if_layout_changed() { - const bool compactLayout = should_use_compact_layout(); - if (compactLayout == m_compactLayout) { - return; - } - - if (Rml::Context* context = aurora::rmlui::get_context()) { - if (Rml::Element* focused = context->GetFocusElement()) { - m_pendingFocusId = focused->GetId(); - } - } - rebuild(); - } - - void queue_activation(std::string id) { m_requestedActivation = std::move(id); } - - void queue_back() { m_requestedBack = true; } - - void queue_cycle(std::string id, int direction) { - m_requestedCycleId = std::move(id); - m_requestedCycleDirection = direction; - } - - void add_button_control( - Rml::Element* parent, std::string_view id, std::string_view text, ButtonVariant variant) { - const std::string idString(id); - m_focusIds.push_back(idString); - m_buttons.push_back(std::make_unique + +
+ + + +)RML"; + +bool setup_window_model(Rml::Context* context, WindowModel& model, Rml::DataModelHandle& handle) { + Rml::DataModelConstructor constructor = context->CreateDataModel("window"); + if (!constructor) { + return false; } - const bool active = m_hovered || m_focused; - - int textOpacity; - if (m_selected) { - textOpacity = 255; - } else if (active) { - textOpacity = 200; + if (auto tab_handle = constructor.RegisterStruct()) { + tab_handle.RegisterMember("label", &WindowTab::label); } else { - textOpacity = 110; - } - const Color textColor = m_selected ? TextActive : Text; - m_label->SetProperty(Rml::PropertyId::Color, rml_color(textColor, textOpacity)); - - if (m_indicator != nullptr) { - const int indicatorOpacity = m_selected ? 255 : (active ? 96 : 0); - m_indicator->SetProperty( - Rml::PropertyId::BackgroundColor, rml_color(WindowAccent, indicatorOpacity)); + return false; } - set_focus_border_visible(m_element, m_focused); + if (!constructor.RegisterArray >()) { + return false; + } + + constructor.Bind("active_tab", &model.activeTab); + constructor.Bind("tabs", &model.tabs); + constructor.BindEventCallback("set_active_tab", &WindowModel::set_active_tab, &model); + + handle = constructor.GetModelHandle(); + return true; } -Window::Window(Rml::Element* parent, std::string_view id, std::function closeCallback) - : m_closeCallback(std::move(closeCallback)) { - using namespace theme; +} // namespace - m_element = append(parent, "div", id, - { - {Rml::PropertyId::Display, Rml::Style::Display::Flex}, - {Rml::PropertyId::FlexDirection, Rml::Style::FlexDirection::Column}, - {Rml::PropertyId::BoxSizing, Rml::Style::BoxSizing::BorderBox}, - {Rml::PropertyId::Width, rml_percent(100.0f)}, - {Rml::PropertyId::MaxWidth, rml_dp(1088.0f)}, - {Rml::PropertyId::BorderTopWidth, rml_dp(BorderWidth)}, - {Rml::PropertyId::BorderRightWidth, rml_dp(BorderWidth)}, - {Rml::PropertyId::BorderBottomWidth, rml_dp(BorderWidth)}, - {Rml::PropertyId::BorderLeftWidth, rml_dp(BorderWidth)}, - {Rml::PropertyId::BorderTopLeftRadius, rml_dp(BorderRadiusMedium)}, - {Rml::PropertyId::BorderTopRightRadius, rml_dp(BorderRadiusMedium)}, - {Rml::PropertyId::BorderBottomRightRadius, rml_dp(BorderRadiusMedium)}, - {Rml::PropertyId::BorderBottomLeftRadius, rml_dp(BorderRadiusMedium)}, - {Rml::PropertyId::BorderTopColor, rml_color(ElevatedBorder)}, - {Rml::PropertyId::BorderRightColor, rml_color(ElevatedBorder)}, - {Rml::PropertyId::BorderBottomColor, rml_color(ElevatedBorder)}, - {Rml::PropertyId::BorderLeftColor, rml_color(ElevatedBorder)}, - {Rml::PropertyId::BackgroundColor, rml_color(WindowSurface)}, - {Rml::PropertyId::OverflowX, Rml::Style::Overflow::Hidden}, - {Rml::PropertyId::OverflowY, Rml::Style::Overflow::Hidden}, - }); - set_props(m_element, { - {"backdrop-filter", "blur(5dp)"}, - {"box-shadow", "0 0 25dp 5dp"}, - }); +void WindowModel::set_active_tab( + Rml::DataModelHandle model, Rml::Event& event, const Rml::VariantList& arguments) { + if (arguments.empty()) { + return; + } - m_tabBar = append(m_element, "div", {}, - { - {Rml::PropertyId::Display, Rml::Style::Display::Flex}, - {Rml::PropertyId::Position, Rml::Style::Position::Relative}, - {Rml::PropertyId::FlexDirection, Rml::Style::FlexDirection::Row}, - {Rml::PropertyId::AlignItems, Rml::Style::AlignItems::Center}, - {Rml::PropertyId::BoxSizing, Rml::Style::BoxSizing::BorderBox}, - {Rml::PropertyId::Width, rml_percent(100.0f)}, - {Rml::PropertyId::Height, rml_dp(WindowTabBarHeight)}, - {Rml::PropertyId::MinHeight, rml_dp(WindowTabBarHeight)}, - {Rml::PropertyId::PaddingLeft, rml_dp(12.0f)}, - {Rml::PropertyId::PaddingRight, rml_dp(12.0f)}, - {Rml::PropertyId::RowGap, rml_dp(4.0f)}, - {Rml::PropertyId::ColumnGap, rml_dp(4.0f)}, - {Rml::PropertyId::BackgroundColor, rml_color(WindowTitleOverlay)}, - {Rml::PropertyId::BorderBottomWidth, rml_dp(BorderWidth * 1.5f)}, - {Rml::PropertyId::BorderBottomColor, rml_color(WindowDivider)}, - }); + const int tabIndex = arguments[0].Get(); + if (tabIndex < 0 || tabIndex >= static_cast(tabs.size()) || tabIndex == activeTab) { + return; + } - m_tabStrip = append(m_tabBar, "div", {}, - { - {Rml::PropertyId::Display, Rml::Style::Display::Flex}, - {Rml::PropertyId::FlexDirection, Rml::Style::FlexDirection::Row}, - {Rml::PropertyId::AlignItems, Rml::Style::AlignItems::Stretch}, - {Rml::PropertyId::JustifyContent, Rml::Style::JustifyContent::FlexStart}, - {Rml::PropertyId::FlexGrow, rml_number(1.0f)}, - {Rml::PropertyId::FlexShrink, rml_number(1.0f)}, - {Rml::PropertyId::MinWidth, rml_px(0.0f)}, - {Rml::PropertyId::Height, rml_percent(100.0f)}, - {Rml::PropertyId::RowGap, rml_dp(4.0f)}, - {Rml::PropertyId::ColumnGap, rml_dp(4.0f)}, - }); + activeTab = tabIndex; + model.DirtyVariable("active_tab"); - const std::string closeId = id.empty() ? std::string{} : std::string(id) + "-close"; - m_closeButton = append(m_tabBar, "button", closeId, - { - {Rml::PropertyId::Display, Rml::Style::Display::Flex}, - {Rml::PropertyId::Position, Rml::Style::Position::Relative}, - {Rml::PropertyId::AlignItems, Rml::Style::AlignItems::Center}, - {Rml::PropertyId::JustifyContent, Rml::Style::JustifyContent::Center}, - {Rml::PropertyId::BoxSizing, Rml::Style::BoxSizing::BorderBox}, - {Rml::PropertyId::Width, rml_dp(36.0f)}, - {Rml::PropertyId::Height, rml_dp(36.0f)}, - {Rml::PropertyId::FlexShrink, rml_number(0.0f)}, - {Rml::PropertyId::BorderTopWidth, rml_px(0.0f)}, - {Rml::PropertyId::BorderRightWidth, rml_px(0.0f)}, - {Rml::PropertyId::BorderBottomWidth, rml_px(0.0f)}, - {Rml::PropertyId::BorderLeftWidth, rml_px(0.0f)}, - {Rml::PropertyId::BorderTopLeftRadius, rml_dp(BorderRadiusSmall)}, - {Rml::PropertyId::BorderTopRightRadius, rml_dp(BorderRadiusSmall)}, - {Rml::PropertyId::BorderBottomRightRadius, rml_dp(BorderRadiusSmall)}, - {Rml::PropertyId::BorderBottomLeftRadius, rml_dp(BorderRadiusSmall)}, - {Rml::PropertyId::BackgroundColor, rml_color(Transparent)}, - {Rml::PropertyId::Cursor, rml_string("pointer")}, - {Rml::PropertyId::TabIndex, Rml::Style::TabIndex::Auto}, - {Rml::PropertyId::FontFamily, rml_string("Inter")}, - }); - add_focus_border(m_closeButton, BorderRadiusSmall); + // Replace window content with new tab content + auto* currentElem = event.GetCurrentElement(); + if (currentElem == nullptr) { + return; + } + auto* doc = currentElem->GetOwnerDocument(); + if (doc == nullptr) { + return; + } + auto* content = doc->GetElementById("content"); + if (content == nullptr) { + return; + } + while (content->GetNumChildren() > 0) { + content->RemoveChild(content->GetFirstChild()); + } + if (tabs[tabIndex].setContent) { + tabs[tabIndex].setContent(content); + } +} - auto* closeGlyph = append(m_closeButton, "span", {}, - { - {Rml::PropertyId::FontSize, rml_dp(22.0f)}, - {Rml::PropertyId::FontWeight, Rml::Style::FontWeight::Normal}, - {Rml::PropertyId::Color, rml_color(WindowGlyph)}, - {Rml::PropertyId::PointerEvents, Rml::Style::PointerEvents::None}, - }); - set_text(closeGlyph, "\xc3\x97"); - - m_closeButton->AddEventListener(Rml::EventId::Click, this); - m_closeButton->AddEventListener(Rml::EventId::Focus, this); - m_closeButton->AddEventListener(Rml::EventId::Blur, this); - m_closeButton->AddEventListener(Rml::EventId::Mouseover, this); - m_closeButton->AddEventListener(Rml::EventId::Mouseout, this); - - m_contentRow = append(m_element, "div", {}, - { - {Rml::PropertyId::Display, Rml::Style::Display::Flex}, - {Rml::PropertyId::FlexDirection, Rml::Style::FlexDirection::Row}, - {Rml::PropertyId::AlignItems, Rml::Style::AlignItems::Stretch}, - {Rml::PropertyId::BoxSizing, Rml::Style::BoxSizing::BorderBox}, - {Rml::PropertyId::Width, rml_percent(100.0f)}, - {Rml::PropertyId::FlexGrow, rml_number(1.0f)}, - {Rml::PropertyId::FlexShrink, rml_number(1.0f)}, - {Rml::PropertyId::MinHeight, rml_px(0.0f)}, - {Rml::PropertyId::MinWidth, rml_px(0.0f)}, - {Rml::PropertyId::RowGap, rml_dp(20.0f)}, - {Rml::PropertyId::ColumnGap, rml_dp(20.0f)}, - }); - - m_leftPane = append(m_contentRow, "div", {}, - { - {Rml::PropertyId::Display, Rml::Style::Display::Flex}, - {Rml::PropertyId::FlexDirection, Rml::Style::FlexDirection::Column}, - {Rml::PropertyId::BoxSizing, Rml::Style::BoxSizing::BorderBox}, - {Rml::PropertyId::MinWidth, rml_px(0.0f)}, - {Rml::PropertyId::MinHeight, rml_px(0.0f)}, - {Rml::PropertyId::PaddingTop, rml_dp(24.0f)}, - {Rml::PropertyId::PaddingRight, rml_dp(24.0f)}, - {Rml::PropertyId::PaddingBottom, rml_dp(24.0f)}, - {Rml::PropertyId::PaddingLeft, rml_dp(24.0f)}, - {Rml::PropertyId::RowGap, rml_dp(12.0f)}, - {Rml::PropertyId::ColumnGap, rml_dp(12.0f)}, - }); - - m_rightPane = append(m_contentRow, "div", {}, - { - {Rml::PropertyId::Display, Rml::Style::Display::None}, - {Rml::PropertyId::FlexDirection, Rml::Style::FlexDirection::Column}, - {Rml::PropertyId::BoxSizing, Rml::Style::BoxSizing::BorderBox}, - {Rml::PropertyId::MinWidth, rml_px(0.0f)}, - {Rml::PropertyId::MinHeight, rml_px(0.0f)}, - {Rml::PropertyId::PaddingTop, rml_dp(24.0f)}, - {Rml::PropertyId::PaddingBottom, rml_dp(24.0f)}, - {Rml::PropertyId::PaddingRight, rml_dp(24.0f)}, - {Rml::PropertyId::PaddingLeft, rml_dp(8.0f)}, - {Rml::PropertyId::AlignItems, Rml::Style::AlignItems::FlexStart}, - {Rml::PropertyId::OverflowY, Rml::Style::Overflow::Auto}, - }); - - m_rightPane = append(m_contentRow, "div", {}, - { - {Rml::PropertyId::Display, Rml::Style::Display::None}, - {Rml::PropertyId::FlexDirection, Rml::Style::FlexDirection::Column}, - {Rml::PropertyId::BoxSizing, Rml::Style::BoxSizing::BorderBox}, - {Rml::PropertyId::MinWidth, rml_px(0.0f)}, - {Rml::PropertyId::MinHeight, rml_px(0.0f)}, - {Rml::PropertyId::PaddingTop, rml_dp(24.0f)}, - {Rml::PropertyId::PaddingBottom, rml_dp(24.0f)}, - {Rml::PropertyId::PaddingRight, rml_dp(24.0f)}, - {Rml::PropertyId::PaddingLeft, rml_dp(8.0f)}, - {Rml::PropertyId::AlignItems, Rml::Style::AlignItems::FlexStart}, - {Rml::PropertyId::OverflowY, Rml::Style::Overflow::Auto}, - }); - - apply_pane_layout(); - apply_close_style(); +Window::Window(WindowModel model) : mModel(std::move(model)) { + auto* context = aurora::rmlui::get_context(); + if (context == nullptr) { + return; + } + setup_window_model(context, mModel, mModelHandle); + mDocument = context->LoadDocumentFromMemory(kWindowDocumentRml); + if (mDocument == nullptr) { + return; + } + mModel.tabs[0].setContent(mDocument->GetElementById("content")); } Window::~Window() { - m_tabs.clear(); - if (m_closeButton != nullptr) { - m_closeButton->RemoveEventListener(Rml::EventId::Click, this); - m_closeButton->RemoveEventListener(Rml::EventId::Focus, this); - m_closeButton->RemoveEventListener(Rml::EventId::Blur, this); - m_closeButton->RemoveEventListener(Rml::EventId::Mouseover, this); - m_closeButton->RemoveEventListener(Rml::EventId::Mouseout, this); - m_closeButton = nullptr; - } - m_element = nullptr; -} - -void Window::ProcessEvent(Rml::Event& event) { - if (event.GetTargetElement() != m_closeButton) { - return; - } - - switch (event.GetId()) { - case Rml::EventId::Click: - if (m_closeCallback) { - m_closeCallback(); - } - break; - case Rml::EventId::Focus: - m_closeFocused = true; - apply_close_style(); - break; - case Rml::EventId::Blur: - m_closeFocused = false; - apply_close_style(); - break; - case Rml::EventId::Mouseover: - m_closeHovered = true; - apply_close_style(); - break; - case Rml::EventId::Mouseout: - m_closeHovered = false; - apply_close_style(); - break; - default: - break; + auto* context = aurora::rmlui::get_context(); + if (context != nullptr && mDocument != nullptr) { + context->UnloadDocument(mDocument); + mDocument = nullptr; } } -WindowTab* Window::add_tab( - std::string_view id, std::string_view label, std::function selectedCallback) { - if (m_tabStrip == nullptr) { - return nullptr; - } - - const std::string idString(id); - auto wrapped = [this, idString, cb = std::move(selectedCallback)]() { - set_selected_tab(idString); - if (cb) { - cb(); - } - }; - auto tab = std::make_unique(m_tabStrip, idString, label, std::move(wrapped)); - WindowTab* raw = tab.get(); - m_tabs.push_back(std::move(tab)); - return raw; -} - -void Window::set_selected_tab(std::string_view id) { - for (auto& tab : m_tabs) { - tab->set_selected(tab->id() == id); +void Window::show() { + if (mDocument != nullptr) { + mDocument->Show(); } } -std::string Window::selected_tab_id() const { - for (const auto& tab : m_tabs) { - if (tab->is_selected()) { - return tab->id(); - } - } - return {}; -} - -void Window::set_right_pane_visible(bool visible) { - if (m_rightPaneVisible == visible) { - return; - } - m_rightPaneVisible = visible; - apply_pane_layout(); -} - -void Window::apply_pane_layout() { - using namespace theme; - - if (m_leftPane == nullptr || m_rightPane == nullptr) { - return; - } - - if (m_rightPaneVisible) { - set_props(m_leftPane, { - {Rml::PropertyId::Display, Rml::Style::Display::Flex}, - {Rml::PropertyId::FlexGrow, rml_number(1.0f)}, - {Rml::PropertyId::FlexShrink, rml_number(1.0f)}, - {Rml::PropertyId::FlexBasis, rml_px(0.0f)}, - {Rml::PropertyId::MinWidth, rml_px(0.0f)}, - }); - set_props(m_rightPane, { - {Rml::PropertyId::Display, Rml::Style::Display::Flex}, - {Rml::PropertyId::FlexGrow, rml_number(0.0f)}, - {Rml::PropertyId::FlexShrink, rml_number(0.0f)}, - {Rml::PropertyId::FlexBasis, rml_percent(40.0f)}, - {Rml::PropertyId::MinWidth, rml_px(0.0f)}, - }); - } else { - set_props( - m_leftPane, { - {Rml::PropertyId::Display, Rml::Style::Display::Flex}, - {Rml::PropertyId::FlexGrow, rml_number(1.0f)}, - {Rml::PropertyId::FlexShrink, rml_number(1.0f)}, - {Rml::PropertyId::FlexBasis, Rml::Style::LengthPercentageAuto::Auto}, - {Rml::PropertyId::Width, rml_percent(100.0f)}, - }); - set_props(m_rightPane, { - {Rml::PropertyId::Display, Rml::Style::Display::None}, - }); +void Window::hide() { + if (mDocument != nullptr) { + mDocument->Hide(); } } -void Window::apply_close_style() { - using namespace theme; - - if (m_closeButton == nullptr) { - return; - } - - const bool active = m_closeHovered || m_closeFocused; - m_closeButton->SetProperty(Rml::PropertyId::BackgroundColor, - active ? rml_color(WindowAccent, 56) : rml_color(Transparent)); - set_focus_border_visible(m_closeButton, m_closeFocused); -} } // namespace dusk::ui diff --git a/src/dusk/ui/window.hpp b/src/dusk/ui/window.hpp index 66a30ad2b9..2faa4fb140 100644 --- a/src/dusk/ui/window.hpp +++ b/src/dusk/ui/window.hpp @@ -1,84 +1,35 @@ #pragma once -#include - -#include -#include -#include -#include -#include - -namespace Rml { -class Element; -} +#include +#include namespace dusk::ui { -class WindowTab : public Rml::EventListener { -public: - WindowTab(Rml::Element* parent, std::string_view id, std::string_view label, - std::function selectedCallback); - ~WindowTab() override; - WindowTab(const WindowTab&) = delete; - WindowTab& operator=(const WindowTab&) = delete; - - void ProcessEvent(Rml::Event& event) override; - - Rml::Element* element() const { return m_element; } - std::string id() const; - - void set_selected(bool selected); - bool is_selected() const { return m_selected; } - -private: - Rml::Element* m_element = nullptr; - Rml::Element* m_label = nullptr; - Rml::Element* m_indicator = nullptr; - std::function m_selectedCallback; - bool m_hovered = false; - bool m_focused = false; - bool m_selected = false; - - void apply_style(); +struct WindowTab { + Rml::String label; + std::function setContent; }; -class Window : public Rml::EventListener { +struct WindowModel { + int activeTab = 0; + std::vector tabs; + + void set_active_tab( + Rml::DataModelHandle model, Rml::Event& event, const Rml::VariantList& arguments); +}; + +class Window { public: - Window(Rml::Element* parent, std::string_view id, std::function closeCallback = {}); - ~Window() override; + Window(WindowModel model); + ~Window(); - Window(const Window&) = delete; - Window& operator=(const Window&) = delete; - - void ProcessEvent(Rml::Event& event) override; - - Rml::Element* element() const { return m_element; } - Rml::Element* body() const { return m_leftPane; } - Rml::Element* right_pane() const { return m_rightPane; } - void set_right_pane_visible(bool visible); - - Rml::Element* tab_strip() const { return m_tabStrip; } - - WindowTab* add_tab( - std::string_view id, std::string_view label, std::function selectedCallback); - void set_selected_tab(std::string_view id); - std::string selected_tab_id() const; + void show(); + void hide(); private: - Rml::Element* m_element = nullptr; - Rml::Element* m_tabBar = nullptr; - Rml::Element* m_tabStrip = nullptr; - Rml::Element* m_closeButton = nullptr; - Rml::Element* m_contentRow = nullptr; - Rml::Element* m_leftPane = nullptr; - Rml::Element* m_rightPane = nullptr; - bool m_rightPaneVisible = false; - std::function m_closeCallback; - std::vector > m_tabs; - bool m_closeHovered = false; - bool m_closeFocused = false; - - void apply_close_style(); - void apply_pane_layout(); + WindowModel mModel; + Rml::DataModelHandle mModelHandle; + Rml::ElementDocument* mDocument; }; -} // namespace dusk::ui + +} // namespace dusk::ui \ No newline at end of file diff --git a/src/m_Do/m_Do_main.cpp b/src/m_Do/m_Do_main.cpp index 80a0cd9e51..6f58b95e89 100644 --- a/src/m_Do/m_Do_main.cpp +++ b/src/m_Do/m_Do_main.cpp @@ -56,8 +56,8 @@ #include "dusk/logging.h" #include "dusk/main.h" #include "dusk/imgui/ImGuiConsole.hpp" -#include "dusk/ui/game_menu.hpp" -#include "dusk/ui/prelaunch_screen.hpp" +#include "dusk/ui/ui.hpp" +#include "dusk/ui/editor.hpp" #include "version.h" #include @@ -77,6 +77,7 @@ #include "tracy/Tracy.hpp" #include "f_pc/f_pc_draw.h" #include "tracy/Tracy.hpp" +#include // --- GLOBALS --- s8 mDoMain::developmentMode = -1; @@ -135,23 +136,18 @@ AuroraStats dusk::lastFrameAuroraStats; float dusk::frameUsagePct = 0.0f; bool launchUILoop() { - const bool useRmlPrelaunch = dusk::ui::prelaunch::initialize(); - while (dusk::IsRunning && !dusk::IsGameLaunched) { const AuroraEvent* event = aurora_update(); while (event != nullptr && event->type != AURORA_NONE) { switch (event->type) { case AURORA_SDL_EVENT: - if (useRmlPrelaunch) { - dusk::ui::prelaunch::handle_event(event->sdl); - } - // dusk::g_imguiConsole.HandleSDLEvent(event->sdl); + dusk::ui::handle_event(event->sdl); + dusk::g_imguiConsole.HandleSDLEvent(event->sdl); break; case AURORA_DISPLAY_SCALE_CHANGED: - // dusk::ImGuiEngine_Initialize(event->windowSize.scale); + dusk::ImGuiEngine_Initialize(event->windowSize.scale); break; case AURORA_EXIT: - dusk::ui::prelaunch::shutdown(); return false; } @@ -163,15 +159,13 @@ bool launchUILoop() { continue; } - if (useRmlPrelaunch) { - dusk::ui::prelaunch::update(); - } + dusk::g_imguiConsole.PreDraw(); + + dusk::g_imguiConsole.PostDraw(); aurora_end_frame(); } - dusk::ui::prelaunch::shutdown(); - return dusk::IsRunning; } @@ -214,7 +208,6 @@ void main01(void) { OSReport("Entering Main Loop (main01)...\n"); dusk::game_clock::ensure_initialized(); - dusk::ui::game_menu::initialize(); do { // 1. Update Window Events @@ -224,7 +217,8 @@ void main01(void) { case AURORA_NONE: goto eventsDone; case AURORA_SDL_EVENT: - dusk::ui::game_menu::handle_event(event->sdl); + dusk::ui::handle_event(event->sdl); + dusk::g_imguiConsole.HandleSDLEvent(event->sdl); if (event->sdl.type == SDL_EVENT_WINDOW_FOCUS_LOST && dusk::getSettings().game.pauseOnFocusLost) { dusk::IsFocusPaused = true; @@ -236,6 +230,9 @@ void main01(void) { dusk::game_clock::reset_frame_timer(); } break; + case AURORA_DISPLAY_SCALE_CHANGED: + dusk::ImGuiEngine_Initialize(event->windowSize.scale); + break; case AURORA_EXIT: goto exit; } @@ -260,7 +257,7 @@ void main01(void) { mDoGph_gInf_c::updateRenderSize(); - dusk::ui::game_menu::update(); + dusk::ui::update(); const auto pacing = dusk::game_clock::advance_main_loop(); if (pacing.is_interpolating) { @@ -313,7 +310,7 @@ void main01(void) { } while (dusk::IsRunning); exit:; - dusk::ui::game_menu::shutdown(); + dusk::ui::shutdown(); } static bool IsBackendAvailable(AuroraBackend backend) { @@ -359,6 +356,11 @@ static AuroraBackend ResolveDesiredBackend(const cxxopts::ParseResult& parsedArg return desiredBackend; } +static void aurora_imgui_init_callback(const AuroraWindowSize* size) { + dusk::ImGuiEngine_Initialize(size->scale); + dusk::ImGuiEngine_AddTextures(); +} + static void ApplyCVarOverrides(const cxxopts::OptionValue& option) { if (option.count() == 0) { return; @@ -559,6 +561,7 @@ int game_main(int argc, char* argv[]) { config.mem1Size = 256 * 1024 * 1024; config.mem2Size = 24 * 1024 * 1024; config.allowJoystickBackgroundEvents = true; + config.imGuiInitCallback = &aurora_imgui_init_callback; config.allowTextureReplacements = true; config.allowTextureDumps = false; auroraInfo = aurora_initialize(argc, argv, &config); @@ -581,6 +584,11 @@ int game_main(int argc, char* argv[]) { dusk::audio::SetMasterVolume(dusk::getSettings().audio.masterVolume / 100.0f); dusk::audio::SetEnableReverb(dusk::getSettings().audio.enableReverb); + dusk::ui::initialize(); + + // TODO: just for testing + dusk::ui::EditorWindow editorWindow; + editorWindow.show(); std::string dvd_path; bool dvd_opened = false; @@ -659,6 +667,7 @@ int game_main(int argc, char* argv[]) { #ifdef DUSK_DISCORD_RPC dusk::discord::Shutdown(); #endif + dusk::ui::shutdown(); aurora_shutdown(); return 0;