summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTor Arne Vestbø <tor.arne.vestbo@qt.io>2023-03-10 13:49:39 +0100
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2023-03-13 16:44:02 +0000
commit6116f9591b591aef8248d93387467217b77dc012 (patch)
tree274c0093d7f9675142bfbe4aff08d321e1bbe07a
parent5695f1d8c7e4746618cca48eb0c8c7c999b86e34 (diff)
macOS: Guard text input client from destroyed QCocoaWindow
The text input system on macOS may in some cases hold references to our QNSView, even after we've destroyed the corresponding QCocoaWindow. This happens e.g. when using the Keyboard Viewer to input text into a dialog, and then closing the dialog. In this situation we get text input callbacks such as selectedRange, attributedSubstringForProposedRange, firstRectForCharacterRange, and need to account for the lack of a valid platform window. This happens even if NSTextInputContext.currentInputContext has been updated to the input context of the parent window, and even if we explicitly deactivate the old input context and return nil for the input context of the now QCocoaWindow-less QNSView. We can combine this situation with the handling of a missing focus object, so that each callback doesn't need explicit platform window checks. Fixes: QTBUG-106369 Fixes: QTBUG-111183 Fixes: QTBUG-105250 Change-Id: I5bc1b9667376c87221fe5007db162224c022c09f Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io> (cherry picked from commit 441993a9a2e6cb236667ff67e06f1673df06db0a) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r--src/plugins/platforms/cocoa/qnsview.mm1
-rw-r--r--src/plugins/platforms/cocoa/qnsview_complextext.mm36
2 files changed, 24 insertions, 13 deletions
diff --git a/src/plugins/platforms/cocoa/qnsview.mm b/src/plugins/platforms/cocoa/qnsview.mm
index cea94840cc..7e95b2c2e2 100644
--- a/src/plugins/platforms/cocoa/qnsview.mm
+++ b/src/plugins/platforms/cocoa/qnsview.mm
@@ -117,6 +117,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSViewMouseMoveHelper);
@end
@interface QNSView (ComplexText) <NSTextInputClient>
+@property (readonly) QObject* focusObject;
@end
@implementation QNSView {
diff --git a/src/plugins/platforms/cocoa/qnsview_complextext.mm b/src/plugins/platforms/cocoa/qnsview_complextext.mm
index 6e7dadf44b..075b0b7f4e 100644
--- a/src/plugins/platforms/cocoa/qnsview_complextext.mm
+++ b/src/plugins/platforms/cocoa/qnsview_complextext.mm
@@ -43,6 +43,17 @@
// ------------- Text insertion -------------
+- (QObject*)focusObject
+{
+ // The text input system may still hold a reference to our QNSView,
+ // even after QCocoaWindow has been destructed, delivering text input
+ // events to us, so we need to guard for this situation explicitly.
+ if (!m_platformWindow)
+ return nullptr;
+
+ return m_platformWindow->window()->focusObject();
+}
+
/*
Inserts the given text, potentially replacing existing text.
@@ -88,8 +99,7 @@
}
}
- QObject *focusObject = m_platformWindow->window()->focusObject();
- if (queryInputMethod(focusObject)) {
+ if (queryInputMethod(self.focusObject)) {
QInputMethodEvent inputMethodEvent;
const bool isAttributedString = [text isKindOfClass:NSAttributedString.class];
@@ -111,7 +121,7 @@
inputMethodEvent.setCommitString(commitString, replaceFrom, replaceLength);
}
- QCoreApplication::sendEvent(focusObject, &inputMethodEvent);
+ QCoreApplication::sendEvent(self.focusObject, &inputMethodEvent);
}
m_composingText.clear();
@@ -122,6 +132,9 @@
{
Q_UNUSED(sender);
+ if (!m_platformWindow)
+ return;
+
// Depending on the input method, pressing enter may
// result in simply dismissing the input method editor,
// without confirming the composition. In other cases
@@ -278,7 +291,7 @@
// Update the composition, now that we've computed the replacement range
m_composingText = preeditString;
- if (QObject *focusObject = m_platformWindow->window()->focusObject()) {
+ if (QObject *focusObject = self.focusObject) {
m_composingFocusObject = focusObject;
if (queryInputMethod(focusObject)) {
QInputMethodEvent event(preeditString, preeditAttributes);
@@ -320,8 +333,7 @@
*/
- (NSRange)markedRange
{
- QObject *focusObject = m_platformWindow->window()->focusObject();
- if (auto queryResult = queryInputMethod(focusObject, Qt::ImAbsolutePosition)) {
+ if (auto queryResult = queryInputMethod(self.focusObject, Qt::ImAbsolutePosition)) {
int absoluteCursorPosition = queryResult.value(Qt::ImAbsolutePosition).toInt();
// The cursor position as reflected by Qt::ImAbsolutePosition is not
@@ -356,7 +368,7 @@
<< "for focus object" << m_composingFocusObject;
if (!m_composingText.isEmpty()) {
- QObject *focusObject = m_platformWindow->window()->focusObject();
+ QObject *focusObject = self.focusObject;
if (queryInputMethod(focusObject)) {
QInputMethodEvent e;
e.setCommitString(m_composingText);
@@ -417,8 +429,7 @@
*/
- (NSRange)selectedRange
{
- QObject *focusObject = m_platformWindow->window()->focusObject();
- if (auto queryResult = queryInputMethod(focusObject,
+ if (auto queryResult = queryInputMethod(self.focusObject,
Qt::ImCursorPosition | Qt::ImAbsolutePosition | Qt::ImAnchorPosition)) {
// Unfortunately the Qt::InputMethodQuery values are all relative
@@ -465,8 +476,7 @@
*/
- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)range actualRange:(NSRangePointer)actualRange
{
- QObject *focusObject = m_platformWindow->window()->focusObject();
- if (auto queryResult = queryInputMethod(focusObject,
+ if (auto queryResult = queryInputMethod(self.focusObject,
Qt::ImAbsolutePosition | Qt::ImTextBeforeCursor | Qt::ImTextAfterCursor)) {
const int absoluteCursorPosition = queryResult.value(Qt::ImAbsolutePosition).toInt();
const QString textBeforeCursor = queryResult.value(Qt::ImTextBeforeCursor).toString();
@@ -502,8 +512,8 @@
Q_UNUSED(range);
Q_UNUSED(actualRange);
- QWindow *window = m_platformWindow->window();
- if (queryInputMethod(window->focusObject())) {
+ QWindow *window = m_platformWindow ? m_platformWindow->window() : nullptr;
+ if (window && queryInputMethod(window->focusObject())) {
QRect cursorRect = qApp->inputMethod()->cursorRectangle().toRect();
cursorRect.moveBottomLeft(window->mapToGlobal(cursorRect.bottomLeft()));
return QCocoaScreen::mapToNative(cursorRect);