From 41facaece3dbb9c831ad5c6a8290e8fd7f79e602 Mon Sep 17 00:00:00 2001 From: z87 Date: Wed, 1 Apr 2020 21:07:16 +0300 Subject: [PATCH 1/4] input_common: add TouchFromButtonDevice --- src/citra_qt/CMakeLists.txt | 3 + src/citra_qt/configuration/config.cpp | 58 +++- .../configuration/configure_motion_touch.cpp | 34 +- .../configuration/configure_motion_touch.h | 4 + .../configuration/configure_motion_touch.ui | 33 ++ .../configure_touch_from_button.cpp | 321 ++++++++++++++++++ .../configure_touch_from_button.h | 74 ++++ .../configure_touch_from_button.ui | 169 +++++++++ src/citra_qt/main.cpp | 2 + src/core/hle/service/hid/hid.cpp | 8 + src/core/hle/service/hid/hid.h | 1 + src/core/settings.h | 8 + src/input_common/CMakeLists.txt | 2 + src/input_common/main.cpp | 4 + src/input_common/touch_from_button.cpp | 49 +++ src/input_common/touch_from_button.h | 24 ++ 16 files changed, 792 insertions(+), 2 deletions(-) create mode 100644 src/citra_qt/configuration/configure_touch_from_button.cpp create mode 100644 src/citra_qt/configuration/configure_touch_from_button.h create mode 100644 src/citra_qt/configuration/configure_touch_from_button.ui create mode 100644 src/input_common/touch_from_button.cpp create mode 100644 src/input_common/touch_from_button.h diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index 836e3c8ae0..1b62434603 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt @@ -69,6 +69,9 @@ add_executable(citra-qt configuration/configure_system.cpp configuration/configure_system.h configuration/configure_system.ui + configuration/configure_touch_from_button.cpp + configuration/configure_touch_from_button.h + configuration/configure_touch_from_button.ui configuration/configure_ui.cpp configuration/configure_ui.h configuration/configure_ui.ui diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp index 88be0330f3..1c881e5781 100644 --- a/src/citra_qt/configuration/config.cpp +++ b/src/citra_qt/configuration/config.cpp @@ -164,10 +164,42 @@ void Config::ReadCameraValues() { void Config::ReadControlValues() { qt_config->beginGroup(QStringLiteral("Controls")); + int num_touch_from_button_maps = + qt_config->beginReadArray(QStringLiteral("touch_from_button_maps")); + + if (num_touch_from_button_maps > 0) { + const auto& append_touch_from_button_map = [this] { + Settings::TouchFromButtonMap map; + map.name = ReadSetting(QStringLiteral("name"), QStringLiteral("default")) + .toString() + .toStdString(); + const std::size_t num_touch_maps = qt_config->beginReadArray(QStringLiteral("entries")); + map.buttons.reserve(num_touch_maps); + for (int i = 0; i < num_touch_maps; i++) { + qt_config->setArrayIndex(i); + std::string touch_mapping = + ReadSetting(QStringLiteral("bind")).toString().toStdString(); + map.buttons.emplace_back(std::move(touch_mapping)); + } + qt_config->endArray(); // entries + Settings::values.touch_from_button_maps.emplace_back(std::move(map)); + }; + + for (int i = 0; i < num_touch_from_button_maps; ++i) { + qt_config->setArrayIndex(i); + append_touch_from_button_map(); + } + } else { + Settings::values.touch_from_button_maps.emplace_back( + Settings::TouchFromButtonMap{"default", {}}); + num_touch_from_button_maps = 1; + } + qt_config->endArray(); + Settings::values.current_input_profile_index = ReadSetting(QStringLiteral("profile"), 0).toInt(); - const auto append_profile = [this] { + const auto append_profile = [this, num_touch_from_button_maps] { Settings::InputProfile profile; profile.name = ReadSetting(QStringLiteral("name"), QStringLiteral("default")).toString().toStdString(); @@ -201,6 +233,12 @@ void Config::ReadControlValues() { ReadSetting(QStringLiteral("touch_device"), QStringLiteral("engine:emu_window")) .toString() .toStdString(); + profile.use_touch_from_button = + ReadSetting(QStringLiteral("use_touch_from_button"), false).toBool(); + profile.touch_from_button_map_index = + ReadSetting(QStringLiteral("touch_from_button_map"), 0).toInt(); + profile.touch_from_button_map_index = + std::clamp(profile.touch_from_button_map_index, 0, num_touch_from_button_maps - 1); profile.udp_input_address = ReadSetting(QStringLiteral("udp_input_address"), QString::fromUtf8(InputCommon::CemuhookUDP::DEFAULT_ADDR)) @@ -758,6 +796,9 @@ void Config::SaveControlValues() { QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01,tilt_clamp:90.0")); WriteSetting(QStringLiteral("touch_device"), QString::fromStdString(profile.touch_device), QStringLiteral("engine:emu_window")); + WriteSetting(QStringLiteral("use_touch_from_button"), profile.use_touch_from_button, false); + WriteSetting(QStringLiteral("touch_from_button_map"), profile.touch_from_button_map_index, + 0); WriteSetting(QStringLiteral("udp_input_address"), QString::fromStdString(profile.udp_input_address), QString::fromUtf8(InputCommon::CemuhookUDP::DEFAULT_ADDR)); @@ -767,6 +808,21 @@ void Config::SaveControlValues() { } qt_config->endArray(); + qt_config->beginWriteArray(QStringLiteral("touch_from_button_maps")); + for (std::size_t p = 0; p < Settings::values.touch_from_button_maps.size(); ++p) { + qt_config->setArrayIndex(static_cast(p)); + const auto& map = Settings::values.touch_from_button_maps[p]; + WriteSetting(QStringLiteral("name"), QString::fromStdString(map.name), + QStringLiteral("default")); + qt_config->beginWriteArray(QStringLiteral("entries")); + for (std::size_t q = 0; q < map.buttons.size(); ++q) { + qt_config->setArrayIndex(static_cast(q)); + WriteSetting(QStringLiteral("bind"), QString::fromStdString(map.buttons[q])); + } + qt_config->endArray(); + } + qt_config->endArray(); + qt_config->endGroup(); } diff --git a/src/citra_qt/configuration/configure_motion_touch.cpp b/src/citra_qt/configuration/configure_motion_touch.cpp index a5457cdf79..dab5813880 100644 --- a/src/citra_qt/configuration/configure_motion_touch.cpp +++ b/src/citra_qt/configuration/configure_motion_touch.cpp @@ -9,7 +9,7 @@ #include #include #include "citra_qt/configuration/configure_motion_touch.h" -#include "core/settings.h" +#include "citra_qt/configuration/configure_touch_from_button.h" #include "input_common/main.h" #include "ui_configure_motion_touch.h" @@ -111,6 +111,14 @@ void ConfigureMotionTouch::SetConfiguration() { ui->motion_provider->findData(QString::fromStdString(motion_engine))); ui->touch_provider->setCurrentIndex( ui->touch_provider->findData(QString::fromStdString(touch_engine))); + ui->touch_from_button_checkbox->setChecked( + Settings::values.current_input_profile.use_touch_from_button); + touch_from_button_maps = Settings::values.touch_from_button_maps; + for (const auto& touch_map : touch_from_button_maps) { + ui->touch_from_button_map->addItem(QString::fromStdString(touch_map.name)); + } + ui->touch_from_button_map->setCurrentIndex( + Settings::values.current_input_profile.touch_from_button_map_index); ui->motion_sensitivity->setValue(motion_param.Get("sensitivity", 0.01f)); min_x = touch_param.Get("min_x", 100); @@ -166,6 +174,8 @@ void ConfigureMotionTouch::ConnectEvents() { connect(ui->udp_test, &QPushButton::clicked, this, &ConfigureMotionTouch::OnCemuhookUDPTest); connect(ui->touch_calibration_config, &QPushButton::clicked, this, &ConfigureMotionTouch::OnConfigureTouchCalibration); + connect(ui->touch_from_button_config_btn, &QPushButton::clicked, this, + &ConfigureMotionTouch::OnConfigureTouchFromButton); connect(ui->buttonBox, &QDialogButtonBox::rejected, this, [this] { if (CanCloseDialog()) reject(); @@ -234,6 +244,23 @@ void ConfigureMotionTouch::ShowUDPTestResult(bool result) { ui->udp_test->setText(tr("Test")); } +void ConfigureMotionTouch::OnConfigureTouchFromButton() { + ConfigureTouchFromButton dialog{this, touch_from_button_maps, + ui->touch_from_button_map->currentIndex()}; + if (dialog.exec() != QDialog::Accepted) { + return; + } + touch_from_button_maps = dialog.GetMaps(); + + while (ui->touch_from_button_map->count() > 0) { + ui->touch_from_button_map->removeItem(0); + } + for (const auto& touch_map : touch_from_button_maps) { + ui->touch_from_button_map->addItem(QString::fromStdString(touch_map.name)); + } + ui->touch_from_button_map->setCurrentIndex(dialog.GetSelectedIndex()); +} + bool ConfigureMotionTouch::CanCloseDialog() { if (udp_test_in_progress) { QMessageBox::warning(this, tr("Citra"), @@ -268,6 +295,11 @@ void ConfigureMotionTouch::ApplyConfiguration() { Settings::values.current_input_profile.motion_device = motion_param.Serialize(); Settings::values.current_input_profile.touch_device = touch_param.Serialize(); + Settings::values.current_input_profile.use_touch_from_button = + ui->touch_from_button_checkbox->isChecked(); + Settings::values.current_input_profile.touch_from_button_map_index = + ui->touch_from_button_map->currentIndex(); + Settings::values.touch_from_button_maps = touch_from_button_maps; Settings::values.current_input_profile.udp_input_address = ui->udp_server->text().toStdString(); Settings::values.current_input_profile.udp_input_port = static_cast(ui->udp_port->text().toInt()); diff --git a/src/citra_qt/configuration/configure_motion_touch.h b/src/citra_qt/configuration/configure_motion_touch.h index 40bd17f514..11485f2ec5 100644 --- a/src/citra_qt/configuration/configure_motion_touch.h +++ b/src/citra_qt/configuration/configure_motion_touch.h @@ -7,6 +7,7 @@ #include #include #include "common/param_package.h" +#include "core/settings.h" #include "input_common/udp/udp.h" class QVBoxLayout; @@ -54,6 +55,7 @@ public slots: private slots: void OnCemuhookUDPTest(); void OnConfigureTouchCalibration(); + void OnConfigureTouchFromButton(); private: void closeEvent(QCloseEvent* event) override; @@ -69,4 +71,6 @@ private: int min_x, min_y, max_x, max_y; bool udp_test_in_progress{}; + + std::vector touch_from_button_maps; }; diff --git a/src/citra_qt/configuration/configure_motion_touch.ui b/src/citra_qt/configuration/configure_motion_touch.ui index 133c4de330..602cf8cd83 100644 --- a/src/citra_qt/configuration/configure_motion_touch.ui +++ b/src/citra_qt/configuration/configure_motion_touch.ui @@ -124,6 +124,39 @@ + + + + + + + 0 + 0 + + + + Use button mapping: + + + + + + + + + + + 0 + 0 + + + + Configure + + + + + diff --git a/src/citra_qt/configuration/configure_touch_from_button.cpp b/src/citra_qt/configuration/configure_touch_from_button.cpp new file mode 100644 index 0000000000..add1f1f6da --- /dev/null +++ b/src/citra_qt/configuration/configure_touch_from_button.cpp @@ -0,0 +1,321 @@ +// Copyright 2020 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include +#include +#include +#include "citra_qt/configuration/configure_touch_from_button.h" +#include "common/param_package.h" +#include "input_common/main.h" +#include "ui_configure_touch_from_button.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 QString{}; + default: + return QKeySequence(key_code).toString(); + } +} + +static QString ButtonToText(const Common::ParamPackage& param) { + if (!param.Has("engine")) { + return QObject::tr("[not set]"); + } + + if (param.Get("engine", "") == "keyboard") { + return GetKeyName(param.Get("code", 0)); + } + + if (param.Get("engine", "") == "sdl") { + if (param.Has("hat")) { + const QString hat_str = QString::fromStdString(param.Get("hat", "")); + const QString direction_str = QString::fromStdString(param.Get("direction", "")); + + return QObject::tr("Hat %1 %2").arg(hat_str, direction_str); + } + + if (param.Has("axis")) { + const QString axis_str = QString::fromStdString(param.Get("axis", "")); + const QString direction_str = QString::fromStdString(param.Get("direction", "")); + + return QObject::tr("Axis %1%2").arg(axis_str, direction_str); + } + + if (param.Has("button")) { + const QString button_str = QString::fromStdString(param.Get("button", "")); + + return QObject::tr("Button %1").arg(button_str); + } + + return {}; + } + + return QObject::tr("[unknown]"); +} + +ConfigureTouchFromButton::ConfigureTouchFromButton( + QWidget* parent, std::vector touch_maps, int default_index) + : QDialog(parent), touch_maps(touch_maps), selected_index(default_index), + ui(std::make_unique()), + timeout_timer(std::make_unique()), poll_timer(std::make_unique()) { + + ui->setupUi(this); + binding_list_model = std::make_unique(0, 3, this); + binding_list_model->setHorizontalHeaderLabels({tr("Button"), tr("X"), tr("Y")}); + ui->binding_list->setModel(binding_list_model.get()); + + SetConfiguration(); + UpdateUiDisplay(); + ConnectEvents(); +} + +ConfigureTouchFromButton::~ConfigureTouchFromButton() = default; + +void ConfigureTouchFromButton::showEvent(QShowEvent* ev) { + QWidget::showEvent(ev); + + // width values are not valid in the constructor + const int w = ui->binding_list->contentsRect().width() / binding_list_model->columnCount(); + if (w > 0) { + ui->binding_list->setColumnWidth(0, w); + ui->binding_list->setColumnWidth(1, w); + ui->binding_list->setColumnWidth(2, w); + } +} + +void ConfigureTouchFromButton::SetConfiguration() { + for (const auto& touch_map : touch_maps) { + ui->mapping->addItem(QString::fromStdString(touch_map.name)); + } + + ui->mapping->setCurrentIndex(selected_index); +} + +void ConfigureTouchFromButton::UpdateUiDisplay() { + const bool have_maps = !touch_maps.empty(); + + ui->button_delete->setEnabled(touch_maps.size() > 1); + ui->button_rename->setEnabled(have_maps); + ui->binding_list->setEnabled(have_maps); + ui->button_add_bind->setEnabled(have_maps); + ui->button_delete_bind->setEnabled(false); + + binding_list_model->removeRows(0, binding_list_model->rowCount()); + + if (!have_maps) { + return; + } + + for (const auto& button_str : touch_maps[selected_index].buttons) { + Common::ParamPackage package{button_str}; + QStandardItem* button = new QStandardItem(ButtonToText(package)); + button->setData(QString::fromStdString(button_str)); + button->setEditable(false); + QStandardItem* xcoord = new QStandardItem(QString::number(package.Get("x", 0))); + QStandardItem* ycoord = new QStandardItem(QString::number(package.Get("y", 0))); + binding_list_model->appendRow({button, xcoord, ycoord}); + } +} + +void ConfigureTouchFromButton::ConnectEvents() { + connect(ui->mapping, qOverload(&QComboBox::activated), this, [this](int index) { + SaveCurrentMapping(); + selected_index = index; + UpdateUiDisplay(); + }); + connect(ui->button_new, &QPushButton::clicked, this, &ConfigureTouchFromButton::NewMapping); + connect(ui->button_delete, &QPushButton::clicked, this, + &ConfigureTouchFromButton::DeleteMapping); + connect(ui->button_rename, &QPushButton::clicked, this, + &ConfigureTouchFromButton::RenameMapping); + connect(ui->button_add_bind, &QPushButton::clicked, this, + &ConfigureTouchFromButton::NewBinding); + connect(ui->button_delete_bind, &QPushButton::clicked, this, + &ConfigureTouchFromButton::DeleteBinding); + connect(ui->binding_list, &QTreeView::doubleClicked, this, + &ConfigureTouchFromButton::EditBinding); + connect(ui->binding_list->selectionModel(), &QItemSelectionModel::selectionChanged, this, + [this](const QItemSelection& selected, const QItemSelection& deselected) { + ui->button_delete_bind->setEnabled(!selected.indexes().isEmpty()); + }); + connect(ui->buttonBox, &QDialogButtonBox::accepted, this, + &ConfigureTouchFromButton::ApplyConfiguration); + + 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; + } + } + }); +} + +void ConfigureTouchFromButton::SaveCurrentMapping() { + auto& map = touch_maps[selected_index]; + map.buttons.clear(); + for (int i = 0, rc = binding_list_model->rowCount(); i < rc; ++i) { + auto bind_str = binding_list_model->index(i, 0) + .data(Qt::ItemDataRole::UserRole + 1) + .toString() + .toStdString(); + if (bind_str.empty()) { + continue; + } + Common::ParamPackage params{bind_str}; + if (!params.Has("engine")) { + continue; + } + params.Set("x", binding_list_model->index(i, 1).data().toInt()); + params.Set("y", binding_list_model->index(i, 2).data().toInt()); + map.buttons.emplace_back(params.Serialize()); + } +} + +void ConfigureTouchFromButton::NewMapping() { + const QString name = + QInputDialog::getText(this, tr("New Profile"), tr("Enter the name for the new profile.")); + if (name.isEmpty()) { + return; + } + + if (selected_index > 0) { + SaveCurrentMapping(); + } + touch_maps.emplace_back(Settings::TouchFromButtonMap{name.toStdString(), {}}); + selected_index = touch_maps.size() - 1; + + ui->mapping->addItem(name); + ui->mapping->setCurrentIndex(selected_index); + UpdateUiDisplay(); +} + +void ConfigureTouchFromButton::DeleteMapping() { + const auto answer = QMessageBox::question( + this, tr("Delete Profile"), tr("Delete profile %1?").arg(ui->mapping->currentText())); + if (answer != QMessageBox::Yes) { + return; + } + ui->mapping->removeItem(selected_index); + ui->mapping->setCurrentIndex(0); + touch_maps.erase(touch_maps.begin() + selected_index); + selected_index = touch_maps.size() ? 0 : -1; + UpdateUiDisplay(); +} + +void ConfigureTouchFromButton::RenameMapping() { + const QString new_name = QInputDialog::getText(this, tr("Rename Profile"), tr("New name:")); + if (new_name.isEmpty()) { + return; + } + ui->mapping->setItemText(selected_index, new_name); + touch_maps[selected_index].name = new_name.toStdString(); +} + +void ConfigureTouchFromButton::GetButtonInput(int row_index, bool is_new) { + binding_list_model->item(row_index, 0)->setText(tr("[press key]")); + + input_setter = [this, row_index, is_new](const Common::ParamPackage& params, + const bool cancel) { + auto cell = binding_list_model->item(row_index, 0); + if (!cancel) { + cell->setText(ButtonToText(params)); + cell->setData(QString::fromStdString(params.Serialize())); + } else { + if (is_new) { + binding_list_model->removeRow(row_index); + } else { + cell->setText( + ButtonToText(Common::ParamPackage{cell->data().toString().toStdString()})); + } + } + }; + + device_pollers = InputCommon::Polling::GetPollers(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 ConfigureTouchFromButton::NewBinding() { + QStandardItem* button = new QStandardItem(); + button->setEditable(false); + binding_list_model->appendRow( + {button, new QStandardItem(QStringLiteral("0")), new QStandardItem(QStringLiteral("0"))}); + ui->binding_list->setFocus(); + ui->binding_list->setCurrentIndex(button->index()); + + GetButtonInput(binding_list_model->rowCount() - 1, true); +} + +void ConfigureTouchFromButton::EditBinding(const QModelIndex& qi) { + if (qi.row() >= 0 && qi.column() == 0) { + GetButtonInput(qi.row(), false); + } +} + +void ConfigureTouchFromButton::DeleteBinding() { + const int row_index = ui->binding_list->currentIndex().row(); + if (row_index >= 0) { + binding_list_model->removeRow(row_index); + } +} + +void ConfigureTouchFromButton::SetPollingResult(const Common::ParamPackage& params, bool cancel) { + releaseKeyboard(); + releaseMouse(); + timeout_timer->stop(); + poll_timer->stop(); + for (auto& poller : device_pollers) { + poller->Stop(); + } + if (input_setter) { + (*input_setter)(params, cancel); + input_setter.reset(); + } +} + +void ConfigureTouchFromButton::keyPressEvent(QKeyEvent* event) { + if (!input_setter || !event) + return QDialog::keyPressEvent(event); + + if (event->key() != Qt::Key_Escape) { + SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())}, + false); + } else { + SetPollingResult({}, true); + } +} + +void ConfigureTouchFromButton::ApplyConfiguration() { + SaveCurrentMapping(); + accept(); +} + +const int ConfigureTouchFromButton::GetSelectedIndex() { + return selected_index; +} + +const std::vector ConfigureTouchFromButton::GetMaps() { + return touch_maps; +}; \ No newline at end of file diff --git a/src/citra_qt/configuration/configure_touch_from_button.h b/src/citra_qt/configuration/configure_touch_from_button.h new file mode 100644 index 0000000000..20ba97e5fd --- /dev/null +++ b/src/citra_qt/configuration/configure_touch_from_button.h @@ -0,0 +1,74 @@ +// Copyright 2020 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include +#include "core/settings.h" + +class QKeyEvent; +class QModelIndex; +class QStandardItemModel; +class QTimer; + +namespace Common { +class ParamPackage; +} + +namespace InputCommon { +namespace Polling { +class DevicePoller; +} +} // namespace InputCommon + +namespace Ui { +class ConfigureTouchFromButton; +} + +class ConfigureTouchFromButton : public QDialog { + Q_OBJECT + +public: + explicit ConfigureTouchFromButton(QWidget* parent, + std::vector touch_maps, + int default_index = 0); + ~ConfigureTouchFromButton() override; + + const int GetSelectedIndex(); + const std::vector GetMaps(); + +public slots: + void ApplyConfiguration(); + +protected: + void showEvent(QShowEvent* ev); + +private: + void SetConfiguration(); + void UpdateUiDisplay(); + void ConnectEvents(); + void NewMapping(); + void DeleteMapping(); + void RenameMapping(); + void NewBinding(); + void EditBinding(const QModelIndex& qi); + void DeleteBinding(); + void GetButtonInput(int row_index, bool is_new); + void SetPollingResult(const Common::ParamPackage& params, bool cancel); + void SaveCurrentMapping(); + void keyPressEvent(QKeyEvent* event) override; + + std::unique_ptr ui; + std::unique_ptr binding_list_model; + std::vector touch_maps; + int selected_index; + + std::unique_ptr timeout_timer; + std::unique_ptr poll_timer; + std::vector> device_pollers; + std::optional> input_setter; +}; diff --git a/src/citra_qt/configuration/configure_touch_from_button.ui b/src/citra_qt/configuration/configure_touch_from_button.ui new file mode 100644 index 0000000000..c022c7858c --- /dev/null +++ b/src/citra_qt/configuration/configure_touch_from_button.ui @@ -0,0 +1,169 @@ + + + ConfigureTouchFromButton + + + + 0 + 0 + 500 + 450 + + + + Configure Touchscreen Mappings + + + + + + + + Mapping: + + + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + New + + + + + + + + 0 + 0 + + + + Delete + + + + + + + + 0 + 0 + + + + Rename + + + + + + + + + Qt::Horizontal + + + + + + + + + Double-click to change a field. + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Add + + + + + + + Delete + + + + + + + + + + 0 + 0 + + + + false + + + true + + + false + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + rejected() + ConfigureTouchFromButton + reject() + + + 249 + 428 + + + 249 + 224 + + + + + diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index 62604caec1..ca93a078bb 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -1693,6 +1693,7 @@ void GMainWindow::OnConfigure() { auto old_theme = UISettings::values.theme; const int old_input_profile_index = Settings::values.current_input_profile_index; const auto old_input_profiles = Settings::values.input_profiles; + const auto old_touch_from_button_maps = Settings::values.touch_from_button_maps; const bool old_discord_presence = UISettings::values.enable_discord_presence; auto result = configureDialog.exec(); if (result == QDialog::Accepted) { @@ -1718,6 +1719,7 @@ void GMainWindow::OnConfigure() { } } else { Settings::values.input_profiles = old_input_profiles; + Settings::values.touch_from_button_maps = old_touch_from_button_maps; Settings::LoadProfile(old_input_profile_index); } } diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 4c87182766..39d7a180ce 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -103,6 +103,11 @@ void Module::LoadInputDevices() { Settings::values.current_input_profile.motion_device); touch_device = Input::CreateDevice( Settings::values.current_input_profile.touch_device); + if (Settings::values.current_input_profile.use_touch_from_button) { + touch_btn_device = Input::CreateDevice("engine:touch_from_button"); + } else { + touch_btn_device.reset(); + } } void Module::UpdatePadCallback(u64 userdata, s64 cycles_late) { @@ -177,6 +182,9 @@ void Module::UpdatePadCallback(u64 userdata, s64 cycles_late) { bool pressed = false; float x, y; std::tie(x, y, pressed) = touch_device->GetStatus(); + if (!pressed && touch_btn_device) { + std::tie(x, y, pressed) = touch_btn_device->GetStatus(); + } touch_entry.x = static_cast(x * Core::kScreenBottomWidth); touch_entry.y = static_cast(y * Core::kScreenBottomHeight); touch_entry.valid.Assign(pressed ? 1 : 0); diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index 58a3c68a59..18e7aa3f3a 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -336,6 +336,7 @@ private: std::unique_ptr circle_pad; std::unique_ptr motion_device; std::unique_ptr touch_device; + std::unique_ptr touch_btn_device; template void serialize(Archive& ar, const unsigned int); diff --git a/src/core/settings.h b/src/core/settings.h index 70bdd8c4c1..a025921746 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -112,11 +112,18 @@ struct InputProfile { std::array analogs; std::string motion_device; std::string touch_device; + bool use_touch_from_button; + int touch_from_button_map_index; std::string udp_input_address; u16 udp_input_port; u8 udp_pad_index; }; +struct TouchFromButtonMap { + std::string name; + std::vector buttons; +}; + struct Values { // CheckNew3DS bool is_new_3ds; @@ -125,6 +132,7 @@ struct Values { InputProfile current_input_profile; ///< The current input profile int current_input_profile_index; ///< The current input profile index std::vector input_profiles; ///< The list of input profiles + std::vector touch_from_button_maps; // Core bool use_cpu_jit; diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt index 2520ba321c..0a7ad1d2f9 100644 --- a/src/input_common/CMakeLists.txt +++ b/src/input_common/CMakeLists.txt @@ -7,6 +7,8 @@ add_library(input_common STATIC main.h motion_emu.cpp motion_emu.h + touch_from_button.cpp + touch_from_button.h sdl/sdl.cpp sdl/sdl.h udp/client.cpp diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp index 4674a0978d..7aa15a9b44 100644 --- a/src/input_common/main.cpp +++ b/src/input_common/main.cpp @@ -10,6 +10,7 @@ #include "input_common/main.h" #include "input_common/motion_emu.h" #include "input_common/sdl/sdl.h" +#include "input_common/touch_from_button.h" #include "input_common/udp/udp.h" namespace InputCommon { @@ -26,6 +27,8 @@ void Init() { std::make_shared()); motion_emu = std::make_shared(); Input::RegisterFactory("motion_emu", motion_emu); + Input::RegisterFactory("touch_from_button", + std::make_shared()); sdl = SDL::Init(); @@ -38,6 +41,7 @@ void Shutdown() { Input::UnregisterFactory("analog_from_button"); Input::UnregisterFactory("motion_emu"); motion_emu.reset(); + Input::UnregisterFactory("touch_from_button"); sdl.reset(); udp.reset(); } diff --git a/src/input_common/touch_from_button.cpp b/src/input_common/touch_from_button.cpp new file mode 100644 index 0000000000..3f731f6246 --- /dev/null +++ b/src/input_common/touch_from_button.cpp @@ -0,0 +1,49 @@ +// Copyright 2020 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/3ds.h" +#include "core/settings.h" +#include "input_common/touch_from_button.h" + +namespace InputCommon { + +class TouchFromButtonDevice final : public Input::TouchDevice { +public: + TouchFromButtonDevice() { + for (const auto& config_entry : + Settings::values + .touch_from_button_maps[Settings::values.current_input_profile + .touch_from_button_map_index] + .buttons) { + + const Common::ParamPackage package{config_entry}; + map.emplace_back(Input::CreateDevice(config_entry), + std::clamp(package.Get("x", 0), 0, Core::kScreenBottomWidth), + std::clamp(package.Get("y", 0), 0, Core::kScreenBottomHeight)); + } + } + + std::tuple GetStatus() const override { + for (const auto& m : map) { + const bool state = std::get<0>(m)->GetStatus(); + if (state) { + const float x = static_cast(std::get<1>(m)) / Core::kScreenBottomWidth; + const float y = static_cast(std::get<2>(m)) / Core::kScreenBottomHeight; + return std::make_tuple(x, y, true); + } + } + return std::make_tuple(0.0f, 0.0f, false); + } + +private: + std::vector, int, int>> map; // button, x, y +}; + +std::unique_ptr TouchFromButtonFactory::Create( + const Common::ParamPackage& params) { + + return std::make_unique(); +} + +} // namespace InputCommon diff --git a/src/input_common/touch_from_button.h b/src/input_common/touch_from_button.h new file mode 100644 index 0000000000..b9ff3888e3 --- /dev/null +++ b/src/input_common/touch_from_button.h @@ -0,0 +1,24 @@ +// Copyright 2020 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include "core/frontend/input.h" + +namespace InputCommon { + +/** + * A touch device factory that takes a list of button devices and combines them into a touch device. + */ +class TouchFromButtonFactory final : public Input::Factory { +public: + /** + * Creates a touch device from a list of button devices + * @param unused + */ + std::unique_ptr Create(const Common::ParamPackage& params) override; +}; + +} // namespace InputCommon From 09cba69b48e22914c5332cc1213b3c391cb8f233 Mon Sep 17 00:00:00 2001 From: z87 Date: Mon, 4 May 2020 10:41:51 +0300 Subject: [PATCH 2/4] citra_qt: improve touchscreen mapping UI --- dist/qt_themes/qdarkstyle/style.qss | 5 + src/citra_qt/CMakeLists.txt | 1 + .../configure_touch_from_button.cpp | 358 ++++++++++++++++-- .../configure_touch_from_button.h | 41 +- .../configure_touch_from_button.ui | 88 ++++- .../configuration/configure_touch_widget.h | 61 +++ 6 files changed, 493 insertions(+), 61 deletions(-) create mode 100644 src/citra_qt/configuration/configure_touch_widget.h diff --git a/dist/qt_themes/qdarkstyle/style.qss b/dist/qt_themes/qdarkstyle/style.qss index 9814b06ddc..a2b51c6dd7 100644 --- a/dist/qt_themes/qdarkstyle/style.qss +++ b/dist/qt_themes/qdarkstyle/style.qss @@ -1236,3 +1236,8 @@ QToolButton:disabled, QPlainTextEdit:disabled { background-color: #2b2e31; } + +/* touchscreen mapping widget */ +TouchScreenPreview { + qproperty-dotHighlightColor: #3daee9; +} diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index 1b62434603..eea517bc60 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt @@ -72,6 +72,7 @@ add_executable(citra-qt configuration/configure_touch_from_button.cpp configuration/configure_touch_from_button.h configuration/configure_touch_from_button.ui + configuration/configure_touch_widget.h configuration/configure_ui.cpp configuration/configure_ui.h configuration/configure_ui.ui diff --git a/src/citra_qt/configuration/configure_touch_from_button.cpp b/src/citra_qt/configuration/configure_touch_from_button.cpp index add1f1f6da..17e979b54a 100644 --- a/src/citra_qt/configuration/configure_touch_from_button.cpp +++ b/src/citra_qt/configuration/configure_touch_from_button.cpp @@ -5,10 +5,14 @@ #include #include #include +#include +#include #include #include #include "citra_qt/configuration/configure_touch_from_button.h" +#include "citra_qt/configuration/configure_touch_widget.h" #include "common/param_package.h" +#include "core/3ds.h" #include "input_common/main.h" #include "ui_configure_touch_from_button.h" @@ -64,15 +68,17 @@ static QString ButtonToText(const Common::ParamPackage& param) { } ConfigureTouchFromButton::ConfigureTouchFromButton( - QWidget* parent, std::vector touch_maps, int default_index) - : QDialog(parent), touch_maps(touch_maps), selected_index(default_index), - ui(std::make_unique()), - timeout_timer(std::make_unique()), poll_timer(std::make_unique()) { + QWidget* parent, const std::vector& touch_maps, + const int default_index) + : QDialog(parent), ui(std::make_unique()), touch_maps(touch_maps), + selected_index(default_index), timeout_timer(std::make_unique()), + poll_timer(std::make_unique()) { ui->setupUi(this); binding_list_model = std::make_unique(0, 3, this); binding_list_model->setHorizontalHeaderLabels({tr("Button"), tr("X"), tr("Y")}); ui->binding_list->setModel(binding_list_model.get()); + ui->bottom_screen->SetCoordLabel(ui->coord_label); SetConfiguration(); UpdateUiDisplay(); @@ -85,7 +91,8 @@ void ConfigureTouchFromButton::showEvent(QShowEvent* ev) { QWidget::showEvent(ev); // width values are not valid in the constructor - const int w = ui->binding_list->contentsRect().width() / binding_list_model->columnCount(); + const int w = + ui->binding_list->viewport()->contentsRect().width() / binding_list_model->columnCount(); if (w > 0) { ui->binding_list->setColumnWidth(0, w); ui->binding_list->setColumnWidth(1, w); @@ -102,20 +109,11 @@ void ConfigureTouchFromButton::SetConfiguration() { } void ConfigureTouchFromButton::UpdateUiDisplay() { - const bool have_maps = !touch_maps.empty(); - ui->button_delete->setEnabled(touch_maps.size() > 1); - ui->button_rename->setEnabled(have_maps); - ui->binding_list->setEnabled(have_maps); - ui->button_add_bind->setEnabled(have_maps); ui->button_delete_bind->setEnabled(false); binding_list_model->removeRows(0, binding_list_model->rowCount()); - if (!have_maps) { - return; - } - for (const auto& button_str : touch_maps[selected_index].buttons) { Common::ParamPackage package{button_str}; QStandardItem* button = new QStandardItem(ButtonToText(package)); @@ -124,6 +122,9 @@ void ConfigureTouchFromButton::UpdateUiDisplay() { QStandardItem* xcoord = new QStandardItem(QString::number(package.Get("x", 0))); QStandardItem* ycoord = new QStandardItem(QString::number(package.Get("y", 0))); binding_list_model->appendRow({button, xcoord, ycoord}); + + int dot = ui->bottom_screen->AddDot(package.Get("x", 0), package.Get("y", 0)); + button->setData(dot, data_role_dot); } } @@ -138,16 +139,22 @@ void ConfigureTouchFromButton::ConnectEvents() { &ConfigureTouchFromButton::DeleteMapping); connect(ui->button_rename, &QPushButton::clicked, this, &ConfigureTouchFromButton::RenameMapping); - connect(ui->button_add_bind, &QPushButton::clicked, this, - &ConfigureTouchFromButton::NewBinding); connect(ui->button_delete_bind, &QPushButton::clicked, this, &ConfigureTouchFromButton::DeleteBinding); connect(ui->binding_list, &QTreeView::doubleClicked, this, &ConfigureTouchFromButton::EditBinding); connect(ui->binding_list->selectionModel(), &QItemSelectionModel::selectionChanged, this, - [this](const QItemSelection& selected, const QItemSelection& deselected) { - ui->button_delete_bind->setEnabled(!selected.indexes().isEmpty()); - }); + &ConfigureTouchFromButton::OnBindingSelection); + connect(binding_list_model.get(), &QStandardItemModel::itemChanged, this, + &ConfigureTouchFromButton::OnBindingChanged); + connect(ui->binding_list->model(), &QStandardItemModel::rowsAboutToBeRemoved, this, + &ConfigureTouchFromButton::OnBindingDeleted); + connect(ui->bottom_screen, &TouchScreenPreview::DotAdded, this, + &ConfigureTouchFromButton::NewBinding); + connect(ui->bottom_screen, &TouchScreenPreview::DotSelected, this, + &ConfigureTouchFromButton::SetActiveBinding); + connect(ui->bottom_screen, &TouchScreenPreview::DotMoved, this, + &ConfigureTouchFromButton::SetCoordinates); connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &ConfigureTouchFromButton::ApplyConfiguration); @@ -169,10 +176,10 @@ void ConfigureTouchFromButton::SaveCurrentMapping() { auto& map = touch_maps[selected_index]; map.buttons.clear(); for (int i = 0, rc = binding_list_model->rowCount(); i < rc; ++i) { - auto bind_str = binding_list_model->index(i, 0) - .data(Qt::ItemDataRole::UserRole + 1) - .toString() - .toStdString(); + const auto bind_str = binding_list_model->index(i, 0) + .data(Qt::ItemDataRole::UserRole + 1) + .toString() + .toStdString(); if (bind_str.empty()) { continue; } @@ -197,7 +204,7 @@ void ConfigureTouchFromButton::NewMapping() { SaveCurrentMapping(); } touch_maps.emplace_back(Settings::TouchFromButtonMap{name.toStdString(), {}}); - selected_index = touch_maps.size() - 1; + selected_index = static_cast(touch_maps.size()) - 1; ui->mapping->addItem(name); ui->mapping->setCurrentIndex(selected_index); @@ -226,7 +233,7 @@ void ConfigureTouchFromButton::RenameMapping() { touch_maps[selected_index].name = new_name.toStdString(); } -void ConfigureTouchFromButton::GetButtonInput(int row_index, bool is_new) { +void ConfigureTouchFromButton::GetButtonInput(const int row_index, const bool is_new) { binding_list_model->item(row_index, 0)->setText(tr("[press key]")); input_setter = [this, row_index, is_new](const Common::ParamPackage& params, @@ -253,15 +260,21 @@ void ConfigureTouchFromButton::GetButtonInput(int row_index, bool is_new) { grabKeyboard(); grabMouse(); + qApp->setOverrideCursor(QCursor(Qt::CursorShape::ArrowCursor)); timeout_timer->start(5000); // Cancel after 5 seconds poll_timer->start(200); // Check for new inputs every 200ms } -void ConfigureTouchFromButton::NewBinding() { +void ConfigureTouchFromButton::NewBinding(const QPoint& pos) { QStandardItem* button = new QStandardItem(); button->setEditable(false); - binding_list_model->appendRow( - {button, new QStandardItem(QStringLiteral("0")), new QStandardItem(QStringLiteral("0"))}); + QStandardItem* xcoord = new QStandardItem(QString::number(pos.x())); + QStandardItem* ycoord = new QStandardItem(QString::number(pos.y())); + + int dot_id = ui->bottom_screen->AddDot(pos.x(), pos.y()); + button->setData(dot_id, data_role_dot); + + binding_list_model->appendRow({button, xcoord, ycoord}); ui->binding_list->setFocus(); ui->binding_list->setCurrentIndex(button->index()); @@ -277,13 +290,86 @@ void ConfigureTouchFromButton::EditBinding(const QModelIndex& qi) { void ConfigureTouchFromButton::DeleteBinding() { const int row_index = ui->binding_list->currentIndex().row(); if (row_index >= 0) { + ui->bottom_screen->RemoveDot( + binding_list_model->index(row_index, 0).data(data_role_dot).toInt()); binding_list_model->removeRow(row_index); } } -void ConfigureTouchFromButton::SetPollingResult(const Common::ParamPackage& params, bool cancel) { +void ConfigureTouchFromButton::OnBindingSelection(const QItemSelection& selected, + const QItemSelection& deselected) { + ui->button_delete_bind->setEnabled(!selected.isEmpty()); + if (!selected.isEmpty()) { + const auto dot_data = selected.indexes().first().data(data_role_dot); + if (dot_data.isValid()) { + ui->bottom_screen->HighlightDot(dot_data.toInt()); + } + } + if (!deselected.isEmpty()) { + const auto dot_data = deselected.indexes().first().data(data_role_dot); + if (dot_data.isValid()) { + ui->bottom_screen->HighlightDot(dot_data.toInt(), false); + } + } +} + +void ConfigureTouchFromButton::OnBindingChanged(QStandardItem* item) { + if (item->column() == 0) { + return; + } + + const bool blocked = binding_list_model->blockSignals(true); + item->setText(QString::number(std::clamp( + item->text().toInt(), 0, + (item->column() == 1 ? Core::kScreenBottomWidth : Core::kScreenBottomHeight) - 1))); + binding_list_model->blockSignals(blocked); + + const auto dot_data = binding_list_model->index(item->row(), 0).data(data_role_dot); + if (dot_data.isValid()) { + ui->bottom_screen->MoveDot(dot_data.toInt(), + binding_list_model->item(item->row(), 1)->text().toInt(), + binding_list_model->item(item->row(), 2)->text().toInt()); + } +} + +void ConfigureTouchFromButton::OnBindingDeleted(const QModelIndex& parent, int first, int last) { + for (int i = first; i <= last; ++i) { + auto ix = binding_list_model->index(i, 0); + if (!ix.isValid()) { + return; + } + const auto dot_data = ix.data(data_role_dot); + if (dot_data.isValid()) { + ui->bottom_screen->RemoveDot(dot_data.toInt()); + } + } +} + +void ConfigureTouchFromButton::SetActiveBinding(const int dot_id) { + for (int i = 0; i < binding_list_model->rowCount(); ++i) { + if (binding_list_model->index(i, 0).data(data_role_dot) == dot_id) { + ui->binding_list->setCurrentIndex(binding_list_model->index(i, 0)); + ui->binding_list->setFocus(); + return; + } + } +} + +void ConfigureTouchFromButton::SetCoordinates(const int dot_id, const QPoint& pos) { + for (int i = 0; i < binding_list_model->rowCount(); ++i) { + if (binding_list_model->item(i, 0)->data(data_role_dot) == dot_id) { + binding_list_model->item(i, 1)->setText(QString::number(pos.x())); + binding_list_model->item(i, 2)->setText(QString::number(pos.y())); + return; + } + } +} + +void ConfigureTouchFromButton::SetPollingResult(const Common::ParamPackage& params, + const bool cancel) { releaseKeyboard(); releaseMouse(); + qApp->restoreOverrideCursor(); timeout_timer->stop(); poll_timer->stop(); for (auto& poller : device_pollers) { @@ -296,8 +382,14 @@ void ConfigureTouchFromButton::SetPollingResult(const Common::ParamPackage& para } void ConfigureTouchFromButton::keyPressEvent(QKeyEvent* event) { - if (!input_setter || !event) + if (!input_setter && event->key() == Qt::Key_Delete) { + DeleteBinding(); + return; + } + + if (!input_setter) { return QDialog::keyPressEvent(event); + } if (event->key() != Qt::Key_Escape) { SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())}, @@ -312,10 +404,210 @@ void ConfigureTouchFromButton::ApplyConfiguration() { accept(); } -const int ConfigureTouchFromButton::GetSelectedIndex() { +int ConfigureTouchFromButton::GetSelectedIndex() const { return selected_index; } -const std::vector ConfigureTouchFromButton::GetMaps() { +std::vector ConfigureTouchFromButton::GetMaps() const { return touch_maps; -}; \ No newline at end of file +} + +TouchScreenPreview::TouchScreenPreview(QWidget* parent) : QFrame(parent) { + setBackgroundRole(QPalette::ColorRole::Base); +} + +TouchScreenPreview::~TouchScreenPreview() = default; + +void TouchScreenPreview::SetCoordLabel(QLabel* const label) { + coord_label = label; +} + +int TouchScreenPreview::AddDot(const int device_x, const int device_y) { + QFont dot_font{QStringLiteral("monospace")}; + dot_font.setStyleHint(QFont::Monospace); + dot_font.setPointSize(20); + + QLabel* dot = new QLabel(this); + dot->setAttribute(Qt::WA_TranslucentBackground); + dot->setFont(dot_font); + dot->setText(QChar(0xD7)); // U+00D7 Multiplication Sign + dot->setAlignment(Qt::AlignmentFlag::AlignCenter); + dot->setProperty(prop_id, ++max_dot_id); + dot->setProperty(prop_x, device_x); + dot->setProperty(prop_y, device_y); + dot->setCursor(Qt::CursorShape::PointingHandCursor); + dot->setMouseTracking(true); + dot->installEventFilter(this); + dot->show(); + PositionDot(dot, device_x, device_y); + dots.emplace_back(max_dot_id, dot); + return max_dot_id; +} + +void TouchScreenPreview::RemoveDot(const int id) { + for (auto dot_it = dots.begin(); dot_it < dots.end(); ++dot_it) { + if (dot_it->first == id) { + dot_it->second->deleteLater(); + dots.erase(dot_it); + return; + } + } +} + +void TouchScreenPreview::HighlightDot(const int id, const bool active) const { + for (const auto& dot : dots) { + if (dot.first == id) { + // use color property from the stylesheet, or fall back to the default palette + if (dot_highlight_color.isValid()) { + dot.second->setStyleSheet( + active ? QStringLiteral("color: %1").arg(dot_highlight_color.name()) + : QString{}); + } else { + dot.second->setForegroundRole(active ? QPalette::ColorRole::LinkVisited + : QPalette::ColorRole::NoRole); + } + return; + } + } +} + +void TouchScreenPreview::MoveDot(const int id, const int device_x, const int device_y) const { + for (const auto& dot : dots) { + if (dot.first == id) { + dot.second->setProperty(prop_x, device_x); + dot.second->setProperty(prop_y, device_y); + PositionDot(dot.second, device_x, device_y); + return; + } + } +} + +void TouchScreenPreview::resizeEvent(QResizeEvent* event) { + if (ignore_resize) { + return; + } + + const int target_width = std::min(width(), height() * 4 / 3); + const int target_height = std::min(height(), width() * 3 / 4); + if (target_width == width() && target_height == height()) { + return; + } + ignore_resize = true; + setGeometry((parentWidget()->contentsRect().width() - target_width) / 2, y(), target_width, + target_height); + ignore_resize = false; + + if (event->oldSize().width() != target_width || event->oldSize().height() != target_height) { + for (const auto& dot : dots) { + PositionDot(dot.second); + } + } +} + +void TouchScreenPreview::mouseMoveEvent(QMouseEvent* event) { + if (!coord_label) { + return; + } + const auto point = MapToDeviceCoords(event->x(), event->y()); + if (point.has_value()) { + coord_label->setText(QStringLiteral("X: %1, Y: %2").arg(point->x()).arg(point->y())); + } else { + coord_label->clear(); + } +} + +void TouchScreenPreview::leaveEvent(QEvent* event) { + if (coord_label) { + coord_label->clear(); + } +} + +void TouchScreenPreview::mousePressEvent(QMouseEvent* event) { + if (event->button() == Qt::MouseButton::LeftButton) { + const auto pos = MapToDeviceCoords(event->x(), event->y()); + if (pos.has_value()) { + emit DotAdded(*pos); + } + } +} + +bool TouchScreenPreview::eventFilter(QObject* obj, QEvent* event) { + switch (event->type()) { + case QEvent::Type::MouseButtonPress: { + const auto mouse_event = static_cast(event); + if (mouse_event->button() != Qt::MouseButton::LeftButton) { + break; + } + emit DotSelected(obj->property(prop_id).toInt()); + + drag_state.dot = qobject_cast(obj); + drag_state.start_pos = mouse_event->globalPos(); + return true; + } + case QEvent::Type::MouseMove: { + if (!drag_state.dot) { + break; + } + const auto mouse_event = static_cast(event); + if (!drag_state.active) { + drag_state.active = + (mouse_event->globalPos() - drag_state.start_pos).manhattanLength() >= + QApplication::startDragDistance(); + if (!drag_state.active) { + break; + } + } + auto current_pos = mapFromGlobal(mouse_event->globalPos()); + current_pos.setX(std::clamp(current_pos.x(), contentsMargins().left(), + contentsMargins().left() + contentsRect().width())); + current_pos.setY(std::clamp(current_pos.y(), contentsMargins().top(), + contentsMargins().top() + contentsRect().height())); + const auto device_coord = MapToDeviceCoords(current_pos.x(), current_pos.y()); + if (device_coord.has_value()) { + drag_state.dot->setProperty(prop_x, device_coord->x()); + drag_state.dot->setProperty(prop_y, device_coord->y()); + PositionDot(drag_state.dot, device_coord->x(), device_coord->y()); + emit DotMoved(drag_state.dot->property(prop_id).toInt(), *device_coord); + if (coord_label) { + coord_label->setText( + QStringLiteral("X: %1, Y: %2").arg(device_coord->x()).arg(device_coord->y())); + } + } + return true; + } + case QEvent::Type::MouseButtonRelease: { + drag_state.dot.clear(); + drag_state.active = false; + return true; + } + default: + break; + } + return obj->eventFilter(obj, event); +} + +std::optional TouchScreenPreview::MapToDeviceCoords(const int screen_x, + const int screen_y) const { + const float t_x = 0.5f + static_cast(screen_x - contentsMargins().left()) * + (Core::kScreenBottomWidth - 1) / contentsRect().width(); + const float t_y = 0.5f + static_cast(screen_y - contentsMargins().top()) * + (Core::kScreenBottomHeight - 1) / contentsRect().height(); + if (t_x >= 0.5f && t_x < Core::kScreenBottomWidth && t_y >= 0.5f && + t_y < Core::kScreenBottomHeight) { + + return QPoint{static_cast(t_x), static_cast(t_y)}; + } + return std::nullopt; +} + +void TouchScreenPreview::PositionDot(QLabel* const dot, const int device_x, + const int device_y) const { + dot->move(static_cast( + static_cast(device_x >= 0 ? device_x : dot->property(prop_x).toInt()) * + (contentsRect().width() - 1) / (Core::kScreenBottomWidth - 1) + + contentsMargins().left() - static_cast(dot->width()) / 2 + 0.5f), + static_cast( + static_cast(device_y >= 0 ? device_y : dot->property(prop_y).toInt()) * + (contentsRect().height() - 1) / (Core::kScreenBottomHeight - 1) + + contentsMargins().top() - static_cast(dot->height()) / 2 + 0.5f)); +} diff --git a/src/citra_qt/configuration/configure_touch_from_button.h b/src/citra_qt/configuration/configure_touch_from_button.h index 20ba97e5fd..d9a6848135 100644 --- a/src/citra_qt/configuration/configure_touch_from_button.h +++ b/src/citra_qt/configuration/configure_touch_from_button.h @@ -7,12 +7,14 @@ #include #include #include +#include #include #include "core/settings.h" -class QKeyEvent; +class QItemSelection; class QModelIndex; class QStandardItemModel; +class QStandardItem; class QTimer; namespace Common { @@ -34,33 +36,40 @@ class ConfigureTouchFromButton : public QDialog { public: explicit ConfigureTouchFromButton(QWidget* parent, - std::vector touch_maps, - int default_index = 0); + const std::vector& touch_maps, + const int default_index = 0); ~ConfigureTouchFromButton() override; - const int GetSelectedIndex(); - const std::vector GetMaps(); + int GetSelectedIndex() const; + std::vector GetMaps() const; public slots: void ApplyConfiguration(); + void NewBinding(const QPoint& pos); + void SetActiveBinding(const int dot_id); + void SetCoordinates(const int dot_id, const QPoint& pos); protected: - void showEvent(QShowEvent* ev); + virtual void showEvent(QShowEvent* ev) override; + virtual void keyPressEvent(QKeyEvent* event) override; + +private slots: + void NewMapping(); + void DeleteMapping(); + void RenameMapping(); + void EditBinding(const QModelIndex& qi); + void DeleteBinding(); + void OnBindingSelection(const QItemSelection& selected, const QItemSelection& deselected); + void OnBindingChanged(QStandardItem* item); + void OnBindingDeleted(const QModelIndex& parent, int first, int last); private: void SetConfiguration(); void UpdateUiDisplay(); void ConnectEvents(); - void NewMapping(); - void DeleteMapping(); - void RenameMapping(); - void NewBinding(); - void EditBinding(const QModelIndex& qi); - void DeleteBinding(); - void GetButtonInput(int row_index, bool is_new); - void SetPollingResult(const Common::ParamPackage& params, bool cancel); + void GetButtonInput(const int row_index, const bool is_new); + void SetPollingResult(const Common::ParamPackage& params, const bool cancel); void SaveCurrentMapping(); - void keyPressEvent(QKeyEvent* event) override; std::unique_ptr ui; std::unique_ptr binding_list_model; @@ -71,4 +80,6 @@ private: std::unique_ptr poll_timer; std::vector> device_pollers; std::optional> input_setter; + + static constexpr int data_role_dot = Qt::ItemDataRole::UserRole + 2; }; diff --git a/src/citra_qt/configuration/configure_touch_from_button.ui b/src/citra_qt/configuration/configure_touch_from_button.ui index c022c7858c..974400c8af 100644 --- a/src/citra_qt/configuration/configure_touch_from_button.ui +++ b/src/citra_qt/configuration/configure_touch_from_button.ui @@ -7,7 +7,7 @@ 0 0 500 - 450 + 500 @@ -21,6 +21,9 @@ Mapping: + + Qt::PlainText + @@ -86,7 +89,11 @@ - Double-click to change a field. + Click the bottom area to add a point, then press a button to bind. +Drag points to change position, or double-click table cells to edit values. + + + Qt::PlainText @@ -103,17 +110,10 @@ - - - - Add - - - - Delete + Delete point @@ -139,14 +139,76 @@ - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + 0 + 0 + + + + + 160 + 120 + + + + + 320 + 240 + + + + CrossCursor + + + true + + + true + + + QFrame::StyledPanel + + + QFrame::Sunken + + + + + + + 0 + 0 + + + + Qt::PlainText + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + TouchScreenPreview + QFrame +
citra_qt/configuration/configure_touch_widget.h
+ 1 +
+
diff --git a/src/citra_qt/configuration/configure_touch_widget.h b/src/citra_qt/configuration/configure_touch_widget.h new file mode 100644 index 0000000000..567330fa0a --- /dev/null +++ b/src/citra_qt/configuration/configure_touch_widget.h @@ -0,0 +1,61 @@ +// Copyright 2020 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include +#include + +class QLabel; + +// Widget for representing touchscreen coordinates +class TouchScreenPreview : public QFrame { + Q_OBJECT + Q_PROPERTY(QColor dotHighlightColor MEMBER dot_highlight_color) + +public: + TouchScreenPreview(QWidget* parent); + ~TouchScreenPreview() override; + + void SetCoordLabel(QLabel* const); + int AddDot(const int device_x, const int device_y); + void RemoveDot(const int id); + void HighlightDot(const int id, const bool active = true) const; + void MoveDot(const int id, const int device_x, const int device_y) const; + +signals: + void DotAdded(const QPoint& pos); + void DotSelected(const int dot_id); + void DotMoved(const int dot_id, const QPoint& pos); + +protected: + virtual void resizeEvent(QResizeEvent*) override; + virtual void mouseMoveEvent(QMouseEvent*) override; + virtual void leaveEvent(QEvent*) override; + virtual void mousePressEvent(QMouseEvent*) override; + virtual bool eventFilter(QObject*, QEvent*) override; + +private: + std::optional MapToDeviceCoords(const int screen_x, const int screen_y) const; + void PositionDot(QLabel* const dot, const int device_x = -1, const int device_y = -1) const; + + bool ignore_resize = false; + QPointer coord_label; + + std::vector> dots; + int max_dot_id = 0; + QColor dot_highlight_color; + static constexpr char prop_id[] = "dot_id"; + static constexpr char prop_x[] = "device_x"; + static constexpr char prop_y[] = "device_y"; + + struct { + bool active = false; + QPointer dot; + QPoint start_pos; + } drag_state; +}; From e792c3d90c1a2ee135ae0bcf6e109eefe8f80435 Mon Sep 17 00:00:00 2001 From: z87 Date: Mon, 4 May 2020 16:24:25 +0300 Subject: [PATCH 3/4] citra_qt: remove incorrect style in the Dark theme Property "overflow" isn't documented, and it makes Qt complain about an unknown property in the terminal. --- dist/qt_themes/qdarkstyle/style.qss | 4 ---- 1 file changed, 4 deletions(-) diff --git a/dist/qt_themes/qdarkstyle/style.qss b/dist/qt_themes/qdarkstyle/style.qss index a2b51c6dd7..a5eee211ee 100644 --- a/dist/qt_themes/qdarkstyle/style.qss +++ b/dist/qt_themes/qdarkstyle/style.qss @@ -673,10 +673,6 @@ QTabWidget::pane { border-bottom-left-radius: 2px; } -QTabWidget::tab-bar { - overflow: visible; -} - QTabBar { qproperty-drawBase: 0; border-radius: 3px; From 26a6f644186ae6101a089b2a4f462eb761bcb5da Mon Sep 17 00:00:00 2001 From: z87 Date: Mon, 11 May 2020 17:05:05 +0300 Subject: [PATCH 4/4] address review comments --- src/citra_qt/configuration/config.cpp | 4 +- .../configure_touch_from_button.cpp | 89 +++++++++---------- .../configure_touch_from_button.h | 14 +-- .../configure_touch_from_button.ui | 2 +- .../configuration/configure_touch_widget.h | 26 +++--- 5 files changed, 66 insertions(+), 69 deletions(-) diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp index 1c881e5781..cde48c67eb 100644 --- a/src/citra_qt/configuration/config.cpp +++ b/src/citra_qt/configuration/config.cpp @@ -168,12 +168,12 @@ void Config::ReadControlValues() { qt_config->beginReadArray(QStringLiteral("touch_from_button_maps")); if (num_touch_from_button_maps > 0) { - const auto& append_touch_from_button_map = [this] { + const auto append_touch_from_button_map = [this] { Settings::TouchFromButtonMap map; map.name = ReadSetting(QStringLiteral("name"), QStringLiteral("default")) .toString() .toStdString(); - const std::size_t num_touch_maps = qt_config->beginReadArray(QStringLiteral("entries")); + const int num_touch_maps = qt_config->beginReadArray(QStringLiteral("entries")); map.buttons.reserve(num_touch_maps); for (int i = 0; i < num_touch_maps; i++) { qt_config->setArrayIndex(i); diff --git a/src/citra_qt/configuration/configure_touch_from_button.cpp b/src/citra_qt/configuration/configure_touch_from_button.cpp index 17e979b54a..12473e3f63 100644 --- a/src/citra_qt/configuration/configure_touch_from_button.cpp +++ b/src/citra_qt/configuration/configure_touch_from_button.cpp @@ -124,12 +124,12 @@ void ConfigureTouchFromButton::UpdateUiDisplay() { binding_list_model->appendRow({button, xcoord, ycoord}); int dot = ui->bottom_screen->AddDot(package.Get("x", 0), package.Get("y", 0)); - button->setData(dot, data_role_dot); + button->setData(dot, DataRoleDot); } } void ConfigureTouchFromButton::ConnectEvents() { - connect(ui->mapping, qOverload(&QComboBox::activated), this, [this](int index) { + connect(ui->mapping, qOverload(&QComboBox::currentIndexChanged), this, [this](int index) { SaveCurrentMapping(); selected_index = index; UpdateUiDisplay(); @@ -199,16 +199,9 @@ void ConfigureTouchFromButton::NewMapping() { if (name.isEmpty()) { return; } - - if (selected_index > 0) { - SaveCurrentMapping(); - } touch_maps.emplace_back(Settings::TouchFromButtonMap{name.toStdString(), {}}); - selected_index = static_cast(touch_maps.size()) - 1; - ui->mapping->addItem(name); - ui->mapping->setCurrentIndex(selected_index); - UpdateUiDisplay(); + ui->mapping->setCurrentIndex(ui->mapping->count() - 1); } void ConfigureTouchFromButton::DeleteMapping() { @@ -217,10 +210,11 @@ void ConfigureTouchFromButton::DeleteMapping() { if (answer != QMessageBox::Yes) { return; } + const bool blocked = ui->mapping->blockSignals(true); ui->mapping->removeItem(selected_index); - ui->mapping->setCurrentIndex(0); + ui->mapping->blockSignals(blocked); touch_maps.erase(touch_maps.begin() + selected_index); - selected_index = touch_maps.size() ? 0 : -1; + selected_index = ui->mapping->currentIndex(); UpdateUiDisplay(); } @@ -239,16 +233,16 @@ void ConfigureTouchFromButton::GetButtonInput(const int row_index, const bool is input_setter = [this, row_index, is_new](const Common::ParamPackage& params, const bool cancel) { auto cell = binding_list_model->item(row_index, 0); - if (!cancel) { - cell->setText(ButtonToText(params)); - cell->setData(QString::fromStdString(params.Serialize())); - } else { + if (cancel) { if (is_new) { binding_list_model->removeRow(row_index); } else { cell->setText( ButtonToText(Common::ParamPackage{cell->data().toString().toStdString()})); } + } else { + cell->setText(ButtonToText(params)); + cell->setData(QString::fromStdString(params.Serialize())); } }; @@ -271,8 +265,8 @@ void ConfigureTouchFromButton::NewBinding(const QPoint& pos) { QStandardItem* xcoord = new QStandardItem(QString::number(pos.x())); QStandardItem* ycoord = new QStandardItem(QString::number(pos.y())); - int dot_id = ui->bottom_screen->AddDot(pos.x(), pos.y()); - button->setData(dot_id, data_role_dot); + const int dot_id = ui->bottom_screen->AddDot(pos.x(), pos.y()); + button->setData(dot_id, DataRoleDot); binding_list_model->appendRow({button, xcoord, ycoord}); ui->binding_list->setFocus(); @@ -291,7 +285,7 @@ void ConfigureTouchFromButton::DeleteBinding() { const int row_index = ui->binding_list->currentIndex().row(); if (row_index >= 0) { ui->bottom_screen->RemoveDot( - binding_list_model->index(row_index, 0).data(data_role_dot).toInt()); + binding_list_model->index(row_index, 0).data(DataRoleDot).toInt()); binding_list_model->removeRow(row_index); } } @@ -300,13 +294,13 @@ void ConfigureTouchFromButton::OnBindingSelection(const QItemSelection& selected const QItemSelection& deselected) { ui->button_delete_bind->setEnabled(!selected.isEmpty()); if (!selected.isEmpty()) { - const auto dot_data = selected.indexes().first().data(data_role_dot); + const auto dot_data = selected.indexes().first().data(DataRoleDot); if (dot_data.isValid()) { ui->bottom_screen->HighlightDot(dot_data.toInt()); } } if (!deselected.isEmpty()) { - const auto dot_data = deselected.indexes().first().data(data_role_dot); + const auto dot_data = deselected.indexes().first().data(DataRoleDot); if (dot_data.isValid()) { ui->bottom_screen->HighlightDot(dot_data.toInt(), false); } @@ -324,7 +318,7 @@ void ConfigureTouchFromButton::OnBindingChanged(QStandardItem* item) { (item->column() == 1 ? Core::kScreenBottomWidth : Core::kScreenBottomHeight) - 1))); binding_list_model->blockSignals(blocked); - const auto dot_data = binding_list_model->index(item->row(), 0).data(data_role_dot); + const auto dot_data = binding_list_model->index(item->row(), 0).data(DataRoleDot); if (dot_data.isValid()) { ui->bottom_screen->MoveDot(dot_data.toInt(), binding_list_model->item(item->row(), 1)->text().toInt(), @@ -338,7 +332,7 @@ void ConfigureTouchFromButton::OnBindingDeleted(const QModelIndex& parent, int f if (!ix.isValid()) { return; } - const auto dot_data = ix.data(data_role_dot); + const auto dot_data = ix.data(DataRoleDot); if (dot_data.isValid()) { ui->bottom_screen->RemoveDot(dot_data.toInt()); } @@ -347,7 +341,7 @@ void ConfigureTouchFromButton::OnBindingDeleted(const QModelIndex& parent, int f void ConfigureTouchFromButton::SetActiveBinding(const int dot_id) { for (int i = 0; i < binding_list_model->rowCount(); ++i) { - if (binding_list_model->index(i, 0).data(data_role_dot) == dot_id) { + if (binding_list_model->index(i, 0).data(DataRoleDot) == dot_id) { ui->binding_list->setCurrentIndex(binding_list_model->index(i, 0)); ui->binding_list->setFocus(); return; @@ -357,7 +351,7 @@ void ConfigureTouchFromButton::SetActiveBinding(const int dot_id) { void ConfigureTouchFromButton::SetCoordinates(const int dot_id, const QPoint& pos) { for (int i = 0; i < binding_list_model->rowCount(); ++i) { - if (binding_list_model->item(i, 0)->data(data_role_dot) == dot_id) { + if (binding_list_model->item(i, 0)->data(DataRoleDot) == dot_id) { binding_list_model->item(i, 1)->setText(QString::number(pos.x())); binding_list_model->item(i, 2)->setText(QString::number(pos.y())); return; @@ -432,9 +426,9 @@ int TouchScreenPreview::AddDot(const int device_x, const int device_y) { dot->setFont(dot_font); dot->setText(QChar(0xD7)); // U+00D7 Multiplication Sign dot->setAlignment(Qt::AlignmentFlag::AlignCenter); - dot->setProperty(prop_id, ++max_dot_id); - dot->setProperty(prop_x, device_x); - dot->setProperty(prop_y, device_y); + dot->setProperty(PropId, ++max_dot_id); + dot->setProperty(PropX, device_x); + dot->setProperty(PropY, device_y); dot->setCursor(Qt::CursorShape::PointingHandCursor); dot->setMouseTracking(true); dot->installEventFilter(this); @@ -445,7 +439,7 @@ int TouchScreenPreview::AddDot(const int device_x, const int device_y) { } void TouchScreenPreview::RemoveDot(const int id) { - for (auto dot_it = dots.begin(); dot_it < dots.end(); ++dot_it) { + for (auto dot_it = dots.begin(); dot_it != dots.end(); ++dot_it) { if (dot_it->first == id) { dot_it->second->deleteLater(); dots.erase(dot_it); @@ -466,6 +460,9 @@ void TouchScreenPreview::HighlightDot(const int id, const bool active) const { dot.second->setForegroundRole(active ? QPalette::ColorRole::LinkVisited : QPalette::ColorRole::NoRole); } + if (active) { + dot.second->raise(); + } return; } } @@ -474,8 +471,8 @@ void TouchScreenPreview::HighlightDot(const int id, const bool active) const { void TouchScreenPreview::MoveDot(const int id, const int device_x, const int device_y) const { for (const auto& dot : dots) { if (dot.first == id) { - dot.second->setProperty(prop_x, device_x); - dot.second->setProperty(prop_y, device_y); + dot.second->setProperty(PropX, device_x); + dot.second->setProperty(PropY, device_y); PositionDot(dot.second, device_x, device_y); return; } @@ -508,9 +505,9 @@ void TouchScreenPreview::mouseMoveEvent(QMouseEvent* event) { if (!coord_label) { return; } - const auto point = MapToDeviceCoords(event->x(), event->y()); - if (point.has_value()) { - coord_label->setText(QStringLiteral("X: %1, Y: %2").arg(point->x()).arg(point->y())); + const auto pos = MapToDeviceCoords(event->x(), event->y()); + if (pos) { + coord_label->setText(QStringLiteral("X: %1, Y: %2").arg(pos->x()).arg(pos->y())); } else { coord_label->clear(); } @@ -525,7 +522,7 @@ void TouchScreenPreview::leaveEvent(QEvent* event) { void TouchScreenPreview::mousePressEvent(QMouseEvent* event) { if (event->button() == Qt::MouseButton::LeftButton) { const auto pos = MapToDeviceCoords(event->x(), event->y()); - if (pos.has_value()) { + if (pos) { emit DotAdded(*pos); } } @@ -538,7 +535,7 @@ bool TouchScreenPreview::eventFilter(QObject* obj, QEvent* event) { if (mouse_event->button() != Qt::MouseButton::LeftButton) { break; } - emit DotSelected(obj->property(prop_id).toInt()); + emit DotSelected(obj->property(PropId).toInt()); drag_state.dot = qobject_cast(obj); drag_state.start_pos = mouse_event->globalPos(); @@ -559,15 +556,15 @@ bool TouchScreenPreview::eventFilter(QObject* obj, QEvent* event) { } auto current_pos = mapFromGlobal(mouse_event->globalPos()); current_pos.setX(std::clamp(current_pos.x(), contentsMargins().left(), - contentsMargins().left() + contentsRect().width())); + contentsMargins().left() + contentsRect().width() - 1)); current_pos.setY(std::clamp(current_pos.y(), contentsMargins().top(), - contentsMargins().top() + contentsRect().height())); + contentsMargins().top() + contentsRect().height() - 1)); const auto device_coord = MapToDeviceCoords(current_pos.x(), current_pos.y()); - if (device_coord.has_value()) { - drag_state.dot->setProperty(prop_x, device_coord->x()); - drag_state.dot->setProperty(prop_y, device_coord->y()); + if (device_coord) { + drag_state.dot->setProperty(PropX, device_coord->x()); + drag_state.dot->setProperty(PropY, device_coord->y()); PositionDot(drag_state.dot, device_coord->x(), device_coord->y()); - emit DotMoved(drag_state.dot->property(prop_id).toInt(), *device_coord); + emit DotMoved(drag_state.dot->property(PropId).toInt(), *device_coord); if (coord_label) { coord_label->setText( QStringLiteral("X: %1, Y: %2").arg(device_coord->x()).arg(device_coord->y())); @@ -589,9 +586,9 @@ bool TouchScreenPreview::eventFilter(QObject* obj, QEvent* event) { std::optional TouchScreenPreview::MapToDeviceCoords(const int screen_x, const int screen_y) const { const float t_x = 0.5f + static_cast(screen_x - contentsMargins().left()) * - (Core::kScreenBottomWidth - 1) / contentsRect().width(); + (Core::kScreenBottomWidth - 1) / (contentsRect().width() - 1); const float t_y = 0.5f + static_cast(screen_y - contentsMargins().top()) * - (Core::kScreenBottomHeight - 1) / contentsRect().height(); + (Core::kScreenBottomHeight - 1) / (contentsRect().height() - 1); if (t_x >= 0.5f && t_x < Core::kScreenBottomWidth && t_y >= 0.5f && t_y < Core::kScreenBottomHeight) { @@ -603,11 +600,11 @@ std::optional TouchScreenPreview::MapToDeviceCoords(const int screen_x, void TouchScreenPreview::PositionDot(QLabel* const dot, const int device_x, const int device_y) const { dot->move(static_cast( - static_cast(device_x >= 0 ? device_x : dot->property(prop_x).toInt()) * + static_cast(device_x >= 0 ? device_x : dot->property(PropX).toInt()) * (contentsRect().width() - 1) / (Core::kScreenBottomWidth - 1) + contentsMargins().left() - static_cast(dot->width()) / 2 + 0.5f), static_cast( - static_cast(device_y >= 0 ? device_y : dot->property(prop_y).toInt()) * + static_cast(device_y >= 0 ? device_y : dot->property(PropY).toInt()) * (contentsRect().height() - 1) / (Core::kScreenBottomHeight - 1) + contentsMargins().top() - static_cast(dot->height()) / 2 + 0.5f)); } diff --git a/src/citra_qt/configuration/configure_touch_from_button.h b/src/citra_qt/configuration/configure_touch_from_button.h index d9a6848135..f970fb7ad7 100644 --- a/src/citra_qt/configuration/configure_touch_from_button.h +++ b/src/citra_qt/configuration/configure_touch_from_button.h @@ -37,7 +37,7 @@ class ConfigureTouchFromButton : public QDialog { public: explicit ConfigureTouchFromButton(QWidget* parent, const std::vector& touch_maps, - const int default_index = 0); + int default_index = 0); ~ConfigureTouchFromButton() override; int GetSelectedIndex() const; @@ -46,8 +46,8 @@ public: public slots: void ApplyConfiguration(); void NewBinding(const QPoint& pos); - void SetActiveBinding(const int dot_id); - void SetCoordinates(const int dot_id, const QPoint& pos); + void SetActiveBinding(int dot_id); + void SetCoordinates(int dot_id, const QPoint& pos); protected: virtual void showEvent(QShowEvent* ev) override; @@ -67,8 +67,8 @@ private: void SetConfiguration(); void UpdateUiDisplay(); void ConnectEvents(); - void GetButtonInput(const int row_index, const bool is_new); - void SetPollingResult(const Common::ParamPackage& params, const bool cancel); + void GetButtonInput(int row_index, bool is_new); + void SetPollingResult(const Common::ParamPackage& params, bool cancel); void SaveCurrentMapping(); std::unique_ptr ui; @@ -79,7 +79,7 @@ private: std::unique_ptr timeout_timer; std::unique_ptr poll_timer; std::vector> device_pollers; - std::optional> input_setter; + std::optional> input_setter; - static constexpr int data_role_dot = Qt::ItemDataRole::UserRole + 2; + static constexpr int DataRoleDot = Qt::ItemDataRole::UserRole + 2; }; diff --git a/src/citra_qt/configuration/configure_touch_from_button.ui b/src/citra_qt/configuration/configure_touch_from_button.ui index 974400c8af..d0598bdbd6 100644 --- a/src/citra_qt/configuration/configure_touch_from_button.ui +++ b/src/citra_qt/configuration/configure_touch_from_button.ui @@ -113,7 +113,7 @@ Drag points to change position, or double-click table cells to edit values. - Delete point + Delete Point diff --git a/src/citra_qt/configuration/configure_touch_widget.h b/src/citra_qt/configuration/configure_touch_widget.h index 567330fa0a..c85960f823 100644 --- a/src/citra_qt/configuration/configure_touch_widget.h +++ b/src/citra_qt/configuration/configure_touch_widget.h @@ -18,19 +18,19 @@ class TouchScreenPreview : public QFrame { Q_PROPERTY(QColor dotHighlightColor MEMBER dot_highlight_color) public: - TouchScreenPreview(QWidget* parent); + explicit TouchScreenPreview(QWidget* parent); ~TouchScreenPreview() override; - void SetCoordLabel(QLabel* const); - int AddDot(const int device_x, const int device_y); - void RemoveDot(const int id); - void HighlightDot(const int id, const bool active = true) const; - void MoveDot(const int id, const int device_x, const int device_y) const; + void SetCoordLabel(QLabel*); + int AddDot(int device_x, int device_y); + void RemoveDot(int id); + void HighlightDot(int id, bool active = true) const; + void MoveDot(int id, int device_x, int device_y) const; signals: void DotAdded(const QPoint& pos); - void DotSelected(const int dot_id); - void DotMoved(const int dot_id, const QPoint& pos); + void DotSelected(int dot_id); + void DotMoved(int dot_id, const QPoint& pos); protected: virtual void resizeEvent(QResizeEvent*) override; @@ -40,8 +40,8 @@ protected: virtual bool eventFilter(QObject*, QEvent*) override; private: - std::optional MapToDeviceCoords(const int screen_x, const int screen_y) const; - void PositionDot(QLabel* const dot, const int device_x = -1, const int device_y = -1) const; + std::optional MapToDeviceCoords(int screen_x, int screen_y) const; + void PositionDot(QLabel* dot, int device_x = -1, int device_y = -1) const; bool ignore_resize = false; QPointer coord_label; @@ -49,9 +49,9 @@ private: std::vector> dots; int max_dot_id = 0; QColor dot_highlight_color; - static constexpr char prop_id[] = "dot_id"; - static constexpr char prop_x[] = "device_x"; - static constexpr char prop_y[] = "device_y"; + static constexpr char PropId[] = "dot_id"; + static constexpr char PropX[] = "device_x"; + static constexpr char PropY[] = "device_y"; struct { bool active = false;