diff --git a/CMakeLists.txt b/CMakeLists.txt index a8b7bb023..a23c16793 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -193,10 +193,11 @@ set(ES_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/pugiXML/pugiconfig.hpp ${CMAKE_CURRENT_SOURCE_DIR}/src/pugiXML/pugixml.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/views/BasicGameListView.h - ${CMAKE_CURRENT_SOURCE_DIR}/src/views/DetailedGameListView.h - ${CMAKE_CURRENT_SOURCE_DIR}/src/views/GameListView.h - ${CMAKE_CURRENT_SOURCE_DIR}/src/views/GridGameListView.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/views/gamelist/BasicGameListView.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/views/gamelist/DetailedGameListView.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/views/gamelist/IGameListView.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/views/gamelist/ISimpleGameListView.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/views/gamelist/GridGameListView.h ${CMAKE_CURRENT_SOURCE_DIR}/src/views/SystemListView.h ${CMAKE_CURRENT_SOURCE_DIR}/src/views/SystemView.h ${CMAKE_CURRENT_SOURCE_DIR}/src/views/ViewController.h @@ -271,10 +272,11 @@ set(ES_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/resources/ResourceManager.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/resources/TextureResource.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/views/BasicGameListView.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/views/DetailedGameListView.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/views/GameListView.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/views/GridGameListView.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/views/gamelist/BasicGameListView.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/views/gamelist/DetailedGameListView.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/views/gamelist/IGameListView.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/views/gamelist/ISimpleGameListView.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/views/gamelist/GridGameListView.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/views/SystemListView.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/views/SystemView.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/views/ViewController.cpp diff --git a/src/components/GuiFastSelect.cpp b/src/components/GuiFastSelect.cpp index 0022f4abc..2df321dea 100644 --- a/src/components/GuiFastSelect.cpp +++ b/src/components/GuiFastSelect.cpp @@ -5,7 +5,7 @@ static const std::string LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; -GuiFastSelect::GuiFastSelect(Window* window, GameListView* gamelist) : GuiComponent(window), +GuiFastSelect::GuiFastSelect(Window* window, IGameListView* gamelist) : GuiComponent(window), mBackground(window), mSortText(window), mLetterText(window), mGameList(gamelist) { setPosition(Renderer::getScreenWidth() * 0.2f, Renderer::getScreenHeight() * 0.2f); diff --git a/src/components/GuiFastSelect.h b/src/components/GuiFastSelect.h index dfee83433..3e2eb309f 100644 --- a/src/components/GuiFastSelect.h +++ b/src/components/GuiFastSelect.h @@ -1,7 +1,7 @@ #pragma once #include "../GuiComponent.h" -#include "../views/GameListView.h" +#include "../views/gamelist/IGameListView.h" #include "NinePatchComponent.h" #include "TextComponent.h" @@ -9,7 +9,7 @@ class GuiFastSelect : public GuiComponent { public: - GuiFastSelect(Window* window, GameListView* gamelist); + GuiFastSelect(Window* window, IGameListView* gamelist); bool input(InputConfig* config, Input input); void update(int deltaTime); @@ -31,5 +31,5 @@ private: TextComponent mSortText; TextComponent mLetterText; - GameListView* mGameList; + IGameListView* mGameList; }; diff --git a/src/components/ImageGridComponent.h b/src/components/ImageGridComponent.h index c185ac1dc..f9e73ce94 100644 --- a/src/components/ImageGridComponent.h +++ b/src/components/ImageGridComponent.h @@ -24,6 +24,7 @@ public: void clear(); void setCursor(const T& select); + void setCursor(typename const std::vector::const_iterator& it); inline const T& getSelected() const { return mEntries.at(mCursor).object; } inline const std::vector& getList() const { return mEntries; } @@ -171,6 +172,14 @@ void ImageGridComponent::setCursor(const T& obj) LOG(LogError) << "Tried to set cursor to object we couldn't find"; } +template +void ImageGridComponent::setCursor(typename const std::vector::const_iterator& it) +{ + assert(it != mEntries.end()); + mCursor = it - mEntries.begin(); + onCursorChanged(CURSOR_STOPPED); +} + template void ImageGridComponent::stopScrolling() { diff --git a/src/components/TextListComponent.h b/src/components/TextListComponent.h index 4313694bf..c63c50050 100644 --- a/src/components/TextListComponent.h +++ b/src/components/TextListComponent.h @@ -49,6 +49,7 @@ public: inline const std::vector& getList() const { return mRowVector; } void setCursor(const T& select); + void setCursor(typename const std::vector::const_iterator& it); void stopScrolling(); inline bool isScrolling() const { return mScrollDir != 0; } @@ -375,6 +376,15 @@ void TextListComponent::setCursor(const T& obj) LOG(LogError) << "Tried to set cursor to object we couldn't find"; } + +template +void TextListComponent::setCursor(typename const std::vector::const_iterator& it) +{ + assert(it != mRowVector.end()); + mCursor = it - mRowVector.begin(); + onCursorChanged(CURSOR_STOPPED); +} + template void TextListComponent::onCursorChanged(CursorState state) { diff --git a/src/views/BasicGameListView.cpp b/src/views/BasicGameListView.cpp deleted file mode 100644 index ee886d837..000000000 --- a/src/views/BasicGameListView.cpp +++ /dev/null @@ -1,183 +0,0 @@ -#include "BasicGameListView.h" -#include "ViewController.h" -#include "../Renderer.h" -#include "../Window.h" -#include "../ThemeData.h" - -BasicGameListView::BasicGameListView(Window* window, FileData* root) - : GameListView(window, root), - mHeaderText(window), mHeaderImage(window), mBackground(window), mList(window) -{ - mHeaderText.setText("Header"); - mHeaderText.setSize(mSize.x(), 0); - mHeaderText.setPosition(0, 0); - mHeaderText.setCentered(true); - - mHeaderImage.setResize(0, mSize.y() * 0.185f, false); - mHeaderImage.setOrigin(0.5f, 0.0f); - mHeaderImage.setPosition(mSize.x() / 2, 0); - - mBackground.setResize(mSize.x(), mSize.y(), true); - - mList.setSize(mSize.x(), mSize.y() * 0.8f); - mList.setPosition(0, mSize.y() * 0.2f); - - populateList(root); - - addChild(&mBackground); - addChild(&mList); - addChild(&mHeaderText); -} - -void BasicGameListView::onThemeChanged(const std::shared_ptr& theme) -{ - const ImageDef& bg = theme->getImage("backgroundImage"); - mBackground.setTiling(bg.tile); - mBackground.setImage(bg.getTexture()); - - const ImageDef& hdr = theme->getImage("headerImage"); - mHeaderImage.setTiling(hdr.tile); - mHeaderImage.setImage(hdr.getTexture()); - - if(mHeaderImage.hasImage()) - { - removeChild(&mHeaderText); - addChild(&mHeaderImage); - }else{ - addChild(&mHeaderText); - removeChild(&mHeaderImage); - } - - mList.setTheme(theme); -} - -void BasicGameListView::onFileChanged(FileData* file, FileChangeType change) -{ - // we don't care about metadata changes (since we don't display metadata), - // so we just ignore the FILE_METADATA_CHANGED case - - // if it's immediately inside our current folder - if(file->getParent() == getCurrentFolder()) - { - if(change == FILE_REMOVED) - { - mList.remove(file); // will automatically make sure cursor ends up in a "safe" place - }else if(change == FILE_ADDED) - { - FileData* cursor = getCursor(); - populateList(cursor->getParent()); - mList.setCursor(cursor); - } - } - - // the root file was sorted (so children were sorted too) - if(file == mRoot && change == FILE_SORTED) - { - FileData* cursor = getCursor(); - populateList(cursor->getParent()); - mList.setCursor(cursor); - } -} - -void buildHeader(FileData* from, std::stringstream& ss) -{ - if(from->getParent()) - { - buildHeader(from->getParent(), ss); - ss << " -> "; - } - - ss << from->getName(); -} - -void BasicGameListView::populateList(FileData* root) -{ - mList.clear(); - - std::stringstream ss; - buildHeader(root, ss); - mHeaderText.setText(ss.str()); - - for(auto it = root->getChildren().begin(); it != root->getChildren().end(); it++) - { - mList.add((*it)->getName(), *it, ((*it)->getType() == FOLDER)); - } -} - -void BasicGameListView::setCursor(FileData* cursor) -{ - if(cursor->getParent() != getCursor()->getParent()) - { - // Rebuild the folder stack - std::stack path; - FileData* cur = cursor; - while((cur = cur->getParent()) != mRoot) - path.push(cur); - - while(!mCursorStack.empty()) - mCursorStack.pop(); - - while(!path.empty()) // put back in reverse order (flip) - { - mCursorStack.push(path.top()); - path.pop(); - } - - populateList(cursor->getParent()); - } - - mList.setCursor(cursor); -} - -bool BasicGameListView::input(InputConfig* config, Input input) -{ - if(input.value != 0) - { - if(config->isMappedTo("a", input)) - { - if(mList.getList().size() > 0) - { - FileData* cursor = getCursor(); - if(cursor->getType() == GAME) - { - mWindow->getViewController()->launch(cursor); - }else{ - // it's a folder - if(cursor->getChildren().size() > 0) - { - mCursorStack.push(cursor); - populateList(cursor); - } - } - - return true; - } - }else if(config->isMappedTo("b", input)) - { - if(mCursorStack.size()) - { - populateList(mCursorStack.top()->getParent()); - mList.setCursor(mCursorStack.top()); - mCursorStack.pop(); - mTheme->playSound("backSound"); - }else{ - mList.stopScrolling(); - mWindow->getViewController()->goToSystemSelect(); - } - - return true; - }else if(config->isMappedTo("right", input)) - { - mList.stopScrolling(); - mWindow->getViewController()->goToNextGameList(); - return true; - }else if(config->isMappedTo("left", input)) - { - mList.stopScrolling(); - mWindow->getViewController()->goToPrevGameList(); - return true; - } - } - - return GameListView::input(config, input); -} diff --git a/src/views/BasicGameListView.h b/src/views/BasicGameListView.h deleted file mode 100644 index f59beab37..000000000 --- a/src/views/BasicGameListView.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include "GameListView.h" -#include "../components/TextListComponent.h" -#include "../components/TextComponent.h" -#include "../components/ImageComponent.h" - -class BasicGameListView : public GameListView -{ -public: - BasicGameListView(Window* window, FileData* root); - - // Called when a FileData* is added, has its metadata changed, or is removed - virtual void onFileChanged(FileData* file, FileChangeType change); - - virtual bool input(InputConfig* config, Input input) override; - - virtual void onThemeChanged(const std::shared_ptr& theme) override; - - inline FileData* getCursor() { return mList.getSelected(); } - virtual void setCursor(FileData* file) override; - -protected: - void populateList(FileData* root); - - TextComponent mHeaderText; - ImageComponent mHeaderImage; - ImageComponent mBackground; - TextListComponent mList; - - inline FileData* getCurrentFolder() { return getCursor()->getParent(); } - - std::stack mCursorStack; -}; diff --git a/src/views/GridGameListView.cpp b/src/views/GridGameListView.cpp deleted file mode 100644 index a833ea352..000000000 --- a/src/views/GridGameListView.cpp +++ /dev/null @@ -1,93 +0,0 @@ -#include "GridGameListView.h" -#include "../ThemeData.h" -#include "../Window.h" -#include "ViewController.h" - -GridGameListView::GridGameListView(Window* window, FileData* root) : GameListView(window, root), - mGrid(window), mBackground(window) -{ - mBackground.setResize(mSize.x(), mSize.y(), true); - addChild(&mBackground); - - mGrid.setPosition(0, mSize.y() * 0.2f); - mGrid.setSize(mSize.x(), mSize.y() * 0.8f); - addChild(&mGrid); - - populateList(root); -} - -void GridGameListView::onFileChanged(FileData* file, FileChangeType change) -{ - // fuck it, just completely repopulate always all the time - FileData* cursor = getCursor(); - populateList(cursor->getParent()); - mGrid.setCursor(cursor); -} - -void GridGameListView::onThemeChanged(const std::shared_ptr& theme) -{ - mBackground.setImage(theme->getImage("backgroundImage").getTexture()); - mBackground.setTiling(theme->getImage("backgroundImage").tile); -} - -FileData* GridGameListView::getCursor() -{ - return mGrid.getSelected(); -} - -void GridGameListView::setCursor(FileData* file) -{ - assert(file->getParent() == getCursor()->getParent()); // too lazy to implement right now - mGrid.setCursor(file); -} - -bool GridGameListView::input(InputConfig* config, Input input) -{ - if(input.value != 0) - { - if(config->isMappedTo("a", input)) - { - if(mGrid.getList().size() > 0) - { - FileData* cursor = getCursor(); - if(cursor->getType() == GAME) - { - mWindow->getViewController()->launch(cursor); - }else{ - // it's a folder - if(cursor->getChildren().size() > 0) - { - mCursorStack.push(cursor); - populateList(cursor); - } - } - - return true; - } - }else if(config->isMappedTo("b", input)) - { - if(mCursorStack.size()) - { - populateList(mCursorStack.top()->getParent()); - mGrid.setCursor(mCursorStack.top()); - mCursorStack.pop(); - mTheme->playSound("backSound"); - }else{ - mGrid.stopScrolling(); - mWindow->getViewController()->goToSystemSelect(); - } - - return true; - } - } - return GameListView::input(config, input); -} - -void GridGameListView::populateList(FileData* root) -{ - mGrid.clear(); - for(auto it = root->getChildren().begin(); it != root->getChildren().end(); it++) - { - mGrid.add((*it)->getThumbnailPath(), *it); - } -} diff --git a/src/views/GridGameListView.h b/src/views/GridGameListView.h deleted file mode 100644 index f9c36afe0..000000000 --- a/src/views/GridGameListView.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include "GameListView.h" -#include "../components/ImageGridComponent.h" -#include "../components/ImageComponent.h" -#include - -class GridGameListView : public GameListView -{ -public: - GridGameListView(Window* window, FileData* root); - - virtual void onFileChanged(FileData* file, FileChangeType change) override; - virtual void onThemeChanged(const std::shared_ptr& theme) override; - - FileData* getCursor() override; - void setCursor(FileData*) override; - - virtual bool input(InputConfig* config, Input input) override; - -private: - void populateList(FileData* root); - - ImageGridComponent mGrid; - ImageComponent mBackground; - - std::stack mCursorStack; -}; diff --git a/src/views/ViewController.cpp b/src/views/ViewController.cpp index 36d8850a6..20d4814f9 100644 --- a/src/views/ViewController.cpp +++ b/src/views/ViewController.cpp @@ -2,9 +2,9 @@ #include "../Log.h" #include "../SystemData.h" -#include "BasicGameListView.h" -#include "DetailedGameListView.h" -#include "GridGameListView.h" +#include "gamelist/BasicGameListView.h" +#include "gamelist/DetailedGameListView.h" +#include "gamelist/GridGameListView.h" #include "../components/GuiMenu.h" #include "../animations/LaunchAnimation.h" #include "../animations/MoveCameraAnimation.h" @@ -116,7 +116,7 @@ void ViewController::launch(FileData* game, Eigen::Vector3f center) }); } -std::shared_ptr ViewController::getGameListView(SystemData* system) +std::shared_ptr ViewController::getGameListView(SystemData* system) { //if we already made one, return that one auto exists = mGameListViews.find(system); @@ -124,7 +124,7 @@ std::shared_ptr ViewController::getGameListView(SystemData* system return exists->second; //if we didn't, make it, remember it, and return it - std::shared_ptr view; + std::shared_ptr view; if(system != NULL) { @@ -141,11 +141,11 @@ std::shared_ptr ViewController::getGameListView(SystemData* system } if(detailed) - view = std::shared_ptr(new DetailedGameListView(mWindow, system->getRootFolder())); + view = std::shared_ptr(new DetailedGameListView(mWindow, system->getRootFolder())); else - view = std::shared_ptr(new BasicGameListView(mWindow, system->getRootFolder())); + view = std::shared_ptr(new BasicGameListView(mWindow, system->getRootFolder())); - //view = std::shared_ptr(new GridGameListView(mWindow, system->getRootFolder())); + view = std::shared_ptr(new GridGameListView(mWindow, system->getRootFolder())); view->setTheme(system->getTheme()); }else{ @@ -240,3 +240,21 @@ void ViewController::preload() getGameListView(*it); } } + +void ViewController::reloadGameListView(IGameListView* view) +{ + for(auto it = mGameListViews.begin(); it != mGameListViews.end(); it++) + { + if(it->second.get() == view) + { + SystemData* system = it->first; + FileData* cursor = view->getCursor(); + mGameListViews.erase(it); + + std::shared_ptr newView = getGameListView(system); + newView->setCursor(cursor); + mCurrentView = newView; + break; + } + } +} diff --git a/src/views/ViewController.h b/src/views/ViewController.h index bb3472cb9..c9f82363e 100644 --- a/src/views/ViewController.h +++ b/src/views/ViewController.h @@ -1,6 +1,6 @@ #pragma once -#include "GameListView.h" +#include "gamelist/IGameListView.h" #include "SystemListView.h" class SystemData; @@ -14,6 +14,10 @@ public: // Caches things so there's no pauses during transitions. void preload(); + // If a basic view detected a metadata change, it can request to recreate + // the current gamelist view (as it may change to be detailed). + void reloadGameListView(IGameListView* gamelist); + // Navigation. void goToNextGameList(); void goToPrevGameList(); @@ -55,11 +59,11 @@ public: private: void playViewTransition(); - std::shared_ptr getGameListView(SystemData* system); + std::shared_ptr getGameListView(SystemData* system); std::shared_ptr getSystemListView(); std::shared_ptr mCurrentView; - std::map< SystemData*, std::shared_ptr > mGameListViews; + std::map< SystemData*, std::shared_ptr > mGameListViews; std::shared_ptr mSystemListView; Eigen::Affine3f mCamera; diff --git a/src/views/gamelist/BasicGameListView.cpp b/src/views/gamelist/BasicGameListView.cpp new file mode 100644 index 000000000..b1663ff55 --- /dev/null +++ b/src/views/gamelist/BasicGameListView.cpp @@ -0,0 +1,71 @@ +#include "BasicGameListView.h" +#include "../ViewController.h" +#include "../../Renderer.h" +#include "../../Window.h" +#include "../../ThemeData.h" +#include "../../SystemData.h" + +BasicGameListView::BasicGameListView(Window* window, FileData* root) + : ISimpleGameListView(window, root), mList(window) +{ + mList.setSize(mSize.x(), mSize.y() * 0.8f); + mList.setPosition(0, mSize.y() * 0.2f); + addChild(&mList); + + populateList(root->getChildren()); +} + +void BasicGameListView::onThemeChanged(const std::shared_ptr& theme) +{ + ISimpleGameListView::onThemeChanged(theme); + mList.setTheme(theme); +} + +void BasicGameListView::onFileChanged(FileData* file, FileChangeType change) +{ + if(change == FILE_METADATA_CHANGED) + { + // might switch to a detailed view + mWindow->getViewController()->reloadGameListView(this); + return; + } + + ISimpleGameListView::onFileChanged(file, change); +} + +void BasicGameListView::populateList(const std::vector& files) +{ + mList.clear(); + + mHeaderText.setText(files.at(0)->getSystem()->getFullName()); + + for(auto it = files.begin(); it != files.end(); it++) + { + mList.add((*it)->getName(), *it, ((*it)->getType() == FOLDER)); + } +} + +FileData* BasicGameListView::getCursor() +{ + return mList.getSelected(); +} + +void BasicGameListView::setCursor(FileData* cursor) +{ + typedef TextListComponent::ListRow Row; + const std::vector& list = mList.getList(); + auto found = std::find_if(list.begin(), list.end(), [&](const Row& row) { return (row.object == cursor); }); + + if(found != list.end()) + { + mList.setCursor(found); + }else{ + populateList(cursor->getParent()->getChildren()); + mList.setCursor(cursor); + } +} + +void BasicGameListView::launch(FileData* game) +{ + mWindow->getViewController()->launch(game); +} diff --git a/src/views/gamelist/BasicGameListView.h b/src/views/gamelist/BasicGameListView.h new file mode 100644 index 000000000..347934629 --- /dev/null +++ b/src/views/gamelist/BasicGameListView.h @@ -0,0 +1,24 @@ +#pragma once + +#include "ISimpleGameListView.h" +#include "../../components/TextListComponent.h" + +class BasicGameListView : public ISimpleGameListView +{ +public: + BasicGameListView(Window* window, FileData* root); + + // Called when a FileData* is added, has its metadata changed, or is removed + virtual void onFileChanged(FileData* file, FileChangeType change); + + virtual void onThemeChanged(const std::shared_ptr& theme); + + virtual FileData* getCursor() override; + virtual void setCursor(FileData* file) override; + +protected: + virtual void populateList(const std::vector& files) override; + virtual void launch(FileData* game) override; + + TextListComponent mList; +}; diff --git a/src/views/DetailedGameListView.cpp b/src/views/gamelist/DetailedGameListView.cpp similarity index 89% rename from src/views/DetailedGameListView.cpp rename to src/views/gamelist/DetailedGameListView.cpp index e21c8edcc..06bf730d5 100644 --- a/src/views/DetailedGameListView.cpp +++ b/src/views/gamelist/DetailedGameListView.cpp @@ -1,4 +1,6 @@ #include "DetailedGameListView.h" +#include "../../Window.h" +#include "../ViewController.h" DetailedGameListView::DetailedGameListView(Window* window, FileData* root) : BasicGameListView(window, root), @@ -75,10 +77,11 @@ void DetailedGameListView::updateInfoPanel() } } -void DetailedGameListView::onFileChanged(FileData* file, FileChangeType type) +void DetailedGameListView::launch(FileData* game) { - BasicGameListView::onFileChanged(file, type); + Eigen::Vector3f target(Renderer::getScreenWidth() / 2.0f, Renderer::getScreenHeight() / 2.0f, 0); + if(mImage.hasImage()) + target = mImage.getPosition(); - if(type == FILE_METADATA_CHANGED && file == getCursor()) - updateInfoPanel(); + mWindow->getViewController()->launch(game, target); } diff --git a/src/views/DetailedGameListView.h b/src/views/gamelist/DetailedGameListView.h similarity index 68% rename from src/views/DetailedGameListView.h rename to src/views/gamelist/DetailedGameListView.h index 7f7ab50d3..7e6405244 100644 --- a/src/views/DetailedGameListView.h +++ b/src/views/gamelist/DetailedGameListView.h @@ -1,9 +1,7 @@ #pragma once #include "BasicGameListView.h" -#include "../components/ImageComponent.h" -#include "../components/TextComponent.h" -#include "../components/ScrollableContainer.h" +#include "../../components/ScrollableContainer.h" class DetailedGameListView : public BasicGameListView { @@ -12,7 +10,8 @@ public: virtual void onThemeChanged(const std::shared_ptr& theme) override; - virtual void onFileChanged(FileData* file, FileChangeType change); +protected: + virtual void launch(FileData* game) override; private: void updateInfoPanel(); @@ -24,4 +23,3 @@ private: ScrollableContainer mDescContainer; TextComponent mDescription; }; - diff --git a/src/views/gamelist/GridGameListView.cpp b/src/views/gamelist/GridGameListView.cpp new file mode 100644 index 000000000..d6afd601a --- /dev/null +++ b/src/views/gamelist/GridGameListView.cpp @@ -0,0 +1,56 @@ +#include "GridGameListView.h" +#include "../../ThemeData.h" +#include "../../Window.h" +#include "../ViewController.h" + +GridGameListView::GridGameListView(Window* window, FileData* root) : ISimpleGameListView(window, root), + mGrid(window) +{ + mGrid.setPosition(0, mSize.y() * 0.2f); + mGrid.setSize(mSize.x(), mSize.y() * 0.8f); + addChild(&mGrid); + + populateList(root->getChildren()); +} + +FileData* GridGameListView::getCursor() +{ + return mGrid.getSelected(); +} + +void GridGameListView::setCursor(FileData* file) +{ + typedef ImageGridComponent::Entry Entry; + auto& list = mGrid.getList(); + auto found = std::find_if(list.begin(), list.end(), [&] (const Entry& e) { return (e.object == file); }); + + if(found != list.end()) + { + mGrid.setCursor(found); + }else{ + populateList(file->getParent()->getChildren()); + mGrid.setCursor(file); + } +} + +bool GridGameListView::input(InputConfig* config, Input input) +{ + if(config->isMappedTo("left", input) || config->isMappedTo("right", input)) + return GuiComponent::input(config, input); + + return ISimpleGameListView::input(config, input); +} + +void GridGameListView::populateList(const std::vector& files) +{ + mGrid.clear(); + for(auto it = files.begin(); it != files.end(); it++) + { + mGrid.add((*it)->getThumbnailPath(), *it); + } +} + +void GridGameListView::launch(FileData* game) +{ + mWindow->getViewController()->launch(game); +} diff --git a/src/views/gamelist/GridGameListView.h b/src/views/gamelist/GridGameListView.h new file mode 100644 index 000000000..42db55ede --- /dev/null +++ b/src/views/gamelist/GridGameListView.h @@ -0,0 +1,25 @@ +#pragma once + +#include "ISimpleGameListView.h" +#include "../../components/ImageGridComponent.h" +#include "../../components/ImageComponent.h" +#include + +class GridGameListView : public ISimpleGameListView +{ +public: + GridGameListView(Window* window, FileData* root); + + //virtual void onThemeChanged(const std::shared_ptr& theme) override; + + virtual FileData* getCursor() override; + virtual void setCursor(FileData*) override; + + virtual bool input(InputConfig* config, Input input) override; + +protected: + virtual void populateList(const std::vector& files) override; + virtual void launch(FileData* game) override; + + ImageGridComponent mGrid; +}; diff --git a/src/views/GameListView.cpp b/src/views/gamelist/IGameListView.cpp similarity index 68% rename from src/views/GameListView.cpp rename to src/views/gamelist/IGameListView.cpp index 743fb0bf6..26e1fabd4 100644 --- a/src/views/GameListView.cpp +++ b/src/views/gamelist/IGameListView.cpp @@ -1,11 +1,11 @@ -#include "GameListView.h" -#include "../Window.h" -#include "../components/GuiMetaDataEd.h" -#include "../components/GuiMenu.h" -#include "../components/GuiFastSelect.h" -#include "ViewController.h" +#include "IGameListView.h" +#include "../../Window.h" +#include "../../components/GuiMetaDataEd.h" +#include "../../components/GuiMenu.h" +#include "../../components/GuiFastSelect.h" +#include "../ViewController.h" -bool GameListView::input(InputConfig* config, Input input) +bool IGameListView::input(InputConfig* config, Input input) { if(config->getDeviceId() == DEVICE_KEYBOARD && input.id == SDLK_F3 && input.value != 0) { @@ -15,7 +15,7 @@ bool GameListView::input(InputConfig* config, Input input) p.game = file; p.system = file->getSystem(); mWindow->pushGui(new GuiMetaDataEd(mWindow, &file->metadata, file->metadata.getMDD(), p, file->getPath().filename().string(), - std::bind(&GameListView::onFileChanged, this, file, FILE_METADATA_CHANGED), [file, this] { + std::bind(&IGameListView::onFileChanged, this, file, FILE_METADATA_CHANGED), [file, this] { boost::filesystem::remove(file->getPath()); //actually delete the file on the filesystem file->getParent()->removeChild(file); //unlink it so list repopulations triggered from onFileChanged won't see it onFileChanged(file, FILE_REMOVED); //tell the view @@ -33,7 +33,7 @@ bool GameListView::input(InputConfig* config, Input input) return GuiComponent::input(config, input); } -void GameListView::setTheme(const std::shared_ptr& theme) +void IGameListView::setTheme(const std::shared_ptr& theme) { mTheme = theme; onThemeChanged(theme); diff --git a/src/views/GameListView.h b/src/views/gamelist/IGameListView.h similarity index 81% rename from src/views/GameListView.h rename to src/views/gamelist/IGameListView.h index 1ea16bd7a..14abab01d 100644 --- a/src/views/GameListView.h +++ b/src/views/gamelist/IGameListView.h @@ -1,24 +1,24 @@ #pragma once -#include "../FileData.h" -#include "../Renderer.h" +#include "../../FileData.h" +#include "../../Renderer.h" class Window; class GuiComponent; class FileData; class ThemeData; -//GameListView needs to know: +//IGameListView needs to know: // What theme data to use // The root FileData for the tree it should explore -class GameListView : public GuiComponent +class IGameListView : public GuiComponent { public: - GameListView(Window* window, FileData* root) : GuiComponent(window), mRoot(root) + IGameListView(Window* window, FileData* root) : GuiComponent(window), mRoot(root) { setSize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight()); } - virtual ~GameListView() {} + virtual ~IGameListView() {} // Called when a new file is added, a file is removed, a file's metadata changes, or a file's children are sorted. // NOTE: FILE_SORTED is only reported for the topmost FileData, where the sort started. diff --git a/src/views/gamelist/ISimpleGameListView.cpp b/src/views/gamelist/ISimpleGameListView.cpp new file mode 100644 index 000000000..f2913b20f --- /dev/null +++ b/src/views/gamelist/ISimpleGameListView.cpp @@ -0,0 +1,101 @@ +#include "ISimpleGameListView.h" +#include "../../ThemeData.h" +#include "../../Window.h" +#include "../ViewController.h" + +ISimpleGameListView::ISimpleGameListView(Window* window, FileData* root) : IGameListView(window, root), + mHeaderText(window), mHeaderImage(window), mBackground(window) +{ + mHeaderText.setText("Header"); + mHeaderText.setSize(mSize.x(), 0); + mHeaderText.setPosition(0, 0); + mHeaderText.setCentered(true); + + mHeaderImage.setResize(0, mSize.y() * 0.185f, false); + mHeaderImage.setOrigin(0.5f, 0.0f); + mHeaderImage.setPosition(mSize.x() / 2, 0); + + mBackground.setResize(mSize.x(), mSize.y(), true); + + addChild(&mHeaderText); + addChild(&mBackground); +} + +void ISimpleGameListView::onThemeChanged(const std::shared_ptr& theme) +{ + const ImageDef& bg = theme->getImage("backgroundImage"); + mBackground.setTiling(bg.tile); + mBackground.setImage(bg.getTexture()); + + const ImageDef& hdr = theme->getImage("headerImage"); + mHeaderImage.setTiling(hdr.tile); + mHeaderImage.setImage(hdr.getTexture()); + + if(mHeaderImage.hasImage()) + { + removeChild(&mHeaderText); + addChild(&mHeaderImage); + }else{ + addChild(&mHeaderText); + removeChild(&mHeaderImage); + } +} + +void ISimpleGameListView::onFileChanged(FileData* file, FileChangeType change) +{ + // we could be tricky here to be efficient; + // but this shouldn't happen very often so we'll just always repopulate + FileData* cursor = getCursor(); + populateList(cursor->getParent()->getChildren()); + setCursor(cursor); +} + +bool ISimpleGameListView::input(InputConfig* config, Input input) +{ + if(input.value != 0) + { + if(config->isMappedTo("a", input)) + { + FileData* cursor = getCursor(); + if(cursor->getType() == GAME) + { + launch(cursor); + }else{ + // it's a folder + if(cursor->getChildren().size() > 0) + { + mCursorStack.push(cursor); + populateList(cursor->getChildren()); + } + } + + return true; + }else if(config->isMappedTo("b", input)) + { + if(mCursorStack.size()) + { + populateList(mCursorStack.top()->getParent()->getChildren()); + setCursor(mCursorStack.top()); + mCursorStack.pop(); + getTheme()->playSound("backSound"); + }else{ + onFocusLost(); + mWindow->getViewController()->goToSystemSelect(); + } + + return true; + }else if(config->isMappedTo("right", input)) + { + onFocusLost(); + mWindow->getViewController()->goToNextGameList(); + return true; + }else if(config->isMappedTo("left", input)) + { + onFocusLost(); + mWindow->getViewController()->goToPrevGameList(); + return true; + } + } + + return IGameListView::input(config, input); +} diff --git a/src/views/gamelist/ISimpleGameListView.h b/src/views/gamelist/ISimpleGameListView.h new file mode 100644 index 000000000..41f269bd2 --- /dev/null +++ b/src/views/gamelist/ISimpleGameListView.h @@ -0,0 +1,36 @@ +#pragma once + +#include "IGameListView.h" + +#include "../../components/TextComponent.h" +#include "../../components/ImageComponent.h" + +class ISimpleGameListView : public IGameListView +{ +public: + ISimpleGameListView(Window* window, FileData* root); + virtual ~ISimpleGameListView() {} + + // Called when a new file is added, a file is removed, a file's metadata changes, or a file's children are sorted. + // NOTE: FILE_SORTED is only reported for the topmost FileData, where the sort started. + // Since sorts are recursive, that FileData's children probably changed too. + virtual void onFileChanged(FileData* file, FileChangeType change); + + // Called whenever the theme changes. + virtual void onThemeChanged(const std::shared_ptr& theme); + + virtual FileData* getCursor() = 0; + virtual void setCursor(FileData*) = 0; + + virtual bool input(InputConfig* config, Input input) override; + +protected: + virtual void populateList(const std::vector& files) = 0; + virtual void launch(FileData* game) = 0; + + TextComponent mHeaderText; + ImageComponent mHeaderImage; + ImageComponent mBackground; + + std::stack mCursorStack; +};