Launchers: handle Alt+X/P/G in eventFilter, not QAction shortcuts
Alt+P (and the other launcher keys) collided with Kate's own Alt+<letter> bindings; registered as ApplicationShortcut QActions this makes Qt report an ambiguous shortcut and fire neither. Go nuclear: drop the QAction shortcuts entirely and intercept Alt+X/P/G in eventFilter (accept ShortcutOverride, act on KeyPress), scoped to our window — the same ambiguity-proof technique used for the Acme keys. QActions kept for objectName/label; onRadialActivated now resolves all three launcher ids directly so the radial escape-hatch still reaches them.
This commit is contained in:
parent
07af27e82c
commit
0e1c6943ac
16
docs/PLAN.md
16
docs/PLAN.md
|
|
@ -78,9 +78,17 @@ Therefore the palette is replaced, not extended, and the replacement is built on
|
|||
### M3 — KTextEditor plugin + M-x + action registry — DONE
|
||||
- `src/plugin/ollieplugin.{h,cpp}` + `ollieplugin.json`: a loadable
|
||||
`KTextEditor::Plugin` (`olliepalette.so`) built with `kcoreaddons_add_plugin`.
|
||||
- `OllieView` (one per MainWindow) registers the **M-x** action, `Alt+X`, a
|
||||
single modifier with the keys adjacent on a compact layout; `ApplicationShortcut`
|
||||
context so it is always live.
|
||||
- `OllieView` (one per MainWindow) provides the **M-x** launcher on `Alt+X`.
|
||||
LAUNCHER KEYS ARE NOT QAction SHORTCUTS: `Alt+X` (M-x), `Alt+P` (Go to File)
|
||||
and `Alt+G` (Go to Symbol) are intercepted in `eventFilter()` (accept the
|
||||
`ShortcutOverride`, act on the `KeyPress`), scoped to this plugin's window.
|
||||
Registering them as `ApplicationShortcut` QActions clashed with Kate's own
|
||||
`Alt+<letter>` bindings and Qt refused to fire either ("ambiguous shortcut").
|
||||
The event-filter route cannot be ambiguous because nothing is registered —
|
||||
the same technique already used for the Acme line-editing keys. The QActions
|
||||
still exist (objectName/label) for command-palette harvest and as radial
|
||||
escape-hatch slices (`ollie_command_palette` / `ollie_goto_file` /
|
||||
`ollie_goto_symbol`, handled directly in `onRadialActivated`).
|
||||
- On trigger it aggregates every enabled, visible `QAction` from the window's
|
||||
`guiFactory()->clients()` action collections, keyed by stable `objectName`,
|
||||
and feeds them to `PaletteWidget`. The action's current shortcut is shown in
|
||||
|
|
@ -237,7 +245,7 @@ Therefore the palette is replaced, not extended, and the replacement is built on
|
|||
whole path (orderless: "palette model" matches `src/palette/palettemodel.cpp`)
|
||||
and highlights matched characters along it; a base-name hit still ranks first
|
||||
because `/` is a word boundary and the length penalty favours shorter paths.
|
||||
The root is `currentProjectRoot()` (explicit folder when set, else derived).
|
||||
The root is `currentProjectRoot()` (Kate's loaded project, else derived).
|
||||
Activation opens the file via `MainWindow::openUrl` + `activateView`. Also
|
||||
reachable from the M-x palette (`ollie:switch:file`) and, by objectName
|
||||
`ollie_goto_file`, from a radial. (2 path-ranking tests in
|
||||
|
|
|
|||
|
|
@ -221,43 +221,30 @@ OllieView::OllieView(OlliePlugin *plugin, KTextEditor::MainWindow *mainWindow)
|
|||
QStringLiteral("olliepalette/frecency.json")));
|
||||
m_frecency.load();
|
||||
|
||||
// The one keyboard door: Alt+X (M-x). A single modifier, no chord, and the
|
||||
// keys sit together on a compact layout.
|
||||
auto *openAction = new QAction(i18n("Command Palette (M-x)"), this);
|
||||
// The three launcher "doors". Their shortcuts are NOT registered as
|
||||
// QAction/ApplicationShortcut: Kate (and other plugins) already claim many
|
||||
// Alt+<letter> combos, and a registered duplicate makes Qt refuse to fire
|
||||
// either, reporting an "ambiguous shortcut". We therefore register NO
|
||||
// shortcut here and instead intercept the key in eventFilter() (the same
|
||||
// technique used for the Acme line-editing keys), which cannot be
|
||||
// ambiguous because nothing is registered. The QActions are kept only for
|
||||
// their objectName/label (command-palette harvest, radial escape-hatch).
|
||||
auto *openAction = new QAction(i18n("Command Palette (M-x) [Alt+X]"), this);
|
||||
openAction->setObjectName(QStringLiteral("ollie_command_palette"));
|
||||
openAction->setShortcut(QKeySequence(Qt::ALT | Qt::Key_X));
|
||||
openAction->setShortcutContext(Qt::ApplicationShortcut);
|
||||
connect(openAction, &QAction::triggered, this, &OllieView::showCommandPalette);
|
||||
|
||||
// Attach the shortcut to the window so it is always live.
|
||||
auto *gotoFileAction = new QAction(i18n("Go to File (project) [Alt+P]"), this);
|
||||
gotoFileAction->setObjectName(QStringLiteral("ollie_goto_file"));
|
||||
connect(gotoFileAction, &QAction::triggered, this, &OllieView::showFileSwitcher);
|
||||
|
||||
auto *gotoSymbolAction = new QAction(i18n("Go to Symbol (project) [Alt+G]"), this);
|
||||
gotoSymbolAction->setObjectName(QStringLiteral("ollie_goto_symbol"));
|
||||
connect(gotoSymbolAction, &QAction::triggered, this, &OllieView::showSymbolSwitcher);
|
||||
|
||||
// Keep them discoverable as window actions (no shortcut attached).
|
||||
if (QWidget *w = m_mainWindow->window()) {
|
||||
w->addAction(openAction);
|
||||
}
|
||||
|
||||
// Project "Go to File" switcher: Alt+P (single modifier; the Sublime/VSCode
|
||||
// Ctrl+P idiom adapted to one-handed use). Lists the project's files —
|
||||
// "open folder == open project" — scoped to the active document's git/root
|
||||
// directory.
|
||||
auto *gotoFileAction = new QAction(i18n("Go to File (project)"), this);
|
||||
gotoFileAction->setObjectName(QStringLiteral("ollie_goto_file"));
|
||||
gotoFileAction->setShortcut(QKeySequence(Qt::ALT | Qt::Key_P));
|
||||
gotoFileAction->setShortcutContext(Qt::ApplicationShortcut);
|
||||
connect(gotoFileAction, &QAction::triggered, this, &OllieView::showFileSwitcher);
|
||||
if (QWidget *w = m_mainWindow->window()) {
|
||||
w->addAction(gotoFileAction);
|
||||
}
|
||||
|
||||
// Project "Go to Symbol" switcher: Alt+G (single modifier; "Go to symbol").
|
||||
// Alt+R is reserved for the radial key trigger, so the symbol switcher uses
|
||||
// Alt+G instead. Lists ctags symbols across the project's files, scoped to
|
||||
// the same root as the file switcher. Activation opens the file and jumps to
|
||||
// the symbol's line.
|
||||
auto *gotoSymbolAction = new QAction(i18n("Go to Symbol (project)"), this);
|
||||
gotoSymbolAction->setObjectName(QStringLiteral("ollie_goto_symbol"));
|
||||
gotoSymbolAction->setShortcut(QKeySequence(Qt::ALT | Qt::Key_G));
|
||||
gotoSymbolAction->setShortcutContext(Qt::ApplicationShortcut);
|
||||
connect(gotoSymbolAction, &QAction::triggered, this, &OllieView::showSymbolSwitcher);
|
||||
if (QWidget *w = m_mainWindow->window()) {
|
||||
w->addAction(gotoSymbolAction);
|
||||
}
|
||||
|
||||
|
|
@ -879,6 +866,33 @@ bool OllieView::eventFilter(QObject *obj, QEvent *event)
|
|||
return true; // consume the keypress
|
||||
}
|
||||
}
|
||||
|
||||
// Launcher doors: Alt+X (M-x), Alt+P (Go to File), Alt+G (Go to Symbol).
|
||||
// Handled here rather than via QAction shortcuts so they can never clash
|
||||
// with Kate's own Alt+<letter> bindings (which produced "ambiguous
|
||||
// shortcut" and swallowed the key). Scope to this plugin's window so we
|
||||
// do not hijack the combo in other Kate main windows.
|
||||
const bool altOnly =
|
||||
(ke->modifiers() & Qt::KeyboardModifierMask) == Qt::AltModifier;
|
||||
if (altOnly
|
||||
&& (ke->key() == Qt::Key_X || ke->key() == Qt::Key_P || ke->key() == Qt::Key_G)) {
|
||||
auto *w = qobject_cast<QWidget *>(obj);
|
||||
QWidget *ourWindow = m_mainWindow->window();
|
||||
const bool ours = w && ourWindow && ourWindow->isAncestorOf(w);
|
||||
if (ours) {
|
||||
if (event->type() == QEvent::ShortcutOverride) {
|
||||
event->accept(); // suppress any registered (ambiguous) shortcut
|
||||
return true;
|
||||
}
|
||||
switch (ke->key()) {
|
||||
case Qt::Key_X: showCommandPalette(); break;
|
||||
case Qt::Key_P: showFileSwitcher(); break;
|
||||
case Qt::Key_G: showSymbolSwitcher(); break;
|
||||
default: break;
|
||||
}
|
||||
return true; // consume the keypress
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Bracket-pair selection: double-clicking next to a bracket selects the
|
||||
|
|
@ -1045,11 +1059,19 @@ void OllieView::showRadialAt(int defIndex, const QPoint &globalPos)
|
|||
|
||||
void OllieView::onRadialActivated(const QString &actionId)
|
||||
{
|
||||
// The palette is reachable from inside a radial as a special slice.
|
||||
// The launcher doors are reachable from inside a radial as special slices.
|
||||
if (actionId == QLatin1String("ollie_command_palette")) {
|
||||
showCommandPalette();
|
||||
return;
|
||||
}
|
||||
if (actionId == QLatin1String("ollie_goto_file")) {
|
||||
showFileSwitcher();
|
||||
return;
|
||||
}
|
||||
if (actionId == QLatin1String("ollie_goto_symbol")) {
|
||||
showSymbolSwitcher();
|
||||
return;
|
||||
}
|
||||
if (QAction *a = findActionByName(actionId)) {
|
||||
a->trigger();
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue