From 966b513c9c54ff1153a328bd41ad423c27752aef Mon Sep 17 00:00:00 2001 From: Gemba Date: Wed, 1 Mar 2023 07:37:32 +0100 Subject: [PATCH 1/2] Squashed commit of the following: commit 47fc0c9edb95b08fccde01fd1778e3789f6e617a Merge: 7f3bc53 5a3b907 Author: Gemba Date: Wed Mar 1 07:36:05 2023 +0100 Merge branch 'master' of https://github.com/Gemba/EmulationStation into fix_short_desc_wordwrap commit 5a3b9074e14b744548acf8a8b0fe7adb63148ce6 Merge: 34b2545 0057745 Author: pjft Date: Mon Feb 27 20:01:05 2023 +0000 Merge pull request #801 from Gemba/fix_ui_transition_style_slide_gamelistview_rollover UI Slide Mode: Changes to allow rollover left/right at gamelist-view edges commit 00577453d2e1eed7234e4dacc04de20e1cfd775b Author: Gemba Date: Mon Feb 27 20:46:58 2023 +0100 Squashed commit of the following: commit 895e176c513ca1556c60c5c29c85bd43da3675e0 Author: Gemba Date: Fri Aug 5 13:37:44 2022 +0200 Changes to allow rollover at gamelist edges instead of rolling back in 'slide' mode. Needs 'Transition Style' set to 'slide'. Needs 'Quick Navigation' in Gamelist view enabled. commit 7f3bc5345577f6d0d6625bec6a40fa2468e0395d Merge: be99449 b88f5ac Author: Gemba Date: Sat Feb 18 16:11:23 2023 +0100 Merge branch 'fix_short_desc_wordwrap' of https://github.com/Gemba/EmulationStation into fix_short_desc_wordwrap commit b88f5acbc03955aa251cb87445bce1437a4abf06 Merge: 1eb0147 34b2545 Author: Gemba Date: Sat Feb 18 12:58:46 2023 +0100 Merge branch 'RetroPie:master' into fix_short_desc_wordwrap commit 1eb0147e24573e761f5a65ce7f46e2b055cb124c Merge: 07002d6 0c4b42d Author: Gemba Date: Sun Sep 25 14:31:36 2022 +0200 Merge branch 'RetroPie:master' into fix_short_desc_wordwrap commit 07002d61d5a1722588c2b34bee6783c01a7a9c18 Author: Gemba Date: Sat Aug 13 18:37:11 2022 +0200 Make short descriptions wrap to next line instead of adding ellipsis. The ellipsis effect instead of wrapping to a new line had two causes: 1. A short description ends slightly after the x-bounding value of the textcomponent (can happen in any theme). 2. Themes with a linespacing with less than 1.2 (120% of highest character) get a wrong flag set for detecting if it is a multi line text. Carbon uses 1.5 linespacing by default thus this case never to carbon. Fixes: 1. An additional clause in Font.cpp::wrapText() was added to fix this effect and to force wrapping instead of adding ellipsis. 2. The evaluation of the variable isMultiline in TextComponent::onTextChanged() takes the actual linespacing for calculation instead of the "magic float" of 1.2. cf. https://retropie.org.uk/forum/topic/32893/very-short-descriptions-don-t-wrap commit be9944921e47ebfa57acae8200d495f356774c2b Author: Gemba Date: Sat Aug 13 18:37:11 2022 +0200 Make short descriptions wrap to next line instead of adding ellipsis. This effect was agnostic to the theme in use. The ellipsis effect was most prevalent on short descriptions, but may also occour whenever the last line of a description shortly ends after the maximum width of the bounding box, overrunning the bounding box by upto two non whitespace characters. An additional clause in Font.cpp::wrapText() was added to fix this effect and to force wrapping instead of adding ellipsis. cf. https://retropie.org.uk/forum/topic/32893/very-short-descriptions-don-t-wrap --- es-app/src/animations/MoveCameraAnimation.h | 7 ++- es-app/src/views/ViewController.cpp | 47 +++++++++++++++++++-- es-core/src/components/TextComponent.cpp | 11 ++--- es-core/src/resources/Font.cpp | 6 ++- 4 files changed, 55 insertions(+), 16 deletions(-) diff --git a/es-app/src/animations/MoveCameraAnimation.h b/es-app/src/animations/MoveCameraAnimation.h index b5d05ec33e..23ad9640ba 100644 --- a/es-app/src/animations/MoveCameraAnimation.h +++ b/es-app/src/animations/MoveCameraAnimation.h @@ -7,22 +7,21 @@ class MoveCameraAnimation : public Animation { public: - MoveCameraAnimation(Transform4x4f& camera, const Vector3f& target) : mCameraStart(camera), mTarget(target), cameraOut(camera) {} + MoveCameraAnimation(Transform4x4f& camera, const Vector3f& target) : mCameraStart(camera), mTarget(target), mCameraOut(camera) { } int getDuration() const override { return 400; } void apply(float t) override { - // cubic ease out t -= 1; - cameraOut.translation() = -Vector3f().lerp(-mCameraStart.translation(), mTarget, t*t*t + 1); + mCameraOut.translation() = -Vector3f().lerp(-mCameraStart.translation(), mTarget, t*t*t + 1 /*cubic ease out*/); } private: Transform4x4f mCameraStart; Vector3f mTarget; - Transform4x4f& cameraOut; + Transform4x4f& mCameraOut; }; #endif // ES_APP_ANIMATIONS_MOVE_CAMERA_ANIMATION_H diff --git a/es-app/src/views/ViewController.cpp b/es-app/src/views/ViewController.cpp index 465490b1f7..f424f955bf 100644 --- a/es-app/src/views/ViewController.cpp +++ b/es-app/src/views/ViewController.cpp @@ -54,7 +54,7 @@ void ViewController::goToStart() if ((*it)->getName() == requestedSystem) { goToGameList(*it); - Scripting::fireEvent("system-select", requestedSystem, "requestedsystem"); + Scripting::fireEvent("system-select", requestedSystem, "requestedsystem"); FileData* cursor = getGameListView(*it)->getCursor(); if (cursor != NULL) { @@ -193,11 +193,50 @@ void ViewController::playViewTransition() }else{ advanceAnimation(0, (int)(mFadeOpacity * FADE_DURATION)); } - } else if (transition_style == "slide"){ + } + else if (transition_style == "slide") + { // slide or simple slide - setAnimation(new MoveCameraAnimation(mCamera, target)); + bool inGamelistNav = -mCamera.translation().y() == target.y() // not in/out gamelist nav + && -mCamera.translation().x() - target.x(); // left/right movement + cancelAnimation(0); + Vector3f tgt = Vector3f(target); + Vector3f positionOrig; + if (inGamelistNav) { + const float screenWidth = (float)Renderer::getScreenWidth(); + if (-mCamera.translation().x() - tgt.x() >= 2 * screenWidth) + { + // right rollover + mLockInput = true; + tgt.x() = screenWidth * mGameListViews.size(); + } + else if (-mCamera.translation().x() - tgt.x() <= 2 * -screenWidth) + { + // left rollover + mLockInput = true; + tgt.x() = -screenWidth; + } + // deny any further input on rollover as mCurrentView would be + // different on subsequent animations, resulting in restoring + // a unrelated mCurrentView/mCamera with the original position + if (mLockInput) + { + positionOrig = Vector3f(mCurrentView->getPosition()); + mCurrentView->setPosition(tgt.x(), tgt.y()); + } + } + + setAnimation(new MoveCameraAnimation(mCamera, tgt), 0, [this, positionOrig] { + if (mLockInput) { + mCurrentView->setPosition(positionOrig); + mCamera.translation() = -positionOrig; + } + mLockInput = false; + }); updateHelpPrompts(); // update help prompts immediately - } else { + } + else + { // instant setAnimation(new LambdaAnimation( [this, target](float /*t*/) diff --git a/es-core/src/components/TextComponent.cpp b/es-core/src/components/TextComponent.cpp index bd8b7cdc0d..08ccf5aba6 100644 --- a/es-core/src/components/TextComponent.cpp +++ b/es-core/src/components/TextComponent.cpp @@ -158,11 +158,9 @@ void TextComponent::calculateExtent() if(mAutoCalcExtent.x()) { mSize = mFont->sizeText(mUppercase ? Utils::String::toUpper(mText) : mText, mLineSpacing); - }else{ - if(mAutoCalcExtent.y()) - { - mSize[1] = mFont->sizeWrappedText(mUppercase ? Utils::String::toUpper(mText) : mText, getSize().x(), mLineSpacing).y(); - } + }else if(mAutoCalcExtent.y()) + { + mSize.y() = mFont->sizeWrappedText(mUppercase ? Utils::String::toUpper(mText) : mText, getSize().x(), mLineSpacing).y(); } } @@ -179,8 +177,7 @@ void TextComponent::onTextChanged() std::string text = mUppercase ? Utils::String::toUpper(mText) : mText; std::shared_ptr f = mFont; - const bool isMultiline = (mSize.y() == 0 || mSize.y() > f->getHeight()*1.2f); - + const bool isMultiline = (mSize.y() == 0 || mSize.y() > f->getHeight(mLineSpacing)); bool addAbbrev = false; if(!isMultiline) { diff --git a/es-core/src/resources/Font.cpp b/es-core/src/resources/Font.cpp index f3d074404a..d39c1f5521 100644 --- a/es-core/src/resources/Font.cpp +++ b/es-core/src/resources/Font.cpp @@ -526,7 +526,11 @@ std::string Font::wrapText(std::string text, float maxWidth) } } - if(cursor == text.length()) // arrived at end of text. + if(cursor == text.length() && lineWidth <= maxWidth) + // arrived at end of text while being in bounds of textbox + // second clause is mandatory for short descriptions which coincidentially + // ending with cursor at text end but slightly overrunning the bounding box + // to be wrapped on the next line, thus have to hit the else branch { out += text; text.erase(); From 955fe060868dde5a2c8f4338f4aa6195a52a2bfb Mon Sep 17 00:00:00 2001 From: Gemba Date: Wed, 22 Mar 2023 01:14:58 +0100 Subject: [PATCH 2/2] Squashed commit of the following: commit 5d07dac51f0ac77cb3771dc2a7c69677b882de00 Author: Gemba Date: Wed Mar 22 01:04:40 2023 +0100 re-enable wrapping when fixed >0 y-size value of TextComponent is given --- es-core/src/components/TextComponent.cpp | 16 ++++++++++++---- es-core/src/components/TextComponent.h | 2 +- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/es-core/src/components/TextComponent.cpp b/es-core/src/components/TextComponent.cpp index 698c0f920c..a04558cb07 100644 --- a/es-core/src/components/TextComponent.cpp +++ b/es-core/src/components/TextComponent.cpp @@ -153,16 +153,24 @@ void TextComponent::render(const Transform4x4f& parentTrans) } } -std::string TextComponent::calculateExtent() +std::string TextComponent::calculateExtent(bool allow_wrapping) { std::string text = mUppercase ? Utils::String::toUpper(mText) : mText; if(mAutoCalcExtent.x()) { mSize = mFont->sizeText(text, mLineSpacing); - }else if(mAutoCalcExtent.y()) + }else if(mAutoCalcExtent.y() || allow_wrapping) + // usually a textcomponent wraps only when x > 0 and y == 0 in size (see TextComponent.h). + // The extra flag allow_wrapping does wrapping if an textcomponent has x > 0 and y > height of + // one line (calculated by fontsize and line spacing). + // Some themes rely on this wrap functionality while having an fixed y (y>0) in . { text = mFont->wrapText(text, getSize().x()); - mSize.y() = mFont->sizeText(text, mLineSpacing).y(); + if (mAutoCalcExtent.y()) { + // only resize when y was 0 before + // otherwise leave y value as defined before (i.e. theme value) + mSize.y() = mFont->sizeText(text, mLineSpacing).y(); + } } return text; } @@ -176,7 +184,7 @@ void TextComponent::onTextChanged() } std::shared_ptr f = mFont; - std::string text = calculateExtent(); + std::string text = calculateExtent(mSize.y() > f->getHeight(mLineSpacing)); const bool oneLiner = mSize.y() > 0 && mSize.y() <= f->getHeight(mLineSpacing); if(oneLiner) diff --git a/es-core/src/components/TextComponent.h b/es-core/src/components/TextComponent.h index 30527ecfab..c464ef1140 100644 --- a/es-core/src/components/TextComponent.h +++ b/es-core/src/components/TextComponent.h @@ -49,7 +49,7 @@ class TextComponent : public GuiComponent std::shared_ptr mFont; private: - std::string calculateExtent(); + std::string calculateExtent(bool allow_wrapping); void onColorChanged();