mirror of
https://github.com/libretro/ppsspp.git
synced 2024-11-27 18:30:56 +00:00
Merge pull request #4750 from unknownbrackets/ui-tweaks
Support pinning paths in the game browser
This commit is contained in:
commit
2871633e36
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -74,6 +74,7 @@ public:
|
||||
bool bAutoSaveSymbolMap;
|
||||
std::string sReportHost;
|
||||
std::vector<std::string> recentIsos;
|
||||
std::vector<std::string> vPinnedPaths;
|
||||
std::string sLanguageIni;
|
||||
|
||||
|
||||
|
@ -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" />
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
|
@ -202,6 +202,7 @@
|
||||
<ClCompile Include="WindowsHeadlessHostDx9.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\test.py" />
|
||||
<None Include="headless.txt" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -10,6 +10,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="headless.txt" />
|
||||
<None Include="..\test.py" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="StubHost.h" />
|
||||
|
Loading…
Reference in New Issue
Block a user