Qt: Initial hotkey implementation

This commit is contained in:
Connor McLaughlin 2020-01-05 12:46:03 +10:00
parent 0590f0935c
commit 40e1b7af23
15 changed files with 409 additions and 159 deletions

View File

@ -12,6 +12,10 @@ add_executable(duckstation-qt
gamelistsettingswidget.ui
gamelistwidget.cpp
gamelistwidget.h
hotkeysettingswidget.cpp
hotkeysettingswidget.h
inputbindingwidgets.cpp
inputbindingwidgets.h
main.cpp
mainwindow.cpp
mainwindow.h

View File

@ -37,6 +37,8 @@
<ItemGroup>
<ClCompile Include="consolesettingswidget.cpp" />
<ClCompile Include="gpusettingswidget.cpp" />
<ClCompile Include="hotkeysettingswidget.cpp" />
<ClCompile Include="inputbindingwidgets.cpp" />
<ClCompile Include="qtdisplaywindow.cpp" />
<ClCompile Include="gamelistsettingswidget.cpp" />
<ClCompile Include="gamelistwidget.cpp" />
@ -53,6 +55,8 @@
<QtMoc Include="portsettingswidget.h" />
<QtMoc Include="qtdisplaywindow.h" />
<QtMoc Include="gpusettingswidget.h" />
<QtMoc Include="hotkeysettingswidget.h" />
<QtMoc Include="inputbindingwidgets.h" />
<ClInclude Include="settingwidgetbinder.h" />
<QtMoc Include="consolesettingswidget.h" />
<QtMoc Include="gamelistsettingswidget.h" />
@ -105,6 +109,8 @@
<ClCompile Include="$(IntDir)moc_gamelistsettingswidget.cpp" />
<ClCompile Include="$(IntDir)moc_gamelistwidget.cpp" />
<ClCompile Include="$(IntDir)moc_gpusettingswidget.cpp" />
<ClCompile Include="$(IntDir)moc_hotkeysettingswidget.cpp" />
<ClCompile Include="$(IntDir)moc_inputbindingwidgets.cpp" />
<ClCompile Include="$(IntDir)moc_mainwindow.cpp" />
<ClCompile Include="$(IntDir)moc_opengldisplaywindow.cpp" />
<ClCompile Include="$(IntDir)moc_portsettingswidget.cpp" />

View File

@ -25,11 +25,15 @@
<ClCompile Include="$(IntDir)moc_qtdisplaywindow.cpp" />
<ClCompile Include="gpusettingswidget.cpp" />
<ClCompile Include="$(IntDir)moc_gpusettingswidget.cpp" />
<ClCompile Include="inputbindingwidgets.cpp" />
<ClCompile Include="hotkeysettingswidget.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="qtsettingsinterface.h" />
<ClInclude Include="qtutils.h" />
<ClInclude Include="settingwidgetbinder.h" />
<ClInclude Include="inputbindingwidgets.h" />
<ClInclude Include="hotkeysettingswidget.h" />
</ItemGroup>
<ItemGroup>
<Filter Include="resources">

View File

@ -0,0 +1,63 @@
#include "hotkeysettingswidget.h"
#include "core/controller.h"
#include "core/settings.h"
#include "inputbindingwidgets.h"
#include "qthostinterface.h"
#include "qtutils.h"
#include <QtCore/QTimer>
#include <QtGui/QKeyEvent>
#include <QtWidgets/QGridLayout>
#include <QtWidgets/QLabel>
HotkeySettingsWidget::HotkeySettingsWidget(QtHostInterface* host_interface, QWidget* parent /* = nullptr */)
: QWidget(parent), m_host_interface(host_interface)
{
createUi();
}
HotkeySettingsWidget::~HotkeySettingsWidget() = default;
void HotkeySettingsWidget::createUi()
{
QGridLayout* layout = new QGridLayout(this);
m_tab_widget = new QTabWidget(this);
createButtons();
layout->addWidget(m_tab_widget, 0, 0, 1, 1);
setLayout(layout);
}
void HotkeySettingsWidget::createButtons()
{
std::vector<QtHostInterface::HotkeyInfo> hotkeys = m_host_interface->getHotkeyList();
for (const QtHostInterface::HotkeyInfo& hi : hotkeys)
{
auto iter = m_categories.find(hi.category);
if (iter == m_categories.end())
{
QWidget* container = new QWidget(m_tab_widget);
QVBoxLayout* vlayout = new QVBoxLayout(container);
QGridLayout* layout = new QGridLayout();
layout->setContentsMargins(0, 0, 0, 0);
vlayout->addLayout(layout);
vlayout->addStretch(1);
iter = m_categories.insert(hi.category, Category{container, layout});
m_tab_widget->addTab(container, hi.category);
}
QWidget* container = iter->container;
QGridLayout* layout = iter->layout;
const int layout_count = layout->count() / 2;
const int target_column = (layout_count / ROWS_PER_COLUMN) * 2;
const int target_row = layout_count % ROWS_PER_COLUMN;
const QString setting_name = QStringLiteral("Hotkeys/%1").arg(hi.name);
layout->addWidget(new QLabel(hi.display_name, container), target_row, target_column);
layout->addWidget(new InputButtonBindingWidget(m_host_interface, setting_name, container), target_row,
target_column + 1);
}
}

View File

@ -0,0 +1,39 @@
#pragma once
#include "core/types.h"
#include <QtWidgets/QTabWidget>
#include <QtCore/QMap>
#include <array>
#include <vector>
class QtHostInterface;
class QGridLayout;
class HotkeySettingsWidget : public QWidget
{
Q_OBJECT
public:
HotkeySettingsWidget(QtHostInterface* host_interface, QWidget* parent = nullptr);
~HotkeySettingsWidget();
private:
enum : u32
{
ROWS_PER_COLUMN = 10
};
void createUi();
void createButtons();
QtHostInterface* m_host_interface;
QTabWidget* m_tab_widget;
struct Category
{
QWidget* container;
QGridLayout* layout;
};
QMap<QString, Category> m_categories;
};

View File

@ -0,0 +1,85 @@
#include "inputbindingwidgets.h"
#include "core/settings.h"
#include "qthostinterface.h"
#include "qtutils.h"
#include <QtCore/QTimer>
#include <QtGui/QKeyEvent>
InputButtonBindingWidget::InputButtonBindingWidget(QtHostInterface* host_interface, QString setting_name,
QWidget* parent)
: QPushButton(parent), m_host_interface(host_interface), m_setting_name(std::move(setting_name))
{
m_current_binding_value = m_host_interface->getQSettings().value(m_setting_name).toString();
setText(m_current_binding_value);
connect(this, &QPushButton::pressed, this, &InputButtonBindingWidget::onPressed);
}
InputButtonBindingWidget::~InputButtonBindingWidget() = default;
void InputButtonBindingWidget::keyPressEvent(QKeyEvent* event)
{
// ignore the key press if we're listening for input
if (isListeningForInput())
return;
QPushButton::keyPressEvent(event);
}
void InputButtonBindingWidget::keyReleaseEvent(QKeyEvent* event)
{
if (!isListeningForInput())
{
QPushButton::keyReleaseEvent(event);
return;
}
QString key_name = QtUtils::GetKeyIdentifier(event->key());
if (!key_name.isEmpty())
{
// TODO: Update input map
m_current_binding_value = QStringLiteral("Keyboard/%1").arg(key_name);
m_host_interface->getQSettings().setValue(m_setting_name, m_current_binding_value);
}
stopListeningForInput();
}
void InputButtonBindingWidget::onPressed()
{
if (isListeningForInput())
stopListeningForInput();
startListeningForInput();
}
void InputButtonBindingWidget::onInputListenTimerTimeout()
{
m_input_listen_remaining_seconds--;
if (m_input_listen_remaining_seconds == 0)
{
stopListeningForInput();
return;
}
setText(tr("Push Button... [%1]").arg(m_input_listen_remaining_seconds));
}
void InputButtonBindingWidget::startListeningForInput()
{
m_input_listen_timer = new QTimer(this);
m_input_listen_timer->setSingleShot(false);
m_input_listen_timer->start(1000);
m_input_listen_timer->connect(m_input_listen_timer, &QTimer::timeout, this,
&InputButtonBindingWidget::onInputListenTimerTimeout);
m_input_listen_remaining_seconds = 5;
setText(tr("Push Button... [%1]").arg(m_input_listen_remaining_seconds));
}
void InputButtonBindingWidget::stopListeningForInput()
{
setText(m_current_binding_value);
delete m_input_listen_timer;
m_input_listen_timer = nullptr;
}

View File

@ -0,0 +1,35 @@
#pragma once
#include "core/types.h"
#include <QtWidgets/QPushButton>
class QTimer;
class QtHostInterface;
class InputButtonBindingWidget : public QPushButton
{
Q_OBJECT
public:
InputButtonBindingWidget(QtHostInterface* host_interface, QString setting_name, QWidget* parent);
~InputButtonBindingWidget();
protected:
void keyPressEvent(QKeyEvent* event) override;
void keyReleaseEvent(QKeyEvent* event) override;
private Q_SLOTS:
void onPressed();
void onInputListenTimerTimeout();
private:
bool isListeningForInput() const { return m_input_listen_timer != nullptr; }
void startListeningForInput();
void stopListeningForInput();
QtHostInterface* m_host_interface;
QString m_setting_name;
QString m_current_binding_value;
QTimer* m_input_listen_timer = nullptr;
u32 m_input_listen_remaining_seconds = 0;
};

View File

@ -72,8 +72,9 @@
</widget>
<addaction name="actionFullscreen"/>
<addaction name="separator"/>
<addaction name="actionConsoleSettings"/>
<addaction name="actionGameListSettings"/>
<addaction name="actionHotkeySettings"/>
<addaction name="actionConsoleSettings"/>
<addaction name="actionPortSettings"/>
<addaction name="actionGPUSettings"/>
<addaction name="actionAudioSettings"/>
@ -219,13 +220,13 @@
<string>&amp;Port Settings...</string>
</property>
</action>
<action name="actionCPUSettings">
<action name="actionHotkeySettings">
<property name="icon">
<iconset resource="icons.qrc">
<normaloff>:/icons/applications-other.png</normaloff>:/icons/applications-other.png</iconset>
</property>
<property name="text">
<string>&amp;CPU Settings...</string>
<string>&amp;Hotkey Settings...</string>
</property>
</action>
<action name="actionGPUSettings">

View File

@ -1,6 +1,7 @@
#include "portsettingswidget.h"
#include "core/controller.h"
#include "core/settings.h"
#include "inputbindingwidgets.h"
#include "qthostinterface.h"
#include "qtutils.h"
#include <QtCore/QTimer>
@ -94,7 +95,7 @@ void PortSettingsWidget::createPortBindingSettingsUi(int index, PortSettingsUI*
const QString button_name_q = QString::fromStdString(button_name);
const QString setting_name = QStringLiteral("Controller%1/Button%2").arg(index + 1).arg(button_name_q);
QLabel* label = new QLabel(button_name_q, container);
InputButtonBindingWidget* button = new InputButtonBindingWidget(m_host_interface, setting_name, ctype, container);
InputButtonBindingWidget* button = new InputButtonBindingWidget(m_host_interface, setting_name, container);
layout->addWidget(label, start_row + current_row, current_column);
layout->addWidget(button, start_row + current_row, current_column + 1);
@ -129,83 +130,3 @@ void PortSettingsWidget::onControllerTypeChanged(int index)
QString::fromStdString(Settings::GetControllerTypeName(static_cast<ControllerType>(type_index))));
createPortBindingSettingsUi(index, &m_port_ui[index]);
}
InputButtonBindingWidget::InputButtonBindingWidget(QtHostInterface* host_interface, QString setting_name,
ControllerType controller_type, QWidget* parent)
: QPushButton(parent), m_host_interface(host_interface), m_setting_name(std::move(setting_name)),
m_controller_type(controller_type)
{
m_current_binding_value = m_host_interface->getQSettings().value(m_setting_name).toString();
setText(m_current_binding_value);
connect(this, &QPushButton::pressed, this, &InputButtonBindingWidget::onPressed);
}
InputButtonBindingWidget::~InputButtonBindingWidget() = default;
void InputButtonBindingWidget::keyPressEvent(QKeyEvent* event)
{
// ignore the key press if we're listening for input
if (isListeningForInput())
return;
QPushButton::keyPressEvent(event);
}
void InputButtonBindingWidget::keyReleaseEvent(QKeyEvent* event)
{
if (!isListeningForInput())
{
QPushButton::keyReleaseEvent(event);
return;
}
QString key_name = QtUtils::GetKeyIdentifier(event->key());
if (!key_name.isEmpty())
{
// TODO: Update input map
m_current_binding_value = QStringLiteral("Keyboard/%1").arg(key_name);
m_host_interface->getQSettings().setValue(m_setting_name, m_current_binding_value);
}
stopListeningForInput();
}
void InputButtonBindingWidget::onPressed()
{
if (isListeningForInput())
stopListeningForInput();
startListeningForInput();
}
void InputButtonBindingWidget::onInputListenTimerTimeout()
{
m_input_listen_remaining_seconds--;
if (m_input_listen_remaining_seconds == 0)
{
stopListeningForInput();
return;
}
setText(tr("Push Button... [%1]").arg(m_input_listen_remaining_seconds));
}
void InputButtonBindingWidget::startListeningForInput()
{
m_input_listen_timer = new QTimer(this);
m_input_listen_timer->setSingleShot(false);
m_input_listen_timer->start(1000);
m_input_listen_timer->connect(m_input_listen_timer, &QTimer::timeout, this,
&InputButtonBindingWidget::onInputListenTimerTimeout);
m_input_listen_remaining_seconds = 5;
setText(tr("Push Button... [%1]").arg(m_input_listen_remaining_seconds));
}
void InputButtonBindingWidget::stopListeningForInput()
{
setText(m_current_binding_value);
delete m_input_listen_timer;
m_input_listen_timer = nullptr;
}

