From c68f78f3d16ca6da4479afaba7c179072a60e905 Mon Sep 17 00:00:00 2001 From: shadash Date: Fri, 15 Oct 2021 21:28:12 +0200 Subject: [PATCH 01/60] 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 02/60] 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 4c556fc820211eab705232fa987a1f729e0b0153 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sat, 16 Oct 2021 12:23:32 +0200 Subject: [PATCH 03/60] 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 04/60] 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 05/60] 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 06/60] 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 07/60] 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 08/60] 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 09/60] 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 10/60] 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 11/60] 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 12/60] 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 13/60] 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 14/60] 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 15/60] 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 16/60] 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 17/60] 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 18/60] 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 19/60] 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 20/60] (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 21/60] 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 22/60] 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 23/60] 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" /> Date: Sat, 23 Oct 2021 17:35:23 +0200 Subject: [PATCH 24/60] Small simplification in ImageComponent. --- es-core/src/components/ImageComponent.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/es-core/src/components/ImageComponent.cpp b/es-core/src/components/ImageComponent.cpp index 6da5f3d1a..4055c9989 100644 --- a/es-core/src/components/ImageComponent.cpp +++ b/es-core/src/components/ImageComponent.cpp @@ -123,9 +123,9 @@ void ImageComponent::resize() // Make sure sub-pixel values are not rounded to zero. if (mSize.x < 1.0f) - mSize.x = ceilf(mSize.x); + mSize.x = 1.0f; if (mSize.y < 1.0f) - mSize.y = ceilf(mSize.y); + mSize.y = 1.0f; mTexture->rasterizeAt(mSize.x, mSize.y); From edc11e625541f337c4a376759c73fb28c32a2635 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sat, 23 Oct 2021 17:36:13 +0200 Subject: [PATCH 25/60] Added a setter to ImageComponent for mForceLoad. --- es-core/src/components/ImageComponent.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/es-core/src/components/ImageComponent.h b/es-core/src/components/ImageComponent.h index 159d65e3b..40b15cf3d 100644 --- a/es-core/src/components/ImageComponent.h +++ b/es-core/src/components/ImageComponent.h @@ -30,6 +30,8 @@ public: // Use an already existing texture. void setImage(const std::shared_ptr& texture, bool resizeTexture = true); + void setForceLoad(bool status) { mForceLoad = status; } + void onSizeChanged() override { updateVertices(); } // Resize the image to fit this size. If one axis is zero, scale that axis to maintain From 84514f67d0865c0f43996ef07d47ce311794238d Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sat, 23 Oct 2021 19:08:32 +0200 Subject: [PATCH 26/60] Added support for controller types. --- es-app/src/MetaData.cpp | 2 + es-app/src/MetaData.h | 1 + es-app/src/guis/GuiMetaDataEd.cpp | 122 +++++++- es-app/src/guis/GuiMetaDataEd.h | 2 + .../views/gamelist/DetailedGameListView.cpp | 18 +- .../src/views/gamelist/VideoGameListView.cpp | 18 +- es-core/src/ThemeData.cpp | 26 +- es-core/src/components/BadgesComponent.cpp | 190 +++++++++--- es-core/src/components/BadgesComponent.h | 27 +- es-core/src/components/FlexboxComponent.cpp | 286 ++++++++++-------- es-core/src/components/FlexboxComponent.h | 31 +- resources/graphics/badge_controller.svg | 147 +++++++++ .../graphics/controllers/gamepad_generic.svg | 69 +++++ .../controllers/gamepad_nintendo_64.svg | 69 +++++ .../controllers/gamepad_nintendo_nes.svg | 69 +++++ .../controllers/gamepad_nintendo_snes.svg | 69 +++++ .../controllers/gamepad_playstation.svg | 69 +++++ .../graphics/controllers/gamepad_xbox.svg | 69 +++++ .../joycon_left_or_right_nintendo.svg | 69 +++++ .../controllers/joycon_pair_nintendo.svg | 69 +++++ .../controllers/joystick_arcade_2_buttons.svg | 69 +++++ .../controllers/joystick_arcade_3_buttons.svg | 69 +++++ .../controllers/joystick_arcade_4_buttons.svg | 69 +++++ .../controllers/joystick_arcade_6_buttons.svg | 69 +++++ .../graphics/controllers/joystick_generic.svg | 69 +++++ .../graphics/controllers/keyboard_generic.svg | 69 +++++ .../controllers/keyboard_mouse_generic.svg | 69 +++++ .../graphics/controllers/lightgun_generic.svg | 69 +++++ .../controllers/lightgun_nintendo.svg | 69 +++++ .../graphics/controllers/mouse_amiga.svg | 69 +++++ .../graphics/controllers/mouse_generic.svg | 69 +++++ .../controllers/steering_wheel_generic.svg | 69 +++++ .../controllers/trackball_generic.svg | 69 +++++ resources/graphics/controllers/unknown.svg | 69 +++++ .../controllers/wii_remote_nintendo.svg | 69 +++++ .../wii_remote_nunchuck_nintendo.svg | 69 +++++ themes/rbsimple-DE/theme.xml | 10 +- 37 files changed, 2333 insertions(+), 203 deletions(-) create mode 100644 resources/graphics/badge_controller.svg create mode 100644 resources/graphics/controllers/gamepad_generic.svg create mode 100644 resources/graphics/controllers/gamepad_nintendo_64.svg create mode 100644 resources/graphics/controllers/gamepad_nintendo_nes.svg create mode 100644 resources/graphics/controllers/gamepad_nintendo_snes.svg create mode 100644 resources/graphics/controllers/gamepad_playstation.svg create mode 100644 resources/graphics/controllers/gamepad_xbox.svg create mode 100644 resources/graphics/controllers/joycon_left_or_right_nintendo.svg create mode 100644 resources/graphics/controllers/joycon_pair_nintendo.svg create mode 100644 resources/graphics/controllers/joystick_arcade_2_buttons.svg create mode 100644 resources/graphics/controllers/joystick_arcade_3_buttons.svg create mode 100644 resources/graphics/controllers/joystick_arcade_4_buttons.svg create mode 100644 resources/graphics/controllers/joystick_arcade_6_buttons.svg create mode 100644 resources/graphics/controllers/joystick_generic.svg create mode 100644 resources/graphics/controllers/keyboard_generic.svg create mode 100644 resources/graphics/controllers/keyboard_mouse_generic.svg create mode 100644 resources/graphics/controllers/lightgun_generic.svg create mode 100644 resources/graphics/controllers/lightgun_nintendo.svg create mode 100644 resources/graphics/controllers/mouse_amiga.svg create mode 100644 resources/graphics/controllers/mouse_generic.svg create mode 100644 resources/graphics/controllers/steering_wheel_generic.svg create mode 100644 resources/graphics/controllers/trackball_generic.svg create mode 100644 resources/graphics/controllers/unknown.svg create mode 100644 resources/graphics/controllers/wii_remote_nintendo.svg create mode 100644 resources/graphics/controllers/wii_remote_nunchuck_nintendo.svg diff --git a/es-app/src/MetaData.cpp b/es-app/src/MetaData.cpp index 98a354e59..84f52233d 100644 --- a/es-app/src/MetaData.cpp +++ b/es-app/src/MetaData.cpp @@ -37,6 +37,7 @@ MetaDataDecl gameDecls[] = { {"nomultiscrape", MD_BOOL, "false", false, "exclude from multi-scraper", "enter no multi-scrape off/on", false}, {"hidemetadata", MD_BOOL, "false", false, "hide metadata fields", "enter hide metadata off/on", false}, {"playcount", MD_INT, "0", false, "times played", "enter number of times played", false}, +{"controller", MD_CONTROLLER, "", false, "controller type", "select controller type", false}, {"altemulator", MD_ALT_EMULATOR, "", false, "alternative emulator", "select alternative emulator", false}, {"lastplayed", MD_TIME, "0", true, "last played", "enter last played date", false} }; @@ -57,6 +58,7 @@ MetaDataDecl folderDecls[] = { {"broken", MD_BOOL, "false", false, "broken/not working", "enter broken off/on", false}, {"nomultiscrape", MD_BOOL, "false", false, "exclude from multi-scraper", "enter no multi-scrape off/on", false}, {"hidemetadata", MD_BOOL, "false", false, "hide metadata fields", "enter hide metadata off/on", false}, +{"controller", MD_CONTROLLER, "", false, "controller type", "select controller type", false}, {"lastplayed", MD_TIME, "0", true, "last played", "enter last played date", false} }; // clang-format on diff --git a/es-app/src/MetaData.h b/es-app/src/MetaData.h index 8f0ac5964..e7867e9e4 100644 --- a/es-app/src/MetaData.h +++ b/es-app/src/MetaData.h @@ -32,6 +32,7 @@ enum MetaDataType { // Specialized types. MD_MULTILINE_STRING, + MD_CONTROLLER, MD_ALT_EMULATOR, MD_PATH, MD_RATING, diff --git a/es-app/src/guis/GuiMetaDataEd.cpp b/es-app/src/guis/GuiMetaDataEd.cpp index defc04f04..42d9c52a9 100644 --- a/es-app/src/guis/GuiMetaDataEd.cpp +++ b/es-app/src/guis/GuiMetaDataEd.cpp @@ -53,6 +53,12 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window, , mMediaFilesUpdated{false} , mInvalidEmulatorEntry{false} { + mControllerTypes = BadgesComponent::getControllerTypes(); + + // Remove the last "unknown" controller entry. + if (mControllerTypes.size() > 1) + mControllerTypes.pop_back(); + addChild(&mBackground); addChild(&mGrid); @@ -137,8 +143,7 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window, ed = std::make_shared(window); // Make the switches slightly smaller. glm::vec2 switchSize{ed->getSize() * 0.9f}; - ed->setResize(switchSize.x, switchSize.y); - ed->setOrigin(-0.05f, -0.09f); + ed->setResize(ceilf(switchSize.x), switchSize.y); ed->setChangedColor(ICONCOLOR_USERMARKED); row.addElement(ed, false, true); @@ -175,6 +180,89 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window, std::placeholders::_2); break; } + case MD_CONTROLLER: { + ed = std::make_shared(window, "", + Font::get(FONT_SIZE_SMALL, FONT_PATH_LIGHT), + 0x777777FF, ALIGN_RIGHT); + row.addElement(ed, true); + + auto spacer = std::make_shared(mWindow); + spacer->setSize(Renderer::getScreenWidth() * 0.005f, 0.0f); + row.addElement(spacer, false); + + auto bracket = std::make_shared(mWindow); + bracket->setImage(":/graphics/arrow.svg"); + bracket->setResize(glm::vec2{0.0f, lbl->getFont()->getLetterHeight()}); + row.addElement(bracket, false); + + const std::string title = iter->displayPrompt; + + // OK callback (apply new value to ed). + auto updateVal = [ed, originalValue](const std::string& newVal) { + ed->setValue(newVal); + if (newVal == BadgesComponent::getDisplayName(originalValue)) + ed->setColor(DEFAULT_TEXTCOLOR); + else + ed->setColor(TEXTCOLOR_USERMARKED); + }; + + row.makeAcceptInputHandler([this, title, scraperParams, ed, updateVal, + originalValue] { + GuiSettings* s = new GuiSettings(mWindow, title); + + for (auto controllerType : mControllerTypes) { + std::string selectedLabel = ed->getValue(); + std::string label; + ComponentListRow row; + + std::shared_ptr labelText = std::make_shared( + mWindow, label, Font::get(FONT_SIZE_MEDIUM), 0x777777FF); + labelText->setSelectable(true); + labelText->setValue(controllerType.displayName); + + label = controllerType.displayName; + + row.addElement(labelText, true); + + row.makeAcceptInputHandler([s, updateVal, controllerType] { + updateVal(controllerType.displayName); + delete s; + }); + + // Select the row that corresponds to the selected label. + if (selectedLabel == label) + s->addRow(row, true); + else + s->addRow(row, false); + } + + // If a value is set, then display "Clear entry" as the last entry. + if (ed->getValue() != "") { + ComponentListRow row; + std::shared_ptr clearText = std::make_shared( + mWindow, ViewController::CROSSEDCIRCLE_CHAR + " CLEAR ENTRY", + Font::get(FONT_SIZE_MEDIUM), 0x777777FF); + clearText->setSelectable(true); + row.addElement(clearText, true); + row.makeAcceptInputHandler([s, ed] { + ed->setValue(""); + delete s; + }); + s->addRow(row, false); + } + + float aspectValue = 1.778f / Renderer::getScreenAspectRatio(); + float maxWidthModifier = glm::clamp(0.64f * aspectValue, 0.42f, 0.92f); + float maxWidth = + static_cast(Renderer::getScreenWidth()) * maxWidthModifier; + + s->setMenuSize(glm::vec2{maxWidth, s->getMenuSize().y}); + s->setMenuPosition( + glm::vec3{(s->getSize().x - maxWidth) / 2.0f, mPosition.y, mPosition.z}); + mWindow->pushGui(s); + }); + break; + } case MD_ALT_EMULATOR: { mInvalidEmulatorEntry = false; @@ -296,11 +384,7 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window, s->addRow(row, false); } - // Adjust the width depending on the aspect ratio of the screen, to make the - // screen look somewhat coherent regardless of screen type. The 1.778 aspect - // ratio value is the 16:9 reference. float aspectValue = 1.778f / Renderer::getScreenAspectRatio(); - float maxWidthModifier = glm::clamp(0.64f * aspectValue, 0.42f, 0.92f); float maxWidth = static_cast(Renderer::getScreenWidth()) * maxWidthModifier; @@ -390,10 +474,19 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window, assert(ed); mList->addRow(row); - if (iter->type == MD_ALT_EMULATOR && mInvalidEmulatorEntry == true) + if (iter->type == MD_ALT_EMULATOR && mInvalidEmulatorEntry == true) { ed->setValue(ViewController::EXCLAMATION_CHAR + " " + originalValue); - else + } + else if (iter->type == MD_CONTROLLER && mMetaData->get(iter->key) != "") { + std::string displayName = BadgesComponent::getDisplayName(mMetaData->get(iter->key)); + if (displayName != "unknown") + ed->setValue(displayName); + else + ed->setValue(ViewController::EXCLAMATION_CHAR + " " + mMetaData->get(iter->key)); + } + else { ed->setValue(mMetaData->get(iter->key)); + } mEditors.push_back(ed); } @@ -524,6 +617,13 @@ void GuiMetaDataEd::save() if (mMetaDataDecl.at(i).key == "altemulator" && mInvalidEmulatorEntry == true) continue; + if (mMetaDataDecl.at(i).key == "controller" && mEditors.at(i)->getValue() != "") { + std::string shortName = BadgesComponent::getShortName(mEditors.at(i)->getValue()); + if (shortName != "unknown") + mMetaData->set(mMetaDataDecl.at(i).key, shortName); + continue; + } + if (!showHiddenGames && mMetaDataDecl.at(i).key == "hidden" && mEditors.at(i)->getValue() != mMetaData->get("hidden")) hideGameWhileHidden = true; @@ -668,6 +768,12 @@ void GuiMetaDataEd::close() if (key == "altemulator" && mInvalidEmulatorEntry == true) continue; + if (mMetaDataDecl.at(i).key == "controller" && mEditors.at(i)->getValue() != "") { + std::string shortName = BadgesComponent::getShortName(mEditors.at(i)->getValue()); + if (shortName == "unknown" || mMetaDataValue == shortName) + continue; + } + if (mMetaDataValue != mEditorsValue) { metadataUpdated = true; break; diff --git a/es-app/src/guis/GuiMetaDataEd.h b/es-app/src/guis/GuiMetaDataEd.h index ec1793145..e73714e8d 100644 --- a/es-app/src/guis/GuiMetaDataEd.h +++ b/es-app/src/guis/GuiMetaDataEd.h @@ -13,6 +13,7 @@ #include "GuiComponent.h" #include "MetaData.h" +#include "components/BadgesComponent.h" #include "components/ComponentGrid.h" #include "components/NinePatchComponent.h" #include "components/ScrollIndicatorComponent.h" @@ -60,6 +61,7 @@ private: ScraperSearchParams mScraperParams; + std::vector mControllerTypes; std::vector> mEditors; std::vector mMetaDataDecl; diff --git a/es-app/src/views/gamelist/DetailedGameListView.cpp b/es-app/src/views/gamelist/DetailedGameListView.cpp index b343e8b3c..a47f65f98 100644 --- a/es-app/src/views/gamelist/DetailedGameListView.cpp +++ b/es-app/src/views/gamelist/DetailedGameListView.cpp @@ -104,7 +104,7 @@ DetailedGameListView::DetailedGameListView(Window* window, FileData* root) // Badges. addChild(&mBadges); - mBadges.setOrigin(0.0f, 0.0f); + mBadges.setOrigin(0.5f, 0.5f); mBadges.setPosition(mSize.x * 0.8f, mSize.y * 0.7f); mBadges.setSize(mSize.x * 0.15f, mSize.y * 0.2f); mBadges.setDefaultZIndex(50.0f); @@ -410,15 +410,23 @@ void DetailedGameListView::updateInfoPanel() mPlayers.setValue(file->metadata.get("players")); // Populate the badge slots based on game metadata. - std::vector badgeSlots; + std::vector badgeSlots; for (auto badge : mBadges.getBadgeTypes()) { - if (badge == "altemulator") { + BadgesComponent::BadgeInfo badgeInfo; + badgeInfo.badgeType = badge; + if (badge == "controller") { + if (file->metadata.get("controller").compare("") != 0) { + badgeInfo.controllerType = file->metadata.get("controller"); + badgeSlots.push_back(badgeInfo); + } + } + else if (badge == "altemulator") { if (file->metadata.get(badge).compare("") != 0) - badgeSlots.push_back(badge); + badgeSlots.push_back(badgeInfo); } else { if (file->metadata.get(badge).compare("true") == 0) - badgeSlots.push_back(badge); + badgeSlots.push_back(badgeInfo); } } mBadges.setBadges(badgeSlots); diff --git a/es-app/src/views/gamelist/VideoGameListView.cpp b/es-app/src/views/gamelist/VideoGameListView.cpp index c0f1c1bc4..699c253f8 100644 --- a/es-app/src/views/gamelist/VideoGameListView.cpp +++ b/es-app/src/views/gamelist/VideoGameListView.cpp @@ -121,7 +121,7 @@ VideoGameListView::VideoGameListView(Window* window, FileData* root) // Badges. addChild(&mBadges); - mBadges.setOrigin(0.0f, 0.0f); + mBadges.setOrigin(0.5f, 0.5f); mBadges.setPosition(mSize.x * 0.8f, mSize.y * 0.7f); mBadges.setSize(mSize.x * 0.15f, mSize.y * 0.2f); mBadges.setDefaultZIndex(50.0f); @@ -450,15 +450,23 @@ void VideoGameListView::updateInfoPanel() mPlayers.setValue(file->metadata.get("players")); // Populate the badge slots based on game metadata. - std::vector badgeSlots; + std::vector badgeSlots; for (auto badge : mBadges.getBadgeTypes()) { - if (badge == "altemulator") { + BadgesComponent::BadgeInfo badgeInfo; + badgeInfo.badgeType = badge; + if (badge == "controller") { + if (file->metadata.get("controller").compare("") != 0) { + badgeInfo.controllerType = file->metadata.get("controller"); + badgeSlots.push_back(badgeInfo); + } + } + else if (badge == "altemulator") { if (file->metadata.get(badge).compare("") != 0) - badgeSlots.push_back(badge); + badgeSlots.push_back(badgeInfo); } else { if (file->metadata.get(badge).compare("true") == 0) - badgeSlots.push_back(badge); + badgeSlots.push_back(badgeInfo); } } mBadges.setBadges(badgeSlots); diff --git a/es-core/src/ThemeData.cpp b/es-core/src/ThemeData.cpp index 603a0131b..e1e83f432 100644 --- a/es-core/src/ThemeData.cpp +++ b/es-core/src/ThemeData.cpp @@ -155,10 +155,12 @@ std::map> The {"alignment", STRING}, {"itemsPerRow", FLOAT}, {"rows", FLOAT}, - {"itemPlacement", STRING}, {"itemMargin", NORMALIZED_PAIR}, {"slots", STRING}, + {"controllerPos", NORMALIZED_PAIR}, + {"controllerSize", FLOAT}, {"customBadgeIcon", PATH}, + {"customControllerIcon", PATH}, {"visible", BOOLEAN}, {"zIndex", FLOAT}}}, {"sound", {{"path", PATH}}}, @@ -519,21 +521,29 @@ void ThemeData::parseElement(const pugi::xml_node& root, } // Special parsing instruction for recurring options. - // Store as it's attribute to prevent nodes overwriting each other. + // Store as its attribute to prevent nodes overwriting each other. if (strcmp(node.name(), "customButtonIcon") == 0) { - const auto btn = node.attribute("button").as_string(""); - if (strcmp(btn, "") == 0) + const auto button = node.attribute("button").as_string(""); + if (strcmp(button, "") == 0) LOG(LogError) << " element requires the `button` property."; else - element.properties[btn] = path; + element.properties[button] = path; } else if (strcmp(node.name(), "customBadgeIcon") == 0) { - const auto btn = node.attribute("badge").as_string(""); - if (strcmp(btn, "") == 0) + const auto badge = node.attribute("badge").as_string(""); + if (strcmp(badge, "") == 0) LOG(LogError) << " element requires the `badge` property."; else - element.properties[btn] = path; + element.properties[badge] = path; + } + else if (strcmp(node.name(), "customControllerIcon") == 0) { + const auto controller = node.attribute("controller").as_string(""); + if (strcmp(controller, "") == 0) + LOG(LogError) + << " element requires the `controller` property."; + else + element.properties[controller] = path; } else element.properties[node.name()] = path; diff --git a/es-core/src/components/BadgesComponent.cpp b/es-core/src/components/BadgesComponent.cpp index 00c023b64..b932fddea 100644 --- a/es-core/src/components/BadgesComponent.cpp +++ b/es-core/src/components/BadgesComponent.cpp @@ -11,6 +11,7 @@ #define SLOT_COMPLETED "completed" #define SLOT_KIDGAME "kidgame" #define SLOT_BROKEN "broken" +#define SLOT_CONTROLLER "controller" #define SLOT_ALTEMULATOR "altemulator" #include "components/BadgesComponent.h" @@ -19,46 +20,134 @@ #include "ThemeData.h" #include "utils/StringUtil.h" +std::vector BadgesComponent::sControllerTypes; + +// clang-format off + +// The "unknown" controller entry has to be placed last. +ControllerTypes sControllerDefinitions [] = { + // shortName displayName fileName + {"gamepad_generic", "Gamepad (Generic)", ":/graphics/controllers/gamepad_generic.svg"}, + {"gamepad_xbox", "Gamepad (Xbox)", ":/graphics/controllers/gamepad_xbox.svg"}, + {"gamepad_playstation", "Gamepad (PlayStation)", ":/graphics/controllers/gamepad_playstation.svg"}, + {"gamepad_nintendo_nes", "Gamepad (Nintendo NES)", ":/graphics/controllers/gamepad_nintendo_nes.svg"}, + {"gamepad_nintendo_snes", "Gamepad (Nintendo SNES)", ":/graphics/controllers/gamepad_nintendo_snes.svg"}, + {"gamepad_nintendo_64", "Gamepad (Nintendo 64)", ":/graphics/controllers/gamepad_nintendo_64.svg"}, + {"joystick_generic", "Joystick (Generic)", ":/graphics/controllers/joystick_generic.svg"}, + {"joystick_arcade_2_buttons", "Joystick (Arcade 2 Buttons)", ":/graphics/controllers/joystick_arcade_2_buttons.svg"}, + {"joystick_arcade_3_buttons", "Joystick (Arcade 3 Buttons)", ":/graphics/controllers/joystick_arcade_3_buttons.svg"}, + {"joystick_arcade_4_buttons", "Joystick (Arcade 4 Buttons)", ":/graphics/controllers/joystick_arcade_4_buttons.svg"}, + {"joystick_arcade_6_buttons", "Joystick (Arcade 6 Buttons)", ":/graphics/controllers/joystick_arcade_6_buttons.svg"}, + {"trackball_generic", "Trackball (Generic)", ":/graphics/controllers/trackball_generic.svg"}, + {"lightgun_generic", "Lightgun (Generic)", ":/graphics/controllers/lightgun_generic.svg"}, + {"lightgun_nintendo", "Lightgun (Nintendo)", ":/graphics/controllers/lightgun_nintendo.svg"}, + {"keyboard_generic", "Keyboard (Generic)", ":/graphics/controllers/keyboard_generic.svg"}, + {"mouse_generic", "Mouse (Generic)", ":/graphics/controllers/mouse_generic.svg"}, + {"mouse_amiga", "Mouse (Amiga)", ":/graphics/controllers/mouse_amiga.svg"}, + {"keyboard_mouse_generic", "Keyboard and Mouse (Generic)", ":/graphics/controllers/keyboard_mouse_generic.svg"}, + {"steering_wheel_generic", "Steering Wheel (Generic)", ":/graphics/controllers/steering_wheel_generic.svg"}, + {"wii_remote_nintendo", "Wii Remote (Nintendo)", ":/graphics/controllers/wii_remote_nintendo.svg"}, + {"wii_remote_nunchuck_nintendo", "Wii Remote and Nunchuck (Nintendo)", ":/graphics/controllers/wii_remote_nunchuck_nintendo.svg"}, + {"joycon_left_or_right_nintendo", "Joy-Con Left or Right (Nintendo)", ":/graphics/controllers/joycon_left_or_right_nintendo.svg"}, + {"joycon_pair_nintendo", "Joy-Con Pair (Nintendo)", ":/graphics/controllers/joycon_pair_nintendo.svg"}, + {"unknown", "Unknown Controller", ":/graphics/controllers/unknown.svg"} +}; + +// clang-format on + BadgesComponent::BadgesComponent(Window* window) : GuiComponent{window} - , mFlexboxComponent{window, mBadgeImages} - , mBadgeTypes{{SLOT_FAVORITE, SLOT_COMPLETED, SLOT_KIDGAME, SLOT_BROKEN, SLOT_ALTEMULATOR}} + , mFlexboxItems{} + , mFlexboxComponent{window, mFlexboxItems} + , mBadgeTypes{{SLOT_FAVORITE, SLOT_COMPLETED, SLOT_KIDGAME, SLOT_BROKEN, SLOT_CONTROLLER, + SLOT_ALTEMULATOR}} { mBadgeIcons[SLOT_FAVORITE] = ":/graphics/badge_favorite.svg"; mBadgeIcons[SLOT_COMPLETED] = ":/graphics/badge_completed.svg"; mBadgeIcons[SLOT_KIDGAME] = ":/graphics/badge_kidgame.svg"; mBadgeIcons[SLOT_BROKEN] = ":/graphics/badge_broken.svg"; + mBadgeIcons[SLOT_CONTROLLER] = ":/graphics/badge_controller.svg"; mBadgeIcons[SLOT_ALTEMULATOR] = ":/graphics/badge_altemulator.svg"; } -void BadgesComponent::setBadges(const std::vector& badges) +void BadgesComponent::populateControllerTypes() +{ + sControllerTypes.clear(); + for (auto type : sControllerDefinitions) + sControllerTypes.push_back(type); +} + +void BadgesComponent::setBadges(const std::vector& badges) { std::map prevVisibility; + std::map prevPlayers; + std::map prevController; // Save the visibility status to know whether any badges changed. - for (auto& image : mBadgeImages) { - prevVisibility[image.first] = image.second.isVisible(); - image.second.setVisible(false); + for (auto& item : mFlexboxItems) { + prevVisibility[item.label] = item.visible; + prevController[item.label] = item.overlayImage.getTexture()->getTextureFilePath(); + item.visible = false; } for (auto& badge : badges) { auto it = std::find_if( - mBadgeImages.begin(), mBadgeImages.end(), - [badge](std::pair image) { return image.first == badge; }); + mFlexboxItems.begin(), mFlexboxItems.end(), + [badge](FlexboxComponent::FlexboxItem item) { return item.label == badge.badgeType; }); - if (it != mBadgeImages.cend()) - it->second.setVisible(true); + if (it != mFlexboxItems.end()) { + it->visible = true; + if (badge.controllerType != "" && + badge.controllerType != it->overlayImage.getTexture()->getTextureFilePath()) { + + auto it2 = std::find_if(sControllerTypes.begin(), sControllerTypes.end(), + [badge](ControllerTypes controllerType) { + return controllerType.shortName == badge.controllerType; + }); + + if (it2 != sControllerTypes.cend()) { + it->overlayImage.setImage((*it2).fileName); + } + else if (badge.controllerType != "") + it->overlayImage.setImage(sControllerTypes.back().fileName); + } + } } // Only recalculate the flexbox if any badges changed. - for (auto& image : mBadgeImages) { - if (prevVisibility[image.first] != image.second.isVisible()) { + for (auto& item : mFlexboxItems) { + if (prevVisibility[item.label] != item.visible || + prevController[item.label] != item.label) { mFlexboxComponent.onSizeChanged(); break; } } } +const std::string BadgesComponent::getShortName(const std::string& displayName) +{ + auto it = std::find_if(sControllerTypes.begin(), sControllerTypes.end(), + [displayName](ControllerTypes controllerType) { + return controllerType.displayName == displayName; + }); + if (it != sControllerTypes.end()) + return (*it).shortName; + else + return "unknown"; +} + +const std::string BadgesComponent::getDisplayName(const std::string& shortName) +{ + auto it = std::find_if(sControllerTypes.begin(), sControllerTypes.end(), + [shortName](ControllerTypes controllerType) { + return controllerType.shortName == shortName; + }); + if (it != sControllerTypes.end()) + return (*it).displayName; + else + return "unknown"; +} + void BadgesComponent::render(const glm::mat4& parentTrans) { if (!isVisible()) @@ -79,6 +168,8 @@ void BadgesComponent::applyTheme(const std::shared_ptr& theme, const std::string& element, unsigned int properties) { + populateControllerTypes(); + using namespace ThemeFlags; const ThemeData::ThemeElement* elem{theme->getElement(view, element, "badges")}; @@ -119,27 +210,11 @@ void BadgesComponent::applyTheme(const std::shared_ptr& theme, } } - if (elem->has("itemPlacement")) { - std::string itemPlacement{elem->get("itemPlacement")}; - if (itemPlacement != "top" && itemPlacement != "center" && itemPlacement != "bottom" && - itemPlacement != "stretch") { - LOG(LogWarning) - << "BadgesComponent: Invalid theme configuration, set to \"" - << itemPlacement << "\""; - } - else { - if (itemPlacement == "top") - itemPlacement = "start"; - else if (itemPlacement == "bottom") - itemPlacement = "end"; - mFlexboxComponent.setItemPlacement(itemPlacement); - } - } - if (elem->has("itemMargin")) { - const glm::vec2 itemMargin = elem->get("itemMargin"); - if (itemMargin.x < 0.0f || itemMargin.x > 0.2f || itemMargin.y < 0.0f || - itemMargin.y > 0.2f) { + glm::vec2 itemMargin = elem->get("itemMargin"); + if ((itemMargin.x != -1.0 && itemMargin.y != -1.0) && + (itemMargin.x < 0.0f || itemMargin.x > 0.2f || itemMargin.y < 0.0f || + itemMargin.y > 0.2f)) { LOG(LogWarning) << "BadgesComponent: Invalid theme configuration, set to \"" << itemMargin.x << " " << itemMargin.y << "\""; @@ -149,26 +224,67 @@ void BadgesComponent::applyTheme(const std::shared_ptr& theme, } } + if (elem->has("controllerPos")) { + const glm::vec2 controllerPos = elem->get("controllerPos"); + if (controllerPos.x < -1.0f || controllerPos.x > 2.0f || controllerPos.y < -1.0f || + controllerPos.y > 2.0f) { + LOG(LogWarning) + << "BadgesComponent: Invalid theme configuration, set to \"" + << controllerPos.x << " " << controllerPos.y << "\""; + } + else { + mFlexboxComponent.setOverlayPosition(controllerPos); + } + } + + if (elem->has("controllerSize")) { + const float controllerSize = elem->get("controllerSize"); + if (controllerSize < 0.1f || controllerSize > 2.0f) { + LOG(LogWarning) + << "BadgesComponent: Invalid theme configuration, set to \"" + << controllerSize << "\""; + } + else { + mFlexboxComponent.setOverlaySize(controllerSize); + } + } + if (elem->has("slots")) { - std::vector slots = Utils::String::delimitedStringToVector( - Utils::String::toLower(elem->get("slots")), " "); + // Replace possible whitespace separators with commas. + std::string slotsTag = Utils::String::toLower(elem->get("slots")); + for (auto& character : slotsTag) { + if (std::isspace(character)) + character = ','; + } + slotsTag = Utils::String::replace(slotsTag, ",,", ","); + std::vector slots = Utils::String::delimitedStringToVector(slotsTag, ","); for (auto slot : slots) { if (std::find(mBadgeTypes.cbegin(), mBadgeTypes.cend(), slot) != mBadgeTypes.end()) { if (properties & PATH && elem->has(slot)) mBadgeIcons[slot] = elem->get(slot); - ImageComponent badgeImage{mWindow}; + FlexboxComponent::FlexboxItem item; + item.label = slot; + ImageComponent badgeImage{mWindow}; + badgeImage.setForceLoad(true); badgeImage.setImage(mBadgeIcons[slot]); - badgeImage.setVisible(false); - mBadgeImages.push_back(std::make_pair(slot, badgeImage)); + item.baseImage = badgeImage; + item.overlayImage = ImageComponent{mWindow}; + + mFlexboxItems.push_back(item); } else { LOG(LogError) << "Invalid badge slot \"" << slot << "\" defined"; } } + for (auto& controllerType : sControllerTypes) { + if (properties & PATH && elem->has(controllerType.shortName)) + controllerType.fileName = elem->get(controllerType.shortName); + } + GuiComponent::applyTheme(theme, view, element, properties); mFlexboxComponent.setPosition(mPosition); diff --git a/es-core/src/components/BadgesComponent.h b/es-core/src/components/BadgesComponent.h index 12cbd5927..a064966eb 100644 --- a/es-core/src/components/BadgesComponent.h +++ b/es-core/src/components/BadgesComponent.h @@ -13,13 +13,34 @@ #include "FlexboxComponent.h" #include "GuiComponent.h" +struct ControllerTypes { + std::string shortName; + std::string displayName; + std::string fileName; +}; + class BadgesComponent : public GuiComponent { public: BadgesComponent(Window* window); + struct BadgeInfo { + std::string badgeType; + std::string controllerType; + }; + + static void populateControllerTypes(); std::vector getBadgeTypes() { return mBadgeTypes; } - void setBadges(const std::vector& badges); + void setBadges(const std::vector& badges); + static const std::vector& getControllerTypes() + { + if (sControllerTypes.empty()) + populateControllerTypes(); + return sControllerTypes; + } + + static const std::string getShortName(const std::string& displayName); + static const std::string getDisplayName(const std::string& shortName); void render(const glm::mat4& parentTrans) override; void onSizeChanged() override { mFlexboxComponent.onSizeChanged(); } @@ -30,11 +51,13 @@ public: unsigned int properties) override; private: + static std::vector sControllerTypes; + + std::vector mFlexboxItems; FlexboxComponent mFlexboxComponent; std::vector mBadgeTypes; std::map mBadgeIcons; - std::vector> mBadgeImages; }; #endif // ES_CORE_COMPONENTS_BADGES_COMPONENT_H diff --git a/es-core/src/components/FlexboxComponent.cpp b/es-core/src/components/FlexboxComponent.cpp index 4979de675..da3bf1b1c 100644 --- a/es-core/src/components/FlexboxComponent.cpp +++ b/es-core/src/components/FlexboxComponent.cpp @@ -19,16 +19,17 @@ #include "Settings.h" #include "ThemeData.h" -FlexboxComponent::FlexboxComponent(Window* window, - std::vector>& images) +FlexboxComponent::FlexboxComponent(Window* window, std::vector& items) : GuiComponent{window} - , mImages(images) + , mItems(items) , mDirection{DEFAULT_DIRECTION} , mAlignment{DEFAULT_ALIGNMENT} , mItemsPerLine{DEFAULT_ITEMS_PER_LINE} , mLines{DEFAULT_LINES} , mItemPlacement{DEFAULT_ITEM_PLACEMENT} , mItemMargin{glm::vec2{DEFAULT_MARGIN_X, DEFAULT_MARGIN_Y}} + , mOverlayPosition{0.5f, 0.5f} + , mOverlaySize{0.5f} , mLayoutValid{false} { } @@ -45,35 +46,47 @@ void FlexboxComponent::render(const glm::mat4& parentTrans) Renderer::setMatrix(trans); if (Settings::getInstance()->getBool("DebugImage")) - Renderer::drawRect(0.0f, 0.0f, mSize.x, mSize.y, 0xFF000033, 0xFF000033); + Renderer::drawRect(0.0f, 0.0f, ceilf(mSize.x), ceilf(mSize.y), 0xFF000033, 0xFF000033); - for (auto& image : mImages) { + for (auto& item : mItems) { + if (!item.visible) + continue; if (mOpacity == 255) { - image.second.render(trans); + item.baseImage.render(trans); + if (item.overlayImage.getTexture() != nullptr) + item.overlayImage.render(trans); } else { - image.second.setOpacity(mOpacity); - image.second.render(trans); - image.second.setOpacity(255); + item.baseImage.setOpacity(mOpacity); + item.baseImage.render(trans); + item.baseImage.setOpacity(255); + if (item.overlayImage.getTexture() != nullptr) { + item.overlayImage.setOpacity(mOpacity); + item.overlayImage.render(trans); + item.overlayImage.setOpacity(255); + } } } } +void FlexboxComponent::setItemMargin(glm::vec2 value) +{ + if (value.x == -1.0f) + mItemMargin.x = std::roundf(value.y * Renderer::getScreenHeight()); + else + mItemMargin.x = std::roundf(value.x * Renderer::getScreenWidth()); + + if (value.y == -1.0f) + mItemMargin.y = std::roundf(value.x * Renderer::getScreenWidth()); + else + mItemMargin.y = std::roundf(value.y * Renderer::getScreenHeight()); + + mLayoutValid = false; +} + void FlexboxComponent::computeLayout() { - // Start placing items in the top-left. - float anchorX{0.0f}; - float anchorY{0.0f}; - - // Translation directions when placing items. - glm::vec2 directionLine{1, 0}; - glm::vec2 directionRow{0, 1}; - - // Change direction. - if (mDirection == "column") { - directionLine = {0, 1}; - directionRow = {1, 0}; - } + // TODO: There is no right-alignment support for column mode. // If we're not clamping itemMargin to a reasonable value, all kinds of weird rendering // issues could occur. @@ -86,125 +99,146 @@ void FlexboxComponent::computeLayout() mSize.y = glm::clamp(mSize.y, static_cast(Renderer::getScreenHeight()) * 0.03f, static_cast(Renderer::getScreenHeight())); - // Compute maximum image dimensions. - glm::vec2 grid; - if (mDirection == "row") - grid = {mItemsPerLine, mLines}; - else - grid = {mLines, mItemsPerLine}; + if (mItemsPerLine * mLines < mItems.size()) { + LOG(LogWarning) + << "FlexboxComponent: Invalid theme configuration, the number of badges" + " exceeds the product of times , setting to " + << mItems.size(); + mItemsPerLine = static_cast(mItems.size()); + } + glm::vec2 grid{mItemsPerLine, mLines}; glm::vec2 maxItemSize{(mSize + mItemMargin - grid * mItemMargin) / grid}; - maxItemSize.x = floorf(maxItemSize.x); - maxItemSize.y = floorf(maxItemSize.y); - if (grid.x * grid.y < static_cast(mImages.size())) { - LOG(LogWarning) << "FlexboxComponent: Invalid theme configuration, the number of badges " - "exceeds the product of times "; - } + float rowHeight{0.0f}; + bool firstItem{true}; - glm::vec2 sizeChange{0.0f, 0.0f}; - - // Set final image dimensions. - for (auto& image : mImages) { - if (!image.second.isVisible()) - continue; - auto oldSize{image.second.getSize()}; - if (oldSize.x == 0) - oldSize.x = maxItemSize.x; - glm::vec2 sizeMaxX{maxItemSize.x, oldSize.y * (maxItemSize.x / oldSize.x)}; - glm::vec2 sizeMaxY{oldSize.x * (maxItemSize.y / oldSize.y), maxItemSize.y}; - glm::vec2 newSize; - if (sizeMaxX.y > maxItemSize.y) - newSize = sizeMaxY; - else if (sizeMaxY.x > maxItemSize.x) - newSize = sizeMaxX; - else - newSize = sizeMaxX.x * sizeMaxX.y >= sizeMaxY.x * sizeMaxY.y ? sizeMaxX : sizeMaxY; - - if (image.second.getSize() != newSize) - image.second.setResize(std::round(newSize.x), std::round(newSize.y)); - - // In case maxItemSize needs to be updated. - if (newSize.x != sizeChange.x) - sizeChange.x = newSize.x; - if (newSize.y != sizeChange.y) - sizeChange.y = newSize.y; - } - - if (maxItemSize.x != sizeChange.x) - maxItemSize.x = std::round(sizeChange.x); - if (maxItemSize.y != sizeChange.y) - maxItemSize.y = std::round(sizeChange.y); - - // Pre-compute layout parameters. - float anchorXStart{anchorX}; - float anchorYStart{anchorY}; - - int i = 0; - - // Iterate through the images. - for (auto& image : mImages) { - if (!image.second.isVisible()) + // Calculate maximum item dimensions. + for (auto& item : mItems) { + if (!item.visible) continue; - auto size{image.second.getSize()}; + glm::vec2 sizeDiff{item.baseImage.getSize() / maxItemSize}; - // Top-left anchor position. - float x{anchorX}; - float y{anchorY}; - - // Apply alignment. - if (mItemPlacement == "end") { - x += directionLine.x == 0 ? (maxItemSize.x - size.x) : 0; - y += directionLine.y == 0 ? (maxItemSize.y - size.y) : 0; - } - else if (mItemPlacement == "center") { - x += directionLine.x == 0 ? (maxItemSize.x - size.x) / 2 : 0; - y += directionLine.y == 0 ? (maxItemSize.y - size.y) / 2 : 0; - } - else if (mItemPlacement == "stretch" && mDirection == "row") { - image.second.setSize(image.second.getSize().x, maxItemSize.y); - } - - // Apply overall container alignment. - if (mAlignment == "right") - x += (mSize.x - maxItemSize.x * grid.x - (grid.x - 1) * mItemMargin.x); - - // Store final item position. - image.second.setPosition(x, y); - - // Translate anchor. - if ((i++ + 1) % std::max(1, static_cast(mItemsPerLine)) != 0) { - // Translate on same line. - anchorX += (size.x + mItemMargin.x) * static_cast(directionLine.x); - anchorY += (size.y + mItemMargin.y) * static_cast(directionLine.y); + // The first item dictates the maximum width for the rest. + if (firstItem) { + maxItemSize.x = (item.baseImage.getSize() / std::max(sizeDiff.x, sizeDiff.y)).x; + sizeDiff = item.baseImage.getSize() / maxItemSize; + item.baseImage.setSize((item.baseImage.getSize() / std::max(sizeDiff.x, sizeDiff.y))); + firstItem = false; } else { - // Translate to first position of next line. - if (directionRow.x == 0) { - anchorY += size.y + mItemMargin.y; - anchorX = anchorXStart; + item.baseImage.setSize((item.baseImage.getSize() / std::max(sizeDiff.x, sizeDiff.y))); + } + + if (item.baseImage.getSize().y > rowHeight) + rowHeight = item.baseImage.getSize().y; + } + + // Update the maximum item height. + maxItemSize.y = 0.0f; + for (auto& item : mItems) { + if (!item.visible) + continue; + if (item.baseImage.getSize().y > maxItemSize.y) + maxItemSize.y = item.baseImage.getSize().y; + } + + maxItemSize = glm::round(maxItemSize); + + bool alignRight{mAlignment == "right" && mDirection == "row"}; + float alignRightComp{0.0f}; + + // If right-aligning, move the overall container contents during grid setup. + if (alignRight) + alignRightComp = + std::round(mSize.x - ((maxItemSize.x + mItemMargin.x) * grid.x) + mItemMargin.x); + + std::vector itemPositions; + + // Lay out the grid. + if (mDirection == "row") { + for (int y = 0; y < grid.y; y++) { + for (int x = 0; x < grid.x; x++) { + itemPositions.push_back( + glm::vec2{(x * (maxItemSize.x + mItemMargin.x) + alignRightComp), + y * (rowHeight + mItemMargin.y)}); } - else { - anchorX += size.x + mItemMargin.x; - anchorY = anchorYStart; + } + } + else { // Column mode. + for (int x = 0; x < grid.x; x++) { + for (int y = 0; y < grid.y; y++) { + itemPositions.push_back( + glm::vec2{(x * (maxItemSize.x + mItemMargin.x) + alignRightComp), + y * (rowHeight + mItemMargin.y)}); } } } - // Apply right-align to the images on the last row, if needed. - if (mAlignment == "right") { - std::vector imagesToAlign; - for (auto& image : mImages) { - if (!image.second.isVisible()) - continue; - // Only include images on the last row. - if (image.second.getPosition().y == anchorY) - imagesToAlign.push_back(&image.second); + int pos{0}; + float lastY{0.0f}; + float itemsOnLastRow{0}; + + // Position items on the grid. + for (auto& item : mItems) { + if (!item.visible) + continue; + + if (pos > 0) { + if (itemPositions[pos - 1].y < itemPositions[pos].y) { + lastY = itemPositions[pos].y; + itemsOnLastRow = 0; + } } - for (auto& moveImage : imagesToAlign) { - float offset = (maxItemSize.x + mItemMargin.x) * (grid.x - imagesToAlign.size()); - moveImage->setPosition(moveImage->getPosition().x + offset, moveImage->getPosition().y); + + float verticalOffset{0.0f}; + + // For any items that do not fill the maximum height, position these either on + // top/start (implicit), center or bottom/end. + if (item.baseImage.getSize().y < maxItemSize.y) { + if (mItemPlacement == "center") { + verticalOffset = std::floor((maxItemSize.y - item.baseImage.getSize().y) / 2.0f); + } + else if (mItemPlacement == "end") { + verticalOffset = maxItemSize.y - item.baseImage.getSize().y; + } + } + + item.baseImage.setPosition(itemPositions[pos].x, itemPositions[pos].y + verticalOffset, + 0.0f); + + // Optional overlay image. + if (item.overlayImage.getTexture() != nullptr) { + item.overlayImage.setResize(item.baseImage.getSize().x * mOverlaySize, 0.0f); + item.overlayImage.setPosition( + item.baseImage.getPosition().x + (item.baseImage.getSize().x * mOverlayPosition.x) - + item.overlayImage.getSize().x / 2.0f, + item.baseImage.getPosition().y + (item.baseImage.getSize().y * mOverlayPosition.y) - + item.overlayImage.getSize().y / 2.0f); + } + + // This rasterizes the SVG images so they look nice and smooth. + item.baseImage.setResize(item.baseImage.getSize()); + + itemsOnLastRow++; + pos++; + } + + // Apply right-align to the items (only works in row mode). + if (alignRight) { + for (auto& item : mItems) { + if (!item.visible) + continue; + glm::vec3 currPos{item.baseImage.getPosition()}; + if (currPos.y == lastY) { + const float offset{(grid.x - itemsOnLastRow) * (maxItemSize.x + mItemMargin.x)}; + item.baseImage.setPosition(currPos.x + offset, currPos.y, currPos.z); + if (item.overlayImage.getTexture() != nullptr) { + currPos = item.overlayImage.getPosition(); + item.overlayImage.setPosition(currPos.x + offset, currPos.y, currPos.z); + } + } } } diff --git a/es-core/src/components/FlexboxComponent.h b/es-core/src/components/FlexboxComponent.h index 41c24433e..4ab5ee5c3 100644 --- a/es-core/src/components/FlexboxComponent.h +++ b/es-core/src/components/FlexboxComponent.h @@ -10,12 +10,23 @@ #define ES_CORE_COMPONENTS_FLEXBOX_COMPONENT_H #include "GuiComponent.h" +#include "Window.h" #include "components/ImageComponent.h" class FlexboxComponent : public GuiComponent { public: - FlexboxComponent(Window* window, std::vector>& images); + struct FlexboxItem { + // Optional label, mostly a convenience for the calling class to track items. + std::string label; + // Main image that governs grid sizing and placement. + ImageComponent baseImage{nullptr}; + // Optional overlay image that can be sized and positioned relative to the base image. + ImageComponent overlayImage{nullptr}; + bool visible = false; + }; + + FlexboxComponent(Window* window, std::vector& items); // Getters/setters for the layout. std::string getDirection() const { return mDirection; } @@ -56,12 +67,13 @@ public: } glm::vec2 getItemMargin() const { return mItemMargin; } - void setItemMargin(glm::vec2 value) - { - mItemMargin.x = std::roundf(value.x * Renderer::getScreenWidth()); - mItemMargin.y = std::roundf(value.y * Renderer::getScreenHeight()); - mLayoutValid = false; - } + void setItemMargin(glm::vec2 value); + + glm::vec2 getOverlayPosition() const { return mOverlayPosition; } + void setOverlayPosition(glm::vec2 position) { mOverlayPosition = position; } + + float getOverlaySize() const { return mOverlaySize; } + void setOverlaySize(float size) { mOverlaySize = size; } void onSizeChanged() override { mLayoutValid = false; } void render(const glm::mat4& parentTrans) override; @@ -70,7 +82,7 @@ private: // Calculate flexbox layout. void computeLayout(); - std::vector>& mImages; + std::vector& mItems; // Layout options. std::string mDirection; @@ -80,6 +92,9 @@ private: std::string mItemPlacement; glm::vec2 mItemMargin; + glm::vec2 mOverlayPosition; + float mOverlaySize; + bool mLayoutValid; }; diff --git a/resources/graphics/badge_controller.svg b/resources/graphics/badge_controller.svg new file mode 100644 index 000000000..53faecd2f --- /dev/null +++ b/resources/graphics/badge_controller.svg @@ -0,0 +1,147 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/graphics/controllers/gamepad_generic.svg b/resources/graphics/controllers/gamepad_generic.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/gamepad_generic.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/gamepad_nintendo_64.svg b/resources/graphics/controllers/gamepad_nintendo_64.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/gamepad_nintendo_64.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/gamepad_nintendo_nes.svg b/resources/graphics/controllers/gamepad_nintendo_nes.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/gamepad_nintendo_nes.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/gamepad_nintendo_snes.svg b/resources/graphics/controllers/gamepad_nintendo_snes.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/gamepad_nintendo_snes.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/gamepad_playstation.svg b/resources/graphics/controllers/gamepad_playstation.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/gamepad_playstation.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/gamepad_xbox.svg b/resources/graphics/controllers/gamepad_xbox.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/gamepad_xbox.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/joycon_left_or_right_nintendo.svg b/resources/graphics/controllers/joycon_left_or_right_nintendo.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/joycon_left_or_right_nintendo.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/joycon_pair_nintendo.svg b/resources/graphics/controllers/joycon_pair_nintendo.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/joycon_pair_nintendo.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/joystick_arcade_2_buttons.svg b/resources/graphics/controllers/joystick_arcade_2_buttons.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/joystick_arcade_2_buttons.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/joystick_arcade_3_buttons.svg b/resources/graphics/controllers/joystick_arcade_3_buttons.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/joystick_arcade_3_buttons.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/joystick_arcade_4_buttons.svg b/resources/graphics/controllers/joystick_arcade_4_buttons.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/joystick_arcade_4_buttons.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/joystick_arcade_6_buttons.svg b/resources/graphics/controllers/joystick_arcade_6_buttons.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/joystick_arcade_6_buttons.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/joystick_generic.svg b/resources/graphics/controllers/joystick_generic.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/joystick_generic.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/keyboard_generic.svg b/resources/graphics/controllers/keyboard_generic.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/keyboard_generic.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/keyboard_mouse_generic.svg b/resources/graphics/controllers/keyboard_mouse_generic.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/keyboard_mouse_generic.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/lightgun_generic.svg b/resources/graphics/controllers/lightgun_generic.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/lightgun_generic.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/lightgun_nintendo.svg b/resources/graphics/controllers/lightgun_nintendo.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/lightgun_nintendo.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/mouse_amiga.svg b/resources/graphics/controllers/mouse_amiga.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/mouse_amiga.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/mouse_generic.svg b/resources/graphics/controllers/mouse_generic.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/mouse_generic.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/steering_wheel_generic.svg b/resources/graphics/controllers/steering_wheel_generic.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/steering_wheel_generic.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/trackball_generic.svg b/resources/graphics/controllers/trackball_generic.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/trackball_generic.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/unknown.svg b/resources/graphics/controllers/unknown.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/unknown.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/wii_remote_nintendo.svg b/resources/graphics/controllers/wii_remote_nintendo.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/wii_remote_nintendo.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/resources/graphics/controllers/wii_remote_nunchuck_nintendo.svg b/resources/graphics/controllers/wii_remote_nunchuck_nintendo.svg new file mode 100644 index 000000000..b6b01c15a --- /dev/null +++ b/resources/graphics/controllers/wii_remote_nunchuck_nintendo.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/themes/rbsimple-DE/theme.xml b/themes/rbsimple-DE/theme.xml index 690d7f0c4..130467fbc 100644 --- a/themes/rbsimple-DE/theme.xml +++ b/themes/rbsimple-DE/theme.xml @@ -237,14 +237,16 @@ based on: 'recalbox-multi' by the Recalbox community right - 0.815 0.675 + 0.880 0.757 0.13 0.1635 - 0 0 + 0.5 0.5 left 3 2 - 0.0028125 0.005 - favorite completed kidgame broken altemulator + 0.5 0.572 + 0.67 + -1.0 0.005 + favorite, completed, kidgame, broken, controller, altemulator From 9c8a5ca5d3ab0b8f049889b01508c601440a5c7f Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sat, 23 Oct 2021 19:30:58 +0200 Subject: [PATCH 27/60] Documentation update. --- CHANGELOG.md | 4 ++++ THEMES-DEV.md | 16 ++++++++++++---- USERGUIDE-DEV.md | 6 +++++- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f21da3ca2..9d802b212 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ * Populated the bundled es_systems.xml files with alternative emulator entries for most RetroArch cores * Added a virtual keyboard, partly based on code from batocera-emulationstation * Added badges that indicate favorite/completed/broken games as well as games suitable for children and those with a selected alternative emulator +* Added controller types, selectable via the metadata editor and displayed as a controller badge * 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 @@ -80,6 +81,7 @@ * 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 +* Fixed multiple issues with scaling of images which lead to various inconsistencies and sometimes cut-off graphics * Removing games from custom collections did not remove their filter index entries * Input consisting of only whitespace characters would get accepted by TextEditComponent which led to various strange behaviors * Leading and trailing whitespace characters would not get trimmed from the collection name when creating a new custom collection @@ -98,6 +100,8 @@ * 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 * When marking a game to not be counted in the metadata editor and the game was part of a custom collection, no collection disabling notification was displayed +* SliderComponent did not properly align the knob and bar vertically +* Resizing in SwitchComponent did not reposition the image properly leading to a non-centered image * Horizontal sizing of the TextComponent input field was not consistent across different screen resolutions * The sizing of the metadata editor was strange, which was clearly visible when activating the Ctrl+G debug mode * The "sortname" window header was incorrectly spelled when editing this type of entry in the metadata editor diff --git a/THEMES-DEV.md b/THEMES-DEV.md index e13aea2c4..6d078639e 100644 --- a/THEMES-DEV.md +++ b/THEMES-DEV.md @@ -910,6 +910,8 @@ ES-DE borrows the concept of "nine patches" from Android (or "9-Slices"). Curren #### badges +It's strongly recommended to use the same image dimensions for all badges as varying aspect ratios will lead to alignment issues. For the controller images it's recommended to keep to the square canvas size used by the default bundled graphics as otherwise sizing and placement will be inconsistent (unless all controller graphic files are customized of course). Overall it's a very good idea to keep the image dimensions small. This is especially true for SVG graphics as rasterization will otherwise take a long time which will slow down application startup and gamelist navigation. + * `pos` - type: NORMALIZED_PAIR. * `size` - type: NORMALIZED_PAIR. - Possible combinations: @@ -924,20 +926,26 @@ ES-DE borrows the concept of "nine patches" from Android (or "9-Slices"). Curren - Number of badges that fit on a row. When more badges are available a new row will be started. Default is `4`. * `rows` - type: FLOAT. - The number of rows available. Default is `2`. -* `itemPlacement` - type: STRING. - - Valid values are "top", "center", "bottom", or "stretch". Controls vertical alignment of each badge if images of different heights are used. "Stretch" will stretch the badge to the full height. Default is `center`. * `itemMargin` - type: NORMALIZED_PAIR. - The margins between badges. Possible combinations: - - `x y` - horizontal and vertical margins. Minimum value per axis is `0`, maximum value is `0.2`. Default is `0.01`. + - `x y` - horizontal and vertical margins. Minimum value per axis is `0`, maximum value is `0.2`. Default is `0.01 0.01`. If one of the axis is set to `-1` the margin of the other axis (in pixels) will be used, which makes it possible to get identical spacing between all items regardless of screen aspect ratio. * `slots` - type: STRING. - - The badge types that should be displayed. Should be specified as a list of strings separated by spaces. The order will be followed when placing badges on the screen. Available badges are: + - The badge types that should be displayed. Should be specified as a list of strings delimited by any characters of `\t\r\n ,` - that is, whitespace and commas. The order will be followed when placing badges on the screen. Available badges are: - `favorite`: Will be shown when the game is marked as favorite. - `completed`: Will be shown when the game is marked as completed. - `kidgame`: Will be shown when the game is marked as a kids game. - `broken`: Will be shown when the game is marked as broken. + - `controller`: Will be shown and overlaid by the corresponding controller icon if a controller type has been selected for the game using the metadata editor. - `altemulator`: Will be shown when an alternative emulator is setup for the game. +* `controllerPos` - type: NORMALIZED_PAIR. + - The position of the controller icon relative to the parent `controller` badge. Minimum value per axis is `-1.0`, maximum value is `2.0`. Default is `0.5 0.5` which centers the controller icon on the badge. +* `controllerSize` - type: FLOAT. + - The size of the controller icon relative to the parent `controller` badge. Minimum value is `0.1`, maximum value is `2.0`. Setting the value to `1.0` sizes the controller icon to the same width as the parent badge. The image aspect ratio is always maintained. * `customBadgeIcon` - type: PATH. - A badge icon override. Specify the badge type in the attribute `badge`. The available badges are the ones listed above. +* `customControllerIcon` - type: PATH. + - A controller icon override. Specify the controller type in the attribute `controller`. These are the available types: + - `gamepad_generic`, `gamepad_xbox`, `gamepad_playstation`, `gamepad_nintendo_nes`, `gamepad_nintendo_snes`, `gamepad_nintendo_64`, `joystick_generic`, `joystick_arcade_2_buttons`, `joystick_arcade_3_buttons`, `joystick_arcade_4_buttons`, `joystick_arcade_6_buttons`, `trackball_generic`, `lightgun_generic`, `lightgun_nintendo`, `keyboard_generic`, `mouse_generic`, `mouse_amiga`, `keyboard_mouse_generic`, `steering_wheel_generic`, `wii_remote_nintendo`, `wii_remote_nunchuck_nintendo`, `joycon_left_or_right_nintendo`, `joycon_pair_nintendo`, `unknown`. * `visible` - type: BOOLEAN. - If true, component will be rendered, otherwise rendering will be skipped. Can be used to hide elements from a particular view. * `zIndex` - type: FLOAT. diff --git a/USERGUIDE-DEV.md b/USERGUIDE-DEV.md index 6c944394a..7ce7db6eb 100644 --- a/USERGUIDE-DEV.md +++ b/USERGUIDE-DEV.md @@ -215,7 +215,7 @@ In addition to the styles just described, there is a **Grid** view style as well If the theme supports it, there's a gamelist information field displayed in the gamelist view, showing the number of games for the system (total and favorites) as well as a folder icon if a folder has been entered. When applying any filters to the gamelist, the game counter is replaced with the amount of games filtered, as in 'filtered / total games', e.g. '19 / 77'. If there are game entries in the filter result that are marked not to be counted as games, the number of such files will be indicated as 'filtered + filtered non-games / total games', for example '23 + 4 / 77' indicating 23 normal games, 4 non-games out of a total of 77. Due to this approach it's theoretically possible that the combined filtered game amount exceeds the number of counted games in the collection, for instance '69 + 11 / 77'. This is not considered a bug and is so by design. This gamelist information field functionality is specific to EmulationStation Desktop Edition so older themes will not support this. -Another feature which requires theme support is **Badges**, which is a set of icons displaying the status for various metadata fields. The currently supported badge types are _favorite, completed, kidgame, broken_ and _alternative emulator_. If any of the first four metadata fields have been set for a game, their corresponding badges will be displayed, and if an alternative emulator has been selected for the specific game, that badge will be shown. Setting an alternative emulator system-wide will not display this badge as it's only intended to indicate game-specific overrides. +Another feature which requires theme support is **Badges**, which is a set of icons displaying the status for various metadata fields. The currently supported badge types are _favorite, completed, kidgame, broken, controller_ and _alternative emulator_. If any of the first four metadata fields have been set for a game, their corresponding badges will be displayed. If a controller type has been selected for the game, the corresponding controller icon will be shown on the controller badge, and if an alternative emulator has been selected for the specific game, that badge will be shown. Setting an alternative emulator system-wide will not display this badge as it's only intended to indicate game-specific overrides. ![alt text](images/es-de_gamelist_view.png "ES-DE Gamelist View") _The **Gamelist view** is where you browse the games for a specific system._ @@ -1476,6 +1476,10 @@ This option will hide most metadata fields as well as any badges. The intention A statistics counter that tracks how many times you have played the game. You normally don't need to touch this, but if you want to, the possibility is there. +**Controller type** + +Contains a list of controller types that are built into ES-DE. The selected controller will be displayed as a badge if the current theme set support badges. This functionality is only cosmetic and will not affect the actual emulators. + **Alternative emulator** _(files only)_ If the option _Enable alternative emulators per game_ has been enabled, there will be an entry shown where you can select between alternative emulators for the specific game. There is a similar _Alternative emulators_ entry under the _Other settings_ menu, but that will apply the selection to the entire game system. If you select an alternative for a specific game using the metadata editor, that will take precedence and override any system-wide emulator selection (the currently selected system-wide emulator will be clearly marked on the selection screen). The alternative emulators need to be defined in the es_systems.xml file, and if there are no alternatives available for the current system, this row in the metadata editor will be grayed out. If you select an alternative emulator and later remove its corresponding entry from the es_systems.xml file, an error notice will be shown on this row. In this case you have the option to remove the invalid entry. But even if there is an invalid entry, games will still launch using the default emulator while logging a warning message to the es_log.txt file. Apart from this, the emulator selection should hopefully be self-explanatory. From b5a61a0617d9ec01b5818511f8c2fc851e09ddcb Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sat, 23 Oct 2021 20:14:17 +0200 Subject: [PATCH 28/60] Renamed 'controller types' to 'game controllers' (code) and simply 'controllers' (metadata editor). --- es-app/src/MetaData.cpp | 4 +- es-app/src/guis/GuiMetaDataEd.cpp | 16 +++--- es-app/src/guis/GuiMetaDataEd.h | 2 +- .../views/gamelist/DetailedGameListView.cpp | 2 +- .../src/views/gamelist/VideoGameListView.cpp | 2 +- es-core/src/components/BadgesComponent.cpp | 52 +++++++++---------- es-core/src/components/BadgesComponent.h | 16 +++--- 7 files changed, 47 insertions(+), 47 deletions(-) diff --git a/es-app/src/MetaData.cpp b/es-app/src/MetaData.cpp index 84f52233d..943099fcb 100644 --- a/es-app/src/MetaData.cpp +++ b/es-app/src/MetaData.cpp @@ -37,7 +37,7 @@ MetaDataDecl gameDecls[] = { {"nomultiscrape", MD_BOOL, "false", false, "exclude from multi-scraper", "enter no multi-scrape off/on", false}, {"hidemetadata", MD_BOOL, "false", false, "hide metadata fields", "enter hide metadata off/on", false}, {"playcount", MD_INT, "0", false, "times played", "enter number of times played", false}, -{"controller", MD_CONTROLLER, "", false, "controller type", "select controller type", false}, +{"controller", MD_CONTROLLER, "", false, "controller", "select controller", false}, {"altemulator", MD_ALT_EMULATOR, "", false, "alternative emulator", "select alternative emulator", false}, {"lastplayed", MD_TIME, "0", true, "last played", "enter last played date", false} }; @@ -58,7 +58,7 @@ MetaDataDecl folderDecls[] = { {"broken", MD_BOOL, "false", false, "broken/not working", "enter broken off/on", false}, {"nomultiscrape", MD_BOOL, "false", false, "exclude from multi-scraper", "enter no multi-scrape off/on", false}, {"hidemetadata", MD_BOOL, "false", false, "hide metadata fields", "enter hide metadata off/on", false}, -{"controller", MD_CONTROLLER, "", false, "controller type", "select controller type", false}, +{"controller", MD_CONTROLLER, "", false, "controller", "select controller", false}, {"lastplayed", MD_TIME, "0", true, "last played", "enter last played date", false} }; // clang-format on diff --git a/es-app/src/guis/GuiMetaDataEd.cpp b/es-app/src/guis/GuiMetaDataEd.cpp index 42d9c52a9..24e755f6a 100644 --- a/es-app/src/guis/GuiMetaDataEd.cpp +++ b/es-app/src/guis/GuiMetaDataEd.cpp @@ -53,11 +53,11 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window, , mMediaFilesUpdated{false} , mInvalidEmulatorEntry{false} { - mControllerTypes = BadgesComponent::getControllerTypes(); + mGameControllers = BadgesComponent::getGameControllers(); // Remove the last "unknown" controller entry. - if (mControllerTypes.size() > 1) - mControllerTypes.pop_back(); + if (mGameControllers.size() > 1) + mGameControllers.pop_back(); addChild(&mBackground); addChild(&mGrid); @@ -210,7 +210,7 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window, originalValue] { GuiSettings* s = new GuiSettings(mWindow, title); - for (auto controllerType : mControllerTypes) { + for (auto controller : mGameControllers) { std::string selectedLabel = ed->getValue(); std::string label; ComponentListRow row; @@ -218,14 +218,14 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window, std::shared_ptr labelText = std::make_shared( mWindow, label, Font::get(FONT_SIZE_MEDIUM), 0x777777FF); labelText->setSelectable(true); - labelText->setValue(controllerType.displayName); + labelText->setValue(controller.displayName); - label = controllerType.displayName; + label = controller.displayName; row.addElement(labelText, true); - row.makeAcceptInputHandler([s, updateVal, controllerType] { - updateVal(controllerType.displayName); + row.makeAcceptInputHandler([s, updateVal, controller] { + updateVal(controller.displayName); delete s; }); diff --git a/es-app/src/guis/GuiMetaDataEd.h b/es-app/src/guis/GuiMetaDataEd.h index e73714e8d..054c112f3 100644 --- a/es-app/src/guis/GuiMetaDataEd.h +++ b/es-app/src/guis/GuiMetaDataEd.h @@ -61,7 +61,7 @@ private: ScraperSearchParams mScraperParams; - std::vector mControllerTypes; + std::vector mGameControllers; std::vector> mEditors; std::vector mMetaDataDecl; diff --git a/es-app/src/views/gamelist/DetailedGameListView.cpp b/es-app/src/views/gamelist/DetailedGameListView.cpp index a47f65f98..9fc1d6805 100644 --- a/es-app/src/views/gamelist/DetailedGameListView.cpp +++ b/es-app/src/views/gamelist/DetailedGameListView.cpp @@ -416,7 +416,7 @@ void DetailedGameListView::updateInfoPanel() badgeInfo.badgeType = badge; if (badge == "controller") { if (file->metadata.get("controller").compare("") != 0) { - badgeInfo.controllerType = file->metadata.get("controller"); + badgeInfo.gameController = file->metadata.get("controller"); badgeSlots.push_back(badgeInfo); } } diff --git a/es-app/src/views/gamelist/VideoGameListView.cpp b/es-app/src/views/gamelist/VideoGameListView.cpp index 699c253f8..8965def88 100644 --- a/es-app/src/views/gamelist/VideoGameListView.cpp +++ b/es-app/src/views/gamelist/VideoGameListView.cpp @@ -456,7 +456,7 @@ void VideoGameListView::updateInfoPanel() badgeInfo.badgeType = badge; if (badge == "controller") { if (file->metadata.get("controller").compare("") != 0) { - badgeInfo.controllerType = file->metadata.get("controller"); + badgeInfo.gameController = file->metadata.get("controller"); badgeSlots.push_back(badgeInfo); } } diff --git a/es-core/src/components/BadgesComponent.cpp b/es-core/src/components/BadgesComponent.cpp index b932fddea..8236593a0 100644 --- a/es-core/src/components/BadgesComponent.cpp +++ b/es-core/src/components/BadgesComponent.cpp @@ -20,12 +20,12 @@ #include "ThemeData.h" #include "utils/StringUtil.h" -std::vector BadgesComponent::sControllerTypes; +std::vector BadgesComponent::sGameControllers; // clang-format off // The "unknown" controller entry has to be placed last. -ControllerTypes sControllerDefinitions [] = { +GameControllers sControllerDefinitions [] = { // shortName displayName fileName {"gamepad_generic", "Gamepad (Generic)", ":/graphics/controllers/gamepad_generic.svg"}, {"gamepad_xbox", "Gamepad (Xbox)", ":/graphics/controllers/gamepad_xbox.svg"}, @@ -70,11 +70,11 @@ BadgesComponent::BadgesComponent(Window* window) mBadgeIcons[SLOT_ALTEMULATOR] = ":/graphics/badge_altemulator.svg"; } -void BadgesComponent::populateControllerTypes() +void BadgesComponent::populateGameControllers() { - sControllerTypes.clear(); - for (auto type : sControllerDefinitions) - sControllerTypes.push_back(type); + sGameControllers.clear(); + for (auto controller : sControllerDefinitions) + sGameControllers.push_back(controller); } void BadgesComponent::setBadges(const std::vector& badges) @@ -97,19 +97,19 @@ void BadgesComponent::setBadges(const std::vector& badges) if (it != mFlexboxItems.end()) { it->visible = true; - if (badge.controllerType != "" && - badge.controllerType != it->overlayImage.getTexture()->getTextureFilePath()) { + if (badge.gameController != "" && + badge.gameController != it->overlayImage.getTexture()->getTextureFilePath()) { - auto it2 = std::find_if(sControllerTypes.begin(), sControllerTypes.end(), - [badge](ControllerTypes controllerType) { - return controllerType.shortName == badge.controllerType; + auto it2 = std::find_if(sGameControllers.begin(), sGameControllers.end(), + [badge](GameControllers gameController) { + return gameController.shortName == badge.gameController; }); - if (it2 != sControllerTypes.cend()) { + if (it2 != sGameControllers.cend()) { it->overlayImage.setImage((*it2).fileName); } - else if (badge.controllerType != "") - it->overlayImage.setImage(sControllerTypes.back().fileName); + else if (badge.gameController != "") + it->overlayImage.setImage(sGameControllers.back().fileName); } } } @@ -126,11 +126,11 @@ void BadgesComponent::setBadges(const std::vector& badges) const std::string BadgesComponent::getShortName(const std::string& displayName) { - auto it = std::find_if(sControllerTypes.begin(), sControllerTypes.end(), - [displayName](ControllerTypes controllerType) { - return controllerType.displayName == displayName; + auto it = std::find_if(sGameControllers.begin(), sGameControllers.end(), + [displayName](GameControllers gameController) { + return gameController.displayName == displayName; }); - if (it != sControllerTypes.end()) + if (it != sGameControllers.end()) return (*it).shortName; else return "unknown"; @@ -138,11 +138,11 @@ const std::string BadgesComponent::getShortName(const std::string& displayName) const std::string BadgesComponent::getDisplayName(const std::string& shortName) { - auto it = std::find_if(sControllerTypes.begin(), sControllerTypes.end(), - [shortName](ControllerTypes controllerType) { - return controllerType.shortName == shortName; + auto it = std::find_if(sGameControllers.begin(), sGameControllers.end(), + [shortName](GameControllers gameController) { + return gameController.shortName == shortName; }); - if (it != sControllerTypes.end()) + if (it != sGameControllers.end()) return (*it).displayName; else return "unknown"; @@ -168,7 +168,7 @@ void BadgesComponent::applyTheme(const std::shared_ptr& theme, const std::string& element, unsigned int properties) { - populateControllerTypes(); + populateGameControllers(); using namespace ThemeFlags; @@ -280,9 +280,9 @@ void BadgesComponent::applyTheme(const std::shared_ptr& theme, } } - for (auto& controllerType : sControllerTypes) { - if (properties & PATH && elem->has(controllerType.shortName)) - controllerType.fileName = elem->get(controllerType.shortName); + for (auto& gameController : sGameControllers) { + if (properties & PATH && elem->has(gameController.shortName)) + gameController.fileName = elem->get(gameController.shortName); } GuiComponent::applyTheme(theme, view, element, properties); diff --git a/es-core/src/components/BadgesComponent.h b/es-core/src/components/BadgesComponent.h index a064966eb..a87c34e2b 100644 --- a/es-core/src/components/BadgesComponent.h +++ b/es-core/src/components/BadgesComponent.h @@ -13,7 +13,7 @@ #include "FlexboxComponent.h" #include "GuiComponent.h" -struct ControllerTypes { +struct GameControllers { std::string shortName; std::string displayName; std::string fileName; @@ -26,17 +26,17 @@ public: struct BadgeInfo { std::string badgeType; - std::string controllerType; + std::string gameController; }; - static void populateControllerTypes(); + static void populateGameControllers(); std::vector getBadgeTypes() { return mBadgeTypes; } void setBadges(const std::vector& badges); - static const std::vector& getControllerTypes() + static const std::vector& getGameControllers() { - if (sControllerTypes.empty()) - populateControllerTypes(); - return sControllerTypes; + if (sGameControllers.empty()) + populateGameControllers(); + return sGameControllers; } static const std::string getShortName(const std::string& displayName); @@ -51,7 +51,7 @@ public: unsigned int properties) override; private: - static std::vector sControllerTypes; + static std::vector sGameControllers; std::vector mFlexboxItems; FlexboxComponent mFlexboxComponent; From 0351a2ccdbfc1ba3185bf5e545ad3802a2073c20 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sat, 23 Oct 2021 20:15:45 +0200 Subject: [PATCH 29/60] Small documentation update for the game controller functionality. --- CHANGELOG.md | 2 +- USERGUIDE-DEV.md | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d802b212..3069bc8f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ * Populated the bundled es_systems.xml files with alternative emulator entries for most RetroArch cores * Added a virtual keyboard, partly based on code from batocera-emulationstation * Added badges that indicate favorite/completed/broken games as well as games suitable for children and those with a selected alternative emulator -* Added controller types, selectable via the metadata editor and displayed as a controller badge +* Added game-specific controller images that are selectable via the metadata editor and displayed as a controller badge * 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 diff --git a/USERGUIDE-DEV.md b/USERGUIDE-DEV.md index 7ce7db6eb..483fb7a3b 100644 --- a/USERGUIDE-DEV.md +++ b/USERGUIDE-DEV.md @@ -215,7 +215,7 @@ In addition to the styles just described, there is a **Grid** view style as well If the theme supports it, there's a gamelist information field displayed in the gamelist view, showing the number of games for the system (total and favorites) as well as a folder icon if a folder has been entered. When applying any filters to the gamelist, the game counter is replaced with the amount of games filtered, as in 'filtered / total games', e.g. '19 / 77'. If there are game entries in the filter result that are marked not to be counted as games, the number of such files will be indicated as 'filtered + filtered non-games / total games', for example '23 + 4 / 77' indicating 23 normal games, 4 non-games out of a total of 77. Due to this approach it's theoretically possible that the combined filtered game amount exceeds the number of counted games in the collection, for instance '69 + 11 / 77'. This is not considered a bug and is so by design. This gamelist information field functionality is specific to EmulationStation Desktop Edition so older themes will not support this. -Another feature which requires theme support is **Badges**, which is a set of icons displaying the status for various metadata fields. The currently supported badge types are _favorite, completed, kidgame, broken, controller_ and _alternative emulator_. If any of the first four metadata fields have been set for a game, their corresponding badges will be displayed. If a controller type has been selected for the game, the corresponding controller icon will be shown on the controller badge, and if an alternative emulator has been selected for the specific game, that badge will be shown. Setting an alternative emulator system-wide will not display this badge as it's only intended to indicate game-specific overrides. +Another feature which requires theme support is **Badges**, which is a set of icons displaying the status for various metadata fields. The currently supported badge types are _favorite, completed, kidgame, broken, controller_ and _alternative emulator_. If any of the first four metadata fields have been set for a game, their corresponding badges will be displayed. If a game-specific controller has been selected, the corresponding controller icon will be shown on the controller badge, and if an alternative emulator has been selected for the specific game, that badge will be shown. Setting an alternative emulator system-wide will not display this badge as it's only intended to indicate game-specific overrides. ![alt text](images/es-de_gamelist_view.png "ES-DE Gamelist View") _The **Gamelist view** is where you browse the games for a specific system._ @@ -1476,9 +1476,9 @@ This option will hide most metadata fields as well as any badges. The intention A statistics counter that tracks how many times you have played the game. You normally don't need to touch this, but if you want to, the possibility is there. -**Controller type** +**Controller** -Contains a list of controller types that are built into ES-DE. The selected controller will be displayed as a badge if the current theme set support badges. This functionality is only cosmetic and will not affect the actual emulators. +Contains a list of controller images that are built into ES-DE. The selected controller will be displayed as a badge if the current theme set support badges. This functionality is only cosmetic and will not affect the actual emulators, and it will not affect the controller input for ES-DE itself. **Alternative emulator** _(files only)_ From 1aa360598dd7cce846055e96c38d036c52728f02 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sat, 23 Oct 2021 20:28:07 +0200 Subject: [PATCH 30/60] Renamed BadgesComponent to BadgeComponent. --- es-app/src/guis/GuiMetaDataEd.cpp | 10 ++--- es-app/src/guis/GuiMetaDataEd.h | 2 +- .../views/gamelist/DetailedGameListView.cpp | 4 +- .../src/views/gamelist/DetailedGameListView.h | 4 +- es-app/src/views/gamelist/GridGameListView.h | 4 +- .../src/views/gamelist/VideoGameListView.cpp | 4 +- es-app/src/views/gamelist/VideoGameListView.h | 4 +- es-core/CMakeLists.txt | 4 +- ...BadgesComponent.cpp => BadgeComponent.cpp} | 41 +++++++++---------- .../{BadgesComponent.h => BadgeComponent.h} | 12 +++--- 10 files changed, 44 insertions(+), 45 deletions(-) rename es-core/src/components/{BadgesComponent.cpp => BadgeComponent.cpp} (89%) rename es-core/src/components/{BadgesComponent.h => BadgeComponent.h} (86%) diff --git a/es-app/src/guis/GuiMetaDataEd.cpp b/es-app/src/guis/GuiMetaDataEd.cpp index 24e755f6a..e46c1bb62 100644 --- a/es-app/src/guis/GuiMetaDataEd.cpp +++ b/es-app/src/guis/GuiMetaDataEd.cpp @@ -53,7 +53,7 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window, , mMediaFilesUpdated{false} , mInvalidEmulatorEntry{false} { - mGameControllers = BadgesComponent::getGameControllers(); + mGameControllers = BadgeComponent::getGameControllers(); // Remove the last "unknown" controller entry. if (mGameControllers.size() > 1) @@ -200,7 +200,7 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window, // OK callback (apply new value to ed). auto updateVal = [ed, originalValue](const std::string& newVal) { ed->setValue(newVal); - if (newVal == BadgesComponent::getDisplayName(originalValue)) + if (newVal == BadgeComponent::getDisplayName(originalValue)) ed->setColor(DEFAULT_TEXTCOLOR); else ed->setColor(TEXTCOLOR_USERMARKED); @@ -478,7 +478,7 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window, ed->setValue(ViewController::EXCLAMATION_CHAR + " " + originalValue); } else if (iter->type == MD_CONTROLLER && mMetaData->get(iter->key) != "") { - std::string displayName = BadgesComponent::getDisplayName(mMetaData->get(iter->key)); + std::string displayName = BadgeComponent::getDisplayName(mMetaData->get(iter->key)); if (displayName != "unknown") ed->setValue(displayName); else @@ -618,7 +618,7 @@ void GuiMetaDataEd::save() continue; if (mMetaDataDecl.at(i).key == "controller" && mEditors.at(i)->getValue() != "") { - std::string shortName = BadgesComponent::getShortName(mEditors.at(i)->getValue()); + std::string shortName = BadgeComponent::getShortName(mEditors.at(i)->getValue()); if (shortName != "unknown") mMetaData->set(mMetaDataDecl.at(i).key, shortName); continue; @@ -769,7 +769,7 @@ void GuiMetaDataEd::close() continue; if (mMetaDataDecl.at(i).key == "controller" && mEditors.at(i)->getValue() != "") { - std::string shortName = BadgesComponent::getShortName(mEditors.at(i)->getValue()); + std::string shortName = BadgeComponent::getShortName(mEditors.at(i)->getValue()); if (shortName == "unknown" || mMetaDataValue == shortName) continue; } diff --git a/es-app/src/guis/GuiMetaDataEd.h b/es-app/src/guis/GuiMetaDataEd.h index 054c112f3..a4833c257 100644 --- a/es-app/src/guis/GuiMetaDataEd.h +++ b/es-app/src/guis/GuiMetaDataEd.h @@ -13,7 +13,7 @@ #include "GuiComponent.h" #include "MetaData.h" -#include "components/BadgesComponent.h" +#include "components/BadgeComponent.h" #include "components/ComponentGrid.h" #include "components/NinePatchComponent.h" #include "components/ScrollIndicatorComponent.h" diff --git a/es-app/src/views/gamelist/DetailedGameListView.cpp b/es-app/src/views/gamelist/DetailedGameListView.cpp index 9fc1d6805..5a0846ff0 100644 --- a/es-app/src/views/gamelist/DetailedGameListView.cpp +++ b/es-app/src/views/gamelist/DetailedGameListView.cpp @@ -410,9 +410,9 @@ void DetailedGameListView::updateInfoPanel() mPlayers.setValue(file->metadata.get("players")); // Populate the badge slots based on game metadata. - std::vector badgeSlots; + std::vector badgeSlots; for (auto badge : mBadges.getBadgeTypes()) { - BadgesComponent::BadgeInfo badgeInfo; + BadgeComponent::BadgeInfo badgeInfo; badgeInfo.badgeType = badge; if (badge == "controller") { if (file->metadata.get("controller").compare("") != 0) { diff --git a/es-app/src/views/gamelist/DetailedGameListView.h b/es-app/src/views/gamelist/DetailedGameListView.h index a6033b2f6..ed4db80c2 100644 --- a/es-app/src/views/gamelist/DetailedGameListView.h +++ b/es-app/src/views/gamelist/DetailedGameListView.h @@ -9,7 +9,7 @@ #ifndef ES_APP_VIEWS_GAME_LIST_DETAILED_GAME_LIST_VIEW_H #define ES_APP_VIEWS_GAME_LIST_DETAILED_GAME_LIST_VIEW_H -#include "components/BadgesComponent.h" +#include "components/BadgeComponent.h" #include "components/DateTimeComponent.h" #include "components/RatingComponent.h" #include "components/ScrollableContainer.h" @@ -56,7 +56,7 @@ private: DateTimeComponent mLastPlayed; TextComponent mPlayCount; TextComponent mName; - BadgesComponent mBadges; + BadgeComponent mBadges; std::vector getMDLabels(); std::vector getMDValues(); diff --git a/es-app/src/views/gamelist/GridGameListView.h b/es-app/src/views/gamelist/GridGameListView.h index 2c2f50458..f84d52cf7 100644 --- a/es-app/src/views/gamelist/GridGameListView.h +++ b/es-app/src/views/gamelist/GridGameListView.h @@ -9,7 +9,7 @@ #ifndef ES_APP_VIEWS_GAME_LIST_GRID_GAME_LIST_VIEW_H #define ES_APP_VIEWS_GAME_LIST_GRID_GAME_LIST_VIEW_H -#include "components/BadgesComponent.h" +#include "components/BadgeComponent.h" #include "components/DateTimeComponent.h" #include "components/ImageGridComponent.h" #include "components/RatingComponent.h" @@ -89,7 +89,7 @@ private: TextComponent mLblLastPlayed; TextComponent mLblPlayCount; - BadgesComponent mBadges; + BadgeComponent mBadges; RatingComponent mRating; DateTimeComponent mReleaseDate; TextComponent mDeveloper; diff --git a/es-app/src/views/gamelist/VideoGameListView.cpp b/es-app/src/views/gamelist/VideoGameListView.cpp index 8965def88..ef76d29e0 100644 --- a/es-app/src/views/gamelist/VideoGameListView.cpp +++ b/es-app/src/views/gamelist/VideoGameListView.cpp @@ -450,9 +450,9 @@ void VideoGameListView::updateInfoPanel() mPlayers.setValue(file->metadata.get("players")); // Populate the badge slots based on game metadata. - std::vector badgeSlots; + std::vector badgeSlots; for (auto badge : mBadges.getBadgeTypes()) { - BadgesComponent::BadgeInfo badgeInfo; + BadgeComponent::BadgeInfo badgeInfo; badgeInfo.badgeType = badge; if (badge == "controller") { if (file->metadata.get("controller").compare("") != 0) { diff --git a/es-app/src/views/gamelist/VideoGameListView.h b/es-app/src/views/gamelist/VideoGameListView.h index f5cae00d0..06d1c1034 100644 --- a/es-app/src/views/gamelist/VideoGameListView.h +++ b/es-app/src/views/gamelist/VideoGameListView.h @@ -9,7 +9,7 @@ #ifndef ES_APP_VIEWS_GAME_LIST_VIDEO_GAME_LIST_VIEW_H #define ES_APP_VIEWS_GAME_LIST_VIDEO_GAME_LIST_VIEW_H -#include "components/BadgesComponent.h" +#include "components/BadgeComponent.h" #include "components/DateTimeComponent.h" #include "components/RatingComponent.h" #include "components/ScrollableContainer.h" @@ -60,7 +60,7 @@ private: DateTimeComponent mLastPlayed; TextComponent mPlayCount; TextComponent mName; - BadgesComponent mBadges; + BadgeComponent mBadges; std::vector getMDLabels(); std::vector getMDValues(); diff --git a/es-core/CMakeLists.txt b/es-core/CMakeLists.txt index 6949f2a32..d579317c6 100644 --- a/es-core/CMakeLists.txt +++ b/es-core/CMakeLists.txt @@ -34,7 +34,7 @@ set(CORE_HEADERS # GUI components ${CMAKE_CURRENT_SOURCE_DIR}/src/components/AnimatedImageComponent.h - ${CMAKE_CURRENT_SOURCE_DIR}/src/components/BadgesComponent.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/components/BadgeComponent.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/BusyComponent.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/ButtonComponent.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/ComponentGrid.h @@ -112,7 +112,7 @@ set(CORE_SOURCES # GUI components ${CMAKE_CURRENT_SOURCE_DIR}/src/components/AnimatedImageComponent.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/components/BadgesComponent.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/components/BadgeComponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/BusyComponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/ButtonComponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/ComponentGrid.cpp diff --git a/es-core/src/components/BadgesComponent.cpp b/es-core/src/components/BadgeComponent.cpp similarity index 89% rename from es-core/src/components/BadgesComponent.cpp rename to es-core/src/components/BadgeComponent.cpp index 8236593a0..5402b62a9 100644 --- a/es-core/src/components/BadgesComponent.cpp +++ b/es-core/src/components/BadgeComponent.cpp @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT // // EmulationStation Desktop Edition -// BadgesComponent.cpp +// BadgeComponent.cpp // // Game badges icons. // Used by the gamelist views. @@ -14,13 +14,13 @@ #define SLOT_CONTROLLER "controller" #define SLOT_ALTEMULATOR "altemulator" -#include "components/BadgesComponent.h" +#include "components/BadgeComponent.h" #include "Log.h" #include "ThemeData.h" #include "utils/StringUtil.h" -std::vector BadgesComponent::sGameControllers; +std::vector BadgeComponent::sGameControllers; // clang-format off @@ -55,7 +55,7 @@ GameControllers sControllerDefinitions [] = { // clang-format on -BadgesComponent::BadgesComponent(Window* window) +BadgeComponent::BadgeComponent(Window* window) : GuiComponent{window} , mFlexboxItems{} , mFlexboxComponent{window, mFlexboxItems} @@ -70,14 +70,14 @@ BadgesComponent::BadgesComponent(Window* window) mBadgeIcons[SLOT_ALTEMULATOR] = ":/graphics/badge_altemulator.svg"; } -void BadgesComponent::populateGameControllers() +void BadgeComponent::populateGameControllers() { sGameControllers.clear(); for (auto controller : sControllerDefinitions) sGameControllers.push_back(controller); } -void BadgesComponent::setBadges(const std::vector& badges) +void BadgeComponent::setBadges(const std::vector& badges) { std::map prevVisibility; std::map prevPlayers; @@ -124,7 +124,7 @@ void BadgesComponent::setBadges(const std::vector& badges) } } -const std::string BadgesComponent::getShortName(const std::string& displayName) +const std::string BadgeComponent::getShortName(const std::string& displayName) { auto it = std::find_if(sGameControllers.begin(), sGameControllers.end(), [displayName](GameControllers gameController) { @@ -136,7 +136,7 @@ const std::string BadgesComponent::getShortName(const std::string& displayName) return "unknown"; } -const std::string BadgesComponent::getDisplayName(const std::string& shortName) +const std::string BadgeComponent::getDisplayName(const std::string& shortName) { auto it = std::find_if(sGameControllers.begin(), sGameControllers.end(), [shortName](GameControllers gameController) { @@ -148,7 +148,7 @@ const std::string BadgesComponent::getDisplayName(const std::string& shortName) return "unknown"; } -void BadgesComponent::render(const glm::mat4& parentTrans) +void BadgeComponent::render(const glm::mat4& parentTrans) { if (!isVisible()) return; @@ -163,10 +163,10 @@ void BadgesComponent::render(const glm::mat4& parentTrans) } } -void BadgesComponent::applyTheme(const std::shared_ptr& theme, - const std::string& view, - const std::string& element, - unsigned int properties) +void BadgeComponent::applyTheme(const std::shared_ptr& theme, + const std::string& view, + const std::string& element, + unsigned int properties) { populateGameControllers(); @@ -179,7 +179,7 @@ void BadgesComponent::applyTheme(const std::shared_ptr& theme, if (elem->has("alignment")) { const std::string alignment{elem->get("alignment")}; if (alignment != "left" && alignment != "right") { - LOG(LogWarning) << "BadgesComponent: Invalid theme configuration, set to \"" + LOG(LogWarning) << "BadgeComponent: Invalid theme configuration, set to \"" << alignment << "\""; } else { @@ -191,7 +191,7 @@ void BadgesComponent::applyTheme(const std::shared_ptr& theme, const float itemsPerRow{elem->get("itemsPerRow")}; if (itemsPerRow < 1.0f || itemsPerRow > 10.0f) { LOG(LogWarning) - << "BadgesComponent: Invalid theme configuration, set to \"" + << "BadgeComponent: Invalid theme configuration, set to \"" << itemsPerRow << "\""; } else { @@ -202,7 +202,7 @@ void BadgesComponent::applyTheme(const std::shared_ptr& theme, if (elem->has("rows")) { const float rows{elem->get("rows")}; if (rows < 1.0f || rows > 10.0f) { - LOG(LogWarning) << "BadgesComponent: Invalid theme configuration, set to \"" + LOG(LogWarning) << "BadgeComponent: Invalid theme configuration, set to \"" << rows << "\""; } else { @@ -215,9 +215,8 @@ void BadgesComponent::applyTheme(const std::shared_ptr& theme, if ((itemMargin.x != -1.0 && itemMargin.y != -1.0) && (itemMargin.x < 0.0f || itemMargin.x > 0.2f || itemMargin.y < 0.0f || itemMargin.y > 0.2f)) { - LOG(LogWarning) - << "BadgesComponent: Invalid theme configuration, set to \"" - << itemMargin.x << " " << itemMargin.y << "\""; + LOG(LogWarning) << "BadgeComponent: Invalid theme configuration, set to \"" + << itemMargin.x << " " << itemMargin.y << "\""; } else { mFlexboxComponent.setItemMargin(itemMargin); @@ -229,7 +228,7 @@ void BadgesComponent::applyTheme(const std::shared_ptr& theme, if (controllerPos.x < -1.0f || controllerPos.x > 2.0f || controllerPos.y < -1.0f || controllerPos.y > 2.0f) { LOG(LogWarning) - << "BadgesComponent: Invalid theme configuration, set to \"" + << "BadgeComponent: Invalid theme configuration, set to \"" << controllerPos.x << " " << controllerPos.y << "\""; } else { @@ -241,7 +240,7 @@ void BadgesComponent::applyTheme(const std::shared_ptr& theme, const float controllerSize = elem->get("controllerSize"); if (controllerSize < 0.1f || controllerSize > 2.0f) { LOG(LogWarning) - << "BadgesComponent: Invalid theme configuration, set to \"" + << "BadgeComponent: Invalid theme configuration, set to \"" << controllerSize << "\""; } else { diff --git a/es-core/src/components/BadgesComponent.h b/es-core/src/components/BadgeComponent.h similarity index 86% rename from es-core/src/components/BadgesComponent.h rename to es-core/src/components/BadgeComponent.h index a87c34e2b..2f998b9da 100644 --- a/es-core/src/components/BadgesComponent.h +++ b/es-core/src/components/BadgeComponent.h @@ -1,14 +1,14 @@ // SPDX-License-Identifier: MIT // // EmulationStation Desktop Edition -// BadgesComponent.h +// BadgeComponent.h // // Game badges icons. // Used by the gamelist views. // -#ifndef ES_CORE_COMPONENTS_BADGES_COMPONENT_H -#define ES_CORE_COMPONENTS_BADGES_COMPONENT_H +#ifndef ES_CORE_COMPONENTS_BADGE_COMPONENT_H +#define ES_CORE_COMPONENTS_BADGE_COMPONENT_H #include "FlexboxComponent.h" #include "GuiComponent.h" @@ -19,10 +19,10 @@ struct GameControllers { std::string fileName; }; -class BadgesComponent : public GuiComponent +class BadgeComponent : public GuiComponent { public: - BadgesComponent(Window* window); + BadgeComponent(Window* window); struct BadgeInfo { std::string badgeType; @@ -60,4 +60,4 @@ private: std::map mBadgeIcons; }; -#endif // ES_CORE_COMPONENTS_BADGES_COMPONENT_H +#endif // ES_CORE_COMPONENTS_BADGE_COMPONENT_H From f2c787e7696cbb41e238cd21e60264794fe98cd8 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sat, 23 Oct 2021 20:49:00 +0200 Subject: [PATCH 31/60] Moved the resources/help directory to resources/graphics/help --- es-core/src/components/HelpComponent.cpp | 78 +++++++-------- es-core/src/guis/GuiInputConfig.cpp | 96 +++++++++---------- resources/{ => graphics}/help/button_1.svg | 0 resources/{ => graphics}/help/button_2.svg | 0 resources/{ => graphics}/help/button_3.svg | 0 resources/{ => graphics}/help/button_4.svg | 0 resources/{ => graphics}/help/button_a_PS.svg | 0 .../{ => graphics}/help/button_a_SNES.svg | 0 .../{ => graphics}/help/button_a_XBOX.svg | 0 resources/{ => graphics}/help/button_b_PS.svg | 0 .../{ => graphics}/help/button_b_SNES.svg | 0 .../{ => graphics}/help/button_b_XBOX.svg | 0 .../{ => graphics}/help/button_back_PS4.svg | 0 .../{ => graphics}/help/button_back_PS5.svg | 0 .../{ => graphics}/help/button_back_SNES.svg | 0 .../{ => graphics}/help/button_back_XBOX.svg | 0 .../help/button_back_XBOX360.svg | 0 .../{ => graphics}/help/button_hotkey.svg | 0 resources/{ => graphics}/help/button_l.svg | 0 resources/{ => graphics}/help/button_lr.svg | 0 resources/{ => graphics}/help/button_lt.svg | 0 resources/{ => graphics}/help/button_r.svg | 0 resources/{ => graphics}/help/button_rt.svg | 0 .../{ => graphics}/help/button_start_PS4.svg | 0 .../{ => graphics}/help/button_start_PS5.svg | 0 .../{ => graphics}/help/button_start_SNES.svg | 0 .../{ => graphics}/help/button_start_XBOX.svg | 0 .../help/button_start_XBOX360.svg | 0 resources/{ => graphics}/help/button_x_PS.svg | 0 .../{ => graphics}/help/button_x_SNES.svg | 0 .../{ => graphics}/help/button_x_XBOX.svg | 0 resources/{ => graphics}/help/button_y_PS.svg | 0 .../{ => graphics}/help/button_y_SNES.svg | 0 .../{ => graphics}/help/button_y_XBOX.svg | 0 resources/{ => graphics}/help/dpad_all.svg | 0 resources/{ => graphics}/help/dpad_down.svg | 0 resources/{ => graphics}/help/dpad_left.svg | 0 .../{ => graphics}/help/dpad_leftright.svg | 0 resources/{ => graphics}/help/dpad_right.svg | 0 resources/{ => graphics}/help/dpad_up.svg | 0 resources/{ => graphics}/help/dpad_updown.svg | 0 .../{ => graphics}/help/mbuttons_a_PS.svg | 0 .../{ => graphics}/help/mbuttons_a_SNES.svg | 0 .../{ => graphics}/help/mbuttons_a_XBOX.svg | 0 .../{ => graphics}/help/mbuttons_b_PS.svg | 0 .../{ => graphics}/help/mbuttons_b_SNES.svg | 0 .../{ => graphics}/help/mbuttons_b_XBOX.svg | 0 .../{ => graphics}/help/mbuttons_x_PS.svg | 0 .../{ => graphics}/help/mbuttons_x_SNES.svg | 0 .../{ => graphics}/help/mbuttons_x_XBOX.svg | 0 .../{ => graphics}/help/mbuttons_y_PS.svg | 0 .../{ => graphics}/help/mbuttons_y_SNES.svg | 0 .../{ => graphics}/help/mbuttons_y_XBOX.svg | 0 resources/{ => graphics}/help/thumbstick.svg | 0 .../{ => graphics}/help/thumbstick_click.svg | 0 .../{ => graphics}/help/thumbstick_down.svg | 0 .../{ => graphics}/help/thumbstick_left.svg | 0 .../{ => graphics}/help/thumbstick_right.svg | 0 .../{ => graphics}/help/thumbstick_up.svg | 0 59 files changed, 87 insertions(+), 87 deletions(-) rename resources/{ => graphics}/help/button_1.svg (100%) rename resources/{ => graphics}/help/button_2.svg (100%) rename resources/{ => graphics}/help/button_3.svg (100%) rename resources/{ => graphics}/help/button_4.svg (100%) rename resources/{ => graphics}/help/button_a_PS.svg (100%) rename resources/{ => graphics}/help/button_a_SNES.svg (100%) rename resources/{ => graphics}/help/button_a_XBOX.svg (100%) rename resources/{ => graphics}/help/button_b_PS.svg (100%) rename resources/{ => graphics}/help/button_b_SNES.svg (100%) rename resources/{ => graphics}/help/button_b_XBOX.svg (100%) rename resources/{ => graphics}/help/button_back_PS4.svg (100%) rename resources/{ => graphics}/help/button_back_PS5.svg (100%) rename resources/{ => graphics}/help/button_back_SNES.svg (100%) rename resources/{ => graphics}/help/button_back_XBOX.svg (100%) rename resources/{ => graphics}/help/button_back_XBOX360.svg (100%) rename resources/{ => graphics}/help/button_hotkey.svg (100%) rename resources/{ => graphics}/help/button_l.svg (100%) rename resources/{ => graphics}/help/button_lr.svg (100%) rename resources/{ => graphics}/help/button_lt.svg (100%) rename resources/{ => graphics}/help/button_r.svg (100%) rename resources/{ => graphics}/help/button_rt.svg (100%) rename resources/{ => graphics}/help/button_start_PS4.svg (100%) rename resources/{ => graphics}/help/button_start_PS5.svg (100%) rename resources/{ => graphics}/help/button_start_SNES.svg (100%) rename resources/{ => graphics}/help/button_start_XBOX.svg (100%) rename resources/{ => graphics}/help/button_start_XBOX360.svg (100%) rename resources/{ => graphics}/help/button_x_PS.svg (100%) rename resources/{ => graphics}/help/button_x_SNES.svg (100%) rename resources/{ => graphics}/help/button_x_XBOX.svg (100%) rename resources/{ => graphics}/help/button_y_PS.svg (100%) rename resources/{ => graphics}/help/button_y_SNES.svg (100%) rename resources/{ => graphics}/help/button_y_XBOX.svg (100%) rename resources/{ => graphics}/help/dpad_all.svg (100%) rename resources/{ => graphics}/help/dpad_down.svg (100%) rename resources/{ => graphics}/help/dpad_left.svg (100%) rename resources/{ => graphics}/help/dpad_leftright.svg (100%) rename resources/{ => graphics}/help/dpad_right.svg (100%) rename resources/{ => graphics}/help/dpad_up.svg (100%) rename resources/{ => graphics}/help/dpad_updown.svg (100%) rename resources/{ => graphics}/help/mbuttons_a_PS.svg (100%) rename resources/{ => graphics}/help/mbuttons_a_SNES.svg (100%) rename resources/{ => graphics}/help/mbuttons_a_XBOX.svg (100%) rename resources/{ => graphics}/help/mbuttons_b_PS.svg (100%) rename resources/{ => graphics}/help/mbuttons_b_SNES.svg (100%) rename resources/{ => graphics}/help/mbuttons_b_XBOX.svg (100%) rename resources/{ => graphics}/help/mbuttons_x_PS.svg (100%) rename resources/{ => graphics}/help/mbuttons_x_SNES.svg (100%) rename resources/{ => graphics}/help/mbuttons_x_XBOX.svg (100%) rename resources/{ => graphics}/help/mbuttons_y_PS.svg (100%) rename resources/{ => graphics}/help/mbuttons_y_SNES.svg (100%) rename resources/{ => graphics}/help/mbuttons_y_XBOX.svg (100%) rename resources/{ => graphics}/help/thumbstick.svg (100%) rename resources/{ => graphics}/help/thumbstick_click.svg (100%) rename resources/{ => graphics}/help/thumbstick_down.svg (100%) rename resources/{ => graphics}/help/thumbstick_left.svg (100%) rename resources/{ => graphics}/help/thumbstick_right.svg (100%) rename resources/{ => graphics}/help/thumbstick_up.svg (100%) diff --git a/es-core/src/components/HelpComponent.cpp b/es-core/src/components/HelpComponent.cpp index 7b6c7197f..49b68e354 100644 --- a/es-core/src/components/HelpComponent.cpp +++ b/es-core/src/components/HelpComponent.cpp @@ -34,129 +34,129 @@ void HelpComponent::assignIcons() // These graphics files are common between all controller types. sIconPathMap["up/down"] = mStyle.mCustomButtons.dpad_updown.empty() ? - ":/help/dpad_updown.svg" : + ":/graphics/help/dpad_updown.svg" : mStyle.mCustomButtons.dpad_updown; sIconPathMap["left/right"] = mStyle.mCustomButtons.dpad_leftright.empty() ? - ":/help/dpad_leftright.svg" : + ":/graphics/help/dpad_leftright.svg" : mStyle.mCustomButtons.dpad_leftright; sIconPathMap["up/down/left/right"] = mStyle.mCustomButtons.dpad_all.empty() ? - ":/help/dpad_all.svg" : + ":/graphics/help/dpad_all.svg" : mStyle.mCustomButtons.dpad_all; sIconPathMap["thumbstickclick"] = mStyle.mCustomButtons.thumbstick_click.empty() ? - ":/help/thumbstick_click.svg" : + ":/graphics/help/thumbstick_click.svg" : mStyle.mCustomButtons.thumbstick_click; - sIconPathMap["l"] = mStyle.mCustomButtons.button_l.empty() ? ":/help/button_l.svg" : + sIconPathMap["l"] = mStyle.mCustomButtons.button_l.empty() ? ":/graphics/help/button_l.svg" : mStyle.mCustomButtons.button_l; - sIconPathMap["r"] = mStyle.mCustomButtons.button_r.empty() ? ":/help/button_r.svg" : + sIconPathMap["r"] = mStyle.mCustomButtons.button_r.empty() ? ":/graphics/help/button_r.svg" : mStyle.mCustomButtons.button_r; - sIconPathMap["lr"] = mStyle.mCustomButtons.button_lr.empty() ? ":/help/button_lr.svg" : + sIconPathMap["lr"] = mStyle.mCustomButtons.button_lr.empty() ? ":/graphics/help/button_lr.svg" : mStyle.mCustomButtons.button_lr; - sIconPathMap["lt"] = mStyle.mCustomButtons.button_lt.empty() ? ":/help/button_lt.svg" : + sIconPathMap["lt"] = mStyle.mCustomButtons.button_lt.empty() ? ":/graphics/help/button_lt.svg" : mStyle.mCustomButtons.button_lt; - sIconPathMap["rt"] = mStyle.mCustomButtons.button_rt.empty() ? ":/help/button_rt.svg" : + sIconPathMap["rt"] = mStyle.mCustomButtons.button_rt.empty() ? ":/graphics/help/button_rt.svg" : mStyle.mCustomButtons.button_rt; // These graphics files are custom per controller type. if (controllerType == "snes") { sIconPathMap["a"] = mStyle.mCustomButtons.button_a_SNES.empty() ? - ":/help/button_a_SNES.svg" : + ":/graphics/help/button_a_SNES.svg" : mStyle.mCustomButtons.button_a_SNES; sIconPathMap["b"] = mStyle.mCustomButtons.button_b_SNES.empty() ? - ":/help/button_b_SNES.svg" : + ":/graphics/help/button_b_SNES.svg" : mStyle.mCustomButtons.button_b_SNES; sIconPathMap["x"] = mStyle.mCustomButtons.button_x_SNES.empty() ? - ":/help/button_x_SNES.svg" : + ":/graphics/help/button_x_SNES.svg" : mStyle.mCustomButtons.button_x_SNES; sIconPathMap["y"] = mStyle.mCustomButtons.button_y_SNES.empty() ? - ":/help/button_y_SNES.svg" : + ":/graphics/help/button_y_SNES.svg" : mStyle.mCustomButtons.button_y_SNES; sIconPathMap["start"] = mStyle.mCustomButtons.button_start_SNES.empty() ? - ":/help/button_start_SNES.svg" : + ":/graphics/help/button_start_SNES.svg" : mStyle.mCustomButtons.button_start_SNES; sIconPathMap["back"] = mStyle.mCustomButtons.button_back_SNES.empty() ? - ":/help/button_back_SNES.svg" : + ":/graphics/help/button_back_SNES.svg" : mStyle.mCustomButtons.button_back_SNES; } else if (controllerType == "ps4") { sIconPathMap["a"] = mStyle.mCustomButtons.button_a_PS.empty() ? - ":/help/button_a_PS.svg" : + ":/graphics/help/button_a_PS.svg" : mStyle.mCustomButtons.button_a_PS; sIconPathMap["b"] = mStyle.mCustomButtons.button_b_PS.empty() ? - ":/help/button_b_PS.svg" : + ":/graphics/help/button_b_PS.svg" : mStyle.mCustomButtons.button_b_PS; sIconPathMap["x"] = mStyle.mCustomButtons.button_x_PS.empty() ? - ":/help/button_x_PS.svg" : + ":/graphics/help/button_x_PS.svg" : mStyle.mCustomButtons.button_x_PS; sIconPathMap["y"] = mStyle.mCustomButtons.button_y_PS.empty() ? - ":/help/button_y_PS.svg" : + ":/graphics/help/button_y_PS.svg" : mStyle.mCustomButtons.button_y_PS; sIconPathMap["start"] = mStyle.mCustomButtons.button_start_PS4.empty() ? - ":/help/button_start_PS4.svg" : + ":/graphics/help/button_start_PS4.svg" : mStyle.mCustomButtons.button_start_PS4; sIconPathMap["back"] = mStyle.mCustomButtons.button_back_PS4.empty() ? - ":/help/button_back_PS4.svg" : + ":/graphics/help/button_back_PS4.svg" : mStyle.mCustomButtons.button_back_PS4; } else if (controllerType == "ps5") { sIconPathMap["a"] = mStyle.mCustomButtons.button_a_PS.empty() ? - ":/help/button_a_PS.svg" : + ":/graphics/help/button_a_PS.svg" : mStyle.mCustomButtons.button_a_PS; sIconPathMap["b"] = mStyle.mCustomButtons.button_b_PS.empty() ? - ":/help/button_b_PS.svg" : + ":/graphics/help/button_b_PS.svg" : mStyle.mCustomButtons.button_b_PS; sIconPathMap["x"] = mStyle.mCustomButtons.button_x_PS.empty() ? - ":/help/button_x_PS.svg" : + ":/graphics/help/button_x_PS.svg" : mStyle.mCustomButtons.button_x_PS; sIconPathMap["y"] = mStyle.mCustomButtons.button_y_PS.empty() ? - ":/help/button_y_PS.svg" : + ":/graphics/help/button_y_PS.svg" : mStyle.mCustomButtons.button_y_PS; sIconPathMap["start"] = mStyle.mCustomButtons.button_start_PS5.empty() ? - ":/help/button_start_PS5.svg" : + ":/graphics/help/button_start_PS5.svg" : mStyle.mCustomButtons.button_start_PS5; sIconPathMap["back"] = mStyle.mCustomButtons.button_back_PS5.empty() ? - ":/help/button_back_PS5.svg" : + ":/graphics/help/button_back_PS5.svg" : mStyle.mCustomButtons.button_back_PS5; } else if (controllerType == "xbox360") { sIconPathMap["a"] = mStyle.mCustomButtons.button_a_XBOX.empty() ? - ":/help/button_a_XBOX.svg" : + ":/graphics/help/button_a_XBOX.svg" : mStyle.mCustomButtons.button_a_XBOX; sIconPathMap["b"] = mStyle.mCustomButtons.button_b_XBOX.empty() ? - ":/help/button_b_XBOX.svg" : + ":/graphics/help/button_b_XBOX.svg" : mStyle.mCustomButtons.button_b_XBOX; sIconPathMap["x"] = mStyle.mCustomButtons.button_x_XBOX.empty() ? - ":/help/button_x_XBOX.svg" : + ":/graphics/help/button_x_XBOX.svg" : mStyle.mCustomButtons.button_x_XBOX; sIconPathMap["y"] = mStyle.mCustomButtons.button_y_XBOX.empty() ? - ":/help/button_y_XBOX.svg" : + ":/graphics/help/button_y_XBOX.svg" : mStyle.mCustomButtons.button_y_XBOX; sIconPathMap["start"] = mStyle.mCustomButtons.button_start_XBOX360.empty() ? - ":/help/button_start_XBOX360.svg" : + ":/graphics/help/button_start_XBOX360.svg" : mStyle.mCustomButtons.button_start_XBOX360; sIconPathMap["back"] = mStyle.mCustomButtons.button_back_XBOX360.empty() ? - ":/help/button_back_XBOX360.svg" : + ":/graphics/help/button_back_XBOX360.svg" : mStyle.mCustomButtons.button_back_XBOX360; } else { // Xbox One and later. sIconPathMap["a"] = mStyle.mCustomButtons.button_a_XBOX.empty() ? - ":/help/button_a_XBOX.svg" : + ":/graphics/help/button_a_XBOX.svg" : mStyle.mCustomButtons.button_a_XBOX; sIconPathMap["b"] = mStyle.mCustomButtons.button_b_XBOX.empty() ? - ":/help/button_b_XBOX.svg" : + ":/graphics/help/button_b_XBOX.svg" : mStyle.mCustomButtons.button_b_XBOX; sIconPathMap["x"] = mStyle.mCustomButtons.button_x_XBOX.empty() ? - ":/help/button_x_XBOX.svg" : + ":/graphics/help/button_x_XBOX.svg" : mStyle.mCustomButtons.button_x_XBOX; sIconPathMap["y"] = mStyle.mCustomButtons.button_y_XBOX.empty() ? - ":/help/button_y_XBOX.svg" : + ":/graphics/help/button_y_XBOX.svg" : mStyle.mCustomButtons.button_y_XBOX; sIconPathMap["start"] = mStyle.mCustomButtons.button_start_XBOX.empty() ? - ":/help/button_start_XBOX.svg" : + ":/graphics/help/button_start_XBOX.svg" : mStyle.mCustomButtons.button_start_XBOX; sIconPathMap["back"] = mStyle.mCustomButtons.button_back_XBOX.empty() ? - ":/help/button_back_XBOX.svg" : + ":/graphics/help/button_back_XBOX.svg" : mStyle.mCustomButtons.button_back_XBOX; } diff --git a/es-core/src/guis/GuiInputConfig.cpp b/es-core/src/guis/GuiInputConfig.cpp index 516d17c6e..f9bf3d10f 100644 --- a/es-core/src/guis/GuiInputConfig.cpp +++ b/es-core/src/guis/GuiInputConfig.cpp @@ -201,67 +201,67 @@ void GuiInputConfig::populateConfigList() std::string controllerType = Settings::getInstance()->getString("InputControllerType"); // clang-format off - sGuiInputConfigList[0] = {"Up", false, "D-PAD UP", ":/help/dpad_up.svg"}; - sGuiInputConfigList[1] = {"Down", false, "D-PAD DOWN", ":/help/dpad_down.svg"}; - sGuiInputConfigList[2] = {"Left", false, "D-PAD LEFT", ":/help/dpad_left.svg"}; - sGuiInputConfigList[3] = {"Right", false, "D-PAD RIGHT", ":/help/dpad_right.svg"}; + sGuiInputConfigList[0] = {"Up", false, "D-PAD UP", ":/graphics/help/dpad_up.svg"}; + sGuiInputConfigList[1] = {"Down", false, "D-PAD DOWN", ":/graphics/help/dpad_down.svg"}; + sGuiInputConfigList[2] = {"Left", false, "D-PAD LEFT", ":/graphics/help/dpad_left.svg"}; + sGuiInputConfigList[3] = {"Right", false, "D-PAD RIGHT", ":/graphics/help/dpad_right.svg"}; if (controllerType == "snes") { - sGuiInputConfigList[4] = {"Back", false, "SELECT", ":/help/button_back_SNES.svg"}; - sGuiInputConfigList[5] = {"Start", false, "START", ":/help/button_start_SNES.svg"}; - sGuiInputConfigList[6] = {"A", false, "B", ":/help/mbuttons_a_SNES.svg"}; - sGuiInputConfigList[7] = {"B", false, "A", ":/help/mbuttons_b_SNES.svg"}; - sGuiInputConfigList[8] = {"X", true, "Y", ":/help/mbuttons_x_SNES.svg"}; - sGuiInputConfigList[9] = {"Y", true, "X", ":/help/mbuttons_y_SNES.svg"}; + sGuiInputConfigList[4] = {"Back", false, "SELECT", ":/graphics/help/button_back_SNES.svg"}; + sGuiInputConfigList[5] = {"Start", false, "START", ":/graphics/help/button_start_SNES.svg"}; + sGuiInputConfigList[6] = {"A", false, "B", ":/graphics/help/mbuttons_a_SNES.svg"}; + sGuiInputConfigList[7] = {"B", false, "A", ":/graphics/help/mbuttons_b_SNES.svg"}; + sGuiInputConfigList[8] = {"X", true, "Y", ":/graphics/help/mbuttons_x_SNES.svg"}; + sGuiInputConfigList[9] = {"Y", true, "X", ":/graphics/help/mbuttons_y_SNES.svg"}; } else if (controllerType == "ps4") { - sGuiInputConfigList[4] = {"Back", false, "SHARE", ":/help/button_back_PS4.svg"}; - sGuiInputConfigList[5] = {"Start", false, "OPTIONS", ":/help/button_start_PS4.svg"}; - sGuiInputConfigList[6] = {"A", false, "CROSS", ":/help/mbuttons_a_PS.svg"}; - sGuiInputConfigList[7] = {"B", false, "CIRCLE", ":/help/mbuttons_b_PS.svg"}; - sGuiInputConfigList[8] = {"X", true, "SQUARE", ":/help/mbuttons_x_PS.svg"}; - sGuiInputConfigList[9] = {"Y", true, "TRIANGLE", ":/help/mbuttons_y_PS.svg"}; + sGuiInputConfigList[4] = {"Back", false, "SHARE", ":/graphics/help/button_back_PS4.svg"}; + sGuiInputConfigList[5] = {"Start", false, "OPTIONS", ":/graphics/help/button_start_PS4.svg"}; + sGuiInputConfigList[6] = {"A", false, "CROSS", ":/graphics/help/mbuttons_a_PS.svg"}; + sGuiInputConfigList[7] = {"B", false, "CIRCLE", ":/graphics/help/mbuttons_b_PS.svg"}; + sGuiInputConfigList[8] = {"X", true, "SQUARE", ":/graphics/help/mbuttons_x_PS.svg"}; + sGuiInputConfigList[9] = {"Y", true, "TRIANGLE", ":/graphics/help/mbuttons_y_PS.svg"}; } else if (controllerType == "ps5") { - sGuiInputConfigList[4] = {"Back", false, "CREATE", ":/help/button_back_PS5.svg"}; - sGuiInputConfigList[5] = {"Start", false, "OPTIONS", ":/help/button_start_PS5.svg"}; - sGuiInputConfigList[6] = {"A", false, "CROSS", ":/help/mbuttons_a_PS.svg"}; - sGuiInputConfigList[7] = {"B", false, "CIRCLE", ":/help/mbuttons_b_PS.svg"}; - sGuiInputConfigList[8] = {"X", true, "SQUARE", ":/help/mbuttons_x_PS.svg"}; - sGuiInputConfigList[9] = {"Y", true, "TRIANGLE", ":/help/mbuttons_y_PS.svg"}; + sGuiInputConfigList[4] = {"Back", false, "CREATE", ":/graphics/help/button_back_PS5.svg"}; + sGuiInputConfigList[5] = {"Start", false, "OPTIONS", ":/graphics/help/button_start_PS5.svg"}; + sGuiInputConfigList[6] = {"A", false, "CROSS", ":/graphics/help/mbuttons_a_PS.svg"}; + sGuiInputConfigList[7] = {"B", false, "CIRCLE", ":/graphics/help/mbuttons_b_PS.svg"}; + sGuiInputConfigList[8] = {"X", true, "SQUARE", ":/graphics/help/mbuttons_x_PS.svg"}; + sGuiInputConfigList[9] = {"Y", true, "TRIANGLE", ":/graphics/help/mbuttons_y_PS.svg"}; } else if (controllerType == "xbox360") { - sGuiInputConfigList[4] = {"Back", false, "BACK", ":/help/button_back_XBOX360.svg"}; - sGuiInputConfigList[5] = {"Start", false, "START", ":/help/button_start_XBOX360.svg"}; - sGuiInputConfigList[6] = {"A", false, "A", ":/help/mbuttons_a_XBOX.svg"}; - sGuiInputConfigList[7] = {"B", false, "B", ":/help/mbuttons_b_XBOX.svg"}; - sGuiInputConfigList[8] = {"X", true, "X", ":/help/mbuttons_x_XBOX.svg"}; - sGuiInputConfigList[9] = {"Y", true, "Y", ":/help/mbuttons_y_XBOX.svg"}; + sGuiInputConfigList[4] = {"Back", false, "BACK", ":/graphics/help/button_back_XBOX360.svg"}; + sGuiInputConfigList[5] = {"Start", false, "START", ":/graphics/help/button_start_XBOX360.svg"}; + sGuiInputConfigList[6] = {"A", false, "A", ":/graphics/help/mbuttons_a_XBOX.svg"}; + sGuiInputConfigList[7] = {"B", false, "B", ":/graphics/help/mbuttons_b_XBOX.svg"}; + sGuiInputConfigList[8] = {"X", true, "X", ":/graphics/help/mbuttons_x_XBOX.svg"}; + sGuiInputConfigList[9] = {"Y", true, "Y", ":/graphics/help/mbuttons_y_XBOX.svg"}; } else { // Xbox One and later. - sGuiInputConfigList[4] = {"Back", false, "VIEW", ":/help/button_back_XBOX.svg"}; - sGuiInputConfigList[5] = {"Start", false, "MENU", ":/help/button_start_XBOX.svg"}; - sGuiInputConfigList[6] = {"A", false, "A", ":/help/mbuttons_a_XBOX.svg"}; - sGuiInputConfigList[7] = {"B", false, "B", ":/help/mbuttons_b_XBOX.svg"}; - sGuiInputConfigList[8] = {"X", true, "X", ":/help/mbuttons_x_XBOX.svg"}; - sGuiInputConfigList[9] = {"Y", true, "Y", ":/help/mbuttons_y_XBOX.svg"}; + sGuiInputConfigList[4] = {"Back", false, "VIEW", ":/graphics/help/button_back_XBOX.svg"}; + sGuiInputConfigList[5] = {"Start", false, "MENU", ":/graphics/help/button_start_XBOX.svg"}; + sGuiInputConfigList[6] = {"A", false, "A", ":/graphics/help/mbuttons_a_XBOX.svg"}; + sGuiInputConfigList[7] = {"B", false, "B", ":/graphics/help/mbuttons_b_XBOX.svg"}; + sGuiInputConfigList[8] = {"X", true, "X", ":/graphics/help/mbuttons_x_XBOX.svg"}; + sGuiInputConfigList[9] = {"Y", true, "Y", ":/graphics/help/mbuttons_y_XBOX.svg"}; } - sGuiInputConfigList[10] = {"LeftShoulder", true, "LEFT SHOULDER", ":/help/button_l.svg"}; - sGuiInputConfigList[11] = {"RightShoulder", true, "RIGHT SHOULDER", ":/help/button_r.svg"}; - sGuiInputConfigList[12] = {"LeftTrigger", true, "LEFT TRIGGER", ":/help/button_lt.svg"}; - sGuiInputConfigList[13] = {"RightTrigger", true, "RIGHT TRIGGER", ":/help/button_rt.svg"}; - sGuiInputConfigList[14] = {"LeftThumbstickUp", true, "LEFT THUMBSTICK UP", ":/help/thumbstick_up.svg"}; - sGuiInputConfigList[15] = {"LeftThumbstickDown", true, "LEFT THUMBSTICK DOWN", ":/help/thumbstick_down.svg"}; - sGuiInputConfigList[16] = {"LeftThumbstickLeft", true, "LEFT THUMBSTICK LEFT", ":/help/thumbstick_left.svg"}; - sGuiInputConfigList[17] = {"LeftThumbstickRight", true, "LEFT THUMBSTICK RIGHT", ":/help/thumbstick_right.svg"}; - sGuiInputConfigList[18] = {"LeftThumbstickClick", true, "LEFT THUMBSTICK CLICK", ":/help/thumbstick_click.svg"}; - sGuiInputConfigList[19] = {"RightThumbstickUp", true, "RIGHT THUMBSTICK UP", ":/help/thumbstick_up.svg"}; - sGuiInputConfigList[20] = {"RightThumbstickDown", true, "RIGHT THUMBSTICK DOWN", ":/help/thumbstick_down.svg"}; - sGuiInputConfigList[21] = {"RightThumbstickLeft", true, "RIGHT THUMBSTICK LEFT", ":/help/thumbstick_left.svg"}; - sGuiInputConfigList[22] = {"RightThumbstickRight", true, "RIGHT THUMBSTICK RIGHT", ":/help/thumbstick_right.svg"}; - sGuiInputConfigList[23] = {"RightThumbstickClick", true, "RIGHT THUMBSTICK CLICK", ":/help/thumbstick_click.svg"}; + sGuiInputConfigList[10] = {"LeftShoulder", true, "LEFT SHOULDER", ":/graphics/help/button_l.svg"}; + sGuiInputConfigList[11] = {"RightShoulder", true, "RIGHT SHOULDER", ":/graphics/help/button_r.svg"}; + sGuiInputConfigList[12] = {"LeftTrigger", true, "LEFT TRIGGER", ":/graphics/help/button_lt.svg"}; + sGuiInputConfigList[13] = {"RightTrigger", true, "RIGHT TRIGGER", ":/graphics/help/button_rt.svg"}; + sGuiInputConfigList[14] = {"LeftThumbstickUp", true, "LEFT THUMBSTICK UP", ":/graphics/help/thumbstick_up.svg"}; + sGuiInputConfigList[15] = {"LeftThumbstickDown", true, "LEFT THUMBSTICK DOWN", ":/graphics/help/thumbstick_down.svg"}; + sGuiInputConfigList[16] = {"LeftThumbstickLeft", true, "LEFT THUMBSTICK LEFT", ":/graphics/help/thumbstick_left.svg"}; + sGuiInputConfigList[17] = {"LeftThumbstickRight", true, "LEFT THUMBSTICK RIGHT", ":/graphics/help/thumbstick_right.svg"}; + sGuiInputConfigList[18] = {"LeftThumbstickClick", true, "LEFT THUMBSTICK CLICK", ":/graphics/help/thumbstick_click.svg"}; + sGuiInputConfigList[19] = {"RightThumbstickUp", true, "RIGHT THUMBSTICK UP", ":/graphics/help/thumbstick_up.svg"}; + sGuiInputConfigList[20] = {"RightThumbstickDown", true, "RIGHT THUMBSTICK DOWN", ":/graphics/help/thumbstick_down.svg"}; + sGuiInputConfigList[21] = {"RightThumbstickLeft", true, "RIGHT THUMBSTICK LEFT", ":/graphics/help/thumbstick_left.svg"}; + sGuiInputConfigList[22] = {"RightThumbstickRight", true, "RIGHT THUMBSTICK RIGHT", ":/graphics/help/thumbstick_right.svg"}; + sGuiInputConfigList[23] = {"RightThumbstickClick", true, "RIGHT THUMBSTICK CLICK", ":/graphics/help/thumbstick_click.svg"}; // clang-format on } diff --git a/resources/help/button_1.svg b/resources/graphics/help/button_1.svg similarity index 100% rename from resources/help/button_1.svg rename to resources/graphics/help/button_1.svg diff --git a/resources/help/button_2.svg b/resources/graphics/help/button_2.svg similarity index 100% rename from resources/help/button_2.svg rename to resources/graphics/help/button_2.svg diff --git a/resources/help/button_3.svg b/resources/graphics/help/button_3.svg similarity index 100% rename from resources/help/button_3.svg rename to resources/graphics/help/button_3.svg diff --git a/resources/help/button_4.svg b/resources/graphics/help/button_4.svg similarity index 100% rename from resources/help/button_4.svg rename to resources/graphics/help/button_4.svg diff --git a/resources/help/button_a_PS.svg b/resources/graphics/help/button_a_PS.svg similarity index 100% rename from resources/help/button_a_PS.svg rename to resources/graphics/help/button_a_PS.svg diff --git a/resources/help/button_a_SNES.svg b/resources/graphics/help/button_a_SNES.svg similarity index 100% rename from resources/help/button_a_SNES.svg rename to resources/graphics/help/button_a_SNES.svg diff --git a/resources/help/button_a_XBOX.svg b/resources/graphics/help/button_a_XBOX.svg similarity index 100% rename from resources/help/button_a_XBOX.svg rename to resources/graphics/help/button_a_XBOX.svg diff --git a/resources/help/button_b_PS.svg b/resources/graphics/help/button_b_PS.svg similarity index 100% rename from resources/help/button_b_PS.svg rename to resources/graphics/help/button_b_PS.svg diff --git a/resources/help/button_b_SNES.svg b/resources/graphics/help/button_b_SNES.svg similarity index 100% rename from resources/help/button_b_SNES.svg rename to resources/graphics/help/button_b_SNES.svg diff --git a/resources/help/button_b_XBOX.svg b/resources/graphics/help/button_b_XBOX.svg similarity index 100% rename from resources/help/button_b_XBOX.svg rename to resources/graphics/help/button_b_XBOX.svg diff --git a/resources/help/button_back_PS4.svg b/resources/graphics/help/button_back_PS4.svg similarity index 100% rename from resources/help/button_back_PS4.svg rename to resources/graphics/help/button_back_PS4.svg diff --git a/resources/help/button_back_PS5.svg b/resources/graphics/help/button_back_PS5.svg similarity index 100% rename from resources/help/button_back_PS5.svg rename to resources/graphics/help/button_back_PS5.svg diff --git a/resources/help/button_back_SNES.svg b/resources/graphics/help/button_back_SNES.svg similarity index 100% rename from resources/help/button_back_SNES.svg rename to resources/graphics/help/button_back_SNES.svg diff --git a/resources/help/button_back_XBOX.svg b/resources/graphics/help/button_back_XBOX.svg similarity index 100% rename from resources/help/button_back_XBOX.svg rename to resources/graphics/help/button_back_XBOX.svg diff --git a/resources/help/button_back_XBOX360.svg b/resources/graphics/help/button_back_XBOX360.svg similarity index 100% rename from resources/help/button_back_XBOX360.svg rename to resources/graphics/help/button_back_XBOX360.svg diff --git a/resources/help/button_hotkey.svg b/resources/graphics/help/button_hotkey.svg similarity index 100% rename from resources/help/button_hotkey.svg rename to resources/graphics/help/button_hotkey.svg diff --git a/resources/help/button_l.svg b/resources/graphics/help/button_l.svg similarity index 100% rename from resources/help/button_l.svg rename to resources/graphics/help/button_l.svg diff --git a/resources/help/button_lr.svg b/resources/graphics/help/button_lr.svg similarity index 100% rename from resources/help/button_lr.svg rename to resources/graphics/help/button_lr.svg diff --git a/resources/help/button_lt.svg b/resources/graphics/help/button_lt.svg similarity index 100% rename from resources/help/button_lt.svg rename to resources/graphics/help/button_lt.svg diff --git a/resources/help/button_r.svg b/resources/graphics/help/button_r.svg similarity index 100% rename from resources/help/button_r.svg rename to resources/graphics/help/button_r.svg diff --git a/resources/help/button_rt.svg b/resources/graphics/help/button_rt.svg similarity index 100% rename from resources/help/button_rt.svg rename to resources/graphics/help/button_rt.svg diff --git a/resources/help/button_start_PS4.svg b/resources/graphics/help/button_start_PS4.svg similarity index 100% rename from resources/help/button_start_PS4.svg rename to resources/graphics/help/button_start_PS4.svg diff --git a/resources/help/button_start_PS5.svg b/resources/graphics/help/button_start_PS5.svg similarity index 100% rename from resources/help/button_start_PS5.svg rename to resources/graphics/help/button_start_PS5.svg diff --git a/resources/help/button_start_SNES.svg b/resources/graphics/help/button_start_SNES.svg similarity index 100% rename from resources/help/button_start_SNES.svg rename to resources/graphics/help/button_start_SNES.svg diff --git a/resources/help/button_start_XBOX.svg b/resources/graphics/help/button_start_XBOX.svg similarity index 100% rename from resources/help/button_start_XBOX.svg rename to resources/graphics/help/button_start_XBOX.svg diff --git a/resources/help/button_start_XBOX360.svg b/resources/graphics/help/button_start_XBOX360.svg similarity index 100% rename from resources/help/button_start_XBOX360.svg rename to resources/graphics/help/button_start_XBOX360.svg diff --git a/resources/help/button_x_PS.svg b/resources/graphics/help/button_x_PS.svg similarity index 100% rename from resources/help/button_x_PS.svg rename to resources/graphics/help/button_x_PS.svg diff --git a/resources/help/button_x_SNES.svg b/resources/graphics/help/button_x_SNES.svg similarity index 100% rename from resources/help/button_x_SNES.svg rename to resources/graphics/help/button_x_SNES.svg diff --git a/resources/help/button_x_XBOX.svg b/resources/graphics/help/button_x_XBOX.svg similarity index 100% rename from resources/help/button_x_XBOX.svg rename to resources/graphics/help/button_x_XBOX.svg diff --git a/resources/help/button_y_PS.svg b/resources/graphics/help/button_y_PS.svg similarity index 100% rename from resources/help/button_y_PS.svg rename to resources/graphics/help/button_y_PS.svg diff --git a/resources/help/button_y_SNES.svg b/resources/graphics/help/button_y_SNES.svg similarity index 100% rename from resources/help/button_y_SNES.svg rename to resources/graphics/help/button_y_SNES.svg diff --git a/resources/help/button_y_XBOX.svg b/resources/graphics/help/button_y_XBOX.svg similarity index 100% rename from resources/help/button_y_XBOX.svg rename to resources/graphics/help/button_y_XBOX.svg diff --git a/resources/help/dpad_all.svg b/resources/graphics/help/dpad_all.svg similarity index 100% rename from resources/help/dpad_all.svg rename to resources/graphics/help/dpad_all.svg diff --git a/resources/help/dpad_down.svg b/resources/graphics/help/dpad_down.svg similarity index 100% rename from resources/help/dpad_down.svg rename to resources/graphics/help/dpad_down.svg diff --git a/resources/help/dpad_left.svg b/resources/graphics/help/dpad_left.svg similarity index 100% rename from resources/help/dpad_left.svg rename to resources/graphics/help/dpad_left.svg diff --git a/resources/help/dpad_leftright.svg b/resources/graphics/help/dpad_leftright.svg similarity index 100% rename from resources/help/dpad_leftright.svg rename to resources/graphics/help/dpad_leftright.svg diff --git a/resources/help/dpad_right.svg b/resources/graphics/help/dpad_right.svg similarity index 100% rename from resources/help/dpad_right.svg rename to resources/graphics/help/dpad_right.svg diff --git a/resources/help/dpad_up.svg b/resources/graphics/help/dpad_up.svg similarity index 100% rename from resources/help/dpad_up.svg rename to resources/graphics/help/dpad_up.svg diff --git a/resources/help/dpad_updown.svg b/resources/graphics/help/dpad_updown.svg similarity index 100% rename from resources/help/dpad_updown.svg rename to resources/graphics/help/dpad_updown.svg diff --git a/resources/help/mbuttons_a_PS.svg b/resources/graphics/help/mbuttons_a_PS.svg similarity index 100% rename from resources/help/mbuttons_a_PS.svg rename to resources/graphics/help/mbuttons_a_PS.svg diff --git a/resources/help/mbuttons_a_SNES.svg b/resources/graphics/help/mbuttons_a_SNES.svg similarity index 100% rename from resources/help/mbuttons_a_SNES.svg rename to resources/graphics/help/mbuttons_a_SNES.svg diff --git a/resources/help/mbuttons_a_XBOX.svg b/resources/graphics/help/mbuttons_a_XBOX.svg similarity index 100% rename from resources/help/mbuttons_a_XBOX.svg rename to resources/graphics/help/mbuttons_a_XBOX.svg diff --git a/resources/help/mbuttons_b_PS.svg b/resources/graphics/help/mbuttons_b_PS.svg similarity index 100% rename from resources/help/mbuttons_b_PS.svg rename to resources/graphics/help/mbuttons_b_PS.svg diff --git a/resources/help/mbuttons_b_SNES.svg b/resources/graphics/help/mbuttons_b_SNES.svg similarity index 100% rename from resources/help/mbuttons_b_SNES.svg rename to resources/graphics/help/mbuttons_b_SNES.svg diff --git a/resources/help/mbuttons_b_XBOX.svg b/resources/graphics/help/mbuttons_b_XBOX.svg similarity index 100% rename from resources/help/mbuttons_b_XBOX.svg rename to resources/graphics/help/mbuttons_b_XBOX.svg diff --git a/resources/help/mbuttons_x_PS.svg b/resources/graphics/help/mbuttons_x_PS.svg similarity index 100% rename from resources/help/mbuttons_x_PS.svg rename to resources/graphics/help/mbuttons_x_PS.svg diff --git a/resources/help/mbuttons_x_SNES.svg b/resources/graphics/help/mbuttons_x_SNES.svg similarity index 100% rename from resources/help/mbuttons_x_SNES.svg rename to resources/graphics/help/mbuttons_x_SNES.svg diff --git a/resources/help/mbuttons_x_XBOX.svg b/resources/graphics/help/mbuttons_x_XBOX.svg similarity index 100% rename from resources/help/mbuttons_x_XBOX.svg rename to resources/graphics/help/mbuttons_x_XBOX.svg diff --git a/resources/help/mbuttons_y_PS.svg b/resources/graphics/help/mbuttons_y_PS.svg similarity index 100% rename from resources/help/mbuttons_y_PS.svg rename to resources/graphics/help/mbuttons_y_PS.svg diff --git a/resources/help/mbuttons_y_SNES.svg b/resources/graphics/help/mbuttons_y_SNES.svg similarity index 100% rename from resources/help/mbuttons_y_SNES.svg rename to resources/graphics/help/mbuttons_y_SNES.svg diff --git a/resources/help/mbuttons_y_XBOX.svg b/resources/graphics/help/mbuttons_y_XBOX.svg similarity index 100% rename from resources/help/mbuttons_y_XBOX.svg rename to resources/graphics/help/mbuttons_y_XBOX.svg diff --git a/resources/help/thumbstick.svg b/resources/graphics/help/thumbstick.svg similarity index 100% rename from resources/help/thumbstick.svg rename to resources/graphics/help/thumbstick.svg diff --git a/resources/help/thumbstick_click.svg b/resources/graphics/help/thumbstick_click.svg similarity index 100% rename from resources/help/thumbstick_click.svg rename to resources/graphics/help/thumbstick_click.svg diff --git a/resources/help/thumbstick_down.svg b/resources/graphics/help/thumbstick_down.svg similarity index 100% rename from resources/help/thumbstick_down.svg rename to resources/graphics/help/thumbstick_down.svg diff --git a/resources/help/thumbstick_left.svg b/resources/graphics/help/thumbstick_left.svg similarity index 100% rename from resources/help/thumbstick_left.svg rename to resources/graphics/help/thumbstick_left.svg diff --git a/resources/help/thumbstick_right.svg b/resources/graphics/help/thumbstick_right.svg similarity index 100% rename from resources/help/thumbstick_right.svg rename to resources/graphics/help/thumbstick_right.svg diff --git a/resources/help/thumbstick_up.svg b/resources/graphics/help/thumbstick_up.svg similarity index 100% rename from resources/help/thumbstick_up.svg rename to resources/graphics/help/thumbstick_up.svg From f9fb0ae2875b936e4a2b784135afc70b99c5aac5 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sat, 23 Oct 2021 20:53:25 +0200 Subject: [PATCH 32/60] Removed two unused graphics files. --- resources/graphics/fav_add.svg | 5 ----- resources/graphics/fav_remove.svg | 4 ---- 2 files changed, 9 deletions(-) delete mode 100644 resources/graphics/fav_add.svg delete mode 100644 resources/graphics/fav_remove.svg diff --git a/resources/graphics/fav_add.svg b/resources/graphics/fav_add.svg deleted file mode 100644 index 34402f462..000000000 --- a/resources/graphics/fav_add.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/resources/graphics/fav_remove.svg b/resources/graphics/fav_remove.svg deleted file mode 100644 index bb5f3f2a7..000000000 --- a/resources/graphics/fav_remove.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - From 5c4d0821e290f5c4d16cb28bed7ebc8451ab6bc5 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sat, 23 Oct 2021 22:49:34 +0200 Subject: [PATCH 33/60] Renamed 'Controller' to 'Controller badge' in the metadata editor. --- es-app/src/MetaData.cpp | 4 ++-- es-app/src/guis/GuiMetaDataEd.cpp | 11 +++++------ es-app/src/guis/GuiMetaDataEd.h | 2 +- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/es-app/src/MetaData.cpp b/es-app/src/MetaData.cpp index 943099fcb..1dc7a10e1 100644 --- a/es-app/src/MetaData.cpp +++ b/es-app/src/MetaData.cpp @@ -37,7 +37,7 @@ MetaDataDecl gameDecls[] = { {"nomultiscrape", MD_BOOL, "false", false, "exclude from multi-scraper", "enter no multi-scrape off/on", false}, {"hidemetadata", MD_BOOL, "false", false, "hide metadata fields", "enter hide metadata off/on", false}, {"playcount", MD_INT, "0", false, "times played", "enter number of times played", false}, -{"controller", MD_CONTROLLER, "", false, "controller", "select controller", false}, +{"controller", MD_CONTROLLER, "", false, "controller badge", "select controller badge", false}, {"altemulator", MD_ALT_EMULATOR, "", false, "alternative emulator", "select alternative emulator", false}, {"lastplayed", MD_TIME, "0", true, "last played", "enter last played date", false} }; @@ -58,7 +58,7 @@ MetaDataDecl folderDecls[] = { {"broken", MD_BOOL, "false", false, "broken/not working", "enter broken off/on", false}, {"nomultiscrape", MD_BOOL, "false", false, "exclude from multi-scraper", "enter no multi-scrape off/on", false}, {"hidemetadata", MD_BOOL, "false", false, "hide metadata fields", "enter hide metadata off/on", false}, -{"controller", MD_CONTROLLER, "", false, "controller", "select controller", false}, +{"controller", MD_CONTROLLER, "", false, "controller badge", "select controller badge", false}, {"lastplayed", MD_TIME, "0", true, "last played", "enter last played date", false} }; // clang-format on diff --git a/es-app/src/guis/GuiMetaDataEd.cpp b/es-app/src/guis/GuiMetaDataEd.cpp index e46c1bb62..02d713c42 100644 --- a/es-app/src/guis/GuiMetaDataEd.cpp +++ b/es-app/src/guis/GuiMetaDataEd.cpp @@ -53,11 +53,11 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window, , mMediaFilesUpdated{false} , mInvalidEmulatorEntry{false} { - mGameControllers = BadgeComponent::getGameControllers(); + mControllerBadges = BadgeComponent::getGameControllers(); // Remove the last "unknown" controller entry. - if (mGameControllers.size() > 1) - mGameControllers.pop_back(); + if (mControllerBadges.size() > 1) + mControllerBadges.pop_back(); addChild(&mBackground); addChild(&mGrid); @@ -206,11 +206,10 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window, ed->setColor(TEXTCOLOR_USERMARKED); }; - row.makeAcceptInputHandler([this, title, scraperParams, ed, updateVal, - originalValue] { + row.makeAcceptInputHandler([this, title, ed, updateVal] { GuiSettings* s = new GuiSettings(mWindow, title); - for (auto controller : mGameControllers) { + for (auto controller : mControllerBadges) { std::string selectedLabel = ed->getValue(); std::string label; ComponentListRow row; diff --git a/es-app/src/guis/GuiMetaDataEd.h b/es-app/src/guis/GuiMetaDataEd.h index a4833c257..ec4ca13af 100644 --- a/es-app/src/guis/GuiMetaDataEd.h +++ b/es-app/src/guis/GuiMetaDataEd.h @@ -61,7 +61,7 @@ private: ScraperSearchParams mScraperParams; - std::vector mGameControllers; + std::vector mControllerBadges; std::vector> mEditors; std::vector mMetaDataDecl; From 5247a9f5fe6ce0ad95986a09ae65597bdcb190c7 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sun, 24 Oct 2021 12:10:38 +0200 Subject: [PATCH 34/60] Fixed an issue where the wrong scroll indicator could be shown. --- es-core/src/components/ComponentList.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/es-core/src/components/ComponentList.cpp b/es-core/src/components/ComponentList.cpp index 1457e3d14..cee1ab3bb 100644 --- a/es-core/src/components/ComponentList.cpp +++ b/es-core/src/components/ComponentList.cpp @@ -249,8 +249,16 @@ void ComponentList::updateCameraOffset() while (mCameraOffset < target && i < mEntries.size()) { mCameraOffset += getRowHeight(mEntries.at(i).data); if (mCameraOffset > totalHeight - mSize.y) { - if (mSetupCompleted && mCameraOffset != oldCameraOffset) - mBottomCameraOffset = true; + if (mSetupCompleted) { + if (mScrollIndicatorStatus == ComponentList::SCROLL_NONE && + oldCameraOffset == 0.0f) + break; + if (mScrollIndicatorStatus != ComponentList::SCROLL_NONE && + oldCameraOffset == 0.0f) + mBottomCameraOffset = true; + else if (mCameraOffset != oldCameraOffset) + mBottomCameraOffset = true; + } break; } i++; From 0cf52c2d7172a8691038e471d31e368d972640de Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sun, 24 Oct 2021 13:05:44 +0200 Subject: [PATCH 35/60] Made it possible to filter the 'Controller badge' metadata field. --- es-app/src/FileFilterIndex.cpp | 44 ++++++++++++++++++++------- es-app/src/FileFilterIndex.h | 5 +++ es-app/src/guis/GuiGamelistFilter.cpp | 19 ++++++++++-- 3 files changed, 55 insertions(+), 13 deletions(-) diff --git a/es-app/src/FileFilterIndex.cpp b/es-app/src/FileFilterIndex.cpp index 20063dea8..ec4384c18 100644 --- a/es-app/src/FileFilterIndex.cpp +++ b/es-app/src/FileFilterIndex.cpp @@ -33,6 +33,7 @@ FileFilterIndex::FileFilterIndex() , mFilterByKidGame(false) , mFilterByHidden(false) , mFilterByBroken(false) + , mFilterByController(false) , mFilterByAltemulator(false) { clearAllFilters(); @@ -50,6 +51,7 @@ FileFilterIndex::FileFilterIndex() {KIDGAME_FILTER, &mKidGameIndexAllKeys, &mFilterByKidGame, &mKidGameIndexFilteredKeys, "kidgame", false, "", "KIDGAME"}, {HIDDEN_FILTER, &mHiddenIndexAllKeys, &mFilterByHidden, &mHiddenIndexFilteredKeys, "hidden", false, "", "HIDDEN"}, {BROKEN_FILTER, &mBrokenIndexAllKeys, &mFilterByBroken, &mBrokenIndexFilteredKeys, "broken", false, "", "BROKEN"}, + {CONTROLLER_FILTER, &mControllerIndexAllKeys, &mFilterByController, &mControllerIndexFilteredKeys, "controller", false, "", "CONTROLLER BADGE"}, {ALTEMULATOR_FILTER, &mAltemulatorIndexAllKeys, &mFilterByAltemulator, &mAltemulatorIndexFilteredKeys, "altemulator", false, "", "ALTERNATIVE EMULATOR"} }; // clang-format on @@ -82,6 +84,7 @@ void FileFilterIndex::importIndex(FileFilterIndex* indexToImport) {&mKidGameIndexAllKeys, &(indexToImport->mKidGameIndexAllKeys)}, {&mHiddenIndexAllKeys, &(indexToImport->mHiddenIndexAllKeys)}, {&mBrokenIndexAllKeys, &(indexToImport->mBrokenIndexAllKeys)}, + {&mControllerIndexAllKeys, &(indexToImport->mControllerIndexAllKeys)}, {&mAltemulatorIndexAllKeys, &(indexToImport->mAltemulatorIndexAllKeys)}, }; @@ -119,6 +122,7 @@ void FileFilterIndex::resetIndex() clearIndex(mKidGameIndexAllKeys); clearIndex(mHiddenIndexAllKeys); clearIndex(mBrokenIndexAllKeys); + clearIndex(mControllerIndexAllKeys); clearIndex(mAltemulatorIndexAllKeys); } @@ -215,6 +219,12 @@ std::string FileFilterIndex::getIndexableKey(FileData* game, key = Utils::String::toUpper(game->metadata.get("broken")); break; } + case CONTROLLER_FILTER: { + if (getSecondary) + break; + key = Utils::String::toUpper(game->metadata.get("controller")); + break; + } case ALTEMULATOR_FILTER: { if (getSecondary) break; @@ -231,8 +241,8 @@ std::string FileFilterIndex::getIndexableKey(FileData* game, type == PUBLISHER_FILTER) && Utils::String::toUpper(key) == UNKNOWN_LABEL) key = ViewController::CROSSEDCIRCLE_CHAR + " UNKNOWN"; - else if (type == ALTEMULATOR_FILTER && key.empty()) - key = ViewController::CROSSEDCIRCLE_CHAR + " NONE DEFINED"; + else if ((type == CONTROLLER_FILTER || type == ALTEMULATOR_FILTER) && key.empty()) + key = ViewController::CROSSEDCIRCLE_CHAR + " NONE SELECTED"; else if (key.empty() || (type == RATINGS_FILTER && key == "0 STARS")) key = UNKNOWN_LABEL; @@ -251,6 +261,7 @@ void FileFilterIndex::addToIndex(FileData* game) manageKidGameEntryInIndex(game); manageHiddenEntryInIndex(game); manageBrokenEntryInIndex(game); + manageControllerEntryInIndex(game); manageAltemulatorEntryInIndex(game); } @@ -266,6 +277,7 @@ void FileFilterIndex::removeFromIndex(FileData* game) manageKidGameEntryInIndex(game, true); manageHiddenEntryInIndex(game, true); manageBrokenEntryInIndex(game, true); + manageControllerEntryInIndex(game, true); manageAltemulatorEntryInIndex(game, true); } @@ -365,6 +377,9 @@ void FileFilterIndex::debugPrintIndexes() for (auto x : mBrokenIndexAllKeys) { LOG(LogInfo) << "Broken Index: " << x.first << ": " << x.second; } + for (auto x : mControllerIndexAllKeys) { + LOG(LogInfo) << "Controller Index: " << x.first << ": " << x.second; + } for (auto x : mAltemulatorIndexAllKeys) { LOG(LogInfo) << "Altemulator Index: " << x.first << ": " << x.second; } @@ -444,28 +459,29 @@ bool FileFilterIndex::isFiltered() if (UIModeController::getInstance()->isUIModeKid()) { return (mFilterByText || mFilterByRatings || mFilterByDeveloper || mFilterByPublisher || mFilterByGenre || mFilterByPlayers || mFilterByFavorites || mFilterByCompleted || - mFilterByHidden || mFilterByBroken || mFilterByAltemulator); + mFilterByHidden || mFilterByBroken || mFilterByController || mFilterByAltemulator); } else { return (mFilterByText || mFilterByRatings || mFilterByDeveloper || mFilterByPublisher || mFilterByGenre || mFilterByPlayers || mFilterByFavorites || mFilterByCompleted || - mFilterByKidGame || mFilterByHidden || mFilterByBroken || mFilterByAltemulator); + mFilterByKidGame || mFilterByHidden || mFilterByBroken || mFilterByController || + mFilterByAltemulator); } } bool FileFilterIndex::isKeyBeingFilteredBy(std::string key, FilterIndexType type) { - const FilterIndexType filterTypes[11] = {RATINGS_FILTER, DEVELOPER_FILTER, PUBLISHER_FILTER, - GENRE_FILTER, PLAYER_FILTER, FAVORITES_FILTER, - COMPLETED_FILTER, KIDGAME_FILTER, HIDDEN_FILTER, - BROKEN_FILTER, ALTEMULATOR_FILTER}; - std::vector filterKeysList[11] = { + const FilterIndexType filterTypes[12] = { + RATINGS_FILTER, DEVELOPER_FILTER, PUBLISHER_FILTER, GENRE_FILTER, + PLAYER_FILTER, FAVORITES_FILTER, COMPLETED_FILTER, KIDGAME_FILTER, + HIDDEN_FILTER, BROKEN_FILTER, CONTROLLER_FILTER, ALTEMULATOR_FILTER}; + std::vector filterKeysList[12] = { mRatingsIndexFilteredKeys, mDeveloperIndexFilteredKeys, mPublisherIndexFilteredKeys, mGenreIndexFilteredKeys, mPlayersIndexFilteredKeys, mFavoritesIndexFilteredKeys, mCompletedIndexFilteredKeys, mKidGameIndexFilteredKeys, mHiddenIndexFilteredKeys, - mBrokenIndexFilteredKeys, mAltemulatorIndexFilteredKeys}; + mBrokenIndexFilteredKeys, mControllerIndexFilteredKeys, mAltemulatorIndexFilteredKeys}; - for (int i = 0; i < 11; i++) { + for (int i = 0; i < 12; i++) { if (filterTypes[i] == type) { for (std::vector::const_iterator it = filterKeysList[i].cbegin(); it != filterKeysList[i].cend(); it++) { @@ -611,6 +627,12 @@ void FileFilterIndex::manageBrokenEntryInIndex(FileData* game, bool remove) manageIndexEntry(&mBrokenIndexAllKeys, key, remove); } +void FileFilterIndex::manageControllerEntryInIndex(FileData* game, bool remove) +{ + std::string key = getIndexableKey(game, CONTROLLER_FILTER, false); + manageIndexEntry(&mControllerIndexAllKeys, key, remove); +} + void FileFilterIndex::manageAltemulatorEntryInIndex(FileData* game, bool remove) { std::string key = getIndexableKey(game, ALTEMULATOR_FILTER, false); diff --git a/es-app/src/FileFilterIndex.h b/es-app/src/FileFilterIndex.h index d4d744a7f..975c58432 100644 --- a/es-app/src/FileFilterIndex.h +++ b/es-app/src/FileFilterIndex.h @@ -31,6 +31,7 @@ enum FilterIndexType { KIDGAME_FILTER, HIDDEN_FILTER, BROKEN_FILTER, + CONTROLLER_FILTER, ALTEMULATOR_FILTER }; @@ -82,6 +83,7 @@ private: void manageKidGameEntryInIndex(FileData* game, bool remove = false); void manageHiddenEntryInIndex(FileData* game, bool remove = false); void manageBrokenEntryInIndex(FileData* game, bool remove = false); + void manageControllerEntryInIndex(FileData* game, bool remove = false); void manageAltemulatorEntryInIndex(FileData* game, bool remove = false); void manageIndexEntry(std::map* index, std::string key, bool remove); @@ -102,6 +104,7 @@ private: bool mFilterByKidGame; bool mFilterByHidden; bool mFilterByBroken; + bool mFilterByController; bool mFilterByAltemulator; std::map mRatingsIndexAllKeys; @@ -114,6 +117,7 @@ private: std::map mKidGameIndexAllKeys; std::map mHiddenIndexAllKeys; std::map mBrokenIndexAllKeys; + std::map mControllerIndexAllKeys; std::map mAltemulatorIndexAllKeys; std::vector mRatingsIndexFilteredKeys; @@ -126,6 +130,7 @@ private: std::vector mKidGameIndexFilteredKeys; std::vector mHiddenIndexFilteredKeys; std::vector mBrokenIndexFilteredKeys; + std::vector mControllerIndexFilteredKeys; std::vector mAltemulatorIndexFilteredKeys; }; diff --git a/es-app/src/guis/GuiGamelistFilter.cpp b/es-app/src/guis/GuiGamelistFilter.cpp index 6feda35e7..7721e38bf 100644 --- a/es-app/src/guis/GuiGamelistFilter.cpp +++ b/es-app/src/guis/GuiGamelistFilter.cpp @@ -11,9 +11,11 @@ #include "guis/GuiGamelistFilter.h" #include "SystemData.h" +#include "components/BadgeComponent.h" #include "components/OptionListComponent.h" #include "guis/GuiTextEditKeyboardPopup.h" #include "guis/GuiTextEditPopup.h" +#include "utils/StringUtil.h" #include "views/UIModeController.h" #include "views/ViewController.h" @@ -185,8 +187,21 @@ void GuiGamelistFilter::addFiltersToMenu() optionList->setOverrideMultiText("NOTHING TO FILTER"); } - for (auto it : *allKeys) - optionList->add(it.first, it.first, mFilterIndex->isKeyBeingFilteredBy(it.first, type)); + if (type == CONTROLLER_FILTER) { + for (auto it : *allKeys) { + std::string displayName = + BadgeComponent::getDisplayName(Utils::String::toLower(it.first)); + if (displayName == "unknown") + displayName = it.first; + optionList->add(displayName, it.first, + mFilterIndex->isKeyBeingFilteredBy(it.first, type)); + } + } + else { + for (auto it : *allKeys) + optionList->add(it.first, it.first, + mFilterIndex->isKeyBeingFilteredBy(it.first, type)); + } if (allKeys->size() == 0) optionList->add("", "", false); From 593cfdbdd72ffae58073d67fec50dfda12792e19 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sun, 24 Oct 2021 14:11:19 +0200 Subject: [PATCH 36/60] Fixed multiple issues where menu sizes were not properly initialized. --- es-app/src/guis/GuiMenu.cpp | 6 ++++++ es-app/src/guis/GuiScreensaverOptions.cpp | 5 +++++ es-app/src/guis/GuiScreensaverOptions.h | 2 ++ 3 files changed, 13 insertions(+) diff --git a/es-app/src/guis/GuiMenu.cpp b/es-app/src/guis/GuiMenu.cpp index ffa9cd0f2..58bf31976 100644 --- a/es-app/src/guis/GuiMenu.cpp +++ b/es-app/src/guis/GuiMenu.cpp @@ -593,6 +593,7 @@ void GuiMenu::openUIOptions() } }); + s->setSize(mSize); mWindow->pushGui(s); } @@ -700,6 +701,7 @@ void GuiMenu::openSoundOptions() }); } + s->setSize(mSize); mWindow->pushGui(s); } @@ -756,6 +758,7 @@ void GuiMenu::openInputDeviceOptions() configure_input_row.makeAcceptInputHandler(std::bind(&GuiMenu::openConfigInput, this, s)); s->addRow(configure_input_row); + s->setSize(mSize); mWindow->pushGui(s); } @@ -1180,12 +1183,14 @@ void GuiMenu::openOtherOptions() run_in_background->setCallback(launchWorkaroundToggleFunc); #endif + s->setSize(mSize); mWindow->pushGui(s); } void GuiMenu::openUtilitiesMenu() { auto s = new GuiSettings(mWindow, "UTILITIES"); + s->setSize(mSize); mWindow->pushGui(s); } @@ -1263,6 +1268,7 @@ void GuiMenu::openQuitMenu() row.addElement(powerOffText, true); s->addRow(row); + s->setSize(mSize); mWindow->pushGui(s); } } diff --git a/es-app/src/guis/GuiScreensaverOptions.cpp b/es-app/src/guis/GuiScreensaverOptions.cpp index ea6eaf037..cb5bffae2 100644 --- a/es-app/src/guis/GuiScreensaverOptions.cpp +++ b/es-app/src/guis/GuiScreensaverOptions.cpp @@ -17,6 +17,7 @@ GuiScreensaverOptions::GuiScreensaverOptions(Window* window, const std::string& title) : GuiSettings(window, title) + , mWindow(window) { // Screensaver timer. auto screensaver_timer = std::make_shared(mWindow, 0.0f, 30.0f, 1.0f, "m"); @@ -95,6 +96,8 @@ GuiScreensaverOptions::GuiScreensaverOptions(Window* window, const std::string& row.makeAcceptInputHandler( std::bind(&GuiScreensaverOptions::openVideoScreensaverOptions, this)); addRow(row); + + setSize(getMenuSize()); } void GuiScreensaverOptions::openSlideshowScreensaverOptions() @@ -206,6 +209,7 @@ void GuiScreensaverOptions::openSlideshowScreensaverOptions() } }); + s->setSize(mSize); mWindow->pushGui(s); } @@ -287,5 +291,6 @@ void GuiScreensaverOptions::openVideoScreensaverOptions() }); #endif + s->setSize(mSize); mWindow->pushGui(s); } diff --git a/es-app/src/guis/GuiScreensaverOptions.h b/es-app/src/guis/GuiScreensaverOptions.h index 9423ab6fe..1fe1993e3 100644 --- a/es-app/src/guis/GuiScreensaverOptions.h +++ b/es-app/src/guis/GuiScreensaverOptions.h @@ -18,6 +18,8 @@ public: GuiScreensaverOptions(Window* window, const std::string& title); private: + Window* mWindow; + void openSlideshowScreensaverOptions(); void openVideoScreensaverOptions(); }; From 71b5c50b8594814f2e34e485aa15639390ac9844 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sun, 24 Oct 2021 14:17:43 +0200 Subject: [PATCH 37/60] Made the SliderComponent width consistent across different screen aspect ratios. --- es-core/src/components/SliderComponent.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/es-core/src/components/SliderComponent.cpp b/es-core/src/components/SliderComponent.cpp index 07eb7d36c..77f837529 100644 --- a/es-core/src/components/SliderComponent.cpp +++ b/es-core/src/components/SliderComponent.cpp @@ -8,6 +8,7 @@ #include "components/SliderComponent.h" +#include "Window.h" #include "resources/Font.h" #define MOVE_REPEAT_DELAY 500 @@ -32,7 +33,7 @@ SliderComponent::SliderComponent( mKnob.setOrigin(0.5f, 0.5f); mKnob.setImage(":/graphics/slider_knob.svg"); - setSize(Renderer::getScreenWidth() * 0.15f, Font::get(FONT_SIZE_MEDIUM)->getLetterHeight()); + setSize(window->peekGui()->getSize().x * 0.26f, Font::get(FONT_SIZE_MEDIUM)->getLetterHeight()); } bool SliderComponent::input(InputConfig* config, Input input) From b83db7d33e0c06259d2bd19f75908612a64d6bb0 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sun, 24 Oct 2021 14:48:42 +0200 Subject: [PATCH 38/60] Added text debug overlay to SliderComponent. Also changed a variable name to align with the standard naming conventions. --- es-core/src/components/SliderComponent.cpp | 26 ++++++++++++++-------- es-core/src/components/SliderComponent.h | 2 +- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/es-core/src/components/SliderComponent.cpp b/es-core/src/components/SliderComponent.cpp index 77f837529..ddcb981e1 100644 --- a/es-core/src/components/SliderComponent.cpp +++ b/es-core/src/components/SliderComponent.cpp @@ -81,15 +81,23 @@ void SliderComponent::render(const glm::mat4& parentTrans) glm::mat4 trans{parentTrans * getTransform()}; Renderer::setMatrix(trans); - // Render suffix. - if (mValueCache) - mFont->renderTextCache(mValueCache.get()); + if (Settings::getInstance()->getBool("DebugText")) { + Renderer::drawRect(mSize.x - mTextCache->metrics.size.x, 0.0f, mTextCache->metrics.size.x, + mSize.y, 0x0000FF33, 0x0000FF33); + Renderer::drawRect( + mSize.x - mTextCache->metrics.size.x, (mSize.y - mTextCache->metrics.size.y) / 2.0f, + mTextCache->metrics.size.x, mTextCache->metrics.size.y, 0x0000FF33, 0x0000FF33); + } float width{mSize.x - mKnob.getSize().x - - (mValueCache ? - mValueCache->metrics.size.x + (4.0f * Renderer::getScreenWidthModifier()) : + (mTextCache ? + mTextCache->metrics.size.x + (4.0f * Renderer::getScreenWidthModifier()) : 0.0f)}; + // Render suffix. + if (mTextCache) + mFont->renderTextCache(mTextCache.get()); + // Render bar. Renderer::drawRect(mKnob.getSize().x / 2.0f, mSize.y / 2.0f - mBarHeight / 2.0f, width, mBarHeight, 0x777777FF, 0x777777FF); @@ -139,17 +147,17 @@ void SliderComponent::onValueChanged() const std::string max = ss.str(); glm::vec2 textSize = mFont->sizeText(max); - mValueCache = std::shared_ptr(mFont->buildTextCache( + mTextCache = std::shared_ptr(mFont->buildTextCache( val, mSize.x - textSize.x, (mSize.y - textSize.y) / 2.0f, 0x777777FF)); - mValueCache->metrics.size.x = textSize.x; // Fudge the width. + mTextCache->metrics.size.x = textSize.x; // Fudge the width. } 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); + (mTextCache ? mTextCache->metrics.size.x + (4.0f * Renderer::getScreenWidthModifier()) : + 0.0f); int barHeight = static_cast(std::round(2.0f * Renderer::getScreenHeightModifier())); diff --git a/es-core/src/components/SliderComponent.h b/es-core/src/components/SliderComponent.h index 9392364d9..2ca75cb9a 100644 --- a/es-core/src/components/SliderComponent.h +++ b/es-core/src/components/SliderComponent.h @@ -52,7 +52,7 @@ private: std::string mSuffix; std::shared_ptr mFont; - std::shared_ptr mValueCache; + std::shared_ptr mTextCache; }; #endif // ES_CORE_COMPONENTS_SLIDER_COMPONENT_H From 960f6e56be5b02409bd3055726005239f6328545 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sun, 24 Oct 2021 14:53:53 +0200 Subject: [PATCH 39/60] Fixed an incorrect text debug overlay color in SliderComponent. --- es-core/src/components/SliderComponent.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/es-core/src/components/SliderComponent.cpp b/es-core/src/components/SliderComponent.cpp index ddcb981e1..bcf0ca9d9 100644 --- a/es-core/src/components/SliderComponent.cpp +++ b/es-core/src/components/SliderComponent.cpp @@ -82,11 +82,11 @@ void SliderComponent::render(const glm::mat4& parentTrans) Renderer::setMatrix(trans); if (Settings::getInstance()->getBool("DebugText")) { - Renderer::drawRect(mSize.x - mTextCache->metrics.size.x, 0.0f, mTextCache->metrics.size.x, - mSize.y, 0x0000FF33, 0x0000FF33); Renderer::drawRect( mSize.x - mTextCache->metrics.size.x, (mSize.y - mTextCache->metrics.size.y) / 2.0f, mTextCache->metrics.size.x, mTextCache->metrics.size.y, 0x0000FF33, 0x0000FF33); + Renderer::drawRect(mSize.x - mTextCache->metrics.size.x, 0.0f, mTextCache->metrics.size.x, + mSize.y, 0x00000033, 0x00000033); } float width{mSize.x - mKnob.getSize().x - From 0e959c3c613231616d78f24f16b2810ef0c27da4 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sun, 24 Oct 2021 18:20:56 +0200 Subject: [PATCH 40/60] Made the 'switch' system logo smaller. --- themes/rbsimple-DE/switch/images/logo.svg | 32 +++++++++++++---------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/themes/rbsimple-DE/switch/images/logo.svg b/themes/rbsimple-DE/switch/images/logo.svg index 04414e24f..5ca4ddafb 100644 --- a/themes/rbsimple-DE/switch/images/logo.svg +++ b/themes/rbsimple-DE/switch/images/logo.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="2922" - height="644" + width="800" + height="176.36418" version="1.1" - viewBox="0 0 2922 644" + viewBox="0 0 800 176.36418" id="svg2" sodipodi:docname="logo.svg" inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"> @@ -41,31 +41,35 @@ inkscape:window-height="2065" id="namedview4" showgrid="false" - inkscape:zoom="0.30969238" - inkscape:cx="338.77838" - inkscape:cy="458.73741" + inkscape:zoom="2.184783" + inkscape:cx="366.47128" + inkscape:cy="193.80534" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" - inkscape:current-layer="svg2" /> + inkscape:current-layer="svg2" + fit-margin-top="0" + fit-margin-left="0" + fit-margin-right="0" + fit-margin-bottom="0" /> + d="M 37.76031,0.58425297 C 20.19122,3.7246229 6.0555905,16.724283 1.5993975,33.813713 c -1.60715171,6.17119 -1.71673023,9.78626 -1.53409936,57.6586 0.10957852,43.965127 0.14610469,44.987577 0.87662817,48.383547 4.05440539,18.29447 17.05772269,31.33064 35.53996669,35.63952 2.410727,0.54774 5.478927,0.65728 25.239586,0.76683 20.454656,0.14606 22.573174,0.10955 23.121068,-0.43819 0.547893,-0.54774 0.584418,-7.5953 0.584418,-87.492077 0,-59.22878 -0.109579,-87.1269201 -0.365262,-87.63814003 -0.365262,-0.65729 -0.986206,-0.6938000088983 -22.390543,-0.65729 -17.386458,0.0365 -22.609702,0.14607 -24.91085,0.54774 z M 70.816496,88.222393 v 73.981197 l -14.866152,-0.18258 c -13.697316,-0.14606 -15.158362,-0.21909 -17.897826,-0.91289 -11.761427,-3.03082 -20.491183,-12.08677 -22.938436,-23.8814 -0.803576,-3.6881 -0.803576,-94.539647 -0.03653,-98.154717 2.191571,-10.26096 9.314175,-18.76917 18.920557,-22.60334 4.821456,-1.93534 7.049552,-2.15443 22.682754,-2.19095 l 14.13563,-0.0365 z" /> + d="m 40.901559,36.552403 c -2.301147,0.4382 -5.807661,2.19096 -7.670494,3.83417 -3.83525,3.32295 -5.734609,8.0335 -5.442401,13.58392 0.146105,2.88475 0.328736,3.65158 1.497574,5.9886 1.716728,3.54204 4.310086,6.13467 7.853125,7.88744 2.447255,1.20502 3.0682,1.35108 6.245977,1.46063 2.885568,0.10955 3.908301,0 5.844189,-0.65729 7.926179,-2.66565 12.711107,-10.37051 11.359638,-18.25794 -1.570626,-9.38459 -10.483011,-15.66532 -19.687608,-13.83953 z" /> + d="m 103.21521,0.25561297 c -0.14611,0.10955 -0.25568,39.69277003 -0.25568,87.96678003 0,79.641157 0.0365,87.711177 0.58441,87.930267 0.98621,0.36516 29.33052,0.2191 32.83703,-0.14606 14.82963,-1.67973 27.906,-10.69916 35.0286,-24.10049 0.91316,-1.71625 2.11852,-4.601 2.73947,-6.39028 2.30115,-6.86499 2.22809,-5.03919 2.22809,-57.512527 0,-41.88373 -0.073,-48.12795 -0.58441,-50.79361 C 172.17662,18.184913 157.67573,3.8341629 138.60907,0.54773297 136.05224,0.10954297 132.10741,2.9611016e-6 119.3963,2.9611016e-6 110.63002,2.9611016e-6 103.32479,0.10954297 103.21521,0.25561297 Z M 142.22516,79.787223 c 5.69808,1.49715 10.37343,5.84254 12.2728,11.39296 1.20536,3.4325 1.16883,8.47169 -0.0365,11.612047 -2.2281,5.76952 -6.61124,9.82278 -12.23627,11.31993 -9.13154,2.37353 -18.84751,-3.14037 -21.51391,-12.19631 -0.80358,-2.775207 -0.76705,-7.449237 0.1461,-10.297477 2.73946,-8.94639 12.23627,-14.20468 21.36781,-11.83115 z" /> + transform="matrix(0.62745924,0,0,0.62745924,-274.21022,-113.86845)"> Date: Sun, 24 Oct 2021 18:21:36 +0200 Subject: [PATCH 41/60] (rbsimple-DE) Increased the size of the controller icons. --- themes/rbsimple-DE/theme.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/themes/rbsimple-DE/theme.xml b/themes/rbsimple-DE/theme.xml index 130467fbc..7d543fe59 100644 --- a/themes/rbsimple-DE/theme.xml +++ b/themes/rbsimple-DE/theme.xml @@ -244,7 +244,7 @@ based on: 'recalbox-multi' by the Recalbox community 3 2 0.5 0.572 - 0.67 + 0.81 -1.0 0.005 favorite, completed, kidgame, broken, controller, altemulator From 3ea255731495cee80338fbc0b637e67dc2909d92 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sun, 24 Oct 2021 18:23:13 +0200 Subject: [PATCH 42/60] Added some controller graphics. --- .../controllers/gamepad_nintendo_64.svg | 251 +++++++++++++- .../controllers/gamepad_nintendo_nes.svg | 289 +++++++++++++++- .../controllers/gamepad_nintendo_snes.svg | 213 +++++++++++- .../controllers/gamepad_playstation.svg | 118 ++++++- .../joycon_left_or_right_nintendo.svg | 269 ++++++++++++++- .../controllers/joycon_pair_nintendo.svg | 267 +++++++++++++- .../graphics/controllers/mouse_amiga.svg | 67 +++- .../controllers/wii_remote_nintendo.svg | 289 +++++++++++++++- .../wii_remote_nunchuck_nintendo.svg | 325 +++++++++++++++++- 9 files changed, 1976 insertions(+), 112 deletions(-) diff --git a/resources/graphics/controllers/gamepad_nintendo_64.svg b/resources/graphics/controllers/gamepad_nintendo_64.svg index b6b01c15a..48c9bf4e8 100644 --- a/resources/graphics/controllers/gamepad_nintendo_64.svg +++ b/resources/graphics/controllers/gamepad_nintendo_64.svg @@ -15,9 +15,34 @@ version="1.1" id="svg4925" inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)" - sodipodi:docname="unknown.svg"> + sodipodi:docname="gamepad_nintendo_64.svg"> + id="defs4919"> + + + + + + + + + + + inkscape:window-maximized="1" + inkscape:snap-bbox="false" /> @@ -55,14 +81,213 @@ id="layer1" transform="translate(0,-285.88748)"> + id="g6500"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/graphics/controllers/gamepad_nintendo_nes.svg b/resources/graphics/controllers/gamepad_nintendo_nes.svg index b6b01c15a..e546a52eb 100644 --- a/resources/graphics/controllers/gamepad_nintendo_nes.svg +++ b/resources/graphics/controllers/gamepad_nintendo_nes.svg @@ -15,7 +15,7 @@ version="1.1" id="svg4925" inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)" - sodipodi:docname="unknown.svg"> + sodipodi:docname="gamepad_nintendo_nes.svg"> + inkscape:window-maximized="1" + inkscape:snap-global="false" /> @@ -55,14 +56,278 @@ id="layer1" transform="translate(0,-285.88748)"> + id="g5404" + transform="matrix(0.98413614,0,0,0.98413614,0.08814355,4.6234203)"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/graphics/controllers/gamepad_nintendo_snes.svg b/resources/graphics/controllers/gamepad_nintendo_snes.svg index b6b01c15a..f4e823894 100644 --- a/resources/graphics/controllers/gamepad_nintendo_snes.svg +++ b/resources/graphics/controllers/gamepad_nintendo_snes.svg @@ -2,6 +2,7 @@ + sodipodi:docname="gamepad_nintendo_snes.svg"> + id="g5490" + transform="translate(-1e-7,3.169167e-6)"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/graphics/controllers/gamepad_playstation.svg b/resources/graphics/controllers/gamepad_playstation.svg index b6b01c15a..5b5c4016e 100644 --- a/resources/graphics/controllers/gamepad_playstation.svg +++ b/resources/graphics/controllers/gamepad_playstation.svg @@ -15,9 +15,18 @@ version="1.1" id="svg4925" inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)" - sodipodi:docname="unknown.svg"> + sodipodi:docname="gamepad_playstation.svg"> + id="defs4919"> + + + + + id="g14725" + transform="translate(4.3347377e-7,2.6668856e-5)"> + inkscape:connector-curvature="0" + id="path30-4" + d="m 2.918825,288.73845 c -0.3626261,0 -0.616968,0.0767 -0.6457872,0.22565 -0.01881,0.097 -0.050224,0.30821 -0.078304,0.49129 -0.41776,0.24637 -0.6988306,0.69994 -0.6988306,1.22 0,0.0214 0.00244,0.0421 0.00334,0.0631 -0.162574,0.97662 -0.2776564,1.9435 -0.3645706,2.39876 -0.15520473,0.81336 1.2039422,1.49728 1.7192908,0.55737 0.2440554,-0.44511 0.4645608,-0.87975 0.6276849,-1.20611 0.1572849,0.27068 0.4470001,0.45466 0.7826057,0.45466 0.3725061,0 0.6916645,-0.22473 0.8318607,-0.54559 h 0.9038486 c 0.1406855,0.31961 0.458946,0.5435 0.8305979,0.5435 0.3414904,0 0.6363897,-0.19031 0.7914465,-0.46899 0.164161,0.32858 0.3886006,0.7719 0.6356832,1.22253 0.5153483,0.93993 1.8802691,0.25485 1.7192919,-0.55737 -0.088057,-0.44424 -0.2047877,-1.44363 -0.3708866,-2.43665 9.23e-5,-0.006 8.063e-4,-0.0131 8.063e-4,-0.0198 0,-0.53576 -0.2975275,-1.00202 -0.7362977,-1.24274 -0.026471,-0.17238 -0.057632,-0.38042 -0.075777,-0.47403 -0.028798,-0.14887 -0.2831807,-0.22565 -0.6457867,-0.22565 -0.3626265,0 -0.6169468,0.0768 -0.6457865,0.22565 -0.020408,0.10559 -0.053047,0.32285 -0.085459,0.53464 -0.043019,0.0282 -0.087115,0.055 -0.1267154,0.0876 H 5.8530379 c -0.010012,0.0164 -0.028546,0.0278 -0.049255,0.0278 H 5.2775551 c -0.020687,0 -0.03798,-0.0113 -0.047992,-0.0278 h -1.411553 c -0.054123,-0.045 -0.1129686,-0.0841 -0.1734445,-0.12082 -0.029054,-0.1895 -0.060403,-0.40239 -0.079566,-0.5014 -0.028798,-0.14887 -0.2836438,-0.22565 -0.6462072,-0.22565 z" + style="fill:#c2c2c2;fill-opacity:1;fill-rule:nonzero;stroke:#010101;stroke-width:0.12790291;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + + + + + + + + + + + + + + + + + diff --git a/resources/graphics/controllers/joycon_left_or_right_nintendo.svg b/resources/graphics/controllers/joycon_left_or_right_nintendo.svg index b6b01c15a..e4e991891 100644 --- a/resources/graphics/controllers/joycon_left_or_right_nintendo.svg +++ b/resources/graphics/controllers/joycon_left_or_right_nintendo.svg @@ -15,7 +15,7 @@ version="1.1" id="svg4925" inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)" - sodipodi:docname="unknown.svg"> + sodipodi:docname="joycon_left_or_right_nintendo.svg"> image/svg+xml - + @@ -55,15 +55,258 @@ id="layer1" transform="translate(0,-285.88748)"> + id="g5555-7" + transform="translate(-0.02566855,1.3229153)"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + inkscape:transform-center-y="-0.036071626" + d="m 2.9824786,291.04147 0.1249556,0.21643 -0.2499112,0 z" + inkscape:randomized="0" + inkscape:rounded="0" + inkscape:flatsided="true" + sodipodi:arg2="-0.52359878" + sodipodi:arg1="-1.5707963" + sodipodi:r2="0.072143152" + sodipodi:r1="0.1442863" + sodipodi:cy="291.18576" + sodipodi:cx="2.9824786" + sodipodi:sides="3" + id="path5401-0" + style="opacity:1;vector-effect:none;fill:#282828;fill-opacity:1;stroke:none;stroke-width:0.08117354;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal" + sodipodi:type="star" /> + + + diff --git a/resources/graphics/controllers/joycon_pair_nintendo.svg b/resources/graphics/controllers/joycon_pair_nintendo.svg index b6b01c15a..37aec1f15 100644 --- a/resources/graphics/controllers/joycon_pair_nintendo.svg +++ b/resources/graphics/controllers/joycon_pair_nintendo.svg @@ -15,7 +15,7 @@ version="1.1" id="svg4925" inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)" - sodipodi:docname="unknown.svg"> + sodipodi:docname="joycon_pair_nintendo.svg"> + id="g5555" + transform="translate(-0.02566855,-1.3888992e-6)"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + inkscape:transform-center-y="-0.036071626" + d="m 2.9824786,291.04147 0.1249556,0.21643 -0.2499112,0 z" + inkscape:randomized="0" + inkscape:rounded="0" + inkscape:flatsided="true" + sodipodi:arg2="-0.52359878" + sodipodi:arg1="-1.5707963" + sodipodi:r2="0.072143152" + sodipodi:r1="0.1442863" + sodipodi:cy="291.18576" + sodipodi:cx="2.9824786" + sodipodi:sides="3" + id="path5401" + style="opacity:1;vector-effect:none;fill:#282828;fill-opacity:1;stroke:none;stroke-width:0.08117354;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal" + sodipodi:type="star" /> + + + diff --git a/resources/graphics/controllers/mouse_amiga.svg b/resources/graphics/controllers/mouse_amiga.svg index b6b01c15a..696cbd88d 100644 --- a/resources/graphics/controllers/mouse_amiga.svg +++ b/resources/graphics/controllers/mouse_amiga.svg @@ -15,7 +15,7 @@ version="1.1" id="svg4925" inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)" - sodipodi:docname="unknown.svg"> + sodipodi:docname="mouse_amiga.svg"> + inkscape:window-maximized="1" + inkscape:snap-bbox="false" + inkscape:snap-global="false" /> @@ -55,15 +57,54 @@ id="layer1" transform="translate(0,-285.88748)"> + id="g5117"> + inkscape:connector-curvature="0" + id="rect4891" + d="m 5.4289836,285.88748 h 0.2545328 v 2.06353 H 5.4289836 Z" + style="opacity:1;vector-effect:none;fill:#60605d;fill-opacity:1;stroke:none;stroke-width:0.04231874;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal" /> + + + + + + + + + + + diff --git a/resources/graphics/controllers/wii_remote_nintendo.svg b/resources/graphics/controllers/wii_remote_nintendo.svg index b6b01c15a..c8a5055c7 100644 --- a/resources/graphics/controllers/wii_remote_nintendo.svg +++ b/resources/graphics/controllers/wii_remote_nintendo.svg @@ -15,7 +15,7 @@ version="1.1" id="svg4925" inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)" - sodipodi:docname="unknown.svg"> + sodipodi:docname="wii_remote_nintendo.svg"> + id="g5266"> + inkscape:connector-curvature="0" + id="path6" + d="m 4.492749,286.76443 c -0.150954,0 -0.274402,0.12397 -0.274402,0.27492 v 9.05681 c 0,0.15095 0.123448,0.27492 0.274402,0.27492 h 2.1270018 c 0.1509541,0 0.2744021,-0.12397 0.2744021,-0.27492 v -9.05681 c 0,-0.15095 -0.123448,-0.27492 -0.2744021,-0.27492 z" + style="fill:#f8f8f8;stroke:#000000;stroke-width:0.079375;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;fill-opacity:1" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/graphics/controllers/wii_remote_nunchuck_nintendo.svg b/resources/graphics/controllers/wii_remote_nunchuck_nintendo.svg index b6b01c15a..d9699c383 100644 --- a/resources/graphics/controllers/wii_remote_nunchuck_nintendo.svg +++ b/resources/graphics/controllers/wii_remote_nunchuck_nintendo.svg @@ -15,7 +15,7 @@ version="1.1" id="svg4925" inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)" - sodipodi:docname="unknown.svg"> + sodipodi:docname="wii_remote_nunchuck_nintendo.svg"> + inkscape:window-maximized="1" + inkscape:snap-bbox="true" /> @@ -55,15 +56,313 @@ id="layer1" transform="translate(0,-285.88748)"> + id="g5629" + transform="matrix(0.97763205,0,0,0.97763205,0.12423766,6.4933408)"> + id="path188" + d="m 3.330473,296.25878 c -0.6814899,0 -0.8835396,-1.82872 -1.0821648,-3.5958 -0.034246,-0.29793 -0.068491,-0.60615 -0.1027371,-0.89723 -0.1678041,-1.35271 0.1335583,-2.01708 0.4177977,-2.33556 0.2260218,-0.25684 0.5239596,-0.3904 0.8629922,-0.3904 0.3390326,0 0.6369704,0.13356 0.8629921,0.3904 0.2808149,0.31848 0.5821773,0.98285 0.4177978,2.33556 -0.034246,0.29108 -0.068491,0.5993 -0.1027372,0.89723 -0.1952006,1.76708 -0.4006749,3.5958 -1.0821647,3.5958 z" + inkscape:connector-curvature="0" + style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.10556875;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 5b7483c4d73dedeec94900fe3b2cccf312b6965f Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sun, 24 Oct 2021 18:30:23 +0200 Subject: [PATCH 43/60] Adjusted two controller graphics files. --- .../joycon_left_or_right_nintendo.svg | 136 +++++++++--------- .../controllers/joycon_pair_nintendo.svg | 78 +++++----- 2 files changed, 113 insertions(+), 101 deletions(-) diff --git a/resources/graphics/controllers/joycon_left_or_right_nintendo.svg b/resources/graphics/controllers/joycon_left_or_right_nintendo.svg index e4e991891..3a8554f7d 100644 --- a/resources/graphics/controllers/joycon_left_or_right_nintendo.svg +++ b/resources/graphics/controllers/joycon_left_or_right_nintendo.svg @@ -25,9 +25,9 @@ borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" - inkscape:zoom="24.884308" - inkscape:cx="13.368346" - inkscape:cy="25.181666" + inkscape:zoom="17.595863" + inkscape:cx="-0.77044279" + inkscape:cy="30.632636" inkscape:document-units="mm" inkscape:current-layer="layer1" showgrid="false" @@ -55,83 +55,84 @@ id="layer1" transform="translate(0,-285.88748)"> + id="g4910" + transform="matrix(1.0578414,0,0,1.0578414,-0.32138125,-15.666888)"> + transform="translate(-3.8154512,-1.3888992e-6)" + id="g5517"> + transform="translate(0.07832299,-1.3888992e-6)" + id="g5361"> + transform="translate(-0.58238277,-1.3888992e-6)" + id="g5361-2"> + id="g4874" + transform="matrix(1.0578414,0,0,1.0578414,-0.32138125,-18.048138)"> + transform="translate(-0.02566855,-1.3888992e-6)" + id="g5474"> diff --git a/resources/graphics/controllers/joycon_pair_nintendo.svg b/resources/graphics/controllers/joycon_pair_nintendo.svg index 37aec1f15..649377f0a 100644 --- a/resources/graphics/controllers/joycon_pair_nintendo.svg +++ b/resources/graphics/controllers/joycon_pair_nintendo.svg @@ -17,7 +17,15 @@ inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)" sodipodi:docname="joycon_pair_nintendo.svg"> + id="defs4919"> + + + id="g4910" + transform="matrix(1.0578414,0,0,1.0578414,-0.32138125,-16.857513)"> + id="g4874" + transform="matrix(1.0578414,0,0,1.0578414,-0.32138125,-16.857513)"> Date: Sun, 24 Oct 2021 18:38:14 +0200 Subject: [PATCH 44/60] Documentation update. --- CHANGELOG.md | 3 +++ CONTRIBUTING.md | 2 +- INSTALL-DEV.md | 2 ++ USERGUIDE-DEV.md | 10 ++++++---- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3069bc8f5..1322caa21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,6 +56,8 @@ * Added support for a new type of "flat style" button to ButtonComponent * Added support for correctly navigating arbitrarily sized ComponentGrid entries, i.e. those spanning multiple cells * Bundled the bold font version of Fontfabric Akrobat +* Moved the resources/help directory to resources/graphics/help +* Removed the unused graphics files resources/graphics/fav_add.svg and resources/graphics/fav_remove.svg * Added RapidJSON as a Git subtree * Added the GLM (OpenGL Mathematics) library as a Git subtree * Replaced all built-in matrix and vector data types and functions with GLM library equivalents @@ -100,6 +102,7 @@ * 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 * When marking a game to not be counted in the metadata editor and the game was part of a custom collection, no collection disabling notification was displayed +* SliderComponent had very inconsistent widths at different screen aspect ratios * SliderComponent did not properly align the knob and bar vertically * Resizing in SwitchComponent did not reposition the image properly leading to a non-centered image * Horizontal sizing of the TextComponent input field was not consistent across different screen resolutions diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b37752bfe..e35e752ba 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -50,10 +50,10 @@ This plan is under constant review so expect it to change from time to time. Sti * Localization/multi-language support * Reorganize the menus, possibly adding basic/advanced modes * Authoring tools to clean up orphaned gamelist entries, media files etc. +* Add scraping of game manuals and maps and create a viewer for these (with PDF, GIF, JPG and PNG support) * Scrollbar component for the gamelist view which can be used by the themes * Web proxy support for the scraper * Add "time played" counter per game, similar to how it works in Steam -* Preload all built-in resources and never clear them from the cache * Improve multi-threading #### v1.5 diff --git a/INSTALL-DEV.md b/INSTALL-DEV.md index cd33d350f..cead7d5fd 100644 --- a/INSTALL-DEV.md +++ b/INSTALL-DEV.md @@ -1870,6 +1870,7 @@ There are two basic categories of metadata, `game` and `folders` and the metdata * `nomultiscrape` - bool, indicates whether the game should be excluded from the multi-scraper * `hidemetadata` - bool, indicates whether to hide most of the metadata fields when displaying the game in the gamelist view * `playcount` - integer, the number of times this game has been played +* `controller` - string, used to display controller badges * `altemulator` - string, overrides the emulator/launch command on a per game basis * `lastplayed` - statistic, datetime, the last date and time this game was played @@ -1891,6 +1892,7 @@ For folders, most of the fields are identical although some are removed. In the * `broken` * `nomultiscrape` * `hidemetadata` +* `controller` * `lastplayed` - statistic, for folders this is inherited by the latest game file launched inside the folder. **Additional gamelist.xml information:** diff --git a/USERGUIDE-DEV.md b/USERGUIDE-DEV.md index 483fb7a3b..8669a463e 100644 --- a/USERGUIDE-DEV.md +++ b/USERGUIDE-DEV.md @@ -215,7 +215,7 @@ In addition to the styles just described, there is a **Grid** view style as well If the theme supports it, there's a gamelist information field displayed in the gamelist view, showing the number of games for the system (total and favorites) as well as a folder icon if a folder has been entered. When applying any filters to the gamelist, the game counter is replaced with the amount of games filtered, as in 'filtered / total games', e.g. '19 / 77'. If there are game entries in the filter result that are marked not to be counted as games, the number of such files will be indicated as 'filtered + filtered non-games / total games', for example '23 + 4 / 77' indicating 23 normal games, 4 non-games out of a total of 77. Due to this approach it's theoretically possible that the combined filtered game amount exceeds the number of counted games in the collection, for instance '69 + 11 / 77'. This is not considered a bug and is so by design. This gamelist information field functionality is specific to EmulationStation Desktop Edition so older themes will not support this. -Another feature which requires theme support is **Badges**, which is a set of icons displaying the status for various metadata fields. The currently supported badge types are _favorite, completed, kidgame, broken, controller_ and _alternative emulator_. If any of the first four metadata fields have been set for a game, their corresponding badges will be displayed. If a game-specific controller has been selected, the corresponding controller icon will be shown on the controller badge, and if an alternative emulator has been selected for the specific game, that badge will be shown. Setting an alternative emulator system-wide will not display this badge as it's only intended to indicate game-specific overrides. +Another feature which requires theme support is **Badges**, which is a set of icons displaying the status for various metadata fields. The currently supported badge types are _favorite, completed, kidgame, broken, controller_ and _alternative emulator_. If any of the first four metadata fields have been set for a game, their corresponding badges will be displayed. If a game-specific controller has been selected via the metadata editor, the corresponding controller badge will be shown. And if an alternative emulator has been selected for the specific game, that badge will be displayed. Setting an alternative emulator system-wide will not display this badge as it's only intended to indicate game-specific overrides. ![alt text](images/es-de_gamelist_view.png "ES-DE Gamelist View") _The **Gamelist view** is where you browse the games for a specific system._ @@ -1372,9 +1372,11 @@ The following filters can be applied: **Broken** +**Controller badge** + **Alternative emulator** -With the exception of the game name text filter, all available filter values are assembled from metadata from the actual gamelist, so if there is no data to filter for the specific field, the text _Nothing to filter_ will be displayed. This for example happens for the _Completed_ filter if there are no games marked as having been completed in the current gamelist. +With the exception of the game name text filter, all available filter values are assembled from metadata from the actual gamelist, so if there is no data to filter for the specific field, the text _Nothing to filter_ will be displayed. This for example happens for the _Completed_ filter if there are no games marked as having been completed in the current gamelist. The same happens if a metadata setting is identical for all games, such as all games being flagged as favorites. Be aware that although folders can have most of the metadata values set, the filters are only applied to files (this is also true for the game name text filter). So if you for example set a filter to only display your favorite games, any folder that contains a favorite game will be displayed, and other folders which are themselves marked as favorites but that do not contain any favorite games will be hidden. @@ -1476,9 +1478,9 @@ This option will hide most metadata fields as well as any badges. The intention A statistics counter that tracks how many times you have played the game. You normally don't need to touch this, but if you want to, the possibility is there. -**Controller** +**Controller badge** -Contains a list of controller images that are built into ES-DE. The selected controller will be displayed as a badge if the current theme set support badges. This functionality is only cosmetic and will not affect the actual emulators, and it will not affect the controller input for ES-DE itself. +This entry provides a selection of controller icons that are built into ES-DE (although the theme set can override the actual graphics files). The selected icon will be displayed as a badge if the current theme set support badges. This functionality is only cosmetic and will not affect the actual emulators. **Alternative emulator** _(files only)_ From b3022438ec02eb885806c3530c6be9314ef938f1 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sun, 24 Oct 2021 21:25:06 +0200 Subject: [PATCH 45/60] Added a controller graphics file and updated two other. --- .../controllers/gamepad_playstation.svg | 186 +++++++++++-- .../graphics/controllers/joystick_generic.svg | 263 +++++++++++++++++- resources/graphics/controllers/unknown.svg | 8 +- 3 files changed, 415 insertions(+), 42 deletions(-) diff --git a/resources/graphics/controllers/gamepad_playstation.svg b/resources/graphics/controllers/gamepad_playstation.svg index 5b5c4016e..9ae7276e1 100644 --- a/resources/graphics/controllers/gamepad_playstation.svg +++ b/resources/graphics/controllers/gamepad_playstation.svg @@ -26,6 +26,22 @@ id="path18" d="M 0,428.623 H 265.712 V 0 H 0 Z" /> + + + + + + + id="g6486"> + + + d="m 2.9188254,288.73848 c -0.3626261,0 -0.616968,0.0767 -0.6457872,0.22565 -0.01881,0.097 -0.050224,0.30821 -0.078304,0.49129 -0.41776,0.24637 -0.6988306,0.69994 -0.6988306,1.22 0,0.0214 0.00244,0.0421 0.00334,0.0631 -0.162574,0.97662 -0.2776564,1.9435 -0.3645706,2.39876 -0.1552047,0.81336 1.2039422,1.49728 1.7192908,0.55737 0.2440554,-0.44511 0.4645608,-0.87975 0.6276849,-1.20611 0.1572849,0.27068 0.4470001,0.45466 0.7826057,0.45466 0.3725061,0 0.6916645,-0.22473 0.8318607,-0.54559 h 0.9038486 c 0.1406855,0.31961 0.458946,0.5435 0.8305979,0.5435 0.3414904,0 0.6363897,-0.19031 0.7914465,-0.46899 0.164161,0.32858 0.3886006,0.7719 0.6356832,1.22253 0.5153483,0.93993 1.8802687,0.25485 1.7192919,-0.55737 -0.088057,-0.44424 -0.2047877,-1.44363 -0.3708866,-2.43665 9.23e-5,-0.006 8.063e-4,-0.0131 8.063e-4,-0.0198 0,-0.53576 -0.2975275,-1.00202 -0.7362977,-1.24274 -0.026471,-0.17238 -0.057632,-0.38042 -0.075777,-0.47403 -0.028798,-0.14887 -0.2831807,-0.22565 -0.6457867,-0.22565 -0.3626265,0 -0.6169468,0.0768 -0.6457865,0.22565 -0.020408,0.10559 -0.053047,0.32285 -0.085459,0.53464 -0.043019,0.0282 -0.087115,0.055 -0.1267154,0.0876 H 5.8530383 c -0.010012,0.0164 -0.028546,0.0278 -0.049255,0.0278 H 5.2775555 c -0.020687,0 -0.03798,-0.0113 -0.047992,-0.0278 h -1.411553 c -0.054123,-0.045 -0.1129686,-0.0841 -0.1734445,-0.12082 -0.029054,-0.1895 -0.060403,-0.40239 -0.079566,-0.5014 -0.028798,-0.14887 -0.2836438,-0.22565 -0.6462072,-0.22565 z" + style="fill:#c6c7c9;fill-opacity:1;fill-rule:nonzero;stroke:#010101;stroke-width:0.12790291;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/graphics/controllers/joystick_generic.svg b/resources/graphics/controllers/joystick_generic.svg index b6b01c15a..a8e02278f 100644 --- a/resources/graphics/controllers/joystick_generic.svg +++ b/resources/graphics/controllers/joystick_generic.svg @@ -15,7 +15,7 @@ version="1.1" id="svg4925" inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)" - sodipodi:docname="unknown.svg"> + sodipodi:docname="joystick_generic.svg"> + id="g5030" + transform="matrix(1.0307912,0,0,1.0307912,-0.17108346,-8.8028203)"> + + + + + + + + + + + + style="fill:#3c3c3b;fill-opacity:1;stroke-width:0.04973952" + inkscape:connector-curvature="0" + id="path30" + d="m 4.1528229,289.19656 c 0,0.45263 -0.3680723,0.8207 -0.820702,0.8207 -0.4526295,0 -0.8207019,-0.36807 -0.8207019,-0.8207 0,-0.45263 0.3680724,-0.82071 0.8207019,-0.82071 0.4526297,0 0.820702,0.36808 0.820702,0.82071 z m -0.820702,-0.72123 c -0.397916,0 -0.7212229,0.32331 -0.7212229,0.72123 0,0.39792 0.3233069,0.72122 0.7212229,0.72122 0.3979161,0 0.7212231,-0.3233 0.7212231,-0.72122 0,-0.39792 -0.323307,-0.72123 -0.7212231,-0.72123 z" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/graphics/controllers/unknown.svg b/resources/graphics/controllers/unknown.svg index b6b01c15a..e7b99fea2 100644 --- a/resources/graphics/controllers/unknown.svg +++ b/resources/graphics/controllers/unknown.svg @@ -25,9 +25,9 @@ borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" - inkscape:zoom="6.7728148" - inkscape:cx="-21.346482" - inkscape:cy="-0.76992782" + inkscape:zoom="16.892814" + inkscape:cx="-37.558608" + inkscape:cy="23.448283" inkscape:document-units="mm" inkscape:current-layer="layer1" showgrid="false" @@ -58,7 +58,7 @@ aria-label="?" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:18.72348785px;line-height:1.25;font-family:Digitalt;-inkscape-font-specification:Digitalt;letter-spacing:0px;word-spacing:0px;fill:#f00000;fill-opacity:1;stroke:#000000;stroke-width:0.2605817;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" id="text4825" - transform="matrix(0.91316098,0,0,0.91316098,1.9388092,25.308702)"> + transform="matrix(0.76718259,0,0,0.76718259,2.5170951,67.853186)"> Date: Sun, 24 Oct 2021 21:26:06 +0200 Subject: [PATCH 46/60] Improved the controller graphics for the 'c64' system. --- themes/rbsimple-DE/c64/images/controller.svg | 310 ++++++++++++++----- 1 file changed, 238 insertions(+), 72 deletions(-) diff --git a/themes/rbsimple-DE/c64/images/controller.svg b/themes/rbsimple-DE/c64/images/controller.svg index f38851465..536f68500 100644 --- a/themes/rbsimple-DE/c64/images/controller.svg +++ b/themes/rbsimple-DE/c64/images/controller.svg @@ -20,13 +20,36 @@ id="metadata43">image/svg+xml + id="defs41"> - - - - + + - - - - - - - - - - - - - - - - + - - \ No newline at end of file + style="fill:#cbc7b8;fill-opacity:1;stroke-width:0.77960926" /> \ No newline at end of file From 270a2e38578960875072d4ba74311eeda3f4f3af Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Mon, 25 Oct 2021 17:56:17 +0200 Subject: [PATCH 47/60] Added an option to preload gamelists on startup. --- es-app/src/guis/GuiMenu.cpp | 11 +++++++++++ es-app/src/views/ViewController.cpp | 6 +++++- es-app/src/views/gamelist/DetailedGameListView.h | 2 ++ es-app/src/views/gamelist/IGameListView.h | 2 ++ es-app/src/views/gamelist/VideoGameListView.h | 2 ++ es-core/src/Settings.cpp | 1 + 6 files changed, 23 insertions(+), 1 deletion(-) diff --git a/es-app/src/guis/GuiMenu.cpp b/es-app/src/guis/GuiMenu.cpp index 58bf31976..ebcfee71a 100644 --- a/es-app/src/guis/GuiMenu.cpp +++ b/es-app/src/guis/GuiMenu.cpp @@ -1046,6 +1046,17 @@ void GuiMenu::openOtherOptions() } }); + // Whether to preload the gamelists on application startup. + auto preloadGamelists = std::make_shared(mWindow); + preloadGamelists->setState(Settings::getInstance()->getBool("PreloadGamelists")); + s->addWithLabel("PRELOAD GAMELISTS ON STARTUP", preloadGamelists); + s->addSaveFunc([preloadGamelists, s] { + if (preloadGamelists->getState() != Settings::getInstance()->getBool("PreloadGamelists")) { + Settings::getInstance()->setBool("PreloadGamelists", preloadGamelists->getState()); + s->setNeedsSaving(); + } + }); + // Whether to enable alternative emulators per game (the option to disable this is intended // primarily for testing purposes). auto alternativeEmulatorPerGame = std::make_shared(mWindow); diff --git a/es-app/src/views/ViewController.cpp b/es-app/src/views/ViewController.cpp index 5daa8fdc9..366c30a81 100644 --- a/es-app/src/views/ViewController.cpp +++ b/es-app/src/views/ViewController.cpp @@ -961,7 +961,11 @@ void ViewController::preload() std::to_string(systemCount) + ")"); } (*it)->getIndex()->resetFilters(); - getGameListView(*it); + + if (Settings::getInstance()->getBool("PreloadGamelists")) + getGameListView(*it)->preloadGamelist(); + else + getGameListView(*it); } // Load navigation sounds, either from the theme if it supports it, or otherwise from diff --git a/es-app/src/views/gamelist/DetailedGameListView.h b/es-app/src/views/gamelist/DetailedGameListView.h index ed4db80c2..08302d5a7 100644 --- a/es-app/src/views/gamelist/DetailedGameListView.h +++ b/es-app/src/views/gamelist/DetailedGameListView.h @@ -25,6 +25,8 @@ public: virtual std::string getName() const override { return "detailed"; } virtual void launch(FileData* game) override; + virtual void preloadGamelist() override { updateInfoPanel(); } + protected: virtual void update(int deltaTime) override; diff --git a/es-app/src/views/gamelist/IGameListView.h b/es-app/src/views/gamelist/IGameListView.h index 853aa925e..212a062e9 100644 --- a/es-app/src/views/gamelist/IGameListView.h +++ b/es-app/src/views/gamelist/IGameListView.h @@ -32,6 +32,8 @@ public: void setTheme(const std::shared_ptr& theme); const std::shared_ptr& getTheme() const { return mTheme; } + virtual void preloadGamelist(){}; + virtual FileData* getCursor() = 0; virtual void setCursor(FileData*) = 0; virtual FileData* getNextEntry() = 0; diff --git a/es-app/src/views/gamelist/VideoGameListView.h b/es-app/src/views/gamelist/VideoGameListView.h index 06d1c1034..e0d6775de 100644 --- a/es-app/src/views/gamelist/VideoGameListView.h +++ b/es-app/src/views/gamelist/VideoGameListView.h @@ -28,6 +28,8 @@ public: virtual std::string getName() const override { return "video"; } virtual void launch(FileData* game) override; + virtual void preloadGamelist() override { updateInfoPanel(); } + protected: virtual void update(int deltaTime) override; diff --git a/es-core/src/Settings.cpp b/es-core/src/Settings.cpp index e7035633c..34c3a8c8b 100644 --- a/es-core/src/Settings.cpp +++ b/es-core/src/Settings.cpp @@ -242,6 +242,7 @@ void Settings::setDefaults() mBoolMap["VideoHardwareDecoding"] = {false, false}; #endif mBoolMap["VideoUpscaleFrameRate"] = {false, false}; + mBoolMap["PreloadGamelists"] = {true, true}; mBoolMap["AlternativeEmulatorPerGame"] = {true, true}; mBoolMap["ShowHiddenFiles"] = {true, true}; mBoolMap["ShowHiddenGames"] = {true, true}; From 7ed0267f5b05d6d12ad74379d594d01359563b98 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Mon, 25 Oct 2021 18:39:58 +0200 Subject: [PATCH 48/60] Made a large optimization to the SVG rasterization logic. --- es-core/src/Window.cpp | 2 +- es-core/src/components/ImageComponent.h | 3 +- es-core/src/components/NinePatchComponent.cpp | 2 +- es-core/src/components/OptionListComponent.h | 8 ++- es-core/src/components/SwitchComponent.cpp | 11 +-- es-core/src/components/SwitchComponent.h | 3 +- es-core/src/resources/TextureData.cpp | 67 ++++++++++++------- es-core/src/resources/TextureData.h | 7 ++ es-core/src/resources/TextureResource.cpp | 22 ++++-- es-core/src/resources/TextureResource.h | 6 ++ 10 files changed, 81 insertions(+), 50 deletions(-) diff --git a/es-core/src/Window.cpp b/es-core/src/Window.cpp index 0943e8f04..629872054 100644 --- a/es-core/src/Window.cpp +++ b/es-core/src/Window.cpp @@ -600,8 +600,8 @@ void Window::renderLoadingScreen(std::string text) static_cast(Renderer::getScreenHeight()), 0x000000FF, 0x000000FF); ImageComponent splash(this, true); - splash.setResize(Renderer::getScreenWidth() * 0.6f, 0.0f); splash.setImage(":/graphics/splash.svg"); + splash.setResize(Renderer::getScreenWidth() * 0.6f, 0.0f); splash.setPosition((Renderer::getScreenWidth() - splash.getSize().x) / 2.0f, (Renderer::getScreenHeight() - splash.getSize().y) / 2.0f * 0.6f); splash.render(trans); diff --git a/es-core/src/components/ImageComponent.h b/es-core/src/components/ImageComponent.h index 40b15cf3d..0f22d86a1 100644 --- a/es-core/src/components/ImageComponent.h +++ b/es-core/src/components/ImageComponent.h @@ -109,7 +109,8 @@ private: bool mTargetIsMin; // Calculates the correct mSize from our resizing information (set by setResize/setMaxSize). - // Used internally whenever the resizing parameters or texture change. + // Used internally whenever the resizing parameters or texture change. This function also + // initiates the SVG rasterization. void resize(); Renderer::Vertex mVertices[4]; diff --git a/es-core/src/components/NinePatchComponent.cpp b/es-core/src/components/NinePatchComponent.cpp index d908499fd..ad6eaab3f 100644 --- a/es-core/src/components/NinePatchComponent.cpp +++ b/es-core/src/components/NinePatchComponent.cpp @@ -59,7 +59,7 @@ void NinePatchComponent::buildVertices() else scaleFactor = glm::clamp(Renderer::getScreenWidthModifier(), 0.4f, 3.0f); - mTexture = TextureResource::get(mPath, false, false, true, true, scaleFactor); + mTexture = TextureResource::get(mPath, false, false, true, true, true, scaleFactor); if (mTexture->getSize() == glm::ivec2{}) { mVertices = nullptr; diff --git a/es-core/src/components/OptionListComponent.h b/es-core/src/components/OptionListComponent.h index c25812743..181b8693a 100644 --- a/es-core/src/components/OptionListComponent.h +++ b/es-core/src/components/OptionListComponent.h @@ -76,8 +76,12 @@ public: // Handles positioning/resizing of text and arrows. void onSizeChanged() override { - mLeftArrow.setResize(0, mText.getFont()->getLetterHeight()); - mRightArrow.setResize(0, mText.getFont()->getLetterHeight()); + if (mText.getFont()->getLetterHeight() != mLeftArrow.getSize().y || + mLeftArrow.getTexture()->getPendingRasterization()) + mLeftArrow.setResize(0, mText.getFont()->getLetterHeight()); + if (mText.getFont()->getLetterHeight() != mRightArrow.getSize().y || + mRightArrow.getTexture()->getPendingRasterization()) + mRightArrow.setResize(0, mText.getFont()->getLetterHeight()); if (mSize.x < (mLeftArrow.getSize().x + mRightArrow.getSize().x)) { LOG(LogWarning) << "OptionListComponent too narrow"; diff --git a/es-core/src/components/SwitchComponent.cpp b/es-core/src/components/SwitchComponent.cpp index a2712ec5d..e1249009a 100644 --- a/es-core/src/components/SwitchComponent.cpp +++ b/es-core/src/components/SwitchComponent.cpp @@ -49,16 +49,6 @@ 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; @@ -81,6 +71,7 @@ void SwitchComponent::setValue(const std::string& statestring) void SwitchComponent::onStateChanged() { mImage.setImage(mState ? ":/graphics/on.svg" : ":/graphics/off.svg"); + mImage.setResize(mSize); // Change the color of the switch to reflect the changes. if (mState == mOriginalValue) diff --git a/es-core/src/components/SwitchComponent.h b/es-core/src/components/SwitchComponent.h index a0d1ac33c..47011b29d 100644 --- a/es-core/src/components/SwitchComponent.h +++ b/es-core/src/components/SwitchComponent.h @@ -22,8 +22,7 @@ public: void render(const glm::mat4& parentTrans) override; void onSizeChanged() override { mImage.setSize(mSize); } - void setResize(float width, float height) override; - + void setResize(float width, float height) override { setSize(width, height); } bool getState() const { return mState; } void setState(bool state); std::string getValue() const override; diff --git a/es-core/src/resources/TextureData.cpp b/es-core/src/resources/TextureData.cpp index 9537b25d7..927a03854 100644 --- a/es-core/src/resources/TextureData.cpp +++ b/es-core/src/resources/TextureData.cpp @@ -24,16 +24,18 @@ #define DPI 96 TextureData::TextureData(bool tile) - : mTile(tile) - , mTextureID(0) + : mTile{tile} + , mTextureID{0} , mDataRGBA({}) - , mWidth(0) - , mHeight(0) - , mSourceWidth(0.0f) - , mSourceHeight(0.0f) - , mScaleDuringLoad(1.0f) - , mScalable(false) - , mLinearMagnify(false) + , mWidth{0} + , mHeight{0} + , mSourceWidth{0.0f} + , mSourceHeight{0.0f} + , mScaleDuringLoad{1.0f} + , mScalable{false} + , mLinearMagnify{false} + , mAlwaysRasterize{false} + , mPendingRasterization{false} { } @@ -54,21 +56,26 @@ void TextureData::initFromPath(const std::string& path) bool TextureData::initSVGFromMemory(const std::string& fileData) { // If already initialized then don't process it again. - std::unique_lock lock(mMutex); + std::unique_lock lock{mMutex}; if (!mDataRGBA.empty()) return true; - NSVGimage* svgImage = nsvgParse(const_cast(fileData.c_str()), "px", DPI); + NSVGimage* svgImage{nsvgParse(const_cast(fileData.c_str()), "px", DPI)}; if (!svgImage) { LOG(LogError) << "Couldn't parse SVG image"; return false; } - // We want to rasterize this texture at a specific resolution. If the source size - // variables are set then use them, otherwise get them from the parsed file. - if ((mSourceWidth == 0.0f) && (mSourceHeight == 0.0f)) { + bool rasterize{true}; + + // If there is no image size defined yet, then don't rasterize unless mAlwaysRasterize has + // been set (this is only used by NinePatchComponent to avoid flickering menus). + if (mSourceWidth == 0.0f && mSourceHeight == 0.0f) { + if (!mAlwaysRasterize) + rasterize = false; + mSourceWidth = svgImage->width; mSourceHeight = svgImage->height; } @@ -87,23 +94,31 @@ bool TextureData::initSVGFromMemory(const std::string& fileData) std::round((static_cast(mWidth) / svgImage->width) * svgImage->height)); } - std::vector tempVector; - tempVector.reserve(mWidth * mHeight * 4); + if (rasterize) { + std::vector tempVector; + tempVector.reserve(mWidth * mHeight * 4); - NSVGrasterizer* rast = nsvgCreateRasterizer(); + NSVGrasterizer* rast = nsvgCreateRasterizer(); - nsvgRasterize(rast, svgImage, 0, 0, mHeight / svgImage->height, tempVector.data(), mWidth, - mHeight, mWidth * 4); + nsvgRasterize(rast, svgImage, 0, 0, mHeight / svgImage->height, tempVector.data(), mWidth, + mHeight, mWidth * 4); + + nsvgDeleteRasterizer(rast); + + mDataRGBA.insert(mDataRGBA.begin(), tempVector.data(), + tempVector.data() + (mWidth * mHeight * 4)); + + ImageIO::flipPixelsVert(mDataRGBA.data(), mWidth, mHeight); + mPendingRasterization = false; + } + else { + // TODO: Fix this properly instead of using the single byte texture workaround. + mDataRGBA.push_back(0); + mPendingRasterization = true; + } - // This is important in order to avoid memory leaks. - nsvgDeleteRasterizer(rast); nsvgDelete(svgImage); - mDataRGBA.insert(mDataRGBA.begin(), tempVector.data(), - tempVector.data() + (mWidth * mHeight * 4)); - - ImageIO::flipPixelsVert(mDataRGBA.data(), mWidth, mHeight); - return true; } diff --git a/es-core/src/resources/TextureData.h b/es-core/src/resources/TextureData.h index 0e76d8159..16394167e 100644 --- a/es-core/src/resources/TextureData.h +++ b/es-core/src/resources/TextureData.h @@ -61,6 +61,11 @@ public: void setScaleDuringLoad(float scale) { mScaleDuringLoad = scale; } // Whether to use linear filtering when magnifying the texture. void setLinearMagnify(bool setting) { mLinearMagnify = setting; } + // Whether to rasterize the image even if a size has not been set yet. + void setAlwaysRasterize(bool setting) { mAlwaysRasterize = setting; } + + // Has the image been loaded but not yet been rasterized as the size was not known? + bool getPendingRasterization() { return mPendingRasterization; } std::vector getRawRGBAData() { return mDataRGBA; } std::string getTextureFilePath() { return mPath; } @@ -80,6 +85,8 @@ private: bool mScalable; bool mLinearMagnify; bool mReloadable; + bool mAlwaysRasterize; + bool mPendingRasterization; }; #endif // ES_CORE_RESOURCES_TEXTURE_DATA_H diff --git a/es-core/src/resources/TextureResource.cpp b/es-core/src/resources/TextureResource.cpp index b6f7b8d8a..0b2c3820d 100644 --- a/es-core/src/resources/TextureResource.cpp +++ b/es-core/src/resources/TextureResource.cpp @@ -8,16 +8,20 @@ #include "resources/TextureResource.h" -#include "resources/TextureData.h" #include "utils/FileSystemUtil.h" +#include "utils/StringUtil.h" TextureDataManager TextureResource::sTextureDataManager; std::map> TextureResource::sTextureMap; std::set TextureResource::sAllTextures; -TextureResource::TextureResource( - const std::string& path, bool tile, bool dynamic, bool linearMagnify, float scaleDuringLoad) +TextureResource::TextureResource(const std::string& path, + bool tile, + bool dynamic, + bool linearMagnify, + bool alwaysRasterize, + float scaleDuringLoad) : mTextureData(nullptr) , mForceLoad(false) { @@ -32,6 +36,7 @@ TextureResource::TextureResource( if (scaleDuringLoad != 1.0f) data->setScaleDuringLoad(scaleDuringLoad); data->setLinearMagnify(linearMagnify); + data->setAlwaysRasterize(alwaysRasterize); // Force the texture manager to load it using a blocking load. sTextureDataManager.load(data, true); } @@ -42,6 +47,7 @@ TextureResource::TextureResource( if (scaleDuringLoad != 1.0f) data->setScaleDuringLoad(scaleDuringLoad); data->setLinearMagnify(linearMagnify); + data->setAlwaysRasterize(alwaysRasterize); // Load it so we can read the width/height. data->load(); } @@ -148,6 +154,7 @@ std::shared_ptr TextureResource::get(const std::string& path, bool forceLoad, bool dynamic, bool linearMagnify, + bool alwaysRasterize, float scaleDuringLoad) { std::shared_ptr& rm = ResourceManager::getInstance(); @@ -155,7 +162,7 @@ std::shared_ptr TextureResource::get(const std::string& path, const std::string canonicalPath = Utils::FileSystem::getCanonicalPath(path); if (canonicalPath.empty()) { std::shared_ptr tex( - new TextureResource("", tile, false, linearMagnify, scaleDuringLoad)); + new TextureResource("", tile, false, linearMagnify, alwaysRasterize, scaleDuringLoad)); // Make sure we get properly deinitialized even though we do nothing on reinitialization. rm->addReloadable(tex); return tex; @@ -171,12 +178,13 @@ std::shared_ptr TextureResource::get(const std::string& path, // Need to create it. std::shared_ptr tex; - tex = std::shared_ptr( - new TextureResource(key.first, tile, dynamic, linearMagnify, scaleDuringLoad)); + tex = std::shared_ptr(new TextureResource( + key.first, tile, dynamic, linearMagnify, alwaysRasterize, scaleDuringLoad)); std::shared_ptr data = sTextureDataManager.get(tex.get()); // Is it an SVG? - if (key.first.substr(key.first.size() - 4, std::string::npos) != ".svg") { + if (Utils::String::toLower(key.first.substr(key.first.size() - 4, std::string::npos)) != + ".svg") { // Probably not. Add it to our map. We don't add SVGs because 2 SVGs might be // rasterized at different sizes. sTextureMap[key] = std::weak_ptr(tex); diff --git a/es-core/src/resources/TextureResource.h b/es-core/src/resources/TextureResource.h index 91ba5ef7e..9e5f02a2f 100644 --- a/es-core/src/resources/TextureResource.h +++ b/es-core/src/resources/TextureResource.h @@ -10,6 +10,7 @@ #define ES_CORE_RESOURCES_TEXTURE_RESOURCE_H #include "resources/ResourceManager.h" +#include "resources/TextureData.h" #include "resources/TextureDataManager.h" #include "utils/MathUtil.h" @@ -30,6 +31,7 @@ public: bool forceLoad = false, bool dynamic = true, bool linearMagnify = false, + bool alwaysRasterize = false, float scaleDuringLoad = 1.0f); void initFromPixels(const unsigned char* dataRGBA, size_t width, size_t height); virtual void initFromMemory(const char* data, size_t length); @@ -38,6 +40,9 @@ public: // Returns the raw pixel values. std::vector getRawRGBAData(); + // Has the image been loaded but not yet been rasterized as the size was not known? + bool getPendingRasterization() { return mTextureData->getPendingRasterization(); } + std::string getTextureFilePath(); // For SVG graphics this function effectively rescales the image to the defined size. @@ -65,6 +70,7 @@ protected: bool tile, bool dynamic, bool linearMagnify, + bool alwaysRasterize, float scaleDuringLoad); virtual void unload(std::shared_ptr& rm); virtual void reload(std::shared_ptr& rm); From e149fea3fbe25d5bd29659c8365d85094351e7d4 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Mon, 25 Oct 2021 18:42:09 +0200 Subject: [PATCH 49/60] Small adjustment to the 'completed' badge graphics. --- resources/graphics/badge_completed.svg | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/graphics/badge_completed.svg b/resources/graphics/badge_completed.svg index 376d4f21b..941c24cc5 100644 --- a/resources/graphics/badge_completed.svg +++ b/resources/graphics/badge_completed.svg @@ -33,9 +33,9 @@ fit-margin-left="0" fit-margin-right="0" fit-margin-bottom="0" - inkscape:zoom="9.6754505" - inkscape:cx="14.68785" - inkscape:cy="65.424484" + inkscape:zoom="3.0165741" + inkscape:cx="-308.2567" + inkscape:cy="78.979511" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" @@ -143,7 +143,7 @@ style="fill:#f0f0f0;fill-opacity:1" /> From 4f6d2c7faed81de5b6e6e005bdb1f9d2b5d581d0 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Mon, 25 Oct 2021 18:45:18 +0200 Subject: [PATCH 50/60] (rbsimple-DE) Minor adjustments to the 'c64' system controller graphics. --- themes/rbsimple-DE/c64/images/controller.svg | 457 ++++++++++--------- 1 file changed, 243 insertions(+), 214 deletions(-) diff --git a/themes/rbsimple-DE/c64/images/controller.svg b/themes/rbsimple-DE/c64/images/controller.svg index 536f68500..c30aa519d 100644 --- a/themes/rbsimple-DE/c64/images/controller.svg +++ b/themes/rbsimple-DE/c64/images/controller.svg @@ -19,7 +19,7 @@ inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)">image/svg+xml - - - + + \ No newline at end of file + transform="matrix(0.61238028,0,0,0.61238028,90.172607,35.147976)" + id="g8-69-2"> \ No newline at end of file From f32c3dc6f48f92520f9834d58d2a9f7f1c23ca91 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Mon, 25 Oct 2021 19:13:54 +0200 Subject: [PATCH 51/60] Reintroduced column mode for BadgeComponent. Also fixed an issue with direction in FlexboxComponent when using column mode. --- es-core/src/ThemeData.cpp | 5 +-- es-core/src/components/BadgeComponent.cpp | 37 +++++++++++++-------- es-core/src/components/FlexboxComponent.cpp | 34 +++++++++++++------ es-core/src/components/FlexboxComponent.h | 16 ++++----- themes/rbsimple-DE/theme.xml | 5 +-- 5 files changed, 61 insertions(+), 36 deletions(-) diff --git a/es-core/src/ThemeData.cpp b/es-core/src/ThemeData.cpp index e1e83f432..2a1c7f07f 100644 --- a/es-core/src/ThemeData.cpp +++ b/es-core/src/ThemeData.cpp @@ -153,8 +153,9 @@ std::map> The {"rotation", FLOAT}, {"rotationOrigin", NORMALIZED_PAIR}, {"alignment", STRING}, - {"itemsPerRow", FLOAT}, - {"rows", FLOAT}, + {"direction", STRING}, + {"lines", FLOAT}, + {"itemsPerLine", FLOAT}, {"itemMargin", NORMALIZED_PAIR}, {"slots", STRING}, {"controllerPos", NORMALIZED_PAIR}, diff --git a/es-core/src/components/BadgeComponent.cpp b/es-core/src/components/BadgeComponent.cpp index 5402b62a9..3683b94b3 100644 --- a/es-core/src/components/BadgeComponent.cpp +++ b/es-core/src/components/BadgeComponent.cpp @@ -187,26 +187,37 @@ void BadgeComponent::applyTheme(const std::shared_ptr& theme, } } - if (elem->has("itemsPerRow")) { - const float itemsPerRow{elem->get("itemsPerRow")}; - if (itemsPerRow < 1.0f || itemsPerRow > 10.0f) { - LOG(LogWarning) - << "BadgeComponent: Invalid theme configuration, set to \"" - << itemsPerRow << "\""; + if (elem->has("direction")) { + const std::string direction{elem->get("direction")}; + if (direction != "row" && direction != "column") { + LOG(LogWarning) << "BadgeComponent: Invalid theme configuration, set to \"" + << direction << "\""; } else { - mFlexboxComponent.setItemsPerLine(static_cast(itemsPerRow)); + mFlexboxComponent.setDirection(direction); } } - if (elem->has("rows")) { - const float rows{elem->get("rows")}; - if (rows < 1.0f || rows > 10.0f) { - LOG(LogWarning) << "BadgeComponent: Invalid theme configuration, set to \"" - << rows << "\""; + if (elem->has("lines")) { + const float lines{elem->get("lines")}; + if (lines < 1.0f || lines > 10.0f) { + LOG(LogWarning) << "BadgeComponent: Invalid theme configuration, set to \"" + << lines << "\""; } else { - mFlexboxComponent.setLines(static_cast(rows)); + mFlexboxComponent.setLines(static_cast(lines)); + } + } + + if (elem->has("itemsPerLine")) { + const float itemsPerLine{elem->get("itemsPerLine")}; + if (itemsPerLine < 1.0f || itemsPerLine > 10.0f) { + LOG(LogWarning) + << "BadgeComponent: Invalid theme configuration, set to \"" + << itemsPerLine << "\""; + } + else { + mFlexboxComponent.setItemsPerLine(static_cast(itemsPerLine)); } } diff --git a/es-core/src/components/FlexboxComponent.cpp b/es-core/src/components/FlexboxComponent.cpp index da3bf1b1c..3b9fe9a0d 100644 --- a/es-core/src/components/FlexboxComponent.cpp +++ b/es-core/src/components/FlexboxComponent.cpp @@ -24,8 +24,8 @@ FlexboxComponent::FlexboxComponent(Window* window, std::vector& ite , mItems(items) , mDirection{DEFAULT_DIRECTION} , mAlignment{DEFAULT_ALIGNMENT} - , mItemsPerLine{DEFAULT_ITEMS_PER_LINE} , mLines{DEFAULT_LINES} + , mItemsPerLine{DEFAULT_ITEMS_PER_LINE} , mItemPlacement{DEFAULT_ITEM_PLACEMENT} , mItemMargin{glm::vec2{DEFAULT_MARGIN_X, DEFAULT_MARGIN_Y}} , mOverlayPosition{0.5f, 0.5f} @@ -86,8 +86,6 @@ void FlexboxComponent::setItemMargin(glm::vec2 value) void FlexboxComponent::computeLayout() { - // TODO: There is no right-alignment support for column mode. - // If we're not clamping itemMargin to a reasonable value, all kinds of weird rendering // issues could occur. mItemMargin.x = glm::clamp(mItemMargin.x, 0.0f, mSize.x / 2.0f); @@ -107,7 +105,13 @@ void FlexboxComponent::computeLayout() mItemsPerLine = static_cast(mItems.size()); } - glm::vec2 grid{mItemsPerLine, mLines}; + glm::vec2 grid{}; + + if (mDirection == "row") + grid = {mItemsPerLine, mLines}; + else + grid = {mLines, mItemsPerLine}; + glm::vec2 maxItemSize{(mSize + mItemMargin - grid * mItemMargin) / grid}; float rowHeight{0.0f}; @@ -146,11 +150,11 @@ void FlexboxComponent::computeLayout() maxItemSize = glm::round(maxItemSize); - bool alignRight{mAlignment == "right" && mDirection == "row"}; + bool alignRight{mAlignment == "right"}; float alignRightComp{0.0f}; // If right-aligning, move the overall container contents during grid setup. - if (alignRight) + if (alignRight && mDirection == "row") alignRightComp = std::round(mSize.x - ((maxItemSize.x + mItemMargin.x) * grid.x) + mItemMargin.x); @@ -166,11 +170,19 @@ void FlexboxComponent::computeLayout() } } } - else { // Column mode. + else if (mDirection == "column" && !alignRight) { + for (int x = 0; x < grid.x; x++) { + for (int y = 0; y < grid.y; y++) { + itemPositions.push_back(glm::vec2{(x * (maxItemSize.x + mItemMargin.x)), + y * (rowHeight + mItemMargin.y)}); + } + } + } + else { // Right-aligned. for (int x = 0; x < grid.x; x++) { for (int y = 0; y < grid.y; y++) { itemPositions.push_back( - glm::vec2{(x * (maxItemSize.x + mItemMargin.x) + alignRightComp), + glm::vec2{(mSize.x - (x * (maxItemSize.x + mItemMargin.x)) - maxItemSize.x), y * (rowHeight + mItemMargin.y)}); } } @@ -185,7 +197,7 @@ void FlexboxComponent::computeLayout() if (!item.visible) continue; - if (pos > 0) { + if (mDirection == "row" && pos > 0) { if (itemPositions[pos - 1].y < itemPositions[pos].y) { lastY = itemPositions[pos].y; itemsOnLastRow = 0; @@ -225,8 +237,8 @@ void FlexboxComponent::computeLayout() pos++; } - // Apply right-align to the items (only works in row mode). - if (alignRight) { + // Apply right-align to the items if we're using row mode. + if (alignRight && mDirection == "row") { for (auto& item : mItems) { if (!item.visible) continue; diff --git a/es-core/src/components/FlexboxComponent.h b/es-core/src/components/FlexboxComponent.h index 4ab5ee5c3..ce5a6c60f 100644 --- a/es-core/src/components/FlexboxComponent.h +++ b/es-core/src/components/FlexboxComponent.h @@ -44,13 +44,6 @@ public: mLayoutValid = false; } - unsigned int getItemsPerLine() const { return mItemsPerLine; } - void setItemsPerLine(unsigned int value) - { - mItemsPerLine = value; - mLayoutValid = false; - } - unsigned int getLines() const { return mLines; } void setLines(unsigned int value) { @@ -58,6 +51,13 @@ public: mLayoutValid = false; } + unsigned int getItemsPerLine() const { return mItemsPerLine; } + void setItemsPerLine(unsigned int value) + { + mItemsPerLine = value; + mLayoutValid = false; + } + std::string getItemPlacement() const { return mItemPlacement; } void setItemPlacement(const std::string& value) { @@ -87,8 +87,8 @@ private: // Layout options. std::string mDirection; std::string mAlignment; - unsigned int mItemsPerLine; unsigned int mLines; + unsigned int mItemsPerLine; std::string mItemPlacement; glm::vec2 mItemMargin; diff --git a/themes/rbsimple-DE/theme.xml b/themes/rbsimple-DE/theme.xml index 7d543fe59..4d59d93e2 100644 --- a/themes/rbsimple-DE/theme.xml +++ b/themes/rbsimple-DE/theme.xml @@ -241,8 +241,9 @@ based on: 'recalbox-multi' by the Recalbox community 0.13 0.1635 0.5 0.5 left - 3 - 2 + row + 2 + 3 0.5 0.572 0.81 -1.0 0.005 From be74fd26ef4e0ffdb0ea088bc43bbfe69827f98a Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Mon, 25 Oct 2021 19:15:37 +0200 Subject: [PATCH 52/60] Small adjustments to the generic joystick controller icon. --- .../graphics/controllers/joystick_generic.svg | 398 ++++++++++-------- 1 file changed, 212 insertions(+), 186 deletions(-) diff --git a/resources/graphics/controllers/joystick_generic.svg b/resources/graphics/controllers/joystick_generic.svg index a8e02278f..ebabd7953 100644 --- a/resources/graphics/controllers/joystick_generic.svg +++ b/resources/graphics/controllers/joystick_generic.svg @@ -25,9 +25,9 @@ borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" - inkscape:zoom="22.627417" - inkscape:cx="-1.2394882" - inkscape:cy="24.852525" + inkscape:zoom="28.21875" + inkscape:cx="6.1924519" + inkscape:cy="24.290783" inkscape:document-units="mm" inkscape:current-layer="layer1" showgrid="false" @@ -55,81 +55,80 @@ id="layer1" transform="translate(0,-285.88748)"> + id="g4868"> + style="opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.00956567;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal" /> + style="opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.02827883;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal" /> + style="opacity:1;vector-effect:none;fill:#161616;fill-opacity:1;stroke:none;stroke-width:0.02487354;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal" /> + style="opacity:1;vector-effect:none;fill:#161616;fill-opacity:1;stroke:none;stroke-width:0.03217189;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal" /> + style="opacity:1;vector-effect:none;fill:#161616;fill-opacity:1;stroke:none;stroke-width:0.02511485;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal" /> + style="opacity:1;vector-effect:none;fill:#161616;fill-opacity:1;stroke:none;stroke-width:0.02944537;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal" /> + style="opacity:1;vector-effect:none;fill:#333333;fill-opacity:1;stroke:none;stroke-width:0.03223006;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal" /> + style="opacity:1;vector-effect:none;fill:#0c0c0c;fill-opacity:1;stroke:#0a0a0a;stroke-width:0.08007559;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal" /> - - - - - - - - - - - - - - - - - - - - - - - - - - - + d="m 4.1096097,289.29845 c 0,0.46657 -0.3794056,0.84597 -0.8459724,0.84597 -0.4665665,0 -0.8459723,-0.3794 -0.8459723,-0.84597 0,-0.46657 0.3794058,-0.84598 0.8459723,-0.84598 0.4665668,0 0.8459724,0.37941 0.8459724,0.84598 z m -0.8459724,-0.74344 c -0.4101683,0 -0.7434302,0.33327 -0.7434302,0.74344 0,0.41017 0.3332619,0.74343 0.7434302,0.74343 0.4101685,0 0.7434305,-0.33326 0.7434305,-0.74343 0,-0.41017 -0.333262,-0.74344 -0.7434305,-0.74344 z" /> + d="m 8.6954757,289.29845 c 0,0.46657 -0.3794058,0.84597 -0.8459722,0.84597 -0.4665662,0 -0.8459718,-0.3794 -0.8459718,-0.84597 0,-0.46657 0.3794056,-0.84598 0.8459718,-0.84598 0.4665664,0 0.8459722,0.37941 0.8459722,0.84598 z m -0.8459722,-0.74344 c -0.4101681,0 -0.7434299,0.33327 -0.7434299,0.74344 0,0.41016 0.3332618,0.74343 0.7434299,0.74343 0.4101688,0 0.7434306,-0.33327 0.7434306,-0.74343 0,-0.41017 -0.3332618,-0.74344 -0.7434306,-0.74344 z" /> + + + + + + + + + + + + + + + + + + + + + + + + + + From c0f37cd088da929d4c51d5fe5b3e9d891f8dac60 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Mon, 25 Oct 2021 19:17:40 +0200 Subject: [PATCH 53/60] Documentation update. --- CHANGELOG.md | 4 +++- CONTRIBUTING.md | 4 ++-- THEMES-DEV.md | 12 +++++++----- USERGUIDE-DEV.md | 4 ++++ 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1322caa21..af4d4a9f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,14 +23,16 @@ * 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. -* Added a filter for "Alternative emulator" and sorted the filters in the same order as the metadata editor fields +* Added filters for "Alternative emulator" and "Controller badges" and sorted the filters in the same order as the metadata editor fields * Added a menu option to change the application exit key combination +* Added an option to preload the gamelists on startup which leads to smoother navigation when first entering each gamelist * 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 +* Made large optimizations to the SVG rendering which reduces application startup time dramatically when many systems are populated * 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 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e35e752ba..15f31d09b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -47,8 +47,8 @@ This plan is under constant review so expect it to change from time to time. Sti #### v1.4 +* Bulk metadata editor * Localization/multi-language support -* Reorganize the menus, possibly adding basic/advanced modes * Authoring tools to clean up orphaned gamelist entries, media files etc. * Add scraping of game manuals and maps and create a viewer for these (with PDF, GIF, JPG and PNG support) * Scrollbar component for the gamelist view which can be used by the themes @@ -58,7 +58,7 @@ This plan is under constant review so expect it to change from time to time. Sti #### v1.5 -* Bulk metadata editor +* Reorganize the menus, possibly adding basic/advanced modes * Simple file browsing component * Improve the performance of the GLSL shader code * Animated menu elements like switches, tick boxes, smooth scrolling etc. diff --git a/THEMES-DEV.md b/THEMES-DEV.md index 6d078639e..9e9e1815c 100644 --- a/THEMES-DEV.md +++ b/THEMES-DEV.md @@ -910,7 +910,7 @@ ES-DE borrows the concept of "nine patches" from Android (or "9-Slices"). Curren #### badges -It's strongly recommended to use the same image dimensions for all badges as varying aspect ratios will lead to alignment issues. For the controller images it's recommended to keep to the square canvas size used by the default bundled graphics as otherwise sizing and placement will be inconsistent (unless all controller graphic files are customized of course). Overall it's a very good idea to keep the image dimensions small. This is especially true for SVG graphics as rasterization will otherwise take a long time which will slow down application startup and gamelist navigation. +It's strongly recommended to use the same image dimensions for all badges as varying aspect ratios will lead to alignment issues. For the controller images it's recommended to keep to the square canvas size used by the default bundled graphics as otherwise sizing and placement will be inconsistent (unless all controller graphic files are customized of course). * `pos` - type: NORMALIZED_PAIR. * `size` - type: NORMALIZED_PAIR. @@ -922,10 +922,12 @@ It's strongly recommended to use the same image dimensions for all badges as var - angle in degrees that the image should be rotated. Positive values will rotate clockwise, negative values will rotate counterclockwise. Default is `0`. * `rotationOrigin` - type: NORMALIZED_PAIR. - Point around which the image will be rotated. Default is `0.5 0.5`. -* `itemsPerRow` - type: FLOAT. - - Number of badges that fit on a row. When more badges are available a new row will be started. Default is `4`. -* `rows` - type: FLOAT. - - The number of rows available. Default is `2`. +* `direction` - type: STRING. + - Valid values are "row" or "column". Controls the primary layout direction (line axis) for the badges. Lines will fill up in the specified direction. Default is `row`. +* `lines` - type: FLOAT. + - The number of lines available. Default is `2`. +* `itemsPerLine` - type: FLOAT. + - Number of badges that fit on a line. When more badges are available a new line will be started. Default is `4`. * `itemMargin` - type: NORMALIZED_PAIR. - The margins between badges. Possible combinations: - `x y` - horizontal and vertical margins. Minimum value per axis is `0`, maximum value is `0.2`. Default is `0.01 0.01`. If one of the axis is set to `-1` the margin of the other axis (in pixels) will be used, which makes it possible to get identical spacing between all items regardless of screen aspect ratio. diff --git a/USERGUIDE-DEV.md b/USERGUIDE-DEV.md index 8669a463e..d56306a2a 100644 --- a/USERGUIDE-DEV.md +++ b/USERGUIDE-DEV.md @@ -1252,6 +1252,10 @@ Enabling this option offloads video decoding to the GPU. Whether this actually i With this option enabled, videos with lower frame rates than 60 FPS, such as 24 and 30 will get upscaled to 60 FPS. This results in slightly smoother playback for some videos. There is a small performance hit from this option, so on weaker machines it may be necessary to disable it for fluent video playback. This setting has no effect when using the VLC video player. If the VLC video player is not included in the ES-DE build, the "(FFmpeg)" text is omitted from the setting name. +**Preload gamelists on startup** + +When this option is enabled, all gamelists will be loaded on application startup. This will increase the startup time slightly and lead to a higher initial memory utilization, but navigation will be smoother the first time a gamelist is entered. The improvement is especially noticeable when the _slide_ transition style has been selected. + **Enable alternative emulators per game** If enabled, you will be able to select alternative emulators per game using the metadata editor. It's only recommended to disable this option for testing purposes. From 848e0a1a94e828e5f25da9152c5dc9741fc7a470 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Tue, 26 Oct 2021 18:13:31 +0200 Subject: [PATCH 54/60] Fixed an SVG graphics corruption issue. --- es-core/src/resources/TextureData.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/es-core/src/resources/TextureData.cpp b/es-core/src/resources/TextureData.cpp index 927a03854..e8476fbd8 100644 --- a/es-core/src/resources/TextureData.cpp +++ b/es-core/src/resources/TextureData.cpp @@ -75,9 +75,9 @@ bool TextureData::initSVGFromMemory(const std::string& fileData) if (mSourceWidth == 0.0f && mSourceHeight == 0.0f) { if (!mAlwaysRasterize) rasterize = false; - - mSourceWidth = svgImage->width; - mSourceHeight = svgImage->height; + // Set a small temporary size that maintains the image aspect ratio. + mSourceWidth = 64.0f; + mSourceHeight = 64.0f * (svgImage->height / svgImage->width); } mWidth = static_cast(std::round(mSourceWidth * mScaleDuringLoad)); From 3d3d9518422b632727fb3dd64189ac501eb22ca5 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Tue, 26 Oct 2021 18:22:41 +0200 Subject: [PATCH 55/60] Renamed alwaysRasterize to forceRasterization. Also removed an unnecessary function call in ImageComponent. --- es-core/src/components/ImageComponent.cpp | 4 ++-- es-core/src/resources/TextureData.cpp | 6 +++--- es-core/src/resources/TextureData.h | 4 ++-- es-core/src/resources/TextureResource.cpp | 16 ++++++++-------- es-core/src/resources/TextureResource.h | 10 ++++++---- 5 files changed, 21 insertions(+), 19 deletions(-) diff --git a/es-core/src/components/ImageComponent.cpp b/es-core/src/components/ImageComponent.cpp index 4055c9989..92aa7c7d7 100644 --- a/es-core/src/components/ImageComponent.cpp +++ b/es-core/src/components/ImageComponent.cpp @@ -333,7 +333,7 @@ void ImageComponent::setSaturation(float saturation) void ImageComponent::updateVertices() { - if (!mTexture || !mTexture->isInitialized()) + if (!mTexture) return; // We go through this mess to make sure everything is properly rounded. @@ -398,7 +398,7 @@ void ImageComponent::render(const glm::mat4& parentTrans) Renderer::drawRect(0.0f, 0.0f, mSize.x, mSize.y, 0xFF000033, 0xFF000033); } // An image with zero size would normally indicate a corrupt image file. - if (mTexture->isInitialized() && mTexture->getSize() != glm::ivec2{}) { + if (mTexture->getSize() != glm::ivec2{}) { // Actually draw the image. // The bind() function returns false if the texture is not currently loaded. A blank // texture is bound in this case but we want to handle a fade so it doesn't just diff --git a/es-core/src/resources/TextureData.cpp b/es-core/src/resources/TextureData.cpp index e8476fbd8..e9c05e198 100644 --- a/es-core/src/resources/TextureData.cpp +++ b/es-core/src/resources/TextureData.cpp @@ -34,7 +34,7 @@ TextureData::TextureData(bool tile) , mScaleDuringLoad{1.0f} , mScalable{false} , mLinearMagnify{false} - , mAlwaysRasterize{false} + , mForceRasterization{false} , mPendingRasterization{false} { } @@ -70,10 +70,10 @@ bool TextureData::initSVGFromMemory(const std::string& fileData) bool rasterize{true}; - // If there is no image size defined yet, then don't rasterize unless mAlwaysRasterize has + // If there is no image size defined yet, then don't rasterize unless mForceRasterization has // been set (this is only used by NinePatchComponent to avoid flickering menus). if (mSourceWidth == 0.0f && mSourceHeight == 0.0f) { - if (!mAlwaysRasterize) + if (!mForceRasterization) rasterize = false; // Set a small temporary size that maintains the image aspect ratio. mSourceWidth = 64.0f; diff --git a/es-core/src/resources/TextureData.h b/es-core/src/resources/TextureData.h index 16394167e..4fe3eb827 100644 --- a/es-core/src/resources/TextureData.h +++ b/es-core/src/resources/TextureData.h @@ -62,7 +62,7 @@ public: // Whether to use linear filtering when magnifying the texture. void setLinearMagnify(bool setting) { mLinearMagnify = setting; } // Whether to rasterize the image even if a size has not been set yet. - void setAlwaysRasterize(bool setting) { mAlwaysRasterize = setting; } + void setForceRasterization(bool setting) { mForceRasterization = setting; } // Has the image been loaded but not yet been rasterized as the size was not known? bool getPendingRasterization() { return mPendingRasterization; } @@ -85,7 +85,7 @@ private: bool mScalable; bool mLinearMagnify; bool mReloadable; - bool mAlwaysRasterize; + bool mForceRasterization; bool mPendingRasterization; }; diff --git a/es-core/src/resources/TextureResource.cpp b/es-core/src/resources/TextureResource.cpp index 0b2c3820d..8217a9f58 100644 --- a/es-core/src/resources/TextureResource.cpp +++ b/es-core/src/resources/TextureResource.cpp @@ -20,7 +20,7 @@ TextureResource::TextureResource(const std::string& path, bool tile, bool dynamic, bool linearMagnify, - bool alwaysRasterize, + bool forceRasterization, float scaleDuringLoad) : mTextureData(nullptr) , mForceLoad(false) @@ -36,7 +36,7 @@ TextureResource::TextureResource(const std::string& path, if (scaleDuringLoad != 1.0f) data->setScaleDuringLoad(scaleDuringLoad); data->setLinearMagnify(linearMagnify); - data->setAlwaysRasterize(alwaysRasterize); + data->setForceRasterization(forceRasterization); // Force the texture manager to load it using a blocking load. sTextureDataManager.load(data, true); } @@ -47,7 +47,7 @@ TextureResource::TextureResource(const std::string& path, if (scaleDuringLoad != 1.0f) data->setScaleDuringLoad(scaleDuringLoad); data->setLinearMagnify(linearMagnify); - data->setAlwaysRasterize(alwaysRasterize); + data->setForceRasterization(forceRasterization); // Load it so we can read the width/height. data->load(); } @@ -154,15 +154,15 @@ std::shared_ptr TextureResource::get(const std::string& path, bool forceLoad, bool dynamic, bool linearMagnify, - bool alwaysRasterize, + bool forceRasterization, float scaleDuringLoad) { std::shared_ptr& rm = ResourceManager::getInstance(); const std::string canonicalPath = Utils::FileSystem::getCanonicalPath(path); if (canonicalPath.empty()) { - std::shared_ptr tex( - new TextureResource("", tile, false, linearMagnify, alwaysRasterize, scaleDuringLoad)); + std::shared_ptr tex(new TextureResource( + "", tile, false, linearMagnify, forceRasterization, scaleDuringLoad)); // Make sure we get properly deinitialized even though we do nothing on reinitialization. rm->addReloadable(tex); return tex; @@ -179,7 +179,7 @@ std::shared_ptr TextureResource::get(const std::string& path, // Need to create it. std::shared_ptr tex; tex = std::shared_ptr(new TextureResource( - key.first, tile, dynamic, linearMagnify, alwaysRasterize, scaleDuringLoad)); + key.first, tile, dynamic, linearMagnify, forceRasterization, scaleDuringLoad)); std::shared_ptr data = sTextureDataManager.get(tex.get()); // Is it an SVG? @@ -217,7 +217,7 @@ void TextureResource::rasterizeAt(float width, float height) data = sTextureDataManager.get(this); mSourceSize = glm::vec2{static_cast(width), static_cast(height)}; data->setSourceSize(static_cast(width), static_cast(height)); - if (mForceLoad || (mTextureData != nullptr)) + if (mForceLoad || mTextureData != nullptr) data->load(); } diff --git a/es-core/src/resources/TextureResource.h b/es-core/src/resources/TextureResource.h index 9e5f02a2f..7ae64728b 100644 --- a/es-core/src/resources/TextureResource.h +++ b/es-core/src/resources/TextureResource.h @@ -31,7 +31,7 @@ public: bool forceLoad = false, bool dynamic = true, bool linearMagnify = false, - bool alwaysRasterize = false, + bool forceRasterization = false, float scaleDuringLoad = 1.0f); void initFromPixels(const unsigned char* dataRGBA, size_t width, size_t height); virtual void initFromMemory(const char* data, size_t length); @@ -41,7 +41,10 @@ public: std::vector getRawRGBAData(); // Has the image been loaded but not yet been rasterized as the size was not known? - bool getPendingRasterization() { return mTextureData->getPendingRasterization(); } + bool getPendingRasterization() + { + return (mTextureData != nullptr ? mTextureData->getPendingRasterization() : false); + } std::string getTextureFilePath(); @@ -54,7 +57,6 @@ public: virtual ~TextureResource(); - bool isInitialized() const { return true; } bool isTiled() const; const glm::ivec2 getSize() const { return mSize; } @@ -70,7 +72,7 @@ protected: bool tile, bool dynamic, bool linearMagnify, - bool alwaysRasterize, + bool forceRasterization, float scaleDuringLoad); virtual void unload(std::shared_ptr& rm); virtual void reload(std::shared_ptr& rm); From 58345863b3e9d9edbebd8794c86d09a139135217 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Tue, 26 Oct 2021 18:23:52 +0200 Subject: [PATCH 56/60] Changed an RGBA vector getter from copy to reference to reduce CPU usage. --- es-core/src/resources/TextureData.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/es-core/src/resources/TextureData.h b/es-core/src/resources/TextureData.h index 4fe3eb827..1717bf101 100644 --- a/es-core/src/resources/TextureData.h +++ b/es-core/src/resources/TextureData.h @@ -67,7 +67,7 @@ public: // Has the image been loaded but not yet been rasterized as the size was not known? bool getPendingRasterization() { return mPendingRasterization; } - std::vector getRawRGBAData() { return mDataRGBA; } + std::vector& getRawRGBAData() { return mDataRGBA; } std::string getTextureFilePath() { return mPath; } bool tiled() { return mTile; } From 4f9737cd23bf38c6c6d6f8a7f72ada354749b982 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Tue, 26 Oct 2021 18:26:00 +0200 Subject: [PATCH 57/60] Fixed a compiler warning when building with the GLES renderer. --- es-core/src/Window.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/es-core/src/Window.cpp b/es-core/src/Window.cpp index 629872054..39c419136 100644 --- a/es-core/src/Window.cpp +++ b/es-core/src/Window.cpp @@ -24,7 +24,9 @@ #include #include +#if defined(USE_OPENGL_21) #define CLOCK_BACKGROUND_CREATION false +#endif Window::Window() : mScreensaver(nullptr) From 4e02ce230b16338eadd910166a91f9a6d12fc341 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Tue, 26 Oct 2021 18:27:30 +0200 Subject: [PATCH 58/60] Disabled dynamic loading of images in BadgeComponent. --- es-core/src/components/BadgeComponent.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/es-core/src/components/BadgeComponent.cpp b/es-core/src/components/BadgeComponent.cpp index 3683b94b3..abe74f0d7 100644 --- a/es-core/src/components/BadgeComponent.cpp +++ b/es-core/src/components/BadgeComponent.cpp @@ -277,8 +277,7 @@ void BadgeComponent::applyTheme(const std::shared_ptr& theme, FlexboxComponent::FlexboxItem item; item.label = slot; - ImageComponent badgeImage{mWindow}; - badgeImage.setForceLoad(true); + ImageComponent badgeImage{mWindow, false, false}; badgeImage.setImage(mBadgeIcons[slot]); item.baseImage = badgeImage; item.overlayImage = ImageComponent{mWindow}; From fbb974de0316eb8b5a247fad3278a1354be8d4fb Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Tue, 26 Oct 2021 18:28:21 +0200 Subject: [PATCH 59/60] Removed an unnecessary force load setter from ImageComponent. --- es-core/src/components/ImageComponent.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/es-core/src/components/ImageComponent.h b/es-core/src/components/ImageComponent.h index 0f22d86a1..e91955204 100644 --- a/es-core/src/components/ImageComponent.h +++ b/es-core/src/components/ImageComponent.h @@ -30,8 +30,6 @@ public: // Use an already existing texture. void setImage(const std::shared_ptr& texture, bool resizeTexture = true); - void setForceLoad(bool status) { mForceLoad = status; } - void onSizeChanged() override { updateVertices(); } // Resize the image to fit this size. If one axis is zero, scale that axis to maintain From a979c6d0e599f5333ea41a65df358258a49204a6 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Tue, 26 Oct 2021 18:29:30 +0200 Subject: [PATCH 60/60] Made the menu texture render correctly when running really low on texture memory. --- es-core/src/components/NinePatchComponent.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/es-core/src/components/NinePatchComponent.cpp b/es-core/src/components/NinePatchComponent.cpp index ad6eaab3f..e1b62b366 100644 --- a/es-core/src/components/NinePatchComponent.cpp +++ b/es-core/src/components/NinePatchComponent.cpp @@ -59,7 +59,7 @@ void NinePatchComponent::buildVertices() else scaleFactor = glm::clamp(Renderer::getScreenWidthModifier(), 0.4f, 3.0f); - mTexture = TextureResource::get(mPath, false, false, true, true, true, scaleFactor); + mTexture = TextureResource::get(mPath, false, false, false, true, true, scaleFactor); if (mTexture->getSize() == glm::ivec2{}) { mVertices = nullptr;