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:
John Rassa 2018-04-06 19:34:21 -07:00 committed by GitHub
commit 016956703a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 310 additions and 4 deletions

View file

@ -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

View file

@ -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;

View file

@ -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

View file

@ -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>