Further improvements to the badges code.

This commit is contained in:
Leon Styhre 2021-10-12 22:53:02 +02:00
parent 848277141a
commit ae96cb4c54
10 changed files with 214 additions and 136 deletions

View file

@ -29,7 +29,6 @@ DetailedGameListView::DetailedGameListView(Window* window, FileData* root)
, mLblPlayers(window)
, mLblLastPlayed(window)
, mLblPlayCount(window)
, mBadges(window)
, mRating(window)
, mReleaseDate(window)
, mDeveloper(window)
@ -39,6 +38,7 @@ DetailedGameListView::DetailedGameListView(Window* window, FileData* root)
, mLastPlayed(window)
, mPlayCount(window)
, mName(window)
, mBadges(window)
, mDescContainer(window)
, mDescription(window)
, mGamelistInfo(window)
@ -94,7 +94,6 @@ DetailedGameListView::DetailedGameListView(Window* window, FileData* root)
mLblPlayers.setText("Players: ");
addChild(&mLblPlayers);
addChild(&mPlayers);
addChild(&mBadges);
mLblLastPlayed.setText("Last played: ");
addChild(&mLblLastPlayed);
mLastPlayed.setDisplayRelative(true);
@ -103,6 +102,13 @@ DetailedGameListView::DetailedGameListView(Window* window, FileData* root)
addChild(&mLblPlayCount);
addChild(&mPlayCount);
// Badges.
addChild(&mBadges);
mBadges.setOrigin(0.0f, 0.0f);
mBadges.setPosition(mSize.x * 0.8f, mSize.y * 0.7f);
mBadges.setSize(mSize.x * 0.15, mSize.y * 0.2f);
mBadges.setDefaultZIndex(50.0f);
mName.setPosition(mSize.x, mSize.y);
mName.setDefaultZIndex(40.0f);
mName.setColor(0xAAAAAAFF);
@ -143,6 +149,7 @@ void DetailedGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& them
mImage.applyTheme(theme, getName(), "md_image",
POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE);
mName.applyTheme(theme, getName(), "md_name", ALL);
mBadges.applyTheme(theme, getName(), "md_badges", ALL);
initMDLabels();
std::vector<TextComponent*> labels = getMDLabels();
@ -156,10 +163,10 @@ void DetailedGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& them
initMDValues();
std::vector<GuiComponent*> values = getMDValues();
assert(values.size() == 9);
std::vector<std::string> valElements = {"md_rating", "md_releasedate", "md_developer",
"md_publisher", "md_genre", "md_players",
"md_badges", "md_lastplayed", "md_playcount"};
assert(values.size() == 8);
std::vector<std::string> valElements = {"md_rating", "md_releasedate", "md_developer",
"md_publisher", "md_genre", "md_players",
"md_lastplayed", "md_playcount"};
for (unsigned int i = 0; i < values.size(); i++)
values[i]->applyTheme(theme, getName(), valElements[i], ALL ^ ThemeFlags::TEXT);
@ -226,8 +233,6 @@ void DetailedGameListView::initMDValues()
mLastPlayed.setFont(defaultFont);
mPlayCount.setFont(defaultFont);
mBadges.setSize(defaultFont->getHeight() * 5.0f, static_cast<float>(defaultFont->getHeight()));
float bottom = 0.0f;
const float colSize = (mSize.x * 0.48f) / 2.0f;
@ -297,11 +302,11 @@ void DetailedGameListView::updateInfoPanel()
mGenre.setVisible(false);
mLblPlayers.setVisible(false);
mPlayers.setVisible(false);
mBadges.setVisible(false);
mLblLastPlayed.setVisible(false);
mLastPlayed.setVisible(false);
mLblPlayCount.setVisible(false);
mPlayCount.setVisible(false);
mBadges.setVisible(false);
}
else {
mLblRating.setVisible(true);
@ -316,11 +321,11 @@ void DetailedGameListView::updateInfoPanel()
mGenre.setVisible(true);
mLblPlayers.setVisible(true);
mPlayers.setVisible(true);
mBadges.setVisible(true);
mLblLastPlayed.setVisible(true);
mLastPlayed.setVisible(true);
mLblPlayCount.setVisible(true);
mPlayCount.setVisible(true);
mBadges.setVisible(true);
}
bool fadingOut = false;
@ -443,6 +448,7 @@ void DetailedGameListView::updateInfoPanel()
comps.push_back(&mImage);
comps.push_back(&mDescription);
comps.push_back(&mName);
comps.push_back(&mBadges);
std::vector<TextComponent*> labels = getMDLabels();
comps.insert(comps.cend(), labels.cbegin(), labels.cend());
@ -488,7 +494,6 @@ std::vector<GuiComponent*> DetailedGameListView::getMDValues()
ret.push_back(&mPublisher);
ret.push_back(&mGenre);
ret.push_back(&mPlayers);
ret.push_back(&mBadges);
ret.push_back(&mLastPlayed);
ret.push_back(&mPlayCount);
return ret;

