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

View file

@ -8,6 +8,8 @@
#include "components/ComponentList.h" #include "components/ComponentList.h"
#include "resources/Font.h"
#define TOTAL_HORIZONTAL_PADDING_PX 20.0f #define TOTAL_HORIZONTAL_PADDING_PX 20.0f
ComponentList::ComponentList(Window* window) ComponentList::ComponentList(Window* window)
@ -18,6 +20,11 @@ ComponentList::ComponentList(Window* window)
, mSingleRowScroll{false} , mSingleRowScroll{false}
, mSelectorBarOffset{0.0f} , mSelectorBarOffset{0.0f}
, mCameraOffset{0.0f} , mCameraOffset{0.0f}
, mLoopRows{false}
, mLoopScroll{false}
, mLoopOffset{0}
, mLoopOffset2{0}
, mLoopTime{0}
, mScrollIndicatorStatus{SCROLL_NONE} , mScrollIndicatorStatus{SCROLL_NONE}
{ {
// Adjust the padding relative to the aspect ratio and screen resolution to make it look // 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) void ComponentList::update(int deltaTime)
{ {
if (!mFocused && mLoopRows) {
mLoopOffset = 0;
mLoopOffset2 = 0;
mLoopTime = 0;
}
const float totalHeight = getTotalRowHeight(); const float totalHeight = getTotalRowHeight();
// Scroll indicator logic, used by ScrollIndicatorComponent. // Scroll indicator logic, used by ScrollIndicatorComponent.
@ -152,10 +165,39 @@ void ComponentList::update(int deltaTime)
listUpdate(deltaTime); listUpdate(deltaTime);
if (size()) { if (size()) {
float rowWidth{0.0f};
// Update our currently selected row. // Update our currently selected row.
for (auto it = mEntries.at(mCursor).data.elements.cbegin(); 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); 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; mSetupCompleted = true;
if (mLoopRows) {
mLoopOffset = 0;
mLoopOffset2 = 0;
mLoopTime = 0;
}
// Update the selector bar position. // Update the selector bar position.
// In the future this might be animated. // In the future this might be animated.
mSelectorBarOffset = 0; 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.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; dim.y = (trans[1].y * dim.y + trans[3].y) - trans[3].y;
Renderer::pushClipRect( const int clipRectPosX{static_cast<int>(std::round(trans[3].x))};
glm::ivec2{static_cast<int>(std::round(trans[3].x)), const int clipRectPosY{static_cast<int>(std::round(trans[3].y))};
static_cast<int>(std::round(trans[3].y))}, const int clipRectSizeX{static_cast<int>(std::round(dim.x))};
glm::ivec2{static_cast<int>(std::round(dim.x)), static_cast<int>(std::round(dim.y))}); const int clipRectSizeY{static_cast<int>(std::round(dim.y))};
Renderer::pushClipRect(glm::ivec2{clipRectPosX, clipRectPosY},
glm::ivec2{clipRectSizeX, clipRectSizeY});
// Scroll the camera. // Scroll the camera.
trans = glm::translate(trans, glm::vec3{0.0f, -mCameraOffset, 0.0f}); trans = glm::translate(trans, glm::vec3{0.0f, -mCameraOffset, 0.0f});
glm::mat4 loopTrans{trans};
// Draw our entries. // Draw our entries.
std::vector<GuiComponent*> drawAfterCursor; std::vector<GuiComponent*> drawAfterCursor;
bool drawAll; bool drawAll;
for (size_t i = 0; i < mEntries.size(); i++) { 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); auto& entry = mEntries.at(i);
drawAll = !mFocused || i != static_cast<unsigned int>(mCursor); drawAll = !mFocused || i != static_cast<unsigned int>(mCursor);
for (auto it = entry.data.elements.cbegin(); it != entry.data.elements.cend(); it++) { for (auto it = entry.data.elements.cbegin(); it != entry.data.elements.cend(); it++) {
if (drawAll || it->invert_when_selected) { 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 // 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 // font or image before inverting, as it would otherwise lead to an ugly
// inverted color (e.g. red inverting to a green hue). // 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; unsigned char byteBlue = origColor >> 8 & 0xFF;
// If it's neutral, just proceed with normal rendering. // If it's neutral, just proceed with normal rendering.
if (byteRed == byteGreen && byteGreen == byteBlue) { if (byteRed == byteGreen && byteGreen == byteBlue) {
it->component->render(trans); renderLoopFunc();
} }
else { else {
if (isTextComponent) if (isTextComponent)
it->component->setColor(DEFAULT_INVERTED_TEXTCOLOR); it->component->setColor(DEFAULT_INVERTED_TEXTCOLOR);
else else
it->component->setColorShift(DEFAULT_INVERTED_IMAGECOLOR); it->component->setColorShift(DEFAULT_INVERTED_IMAGECOLOR);
renderLoopFunc();
it->component->render(trans);
// Revert to the original color after rendering. // Revert to the original color after rendering.
if (isTextComponent) if (isTextComponent)
it->component->setColor(origColor); it->component->setColor(origColor);

View file

@ -86,6 +86,15 @@ public:
float getTotalRowHeight() const; float getTotalRowHeight() const;
float getRowHeight(int row) const { return getRowHeight(mEntries.at(row).data); } 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() void resetScrollIndicatorStatus()
{ {
mScrollIndicatorStatus = SCROLL_NONE; mScrollIndicatorStatus = SCROLL_NONE;
@ -126,6 +135,12 @@ private:
float mSelectorBarOffset; float mSelectorBarOffset;
float mCameraOffset; float mCameraOffset;
bool mLoopRows;
bool mLoopScroll;
int mLoopOffset;
int mLoopOffset2;
int mLoopTime;
std::function<void(CursorState state)> mCursorChangedCallback; std::function<void(CursorState state)> mCursorChangedCallback;
std::function<void(ScrollIndicator state, bool singleRowScroll)> std::function<void(ScrollIndicator state, bool singleRowScroll)>
mScrollIndicatorChangedCallback; mScrollIndicatorChangedCallback;