Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qiosinputcontext.mm
Go to the documentation of this file.
1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qiosinputcontext.h"
5
6#import <UIKit/UIGestureRecognizerSubclass.h>
7
8#include "qiosglobal.h"
9#include "qiosintegration.h"
10#include "qiosscreen.h"
11#include "qiostextresponder.h"
12#include "qiosviewcontroller.h"
13#include "qioswindow.h"
14#include "quiview.h"
15
16#include <QtCore/private/qcore_mac_p.h>
17
18#include <QGuiApplication>
19#include <QtGui/private/qwindow_p.h>
20
21#include <QtCore/qpointer.h>
22
23// -------------------------------------------------------------------------
24
26{
27 return qApp->focusWindow() ?
28 reinterpret_cast<QUIView *>(qApp->focusWindow()->winId()) : 0;
29}
30
31// -------------------------------------------------------------------------
32
33@interface QIOSLocaleListener : NSObject
34@end
35
36@implementation QIOSLocaleListener
37
38- (instancetype)init
39{
40 if (self = [super init]) {
41 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
42 [notificationCenter addObserver:self
43 selector:@selector(localeDidChange:)
44 name:NSCurrentLocaleDidChangeNotification object:nil];
45 }
46
47 return self;
48}
49
50- (void)dealloc
51{
52 [[NSNotificationCenter defaultCenter] removeObserver:self];
53 [super dealloc];
54}
55
56- (void)localeDidChange:(NSNotification *)notification
57{
58 Q_UNUSED(notification);
59 QIOSInputContext::instance()->emitLocaleChanged();
60}
61
62@end
63
64// -------------------------------------------------------------------------
65
66@interface QIOSKeyboardListener : UIGestureRecognizer <UIGestureRecognizerDelegate>
68@end
69
70@implementation QIOSKeyboardListener {
71 QT_PREPEND_NAMESPACE(QIOSInputContext) *m_context;
72}
73
74- (instancetype)initWithQIOSInputContext:(QT_PREPEND_NAMESPACE(QIOSInputContext) *)context
75{
76 if (self = [super initWithTarget:self action:@selector(gestureStateChanged:)]) {
77
78 m_context = context;
79
80 self.hasDeferredScrollToCursor = NO;
81
82 // UIGestureRecognizer
83 self.enabled = NO;
84 self.cancelsTouchesInView = NO;
85 self.delaysTouchesEnded = NO;
86
87#ifndef Q_OS_TVOS
88 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
89
90 [notificationCenter addObserver:self
91 selector:@selector(keyboardWillShow:)
92 name:UIKeyboardWillShowNotification object:nil];
93 [notificationCenter addObserver:self
94 selector:@selector(keyboardWillOrDidChange:)
95 name:UIKeyboardDidShowNotification object:nil];
96 [notificationCenter addObserver:self
97 selector:@selector(keyboardWillHide:)
98 name:UIKeyboardWillHideNotification object:nil];
99 [notificationCenter addObserver:self
100 selector:@selector(keyboardWillOrDidChange:)
101 name:UIKeyboardDidHideNotification object:nil];
102 [notificationCenter addObserver:self
103 selector:@selector(keyboardDidChangeFrame:)
104 name:UIKeyboardDidChangeFrameNotification object:nil];
105#endif
106 }
107
108 return self;
109}
110
111- (void)dealloc
112{
113 [[NSNotificationCenter defaultCenter] removeObserver:self];
114
115 [super dealloc];
116}
117
118// -------------------------------------------------------------------------
119
120- (void)keyboardWillShow:(NSNotification *)notification
121{
122 [self keyboardWillOrDidChange:notification];
123
124 UIResponder *firstResponder = [UIResponder qt_currentFirstResponder];
125 if (![firstResponder isKindOfClass:[QIOSTextInputResponder class]])
126 return;
127
128 // Enable hide-keyboard gesture
129 self.enabled = m_context->isInputPanelVisible();
130
131 m_context->scrollToCursor();
132}
133
134- (void)keyboardWillHide:(NSNotification *)notification
135{
136 [self keyboardWillOrDidChange:notification];
137
138 if (self.state != UIGestureRecognizerStateBegan) {
139 // Only disable the gesture if the hiding of the keyboard was not caused by it.
140 // Otherwise we need to await the final touchEnd callback for doing some clean-up.
141 self.enabled = NO;
142 }
143 m_context->scroll(0);
144}
145
146- (void)keyboardDidChangeFrame:(NSNotification *)notification
147{
148 [self keyboardWillOrDidChange:notification];
149
150 // If the keyboard was visible and docked from before, this is just a geometry
151 // change (normally caused by an orientation change). In that case, update scroll:
152 if (m_context->isInputPanelVisible())
153 m_context->scrollToCursor();
154}
155
156- (void)keyboardWillOrDidChange:(NSNotification *)notification
157{
158 m_context->updateKeyboardState(notification);
159}
160
161// -------------------------------------------------------------------------
162
163- (BOOL)canPreventGestureRecognizer:(UIGestureRecognizer *)other
164{
166 return NO;
167}
168
169- (BOOL)canBePreventedByGestureRecognizer:(UIGestureRecognizer *)other
170{
172 return NO;
173}
174
175- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
176{
177 [super touchesBegan:touches withEvent:event];
178
179 if (!m_context->isInputPanelVisible()) {
180 qImDebug("keyboard was hidden by sliding it down, disabling hide-keyboard gesture");
181 self.enabled = NO;
182 return;
183 }
184
185 if ([touches count] != 1)
186 self.state = UIGestureRecognizerStateFailed;
187}
188
189- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
190{
191 [super touchesMoved:touches withEvent:event];
192
193 if (self.state != UIGestureRecognizerStatePossible)
194 return;
195
196 CGPoint touchPoint = [[touches anyObject] locationInView:self.view];
197 if (CGRectContainsPoint(m_context->keyboardState().keyboardEndRect, touchPoint))
198 self.state = UIGestureRecognizerStateBegan;
199}
200
201- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
202{
203 [super touchesEnded:touches withEvent:event];
204
205 [self touchesEndedOrCancelled];
206}
207
208- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
209{
210 [super touchesCancelled:touches withEvent:event];
211
212 [self touchesEndedOrCancelled];
213}
214
215- (void)touchesEndedOrCancelled
216{
217 // Defer final state change until next runloop iteration, so that Qt
218 // has a chance to process the final touch events first, before we eg.
219 // scroll the view.
220 dispatch_async(dispatch_get_main_queue (), ^{
221 // iOS will transition from began to changed by itself
222 Q_ASSERT(self.state != UIGestureRecognizerStateBegan);
223
224 if (self.state == UIGestureRecognizerStateChanged)
225 self.state = UIGestureRecognizerStateEnded;
226 else
227 self.state = UIGestureRecognizerStateFailed;
228 });
229}
230
231- (void)gestureStateChanged:(id)sender
232{
233 Q_UNUSED(sender);
234
235 if (self.state == UIGestureRecognizerStateBegan) {
236 qImDebug("hide keyboard gesture was triggered");
237 UIResponder *firstResponder = [UIResponder qt_currentFirstResponder];
238 Q_ASSERT([firstResponder isKindOfClass:[QIOSTextInputResponder class]]);
239 [firstResponder resignFirstResponder];
240 }
241}
242
243- (void)reset
244{
245 [super reset];
246
247 if (!m_context->isInputPanelVisible()) {
248 qImDebug("keyboard was hidden, disabling hide-keyboard gesture");
249 self.enabled = NO;
250 } else {
251 qImDebug("gesture completed without triggering");
252 if (self.hasDeferredScrollToCursor) {
253 qImDebug("applying deferred scroll to cursor");
254 m_context->scrollToCursor();
255 }
256 }
257
258 self.hasDeferredScrollToCursor = NO;
259}
260
261@end
262
263// -------------------------------------------------------------------------
264
266
267Qt::InputMethodQueries ImeState::update(Qt::InputMethodQueries properties)
268{
269 if (!properties)
270 return {};
271
273
274 // Update the focus object that the new state is based on
275 focusObject = qApp ? qApp->focusObject() : 0;
276
277 if (focusObject)
279
280 Qt::InputMethodQueries updatedProperties;
281 for (uint i = 0; i < (sizeof(Qt::ImQueryAll) * CHAR_BIT); ++i) {
283 if (newState.value(property) != currentState.value(property)) {
284 updatedProperties |= property;
285 currentState.setValue(property, newState.value(property));
286 }
287 }
288 }
289
290 return updatedProperties;
291}
292
293// -------------------------------------------------------------------------
294
296{
297 return static_cast<QIOSInputContext *>(QIOSIntegration::instance()->inputContext());
298}
299
302 , m_localeListener([QIOSLocaleListener new])
303 , m_keyboardHideGesture([[QIOSKeyboardListener alloc] initWithQIOSInputContext:this])
304 , m_textResponder(0)
305{
306 Q_ASSERT(!qGuiApp->focusWindow());
308}
309
311{
312 [m_localeListener release];
313 [m_keyboardHideGesture.view removeGestureRecognizer:m_keyboardHideGesture];
314 [m_keyboardHideGesture release];
315
316 [m_textResponder release];
317}
318
320{
321 // No-op, keyboard controlled fully by platform based on focus
322 qImDebug("can't show virtual keyboard without a focus object, ignoring");
323}
324
326{
327 if (![m_textResponder isFirstResponder]) {
328 qImDebug("QIOSTextInputResponder is not first responder, ignoring");
329 return;
330 }
331
332 if (qGuiApp->focusObject() != m_imeState.focusObject) {
333 qImDebug("current focus object does not match IM state, likely hiding from focusOut event, so ignoring");
334 return;
335 }
336
337 qImDebug("hiding VKB as requested by QInputMethod::hide()");
338 [m_textResponder resignFirstResponder];
339}
340
342{
343 if (QWindow *focusWindow = qApp->focusWindow())
344 static_cast<QWindowPrivate *>(QObjectPrivate::get(focusWindow))->clearFocusObject();
345}
346
347// -------------------------------------------------------------------------
348
349void QIOSInputContext::updateKeyboardState(NSNotification *notification)
350{
351#if defined(Q_OS_TVOS) || defined(Q_OS_VISIONOS)
352 Q_UNUSED(notification);
353#else
354 static CGRect currentKeyboardRect = CGRectZero;
355
356 KeyboardState previousState = m_keyboardState;
357
358 if (notification) {
359 NSDictionary *userInfo = [notification userInfo];
360
361 CGRect frameBegin = [[userInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
362 CGRect frameEnd = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
363
364 bool atEndOfKeyboardTransition = [notification.name rangeOfString:@"Did"].location != NSNotFound;
365
366 currentKeyboardRect = atEndOfKeyboardTransition ? frameEnd : frameBegin;
367
368 // The isInputPanelVisible() property is based on whether or not the virtual keyboard
369 // is visible on screen, and does not follow the logic of the iOS WillShow and WillHide
370 // notifications which are not emitted for undocked keyboards, and are buggy when dealing
371 // with input-accessory-views. The reason for using frameEnd here (the future state),
372 // instead of the current state reflected in frameBegin, is that QInputMethod::isVisible()
373 // is documented to reflect the future state in the case of animated transitions.
374 m_keyboardState.keyboardVisible = CGRectIntersectsRect(frameEnd, [UIScreen mainScreen].bounds);
375
376 // Used for auto-scroller, and will be used for animation-signal in the future
377 m_keyboardState.keyboardEndRect = frameEnd;
378
379 if (m_keyboardState.animationCurve < 0) {
380 // We only set the animation curve the first time it has a valid value, since iOS will sometimes report
381 // an invalid animation curve even if the keyboard is animating, and we don't want to overwrite the
382 // curve in that case.
383 m_keyboardState.animationCurve = UIViewAnimationCurve([[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] integerValue]);
384 }
385
386 m_keyboardState.animationDuration = [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
387 m_keyboardState.keyboardAnimating = m_keyboardState.animationDuration > 0 && !atEndOfKeyboardTransition;
388
389 qImDebug() << qPrintable(QString::fromNSString(notification.name)) << "from" << QRectF::fromCGRect(frameBegin) << "to" << QRectF::fromCGRect(frameEnd)
390 << "(curve =" << m_keyboardState.animationCurve << "duration =" << m_keyboardState.animationDuration << "s)";
391 } else {
392 qImDebug("No notification to update keyboard state based on, just updating keyboard rect");
393 }
394
395 if (!focusView() || CGRectIsEmpty(currentKeyboardRect))
396 m_keyboardState.keyboardRect = QRectF();
397 else // QInputmethod::keyboardRectangle() is documented to be in window coordinates.
398 m_keyboardState.keyboardRect = QRectF::fromCGRect([focusView() convertRect:currentKeyboardRect fromView:nil]);
399
400 // Emit for all changed properties
401 if (m_keyboardState.keyboardVisible != previousState.keyboardVisible)
403 if (m_keyboardState.keyboardAnimating != previousState.keyboardAnimating)
405 if (m_keyboardState.keyboardRect != previousState.keyboardRect)
407#endif
408}
409
411{
412 return m_keyboardState.keyboardVisible;
413}
414
416{
417 return m_keyboardState.keyboardAnimating;
418}
419
421{
422 return m_keyboardState.keyboardRect;
423}
424
425// -------------------------------------------------------------------------
426
427UIView *QIOSInputContext::scrollableRootView()
428{
429 if (!m_keyboardHideGesture.view)
430 return 0;
431
432 UIWindow *window = static_cast<UIWindow*>(m_keyboardHideGesture.view);
433 if (![window.rootViewController isKindOfClass:[QIOSViewController class]])
434 return 0;
435
436 return window.rootViewController.view;
437}
438
440{
441#if !defined(Q_OS_VISIONOS)
442 if (!isQtApplication())
443 return;
444
445 if (m_keyboardHideGesture.state == UIGestureRecognizerStatePossible && m_keyboardHideGesture.numberOfTouches == 1) {
446 // Don't scroll to the cursor if the user is touching the screen and possibly
447 // trying to trigger the hide-keyboard gesture.
448 qImDebug("deferring scrolling to cursor as we're still waiting for a possible gesture");
449 m_keyboardHideGesture.hasDeferredScrollToCursor = YES;
450 return;
451 }
452
453 UIView *rootView = scrollableRootView();
454 if (!rootView)
455 return;
456
457 if (!focusView())
458 return;
459
460 if (rootView.window != focusView().window)
461 return;
462
463 // We only support auto-scroll for docked keyboards for now, so make sure that's the case
464 if (CGRectGetMaxY(m_keyboardState.keyboardEndRect) != CGRectGetMaxY([UIScreen mainScreen].bounds)) {
465 qImDebug("Keyboard not docked, ignoring request to scroll to reveal cursor");
466 return;
467 }
468
469 QPlatformWindow *focusWindow = qApp->focusWindow()->handle();
470 QRect windowCurosorRect = QPlatformInputContext::cursorRectangle().toRect();
471 QRect cursorRect = QRect(focusWindow->mapToGlobal(windowCurosorRect.topLeft()), windowCurosorRect.size());
472
473 // We explicitly ask for the geometry of the screen instead of the availableGeometry,
474 // as we hide the status bar when scrolling the screen, so the available geometry will
475 // include the space taken by the status bar at the moment.
476 QRect screenGeometry = focusWindow->screen()->geometry();
477
478 if (!cursorRect.isNull()) {
479 // Add some padding so that the cursor does not end up directly above the keyboard
480 static const int kCursorRectPadding = 20;
481 cursorRect.adjust(0, -kCursorRectPadding, 0, kCursorRectPadding);
482
483 // Make sure the cursor rect is still within the screen geometry after padding
484 cursorRect &= screenGeometry;
485 }
486
487 QRect keyboardGeometry = QRectF::fromCGRect(m_keyboardState.keyboardEndRect).toRect();
488 QRect availableGeometry = (QRegion(screenGeometry) - keyboardGeometry).boundingRect();
489
490 if (!cursorRect.isNull() && !availableGeometry.contains(cursorRect)) {
491 qImDebug() << "cursor rect" << cursorRect << "not fully within" << availableGeometry;
492 int scrollToCenter = -(availableGeometry.center() - cursorRect.center()).y();
493 int scrollToBottom = focusWindow->screen()->geometry().bottom() - availableGeometry.bottom();
494 scroll(qMin(scrollToCenter, scrollToBottom));
495 } else {
496 scroll(0);
497 }
498#endif
499}
500
502{
503 Q_ASSERT(y >= 0);
504
505 UIView *rootView = scrollableRootView();
506 if (!rootView)
507 return;
508
510 qWarning() << "can't scroll root view in application extension";
511 return;
512 }
513
514 CATransform3D translationTransform = CATransform3DMakeTranslation(0.0, -y, 0.0);
515 if (CATransform3DEqualToTransform(translationTransform, rootView.layer.sublayerTransform))
516 return;
517
518 qImDebug() << "scrolling root view to y =" << -y;
519
520 QPointer<QIOSInputContext> self = this;
521 [UIView animateWithDuration:m_keyboardState.animationDuration delay:0
522 options:(m_keyboardState.animationCurve << 16) | UIViewAnimationOptionBeginFromCurrentState
523 animations:^{
524 // The sublayerTransform property of CALayer is not implicitly animated for a
525 // layer-backed view, even inside a UIView animation block, so we need to set up
526 // an explicit CoreAnimation animation. Since there is no predefined media timing
527 // function that matches the custom keyboard animation curve we cheat by asking
528 // the view for an animation of another property, which will give us an animation
529 // that matches the parameters we passed to [UIView animateWithDuration] above.
530 // The reason we ask for the animation of 'backgroundColor' is that it's a simple
531 // property that will not return a compound animation, like eg. bounds will.
532 NSObject *action = (NSObject*)[rootView actionForLayer:rootView.layer forKey:@"backgroundColor"];
533
534 CABasicAnimation *animation;
535 if ([action isKindOfClass:[CABasicAnimation class]]) {
536 animation = static_cast<CABasicAnimation*>(action);
537 animation.keyPath = @"sublayerTransform"; // Instead of backgroundColor
538 } else {
539 animation = [CABasicAnimation animationWithKeyPath:@"sublayerTransform"];
540 }
541
542 CATransform3D currentSublayerTransform = static_cast<CALayer *>([rootView.layer presentationLayer]).sublayerTransform;
543 animation.fromValue = [NSValue valueWithCATransform3D:currentSublayerTransform];
544 animation.toValue = [NSValue valueWithCATransform3D:translationTransform];
545 [rootView.layer addAnimation:animation forKey:@"AnimateSubLayerTransform"];
546 rootView.layer.sublayerTransform = translationTransform;
547
548 bool keyboardScrollIsActive = y != 0;
549
550 // Raise all known windows to above the status-bar if we're scrolling the screen,
551 // while keeping the relative window level between the windows the same.
552 NSArray<UIWindow *> *applicationWindows = [qt_apple_sharedApplication() windows];
553 static QHash<UIWindow *, UIWindowLevel> originalWindowLevels;
554 for (UIWindow *window in applicationWindows) {
555 if (keyboardScrollIsActive && !originalWindowLevels.contains(window))
556 originalWindowLevels.insert(window, window.windowLevel);
557
558#ifndef Q_OS_TVOS
559 UIWindowLevel windowLevelAdjustment = keyboardScrollIsActive ? UIWindowLevelStatusBar : 0;
560#else
561 UIWindowLevel windowLevelAdjustment = 0;
562#endif
563 window.windowLevel = originalWindowLevels.value(window) + windowLevelAdjustment;
564
565 if (!keyboardScrollIsActive)
566 originalWindowLevels.remove(window);
567 }
568 }
569 completion:^(BOOL){
570 if (self) {
571 // Scrolling the root view results in the keyboard being moved
572 // relative to the focus window, so we need to re-evaluate the
573 // keyboard rectangle.
574 updateKeyboardState();
575 }
576 }
577 ];
578}
579
580// -------------------------------------------------------------------------
581
583{
584 Q_UNUSED(focusObject);
585
586 qImDebug() << "new focus object =" << focusObject;
587
589 && m_keyboardHideGesture.state == UIGestureRecognizerStateChanged) {
590 // A new focus object may be set as part of delivering touch events to
591 // application during the hide-keyboard gesture, but we don't want that
592 // to result in a new object getting focus and bringing the keyboard up
593 // again.
594 qImDebug() << "clearing focus object" << focusObject << "as hide-keyboard gesture is active";
596 return;
597 } else if (focusObject == m_imeState.focusObject) {
598 qImDebug("same focus object as last update, skipping reset");
599 return;
600 }
601
602 reset();
603
606}
607
609{
610 qImDebug() << "new focus window =" << focusWindow;
611
612 reset();
613
614 if (isQtApplication()) {
615 [m_keyboardHideGesture.view removeGestureRecognizer:m_keyboardHideGesture];
616 [focusView().window addGestureRecognizer:m_keyboardHideGesture];
617 }
618
619 // The keyboard rectangle depend on the focus window, so
620 // we need to re-evaluate the keyboard state.
622
625}
626
633void QIOSInputContext::update(Qt::InputMethodQueries updatedProperties)
634{
635 qImDebug() << "fw =" << qApp->focusWindow() << "fo =" << qApp->focusObject();
636
637 // Changes to the focus object should always result in a call to setFocusObject(),
638 // triggering a reset() which will update all the properties based on the new
639 // focus object. We try to detect code paths that fail this assertion and smooth
640 // over the situation by doing a manual update of the focus object.
641 if (qApp->focusObject() != m_imeState.focusObject && updatedProperties != Qt::ImQueryAll) {
642 qWarning() << "stale focus object" << static_cast<void *>(m_imeState.focusObject)
643 << ", doing manual update";
644 setFocusObject(qApp->focusObject());
645 return;
646 }
647
648 // Mask for properties that we are interested in and see if any of them changed
650
651 // Perform update first, so we can trust the value of inputMethodAccepted()
652 Qt::InputMethodQueries changedProperties = m_imeState.update(updatedProperties);
653
654 const bool inputIsReadOnly = m_imeState.currentState.value(Qt::ImReadOnly).toBool();
655
656 if (inputMethodAccepted() || inputIsReadOnly) {
657 if (!m_textResponder || [m_textResponder needsKeyboardReconfigure:changedProperties]) {
658 [m_textResponder autorelease];
659 if (inputIsReadOnly) {
660 qImDebug("creating new read-only text responder");
661 m_textResponder = [[QIOSTextResponder alloc] initWithInputContext:this];
662 } else {
663 qImDebug("creating new read/write text responder");
664 m_textResponder = [[QIOSTextInputResponder alloc] initWithInputContext:this];
665 }
666 } else {
667 qImDebug("no need to reconfigure keyboard, just notifying input delegate");
668 [m_textResponder notifyInputDelegate:changedProperties];
669 }
670
671 if (![m_textResponder isFirstResponder]) {
672 qImDebug("IM enabled, making text responder first responder");
673 [m_textResponder becomeFirstResponder];
674 }
675
676 if (changedProperties & Qt::ImCursorRectangle)
678 } else if ([m_textResponder isFirstResponder]) {
679 qImDebug("IM not enabled, resigning text responder as first responder");
680 [m_textResponder resignFirstResponder];
681 }
682}
683
685{
686 // The IM enablement state is based on the last call to update()
687 bool lastKnownImEnablementState = m_imeState.currentState.value(Qt::ImEnabled).toBool();
688
689#if !defined(QT_NO_DEBUG)
690 // QPlatformInputContext keeps a cached value of the current IM enablement state that is
691 // updated by QGuiApplication when the current focus object changes, or by QInputMethod's
692 // update() function. If the focus object changes, but the change is not propagated as
693 // a signal to QGuiApplication due to bugs in the widget/graphicsview/qml stack, we'll
694 // end up with a stale value for QPlatformInputContext::inputMethodAccepted(). To be on
695 // the safe side we always use our own cached value to decide if IM is enabled, and try
696 // to detect the case where the two values are out of sync.
697 if (lastKnownImEnablementState != QPlatformInputContext::inputMethodAccepted())
698 qWarning("QPlatformInputContext::inputMethodAccepted() does not match actual focus object IM enablement!");
699#endif
700
701 return lastKnownImEnablementState;
702}
703
708{
709 qImDebug("releasing text responder");
710
711 // UIKit will sometimes, for unknown reasons, unset the input delegate on the
712 // current text responder. This seems to happen as a result of us calling
713 // [self.inputDelegate textDidChange:self] from [m_textResponder reset].
714 // But it won't be set to nil directly, only after a character is typed on
715 // the input panel after the reset. This strange behavior seems to be related
716 // to us overriding [QUIView setInteraction] to ignore UITextInteraction. If we
717 // didn't do that, the delegate would be kept. But not overriding that function
718 // has its own share of issues, so it seems better to keep that way for now.
719 // Instead, we choose to recreate the text responder as a brute-force solution
720 // until we have better knowledge of what is going on (or implement the new
721 // UITextInteraction protocol).
722 const auto oldResponder = m_textResponder;
723 [m_textResponder reset];
724 [m_textResponder autorelease];
725 m_textResponder = nullptr;
726
728
729 // If update() didn't end up creating a new text responder, oldResponder will still be
730 // the first responder. In that case we need to resign it, so that the input panel hides.
731 // (the input panel will apparently not hide if the first responder is only released).
732 if ([oldResponder isFirstResponder]) {
733 qImDebug("IM not enabled, resigning autoreleased text responder as first responder");
734 [oldResponder resignFirstResponder];
735 }
736}
737
747{
748 qImDebug("unmarking text");
749 [m_textResponder commit];
750}
751
753{
754 return QLocale(QString::fromNSString([[NSLocale currentLocale] objectForKey:NSLocaleIdentifier]));
755}
756
static bool sendEvent(QObject *receiver, QEvent *event)
Sends event event directly to receiver receiver, using the notify() function.
void focusWindowChanged(QWindow *focusWindow)
This signal is emitted when the focused window changes.
void setFocusObject(QObject *object) override
This virtual method gets called to notify updated focus to object.
void clearCurrentFocusObject()
QRectF keyboardRect() const override
This function can be reimplemented to return virtual keyboard rectangle in currently active window co...
void updateKeyboardState(NSNotification *notification=nullptr)
void scroll(int y)
static QIOSInputContext * instance()
QLocale locale() const override
void reset() override
Method to be called when input method needs to be reset.
void commit() override
bool isAnimating() const override
This function can be reimplemented to return true whenever input method is animating shown or hidden.
void showInputPanel() override
Request to show input panel.
void focusWindowChanged(QWindow *focusWindow)
void hideInputPanel() override
Request to hide input panel.
bool isInputPanelVisible() const override
Returns input panel visibility status.
bool inputMethodAccepted() const
void update(Qt::InputMethodQueries) override
Notification on editor updates.
static QIOSIntegration * instance()
The QInputMethodQueryEvent class provides an event sent by the input context to input objects.
Definition qevent.h:679
QVariant value(Qt::InputMethodQuery query) const
Returns value of the property query.
Definition qevent.cpp:2381
static QObjectPrivate * get(QObject *o)
Definition qobject_p.h:150
\inmodule QtCore
Definition qobject.h:103
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
The QPlatformInputContext class abstracts the input method dependent data and composing state.
bool inputMethodAccepted() const
Returns true if current focus object supports input method events.
void emitAnimatingChanged()
Active QPlatformInputContext is responsible for providing animating property to QInputMethod.
void emitInputPanelVisibleChanged()
Active QPlatformInputContext is responsible for providing visible property to QInputMethod.
void emitKeyboardRectChanged()
Active QPlatformInputContext is responsible for providing keyboardRectangle property to QInputMethod.
static QRectF cursorRectangle()
QPlatformInputContext::cursorRectangle.
virtual QRect geometry() const =0
Reimplement in subclass to return the pixel geometry of the screen.
The QPlatformWindow class provides an abstraction for top-level windows.
QPlatformScreen * screen() const override
Returns the platform screen handle corresponding to this platform window, or null if the window is no...
virtual QPoint mapToGlobal(const QPoint &pos) const
Translates the window coordinate pos to global screen coordinates using native methods.
\inmodule QtCore\reentrant
Definition qrect.h:484
\inmodule QtCore\reentrant
Definition qrect.h:30
constexpr void adjust(int x1, int y1, int x2, int y2) noexcept
Adds dx1, dy1, dx2 and dy2 respectively to the existing coordinates of the rectangle.
Definition qrect.h:373
constexpr bool isNull() const noexcept
Returns true if the rectangle is a null rectangle, otherwise returns false.
Definition qrect.h:164
bool contains(const QRect &r, bool proper=false) const noexcept
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qrect.cpp:855
The QRegion class specifies a clip region for a painter.
Definition qregion.h:27
bool toBool() const
Returns the variant as a bool if the variant has userType() Bool.
\inmodule QtGui
Definition qwindow.h:63
#define this
Definition dialogs.cpp:9
void newState(QList< State > &states, const char *token, const char *lexem, bool pre)
Combined button and popup list for selecting options.
InputMethodQuery
@ ImPlatformData
@ ImEnterKeyType
@ ImReadOnly
@ ImCursorRectangle
@ ImHints
@ ImQueryInput
@ ImEnabled
@ ImQueryAll
QTextStream & center(QTextStream &stream)
Calls QTextStream::setFieldAlignment(QTextStream::AlignCenter) on stream and returns stream.
QString self
Definition language.cpp:58
static void * context
bool qt_apple_isApplicationExtension()
Definition qcore_mac.mm:424
#define qApp
static const QCssKnownValue properties[NumProperties - 1]
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage void
#define qGuiApp
bool isQtApplication()
Definition qiosglobal.mm:20
#define qImDebug
Definition qiosglobal.h:20
static QUIView * focusView()
#define qWarning
Definition qlogging.h:166
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
GLenum GLuint id
[7]
GLenum GLenum GLsizei count
GLint GLint bottom
GLint y
struct _cl_event * event
GLboolean reset
static const QRectF boundingRect(const QPointF *points, int pointCount)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define qPrintable(string)
Definition qstring.h:1531
static QT_BEGIN_NAMESPACE void init(QTextBoundaryFinder::BoundaryType type, QStringView str, QCharAttributes *attributes)
#define Q_UNUSED(x)
unsigned int uint
Definition qtypes.h:34
const char property[13]
Definition qwizard.cpp:101
QSharedPointer< T > other(t)
[5]
aWidget window() -> setWindowTitle("New Window Title")
[2]
Qt::InputMethodQueries update(Qt::InputMethodQueries properties)
QInputMethodQueryEvent currentState
QObject * focusObject
NSTimeInterval animationDuration
UIViewAnimationCurve animationCurve