summaryrefslogtreecommitdiffstats
path: root/src/plugins
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@theqtcompany.com>2014-12-10 07:58:06 +0100
committerSimon Hausmann <simon.hausmann@theqtcompany.com>2014-12-10 07:58:06 +0100
commit015002fec9abff6a4c1bb3fa4b9de87279a079c3 (patch)
treead93af535a503d0a49d6c6367e990a8fbca163d3 /src/plugins
parentf1e00262321cc8daa3c7506153653453e2779886 (diff)
parentb9547af45ea2bbbc634722c1ef41afdb54216ce2 (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')
-rw-r--r--src/plugins/platforms/android/androidjniinput.cpp43
-rw-r--r--src/plugins/platforms/android/qandroidinputcontext.cpp4
-rw-r--r--src/plugins/platforms/android/qandroidplatformscreen.cpp15
-rw-r--r--src/plugins/platforms/android/qandroidplatformtheme.cpp3
-rw-r--r--src/plugins/platforms/cocoa/qcocoamenuloader.mm6
-rw-r--r--src/plugins/platforms/ios/qiosinputcontext.h26
-rw-r--r--src/plugins/platforms/ios/qiosinputcontext.mm300
-rw-r--r--src/plugins/platforms/ios/qiosintegration.mm8
-rw-r--r--src/plugins/platforms/ios/qiosscreen.h1
-rw-r--r--src/plugins/platforms/ios/qiosscreen.mm5
-rw-r--r--src/plugins/platforms/windows/qwindowscontext.cpp3
-rw-r--r--src/plugins/platforms/windows/qwindowscursor.cpp15
-rw-r--r--src/plugins/platforms/windows/qwindowsfontdatabase.cpp6
-rw-r--r--src/plugins/platforms/windows/qwindowswindow.cpp23
-rw-r--r--src/plugins/platforms/windows/qwindowswindow.h2
-rw-r--r--src/plugins/platforms/winrt/qwinrtscreen.cpp23
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;