From a1ed59553f12f691b2ce48aee551d9e9a7e883b9 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Thu, 14 Oct 2021 21:29:23 +0200 Subject: [PATCH 01/35] Made it possible to set a 'a/select' help prompt for TextComponent. --- es-core/src/components/TextComponent.cpp | 58 ++++++++++++++---------- es-core/src/components/TextComponent.h | 5 ++ 2 files changed, 39 insertions(+), 24 deletions(-) diff --git a/es-core/src/components/TextComponent.cpp b/es-core/src/components/TextComponent.cpp index ad4dda89f..fa15ce628 100644 --- a/es-core/src/components/TextComponent.cpp +++ b/es-core/src/components/TextComponent.cpp @@ -13,18 +13,19 @@ #include "utils/StringUtil.h" TextComponent::TextComponent(Window* window) - : GuiComponent(window) - , mFont(Font::get(FONT_SIZE_MEDIUM)) - , mColor(0x000000FF) - , mBgColor(0) - , mMargin(0.0f) - , mRenderBackground(false) - , mUppercase(false) - , mAutoCalcExtent(true, true) - , mHorizontalAlignment(ALIGN_LEFT) - , mVerticalAlignment(ALIGN_CENTER) - , mLineSpacing(1.5f) - , mNoTopMargin(false) + : GuiComponent{window} + , mFont{Font::get(FONT_SIZE_MEDIUM)} + , mColor{0x000000FF} + , mBgColor{0} + , mMargin{0.0f} + , mRenderBackground{false} + , mUppercase{false} + , mAutoCalcExtent{1, 1} + , mHorizontalAlignment{ALIGN_LEFT} + , mVerticalAlignment{ALIGN_CENTER} + , mLineSpacing{1.5f} + , mNoTopMargin{false} + , mSelectable{false} { } @@ -37,18 +38,19 @@ TextComponent::TextComponent(Window* window, glm::vec2 size, unsigned int bgcolor, float margin) - : GuiComponent(window) - , mFont(nullptr) - , mColor(0x000000FF) - , mBgColor(0) - , mMargin(margin) - , mRenderBackground(false) - , mUppercase(false) - , mAutoCalcExtent(true, true) - , mHorizontalAlignment(align) - , mVerticalAlignment(ALIGN_CENTER) - , mLineSpacing(1.5f) - , mNoTopMargin(false) + : GuiComponent{window} + , mFont{nullptr} + , mColor{0x000000FF} + , mBgColor{0} + , mMargin{margin} + , mRenderBackground{false} + , mUppercase{false} + , mAutoCalcExtent{1, 1} + , mHorizontalAlignment{align} + , mVerticalAlignment{ALIGN_CENTER} + , mLineSpacing{1.5f} + , mNoTopMargin{false} + , mSelectable{false} { setFont(font); setColor(color); @@ -282,6 +284,14 @@ void TextComponent::setNoTopMargin(bool margin) onTextChanged(); } +std::vector TextComponent::getHelpPrompts() +{ + std::vector prompts; + if (mSelectable) + prompts.push_back(HelpPrompt("a", "select")); + return prompts; +} + void TextComponent::applyTheme(const std::shared_ptr& theme, const std::string& view, const std::string& element, diff --git a/es-core/src/components/TextComponent.h b/es-core/src/components/TextComponent.h index 57a5561c5..cd07f5592 100644 --- a/es-core/src/components/TextComponent.h +++ b/es-core/src/components/TextComponent.h @@ -60,11 +60,15 @@ public: unsigned char getOpacity() const override { return mColor & 0x000000FF; } void setOpacity(unsigned char opacity) override; + void setSelectable(bool status) { mSelectable = status; } + virtual void applyTheme(const std::shared_ptr& theme, const std::string& view, const std::string& element, unsigned int properties) override; + virtual std::vector getHelpPrompts() override; + unsigned int getColor() const override { return mColor; } std::shared_ptr getFont() const override { return mFont; } Alignment getHorizontalAlignment() { return mHorizontalAlignment; } @@ -95,6 +99,7 @@ private: Alignment mVerticalAlignment; float mLineSpacing; bool mNoTopMargin; + bool mSelectable; }; #endif // ES_CORE_COMPONENTS_TEXT_COMPONENT_H From fe5e3ad5d422135beacb65263aff9b78818cc4d5 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Thu, 14 Oct 2021 21:47:32 +0200 Subject: [PATCH 02/35] The alternative emulators GUI now looks good at all resolutions. --- es-app/src/guis/GuiAlternativeEmulators.cpp | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/es-app/src/guis/GuiAlternativeEmulators.cpp b/es-app/src/guis/GuiAlternativeEmulators.cpp index 50420a17b..e19de6ef9 100644 --- a/es-app/src/guis/GuiAlternativeEmulators.cpp +++ b/es-app/src/guis/GuiAlternativeEmulators.cpp @@ -39,13 +39,6 @@ GuiAlternativeEmulators::GuiAlternativeEmulators(Window* window) ComponentListRow row; - // This transparent bracket is only added to generate a left margin. - auto bracket = std::make_shared(mWindow); - bracket->setImage(":/graphics/arrow.svg"); - bracket->setOpacity(0); - bracket->setSize(bracket->getSize() / 3.0f); - row.addElement(bracket, false); - std::string name = (*it)->getName(); std::shared_ptr systemText = std::make_shared(mWindow, name, Font::get(FONT_SIZE_MEDIUM), 0x777777FF); @@ -94,7 +87,9 @@ GuiAlternativeEmulators::GuiAlternativeEmulators(Window* window) labelText->setColor(TEXTCOLOR_SCRAPERMARKED); mCommandRows[name] = labelText; - labelText->setSize(labelSizeX, labelText->getSize().y); + labelText->setSize(mMenu.getSize().x - systemSizeX - + 20.0f * Renderer::getScreenHeightModifier(), + systemText->getSize().y); row.addElement(labelText, false); row.makeAcceptInputHandler([this, it, labelText] { @@ -157,6 +152,7 @@ void GuiAlternativeEmulators::selectorWindow(SystemData* system) std::shared_ptr labelText = std::make_shared( mWindow, label, Font::get(FONT_SIZE_MEDIUM), 0x777777FF, ALIGN_LEFT); + labelText->setSelectable(true); if (system->getSystemEnvData()->mLaunchCommands.front().second == label) labelText->setValue(labelText->getValue().append(" [DEFAULT]")); @@ -193,13 +189,6 @@ void GuiAlternativeEmulators::selectorWindow(SystemData* system) delete s; }); - // This transparent bracket is only added to generate the correct help prompts. - auto bracket = std::make_shared(mWindow); - bracket->setImage(":/graphics/arrow.svg"); - bracket->setOpacity(0); - bracket->setSize(bracket->getSize() / 3.0f); - row.addElement(bracket, false); - // Select the row that corresponds to the selected label. if (selectedLabel == label) s->addRow(row, true); From 10e284c92972bbc8148e098eb8172798ffae4ebc Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Thu, 14 Oct 2021 21:59:09 +0200 Subject: [PATCH 03/35] Text in ScrollableContainer now mostly stays within the initial area. --- es-core/src/components/ScrollableContainer.cpp | 11 +++++++++++ es-core/src/components/ScrollableContainer.h | 1 + 2 files changed, 12 insertions(+) diff --git a/es-core/src/components/ScrollableContainer.cpp b/es-core/src/components/ScrollableContainer.cpp index 218b7cd3a..51f33f917 100644 --- a/es-core/src/components/ScrollableContainer.cpp +++ b/es-core/src/components/ScrollableContainer.cpp @@ -18,6 +18,7 @@ ScrollableContainer::ScrollableContainer(Window* window) : GuiComponent{window} , mScrollPos{0.0f, 0.0f} , mScrollDir{0.0f, 0.0f} + , mClipSpacing{0.0f} , mAutoScrollDelay{0} , mAutoScrollSpeed{0} , mAutoScrollAccumulator{0} @@ -81,6 +82,11 @@ void ScrollableContainer::update(int deltaTime) float lineSpacing{mChildren.front()->getLineSpacing()}; float combinedHeight{mChildren.front()->getFont()->getHeight(lineSpacing)}; + // Calculate the line spacing which will be used to clip the container. + if (mClipSpacing == 0.0f) + mClipSpacing = + std::round((combinedHeight - mChildren.front()->getFont()->getLetterHeight()) / 2.0f); + // Resize container to font height boundary to avoid rendering a fraction of the last line. if (!mUpdatedSize && contentSize.y > mSize.y) { float numLines{mSize.y / combinedHeight}; @@ -173,6 +179,11 @@ void ScrollableContainer::render(const glm::mat4& parentTrans) glm::ivec2 clipDim{static_cast(dimScaled.x - trans[3].x), static_cast(dimScaled.y - trans[3].y)}; + // By effectively clipping the upper and lower boundaries of the container we mostly avoid + // scrolling outside the vertical starting and ending positions. + clipPos.y += mClipSpacing; + clipDim.y -= mClipSpacing * 0.9f; + Renderer::pushClipRect(clipPos, clipDim); trans = glm::translate(trans, -glm::vec3{mScrollPos.x, mScrollPos.y, 0.0f}); diff --git a/es-core/src/components/ScrollableContainer.h b/es-core/src/components/ScrollableContainer.h index 27b63f459..fb80b7eea 100644 --- a/es-core/src/components/ScrollableContainer.h +++ b/es-core/src/components/ScrollableContainer.h @@ -44,6 +44,7 @@ private: float mAutoScrollDelayConstant; float mAutoScrollSpeedConstant; float mResolutionModifier; + float mClipSpacing; int mAutoScrollDelay; int mAutoScrollSpeed; From eed27d1ee30f5282caeb4cf72c31c3bfef00fb27 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Thu, 14 Oct 2021 22:13:13 +0200 Subject: [PATCH 04/35] Changed the font size for the custom collection deletion screen. Also fixed an issue with incorrect row heights at lower resolutions and removed a help prompt hack. --- .../src/guis/GuiCollectionSystemsOptions.cpp | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/es-app/src/guis/GuiCollectionSystemsOptions.cpp b/es-app/src/guis/GuiCollectionSystemsOptions.cpp index 2d1e31059..fd18b1cb5 100644 --- a/es-app/src/guis/GuiCollectionSystemsOptions.cpp +++ b/es-app/src/guis/GuiCollectionSystemsOptions.cpp @@ -177,12 +177,8 @@ GuiCollectionSystemsOptions::GuiCollectionSystemsOptions(Window* window, std::st row.makeAcceptInputHandler(createCollectionCall); auto themeFolder = std::make_shared( mWindow, Utils::String::toUpper(name), Font::get(FONT_SIZE_SMALL), 0x777777FF); + themeFolder->setSelectable(true); row.addElement(themeFolder, true); - // This transparent bracket is only added to generate the correct help prompts. - auto bracket = std::make_shared(mWindow); - bracket->setImage(":/graphics/arrow.svg"); - bracket->setOpacity(0); - row.addElement(bracket, false); ss->addRow(row); } mWindow->pushGui(ss); @@ -287,15 +283,17 @@ GuiCollectionSystemsOptions::GuiCollectionSystemsOptions(Window* window, std::st }; row.makeAcceptInputHandler(deleteCollectionCall); auto customCollection = std::make_shared( - mWindow, Utils::String::toUpper(name), Font::get(FONT_SIZE_SMALL), 0x777777FF); + mWindow, Utils::String::toUpper(name), Font::get(FONT_SIZE_MEDIUM), 0x777777FF); + customCollection->setSelectable(true); row.addElement(customCollection, true); - // This transparent bracket is only added generate the correct help prompts. - auto bracket = std::make_shared(mWindow); - bracket->setImage(":/graphics/arrow.svg"); - bracket->setOpacity(0); - row.addElement(bracket, false); ss->addRow(row); } + // Make the menu slightly wider to fit the scroll indicators. + glm::vec2 menuSize{ss->getMenuSize()}; + glm::vec3 menuPos{ss->getMenuPosition()}; + ss->setMenuSize(glm::vec2{menuSize.x * 1.08f, menuSize.y}); + menuPos.x = static_cast((Renderer::getScreenWidth()) - ss->getMenuSize().x) / 2.0f; + ss->setMenuPosition(menuPos); mWindow->pushGui(ss); }); addRow(row); From b4045f05aee9a572f2373d3c6e750a6c0dbe1d8c Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Thu, 14 Oct 2021 22:21:21 +0200 Subject: [PATCH 05/35] Removed some help prompt hacks in GuiMenu. --- es-app/src/guis/GuiMenu.cpp | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/es-app/src/guis/GuiMenu.cpp b/es-app/src/guis/GuiMenu.cpp index 74f707d68..f339f06ee 100644 --- a/es-app/src/guis/GuiMenu.cpp +++ b/es-app/src/guis/GuiMenu.cpp @@ -1207,11 +1207,6 @@ void GuiMenu::openQuitMenu() Window* window = mWindow; HelpStyle style = getHelpStyle(); - // This transparent bracket is only neeeded to generate the correct help prompts. - auto bracket = std::make_shared(mWindow); - bracket->setImage(":/graphics/arrow.svg"); - bracket->setOpacity(0); - ComponentListRow row; row.makeAcceptInputHandler([window, this] { @@ -1224,10 +1219,10 @@ void GuiMenu::openQuitMenu() }, "NO", nullptr)); }); - row.addElement(std::make_shared(window, "QUIT EMULATIONSTATION", - Font::get(FONT_SIZE_MEDIUM), 0x777777FF), - true); - row.addElement(bracket, false); + auto quitText = std::make_shared(window, "QUIT EMULATIONSTATION", + Font::get(FONT_SIZE_MEDIUM), 0x777777FF); + quitText->setSelectable(true); + row.addElement(quitText, true); s->addRow(row); row.elements.clear(); @@ -1243,10 +1238,10 @@ void GuiMenu::openQuitMenu() }, "NO", nullptr)); }); - row.addElement(std::make_shared(window, "REBOOT SYSTEM", - Font::get(FONT_SIZE_MEDIUM), 0x777777FF), - true); - row.addElement(bracket, false); + auto rebootText = std::make_shared(window, "REBOOT SYSTEM", + Font::get(FONT_SIZE_MEDIUM), 0x777777FF); + rebootText->setSelectable(true); + row.addElement(rebootText, true); s->addRow(row); row.elements.clear(); @@ -1262,10 +1257,10 @@ void GuiMenu::openQuitMenu() }, "NO", nullptr)); }); - row.addElement(std::make_shared(window, "POWER OFF SYSTEM", - Font::get(FONT_SIZE_MEDIUM), 0x777777FF), - true); - row.addElement(bracket, false); + auto powerOffText = std::make_shared( + window, "POWER OFF SYSTEM", Font::get(FONT_SIZE_MEDIUM), 0x777777FF); + powerOffText->setSelectable(true); + row.addElement(powerOffText, true); s->addRow(row); mWindow->pushGui(s); From 6ccee6e4c4ee87379c0217304756f6540d497f10 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Thu, 14 Oct 2021 22:31:50 +0200 Subject: [PATCH 06/35] Removed an unnecessary column from GuiMetaDataEd. Also moved the scroll indicators slightly and removed a help prompt hack. --- es-app/src/guis/GuiMetaDataEd.cpp | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/es-app/src/guis/GuiMetaDataEd.cpp b/es-app/src/guis/GuiMetaDataEd.cpp index cf12889a7..594b55fe6 100644 --- a/es-app/src/guis/GuiMetaDataEd.cpp +++ b/es-app/src/guis/GuiMetaDataEd.cpp @@ -43,7 +43,7 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window, std::function deleteGameFunc) : GuiComponent{window} , mBackground{window, ":/graphics/frame.svg"} - , mGrid{window, glm::ivec2{3, 6}} + , mGrid{window, glm::ivec2{2, 6}} , mScraperParams{scraperParams} , mMetaDataDecl{mdd} , mMetaData{md} @@ -58,7 +58,7 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window, mTitle = std::make_shared(mWindow, "EDIT METADATA", Font::get(FONT_SIZE_LARGE), 0x555555FF, ALIGN_CENTER); - mGrid.setEntry(mTitle, glm::ivec2{0, 0}, false, true, glm::ivec2{3, 2}); + mGrid.setEntry(mTitle, glm::ivec2{0, 0}, false, true, glm::ivec2{2, 2}); // Extract possible subfolders from the path. std::string folderPath = @@ -83,10 +83,10 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window, Font::get(FONT_SIZE_SMALL), 0x777777FF, ALIGN_CENTER, glm::vec3{}, glm::vec2{}, 0x00000000, 0.05f); - mGrid.setEntry(mSubtitle, glm::ivec2{0, 2}, false, true, glm::ivec2{3, 1}); + mGrid.setEntry(mSubtitle, glm::ivec2{0, 2}, false, true, glm::ivec2{2, 1}); mList = std::make_shared(mWindow); - mGrid.setEntry(mList, glm::ivec2{0, 4}, true, true, glm::ivec2{3, 1}); + mGrid.setEntry(mList, glm::ivec2{0, 4}, true, true, glm::ivec2{2, 1}); // Set up scroll indicators. mScrollUp = std::make_shared(mWindow); @@ -99,8 +99,8 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window, mScrollDown->setResize(0.0f, mTitle->getFont()->getLetterHeight() / 2.0f); mScrollDown->setOrigin(0.0f, 0.35f); - mGrid.setEntry(mScrollUp, glm::ivec2{2, 0}, false, false, glm::ivec2{1, 1}); - mGrid.setEntry(mScrollDown, glm::ivec2{2, 1}, false, false, glm::ivec2{1, 1}); + mGrid.setEntry(mScrollUp, glm::ivec2{1, 0}, false, false, glm::ivec2{1, 1}); + mGrid.setEntry(mScrollDown, glm::ivec2{1, 1}, false, false, glm::ivec2{1, 1}); // Populate list. for (auto iter = mdd.cbegin(); iter != mdd.cend(); iter++) { @@ -265,6 +265,7 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window, std::shared_ptr labelText = std::make_shared( mWindow, label, Font::get(FONT_SIZE_MEDIUM), 0x777777FF); + labelText->setSelectable(true); if (scraperParams.system->getAlternativeEmulator() == "" && scraperParams.system->getSystemEnvData() @@ -289,14 +290,6 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window, delete s; }); - // This transparent bracket is only added to generate the correct help - // prompts. - auto bracket = std::make_shared(mWindow); - bracket->setImage(":/graphics/arrow.svg"); - bracket->setOpacity(0); - bracket->setSize(bracket->getSize() / 3.0f); - row.addElement(bracket, false); - // Select the row that corresponds to the selected label. if (selectedLabel == label) s->addRow(row, true); @@ -481,7 +474,7 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window, } mButtons = makeButtonGrid(mWindow, buttons); - mGrid.setEntry(mButtons, glm::ivec2{0, 5}, true, false, glm::ivec2{3, 1}); + mGrid.setEntry(mButtons, glm::ivec2{0, 5}, true, false, glm::ivec2{2, 1}); // Resize + center. float width = @@ -505,8 +498,7 @@ void GuiMetaDataEd::onSizeChanged() mGrid.setRowHeightPerc(3, (titleSubtitleSpacing * 1.2f) / mSize.y); mGrid.setRowHeightPerc(4, ((mList->getRowHeight(0) * 10.0f) + 2.0f) / mSize.y); - mGrid.setColWidthPerc(0, 0.07f); - mGrid.setColWidthPerc(2, 0.07f); + mGrid.setColWidthPerc(1, 0.055f); mGrid.setSize(mSize); mBackground.fitTo(mSize, glm::vec3{}, glm::vec2{-32.0f, -32.0f}); From 7311a49f82258ad2175a1c114c01e02e3dfd8b50 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Thu, 14 Oct 2021 22:38:30 +0200 Subject: [PATCH 07/35] Removed an unnecessary column from MenuComponent and adjusted the scroll indicators. --- es-core/src/components/MenuComponent.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/es-core/src/components/MenuComponent.cpp b/es-core/src/components/MenuComponent.cpp index 4f8a2dbf9..e7c3c0964 100644 --- a/es-core/src/components/MenuComponent.cpp +++ b/es-core/src/components/MenuComponent.cpp @@ -21,7 +21,7 @@ MenuComponent::MenuComponent(Window* window, const std::shared_ptr& titleFont) : GuiComponent(window) , mBackground(window) - , mGrid(window, glm::ivec2{3, 4}) + , mGrid(window, glm::ivec2{2, 4}) , mNeedsSaving(false) { addChild(&mBackground); @@ -34,11 +34,11 @@ MenuComponent::MenuComponent(Window* window, mTitle->setHorizontalAlignment(ALIGN_CENTER); mTitle->setColor(0x555555FF); setTitle(title, titleFont); - mGrid.setEntry(mTitle, glm::ivec2{0, 0}, false, true, glm::ivec2{3, 2}); + mGrid.setEntry(mTitle, glm::ivec2{0, 0}, false, true, glm::ivec2{2, 2}); // Set up list which will never change (externally, anyway). mList = std::make_shared(mWindow); - mGrid.setEntry(mList, glm::ivec2{0, 2}, true, true, glm::ivec2{3, 1}); + mGrid.setEntry(mList, glm::ivec2{0, 2}, true, true, glm::ivec2{2, 1}); // Set up scroll indicators. mScrollUp = std::make_shared(mWindow); @@ -51,8 +51,8 @@ MenuComponent::MenuComponent(Window* window, mScrollDown->setResize(0.0f, mTitle->getFont()->getLetterHeight() / 2.0f); mScrollDown->setOrigin(0.0f, 0.35f); - mGrid.setEntry(mScrollUp, glm::ivec2{2, 0}, false, false, glm::ivec2{1, 1}); - mGrid.setEntry(mScrollDown, glm::ivec2{2, 1}, false, false, glm::ivec2{1, 1}); + mGrid.setEntry(mScrollUp, glm::ivec2{1, 0}, false, false, glm::ivec2{1, 1}); + mGrid.setEntry(mScrollDown, glm::ivec2{1, 1}, false, false, glm::ivec2{1, 1}); updateGrid(); updateSize(); @@ -127,8 +127,7 @@ void MenuComponent::onSizeChanged() mGrid.setRowHeightPerc(1, TITLE_HEIGHT / mSize.y / 2.0f); mGrid.setRowHeightPerc(3, getButtonGridHeight() / mSize.y); - mGrid.setColWidthPerc(0, 0.07f); - mGrid.setColWidthPerc(2, 0.07f); + mGrid.setColWidthPerc(1, 0.055f); mGrid.setSize(mSize); } @@ -152,7 +151,7 @@ void MenuComponent::updateGrid() if (mButtons.size()) { mButtonGrid = makeButtonGrid(mWindow, mButtons); - mGrid.setEntry(mButtonGrid, glm::ivec2{0, 3}, true, false, glm::ivec2{3, 1}); + mGrid.setEntry(mButtonGrid, glm::ivec2{0, 3}, true, false, glm::ivec2{2, 1}); } } From 52b516b9ca5ff946f17868a0508555f90b9c6d4f Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Thu, 14 Oct 2021 22:48:54 +0200 Subject: [PATCH 08/35] Updated the default badges graphics. --- resources/graphics/badge_altemulator.svg | 335 +++++++++++------------ resources/graphics/badge_broken.svg | 146 ++-------- resources/graphics/badge_completed.svg | 144 ++-------- resources/graphics/badge_favorite.svg | 140 ++-------- resources/graphics/badge_kidgame.svg | 146 ++-------- themes/rbsimple-DE/theme.xml | 4 +- 6 files changed, 232 insertions(+), 683 deletions(-) diff --git a/resources/graphics/badge_altemulator.svg b/resources/graphics/badge_altemulator.svg index 0a07c9872..055f15766 100644 --- a/resources/graphics/badge_altemulator.svg +++ b/resources/graphics/badge_altemulator.svg @@ -8,8 +8,8 @@ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="80" - height="112" - viewBox="0 0 21.166666 29.633334" + height="92" + viewBox="0 0 21.166666 24.341667" version="1.1" id="svg4842" inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)" @@ -23,9 +23,9 @@ borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" - inkscape:zoom="12.442154" - inkscape:cx="-18.41824" - inkscape:cy="59.681612" + inkscape:zoom="19.3509" + inkscape:cx="5.374237" + inkscape:cy="26.957777" inkscape:document-units="mm" inkscape:current-layer="layer2" showgrid="false" @@ -59,155 +59,12 @@ inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1" - transform="translate(-1.9829021e-4,-266.11715)"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + transform="translate(-1.9829021e-4,-271.40882)"> + style="fill:#ffffff;stroke-width:0.25911999"> + + id="g16" + transform="matrix(0.10079384,0,0,0.08235571,-2.2547839e-4,271.45845)" + style="fill:#d7d7d7;fill-opacity:1"> + + + + + + + + + @@ -380,7 +281,7 @@ + style="opacity:1;vector-effect:none;fill:#ffd700;fill-opacity:1;stroke:#282828;stroke-width:0.21396799;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:stroke fill markers" /> - - + aria-label="ALTERNATIVE" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.60125422px;line-height:1.25;font-family:FontAwesome;-inkscape-font-specification:FontAwesome;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="text4942" + transform="matrix(1.1491297,0,0,1.5857309,-1.3267257,-15.489625)"> + d="m 3.9524717,26.040316 q 0,-0.01016 0.012066,-0.07938 0.033659,-0.208938 0.076844,-0.432484 0.043185,-0.22418 0.1117727,-0.555688 0.068588,-0.332142 0.089545,-0.435659 0.1270144,-0.0057 0.3492895,-0.0057 0.2070334,0 0.3619909,0.0057 0.010161,0 0.019052,0.0076 0.00889,0.007 0.010796,0.01778 0.027943,0.141621 0.094626,0.462967 0.067318,0.321347 0.1143129,0.56839 0.04763,0.246408 0.081289,0.467413 -0.1200285,0.0057 -0.221005,0.0057 -0.1619433,0 -0.2489481,-0.0057 -0.010796,-6.35e-4 -0.019687,-0.0076 -0.00826,-0.0076 -0.010161,-0.01778 -0.028578,-0.146067 -0.04636,-0.254029 H 4.4922827 q -0.020322,0.135905 -0.0489,0.279432 -0.1219338,0.0057 -0.2102088,0.0057 -0.1054219,0 -0.2597444,-0.0057 -0.00953,0 -0.015242,-0.0057 -0.00572,-0.0064 -0.00572,-0.01524 z m 0.5823608,-0.588711 h 0.1428912 q -0.015242,-0.104152 -0.04128,-0.296579 -0.026038,-0.193062 -0.037469,-0.273081 -0.012701,0.103517 -0.033659,0.297214 -0.020957,0.193062 -0.030483,0.272446 z" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:digitalt;-inkscape-font-specification:digitalt;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path4944" + inkscape:connector-curvature="0" /> + d="m 5.3655065,25.299187 q 0,-0.35564 0.010161,-0.762086 0.1206636,-0.0057 0.2222751,-0.0057 0.1047869,0 0.2603795,0.0057 0.010161,0 0.017782,0.0076 0.00762,0.0076 0.00762,0.01778 0.010161,0.477574 0.010161,0.736683 0,0.168929 -0.00191,0.329603 h 0.010796 q 0.2318012,0 0.3873938,0.0057 0.010161,0 0.017782,0.0076 0.00762,0.0076 0.00762,0.01778 0.00254,0.154958 0.00254,0.219735 0,0.09971 -0.00254,0.186711 -0.1206637,0.0057 -0.3746924,0.0057 -0.062872,0 -0.2680003,-0.0044 -0.2044932,-0.0038 -0.2718108,-0.0063 -0.010161,0 -0.017782,-0.0076 -0.00762,-0.0076 -0.00762,-0.01778 -0.010161,-0.477574 -0.010161,-0.736684 z" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:digitalt;-inkscape-font-specification:digitalt;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path4946" + inkscape:connector-curvature="0" /> + d="m 6.2012611,24.730163 q 0,-0.102247 0.00254,-0.193062 0.1212988,-0.0057 0.5880766,-0.0057 0.3689767,0 0.5245693,0.0057 0.010161,0 0.017782,0.0076 0.00762,0.0076 0.00762,0.01778 0.00254,0.143526 0.00254,0.215924 0,0.103517 -0.00254,0.190522 -0.04128,0.0019 -0.3073748,0.0044 0.00254,0.137175 0.00254,0.325791 0,0.355641 -0.010161,0.762087 -0.1206636,0.0057 -0.2222751,0.0057 -0.1047869,0 -0.2603795,-0.0057 -0.010161,0 -0.017782,-0.0076 -0.00762,-0.0076 -0.00762,-0.01778 -0.010161,-0.477574 -0.010161,-0.736684 0,-0.117488 0.00254,-0.328332 -0.1460665,-0.0032 -0.2819719,-0.007 -0.010161,0 -0.017782,-0.0076 -0.00762,-0.0076 -0.00762,-0.01778 -0.00254,-0.146067 -0.00254,-0.208304 z" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:digitalt;-inkscape-font-specification:digitalt;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path4948" + inkscape:connector-curvature="0" /> + d="m 7.4485421,25.299187 q 0,-0.35564 0.010161,-0.762086 0.1206637,-0.0057 0.5271097,-0.0057 0.2876875,0 0.4432801,0.0057 0.010161,0 0.017782,0.0076 0.00762,0.0076 0.00762,0.01778 0.00254,0.154958 0.00254,0.219735 0,0.09971 -0.00254,0.186711 -0.1073272,0.0051 -0.4039057,0.0051 -0.053346,0 -0.096531,-6.35e-4 -0.00127,0.08764 -0.00191,0.133365 0.2273557,0.0013 0.3041994,0.0044 0.010161,0 0.017782,0.0076 0.00762,0.0076 0.00762,0.01778 0.00191,0.104152 0.00191,0.191157 0,0.07811 -0.00191,0.169564 H 7.9521541 q 6.35e-4,0.04445 0.00191,0.132095 h 0.030483 q 0.3111852,0 0.4496309,0.0051 0.010161,0 0.017782,0.0076 0.00762,0.0076 0.00762,0.01778 0.00254,0.154958 0.00254,0.219735 0,0.09971 -0.00254,0.186711 -0.1206637,0.0057 -0.4356593,0.0057 -0.062872,0 -0.2680003,-0.0044 -0.2044932,-0.0038 -0.2718108,-0.0063 -0.010161,0 -0.017782,-0.0076 -0.00762,-0.0076 -0.00762,-0.01778 -0.010161,-0.477574 -0.010161,-0.736684 z" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:digitalt;-inkscape-font-specification:digitalt;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path4950" + inkscape:connector-curvature="0" /> + d="m 8.6069129,25.266164 q 0,-0.127015 0.00254,-0.35183 0.00254,-0.224816 0.00254,-0.35183 0.1270144,-0.01969 0.2699055,-0.03112 0.1435263,-0.01207 0.2508534,-0.01207 0.092721,0 0.1555926,0.0032 0.062872,0.0025 0.1352703,0.0127 0.073033,0.0095 0.1200286,0.02794 0.046995,0.01842 0.093991,0.05081 0.046995,0.03239 0.073033,0.07875 0.026673,0.04636 0.043185,0.113678 0.016512,0.06732 0.016512,0.154322 0,0.137811 -0.071128,0.242598 -0.071128,0.104786 -0.1981424,0.164483 0.08637,0.02985 0.1403509,0.121934 0.1263793,0.248313 0.2019528,0.571565 -0.1168532,0.01397 -0.2660951,0.01397 -0.1079622,0 -0.2038581,-0.0064 -0.012066,-6.35e-4 -0.023498,-0.0076 -0.011431,-0.007 -0.015242,-0.01778 -0.123839,-0.375963 -0.1924268,-0.607129 h -0.033659 q 0.00127,0.07113 0.00572,0.291498 0.00508,0.22037 0.00572,0.333413 -0.1041518,0.0044 -0.2229102,0.0044 -0.1073272,0 -0.2597444,-0.0044 -0.010796,0 -0.018417,-0.007 -0.00699,-0.0076 -0.00699,-0.01842 0,-0.117489 -0.00254,-0.379138 -0.00254,-0.26165 -0.00254,-0.390569 z m 0.5004366,-0.142256 h 0.043185 q 0.060332,0 0.097166,-0.02985 0.037469,-0.03048 0.037469,-0.08002 0,-0.06224 -0.025403,-0.09209 -0.025403,-0.03048 -0.09018,-0.03048 -0.033024,0 -0.060967,0.0019 -0.00127,0.148607 -0.00127,0.230532 z" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:digitalt;-inkscape-font-specification:digitalt;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path4952" + inkscape:connector-curvature="0" /> + d="m 9.9380235,25.299187 q 0,-0.35564 0.010161,-0.762086 0.1206635,-0.0057 0.2095735,-0.0057 0.07557,0 0.219735,0.0057 0.01842,6.35e-4 0.02794,0.0254 l 0.26292,0.650314 -0.0089,-0.675717 q 0.121299,-0.0057 0.222275,-0.0057 0.09907,0 0.234977,0.0057 0.0108,6.35e-4 0.01778,0.0083 0.0076,0.007 0.0076,0.01715 0.01016,0.477574 0.01016,0.736683 0,0.355641 -0.01016,0.762087 -0.105422,0.0057 -0.196873,0.0057 -0.108597,0 -0.232436,-0.0057 -0.01905,-6.35e-4 -0.02794,-0.0254 l -0.26292,-0.650314 0.0089,0.675717 q -0.120663,0.0057 -0.209573,0.0057 -0.100342,0 -0.2476786,-0.0057 -0.010796,-6.35e-4 -0.018417,-0.0076 -0.00699,-0.0076 -0.00699,-0.01778 -0.010161,-0.477574 -0.010161,-0.736684 z" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:digitalt;-inkscape-font-specification:digitalt;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path4954" + inkscape:connector-curvature="0" /> + + + + + + + + + + + + + + + diff --git a/resources/graphics/badge_broken.svg b/resources/graphics/badge_broken.svg index a1d7dbc3c..31eea29f1 100644 --- a/resources/graphics/badge_broken.svg +++ b/resources/graphics/badge_broken.svg @@ -10,8 +10,8 @@ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="80" - height="112" - viewBox="0 0 21.166666 29.633334" + height="92" + viewBox="0 0 21.166666 24.341667" version="1.1" id="svg4842" inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)" @@ -25,9 +25,9 @@ borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" - inkscape:zoom="9.5782066" - inkscape:cx="1.1766084" - inkscape:cy="50.443327" + inkscape:zoom="7.4483451" + inkscape:cx="-60.233009" + inkscape:cy="71.400581" inkscape:document-units="mm" inkscape:current-layer="layer1" showgrid="false" @@ -49,7 +49,7 @@ image/svg+xml - + @@ -57,17 +57,17 @@ inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1" - transform="translate(-1.9829021e-4,-266.11715)"> + transform="translate(-1.9829021e-4,-271.40882)"> + height="24.341667" + x="0.0001986081" + y="271.40881" /> + transform="matrix(0.10079384,0,0,0.0816046,-2.2531965e-4,271.54688)"> - - - - - - - - - - - - - - - - - - - - - - - + y="276.70892" + x="1.8134596" /> diff --git a/resources/graphics/badge_completed.svg b/resources/graphics/badge_completed.svg index a09e55f4c..cbab8a44a 100644 --- a/resources/graphics/badge_completed.svg +++ b/resources/graphics/badge_completed.svg @@ -7,10 +7,10 @@ xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="80.000008" - height="112" + width="80" + height="92" version="1.1" - viewBox="0 0 21.166668 29.633334" + viewBox="0 0 21.166666 24.341667" id="svg92" sodipodi:docname="badge_completed.svg" inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"> @@ -33,9 +33,9 @@ fit-margin-left="0" fit-margin-right="0" fit-margin-bottom="0" - inkscape:zoom="12.442155" - inkscape:cx="-34.776172" - inkscape:cy="67.39851" + inkscape:zoom="5.4859697" + inkscape:cx="-52.049045" + inkscape:cy="47.629762" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" @@ -49,20 +49,20 @@ image/svg+xml - + + height="24.341667" + x="2.2368853e-07" + y="-2.6645353e-15" /> + transform="matrix(0.10079384,0,0,0.0816046,-4.2317622e-4,0.13809161)"> - - - - - - - - - - - - - - - - - - - - - - - + y="5.3001137" + x="1.8323119" /> + transform="matrix(0.09207517,0,0,0.11015111,-4.2465753e-4,-1.9389228e-4)"> diff --git a/resources/graphics/badge_favorite.svg b/resources/graphics/badge_favorite.svg index 4b8236cae..1b723f5d7 100644 --- a/resources/graphics/badge_favorite.svg +++ b/resources/graphics/badge_favorite.svg @@ -8,9 +8,9 @@ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="80" - height="112" + height="92" version="1.1" - viewBox="0 0 21.166666 29.633332" + viewBox="0 0 21.166666 24.341666" id="svg90" sodipodi:docname="badge_favorite.svg" inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"> @@ -30,9 +30,9 @@ id="namedview92" showgrid="false" units="px" - inkscape:zoom="5.4859695" - inkscape:cx="-51.316674" - inkscape:cy="71.543477" + inkscape:zoom="2.8284271" + inkscape:cx="-170.96844" + inkscape:cy="39.989024" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" @@ -46,20 +46,20 @@ image/svg+xml - + + y="-2.6253017e-14" /> + transform="matrix(0.10079384,0,0,0.0816046,-4.2392733e-4,0.13809204)"> + d="m 17.240062,12.456484 -4.709693,-0.491108 -1.927985,-4.2699571 -1.9279844,4.2699571 -4.7096929,0.491108 3.5178058,3.130612 -0.9820949,4.573603 4.1020064,-2.335917 4.102007,2.335917 -0.982095,-4.573603 z" /> + transform="matrix(0.10111638,0,0,0.09920503,-4.2392733e-4,-1.9532637e-4)"> - - - - - - - - - - - - - - - - - - - - - - - diff --git a/resources/graphics/badge_kidgame.svg b/resources/graphics/badge_kidgame.svg index 565806a74..464bf61d9 100644 --- a/resources/graphics/badge_kidgame.svg +++ b/resources/graphics/badge_kidgame.svg @@ -7,10 +7,10 @@ xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="80.000008" - height="112" + width="80" + height="92" version="1.1" - viewBox="0 0 21.166668 29.633334" + viewBox="0 0 21.166666 24.341667" id="svg90" sodipodi:docname="badge_kidgame.svg" inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"> @@ -33,9 +33,9 @@ fit-margin-left="0" fit-margin-right="0" fit-margin-bottom="0" - inkscape:zoom="8.2057888" - inkscape:cx="-57.797153" - inkscape:cy="68.015272" + inkscape:zoom="5.1167374" + inkscape:cx="-67.47745" + inkscape:cy="38.966505" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" @@ -49,20 +49,20 @@ image/svg+xml - + + height="24.341667" + x="2.2368853e-07" + y="-2.6645353e-15" /> + transform="matrix(0.10079384,0,0,0.0816046,-4.237873e-4,0.13809261)"> - - - - - - - - - - - - - - - - - - - - - - - + y="5.3001156" + x="1.8132613" /> + d="M 4.2719194,9.2899832 7.5642988,20.858601 16.894338,18.490114 16.025104,15.486921 10.530737,16.910518 10.155183,15.577444 15.650846,14.180192 14.791009,11.17484 9.310487,12.587467 8.9381592,11.320213 14.438408,9.9434334 13.590192,6.9470637 Z" /> + transform="matrix(0.09733762,0,0,0.10419427,-4.2465753e-4,-1.9389228e-4)"> right - 0.8125 0.675 - 0.15 0.21 + 0.815 0.675 + 0.13 0.1635 0 0 left 3 From 050dccb6b85646864f4ebdbcd8600f3fab3ba655 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Fri, 15 Oct 2021 20:31:51 +0200 Subject: [PATCH 09/35] Fixed an issue with international characters getting clipped in ScrollableContainer. --- es-core/src/components/ScrollableContainer.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/es-core/src/components/ScrollableContainer.cpp b/es-core/src/components/ScrollableContainer.cpp index 51f33f917..5568d0f63 100644 --- a/es-core/src/components/ScrollableContainer.cpp +++ b/es-core/src/components/ScrollableContainer.cpp @@ -82,10 +82,12 @@ void ScrollableContainer::update(int deltaTime) float lineSpacing{mChildren.front()->getLineSpacing()}; float combinedHeight{mChildren.front()->getFont()->getHeight(lineSpacing)}; - // Calculate the line spacing which will be used to clip the container. - if (mClipSpacing == 0.0f) - mClipSpacing = - std::round((combinedHeight - mChildren.front()->getFont()->getLetterHeight()) / 2.0f); + // Calculate the spacing which will be used to clip the container. + if (lineSpacing > 1.2f && mClipSpacing == 0.0f) { + const float minimumSpacing = mChildren.front()->getFont()->getHeight(1.2f); + const float currentSpacing = mChildren.front()->getFont()->getHeight(lineSpacing); + mClipSpacing = std::round((currentSpacing - minimumSpacing) / 2.0f); + } // Resize container to font height boundary to avoid rendering a fraction of the last line. if (!mUpdatedSize && contentSize.y > mSize.y) { @@ -176,13 +178,13 @@ void ScrollableContainer::render(const glm::mat4& parentTrans) dimScaled.x = std::fabs(trans[3].x + mSize.x); dimScaled.y = std::fabs(trans[3].y + mSize.y); - glm::ivec2 clipDim{static_cast(dimScaled.x - trans[3].x), - static_cast(dimScaled.y - trans[3].y)}; + glm::ivec2 clipDim{static_cast(ceilf(dimScaled.x - trans[3].x)), + static_cast(ceilf(dimScaled.y - trans[3].y))}; // By effectively clipping the upper and lower boundaries of the container we mostly avoid // scrolling outside the vertical starting and ending positions. - clipPos.y += mClipSpacing; - clipDim.y -= mClipSpacing * 0.9f; + clipPos.y += static_cast(mClipSpacing); + clipDim.y -= static_cast(mClipSpacing); Renderer::pushClipRect(clipPos, clipDim); From 36838660626d4019f41fd907ecef93d6908364a2 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Fri, 15 Oct 2021 20:58:40 +0200 Subject: [PATCH 10/35] Improved the layout of the scraper GUIs. Also added the scroll indicators and replaced a text margin hack with a proper solution. --- es-app/src/guis/GuiGameScraper.cpp | 85 ++++++++++++++++++------ es-app/src/guis/GuiGameScraper.h | 5 ++ es-app/src/guis/GuiMetaDataEd.cpp | 8 ++- es-app/src/guis/GuiScraperMulti.cpp | 81 +++++++++++++++++----- es-app/src/guis/GuiScraperMulti.h | 5 ++ es-app/src/guis/GuiScraperSearch.cpp | 51 +++++++------- es-app/src/guis/GuiScraperSearch.h | 7 ++ es-core/src/components/ComponentList.h | 7 ++ es-core/src/components/TextComponent.cpp | 11 ++- es-core/src/components/TextComponent.h | 4 +- 10 files changed, 191 insertions(+), 73 deletions(-) diff --git a/es-app/src/guis/GuiGameScraper.cpp b/es-app/src/guis/GuiGameScraper.cpp index 9ddc85caa..bcda1e93f 100644 --- a/es-app/src/guis/GuiGameScraper.cpp +++ b/es-app/src/guis/GuiGameScraper.cpp @@ -23,15 +23,13 @@ GuiGameScraper::GuiGameScraper(Window* window, std::function doneFunc) : GuiComponent(window) , mClose(false) - , mGrid(window, glm::ivec2{1, 7}) + , mGrid(window, glm::ivec2{2, 6}) , mBox(window, ":/graphics/frame.svg") , mSearchParams(params) { addChild(&mBox); addChild(&mGrid); - // Row 0 is a spacer. - std::string scrapeName; if (Settings::getInstance()->getBool("ScraperSearchMetadataName")) { @@ -51,21 +49,37 @@ GuiGameScraper::GuiGameScraper(Window* window, mWindow, scrapeName + ((mSearchParams.game->getType() == FOLDER) ? " " + ViewController::FOLDER_CHAR : ""), - Font::get(FONT_SIZE_MEDIUM), 0x777777FF, ALIGN_CENTER); - mGrid.setEntry(mGameName, glm::ivec2{0, 1}, false, true); - - // Row 2 is a spacer. + Font::get(FONT_SIZE_LARGE), 0x777777FF, ALIGN_CENTER); + mGameName->setColor(0x555555FF); + mGrid.setEntry(mGameName, glm::ivec2{0, 0}, false, true, glm::ivec2{2, 2}); mSystemName = std::make_shared( mWindow, Utils::String::toUpper(mSearchParams.system->getFullName()), Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_CENTER); - mGrid.setEntry(mSystemName, glm::ivec2{0, 3}, false, true); + mGrid.setEntry(mSystemName, glm::ivec2{0, 2}, false, true, glm::ivec2{2, 1}); - // Row 4 is a spacer. + // Row 3 is a spacer. // GuiScraperSearch. mSearch = std::make_shared(window, GuiScraperSearch::NEVER_AUTO_ACCEPT, 1); - mGrid.setEntry(mSearch, glm::ivec2{0, 5}, true); + mGrid.setEntry(mSearch, glm::ivec2{0, 4}, true, true, glm::ivec2{2, 1}); + + mResultList = mSearch->getResultList(); + + // Set up scroll indicators. + mScrollUp = std::make_shared(mWindow); + mScrollDown = std::make_shared(mWindow); + mScrollIndicator = + std::make_shared(mResultList, mScrollUp, mScrollDown); + + mScrollUp->setResize(0.0f, mGameName->getFont()->getLetterHeight() / 2.0f); + mScrollUp->setOrigin(0.0f, -0.35f); + + mScrollDown->setResize(0.0f, mGameName->getFont()->getLetterHeight() / 2.0f); + mScrollDown->setOrigin(0.0f, 0.35f); + + mGrid.setEntry(mScrollUp, glm::ivec2{1, 0}, false, false, glm::ivec2{1, 1}); + mGrid.setEntry(mScrollDown, glm::ivec2{1, 1}, false, false, glm::ivec2{1, 1}); // Buttons std::vector> buttons; @@ -74,6 +88,9 @@ GuiGameScraper::GuiGameScraper(Window* window, std::make_shared(mWindow, "REFINE SEARCH", "refine search", [&] { // Refine the search, unless the result has already been accepted. if (!mSearch->getAcceptedResult()) { + // Copy any search refine that may have been previously entered by opening + // the input screen using the "Y" button shortcut. + mSearchParams.nameOverride = mSearch->getNameOverride(); mSearch->openInputScreen(mSearchParams); mGrid.resetCursor(); } @@ -92,20 +109,34 @@ GuiGameScraper::GuiGameScraper(Window* window, })); mButtonGrid = makeButtonGrid(mWindow, buttons); - mGrid.setEntry(mButtonGrid, glm::ivec2{0, 6}, true, false); + mGrid.setEntry(mButtonGrid, glm::ivec2{0, 5}, true, false, glm::ivec2{2, 1}); mSearch->setAcceptCallback([this, doneFunc](const ScraperSearchResult& result) { doneFunc(result); close(); }); mSearch->setCancelCallback([&] { delete this; }); + mSearch->setRefineCallback([&] { + mScrollUp->setOpacity(0); + mScrollDown->setOpacity(0); + mResultList->resetScrollIndicatorStatus(); + }); // Limit the width of the GUI on ultrawide monitors. The 1.778 aspect ratio value is // the 16:9 reference. float aspectValue = 1.778f / Renderer::getScreenAspectRatio(); float width = glm::clamp(0.95f * aspectValue, 0.70f, 0.95f) * Renderer::getScreenWidth(); - setSize(width, Renderer::getScreenHeight() * 0.747f); + float height = (mGameName->getFont()->getLetterHeight() + + static_cast(Renderer::getScreenHeight()) * 0.0637f) + + mSystemName->getFont()->getLetterHeight() + + static_cast(Renderer::getScreenHeight()) * 0.04f + + mButtonGrid->getSize().y + Font::get(FONT_SIZE_MEDIUM)->getHeight() * 8.0f; + + // TODO: Temporary hack, see below. + height -= 7.0f * Renderer::getScreenHeightModifier(); + + setSize(width, height); setPosition((Renderer::getScreenWidth() - mSize.x) / 2.0f, (Renderer::getScreenHeight() - mSize.y) / 2.0f); @@ -115,15 +146,31 @@ GuiGameScraper::GuiGameScraper(Window* window, void GuiGameScraper::onSizeChanged() { + mGrid.setRowHeightPerc( + 0, (mGameName->getFont()->getLetterHeight() + Renderer::getScreenHeight() * 0.0637f) / + mSize.y / 2.0f); + mGrid.setRowHeightPerc( + 1, (mGameName->getFont()->getLetterHeight() + Renderer::getScreenHeight() * 0.0637f) / + mSize.y / 2.0f); + mGrid.setRowHeightPerc(2, mSystemName->getFont()->getLetterHeight() / mSize.y, false); + mGrid.setRowHeightPerc(3, 0.04f, false); + mGrid.setRowHeightPerc(4, ((Font::get(FONT_SIZE_MEDIUM)->getHeight() * 8.0f)) / mSize.y, false); + + // TODO: Replace this temporary hack with a proper solution. There is some kind of rounding + // issue somewhere that causes a small alignment error. This code partly compensates for this + // at higher resolutions than 1920x1080. + if (Renderer::getScreenHeightModifier() > 1.0f) + mSize.y -= 3.0f * Renderer::getScreenHeightModifier(); + + mGrid.setColWidthPerc(1, 0.04f); + + mGrid.setSize(mSize); mBox.fitTo(mSize, glm::vec3{}, glm::vec2{-32.0f, -32.0f}); - mGrid.setRowHeightPerc(0, 0.04f, false); - mGrid.setRowHeightPerc(1, mGameName->getFont()->getLetterHeight() / mSize.y, false); - mGrid.setRowHeightPerc(2, 0.04f, false); - mGrid.setRowHeightPerc(3, mSystemName->getFont()->getLetterHeight() / mSize.y, false); - mGrid.setRowHeightPerc(4, 0.04f, false); - mGrid.setRowHeightPerc(6, mButtonGrid->getSize().y / mSize.y, false); - mGrid.setSize(mSize); + // Add some extra margins to the game name. + const float newSizeX = mSize.x * 0.96f; + mGameName->setSize(newSizeX, mGameName->getSize().y); + mGameName->setPosition((mSize.x - newSizeX) / 2.0f, 0.0f); } bool GuiGameScraper::input(InputConfig* config, Input input) diff --git a/es-app/src/guis/GuiGameScraper.h b/es-app/src/guis/GuiGameScraper.h index b910c1351..040f1e072 100644 --- a/es-app/src/guis/GuiGameScraper.h +++ b/es-app/src/guis/GuiGameScraper.h @@ -13,6 +13,7 @@ #include "GuiComponent.h" #include "components/NinePatchComponent.h" +#include "components/ScrollIndicatorComponent.h" #include "guis/GuiScraperSearch.h" class GuiGameScraper : public GuiComponent @@ -38,9 +39,13 @@ private: NinePatchComponent mBox; std::shared_ptr mGameName; + std::shared_ptr mScrollUp; + std::shared_ptr mScrollDown; + std::shared_ptr mScrollIndicator; std::shared_ptr mSystemName; std::shared_ptr mSearch; std::shared_ptr mButtonGrid; + std::shared_ptr mResultList; ScraperSearchParams mSearchParams; diff --git a/es-app/src/guis/GuiMetaDataEd.cpp b/es-app/src/guis/GuiMetaDataEd.cpp index 594b55fe6..5a945c640 100644 --- a/es-app/src/guis/GuiMetaDataEd.cpp +++ b/es-app/src/guis/GuiMetaDataEd.cpp @@ -80,8 +80,7 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window, folderPath + Utils::FileSystem::getFileName(scraperParams.game->getPath()) + " [" + Utils::String::toUpper(scraperParams.system->getName()) + "]" + (scraperParams.game->getType() == FOLDER ? " " + ViewController::FOLDER_CHAR : ""), - Font::get(FONT_SIZE_SMALL), 0x777777FF, ALIGN_CENTER, glm::vec3{}, glm::vec2{}, 0x00000000, - 0.05f); + Font::get(FONT_SIZE_SMALL), 0x777777FF, ALIGN_CENTER); mGrid.setEntry(mSubtitle, glm::ivec2{0, 2}, false, true, glm::ivec2{2, 1}); @@ -505,6 +504,11 @@ void GuiMetaDataEd::onSizeChanged() setPosition((Renderer::getScreenWidth() - mSize.x) / 2.0f, (Renderer::getScreenHeight() - mSize.y) / 2.0f); + + // Add some extra margins to the file/folder name. + const float newSizeX = mSize.x * 0.96f; + mSubtitle->setSize(newSizeX, mSubtitle->getSize().y); + mSubtitle->setPosition((mSize.x - newSizeX) / 2.0f, mSubtitle->getPosition().y); } void GuiMetaDataEd::save() diff --git a/es-app/src/guis/GuiScraperMulti.cpp b/es-app/src/guis/GuiScraperMulti.cpp index 067352199..50b8aab67 100644 --- a/es-app/src/guis/GuiScraperMulti.cpp +++ b/es-app/src/guis/GuiScraperMulti.cpp @@ -28,7 +28,7 @@ GuiScraperMulti::GuiScraperMulti(Window* window, bool approveResults) : GuiComponent(window) , mBackground(window, ":/graphics/frame.svg") - , mGrid(window, glm::ivec2{1, 5}) + , mGrid(window, glm::ivec2{2, 6}) , mSearchQueue(searches) , mApproveResults(approveResults) { @@ -47,15 +47,15 @@ GuiScraperMulti::GuiScraperMulti(Window* window, // Set up grid. mTitle = std::make_shared(mWindow, "SCRAPING IN PROGRESS", Font::get(FONT_SIZE_LARGE), 0x555555FF, ALIGN_CENTER); - mGrid.setEntry(mTitle, glm::ivec2{0, 0}, false, true); + mGrid.setEntry(mTitle, glm::ivec2{0, 0}, false, true, glm::ivec2{2, 2}); mSystem = std::make_shared(mWindow, "SYSTEM", Font::get(FONT_SIZE_MEDIUM), 0x777777FF, ALIGN_CENTER); - mGrid.setEntry(mSystem, glm::ivec2{0, 1}, false, true); + mGrid.setEntry(mSystem, glm::ivec2{0, 2}, false, true, glm::ivec2{2, 1}); mSubtitle = std::make_shared( mWindow, "subtitle text", Font::get(FONT_SIZE_SMALL), 0x888888FF, ALIGN_CENTER); - mGrid.setEntry(mSubtitle, glm::ivec2{0, 2}, false, true); + mGrid.setEntry(mSubtitle, glm::ivec2{0, 3}, false, true, glm::ivec2{2, 1}); if (mApproveResults && !Settings::getInstance()->getBool("ScraperSemiautomatic")) mSearchComp = std::make_shared( @@ -70,10 +70,34 @@ GuiScraperMulti::GuiScraperMulti(Window* window, std::bind(&GuiScraperMulti::acceptResult, this, std::placeholders::_1)); mSearchComp->setSkipCallback(std::bind(&GuiScraperMulti::skip, this)); mSearchComp->setCancelCallback(std::bind(&GuiScraperMulti::finish, this)); - mGrid.setEntry(mSearchComp, glm::ivec2{0, 3}, - mSearchComp->getSearchType() != GuiScraperSearch::ALWAYS_ACCEPT_FIRST_RESULT, - true); + mSearchComp->setRefineCallback([&] { + mScrollUp->setOpacity(0); + mScrollDown->setOpacity(0); + mResultList->resetScrollIndicatorStatus(); + }); + mGrid.setEntry(mSearchComp, glm::ivec2{0, 4}, + mSearchComp->getSearchType() != GuiScraperSearch::ALWAYS_ACCEPT_FIRST_RESULT, + true, glm::ivec2{2, 1}); + + mResultList = mSearchComp->getResultList(); + + // Set up scroll indicators. + mScrollUp = std::make_shared(mWindow); + mScrollDown = std::make_shared(mWindow); + mScrollIndicator = + std::make_shared(mResultList, mScrollUp, mScrollDown); + + mScrollUp->setResize(0.0f, mTitle->getFont()->getLetterHeight() / 2.0f); + mScrollUp->setOrigin(0.0f, -0.35f); + + mScrollDown->setResize(0.0f, mTitle->getFont()->getLetterHeight() / 2.0f); + mScrollDown->setOrigin(0.0f, 0.35f); + + mGrid.setEntry(mScrollUp, glm::ivec2{1, 0}, false, false, glm::ivec2{1, 1}); + mGrid.setEntry(mScrollDown, glm::ivec2{1, 1}, false, false, glm::ivec2{1, 1}); + + // Buttons. std::vector> buttons; if (mApproveResults) { @@ -125,14 +149,23 @@ GuiScraperMulti::GuiScraperMulti(Window* window, std::bind(&GuiScraperMulti::finish, this))); mButtonGrid = makeButtonGrid(mWindow, buttons); - mGrid.setEntry(mButtonGrid, glm::ivec2{0, 4}, true, false); + mGrid.setEntry(mButtonGrid, glm::ivec2{0, 5}, true, false, glm::ivec2{2, 1}); // Limit the width of the GUI on ultrawide monitors. The 1.778 aspect ratio value is // the 16:9 reference. float aspectValue = 1.778f / Renderer::getScreenAspectRatio(); float width = glm::clamp(0.95f * aspectValue, 0.70f, 0.95f) * Renderer::getScreenWidth(); - setSize(width, Renderer::getScreenHeight() * 0.849f); + float height = (mTitle->getFont()->getLetterHeight() + + static_cast(Renderer::getScreenHeight()) * 0.0637f) + + mSystem->getFont()->getLetterHeight() + + mSubtitle->getFont()->getHeight() * 1.75f + mButtonGrid->getSize().y + + Font::get(FONT_SIZE_MEDIUM)->getHeight() * 7.0f; + + // TODO: Temporary hack, see below. + height -= 7.0f * Renderer::getScreenHeightModifier(); + + setSize(width, height); setPosition((Renderer::getScreenWidth() - mSize.x) / 2.0f, (Renderer::getScreenHeight() - mSize.y) / 2.0f); @@ -153,13 +186,26 @@ GuiScraperMulti::~GuiScraperMulti() void GuiScraperMulti::onSizeChanged() { - mBackground.fitTo(mSize, glm::vec3{}, glm::vec2{-32.0f, -32.0f}); + mGrid.setRowHeightPerc( + 0, (mTitle->getFont()->getLetterHeight() + Renderer::getScreenHeight() * 0.0637f) / + mSize.y / 2.0f); + mGrid.setRowHeightPerc( + 1, (mTitle->getFont()->getLetterHeight() + Renderer::getScreenHeight() * 0.0637f) / + mSize.y / 2.0f); + mGrid.setRowHeightPerc(2, (mSystem->getFont()->getLetterHeight()) / mSize.y, false); + mGrid.setRowHeightPerc(3, mSubtitle->getFont()->getHeight() * 1.75f / mSize.y, false); + mGrid.setRowHeightPerc(4, ((Font::get(FONT_SIZE_MEDIUM)->getHeight() * 7.0f)) / mSize.y, false); + + // TODO: Replace this temporary hack with a proper solution. There is some kind of rounding + // issue somewhere that causes a small alignment error. This code partly compensates for this + // at higher resolutions than 1920x1080. + if (Renderer::getScreenHeightModifier() > 1.0f) + mSize.y -= 3.0f * Renderer::getScreenHeightModifier(); + + mGrid.setColWidthPerc(1, 0.04f); - mGrid.setRowHeightPerc(0, mTitle->getFont()->getLetterHeight() * 1.9725f / mSize.y, false); - mGrid.setRowHeightPerc(1, (mSystem->getFont()->getLetterHeight() + 2.0f) / mSize.y, false); - mGrid.setRowHeightPerc(2, mSubtitle->getFont()->getHeight() * 1.75f / mSize.y, false); - mGrid.setRowHeightPerc(4, mButtonGrid->getSize().y / mSize.y, false); mGrid.setSize(mSize); + mBackground.fitTo(mSize, glm::vec3{}, glm::vec2{-32.0f, -32.0f}); } void GuiScraperMulti::doNextSearch() @@ -189,6 +235,10 @@ void GuiScraperMulti::doNextSearch() scrapeName = Utils::FileSystem::getFileName(mSearchQueue.front().game->getPath()); } + mScrollUp->setOpacity(0); + mScrollDown->setOpacity(0); + mResultList->resetScrollIndicatorStatus(); + // Extract possible subfolders from the path. std::string folderPath = Utils::String::replace(Utils::FileSystem::getParent(mSearchQueue.front().game->getPath()), @@ -264,9 +314,6 @@ void GuiScraperMulti::finish() std::vector GuiScraperMulti::getHelpPrompts() { std::vector prompts = mGrid.getHelpPrompts(); - // Remove the 'Choose' entry if in fully automatic mode. - if (!mApproveResults) - prompts.pop_back(); return prompts; } diff --git a/es-app/src/guis/GuiScraperMulti.h b/es-app/src/guis/GuiScraperMulti.h index 63c755f6b..d71525086 100644 --- a/es-app/src/guis/GuiScraperMulti.h +++ b/es-app/src/guis/GuiScraperMulti.h @@ -16,6 +16,7 @@ #include "MetaData.h" #include "components/ComponentGrid.h" #include "components/NinePatchComponent.h" +#include "components/ScrollIndicatorComponent.h" #include "scrapers/Scraper.h" class GuiScraperSearch; @@ -45,10 +46,14 @@ private: ComponentGrid mGrid; std::shared_ptr mTitle; + std::shared_ptr mScrollUp; + std::shared_ptr mScrollDown; + std::shared_ptr mScrollIndicator; std::shared_ptr mSystem; std::shared_ptr mSubtitle; std::shared_ptr mSearchComp; std::shared_ptr mButtonGrid; + std::shared_ptr mResultList; std::queue mSearchQueue; std::vector mMetaDataDecl; diff --git a/es-app/src/guis/GuiScraperSearch.cpp b/es-app/src/guis/GuiScraperSearch.cpp index 340eb81e2..6a8709c35 100644 --- a/es-app/src/guis/GuiScraperSearch.cpp +++ b/es-app/src/guis/GuiScraperSearch.cpp @@ -39,7 +39,7 @@ GuiScraperSearch::GuiScraperSearch(Window* window, SearchType type, unsigned int scrapeCount) : GuiComponent(window) - , mGrid(window, glm::ivec2{4, 3}) + , mGrid(window, glm::ivec2{5, 3}) , mSearchType(type) , mScrapeCount(scrapeCount) , mRefinedSearch(false) @@ -88,14 +88,11 @@ GuiScraperSearch::GuiScraperSearch(Window* window, SearchType type, unsigned int mMD_ReleaseDate = std::make_shared(mWindow); mMD_ReleaseDate->setColor(mdColor); mMD_ReleaseDate->setUppercase(true); - mMD_Developer = std::make_shared(mWindow, "", font, mdColor, ALIGN_LEFT, - glm::vec3{}, glm::vec2{}, 0x00000000, 0.02f); - mMD_Publisher = std::make_shared(mWindow, "", font, mdColor, ALIGN_LEFT, - glm::vec3{}, glm::vec2{}, 0x00000000, 0.02f); - mMD_Genre = std::make_shared(mWindow, "", font, mdColor, ALIGN_LEFT, glm::vec3{}, - glm::vec2{}, 0x00000000, 0.02f); - mMD_Players = std::make_shared(mWindow, "", font, mdColor, ALIGN_LEFT, - glm::vec3{}, glm::vec2{}, 0x00000000, 0.02f); + mMD_Developer = std::make_shared(mWindow, "", font, mdColor, ALIGN_LEFT); + mMD_Publisher = std::make_shared(mWindow, "", font, mdColor, ALIGN_LEFT); + mMD_Genre = + std::make_shared(mWindow, "", font, mdColor, ALIGN_LEFT, glm::vec3{}); + mMD_Players = std::make_shared(mWindow, "", font, mdColor, ALIGN_LEFT); mMD_Filler = std::make_shared(mWindow, "", font, mdColor); if (Settings::getInstance()->getString("Scraper") != "thegamesdb") @@ -193,45 +190,47 @@ void GuiScraperSearch::onSizeChanged() mGrid.setColWidthPerc(1, 0.25f); if (mSearchType == ALWAYS_ACCEPT_FIRST_RESULT) - mGrid.setColWidthPerc(2, 0.25f); + mGrid.setColWidthPerc(2, 0.33f); else - mGrid.setColWidthPerc(2, 0.28f); + mGrid.setColWidthPerc(2, 0.30f); // Row heights. if (mSearchType == ALWAYS_ACCEPT_FIRST_RESULT) // Show name. mGrid.setRowHeightPerc(0, (mResultName->getFont()->getHeight() * 1.6f) / mGrid.getSize().y); // Result name. else - mGrid.setRowHeightPerc(0, 0.0825f); // Hide name but do padding. + mGrid.setRowHeightPerc(0, 0.0725f); // Hide name but do padding. if (mSearchType == ALWAYS_ACCEPT_FIRST_RESULT) mGrid.setRowHeightPerc(2, 0.2f); else mGrid.setRowHeightPerc(1, 0.505f); - const float boxartCellScale = 0.9f; + const float thumbnailCellScale = 0.93f; // Limit thumbnail size using setMaxHeight - we do this instead of letting mGrid // call setSize because it maintains the aspect ratio. // We also pad a little so it doesn't rub up against the metadata labels. - mResultThumbnail->setMaxSize(mGrid.getColWidth(1) * boxartCellScale, mGrid.getRowHeight(1)); + mResultThumbnail->setMaxSize(mGrid.getColWidth(1) * thumbnailCellScale, mGrid.getRowHeight(1)); // Metadata. resizeMetadata(); + // Small vertical spacer between the metadata fields and the result list. + mGrid.setColWidthPerc(3, 0.004f); + if (mSearchType != ALWAYS_ACCEPT_FIRST_RESULT) - mDescContainer->setSize(mGrid.getColWidth(1) * boxartCellScale + mGrid.getColWidth(2), + mDescContainer->setSize(mGrid.getColWidth(1) * thumbnailCellScale + mGrid.getColWidth(2), mResultDesc->getFont()->getHeight() * 3.0f); else - mDescContainer->setSize(mGrid.getColWidth(3) * boxartCellScale, + mDescContainer->setSize(mGrid.getColWidth(4) * thumbnailCellScale, mResultDesc->getFont()->getHeight() * 6.0f); // Make description text wrap at edge of container. mResultDesc->setSize(mDescContainer->getSize().x, 0.0f); // Set the width of mResultName to the cell width so that text abbreviation will work correctly. - glm::vec2 resultNameSize{mResultName->getSize()}; - mResultName->setSize(mGrid.getColWidth(3), resultNameSize.y); + mResultName->setSize(mGrid.getColWidth(1) + mGrid.getColWidth(2), mResultName->getSize().y); mGrid.onSizeChanged(); mBusyAnim.setSize(mSize); @@ -289,30 +288,30 @@ void GuiScraperSearch::updateViewStyle() // Add them back depending on search type. if (mSearchType == ALWAYS_ACCEPT_FIRST_RESULT) { // Show name. - mGrid.setEntry(mResultName, glm::ivec2{1, 0}, false, false, glm::ivec2{2, 1}, + mGrid.setEntry(mResultName, glm::ivec2{1, 0}, false, false, glm::ivec2{3, 1}, GridFlags::BORDER_TOP); // Need a border on the bottom left. mGrid.setEntry(std::make_shared(mWindow), glm::ivec2{0, 2}, false, false, - glm::ivec2{3, 1}, GridFlags::BORDER_BOTTOM); + glm::ivec2{4, 1}, GridFlags::BORDER_BOTTOM); // Show description on the right. - mGrid.setEntry(mDescContainer, glm::ivec2{3, 0}, false, false, glm::ivec2{1, 3}, - GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM); + mGrid.setEntry(mDescContainer, glm::ivec2{4, 0}, false, false, glm::ivec2{1, 3}, + GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM | GridFlags::BORDER_LEFT); // Make description text wrap at edge of container. mResultDesc->setSize(mDescContainer->getSize().x, 0.0f); } else { // Fake row where name would be. mGrid.setEntry(std::make_shared(mWindow), glm::ivec2{1, 0}, false, true, - glm::ivec2{2, 1}, GridFlags::BORDER_TOP); + glm::ivec2{3, 1}, GridFlags::BORDER_TOP); // Show result list on the right. - mGrid.setEntry(mResultList, glm::ivec2{3, 0}, true, true, glm::ivec2{1, 3}, + mGrid.setEntry(mResultList, glm::ivec2{4, 0}, true, true, glm::ivec2{1, 3}, GridFlags::BORDER_LEFT | GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM); // Show description under image/info. - mGrid.setEntry(mDescContainer, glm::ivec2{1, 2}, false, false, glm::ivec2{2, 1}, + mGrid.setEntry(mDescContainer, glm::ivec2{1, 2}, false, false, glm::ivec2{3, 1}, GridFlags::BORDER_BOTTOM); // Make description text wrap at edge of container. mResultDesc->setSize(mDescContainer->getSize().x, 0); @@ -798,6 +797,8 @@ void GuiScraperSearch::openInputScreen(ScraperSearchParams& params) stop(); mRefinedSearch = true; params.nameOverride = name; + if (mRefineCallback != nullptr) + mRefineCallback(); search(params); }; diff --git a/es-app/src/guis/GuiScraperSearch.h b/es-app/src/guis/GuiScraperSearch.h index 449124ba0..082e8e3b0 100644 --- a/es-app/src/guis/GuiScraperSearch.h +++ b/es-app/src/guis/GuiScraperSearch.h @@ -71,6 +71,10 @@ public: { mCancelCallback = cancelCallback; } + void setRefineCallback(const std::function& refineCallback) + { + mRefineCallback = refineCallback; + } bool input(InputConfig* config, Input input) override; void update(int deltaTime) override; @@ -92,6 +96,8 @@ public: void onFocusGained() override { mGrid.onFocusGained(); } void onFocusLost() override { mGrid.onFocusLost(); } + std::shared_ptr& getResultList() { return mResultList; } + private: void updateViewStyle(); void updateThumbnail(); @@ -152,6 +158,7 @@ private: std::function mAcceptCallback; std::function mSkipCallback; std::function mCancelCallback; + std::function mRefineCallback; unsigned int mScrapeCount; bool mRefinedSearch; bool mBlockAccept; diff --git a/es-core/src/components/ComponentList.h b/es-core/src/components/ComponentList.h index 9035cd881..fd7739fc5 100644 --- a/es-core/src/components/ComponentList.h +++ b/es-core/src/components/ComponentList.h @@ -86,6 +86,13 @@ public: float getTotalRowHeight() const; float getRowHeight(int row) const { return getRowHeight(mEntries.at(row).data); } + void resetScrollIndicatorStatus() + { + mScrollIndicatorStatus = SCROLL_NONE; + if (mScrollIndicatorChangedCallback != nullptr) + mScrollIndicatorChangedCallback(mScrollIndicatorStatus); + } + void setCursorChangedCallback(const std::function& callback) { mCursorChangedCallback = callback; diff --git a/es-core/src/components/TextComponent.cpp b/es-core/src/components/TextComponent.cpp index fa15ce628..08aa17674 100644 --- a/es-core/src/components/TextComponent.cpp +++ b/es-core/src/components/TextComponent.cpp @@ -17,7 +17,6 @@ TextComponent::TextComponent(Window* window) , mFont{Font::get(FONT_SIZE_MEDIUM)} , mColor{0x000000FF} , mBgColor{0} - , mMargin{0.0f} , mRenderBackground{false} , mUppercase{false} , mAutoCalcExtent{1, 1} @@ -36,13 +35,11 @@ TextComponent::TextComponent(Window* window, Alignment align, glm::vec3 pos, glm::vec2 size, - unsigned int bgcolor, - float margin) + unsigned int bgcolor) : GuiComponent{window} , mFont{nullptr} , mColor{0x000000FF} , mBgColor{0} - , mMargin{margin} , mRenderBackground{false} , mUppercase{false} , mAutoCalcExtent{1, 1} @@ -238,12 +235,12 @@ void TextComponent::onTextChanged() // Abbreviate text. const std::string abbrev = "..."; glm::vec2 abbrevSize{f->sizeText(abbrev)}; - // mMargin adds a margin around the text if it's abbreviated. - float marginAdjustedSize = mSize.x - (mSize.x * mMargin); - while (text.size() && size.x + abbrevSize.x > marginAdjustedSize) { + while (text.size() && size.x + abbrevSize.x > mSize.x) { size_t newSize = Utils::String::prevCursor(text, text.size()); text.erase(newSize, text.size() - newSize); + if (!text.empty() && text.back() == ' ') + text.pop_back(); size = f->sizeText(text); } diff --git a/es-core/src/components/TextComponent.h b/es-core/src/components/TextComponent.h index cd07f5592..eb1694cdc 100644 --- a/es-core/src/components/TextComponent.h +++ b/es-core/src/components/TextComponent.h @@ -32,8 +32,7 @@ public: Alignment align = ALIGN_LEFT, glm::vec3 pos = {}, glm::vec2 size = {}, - unsigned int bgcolor = 0x00000000, - float margin = 0.0f); + unsigned int bgcolor = 0x00000000); void setFont(const std::shared_ptr& font); void setUppercase(bool uppercase); @@ -89,7 +88,6 @@ private: unsigned int mBgColor; unsigned char mColorOpacity; unsigned char mBgColorOpacity; - float mMargin; bool mRenderBackground; bool mUppercase; From 97af891b664d495b1baa430f22322821d3eb1172 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Fri, 15 Oct 2021 21:21:49 +0200 Subject: [PATCH 11/35] Increased the game description row count for the automatic multi-scraper. --- es-app/src/guis/GuiScraperSearch.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/es-app/src/guis/GuiScraperSearch.cpp b/es-app/src/guis/GuiScraperSearch.cpp index 6a8709c35..14da96239 100644 --- a/es-app/src/guis/GuiScraperSearch.cpp +++ b/es-app/src/guis/GuiScraperSearch.cpp @@ -224,7 +224,7 @@ void GuiScraperSearch::onSizeChanged() mResultDesc->getFont()->getHeight() * 3.0f); else mDescContainer->setSize(mGrid.getColWidth(4) * thumbnailCellScale, - mResultDesc->getFont()->getHeight() * 6.0f); + mResultDesc->getFont()->getHeight() * 8.0f); // Make description text wrap at edge of container. mResultDesc->setSize(mDescContainer->getSize().x, 0.0f); From c68f78f3d16ca6da4479afaba7c179072a60e905 Mon Sep 17 00:00:00 2001 From: shadash Date: Fri, 15 Oct 2021 21:28:12 +0200 Subject: [PATCH 12/35] fix right align Signed-off-by: Sophia Hadash --- es-core/src/components/FlexboxComponent.cpp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/es-core/src/components/FlexboxComponent.cpp b/es-core/src/components/FlexboxComponent.cpp index f9b81abd9..f448fbc16 100644 --- a/es-core/src/components/FlexboxComponent.cpp +++ b/es-core/src/components/FlexboxComponent.cpp @@ -166,10 +166,9 @@ void FlexboxComponent::computeLayout() image.second.setSize(image.second.getSize().x, maxItemSize.y); } - // TODO: Doesn't work correctly. // Apply overall container alignment. if (mAlignment == "right") - x += (mSize.x - size.x * grid.x) - mItemMargin.x; + x += (mSize.x - maxItemSize.x * grid.x - (grid.x - 1) * mItemMargin.x); // Store final item position. image.second.setPosition(x, y); @@ -193,5 +192,22 @@ void FlexboxComponent::computeLayout() } } + // Apply right-align + if (mAlignment == "right") { + unsigned int n = mItemsPerLine - (--i + 1) % std::max(1, static_cast(mItemsPerLine)); + i = 0; + unsigned int line = 1; + for (auto& image : mImages) { + if (!image.second.isVisible()) + continue; + if (line == mLines) + image.second.setPosition(image.second.getPosition().x + + (maxItemSize.x + mItemMargin.x) * n, + image.second.getPosition().y); + if ((i++ + 1) % std::max(1, static_cast(mItemsPerLine)) == 0) + line++; + } + } + mLayoutValid = true; } From 1c93ca2c0759fc2a5d067b9bf4b3a08d33d70cbf Mon Sep 17 00:00:00 2001 From: Sophia Hadash Date: Fri, 15 Oct 2021 21:33:34 +0200 Subject: [PATCH 13/35] fix right align Signed-off-by: Sophia Hadash --- es-core/src/components/FlexboxComponent.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/es-core/src/components/FlexboxComponent.cpp b/es-core/src/components/FlexboxComponent.cpp index f448fbc16..a31871102 100644 --- a/es-core/src/components/FlexboxComponent.cpp +++ b/es-core/src/components/FlexboxComponent.cpp @@ -201,9 +201,10 @@ void FlexboxComponent::computeLayout() if (!image.second.isVisible()) continue; if (line == mLines) - image.second.setPosition(image.second.getPosition().x + - (maxItemSize.x + mItemMargin.x) * n, - image.second.getPosition().y); + image.second.setPosition( + image.second.getPosition().x + + floorf((maxItemSize.x + mItemMargin.x) * static_cast(n)), + image.second.getPosition().y); if ((i++ + 1) % std::max(1, static_cast(mItemsPerLine)) == 0) line++; } From aa8b68f2a9a08b0350fe9b49db130142092ca5bf Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Fri, 15 Oct 2021 22:35:57 +0200 Subject: [PATCH 14/35] Set the menu scroll indicators as enabled by default. --- es-core/src/Settings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/es-core/src/Settings.cpp b/es-core/src/Settings.cpp index a69705ea9..e7035633c 100644 --- a/es-core/src/Settings.cpp +++ b/es-core/src/Settings.cpp @@ -181,7 +181,7 @@ void Settings::setDefaults() mBoolMap["SpecialCharsASCII"] = {false, false}; mBoolMap["ListScrollOverlay"] = {false, false}; mBoolMap["VirtualKeyboard"] = {true, true}; - mBoolMap["ScrollIndicators"] = {false, false}; + mBoolMap["ScrollIndicators"] = {true, true}; mBoolMap["FavoritesAddButton"] = {true, true}; mBoolMap["RandomAddButton"] = {false, false}; mBoolMap["GamelistFilters"] = {true, true}; From 4c556fc820211eab705232fa987a1f729e0b0153 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sat, 16 Oct 2021 12:23:32 +0200 Subject: [PATCH 15/35] Changed the position of the per-game alternative emulator selector window. --- es-app/src/guis/GuiMetaDataEd.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/es-app/src/guis/GuiMetaDataEd.cpp b/es-app/src/guis/GuiMetaDataEd.cpp index 5a945c640..defc04f04 100644 --- a/es-app/src/guis/GuiMetaDataEd.cpp +++ b/es-app/src/guis/GuiMetaDataEd.cpp @@ -306,13 +306,8 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window, static_cast(Renderer::getScreenWidth()) * maxWidthModifier; s->setMenuSize(glm::vec2{maxWidth, s->getMenuSize().y}); - - auto menuSize = s->getMenuSize(); - auto menuPos = s->getMenuPosition(); - - s->setMenuPosition(glm::vec3{(s->getSize().x - menuSize.x) / 2.0f, - (s->getSize().y - menuSize.y) / 3.0f, - menuPos.z}); + s->setMenuPosition(glm::vec3{(s->getSize().x - maxWidth) / 2.0f, + mPosition.y, mPosition.z}); mWindow->pushGui(s); }); } From 690083a123e4c0d4c8b78996709ec2e9a4095d0e Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sat, 16 Oct 2021 13:21:52 +0200 Subject: [PATCH 16/35] The scroll indicators don't fade in and out any longer if quick jumping in a list. --- es-core/src/components/ComponentList.cpp | 7 +++++- es-core/src/components/ComponentList.h | 8 ++++--- .../src/components/ScrollIndicatorComponent.h | 24 ++++++++++++------- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/es-core/src/components/ComponentList.cpp b/es-core/src/components/ComponentList.cpp index c829399ee..d05047e79 100644 --- a/es-core/src/components/ComponentList.cpp +++ b/es-core/src/components/ComponentList.cpp @@ -15,6 +15,7 @@ ComponentList::ComponentList(Window* window) , mFocused{false} , mSetupCompleted{false} , mBottomCameraOffset{false} + , mSingleRowScroll{false} , mSelectorBarOffset{0.0f} , mCameraOffset{0.0f} , mScrollIndicatorStatus{SCROLL_NONE} @@ -63,6 +64,8 @@ bool ComponentList::input(InputConfig* config, Input input) if (size() == 0) return false; + mSingleRowScroll = false; + if (input.value && (config->isMappedTo("a", input) || config->isMappedLike("lefttrigger", input) || config->isMappedLike("righttrigger", input))) { @@ -86,9 +89,11 @@ bool ComponentList::input(InputConfig* config, Input input) // Input handler didn't consume the input - try to scroll. if (config->isMappedLike("up", input)) { + mSingleRowScroll = true; return listInput(input.value != 0 ? -1 : 0); } else if (config->isMappedLike("down", input)) { + mSingleRowScroll = true; return listInput(input.value != 0 ? 1 : 0); } else if (config->isMappedLike("leftshoulder", input)) { @@ -142,7 +147,7 @@ void ComponentList::update(int deltaTime) } if (scrollIndicatorChanged == true && mScrollIndicatorChangedCallback != nullptr) - mScrollIndicatorChangedCallback(mScrollIndicatorStatus); + mScrollIndicatorChangedCallback(mScrollIndicatorStatus, mSingleRowScroll); listUpdate(deltaTime); diff --git a/es-core/src/components/ComponentList.h b/es-core/src/components/ComponentList.h index fd7739fc5..d1a3eee01 100644 --- a/es-core/src/components/ComponentList.h +++ b/es-core/src/components/ComponentList.h @@ -90,7 +90,7 @@ public: { mScrollIndicatorStatus = SCROLL_NONE; if (mScrollIndicatorChangedCallback != nullptr) - mScrollIndicatorChangedCallback(mScrollIndicatorStatus); + mScrollIndicatorChangedCallback(mScrollIndicatorStatus, false); } void setCursorChangedCallback(const std::function& callback) @@ -102,7 +102,7 @@ public: return mCursorChangedCallback; } void setScrollIndicatorChangedCallback( - const std::function& callback) + const std::function& callback) { mScrollIndicatorChangedCallback = callback; } @@ -114,6 +114,7 @@ private: bool mFocused; bool mSetupCompleted; bool mBottomCameraOffset; + bool mSingleRowScroll; void updateCameraOffset(); void updateElementPosition(const ComponentListRow& row); @@ -126,7 +127,8 @@ private: float mCameraOffset; std::function mCursorChangedCallback; - std::function mScrollIndicatorChangedCallback; + std::function + mScrollIndicatorChangedCallback; ScrollIndicator mScrollIndicatorStatus; }; diff --git a/es-core/src/components/ScrollIndicatorComponent.h b/es-core/src/components/ScrollIndicatorComponent.h index 4c0f5801c..58afa5b9b 100644 --- a/es-core/src/components/ScrollIndicatorComponent.h +++ b/es-core/src/components/ScrollIndicatorComponent.h @@ -34,7 +34,7 @@ public: // If the scroll indicators setting is disabled, then show a permanent down arrow // symbol when the component list contains more entries than can fit on screen. componentList.get()->setScrollIndicatorChangedCallback( - [scrollUp, scrollDown](ComponentList::ScrollIndicator state) { + [scrollUp, scrollDown](ComponentList::ScrollIndicator state, bool singleRowScroll) { if (state == ComponentList::SCROLL_UP || state == ComponentList::SCROLL_UP_DOWN || state == ComponentList::SCROLL_DOWN) { @@ -46,8 +46,9 @@ public: // If the scroll indicator setting is enabled, then also show the up and up/down // combination and switch between these as the list is scrolled. componentList.get()->setScrollIndicatorChangedCallback( - [this, scrollUp, scrollDown](ComponentList::ScrollIndicator state) { - float fadeInTime{FADE_IN_TIME}; + [this, scrollUp, scrollDown](ComponentList::ScrollIndicator state, + bool singleRowScroll) { + float fadeTime{FADE_IN_TIME}; bool upFadeIn = false; bool upFadeOut = false; @@ -68,7 +69,7 @@ public: else if (state == ComponentList::SCROLL_UP && mPreviousScrollState == ComponentList::SCROLL_DOWN) { upFadeIn = true; - fadeInTime *= 1.5f; + fadeTime *= 2.0f; scrollDown->setOpacity(0); } else if (state == ComponentList::SCROLL_UP_DOWN && @@ -95,17 +96,22 @@ public: else if (state == ComponentList::SCROLL_DOWN && mPreviousScrollState == ComponentList::SCROLL_UP) { downFadeIn = true; - fadeInTime *= 1.5f; + fadeTime *= 2.0f; scrollUp->setOpacity(0); } + // If jumping more than one row using the shoulder or trigger buttons, then + // don't fade the indicators. + if (!singleRowScroll) + fadeTime = 0.0f; + if (upFadeIn) { auto upFadeInFunc = [scrollUp](float t) { scrollUp->setOpacity( static_cast(glm::mix(0.0f, 1.0f, t) * 255)); }; scrollUp->setAnimation( - new LambdaAnimation(upFadeInFunc, static_cast(fadeInTime)), 0, + new LambdaAnimation(upFadeInFunc, static_cast(fadeTime)), 0, nullptr, false); } @@ -115,7 +121,7 @@ public: static_cast(glm::mix(0.0f, 1.0f, t) * 255)); }; scrollUp->setAnimation( - new LambdaAnimation(upFadeOutFunc, static_cast(fadeInTime)), 0, + new LambdaAnimation(upFadeOutFunc, static_cast(fadeTime)), 0, nullptr, true); } @@ -125,7 +131,7 @@ public: static_cast(glm::mix(0.0f, 1.0f, t) * 255)); }; scrollDown->setAnimation( - new LambdaAnimation(downFadeInFunc, static_cast(fadeInTime)), 0, + new LambdaAnimation(downFadeInFunc, static_cast(fadeTime)), 0, nullptr, false); } @@ -135,7 +141,7 @@ public: static_cast(glm::mix(0.0f, 1.0f, t) * 255)); }; scrollDown->setAnimation( - new LambdaAnimation(downFadeOutFunc, static_cast(fadeInTime)), 0, + new LambdaAnimation(downFadeOutFunc, static_cast(fadeTime)), 0, nullptr, true); } From 8fd05fcd77a3e747431a7d5fd90f8f2d250d41fd Mon Sep 17 00:00:00 2001 From: shadash Date: Sun, 17 Oct 2021 01:11:01 +0200 Subject: [PATCH 17/35] bugfix, simplification Signed-off-by: Sophia Hadash --- es-core/src/components/FlexboxComponent.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/es-core/src/components/FlexboxComponent.cpp b/es-core/src/components/FlexboxComponent.cpp index a31871102..612a61bad 100644 --- a/es-core/src/components/FlexboxComponent.cpp +++ b/es-core/src/components/FlexboxComponent.cpp @@ -194,7 +194,8 @@ void FlexboxComponent::computeLayout() // Apply right-align if (mAlignment == "right") { - unsigned int n = mItemsPerLine - (--i + 1) % std::max(1, static_cast(mItemsPerLine)); + unsigned int m = i % std::max(1, static_cast(mItemsPerLine)); + unsigned int n = m > 0 ? mItemsPerLine - m : m; i = 0; unsigned int line = 1; for (auto& image : mImages) { From 11ca17fc9136b7fbcbded13cfce4c3c9b835d8a6 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sun, 17 Oct 2021 16:14:28 +0200 Subject: [PATCH 18/35] Fixed an issue where the wrong scroll indicators could be displayed. --- es-core/src/components/ComponentList.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/es-core/src/components/ComponentList.cpp b/es-core/src/components/ComponentList.cpp index d05047e79..31eba491a 100644 --- a/es-core/src/components/ComponentList.cpp +++ b/es-core/src/components/ComponentList.cpp @@ -208,7 +208,9 @@ void ComponentList::updateCameraOffset() i++; } - if (mCameraOffset < oldCameraOffset) + if (mCameraOffset < oldCameraOffset && + (oldCameraOffset > mSelectorBarOffset || + mScrollIndicatorStatus != ComponentList::SCROLL_NONE)) mBottomCameraOffset = false; if (mCameraOffset < 0.0f) From af1d1b310966e2cd342e989ae07e60b9acd32502 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sun, 17 Oct 2021 18:45:21 +0200 Subject: [PATCH 19/35] Fixed some rounding issues and the right-alignment in FlexboxComponent. --- es-core/src/components/FlexboxComponent.cpp | 27 +++++++++------------ 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/es-core/src/components/FlexboxComponent.cpp b/es-core/src/components/FlexboxComponent.cpp index 612a61bad..4979de675 100644 --- a/es-core/src/components/FlexboxComponent.cpp +++ b/es-core/src/components/FlexboxComponent.cpp @@ -122,7 +122,7 @@ void FlexboxComponent::computeLayout() newSize = sizeMaxX.x * sizeMaxX.y >= sizeMaxY.x * sizeMaxY.y ? sizeMaxX : sizeMaxY; if (image.second.getSize() != newSize) - image.second.setResize(newSize.x, newSize.y); + image.second.setResize(std::round(newSize.x), std::round(newSize.y)); // In case maxItemSize needs to be updated. if (newSize.x != sizeChange.x) @@ -132,9 +132,9 @@ void FlexboxComponent::computeLayout() } if (maxItemSize.x != sizeChange.x) - maxItemSize.x = sizeChange.x; + maxItemSize.x = std::round(sizeChange.x); if (maxItemSize.y != sizeChange.y) - maxItemSize.y = sizeChange.y; + maxItemSize.y = std::round(sizeChange.y); // Pre-compute layout parameters. float anchorXStart{anchorX}; @@ -192,22 +192,19 @@ void FlexboxComponent::computeLayout() } } - // Apply right-align + // Apply right-align to the images on the last row, if needed. if (mAlignment == "right") { - unsigned int m = i % std::max(1, static_cast(mItemsPerLine)); - unsigned int n = m > 0 ? mItemsPerLine - m : m; - i = 0; - unsigned int line = 1; + std::vector imagesToAlign; for (auto& image : mImages) { if (!image.second.isVisible()) continue; - if (line == mLines) - image.second.setPosition( - image.second.getPosition().x + - floorf((maxItemSize.x + mItemMargin.x) * static_cast(n)), - image.second.getPosition().y); - if ((i++ + 1) % std::max(1, static_cast(mItemsPerLine)) == 0) - line++; + // Only include images on the last row. + if (image.second.getPosition().y == anchorY) + imagesToAlign.push_back(&image.second); + } + for (auto& moveImage : imagesToAlign) { + float offset = (maxItemSize.x + mItemMargin.x) * (grid.x - imagesToAlign.size()); + moveImage->setPosition(moveImage->getPosition().x + offset, moveImage->getPosition().y); } } From 975ff0eb695ce2f86300f3cfbdfe21299b56c1f0 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sun, 17 Oct 2021 21:20:17 +0200 Subject: [PATCH 20/35] Fixed a potential rounding issue. --- es-core/src/components/MenuComponent.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/es-core/src/components/MenuComponent.cpp b/es-core/src/components/MenuComponent.cpp index e7c3c0964..f1cfe029d 100644 --- a/es-core/src/components/MenuComponent.cpp +++ b/es-core/src/components/MenuComponent.cpp @@ -103,7 +103,7 @@ void MenuComponent::updateSize() int i = 0; while (i < mList->size()) { // Add the separator height to the row height so that it also gets properly rendered. - float rowHeight = mList->getRowHeight(i) + (1 * Renderer::getScreenHeightModifier()); + float rowHeight = mList->getRowHeight(i) + (1.0f * Renderer::getScreenHeightModifier()); if (height + rowHeight < maxHeight) height += rowHeight; else From 11665394669cf14915ce30a0d320d94cfe722e89 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sun, 17 Oct 2021 21:51:21 +0200 Subject: [PATCH 21/35] Fixed a general ImageComponent scaling issue caused by incorrect rounding. --- es-core/src/components/ImageComponent.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/es-core/src/components/ImageComponent.cpp b/es-core/src/components/ImageComponent.cpp index f6c2d2af2..0743005a0 100644 --- a/es-core/src/components/ImageComponent.cpp +++ b/es-core/src/components/ImageComponent.cpp @@ -130,9 +130,9 @@ void ImageComponent::resize() } } - mSize.x = floorf(mSize.x); - mSize.y = floorf(mSize.y); - // mSize.y() should already be rounded. + mSize.x = ceilf(mSize.x); + mSize.y = ceilf(mSize.y); + mTexture->rasterizeAt(static_cast(mSize.x), static_cast(mSize.y)); onSizeChanged(); From 94c825e3a37c03a3a927ad2f7968d2c693315ac3 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Mon, 18 Oct 2021 18:07:20 +0200 Subject: [PATCH 22/35] Changed the 'marquee' variable names to 'loop' in TextListComponent.h --- es-core/src/components/TextListComponent.h | 66 +++++++++++----------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/es-core/src/components/TextListComponent.h b/es-core/src/components/TextListComponent.h index 35174aed0..1c60e8ddd 100644 --- a/es-core/src/components/TextListComponent.h +++ b/es-core/src/components/TextListComponent.h @@ -24,7 +24,7 @@ struct TextListData { std::shared_ptr textCache; }; -// A graphical list. Supports multiple colors for rows and scrolling. +// A scrollable text list supporting multiple row colors. template class TextListComponent : public IList { protected: @@ -108,10 +108,10 @@ protected: virtual void onCursorChanged(const CursorState& state) override; private: - int mMarqueeOffset; - int mMarqueeOffset2; - int mMarqueeTime; - bool mMarqueeScroll; + int mLoopOffset; + int mLoopOffset2; + int mLoopTime; + bool mLoopScroll; Alignment mAlignment; float mHorizontalMargin; @@ -138,10 +138,10 @@ TextListComponent::TextListComponent(Window* window) : IList(window) , mSelectorImage(window) { - mMarqueeOffset = 0; - mMarqueeOffset2 = 0; - mMarqueeTime = 0; - mMarqueeScroll = false; + mLoopOffset = 0; + mLoopOffset2 = 0; + mLoopTime = 0; + mLoopScroll = false; mHorizontalMargin = 0.0f; mAlignment = ALIGN_CENTER; @@ -271,26 +271,26 @@ template void TextListComponent::render(const glm::mat4& parentT // Render text. glm::mat4 drawTrans{trans}; - // Currently selected item text might be scrolling. - if (mCursor == i && mMarqueeOffset > 0) + // Currently selected item text might be looping. + if (mCursor == i && mLoopOffset > 0) drawTrans = glm::translate( - drawTrans, offset - glm::vec3{static_cast(mMarqueeOffset), 0.0f, 0.0f}); + drawTrans, offset - glm::vec3{static_cast(mLoopOffset), 0.0f, 0.0f}); else drawTrans = glm::translate(drawTrans, offset); // Needed to avoid flickering when returning to the start position. - if (mMarqueeOffset == 0 && mMarqueeOffset2 == 0) - mMarqueeScroll = false; + if (mLoopOffset == 0 && mLoopOffset2 == 0) + mLoopScroll = false; Renderer::setMatrix(drawTrans); font->renderTextCache(entry.data.textCache.get()); - // Render currently selected row again if marquee is scrolled far enough for it to repeat. - if ((mCursor == i && mMarqueeOffset2 < 0) || (mCursor == i && mMarqueeScroll)) { - mMarqueeScroll = true; + // Render currently selected row again if text is moved far enough for it to repeat. + if ((mCursor == i && mLoopOffset2 < 0) || (mCursor == i && mLoopScroll)) { + mLoopScroll = true; drawTrans = trans; drawTrans = glm::translate( - drawTrans, offset - glm::vec3{static_cast(mMarqueeOffset2), 0.0f, 0.0f}); + drawTrans, offset - glm::vec3{static_cast(mLoopOffset2), 0.0f, 0.0f}); Renderer::setMatrix(drawTrans); font->renderTextCache(entry.data.textCache.get()); } @@ -353,11 +353,11 @@ template void TextListComponent::update(int deltaTime) stopScrolling(); if (!isScrolling() && size() > 0) { - // Always reset the marquee offsets. - mMarqueeOffset = 0; - mMarqueeOffset2 = 0; + // Always reset the loop offsets. + mLoopOffset = 0; + mLoopOffset2 = 0; - // If we're not scrolling and this object's text exceeds our size, then marquee it. + // If we're not scrolling and this object's text exceeds our size, then loop it. const float textLength = mFont ->sizeText(Utils::String::toUpper( mEntries.at(static_cast(mCursor)).name)) @@ -374,16 +374,16 @@ template void TextListComponent::update(int deltaTime) const float returnTime = (returnLength * 1000.0f) / speed; const int maxTime = static_cast(delay + scrollTime + returnTime); - mMarqueeTime += deltaTime; - while (mMarqueeTime > maxTime) - mMarqueeTime -= maxTime; + mLoopTime += deltaTime; + while (mLoopTime > maxTime) + mLoopTime -= maxTime; - mMarqueeOffset = static_cast(Utils::Math::loop(delay, scrollTime + returnTime, - static_cast(mMarqueeTime), - scrollLength + returnLength)); + mLoopOffset = static_cast(Utils::Math::loop(delay, scrollTime + returnTime, + static_cast(mLoopTime), + scrollLength + returnLength)); - if (mMarqueeOffset > (scrollLength - (limit - returnLength))) - mMarqueeOffset2 = static_cast(mMarqueeOffset - (scrollLength + returnLength)); + if (mLoopOffset > (scrollLength - (limit - returnLength))) + mLoopOffset2 = static_cast(mLoopOffset - (scrollLength + returnLength)); } } @@ -405,9 +405,9 @@ void TextListComponent::add(const std::string& name, const T& obj, unsigned i template void TextListComponent::onCursorChanged(const CursorState& state) { - mMarqueeOffset = 0; - mMarqueeOffset2 = 0; - mMarqueeTime = 0; + mLoopOffset = 0; + mLoopOffset2 = 0; + mLoopTime = 0; if (mCursorChangedCallback) mCursorChangedCallback(state); From 1650b33b9ae3208112df70a5353a54070e30bd5c Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Mon, 18 Oct 2021 18:12:19 +0200 Subject: [PATCH 23/35] Changed a few code comments. --- es-app/src/guis/GuiMenu.cpp | 6 +++--- es-app/src/views/gamelist/GridGameListView.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/es-app/src/guis/GuiMenu.cpp b/es-app/src/guis/GuiMenu.cpp index f339f06ee..ffa9cd0f2 100644 --- a/es-app/src/guis/GuiMenu.cpp +++ b/es-app/src/guis/GuiMenu.cpp @@ -61,7 +61,7 @@ GuiMenu::GuiMenu(Window* window) if (isFullUI) addEntry("OTHER SETTINGS", 0x777777FF, true, [this] { openOtherOptions(); }); - // TEMPORARY - disabled for now, will be used in the future. + // TEMPORARY: Disabled for now, will be used in the future. // if (isFullUI) // addEntry("UTILITIES", 0x777777FF, true, [this] { // openUtilitiesMenu(); }); @@ -600,8 +600,8 @@ void GuiMenu::openSoundOptions() { auto s = new GuiSettings(mWindow, "SOUND SETTINGS"); -// TEMPORARY - Hide the volume slider on macOS and BSD Unix until the volume control logic -// has been implemented for these operating systems. +// TODO: Hide the volume slider on macOS and BSD Unix until the volume control logic has been +// implemented for these operating systems. #if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__) // System volume. auto system_volume = std::make_shared(mWindow, 0.f, 100.f, 1.f, "%"); diff --git a/es-app/src/views/gamelist/GridGameListView.cpp b/es-app/src/views/gamelist/GridGameListView.cpp index 1efa09469..284762deb 100644 --- a/es-app/src/views/gamelist/GridGameListView.cpp +++ b/es-app/src/views/gamelist/GridGameListView.cpp @@ -493,7 +493,7 @@ void GridGameListView::updateInfoPanel() if ((comp->isAnimationPlaying(0) && comp->isAnimationReversed(0) != fadingOut) || (!comp->isAnimationPlaying(0) && comp->getOpacity() != (fadingOut ? 0 : 255))) { - // TEMPORARY - This does not seem to work, needs to be reviewed later. + // TODO: This does not seem to work, needs to be reviewed later. // auto func = [comp](float t) { auto func = [](float t) { // comp->setOpacity(static_cast(glm::mix(0.0f, 1.0f, t) * 255)); From c3c9e8408c2ff1c5df51c6544200b0b00b5c0623 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Mon, 18 Oct 2021 18:13:21 +0200 Subject: [PATCH 24/35] Adjusted the colors for the alt and shift keys on the virtual keyboard. --- es-core/src/guis/GuiTextEditKeyboardPopup.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/es-core/src/guis/GuiTextEditKeyboardPopup.cpp b/es-core/src/guis/GuiTextEditKeyboardPopup.cpp index a3ce28161..8a1c270f3 100644 --- a/es-core/src/guis/GuiTextEditKeyboardPopup.cpp +++ b/es-core/src/guis/GuiTextEditKeyboardPopup.cpp @@ -567,8 +567,8 @@ void GuiTextEditKeyboardPopup::shiftKeys() mShift = !mShift; if (mShift) { - mShiftButton->setFlatColorFocused(0xFF2222FF); - mShiftButton->setFlatColorUnfocused(0xFF2222FF); + mShiftButton->setFlatColorFocused(0xF26767FF); + mShiftButton->setFlatColorUnfocused(0xF26767FF); } else { mShiftButton->setFlatColorFocused(0x878787FF); @@ -600,8 +600,8 @@ void GuiTextEditKeyboardPopup::altKeys() mAlt = !mAlt; if (mAlt) { - mAltButton->setFlatColorFocused(0xFF2222FF); - mAltButton->setFlatColorUnfocused(0xFF2222FF); + mAltButton->setFlatColorFocused(0xF26767FF); + mAltButton->setFlatColorUnfocused(0xF26767FF); } else { mAltButton->setFlatColorFocused(0x878787FF); From 0fecb43066f3fcc5245e8b82aafee5cde6e7dbdc Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Mon, 18 Oct 2021 18:14:49 +0200 Subject: [PATCH 25/35] Increased the maximum supported display height to 7680 pixels. --- es-app/src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/es-app/src/main.cpp b/es-app/src/main.cpp index be86c130f..c68c06bbb 100644 --- a/es-app/src/main.cpp +++ b/es-app/src/main.cpp @@ -232,7 +232,7 @@ bool parseArgs(int argc, char* argv[]) } int width = atoi(argv[i + 1]); int height = atoi(argv[i + 2]); - if (width < 224 || height < 224 || width > 7680 || height > 4320 || + if (width < 224 || height < 224 || width > 7680 || height > 7680 || height < width / 4 || width < height / 2) { std::cerr << "Error: Unsupported resolution " << width << "x" << height << " supplied.\n"; From ccc3cae46b721d6c4c77b1f1e38256f80d786532 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Mon, 18 Oct 2021 19:15:50 +0200 Subject: [PATCH 26/35] Fixed some small rounding issues in TextListComponent.h --- es-core/src/components/TextListComponent.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/es-core/src/components/TextListComponent.h b/es-core/src/components/TextListComponent.h index 1c60e8ddd..c551ec9b8 100644 --- a/es-core/src/components/TextListComponent.h +++ b/es-core/src/components/TextListComponent.h @@ -223,8 +223,10 @@ template void TextListComponent::render(const glm::mat4& parentT dim.y = (trans[1].y * dim.y + trans[3].y) - trans[3].y; Renderer::pushClipRect( - glm::ivec2{static_cast(trans[3].x + mHorizontalMargin), static_cast(trans[3].y)}, - glm::ivec2{static_cast(dim.x - mHorizontalMargin * 2.0f), static_cast(dim.y)}); + glm::ivec2{static_cast(std::round(trans[3].x + mHorizontalMargin)), + static_cast(std::round(trans[3].y))}, + glm::ivec2{static_cast(std::round(dim.x - mHorizontalMargin * 2.0f)), + static_cast(std::round(dim.y))}); for (int i = startEntry; i < listCutoff; i++) { typename IList::Entry& entry = mEntries.at(static_cast(i)); From 484606fb6f72442ef8653bc60ff6c0b54d91d729 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Mon, 18 Oct 2021 19:24:47 +0200 Subject: [PATCH 27/35] Added horizontal scrolling of long game names to the scraper GUI. --- es-app/src/guis/GuiScraperSearch.cpp | 9 ++- es-core/src/components/ComponentList.cpp | 88 +++++++++++++++++++++--- es-core/src/components/ComponentList.h | 15 ++++ 3 files changed, 102 insertions(+), 10 deletions(-) diff --git a/es-app/src/guis/GuiScraperSearch.cpp b/es-app/src/guis/GuiScraperSearch.cpp index 14da96239..bf7aba7b9 100644 --- a/es-app/src/guis/GuiScraperSearch.cpp +++ b/es-app/src/guis/GuiScraperSearch.cpp @@ -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& 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& resu row.addElement( std::make_shared( 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; diff --git a/es-core/src/components/ComponentList.cpp b/es-core/src/components/ComponentList.cpp index 31eba491a..849e90195 100644 --- a/es-core/src/components/ComponentList.cpp +++ b/es-core/src/components/ComponentList.cpp @@ -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(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; + } } } @@ -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(std::round(trans[3].x)), - static_cast(std::round(trans[3].y))}, - glm::ivec2{static_cast(std::round(dim.x)), static_cast(std::round(dim.y))}); + const int clipRectPosX{static_cast(std::round(trans[3].x))}; + const int clipRectPosY{static_cast(std::round(trans[3].y))}; + const int clipRectSizeX{static_cast(std::round(dim.x))}; + const int clipRectSizeY{static_cast(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 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(-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); + } + }; + // 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); diff --git a/es-core/src/components/ComponentList.h b/es-core/src/components/ComponentList.h index d1a3eee01..8b9d9f76c 100644 --- a/es-core/src/components/ComponentList.h +++ b/es-core/src/components/ComponentList.h @@ -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 mCursorChangedCallback; std::function mScrollIndicatorChangedCallback; From 96a3b581a22fc4a15a1637a0e078ac01b4cb5275 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Mon, 18 Oct 2021 19:46:41 +0200 Subject: [PATCH 28/35] Documentation update. --- CHANGELOG.md | 8 ++++++++ CONTRIBUTING.md | 1 + USERGUIDE-DEV.md | 2 ++ 3 files changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a7a83df73..f21da3ca2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,8 @@ * Added the ability to make complementary game system customizations without having to replace the entire bundled es_systems.xml file * Added support for an optional \ tag for es_systems.xml that can be used to override the default \ systems sorting * Added menu scroll indicators showing if there are additional entries available below or above what's currently shown on screen +* Improved the layout of the scraper GUIs (single-game scraper and multi-scraper) +* Added horizontal scrolling of long game names to the scraper GUIs * Improved the gamelist filter screen to not allow filtering of values where there is no actual data to filter, e.g. Favorites for a system with no favorite games * Grayed out all fields in the gamelist filter screen where there is no data to filter, previously some fields were removed entirely and some could still be used * Added the ability to filter on blank/unknown values for Genre, Player, Developer, Publisher and Alternative emulator. @@ -25,18 +27,22 @@ * Lowered the minimum supported screen resolution from 640x480 to 224x224 to support arcade cabinet displays such as those running at 384x224 and 224x384 * Expanded the themeable options for "helpsystem" to support custom button graphics, dimmed text and icon colors, upper/lower/camel case and custom spacing * Made the scrolling speed of ScrollableContainer more consistent across various screen resolutions and display aspect ratios +* Decreased the amount of text that ScrollableContainer renders above and below the starting position as content is scrolled * Made the game name and description stop scrolling when running the media viewer, the screensaver or when running in the background while a game is launched * Added notification popups when plugging in or removing controllers * Changed to loading the default theme set rbsimple-DE instead of the first available theme if the currently configured theme is missing * Added support for using the left and right trigger buttons in the help prompts * Removed the "Choose" entry from the help prompts in the gamelist view +* Replaced a number of help prompt hacks with proper solutions * Changed the "Toggle screensaver" help entry in the system view to simply "Screensaver" +* Changed the font size for the custom collection deletion screen to the same size as for all other menus * Added support for upscaling bitmap images using linear filtering * Changed the marquee image upscale filtering from nearest neighbor to linear for the launch screen and the gamelist views * Moved the Media Viewer and Screensaver settings higher in the UI Settings menu * Moved the game media directory setting to the top of the Other Settings menu, following the new Alternative Emulators entry * Added a blinking cursor to TextEditComponent * Changed the filter description "Text filter (game name)" to "Game name" +* Removed a margin hack from TextComponent and if abbreviated strings end with a space character, that space is now removed * Added support for multi-select total count and exclusive multi-select to OptionListComponent * Added support for a maximum name length to OptionListComponent (non-multiselect only) with an abbreviation of the name if it exceeds this value * Added support for key repeat to OptionListComponent, making it possible to cycle through the options by holding the left or right button @@ -70,6 +76,7 @@ * When scraping in interactive mode, the game counter was not decreased when skipping games, making it impossible to skip the final games in the queue * When scraping in interactive mode, "No games found" results could be accepted using the "A" button * When scraping in interactive mode, any refining done using the "Y" button shortcut would not be shown when doing another refine using the "Refine search" button +* Fixed multiple minor rendering issues where graphics would be slightly cut off or incorrectly resized * Under some circumstances ScrollableContainer (used for the game descriptions) would contain a partially rendered bottom line * If the TextListComponent height was not evenly dividable by the font height + line spacing, a partial bottom row would get rendered * The line spacing for TextListComponent was incorrectly calculated for some resolutions such as 2560x1440 @@ -86,6 +93,7 @@ * Under some circumstances and at some screen resolutions, the last menu separator line would not get rendered (still an issue at extreme resolutions like 320x240) * When scrolling in menus, pressing other buttons than "Up" or "Down" did not stop the scrolling which caused all sorts of weird behavior * With the menu scale-up effect enabled and entering a submenu before the parent menu was completely scaled up, the parent would get stuck at a semi-scaled size +* The custom collection deletion screen had incorrect row heights when running at lower resolutions such as 1280x720 * If there was an abbreviated full system name for the "Gamelist on startup" option, that abbreviation would also get displayed when opening the selector window * Really long theme set names would not get abbreviated in the UI settings menu, leading to a garbled "Theme set" setting row * Disabling a collection while its gamelist was displayed would lead to a slide transition from a black screen if a gamelist on startup had been set diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fe83c2b6b..b37752bfe 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -48,6 +48,7 @@ This plan is under constant review so expect it to change from time to time. Sti #### v1.4 * Localization/multi-language support +* Reorganize the menus, possibly adding basic/advanced modes * Authoring tools to clean up orphaned gamelist entries, media files etc. * Scrollbar component for the gamelist view which can be used by the themes * Web proxy support for the scraper diff --git a/USERGUIDE-DEV.md b/USERGUIDE-DEV.md index c0117dc04..6c944394a 100644 --- a/USERGUIDE-DEV.md +++ b/USERGUIDE-DEV.md @@ -47,6 +47,8 @@ The following operating systems have been tested (all for the x86 architecture u **Note:** If using a Mac with an ARM CPU (e.g. M1) you need to install the x86 version of RetroArch and any other emulators, or you won't be able to launch any games. This will be fixed whenever a native macOS ARM build of ES-DE is released. +As for display resolutions, the minimum pixel value is 224 and the maximum is 7680. This means that you can run ES-DE at for instance 320x224 all the way up to 7680x4320 (8K UHD). Vertical screen orientation is also supported, as well as ultra-wide resolutions like 3840x1440. Note that there could be some minor visual glitches when running in vertical orientation (this will be fixed in future ES-DE releases) and for the best experience you will probably need to use a customized theme set when running at extreme or unusual resolutions. + The installation procedure is just covered briefly here and may differ a bit for your specific operating system, so in case of problems refer to your system documentation. **Installing a Linux .deb package** From 114c91679e68b5fc8cd0b28b906b48d1a123ec18 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Mon, 18 Oct 2021 19:58:04 +0200 Subject: [PATCH 29/35] Increased the start delay slightly for the scraper GUI game name scrolling. --- es-core/src/components/ComponentList.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/es-core/src/components/ComponentList.cpp b/es-core/src/components/ComponentList.cpp index 849e90195..1457e3d14 100644 --- a/es-core/src/components/ComponentList.cpp +++ b/es-core/src/components/ComponentList.cpp @@ -178,7 +178,7 @@ void ComponentList::update(int deltaTime) // Loop the text. const float speed{ Font::get(FONT_SIZE_MEDIUM)->sizeText("ABCDEFGHIJKLMNOPQRSTUVWXYZ").x * 0.247f}; - const float delay{1300.0f}; + const float delay{1500.0f}; const float scrollLength{rowWidth}; const float returnLength{speed * 1.5f}; const float scrollTime{(scrollLength * 1000.0f) / speed}; From 022f8c7e8b8efff3e079cd543bc4f95ab4ac21d6 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sat, 23 Oct 2021 15:30:35 +0200 Subject: [PATCH 30/35] Fixed an issue where resizing in SwitchComponent would not reposition the image. --- es-core/src/components/SwitchComponent.cpp | 10 ++++++++++ es-core/src/components/SwitchComponent.h | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/es-core/src/components/SwitchComponent.cpp b/es-core/src/components/SwitchComponent.cpp index 23f64dd24..a2712ec5d 100644 --- a/es-core/src/components/SwitchComponent.cpp +++ b/es-core/src/components/SwitchComponent.cpp @@ -49,6 +49,16 @@ void SwitchComponent::render(const glm::mat4& parentTrans) renderChildren(trans); } +void SwitchComponent::setResize(float width, float height) +{ + // Reposition the switch after resizing to make it centered. + const glm::vec2 oldSize = mImage.getSize(); + mImage.setResize(width, height); + const float xDiff = oldSize.x - mImage.getSize().x; + const float yDiff = oldSize.y - mImage.getSize().y; + mImage.setPosition(mImage.getPosition().x + xDiff, mImage.getPosition().y + yDiff / 2.0f); +} + void SwitchComponent::setState(bool state) { mState = state; diff --git a/es-core/src/components/SwitchComponent.h b/es-core/src/components/SwitchComponent.h index c21219144..a0d1ac33c 100644 --- a/es-core/src/components/SwitchComponent.h +++ b/es-core/src/components/SwitchComponent.h @@ -22,7 +22,7 @@ public: void render(const glm::mat4& parentTrans) override; void onSizeChanged() override { mImage.setSize(mSize); } - void setResize(float width, float height) override { mImage.setResize(width, height); } + void setResize(float width, float height) override; bool getState() const { return mState; } void setState(bool state); From bd62f2af14cf20a79f077e6ab7f9bf73ab031eaa Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sat, 23 Oct 2021 15:36:16 +0200 Subject: [PATCH 31/35] Fixed an issue where the bar and knob in SliderComponent were not correctly aligned vertically. --- es-core/src/components/SliderComponent.cpp | 52 ++++++++++++++-------- es-core/src/components/SliderComponent.h | 1 + 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/es-core/src/components/SliderComponent.cpp b/es-core/src/components/SliderComponent.cpp index bd0c40696..07eb7d36c 100644 --- a/es-core/src/components/SliderComponent.cpp +++ b/es-core/src/components/SliderComponent.cpp @@ -15,15 +15,16 @@ SliderComponent::SliderComponent( Window* window, float min, float max, float increment, const std::string& suffix) - : GuiComponent(window) - , mMin(min) - , mMax(max) - , mSingleIncrement(increment) - , mMoveRate(0) - , mKnob(window) - , mSuffix(suffix) + : GuiComponent{window} + , mMin{min} + , mMax{max} + , mSingleIncrement{increment} + , mMoveRate{0.0f} + , mBarHeight{0.0f} + , mKnob{window} + , mSuffix{suffix} { - assert((min - max) != 0); + assert((min - max) != 0.0f); // Some sane default value. mValue = (max + min) / 2.0f; @@ -41,7 +42,7 @@ bool SliderComponent::input(InputConfig* config, Input input) if (input.value) setValue(mValue - mSingleIncrement); - mMoveRate = input.value ? -mSingleIncrement : 0; + mMoveRate = input.value ? -mSingleIncrement : 0.0f; mMoveAccumulator = -MOVE_REPEAT_DELAY; return true; } @@ -49,13 +50,13 @@ bool SliderComponent::input(InputConfig* config, Input input) if (input.value) setValue(mValue + mSingleIncrement); - mMoveRate = input.value ? mSingleIncrement : 0; + mMoveRate = input.value ? mSingleIncrement : 0.0f; mMoveAccumulator = -MOVE_REPEAT_DELAY; return true; } } else { - mMoveRate = 0; + mMoveRate = 0.0f; } return GuiComponent::input(config, input); @@ -88,10 +89,9 @@ void SliderComponent::render(const glm::mat4& parentTrans) mValueCache->metrics.size.x + (4.0f * Renderer::getScreenWidthModifier()) : 0.0f)}; - // Render line. - const float lineWidth{2.0f * Renderer::getScreenHeightModifier()}; - Renderer::drawRect(mKnob.getSize().x / 2.0f, mSize.y / 2.0f - lineWidth / 2.0f, width, - lineWidth, 0x777777FF, 0x777777FF); + // Render bar. + Renderer::drawRect(mKnob.getSize().x / 2.0f, mSize.y / 2.0f - mBarHeight / 2.0f, width, + mBarHeight, 0x777777FF, 0x777777FF); // Render knob. mKnob.render(trans); @@ -143,14 +143,28 @@ void SliderComponent::onValueChanged() mValueCache->metrics.size.x = textSize.x; // Fudge the width. } - // Update knob position/size. - mKnob.setResize(0, mSize.y * 0.7f); - float lineLength = + mKnob.setResize(0.0f, std::round(mSize.y * 0.7f)); + + float barLength = mSize.x - mKnob.getSize().x - (mValueCache ? mValueCache->metrics.size.x + (4.0f * Renderer::getScreenWidthModifier()) : 0.0f); - mKnob.setPosition(((mValue - mMin / 2.0f) / mMax) * lineLength + mKnob.getSize().x / 2.0f, + int barHeight = static_cast(std::round(2.0f * Renderer::getScreenHeightModifier())); + + // For very low resolutions, make sure the bar height is not rounded to zero. + if (barHeight == 0) + barHeight = 1; + + // Resize the knob one pixel if necessary to keep the bar centered. + if (barHeight % 2 == 0 && static_cast(mKnob.getSize().y) % 2 != 0) + mKnob.setResize(mKnob.getSize().x - 1.0f, mKnob.getSize().y - 1.0f); + else if (barHeight == 1 && static_cast(mKnob.getSize().y) % 2 == 0) + mKnob.setResize(mKnob.getSize().x - 1.0f, mKnob.getSize().y - 1); + + mBarHeight = static_cast(barHeight); + + mKnob.setPosition(((mValue - mMin / 2.0f) / mMax) * barLength + mKnob.getSize().x / 2.0f, mSize.y / 2.0f); } diff --git a/es-core/src/components/SliderComponent.h b/es-core/src/components/SliderComponent.h index 1e6214bc8..9392364d9 100644 --- a/es-core/src/components/SliderComponent.h +++ b/es-core/src/components/SliderComponent.h @@ -45,6 +45,7 @@ private: float mValue; float mSingleIncrement; float mMoveRate; + float mBarHeight; int mMoveAccumulator; ImageComponent mKnob; From 39acfdfe00335cff4b68f9c31d53f593fe06df90 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sat, 23 Oct 2021 15:38:13 +0200 Subject: [PATCH 32/35] (rbsimple-DE) Fixed an issue where the 'megadrive' logo was cut off slightly on right side. --- themes/rbsimple-DE/megadrive/images/logo.svg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/themes/rbsimple-DE/megadrive/images/logo.svg b/themes/rbsimple-DE/megadrive/images/logo.svg index 4bcf3bded..9371aa60a 100644 --- a/themes/rbsimple-DE/megadrive/images/logo.svg +++ b/themes/rbsimple-DE/megadrive/images/logo.svg @@ -3,8 +3,8 @@ + xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="567.500px" + height="90.32px" viewBox="0 0 567.500 90.32" enable-background="new 0 0 566.932 90.32" xml:space="preserve"> From 6cee6d2732ea2ff7bae336c5555dc444bc888883 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sat, 23 Oct 2021 15:45:44 +0200 Subject: [PATCH 33/35] Fixed multiple image scaling and rasterization issues. --- es-core/src/components/ImageComponent.cpp | 32 +++++++++-------------- es-core/src/resources/TextureData.cpp | 9 +++---- es-core/src/resources/TextureData.h | 4 +-- es-core/src/resources/TextureResource.cpp | 2 +- es-core/src/resources/TextureResource.h | 2 +- 5 files changed, 21 insertions(+), 28 deletions(-) diff --git a/es-core/src/components/ImageComponent.cpp b/es-core/src/components/ImageComponent.cpp index 0743005a0..6da5f3d1a 100644 --- a/es-core/src/components/ImageComponent.cpp +++ b/es-core/src/components/ImageComponent.cpp @@ -63,11 +63,8 @@ void ImageComponent::resize() else { // SVG rasterization is determined by height and rasterization is done in terms of pixels. // If rounding is off enough in the rasterization step (for images with extreme aspect - // ratios), it can cause cutoff when the aspect ratio breaks. - // So we always make sure the resultant height is an integer to make sure cutoff doesn't - // happen, and scale width from that (you'll see this scattered throughout the function). - // It's important to use floorf rather than round for this, as we never want to round up - // since that can lead to the cutoff just described. + // ratios), it can cause cutoff when the aspect ratio breaks. So we always make sure to + // round accordingly to avoid such issues. if (mTargetIsMax) { mSize = textureSize; @@ -77,13 +74,11 @@ void ImageComponent::resize() // This will be mTargetSize.x. We can't exceed it, nor be lower than it. mSize.x *= resizeScale.x; // We need to make sure we're not creating an image larger than max size. - mSize.y = std::min(floorf(mSize.y * resizeScale.x), mTargetSize.y); + mSize.y = floorf(std::min(mSize.y * resizeScale.x, mTargetSize.y)); } else { // This will be mTargetSize.y(). We can't exceed it. - mSize.y = floorf(mSize.y * resizeScale.y); - // For SVG rasterization, always calculate width from rounded height (see comment - // above). We need to make sure we're not creating an image larger than max size. + mSize.y *= resizeScale.y; mSize.x = std::min((mSize.y / textureSize.y) * textureSize.x, mTargetSize.x); } } @@ -106,9 +101,7 @@ void ImageComponent::resize() float cropPercent = (mSize.x - mTargetSize.x) / (mSize.x * 2.0f); crop(cropPercent, 0.0f, cropPercent, 0.0f); } - // For SVG rasterization, always calculate width from rounded height (see comment - // above). We need to make sure we're not creating an image smaller than min size. - mSize.y = std::max(floorf(mSize.y), mTargetSize.y); + mSize.y = std::max(mSize.y, mTargetSize.y); mSize.x = std::max((mSize.y / textureSize.y) * textureSize.x, mTargetSize.x); } else { @@ -117,23 +110,24 @@ void ImageComponent::resize() mSize = mTargetSize == glm::vec2{} ? textureSize : mTargetSize; // If only one component is set, we resize in a way that maintains aspect ratio. - // For SVG rasterization, we always calculate width from rounded height (see - // comment above). if (!mTargetSize.x && mTargetSize.y) { - mSize.y = floorf(mTargetSize.y); + mSize.y = mTargetSize.y; mSize.x = (mSize.y / textureSize.y) * textureSize.x; } else if (mTargetSize.x && !mTargetSize.y) { - mSize.y = floorf((mTargetSize.x / textureSize.x) * textureSize.y); + mSize.y = (mTargetSize.x / textureSize.x) * textureSize.y; mSize.x = (mSize.y / textureSize.y) * textureSize.x; } } } - mSize.x = ceilf(mSize.x); - mSize.y = ceilf(mSize.y); + // Make sure sub-pixel values are not rounded to zero. + if (mSize.x < 1.0f) + mSize.x = ceilf(mSize.x); + if (mSize.y < 1.0f) + mSize.y = ceilf(mSize.y); - mTexture->rasterizeAt(static_cast(mSize.x), static_cast(mSize.y)); + mTexture->rasterizeAt(mSize.x, mSize.y); onSizeChanged(); } diff --git a/es-core/src/resources/TextureData.cpp b/es-core/src/resources/TextureData.cpp index 70375c5b3..8e1eba5c5 100644 --- a/es-core/src/resources/TextureData.cpp +++ b/es-core/src/resources/TextureData.cpp @@ -73,8 +73,8 @@ bool TextureData::initSVGFromMemory(const std::string& fileData) mSourceHeight = svgImage->height; } - mWidth = static_cast(floorf(floorf(mSourceWidth) * mScaleDuringLoad)); - mHeight = static_cast(floorf(floorf(mSourceHeight) * mScaleDuringLoad)); + mWidth = static_cast(std::round(mSourceWidth * mScaleDuringLoad)); + mHeight = static_cast(std::round(mSourceHeight * mScaleDuringLoad)); if (mWidth == 0) { // Auto scale width to keep aspect ratio. @@ -92,9 +92,8 @@ bool TextureData::initSVGFromMemory(const std::string& fileData) NSVGrasterizer* rast = nsvgCreateRasterizer(); - nsvgRasterize(rast, svgImage, 0, 0, mHeight / svgImage->height, tempVector.data(), - static_cast(mWidth), static_cast(mHeight), - static_cast(mWidth) * 4); + nsvgRasterize(rast, svgImage, 0, 0, mHeight / svgImage->height, tempVector.data(), mWidth, + mHeight, mWidth * 4); // This is important in order to avoid memory leaks. nsvgDeleteRasterizer(rast); diff --git a/es-core/src/resources/TextureData.h b/es-core/src/resources/TextureData.h index c50cd19d3..0e76d8159 100644 --- a/es-core/src/resources/TextureData.h +++ b/es-core/src/resources/TextureData.h @@ -72,8 +72,8 @@ private: std::string mPath; unsigned int mTextureID; std::vector mDataRGBA; - size_t mWidth; - size_t mHeight; + int mWidth; + int mHeight; float mSourceWidth; float mSourceHeight; float mScaleDuringLoad; diff --git a/es-core/src/resources/TextureResource.cpp b/es-core/src/resources/TextureResource.cpp index 74e5d5bfe..b6f7b8d8a 100644 --- a/es-core/src/resources/TextureResource.cpp +++ b/es-core/src/resources/TextureResource.cpp @@ -194,7 +194,7 @@ std::shared_ptr TextureResource::get(const std::string& path, return tex; } -void TextureResource::rasterizeAt(size_t width, size_t height) +void TextureResource::rasterizeAt(float width, float height) { if (mTextureData != nullptr) { glm::vec2 textureSize = mTextureData.get()->getSize(); diff --git a/es-core/src/resources/TextureResource.h b/es-core/src/resources/TextureResource.h index 223d60fcc..91ba5ef7e 100644 --- a/es-core/src/resources/TextureResource.h +++ b/es-core/src/resources/TextureResource.h @@ -44,7 +44,7 @@ public: // It does unload and re-rasterize the texture though which may cause flickering in some // situations. An alternative is to set a scaling factor directly when loading the texture // using get(), by using the scaleDuringLoad parameter (which also works for raster graphics). - void rasterizeAt(size_t width, size_t height); + void rasterizeAt(float width, float height); glm::vec2 getSourceImageSize() const { return mSourceSize; } virtual ~TextureResource(); From dd94aeec96a2967bb65181bf0fd87f398701e524 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sat, 23 Oct 2021 15:53:31 +0200 Subject: [PATCH 34/35] Fixed two casting issues. --- es-core/src/components/RatingComponent.cpp | 5 ++--- es-core/src/resources/TextureData.cpp | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/es-core/src/components/RatingComponent.cpp b/es-core/src/components/RatingComponent.cpp index 6c2c22c6f..7dcdecf7b 100644 --- a/es-core/src/components/RatingComponent.cpp +++ b/es-core/src/components/RatingComponent.cpp @@ -102,11 +102,10 @@ void RatingComponent::onSizeChanged() mSize.x = mSize.y * NUM_RATING_STARS; if (mSize.y > 0.0f) { - size_t heightPx = static_cast(std::round(mSize.y)); if (mFilledTexture) - mFilledTexture->rasterizeAt(heightPx, heightPx); + mFilledTexture->rasterizeAt(mSize.y, mSize.y); if (mUnfilledTexture) - mUnfilledTexture->rasterizeAt(heightPx, heightPx); + mUnfilledTexture->rasterizeAt(mSize.y, mSize.y); } updateVertices(); diff --git a/es-core/src/resources/TextureData.cpp b/es-core/src/resources/TextureData.cpp index 8e1eba5c5..9537b25d7 100644 --- a/es-core/src/resources/TextureData.cpp +++ b/es-core/src/resources/TextureData.cpp @@ -147,8 +147,8 @@ bool TextureData::initFromRGBA(const unsigned char* dataRGBA, size_t width, size mDataRGBA.reserve(width * height * 4); mDataRGBA.insert(mDataRGBA.begin(), dataRGBA, dataRGBA + (width * height * 4)); - mWidth = width; - mHeight = height; + mWidth = static_cast(width); + mHeight = static_cast(height); return true; } From 3c567c07d8c13172b34419e2f4c051d91e436c00 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sat, 23 Oct 2021 15:56:05 +0200 Subject: [PATCH 35/35] Some very minor adjustments to the bundled badge graphics files. --- resources/graphics/badge_altemulator.svg | 13 +++++++------ resources/graphics/badge_broken.svg | 11 ++++++----- resources/graphics/badge_completed.svg | 13 +++++++------ resources/graphics/badge_favorite.svg | 13 +++++++------ resources/graphics/badge_kidgame.svg | 13 +++++++------ 5 files changed, 34 insertions(+), 29 deletions(-) diff --git a/resources/graphics/badge_altemulator.svg b/resources/graphics/badge_altemulator.svg index 055f15766..f8a9d5442 100644 --- a/resources/graphics/badge_altemulator.svg +++ b/resources/graphics/badge_altemulator.svg @@ -23,11 +23,11 @@ borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" - inkscape:zoom="19.3509" - inkscape:cx="5.374237" - inkscape:cy="26.957777" + inkscape:zoom="12.066296" + inkscape:cx="11.393424" + inkscape:cy="66.081732" inkscape:document-units="mm" - inkscape:current-layer="layer2" + inkscape:current-layer="layer1" showgrid="false" inkscape:window-width="3840" inkscape:window-height="2065" @@ -175,14 +175,15 @@ + transform="matrix(0.10079384,0,0,0.0816046,-2.2547839e-4,271.5469)" + style="fill:#000000;fill-opacity:1"> + style="stroke-width:0.26458001;fill:#000000;fill-opacity:1" /> + transform="matrix(0.10079384,0,0,0.0816046,-2.2531965e-4,271.54688)" + style="fill:#000000;fill-opacity:1"> + style="stroke-width:0.26458001;fill:#000000;fill-opacity:1" /> image/svg+xml - + @@ -74,14 +74,15 @@ + transform="matrix(0.10079384,0,0,0.0816046,-4.2317622e-4,0.13809161)" + style="fill:#0e0e0e;fill-opacity:1"> + style="stroke-width:0.26458001;fill:#0e0e0e;fill-opacity:1" /> image/svg+xml - + @@ -71,14 +71,15 @@ + transform="matrix(0.10079384,0,0,0.0816046,-4.2392733e-4,0.13809204)" + style="fill:#000000;fill-opacity:1"> + style="stroke-width:0.26458001;fill:#000000;fill-opacity:1" /> image/svg+xml - + @@ -74,14 +74,15 @@ + transform="matrix(0.10079384,0,0,0.0816046,-4.237873e-4,0.13809261)" + style="fill:#000000;fill-opacity:1"> + style="stroke-width:0.26458001;fill:#000000;fill-opacity:1" />