6#import <UIKit/UIGestureRecognizerSubclass.h>
16#include <QtCore/private/qcore_mac_p.h>
18#include <QGuiApplication>
19#include <QtGui/private/qwindow_p.h>
25 return qApp->focusWindow() ?
26 reinterpret_cast<QUIView *
>(
qApp->focusWindow()->winId()) : 0;
38 if (self = [super
init]) {
39 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
40 [notificationCenter addObserver:self
41 selector:@selector(localeDidChange:)
42 name:NSCurrentLocaleDidChangeNotification object:nil];
50 [[NSNotificationCenter defaultCenter] removeObserver:self];
54- (
void)localeDidChange:(NSNotification *)notification
74 if (self = [super initWithTarget:self action:
@selector(gestureStateChanged:)]) {
78 self.hasDeferredScrollToCursor = NO;
82 self.cancelsTouchesInView = NO;
83 self.delaysTouchesEnded = NO;
86 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
88 [notificationCenter addObserver:self
89 selector:@selector(keyboardWillShow:)
90 name:UIKeyboardWillShowNotification object:nil];
91 [notificationCenter addObserver:self
92 selector:@selector(keyboardWillOrDidChange:)
93 name:UIKeyboardDidShowNotification object:nil];
94 [notificationCenter addObserver:self
95 selector:@selector(keyboardWillHide:)
96 name:UIKeyboardWillHideNotification object:nil];
97 [notificationCenter addObserver:self
98 selector:@selector(keyboardWillOrDidChange:)
99 name:UIKeyboardDidHideNotification object:nil];
100 [notificationCenter addObserver:self
101 selector:@selector(keyboardDidChangeFrame:)
102 name:UIKeyboardDidChangeFrameNotification object:nil];
111 [[NSNotificationCenter defaultCenter] removeObserver:self];
118- (
void)keyboardWillShow:(NSNotification *)notification
120 [
self keyboardWillOrDidChange:notification];
122 UIResponder *firstResponder = [UIResponder currentFirstResponder];
127 self.enabled = m_context->isInputPanelVisible();
129 m_context->scrollToCursor();
132- (
void)keyboardWillHide:(NSNotification *)notification
134 [
self keyboardWillOrDidChange:notification];
136 if (self.state != UIGestureRecognizerStateBegan) {
141 m_context->scroll(0);
144- (
void)keyboardDidChangeFrame:(NSNotification *)notification
146 [
self keyboardWillOrDidChange:notification];
150 if (m_context->isInputPanelVisible())
151 m_context->scrollToCursor();
154- (
void)keyboardWillOrDidChange:(NSNotification *)notification
156 m_context->updateKeyboardState(notification);
161- (BOOL)canPreventGestureRecognizer:(UIGestureRecognizer *)
other
167- (BOOL)canBePreventedByGestureRecognizer:(UIGestureRecognizer *)
other
173- (
void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)
event
175 [
super touchesBegan:touches withEvent:event];
177 if (!m_context->isInputPanelVisible()) {
178 qImDebug(
"keyboard was hidden by sliding it down, disabling hide-keyboard gesture");
183 if ([touches
count] != 1)
184 self.state = UIGestureRecognizerStateFailed;
187- (
void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)
event
189 [
super touchesMoved:touches withEvent:event];
191 if (self.state != UIGestureRecognizerStatePossible)
194 CGPoint touchPoint = [[touches anyObject] locationInView:self.view];
195 if (CGRectContainsPoint(m_context->keyboardState().keyboardEndRect, touchPoint))
196 self.state = UIGestureRecognizerStateBegan;
199- (
void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)
event
201 [
super touchesEnded:touches withEvent:event];
203 [
self touchesEndedOrCancelled];
206- (
void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)
event
208 [
super touchesCancelled:touches withEvent:event];
210 [
self touchesEndedOrCancelled];
213- (
void)touchesEndedOrCancelled
218 dispatch_async(dispatch_get_main_queue (), ^{
220 Q_ASSERT(self.state != UIGestureRecognizerStateBegan);
222 if (self.state == UIGestureRecognizerStateChanged)
223 self.state = UIGestureRecognizerStateEnded;
225 self.state = UIGestureRecognizerStateFailed;
229- (
void)gestureStateChanged:(
id)sender
233 if (self.state == UIGestureRecognizerStateBegan) {
234 qImDebug(
"hide keyboard gesture was triggered");
235 UIResponder *firstResponder = [UIResponder currentFirstResponder];
237 [firstResponder resignFirstResponder];
245 if (!m_context->isInputPanelVisible()) {
246 qImDebug(
"keyboard was hidden, disabling hide-keyboard gesture");
249 qImDebug(
"gesture completed without triggering");
250 if (self.hasDeferredScrollToCursor) {
251 qImDebug(
"applying deferred scroll to cursor");
252 m_context->scrollToCursor();
256 self.hasDeferredScrollToCursor = NO;
273 focusObject =
qApp ?
qApp->focusObject() : 0;
278 Qt::InputMethodQueries updatedProperties;
288 return updatedProperties;
306 [iosScreen->uiWindow() addGestureRecognizer:m_keyboardHideGesture];
314 [m_localeListener release];
315 [m_keyboardHideGesture.view removeGestureRecognizer:m_keyboardHideGesture];
316 [m_keyboardHideGesture release];
318 [m_textResponder release];
324 qImDebug(
"can't show virtual keyboard without a focus object, ignoring");
329 if (![m_textResponder isFirstResponder]) {
330 qImDebug(
"QIOSTextInputResponder is not first responder, ignoring");
335 qImDebug(
"current focus object does not match IM state, likely hiding from focusOut event, so ignoring");
339 qImDebug(
"hiding VKB as requested by QInputMethod::hide()");
340 [m_textResponder resignFirstResponder];
356 static CGRect currentKeyboardRect = CGRectZero;
361 NSDictionary *userInfo = [notification userInfo];
363 CGRect frameBegin = [[userInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
364 CGRect frameEnd = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
366 bool atEndOfKeyboardTransition = [notification.name rangeOfString:@"Did"].location != NSNotFound;
368 currentKeyboardRect = atEndOfKeyboardTransition ? frameEnd : frameBegin;
376 m_keyboardState.
keyboardVisible = CGRectIntersectsRect(frameEnd, [UIScreen mainScreen].bounds);
385 m_keyboardState.
animationCurve = UIViewAnimationCurve([[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] integerValue]);
388 m_keyboardState.
animationDuration = [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
391 qImDebug() <<
qPrintable(QString::fromNSString(notification.name)) <<
"from" << QRectF::fromCGRect(frameBegin) <<
"to" << QRectF::fromCGRect(frameEnd)
394 qImDebug(
"No notification to update keyboard state based on, just updating keyboard rect");
397 if (!
focusView() || CGRectIsEmpty(currentKeyboardRect))
400 m_keyboardState.
keyboardRect = QRectF::fromCGRect([
focusView() convertRect:currentKeyboardRect fromView:nil]);
429UIView *QIOSInputContext::scrollableRootView()
431 if (!m_keyboardHideGesture.view)
434 UIWindow *
window =
static_cast<UIWindow*
>(m_keyboardHideGesture.view);
438 return window.rootViewController.view;
446 if (m_keyboardHideGesture.state == UIGestureRecognizerStatePossible && m_keyboardHideGesture.numberOfTouches == 1) {
449 qImDebug(
"deferring scrolling to cursor as we're still waiting for a possible gesture");
454 UIView *rootView = scrollableRootView();
465 if (CGRectGetMaxY(m_keyboardState.
keyboardEndRect) != CGRectGetMaxY([UIScreen mainScreen].bounds)) {
466 qImDebug(
"Keyboard not docked, ignoring request to scroll to reveal cursor");
479 if (!cursorRect.
isNull()) {
481 static const int kCursorRectPadding = 20;
482 cursorRect.
adjust(0, -kCursorRectPadding, 0, kCursorRectPadding);
485 cursorRect &= screenGeometry;
491 if (!cursorRect.
isNull() && !availableGeometry.
contains(cursorRect)) {
492 qImDebug() <<
"cursor rect" << cursorRect <<
"not fully within" << availableGeometry;
493 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;
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");
612 qImDebug() <<
"new focus window =" << focusWindow;
632 qImDebug() <<
"fw =" <<
qApp->focusWindow() <<
"fo =" <<
qApp->focusObject();
640 <<
", doing manual update";
649 Qt::InputMethodQueries changedProperties = m_imeState.
update(updatedProperties);
654 if (!m_textResponder || [m_textResponder needsKeyboardReconfigure:changedProperties]) {
655 [m_textResponder autorelease];
656 if (inputIsReadOnly) {
657 qImDebug(
"creating new read-only text responder");
660 qImDebug(
"creating new read/write text responder");
664 qImDebug(
"no need to reconfigure keyboard, just notifying input delegate");
665 [m_textResponder notifyInputDelegate:changedProperties];
668 if (![m_textResponder isFirstResponder]) {
669 qImDebug(
"IM enabled, making text responder first responder");
670 [m_textResponder becomeFirstResponder];
675 }
else if ([m_textResponder isFirstResponder]) {
676 qImDebug(
"IM not enabled, resigning text responder as first responder");
677 [m_textResponder resignFirstResponder];
686#if !defined(QT_NO_DEBUG)
695 qWarning(
"QPlatformInputContext::inputMethodAccepted() does not match actual focus object IM enablement!");
698 return lastKnownImEnablementState;
706 qImDebug(
"releasing text responder");
719 const auto oldResponder = m_textResponder;
720 [m_textResponder reset];
721 [m_textResponder autorelease];
722 m_textResponder =
nullptr;
729 if ([oldResponder isFirstResponder]) {
730 qImDebug(
"IM not enabled, resigning autoreleased text responder as first responder");
731 [oldResponder resignFirstResponder];
746 [m_textResponder commit];
751 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.
QScreen * primaryScreen
the primary (or default) screen of the application.
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()
QPlatformInputContext * inputContext() const override
Returns the platforms input context.
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
constexpr QRect toRect() const noexcept
Returns a QRect based on the values of this rectangle.
\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.
constexpr int bottom() const noexcept
Returns the y-coordinate of the rectangle's bottom edge.
constexpr QPoint topLeft() const noexcept
Returns the position of the rectangle's top-left corner.
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...
constexpr QSize size() const noexcept
Returns the size of the rectangle.
The QRegion class specifies a clip region for a painter.
QPlatformScreen * handle() const
Get the platform screen handle.
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_END_NAMESPACE typedef QT_PREPEND_NAMESPACE(quintptr) WId
Qt::InputMethodQueries update(Qt::InputMethodQueries properties)
QInputMethodQueryEvent currentState
NSTimeInterval animationDuration
UIViewAnimationCurve animationCurve