diff options
author | Simon Hausmann <simon.hausmann@theqtcompany.com> | 2014-12-10 07:58:06 +0100 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@theqtcompany.com> | 2014-12-10 07:58:06 +0100 |
commit | 015002fec9abff6a4c1bb3fa4b9de87279a079c3 (patch) | |
tree | ad93af535a503d0a49d6c6367e990a8fbca163d3 /src/plugins/platforms | |
parent | f1e00262321cc8daa3c7506153653453e2779886 (diff) | |
parent | b9547af45ea2bbbc634722c1ef41afdb54216ce2 (diff) |
Merge remote-tracking branch 'origin/5.4' into dev
Conflicts:
doc/global/template/style/online.css
mkspecs/android-g++/qmake.conf
Change-Id: Ib39ea7bd42f5ae12e82a3bc59a66787a16bdfc61
Diffstat (limited to 'src/plugins/platforms')
-rw-r--r-- | src/plugins/platforms/android/androidjniinput.cpp | 43 | ||||
-rw-r--r-- | src/plugins/platforms/android/qandroidinputcontext.cpp | 4 | ||||
-rw-r--r-- | src/plugins/platforms/android/qandroidplatformscreen.cpp | 15 | ||||
-rw-r--r-- | src/plugins/platforms/android/qandroidplatformtheme.cpp | 3 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoamenuloader.mm | 6 | ||||
-rw-r--r-- | src/plugins/platforms/ios/qiosinputcontext.h | 26 | ||||
-rw-r--r-- | src/plugins/platforms/ios/qiosinputcontext.mm | 300 | ||||
-rw-r--r-- | src/plugins/platforms/ios/qiosintegration.mm | 8 | ||||
-rw-r--r-- | src/plugins/platforms/ios/qiosscreen.h | 1 | ||||
-rw-r--r-- | src/plugins/platforms/ios/qiosscreen.mm | 5 | ||||
-rw-r--r-- | src/plugins/platforms/windows/qwindowscontext.cpp | 3 | ||||
-rw-r--r-- | src/plugins/platforms/windows/qwindowscursor.cpp | 15 | ||||
-rw-r--r-- | src/plugins/platforms/windows/qwindowsfontdatabase.cpp | 6 | ||||
-rw-r--r-- | src/plugins/platforms/windows/qwindowswindow.cpp | 23 | ||||
-rw-r--r-- | src/plugins/platforms/windows/qwindowswindow.h | 2 | ||||
-rw-r--r-- | src/plugins/platforms/winrt/qwinrtscreen.cpp | 23 |
16 files changed, 315 insertions, 168 deletions
diff --git a/src/plugins/platforms/android/androidjniinput.cpp b/src/plugins/platforms/android/androidjniinput.cpp index 62da60ab92..59125ac0d2 100644 --- a/src/plugins/platforms/android/androidjniinput.cpp +++ b/src/plugins/platforms/android/androidjniinput.cpp @@ -638,6 +638,25 @@ namespace QtAndroidInput } } + static Qt::KeyboardModifiers mapAndroidModifiers(jint modifiers) + { + Qt::KeyboardModifiers qmodifiers; + + if (modifiers & 0x00000001) // META_SHIFT_ON + qmodifiers |= Qt::ShiftModifier; + + if (modifiers & 0x00000002) // META_ALT_ON + qmodifiers |= Qt::AltModifier; + + if (modifiers & 0x00000004) // META_SYM_ON + qmodifiers |= Qt::MetaModifier; + + if (modifiers & 0x00001000) // META_CTRL_ON + qmodifiers |= Qt::ControlModifier; + + return qmodifiers; + } + // maps 0 to the empty string, and anything else to a single-character string static inline QString toString(jint unicode) { @@ -646,40 +665,20 @@ namespace QtAndroidInput static void keyDown(JNIEnv */*env*/, jobject /*thiz*/, jint key, jint unicode, jint modifier) { - Qt::KeyboardModifiers modifiers; - if (modifier & 1) - modifiers |= Qt::ShiftModifier; - - if (modifier & 2) - modifiers |= Qt::AltModifier; - - if (modifier & 4) - modifiers |= Qt::MetaModifier; - QWindowSystemInterface::handleKeyEvent(0, QEvent::KeyPress, mapAndroidKey(key), - modifiers, + mapAndroidModifiers(modifier), toString(unicode), false); } static void keyUp(JNIEnv */*env*/, jobject /*thiz*/, jint key, jint unicode, jint modifier) { - Qt::KeyboardModifiers modifiers; - if (modifier & 1) - modifiers |= Qt::ShiftModifier; - - if (modifier & 2) - modifiers |= Qt::AltModifier; - - if (modifier & 4) - modifiers |= Qt::MetaModifier; - QWindowSystemInterface::handleKeyEvent(0, QEvent::KeyRelease, mapAndroidKey(key), - modifiers, + mapAndroidModifiers(modifier), toString(unicode), false); } diff --git a/src/plugins/platforms/android/qandroidinputcontext.cpp b/src/plugins/platforms/android/qandroidinputcontext.cpp index a3848c9c2b..7e81735de9 100644 --- a/src/plugins/platforms/android/qandroidinputcontext.cpp +++ b/src/plugins/platforms/android/qandroidinputcontext.cpp @@ -611,6 +611,9 @@ jboolean QAndroidInputContext::endBatchEdit() */ jboolean QAndroidInputContext::commitText(const QString &text, jint newCursorPosition) { + bool updateSelectionWasBlocked = m_blockUpdateSelection; + m_blockUpdateSelection = true; + QInputMethodEvent event; event.setCommitString(text); sendInputMethodEventThreadSafe(&event); @@ -630,6 +633,7 @@ jboolean QAndroidInputContext::commitText(const QString &text, jint newCursorPos newLocalPos, 0, QVariant())); } } + m_blockUpdateSelection = updateSelectionWasBlocked; updateCursorPosition(); return JNI_TRUE; diff --git a/src/plugins/platforms/android/qandroidplatformscreen.cpp b/src/plugins/platforms/android/qandroidplatformscreen.cpp index 8a07735e5f..940add3a38 100644 --- a/src/plugins/platforms/android/qandroidplatformscreen.cpp +++ b/src/plugins/platforms/android/qandroidplatformscreen.cpp @@ -284,9 +284,9 @@ void QAndroidPlatformScreen::doRedraw() if (m_dirtyRect.isEmpty()) return; - // Stop if there no visible raster windows. This is important because if we only have - // RasterGLSurface windows that have renderToTexture children (i.e. they need the - // OpenGL path) then we must bail out right now. + // Stop if there are no visible raster windows. If we only have RasterGLSurface + // windows that have renderToTexture children (i.e. they need the OpenGL path) then + // we do not need an overlay surface. bool hasVisibleRasterWindows = false; foreach (QAndroidPlatformWindow *window, m_windowStack) { if (window->window()->isVisible() && window->isRaster() && !qt_window_private(window->window())->compositing) { @@ -294,9 +294,13 @@ void QAndroidPlatformScreen::doRedraw() break; } } - if (!hasVisibleRasterWindows) + if (!hasVisibleRasterWindows) { + if (m_id != -1) { + QtAndroid::destroySurface(m_id); + m_id = -1; + } return; - + } QMutexLocker lock(&m_surfaceMutex); if (m_id == -1 && m_rasterSurfaces) { m_id = QtAndroid::createSurface(this, m_availableGeometry, true, m_depth); @@ -339,6 +343,7 @@ void QAndroidPlatformScreen::doRedraw() QRegion visibleRegion(m_dirtyRect); foreach (QAndroidPlatformWindow *window, m_windowStack) { if (!window->window()->isVisible() + || qt_window_private(window->window())->compositing || !window->isRaster()) continue; diff --git a/src/plugins/platforms/android/qandroidplatformtheme.cpp b/src/plugins/platforms/android/qandroidplatformtheme.cpp index ef7093942c..e729309ad9 100644 --- a/src/plugins/platforms/android/qandroidplatformtheme.cpp +++ b/src/plugins/platforms/android/qandroidplatformtheme.cpp @@ -278,7 +278,8 @@ static std::shared_ptr<AndroidStyle> loadAndroidStyle(QPalette *defaultPalette) const int pt = paletteType(key); if (pt > -1 || !qtClassName.isEmpty()) { // Extract palette information - QPalette palette; + QPalette palette = *defaultPalette; + attributeIterator = item.find(QLatin1String("defaultTextColorPrimary")); if (attributeIterator != item.constEnd()) palette.setColor(QPalette::WindowText, QRgb(int(attributeIterator.value().toDouble()))); diff --git a/src/plugins/platforms/cocoa/qcocoamenuloader.mm b/src/plugins/platforms/cocoa/qcocoamenuloader.mm index 9340e945fb..0075dea805 100644 --- a/src/plugins/platforms/cocoa/qcocoamenuloader.mm +++ b/src/plugins/platforms/cocoa/qcocoamenuloader.mm @@ -87,7 +87,11 @@ void qt_mac_loadMenuNib(QCocoaMenuLoader *qtMenuLoader) return; } foreach (const QFileInfo &file, nibResource.entryInfoList()) { - QFile::copy(file.absoluteFilePath(), nibDir + QLatin1String("/") + file.fileName()); + QFileInfo destinationFile(nibDir + QLatin1String("/") + file.fileName()); + if (destinationFile.exists() && destinationFile.size() != file.size()) + QFile::remove(destinationFile.absoluteFilePath()); + + QFile::copy(file.absoluteFilePath(), destinationFile.absoluteFilePath()); } // Load and instantiate nib file from temp diff --git a/src/plugins/platforms/ios/qiosinputcontext.h b/src/plugins/platforms/ios/qiosinputcontext.h index b4ff695f1a..863e503c3b 100644 --- a/src/plugins/platforms/ios/qiosinputcontext.h +++ b/src/plugins/platforms/ios/qiosinputcontext.h @@ -48,6 +48,22 @@ QT_BEGIN_NAMESPACE @class QIOSKeyboardListener; @class QIOSTextInputResponder; +@protocol KeyboardState; + +struct KeyboardState +{ + KeyboardState() : + keyboardVisible(false), keyboardAnimating(false), + animationDuration(0), animationCurve(UIViewAnimationCurve(-1)) + {} + + bool keyboardVisible; + bool keyboardAnimating; + QRectF keyboardRect; + CGRect keyboardEndRect; + NSTimeInterval animationDuration; + UIViewAnimationCurve animationCurve; +}; struct ImeState { @@ -69,6 +85,7 @@ public: void hideInputPanel() Q_DECL_OVERRIDE; bool isInputPanelVisible() const Q_DECL_OVERRIDE; + bool isAnimating() const Q_DECL_OVERRIDE; QRectF keyboardRect() const Q_DECL_OVERRIDE; void update(Qt::InputMethodQueries) Q_DECL_OVERRIDE; @@ -84,14 +101,21 @@ public: void scrollToCursor(); void scroll(int y); + void updateKeyboardState(NSNotification *notification = 0); + const ImeState &imeState() { return m_imeState; }; + const KeyboardState &keyboardState() { return m_keyboardState; }; + bool inputMethodAccepted() const; static QIOSInputContext *instance(); private: - QIOSKeyboardListener *m_keyboardListener; + UIView* scrollableRootView(); + + QIOSKeyboardListener *m_keyboardHideGesture; QIOSTextInputResponder *m_textResponder; + KeyboardState m_keyboardState; ImeState m_imeState; }; diff --git a/src/plugins/platforms/ios/qiosinputcontext.mm b/src/plugins/platforms/ios/qiosinputcontext.mm index e417e9a1fb..fe9ee18155 100644 --- a/src/plugins/platforms/ios/qiosinputcontext.mm +++ b/src/plugins/platforms/ios/qiosinputcontext.mm @@ -45,6 +45,7 @@ #include "qiosglobal.h" #include "qiosintegration.h" +#include "qiosscreen.h" #include "qiostextresponder.h" #include "qiosviewcontroller.h" #include "qioswindow.h" @@ -64,15 +65,8 @@ static QUIView *focusView() // ------------------------------------------------------------------------- @interface QIOSKeyboardListener : UIGestureRecognizer <UIGestureRecognizerDelegate> { -@public + @private QIOSInputContext *m_context; - BOOL m_keyboardVisible; - BOOL m_keyboardVisibleAndDocked; - QRectF m_keyboardRect; - CGRect m_keyboardEndRect; - NSTimeInterval m_duration; - UIViewAnimationCurve m_curve; - UIViewController *m_viewController; } @end @@ -80,87 +74,51 @@ static QUIView *focusView() - (id)initWithQIOSInputContext:(QIOSInputContext *)context { - self = [super initWithTarget:self action:@selector(gestureStateChanged:)]; - if (self) { + id originalSelf = self; + if (self = [super initWithTarget:self action:@selector(gestureStateChanged:)]) { + Q_ASSERT(self == originalSelf); + m_context = context; - m_keyboardVisible = NO; - m_keyboardVisibleAndDocked = NO; - m_duration = 0; - m_curve = UIViewAnimationCurveEaseOut; - m_viewController = 0; - - if (isQtApplication()) { - // Get the root view controller that is on the same screen as the keyboard: - for (UIWindow *uiWindow in [[UIApplication sharedApplication] windows]) { - if (uiWindow.screen == [UIScreen mainScreen]) { - m_viewController = [uiWindow.rootViewController retain]; - break; - } - } - Q_ASSERT(m_viewController); - - // Attach 'hide keyboard' gesture to the window, but keep it disabled when the - // keyboard is not visible. - self.enabled = NO; - self.cancelsTouchesInView = NO; - self.delaysTouchesEnded = NO; - [m_viewController.view.window addGestureRecognizer:self]; - } - [[NSNotificationCenter defaultCenter] - addObserver:self + // UIGestureRecognizer + self.enabled = NO; + self.cancelsTouchesInView = NO; + self.delaysTouchesEnded = NO; + + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + + [notificationCenter addObserver:self selector:@selector(keyboardWillShow:) - name:@"UIKeyboardWillShowNotification" object:nil]; - [[NSNotificationCenter defaultCenter] - addObserver:self + name:UIKeyboardWillShowNotification object:nil]; + [notificationCenter addObserver:self + selector:@selector(keyboardWillOrDidChange:) + name:UIKeyboardDidShowNotification object:nil]; + [notificationCenter addObserver:self selector:@selector(keyboardWillHide:) - name:@"UIKeyboardWillHideNotification" object:nil]; - [[NSNotificationCenter defaultCenter] - addObserver:self + name:UIKeyboardWillHideNotification object:nil]; + [notificationCenter addObserver:self + selector:@selector(keyboardWillOrDidChange:) + name:UIKeyboardDidHideNotification object:nil]; + [notificationCenter addObserver:self selector:@selector(keyboardDidChangeFrame:) - name:@"UIKeyboardDidChangeFrameNotification" object:nil]; + name:UIKeyboardDidChangeFrameNotification object:nil]; } + return self; } -- (void) dealloc +- (void)dealloc { - [m_viewController.view.window removeGestureRecognizer:self]; - [m_viewController release]; + [[NSNotificationCenter defaultCenter] removeObserver:self]; - [[NSNotificationCenter defaultCenter] - removeObserver:self - name:@"UIKeyboardWillShowNotification" object:nil]; - [[NSNotificationCenter defaultCenter] - removeObserver:self - name:@"UIKeyboardWillHideNotification" object:nil]; - [[NSNotificationCenter defaultCenter] - removeObserver:self - name:@"UIKeyboardDidChangeFrameNotification" object:nil]; [super dealloc]; } -- (void) keyboardDidChangeFrame:(NSNotification *)notification -{ - Q_UNUSED(notification); - [self handleKeyboardRectChanged]; - - // If the keyboard was visible and docked from before, this is just a geometry - // change (normally caused by an orientation change). In that case, update scroll: - if (m_keyboardVisibleAndDocked) - m_context->scrollToCursor(); -} +// ------------------------------------------------------------------------- -- (void) keyboardWillShow:(NSNotification *)notification +- (void)keyboardWillShow:(NSNotification *)notification { - // Note that UIKeyboardWillShowNotification is only sendt when the keyboard is docked. - m_keyboardVisibleAndDocked = YES; - m_keyboardEndRect = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]; - - if (!m_duration) { - m_duration = [[notification.userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]; - m_curve = UIViewAnimationCurve([[notification.userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] integerValue]); - } + [self keyboardWillOrDidChange:notification]; UIResponder *firstResponder = [UIResponder currentFirstResponder]; if (![firstResponder isKindOfClass:[QIOSTextInputResponder class]]) @@ -172,11 +130,10 @@ static QUIView *focusView() m_context->scrollToCursor(); } -- (void) keyboardWillHide:(NSNotification *)notification +- (void)keyboardWillHide:(NSNotification *)notification { - // Note that UIKeyboardWillHideNotification is also sendt when the keyboard is undocked. - m_keyboardVisibleAndDocked = NO; - m_keyboardEndRect = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]; + [self keyboardWillOrDidChange:notification]; + if (self.state != UIGestureRecognizerStateBegan) { // Only disable the gesture if the hiding of the keyboard was not caused by it. // Otherwise we need to await the final touchEnd callback for doing some clean-up. @@ -185,29 +142,19 @@ static QUIView *focusView() m_context->scroll(0); } -- (void) handleKeyboardRectChanged +- (void)keyboardDidChangeFrame:(NSNotification *)notification { - // QInputmethod::keyboardRectangle() is documented to be in window coordinates. - // If there is no focus window, we return an empty rectangle - UIView *view = focusView(); - QRectF convertedRect = fromCGRect([view convertRect:m_keyboardEndRect fromView:nil]); - - // Set height to zero if keyboard is hidden. Otherwise the rect will not change - // when the keyboard hides on a scrolled screen (since the keyboard will already - // be at the bottom of the 'screen' in that case) - if (!m_keyboardVisibleAndDocked) - convertedRect.setHeight(0); + [self keyboardWillOrDidChange:notification]; - if (convertedRect != m_keyboardRect) { - m_keyboardRect = convertedRect; - m_context->emitKeyboardRectChanged(); - } + // If the keyboard was visible and docked from before, this is just a geometry + // change (normally caused by an orientation change). In that case, update scroll: + if (m_context->isInputPanelVisible()) + m_context->scrollToCursor(); +} - BOOL visible = CGRectIntersectsRect(m_keyboardEndRect, [UIScreen mainScreen].bounds); - if (m_keyboardVisible != visible) { - m_keyboardVisible = visible; - m_context->emitInputPanelVisibleChanged(); - } +- (void)keyboardWillOrDidChange:(NSNotification *)notification +{ + m_context->updateKeyboardState(notification); } // ------------------------------------------------------------------------- @@ -216,7 +163,7 @@ static QUIView *focusView() { [super touchesBegan:touches withEvent:event]; - Q_ASSERT(m_keyboardVisibleAndDocked); + Q_ASSERT(m_context->isInputPanelVisible()); if ([touches count] != 1) self.state = UIGestureRecognizerStateFailed; @@ -229,8 +176,8 @@ static QUIView *focusView() if (self.state != UIGestureRecognizerStatePossible) return; - CGPoint touchPoint = [[touches anyObject] locationInView:m_viewController.view.window]; - if (CGRectContainsPoint(m_keyboardEndRect, touchPoint)) + CGPoint touchPoint = [[touches anyObject] locationInView:self.view]; + if (CGRectContainsPoint(m_context->keyboardState().keyboardEndRect, touchPoint)) self.state = UIGestureRecognizerStateBegan; } @@ -280,7 +227,7 @@ static QUIView *focusView() { [super reset]; - if (!m_keyboardVisibleAndDocked) { + if (!m_context->isInputPanelVisible()) { qImDebug() << "keyboard was hidden, disabling hide-keyboard gesture"; self.enabled = NO; } else { @@ -328,23 +275,25 @@ QIOSInputContext *QIOSInputContext::instance() QIOSInputContext::QIOSInputContext() : QPlatformInputContext() - , m_keyboardListener([[QIOSKeyboardListener alloc] initWithQIOSInputContext:this]) + , m_keyboardHideGesture([[QIOSKeyboardListener alloc] initWithQIOSInputContext:this]) , m_textResponder(0) { - if (isQtApplication()) + if (isQtApplication()) { + QIOSScreen *iosScreen = static_cast<QIOSScreen*>(QGuiApplication::primaryScreen()->handle()); + [iosScreen->uiWindow() addGestureRecognizer:m_keyboardHideGesture]; + connect(qGuiApp->inputMethod(), &QInputMethod::cursorRectangleChanged, this, &QIOSInputContext::cursorRectangleChanged); + } + connect(qGuiApp, &QGuiApplication::focusWindowChanged, this, &QIOSInputContext::focusWindowChanged); } QIOSInputContext::~QIOSInputContext() { - [m_keyboardListener release]; - [m_textResponder release]; -} + [m_keyboardHideGesture.view removeGestureRecognizer:m_keyboardHideGesture]; + [m_keyboardHideGesture release]; -QRectF QIOSInputContext::keyboardRect() const -{ - return m_keyboardListener->m_keyboardRect; + [m_textResponder release]; } void QIOSInputContext::showInputPanel() @@ -375,14 +324,85 @@ void QIOSInputContext::clearCurrentFocusObject() static_cast<QWindowPrivate *>(QObjectPrivate::get(focusWindow))->clearFocusObject(); } +// ------------------------------------------------------------------------- + +void QIOSInputContext::updateKeyboardState(NSNotification *notification) +{ + static CGRect currentKeyboardRect = CGRectZero; + + KeyboardState previousState = m_keyboardState; + + if (notification) { + NSDictionary *userInfo = [notification userInfo]; + + CGRect frameBegin = [[userInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue]; + CGRect frameEnd = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]; + + bool atEndOfKeyboardTransition = [notification.name rangeOfString:@"Did"].location != NSNotFound; + + currentKeyboardRect = atEndOfKeyboardTransition ? frameEnd : frameBegin; + + // The isInputPanelVisible() property is based on whether or not the virtual keyboard + // is visible on screen, and does not follow the logic of the iOS WillShow and WillHide + // notifications which are not emitted for undocked keyboards, and are buggy when dealing + // with input-accesosory-views. The reason for using frameEnd here (the future state), + // instead of the current state reflected in frameBegin, is that QInputMethod::isVisible() + // is documented to reflect the future state in the case of animated transitions. + m_keyboardState.keyboardVisible = CGRectIntersectsRect(frameEnd, [UIScreen mainScreen].bounds); + + // Used for auto-scroller, and will be used for animation-signal in the future + m_keyboardState.keyboardEndRect = frameEnd; + + if (m_keyboardState.animationCurve < 0) { + // We only set the animation curve the first time it has a valid value, since iOS will sometimes report + // an invalid animation curve even if the keyboard is animating, and we don't want to overwrite the + // curve in that case. + m_keyboardState.animationCurve = UIViewAnimationCurve([[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] integerValue]); + } + + m_keyboardState.animationDuration = [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]; + m_keyboardState.keyboardAnimating = m_keyboardState.animationDuration > 0 && !atEndOfKeyboardTransition; + + qImDebug() << qPrintable(QString::fromNSString(notification.name)) << "from" << fromCGRect(frameBegin) << "to" << fromCGRect(frameEnd) + << "(curve =" << m_keyboardState.animationCurve << "duration =" << m_keyboardState.animationDuration << "s)"; + } else { + qImDebug() << "No notification to update keyboard state based on, just updating keyboard rect"; + } + + if (!focusView() || CGRectIsEmpty(currentKeyboardRect)) + m_keyboardState.keyboardRect = QRectF(); + else // QInputmethod::keyboardRectangle() is documented to be in window coordinates. + m_keyboardState.keyboardRect = fromCGRect([focusView() convertRect:currentKeyboardRect fromView:nil]); + + // Emit for all changed properties + if (m_keyboardState.keyboardVisible != previousState.keyboardVisible) + emitInputPanelVisibleChanged(); + if (m_keyboardState.keyboardAnimating != previousState.keyboardAnimating) + emitAnimatingChanged(); + if (m_keyboardState.keyboardRect != previousState.keyboardRect) + emitKeyboardRectChanged(); +} + bool QIOSInputContext::isInputPanelVisible() const { - return m_keyboardListener->m_keyboardVisible; + return m_keyboardState.keyboardVisible; +} + +bool QIOSInputContext::isAnimating() const +{ + return m_keyboardState.keyboardAnimating; +} + +QRectF QIOSInputContext::keyboardRect() const +{ + return m_keyboardState.keyboardRect; } +// ------------------------------------------------------------------------- + void QIOSInputContext::cursorRectangleChanged() { - if (!m_keyboardListener->m_keyboardVisibleAndDocked || !qApp->focusObject()) + if (!isInputPanelVisible() || !qApp->focusObject()) return; // Check if the cursor has changed position inside the input item. Since @@ -397,44 +417,67 @@ void QIOSInputContext::cursorRectangleChanged() prevCursor = cursor; } +UIView *QIOSInputContext::scrollableRootView() +{ + if (!m_keyboardHideGesture.view) + return 0; + + UIWindow *window = static_cast<UIWindow*>(m_keyboardHideGesture.view); + if (![window.rootViewController isKindOfClass:[QIOSViewController class]]) + return 0; + + return window.rootViewController.view; +} + void QIOSInputContext::scrollToCursor() { if (!isQtApplication()) return; - if (m_keyboardListener.state == UIGestureRecognizerStatePossible && m_keyboardListener.numberOfTouches == 1) { + if (m_keyboardHideGesture.state == UIGestureRecognizerStatePossible && m_keyboardHideGesture.numberOfTouches == 1) { // Don't scroll to the cursor if the user is touching the screen and possibly // trying to trigger the hide-keyboard gesture. qImDebug() << "preventing scrolling to cursor as we're still waiting for a possible gesture"; return; } - UIView *view = m_keyboardListener->m_viewController.view; - if (view.window != focusView().window) + UIView *rootView = scrollableRootView(); + if (!rootView) + return; + + if (rootView.window != focusView().window) return; + // We only support auto-scroll for docked keyboards for now, so make sure that's the case + if (CGRectGetMaxY(m_keyboardState.keyboardEndRect) != CGRectGetMaxY([UIScreen mainScreen].bounds)) { + qImDebug() << "Keyboard not docked, ignoring request to scroll to reveal cursor"; + return; + } + const int margin = 20; QRectF translatedCursorPos = qApp->inputMethod()->cursorRectangle(); translatedCursorPos.translate(focusView().qwindow->geometry().topLeft()); - qreal keyboardY = [view convertRect:m_keyboardListener->m_keyboardEndRect fromView:nil].origin.y; + qreal keyboardY = [rootView convertRect:m_keyboardState.keyboardEndRect fromView:nil].origin.y; int statusBarY = qGuiApp->primaryScreen()->availableGeometry().y(); scroll((translatedCursorPos.bottomLeft().y() < keyboardY - margin) ? 0 - : qMin(view.bounds.size.height - keyboardY, translatedCursorPos.y() - statusBarY - margin)); + : qMin(rootView.bounds.size.height - keyboardY, translatedCursorPos.y() - statusBarY - margin)); } void QIOSInputContext::scroll(int y) { - UIView *rootView = m_keyboardListener->m_viewController.view; + UIView *rootView = scrollableRootView(); + if (!rootView) + return; CATransform3D translationTransform = CATransform3DMakeTranslation(0.0, -y, 0.0); if (CATransform3DEqualToTransform(translationTransform, rootView.layer.sublayerTransform)) return; QPointer<QIOSInputContext> self = this; - [UIView animateWithDuration:m_keyboardListener->m_duration delay:0 - options:(m_keyboardListener->m_curve << 16) | UIViewAnimationOptionBeginFromCurrentState + [UIView animateWithDuration:m_keyboardState.animationDuration delay:0 + options:(m_keyboardState.animationCurve << 16) | UIViewAnimationOptionBeginFromCurrentState animations:^{ // The sublayerTransform property of CALayer is not implicitly animated for a // layer-backed view, even inside a UIView animation block, so we need to set up @@ -463,8 +506,12 @@ void QIOSInputContext::scroll(int y) [rootView.qtViewController updateProperties]; } completion:^(BOOL){ - if (self) - [m_keyboardListener handleKeyboardRectChanged]; + if (self) { + // Scrolling the root view results in the keyboard being moved + // relative to the focus window, so we need to re-evaluate the + // keyboard rectangle. + updateKeyboardState(); + } } ]; } @@ -477,7 +524,7 @@ void QIOSInputContext::setFocusObject(QObject *focusObject) qImDebug() << "new focus object =" << focusObject; - if (m_keyboardListener.state == UIGestureRecognizerStateChanged) { + if (m_keyboardHideGesture.state == UIGestureRecognizerStateChanged) { // A new focus object may be set as part of delivering touch events to // application during the hide-keyboard gesture, but we don't want that // to result in a new object getting focus and bringing the keyboard up @@ -489,7 +536,7 @@ void QIOSInputContext::setFocusObject(QObject *focusObject) reset(); - if (m_keyboardListener->m_keyboardVisibleAndDocked) + if (isInputPanelVisible()) scrollToCursor(); } @@ -501,8 +548,11 @@ void QIOSInputContext::focusWindowChanged(QWindow *focusWindow) reset(); - [m_keyboardListener handleKeyboardRectChanged]; - if (m_keyboardListener->m_keyboardVisibleAndDocked) + // The keyboard rectangle depend on the focus window, so + // we need to re-evaluate the keyboard state. + updateKeyboardState(); + + if (isInputPanelVisible()) scrollToCursor(); } diff --git a/src/plugins/platforms/ios/qiosintegration.mm b/src/plugins/platforms/ios/qiosintegration.mm index 9a722ead37..461f160892 100644 --- a/src/plugins/platforms/ios/qiosintegration.mm +++ b/src/plugins/platforms/ios/qiosintegration.mm @@ -88,7 +88,13 @@ QIOSIntegration::QIOSIntegration() // Set current directory to app bundle folder QDir::setCurrent(QString::fromUtf8([[[NSBundle mainBundle] bundlePath] UTF8String])); - for (UIScreen *screen in [UIScreen screens]) + NSMutableArray *screens = [[[UIScreen screens] mutableCopy] autorelease]; + if (![screens containsObject:[UIScreen mainScreen]]) { + // Fallback for iOS 7.1 (QTBUG-42345) + [screens insertObject:[UIScreen mainScreen] atIndex:0]; + } + + for (UIScreen *screen in screens) addScreen(new QIOSScreen(screen)); // Depends on a primary screen being present diff --git a/src/plugins/platforms/ios/qiosscreen.h b/src/plugins/platforms/ios/qiosscreen.h index 7987ef82d5..7aa62b9190 100644 --- a/src/plugins/platforms/ios/qiosscreen.h +++ b/src/plugins/platforms/ios/qiosscreen.h @@ -63,6 +63,7 @@ public: void setOrientationUpdateMask(Qt::ScreenOrientations mask); UIScreen *uiScreen() const; + UIWindow *uiWindow() const; void updateProperties(); diff --git a/src/plugins/platforms/ios/qiosscreen.mm b/src/plugins/platforms/ios/qiosscreen.mm index e70b369b79..4af2a4965f 100644 --- a/src/plugins/platforms/ios/qiosscreen.mm +++ b/src/plugins/platforms/ios/qiosscreen.mm @@ -321,6 +321,11 @@ UIScreen *QIOSScreen::uiScreen() const return m_uiScreen; } +UIWindow *QIOSScreen::uiWindow() const +{ + return m_uiWindow; +} + #include "moc_qiosscreen.cpp" QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp index 4f1a1a375f..13a3d044a0 100644 --- a/src/plugins/platforms/windows/qwindowscontext.cpp +++ b/src/plugins/platforms/windows/qwindowscontext.cpp @@ -952,6 +952,9 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, return true; case QtWindows::CalculateSize: return QWindowsGeometryHint::handleCalculateSize(d->m_creationContext->customMargins, msg, result); + case QtWindows::GeometryChangingEvent: + return QWindowsWindow::handleGeometryChangingMessage(&msg, d->m_creationContext->window, + d->m_creationContext->margins + d->m_creationContext->customMargins); default: break; } diff --git a/src/plugins/platforms/windows/qwindowscursor.cpp b/src/plugins/platforms/windows/qwindowscursor.cpp index f5d6c140bf..d10c7fdb20 100644 --- a/src/plugins/platforms/windows/qwindowscursor.cpp +++ b/src/plugins/platforms/windows/qwindowscursor.cpp @@ -570,8 +570,21 @@ QWindowsWindowCursor QWindowsCursor::pixmapWindowCursor(const QCursor &c) { const QWindowsCursorCacheKey cacheKey(c); CursorCache::iterator it = m_cursorCache.find(cacheKey); - if (it == m_cursorCache.end()) + if (it == m_cursorCache.end()) { + if (m_cursorCache.size() > 50) { + // Prevent the cursor cache from growing indefinitely hitting GDI resource + // limits if new pixmap cursors are created repetitively by purging out + // all-noncurrent pixmap cursors (QTBUG-43515) + const HCURSOR currentCursor = GetCursor(); + for (it = m_cursorCache.begin(); it != m_cursorCache.end() ; ) { + if (it.key().bitmapCacheKey && it.value().handle() != currentCursor) + it = m_cursorCache.erase(it); + else + ++it; + } + } it = m_cursorCache.insert(cacheKey, QWindowsWindowCursor(c)); + } return it.value(); } diff --git a/src/plugins/platforms/windows/qwindowsfontdatabase.cpp b/src/plugins/platforms/windows/qwindowsfontdatabase.cpp index 7492423e69..214273e685 100644 --- a/src/plugins/platforms/windows/qwindowsfontdatabase.cpp +++ b/src/plugins/platforms/windows/qwindowsfontdatabase.cpp @@ -1062,6 +1062,9 @@ QWindowsFontEngineDataPtr sharedFontData() } #endif // QT_NO_THREAD +#ifndef Q_OS_WINCE +extern Q_GUI_EXPORT bool qt_needs_a8_gamma_correction; +#endif QWindowsFontDatabase::QWindowsFontDatabase() { // Properties accessed by QWin32PrintEngine (Qt Print Support) @@ -1075,6 +1078,9 @@ QWindowsFontDatabase::QWindowsFontDatabase() qCDebug(lcQpaFonts) << __FUNCTION__ << "Clear type: " << data->clearTypeEnabled << "gamma: " << data->fontSmoothingGamma; } +#ifndef Q_OS_WINCE + qt_needs_a8_gamma_correction = true; +#endif } QWindowsFontDatabase::~QWindowsFontDatabase() diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index 0b4bb9b09d..5768800947 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -801,7 +801,7 @@ QWindowCreationContext::QWindowCreationContext(const QWindow *w, const QRect &geometry, const QMargins &cm, DWORD style_, DWORD exStyle_) : - geometryHint(w, cm), style(style_), exStyle(exStyle_), + geometryHint(w, cm), window(w), style(style_), exStyle(exStyle_), requestedGeometry(geometry), obtainedGeometry(geometry), margins(QWindowsGeometryHint::frame(style, exStyle)), customMargins(cm), frameX(CW_USEDEFAULT), frameY(CW_USEDEFAULT), @@ -1783,16 +1783,14 @@ void QWindowsWindow::propagateSizeHints() qCDebug(lcQpaWindows) << __FUNCTION__ << this << window(); } -bool QWindowsWindow::handleGeometryChanging(MSG *message) const +bool QWindowsWindow::handleGeometryChangingMessage(MSG *message, const QWindow *qWindow, const QMargins &marginsDp) { #ifndef Q_OS_WINCE - QWindow *qWin = window(); - if (!qWin->isTopLevel()) + if (!qWindow->isTopLevel()) // Implement hasHeightForWidth(). return false; WINDOWPOS *windowPos = reinterpret_cast<WINDOWPOS *>(message->lParam); if ((windowPos->flags & (SWP_NOCOPYBITS | SWP_NOSIZE))) return false; - const QMargins marginsDp = frameMarginsDp(); const QRect suggestedFrameGeometryDp(windowPos->x, windowPos->y, windowPos->cx, windowPos->cy); const qreal factor = QWindowsScaling::factor(); @@ -1800,7 +1798,7 @@ bool QWindowsWindow::handleGeometryChanging(MSG *message) const const QRectF suggestedGeometry = QRectF(QPointF(suggestedGeometryDp.topLeft()) / factor, QSizeF(suggestedGeometryDp.size()) / factor); const QRectF correctedGeometryF = - qt_window_private(qWin)->closestAcceptableGeometry(suggestedGeometry); + qt_window_private(const_cast<QWindow *>(qWindow))->closestAcceptableGeometry(suggestedGeometry); if (!correctedGeometryF.isValid()) return false; const QRect correctedFrameGeometryDp @@ -1820,6 +1818,12 @@ bool QWindowsWindow::handleGeometryChanging(MSG *message) const #endif } +bool QWindowsWindow::handleGeometryChanging(MSG *message) const +{ + const QMargins marginsDp = window()->isTopLevel() ? frameMarginsDp() : QMargins(); + return QWindowsWindow::handleGeometryChangingMessage(message, window(), marginsDp); +} + QMargins QWindowsWindow::frameMarginsDp() const { // Frames are invalidated by style changes (window state, flags). @@ -1827,7 +1831,12 @@ QMargins QWindowsWindow::frameMarginsDp() const // event sequences, introduce a dirty flag mechanism to be able // to cache results. if (testFlag(FrameDirty)) { - m_data.frame = QWindowsGeometryHint::frame(style(), exStyle()); + // Always skip calculating style-dependent margins for windows claimed to be frameless. + // This allows users to remove the margins by handling WM_NCCALCSIZE with WS_THICKFRAME set + // to ensure Areo snap still works (QTBUG-40578). + m_data.frame = window()->flags() & Qt::FramelessWindowHint + ? QMargins(0, 0, 0, 0) + : QWindowsGeometryHint::frame(style(), exStyle()); clearFlag(FrameDirty); } return m_data.frame + m_data.customMargins; diff --git a/src/plugins/platforms/windows/qwindowswindow.h b/src/plugins/platforms/windows/qwindowswindow.h index a63a9f56e3..9822ebce45 100644 --- a/src/plugins/platforms/windows/qwindowswindow.h +++ b/src/plugins/platforms/windows/qwindowswindow.h @@ -84,6 +84,7 @@ struct QWindowCreationContext #endif QWindowsGeometryHint geometryHint; + const QWindow *window; DWORD style; DWORD exStyle; QRect requestedGeometry; @@ -183,6 +184,7 @@ public: void windowEvent(QEvent *event); void propagateSizeHints() Q_DECL_OVERRIDE; + static bool handleGeometryChangingMessage(MSG *message, const QWindow *qWindow, const QMargins &marginsDp); bool handleGeometryChanging(MSG *message) const; QMargins frameMarginsDp() const; QMargins frameMargins() const Q_DECL_OVERRIDE { return frameMarginsDp() / QWindowsScaling::factor(); } diff --git a/src/plugins/platforms/winrt/qwinrtscreen.cpp b/src/plugins/platforms/winrt/qwinrtscreen.cpp index fadcd01b06..37a3ff3d63 100644 --- a/src/plugins/platforms/winrt/qwinrtscreen.cpp +++ b/src/plugins/platforms/winrt/qwinrtscreen.cpp @@ -37,6 +37,9 @@ #include <EGL/eglext.h> #include <d3d11.h> #include <dxgi1_2.h> +#ifndef Q_OS_WINPHONE +#include <dxgi1_3.h> +#endif #include "qwinrtbackingstore.h" #include "qwinrtinputcontext.h" @@ -1078,12 +1081,14 @@ HRESULT QWinRTScreen::onSizeChanged(ICoreWindow *, IWindowSizeChangedEventArgs * d->logicalSize = logicalSize; if (d->eglDisplay) { const QRect newGeometry = geometry(); -#ifdef Q_OS_WINPHONE // Resize the EGL window - const int width = newGeometry.width() * (d->orientation == Qt::InvertedPortraitOrientation || d->orientation == Qt::LandscapeOrientation ? -1 : 1); - const int height = newGeometry.height() * (d->orientation == Qt::InvertedPortraitOrientation || d->orientation == Qt::InvertedLandscapeOrientation ? -1 : 1); + int width = newGeometry.width(); + int height = newGeometry.height(); +#ifdef Q_OS_WINPHONE // Windows Phone can pass in a negative size to provide orientation information + width *= (d->orientation == Qt::InvertedPortraitOrientation || d->orientation == Qt::LandscapeOrientation) ? -1 : 1; + height *= (d->orientation == Qt::InvertedPortraitOrientation || d->orientation == Qt::InvertedLandscapeOrientation) ? -1 : 1; +#endif eglSurfaceAttrib(d->eglDisplay, d->eglSurface, EGL_WIDTH, width); eglSurfaceAttrib(d->eglDisplay, d->eglSurface, EGL_HEIGHT, height); -#endif QWindowSystemInterface::handleScreenGeometryChange(screen(), newGeometry, newGeometry); QPlatformScreen::resizeMaximizedWindows(); handleExpose(); @@ -1113,6 +1118,16 @@ HRESULT QWinRTScreen::onActivated(ICoreWindow *, IWindowActivatedEventArgs *args HRESULT QWinRTScreen::onSuspended(IInspectable *, ISuspendingEventArgs *) { +#ifndef Q_OS_WINPHONE + Q_D(QWinRTScreen); + ComPtr<ID3D11Device> d3dDevice; + const EGLBoolean ok = eglQuerySurfacePointerANGLE(d->eglDisplay, EGL_NO_SURFACE, EGL_DEVICE_EXT, (void **)d3dDevice.GetAddressOf()); + if (ok && d3dDevice) { + ComPtr<IDXGIDevice3> dxgiDevice; + if (SUCCEEDED(d3dDevice.As(&dxgiDevice))) + dxgiDevice->Trim(); + } +#endif QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationSuspended); QWindowSystemInterface::flushWindowSystemEvents(); return S_OK; |