summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/cocoa
diff options
context:
space:
mode:
authorTor Arne Vestbø <tor.arne.vestbo@qt.io>2023-03-10 13:49:39 +0100
committerTor Arne Vestbø <tor.arne.vestbo@qt.io>2023-03-13 15:49:37 +0000
commit441993a9a2e6cb236667ff67e06f1673df06db0a (patch)
tree18bae40b777c932e30d5a156940d1c314306c72c /src/plugins/platforms/cocoa
parentbd68df090525ef33481650c81b6348808d20028b (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 Pick-to: 6.5 6.2 5.15 Change-Id: I5bc1b9667376c87221fe5007db162224c022c09f Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
Diffstat (limited to 'src/plugins/platforms/cocoa')
-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 c574e2b7c8..f8c17e179d 100644
--- a/src/plugins/platforms/cocoa/qnsview.mm
+++ b/src/plugins/platforms/cocoa/qnsview.mm
@@ -83,6 +83,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 cdeb7154fe..3ccaf8269e 100644
--- a/src/plugins/platforms/cocoa/qnsview_complextext.mm
+++ b/src/plugins/platforms/cocoa/qnsview_complextext.mm
@@ -7,6 +7,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.
@@ -52,8 +63,7 @@
}
}
- QObject *focusObject = m_platformWindow->window()->focusObject();
- if (queryInputMethod(focusObject)) {
+ if (queryInputMethod(self.focusObject)) {
QInputMethodEvent inputMethodEvent;
const bool isAttributedString = [text isKindOfClass:NSAttributedString.class];
@@ -75,7 +85,7 @@
inputMethodEvent.setCommitString(commitString, replaceFrom, replaceLength);
}
- QCoreApplication::sendEvent(focusObject, &inputMethodEvent);
+ QCoreApplication::sendEvent(self.focusObject, &inputMethodEvent);
}
m_composingText.clear();
@@ -86,6 +96,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
@@ -242,7 +255,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);
@@ -284,8 +297,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
@@ -320,7 +332,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);
@@ -393,8 +405,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
@@ -441,8 +452,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();
@@ -478,8 +488,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);