mirror of
https://github.com/Mr-Wiseguy/Zelda64Recomp.git
synced 2025-03-12 06:48:49 +01:00
Rewrite mod details under new UI system.
This commit is contained in:
parent
226ccdda74
commit
425dfeec24
@ -172,8 +172,14 @@ set (SOURCES
|
||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ElementOptionTypeRange.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ElementOptionTypeTextField.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ElementModMenu.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ElementModDetailsPanel.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/ui/elements/presets.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_button.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_container.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_element.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_image.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_label.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_mod_details_panel.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ui_toggle.cpp
|
||||
|
||||
${CMAKE_SOURCE_DIR}/rsp/aspMain.cpp
|
||||
${CMAKE_SOURCE_DIR}/rsp/njpgdspMain.cpp
|
||||
|
@ -26,7 +26,8 @@
|
||||
<link type="text/template" href="config_menu/general.rml" />
|
||||
<link type="text/template" href="config_menu/controls.rml" />
|
||||
<link type="text/template" href="config_menu/graphics.rml" />
|
||||
<link type="text/template" href="config_menu/sound.rml" />
|
||||
<link type="text/template" href="config_menu/sound.rml" />
|
||||
<link type="text/template" href="config_menu/mods.rml" />
|
||||
<link type="text/template" href="config_menu/debug.rml" />
|
||||
<link type="text/template" href="config_menu/cheats.rml" />
|
||||
<link type="text/template" href="components/prompt.rml" />
|
||||
@ -65,6 +66,13 @@
|
||||
<panel class="config" data-model="sound_options_model">
|
||||
<template src="config-menu__sound" />
|
||||
</panel>
|
||||
<tab class="tab" id="tab_mods">
|
||||
<div>Mods</div>
|
||||
<div class="tab__indicator"></div>
|
||||
</tab>
|
||||
<panel class="config">
|
||||
<template src="config-menu__mods" />
|
||||
</panel>
|
||||
<tab class="tab" data-model="debug_model" data-if="debug_enabled" id="tab_debug">
|
||||
<div>Debug</div>
|
||||
<div class="tab__indicator"></div>
|
||||
|
9
assets/config_menu/mods.rml
Normal file
9
assets/config_menu/mods.rml
Normal file
@ -0,0 +1,9 @@
|
||||
<template name="config-menu__mods">
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<form class="config__form">
|
||||
<recomp-mod-menu />
|
||||
</form>
|
||||
</body>
|
||||
</template>
|
@ -51,6 +51,10 @@
|
||||
<div class="menu-list-item__bullet">•</div>
|
||||
<div class="menu-list-item__label">Settings</div>
|
||||
</button>
|
||||
<button onclick="open_mods" class="menu-list-item menu-list-item--right">
|
||||
<div class="menu-list-item__bullet">•</div>
|
||||
<div class="menu-list-item__label">Mods</div>
|
||||
</button>
|
||||
<button onclick="exit_game" class="menu-list-item menu-list-item--right">
|
||||
<div class="menu-list-item__bullet">•</div>
|
||||
<div class="menu-list-item__label">Exit</div>
|
||||
@ -61,7 +65,6 @@
|
||||
<label>v{{version_number}}</label>
|
||||
</div>
|
||||
</div>
|
||||
<recomp-mod-menu />
|
||||
</div>
|
||||
</body>
|
||||
</rml>
|
||||
|
2587
assets/recomp.rcss
2587
assets/recomp.rcss
File diff suppressed because one or more lines are too long
@ -1,74 +0,0 @@
|
||||
.mod-details {
|
||||
display: block;
|
||||
position: relative;
|
||||
flex: 1 1 200%;
|
||||
height: 100%;
|
||||
flex-direction: column;
|
||||
border-bottom-right-radius: $border-radius-modal;
|
||||
background-color: $color-bg-overlay;
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
position: relative;
|
||||
flex-direction: row;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
padding: space(16);
|
||||
// border-width: $border-width-thickness;
|
||||
// border-radius: $border-radius-modal;
|
||||
// border-color: $color-border;
|
||||
background: $color-bg-shadow;
|
||||
}
|
||||
|
||||
&__thumbnail-container {
|
||||
height: auto;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
&__thumbnail {
|
||||
height: 100dp;
|
||||
width: 100dp;
|
||||
// max-width: 100dp;
|
||||
// max-height: 100dp;
|
||||
// padding: space(16);
|
||||
// border-bottom-width: $border-width-thickness;
|
||||
// border-top-left-radius: $border-radius-modal;
|
||||
// border-top-right-radius: $border-radius-modal;
|
||||
// border-bottom-color: $color-border;
|
||||
background-color: $color-bg-overlay;
|
||||
}
|
||||
|
||||
&__header-details {
|
||||
display: flex;
|
||||
position: relative;
|
||||
flex-direction: column;
|
||||
justify-content:space-evenly;
|
||||
width: auto;
|
||||
height: auto;
|
||||
margin-left: space(16);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&__title {
|
||||
@extend %header-3;
|
||||
}
|
||||
|
||||
&__version {
|
||||
@extend %label-md;
|
||||
}
|
||||
|
||||
&__body {
|
||||
display: flex;
|
||||
position: relative;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
padding-left: space(16);
|
||||
}
|
||||
|
||||
&__authors, &__description {
|
||||
@extend %label-md;
|
||||
margin-top: space(16);
|
||||
}
|
||||
}
|
||||
|
@ -1,58 +1,11 @@
|
||||
.mod-menu {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
flex-direction: row;
|
||||
position: relative;
|
||||
flex: 1 1 100%;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: space($page-margin);
|
||||
background-color: $color-modal-overlay;
|
||||
|
||||
&__modal-wrapper {
|
||||
display: flex;
|
||||
position: relative;
|
||||
flex: 1 1 100%;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
max-width: space($base-modal-max-width);
|
||||
height: 100%;
|
||||
margin: auto;
|
||||
border-width: $border-width-thickness;
|
||||
border-radius: $border-radius-modal;
|
||||
border-color: $color-border;
|
||||
background: $color-bg-shadow;
|
||||
}
|
||||
|
||||
&__modal-header {
|
||||
display: flex;
|
||||
position: relative;
|
||||
flex: 1 1 auto;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
padding: space(16);
|
||||
border-bottom-width: $border-width-thickness;
|
||||
border-top-left-radius: $border-radius-modal;
|
||||
border-top-right-radius: $border-radius-modal;
|
||||
border-bottom-color: $color-border;
|
||||
background-color: $color-bg-overlay;
|
||||
}
|
||||
|
||||
&__modal-body {
|
||||
display: flex;
|
||||
position: relative;
|
||||
flex: 1 1 auto;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
height: 100%;
|
||||
|
||||
&__list {
|
||||
display: block;
|
||||
@ -91,6 +44,7 @@
|
||||
&:hover, &:focus {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&[is_selected] {
|
||||
border-color: rgb(255,255,255);
|
||||
}
|
||||
|
@ -15,4 +15,3 @@
|
||||
@import "./BottomLeft";
|
||||
@import "./Prompt";
|
||||
@import "./ModMenu";
|
||||
@import "./ModDetails";
|
||||
|
@ -53,6 +53,7 @@ namespace recompui {
|
||||
Controls,
|
||||
Graphics,
|
||||
Audio,
|
||||
Mods,
|
||||
Debug,
|
||||
Count
|
||||
};
|
||||
|
@ -1,72 +0,0 @@
|
||||
#include "ElementModDetailsPanel.h"
|
||||
#include "presets.h"
|
||||
#include "librecomp/mods.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
#define MOD_DETAILS_BEM "mod-details"
|
||||
|
||||
namespace recompui {
|
||||
|
||||
static const std::string cls_base = BLOCK(MOD_DETAILS_BEM);
|
||||
static const std::string cls_header = EL(MOD_DETAILS_BEM, "header");
|
||||
static const std::string cls_thumbnail_container = EL(MOD_DETAILS_BEM, "thumbnail-container");
|
||||
static const std::string cls_thumbnail = EL(MOD_DETAILS_BEM, "thumbnail");
|
||||
static const std::string cls_header_details = EL(MOD_DETAILS_BEM, "header-details");
|
||||
static const std::string cls_title = EL(MOD_DETAILS_BEM, "title");
|
||||
static const std::string cls_version = EL(MOD_DETAILS_BEM, "version");
|
||||
static const std::string cls_body = EL(MOD_DETAILS_BEM, "body");
|
||||
static const std::string cls_authors = EL(MOD_DETAILS_BEM, "authors");
|
||||
static const std::string cls_description = EL(MOD_DETAILS_BEM, "description");
|
||||
|
||||
ElementModDetailsPanel::ElementModDetailsPanel(const Rml::String& tag) : Rml::Element(tag)
|
||||
{
|
||||
SetAttribute("recomp-store-element", true);
|
||||
Rml::ElementDocument *doc = GetOwnerDocument();
|
||||
SetClass(cls_base, true);
|
||||
|
||||
{
|
||||
Rml::Element *header_el = add_div_with_class(doc, this, cls_header);
|
||||
{
|
||||
Rml::Element *thumbnail_container_el = add_div_with_class(doc, header_el, cls_thumbnail_container);
|
||||
{
|
||||
Rml::Element *thumbnail_el = add_div_with_class(doc, thumbnail_container_el, cls_thumbnail);
|
||||
} // thumbnail_container_el
|
||||
|
||||
Rml::Element *header_details_el = add_div_with_class(doc, header_el, cls_header_details);
|
||||
{
|
||||
title_el = add_div_with_class(doc, header_details_el, cls_title);
|
||||
version_el = add_div_with_class(doc, header_details_el, cls_version);
|
||||
} // header_details_el
|
||||
}
|
||||
Rml::Element* body_el = add_div_with_class(doc, this, cls_body);
|
||||
{
|
||||
description_el = add_div_with_class(doc, body_el, cls_description);
|
||||
authors_el = add_div_with_class(doc, body_el, cls_authors);
|
||||
} // body_el
|
||||
}
|
||||
}
|
||||
|
||||
ElementModDetailsPanel::~ElementModDetailsPanel()
|
||||
{
|
||||
}
|
||||
|
||||
void ElementModDetailsPanel::SetModDetails(const recomp::mods::ModDetails& details) {
|
||||
cur_details = details;
|
||||
|
||||
title_el->SetInnerRML(cur_details.mod_id);
|
||||
version_el->SetInnerRML(cur_details.version.to_string());
|
||||
|
||||
std::string authors_str = "<i>Authors</i>:";
|
||||
bool first = true;
|
||||
for (const std::string& author : details.authors) {
|
||||
authors_str += (first ? " " : ", ") + author;
|
||||
first = false;
|
||||
}
|
||||
authors_el->SetInnerRML(authors_str);
|
||||
description_el->SetInnerRML("Placeholder description. Some long text to make sure that wrapping is working correctly. Yet more text and so on.");
|
||||
|
||||
DirtyLayout();
|
||||
}
|
||||
|
||||
} // namespace Rml
|
@ -1,24 +0,0 @@
|
||||
#ifndef RECOMPUI_ELEMENT_MOD_DETAILS_PANEL_H
|
||||
#define RECOMPUI_ELEMENT_MOD_DETAILS_PANEL_H
|
||||
|
||||
#include "common.h"
|
||||
#include "librecomp/mods.hpp"
|
||||
|
||||
namespace recompui {
|
||||
|
||||
class ElementModDetailsPanel : public Rml::Element {
|
||||
public:
|
||||
ElementModDetailsPanel(const Rml::String& tag);
|
||||
virtual ~ElementModDetailsPanel();
|
||||
void SetModDetails(const recomp::mods::ModDetails& details);
|
||||
private:
|
||||
recomp::mods::ModDetails cur_details;
|
||||
Rml::Element* thumbnail_el;
|
||||
Rml::Element* title_el;
|
||||
Rml::Element* authors_el;
|
||||
Rml::Element* version_el;
|
||||
Rml::Element* description_el;
|
||||
};
|
||||
|
||||
} // namespace recompui
|
||||
#endif
|
@ -1,5 +1,4 @@
|
||||
#include "ElementModMenu.h"
|
||||
#include "ElementModDetailsPanel.h"
|
||||
#include "presets.h"
|
||||
#include "librecomp/mods.hpp"
|
||||
|
||||
@ -10,9 +9,6 @@
|
||||
namespace recompui {
|
||||
|
||||
static const std::string cls_base = BLOCK(MOD_MENU_BEM);
|
||||
static const std::string cls_modal_wrapper = EL(MOD_MENU_BEM, "modal-wrapper");
|
||||
static const std::string cls_modal_header = EL(MOD_MENU_BEM, "modal-header");
|
||||
static const std::string cls_modal_body = EL(MOD_MENU_BEM, "modal-body");
|
||||
static const std::string cls_list = EL(MOD_MENU_BEM, "list");
|
||||
static const std::string cls_list_scroll = EL(MOD_MENU_BEM, "list-scroll");
|
||||
static const std::string cls_list_entry = EL(MOD_MENU_BEM, "list-entry");
|
||||
@ -30,10 +26,6 @@ void ElementModMenu::ProcessEvent(Rml::Event& event) {
|
||||
// Refresh
|
||||
if (event_element == refresh_button) {
|
||||
RefreshMods();
|
||||
}
|
||||
// Close
|
||||
else if (event_element == close_button) {
|
||||
|
||||
}
|
||||
break;
|
||||
case Rml::EventId::Focus:
|
||||
@ -41,7 +33,7 @@ void ElementModMenu::ProcessEvent(Rml::Event& event) {
|
||||
size_t mod_index;
|
||||
Rml::Variant *val = event_element->GetAttribute("mod_index");
|
||||
if (val->GetInto(mod_index) && mod_index < mod_details.size()) {
|
||||
details_el->SetModDetails(mod_details[mod_index]);
|
||||
mod_details_panel->set_mod_details(mod_details[mod_index]);
|
||||
}
|
||||
if (active_list_entry_el != nullptr) {
|
||||
active_list_entry_el->RemoveAttribute("is_selected");
|
||||
@ -49,6 +41,8 @@ void ElementModMenu::ProcessEvent(Rml::Event& event) {
|
||||
event_element->SetAttribute("is_selected", true);
|
||||
active_list_entry_el = event_element;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -111,7 +105,7 @@ void ElementModMenu::RefreshMods() {
|
||||
recomp::mods::scan_mods();
|
||||
mod_details = recomp::mods::get_mod_details(game_mod_id);
|
||||
|
||||
details_el->SetModDetails(mod_details[0]);
|
||||
mod_details_panel->set_mod_details(mod_details[0]);
|
||||
|
||||
CreateModList();
|
||||
}
|
||||
@ -122,34 +116,23 @@ ElementModMenu::ElementModMenu(const Rml::String& tag) : Rml::Element(tag) {
|
||||
Rml::ElementDocument *doc = GetOwnerDocument();
|
||||
SetClass(cls_base, true);
|
||||
|
||||
Rml::Element *body_el = add_div_with_class(doc, this, "config__hz-wrapper");
|
||||
{
|
||||
Rml::Element *modal_wrapper_el = add_div_with_class(doc, this, cls_modal_wrapper);
|
||||
list_el = add_div_with_class(doc, body_el, cls_list);
|
||||
{
|
||||
Rml::Element *header_el = add_div_with_class(doc, modal_wrapper_el, cls_modal_header);
|
||||
{
|
||||
refresh_button = add_button(doc, header_el, "Refresh", ButtonVariant::Primary);
|
||||
refresh_button->AddEventListener(Rml::EventId::Click, this, false);
|
||||
refresh_button->SetId("refresh-button");
|
||||
close_button = add_icon_button(doc, header_el, "icons/X.svg", ButtonVariant::Tertiary);
|
||||
close_button->AddEventListener(Rml::EventId::Click, this, false);
|
||||
close_button->SetId("close-button");
|
||||
list_el_scroll = add_div_with_class(doc, list_el, cls_list_scroll);
|
||||
} // list_el
|
||||
|
||||
refresh_button->SetProperty("nav-right", "#" + close_button->GetId());
|
||||
close_button->SetProperty("nav-left", "#" + refresh_button->GetId());
|
||||
} // header_el
|
||||
recompui::Element body_el_compat(body_el);
|
||||
mod_details_panel = std::make_unique<ModDetailsPanel>(&body_el_compat);
|
||||
} // body_el
|
||||
|
||||
Rml::Element *body_el = add_div_with_class(doc, modal_wrapper_el, cls_modal_body);
|
||||
{
|
||||
list_el = add_div_with_class(doc, body_el, cls_list);
|
||||
{
|
||||
list_el_scroll = add_div_with_class(doc, list_el, cls_list_scroll);
|
||||
} // list_el
|
||||
|
||||
details_el =
|
||||
static_cast<ElementModDetailsPanel*>(body_el->AppendChild(doc->CreateElement("recomp-mod-details-panel")));
|
||||
} // body_el
|
||||
} // modal_wrapper_el
|
||||
}
|
||||
Rml::Element *footer_el = add_div_with_class(doc, this, "config__footer");
|
||||
{
|
||||
refresh_button = add_button(doc, footer_el, "Refresh", ButtonVariant::Primary);
|
||||
refresh_button->AddEventListener(Rml::EventId::Click, this, false);
|
||||
refresh_button->SetId("refresh-button");
|
||||
} // footer_el
|
||||
|
||||
RefreshMods();
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
#include "common.h"
|
||||
#include "librecomp/mods.hpp"
|
||||
#include "ElementModDetailsPanel.h"
|
||||
#include "ui_mod_details_panel.h"
|
||||
|
||||
namespace recompui {
|
||||
|
||||
@ -17,10 +17,9 @@ private:
|
||||
void CreateModList();
|
||||
Rml::ElementPtr CreateModListEntry(const recomp::mods::ModDetails& details, size_t index);
|
||||
Rml::Element *refresh_button;
|
||||
Rml::Element *close_button;
|
||||
Rml::Element *list_el; // The root mod list element.
|
||||
Rml::Element *list_el_scroll; // The scroll within the root mod list element.
|
||||
ElementModDetailsPanel *details_el; // The details panel.
|
||||
std::unique_ptr<ModDetailsPanel> mod_details_panel;
|
||||
Rml::Element *active_list_entry_el = nullptr;
|
||||
std::vector<recomp::mods::ModDetails> mod_details{};
|
||||
std::string game_mod_id;
|
||||
|
69
src/ui/elements/ui_button.cpp
Normal file
69
src/ui/elements/ui_button.cpp
Normal file
@ -0,0 +1,69 @@
|
||||
#include "ui_button.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace recompui {
|
||||
|
||||
Button::Button(const std::string &text, ButtonStyle style, Element *parent) : Element(parent, Events(EventType::Click, EventType::Hover), "button") {
|
||||
this->style = style;
|
||||
|
||||
set_text(text);
|
||||
set_display(Display::Block);
|
||||
set_padding(23.0f);
|
||||
set_border_width(1.1f);
|
||||
set_border_radius(12.0f);
|
||||
set_font_size(28.0f);
|
||||
set_letter_spacing(3.08f);
|
||||
set_line_height(28.0f);
|
||||
set_font_style(FontStyle::Normal);
|
||||
set_font_weight(700);
|
||||
set_cursor(Cursor::Pointer);
|
||||
|
||||
// transition: color 0.05s linear-in-out, background-color 0.05s linear-in-out;
|
||||
|
||||
update_properties();
|
||||
}
|
||||
|
||||
void Button::update_properties() {
|
||||
uint8_t border_opacity = hovered ? 255 : 204;
|
||||
uint8_t background_opacity = hovered ? 76 : 13;
|
||||
set_color(hovered ? Color{ 242, 242, 242, 255 } : Color{ 204, 204, 204, 255 });
|
||||
|
||||
switch (style) {
|
||||
case ButtonStyle::Primary: {
|
||||
set_border_color({ 185, 125, 242, border_opacity });
|
||||
set_background_color({ 185, 125, 242, background_opacity });
|
||||
break;
|
||||
}
|
||||
case ButtonStyle::Secondary: {
|
||||
set_border_color({ 23, 214, 232, border_opacity });
|
||||
set_background_color({ 23, 214, 232, background_opacity });
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(false && "Unknown button style.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Button::process_event(const Event &e) {
|
||||
switch (e.type) {
|
||||
case EventType::Click:
|
||||
for (const auto &function : pressed_callbacks) {
|
||||
function();
|
||||
}
|
||||
break;
|
||||
case EventType::Hover:
|
||||
hovered = e.hover.active;
|
||||
update_properties();
|
||||
break;
|
||||
default:
|
||||
assert(false && "Unknown event type.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Button::add_pressed_callback(std::function<void()> callback) {
|
||||
pressed_callbacks.emplace_back(callback);
|
||||
}
|
||||
};
|
28
src/ui/elements/ui_button.h
Normal file
28
src/ui/elements/ui_button.h
Normal file
@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include "ui_element.h"
|
||||
|
||||
namespace recompui {
|
||||
|
||||
enum class ButtonStyle {
|
||||
Primary,
|
||||
Secondary
|
||||
};
|
||||
|
||||
class Button : public Element {
|
||||
protected:
|
||||
ButtonStyle style = ButtonStyle::Primary;
|
||||
std::unique_ptr<Element> floater;
|
||||
std::list<std::function<void()>> pressed_callbacks;
|
||||
bool hovered = false;
|
||||
|
||||
void update_properties();
|
||||
|
||||
// Element overrides.
|
||||
virtual void process_event(const Event &e) override;
|
||||
public:
|
||||
Button(const std::string &text, ButtonStyle style, Element *parent);
|
||||
void add_pressed_callback(std::function<void()> callback);
|
||||
};
|
||||
|
||||
} // namespace recompui
|
14
src/ui/elements/ui_container.cpp
Normal file
14
src/ui/elements/ui_container.cpp
Normal file
@ -0,0 +1,14 @@
|
||||
#include "ui_container.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace recompui {
|
||||
|
||||
Container::Container(FlexDirection direction, JustifyContent justify_content, Element *parent) : Element(parent) {
|
||||
set_display(Display::Flex);
|
||||
set_flex(1.0f, 1.0f);
|
||||
set_flex_direction(direction);
|
||||
set_justify_content(justify_content);
|
||||
}
|
||||
|
||||
};
|
12
src/ui/elements/ui_container.h
Normal file
12
src/ui/elements/ui_container.h
Normal file
@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "ui_element.h"
|
||||
|
||||
namespace recompui {
|
||||
|
||||
class Container : public Element {
|
||||
public:
|
||||
Container(FlexDirection direction, JustifyContent justify_content, Element *parent);
|
||||
};
|
||||
|
||||
} // namespace recompui
|
415
src/ui/elements/ui_element.cpp
Normal file
415
src/ui/elements/ui_element.cpp
Normal file
@ -0,0 +1,415 @@
|
||||
#include "ui_element.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace recompui {
|
||||
|
||||
static Rml::Unit to_rml(Unit unit) {
|
||||
switch (unit) {
|
||||
case Unit::Float:
|
||||
return Rml::Unit::NUMBER;
|
||||
case Unit::Dp:
|
||||
return Rml::Unit::DP;
|
||||
case Unit::Percent:
|
||||
return Rml::Unit::PERCENT;
|
||||
default:
|
||||
return Rml::Unit::UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
static Rml::Style::Overflow to_rml(Overflow overflow) {
|
||||
switch (overflow) {
|
||||
case Overflow::Visible:
|
||||
return Rml::Style::Overflow::Visible;
|
||||
case Overflow::Hidden:
|
||||
return Rml::Style::Overflow::Hidden;
|
||||
case Overflow::Auto:
|
||||
return Rml::Style::Overflow::Auto;
|
||||
case Overflow::Scroll:
|
||||
return Rml::Style::Overflow::Scroll;
|
||||
default:
|
||||
assert(false && "Unknown overflow.");
|
||||
return Rml::Style::Overflow::Visible;
|
||||
}
|
||||
}
|
||||
|
||||
Element::Element(Rml::Element *base) {
|
||||
assert(base != nullptr);
|
||||
|
||||
this->base = base;
|
||||
this->parent = nullptr;
|
||||
this->owner = false;
|
||||
}
|
||||
|
||||
Element::Element(Element *parent, uint32_t events_enabled, Rml::String base_class) {
|
||||
assert(parent != nullptr);
|
||||
|
||||
this->parent = parent;
|
||||
this->owner = true;
|
||||
|
||||
base = parent->base->AppendChild(parent->base->GetOwnerDocument()->CreateElement(base_class));
|
||||
|
||||
register_event_listeners(events_enabled);
|
||||
}
|
||||
|
||||
Element::~Element() {
|
||||
if (owner) {
|
||||
base->GetParentNode()->RemoveChild(base);
|
||||
}
|
||||
}
|
||||
|
||||
void Element::set_property(Rml::PropertyId property_id, const Rml::Property &property, Animation animation) {
|
||||
assert(base != nullptr);
|
||||
|
||||
if (animation.type == AnimationType::None) {
|
||||
base->SetProperty(property_id, property);
|
||||
}
|
||||
else {
|
||||
const Rml::String property_name = Rml::StyleSheetSpecification::GetPropertyName(property_id);
|
||||
base->Animate(property_name, property, animation.duration);
|
||||
}
|
||||
}
|
||||
|
||||
void Element::register_event_listeners(uint32_t events_enabled) {
|
||||
assert(base != nullptr);
|
||||
|
||||
if (events_enabled & Events(EventType::Click)) {
|
||||
base->AddEventListener(Rml::EventId::Mousedown, this);
|
||||
}
|
||||
|
||||
if (events_enabled & Events(EventType::Focus)) {
|
||||
base->AddEventListener(Rml::EventId::Focus, this);
|
||||
base->AddEventListener(Rml::EventId::Blur, this);
|
||||
}
|
||||
|
||||
if (events_enabled & Events(EventType::Hover)) {
|
||||
base->AddEventListener(Rml::EventId::Mouseover, this);
|
||||
base->AddEventListener(Rml::EventId::Mouseout, this);
|
||||
}
|
||||
}
|
||||
|
||||
void Element::ProcessEvent(Rml::Event &event) {
|
||||
// Events that are processed during any phase.
|
||||
switch (event.GetId()) {
|
||||
case Rml::EventId::Mousedown:
|
||||
process_event(Event::click_event(event.GetParameter("mouse_x", 0.0f), event.GetParameter("mouse_y", 0.0f)));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Events that are only processed during the Target phase.
|
||||
if (event.GetPhase() == Rml::EventPhase::Target) {
|
||||
switch (event.GetId()) {
|
||||
case Rml::EventId::Mouseover:
|
||||
process_event(Event::hover_event(true));
|
||||
break;
|
||||
case Rml::EventId::Mouseout:
|
||||
process_event(Event::hover_event(false));
|
||||
break;
|
||||
case Rml::EventId::Focus:
|
||||
process_event(Event::focus_event(true));
|
||||
break;
|
||||
case Rml::EventId::Blur:
|
||||
process_event(Event::focus_event(false));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Element::process_event(const Event &) {
|
||||
// Does nothing by default.
|
||||
}
|
||||
|
||||
void Element::set_position(Position position) {
|
||||
switch (position) {
|
||||
case Position::Absolute:
|
||||
set_property(Rml::PropertyId::Position, Rml::Style::Position::Absolute);
|
||||
break;
|
||||
case Position::Relative:
|
||||
set_property(Rml::PropertyId::Position, Rml::Style::Position::Relative);
|
||||
break;
|
||||
default:
|
||||
assert(false && "Unknown position.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Element::set_left(float left, Unit unit, Animation animation) {
|
||||
set_property(Rml::PropertyId::Left, Rml::Property(left, to_rml(unit)), animation);
|
||||
}
|
||||
|
||||
void Element::set_top(float top, Unit unit, Animation animation) {
|
||||
set_property(Rml::PropertyId::Top, Rml::Property(top, to_rml(unit)), animation);
|
||||
}
|
||||
|
||||
void Element::set_right(float right, Unit unit, Animation animation) {
|
||||
set_property(Rml::PropertyId::Right, Rml::Property(right, to_rml(unit)), animation);
|
||||
}
|
||||
|
||||
void Element::set_bottom(float bottom, Unit unit, Animation animation) {
|
||||
set_property(Rml::PropertyId::Bottom, Rml::Property(bottom, to_rml(unit)), animation);
|
||||
}
|
||||
|
||||
void Element::set_width(float width, Unit unit, Animation animation) {
|
||||
set_property(Rml::PropertyId::Width, Rml::Property(width, to_rml(unit)), animation);
|
||||
}
|
||||
|
||||
void Element::set_width_auto() {
|
||||
set_property(Rml::PropertyId::Width, Rml::Property(Rml::Style::FlexBasis::Type::Auto, Rml::Unit::KEYWORD));
|
||||
}
|
||||
|
||||
void Element::set_height(float height, Unit unit, Animation animation) {
|
||||
set_property(Rml::PropertyId::Height, Rml::Property(height, to_rml(unit)), animation);
|
||||
}
|
||||
|
||||
void Element::set_height_auto() {
|
||||
set_property(Rml::PropertyId::Height, Rml::Property(Rml::Style::FlexBasis::Type::Auto, Rml::Unit::KEYWORD));
|
||||
}
|
||||
|
||||
void Element::set_padding(float padding, Unit unit, Animation animation) {
|
||||
set_property(Rml::PropertyId::PaddingLeft, Rml::Property(padding, to_rml(unit)), animation);
|
||||
set_property(Rml::PropertyId::PaddingTop, Rml::Property(padding, to_rml(unit)), animation);
|
||||
set_property(Rml::PropertyId::PaddingRight, Rml::Property(padding, to_rml(unit)), animation);
|
||||
set_property(Rml::PropertyId::PaddingBottom, Rml::Property(padding, to_rml(unit)), animation);
|
||||
}
|
||||
|
||||
void Element::set_padding_left(float padding, Unit unit, Animation animation) {
|
||||
set_property(Rml::PropertyId::PaddingLeft, Rml::Property(padding, to_rml(unit)), animation);
|
||||
}
|
||||
|
||||
void Element::set_padding_top(float padding, Unit unit, Animation animation) {
|
||||
set_property(Rml::PropertyId::PaddingTop, Rml::Property(padding, to_rml(unit)), animation);
|
||||
}
|
||||
|
||||
void Element::set_padding_right(float padding, Unit unit, Animation animation) {
|
||||
set_property(Rml::PropertyId::PaddingRight, Rml::Property(padding, to_rml(unit)), animation);
|
||||
}
|
||||
|
||||
void Element::set_padding_bottom(float padding, Unit unit, Animation animation) {
|
||||
set_property(Rml::PropertyId::PaddingBottom, Rml::Property(padding, to_rml(unit)), animation);
|
||||
}
|
||||
|
||||
void Element::set_margin(float margin, Unit unit, Animation animation) {
|
||||
set_property(Rml::PropertyId::MarginLeft, Rml::Property(margin, to_rml(unit)), animation);
|
||||
set_property(Rml::PropertyId::MarginTop, Rml::Property(margin, to_rml(unit)), animation);
|
||||
set_property(Rml::PropertyId::MarginRight, Rml::Property(margin, to_rml(unit)), animation);
|
||||
set_property(Rml::PropertyId::MarginBottom, Rml::Property(margin, to_rml(unit)), animation);
|
||||
}
|
||||
|
||||
void Element::set_margin_left(float margin, Unit unit, Animation animation) {
|
||||
set_property(Rml::PropertyId::MarginLeft, Rml::Property(margin, to_rml(unit)), animation);
|
||||
}
|
||||
|
||||
void Element::set_margin_top(float margin, Unit unit, Animation animation) {
|
||||
set_property(Rml::PropertyId::MarginTop, Rml::Property(margin, to_rml(unit)), animation);
|
||||
}
|
||||
|
||||
void Element::set_margin_right(float margin, Unit unit, Animation animation) {
|
||||
set_property(Rml::PropertyId::MarginRight, Rml::Property(margin, to_rml(unit)), animation);
|
||||
}
|
||||
|
||||
void Element::set_margin_bottom(float margin, Unit unit, Animation animation) {
|
||||
set_property(Rml::PropertyId::MarginBottom, Rml::Property(margin, to_rml(unit)), animation);
|
||||
}
|
||||
|
||||
void Element::set_border_width(float width, Unit unit, Animation animation) {
|
||||
Rml::Property property(width, to_rml(unit));
|
||||
set_property(Rml::PropertyId::BorderTopWidth, property, animation);
|
||||
set_property(Rml::PropertyId::BorderBottomWidth, property, animation);
|
||||
set_property(Rml::PropertyId::BorderLeftWidth, property, animation);
|
||||
set_property(Rml::PropertyId::BorderRightWidth, property, animation);
|
||||
}
|
||||
|
||||
void Element::set_border_radius(float radius, Unit unit, Animation animation) {
|
||||
Rml::Property property(radius, to_rml(unit));
|
||||
set_property(Rml::PropertyId::BorderTopLeftRadius, property, animation);
|
||||
set_property(Rml::PropertyId::BorderTopRightRadius, property, animation);
|
||||
set_property(Rml::PropertyId::BorderBottomLeftRadius, property, animation);
|
||||
set_property(Rml::PropertyId::BorderBottomRightRadius, property, animation);
|
||||
}
|
||||
|
||||
void Element::set_border_top_left_radius(float radius, Unit unit, Animation animation) {
|
||||
set_property(Rml::PropertyId::BorderTopLeftRadius, Rml::Property(radius, to_rml(unit)), animation);
|
||||
}
|
||||
|
||||
void Element::set_border_top_right_radius(float radius, Unit unit, Animation animation) {
|
||||
set_property(Rml::PropertyId::BorderTopRightRadius, Rml::Property(radius, to_rml(unit)), animation);
|
||||
}
|
||||
|
||||
void Element::set_border_bottom_left_radius(float radius, Unit unit, Animation animation) {
|
||||
set_property(Rml::PropertyId::BorderBottomLeftRadius, Rml::Property(radius, to_rml(unit)), animation);
|
||||
}
|
||||
|
||||
void Element::set_border_bottom_right_radius(float radius, Unit unit, Animation animation) {
|
||||
set_property(Rml::PropertyId::BorderBottomRightRadius, Rml::Property(radius, to_rml(unit)), animation);
|
||||
}
|
||||
|
||||
void Element::set_background_color(const Color &color, Animation animation) {
|
||||
Rml::Property property(Rml::Colourb(color.r, color.g, color.b, color.a), Rml::Unit::COLOUR);
|
||||
set_property(Rml::PropertyId::BackgroundColor, property, animation);
|
||||
}
|
||||
|
||||
void Element::set_border_color(const Color &color, Animation animation) {
|
||||
Rml::Property property(Rml::Colourb(color.r, color.g, color.b, color.a), Rml::Unit::COLOUR);
|
||||
set_property(Rml::PropertyId::BorderTopColor, property, animation);
|
||||
set_property(Rml::PropertyId::BorderBottomColor, property, animation);
|
||||
set_property(Rml::PropertyId::BorderLeftColor, property, animation);
|
||||
set_property(Rml::PropertyId::BorderRightColor, property, animation);
|
||||
}
|
||||
|
||||
void Element::set_color(const Color &color, Animation animation) {
|
||||
Rml::Property property(Rml::Colourb(color.r, color.g, color.b, color.a), Rml::Unit::COLOUR);
|
||||
set_property(Rml::PropertyId::Color, property, animation);
|
||||
}
|
||||
|
||||
void Element::set_cursor(Cursor cursor) {
|
||||
switch (cursor) {
|
||||
case Cursor::None:
|
||||
assert(false && "Unimplemented.");
|
||||
break;
|
||||
case Cursor::Pointer:
|
||||
set_property(Rml::PropertyId::Cursor, Rml::Property("pointer", Rml::Unit::STRING));
|
||||
break;
|
||||
default:
|
||||
assert(false && "Unknown cursor.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Element::set_opacity(float opacity, Animation animation) {
|
||||
set_property(Rml::PropertyId::Opacity, Rml::Property(opacity, Rml::Unit::NUMBER), animation);
|
||||
}
|
||||
|
||||
void Element::set_display(Display display) {
|
||||
switch (display) {
|
||||
case Display::Block:
|
||||
set_property(Rml::PropertyId::Display, Rml::Style::Display::Block);
|
||||
break;
|
||||
case Display::Flex:
|
||||
set_property(Rml::PropertyId::Display, Rml::Style::Display::Flex);
|
||||
break;
|
||||
default:
|
||||
assert(false && "Unknown display.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Element::set_justify_content(JustifyContent justify_content) {
|
||||
switch (justify_content) {
|
||||
case JustifyContent::FlexStart:
|
||||
set_property(Rml::PropertyId::JustifyContent, Rml::Style::JustifyContent::FlexStart);
|
||||
break;
|
||||
case JustifyContent::FlexEnd:
|
||||
set_property(Rml::PropertyId::JustifyContent, Rml::Style::JustifyContent::FlexEnd);
|
||||
break;
|
||||
case JustifyContent::Center:
|
||||
set_property(Rml::PropertyId::JustifyContent, Rml::Style::JustifyContent::Center);
|
||||
break;
|
||||
case JustifyContent::SpaceBetween:
|
||||
set_property(Rml::PropertyId::JustifyContent, Rml::Style::JustifyContent::SpaceBetween);
|
||||
break;
|
||||
case JustifyContent::SpaceAround:
|
||||
set_property(Rml::PropertyId::JustifyContent, Rml::Style::JustifyContent::SpaceAround);
|
||||
break;
|
||||
case JustifyContent::SpaceEvenly:
|
||||
set_property(Rml::PropertyId::JustifyContent, Rml::Style::JustifyContent::SpaceEvenly);
|
||||
break;
|
||||
default:
|
||||
assert(false && "Unknown justify content.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Element::set_flex_grow(float grow, Animation animation) {
|
||||
set_property(Rml::PropertyId::FlexGrow, Rml::Property(grow, Rml::Unit::NUMBER), animation);
|
||||
}
|
||||
|
||||
void Element::set_flex_shrink(float shrink, Animation animation) {
|
||||
set_property(Rml::PropertyId::FlexShrink, Rml::Property(shrink, Rml::Unit::NUMBER), animation);
|
||||
}
|
||||
|
||||
void Element::set_flex_basis_auto() {
|
||||
set_property(Rml::PropertyId::FlexBasis, Rml::Property(Rml::Style::FlexBasis::Type::Auto, Rml::Unit::KEYWORD));
|
||||
}
|
||||
|
||||
void Element::set_flex_basis_percentage(float basis, Animation animation) {
|
||||
set_property(Rml::PropertyId::FlexBasis, Rml::Property(basis, Rml::Unit::PERCENT), animation);
|
||||
}
|
||||
|
||||
void Element::set_flex(float grow, float shrink, Animation animation) {
|
||||
set_flex_grow(grow, animation);
|
||||
set_flex_shrink(shrink, animation);
|
||||
set_flex_basis_auto();
|
||||
}
|
||||
|
||||
void Element::set_flex(float grow, float shrink, float basis_percentage, Animation animation) {
|
||||
set_flex_grow(grow, animation);
|
||||
set_flex_shrink(shrink, animation);
|
||||
set_flex_basis_percentage(basis_percentage, animation);
|
||||
}
|
||||
|
||||
void Element::set_flex_direction(FlexDirection flex_direction) {
|
||||
switch (flex_direction) {
|
||||
case FlexDirection::Row:
|
||||
set_property(Rml::PropertyId::FlexDirection, Rml::Style::FlexDirection::Row);
|
||||
break;
|
||||
case FlexDirection::Column:
|
||||
set_property(Rml::PropertyId::FlexDirection, Rml::Style::FlexDirection::Column);
|
||||
break;
|
||||
default:
|
||||
assert(false && "Unknown flex direction.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Element::set_overflow(Overflow overflow) {
|
||||
set_property(Rml::PropertyId::OverflowX, to_rml(overflow));
|
||||
set_property(Rml::PropertyId::OverflowY, to_rml(overflow));
|
||||
}
|
||||
|
||||
void Element::set_overflow_x(Overflow overflow) {
|
||||
set_property(Rml::PropertyId::OverflowX, to_rml(overflow));
|
||||
}
|
||||
|
||||
void Element::set_overflow_y(Overflow overflow) {
|
||||
set_property(Rml::PropertyId::OverflowY, to_rml(overflow));
|
||||
}
|
||||
|
||||
void Element::set_font_size(float size, Unit unit, Animation animation) {
|
||||
set_property(Rml::PropertyId::FontSize, Rml::Property(size, to_rml(unit)), animation);
|
||||
}
|
||||
|
||||
void Element::set_letter_spacing(float spacing, Unit unit, Animation animation) {
|
||||
set_property(Rml::PropertyId::LetterSpacing, Rml::Property(spacing, to_rml(unit)), animation);
|
||||
}
|
||||
|
||||
void Element::set_line_height(float height, Unit unit, Animation animation) {
|
||||
set_property(Rml::PropertyId::LineHeight, Rml::Property(height, to_rml(unit)), animation);
|
||||
}
|
||||
|
||||
void Element::set_font_style(FontStyle style) {
|
||||
switch (style) {
|
||||
case FontStyle::Normal:
|
||||
set_property(Rml::PropertyId::FontStyle, Rml::Style::FontStyle::Normal);
|
||||
break;
|
||||
case FontStyle::Italic:
|
||||
set_property(Rml::PropertyId::FontStyle, Rml::Style::FontStyle::Italic);
|
||||
break;
|
||||
default:
|
||||
assert(false && "Unknown font style.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Element::set_font_weight(uint32_t weight, Animation animation) {
|
||||
set_property(Rml::PropertyId::FontWeight, Rml::Style::FontWeight(weight), animation);
|
||||
}
|
||||
|
||||
void Element::set_text(const std::string &text) {
|
||||
base->SetInnerRML(text);
|
||||
}
|
||||
|
||||
};
|
211
src/ui/elements/ui_element.h
Normal file
211
src/ui/elements/ui_element.h
Normal file
@ -0,0 +1,211 @@
|
||||
#pragma once
|
||||
|
||||
#include "RmlUi/Core.h"
|
||||
|
||||
namespace recompui {
|
||||
|
||||
struct Color {
|
||||
uint8_t r = 255;
|
||||
uint8_t g = 255;
|
||||
uint8_t b = 255;
|
||||
uint8_t a = 255;
|
||||
};
|
||||
|
||||
enum class Cursor {
|
||||
None,
|
||||
Pointer
|
||||
};
|
||||
|
||||
enum class EventType {
|
||||
None,
|
||||
Click,
|
||||
Focus,
|
||||
Hover,
|
||||
Count
|
||||
};
|
||||
|
||||
template <typename Enum, typename = std::enable_if_t<std::is_enum_v<Enum>>>
|
||||
constexpr uint32_t Events(Enum first) {
|
||||
return 1u << static_cast<uint32_t>(first);
|
||||
}
|
||||
|
||||
template <typename Enum, typename... Enums, typename = std::enable_if_t<std::is_enum_v<Enum>>>
|
||||
constexpr uint32_t Events(Enum first, Enums... rest) {
|
||||
return Events(first) | Events(rest...);
|
||||
}
|
||||
|
||||
struct Event {
|
||||
struct Mouse {
|
||||
float x;
|
||||
float y;
|
||||
};
|
||||
|
||||
EventType type;
|
||||
|
||||
union {
|
||||
struct {
|
||||
Mouse mouse;
|
||||
} click;
|
||||
|
||||
struct {
|
||||
bool active;
|
||||
} focus;
|
||||
|
||||
struct {
|
||||
bool active;
|
||||
} hover;
|
||||
};
|
||||
|
||||
static Event click_event(float x, float y) {
|
||||
Event e = {};
|
||||
e.type = EventType::Click;
|
||||
e.click.mouse.x = x;
|
||||
e.click.mouse.y = y;
|
||||
return e;
|
||||
}
|
||||
|
||||
static Event focus_event(bool active) {
|
||||
Event e = {};
|
||||
e.type = EventType::Focus;
|
||||
e.focus.active = active;
|
||||
return e;
|
||||
}
|
||||
|
||||
static Event hover_event(bool active) {
|
||||
Event e = {};
|
||||
e.type = EventType::Hover;
|
||||
e.focus.active = active;
|
||||
return e;
|
||||
}
|
||||
};
|
||||
|
||||
enum class Display {
|
||||
Block,
|
||||
Flex
|
||||
};
|
||||
|
||||
enum class Position {
|
||||
Absolute,
|
||||
Relative
|
||||
};
|
||||
|
||||
enum class JustifyContent {
|
||||
FlexStart,
|
||||
FlexEnd,
|
||||
Center,
|
||||
SpaceBetween,
|
||||
SpaceAround,
|
||||
SpaceEvenly
|
||||
};
|
||||
|
||||
enum class FlexDirection {
|
||||
Row,
|
||||
Column
|
||||
};
|
||||
|
||||
enum class Overflow {
|
||||
Visible,
|
||||
Hidden,
|
||||
Auto,
|
||||
Scroll
|
||||
};
|
||||
|
||||
enum class Unit {
|
||||
Float,
|
||||
Dp,
|
||||
Percent
|
||||
};
|
||||
|
||||
enum class AnimationType {
|
||||
None,
|
||||
Tween
|
||||
};
|
||||
|
||||
enum class FontStyle {
|
||||
Normal,
|
||||
Italic
|
||||
};
|
||||
|
||||
struct Animation {
|
||||
AnimationType type = AnimationType::None;
|
||||
float duration = 0.0f;
|
||||
|
||||
static Animation tween(float duration) {
|
||||
Animation a;
|
||||
a.type = AnimationType::Tween;
|
||||
a.duration = duration;
|
||||
return a;
|
||||
}
|
||||
};
|
||||
|
||||
class Element : public Rml::EventListener {
|
||||
private:
|
||||
void set_property(Rml::PropertyId property_id, const Rml::Property &property, Animation animation = Animation());
|
||||
void register_event_listeners(uint32_t events_enabled);
|
||||
|
||||
// Rml::EventListener overrides.
|
||||
virtual void ProcessEvent(Rml::Event &event) override;
|
||||
protected:
|
||||
Element *parent;
|
||||
Rml::Element *base;
|
||||
bool owner;
|
||||
|
||||
virtual void process_event(const Event &e);
|
||||
public:
|
||||
// Used for backwards compatibility with legacy UI elements.
|
||||
Element(Rml::Element *base);
|
||||
|
||||
// Used to actually construct elements.
|
||||
Element(Element *parent, uint32_t events_enabled = 0, Rml::String base_class = "div");
|
||||
virtual ~Element();
|
||||
void set_position(Position position);
|
||||
void set_left(float left, Unit unit = Unit::Dp, Animation animation = Animation());
|
||||
void set_top(float top, Unit unit = Unit::Dp, Animation animation = Animation());
|
||||
void set_right(float right, Unit unit = Unit::Dp, Animation animation = Animation());
|
||||
void set_bottom(float bottom, Unit unit = Unit::Dp, Animation animation = Animation());
|
||||
void set_width(float width, Unit unit = Unit::Dp, Animation animation = Animation());
|
||||
void set_width_auto();
|
||||
void set_height(float height, Unit unit = Unit::Dp, Animation animation = Animation());
|
||||
void set_height_auto();
|
||||
void set_padding(float padding, Unit unit = Unit::Dp, Animation animation = Animation());
|
||||
void set_padding_left(float padding, Unit unit = Unit::Dp, Animation animation = Animation());
|
||||
void set_padding_top(float padding, Unit unit = Unit::Dp, Animation animation = Animation());
|
||||
void set_padding_right(float padding, Unit unit = Unit::Dp, Animation animation = Animation());
|
||||
void set_padding_bottom(float padding, Unit unit = Unit::Dp, Animation animation = Animation());
|
||||
void set_margin(float margin, Unit unit = Unit::Dp, Animation animation = Animation());
|
||||
void set_margin_left(float margin, Unit unit = Unit::Dp, Animation animation = Animation());
|
||||
void set_margin_top(float margin, Unit unit = Unit::Dp, Animation animation = Animation());
|
||||
void set_margin_right(float margin, Unit unit = Unit::Dp, Animation animation = Animation());
|
||||
void set_margin_bottom(float margin, Unit unit = Unit::Dp, Animation animation = Animation());
|
||||
void set_border_width(float width, Unit unit = Unit::Dp, Animation animation = Animation());
|
||||
void set_border_radius(float radius, Unit unit = Unit::Dp, Animation animation = Animation());
|
||||
void set_border_top_left_radius(float radius, Unit unit = Unit::Dp, Animation animation = Animation());
|
||||
void set_border_top_right_radius(float radius, Unit unit = Unit::Dp, Animation animation = Animation());
|
||||
void set_border_bottom_left_radius(float radius, Unit unit = Unit::Dp, Animation animation = Animation());
|
||||
void set_border_bottom_right_radius(float radius, Unit unit = Unit::Dp, Animation animation = Animation());
|
||||
void set_background_color(const Color &color, Animation animation = Animation());
|
||||
void set_border_color(const Color &color, Animation animation = Animation());
|
||||
void set_color(const Color &color, Animation animation = Animation());
|
||||
void set_cursor(Cursor cursor);
|
||||
void set_opacity(float opacity, Animation animation = Animation());
|
||||
void set_display(Display display);
|
||||
void set_justify_content(JustifyContent justify_content);
|
||||
void set_flex_grow(float grow, Animation animation = Animation());
|
||||
void set_flex_shrink(float shrink, Animation animation = Animation());
|
||||
void set_flex_basis_auto();
|
||||
void set_flex_basis_percentage(float basis_percentage, Animation animation = Animation());
|
||||
void set_flex(float grow, float shrink, Animation animation = Animation());
|
||||
void set_flex(float grow, float shrink, float basis_percentage, Animation animation = Animation());
|
||||
void set_flex_direction(FlexDirection flex_direction);
|
||||
void set_overflow(Overflow overflow);
|
||||
void set_overflow_x(Overflow overflow);
|
||||
void set_overflow_y(Overflow overflow);
|
||||
void set_font_size(float size, Unit unit = Unit::Dp, Animation animation = Animation());
|
||||
void set_letter_spacing(float spacing, Unit unit = Unit::Dp, Animation animation = Animation());
|
||||
void set_line_height(float height, Unit unit = Unit::Dp, Animation animation = Animation());
|
||||
void set_font_style(FontStyle style);
|
||||
void set_font_weight(uint32_t weight, Animation animation = Animation());
|
||||
void set_text(const std::string &text);
|
||||
};
|
||||
|
||||
} // namespace recompui
|
11
src/ui/elements/ui_image.cpp
Normal file
11
src/ui/elements/ui_image.cpp
Normal file
@ -0,0 +1,11 @@
|
||||
#include "ui_image.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace recompui {
|
||||
|
||||
Image::Image(Element *parent) : Element(parent) {
|
||||
|
||||
}
|
||||
|
||||
};
|
12
src/ui/elements/ui_image.h
Normal file
12
src/ui/elements/ui_image.h
Normal file
@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "ui_element.h"
|
||||
|
||||
namespace recompui {
|
||||
|
||||
class Image : public Element {
|
||||
public:
|
||||
Image(Element *parent);
|
||||
};
|
||||
|
||||
} // namespace recompui
|
29
src/ui/elements/ui_label.cpp
Normal file
29
src/ui/elements/ui_label.cpp
Normal file
@ -0,0 +1,29 @@
|
||||
#include "ui_label.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace recompui {
|
||||
|
||||
Label::Label(LabelStyle label_style, Element *parent) : Element(parent) {
|
||||
switch (label_style) {
|
||||
case LabelStyle::Normal:
|
||||
set_font_size(28.0f);
|
||||
set_letter_spacing(3.08f);
|
||||
set_line_height(28.0f);
|
||||
break;
|
||||
case LabelStyle::Large:
|
||||
set_font_size(36.0f);
|
||||
set_letter_spacing(2.52f);
|
||||
set_line_height(36.0f);
|
||||
break;
|
||||
}
|
||||
|
||||
set_font_style(FontStyle::Normal);
|
||||
set_font_weight(700);
|
||||
}
|
||||
|
||||
Label::Label(const std::string &text, LabelStyle label_style, Element *parent) : Label(label_style, parent) {
|
||||
set_text(text);
|
||||
}
|
||||
|
||||
};
|
18
src/ui/elements/ui_label.h
Normal file
18
src/ui/elements/ui_label.h
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "ui_element.h"
|
||||
|
||||
namespace recompui {
|
||||
|
||||
enum class LabelStyle {
|
||||
Normal,
|
||||
Large
|
||||
};
|
||||
|
||||
class Label : public Element {
|
||||
public:
|
||||
Label(LabelStyle label_style, Element *parent);
|
||||
Label(const std::string &text, LabelStyle label_style, Element *parent);
|
||||
};
|
||||
|
||||
} // namespace recompui
|
73
src/ui/elements/ui_mod_details_panel.cpp
Normal file
73
src/ui/elements/ui_mod_details_panel.cpp
Normal file
@ -0,0 +1,73 @@
|
||||
#include "ui_mod_details_panel.h"
|
||||
|
||||
#include "presets.h"
|
||||
#include "librecomp/mods.hpp"
|
||||
|
||||
namespace recompui {
|
||||
|
||||
ModDetailsPanel::ModDetailsPanel(Element *parent) : Element(parent) {
|
||||
set_flex(1.0f, 1.0f, 200.0f);
|
||||
set_height(100.0f, Unit::Percent);
|
||||
set_flex_direction(FlexDirection::Column);
|
||||
set_border_bottom_right_radius(16.0f);
|
||||
set_background_color(Color{ 190, 184, 219, 25 });
|
||||
|
||||
header_container = std::make_unique<recompui::Container>(FlexDirection::Row, JustifyContent::FlexStart, this);
|
||||
header_container->set_padding(16.0f);
|
||||
header_container->set_background_color(Color{ 0, 0, 0, 89 });
|
||||
{
|
||||
thumbnail_container = std::make_unique<recompui::Container>(FlexDirection::Column, JustifyContent::SpaceEvenly, header_container.get());
|
||||
{
|
||||
thumbnail_image = std::make_unique<recompui::Image>(thumbnail_container.get());
|
||||
thumbnail_image->set_width(100.0f);
|
||||
thumbnail_image->set_height(100.0f);
|
||||
thumbnail_image->set_background_color(Color{ 190, 184, 219, 25 });
|
||||
}
|
||||
|
||||
header_details_container = std::make_unique<recompui::Container>(FlexDirection::Column, JustifyContent::SpaceEvenly, header_container.get());
|
||||
header_details_container->set_width(100.0f, Unit::Percent);
|
||||
header_details_container->set_margin_left(32.0f);
|
||||
header_details_container->set_overflow(Overflow::Hidden);
|
||||
|
||||
{
|
||||
title_label = std::make_unique<recompui::Label>(LabelStyle::Large, header_details_container.get());
|
||||
version_label = std::make_unique<recompui::Label>(LabelStyle::Normal, header_details_container.get());
|
||||
}
|
||||
}
|
||||
|
||||
body_container = std::make_unique<recompui::Container>(FlexDirection::Column, JustifyContent::FlexStart, this);
|
||||
body_container->set_padding_left(16.0f);
|
||||
{
|
||||
description_label = std::make_unique<recompui::Label>(LabelStyle::Normal, body_container.get());
|
||||
authors_label = std::make_unique<recompui::Label>(LabelStyle::Normal, body_container.get());
|
||||
buttons_container = std::make_unique<recompui::Container>(FlexDirection::Row, JustifyContent::SpaceAround, body_container.get());
|
||||
buttons_container->set_padding_left(16.0f);
|
||||
{
|
||||
enable_toggle = std::make_unique<recompui::Toggle>(buttons_container.get());
|
||||
configure_button = std::make_unique<recompui::Button>("Configure", recompui::ButtonStyle::Secondary, buttons_container.get());
|
||||
erase_button = std::make_unique<recompui::Button>("Erase", recompui::ButtonStyle::Secondary, buttons_container.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ModDetailsPanel::~ModDetailsPanel() {
|
||||
}
|
||||
|
||||
void ModDetailsPanel::set_mod_details(const recomp::mods::ModDetails& details) {
|
||||
cur_details = details;
|
||||
|
||||
title_label->set_text(cur_details.mod_id);
|
||||
version_label->set_text(cur_details.version.to_string());
|
||||
|
||||
std::string authors_str = "<i>Authors</i>:";
|
||||
bool first = true;
|
||||
for (const std::string& author : details.authors) {
|
||||
authors_str += (first ? " " : ", ") + author;
|
||||
first = false;
|
||||
}
|
||||
|
||||
authors_label->set_text(authors_str);
|
||||
description_label->set_text("Placeholder description. Some long text to make sure that wrapping is working correctly. Yet more text and so on.");
|
||||
}
|
||||
|
||||
} // namespace recompui
|
37
src/ui/elements/ui_mod_details_panel.h
Normal file
37
src/ui/elements/ui_mod_details_panel.h
Normal file
@ -0,0 +1,37 @@
|
||||
#ifndef RECOMPUI_ELEMENT_MOD_DETAILS_PANEL_H
|
||||
#define RECOMPUI_ELEMENT_MOD_DETAILS_PANEL_H
|
||||
|
||||
#include "common.h"
|
||||
#include "librecomp/mods.hpp"
|
||||
#include "ui_button.h"
|
||||
#include "ui_container.h"
|
||||
#include "ui_image.h"
|
||||
#include "ui_label.h"
|
||||
#include "ui_toggle.h"
|
||||
|
||||
namespace recompui {
|
||||
|
||||
class ModDetailsPanel : public Element {
|
||||
public:
|
||||
ModDetailsPanel(Element *parent);
|
||||
virtual ~ModDetailsPanel();
|
||||
void set_mod_details(const recomp::mods::ModDetails& details);
|
||||
private:
|
||||
recomp::mods::ModDetails cur_details;
|
||||
std::unique_ptr<recompui::Container> thumbnail_container;
|
||||
std::unique_ptr<recompui::Image> thumbnail_image;
|
||||
std::unique_ptr<recompui::Container> header_container;
|
||||
std::unique_ptr<recompui::Container> header_details_container;
|
||||
std::unique_ptr<recompui::Label> title_label;
|
||||
std::unique_ptr<recompui::Label> version_label;
|
||||
std::unique_ptr<recompui::Container> body_container;
|
||||
std::unique_ptr<recompui::Label> description_label;
|
||||
std::unique_ptr<recompui::Label> authors_label;
|
||||
std::unique_ptr<recompui::Container> buttons_container;
|
||||
std::unique_ptr<recompui::Toggle> enable_toggle;
|
||||
std::unique_ptr<recompui::Button> configure_button;
|
||||
std::unique_ptr<recompui::Button> erase_button;
|
||||
};
|
||||
|
||||
} // namespace recompui
|
||||
#endif
|
82
src/ui/elements/ui_toggle.cpp
Normal file
82
src/ui/elements/ui_toggle.cpp
Normal file
@ -0,0 +1,82 @@
|
||||
#include "ui_toggle.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace recompui {
|
||||
|
||||
Toggle::Toggle(Element *parent) : Element(parent, Events(EventType::Click, EventType::Hover), "button") {
|
||||
set_width(162.0f);
|
||||
set_height(72.0f);
|
||||
set_border_radius(36.0f);
|
||||
set_opacity(0.9f);
|
||||
set_cursor(Cursor::Pointer);
|
||||
set_border_width(2.0f);
|
||||
|
||||
floater = std::make_unique<Element>(this);
|
||||
floater->set_position(Position::Relative);
|
||||
floater->set_top(2.0f);
|
||||
floater->set_width(80.0f);
|
||||
floater->set_height(64.0f);
|
||||
floater->set_border_radius(32.0f);
|
||||
|
||||
set_checked_internal(false, false, true);
|
||||
update_properties();
|
||||
}
|
||||
|
||||
void Toggle::set_checked_internal(bool checked, bool animate, bool setup) {
|
||||
if (this->checked != checked || setup) {
|
||||
this->checked = checked;
|
||||
|
||||
floater->set_left(floater_left_target(), Unit::Dp, animate ? Animation::tween(0.1f) : Animation());
|
||||
|
||||
if (!setup) {
|
||||
update_properties();
|
||||
|
||||
for (const auto &function : checked_callbacks) {
|
||||
function(checked);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Toggle::update_properties() {
|
||||
Color border_color = checked ? Color{ 34, 177, 76, 255 } : Color{ 177, 76, 34, 255 };
|
||||
Color main_color = checked ? Color{ 68, 206, 120, 255 } : Color{ 206, 120, 68, 255 };
|
||||
main_color.a = hovered ? 76 : 0;
|
||||
|
||||
set_border_color(border_color);
|
||||
set_background_color(main_color);
|
||||
floater->set_background_color(border_color);
|
||||
}
|
||||
|
||||
float Toggle::floater_left_target() const {
|
||||
return checked ? 4.0f : 78.0f;
|
||||
}
|
||||
|
||||
void Toggle::process_event(const Event &e) {
|
||||
switch (e.type) {
|
||||
case EventType::Click:
|
||||
set_checked_internal(!checked, true, false);
|
||||
break;
|
||||
case EventType::Hover:
|
||||
hovered = e.hover.active;
|
||||
update_properties();
|
||||
break;
|
||||
default:
|
||||
assert(false && "Unknown event type.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Toggle::set_checked(bool checked) {
|
||||
set_checked_internal(checked, false, false);
|
||||
}
|
||||
|
||||
bool Toggle::is_checked() const {
|
||||
return checked;
|
||||
}
|
||||
|
||||
void Toggle::add_checked_callback(std::function<void(bool)> callback) {
|
||||
checked_callbacks.emplace_back(callback);
|
||||
}
|
||||
};
|
27
src/ui/elements/ui_toggle.h
Normal file
27
src/ui/elements/ui_toggle.h
Normal file
@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include "ui_element.h"
|
||||
|
||||
namespace recompui {
|
||||
|
||||
class Toggle : public Element {
|
||||
protected:
|
||||
std::unique_ptr<Element> floater;
|
||||
std::list<std::function<void(bool)>> checked_callbacks;
|
||||
bool checked = false;
|
||||
bool hovered = false;
|
||||
|
||||
void set_checked_internal(bool checked, bool animate, bool setup);
|
||||
void update_properties();
|
||||
float floater_left_target() const;
|
||||
|
||||
// Element overrides.
|
||||
virtual void process_event(const Event &e) override;
|
||||
public:
|
||||
Toggle(Element *parent);
|
||||
void set_checked(bool checked);
|
||||
bool is_checked() const;
|
||||
void add_checked_callback(std::function<void(bool)> callback);
|
||||
};
|
||||
|
||||
} // namespace recompui
|
@ -20,7 +20,6 @@ static RecompElementConfig custom_elements[] = {
|
||||
CUSTOM_ELEMENT("recomp-option-type-radio-tabs", recompui::ElementOptionTypeRadioTabs),
|
||||
CUSTOM_ELEMENT("recomp-option-type-range", recompui::ElementOptionTypeRange),
|
||||
CUSTOM_ELEMENT("recomp-mod-menu", recompui::ElementModMenu),
|
||||
CUSTOM_ELEMENT("recomp-mod-details-panel", recompui::ElementModDetailsPanel),
|
||||
};
|
||||
|
||||
void recompui::register_custom_elements() {
|
||||
|
@ -15,7 +15,6 @@
|
||||
#include "elements/ElementOptionTypeTextField.h"
|
||||
#include "elements/ElementDescription.h"
|
||||
#include "elements/ElementModMenu.h"
|
||||
#include "elements/ElementModDetailsPanel.h"
|
||||
|
||||
namespace recompui {
|
||||
void register_custom_elements();
|
||||
|
@ -93,6 +93,12 @@ public:
|
||||
recompui::set_config_submenu(recompui::ConfigSubmenu::General);
|
||||
}
|
||||
);
|
||||
recompui::register_event(listener, "open_mods",
|
||||
[](const std::string ¶m, Rml::Event &event) {
|
||||
recompui::set_current_menu(recompui::Menu::Config);
|
||||
recompui::set_config_submenu(recompui::ConfigSubmenu::Mods);
|
||||
}
|
||||
);
|
||||
recompui::register_event(listener, "exit_game",
|
||||
[](const std::string& param, Rml::Event& event) {
|
||||
ultramodern::quit();
|
||||
|
@ -105,6 +105,14 @@ T from_bytes_le(const char* input) {
|
||||
void load_document();
|
||||
|
||||
class RmlRenderInterface_RT64 : public Rml::RenderInterfaceCompatibility {
|
||||
struct DynamicBuffer {
|
||||
std::unique_ptr<RT64::RenderBuffer> buffer_{};
|
||||
uint32_t size_ = 0;
|
||||
uint32_t bytes_used_ = 0;
|
||||
uint8_t* mapped_data_ = nullptr;
|
||||
RT64::RenderBufferFlags flags_ = RT64::RenderBufferFlag::NONE;
|
||||
};
|
||||
|
||||
static constexpr uint32_t per_frame_descriptor_set = 0;
|
||||
static constexpr uint32_t per_draw_descriptor_set = 1;
|
||||
|
||||
@ -129,9 +137,9 @@ class RmlRenderInterface_RT64 : public Rml::RenderInterfaceCompatibility {
|
||||
Rml::Matrix4f mvp_ = Rml::Matrix4f::Identity();
|
||||
std::unordered_map<Rml::TextureHandle, TextureHandle> textures_{};
|
||||
Rml::TextureHandle texture_count_ = 1; // Start at 1 to reserve texture 0 as the 1x1 pixel white texture
|
||||
std::unique_ptr<RT64::RenderBuffer> upload_buffer_{};
|
||||
std::unique_ptr<RT64::RenderBuffer> vertex_buffer_{};
|
||||
std::unique_ptr<RT64::RenderBuffer> index_buffer_{};
|
||||
DynamicBuffer upload_buffer_;
|
||||
DynamicBuffer vertex_buffer_;
|
||||
DynamicBuffer index_buffer_;
|
||||
std::unique_ptr<RT64::RenderSampler> nearestSampler_{};
|
||||
std::unique_ptr<RT64::RenderSampler> linearSampler_{};
|
||||
std::unique_ptr<RT64::RenderShader> vertex_shader_{};
|
||||
@ -147,11 +155,6 @@ class RmlRenderInterface_RT64 : public Rml::RenderInterfaceCompatibility {
|
||||
std::unique_ptr<RT64::RenderDescriptorSet> screen_descriptor_set_{};
|
||||
std::unique_ptr<RT64::RenderBuffer> screen_vertex_buffer_{};
|
||||
uint64_t screen_vertex_buffer_size_ = 0;
|
||||
uint32_t upload_buffer_size_ = 0;
|
||||
uint32_t upload_buffer_bytes_used_ = 0;
|
||||
uint8_t* upload_buffer_mapped_data_ = nullptr;
|
||||
uint32_t vertex_buffer_size_ = 0;
|
||||
uint32_t index_buffer_size_ = 0;
|
||||
uint32_t gTexture_descriptor_index;
|
||||
RT64::RenderInputSlot vertex_slot_{ 0, sizeof(Rml::Vertex) };
|
||||
RT64::RenderCommandList* list_ = nullptr;
|
||||
@ -167,10 +170,13 @@ public:
|
||||
multisampling_.sampleCount = desired_sample_count;
|
||||
}
|
||||
|
||||
vertex_buffer_.flags_ = RT64::RenderBufferFlag::VERTEX;
|
||||
index_buffer_.flags_ = RT64::RenderBufferFlag::INDEX;
|
||||
|
||||
// Create the texture upload buffer, vertex buffer and index buffer
|
||||
resize_upload_buffer(initial_upload_buffer_size, false);
|
||||
resize_vertex_buffer(initial_vertex_buffer_size);
|
||||
resize_index_buffer(initial_index_buffer_size);
|
||||
resize_dynamic_buffer(upload_buffer_, initial_upload_buffer_size, false);
|
||||
resize_dynamic_buffer(vertex_buffer_, initial_vertex_buffer_size, false);
|
||||
resize_dynamic_buffer(index_buffer_, initial_index_buffer_size, false);
|
||||
|
||||
// Describe the vertex format
|
||||
std::vector<RT64::RenderInputElement> vertex_elements{};
|
||||
@ -260,90 +266,78 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void resize_upload_buffer(uint32_t new_size, bool map = true) {
|
||||
// Unmap the upload buffer if it's mapped
|
||||
if (upload_buffer_mapped_data_ != nullptr) {
|
||||
upload_buffer_->unmap();
|
||||
void reset_dynamic_buffer(DynamicBuffer &dynamic_buffer) {
|
||||
assert(dynamic_buffer.mapped_data_ == nullptr);
|
||||
dynamic_buffer.bytes_used_ = 0;
|
||||
dynamic_buffer.mapped_data_ = reinterpret_cast<uint8_t*>(dynamic_buffer.buffer_->map());
|
||||
}
|
||||
|
||||
void end_dynamic_buffer(DynamicBuffer &dynamic_buffer) {
|
||||
assert(dynamic_buffer.mapped_data_ != nullptr);
|
||||
dynamic_buffer.buffer_->unmap();
|
||||
dynamic_buffer.mapped_data_ = nullptr;
|
||||
}
|
||||
|
||||
void resize_dynamic_buffer(DynamicBuffer &dynamic_buffer, uint32_t new_size, bool map = true) {
|
||||
// Unmap the buffer if it's mapped
|
||||
if (dynamic_buffer.mapped_data_ != nullptr) {
|
||||
dynamic_buffer.buffer_->unmap();
|
||||
}
|
||||
|
||||
// If there's already an upload buffer, move it into the stale buffers so it persists until the start of next frame.
|
||||
if (upload_buffer_) {
|
||||
stale_buffers_.emplace_back(std::move(upload_buffer_));
|
||||
// If there's already a buffer, move it into the stale buffers so it persists until the start of next frame.
|
||||
if (dynamic_buffer.buffer_ != nullptr) {
|
||||
stale_buffers_.emplace_back(std::move(dynamic_buffer.buffer_));
|
||||
}
|
||||
|
||||
// Create the new upload buffer, update the size and map it.
|
||||
upload_buffer_ = render_context_->device->createBuffer(RT64::RenderBufferDesc::UploadBuffer(new_size));
|
||||
upload_buffer_size_ = new_size;
|
||||
upload_buffer_bytes_used_ = 0;
|
||||
// Create the new buffer, update the size and map it.
|
||||
dynamic_buffer.buffer_ = render_context_->device->createBuffer(RT64::RenderBufferDesc::UploadBuffer(new_size, dynamic_buffer.flags_));
|
||||
dynamic_buffer.size_ = new_size;
|
||||
dynamic_buffer.bytes_used_ = 0;
|
||||
|
||||
if (map) {
|
||||
upload_buffer_mapped_data_ = reinterpret_cast<uint8_t*>(upload_buffer_->map());
|
||||
}
|
||||
else {
|
||||
upload_buffer_mapped_data_ = nullptr;
|
||||
dynamic_buffer.mapped_data_ = reinterpret_cast<uint8_t*>(dynamic_buffer.buffer_->map());
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t allocate_upload_data(uint32_t num_bytes) {
|
||||
// Check if there's enough remaining room in the upload buffer to allocate the requested bytes.
|
||||
uint32_t total_bytes = num_bytes + upload_buffer_bytes_used_;
|
||||
uint32_t allocate_dynamic_data(DynamicBuffer &dynamic_buffer, uint32_t num_bytes) {
|
||||
// Check if there's enough remaining room in the buffer to allocate the requested bytes.
|
||||
uint32_t total_bytes = num_bytes + dynamic_buffer.bytes_used_;
|
||||
|
||||
if (total_bytes > upload_buffer_size_) {
|
||||
// There isn't, so mark the current upload buffer as stale and allocate a new one with 50% more space than the required amount.
|
||||
resize_upload_buffer(total_bytes + total_bytes / 2);
|
||||
if (total_bytes > dynamic_buffer.size_) {
|
||||
// There isn't, so mark the current buffer as stale and allocate a new one with 50% more space than the required amount.
|
||||
resize_dynamic_buffer(dynamic_buffer, total_bytes + total_bytes / 2);
|
||||
}
|
||||
|
||||
// Record the current end of the upload buffer to return.
|
||||
uint32_t offset = upload_buffer_bytes_used_;
|
||||
// Record the current end of the buffer to return.
|
||||
uint32_t offset = dynamic_buffer.bytes_used_;
|
||||
|
||||
// Bump the upload buffer's end forward by the number of bytes allocated.
|
||||
upload_buffer_bytes_used_ += num_bytes;
|
||||
// Bump the buffer's end forward by the number of bytes allocated.
|
||||
dynamic_buffer.bytes_used_ += num_bytes;
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
uint32_t allocate_upload_data_aligned(uint32_t num_bytes, uint32_t alignment) {
|
||||
// Check if there's enough remaining room in the upload buffer to allocate the requested bytes.
|
||||
uint32_t total_bytes = num_bytes + upload_buffer_bytes_used_;
|
||||
uint32_t allocate_dynamic_data_aligned(DynamicBuffer &dynamic_buffer, uint32_t num_bytes, uint32_t alignment) {
|
||||
// Check if there's enough remaining room in the buffer to allocate the requested bytes.
|
||||
uint32_t total_bytes = num_bytes + dynamic_buffer.bytes_used_;
|
||||
|
||||
// Determine the amount of padding needed to meet the target alignment.
|
||||
uint32_t padding_bytes = ((upload_buffer_bytes_used_ + alignment - 1) / alignment) * alignment - upload_buffer_bytes_used_;
|
||||
uint32_t padding_bytes = ((dynamic_buffer.bytes_used_ + alignment - 1) / alignment) * alignment - dynamic_buffer.bytes_used_;
|
||||
|
||||
// If there isn't enough room to allocate the required bytes plus the padding then resize the upload buffer and allocate from the start of the new one.
|
||||
if (total_bytes + padding_bytes > upload_buffer_size_) {
|
||||
resize_upload_buffer(total_bytes + total_bytes / 2);
|
||||
// If there isn't enough room to allocate the required bytes plus the padding then resize the buffer and allocate from the start of the new one.
|
||||
if (total_bytes + padding_bytes > dynamic_buffer.size_) {
|
||||
resize_dynamic_buffer(dynamic_buffer, total_bytes + total_bytes / 2);
|
||||
|
||||
upload_buffer_bytes_used_ += num_bytes;
|
||||
dynamic_buffer.bytes_used_ += num_bytes;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Otherwise allocate the padding and required bytes and offset the allocated position by the padding size.
|
||||
return allocate_upload_data(padding_bytes + num_bytes) + padding_bytes;
|
||||
}
|
||||
|
||||
void resize_vertex_buffer(uint32_t new_size) {
|
||||
if (vertex_buffer_) {
|
||||
stale_buffers_.emplace_back(std::move(vertex_buffer_));
|
||||
}
|
||||
vertex_buffer_ = render_context_->device->createBuffer(RT64::RenderBufferDesc::VertexBuffer(new_size, RT64::RenderHeapType::DEFAULT));
|
||||
vertex_buffer_size_ = new_size;
|
||||
}
|
||||
|
||||
void resize_index_buffer(uint32_t new_size) {
|
||||
if (index_buffer_) {
|
||||
stale_buffers_.emplace_back(std::move(index_buffer_));
|
||||
}
|
||||
index_buffer_ = render_context_->device->createBuffer(RT64::RenderBufferDesc::IndexBuffer(new_size, RT64::RenderHeapType::DEFAULT));
|
||||
index_buffer_size_ = new_size;
|
||||
return allocate_dynamic_data(dynamic_buffer, padding_bytes + num_bytes) + padding_bytes;
|
||||
}
|
||||
|
||||
void RenderGeometry(Rml::Vertex* vertices, int num_vertices, int* indices, int num_indices, Rml::TextureHandle texture, const Rml::Vector2f& translation) override {
|
||||
uint32_t vert_size_bytes = num_vertices * sizeof(*vertices);
|
||||
uint32_t index_size_bytes = num_indices * sizeof(*indices);
|
||||
uint32_t total_bytes = vert_size_bytes + index_size_bytes;
|
||||
uint32_t index_bytes_start = vert_size_bytes;
|
||||
|
||||
|
||||
if (!textures_.contains(texture)) {
|
||||
if (texture == 0) {
|
||||
// Create a 1x1 pixel white texture as the first handle
|
||||
@ -355,37 +349,13 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t upload_buffer_offset = allocate_upload_data(total_bytes);
|
||||
|
||||
if (vert_size_bytes > vertex_buffer_size_) {
|
||||
resize_vertex_buffer(vert_size_bytes + vert_size_bytes / 2);
|
||||
}
|
||||
|
||||
if (index_size_bytes > index_buffer_size_) {
|
||||
resize_index_buffer(index_size_bytes + index_size_bytes / 2);
|
||||
}
|
||||
|
||||
// Copy the vertex and index data into the mapped upload buffer.
|
||||
memcpy(upload_buffer_mapped_data_ + upload_buffer_offset, vertices, vert_size_bytes);
|
||||
memcpy(upload_buffer_mapped_data_ + upload_buffer_offset + vert_size_bytes, indices, index_size_bytes);
|
||||
|
||||
// Prepare the vertex and index buffers for being copied to.
|
||||
RT64::RenderBufferBarrier copy_barriers[] = {
|
||||
RT64::RenderBufferBarrier(vertex_buffer_.get(), RT64::RenderBufferAccess::WRITE),
|
||||
RT64::RenderBufferBarrier(index_buffer_.get(), RT64::RenderBufferAccess::WRITE)
|
||||
};
|
||||
list_->barriers(RT64::RenderBarrierStage::COPY, copy_barriers, uint32_t(std::size(copy_barriers)));
|
||||
|
||||
// Copy from the upload buffer to the vertex and index buffers.
|
||||
list_->copyBufferRegion(vertex_buffer_->at(0), upload_buffer_->at(upload_buffer_offset), vert_size_bytes);
|
||||
list_->copyBufferRegion(index_buffer_->at(0), upload_buffer_->at(upload_buffer_offset + index_bytes_start), index_size_bytes);
|
||||
|
||||
// Prepare the vertex and index buffers for being used for rendering.
|
||||
RT64::RenderBufferBarrier usage_barriers[] = {
|
||||
RT64::RenderBufferBarrier(vertex_buffer_.get(), RT64::RenderBufferAccess::READ),
|
||||
RT64::RenderBufferBarrier(index_buffer_.get(), RT64::RenderBufferAccess::READ)
|
||||
};
|
||||
list_->barriers(RT64::RenderBarrierStage::GRAPHICS, usage_barriers, uint32_t(std::size(usage_barriers)));
|
||||
// Copy the vertex and index data into the mapped buffers.
|
||||
uint32_t vert_size_bytes = num_vertices * sizeof(*vertices);
|
||||
uint32_t index_size_bytes = num_indices * sizeof(*indices);
|
||||
uint32_t vertex_buffer_offset = allocate_dynamic_data(vertex_buffer_, vert_size_bytes);
|
||||
uint32_t index_buffer_offset = allocate_dynamic_data(index_buffer_, index_size_bytes);
|
||||
memcpy(vertex_buffer_.mapped_data_ + vertex_buffer_offset, vertices, vert_size_bytes);
|
||||
memcpy(index_buffer_.mapped_data_ + index_buffer_offset, indices, index_size_bytes);
|
||||
|
||||
list_->setViewports(RT64::RenderViewport{ 0, 0, float(window_width_), float(window_height_) });
|
||||
if (scissor_enabled_) {
|
||||
@ -399,9 +369,9 @@ public:
|
||||
list_->setScissors(RT64::RenderRect{ 0, 0, window_width_, window_height_ });
|
||||
}
|
||||
|
||||
RT64::RenderIndexBufferView index_view{index_buffer_->at(0), index_size_bytes, RT64::RenderFormat::R32_UINT};
|
||||
RT64::RenderIndexBufferView index_view{index_buffer_.buffer_->at(index_buffer_offset), index_size_bytes, RT64::RenderFormat::R32_UINT};
|
||||
list_->setIndexBuffer(&index_view);
|
||||
RT64::RenderVertexBufferView vertex_view{vertex_buffer_->at(0), vert_size_bytes};
|
||||
RT64::RenderVertexBufferView vertex_view{vertex_buffer_.buffer_->at(vertex_buffer_offset), vert_size_bytes};
|
||||
list_->setVertexBuffers(0, &vertex_view, 1, &vertex_slot_);
|
||||
list_->setGraphicsDescriptorSet(textures_.at(texture).set.get(), 1);
|
||||
|
||||
@ -522,10 +492,10 @@ public:
|
||||
uint32_t uploaded_size_bytes = row_byte_width * source_dimensions.y;
|
||||
|
||||
// Allocate room in the upload buffer for the uploaded data.
|
||||
uint32_t upload_buffer_offset = allocate_upload_data_aligned(uploaded_size_bytes, 512);
|
||||
uint32_t upload_buffer_offset = allocate_dynamic_data_aligned(upload_buffer_, uploaded_size_bytes, 512);
|
||||
|
||||
// Copy the source data into the upload buffer.
|
||||
uint8_t* dst_data = upload_buffer_mapped_data_ + upload_buffer_offset;
|
||||
uint8_t* dst_data = upload_buffer_.mapped_data_ + upload_buffer_offset;
|
||||
|
||||
if (row_byte_padding == 0) {
|
||||
// Copy row-by-row if the image is flipped.
|
||||
@ -557,7 +527,7 @@ public:
|
||||
// Copy the upload buffer into the texture.
|
||||
list_->copyTextureRegion(
|
||||
RT64::RenderTextureCopyLocation::Subresource(texture.get()),
|
||||
RT64::RenderTextureCopyLocation::PlacedFootprint(upload_buffer_.get(), RmlTextureFormat, source_dimensions.x, source_dimensions.y, 1, row_width, upload_buffer_offset));
|
||||
RT64::RenderTextureCopyLocation::PlacedFootprint(upload_buffer_.buffer_.get(), RmlTextureFormat, source_dimensions.x, source_dimensions.y, 1, row_width, upload_buffer_offset));
|
||||
|
||||
// Prepare the texture for being read from a pixel shader.
|
||||
list_->barriers(RT64::RenderBarrierStage::GRAPHICS, RT64::RenderTextureBarrier(texture.get(), RT64::RenderTextureLayout::SHADER_READ));
|
||||
@ -621,9 +591,10 @@ public:
|
||||
// Clear out any stale buffers from the last command list.
|
||||
stale_buffers_.clear();
|
||||
|
||||
// Reset and map the upload buffer.
|
||||
upload_buffer_bytes_used_ = 0;
|
||||
upload_buffer_mapped_data_ = reinterpret_cast<uint8_t*>(upload_buffer_->map());
|
||||
// Reset buffers.
|
||||
reset_dynamic_buffer(upload_buffer_);
|
||||
reset_dynamic_buffer(vertex_buffer_);
|
||||
reset_dynamic_buffer(index_buffer_);
|
||||
|
||||
// Set an internal texture as the render target if MSAA is enabled.
|
||||
if (multisampling_.sampleCount > 1) {
|
||||
@ -661,13 +632,11 @@ public:
|
||||
list->drawInstanced(3, 1, 0, 0);
|
||||
}
|
||||
|
||||
list_ = nullptr;
|
||||
end_dynamic_buffer(upload_buffer_);
|
||||
end_dynamic_buffer(vertex_buffer_);
|
||||
end_dynamic_buffer(index_buffer_);
|
||||
|
||||
// Unmap the upload buffer if it's mapped.
|
||||
if (upload_buffer_mapped_data_) {
|
||||
upload_buffer_->unmap();
|
||||
upload_buffer_mapped_data_ = nullptr;
|
||||
}
|
||||
list_ = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user