Extend sloppy focus to all panes (tool views, terminals, etc.)
Generalize focus tracking from KTextEditor::View-only to any focusable widget pane. paneForWidget() walks up the hierarchy: - KTextEditor::View takes priority (editor splits) - Falls back to any widget with StrongFocus/WheelFocus policy (terminals, file browser, build output, plugin panels, etc.) This means hovering over any Kate pane will give it keyboard focus, not just editor splits.
This commit is contained in:
parent
a641cbfc00
commit
be18599c9b
|
|
@ -116,7 +116,7 @@ SloppyFocusView::SloppyFocusView(SloppyFocusPlugin *plugin, KTextEditor::MainWin
|
|||
, m_mainWindow(mainWindow)
|
||||
{
|
||||
m_focusTimer.setSingleShot(true);
|
||||
connect(&m_focusTimer, &QTimer::timeout, this, &SloppyFocusView::activatePendingView);
|
||||
connect(&m_focusTimer, &QTimer::timeout, this, &SloppyFocusView::activatePendingPane);
|
||||
connect(m_plugin, &SloppyFocusPlugin::configChanged, this, &SloppyFocusView::onConfigChanged);
|
||||
|
||||
m_lastFocusChange.start();
|
||||
|
|
@ -153,16 +153,16 @@ bool SloppyFocusView::eventFilter(QObject *obj, QEvent *event)
|
|||
return false;
|
||||
}
|
||||
|
||||
// Find the KTextEditor::View ancestor of the entered widget
|
||||
KTextEditor::View *view = viewForWidget(widget);
|
||||
if (!view) {
|
||||
// Find the focusable pane (editor view, tool view, terminal, etc.)
|
||||
QWidget *pane = paneForWidget(widget);
|
||||
if (!pane) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't re-trigger for the view that already has focus
|
||||
if (view == m_currentView) {
|
||||
// Don't re-trigger for the pane that already has focus
|
||||
if (pane == m_currentPane) {
|
||||
m_focusTimer.stop();
|
||||
m_pendingView = nullptr;
|
||||
m_pendingPane = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -178,59 +178,70 @@ bool SloppyFocusView::eventFilter(QObject *obj, QEvent *event)
|
|||
}
|
||||
|
||||
// Schedule focus change after delay
|
||||
m_pendingView = view;
|
||||
m_pendingPane = pane;
|
||||
m_focusTimer.start(m_plugin->focusDelay());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void SloppyFocusView::activatePendingView()
|
||||
void SloppyFocusView::activatePendingPane()
|
||||
{
|
||||
if (!m_pendingView) {
|
||||
if (!m_pendingPane) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Final check: is the mouse still over this view?
|
||||
// (The view might have been destroyed or mouse moved away)
|
||||
KTextEditor::View *view = m_pendingView;
|
||||
m_pendingView = nullptr;
|
||||
QWidget *pane = m_pendingPane;
|
||||
m_pendingPane = nullptr;
|
||||
|
||||
if (!view->isVisible()) {
|
||||
if (!pane->isVisible()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Verify mouse is still within the view's geometry
|
||||
// Verify mouse is still within the pane's geometry
|
||||
QPoint globalPos = QCursor::pos();
|
||||
QRect viewRect = view->rect();
|
||||
QPoint viewTopLeft = view->mapToGlobal(viewRect.topLeft());
|
||||
QRect globalRect(viewTopLeft, viewRect.size());
|
||||
QPoint paneTopLeft = pane->mapToGlobal(QPoint(0, 0));
|
||||
QRect globalRect(paneTopLeft, pane->size());
|
||||
if (!globalRect.contains(globalPos)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Perform the focus switch — just move keyboard focus to the view
|
||||
// in its current split pane. Do NOT use activateView(document) as that
|
||||
// can open the document in a different view space.
|
||||
view->setFocus();
|
||||
m_currentView = view;
|
||||
// Move keyboard focus to the pane
|
||||
pane->setFocus();
|
||||
m_currentPane = pane;
|
||||
m_lastFocusChange.restart();
|
||||
|
||||
if (m_plugin->raiseOnFocus()) {
|
||||
view->raise();
|
||||
pane->raise();
|
||||
}
|
||||
}
|
||||
|
||||
KTextEditor::View *SloppyFocusView::viewForWidget(QWidget *widget) const
|
||||
QWidget *SloppyFocusView::paneForWidget(QWidget *widget) const
|
||||
{
|
||||
// Walk up the widget hierarchy looking for a KTextEditor::View
|
||||
// Walk up the widget hierarchy to find the nearest focusable pane.
|
||||
// Priority: KTextEditor::View first, then any widget with a keyboard
|
||||
// focus policy (tool views, terminals, file browsers, etc.).
|
||||
QWidget *w = widget;
|
||||
while (w) {
|
||||
if (auto *view = qobject_cast<KTextEditor::View *>(w)) {
|
||||
return view;
|
||||
QWidget *focusCandidate = nullptr;
|
||||
|
||||
while (w && w != m_mainWindow->window()) {
|
||||
// KTextEditor::View is always a valid pane
|
||||
if (qobject_cast<KTextEditor::View *>(w)) {
|
||||
return w;
|
||||
}
|
||||
|
||||
// Track the deepest widget that accepts keyboard focus as a
|
||||
// fallback. This catches tool views, terminals, tree views, etc.
|
||||
if (!focusCandidate) {
|
||||
auto policy = w->focusPolicy();
|
||||
if (policy == Qt::StrongFocus || policy == Qt::WheelFocus) {
|
||||
focusCandidate = w;
|
||||
}
|
||||
}
|
||||
|
||||
w = w->parentWidget();
|
||||
}
|
||||
return nullptr;
|
||||
|
||||
return focusCandidate;
|
||||
}
|
||||
|
||||
bool SloppyFocusView::shouldIgnoreFocus() const
|
||||
|
|
|
|||
|
|
@ -62,19 +62,19 @@ protected:
|
|||
bool eventFilter(QObject *obj, QEvent *event) override;
|
||||
|
||||
private Q_SLOTS:
|
||||
void activatePendingView();
|
||||
void activatePendingPane();
|
||||
void onConfigChanged();
|
||||
|
||||
private:
|
||||
KTextEditor::View *viewForWidget(QWidget *widget) const;
|
||||
QWidget *paneForWidget(QWidget *widget) const;
|
||||
bool shouldIgnoreFocus() const;
|
||||
|
||||
SloppyFocusPlugin *m_plugin;
|
||||
KTextEditor::MainWindow *m_mainWindow;
|
||||
QTimer m_focusTimer;
|
||||
QElapsedTimer m_lastFocusChange;
|
||||
QPointer<KTextEditor::View> m_pendingView;
|
||||
QPointer<KTextEditor::View> m_currentView;
|
||||
QPointer<QWidget> m_pendingPane;
|
||||
QPointer<QWidget> m_currentPane;
|
||||
};
|
||||
|
||||
class SloppyFocusConfigPage : public KTextEditor::ConfigPage
|
||||
|
|
|
|||
Loading…
Reference in New Issue