mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-31 19:47:06 +01:00
598 lines
21 KiB
C++
598 lines
21 KiB
C++
// Copyright (c) 2012- PPSSPP Project.
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, version 2.0 or later versions.
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License 2.0 for more details.
|
|
|
|
// A copy of the GPL 2.0 should have been included with the program.
|
|
// If not, see http://www.gnu.org/licenses/
|
|
|
|
// Official git repository and contact information can be found at
|
|
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
|
|
|
#include "Common/Data/Text/I18n.h"
|
|
#include "Common/Input/KeyCodes.h"
|
|
#include "Common/StringUtils.h"
|
|
#include "Common/System/Request.h"
|
|
#include "Common/UI/Context.h"
|
|
#include "Common/UI/PopupScreens.h"
|
|
#include "Common/UI/Root.h"
|
|
#include "Common/UI/ScreenManager.h"
|
|
#include "Common/UI/ScrollView.h"
|
|
#include "Common/UI/TabHolder.h"
|
|
#include "Common/UI/ViewGroup.h"
|
|
#include "Core/Config.h"
|
|
#include "UI/MiscViews.h"
|
|
#include "UI/SettingsPage.h"
|
|
#include "UI/SettingsPagesDialogScreen.h"
|
|
|
|
// Pages can link to each other, so search doesn't follow links forever.
|
|
static const int MAX_PAGE_DEPTH = 8;
|
|
|
|
void SettingsPagesDialogScreen::AddSettingsTab(const char *tag, std::string_view title, ImageID imageId, I18NCat cat, std::function<void(SettingsPage *)> build, const char *helpTag) {
|
|
int index = (int)settingsTabs_.size();
|
|
if (stacked_) {
|
|
settingsTabs_.push_back(SettingsTab{ index, tag, std::string(title), cat, std::move(build), helpTag ? helpTag : "", imageId });
|
|
return;
|
|
}
|
|
settingsTabs_.push_back(SettingsTab{ (int)tabHolder_->GetTabContentViews().size(), tag, std::string(title), cat, std::move(build), helpTag ? helpTag : "", imageId });
|
|
tabHolder_->AddTabDeferred(title, imageId, [this, index]() -> UI::ViewGroup * {
|
|
using namespace UI;
|
|
LinearLayout *contents = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT));
|
|
contents->SetSpacing(0);
|
|
// Created on demand, outside CreateViews (see AddTab).
|
|
bool wasCreatingViews = IsCreatingViews();
|
|
SetCreatingViews(true);
|
|
CreateSettingsTabContents(index, contents);
|
|
SetCreatingViews(wasCreatingViews);
|
|
return contents;
|
|
});
|
|
}
|
|
|
|
void SettingsPagesDialogScreen::OpenPage(const char *tabTag, const char *linkKey) {
|
|
openTabTag_ = tabTag;
|
|
openLinkKey_ = linkKey;
|
|
}
|
|
|
|
std::unique_ptr<SettingsPage> SettingsPagesDialogScreen::BuildPage(const SettingsTab &tab, std::vector<int> *path, std::vector<std::string> *titles, std::string *helpTag) {
|
|
std::unique_ptr<SettingsPage> page = std::make_unique<SettingsPage>(tab.cat, this);
|
|
tab.build(page.get());
|
|
titles->push_back(tab.title);
|
|
*helpTag = tab.helpTag;
|
|
for (size_t i = 0; i < path->size(); i++) {
|
|
int index = (*path)[i];
|
|
if (index < 0 || index >= (int)page->Items().size() || page->Items()[index].type != SettingType::Link || i >= MAX_PAGE_DEPTH) {
|
|
path->resize(i);
|
|
break;
|
|
}
|
|
const SettingItem &link = page->Items()[index];
|
|
titles->push_back(std::string(link.text.Translate(page->DefaultCat())));
|
|
if (link.helpTag) {
|
|
*helpTag = link.helpTag;
|
|
}
|
|
std::unique_ptr<SettingsPage> next = std::make_unique<SettingsPage>(link.linkCat, page->Host());
|
|
link.linkBuild(next.get());
|
|
page = std::move(next);
|
|
}
|
|
return page;
|
|
}
|
|
|
|
void SettingsPagesDialogScreen::CreateSettingsTabContents(int settingsTab, UI::LinearLayout *parent) {
|
|
using namespace UI;
|
|
SettingsTab &tab = settingsTabs_[settingsTab];
|
|
std::vector<int> &path = pagePaths_[tab.tag];
|
|
std::vector<std::string> titles;
|
|
std::string helpTag;
|
|
std::unique_ptr<SettingsPage> page = BuildPage(tab, &path, &titles, &helpTag);
|
|
if (settingsTab == stackRootTab_ && !path.empty()) {
|
|
// What tabs show when rotating to them.
|
|
if (currentTabSetting_) {
|
|
*currentTabSetting_ = path[0];
|
|
}
|
|
pagePaths_[settingsTabs_[path[0]].tag].assign(path.begin() + 1, path.end());
|
|
}
|
|
|
|
if (!searchText_.empty()) {
|
|
auto ms = GetI18NCategory(I18NCat::MAINSETTINGS);
|
|
titles = { std::string(ms->T("Search")) + ": " + searchText_ };
|
|
}
|
|
CreateNavBar(parent, settingsTab, titles, helpTag);
|
|
|
|
std::string pageKey = tab.tag;
|
|
for (int index : path) {
|
|
pageKey += "/" + std::to_string(index);
|
|
}
|
|
ScrollView *scroll = parent->Add(new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT, 1.0f)));
|
|
// Search results get their own tag, so their scroll position isn't restored onto the page when leaving them.
|
|
scroll->SetTag(searchText_.empty() ? pageKey : pageKey + "/search");
|
|
LinearLayout *contents = scroll->Add(new ChoiceGroupList());
|
|
|
|
tab.views.clear();
|
|
if (!searchText_.empty()) {
|
|
CreateSearchResults(contents);
|
|
return;
|
|
}
|
|
|
|
// Each page remembers where it was scrolled to, for coming back to it.
|
|
scroll->RememberPosition(&scrollPositions_[pageKey]);
|
|
CreateSettingViews(*page, contents, screenManager(), &tab.views);
|
|
|
|
const std::deque<SettingItem> &items = page->Items();
|
|
for (int i = 0; i < (int)items.size() && i < (int)tab.views.size(); i++) {
|
|
if (items[i].type == SettingType::Link && tab.views[i]) {
|
|
static_cast<Choice *>(tab.views[i])->OnClick.Add([this, settingsTab, i](UI::EventParams &) {
|
|
std::vector<int> path = pagePaths_[settingsTabs_[settingsTab].tag];
|
|
path.push_back(i);
|
|
ShowPage(settingsTab, path, 0);
|
|
});
|
|
}
|
|
}
|
|
|
|
if (pendingTab_ == settingsTab && pendingItem_ >= 0) {
|
|
for (int i = pendingItem_; i < (int)tab.views.size(); i++) {
|
|
if (tab.views[i] && tab.views[i]->CanBeFocused()) {
|
|
pendingFocusView_ = tab.views[i];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (pendingTab_ == settingsTab) {
|
|
pendingTab_ = -1;
|
|
pendingItem_ = -1;
|
|
}
|
|
}
|
|
|
|
// Where you are, with the earlier pages clickable to go back to them. Also has the search button.
|
|
void SettingsPagesDialogScreen::CreateNavBar(UI::ViewGroup *parent, int settingsTab, const std::vector<std::string> &titles, const std::string &helpTag) {
|
|
using namespace UI;
|
|
const SettingsTab &tab = settingsTabs_[settingsTab];
|
|
const Margins barPadding(12, 0, 8, 0);
|
|
LinearLayout *bar = parent->Add(new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, ITEM_HEIGHT, Margins(0, 8))));
|
|
bar->SetPadding(barPadding);
|
|
bar->SetSpacing(8.0f);
|
|
|
|
// When stacked in portrait, there's no other back button, so it's always there, and leaves the screen from the root page.
|
|
if (stacked_ || !searchText_.empty() || !pagePaths_[tab.tag].empty()) {
|
|
Choice *backButton = bar->Add(new Choice(ImageID("I_CHEVRON_LEFT"), new LinearLayoutParams(ITEM_HEIGHT, ITEM_HEIGHT)));
|
|
backButton->SetRoundCorners(RoundCorners::All);
|
|
backButton->OnClick.Add([this, settingsTab](UI::EventParams &) {
|
|
if (!GoBack(settingsTab)) {
|
|
TriggerFinish(DR_BACK);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Portrait has no room for the whole path, and the back button goes up it.
|
|
int firstShownTitle = stacked_ ? (int)titles.size() - 1 : 0;
|
|
for (int i = firstShownTitle; i < (int)titles.size(); i++) {
|
|
if (i > firstShownTitle) {
|
|
ImageView *separator = bar->Add(new ImageView(ImageID("I_CHEVRON_RIGHT"), "", new LinearLayoutParams(0.0f, Gravity::G_VCENTER, Margins(4, 0, 4, 0))));
|
|
separator->SetScale(0.66f);
|
|
}
|
|
if (i == (int)titles.size() - 1) {
|
|
SimpleTextView *title = bar->Add(new SimpleTextView(titles[i], new LinearLayoutParams(0.0f, Gravity::G_VCENTER)));
|
|
title->SetBig(true);
|
|
continue;
|
|
}
|
|
Choice *crumb = bar->Add(new Choice(titles[i], new LinearLayoutParams(WRAP_CONTENT, ITEM_HEIGHT, 0.0f, Gravity::G_VCENTER)));
|
|
crumb->SetRoundCorners(RoundCorners::All);
|
|
crumb->OnClick.Add([this, settingsTab, i](UI::EventParams &) {
|
|
std::vector<int> path = pagePaths_[settingsTabs_[settingsTab].tag];
|
|
int focusItem = path[i]; // The link to the page we're leaving.
|
|
path.resize(i);
|
|
ShowPage(settingsTab, path, focusItem);
|
|
});
|
|
}
|
|
|
|
bar->Add(new Spacer(10.0f, new LinearLayoutParams(1.0f)));
|
|
|
|
if (!gamePath_.empty() && g_Config.IsGameSpecific()) {
|
|
bar->Add(CreateGameIconView(gamePath_, new LinearLayoutParams(WRAP_CONTENT, WRAP_CONTENT, 0.0f, Gravity::G_VCENTER)));
|
|
}
|
|
|
|
// While searching, the back button leaves the search, and this changes it.
|
|
if (CanShowSearch()) {
|
|
Choice *searchButton = bar->Add(new Choice(ImageID("I_SEARCH"), new LinearLayoutParams(ITEM_HEIGHT, ITEM_HEIGHT, Margins(8, 0, 0, 0))));
|
|
searchButton->SetRoundCorners(RoundCorners::All);
|
|
searchButton->OnClick.Add([this](UI::EventParams &) {
|
|
AskForSearchText();
|
|
});
|
|
}
|
|
|
|
if (!helpTag.empty() && searchText_.empty()) {
|
|
std::string settingsUrl = join("https://www.ppsspp.org/docs/settings/", helpTag);
|
|
Choice *helpButton = bar->Add(new Choice(ImageID("I_LINK_OUT_QUESTION"), new LinearLayoutParams(ITEM_HEIGHT, ITEM_HEIGHT, Margins(8, 0, 0, 0))));
|
|
helpButton->SetRoundCorners(RoundCorners::All);
|
|
helpButton->OnClick.Add([settingsUrl](UI::EventParams &) {
|
|
System_LaunchUrl(LaunchUrlType::BROWSER_URL, settingsUrl);
|
|
});
|
|
}
|
|
}
|
|
|
|
void SettingsPagesDialogScreen::AskForSearchText() {
|
|
auto se = GetI18NCategory(I18NCat::SEARCH);
|
|
auto apply = [this](std::string_view text) {
|
|
SetSearchText(StripSpaces(text));
|
|
};
|
|
if (System_GetPropertyBool(SYSPROP_HAS_TEXT_INPUT_DIALOG)) {
|
|
System_InputBoxGetString(GetRequesterToken(), se->T("Find settings"), searchText_, false, [apply](std::string_view value, int) {
|
|
apply(value);
|
|
});
|
|
return;
|
|
}
|
|
// The popup writes the text here before calling OnChange.
|
|
searchEditText_ = searchText_;
|
|
UI::TextEditPopupScreen *popup = new UI::TextEditPopupScreen(&searchEditText_, "", se->T("Find settings"), 64);
|
|
if (System_GetPropertyBool(SYSPROP_KEYBOARD_IS_SOFT)) {
|
|
popup->SetAlignTop(true);
|
|
}
|
|
popup->OnChange.Add([this, apply](UI::EventParams &) {
|
|
apply(searchEditText_);
|
|
});
|
|
screenManager()->push(popup);
|
|
}
|
|
|
|
// Every tab shows the search results while searching, so they all change.
|
|
void SettingsPagesDialogScreen::SetSearchText(std::string_view text) {
|
|
if (text == searchText_) {
|
|
return;
|
|
}
|
|
searchText_ = text;
|
|
pageChange_ = true;
|
|
RecreateViews();
|
|
}
|
|
|
|
// Recreating the views shows it. That waits for update, since this is usually called from a click on a view
|
|
// that will be deleted.
|
|
void SettingsPagesDialogScreen::ShowPage(int settingsTab, const std::vector<int> &path, int itemIndex) {
|
|
if (stacked_ && settingsTab != stackRootTab_) {
|
|
std::vector<int> rootPath = path;
|
|
rootPath.insert(rootPath.begin(), settingsTab);
|
|
ShowPage(stackRootTab_, rootPath, itemIndex);
|
|
return;
|
|
}
|
|
pagePaths_[settingsTabs_[settingsTab].tag] = path;
|
|
pageChange_ = true;
|
|
RecreateViews();
|
|
pendingTab_ = settingsTab;
|
|
pendingItem_ = itemIndex;
|
|
pendingHighlight_ = false;
|
|
}
|
|
|
|
bool SettingsPagesDialogScreen::GoBack(int settingsTab) {
|
|
if (!searchText_.empty()) {
|
|
SetSearchText("");
|
|
return true;
|
|
}
|
|
if (settingsTab < 0) {
|
|
return false;
|
|
}
|
|
std::vector<int> path = pagePaths_[settingsTabs_[settingsTab].tag];
|
|
if (path.empty()) {
|
|
return false;
|
|
}
|
|
int focusItem = path.back(); // The link to the page we're leaving.
|
|
path.pop_back();
|
|
ShowPage(settingsTab, path, focusItem);
|
|
return true;
|
|
}
|
|
|
|
// Links to the tabs, like a tab strip would.
|
|
void SettingsPagesDialogScreen::BuildStackRootPage(SettingsPage *page) {
|
|
for (int t = 0; t < stackRootTab_; t++) {
|
|
const SettingsTab &tab = settingsTabs_[t];
|
|
SettingItem &link = page->Link(SettingText::Raw(tab.title.c_str()), tab.cat, tab.build);
|
|
link.image = tab.icon;
|
|
if (!tab.helpTag.empty()) {
|
|
link.helpTag = tab.helpTag.c_str();
|
|
}
|
|
}
|
|
}
|
|
|
|
// A page asked for with OpenPage.
|
|
int SettingsPagesDialogScreen::OpenRequestedPage() {
|
|
if (openTabTag_.empty()) {
|
|
return -1;
|
|
}
|
|
int opened = -1;
|
|
for (int t = 0; t < (int)settingsTabs_.size(); t++) {
|
|
const SettingsTab &tab = settingsTabs_[t];
|
|
if (tab.tag != openTabTag_) {
|
|
continue;
|
|
}
|
|
SettingsPage page(tab.cat, this);
|
|
tab.build(&page);
|
|
for (int i = 0; i < (int)page.Items().size(); i++) {
|
|
const SettingItem &item = page.Items()[i];
|
|
if (item.type == SettingType::Link && item.text.key && openLinkKey_ == item.text.key) {
|
|
pagePaths_[tab.tag] = { i };
|
|
pendingTab_ = t;
|
|
opened = t;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
openTabTag_.clear();
|
|
openLinkKey_.clear();
|
|
return opened;
|
|
}
|
|
|
|
int SettingsPagesDialogScreen::CurrentSettingsTab() const {
|
|
if (stacked_) {
|
|
return stackRootTab_;
|
|
}
|
|
if (!tabHolder_) {
|
|
return -1;
|
|
}
|
|
int current = tabHolder_->GetCurrentTab();
|
|
for (int t = 0; t < (int)settingsTabs_.size(); t++) {
|
|
if (settingsTabs_[t].tabIndex == current) {
|
|
return t;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
bool SettingsPagesDialogScreen::CanShowSearch() {
|
|
if (!System_GetPropertyBool(SYSPROP_HAS_KEYBOARD) && !System_GetPropertyBool(SYSPROP_HAS_TEXT_INPUT_DIALOG)) {
|
|
return false;
|
|
}
|
|
return System_GetPropertyInt(SYSPROP_DEVICE_TYPE) != DEVICE_TYPE_VR;
|
|
}
|
|
|
|
// Back closes the search, then goes back a page, before leaving the screen.
|
|
bool SettingsPagesDialogScreen::key(const KeyInput &key) {
|
|
if (key.flags & KeyInputFlags::DOWN) {
|
|
ClearHighlight();
|
|
}
|
|
if ((key.flags & KeyInputFlags::DOWN) && UI::IsEscapeKey(key)) {
|
|
if (GoBack(CurrentSettingsTab())) {
|
|
UI::PlayUISound(UI::UISound::BACK);
|
|
return true;
|
|
}
|
|
}
|
|
return UIBaseDialogScreen::key(key);
|
|
}
|
|
|
|
bool SettingsPagesDialogScreen::touch(const TouchInput &touch) {
|
|
if (touch.flags & TouchInputFlags::DOWN) {
|
|
ClearHighlight();
|
|
}
|
|
return UIBaseDialogScreen::touch(touch);
|
|
}
|
|
|
|
void SettingsPagesDialogScreen::ClearHighlight() {
|
|
if (highlightedView_) {
|
|
highlightedView_->SetHighlighted(false);
|
|
highlightedView_ = nullptr;
|
|
}
|
|
}
|
|
|
|
static bool SettingTextMatches(const SettingText &text, I18NCat pageCat, std::string_view query) {
|
|
if (text.empty()) {
|
|
return false;
|
|
}
|
|
return containsNoCase(text.Translate(pageCat), query) || containsNoCase(text.English(), query);
|
|
}
|
|
|
|
// Lists the matching settings from all tabs and the pages they link to, each leading to where it is.
|
|
void SettingsPagesDialogScreen::CreateSearchResults(UI::ViewGroup *parent) {
|
|
using namespace UI;
|
|
auto se = GetI18NCategory(I18NCat::SEARCH);
|
|
|
|
bool found = false;
|
|
for (int t = 0; t < (int)settingsTabs_.size(); t++) {
|
|
// It only links to the others.
|
|
if (t == stackRootTab_) {
|
|
continue;
|
|
}
|
|
const SettingsTab &tab = settingsTabs_[t];
|
|
SettingsPage page(tab.cat, this);
|
|
tab.build(&page);
|
|
if (SearchPage(parent, t, {}, page, tab.title, 0)) {
|
|
found = true;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
parent->Add(new TextView(ApplySafeSubstitutions(se->T("No settings matched '%1'"), searchText_), new LinearLayoutParams(Margins(20, 5))));
|
|
}
|
|
}
|
|
|
|
bool SettingsPagesDialogScreen::SearchPage(UI::ViewGroup *parent, int settingsTab, const std::vector<int> &path, const SettingsPage &page, const std::string &title, int depth) {
|
|
using namespace UI;
|
|
const I18NCat cat = page.DefaultCat();
|
|
const std::deque<SettingItem> &items = page.Items();
|
|
|
|
// A matching header shows its whole section, and a matching hint the setting it belongs to.
|
|
std::vector<bool> show(items.size());
|
|
bool sectionMatches = false;
|
|
int lastSetting = -1;
|
|
for (int i = 0; i < (int)items.size(); i++) {
|
|
const SettingItem &item = items[i];
|
|
bool matches = SettingTextMatches(item.text, cat, searchText_);
|
|
switch (item.type) {
|
|
case SettingType::Header:
|
|
sectionMatches = matches;
|
|
break;
|
|
case SettingType::Hint:
|
|
if (matches && lastSetting >= 0) {
|
|
show[lastSetting] = true;
|
|
}
|
|
break;
|
|
default:
|
|
lastSetting = i;
|
|
show[i] = matches || sectionMatches;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// The page title is the last part of title.
|
|
size_t lastSeparator = title.rfind(" > ");
|
|
std::string_view pageTitle = lastSeparator == std::string::npos ? std::string_view(title) : std::string_view(title).substr(lastSeparator + 3);
|
|
|
|
bool found = false;
|
|
const SettingItem *header = nullptr;
|
|
// The section's results, created with its first.
|
|
ChoiceGroup *group = nullptr;
|
|
for (int i = 0; i < (int)items.size(); i++) {
|
|
const SettingItem &item = items[i];
|
|
if (item.type == SettingType::Header) {
|
|
header = &item;
|
|
group = nullptr;
|
|
continue;
|
|
}
|
|
if (!show[i]) {
|
|
continue;
|
|
}
|
|
|
|
if (!group) {
|
|
std::string headerTitle = title;
|
|
// Pages often start with a header repeating their title.
|
|
if (header && !header->text.empty() && header->text.Translate(cat) != pageTitle) {
|
|
headerTitle += " > ";
|
|
headerTitle += header->text.Translate(cat);
|
|
}
|
|
group = parent->Add(new ChoiceGroup(headerTitle));
|
|
}
|
|
// Goes to the setting, highlighted, or opens the page that a link leads to.
|
|
Choice *result = group->Add(new Choice(item.text.Translate(cat)));
|
|
result->SetIconRight(ImageID("I_CHEVRON_RIGHT"));
|
|
std::vector<int> pagePath = path;
|
|
int focusItem = i;
|
|
if (item.type == SettingType::Link) {
|
|
pagePath.push_back(i);
|
|
focusItem = -1;
|
|
}
|
|
result->OnClick.Add([this, settingsTab, pagePath, focusItem](UI::EventParams &) {
|
|
GoToPage(settingsTab, pagePath, focusItem);
|
|
});
|
|
found = true;
|
|
}
|
|
|
|
// And the pages this one links to.
|
|
if (depth < MAX_PAGE_DEPTH) {
|
|
for (int i = 0; i < (int)items.size(); i++) {
|
|
const SettingItem &item = items[i];
|
|
if (item.type != SettingType::Link) {
|
|
continue;
|
|
}
|
|
SettingsPage linked(item.linkCat, page.Host());
|
|
item.linkBuild(&linked);
|
|
std::vector<int> linkPath = path;
|
|
linkPath.push_back(i);
|
|
if (SearchPage(parent, settingsTab, linkPath, linked, title + " > " + std::string(item.text.Translate(cat)), depth + 1)) {
|
|
found = true;
|
|
}
|
|
}
|
|
}
|
|
return found;
|
|
}
|
|
|
|
// Leaves the search, and shows the page at path in its tab, with the item focused (in update, so it's
|
|
// highlighted and scrolled into view).
|
|
void SettingsPagesDialogScreen::GoToPage(int settingsTab, const std::vector<int> &path, int itemIndex) {
|
|
SetSearchText("");
|
|
if (!stacked_) {
|
|
// The tab strip keeps showing its tab when the views are recreated. This creates the tab if it wasn't
|
|
// already, so it has to come before ShowPage, or that would take the item to focus.
|
|
SetCurrentTab(settingsTabs_[settingsTab].tabIndex);
|
|
}
|
|
ShowPage(settingsTab, path, itemIndex);
|
|
pendingHighlight_ = true;
|
|
}
|
|
|
|
void SettingsPagesDialogScreen::update() {
|
|
UIBaseDialogScreen::update();
|
|
|
|
// The tab may have just been created, or not been laid out while hidden. Scrolling needs the
|
|
// view's position, so wait for a layout.
|
|
if (pendingFocusView_ && pendingFocusView_->GetBounds().h > 0.0f) {
|
|
// Moving between pages only focuses with the keyboard (or a controller), which needs a focused view.
|
|
// A setting picked from search results is always shown, and with a mouse or touch, highlighted.
|
|
if (pendingHighlight_ || UI::IsFocusMovementEnabled()) {
|
|
UI::ScrollView *scrollView = UI::GetEnclosingScrollView(pendingFocusView_);
|
|
if (scrollView) {
|
|
scrollView->ScrollToCenter(pendingFocusView_);
|
|
}
|
|
if (UI::IsFocusMovementEnabled()) {
|
|
UI::SetFocusedView(pendingFocusView_, UI::FocusFlags::CAUSE_RESTORE);
|
|
} else if (UI::Clickable *clickable = dynamic_cast<UI::Clickable *>(pendingFocusView_)) {
|
|
ClearHighlight();
|
|
clickable->SetHighlighted(true);
|
|
highlightedView_ = clickable;
|
|
}
|
|
}
|
|
pendingFocusView_ = nullptr;
|
|
pendingHighlight_ = false;
|
|
}
|
|
}
|
|
|
|
|
|
void SettingsPagesDialogScreen::CreateViews() {
|
|
settingsTabs_.clear();
|
|
pendingFocusView_ = nullptr;
|
|
highlightedView_ = nullptr;
|
|
|
|
bool wasStacked = stacked_;
|
|
stacked_ = GetDeviceOrientation() == DeviceOrientation::Portrait;
|
|
using namespace UI;
|
|
|
|
if (stacked_) {
|
|
PreCreateViews();
|
|
tabHolder_ = nullptr;
|
|
CreateTabs();
|
|
const int requestedTab = OpenRequestedPage();
|
|
|
|
stackRootTab_ = (int)settingsTabs_.size();
|
|
settingsTabs_.push_back(SettingsTab{ stackRootTab_, "SettingsRoot", std::string(T(I18NCat::DIALOG, "Settings")), I18NCat::DIALOG, [this](SettingsPage *page) {
|
|
BuildStackRootPage(page);
|
|
}, "", ImageID::invalid() });
|
|
SettingsTab &rootTab = settingsTabs_[stackRootTab_];
|
|
|
|
// Show the tab that was asked for, or that was shown before rotating, under the page linking to it.
|
|
// (Pages shown from here are already on the root tab's path.)
|
|
int showTab = -1;
|
|
if (requestedTab >= 0) {
|
|
showTab = requestedTab;
|
|
pendingTab_ = stackRootTab_;
|
|
} else if (viewsCreated_ && !wasStacked && currentTabSetting_ && *currentTabSetting_ >= 0 && *currentTabSetting_ < stackRootTab_) {
|
|
showTab = *currentTabSetting_;
|
|
}
|
|
if (showTab >= 0) {
|
|
std::vector<int> rootPath = pagePaths_[settingsTabs_[showTab].tag];
|
|
rootPath.insert(rootPath.begin(), showTab);
|
|
pagePaths_[rootTab.tag] = rootPath;
|
|
}
|
|
|
|
LinearLayout *contents = new LinearLayout(ORIENT_VERTICAL, new LayoutParams(FILL_PARENT, FILL_PARENT));
|
|
contents->SetSpacing(0.0f);
|
|
root_ = contents;
|
|
CreateSettingsTabContents(stackRootTab_, contents);
|
|
viewsCreated_ = true;
|
|
pageChange_ = false;
|
|
return;
|
|
}
|
|
stackRootTab_ = -1;
|
|
|
|
UITabbedBaseDialogScreen::CreateViews();
|
|
viewsCreated_ = true;
|
|
pageChange_ = false;
|
|
}
|
|
|
|
// A page asked for with OpenPage is opened on its tab.
|
|
int SettingsPagesDialogScreen::InitialTab() {
|
|
OpenRequestedPage();
|
|
return pendingTab_ >= 0 ? settingsTabs_[pendingTab_].tabIndex : -1;
|
|
}
|
|
|
|
// Picking a tab means leaving the search for it.
|
|
void SettingsPagesDialogScreen::TabChanged(int tab) {
|
|
SetSearchText("");
|
|
}
|