diff options
Diffstat (limited to 'src/plugins/platforms/ios')
-rw-r--r-- | src/plugins/platforms/ios/qiosfiledialog.mm | 4 | ||||
-rw-r--r-- | src/plugins/platforms/ios/qiosinputcontext.mm | 36 | ||||
-rw-r--r-- | src/plugins/platforms/ios/qiosintegration.h | 6 | ||||
-rw-r--r-- | src/plugins/platforms/ios/qiosintegration.mm | 11 | ||||
-rw-r--r-- | src/plugins/platforms/ios/qiosmenu.mm | 9 | ||||
-rw-r--r-- | src/plugins/platforms/ios/qiostextresponder.h | 4 | ||||
-rw-r--r-- | src/plugins/platforms/ios/qiostextresponder.mm | 62 | ||||
-rw-r--r-- | src/plugins/platforms/ios/qiosviewcontroller.mm | 51 | ||||
-rw-r--r-- | src/plugins/platforms/ios/qioswindow.mm | 21 | ||||
-rw-r--r-- | src/plugins/platforms/ios/quiview.h | 1 | ||||
-rw-r--r-- | src/plugins/platforms/ios/quiview.mm | 31 |
11 files changed, 198 insertions, 38 deletions
diff --git a/src/plugins/platforms/ios/qiosfiledialog.mm b/src/plugins/platforms/ios/qiosfiledialog.mm index 0e12da9d57..7c32784e9d 100644 --- a/src/plugins/platforms/ios/qiosfiledialog.mm +++ b/src/plugins/platforms/ios/qiosfiledialog.mm @@ -95,8 +95,10 @@ bool QIOSFileDialog::show(Qt::WindowFlags windowFlags, Qt::WindowModality window Q_UNUSED(windowFlags); Q_UNUSED(windowModality); + bool acceptOpen = options()->acceptMode() == QFileDialogOptions::AcceptOpen; QString directory = options()->initialDirectory().toLocalFile(); - if (directory.startsWith(QLatin1String("assets-library:"))) { + + if (acceptOpen && directory.startsWith(QLatin1String("assets-library:"))) { m_viewController = [[QIOSImagePickerController alloc] initWithQIOSFileDialog:this]; UIWindow *window = parent ? reinterpret_cast<UIView *>(parent->winId()).window : [UIApplication sharedApplication].keyWindow; diff --git a/src/plugins/platforms/ios/qiosinputcontext.mm b/src/plugins/platforms/ios/qiosinputcontext.mm index a558ebe1f7..2ebd87f29c 100644 --- a/src/plugins/platforms/ios/qiosinputcontext.mm +++ b/src/plugins/platforms/ios/qiosinputcontext.mm @@ -540,6 +540,9 @@ void QIOSInputContext::setFocusObject(QObject *focusObject) qImDebug() << "clearing focus object" << focusObject << "as hide-keyboard gesture is active"; clearCurrentFocusObject(); return; + } else if (focusObject == m_imeState.focusObject) { + qImDebug() << "same focus object as last update, skipping reset"; + return; } reset(); @@ -575,37 +578,30 @@ void QIOSInputContext::update(Qt::InputMethodQueries updatedProperties) // Mask for properties that we are interested in and see if any of them changed updatedProperties &= (Qt::ImEnabled | Qt::ImHints | Qt::ImQueryInput | Qt::ImPlatformData); - if (updatedProperties & Qt::ImEnabled) { - // Switching on and off input-methods needs a re-fresh of hints and platform - // data when we turn them on again, as the IM state we have may have been - // invalidated when IM was switched off. We could defer this until we know - // if IM was turned on, to limit the extra query parameters, but for simplicity - // we always do the update. - updatedProperties |= (Qt::ImHints | Qt::ImPlatformData); - } - qImDebug() << "fw =" << qApp->focusWindow() << "fo =" << qApp->focusObject(); + // Perform update first, so we can trust the value of inputMethodAccepted() Qt::InputMethodQueries changedProperties = m_imeState.update(updatedProperties); - if (m_textResponder && changedProperties & (Qt::ImHints | Qt::ImPlatformData)) { - qImDebug() << "current IM state requires new text responder"; - [m_textResponder autorelease]; - m_textResponder = 0; - } if (inputMethodAccepted()) { - if (!m_textResponder) { + if (!m_textResponder || [m_textResponder needsKeyboardReconfigure:changedProperties]) { qImDebug() << "creating new text responder"; + [m_textResponder autorelease]; m_textResponder = [[QIOSTextInputResponder alloc] initWithInputContext:this]; + } else { + qImDebug() << "no need to reconfigure keyboard, just notifying input delegate"; + [m_textResponder notifyInputDelegate:changedProperties]; } - if (![m_textResponder isFirstResponder]) - [m_textResponder becomeFirstResponder]; - [m_textResponder notifyInputDelegate:changedProperties]; + if (![m_textResponder isFirstResponder]) { + qImDebug() << "IM enabled, making text responder first responder"; + [m_textResponder becomeFirstResponder]; + } if (changedProperties & Qt::ImCursorRectangle) scrollToCursor(); } else if ([m_textResponder isFirstResponder]) { + qImDebug() << "IM not enabled, resigning text responder as first responder"; [m_textResponder resignFirstResponder]; } } @@ -635,6 +631,8 @@ bool QIOSInputContext::inputMethodAccepted() const */ void QIOSInputContext::reset() { + qImDebug() << "updating Qt::ImQueryAll and unmarking text"; + update(Qt::ImQueryAll); [m_textResponder setMarkedText:@"" selectedRange:NSMakeRange(0, 0)]; @@ -651,6 +649,8 @@ void QIOSInputContext::reset() */ void QIOSInputContext::commit() { + qImDebug() << "unmarking text"; + [m_textResponder unmarkText]; [m_textResponder notifyInputDelegate:Qt::ImSurroundingText]; } diff --git a/src/plugins/platforms/ios/qiosintegration.h b/src/plugins/platforms/ios/qiosintegration.h index c22c43e455..0fe318dce7 100644 --- a/src/plugins/platforms/ios/qiosintegration.h +++ b/src/plugins/platforms/ios/qiosintegration.h @@ -48,6 +48,7 @@ class QIOSServices; class QIOSIntegration : public QPlatformNativeInterface, public QPlatformIntegration { Q_OBJECT + Q_PROPERTY(bool debugWindowManagement READ debugWindowManagement WRITE setDebugWindowManagement); public: QIOSIntegration(); @@ -87,6 +88,9 @@ public: void *nativeResourceForWindow(const QByteArray &resource, QWindow *window); + void setDebugWindowManagement(bool); + bool debugWindowManagement() const; + private: QPlatformFontDatabase *m_fontDatabase; QPlatformClipboard *m_clipboard; @@ -96,6 +100,8 @@ private: QIOSServices *m_platformServices; mutable QPlatformAccessibility *m_accessibility; QIOSFileEngineFactory m_fileEngineFactory; + + bool m_debugWindowManagement; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/ios/qiosintegration.mm b/src/plugins/platforms/ios/qiosintegration.mm index fcfd6c7cc8..e395c832f1 100644 --- a/src/plugins/platforms/ios/qiosintegration.mm +++ b/src/plugins/platforms/ios/qiosintegration.mm @@ -67,6 +67,7 @@ QIOSIntegration::QIOSIntegration() , m_inputContext(0) , m_platformServices(new QIOSServices) , m_accessibility(0) + , m_debugWindowManagement(false) { if (![UIApplication sharedApplication]) { qFatal("Error: You are creating QApplication before calling UIApplicationMain.\n" \ @@ -249,6 +250,16 @@ void *QIOSIntegration::nativeResourceForWindow(const QByteArray &resource, QWind return 0; } +void QIOSIntegration::setDebugWindowManagement(bool enabled) +{ + m_debugWindowManagement = enabled; +} + +bool QIOSIntegration::debugWindowManagement() const +{ + return m_debugWindowManagement; +} + // --------------------------------------------------------- #include "moc_qiosintegration.cpp" diff --git a/src/plugins/platforms/ios/qiosmenu.mm b/src/plugins/platforms/ios/qiosmenu.mm index 992668805f..dfab835579 100644 --- a/src/plugins/platforms/ios/qiosmenu.mm +++ b/src/plugins/platforms/ios/qiosmenu.mm @@ -128,7 +128,8 @@ static NSString *const kSelectorPrefix = @"_qtMenuItem_"; if (m_selectedRow == -1) m_selectedRow = 0; - self.toolbar = [[[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, 100, 44)] autorelease]; + self.toolbar = [[[UIToolbar alloc] init] autorelease]; + self.toolbar.frame.size = [self.toolbar sizeThatFits:self.bounds.size]; self.toolbar.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; UIBarButtonItem *spaceButton = [[[UIBarButtonItem alloc] @@ -201,7 +202,6 @@ static NSString *const kSelectorPrefix = @"_qtMenuItem_"; - (void)closeMenu { - [self listenForKeyboardWillHideNotification:NO]; if (!m_visibleMenuItems.isEmpty()) QIOSMenu::currentMenu()->handleItemSelected(m_visibleMenuItems.at(m_selectedRow)); else @@ -210,7 +210,6 @@ static NSString *const kSelectorPrefix = @"_qtMenuItem_"; - (void)cancelMenu { - [self listenForKeyboardWillHideNotification:NO]; QIOSMenu::currentMenu()->dismiss(); } @@ -466,12 +465,14 @@ void QIOSMenu::toggleShowUsingUIPickerView(bool show) } else { Q_ASSERT(focusObjectWithPickerView); focusObjectWithPickerView->removeEventFilter(this); - qApp->inputMethod()->update(Qt::ImEnabled | Qt::ImPlatformData); focusObjectWithPickerView = 0; Q_ASSERT(m_pickerView); + [m_pickerView listenForKeyboardWillHideNotification:NO]; [m_pickerView release]; m_pickerView = 0; + + qApp->inputMethod()->update(Qt::ImEnabled | Qt::ImPlatformData); } } diff --git a/src/plugins/platforms/ios/qiostextresponder.h b/src/plugins/platforms/ios/qiostextresponder.h index 4cb8a9c815..96d30ea89a 100644 --- a/src/plugins/platforms/ios/qiostextresponder.h +++ b/src/plugins/platforms/ios/qiostextresponder.h @@ -34,6 +34,7 @@ #import <UIKit/UIKit.h> #include <QtCore/qstring.h> +#include <QtGui/qevent.h> class QIOSInputContext; @@ -41,12 +42,15 @@ class QIOSInputContext; { @private QIOSInputContext *m_inputContext; + QInputMethodQueryEvent *m_configuredImeState; QString m_markedText; BOOL m_inSendEventToFocusObject; BOOL m_inSelectionChange; } - (id)initWithInputContext:(QIOSInputContext *)context; +- (BOOL)needsKeyboardReconfigure:(Qt::InputMethodQueries)updatedProperties; + - (void)notifyInputDelegate:(Qt::InputMethodQueries)updatedProperties; @property(readwrite, retain) UIView *inputView; diff --git a/src/plugins/platforms/ios/qiostextresponder.mm b/src/plugins/platforms/ios/qiostextresponder.mm index f0cb021da0..5d2a675f90 100644 --- a/src/plugins/platforms/ios/qiostextresponder.mm +++ b/src/plugins/platforms/ios/qiostextresponder.mm @@ -166,8 +166,9 @@ m_inSelectionChange = NO; m_inputContext = inputContext; - QVariantMap platformData = [self imValue:Qt::ImPlatformData].toMap(); - Qt::InputMethodHints hints = Qt::InputMethodHints([self imValue:Qt::ImHints].toUInt()); + m_configuredImeState = new QInputMethodQueryEvent(m_inputContext->imeState().currentState); + QVariantMap platformData = m_configuredImeState->value(Qt::ImPlatformData).toMap(); + Qt::InputMethodHints hints = Qt::InputMethodHints(m_configuredImeState->value(Qt::ImHints).toUInt()); self.returnKeyType = platformData.value(kImePlatformDataReturnKeyType).isValid() ? UIReturnKeyType(platformData.value(kImePlatformDataReturnKeyType).toInt()) : @@ -211,9 +212,42 @@ { self.inputView = 0; self.inputAccessoryView = 0; + delete m_configuredImeState; + [super dealloc]; } +- (BOOL)needsKeyboardReconfigure:(Qt::InputMethodQueries)updatedProperties +{ + if ((updatedProperties & Qt::ImEnabled)) { + Q_ASSERT([self currentImeState:Qt::ImEnabled].toBool()); + + // When switching on input-methods we need to consider hints and platform data + // as well, as the IM state that we were based on may have been invalidated when + // IM was switched off. + + qImDebug() << "IM was turned on, we need to check hints and platform data as well"; + updatedProperties |= (Qt::ImHints | Qt::ImPlatformData); + } + + // Based on what we set up in initWithInputContext above + updatedProperties &= (Qt::ImHints | Qt::ImPlatformData); + + if (!updatedProperties) + return NO; + + for (uint i = 0; i < (sizeof(Qt::ImQueryAll) * CHAR_BIT); ++i) { + if (Qt::InputMethodQuery property = Qt::InputMethodQuery(int(updatedProperties & (1 << i)))) { + if ([self currentImeState:property] != m_configuredImeState->value(property)) { + qImDebug() << property << "has changed since text responder was configured, need reconfigure"; + return YES; + } + } + } + + return NO; +} + - (BOOL)canBecomeFirstResponder { return YES; @@ -258,7 +292,7 @@ if ([UIResponder currentFirstResponder] == [self nextResponder]) { // We have resigned the keyboard, and transferred first responder back to the parent view Q_ASSERT(!FirstResponderCandidate::currentCandidate()); - if ([self imValue:Qt::ImEnabled].toBool()) { + if ([self currentImeState:Qt::ImEnabled].toBool()) { // The current focus object expects text input, but there // is no keyboard to get input from. So we clear focus. qImDebug() << "no keyboard available, clearing focus object"; @@ -320,7 +354,7 @@ QCoreApplication::sendEvent(focusObject, &e); } -- (QVariant)imValue:(Qt::InputMethodQuery)query +- (QVariant)currentImeState:(Qt::InputMethodQuery)query { return m_inputContext->imeState().currentState.value(query); } @@ -337,7 +371,7 @@ -(UITextPosition *)endOfDocument { - int endPosition = [self imValue:Qt::ImSurroundingText].toString().length(); + int endPosition = [self currentImeState:Qt::ImSurroundingText].toString().length(); return [QUITextPosition positionWithIndex:endPosition]; } @@ -361,8 +395,8 @@ - (UITextRange *)selectedTextRange { - int cursorPos = [self imValue:Qt::ImCursorPosition].toInt(); - int anchorPos = [self imValue:Qt::ImAnchorPosition].toInt(); + int cursorPos = [self currentImeState:Qt::ImCursorPosition].toInt(); + int anchorPos = [self currentImeState:Qt::ImAnchorPosition].toInt(); return [QUITextRange rangeWithNSRange:NSMakeRange(qMin(cursorPos, anchorPos), qAbs(anchorPos - cursorPos))]; } @@ -370,7 +404,7 @@ { int s = static_cast<QUITextPosition *>([range start]).index; int e = static_cast<QUITextPosition *>([range end]).index; - return [self imValue:Qt::ImSurroundingText].toString().mid(s, e - s).toNSString(); + return [self currentImeState:Qt::ImSurroundingText].toString().mid(s, e - s).toNSString(); } - (void)setMarkedText:(NSString *)markedText selectedRange:(NSRange)selectedRange @@ -482,8 +516,8 @@ if (!m_markedText.isEmpty()) return CGRectZero; - int cursorPos = [self imValue:Qt::ImCursorPosition].toInt(); - int anchorPos = [self imValue:Qt::ImAnchorPosition].toInt(); + int cursorPos = [self currentImeState:Qt::ImCursorPosition].toInt(); + int anchorPos = [self currentImeState:Qt::ImAnchorPosition].toInt(); NSRange r = static_cast<QUITextRange*>(range).range; QList<QInputMethodEvent::Attribute> attrs; @@ -547,7 +581,7 @@ int p = static_cast<QUITextPosition *>(position).index; if (direction == UITextLayoutDirectionLeft) return [QUITextRange rangeWithNSRange:NSMakeRange(0, p)]; - int l = [self imValue:Qt::ImSurroundingText].toString().length(); + int l = [self currentImeState:Qt::ImSurroundingText].toString().length(); return [QUITextRange rangeWithNSRange:NSMakeRange(p, l - p)]; } @@ -555,7 +589,7 @@ { // No API in Qt for determining this. Use sensible default instead: Q_UNUSED(point); - return [QUITextPosition positionWithIndex:[self imValue:Qt::ImCursorPosition].toInt()]; + return [QUITextPosition positionWithIndex:[self currentImeState:Qt::ImCursorPosition].toInt()]; } - (UITextPosition *)closestPositionToPoint:(CGPoint)point withinRange:(UITextRange *)range @@ -563,14 +597,14 @@ // No API in Qt for determining this. Use sensible default instead: Q_UNUSED(point); Q_UNUSED(range); - return [QUITextPosition positionWithIndex:[self imValue:Qt::ImCursorPosition].toInt()]; + return [QUITextPosition positionWithIndex:[self currentImeState:Qt::ImCursorPosition].toInt()]; } - (UITextRange *)characterRangeAtPoint:(CGPoint)point { // No API in Qt for determining this. Use sensible default instead: Q_UNUSED(point); - return [QUITextRange rangeWithNSRange:NSMakeRange([self imValue:Qt::ImCursorPosition].toInt(), 0)]; + return [QUITextRange rangeWithNSRange:NSMakeRange([self currentImeState:Qt::ImCursorPosition].toInt(), 0)]; } - (void)setMarkedTextStyle:(NSDictionary *)style diff --git a/src/plugins/platforms/ios/qiosviewcontroller.mm b/src/plugins/platforms/ios/qiosviewcontroller.mm index d144b419aa..9abd105d94 100644 --- a/src/plugins/platforms/ios/qiosviewcontroller.mm +++ b/src/plugins/platforms/ios/qiosviewcontroller.mm @@ -41,6 +41,7 @@ #include <QtGui/private/qwindow_p.h> +#include "qiosintegration.h" #include "qiosscreen.h" #include "qiosglobal.h" #include "qioswindow.h" @@ -64,6 +65,56 @@ @implementation QIOSDesktopManagerView +- (id)init +{ + if (!(self = [super init])) + return nil; + + QIOSIntegration *iosIntegration = QIOSIntegration::instance(); + if (iosIntegration && iosIntegration->debugWindowManagement()) { + static UIImage *gridPattern = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + CGFloat dimension = 100.f; + + UIGraphicsBeginImageContextWithOptions(CGSizeMake(dimension, dimension), YES, 0.0f); + CGContextRef context = UIGraphicsGetCurrentContext(); + + CGContextTranslateCTM(context, -0.5, -0.5); + + #define gridColorWithBrightness(br) \ + [UIColor colorWithHue:0.6 saturation:0.0 brightness:br alpha:1.0].CGColor + + CGContextSetFillColorWithColor(context, gridColorWithBrightness(0.05)); + CGContextFillRect(context, CGRectMake(0, 0, dimension, dimension)); + + CGFloat gridLines[][2] = { { 10, 0.1 }, { 20, 0.2 }, { 100, 0.3 } }; + for (size_t l = 0; l < sizeof(gridLines) / sizeof(gridLines[0]); ++l) { + CGFloat step = gridLines[l][0]; + for (int c = step; c <= dimension; c += step) { + CGContextMoveToPoint(context, c, 0); + CGContextAddLineToPoint(context, c, dimension); + CGContextMoveToPoint(context, 0, c); + CGContextAddLineToPoint(context, dimension, c); + } + + CGFloat brightness = gridLines[l][1]; + CGContextSetStrokeColorWithColor(context, gridColorWithBrightness(brightness)); + CGContextStrokePath(context); + } + + gridPattern = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + [gridPattern retain]; + }); + + self.backgroundColor = [UIColor colorWithPatternImage:gridPattern]; + } + + return self; +} + - (void)didAddSubview:(UIView *)subview { Q_UNUSED(subview); diff --git a/src/plugins/platforms/ios/qioswindow.mm b/src/plugins/platforms/ios/qioswindow.mm index fd04ecf474..95a4c28246 100644 --- a/src/plugins/platforms/ios/qioswindow.mm +++ b/src/plugins/platforms/ios/qioswindow.mm @@ -120,7 +120,7 @@ void QIOSWindow::setVisible(bool visible) if (visible && shouldAutoActivateWindow()) { requestActivateWindow(); - } else if (!visible && qGuiApp->focusWindow() == window()) { + } else if (!visible && [m_view isActiveWindow]) { // Our window was active/focus window but now hidden, so relinquish // focus to the next possible window in the stack. NSArray *subviews = m_view.viewController.view.subviews; @@ -206,6 +206,25 @@ void QIOSWindow::applyGeometry(const QRect &rect) bool QIOSWindow::isExposed() const { + // Note: At startup of an iOS app it will enter UIApplicationStateInactive + // while showing the launch screen, and once the application returns from + // applicationDidFinishLaunching it will hide the launch screen and enter + // UIApplicationStateActive. Technically, a window is not exposed until + // it's actually visible on screen, and Apple also documents that "Apps + // that use OpenGL ES for drawing must not use didFinishLaunching to + // prepare their drawing environment. Instead, defer any OpenGL ES + // drawing calls to applicationDidBecomeActive". Unfortunately, if we + // wait until the applicationState reaches ApplicationActive to signal + // that the window is exposed, we get a lag between hiding the launch + // screen and blitting the first pixels of the application, as Qt + // spends some time drawing those pixels in response to the expose. + // In practice there doesn't seem to be any issues starting GL setup + // and drawing from within applicationDidFinishLaunching, and this is + // also the recommended approach for other 3rd party GL toolkits on iOS, + // so we 'cheat', and report that a window is exposed even if the app + // is in UIApplicationStateInactive, so that the startup transition + // between the launch screen and the application content is smooth. + return qApp->applicationState() > Qt::ApplicationHidden && window()->isVisible() && !window()->geometry().isEmpty(); } diff --git a/src/plugins/platforms/ios/quiview.h b/src/plugins/platforms/ios/quiview.h index 065c52f881..dfe0927b00 100644 --- a/src/plugins/platforms/ios/quiview.h +++ b/src/plugins/platforms/ios/quiview.h @@ -56,6 +56,7 @@ class QIOSWindow; - (id)initWithQIOSWindow:(QIOSWindow *)window; - (void)sendUpdatedExposeEvent; +- (BOOL)isActiveWindow; @end @interface QUIView (Accessibility) diff --git a/src/plugins/platforms/ios/quiview.mm b/src/plugins/platforms/ios/quiview.mm index f36cbaf685..8be3515cb5 100644 --- a/src/plugins/platforms/ios/quiview.mm +++ b/src/plugins/platforms/ios/quiview.mm @@ -73,6 +73,20 @@ self.hidden = YES; self.multipleTouchEnabled = YES; + + if (QIOSIntegration::instance()->debugWindowManagement()) { + static CGFloat hue = 0.0; + CGFloat lastHue = hue; + for (CGFloat diff = 0; diff < 0.1 || diff > 0.9; diff = fabsf(hue - lastHue)) + hue = drand48(); + + #define colorWithBrightness(br) \ + [UIColor colorWithHue:hue saturation:0.5 brightness:br alpha:1.0].CGColor + + self.layer.backgroundColor = colorWithBrightness(0.5); + self.layer.borderColor = colorWithBrightness(1.0); + self.layer.borderWidth = 1.0; + } } return self; @@ -247,6 +261,23 @@ return YES; } +- (BOOL)isActiveWindow +{ + // Normally this is determined exclusivly by being firstResponder, but + // since we employ a separate first responder for text input we need to + // handle both cases as this view being the active Qt window. + + if ([self isFirstResponder]) + return YES; + + UIResponder *firstResponder = [UIResponder currentFirstResponder]; + if ([firstResponder isKindOfClass:[QIOSTextInputResponder class]] + && [firstResponder nextResponder] == self) + return YES; + + return NO; +} + // ------------------------------------------------------------------------- |