From a4ba7de7224523b578816a167b5d692ceaf7117c Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sun, 6 Aug 2023 23:51:53 +0200 Subject: [PATCH] Added a 'stationary' property to all secondary elements to set them as stationary during slide transitions Also changed the view mode enum to an enum class --- es-app/src/views/GamelistBase.cpp | 5 +- es-app/src/views/GamelistView.cpp | 79 ++++++++++++++-- es-app/src/views/SystemView.cpp | 94 +++++++++++++++++-- es-app/src/views/ViewController.cpp | 76 ++++++++------- es-app/src/views/ViewController.h | 6 +- es-core/src/GuiComponent.cpp | 23 ++--- es-core/src/GuiComponent.h | 10 ++ es-core/src/ThemeData.cpp | 8 ++ es-core/src/components/BadgeComponent.cpp | 16 ++++ es-core/src/components/DateTimeComponent.cpp | 35 +++++-- es-core/src/components/GIFAnimComponent.cpp | 16 ++++ es-core/src/components/ImageComponent.cpp | 16 ++++ .../src/components/LottieAnimComponent.cpp | 16 ++++ es-core/src/components/RatingComponent.cpp | 16 ++++ es-core/src/components/TextComponent.cpp | 17 ++++ es-core/src/components/VideoComponent.cpp | 16 ++++ 16 files changed, 376 insertions(+), 73 deletions(-) diff --git a/es-app/src/views/GamelistBase.cpp b/es-app/src/views/GamelistBase.cpp index 030a81f45..e9bf0b092 100644 --- a/es-app/src/views/GamelistBase.cpp +++ b/es-app/src/views/GamelistBase.cpp @@ -177,7 +177,7 @@ bool GamelistBase::input(InputConfig* config, Input input) mRoot->getSystem()->getThemeFolder() == "custom-collections" && mCursorStack.empty() && ViewController::getInstance()->getState().viewing == - ViewController::GAMELIST) { + ViewController::ViewMode::GAMELIST) { NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND); // Jump to the randomly selected game. if (mRandomGame) { @@ -233,7 +233,8 @@ bool GamelistBase::input(InputConfig* config, Input input) else if (config->isMappedTo("y", input) && mRoot->getSystem()->getThemeFolder() == "custom-collections" && !CollectionSystemsManager::getInstance()->isEditing() && mCursorStack.empty() && - ViewController::getInstance()->getState().viewing == ViewController::GAMELIST) { + ViewController::getInstance()->getState().viewing == + ViewController::ViewMode::GAMELIST) { // Jump to the randomly selected game. if (mRandomGame) { NavigationSounds::getInstance().playThemeNavigationSound(SELECTSOUND); diff --git a/es-app/src/views/GamelistView.cpp b/es-app/src/views/GamelistView.cpp index 115aa1439..8a32a5e48 100644 --- a/es-app/src/views/GamelistView.cpp +++ b/es-app/src/views/GamelistView.cpp @@ -400,12 +400,77 @@ void GamelistView::render(const glm::mat4& parentTrans) glm::mat4 trans {parentTrans * getTransform()}; // Make sure nothing renders outside our designated area. - mRenderer->pushClipRect( - glm::ivec2 {static_cast(std::round(trans[3].x)), - static_cast(std::round(trans[3].y))}, - glm::ivec2 {static_cast(std::round(mSize.x)), static_cast(std::round(mSize.y))}); + auto clipRectFunc = [this, trans]() { + mRenderer->pushClipRect(glm::ivec2 {static_cast(std::round(trans[3].x)), + static_cast(std::round(trans[3].y))}, + glm::ivec2 {static_cast(std::round(mSize.x)), + static_cast(std::round(mSize.y))}); + }; + + clipRectFunc(); + + const ViewController::State viewState {ViewController::getInstance()->getState()}; + bool stationaryApplicable {false}; + + // If it's the startup animation, then don't apply stationary properties. + if (viewState.previouslyViewed == ViewController::ViewMode::NOTHING) + stationaryApplicable = false; + + // If it's a gamelist to gamelist transition and these animations are set to slide. + if (static_cast(Settings::getInstance()->getInt( + "TransitionsGamelistToGamelist")) == ViewTransitionAnimation::SLIDE && + viewState.viewing == ViewController::ViewMode::GAMELIST && + viewState.previouslyViewed == ViewController::ViewMode::GAMELIST) + stationaryApplicable = true; + + // If it's a gamelist to system transition and these animations are set to slide. + if (static_cast(Settings::getInstance()->getInt( + "TransitionsGamelistToSystem")) == ViewTransitionAnimation::SLIDE && + viewState.viewing == ViewController::ViewMode::SYSTEM_SELECT) + stationaryApplicable = true; + + // If it's a system to gamelist transition and these animations are set to slide. + if (static_cast(Settings::getInstance()->getInt( + "TransitionsSystemToGamelist")) == ViewTransitionAnimation::SLIDE && + viewState.previouslyViewed == ViewController::ViewMode::SYSTEM_SELECT) + stationaryApplicable = true; + + for (unsigned int i {0}; i < getChildCount(); ++i) { + bool childStationary {false}; + if (stationaryApplicable) { + if (getChild(i)->getStationary() == Stationary::NEVER) { + childStationary = false; + } + else if (viewState.viewing == ViewController::ViewMode::GAMELIST && + viewState.previouslyViewed == ViewController::ViewMode::GAMELIST && + (getChild(i)->getStationary() == Stationary::WITHIN_VIEW || + getChild(i)->getStationary() == Stationary::ALWAYS)) { + childStationary = true; + } + else if (viewState.viewing == ViewController::ViewMode::SYSTEM_SELECT && + (getChild(i)->getStationary() == Stationary::BETWEEN_VIEWS || + getChild(i)->getStationary() == Stationary::ALWAYS)) { + childStationary = true; + } + else if (viewState.previouslyViewed == ViewController::ViewMode::SYSTEM_SELECT && + (getChild(i)->getStationary() == Stationary::BETWEEN_VIEWS || + getChild(i)->getStationary() == Stationary::ALWAYS)) { + childStationary = true; + } + } + + if (childStationary) { + if (viewState.getSystem() != mRoot->getSystem()) + continue; + mRenderer->popClipRect(); + getChild(i)->render(mRenderer->getIdentity()); + clipRectFunc(); + } + else { + getChild(i)->render(trans); + } + } - renderChildren(trans); mRenderer->popClipRect(); } @@ -423,7 +488,7 @@ std::vector GamelistView::getHelpPrompts() } if (mRoot->getSystem()->getThemeFolder() == "custom-collections" && mCursorStack.empty() && - ViewController::getInstance()->getState().viewing == ViewController::GAMELIST) + ViewController::getInstance()->getState().viewing == ViewController::ViewMode::GAMELIST) prompts.push_back(HelpPrompt("a", "select")); else prompts.push_back(HelpPrompt("a", "select")); @@ -440,7 +505,7 @@ std::vector GamelistView::getHelpPrompts() if (mRoot->getSystem()->getThemeFolder() == "custom-collections" && !CollectionSystemsManager::getInstance()->isEditing() && mCursorStack.empty() && - ViewController::getInstance()->getState().viewing == ViewController::GAMELIST) { + ViewController::getInstance()->getState().viewing == ViewController::ViewMode::GAMELIST) { prompts.push_back(HelpPrompt("y", "jump to game")); } else if (mRoot->getSystem()->isGameSystem() && diff --git a/es-app/src/views/SystemView.cpp b/es-app/src/views/SystemView.cpp index f6e296012..73f82ca19 100644 --- a/es-app/src/views/SystemView.cpp +++ b/es-app/src/views/SystemView.cpp @@ -1395,12 +1395,38 @@ void SystemView::renderElements(const glm::mat4& parentTrans, bool abovePrimary) int renderBefore {static_cast(mCamOffset)}; int renderAfter {static_cast(mCamOffset)}; - // If we're transitioning then also render the previous and next systems. - if (isAnimationPlaying(0)) { + const ViewController::State viewState {ViewController::getInstance()->getState()}; + + // If we're transitioning between systems, then also render the previous and next systems. + if (isAnimationPlaying(0) && viewState.viewing == ViewController::ViewMode::SYSTEM_SELECT) { renderBefore -= 1; renderAfter += 1; } + bool stationaryApplicable {false}; + + // If it's the startup animation, then don't apply stationary properties. + if (viewState.previouslyViewed == ViewController::ViewMode::NOTHING) + stationaryApplicable = false; + + // If it's a system to system transition and these animations are set to slide. + if (static_cast(Settings::getInstance()->getInt( + "TransitionsSystemToSystem")) == ViewTransitionAnimation::SLIDE && + isAnimationPlaying(0)) + stationaryApplicable = true; + + // If it's a system to gamelist transition and these animations are set to slide. + if (static_cast(Settings::getInstance()->getInt( + "TransitionsSystemToGamelist")) == ViewTransitionAnimation::SLIDE && + viewState.viewing == ViewController::ViewMode::GAMELIST) + stationaryApplicable = true; + + // If it's a gamelist to system transition and these animations are set to slide. + if (static_cast(Settings::getInstance()->getInt( + "TransitionsGamelistToSystem")) == ViewTransitionAnimation::SLIDE && + viewState.previouslyViewed == ViewController::ViewMode::GAMELIST) + stationaryApplicable = true; + for (int i {renderBefore}; i <= renderAfter; ++i) { int index {i}; while (index < 0) @@ -1432,13 +1458,46 @@ void SystemView::renderElements(const glm::mat4& parentTrans, bool abovePrimary) elementTrans, glm::round(glm::vec3 {0.0f, (i - mCamOffset) * mSize.y, 0.0f})); } - mRenderer->pushClipRect( - glm::ivec2 {static_cast(glm::round(elementTrans[3].x)), - static_cast(glm::round(elementTrans[3].y))}, - glm::ivec2 {static_cast(mSize.x), static_cast(mSize.y)}); + auto clipRectFunc = [this, elementTrans]() { + mRenderer->pushClipRect( + glm::ivec2 {static_cast(glm::round(elementTrans[3].x)), + static_cast(glm::round(elementTrans[3].y))}, + glm::ivec2 {static_cast(mSize.x), static_cast(mSize.y)}); + }; + + clipRectFunc(); if (mSystemElements.size() > static_cast(index)) { for (auto child : mSystemElements[index].children) { + bool renderChild {true}; + bool childStationary {false}; + if (stationaryApplicable) { + if (child->getStationary() == Stationary::NEVER) { + childStationary = false; + } + else if ((child->getStationary() == Stationary::WITHIN_VIEW || + child->getStationary() == Stationary::ALWAYS) && + isAnimationPlaying(0)) { + childStationary = true; + if (index != static_cast(std::round(mCamOffset))) { + if (mCamOffset <= mSystemElements.size() - 1) + renderChild = false; + if (mCamOffset > static_cast(mSystemElements.size() - 1) && + index != 0) + renderChild = false; + if (mCamOffset < + static_cast(mSystemElements.size()) - 0.5f && + index == 0) + renderChild = false; + } + } + else if ((child->getStationary() == Stationary::BETWEEN_VIEWS || + child->getStationary() == Stationary::ALWAYS) && + !isAnimationPlaying(0)) { + childStationary = true; + } + } + if (abovePrimary && (child->getZIndex() > primaryZIndex)) { if (mFadeTransitions && mPrimary->getFadeAbovePrimary()) { if (mFadeTransitions || child->getOpacity() != 1.0f) @@ -1447,13 +1506,30 @@ void SystemView::renderElements(const glm::mat4& parentTrans, bool abovePrimary) else { child->setOpacity(1.0f); } - child->render(elementTrans); + if (renderChild) { + if (childStationary) { + mRenderer->popClipRect(); + child->render(mRenderer->getIdentity()); + clipRectFunc(); + } + else { + child->render(elementTrans); + } + } } - else if (!abovePrimary && child->getZIndex() <= primaryZIndex) { if (mFadeTransitions || child->getDimming() != 1.0f) child->setDimming(1.0f - mFadeOpacity); - child->render(elementTrans); + if (renderChild) { + if (childStationary) { + mRenderer->popClipRect(); + child->render(mRenderer->getIdentity()); + clipRectFunc(); + } + else { + child->render(elementTrans); + } + } } } } diff --git a/es-app/src/views/ViewController.cpp b/es-app/src/views/ViewController.cpp index c36152f4f..dfb1b164e 100644 --- a/es-app/src/views/ViewController.cpp +++ b/es-app/src/views/ViewController.cpp @@ -50,7 +50,8 @@ ViewController::ViewController() noexcept , mLockInput {false} , mNextSystem {false} { - mState.viewing = NOTHING; + mState.viewing = ViewMode::NOTHING; + mState.previouslyViewed = ViewMode::NOTHING; } ViewController* ViewController::getInstance() @@ -410,7 +411,7 @@ void ViewController::ReloadAndGoToStart() { mWindow->renderSplashScreen(Window::SplashScreenState::RELOADING, 0.0f); reloadAll(); - if (mState.viewing == GAMELIST) { + if (mState.viewing == ViewMode::GAMELIST) { goToSystemView(SystemData::sSystemVector.front(), false); goToSystem(SystemData::sSystemVector.front(), false); } @@ -488,7 +489,7 @@ void ViewController::goToSystemView(SystemData* system, bool playTransition) { bool applicationStartup {false}; - if (mState.viewing == NOTHING) + if (mState.viewing == ViewMode::NOTHING) applicationStartup = true; // Restore the X position for the view, if it was previously moved. @@ -508,7 +509,8 @@ void ViewController::goToSystemView(SystemData* system, bool playTransition) if (system->isGroupedCustomCollection()) system = system->getRootFolder()->getParent()->getSystem(); - mState.viewing = SYSTEM_SELECT; + mState.previouslyViewed = mState.viewing; + mState.viewing = ViewMode::SYSTEM_SELECT; mState.system = system; mSystemViewTransition = true; @@ -577,7 +579,7 @@ void ViewController::goToSystem(SystemData* system, bool animate) void ViewController::goToNextGamelist() { - assert(mState.viewing == GAMELIST); + assert(mState.viewing == ViewMode::GAMELIST); SystemData* system {getState().getSystem()}; assert(system); NavigationSounds::getInstance().playThemeNavigationSound(QUICKSYSSELECTSOUND); @@ -587,7 +589,7 @@ void ViewController::goToNextGamelist() void ViewController::goToPrevGamelist() { - assert(mState.viewing == GAMELIST); + assert(mState.viewing == ViewMode::GAMELIST); SystemData* system {getState().getSystem()}; assert(system); NavigationSounds::getInstance().playThemeNavigationSound(QUICKSYSSELECTSOUND); @@ -608,12 +610,12 @@ void ViewController::goToGamelist(SystemData* system) ViewTransition transitionType; ViewTransitionAnimation transitionAnim; - if (mState.viewing == SYSTEM_SELECT) { + if (mState.viewing == ViewMode::SYSTEM_SELECT) { transitionType = ViewTransition::SYSTEM_TO_GAMELIST; transitionAnim = static_cast( Settings::getInstance()->getInt("TransitionsSystemToGamelist")); } - else if (mState.viewing == NOTHING) { + else if (mState.viewing == ViewMode::NOTHING) { transitionType = ViewTransition::STARTUP_TO_GAMELIST; transitionAnim = static_cast( Settings::getInstance()->getInt("TransitionsStartupToGamelist")); @@ -642,13 +644,13 @@ void ViewController::goToGamelist(SystemData* system) mPreviousView.reset(); mPreviousView = nullptr; } - else if (!mPreviousView && mState.viewing == GAMELIST) { + else if (!mPreviousView && mState.viewing == ViewMode::GAMELIST) { // This is needed as otherwise the static image would not get rendered during the // first Slide transition when coming from the System view. mSkipView = getGamelistView(system); } - if (mState.viewing != SYSTEM_SELECT) { + if (mState.viewing != ViewMode::SYSTEM_SELECT) { mPreviousView = mCurrentView; mSystemViewTransition = false; } @@ -659,7 +661,8 @@ void ViewController::goToGamelist(SystemData* system) // Find if we're wrapping around the first and last systems, which requires the gamelist // to be moved in order to avoid weird camera movements. This is only needed for the // slide transition style. - if (mState.viewing == GAMELIST && SystemData::sSystemVector.size() > 1 && slideTransitions) { + if (mState.viewing == ViewMode::GAMELIST && SystemData::sSystemVector.size() > 1 && + slideTransitions) { if (SystemData::sSystemVector.front() == mState.getSystem()) { if (SystemData::sSystemVector.back() == system) wrapFirstToLast = true; @@ -671,7 +674,7 @@ void ViewController::goToGamelist(SystemData* system) } // Stop any scrolling, animations and camera movements. - if (mState.viewing == SYSTEM_SELECT) { + if (mState.viewing == ViewMode::SYSTEM_SELECT) { mSystemListView->stopScrolling(); if (mSystemListView->isSystemAnimationPlaying(0)) mSystemListView->finishSystemAnimation(0); @@ -681,7 +684,7 @@ void ViewController::goToGamelist(SystemData* system) (!fadeTransitions && mLastTransitionAnim == ViewTransitionAnimation::FADE)) cancelViewTransitions(); - if (mState.viewing == SYSTEM_SELECT) { + if (mState.viewing == ViewMode::SYSTEM_SELECT) { // Move the system list. auto sysList = getSystemListView(); float offsetX {sysList->getPosition().x}; @@ -728,7 +731,7 @@ void ViewController::goToGamelist(SystemData* system) mCurrentView->finishAnimation(0); // Application startup animation, if starting in a gamelist rather than in the system view. - if (mState.viewing == NOTHING) { + if (mState.viewing == ViewMode::NOTHING) { if (mLastTransitionAnim == ViewTransitionAnimation::FADE) cancelViewTransitions(); mCamera = glm::translate(mCamera, glm::round(-mCurrentView->getPosition())); @@ -744,7 +747,8 @@ void ViewController::goToGamelist(SystemData* system) } } - mState.viewing = GAMELIST; + mState.previouslyViewed = mState.viewing; + mState.viewing = ViewMode::GAMELIST; mState.system = system; if (mCurrentView) @@ -1157,9 +1161,9 @@ void ViewController::render(const glm::mat4& parentTrans) glm::mat4 transInverse {glm::inverse(trans)}; // Camera position, position + size. - glm::vec3 viewStart {transInverse[3]}; - glm::vec3 viewEnd {std::fabs(trans[3].x) + Renderer::getScreenWidth(), - std::fabs(trans[3].y) + Renderer::getScreenHeight(), 0.0f}; + const glm::vec3 viewStart {transInverse[3]}; + const glm::vec3 viewEnd {std::fabs(trans[3].x) + Renderer::getScreenWidth(), + std::fabs(trans[3].y) + Renderer::getScreenHeight(), 0.0f}; // Keep track of UI mode changes. UIModeController::getInstance()->monitorUIMode(); @@ -1169,19 +1173,27 @@ void ViewController::render(const glm::mat4& parentTrans) if (mSystemListView == mCurrentView || (mSystemViewTransition && isCameraMoving())) getSystemListView()->render(trans); - // Draw the gamelists. - for (auto it = mGamelistViews.cbegin(); it != mGamelistViews.cend(); ++it) { - // Same thing as for the system view, limit the rendering only to what needs to be drawn. - if (it->second == mCurrentView || (it->second == mPreviousView && isCameraMoving())) { - // Clipping. - glm::vec3 guiStart {it->second->getPosition()}; - glm::vec3 guiEnd {it->second->getPosition() + - glm::vec3 {it->second->getSize().x, it->second->getSize().y, 0.0f}}; + auto gamelistRenderFunc = [this, trans, viewStart, viewEnd](auto it) { + const glm::vec3 guiStart {it->second->getPosition()}; + const glm::vec3 guiEnd {it->second->getPosition() + + glm::vec3 {it->second->getSize().x, it->second->getSize().y, 0.0f}}; + if (guiEnd.x >= viewStart.x && guiEnd.y >= viewStart.y && guiStart.x <= viewEnd.x && + guiStart.y <= viewEnd.y) + it->second->render(trans); + }; - if (guiEnd.x >= viewStart.x && guiEnd.y >= viewStart.y && guiStart.x <= viewEnd.x && - guiStart.y <= viewEnd.y) - it->second->render(trans); - } + // Draw the gamelists. In the same manner as for the system view, limit the rendering only + // to what needs to be drawn. + for (auto it = mGamelistViews.cbegin(); it != mGamelistViews.cend(); ++it) { + if (it->second == mPreviousView && isCameraMoving()) + gamelistRenderFunc(it); + } + + // Always render the currently selected system last so that any stationary elements will get + // correctly rendered on top. + for (auto it = mGamelistViews.cbegin(); it != mGamelistViews.cend(); ++it) { + if (it->second == mCurrentView) + gamelistRenderFunc(it); } if (mWindow->peekGui() == this) @@ -1364,10 +1376,10 @@ void ViewController::reloadAll() } // Update mCurrentView since the pointers changed. - if (mState.viewing == GAMELIST) { + if (mState.viewing == ViewMode::GAMELIST) { mCurrentView = getGamelistView(mState.getSystem()); } - else if (mState.viewing == SYSTEM_SELECT) { + else if (mState.viewing == ViewMode::SYSTEM_SELECT) { SystemData* system {mState.getSystem()}; mSystemListView->goToSystem(system, false); mCurrentView = mSystemListView; diff --git a/es-app/src/views/ViewController.h b/es-app/src/views/ViewController.h index 615ca1cdf..9f10a8b60 100644 --- a/es-app/src/views/ViewController.h +++ b/es-app/src/views/ViewController.h @@ -98,19 +98,19 @@ public: void update(int deltaTime) override; void render(const glm::mat4& parentTrans) override; - enum ViewMode { + enum class ViewMode { NOTHING, - START_SCREEN, SYSTEM_SELECT, GAMELIST }; struct State { ViewMode viewing; + ViewMode previouslyViewed; SystemData* getSystem() const { - assert(viewing == GAMELIST || viewing == SYSTEM_SELECT); + assert(viewing == ViewMode::GAMELIST || viewing == ViewMode::SYSTEM_SELECT); return system; } diff --git a/es-core/src/GuiComponent.cpp b/es-core/src/GuiComponent.cpp index 84d532ac2..d21707f15 100644 --- a/es-core/src/GuiComponent.cpp +++ b/es-core/src/GuiComponent.cpp @@ -30,6 +30,7 @@ GuiComponent::GuiComponent() , mOrigin {0.0f, 0.0f} , mRotationOrigin {0.5f, 0.5f} , mSize {0.0f, 0.0f} + , mStationary {Stationary::NEVER} , mBrightness {0.0f} , mOpacity {1.0f} , mSaturation {1.0f} @@ -45,7 +46,7 @@ GuiComponent::GuiComponent() , mEnabled {true} , mTransform {Renderer::getIdentity()} { - for (unsigned char i = 0; i < MAX_ANIMATIONS; ++i) + for (unsigned char i {0}; i < MAX_ANIMATIONS; ++i) mAnimationMap[i] = nullptr; } @@ -58,7 +59,7 @@ GuiComponent::~GuiComponent() if (mParent) mParent->removeChild(this); - for (unsigned int i = 0; i < getChildCount(); ++i) + for (unsigned int i {0}; i < getChildCount(); ++i) getChild(i)->setParent(nullptr); } @@ -70,7 +71,7 @@ void GuiComponent::textInput(const std::string& text, const bool pasting) bool GuiComponent::input(InputConfig* config, Input input) { - for (unsigned int i = 0; i < getChildCount(); ++i) { + for (unsigned int i {0}; i < getChildCount(); ++i) { if (getChild(i)->input(config, input)) return true; } @@ -147,7 +148,7 @@ void GuiComponent::removeChild(GuiComponent* cmp) cmp->setParent(nullptr); - for (auto i = mChildren.cbegin(); i != mChildren.cend(); ++i) { + for (auto i {mChildren.cbegin()}; i != mChildren.cend(); ++i) { if (*i == cmp) { mChildren.erase(i); return; @@ -252,13 +253,13 @@ const bool GuiComponent::advanceAnimation(unsigned char slot, unsigned int time) void GuiComponent::stopAllAnimations() { - for (unsigned char i = 0; i < MAX_ANIMATIONS; ++i) + for (unsigned char i {0}; i < MAX_ANIMATIONS; ++i) stopAnimation(i); } void GuiComponent::cancelAllAnimations() { - for (unsigned char i = 0; i < MAX_ANIMATIONS; ++i) + for (unsigned char i {0}; i < MAX_ANIMATIONS; ++i) cancelAnimation(i); } @@ -327,13 +328,13 @@ const glm::mat4& GuiComponent::getTransform() void GuiComponent::onShow() { - for (unsigned int i = 0; i < getChildCount(); ++i) + for (unsigned int i {0}; i < getChildCount(); ++i) getChild(i)->onShow(); } void GuiComponent::onHide() { - for (unsigned int i = 0; i < getChildCount(); ++i) + for (unsigned int i {0}; i < getChildCount(); ++i) getChild(i)->onHide(); } @@ -413,18 +414,18 @@ void GuiComponent::updateHelpPrompts() void GuiComponent::updateSelf(int deltaTime) { - for (unsigned char i = 0; i < MAX_ANIMATIONS; ++i) + for (unsigned char i {0}; i < MAX_ANIMATIONS; ++i) advanceAnimation(i, deltaTime); } void GuiComponent::updateChildren(int deltaTime) { - for (unsigned int i = 0; i < getChildCount(); ++i) + for (unsigned int i {0}; i < getChildCount(); ++i) getChild(i)->update(deltaTime); } void GuiComponent::renderChildren(const glm::mat4& transform) const { - for (unsigned int i = 0; i < getChildCount(); ++i) + for (unsigned int i {0}; i < getChildCount(); ++i) getChild(i)->render(transform); } diff --git a/es-core/src/GuiComponent.h b/es-core/src/GuiComponent.h index d5366d7a0..988374e00 100644 --- a/es-core/src/GuiComponent.h +++ b/es-core/src/GuiComponent.h @@ -57,6 +57,13 @@ enum class LetterCase { UNDEFINED }; +enum class Stationary { + NEVER, + ALWAYS, + WITHIN_VIEW, + BETWEEN_VIEWS +}; + class GuiComponent { public: @@ -109,6 +116,8 @@ public: } void setRotationOrigin(glm::vec2 origin) { setRotationOrigin(origin.x, origin.y); } + const Stationary getStationary() const { return mStationary; } + virtual glm::vec2 getSize() const { return mSize; } void setSize(const glm::vec2& size) { setSize(size.x, size.y); } void setSize(const float w, const float h); @@ -388,6 +397,7 @@ protected: glm::vec2 mOrigin; glm::vec2 mRotationOrigin; glm::vec2 mSize; + Stationary mStationary; float mBrightness; float mOpacity; diff --git a/es-core/src/ThemeData.cpp b/es-core/src/ThemeData.cpp index dbc662b95..486946ddb 100644 --- a/es-core/src/ThemeData.cpp +++ b/es-core/src/ThemeData.cpp @@ -254,6 +254,7 @@ std::map> {"origin", NORMALIZED_PAIR}, {"rotation", FLOAT}, {"rotationOrigin", NORMALIZED_PAIR}, + {"stationary", STRING}, {"flipHorizontal", BOOLEAN}, {"flipVertical", BOOLEAN}, {"path", PATH}, @@ -283,6 +284,7 @@ std::map> {"cropSize", NORMALIZED_PAIR}, {"maxSize", NORMALIZED_PAIR}, {"origin", NORMALIZED_PAIR}, + {"stationary", STRING}, {"path", PATH}, {"default", PATH}, {"defaultImage", PATH}, @@ -313,6 +315,7 @@ std::map> {"origin", NORMALIZED_PAIR}, {"rotation", FLOAT}, {"rotationOrigin", NORMALIZED_PAIR}, + {"stationary", STRING}, {"metadataElement", BOOLEAN}, {"path", PATH}, {"speed", FLOAT}, @@ -333,6 +336,7 @@ std::map> {"origin", NORMALIZED_PAIR}, {"rotation", FLOAT}, {"rotationOrigin", NORMALIZED_PAIR}, + {"stationary", STRING}, {"horizontalAlignment", STRING}, {"direction", STRING}, {"lines", UNSIGNED_INTEGER}, @@ -364,6 +368,7 @@ std::map> {"origin", NORMALIZED_PAIR}, {"rotation", FLOAT}, {"rotationOrigin", NORMALIZED_PAIR}, + {"stationary", STRING}, {"text", STRING}, {"systemdata", STRING}, {"metadata", STRING}, @@ -395,6 +400,7 @@ std::map> {"origin", NORMALIZED_PAIR}, {"rotation", FLOAT}, {"rotationOrigin", NORMALIZED_PAIR}, + {"stationary", STRING}, {"metadata", STRING}, {"defaultValue", STRING}, {"gameselector", STRING}, @@ -418,6 +424,7 @@ std::map> {"origin", NORMALIZED_PAIR}, {"rotation", FLOAT}, {"rotationOrigin", NORMALIZED_PAIR}, + {"stationary", STRING}, {"fontPath", PATH}, {"fontSize", FLOAT}, {"horizontalAlignment", STRING}, @@ -433,6 +440,7 @@ std::map> {"origin", NORMALIZED_PAIR}, {"rotation", FLOAT}, {"rotationOrigin", NORMALIZED_PAIR}, + {"stationary", STRING}, {"gameselector", STRING}, {"gameselectorEntry", UNSIGNED_INTEGER}, {"interpolation", STRING}, diff --git a/es-core/src/components/BadgeComponent.cpp b/es-core/src/components/BadgeComponent.cpp index 660f30562..30a0f75ff 100644 --- a/es-core/src/components/BadgeComponent.cpp +++ b/es-core/src/components/BadgeComponent.cpp @@ -215,6 +215,22 @@ void BadgeComponent::applyTheme(const std::shared_ptr& theme, if (!elem) return; + if (properties & ThemeFlags::POSITION && elem->has("stationary")) { + const std::string& stationary {elem->get("stationary")}; + if (stationary == "never") + mStationary = Stationary::NEVER; + else if (stationary == "always") + mStationary = Stationary::ALWAYS; + else if (stationary == "withinView") + mStationary = Stationary::WITHIN_VIEW; + else if (stationary == "betweenViews") + mStationary = Stationary::BETWEEN_VIEWS; + else + LOG(LogWarning) << "BadgeComponent: Invalid theme configuration, property " + "\"stationary\" for element \"" + << element.substr(7) << "\" defined as \"" << stationary << "\""; + } + if (elem->has("horizontalAlignment")) { const std::string horizontalAlignment {elem->get("horizontalAlignment")}; if (horizontalAlignment != "left" && horizontalAlignment != "center" && diff --git a/es-core/src/components/DateTimeComponent.cpp b/es-core/src/components/DateTimeComponent.cpp index 95b31330f..36e66c618 100644 --- a/es-core/src/components/DateTimeComponent.cpp +++ b/es-core/src/components/DateTimeComponent.cpp @@ -125,6 +125,22 @@ void DateTimeComponent::applyTheme(const std::shared_ptr& theme, if (!elem) return; + if (properties & ThemeFlags::POSITION && elem->has("stationary")) { + const std::string& stationary {elem->get("stationary")}; + if (stationary == "never") + mStationary = Stationary::NEVER; + else if (stationary == "always") + mStationary = Stationary::ALWAYS; + else if (stationary == "withinView") + mStationary = Stationary::WITHIN_VIEW; + else if (stationary == "betweenViews") + mStationary = Stationary::BETWEEN_VIEWS; + else + LOG(LogWarning) << "DateTimeComponent: Invalid theme configuration, property " + "\"stationary\" for element \"" + << element.substr(9) << "\" defined as \"" << stationary << "\""; + } + if (elem->has("format")) setFormat(elem->get("format")); @@ -147,8 +163,9 @@ void DateTimeComponent::applyTheme(const std::shared_ptr& theme, setHorizontalAlignment(ALIGN_RIGHT); else LOG(LogWarning) << "DateTimeComponent: Invalid theme configuration, property " - " defined as \"" - << horizontalAlignment << "\""; + "\"horizontalAlignment\" for element \"" + << element.substr(9) << "\" defined as \"" << horizontalAlignment + << "\""; } if (properties & ALIGNMENT && elem->has("verticalAlignment")) { @@ -161,8 +178,8 @@ void DateTimeComponent::applyTheme(const std::shared_ptr& theme, setVerticalAlignment(ALIGN_BOTTOM); else LOG(LogWarning) << "DateTimeComponent: Invalid theme configuration, property " - " defined as \"" - << verticalAlignment << "\""; + "\"verticalAlignment\" for element \"" + << element.substr(9) << "\" defined as \"" << verticalAlignment << "\""; } if (properties & METADATA && elem->has("metadata")) { @@ -179,9 +196,9 @@ void DateTimeComponent::applyTheme(const std::shared_ptr& theme, mThemeMetadata = metadata; } else { - LOG(LogError) << "DateTimeComponent: Invalid theme configuration, property " - " defined as \"" - << metadata << "\""; + LOG(LogWarning) << "DateTimeComponent: Invalid theme configuration, property " + "\"metadata\" for element \"" + << element.substr(9) << "\" defined as \"" << metadata << "\""; } } @@ -204,8 +221,8 @@ void DateTimeComponent::applyTheme(const std::shared_ptr& theme, } else if (letterCase != "none") { LOG(LogWarning) << "DateTimeComponent: Invalid theme configuration, property " - " defined as \"" - << letterCase << "\""; + "\"letterCase\" for element \"" + << element.substr(9) << "\" defined as \"" << letterCase << "\""; } } diff --git a/es-core/src/components/GIFAnimComponent.cpp b/es-core/src/components/GIFAnimComponent.cpp index acbebe527..d0aa3ce9c 100644 --- a/es-core/src/components/GIFAnimComponent.cpp +++ b/es-core/src/components/GIFAnimComponent.cpp @@ -317,6 +317,22 @@ void GIFAnimComponent::applyTheme(const std::shared_ptr& theme, mTargetSize = mSize; } + if (properties & ThemeFlags::POSITION && elem->has("stationary")) { + const std::string& stationary {elem->get("stationary")}; + if (stationary == "never") + mStationary = Stationary::NEVER; + else if (stationary == "always") + mStationary = Stationary::ALWAYS; + else if (stationary == "withinView") + mStationary = Stationary::WITHIN_VIEW; + else if (stationary == "betweenViews") + mStationary = Stationary::BETWEEN_VIEWS; + else + LOG(LogWarning) << "GIFAnimComponent: Invalid theme configuration, property " + "\"stationary\" for element \"" + << element.substr(10) << "\" defined as \"" << stationary << "\""; + } + if (elem->has("metadataElement") && elem->get("metadataElement")) mComponentThemeFlags |= ComponentThemeFlags::METADATA_ELEMENT; diff --git a/es-core/src/components/ImageComponent.cpp b/es-core/src/components/ImageComponent.cpp index 97750074e..e2f66a660 100644 --- a/es-core/src/components/ImageComponent.cpp +++ b/es-core/src/components/ImageComponent.cpp @@ -503,6 +503,22 @@ void ImageComponent::applyTheme(const std::shared_ptr& theme, } } + if (properties & ThemeFlags::POSITION && elem->has("stationary")) { + const std::string& stationary {elem->get("stationary")}; + if (stationary == "never") + mStationary = Stationary::NEVER; + else if (stationary == "always") + mStationary = Stationary::ALWAYS; + else if (stationary == "withinView") + mStationary = Stationary::WITHIN_VIEW; + else if (stationary == "betweenViews") + mStationary = Stationary::BETWEEN_VIEWS; + else + LOG(LogWarning) << "ImageComponent: Invalid theme configuration, property " + "\"stationary\" for element \"" + << element.substr(6) << "\" defined as \"" << stationary << "\""; + } + if (elem->has("interpolation")) { const std::string& interpolation {elem->get("interpolation")}; if (interpolation == "linear") { diff --git a/es-core/src/components/LottieAnimComponent.cpp b/es-core/src/components/LottieAnimComponent.cpp index d6e78144c..23a3a7690 100644 --- a/es-core/src/components/LottieAnimComponent.cpp +++ b/es-core/src/components/LottieAnimComponent.cpp @@ -287,6 +287,22 @@ void LottieAnimComponent::applyTheme(const std::shared_ptr& theme, mTargetSize = mSize; } + if (properties & ThemeFlags::POSITION && elem->has("stationary")) { + const std::string& stationary {elem->get("stationary")}; + if (stationary == "never") + mStationary = Stationary::NEVER; + else if (stationary == "always") + mStationary = Stationary::ALWAYS; + else if (stationary == "withinView") + mStationary = Stationary::WITHIN_VIEW; + else if (stationary == "betweenViews") + mStationary = Stationary::BETWEEN_VIEWS; + else + LOG(LogWarning) << "LottieAnimComponent: Invalid theme configuration, property " + "\"stationary\" for element \"" + << element.substr(10) << "\" defined as \"" << stationary << "\""; + } + if (elem->has("metadataElement") && elem->get("metadataElement")) mComponentThemeFlags |= ComponentThemeFlags::METADATA_ELEMENT; diff --git a/es-core/src/components/RatingComponent.cpp b/es-core/src/components/RatingComponent.cpp index ad4482aec..ff6dd5012 100644 --- a/es-core/src/components/RatingComponent.cpp +++ b/es-core/src/components/RatingComponent.cpp @@ -211,6 +211,22 @@ void RatingComponent::applyTheme(const std::shared_ptr& theme, mSize.x = std::round(mSize.y * mImageRatio) * NUM_RATING_STARS; } + if (properties & ThemeFlags::POSITION && elem->has("stationary")) { + const std::string& stationary {elem->get("stationary")}; + if (stationary == "never") + mStationary = Stationary::NEVER; + else if (stationary == "always") + mStationary = Stationary::ALWAYS; + else if (stationary == "withinView") + mStationary = Stationary::WITHIN_VIEW; + else if (stationary == "betweenViews") + mStationary = Stationary::BETWEEN_VIEWS; + else + LOG(LogWarning) << "RatingComponent: Invalid theme configuration, property " + "\"stationary\" for element \"" + << element.substr(7) << "\" defined as \"" << stationary << "\""; + } + bool linearInterpolation {false}; if (elem->has("interpolation")) { diff --git a/es-core/src/components/TextComponent.cpp b/es-core/src/components/TextComponent.cpp index 05cf8e972..73b6e8c83 100644 --- a/es-core/src/components/TextComponent.cpp +++ b/es-core/src/components/TextComponent.cpp @@ -374,6 +374,23 @@ void TextComponent::applyTheme(const std::shared_ptr& theme, if (!elem) return; + if (properties & ThemeFlags::POSITION && elem->has("stationary")) { + const std::string& stationary {elem->get("stationary")}; + if (stationary == "never") + mStationary = Stationary::NEVER; + else if (stationary == "always") + mStationary = Stationary::ALWAYS; + else if (stationary == "withinView") + mStationary = Stationary::WITHIN_VIEW; + else if (stationary == "betweenViews") + mStationary = Stationary::BETWEEN_VIEWS; + else + LOG(LogWarning) << componentName + << ": Invalid theme configuration, property " + "\"stationary\" for element \"" + << element.substr(5) << "\" defined as \"" << stationary << "\""; + } + if (elem->has("metadataElement") && elem->get("metadataElement")) mComponentThemeFlags |= ComponentThemeFlags::METADATA_ELEMENT; diff --git a/es-core/src/components/VideoComponent.cpp b/es-core/src/components/VideoComponent.cpp index 660ba2789..f6b6a3e67 100644 --- a/es-core/src/components/VideoComponent.cpp +++ b/es-core/src/components/VideoComponent.cpp @@ -167,6 +167,22 @@ void VideoComponent::applyTheme(const std::shared_ptr& theme, mVideoAreaPos = elem->get("pos") * scale; } + if (properties & ThemeFlags::POSITION && elem->has("stationary")) { + const std::string& stationary {elem->get("stationary")}; + if (stationary == "never") + mStationary = Stationary::NEVER; + else if (stationary == "always") + mStationary = Stationary::ALWAYS; + else if (stationary == "withinView") + mStationary = Stationary::WITHIN_VIEW; + else if (stationary == "betweenViews") + mStationary = Stationary::BETWEEN_VIEWS; + else + LOG(LogWarning) << "VideoComponent: Invalid theme configuration, property " + "\"stationary\" for element \"" + << element.substr(6) << "\" defined as \"" << stationary << "\""; + } + if (elem->has("metadataElement") && elem->get("metadataElement")) mComponentThemeFlags |= ComponentThemeFlags::METADATA_ELEMENT;