mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2024-11-26 08:05:38 +00:00
Merge pull request #404 from Koerty/grid-metadata
[GRID 4] Add metadata from the detailed view to the grid view
This commit is contained in:
commit
016956703a
33
THEMES.md
33
THEMES.md
|
@ -472,6 +472,39 @@ Reference
|
||||||
* `image name="logo"` - ALL
|
* `image name="logo"` - ALL
|
||||||
- A header image. If a non-empty `path` is specified, `text name="headerText"` will be hidden and this image will be, by default, displayed roughly in its place.
|
- A header image. If a non-empty `path` is specified, `text name="headerText"` will be hidden and this image will be, by default, displayed roughly in its place.
|
||||||
|
|
||||||
|
* Metadata
|
||||||
|
* Labels
|
||||||
|
* `text name="md_lbl_rating"` - ALL
|
||||||
|
* `text name="md_lbl_releasedate"` - ALL
|
||||||
|
* `text name="md_lbl_developer"` - ALL
|
||||||
|
* `text name="md_lbl_publisher"` - ALL
|
||||||
|
* `text name="md_lbl_genre"` - ALL
|
||||||
|
* `text name="md_lbl_players"` - ALL
|
||||||
|
* `text name="md_lbl_lastplayed"` - ALL
|
||||||
|
* `text name="md_lbl_playcount"` - ALL
|
||||||
|
|
||||||
|
* Values
|
||||||
|
* All values will follow to the right of their labels if a position isn't specified.
|
||||||
|
|
||||||
|
* `rating name="md_rating"` - ALL
|
||||||
|
- The "rating" metadata.
|
||||||
|
* `datetime name="md_releasedate"` - ALL
|
||||||
|
- The "releasedate" metadata.
|
||||||
|
* `text name="md_developer"` - ALL
|
||||||
|
- The "developer" metadata.
|
||||||
|
* `text name="md_publisher"` - ALL
|
||||||
|
- The "publisher" metadata.
|
||||||
|
* `text name="md_genre"` - ALL
|
||||||
|
- The "genre" metadata.
|
||||||
|
* `text name="md_players"` - ALL
|
||||||
|
- The "players" metadata (number of players the game supports).
|
||||||
|
* `datetime name="md_lastplayed"` - ALL
|
||||||
|
- The "lastplayed" metadata. Displayed as a string representing the time relative to "now" (e.g. "3 hours ago").
|
||||||
|
* `text name="md_playcount"` - ALL
|
||||||
|
- The "playcount" metadata (number of times the game has been played).
|
||||||
|
* `text name="md_description"` - POSITION | SIZE | FONT_PATH | FONT_SIZE | COLOR | Z_INDEX
|
||||||
|
- Text is the "desc" metadata. If no `pos`/`size` is specified, will move and resize to fit under the lowest label and reach to the bottom of the screen.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
#### system
|
#### system
|
||||||
|
|
|
@ -1,19 +1,73 @@
|
||||||
#include "views/gamelist/GridGameListView.h"
|
#include "views/gamelist/GridGameListView.h"
|
||||||
|
|
||||||
|
#include "animations/LambdaAnimation.h"
|
||||||
#include "views/UIModeController.h"
|
#include "views/UIModeController.h"
|
||||||
#include "views/ViewController.h"
|
#include "views/ViewController.h"
|
||||||
#include "CollectionSystemManager.h"
|
#include "CollectionSystemManager.h"
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
#include "SystemData.h"
|
#include "SystemData.h"
|
||||||
|
|
||||||
GridGameListView::GridGameListView(Window* window, FileData* root) : ISimpleGameListView(window, root),
|
GridGameListView::GridGameListView(Window* window, FileData* root) :
|
||||||
mGrid(window)
|
ISimpleGameListView(window, root),
|
||||||
|
mGrid(window),
|
||||||
|
mDescContainer(window), mDescription(window),
|
||||||
|
|
||||||
|
mLblRating(window), mLblReleaseDate(window), mLblDeveloper(window), mLblPublisher(window),
|
||||||
|
mLblGenre(window), mLblPlayers(window), mLblLastPlayed(window), mLblPlayCount(window),
|
||||||
|
|
||||||
|
mRating(window), mReleaseDate(window), mDeveloper(window), mPublisher(window),
|
||||||
|
mGenre(window), mPlayers(window), mLastPlayed(window), mPlayCount(window)
|
||||||
{
|
{
|
||||||
|
const float padding = 0.01f;
|
||||||
|
|
||||||
mGrid.setPosition(mSize.x() * 0.1f, mSize.y() * 0.1f);
|
mGrid.setPosition(mSize.x() * 0.1f, mSize.y() * 0.1f);
|
||||||
// mGrid.setSize(mSize.x(), mSize.y() * 0.8f);
|
// mGrid.setSize(mSize.x(), mSize.y() * 0.8f);
|
||||||
|
mGrid.setCursorChangedCallback([&](const CursorState& /*state*/) { updateInfoPanel(); });
|
||||||
addChild(&mGrid);
|
addChild(&mGrid);
|
||||||
|
|
||||||
populateList(root->getChildrenListToDisplay());
|
populateList(root->getChildrenListToDisplay());
|
||||||
|
|
||||||
|
// metadata labels + values
|
||||||
|
mLblRating.setText("Rating: ");
|
||||||
|
addChild(&mLblRating);
|
||||||
|
addChild(&mRating);
|
||||||
|
mLblReleaseDate.setText("Released: ");
|
||||||
|
addChild(&mLblReleaseDate);
|
||||||
|
addChild(&mReleaseDate);
|
||||||
|
mLblDeveloper.setText("Developer: ");
|
||||||
|
addChild(&mLblDeveloper);
|
||||||
|
addChild(&mDeveloper);
|
||||||
|
mLblPublisher.setText("Publisher: ");
|
||||||
|
addChild(&mLblPublisher);
|
||||||
|
addChild(&mPublisher);
|
||||||
|
mLblGenre.setText("Genre: ");
|
||||||
|
addChild(&mLblGenre);
|
||||||
|
addChild(&mGenre);
|
||||||
|
mLblPlayers.setText("Players: ");
|
||||||
|
addChild(&mLblPlayers);
|
||||||
|
addChild(&mPlayers);
|
||||||
|
mLblLastPlayed.setText("Last played: ");
|
||||||
|
addChild(&mLblLastPlayed);
|
||||||
|
mLastPlayed.setDisplayMode(DateTimeComponent::DISP_RELATIVE_TO_NOW);
|
||||||
|
addChild(&mLastPlayed);
|
||||||
|
mLblPlayCount.setText("Times played: ");
|
||||||
|
addChild(&mLblPlayCount);
|
||||||
|
addChild(&mPlayCount);
|
||||||
|
|
||||||
|
mDescContainer.setPosition(mSize.x() * padding, mSize.y() * 0.65f);
|
||||||
|
mDescContainer.setSize(mSize.x() * (0.50f - 2*padding), mSize.y() - mDescContainer.getPosition().y());
|
||||||
|
mDescContainer.setAutoScroll(true);
|
||||||
|
mDescContainer.setDefaultZIndex(40);
|
||||||
|
addChild(&mDescContainer);
|
||||||
|
|
||||||
|
mDescription.setFont(Font::get(FONT_SIZE_SMALL));
|
||||||
|
mDescription.setSize(mDescContainer.getSize().x(), 0);
|
||||||
|
mDescContainer.addChild(&mDescription);
|
||||||
|
|
||||||
|
|
||||||
|
initMDLabels();
|
||||||
|
initMDValues();
|
||||||
|
updateInfoPanel();
|
||||||
}
|
}
|
||||||
|
|
||||||
FileData* GridGameListView::getCursor()
|
FileData* GridGameListView::getCursor()
|
||||||
|
@ -65,6 +119,164 @@ void GridGameListView::populateList(const std::vector<FileData*>& files)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GridGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& theme)
|
||||||
|
{
|
||||||
|
ISimpleGameListView::onThemeChanged(theme);
|
||||||
|
|
||||||
|
using namespace ThemeFlags;
|
||||||
|
|
||||||
|
initMDLabels();
|
||||||
|
std::vector<TextComponent*> labels = getMDLabels();
|
||||||
|
assert(labels.size() == 8);
|
||||||
|
const char* lblElements[8] = {
|
||||||
|
"md_lbl_rating", "md_lbl_releasedate", "md_lbl_developer", "md_lbl_publisher",
|
||||||
|
"md_lbl_genre", "md_lbl_players", "md_lbl_lastplayed", "md_lbl_playcount"
|
||||||
|
};
|
||||||
|
|
||||||
|
for(unsigned int i = 0; i < labels.size(); i++)
|
||||||
|
{
|
||||||
|
labels[i]->applyTheme(theme, getName(), lblElements[i], ALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
initMDValues();
|
||||||
|
std::vector<GuiComponent*> values = getMDValues();
|
||||||
|
assert(values.size() == 8);
|
||||||
|
const char* valElements[8] = {
|
||||||
|
"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);
|
||||||
|
}
|
||||||
|
|
||||||
|
mDescContainer.applyTheme(theme, getName(), "md_description", POSITION | ThemeFlags::SIZE | Z_INDEX);
|
||||||
|
mDescription.setSize(mDescContainer.getSize().x(), 0);
|
||||||
|
mDescription.applyTheme(theme, getName(), "md_description", ALL ^ (POSITION | ThemeFlags::SIZE | ThemeFlags::ORIGIN | TEXT | ROTATION));
|
||||||
|
|
||||||
|
sortChildren();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GridGameListView::initMDLabels()
|
||||||
|
{
|
||||||
|
std::vector<TextComponent*> components = getMDLabels();
|
||||||
|
|
||||||
|
const unsigned int colCount = 2;
|
||||||
|
const unsigned int rowCount = (int)(components.size() / 2);
|
||||||
|
|
||||||
|
Vector3f start(mSize.x() * 0.01f, mSize.y() * 0.625f, 0.0f);
|
||||||
|
|
||||||
|
const float colSize = (mSize.x() * 0.48f) / colCount;
|
||||||
|
const float rowPadding = 0.01f * mSize.y();
|
||||||
|
|
||||||
|
for(unsigned int i = 0; i < components.size(); i++)
|
||||||
|
{
|
||||||
|
const unsigned int row = i % rowCount;
|
||||||
|
Vector3f pos(0.0f, 0.0f, 0.0f);
|
||||||
|
if(row == 0)
|
||||||
|
{
|
||||||
|
pos = start + Vector3f(colSize * (i / rowCount), 0, 0);
|
||||||
|
}else{
|
||||||
|
// work from the last component
|
||||||
|
GuiComponent* lc = components[i-1];
|
||||||
|
pos = lc->getPosition() + Vector3f(0, lc->getSize().y() + rowPadding, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
components[i]->setFont(Font::get(FONT_SIZE_SMALL));
|
||||||
|
components[i]->setPosition(pos);
|
||||||
|
components[i]->setDefaultZIndex(40);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GridGameListView::initMDValues()
|
||||||
|
{
|
||||||
|
std::vector<TextComponent*> labels = getMDLabels();
|
||||||
|
std::vector<GuiComponent*> values = getMDValues();
|
||||||
|
|
||||||
|
std::shared_ptr<Font> defaultFont = Font::get(FONT_SIZE_SMALL);
|
||||||
|
mRating.setSize(defaultFont->getHeight() * 5.0f, (float)defaultFont->getHeight());
|
||||||
|
mReleaseDate.setFont(defaultFont);
|
||||||
|
mDeveloper.setFont(defaultFont);
|
||||||
|
mPublisher.setFont(defaultFont);
|
||||||
|
mGenre.setFont(defaultFont);
|
||||||
|
mPlayers.setFont(defaultFont);
|
||||||
|
mLastPlayed.setFont(defaultFont);
|
||||||
|
mPlayCount.setFont(defaultFont);
|
||||||
|
|
||||||
|
float bottom = 0.0f;
|
||||||
|
|
||||||
|
const float colSize = (mSize.x() * 0.48f) / 2;
|
||||||
|
for(unsigned int i = 0; i < labels.size(); i++)
|
||||||
|
{
|
||||||
|
const float heightDiff = (labels[i]->getSize().y() - values[i]->getSize().y()) / 2;
|
||||||
|
values[i]->setPosition(labels[i]->getPosition() + Vector3f(labels[i]->getSize().x(), heightDiff, 0));
|
||||||
|
values[i]->setSize(colSize - labels[i]->getSize().x(), values[i]->getSize().y());
|
||||||
|
values[i]->setDefaultZIndex(40);
|
||||||
|
|
||||||
|
float testBot = values[i]->getPosition().y() + values[i]->getSize().y();
|
||||||
|
if(testBot > bottom)
|
||||||
|
bottom = testBot;
|
||||||
|
}
|
||||||
|
|
||||||
|
mDescContainer.setPosition(mDescContainer.getPosition().x(), bottom + mSize.y() * 0.01f);
|
||||||
|
mDescContainer.setSize(mDescContainer.getSize().x(), mSize.y() - mDescContainer.getPosition().y());
|
||||||
|
}
|
||||||
|
|
||||||
|
void GridGameListView::updateInfoPanel()
|
||||||
|
{
|
||||||
|
FileData* file = (mGrid.size() == 0 || mGrid.isScrolling()) ? NULL : mGrid.getSelected();
|
||||||
|
|
||||||
|
bool fadingOut;
|
||||||
|
if(file == NULL)
|
||||||
|
{
|
||||||
|
//mDescription.setText("");
|
||||||
|
fadingOut = true;
|
||||||
|
}else{
|
||||||
|
mDescription.setText(file->metadata.get("desc"));
|
||||||
|
mDescContainer.reset();
|
||||||
|
|
||||||
|
mRating.setValue(file->metadata.get("rating"));
|
||||||
|
mReleaseDate.setValue(file->metadata.get("releasedate"));
|
||||||
|
mDeveloper.setValue(file->metadata.get("developer"));
|
||||||
|
mPublisher.setValue(file->metadata.get("publisher"));
|
||||||
|
mGenre.setValue(file->metadata.get("genre"));
|
||||||
|
mPlayers.setValue(file->metadata.get("players"));
|
||||||
|
|
||||||
|
if(file->getType() == GAME)
|
||||||
|
{
|
||||||
|
mLastPlayed.setValue(file->metadata.get("lastplayed"));
|
||||||
|
mPlayCount.setValue(file->metadata.get("playcount"));
|
||||||
|
}
|
||||||
|
|
||||||
|
fadingOut = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<GuiComponent*> comps = getMDValues();
|
||||||
|
comps.push_back(&mDescription);
|
||||||
|
std::vector<TextComponent*> labels = getMDLabels();
|
||||||
|
comps.insert(comps.cend(), labels.cbegin(), labels.cend());
|
||||||
|
|
||||||
|
for(auto it = comps.cbegin(); it != comps.cend(); it++)
|
||||||
|
{
|
||||||
|
GuiComponent* comp = *it;
|
||||||
|
// an animation is playing
|
||||||
|
// then animate if reverse != fadingOut
|
||||||
|
// an animation is not playing
|
||||||
|
// then animate if opacity != our target opacity
|
||||||
|
if((comp->isAnimationPlaying(0) && comp->isAnimationReversed(0) != fadingOut) ||
|
||||||
|
(!comp->isAnimationPlaying(0) && comp->getOpacity() != (fadingOut ? 0 : 255)))
|
||||||
|
{
|
||||||
|
auto func = [comp](float t)
|
||||||
|
{
|
||||||
|
comp->setOpacity((unsigned char)(Math::lerp(0.0f, 1.0f, t)*255));
|
||||||
|
};
|
||||||
|
comp->setAnimation(new LambdaAnimation(func, 150), 0, nullptr, fadingOut);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void GridGameListView::addPlaceholder()
|
void GridGameListView::addPlaceholder()
|
||||||
{
|
{
|
||||||
// empty grid - add a placeholder
|
// empty grid - add a placeholder
|
||||||
|
@ -106,6 +318,34 @@ void GridGameListView::remove(FileData *game, bool deleteFile)
|
||||||
onFileChanged(parent, FILE_REMOVED); // update the view, with game removed
|
onFileChanged(parent, FILE_REMOVED); // update the view, with game removed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<TextComponent*> GridGameListView::getMDLabels()
|
||||||
|
{
|
||||||
|
std::vector<TextComponent*> ret;
|
||||||
|
ret.push_back(&mLblRating);
|
||||||
|
ret.push_back(&mLblReleaseDate);
|
||||||
|
ret.push_back(&mLblDeveloper);
|
||||||
|
ret.push_back(&mLblPublisher);
|
||||||
|
ret.push_back(&mLblGenre);
|
||||||
|
ret.push_back(&mLblPlayers);
|
||||||
|
ret.push_back(&mLblLastPlayed);
|
||||||
|
ret.push_back(&mLblPlayCount);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<GuiComponent*> GridGameListView::getMDValues()
|
||||||
|
{
|
||||||
|
std::vector<GuiComponent*> ret;
|
||||||
|
ret.push_back(&mRating);
|
||||||
|
ret.push_back(&mReleaseDate);
|
||||||
|
ret.push_back(&mDeveloper);
|
||||||
|
ret.push_back(&mPublisher);
|
||||||
|
ret.push_back(&mGenre);
|
||||||
|
ret.push_back(&mPlayers);
|
||||||
|
ret.push_back(&mLastPlayed);
|
||||||
|
ret.push_back(&mPlayCount);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<HelpPrompt> GridGameListView::getHelpPrompts()
|
std::vector<HelpPrompt> GridGameListView::getHelpPrompts()
|
||||||
{
|
{
|
||||||
std::vector<HelpPrompt> prompts;
|
std::vector<HelpPrompt> prompts;
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
#ifndef ES_APP_VIEWS_GAME_LIST_GRID_GAME_LIST_VIEW_H
|
#ifndef ES_APP_VIEWS_GAME_LIST_GRID_GAME_LIST_VIEW_H
|
||||||
#define ES_APP_VIEWS_GAME_LIST_GRID_GAME_LIST_VIEW_H
|
#define ES_APP_VIEWS_GAME_LIST_GRID_GAME_LIST_VIEW_H
|
||||||
|
|
||||||
|
#include "components/DateTimeComponent.h"
|
||||||
|
#include "components/RatingComponent.h"
|
||||||
|
#include "components/ScrollableContainer.h"
|
||||||
#include "components/ImageGridComponent.h"
|
#include "components/ImageGridComponent.h"
|
||||||
#include "views/gamelist/ISimpleGameListView.h"
|
#include "views/gamelist/ISimpleGameListView.h"
|
||||||
|
|
||||||
|
@ -10,7 +13,7 @@ class GridGameListView : public ISimpleGameListView
|
||||||
public:
|
public:
|
||||||
GridGameListView(Window* window, FileData* root);
|
GridGameListView(Window* window, FileData* root);
|
||||||
|
|
||||||
//virtual void onThemeChanged(const std::shared_ptr<ThemeData>& theme) override;
|
virtual void onThemeChanged(const std::shared_ptr<ThemeData>& theme) override;
|
||||||
|
|
||||||
virtual FileData* getCursor() override;
|
virtual FileData* getCursor() override;
|
||||||
virtual void setCursor(FileData*) override;
|
virtual void setCursor(FileData*) override;
|
||||||
|
@ -30,6 +33,29 @@ protected:
|
||||||
virtual void addPlaceholder();
|
virtual void addPlaceholder();
|
||||||
|
|
||||||
ImageGridComponent<FileData*> mGrid;
|
ImageGridComponent<FileData*> mGrid;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void updateInfoPanel();
|
||||||
|
|
||||||
|
void initMDLabels();
|
||||||
|
void initMDValues();
|
||||||
|
|
||||||
|
TextComponent mLblRating, mLblReleaseDate, mLblDeveloper, mLblPublisher, mLblGenre, mLblPlayers, mLblLastPlayed, mLblPlayCount;
|
||||||
|
|
||||||
|
RatingComponent mRating;
|
||||||
|
DateTimeComponent mReleaseDate;
|
||||||
|
TextComponent mDeveloper;
|
||||||
|
TextComponent mPublisher;
|
||||||
|
TextComponent mGenre;
|
||||||
|
TextComponent mPlayers;
|
||||||
|
DateTimeComponent mLastPlayed;
|
||||||
|
TextComponent mPlayCount;
|
||||||
|
|
||||||
|
std::vector<TextComponent*> getMDLabels();
|
||||||
|
std::vector<GuiComponent*> getMDValues();
|
||||||
|
|
||||||
|
ScrollableContainer mDescContainer;
|
||||||
|
TextComponent mDescription;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ES_APP_VIEWS_GAME_LIST_GRID_GAME_LIST_VIEW_H
|
#endif // ES_APP_VIEWS_GAME_LIST_GRID_GAME_LIST_VIEW_H
|
||||||
|
|
|
@ -39,6 +39,8 @@ public:
|
||||||
void update(int deltaTime) override;
|
void update(int deltaTime) override;
|
||||||
void render(const Transform4x4f& parentTrans) override;
|
void render(const Transform4x4f& parentTrans) override;
|
||||||
|
|
||||||
|
inline void setCursorChangedCallback(const std::function<void(CursorState state)>& func) { mCursorChangedCallback = func; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Calculate how much tiles of size mTileMaxSize we can fit in a grid of size mSize using a margin of size mMargin
|
// Calculate how much tiles of size mTileMaxSize we can fit in a grid of size mSize using a margin of size mMargin
|
||||||
Vector2i getGridDimension() const
|
Vector2i getGridDimension() const
|
||||||
|
@ -54,6 +56,8 @@ private:
|
||||||
|
|
||||||
virtual void onCursorChanged(const CursorState& state);
|
virtual void onCursorChanged(const CursorState& state);
|
||||||
|
|
||||||
|
std::function<void(CursorState state)> mCursorChangedCallback;
|
||||||
|
|
||||||
bool mEntriesDirty;
|
bool mEntriesDirty;
|
||||||
|
|
||||||
Vector2f mMargin;
|
Vector2f mMargin;
|
||||||
|
@ -170,9 +174,12 @@ void ImageGridComponent<T>::render(const Transform4x4f& parentTrans)
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void ImageGridComponent<T>::onCursorChanged(const CursorState& /*state*/)
|
void ImageGridComponent<T>::onCursorChanged(const CursorState& state)
|
||||||
{
|
{
|
||||||
updateImages();
|
updateImages();
|
||||||
|
|
||||||
|
if(mCursorChangedCallback)
|
||||||
|
mCursorChangedCallback(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
|
Loading…
Reference in a new issue