mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 13:50:03 +00:00 
			
		
		
		
	Merge pull request #4437 from adityaruplaha/hotkey-config-squashed
citra-qt: Make hotkeys configurable via the GUI (Attempt 2)
This commit is contained in:
		
						commit
						f620c862f3
					
				
					 23 changed files with 593 additions and 310 deletions
				
			
		|  | @ -41,6 +41,8 @@ add_executable(citra-qt | |||
|     configuration/configure_general.h | ||||
|     configuration/configure_graphics.cpp | ||||
|     configuration/configure_graphics.h | ||||
|     configuration/configure_hotkeys.cpp | ||||
|     configuration/configure_hotkeys.h | ||||
|     configuration/configure_input.cpp | ||||
|     configuration/configure_input.h | ||||
|     configuration/configure_motion_touch.cpp | ||||
|  | @ -109,8 +111,10 @@ add_executable(citra-qt | |||
|     updater/updater.cpp | ||||
|     updater/updater.h | ||||
|     updater/updater_p.h | ||||
|     util/clickable_label.h | ||||
|     util/clickable_label.cpp | ||||
|     util/clickable_label.h | ||||
|     util/sequence_dialog/sequence_dialog.cpp | ||||
|     util/sequence_dialog/sequence_dialog.h | ||||
|     util/spinbox.cpp | ||||
|     util/spinbox.h | ||||
|     util/util.cpp | ||||
|  | @ -126,6 +130,7 @@ set(UIS | |||
|     configuration/configure_debug.ui | ||||
|     configuration/configure_general.ui | ||||
|     configuration/configure_graphics.ui | ||||
|     configuration/configure_hotkeys.ui | ||||
|     configuration/configure_input.ui | ||||
|     configuration/configure_motion_touch.ui | ||||
|     configuration/configure_system.ui | ||||
|  | @ -140,7 +145,6 @@ set(UIS | |||
|     multiplayer/moderation_dialog.ui | ||||
|     aboutdialog.ui | ||||
|     cheats.ui | ||||
|     hotkeys.ui | ||||
|     main.ui | ||||
|     compatdb.ui | ||||
| ) | ||||
|  |  | |||
|  | @ -3,10 +3,11 @@ | |||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <algorithm> | ||||
| #include <array> | ||||
| #include <unordered_map> | ||||
| #include <QKeySequence> | ||||
| #include <QSettings> | ||||
| #include "citra_qt/configuration/config.h" | ||||
| #include "citra_qt/ui_settings.h" | ||||
| #include "common/file_util.h" | ||||
| #include "core/hle/service/service.h" | ||||
| #include "input_common/main.h" | ||||
|  | @ -19,7 +20,6 @@ Config::Config() { | |||
|     FileUtil::CreateFullPath(qt_config_loc); | ||||
|     qt_config = | ||||
|         std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat); | ||||
| 
 | ||||
|     Reload(); | ||||
| } | ||||
| 
 | ||||
|  | @ -50,6 +50,31 @@ const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> Config: | |||
|     }, | ||||
| }}; | ||||
| 
 | ||||
| // This shouldn't have anything except static initializers (no functions). So
 | ||||
| // QKeySequnce(...).toString() is NOT ALLOWED HERE.
 | ||||
| // This must be in alphabetical order according to action name as it must have the same order as
 | ||||
| // UISetting::values.shortcuts, which is alphabetically ordered.
 | ||||
| const std::array<UISettings::Shortcut, 19> Config::default_hotkeys{ | ||||
|     {{"Advance Frame", "Main Window", {"\\", Qt::ApplicationShortcut}}, | ||||
|      {"Capture Screenshot", "Main Window", {"Ctrl+P", Qt::ApplicationShortcut}}, | ||||
|      {"Continue/Pause Emulation", "Main Window", {"F4", Qt::WindowShortcut}}, | ||||
|      {"Decrease Speed Limit", "Main Window", {"-", Qt::ApplicationShortcut}}, | ||||
|      {"Exit Citra", "Main Window", {"Ctrl+Q", Qt::WindowShortcut}}, | ||||
|      {"Exit Fullscreen", "Main Window", {"Esc", Qt::WindowShortcut}}, | ||||
|      {"Fullscreen", "Main Window", {"F11", Qt::WindowShortcut}}, | ||||
|      {"Increase Speed Limit", "Main Window", {"+", Qt::ApplicationShortcut}}, | ||||
|      {"Load Amiibo", "Main Window", {"F2", Qt::ApplicationShortcut}}, | ||||
|      {"Load File", "Main Window", {"Ctrl+O", Qt::WindowShortcut}}, | ||||
|      {"Remove Amiibo", "Main Window", {"F3", Qt::ApplicationShortcut}}, | ||||
|      {"Restart Emulation", "Main Window", {"F6", Qt::WindowShortcut}}, | ||||
|      {"Stop Emulation", "Main Window", {"F5", Qt::WindowShortcut}}, | ||||
|      {"Swap Screens", "Main Window", {"F9", Qt::WindowShortcut}}, | ||||
|      {"Toggle Filter Bar", "Main Window", {"Ctrl+F", Qt::WindowShortcut}}, | ||||
|      {"Toggle Frame Advancing", "Main Window", {"Ctrl+A", Qt::ApplicationShortcut}}, | ||||
|      {"Toggle Screen Layout", "Main Window", {"F10", Qt::WindowShortcut}}, | ||||
|      {"Toggle Speed Limit", "Main Window", {"Ctrl+Z", Qt::ApplicationShortcut}}, | ||||
|      {"Toggle Status Bar", "Main Window", {"Ctrl+S", Qt::WindowShortcut}}}}; | ||||
| 
 | ||||
| void Config::ReadValues() { | ||||
|     qt_config->beginGroup("Controls"); | ||||
| 
 | ||||
|  | @ -318,20 +343,15 @@ void Config::ReadValues() { | |||
|     qt_config->endGroup(); | ||||
| 
 | ||||
|     qt_config->beginGroup("Shortcuts"); | ||||
|     QStringList groups = qt_config->childGroups(); | ||||
|     for (auto group : groups) { | ||||
|     for (auto [name, group, shortcut] : default_hotkeys) { | ||||
|         auto [keyseq, context] = shortcut; | ||||
|         qt_config->beginGroup(group); | ||||
| 
 | ||||
|         QStringList hotkeys = qt_config->childGroups(); | ||||
|         for (auto hotkey : hotkeys) { | ||||
|             qt_config->beginGroup(hotkey); | ||||
|             UISettings::values.shortcuts.emplace_back(UISettings::Shortcut( | ||||
|                 group + "/" + hotkey, | ||||
|                 UISettings::ContextualShortcut(ReadSetting("KeySeq").toString(), | ||||
|                                                ReadSetting("Context").toInt()))); | ||||
|             qt_config->endGroup(); | ||||
|         } | ||||
| 
 | ||||
|         qt_config->beginGroup(name); | ||||
|         UISettings::values.shortcuts.push_back( | ||||
|             {name, | ||||
|              group, | ||||
|              {ReadSetting("KeySeq", keyseq).toString(), ReadSetting("Context", context).toInt()}}); | ||||
|         qt_config->endGroup(); | ||||
|         qt_config->endGroup(); | ||||
|     } | ||||
|     qt_config->endGroup(); | ||||
|  | @ -563,9 +583,16 @@ void Config::SaveValues() { | |||
|     qt_config->endGroup(); | ||||
| 
 | ||||
|     qt_config->beginGroup("Shortcuts"); | ||||
|     for (auto shortcut : UISettings::values.shortcuts) { | ||||
|         WriteSetting(shortcut.first + "/KeySeq", shortcut.second.first); | ||||
|         WriteSetting(shortcut.first + "/Context", shortcut.second.second); | ||||
|     // Lengths of UISettings::values.shortcuts & default_hotkeys are same.
 | ||||
|     // However, their ordering must also be the same.
 | ||||
|     for (std::size_t i = 0; i < default_hotkeys.size(); i++) { | ||||
|         auto [name, group, shortcut] = UISettings::values.shortcuts[i]; | ||||
|         qt_config->beginGroup(group); | ||||
|         qt_config->beginGroup(name); | ||||
|         WriteSetting("KeySeq", shortcut.first, default_hotkeys[i].shortcut.first); | ||||
|         WriteSetting("Context", shortcut.second, default_hotkeys[i].shortcut.second); | ||||
|         qt_config->endGroup(); | ||||
|         qt_config->endGroup(); | ||||
|     } | ||||
|     qt_config->endGroup(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -8,6 +8,7 @@ | |||
| #include <memory> | ||||
| #include <string> | ||||
| #include <QVariant> | ||||
| #include "citra_qt/ui_settings.h" | ||||
| #include "core/settings.h" | ||||
| 
 | ||||
| class QSettings; | ||||
|  | @ -31,6 +32,8 @@ private: | |||
|     void WriteSetting(const QString& name, const QVariant& value); | ||||
|     void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value); | ||||
| 
 | ||||
|     static const std::array<UISettings::Shortcut, 19> default_hotkeys; | ||||
| 
 | ||||
|     std::unique_ptr<QSettings> qt_config; | ||||
|     std::string qt_config_loc; | ||||
| }; | ||||
|  |  | |||
|  | @ -38,6 +38,11 @@ | |||
|          <string>Input</string> | ||||
|         </attribute> | ||||
|        </widget> | ||||
|        <widget class="ConfigureHotkeys" name="hotkeysTab"> | ||||
|         <attribute name="title"> | ||||
|          <string>Hotkeys</string> | ||||
|         </attribute> | ||||
|        </widget> | ||||
|        <widget class="ConfigureGraphics" name="graphicsTab"> | ||||
|         <attribute name="title"> | ||||
|          <string>Graphics</string> | ||||
|  | @ -118,6 +123,12 @@ | |||
|    <header>configuration/configure_input.h</header> | ||||
|    <container>1</container> | ||||
|   </customwidget> | ||||
|   <customwidget> | ||||
|    <class>ConfigureHotkeys</class> | ||||
|    <extends>QWidget</extends> | ||||
|    <header>configuration/configure_hotkeys.h</header> | ||||
|    <container>1</container> | ||||
|   </customwidget> | ||||
|   <customwidget> | ||||
|    <class>ConfigureGraphics</class> | ||||
|    <extends>QWidget</extends> | ||||
|  |  | |||
|  | @ -10,18 +10,27 @@ | |||
| #include "core/settings.h" | ||||
| #include "ui_configure.h" | ||||
| 
 | ||||
| ConfigureDialog::ConfigureDialog(QWidget* parent, const HotkeyRegistry& registry) | ||||
|     : QDialog(parent), ui(new Ui::ConfigureDialog) { | ||||
| ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry) | ||||
|     : QDialog(parent), registry(registry), ui(new Ui::ConfigureDialog) { | ||||
|     ui->setupUi(this); | ||||
|     ui->generalTab->PopulateHotkeyList(registry); | ||||
|     ui->hotkeysTab->Populate(registry); | ||||
| 
 | ||||
|     this->PopulateSelectionList(); | ||||
|     connect(ui->uiTab, &ConfigureUi::languageChanged, this, &ConfigureDialog::onLanguageChanged); | ||||
|     connect(ui->selectorList, &QListWidget::itemSelectionChanged, this, | ||||
|             &ConfigureDialog::UpdateVisibleTabs); | ||||
| 
 | ||||
|     adjustSize(); | ||||
| 
 | ||||
|     ui->selectorList->setCurrentRow(0); | ||||
| 
 | ||||
|     // Set up used key list synchronisation
 | ||||
|     connect(ui->inputTab, &ConfigureInput::InputKeysChanged, ui->hotkeysTab, | ||||
|             &ConfigureHotkeys::OnInputKeysChanged); | ||||
|     connect(ui->hotkeysTab, &ConfigureHotkeys::HotkeysChanged, ui->inputTab, | ||||
|             &ConfigureInput::OnHotkeysChanged); | ||||
| 
 | ||||
|     // Synchronise lists upon initialisation
 | ||||
|     ui->inputTab->EmitInputKeysChanged(); | ||||
|     ui->hotkeysTab->EmitHotkeysChanged(); | ||||
| } | ||||
| 
 | ||||
| ConfigureDialog::~ConfigureDialog() = default; | ||||
|  | @ -43,6 +52,7 @@ void ConfigureDialog::applyConfiguration() { | |||
|     ui->systemTab->applyConfiguration(); | ||||
|     ui->inputTab->applyConfiguration(); | ||||
|     ui->inputTab->ApplyProfile(); | ||||
|     ui->hotkeysTab->applyConfiguration(registry); | ||||
|     ui->graphicsTab->applyConfiguration(); | ||||
|     ui->audioTab->applyConfiguration(); | ||||
|     ui->cameraTab->applyConfiguration(); | ||||
|  | @ -61,7 +71,7 @@ void ConfigureDialog::PopulateSelectionList() { | |||
|           {QT_TR_NOOP("General"), QT_TR_NOOP("Web"), QT_TR_NOOP("Debug"), QT_TR_NOOP("UI")}}, | ||||
|          {tr("System"), {QT_TR_NOOP("System"), QT_TR_NOOP("Audio"), QT_TR_NOOP("Camera")}}, | ||||
|          {tr("Graphics"), {QT_TR_NOOP("Graphics")}}, | ||||
|          {tr("Controls"), {QT_TR_NOOP("Input")}}}}; | ||||
|          {tr("Controls"), {QT_TR_NOOP("Input"), QT_TR_NOOP("Hotkeys")}}}}; | ||||
| 
 | ||||
|     for (const auto& entry : items) { | ||||
|         auto* item = new QListWidgetItem(entry.first); | ||||
|  | @ -91,6 +101,7 @@ void ConfigureDialog::retranslateUi() { | |||
|     ui->generalTab->retranslateUi(); | ||||
|     ui->systemTab->retranslateUi(); | ||||
|     ui->inputTab->retranslateUi(); | ||||
|     ui->hotkeysTab->retranslateUi(); | ||||
|     ui->graphicsTab->retranslateUi(); | ||||
|     ui->audioTab->retranslateUi(); | ||||
|     ui->cameraTab->retranslateUi(); | ||||
|  | @ -105,9 +116,11 @@ void ConfigureDialog::UpdateVisibleTabs() { | |||
|         return; | ||||
| 
 | ||||
|     const QHash<QString, QWidget*> widgets = { | ||||
|         {"General", ui->generalTab},   {"System", ui->systemTab}, {"Input", ui->inputTab}, | ||||
|         {"Graphics", ui->graphicsTab}, {"Audio", ui->audioTab},   {"Camera", ui->cameraTab}, | ||||
|         {"Debug", ui->debugTab},       {"Web", ui->webTab},       {"UI", ui->uiTab}}; | ||||
|         {"General", ui->generalTab},   {"System", ui->systemTab}, | ||||
|         {"Input", ui->inputTab},       {"Hotkeys", ui->hotkeysTab}, | ||||
|         {"Graphics", ui->graphicsTab}, {"Audio", ui->audioTab}, | ||||
|         {"Camera", ui->cameraTab},     {"Debug", ui->debugTab}, | ||||
|         {"Web", ui->webTab},           {"UI", ui->uiTab}}; | ||||
| 
 | ||||
|     ui->tabWidget->clear(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -17,7 +17,7 @@ class ConfigureDialog : public QDialog { | |||
|     Q_OBJECT | ||||
| 
 | ||||
| public: | ||||
|     explicit ConfigureDialog(QWidget* parent, const HotkeyRegistry& registry); | ||||
|     explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry); | ||||
|     ~ConfigureDialog() override; | ||||
| 
 | ||||
|     void applyConfiguration(); | ||||
|  | @ -35,4 +35,5 @@ private: | |||
|     void retranslateUi(); | ||||
| 
 | ||||
|     std::unique_ptr<Ui::ConfigureDialog> ui; | ||||
|     HotkeyRegistry& registry; | ||||
| }; | ||||
|  |  | |||
|  | @ -32,10 +32,6 @@ void ConfigureGeneral::setConfiguration() { | |||
|     ui->region_combobox->setCurrentIndex(Settings::values.region_value + 1); | ||||
| } | ||||
| 
 | ||||
| void ConfigureGeneral::PopulateHotkeyList(const HotkeyRegistry& registry) { | ||||
|     ui->hotkeysDialog->Populate(registry); | ||||
| } | ||||
| 
 | ||||
| void ConfigureGeneral::ResetDefaults() { | ||||
|     QMessageBox::StandardButton answer = QMessageBox::question( | ||||
|         this, tr("Citra"), | ||||
|  | @ -60,5 +56,4 @@ void ConfigureGeneral::applyConfiguration() { | |||
| 
 | ||||
| void ConfigureGeneral::retranslateUi() { | ||||
|     ui->retranslateUi(this); | ||||
|     ui->hotkeysDialog->retranslateUi(); | ||||
| } | ||||
|  |  | |||
|  | @ -20,7 +20,6 @@ public: | |||
|     explicit ConfigureGeneral(QWidget* parent = nullptr); | ||||
|     ~ConfigureGeneral() override; | ||||
| 
 | ||||
|     void PopulateHotkeyList(const HotkeyRegistry& registry); | ||||
|     void ResetDefaults(); | ||||
|     void applyConfiguration(); | ||||
|     void retranslateUi(); | ||||
|  |  | |||
|  | @ -7,7 +7,7 @@ | |||
|     <x>0</x> | ||||
|     <y>0</y> | ||||
|     <width>345</width> | ||||
|     <height>504</height> | ||||
|     <height>357</height> | ||||
|    </rect> | ||||
|   </property> | ||||
|   <property name="windowTitle"> | ||||
|  | @ -21,17 +21,13 @@ | |||
|        <property name="title"> | ||||
|         <string>General</string> | ||||
|        </property> | ||||
|        <layout class="QHBoxLayout" name="horizontalLayout_3"> | ||||
|        <layout class="QVBoxLayout" name="verticalLayout_5"> | ||||
|         <item> | ||||
|          <layout class="QVBoxLayout" name="verticalLayout_2"> | ||||
|           <item> | ||||
|            <widget class="QCheckBox" name="toggle_check_exit"> | ||||
|             <property name="text"> | ||||
|              <string>Confirm exit while emulation is running</string> | ||||
|             </property> | ||||
|            </widget> | ||||
|           </item> | ||||
|          </layout> | ||||
|          <widget class="QCheckBox" name="toggle_check_exit"> | ||||
|           <property name="text"> | ||||
|            <string>Confirm exit while emulation is running</string> | ||||
|           </property> | ||||
|          </widget> | ||||
|         </item> | ||||
|        </layout> | ||||
|       </widget> | ||||
|  | @ -41,24 +37,20 @@ | |||
|        <property name="title"> | ||||
|         <string>Updates</string> | ||||
|        </property> | ||||
|        <layout class="QHBoxLayout" name="horizontalLayout_update"> | ||||
|        <layout class="QVBoxLayout" name="verticalLayout_4"> | ||||
|         <item> | ||||
|          <layout class="QVBoxLayout" name="verticalLayout_update"> | ||||
|           <item> | ||||
|            <widget class="QCheckBox" name="toggle_update_check"> | ||||
|             <property name="text"> | ||||
|              <string>Check for updates on start</string> | ||||
|             </property> | ||||
|            </widget> | ||||
|           </item> | ||||
|           <item> | ||||
|            <widget class="QCheckBox" name="toggle_auto_update"> | ||||
|             <property name="text"> | ||||
|              <string>Silently auto update after closing</string> | ||||
|             </property> | ||||
|            </widget> | ||||
|           </item> | ||||
|          </layout> | ||||
|          <widget class="QCheckBox" name="toggle_update_check"> | ||||
|           <property name="text"> | ||||
|            <string>Check for updates on start</string> | ||||
|           </property> | ||||
|          </widget> | ||||
|         </item> | ||||
|         <item> | ||||
|          <widget class="QCheckBox" name="toggle_auto_update"> | ||||
|           <property name="text"> | ||||
|            <string>Silently auto update after closing</string> | ||||
|           </property> | ||||
|          </widget> | ||||
|         </item> | ||||
|        </layout> | ||||
|       </widget> | ||||
|  | @ -68,81 +60,57 @@ | |||
|        <property name="title"> | ||||
|         <string>Emulation</string> | ||||
|        </property> | ||||
|        <layout class="QHBoxLayout" name="horizontalLayout_5"> | ||||
|         <item> | ||||
|          <layout class="QVBoxLayout" name="verticalLayout_6"> | ||||
|           <item> | ||||
|            <layout class="QHBoxLayout" name="horizontalLayout_6"> | ||||
|             <item> | ||||
|              <widget class="QLabel" name="label"> | ||||
|               <property name="text"> | ||||
|                <string>Region:</string> | ||||
|               </property> | ||||
|              </widget> | ||||
|             </item> | ||||
|             <item> | ||||
|              <widget class="QComboBox" name="region_combobox"> | ||||
|               <item> | ||||
|                <property name="text"> | ||||
|                 <string>Auto-select</string> | ||||
|                </property> | ||||
|               </item> | ||||
|               <item> | ||||
|                <property name="text"> | ||||
|                 <string notr="true">JPN</string> | ||||
|                </property> | ||||
|               </item> | ||||
|               <item> | ||||
|                <property name="text"> | ||||
|                 <string notr="true">USA</string> | ||||
|                </property> | ||||
|               </item> | ||||
|               <item> | ||||
|                <property name="text"> | ||||
|                 <string notr="true">EUR</string> | ||||
|                </property> | ||||
|               </item> | ||||
|               <item> | ||||
|                <property name="text"> | ||||
|                 <string notr="true">AUS</string> | ||||
|                </property> | ||||
|               </item> | ||||
|               <item> | ||||
|                <property name="text"> | ||||
|                 <string notr="true">CHN</string> | ||||
|                </property> | ||||
|               </item> | ||||
|               <item> | ||||
|                <property name="text"> | ||||
|                 <string notr="true">KOR</string> | ||||
|                </property> | ||||
|               </item> | ||||
|               <item> | ||||
|                <property name="text"> | ||||
|                 <string notr="true">TWN</string> | ||||
|                </property> | ||||
|               </item> | ||||
|              </widget> | ||||
|             </item> | ||||
|            </layout> | ||||
|           </item> | ||||
|          </layout> | ||||
|        <layout class="QGridLayout" name="gridLayout"> | ||||
|         <item row="0" column="0"> | ||||
|          <widget class="QLabel" name="label"> | ||||
|           <property name="text"> | ||||
|            <string>Region:</string> | ||||
|           </property> | ||||
|          </widget> | ||||
|         </item> | ||||
|        </layout> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <widget class="QGroupBox" name="groupBox_3"> | ||||
|        <property name="title"> | ||||
|         <string>Hotkeys</string> | ||||
|        </property> | ||||
|        <layout class="QHBoxLayout" name="horizontalLayout_4"> | ||||
|         <item> | ||||
|          <layout class="QVBoxLayout" name="verticalLayout_4"> | ||||
|         <item row="0" column="1"> | ||||
|          <widget class="QComboBox" name="region_combobox"> | ||||
|           <item> | ||||
|            <widget class="GHotkeysDialog" name="hotkeysDialog" native="true"/> | ||||
|            <property name="text"> | ||||
|             <string>Auto-select</string> | ||||
|            </property> | ||||
|           </item> | ||||
|          </layout> | ||||
|           <item> | ||||
|            <property name="text"> | ||||
|             <string notr="true">JPN</string> | ||||
|            </property> | ||||
|           </item> | ||||
|           <item> | ||||
|            <property name="text"> | ||||
|             <string notr="true">USA</string> | ||||
|            </property> | ||||
|           </item> | ||||
|           <item> | ||||
|            <property name="text"> | ||||
|             <string notr="true">EUR</string> | ||||
|            </property> | ||||
|           </item> | ||||
|           <item> | ||||
|            <property name="text"> | ||||
|             <string notr="true">AUS</string> | ||||
|            </property> | ||||
|           </item> | ||||
|           <item> | ||||
|            <property name="text"> | ||||
|             <string notr="true">CHN</string> | ||||
|            </property> | ||||
|           </item> | ||||
|           <item> | ||||
|            <property name="text"> | ||||
|             <string notr="true">KOR</string> | ||||
|            </property> | ||||
|           </item> | ||||
|           <item> | ||||
|            <property name="text"> | ||||
|             <string notr="true">TWN</string> | ||||
|            </property> | ||||
|           </item> | ||||
|          </widget> | ||||
|         </item> | ||||
|        </layout> | ||||
|       </widget> | ||||
|  | @ -154,18 +122,23 @@ | |||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <spacer name="verticalSpacer"> | ||||
|        <property name="orientation"> | ||||
|         <enum>Qt::Vertical</enum> | ||||
|        </property> | ||||
|        <property name="sizeHint" stdset="0"> | ||||
|         <size> | ||||
|          <width>20</width> | ||||
|          <height>40</height> | ||||
|         </size> | ||||
|        </property> | ||||
|       </spacer> | ||||
|      </item> | ||||
|     </layout> | ||||
|    </item> | ||||
|   </layout> | ||||
|  </widget> | ||||
|  <customwidgets> | ||||
|   <customwidget> | ||||
|    <class>GHotkeysDialog</class> | ||||
|    <extends>QWidget</extends> | ||||
|    <header>hotkeys.h</header> | ||||
|    <container>1</container> | ||||
|   </customwidget> | ||||
|  </customwidgets> | ||||
|  <resources/> | ||||
|  <connections/> | ||||
| </ui> | ||||
|  |  | |||
							
								
								
									
										125
									
								
								src/citra_qt/configuration/configure_hotkeys.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								src/citra_qt/configuration/configure_hotkeys.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,125 @@ | |||
| // Copyright 2017 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <QMessageBox> | ||||
| #include <QStandardItemModel> | ||||
| #include "citra_qt/configuration/configure_hotkeys.h" | ||||
| #include "citra_qt/hotkeys.h" | ||||
| #include "citra_qt/util/sequence_dialog/sequence_dialog.h" | ||||
| #include "core/settings.h" | ||||
| #include "ui_configure_hotkeys.h" | ||||
| 
 | ||||
| ConfigureHotkeys::ConfigureHotkeys(QWidget* parent) | ||||
|     : QWidget(parent), ui(std::make_unique<Ui::ConfigureHotkeys>()) { | ||||
|     ui->setupUi(this); | ||||
|     setFocusPolicy(Qt::ClickFocus); | ||||
| 
 | ||||
|     model = new QStandardItemModel(this); | ||||
|     model->setColumnCount(3); | ||||
|     model->setHorizontalHeaderLabels({tr("Action"), tr("Hotkey"), tr("Context")}); | ||||
| 
 | ||||
|     connect(ui->hotkey_list, &QTreeView::doubleClicked, this, &ConfigureHotkeys::Configure); | ||||
|     ui->hotkey_list->setModel(model); | ||||
| 
 | ||||
|     // TODO(Kloen): Make context configurable as well (hiding the column for now)
 | ||||
|     ui->hotkey_list->hideColumn(2); | ||||
| 
 | ||||
|     ui->hotkey_list->setColumnWidth(0, 200); | ||||
|     ui->hotkey_list->resizeColumnToContents(1); | ||||
| } | ||||
| 
 | ||||
| ConfigureHotkeys::~ConfigureHotkeys() = default; | ||||
| 
 | ||||
| void ConfigureHotkeys::EmitHotkeysChanged() { | ||||
|     emit HotkeysChanged(GetUsedKeyList()); | ||||
| } | ||||
| 
 | ||||
| QList<QKeySequence> ConfigureHotkeys::GetUsedKeyList() { | ||||
|     QList<QKeySequence> list; | ||||
|     for (int r = 0; r < model->rowCount(); r++) { | ||||
|         QStandardItem* parent = model->item(r, 0); | ||||
|         for (int r2 = 0; r2 < parent->rowCount(); r2++) { | ||||
|             QStandardItem* keyseq = parent->child(r2, 1); | ||||
|             list << QKeySequence::fromString(keyseq->text(), QKeySequence::NativeText); | ||||
|         } | ||||
|     } | ||||
|     return list; | ||||
| } | ||||
| 
 | ||||
| void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) { | ||||
|     for (const auto& group : registry.hotkey_groups) { | ||||
|         QStandardItem* parent_item = new QStandardItem(group.first); | ||||
|         parent_item->setEditable(false); | ||||
|         for (const auto& hotkey : group.second) { | ||||
|             QStandardItem* action = new QStandardItem(hotkey.first); | ||||
|             QStandardItem* keyseq = | ||||
|                 new QStandardItem(hotkey.second.keyseq.toString(QKeySequence::NativeText)); | ||||
|             action->setEditable(false); | ||||
|             keyseq->setEditable(false); | ||||
|             parent_item->appendRow({action, keyseq}); | ||||
|         } | ||||
|         model->appendRow(parent_item); | ||||
|     } | ||||
| 
 | ||||
|     ui->hotkey_list->expandAll(); | ||||
| } | ||||
| 
 | ||||
| void ConfigureHotkeys::OnInputKeysChanged(QList<QKeySequence> new_key_list) { | ||||
|     input_keys_list = new_key_list; | ||||
| } | ||||
| 
 | ||||
| void ConfigureHotkeys::Configure(QModelIndex index) { | ||||
|     if (index.parent() == QModelIndex()) | ||||
|         return; | ||||
| 
 | ||||
|     index = index.sibling(index.row(), 1); | ||||
|     auto* model = ui->hotkey_list->model(); | ||||
|     auto previous_key = model->data(index); | ||||
| 
 | ||||
|     auto* hotkey_dialog = new SequenceDialog; | ||||
|     int return_code = hotkey_dialog->exec(); | ||||
| 
 | ||||
|     auto key_sequence = hotkey_dialog->GetSequence(); | ||||
| 
 | ||||
|     if (return_code == QDialog::Rejected || key_sequence.isEmpty()) | ||||
|         return; | ||||
| 
 | ||||
|     if (IsUsedKey(key_sequence) && key_sequence != QKeySequence(previous_key.toString())) { | ||||
|         QMessageBox::critical(this, tr("Error in inputted key"), | ||||
|                               tr("You're using a key that's already bound.")); | ||||
|     } else { | ||||
|         model->setData(index, key_sequence.toString(QKeySequence::NativeText)); | ||||
|         EmitHotkeysChanged(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool ConfigureHotkeys::IsUsedKey(QKeySequence key_sequence) { | ||||
|     return input_keys_list.contains(key_sequence) || GetUsedKeyList().contains(key_sequence); | ||||
| } | ||||
| 
 | ||||
| void ConfigureHotkeys::applyConfiguration(HotkeyRegistry& registry) { | ||||
|     for (int key_id = 0; key_id < model->rowCount(); key_id++) { | ||||
|         QStandardItem* parent = model->item(key_id, 0); | ||||
|         for (int key_column_id = 0; key_column_id < parent->rowCount(); key_column_id++) { | ||||
|             QStandardItem* action = parent->child(key_column_id, 0); | ||||
|             QStandardItem* keyseq = parent->child(key_column_id, 1); | ||||
|             for (auto& [group, sub_actions] : registry.hotkey_groups) { | ||||
|                 if (group != parent->text()) | ||||
|                     continue; | ||||
|                 for (auto& [action_name, hotkey] : sub_actions) { | ||||
|                     if (action_name != action->text()) | ||||
|                         continue; | ||||
|                     hotkey.keyseq = QKeySequence(keyseq->text()); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     registry.SaveHotkeys(); | ||||
|     Settings::Apply(); | ||||
| } | ||||
| 
 | ||||
| void ConfigureHotkeys::retranslateUi() { | ||||
|     ui->retranslateUi(this); | ||||
| } | ||||
							
								
								
									
										58
									
								
								src/citra_qt/configuration/configure_hotkeys.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								src/citra_qt/configuration/configure_hotkeys.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,58 @@ | |||
| // Copyright 2017 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <memory> | ||||
| #include <QWidget> | ||||
| #include "core/settings.h" | ||||
| 
 | ||||
| namespace Ui { | ||||
| class ConfigureHotkeys; | ||||
| } | ||||
| 
 | ||||
| class HotkeyRegistry; | ||||
| class QStandardItemModel; | ||||
| 
 | ||||
| class ConfigureHotkeys : public QWidget { | ||||
|     Q_OBJECT | ||||
| 
 | ||||
| public: | ||||
|     explicit ConfigureHotkeys(QWidget* parent = nullptr); | ||||
|     ~ConfigureHotkeys(); | ||||
| 
 | ||||
|     void applyConfiguration(HotkeyRegistry& registry); | ||||
|     void retranslateUi(); | ||||
| 
 | ||||
|     void EmitHotkeysChanged(); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Populates the hotkey list widget using data from the provided registry. | ||||
|      * Called everytime the Configure dialog is opened. | ||||
|      * @param registry The HotkeyRegistry whose data is used to populate the list. | ||||
|      */ | ||||
|     void Populate(const HotkeyRegistry& registry); | ||||
| 
 | ||||
| public slots: | ||||
|     void OnInputKeysChanged(QList<QKeySequence> new_key_list); | ||||
| 
 | ||||
| signals: | ||||
|     void HotkeysChanged(QList<QKeySequence> new_key_list); | ||||
| 
 | ||||
| private: | ||||
|     void Configure(QModelIndex index); | ||||
|     bool IsUsedKey(QKeySequence key_sequence); | ||||
|     QList<QKeySequence> GetUsedKeyList(); | ||||
| 
 | ||||
|     std::unique_ptr<Ui::ConfigureHotkeys> ui; | ||||
| 
 | ||||
|     /**
 | ||||
|      * List of keyboard keys currently registered to any of the 3DS inputs. | ||||
|      * These can't be bound to any hotkey. | ||||
|      * Synchronised with ConfigureInput via signal-slot. | ||||
|      */ | ||||
|     QList<QKeySequence> input_keys_list; | ||||
| 
 | ||||
|     QStandardItemModel* model; | ||||
| }; | ||||
							
								
								
									
										42
									
								
								src/citra_qt/configuration/configure_hotkeys.ui
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/citra_qt/configuration/configure_hotkeys.ui
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,42 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <ui version="4.0"> | ||||
|  <class>ConfigureHotkeys</class> | ||||
|  <widget class="QWidget" name="ConfigureHotkeys"> | ||||
|   <property name="geometry"> | ||||
|    <rect> | ||||
|     <x>0</x> | ||||
|     <y>0</y> | ||||
|     <width>363</width> | ||||
|     <height>388</height> | ||||
|    </rect> | ||||
|   </property> | ||||
|   <property name="windowTitle"> | ||||
|    <string>Hotkey Settings</string> | ||||
|   </property> | ||||
|   <layout class="QVBoxLayout" name="verticalLayout"> | ||||
|    <item> | ||||
|     <layout class="QVBoxLayout" name="verticalLayout_2"> | ||||
|      <item> | ||||
|       <widget class="QLabel" name="label_2"> | ||||
|        <property name="text"> | ||||
|         <string>Double-click on a binding to change it.</string> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <widget class="QTreeView" name="hotkey_list"> | ||||
|        <property name="editTriggers"> | ||||
|         <set>QAbstractItemView::NoEditTriggers</set> | ||||
|        </property> | ||||
|        <property name="sortingEnabled"> | ||||
|         <bool>false</bool> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|     </layout> | ||||
|    </item> | ||||
|   </layout> | ||||
|  </widget> | ||||
|  <resources/> | ||||
|  <connections/> | ||||
| </ui> | ||||
|  | @ -276,6 +276,30 @@ void ConfigureInput::ApplyProfile() { | |||
|     Settings::values.current_input_profile_index = ui->profile->currentIndex(); | ||||
| } | ||||
| 
 | ||||
| void ConfigureInput::EmitInputKeysChanged() { | ||||
|     emit InputKeysChanged(GetUsedKeyboardKeys()); | ||||
| } | ||||
| 
 | ||||
| void ConfigureInput::OnHotkeysChanged(QList<QKeySequence> new_key_list) { | ||||
|     hotkey_list = new_key_list; | ||||
| } | ||||
| 
 | ||||
| QList<QKeySequence> ConfigureInput::GetUsedKeyboardKeys() { | ||||
|     QList<QKeySequence> list; | ||||
|     for (int button = 0; button < Settings::NativeButton::NumButtons; button++) { | ||||
|         auto button_param = buttons_param[button]; | ||||
| 
 | ||||
|         if (button_param.Get("engine", "") == "keyboard") { | ||||
|             list << QKeySequence(button_param.Get("code", 0)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // TODO(adityaruplaha): Add home button to list when we finally emulate it
 | ||||
|     // Button ID of home button is 14: Referred from citra_qt/configuration/config.cpp
 | ||||
|     list.removeOne(list.indexOf(QKeySequence(buttons_param[14].Get("code", 0)))); | ||||
|     return list; | ||||
| } | ||||
| 
 | ||||
| void ConfigureInput::loadConfiguration() { | ||||
|     std::transform(Settings::values.current_input_profile.buttons.begin(), | ||||
|                    Settings::values.current_input_profile.buttons.end(), buttons_param.begin(), | ||||
|  | @ -332,11 +356,14 @@ void ConfigureInput::updateButtonLabels() { | |||
|         } | ||||
|         analog_map_stick[analog_id]->setText(tr("Set Analog Stick")); | ||||
|     } | ||||
| 
 | ||||
|     EmitInputKeysChanged(); | ||||
| } | ||||
| 
 | ||||
| void ConfigureInput::handleClick(QPushButton* button, | ||||
|                                  std::function<void(const Common::ParamPackage&)> new_input_setter, | ||||
|                                  InputCommon::Polling::DeviceType type) { | ||||
|     previous_key_code = QKeySequence(button->text())[0]; | ||||
|     button->setText(tr("[press key]")); | ||||
|     button->setFocus(); | ||||
| 
 | ||||
|  | @ -378,16 +405,26 @@ void ConfigureInput::keyPressEvent(QKeyEvent* event) { | |||
|     if (!input_setter || !event) | ||||
|         return; | ||||
| 
 | ||||
|     if (event->key() != Qt::Key_Escape) { | ||||
|     if (event->key() != Qt::Key_Escape && event->key() != previous_key_code) { | ||||
|         if (want_keyboard_keys) { | ||||
|             // Check if key is already bound
 | ||||
|             if (hotkey_list.contains(QKeySequence(event->key())) || | ||||
|                 GetUsedKeyboardKeys().contains(QKeySequence(event->key()))) { | ||||
|                 setPollingResult({}, true); | ||||
|                 QMessageBox::critical(this, tr("Error!"), | ||||
|                                       tr("You're using a key that's already bound.")); | ||||
|                 return; | ||||
|             } | ||||
|             setPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())}, | ||||
|                              false); | ||||
|         } else { | ||||
|             // Escape key wasn't pressed and we don't want any keyboard keys, so don't stop polling
 | ||||
|             // Escape key wasn't pressed and we don't want any keyboard keys, so don't stop
 | ||||
|             // polling
 | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|     setPollingResult({}, true); | ||||
|     previous_key_code = 0; | ||||
| } | ||||
| 
 | ||||
| void ConfigureInput::retranslateUi() { | ||||
|  |  | |||
|  | @ -11,6 +11,7 @@ | |||
| #include <string> | ||||
| #include <unordered_map> | ||||
| #include <QKeyEvent> | ||||
| #include <QKeySequence> | ||||
| #include <QWidget> | ||||
| #include "common/param_package.h" | ||||
| #include "core/settings.h" | ||||
|  | @ -38,9 +39,15 @@ public: | |||
| 
 | ||||
|     /// Load configuration settings.
 | ||||
|     void loadConfiguration(); | ||||
|     void EmitInputKeysChanged(); | ||||
| 
 | ||||
|     // Save the current input profile index
 | ||||
|     /// Save the current input profile index
 | ||||
|     void ApplyProfile(); | ||||
| public slots: | ||||
|     void OnHotkeysChanged(QList<QKeySequence> new_key_list); | ||||
| 
 | ||||
| signals: | ||||
|     void InputKeysChanged(QList<QKeySequence> new_key_list); | ||||
| 
 | ||||
| private: | ||||
|     std::unique_ptr<Ui::ConfigureInput> ui; | ||||
|  | @ -72,10 +79,20 @@ private: | |||
| 
 | ||||
|     std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers; | ||||
| 
 | ||||
|     /**
 | ||||
|      * List of keys currently registered to hotkeys. | ||||
|      * These can't be bound to any input key. | ||||
|      * Synchronised with ConfigureHotkeys via signal-slot. | ||||
|      */ | ||||
|     QList<QKeySequence> hotkey_list; | ||||
| 
 | ||||
|     /// A flag to indicate if keyboard keys are okay when configuring an input. If this is false,
 | ||||
|     /// keyboard events are ignored.
 | ||||
|     bool want_keyboard_keys = false; | ||||
| 
 | ||||
|     /// Generates list of all used keys
 | ||||
|     QList<QKeySequence> GetUsedKeyboardKeys(); | ||||
| 
 | ||||
|     /// Restore all buttons to their default values.
 | ||||
|     void restoreDefaults(); | ||||
|     /// Clear all input configuration
 | ||||
|  | @ -89,6 +106,9 @@ private: | |||
|                      std::function<void(const Common::ParamPackage&)> new_input_setter, | ||||
|                      InputCommon::Polling::DeviceType type); | ||||
| 
 | ||||
|     /// The key code of the previous state of the key being currently bound.
 | ||||
|     int previous_key_code; | ||||
| 
 | ||||
|     /// Finish polling and configure input using the input_setter
 | ||||
|     void setPollingResult(const Common::ParamPackage& params, bool abort); | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,7 +2,6 @@ | |||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <map> | ||||
| #include <QKeySequence> | ||||
| #include <QShortcut> | ||||
| #include <QtGlobal> | ||||
|  | @ -12,47 +11,32 @@ | |||
| HotkeyRegistry::HotkeyRegistry() = default; | ||||
| HotkeyRegistry::~HotkeyRegistry() = default; | ||||
| 
 | ||||
| void HotkeyRegistry::LoadHotkeys() { | ||||
|     // Make sure NOT to use a reference here because it would become invalid once we call
 | ||||
|     // beginGroup()
 | ||||
|     for (auto shortcut : UISettings::values.shortcuts) { | ||||
|         const QStringList cat = shortcut.first.split('/'); | ||||
|         Q_ASSERT(cat.size() >= 2); | ||||
| 
 | ||||
|         // RegisterHotkey assigns default keybindings, so use old values as default parameters
 | ||||
|         Hotkey& hk = hotkey_groups[cat[0]][cat[1]]; | ||||
|         if (!shortcut.second.first.isEmpty()) { | ||||
|             hk.keyseq = QKeySequence::fromString(shortcut.second.first); | ||||
|             hk.context = static_cast<Qt::ShortcutContext>(shortcut.second.second); | ||||
|         } | ||||
|         if (hk.shortcut) | ||||
|             hk.shortcut->setKey(hk.keyseq); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void HotkeyRegistry::SaveHotkeys() { | ||||
|     UISettings::values.shortcuts.clear(); | ||||
|     for (const auto& group : hotkey_groups) { | ||||
|         for (const auto& hotkey : group.second) { | ||||
|             UISettings::values.shortcuts.emplace_back( | ||||
|                 UISettings::Shortcut(group.first + '/' + hotkey.first, | ||||
|                                      UISettings::ContextualShortcut(hotkey.second.keyseq.toString(), | ||||
|                                                                     hotkey.second.context))); | ||||
|             UISettings::values.shortcuts.push_back( | ||||
|                 {hotkey.first, group.first, | ||||
|                  UISettings::ContextualShortcut(hotkey.second.keyseq.toString(), | ||||
|                                                 hotkey.second.context)}); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void HotkeyRegistry::RegisterHotkey(const QString& group, const QString& action, | ||||
|                                     const QKeySequence& default_keyseq, | ||||
|                                     Qt::ShortcutContext default_context) { | ||||
|     auto& hotkey_group = hotkey_groups[group]; | ||||
|     if (hotkey_group.find(action) != hotkey_group.end()) { | ||||
|         return; | ||||
| void HotkeyRegistry::LoadHotkeys() { | ||||
|     // Make sure NOT to use a reference here because it would become invalid once we call
 | ||||
|     // beginGroup()
 | ||||
|     for (auto shortcut : UISettings::values.shortcuts) { | ||||
|         Hotkey& hk = hotkey_groups[shortcut.group][shortcut.name]; | ||||
|         if (!shortcut.shortcut.first.isEmpty()) { | ||||
|             hk.keyseq = QKeySequence::fromString(shortcut.shortcut.first, QKeySequence::NativeText); | ||||
|             hk.context = static_cast<Qt::ShortcutContext>(shortcut.shortcut.second); | ||||
|         } | ||||
|         if (hk.shortcut) { | ||||
|             hk.shortcut->disconnect(); | ||||
|             hk.shortcut->setKey(hk.keyseq); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     auto& hotkey_action = hotkey_groups[group][action]; | ||||
|     hotkey_action.keyseq = default_keyseq; | ||||
|     hotkey_action.context = default_context; | ||||
| } | ||||
| 
 | ||||
| QShortcut* HotkeyRegistry::GetHotkey(const QString& group, const QString& action, QWidget* widget) { | ||||
|  | @ -64,28 +48,13 @@ QShortcut* HotkeyRegistry::GetHotkey(const QString& group, const QString& action | |||
|     return hk.shortcut; | ||||
| } | ||||
| 
 | ||||
| GHotkeysDialog::GHotkeysDialog(QWidget* parent) : QWidget(parent) { | ||||
|     ui.setupUi(this); | ||||
| QKeySequence HotkeyRegistry::GetKeySequence(const QString& group, const QString& action) { | ||||
|     Hotkey& hk = hotkey_groups[group][action]; | ||||
|     return hk.keyseq; | ||||
| } | ||||
| 
 | ||||
| void GHotkeysDialog::Populate(const HotkeyRegistry& registry) { | ||||
|     for (const auto& group : registry.hotkey_groups) { | ||||
|         QTreeWidgetItem* toplevel_item = new QTreeWidgetItem(QStringList(group.first)); | ||||
|         for (const auto& hotkey : group.second) { | ||||
|             QStringList columns; | ||||
|             columns << hotkey.first << hotkey.second.keyseq.toString(); | ||||
|             QTreeWidgetItem* item = new QTreeWidgetItem(columns); | ||||
|             toplevel_item->addChild(item); | ||||
|         } | ||||
|         ui.treeWidget->addTopLevelItem(toplevel_item); | ||||
|     } | ||||
|     // TODO: Make context configurable as well (hiding the column for now)
 | ||||
|     ui.treeWidget->setColumnCount(2); | ||||
| 
 | ||||
|     ui.treeWidget->resizeColumnToContents(0); | ||||
|     ui.treeWidget->resizeColumnToContents(1); | ||||
| } | ||||
| 
 | ||||
| void GHotkeysDialog::retranslateUi() { | ||||
|     ui.retranslateUi(this); | ||||
| Qt::ShortcutContext HotkeyRegistry::GetShortcutContext(const QString& group, | ||||
|                                                        const QString& action) { | ||||
|     Hotkey& hk = hotkey_groups[group][action]; | ||||
|     return hk.context; | ||||
| } | ||||
|  |  | |||
|  | @ -5,7 +5,6 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <map> | ||||
| #include "ui_hotkeys.h" | ||||
| 
 | ||||
| class QDialog; | ||||
| class QKeySequence; | ||||
|  | @ -14,7 +13,7 @@ class QShortcut; | |||
| 
 | ||||
| class HotkeyRegistry final { | ||||
| public: | ||||
|     friend class GHotkeysDialog; | ||||
|     friend class ConfigureHotkeys; | ||||
| 
 | ||||
|     explicit HotkeyRegistry(); | ||||
|     ~HotkeyRegistry(); | ||||
|  | @ -49,22 +48,27 @@ public: | |||
|     QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Register a hotkey. | ||||
|      * Returns a QKeySequence object whose signal can be connected to QAction::setShortcut. | ||||
|      * | ||||
|      * @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger") | ||||
|      * @param action Name of the action (e.g. "Start Emulation", "Load Image") | ||||
|      * @param default_keyseq Default key sequence to assign if the hotkey wasn't present in the | ||||
|      *                       settings file before | ||||
|      * @param default_context Default context to assign if the hotkey wasn't present in the settings | ||||
|      *                        file before | ||||
|      * @warning Both the group and action strings will be displayed in the hotkey settings dialog | ||||
|      * @param group  General group this hotkey belongs to (e.g. "Main Window", "Debugger"). | ||||
|      * @param action Name of the action (e.g. "Start Emulation", "Load Image"). | ||||
|      */ | ||||
|     void RegisterHotkey(const QString& group, const QString& action, | ||||
|                         const QKeySequence& default_keyseq = {}, | ||||
|                         Qt::ShortcutContext default_context = Qt::WindowShortcut); | ||||
|     QKeySequence GetKeySequence(const QString& group, const QString& action); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Returns a Qt::ShortcutContext object who can be connected to other | ||||
|      * QAction::setShortcutContext. | ||||
|      * | ||||
|      * @param group  General group this shortcut context belongs to (e.g. "Main Window", | ||||
|      * "Debugger"). | ||||
|      * @param action Name of the action (e.g. "Start Emulation", "Load Image"). | ||||
|      */ | ||||
|     Qt::ShortcutContext GetShortcutContext(const QString& group, const QString& action); | ||||
| 
 | ||||
| private: | ||||
|     struct Hotkey { | ||||
|         Hotkey() : shortcut(nullptr), context(Qt::WindowShortcut) {} | ||||
| 
 | ||||
|         QKeySequence keyseq; | ||||
|         QShortcut* shortcut = nullptr; | ||||
|         Qt::ShortcutContext context = Qt::WindowShortcut; | ||||
|  | @ -75,16 +79,3 @@ private: | |||
| 
 | ||||
|     HotkeyGroupMap hotkey_groups; | ||||
| }; | ||||
| 
 | ||||
| class GHotkeysDialog : public QWidget { | ||||
|     Q_OBJECT | ||||
| 
 | ||||
| public: | ||||
|     explicit GHotkeysDialog(QWidget* parent = nullptr); | ||||
|     void retranslateUi(); | ||||
| 
 | ||||
|     void Populate(const HotkeyRegistry& registry); | ||||
| 
 | ||||
| private: | ||||
|     Ui::hotkeys ui; | ||||
| }; | ||||
|  |  | |||
|  | @ -1,46 +0,0 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <ui version="4.0"> | ||||
|  <class>hotkeys</class> | ||||
|  <widget class="QWidget" name="hotkeys"> | ||||
|   <property name="geometry"> | ||||
|    <rect> | ||||
|     <x>0</x> | ||||
|     <y>0</y> | ||||
|     <width>363</width> | ||||
|     <height>388</height> | ||||
|    </rect> | ||||
|   </property> | ||||
|   <property name="windowTitle"> | ||||
|    <string>Hotkey Settings</string> | ||||
|   </property> | ||||
|   <layout class="QVBoxLayout" name="verticalLayout"> | ||||
|    <item> | ||||
|     <widget class="QTreeWidget" name="treeWidget"> | ||||
|      <property name="selectionBehavior"> | ||||
|       <enum>QAbstractItemView::SelectItems</enum> | ||||
|      </property> | ||||
|      <property name="headerHidden"> | ||||
|       <bool>false</bool> | ||||
|      </property> | ||||
|      <column> | ||||
|       <property name="text"> | ||||
|        <string>Action</string> | ||||
|       </property> | ||||
|      </column> | ||||
|      <column> | ||||
|       <property name="text"> | ||||
|        <string>Hotkey</string> | ||||
|       </property> | ||||
|      </column> | ||||
|      <column> | ||||
|       <property name="text"> | ||||
|        <string>Context</string> | ||||
|       </property> | ||||
|      </column> | ||||
|     </widget> | ||||
|    </item> | ||||
|   </layout> | ||||
|  </widget> | ||||
|  <resources/> | ||||
|  <connections/> | ||||
| </ui> | ||||
|  | @ -344,40 +344,35 @@ void GMainWindow::InitializeRecentFileMenuActions() { | |||
| } | ||||
| 
 | ||||
| void GMainWindow::InitializeHotkeys() { | ||||
|     hotkey_registry.RegisterHotkey("Main Window", "Load File", QKeySequence::Open); | ||||
|     hotkey_registry.RegisterHotkey("Main Window", "Start Emulation"); | ||||
|     hotkey_registry.RegisterHotkey("Main Window", "Continue/Pause", QKeySequence(Qt::Key_F4)); | ||||
|     hotkey_registry.RegisterHotkey("Main Window", "Restart", QKeySequence(Qt::Key_F5)); | ||||
|     hotkey_registry.RegisterHotkey("Main Window", "Swap Screens", QKeySequence(Qt::Key_F9)); | ||||
|     hotkey_registry.RegisterHotkey("Main Window", "Toggle Screen Layout", | ||||
|                                    QKeySequence(Qt::Key_F10)); | ||||
|     hotkey_registry.RegisterHotkey("Main Window", "Fullscreen", QKeySequence::FullScreen); | ||||
|     hotkey_registry.RegisterHotkey("Main Window", "Exit Fullscreen", QKeySequence(Qt::Key_Escape), | ||||
|                                    Qt::ApplicationShortcut); | ||||
|     hotkey_registry.RegisterHotkey("Main Window", "Toggle Speed Limit", QKeySequence("CTRL+Z"), | ||||
|                                    Qt::ApplicationShortcut); | ||||
|     hotkey_registry.RegisterHotkey("Main Window", "Increase Speed Limit", QKeySequence("+"), | ||||
|                                    Qt::ApplicationShortcut); | ||||
|     hotkey_registry.RegisterHotkey("Main Window", "Decrease Speed Limit", QKeySequence("-"), | ||||
|                                    Qt::ApplicationShortcut); | ||||
|     hotkey_registry.RegisterHotkey("Main Window", "Toggle Frame Advancing", QKeySequence("CTRL+A"), | ||||
|                                    Qt::ApplicationShortcut); | ||||
|     hotkey_registry.RegisterHotkey("Main Window", "Advance Frame", QKeySequence(Qt::Key_Backslash), | ||||
|                                    Qt::ApplicationShortcut); | ||||
|     hotkey_registry.RegisterHotkey("Main Window", "Load Amiibo", QKeySequence(Qt::Key_F2), | ||||
|                                    Qt::ApplicationShortcut); | ||||
|     hotkey_registry.RegisterHotkey("Main Window", "Remove Amiibo", QKeySequence(Qt::Key_F3), | ||||
|                                    Qt::ApplicationShortcut); | ||||
|     hotkey_registry.RegisterHotkey("Main Window", "Capture Screenshot", QKeySequence(tr("CTRL+P"))); | ||||
| 
 | ||||
|     hotkey_registry.LoadHotkeys(); | ||||
| 
 | ||||
|     ui.action_Load_File->setShortcut(hotkey_registry.GetKeySequence("Main Window", "Load File")); | ||||
|     ui.action_Load_File->setShortcutContext( | ||||
|         hotkey_registry.GetShortcutContext("Main Window", "Load File")); | ||||
| 
 | ||||
|     ui.action_Exit->setShortcut(hotkey_registry.GetKeySequence("Main Window", "Exit Citra")); | ||||
|     ui.action_Exit->setShortcutContext( | ||||
|         hotkey_registry.GetShortcutContext("Main Window", "Exit Citra")); | ||||
| 
 | ||||
|     ui.action_Stop->setShortcut(hotkey_registry.GetKeySequence("Main Window", "Stop Emulation")); | ||||
|     ui.action_Stop->setShortcutContext( | ||||
|         hotkey_registry.GetShortcutContext("Main Window", "Stop Emulation")); | ||||
| 
 | ||||
|     ui.action_Show_Filter_Bar->setShortcut( | ||||
|         hotkey_registry.GetKeySequence("Main Window", "Toggle Filter Bar")); | ||||
|     ui.action_Show_Filter_Bar->setShortcutContext( | ||||
|         hotkey_registry.GetShortcutContext("Main Window", "Toggle Filter Bar")); | ||||
| 
 | ||||
|     ui.action_Show_Status_Bar->setShortcut( | ||||
|         hotkey_registry.GetKeySequence("Main Window", "Toggle Status Bar")); | ||||
|     ui.action_Show_Status_Bar->setShortcutContext( | ||||
|         hotkey_registry.GetShortcutContext("Main Window", "Toggle Status Bar")); | ||||
| 
 | ||||
|     connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated, | ||||
|             this, &GMainWindow::OnMenuLoadFile); | ||||
|     connect(hotkey_registry.GetHotkey("Main Window", "Start Emulation", this), | ||||
|             &QShortcut::activated, this, &GMainWindow::OnStartGame); | ||||
|     connect(hotkey_registry.GetHotkey("Main Window", "Continue/Pause", this), &QShortcut::activated, | ||||
|             this, [&] { | ||||
| 
 | ||||
|     connect(hotkey_registry.GetHotkey("Main Window", "Continue/Pause Emulation", this), | ||||
|             &QShortcut::activated, this, [&] { | ||||
|                 if (emulation_running) { | ||||
|                     if (emu_thread->IsRunning()) { | ||||
|                         OnPauseGame(); | ||||
|  | @ -386,8 +381,8 @@ void GMainWindow::InitializeHotkeys() { | |||
|                     } | ||||
|                 } | ||||
|             }); | ||||
|     connect(hotkey_registry.GetHotkey("Main Window", "Restart", this), &QShortcut::activated, this, | ||||
|             [this] { | ||||
|     connect(hotkey_registry.GetHotkey("Main Window", "Restart Emulation", this), | ||||
|             &QShortcut::activated, this, [this] { | ||||
|                 if (!Core::System::GetInstance().IsPoweredOn()) | ||||
|                     return; | ||||
|                 BootGame(QString(game_path)); | ||||
|  | @ -546,7 +541,6 @@ void GMainWindow::ConnectMenuEvents() { | |||
|             &GMainWindow::ToggleWindowMode); | ||||
|     connect(ui.action_Display_Dock_Widget_Headers, &QAction::triggered, this, | ||||
|             &GMainWindow::OnDisplayTitleBars); | ||||
|     ui.action_Show_Filter_Bar->setShortcut(tr("CTRL+F")); | ||||
|     connect(ui.action_Show_Filter_Bar, &QAction::triggered, this, &GMainWindow::OnToggleFilterBar); | ||||
|     connect(ui.action_Show_Status_Bar, &QAction::triggered, statusBar(), &QStatusBar::setVisible); | ||||
| 
 | ||||
|  | @ -1332,6 +1326,7 @@ void GMainWindow::OnConfigure() { | |||
|     auto result = configureDialog.exec(); | ||||
|     if (result == QDialog::Accepted) { | ||||
|         configureDialog.applyConfiguration(); | ||||
|         InitializeHotkeys(); | ||||
|         if (UISettings::values.theme != old_theme) | ||||
|             UpdateUITheme(); | ||||
|         if (UISettings::values.enable_discord_presence != old_discord_presence) | ||||
|  |  | |||
|  | @ -96,7 +96,6 @@ private: | |||
|     void InitializeWidgets(); | ||||
|     void InitializeDebugWidgets(); | ||||
|     void InitializeRecentFileMenuActions(); | ||||
|     void InitializeHotkeys(); | ||||
| 
 | ||||
|     void SetDefaultUIGeometry(); | ||||
|     void SyncMenuUISettings(); | ||||
|  | @ -171,6 +170,7 @@ private slots: | |||
|     void OnOpenCitraFolder(); | ||||
|     void OnToggleFilterBar(); | ||||
|     void OnDisplayTitleBars(bool); | ||||
|     void InitializeHotkeys(); | ||||
|     void ToggleFullscreen(); | ||||
|     void ChangeScreenLayout(); | ||||
|     void ToggleScreenLayout(); | ||||
|  |  | |||
|  | @ -14,5 +14,4 @@ const Themes themes{{ | |||
| }}; | ||||
| 
 | ||||
| Values values = {}; | ||||
| 
 | ||||
| } // namespace UISettings
 | ||||
|  |  | |||
|  | @ -17,7 +17,12 @@ | |||
| namespace UISettings { | ||||
| 
 | ||||
| using ContextualShortcut = std::pair<QString, int>; | ||||
| using Shortcut = std::pair<QString, ContextualShortcut>; | ||||
| 
 | ||||
| struct Shortcut { | ||||
|     QString name; | ||||
|     QString group; | ||||
|     ContextualShortcut shortcut; | ||||
| }; | ||||
| 
 | ||||
| using Themes = std::array<std::pair<const char*, const char*>, 4>; | ||||
| extern const Themes themes; | ||||
|  |  | |||
							
								
								
									
										37
									
								
								src/citra_qt/util/sequence_dialog/sequence_dialog.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/citra_qt/util/sequence_dialog/sequence_dialog.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,37 @@ | |||
| // Copyright 2018 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <QDialogButtonBox> | ||||
| #include <QKeySequenceEdit> | ||||
| #include <QVBoxLayout> | ||||
| #include "citra_qt/util/sequence_dialog/sequence_dialog.h" | ||||
| 
 | ||||
| SequenceDialog::SequenceDialog(QWidget* parent) : QDialog(parent) { | ||||
|     setWindowTitle(tr("Enter a hotkey")); | ||||
|     auto* layout = new QVBoxLayout(this); | ||||
|     key_sequence = new QKeySequenceEdit; | ||||
|     layout->addWidget(key_sequence); | ||||
|     auto* buttons = | ||||
|         new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal); | ||||
|     buttons->setCenterButtons(true); | ||||
|     layout->addWidget(buttons); | ||||
|     connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept); | ||||
|     connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); | ||||
|     setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); | ||||
| } | ||||
| 
 | ||||
| SequenceDialog::~SequenceDialog() = default; | ||||
| 
 | ||||
| QKeySequence SequenceDialog::GetSequence() { | ||||
|     // Only the first key is returned. The other 3, if present, are ignored.
 | ||||
|     return QKeySequence(key_sequence->keySequence()[0]); | ||||
| } | ||||
| 
 | ||||
| bool SequenceDialog::focusNextPrevChild(bool next) { | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| void SequenceDialog::closeEvent(QCloseEvent*) { | ||||
|     reject(); | ||||
| } | ||||
							
								
								
									
										25
									
								
								src/citra_qt/util/sequence_dialog/sequence_dialog.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/citra_qt/util/sequence_dialog/sequence_dialog.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,25 @@ | |||
| // Copyright 2018 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <memory> | ||||
| #include <QDialog> | ||||
| 
 | ||||
| class QKeySequenceEdit; | ||||
| 
 | ||||
| class SequenceDialog : public QDialog { | ||||
|     Q_OBJECT | ||||
| 
 | ||||
| public: | ||||
|     explicit SequenceDialog(QWidget* parent = nullptr); | ||||
|     ~SequenceDialog(); | ||||
| 
 | ||||
|     QKeySequence GetSequence(); | ||||
|     void closeEvent(QCloseEvent*) override; | ||||
| 
 | ||||
| private: | ||||
|     QKeySequenceEdit* key_sequence; | ||||
|     bool focusNextPrevChild(bool next) override; | ||||
| }; | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue