diff --git a/src/blackgui/components/settingscomponent.cpp b/src/blackgui/components/settingscomponent.cpp index a46dabdd6..d525ec775 100644 --- a/src/blackgui/components/settingscomponent.cpp +++ b/src/blackgui/components/settingscomponent.cpp @@ -15,7 +15,7 @@ #include "blackcore/context_network.h" #include "blackcore/context_settings.h" #include "blackcore/context_audio.h" -#include "blackmisc/hardware/keyboardkeylist.h" +#include "blackmisc/input/keyboardkeylist.h" #include "blackmisc/logmessage.h" #include "blackmisc/settingsblackmiscclasses.h" #include @@ -29,7 +29,7 @@ using namespace BlackMisc::Audio; using namespace BlackMisc::PhysicalQuantities; using namespace BlackMisc::Geo; using namespace BlackMisc::Settings; -using namespace BlackMisc::Hardware; +using namespace BlackMisc::Input; namespace BlackGui { @@ -80,7 +80,6 @@ namespace BlackGui //! \todo Settings are loaded twice, this here is for init but each component also consumes the signal changed slot this->ui->comp_AudioSetup->reloadSettings(); this->ui->comp_SettingsServersComponent->reloadSettings(); - this->ui->comp_SettingsHotkeysComponent->reloadSettings(); } /* diff --git a/src/blackgui/components/settingshotkeycomponent.cpp b/src/blackgui/components/settingshotkeycomponent.cpp index 3dea678ef..ec4bf5e3c 100644 --- a/src/blackgui/components/settingshotkeycomponent.cpp +++ b/src/blackgui/components/settingshotkeycomponent.cpp @@ -9,13 +9,13 @@ #include "settingshotkeycomponent.h" #include "ui_settingshotkeycomponent.h" -#include "blackcore/context_settings.h" -#include "blackmisc/settingutilities.h" -#include "blackmisc/variant.h" +#include "blackgui/hotkeydialog.h" +#include "blackcore/context_application.h" +#include -using namespace BlackCore; using namespace BlackMisc; -using namespace BlackMisc::Settings; +using namespace BlackMisc::Input; +using namespace BlackGui::Models; namespace BlackGui { @@ -27,48 +27,111 @@ namespace BlackGui ui(new Ui::CSettingsHotkeyComponent) { ui->setupUi(this); + ui->tv_hotkeys->setModel(&m_model); + + connect(ui->pb_addHotkey, &QPushButton::clicked, this, &CSettingsHotkeyComponent::ps_addEntry); + connect(ui->pb_editHotkey, &QPushButton::clicked, this, &CSettingsHotkeyComponent::ps_editEntry); + connect(ui->pb_removeHotkey, &QPushButton::clicked, this, &CSettingsHotkeyComponent::ps_removeEntry); + + ui->tv_hotkeys->selectRow(0); } - CSettingsHotkeyComponent::~CSettingsHotkeyComponent() { } - - void CSettingsHotkeyComponent::runtimeHasBeenSet() + CSettingsHotkeyComponent::~CSettingsHotkeyComponent() { - Q_ASSERT_X(this->getIContextSettings(), Q_FUNC_INFO, "Missing settings"); - this->connect(this->getIContextSettings(), &IContextSettings::changedSettings, this, &CSettingsHotkeyComponent::ps_changedSettings); - - // Settings hotkeys - this->connect(this->ui->pb_SettingsCancel, &QPushButton::clicked, this, &CSettingsHotkeyComponent::reloadSettings); - this->connect(this->ui->pb_SettingsSave, &QPushButton::clicked, this, &CSettingsHotkeyComponent::ps_saveHotkeys); - this->connect(this->ui->pb_SettingsRemove, &QPushButton::clicked, this, &CSettingsHotkeyComponent::ps_clearHotkey); } - void CSettingsHotkeyComponent::reloadSettings() + void CSettingsHotkeyComponent::ps_addEntry() { - // update hot keys - this->ui->tvp_SettingsMiscHotkeys->updateContainer(this->getIContextSettings()->getHotkeys()); + BlackMisc::CIdentifierList registeredApps; + if (getIContextApplication()) registeredApps = getIContextApplication()->getRegisteredApplications(); + // add local application + registeredApps.push_back(CIdentifier()); + auto selectedActionHotkey = CHotkeyDialog::getActionHotkey(CActionHotkey(), registeredApps, this); + if (selectedActionHotkey.isValid() && checkAndConfirmConflicts(selectedActionHotkey)) + { + addHotkeytoSettings(selectedActionHotkey); + int position = m_model.rowCount(); + m_model.insertRows(position, 1, QModelIndex()); + QModelIndex index = m_model.index(position, 0, QModelIndex()); + m_model.setData(index, QVariant::fromValue(selectedActionHotkey), CActionHotkeyListModel::ActionHotkeyRole); + } } - void CSettingsHotkeyComponent::ps_changedSettings(uint typeValue) + void CSettingsHotkeyComponent::ps_editEntry() { - IContextSettings::SettingsType type = static_cast(typeValue); - this->reloadSettings(); - Q_UNUSED(type); + auto index = ui->tv_hotkeys->selectionModel()->currentIndex(); + if (!index.isValid()) return; + + const auto model = ui->tv_hotkeys->model(); + const QModelIndex indexHotkey = model->index(index.row(), 0, QModelIndex()); + Q_ASSERT(indexHotkey.data(CActionHotkeyListModel::ActionHotkeyRole).canConvert()); + CActionHotkey actionHotkey = indexHotkey.data(CActionHotkeyListModel::ActionHotkeyRole).value(); + BlackMisc::CIdentifierList registeredApps; + if (getIContextApplication()) registeredApps = getIContextApplication()->getRegisteredApplications(); + // add local application + registeredApps.push_back(CIdentifier()); + auto selectedActionHotkey = CHotkeyDialog::getActionHotkey(actionHotkey, registeredApps, this); + if (selectedActionHotkey.isValid() && checkAndConfirmConflicts(selectedActionHotkey, { actionHotkey })) + { + updateHotkeyInSettings(actionHotkey, selectedActionHotkey); + m_model.setData(indexHotkey, QVariant::fromValue(selectedActionHotkey), CActionHotkeyListModel::ActionHotkeyRole); + } } - void CSettingsHotkeyComponent::ps_saveHotkeys() + void CSettingsHotkeyComponent::ps_removeEntry() { - const QString path = CSettingUtilities::appendPaths(IContextSettings::PathRoot(), IContextSettings::PathHotkeys()); - this->getIContextSettings()->value(path, CSettingUtilities::CmdUpdate(), CVariant::from(this->ui->tvp_SettingsMiscHotkeys->derivedModel()->getContainer())); + QModelIndexList indexes = ui->tv_hotkeys->selectionModel()->selectedRows(); + for (const auto &index : indexes) + { + CActionHotkey actionHotkey = index.data(CActionHotkeyListModel::ActionHotkeyRole).value(); + removeHotkeyFromSettings(actionHotkey); + m_model.removeRows(index.row(), 1, QModelIndex()); + } } - void CSettingsHotkeyComponent::ps_clearHotkey() + void CSettingsHotkeyComponent::addHotkeytoSettings(const CActionHotkey &actionHotkey) { - QModelIndex i = this->ui->tvp_SettingsMiscHotkeys->currentIndex(); - if (i.row() < 0 || i.row() >= this->ui->tvp_SettingsMiscHotkeys->rowCount()) return; - CSettingKeyboardHotkey hotkey = this->ui->tvp_SettingsMiscHotkeys->at(i); - CSettingKeyboardHotkey defaultHotkey; - defaultHotkey.setFunction(hotkey.getFunction()); - this->ui->tvp_SettingsMiscHotkeys->derivedModel()->update(i, defaultHotkey); + CActionHotkeyList actionHotkeyList(m_actionHotkeys.get()); + actionHotkeyList.push_back(actionHotkey); + m_actionHotkeys.set(actionHotkeyList); + } + + void CSettingsHotkeyComponent::updateHotkeyInSettings(const CActionHotkey &oldValue, const CActionHotkey &newValue) + { + CActionHotkeyList actionHotkeyList(m_actionHotkeys.get()); + actionHotkeyList.replace(oldValue, newValue); + m_actionHotkeys.set(actionHotkeyList); + } + + void CSettingsHotkeyComponent::removeHotkeyFromSettings(const CActionHotkey &actionHotkey) + { + CActionHotkeyList actionHotkeyList(m_actionHotkeys.get()); + actionHotkeyList.remove(actionHotkey); + m_actionHotkeys.set(actionHotkeyList); + } + + bool CSettingsHotkeyComponent::checkAndConfirmConflicts(const CActionHotkey &actionHotkey, const CActionHotkeyList &ignore) + { + auto configuredHotkeys = m_actionHotkeys.get(); + CActionHotkeyList conflicts = configuredHotkeys.findSupersetsOf(actionHotkey); + conflicts.push_back(configuredHotkeys.findSubsetsOf(actionHotkey)); + conflicts.removeIfIn(ignore); + + if (!conflicts.isEmpty()) + { + QString message = QString("The selected combination conflicts with the following %1 combinations(s):\n\n").arg(conflicts.size()); + for (const auto &conflict : conflicts) + { + message += conflict.getCombination().toQString(); + message += "\n"; + } + message += "\n Do you want to use it anway?"; + auto reply = QMessageBox::warning(this, "SettingsHotkeyComponent", + message, + QMessageBox::Yes | QMessageBox::No, QMessageBox::No); + if (reply == QMessageBox::No) { return false; } + } + return true; } } // ns diff --git a/src/blackgui/components/settingshotkeycomponent.h b/src/blackgui/components/settingshotkeycomponent.h index 343be69ae..fcd3ce710 100644 --- a/src/blackgui/components/settingshotkeycomponent.h +++ b/src/blackgui/components/settingshotkeycomponent.h @@ -12,18 +12,22 @@ #ifndef BLACKGUI_COMPONENTS_SETTINGSHOTKEYCOMPONENT_H #define BLACKGUI_COMPONENTS_SETTINGSHOTKEYCOMPONENT_H -#include "blackgui/blackguiexport.h" +#include "blackgui/models/actionhotkeylistmodel.h" #include "blackgui/components/enableforruntime.h" +#include "blackcore/settings/application.h" + #include -namespace Ui { class CSettingsHotkeyComponent; } +namespace Ui { + class CSettingsHotkeyComponent; +} namespace BlackGui { namespace Components { - //! Define hotkeys + //! Configure hotkeys class BLACKGUI_EXPORT CSettingsHotkeyComponent : public QFrame, public CEnableForRuntime @@ -32,31 +36,27 @@ namespace BlackGui public: //! Constructor - explicit CSettingsHotkeyComponent(QWidget *parent = nullptr); + CSettingsHotkeyComponent(QWidget *parent = nullptr); //! Destructor ~CSettingsHotkeyComponent(); - //! Reload settings - void reloadSettings(); - - protected: - //! \copydoc CRuntimeBasedComponent::runtimeHasBeenSet - virtual void runtimeHasBeenSet() override; - private slots: - - //! Settings have been changed - void ps_changedSettings(uint typeValue); - - //! Save the Hotkeys - void ps_saveHotkeys(); - - //! Clear single hotkey - void ps_clearHotkey(); + void ps_addEntry(); + void ps_editEntry(); + void ps_removeEntry(); private: + void addHotkeytoSettings(const BlackMisc::Input::CActionHotkey &actionHotkey); + void updateHotkeyInSettings(const BlackMisc::Input::CActionHotkey &oldValue, const BlackMisc::Input::CActionHotkey &newValue); + void removeHotkeyFromSettings(const BlackMisc::Input::CActionHotkey &actionHotkey); + bool checkAndConfirmConflicts(const BlackMisc::Input::CActionHotkey &actionHotkey, const BlackMisc::Input::CActionHotkeyList &ignore = {}); + QScopedPointer ui; + BlackGui::Models::CActionHotkeyListModel m_model; + BlackCore::CSetting m_actionHotkeys { this }; + + void ps_hotkeySlot(bool keyDown); }; } // ns diff --git a/src/blackgui/components/settingshotkeycomponent.ui b/src/blackgui/components/settingshotkeycomponent.ui index 7d560d56a..4268827b1 100644 --- a/src/blackgui/components/settingshotkeycomponent.ui +++ b/src/blackgui/components/settingshotkeycomponent.ui @@ -7,11 +7,11 @@ 0 0 400 - 300 + 193 - Frame + sample_hotkeys QFrame::StyledPanel @@ -19,102 +19,88 @@ QFrame::Raised - - - 3 - + - 0 + 1 - 0 + 1 - 0 + 1 - - 0 - - - - - true + + + + Add + + + + + + Edit + + + + + + + Remove + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + true + false + + false + - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - Save - - - - - - - - 0 - 0 - - - - Remove - - - - - - - - 0 - 0 - - - - Cancel - - - - - - - - + + Qt::Vertical - QSizePolicy::Fixed + QSizePolicy::MinimumExpanding 20 - 20 + 40 - - - BlackGui::Views::CKeyboardKeyView - QTableView -
blackgui/views/keyboardkeyview.h
-
-
+ diff --git a/src/blackgui/hotkeydialog.cpp b/src/blackgui/hotkeydialog.cpp new file mode 100644 index 000000000..3bf491ca2 --- /dev/null +++ b/src/blackgui/hotkeydialog.cpp @@ -0,0 +1,251 @@ +#include "hotkeydialog.h" +#include "ui_hotkeydialog.h" +#include "blackgui/stylesheetutility.h" +#include "blackmisc/input/hotkeycombination.h" +#include "blackmisc/icon.h" +#include "blackmisc/logmessage.h" +#include +#include + +using namespace BlackMisc; +using namespace BlackMisc::Input; + +namespace BlackGui +{ + + CKeySelectionBox::CKeySelectionBox(QWidget *parent) : + QComboBox(parent) + { + connect(this, static_cast(&CKeySelectionBox::currentIndexChanged), this, &CKeySelectionBox::ps_updateSelectedIndex); + } + + void CKeySelectionBox::setSelectedIndex(int index) + { + m_oldIndex = index; + setCurrentIndex(m_oldIndex); + } + + void CKeySelectionBox::ps_updateSelectedIndex(int index) + { + emit keySelectionChanged(m_oldIndex, index); + m_oldIndex = index; + } + + CHotkeyDialog::CHotkeyDialog(const BlackMisc::Input::CActionHotkey &actionHotkey, QWidget *parent) : + QDialog(parent), + ui(new Ui::CHotkeyDialog), + m_actionHotkey(actionHotkey), + m_actionModel(this) + { + m_inputManager = BlackCore::CInputManager::instance(); + + ui->setupUi(this); + ui->advancedFrame->hide(); + + ui->tv_actions->setModel(&m_actionModel); + ui->pb_advancedMode->setIcon(BlackMisc::CIcons::arrowMediumSouth16()); + selectAction(); + + if (!actionHotkey.getCombination().isEmpty()) { ui->pb_selectedHotkey->setText(actionHotkey.getCombination().toQString()); } + + connect(ui->pb_advancedMode, &QPushButton::clicked, this, &CHotkeyDialog::ps_advancedModeChanged); + connect(ui->pb_selectedHotkey, &QPushButton::clicked, this, &CHotkeyDialog::ps_selectHotkey); + connect(ui->pb_accept, &QPushButton::clicked, this, &CHotkeyDialog::ps_accept); + connect(ui->pb_cancel, &QPushButton::clicked, this, &CHotkeyDialog::reject); + connect(m_inputManager, &BlackCore::CInputManager::combinationSelectionChanged, this, &CHotkeyDialog::ps_combinationSelectionChanged); + connect(m_inputManager, &BlackCore::CInputManager::combinationSelectionFinished, this, &CHotkeyDialog::ps_combinationSelectionFinished); + connect(ui->tv_actions->selectionModel(), &QItemSelectionModel::selectionChanged, this, &CHotkeyDialog::ps_changeSelectedAction); + + initStyleSheet(); + } + + CHotkeyDialog::~CHotkeyDialog() + { + } + + void CHotkeyDialog::setRegisteredApplications(const BlackMisc::CIdentifierList &applications) + { + for (const auto & app : applications) + { + ui->cb_identifier->addItem(app.getMachineName(), QVariant::fromValue(app)); + } + } + + void CHotkeyDialog::initStyleSheet() + { + const QString s = CStyleSheetUtility::instance().styles( + { + CStyleSheetUtility::fileNameFonts(), + CStyleSheetUtility::fileNameStandardWidget() + } + ); + setStyleSheet(s); + } + + CActionHotkey CHotkeyDialog::getActionHotkey(const CActionHotkey &initial, const CIdentifierList &applications, QWidget *parent) + { + CHotkeyDialog editDialog(initial, parent); + editDialog.setWindowModality(Qt::WindowModal); + // add local application + editDialog.setRegisteredApplications(applications); + if (editDialog.exec()) { return editDialog.getSelectedActionHotkey(); } + else { return {}; } + + } + + void CHotkeyDialog::ps_advancedModeChanged() + { + if (m_actionHotkey.getCombination().isEmpty()) return; + + if (!ui->advancedFrame->isVisible()) + { + setupAdvancedFrame(); + ui->advancedFrame->show(); + ui->pb_advancedMode->setIcon(BlackMisc::CIcons::arrowMediumNorth16()); + } + else + { + ui->pb_advancedMode->setIcon(BlackMisc::CIcons::arrowMediumSouth16()); + ui->advancedFrame->hide(); + ui->gb_hotkey->resize(0, 0); + } + } + + void CHotkeyDialog::ps_selectHotkey() + { + ui->pb_selectedHotkey->setText("Press any key/button..."); + m_inputManager->startCapture(); + } + + void CHotkeyDialog::ps_combinationSelectionChanged(const BlackMisc::Input::CHotkeyCombination &combination) + { + ui->pb_selectedHotkey->setText(combination.toFormattedQString()); + } + + void CHotkeyDialog::ps_combinationSelectionFinished(const BlackMisc::Input::CHotkeyCombination &combination) + { + m_actionHotkey.setCombination(combination); + synchronize(); + } + + void CHotkeyDialog::ps_changeSelectedAction(const QItemSelection &selected, const QItemSelection &deselected) + { + Q_UNUSED(deselected); + const auto index = selected.indexes().first(); + m_actionHotkey.setAction(index.data(Models::CActionModel::ActionRole).toString()); + } + + void CHotkeyDialog::ps_accept() + { + if (m_actionHotkey.getApplicableMachine().getMachineName().isEmpty()) + { + CLogMessage().validationWarning("Missing %1") << ui->gb_machine->title(); + return; + } + + if (m_actionHotkey.getCombination().isEmpty()) + { + CLogMessage().validationWarning("Missing %1") << ui->gb_hotkey->title(); + return; + } + + if (m_actionHotkey.getAction().isEmpty()) + { + CLogMessage().validationWarning("Missing %1") << ui->gb_action->title(); + return; + } + + QDialog::accept(); + } + + void CHotkeyDialog::synchronize() + { + synchronizeSimpleSelection(); + synchronizeAdvancedSelection(); + } + + void CHotkeyDialog::synchronizeSimpleSelection() + { + ui->pb_selectedHotkey->setText(m_actionHotkey.getCombination().toFormattedQString()); + } + + void CHotkeyDialog::synchronizeAdvancedSelection() + { + if (ui->advancedFrame->isVisible()) { setupAdvancedFrame(); } + } + + void CHotkeyDialog::setupAdvancedFrame() + { + clearAdvancedFrame(); + auto allSupportedKeys = CKeyboardKeyList::allSupportedKeys(); + + QStringList splittedKeys = m_actionHotkey.getCombination().toQString().split('+', QString::SkipEmptyParts); + for (const auto &splittedKey : splittedKeys) + { + if (splittedKey == "+") continue; + + int currentIndex = -1; + CKeySelectionBox *ksb = new CKeySelectionBox(ui->advancedFrame); + for (const auto &supportedKey : allSupportedKeys) + { + QString supportedKeyAsString = supportedKey.toQString(); + ksb->addItem(supportedKeyAsString, QVariant::fromValue(supportedKey)); + if (supportedKeyAsString == splittedKey) + { + currentIndex = ksb->count() - 1; + } + } + ksb->setSelectedIndex(currentIndex); + ui->advancedFrame->layout()->addWidget(ksb); + int position = ui->advancedFrame->layout()->count() - 1; + ksb->setProperty("position", position); + connect(ksb, &CKeySelectionBox::keySelectionChanged, this, &CHotkeyDialog::advancedKeyChanged); + } + } + + void CHotkeyDialog::clearAdvancedFrame() + { + QLayout *layout = ui->advancedFrame->layout(); + QLayoutItem *child; + + while ((child = layout->takeAt(0)) != 0) + { + if (child->widget()) child->widget()->deleteLater(); + delete child; + } + } + + void CHotkeyDialog::advancedKeyChanged(int oldIndex, int newIndex) + { + CKeySelectionBox* ksb = qobject_cast(sender()); + Q_ASSERT(ksb); + CKeyboardKey oldKey = ksb->itemData(oldIndex).value(); + CKeyboardKey newKey = ksb->itemData(newIndex).value(); + + auto combination = m_actionHotkey.getCombination(); + combination.replaceKey(oldKey, newKey); + m_actionHotkey.setCombination(combination); + synchronize(); + } + + void CHotkeyDialog::selectAction() + { + if (m_actionHotkey.getAction().isEmpty()) return; + + const auto tokens = m_actionHotkey.getAction().split("/", QString::SkipEmptyParts); + QModelIndex parentIndex = QModelIndex(); + + for (const auto &token : tokens) + { + QModelIndex startIndex = m_actionModel.index(0, 0, parentIndex); + auto indexList = m_actionModel.match(startIndex, Qt::DisplayRole, QVariant::fromValue(token)); + if (indexList.isEmpty()) return; + parentIndex = indexList.first(); + ui->tv_actions->expand(parentIndex); + } + + QItemSelectionModel * selectionModel = ui->tv_actions->selectionModel(); + selectionModel->select(parentIndex, QItemSelectionModel::Select); + } + +} diff --git a/src/blackgui/hotkeydialog.h b/src/blackgui/hotkeydialog.h new file mode 100644 index 000000000..d473d9e16 --- /dev/null +++ b/src/blackgui/hotkeydialog.h @@ -0,0 +1,108 @@ +/* Copyright (C) 2014 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +//! \file + +#ifndef BLACKGUI_HOTKEYDIALOG_H +#define BLACKGUI_HOTKEYDIALOG_H + +#include "blackgui/models/actionmodel.h" +#include "blackcore/input_manager.h" +#include "blackmisc/input/hotkeycombination.h" +#include "blackmisc/input/actionhotkeylist.h" +#include "blackmisc/identifierlist.h" +#include +#include + +namespace Ui +{ + class CHotkeyDialog; +} + +namespace BlackGui +{ + + /*! + * Combobox for selecting keyboard keys + */ + class CKeySelectionBox : public QComboBox + { + Q_OBJECT + + public: + //! Constructor + CKeySelectionBox(QWidget *parent = nullptr); + + //! Set key with index as selected + void setSelectedIndex(int index); + + signals: + //! User has changed the selection + void keySelectionChanged(int oldIndex, int newIndex); + + private slots: + void ps_updateSelectedIndex(int index); + + private: + int m_oldIndex; + }; + + /*! + * Hotkey dialog + */ + class CHotkeyDialog : public QDialog + { + Q_OBJECT + + public: + //! Constructor + CHotkeyDialog(const BlackMisc::Input::CActionHotkey &actionHotkey, QWidget *parent = nullptr); + + //! Destructor + ~CHotkeyDialog(); + + //! Get hotkey selected by user + BlackMisc::Input::CActionHotkey getSelectedActionHotkey() const { return m_actionHotkey; } + + //! Set registered applications + void setRegisteredApplications(const BlackMisc::CIdentifierList &applications); + + //! Init style sheet + void initStyleSheet(); + + //! getHotkey runs the hotkey dialog and returns the result + static BlackMisc::Input::CActionHotkey getActionHotkey(const BlackMisc::Input::CActionHotkey &initial, const BlackMisc::CIdentifierList &applications, + QWidget *parent = nullptr); + + private: + void ps_advancedModeChanged(); + void ps_selectHotkey(); + void ps_combinationSelectionChanged(const BlackMisc::Input::CHotkeyCombination &combination); + void ps_combinationSelectionFinished(const BlackMisc::Input::CHotkeyCombination &combination); + void ps_changeSelectedAction(const QItemSelection &selected, const QItemSelection &deselected); + void ps_accept(); + + + void synchronize(); + void synchronizeSimpleSelection(); + void synchronizeAdvancedSelection(); + void setupAdvancedFrame(); + void clearAdvancedFrame(); + void advancedKeyChanged(int oldIndex, int newIndex); + void selectAction(); + + QScopedPointer ui; + BlackMisc::Input::CActionHotkey m_actionHotkey; + BlackGui::Models::CActionModel m_actionModel; + BlackCore::CInputManager *m_inputManager; + }; + +} + +#endif diff --git a/src/blackgui/hotkeydialog.ui b/src/blackgui/hotkeydialog.ui new file mode 100644 index 000000000..8e8fad14f --- /dev/null +++ b/src/blackgui/hotkeydialog.ui @@ -0,0 +1,167 @@ + + + CHotkeyDialog + + + + 0 + 0 + 269 + 384 + + + + Edit hotkey + + + + + + Machine + + + + + + + + + + + + Combination + + + + + + + 0 + 0 + + + + + 21 + 21 + + + + + 21 + 21 + + + + + + + + + + + Press + + + true + + + + + + + + 0 + 0 + + + + [Select] + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Release + + + + + + + true + + + QFrame::NoFrame + + + QFrame::Raised + + + + + + + + + + + Action + + + + + + false + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Ok + + + + + + + Cancel + + + + + + + + diff --git a/src/blackgui/models/actionhotkeylistmodel.cpp b/src/blackgui/models/actionhotkeylistmodel.cpp new file mode 100644 index 000000000..7180f4a6f --- /dev/null +++ b/src/blackgui/models/actionhotkeylistmodel.cpp @@ -0,0 +1,136 @@ +/* Copyright (C) 2013 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +#include "actionhotkeylistmodel.h" +#include "blackmisc/blackmiscfreefunctions.h" + +using namespace BlackMisc; +using namespace BlackMisc::Input; + +namespace BlackGui +{ + namespace Models + { + CActionHotkeyListModel::CActionHotkeyListModel(QObject *parent) : + QAbstractTableModel(parent) + { + } + + int CActionHotkeyListModel::rowCount(const QModelIndex & /** parent **/) const + { + return m_actionHotkeys.size(); + } + + int CActionHotkeyListModel::columnCount(const QModelIndex & /** parent **/) const + { + return 3; + } + + QVariant CActionHotkeyListModel::data(const QModelIndex &index, int role) const + { + if (!index.isValid()) { return QVariant(); } + + if (index.row() >= m_actionHotkeys.size() || index.row() < 0) { return QVariant(); } + + if (role == Qt::DisplayRole) + { + if (index.column() == 0) + { + BlackMisc::CIdentifier identifier = m_actionHotkeys[index.row()].getApplicableMachine(); + return identifier.getMachineName(); + } + if (index.column() == 1) + { + CHotkeyCombination combination = m_actionHotkeys[index.row()].getCombination(); + return combination.toQString(); + } + if (index.column() == 2) + { + return m_actionHotkeys[index.row()].getAction(); + } + } + else if (role == ActionHotkeyRole) + { + auto hotkey = m_actionHotkeys[index.row()]; + return QVariant::fromValue(hotkey); + } + return {}; + } + + QVariant CActionHotkeyListModel::headerData(int section, Qt::Orientation orientation, int role) const + { + if (role == Qt::DisplayRole) + { + if (orientation == Qt::Horizontal) { + switch (section) + { + case 0: + return QStringLiteral("Machine"); + case 1: + return QStringLiteral("Combination"); + case 2: + return QStringLiteral("Action"); + } + } + } + return {}; + } + + bool CActionHotkeyListModel::insertRows(int position, int rows, const QModelIndex &index) + { + Q_UNUSED(index); + beginInsertRows(QModelIndex(), position, position + rows - 1); + + for (int row = 0; row < rows; ++row) + { + m_actionHotkeys.push_back(BlackMisc::Input::CActionHotkey()); + } + + endInsertRows(); + return true; + } + + bool CActionHotkeyListModel::removeRows(int position, int rows, const QModelIndex &index) + { + Q_UNUSED(index); + beginRemoveRows(QModelIndex(), position, position + rows - 1); + + Q_ASSERT(position + rows - 1 < m_actionHotkeys.size()); + + for (int row = 0; row < rows; ++row) + { + auto toRemove = m_actionHotkeys[position + row]; + m_actionHotkeys.remove(toRemove); + } + + endRemoveRows(); + return true; + } + + + bool CActionHotkeyListModel::setData(const QModelIndex &index, const QVariant &var, int role) + { + if (index.isValid() && role == ActionHotkeyRole) + { + m_actionHotkeys[index.row()] = var.value(); + emit dataChanged(index, index); + return true; + } + return false; + } + + void CActionHotkeyListModel::clear() + { + beginResetModel(); + m_actionHotkeys.clear(); + endResetModel(); + } + + } +} // namespace diff --git a/src/blackgui/models/actionhotkeylistmodel.h b/src/blackgui/models/actionhotkeylistmodel.h new file mode 100644 index 000000000..69f6c004a --- /dev/null +++ b/src/blackgui/models/actionhotkeylistmodel.h @@ -0,0 +1,70 @@ +/* Copyright (C) 2013 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +//! \file + +#ifndef BLACKGUI_ACTIONHOTKEYLISTMODEL_H +#define BLACKGUI_ACTIONHOTKEYLISTMODEL_H + +#include "blackgui/blackguiexport.h" +#include "blackmisc/input/actionhotkeylist.h" +#include + +namespace BlackGui +{ + namespace Models + { + //! Hotkey list model + class BLACKGUI_EXPORT CActionHotkeyListModel : public QAbstractTableModel + { + Q_OBJECT + + public: + //! Item role + enum ItemRole + { + ActionHotkeyRole = Qt::UserRole + }; + + //! Constructor + CActionHotkeyListModel(QObject *parent = nullptr); + + //! Destructor + virtual ~CActionHotkeyListModel() {} + + //! \copydoc QAbstractTableModel::rowCount + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + + //! \copydoc QAbstractTableModel::columCount + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + + //! \copydoc QAbstractTableModel::data + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + + //! \copydoc QAbstractTableModel::setData + bool setData(const QModelIndex &index, const QVariant &var, int role) override; + + //! \copydoc QAbstractTableModel::headerData + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + + //! \copydoc QAbstractTableModel::insertRows + bool insertRows(int position, int rows, const QModelIndex &index) override; + + //! \copydoc QAbstractTableModel::removeRows + bool removeRows(int position, int rows, const QModelIndex &index) override; + + //! Clear model + void clear(); + + private: + BlackMisc::Input::CActionHotkeyList m_actionHotkeys; + }; + } +} +#endif // guard diff --git a/src/blackgui/models/actionitem.cpp b/src/blackgui/models/actionitem.cpp new file mode 100644 index 000000000..7535cc568 --- /dev/null +++ b/src/blackgui/models/actionitem.cpp @@ -0,0 +1,71 @@ +#include "actionitem.h" + + +namespace BlackGui +{ + namespace Models + { + + ActionItem::ActionItem(const QString &action, const QString &name, ActionItem *parent) : + m_action(action), m_actionName(name), m_parentItem(parent) + { + } + + ActionItem::~ActionItem() + { + qDeleteAll(m_childItems); + } + + void ActionItem::appendChild(ActionItem *item) + { + m_childItems.append(item); + } + + ActionItem *ActionItem::findChildByName(const QString &name) + { + for (auto child : m_childItems) + { + if (child->getActionName() == name) return child; + } + return nullptr; + } + + ActionItem *ActionItem::getChildByRow(int row) + { + return m_childItems.value(row); + } + + int ActionItem::getChildCount() const + { + return m_childItems.count(); + } + + int ActionItem::getColumnCount() const + { + return 1; + } + + QString ActionItem::getAction() const + { + return m_action; + } + + QString ActionItem::getActionName() const + { + return m_actionName; + } + + ActionItem *ActionItem::getParentItem() + { + return m_parentItem; + } + + int ActionItem::getRow() const + { + if (m_parentItem) { return m_parentItem->m_childItems.indexOf(const_cast(this)); } + + return 0; + } + + } +} diff --git a/src/blackgui/models/actionitem.h b/src/blackgui/models/actionitem.h new file mode 100644 index 000000000..0c2fe6d9f --- /dev/null +++ b/src/blackgui/models/actionitem.h @@ -0,0 +1,70 @@ +/* Copyright (C) 2015 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +//! \file + +#ifndef BLACKGUI_ACTIONITEM_H +#define BLACKGUI_ACTIONITEM_H + +#include +#include + +namespace BlackGui +{ + namespace Models + { + + //! One single action item in a tree + class ActionItem + { + public: + //! Constructor + ActionItem(const QString &action, const QString &name, ActionItem *parentItem = nullptr); + + //! Destructor + ~ActionItem(); + + //! Append a new child + void appendChild(ActionItem *child); + + //! Find child by its name + ActionItem *findChildByName(const QString &name); + + //! Get child by row + ActionItem *getChildByRow(int row); + + //! Number of childs + int getChildCount() const; + + //! Number of columns + int getColumnCount() const; + + //! Returns the stored action + QString getAction() const; + + //! Get action name + QString getActionName() const; + + //! Get row of this item + int getRow() const; + + //! Get parent item + ActionItem *getParentItem(); + + private: + QList m_childItems; + QString m_action; + QString m_actionName; + ActionItem *m_parentItem; + }; + + } +} + +#endif // guard diff --git a/src/blackgui/models/actionmodel.cpp b/src/blackgui/models/actionmodel.cpp new file mode 100644 index 000000000..eb42cac48 --- /dev/null +++ b/src/blackgui/models/actionmodel.cpp @@ -0,0 +1,103 @@ +#include "actionmodel.h" +#include "actionitem.h" +#include "blackcore/input_manager.h" + +namespace BlackGui +{ + namespace Models + { + CActionModel::CActionModel(QObject *parent) : + QAbstractItemModel(parent), + m_rootItem(new ActionItem(QString(), QString())) + { + setupModelData(); + } + + CActionModel::~CActionModel() + { + } + + int CActionModel::columnCount(const QModelIndex &parent) const + { + if (parent.isValid()) { return static_cast(parent.internalPointer())->getColumnCount(); } + else { return m_rootItem->getColumnCount(); } + } + + QVariant CActionModel::data(const QModelIndex &index, int role) const + { + if (!index.isValid()) { return QVariant(); } + + ActionItem *item = static_cast(index.internalPointer()); + + if (role == Qt::DisplayRole) { return item->getActionName(); } + if (role == ActionRole) { return item->getAction(); } + + return {}; + } + + Qt::ItemFlags CActionModel::flags(const QModelIndex &index) const + { + if (!index.isValid()) { return 0; } + + return QAbstractItemModel::flags(index); + } + + QModelIndex CActionModel::index(int row, int column, const QModelIndex &parent) const + { + if (!hasIndex(row, column, parent)) { return QModelIndex(); } + + ActionItem *parentItem; + if (!parent.isValid()) { parentItem = m_rootItem.data(); } + else { parentItem = static_cast(parent.internalPointer()); } + + ActionItem *childItem = parentItem->getChildByRow(row); + if (childItem) { return createIndex(row, column, childItem); } + else { return {}; } + } + + QModelIndex CActionModel::parent(const QModelIndex &index) const + { + if (!index.isValid()) { return {}; } + + ActionItem *childItem = static_cast(index.internalPointer()); + ActionItem *parentItem = childItem->getParentItem(); + + if (parentItem == m_rootItem.data()) { return {}; } + + return createIndex(parentItem->getRow(), 0, parentItem); + } + + int CActionModel::rowCount(const QModelIndex &parent) const + { + ActionItem *parentItem; + if (parent.column() > 0) { return 0; } + + if (!parent.isValid()) { parentItem = m_rootItem.data(); } + else { parentItem = static_cast(parent.internalPointer()); } + + return parentItem->getChildCount(); + } + + void CActionModel::setupModelData() + { + m_rootItem.reset(new ActionItem(QString(), QString())); + + for (const auto &actionPath : BlackCore::CInputManager::instance()->allAvailableActions()) + { + const auto tokens = actionPath.split("/", QString::SkipEmptyParts); + ActionItem *parentItem = m_rootItem.data(); + for (const auto &token : tokens) + { + ActionItem *child = parentItem->findChildByName(token); + if (child == nullptr) + { + child = new ActionItem(actionPath, token, parentItem); + parentItem->appendChild(child); + } + Q_ASSERT(child); + parentItem = child; + } + } + } + } +} diff --git a/src/blackgui/models/actionmodel.h b/src/blackgui/models/actionmodel.h new file mode 100644 index 000000000..e10268830 --- /dev/null +++ b/src/blackgui/models/actionmodel.h @@ -0,0 +1,74 @@ +/* Copyright (C) 2015 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +//! \file + +#ifndef BLACKGUI_ACTIONMODEL_H +#define BLACKGUI_ACTIONMODEL_H + +#include "blackcore/actionbind.h" +#include +#include + +namespace BlackGui +{ + namespace Models + { + + class ActionItem; + + /*! + * Action tree model + */ + class CActionModel : public QAbstractItemModel + { + Q_OBJECT + + public: + + //! User roles + enum ItemRole + { + ActionRole = Qt::UserRole + }; + + //! Constructor + CActionModel(QObject *parent = nullptr); + + //! Destructor + ~CActionModel(); + + //! \copydoc QAbstractItemModel::data + QVariant data(const QModelIndex &index, int role) const override; + + //! \copydoc QAbstractItemModel::flags + Qt::ItemFlags flags(const QModelIndex &index) const override; + + //! \copydoc QAbstractItemModel::index + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; + + //! \copydoc QAbstractItemModel::parent + QModelIndex parent(const QModelIndex &index) const override; + + //! \copydoc QAbstractItemModel::rowCount + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + + //! \copydoc QAbstractItemModel::columnCount + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + + private: + void setupModelData(); + + QScopedPointer m_rootItem; + }; + + } +} + +#endif // guard diff --git a/src/blackgui/models/allmodelcontainers.h b/src/blackgui/models/allmodelcontainers.h index 5828959d9..6c6a138e9 100644 --- a/src/blackgui/models/allmodelcontainers.h +++ b/src/blackgui/models/allmodelcontainers.h @@ -20,6 +20,7 @@ #include "blackmisc/network/textmessagelist.h" #include "blackmisc/network/aircraftmappinglist.h" #include "blackmisc/setkeyboardhotkeylist.h" +#include "blackmisc/input/actionhotkeylist.h" #include "blackmisc/simulation/simulatedaircraftlist.h" #include "blackmisc/simulation/aircraftmodellist.h" #include "blackmisc/simulation/distributorlist.h" diff --git a/src/blackgui/models/allmodels.h b/src/blackgui/models/allmodels.h index 0a85f0ddb..3e92e167c 100644 --- a/src/blackgui/models/allmodels.h +++ b/src/blackgui/models/allmodels.h @@ -25,6 +25,6 @@ #include "blackgui/models/liverylistmodel.h" #include "blackgui/models/distributorlistmodel.h" #include "blackgui/models/keyboardkeylistmodel.h" - +#include "blackgui/models/actionhotkeylistmodel.h" #endif // guard diff --git a/src/blackgui/qss/stdwidget.qss b/src/blackgui/qss/stdwidget.qss index b1cf026ce..c7ea3e409 100644 --- a/src/blackgui/qss/stdwidget.qss +++ b/src/blackgui/qss/stdwidget.qss @@ -29,6 +29,24 @@ QMainWindow::separator:hover { background: transparent; } +/** Main window **/ +QDialog { + background-image: url(:/textures/icons/textures/texture-outer.jpg); + background-color: darkslategray; +} + +/** separator between info areas and rest **/ +/** this hides them **/ +QDialog::separator { + background: transparent; + width: 0px; /* when vertical */ + height: 0px; /* when horizontal */ +} + +QDialog::separator:hover { + background: transparent; +} + /** Required when dock widget is floating 1) background-image not working on QDockWidget, so I use direct children for that diff --git a/src/blackgui/qss/swiftstdgui.qss b/src/blackgui/qss/swiftstdgui.qss index 955e714df..d0a23fb17 100644 --- a/src/blackgui/qss/swiftstdgui.qss +++ b/src/blackgui/qss/swiftstdgui.qss @@ -139,3 +139,15 @@ QAbstractScrollArea #pg_StatusPageCons ole { background-color: black; } padding: 3px; border-radius: 5px; } + +#gb_hotkey { + background-image: url(:/textures/icons/textures/texture-inner.jpg); +} + +#gb_action { + background-image: url(:/textures/icons/textures/texture-inner.jpg); +} + +#gb_machine { + background-image: url(:/textures/icons/textures/texture-inner.jpg); +}