Confine Mouse (cross platform)
* This pull request can confine the mouse cursor cross platform * In bootmanager.cpp the mouse cursor will be warp in the touchscreen area if it exits limits * The mouse cursor is warped too slowly and by clicking the mouse can change the focus window * The transparent QWidget "window_frame" prevent this to happen by showing it at fullscreen
This commit is contained in:
parent
3d0a3c2c45
commit
466073de1e
10 changed files with 109 additions and 28 deletions
|
@ -12,6 +12,7 @@
|
|||
#include <QWindow>
|
||||
#include "citra_qt/bootmanager.h"
|
||||
#include "citra_qt/main.h"
|
||||
#include "citra_qt/uisettings.h"
|
||||
#include "common/color.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "common/scm_rev.h"
|
||||
|
@ -48,17 +49,6 @@ EmuThread::EmuThread(Frontend::GraphicsContext& core_context) : core_context(cor
|
|||
|
||||
EmuThread::~EmuThread() = default;
|
||||
|
||||
static GMainWindow* GetMainWindow() {
|
||||
const auto widgets = qApp->topLevelWidgets();
|
||||
for (QWidget* w : widgets) {
|
||||
if (GMainWindow* main = qobject_cast<GMainWindow*>(w)) {
|
||||
return main;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void EmuThread::run() {
|
||||
MicroProfileOnThreadCreate("EmuThread");
|
||||
const auto scope = core_context.Acquire();
|
||||
|
@ -386,6 +376,8 @@ GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread_, Core::Sys
|
|||
bool is_secondary_)
|
||||
: QWidget(parent_), EmuWindow(is_secondary_), emu_thread(emu_thread_), system{system_} {
|
||||
|
||||
window_frame = new QDialog(this);
|
||||
window_frame->setWindowOpacity(0.004);
|
||||
setWindowTitle(QStringLiteral("Citra %1 | %2-%3")
|
||||
.arg(QString::fromUtf8(Common::g_build_name),
|
||||
QString::fromUtf8(Common::g_scm_branch),
|
||||
|
@ -398,8 +390,8 @@ GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread_, Core::Sys
|
|||
this->setMouseTracking(true);
|
||||
strict_context_required = QGuiApplication::platformName() == QStringLiteral("wayland");
|
||||
|
||||
GMainWindow* parent = GetMainWindow();
|
||||
connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete);
|
||||
main_window = qobject_cast<GMainWindow*>(parent_);
|
||||
connect(this, &GRenderWindow::FirstFrameDisplayed, main_window, &GMainWindow::OnLoadComplete);
|
||||
}
|
||||
|
||||
GRenderWindow::~GRenderWindow() = default;
|
||||
|
@ -486,9 +478,25 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) {
|
|||
return; // touch input is handled in TouchBeginEvent
|
||||
}
|
||||
|
||||
if (UISettings::values.confine_mouse_to_the_touchscreen.GetValue() && !confined) {
|
||||
foreground_window = window();
|
||||
child_widget->grabMouse();
|
||||
window_frame->showFullScreen();
|
||||
foreground_window->move(main_window->pos());
|
||||
foreground_window->setFixedSize(main_window->size());
|
||||
foreground_window->setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint);
|
||||
foreground_window->show();
|
||||
if (parentWidget() == nullptr) {
|
||||
main_window->hide();
|
||||
}
|
||||
confined = true;
|
||||
}
|
||||
auto pos = event->pos();
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
const auto [x, y] = ScaleTouch(pos);
|
||||
if (confined) {
|
||||
ConfineMouse();
|
||||
}
|
||||
this->TouchPressed(x, y);
|
||||
} else if (event->button() == Qt::RightButton) {
|
||||
InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y());
|
||||
|
@ -503,6 +511,9 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
|
|||
|
||||
auto pos = event->pos();
|
||||
const auto [x, y] = ScaleTouch(pos);
|
||||
if (confined) {
|
||||
ConfineMouse();
|
||||
}
|
||||
this->TouchMoved(x, y);
|
||||
InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y());
|
||||
emit MouseActivity();
|
||||
|
@ -725,6 +736,7 @@ void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) {
|
|||
|
||||
void GRenderWindow::OnEmulationStopping() {
|
||||
emu_thread = nullptr;
|
||||
child_widget->releaseMouse();
|
||||
}
|
||||
|
||||
void GRenderWindow::showEvent(QShowEvent* event) {
|
||||
|
@ -744,3 +756,32 @@ std::unique_ptr<Frontend::GraphicsContext> GRenderWindow::CreateSharedContext()
|
|||
#endif
|
||||
return std::make_unique<DummyContext>();
|
||||
}
|
||||
|
||||
void GRenderWindow::ConfineMouse() {
|
||||
auto layout = GetFramebufferLayout();
|
||||
auto posi = QCursor::pos();
|
||||
qint32 x_limit =
|
||||
qBound(child_widget->mapToGlobal(QPoint(layout.bottom_screen.left, 0)).x(), posi.x(),
|
||||
child_widget->mapToGlobal(QPoint(layout.bottom_screen.right, 0)).x());
|
||||
qint32 y_limit =
|
||||
qBound(child_widget->mapToGlobal(QPoint(0, layout.bottom_screen.top)).y(), posi.y(),
|
||||
child_widget->mapToGlobal(QPoint(0, layout.bottom_screen.bottom)).y());
|
||||
if (x_limit != posi.x() || y_limit != posi.y()) {
|
||||
QCursor::setPos(x_limit, y_limit);
|
||||
}
|
||||
}
|
||||
|
||||
void GRenderWindow::UnconfineMouse() {
|
||||
if (!confined) {
|
||||
return;
|
||||
}
|
||||
child_widget->releaseMouse();
|
||||
confined = false;
|
||||
window_frame->close();
|
||||
foreground_window->setWindowFlags(Qt::Window);
|
||||
foreground_window->setFixedSize(QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX));
|
||||
foreground_window->show();
|
||||
if (parentWidget() == nullptr) {
|
||||
main_window->show();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ class QKeyEvent;
|
|||
class QTouchEvent;
|
||||
|
||||
class GRenderWindow;
|
||||
class GMainWindow;
|
||||
|
||||
namespace VideoCore {
|
||||
enum class LoadCallbackStage;
|
||||
|
@ -161,6 +162,7 @@ public slots:
|
|||
void OnEmulationStarting(EmuThread* emu_thread);
|
||||
void OnEmulationStopping();
|
||||
void OnFramebufferSizeChanged();
|
||||
void UnconfineMouse();
|
||||
|
||||
signals:
|
||||
/// Emitted when the window is closed
|
||||
|
@ -178,12 +180,15 @@ private:
|
|||
void TouchBeginEvent(const QTouchEvent* event);
|
||||
void TouchUpdateEvent(const QTouchEvent* event);
|
||||
void TouchEndEvent();
|
||||
void ConfineMouse();
|
||||
|
||||
void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override;
|
||||
|
||||
bool InitializeOpenGL();
|
||||
void InitializeSoftware();
|
||||
bool LoadOpenGL();
|
||||
GMainWindow* main_window;
|
||||
QDialog* window_frame;
|
||||
|
||||
QWidget* child_widget = nullptr;
|
||||
|
||||
|
@ -200,6 +205,8 @@ private:
|
|||
QByteArray geometry;
|
||||
bool first_frame = false;
|
||||
bool has_focus = false;
|
||||
bool confined = false;
|
||||
QWidget* foreground_window;
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent* event) override;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <array>
|
||||
#include <QKeySequence>
|
||||
#include <QSettings>
|
||||
#include "citra_qt/bootmanager.h"
|
||||
#include "citra_qt/configuration/config.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/settings.h"
|
||||
|
@ -54,7 +55,7 @@ const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> Config:
|
|||
// This must be in alphabetical order according to action name as it must have the same order as
|
||||
// UISetting::values.shortcuts, which is alphabetically ordered.
|
||||
// clang-format off
|
||||
const std::array<UISettings::Shortcut, 28> Config::default_hotkeys {{
|
||||
const std::array<UISettings::Shortcut, 29> Config::default_hotkeys {{
|
||||
{QStringLiteral("Advance Frame"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::WidgetWithChildrenShortcut}},
|
||||
{QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}},
|
||||
|
@ -83,6 +84,7 @@ const std::array<UISettings::Shortcut, 28> Config::default_hotkeys {{
|
|||
{QStringLiteral("Toggle Status Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+S"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Toggle Texture Dumping"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Toggle Custom Textures"), QStringLiteral("Main Window"), {QStringLiteral("F7"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Unconfine Mouse Cursor"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+U"), Qt::ApplicationShortcut}}
|
||||
}};
|
||||
// clang-format on
|
||||
|
||||
|
@ -651,6 +653,7 @@ void Config::ReadShortcutValues() {
|
|||
qt_config->beginGroup(QStringLiteral("Shortcuts"));
|
||||
|
||||
for (const auto& [name, group, shortcut] : default_hotkeys) {
|
||||
auto [keyseq, context] = shortcut;
|
||||
qt_config->beginGroup(group);
|
||||
qt_config->beginGroup(name);
|
||||
// No longer using ReadSetting for shortcut.second as it innacurately returns a value of 1
|
||||
|
@ -759,6 +762,7 @@ void Config::ReadUIValues() {
|
|||
ReadBasicSetting(UISettings::values.pause_when_in_background);
|
||||
ReadBasicSetting(UISettings::values.hide_mouse);
|
||||
}
|
||||
UISettings::values.hide_mouse = ReadSetting(QStringLiteral("ConfineMouse"), false).toBool();
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
|
@ -1216,6 +1220,7 @@ void Config::SaveUIValues() {
|
|||
WriteBasicSetting(UISettings::values.show_console);
|
||||
WriteBasicSetting(UISettings::values.pause_when_in_background);
|
||||
WriteBasicSetting(UISettings::values.hide_mouse);
|
||||
WriteBasicSetting(UISettings::values.confine_mouse_to_the_touchscreen);
|
||||
}
|
||||
|
||||
qt_config->endGroup();
|
||||
|
|
|
@ -26,7 +26,7 @@ public:
|
|||
|
||||
static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
|
||||
static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs;
|
||||
static const std::array<UISettings::Shortcut, 28> default_hotkeys;
|
||||
static const std::array<UISettings::Shortcut, 29> default_hotkeys;
|
||||
|
||||
private:
|
||||
void Initialize(const std::string& config_name);
|
||||
|
|
|
@ -2,6 +2,14 @@
|
|||
<ui version="4.0">
|
||||
<class>ConfigureDialog</class>
|
||||
<widget class="QDialog" name="ConfigureDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>382</width>
|
||||
<height>242</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Citra Configuration</string>
|
||||
</property>
|
||||
|
@ -9,19 +17,12 @@
|
|||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QListWidget" name="selectorList">
|
||||
<property name="minimumWidth">
|
||||
<number>150</number>
|
||||
</property>
|
||||
<property name="maximumWidth">
|
||||
<number>150</number>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QListWidget" name="selectorList"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
<number>6</number>
|
||||
</property>
|
||||
<widget class="ConfigureGeneral" name="generalTab">
|
||||
<attribute name="title">
|
||||
|
@ -49,9 +50,9 @@
|
|||
</attribute>
|
||||
</widget>
|
||||
<widget class="ConfigureEnhancements" name="enhancementsTab">
|
||||
<attribute name="title">
|
||||
<string>Enhancements</string>
|
||||
</attribute>
|
||||
<attribute name="title">
|
||||
<string>Enhancements</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="ConfigureAudio" name="audioTab">
|
||||
<attribute name="title">
|
||||
|
@ -127,7 +128,7 @@
|
|||
<header>configuration/configure_debug.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<customwidget>
|
||||
<class>ConfigureStorage</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>configuration/configure_storage.h</header>
|
||||
|
|
|
@ -75,6 +75,8 @@ void ConfigureGeneral::SetConfiguration() {
|
|||
ui->toggle_update_check->setChecked(
|
||||
UISettings::values.check_for_update_on_start.GetValue());
|
||||
ui->toggle_auto_update->setChecked(UISettings::values.update_on_close.GetValue());
|
||||
ui->toggle_confine_mouse_touchscreen->setChecked(
|
||||
UISettings::values.confine_mouse_to_the_touchscreen.GetValue());
|
||||
}
|
||||
|
||||
if (Settings::values.frame_limit.GetValue() == 0) {
|
||||
|
@ -171,6 +173,8 @@ void ConfigureGeneral::ApplyConfiguration() {
|
|||
|
||||
UISettings::values.check_for_update_on_start = ui->toggle_update_check->isChecked();
|
||||
UISettings::values.update_on_close = ui->toggle_auto_update->isChecked();
|
||||
UISettings::values.confine_mouse_to_the_touchscreen =
|
||||
ui->toggle_confine_mouse_touchscreen->isChecked();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -43,6 +43,13 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_confine_mouse_touchscreen">
|
||||
<property name="text">
|
||||
<string>confine mouse touchscreen</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
|
@ -525,6 +525,7 @@ void GMainWindow::InitializeHotkeys() {
|
|||
|
||||
const QString main_window = QStringLiteral("Main Window");
|
||||
const QString fullscreen = QStringLiteral("Fullscreen");
|
||||
const QString unconfine_mouse = QStringLiteral("Unconfine Mouse Cursor");
|
||||
const QString toggle_screen_layout = QStringLiteral("Toggle Screen Layout");
|
||||
const QString swap_screens = QStringLiteral("Swap Screens");
|
||||
const QString rotate_screens = QStringLiteral("Rotate Screens Upright");
|
||||
|
@ -655,6 +656,8 @@ void GMainWindow::InitializeHotkeys() {
|
|||
UpdateStatusBar();
|
||||
}
|
||||
});
|
||||
connect(hotkey_registry.GetHotkey(main_window, unconfine_mouse, render_window),
|
||||
&QShortcut::activated, ui->action_Unconfine_Mouse, &QAction::trigger);
|
||||
}
|
||||
|
||||
void GMainWindow::SetDefaultUIGeometry() {
|
||||
|
@ -749,6 +752,8 @@ void GMainWindow::ConnectWidgetEvents() {
|
|||
connect(this, &GMainWindow::CIAInstallFinished, this, &GMainWindow::OnCIAInstallFinished);
|
||||
connect(this, &GMainWindow::UpdateThemedIcons, multiplayer_state,
|
||||
&MultiplayerState::UpdateThemedIcons);
|
||||
connect(ui->action_Unconfine_Mouse, &QAction::triggered, render_window,
|
||||
&GRenderWindow::UnconfineMouse);
|
||||
}
|
||||
|
||||
void GMainWindow::ConnectMenuEvents() {
|
||||
|
|
|
@ -601,6 +601,14 @@
|
|||
<enum>QAction::NoRole</enum>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Unconfine_Mouse">
|
||||
<property name="text">
|
||||
<string>Unconfine Mouse</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Unconfine the mouse cursor</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
|
|
@ -133,6 +133,9 @@ struct Values {
|
|||
|
||||
// logging
|
||||
Settings::Setting<bool> show_console{false, "showConsole"};
|
||||
|
||||
// ConfineMouse
|
||||
Settings::Setting<bool> confine_mouse_to_the_touchscreen{false, "ConfineMouse"};
|
||||
};
|
||||
|
||||
extern Values values;
|
||||
|
|
Loading…
Reference in a new issue