View file

@ -47,7 +47,6 @@ private:
TextComponent mLblLastPlayed;
TextComponent mLblPlayCount;
BadgesComponent mBadges;
RatingComponent mRating;
DateTimeComponent mReleaseDate;
TextComponent mDeveloper;
@ -57,6 +56,7 @@ private:
DateTimeComponent mLastPlayed;
TextComponent mPlayCount;
TextComponent mName;
BadgesComponent mBadges;
std::vector<TextComponent*> getMDLabels();
std::vector<GuiComponent*> getMDValues();

View file

@ -42,10 +42,10 @@ VideoGameListView::VideoGameListView(Window* window, FileData* root)
, mPublisher(window)
, mGenre(window)
, mPlayers(window)
, mBadges(window)
, mLastPlayed(window)
, mPlayCount(window)
, mName(window)
, mBadges(window)
, mDescContainer(window)
, mDescription(window)
, mGamelistInfo(window)
@ -111,7 +111,6 @@ VideoGameListView::VideoGameListView(Window* window, FileData* root)
mLblPlayers.setText("Players: ");
addChild(&mLblPlayers);
addChild(&mPlayers);
addChild(&mBadges);
mLblLastPlayed.setText("Last played: ");
addChild(&mLblLastPlayed);
mLastPlayed.setDisplayRelative(true);
@ -120,6 +119,13 @@ VideoGameListView::VideoGameListView(Window* window, FileData* root)
addChild(&mLblPlayCount);
addChild(&mPlayCount);
// Badges.
addChild(&mBadges);
mBadges.setOrigin(0.0f, 0.0f);
mBadges.setPosition(mSize.x * 0.8f, mSize.y * 0.7f);
mBadges.setSize(mSize.x * 0.15, mSize.y * 0.2f);
mBadges.setDefaultZIndex(50.0f);
mName.setPosition(mSize.x, mSize.y);
mName.setDefaultZIndex(40.0f);
mName.setColor(0xAAAAAAFF);
@ -165,6 +171,7 @@ void VideoGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& theme)
POSITION | ThemeFlags::SIZE | ThemeFlags::DELAY | Z_INDEX | ROTATION |
VISIBLE);
mName.applyTheme(theme, getName(), "md_name", ALL);
mBadges.applyTheme(theme, getName(), "md_badges", ALL);
initMDLabels();
std::vector<TextComponent*> labels = getMDLabels();
@ -178,10 +185,10 @@ void VideoGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& theme)
initMDValues();
std::vector<GuiComponent*> values = getMDValues();
assert(values.size() == 9);
std::vector<std::string> valElements = {"md_rating", "md_releasedate", "md_developer",
"md_publisher", "md_genre", "md_players",
"md_badges", "md_lastplayed", "md_playcount"};
assert(values.size() == 8);
std::vector<std::string> valElements = {"md_rating", "md_releasedate", "md_developer",
"md_publisher", "md_genre", "md_players",
"md_lastplayed", "md_playcount"};
for (unsigned int i = 0; i < values.size(); i++)
values[i]->applyTheme(theme, getName(), valElements[i], ALL ^ ThemeFlags::TEXT);
@ -245,9 +252,6 @@ void VideoGameListView::initMDValues()
mPublisher.setFont(defaultFont);
mGenre.setFont(defaultFont);
mPlayers.setFont(defaultFont);
mBadges.setSize(defaultFont->getHeight() * 5.0f, static_cast<float>(defaultFont->getHeight()));
mLastPlayed.setFont(defaultFont);
mPlayCount.setFont(defaultFont);
@ -320,11 +324,11 @@ void VideoGameListView::updateInfoPanel()
mGenre.setVisible(false);
mLblPlayers.setVisible(false);
mPlayers.setVisible(false);
mBadges.setVisible(false);
mLblLastPlayed.setVisible(false);
mLastPlayed.setVisible(false);
mLblPlayCount.setVisible(false);
mPlayCount.setVisible(false);
mBadges.setVisible(false);
}
else {
mLblRating.setVisible(true);
@ -339,11 +343,11 @@ void VideoGameListView::updateInfoPanel()
mGenre.setVisible(true);
mLblPlayers.setVisible(true);
mPlayers.setVisible(true);
mBadges.setVisible(true);
mLblLastPlayed.setVisible(true);
mLastPlayed.setVisible(true);
mLblPlayCount.setVisible(true);
mPlayCount.setVisible(true);
mBadges.setVisible(true);
}
bool fadingOut = false;
@ -484,6 +488,7 @@ void VideoGameListView::updateInfoPanel()
comps.push_back(mVideo);
comps.push_back(&mDescription);
comps.push_back(&mName);
comps.push_back(&mBadges);
std::vector<TextComponent*> labels = getMDLabels();
comps.insert(comps.cend(), labels.cbegin(), labels.cend());
@ -526,7 +531,6 @@ std::vector<GuiComponent*> VideoGameListView::getMDValues()
ret.push_back(&mPublisher);
ret.push_back(&mGenre);
ret.push_back(&mPlayers);
ret.push_back(&mBadges);
ret.push_back(&mLastPlayed);
ret.push_back(&mPlayCount);
return ret;

View file

@ -57,10 +57,10 @@ private:
TextComponent mPublisher;
TextComponent mGenre;
TextComponent mPlayers;
BadgesComponent mBadges;
DateTimeComponent mLastPlayed;
TextComponent mPlayCount;
TextComponent mName;
BadgesComponent mBadges;
std::vector<TextComponent*> getMDLabels();
std::vector<GuiComponent*> getMDValues();

View file

@ -150,10 +150,12 @@ std::map<std::string, std::map<std::string, ThemeData::ElementPropertyType>> The
{{"pos", NORMALIZED_PAIR},
{"size", NORMALIZED_PAIR},
{"origin", NORMALIZED_PAIR},
{"direction", STRING},
{"align", STRING},
{"itemsPerLine", FLOAT},
{"lines", FLOAT},
{"rotation", FLOAT},
{"rotationOrigin", NORMALIZED_PAIR},
{"alignment", STRING},
{"itemsPerRow", FLOAT},
{"rows", FLOAT},
{"itemPlacement", STRING},
{"itemMargin", NORMALIZED_PAIR},
{"slots", STRING},
{"customBadgeIcon", PATH},

View file

@ -54,7 +54,6 @@ namespace ThemeFlags
Z_INDEX = 8192,
ROTATION = 16384,
VISIBLE = 32768,
DIRECTION = 65536,
ALL = 0xFFFFFFFF
};
}

View file

@ -4,30 +4,31 @@
// BadgesComponent.cpp
//
// Game badges icons.
// Used by gamelist views.
// Used by the gamelist views.
//
#define SLOT_FAVORITE "favorite"
#define SLOT_COMPLETED "completed"
#define SLOT_KIDGAME "kidgame"
#define SLOT_BROKEN "broken"
#define SLOT_ALTERNATIVE_EMULATOR "altemulator"
#define SLOT_ALTEMULATOR "altemulator"
#include "components/BadgesComponent.h"
#include "Log.h"
#include "ThemeData.h"
#include "utils/StringUtil.h"
BadgesComponent::BadgesComponent(Window* window)
: FlexboxComponent{window, mBadgeImages}
, mBadgeTypes{
{SLOT_FAVORITE, SLOT_COMPLETED, SLOT_KIDGAME, SLOT_BROKEN, SLOT_ALTERNATIVE_EMULATOR}}
: GuiComponent{window}
, mFlexboxComponent{window, mBadgeImages}
, mBadgeTypes{{SLOT_FAVORITE, SLOT_COMPLETED, SLOT_KIDGAME, SLOT_BROKEN, SLOT_ALTEMULATOR}}
{
mBadgeIcons[SLOT_FAVORITE] = ":/graphics/badge_favorite.svg";
mBadgeIcons[SLOT_COMPLETED] = ":/graphics/badge_completed.svg";
mBadgeIcons[SLOT_KIDGAME] = ":/graphics/badge_kidgame.svg";
mBadgeIcons[SLOT_BROKEN] = ":/graphics/badge_broken.svg";
mBadgeIcons[SLOT_ALTERNATIVE_EMULATOR] = ":/graphics/badge_altemulator.svg";
mBadgeIcons[SLOT_ALTEMULATOR] = ":/graphics/badge_altemulator.svg";
}
void BadgesComponent::setBadges(const std::vector<std::string>& badges)
@ -52,12 +53,27 @@ void BadgesComponent::setBadges(const std::vector<std::string>& badges)
// Only recalculate the flexbox if any badges changed.
for (auto& image : mBadgeImages) {
if (prevVisibility[image.first] != image.second.isVisible()) {
onSizeChanged();
mFlexboxComponent.onSizeChanged();
break;
}
}
}
void BadgesComponent::render(const glm::mat4& parentTrans)
{
if (!isVisible())
return;
if (mOpacity == 255) {
mFlexboxComponent.render(parentTrans);
}
else {
mFlexboxComponent.setOpacity(mOpacity);
mFlexboxComponent.render(parentTrans);
mFlexboxComponent.setOpacity(255);
}
}
void BadgesComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
const std::string& view,
const std::string& element,
@ -69,6 +85,70 @@ void BadgesComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
if (!elem)
return;
if (elem->has("alignment")) {
const std::string alignment{elem->get<std::string>("alignment")};
if (alignment != "left" && alignment != "right") {
LOG(LogWarning) << "BadgesComponent: Invalid theme configuration, <alignment> set to \""
<< alignment << "\"";
}
else {
mFlexboxComponent.setAlignment(alignment);
}
}
if (elem->has("itemsPerRow")) {
const float itemsPerRow{elem->get<float>("itemsPerRow")};
if (itemsPerRow < 1.0f || itemsPerRow > 10.0f) {
LOG(LogWarning)
<< "BadgesComponent: Invalid theme configuration, <itemsPerRow> set to \""
<< itemsPerRow << "\"";
}
else {
mFlexboxComponent.setItemsPerLine(static_cast<unsigned int>(itemsPerRow));
}
}
if (elem->has("rows")) {
const float rows{elem->get<float>("rows")};
if (rows < 1.0f || rows > 10.0f) {
LOG(LogWarning) << "BadgesComponent: Invalid theme configuration, <rows> set to \""
<< rows << "\"";
}
else {
mFlexboxComponent.setLines(static_cast<unsigned int>(rows));
}
}
if (elem->has("itemPlacement")) {
std::string itemPlacement{elem->get<std::string>("itemPlacement")};
if (itemPlacement != "top" && itemPlacement != "center" && itemPlacement != "bottom" &&
itemPlacement != "stretch") {
LOG(LogWarning)
<< "BadgesComponent: Invalid theme configuration, <itemPlacement> set to \""
<< itemPlacement << "\"";
}
else {
if (itemPlacement == "top")
itemPlacement = "start";
else if (itemPlacement == "bottom")
itemPlacement = "end";
mFlexboxComponent.setItemPlacement(itemPlacement);
}
}
if (elem->has("itemMargin")) {
const glm::vec2 itemMargin = elem->get<glm::vec2>("itemMargin");
if (itemMargin.x < 0.0f || itemMargin.x > 100.0f || itemMargin.y < 0.0f ||
itemMargin.y > 100.0f) {
LOG(LogWarning)
<< "BadgesComponent: Invalid theme configuration, <itemMargin> set to \""
<< itemMargin.x << "x" << itemMargin.y << "\"";
}
else {
mFlexboxComponent.setItemMargin(itemMargin);
}
}
if (elem->has("slots")) {
std::vector<std::string> slots = Utils::String::delimitedStringToVector(
Utils::String::toLower(elem->get<std::string>("slots")), " ");
@ -89,7 +169,15 @@ void BadgesComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
}
}
// Apply theme on the flexbox component parent.
FlexboxComponent::applyTheme(theme, view, element, properties);
GuiComponent::applyTheme(theme, view, element, properties);
mFlexboxComponent.setPosition(mPosition);
mFlexboxComponent.setSize(mSize);
mFlexboxComponent.setOrigin(mOrigin);
mFlexboxComponent.setRotation(mRotation);
mFlexboxComponent.setRotationOrigin(mRotationOrigin);
mFlexboxComponent.setVisible(mVisible);
mFlexboxComponent.setDefaultZIndex(mDefaultZIndex);
mFlexboxComponent.setZIndex(mZIndex);
}
}

