From c30d035e3f3e22c274900a5e51acdaf0deb22b79 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Tue, 8 Aug 2023 19:18:16 +0200 Subject: [PATCH] Removed all horizontal text scrolling code from ComponentList (TextComponent is now used instead for this) Also some general code cleanup and refactoring --- es-app/src/guis/GuiMetaDataEd.cpp | 8 +- es-app/src/guis/GuiScraperSearch.cpp | 16 +- es-app/src/views/GamelistView.cpp | 11 +- es-app/src/views/SystemView.cpp | 16 +- es-core/src/GuiComponent.h | 6 +- es-core/src/components/ComponentList.cpp | 105 +++----------- es-core/src/components/ComponentList.h | 67 ++++----- es-core/src/components/GIFAnimComponent.cpp | 2 +- es-core/src/components/GIFAnimComponent.h | 2 +- .../src/components/LottieAnimComponent.cpp | 2 +- es-core/src/components/LottieAnimComponent.h | 2 +- .../src/components/ScrollableContainer.cpp | 6 +- es-core/src/components/ScrollableContainer.h | 2 +- es-core/src/components/TextComponent.cpp | 137 ++++++++---------- es-core/src/components/TextComponent.h | 30 ++-- es-core/src/guis/GuiInputConfig.cpp | 2 +- 16 files changed, 172 insertions(+), 242 deletions(-) diff --git a/es-app/src/guis/GuiMetaDataEd.cpp b/es-app/src/guis/GuiMetaDataEd.cpp index af5bea406..bec253683 100644 --- a/es-app/src/guis/GuiMetaDataEd.cpp +++ b/es-app/src/guis/GuiMetaDataEd.cpp @@ -172,8 +172,8 @@ GuiMetaDataEd::GuiMetaDataEd(MetaDataList* md, row.addElement(ed, false, true); // Pass input to the actual RatingComponent instead of the spacer. - row.input_handler = std::bind(&GuiComponent::input, ed.get(), std::placeholders::_1, - std::placeholders::_2); + row.inputHandler = std::bind(&GuiComponent::input, ed.get(), std::placeholders::_1, + std::placeholders::_2); break; } case MD_DATE: { @@ -187,8 +187,8 @@ GuiMetaDataEd::GuiMetaDataEd(MetaDataList* md, row.addElement(ed, false); // Pass input to the actual DateTimeEditComponent instead of the spacer. - row.input_handler = std::bind(&GuiComponent::input, ed.get(), std::placeholders::_1, - std::placeholders::_2); + row.inputHandler = std::bind(&GuiComponent::input, ed.get(), std::placeholders::_1, + std::placeholders::_2); break; } case MD_CONTROLLER: { diff --git a/es-app/src/guis/GuiScraperSearch.cpp b/es-app/src/guis/GuiScraperSearch.cpp index 7b36f3289..c30992c6e 100644 --- a/es-app/src/guis/GuiScraperSearch.cpp +++ b/es-app/src/guis/GuiScraperSearch.cpp @@ -350,7 +350,6 @@ void GuiScraperSearch::search(ScraperSearchParams& params) mAutomaticModeGameEntry = 0; mResultList->clear(); - mResultList->setLoopRows(false); mScraperResults.clear(); mMDRetrieveURLsHandle.reset(); mThumbnailReqMap.clear(); @@ -409,9 +408,7 @@ void GuiScraperSearch::stop() void GuiScraperSearch::onSearchDone(std::vector& results) { mResultList->clear(); - mScraperResults = results; - mResultList->setLoopRows(true); auto font = Font::get(FONT_SIZE_MEDIUM); unsigned int color {mMenuColorPrimary}; @@ -512,9 +509,10 @@ void GuiScraperSearch::onSearchDone(std::vector& results) gameName.append(" [").append(otherPlatforms).append("]"); row.elements.clear(); - row.addElement( - std::make_shared(Utils::String::toUpper(gameName), font, color), - false); + auto gameEntry = + std::make_shared(Utils::String::toUpper(gameName), font, color); + gameEntry->setHorizontalScrolling(true); + row.addElement(gameEntry, true); row.makeAcceptInputHandler([this, i] { returnResult(mScraperResults.at(i)); }); mResultList->addRow(row); } @@ -602,7 +600,7 @@ void GuiScraperSearch::updateInfoPane() mResultName->setText(Utils::String::toUpper(res.mdl.get("name"))); mResultDesc->setText(Utils::String::toUpper(res.mdl.get("desc"))); - mDescContainer->reset(); + mDescContainer->resetComponent(); mResultThumbnail->setImage(""); const std::string& thumb {res.screenshotUrl.empty() ? res.coverUrl : res.screenshotUrl}; @@ -670,7 +668,7 @@ bool GuiScraperSearch::input(InputConfig* config, Input input) if (config->isMappedTo("a", input) && input.value != 0) { if (mBlockAccept || mScraperResults.empty()) return true; - mResultList->setLoopRows(false); + mResultList->setHorizontalScrolling(false); } // Check whether we should allow a refine of the game name. @@ -691,7 +689,7 @@ bool GuiScraperSearch::input(InputConfig* config, Input input) allowRefine = true; if (allowRefine) { - mResultList->stopLooping(); + mResultList->resetSelectedRow(); openInputScreen(mLastSearch); } } diff --git a/es-app/src/views/GamelistView.cpp b/es-app/src/views/GamelistView.cpp index 2215f9a2e..b702cdb17 100644 --- a/es-app/src/views/GamelistView.cpp +++ b/es-app/src/views/GamelistView.cpp @@ -79,10 +79,10 @@ void GamelistView::onFileChanged(FileData* file, bool reloadGamelist) void GamelistView::onShow() { for (auto& animation : mLottieAnimComponents) - animation->resetFileAnimation(); + animation->resetComponent(); for (auto& animation : mGIFAnimComponents) - animation->resetFileAnimation(); + animation->resetComponent(); for (auto& video : mStaticVideoComponents) video->stopVideoPlayer(); @@ -795,10 +795,11 @@ void GamelistView::updateView(const CursorState& state) } for (auto& container : mContainerComponents) - container->reset(); + container->resetComponent(); - for (auto& scrollableText : mTextComponents) - scrollableText->resetLooping(); + // Reset horizontally scrolling text. + for (auto& text : mTextComponents) + text->resetComponent(); for (auto& rating : mRatingComponents) rating->setValue(file->metadata.get("rating")); diff --git a/es-app/src/views/SystemView.cpp b/es-app/src/views/SystemView.cpp index 13d193e5c..312d99e42 100644 --- a/es-app/src/views/SystemView.cpp +++ b/es-app/src/views/SystemView.cpp @@ -69,17 +69,18 @@ void SystemView::goToSystem(SystemData* system, bool animate) selector->setNeedsRefresh(); } + // Reset horizontally scrolling text. for (auto& text : mSystemElements[mPrimary->getCursor()].textComponents) - text->resetLooping(); + text->resetComponent(); for (auto& video : mSystemElements[mPrimary->getCursor()].videoComponents) video->setStaticVideo(); for (auto& anim : mSystemElements[mPrimary->getCursor()].lottieAnimComponents) - anim->resetFileAnimation(); + anim->resetComponent(); for (auto& anim : mSystemElements[mPrimary->getCursor()].GIFAnimComponents) - anim->resetFileAnimation(); + anim->resetComponent(); updateGameSelectors(); updateGameCount(); @@ -223,8 +224,9 @@ std::vector SystemView::getHelpPrompts() void SystemView::onCursorChanged(const CursorState& state) { + // Reset horizontally scrolling text. for (auto& text : mSystemElements[mPrimary->getCursor()].textComponents) - text->resetLooping(); + text->resetComponent(); const int cursor {mPrimary->getCursor()}; const int scrollVelocity {mPrimary->getScrollingVelocity()}; @@ -279,7 +281,7 @@ void SystemView::onCursorChanged(const CursorState& state) } for (auto& container : mSystemElements[mPrimary->getCursor()].containerComponents) - container->reset(); + container->resetComponent(); // This is needed to avoid erratic camera movements during extreme navigation input when using // slide transitions. This should very rarely occur during normal application usage. @@ -312,10 +314,10 @@ void SystemView::onCursorChanged(const CursorState& state) video->setStaticVideo(); for (auto& anim : mSystemElements[mPrimary->getCursor()].lottieAnimComponents) - anim->resetFileAnimation(); + anim->resetComponent(); for (auto& anim : mSystemElements[mPrimary->getCursor()].GIFAnimComponents) - anim->resetFileAnimation(); + anim->resetComponent(); updateGameSelectors(); startViewVideos(); diff --git a/es-core/src/GuiComponent.h b/es-core/src/GuiComponent.h index 988374e00..758229c49 100644 --- a/es-core/src/GuiComponent.h +++ b/es-core/src/GuiComponent.h @@ -310,8 +310,10 @@ public: virtual void pauseViewVideos() {} virtual void muteViewVideos() {} - // For Lottie animations. - virtual void resetFileAnimation() {}; + // Used to reset various components like text scrolling, animations etc. + virtual void resetComponent() {} + // Used by TextComponent. + virtual void setHorizontalScrolling(bool state) {} // Default implementation just handles and tags as normalized float pairs. // You probably want to keep this behavior for any derived classes as well as add your own. diff --git a/es-core/src/components/ComponentList.cpp b/es-core/src/components/ComponentList.cpp index 84e5e2ca8..6532a135e 100644 --- a/es-core/src/components/ComponentList.cpp +++ b/es-core/src/components/ComponentList.cpp @@ -22,11 +22,6 @@ ComponentList::ComponentList() , mRowHeight {std::round(Font::get(FONT_SIZE_MEDIUM)->getHeight())} , 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 @@ -85,8 +80,8 @@ bool ComponentList::input(InputConfig* config, Input input) } // Give it to the current row's input handler. - if (mEntries.at(mCursor).data.input_handler) { - if (mEntries.at(mCursor).data.input_handler(config, input)) + if (mEntries.at(mCursor).data.inputHandler) { + if (mEntries.at(mCursor).data.inputHandler(config, input)) return true; } else { @@ -132,12 +127,6 @@ bool ComponentList::input(InputConfig* config, Input input) void ComponentList::update(int deltaTime) { - if (!mFocused && mLoopRows) { - mLoopOffset = 0; - mLoopOffset2 = 0; - mLoopTime = 0; - } - // Scroll indicator logic, used by ScrollIndicatorComponent. bool scrollIndicatorChanged {false}; @@ -167,39 +156,11 @@ void ComponentList::update(int deltaTime) listUpdate(deltaTime); - if (size()) { - float rowWidth {0.0f}; - + if (mFocused && size()) { // Update our currently selected row. for (auto it = mEntries.at(mCursor).data.elements.cbegin(); 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 {1500.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(delay + scrollTime + returnTime)}; - - mLoopTime += deltaTime; - while (mLoopTime > maxTime) - mLoopTime -= maxTime; - - mLoopOffset = static_cast(Utils::Math::loop(delay, scrollTime + returnTime, - static_cast(mLoopTime), - scrollLength + returnLength)); - - if (mLoopOffset > (scrollLength - (mSize.x - returnLength))) - mLoopOffset2 = static_cast(mLoopOffset - (scrollLength + returnLength)); - else if (mLoopOffset2 < 0) - mLoopOffset2 = 0; } } } @@ -208,12 +169,6 @@ 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; @@ -299,10 +254,9 @@ void ComponentList::render(const glm::mat4& parentTrans) mRenderer->pushClipRect(glm::ivec2 {clipRectPosX, clipRectPosY}, glm::ivec2 {clipRectSizeX, clipRectSizeY}); - // Scroll the camera. + // Move camera the scroll distance. trans = glm::translate(trans, glm::vec3 {0.0f, -mCameraOffset, 0.0f}); - glm::mat4 loopTrans {trans}; const bool darkColorScheme {Settings::getInstance()->getString("MenuColorScheme") != "light"}; // Draw selector bar if we're using the dark color scheme. @@ -316,30 +270,10 @@ void ComponentList::render(const glm::mat4& parentTrans) std::vector drawAfterCursor; bool drawAll {false}; for (size_t i {0}; i < mEntries.size(); ++i) { - - if (mLoopRows && mFocused && mLoopOffset > 0) { - loopTrans = - glm::translate(trans, glm::vec3 {static_cast(-mLoopOffset), 0.0f, 0.0f}); - } - auto& entry = mEntries.at(i); drawAll = !mFocused || i != static_cast(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(-mLoopOffset2), 0.0f, 0.0f}); - it->component->render(loopTrans); - } - }; - + if (drawAll || it->invertWhenSelected) { // 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). @@ -358,14 +292,14 @@ void ComponentList::render(const glm::mat4& parentTrans) unsigned char byteBlue {static_cast(origColor >> 8 & 0xFF)}; // If it's neutral, just proceed with normal rendering. if (byteRed == byteGreen && byteGreen == byteBlue) { - renderLoopFunc(); + it->component->render(trans); } else { if (isTextComponent) it->component->setColor(mMenuColorPrimary); else it->component->setColorShift(mMenuColorPrimary); - renderLoopFunc(); + it->component->render(trans); // Revert to the original color after rendering. if (isTextComponent) it->component->setColor(origColor); @@ -383,7 +317,6 @@ void ComponentList::render(const glm::mat4& parentTrans) } } - // Custom rendering. mRenderer->setMatrix(trans); // Draw selector bar if we're using the light color scheme. @@ -408,14 +341,14 @@ void ComponentList::render(const glm::mat4& parentTrans) } // Draw separators. - float y {0.0f}; + float offsetY {0.0f}; for (unsigned int i {0}; i < mEntries.size(); ++i) { - mRenderer->drawRect(0.0f, y, mSize.x, 1.0f * mRenderer->getScreenResolutionModifier(), + mRenderer->drawRect(0.0f, offsetY, mSize.x, 1.0f * mRenderer->getScreenResolutionModifier(), mMenuColorSeparators, mMenuColorSeparators, false, mOpacity, mDimming); - y += mRowHeight; + offsetY += mRowHeight; } - mRenderer->drawRect(0.0f, y, mSize.x, 1.0f * mRenderer->getScreenResolutionModifier(), + mRenderer->drawRect(0.0f, offsetY, mSize.x, 1.0f * mRenderer->getScreenResolutionModifier(), mMenuColorSeparators, mMenuColorSeparators, false, mOpacity, mDimming); mRenderer->popClipRect(); } @@ -427,14 +360,14 @@ void ComponentList::updateElementPosition(const ComponentListRow& row) yOffset += mRowHeight; // Assumes updateElementSize has already been called. - float x {mHorizontalPadding / 2.0f}; + float offsetX {mHorizontalPadding / 2.0f}; for (unsigned int i {0}; i < row.elements.size(); ++i) { const auto comp = row.elements.at(i).component; // Center vertically. - comp->setPosition(x, (mRowHeight - std::floor(comp->getSize().y)) / 2.0f + yOffset); - x += comp->getSize().x; + comp->setPosition(offsetX, (mRowHeight - std::floor(comp->getSize().y)) / 2.0f + yOffset); + offsetX += comp->getSize().x; } } @@ -444,13 +377,13 @@ void ComponentList::updateElementSize(const ComponentListRow& row) std::vector> resizeVec; for (auto it = row.elements.cbegin(); it != row.elements.cend(); ++it) { - if (it->resize_width) + if (it->resizeWidth) resizeVec.push_back(it->component); else width -= it->component->getSize().x; } - // Redistribute the "unused" width equally among the components with resize_width set to true. + // Redistribute the "unused" width equally among the components if resizeWidth is set to true. width = width / resizeVec.size(); for (auto it = resizeVec.cbegin(); it != resizeVec.cend(); ++it) (*it)->setSize(width, (*it)->getSize().y); @@ -487,9 +420,9 @@ std::vector ComponentList::getHelpPrompts() return prompts; } -bool ComponentList::moveCursor(int amt) +bool ComponentList::moveCursor(int amount) { - bool ret {listInput(amt)}; + const bool returnValue {listInput(amount)}; listInput(0); - return ret; + return returnValue; } diff --git a/es-core/src/components/ComponentList.h b/es-core/src/components/ComponentList.h index 57be3562a..53c674096 100644 --- a/es-core/src/components/ComponentList.h +++ b/es-core/src/components/ComponentList.h @@ -12,41 +12,41 @@ #include "IList.h" struct ComponentListElement { - ComponentListElement(const std::shared_ptr& cmp = nullptr, - bool resize_w = true, - bool inv = true) - : component(cmp) - , resize_width(resize_w) - , invert_when_selected(inv) + ComponentListElement(const std::shared_ptr& componentArg = nullptr, + bool resizeWidthArg = true, + bool invertWhenSelectedArg = true) + : component {componentArg} + , resizeWidth {resizeWidthArg} + , invertWhenSelected {invertWhenSelectedArg} { } std::shared_ptr component; - bool resize_width; - bool invert_when_selected; + bool resizeWidth; + bool invertWhenSelected; }; struct ComponentListRow { std::vector elements; // The input handler is called when the user enters any input while this row is - // highlighted (including up/down). + // highlighted (including up/down navigation). // Return false to let the list try to use it or true if the input has been consumed. - // If no input handler is supplied (input_handler == nullptr), the default behavior is - // to forward the input to the rightmost element in the currently selected row. - std::function input_handler; + // If no input handler is supplied (inputHandler == nullptr), then the default behavior + // is to forward the input to the rightmost element in the currently selected row. + std::function inputHandler; void addElement(const std::shared_ptr& component, - bool resize_width, - bool invert_when_selected = true) + bool resizeWidth, + bool invertWhenSelected = true) { - elements.push_back(ComponentListElement(component, resize_width, invert_when_selected)); + elements.push_back(ComponentListElement(component, resizeWidth, invertWhenSelected)); } - // Utility function for making an input handler for "when the users presses A on this, do func". + // Utility function for making an input handler for an input event. void makeAcceptInputHandler(const std::function& func) { - input_handler = [func](InputConfig* config, Input input) -> bool { + inputHandler = [func](InputConfig* config, Input input) -> bool { if (config->isMappedTo("a", input) && input.value != 0) { func(); return true; @@ -78,26 +78,33 @@ public: void onSizeChanged() override; void onFocusGained() override { mFocused = true; } - void onFocusLost() override { mFocused = false; } + void onFocusLost() override + { + mFocused = false; + resetSelectedRow(); + } - bool moveCursor(int amt); + bool moveCursor(int amount); int getCursorId() const { return mCursor; } const float getRowHeight() const { return mRowHeight; } void setRowHeight(float height) { mRowHeight = height; } const float getTotalRowHeight() const { return mRowHeight * mEntries.size(); } - // Horizontal looping for row content that doesn't fit on-screen. - void setLoopRows(bool state) + void resetSelectedRow() { - stopLooping(); - mLoopRows = state; + if (mEntries.size() > static_cast(mCursor)) { + for (auto& comp : mEntries.at(mCursor).data.elements) + comp.component->resetComponent(); + } } - void stopLooping() + + void setHorizontalScrolling(bool state) { - mLoopOffset = 0; - mLoopOffset2 = 0; - mLoopTime = 0; + for (auto& entry : mEntries) { + for (auto& element : entry.data.elements) + element.component->setHorizontalScrolling(state); + } } void resetScrollIndicatorStatus() @@ -140,12 +147,6 @@ private: float mSelectorBarOffset; float mCameraOffset; - bool mLoopRows; - bool mLoopScroll; - int mLoopOffset; - int mLoopOffset2; - int mLoopTime; - std::function mCursorChangedCallback; std::function mScrollIndicatorChangedCallback; diff --git a/es-core/src/components/GIFAnimComponent.cpp b/es-core/src/components/GIFAnimComponent.cpp index d0aa3ce9c..ad22af7c9 100644 --- a/es-core/src/components/GIFAnimComponent.cpp +++ b/es-core/src/components/GIFAnimComponent.cpp @@ -263,7 +263,7 @@ void GIFAnimComponent::setAnimation(const std::string& path) mAnimationStartTime = std::chrono::system_clock::now(); } -void GIFAnimComponent::resetFileAnimation() +void GIFAnimComponent::resetComponent() { mExternalPause = false; mPlayCount = 0; diff --git a/es-core/src/components/GIFAnimComponent.h b/es-core/src/components/GIFAnimComponent.h index 883f368b8..f3bfe067a 100644 --- a/es-core/src/components/GIFAnimComponent.h +++ b/es-core/src/components/GIFAnimComponent.h @@ -27,7 +27,7 @@ public: void setAnimation(const std::string& path); void setPauseAnimation(bool state) { mExternalPause = state; } - void resetFileAnimation() override; + void resetComponent() override; void onSizeChanged() override; virtual void applyTheme(const std::shared_ptr& theme, diff --git a/es-core/src/components/LottieAnimComponent.cpp b/es-core/src/components/LottieAnimComponent.cpp index 23a3a7690..4fe6433a4 100644 --- a/es-core/src/components/LottieAnimComponent.cpp +++ b/es-core/src/components/LottieAnimComponent.cpp @@ -229,7 +229,7 @@ void LottieAnimComponent::setAnimation(const std::string& path) mAnimationStartTime = std::chrono::system_clock::now(); } -void LottieAnimComponent::resetFileAnimation() +void LottieAnimComponent::resetComponent() { mExternalPause = false; mPlayCount = 0; diff --git a/es-core/src/components/LottieAnimComponent.h b/es-core/src/components/LottieAnimComponent.h index 30f121c72..d1a1a170b 100644 --- a/es-core/src/components/LottieAnimComponent.h +++ b/es-core/src/components/LottieAnimComponent.h @@ -39,7 +39,7 @@ public: } void setPauseAnimation(bool state) { mExternalPause = state; } - void resetFileAnimation() override; + void resetComponent() override; void onSizeChanged() override; virtual void applyTheme(const std::shared_ptr& theme, diff --git a/es-core/src/components/ScrollableContainer.cpp b/es-core/src/components/ScrollableContainer.cpp index 07d4ec040..5bc21be91 100644 --- a/es-core/src/components/ScrollableContainer.cpp +++ b/es-core/src/components/ScrollableContainer.cpp @@ -41,7 +41,7 @@ void ScrollableContainer::setAutoScroll(bool autoScroll) if (autoScroll) { mScrollDir = glm::vec2 {0.0f, 1.0f}; mAutoScrollDelay = static_cast(mAutoScrollDelayConstant); - reset(); + resetComponent(); } else { mScrollDir = glm::vec2 {}; @@ -60,7 +60,7 @@ void ScrollableContainer::setScrollParameters(float autoScrollDelayConstant, mAutoScrollSpeedConstant = AUTO_SCROLL_SPEED / glm::clamp(autoScrollSpeedConstant, 0.1f, 10.0f); } -void ScrollableContainer::reset() +void ScrollableContainer::resetComponent() { mScrollPos = glm::vec2 {0.0f, 0.0f}; mAutoScrollResetAccumulator = 0; @@ -161,7 +161,7 @@ void ScrollableContainer::update(int deltaTime) if (mWindow->isMediaViewerActive() || mWindow->isScreensaverActive() || !mWindow->getAllowTextScrolling()) { if (mScrollPos != glm::vec2 {0.0f, 0.0f} && !mWindow->isLaunchScreenDisplayed()) - reset(); + resetComponent(); return; } diff --git a/es-core/src/components/ScrollableContainer.h b/es-core/src/components/ScrollableContainer.h index 816b5b204..966b691c3 100644 --- a/es-core/src/components/ScrollableContainer.h +++ b/es-core/src/components/ScrollableContainer.h @@ -34,7 +34,7 @@ public: void setScrollParameters(float autoScrollDelayConstant, float autoScrollResetDelayConstant, float autoScrollSpeedConstant) override; - void reset(); + void resetComponent() override; void applyTheme(const std::shared_ptr& theme, const std::string& view, diff --git a/es-core/src/components/TextComponent.cpp b/es-core/src/components/TextComponent.cpp index 8bed4c33c..81122bfd0 100644 --- a/es-core/src/components/TextComponent.cpp +++ b/es-core/src/components/TextComponent.cpp @@ -33,14 +33,13 @@ TextComponent::TextComponent() , mNoTopMargin {false} , mSelectable {false} , mVerticalAutoSizing {false} - , mLoopHorizontal {false} - , mLoopScroll {false} - , mLoopSpeed {0.0f} - , mLoopSpeedMultiplier {1.0f} - , mLoopDelay {1500.0f} - , mLoopOffset1 {0} - , mLoopOffset2 {0} - , mLoopTime {0} + , mHorizontalScrolling {false} + , mScrollSpeed {0.0f} + , mScrollSpeedMultiplier {1.0f} + , mScrollDelay {1500.0f} + , mScrollOffset1 {0.0f} + , mScrollOffset2 {0.0f} + , mScrollTime {0.0f} { } @@ -71,14 +70,13 @@ TextComponent::TextComponent(const std::string& text, , mNoTopMargin {false} , mSelectable {false} , mVerticalAutoSizing {false} - , mLoopHorizontal {false} - , mLoopScroll {false} - , mLoopSpeed {0.0f} - , mLoopSpeedMultiplier {1.0f} - , mLoopDelay {1500.0f} - , mLoopOffset1 {0} - , mLoopOffset2 {0} - , mLoopTime {0} + , mHorizontalScrolling {false} + , mScrollSpeed {0.0f} + , mScrollSpeedMultiplier {1.0f} + , mScrollDelay {1500.0f} + , mScrollOffset1 {0.0f} + , mScrollOffset2 {0.0f} + , mScrollTime {0.0f} { setFont(font); setColor(color); @@ -200,7 +198,7 @@ void TextComponent::render(const glm::mat4& parentTrans) mRenderer->drawRect(0.0f, 0.0f, mSize.x, mSize.y, 0x0000FF33, 0x0000FF33); } - if (mLoopHorizontal && mTextCache != nullptr) { + if (mHorizontalScrolling && mTextCache != nullptr) { // Clip everything to be inside our bounds. glm::vec3 dim {mSize.x, mSize.y, 0.0f}; dim.x = (trans[0].x * dim.x + trans[3].x) - trans[3].x; @@ -218,13 +216,12 @@ void TextComponent::render(const glm::mat4& parentTrans) if (mTextCache->metrics.size.x < mSize.x) { if (mHorizontalAlignment == Alignment::ALIGN_CENTER) - offsetX = static_cast((mSize.x - mTextCache->metrics.size.x) / 2.0f); + offsetX = (mSize.x - mTextCache->metrics.size.x) / 2.0f; else if (mHorizontalAlignment == Alignment::ALIGN_RIGHT) offsetX = mSize.x - mTextCache->metrics.size.x; } - trans = glm::translate(trans, - glm::vec3 {offsetX - static_cast(mLoopOffset1), 0.0f, 0.0f}); + trans = glm::translate(trans, glm::vec3 {offsetX - mScrollOffset1, 0.0f, 0.0f}); } auto renderFunc = [this](glm::mat4 trans) { @@ -271,17 +268,18 @@ void TextComponent::render(const glm::mat4& parentTrans) break; } case ALIGN_CENTER: { - mRenderer->drawRect( - mLoopHorizontal ? 0.0f : (mSize.x - mTextCache->metrics.size.x) / 2.0f, - 0.0f, mTextCache->metrics.size.x, mTextCache->metrics.size.y, - 0x00000033, 0x00000033); + mRenderer->drawRect(mHorizontalScrolling ? + 0.0f : + (mSize.x - mTextCache->metrics.size.x) / 2.0f, + 0.0f, mTextCache->metrics.size.x, + mTextCache->metrics.size.y, 0x00000033, 0x00000033); break; } case ALIGN_RIGHT: { - mRenderer->drawRect(mLoopHorizontal ? 0.0f : - mSize.x - mTextCache->metrics.size.x, - 0.0f, mTextCache->metrics.size.x, - mTextCache->metrics.size.y, 0x00000033, 0x00000033); + mRenderer->drawRect( + mHorizontalScrolling ? 0.0f : mSize.x - mTextCache->metrics.size.x, + 0.0f, mTextCache->metrics.size.x, mTextCache->metrics.size.y, + 0x00000033, 0x00000033); break; } default: { @@ -295,21 +293,15 @@ void TextComponent::render(const glm::mat4& parentTrans) renderFunc(trans); - if (mLoopHorizontal && mTextCache != nullptr && mTextCache->metrics.size.x > mSize.x) { - // Needed to avoid flickering when returning to the start position. - if (mLoopOffset1 == 0 && mLoopOffset2 == 0) - mLoopScroll = false; - // Render again if text has moved far enough for it to repeat. - if (mLoopOffset2 < 0 || (mLoopDelay != 0.0f && mLoopScroll)) { - mLoopScroll = true; + if (mHorizontalScrolling && mTextCache != nullptr && mTextCache->metrics.size.x > mSize.x) { + if (mScrollOffset2 < 0.0f) { trans = glm::translate(parentTrans * getTransform(), - glm::vec3 {static_cast(-mLoopOffset2), 0.0f, 0.0f}); - mRenderer->setMatrix(trans); + glm::vec3 {-mScrollOffset2, 0.0f, 0.0f}); renderFunc(trans); } } - if (mLoopHorizontal && mTextCache != nullptr) + if (mHorizontalScrolling && mTextCache != nullptr) mRenderer->popClipRect(); } @@ -320,7 +312,7 @@ void TextComponent::setValue(const std::string& value) mThemeMetadata == "genre" || mThemeMetadata == "players")) { setText(mDefaultValue); } - else if (mLoopHorizontal) { + else if (mHorizontalScrolling) { setText(Utils::String::replace(value, "\n", " ")); } else { @@ -328,53 +320,52 @@ void TextComponent::setValue(const std::string& value) } } -void TextComponent::setHorizontalLooping(bool state) +void TextComponent::setHorizontalScrolling(bool state) { - resetLooping(); - mLoopHorizontal = state; + resetComponent(); + mHorizontalScrolling = state; - if (mLoopHorizontal) - mLoopSpeed = - mFont->sizeText("ABCDEFGHIJKLMNOPQRSTUVWXYZ").x * 0.247f * mLoopSpeedMultiplier; + if (mHorizontalScrolling) + mScrollSpeed = + mFont->sizeText("ABCDEFGHIJKLMNOPQRSTUVWXYZ").x * 0.247f * mScrollSpeedMultiplier; } void TextComponent::update(int deltaTime) { - if (mLoopHorizontal && mTextCache != nullptr) { + if (mHorizontalScrolling && mTextCache != nullptr) { // Don't scroll if the media viewer or screensaver is active or if text scrolling // is disabled; if (mWindow->isMediaViewerActive() || mWindow->isScreensaverActive() || !mWindow->getAllowTextScrolling()) { - if (mLoopTime != 0 && !mWindow->isLaunchScreenDisplayed()) - resetLooping(); + if (mScrollTime != 0 && !mWindow->isLaunchScreenDisplayed()) + resetComponent(); return; } - assert(mLoopSpeed != 0.0f); + assert(mScrollSpeed != 0.0f); - mLoopOffset1 = 0; - mLoopOffset2 = 0; + mScrollOffset1 = 0.0f; + mScrollOffset2 = 0.0f; if (mTextCache->metrics.size.x > mSize.x) { - // Loop the text. const float scrollLength {mTextCache->metrics.size.x}; - const float returnLength {mLoopSpeed * 1.5f / mLoopSpeedMultiplier}; - const float scrollTime {(scrollLength * 1000.0f) / mLoopSpeed}; - const float returnTime {(returnLength * 1000.0f) / mLoopSpeed}; - const int maxTime {static_cast(mLoopDelay + scrollTime + returnTime)}; + const float returnLength {mScrollSpeed * 1.5f / mScrollSpeedMultiplier}; + const float scrollTime {(scrollLength * 1000.0f) / mScrollSpeed}; + const float returnTime {(returnLength * 1000.0f) / mScrollSpeed}; + const float maxTime {mScrollDelay + scrollTime + returnTime}; - mLoopTime += deltaTime; - while (mLoopTime > maxTime) - mLoopTime -= maxTime; + mScrollTime += deltaTime; - mLoopOffset1 = static_cast(Utils::Math::loop(mLoopDelay, scrollTime + returnTime, - static_cast(mLoopTime), - scrollLength + returnLength)); + while (mScrollTime > maxTime) + mScrollTime -= maxTime; - if (mLoopOffset1 > (scrollLength - (mSize.x - returnLength))) - mLoopOffset2 = static_cast(mLoopOffset1 - (scrollLength + returnLength)); - else if (mLoopOffset2 < 0) - mLoopOffset2 = 0; + mScrollOffset1 = Utils::Math::loop(mScrollDelay, scrollTime + returnTime, mScrollTime, + scrollLength + returnLength); + + if (mScrollOffset1 > (scrollLength - (mSize.x - returnLength))) + mScrollOffset2 = mScrollOffset1 - (scrollLength + returnLength); + else if (mScrollOffset2 < 0) + mScrollOffset2 = 0; } } } @@ -418,7 +409,7 @@ void TextComponent::onTextChanged() const bool isMultiline {mAutoCalcExtent.y == 1 || mSize.y > lineHeight}; - if (mLoopHorizontal) { + if (mHorizontalScrolling) { mTextCache = std::shared_ptr(font->buildTextCache(text, 0.0f, 0.0f, mColor)); } else if (isMultiline && !isScrollable) { @@ -564,14 +555,14 @@ void TextComponent::applyTheme(const std::shared_ptr& theme, const std::string& containerType {elem->get("containerType")}; if (containerType == "horizontal") { if (elem->has("containerScrollSpeed")) { - mLoopSpeedMultiplier = + mScrollSpeedMultiplier = glm::clamp(elem->get("containerScrollSpeed"), 0.1f, 10.0f); } if (elem->has("containerStartDelay")) { - mLoopDelay = + mScrollDelay = glm::clamp(elem->get("containerStartDelay"), 0.0f, 10.0f) * 1000.0f; } - mLoopHorizontal = true; + mHorizontalScrolling = true; } else if (containerType != "vertical") { LOG(LogError) << "TextComponent: Invalid theme configuration, property " @@ -692,7 +683,7 @@ void TextComponent::applyTheme(const std::shared_ptr& theme, setFont(Font::getFromTheme(elem, properties, mFont, maxHeight, false)); - // We need to do this after setting the font as the loop speed is calculated from its size. - if (mLoopHorizontal) - setHorizontalLooping(true); + // We need to do this after setting the font as the scroll speed is calculated from its size. + if (mHorizontalScrolling) + setHorizontalScrolling(true); } diff --git a/es-core/src/components/TextComponent.h b/es-core/src/components/TextComponent.h index 49863f2f2..2744d18f2 100644 --- a/es-core/src/components/TextComponent.h +++ b/es-core/src/components/TextComponent.h @@ -48,6 +48,7 @@ public: void setRenderBackground(bool render) { mRenderBackground = render; } void render(const glm::mat4& parentTrans) override; + void onFocusLost() override { resetComponent(); } std::string getValue() const override { return mText; } void setValue(const std::string& value) override; @@ -86,14 +87,16 @@ public: return (mTextCache == nullptr ? 0 : mTextCache->metrics.maxGlyphHeight); } - // Horizontal looping for single-line content that is too long to fit. - void setHorizontalLooping(bool state); + // Horizontal scrolling for single-line content that is too long to fit. + void setHorizontalScrolling(bool state) override; + void setHorizontalScrollingSpeedMultiplier(float speed) { mScrollSpeedMultiplier = speed; } + void setHorizontalScrollingDelay(float delay) { mScrollDelay = delay; } - void resetLooping() + void resetComponent() { - mLoopOffset1 = 0; - mLoopOffset2 = 0; - mLoopTime = 0; + mScrollOffset1 = 0; + mScrollOffset2 = 0; + mScrollTime = 0; } void update(int deltaTime) override; @@ -144,14 +147,13 @@ private: bool mSelectable; bool mVerticalAutoSizing; - bool mLoopHorizontal; - bool mLoopScroll; - float mLoopSpeed; - float mLoopSpeedMultiplier; - float mLoopDelay; - int mLoopOffset1; - int mLoopOffset2; - int mLoopTime; + bool mHorizontalScrolling; + float mScrollSpeed; + float mScrollSpeedMultiplier; + float mScrollDelay; + float mScrollOffset1; + float mScrollOffset2; + float mScrollTime; }; #endif // ES_CORE_COMPONENTS_TEXT_COMPONENT_H diff --git a/es-core/src/guis/GuiInputConfig.cpp b/es-core/src/guis/GuiInputConfig.cpp index 1ef5e8f8a..ea45fe2fb 100644 --- a/es-core/src/guis/GuiInputConfig.cpp +++ b/es-core/src/guis/GuiInputConfig.cpp @@ -107,7 +107,7 @@ GuiInputConfig::GuiInputConfig(InputConfig* target, row.addElement(mapping, true); mMappings.push_back(mapping); - row.input_handler = [this, i, mapping](InputConfig* config, Input input) -> bool { + row.inputHandler = [this, i, mapping](InputConfig* config, Input input) -> bool { // Ignore input not from our target device. if (config != mTargetConfig) return false;