Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

User Interface: Game searching improvements #13719

Merged
merged 3 commits into from
Apr 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion rpcs3/rpcs3.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,9 @@
<ClCompile Include="QTGeneratedFiles\Debug\moc_game_compatibility.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_game_list.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_game_list_frame.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
Expand Down Expand Up @@ -473,6 +476,9 @@
<ClCompile Include="QTGeneratedFiles\Release\moc_game_compatibility.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_game_list.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_game_list_frame.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile>
Expand Down Expand Up @@ -1396,7 +1402,16 @@
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent"</Command>
</CustomBuild>
<ClInclude Include="rpcs3qt\game_list.h" />
<CustomBuild Include="rpcs3qt\game_list.h">
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing %(Identity)...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWIN32_LEAN_AND_MEAN -DHAVE_VULKAN -DMINIUPNP_STATICLIB -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -DQT_MULTIMEDIA_LIB -DQT_MULTIMEDIAWIDGETS_LIB -DQT_SVG_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\SoundTouch\soundtouch\include" "-I.\..\3rdparty\cubeb\extra" "-I.\..\3rdparty\cubeb\cubeb\include" "-I.\..\3rdparty\flatbuffers\include" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtMultimedia" "-I$(QTDIR)\include\QtMultimediaWidgets" "-I$(QTDIR)\include\QtSvg"</Command>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Moc%27ing %(Identity)...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWIN32_LEAN_AND_MEAN -DHAVE_VULKAN -DMINIUPNP_STATICLIB -DHAVE_SDL2 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -DQT_MULTIMEDIA_LIB -DQT_MULTIMEDIAWIDGETS_LIB -DQT_SVG_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\SoundTouch\soundtouch\include" "-I.\..\3rdparty\cubeb\extra" "-I.\..\3rdparty\cubeb\cubeb\include" "-I.\..\3rdparty\flatbuffers\include" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\libsdl-org\SDL\include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtMultimedia" "-I$(QTDIR)\include\QtMultimediaWidgets" "-I$(QTDIR)\include\QtSvg"</Command>
</CustomBuild>
<ClInclude Include="rpcs3qt\game_list_grid_delegate.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="rpcs3qt\gl_gs_frame.h" />
Expand Down
11 changes: 8 additions & 3 deletions rpcs3/rpcs3.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -941,6 +941,11 @@
</ClCompile>
<ClCompile Include="rpcs3qt\movie_item.cpp">
<Filter>Gui\custom items</Filter>
<ClCompile Include="QTGeneratedFiles\Debug\moc_game_list.cpp">
<Filter>Generated Files\Debug</Filter>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_game_list.cpp">
<Filter>Generated Files\Release</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
Expand Down Expand Up @@ -980,9 +985,6 @@
<ClInclude Include="rpcs3qt\gl_gs_frame.h">
<Filter>Gui\game window</Filter>
</ClInclude>
<ClInclude Include="rpcs3qt\game_list.h">
<Filter>Gui\game list</Filter>
</ClInclude>
<ClInclude Include="rpcs3qt\game_list_grid_delegate.h">
<Filter>Gui\game list</Filter>
</ClInclude>
Expand Down Expand Up @@ -1390,6 +1392,9 @@
<CustomBuild Include="rpcs3qt\system_cmd_dialog.h">
<Filter>Gui\dev tools</Filter>
</CustomBuild>
<CustomBuild Include="rpcs3qt\game_list.h">
<Filter>Gui\game list</Filter>
</CustomBuild>
</ItemGroup>
<ItemGroup>
<Image Include="rpcs3.ico" />
Expand Down
3 changes: 1 addition & 2 deletions rpcs3/rpcs3qt/debugger_frame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@

#include <QKeyEvent>
#include <QScrollBar>
#include <QApplication>
#include <QFontDatabase>
#include <QCompleter>
#include <QVBoxLayout>
Expand Down Expand Up @@ -333,7 +332,7 @@ void debugger_frame::keyPressEvent(QKeyEvent* event)
const u32 pc = (m_debugger_list->m_pc & address_limits);
const u32 selected = (m_debugger_list->m_showing_selected_instruction ? m_debugger_list->m_selected_instruction : cpu->get_pc()) & address_limits;

const auto modifiers = QApplication::keyboardModifiers();
const auto modifiers = event->modifiers();

if (modifiers & Qt::ControlModifier)
{
Expand Down
23 changes: 23 additions & 0 deletions rpcs3/rpcs3qt/game_list.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,19 @@ void game_list::mouseMoveEvent(QMouseEvent *event)
m_last_hover_item = new_item;
}

void game_list::keyPressEvent(QKeyEvent* event)
{
const auto modifiers = event->modifiers();

if (modifiers == Qt::ControlModifier && event->key() == Qt::Key_F && !event->isAutoRepeat())
Megamouse marked this conversation as resolved.
Show resolved Hide resolved
{
Q_EMIT FocusToSearchBar();
return;
Megamouse marked this conversation as resolved.
Show resolved Hide resolved
}

QTableWidget::keyPressEvent(event);
}

void game_list::leaveEvent(QEvent */*event*/)
{
if (m_last_hover_item)
Expand All @@ -45,3 +58,13 @@ void game_list::leaveEvent(QEvent */*event*/)
m_last_hover_item = nullptr;
}
}

void game_list::FocusAndSelectFirstEntryIfNoneIs()
{
if (QTableWidgetItem* item = itemAt(0, 0); item && selectedIndexes().isEmpty())
{
setCurrentItem(item);
}

setFocus();
}
10 changes: 10 additions & 0 deletions rpcs3/rpcs3qt/game_list.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <QTableWidget>
#include <QMouseEvent>
#include <QKeyEvent>
#include <QPixmap>

#include "game_compatibility.h"
Expand Down Expand Up @@ -32,13 +33,22 @@ Q_DECLARE_METATYPE(game_info)
*/
class game_list : public QTableWidget
{
Q_OBJECT

public:
void clear_list(); // Use this instead of clearContents

public Q_SLOTS:
void FocusAndSelectFirstEntryIfNoneIs();

Q_SIGNALS:
void FocusToSearchBar();

protected:
movie_item* m_last_hover_item = nullptr;

void mousePressEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
void keyPressEvent(QKeyEvent *event) override;
void leaveEvent(QEvent *event) override;
};
85 changes: 79 additions & 6 deletions rpcs3/rpcs3qt/game_list_frame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,9 @@ game_list_frame::game_list_frame(std::shared_ptr<gui_settings> gui_settings, std
QMessageBox::warning(this, tr("Warning!"), tr("Failed to retrieve the online compatibility database!\nFalling back to local database.\n\n%0").arg(error));
});

connect(m_game_list, &game_list::FocusToSearchBar, this, &game_list_frame::FocusToSearchBar);
connect(m_game_grid, &game_list::FocusToSearchBar, this, &game_list_frame::FocusToSearchBar);

for (int col = 0; col < m_columnActs.count(); ++col)
{
m_columnActs[col]->setCheckable(true);
Expand Down Expand Up @@ -332,7 +335,7 @@ void game_list_frame::OnColClicked(int col)
}

// Get visibility of entries
bool game_list_frame::IsEntryVisible(const game_info& game)
bool game_list_frame::IsEntryVisible(const game_info& game, bool search_fallback)
{
const auto matches_category = [&]()
{
Expand All @@ -346,7 +349,7 @@ bool game_list_frame::IsEntryVisible(const game_info& game)

const QString serial = qstr(game->info.serial);
const bool is_visible = m_show_hidden || !m_hidden_list.contains(serial);
return is_visible && matches_category() && SearchMatchesApp(qstr(game->info.name), serial);
return is_visible && matches_category() && SearchMatchesApp(qstr(game->info.name), serial, search_fallback);
}

void game_list_frame::SortGameList() const
Expand Down Expand Up @@ -2393,6 +2396,7 @@ void game_list_frame::RepaintIcons(const bool& from_settings)
connect(m_game_grid, &QTableWidget::customContextMenuRequested, this, &game_list_frame::ShowContextMenu);
connect(m_game_grid, &QTableWidget::itemSelectionChanged, this, &game_list_frame::ItemSelectionChangedSlot);
connect(m_game_grid, &QTableWidget::itemDoubleClicked, this, &game_list_frame::doubleClickedSlot);
connect(m_game_grid, &game_list::FocusToSearchBar, this, &game_list_frame::FocusToSearchBar);
m_central_widget->addWidget(m_game_grid);
m_central_widget->setCurrentWidget(m_game_grid);
m_game_grid->verticalScrollBar()->setValue(scroll_position);
Expand Down Expand Up @@ -2422,6 +2426,28 @@ void game_list_frame::SetSearchText(const QString& text)
Refresh();
}

void game_list_frame::FocusAndSelectFirstEntryIfNoneIs()
{
if (m_is_list_layout)
{
if (!m_game_list)
{
return;
}

m_game_list->FocusAndSelectFirstEntryIfNoneIs();
}
else
{
if (!m_game_grid)
{
return;
}

m_game_grid->FocusAndSelectFirstEntryIfNoneIs();
}
}

