diff --git a/es-app/src/guis/GuiScraperSearch.cpp b/es-app/src/guis/GuiScraperSearch.cpp index a78e00d38..d988f59c5 100644 --- a/es-app/src/guis/GuiScraperSearch.cpp +++ b/es-app/src/guis/GuiScraperSearch.cpp @@ -379,8 +379,10 @@ void GuiScraperSearch::updateInfoPane() // Metadata. if (Settings::getInstance()->getBool("ScrapeRatings") && - Settings::getInstance()->getString("Scraper") != "TheGamesDB") + Settings::getInstance()->getString("Scraper") != "TheGamesDB") { mMD_Rating->setValue(Utils::String::toUpper(res.mdl.get("rating"))); + mMD_Rating->setOpacity(255); + } mMD_ReleaseDate->setValue(Utils::String::toUpper(res.mdl.get("releasedate"))); mMD_Developer->setText(Utils::String::toUpper(res.mdl.get("developer"))); mMD_Publisher->setText(Utils::String::toUpper(res.mdl.get("publisher"))); @@ -395,9 +397,11 @@ void GuiScraperSearch::updateInfoPane() // Metadata. if (Settings::getInstance()->getBool("ScrapeRatings") && - Settings::getInstance()->getString("Scraper") != "TheGamesDB") + Settings::getInstance()->getString("Scraper") != "TheGamesDB") { mMD_Rating->setValue(""); - mMD_ReleaseDate->setValue(""); + mMD_Rating->setOpacity(0); + } + mMD_ReleaseDate->setValue("99990101T000000"); mMD_Developer->setText(""); mMD_Publisher->setText(""); mMD_Genre->setText(""); diff --git a/es-app/src/views/UIModeController.cpp b/es-app/src/views/UIModeController.cpp index 44c6e4361..f1fe6aeb3 100644 --- a/es-app/src/views/UIModeController.cpp +++ b/es-app/src/views/UIModeController.cpp @@ -2,7 +2,7 @@ // UIModeController.cpp // // Handling of application user interface modes (full, kiosk and kid). -// This includes switching the mode when the UI mode passkey was used. +// This includes switching the mode when the UI mode passkey is used. // #include "UIModeController.h" diff --git a/es-app/src/views/UIModeController.h b/es-app/src/views/UIModeController.h index 19ec08ed3..98d5469fc 100644 --- a/es-app/src/views/UIModeController.h +++ b/es-app/src/views/UIModeController.h @@ -2,7 +2,7 @@ // UIModeController.h // // Handling of application user interface modes (full, kiosk and kid). -// This includes switching the mode when the UI mode passkey was used. +// This includes switching the mode when the UI mode passkey is used. // #pragma once diff --git a/es-core/src/components/ComponentGrid.cpp b/es-core/src/components/ComponentGrid.cpp index 1ad0fb44c..b08992d0e 100644 --- a/es-core/src/components/ComponentGrid.cpp +++ b/es-core/src/components/ComponentGrid.cpp @@ -1,7 +1,7 @@ // // ComponentGrid.cpp // -// Providing basic layout of other components in an X*Y grid. +// Provides basic layout of components in an X*Y grid. // #include "components/ComponentGrid.h" diff --git a/es-core/src/components/ComponentGrid.h b/es-core/src/components/ComponentGrid.h index f6eaf39ea..de58f237e 100644 --- a/es-core/src/components/ComponentGrid.h +++ b/es-core/src/components/ComponentGrid.h @@ -1,7 +1,7 @@ // // ComponentGrid.h // -// Providing basic layout of other components in an X*Y grid. +// Provides basic layout of components in an X*Y grid. // #pragma once diff --git a/es-core/src/components/DateTimeComponent.cpp b/es-core/src/components/DateTimeComponent.cpp index ee4ac7428..9eb2f2f59 100644 --- a/es-core/src/components/DateTimeComponent.cpp +++ b/es-core/src/components/DateTimeComponent.cpp @@ -1,7 +1,9 @@ // // DateTimeComponent.cpp // -// Date and time component. +// Provides the date and time, in absolute (actual date) or relative +// (delta from current date and time) form. +// Used by the gamelist views. // #include "components/DateTimeComponent.h" @@ -13,7 +15,8 @@ DateTimeComponent::DateTimeComponent(Window* window) : TextComponent(window), mDisplayRelative(false) { - setFormat("%m/%d/%Y"); + // ISO 8601 date format. + setFormat("%Y-%m-%d"); } DateTimeComponent::DateTimeComponent( @@ -28,7 +31,8 @@ DateTimeComponent::DateTimeComponent( : TextComponent(window, text, font, color, align, pos, size, bgcolor), mDisplayRelative(false) { - setFormat("%m/%d/%Y"); + // ISO 8601 date format. + setFormat("%Y-%m-%d"); } void DateTimeComponent::setValue(const std::string& val) @@ -95,7 +99,6 @@ void DateTimeComponent::render(const Transform4x4f& parentTrans) TextComponent::render(parentTrans); } - void DateTimeComponent::applyTheme(const std::shared_ptr& theme, const std::string& view, const std::string& element, unsigned int properties) { diff --git a/es-core/src/components/DateTimeComponent.h b/es-core/src/components/DateTimeComponent.h index 69ac36e7d..09ade0895 100644 --- a/es-core/src/components/DateTimeComponent.h +++ b/es-core/src/components/DateTimeComponent.h @@ -1,7 +1,9 @@ // // DateTimeComponent.h // -// Date and time component. +// Provides the date and time, in absolute (actual date) or relative +// (delta from current date and time) form. +// Used by the gamelist views. // #pragma once @@ -13,7 +15,7 @@ class ThemeData; -// Used to display date times. +// Used to display date and time. class DateTimeComponent : public TextComponent { public: diff --git a/es-core/src/components/DateTimeEditComponent.cpp b/es-core/src/components/DateTimeEditComponent.cpp index 655a0082d..87044885c 100644 --- a/es-core/src/components/DateTimeEditComponent.cpp +++ b/es-core/src/components/DateTimeEditComponent.cpp @@ -63,15 +63,22 @@ bool DateTimeEditComponent::input(InputConfig* config, Input input) } int incDir = 0; - if (config->isMappedLike("up", input) || config->isMappedLike("leftshoulder", input)) + if (config->isMappedLike("up", input) || config->isMappedLike("rightshoulder", input)) incDir = 1; - else if (config->isMappedLike("down", input) || config->isMappedLike("rightshoulder", input)) + else if (config->isMappedLike("down", input) || config->isMappedLike("leftshoulder", input)) incDir = -1; if (incDir != 0) { tm new_tm = mTime; + // ISO 8601 date format. if (mEditIndex == 0) { + new_tm.tm_year += incDir; + + if (new_tm.tm_year < 0) + new_tm.tm_year = 0; + } + else if (mEditIndex == 1) { new_tm.tm_mon += incDir; if (new_tm.tm_mon > 11) @@ -80,7 +87,7 @@ bool DateTimeEditComponent::input(InputConfig* config, Input input) new_tm.tm_mon = 11; } - else if (mEditIndex == 1) { + else if (mEditIndex == 2) { const int days_in_month = Utils::Time::daysInMonth(new_tm.tm_year + 1900, new_tm.tm_mon + 1); new_tm.tm_mday += incDir; @@ -91,12 +98,6 @@ bool DateTimeEditComponent::input(InputConfig* config, Input input) new_tm.tm_mday = days_in_month; } - else if (mEditIndex == 2) { - new_tm.tm_year += incDir; - - if (new_tm.tm_year < 0) - new_tm.tm_year = 0; - } // Validate day. const int days_in_month = @@ -191,16 +192,21 @@ DateTimeEditComponent::DisplayMode DateTimeEditComponent::getCurrentDisplayMode( std::string DateTimeEditComponent::getDisplayString(DisplayMode mode) const { + // ISO 8601 date format. std::string fmt; switch (mode) { case DISP_DATE: { - fmt = "%m/%d/%Y"; + if (mTime.getTime() == 0) + // The extra blankspaces are for visual alignment. + return "unknown "; + fmt = "%Y-%m-%d"; break; } case DISP_DATE_TIME: { if (mTime.getTime() == 0) - return "unknown"; - fmt = "%m/%d/%Y %H:%M:%S"; + // The extra blankspaces are for visual alignment. + return "unknown "; + fmt = "%Y-%m-%d %H:%M:%S"; break; } case DISP_RELATIVE_TO_NOW: { @@ -243,8 +249,18 @@ std::shared_ptr DateTimeEditComponent::getFont() const void DateTimeEditComponent::updateTextCache() { DisplayMode mode = getCurrentDisplayMode(); - const std::string dispString = mUppercase ? - Utils::String::toUpper(getDisplayString(mode)) : getDisplayString(mode); + + std::string dispString; + + // Hack to set date string to blank instead of 'unknown'. + // The calling function simply needs to set this string using setValue(). + if (mTime.getIsoString() == "99990101T000000") { + dispString = ""; + } + else { + dispString = mUppercase ? Utils::String::toUpper(getDisplayString(mode)) : + getDisplayString(mode); + } std::shared_ptr font = getFont(); mTextCache = std::unique_ptr(font->buildTextCache(dispString, 0, 0, mColor)); @@ -256,32 +272,35 @@ void DateTimeEditComponent::updateTextCache() getParent()->onSizeChanged(); } + if (dispString == "unknown " || dispString == "") + return; + // Set up cursor positions. mCursorBoxes.clear(); if (dispString.empty() || mode == DISP_RELATIVE_TO_NOW) return; - // Month. + // Year. Vector2f start(0, 0); - Vector2f end = font->sizeText(dispString.substr(0, 2)); + Vector2f end = font->sizeText(dispString.substr(0, 4)); Vector2f diff = end - start; mCursorBoxes.push_back(Vector4f(start[0], start[1], diff[0], diff[1])); - // Day. - start[0] = font->sizeText(dispString.substr(0, 3)).x(); - end = font->sizeText(dispString.substr(0, 5)); + // Month. + start[0] = font->sizeText(dispString.substr(0, 5)).x(); + end = font->sizeText(dispString.substr(0, 7)); diff = end - start; mCursorBoxes.push_back(Vector4f(start[0], start[1], diff[0], diff[1])); - // Year. - start[0] = font->sizeText(dispString.substr(0, 6)).x(); + // Day. + start[0] = font->sizeText(dispString.substr(0, 8)).x(); end = font->sizeText(dispString.substr(0, 10)); diff = end - start; mCursorBoxes.push_back(Vector4f(start[0], start[1], diff[0], diff[1])); // The logic for handling time for 'mode = DISP_DATE_TIME' is missing, but - // nobody will use it anyway so it's not implemented. + // nobody will use it anyway so it's not worthwhile implementing. } void DateTimeEditComponent::setColor(unsigned int color) diff --git a/es-core/src/components/RatingComponent.cpp b/es-core/src/components/RatingComponent.cpp index 6eb16b919..8de87db8a 100644 --- a/es-core/src/components/RatingComponent.cpp +++ b/es-core/src/components/RatingComponent.cpp @@ -1,29 +1,38 @@ +// +// RatingComponent.cpp +// +// Game rating icons. +// Used by gamelist views, metadata editor and scraper. +// + #include "components/RatingComponent.h" #include "resources/TextureResource.h" #include "ThemeData.h" -RatingComponent::RatingComponent(Window* window) : GuiComponent(window), mColorShift(0xFFFFFFFF), mUnfilledColor(0xFFFFFFFF) +RatingComponent::RatingComponent(Window* window) : GuiComponent(window), + mColorShift(0xFFFFFFFF), mUnfilledColor(0xFFFFFFFF) { mFilledTexture = TextureResource::get(":/star_filled.svg", true); mUnfilledTexture = TextureResource::get(":/star_unfilled.svg", true); mValue = 0.5f; mSize = Vector2f(64 * NUM_RATING_STARS, 64); + mHideRatingComponent = false; updateVertices(); updateColors(); } void RatingComponent::setValue(const std::string& value) { - if(value.empty()) - { + if (value.empty()) { mValue = 0.0f; - }else{ - // Round up to the closest .1 value, i.e. to the closest half-star + } + else { + // Round up to the closest .1 value, i.e. to the closest half-icon. mValue = Math::ceilf(stof(value) / 0.1) / 10; - if(mValue > 1.0f) + if (mValue > 1.0f) mValue = 1.0f; - else if(mValue < 0.0f) + else if (mValue < 0.0f) mValue = 0.0f; } @@ -32,8 +41,8 @@ void RatingComponent::setValue(const std::string& value) std::string RatingComponent::getValue() const { - // do not use std::to_string here as it will use the current locale - // and that sometimes encodes decimals as commas + // Do not use std::to_string here as it will use the current locale + // and that sometimes encodes decimals as commas. std::stringstream ss; ss << mValue; return ss.str(); @@ -41,6 +50,12 @@ std::string RatingComponent::getValue() const void RatingComponent::setOpacity(unsigned char opacity) { + // Completely hide component if opacity if set to zero. + if (opacity == 0) + mHideRatingComponent = true; + else + mHideRatingComponent = false; + mOpacity = opacity; mColorShift = (mColorShift >> 8 << 8) | mOpacity; updateColors(); @@ -49,25 +64,24 @@ void RatingComponent::setOpacity(unsigned char opacity) void RatingComponent::setColorShift(unsigned int color) { mColorShift = color; - // Grab the opacity from the color shift because we may need to apply it if - // fading textures in + // Grab the opacity from the color shift because we may need + // to apply it if fading in textures. mOpacity = color & 0xff; updateColors(); } void RatingComponent::onSizeChanged() { - if(mSize.y() == 0) + if (mSize.y() == 0) mSize[1] = mSize.x() / NUM_RATING_STARS; - else if(mSize.x() == 0) + else if (mSize.x() == 0) mSize[0] = mSize.y() * NUM_RATING_STARS; - if(mSize.y() > 0) - { + if (mSize.y() > 0) { size_t heightPx = (size_t)Math::round(mSize.y()); if (mFilledTexture) mFilledTexture->rasterizeAt(heightPx, heightPx); - if(mUnfilledTexture) + if (mUnfilledTexture) mUnfilledTexture->rasterizeAt(heightPx, heightPx); } @@ -77,7 +91,7 @@ void RatingComponent::onSizeChanged() void RatingComponent::updateVertices() { const float numStars = NUM_RATING_STARS; - const float h = getSize().y(); // is the same as a single star's width + const float h = getSize().y(); // Ss the same as a single star's width. const float w = getSize().y() * mValue * numStars; const float fw = getSize().y() * numStars; const unsigned int color = Renderer::convertColor(mColorShift); @@ -92,16 +106,18 @@ void RatingComponent::updateVertices() mVertices[6] = { { fw, 0.0f }, { numStars, 1.0f }, color }; mVertices[7] = { { fw, h }, { numStars, 0.0f }, color }; - // round vertices - for(int i = 0; i < 8; ++i) - mVertices[i].pos.round(); + // Round vertices. + // Disabled as it caused subtle but strange rendering errors where + // the icons changed size slightly when changing rating scores. +// for (int i = 0; i < 8; ++i) +// mVertices[i].pos.round(); } void RatingComponent::updateColors() { const unsigned int color = Renderer::convertColor(mColorShift); - for(int i = 0; i < 8; ++i) + for (int i = 0; i < 8; ++i) mVertices[i].col = color; } @@ -109,15 +125,17 @@ void RatingComponent::render(const Transform4x4f& parentTrans) { if (!isVisible() || mFilledTexture == nullptr || mUnfilledTexture == nullptr) return; - + + // If set to true, hide rating component. + if (mHideRatingComponent) + return; + Transform4x4f trans = parentTrans * getTransform(); Renderer::setMatrix(trans); - if (mUnfilledTexture->bind()) - { - if (mUnfilledColor != mColorShift) - { + if (mUnfilledTexture->bind()) { + if (mUnfilledColor != mColorShift) { const unsigned int color = Renderer::convertColor(mUnfilledColor); for (int i = 0; i < 8; ++i) mVertices[i].col = color; @@ -130,22 +148,19 @@ void RatingComponent::render(const Transform4x4f& parentTrans) updateColors(); } - - if (mFilledTexture->bind()) - { + if (mFilledTexture->bind()) { Renderer::drawTriangleStrips(&mVertices[0], 4); Renderer::bindTexture(0); } - + renderChildren(trans); } bool RatingComponent::input(InputConfig* config, Input input) { - if(config->isMappedTo("a", input) && input.value != 0) - { + if (config->isMappedTo("a", input) && input.value != 0) { mValue += (1.f/2) / NUM_RATING_STARS; - if(mValue > 1.05f) + if (mValue > 1.05f) mValue = 0.0f; updateVertices(); @@ -154,30 +169,27 @@ bool RatingComponent::input(InputConfig* config, Input input) return GuiComponent::input(config, input); } -void RatingComponent::applyTheme(const std::shared_ptr& theme, const std::string& view, const std::string& element, unsigned int properties) +void RatingComponent::applyTheme(const std::shared_ptr& theme, + const std::string& view, const std::string& element, unsigned int properties) { GuiComponent::applyTheme(theme, view, element, properties); - using namespace ThemeFlags; const ThemeData::ThemeElement* elem = theme->getElement(view, element, "rating"); - if(!elem) + if (!elem) return; bool imgChanged = false; - if(properties & PATH && elem->has("filledPath")) - { + if (properties & PATH && elem->has("filledPath")) { mFilledTexture = TextureResource::get(elem->get("filledPath"), true); imgChanged = true; } - if(properties & PATH && elem->has("unfilledPath")) - { + if (properties & PATH && elem->has("unfilledPath")) { mUnfilledTexture = TextureResource::get(elem->get("unfilledPath"), true); imgChanged = true; } - if (properties & COLOR) - { + if (properties & COLOR) { if (elem->has("color")) setColorShift(elem->get("color")); @@ -187,7 +199,7 @@ void RatingComponent::applyTheme(const std::shared_ptr& theme, const mUnfilledColor = mColorShift; } - if(imgChanged) + if (imgChanged) onSizeChanged(); } diff --git a/es-core/src/components/RatingComponent.h b/es-core/src/components/RatingComponent.h index efc6cac9f..511acc283 100644 --- a/es-core/src/components/RatingComponent.h +++ b/es-core/src/components/RatingComponent.h @@ -1,3 +1,10 @@ +// +// RatingComponent.h +// +// Game rating icons. +// Used by gamelist views, metadata editor and scraper. +// + #pragma once #ifndef ES_APP_COMPONENTS_RATING_COMPONENT_H #define ES_APP_COMPONENTS_RATING_COMPONENT_H @@ -13,14 +20,15 @@ class TextureResource; // setSize(x, y) works a little differently than you might expect: // * (0, y != 0) - x will be automatically calculated (5*y). // * (x != 0, 0) - y will be automatically calculated (x/5). -// * (x != 0, y != 0) - you better be sure x = y*5 +// * (x != 0, y != 0) - you better be sure x = y*5. class RatingComponent : public GuiComponent { public: RatingComponent(Window* window); std::string getValue() const override; - void setValue(const std::string& value) override; // Should be a normalized float (in the range [0..1]) - if it's not, it will be clamped. + // Should be a normalized float (in the range [0..1]) - if it's not, it will be clamped. + void setValue(const std::string& value) override; bool input(InputConfig* config, Input input) override; void render(const Transform4x4f& parentTrans); @@ -32,7 +40,8 @@ public: // Multiply all pixels in the image by this color when rendering. void setColorShift(unsigned int color); - virtual void applyTheme(const std::shared_ptr& theme, const std::string& view, const std::string& element, unsigned int properties) override; + virtual void applyTheme(const std::shared_ptr& theme, const std::string& view, + const std::string& element, unsigned int properties) override; virtual std::vector getHelpPrompts() override; @@ -46,6 +55,8 @@ private: unsigned int mColorShift; unsigned int mUnfilledColor; + // If set to true, the rating component is hidden. + bool mHideRatingComponent; std::shared_ptr mFilledTexture; std::shared_ptr mUnfilledTexture;