mirror of
https://github.com/Zelda64Recomp/Zelda64Recomp.git
synced 2026-04-27 20:51:40 +00:00
Rewrite mod details under new UI system.
This commit is contained in:
parent
226ccdda74
commit
425dfeec24
31 changed files with 3765 additions and 377 deletions
|
|
@ -172,8 +172,14 @@ set (SOURCES
|
||||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ElementOptionTypeRange.cpp
|
${CMAKE_SOURCE_DIR}/src/ui/elements/ElementOptionTypeRange.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ElementOptionTypeTextField.cpp
|
${CMAKE_SOURCE_DIR}/src/ui/elements/ElementOptionTypeTextField.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ElementModMenu.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/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/aspMain.cpp
|
||||||
${CMAKE_SOURCE_DIR}/rsp/njpgdspMain.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/general.rml" />
|
||||||
<link type="text/template" href="config_menu/controls.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/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/debug.rml" />
|
||||||
<link type="text/template" href="config_menu/cheats.rml" />
|
<link type="text/template" href="config_menu/cheats.rml" />
|
||||||
<link type="text/template" href="components/prompt.rml" />
|
<link type="text/template" href="components/prompt.rml" />
|
||||||
|
|
@ -65,6 +66,13 @@
|
||||||
<panel class="config" data-model="sound_options_model">
|
<panel class="config" data-model="sound_options_model">
|
||||||
<template src="config-menu__sound" />
|
<template src="config-menu__sound" />
|
||||||
</panel>
|
</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">
|
<tab class="tab" data-model="debug_model" data-if="debug_enabled" id="tab_debug">
|
||||||
<div>Debug</div>
|
<div>Debug</div>
|
||||||
<div class="tab__indicator"></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__bullet">•</div>
|
||||||
<div class="menu-list-item__label">Settings</div>
|
<div class="menu-list-item__label">Settings</div>
|
||||||
</button>
|
</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">
|
<button onclick="exit_game" class="menu-list-item menu-list-item--right">
|
||||||
<div class="menu-list-item__bullet">•</div>
|
<div class="menu-list-item__bullet">•</div>
|
||||||
<div class="menu-list-item__label">Exit</div>
|
<div class="menu-list-item__label">Exit</div>
|
||||||
|
|
@ -61,7 +65,6 @@
|
||||||
<label>v{{version_number}}</label>
|
<label>v{{version_number}}</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<recomp-mod-menu />
|
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</rml>
|
</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 {
|
.mod-menu {
|
||||||
display: flex;
|
display: flex;
|
||||||
position: absolute;
|
position: relative;
|
||||||
top: 0;
|
flex: 1 1 100%;
|
||||||
right: 0;
|
flex-direction: column;
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: space($page-margin);
|
height: 100%;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__list {
|
&__list {
|
||||||
display: block;
|
display: block;
|
||||||
|
|
@ -91,6 +44,7 @@
|
||||||
&:hover, &:focus {
|
&:hover, &:focus {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
&[is_selected] {
|
&[is_selected] {
|
||||||
border-color: rgb(255,255,255);
|
border-color: rgb(255,255,255);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,4 +15,3 @@
|
||||||
@import "./BottomLeft";
|
@import "./BottomLeft";
|
||||||
@import "./Prompt";
|
@import "./Prompt";
|
||||||
@import "./ModMenu";
|
@import "./ModMenu";
|
||||||
@import "./ModDetails";
|
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@ namespace recompui {
|
||||||
Controls,
|
Controls,
|
||||||
Graphics,
|
Graphics,
|
||||||
Audio,
|
Audio,
|
||||||
|
Mods,
|
||||||
Debug,
|
Debug,
|
||||||
Count
|
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 "ElementModMenu.h"
|
||||||
#include "ElementModDetailsPanel.h"
|
|
||||||
#include "presets.h"
|
#include "presets.h"
|
||||||
#include "librecomp/mods.hpp"
|
#include "librecomp/mods.hpp"
|
||||||
|
|
||||||
|
|
@ -10,9 +9,6 @@
|
||||||
namespace recompui {
|
namespace recompui {
|
||||||
|
|
||||||
static const std::string cls_base = BLOCK(MOD_MENU_BEM);
|
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 = EL(MOD_MENU_BEM, "list");
|
||||||
static const std::string cls_list_scroll = EL(MOD_MENU_BEM, "list-scroll");
|
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");
|
static const std::string cls_list_entry = EL(MOD_MENU_BEM, "list-entry");
|
||||||
|
|
@ -30,10 +26,6 @@ void ElementModMenu::ProcessEvent(Rml::Event& event) {
|
||||||
// Refresh
|
// Refresh
|
||||||
if (event_element == refresh_button) {
|
if (event_element == refresh_button) {
|
||||||
RefreshMods();
|
RefreshMods();
|
||||||
}
|
|
||||||
// Close
|
|
||||||
else if (event_element == close_button) {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Rml::EventId::Focus:
|
case Rml::EventId::Focus:
|
||||||
|
|
@ -41,7 +33,7 @@ void ElementModMenu::ProcessEvent(Rml::Event& event) {
|
||||||
size_t mod_index;
|
size_t mod_index;
|
||||||
Rml::Variant *val = event_element->GetAttribute("mod_index");
|
Rml::Variant *val = event_element->GetAttribute("mod_index");
|
||||||
if (val->GetInto(mod_index) && mod_index < mod_details.size()) {
|
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) {
|
if (active_list_entry_el != nullptr) {
|
||||||
active_list_entry_el->RemoveAttribute("is_selected");
|
active_list_entry_el->RemoveAttribute("is_selected");
|
||||||
|
|
@ -49,6 +41,8 @@ void ElementModMenu::ProcessEvent(Rml::Event& event) {
|
||||||
event_element->SetAttribute("is_selected", true);
|
event_element->SetAttribute("is_selected", true);
|
||||||
active_list_entry_el = event_element;
|
active_list_entry_el = event_element;
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -111,7 +105,7 @@ void ElementModMenu::RefreshMods() {
|
||||||
recomp::mods::scan_mods();
|
recomp::mods::scan_mods();
|
||||||
mod_details = recomp::mods::get_mod_details(game_mod_id);
|
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();
|
CreateModList();
|
||||||
}
|
}
|
||||||
|
|
@ -122,34 +116,23 @@ ElementModMenu::ElementModMenu(const Rml::String& tag) : Rml::Element(tag) {
|
||||||
Rml::ElementDocument *doc = GetOwnerDocument();
|
Rml::ElementDocument *doc = GetOwnerDocument();
|
||||||
SetClass(cls_base, true);
|
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);
|
list_el_scroll = add_div_with_class(doc, list_el, cls_list_scroll);
|
||||||
{
|
} // list_el
|
||||||
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");
|
|
||||||
|
|
||||||
refresh_button->SetProperty("nav-right", "#" + close_button->GetId());
|
recompui::Element body_el_compat(body_el);
|
||||||
close_button->SetProperty("nav-left", "#" + refresh_button->GetId());
|
mod_details_panel = std::make_unique<ModDetailsPanel>(&body_el_compat);
|
||||||
} // header_el
|
} // body_el
|
||||||
|
|
||||||
Rml::Element *body_el = add_div_with_class(doc, modal_wrapper_el, cls_modal_body);
|
Rml::Element *footer_el = add_div_with_class(doc, this, "config__footer");
|
||||||
{
|
{
|
||||||
list_el = add_div_with_class(doc, body_el, cls_list);
|
refresh_button = add_button(doc, footer_el, "Refresh", ButtonVariant::Primary);
|
||||||
{
|
refresh_button->AddEventListener(Rml::EventId::Click, this, false);
|
||||||
list_el_scroll = add_div_with_class(doc, list_el, cls_list_scroll);
|
refresh_button->SetId("refresh-button");
|
||||||
} // list_el
|
} // footer_el
|
||||||
|
|
||||||
details_el =
|
|
||||||
static_cast<ElementModDetailsPanel*>(body_el->AppendChild(doc->CreateElement("recomp-mod-details-panel")));
|
|
||||||
} // body_el
|
|
||||||
} // modal_wrapper_el
|
|
||||||
}
|
|
||||||
|
|
||||||
RefreshMods();
|
RefreshMods();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "librecomp/mods.hpp"
|
#include "librecomp/mods.hpp"
|
||||||
#include "ElementModDetailsPanel.h"
|
#include "ui_mod_details_panel.h"
|
||||||
|
|
||||||
namespace recompui {
|
namespace recompui {
|
||||||
|
|
||||||
|
|
@ -17,10 +17,9 @@ private:
|
||||||
void CreateModList();
|
void CreateModList();
|
||||||
Rml::ElementPtr CreateModListEntry(const recomp::mods::ModDetails& details, size_t index);
|
Rml::ElementPtr CreateModListEntry(const recomp::mods::ModDetails& details, size_t index);
|
||||||
Rml::Element *refresh_button;
|
Rml::Element *refresh_button;
|
||||||
Rml::Element *close_button;
|
|
||||||
Rml::Element *list_el; // The root mod list element.
|
Rml::Element *list_el; // The root mod list element.
|
||||||
Rml::Element *list_el_scroll; // The scroll within 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;
|
Rml::Element *active_list_entry_el = nullptr;
|
||||||
std::vector<recomp::mods::ModDetails> mod_details{};
|
std::vector<recomp::mods::ModDetails> mod_details{};
|
||||||
std::string game_mod_id;
|
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-radio-tabs", recompui::ElementOptionTypeRadioTabs),
|
||||||
CUSTOM_ELEMENT("recomp-option-type-range", recompui::ElementOptionTypeRange),
|
CUSTOM_ELEMENT("recomp-option-type-range", recompui::ElementOptionTypeRange),
|
||||||
CUSTOM_ELEMENT("recomp-mod-menu", recompui::ElementModMenu),
|
CUSTOM_ELEMENT("recomp-mod-menu", recompui::ElementModMenu),
|
||||||
CUSTOM_ELEMENT("recomp-mod-details-panel", recompui::ElementModDetailsPanel),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void recompui::register_custom_elements() {
|
void recompui::register_custom_elements() {
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@
|
||||||
#include "elements/ElementOptionTypeTextField.h"
|
#include "elements/ElementOptionTypeTextField.h"
|
||||||
#include "elements/ElementDescription.h"
|
#include "elements/ElementDescription.h"
|
||||||
#include "elements/ElementModMenu.h"
|
#include "elements/ElementModMenu.h"
|
||||||
#include "elements/ElementModDetailsPanel.h"
|
|
||||||
|
|
||||||
namespace recompui {
|
namespace recompui {
|
||||||
void register_custom_elements();
|
void register_custom_elements();
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,12 @@ public:
|
||||||
recompui::set_config_submenu(recompui::ConfigSubmenu::General);
|
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",
|
recompui::register_event(listener, "exit_game",
|
||||||
[](const std::string& param, Rml::Event& event) {
|
[](const std::string& param, Rml::Event& event) {
|
||||||
ultramodern::quit();
|
ultramodern::quit();
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,14 @@ T from_bytes_le(const char* input) {
|
||||||
void load_document();
|
void load_document();
|
||||||
|
|
||||||
class RmlRenderInterface_RT64 : public Rml::RenderInterfaceCompatibility {
|
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_frame_descriptor_set = 0;
|
||||||
static constexpr uint32_t per_draw_descriptor_set = 1;
|
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();
|
Rml::Matrix4f mvp_ = Rml::Matrix4f::Identity();
|
||||||
std::unordered_map<Rml::TextureHandle, TextureHandle> textures_{};
|
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
|
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_{};
|
DynamicBuffer upload_buffer_;
|
||||||
std::unique_ptr<RT64::RenderBuffer> vertex_buffer_{};
|
DynamicBuffer vertex_buffer_;
|
||||||
std::unique_ptr<RT64::RenderBuffer> index_buffer_{};
|
DynamicBuffer index_buffer_;
|
||||||
std::unique_ptr<RT64::RenderSampler> nearestSampler_{};
|
std::unique_ptr<RT64::RenderSampler> nearestSampler_{};
|
||||||
std::unique_ptr<RT64::RenderSampler> linearSampler_{};
|
std::unique_ptr<RT64::RenderSampler> linearSampler_{};
|
||||||
std::unique_ptr<RT64::RenderShader> vertex_shader_{};
|
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::RenderDescriptorSet> screen_descriptor_set_{};
|
||||||
std::unique_ptr<RT64::RenderBuffer> screen_vertex_buffer_{};
|
std::unique_ptr<RT64::RenderBuffer> screen_vertex_buffer_{};
|
||||||
uint64_t screen_vertex_buffer_size_ = 0;
|
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;
|
uint32_t gTexture_descriptor_index;
|
||||||
RT64::RenderInputSlot vertex_slot_{ 0, sizeof(Rml::Vertex) };
|
RT64::RenderInputSlot vertex_slot_{ 0, sizeof(Rml::Vertex) };
|
||||||
RT64::RenderCommandList* list_ = nullptr;
|
RT64::RenderCommandList* list_ = nullptr;
|
||||||
|
|
@ -167,10 +170,13 @@ public:
|
||||||
multisampling_.sampleCount = desired_sample_count;
|
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
|
// Create the texture upload buffer, vertex buffer and index buffer
|
||||||
resize_upload_buffer(initial_upload_buffer_size, false);
|
resize_dynamic_buffer(upload_buffer_, initial_upload_buffer_size, false);
|
||||||
resize_vertex_buffer(initial_vertex_buffer_size);
|
resize_dynamic_buffer(vertex_buffer_, initial_vertex_buffer_size, false);
|
||||||
resize_index_buffer(initial_index_buffer_size);
|
resize_dynamic_buffer(index_buffer_, initial_index_buffer_size, false);
|
||||||
|
|
||||||
// Describe the vertex format
|
// Describe the vertex format
|
||||||
std::vector<RT64::RenderInputElement> vertex_elements{};
|
std::vector<RT64::RenderInputElement> vertex_elements{};
|
||||||
|
|
@ -260,90 +266,78 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void resize_upload_buffer(uint32_t new_size, bool map = true) {
|
void reset_dynamic_buffer(DynamicBuffer &dynamic_buffer) {
|
||||||
// Unmap the upload buffer if it's mapped
|
assert(dynamic_buffer.mapped_data_ == nullptr);
|
||||||
if (upload_buffer_mapped_data_ != nullptr) {
|
dynamic_buffer.bytes_used_ = 0;
|
||||||
upload_buffer_->unmap();
|
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 there's already a buffer, move it into the stale buffers so it persists until the start of next frame.
|
||||||
if (upload_buffer_) {
|
if (dynamic_buffer.buffer_ != nullptr) {
|
||||||
stale_buffers_.emplace_back(std::move(upload_buffer_));
|
stale_buffers_.emplace_back(std::move(dynamic_buffer.buffer_));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the new upload buffer, update the size and map it.
|
// Create the new buffer, update the size and map it.
|
||||||
upload_buffer_ = render_context_->device->createBuffer(RT64::RenderBufferDesc::UploadBuffer(new_size));
|
dynamic_buffer.buffer_ = render_context_->device->createBuffer(RT64::RenderBufferDesc::UploadBuffer(new_size, dynamic_buffer.flags_));
|
||||||
upload_buffer_size_ = new_size;
|
dynamic_buffer.size_ = new_size;
|
||||||
upload_buffer_bytes_used_ = 0;
|
dynamic_buffer.bytes_used_ = 0;
|
||||||
|
|
||||||
if (map) {
|
if (map) {
|
||||||
upload_buffer_mapped_data_ = reinterpret_cast<uint8_t*>(upload_buffer_->map());
|
dynamic_buffer.mapped_data_ = reinterpret_cast<uint8_t*>(dynamic_buffer.buffer_->map());
|
||||||
}
|
|
||||||
else {
|
|
||||||
upload_buffer_mapped_data_ = nullptr;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t allocate_upload_data(uint32_t num_bytes) {
|
uint32_t allocate_dynamic_data(DynamicBuffer &dynamic_buffer, uint32_t num_bytes) {
|
||||||
// Check if there's enough remaining room in the upload buffer to allocate the requested bytes.
|
// Check if there's enough remaining room in the buffer to allocate the requested bytes.
|
||||||
uint32_t total_bytes = num_bytes + upload_buffer_bytes_used_;
|
uint32_t total_bytes = num_bytes + dynamic_buffer.bytes_used_;
|
||||||
|
|
||||||
if (total_bytes > upload_buffer_size_) {
|
if (total_bytes > dynamic_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.
|
// There isn't, so mark the current buffer as stale and allocate a new one with 50% more space than the required amount.
|
||||||
resize_upload_buffer(total_bytes + total_bytes / 2);
|
resize_dynamic_buffer(dynamic_buffer, total_bytes + total_bytes / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Record the current end of the upload buffer to return.
|
// Record the current end of the buffer to return.
|
||||||
uint32_t offset = upload_buffer_bytes_used_;
|
uint32_t offset = dynamic_buffer.bytes_used_;
|
||||||
|
|
||||||
// Bump the upload buffer's end forward by the number of bytes allocated.
|
// Bump the buffer's end forward by the number of bytes allocated.
|
||||||
upload_buffer_bytes_used_ += num_bytes;
|
dynamic_buffer.bytes_used_ += num_bytes;
|
||||||
|
|
||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t allocate_upload_data_aligned(uint32_t num_bytes, uint32_t alignment) {
|
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 upload buffer to allocate the requested bytes.
|
// Check if there's enough remaining room in the buffer to allocate the requested bytes.
|
||||||
uint32_t total_bytes = num_bytes + upload_buffer_bytes_used_;
|
uint32_t total_bytes = num_bytes + dynamic_buffer.bytes_used_;
|
||||||
|
|
||||||
// Determine the amount of padding needed to meet the target alignment.
|
// 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 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 > upload_buffer_size_) {
|
if (total_bytes + padding_bytes > dynamic_buffer.size_) {
|
||||||
resize_upload_buffer(total_bytes + total_bytes / 2);
|
resize_dynamic_buffer(dynamic_buffer, total_bytes + total_bytes / 2);
|
||||||
|
|
||||||
upload_buffer_bytes_used_ += num_bytes;
|
dynamic_buffer.bytes_used_ += num_bytes;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise allocate the padding and required bytes and offset the allocated position by the padding size.
|
// 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;
|
return allocate_dynamic_data(dynamic_buffer, 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderGeometry(Rml::Vertex* vertices, int num_vertices, int* indices, int num_indices, Rml::TextureHandle texture, const Rml::Vector2f& translation) override {
|
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 (!textures_.contains(texture)) {
|
||||||
if (texture == 0) {
|
if (texture == 0) {
|
||||||
// Create a 1x1 pixel white texture as the first handle
|
// 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);
|
// Copy the vertex and index data into the mapped buffers.
|
||||||
|
uint32_t vert_size_bytes = num_vertices * sizeof(*vertices);
|
||||||
if (vert_size_bytes > vertex_buffer_size_) {
|
uint32_t index_size_bytes = num_indices * sizeof(*indices);
|
||||||
resize_vertex_buffer(vert_size_bytes + vert_size_bytes / 2);
|
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);
|
||||||
if (index_size_bytes > index_buffer_size_) {
|
memcpy(index_buffer_.mapped_data_ + index_buffer_offset, indices, index_size_bytes);
|
||||||
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)));
|
|
||||||
|
|
||||||
list_->setViewports(RT64::RenderViewport{ 0, 0, float(window_width_), float(window_height_) });
|
list_->setViewports(RT64::RenderViewport{ 0, 0, float(window_width_), float(window_height_) });
|
||||||
if (scissor_enabled_) {
|
if (scissor_enabled_) {
|
||||||
|
|
@ -399,9 +369,9 @@ public:
|
||||||
list_->setScissors(RT64::RenderRect{ 0, 0, window_width_, window_height_ });
|
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);
|
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_->setVertexBuffers(0, &vertex_view, 1, &vertex_slot_);
|
||||||
list_->setGraphicsDescriptorSet(textures_.at(texture).set.get(), 1);
|
list_->setGraphicsDescriptorSet(textures_.at(texture).set.get(), 1);
|
||||||
|
|
||||||
|
|
@ -522,10 +492,10 @@ public:
|
||||||
uint32_t uploaded_size_bytes = row_byte_width * source_dimensions.y;
|
uint32_t uploaded_size_bytes = row_byte_width * source_dimensions.y;
|
||||||
|
|
||||||
// Allocate room in the upload buffer for the uploaded data.
|
// 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.
|
// 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) {
|
if (row_byte_padding == 0) {
|
||||||
// Copy row-by-row if the image is flipped.
|
// Copy row-by-row if the image is flipped.
|
||||||
|
|
@ -557,7 +527,7 @@ public:
|
||||||
// Copy the upload buffer into the texture.
|
// Copy the upload buffer into the texture.
|
||||||
list_->copyTextureRegion(
|
list_->copyTextureRegion(
|
||||||
RT64::RenderTextureCopyLocation::Subresource(texture.get()),
|
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.
|
// Prepare the texture for being read from a pixel shader.
|
||||||
list_->barriers(RT64::RenderBarrierStage::GRAPHICS, RT64::RenderTextureBarrier(texture.get(), RT64::RenderTextureLayout::SHADER_READ));
|
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.
|
// Clear out any stale buffers from the last command list.
|
||||||
stale_buffers_.clear();
|
stale_buffers_.clear();
|
||||||
|
|
||||||
// Reset and map the upload buffer.
|
// Reset buffers.
|
||||||
upload_buffer_bytes_used_ = 0;
|
reset_dynamic_buffer(upload_buffer_);
|
||||||
upload_buffer_mapped_data_ = reinterpret_cast<uint8_t*>(upload_buffer_->map());
|
reset_dynamic_buffer(vertex_buffer_);
|
||||||
|
reset_dynamic_buffer(index_buffer_);
|
||||||
|
|
||||||
// Set an internal texture as the render target if MSAA is enabled.
|
// Set an internal texture as the render target if MSAA is enabled.
|
||||||
if (multisampling_.sampleCount > 1) {
|
if (multisampling_.sampleCount > 1) {
|
||||||
|
|
@ -661,13 +632,11 @@ public:
|
||||||
list->drawInstanced(3, 1, 0, 0);
|
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.
|
list_ = nullptr;
|
||||||
if (upload_buffer_mapped_data_) {
|
|
||||||
upload_buffer_->unmap();
|
|
||||||
upload_buffer_mapped_data_ = nullptr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue