6#import <UIKit/UIGestureRecognizerSubclass.h>
16#include <QtCore/private/qcore_mac_p.h>
18#include <QGuiApplication>
19#include <QtGui/private/qwindow_p.h>
21#include <QtCore/qpointer.h>
27 return qApp->focusWindow() ?
28 reinterpret_cast<QUIView *
>(
qApp->focusWindow()->winId()) : 0;
40 if (self = [super
init]) {
41 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
42 [notificationCenter addObserver:self
43 selector:@selector(localeDidChange:)
44 name:NSCurrentLocaleDidChangeNotification object:nil];
52 [[NSNotificationCenter defaultCenter] removeObserver:self];
56- (
void)localeDidChange:(NSNotification *)notification
76 if (self = [super initWithTarget:self action:
@selector(gestureStateChanged:)]) {
80 self.hasDeferredScrollToCursor = NO;
84 self.cancelsTouchesInView = NO;
85 self.delaysTouchesEnded = NO;
88 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
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];
113 [[NSNotificationCenter defaultCenter] removeObserver:self];
120- (
void)keyboardWillShow:(NSNotification *)notification
122 [
self keyboardWillOrDidChange:notification];
124 UIResponder *firstResponder = [UIResponder qt_currentFirstResponder];
129 self.enabled = m_context->isInputPanelVisible();
131 m_context->scrollToCursor();
134- (
void)keyboardWillHide:(NSNotification *)notification
136 [
self keyboardWillOrDidChange:notification];
138 if (self.state != UIGestureRecognizerStateBegan) {
143 m_context->scroll(0);
146- (
void)keyboardDidChangeFrame:(NSNotification *)notification
148 [
self keyboardWillOrDidChange:notification];
152 if (m_context->isInputPanelVisible())
153 m_context->scrollToCursor();
156- (
void)keyboardWillOrDidChange:(NSNotification *)notification
158 m_context->updateKeyboardState(notification);
163- (BOOL)canPreventGestureRecognizer:(UIGestureRecognizer *)
other
169- (BOOL)canBePreventedByGestureRecognizer:(UIGestureRecognizer *)
other
175- (
void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)
event
177 [
super touchesBegan:touches withEvent:event];
179 if (!m_context->isInputPanelVisible()) {
180 qImDebug(
"keyboard was hidden by sliding it down, disabling hide-keyboard gesture");
185 if ([touches
count] != 1)
186 self.state = UIGestureRecognizerStateFailed;
189- (
void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)
event
191 [
super touchesMoved:touches withEvent:event];
193 if (self.state != UIGestureRecognizerStatePossible)
196 CGPoint touchPoint = [[touches anyObject] locationInView:self.view];
197 if (CGRectContainsPoint(m_context->keyboardState().keyboardEndRect, touchPoint))
198 self.state = UIGestureRecognizerStateBegan;
201- (
void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)
event
203 [
super touchesEnded:touches withEvent:event];
205 [
self touchesEndedOrCancelled];
208- (
void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)
event
210 [
super touchesCancelled:touches withEvent:event];
212 [
self touchesEndedOrCancelled];
215- (
void)touchesEndedOrCancelled
220 dispatch_async(dispatch_get_main_queue (), ^{
222 Q_ASSERT(self.state != UIGestureRecognizerStateBegan);
224 if (self.state == UIGestureRecognizerStateChanged)
225 self.state = UIGestureRecognizerStateEnded;
227 self.state = UIGestureRecognizerStateFailed;
231- (
void)gestureStateChanged:(
id)sender
235 if (self.state == UIGestureRecognizerStateBegan) {
236 qImDebug(
"hide keyboard gesture was triggered");
237 UIResponder *firstResponder = [UIResponder qt_currentFirstResponder];
239 [firstResponder resignFirstResponder];
247 if (!m_context->isInputPanelVisible()) {
248 qImDebug(
"keyboard was hidden, disabling hide-keyboard gesture");
251 qImDebug(
"gesture completed without triggering");
252 if (self.hasDeferredScrollToCursor) {
253 qImDebug(
"applying deferred scroll to cursor");
254 m_context->scrollToCursor();
258 self.hasDeferredScrollToCursor = NO;
275 focusObject =
qApp ?
qApp->focusObject() : 0;
280 Qt::InputMethodQueries updatedProperties;
290 return updatedProperties;
312 [m_localeListener release];
313 [m_keyboardHideGesture.view removeGestureRecognizer:m_keyboardHideGesture];
314 [m_keyboardHideGesture release];
316 [m_textResponder release];
322 qImDebug(
"can't show virtual keyboard without a focus object, ignoring");
327 if (![m_textResponder isFirstResponder]) {
328 qImDebug(
"QIOSTextInputResponder is not first responder, ignoring");
333 qImDebug(
"current focus object does not match IM state, likely hiding from focusOut event, so ignoring");
337 qImDebug(
"hiding VKB as requested by QInputMethod::hide()");
338 [m_textResponder resignFirstResponder];
351#if defined(Q_OS_TVOS) || defined(Q_OS_VISIONOS)
354 static CGRect currentKeyboardRect = CGRectZero;
359 NSDictionary *userInfo = [notification userInfo];
361 CGRect frameBegin = [[userInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
362 CGRect frameEnd = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
364 bool atEndOfKeyboardTransition = [notification.name rangeOfString:@"Did"].location != NSNotFound;
366 currentKeyboardRect = atEndOfKeyboardTransition ? frameEnd : frameBegin;
374 m_keyboardState.
keyboardVisible = CGRectIntersectsRect(frameEnd, [UIScreen mainScreen].bounds);
383 m_keyboardState.
animationCurve = UIViewAnimationCurve([[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] integerValue]);
386 m_keyboardState.
animationDuration = [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
389 qImDebug() <<
qPrintable(QString::fromNSString(notification.name)) <<
"from" << QRectF::fromCGRect(frameBegin) <<
"to" << QRectF::fromCGRect(frameEnd)
392 qImDebug(
"No notification to update keyboard state based on, just updating keyboard rect");
395 if (!
focusView() || CGRectIsEmpty(currentKeyboardRect))
398 m_keyboardState.
keyboardRect = QRectF::fromCGRect([
focusView() convertRect:currentKeyboardRect fromView:nil]);
427UIView *QIOSInputContext::scrollableRootView()
429 if (!m_keyboardHideGesture.view)
432 UIWindow *
window =
static_cast<UIWindow*
>(m_keyboardHideGesture.view);
436 return window.rootViewController.view;
441#if !defined(Q_OS_VISIONOS)
445 if (m_keyboardHideGesture.state == UIGestureRecognizerStatePossible && m_keyboardHideGesture.numberOfTouches == 1) {
448 qImDebug(
"deferring scrolling to cursor as we're still waiting for a possible gesture");
453 UIView *rootView = scrollableRootView();
464 if (CGRectGetMaxY(m_keyboardState.
keyboardEndRect) != CGRectGetMaxY([UIScreen mainScreen].bounds)) {
465 qImDebug(
"Keyboard not docked, ignoring request to scroll to reveal cursor");
471 QRect cursorRect =
QRect(focusWindow->
mapToGlobal(windowCurosorRect.topLeft()), windowCurosorRect.size());
478 if (!cursorRect.
isNull()) {
480 static const int kCursorRectPadding = 20;
481 cursorRect.
adjust(0, -kCursorRectPadding, 0, kCursorRectPadding);
484 cursorRect &= screenGeometry;
490 if (!cursorRect.
isNull() && !availableGeometry.
contains(cursorRect)) {
491 qImDebug() <<
"cursor rect" << cursorRect <<
"not fully within" << availableGeometry;
492 int scrollToCenter = -(availableGeometry.
center() - cursorRect.
center()).
y();
505 UIView *rootView = scrollableRootView();
510 qWarning() <<
"can't scroll root view in application extension";
514 CATransform3D translationTransform = CATransform3DMakeTranslation(0.0, -
y, 0.0);
515 if (CATransform3DEqualToTransform(translationTransform, rootView.layer.sublayerTransform))
518 qImDebug() <<
"scrolling root view to y =" << -
y;
520 QPointer<QIOSInputContext>
self =
this;
521 [UIView animateWithDuration:m_keyboardState.animationDuration delay:0
522 options:(m_keyboardState.animationCurve << 16) | UIViewAnimationOptionBeginFromCurrentState
532 NSObject *action = (NSObject*)[rootView actionForLayer:rootView.layer forKey:@"backgroundColor"];
534 CABasicAnimation *animation;
535 if ([action isKindOfClass:[CABasicAnimation class]]) {
536 animation = static_cast<CABasicAnimation*>(action);
537 animation.keyPath = @"sublayerTransform";
539 animation = [CABasicAnimation animationWithKeyPath:@"sublayerTransform"];
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;
548 bool keyboardScrollIsActive = y != 0;
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);
559 UIWindowLevel windowLevelAdjustment = keyboardScrollIsActive ? UIWindowLevelStatusBar : 0;
561 UIWindowLevel windowLevelAdjustment = 0;
563 window.windowLevel = originalWindowLevels.value(window) + windowLevelAdjustment;
565 if (!keyboardScrollIsActive)
566 originalWindowLevels.remove(window);
574 updateKeyboardState();
586 qImDebug() <<
"new focus object =" << focusObject;
589 && m_keyboardHideGesture.state == UIGestureRecognizerStateChanged) {
594 qImDebug() <<
"clearing focus object" << focusObject <<
"as hide-keyboard gesture is active";
597 }
else if (focusObject == m_imeState.
focusObject) {
598 qImDebug(
"same focus object as last update, skipping reset");
610 qImDebug() <<
"new focus window =" << focusWindow;
615 [m_keyboardHideGesture.view removeGestureRecognizer:m_keyboardHideGesture];
616 [focusView().window addGestureRecognizer:m_keyboardHideGesture];
635 qImDebug() <<
"fw =" <<
qApp->focusWindow() <<
"fo =" <<
qApp->focusObject();
643 <<
", doing manual update";
652 Qt::InputMethodQueries changedProperties = m_imeState.
update(updatedProperties);
657 if (!m_textResponder || [m_textResponder needsKeyboardReconfigure:changedProperties]) {
658 [m_textResponder autorelease];
659 if (inputIsReadOnly) {
660 qImDebug(
"creating new read-only text responder");
663 qImDebug(
"creating new read/write text responder");
667 qImDebug(
"no need to reconfigure keyboard, just notifying input delegate");
668 [m_textResponder notifyInputDelegate:changedProperties];
671 if (![m_textResponder isFirstResponder]) {
672 qImDebug(
"IM enabled, making text responder first responder");
673 [m_textResponder becomeFirstResponder];
678 }
else if ([m_textResponder isFirstResponder]) {
679 qImDebug(
"IM not enabled, resigning text responder as first responder");
680 [m_textResponder resignFirstResponder];
689#if !defined(QT_NO_DEBUG)
698 qWarning(
"QPlatformInputContext::inputMethodAccepted() does not match actual focus object IM enablement!");
701 return lastKnownImEnablementState;
709 qImDebug(
"releasing text responder");
722 const auto oldResponder = m_textResponder;
723 [m_textResponder reset];
724 [m_textResponder autorelease];
725 m_textResponder =
nullptr;
732 if ([oldResponder isFirstResponder]) {
733 qImDebug(
"IM not enabled, resigning autoreleased text responder as first responder");
734 [oldResponder resignFirstResponder];
749 [m_textResponder commit];
754 return QLocale(QString::fromNSString([[NSLocale currentLocale] objectForKey:NSLocaleIdentifier]));
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)
static QIOSInputContext * instance()
QLocale locale() const override
void reset() override
Method to be called when input method needs to be reset.
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()
static QObjectPrivate * get(QObject *o)
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
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.
\inmodule QtCore\reentrant
\inmodule QtCore\reentrant
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.
constexpr bool isNull() const noexcept
Returns true if the rectangle is a null rectangle, otherwise returns false.
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...
The QRegion class specifies a clip region for a painter.
bool toBool() const
Returns the variant as a bool if the variant has userType() Bool.
void newState(QList< State > &states, const char *token, const char *lexem, bool pre)
BOOL hasDeferredScrollToCursor
Combined button and popup list for selecting options.
QTextStream & center(QTextStream &stream)
Calls QTextStream::setFieldAlignment(QTextStream::AlignCenter) on stream and returns stream.
bool qt_apple_isApplicationExtension()
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
static QUIView * focusView()
constexpr const T & qMin(const T &a, const T &b)
GLenum GLenum GLsizei count
static const QRectF boundingRect(const QPointF *points, int pointCount)
#define qPrintable(string)
static QT_BEGIN_NAMESPACE void init(QTextBoundaryFinder::BoundaryType type, QStringView str, QCharAttributes *attributes)
Qt::InputMethodQueries update(Qt::InputMethodQueries properties)
QInputMethodQueryEvent currentState
NSTimeInterval animationDuration
UIViewAnimationCurve animationCurve