mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2025-02-16 20:15:38 +00:00
Further improvements to the badges code.
This commit is contained in:
parent
848277141a
commit
ae96cb4c54
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -54,7 +54,6 @@ namespace ThemeFlags
|
|||
Z_INDEX = 8192,
|
||||
ROTATION = 16384,
|
||||
VISIBLE = 32768,
|
||||
DIRECTION = 65536,
|
||||
ALL = 0xFFFFFFFF
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue