From 3129662f3185ebbe43e432493d27ae02ad2023d5 Mon Sep 17 00:00:00 2001 From: Levi Neely Date: Wed, 7 Oct 2026 17:19:57 +0200 Subject: [PATCH] Chord: add Acme left+right = Paste MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The paste chord (Acme 1-3) now works: with the left button held, a right tap pastes over the selection via edit_paste (standard KDE clipboard). This is conflict-free because the radial gesture is already suppressed while the left button is held — right alone opens the radial, right while left-held pastes. The paired right release is swallowed so it doesn't leak a context menu or other action. All 5 ctest suites green. --- docs/PLAN.md | 26 ++++++++++++-------------- src/plugin/ollieplugin.cpp | 28 +++++++++++++++++++++------- src/plugin/ollieplugin.h | 1 + 3 files changed, 34 insertions(+), 21 deletions(-) diff --git a/docs/PLAN.md b/docs/PLAN.md index d8c3871..e243c86 100644 --- a/docs/PLAN.md +++ b/docs/PLAN.md @@ -128,20 +128,18 @@ Therefore the palette is replaced, not extended, and the replacement is built on - Not yet done: free-form stroke gestures (only button-press gesture so far); user-file config loading (built-in default only); frecency. -### Mouse chording (Acme-style) — DONE (first increment) -- Left+middle chord = **Cut**: with the left button held while selecting - (button 1 anchor), a tap of the middle button (button 2) cuts the current - selection via `edit_cut`, which uses the **standard KDE clipboard** - (Klipper-backed), not the X11 primary selection. -- **Middle-click primary paste is suppressed** inside the editor. Kate pastes - the primary selection on the middle event; the filter consumes the middle - button on **both press and release** (swallowing only the press let the paste - leak through on release). So middle-click no longer pastes in the editor — - paste is the normal `Ctrl+V` / palette paste from the KDE clipboard. -- Right-button chords are intentionally **not** ported — the right button is the - radial gesture. (Revisit later.) -- The radial gesture is suppressed while the left button is held, so an RMB tap - mid-selection (or during a left+middle chord) does not pop the radial. +### Mouse chording (Acme-style) — DONE +- Left-button chords (button 1 held while selecting is the anchor): + **left + middle = Cut**, **left + right = Paste**. Both use `edit_cut` / + `edit_paste` → the **standard KDE clipboard** (Klipper-backed), not the X11 + primary selection. +- The right-button paste chord is free of conflict because the radial gesture is + already suppressed while the left button is held: right alone = radial, right + while left-held = paste. +- **Middle-click primary paste is suppressed** inside the editor — the middle + button is consumed on both press and release (the paste fires on release, so + swallowing only the press leaked it). Paste in the editor is the KDE-clipboard + paste (`Ctrl+V`, palette, or the left+right chord). ### M5 — Command vocabulary + project model + switchers — NEXT - `:`-verb pack via `KTextEditor::Command`: `sort`, `align`, `json`, `b64`, diff --git a/src/plugin/ollieplugin.cpp b/src/plugin/ollieplugin.cpp index 070f849..45637df 100644 --- a/src/plugin/ollieplugin.cpp +++ b/src/plugin/ollieplugin.cpp @@ -347,13 +347,14 @@ bool OllieView::eventFilter(QObject *obj, QEvent *event) } } - // Acme-style left+middle chord = Cut, and suppression of X11 primary- - // selection paste. Middle-button events inside the view are consumed on - // BOTH press and release (Kate pastes the primary selection on the middle - // event; swallowing only the press lets the paste leak through on release). - // When the left button is held, the middle tap cuts the current selection - // via edit_cut, which uses the standard KDE clipboard (Klipper-backed). - // Right-button chords are intentionally not ported (RMB is the radial). + // Acme-style left-button chords, plus suppression of X11 primary-selection + // paste. Button 1 (left) held while selecting is the anchor: + // left + middle tap = Cut, left + right tap = Paste. + // Both use edit_cut / edit_paste, which use the standard KDE clipboard + // (Klipper-backed). The right-button chord is free because the radial + // gesture is already suppressed while the left button is held. Middle-button + // events in the view are consumed on BOTH press and release to kill the + // middle-click primary paste (it fires on release, not just press). if (event->type() == QEvent::MouseButtonRelease) { auto *mev = static_cast(event); if (mev->button() == Qt::LeftButton) { @@ -363,12 +364,25 @@ bool OllieView::eventFilter(QObject *obj, QEvent *event) m_swallowMiddleRelease = false; return true; // suppress the primary paste that fires on release } + if (mev->button() == Qt::RightButton && m_swallowRightRelease) { + m_swallowRightRelease = false; + return true; // paired release of a paste chord + } } else if (event->type() == QEvent::MouseButtonPress) { auto *mev = static_cast(event); KTextEditor::View *v = m_mainWindow->activeView(); auto *w = qobject_cast(obj); const bool inView = v && w && v->isAncestorOf(w); + // left + right tap = Paste over the selection. + if (mev->button() == Qt::RightButton && m_leftDownInView && inView) { + if (QAction *paste = findActionByName(QStringLiteral("edit_paste"))) { + paste->trigger(); + } + m_swallowRightRelease = true; + return true; // consume: chord, not a radial / context menu + } + if (mev->button() == Qt::MiddleButton && inView) { if (m_leftDownInView) { if (QAction *cut = findActionByName(QStringLiteral("edit_cut"))) { diff --git a/src/plugin/ollieplugin.h b/src/plugin/ollieplugin.h index 9d0db1d..045026e 100644 --- a/src/plugin/ollieplugin.h +++ b/src/plugin/ollieplugin.h @@ -91,6 +91,7 @@ private: bool m_gestureActive = false; // an RMB drag gesture is in progress bool m_leftDownInView = false; // LMB held inside the view (Acme chord anchor) bool m_swallowMiddleRelease = false; // swallow the release paired with a consumed middle press + bool m_swallowRightRelease = false; // swallow the release paired with a left+right paste chord QHash> m_actionsById; };