Initial work on instant type-to-search.

This commit is contained in:
Henrik Rydgård committed 2026-04-14 18:17:54 -06:00
1 parent eb476d522e
commit 6c2c4606dc
3 files changed
+91 -10

No files matched your search

+61 -5
View File
@@ -31,6 +31,7 @@
#include "Common/TimeUtil.h"
#include "Common/StringUtils.h"
#include "Common/System/OSD.h"
#include "Common/Data/Encoding/Utf8.h"
#include "Core/System.h"
#include "Core/Util/RecentFiles.h"
#include "Core/HLE/sceCtrl.h"
@@ -532,26 +533,65 @@ void GameBrowser::SetPath(const Path &path) {
Refresh();
}
void GameBrowser::ApplySearchFilter(const std::string &filter) {
searchFilter_ = filter;
std::transform(searchFilter_.begin(), searchFilter_.end(), searchFilter_.begin(), tolower);
bool GameBrowser::Key(const KeyInput &input) {
bool retval = LinearLayout::Key(input);
if (retval) {
return true;
}
// Only one is visible at a time, so we can just grab all Char input.
if (input.flags & KeyInputFlags::CHAR) {
const int unichar = input.keyCode;
if (unichar >= 0x20) {
// Insert it! (todo: do it with a string insert)
char buf[8];
buf[u8_wc_toutf8(buf, unichar)] = '\0';
searchFilter_ += buf;
ApplySearchFilter();
retval = true;
}
} else if (input.flags & KeyInputFlags::DOWN) {
if (input.keyCode == NKCODE_DEL) {
if (!searchFilter_.empty()) {
searchFilter_.pop_back();
ApplySearchFilter();
retval = true;
}
} else if (!searchFilter_.empty() && input.keyCode == NKCODE_ESCAPE) {
searchFilter_.clear();
ApplySearchFilter();
retval = true;
}
}
return retval;
}
void GameBrowser::SetSearchFilter(const std::string &filter) {
searchFilter_ = filter;
// We don't refresh because game info loads asynchronously anyway.
ApplySearchFilter();
}
void GameBrowser::ApplySearchFilter() {
if (searchBar_) {
searchBar_->SetSearchFilter(searchFilter_);
searchBar_->SetVisibility(searchFilter_.empty() ? UI::V_GONE : UI::V_VISIBLE);
}
if (searchFilter_.empty() && searchStates_.empty()) {
// We haven't hidden anything, and we're not searching, so do nothing.
searchPending_ = false;
return;
}
std::string filter = searchFilter_;
std::transform(filter.begin(), filter.end(), filter.begin(), tolower);
searchPending_ = false;
// By default, everything is matching.
searchStates_.resize(gameList_->GetNumSubviews(), SearchState::MATCH);
if (searchFilter_.empty()) {
if (filter.empty()) {
// Just quickly mark anything we hid as visible again.
for (int i = 0; i < gameList_->GetNumSubviews(); ++i) {
UI::View *v = gameList_->GetViewByIndex(i);
@@ -581,7 +621,7 @@ void GameBrowser::ApplySearchFilter() {
}
std::transform(label.begin(), label.end(), label.begin(), tolower);
bool match = v->CanBeFocused() && label.find(searchFilter_) != label.npos;
bool match = v->CanBeFocused() && label.find(filter) != label.npos;
if (match && searchStates_[i] != SearchState::MATCH) {
// It was previously visible and force hidden, so show it again.
v->SetVisibility(UI::V_VISIBLE);
@@ -721,11 +761,27 @@ void GameBrowser::Draw(UIContext &dc) {
view->Draw(dc);
}
}
if (clip_) {
dc.PopScissor();
}
}
void SearchBar::Draw(UIContext &dc) {
using namespace UI;
Bounds overlayBounds = bounds_.Inset(12, 0, 12, 0);
dc.FillRect(dc.GetTheme().itemStyle.background, overlayBounds);
dc.DrawText(searchFilter_, overlayBounds.x + 10, overlayBounds.centerY(), 0xFFFFFFFF, ALIGN_VCENTER);
}
void SearchBar::GetContentDimensions(const UIContext &dc, float &w, float &h) const {
w = 0;
h = 0;
dc.MeasureText(dc.GetTheme().uiFont, 1.0f, 1.0f, searchFilter_, &w, &h);
w += 20; // Padding
h += 10;
}
static bool IsValidPBP(const Path &path, bool allowHomebrew) {
if (!File::Exists(path))
return false;
+19 -1
View File
@@ -23,6 +23,19 @@ enum class BrowseFlags {
};
ENUM_CLASS_BITOPS(BrowseFlags);
class SearchBar : public UI::InertView {
public:
SearchBar(UI::LayoutParams *params) : UI::InertView(params) { SetVisibility(UI::Visibility::V_GONE); }
void Draw(UIContext &dc) override;
void SetSearchFilter(std::string_view filter) {
searchFilter_ = filter;
}
void GetContentDimensions(const UIContext &dc, float &w, float &h) const override;
private:
std::string searchFilter_ = "N/A";
};
class GameBrowser : public UI::LinearLayout {
public:
GameBrowser(int token, const Path &path, BrowseFlags browseFlags, bool portrait, bool *gridStyle, ScreenManager *screenManager, std::string_view lastText, std::string_view lastLink, UI::LayoutParams *layoutParams = nullptr);
@@ -33,7 +46,11 @@ public:
void FocusGame(const Path &gamePath);
void SetPath(const Path &path);
void ApplySearchFilter(const std::string &filter);
void SetSearchBar(SearchBar *searchBar) {
searchBar_ = searchBar;
}
bool Key(const KeyInput &key) override;
void SetSearchFilter(const std::string &filter);
void Draw(UIContext &dc) override;
void Update() override;
void RequestRefresh() {
@@ -80,6 +97,7 @@ private:
UI::ViewGroup *gameList_ = nullptr;
PathBrowser path_;
SearchBar *searchBar_ = nullptr;
bool *gridStyle_ = nullptr;
BrowseFlags browseFlags_;
std::string lastText_;
+11 -4
View File
@@ -138,7 +138,10 @@ void MainScreen::CreateRecentTab() {
using namespace UI;
auto mm = GetI18NCategory(I18NCat::MAINMENU);
ScrollView *scrollRecentGames = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT));
LinearLayout *recentContainer = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT));
SearchBar *search = recentContainer->Add(new SearchBar(new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
ScrollView *scrollRecentGames = recentContainer->Add(new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
scrollRecentGames->SetTag("MainScreenRecentGames");
bool portrait = GetDeviceOrientation() == DeviceOrientation::Portrait;
@@ -146,11 +149,12 @@ void MainScreen::CreateRecentTab() {
GameBrowser *tabRecentGames = new GameBrowser(GetRequesterToken(),
Path("!RECENT"), BrowseFlags::NONE, portrait, &g_Config.bGridView1, screenManager(), "", "",
new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT));
tabRecentGames->SetSearchBar(search);
scrollRecentGames->Add(tabRecentGames);
gameBrowsers_.push_back(tabRecentGames);
tabHolder_->AddTab(mm->T("Recent"), ImageID::invalid(), scrollRecentGames);
tabHolder_->AddTab(mm->T("Recent"), ImageID::invalid(), recentContainer);
tabRecentGames->OnChoice.Handle(this, &MainScreen::OnGameSelectedInstant);
tabRecentGames->OnHoldChoice.Handle(this, &MainScreen::OnGameSelected);
tabRecentGames->OnHighlight.Handle(this, &MainScreen::OnGameHighlight);
@@ -572,8 +576,11 @@ void MainScreen::update() {
UpdateUIState(UISTATE_MENU);
if (searchChanged_) {
for (auto browser : gameBrowsers_)
browser->ApplySearchFilter(searchFilter_);
for (auto browser : gameBrowsers_) {
if (browser->GetVisibility() == UI::V_VISIBLE) {
browser->SetSearchFilter(searchFilter_);
}
}
searchChanged_ = false;
}
}