2020-09-17 20:00:07 +00:00
|
|
|
// SPDX-License-Identifier: MIT
|
2020-06-28 16:39:18 +00:00
|
|
|
//
|
2020-09-17 20:00:07 +00:00
|
|
|
// EmulationStation Desktop Edition
|
2020-06-28 16:39:18 +00:00
|
|
|
// ScrollableContainer.cpp
|
|
|
|
//
|
|
|
|
// Area containing scrollable information, for example the game description
|
|
|
|
// text container in the detailed, video and grid views.
|
|
|
|
//
|
|
|
|
|
2014-06-20 01:30:09 +00:00
|
|
|
#include "components/ScrollableContainer.h"
|
2017-11-01 22:21:10 +00:00
|
|
|
|
2021-07-07 18:31:46 +00:00
|
|
|
#include "Window.h"
|
2021-01-02 20:17:23 +00:00
|
|
|
#include "animations/LambdaAnimation.h"
|
2019-08-08 20:16:11 +00:00
|
|
|
#include "renderers/Renderer.h"
|
2021-01-17 21:02:22 +00:00
|
|
|
#include "resources/Font.h"
|
2013-07-03 01:01:58 +00:00
|
|
|
|
2021-07-07 18:31:46 +00:00
|
|
|
ScrollableContainer::ScrollableContainer(Window* window)
|
|
|
|
: GuiComponent(window)
|
|
|
|
, mAutoScrollDelay(0)
|
|
|
|
, mAutoScrollSpeed(0)
|
|
|
|
, mAutoScrollAccumulator(0)
|
|
|
|
, mScrollPos(0, 0)
|
|
|
|
, mScrollDir(0, 0)
|
|
|
|
, mAutoScrollResetAccumulator(0)
|
|
|
|
, mFontSize(0.0f)
|
2013-07-03 01:01:58 +00:00
|
|
|
{
|
2021-01-02 20:17:23 +00:00
|
|
|
// Set the modifier to get equivalent scrolling speed regardless of screen resolution.
|
2021-01-18 23:11:02 +00:00
|
|
|
mResolutionModifier = Renderer::getScreenHeightModifier();
|
|
|
|
mSmallFontSize = static_cast<float>(Font::get(FONT_SIZE_SMALL)->getSize());
|
|
|
|
|
|
|
|
// For narrower aspect ratios than 16:9 there is a need to set an additional compensation
|
|
|
|
// to get somehow consistent scrolling speeds.
|
|
|
|
float aspectCompensation = static_cast<float>(Renderer::getScreenHeight()) /
|
2021-07-07 18:31:46 +00:00
|
|
|
static_cast<float>(Renderer::getScreenWidth()) -
|
|
|
|
0.5625f;
|
2021-01-18 23:11:02 +00:00
|
|
|
if (aspectCompensation > 0)
|
|
|
|
mResolutionModifier += aspectCompensation * 4.0f;
|
2021-01-05 11:52:21 +00:00
|
|
|
|
|
|
|
mAutoScrollResetDelayConstant = AUTO_SCROLL_RESET_DELAY;
|
|
|
|
mAutoScrollDelayConstant = AUTO_SCROLL_DELAY;
|
|
|
|
mAutoScrollSpeedConstant = AUTO_SCROLL_SPEED;
|
2013-07-03 01:01:58 +00:00
|
|
|
}
|
|
|
|
|
2014-05-19 01:15:15 +00:00
|
|
|
void ScrollableContainer::setAutoScroll(bool autoScroll)
|
2013-07-03 01:01:58 +00:00
|
|
|
{
|
2020-06-28 16:39:18 +00:00
|
|
|
if (autoScroll) {
|
2021-08-17 16:41:45 +00:00
|
|
|
mScrollDir = glm::vec2{0.0f, 1.0f};
|
2021-01-18 23:11:02 +00:00
|
|
|
mAutoScrollDelay = static_cast<int>(mAutoScrollDelayConstant);
|
2021-01-05 11:52:21 +00:00
|
|
|
mAutoScrollSpeed = mAutoScrollSpeedConstant;
|
2021-07-07 18:31:46 +00:00
|
|
|
mAutoScrollSpeed =
|
|
|
|
static_cast<int>(static_cast<float>(mAutoScrollSpeedConstant) / mResolutionModifier);
|
2020-06-28 16:39:18 +00:00
|
|
|
reset();
|
|
|
|
}
|
|
|
|
else {
|
2021-08-17 16:41:45 +00:00
|
|
|
mScrollDir = glm::vec2{};
|
2020-06-28 16:39:18 +00:00
|
|
|
mAutoScrollDelay = 0;
|
|
|
|
mAutoScrollSpeed = 0;
|
|
|
|
mAutoScrollAccumulator = 0;
|
|
|
|
}
|
2013-07-03 01:01:58 +00:00
|
|
|
}
|
|
|
|
|
2021-01-17 09:17:41 +00:00
|
|
|
void ScrollableContainer::setScrollParameters(float autoScrollDelayConstant,
|
2021-07-07 18:31:46 +00:00
|
|
|
float autoScrollResetDelayConstant,
|
|
|
|
int autoScrollSpeedConstant)
|
2013-07-03 01:01:58 +00:00
|
|
|
{
|
2021-01-05 11:52:21 +00:00
|
|
|
mAutoScrollResetDelayConstant = autoScrollResetDelayConstant;
|
|
|
|
mAutoScrollDelayConstant = autoScrollDelayConstant;
|
|
|
|
mAutoScrollSpeedConstant = autoScrollSpeedConstant;
|
2021-01-17 21:02:22 +00:00
|
|
|
}
|
|
|
|
|
2021-01-05 11:52:21 +00:00
|
|
|
void ScrollableContainer::reset()
|
2013-07-03 01:01:58 +00:00
|
|
|
{
|
2021-08-17 16:41:45 +00:00
|
|
|
mScrollPos = glm::vec2{};
|
2021-01-05 11:52:21 +00:00
|
|
|
mAutoScrollResetAccumulator = 0;
|
|
|
|
mAutoScrollAccumulator = -mAutoScrollDelay + mAutoScrollSpeed;
|
|
|
|
mAtEnd = false;
|
2013-07-03 01:01:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ScrollableContainer::update(int deltaTime)
|
|
|
|
{
|
2021-05-16 11:12:31 +00:00
|
|
|
// Don't scroll if the media viewer or screensaver is active or if text scrolling is disabled;
|
|
|
|
if (mWindow->isMediaViewerActive() || mWindow->isScreensaverActive() ||
|
2021-07-07 18:31:46 +00:00
|
|
|
!mWindow->getAllowTextScrolling()) {
|
2021-08-17 16:41:45 +00:00
|
|
|
if (mScrollPos != glm::vec2{} && !mWindow->isLaunchScreenDisplayed())
|
2020-09-17 20:00:07 +00:00
|
|
|
reset();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-08-17 16:41:45 +00:00
|
|
|
const glm::vec2 contentSize{getContentSize()};
|
2021-01-18 23:11:02 +00:00
|
|
|
int adjustedAutoScrollSpeed = mAutoScrollSpeed;
|
|
|
|
|
|
|
|
// Adjust the scrolling speed based on the width of the container.
|
2021-08-16 16:25:01 +00:00
|
|
|
float widthModifier = contentSize.x / static_cast<float>(Renderer::getScreenWidth());
|
2021-01-18 23:26:02 +00:00
|
|
|
adjustedAutoScrollSpeed = static_cast<int>(adjustedAutoScrollSpeed * widthModifier);
|
2021-01-02 20:17:23 +00:00
|
|
|
|
2021-01-18 23:11:02 +00:00
|
|
|
// Also adjust the scrolling speed based on the size of the font.
|
|
|
|
float fontSizeModifier = mSmallFontSize / mFontSize;
|
2021-07-07 18:31:46 +00:00
|
|
|
adjustedAutoScrollSpeed =
|
|
|
|
static_cast<int>(adjustedAutoScrollSpeed * fontSizeModifier * fontSizeModifier);
|
2020-10-17 10:16:58 +00:00
|
|
|
|
2021-01-18 23:11:02 +00:00
|
|
|
if (adjustedAutoScrollSpeed < 0)
|
|
|
|
adjustedAutoScrollSpeed = 1;
|
2020-06-28 16:39:18 +00:00
|
|
|
|
2021-01-18 23:11:02 +00:00
|
|
|
if (adjustedAutoScrollSpeed != 0) {
|
|
|
|
mAutoScrollAccumulator += deltaTime;
|
|
|
|
while (mAutoScrollAccumulator >= adjustedAutoScrollSpeed) {
|
2021-08-16 16:25:01 +00:00
|
|
|
if (contentSize.y > mSize.y)
|
2021-03-14 08:49:26 +00:00
|
|
|
mScrollPos += mScrollDir;
|
2021-01-18 23:11:02 +00:00
|
|
|
mAutoScrollAccumulator -= adjustedAutoScrollSpeed;
|
2020-06-28 16:39:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clip scrolling within bounds.
|
2021-08-16 16:25:01 +00:00
|
|
|
if (mScrollPos.x < 0.0f)
|
|
|
|
mScrollPos.x = 0.0f;
|
|
|
|
if (mScrollPos.y < 0.0f)
|
|
|
|
mScrollPos.y = 0.0f;
|
2020-06-28 16:39:18 +00:00
|
|
|
|
2021-08-16 16:25:01 +00:00
|
|
|
if (mScrollPos.x + getSize().x > contentSize.x) {
|
|
|
|
mScrollPos.x = contentSize.x - getSize().x;
|
2020-06-28 16:39:18 +00:00
|
|
|
mAtEnd = true;
|
|
|
|
}
|
|
|
|
|
2021-08-16 16:25:01 +00:00
|
|
|
if (contentSize.y < getSize().y) {
|
|
|
|
mScrollPos.y = 0.0f;
|
2020-06-28 16:39:18 +00:00
|
|
|
}
|
2021-08-16 16:25:01 +00:00
|
|
|
else if (mScrollPos.y + getSize().y > contentSize.y) {
|
|
|
|
mScrollPos.y = contentSize.y - getSize().y;
|
2020-06-28 16:39:18 +00:00
|
|
|
mAtEnd = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mAtEnd) {
|
|
|
|
mAutoScrollResetAccumulator += deltaTime;
|
2021-01-17 21:02:22 +00:00
|
|
|
if (mAutoScrollResetAccumulator >= static_cast<int>(mAutoScrollResetDelayConstant)) {
|
2021-01-02 20:17:23 +00:00
|
|
|
// Fade in the text as it resets to the start position.
|
|
|
|
auto func = [this](float t) {
|
2021-08-17 18:55:29 +00:00
|
|
|
this->setOpacity(static_cast<unsigned char>(glm::mix(0.0f, 1.0f, t) * 255));
|
2021-08-17 16:41:45 +00:00
|
|
|
mScrollPos = glm::vec2{};
|
2021-01-02 20:17:23 +00:00
|
|
|
mAutoScrollResetAccumulator = 0;
|
|
|
|
mAutoScrollAccumulator = -mAutoScrollDelay + mAutoScrollSpeed;
|
|
|
|
mAtEnd = false;
|
|
|
|
};
|
|
|
|
this->setAnimation(new LambdaAnimation(func, 300), 0, nullptr, false);
|
|
|
|
}
|
2020-06-28 16:39:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GuiComponent::update(deltaTime);
|
2013-07-03 01:01:58 +00:00
|
|
|
}
|
|
|
|
|
2021-08-15 17:30:31 +00:00
|
|
|
void ScrollableContainer::render(const glm::mat4& parentTrans)
|
2021-01-05 11:52:21 +00:00
|
|
|
{
|
|
|
|
if (!isVisible())
|
|
|
|
return;
|
|
|
|
|
2021-08-17 16:41:45 +00:00
|
|
|
glm::mat4 trans{parentTrans * getTransform()};
|
2021-01-05 11:52:21 +00:00
|
|
|
|
2021-08-17 16:41:45 +00:00
|
|
|
glm::ivec2 clipPos{static_cast<int>(trans[3].x), static_cast<int>(trans[3].y)};
|
2021-08-15 17:30:31 +00:00
|
|
|
|
2021-08-17 16:41:45 +00:00
|
|
|
glm::vec3 dimScaled{};
|
2021-08-16 16:25:01 +00:00
|
|
|
dimScaled.x = std::fabs(trans[3].x + mSize.x);
|
|
|
|
dimScaled.y = std::fabs(trans[3].y + mSize.y);
|
2021-08-15 17:30:31 +00:00
|
|
|
|
2021-08-17 16:41:45 +00:00
|
|
|
glm::ivec2 clipDim{static_cast<int>(dimScaled.x - trans[3].x),
|
|
|
|
static_cast<int>(dimScaled.y - trans[3].y)};
|
2021-01-05 11:52:21 +00:00
|
|
|
|
|
|
|
Renderer::pushClipRect(clipPos, clipDim);
|
|
|
|
|
2021-08-17 16:41:45 +00:00
|
|
|
trans = glm::translate(trans, -glm::vec3{mScrollPos.x, mScrollPos.y, 0.0f});
|
2021-01-05 11:52:21 +00:00
|
|
|
Renderer::setMatrix(trans);
|
|
|
|
|
|
|
|
GuiComponent::renderChildren(trans);
|
|
|
|
Renderer::popClipRect();
|
|
|
|
}
|
|
|
|
|
2021-08-16 16:25:01 +00:00
|
|
|
glm::vec2 ScrollableContainer::getContentSize()
|
2013-07-03 01:01:58 +00:00
|
|
|
{
|
2021-08-17 16:41:45 +00:00
|
|
|
glm::vec2 max{};
|
2020-06-28 16:39:18 +00:00
|
|
|
for (unsigned int i = 0; i < mChildren.size(); i++) {
|
2021-08-17 16:41:45 +00:00
|
|
|
glm::vec2 pos{mChildren.at(i)->getPosition().x, mChildren.at(i)->getPosition().y};
|
|
|
|
glm::vec2 bottomRight{mChildren.at(i)->getSize() + pos};
|
2021-08-16 16:25:01 +00:00
|
|
|
if (bottomRight.x > max.x)
|
|
|
|
max.x = bottomRight.x;
|
|
|
|
if (bottomRight.y > max.y)
|
|
|
|
max.y = bottomRight.y;
|
2021-01-18 23:11:02 +00:00
|
|
|
if (!mFontSize)
|
|
|
|
mFontSize = static_cast<float>(mChildren.at(i)->getFont()->getSize());
|
2020-06-28 16:39:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return max;
|
2013-07-03 01:01:58 +00:00
|
|
|
}
|