diff --git a/docs/PLAN.md b/docs/PLAN.md index d9840ae..98b986b 100644 --- a/docs/PLAN.md +++ b/docs/PLAN.md @@ -128,6 +128,16 @@ 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. Driven by the same application event filter — it tracks the left + button held inside the view and, on a middle press while held, triggers + `edit_cut` and consumes the event. Middle-click alone (no left held) still + does the usual primary-selection paste. +- Right-button chords are intentionally **not** ported — the right button is the + radial gesture. (Revisit later.) + ### M5 — Command vocabulary + project model + switchers — NEXT - `:`-verb pack via `KTextEditor::Command`: `sort`, `align`, `json`, `b64`, `uuid`, `case`, `pipe `, etc. These populate M-x and the radial. diff --git a/src/plugin/ollieplugin.cpp b/src/plugin/ollieplugin.cpp index fa92fd0..b2493d8 100644 --- a/src/plugin/ollieplugin.cpp +++ b/src/plugin/ollieplugin.cpp @@ -67,6 +67,11 @@ OllieView::OllieView(OlliePlugin *plugin, KTextEditor::MainWindow *mainWindow) } setupRadials(); + + // One application-wide filter serves both the mouse-button radial gesture + // and the Acme-style left+middle chord, catching presses no matter which + // internal child of the view actually receives them. + qApp->installEventFilter(this); } OllieView::~OllieView() @@ -284,7 +289,6 @@ void OllieView::setupRadials() m_radialDefs = RadialConfig::builtinDefault(); QWidget *w = m_mainWindow->window(); - bool wantMouseGestures = false; for (int i = 0; i < m_radialDefs.size(); ++i) { const RadialDef &def = m_radialDefs.at(i); @@ -312,16 +316,9 @@ void OllieView::setupRadials() } if (btn != Qt::NoButton) { m_buttonToRadial.insert(static_cast(btn), i); - wantMouseGestures = true; } } } - - // A single application-wide filter catches the gesture no matter which - // internal child of the view actually receives the press. - if (wantMouseGestures) { - qApp->installEventFilter(this); - } } bool OllieView::eventFilter(QObject *obj, QEvent *event) @@ -350,6 +347,31 @@ bool OllieView::eventFilter(QObject *obj, QEvent *event) } } + // Acme-style left+middle chord = Cut. Button 1 (left) held while selecting + // is the anchor; a tap of button 2 (middle) cuts the current selection. + // Right-button chords are intentionally not ported (RMB is the radial). + // Middle-click alone (no left held) still does the usual primary paste. + if (event->type() == QEvent::MouseButtonRelease) { + if (static_cast(event)->button() == Qt::LeftButton) { + m_leftDownInView = false; + } + } 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); + + if (mev->button() == Qt::MiddleButton && m_leftDownInView && inView) { + if (QAction *cut = findActionByName(QStringLiteral("edit_cut"))) { + cut->trigger(); + } + return true; // consume: this is the chord, not a primary paste + } + if (mev->button() == Qt::LeftButton && inView) { + m_leftDownInView = true; // anchor set; selection proceeds normally + } + } + // Suppress the context menu that would otherwise follow an RMB gesture. if (event->type() == QEvent::ContextMenu && m_buttonToRadial.contains(static_cast(Qt::RightButton))) { diff --git a/src/plugin/ollieplugin.h b/src/plugin/ollieplugin.h index 29c90fd..cf449a1 100644 --- a/src/plugin/ollieplugin.h +++ b/src/plugin/ollieplugin.h @@ -89,6 +89,7 @@ private: QList m_radialDefs; QHash m_buttonToRadial; // Qt::MouseButton (int) -> def index bool m_gestureActive = false; // an RMB drag gesture is in progress + bool m_leftDownInView = false; // LMB held inside the view (Acme chord anchor) QHash> m_actionsById; };