Move more of the search code out of GameBrowser

This commit is contained in:
Henrik Rydgård
2026-05-03 17:44:01 +02:00
parent e354d0dfdd
commit 2d90058421
4 changed files with 129 additions and 127 deletions
-110
View File
@@ -558,122 +558,12 @@ bool GameBrowser::Key(const KeyInput &input) {
return search_.Key(gameList_, input);
}
bool SearchEngine::Key(UI::ViewGroup *viewGroup, const KeyInput &input) {
bool retval = false;
// 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) {
// TODO: Save focus state here.
// Insert it! (todo: do it with a string insert)
char buf[8];
buf[u8_wc_toutf8(buf, unichar)] = '\0';
searchFilter += buf;
ApplySearchFilter(viewGroup, true);
retval = true;
}
} else if (input.flags & KeyInputFlags::DOWN) {
if (input.keyCode == NKCODE_DEL) {
if (!searchFilter.empty()) {
searchFilter.pop_back();
ApplySearchFilter(viewGroup, true);
retval = true;
if (searchFilter.empty()) {
// TODO: Restore focus state here.
UI::EnableFocusMovement(false);
}
} else {
// Empty search filter. Navigate upwards on backspace?
}
} else if (!searchFilter.empty() && input.keyCode == NKCODE_ESCAPE) {
searchFilter.clear();
ApplySearchFilter(viewGroup, false);
retval = true;
// TODO: Restore focus state here.
UI::EnableFocusMovement(false);
}
}
return retval;
}
void GameBrowser::SetSearchFilter(const std::string &filter, bool setKeyboardFocus) {
search_.searchFilter = filter;
// We don't refresh because game info loads asynchronously anyway.
search_.ApplySearchFilter(gameList_, setKeyboardFocus);
}
void SearchEngine::ApplySearchFilter(UI::ViewGroup *viewGroup, bool setKeyboardFocus) {
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 = NormalizeForSearch(searchFilter);
searchPending = false;
// By default, everything is matching.
searchStates.resize(viewGroup->GetNumSubviews(), SearchState::MATCH);
if (filter.empty()) {
// Just quickly mark anything we hid as visible again.
for (int i = 0; i < viewGroup->GetNumSubviews(); ++i) {
UI::View *v = viewGroup->GetViewByIndex(i);
if (searchStates[i] != SearchState::MATCH)
v->SetVisibility(UI::V_VISIBLE);
}
searchStates.clear();
return;
}
UI::View *firstMatch = nullptr;
for (int i = 0; i < viewGroup->GetNumSubviews(); ++i) {
UI::View *v = viewGroup->GetViewByIndex(i);
std::string label = v->DescribeText();
// This is a bit of a hack to recognize a pending game title.
if (label == "...") {
searchPending = true;
// Hide anything pending while, we'll pop-in search results as they match.
// Note: we leave it at MATCH if gone before, so we don't show it again.
if (v->GetVisibility() == UI::V_VISIBLE) {
if (searchStates[i] == SearchState::MATCH)
v->SetVisibility(UI::V_GONE);
searchStates[i] = SearchState::PENDING;
}
continue;
}
label = NormalizeForSearch(label);
bool match = v->CanBeFocused() && label.find(filter) != label.npos;
if (match && !firstMatch) {
firstMatch = v;
}
if (match && searchStates[i] != SearchState::MATCH) {
// It was previously visible and force hidden, so show it again.
v->SetVisibility(UI::V_VISIBLE);
searchStates[i] = SearchState::MATCH;
} else if (!match && searchStates[i] == SearchState::MATCH && v->GetVisibility() == UI::V_VISIBLE) {
v->SetVisibility(UI::V_GONE);
searchStates[i] = SearchState::MISMATCH;
}
}
if (firstMatch) {
if (setKeyboardFocus) {
UI::EnableFocusMovement(true);
}
UI::SetFocusedView(firstMatch);
}
}
void GameBrowser::LayoutChange(UI::EventParams &e) {
*gridStyle_ = e.a == 0 ? true : false;
Refresh();
+1 -17
View File
@@ -24,22 +24,6 @@ enum class BrowseFlags {
};
ENUM_CLASS_BITOPS(BrowseFlags);
enum class SearchState {
MATCH,
MISMATCH,
PENDING,
};
struct SearchEngine {
SearchBar *searchBar;
std::string searchFilter;
std::vector<SearchState> searchStates;
bool searchPending;
void ApplySearchFilter(UI::ViewGroup *viewGroup, bool setKeyboardFocus);
bool Key(UI::ViewGroup *viewGroup, const KeyInput &input);
};
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);
@@ -97,7 +81,7 @@ private:
std::string lastText_;
std::string lastLink_;
SearchEngine search_{};
ViewSearch search_{};
Path focusGamePath_;
bool listingPending_ = false;
+112
View File
@@ -7,6 +7,8 @@
#include "Common/Data/Text/I18n.h"
#include "Common/Math/curves.h"
#include "Common/StringUtils.h"
#include "Common/Data/Encoding/Utf8.h"
#include "Common/UI/Root.h"
#include "UI/MiscViews.h"
#include "UI/GameInfoCache.h"
#include "Common/UI/PopupScreens.h"
@@ -350,3 +352,113 @@ bool SearchBar::Touch(const TouchInput &input) {
}
return retval;
}
void ViewSearch::ApplySearchFilter(UI::ViewGroup *viewGroup, bool setKeyboardFocus) {
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 = NormalizeForSearch(searchFilter);
searchPending = false;
// By default, everything is matching.
searchStates.resize(viewGroup->GetNumSubviews(), SearchState::MATCH);
if (filter.empty()) {
// Just quickly mark anything we hid as visible again.
for (int i = 0; i < viewGroup->GetNumSubviews(); ++i) {
UI::View *v = viewGroup->GetViewByIndex(i);
if (searchStates[i] != SearchState::MATCH)
v->SetVisibility(UI::V_VISIBLE);
}
searchStates.clear();
return;
}
UI::View *firstMatch = nullptr;
for (int i = 0; i < viewGroup->GetNumSubviews(); ++i) {
UI::View *v = viewGroup->GetViewByIndex(i);
std::string label = v->DescribeText();
// This is a bit of a hack to recognize a pending game title.
if (label == "...") {
searchPending = true;
// Hide anything pending while, we'll pop-in search results as they match.
// Note: we leave it at MATCH if gone before, so we don't show it again.
if (v->GetVisibility() == UI::V_VISIBLE) {
if (searchStates[i] == SearchState::MATCH)
v->SetVisibility(UI::V_GONE);
searchStates[i] = SearchState::PENDING;
}
continue;
}
label = NormalizeForSearch(label);
bool match = v->CanBeFocused() && label.find(filter) != label.npos;
if (match && !firstMatch) {
firstMatch = v;
}
if (match && searchStates[i] != SearchState::MATCH) {
// It was previously visible and force hidden, so show it again.
v->SetVisibility(UI::V_VISIBLE);
searchStates[i] = SearchState::MATCH;
} else if (!match && searchStates[i] == SearchState::MATCH && v->GetVisibility() == UI::V_VISIBLE) {
v->SetVisibility(UI::V_GONE);
searchStates[i] = SearchState::MISMATCH;
}
}
if (firstMatch) {
if (setKeyboardFocus) {
UI::EnableFocusMovement(true);
}
UI::SetFocusedView(firstMatch);
}
}
bool ViewSearch::Key(UI::ViewGroup *viewGroup, const KeyInput &input) {
bool retval = false;
// 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) {
// TODO: Save focus state here.
// Insert it! (todo: do it with a string insert)
char buf[8];
buf[u8_wc_toutf8(buf, unichar)] = '\0';
searchFilter += buf;
ApplySearchFilter(viewGroup, true);
retval = true;
}
} else if (input.flags & KeyInputFlags::DOWN) {
if (input.keyCode == NKCODE_DEL) {
if (!searchFilter.empty()) {
searchFilter.pop_back();
ApplySearchFilter(viewGroup, true);
retval = true;
if (searchFilter.empty()) {
// TODO: Restore focus state here.
UI::EnableFocusMovement(false);
}
} else {
// Empty search filter. Navigate upwards on backspace?
}
} else if (!searchFilter.empty() && input.keyCode == NKCODE_ESCAPE) {
searchFilter.clear();
ApplySearchFilter(viewGroup, false);
retval = true;
// TODO: Restore focus state here.
UI::EnableFocusMovement(false);
}
}
return retval;
}
+16
View File
@@ -115,3 +115,19 @@ public:
private:
std::string searchFilter_ = "N/A";
};
enum class SearchState {
MATCH,
MISMATCH,
PENDING,
};
struct ViewSearch {
SearchBar *searchBar;
std::string searchFilter;
std::vector<SearchState> searchStates;
bool searchPending;
void ApplySearchFilter(UI::ViewGroup *viewGroup, bool setKeyboardFocus);
bool Key(UI::ViewGroup *viewGroup, const KeyInput &input);
};