ES-DE/es-core/src/components/SliderComponent.cpp

218 lines
6.4 KiB
C++

// SPDX-License-Identifier: MIT
//
// EmulationStation Desktop Edition
// SliderComponent.cpp
//
// Slider to set value in a predefined range.
//
#include "components/SliderComponent.h"
#include "Window.h"
#include "resources/Font.h"
#define MOVE_REPEAT_DELAY 500
#define MOVE_REPEAT_RATE 40
SliderComponent::SliderComponent(float min, float max, float increment, const std::string& suffix)
: mRenderer {Renderer::getInstance()}
, mMin {min}
, mMax {max}
, mSingleIncrement {increment}
, mMoveRate {0.0f}
, mBarHeight {0.0f}
, mBarPosY {0.0f}
, mSuffix {suffix}
{
assert((min - max) != 0.0f);
setSize(mWindow->peekGui()->getSize().x * 0.26f,
Font::get(FONT_SIZE_MEDIUM)->getLetterHeight());
// Some sane default value.
mValue = (max + min) / 2.0f;
mKnob.setResize(0.0f, std::round(mSize.y * 0.7f));
mKnob.setOrigin(0.5f, 0.0f);
mKnob.setImage(":/graphics/slider_knob.svg");
mKnob.setColorShift(mMenuColorPrimary);
mKnobDisabled.setResize(0.0f, std::round(mSize.y * 0.7f));
mKnobDisabled.setOrigin(0.5f, 0.0f);
mKnobDisabled.setImage(":/graphics/slider_knob.svg");
mKnobDisabled.setColorShift(mMenuColorSliderKnobDisabled);
}
bool SliderComponent::input(InputConfig* config, Input input)
{
// Ignore input if the component has been disabled.
if (!mEnabled)
return false;
if (input.value != 0) {
if (config->isMappedLike("left", input)) {
if (input.value)
setValue(mValue - mSingleIncrement);
mMoveRate = input.value ? -mSingleIncrement : 0.0f;
mMoveAccumulator = -MOVE_REPEAT_DELAY;
return true;
}
if (config->isMappedLike("right", input)) {
if (input.value)
setValue(mValue + mSingleIncrement);
mMoveRate = input.value ? mSingleIncrement : 0.0f;
mMoveAccumulator = -MOVE_REPEAT_DELAY;
return true;
}
}
else {
mMoveRate = 0.0f;
}
return GuiComponent::input(config, input);
}
void SliderComponent::update(int deltaTime)
{
if (mMoveRate != 0) {
mMoveAccumulator += deltaTime;
while (mMoveAccumulator >= MOVE_REPEAT_RATE) {
setValue(mValue + mMoveRate);
mMoveAccumulator -= MOVE_REPEAT_RATE;
}
}
GuiComponent::update(deltaTime);
}
void SliderComponent::render(const glm::mat4& parentTrans)
{
glm::mat4 trans {parentTrans * getTransform()};
mRenderer->setMatrix(trans);
if (Settings::getInstance()->getBool("DebugText")) {
mRenderer->drawRect(
mSize.x - mTextCache->metrics.size.x, (mSize.y - mTextCache->metrics.size.y) / 2.0f,
mTextCache->metrics.size.x, mTextCache->metrics.size.y, 0x0000FF33, 0x0000FF33);
mRenderer->drawRect(mSize.x - mTextCache->metrics.size.x, 0.0f, mTextCache->metrics.size.x,
mSize.y, 0x00000033, 0x00000033);
}
const float width {
mSize.x - mKnob.getSize().x -
(mTextCache ? mTextCache->metrics.size.x + (4.0f * mRenderer->getScreenWidthModifier()) :
0.0f)};
if (mTextCache)
mFont->renderTextCache(mTextCache.get());
mRenderer->drawRect(
mKnob.getSize().x / 2.0f, mBarPosY, width, mBarHeight,
(mMenuColorPrimary & 0xFFFFFF00) | static_cast<unsigned int>(mOpacity * 255.0f),
(mMenuColorPrimary & 0xFFFFFF00) | static_cast<unsigned int>(mOpacity * 255.0f));
if (mOpacity > DISABLED_OPACITY)
mKnob.render(trans);
else
mKnobDisabled.render(trans);
GuiComponent::renderChildren(trans);
}
void SliderComponent::setValue(float value)
{
mValue = value;
if (mValue < mMin)
mValue = mMin;
else if (mValue > mMax)
mValue = mMax;
onValueChanged();
}
void SliderComponent::onSizeChanged()
{
mFont = Font::get(mSize.y, FONT_PATH_LIGHT);
onValueChanged();
}
void SliderComponent::onValueChanged()
{
// Update suffix textcache.
if (mFont) {
std::stringstream ss;
ss << std::fixed;
ss.precision(0);
ss << mValue;
ss << mSuffix;
const std::string val {ss.str()};
ss.str("");
ss.clear();
ss << std::fixed;
ss.precision(0);
ss << mMax;
ss << mSuffix;
const std::string max {ss.str()};
glm::vec2 textSize {mFont->sizeText(max)};
mTextCache = std::shared_ptr<TextCache>(mFont->buildTextCache(
val, mSize.x - textSize.x, (mSize.y - textSize.y) / 2.0f, mMenuColorPrimary));
mTextCache->metrics.size.x = textSize.x; // Fudge the width.
}
mKnob.setResize(0.0f, std::round(mSize.y * 0.7f));
float barLength {
mSize.x - mKnob.getSize().x -
(mTextCache ? mTextCache->metrics.size.x + (4.0f * mRenderer->getScreenWidthModifier()) :
0.0f)};
if (mRenderer->getScreenWidth() > mRenderer->getScreenHeight())
mBarHeight = std::round(2.0f * mRenderer->getScreenHeightModifier());
else
mBarHeight = std::round(2.0f * std::round(mRenderer->getScreenWidthModifier()));
// For very low resolutions, make sure the bar height is not rounded to zero.
if (mBarHeight < 1.0f)
mBarHeight = 1.0f;
// Always make both mSize and the knob odd or even for correct positioning.
if (static_cast<int>(mSize.y) % 2 != static_cast<int>(mKnob.getSize().y) % 2) {
mKnob.setResize(mKnob.getSize().x - 1.0f, mKnob.getSize().y - 1.0f);
setSize(getSize().x, getSize().y - 1.0f);
}
// Likewise for the bar.
if (static_cast<int>(mSize.y) % 2 != static_cast<int>(mBarHeight) % 2) {
if (mBarHeight > 1.0f && mSize.y / mBarHeight < 5.0f)
--mBarHeight;
else
++mBarHeight;
}
const float val {(mValue - mMin) / (mMax - mMin)};
// For smooth outer boundaries.
// const float val {glm::smoothstep(mMin, mMax, mValue)};
const float posY {(mSize.y - mKnob.getSize().y) / 2.0f};
mKnob.setPosition(val * barLength + mKnob.getSize().x / 2.0f, posY);
mKnobDisabled.setResize(mKnob.getSize());
mKnobDisabled.setPosition(mKnob.getPosition());
mBarPosY = (mSize.y - mBarHeight) / 2.0f;
if (mChangedValueCallback)
mChangedValueCallback();
}
std::vector<HelpPrompt> SliderComponent::getHelpPrompts()
{
std::vector<HelpPrompt> prompts;
prompts.push_back(HelpPrompt("left/right", "change value"));
return prompts;
}