Merge pull request #4750 from unknownbrackets/ui-tweaks

Support pinning paths in the game browser
This commit is contained in:
Henrik Rydgård 2013-12-16 06:08:05 -08:00
commit 2871633e36
7 changed files with 152 additions and 16 deletions

View File

@ -131,6 +131,12 @@ void Config::Load(const char *iniFileName, const char *controllerIniFilename) {
}
}
auto pinnedPaths = iniFile.GetOrCreateSection("PinnedPaths")->ToMap();
vPinnedPaths.clear();
for (auto it = pinnedPaths.begin(), end = pinnedPaths.end(); it != end; ++it) {
vPinnedPaths.push_back(it->second);
}
IniFile::Section *cpu = iniFile.GetOrCreateSection("CPU");
#ifdef IOS
cpu->Get("Jit", &bJit, iosCanUseJit);
@ -464,6 +470,14 @@ void Config::Save() {
}
}
IniFile::Section *pinnedPaths = iniFile.GetOrCreateSection("PinnedPaths");
pinnedPaths->Clear();
for (size_t i = 0; i < vPinnedPaths.size(); ++i) {
char keyName[64];
snprintf(keyName, sizeof(keyName), "Path%d", i);
pinnedPaths->Set(keyName, vPinnedPaths[i]);
}
IniFile::Section *cpu = iniFile.GetOrCreateSection("CPU");
cpu->Set("Jit", bJit);
cpu->Set("SeparateCPUThread", bSeparateCPUThread);
@ -804,4 +818,4 @@ void Config::ResetControlLayout() {
g_Config.fAnalogStickX = -1.0;
g_Config.fAnalogStickY = -1.0;
g_Config.fAnalogStickScale = defaultScale;
}
}

View File

@ -74,6 +74,7 @@ public:
bool bAutoSaveSymbolMap;
std::string sReportHost;
std::vector<std::string> recentIsos;
std::vector<std::string> vPinnedPaths;
std::string sLanguageIni;

View File

@ -296,8 +296,12 @@
<ClCompile Include="Common\TextureDecoderNEON.cpp">
<Filter>Common</Filter>
</ClCompile>
<ClCompile Include="GLES\VertexDecoderX86.cpp" />
<ClCompile Include="GLES\VertexDecoderArm.cpp" />
<ClCompile Include="GLES\VertexDecoderArm.cpp">
<Filter>GLES</Filter>
</ClCompile>
<ClCompile Include="GLES\VertexDecoderX86.cpp">
<Filter>GLES</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="CMakeLists.txt" />

View File

@ -54,11 +54,13 @@ void GameScreen::CreateViews() {
leftColumn->Add(new Choice(d->T("Back"), "", false, new AnchorLayoutParams(150, WRAP_CONTENT, 10, NONE, NONE, 10)))->OnClick.Handle(this, &GameScreen::OnSwitchBack);
if (info) {
texvGameIcon_ = leftColumn->Add(new TextureView(0, IS_DEFAULT, new AnchorLayoutParams(144 * 2, 80 * 2, 10, 10, NONE, NONE)));
tvTitle_ = leftColumn->Add(new TextView(info->title, ALIGN_LEFT, 1.0f, new AnchorLayoutParams(10, 200, NONE, NONE)));
tvGameSize_ = leftColumn->Add(new TextView("...", ALIGN_LEFT, 1.0f, new AnchorLayoutParams(10, 250, NONE, NONE)));
tvSaveDataSize_ = leftColumn->Add(new TextView("...", ALIGN_LEFT, 1.0f, new AnchorLayoutParams(10, 290, NONE, NONE)));
tvInstallDataSize_ = leftColumn->Add(new TextView("", ALIGN_LEFT, 1.0f, new AnchorLayoutParams(10, 330, NONE, NONE)));
tvRegion_ = leftColumn->Add(new TextView("", ALIGN_LEFT, 1.0f, new AnchorLayoutParams(10, 370, NONE, NONE)));
tvTitle_ = leftColumn->Add(new TextView(info->title, ALIGN_LEFT, false, new AnchorLayoutParams(10, 200, NONE, NONE)));
// This one doesn't need to be updated.
leftColumn->Add(new TextView(gamePath_, ALIGN_LEFT, true, new AnchorLayoutParams(10, 250, NONE, NONE)));
tvGameSize_ = leftColumn->Add(new TextView("...", ALIGN_LEFT, true, new AnchorLayoutParams(10, 290, NONE, NONE)));
tvSaveDataSize_ = leftColumn->Add(new TextView("...", ALIGN_LEFT, true, new AnchorLayoutParams(10, 320, NONE, NONE)));
tvInstallDataSize_ = leftColumn->Add(new TextView("", ALIGN_LEFT, true, new AnchorLayoutParams(10, 350, NONE, NONE)));
tvRegion_ = leftColumn->Add(new TextView("", ALIGN_LEFT, true, new AnchorLayoutParams(10, 380, NONE, NONE)));
}
ViewGroup *rightColumn = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(300, FILL_PARENT, actionMenuMargins));

View File

@ -263,10 +263,25 @@ enum GameBrowserFlags {
class DirButton : public UI::Button {
public:
DirButton(std::string path, UI::LayoutParams *layoutParams)
: UI::Button(path, layoutParams) {}
DirButton(const std::string &path, UI::LayoutParams *layoutParams)
: UI::Button(path, layoutParams), path_(path), absolute_(false) {}
DirButton(const std::string &path, const std::string &text, LayoutParams *layoutParams = 0)
: UI::Button(text, layoutParams), path_(path), absolute_(true) {}
virtual void Draw(UIContext &dc);
const std::string GetPath() const {
return path_;
}
bool PathAbsolute() const {
return absolute_;
}
private:
std::string path_;
bool absolute_;
};
void DirButton::Draw(UIContext &dc) {
@ -325,6 +340,9 @@ public:
UI::Choice *HomebrewStoreButton() { return homebrewStoreButton_; }
private:
void Refresh();
bool IsCurrentPathPinned();
const std::vector<std::string> GetPinnedPaths();
const std::string GetBaseName(const std::string &path);
UI::EventReturn GameButtonClick(UI::EventParams &e);
UI::EventReturn GameButtonHoldClick(UI::EventParams &e);
@ -332,6 +350,7 @@ private:
UI::EventReturn LayoutChange(UI::EventParams &e);
UI::EventReturn LastClick(UI::EventParams &e);
UI::EventReturn HomeClick(UI::EventParams &e);
UI::EventReturn PinToggleClick(UI::EventParams &e);
UI::ViewGroup *gameList_;
PathBrowser path_;
@ -387,6 +406,17 @@ UI::EventReturn GameBrowser::HomeClick(UI::EventParams &e) {
return UI::EVENT_DONE;
}
UI::EventReturn GameBrowser::PinToggleClick(UI::EventParams &e) {
auto &pinnedPaths = g_Config.vPinnedPaths;
if (IsCurrentPathPinned()) {
pinnedPaths.erase(std::remove(pinnedPaths.begin(), pinnedPaths.end(), path_.GetPath()), pinnedPaths.end());
} else {
pinnedPaths.push_back(path_.GetPath());
}
Refresh();
return UI::EVENT_DONE;
}
void GameBrowser::Refresh() {
using namespace UI;
@ -431,7 +461,7 @@ void GameBrowser::Refresh() {
Add(gameList_);
// Find games in the current directory and create new ones.
std::vector<UI::Button *> dirButtons;
std::vector<DirButton *> dirButtons;
std::vector<GameButton *> gameButtons;
if (path_.GetPath() == "!RECENT") {
@ -442,8 +472,14 @@ void GameBrowser::Refresh() {
std::vector<FileInfo> fileInfo;
path_.GetListing(fileInfo, "iso:cso:pbp:elf:prx:");
for (size_t i = 0; i < fileInfo.size(); i++) {
if (fileInfo[i].isDirectory && (path_.GetPath().size() < 4 || !File::Exists(path_.GetPath() + fileInfo[i].name + "/EBOOT.PBP"))) {
// Check if eboot directory
bool isGame = !fileInfo[i].isDirectory;
// Check if eboot directory
if (!isGame && path_.GetPath().size() >= 4 && File::Exists(path_.GetPath() + fileInfo[i].name + "/EBOOT.PBP"))
isGame = true;
else if (!isGame && File::Exists(path_.GetPath() + fileInfo[i].name + "/PSP_GAME/SYSDIR"))
isGame = true;
if (!isGame) {
if (allowBrowsing_) {
dirButtons.push_back(new DirButton(fileInfo[i].name, new UI::LinearLayoutParams(UI::FILL_PARENT, UI::FILL_PARENT)));
}
@ -475,6 +511,13 @@ void GameBrowser::Refresh() {
if (allowBrowsing_) {
gameList_->Add(new DirButton("..", new UI::LinearLayoutParams(UI::FILL_PARENT, UI::FILL_PARENT)))->
OnClick.Handle(this, &GameBrowser::NavigateClick);
// Add any pinned paths before other directories.
auto pinnedPaths = GetPinnedPaths();
for (auto it = pinnedPaths.begin(), end = pinnedPaths.end(); it != end; ++it) {
gameList_->Add(new DirButton(*it, GetBaseName(*it), new UI::LinearLayoutParams(UI::FILL_PARENT, UI::FILL_PARENT)))->
OnClick.Handle(this, &GameBrowser::NavigateClick);
}
}
for (size_t i = 0; i < dirButtons.size(); i++) {
@ -487,6 +530,16 @@ void GameBrowser::Refresh() {
b->OnHoldClick.Handle(this, &GameBrowser::GameButtonHoldClick);
}
// Show a button to toggle pinning at the very end.
if (allowBrowsing_) {
std::string caption = IsCurrentPathPinned() ? "-" : "+";
if (!*gridStyle_) {
caption = IsCurrentPathPinned() ? m->T("UnpinPath", "Unpin") : m->T("PinPath", "Pin");
}
gameList_->Add(new UI::Button(caption, new UI::LinearLayoutParams(UI::FILL_PARENT, UI::FILL_PARENT)))->
OnClick.Handle(this, &GameBrowser::PinToggleClick);
}
if (g_Config.bHomebrewStore && (flags_ & FLAG_HOMEBREWSTOREBUTTON)) {
Add(new Spacer());
homebrewStoreButton_ = Add(new Choice(m->T("DownloadFromStore", "Download from the PPSSPP Homebrew Store"), new UI::LinearLayoutParams(UI::WRAP_CONTENT, UI::WRAP_CONTENT)));
@ -500,6 +553,62 @@ void GameBrowser::Refresh() {
}
}
bool GameBrowser::IsCurrentPathPinned() {
const auto paths = g_Config.vPinnedPaths;
return std::find(paths.begin(), paths.end(), path_.GetPath()) != paths.end();
}
const std::vector<std::string> GameBrowser::GetPinnedPaths() {
#ifdef _WIN32
static const std::string sepChars = "/";
#else
static const std::string sepChars = "/\\";
#endif
const std::string currentPath = path_.GetPath();
const std::vector<std::string> paths = g_Config.vPinnedPaths;
std::vector<std::string> results;
for (size_t i = 0; i < paths.size(); ++i) {
// We want to exclude the current path, and its direct children.
if (paths[i] == currentPath) {
continue;
}
if (startsWith(paths[i], currentPath)) {
std::string descendant = paths[i].substr(currentPath.size());
// If there's only one separator (or none), its a direct child.
if (descendant.find_last_of(sepChars) == descendant.find_first_of(sepChars)) {
continue;
}
}
results.push_back(paths[i]);
}
return results;
}
const std::string GameBrowser::GetBaseName(const std::string &path) {
#ifdef _WIN32
static const std::string sepChars = "/";
#else
static const std::string sepChars = "/\\";
#endif
auto trailing = path.find_last_not_of(sepChars);
if (trailing != path.npos) {
size_t start = path.find_last_of(sepChars, trailing);
if (start != path.npos) {
return path.substr(start + 1, trailing - start);
}
return path.substr(0, trailing);
}
size_t start = path.find_last_of(sepChars);
if (start != path.npos) {
return path.substr(start + 1);
}
return path;
}
UI::EventReturn GameBrowser::GameButtonClick(UI::EventParams &e) {
GameButton *button = static_cast<GameButton *>(e.v);
UI::EventParams e2;
@ -519,9 +628,13 @@ UI::EventReturn GameBrowser::GameButtonHoldClick(UI::EventParams &e) {
}
UI::EventReturn GameBrowser::NavigateClick(UI::EventParams &e) {
UI::Button *button = static_cast<UI::Button *>(e.v);
std::string text = button->GetText();
path_.Navigate(text);
DirButton *button = static_cast<DirButton *>(e.v);
std::string text = button->GetPath();
if (button->PathAbsolute()) {
path_.SetPath(text);
} else {
path_.Navigate(text);
}
g_Config.currentDirectory = path_.GetPath();
Refresh();
return UI::EVENT_DONE;

View File

@ -202,6 +202,7 @@
<ClCompile Include="WindowsHeadlessHostDx9.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="..\test.py" />
<None Include="headless.txt" />
</ItemGroup>
<ItemGroup>

View File

@ -10,6 +10,7 @@
</ItemGroup>
<ItemGroup>
<None Include="headless.txt" />
<None Include="..\test.py" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="StubHost.h" />