TextEdit and TextInput: map QContextMenuEvent to text cursor pos
...if the position is not already set. Events that come from a keyboard menu key or shortcut often have pos() == {0, 0}, but we want the context menu to be in the context of what the user is editing. First though, we need QQuickDeliveryAgentPrivate::contextMenuTargets() to search for items at the correct position. We don't override delivery order, but activeFocusItem should be in the list that is returned. Pick-to: 6.9 6.8 Fixes: QTBUG-136253 Change-Id: I7eea03e118a95a1a267f02bd3385cc1ae4cbb0a0 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
This commit is contained in:
parent
cc98876fea
commit
31ca3936d3
|
@ -3316,6 +3316,18 @@ void QQuickTextEdit::focusOutEvent(QFocusEvent *event)
|
|||
QQuickImplicitSizeItem::focusOutEvent(event);
|
||||
}
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
|
||||
bool QQuickTextEditPrivate::handleContextMenuEvent(QContextMenuEvent *event)
|
||||
#else
|
||||
bool QQuickTextEdit::contextMenuEvent(QContextMenuEvent *event)
|
||||
#endif
|
||||
{
|
||||
Q_Q(QQuickTextEdit);
|
||||
QContextMenuEvent mapped(event->reason(), q->cursorRectangle().center().toPoint(),
|
||||
event->globalPos(), event->modifiers());
|
||||
return QQuickItemPrivate::handleContextMenuEvent(&mapped);
|
||||
}
|
||||
|
||||
void QQuickTextEditPrivate::handleFocusEvent(QFocusEvent *event)
|
||||
{
|
||||
Q_Q(QQuickTextEdit);
|
||||
|
|
|
@ -406,6 +406,9 @@ protected:
|
|||
void mouseReleaseEvent(QMouseEvent *event) override;
|
||||
void mouseDoubleClickEvent(QMouseEvent *event) override;
|
||||
void mouseMoveEvent(QMouseEvent *event) override;
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(7, 0, 0)
|
||||
bool contextMenuEvent(QContextMenuEvent *event) override;
|
||||
#endif
|
||||
#if QT_CONFIG(im)
|
||||
void inputMethodEvent(QInputMethodEvent *e) override;
|
||||
#endif
|
||||
|
|
|
@ -129,6 +129,9 @@ public:
|
|||
#endif
|
||||
|
||||
void setNativeCursorEnabled(bool) {}
|
||||
#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
|
||||
bool handleContextMenuEvent(QContextMenuEvent *event) override;
|
||||
#endif
|
||||
void handleFocusEvent(QFocusEvent *event);
|
||||
void addCurrentTextNodeToRoot(QQuickTextNodeEngine *, QSGTransformNode *, QSGInternalTextNode *, TextNodeIterator&, int startPos);
|
||||
QSGInternalTextNode* createTextNode();
|
||||
|
|
|
@ -1718,6 +1718,18 @@ void QQuickTextInput::mouseReleaseEvent(QMouseEvent *event)
|
|||
QQuickImplicitSizeItem::mouseReleaseEvent(event);
|
||||
}
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
|
||||
bool QQuickTextInputPrivate::handleContextMenuEvent(QContextMenuEvent *event)
|
||||
#else
|
||||
bool QQuickTextInput::contextMenuEvent(QContextMenuEvent *event)
|
||||
#endif
|
||||
{
|
||||
Q_Q(QQuickTextInput);
|
||||
QContextMenuEvent mapped(event->reason(), q->cursorRectangle().center().toPoint(),
|
||||
event->globalPos(), event->modifiers());
|
||||
return QQuickItemPrivate::handleContextMenuEvent(&mapped);
|
||||
}
|
||||
|
||||
bool QQuickTextInputPrivate::sendMouseEventToInputContext(QMouseEvent *event)
|
||||
{
|
||||
#if QT_CONFIG(im)
|
||||
|
|
|
@ -359,6 +359,9 @@ protected:
|
|||
void mouseReleaseEvent(QMouseEvent *event) override;
|
||||
void mouseDoubleClickEvent(QMouseEvent *event) override;
|
||||
void keyPressEvent(QKeyEvent* ev) override;
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(7, 0, 0)
|
||||
bool contextMenuEvent(QContextMenuEvent *event) override;
|
||||
#endif
|
||||
#if QT_CONFIG(im)
|
||||
void inputMethodEvent(QInputMethodEvent *) override;
|
||||
#endif
|
||||
|
|
|
@ -150,6 +150,9 @@ public:
|
|||
bool determineHorizontalAlignment();
|
||||
bool setHAlign(QQuickTextInput::HAlignment, bool forceAlign = false);
|
||||
void mirrorChange() override;
|
||||
#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
|
||||
bool handleContextMenuEvent(QContextMenuEvent *event) override;
|
||||
#endif
|
||||
bool sendMouseEventToInputContext(QMouseEvent *event);
|
||||
#if QT_CONFIG(im)
|
||||
Qt::InputMethodHints effectiveInputMethodHints() const;
|
||||
|
|
|
@ -2943,7 +2943,10 @@ QVector<QQuickItem *> QQuickDeliveryAgentPrivate::contextMenuTargets(QQuickItem
|
|||
return std::nullopt;
|
||||
};
|
||||
|
||||
return eventTargets(item, event, event->pos(), predicate);
|
||||
const auto pos = event->pos().isNull() ? activeFocusItem->mapToScene({}).toPoint() : event->pos();
|
||||
if (event->pos().isNull())
|
||||
qCDebug(lcContextMenu) << "for QContextMenuEvent, active focus item is" << activeFocusItem << "@" << pos;
|
||||
return eventTargets(item, event, pos, predicate);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
|
||||
ApplicationWindow {
|
||||
id: window
|
||||
width: 600
|
||||
height: 400
|
||||
|
||||
TextArea {
|
||||
id: textArea
|
||||
objectName: "textArea"
|
||||
text: qsTr("Some, well, text here (surprise!)")
|
||||
width: parent.width
|
||||
height: parent.height / 3
|
||||
}
|
||||
|
||||
TextField {
|
||||
objectName: "textField"
|
||||
text: qsTr("A not-so-vast partially-open field")
|
||||
width: parent.width
|
||||
y: parent.height * 2 / 3
|
||||
}
|
||||
|
||||
contentItem.ContextMenu.menu: Menu {
|
||||
id: windowMenu
|
||||
objectName: "windowMenu"
|
||||
|
||||
MenuItem {
|
||||
text: qsTr("Open window")
|
||||
}
|
||||
MenuItem {
|
||||
text: qsTr("Wash window")
|
||||
}
|
||||
MenuItem {
|
||||
text: qsTr("Admire the view")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -37,6 +37,7 @@ private slots:
|
|||
void drawerShouldntPreventOpening();
|
||||
void explicitMenuPreventsBuiltInMenu();
|
||||
void menuItemShouldntTriggerOnRelease();
|
||||
void textControlsMenuKey();
|
||||
|
||||
private:
|
||||
bool contextMenuTriggeredOnRelease = false;
|
||||
|
@ -350,6 +351,53 @@ void tst_QQuickContextMenu::menuItemShouldntTriggerOnRelease() // QTBUG-133302
|
|||
QCOMPARE(triggeredSpy.size(), 0);
|
||||
}
|
||||
|
||||
void tst_QQuickContextMenu::textControlsMenuKey()
|
||||
{
|
||||
QQuickApplicationHelper helper(this, "textControlsAndParentMenus.qml");
|
||||
QVERIFY2(helper.ready, helper.failureMessage());
|
||||
QQuickWindow *window = helper.window;
|
||||
window->show();
|
||||
QVERIFY(QTest::qWaitForWindowExposed(window));
|
||||
|
||||
auto *textArea = window->findChild<QQuickItem *>("textArea");
|
||||
QVERIFY(textArea);
|
||||
auto *textField = window->findChild<QQuickItem *>("textField");
|
||||
QVERIFY(textField);
|
||||
auto *windowMenu = window->findChild<QQuickMenu *>("windowMenu");
|
||||
QVERIFY(windowMenu);
|
||||
const QPoint &windowCenter = mapCenterToWindow(window->contentItem());
|
||||
|
||||
// give position in the middle of the window: expect the window menu
|
||||
{
|
||||
QContextMenuEvent cme(QContextMenuEvent::Keyboard, windowCenter, window->mapToGlobal(windowCenter));
|
||||
QGuiApplication::sendEvent(window, &cme);
|
||||
auto *openMenu = window->findChild<QQuickMenu *>();
|
||||
QVERIFY(openMenu);
|
||||
QCOMPARE(openMenu->objectName(), "windowMenu");
|
||||
openMenu->close();
|
||||
}
|
||||
|
||||
// focus the TextArea and give position 0, 0: expect the TextArea's menu
|
||||
{
|
||||
textArea->forceActiveFocus();
|
||||
QContextMenuEvent cme(QContextMenuEvent::Keyboard, {}, window->mapToGlobal(QPoint()));
|
||||
QGuiApplication::sendEvent(window, &cme);
|
||||
auto *openMenu = textArea->findChild<QQuickMenu *>();
|
||||
QVERIFY(openMenu);
|
||||
openMenu->close();
|
||||
}
|
||||
|
||||
// focus the TextField and give position 0, 0: expect the TextField's menu
|
||||
{
|
||||
textField->forceActiveFocus();
|
||||
QContextMenuEvent cme(QContextMenuEvent::Keyboard, {}, window->mapToGlobal(QPoint()));
|
||||
QGuiApplication::sendEvent(window, &cme);
|
||||
auto *openMenu = textField->findChild<QQuickMenu *>();
|
||||
QVERIFY(openMenu);
|
||||
openMenu->close();
|
||||
}
|
||||
}
|
||||
|
||||
QTEST_QUICKCONTROLS_MAIN(tst_QQuickContextMenu)
|
||||
|
||||
#include "tst_qquickcontextmenu.moc"
|
||||
|
|
Loading…
Reference in New Issue