Skip to content

Commit

Permalink
Merge pull request RetroPie#404 from Koerty/grid-metadata
Browse files Browse the repository at this point in the history
[GRID 4] Add metadata from the detailed view to the grid view
  • Loading branch information
jrassa authored Apr 7, 2018
2 parents e82895f + a006650 commit 0169567
Show file tree
Hide file tree
Showing 4 changed files with 310 additions and 4 deletions.
33 changes: 33 additions & 0 deletions THEMES.md
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,39 @@ Reference
* `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.
* 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
Expand Down
244 changes: 242 additions & 2 deletions es-app/src/views/gamelist/GridGameListView.cpp
Original file line number Diff line number Diff line change
@@ -1,19 +1,73 @@
#include "views/gamelist/GridGameListView.h"

#include "animations/LambdaAnimation.h"
#include "views/UIModeController.h"
#include "views/ViewController.h"
#include "CollectionSystemManager.h"
#include "Settings.h"
#include "SystemData.h"

GridGameListView::GridGameListView(Window* window, FileData* root) : ISimpleGameListView(window, root),
mGrid(window)
GridGameListView::GridGameListView(Window* window, FileData* root) :
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.setSize(mSize.x(), mSize.y() * 0.8f);
mGrid.setCursorChangedCallback([&](const CursorState& /*state*/) { updateInfoPanel(); });
addChild(&mGrid);

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()
Expand Down Expand Up @@ -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()
{
// empty grid - add a placeholder
Expand Down Expand Up @@ -106,6 +318,34 @@ void GridGameListView::remove(FileData *game, bool deleteFile)
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> prompts;
Expand Down
28 changes: 27 additions & 1 deletion es-app/src/views/gamelist/GridGameListView.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
#ifndef 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 "views/gamelist/ISimpleGameListView.h"

Expand All @@ -10,7 +13,7 @@ class GridGameListView : public ISimpleGameListView
public:
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 void setCursor(FileData*) override;
Expand All @@ -30,6 +33,29 @@ class GridGameListView : public ISimpleGameListView
virtual void addPlaceholder();

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
9 changes: 8 additions & 1 deletion es-core/src/components/ImageGridComponent.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ class ImageGridComponent : public IList<ImageGridData, T>
void update(int deltaTime) override;
void render(const Transform4x4f& parentTrans) override;

inline void setCursorChangedCallback(const std::function<void(CursorState state)>& func) { mCursorChangedCallback = func; }

private:
// 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
Expand All @@ -54,6 +56,8 @@ class ImageGridComponent : public IList<ImageGridData, T>

virtual void onCursorChanged(const CursorState& state);

std::function<void(CursorState state)> mCursorChangedCallback;

bool mEntriesDirty;

Vector2f mMargin;
Expand Down Expand Up @@ -170,9 +174,12 @@ void ImageGridComponent<T>::render(const Transform4x4f& parentTrans)
}

template<typename T>
void ImageGridComponent<T>::onCursorChanged(const CursorState& /*state*/)
void ImageGridComponent<T>::onCursorChanged(const CursorState& state)
{
updateImages();

if(mCursorChangedCallback)
mCursorChangedCallback(state);
}

template<typename T>
Expand Down

0 comments on commit 0169567

Please sign in to comment.