Browse Source
qt: Add UI to configure mouse buttons
qt: Add UI to configure mouse buttons
Supports setting the five mouse buttons to any valid controller button/keyboard key (Left, Right, Middle, Foward, Back)pull/15/merge
4 changed files with 542 additions and 0 deletions
-
3src/yuzu/CMakeLists.txt
-
211src/yuzu/configuration/configure_mouse_advanced.cpp
-
67src/yuzu/configuration/configure_mouse_advanced.h
-
261src/yuzu/configuration/configure_mouse_advanced.ui
@ -0,0 +1,211 @@ |
|||||
|
// Copyright 2016 Citra Emulator Project
|
||||
|
// Licensed under GPLv2 or any later version
|
||||
|
// Refer to the license.txt file included.
|
||||
|
|
||||
|
#include <algorithm>
|
||||
|
#include <memory>
|
||||
|
#include <utility>
|
||||
|
#include <QKeyEvent>
|
||||
|
#include <QMenu>
|
||||
|
#include <QMessageBox>
|
||||
|
#include <QTimer>
|
||||
|
#include "common/assert.h"
|
||||
|
#include "common/param_package.h"
|
||||
|
#include "input_common/main.h"
|
||||
|
#include "ui_configure_mouse_advanced.h"
|
||||
|
#include "yuzu/configuration/config.h"
|
||||
|
#include "yuzu/configuration/configure_mouse_advanced.h"
|
||||
|
|
||||
|
static QString getKeyName(int key_code) { |
||||
|
switch (key_code) { |
||||
|
case Qt::Key_Shift: |
||||
|
return QObject::tr("Shift"); |
||||
|
case Qt::Key_Control: |
||||
|
return QObject::tr("Ctrl"); |
||||
|
case Qt::Key_Alt: |
||||
|
return QObject::tr("Alt"); |
||||
|
case Qt::Key_Meta: |
||||
|
return ""; |
||||
|
default: |
||||
|
return QKeySequence(key_code).toString(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
static QString ButtonToText(const Common::ParamPackage& param) { |
||||
|
if (!param.Has("engine")) { |
||||
|
return QObject::tr("[not set]"); |
||||
|
} else if (param.Get("engine", "") == "keyboard") { |
||||
|
return getKeyName(param.Get("code", 0)); |
||||
|
} else if (param.Get("engine", "") == "sdl") { |
||||
|
if (param.Has("hat")) { |
||||
|
return QString(QObject::tr("Hat %1 %2")) |
||||
|
.arg(param.Get("hat", "").c_str(), param.Get("direction", "").c_str()); |
||||
|
} |
||||
|
if (param.Has("axis")) { |
||||
|
return QString(QObject::tr("Axis %1%2")) |
||||
|
.arg(param.Get("axis", "").c_str(), param.Get("direction", "").c_str()); |
||||
|
} |
||||
|
if (param.Has("button")) { |
||||
|
return QString(QObject::tr("Button %1")).arg(param.Get("button", "").c_str()); |
||||
|
} |
||||
|
return QString(); |
||||
|
} else { |
||||
|
return QObject::tr("[unknown]"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
ConfigureMouseAdvanced::ConfigureMouseAdvanced(QWidget* parent) |
||||
|
: QDialog(parent), ui(std::make_unique<Ui::ConfigureMouseAdvanced>()), |
||||
|
timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()) { |
||||
|
ui->setupUi(this); |
||||
|
setFocusPolicy(Qt::ClickFocus); |
||||
|
|
||||
|
button_map = { |
||||
|
ui->left_button, ui->right_button, ui->middle_button, ui->forward_button, ui->back_button, |
||||
|
}; |
||||
|
|
||||
|
for (int button_id = 0; button_id < Settings::NativeMouseButton::NumMouseButtons; button_id++) { |
||||
|
if (!button_map[button_id]) |
||||
|
continue; |
||||
|
button_map[button_id]->setContextMenuPolicy(Qt::CustomContextMenu); |
||||
|
connect(button_map[button_id], &QPushButton::released, [=]() { |
||||
|
handleClick( |
||||
|
button_map[button_id], |
||||
|
[=](const Common::ParamPackage& params) { buttons_param[button_id] = params; }, |
||||
|
InputCommon::Polling::DeviceType::Button); |
||||
|
}); |
||||
|
connect(button_map[button_id], &QPushButton::customContextMenuRequested, |
||||
|
[=](const QPoint& menu_location) { |
||||
|
QMenu context_menu; |
||||
|
context_menu.addAction(tr("Clear"), [&] { |
||||
|
buttons_param[button_id].Clear(); |
||||
|
button_map[button_id]->setText(tr("[not set]")); |
||||
|
}); |
||||
|
context_menu.addAction(tr("Restore Default"), [&] { |
||||
|
buttons_param[button_id] = |
||||
|
Common::ParamPackage{InputCommon::GenerateKeyboardParam( |
||||
|
Config::default_mouse_buttons[button_id])}; |
||||
|
button_map[button_id]->setText(ButtonToText(buttons_param[button_id])); |
||||
|
}); |
||||
|
context_menu.exec(button_map[button_id]->mapToGlobal(menu_location)); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
connect(ui->buttonClearAll, &QPushButton::released, [this] { ClearAll(); }); |
||||
|
connect(ui->buttonRestoreDefaults, &QPushButton::released, [this]() { restoreDefaults(); }); |
||||
|
|
||||
|
timeout_timer->setSingleShot(true); |
||||
|
connect(timeout_timer.get(), &QTimer::timeout, [this]() { setPollingResult({}, true); }); |
||||
|
|
||||
|
connect(poll_timer.get(), &QTimer::timeout, [this]() { |
||||
|
Common::ParamPackage params; |
||||
|
for (auto& poller : device_pollers) { |
||||
|
params = poller->GetNextInput(); |
||||
|
if (params.Has("engine")) { |
||||
|
setPollingResult(params, false); |
||||
|
return; |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
loadConfiguration(); |
||||
|
resize(0, 0); |
||||
|
} |
||||
|
|
||||
|
void ConfigureMouseAdvanced::applyConfiguration() { |
||||
|
std::transform(buttons_param.begin(), buttons_param.end(), |
||||
|
Settings::values.mouse_buttons.begin(), |
||||
|
[](const Common::ParamPackage& param) { return param.Serialize(); }); |
||||
|
} |
||||
|
|
||||
|
void ConfigureMouseAdvanced::loadConfiguration() { |
||||
|
std::transform(Settings::values.mouse_buttons.begin(), Settings::values.mouse_buttons.end(), |
||||
|
buttons_param.begin(), |
||||
|
[](const std::string& str) { return Common::ParamPackage(str); }); |
||||
|
updateButtonLabels(); |
||||
|
} |
||||
|
|
||||
|
void ConfigureMouseAdvanced::restoreDefaults() { |
||||
|
for (int button_id = 0; button_id < Settings::NativeMouseButton::NumMouseButtons; button_id++) { |
||||
|
buttons_param[button_id] = Common::ParamPackage{ |
||||
|
InputCommon::GenerateKeyboardParam(Config::default_mouse_buttons[button_id])}; |
||||
|
} |
||||
|
|
||||
|
updateButtonLabels(); |
||||
|
} |
||||
|
|
||||
|
void ConfigureMouseAdvanced::ClearAll() { |
||||
|
for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) { |
||||
|
if (button_map[i] && button_map[i]->isEnabled()) |
||||
|
buttons_param[i].Clear(); |
||||
|
} |
||||
|
|
||||
|
updateButtonLabels(); |
||||
|
} |
||||
|
|
||||
|
void ConfigureMouseAdvanced::updateButtonLabels() { |
||||
|
for (int button = 0; button < Settings::NativeMouseButton::NumMouseButtons; button++) { |
||||
|
button_map[button]->setText(ButtonToText(buttons_param[button])); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void ConfigureMouseAdvanced::handleClick( |
||||
|
QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter, |
||||
|
InputCommon::Polling::DeviceType type) { |
||||
|
button->setText(tr("[press key]")); |
||||
|
button->setFocus(); |
||||
|
|
||||
|
const auto iter = std::find(button_map.begin(), button_map.end(), button); |
||||
|
ASSERT(iter != button_map.end()); |
||||
|
const auto index = std::distance(button_map.begin(), iter); |
||||
|
ASSERT(index < Settings::NativeButton::NumButtons && index >= 0); |
||||
|
|
||||
|
input_setter = new_input_setter; |
||||
|
|
||||
|
device_pollers = InputCommon::Polling::GetPollers(type); |
||||
|
|
||||
|
// Keyboard keys can only be used as button devices
|
||||
|
want_keyboard_keys = type == InputCommon::Polling::DeviceType::Button; |
||||
|
|
||||
|
for (auto& poller : device_pollers) { |
||||
|
poller->Start(); |
||||
|
} |
||||
|
|
||||
|
grabKeyboard(); |
||||
|
grabMouse(); |
||||
|
timeout_timer->start(5000); // Cancel after 5 seconds
|
||||
|
poll_timer->start(200); // Check for new inputs every 200ms
|
||||
|
} |
||||
|
|
||||
|
void ConfigureMouseAdvanced::setPollingResult(const Common::ParamPackage& params, bool abort) { |
||||
|
releaseKeyboard(); |
||||
|
releaseMouse(); |
||||
|
timeout_timer->stop(); |
||||
|
poll_timer->stop(); |
||||
|
for (auto& poller : device_pollers) { |
||||
|
poller->Stop(); |
||||
|
} |
||||
|
|
||||
|
if (!abort) { |
||||
|
(*input_setter)(params); |
||||
|
} |
||||
|
|
||||
|
updateButtonLabels(); |
||||
|
input_setter = boost::none; |
||||
|
} |
||||
|
|
||||
|
void ConfigureMouseAdvanced::keyPressEvent(QKeyEvent* event) { |
||||
|
if (!input_setter || !event) |
||||
|
return; |
||||
|
|
||||
|
if (event->key() != Qt::Key_Escape) { |
||||
|
if (want_keyboard_keys) { |
||||
|
setPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())}, |
||||
|
false); |
||||
|
} else { |
||||
|
// Escape key wasn't pressed and we don't want any keyboard keys, so don't stop polling
|
||||
|
return; |
||||
|
} |
||||
|
} |
||||
|
setPollingResult({}, true); |
||||
|
} |
||||
@ -0,0 +1,67 @@ |
|||||
|
// Copyright 2016 Citra Emulator Project |
||||
|
// Licensed under GPLv2 or any later version |
||||
|
// Refer to the license.txt file included. |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include <memory> |
||||
|
#include <QDialog> |
||||
|
#include <QWidget> |
||||
|
#include <boost/optional.hpp> |
||||
|
#include "core/settings.h" |
||||
|
|
||||
|
class QCheckBox; |
||||
|
class QPushButton; |
||||
|
class QTimer; |
||||
|
|
||||
|
namespace Ui { |
||||
|
class ConfigureMouseAdvanced; |
||||
|
} |
||||
|
|
||||
|
class ConfigureMouseAdvanced : public QDialog { |
||||
|
Q_OBJECT |
||||
|
|
||||
|
public: |
||||
|
explicit ConfigureMouseAdvanced(QWidget* parent); |
||||
|
|
||||
|
void applyConfiguration(); |
||||
|
|
||||
|
private: |
||||
|
std::unique_ptr<Ui::ConfigureMouseAdvanced> ui; |
||||
|
|
||||
|
/// This will be the the setting function when an input is awaiting configuration. |
||||
|
boost::optional<std::function<void(const Common::ParamPackage&)>> input_setter; |
||||
|
|
||||
|
std::array<QPushButton*, Settings::NativeMouseButton::NumMouseButtons> button_map; |
||||
|
std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons> buttons_param; |
||||
|
|
||||
|
std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers; |
||||
|
|
||||
|
std::unique_ptr<QTimer> timeout_timer; |
||||
|
std::unique_ptr<QTimer> poll_timer; |
||||
|
|
||||
|
/// A flag to indicate if keyboard keys are okay when configuring an input. If this is false, |
||||
|
/// keyboard events are ignored. |
||||
|
bool want_keyboard_keys = false; |
||||
|
|
||||
|
/// Load configuration settings. |
||||
|
void loadConfiguration(); |
||||
|
/// Restore all buttons to their default values. |
||||
|
void restoreDefaults(); |
||||
|
/// Clear all input configuration |
||||
|
void ClearAll(); |
||||
|
|
||||
|
/// Update UI to reflect current configuration. |
||||
|
void updateButtonLabels(); |
||||
|
|
||||
|
/// Called when the button was pressed. |
||||
|
void handleClick(QPushButton* button, |
||||
|
std::function<void(const Common::ParamPackage&)> new_input_setter, |
||||
|
InputCommon::Polling::DeviceType type); |
||||
|
|
||||
|
/// Finish polling and configure input using the input_setter |
||||
|
void setPollingResult(const Common::ParamPackage& params, bool abort); |
||||
|
|
||||
|
/// Handle key press events. |
||||
|
void keyPressEvent(QKeyEvent* event) override; |
||||
|
}; |
||||
@ -0,0 +1,261 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<ui version="4.0"> |
||||
|
<class>ConfigureMouseAdvanced</class> |
||||
|
<widget class="QDialog" name="ConfigureMouseAdvanced"> |
||||
|
<property name="geometry"> |
||||
|
<rect> |
||||
|
<x>0</x> |
||||
|
<y>0</y> |
||||
|
<width>250</width> |
||||
|
<height>261</height> |
||||
|
</rect> |
||||
|
</property> |
||||
|
<property name="windowTitle"> |
||||
|
<string>Configure Mouse</string> |
||||
|
</property> |
||||
|
<layout class="QVBoxLayout" name="verticalLayout"> |
||||
|
<item> |
||||
|
<widget class="QGroupBox" name="gridGroupBox"> |
||||
|
<property name="title"> |
||||
|
<string>Mouse Buttons</string> |
||||
|
</property> |
||||
|
<layout class="QGridLayout" name="gridLayout"> |
||||
|
<item row="0" column="4"> |
||||
|
<spacer name="horizontalSpacer_2"> |
||||
|
<property name="orientation"> |
||||
|
<enum>Qt::Horizontal</enum> |
||||
|
</property> |
||||
|
<property name="sizeType"> |
||||
|
<enum>QSizePolicy::Fixed</enum> |
||||
|
</property> |
||||
|
<property name="sizeHint" stdset="0"> |
||||
|
<size> |
||||
|
<width>20</width> |
||||
|
<height>20</height> |
||||
|
</size> |
||||
|
</property> |
||||
|
</spacer> |
||||
|
</item> |
||||
|
<item row="0" column="3"> |
||||
|
<layout class="QVBoxLayout" name="verticalLayout_4"> |
||||
|
<item> |
||||
|
<layout class="QHBoxLayout" name="horizontalLayout_3"> |
||||
|
<item> |
||||
|
<widget class="QLabel" name="label_3"> |
||||
|
<property name="text"> |
||||
|
<string>Right:</string> |
||||
|
</property> |
||||
|
</widget> |
||||
|
</item> |
||||
|
</layout> |
||||
|
</item> |
||||
|
<item> |
||||
|
<widget class="QPushButton" name="right_button"> |
||||
|
<property name="minimumSize"> |
||||
|
<size> |
||||
|
<width>75</width> |
||||
|
<height>0</height> |
||||
|
</size> |
||||
|
</property> |
||||
|
<property name="text"> |
||||
|
<string/> |
||||
|
</property> |
||||
|
</widget> |
||||
|
</item> |
||||
|
</layout> |
||||
|
</item> |
||||
|
<item row="0" column="0"> |
||||
|
<spacer name="horizontalSpacer"> |
||||
|
<property name="orientation"> |
||||
|
<enum>Qt::Horizontal</enum> |
||||
|
</property> |
||||
|
<property name="sizeType"> |
||||
|
<enum>QSizePolicy::Fixed</enum> |
||||
|
</property> |
||||
|
<property name="sizeHint" stdset="0"> |
||||
|
<size> |
||||
|
<width>20</width> |
||||
|
<height>20</height> |
||||
|
</size> |
||||
|
</property> |
||||
|
</spacer> |
||||
|
</item> |
||||
|
<item row="2" column="1"> |
||||
|
<layout class="QVBoxLayout" name="verticalLayout_3"> |
||||
|
<item> |
||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2"> |
||||
|
<item> |
||||
|
<widget class="QLabel" name="label_2"> |
||||
|
<property name="text"> |
||||
|
<string>Middle:</string> |
||||
|
</property> |
||||
|
</widget> |
||||
|
</item> |
||||
|
</layout> |
||||
|
</item> |
||||
|
<item> |
||||
|
<widget class="QPushButton" name="middle_button"> |
||||
|
<property name="text"> |
||||
|
<string/> |
||||
|
</property> |
||||
|
</widget> |
||||
|
</item> |
||||
|
</layout> |
||||
|
</item> |
||||
|
<item row="3" column="1"> |
||||
|
<layout class="QVBoxLayout" name="verticalLayout_5"> |
||||
|
<item> |
||||
|
<layout class="QHBoxLayout" name="horizontalLayout_4"> |
||||
|
<item> |
||||
|
<widget class="QLabel" name="label_4"> |
||||
|
<property name="minimumSize"> |
||||
|
<size> |
||||
|
<width>54</width> |
||||
|
<height>0</height> |
||||
|
</size> |
||||
|
</property> |
||||
|
<property name="text"> |
||||
|
<string>Back:</string> |
||||
|
</property> |
||||
|
</widget> |
||||
|
</item> |
||||
|
</layout> |
||||
|
</item> |
||||
|
<item> |
||||
|
<widget class="QPushButton" name="back_button"> |
||||
|
<property name="text"> |
||||
|
<string/> |
||||
|
</property> |
||||
|
</widget> |
||||
|
</item> |
||||
|
</layout> |
||||
|
</item> |
||||
|
<item row="0" column="1"> |
||||
|
<layout class="QVBoxLayout" name="verticalLayout_2"> |
||||
|
<item> |
||||
|
<layout class="QHBoxLayout" name="horizontalLayout"> |
||||
|
<item> |
||||
|
<widget class="QLabel" name="label"> |
||||
|
<property name="text"> |
||||
|
<string>Left:</string> |
||||
|
</property> |
||||
|
</widget> |
||||
|
</item> |
||||
|
</layout> |
||||
|
</item> |
||||
|
<item> |
||||
|
<widget class="QPushButton" name="left_button"> |
||||
|
<property name="minimumSize"> |
||||
|
<size> |
||||
|
<width>75</width> |
||||
|
<height>0</height> |
||||
|
</size> |
||||
|
</property> |
||||
|
<property name="text"> |
||||
|
<string/> |
||||
|
</property> |
||||
|
</widget> |
||||
|
</item> |
||||
|
</layout> |
||||
|
</item> |
||||
|
<item row="3" column="3"> |
||||
|
<layout class="QVBoxLayout" name="verticalLayout_6"> |
||||
|
<item> |
||||
|
<layout class="QHBoxLayout" name="horizontalLayout_5"> |
||||
|
<item> |
||||
|
<widget class="QLabel" name="label_5"> |
||||
|
<property name="text"> |
||||
|
<string>Forward:</string> |
||||
|
</property> |
||||
|
</widget> |
||||
|
</item> |
||||
|
</layout> |
||||
|
</item> |
||||
|
<item> |
||||
|
<widget class="QPushButton" name="forward_button"> |
||||
|
<property name="text"> |
||||
|
<string/> |
||||
|
</property> |
||||
|
</widget> |
||||
|
</item> |
||||
|
</layout> |
||||
|
</item> |
||||
|
</layout> |
||||
|
</widget> |
||||
|
</item> |
||||
|
<item> |
||||
|
<layout class="QHBoxLayout" name="horizontalLayout_6"> |
||||
|
<item> |
||||
|
<widget class="QPushButton" name="buttonClearAll"> |
||||
|
<property name="text"> |
||||
|
<string>Clear All</string> |
||||
|
</property> |
||||
|
</widget> |
||||
|
</item> |
||||
|
<item> |
||||
|
<widget class="QPushButton" name="buttonRestoreDefaults"> |
||||
|
<property name="text"> |
||||
|
<string>Restore Defaults</string> |
||||
|
</property> |
||||
|
</widget> |
||||
|
</item> |
||||
|
<item> |
||||
|
<spacer name="horizontalSpacer_3"> |
||||
|
<property name="orientation"> |
||||
|
<enum>Qt::Horizontal</enum> |
||||
|
</property> |
||||
|
<property name="sizeHint" stdset="0"> |
||||
|
<size> |
||||
|
<width>40</width> |
||||
|
<height>20</height> |
||||
|
</size> |
||||
|
</property> |
||||
|
</spacer> |
||||
|
</item> |
||||
|
</layout> |
||||
|
</item> |
||||
|
<item> |
||||
|
<widget class="QDialogButtonBox" name="buttonBox"> |
||||
|
<property name="standardButtons"> |
||||
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> |
||||
|
</property> |
||||
|
</widget> |
||||
|
</item> |
||||
|
</layout> |
||||
|
</widget> |
||||
|
<resources/> |
||||
|
<connections> |
||||
|
<connection> |
||||
|
<sender>buttonBox</sender> |
||||
|
<signal>accepted()</signal> |
||||
|
<receiver>ConfigureMouseAdvanced</receiver> |
||||
|
<slot>accept()</slot> |
||||
|
<hints> |
||||
|
<hint type="sourcelabel"> |
||||
|
<x>124</x> |
||||
|
<y>266</y> |
||||
|
</hint> |
||||
|
<hint type="destinationlabel"> |
||||
|
<x>124</x> |
||||
|
<y>143</y> |
||||
|
</hint> |
||||
|
</hints> |
||||
|
</connection> |
||||
|
<connection> |
||||
|
<sender>buttonBox</sender> |
||||
|
<signal>rejected()</signal> |
||||
|
<receiver>ConfigureMouseAdvanced</receiver> |
||||
|
<slot>reject()</slot> |
||||
|
<hints> |
||||
|
<hint type="sourcelabel"> |
||||
|
<x>124</x> |
||||
|
<y>266</y> |
||||
|
</hint> |
||||
|
<hint type="destinationlabel"> |
||||
|
<x>124</x> |
||||
|
<y>143</y> |
||||
|
</hint> |
||||
|
</hints> |
||||
|
</connection> |
||||
|
</connections> |
||||
|
</ui> |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue