kate: fix git widget integration via ChildAdded event filter

The previous approach using QMetaObject::invokeMethod on gitWidget()
failed because that method isn't Q_INVOKABLE. The new approach:

- Find git tool view by object name or 'git' in name
- Install event filter to catch QEvent::ChildAdded
- When a QMenu child is added, connect to aboutToShow
- Add Ollie actions to the menu before it's shown

Also add retry timer for setupProjectGitWidget since the plugin
view may not exist at construction time.
This commit is contained in:
Levi Neely 2026-10-08 10:34:06 +02:00
parent a546b8721a
commit c3782dd3b7
2 changed files with 54 additions and 40 deletions

View File

@ -9,6 +9,7 @@
#include <QApplication> #include <QApplication>
#include <QCheckBox> #include <QCheckBox>
#include <QChildEvent>
#include <QContextMenuEvent> #include <QContextMenuEvent>
#include <QDir> #include <QDir>
#include <QFileInfo> #include <QFileInfo>
@ -465,22 +466,36 @@ void OllieKateView::setupGitPlusPlusDiffAction()
void OllieKateView::setupProjectGitWidget() void OllieKateView::setupProjectGitWidget()
{ {
QObject *projectView = m_mainWindow->pluginView(QStringLiteral("kateprojectplugin")); // Find the git tool view by name — it's created with ID "kateprojectgit"
if (!projectView) return; QWidget *gitToolView = m_mainWindow->window()->findChild<QWidget *>(QStringLiteral("kateprojectgit"));
if (!gitToolView) {
// Try alternate lookup via mainWindow
for (auto *child : m_mainWindow->window()->findChildren<QWidget *>()) {
if (child->objectName().contains(QStringLiteral("git"), Qt::CaseInsensitive)) {
gitToolView = child;
break;
}
}
}
if (!gitToolView) {
// Schedule retry — plugin view might not be created yet
QTimer::singleShot(1000, this, &OllieKateView::setupProjectGitWidget);
return;
}
// Get the GitWidget via the gitWidget() method // Find the tree view inside the git tool view and store for later use
QWidget *gitWidget = nullptr; auto *treeView = gitToolView->findChild<QTreeView *>();
QMetaObject::invokeMethod(projectView, "gitWidget", if (treeView) {
Qt::DirectConnection,
Q_RETURN_ARG(QWidget *, gitWidget));
if (!gitWidget) return;
// Find the tree view inside the git widget for context menu interception
auto *treeView = gitWidget->findChild<QTreeView *>();
if (treeView && treeView->viewport()) {
treeView->viewport()->installEventFilter(this);
m_gitTreeView = treeView; m_gitTreeView = treeView;
} }
// Install event filter on the git tool view to intercept menus
gitToolView->installEventFilter(this);
}
void OllieKateView::onGitTreeContextMenu(const QPoint &)
{
// Unused - left for API compatibility
} }
void OllieKateView::onWidgetAdded(QWidget *widget) void OllieKateView::onWidgetAdded(QWidget *widget)
@ -546,24 +561,6 @@ void OllieKateView::reviewDiffFromWidget(QWidget *diffWidget)
m_9p->write(agentPath() + "/prompt", prompt.toUtf8()); m_9p->write(agentPath() + "/prompt", prompt.toUtf8());
} }
void OllieKateView::addOllieActionsToGitContextMenu(const QPoint &)
{
// Find the active popup menu (Kate's git widget context menu)
QMenu *menu = qobject_cast<QMenu *>(QApplication::activePopupWidget());
if (!menu) return;
// Add separator and Ollie actions
menu->addSeparator();
auto *reviewAction = menu->addAction(QIcon::fromTheme(QStringLiteral("document-preview")),
QStringLiteral("Ollie: Review Staged Changes"));
connect(reviewAction, &QAction::triggered, this, &OllieKateView::reviewStagedChanges);
auto *askAction = menu->addAction(QIcon::fromTheme(QStringLiteral("help-about")),
QStringLiteral("Ollie: Ask About Selection"));
connect(askAction, &QAction::triggered, this, &OllieKateView::askAboutGitSelection);
}
void OllieKateView::reviewStagedChanges() void OllieKateView::reviewStagedChanges()
{ {
QString baseDir = projectBaseDir(); QString baseDir = projectBaseDir();
@ -782,14 +779,31 @@ void OllieKateView::setupGhostProvider()
bool OllieKateView::eventFilter(QObject *obj, QEvent *event) bool OllieKateView::eventFilter(QObject *obj, QEvent *event)
{ {
// Handle context menu for git widget's tree view // Watch for menus being created in the git tool view area
if (event->type() == QEvent::ContextMenu && m_gitTreeView && obj == m_gitTreeView->viewport()) { if (event->type() == QEvent::ChildAdded) {
auto *contextEvent = static_cast<QContextMenuEvent *>(event); auto *childEvent = static_cast<QChildEvent *>(event);
// Don't block the event — let the original handler run first if (auto *menu = qobject_cast<QMenu *>(childEvent->child())) {
// We'll add our items via a delayed single-shot // Connect to aboutToShow to add our items
QTimer::singleShot(0, this, [this, pos = contextEvent->globalPos()]() { connect(menu, &QMenu::aboutToShow, this, [this, menu]() {
addOllieActionsToGitContextMenu(pos); // Only add if we haven't already
}); bool hasOllieAction = false;
for (auto *action : menu->actions()) {
if (action->text().startsWith(QStringLiteral("Ollie:"))) {
hasOllieAction = true;
break;
}
}
if (!hasOllieAction && menu->actions().count() > 0) {
menu->addSeparator();
menu->addAction(QIcon::fromTheme(QStringLiteral("document-preview")),
QStringLiteral("Ollie: Review Staged Changes"),
this, &OllieKateView::reviewStagedChanges);
menu->addAction(QIcon::fromTheme(QStringLiteral("help-about")),
QStringLiteral("Ollie: Ask About Selection"),
this, &OllieKateView::askAboutGitSelection);
}
}, Qt::UniqueConnection);
}
} }
if (event->type() == QEvent::KeyPress && m_ghost) { if (event->type() == QEvent::KeyPress && m_ghost) {

View File

@ -63,6 +63,7 @@ private slots:
void onProjectChanged(); void onProjectChanged();
void heartbeat(); void heartbeat();
void onWidgetAdded(QWidget *widget); void onWidgetAdded(QWidget *widget);
void onGitTreeContextMenu(const QPoint &pos);
private: private:
// Actions // Actions
@ -92,7 +93,6 @@ private:
void setupGitPlusPlusDiffAction(); void setupGitPlusPlusDiffAction();
void setupProjectGitWidget(); void setupProjectGitWidget();
void onDiffWidgetContextMenu(QWidget *diffWidget, const QPoint &pos); void onDiffWidgetContextMenu(QWidget *diffWidget, const QPoint &pos);
void addOllieActionsToGitContextMenu(const QPoint &globalPos);
// Ghost text // Ghost text
void setupGhostProvider(); void setupGhostProvider();