2020-09-21 17:17:34 +00:00
|
|
|
// SPDX-License-Identifier: MIT
|
2020-06-28 16:39:18 +00:00
|
|
|
//
|
2020-09-21 17:17:34 +00:00
|
|
|
// EmulationStation Desktop Edition
|
2020-06-28 16:39:18 +00:00
|
|
|
// SliderComponent.cpp
|
|
|
|
//
|
|
|
|
// Slider to set value in a predefined range.
|
|
|
|
//
|
|
|
|
|
2014-06-20 01:30:09 +00:00
|
|
|
#include "components/SliderComponent.h"
|
2017-11-01 22:21:10 +00:00
|
|
|
|
2021-10-24 12:17:43 +00:00
|
|
|
#include "Window.h"
|
2014-06-20 01:30:09 +00:00
|
|
|
#include "resources/Font.h"
|
2013-06-19 01:12:30 +00:00
|
|
|
|
2014-03-22 21:55:18 +00:00
|
|
|
#define MOVE_REPEAT_DELAY 500
|
|
|
|
#define MOVE_REPEAT_RATE 40
|
|
|
|
|
2022-01-19 17:01:54 +00:00
|
|
|
SliderComponent::SliderComponent(float min, float max, float increment, const std::string& suffix)
|
2022-03-14 18:51:48 +00:00
|
|
|
: mRenderer {Renderer::getInstance()}
|
|
|
|
, mMin {min}
|
2022-01-16 11:09:55 +00:00
|
|
|
, mMax {max}
|
|
|
|
, mSingleIncrement {increment}
|
|
|
|
, mMoveRate {0.0f}
|
|
|
|
, mBarHeight {0.0f}
|
2023-01-24 17:32:28 +00:00
|
|
|
, mBarPosY {0.0f}
|
2022-01-16 11:09:55 +00:00
|
|
|
, mSuffix {suffix}
|
2013-06-19 01:12:30 +00:00
|
|
|
{
|
2021-10-23 13:36:16 +00:00
|
|
|
assert((min - max) != 0.0f);
|
2013-06-19 01:12:30 +00:00
|
|
|
|
2020-06-28 16:39:18 +00:00
|
|
|
// Some sane default value.
|
2021-07-07 18:31:46 +00:00
|
|
|
mValue = (max + min) / 2.0f;
|
2013-06-19 01:12:30 +00:00
|
|
|
|
2023-02-11 11:28:06 +00:00
|
|
|
mKnob.setOrigin(0.5f, 0.0f);
|
2020-06-28 16:39:18 +00:00
|
|
|
mKnob.setImage(":/graphics/slider_knob.svg");
|
2019-08-25 15:23:02 +00:00
|
|
|
|
2023-02-11 11:28:06 +00:00
|
|
|
mKnobDisabled.setOrigin(0.5f, 0.0f);
|
|
|
|
mKnobDisabled.setImage(":/graphics/slider_knob_disabled.svg");
|
|
|
|
|
2022-01-19 17:01:54 +00:00
|
|
|
setSize(mWindow->peekGui()->getSize().x * 0.26f,
|
|
|
|
Font::get(FONT_SIZE_MEDIUM)->getLetterHeight());
|
2013-06-19 01:12:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool SliderComponent::input(InputConfig* config, Input input)
|
|
|
|
{
|
2023-02-11 11:28:06 +00:00
|
|
|
// Ignore input if the component has been disabled.
|
|
|
|
if (!mEnabled)
|
|
|
|
return false;
|
|
|
|
|
2021-01-23 14:43:48 +00:00
|
|
|
if (input.value != 0) {
|
|
|
|
if (config->isMappedLike("left", input)) {
|
|
|
|
if (input.value)
|
|
|
|
setValue(mValue - mSingleIncrement);
|
|
|
|
|
2021-10-23 13:36:16 +00:00
|
|
|
mMoveRate = input.value ? -mSingleIncrement : 0.0f;
|
2021-01-23 14:43:48 +00:00
|
|
|
mMoveAccumulator = -MOVE_REPEAT_DELAY;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (config->isMappedLike("right", input)) {
|
|
|
|
if (input.value)
|
|
|
|
setValue(mValue + mSingleIncrement);
|
2020-06-28 16:39:18 +00:00
|
|
|
|
2021-10-23 13:36:16 +00:00
|
|
|
mMoveRate = input.value ? mSingleIncrement : 0.0f;
|
2021-01-23 14:43:48 +00:00
|
|
|
mMoveAccumulator = -MOVE_REPEAT_DELAY;
|
|
|
|
return true;
|
|
|
|
}
|
2020-06-28 16:39:18 +00:00
|
|
|
}
|
2021-01-23 14:43:48 +00:00
|
|
|
else {
|
2021-10-23 13:36:16 +00:00
|
|
|
mMoveRate = 0.0f;
|
2020-06-28 16:39:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return GuiComponent::input(config, input);
|
2013-06-19 01:12:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void SliderComponent::update(int deltaTime)
|
|
|
|
{
|
2020-06-28 16:39:18 +00:00
|
|
|
if (mMoveRate != 0) {
|
|
|
|
mMoveAccumulator += deltaTime;
|
|
|
|
while (mMoveAccumulator >= MOVE_REPEAT_RATE) {
|
|
|
|
setValue(mValue + mMoveRate);
|
|
|
|
mMoveAccumulator -= MOVE_REPEAT_RATE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
GuiComponent::update(deltaTime);
|
2013-06-19 01:12:30 +00:00
|
|
|
}
|
|
|
|
|
2021-08-15 17:30:31 +00:00
|
|
|
void SliderComponent::render(const glm::mat4& parentTrans)
|
2013-06-19 01:12:30 +00:00
|
|
|
{
|
2022-01-16 11:09:55 +00:00
|
|
|
glm::mat4 trans {parentTrans * getTransform()};
|
2022-03-14 18:51:48 +00:00
|
|
|
mRenderer->setMatrix(trans);
|
2013-07-10 11:29:43 +00:00
|
|
|
|
2021-10-24 12:48:42 +00:00
|
|
|
if (Settings::getInstance()->getBool("DebugText")) {
|
2022-03-14 18:51:48 +00:00
|
|
|
mRenderer->drawRect(
|
2021-10-24 12:48:42 +00:00
|
|
|
mSize.x - mTextCache->metrics.size.x, (mSize.y - mTextCache->metrics.size.y) / 2.0f,
|
|
|
|
mTextCache->metrics.size.x, mTextCache->metrics.size.y, 0x0000FF33, 0x0000FF33);
|
2022-03-14 18:51:48 +00:00
|
|
|
mRenderer->drawRect(mSize.x - mTextCache->metrics.size.x, 0.0f, mTextCache->metrics.size.x,
|
|
|
|
mSize.y, 0x00000033, 0x00000033);
|
2021-10-24 12:48:42 +00:00
|
|
|
}
|
2014-03-08 00:16:08 +00:00
|
|
|
|
2023-01-24 17:32:28 +00:00
|
|
|
const float width {
|
|
|
|
mSize.x - mKnob.getSize().x -
|
|
|
|
(mTextCache ? mTextCache->metrics.size.x + (4.0f * mRenderer->getScreenWidthModifier()) :
|
2022-01-16 11:09:55 +00:00
|
|
|
0.0f)};
|
2014-01-10 22:01:28 +00:00
|
|
|
|
2021-10-24 12:48:42 +00:00
|
|
|
if (mTextCache)
|
|
|
|
mFont->renderTextCache(mTextCache.get());
|
|
|
|
|
2023-02-11 11:28:06 +00:00
|
|
|
mRenderer->drawRect(mKnob.getSize().x / 2.0f, mBarPosY, width, mBarHeight,
|
|
|
|
0x77777700 | static_cast<unsigned int>(mOpacity * 255.0f),
|
|
|
|
0x77777700 | static_cast<unsigned int>(mOpacity * 255.0f));
|
2013-06-19 01:12:30 +00:00
|
|
|
|
2023-02-11 11:28:06 +00:00
|
|
|
if (mOpacity > DISABLED_OPACITY)
|
|
|
|
mKnob.render(trans);
|
|
|
|
else
|
|
|
|
mKnobDisabled.render(trans);
|
2019-08-25 15:23:02 +00:00
|
|
|
|
2020-06-28 16:39:18 +00:00
|
|
|
GuiComponent::renderChildren(trans);
|
2013-06-19 01:12:30 +00:00
|
|
|
}
|
2013-06-19 21:02:42 +00:00
|
|
|
|
|
|
|
void SliderComponent::setValue(float value)
|
|
|
|
{
|
2020-06-28 16:39:18 +00:00
|
|
|
mValue = value;
|
|
|
|
if (mValue < mMin)
|
|
|
|
mValue = mMin;
|
|
|
|
else if (mValue > mMax)
|
|
|
|
mValue = mMax;
|
2014-03-22 21:55:18 +00:00
|
|
|
|
2020-06-28 16:39:18 +00:00
|
|
|
onValueChanged();
|
2013-06-19 21:02:42 +00:00
|
|
|
}
|
|
|
|
|
2014-01-10 22:01:28 +00:00
|
|
|
void SliderComponent::onSizeChanged()
|
|
|
|
{
|
2023-02-11 11:28:06 +00:00
|
|
|
mFont = Font::get(mSize.y, FONT_PATH_LIGHT);
|
2020-06-28 16:39:18 +00:00
|
|
|
onValueChanged();
|
2014-01-10 22:01:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void SliderComponent::onValueChanged()
|
|
|
|
{
|
2020-06-28 16:39:18 +00:00
|
|
|
// Update suffix textcache.
|
|
|
|
if (mFont) {
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << std::fixed;
|
|
|
|
ss.precision(0);
|
|
|
|
ss << mValue;
|
|
|
|
ss << mSuffix;
|
2022-09-28 15:23:44 +00:00
|
|
|
const std::string val {ss.str()};
|
2020-06-28 16:39:18 +00:00
|
|
|
|
|
|
|
ss.str("");
|
|
|
|
ss.clear();
|
|
|
|
ss << std::fixed;
|
|
|
|
ss.precision(0);
|
|
|
|
ss << mMax;
|
|
|
|
ss << mSuffix;
|
2022-09-28 15:23:44 +00:00
|
|
|
const std::string max {ss.str()};
|
2020-06-28 16:39:18 +00:00
|
|
|
|
2022-09-28 15:23:44 +00:00
|
|
|
glm::vec2 textSize {mFont->sizeText(max)};
|
2021-10-24 12:48:42 +00:00
|
|
|
mTextCache = std::shared_ptr<TextCache>(mFont->buildTextCache(
|
2021-08-16 16:25:01 +00:00
|
|
|
val, mSize.x - textSize.x, (mSize.y - textSize.y) / 2.0f, 0x777777FF));
|
2021-10-24 12:48:42 +00:00
|
|
|
mTextCache->metrics.size.x = textSize.x; // Fudge the width.
|
2020-06-28 16:39:18 +00:00
|
|
|
}
|
|
|
|
|
2023-01-24 17:32:28 +00:00
|
|
|
mKnob.setResize(0.0f, std::round(mSize.y * 0.7f));
|
2021-10-23 13:36:16 +00:00
|
|
|
|
2022-09-28 15:23:44 +00:00
|
|
|
float barLength {
|
2021-08-16 16:25:01 +00:00
|
|
|
mSize.x - mKnob.getSize().x -
|
2022-09-28 15:23:44 +00:00
|
|
|
(mTextCache ? mTextCache->metrics.size.x + (4.0f * mRenderer->getScreenWidthModifier()) :
|
|
|
|
0.0f)};
|
2021-07-07 18:31:46 +00:00
|
|
|
|
2023-01-24 17:32:28 +00:00
|
|
|
if (mRenderer->getScreenWidth() > mRenderer->getScreenHeight())
|
|
|
|
mBarHeight = std::round(2.0f * mRenderer->getScreenHeightModifier());
|
|
|
|
else
|
|
|
|
mBarHeight = std::round(2.0f * std::round(mRenderer->getScreenWidthModifier()));
|
2021-10-23 13:36:16 +00:00
|
|
|
|
|
|
|
// For very low resolutions, make sure the bar height is not rounded to zero.
|
2023-01-24 17:32:28 +00:00
|
|
|
if (mBarHeight < 1.0f)
|
|
|
|
mBarHeight = 1.0f;
|
2021-10-23 13:36:16 +00:00
|
|
|
|
2023-01-24 17:32:28 +00:00
|
|
|
// 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) {
|
2021-10-23 13:36:16 +00:00
|
|
|
mKnob.setResize(mKnob.getSize().x - 1.0f, mKnob.getSize().y - 1.0f);
|
2021-11-11 18:45:44 +00:00
|
|
|
setSize(getSize().x, getSize().y - 1.0f);
|
|
|
|
}
|
2023-01-24 17:32:28 +00:00
|
|
|
|
|
|
|
// 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;
|
2021-11-11 18:45:44 +00:00
|
|
|
}
|
2021-10-23 13:36:16 +00:00
|
|
|
|
2022-09-28 15:23:44 +00:00
|
|
|
const float val {(mValue - mMin) / (mMax - mMin)};
|
|
|
|
// For smooth outer boundaries.
|
|
|
|
// const float val {glm::smoothstep(mMin, mMax, mValue)};
|
2021-10-23 13:36:16 +00:00
|
|
|
|
2023-01-24 17:32:28 +00:00
|
|
|
const float posY {(mSize.y - mKnob.getSize().y) / 2.0f};
|
|
|
|
mKnob.setPosition(val * barLength + mKnob.getSize().x / 2.0f, posY);
|
|
|
|
|
2023-02-11 11:28:06 +00:00
|
|
|
mKnobDisabled.setResize(mKnob.getSize());
|
|
|
|
mKnobDisabled.setPosition(mKnob.getPosition());
|
|
|
|
|
2023-01-24 17:32:28 +00:00
|
|
|
mBarPosY = (mSize.y - mBarHeight) / 2.0f;
|
2023-02-11 11:28:06 +00:00
|
|
|
|
|
|
|
if (mChangedValueCallback)
|
|
|
|
mChangedValueCallback();
|
2014-01-10 22:01:28 +00:00
|
|
|
}
|
2014-01-25 23:34:29 +00:00
|
|
|
|
|
|
|
std::vector<HelpPrompt> SliderComponent::getHelpPrompts()
|
|
|
|
{
|
2020-06-28 16:39:18 +00:00
|
|
|
std::vector<HelpPrompt> prompts;
|
|
|
|
prompts.push_back(HelpPrompt("left/right", "change value"));
|
|
|
|
return prompts;
|
2014-01-25 23:34:29 +00:00
|
|
|
}
|