Git commit 025edb11ca9b0fa3685802e6c9cb608a4daa7911 by Simon St James. Committed on 18/06/2016 at 08:35. Pushed by sstjames into branch 'master'. Annoyingly, in Qt 5.5 we can no longer trigger shortcuts by replaying QKeyE= vents (something that the Vi Mode - which stores and swallows all key event= s by default in case they are part of a Mapping and replays them if it turn= s out they weren't - depends on), so if we are stealing shortcuts, they wil= l no longer be triggered e.g. pressing Ctrl+S will not save! This patch rew= orks the whole mechanism, moving from "swallow all QKeyEvents and maybe rep= lay later" to "decide whether to swallow shortcuts, but if not, let Qt's sh= ortcut handling system despatch the QShortcutOverride itself". This mostly = fixes things, though we can't e.g. create a mapping that, when triggered, s= aves the document or triggers any of Kate's shortcuts, plus some other smal= l edge cases :( BUG:35333 REVIEW:D1894 M +1 -1 src/inputmode/kateabstractinputmode.h M +1 -1 src/inputmode/katenormalinputmode.cpp M +1 -1 src/inputmode/katenormalinputmode.h M +25 -22 src/inputmode/kateviinputmode.cpp M +3 -1 src/inputmode/kateviinputmode.h M +4 -2 src/vimode/emulatedcommandbar/emulatedcommandbar.cpp M +2 -1 src/vimode/emulatedcommandbar/emulatedcommandbar.h M +1 -1 src/vimode/inputmodemanager.cpp M +28 -2 src/vimode/keymapper.cpp M +7 -2 src/vimode/modes/normalvimode.cpp http://commits.kde.org/ktexteditor/025edb11ca9b0fa3685802e6c9cb608a4daa7911 diff --git a/src/inputmode/kateabstractinputmode.h b/src/inputmode/kateabst= ractinputmode.h index 70d9512..c34f3ba 100644 --- a/src/inputmode/kateabstractinputmode.h +++ b/src/inputmode/kateabstractinputmode.h @@ -53,7 +53,7 @@ public: virtual bool overwrite() const =3D 0; virtual void overwrittenChar(const QChar &) =3D 0; virtual void clearSelection() =3D 0; - virtual bool stealKey(const QKeyEvent *) const =3D 0; + virtual bool stealKey(QKeyEvent* k) =3D 0; = virtual void gotFocus() =3D 0; virtual void lostFocus() =3D 0; diff --git a/src/inputmode/katenormalinputmode.cpp b/src/inputmode/katenorm= alinputmode.cpp index 4990892..bc18f65 100644 --- a/src/inputmode/katenormalinputmode.cpp +++ b/src/inputmode/katenormalinputmode.cpp @@ -67,7 +67,7 @@ void KateNormalInputMode::clearSelection() view()->clearSelection(); } = -bool KateNormalInputMode::stealKey(const QKeyEvent *) const +bool KateNormalInputMode::stealKey( QKeyEvent *) { return false; } diff --git a/src/inputmode/katenormalinputmode.h b/src/inputmode/katenormal= inputmode.h index fcb4e7f..7baaad0 100644 --- a/src/inputmode/katenormalinputmode.h +++ b/src/inputmode/katenormalinputmode.h @@ -46,7 +46,7 @@ public: void overwrittenChar(const QChar &) Q_DECL_OVERRIDE; = void clearSelection() Q_DECL_OVERRIDE; - bool stealKey(const QKeyEvent *) const Q_DECL_OVERRIDE; + bool stealKey(QKeyEvent *) Q_DECL_OVERRIDE; = void gotFocus() Q_DECL_OVERRIDE; void lostFocus() Q_DECL_OVERRIDE; diff --git a/src/inputmode/kateviinputmode.cpp b/src/inputmode/kateviinputm= ode.cpp index 557cec1..a7609cd 100644 --- a/src/inputmode/kateviinputmode.cpp +++ b/src/inputmode/kateviinputmode.cpp @@ -65,6 +65,7 @@ KateViInputMode::KateViInputMode(KateViewInternal *viewIn= ternal, KateVi::GlobalS , m_viModeEmulatedCommandBar(0) , m_viGlobal(global) , m_caret(KateRenderer::Block) + , m_nextKeypressIsOverriddenShortCut(false) , m_activated(false) { m_relLineNumbers =3D KateViewConfig::global()->viRelativeLineNumbers(); @@ -131,11 +132,22 @@ void KateViInputMode::clearSelection() // do nothing, handled elsewhere } = -bool KateViInputMode::stealKey(const QKeyEvent *k) const +bool KateViInputMode::stealKey(QKeyEvent *k) { - const bool steal =3D KateViewConfig::global()->viInputModeStealKeys(); - return steal && (m_viModeManager->getCurrentViMode() !=3D KateVi::ViMo= de::InsertMode || - k->modifiers() =3D=3D Qt::ControlModifier || k->ke= y() =3D=3D Qt::Key_Insert); + if (!KateViewConfig::global()->viInputModeStealKeys()) + { + return false; + } + + // Actually see if we can make use of this key - if so, we've stolen i= t; if not, + // let Qt's shortcut handling system deal with it. + const bool stolen =3D keyPress(k); + if (stolen) + { + // Qt will replay this QKeyEvent, next time as an ordinary KeyPres= s. + m_nextKeypressIsOverriddenShortCut =3D true; + } + return stolen; } = KTextEditor::View::InputMode KateViInputMode::viewInputMode() const @@ -255,7 +267,7 @@ void KateViInputMode::showViModeEmulatedCommandBar() KateVi::EmulatedCommandBar *KateViInputMode::viModeEmulatedCommandBar() { if (!m_viModeEmulatedCommandBar) { - m_viModeEmulatedCommandBar =3D new KateVi::EmulatedCommandBar(m_vi= ModeManager, view()); + m_viModeEmulatedCommandBar =3D new KateVi::EmulatedCommandBar(this= , m_viModeManager, view()); m_viModeEmulatedCommandBar->hide(); } = @@ -269,24 +281,15 @@ void KateViInputMode::updateRendererConfig() = bool KateViInputMode::keyPress(QKeyEvent *e) { - if (m_viModeManager->getCurrentViMode() =3D=3D KateVi::InsertMode || - m_viModeManager->getCurrentViMode() =3D=3D KateVi::ReplaceMode) { + if (m_nextKeypressIsOverriddenShortCut) + { + // This is just the replay of a shortcut that we stole, this time = as a QKeyEvent. + // Ignore it, as we'll have already handled it via stealKey()! + m_nextKeypressIsOverriddenShortCut =3D false; + return true; + } = - if (m_viModeManager->handleKeypress(e)) { - return true; - } else if (e->modifiers() !=3D Qt::NoModifier && e->modifiers() != =3D Qt::ShiftModifier) { - // re-post key events not handled if they have a modifier othe= r than shift - QEvent *copy =3D new QKeyEvent(e->type(), e->key(), e->modifie= rs(), e->text(), - e->isAutoRepeat(), e->count()); - QCoreApplication::postEvent(viewInternal()->parent(), copy); - } - } else { // !InsertMode - if (!m_viModeManager->handleKeypress(e)) { - // we didn't need that keypress, un-steal it :-) - QEvent *copy =3D new QKeyEvent(e->type(), e->key(), e->modifie= rs(), e->text(), - e->isAutoRepeat(), e->count()); - QCoreApplication::postEvent(viewInternal()->parent(), copy); - } + if (m_viModeManager->handleKeypress(e)) { emit view()->viewModeChanged(view(), viewMode()); return true; } diff --git a/src/inputmode/kateviinputmode.h b/src/inputmode/kateviinputmod= e.h index 21f95cb..330148f 100644 --- a/src/inputmode/kateviinputmode.h +++ b/src/inputmode/kateviinputmode.h @@ -49,7 +49,7 @@ public: void overwrittenChar(const QChar &) Q_DECL_OVERRIDE; = void clearSelection() Q_DECL_OVERRIDE; - bool stealKey(const QKeyEvent *) const Q_DECL_OVERRIDE; + bool stealKey(QKeyEvent *) Q_DECL_OVERRIDE; = void gotFocus() Q_DECL_OVERRIDE; void lostFocus() Q_DECL_OVERRIDE; @@ -92,6 +92,8 @@ private: KateVi::GlobalState *m_viGlobal; KateRenderer::caretStyles m_caret; = + bool m_nextKeypressIsOverriddenShortCut; + // configs bool m_relLineNumbers; bool m_activated; diff --git a/src/vimode/emulatedcommandbar/emulatedcommandbar.cpp b/src/vim= ode/emulatedcommandbar/emulatedcommandbar.cpp index 818ec95..3197981 100644 --- a/src/vimode/emulatedcommandbar/emulatedcommandbar.cpp +++ b/src/vimode/emulatedcommandbar/emulatedcommandbar.cpp @@ -27,6 +27,7 @@ #include "../globalstate.h" #include #include +#include #include #include "matchhighlighter.h" #include "interactivesedreplacemode.h" @@ -69,8 +70,9 @@ QString escapedForSearchingAsLiteral(const QString &origi= nalQtRegex) } } = -EmulatedCommandBar::EmulatedCommandBar(InputModeManager *viInputModeManage= r, QWidget *parent) +EmulatedCommandBar::EmulatedCommandBar(KateViInputMode* viInputMode, Input= ModeManager *viInputModeManager, QWidget *parent) : KateViewBarWidget(false, parent), + m_viInputMode(viInputMode), m_viInputModeManager(viInputModeManager), m_view(viInputModeManager->view()) { @@ -230,7 +232,7 @@ bool EmulatedCommandBar::eventFilter(QObject *object, Q= Event *event) if (event->type() =3D=3D QEvent::KeyPress) { // Re-route this keypress through Vim's central keypress handling = area, so that we can use the keypress in e.g. // mappings and macros. - return m_viInputModeManager->handleKeypress(static_cast(event)); + return m_viInputMode->keyPress(static_cast(event)); } return false; } diff --git a/src/vimode/emulatedcommandbar/emulatedcommandbar.h b/src/vimod= e/emulatedcommandbar/emulatedcommandbar.h index 2751cae..3704662 100644 --- a/src/vimode/emulatedcommandbar/emulatedcommandbar.h +++ b/src/vimode/emulatedcommandbar/emulatedcommandbar.h @@ -56,7 +56,7 @@ class KTEXTEDITOR_EXPORT EmulatedCommandBar : public Kate= ViewBarWidget = public: enum Mode { NoMode, SearchForward, SearchBackward, Command }; - explicit EmulatedCommandBar(InputModeManager *viInputModeManager, QWid= get *parent =3D 0); + explicit EmulatedCommandBar(KateViInputMode* viInputMode, InputModeMan= ager *viInputModeManager, QWidget *parent =3D 0); virtual ~EmulatedCommandBar(); void init(Mode mode, const QString &initialText =3D QString()); bool isActive(); @@ -71,6 +71,7 @@ public: = private: = + KateViInputMode *m_viInputMode; InputModeManager *m_viInputModeManager; bool m_isActive =3D false; bool m_wasAborted =3D true; diff --git a/src/vimode/inputmodemanager.cpp b/src/vimode/inputmodemanager.= cpp index db06acf..650c09e 100644 --- a/src/vimode/inputmodemanager.cpp +++ b/src/vimode/inputmodemanager.cpp @@ -118,7 +118,7 @@ bool InputModeManager::handleKeypress(const QKeyEvent *= e) // of a mapping, we don't want to record them when they are played bac= k by m_keyMapper, hence // the "!isPlayingBackRejectedKeys()". And obviously, since we're reco= rding keys before they are mapped, we don't // want to also record the executed mapping, as when we replayed the m= acro, we'd get duplication! - if (m_macroRecorder->isRecording() && !m_macroRecorder->isReplaying() = && !isSyntheticSearchCompletedKeyPress && !keyMapper()->isExecutingMapping(= ) && !keyMapper()->isPlayingBackRejectedKeys()) { + if (m_macroRecorder->isRecording() && !m_macroRecorder->isReplaying() = && !isSyntheticSearchCompletedKeyPress && !keyMapper()->isExecutingMapping(= ) && !keyMapper()->isPlayingBackRejectedKeys() && !lastChangeRecorder()->is= Replaying()) { m_macroRecorder->record(*e); } = diff --git a/src/vimode/keymapper.cpp b/src/vimode/keymapper.cpp index 10091de..887b3f6 100644 --- a/src/vimode/keymapper.cpp +++ b/src/vimode/keymapper.cpp @@ -26,6 +26,7 @@ #include #include #include +#include "macrorecorder.h" = #include = @@ -119,8 +120,33 @@ bool KeyMapper::handleKeypress(QChar key) // We've been swallowing all the keypresses meant for m_keys for o= ur mapping keys; now that we know // this cannot be a mapping, restore them. Q_ASSERT(!isPartialMapping && !isFullMapping); - playBackRejectedKeys(); - return true; + const bool isUserKeypress =3D !m_viInputModeManager->macroRecorder= ()->isReplaying() && !isExecutingMapping(); + if (isUserKeypress && m_mappingKeys.size() =3D=3D 1) + { + // Ugh - unpleasant complication starting with Qt 5.5-ish - it= is no + // longer possible to replay QKeyEvents in such a way that sho= rtcuts + // are triggered, so if we want to correctly handle a shortcut= (e.g. + // ctrl+s for e.g. Save), we can no longer pop it into m_mappi= ngKeys + // then immediately playBackRejectedKeys() (as this will not t= rigger + // the e.g. Save shortcut) - the best we can do is, if e.g. ct= rl+s is + // not part of any mapping, immediately return false, *not* ca= lling + // playBackRejectedKeys() and clearing m_mappingKeys ourselves. + // If e.g. ctrl+s *is* part of a mapping, then if the mapping = is + // rejected, the played back e.g. ctrl+s does not trigger the = e.g. + // Save shortcut. Likewise, we can no longer have e.g. ctrl+s = inside + // mappings or macros - the e.g. Save shortcut will not be tri= ggered! + // Altogether, a pretty disastrous change from Qt's old behavi= our - + // either they "fix" it (although it could be argued that bein= g able + // to trigger shortcuts from QKeyEvents was never the desired = behaviour) + // or we try to emulate Shortcut-handling ourselves :( + m_mappingKeys.clear(); + return false; + } + else + { + playBackRejectedKeys(); + return true; + } } m_doNotMapNextKeypress =3D false; return false; diff --git a/src/vimode/modes/normalvimode.cpp b/src/vimode/modes/normalvim= ode.cpp index b306376..e8dbb4f 100644 --- a/src/vimode/modes/normalvimode.cpp +++ b/src/vimode/modes/normalvimode.cpp @@ -426,11 +426,16 @@ bool NormalViMode::handleKeypress(const QKeyEvent *e) } } else if (m_matchingCommands.size() =3D=3D 0 && m_matchingMotions.siz= e() =3D=3D 0) { resetParser(); - return false; + // A bit ugly: we haven't made use of the key event, + // but don't want "typeable" keypresses (e.g. a, b, 3, etc) to be = marked + // as unused as they will then be added to the document, but we do= n't + // want to swallow all keys in case this was a shortcut. + // So say we made use of it if and only if it was *not* a shortcut. + return e->type() !=3D QEvent::ShortcutOverride; } = m_matchingMotions.clear(); - return false; + return true; // TODO - need to check this - it's currently required fo= r making tests pass, but seems odd. } = /**