From ee54ba970a48183d0db24ebd92eac86a93352483 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Fri, 14 Feb 2020 01:08:02 +0100 Subject: [PATCH] GUI: add custom title format to settings dialog --- rpcs3/Emu/CMakeLists.txt | 1 + rpcs3/Emu/System.cpp | 127 +++--------------------------- rpcs3/Emu/System.h | 3 +- rpcs3/Emu/title.cpp | 104 ++++++++++++++++++++++++ rpcs3/Emu/title.h | 17 ++++ rpcs3/emucore.vcxproj | 2 + rpcs3/emucore.vcxproj.filters | 6 ++ rpcs3/rpcs3qt/emu_settings.h | 4 +- rpcs3/rpcs3qt/game_list_frame.cpp | 2 +- rpcs3/rpcs3qt/gs_frame.cpp | 4 +- rpcs3/rpcs3qt/input_dialog.cpp | 27 ++++--- rpcs3/rpcs3qt/input_dialog.h | 5 +- rpcs3/rpcs3qt/settings_dialog.cpp | 78 +++++++++++++++++- rpcs3/rpcs3qt/settings_dialog.ui | 33 ++++++-- rpcs3/rpcs3qt/tooltips.h | 2 +- 15 files changed, 268 insertions(+), 147 deletions(-) create mode 100644 rpcs3/Emu/title.cpp create mode 100644 rpcs3/Emu/title.h diff --git a/rpcs3/Emu/CMakeLists.txt b/rpcs3/Emu/CMakeLists.txt index 79dd04e0ba..c9faf34fa3 100644 --- a/rpcs3/Emu/CMakeLists.txt +++ b/rpcs3/Emu/CMakeLists.txt @@ -3,6 +3,7 @@ System.cpp VFS.cpp GDB.cpp + title.cpp ) target_link_libraries(rpcs3_emu diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index 15c258433d..831369ca9d 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -16,6 +16,7 @@ #include "Emu/Cell/lv2/sys_prx.h" #include "Emu/Cell/lv2/sys_rsx.h" +#include "Emu/title.h" #include "Emu/IdManager.h" #include "Emu/RSX/GSRender.h" #include "Emu/RSX/Capture/rsx_replay.h" @@ -44,8 +45,6 @@ #include "display_sleep_control.h" -#include "rpcs3_version.h" - #if defined(_WIN32) || defined(HAVE_VULKAN) #include "Emu/RSX/VK/VulkanAPI.h" #endif @@ -1867,124 +1866,16 @@ void Emulator::Stop(bool restart) } } -std::string Emulator::FormatTitle(double fps) const +std::string Emulator::GetFormattedTitle(double fps) const { - // Get version by substringing VersionNumber-buildnumber-commithash to get just the part before the dash - std::string version = rpcs3::get_version().to_string(); - const auto last_minus = version.find_last_of('-'); + rpcs3::title_format_data title_data; + title_data.format = g_cfg.misc.title_format.to_string(); + title_data.title = GetTitle(); + title_data.title_id = GetTitleID(); + title_data.renderer = g_cfg.video.renderer.to_string(); + title_data.fps = fps; - // Add branch and commit hash to version on frame unless it's master. - if (rpcs3::get_branch() != "master"sv && rpcs3::get_branch() != "HEAD"sv) - { - version = version.substr(0, ~last_minus ? last_minus + 9 : last_minus); - version += '-'; - version += rpcs3::get_branch(); - } - else - { - version = version.substr(0, last_minus); - } - - auto [title_format, life1] = g_cfg.misc.title_format.get(); - - // Parse title format string - std::string title_string; - - // Backward compatibility hack - std::size_t fmt_start = 0; - - if (g_cfg.misc.show_fps_in_title.get() == false) - { - if (title_format.starts_with("FPS: %F | ")) - { - // Remove "FPS" from the title if detected - fmt_start = 10; - } - } - - for (std::size_t i = fmt_start; i < title_format.size();) - { - const char c1 = title_format[i]; - - if (c1 == '\0') - { - break; - } - - switch (c1) - { - case '%': - { - const char c2 = title_format[i + 1]; - - if (c2 == '\0') - { - title_string += '%'; - i++; - continue; - } - - switch (c2) - { - case '%': - { - title_string += '%'; - break; - } - case 'T': - { - title_string += this->GetTitle(); - break; - } - case 't': - { - title_string += this->GetTitleID(); - break; - } - case 'R': - { - fmt::append(title_string, "%s", g_cfg.video.renderer.get()); - break; - } - case 'V': - { - title_string += version; - break; - } - case 'F': - { - if (g_cfg.misc.show_fps_in_title) - { - fmt::append(title_string, "%.2f", fps); - } - else - { - title_string += "Disabled"; - } - - break; - } - default: - { - title_string += '%'; - title_string += c2; - break; - } - } - - i += 2; - break; - } - default: - { - title_string += c1; - i += 1; - break; - } - } - } - - return title_string; + return rpcs3::get_formatted_title(title_data); } std::string cfg_root::node_vfs::get(const cfg::string& _cfg, const char* _def) const diff --git a/rpcs3/Emu/System.h b/rpcs3/Emu/System.h index 5208a0035f..f12414c027 100644 --- a/rpcs3/Emu/System.h +++ b/rpcs3/Emu/System.h @@ -378,7 +378,7 @@ public: bool HasGui() const { return m_has_gui; } void SetHasGui(bool has_gui) { m_has_gui = has_gui; } - std::string FormatTitle(double fps) const; + std::string GetFormattedTitle(double fps) const; }; extern Emulator Emu; @@ -622,7 +622,6 @@ struct cfg_root : cfg::node cfg::_bool autoexit{ this, "Exit RPCS3 when process finishes", false, true }; cfg::_bool start_fullscreen{ this, "Start games in fullscreen mode", false, true }; cfg::_bool prevent_display_sleep{ this, "Prevent display sleep while running games", true}; - cfg::_bool show_fps_in_title{ this, "Show FPS counter in window title", true, true }; cfg::_bool show_trophy_popups{ this, "Show trophy popups", true, true }; cfg::_bool show_shader_compilation_hint{ this, "Show shader compilation hint", true, true }; cfg::_bool use_native_interface{ this, "Use native user interface", true }; diff --git a/rpcs3/Emu/title.cpp b/rpcs3/Emu/title.cpp new file mode 100644 index 0000000000..8162aacf4b --- /dev/null +++ b/rpcs3/Emu/title.cpp @@ -0,0 +1,104 @@ +#include "stdafx.h" +#include "title.h" +#include "rpcs3_version.h" + +namespace rpcs3 +{ + std::string get_formatted_title(const title_format_data& title_data) + { + // Get version by substringing VersionNumber-buildnumber-commithash to get just the part before the dash + std::string version = rpcs3::get_version().to_string(); + const auto last_minus = version.find_last_of('-'); + + // Add branch and commit hash to version on frame unless it's master. + if (rpcs3::get_branch() != "master"sv && rpcs3::get_branch() != "HEAD"sv) + { + version = version.substr(0, ~last_minus ? last_minus + 9 : last_minus); + version += '-'; + version += rpcs3::get_branch(); + } + else + { + version = version.substr(0, last_minus); + } + + // Parse title format string + std::string title_string; + + for (std::size_t i = 0; i < title_data.format.size();) + { + const char c1 = title_data.format[i]; + + if (c1 == '\0') + { + break; + } + + switch (c1) + { + case '%': + { + const char c2 = title_data.format[i + 1]; + + if (c2 == '\0') + { + title_string += '%'; + i++; + continue; + } + + switch (c2) + { + case '%': + { + title_string += '%'; + break; + } + case 'T': + { + title_string += title_data.title; + break; + } + case 't': + { + title_string += title_data.title_id; + break; + } + case 'R': + { + fmt::append(title_string, "%s", title_data.renderer); + break; + } + case 'V': + { + title_string += version; + break; + } + case 'F': + { + fmt::append(title_string, "%.2f", title_data.fps); + break; + } + default: + { + title_string += '%'; + title_string += c2; + break; + } + } + + i += 2; + break; + } + default: + { + title_string += c1; + i += 1; + break; + } + } + } + + return title_string; + } +} diff --git a/rpcs3/Emu/title.h b/rpcs3/Emu/title.h new file mode 100644 index 0000000000..b6a3d58ca2 --- /dev/null +++ b/rpcs3/Emu/title.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +namespace rpcs3 +{ + struct title_format_data + { + std::string format; + std::string title; + std::string title_id; + std::string renderer; + double fps = .0; + }; + + std::string get_formatted_title(const title_format_data& title_data); +} diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index 747774e16b..db06f0d317 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -72,6 +72,7 @@ + NotUsing @@ -415,6 +416,7 @@ + diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index 90ec99f6ee..8a8b312a3e 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -881,6 +881,9 @@ Emu\Cell\lv2 + + Emu + @@ -1672,5 +1675,8 @@ Header Files + + Emu + \ No newline at end of file diff --git a/rpcs3/rpcs3qt/emu_settings.h b/rpcs3/rpcs3qt/emu_settings.h index 1edb90d680..2fd5cb6814 100644 --- a/rpcs3/rpcs3qt/emu_settings.h +++ b/rpcs3/rpcs3qt/emu_settings.h @@ -132,11 +132,11 @@ public: StartOnBoot, StartGameFullscreen, PreventDisplaySleep, - ShowFPSInTitle, ShowTrophyPopups, ShowWelcomeScreen, UseNativeInterface, ShowShaderCompilationHint, + WindowTitleFormat, // Network ConnectionStatus, @@ -364,12 +364,12 @@ private: { StartOnBoot, { "Miscellaneous", "Automatically start games after boot" }}, { StartGameFullscreen, { "Miscellaneous", "Start games in fullscreen mode"}}, { PreventDisplaySleep, { "Miscellaneous", "Prevent display sleep while running games"}}, - { ShowFPSInTitle, { "Miscellaneous", "Show FPS counter in window title"}}, { ShowTrophyPopups, { "Miscellaneous", "Show trophy popups"}}, { ShowWelcomeScreen, { "Miscellaneous", "Show Welcome Screen"}}, { UseNativeInterface, { "Miscellaneous", "Use native user interface"}}, { ShowShaderCompilationHint, { "Miscellaneous", "Show shader compilation hint"}}, { SilenceAllLogs, { "Miscellaneous", "Silence All Logs" }}, + { WindowTitleFormat, { "Miscellaneous", "Window Title Format" }}, // Networking { ConnectionStatus, { "Net", "Connection status"}}, diff --git a/rpcs3/rpcs3qt/game_list_frame.cpp b/rpcs3/rpcs3qt/game_list_frame.cpp index 14933f9341..1c5b94d146 100644 --- a/rpcs3/rpcs3qt/game_list_frame.cpp +++ b/rpcs3/rpcs3qt/game_list_frame.cpp @@ -1138,7 +1138,7 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) input_dialog dlg(128, old_title, tr("Rename Title"), tr("%0\n%1\n\nYou can clear the line in order to use the original title.").arg(name).arg(serial), name, this); dlg.move(globalPos); - connect(&dlg, &input_dialog::text_changed, this, [&new_title](const QString& text) + connect(&dlg, &input_dialog::text_changed, [&new_title](const QString& text) { new_title = text.simplified(); }); diff --git a/rpcs3/rpcs3qt/gs_frame.cpp b/rpcs3/rpcs3qt/gs_frame.cpp index 153279a32e..5679ea2311 100644 --- a/rpcs3/rpcs3qt/gs_frame.cpp +++ b/rpcs3/rpcs3qt/gs_frame.cpp @@ -38,7 +38,7 @@ gs_frame::gs_frame(const QRect& geometry, const QIcon& appIcon, const std::share { m_disable_mouse = gui_settings->GetValue(gui::gs_disableMouse).toBool(); - m_window_title = qstr(Emu.FormatTitle(0)); + m_window_title = qstr(Emu.GetFormattedTitle(0)); if (!appIcon.isNull()) { @@ -273,7 +273,7 @@ void gs_frame::flip(draw_context_t, bool /*skip_frame*/) if (fps_t.GetElapsedTimeInSec() >= 0.5) { - QString new_title = qstr(Emu.FormatTitle(m_frames / fps_t.GetElapsedTimeInSec())); + const QString new_title = qstr(Emu.GetFormattedTitle(m_frames / fps_t.GetElapsedTimeInSec())); if (new_title != m_window_title) { diff --git a/rpcs3/rpcs3qt/input_dialog.cpp b/rpcs3/rpcs3qt/input_dialog.cpp index edd590d5cd..41ec1e0eed 100644 --- a/rpcs3/rpcs3qt/input_dialog.cpp +++ b/rpcs3/rpcs3qt/input_dialog.cpp @@ -1,4 +1,4 @@ -#include "input_dialog.h" +#include "input_dialog.h" #include #include @@ -8,14 +8,14 @@ input_dialog::input_dialog(int max_length, const QString& text, const QString& t { setWindowTitle(title); - QLabel* m_label = new QLabel(label); + m_label = new QLabel(label); - QLineEdit* m_input = new QLineEdit(); - m_input->setPlaceholderText(placeholder); - m_input->setText(text); - m_input->setMaxLength(max_length); - m_input->setClearButtonEnabled(true); - connect(m_input, &QLineEdit::textChanged, this, &input_dialog::text_changed); + QLineEdit* input = new QLineEdit(); + input->setPlaceholderText(placeholder); + input->setText(text); + input->setMaxLength(max_length); + input->setClearButtonEnabled(true); + connect(input, &QLineEdit::textChanged, this, &input_dialog::text_changed); QDialogButtonBox* button_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); connect(button_box, &QDialogButtonBox::accepted, this, &QDialog::accept); @@ -23,11 +23,18 @@ input_dialog::input_dialog(int max_length, const QString& text, const QString& t QVBoxLayout* layout = new QVBoxLayout(); layout->addWidget(m_label); - layout->addWidget(m_input); + layout->addWidget(input); layout->addWidget(button_box); setLayout(layout); setFixedHeight(sizeHint().height()); } -input_dialog::~input_dialog(){} +input_dialog::~input_dialog() +{ +} + +void input_dialog::set_label_text(const QString& text) +{ + m_label->setText(text); +} diff --git a/rpcs3/rpcs3qt/input_dialog.h b/rpcs3/rpcs3qt/input_dialog.h index 922d1e9def..c10a91c347 100644 --- a/rpcs3/rpcs3qt/input_dialog.h +++ b/rpcs3/rpcs3qt/input_dialog.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include #include @@ -13,7 +13,10 @@ public: input_dialog(int max_length, const QString& text, const QString& title, const QString& label, const QString& placeholder, QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); ~input_dialog(); + void set_label_text(const QString& text); + private: + QLabel* m_label = nullptr; QString m_text; Q_SIGNALS: diff --git a/rpcs3/rpcs3qt/settings_dialog.cpp b/rpcs3/rpcs3qt/settings_dialog.cpp index 9f7831bc2b..fa84ba2ffc 100644 --- a/rpcs3/rpcs3qt/settings_dialog.cpp +++ b/rpcs3/rpcs3qt/settings_dialog.cpp @@ -17,9 +17,11 @@ #include "settings_dialog.h" #include "ui_settings_dialog.h" #include "tooltips.h" +#include "input_dialog.h" #include "stdafx.h" #include "Emu/System.h" +#include "Emu/title.h" #include "Crypto/unself.h" #include "Utilities/sysinfo.h" @@ -1144,9 +1146,6 @@ settings_dialog::settings_dialog(std::shared_ptr guiSettings, std: SubscribeTooltip(ui->preventDisplaySleep, tooltips.settings.prevent_display_sleep); ui->preventDisplaySleep->setEnabled(display_sleep_control_supported()); - xemu_settings->EnhanceCheckBox(ui->showFPSInTitle, emu_settings::ShowFPSInTitle); - SubscribeTooltip(ui->showFPSInTitle, tooltips.settings.show_fps_in_title); - xemu_settings->EnhanceCheckBox(ui->showTrophyPopups, emu_settings::ShowTrophyPopups); SubscribeTooltip(ui->showTrophyPopups, tooltips.settings.show_trophy_popups); @@ -1288,6 +1287,79 @@ settings_dialog::settings_dialog(std::shared_ptr guiSettings, std: ui->gb_viewport->setVisible(false); } + // Game window title builder + + connect(ui->edit_button_game_window_title_format, &QAbstractButton::clicked, [this, game]() + { + const std::string game_title_format = xemu_settings->GetSetting(emu_settings::WindowTitleFormat); + + auto get_game_window_title_label = [=](const QString& new_format) + { + rpcs3::title_format_data title_data; + title_data.format = sstr(new_format); + title_data.renderer = xemu_settings->GetSetting(emu_settings::Renderer); + title_data.fps = 60.; + + if (game) + { + title_data.title = game->name; + title_data.title_id = game->serial; + } + else + { + title_data.title = sstr(tr("My Game")); + title_data.title_id = "ABCD12345"; + } + + QString game_window_title = qstr(rpcs3::get_formatted_title(title_data)); + + if (game_window_title.isEmpty()) + { + game_window_title = "RPCS3"; + } + + const std::vector> window_title_glossary = + { + { "%F", tr("Framerate") }, + { "%R", tr("Renderer") }, + { "%T", tr("Title") }, + { "%t", tr("Title ID") }, + { "%V", tr("RPCS3 Version") } + }; + + QString glossary; + + for (const auto& [format, description] : window_title_glossary) + { + glossary += format + "\t = " + description + "\n"; + } + + return tr("Glossary:\n\n%0\nPreview:\n\n%1\n").arg(glossary).arg(game_window_title); + }; + + QString edited_format = qstr(game_title_format); + + input_dialog dlg(30, edited_format, tr("Game Window Title Format"), get_game_window_title_label(edited_format), "", this); + dlg.resize(width() * .75, dlg.height()); + + connect(&dlg, &input_dialog::text_changed, [&](const QString& text) + { + edited_format = text.simplified(); + dlg.set_label_text(get_game_window_title_label(edited_format)); + }); + + if (dlg.exec() == QDialog::Accepted) + { + xemu_settings->SetSetting(emu_settings::WindowTitleFormat, sstr(edited_format)); + ui->label_game_window_title_format->setText(qstr(xemu_settings->GetSetting(emu_settings::WindowTitleFormat))); + } + }); + + // Load and apply the configured game window title format + ui->label_game_window_title_format->setText(qstr(xemu_settings->GetSetting(emu_settings::WindowTitleFormat))); + + SubscribeTooltip(ui->gb_game_window_title, tooltips.settings.game_window_title_format); + // _____ _ _ _ _______ _ // / ____|| | | || | |__ __| | | // | | __|| | | || | | | __ _| |__ diff --git a/rpcs3/rpcs3qt/settings_dialog.ui b/rpcs3/rpcs3qt/settings_dialog.ui index 6409fa3e9b..484dbe40a7 100644 --- a/rpcs3/rpcs3qt/settings_dialog.ui +++ b/rpcs3/rpcs3qt/settings_dialog.ui @@ -2052,13 +2052,6 @@ - - - - Show framerate in the window title - - - @@ -2267,6 +2260,32 @@ + + + + Game Window Title + + + + + + FPS: 60 | Renderer | Version | Game [ID] + + + + + + + Reset the title to default + + + Edit + + + + + + diff --git a/rpcs3/rpcs3qt/tooltips.h b/rpcs3/rpcs3qt/tooltips.h index 22e561cc1a..7b5089b562 100644 --- a/rpcs3/rpcs3qt/tooltips.h +++ b/rpcs3/rpcs3qt/tooltips.h @@ -97,7 +97,7 @@ public: const QString start_on_boot = tr("Leave this enabled unless you are a developer."); const QString start_game_fullscreen = tr("Automatically puts the game window in fullscreen.\nDouble click on the game window or press alt+enter to toggle fullscreen and windowed mode."); const QString prevent_display_sleep = tr("Prevent the display from sleeping while a game is running.\nThis requires the org.freedesktop.ScreenSaver D-Bus service on Linux.\nThis option will be disabled if the current platform does not support display sleep control."); - const QString show_fps_in_title = tr("Shows the frame rate in the game window title. May cause buggy or outdated recording software to not notice RPCS3."); + const QString game_window_title_format = tr("Configure the game window title.\nChanging this and/or adding the framerate may cause buggy or outdated recording software to not notice RPCS3."); const QString resize_on_boot = tr("Automatically resizes the game window on boot.\nThis does not change the internal game resolution."); const QString show_trophy_popups = tr("Show trophy pop-ups when a trophy is unlocked."); const QString disable_mouse = tr("Disables the activation of fullscreen mode per double-click while the game screen is active.\nCheck this if you want to play with mouse and keyboard (for example with UCR).");