void game_list_frame::closeEvent(QCloseEvent *event)
{
QDockWidget::closeEvent(event);
Expand Down Expand Up @@ -2522,11 +2548,15 @@ void game_list_frame::PopulateGameList()

int row = 0;
int index = -1;

// Fallback is not needed when at least one entry is visible
const bool use_search_fallback = std::none_of(m_game_data.begin(), m_game_data.end(), [this](auto& game){ return IsEntryVisible(game); });
Megamouse marked this conversation as resolved.
Show resolved Hide resolved

for (const auto& game : m_game_data)
{
index++;

if (!IsEntryVisible(game))
if (!IsEntryVisible(game, use_search_fallback))
{
game->item = nullptr;
continue;
Expand Down Expand Up @@ -2726,6 +2756,18 @@ void game_list_frame::PopulateGameGrid(int maxCols, const QSize& image_size, con
}
}

// Fallback is not needed when at least one entry is visible
if (matching_apps.isEmpty())
{
for (const auto& app : m_game_data)
{
if (IsEntryVisible(app, true))
{
matching_apps.push_back(app);
}
}
}

const int entries = matching_apps.count();

// Edge cases!
Expand Down Expand Up @@ -2798,12 +2840,43 @@ void game_list_frame::PopulateGameGrid(int maxCols, const QSize& image_size, con
/**
* Returns false if the game should be hidden because it doesn't match search term in toolbar.
*/
bool game_list_frame::SearchMatchesApp(const QString& name, const QString& serial) const
bool game_list_frame::SearchMatchesApp(const QString& name, const QString& serial, bool fallback) const
{
if (!m_search_text.isEmpty())
{
const QString search_text = m_search_text.toLower();
return m_titles.value(serial, name).toLower().contains(search_text) || serial.toLower().contains(search_text);
QString search_text = m_search_text.toLower();
QString title_name = m_titles.value(serial, name).toLower();

// Ignore trademarks when no search results have been yielded by unmodified search
static const QRegularExpression s_ignored_on_fallback(reinterpret_cast<const char*>(u8"[:\\-®©™]+"));

if (fallback)
{
search_text = search_text.simplified();
title_name = title_name.simplified();

QString title_name_replaced_trademarks_with_spaces = title_name;
QString title_name_simplified = title_name;

search_text.remove(s_ignored_on_fallback);
title_name.remove(s_ignored_on_fallback);
title_name_replaced_trademarks_with_spaces.replace(s_ignored_on_fallback, " ");

// Before simplify to allow spaces in the beginning and end where ignored characters may have been
if (title_name_replaced_trademarks_with_spaces.contains(search_text))
{
return true;
}

title_name_replaced_trademarks_with_spaces = title_name_replaced_trademarks_with_spaces.simplified();

if (title_name_replaced_trademarks_with_spaces.contains(search_text))
{
return true;
}
}

return title_name.contains(search_text) || serial.toLower().contains(search_text);
}
return true;
}
Expand Down
6 changes: 4 additions & 2 deletions rpcs3/rpcs3qt/game_list_frame.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ public Q_SLOTS:
void SetShowCompatibilityInGrid(bool show);
void SetShowCustomIcons(bool show);
void SetPlayHoverGifs(bool play);
void FocusAndSelectFirstEntryIfNoneIs();

private Q_SLOTS:
void OnRefreshFinished();
Expand All @@ -93,6 +94,7 @@ private Q_SLOTS:
void NotifyEmuSettingsChange();
void IconReady(movie_item* item);
void SizeOnDiskReady(const game_info& game);
void FocusToSearchBar();
protected:
/** Override inherited method from Qt to allow signalling when close happened.*/
void closeEvent(QCloseEvent* event) override;
Expand All @@ -108,9 +110,9 @@ private Q_SLOTS:
void ShowCustomConfigIcon(const game_info& game);
void PopulateGameList();
void PopulateGameGrid(int maxCols, const QSize& image_size, const QColor& image_color);
bool IsEntryVisible(const game_info& game);
bool IsEntryVisible(const game_info& game, bool search_fallback = false);
void SortGameList() const;
bool SearchMatchesApp(const QString& name, const QString& serial) const;
bool SearchMatchesApp(const QString& name, const QString& serial, bool fallback = false) const;

bool RemoveCustomConfiguration(const std::string& title_id, const game_info& game = nullptr, bool is_interactive = false);
bool RemoveCustomPadConfiguration(const std::string& title_id, const game_info& game = nullptr, bool is_interactive = false);
Expand Down
5 changes: 5 additions & 0 deletions rpcs3/rpcs3qt/main_window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,9 @@ bool main_window::Init([[maybe_unused]] bool with_cli_boot)
// Disable vsh if not present.
ui->bootVSHAct->setEnabled(fs::is_file(g_cfg_vfs.get_dev_flash() + "vsh/module/vsh.self"));

// Focus to search bar by default
ui->mw_searchbar->setFocus();
Megamouse marked this conversation as resolved.
Show resolved Hide resolved

return true;
}

Expand Down Expand Up @@ -2784,6 +2787,8 @@ void main_window::CreateConnects()
});

connect(ui->mw_searchbar, &QLineEdit::textChanged, m_game_list_frame, &game_list_frame::SetSearchText);
connect(ui->mw_searchbar, &QLineEdit::returnPressed, m_game_list_frame, &game_list_frame::FocusAndSelectFirstEntryIfNoneIs);
connect(m_game_list_frame, &game_list_frame::FocusToSearchBar, this, [this]() { ui->mw_searchbar->setFocus(); });
}

void main_window::CreateDockWindows()
Expand Down