Added horizontal scrolling of long game names to the scraper GUI.

This commit is contained in:
Leon Styhre 2021-10-18 19:24:47 +02:00
parent ccc3cae46b
commit 484606fb6f
3 changed files with 102 additions and 10 deletions

View file

@ -327,6 +327,7 @@ void GuiScraperSearch::search(const ScraperSearchParams& params)
mScrapeResult = {};
mResultList->clear();
mResultList->setLoopRows(false);
mScraperResults.clear();
mMDRetrieveURLsHandle.reset();
mThumbnailReqMap.clear();
@ -355,6 +356,7 @@ void GuiScraperSearch::onSearchDone(const std::vector<ScraperSearchResult>& resu
mResultList->clear();
mScraperResults = results;
mResultList->setLoopRows(true);
auto font = Font::get(FONT_SIZE_MEDIUM);
unsigned int color = 0x777777FF;
@ -389,7 +391,7 @@ void GuiScraperSearch::onSearchDone(const std::vector<ScraperSearchResult>& resu
row.addElement(
std::make_shared<TextComponent>(
mWindow, Utils::String::toUpper(results.at(i).mdl.get("name")), font, color),
true);
false);
row.makeAcceptInputHandler([this, i] { returnResult(mScraperResults.at(i)); });
mResultList->addRow(row);
}
@ -562,8 +564,10 @@ bool GuiScraperSearch::input(InputConfig* config, Input input)
else if (mSearchType == ACCEPT_SINGLE_MATCHES && !mFoundGame)
allowRefine = true;
if (allowRefine)
if (allowRefine) {
mResultList->stopLooping();
openInputScreen(mLastSearch);
}
}
// If multi-scraping, skip game unless the result has already been accepted.
@ -589,6 +593,7 @@ void GuiScraperSearch::render(const glm::mat4& parentTrans)
void GuiScraperSearch::returnResult(ScraperSearchResult result)
{
mResultList->setLoopRows(false);
mBlockAccept = true;
mAcceptedResult = true;

View file

@ -8,6 +8,8 @@
#include "components/ComponentList.h"
#include "resources/Font.h"
#define TOTAL_HORIZONTAL_PADDING_PX 20.0f
ComponentList::ComponentList(Window* window)
@ -18,6 +20,11 @@ ComponentList::ComponentList(Window* window)
, mSingleRowScroll{false}
, mSelectorBarOffset{0.0f}
, mCameraOffset{0.0f}
, mLoopRows{false}
, mLoopScroll{false}
, mLoopOffset{0}
, mLoopOffset2{0}
, mLoopTime{0}
, mScrollIndicatorStatus{SCROLL_NONE}
{
// Adjust the padding relative to the aspect ratio and screen resolution to make it look
@ -120,6 +127,12 @@ bool ComponentList::input(InputConfig* config, Input input)
void ComponentList::update(int deltaTime)
{
if (!mFocused && mLoopRows) {
mLoopOffset = 0;
mLoopOffset2 = 0;
mLoopTime = 0;
}
const float totalHeight = getTotalRowHeight();
// Scroll indicator logic, used by ScrollIndicatorComponent.
@ -152,10 +165,39 @@ void ComponentList::update(int deltaTime)
listUpdate(deltaTime);
if (size()) {
float rowWidth{0.0f};
// Update our currently selected row.
for (auto it = mEntries.at(mCursor).data.elements.cbegin();
it != mEntries.at(mCursor).data.elements.cend(); it++)
it != mEntries.at(mCursor).data.elements.cend(); it++) {
it->component->update(deltaTime);
rowWidth += it->component->getSize().x;
}
if (mLoopRows && rowWidth + mHorizontalPadding / 2.0f > mSize.x) {
// Loop the text.
const float speed{
Font::get(FONT_SIZE_MEDIUM)->sizeText("ABCDEFGHIJKLMNOPQRSTUVWXYZ").x * 0.247f};
const float delay{1300.0f};
const float scrollLength{rowWidth};
const float returnLength{speed * 1.5f};
const float scrollTime{(scrollLength * 1000.0f) / speed};
const float returnTime{(returnLength * 1000.0f) / speed};
const int maxTime{static_cast<int>(delay + scrollTime + returnTime)};
mLoopTime += deltaTime;
while (mLoopTime > maxTime)
mLoopTime -= maxTime;
mLoopOffset = static_cast<int>(Utils::Math::loop(delay, scrollTime + returnTime,
static_cast<float>(mLoopTime),
scrollLength + returnLength));
if (mLoopOffset > (scrollLength - (mSize.x - returnLength)))
mLoopOffset2 = static_cast<int>(mLoopOffset - (scrollLength + returnLength));
else if (mLoopOffset2 < 0)
mLoopOffset2 = 0;
}
}
}
@ -163,6 +205,12 @@ void ComponentList::onCursorChanged(const CursorState& state)
{
mSetupCompleted = true;
if (mLoopRows) {
mLoopOffset = 0;
mLoopOffset2 = 0;
mLoopTime = 0;
}
// Update the selector bar position.
// In the future this might be animated.
mSelectorBarOffset = 0;
@ -233,22 +281,47 @@ void ComponentList::render(const glm::mat4& parentTrans)
dim.x = (trans[0].x * dim.x + trans[3].x) - trans[3].x;
dim.y = (trans[1].y * dim.y + trans[3].y) - trans[3].y;
Renderer::pushClipRect(
glm::ivec2{static_cast<int>(std::round(trans[3].x)),
static_cast<int>(std::round(trans[3].y))},
glm::ivec2{static_cast<int>(std::round(dim.x)), static_cast<int>(std::round(dim.y))});
const int clipRectPosX{static_cast<int>(std::round(trans[3].x))};
const int clipRectPosY{static_cast<int>(std::round(trans[3].y))};
const int clipRectSizeX{static_cast<int>(std::round(dim.x))};
const int clipRectSizeY{static_cast<int>(std::round(dim.y))};
Renderer::pushClipRect(glm::ivec2{clipRectPosX, clipRectPosY},
glm::ivec2{clipRectSizeX, clipRectSizeY});
// Scroll the camera.
trans = glm::translate(trans, glm::vec3{0.0f, -mCameraOffset, 0.0f});
glm::mat4 loopTrans{trans};
// Draw our entries.
std::vector<GuiComponent*> drawAfterCursor;
bool drawAll;
for (size_t i = 0; i < mEntries.size(); i++) {
if (mLoopRows && mFocused && mLoopOffset > 0) {
loopTrans =
glm::translate(trans, glm::vec3{static_cast<float>(-mLoopOffset), 0.0f, 0.0f});
}
auto& entry = mEntries.at(i);
drawAll = !mFocused || i != static_cast<unsigned int>(mCursor);
for (auto it = entry.data.elements.cbegin(); it != entry.data.elements.cend(); it++) {
if (drawAll || it->invert_when_selected) {
auto renderLoopFunc = [&]() {
// Needed to avoid flickering when returning to the start position.
if (mLoopOffset == 0 && mLoopOffset2 == 0)
mLoopScroll = false;
it->component->render(loopTrans);
// Render row again if text is moved far enough for it to repeat.
if (mLoopOffset2 < 0 || mLoopScroll) {
mLoopScroll = true;
loopTrans = glm::translate(
trans, glm::vec3{static_cast<float>(-mLoopOffset2), 0.0f, 0.0f});
it->component->render(loopTrans);
}
};
// For the row where the cursor is at, we want to remove any hue from the
// font or image before inverting, as it would otherwise lead to an ugly
// inverted color (e.g. red inverting to a green hue).
@ -267,15 +340,14 @@ void ComponentList::render(const glm::mat4& parentTrans)
unsigned char byteBlue = origColor >> 8 & 0xFF;
// If it's neutral, just proceed with normal rendering.
if (byteRed == byteGreen && byteGreen == byteBlue) {
it->component->render(trans);
renderLoopFunc();
}
else {
if (isTextComponent)
it->component->setColor(DEFAULT_INVERTED_TEXTCOLOR);
else
it->component->setColorShift(DEFAULT_INVERTED_IMAGECOLOR);
it->component->render(trans);
renderLoopFunc();
// Revert to the original color after rendering.
if (isTextComponent)
it->component->setColor(origColor);

View file

@ -86,6 +86,15 @@ public:
float getTotalRowHeight() const;
float getRowHeight(int row) const { return getRowHeight(mEntries.at(row).data); }
// Horizontal looping for row content that doesn't fit on-screen.
void setLoopRows(bool state) { mLoopRows = state; }
void stopLooping()
{
mLoopOffset = 0;
mLoopOffset2 = 0;
mLoopTime = 0;
}
void resetScrollIndicatorStatus()
{
mScrollIndicatorStatus = SCROLL_NONE;
@ -126,6 +135,12 @@ private:
float mSelectorBarOffset;
float mCameraOffset;
bool mLoopRows;
bool mLoopScroll;
int mLoopOffset;
int mLoopOffset2;
int mLoopTime;
std::function<void(CursorState state)> mCursorChangedCallback;
std::function<void(ScrollIndicator state, bool singleRowScroll)>
mScrollIndicatorChangedCallback;