Add Acme-style left+middle mouse chord = Cut
Left-button chords only (right button stays the radial). With the left button held while selecting, a middle-button tap cuts the current selection and is consumed, so it does not also trigger a primary-selection paste; middle-click without the left held still pastes as usual. Implemented in the shared application event filter: track the left button held inside the active view, and on a middle press while held trigger edit_cut. The filter is now installed unconditionally (it serves both the radial gesture and the chord). All 5 ctest suites green.
This commit is contained in:
parent
dca2514023
commit
d117aebb7f
10
docs/PLAN.md
10
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 <shell>`, etc. These populate M-x and the radial.
|
||||
|
|
|
|||
|
|
@ -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<int>(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<QMouseEvent *>(event)->button() == Qt::LeftButton) {
|
||||
m_leftDownInView = false;
|
||||
}
|
||||
} else if (event->type() == QEvent::MouseButtonPress) {
|
||||
auto *mev = static_cast<QMouseEvent *>(event);
|
||||
KTextEditor::View *v = m_mainWindow->activeView();
|
||||
auto *w = qobject_cast<QWidget *>(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<int>(Qt::RightButton))) {
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ private:
|
|||
QList<RadialDef> m_radialDefs;
|
||||
QHash<int, int> 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<QString, QPointer<QAction>> m_actionsById;
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue