forked from qt-creator/qt-creator
Improve keyboard shortcut settings
- change the line edit to accept actual text input in a form similar to QKeySequence::fromString (with special "native" form on OS X) - add a button that allows entering a key sequence by pressing keys, including support for e.g. escape key, which was broken before because it closed the dialog - add a warning label, that allows filtering the list for all potentially conflicting shortcuts Task-number: QTCREATORBUG-6 Change-Id: I94fc63525f653127e87f6ef2bffe72d8dcaa867d Reviewed-by: Leena Miettinen <riitta-leena.miettinen@theqtcompany.com>
This commit is contained in:
@@ -82,24 +82,6 @@ public:
|
||||
importButton = new QPushButton(CommandMappings::tr("Import..."), groupBox);
|
||||
exportButton = new QPushButton(CommandMappings::tr("Export..."), groupBox);
|
||||
|
||||
targetEditGroup = new QGroupBox(CommandMappings::tr("Target Identifier"), parent);
|
||||
targetEditGroup->setEnabled(false);
|
||||
|
||||
targetEdit = new FancyLineEdit(targetEditGroup);
|
||||
targetEdit->setAutoHideButton(FancyLineEdit::Right, true);
|
||||
targetEdit->setPlaceholderText(QString());
|
||||
targetEdit->setFiltering(true);
|
||||
targetEdit->setValidationFunction([this](FancyLineEdit *, QString *) {
|
||||
return !q->hasConflicts();
|
||||
});
|
||||
|
||||
resetButton = new QPushButton(targetEditGroup);
|
||||
resetButton->setToolTip(CommandMappings::tr("Reset to default."));
|
||||
resetButton->setText(CommandMappings::tr("Reset"));
|
||||
|
||||
QLabel *infoLabel = new QLabel(targetEditGroup);
|
||||
infoLabel->setTextFormat(Qt::RichText);
|
||||
|
||||
QHBoxLayout *hboxLayout1 = new QHBoxLayout();
|
||||
hboxLayout1->addWidget(defaultButton);
|
||||
hboxLayout1->addStretch();
|
||||
@@ -114,25 +96,9 @@ public:
|
||||
vboxLayout1->addWidget(commandList);
|
||||
vboxLayout1->addLayout(hboxLayout1);
|
||||
|
||||
targetLabel = new QLabel(CommandMappings::tr("Target:"));
|
||||
|
||||
QHBoxLayout *hboxLayout2 = new QHBoxLayout();
|
||||
hboxLayout2->addWidget(targetLabel);
|
||||
hboxLayout2->addWidget(targetEdit);
|
||||
hboxLayout2->addWidget(resetButton);
|
||||
|
||||
QVBoxLayout *vboxLayout2 = new QVBoxLayout(targetEditGroup);
|
||||
vboxLayout2->addLayout(hboxLayout2);
|
||||
vboxLayout2->addWidget(infoLabel);
|
||||
|
||||
QVBoxLayout *vboxLayout = new QVBoxLayout(parent);
|
||||
vboxLayout->addWidget(groupBox);
|
||||
vboxLayout->addWidget(targetEditGroup);
|
||||
|
||||
q->connect(targetEdit, &FancyLineEdit::buttonClicked,
|
||||
q, &CommandMappings::removeTargetIdentifier);
|
||||
q->connect(resetButton, &QPushButton::clicked,
|
||||
q, &CommandMappings::resetTargetIdentifier);
|
||||
q->connect(exportButton, &QPushButton::clicked,
|
||||
q, &CommandMappings::exportAction);
|
||||
q->connect(importButton, &QPushButton::clicked,
|
||||
@@ -145,9 +111,7 @@ public:
|
||||
q->connect(filterEdit, &FancyLineEdit::textChanged,
|
||||
q, &CommandMappings::filterChanged);
|
||||
q->connect(commandList, &QTreeWidget::currentItemChanged,
|
||||
q, &CommandMappings::commandChanged);
|
||||
q->connect(targetEdit, &FancyLineEdit::textChanged,
|
||||
q, &CommandMappings::targetIdentifierChanged);
|
||||
q, &CommandMappings::currentCommandChanged);
|
||||
|
||||
new HeaderViewStretcher(commandList->header(), 1);
|
||||
}
|
||||
@@ -160,10 +124,6 @@ public:
|
||||
QPushButton *defaultButton;
|
||||
QPushButton *importButton;
|
||||
QPushButton *exportButton;
|
||||
QGroupBox *targetEditGroup;
|
||||
QLabel *targetLabel;
|
||||
FancyLineEdit *targetEdit;
|
||||
QPushButton *resetButton;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
@@ -189,41 +149,16 @@ QTreeWidget *CommandMappings::commandList() const
|
||||
return d->commandList;
|
||||
}
|
||||
|
||||
QLineEdit *CommandMappings::targetEdit() const
|
||||
{
|
||||
return d->targetEdit;
|
||||
}
|
||||
|
||||
void CommandMappings::setPageTitle(const QString &s)
|
||||
{
|
||||
d->groupBox->setTitle(s);
|
||||
}
|
||||
|
||||
void CommandMappings::setTargetLabelText(const QString &s)
|
||||
{
|
||||
d->targetLabel->setText(s);
|
||||
}
|
||||
|
||||
void CommandMappings::setTargetEditTitle(const QString &s)
|
||||
{
|
||||
d->targetEditGroup->setTitle(s);
|
||||
}
|
||||
|
||||
void CommandMappings::setTargetHeader(const QString &s)
|
||||
{
|
||||
d->commandList->setHeaderLabels(QStringList() << tr("Command") << tr("Label") << s);
|
||||
}
|
||||
|
||||
void CommandMappings::commandChanged(QTreeWidgetItem *current)
|
||||
{
|
||||
if (!current || !current->data(0, Qt::UserRole).isValid()) {
|
||||
d->targetEdit->clear();
|
||||
d->targetEditGroup->setEnabled(false);
|
||||
return;
|
||||
}
|
||||
d->targetEditGroup->setEnabled(true);
|
||||
}
|
||||
|
||||
void CommandMappings::filterChanged(const QString &f)
|
||||
{
|
||||
for (int i = 0; i < d->commandList->topLevelItemCount(); ++i) {
|
||||
@@ -232,11 +167,6 @@ void CommandMappings::filterChanged(const QString &f)
|
||||
}
|
||||
}
|
||||
|
||||
bool CommandMappings::hasConflicts() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CommandMappings::filter(const QString &filterString, QTreeWidgetItem *item)
|
||||
{
|
||||
bool visible = filterString.isEmpty();
|
||||
@@ -275,7 +205,12 @@ void CommandMappings::setModified(QTreeWidgetItem *item , bool modified)
|
||||
|
||||
QString CommandMappings::filterText() const
|
||||
{
|
||||
return d->filterEdit ? d->filterEdit->text() : QString();
|
||||
return d->filterEdit->text();
|
||||
}
|
||||
|
||||
void CommandMappings::setFilterText(const QString &text)
|
||||
{
|
||||
d->filterEdit->setText(text);
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -54,15 +54,11 @@ class CORE_EXPORT CommandMappings : public QWidget
|
||||
public:
|
||||
CommandMappings(QWidget *parent = 0);
|
||||
~CommandMappings();
|
||||
virtual bool hasConflicts() const;
|
||||
|
||||
protected slots:
|
||||
signals:
|
||||
void currentCommandChanged(QTreeWidgetItem *current);
|
||||
|
||||
protected:
|
||||
virtual void removeTargetIdentifier() = 0;
|
||||
virtual void resetTargetIdentifier() = 0;
|
||||
virtual void targetIdentifierChanged() = 0;
|
||||
|
||||
virtual void defaultAction() = 0;
|
||||
|
||||
virtual void exportAction() {}
|
||||
@@ -72,16 +68,12 @@ protected:
|
||||
|
||||
void filterChanged(const QString &f);
|
||||
|
||||
virtual void commandChanged(QTreeWidgetItem *current);
|
||||
|
||||
// access to m_page
|
||||
void setImportExportEnabled(bool enabled);
|
||||
QTreeWidget *commandList() const;
|
||||
QLineEdit *targetEdit() const;
|
||||
QString filterText() const;
|
||||
void setFilterText(const QString &text);
|
||||
void setPageTitle(const QString &s);
|
||||
void setTargetLabelText(const QString &s);
|
||||
void setTargetEditTitle(const QString &s);
|
||||
void setTargetHeader(const QString &s);
|
||||
void setModified(QTreeWidgetItem *item, bool modified);
|
||||
|
||||
|
||||
@@ -41,12 +41,16 @@
|
||||
#include <utils/fancylineedit.h>
|
||||
#include <utils/hostosinfo.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/theme/theme.h>
|
||||
|
||||
#include <QKeyEvent>
|
||||
#include <QFileDialog>
|
||||
#include <QGroupBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QTreeWidgetItem>
|
||||
#include <QCoreApplication>
|
||||
#include <QApplication>
|
||||
#include <QDebug>
|
||||
|
||||
Q_DECLARE_METATYPE(Core::Internal::ShortcutItem*)
|
||||
@@ -71,23 +75,208 @@ static int translateModifiers(Qt::KeyboardModifiers state,
|
||||
return result;
|
||||
}
|
||||
|
||||
static QString keySequenceToEditString(const QKeySequence &sequence)
|
||||
{
|
||||
QString text = sequence.toString(QKeySequence::PortableText);
|
||||
if (Utils::HostOsInfo::isMacHost()) {
|
||||
// adapt the modifier names
|
||||
text.replace(QLatin1String("Ctrl"), QLatin1String("Cmd"), Qt::CaseInsensitive);
|
||||
text.replace(QLatin1String("Alt"), QLatin1String("Opt"), Qt::CaseInsensitive);
|
||||
text.replace(QLatin1String("Meta"), QLatin1String("Ctrl"), Qt::CaseInsensitive);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
static QKeySequence keySequenceFromEditString(const QString &editString)
|
||||
{
|
||||
QString text = editString;
|
||||
if (Utils::HostOsInfo::isMacHost()) {
|
||||
// adapt the modifier names
|
||||
text.replace(QLatin1String("Opt"), QLatin1String("Alt"), Qt::CaseInsensitive);
|
||||
text.replace(QLatin1String("Ctrl"), QLatin1String("Meta"), Qt::CaseInsensitive);
|
||||
text.replace(QLatin1String("Cmd"), QLatin1String("Ctrl"), Qt::CaseInsensitive);
|
||||
}
|
||||
return QKeySequence::fromString(text, QKeySequence::PortableText);
|
||||
}
|
||||
|
||||
static bool keySequenceIsValid(const QKeySequence &sequence)
|
||||
{
|
||||
if (sequence.isEmpty())
|
||||
return false;
|
||||
for (int i = 0; i < sequence.count(); ++i) {
|
||||
if (sequence[i] == Qt::Key_unknown)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
namespace Internal {
|
||||
|
||||
ShortcutButton::ShortcutButton(QWidget *parent)
|
||||
: QPushButton(parent)
|
||||
{
|
||||
setToolTip(tr("Click and type the new key sequence."));
|
||||
setCheckable(true);
|
||||
m_checkedText = tr("Stop Recording");
|
||||
m_uncheckedText = tr("Record");
|
||||
updateText();
|
||||
connect(this, &ShortcutButton::toggled, this, &ShortcutButton::handleToggleChange);
|
||||
}
|
||||
|
||||
QSize ShortcutButton::sizeHint() const
|
||||
{
|
||||
if (m_preferredWidth < 0) { // initialize size hint
|
||||
const QString originalText = text();
|
||||
ShortcutButton *that = const_cast<ShortcutButton *>(this);
|
||||
that->setText(m_checkedText);
|
||||
m_preferredWidth = QPushButton::sizeHint().width();
|
||||
that->setText(m_uncheckedText);
|
||||
int otherWidth = QPushButton::sizeHint().width();
|
||||
if (otherWidth > m_preferredWidth)
|
||||
m_preferredWidth = otherWidth;
|
||||
that->setText(originalText);
|
||||
}
|
||||
return QSize(m_preferredWidth, QPushButton::sizeHint().height());
|
||||
}
|
||||
|
||||
bool ShortcutButton::eventFilter(QObject *obj, QEvent *evt)
|
||||
{
|
||||
if (evt->type() == QEvent::ShortcutOverride) {
|
||||
evt->accept();
|
||||
return true;
|
||||
}
|
||||
if (evt->type() == QEvent::KeyRelease
|
||||
|| evt->type() == QEvent::Shortcut
|
||||
|| evt->type() == QEvent::Close/*Escape tries to close dialog*/) {
|
||||
return true;
|
||||
}
|
||||
if (evt->type() == QEvent::MouseButtonPress && isChecked()) {
|
||||
setChecked(false);
|
||||
return true;
|
||||
}
|
||||
if (evt->type() == QEvent::KeyPress) {
|
||||
QKeyEvent *k = static_cast<QKeyEvent*>(evt);
|
||||
int nextKey = k->key();
|
||||
if (m_keyNum > 3
|
||||
|| nextKey == Qt::Key_Control
|
||||
|| nextKey == Qt::Key_Shift
|
||||
|| nextKey == Qt::Key_Meta
|
||||
|| nextKey == Qt::Key_Alt) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nextKey |= translateModifiers(k->modifiers(), k->text());
|
||||
switch (m_keyNum) {
|
||||
case 0:
|
||||
m_key[0] = nextKey;
|
||||
break;
|
||||
case 1:
|
||||
m_key[1] = nextKey;
|
||||
break;
|
||||
case 2:
|
||||
m_key[2] = nextKey;
|
||||
break;
|
||||
case 3:
|
||||
m_key[3] = nextKey;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
m_keyNum++;
|
||||
k->accept();
|
||||
emit keySequenceChanged(QKeySequence(m_key[0], m_key[1], m_key[2], m_key[3]));
|
||||
if (m_keyNum > 3)
|
||||
setChecked(false);
|
||||
return true;
|
||||
}
|
||||
return QPushButton::eventFilter(obj, evt);
|
||||
}
|
||||
|
||||
void ShortcutButton::updateText()
|
||||
{
|
||||
setText(isChecked() ? m_checkedText : m_uncheckedText);
|
||||
}
|
||||
|
||||
void ShortcutButton::handleToggleChange(bool toogleState)
|
||||
{
|
||||
updateText();
|
||||
m_keyNum = m_key[0] = m_key[1] = m_key[2] = m_key[3] = 0;
|
||||
if (toogleState) {
|
||||
if (qApp->focusWidget())
|
||||
qApp->focusWidget()->clearFocus(); // funny things happen otherwise
|
||||
qApp->installEventFilter(this);
|
||||
} else {
|
||||
qApp->removeEventFilter(this);
|
||||
}
|
||||
}
|
||||
|
||||
ShortcutSettingsWidget::ShortcutSettingsWidget(QWidget *parent)
|
||||
: CommandMappings(parent)
|
||||
{
|
||||
setPageTitle(tr("Keyboard Shortcuts"));
|
||||
setTargetLabelText(tr("Key sequence:"));
|
||||
setTargetEditTitle(tr("Shortcut"));
|
||||
setTargetHeader(tr("Shortcut"));
|
||||
targetEdit()->setPlaceholderText(tr("Type to set shortcut"));
|
||||
|
||||
m_keyNum = m_key[0] = m_key[1] = m_key[2] = m_key[3] = 0;
|
||||
|
||||
connect(ActionManager::instance(), &ActionManager::commandListChanged,
|
||||
this, &ShortcutSettingsWidget::initialize);
|
||||
targetEdit()->installEventFilter(this);
|
||||
connect(this, &ShortcutSettingsWidget::currentCommandChanged,
|
||||
this, &ShortcutSettingsWidget::handleCurrentCommandChanged);
|
||||
|
||||
m_shortcutBox = new QGroupBox(tr("Shortcut"), this);
|
||||
m_shortcutBox->setEnabled(false);
|
||||
auto vboxLayout = new QVBoxLayout(m_shortcutBox);
|
||||
m_shortcutBox->setLayout(vboxLayout);
|
||||
auto hboxLayout = new QHBoxLayout;
|
||||
vboxLayout->addLayout(hboxLayout);
|
||||
m_shortcutEdit = new Utils::FancyLineEdit(m_shortcutBox);
|
||||
m_shortcutEdit->setFiltering(true);
|
||||
m_shortcutEdit->setPlaceholderText(tr("Enter key sequence as text"));
|
||||
m_shortcutEdit->setValidationFunction([this](Utils::FancyLineEdit *, QString *) {
|
||||
return validateShortcutEdit();
|
||||
});
|
||||
auto shortcutLabel = new QLabel(tr("Key sequence:"));
|
||||
shortcutLabel->setToolTip(Utils::HostOsInfo::isMacHost()
|
||||
? QLatin1String("<html><body>")
|
||||
+ tr("Use \"Cmd\", \"Opt\", \"Ctrl\", and \"Shift\" for modifier keys. "
|
||||
"Use \"Escape\", \"Backspace\", \"Delete\", \"Insert\", \"Home\", and so on, for special keys. "
|
||||
"Combine individual keys with \"+\", "
|
||||
"and combine multiple shortcuts to a shortcut sequence with \",\". "
|
||||
"For example, if the user must hold the Ctrl and Shift modifier keys "
|
||||
"while pressing Escape, and then release and press A, "
|
||||
"enter \"Ctrl+Shift+Escape,A\".")
|
||||
+ QLatin1String("</body></html>")
|
||||
: QLatin1String("<html><body>")
|
||||
+ tr("Use \"Ctrl\", \"Alt\", \"Meta\", and \"Shift\" for modifier keys. "
|
||||
"Use \"Escape\", \"Backspace\", \"Delete\", \"Insert\", \"Home\", and so on, for special keys. "
|
||||
"Combine individual keys with \"+\", "
|
||||
"and combine multiple shortcuts to a shortcut sequence with \",\". "
|
||||
"For example, if the user must hold the Ctrl and Shift modifier keys "
|
||||
"while pressing Escape, and then release and press A, "
|
||||
"enter \"Ctrl+Shift+Escape,A\".")
|
||||
+ QLatin1String("</body></html>"));
|
||||
auto shortcutButton = new ShortcutButton(m_shortcutBox);
|
||||
connect(shortcutButton, &ShortcutButton::keySequenceChanged,
|
||||
this, &ShortcutSettingsWidget::setKeySequence);
|
||||
auto resetButton = new QPushButton(tr("Reset"), m_shortcutBox);
|
||||
resetButton->setToolTip(tr("Reset to default."));
|
||||
connect(resetButton, &QPushButton::clicked,
|
||||
this, &ShortcutSettingsWidget::resetToDefault);
|
||||
hboxLayout->addWidget(shortcutLabel);
|
||||
hboxLayout->addWidget(m_shortcutEdit);
|
||||
hboxLayout->addWidget(shortcutButton);
|
||||
hboxLayout->addWidget(resetButton);
|
||||
|
||||
m_warningLabel = new QLabel(m_shortcutBox);
|
||||
m_warningLabel->setTextFormat(Qt::RichText);
|
||||
QPalette palette = m_warningLabel->palette();
|
||||
palette.setColor(QPalette::Active, QPalette::WindowText,
|
||||
Utils::creatorTheme()->color(Utils::Theme::TextColorError));
|
||||
m_warningLabel->setPalette(palette);
|
||||
connect(m_warningLabel, &QLabel::linkActivated, this, &ShortcutSettingsWidget::showConflicts);
|
||||
vboxLayout->addWidget(m_warningLabel);
|
||||
|
||||
layout()->addWidget(m_shortcutBox);
|
||||
|
||||
initialize();
|
||||
}
|
||||
|
||||
@@ -130,122 +319,86 @@ void ShortcutSettings::finish()
|
||||
delete m_widget;
|
||||
}
|
||||
|
||||
bool ShortcutSettingsWidget::eventFilter(QObject *o, QEvent *e)
|
||||
void ShortcutSettingsWidget::handleCurrentCommandChanged(QTreeWidgetItem *current)
|
||||
{
|
||||
Q_UNUSED(o)
|
||||
|
||||
if ( e->type() == QEvent::KeyPress ) {
|
||||
QKeyEvent *k = static_cast<QKeyEvent*>(e);
|
||||
handleKeyEvent(k);
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( e->type() == QEvent::Shortcut ||
|
||||
e->type() == QEvent::KeyRelease ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (e->type() == QEvent::ShortcutOverride) {
|
||||
// for shortcut overrides, we need to accept as well
|
||||
e->accept();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ShortcutSettingsWidget::commandChanged(QTreeWidgetItem *current)
|
||||
{
|
||||
CommandMappings::commandChanged(current);
|
||||
if (!current || !current->data(0, Qt::UserRole).isValid())
|
||||
if (!current || !current->data(0, Qt::UserRole).isValid()) {
|
||||
m_shortcutEdit->clear();
|
||||
m_warningLabel->clear();
|
||||
m_shortcutBox->setEnabled(false);
|
||||
return;
|
||||
} else {
|
||||
ShortcutItem *scitem = qvariant_cast<ShortcutItem *>(current->data(0, Qt::UserRole));
|
||||
setKeySequence(scitem->m_key);
|
||||
markCollisions(scitem);
|
||||
}
|
||||
|
||||
void ShortcutSettingsWidget::targetIdentifierChanged()
|
||||
{
|
||||
QTreeWidgetItem *current = commandList()->currentItem();
|
||||
if (current && current->data(0, Qt::UserRole).isValid()) {
|
||||
ShortcutItem *scitem = qvariant_cast<ShortcutItem *>(current->data(0, Qt::UserRole));
|
||||
scitem->m_key = QKeySequence(m_key[0], m_key[1], m_key[2], m_key[3]);
|
||||
if (scitem->m_cmd->defaultKeySequence() != scitem->m_key)
|
||||
setModified(current, true);
|
||||
else
|
||||
setModified(current, false);
|
||||
current->setText(2, scitem->m_key.toString(QKeySequence::NativeText));
|
||||
markCollisions(scitem);
|
||||
m_shortcutBox->setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
bool ShortcutSettingsWidget::hasConflicts() const
|
||||
bool ShortcutSettingsWidget::validateShortcutEdit() const
|
||||
{
|
||||
m_warningLabel->clear();
|
||||
QTreeWidgetItem *current = commandList()->currentItem();
|
||||
if (!current || !current->data(0, Qt::UserRole).isValid())
|
||||
return false;
|
||||
return true;
|
||||
|
||||
ShortcutItem *item = qvariant_cast<ShortcutItem *>(current->data(0, Qt::UserRole));
|
||||
if (!item)
|
||||
return false;
|
||||
QTC_ASSERT(item, return true);
|
||||
|
||||
const QKeySequence currentKey = QKeySequence::fromString(targetEdit()->text(), QKeySequence::NativeText);
|
||||
if (currentKey.isEmpty())
|
||||
return false;
|
||||
bool valid = false;
|
||||
|
||||
const Id globalId(Constants::C_GLOBAL);
|
||||
const Context itemContext = item->m_cmd->context();
|
||||
const QString text = m_shortcutEdit->text().trimmed();
|
||||
const QKeySequence currentKey = keySequenceFromEditString(text);
|
||||
|
||||
foreach (ShortcutItem *listItem, m_scitems) {
|
||||
if (item == listItem)
|
||||
continue;
|
||||
if (listItem->m_key.isEmpty())
|
||||
continue;
|
||||
if (listItem->m_key.matches(currentKey) == QKeySequence::NoMatch)
|
||||
continue;
|
||||
|
||||
const Context listContext = listItem->m_cmd->context();
|
||||
if (itemContext.contains(globalId) && !listContext.isEmpty())
|
||||
return true;
|
||||
if (listContext.contains(globalId) && !itemContext.isEmpty())
|
||||
return true;
|
||||
foreach (Id id, listContext)
|
||||
if (itemContext.contains(id))
|
||||
return true;
|
||||
if (keySequenceIsValid(currentKey) || text.isEmpty()) {
|
||||
item->m_key = currentKey;
|
||||
auto that = const_cast<ShortcutSettingsWidget *>(this);
|
||||
if (item->m_cmd->defaultKeySequence() != item->m_key)
|
||||
that->setModified(current, true);
|
||||
else
|
||||
that->setModified(current, false);
|
||||
current->setText(2, item->m_key.toString(QKeySequence::NativeText));
|
||||
valid = !that->markCollisions(item);
|
||||
if (!valid) {
|
||||
m_warningLabel->setText(
|
||||
tr("Key sequence has potential conflicts. <a href=\"#conflicts\">Show.</a>"));
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
m_warningLabel->setText(tr("Invalid key sequence."));
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
bool ShortcutSettingsWidget::filterColumn(const QString &filterString, QTreeWidgetItem *item,
|
||||
int column) const
|
||||
{
|
||||
QString text = item->text(column);
|
||||
if (Utils::HostOsInfo::isMacHost()) {
|
||||
// accept e.g. Cmd+E in the filter. the text shows special fancy characters for Cmd
|
||||
QString text;
|
||||
if (column == item->columnCount() - 1) {
|
||||
QKeySequence key = QKeySequence::fromString(text, QKeySequence::NativeText);
|
||||
if (!key.isEmpty()) {
|
||||
text = key.toString(QKeySequence::PortableText);
|
||||
text.replace(QLatin1String("Ctrl"), QLatin1String("Cmd"));
|
||||
text.replace(QLatin1String("Meta"), QLatin1String("Ctrl"));
|
||||
text.replace(QLatin1String("Alt"), QLatin1String("Opt"));
|
||||
}
|
||||
}
|
||||
// filter on the shortcut edit text
|
||||
if (!item->data(0, Qt::UserRole).isValid())
|
||||
return true;
|
||||
ShortcutItem *scitem = qvariant_cast<ShortcutItem *>(item->data(0, Qt::UserRole));
|
||||
text = keySequenceToEditString(scitem->m_key);
|
||||
} else {
|
||||
text = item->text(column);
|
||||
}
|
||||
return !text.contains(filterString, Qt::CaseInsensitive);
|
||||
}
|
||||
|
||||
void ShortcutSettingsWidget::setKeySequence(const QKeySequence &key)
|
||||
{
|
||||
m_key[0] = m_key[1] = m_key[2] = m_key[3] = 0;
|
||||
m_keyNum = key.count();
|
||||
for (int i = 0; i < m_keyNum; ++i) {
|
||||
m_key[i] = key[i];
|
||||
}
|
||||
targetEdit()->setText(key.toString(QKeySequence::NativeText));
|
||||
m_shortcutEdit->setText(keySequenceToEditString(key));
|
||||
}
|
||||
|
||||
void ShortcutSettingsWidget::resetTargetIdentifier()
|
||||
void ShortcutSettingsWidget::showConflicts()
|
||||
{
|
||||
QTreeWidgetItem *current = commandList()->currentItem();
|
||||
if (current && current->data(0, Qt::UserRole).isValid()) {
|
||||
ShortcutItem *scitem = qvariant_cast<ShortcutItem *>(current->data(0, Qt::UserRole));
|
||||
setFilterText(keySequenceToEditString(scitem->m_key));
|
||||
}
|
||||
}
|
||||
|
||||
void ShortcutSettingsWidget::resetToDefault()
|
||||
{
|
||||
QTreeWidgetItem *current = commandList()->currentItem();
|
||||
if (current && current->data(0, Qt::UserRole).isValid()) {
|
||||
@@ -256,15 +409,6 @@ void ShortcutSettingsWidget::resetTargetIdentifier()
|
||||
}
|
||||
}
|
||||
|
||||
void ShortcutSettingsWidget::removeTargetIdentifier()
|
||||
{
|
||||
m_keyNum = m_key[0] = m_key[1] = m_key[2] = m_key[3] = 0;
|
||||
targetEdit()->clear();
|
||||
|
||||
foreach (ShortcutItem *item, m_scitems)
|
||||
markCollisions(item);
|
||||
}
|
||||
|
||||
void ShortcutSettingsWidget::importAction()
|
||||
{
|
||||
QString fileName = QFileDialog::getOpenFileName(ICore::dialogParent(), tr("Import Keyboard Mapping Scheme"),
|
||||
@@ -281,7 +425,7 @@ void ShortcutSettingsWidget::importAction()
|
||||
item->m_key = mapping.value(sid);
|
||||
item->m_item->setText(2, item->m_key.toString(QKeySequence::NativeText));
|
||||
if (item->m_item == commandList()->currentItem())
|
||||
commandChanged(item->m_item);
|
||||
currentCommandChanged(item->m_item);
|
||||
|
||||
if (item->m_cmd->defaultKeySequence() != item->m_key)
|
||||
setModified(item->m_item, true);
|
||||
@@ -302,7 +446,7 @@ void ShortcutSettingsWidget::defaultAction()
|
||||
item->m_item->setText(2, item->m_key.toString(QKeySequence::NativeText));
|
||||
setModified(item->m_item, false);
|
||||
if (item->m_item == commandList()->currentItem())
|
||||
commandChanged(item->m_item);
|
||||
currentCommandChanged(item->m_item);
|
||||
}
|
||||
|
||||
foreach (ShortcutItem *item, m_scitems)
|
||||
@@ -377,66 +521,39 @@ void ShortcutSettingsWidget::initialize()
|
||||
filterChanged(filterText());
|
||||
}
|
||||
|
||||
void ShortcutSettingsWidget::handleKeyEvent(QKeyEvent *e)
|
||||
{
|
||||
int nextKey = e->key();
|
||||
if ( m_keyNum > 3 ||
|
||||
nextKey == Qt::Key_Control ||
|
||||
nextKey == Qt::Key_Shift ||
|
||||
nextKey == Qt::Key_Meta ||
|
||||
nextKey == Qt::Key_Alt )
|
||||
return;
|
||||
|
||||
nextKey |= translateModifiers(e->modifiers(), e->text());
|
||||
switch (m_keyNum) {
|
||||
case 0:
|
||||
m_key[0] = nextKey;
|
||||
break;
|
||||
case 1:
|
||||
m_key[1] = nextKey;
|
||||
break;
|
||||
case 2:
|
||||
m_key[2] = nextKey;
|
||||
break;
|
||||
case 3:
|
||||
m_key[3] = nextKey;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
m_keyNum++;
|
||||
QKeySequence ks(m_key[0], m_key[1], m_key[2], m_key[3]);
|
||||
targetEdit()->setText(ks.toString(QKeySequence::NativeText));
|
||||
e->accept();
|
||||
}
|
||||
|
||||
void ShortcutSettingsWidget::markCollisions(ShortcutItem *item)
|
||||
bool ShortcutSettingsWidget::markCollisions(ShortcutItem *item)
|
||||
{
|
||||
bool hasCollision = false;
|
||||
if (!item->m_key.isEmpty()) {
|
||||
Id globalId = Context(Constants::C_GLOBAL).at(0);
|
||||
Id globalId(Constants::C_GLOBAL);
|
||||
const Context itemContext = item->m_cmd->context();
|
||||
|
||||
const bool itemHasGlobalContext = itemContext.contains(globalId);
|
||||
foreach (ShortcutItem *currentItem, m_scitems) {
|
||||
|
||||
if (currentItem->m_key.isEmpty() || item == currentItem
|
||||
|| item->m_key != currentItem->m_key)
|
||||
continue;
|
||||
|
||||
foreach (Id id, currentItem->m_cmd->context()) {
|
||||
// conflict if context is identical, OR if one
|
||||
// of the contexts is the global context
|
||||
const Context thisContext = currentItem->m_cmd->context();
|
||||
if (itemContext.contains(id)
|
||||
|| (itemContext.contains(globalId) && !thisContext.isEmpty())
|
||||
|| (thisContext.contains(globalId) && !itemContext.isEmpty())) {
|
||||
currentItem->m_item->setForeground(2, Qt::red);
|
||||
const Context currentContext = currentItem->m_cmd->context();
|
||||
bool currentIsConflicting = (itemHasGlobalContext && !currentContext.isEmpty());
|
||||
if (!currentIsConflicting) {
|
||||
foreach (const Id &id, currentContext) {
|
||||
if ((id == globalId && !itemContext.isEmpty())
|
||||
|| itemContext.contains(id)) {
|
||||
currentIsConflicting = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (currentIsConflicting) {
|
||||
currentItem->m_item->setForeground(
|
||||
2, Utils::creatorTheme()->color(Utils::Theme::TextColorError));
|
||||
hasCollision = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
item->m_item->setForeground(2, hasCollision ? Qt::red : commandList()->palette().foreground());
|
||||
item->m_item->setForeground(2, hasCollision
|
||||
? Utils::creatorTheme()->color(Utils::Theme::TextColorError)
|
||||
: commandList()->palette().foreground());
|
||||
return hasCollision;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
@@ -36,9 +36,12 @@
|
||||
|
||||
#include <QKeySequence>
|
||||
#include <QPointer>
|
||||
#include <QPushButton>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QGroupBox;
|
||||
class QKeyEvent;
|
||||
class QLabel;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Core {
|
||||
@@ -57,6 +60,31 @@ struct ShortcutItem
|
||||
QTreeWidgetItem *m_item;
|
||||
};
|
||||
|
||||
class ShortcutButton : public QPushButton
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ShortcutButton(QWidget *parent = 0);
|
||||
|
||||
QSize sizeHint() const;
|
||||
|
||||
signals:
|
||||
void keySequenceChanged(const QKeySequence &sequence);
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *obj, QEvent *evt);
|
||||
|
||||
private:
|
||||
void updateText();
|
||||
void handleToggleChange(bool toggleState);
|
||||
|
||||
QString m_checkedText;
|
||||
QString m_uncheckedText;
|
||||
mutable int m_preferredWidth = -1;
|
||||
int m_key[4] = { 0, 0, 0, 0 };
|
||||
int m_keyNum = 0;
|
||||
};
|
||||
|
||||
class ShortcutSettingsWidget : public CommandMappings
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -68,28 +96,25 @@ public:
|
||||
void apply();
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *o, QEvent *e) override;
|
||||
|
||||
void commandChanged(QTreeWidgetItem *current) override;
|
||||
void targetIdentifierChanged() override;
|
||||
void resetTargetIdentifier() override;
|
||||
void removeTargetIdentifier() override;
|
||||
void importAction() override;
|
||||
void exportAction() override;
|
||||
void defaultAction() override;
|
||||
bool hasConflicts() const override;
|
||||
|
||||
bool filterColumn(const QString &filterString, QTreeWidgetItem *item, int column) const override;
|
||||
|
||||
private:
|
||||
void initialize();
|
||||
void handleKeyEvent(QKeyEvent *e);
|
||||
void markCollisions(ShortcutItem *);
|
||||
void handleCurrentCommandChanged(QTreeWidgetItem *current);
|
||||
void resetToDefault();
|
||||
bool validateShortcutEdit() const;
|
||||
bool markCollisions(ShortcutItem *);
|
||||
void setKeySequence(const QKeySequence &key);
|
||||
void showConflicts();
|
||||
void clear();
|
||||
|
||||
QList<ShortcutItem *> m_scitems;
|
||||
int m_key[4], m_keyNum;
|
||||
QGroupBox *m_shortcutBox;
|
||||
Utils::FancyLineEdit *m_shortcutEdit;
|
||||
QLabel *m_warningLabel;
|
||||
};
|
||||
|
||||
class ShortcutSettings : public IOptionsPage
|
||||
|
||||
@@ -544,25 +544,14 @@ class FakeVimExCommandsWidget : public CommandMappings
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FakeVimExCommandsWidget(FakeVimPluginPrivate *q, QWidget *parent = 0)
|
||||
: CommandMappings(parent), m_q(q)
|
||||
{
|
||||
setPageTitle(Tr::tr("Ex Command Mapping"));
|
||||
setTargetHeader(Tr::tr("Ex Trigger Expression"));
|
||||
setTargetLabelText(Tr::tr("Regular expression:"));
|
||||
setTargetEditTitle(Tr::tr("Ex Command"));
|
||||
targetEdit()->setPlaceholderText(QString());
|
||||
setImportExportEnabled(false);
|
||||
initialize();
|
||||
}
|
||||
FakeVimExCommandsWidget(FakeVimPluginPrivate *q, QWidget *parent = 0);
|
||||
|
||||
protected:
|
||||
void targetIdentifierChanged() override;
|
||||
void resetTargetIdentifier() override;
|
||||
void removeTargetIdentifier() override;
|
||||
void commandChanged();
|
||||
void resetToDefault();
|
||||
void defaultAction() override;
|
||||
|
||||
void commandChanged(QTreeWidgetItem *current) override;
|
||||
void handleCurrentCommandChanged(QTreeWidgetItem *current);
|
||||
|
||||
private:
|
||||
void initialize();
|
||||
@@ -571,8 +560,41 @@ private:
|
||||
ExCommandMap &defaultExCommandMap();
|
||||
|
||||
FakeVimPluginPrivate *m_q;
|
||||
QGroupBox *m_commandBox;
|
||||
Utils::FancyLineEdit *m_commandEdit;
|
||||
};
|
||||
|
||||
FakeVimExCommandsWidget::FakeVimExCommandsWidget(FakeVimPluginPrivate *q, QWidget *parent)
|
||||
: CommandMappings(parent), m_q(q)
|
||||
{
|
||||
setPageTitle(Tr::tr("Ex Command Mapping"));
|
||||
setTargetHeader(Tr::tr("Ex Trigger Expression"));
|
||||
setImportExportEnabled(false);
|
||||
|
||||
connect(this, &FakeVimExCommandsWidget::currentCommandChanged,
|
||||
this, &FakeVimExCommandsWidget::handleCurrentCommandChanged);
|
||||
|
||||
m_commandBox = new QGroupBox(Tr::tr("Ex Command"), this);
|
||||
m_commandBox->setEnabled(false);
|
||||
auto boxLayout = new QHBoxLayout(m_commandBox);
|
||||
m_commandEdit = new Utils::FancyLineEdit(m_commandBox);
|
||||
m_commandEdit->setFiltering(true);
|
||||
m_commandEdit->setPlaceholderText(QString());
|
||||
connect(m_commandEdit, &Utils::FancyLineEdit::textChanged,
|
||||
this, &FakeVimExCommandsWidget::commandChanged);
|
||||
auto resetButton = new QPushButton(Tr::tr("Reset"), m_commandBox);
|
||||
resetButton->setToolTip(Tr::tr("Reset to default."));
|
||||
connect(resetButton, &QPushButton::clicked,
|
||||
this, &FakeVimExCommandsWidget::resetToDefault);
|
||||
boxLayout->addWidget(new QLabel(Tr::tr("Regular expression:")));
|
||||
boxLayout->addWidget(m_commandEdit);
|
||||
boxLayout->addWidget(resetButton);
|
||||
|
||||
layout()->addWidget(m_commandBox);
|
||||
|
||||
initialize();
|
||||
}
|
||||
|
||||
class FakeVimExCommandsPage : public IOptionsPage
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -647,24 +669,28 @@ void FakeVimExCommandsWidget::initialize()
|
||||
setModified(item, true);
|
||||
}
|
||||
|
||||
commandChanged(0);
|
||||
handleCurrentCommandChanged(0);
|
||||
}
|
||||
|
||||
void FakeVimExCommandsWidget::commandChanged(QTreeWidgetItem *current)
|
||||
void FakeVimExCommandsWidget::handleCurrentCommandChanged(QTreeWidgetItem *current)
|
||||
{
|
||||
CommandMappings::commandChanged(current);
|
||||
if (current)
|
||||
targetEdit()->setText(current->text(2));
|
||||
if (current) {
|
||||
m_commandEdit->setText(current->text(2));
|
||||
m_commandBox->setEnabled(true);
|
||||
} else {
|
||||
m_commandEdit->clear();
|
||||
m_commandBox->setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
void FakeVimExCommandsWidget::targetIdentifierChanged()
|
||||
void FakeVimExCommandsWidget::commandChanged()
|
||||
{
|
||||
QTreeWidgetItem *current = commandList()->currentItem();
|
||||
if (!current)
|
||||
return;
|
||||
|
||||
const QString name = current->data(0, CommandRole).toString();
|
||||
const QString regex = targetEdit()->text();
|
||||
const QString regex = m_commandEdit->text();
|
||||
|
||||
if (current->data(0, Qt::UserRole).isValid()) {
|
||||
current->setText(2, regex);
|
||||
@@ -674,7 +700,7 @@ void FakeVimExCommandsWidget::targetIdentifierChanged()
|
||||
setModified(current, regex != defaultExCommandMap()[name].pattern());
|
||||
}
|
||||
|
||||
void FakeVimExCommandsWidget::resetTargetIdentifier()
|
||||
void FakeVimExCommandsWidget::resetToDefault()
|
||||
{
|
||||
QTreeWidgetItem *current = commandList()->currentItem();
|
||||
if (!current)
|
||||
@@ -683,12 +709,7 @@ void FakeVimExCommandsWidget::resetTargetIdentifier()
|
||||
QString regex;
|
||||
if (defaultExCommandMap().contains(name))
|
||||
regex = defaultExCommandMap()[name].pattern();
|
||||
targetEdit()->setText(regex);
|
||||
}
|
||||
|
||||
void FakeVimExCommandsWidget::removeTargetIdentifier()
|
||||
{
|
||||
targetEdit()->clear();
|
||||
m_commandEdit->setText(regex);
|
||||
}
|
||||
|
||||
void FakeVimExCommandsWidget::defaultAction()
|
||||
@@ -706,7 +727,7 @@ void FakeVimExCommandsWidget::defaultAction()
|
||||
setModified(item, false);
|
||||
item->setText(2, regex);
|
||||
if (item == commandList()->currentItem())
|
||||
commandChanged(item);
|
||||
currentCommandChanged(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user