diff options
Diffstat (limited to 'src/plugins/platforms/cocoa/qnsview_complextext.mm')
-rw-r--r-- | src/plugins/platforms/cocoa/qnsview_complextext.mm | 131 |
1 files changed, 75 insertions, 56 deletions
diff --git a/src/plugins/platforms/cocoa/qnsview_complextext.mm b/src/plugins/platforms/cocoa/qnsview_complextext.mm index 39739d9725..d7f8f4baf0 100644 --- a/src/plugins/platforms/cocoa/qnsview_complextext.mm +++ b/src/plugins/platforms/cocoa/qnsview_complextext.mm @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only // This file is included from qnsview.mm, and only used to organize the code @@ -43,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. @@ -88,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]; @@ -111,7 +85,7 @@ inputMethodEvent.setCommitString(commitString, replaceFrom, replaceLength); } - QCoreApplication::sendEvent(focusObject, &inputMethodEvent); + QCoreApplication::sendEvent(self.focusObject, &inputMethodEvent); } m_composingText.clear(); @@ -122,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 @@ -145,10 +122,17 @@ // e.g. "~\r". We have already inserted the composition, // so we need to follow up with a single newline event. - KeyEvent newlineEvent(NSApp.currentEvent); - newlineEvent.key = Qt::Key_Return; - newlineEvent.text = QLatin1Char(kReturnCharCode); - newlineEvent.nativeVirtualKey = kVK_Return; + KeyEvent newlineEvent(m_currentlyInterpretedKeyEvent ? + m_currentlyInterpretedKeyEvent : NSApp.currentEvent); + newlineEvent.type = QEvent::KeyPress; + + const bool isEnter = newlineEvent.modifiers & Qt::KeypadModifier; + newlineEvent.key = isEnter ? Qt::Key_Enter : Qt::Key_Return; + newlineEvent.text = isEnter ? QLatin1Char(kEnterCharCode) + : QLatin1Char(kReturnCharCode); + newlineEvent.nativeVirtualKey = isEnter ? quint32(kVK_ANSI_KeypadEnter) + : quint32(kVK_Return); + qCDebug(lcQpaKeys) << "Inserting newline via" << newlineEvent; newlineEvent.sendWindowSystemEvent(m_platformWindow->window()); } @@ -271,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); @@ -313,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 @@ -349,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); @@ -396,8 +379,20 @@ // pass the originating key event up the responder chain if applicable. qCDebug(lcQpaKeys) << "Trying to perform command" << selector; - if (![self tryToPerform:selector with:self]) + if (![self tryToPerform:selector with:self]) { m_sendKeyEvent = true; + + if (![NSStringFromSelector(selector) hasPrefix:@"insert"]) { + // The text input system determined that the key event was not + // meant for text insertion, and instead asked us to treat it + // as a (possibly noop) command. This typically happens for key + // events with either ⌘ or ⌃, function keys such as F1-F35, + // arrow keys, etc. We reflect that when sending the key event + // later on, by removing the text from the event, so that the + // event does not result in text insertion on the client side. + m_sendKeyEventWithoutText = true; + } + } } // ------------- Various text properties ------------- @@ -410,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 @@ -458,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(); @@ -495,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); @@ -512,6 +505,32 @@ return NSNotFound; } +/* + Returns the window level of the text input. + + This allows the input method to place its input panel + above the text input. +*/ +- (NSInteger)windowLevel +{ + // The default level assumed by input methods is NSFloatingWindowLevel, + // but our NSWindow level could be higher than that for many reasons, + // including being set via QWindow::setFlags() or directly on the + // NSWindow, or because we're embedded into a native view hierarchy. + // Return the actual window level to account for this. + auto level = m_platformWindow ? m_platformWindow->nativeWindow().level + : NSNormalWindowLevel; + + // The logic above only covers our own window though. In some cases, + // such as when a completer is active, the text input has a lower + // window level than another window that's also visible, and we don't + // want the input panel to be sandwiched between these two windows. + // Account for this by explicitly using NSPopUpMenuWindowLevel as + // the minimum window level, which corresponds to the highest level + // one can get via QWindow::setFlags(), except for Qt::ToolTip. + return qMax(level, NSPopUpMenuWindowLevel); +} + // ------------- Helper functions ------------- /* |