mirror of
https://git.citron-emu.org/citron/emulator
synced 2025-12-19 18:53:32 +00:00
Merge pull request 'refactor: Exit Fullscreen & Mouse Panning Hotkeys to GRenderWindow' (#67) from refactor/hotkeys into main
Reviewed-on: https://git.citron-emu.org/Citron/Emulator/pulls/67
This commit is contained in:
@@ -284,13 +284,14 @@ struct NullRenderWidget : public RenderWidget {
|
|||||||
|
|
||||||
GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_,
|
GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_,
|
||||||
std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_,
|
std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_,
|
||||||
Core::System& system_)
|
Core::System& system_, HotkeyRegistry& hotkey_registry_)
|
||||||
: QWidget(parent),
|
: QWidget(parent),
|
||||||
emu_thread(emu_thread_), input_subsystem{std::move(input_subsystem_)}, system{system_} {
|
emu_thread(emu_thread_), input_subsystem{std::move(input_subsystem_)}, system{system_},
|
||||||
|
hotkey_registry{hotkey_registry_} {
|
||||||
setWindowTitle(QStringLiteral("citron %1 | %2-%3")
|
setWindowTitle(QStringLiteral("citron %1 | %2-%3")
|
||||||
.arg(QString::fromUtf8(Common::g_build_name),
|
.arg(QString::fromUtf8(Common::g_build_name),
|
||||||
QString::fromUtf8(Common::g_scm_branch),
|
QString::fromUtf8(Common::g_scm_branch),
|
||||||
QString::fromUtf8(Common::g_scm_desc)));
|
QString::fromUtf8(Common::g_scm_desc)));
|
||||||
setAttribute(Qt::WA_AcceptTouchEvents);
|
setAttribute(Qt::WA_AcceptTouchEvents);
|
||||||
auto* layout = new QHBoxLayout(this);
|
auto* layout = new QHBoxLayout(this);
|
||||||
layout->setContentsMargins(0, 0, 0, 0);
|
layout->setContentsMargins(0, 0, 0, 0);
|
||||||
@@ -299,7 +300,7 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_,
|
|||||||
this->setMouseTracking(true);
|
this->setMouseTracking(true);
|
||||||
|
|
||||||
strict_context_required = QGuiApplication::platformName() == QStringLiteral("wayland") ||
|
strict_context_required = QGuiApplication::platformName() == QStringLiteral("wayland") ||
|
||||||
QGuiApplication::platformName() == QStringLiteral("wayland-egl");
|
QGuiApplication::platformName() == QStringLiteral("wayland-egl");
|
||||||
|
|
||||||
connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete);
|
connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete);
|
||||||
connect(this, &GRenderWindow::ExecuteProgramSignal, parent, &GMainWindow::OnExecuteProgram,
|
connect(this, &GRenderWindow::ExecuteProgramSignal, parent, &GMainWindow::OnExecuteProgram,
|
||||||
@@ -310,11 +311,13 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_,
|
|||||||
mouse_constrain_timer.setInterval(default_mouse_constrain_timeout);
|
mouse_constrain_timer.setInterval(default_mouse_constrain_timeout);
|
||||||
connect(&mouse_constrain_timer, &QTimer::timeout, this, &GRenderWindow::ConstrainMouse);
|
connect(&mouse_constrain_timer, &QTimer::timeout, this, &GRenderWindow::ConstrainMouse);
|
||||||
|
|
||||||
// mouse-hiding logic for Wayland
|
|
||||||
constexpr int default_mouse_hide_timeout = 2500; // 2.5 seconds
|
constexpr int default_mouse_hide_timeout = 2500; // 2.5 seconds
|
||||||
mouse_hide_timer.setInterval(default_mouse_hide_timeout);
|
mouse_hide_timer.setInterval(default_mouse_hide_timeout);
|
||||||
mouse_hide_timer.setSingleShot(true); // The timer fires only once per start()
|
mouse_hide_timer.setSingleShot(true); // The timer fires only once per start()
|
||||||
connect(&mouse_hide_timer, &QTimer::timeout, this, &GRenderWindow::HideMouseCursor);
|
connect(&mouse_hide_timer, &QTimer::timeout, this, &GRenderWindow::HideMouseCursor);
|
||||||
|
connect(this, &GRenderWindow::PanningToggleHotkeyPressed, this, [this]() {
|
||||||
|
SetMousePanningState(!Settings::values.mouse_panning.GetValue());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void GRenderWindow::ExecuteProgram(std::size_t program_index) {
|
void GRenderWindow::ExecuteProgram(std::size_t program_index) {
|
||||||
@@ -599,22 +602,31 @@ int GRenderWindow::QtModifierToSwitchModifier(Qt::KeyboardModifiers qt_modifiers
|
|||||||
}
|
}
|
||||||
|
|
||||||
void GRenderWindow::keyPressEvent(QKeyEvent* event) {
|
void GRenderWindow::keyPressEvent(QKeyEvent* event) {
|
||||||
/**
|
if (event->isAutoRepeat()) {
|
||||||
* This feature can be enhanced with the following functions, but they do not provide
|
return; // Ignore auto-repeated key presses
|
||||||
* cross-platform behavior.
|
|
||||||
*
|
|
||||||
* event->nativeVirtualKey() can distinguish between keys on the numpad.
|
|
||||||
* event->nativeModifiers() can distinguish between left and right keys and numlock,
|
|
||||||
* capslock, scroll lock.
|
|
||||||
*/
|
|
||||||
if (!event->isAutoRepeat()) {
|
|
||||||
const auto modifier = QtModifierToSwitchModifier(event->modifiers());
|
|
||||||
const auto key = QtKeyToSwitchKey(Qt::Key(event->key()));
|
|
||||||
input_subsystem->GetKeyboard()->SetKeyboardModifiers(modifier);
|
|
||||||
input_subsystem->GetKeyboard()->PressKeyboardKey(key);
|
|
||||||
// This is used for gamepads that can have any key mapped
|
|
||||||
input_subsystem->GetKeyboard()->PressKey(event->key());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QKeySequence key_sequence(event->modifiers() | event->key());
|
||||||
|
static const std::string main_window_id = "Main Window";
|
||||||
|
|
||||||
|
if (key_sequence == hotkey_registry.GetKeySequence(main_window_id, "Toggle Mouse Panning")) {
|
||||||
|
emit PanningToggleHotkeyPressed(); // Signal the main window
|
||||||
|
event->accept(); // Consume the event
|
||||||
|
return; // Stop processing
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key_sequence == hotkey_registry.GetKeySequence(main_window_id, "Exit Fullscreen")) {
|
||||||
|
emit FullscreenExitHotkeyPressed(); // Signal the main window
|
||||||
|
event->accept(); // Consume the event
|
||||||
|
return; // Stop processing
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- If not a critical hotkey, pass to game as normal ---
|
||||||
|
const auto modifier = QtModifierToSwitchModifier(event->modifiers());
|
||||||
|
const auto key = QtKeyToSwitchKey(static_cast<Qt::Key>(event->key()));
|
||||||
|
input_subsystem->GetKeyboard()->SetKeyboardModifiers(modifier);
|
||||||
|
input_subsystem->GetKeyboard()->PressKeyboardKey(key);
|
||||||
|
input_subsystem->GetKeyboard()->PressKey(event->key());
|
||||||
}
|
}
|
||||||
|
|
||||||
void GRenderWindow::keyReleaseEvent(QKeyEvent* event) {
|
void GRenderWindow::keyReleaseEvent(QKeyEvent* event) {
|
||||||
@@ -1173,15 +1185,19 @@ void GRenderWindow::showEvent(QShowEvent* event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool GRenderWindow::eventFilter(QObject* object, QEvent* event) {
|
bool GRenderWindow::eventFilter(QObject* object, QEvent* event) {
|
||||||
|
// Only handle HoverMove for panning.
|
||||||
if (event->type() == QEvent::HoverMove) {
|
if (event->type() == QEvent::HoverMove) {
|
||||||
if (Settings::values.mouse_panning || Settings::values.mouse_enabled) {
|
if (Settings::values.mouse_panning.GetValue() || Settings::values.mouse_enabled.GetValue()) {
|
||||||
auto* hover_event = static_cast<QMouseEvent*>(event);
|
auto* hover_event = static_cast<QMouseEvent*>(event);
|
||||||
mouseMoveEvent(hover_event);
|
mouseMoveEvent(hover_event);
|
||||||
return false;
|
// Consume the hover event to prevent it from being processed twice.
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
emit MouseActivity();
|
emit MouseActivity();
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
|
// Pass on all other events for default processing.
|
||||||
|
return QWidget::eventFilter(object, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GRenderWindow::HideMouseCursor() {
|
void GRenderWindow::HideMouseCursor() {
|
||||||
@@ -1189,3 +1205,15 @@ void GRenderWindow::HideMouseCursor() {
|
|||||||
QApplication::setOverrideCursor(QCursor(Qt::BlankCursor));
|
QApplication::setOverrideCursor(QCursor(Qt::BlankCursor));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GRenderWindow::SetMousePanningState(bool enabled) {
|
||||||
|
Settings::values.mouse_panning = enabled;
|
||||||
|
if (enabled) {
|
||||||
|
installEventFilter(this);
|
||||||
|
setAttribute(Qt::WA_Hover, true);
|
||||||
|
} else {
|
||||||
|
QApplication::restoreOverrideCursor();
|
||||||
|
removeEventFilter(this);
|
||||||
|
setAttribute(Qt::WA_Hover, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -30,8 +30,10 @@
|
|||||||
#include "common/polyfill_thread.h"
|
#include "common/polyfill_thread.h"
|
||||||
#include "common/thread.h"
|
#include "common/thread.h"
|
||||||
#include "core/frontend/emu_window.h"
|
#include "core/frontend/emu_window.h"
|
||||||
|
#include "citron/hotkeys.h"
|
||||||
|
|
||||||
class GMainWindow;
|
class GMainWindow;
|
||||||
|
class HotkeyRegistry;
|
||||||
class QCamera;
|
class QCamera;
|
||||||
class QCameraImageCapture;
|
class QCameraImageCapture;
|
||||||
class QCloseEvent;
|
class QCloseEvent;
|
||||||
@@ -150,7 +152,7 @@ class GRenderWindow : public QWidget, public Core::Frontend::EmuWindow {
|
|||||||
public:
|
public:
|
||||||
explicit GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_,
|
explicit GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_,
|
||||||
std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_,
|
std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_,
|
||||||
Core::System& system_);
|
Core::System& system_, HotkeyRegistry& hotkey_registry_);
|
||||||
~GRenderWindow() override;
|
~GRenderWindow() override;
|
||||||
|
|
||||||
// EmuWindow implementation.
|
// EmuWindow implementation.
|
||||||
@@ -197,6 +199,7 @@ public:
|
|||||||
void focusOutEvent(QFocusEvent* event) override;
|
void focusOutEvent(QFocusEvent* event) override;
|
||||||
|
|
||||||
bool InitRenderTarget();
|
bool InitRenderTarget();
|
||||||
|
void SetMousePanningState(bool enabled); // <<< ADDED
|
||||||
|
|
||||||
/// Destroy the previous run's child_widget which should also destroy the child_window
|
/// Destroy the previous run's child_widget which should also destroy the child_window
|
||||||
void ReleaseRenderTarget();
|
void ReleaseRenderTarget();
|
||||||
@@ -227,6 +230,8 @@ signals:
|
|||||||
void ExitSignal();
|
void ExitSignal();
|
||||||
void MouseActivity();
|
void MouseActivity();
|
||||||
void TasPlaybackStateChanged();
|
void TasPlaybackStateChanged();
|
||||||
|
void PanningToggleHotkeyPressed();
|
||||||
|
void FullscreenExitHotkeyPressed();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void HideMouseCursor();
|
void HideMouseCursor();
|
||||||
@@ -265,6 +270,7 @@ private:
|
|||||||
|
|
||||||
bool first_frame = false;
|
bool first_frame = false;
|
||||||
InputCommon::TasInput::TasState last_tas_state;
|
InputCommon::TasInput::TasState last_tas_state;
|
||||||
|
bool strict_context_required;
|
||||||
|
|
||||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && CITRON_USE_QT_MULTIMEDIA
|
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && CITRON_USE_QT_MULTIMEDIA
|
||||||
bool is_virtual_camera;
|
bool is_virtual_camera;
|
||||||
@@ -279,6 +285,7 @@ private:
|
|||||||
QTimer mouse_hide_timer;
|
QTimer mouse_hide_timer;
|
||||||
|
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
|
HotkeyRegistry& hotkey_registry;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void showEvent(QShowEvent* event) override;
|
void showEvent(QShowEvent* event) override;
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
|
// SPDX-FileCopyrightText: 2025 citron Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <QCloseEvent>
|
#include <QCloseEvent>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
|
||||||
|
#include "citron/main.h"
|
||||||
|
#include "citron/bootmanager.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "ui_configure_mouse_panning.h"
|
#include "ui_configure_mouse_panning.h"
|
||||||
#include "citron/configuration/configure_mouse_panning.h"
|
#include "citron/configuration/configure_mouse_panning.h"
|
||||||
@@ -69,7 +72,14 @@ void ConfigureMousePanning::ConnectEvents() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ConfigureMousePanning::ApplyConfiguration() {
|
void ConfigureMousePanning::ApplyConfiguration() {
|
||||||
Settings::values.mouse_panning = ui->enable->isChecked();
|
GMainWindow* main_window = qobject_cast<GMainWindow*>(this->parent());
|
||||||
|
|
||||||
|
if (main_window) {
|
||||||
|
main_window->GetRenderWindow()->SetMousePanningState(ui->enable->isChecked());
|
||||||
|
} else {
|
||||||
|
Settings::values.mouse_panning = ui->enable->isChecked();
|
||||||
|
}
|
||||||
|
|
||||||
Settings::values.mouse_panning_x_sensitivity = static_cast<float>(ui->x_sensitivity->value());
|
Settings::values.mouse_panning_x_sensitivity = static_cast<float>(ui->x_sensitivity->value());
|
||||||
Settings::values.mouse_panning_y_sensitivity = static_cast<float>(ui->y_sensitivity->value());
|
Settings::values.mouse_panning_y_sensitivity = static_cast<float>(ui->y_sensitivity->value());
|
||||||
Settings::values.mouse_panning_deadzone_counterweight =
|
Settings::values.mouse_panning_deadzone_counterweight =
|
||||||
@@ -78,7 +88,11 @@ void ConfigureMousePanning::ApplyConfiguration() {
|
|||||||
Settings::values.mouse_panning_min_decay = static_cast<float>(ui->min_decay->value());
|
Settings::values.mouse_panning_min_decay = static_cast<float>(ui->min_decay->value());
|
||||||
|
|
||||||
if (Settings::values.mouse_enabled && Settings::values.mouse_panning) {
|
if (Settings::values.mouse_enabled && Settings::values.mouse_panning) {
|
||||||
Settings::values.mouse_panning = false;
|
if (main_window) {
|
||||||
|
main_window->GetRenderWindow()->SetMousePanningState(false);
|
||||||
|
} else {
|
||||||
|
Settings::values.mouse_panning = false;
|
||||||
|
}
|
||||||
QMessageBox::critical(
|
QMessageBox::critical(
|
||||||
this, tr("Emulated mouse is enabled"),
|
this, tr("Emulated mouse is enabled"),
|
||||||
tr("Real mouse input and mouse panning are incompatible. Please disable the "
|
tr("Real mouse input and mouse panning are incompatible. Please disable the "
|
||||||
|
|||||||
@@ -1079,7 +1079,7 @@ void GMainWindow::InitializeWidgets() {
|
|||||||
#ifdef CITRON_ENABLE_COMPATIBILITY_REPORTING
|
#ifdef CITRON_ENABLE_COMPATIBILITY_REPORTING
|
||||||
ui->action_Report_Compatibility->setVisible(true);
|
ui->action_Report_Compatibility->setVisible(true);
|
||||||
#endif
|
#endif
|
||||||
render_window = new GRenderWindow(this, emu_thread.get(), input_subsystem, *system);
|
render_window = new GRenderWindow(this, emu_thread.get(), input_subsystem, *system, hotkey_registry);
|
||||||
render_window->hide();
|
render_window->hide();
|
||||||
|
|
||||||
game_list = new GameList(vfs, provider.get(), *play_time_manager, *system, this);
|
game_list = new GameList(vfs, provider.get(), *play_time_manager, *system, this);
|
||||||
@@ -1106,7 +1106,6 @@ void GMainWindow::InitializeWidgets() {
|
|||||||
|
|
||||||
// Create status bar
|
// Create status bar
|
||||||
message_label = new QLabel();
|
message_label = new QLabel();
|
||||||
// Configured separately for left alignment
|
|
||||||
message_label->setFrameStyle(QFrame::NoFrame);
|
message_label->setFrameStyle(QFrame::NoFrame);
|
||||||
message_label->setContentsMargins(4, 0, 4, 0);
|
message_label->setContentsMargins(4, 0, 4, 0);
|
||||||
message_label->setAlignment(Qt::AlignLeft);
|
message_label->setAlignment(Qt::AlignLeft);
|
||||||
@@ -1145,7 +1144,6 @@ void GMainWindow::InitializeWidgets() {
|
|||||||
statusBar()->addPermanentWidget(multiplayer_state->GetStatusText(), 0);
|
statusBar()->addPermanentWidget(multiplayer_state->GetStatusText(), 0);
|
||||||
statusBar()->addPermanentWidget(multiplayer_state->GetStatusIcon(), 0);
|
statusBar()->addPermanentWidget(multiplayer_state->GetStatusIcon(), 0);
|
||||||
|
|
||||||
// Create performance overlay
|
|
||||||
performance_overlay = new PerformanceOverlay(this);
|
performance_overlay = new PerformanceOverlay(this);
|
||||||
performance_overlay->hide();
|
performance_overlay->hide();
|
||||||
|
|
||||||
@@ -1215,7 +1213,6 @@ void GMainWindow::InitializeWidgets() {
|
|||||||
|
|
||||||
statusBar()->insertPermanentWidget(0, volume_button);
|
statusBar()->insertPermanentWidget(0, volume_button);
|
||||||
|
|
||||||
// setup AA button
|
|
||||||
aa_status_button = new QPushButton();
|
aa_status_button = new QPushButton();
|
||||||
aa_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton"));
|
aa_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton"));
|
||||||
aa_status_button->setFocusPolicy(Qt::NoFocus);
|
aa_status_button->setFocusPolicy(Qt::NoFocus);
|
||||||
@@ -1247,7 +1244,6 @@ void GMainWindow::InitializeWidgets() {
|
|||||||
});
|
});
|
||||||
statusBar()->insertPermanentWidget(0, aa_status_button);
|
statusBar()->insertPermanentWidget(0, aa_status_button);
|
||||||
|
|
||||||
// Setup Filter button
|
|
||||||
filter_status_button = new QPushButton();
|
filter_status_button = new QPushButton();
|
||||||
filter_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton"));
|
filter_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton"));
|
||||||
filter_status_button->setFocusPolicy(Qt::NoFocus);
|
filter_status_button->setFocusPolicy(Qt::NoFocus);
|
||||||
@@ -1271,7 +1267,6 @@ void GMainWindow::InitializeWidgets() {
|
|||||||
});
|
});
|
||||||
statusBar()->insertPermanentWidget(0, filter_status_button);
|
statusBar()->insertPermanentWidget(0, filter_status_button);
|
||||||
|
|
||||||
// Setup Dock button
|
|
||||||
dock_status_button = new QPushButton();
|
dock_status_button = new QPushButton();
|
||||||
dock_status_button->setObjectName(QStringLiteral("DockingStatusBarButton"));
|
dock_status_button->setObjectName(QStringLiteral("DockingStatusBarButton"));
|
||||||
dock_status_button->setFocusPolicy(Qt::NoFocus);
|
dock_status_button->setFocusPolicy(Qt::NoFocus);
|
||||||
@@ -1282,7 +1277,6 @@ void GMainWindow::InitializeWidgets() {
|
|||||||
connect(dock_status_button, &QPushButton::customContextMenuRequested,
|
connect(dock_status_button, &QPushButton::customContextMenuRequested,
|
||||||
[this](const QPoint& menu_location) {
|
[this](const QPoint& menu_location) {
|
||||||
QMenu context_menu;
|
QMenu context_menu;
|
||||||
|
|
||||||
for (auto const& pair : ConfigurationShared::use_docked_mode_texts_map) {
|
for (auto const& pair : ConfigurationShared::use_docked_mode_texts_map) {
|
||||||
context_menu.addAction(pair.second, [this, &pair] {
|
context_menu.addAction(pair.second, [this, &pair] {
|
||||||
if (pair.first != Settings::values.use_docked_mode.GetValue()) {
|
if (pair.first != Settings::values.use_docked_mode.GetValue()) {
|
||||||
@@ -1295,7 +1289,6 @@ void GMainWindow::InitializeWidgets() {
|
|||||||
});
|
});
|
||||||
statusBar()->insertPermanentWidget(0, dock_status_button);
|
statusBar()->insertPermanentWidget(0, dock_status_button);
|
||||||
|
|
||||||
// Setup GPU Accuracy button
|
|
||||||
gpu_accuracy_button = new QPushButton();
|
gpu_accuracy_button = new QPushButton();
|
||||||
gpu_accuracy_button->setObjectName(QStringLiteral("GPUStatusBarButton"));
|
gpu_accuracy_button->setObjectName(QStringLiteral("GPUStatusBarButton"));
|
||||||
gpu_accuracy_button->setCheckable(true);
|
gpu_accuracy_button->setCheckable(true);
|
||||||
@@ -1306,7 +1299,6 @@ void GMainWindow::InitializeWidgets() {
|
|||||||
connect(gpu_accuracy_button, &QPushButton::customContextMenuRequested,
|
connect(gpu_accuracy_button, &QPushButton::customContextMenuRequested,
|
||||||
[this](const QPoint& menu_location) {
|
[this](const QPoint& menu_location) {
|
||||||
QMenu context_menu;
|
QMenu context_menu;
|
||||||
|
|
||||||
for (auto const& gpu_accuracy_pair : ConfigurationShared::gpu_accuracy_texts_map) {
|
for (auto const& gpu_accuracy_pair : ConfigurationShared::gpu_accuracy_texts_map) {
|
||||||
if (gpu_accuracy_pair.first == Settings::GpuAccuracy::Extreme) {
|
if (gpu_accuracy_pair.first == Settings::GpuAccuracy::Extreme) {
|
||||||
continue;
|
continue;
|
||||||
@@ -1321,7 +1313,6 @@ void GMainWindow::InitializeWidgets() {
|
|||||||
});
|
});
|
||||||
statusBar()->insertPermanentWidget(0, gpu_accuracy_button);
|
statusBar()->insertPermanentWidget(0, gpu_accuracy_button);
|
||||||
|
|
||||||
// Setup Renderer API button
|
|
||||||
renderer_status_button = new QPushButton();
|
renderer_status_button = new QPushButton();
|
||||||
renderer_status_button->setObjectName(QStringLiteral("RendererStatusBarButton"));
|
renderer_status_button->setObjectName(QStringLiteral("RendererStatusBarButton"));
|
||||||
renderer_status_button->setCheckable(true);
|
renderer_status_button->setCheckable(true);
|
||||||
@@ -1335,7 +1326,6 @@ void GMainWindow::InitializeWidgets() {
|
|||||||
connect(renderer_status_button, &QPushButton::customContextMenuRequested,
|
connect(renderer_status_button, &QPushButton::customContextMenuRequested,
|
||||||
[this](const QPoint& menu_location) {
|
[this](const QPoint& menu_location) {
|
||||||
QMenu context_menu;
|
QMenu context_menu;
|
||||||
|
|
||||||
for (auto const& renderer_backend_pair :
|
for (auto const& renderer_backend_pair :
|
||||||
ConfigurationShared::renderer_backend_texts_map) {
|
ConfigurationShared::renderer_backend_texts_map) {
|
||||||
if (renderer_backend_pair.first == Settings::RendererBackend::Null) {
|
if (renderer_backend_pair.first == Settings::RendererBackend::Null) {
|
||||||
@@ -1428,6 +1418,7 @@ void GMainWindow::LinkActionShortcut(QAction* action, const QString& action_name
|
|||||||
void GMainWindow::InitializeHotkeys() {
|
void GMainWindow::InitializeHotkeys() {
|
||||||
hotkey_registry.LoadHotkeys();
|
hotkey_registry.LoadHotkeys();
|
||||||
|
|
||||||
|
// Link all standard menu actions
|
||||||
LinkActionShortcut(ui->action_Load_File, QStringLiteral("Load File"));
|
LinkActionShortcut(ui->action_Load_File, QStringLiteral("Load File"));
|
||||||
LinkActionShortcut(ui->action_Load_Amiibo, QStringLiteral("Load/Remove Amiibo"));
|
LinkActionShortcut(ui->action_Load_Amiibo, QStringLiteral("Load/Remove Amiibo"));
|
||||||
LinkActionShortcut(ui->action_Exit, QStringLiteral("Exit citron"));
|
LinkActionShortcut(ui->action_Exit, QStringLiteral("Exit citron"));
|
||||||
@@ -1446,42 +1437,30 @@ void GMainWindow::InitializeHotkeys() {
|
|||||||
LinkActionShortcut(ui->action_TAS_Start, QStringLiteral("TAS Start/Stop"), true);
|
LinkActionShortcut(ui->action_TAS_Start, QStringLiteral("TAS Start/Stop"), true);
|
||||||
LinkActionShortcut(ui->action_TAS_Record, QStringLiteral("TAS Record"), true);
|
LinkActionShortcut(ui->action_TAS_Record, QStringLiteral("TAS Record"), true);
|
||||||
LinkActionShortcut(ui->action_TAS_Reset, QStringLiteral("TAS Reset"), true);
|
LinkActionShortcut(ui->action_TAS_Reset, QStringLiteral("TAS Reset"), true);
|
||||||
LinkActionShortcut(ui->action_View_Lobby,
|
LinkActionShortcut(ui->action_View_Lobby, QStringLiteral("Multiplayer Browse Public Game Lobby"));
|
||||||
QStringLiteral("Multiplayer Browse Public Game Lobby"));
|
|
||||||
LinkActionShortcut(ui->action_Start_Room, QStringLiteral("Multiplayer Create Room"));
|
LinkActionShortcut(ui->action_Start_Room, QStringLiteral("Multiplayer Create Room"));
|
||||||
LinkActionShortcut(ui->action_Connect_To_Room,
|
LinkActionShortcut(ui->action_Connect_To_Room, QStringLiteral("Multiplayer Direct Connect to Room"));
|
||||||
QStringLiteral("Multiplayer Direct Connect to Room"));
|
|
||||||
LinkActionShortcut(ui->action_Show_Room, QStringLiteral("Multiplayer Show Current Room"));
|
LinkActionShortcut(ui->action_Show_Room, QStringLiteral("Multiplayer Show Current Room"));
|
||||||
LinkActionShortcut(ui->action_Leave_Room, QStringLiteral("Multiplayer Leave Room"));
|
LinkActionShortcut(ui->action_Leave_Room, QStringLiteral("Multiplayer Leave Room"));
|
||||||
|
|
||||||
// Create and connect a dedicated, robust QAction for exiting fullscreen.
|
connect(render_window, &GRenderWindow::FullscreenExitHotkeyPressed, this, [this]() {
|
||||||
action_exit_fullscreen = new QAction(this);
|
|
||||||
connect(action_exit_fullscreen, &QAction::triggered, this, [this] {
|
|
||||||
if (emulation_running && ui->action_Fullscreen->isChecked()) {
|
if (emulation_running && ui->action_Fullscreen->isChecked()) {
|
||||||
// Un-check the toggle to keep the UI in sync and then exit fullscreen.
|
|
||||||
ui->action_Fullscreen->setChecked(false);
|
ui->action_Fullscreen->setChecked(false);
|
||||||
ToggleFullscreen();
|
ToggleFullscreen();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// Now bind the "Exit Fullscreen" hotkey (Esc by default) to this new QAction.
|
|
||||||
LinkActionShortcut(action_exit_fullscreen, QStringLiteral("Exit Fullscreen"));
|
|
||||||
|
|
||||||
render_window->addAction(action_exit_fullscreen);
|
|
||||||
|
|
||||||
static const QString main_window = QStringLiteral("Main Window");
|
|
||||||
const auto connect_shortcut = [&]<typename Fn>(const QString& action_name, const Fn& function) {
|
const auto connect_shortcut = [&]<typename Fn>(const QString& action_name, const Fn& function) {
|
||||||
const auto* hotkey =
|
static const std::string main_window = "Main Window";
|
||||||
hotkey_registry.GetHotkey(main_window.toStdString(), action_name.toStdString(), this);
|
const auto* hotkey = hotkey_registry.GetHotkey(main_window, action_name.toStdString(), this);
|
||||||
auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
|
auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
|
||||||
const auto* controller_hotkey = hotkey_registry.GetControllerHotkey(
|
const auto* controller_hotkey =
|
||||||
main_window.toStdString(), action_name.toStdString(), controller);
|
hotkey_registry.GetControllerHotkey(main_window, action_name.toStdString(), controller);
|
||||||
connect(hotkey, &QShortcut::activated, this, function);
|
connect(hotkey, &QShortcut::activated, this, function);
|
||||||
connect(controller_hotkey, &ControllerShortcut::Activated, this, function,
|
connect(controller_hotkey, &ControllerShortcut::Activated, this, function, Qt::QueuedConnection);
|
||||||
Qt::QueuedConnection);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
connect_shortcut(QStringLiteral("Change Adapting Filter"),
|
connect_shortcut(QStringLiteral("Change Adapting Filter"), &GMainWindow::OnToggleAdaptingFilter);
|
||||||
&GMainWindow::OnToggleAdaptingFilter);
|
|
||||||
connect_shortcut(QStringLiteral("Change Docked Mode"), &GMainWindow::OnToggleDockedMode);
|
connect_shortcut(QStringLiteral("Change Docked Mode"), &GMainWindow::OnToggleDockedMode);
|
||||||
connect_shortcut(QStringLiteral("Change GPU Accuracy"), &GMainWindow::OnToggleGpuAccuracy);
|
connect_shortcut(QStringLiteral("Change GPU Accuracy"), &GMainWindow::OnToggleGpuAccuracy);
|
||||||
connect_shortcut(QStringLiteral("Audio Mute/Unmute"), &GMainWindow::OnMute);
|
connect_shortcut(QStringLiteral("Audio Mute/Unmute"), &GMainWindow::OnMute);
|
||||||
@@ -1495,13 +1474,6 @@ void GMainWindow::InitializeHotkeys() {
|
|||||||
system->GetRenderdocAPI().ToggleCapture();
|
system->GetRenderdocAPI().ToggleCapture();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
connect_shortcut(QStringLiteral("Toggle Mouse Panning"), [&] {
|
|
||||||
Settings::values.mouse_panning = !Settings::values.mouse_panning;
|
|
||||||
if (Settings::values.mouse_panning) {
|
|
||||||
render_window->installEventFilter(render_window);
|
|
||||||
render_window->setAttribute(Qt::WA_Hover, true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::SetDefaultUIGeometry() {
|
void GMainWindow::SetDefaultUIGeometry() {
|
||||||
@@ -3771,6 +3743,14 @@ void GMainWindow::OnMenuRecentFile() {
|
|||||||
void GMainWindow::OnStartGame() {
|
void GMainWindow::OnStartGame() {
|
||||||
PreventOSSleep();
|
PreventOSSleep();
|
||||||
|
|
||||||
|
if (Settings::values.mouse_panning) {
|
||||||
|
render_window->installEventFilter(render_window);
|
||||||
|
render_window->setAttribute(Qt::WA_Hover, true);
|
||||||
|
} else {
|
||||||
|
render_window->removeEventFilter(render_window);
|
||||||
|
render_window->setAttribute(Qt::WA_Hover, false);
|
||||||
|
}
|
||||||
|
|
||||||
emu_thread->SetRunning(true);
|
emu_thread->SetRunning(true);
|
||||||
|
|
||||||
UpdateMenuState();
|
UpdateMenuState();
|
||||||
|
|||||||
@@ -114,6 +114,7 @@ public:
|
|||||||
const std::shared_ptr<FileSys::VfsFilesystem>& GetVFS() const { return vfs; }
|
const std::shared_ptr<FileSys::VfsFilesystem>& GetVFS() const { return vfs; }
|
||||||
bool IsEmulationRunning() const { return emulation_running; }
|
bool IsEmulationRunning() const { return emulation_running; }
|
||||||
void RefreshGameList();
|
void RefreshGameList();
|
||||||
|
GRenderWindow* GetRenderWindow() const { return render_window; }
|
||||||
bool ExtractZipToDirectoryPublic(const std::filesystem::path& zip_path, const std::filesystem::path& extract_path);
|
bool ExtractZipToDirectoryPublic(const std::filesystem::path& zip_path, const std::filesystem::path& extract_path);
|
||||||
signals:
|
signals:
|
||||||
void EmulationStarting(EmuThread* emu_thread);
|
void EmulationStarting(EmuThread* emu_thread);
|
||||||
|
|||||||
Reference in New Issue
Block a user