Skip to content

seach popup#910

Open
XenuIsWatching wants to merge 2 commits intoRetroPie:masterfrom
XenuIsWatching:feature/search
Open

seach popup#910
XenuIsWatching wants to merge 2 commits intoRetroPie:masterfrom
XenuIsWatching:feature/search

Conversation

@XenuIsWatching
Copy link
Copy Markdown

Adds a new full-screen search popup accessible from any game list view.
The popup allows players to search across all systems (or a single
system) in real time using either a physical keyboard or a gamepad.
Results are displayed with rich metadata, and games can be launched
directly from the results list.

The search popup is opened by pressing the Right Trigger (RT) button
from any game list view. The trigger binding is configurable — whatever
input is mapped to "righttrigger" opens the overlay. When opened using a
physical keyboard key bound to RT (e.g. the '2' key), any SDL_TEXTINPUT
event queued by that same keypress is flushed immediately so the
triggering character does not appear in the search bar.

The popup fills the entire screen with a translucent dark background
panel drawn in render(). It is divided into three main areas:

  • Top bar: a search text field showing the current query with a cursor
    position indicator (pipe '|'), and below it a character selection row
    for gamepad text entry.
  • Left column: a scrollable result list (TextListComponent) showing
    matching game names. In all-systems mode each entry is suffixed with
    the system name in brackets. A status/placeholder message overlays
    the list when it is empty.
  • Right column: a metadata panel showing cover image or thumbnail,
    description, rating, developer, publisher, genre, players, release
    date, last played, play count, marquee, and optionally a video
    preview. All metadata panel components are fully theme-driven (see
    Theme Support below).

The character row is a single horizontal strip of selectable character
cells used for gamepad text entry. It supports three modes:

  • LETTERS — mode-switch key "123", space, A–Z, backspace, ← →
  • NUMBERS — mode-switch key "!@#", space, 1–9, 0, backspace, ← →
  • SYMBOLS — mode-switch key "ABC", space, punctuation set, backspace, ← →

The ← and → cells move the text cursor within the search query (not the
char row selection). The backspace cell deletes the character to the left
of the cursor.

Cell widths are computed from the font metrics plus padding. If the
combined width of all cells exceeds the component width (e.g. after
adding many symbols), the per-cell padding is uniformly reduced so that
all characters remain visible without scrolling or clipping.

When focus is on the char row:

  • D-pad / left-stick left/right — move the char row selection cursor
    left or right. Holding the direction triggers auto-scroll repeat
    after an initial delay (500 ms) at a fixed period (100 ms).
  • A button — activate the selected cell: type the character, switch
    mode, trigger backspace, or move the text cursor.
  • X button — immediate backspace. Holding X triggers hold-to-repeat
    after 500 ms at 80 ms intervals (same timing as keyboard held keys).
  • L shoulder / R shoulder — move the text cursor left or right within
    the query string. Holding triggers repeat after 500 ms at 80 ms.
  • D-pad / left-stick down — move focus to the result list (only if
    results are present).
  • B button — close the popup.
  • Start — open the main menu.
  • RT — no-op when already in char row (focus is already here).

When a physical keyboard is in use and focus is on the char row, all
keyboard events are intercepted before the button map is consulted. This
prevents conflicts where a printable key (e.g. 's') is also mapped to a
gamepad button action (e.g. 'x' = backspace). The interception rules:

  • Printable characters — handled via SDL_TEXTINPUT events, which fire
    after SDL_KEYDOWN. Control characters and space are excluded from
    SDL_TEXTINPUT; space is handled directly in the SDLK_SPACE case.
  • Backspace — deletes the character left of the cursor. Holding
    triggers repeat after 500 ms at 80 ms intervals using a software
    timer (mKeyRepeatKey / mKeyRepeatTimer), since SDL filters key.repeat
    for non-printable keys before they reach input().
  • Delete — deletes the character right of the cursor. Same hold-to-
    repeat behaviour as backspace.
  • Left / Right arrow — move the text cursor. Hold-to-repeat applies.
  • Home — jump the text cursor to the start of the query.
  • End — jump the text cursor to the end of the query.
  • Down arrow — move focus to the result list (only if results exist).
  • Escape — close the popup.
  • All other keys — fall through (return false) so SDL_TEXTINPUT fires
    for printable characters.

The help bar switches between keyboard hints ("esc=close") and gamepad
hints immediately whenever the active input device type changes.

