Radial: add window/split radial on the middle mouse button
Second built-in radial 'window' bound to MiddleButton: Split Vertical/ Horizontal, Close Split, Close Others, Toggle Orientation, and a Focus sub-branch (go_left/right/up/down/next/prev_split_view). Leaf ids are Kate view-manager action objectNames, resolved via findActionByName. Route a plain middle press (no left held) to the radial dispatch instead of unconditionally consuming it: the left+middle Cut chord still fires (left held), and the X11 middle-click primary paste is still suppressed on release in both paths. Clear m_swallowMiddleRelease when a gesture consumes the release. Example config + builtinHasWindowRadialOnMiddleButton test updated.
This commit is contained in:
parent
5597dae15d
commit
afedc88ac9
|
|
@ -135,6 +135,14 @@ Therefore the palette is replaced, not extended, and the replacement is built on
|
|||
and by mouse buttons/gestures; the config carries both a key `trigger` and a
|
||||
mouse `button` per radial.
|
||||
- Not yet done: free-form stroke gestures (only button-press gesture so far).
|
||||
- Built-in radials: an **edit** radial (Alt+R / right button) of in-place
|
||||
actions, and a **window** radial (**middle button**) for split management
|
||||
(`view_split_vert|horiz`, `view_close_current_space|others`,
|
||||
`view_split_toggle`, and a "Focus" sub-branch of `go_*_split_view`). A plain
|
||||
middle press (no left button held) routes to this radial; the Acme chords are
|
||||
all **left+something**, so a middle press that is part of a chord
|
||||
(left held) still cuts and never opens the radial. The X11 middle-click
|
||||
primary paste is suppressed on release either way.
|
||||
- User-file radial config — DONE. `RadialConfig::load()` reads
|
||||
`$XDG_CONFIG_HOME/olliepalette/radials.json` when present and non-empty,
|
||||
otherwise returns `builtinDefault()`; a present-but-broken file also falls
|
||||
|
|
|
|||
|
|
@ -25,6 +25,28 @@
|
|||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "window",
|
||||
"button": "MiddleButton",
|
||||
"slices": [
|
||||
{ "label": "Split Vertical", "action": "view_split_vert" },
|
||||
{ "label": "Split Horizontal", "action": "view_split_horiz" },
|
||||
{ "label": "Close Split", "action": "view_close_current_space" },
|
||||
{ "label": "Close Others", "action": "view_close_others" },
|
||||
{ "label": "Toggle Orientation", "action": "view_split_toggle" },
|
||||
{
|
||||
"label": "Focus",
|
||||
"slices": [
|
||||
{ "label": "Left", "action": "go_left_split_view" },
|
||||
{ "label": "Right", "action": "go_right_split_view" },
|
||||
{ "label": "Up", "action": "go_upward_split_view" },
|
||||
{ "label": "Down", "action": "go_downward_split_view" },
|
||||
{ "label": "Next", "action": "go_next_split_view" },
|
||||
{ "label": "Previous", "action": "go_prev_split_view" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -982,6 +982,7 @@ bool OllieView::eventFilter(QObject *obj, QEvent *event)
|
|||
return true;
|
||||
case QEvent::MouseButtonRelease:
|
||||
m_gestureActive = false;
|
||||
m_swallowMiddleRelease = false; // the gesture consumed this release
|
||||
if (m_radial) {
|
||||
m_radial->driveReleaseGlobal(
|
||||
static_cast<QMouseEvent *>(event)->globalPosition().toPoint());
|
||||
|
|
@ -1030,14 +1031,23 @@ bool OllieView::eventFilter(QObject *obj, QEvent *event)
|
|||
|
||||
if (mev->button() == Qt::MiddleButton && inView) {
|
||||
if (m_leftDownInView) {
|
||||
// left + middle = Cut the selection (Acme chord). Consume both
|
||||
// press and release; never a radial.
|
||||
if (QAction *cut = findActionByName(QStringLiteral("edit_cut"))) {
|
||||
cut->trigger();
|
||||
}
|
||||
m_swallowMiddleRelease = true;
|
||||
return true;
|
||||
}
|
||||
// Consume press (and its release) to suppress middle-click primary
|
||||
// paste entirely inside the editor.
|
||||
// A plain middle press (no chord): always suppress the X11 primary
|
||||
// paste that fires on release. If the middle button is bound to a
|
||||
// radial, fall through to the radial dispatch below; otherwise
|
||||
// consume the press here too.
|
||||
m_swallowMiddleRelease = true;
|
||||
return true;
|
||||
if (!m_buttonToRadial.contains(static_cast<int>(Qt::MiddleButton))) {
|
||||
return true;
|
||||
}
|
||||
// else: do not return — let the radial-trigger dispatch handle it.
|
||||
}
|
||||
if (mev->button() == Qt::LeftButton && inView) {
|
||||
m_leftDownInView = true; // anchor set; selection proceeds normally
|
||||
|
|
|
|||
|
|
@ -171,7 +171,37 @@ QList<RadialDef> RadialConfig::builtinDefault()
|
|||
more,
|
||||
};
|
||||
|
||||
return {edit};
|
||||
// A second radial for window/split management, triggered by the middle
|
||||
// mouse button. Split ops are inherently about window layout, not the caret,
|
||||
// so a dedicated radial keeps them off the keyboard and out of the edit
|
||||
// radial. MiddleButton is safe: every Acme chord is left+something, and a
|
||||
// plain middle press (no left held) is routed here, not to a chord. Leaf ids
|
||||
// are Kate's view-manager action objectNames.
|
||||
RadialDef window;
|
||||
window.name = QStringLiteral("window");
|
||||
window.mouseButton = QStringLiteral("MiddleButton");
|
||||
|
||||
RadialNode focus;
|
||||
focus.label = QStringLiteral("Focus");
|
||||
focus.children = {
|
||||
leaf(QStringLiteral("Left"), QStringLiteral("go_left_split_view")),
|
||||
leaf(QStringLiteral("Right"), QStringLiteral("go_right_split_view")),
|
||||
leaf(QStringLiteral("Up"), QStringLiteral("go_upward_split_view")),
|
||||
leaf(QStringLiteral("Down"), QStringLiteral("go_downward_split_view")),
|
||||
leaf(QStringLiteral("Next"), QStringLiteral("go_next_split_view")),
|
||||
leaf(QStringLiteral("Previous"), QStringLiteral("go_prev_split_view")),
|
||||
};
|
||||
|
||||
window.root.children = {
|
||||
leaf(QStringLiteral("Split Vertical"), QStringLiteral("view_split_vert")),
|
||||
leaf(QStringLiteral("Split Horizontal"), QStringLiteral("view_split_horiz")),
|
||||
leaf(QStringLiteral("Close Split"), QStringLiteral("view_close_current_space")),
|
||||
leaf(QStringLiteral("Close Others"), QStringLiteral("view_close_others")),
|
||||
leaf(QStringLiteral("Toggle Orientation"), QStringLiteral("view_split_toggle")),
|
||||
focus,
|
||||
};
|
||||
|
||||
return {edit, window};
|
||||
}
|
||||
|
||||
} // namespace katecustom
|
||||
|
|
|
|||
|
|
@ -129,6 +129,34 @@ private Q_SLOTS:
|
|||
QVERIFY(hasBranch);
|
||||
}
|
||||
|
||||
void builtinHasWindowRadialOnMiddleButton()
|
||||
{
|
||||
const QList<RadialDef> defs = RadialConfig::builtinDefault();
|
||||
QCOMPARE(defs.size(), 2);
|
||||
// Find the window radial (second def), triggered by the middle button.
|
||||
const RadialDef *window = nullptr;
|
||||
for (const RadialDef &d : defs) {
|
||||
if (d.name == QStringLiteral("window")) {
|
||||
window = &d;
|
||||
}
|
||||
}
|
||||
QVERIFY(window);
|
||||
QCOMPARE(window->mouseButton, QStringLiteral("MiddleButton"));
|
||||
|
||||
// It must expose the core split ops by their Kate action objectNames.
|
||||
QStringList ids;
|
||||
for (const RadialNode &n : window->root.children) {
|
||||
ids << n.actionId;
|
||||
for (const RadialNode &c : n.children) {
|
||||
ids << c.actionId;
|
||||
}
|
||||
}
|
||||
QVERIFY(ids.contains(QStringLiteral("view_split_vert")));
|
||||
QVERIFY(ids.contains(QStringLiteral("view_split_horiz")));
|
||||
QVERIFY(ids.contains(QStringLiteral("view_close_current_space")));
|
||||
QVERIFY(ids.contains(QStringLiteral("go_left_split_view")));
|
||||
}
|
||||
|
||||
void builtinDefaultHasLauncherSlices()
|
||||
{
|
||||
// The "More" branch exposes the Go-to-File / Go-to-Symbol switchers and
|
||||
|
|
|
|||
Loading…
Reference in New Issue