Skip to content

Commit

Permalink
Qt: Add icons/decorations to input devices
Browse files Browse the repository at this point in the history
  • Loading branch information
stenzek committed Jan 11, 2025
1 parent 2298227 commit 2d63b34
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 24 deletions.
7 changes: 4 additions & 3 deletions src/duckstation-qt/controllerbindingwidgets.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
58 changes: 45 additions & 13 deletions src/duckstation-qt/qthost.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ void QtHost::RegisterTypes()
qRegisterMetaType<RenderAPI>("RenderAPI");
qRegisterMetaType<GPURenderer>("GPURenderer");
qRegisterMetaType<InputBindingKey>("InputBindingKey");
qRegisterMetaType<InputDeviceListModel::Device>("InputDeviceListModel::Device");
qRegisterMetaType<std::string>("std::string");
qRegisterMetaType<std::vector<std::pair<std::string, std::string>>>(
"std::vector<std::pair<std::string, std::string>>");
Expand Down Expand Up @@ -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();
Expand All @@ -2018,11 +2029,31 @@ QVariant InputDeviceListModel::data(const QModelIndex& index, int role /*= Qt::D
if (index.column() != 0 || row < 0 || static_cast<qsizetype>(row) >= m_devices.size())
return QVariant();

const auto& dev = m_devices[static_cast<qsizetype>(row)];
if (role == Qt::DisplayRole)
return QStringLiteral("%1: %2").arg(dev.first).arg(dev.second);
{
const auto& dev = m_devices[static_cast<qsizetype>(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<qsizetype>(row)];
return getIconForKey(dev.key);
}
else
{
return QVariant();
}
}

void InputDeviceListModel::enumerateDevices()
Expand All @@ -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());
Expand All @@ -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<int>(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<int>(i);
beginRemoveRows(QModelIndex(), index, index);
Expand Down Expand Up @@ -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())
{
Expand All @@ -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 &&
Expand Down
16 changes: 13 additions & 3 deletions src/duckstation-qt/qthost.h
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,14 @@ class InputDeviceListModel final : public QAbstractListModel
Q_OBJECT

public:
using DeviceList = QList<QPair<QString, QString>>;
struct Device
{
InputBindingKey key;
QString identifier;
QString display_name;
};

using DeviceList = QList<Device>;

InputDeviceListModel(QObject* parent = nullptr);
~InputDeviceListModel() override;
Expand All @@ -275,15 +282,18 @@ class InputDeviceListModel final : public QAbstractListModel
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;

// NOTE: Should only be called on EmuThread.
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);
Expand Down
7 changes: 4 additions & 3 deletions src/duckstation-qt/setupwizarddialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
});
Expand Down
4 changes: 2 additions & 2 deletions src/util/input_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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++)
{
Expand Down

0 comments on commit 2d63b34

Please sign in to comment.