Pressing down (gamepad) or the down arrow (keyboard) from the char row
moves focus to the result list. The char row selection highlight dims
and the result list selector becomes visible.

While focus is on the result list:

  • Up / Down — scroll through results. Reaching the top wraps focus
    back to the char row; reaching the bottom also wraps back to the
    char row.
  • A button — launch the selected game.
  • X button — jump to a random result.
  • Y button — toggle the selected game in/out of the current collection
    (not shown in Kid UI mode).
  • Select — open GuiGamelistOptions with a jump-to callback so the
    options dialog can reposition the result list cursor.
  • RT — return focus to the char row. Any SDL_TEXTINPUT events queued
    by the RT keypress are flushed so the triggering character is not
    inserted.
  • B button / Escape — close the popup.
  • Start — open the main menu.
  • L / R shoulder — page through the result list.

Searching runs on a background thread to avoid blocking the render loop.
Each keystroke cancels any in-progress search (sets mCancelFlag), joins
the previous thread, then starts a new std::thread that:

  1. Iterates mAllGames / mLowerNames (built once at popup open).
  2. Checks mCancelFlag after each game so a superseded search exits
    early.
  3. Sorts the matches alphabetically.
  4. Stores results in mPendingResults under mResultMutex and sets
    mResultsReady.

The main thread polls mResultsReady in update() and applies the results
to the TextListComponent on the next frame.

When the result list cursor moves to a game belonging to a different
system than the one currently themed, applyTheme() is called with the
new system's theme. The result list itself is intentionally excluded from
this re-application to avoid flickering the favorite indicator icon on
every cursor movement — its theme is applied exactly once in the
constructor.

The search view ("search") is fully documented in THEMES.md. Supported
elements:

Layout:
background — image, optional, rendered behind everything (z=0)
searchtext — text, the query bar (pos/size/font/color; text managed
by engine — use ALL ^ TEXT)
listmessage — text, status overlay on the result list
gamelist — textlist, the result list

Metadata values:
md_image, md_thumbnail, md_video, md_marquee, md_name
md_description, md_rating, md_releasedate, md_developer,
md_publisher, md_genre, md_players, md_lastplayed, md_playcount

Metadata labels (text):
md_lbl_rating, md_lbl_releasedate, md_lbl_developer,
md_lbl_publisher, md_lbl_genre, md_lbl_players,
md_lbl_lastplayed, md_lbl_playcount

md_video defaults to invisible (setVisible(false)) — a theme must
explicitly set true to enable video playback. All
other metadata components default to off-screen positions so they are
invisible unless the theme positions them.

  • ThemeData: registers "search" in sSupportedViews.
  • HelpComponent: unknown icon names return nullptr silently (no
    LogError) so text-only help prompts (e.g. "esc") render without
    spamming the log. Adds "esc" → button_esc_key.svg icon mapping.
  • IList: adds getCursorIndex() / setCursorIndex(int) accessors.
  • InputManager: SDL2 GameController axis/button events are translated
    to the legacy SDL_JOYSTICK format used by the rest of the input
    pipeline, enabling controllers that are recognised by SDL2's
    GameController API but not the raw joystick API to work correctly.
  • ViewController / SystemView: wires RT to open GuiSearchPopup from
    the game list.
  • Window: passes SDL_TEXTINPUT events through to the top-of-stack GUI
    via the existing textInput() path.
  • GuiGamelistOptions: extended to accept an optional jump-to callback
    and an optional pre-filtered game list for use from search context.
Video.Project.10.mp4

XenuIsWatching and others added 2 commits April 9, 2026 22:55
Shows the LR shoulder button pair as a "page" action in the help bar
for Basic, Detailed, and Video game list views (which all inherit
BasicGameListView::getHelpPrompts). GridGameListView already uses LR
for quick system select so it is left unchanged.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a new full-screen search popup accessible from any game list view.
The popup allows players to search across all systems (or a single
system) in real time using either a physical keyboard or a gamepad.
Results are displayed with rich metadata, and games can be launched
directly from the results list.

The search popup is opened by pressing the Right Trigger (RT) button
from any game list view. The trigger binding is configurable — whatever
input is mapped to "righttrigger" opens the overlay. When opened using a
physical keyboard key bound to RT (e.g. the '2' key), any SDL_TEXTINPUT
event queued by that same keypress is flushed immediately so the
triggering character does not appear in the search bar.

