Skip to content

Commit

Permalink
Deliver QContextMenuEvent via ExtraData.contextMenu, not event filter
Browse files Browse the repository at this point in the history
Event filtering can be problematic. At least this was a more reasonable
use case for a filter than some others in Controls; still, we already
have a switch case in QQuickItem::event(), and plan to add a virtual
QQuickItem::contextMenuEvent() in Qt 7, so we might as well mock it up
now as QQuickItemPrivate::contextMenuEvent(). Dispatching only the
QContextMenuEvents ought to be cheaper than filtering all events.
On the downside, ExtraData gets a little bigger.

We add QQuickItemPrivate::setContextMenu() because
QQuickItem::ExtraData is not exported; and to enable categorized
logging when one menu is replaced with another, this setter returns
the previously-known menu, if any.

Pick-to: 6.9
Change-Id: I9f2553fb579409becf797046dcc473260320c6a5
Reviewed-by: Mitch Curtis <[email protected]>
  • Loading branch information
ec1oud authored and mitchcurtis committed Jan 17, 2025
1 parent 5e69459 commit 24ac769
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 22 deletions.
27 changes: 25 additions & 2 deletions src/quick/items/qquickitem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5802,6 +5802,18 @@ bool QQuickItemPrivate::handlePointerEvent(QPointerEvent *event, bool avoidGrabb
return delivered;
}

#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
bool QQuickItemPrivate::handleContextMenuEvent(QContextMenuEvent *event)
#else
bool QQuickItem::contextMenuEvent(QContextMenuEvent *event)
#endif
{
if (extra.isAllocated() && extra->contextMenu)
return extra->contextMenu->event(event);
event->ignore();
return false;
}

/*!
Called when \a change occurs for this item.
Expand Down Expand Up @@ -9186,7 +9198,7 @@ bool QQuickItem::event(QEvent *ev)
break;
case QEvent::ContextMenu:
// ### Qt 7: add virtual contextMenuEvent (and to QWindow?)
ev->ignore();
d->handleContextMenuEvent(static_cast<QContextMenuEvent*>(ev));
break;
default:
return QObject::event(ev);
Expand Down Expand Up @@ -9492,6 +9504,17 @@ void QQuickItemPrivate::removePointerHandler(QQuickPointerHandler *h)
extra.value().acceptedMouseButtons = extra.value().acceptedMouseButtonsWithoutHandlers;
}

/*! \internal
Replaces any existing context menu with the given \a menu,
and returns the one that was already set before, or \c nullptr.
*/
QObject *QQuickItemPrivate::setContextMenu(QObject *menu)
{
QObject *ret = (extra.isAllocated() ? extra->contextMenu : nullptr);
extra.value().contextMenu = menu;
return ret;
}

#if QT_CONFIG(quick_shadereffect)
QQuickItemLayer::QQuickItemLayer(QQuickItem *item)
: m_item(item)
Expand Down Expand Up @@ -10053,7 +10076,7 @@ QQuickItemPrivate::ExtraData::ExtraData()
: z(0), scale(1), rotation(0), opacity(1),
contents(nullptr), screenAttached(nullptr), layoutDirectionAttached(nullptr),
enterKeyAttached(nullptr),
keyHandler(nullptr),
keyHandler(nullptr), contextMenu(nullptr),
#if QT_CONFIG(quick_shadereffect)
layer(nullptr),
#endif
Expand Down
3 changes: 3 additions & 0 deletions src/quick/items/qquickitem.h
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,9 @@ public Q_SLOTS:
virtual void dropEvent(QDropEvent *);
#endif
virtual bool childMouseEventFilter(QQuickItem *, QEvent *);
#if QT_VERSION >= QT_VERSION_CHECK(7, 0, 0)
virtual bool contextMenuEvent(QContextMenuEvent *event);
#endif

virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *);
virtual void releaseResources();
Expand Down
6 changes: 6 additions & 0 deletions src/quick/items/qquickitem_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,8 @@ class Q_QUICK_EXPORT QQuickItemPrivate
virtual void addPointerHandler(QQuickPointerHandler *h);
virtual void removePointerHandler(QQuickPointerHandler *h);

QObject *setContextMenu(QObject *menu);

// data property
static void data_append(QQmlListProperty<QObject> *, QObject *);
static qsizetype data_count(QQmlListProperty<QObject> *);
Expand Down Expand Up @@ -430,6 +432,7 @@ class Q_QUICK_EXPORT QQuickItemPrivate
QQuickEnterKeyAttached *enterKeyAttached;
QQuickItemKeyFilter *keyHandler;
QVector<QQuickPointerHandler *> pointerHandlers;
QObject *contextMenu;
#if QT_CONFIG(quick_shadereffect)
mutable QQuickItemLayer *layer;
#endif
Expand Down Expand Up @@ -710,6 +713,9 @@ class Q_QUICK_EXPORT QQuickItemPrivate

bool anyPointerHandlerWants(const QPointerEvent *event, const QEventPoint &point) const;
virtual bool handlePointerEvent(QPointerEvent *, bool avoidGrabbers = false);
#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
virtual bool handleContextMenuEvent(QContextMenuEvent *event);
#endif

virtual void setVisible(bool visible);

Expand Down
34 changes: 16 additions & 18 deletions src/quicktemplates/qquickcontextmenu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <QtQml/qqmlcomponent.h>
#include <QtQml/qqmlinfo.h>
#include <QtQuick/qquickwindow.h>
#include <QtQuick/private/qquickitem_p.h>
#include <QtQuickTemplates2/private/qquickmenu_p.h>

QT_BEGIN_NAMESPACE
Expand Down Expand Up @@ -48,8 +49,14 @@ QQuickContextMenu::QQuickContextMenu(QObject *parent)
: QObject(*(new QQuickContextMenuPrivate), parent)
{
Q_ASSERT(parent);
if (!parent->isQuickItemType())
if (parent->isQuickItemType()) {
auto *itemPriv = QQuickItemPrivate::get(static_cast<QQuickItem *>(parent));
Q_ASSERT(itemPriv);
if (QObject *oldMenu = itemPriv->setContextMenu(this))
qCWarning(lcContextMenu) << this << "replaced" << oldMenu << "on" << parent;
} else {
qmlWarning(parent) << "ContextMenu must be attached to an Item";
}
}

QQuickContextMenu *QQuickContextMenu::qmlAttachedProperties(QObject *object)
Expand Down Expand Up @@ -78,44 +85,35 @@ void QQuickContextMenu::setMenu(QQuickMenu *menu)
if (menu == d->menu)
return;

if (d->menu) {
auto *attacheeItem = qobject_cast<QQuickItem *>(parent());
qCDebug(lcContextMenu) << this << "is removing its event filter on attachee" << attacheeItem;
attacheeItem->removeEventFilter(this);
}

d->menu = menu;

if (d->menu) {
auto *attacheeItem = qobject_cast<QQuickItem *>(parent());
qCDebug(lcContextMenu) << this << "is installing an event filter on attachee" << attacheeItem;
attacheeItem->installEventFilter(this);
}

emit menuChanged();
}

bool QQuickContextMenu::eventFilter(QObject *object, QEvent *event)
bool QQuickContextMenu::event(QEvent *event)
{
switch (event->type()) {
case QEvent::ContextMenu: {
qCDebug(lcContextMenu) << this << "is handling filtered ContextMenu event" << event;
qCDebug(lcContextMenu) << this << "handling" << event << "on behalf of" << parent();

auto *attacheeItem = qobject_cast<QQuickItem *>(parent());
auto *menu = this->menu();
if (!menu) {
qCDebug(lcContextMenu) << this << "no menu instance";
return QObject::event(event);
}
menu->setParentItem(attacheeItem);

const auto *contextMenuEvent = static_cast<QContextMenuEvent *>(event);
const QPoint posRelativeToParent(attacheeItem->mapFromScene(contextMenuEvent->pos()).toPoint());
qCDebug(lcContextMenu) << this << "is showing menu instance" << menu << "at" << posRelativeToParent;
qCDebug(lcContextMenu) << this << "showing" << menu << "at" << posRelativeToParent;
menu->popup(posRelativeToParent);
event->accept();
return true;
}
default:
break;
}
return QObject::eventFilter(object, event);
return QObject::event(event);
}

QT_END_NAMESPACE
Expand Down
5 changes: 3 additions & 2 deletions src/quicktemplates/qquickcontextmenu_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,11 @@ class Q_QUICKTEMPLATES2_EXPORT QQuickContextMenu : public QObject
Q_SIGNALS:
void menuChanged();

protected:
bool event(QEvent *) override;

private:
Q_DECLARE_PRIVATE(QQuickContextMenu)

bool eventFilter(QObject *object, QEvent *event) override;
};

QT_END_NAMESPACE
Expand Down

0 comments on commit 24ac769

Please sign in to comment.