2020-06-06 20:04:05 +00:00
|
|
|
//
|
|
|
|
// RatingComponent.cpp
|
|
|
|
//
|
|
|
|
// Game rating icons.
|
|
|
|
// Used by gamelist views, metadata editor and scraper.
|
|
|
|
//
|
|
|
|
|
2014-06-25 16:29:58 +00:00
|
|
|
#include "components/RatingComponent.h"
|
2017-11-01 22:21:10 +00:00
|
|
|
|
|
|
|
#include "resources/TextureResource.h"
|
2020-06-09 18:03:31 +00:00
|
|
|
#include "Settings.h"
|
2017-11-01 22:21:10 +00:00
|
|
|
#include "ThemeData.h"
|
2014-06-25 16:29:58 +00:00
|
|
|
|
2020-06-06 20:04:05 +00:00
|
|
|
RatingComponent::RatingComponent(Window* window) : GuiComponent(window),
|
2020-06-09 18:03:31 +00:00
|
|
|
mColorShift(0xFFFFFFFF), mColorShiftEnd(0xFFFFFFFF), mUnfilledColor(0xFFFFFFFF)
|
2014-06-25 16:29:58 +00:00
|
|
|
{
|
|
|
|
mFilledTexture = TextureResource::get(":/star_filled.svg", true);
|
|
|
|
mUnfilledTexture = TextureResource::get(":/star_unfilled.svg", true);
|
|
|
|
mValue = 0.5f;
|
2017-10-28 20:24:35 +00:00
|
|
|
mSize = Vector2f(64 * NUM_RATING_STARS, 64);
|
2014-06-25 16:29:58 +00:00
|
|
|
updateVertices();
|
2017-06-14 01:01:08 +00:00
|
|
|
updateColors();
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void RatingComponent::setValue(const std::string& value)
|
|
|
|
{
|
2020-06-06 20:04:05 +00:00
|
|
|
if (value.empty()) {
|
2014-06-25 16:29:58 +00:00
|
|
|
mValue = 0.0f;
|
2020-06-06 20:04:05 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Round up to the closest .1 value, i.e. to the closest half-icon.
|
2020-05-16 18:52:33 +00:00
|
|
|
mValue = Math::ceilf(stof(value) / 0.1) / 10;
|
2020-06-06 20:04:05 +00:00
|
|
|
if (mValue > 1.0f)
|
2014-06-25 16:29:58 +00:00
|
|
|
mValue = 1.0f;
|
2020-06-06 20:04:05 +00:00
|
|
|
else if (mValue < 0.0f)
|
2014-06-25 16:29:58 +00:00
|
|
|
mValue = 0.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
updateVertices();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string RatingComponent::getValue() const
|
|
|
|
{
|
2020-06-06 20:04:05 +00:00
|
|
|
// Do not use std::to_string here as it will use the current locale
|
|
|
|
// and that sometimes encodes decimals as commas.
|
2015-01-25 17:26:16 +00:00
|
|
|
std::stringstream ss;
|
|
|
|
ss << mValue;
|
|
|
|
return ss.str();
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
2017-06-08 23:18:27 +00:00
|
|
|
void RatingComponent::setOpacity(unsigned char opacity)
|
|
|
|
{
|
|
|
|
mOpacity = opacity;
|
2020-05-15 16:12:16 +00:00
|
|
|
mColorShift = (mColorShift >> 8 << 8) | mOpacity;
|
2017-06-08 23:18:27 +00:00
|
|
|
updateColors();
|
|
|
|
}
|
|
|
|
|
|
|
|
void RatingComponent::setColorShift(unsigned int color)
|
|
|
|
{
|
|
|
|
mColorShift = color;
|
2020-06-09 18:03:31 +00:00
|
|
|
mColorShiftEnd = color;
|
|
|
|
|
2020-06-06 20:04:05 +00:00
|
|
|
// Grab the opacity from the color shift because we may need
|
|
|
|
// to apply it if fading in textures.
|
2020-05-15 16:12:16 +00:00
|
|
|
mOpacity = color & 0xff;
|
2017-06-08 23:18:27 +00:00
|
|
|
updateColors();
|
|
|
|
}
|
|
|
|
|
2014-06-25 16:29:58 +00:00
|
|
|
void RatingComponent::onSizeChanged()
|
|
|
|
{
|
2020-06-06 20:04:05 +00:00
|
|
|
if (mSize.y() == 0)
|
2014-06-25 16:29:58 +00:00
|
|
|
mSize[1] = mSize.x() / NUM_RATING_STARS;
|
2020-06-06 20:04:05 +00:00
|
|
|
else if (mSize.x() == 0)
|
2014-06-25 16:29:58 +00:00
|
|
|
mSize[0] = mSize.y() * NUM_RATING_STARS;
|
|
|
|
|
2020-06-06 20:04:05 +00:00
|
|
|
if (mSize.y() > 0) {
|
2017-11-13 22:16:38 +00:00
|
|
|
size_t heightPx = (size_t)Math::round(mSize.y());
|
2017-01-22 23:28:06 +00:00
|
|
|
if (mFilledTexture)
|
|
|
|
mFilledTexture->rasterizeAt(heightPx, heightPx);
|
2020-06-06 20:04:05 +00:00
|
|
|
if (mUnfilledTexture)
|
2017-01-22 23:28:06 +00:00
|
|
|
mUnfilledTexture->rasterizeAt(heightPx, heightPx);
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
updateVertices();
|
|
|
|
}
|
|
|
|
|
|
|
|
void RatingComponent::updateVertices()
|
|
|
|
{
|
2020-05-15 16:12:16 +00:00
|
|
|
const float numStars = NUM_RATING_STARS;
|
2020-06-06 20:04:05 +00:00
|
|
|
const float h = getSize().y(); // Ss the same as a single star's width.
|
2020-05-15 16:12:16 +00:00
|
|
|
const float w = getSize().y() * mValue * numStars;
|
|
|
|
const float fw = getSize().y() * numStars;
|
|
|
|
const unsigned int color = Renderer::convertColor(mColorShift);
|
|
|
|
|
|
|
|
mVertices[0] = { { 0.0f, 0.0f }, { 0.0f, 1.0f }, color };
|
|
|
|
mVertices[1] = { { 0.0f, h }, { 0.0f, 0.0f }, color };
|
|
|
|
mVertices[2] = { { w, 0.0f }, { mValue * numStars, 1.0f }, color };
|
|
|
|
mVertices[3] = { { w, h }, { mValue * numStars, 0.0f }, color };
|
|
|
|
|
|
|
|
mVertices[4] = { { 0.0f, 0.0f }, { 0.0f, 1.0f }, color };
|
|
|
|
mVertices[5] = { { 0.0f, h }, { 0.0f, 0.0f }, color };
|
|
|
|
mVertices[6] = { { fw, 0.0f }, { numStars, 1.0f }, color };
|
|
|
|
mVertices[7] = { { fw, h }, { numStars, 0.0f }, color };
|
2019-08-31 16:19:43 +00:00
|
|
|
|
2020-06-06 20:04:05 +00:00
|
|
|
// 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();
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
2017-06-08 23:18:27 +00:00
|
|
|
void RatingComponent::updateColors()
|
|
|
|
{
|
2020-05-15 16:12:16 +00:00
|
|
|
const unsigned int color = Renderer::convertColor(mColorShift);
|
2019-08-08 20:16:11 +00:00
|
|
|
|
2020-06-06 20:04:05 +00:00
|
|
|
for (int i = 0; i < 8; ++i)
|
2019-08-08 20:16:11 +00:00
|
|
|
mVertices[i].col = color;
|
2017-06-08 23:18:27 +00:00
|
|
|
}
|
|
|
|
|
2017-10-28 20:24:35 +00:00
|
|
|
void RatingComponent::render(const Transform4x4f& parentTrans)
|
2014-06-25 16:29:58 +00:00
|
|
|
{
|
2020-05-15 16:12:16 +00:00
|
|
|
if (!isVisible() || mFilledTexture == nullptr || mUnfilledTexture == nullptr)
|
2019-07-22 03:13:48 +00:00
|
|
|
return;
|
2020-06-06 20:04:05 +00:00
|
|
|
|
2017-10-28 20:24:35 +00:00
|
|
|
Transform4x4f trans = parentTrans * getTransform();
|
2020-05-15 16:12:16 +00:00
|
|
|
|
2014-06-25 16:29:58 +00:00
|
|
|
Renderer::setMatrix(trans);
|
|
|
|
|
2020-06-09 18:03:31 +00:00
|
|
|
if (mOpacity > 0) {
|
|
|
|
if(Settings::getInstance()->getBool("DebugImage")) {
|
|
|
|
Renderer::drawRect(0.0f, 0.0f, mSize.y() * NUM_RATING_STARS,
|
|
|
|
mSize.y(), 0x00000033, 0x00000033);
|
2020-05-15 16:12:16 +00:00
|
|
|
}
|
|
|
|
|
2020-06-09 18:03:31 +00:00
|
|
|
if (mUnfilledTexture->bind()) {
|
|
|
|
if (mUnfilledColor != mColorShift) {
|
|
|
|
const unsigned int color = Renderer::convertColor(mUnfilledColor);
|
|
|
|
for (int i = 0; i < 8; ++i)
|
|
|
|
mVertices[i].col = color;
|
|
|
|
}
|
2020-05-15 16:12:16 +00:00
|
|
|
|
2020-06-09 18:03:31 +00:00
|
|
|
Renderer::drawTriangleStrips(&mVertices[4], 4);
|
|
|
|
Renderer::bindTexture(0);
|
|
|
|
|
|
|
|
if (mUnfilledColor != mColorShift)
|
|
|
|
updateColors();
|
|
|
|
}
|
2014-06-25 16:29:58 +00:00
|
|
|
|
2020-06-09 18:03:31 +00:00
|
|
|
if (mFilledTexture->bind()) {
|
|
|
|
Renderer::drawTriangleStrips(&mVertices[0], 4);
|
|
|
|
Renderer::bindTexture(0);
|
|
|
|
}
|
2020-05-15 16:12:16 +00:00
|
|
|
}
|
2020-06-06 20:04:05 +00:00
|
|
|
|
2014-06-25 16:29:58 +00:00
|
|
|
renderChildren(trans);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool RatingComponent::input(InputConfig* config, Input input)
|
|
|
|
{
|
2020-06-06 20:04:05 +00:00
|
|
|
if (config->isMappedTo("a", input) && input.value != 0) {
|
2020-05-16 18:52:33 +00:00
|
|
|
mValue += (1.f/2) / NUM_RATING_STARS;
|
2020-06-06 20:04:05 +00:00
|
|
|
if (mValue > 1.05f)
|
2014-06-25 16:29:58 +00:00
|
|
|
mValue = 0.0f;
|
|
|
|
|
|
|
|
updateVertices();
|
|
|
|
}
|
|
|
|
|
|
|
|
return GuiComponent::input(config, input);
|
|
|
|
}
|
|
|
|
|
2020-06-06 20:04:05 +00:00
|
|
|
void RatingComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
|
|
|
const std::string& view, const std::string& element, unsigned int properties)
|
2014-06-25 16:29:58 +00:00
|
|
|
{
|
|
|
|
GuiComponent::applyTheme(theme, view, element, properties);
|
|
|
|
using namespace ThemeFlags;
|
|
|
|
|
|
|
|
const ThemeData::ThemeElement* elem = theme->getElement(view, element, "rating");
|
2020-06-06 20:04:05 +00:00
|
|
|
if (!elem)
|
2014-06-25 16:29:58 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
bool imgChanged = false;
|
2020-06-06 20:04:05 +00:00
|
|
|
if (properties & PATH && elem->has("filledPath")) {
|
2014-06-25 16:29:58 +00:00
|
|
|
mFilledTexture = TextureResource::get(elem->get<std::string>("filledPath"), true);
|
|
|
|
imgChanged = true;
|
|
|
|
}
|
2020-06-06 20:04:05 +00:00
|
|
|
if (properties & PATH && elem->has("unfilledPath")) {
|
2014-06-25 16:29:58 +00:00
|
|
|
mUnfilledTexture = TextureResource::get(elem->get<std::string>("unfilledPath"), true);
|
|
|
|
imgChanged = true;
|
|
|
|
}
|
|
|
|
|
2020-06-06 20:04:05 +00:00
|
|
|
if (properties & COLOR) {
|
2020-05-15 16:12:16 +00:00
|
|
|
if (elem->has("color"))
|
|
|
|
setColorShift(elem->get<unsigned int>("color"));
|
2017-06-08 23:18:27 +00:00
|
|
|
|
2020-05-15 16:12:16 +00:00
|
|
|
if (elem->has("unfilledColor"))
|
|
|
|
mUnfilledColor = elem->get<unsigned int>("unfilledColor");
|
|
|
|
else
|
|
|
|
mUnfilledColor = mColorShift;
|
|
|
|
}
|
2017-06-08 23:18:27 +00:00
|
|
|
|
2020-06-06 20:04:05 +00:00
|
|
|
if (imgChanged)
|
2014-06-25 16:29:58 +00:00
|
|
|
onSizeChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<HelpPrompt> RatingComponent::getHelpPrompts()
|
|
|
|
{
|
|
|
|
std::vector<HelpPrompt> prompts;
|
2020-06-09 18:03:31 +00:00
|
|
|
prompts.push_back(HelpPrompt("a", "add half star"));
|
2014-06-25 16:29:58 +00:00
|
|
|
return prompts;
|
|
|
|
}
|