The popup fills the entire screen with a translucent dark background
panel drawn in render(). It is divided into three main areas:

  - Top bar: a search text field showing the current query with a cursor
    position indicator (pipe '|'), and below it a character selection row
    for gamepad text entry.
  - Left column: a scrollable result list (TextListComponent) showing
    matching game names. In all-systems mode each entry is suffixed with
    the system name in brackets. A status/placeholder message overlays
    the list when it is empty.
  - Right column: a metadata panel showing cover image or thumbnail,
    description, rating, developer, publisher, genre, players, release
    date, last played, play count, marquee, and optionally a video
    preview. All metadata panel components are fully theme-driven (see
    Theme Support below).

The character row is a single horizontal strip of selectable character
cells used for gamepad text entry. It supports three modes:

  - LETTERS  — mode-switch key "123", space, A–Z, backspace, ← →
  - NUMBERS  — mode-switch key "!@#", space, 1–9, 0, backspace, ← →
  - SYMBOLS  — mode-switch key "ABC", space, punctuation set, backspace, ← →

The ← and → cells move the text cursor within the search query (not the
char row selection). The backspace cell deletes the character to the left
of the cursor.

Cell widths are computed from the font metrics plus padding. If the
combined width of all cells exceeds the component width (e.g. after
adding many symbols), the per-cell padding is uniformly reduced so that
all characters remain visible without scrolling or clipping.

When focus is on the char row:

  - D-pad / left-stick left/right — move the char row selection cursor
    left or right. Holding the direction triggers auto-scroll repeat
    after an initial delay (500 ms) at a fixed period (100 ms).
  - A button — activate the selected cell: type the character, switch
    mode, trigger backspace, or move the text cursor.
  - X button — immediate backspace. Holding X triggers hold-to-repeat
    after 500 ms at 80 ms intervals (same timing as keyboard held keys).
  - L shoulder / R shoulder — move the text cursor left or right within
    the query string. Holding triggers repeat after 500 ms at 80 ms.
  - D-pad / left-stick down — move focus to the result list (only if
    results are present).
  - B button — close the popup.
  - Start — open the main menu.
  - RT — no-op when already in char row (focus is already here).

When a physical keyboard is in use and focus is on the char row, all
keyboard events are intercepted before the button map is consulted. This
prevents conflicts where a printable key (e.g. 's') is also mapped to a
gamepad button action (e.g. 'x' = backspace). The interception rules:

  - Printable characters — handled via SDL_TEXTINPUT events, which fire
    after SDL_KEYDOWN. Control characters and space are excluded from
    SDL_TEXTINPUT; space is handled directly in the SDLK_SPACE case.
  - Backspace — deletes the character left of the cursor. Holding
    triggers repeat after 500 ms at 80 ms intervals using a software
    timer (mKeyRepeatKey / mKeyRepeatTimer), since SDL filters key.repeat
    for non-printable keys before they reach input().
  - Delete — deletes the character right of the cursor. Same hold-to-
    repeat behaviour as backspace.
  - Left / Right arrow — move the text cursor. Hold-to-repeat applies.
  - Home — jump the text cursor to the start of the query.
  - End — jump the text cursor to the end of the query.
  - Down arrow — move focus to the result list (only if results exist).
  - Escape — close the popup.
  - All other keys — fall through (return false) so SDL_TEXTINPUT fires
    for printable characters.

The help bar switches between keyboard hints ("esc=close") and gamepad
hints immediately whenever the active input device type changes.

Pressing down (gamepad) or the down arrow (keyboard) from the char row
moves focus to the result list. The char row selection highlight dims
and the result list selector becomes visible.

While focus is on the result list:

  - Up / Down — scroll through results. Reaching the top wraps focus
    back to the char row; reaching the bottom also wraps back to the
    char row.
  - A button — launch the selected game.
  - X button — jump to a random result.
  - Y button — toggle the selected game in/out of the current collection
    (not shown in Kid UI mode).
  - Select — open GuiGamelistOptions with a jump-to callback so the
    options dialog can reposition the result list cursor.
  - RT — return focus to the char row. Any SDL_TEXTINPUT events queued
    by the RT keypress are flushed so the triggering character is not
    inserted.
  - B button / Escape — close the popup.
  - Start — open the main menu.
  - L / R shoulder — page through the result list.

