mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2024-11-28 17:15:38 +00:00
Mostly reimplemented the fast select GUI.
Still not sure how I want to store sort state. I'd kind of like to move sorting out of the FileData tree altogether and into the display classes.
This commit is contained in:
parent
94ca712759
commit
0cfa38fcf9
|
@ -174,6 +174,7 @@ set(ES_HEADERS
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/TextEditComponent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/TextListComponent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiDetectDevice.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiFastSelect.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiMetaDataEd.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiMsgBoxOk.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiMsgBoxYesNo.h
|
||||
|
@ -239,6 +240,7 @@ set(ES_SOURCES
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/TextComponent.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/TextEditComponent.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiDetectDevice.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiFastSelect.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiMetaDataEd.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiMsgBoxOk.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiMsgBoxYesNo.cpp
|
||||
|
|
|
@ -65,6 +65,7 @@ Fonts are defined like so:
|
|||
|
||||
`<listFont>` - Default size: 0.045.
|
||||
`<descriptionFont>` - Default size: 0.035.
|
||||
`<fastSelectLetterFont>` - Default size: 0.15.
|
||||
|
||||
Colors
|
||||
======
|
||||
|
@ -81,6 +82,8 @@ or
|
|||
`<listSelectorColor>` - Default: 000000FF.
|
||||
`<listSelectedColor>` - Default: 00000000.
|
||||
`<descriptionColor>` - Default: 48474DFF.
|
||||
`<fastSelectLetterColor>` - Default: FFFFFFFF.
|
||||
`<fastSelectTextColor>` - Default: DDDDDDFF.
|
||||
|
||||
Images
|
||||
======
|
||||
|
@ -98,6 +101,7 @@ Pretty much any image format is supported.
|
|||
`<backgroundImage>` - No default.
|
||||
`<headerImage>` - No default.
|
||||
`<infoBackgroundImage>` - No default.
|
||||
`<verticalDividerImage>` - No default.
|
||||
|
||||
Sounds
|
||||
======
|
||||
|
|
|
@ -17,7 +17,8 @@ enum FileChangeType
|
|||
{
|
||||
FILE_ADDED,
|
||||
FILE_METADATA_CHANGED,
|
||||
FILE_REMOVED
|
||||
FILE_REMOVED,
|
||||
FILE_SORTED
|
||||
};
|
||||
|
||||
// Used for loading/saving gamelist.xml.
|
||||
|
|
|
@ -11,14 +11,17 @@
|
|||
// Defaults
|
||||
std::map<std::string, FontDef > ThemeData::sDefaultFonts = boost::assign::map_list_of
|
||||
("listFont", FontDef(0.045f, ""))
|
||||
("descriptionFont", FontDef(0.035f, ""));
|
||||
("descriptionFont", FontDef(0.035f, ""))
|
||||
("fastSelectLetterFont", FontDef(0.15f, ""));
|
||||
|
||||
std::map<std::string, unsigned int> ThemeData::sDefaultColors = boost::assign::map_list_of
|
||||
("listPrimaryColor", 0x0000FFFF)
|
||||
("listSecondaryColor", 0x00FF00FF)
|
||||
("listSelectorColor", 0x000000FF)
|
||||
("listSelectedColor", 0x00000000)
|
||||
("descriptionColor", 0x48474DFF);
|
||||
("descriptionColor", 0x48474DFF)
|
||||
("fastSelectLetterColor", 0xFFFFFFFF)
|
||||
("fastSelectTextColor", 0xDDDDDDFF);
|
||||
|
||||
std::map<std::string, ImageDef> ThemeData::sDefaultImages = boost::assign::map_list_of
|
||||
("backgroundImage", ImageDef("", true))
|
||||
|
|
159
src/components/GuiFastSelect.cpp
Normal file
159
src/components/GuiFastSelect.cpp
Normal file
|
@ -0,0 +1,159 @@
|
|||
#include "GuiFastSelect.h"
|
||||
#include "../ThemeData.h"
|
||||
#include "../FileSorts.h"
|
||||
#include "../SystemData.h"
|
||||
|
||||
static const std::string LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
|
||||
GuiFastSelect::GuiFastSelect(Window* window, GameListView* gamelist) : GuiComponent(window),
|
||||
mBackground(window, ":/button.png"), mSortText(window), mLetterText(window), mGameList(gamelist)
|
||||
{
|
||||
setPosition(Renderer::getScreenWidth() * 0.2f, Renderer::getScreenHeight() * 0.2f);
|
||||
setSize(Renderer::getScreenWidth() * 0.6f, Renderer::getScreenHeight() * 0.6f);
|
||||
|
||||
const std::shared_ptr<ThemeData>& theme = mGameList->getTheme();
|
||||
|
||||
mBackground.fitTo(mSize);
|
||||
addChild(&mBackground);
|
||||
|
||||
mLetterText.setSize(mSize.x(), mSize.y() * 0.75f);
|
||||
mLetterText.setCentered(true);
|
||||
mLetterText.setFromTheme(theme, "fastSelectLetterFont", "fastSelectLetterColor");
|
||||
addChild(&mLetterText);
|
||||
|
||||
mSortText.setPosition(0, mSize.y() * 0.75f);
|
||||
mSortText.setSize(mSize.x(), mSize.y() * 0.25f);
|
||||
mSortText.setCentered(true);
|
||||
mSortText.setFromTheme(theme, "descriptionFont", "fastSelectTextColor");
|
||||
addChild(&mSortText);
|
||||
|
||||
mSortId = 0; // TODO
|
||||
updateSortText();
|
||||
|
||||
mLetterId = LETTERS.find(mGameList->getCursor()->getName()[0]);
|
||||
if(mLetterId == std::string::npos)
|
||||
mLetterId = 0;
|
||||
|
||||
mScrollDir = 0;
|
||||
mScrollAccumulator = 0;
|
||||
scroll(); // initialize the letter value
|
||||
}
|
||||
|
||||
bool GuiFastSelect::input(InputConfig* config, Input input)
|
||||
{
|
||||
if(input.value == 0 && config->isMappedTo("select", input))
|
||||
{
|
||||
// the user let go of select; make our changes to the gamelist and close this gui
|
||||
updateGameListSort();
|
||||
updateGameListCursor();
|
||||
delete this;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(config->isMappedTo("up", input))
|
||||
{
|
||||
if(input.value != 0)
|
||||
setScrollDir(-1);
|
||||
else
|
||||
setScrollDir(0);
|
||||
|
||||
return true;
|
||||
}else if(config->isMappedTo("down", input))
|
||||
{
|
||||
if(input.value != 0)
|
||||
setScrollDir(1);
|
||||
else
|
||||
setScrollDir(0);
|
||||
|
||||
return true;
|
||||
}else if(config->isMappedTo("left", input) && input.value != 0)
|
||||
{
|
||||
mSortId = (mSortId + 1) % FileSorts::SortTypes.size();
|
||||
updateSortText();
|
||||
return true;
|
||||
}else if(config->isMappedTo("right", input) && input.value != 0)
|
||||
{
|
||||
mSortId--;
|
||||
if(mSortId < 0)
|
||||
mSortId += FileSorts::SortTypes.size();
|
||||
|
||||
updateSortText();
|
||||
return true;
|
||||
}
|
||||
|
||||
return GuiComponent::input(config, input);
|
||||
}
|
||||
|
||||
void GuiFastSelect::setScrollDir(int dir)
|
||||
{
|
||||
mScrollDir = dir;
|
||||
scroll();
|
||||
mScrollAccumulator = -500;
|
||||
}
|
||||
|
||||
void GuiFastSelect::update(int deltaTime)
|
||||
{
|
||||
if(mScrollDir != 0)
|
||||
{
|
||||
mScrollAccumulator += deltaTime;
|
||||
while(mScrollAccumulator >= 150)
|
||||
{
|
||||
scroll();
|
||||
mScrollAccumulator -= 150;
|
||||
}
|
||||
}
|
||||
|
||||
GuiComponent::update(deltaTime);
|
||||
}
|
||||
|
||||
void GuiFastSelect::scroll()
|
||||
{
|
||||
mLetterId += mScrollDir;
|
||||
if(mLetterId < 0)
|
||||
mLetterId += LETTERS.length();
|
||||
else if(mLetterId >= (int)LETTERS.length())
|
||||
mLetterId -= LETTERS.length();
|
||||
|
||||
mLetterText.setText(LETTERS.substr(mLetterId, 1));
|
||||
}
|
||||
|
||||
void GuiFastSelect::updateSortText()
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "<- " << FileSorts::SortTypes.at(mSortId).description << " ->";
|
||||
mSortText.setText(ss.str());
|
||||
}
|
||||
|
||||
void GuiFastSelect::updateGameListSort()
|
||||
{
|
||||
const FileData::SortType& sort = FileSorts::SortTypes.at(mSortId);
|
||||
|
||||
FileData* root = mGameList->getCursor()->getSystem()->getRootFolder();
|
||||
root->sort(sort); // will also recursively sort children
|
||||
|
||||
// notify that the root folder was sorted
|
||||
mGameList->onFileChanged(root, FILE_SORTED);
|
||||
}
|
||||
|
||||
void GuiFastSelect::updateGameListCursor()
|
||||
{
|
||||
const std::vector<FileData*>& list = mGameList->getCursor()->getParent()->getChildren();
|
||||
|
||||
// only skip by letter when the sort mode is alphabetical
|
||||
const FileData::SortType& sort = FileSorts::SortTypes.at(mSortId);
|
||||
if(sort.comparisonFunction != &FileSorts::compareFileName)
|
||||
return;
|
||||
|
||||
// find the first entry in the list that either exactly matches our target letter or is beyond our target letter
|
||||
for(auto it = list.cbegin(); it != list.cend(); it++)
|
||||
{
|
||||
char check = (*it)->getName().empty() ? 'A' : (*it)->getName()[0];
|
||||
|
||||
// if there's an exact match or we've passed it, set the cursor to this one
|
||||
if(check == LETTERS[mLetterId] || (sort.ascending && check > LETTERS[mLetterId]) || (!sort.ascending && check < LETTERS[mLetterId]))
|
||||
{
|
||||
mGameList->setCursor(*it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
35
src/components/GuiFastSelect.h
Normal file
35
src/components/GuiFastSelect.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
#pragma once
|
||||
|
||||
#include "../GuiComponent.h"
|
||||
#include "../views/GameListView.h"
|
||||
|
||||
#include "NinePatchComponent.h"
|
||||
#include "TextComponent.h"
|
||||
|
||||
class GuiFastSelect : public GuiComponent
|
||||
{
|
||||
public:
|
||||
GuiFastSelect(Window* window, GameListView* gamelist);
|
||||
|
||||
bool input(InputConfig* config, Input input);
|
||||
void update(int deltaTime);
|
||||
|
||||
private:
|
||||
void setScrollDir(int dir);
|
||||
void scroll();
|
||||
void updateGameListCursor();
|
||||
void updateGameListSort();
|
||||
void updateSortText();
|
||||
|
||||
int mSortId;
|
||||
int mLetterId;
|
||||
|
||||
int mScrollDir;
|
||||
int mScrollAccumulator;
|
||||
|
||||
NinePatchComponent mBackground;
|
||||
TextComponent mSortText;
|
||||
TextComponent mLetterText;
|
||||
|
||||
GameListView* mGameList;
|
||||
};
|
|
@ -2,6 +2,7 @@
|
|||
#include "../Renderer.h"
|
||||
#include "../Log.h"
|
||||
#include "../Window.h"
|
||||
#include "../ThemeData.h"
|
||||
|
||||
TextComponent::TextComponent(Window* window) : GuiComponent(window),
|
||||
mFont(NULL), mColor(0x000000FF), mAutoCalcExtent(true, true), mCentered(false)
|
||||
|
@ -137,3 +138,9 @@ std::string TextComponent::getValue() const
|
|||
{
|
||||
return mText;
|
||||
}
|
||||
|
||||
void TextComponent::setFromTheme(const std::shared_ptr<ThemeData>& theme, const std::string& fontIdentifier, const std::string& colorIdentifier)
|
||||
{
|
||||
setFont(theme->getFont(fontIdentifier));
|
||||
setColor(theme->getColor(colorIdentifier));
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
#include "../GuiComponent.h"
|
||||
#include "../resources/Font.h"
|
||||
|
||||
class ThemeData;
|
||||
|
||||
class TextComponent : public GuiComponent
|
||||
{
|
||||
public:
|
||||
|
@ -26,6 +28,8 @@ public:
|
|||
|
||||
std::shared_ptr<Font> getFont() const;
|
||||
|
||||
void setFromTheme(const std::shared_ptr<ThemeData>& theme, const std::string& fontIdentifier, const std::string& colorIdentifier);
|
||||
|
||||
private:
|
||||
void calculateExtent();
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ BasicGameListView::BasicGameListView(Window* window, FileData* root)
|
|||
addChild(&mHeaderText);
|
||||
}
|
||||
|
||||
void BasicGameListView::setTheme(const std::shared_ptr<ThemeData>& theme)
|
||||
void BasicGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& theme)
|
||||
{
|
||||
const ImageDef& bg = theme->getImage("backgroundImage");
|
||||
mBackground.setTiling(bg.tile);
|
||||
|
@ -69,6 +69,14 @@ void BasicGameListView::onFileChanged(FileData* file, FileChangeType change)
|
|||
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)
|
||||
|
|
|
@ -15,7 +15,7 @@ public:
|
|||
|
||||
virtual bool input(InputConfig* config, Input input) override;
|
||||
|
||||
virtual void setTheme(const std::shared_ptr<ThemeData>& theme) override;
|
||||
virtual void onThemeChanged(const std::shared_ptr<ThemeData>& theme) override;
|
||||
|
||||
inline FileData* getCursor() { return mList.getSelected(); }
|
||||
virtual void setCursor(FileData* file) override;
|
||||
|
|
|
@ -39,10 +39,10 @@ DetailedGameListView::DetailedGameListView(Window* window, FileData* root) :
|
|||
updateInfoPanel();
|
||||
}
|
||||
|
||||
void DetailedGameListView::setTheme(const std::shared_ptr<ThemeData>& theme)
|
||||
void DetailedGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& theme)
|
||||
{
|
||||
mHeaderImage.setResize(mSize.x() * 0.5f, 0, true);
|
||||
BasicGameListView::setTheme(theme);
|
||||
BasicGameListView::onThemeChanged(theme);
|
||||
|
||||
if(mHeaderImage.getPosition().y() + mHeaderImage.getSize().y() > mImage.getPosition().y())
|
||||
mHeaderImage.setResize(0, mSize.y() * 0.185f, true);
|
||||
|
|
|
@ -10,7 +10,7 @@ class DetailedGameListView : public BasicGameListView
|
|||
public:
|
||||
DetailedGameListView(Window* window, FileData* root);
|
||||
|
||||
virtual void setTheme(const std::shared_ptr<ThemeData>& theme) override;
|
||||
virtual void onThemeChanged(const std::shared_ptr<ThemeData>& theme) override;
|
||||
|
||||
virtual void onFileChanged(FileData* file, FileChangeType change);
|
||||
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
#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)
|
||||
{
|
||||
|
@ -24,7 +26,17 @@ bool GameListView::input(InputConfig* config, Input input)
|
|||
{
|
||||
// open menu
|
||||
mWindow->pushGui(new GuiMenu(mWindow));
|
||||
}else if(config->isMappedTo("select", input) && input.value != 0)
|
||||
{
|
||||
// open fast select
|
||||
mWindow->pushGui(new GuiFastSelect(mWindow, this));
|
||||
}
|
||||
|
||||
return GuiComponent::input(config, input);
|
||||
}
|
||||
|
||||
void GameListView::setTheme(const std::shared_ptr<ThemeData>& theme)
|
||||
{
|
||||
mTheme = theme;
|
||||
onThemeChanged(theme);
|
||||
}
|
||||
|
|
|
@ -20,17 +20,23 @@ public:
|
|||
|
||||
virtual ~GameListView() {}
|
||||
|
||||
// Called when a new file is added, a file is removed, or a file's metadata changes.
|
||||
// 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) = 0;
|
||||
|
||||
// Called to set or update theme.
|
||||
virtual void setTheme(const std::shared_ptr<ThemeData>& theme) = 0;
|
||||
// Called whenever the theme changes.
|
||||
virtual void onThemeChanged(const std::shared_ptr<ThemeData>& theme) = 0;
|
||||
|
||||
virtual bool input(InputConfig* config, Input input) override;
|
||||
void setTheme(const std::shared_ptr<ThemeData>& theme);
|
||||
inline const std::shared_ptr<ThemeData>& getTheme() const { return mTheme; }
|
||||
|
||||
virtual FileData* getCursor() = 0;
|
||||
virtual void setCursor(FileData*) = 0;
|
||||
|
||||
virtual bool input(InputConfig* config, Input input) override;
|
||||
|
||||
protected:
|
||||
FileData* mRoot;
|
||||
std::shared_ptr<ThemeData> mTheme;
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue