diff --git a/THEMES.md b/THEMES.md index dfaec05b9..e1b826125 100644 --- a/THEMES.md +++ b/THEMES.md @@ -308,12 +308,8 @@ Reference --- #### system -* `text name="headerText"` - ALL - - A header text, which displays the name of the system. Displayed at the top center of the screen. Centered by default. -* `image name="header"` - ALL - - A header image. If a non-empty `path` is specified, `text name="headerText"` will be hidden and this image will be, by default, displayed roughly in its place. -* image name="system" - ALL - - A large image representing the system (usually a picture of the system itself). +* `image name="header"` - PATH + - A header (logo) image, to be displayed in the system carousel. --- diff --git a/src/views/SystemView.cpp b/src/views/SystemView.cpp index 11b62abb0..29a3594ec 100644 --- a/src/views/SystemView.cpp +++ b/src/views/SystemView.cpp @@ -4,58 +4,57 @@ #include "../Log.h" #include "../Window.h" #include "ViewController.h" +#include "../animations/LambdaAnimation.h" +#include "../SystemData.h" -SystemView::SystemView(Window* window, SystemData* system) : GuiComponent(window), - mSystem(system), +#define SELECTED_SCALE 1.2f +#define LOGO_PADDING (logoSize().x() * (SELECTED_SCALE - 1)/2) - mHeaderImage(window), - mHeaderText(window), - mImage(window), - mExtras(window) +SystemView::SystemView(Window* window) : IList(window) { + mCamOffset = 0; + setSize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight()); - mHeaderImage.setOrigin(0.5f, 0.0f); - mHeaderImage.setPosition(mSize.x() / 2, 0); - mHeaderImage.setResize(0, mSize.y() * 0.2f); - - mHeaderText.setPosition(0, 6); - mHeaderText.setSize(mSize.x(), 0); - mHeaderText.setCentered(true); - - mImage.setOrigin(0.5f, 0.5f); - mImage.setPosition(mSize.x() / 2, mSize.y() * 0.6f); - mImage.setResize(0, mSize.y() * 0.8f); - - addChild(&mExtras); - addChild(&mImage); - addChild(&mHeaderText); - addChild(&mHeaderImage); - - updateData(); + populate(); } -void SystemView::updateData() +void SystemView::populate() { - using namespace ThemeFlags; + mEntries.clear(); - mExtras.setExtras(ThemeData::makeExtras(mSystem->getTheme(), "system", mWindow)); - - mHeaderImage.setImage(""); - mHeaderImage.applyTheme(mSystem->getTheme(), "system", "header", ALL); - - // header - if(mHeaderImage.hasImage()) + for(auto it = SystemData::sSystemVector.begin(); it != SystemData::sSystemVector.end(); it++) { - // use image - mHeaderText.setText(""); - }else{ - // use text - mHeaderImage.setImage(""); - mHeaderText.setText(mSystem->getFullName()); - } + Entry e; + e.name = (*it)->getName(); + e.object = *it; - mImage.applyTheme(mSystem->getTheme(), "system", "system", ALL); + if((*it)->getTheme()->getElement("system", "header", "image")) + { + ImageComponent* logo = new ImageComponent(mWindow); + logo->setMaxSize(logoSize()); + logo->applyTheme((*it)->getTheme(), "system", "header", ThemeFlags::PATH); + logo->setPosition((logoSize().x() - logo->getSize().x()) / 2, 0); + e.data.logo = std::shared_ptr(logo); + }else{ + // no logo in theme; use text + TextComponent* text = new TextComponent(mWindow); + text->setFont(Font::get(FONT_SIZE_LARGE)); + text->setText((*it)->getName()); + text->setSize(logoSize()); + text->setCentered(true); + e.data.logo = std::shared_ptr(text); + } + + e.data.title = nullptr; + + this->add(e); + } +} + +void SystemView::goToSystem(SystemData* system) +{ + setCursor(system); } bool SystemView::input(InputConfig* config, Input input) @@ -64,27 +63,129 @@ bool SystemView::input(InputConfig* config, Input input) { if(config->isMappedTo("left", input)) { - mWindow->getViewController()->goToSystemView(mSystem->getPrev()); + listInput(-1); return true; } if(config->isMappedTo("right", input)) { - mWindow->getViewController()->goToSystemView(mSystem->getNext()); + listInput(1); return true; } if(config->isMappedTo("a", input)) { - mWindow->getViewController()->goToGameList(mSystem); + stopScrolling(); + mWindow->getViewController()->goToGameList(getSelected()); return true; } + }else{ + if(config->isMappedTo("left", input) || config->isMappedTo("right", input)) + listInput(0); } return GuiComponent::input(config, input); } +void SystemView::update(int deltaTime) +{ + listUpdate(deltaTime); + GuiComponent::update(deltaTime); +} + +void SystemView::onCursorChanged(const CursorState& state) +{ + float startPos = mCamOffset; + + const float logoSizeX = logoSize().x() + LOGO_PADDING; + + float posMax = logoSizeX * mEntries.size(); + float target = mCursor * logoSizeX; + + // what's the shortest way to get to our target? + // it's one of these... + + float endPos = target; // directly + float dist = abs(endPos - startPos); + + if(abs(target + posMax - startPos) < dist) + endPos = target + posMax; // loop around the end (0 -> max) + if(abs(target - posMax - startPos) < dist) + endPos = target - posMax; // loop around the start (max - 1 -> -1) + + Animation* anim = new LambdaAnimation( + [startPos, endPos, posMax, this] (float t) + { + t -= 1; + float f = lerp(startPos, endPos, t*t*t + 1); + if(f < 0) + f += posMax; + if(f >= posMax) + f -= posMax; + this->mCamOffset = f; + }, 400); + + setAnimation(anim); +} + +void SystemView::render(const Eigen::Affine3f& parentTrans) +{ + if(size() == 0) + return; + + const float logoSizeX = logoSize().x() + LOGO_PADDING; + + Eigen::Affine3f trans = getTransform() * parentTrans; + + // draw background image + + // draw system's stats + + // now for the list elements (logos) + Eigen::Affine3f logoTrans = trans; + + int logoCount = (int)(mSize.x() / logoSizeX) + 2; // how many logos we need to draw + int center = (int)(mCamOffset / logoSizeX + 0.5f); + + float xOff = (mSize.x() - logoSize().x())/2 - mCamOffset; + float yOff = (mSize.y() - logoSize().y())/2; + + // this fixes the case where i != mCursor when wrapping around the end back to zero + while(center >= (int)mEntries.size()) + { + center -= mEntries.size(); + xOff += logoSizeX * mEntries.size(); + } + + for(int i = center - logoCount/2; i < center + logoCount/2 + logoCount%2; 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); + + std::shared_ptr comp = mEntries.at(index).data.logo; + if(comp) + { + if(i == mCursor) //scale our selection up + { + // fix the centering because we go by left corner and not center (bleh) + logoTrans.translation() -= Eigen::Vector3f((comp->getSize().x() / 2) * (SELECTED_SCALE - 1), (comp->getSize().y() / 2) * (SELECTED_SCALE - 1), 0); + logoTrans.scale(Eigen::Vector3f(SELECTED_SCALE, SELECTED_SCALE, 1.0f)); + mEntries.at(index).data.logo->render(logoTrans); + logoTrans.scale(Eigen::Vector3f(1/SELECTED_SCALE, 1/SELECTED_SCALE, 1.0f)); + }else{ + mEntries.at(index).data.logo->render(logoTrans); + } + } + } +} + std::vector SystemView::getHelpPrompts() { std::vector prompts; + prompts.push_back(HelpPrompt("left/right", "choose")); prompts.push_back(HelpPrompt("a", "select")); return prompts; } diff --git a/src/views/SystemView.h b/src/views/SystemView.h index 89620bf5f..d3edd9113 100644 --- a/src/views/SystemView.h +++ b/src/views/SystemView.h @@ -4,25 +4,37 @@ #include "../components/ImageComponent.h" #include "../components/TextComponent.h" #include "../components/ScrollableContainer.h" +#include "../components/IList.h" +#include "../resources/TextureResource.h" class SystemData; -class SystemView : public GuiComponent +struct SystemViewData +{ + std::shared_ptr title; + std::shared_ptr logo; +}; + +class SystemView : public IList { public: - SystemView(Window* window, SystemData* system); + SystemView(Window* window); - void updateData(); + void goToSystem(SystemData* system); bool input(InputConfig* config, Input input) override; + void update(int deltaTime) override; + void render(const Eigen::Affine3f& parentTrans) override; std::vector getHelpPrompts() override; + +protected: + void onCursorChanged(const CursorState& state) override; private: - SystemData* mSystem; + inline Eigen::Vector2f logoSize() const { return Eigen::Vector2f(mSize.x() * 0.3f, mSize.y() * 0.25f); } - TextComponent mHeaderText; - ImageComponent mHeaderImage; - ImageComponent mImage; - ThemeExtras mExtras; + void populate(); + + float mCamOffset; }; diff --git a/src/views/ViewController.cpp b/src/views/ViewController.cpp index 51f3855b9..063727ac4 100644 --- a/src/views/ViewController.cpp +++ b/src/views/ViewController.cpp @@ -30,7 +30,10 @@ void ViewController::goToStart() void ViewController::goToSystemView(SystemData* system) { mState.viewing = SYSTEM_SELECT; - mCurrentView = getSystemView(system); + mState.system = system; + + getSystemListView()->goToSystem(system); + mCurrentView = getSystemListView(); updateHelpPrompts(); playViewTransition(); } @@ -54,7 +57,7 @@ void ViewController::goToPrevGameList() void ViewController::goToGameList(SystemData* system) { mState.viewing = GAME_LIST; - mState.data.system = system; + mState.system = system; mCurrentView = getGameListView(system); updateHelpPrompts(); @@ -140,18 +143,15 @@ std::shared_ptr ViewController::getGameListView(SystemData* syste return view; } -std::shared_ptr ViewController::getSystemView(SystemData* system) +std::shared_ptr ViewController::getSystemListView() { //if we already made one, return that one - auto exists = mSystemViews.find(system); - if(exists != mSystemViews.end()) - return exists->second; + if(mSystemListView) + return mSystemListView; - //if we didn't, make it, remember it, and return it - std::shared_ptr view = std::shared_ptr(new SystemView(mWindow, system)); - view->setPosition((system->getIterator() - SystemData::sSystemVector.begin()) * (float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight()); - mSystemViews[system] = view; - return view; + mSystemListView = std::shared_ptr(new SystemView(mWindow)); + mSystemListView->setPosition(0, (float)Renderer::getScreenHeight()); + return mSystemListView; } @@ -192,13 +192,9 @@ void ViewController::render(const Eigen::Affine3f& parentTrans) Eigen::Vector3f viewStart = trans.inverse().translation(); Eigen::Vector3f viewEnd = trans.inverse() * Eigen::Vector3f((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight(), 0); - // draw systemviews - for(auto it = mSystemViews.begin(); it != mSystemViews.end(); it++) - { - // should do clipping - it->second->render(trans); - } - + // draw systemview + getSystemListView()->render(trans); + // draw gamelists for(auto it = mGameListViews.begin(); it != mGameListViews.end(); it++) { @@ -223,7 +219,6 @@ void ViewController::preload() { for(auto it = SystemData::sSystemVector.begin(); it != SystemData::sSystemVector.end(); it++) { - getSystemView(*it); getGameListView(*it); } } diff --git a/src/views/ViewController.h b/src/views/ViewController.h index 5104ef3ec..6c052b310 100644 --- a/src/views/ViewController.h +++ b/src/views/ViewController.h @@ -48,14 +48,11 @@ public: { ViewMode viewing; - inline SystemData* getSystem() const { assert(viewing == GAME_LIST); return data.system; } + inline SystemData* getSystem() const { assert(viewing == GAME_LIST || viewing == SYSTEM_SELECT); return system; } private: friend ViewController; - union - { - SystemData* system; - } data; + SystemData* system; }; inline const State& getState() const { return mState; } @@ -65,11 +62,11 @@ public: private: void playViewTransition(); std::shared_ptr getGameListView(SystemData* system); - std::shared_ptr getSystemView(SystemData* system); + std::shared_ptr getSystemListView(); std::shared_ptr mCurrentView; std::map< SystemData*, std::shared_ptr > mGameListViews; - std::map< SystemData*, std::shared_ptr > mSystemViews; + std::shared_ptr mSystemListView; Eigen::Affine3f mCamera; float mFadeOpacity;