Searching runs on a background thread to avoid blocking the render loop.
Each keystroke cancels any in-progress search (sets mCancelFlag), joins
the previous thread, then starts a new std::thread that:

  1. Iterates mAllGames / mLowerNames (built once at popup open).
  2. Checks mCancelFlag after each game so a superseded search exits
     early.
  3. Sorts the matches alphabetically.
  4. Stores results in mPendingResults under mResultMutex and sets
     mResultsReady.

The main thread polls mResultsReady in update() and applies the results
to the TextListComponent on the next frame.

When the result list cursor moves to a game belonging to a different
system than the one currently themed, applyTheme() is called with the
new system's theme. The result list itself is intentionally excluded from
this re-application to avoid flickering the favorite indicator icon on
every cursor movement — its theme is applied exactly once in the
constructor.

The search view ("search") is fully documented in THEMES.md. Supported
elements:

  Layout:
    background    — image, optional, rendered behind everything (z=0)
    searchtext    — text, the query bar (pos/size/font/color; text managed
                    by engine — use ALL ^ TEXT)
    listmessage   — text, status overlay on the result list
    gamelist      — textlist, the result list

  Metadata values:
    md_image, md_thumbnail, md_video, md_marquee, md_name
    md_description, md_rating, md_releasedate, md_developer,
    md_publisher, md_genre, md_players, md_lastplayed, md_playcount

  Metadata labels (text):
    md_lbl_rating, md_lbl_releasedate, md_lbl_developer,
    md_lbl_publisher, md_lbl_genre, md_lbl_players,
    md_lbl_lastplayed, md_lbl_playcount

md_video defaults to invisible (setVisible(false)) — a theme must
explicitly set <visible>true</visible> to enable video playback. All
other metadata components default to off-screen positions so they are
invisible unless the theme positions them.

  - ThemeData: registers "search" in sSupportedViews.
  - HelpComponent: unknown icon names return nullptr silently (no
    LogError) so text-only help prompts (e.g. "esc") render without
    spamming the log. Adds "esc" → button_esc_key.svg icon mapping.
  - IList: adds getCursorIndex() / setCursorIndex(int) accessors.
  - InputManager: SDL2 GameController axis/button events are translated
    to the legacy SDL_JOYSTICK format used by the rest of the input
    pipeline, enabling controllers that are recognised by SDL2's
    GameController API but not the raw joystick API to work correctly.
  - ViewController / SystemView: wires RT to open GuiSearchPopup from
    the game list.
  - Window: passes SDL_TEXTINPUT events through to the top-of-stack GUI
    via the existing textInput() path.
  - GuiGamelistOptions: extended to accept an optional jump-to callback
    and an optional pre-filtered game list for use from search context.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@XenuIsWatching XenuIsWatching marked this pull request as ready for review April 10, 2026 06:25
@XenuIsWatching
Copy link
Copy Markdown
Author

This does currently use "RT" by default, but I'm very open to any other suggestions here (as it appears all buttons are in use)

depends on: RetroPie/RetroPie-Setup#4174 to allow use of the extra buttons from the wizard configurator to be used in emulation station

@cmitu
Copy link
Copy Markdown

cmitu commented Apr 13, 2026

@XenuIsWatching it's an interesting addition for sure.

I have some notes about the implementation, but for now I'm just interested in testing it: do you have a modified ES theme with a 'search' view that I can use ?

Some notes about the implementation (I only tested it with a keyboard):

  • search pop-up shortcut should also close it. Right now closing is done by esc only (for the keyboard input which I tested) and I see that 'b' is used on a gamepad, but I think the same input should be used for closing the search.
  • search should also be available via a menu, at least from a system - where you have the select opening the 'contextual' menu for that system. Unfortunately the 'select' action in the system list context is bound to something else already, so perhaps using the 'start' menu would be better/more consistent. Note that some gamepads may not have an extra button so in this case the search can only accessible through a menu
  • (keyboard related) I see that if a keyboard is detected, it's automatically used for the text input, but that ignores the action mapped keys (i.e. what I have mapped for 'a', 'b', etc.). This poses an issue when the input is not actually a keyboard but a gamepad/joystick that simulates one (like an I-Pac in a certain mode or GPIO mapped keys). Perhaps not starting in text input mode by default and only activating with with some key combo (like Shift + Enter/Ctrl + Enter) may be desired in this case ?

I have other thoughts, but I think the biggest challenge would be (IMHO) how to create a 'search' view for themes that don't implement one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants