From ac5e3ad95f34ac52bc925d618f37fe9b1717b42b Mon Sep 17 00:00:00 2001 From: "D. Polders" Date: Mon, 13 Mar 2017 22:11:07 +0100 Subject: [PATCH] System Carousel feature, now with only a single commit! --- .gitignore | 3 - CMakeLists.txt | 2 +- THEMES.md | 23 ++ es-app/src/views/SystemView.cpp | 314 +++++++++++++++-------- es-app/src/views/SystemView.h | 32 ++- es-app/src/views/ViewController.cpp | 2 +- es-core/src/ThemeData.cpp | 20 +- es-core/src/components/TextComponent.cpp | 55 ++-- es-core/src/components/TextComponent.h | 7 +- 9 files changed, 325 insertions(+), 133 deletions(-) diff --git a/.gitignore b/.gitignore index 685ee8c42..0686c4180 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,3 @@ Makefile .cproject .project .settings/ - -# WinMerge -.bak \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index d15b4303e..1b98f1621 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,7 +86,7 @@ if(CMAKE_COMPILER_IS_GNUCXX) #set up compiler flags for GCC if (CMAKE_BUILD_TYPE MATCHES Debug) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wno-attributes -O0") #support C++11 for std::, optimize + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wno-attributes -O0") #support C++11 for std::, optimize set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -O0") else() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wno-attributes -O3") #support C++11 for std::, optimize diff --git a/THEMES.md b/THEMES.md index a8a111fa1..1c518dafd 100644 --- a/THEMES.md +++ b/THEMES.md @@ -400,8 +400,12 @@ Reference #### system * `helpsystem name="help"` - ALL - The help system style for this view. +* `carousel name="systemcarousel"` -ALL + - The system logo carousel * `image name="logo"` - PATH - A logo image, to be displayed in the system logo carousel. +* `text name="systemInfo"` - ALL + - Displays details of the system currently selected in the carousel. * You can use extra elements (elements with `extra="true"`) to add your own backgrounds, etc. They will be displayed behind the carousel, and scroll relative to the carousel. @@ -468,6 +472,7 @@ Can be created as an extra. - `w h` - works like a "text box." If `h` is non-zero and `h` <= `fontSize` (implying it should be a single line of text), text that goes beyond `w` will be truncated with an elipses (...). * `text` - type: STRING. * `color` - type: COLOR. +* `backgroundColor` - type: COLOR; * `fontPath` - type: PATH. - Path to a truetype font (.ttf). * `fontSize` - type: FLOAT. @@ -541,6 +546,24 @@ EmulationStation borrows the concept of "nine patches" from Android (or "9-Slice * `fontPath` - type: PATH. * `fontSize` - type: FLOAT. +#### carousel + +* `type` - type: STRING. + * Accepted values are "HORIZONTAL" or "VERTICAL". Sets the scoll direction of the carousel. + * Default is "HORIZONTAL". +* `size` - type: NORMALIZED_PAIR. Default is "1 0.2325" +* `pos` - type: NORMALIZED_PAIR. Default is "0 0.38375". +* `color` - type: COLOR. + * Controls the color of the carousel background. + * Default is FFFFFFD8 +* `logoSize` - type: NORMALIZED_PAIR. Default is "0.25 0.155" +* `logoScale` - type: FLOAT. + * Selected logo is increased in size by this scale + * Default is 1.5 +* `maxLogoCount` - type: FLOAT. + * Sets the number of logos to display in the carousel. + * Default is 3 + The help system is a special element that displays a context-sensitive list of actions the user can take at any time. You should try and keep the position constant throughout every screen. Keep in mind the "default" settings (including position) are used whenever the user opens a menu. [*Check out the "official" themes for some more examples!*](http://aloshi.com/emulationstation#themes) diff --git a/es-app/src/views/SystemView.cpp b/es-app/src/views/SystemView.cpp index c760125b9..272d4c634 100644 --- a/es-app/src/views/SystemView.cpp +++ b/es-app/src/views/SystemView.cpp @@ -9,22 +9,15 @@ #include "Settings.h" #include "Util.h" -#define SELECTED_SCALE 1.5f -#define LOGO_PADDING ((logoSize().x() * (SELECTED_SCALE - 1)/2) + (mSize.x() * 0.06f)) -#define BAND_HEIGHT (logoSize().y() * SELECTED_SCALE) - SystemView::SystemView(Window* window) : IList(window, LIST_SCROLL_STYLE_SLOW, LIST_ALWAYS_LOOP), - mSystemInfo(window, "SYSTEM INFO", Font::get(FONT_SIZE_SMALL), 0x33333300, ALIGN_CENTER) + mViewNeedsReload(true), + mSystemInfo(window, "SYSTEM INFO", Font::get(FONT_SIZE_SMALL), 0x33333300, ALIGN_CENTER) { mCamOffset = 0; mExtrasCamOffset = 0; mExtrasFadeOpacity = 0.0f; setSize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight()); - - mSystemInfo.setSize(mSize.x(), mSystemInfo.getSize().y() * 1.333f); - mSystemInfo.setPosition(0, (mSize.y() + BAND_HEIGHT) / 2); - populate(); } @@ -36,6 +29,9 @@ void SystemView::populate() { const std::shared_ptr& theme = (*it)->getTheme(); + if(mViewNeedsReload) + getViewElements(theme); + Entry e; e.name = (*it)->getName(); e.object = *it; @@ -43,18 +39,20 @@ void SystemView::populate() // make logo if(theme->getElement("system", "logo", "image")) { - ImageComponent* logo = new ImageComponent(mWindow, false, false); - logo->setMaxSize(Eigen::Vector2f(logoSize().x(), logoSize().y())); + ImageComponent* logo = new ImageComponent(mWindow); + logo->setMaxSize(Eigen::Vector2f(mCarousel.logoSize.x(), mCarousel.logoSize.y())); logo->applyTheme((*it)->getTheme(), "system", "logo", ThemeFlags::PATH); - logo->setPosition((logoSize().x() - logo->getSize().x()) / 2, (logoSize().y() - logo->getSize().y()) / 2); // center + logo->setPosition((mCarousel.logoSize.x() - logo->getSize().x()) / 2, + (mCarousel.logoSize.y() - logo->getSize().y()) / 2); // center e.data.logo = std::shared_ptr(logo); - ImageComponent* logoSelected = new ImageComponent(mWindow, false, false); - logoSelected->setMaxSize(Eigen::Vector2f(logoSize().x() * SELECTED_SCALE, logoSize().y() * SELECTED_SCALE * 0.70f)); - logoSelected->applyTheme((*it)->getTheme(), "system", "logo", ThemeFlags::PATH); - logoSelected->setPosition((logoSize().x() - logoSelected->getSize().x()) / 2, - (logoSize().y() - logoSelected->getSize().y()) / 2); // center + ImageComponent* logoSelected = new ImageComponent(mWindow); + logoSelected->setMaxSize(Eigen::Vector2f(mCarousel.logoSize.x() * mCarousel.logoScale, mCarousel.logoSize.y() * mCarousel.logoScale)); + logoSelected->applyTheme((*it)->getTheme(), "system", "logo", ThemeFlags::PATH | ThemeFlags::COLOR); + logoSelected->setPosition((mCarousel.logoSize.x() - logoSelected->getSize().x()) / 2, + (mCarousel.logoSize.y() - logoSelected->getSize().y()) / 2); // center e.data.logoSelected = std::shared_ptr(logoSelected); + }else{ // no logo in theme; use text TextComponent* text = new TextComponent(mWindow, @@ -62,15 +60,15 @@ void SystemView::populate() Font::get(FONT_SIZE_LARGE), 0x000000FF, ALIGN_CENTER); - text->setSize(logoSize()); + text->setSize(mCarousel.logoSize); e.data.logo = std::shared_ptr(text); TextComponent* textSelected = new TextComponent(mWindow, (*it)->getName(), - Font::get((int)(FONT_SIZE_LARGE * SELECTED_SCALE)), + Font::get((int)(FONT_SIZE_LARGE * 1.5)), 0x000000FF, ALIGN_CENTER); - textSelected->setSize(logoSize()); + textSelected->setSize(mCarousel.logoSize); e.data.logoSelected = std::shared_ptr(textSelected); } @@ -96,26 +94,40 @@ bool SystemView::input(InputConfig* config, Input input) { if(config->getDeviceId() == DEVICE_KEYBOARD && input.value && input.id == SDLK_r && SDL_GetModState() & KMOD_LCTRL && Settings::getInstance()->getBool("Debug")) { - LOG(LogInfo) << " Reloading SystemList view"; + LOG(LogInfo) << " Reloading all"; + ViewController::get()->reloadAll(); + return true; + } - // reload themes - for(auto it = mEntries.begin(); it != mEntries.end(); it++) - it->object->loadTheme(); + switch (mCarousel.type) + { + case VERTICAL: + if (config->isMappedTo("up", input)) + { + listInput(-1); + return true; + } + if (config->isMappedTo("down", input)) + { + listInput(1); + return true; + } + break; + case HORIZONTAL: + default: + if (config->isMappedTo("left", input)) + { + listInput(-1); + return true; + } + if (config->isMappedTo("right", input)) + { + listInput(1); + return true; + } + break; + } - populate(); - updateHelpPrompts(); - return true; - } - if(config->isMappedTo("left", input)) - { - listInput(-1); - return true; - } - if(config->isMappedTo("right", input)) - { - listInput(1); - return true; - } if(config->isMappedTo("a", input)) { stopScrolling(); @@ -123,7 +135,10 @@ bool SystemView::input(InputConfig* config, Input input) return true; } }else{ - if(config->isMappedTo("left", input) || config->isMappedTo("right", input)) + if(config->isMappedTo("left", input) || + config->isMappedTo("right", input) || + config->isMappedTo("up", input) || + config->isMappedTo("down", input)) listInput(0); } @@ -254,28 +269,136 @@ void SystemView::onCursorChanged(const CursorState& state) void SystemView::render(const Eigen::Affine3f& parentTrans) { if(size() == 0) - return; - - Eigen::Affine3f trans = getTransform() * parentTrans; + return; // nothing to render - // draw the list elements (titles, backgrounds, logos) - const float logoSizeX = logoSize().x() + LOGO_PADDING; + Eigen::Affine3f trans = getTransform() * parentTrans; - int logoCount = (int)(mSize.x() / logoSizeX) + 2; // how many logos we need to draw + renderExtras(trans); + renderCarousel(trans); + renderInfoBar(trans); +} + +std::vector SystemView::getHelpPrompts() +{ + std::vector prompts; + if (mCarousel.type == VERTICAL) + prompts.push_back(HelpPrompt("up/down", "choose")); + else + prompts.push_back(HelpPrompt("left/right", "choose")); + prompts.push_back(HelpPrompt("a", "select")); + return prompts; +} + +HelpStyle SystemView::getHelpStyle() +{ + HelpStyle style; + style.applyTheme(mEntries.at(mCursor).object->getTheme(), "system"); + return style; +} + +void SystemView::onThemeChanged(const std::shared_ptr& theme) +{ + LOG(LogDebug) << "SystemView::onThemeChanged()"; + mViewNeedsReload = true; + populate(); +} + +// Get the ThemeElements that make up the SystemView. +void SystemView::getViewElements(const std::shared_ptr& theme) +{ + LOG(LogDebug) << "SystemView::getViewElements()"; + + getDefaultElements(); + + const ThemeData::ThemeElement* carouselElem = theme->getElement("system", "systemcarousel", "carousel"); + if (carouselElem) + getCarouselFromTheme(carouselElem); + + const ThemeData::ThemeElement* sysInfoElem = theme->getElement("system", "systemInfo", "text"); + if (sysInfoElem) + mSystemInfo.applyTheme(theme, "system", "systemInfo", ThemeFlags::ALL); + + mViewNeedsReload = false; +} + +// Render system carousel +void SystemView::renderCarousel(const Eigen::Affine3f& trans) +{ + Eigen::Vector2i clipPos((int)mCarousel.pos.x(), (int)mCarousel.pos.y()); + Eigen::Vector2i clipSize((int)mCarousel.size.x(), (int)mCarousel.size.y()); + + Renderer::pushClipRect(clipPos, clipSize); + + // background box behind logos + Renderer::setMatrix(trans); + Renderer::drawRect(mCarousel.pos.x(), mCarousel.pos.y(), mCarousel.size.x(), mCarousel.size.y(), mCarousel.color); + + // draw logos + Eigen::Vector2f logoSpacing(0.0, 0.0); // NB: logoSpacing will include the size of the logo itself as well! + float xOff = 0.0; + float yOff = 0.0; + + switch (mCarousel.type) + { + case VERTICAL: + logoSpacing[1] = ((mCarousel.size.y() - (mCarousel.logoSize.y() * mCarousel.maxLogoCount)) / (mCarousel.maxLogoCount)) + mCarousel.logoSize.y(); + xOff = mCarousel.pos.x() + (mCarousel.size.x() / 2) - (mCarousel.logoSize.x() / 2); + yOff = mCarousel.pos.y() + (mCarousel.size.y() - mCarousel.logoSize.y()) / 2 - (mCamOffset * logoSpacing[1]); + break; + case HORIZONTAL: + default: + logoSpacing[0] = ((mCarousel.size.x() - (mCarousel.logoSize.x() * mCarousel.maxLogoCount)) / (mCarousel.maxLogoCount)) + mCarousel.logoSize.x(); + xOff = mCarousel.pos.x() + (mCarousel.size.x() - mCarousel.logoSize.x()) / 2 - (mCamOffset * logoSpacing[0]); + yOff = mCarousel.pos.y() + (mCarousel.size.y() / 2) - (mCarousel.logoSize.y() / 2); + break; + } + + Eigen::Affine3f logoTrans = trans; int center = (int)(mCamOffset); + int logoCount = std::min(mCarousel.maxLogoCount, (int)mEntries.size()) + 2; - if(mEntries.size() == 1) - logoCount = 1; - - // draw background extras - Eigen::Affine3f extrasTrans = trans; - int extrasCenter = (int)mExtrasCamOffset; - for(int i = extrasCenter - 1; i < extrasCenter + 2; i++) + for (int i = center - logoCount / 2; i < center + logoCount / 2 + 1; i++) { int index = i; - while(index < 0) + while (index < 0) index += mEntries.size(); - while(index >= (int)mEntries.size()) + while (index >= (int)mEntries.size()) + index -= mEntries.size(); + + logoTrans.translation() = trans.translation() + Eigen::Vector3f(i * logoSpacing[0] + xOff, i * logoSpacing [1] + yOff, 0); + + if (index == mCursor) //Selected System + { + const std::shared_ptr& comp = mEntries.at(index).data.logoSelected; + comp->setOpacity(0xFF); + comp->render(logoTrans); + } + else { // not selected systems + const std::shared_ptr& comp = mEntries.at(index).data.logo; + comp->setOpacity(0x80); + comp->render(logoTrans); + } + } + Renderer::popClipRect(); +} + +void SystemView::renderInfoBar(const Eigen::Affine3f& trans) +{ + Renderer::setMatrix(trans); + mSystemInfo.render(trans); +} + +// Draw background extras +void SystemView::renderExtras(const Eigen::Affine3f& trans) +{ + Eigen::Affine3f extrasTrans = trans; + int extrasCenter = (int)mExtrasCamOffset; + for (int i = extrasCenter - 1; i < extrasCenter + 2; i++) + { + int index = i; + while (index < 0) + index += mEntries.size(); + while (index >= (int)mEntries.size()) index -= mEntries.size(); extrasTrans.translation() = trans.translation() + Eigen::Vector3f((i - mExtrasCamOffset) * mSize.x(), 0, 0); @@ -287,61 +410,50 @@ void SystemView::render(const Eigen::Affine3f& parentTrans) } // fade extras if necessary - if(mExtrasFadeOpacity) + if (mExtrasFadeOpacity) { Renderer::setMatrix(trans); Renderer::drawRect(0.0f, 0.0f, mSize.x(), mSize.y(), 0x00000000 | (unsigned char)(mExtrasFadeOpacity * 255)); } - - // draw logos - float xOff = (mSize.x() - logoSize().x())/2 - (mCamOffset * logoSizeX); - float yOff = (mSize.y() - logoSize().y())/2; - - // background behind the logos - Renderer::setMatrix(trans); - Renderer::drawRect(0.f, (mSize.y() - BAND_HEIGHT) / 2, mSize.x(), BAND_HEIGHT, 0xFFFFFFD8); - - Eigen::Affine3f logoTrans = trans; - for(int i = center - logoCount/2; i < center + logoCount/2 + 1; i++) - { - int index = i; - while(index < 0) - index += mEntries.size(); - while(index >= (int)mEntries.size()) - index -= mEntries.size(); - - logoTrans.translation() = trans.translation() + Eigen::Vector3f(i * logoSizeX + xOff, yOff, 0); - - if(index == mCursor) //scale our selection up - { - // selected - const std::shared_ptr& comp = mEntries.at(index).data.logoSelected; - comp->setOpacity(0xFF); - comp->render(logoTrans); - }else{ - // not selected - const std::shared_ptr& comp = mEntries.at(index).data.logo; - comp->setOpacity(0x80); - comp->render(logoTrans); - } - } - - Renderer::setMatrix(trans); - Renderer::drawRect(mSystemInfo.getPosition().x(), mSystemInfo.getPosition().y() - 1, mSize.x(), mSystemInfo.getSize().y(), 0xDDDDDD00 | (unsigned char)(mSystemInfo.getOpacity() / 255.f * 0xD8)); - mSystemInfo.render(trans); } -std::vector SystemView::getHelpPrompts() +// Populate the system carousel with the legacy values +void SystemView::getDefaultElements(void) { - std::vector prompts; - prompts.push_back(HelpPrompt("left/right", "choose")); - prompts.push_back(HelpPrompt("a", "select")); - return prompts; + // Carousel + mCarousel.type = HORIZONTAL; + mCarousel.size.x() = mSize.x(); + mCarousel.size.y() = 0.2325f * mSize.y(); + mCarousel.pos.x() = 0.0f; + mCarousel.pos.y() = 0.5f * (mSize.y() - mCarousel.size.y()); + mCarousel.color = 0xFFFFFFD8; + mCarousel.logoScale = 1.5f; + mCarousel.logoSize.x() = 0.25f * mSize.x(); + mCarousel.logoSize.y() = 0.155f * mSize.y(); + mCarousel.maxLogoCount = 3; + + // System Info Bar + mSystemInfo.setSize(mSize.x(), mSystemInfo.getFont()->getLetterHeight()*2.2f); + mSystemInfo.setPosition(0, (mCarousel.pos.y() + mCarousel.size.y())); + mSystemInfo.setBackgroundColor(0xDDDDDDD8); + mSystemInfo.setFont(Font::get((int)(0.035f * mSize.y()), Font::getDefaultPath())); + mSystemInfo.setColor(0x000000FF); } -HelpStyle SystemView::getHelpStyle() +void SystemView::getCarouselFromTheme(const ThemeData::ThemeElement* elem) { - HelpStyle style; - style.applyTheme(mEntries.at(mCursor).object->getTheme(), "system"); - return style; + if (elem->has("type")) + mCarousel.type = !(elem->get("type").compare("vertical")) ? VERTICAL : HORIZONTAL; + if (elem->has("size")) + mCarousel.size = elem->get("size").cwiseProduct(mSize); + if (elem->has("pos")) + mCarousel.pos = elem->get("pos").cwiseProduct(mSize); + if (elem->has("color")) + mCarousel.color = elem->get("color"); + if (elem->has("logoScale")) + mCarousel.logoScale = elem->get("logoScale"); + if (elem->has("logoSize")) + mCarousel.logoSize = elem->get("logoSize").cwiseProduct(mSize); + if (elem->has("maxLogoCount")) + mCarousel.maxLogoCount = std::round(elem->get("maxLogoCount")); } diff --git a/es-app/src/views/SystemView.h b/es-app/src/views/SystemView.h index 15200b903..209c4b0e9 100644 --- a/es-app/src/views/SystemView.h +++ b/es-app/src/views/SystemView.h @@ -10,6 +10,12 @@ class SystemData; class AnimatedImageComponent; +enum CarouselType : unsigned int +{ + HORIZONTAL = 0, + VERTICAL = 1 +}; + struct SystemViewData { std::shared_ptr logo; @@ -17,6 +23,18 @@ struct SystemViewData std::shared_ptr backgroundExtras; }; +struct SystemViewCarousel +{ + CarouselType type; + Eigen::Vector2f pos; + Eigen::Vector2f size; + float logoScale; + Eigen::Vector2f logoSpacing; + unsigned int color; + int maxLogoCount; // number of logos shown on the carousel + Eigen::Vector2f logoSize; +}; + class SystemView : public IList { public: @@ -28,6 +46,8 @@ public: void update(int deltaTime) override; void render(const Eigen::Affine3f& parentTrans) override; + void onThemeChanged(const std::shared_ptr& theme); + std::vector getHelpPrompts() override; virtual HelpStyle getHelpStyle() override; @@ -35,14 +55,22 @@ protected: void onCursorChanged(const CursorState& state) override; private: - inline Eigen::Vector2f logoSize() const { return Eigen::Vector2f(mSize.x() * 0.25f, mSize.y() * 0.155f); } - void populate(); + void getViewElements(const std::shared_ptr& theme); + void getDefaultElements(void); + void getCarouselFromTheme(const ThemeData::ThemeElement* elem); + void renderCarousel(const Eigen::Affine3f& parentTrans); + void renderExtras(const Eigen::Affine3f& parentTrans); + void renderInfoBar(const Eigen::Affine3f& trans); + + SystemViewCarousel mCarousel; TextComponent mSystemInfo; // unit is list index float mCamOffset; float mExtrasCamOffset; float mExtrasFadeOpacity; + + bool mViewNeedsReload; }; diff --git a/es-app/src/views/ViewController.cpp b/es-app/src/views/ViewController.cpp index ce29025e0..cc83eece5 100644 --- a/es-app/src/views/ViewController.cpp +++ b/es-app/src/views/ViewController.cpp @@ -12,7 +12,7 @@ #include "animations/LaunchAnimation.h" #include "animations/MoveCameraAnimation.h" #include "animations/LambdaAnimation.h" -#include +#include ViewController* ViewController::sInstance = NULL; diff --git a/es-core/src/ThemeData.cpp b/es-core/src/ThemeData.cpp index 2b38ce817..ae00a0b20 100644 --- a/es-core/src/ThemeData.cpp +++ b/es-core/src/ThemeData.cpp @@ -24,7 +24,7 @@ ElementMapType makeMap(const T& mapInit) } std::vector ThemeData::sSupportedViews = boost::assign::list_of("system")("basic")("detailed")("video"); -std::vector ThemeData::sSupportedFeatures = boost::assign::list_of("video"); +std::vector ThemeData::sSupportedFeatures = boost::assign::list_of("video")("carousel"); std::map< std::string, ElementMapType > ThemeData::sElementMap = boost::assign::map_list_of ("image", makeMap(boost::assign::map_list_of @@ -39,12 +39,14 @@ std::map< std::string, ElementMapType > ThemeData::sElementMap = boost::assign:: ("pos", NORMALIZED_PAIR) ("size", NORMALIZED_PAIR) ("text", STRING) - ("color", COLOR) + ("backgroundColor", COLOR) ("fontPath", PATH) ("fontSize", FLOAT) + ("color", COLOR) ("alignment", STRING) ("forceUppercase", BOOLEAN) - ("lineSpacing", FLOAT))) + ("lineSpacing", FLOAT) + ("value", STRING))) ("textlist", makeMap(boost::assign::map_list_of ("pos", NORMALIZED_PAIR) ("size", NORMALIZED_PAIR) @@ -94,12 +96,20 @@ std::map< std::string, ElementMapType > ThemeData::sElementMap = boost::assign:: ("default", PATH) ("delay", FLOAT) ("showSnapshotNoVideo", BOOLEAN) - ("showSnapshotDelay", BOOLEAN))); + ("showSnapshotDelay", BOOLEAN))) + ("carousel", makeMap(boost::assign::map_list_of + ("type", STRING) + ("size", NORMALIZED_PAIR) + ("pos", NORMALIZED_PAIR) + ("color", COLOR) + ("logoScale", FLOAT) + ("logoSize", NORMALIZED_PAIR) + ("maxLogoCount", FLOAT))); namespace fs = boost::filesystem; #define MINIMUM_THEME_FORMAT_VERSION 3 -#define CURRENT_THEME_FORMAT_VERSION 3 +#define CURRENT_THEME_FORMAT_VERSION 4 // helper unsigned int getHexColor(const char* str) diff --git a/es-core/src/components/TextComponent.cpp b/es-core/src/components/TextComponent.cpp index bce50da12..9ab5effa5 100644 --- a/es-core/src/components/TextComponent.cpp +++ b/es-core/src/components/TextComponent.cpp @@ -1,4 +1,5 @@ #include "components/TextComponent.h" + #include "Renderer.h" #include "Log.h" #include "Window.h" @@ -7,16 +8,17 @@ #include "Settings.h" TextComponent::TextComponent(Window* window) : GuiComponent(window), - mFont(Font::get(FONT_SIZE_MEDIUM)), mUppercase(false), mColor(0x000000FF), mAutoCalcExtent(true, true), mAlignment(ALIGN_LEFT), mLineSpacing(1.5f) + mFont(Font::get(FONT_SIZE_MEDIUM)), mUppercase(false), mColor(0x000000FF), mAutoCalcExtent(true, true), mAlignment(ALIGN_LEFT), mLineSpacing(1.5f), mBgColor(NULL) { } TextComponent::TextComponent(Window* window, const std::string& text, const std::shared_ptr& font, unsigned int color, Alignment align, - Eigen::Vector3f pos, Eigen::Vector2f size) : GuiComponent(window), - mFont(NULL), mUppercase(false), mColor(0x000000FF), mAutoCalcExtent(true, true), mAlignment(align), mLineSpacing(1.5f) + Eigen::Vector3f pos, Eigen::Vector2f size, unsigned int bgcolor) : GuiComponent(window), + mFont(NULL), mUppercase(false), mColor(0x000000FF), mAutoCalcExtent(true, true), mAlignment(align), mLineSpacing(1.5f), mBgColor(NULL) { setFont(font); setColor(color); + setBackgroundColor(bgcolor); setText(text); setPosition(pos); setSize(size); @@ -34,19 +36,34 @@ void TextComponent::setFont(const std::shared_ptr& font) onTextChanged(); } +// Set the color of the font/text void TextComponent::setColor(unsigned int color) { mColor = color; - - unsigned char opacity = mColor & 0x000000FF; - GuiComponent::setOpacity(opacity); - + mColorOpacity = mColor & 0x000000FF; onColorChanged(); } +// Set the color of the background box +void TextComponent::setBackgroundColor(unsigned int color) +{ + mBgColor = color; + mBgColorOpacity = mBgColor & 0x000000FF; +} + +// Scale the opacity void TextComponent::setOpacity(unsigned char opacity) { - mColor = (mColor & 0xFFFFFF00) | opacity; + // This method is mostly called to do fading in-out of the Text component element. + // Therefore, we assume here that opacity is a fractional value (expressed as an int 0-255), + // of the opacity originally set with setColor() or setBackgroundColor(). + + unsigned char o = (unsigned char)((float)opacity / 255.f * (float) mColorOpacity); + mColor = (mColor & 0xFFFFFF00) | (unsigned char) o; + + unsigned char bgo = (unsigned char)((float)opacity / 255.f * (float)mBgColorOpacity); + mBgColor = (mBgColor & 0xFFFFFF00) | (unsigned char)bgo; + onColorChanged(); GuiComponent::setOpacity(opacity); @@ -73,11 +90,11 @@ void TextComponent::render(const Eigen::Affine3f& parentTrans) { Eigen::Affine3f trans = parentTrans * getTransform(); - /*Eigen::Vector3f dim(mSize.x(), mSize.y(), 0); - dim = trans * dim - trans.translation(); - Renderer::pushClipRect(Eigen::Vector2i((int)trans.translation().x(), (int)trans.translation().y()), - Eigen::Vector2i((int)(dim.x() + 0.5f), (int)(dim.y() + 0.5f))); - */ + if (mBgColor) + { + Renderer::drawRect(getPosition().x(), getPosition().y() - 1, + getSize().x(), getSize().y(), mBgColor); + } if(mTextCache) { @@ -90,7 +107,7 @@ void TextComponent::render(const Eigen::Affine3f& parentTrans) Renderer::setMatrix(trans); Renderer::drawRect(0.f, 0.f, mSize.x(), mSize.y(), 0xFF000033); } - + trans.translate(off); trans = roundMatrix(trans); Renderer::setMatrix(trans); @@ -111,11 +128,8 @@ void TextComponent::render(const Eigen::Affine3f& parentTrans) break; } } - mFont->renderTextCache(mTextCache.get()); } - - //Renderer::popClipRect(); } void TextComponent::calculateExtent() @@ -216,8 +230,11 @@ void TextComponent::applyTheme(const std::shared_ptr& theme, const st if(!elem) return; - if(properties & COLOR && elem->has("color")) - setColor(elem->get("color")); + if (properties & COLOR && elem->has("color")) + setColor(elem->get("color")); + + if (properties & COLOR && elem->has("backgroundColor")) + setBackgroundColor(elem->get("backgroundColor")); if(properties & ALIGNMENT && elem->has("alignment")) { diff --git a/es-core/src/components/TextComponent.h b/es-core/src/components/TextComponent.h index 4a4a33fcd..a0dc4a554 100644 --- a/es-core/src/components/TextComponent.h +++ b/es-core/src/components/TextComponent.h @@ -16,7 +16,7 @@ class TextComponent : public GuiComponent public: TextComponent(Window* window); TextComponent(Window* window, const std::string& text, const std::shared_ptr& font, unsigned int color = 0x000000FF, Alignment align = ALIGN_LEFT, - Eigen::Vector3f pos = Eigen::Vector3f::Zero(), Eigen::Vector2f size = Eigen::Vector2f::Zero()); + Eigen::Vector3f pos = Eigen::Vector3f::Zero(), Eigen::Vector2f size = Eigen::Vector2f::Zero(), unsigned int bgcolor = 0x00000000); void setFont(const std::shared_ptr& font); void setUppercase(bool uppercase); @@ -25,6 +25,7 @@ public: void setColor(unsigned int color); void setAlignment(Alignment align); void setLineSpacing(float spacing); + void setBackgroundColor(unsigned int color); void render(const Eigen::Affine3f& parentTrans) override; @@ -45,6 +46,10 @@ private: void onColorChanged(); unsigned int mColor; + unsigned int mBgColor; + unsigned char mColorOpacity; + unsigned char mBgColorOpacity; + std::shared_ptr mFont; bool mUppercase; Eigen::Matrix mAutoCalcExtent;