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 <QCheckBox>
#include <QChildEvent>
#include <QContextMenuEvent>
#include <QDir>
#include <QFileInfo>
@ -465,22 +466,36 @@ void OllieKateView::setupGitPlusPlusDiffAction()
void OllieKateView::setupProjectGitWidget()
{
QObject *projectView = m_mainWindow->pluginView(QStringLiteral("kateprojectplugin"));
if (!projectView) return;
// Find the git tool view by name — it's created with ID "kateprojectgit"
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
QWidget *gitWidget = nullptr;
QMetaObject::invokeMethod(projectView, "gitWidget",
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);
// Find the tree view inside the git tool view and store for later use
auto *treeView = gitToolView->findChild<QTreeView *>();
if (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)
@ -546,24 +561,6 @@ void OllieKateView::reviewDiffFromWidget(QWidget *diffWidget)
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()
{
QString baseDir = projectBaseDir();
@ -782,14 +779,31 @@ void OllieKateView::setupGhostProvider()
bool OllieKateView::eventFilter(QObject *obj, QEvent *event)
{
// Handle context menu for git widget's tree view
if (event->type() == QEvent::ContextMenu && m_gitTreeView && obj == m_gitTreeView->viewport()) {
auto *contextEvent = static_cast<QContextMenuEvent *>(event);
// Don't block the event — let the original handler run first
// We'll add our items via a delayed single-shot
QTimer::singleShot(0, this, [this, pos = contextEvent->globalPos()]() {
addOllieActionsToGitContextMenu(pos);
});
// Watch for menus being created in the git tool view area
if (event->type() == QEvent::ChildAdded) {
auto *childEvent = static_cast<QChildEvent *>(event);
if (auto *menu = qobject_cast<QMenu *>(childEvent->child())) {
// Connect to aboutToShow to add our items
connect(menu, &QMenu::aboutToShow, this, [this, menu]() {
// 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) {

View File

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