Start using Rml::PropertyId/Property instead of strings

This commit is contained in:
Luke Street
2026-04-28 00:18:52 -06:00
parent 6503b4e7eb
commit ff78bc8d6c
22 changed files with 787 additions and 601 deletions
+1 -1
View File
@@ -2,7 +2,7 @@
Language: Cpp
Standard: C++03
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignAfterOpenBracket: DontAlign
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignOperands: true
+39 -33
View File
@@ -28,43 +28,49 @@ ControlSurfaceTone control_surface_tone(ButtonVariant variant) {
} // namespace
Button::Button(Rml::Element* parent, std::string_view id, std::string_view text,
ButtonVariant variant, std::function<void()> pressedCallback)
ButtonVariant variant, std::function<void()> pressedCallback)
: m_variant(variant), m_pressedCallback(std::move(pressedCallback)) {
using namespace theme;
m_element = append(parent, "button", id);
set_props(m_element, {
{"display", "flex"},
{"position", "relative"},
{"flex-direction", "row"},
{"align-items", "center"},
{"justify-content", "center"},
{"box-sizing", "border-box"},
{"width", "100%"},
{"height", "68dp"},
{"min-height", "68dp"},
{"max-height", "68dp"},
{"padding-left", "22dp"},
{"padding-right", "22dp"},
{"border-width", dp(BorderWidth)},
{"border-radius", dp(BorderRadiusMedium)},
{"cursor", "pointer"},
{"tab-index", "auto"},
{"nav-up", "auto"},
{"nav-down", "auto"},
{"nav-left", "auto"},
{"nav-right", "auto"},
{"opacity", "1"},
{"font-family", "Inter"},
{"color", rgba(Text)},
});
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, {
{"pointer-events", "none"},
{"text-align", "center"},
{Rml::PropertyId::PointerEvents, Rml::Style::PointerEvents::None},
{Rml::PropertyId::TextAlign, Rml::Style::TextAlign::Center},
});
m_element->AddEventListener(Rml::EventId::Click, this);
@@ -136,10 +142,10 @@ void Button::apply_style() {
}
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("color", rgba(active ? TextActive : Text));
m_label->SetProperty("color", rgba(active ? TextActive : Text));
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);
}
+1 -1
View File
@@ -21,7 +21,7 @@ enum class ButtonVariant {
class Button : public Rml::EventListener {
public:
Button(Rml::Element* parent, std::string_view id, std::string_view text, ButtonVariant variant,
std::function<void()> pressedCallback);
std::function<void()> pressedCallback);
~Button() override;
Button(const Button&) = delete;
+11 -3
View File
@@ -41,12 +41,20 @@ ControlSurfaceStyle control_surface_style(ControlSurfaceTone tone) {
}
}
void apply_control_surface_style(Rml::Element* element, const ControlSurfaceStyle& style, bool active) {
void apply_control_surface_style(
Rml::Element* element, const ControlSurfaceStyle& style, bool active) {
if (element == nullptr) {
return;
}
element->SetProperty("border-color", active ? theme::rgba(style.accent, style.activeBorderOpacity) : theme::rgba(theme::ElevatedBorder, style.inactiveBorderOpacity));
element->SetProperty("background-color", theme::rgba(style.accent, active ? style.activeBackgroundOpacity : style.inactiveBackgroundOpacity));
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
+2 -1
View File
@@ -23,5 +23,6 @@ struct ControlSurfaceStyle {
};
ControlSurfaceStyle control_surface_style(ControlSurfaceTone tone);
void apply_control_surface_style(Rml::Element* element, const ControlSurfaceStyle& style, bool active);
void apply_control_surface_style(
Rml::Element* element, const ControlSurfaceStyle& style, bool active);
} // namespace dusk::ui
+49 -35
View File
@@ -12,47 +12,57 @@
namespace dusk::ui {
DiscState::DiscState(Rml::Element* parent, std::string_view id, std::string_view text,
std::string_view statusText, bool statusIsError,
std::function<void()> pressedCallback)
std::string_view statusText, bool statusIsError, std::function<void()> pressedCallback)
: m_pressedCallback(std::move(pressedCallback)), m_statusIsError(statusIsError) {
using namespace theme;
m_element = append(parent, "button", id);
set_props(m_element, {
{"display", "flex"},
{"position", "relative"},
{"flex-direction", "column"},
{"align-items", "stretch"},
{"gap", "6dp"},
{"width", "100%"},
{"box-sizing", "border-box"},
{"padding", "14dp 16dp"},
{"border-width", dp(BorderWidth)},
{"border-radius", dp(BorderRadiusSmall)},
{"cursor", "pointer"},
{"tab-index", "auto"},
{"nav-up", "auto"},
{"nav-down", "auto"},
{"nav-left", "auto"},
{"nav-right", "auto"},
{"font-family", "Inter"},
});
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, {
{"overflow", "hidden"},
{"text-overflow", "ellipsis"},
{"white-space", "nowrap"},
{"pointer-events", "none"},
{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, {
{"pointer-events", "none"},
{"white-space", "normal"},
{Rml::PropertyId::PointerEvents, Rml::Style::PointerEvents::None},
{Rml::PropertyId::WhiteSpace, Rml::Style::WhiteSpace::Normal},
});
}
@@ -123,15 +133,19 @@ void DiscState::apply_style() {
const bool active = m_hovered || m_focused;
const Color accent = m_statusIsError ? Danger : Primary;
m_element->SetProperty("background-color",
rgba(accent, active ? 52 : (m_statusIsError ? 32 : 20)));
m_element->SetProperty("border-color",
rgba(accent, active ? 220 : (m_statusIsError ? 190 : 120)));
m_element->SetProperty("color", rgba(active ? TextActive : Text));
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("color", rgba(active ? TextActive : Text));
m_value->SetProperty(Rml::PropertyId::Color, rml_color(active ? TextActive : Text));
if (m_status != nullptr) {
m_status->SetProperty("color", rgba(m_statusIsError ? Danger : TextDim));
m_status->SetProperty(
Rml::PropertyId::Color, rml_color(m_statusIsError ? Danger : TextDim));
}
set_focus_border_visible(m_element, m_focused);
}
+1 -2
View File
@@ -15,8 +15,7 @@ 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<void()> pressedCallback);
std::string_view statusText, bool statusIsError, std::function<void()> pressedCallback);
~DiscState() override;
DiscState(const DiscState&) = delete;
+18 -5
View File
@@ -34,7 +34,8 @@ std::string escape(std::string_view text) {
return result;
}
Rml::Element* append(Rml::Element* parent, std::string_view tag, std::string_view id) {
Rml::Element* append(Rml::Element* parent, std::string_view tag, std::string_view id,
std::initializer_list<std::pair<Rml::PropertyId, Rml::Property> > properties) {
if (parent == nullptr) {
return nullptr;
}
@@ -52,11 +53,13 @@ Rml::Element* append(Rml::Element* parent, std::string_view tag, std::string_vie
if (!id.empty()) {
rawChild->SetId(std::string(id));
}
return parent->AppendChild(std::move(child));
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* 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;
@@ -69,7 +72,7 @@ void set_text(Rml::Element* element, std::string_view text) {
}
void set_props(Rml::Element* element,
std::initializer_list<std::pair<std::string_view, std::string_view> > properties) {
std::initializer_list<std::pair<std::string_view, std::string_view> > properties) {
if (element == nullptr) {
return;
}
@@ -78,4 +81,14 @@ void set_props(Rml::Element* element,
}
}
void set_props(Rml::Element* element,
std::initializer_list<std::pair<Rml::PropertyId, Rml::Property> > properties) {
if (element == nullptr) {
return;
}
for (const auto& [name, value] : properties) {
element->SetProperty(name, value);
}
}
} // namespace dusk::ui
+7 -4
View File
@@ -8,11 +8,14 @@
namespace dusk::ui {
std::string escape(std::string_view text);
Rml::Element* append(Rml::Element* parent, std::string_view tag, std::string_view id = {});
Rml::Element* append_text(Rml::Element* parent, std::string_view tag, std::string_view text,
std::string_view id = {});
Rml::Element* append(Rml::Element* parent, std::string_view tag, std::string_view id = {},
std::initializer_list<std::pair<Rml::PropertyId, Rml::Property> > 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<std::pair<std::string_view, std::string_view> > properties);
std::initializer_list<std::pair<std::string_view, std::string_view> > properties);
void set_props(Rml::Element* element,
std::initializer_list<std::pair<Rml::PropertyId, Rml::Property> > properties);
} // namespace dusk::ui
+31 -16
View File
@@ -3,24 +3,35 @@
#include "element.hpp"
#include "theme.hpp"
#include <RmlUi/Core.h>
namespace dusk::ui {
Rml::Element* add_focus_border(Rml::Element* parent, float radius) {
using namespace theme;
auto* border = append(parent, "div");
set_props(border, {
{"position", "absolute"},
{"pointer-events", "none"},
{"left", dp(-(BorderWidth * 3.0f))},
{"top", dp(-(BorderWidth * 3.0f))},
{"right", dp(-(BorderWidth * 3.0f))},
{"bottom", dp(-(BorderWidth * 3.0f))},
{"border-width", dp(BorderWidth * 2.0f)},
{"border-radius", dp(radius + BorderWidth * 4.0f)},
{"border-color", rgba(PrimaryLight, 0)},
});
return border;
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) {
@@ -28,9 +39,13 @@ void set_focus_border_visible(Rml::Element* parent, bool visible) {
return;
}
parent->GetChild(0)->SetProperty("border-color", visible ?
theme::rgba(theme::PrimaryLight, 255) :
theme::rgba(theme::PrimaryLight, 0));
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
+135 -119
View File
@@ -70,13 +70,15 @@ constexpr std::array<const char*, 3> 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.";
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 "Uses inter-frame interpolation to enable higher frame rates.\nVisual artifacts, "
"animation glitches, or instability may occur.";
}
return "No description found.";
@@ -257,66 +259,74 @@ private:
void style_document(Rml::ElementDocument* document) {
using namespace theme;
set_props(document, {
{"width", "100%"},
{"height", "100%"},
{"margin", "0"},
{"padding", "0"},
{"font-family", "Inter"},
{"color", rgba(Text)},
{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;
auto* screen = append(m_document, "div", "game-menu-screen");
set_props(screen, {
{"display", "flex"},
{"position", "absolute"},
{"left", "0"},
{"top", "0"},
{"right", "0"},
{"bottom", "0"},
{"flex-direction", "column"},
{"align-items", "center"},
{"justify-content", "center"},
{"box-sizing", "border-box"},
{"padding", "32dp"},
});
return screen;
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");
set_props(row, {
{"display", "flex"},
{"flex-direction", "row"},
{"align-items", "center"},
{"width", "100%"},
{"padding-top", "8dp"},
{"padding-bottom", "4dp"},
});
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, {
{"font-size", "14dp"},
{"letter-spacing", "3dp"},
{"color", theme::rgba(theme::WindowAccentSoft)},
{"flex-shrink", "0"},
{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) {
auto* scroll = append(parent, "div");
set_props(scroll, {
{"display", "flex"},
{"flex-direction", "column"},
{"width", "100%"},
{"flex-grow", "1"},
{"min-height", "0"},
{"gap", "8dp"},
{"overflow-y", "auto"},
});
return scroll;
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<void()> queue_activate(std::string id) {
@@ -330,8 +340,8 @@ private:
}
void add_toggle(Rml::Element* parent, std::string id, std::string_view title,
config::ConfigVar<bool>& var, std::function<void(bool)> sideEffect = {},
std::string_view detail = {}) {
config::ConfigVar<bool>& var, std::function<void(bool)> sideEffect = {},
std::string_view detail = {}) {
auto mutate = [this, &var, sideEffect = std::move(sideEffect)] {
const bool next = !var.getValue();
var.setValue(next);
@@ -342,27 +352,26 @@ private:
m_needsRebuild = true;
};
const std::string_view valueText = var.getValue() ? "On" : "Off";
auto option = std::make_unique<GameOption>(parent, id, title, valueText, detail,
queue_activate(id));
auto option =
std::make_unique<GameOption>(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<void()> action, std::string_view valueText = ">",
std::string_view detail = {}) {
std::function<void()> 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<GameOption>(parent, id, title, valueText, detail,
queue_activate(id));
auto option =
std::make_unique<GameOption>(parent, id, title, valueText, detail, queue_activate(id));
register_row(Row{id, mutate, {}}, std::move(option));
}
template <typename T>
void add_cycle_row(Rml::Element* parent, std::string id, std::string_view title,
std::string_view valueText, std::string_view detail,
std::function<void(int)> cycle) {
std::string_view valueText, std::string_view detail, std::function<void(int)> cycle) {
auto mutate = [this, cycle] {
cycle(1);
m_needsRebuild = true;
@@ -371,15 +380,14 @@ private:
cycle(direction);
m_needsRebuild = true;
};
auto option = std::make_unique<GameOption>(parent, id, title, valueText, detail,
queue_activate(id));
auto option =
std::make_unique<GameOption>(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<int>& var, int minValue, int maxValue,
std::function<std::string(int)> formatter,
std::function<void(int)> sideEffect = {}) {
config::ConfigVar<int>& var, int minValue, int maxValue,
std::function<std::string(int)> formatter, std::function<void(int)> 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) {
@@ -400,11 +408,11 @@ private:
void add_bloom_mode_row(Rml::Element* parent) {
auto& var = getSettings().game.bloomMode;
const int current = std::clamp(static_cast<int>(var.getValue()), 0,
static_cast<int>(kBloomModeNames.size() - 1));
const int current = std::clamp(
static_cast<int>(var.getValue()), 0, static_cast<int>(kBloomModeNames.size() - 1));
const std::string_view valueText = kBloomModeNames[static_cast<size_t>(current)];
auto cycle = [&var](int dir) {
const int count = static_cast<int>(kBloomModeNames.size());
const int count = kBloomModeNames.size();
int next = static_cast<int>(var.getValue()) + dir;
next = (next % count + count) % count;
var.setValue(static_cast<BloomMode>(next));
@@ -427,15 +435,15 @@ private:
closest = i;
}
}
const int count = static_cast<int>(kBloomMultiplierStops.size());
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<int>(parent, "bloom-brightness", "Bloom Brightness", valueText, detail,
std::move(cycle));
add_cycle_row<int>(
parent, "bloom-brightness", "Bloom Brightness", valueText, detail, std::move(cycle));
}
void build_description_pane() {
@@ -446,15 +454,17 @@ private:
return;
}
m_descriptionElement = append_text(right, "p", " ", "option-description");
set_props(m_descriptionElement, {{"color", theme::rgba(theme::TextActive)},
{"font-size", "20dp"},
{"line-height", "1.45em"},
{"text-align", "left"},
{"align-self", "stretch"},
{"width", "100%"},
{"box-sizing", "border-box"},
{"white-space", "pre-line"},
});
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() {
@@ -486,20 +496,23 @@ private:
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); });
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);
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;
int next = std::clamp(getSettings().game.internalResolutionScale.getValue(), 0,
kInternalResolutionScaleMax) +
dir;
if (next < 0) {
next = kInternalResolutionScaleMax;
} else if (next > kInternalResolutionScaleMax) {
@@ -511,22 +524,22 @@ private:
};
add_cycle_row<int>(scroll, "internal-resolution", "Internal Resolution", scaleValue, detail,
std::move(scaleCycle));
std::move(scaleCycle));
add_int_cycle(scroll, "shadow-resolution", "Shadow Resolution",
getSettings().game.shadowResolutionMultiplier, 1, kShadowResolutionMax,
[](int v) { return fmt::format("x{}", v); });
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); });
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");
getSettings().game.enableFrameInterpolation, {}, "Experimental");
add_section_header(scroll, "Post-Processing");
@@ -535,8 +548,8 @@ private:
add_bloom_brightness_row(scroll);
}
add_toggle(scroll, "depth-of-field", "Depth of Field",
getSettings().game.enableDepthOfField);
add_toggle(
scroll, "depth-of-field", "Depth of Field", getSettings().game.enableDepthOfField);
add_section_header(scroll, "Developer Options");
@@ -545,31 +558,34 @@ private:
aurora::gx::enableLodBias = !aurora::gx::enableLodBias;
m_needsRebuild = true;
};
auto lodOption = std::make_unique<GameOption>(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));
auto lodOption = std::make_unique<GameOption>(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);
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");
set_props(wrap, {
{"display", "flex"},
{"flex-direction", "column"},
{"align-items", "center"},
{"justify-content", "center"},
{"width", "100%"},
{"flex-grow", "1"},
{"gap", "12dp"},
});
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, {{"text-align", "center"}});
set_props(heading, {{Rml::PropertyId::TextAlign, Rml::Style::TextAlign::Center}});
auto* sub = add_label(wrap, "Not yet ported.", LabelStyle::Body);
set_props(sub, {{"text-align", "center"}, {"color", theme::rgba(theme::TextDim)}});
set_props(sub, {
{Rml::PropertyId::TextAlign, Rml::Style::TextAlign::Center},
{Rml::PropertyId::Color, rml_color(theme::TextDim)},
});
}
void build_body() {
@@ -630,7 +646,7 @@ private:
void request_close() { m_requestClose = true; }
void switch_tab(int direction) {
const int count = static_cast<int>(kTabs.size());
const int count = kTabs.size();
const int next = (static_cast<int>(m_tab) + direction + count) % count;
m_pendingTabId = kTabs[static_cast<size_t>(next)].id;
}
+87 -72
View File
@@ -13,89 +13,104 @@
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<void()> pressedCallback)
std::string_view value, std::string_view detail, std::function<void()> pressedCallback)
: m_pressedCallback(std::move(pressedCallback)) {
using namespace theme;
m_element = append(parent, "button", id);
set_props(m_element, {
{"display", "flex"},
{"position", "relative"},
{"flex-direction", "row"},
{"align-items", "center"},
{"justify-content", "space-between"},
{"box-sizing", "border-box"},
{"gap", "16dp"},
{"width", "100%"},
{"height", "auto"},
{"padding", "16dp"},
{"border-width", dp(BorderWidth)},
{"border-radius", dp(BorderRadiusSmall)},
{"background-color", rgba(Transparent)},
{"border-color", rgba(ElevatedBorder, 0)},
{"color", rgba(TextDim)},
{"cursor", "pointer"},
{"tab-index", "auto"},
{"nav-up", "auto"},
{"nav-down", "auto"},
{"nav-left", "auto"},
{"nav-right", "auto"},
{"opacity", "1"},
{"font-family", "Inter"},
});
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");
set_props(left, {
{"display", "flex"},
{"flex-direction", "column"},
{"gap", "4dp"},
{"min-width", "0"},
{"width", "0"},
{"flex-grow", "1"},
{"flex-shrink", "1"},
{"pointer-events", "none"},
});
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, {
{"color", rgba(TextDim)},
{"font-size", "28dp"},
{"letter-spacing", "1dp"},
{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");
set_props(right, {
{"display", "flex"},
{"flex-direction", "column"},
{"align-items", "flex-end"},
{"justify-content", "center"},
{"gap", "4dp"},
{"min-width", "170dp"},
{"max-width", "48%"},
{"flex-shrink", "0"},
{"pointer-events", "none"},
});
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, {
{"color", rgba(TextDim)},
{"text-align", "right"},
{"overflow", "hidden"},
{"text-overflow", "ellipsis"},
{"white-space", "nowrap"},
});
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, {
{"color", rgba(TextDim)},
{"text-align", "right"},
{Rml::PropertyId::Color, rml_color(TextDim)},
{Rml::PropertyId::TextAlign, Rml::Style::TextAlign::Right},
});
}
}
@@ -167,18 +182,18 @@ void GameOption::apply_style() {
}
const bool active = m_hovered || m_focused;
apply_control_surface_style(m_element, control_surface_style(ControlSurfaceTone::Quiet),
active);
m_element->SetProperty("color",
active ? theme::rgba(theme::TextActive) : theme::rgba(theme::TextDim));
m_title->SetProperty("color",
active ? theme::rgba(theme::TextActive) : theme::rgba(theme::TextDim));
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("color",
active ? theme::rgba(theme::TextActive) : theme::rgba(theme::TextDim));
m_value->SetProperty(
Rml::PropertyId::Color, rml_color(active ? theme::TextActive : theme::TextDim));
}
if (m_detail != nullptr) {
m_detail->SetProperty("color", theme::rgba(theme::TextDim));
m_detail->SetProperty(Rml::PropertyId::Color, rml_color(theme::TextDim));
}
set_focus_border_visible(m_element, m_focused);
}
+1 -2
View File
@@ -15,8 +15,7 @@ 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<void()> pressedCallback);
std::string_view value, std::string_view detail, std::function<void()> pressedCallback);
~GameOption() override;
GameOption(const GameOption&) = delete;
+24 -16
View File
@@ -10,28 +10,36 @@ void apply_label_style(Rml::Element* element, LabelStyle style) {
switch (style) {
case LabelStyle::Annotation:
set_props(element, {{"font-size", "18dp"},
{"letter-spacing", "2dp"},
{"font-weight", "400"},
{"color", rgba(TextDim)}});
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, {{"font-size", "20dp"},
{"letter-spacing", "0"},
{"font-weight", "400"},
{"color", rgba(Text)}});
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, {{"font-size", "28dp"},
{"letter-spacing", "3dp"},
{"font-weight", "700"},
{"color", rgba(Text)}});
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, {{"font-size", "36dp"},
{"letter-spacing", "4dp"},
{"font-weight", "700"},
{"color", rgba(Text)}});
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;
}
}
+86 -61
View File
@@ -4,77 +4,90 @@
#include "label.hpp"
#include "theme.hpp"
#include <RmlUi/Core.h>
namespace dusk::ui::prelaunch::layout {
void style_document(Rml::ElementDocument* document) {
using namespace theme;
set_props(document, {
{"width", "100%"},
{"height", "100%"},
{"margin", "0"},
{"padding", "0"},
{"font-family", "Inter"},
{"background-color", rgba(Background1)},
{"color", rgba(Text)},
{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;
auto* screen = append(document, "div", "prelaunch-screen");
set_props(screen,
{
{"display", "flex"},
{"position", "absolute"},
{"left", "0"},
{"top", "0"},
{"right", "0"},
{"bottom", "0"},
{"flex-direction", layout == ScreenLayout::CompactSplit ? "row" : "column"},
{"align-items", "center"},
{"justify-content", "center"},
{"gap", layout == ScreenLayout::CompactSplit ? "28dp" : "24dp"},
{"box-sizing", "border-box"},
{"padding", layout == ScreenLayout::CompactSplit ? "24dp" : "48dp 28dp"},
{"background-color", rgba(Background1)},
});
return screen;
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");
set_props(brand, {
{"display", "flex"},
{"flex-direction", "column"},
{"align-items", "center"},
{"justify-content", "center"},
{"gap", compact ? "8dp" : "12dp"},
{"width", compact ? "260dp" : "100%"},
{"max-width", compact ? "32%" : "720dp"},
{"flex-shrink", compact ? "0" : "1"},
});
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, {
{"text-align", "center"},
{"font-size", compact ? "14dp" : "18dp"},
{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, {
{"width", compact ? "220dp" : "360dp"},
{"max-width", compact ? "100%" : "70%"},
{"height", "auto"},
{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, {
{"font-size", compact ? "42dp" : "54dp"},
{"letter-spacing", compact ? "3dp" : "4dp"},
{Rml::PropertyId::FontSize, rml_dp(compact ? 42.0f : 54.0f)},
{Rml::PropertyId::LetterSpacing, rml_dp(compact ? 3.0f : 4.0f)},
});
}
return brand;
@@ -83,9 +96,9 @@ Rml::Element* add_brand(Rml::Element* parent, std::string_view logoPath, bool co
Rml::Element* add_heading(Rml::Element* parent, std::string_view title) {
auto* heading = add_label(parent, title, LabelStyle::Large);
set_props(heading, {
{"width", "100%"},
{"max-width", "840dp"},
{"text-align", "left"},
{Rml::PropertyId::Width, rml_percent(100.0f)},
{Rml::PropertyId::MaxWidth, rml_dp(840.0f)},
{Rml::PropertyId::TextAlign, Rml::Style::TextAlign::Left},
});
return heading;
}
@@ -93,21 +106,33 @@ Rml::Element* add_heading(Rml::Element* parent, std::string_view title) {
Rml::Element* add_panel(Rml::Element* parent, bool wide, bool compact) {
using namespace theme;
auto* panel = append(parent, "div");
set_props(panel, {
{"display", "flex"},
{"flex-direction", "column"},
{"gap", "12dp"},
{"width", wide ? "840dp" : "520dp"},
{"max-width", compact ? "62%" : "100%"},
{"box-sizing", "border-box"},
{"padding", compact ? "16dp" : "20dp"},
{"border-width", dp(BorderWidth)},
{"border-radius", dp(BorderRadiusMedium)},
{"border-color", rgba(ElevatedBorder, ElevatedBorder.a)},
{"background-color", rgba(ElevatedSoft, ElevatedSoft.a)},
});
return panel;
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
+2 -2
View File
@@ -13,8 +13,8 @@ enum class ScreenLayout {
};
void style_document(Rml::ElementDocument* document);
Rml::Element* add_screen(Rml::ElementDocument* document,
ScreenLayout layout = ScreenLayout::Standard);
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);
+26 -23
View File
@@ -53,7 +53,11 @@ struct BackendChoice {
};
constexpr std::array<const char*, 5> kLanguageNames = {
"English", "German", "French", "Spanish", "Italian",
"English",
"German",
"French",
"Spanish",
"Italian",
};
constexpr std::array<SDL_DialogFileFilter, 2> kGameDiscFileFilters{{
@@ -217,7 +221,7 @@ std::string display_path(std::string_view path) {
std::vector<BackendChoice> backend_choices() {
std::vector<BackendChoice> choices;
choices.push_back({BACKEND_AUTO, std::string(backend_id(BACKEND_AUTO)),
std::string(backend_name(BACKEND_AUTO))});
std::string(backend_name(BACKEND_AUTO))});
size_t backendCount = 0;
const AuroraBackend* availableBackends = aurora_get_available_backends(&backendCount);
@@ -458,8 +462,8 @@ private:
m_requestedCycleDirection = direction;
}
void add_button_control(Rml::Element* parent, std::string_view id, std::string_view text,
ButtonVariant variant) {
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<Button>(
@@ -467,20 +471,19 @@ private:
}
void add_option_control(Rml::Element* parent, std::string_view id, std::string_view title,
std::string_view value, std::string_view detail) {
std::string_view value, std::string_view detail) {
const std::string idString(id);
m_focusIds.push_back(idString);
m_options.push_back(
std::make_unique<GameOption>(parent, idString, title, value, detail,
[this, idString] { queue_activation(idString); }));
m_options.push_back(std::make_unique<GameOption>(parent, idString, title, value, detail,
[this, idString] { queue_activation(idString); }));
}
void add_disc_control(Rml::Element* parent) {
const std::string idString("select-disc");
m_focusIds.push_back(idString);
m_discState = std::make_unique<DiscState>(
parent, idString, selected_disc_text(), disc_status_text(), !m_errorString.empty(),
[this, idString] { queue_activation(idString); });
m_discState =
std::make_unique<DiscState>(parent, idString, selected_disc_text(), disc_status_text(),
!m_errorString.empty(), [this, idString] { queue_activation(idString); });
}
void build_main(Rml::Element* screen) {
@@ -525,7 +528,7 @@ private:
if (m_isPal) {
const auto selectedLanguage = getSettings().game.language.getValue();
add_option_control(panel, "language", "Language",
kLanguageNames[static_cast<u8>(selectedLanguage)], "");
kLanguageNames[static_cast<u8>(selectedLanguage)], "");
}
const AuroraBackend backend = configured_backend();
@@ -533,8 +536,8 @@ private:
getSettings().backend.graphicsBackend.getValue() != m_initialGraphicsBackend ?
"Restart required" :
"";
add_option_control(panel, "graphics-backend", "Graphics Backend", backend_name(backend),
restartDetail);
add_option_control(
panel, "graphics-backend", "Graphics Backend", backend_name(backend), restartDetail);
const auto fileType =
static_cast<CARDFileType>(getSettings().backend.cardFileType.getValue());
@@ -556,7 +559,7 @@ private:
for (size_t i = 0; i < kLanguageNames.size(); ++i) {
const std::string id = fmt::format("language-{}", i);
add_option_control(panel, id, kLanguageNames[i],
i == static_cast<size_t>(selectedLanguage) ? "Current" : "", "");
i == static_cast<size_t>(selectedLanguage) ? "Current" : "", "");
}
add_button_control(panel, "back", "Back", ButtonVariant::Quiet);
@@ -574,8 +577,8 @@ private:
const std::string currentBackendId = configured_backend_id();
for (const BackendChoice& choice : backend_choices()) {
const std::string id = "backend-" + choice.id;
add_option_control(panel, id, choice.name,
choice.id == currentBackendId ? "Current" : "", "");
add_option_control(
panel, id, choice.name, choice.id == currentBackendId ? "Current" : "", "");
}
add_button_control(panel, "back", "Back", ButtonVariant::Quiet);
@@ -593,9 +596,9 @@ private:
const auto fileType =
static_cast<CARDFileType>(getSettings().backend.cardFileType.getValue());
add_option_control(panel, "save-gci-folder", "GCI Folder",
fileType == CARD_GCIFOLDER ? "Current" : "", "");
add_option_control(panel, "save-card-image", "Card Image",
fileType == CARD_RAWIMAGE ? "Current" : "", "");
fileType == CARD_GCIFOLDER ? "Current" : "", "");
add_option_control(
panel, "save-card-image", "Card Image", fileType == CARD_RAWIMAGE ? "Current" : "", "");
add_button_control(panel, "back", "Back", ButtonVariant::Quiet);
}
@@ -783,7 +786,7 @@ private:
void show_file_select() {
ShowFileSelect(&file_dialog_callback, this, aurora::window::get_sdl_window(),
kGameDiscFileFilters.data(), kGameDiscFileFilters.size(), nullptr, false);
kGameDiscFileFilters.data(), kGameDiscFileFilters.size(), nullptr, false);
}
void activate(std::string_view id) {
@@ -893,8 +896,8 @@ private:
}
if (id == "save-gci-folder" || id == "save-card-image") {
getSettings().backend.cardFileType.setValue(id == "save-gci-folder" ? CARD_GCIFOLDER :
CARD_RAWIMAGE);
getSettings().backend.cardFileType.setValue(
id == "save-gci-folder" ? CARD_GCIFOLDER : CARD_RAWIMAGE);
Save();
m_view = View::Options;
m_pendingFocusId = "save-file-type";
+1 -17
View File
@@ -1,19 +1,3 @@
#include "theme.hpp"
#include <algorithm>
#include <fmt/format.h>
namespace dusk::ui::theme {
std::string rgba(Color color, int opacity) {
if (opacity >= 0) {
color.a = std::clamp(opacity, 0, 255);
}
return fmt::format("rgba({}, {}, {}, {})", color.r, color.g, color.b, color.a);
}
std::string dp(float value) {
return fmt::format("{}dp", value);
}
} // namespace dusk::ui::theme
namespace dusk::ui::theme {} // namespace dusk::ui::theme
+32 -4
View File
@@ -1,8 +1,11 @@
#pragma once
#include <RmlUi/Core/Property.h>
#include <string>
namespace dusk::ui::theme {
namespace dusk::ui {
namespace theme {
struct Color {
int r = 255;
@@ -37,7 +40,32 @@ inline constexpr float BorderRadiusSmall = 8.0f;
inline constexpr float BorderRadiusMedium = 12.0f;
inline constexpr float BorderWidth = 2.0f;
std::string rgba(Color color, int opacity = -1);
std::string dp(float value);
} // namespace theme
} // namespace dusk::ui::theme
static Rml::Property rml_color(theme::Color color, int opacity = -1) {
if (opacity >= 0) {
color.a = std::clamp(opacity, 0, 255);
}
return Rml::Property(Rml::Colourb(color.r, color.g, color.b, color.a), Rml::Unit::COLOUR);
}
static Rml::Property rml_dp(float value) {
return Rml::Property(value, Rml::Unit::DP);
}
static Rml::Property rml_percent(float value) {
return Rml::Property(value, Rml::Unit::PERCENT);
}
static Rml::Property rml_px(float value) {
return Rml::Property(value, Rml::Unit::PX);
}
static Rml::Property rml_number(float value) {
return Rml::Property(value, Rml::Unit::NUMBER);
}
static Rml::Property rml_string(std::string_view value) {
return Rml::Property(Rml::String(value), Rml::Unit::STRING);
}
} // namespace dusk::ui
+2 -2
View File
@@ -89,8 +89,8 @@ Rml::Input::KeyIdentifier key_from_gamepad_button(Uint8 button) {
}
}
Rml::Input::KeyIdentifier key_from_gamepad_axis(const SDL_GamepadAxisEvent& event,
float axisValue) {
Rml::Input::KeyIdentifier key_from_gamepad_axis(
const SDL_GamepadAxisEvent& event, float axisValue) {
switch (event.axis) {
case SDL_GAMEPAD_AXIS_LEFTX:
return axisValue < 0.0f ? Rml::Input::KI_LEFT : Rml::Input::KI_RIGHT;
+227 -180
View File
@@ -1,59 +1,69 @@
#include "window.hpp"
#include "element.hpp"
#include "focus_border.hpp"
#include "label.hpp"
#include "theme.hpp"
#include <RmlUi/Core.h>
#include <RmlUi/Core/PropertyDictionary.h>
#include <RmlUi/Core/StyleSheetSpecification.h>
#include <algorithm>
namespace dusk::ui {
WindowTab::WindowTab(Rml::Element* parent, std::string_view id, std::string_view label, std::function<void()> selectedCallback) : m_selectedCallback(std::move(selectedCallback)) {
WindowTab::WindowTab(Rml::Element* parent, std::string_view id, std::string_view label,
std::function<void()> selectedCallback)
: m_selectedCallback(std::move(selectedCallback)) {
using namespace theme;
m_element = append(parent, "button", id);
set_props(m_element, {
{"display", "flex"},
{"position", "relative"},
{"flex-direction", "column"},
{"align-items", "center"},
{"justify-content", "center"},
{"box-sizing", "border-box"},
{"height", "100%"},
{"padding-left", "20dp"},
{"padding-right", "20dp"},
{"background-color", rgba(Transparent)},
{"border-width", "0"},
{"cursor", "pointer"},
{"tab-index", "auto"},
{"nav-up", "auto"},
{"nav-down", "auto"},
{"nav-left", "auto"},
{"nav-right", "auto"},
{"font-family", "Inter"},
});
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::Center},
{Rml::PropertyId::JustifyContent, Rml::Style::JustifyContent::Center},
{Rml::PropertyId::BoxSizing, Rml::Style::BoxSizing::BorderBox},
{Rml::PropertyId::Height, rml_percent(100.0f)},
{Rml::PropertyId::PaddingLeft, rml_dp(20.0f)},
{Rml::PropertyId::PaddingRight, rml_dp(20.0f)},
{Rml::PropertyId::BackgroundColor, rml_color(Transparent)},
{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::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_label = append_text(m_element, "span", label);
apply_label_style(m_label, LabelStyle::Body);
set_props(m_label, {
{"pointer-events", "none"},
{"font-size", "20dp"},
{"letter-spacing", "1dp"},
{"font-weight", "700"},
{"text-align", "center"},
});
m_label = append(m_element, "span", {},
{
{Rml::PropertyId::PointerEvents, Rml::Style::PointerEvents::None},
{Rml::PropertyId::FontSize, rml_dp(20.0f)},
{Rml::PropertyId::LetterSpacing, rml_dp(1.0f)},
{Rml::PropertyId::FontWeight, Rml::Style::FontWeight::Bold},
{Rml::PropertyId::TextAlign, Rml::Style::TextAlign::Center},
{Rml::PropertyId::Color, rml_color(Text)},
});
set_text(m_label, label);
m_indicator = append(m_element, "div");
set_props(m_indicator, {
{"position", "absolute"},
{"left", "0"},
{"right", "0"},
{"bottom", dp(-BorderWidth)},
{"height", dp(2.0f)},
{"background-color", rgba(WindowAccent, 0)},
{"pointer-events", "none"},
});
m_indicator = append(m_element, "div", {},
{
{Rml::PropertyId::Position, Rml::Style::Position::Absolute},
{Rml::PropertyId::Left, rml_px(0.0f)},
{Rml::PropertyId::Right, rml_px(0.0f)},
{Rml::PropertyId::Bottom, rml_dp(-BorderWidth)},
{Rml::PropertyId::Height, rml_dp(2.0f)},
{Rml::PropertyId::BackgroundColor, rml_color(WindowAccent, 0)},
{Rml::PropertyId::PointerEvents, Rml::Style::PointerEvents::None},
});
m_element->AddEventListener(Rml::EventId::Click, this);
m_element->AddEventListener(Rml::EventId::Focus, this);
@@ -134,93 +144,116 @@ void WindowTab::apply_style() {
textOpacity = 110;
}
const Color textColor = m_selected ? TextActive : Text;
m_label->SetProperty("color", rgba(textColor, textOpacity));
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("background-color", rgba(WindowAccent, indicatorOpacity));
m_indicator->SetProperty(
Rml::PropertyId::BackgroundColor, rml_color(WindowAccent, indicatorOpacity));
}
set_focus_border_visible(m_element, m_focused);
}
Window::Window(Rml::Element* parent, std::string_view id, std::function<void()> closeCallback) : m_closeCallback(std::move(closeCallback)) {
Window::Window(Rml::Element* parent, std::string_view id, std::function<void()> closeCallback)
: m_closeCallback(std::move(closeCallback)) {
using namespace theme;
m_element = append(parent, "div", id);
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, {
{"display", "flex"},
{"flex-direction", "column"},
{"box-sizing", "border-box"},
{"width", "100%"},
{"max-width", "1088dp"},
{"border-width", dp(BorderWidth)},
{"border-radius", dp(BorderRadiusMedium)},
{"border-color", rgba(ElevatedBorder)},
{"background-color", rgba(WindowSurface)},
{"backdrop-filter", "blur(5dp)"},
{"box-shadow", "0 0 25dp 5dp"},
{"overflow", "hidden"},
});
{"backdrop-filter", "blur(5dp)"},
{"box-shadow", "0 0 25dp 5dp"},
});
m_tabBar = append(m_element, "div");
set_props(m_tabBar, {
{"display", "flex"},
{"position", "relative"},
{"flex-direction", "row"},
{"align-items", "center"},
{"box-sizing", "border-box"},
{"width", "100%"},
{"height", dp(WindowTabBarHeight)},
{"min-height", dp(WindowTabBarHeight)},
{"padding-left", "12dp"},
{"padding-right", "12dp"},
{"gap", "4dp"},
{"background-color", rgba(WindowTitleOverlay)},
{"border-bottom-width", dp(BorderWidth * 1.5f)},
{"border-bottom-color", rgba(WindowDivider)},
});
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)},
});
m_tabStrip = append(m_tabBar, "div");
set_props(m_tabStrip, {
{"display", "flex"},
{"flex-direction", "row"},
{"align-items", "stretch"},
{"justify-content", "flex-start"},
{"flex-grow", "1"},
{"flex-shrink", "1"},
{"min-width", "0"},
{"height", "100%"},
{"gap", "4dp"},
});
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)},
});
const std::string closeId = id.empty() ? std::string{} : std::string(id) + "-close";
m_closeButton = append(m_tabBar, "button", closeId);
set_props(m_closeButton, {
{"display", "flex"},
{"position", "relative"},
{"align-items", "center"},
{"justify-content", "center"},
{"box-sizing", "border-box"},
{"width", "36dp"},
{"height", "36dp"},
{"flex-shrink", "0"},
{"border-width", "0"},
{"border-radius", dp(BorderRadiusSmall)},
{"background-color", rgba(Transparent)},
{"cursor", "pointer"},
{"tab-index", "auto"},
{"font-family", "Inter"},
});
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);
auto* closeGlyph = append_text(m_closeButton, "span", "\xc3\x97");
set_props(closeGlyph, {
{"font-size", "22dp"},
{"font-weight", "400"},
{"color", rgba(WindowGlyph)},
{"pointer-events", "none"},
});
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);
@@ -228,60 +261,65 @@ Window::Window(Rml::Element* parent, std::string_view id, std::function<void()>
m_closeButton->AddEventListener(Rml::EventId::Mouseover, this);
m_closeButton->AddEventListener(Rml::EventId::Mouseout, this);
m_contentRow = append(m_element, "div");
set_props(m_contentRow, {
{"display", "flex"},
{"flex-direction", "row"},
{"align-items", "stretch"},
{"box-sizing", "border-box"},
{"width", "100%"},
{"flex-grow", "1"},
{"flex-shrink", "1"},
{"min-height", "0"},
{"min-width", "0"},
{"gap", "20dp"},
});
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");
set_props(m_leftPane, {
{"display", "flex"},
{"flex-direction", "column"},
{"box-sizing", "border-box"},
{"min-width", "0"},
{"min-height", "0"},
{"padding", "24dp"},
{"gap", "12dp"},
});
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");
set_props(m_rightPane, {
{"display", "none"},
{"flex-direction", "column"},
{"box-sizing", "border-box"},
{"min-width", "0"},
{"min-height", "0"},
{"padding-top", "24dp"},
{"padding-bottom", "24dp"},
{"padding-right", "24dp"},
{"padding-left", "8dp"},
{"align-items", "flex-start"},
{"overflow-y", "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},
});
m_rightPane = append(m_contentRow, "div");
set_props(m_rightPane, {
{"display", "none"},
{"flex-direction", "column"},
{"box-sizing", "border-box"},
{"min-width", "0"},
{"min-height", "0"},
{"padding-top", "24dp"},
{"padding-bottom", "24dp"},
{"padding-right", "24dp"},
{"padding-left", "8dp"},
{"align-items", "flex-start"},
{"overflow-y", "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();
@@ -332,7 +370,8 @@ void Window::ProcessEvent(Rml::Event& event) {
}
}
WindowTab* Window::add_tab(std::string_view id, std::string_view label, std::function<void()> selectedCallback) {
WindowTab* Window::add_tab(
std::string_view id, std::string_view label, std::function<void()> selectedCallback) {
if (m_tabStrip == nullptr) {
return nullptr;
}
@@ -382,24 +421,31 @@ void Window::apply_pane_layout() {
if (m_rightPaneVisible) {
set_props(m_leftPane, {
{"display", "flex"},
{"flex", "1 1 0"},
{"min-width", "0"},
});
{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, {
{"display", "flex"},
{"flex", "0 0 40%"},
{"min-width", "0"},
});
{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, {
{"display", "flex"},
{"flex", "1 1 auto"},
{"width", "100%"},
});
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, {
{"display", "none"},
});
{Rml::PropertyId::Display, Rml::Style::Display::None},
});
}
}
@@ -411,7 +457,8 @@ void Window::apply_close_style() {
}
const bool active = m_closeHovered || m_closeFocused;
m_closeButton->SetProperty("background-color", active ? rgba(WindowAccent, 56) : rgba(Transparent));
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
+4 -2
View File
@@ -15,7 +15,8 @@ class Element;
namespace dusk::ui {
class WindowTab : public Rml::EventListener {
public:
WindowTab(Rml::Element* parent, std::string_view id, std::string_view label, std::function<void()> selectedCallback);
WindowTab(Rml::Element* parent, std::string_view id, std::string_view label,
std::function<void()> selectedCallback);
~WindowTab() override;
WindowTab(const WindowTab&) = delete;
@@ -58,7 +59,8 @@ public:
Rml::Element* tab_strip() const { return m_tabStrip; }
WindowTab* add_tab(std::string_view id, std::string_view label, std::function<void()> selectedCallback);
WindowTab* add_tab(
std::string_view id, std::string_view label, std::function<void()> selectedCallback);
void set_selected_tab(std::string_view id);
std::string selected_tab_id() const;