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