View File

@ -45,33 +45,3 @@ private:
std::array<PortSettingsUI, 2> m_port_ui = {};
};
class InputButtonBindingWidget : public QPushButton
{
Q_OBJECT
public:
InputButtonBindingWidget(QtHostInterface* host_interface, QString setting_name, ControllerType controller_type,
QWidget* parent);
~InputButtonBindingWidget();
protected:
void keyPressEvent(QKeyEvent* event) override;
void keyReleaseEvent(QKeyEvent* event) override;
private Q_SLOTS:
void onPressed();
void onInputListenTimerTimeout();
private:
bool isListeningForInput() const { return m_input_listen_timer != nullptr; }
void startListeningForInput();
void stopListeningForInput();
QtHostInterface* m_host_interface;
QString m_setting_name;
QString m_current_binding_value;
ControllerType m_controller_type;
QTimer* m_input_listen_timer = nullptr;
u32 m_input_listen_remaining_seconds = 0;
};

View File

@ -1,5 +1,6 @@
#include "qthostinterface.h"
#include "YBaseLib/Log.h"
#include "YBaseLib/String.h"
#include "common/null_audio_stream.h"
#include "core/controller.h"
#include "core/game_list.h"
@ -198,6 +199,12 @@ void QtHostInterface::doUpdateInputMap()
{
m_keyboard_input_handlers.clear();
updateControllerInputMap();
updateHotkeyInputMap();
}
void QtHostInterface::updateControllerInputMap()
{
for (u32 controller_index = 0; controller_index < 2; controller_index++)
{
const ControllerType ctype = m_settings.controller_types[controller_index];
@ -212,25 +219,89 @@ void QtHostInterface::doUpdateInputMap()
if (!var.isValid())
continue;
auto handler = [this, controller_index, button_code](bool pressed) {
addButtonToInputMap(var.toString(), [this, controller_index, button_code](bool pressed) {
if (!m_system)
return;
Controller* controller = m_system->GetController(controller_index);
if (controller)
controller->SetButtonState(button_code, pressed);
});
}
}
}
std::vector<QtHostInterface::HotkeyInfo> QtHostInterface::getHotkeyList() const
{
std::vector<HotkeyInfo> hotkeys = {
{QStringLiteral("FastForward"), QStringLiteral("Toggle Fast Forward"), QStringLiteral("General")},
{QStringLiteral("Fullscreen"), QStringLiteral("Toggle Fullscreen"), QStringLiteral("General")},
{QStringLiteral("Pause"), QStringLiteral("Toggle Pause"), QStringLiteral("General")}};
for (u32 i = 1; i <= NUM_SAVE_STATE_HOTKEYS; i++)
{
hotkeys.push_back(
{QStringLiteral("LoadState%1").arg(i), QStringLiteral("Load State %1").arg(i), QStringLiteral("Save States")});
}
for (u32 i = 1; i <= NUM_SAVE_STATE_HOTKEYS; i++)
{
hotkeys.push_back(
{QStringLiteral("SaveState%1").arg(i), QStringLiteral("Save State %1").arg(i), QStringLiteral("Save States")});
}
return hotkeys;
}
void QtHostInterface::updateHotkeyInputMap()
{
auto hk = [this](const QString& hotkey_name, InputButtonHandler handler) {
QVariant var = m_qsettings.value(QStringLiteral("Hotkeys/%1").arg(hotkey_name));
if (!var.isValid())
return;
addButtonToInputMap(var.toString(), std::move(handler));
};
const QString value = var.toString();
const QString device = value.section('/', 0, 0);
const QString button = value.section('/', 1, 1);
hk(QStringLiteral("FastForward"), [this](bool pressed) {
m_speed_limiter_temp_disabled = pressed;
HostInterface::UpdateSpeedLimiterState();
});
hk(QStringLiteral("Fullscreen"), [this](bool pressed) {
if (!pressed)
toggleFullscreen();
});
hk(QStringLiteral("Pause"), [this](bool pressed) {
if (!pressed)
pauseSystem(!m_paused);
});
for (u32 i = 1; i <= NUM_SAVE_STATE_HOTKEYS; i++)
{
hk(QStringLiteral("LoadState%1").arg(i), [this, i](bool pressed) {
if (!pressed)
HostInterface::LoadState(TinyString::FromFormat("savestate_%u.bin", i));
});
hk(QStringLiteral("SaveState%1").arg(i), [this, i](bool pressed) {
if (!pressed)
HostInterface::SaveState(TinyString::FromFormat("savestate_%u.bin", i));
});
}
}
void QtHostInterface::addButtonToInputMap(const QString& binding, InputButtonHandler handler)
{
const QString device = binding.section('/', 0, 0);
const QString button = binding.section('/', 1, 1);
if (device == QStringLiteral("Keyboard"))
{
std::optional<int> key_id = QtUtils::GetKeyIdForIdentifier(button);
if (!key_id.has_value())
{
qWarning() << "Unknown keyboard key " << button;
continue;
return;
}
m_keyboard_input_handlers.emplace(key_id.value(), std::move(handler));
@ -238,12 +309,12 @@ void QtHostInterface::doUpdateInputMap()
else
{
qWarning() << "Unknown input device: " << device;
continue;
}
}
return;
}
}
void QtHostInterface::updateFullscreen() {}
void QtHostInterface::powerOffSystem()
{
if (!isOnWorkerThread())
@ -295,6 +366,18 @@ void QtHostInterface::pauseSystem(bool paused)
void QtHostInterface::changeDisc(QString new_disc_filename) {}
void QtHostInterface::toggleFullscreen()
{
if (!isOnWorkerThread())
{
QMetaObject::invokeMethod(this, "toggleFullscreen", Qt::QueuedConnection);
return;
}
m_settings.display_fullscreen = !m_settings.display_fullscreen;
updateFullscreen();
}
void QtHostInterface::doBootSystem(QString initial_filename, QString initial_save_state_filename)
{
if (!m_display_window->initializeDeviceContext())

View File

@ -8,6 +8,8 @@
#include <functional>
#include <map>
#include <memory>
#include <vector>
#include <utility>
class QWidget;
@ -49,6 +51,14 @@ public:
void updateInputMap();
void handleKeyEvent(int key, bool pressed);
struct HotkeyInfo
{
QString name;
QString display_name;
QString category;
};
std::vector<HotkeyInfo> getHotkeyList() const;
Q_SIGNALS:
void emulationStarting();
void emulationStarted();
@ -61,6 +71,7 @@ public Q_SLOTS:
void resetSystem();
void pauseSystem(bool paused);
void changeDisc(QString new_disc_filename);
void toggleFullscreen();
private Q_SLOTS:
void doStopThread();
@ -70,6 +81,13 @@ private Q_SLOTS:
void onDisplayWindowResized(int width, int height);
private:
using InputButtonHandler = std::function<void(bool)>;
enum : u32
{
NUM_SAVE_STATE_HOTKEYS = 8
};
class Thread : public QThread
{
public:
@ -85,6 +103,10 @@ private:
void checkSettings();
void createGameList();
void updateControllerInputMap();
void updateHotkeyInputMap();
void addButtonToInputMap(const QString& binding, InputButtonHandler handler);
void updateFullscreen();
void createThread();
void stopThread();
void threadEntryPoint();
@ -100,5 +122,5 @@ private:
std::atomic_bool m_shutdown_flag{false};
// input key maps, todo hotkeys
std::map<int, std::function<void(bool)>> m_keyboard_input_handlers;
std::map<int, InputButtonHandler> m_keyboard_input_handlers;
};

View File

@ -2,6 +2,7 @@
#include "consolesettingswidget.h"
#include "gamelistsettingswidget.h"
#include "gpusettingswidget.h"
#include "hotkeysettingswidget.h"
#include "portsettingswidget.h"
#include "qthostinterface.h"
#include <QtWidgets/QTextEdit>
@ -11,17 +12,19 @@ SettingsDialog::SettingsDialog(QtHostInterface* host_interface, QWidget* parent
{
m_ui.setupUi(this);
m_console_settings = new ConsoleSettingsWidget(host_interface, m_ui.settingsContainer);
m_game_list_settings = new GameListSettingsWidget(host_interface, m_ui.settingsContainer);
m_hotkey_settings = new HotkeySettingsWidget(host_interface, m_ui.settingsContainer);
m_console_settings = new ConsoleSettingsWidget(host_interface, m_ui.settingsContainer);
m_port_settings = new PortSettingsWidget(host_interface, m_ui.settingsContainer);
m_gpu_settings = new GPUSettingsWidget(host_interface, m_ui.settingsContainer);
m_audio_settings = new QWidget(m_ui.settingsContainer);
m_ui.settingsContainer->insertWidget(0, m_console_settings);
m_ui.settingsContainer->insertWidget(1, m_game_list_settings);
m_ui.settingsContainer->insertWidget(2, m_port_settings);
m_ui.settingsContainer->insertWidget(3, m_gpu_settings);
m_ui.settingsContainer->insertWidget(4, m_audio_settings);
m_ui.settingsContainer->insertWidget(0, m_game_list_settings);
m_ui.settingsContainer->insertWidget(1, m_hotkey_settings);
m_ui.settingsContainer->insertWidget(2, m_console_settings);
m_ui.settingsContainer->insertWidget(3, m_port_settings);
m_ui.settingsContainer->insertWidget(4, m_gpu_settings);
m_ui.settingsContainer->insertWidget(5, m_audio_settings);
m_ui.settingsCategory->setCurrentRow(0);
m_ui.settingsContainer->setCurrentIndex(0);

View File

@ -1,11 +1,15 @@
#pragma once
#include <QtWidgets/QDialog>
#include "ui_settingsdialog.h"
#include <QtWidgets/QDialog>
class QtHostInterface;
class GameListSettingsWidget;
class HotkeySettingsWidget;
class ConsoleSettingsWidget;
class PortSettingsWidget;
class GPUSettingsWidget;
class SettingsDialog : public QDialog
{
@ -14,15 +18,16 @@ class SettingsDialog : public QDialog
public:
enum class Category
{
ConsoleSettings,
GameListSettings,
HotkeySettings,
ConsoleSettings,
PortSettings,
GPUSettings,
AudioSettings,
Count
};
explicit SettingsDialog(QtHostInterface* host_interface, QWidget* parent = nullptr);
SettingsDialog(QtHostInterface* host_interface, QWidget* parent = nullptr);
~SettingsDialog();
public Q_SLOTS:
@ -36,10 +41,10 @@ private:
QtHostInterface* m_host_interface;
GameListSettingsWidget* m_game_list_settings = nullptr;
HotkeySettingsWidget* m_hotkey_settings = nullptr;
ConsoleSettingsWidget* m_console_settings = nullptr;
QWidget* m_game_list_settings = nullptr;
QWidget* m_port_settings = nullptr;
QWidget* m_cpu_settings = nullptr;
QWidget* m_gpu_settings = nullptr;
PortSettingsWidget* m_port_settings = nullptr;
GPUSettingsWidget* m_gpu_settings = nullptr;
QWidget* m_audio_settings = nullptr;
};

View File

@ -40,15 +40,6 @@
<height>32</height>
</size>
</property>
<item>
<property name="text">
<string>Console Settings</string>
</property>
<property name="icon">
<iconset resource="icons.qrc">
<normaloff>:/icons/utilities-system-monitor.png</normaloff>:/icons/utilities-system-monitor.png</iconset>
</property>
</item>
<item>
<property name="text">
<string>Game List Settings</string>
@ -58,6 +49,24 @@
<normaloff>:/icons/folder-open.png</normaloff>:/icons/folder-open.png</iconset>
</property>
</item>
<item>
<property name="text">
<string>Hotkey Settings</string>
</property>
<property name="icon">
<iconset resource="icons.qrc">
<normaloff>:/icons/applications-other.png</normaloff>:/icons/applications-other.png</iconset>
</property>
</item>
<item>
<property name="text">
<string>Console Settings</string>
</property>
<property name="icon">
<iconset resource="icons.qrc">
<normaloff>:/icons/utilities-system-monitor.png</normaloff>:/icons/utilities-system-monitor.png</iconset>
</property>
</item>
<item>
<property name="text">
<string>Port Settings</string>