View file

@ -4,15 +4,16 @@
// BadgesComponent.h
//
// Game badges icons.
// Used by gamelist views.
// Used by the gamelist views.
//
#ifndef ES_CORE_COMPONENTS_BADGES_COMPONENT_H
#define ES_CORE_COMPONENTS_BADGES_COMPONENT_H
#include "FlexboxComponent.h"
#include "GuiComponent.h"
class BadgesComponent : public FlexboxComponent
class BadgesComponent : public GuiComponent
{
public:
BadgesComponent(Window* window);
@ -20,12 +21,17 @@ public:
std::vector<std::string> getBadgeTypes() { return mBadgeTypes; }
void setBadges(const std::vector<std::string>& badges);
void render(const glm::mat4& parentTrans) override;
void onSizeChanged() override { mFlexboxComponent.onSizeChanged(); }
virtual void applyTheme(const std::shared_ptr<ThemeData>& theme,
const std::string& view,
const std::string& element,
unsigned int properties) override;
private:
FlexboxComponent mFlexboxComponent;
std::vector<std::string> mBadgeTypes;
std::map<std::string, std::string> mBadgeIcons;
std::vector<std::pair<std::string, ImageComponent>> mBadgeImages;

View file

@ -4,28 +4,30 @@
// FlexboxComponent.cpp
//
// Flexbox layout component.
// Used by gamelist views.
//
#define DEFAULT_DIRECTION Direction::row
#define DEFAULT_ALIGN Align::center
#define DEFAULT_DIRECTION "row"
#define DEFAULT_ALIGNMENT "left"
#define DEFAULT_ITEMS_PER_LINE 4
#define DEFAULT_LINES 1
#define DEFAULT_LINES 2
#define DEFAULT_ITEM_PLACEMENT "center"
#define DEFAULT_MARGIN_X 10.0f
#define DEFAULT_MARGIN_Y 10.0f
#include "components/FlexboxComponent.h"
#include "Settings.h"
#include "ThemeData.h"
FlexboxComponent::FlexboxComponent(Window* window,
std::vector<std::pair<std::string, ImageComponent>>& images)
: GuiComponent{window}
, mDirection{DEFAULT_DIRECTION}
, mAlign{DEFAULT_ALIGN}
, mImages(images)
, mDirection{DEFAULT_DIRECTION}
, mAlignment{DEFAULT_ALIGNMENT}
, mItemsPerLine{DEFAULT_ITEMS_PER_LINE}
, mLines{DEFAULT_LINES}
, mItemPlacement{DEFAULT_ITEM_PLACEMENT}
, mItemMargin{glm::vec2{DEFAULT_MARGIN_X, DEFAULT_MARGIN_Y}}
, mLayoutValid{false}
{
@ -36,27 +38,30 @@ void FlexboxComponent::computeLayout()
// Start placing items in the top-left.
float anchorX{0.0f};
float anchorY{0.0f};
float anchorOriginX{0.0f};
float anchorOriginY{0.0f};
// Translation directions when placing items.
glm::ivec2 directionLine{1, 0};
glm::ivec2 directionRow{0, 1};
glm::vec2 directionLine{1, 0};
glm::vec2 directionRow{0, 1};
// Change direction.
if (mDirection == Direction::column) {
if (mDirection == "column") {
directionLine = {0, 1};
directionRow = {1, 0};
}
// Compute maximum image dimensions.
glm::vec2 grid;
if (mDirection == Direction::row)
if (mDirection == "row")
grid = {mItemsPerLine, mLines};
else
grid = {mLines, mItemsPerLine};
glm::vec2 maxItemSize{(mSize + mItemMargin - grid * mItemMargin) / grid};
if (grid.x * grid.y < static_cast<float>(mImages.size())) {
LOG(LogWarning) << "FlexboxComponent: Invalid theme configuration, the number of badges "
"exceeds the product of <lines> times <itemsPerLine>";
}
// Set final image dimensions.
for (auto& image : mImages) {
if (!image.second.isVisible())
@ -77,8 +82,6 @@ void FlexboxComponent::computeLayout()
}
// Pre-compute layout parameters.
float lineWidth = (mDirection == Direction::row ? (maxItemSize.y + mItemMargin.y) :
(maxItemSize.x + mItemMargin.x));
float anchorXStart{anchorX};
float anchorYStart{anchorY};
@ -92,30 +95,29 @@ void FlexboxComponent::computeLayout()
auto size{image.second.getSize()};
// Top-left anchor position.
float x{anchorX - anchorOriginX * size.x};
float y{anchorY - anchorOriginY * size.y};
float x{anchorX};
float y{anchorY};
// Apply alignment
if (mAlign == Align::end) {
// Apply alignment.
if (mItemPlacement == "end") {
x += directionLine.x == 0 ? (maxItemSize.x - size.x) : 0;
y += directionLine.y == 0 ? (maxItemSize.y - size.y) : 0;
}
else if (mAlign == Align::center) {
else if (mItemPlacement == "center") {
x += directionLine.x == 0 ? (maxItemSize.x - size.x) / 2 : 0;
y += directionLine.y == 0 ? (maxItemSize.y - size.y) / 2 : 0;
}
else if (mAlign == Align::stretch && mDirection == Direction::row) {
else if (mItemPlacement == "stretch" && mDirection == "row") {
image.second.setSize(image.second.getSize().x, maxItemSize.y);
}
// Apply origin.
if (mOrigin.x > 0 && mOrigin.x <= 1)
x -= mOrigin.x * mSize.x;
if (mOrigin.y > 0 && mOrigin.y <= 1)
y -= mOrigin.y * mSize.y;
// TODO: Doesn't work correctly.
// Apply overall container alignment.
if (mAlignment == "right")
x += (mSize.x - size.x * grid.x) - mItemMargin.x;
// Store final item position.
image.second.setPosition(getPosition().x + x, getPosition().y + y);
image.second.setPosition(x, y);
// Translate anchor.
if ((i++ + 1) % std::max(1, static_cast<int>(mItemsPerLine)) != 0) {
@ -126,11 +128,11 @@ void FlexboxComponent::computeLayout()
else {
// Translate to first position of next line.
if (directionRow.x == 0) {
anchorY += lineWidth * static_cast<float>(directionRow.y);
anchorY += size.y + mItemMargin.y;
anchorX = anchorXStart;
}
else {
anchorX += lineWidth * static_cast<float>(directionRow.x);
anchorX += size.x + mItemMargin.x;
anchorY = anchorYStart;
}
}
@ -147,47 +149,20 @@ void FlexboxComponent::render(const glm::mat4& parentTrans)
if (!mLayoutValid)
computeLayout();
for (auto& image : mImages)
image.second.render(parentTrans);
}
glm::mat4 trans{parentTrans * getTransform()};
Renderer::setMatrix(trans);
void FlexboxComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
const std::string& view,
const std::string& element,
unsigned int properties)
{
using namespace ThemeFlags;
if (Settings::getInstance()->getBool("DebugImage"))
Renderer::drawRect(0.0f, 0.0f, mSize.x, mSize.y, 0xFF000033, 0xFF000033);
glm::vec2 scale{getParent() ? getParent()->getSize() :
glm::vec2{static_cast<float>(Renderer::getScreenWidth()),
static_cast<float>(Renderer::getScreenHeight())}};
const ThemeData::ThemeElement* elem = theme->getElement(view, element, "badges");
if (!elem)
return;
if (properties & DIRECTION && elem->has("direction"))
mDirection =
elem->get<std::string>("direction") == "row" ? Direction::row : Direction::column;
if (elem->has("align")) {
const auto a = elem->get<std::string>("align");
mAlign = (a == "start" ?
Align::start :
(a == "end" ? Align::end : (a == "center" ? Align::center : Align::stretch)));
for (auto& image : mImages) {
if (mOpacity == 255) {
image.second.render(trans);
}
else {
image.second.setOpacity(mOpacity);
image.second.render(trans);
image.second.setOpacity(255);
}
}
if (elem->has("itemsPerLine"))
mItemsPerLine = elem->get<float>("itemsPerLine");
if (elem->has("lines"))
mLines = elem->get<float>("lines");
if (elem->has("itemMargin"))
mItemMargin = elem->get<glm::vec2>("itemMargin") * scale;
GuiComponent::applyTheme(theme, view, element, properties);
// Layout no longer valid.
mLayoutValid = false;
}

View file

@ -4,7 +4,6 @@
// FlexboxComponent.h
//
// Flexbox layout component.
// Used by gamelist views.
//
#ifndef ES_CORE_COMPONENTS_FLEXBOX_COMPONENT_H
@ -16,26 +15,21 @@
class FlexboxComponent : public GuiComponent
{
public:
enum class Direction : char {
row, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0).
column
};
enum class Align : char {
start, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0).
end,
center,
stretch
};
explicit FlexboxComponent(Window* window,
std::vector<std::pair<std::string, ImageComponent>>& images);
// Getters/Setters for rendering options.
Align getAlign() const { return mAlign; }
void setAlign(Align value)
// Getters/setters for the layout.
void setDirection(const std::string& direction)
{
mAlign = value;
assert(direction == "row" || direction == "column");
mDirection = direction;
}
std::string getAlignment() const { return mAlignment; }
void setAlignment(const std::string& value)
{
assert(value == "left" || value == "right");
mAlignment = value;
mLayoutValid = false;
}
@ -53,34 +47,39 @@ public:
mLayoutValid = false;
}
std::string getItemPlacement() const { return mItemPlacement; }
void setItemPlacement(const std::string& value)
{
assert(value == "start" || value == "center" || value == "end" || value == "stretch");
mItemPlacement = value;
mLayoutValid = false;
}
glm::vec2 getItemMargin() const { return mItemMargin; }
void setItemMargin(glm::vec2 value)
{
mItemMargin = value;
mItemMargin.x = std::roundf(value.x * Renderer::getScreenWidth());
mItemMargin.y = std::roundf(value.y * Renderer::getScreenHeight());
mLayoutValid = false;
}
void onSizeChanged() override { mLayoutValid = false; }
void render(const glm::mat4& parentTrans) override;
void applyTheme(const std::shared_ptr<ThemeData>& theme,
const std::string& view,
const std::string& element,
unsigned int properties) override;
private:
// Calculate flexbox layout.
void computeLayout();
// Layout options.
Direction mDirection;
Align mAlign;
std::vector<std::pair<std::string, ImageComponent>>& mImages;
// Layout options.
std::string mDirection;
std::string mAlignment;
unsigned int mItemsPerLine;
unsigned int mLines;
std::string mItemPlacement;
glm::vec2 mItemMargin;
bool mLayoutValid;
};