From 2d63b34d48a10af08f3c5aa4595dca3a15dfedb0 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 11 Jan 2025 19:27:00 +1000 Subject: [PATCH] Qt: Add icons/decorations to input devices --- .../controllerbindingwidgets.cpp | 7 ++- src/duckstation-qt/qthost.cpp | 58 ++++++++++++++----- src/duckstation-qt/qthost.h | 16 ++++- src/duckstation-qt/setupwizarddialog.cpp | 7 ++- src/util/input_manager.cpp | 4 +- 5 files changed, 68 insertions(+), 24 deletions(-) diff --git a/src/duckstation-qt/controllerbindingwidgets.cpp b/src/duckstation-qt/controllerbindingwidgets.cpp index b03c4d886..52c1f22c1 100644 --- a/src/duckstation-qt/controllerbindingwidgets.cpp +++ b/src/duckstation-qt/controllerbindingwidgets.cpp @@ -272,11 +272,12 @@ void ControllerBindingWidget::onAutomaticBindingClicked() QMenu menu(this); bool added = false; - for (const auto& [identifier, device_name] : g_emu_thread->getInputDeviceListModel()->getDeviceList()) + for (const InputDeviceListModel::Device& dev : g_emu_thread->getInputDeviceListModel()->getDeviceList()) { // we set it as data, because the device list could get invalidated while the menu is up - QAction* action = menu.addAction(QStringLiteral("%1 (%2)").arg(identifier).arg(device_name)); - action->setData(identifier); + QAction* action = menu.addAction(QStringLiteral("%1 (%2)").arg(dev.identifier).arg(dev.display_name)); + action->setIcon(InputDeviceListModel::getIconForKey(dev.key)); + action->setData(dev.identifier); connect(action, &QAction::triggered, this, [this, action]() { doDeviceAutomaticBinding(action->data().toString()); }); added = true; diff --git a/src/duckstation-qt/qthost.cpp b/src/duckstation-qt/qthost.cpp index 6f7030cd0..72fdc1ea4 100644 --- a/src/duckstation-qt/qthost.cpp +++ b/src/duckstation-qt/qthost.cpp @@ -140,6 +140,7 @@ void QtHost::RegisterTypes() qRegisterMetaType("RenderAPI"); qRegisterMetaType("GPURenderer"); qRegisterMetaType("InputBindingKey"); + qRegisterMetaType("InputDeviceListModel::Device"); qRegisterMetaType("std::string"); qRegisterMetaType>>( "std::vector>"); @@ -2007,6 +2008,16 @@ InputDeviceListModel::InputDeviceListModel(QObject* parent) : QAbstractListModel InputDeviceListModel::~InputDeviceListModel() = default; +QIcon InputDeviceListModel::getIconForKey(const InputBindingKey& key) +{ + if (key.source_type == InputSourceType::Keyboard) + return QIcon::fromTheme("keyboard-line"); + else if (key.source_type == InputSourceType::Pointer) + return QIcon::fromTheme("mouse-line"); + else + return QIcon::fromTheme("controller-line"); +} + int InputDeviceListModel::rowCount(const QModelIndex& parent /*= QModelIndex()*/) const { return m_devices.size(); @@ -2018,11 +2029,31 @@ QVariant InputDeviceListModel::data(const QModelIndex& index, int role /*= Qt::D if (index.column() != 0 || row < 0 || static_cast(row) >= m_devices.size()) return QVariant(); - const auto& dev = m_devices[static_cast(row)]; if (role == Qt::DisplayRole) - return QStringLiteral("%1: %2").arg(dev.first).arg(dev.second); + { + const auto& dev = m_devices[static_cast(row)]; + const InputBindingKey key = dev.key; + + // don't display device names for implicit keyboard/mouse + if (key.source_type == InputSourceType::Keyboard || + (key.source_type == InputSourceType::Pointer && !InputManager::IsUsingRawInput())) + { + return dev.display_name; + } + else + { + return QStringLiteral("%1\n%2").arg(dev.identifier).arg(dev.display_name); + } + } + else if (role == Qt::DecorationRole) + { + const auto& dev = m_devices[static_cast(row)]; + return getIconForKey(dev.key); + } else + { return QVariant(); + } } void InputDeviceListModel::enumerateDevices() @@ -2035,7 +2066,7 @@ void InputDeviceListModel::enumerateDevices() DeviceList new_devices; new_devices.reserve(devices.size()); for (const auto& [key, identifier, device_name] : devices) - new_devices.emplace_back(QString::fromStdString(identifier), QString::fromStdString(device_name)); + new_devices.emplace_back(key, QString::fromStdString(identifier), QString::fromStdString(device_name)); QStringList new_motors; new_motors.reserve(motors.size()); @@ -2059,28 +2090,28 @@ void InputDeviceListModel::resetLists(const DeviceList& devices, const QStringLi endResetModel(); } -void InputDeviceListModel::onDeviceConnected(const QString& identifier, const QString& device_name, - const QStringList& vibration_motors) +void InputDeviceListModel::onDeviceConnected(const InputBindingKey& key, const QString& identifier, + const QString& device_name, const QStringList& vibration_motors) { for (const auto& it : m_devices) { - if (it.first == identifier) + if (it.identifier == identifier) return; } const int index = static_cast(m_devices.size()); beginInsertRows(QModelIndex(), index, index); - m_devices.emplace_back(identifier, device_name); + m_devices.emplace_back(key, identifier, device_name); endInsertRows(); m_vibration_motors.append(vibration_motors); } -void InputDeviceListModel::onDeviceDisconnected(const QString& identifier) +void InputDeviceListModel::onDeviceDisconnected(const InputBindingKey& key, const QString& identifier) { for (qsizetype i = 0; i < m_devices.size(); i++) { - if (m_devices[i].first == identifier) + if (m_devices[i].identifier == identifier) { const int index = static_cast(i); beginRemoveRows(QModelIndex(), index, index); @@ -2117,10 +2148,10 @@ void Host::OnInputDeviceConnected(InputBindingKey key, std::string_view identifi } } - QMetaObject::invokeMethod(g_emu_thread->getInputDeviceListModel(), "onDeviceConnected", Qt::QueuedConnection, - Q_ARG(const QString&, QtUtils::StringViewToQString(identifier)), - Q_ARG(const QString&, QtUtils::StringViewToQString(device_name)), - Q_ARG(const QStringList&, vibration_motor_list)); + QMetaObject::invokeMethod( + g_emu_thread->getInputDeviceListModel(), "onDeviceConnected", Qt::QueuedConnection, + Q_ARG(const InputBindingKey&, key), Q_ARG(const QString&, QtUtils::StringViewToQString(identifier)), + Q_ARG(const QString&, QtUtils::StringViewToQString(device_name)), Q_ARG(const QStringList&, vibration_motor_list)); if (System::IsValid() || GPUThread::IsFullscreenUIRequested()) { @@ -2133,6 +2164,7 @@ void Host::OnInputDeviceConnected(InputBindingKey key, std::string_view identifi void Host::OnInputDeviceDisconnected(InputBindingKey key, std::string_view identifier) { QMetaObject::invokeMethod(g_emu_thread->getInputDeviceListModel(), "onDeviceDisconnected", Qt::QueuedConnection, + Q_ARG(const InputBindingKey&, key), Q_ARG(const QString&, QtUtils::StringViewToQString(identifier))); if (g_settings.pause_on_controller_disconnection && System::GetState() == System::State::Running && diff --git a/src/duckstation-qt/qthost.h b/src/duckstation-qt/qthost.h index 36516c690..8503e43fe 100644 --- a/src/duckstation-qt/qthost.h +++ b/src/duckstation-qt/qthost.h @@ -266,7 +266,14 @@ class InputDeviceListModel final : public QAbstractListModel Q_OBJECT public: - using DeviceList = QList>; + struct Device + { + InputBindingKey key; + QString identifier; + QString display_name; + }; + + using DeviceList = QList; InputDeviceListModel(QObject* parent = nullptr); ~InputDeviceListModel() override; @@ -275,6 +282,8 @@ public: ALWAYS_INLINE const DeviceList& getDeviceList() const { return m_devices; } ALWAYS_INLINE const QStringList& getVibrationMotorList() const { return m_vibration_motors; } + static QIcon getIconForKey(const InputBindingKey& key); + int rowCount(const QModelIndex& parent = QModelIndex()) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; @@ -282,8 +291,9 @@ public: void enumerateDevices(); public Q_SLOTS: - void onDeviceConnected(const QString& identifier, const QString& device_name, const QStringList& vibration_motors); - void onDeviceDisconnected(const QString& identifier); + void onDeviceConnected(const InputBindingKey& key, const QString& identifier, const QString& device_name, + const QStringList& vibration_motors); + void onDeviceDisconnected(const InputBindingKey& key, const QString& identifier); private Q_SLOTS: void resetLists(const DeviceList& devices, const QStringList& motors); diff --git a/src/duckstation-qt/setupwizarddialog.cpp b/src/duckstation-qt/setupwizarddialog.cpp index 9bf299b10..1ef15168d 100644 --- a/src/duckstation-qt/setupwizarddialog.cpp +++ b/src/duckstation-qt/setupwizarddialog.cpp @@ -442,11 +442,12 @@ void SetupWizardDialog::openAutomaticMappingMenu(u32 port, QLabel* update_label) QMenu menu(this); bool added = false; - for (const auto& [identifier, device_name] : g_emu_thread->getInputDeviceListModel()->getDeviceList()) + for (const InputDeviceListModel::Device& dev : g_emu_thread->getInputDeviceListModel()->getDeviceList()) { // we set it as data, because the device list could get invalidated while the menu is up - QAction* action = menu.addAction(QStringLiteral("%1 (%2)").arg(identifier).arg(device_name)); - action->setData(identifier); + QAction* action = menu.addAction(QStringLiteral("%1 (%2)").arg(dev.identifier).arg(dev.display_name)); + action->setIcon(InputDeviceListModel::getIconForKey(dev.key)); + action->setData(dev.identifier); connect(action, &QAction::triggered, this, [this, port, update_label, action]() { doDeviceAutomaticBinding(port, update_label, action->data().toString()); }); diff --git a/src/util/input_manager.cpp b/src/util/input_manager.cpp index 798302e84..941df4b95 100644 --- a/src/util/input_manager.cpp +++ b/src/util/input_manager.cpp @@ -2000,8 +2000,8 @@ InputManager::DeviceList InputManager::EnumerateDevices() InputBindingKey mouse_key = {}; mouse_key.source_type = InputSourceType::Pointer; - ret.emplace_back(keyboard_key, "Keyboard", "Keyboard"); - ret.emplace_back(mouse_key, "Mouse", "Mouse"); + ret.emplace_back(keyboard_key, "Keyboard", TRANSLATE_STR("InputManager", "Keyboard")); + ret.emplace_back(mouse_key, GetPointerDeviceName(0), TRANSLATE_STR("InputManager", "Mouse")); for (u32 i = FIRST_EXTERNAL_INPUT_SOURCE; i < LAST_EXTERNAL_INPUT_SOURCE; i++) {