forked from qt-creator/qt-creator
Terminal: lock/unlock keyboard
We copied QShortCutMap into Qtc to allow us tight control over which shortcuts are "enabled" while the focus is inside a terminal, and the keyboard is "locked" to the Terminal. Locked here means that except for a select few, all key presses are send directly to the terminal and cannot be used to activate other actions. Change-Id: I96cddf753033c0f4e7d806b20085bb4755853117 Reviewed-by: Eike Ziller <eike.ziller@qt.io>
This commit is contained in:
@@ -92,6 +92,8 @@ protected:
|
|||||||
void setupContext(const Context &context, QWidget *widget);
|
void setupContext(const Context &context, QWidget *widget);
|
||||||
void setZoomButtonsEnabled(bool enabled);
|
void setZoomButtonsEnabled(bool enabled);
|
||||||
|
|
||||||
|
IContext *m_context = nullptr;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual void updateFilter();
|
virtual void updateFilter();
|
||||||
|
|
||||||
@@ -108,7 +110,6 @@ private:
|
|||||||
QAction *m_filterActionCaseSensitive = nullptr;
|
QAction *m_filterActionCaseSensitive = nullptr;
|
||||||
QAction *m_invertFilterAction = nullptr;
|
QAction *m_invertFilterAction = nullptr;
|
||||||
Utils::FancyLineEdit *m_filterOutputLineEdit = nullptr;
|
Utils::FancyLineEdit *m_filterOutputLineEdit = nullptr;
|
||||||
IContext *m_context = nullptr;
|
|
||||||
bool m_filterRegexp = false;
|
bool m_filterRegexp = false;
|
||||||
bool m_invertFilter = false;
|
bool m_invertFilter = false;
|
||||||
Qt::CaseSensitivity m_filterCaseSensitivity = Qt::CaseInsensitive;
|
Qt::CaseSensitivity m_filterCaseSensitivity = Qt::CaseInsensitive;
|
||||||
|
@@ -9,6 +9,7 @@ add_qtc_plugin(Terminal
|
|||||||
scrollback.cpp scrollback.h
|
scrollback.cpp scrollback.h
|
||||||
shellintegration.cpp shellintegration.h
|
shellintegration.cpp shellintegration.h
|
||||||
shellmodel.cpp shellmodel.h
|
shellmodel.cpp shellmodel.h
|
||||||
|
shortcutmap.cpp shortcutmap.h
|
||||||
terminal.qrc
|
terminal.qrc
|
||||||
terminalconstants.h
|
terminalconstants.h
|
||||||
terminalicons.h
|
terminalicons.h
|
||||||
|
557
src/plugins/terminal/shortcutmap.cpp
Normal file
557
src/plugins/terminal/shortcutmap.cpp
Normal file
@@ -0,0 +1,557 @@
|
|||||||
|
// Copyright (C) 2023 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
// COPIED FROM qshortcutmap.cpp
|
||||||
|
|
||||||
|
#include "shortcutmap.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include <QGuiApplication>
|
||||||
|
#include <QKeyEvent>
|
||||||
|
#include <QLoggingCategory>
|
||||||
|
#include <QWindow>
|
||||||
|
|
||||||
|
Q_LOGGING_CATEGORY(lcShortcutMap, "terminal.shortcutmap", QtWarningMsg)
|
||||||
|
|
||||||
|
namespace Terminal::Internal {
|
||||||
|
|
||||||
|
/* \internal
|
||||||
|
Entry data for ShortcutMap
|
||||||
|
Contains:
|
||||||
|
Keysequence for entry
|
||||||
|
Pointer to parent owning the sequence
|
||||||
|
*/
|
||||||
|
struct ShortcutEntry
|
||||||
|
{
|
||||||
|
ShortcutEntry()
|
||||||
|
: keyseq(0)
|
||||||
|
, context(Qt::WindowShortcut)
|
||||||
|
, enabled(false)
|
||||||
|
, autorepeat(1)
|
||||||
|
, id(0)
|
||||||
|
, owner(nullptr)
|
||||||
|
, contextMatcher(nullptr)
|
||||||
|
{}
|
||||||
|
|
||||||
|
ShortcutEntry(const QKeySequence &k)
|
||||||
|
: keyseq(k)
|
||||||
|
, context(Qt::WindowShortcut)
|
||||||
|
, enabled(false)
|
||||||
|
, autorepeat(1)
|
||||||
|
, id(0)
|
||||||
|
, owner(nullptr)
|
||||||
|
, contextMatcher(nullptr)
|
||||||
|
{}
|
||||||
|
|
||||||
|
ShortcutEntry(QObject *o,
|
||||||
|
const QKeySequence &k,
|
||||||
|
Qt::ShortcutContext c,
|
||||||
|
int i,
|
||||||
|
bool a,
|
||||||
|
ShortcutMap::ContextMatcher m)
|
||||||
|
: keyseq(k)
|
||||||
|
, context(c)
|
||||||
|
, enabled(true)
|
||||||
|
, autorepeat(a)
|
||||||
|
, id(i)
|
||||||
|
, owner(o)
|
||||||
|
, contextMatcher(m)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool correctContext() const { return contextMatcher(owner, context); }
|
||||||
|
|
||||||
|
bool operator<(const ShortcutEntry &f) const { return keyseq < f.keyseq; }
|
||||||
|
|
||||||
|
QKeySequence keyseq;
|
||||||
|
Qt::ShortcutContext context;
|
||||||
|
bool enabled : 1;
|
||||||
|
bool autorepeat : 1;
|
||||||
|
signed int id;
|
||||||
|
QObject *owner;
|
||||||
|
ShortcutMap::ContextMatcher contextMatcher;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* \internal
|
||||||
|
Private data for ShortcutMap
|
||||||
|
*/
|
||||||
|
class ShortcutMapPrivate
|
||||||
|
{
|
||||||
|
Q_DECLARE_PUBLIC(ShortcutMap)
|
||||||
|
|
||||||
|
public:
|
||||||
|
ShortcutMapPrivate(ShortcutMap *parent)
|
||||||
|
: q_ptr(parent)
|
||||||
|
, currentId(0)
|
||||||
|
, ambigCount(0)
|
||||||
|
, currentState(QKeySequence::NoMatch)
|
||||||
|
{
|
||||||
|
identicals.reserve(10);
|
||||||
|
currentSequences.reserve(10);
|
||||||
|
}
|
||||||
|
ShortcutMap *q_ptr; // Private's parent
|
||||||
|
|
||||||
|
QList<ShortcutEntry> sequences; // All sequences!
|
||||||
|
|
||||||
|
int currentId; // Global shortcut ID number
|
||||||
|
int ambigCount; // Index of last enabled ambiguous dispatch
|
||||||
|
QKeySequence::SequenceMatch currentState;
|
||||||
|
QList<QKeySequence> currentSequences; // Sequence for the current state
|
||||||
|
QList<QKeySequence> newEntries;
|
||||||
|
QKeySequence prevSequence; // Sequence for the previous identical match
|
||||||
|
QList<const ShortcutEntry *> identicals; // Last identical matches
|
||||||
|
};
|
||||||
|
|
||||||
|
/*! \internal
|
||||||
|
ShortcutMap constructor.
|
||||||
|
*/
|
||||||
|
ShortcutMap::ShortcutMap()
|
||||||
|
: d_ptr(new ShortcutMapPrivate(this))
|
||||||
|
{
|
||||||
|
resetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \internal
|
||||||
|
ShortcutMap destructor.
|
||||||
|
*/
|
||||||
|
ShortcutMap::~ShortcutMap() {}
|
||||||
|
|
||||||
|
/*! \internal
|
||||||
|
Adds a shortcut to the global map.
|
||||||
|
Returns the id of the newly added shortcut.
|
||||||
|
*/
|
||||||
|
int ShortcutMap::addShortcut(QObject *owner,
|
||||||
|
const QKeySequence &key,
|
||||||
|
Qt::ShortcutContext context,
|
||||||
|
ContextMatcher matcher)
|
||||||
|
{
|
||||||
|
Q_ASSERT_X(owner, "ShortcutMap::addShortcut", "All shortcuts need an owner");
|
||||||
|
Q_ASSERT_X(!key.isEmpty(), "ShortcutMap::addShortcut", "Cannot add keyless shortcuts to map");
|
||||||
|
Q_D(ShortcutMap);
|
||||||
|
|
||||||
|
ShortcutEntry newEntry(owner, key, context, --(d->currentId), true, matcher);
|
||||||
|
const auto it = std::upper_bound(d->sequences.begin(), d->sequences.end(), newEntry);
|
||||||
|
d->sequences.insert(it, newEntry); // Insert sorted
|
||||||
|
qCDebug(lcShortcutMap).nospace() << "ShortcutMap::addShortcut(" << owner << ", " << key << ", "
|
||||||
|
<< context << ") added shortcut with ID " << d->currentId;
|
||||||
|
return d->currentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \internal
|
||||||
|
Removes a shortcut from the global map.
|
||||||
|
If \a owner is \nullptr, all entries in the map with the key sequence specified
|
||||||
|
is removed. If \a key is null, all sequences for \a owner is removed from
|
||||||
|
the map. If \a id is 0, any identical \a key sequences owned by \a owner
|
||||||
|
are removed.
|
||||||
|
Returns the number of sequences removed from the map.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int ShortcutMap::removeShortcut(int id, QObject *owner, const QKeySequence &key)
|
||||||
|
{
|
||||||
|
Q_D(ShortcutMap);
|
||||||
|
int itemsRemoved = 0;
|
||||||
|
bool allOwners = (owner == nullptr);
|
||||||
|
bool allKeys = key.isEmpty();
|
||||||
|
bool allIds = id == 0;
|
||||||
|
|
||||||
|
auto debug = qScopeGuard([&]() {
|
||||||
|
qCDebug(lcShortcutMap).nospace()
|
||||||
|
<< "ShortcutMap::removeShortcut(" << id << ", " << owner << ", " << key << ") removed "
|
||||||
|
<< itemsRemoved << " shortcuts(s)";
|
||||||
|
});
|
||||||
|
|
||||||
|
// Special case, remove everything
|
||||||
|
if (allOwners && allKeys && allIds) {
|
||||||
|
itemsRemoved = d->sequences.size();
|
||||||
|
d->sequences.clear();
|
||||||
|
return itemsRemoved;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i = d->sequences.size() - 1;
|
||||||
|
while (i >= 0) {
|
||||||
|
const ShortcutEntry &entry = d->sequences.at(i);
|
||||||
|
int entryId = entry.id;
|
||||||
|
if ((allOwners || entry.owner == owner) && (allIds || entry.id == id)
|
||||||
|
&& (allKeys || entry.keyseq == key)) {
|
||||||
|
d->sequences.removeAt(i);
|
||||||
|
++itemsRemoved;
|
||||||
|
}
|
||||||
|
if (id == entryId)
|
||||||
|
return itemsRemoved;
|
||||||
|
--i;
|
||||||
|
}
|
||||||
|
return itemsRemoved;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \internal
|
||||||
|
Resets the state of the statemachine to NoMatch
|
||||||
|
*/
|
||||||
|
void ShortcutMap::resetState()
|
||||||
|
{
|
||||||
|
Q_D(ShortcutMap);
|
||||||
|
d->currentState = QKeySequence::NoMatch;
|
||||||
|
clearSequence(d->currentSequences);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \internal
|
||||||
|
Returns the current state of the statemachine
|
||||||
|
*/
|
||||||
|
QKeySequence::SequenceMatch ShortcutMap::state()
|
||||||
|
{
|
||||||
|
Q_D(ShortcutMap);
|
||||||
|
return d->currentState;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \internal
|
||||||
|
Uses nextState(QKeyEvent) to check for a grabbed shortcut.
|
||||||
|
|
||||||
|
If so, it is dispatched using dispatchEvent().
|
||||||
|
|
||||||
|
Returns true if a shortcut handled the event.
|
||||||
|
|
||||||
|
\sa nextState, dispatchEvent
|
||||||
|
*/
|
||||||
|
bool ShortcutMap::tryShortcut(QKeyEvent *e)
|
||||||
|
{
|
||||||
|
Q_D(ShortcutMap);
|
||||||
|
|
||||||
|
if (e->key() == Qt::Key_unknown)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
QKeySequence::SequenceMatch previousState = state();
|
||||||
|
|
||||||
|
switch (nextState(e)) {
|
||||||
|
case QKeySequence::NoMatch:
|
||||||
|
// In the case of going from a partial match to no match we handled the
|
||||||
|
// event, since we already stated that we did for the partial match. But
|
||||||
|
// in the normal case of directly going to no match we say we didn't.
|
||||||
|
return previousState == QKeySequence::PartialMatch;
|
||||||
|
case QKeySequence::PartialMatch:
|
||||||
|
// For a partial match we don't know yet if we will handle the shortcut
|
||||||
|
// but we need to say we did, so that we get the follow-up key-presses.
|
||||||
|
return true;
|
||||||
|
case QKeySequence::ExactMatch: {
|
||||||
|
// Save number of identical matches before dispatching
|
||||||
|
// to keep ShortcutMap and tryShortcut reentrant.
|
||||||
|
const int identicalMatches = d->identicals.size();
|
||||||
|
resetState();
|
||||||
|
dispatchEvent(e);
|
||||||
|
// If there are no identicals we've only found disabled shortcuts, and
|
||||||
|
// shouldn't say that we handled the event.
|
||||||
|
return identicalMatches > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Q_UNREACHABLE_RETURN(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \internal
|
||||||
|
Returns the next state of the statemachine
|
||||||
|
If return value is SequenceMatch::ExactMatch, then a call to matches()
|
||||||
|
will return a QObjects* list of all matching objects for the last matching
|
||||||
|
sequence.
|
||||||
|
*/
|
||||||
|
QKeySequence::SequenceMatch ShortcutMap::nextState(QKeyEvent *e)
|
||||||
|
{
|
||||||
|
Q_D(ShortcutMap);
|
||||||
|
// Modifiers can NOT be shortcuts...
|
||||||
|
if (e->key() >= Qt::Key_Shift && e->key() <= Qt::Key_ScrollLock)
|
||||||
|
return d->currentState;
|
||||||
|
|
||||||
|
QKeySequence::SequenceMatch result = QKeySequence::NoMatch;
|
||||||
|
|
||||||
|
// We start fresh each time..
|
||||||
|
d->identicals.clear();
|
||||||
|
|
||||||
|
result = find(e);
|
||||||
|
if (result == QKeySequence::NoMatch && (e->modifiers() & Qt::KeypadModifier)) {
|
||||||
|
// Try to find a match without keypad modifier
|
||||||
|
result = find(e, Qt::KeypadModifier);
|
||||||
|
}
|
||||||
|
if (result == QKeySequence::NoMatch && e->modifiers() & Qt::ShiftModifier) {
|
||||||
|
// If Shift + Key_Backtab, also try Shift + Qt::Key_Tab
|
||||||
|
if (e->key() == Qt::Key_Backtab) {
|
||||||
|
QKeyEvent pe = QKeyEvent(e->type(), Qt::Key_Tab, e->modifiers(), e->text());
|
||||||
|
result = find(&pe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Does the new state require us to clean up?
|
||||||
|
if (result == QKeySequence::NoMatch)
|
||||||
|
clearSequence(d->currentSequences);
|
||||||
|
d->currentState = result;
|
||||||
|
|
||||||
|
qCDebug(lcShortcutMap).nospace() << "ShortcutMap::nextState(" << e << ") = " << result;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \internal
|
||||||
|
Determines if an enabled shortcut has a matching key sequence.
|
||||||
|
*/
|
||||||
|
bool ShortcutMap::hasShortcutForKeySequence(const QKeySequence &seq) const
|
||||||
|
{
|
||||||
|
Q_D(const ShortcutMap);
|
||||||
|
ShortcutEntry entry(seq); // needed for searching
|
||||||
|
const auto itEnd = d->sequences.cend();
|
||||||
|
auto it = std::lower_bound(d->sequences.cbegin(), itEnd, entry);
|
||||||
|
|
||||||
|
for (; it != itEnd; ++it) {
|
||||||
|
if (matches(entry.keyseq, (*it).keyseq) == QKeySequence::ExactMatch
|
||||||
|
&& (*it).correctContext() && (*it).enabled) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//end of the loop: we didn't find anything
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \internal
|
||||||
|
Returns the next state of the statemachine, based
|
||||||
|
on the new key event \a e.
|
||||||
|
Matches are appended to the list of identicals,
|
||||||
|
which can be access through matches().
|
||||||
|
\sa matches
|
||||||
|
*/
|
||||||
|
QKeySequence::SequenceMatch ShortcutMap::find(QKeyEvent *e, int ignoredModifiers)
|
||||||
|
{
|
||||||
|
Q_D(ShortcutMap);
|
||||||
|
if (!d->sequences.size())
|
||||||
|
return QKeySequence::NoMatch;
|
||||||
|
|
||||||
|
createNewSequences(e, d->newEntries, ignoredModifiers);
|
||||||
|
qCDebug(lcShortcutMap) << "Possible shortcut key sequences:" << d->newEntries;
|
||||||
|
|
||||||
|
// Should never happen
|
||||||
|
if (d->newEntries == d->currentSequences) {
|
||||||
|
Q_ASSERT_X(e->key() != Qt::Key_unknown || e->text().size(),
|
||||||
|
"ShortcutMap::find",
|
||||||
|
"New sequence to find identical to previous");
|
||||||
|
return QKeySequence::NoMatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Looking for new identicals, scrap old
|
||||||
|
d->identicals.clear();
|
||||||
|
|
||||||
|
bool partialFound = false;
|
||||||
|
bool identicalDisabledFound = false;
|
||||||
|
QList<QKeySequence> okEntries;
|
||||||
|
int result = QKeySequence::NoMatch;
|
||||||
|
for (int i = d->newEntries.size() - 1; i >= 0; --i) {
|
||||||
|
ShortcutEntry entry(d->newEntries.at(i)); // needed for searching
|
||||||
|
qCDebug(lcShortcutMap) << "- checking entry" << entry.id << entry.keyseq;
|
||||||
|
const auto itEnd = d->sequences.constEnd();
|
||||||
|
auto it = std::lower_bound(d->sequences.constBegin(), itEnd, entry);
|
||||||
|
|
||||||
|
int oneKSResult = QKeySequence::NoMatch;
|
||||||
|
int tempRes = QKeySequence::NoMatch;
|
||||||
|
do {
|
||||||
|
if (it == itEnd)
|
||||||
|
break;
|
||||||
|
tempRes = matches(entry.keyseq, (*it).keyseq);
|
||||||
|
oneKSResult = qMax(oneKSResult, tempRes);
|
||||||
|
qCDebug(lcShortcutMap) << " - matches returned" << tempRes << "for" << entry.keyseq
|
||||||
|
<< it->keyseq << "- correctContext()?" << it->correctContext();
|
||||||
|
if (tempRes != QKeySequence::NoMatch && (*it).correctContext()) {
|
||||||
|
if (tempRes == QKeySequence::ExactMatch) {
|
||||||
|
if ((*it).enabled)
|
||||||
|
d->identicals.append(&*it);
|
||||||
|
else
|
||||||
|
identicalDisabledFound = true;
|
||||||
|
} else if (tempRes == QKeySequence::PartialMatch) {
|
||||||
|
// We don't need partials, if we have identicals
|
||||||
|
if (d->identicals.size())
|
||||||
|
break;
|
||||||
|
// We only care about enabled partials, so we don't consume
|
||||||
|
// key events when all partials are disabled!
|
||||||
|
partialFound |= (*it).enabled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++it;
|
||||||
|
// If we got a valid match on this run, there might still be more keys to check against,
|
||||||
|
// so we'll loop once more. If we get NoMatch, there's guaranteed no more possible
|
||||||
|
// matches in the shortcutmap.
|
||||||
|
} while (tempRes != QKeySequence::NoMatch);
|
||||||
|
|
||||||
|
// If the type of match improves (ergo, NoMatch->Partial, or Partial->Exact), clear the
|
||||||
|
// previous list. If this match is equal or better than the last match, append to the list
|
||||||
|
if (oneKSResult > result) {
|
||||||
|
okEntries.clear();
|
||||||
|
qCDebug(lcShortcutMap)
|
||||||
|
<< "Found better match (" << d->newEntries << "), clearing key sequence list";
|
||||||
|
}
|
||||||
|
if (oneKSResult && oneKSResult >= result) {
|
||||||
|
okEntries << d->newEntries.at(i);
|
||||||
|
qCDebug(lcShortcutMap) << "Added ok key sequence" << d->newEntries;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d->identicals.size()) {
|
||||||
|
result = QKeySequence::ExactMatch;
|
||||||
|
} else if (partialFound) {
|
||||||
|
result = QKeySequence::PartialMatch;
|
||||||
|
} else if (identicalDisabledFound) {
|
||||||
|
result = QKeySequence::ExactMatch;
|
||||||
|
} else {
|
||||||
|
clearSequence(d->currentSequences);
|
||||||
|
result = QKeySequence::NoMatch;
|
||||||
|
}
|
||||||
|
if (result != QKeySequence::NoMatch)
|
||||||
|
d->currentSequences = okEntries;
|
||||||
|
qCDebug(lcShortcutMap) << "Returning shortcut match == " << result;
|
||||||
|
return QKeySequence::SequenceMatch(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \internal
|
||||||
|
Clears \a seq to an empty QKeySequence.
|
||||||
|
Same as doing (the slower)
|
||||||
|
\snippet code/src_gui_kernel_shortcutmap.cpp 0
|
||||||
|
*/
|
||||||
|
void ShortcutMap::clearSequence(QList<QKeySequence> &ksl)
|
||||||
|
{
|
||||||
|
ksl.clear();
|
||||||
|
d_func()->newEntries.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
static QList<int> extractKeyFromEvent(QKeyEvent *e)
|
||||||
|
{
|
||||||
|
QList<int> result;
|
||||||
|
if (e->key() && (e->key() != Qt::Key_unknown))
|
||||||
|
result << e->keyCombination().toCombined();
|
||||||
|
else if (!e->text().isEmpty())
|
||||||
|
result << int(e->text().at(0).unicode() + (int) e->modifiers());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \internal
|
||||||
|
Alters \a seq to the new sequence state, based on the
|
||||||
|
current sequence state, and the new key event \a e.
|
||||||
|
*/
|
||||||
|
void ShortcutMap::createNewSequences(QKeyEvent *e, QList<QKeySequence> &ksl, int ignoredModifiers)
|
||||||
|
{
|
||||||
|
Q_D(ShortcutMap);
|
||||||
|
|
||||||
|
QList<int> possibleKeys = extractKeyFromEvent(e);
|
||||||
|
qCDebug(lcShortcutMap) << "Creating new sequences for" << e
|
||||||
|
<< "with ignoredModifiers=" << Qt::KeyboardModifiers(ignoredModifiers);
|
||||||
|
int pkTotal = possibleKeys.size();
|
||||||
|
if (!pkTotal)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int ssActual = d->currentSequences.size();
|
||||||
|
int ssTotal = qMax(1, ssActual);
|
||||||
|
// Resize to possible permutations of the current sequence(s).
|
||||||
|
ksl.resize(pkTotal * ssTotal);
|
||||||
|
|
||||||
|
int index = ssActual ? d->currentSequences.at(0).count() : 0;
|
||||||
|
for (int pkNum = 0; pkNum < pkTotal; ++pkNum) {
|
||||||
|
for (int ssNum = 0; ssNum < ssTotal; ++ssNum) {
|
||||||
|
int i = (pkNum * ssTotal) + ssNum;
|
||||||
|
QKeySequence &curKsl = ksl[i];
|
||||||
|
if (ssActual) {
|
||||||
|
const QKeySequence &curSeq = d->currentSequences.at(ssNum);
|
||||||
|
curKsl = QKeySequence(curSeq[0], curSeq[1], curSeq[2], curSeq[3]);
|
||||||
|
} else {
|
||||||
|
curKsl = QKeySequence(QKeyCombination::fromCombined(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<QKeyCombination, 4> cur = {curKsl[0], curKsl[1], curKsl[2], curKsl[3]};
|
||||||
|
cur[index] = QKeyCombination::fromCombined(possibleKeys.at(pkNum) & ~ignoredModifiers);
|
||||||
|
curKsl = QKeySequence(cur[0], cur[1], cur[2], cur[3]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \internal
|
||||||
|
Basically the same function as QKeySequence::matches(const QKeySequence &seq) const
|
||||||
|
only that is specially handles Key_hyphen as Key_Minus, as people mix these up all the time and
|
||||||
|
they conceptually the same.
|
||||||
|
*/
|
||||||
|
QKeySequence::SequenceMatch ShortcutMap::matches(const QKeySequence &seq1,
|
||||||
|
const QKeySequence &seq2) const
|
||||||
|
{
|
||||||
|
uint userN = seq1.count(), seqN = seq2.count();
|
||||||
|
|
||||||
|
if (userN > seqN)
|
||||||
|
return QKeySequence::NoMatch;
|
||||||
|
|
||||||
|
// If equal in length, we have a potential ExactMatch sequence,
|
||||||
|
// else we already know it can only be partial.
|
||||||
|
QKeySequence::SequenceMatch match = (userN == seqN ? QKeySequence::ExactMatch
|
||||||
|
: QKeySequence::PartialMatch);
|
||||||
|
|
||||||
|
for (uint i = 0; i < userN; ++i) {
|
||||||
|
int userKey = seq1[i].toCombined(), sequenceKey = seq2[i].toCombined();
|
||||||
|
if ((userKey & Qt::Key_unknown) == Qt::Key_hyphen)
|
||||||
|
userKey = (userKey & Qt::KeyboardModifierMask) | Qt::Key_Minus;
|
||||||
|
if ((sequenceKey & Qt::Key_unknown) == Qt::Key_hyphen)
|
||||||
|
sequenceKey = (sequenceKey & Qt::KeyboardModifierMask) | Qt::Key_Minus;
|
||||||
|
if (userKey != sequenceKey)
|
||||||
|
return QKeySequence::NoMatch;
|
||||||
|
}
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \internal
|
||||||
|
Returns the list of ShortcutEntry's matching the last Identical state.
|
||||||
|
*/
|
||||||
|
QList<const ShortcutEntry *> ShortcutMap::matches() const
|
||||||
|
{
|
||||||
|
Q_D(const ShortcutMap);
|
||||||
|
return d->identicals;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \internal
|
||||||
|
Dispatches QShortcutEvents to widgets who grabbed the matched key sequence.
|
||||||
|
*/
|
||||||
|
void ShortcutMap::dispatchEvent(QKeyEvent *e)
|
||||||
|
{
|
||||||
|
Q_D(ShortcutMap);
|
||||||
|
if (!d->identicals.size())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const QKeySequence &curKey = d->identicals.at(0)->keyseq;
|
||||||
|
if (d->prevSequence != curKey) {
|
||||||
|
d->ambigCount = 0;
|
||||||
|
d->prevSequence = curKey;
|
||||||
|
}
|
||||||
|
// Find next
|
||||||
|
const ShortcutEntry *current = nullptr, *next = nullptr;
|
||||||
|
int i = 0, enabledShortcuts = 0;
|
||||||
|
QList<const ShortcutEntry *> ambiguousShortcuts;
|
||||||
|
while (i < d->identicals.size()) {
|
||||||
|
current = d->identicals.at(i);
|
||||||
|
if (current->enabled || !next) {
|
||||||
|
++enabledShortcuts;
|
||||||
|
if (lcShortcutMap().isDebugEnabled())
|
||||||
|
ambiguousShortcuts.append(current);
|
||||||
|
if (enabledShortcuts > d->ambigCount + 1)
|
||||||
|
break;
|
||||||
|
next = current;
|
||||||
|
}
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
d->ambigCount = (d->identicals.size() == i ? 0 : d->ambigCount + 1);
|
||||||
|
// Don't trigger shortcut if we're autorepeating and the shortcut is
|
||||||
|
// grabbed with not accepting autorepeats.
|
||||||
|
if (!next || (e->isAutoRepeat() && !next->autorepeat))
|
||||||
|
return;
|
||||||
|
// Dispatch next enabled
|
||||||
|
if (lcShortcutMap().isDebugEnabled()) {
|
||||||
|
if (ambiguousShortcuts.size() > 1) {
|
||||||
|
qCDebug(lcShortcutMap)
|
||||||
|
<< "The following shortcuts are about to be activated ambiguously:";
|
||||||
|
for (const ShortcutEntry *entry : std::as_const(ambiguousShortcuts))
|
||||||
|
qCDebug(lcShortcutMap).nospace()
|
||||||
|
<< "- " << entry->keyseq << " (belonging to " << entry->owner << ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
qCDebug(lcShortcutMap).nospace()
|
||||||
|
<< "ShortcutMap::dispatchEvent(): Sending QShortcutEvent(\"" << next->keyseq.toString()
|
||||||
|
<< "\", " << next->id << ", " << static_cast<bool>(enabledShortcuts > 1)
|
||||||
|
<< ") to object(" << next->owner << ')';
|
||||||
|
}
|
||||||
|
QShortcutEvent se(next->keyseq, next->id, enabledShortcuts > 1);
|
||||||
|
QCoreApplication::sendEvent(const_cast<QObject *>(next->owner), &se);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Terminal::Internal
|
52
src/plugins/terminal/shortcutmap.h
Normal file
52
src/plugins/terminal/shortcutmap.h
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
// Copyright (C) 2023 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
// COPIED FROM shortcutmap_p.h
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QKeySequence>
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
class QKeyEvent;
|
||||||
|
class QObject;
|
||||||
|
|
||||||
|
namespace Terminal::Internal {
|
||||||
|
|
||||||
|
struct ShortcutEntry;
|
||||||
|
class ShortcutMapPrivate;
|
||||||
|
|
||||||
|
class ShortcutMap
|
||||||
|
{
|
||||||
|
Q_DECLARE_PRIVATE(ShortcutMap)
|
||||||
|
public:
|
||||||
|
ShortcutMap();
|
||||||
|
~ShortcutMap();
|
||||||
|
|
||||||
|
typedef bool (*ContextMatcher)(QObject *object, Qt::ShortcutContext context);
|
||||||
|
|
||||||
|
int addShortcut(QObject *owner,
|
||||||
|
const QKeySequence &key,
|
||||||
|
Qt::ShortcutContext context,
|
||||||
|
ContextMatcher matcher);
|
||||||
|
int removeShortcut(int id, QObject *owner, const QKeySequence &key = QKeySequence());
|
||||||
|
|
||||||
|
QKeySequence::SequenceMatch state();
|
||||||
|
|
||||||
|
bool tryShortcut(QKeyEvent *e);
|
||||||
|
bool hasShortcutForKeySequence(const QKeySequence &seq) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void resetState();
|
||||||
|
QKeySequence::SequenceMatch nextState(QKeyEvent *e);
|
||||||
|
void dispatchEvent(QKeyEvent *e);
|
||||||
|
|
||||||
|
QKeySequence::SequenceMatch find(QKeyEvent *e, int ignoredModifiers = 0);
|
||||||
|
QKeySequence::SequenceMatch matches(const QKeySequence &seq1, const QKeySequence &seq2) const;
|
||||||
|
QList<const ShortcutEntry *> matches() const;
|
||||||
|
void createNewSequences(QKeyEvent *e, QList<QKeySequence> &ksl, int ignoredModifiers);
|
||||||
|
void clearSequence(QList<QKeySequence> &ksl);
|
||||||
|
|
||||||
|
QScopedPointer<ShortcutMapPrivate> d_ptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Terminal::Internal
|
@@ -21,6 +21,8 @@ QtcPlugin {
|
|||||||
"shellmodel.h",
|
"shellmodel.h",
|
||||||
"shellintegration.cpp",
|
"shellintegration.cpp",
|
||||||
"shellintegration.h",
|
"shellintegration.h",
|
||||||
|
"shortcutmap.cpp",
|
||||||
|
"shortcutmap.h",
|
||||||
"terminal.qrc",
|
"terminal.qrc",
|
||||||
"terminalconstants.h",
|
"terminalconstants.h",
|
||||||
"terminalicons.h",
|
"terminalicons.h",
|
||||||
|
@@ -4,6 +4,7 @@
|
|||||||
#include "terminalpane.h"
|
#include "terminalpane.h"
|
||||||
|
|
||||||
#include "shellmodel.h"
|
#include "shellmodel.h"
|
||||||
|
#include "shortcutmap.h"
|
||||||
#include "terminalconstants.h"
|
#include "terminalconstants.h"
|
||||||
#include "terminalicons.h"
|
#include "terminalicons.h"
|
||||||
#include "terminalsettings.h"
|
#include "terminalsettings.h"
|
||||||
@@ -36,9 +37,9 @@ using namespace Core;
|
|||||||
|
|
||||||
TerminalPane::TerminalPane(QObject *parent)
|
TerminalPane::TerminalPane(QObject *parent)
|
||||||
: IOutputPane(parent)
|
: IOutputPane(parent)
|
||||||
, m_context("Terminal.Pane", Core::Constants::C_GLOBAL_CUTOFF)
|
, m_selfContext("Terminal.Pane")
|
||||||
{
|
{
|
||||||
setupContext(m_context, &m_tabWidget);
|
setupContext(m_selfContext, &m_tabWidget);
|
||||||
setZoomButtonsEnabled(true);
|
setZoomButtonsEnabled(true);
|
||||||
|
|
||||||
connect(this, &IOutputPane::zoomInRequested, this, [this] {
|
connect(this, &IOutputPane::zoomInRequested, this, [this] {
|
||||||
@@ -52,6 +53,9 @@ TerminalPane::TerminalPane(QObject *parent)
|
|||||||
|
|
||||||
initActions();
|
initActions();
|
||||||
|
|
||||||
|
m_lockKeyboardButton = new QToolButton();
|
||||||
|
m_lockKeyboardButton->setDefaultAction(&lockKeyboard);
|
||||||
|
|
||||||
m_newTerminalButton = new QToolButton();
|
m_newTerminalButton = new QToolButton();
|
||||||
m_newTerminalButton->setDefaultAction(&newTerminal);
|
m_newTerminalButton->setDefaultAction(&newTerminal);
|
||||||
|
|
||||||
@@ -124,6 +128,14 @@ void TerminalPane::openTerminal(const OpenTerminalParameters ¶meters)
|
|||||||
}
|
}
|
||||||
|
|
||||||
const auto terminalWidget = new TerminalWidget(&m_tabWidget, parametersCopy);
|
const auto terminalWidget = new TerminalWidget(&m_tabWidget, parametersCopy);
|
||||||
|
|
||||||
|
using namespace Constants;
|
||||||
|
terminalWidget->unlockGlobalAction("Coreplugin.OutputPane.minmax");
|
||||||
|
terminalWidget->unlockGlobalAction(Core::Constants::LOCATE);
|
||||||
|
terminalWidget->unlockGlobalAction(NEWTERMINAL);
|
||||||
|
terminalWidget->unlockGlobalAction(NEXTTERMINAL);
|
||||||
|
terminalWidget->unlockGlobalAction(PREVTERMINAL);
|
||||||
|
|
||||||
m_tabWidget.setCurrentIndex(m_tabWidget.addTab(terminalWidget, Tr::tr("Terminal")));
|
m_tabWidget.setCurrentIndex(m_tabWidget.addTab(terminalWidget, Tr::tr("Terminal")));
|
||||||
setupTerminalWidget(terminalWidget);
|
setupTerminalWidget(terminalWidget);
|
||||||
|
|
||||||
@@ -229,6 +241,23 @@ void TerminalPane::initActions()
|
|||||||
{
|
{
|
||||||
createShellMenu();
|
createShellMenu();
|
||||||
|
|
||||||
|
lockKeyboard.setCheckable(true);
|
||||||
|
lockKeyboard.setChecked(TerminalSettings::instance().lockKeyboard());
|
||||||
|
|
||||||
|
auto updateLockKeyboard = [this](bool locked) {
|
||||||
|
TerminalSettings::instance().lockKeyboard.setValue(locked);
|
||||||
|
if (locked) {
|
||||||
|
lockKeyboard.setIcon(Icons::LOCKED_TOOLBAR.icon());
|
||||||
|
lockKeyboard.setToolTip(Tr::tr("Keyboard shortcuts will be send to the Terminal"));
|
||||||
|
} else {
|
||||||
|
lockKeyboard.setIcon(Icons::UNLOCKED_TOOLBAR.icon());
|
||||||
|
lockKeyboard.setToolTip(Tr::tr("Keyboard shortcuts will be send to Qt Creator"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
updateLockKeyboard(TerminalSettings::instance().lockKeyboard());
|
||||||
|
connect(&lockKeyboard, &QAction::toggled, this, updateLockKeyboard);
|
||||||
|
|
||||||
newTerminal.setText(Tr::tr("New Terminal"));
|
newTerminal.setText(Tr::tr("New Terminal"));
|
||||||
newTerminal.setIcon(NEW_TERMINAL_ICON.icon());
|
newTerminal.setIcon(NEW_TERMINAL_ICON.icon());
|
||||||
newTerminal.setToolTip(Tr::tr("Create a new Terminal."));
|
newTerminal.setToolTip(Tr::tr("Create a new Terminal."));
|
||||||
@@ -242,25 +271,22 @@ void TerminalPane::initActions()
|
|||||||
|
|
||||||
using namespace Constants;
|
using namespace Constants;
|
||||||
|
|
||||||
ActionManager::registerAction(&newTerminal, NEWTERMINAL, m_context)
|
Command *cmd = ActionManager::registerAction(&newTerminal, NEWTERMINAL, m_selfContext);
|
||||||
->setDefaultKeySequences({QKeySequence(
|
cmd->setDefaultKeySequences({QKeySequence(
|
||||||
HostOsInfo::isMacHost() ? QLatin1String("Ctrl+T") : QLatin1String("Ctrl+Shift+T"))});
|
HostOsInfo::isMacHost() ? QLatin1String("Ctrl+T") : QLatin1String("Ctrl+Shift+T"))});
|
||||||
|
|
||||||
ActionManager::registerAction(&nextTerminal, NEXTTERMINAL, m_context)
|
ActionManager::registerAction(&nextTerminal, NEXTTERMINAL, m_selfContext)
|
||||||
->setDefaultKeySequences(
|
->setDefaultKeySequences(
|
||||||
{QKeySequence("Alt+Tab"),
|
{QKeySequence("Alt+Tab"),
|
||||||
QKeySequence(HostOsInfo::isMacHost() ? QLatin1String("Ctrl+Shift+[")
|
QKeySequence(HostOsInfo::isMacHost() ? QLatin1String("Ctrl+Shift+[")
|
||||||
: QLatin1String("Ctrl+PgUp"))});
|
: QLatin1String("Ctrl+PgUp"))});
|
||||||
|
|
||||||
ActionManager::registerAction(&prevTerminal, PREVTERMINAL, m_context)
|
ActionManager::registerAction(&prevTerminal, PREVTERMINAL, m_selfContext)
|
||||||
->setDefaultKeySequences(
|
->setDefaultKeySequences(
|
||||||
{QKeySequence("Alt+Shift+Tab"),
|
{QKeySequence("Alt+Shift+Tab"),
|
||||||
QKeySequence(HostOsInfo::isMacHost() ? QLatin1String("Ctrl+Shift+]")
|
QKeySequence(HostOsInfo::isMacHost() ? QLatin1String("Ctrl+Shift+]")
|
||||||
: QLatin1String("Ctrl+PgDown"))});
|
: QLatin1String("Ctrl+PgDown"))});
|
||||||
|
|
||||||
m_minMax = TerminalWidget::unlockGlobalAction("Coreplugin.OutputPane.minmax", m_context);
|
|
||||||
m_locate = TerminalWidget::unlockGlobalAction(Core::Constants::LOCATE, m_context);
|
|
||||||
|
|
||||||
connect(&newTerminal, &QAction::triggered, this, [this] { openTerminal({}); });
|
connect(&newTerminal, &QAction::triggered, this, [this] { openTerminal({}); });
|
||||||
connect(&closeTerminal, &QAction::triggered, this, [this] {
|
connect(&closeTerminal, &QAction::triggered, this, [this] {
|
||||||
removeTab(m_tabWidget.currentIndex());
|
removeTab(m_tabWidget.currentIndex());
|
||||||
@@ -303,10 +329,11 @@ void TerminalPane::createShellMenu()
|
|||||||
QList<QWidget *> TerminalPane::toolBarWidgets() const
|
QList<QWidget *> TerminalPane::toolBarWidgets() const
|
||||||
{
|
{
|
||||||
QList<QWidget *> widgets = IOutputPane::toolBarWidgets();
|
QList<QWidget *> widgets = IOutputPane::toolBarWidgets();
|
||||||
|
|
||||||
widgets.prepend(m_newTerminalButton);
|
widgets.prepend(m_newTerminalButton);
|
||||||
widgets.prepend(m_closeTerminalButton);
|
widgets.prepend(m_closeTerminalButton);
|
||||||
|
|
||||||
return widgets << m_openSettingsButton << m_escSettingButton;
|
return widgets << m_openSettingsButton << m_lockKeyboardButton << m_escSettingButton;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString TerminalPane::displayName() const
|
QString TerminalPane::displayName() const
|
||||||
|
@@ -62,18 +62,17 @@ private:
|
|||||||
QToolButton *m_closeTerminalButton{nullptr};
|
QToolButton *m_closeTerminalButton{nullptr};
|
||||||
QToolButton *m_openSettingsButton{nullptr};
|
QToolButton *m_openSettingsButton{nullptr};
|
||||||
QToolButton *m_escSettingButton{nullptr};
|
QToolButton *m_escSettingButton{nullptr};
|
||||||
|
QToolButton *m_lockKeyboardButton{nullptr};
|
||||||
UnlockedGlobalAction m_minMax;
|
|
||||||
UnlockedGlobalAction m_locate;
|
|
||||||
|
|
||||||
QAction newTerminal;
|
QAction newTerminal;
|
||||||
QAction nextTerminal;
|
QAction nextTerminal;
|
||||||
QAction prevTerminal;
|
QAction prevTerminal;
|
||||||
QAction closeTerminal;
|
QAction closeTerminal;
|
||||||
|
QAction lockKeyboard;
|
||||||
|
|
||||||
QMenu m_shellMenu;
|
QMenu m_shellMenu;
|
||||||
|
|
||||||
Core::Context m_context;
|
Core::Context m_selfContext;
|
||||||
|
|
||||||
bool m_widgetInitialized{false};
|
bool m_widgetInitialized{false};
|
||||||
bool m_isVisible{false};
|
bool m_isVisible{false};
|
||||||
|
@@ -32,6 +32,7 @@ public:
|
|||||||
|
|
||||||
Utils::BoolAspect sendEscapeToTerminal{this};
|
Utils::BoolAspect sendEscapeToTerminal{this};
|
||||||
Utils::BoolAspect audibleBell{this};
|
Utils::BoolAspect audibleBell{this};
|
||||||
|
Utils::BoolAspect lockKeyboard{this};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // Terminal
|
} // Terminal
|
||||||
|
@@ -252,10 +252,37 @@ void TerminalWidget::setupColors()
|
|||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
static RegisteredAction registerAction(Id commandId, const Context &context)
|
static bool contextMatcher(QObject *, Qt::ShortcutContext)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TerminalWidget::registerShortcut(Command *cmd)
|
||||||
|
{
|
||||||
|
QTC_ASSERT(cmd, return);
|
||||||
|
auto addShortCut = [this, cmd] {
|
||||||
|
for (const auto &keySequence : cmd->keySequences()) {
|
||||||
|
m_shortcutMap.addShortcut(cmd->action(),
|
||||||
|
keySequence,
|
||||||
|
Qt::ShortcutContext::WindowShortcut,
|
||||||
|
contextMatcher);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
auto removeShortCut = [this, cmd] { m_shortcutMap.removeShortcut(0, cmd->action()); };
|
||||||
|
addShortCut();
|
||||||
|
|
||||||
|
connect(cmd, &Command::keySequenceChanged, this, [addShortCut, removeShortCut]() {
|
||||||
|
removeShortCut();
|
||||||
|
addShortCut();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
RegisteredAction TerminalWidget::registerAction(Id commandId, const Context &context)
|
||||||
{
|
{
|
||||||
QAction *action = new QAction;
|
QAction *action = new QAction;
|
||||||
ActionManager::registerAction(action, commandId, context);
|
Command *cmd = ActionManager::registerAction(action, commandId, context);
|
||||||
|
|
||||||
|
registerShortcut(cmd);
|
||||||
|
|
||||||
return RegisteredAction(action, [commandId](QAction *a) {
|
return RegisteredAction(action, [commandId](QAction *a) {
|
||||||
ActionManager::unregisterAction(a, commandId);
|
ActionManager::unregisterAction(a, commandId);
|
||||||
@@ -287,10 +314,10 @@ void TerminalWidget::setupActions()
|
|||||||
this,
|
this,
|
||||||
&TerminalWidget::moveCursorWordRight);
|
&TerminalWidget::moveCursorWordRight);
|
||||||
|
|
||||||
m_exit = unlockGlobalAction(Core::Constants::EXIT, m_context);
|
unlockGlobalAction(Core::Constants::EXIT);
|
||||||
m_options = unlockGlobalAction(Core::Constants::OPTIONS, m_context);
|
unlockGlobalAction(Core::Constants::OPTIONS);
|
||||||
m_settings = unlockGlobalAction("Preferences.Terminal.General", m_context);
|
unlockGlobalAction("Preferences.Terminal.General");
|
||||||
m_findInDocument = unlockGlobalAction(Core::Constants::FIND_IN_DOCUMENT, m_context);
|
unlockGlobalAction(Core::Constants::FIND_IN_DOCUMENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TerminalWidget::closeTerminal()
|
void TerminalWidget::closeTerminal()
|
||||||
@@ -1506,6 +1533,11 @@ void TerminalWidget::showEvent(QShowEvent *event)
|
|||||||
|
|
||||||
bool TerminalWidget::event(QEvent *event)
|
bool TerminalWidget::event(QEvent *event)
|
||||||
{
|
{
|
||||||
|
if (TerminalSettings::instance().lockKeyboard() && event->type() == QEvent::ShortcutOverride) {
|
||||||
|
event->accept();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (event->type() == QEvent::Paint) {
|
if (event->type() == QEvent::Paint) {
|
||||||
QPainter p(this);
|
QPainter p(this);
|
||||||
p.fillRect(QRect(QPoint(0, 0), size()), m_currentColors[ColorIndex::Background]);
|
p.fillRect(QRect(QPoint(0, 0), size()), m_currentColors[ColorIndex::Background]);
|
||||||
@@ -1513,12 +1545,16 @@ bool TerminalWidget::event(QEvent *event)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (event->type() == QEvent::KeyPress) {
|
if (event->type() == QEvent::KeyPress) {
|
||||||
QKeyEvent *k = (QKeyEvent *) event;
|
auto k = static_cast<QKeyEvent *>(event);
|
||||||
|
|
||||||
|
if (TerminalSettings::instance().lockKeyboard() && m_shortcutMap.tryShortcut(k))
|
||||||
|
return true;
|
||||||
|
|
||||||
keyPressEvent(k);
|
keyPressEvent(k);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (event->type() == QEvent::KeyRelease) {
|
if (event->type() == QEvent::KeyRelease) {
|
||||||
QKeyEvent *k = (QKeyEvent *) event;
|
auto k = static_cast<QKeyEvent *>(event);
|
||||||
keyReleaseEvent(k);
|
keyReleaseEvent(k);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1565,21 +1601,11 @@ void TerminalWidget::initActions()
|
|||||||
ActionManager::registerAction(&clearTerminal, Constants::CLEAR_TERMINAL, context);
|
ActionManager::registerAction(&clearTerminal, Constants::CLEAR_TERMINAL, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
UnlockedGlobalAction TerminalWidget::unlockGlobalAction(const Utils::Id &commandId,
|
void TerminalWidget::unlockGlobalAction(const Utils::Id &commandId)
|
||||||
const Context &context)
|
|
||||||
{
|
{
|
||||||
QAction *srcAction = ActionManager::command(commandId)->actionForContext(
|
Command *cmd = ActionManager::command(commandId);
|
||||||
Core::Constants::C_GLOBAL);
|
QTC_ASSERT(cmd, return);
|
||||||
|
registerShortcut(cmd);
|
||||||
ProxyAction *proxy = ProxyAction::proxyActionWithIcon(srcAction, srcAction->icon());
|
|
||||||
ActionManager::registerAction(proxy, commandId, context);
|
|
||||||
|
|
||||||
UnlockedGlobalAction registeredAction(proxy, [commandId](QAction *a) {
|
|
||||||
ActionManager::unregisterAction(a, commandId);
|
|
||||||
delete a;
|
|
||||||
});
|
|
||||||
|
|
||||||
return registeredAction;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Terminal
|
} // namespace Terminal
|
||||||
|
@@ -3,12 +3,14 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "shortcutmap.h"
|
||||||
#include "terminalsearch.h"
|
#include "terminalsearch.h"
|
||||||
#include "terminalsurface.h"
|
#include "terminalsurface.h"
|
||||||
|
|
||||||
#include <aggregation/aggregate.h>
|
#include <aggregation/aggregate.h>
|
||||||
|
|
||||||
#include <coreplugin/icontext.h>
|
#include <coreplugin/icontext.h>
|
||||||
|
#include <coreplugin/actionmanager/command.h>
|
||||||
|
|
||||||
#include <utils/link.h>
|
#include <utils/link.h>
|
||||||
#include <utils/process.h>
|
#include <utils/process.h>
|
||||||
@@ -24,7 +26,6 @@
|
|||||||
|
|
||||||
namespace Terminal {
|
namespace Terminal {
|
||||||
|
|
||||||
using UnlockedGlobalAction = std::unique_ptr<QAction, std::function<void(QAction *)>>;
|
|
||||||
using RegisteredAction = std::unique_ptr<QAction, std::function<void(QAction *)>>;
|
using RegisteredAction = std::unique_ptr<QAction, std::function<void(QAction *)>>;
|
||||||
|
|
||||||
class TerminalWidget : public QAbstractScrollArea
|
class TerminalWidget : public QAbstractScrollArea
|
||||||
@@ -91,8 +92,7 @@ public:
|
|||||||
|
|
||||||
static void initActions();
|
static void initActions();
|
||||||
|
|
||||||
[[nodiscard]] static UnlockedGlobalAction unlockGlobalAction(const Utils::Id &commandId,
|
void unlockGlobalAction(const Utils::Id &commandId);
|
||||||
const Core::Context &context);
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void started(qint64 pid);
|
void started(qint64 pid);
|
||||||
@@ -189,6 +189,9 @@ protected:
|
|||||||
|
|
||||||
void updateCopyState();
|
void updateCopyState();
|
||||||
|
|
||||||
|
RegisteredAction registerAction(Utils::Id commandId, const Core::Context &context);
|
||||||
|
void registerShortcut(Core::Command *command);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Core::Context m_context;
|
Core::Context m_context;
|
||||||
std::unique_ptr<Utils::Process> m_process;
|
std::unique_ptr<Utils::Process> m_process;
|
||||||
@@ -248,10 +251,7 @@ private:
|
|||||||
RegisteredAction m_moveCursorWordRight;
|
RegisteredAction m_moveCursorWordRight;
|
||||||
RegisteredAction m_close;
|
RegisteredAction m_close;
|
||||||
|
|
||||||
UnlockedGlobalAction m_findInDocument;
|
Internal::ShortcutMap m_shortcutMap;
|
||||||
UnlockedGlobalAction m_exit;
|
|
||||||
UnlockedGlobalAction m_options;
|
|
||||||
UnlockedGlobalAction m_settings;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Terminal
|
} // namespace Terminal
|
||||||
|
Reference in New Issue
Block a user