summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/ios
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/platforms/ios')
-rw-r--r--src/plugins/platforms/ios/ios.pro16
-rw-r--r--src/plugins/platforms/ios/qiosapplicationdelegate.h33
-rw-r--r--src/plugins/platforms/ios/qiosapplicationdelegate.mm45
-rw-r--r--src/plugins/platforms/ios/qiosapplicationstate.h30
-rw-r--r--src/plugins/platforms/ios/qiosbackingstore.h30
-rw-r--r--src/plugins/platforms/ios/qiosclipboard.h30
-rw-r--r--src/plugins/platforms/ios/qiosclipboard.mm4
-rw-r--r--src/plugins/platforms/ios/qioscontext.h30
-rw-r--r--src/plugins/platforms/ios/qioscontext.mm24
-rw-r--r--src/plugins/platforms/ios/qioseventdispatcher.h30
-rw-r--r--src/plugins/platforms/ios/qiosglobal.h34
-rw-r--r--src/plugins/platforms/ios/qiosglobal.mm25
-rw-r--r--src/plugins/platforms/ios/qiosinputcontext.h52
-rw-r--r--src/plugins/platforms/ios/qiosinputcontext.mm264
-rw-r--r--src/plugins/platforms/ios/qiosintegration.h38
-rw-r--r--src/plugins/platforms/ios/qiosintegration.mm32
-rw-r--r--src/plugins/platforms/ios/qiosmenu.h148
-rw-r--r--src/plugins/platforms/ios/qiosmenu.mm528
-rw-r--r--src/plugins/platforms/ios/qiosplatformaccessibility.h52
-rw-r--r--src/plugins/platforms/ios/qiosplatformaccessibility.mm87
-rw-r--r--src/plugins/platforms/ios/qiosscreen.h39
-rw-r--r--src/plugins/platforms/ios/qiosscreen.mm226
-rw-r--r--src/plugins/platforms/ios/qiosservices.h30
-rw-r--r--src/plugins/platforms/ios/qiostextresponder.h77
-rw-r--r--src/plugins/platforms/ios/qiostextresponder.mm (renamed from src/plugins/platforms/ios/quiview_textinput.mm)302
-rw-r--r--src/plugins/platforms/ios/qiostheme.h33
-rw-r--r--src/plugins/platforms/ios/qiostheme.mm12
-rw-r--r--src/plugins/platforms/ios/qiosviewcontroller.h43
-rw-r--r--src/plugins/platforms/ios/qiosviewcontroller.mm231
-rw-r--r--src/plugins/platforms/ios/qioswindow.h33
-rw-r--r--src/plugins/platforms/ios/qioswindow.mm350
-rw-r--r--src/plugins/platforms/ios/quiaccessibilityelement.h50
-rw-r--r--src/plugins/platforms/ios/quiaccessibilityelement.mm193
-rw-r--r--src/plugins/platforms/ios/quiview.h77
-rw-r--r--src/plugins/platforms/ios/quiview.mm385
-rw-r--r--src/plugins/platforms/ios/quiview_accessibility.mm117
36 files changed, 2650 insertions, 1080 deletions
diff --git a/src/plugins/platforms/ios/ios.pro b/src/plugins/platforms/ios/ios.pro
index ffc4ff9b12..6b67a42f69 100644
--- a/src/plugins/platforms/ios/ios.pro
+++ b/src/plugins/platforms/ios/ios.pro
@@ -23,7 +23,12 @@ OBJECTIVE_SOURCES = \
qiostheme.mm \
qiosglobal.mm \
qiosservices.mm \
- qiosclipboard.mm
+ quiview.mm \
+ qiosclipboard.mm \
+ quiaccessibilityelement.mm \
+ qiosplatformaccessibility.mm \
+ qiostextresponder.mm \
+ qiosmenu.mm \
HEADERS = \
qiosintegration.h \
@@ -40,7 +45,12 @@ HEADERS = \
qiosglobal.h \
qiosservices.h \
quiview.h \
- qiosclipboard.h
+ qiosclipboard.h \
+ quiaccessibilityelement.h \
+ qiosplatformaccessibility.h \
+ qiostextresponder.h \
+ qiosmenu.h \
OTHER_FILES = \
- quiview_textinput.mm
+ quiview_textinput.mm \
+ quiview_accessibility.mm
diff --git a/src/plugins/platforms/ios/qiosapplicationdelegate.h b/src/plugins/platforms/ios/qiosapplicationdelegate.h
index 617b740d6e..e89bb0d271 100644
--- a/src/plugins/platforms/ios/qiosapplicationdelegate.h
+++ b/src/plugins/platforms/ios/qiosapplicationdelegate.h
@@ -1,40 +1,32 @@
/****************************************************************************
**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the plugins of the Qt Toolkit.
**
-** $QT_BEGIN_LICENSE:LGPL$
+** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
+** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3.0 as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 3.0 requirements will be
-** met: http://www.gnu.org/copyleft/gpl.html.
-**
-**
** $QT_END_LICENSE$
**
****************************************************************************/
@@ -45,7 +37,4 @@
#import "qiosviewcontroller.h"
@interface QIOSApplicationDelegate : UIResponder <UIApplicationDelegate>
-
-@property (strong, nonatomic) UIWindow *window;
-
@end
diff --git a/src/plugins/platforms/ios/qiosapplicationdelegate.mm b/src/plugins/platforms/ios/qiosapplicationdelegate.mm
index 9cf1047a6b..ef9f924384 100644
--- a/src/plugins/platforms/ios/qiosapplicationdelegate.mm
+++ b/src/plugins/platforms/ios/qiosapplicationdelegate.mm
@@ -46,47 +46,12 @@
#include "qiosviewcontroller.h"
#include "qioswindow.h"
-#include <QtGui/private/qguiapplication_p.h>
#include <qpa/qplatformintegration.h>
#include <QtCore/QtCore>
@implementation QIOSApplicationDelegate
-@synthesize window;
-
-- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
-{
- Q_UNUSED(application);
- Q_UNUSED(launchOptions);
-
- self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
- self.window.rootViewController = [[[QIOSViewController alloc] init] autorelease];
-
-#if QT_IOS_DEPLOYMENT_TARGET_BELOW(__IPHONE_7_0)
- QSysInfo::MacVersion iosVersion = QSysInfo::MacintoshVersion;
-
- // We prefer to keep the root viewcontroller in fullscreen layout, so that
- // we don't have to compensate for the viewcontroller position. This also
- // gives us the same behavior on iOS 5/6 as on iOS 7, where full screen layout
- // is the only way.
- if (iosVersion < QSysInfo::MV_IOS_7_0)
- self.window.rootViewController.wantsFullScreenLayout = YES;
-
- // Use translucent statusbar by default on iOS6 iPhones (unless the user changed
- // the default in the Info.plist), so that windows placed under the stausbar are
- // still visible, just like on iOS7.
- if (iosVersion >= QSysInfo::MV_IOS_6_0 && iosVersion < QSysInfo::MV_IOS_7_0
- && [UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone
- && [UIApplication sharedApplication].statusBarStyle == UIStatusBarStyleDefault)
- [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleBlackTranslucent];
-#endif
-
- self.window.hidden = NO;
-
- return YES;
-}
-
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
Q_UNUSED(application);
@@ -96,17 +61,13 @@
if (!QGuiApplication::instance())
return NO;
- QIOSIntegration *iosIntegration = static_cast<QIOSIntegration *>(QGuiApplicationPrivate::platformIntegration());
+ QIOSIntegration *iosIntegration = QIOSIntegration::instance();
+ Q_ASSERT(iosIntegration);
+
QIOSServices *iosServices = static_cast<QIOSServices *>(iosIntegration->services());
return iosServices->handleUrl(QUrl::fromNSURL(url));
}
-- (void)dealloc
-{
- [window release];
- [super dealloc];
-}
-
@end
diff --git a/src/plugins/platforms/ios/qiosapplicationstate.h b/src/plugins/platforms/ios/qiosapplicationstate.h
index 57ea547e72..49620980db 100644
--- a/src/plugins/platforms/ios/qiosapplicationstate.h
+++ b/src/plugins/platforms/ios/qiosapplicationstate.h
@@ -1,40 +1,32 @@
/****************************************************************************
**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the plugins of the Qt Toolkit.
**
-** $QT_BEGIN_LICENSE:LGPL$
+** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
+** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3.0 as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 3.0 requirements will be
-** met: http://www.gnu.org/copyleft/gpl.html.
-**
-**
** $QT_END_LICENSE$
**
****************************************************************************/
diff --git a/src/plugins/platforms/ios/qiosbackingstore.h b/src/plugins/platforms/ios/qiosbackingstore.h
index 4199530a01..eff7455247 100644
--- a/src/plugins/platforms/ios/qiosbackingstore.h
+++ b/src/plugins/platforms/ios/qiosbackingstore.h
@@ -1,40 +1,32 @@
/****************************************************************************
**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the plugins of the Qt Toolkit.
**
-** $QT_BEGIN_LICENSE:LGPL$
+** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
+** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3.0 as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 3.0 requirements will be
-** met: http://www.gnu.org/copyleft/gpl.html.
-**
-**
** $QT_END_LICENSE$
**
****************************************************************************/
diff --git a/src/plugins/platforms/ios/qiosclipboard.h b/src/plugins/platforms/ios/qiosclipboard.h
index da4226a40d..7d758e3a56 100644
--- a/src/plugins/platforms/ios/qiosclipboard.h
+++ b/src/plugins/platforms/ios/qiosclipboard.h
@@ -1,40 +1,32 @@
/****************************************************************************
**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the plugins of the Qt Toolkit.
**
-** $QT_BEGIN_LICENSE:LGPL$
+** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
+** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3.0 as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 3.0 requirements will be
-** met: http://www.gnu.org/copyleft/gpl.html.
-**
-**
** $QT_END_LICENSE$
**
****************************************************************************/
diff --git a/src/plugins/platforms/ios/qiosclipboard.mm b/src/plugins/platforms/ios/qiosclipboard.mm
index 0a7b34a216..e18ad53b2c 100644
--- a/src/plugins/platforms/ios/qiosclipboard.mm
+++ b/src/plugins/platforms/ios/qiosclipboard.mm
@@ -205,6 +205,10 @@ void QIOSClipboard::setMimeData(QMimeData *mimeData, QClipboard::Mode mode)
Q_ASSERT(supportsMode(mode));
UIPasteboard *pb = [UIPasteboard pasteboardWithQClipboardMode:mode];
+ if (!mimeData) {
+ pb.items = [NSArray array];
+ return;
+ }
NSMutableDictionary *pbItem = [NSMutableDictionary dictionaryWithCapacity:mimeData->formats().size()];
foreach (const QString &mimeType, mimeData->formats()) {
diff --git a/src/plugins/platforms/ios/qioscontext.h b/src/plugins/platforms/ios/qioscontext.h
index 52357a5d58..9bc953cb6a 100644
--- a/src/plugins/platforms/ios/qioscontext.h
+++ b/src/plugins/platforms/ios/qioscontext.h
@@ -1,40 +1,32 @@
/****************************************************************************
**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the plugins of the Qt Toolkit.
**
-** $QT_BEGIN_LICENSE:LGPL$
+** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
+** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3.0 as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 3.0 requirements will be
-** met: http://www.gnu.org/copyleft/gpl.html.
-**
-**
** $QT_END_LICENSE$
**
****************************************************************************/
diff --git a/src/plugins/platforms/ios/qioscontext.mm b/src/plugins/platforms/ios/qioscontext.mm
index ddee52196a..0143b75828 100644
--- a/src/plugins/platforms/ios/qioscontext.mm
+++ b/src/plugins/platforms/ios/qioscontext.mm
@@ -47,19 +47,33 @@
#include <QtGui/QOpenGLContext>
#import <OpenGLES/EAGL.h>
+#import <OpenGLES/ES2/glext.h>
#import <QuartzCore/CAEAGLLayer.h>
QIOSContext::QIOSContext(QOpenGLContext *context)
: QPlatformOpenGLContext()
, m_sharedContext(static_cast<QIOSContext *>(context->shareHandle()))
- , m_eaglContext([[EAGLContext alloc]
- initWithAPI:kEAGLRenderingAPIOpenGLES2
- sharegroup:m_sharedContext ? [m_sharedContext->m_eaglContext sharegroup] : nil])
, m_format(context->format())
{
m_format.setRenderableType(QSurfaceFormat::OpenGLES);
- m_format.setMajorVersion(2);
- m_format.setMinorVersion(0);
+ m_eaglContext = [[EAGLContext alloc]
+ initWithAPI:EAGLRenderingAPI(m_format.majorVersion())
+ sharegroup:m_sharedContext ? [m_sharedContext->m_eaglContext sharegroup] : nil];
+
+ if (m_eaglContext != nil) {
+ EAGLContext *originalContext = [EAGLContext currentContext];
+ [EAGLContext setCurrentContext:m_eaglContext];
+ const GLubyte *s = glGetString(GL_VERSION);
+ if (s) {
+ QByteArray version = QByteArray(reinterpret_cast<const char *>(s));
+ int major, minor;
+ if (QPlatformOpenGLContext::parseOpenGLVersion(version, major, minor)) {
+ m_format.setMajorVersion(major);
+ m_format.setMinorVersion(minor);
+ }
+ }
+ [EAGLContext setCurrentContext:originalContext];
+ }
// iOS internally double-buffers its rendering using copy instead of flipping,
// so technically we could report that we are single-buffered so that clients
diff --git a/src/plugins/platforms/ios/qioseventdispatcher.h b/src/plugins/platforms/ios/qioseventdispatcher.h
index 5caa7f5d2d..fa35939c61 100644
--- a/src/plugins/platforms/ios/qioseventdispatcher.h
+++ b/src/plugins/platforms/ios/qioseventdispatcher.h
@@ -1,40 +1,32 @@
/****************************************************************************
**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the plugins of the Qt Toolkit.
**
-** $QT_BEGIN_LICENSE:LGPL$
+** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
+** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3.0 as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 3.0 requirements will be
-** met: http://www.gnu.org/copyleft/gpl.html.
-**
-**
** $QT_END_LICENSE$
**
****************************************************************************/
diff --git a/src/plugins/platforms/ios/qiosglobal.h b/src/plugins/platforms/ios/qiosglobal.h
index 17184dc21d..c09987f9dc 100644
--- a/src/plugins/platforms/ios/qiosglobal.h
+++ b/src/plugins/platforms/ios/qiosglobal.h
@@ -1,40 +1,32 @@
/****************************************************************************
**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the plugins of the Qt Toolkit.
**
-** $QT_BEGIN_LICENSE:LGPL$
+** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
+** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3.0 as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 3.0 requirements will be
-** met: http://www.gnu.org/copyleft/gpl.html.
-**
-**
** $QT_END_LICENSE$
**
****************************************************************************/
@@ -65,4 +57,8 @@ int infoPlistValue(NSString* key, int defaultValue);
QT_END_NAMESPACE
+@interface UIResponder (QtFirstResponder)
++(id)currentFirstResponder;
+@end
+
#endif // QIOSGLOBAL_H
diff --git a/src/plugins/platforms/ios/qiosglobal.mm b/src/plugins/platforms/ios/qiosglobal.mm
index 2ce064582e..7ff4950599 100644
--- a/src/plugins/platforms/ios/qiosglobal.mm
+++ b/src/plugins/platforms/ios/qiosglobal.mm
@@ -141,5 +141,30 @@ int infoPlistValue(NSString* key, int defaultValue)
return value ? [value intValue] : defaultValue;
}
+// -------------------------------------------------------------------------
+
+@interface QtFirstResponderEvent : UIEvent
+@property (nonatomic, strong) id firstResponder;
+@end
+
+@implementation QtFirstResponderEvent
+@end
+
+@implementation UIResponder (QtFirstResponder)
+
++(id)currentFirstResponder
+{
+ QtFirstResponderEvent *event = [[[QtFirstResponderEvent alloc] init] autorelease];
+ [[UIApplication sharedApplication] sendAction:@selector(qt_findFirstResponder:event:) to:nil from:nil forEvent:event];
+ return event.firstResponder;
+}
+
+- (void)qt_findFirstResponder:(id)sender event:(QtFirstResponderEvent *)event
+{
+ Q_UNUSED(sender);
+ event.firstResponder = self;
+}
+@end
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/ios/qiosinputcontext.h b/src/plugins/platforms/ios/qiosinputcontext.h
index 13255ada56..8850bbf80e 100644
--- a/src/plugins/platforms/ios/qiosinputcontext.h
+++ b/src/plugins/platforms/ios/qiosinputcontext.h
@@ -1,40 +1,32 @@
/****************************************************************************
**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the plugins of the Qt Toolkit.
**
-** $QT_BEGIN_LICENSE:LGPL$
+** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
+** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3.0 as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 3.0 requirements will be
-** met: http://www.gnu.org/copyleft/gpl.html.
-**
-**
** $QT_END_LICENSE$
**
****************************************************************************/
@@ -44,13 +36,24 @@
#include <UIKit/UIKit.h>
+#include <QtGui/qevent.h>
#include <QtGui/qtransform.h>
#include <qpa/qplatforminputcontext.h>
+const char kImePlatformDataInputView[] = "inputView";
+const char kImePlatformDataInputAccessoryView[] = "inputAccessoryView";
+
QT_BEGIN_NAMESPACE
@class QIOSKeyboardListener;
-@class QUIView;
+@class QIOSTextInputResponder;
+
+struct ImeState
+{
+ ImeState() : currentState(0) {}
+ Qt::InputMethodQueries update(Qt::InputMethodQueries properties);
+ QInputMethodQueryEvent currentState;
+};
class QIOSInputContext : public QPlatformInputContext
{
@@ -59,8 +62,11 @@ public:
~QIOSInputContext();
QRectF keyboardRect() const;
+
void showInputPanel();
void hideInputPanel();
+ void hideVirtualKeyboard();
+
bool isInputPanelVisible() const;
void setFocusObject(QObject *object);
@@ -73,10 +79,12 @@ public:
void reset();
void commit();
+ const ImeState &imeState() { return m_imeState; };
+
private:
QIOSKeyboardListener *m_keyboardListener;
- QUIView *m_focusView;
- bool m_hasPendingHideRequest;
+ QIOSTextInputResponder *m_textResponder;
+ ImeState m_imeState;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/ios/qiosinputcontext.mm b/src/plugins/platforms/ios/qiosinputcontext.mm
index d109d53168..13e91889a2 100644
--- a/src/plugins/platforms/ios/qiosinputcontext.mm
+++ b/src/plugins/platforms/ios/qiosinputcontext.mm
@@ -44,11 +44,19 @@
#import <UIKit/UIGestureRecognizerSubclass.h>
#include "qiosglobal.h"
+#include "qiostextresponder.h"
#include "qioswindow.h"
#include "quiview.h"
+
#include <QGuiApplication>
#include <QtGui/private/qwindow_p.h>
+static QUIView *focusView()
+{
+ return qApp->focusWindow() ?
+ reinterpret_cast<QUIView *>(qApp->focusWindow()->winId()) : 0;
+}
+
@interface QIOSKeyboardListener : UIGestureRecognizer {
@public
QIOSInputContext *m_context;
@@ -57,7 +65,7 @@
BOOL m_touchPressWhileKeyboardVisible;
BOOL m_keyboardHiddenByGesture;
QRectF m_keyboardRect;
- QRectF m_keyboardEndRect;
+ CGRect m_keyboardEndRect;
NSTimeInterval m_duration;
UIViewAnimationCurve m_curve;
UIViewController *m_viewController;
@@ -131,20 +139,6 @@
[super dealloc];
}
-- (QRectF) getKeyboardRect:(NSNotification *)notification
-{
- // For Qt applications we rotate the keyboard rect to align with the screen
- // orientation (which is the interface orientation of the root view controller).
- // For hybrid apps we follow native behavior, and return the rect unmodified:
- CGRect keyboardFrame = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
- if (isQtApplication()) {
- UIView *view = m_viewController.view;
- return fromCGRect(CGRectOffset([view convertRect:keyboardFrame fromView:view.window], 0, -view.bounds.origin.y));
- } else {
- return fromCGRect(keyboardFrame);
- }
-}
-
- (void) keyboardDidChangeFrame:(NSNotification *)notification
{
Q_UNUSED(notification);
@@ -158,26 +152,22 @@
- (void) keyboardWillShow:(NSNotification *)notification
{
- if ([QUIView inUpdateKeyboardLayout])
- return;
// Note that UIKeyboardWillShowNotification is only sendt when the keyboard is docked.
m_keyboardVisibleAndDocked = YES;
- m_keyboardEndRect = [self getKeyboardRect:notification];
+ m_keyboardEndRect = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
self.enabled = YES;
if (!m_duration) {
m_duration = [[notification.userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
- m_curve = UIViewAnimationCurve([[notification.userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] integerValue] << 16);
+ m_curve = UIViewAnimationCurve([[notification.userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] integerValue]);
}
m_context->scrollToCursor();
}
- (void) keyboardWillHide:(NSNotification *)notification
{
- if ([QUIView inUpdateKeyboardLayout])
- return;
// Note that UIKeyboardWillHideNotification is also sendt when the keyboard is undocked.
m_keyboardVisibleAndDocked = NO;
- m_keyboardEndRect = [self getKeyboardRect:notification];
+ m_keyboardEndRect = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
if (!m_keyboardHiddenByGesture) {
// 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.
@@ -188,14 +178,23 @@
- (void) handleKeyboardRectChanged
{
- QRectF rect = m_keyboardEndRect;
- rect.moveTop(rect.y() + m_viewController.view.bounds.origin.y);
- if (m_keyboardRect != rect) {
- m_keyboardRect = rect;
+ // 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);
+
+ if (convertedRect != m_keyboardRect) {
+ m_keyboardRect = convertedRect;
m_context->emitKeyboardRectChanged();
}
- BOOL visible = m_keyboardEndRect.intersects(fromCGRect([UIScreen mainScreen].bounds));
+ BOOL visible = CGRectIntersectsRect(m_keyboardEndRect, [UIScreen mainScreen].bounds);
if (m_keyboardVisible != visible) {
m_keyboardVisible = visible;
m_context->emitInputPanelVisibleChanged();
@@ -204,10 +203,10 @@
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
- QPointF p = fromCGPoint([[touches anyObject] locationInView:m_viewController.view]);
- if (m_keyboardRect.contains(p)) {
+ CGPoint p = [[touches anyObject] locationInView:m_viewController.view.window];
+ if (CGRectContainsPoint(m_keyboardEndRect, p)) {
m_keyboardHiddenByGesture = YES;
- m_context->hideInputPanel();
+ m_context->hideVirtualKeyboard();
}
[super touchesMoved:touches withEvent:event];
@@ -253,11 +252,37 @@
@end
+// -------------------------------------------------------------------------
+
+Qt::InputMethodQueries ImeState::update(Qt::InputMethodQueries properties)
+{
+ if (!properties)
+ return 0;
+
+ QInputMethodQueryEvent newState(properties);
+
+ if (qApp && qApp->focusObject())
+ QCoreApplication::sendEvent(qApp->focusObject(), &newState);
+
+ Qt::InputMethodQueries updatedProperties;
+ for (uint i = 0; i < (sizeof(Qt::ImQueryAll) * CHAR_BIT); ++i) {
+ if (Qt::InputMethodQuery property = Qt::InputMethodQuery(int(properties & (1 << i)))) {
+ if (newState.value(property) != currentState.value(property)) {
+ updatedProperties |= property;
+ currentState.setValue(property, newState.value(property));
+ }
+ }
+ }
+
+ return updatedProperties;
+}
+
+// -------------------------------------------------------------------------
+
QIOSInputContext::QIOSInputContext()
: QPlatformInputContext()
, m_keyboardListener([[QIOSKeyboardListener alloc] initWithQIOSInputContext:this])
- , m_focusView(0)
- , m_hasPendingHideRequest(false)
+ , m_textResponder(0)
{
if (isQtApplication())
connect(qGuiApp->inputMethod(), &QInputMethod::cursorRectangleChanged, this, &QIOSInputContext::cursorRectangleChanged);
@@ -267,7 +292,7 @@ QIOSInputContext::QIOSInputContext()
QIOSInputContext::~QIOSInputContext()
{
[m_keyboardListener release];
- [m_focusView release];
+ [m_textResponder release];
}
QRectF QIOSInputContext::keyboardRect() const
@@ -277,61 +302,22 @@ QRectF QIOSInputContext::keyboardRect() const
void QIOSInputContext::showInputPanel()
{
- if (m_keyboardListener->m_keyboardHiddenByGesture) {
- // We refuse to re-show the keyboard until the touch
- // sequence that triggered the gesture has ended.
- return;
- }
-
- // Documentation tells that one should call (and recall, if necessary) becomeFirstResponder/resignFirstResponder
- // to show/hide the keyboard. This is slightly inconvenient, since there exist no API to get the current first
- // responder. Rather than searching for it from the top, we let the active QIOSWindow tell us which view to use.
- // Note that Qt will forward keyevents to whichever QObject that needs it, regardless of which UIView the input
- // actually came from. So in this respect, we're undermining iOS' responder chain.
- m_hasPendingHideRequest = false;
- [m_focusView becomeFirstResponder];
+ // No-op, keyboard controlled fully by platform based on focus
}
void QIOSInputContext::hideInputPanel()
{
- // Delay hiding the keyboard for cases where the user is transferring focus between
- // 'line edits'. In that case the 'line edit' that lost focus will close the input
- // panel, just to see that the new 'line edit' will open it again:
- m_hasPendingHideRequest = true;
- dispatch_async(dispatch_get_main_queue(), ^{
- if (m_hasPendingHideRequest)
- [m_focusView resignFirstResponder];
- });
+ // No-op, keyboard controlled fully by platform based on focus
}
-bool QIOSInputContext::isInputPanelVisible() const
+void QIOSInputContext::hideVirtualKeyboard()
{
- return m_keyboardListener->m_keyboardVisible;
-}
-
-void QIOSInputContext::setFocusObject(QObject *focusObject)
-{
- if (!focusObject || !m_focusView || !m_focusView.isFirstResponder) {
- scroll(0);
- return;
- }
-
- reset();
-
- if (m_keyboardListener->m_keyboardVisibleAndDocked)
- scrollToCursor();
+ static_cast<QWindowPrivate *>(QObjectPrivate::get(qApp->focusWindow()))->clearFocusObject();
}
-void QIOSInputContext::focusWindowChanged(QWindow *focusWindow)
+bool QIOSInputContext::isInputPanelVisible() const
{
- QUIView *view = focusWindow ? reinterpret_cast<QUIView *>(focusWindow->handle()->winId()) : 0;
- if ([m_focusView isFirstResponder])
- [view becomeFirstResponder];
- [m_focusView release];
- m_focusView = [view retain];
-
- if (view.window != m_keyboardListener->m_viewController.view)
- scroll(0);
+ return m_keyboardListener->m_keyboardVisible;
}
void QIOSInputContext::cursorRectangleChanged()
@@ -353,7 +339,7 @@ void QIOSInputContext::cursorRectangleChanged()
void QIOSInputContext::scrollToCursor()
{
- if (!isQtApplication() || !m_focusView)
+ if (!isQtApplication())
return;
if (m_keyboardListener->m_touchPressWhileKeyboardVisible) {
@@ -364,13 +350,14 @@ void QIOSInputContext::scrollToCursor()
}
UIView *view = m_keyboardListener->m_viewController.view;
- if (view.window != m_focusView.window)
+ if (view.window != focusView().window)
return;
const int margin = 20;
QRectF translatedCursorPos = qApp->inputMethod()->cursorRectangle();
- translatedCursorPos.translate(m_focusView.qwindow->geometry().topLeft());
- qreal keyboardY = m_keyboardListener->m_keyboardEndRect.y();
+ translatedCursorPos.translate(focusView().qwindow->geometry().topLeft());
+
+ qreal keyboardY = [view convertRect:m_keyboardListener->m_keyboardEndRect fromView:nil].origin.y;
int statusBarY = qGuiApp->primaryScreen()->availableGeometry().y();
scroll((translatedCursorPos.bottomLeft().y() < keyboardY - margin) ? 0
@@ -379,18 +366,40 @@ void QIOSInputContext::scrollToCursor()
void QIOSInputContext::scroll(int y)
{
- // Scroll the view the same way a UIScrollView
- // works: by changing bounds.origin:
- UIView *view = m_keyboardListener->m_viewController.view;
- if (y == view.bounds.origin.y)
+ UIView *rootView = m_keyboardListener->m_viewController.view;
+
+ CATransform3D translationTransform = CATransform3DMakeTranslation(0.0, -y, 0.0);
+ if (CATransform3DEqualToTransform(translationTransform, rootView.layer.sublayerTransform))
return;
- CGRect newBounds = view.bounds;
- newBounds.origin.y = y;
QPointer<QIOSInputContext> self = this;
[UIView animateWithDuration:m_keyboardListener->m_duration delay:0
- options:m_keyboardListener->m_curve | UIViewAnimationOptionBeginFromCurrentState
- animations:^{ view.bounds = newBounds; }
+ options:(m_keyboardListener->m_curve << 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
+ // an explicit CoreAnimation animation. Since there is no predefined media timing
+ // function that matches the custom keyboard animation curve we cheat by asking
+ // the view for an animation of another property, which will give us an animation
+ // that matches the parameters we passed to [UIView animateWithDuration] above.
+ // The reason we ask for the animation of 'backgroundColor' is that it's a simple
+ // property that will not return a compound animation, like eg. bounds will.
+ NSObject *action = (NSObject*)[rootView actionForLayer:rootView.layer forKey:@"backgroundColor"];
+
+ CABasicAnimation *animation;
+ if ([action isKindOfClass:[CABasicAnimation class]]) {
+ animation = static_cast<CABasicAnimation*>(action);
+ animation.keyPath = @"sublayerTransform"; // Instead of backgroundColor
+ } else {
+ animation = [CABasicAnimation animationWithKeyPath:@"sublayerTransform"];
+ }
+
+ CATransform3D currentSublayerTransform = static_cast<CALayer *>([rootView.layer presentationLayer]).sublayerTransform;
+ animation.fromValue = [NSValue valueWithCATransform3D:currentSublayerTransform];
+ animation.toValue = [NSValue valueWithCATransform3D:translationTransform];
+ [rootView.layer addAnimation:animation forKey:@"AnimateSubLayerTransform"];
+ rootView.layer.sublayerTransform = translationTransform;
+ }
completion:^(BOOL){
if (self)
[m_keyboardListener handleKeyboardRectChanged];
@@ -398,18 +407,85 @@ void QIOSInputContext::scroll(int y)
];
}
-void QIOSInputContext::update(Qt::InputMethodQueries query)
+// -------------------------------------------------------------------------
+
+void QIOSInputContext::setFocusObject(QObject *focusObject)
+{
+ Q_UNUSED(focusObject);
+
+ reset();
+
+ if (m_keyboardListener->m_keyboardVisibleAndDocked)
+ scrollToCursor();
+}
+
+void QIOSInputContext::focusWindowChanged(QWindow *focusWindow)
{
- [m_focusView updateInputMethodWithQuery:query];
+ Q_UNUSED(focusWindow);
+
+ reset();
+
+ [m_keyboardListener handleKeyboardRectChanged];
+ if (m_keyboardListener->m_keyboardVisibleAndDocked)
+ scrollToCursor();
}
+/*!
+ Called by the input item to inform the platform input methods when there has been
+ state changes in editor's input method query attributes. When calling the function
+ \a queries parameter has to be used to tell what has changes, which input method
+ can use to make queries for attributes it's interested with QInputMethodQueryEvent.
+*/
+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);
+
+ Qt::InputMethodQueries changedProperties = m_imeState.update(updatedProperties);
+ if (changedProperties & (Qt::ImEnabled | Qt::ImHints | Qt::ImPlatformData)) {
+ // Changes to enablement or hints require virtual keyboard reconfigure
+ [m_textResponder release];
+ m_textResponder = [[QIOSTextInputResponder alloc] initWithInputContext:this];
+ [m_textResponder reloadInputViews];
+ } else {
+ [m_textResponder notifyInputDelegate:changedProperties];
+ }
+}
+
+/*!
+ Called by the input item to reset the input method state.
+*/
void QIOSInputContext::reset()
{
- [m_focusView reset];
+ update(Qt::ImQueryAll);
+
+ [m_textResponder setMarkedText:@"" selectedRange:NSMakeRange(0, 0)];
+ [m_textResponder notifyInputDelegate:Qt::ImQueryInput];
}
+/*!
+ Commits the word user is currently composing to the editor. The function is
+ mostly needed by the input methods with text prediction features and by the
+ methods where the script used for typing characters is different from the
+ script that actually gets appended to the editor. Any kind of action that
+ interrupts the text composing needs to flush the composing state by calling the
+ commit() function, for example when the cursor is moved elsewhere.
+*/
void QIOSInputContext::commit()
{
- [m_focusView commit];
+ [m_textResponder unmarkText];
+ [m_textResponder notifyInputDelegate:Qt::ImSurroundingText];
}
+// -------------------------------------------------------------------------
+
+@interface QUIView (InputMethods)
+- (void)reloadInputViews;
+@end
+
+@implementation QUIView (InputMethods)
+- (void)reloadInputViews
+{
+ qApp->inputMethod()->reset();
+}
+@end
diff --git a/src/plugins/platforms/ios/qiosintegration.h b/src/plugins/platforms/ios/qiosintegration.h
index 956c112399..85338b1c4d 100644
--- a/src/plugins/platforms/ios/qiosintegration.h
+++ b/src/plugins/platforms/ios/qiosintegration.h
@@ -1,40 +1,32 @@
/****************************************************************************
**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the plugins of the Qt Toolkit.
**
-** $QT_BEGIN_LICENSE:LGPL$
+** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
+** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3.0 as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 3.0 requirements will be
-** met: http://www.gnu.org/copyleft/gpl.html.
-**
-**
** $QT_END_LICENSE$
**
****************************************************************************/
@@ -82,14 +74,20 @@ public:
void *nativeResourceForWindow(const QByteArray &resource, QWindow *window);
QTouchDevice *touchDevice();
+ QPlatformAccessibility *accessibility() const Q_DECL_OVERRIDE;
+
+ void addScreen(QPlatformScreen *screen) { screenAdded(screen); }
+
+ static QIOSIntegration *instance();
+
private:
QPlatformFontDatabase *m_fontDatabase;
QPlatformClipboard *m_clipboard;
QPlatformInputContext *m_inputContext;
- QPlatformScreen *m_screen;
QTouchDevice *m_touchDevice;
QIOSApplicationState m_applicationState;
QIOSServices *m_platformServices;
+ mutable QPlatformAccessibility *m_accessibility;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/ios/qiosintegration.mm b/src/plugins/platforms/ios/qiosintegration.mm
index 0fe7adff9f..9a722ead37 100644
--- a/src/plugins/platforms/ios/qiosintegration.mm
+++ b/src/plugins/platforms/ios/qiosintegration.mm
@@ -45,12 +45,15 @@
#include "qioswindow.h"
#include "qiosbackingstore.h"
#include "qiosscreen.h"
+#include "qiosplatformaccessibility.h"
#include "qioscontext.h"
#include "qiosclipboard.h"
#include "qiosinputcontext.h"
#include "qiostheme.h"
#include "qiosservices.h"
+#include <QtGui/private/qguiapplication_p.h>
+
#include <qpa/qplatformoffscreensurface.h>
#include <QtPlatformSupport/private/qcoretextfontdatabase_p.h>
@@ -61,12 +64,17 @@
QT_BEGIN_NAMESPACE
+QIOSIntegration *QIOSIntegration::instance()
+{
+ return static_cast<QIOSIntegration *>(QGuiApplicationPrivate::platformIntegration());
+}
+
QIOSIntegration::QIOSIntegration()
: m_fontDatabase(new QCoreTextFontDatabase)
, m_clipboard(new QIOSClipboard)
- , m_inputContext(new QIOSInputContext)
- , m_screen(new QIOSScreen(QIOSScreen::MainScreen))
+ , m_inputContext(0)
, m_platformServices(new QIOSServices)
+ , m_accessibility(0)
{
if (![UIApplication sharedApplication]) {
qWarning()
@@ -80,7 +88,11 @@ QIOSIntegration::QIOSIntegration()
// Set current directory to app bundle folder
QDir::setCurrent(QString::fromUtf8([[[NSBundle mainBundle] bundlePath] UTF8String]));
- screenAdded(m_screen);
+ for (UIScreen *screen in [UIScreen screens])
+ addScreen(new QIOSScreen(screen));
+
+ // Depends on a primary screen being present
+ m_inputContext = new QIOSInputContext;
m_touchDevice = new QTouchDevice;
m_touchDevice->setType(QTouchDevice::TouchScreen);
@@ -101,11 +113,14 @@ QIOSIntegration::~QIOSIntegration()
delete m_inputContext;
m_inputContext = 0;
- delete m_screen;
- m_screen = 0;
+ foreach (QScreen *screen, QGuiApplication::screens())
+ delete screen->handle();
delete m_platformServices;
m_platformServices = 0;
+
+ delete m_accessibility;
+ m_accessibility = 0;
}
bool QIOSIntegration::hasCapability(Capability cap) const
@@ -229,4 +244,11 @@ QTouchDevice *QIOSIntegration::touchDevice()
return m_touchDevice;
}
+QPlatformAccessibility *QIOSIntegration::accessibility() const
+{
+ if (!m_accessibility)
+ m_accessibility = new QIOSPlatformAccessibility;
+ return m_accessibility;
+}
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/ios/qiosmenu.h b/src/plugins/platforms/ios/qiosmenu.h
new file mode 100644
index 0000000000..16e1ee5d1f
--- /dev/null
+++ b/src/plugins/platforms/ios/qiosmenu.h
@@ -0,0 +1,148 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QIOSMENU_H
+#define QIOSMENU_H
+
+#import <UIKit/UIKit.h>
+
+#include <QtCore/QtCore>
+#include <qpa/qplatformmenu.h>
+
+#import "quiview.h"
+
+class QIOSMenu;
+@class QUIMenuController;
+@class QUIPickerView;
+
+class QIOSMenuItem : public QPlatformMenuItem
+{
+public:
+ QIOSMenuItem();
+
+ void setTag(quintptr tag) Q_DECL_OVERRIDE;
+ quintptr tag()const Q_DECL_OVERRIDE;
+
+ void setText(const QString &text) Q_DECL_OVERRIDE;
+ void setIcon(const QIcon &) Q_DECL_OVERRIDE {}
+ void setMenu(QPlatformMenu *) Q_DECL_OVERRIDE;
+ void setVisible(bool isVisible) Q_DECL_OVERRIDE;
+ void setIsSeparator(bool) Q_DECL_OVERRIDE;
+ void setFont(const QFont &) Q_DECL_OVERRIDE {}
+ void setRole(MenuRole role) Q_DECL_OVERRIDE;
+ void setCheckable(bool) Q_DECL_OVERRIDE {}
+ void setChecked(bool) Q_DECL_OVERRIDE {}
+ void setShortcut(const QKeySequence&) Q_DECL_OVERRIDE {}
+ void setEnabled(bool enabled) Q_DECL_OVERRIDE;
+ void setIconSize(int) Q_DECL_OVERRIDE {}
+
+ quintptr m_tag;
+ bool m_visible;
+ QString m_text;
+ MenuRole m_role;
+ bool m_enabled;
+ bool m_separator;
+ QIOSMenu *m_menu;
+
+private:
+ QString removeMnemonics(const QString &original);
+};
+
+typedef QList<QIOSMenuItem *> QIOSMenuItemList;
+
+class QIOSMenu : public QPlatformMenu
+{
+public:
+ QIOSMenu();
+ ~QIOSMenu();
+
+ void insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before) Q_DECL_OVERRIDE;
+ void removeMenuItem(QPlatformMenuItem *menuItem) Q_DECL_OVERRIDE;
+ void syncMenuItem(QPlatformMenuItem *) Q_DECL_OVERRIDE {}
+ void syncSeparatorsCollapsible(bool) Q_DECL_OVERRIDE {}
+
+ void setTag(quintptr tag) Q_DECL_OVERRIDE;
+ quintptr tag()const Q_DECL_OVERRIDE;
+
+ void setText(const QString &) Q_DECL_OVERRIDE;
+ void setIcon(const QIcon &) Q_DECL_OVERRIDE {}
+ void setEnabled(bool enabled) Q_DECL_OVERRIDE;
+ void setVisible(bool visible) Q_DECL_OVERRIDE;
+ void setMenuType(MenuType type) Q_DECL_OVERRIDE;
+
+ void showPopup(const QWindow *parentWindow, const QRect &targetRect, const QPlatformMenuItem *item) Q_DECL_OVERRIDE;
+ void dismiss() Q_DECL_OVERRIDE;
+
+ QPlatformMenuItem *menuItemAt(int position) const Q_DECL_OVERRIDE;
+ QPlatformMenuItem *menuItemForTag(quintptr tag) const Q_DECL_OVERRIDE;
+
+ void handleItemSelected(QIOSMenuItem *menuItem);
+
+ static QIOSMenu *currentMenu() { return m_currentMenu; }
+ static id menuActionTarget() { return m_currentMenu ? m_currentMenu->m_menuController : 0; }
+
+protected:
+ bool eventFilter(QObject *obj, QEvent *event);
+
+private:
+ quintptr m_tag;
+ bool m_enabled;
+ bool m_visible;
+ QString m_text;
+ MenuType m_menuType;
+ MenuType m_effectiveMenuType;
+ QPointer<QWindow> m_parentWindow;
+ QRect m_targetRect;
+ const QIOSMenuItem *m_targetItem;
+ QUIMenuController *m_menuController;
+ QUIPickerView *m_pickerView;
+ QIOSMenuItemList m_menuItems;
+
+ static QIOSMenu *m_currentMenu;
+
+ void updateVisibility();
+ void toggleShowUsingUIMenuController(bool show);
+ void toggleShowUsingUIPickerView(bool show);
+ QIOSMenuItemList visibleMenuItems() const;
+ void repositionMenu();
+};
+
+#endif // QIOSMENU_H
diff --git a/src/plugins/platforms/ios/qiosmenu.mm b/src/plugins/platforms/ios/qiosmenu.mm
new file mode 100644
index 0000000000..c247c16514
--- /dev/null
+++ b/src/plugins/platforms/ios/qiosmenu.mm
@@ -0,0 +1,528 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qglobal.h>
+#include <qguiapplication.h>
+
+#include "qiosglobal.h"
+#include "qiosmenu.h"
+#include "qioswindow.h"
+#include "qiosinputcontext.h"
+#include "qiosintegration.h"
+#include "qiostextresponder.h"
+
+// m_currentMenu points to the currently visible menu.
+// Only one menu will be visible at a time, and if a second menu
+// is shown on top of a first, the first one will be told to hide.
+QIOSMenu *QIOSMenu::m_currentMenu = 0;
+
+// -------------------------------------------------------------------------
+
+static NSString *const kSelectorPrefix = @"_qtMenuItem_";
+
+@interface QUIMenuController : UIResponder {
+ QIOSMenuItemList m_visibleMenuItems;
+}
+@end
+
+@implementation QUIMenuController
+
+- (id)initWithVisibleMenuItems:(const QIOSMenuItemList &)visibleMenuItems
+{
+ if (self = [super init]) {
+ m_visibleMenuItems = visibleMenuItems;
+ NSMutableArray *menuItemArray = [NSMutableArray arrayWithCapacity:m_visibleMenuItems.size()];
+ // Create an array of UIMenuItems, one for each visible QIOSMenuItem. Each
+ // UIMenuItem needs a callback assigned, so we assign one of the placeholder methods
+ // added to UIWindow (QIOSMenuActionTargets) below. Each method knows its own index, which
+ // corresponds to the index of the corresponding QIOSMenuItem in m_visibleMenuItems. When
+ // triggered, menuItemActionCallback will end up being called.
+ for (int i = 0; i < m_visibleMenuItems.count(); ++i) {
+ QIOSMenuItem *item = m_visibleMenuItems.at(i);
+ SEL sel = NSSelectorFromString([NSString stringWithFormat:@"%@%i:", kSelectorPrefix, i]);
+ [menuItemArray addObject:[[[UIMenuItem alloc] initWithTitle:item->m_text.toNSString() action:sel] autorelease]];
+ }
+ [UIMenuController sharedMenuController].menuItems = menuItemArray;
+ }
+
+ return self;
+}
+
+- (id)targetForAction:(SEL)action withSender:(id)sender
+{
+ BOOL containsPrefix = ([NSStringFromSelector(action) rangeOfString:kSelectorPrefix].location != NSNotFound);
+ return (containsPrefix && [sender isKindOfClass:[UIMenuController class]]) ? self : 0;
+}
+
+- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
+{
+ Q_UNUSED(selector);
+ // Just return a dummy signature that NSObject can create an NSInvocation from.
+ // We end up only checking selector in forwardInvocation anyway.
+ return [super methodSignatureForSelector:@selector(methodSignatureForSelector:)];
+}
+
+- (void)forwardInvocation:(NSInvocation *)invocation
+{
+ // Since none of the menu item selector methods actually exist, this function
+ // will end up being called as a final resort. We can then handle the action.
+ NSString *selector = NSStringFromSelector(invocation.selector);
+ NSRange range = NSMakeRange(kSelectorPrefix.length, selector.length - kSelectorPrefix.length - 1);
+ NSInteger selectedIndex = [[selector substringWithRange:range] integerValue];
+ QIOSMenu::currentMenu()->handleItemSelected(m_visibleMenuItems.at(selectedIndex));
+}
+
+@end
+
+// -------------------------------------------------------------------------
+
+@interface QUIPickerView : UIPickerView <UIPickerViewDelegate, UIPickerViewDataSource> {
+ QIOSMenuItemList m_visibleMenuItems;
+ QPointer<QObject> m_focusObjectWithPickerView;
+ NSInteger m_selectedRow;
+}
+
+@property(retain) UIToolbar *toolbar;
+
+@end
+
+@implementation QUIPickerView
+
+- (id)initWithVisibleMenuItems:(const QIOSMenuItemList &)visibleMenuItems selectItem:(const QIOSMenuItem *)selectItem
+{
+ if (self = [super init]) {
+ self.autoresizingMask = UIViewAutoresizingFlexibleWidth;
+ m_visibleMenuItems = visibleMenuItems;
+ m_selectedRow = visibleMenuItems.indexOf(const_cast<QIOSMenuItem *>(selectItem));
+ if (m_selectedRow == -1)
+ m_selectedRow = 0;
+
+ self.toolbar = [[[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, 100, 44)] autorelease];
+ self.toolbar.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
+
+ UIBarButtonItem *doneButton = [[[UIBarButtonItem alloc]
+ initWithBarButtonSystemItem:UIBarButtonSystemItemDone
+ target:self action:@selector(closeMenu)] autorelease];
+ UIBarButtonItem *spaceButton = [[[UIBarButtonItem alloc]
+ initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace
+ target:self action:@selector(closeMenu)] autorelease];
+ UIBarButtonItem *cancelButton = [[[UIBarButtonItem alloc]
+ initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
+ target:self action:@selector(cancelMenu)] autorelease];
+ [self.toolbar setItems:[NSArray arrayWithObjects:doneButton, spaceButton, cancelButton, nil]];
+
+ [self setDelegate:self];
+ [self setDataSource:self];
+ [self selectRow:m_selectedRow inComponent:0 animated:false];
+ }
+
+ return self;
+}
+
+-(void)dealloc
+{
+ self.toolbar = 0;
+ [super dealloc];
+}
+
+- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
+{
+ Q_UNUSED(pickerView);
+ Q_UNUSED(component);
+ return m_visibleMenuItems.at(row)->m_text.toNSString();
+}
+
+- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
+{
+ Q_UNUSED(pickerView);
+ return 1;
+}
+
+- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
+{
+ Q_UNUSED(pickerView);
+ Q_UNUSED(component);
+ return m_visibleMenuItems.length();
+}
+
+- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
+{
+ Q_UNUSED(pickerView);
+ Q_UNUSED(component);
+ m_selectedRow = row;
+}
+
+- (void)closeMenu
+{
+ if (!m_visibleMenuItems.isEmpty())
+ QIOSMenu::currentMenu()->handleItemSelected(m_visibleMenuItems.at(m_selectedRow));
+ else
+ QIOSMenu::currentMenu()->dismiss();
+}
+
+- (void)cancelMenu
+{
+ QIOSMenu::currentMenu()->dismiss();
+}
+
+@end
+
+// -------------------------------------------------------------------------
+
+QIOSMenuItem::QIOSMenuItem()
+ : QPlatformMenuItem()
+ , m_tag(0)
+ , m_visible(true)
+ , m_text(QString())
+ , m_role(MenuRole(0))
+ , m_enabled(true)
+ , m_separator(false)
+ , m_menu(0)
+{
+}
+
+void QIOSMenuItem::setTag(quintptr tag)
+{
+ m_tag = tag;
+}
+
+quintptr QIOSMenuItem::tag() const
+{
+ return m_tag;
+}
+
+void QIOSMenuItem::setText(const QString &text)
+{
+ m_text = removeMnemonics(text);
+}
+
+void QIOSMenuItem::setMenu(QPlatformMenu *menu)
+{
+ m_menu = static_cast<QIOSMenu *>(menu);
+}
+
+void QIOSMenuItem::setVisible(bool isVisible)
+{
+ m_visible = isVisible;
+}
+
+void QIOSMenuItem::setIsSeparator(bool isSeparator)
+{
+ m_separator = isSeparator;
+}
+
+void QIOSMenuItem::setRole(QPlatformMenuItem::MenuRole role)
+{
+ m_role = role;
+}
+
+void QIOSMenuItem::setEnabled(bool enabled)
+{
+ m_enabled = enabled;
+}
+
+QString QIOSMenuItem::removeMnemonics(const QString &original)
+{
+ // Copied from qcocoahelpers
+ QString returnText(original.size(), 0);
+ int finalDest = 0;
+ int currPos = 0;
+ int l = original.length();
+ while (l) {
+ if (original.at(currPos) == QLatin1Char('&')
+ && (l == 1 || original.at(currPos + 1) != QLatin1Char('&'))) {
+ ++currPos;
+ --l;
+ if (l == 0)
+ break;
+ } else if (original.at(currPos) == QLatin1Char('(') && l >= 4 &&
+ original.at(currPos + 1) == QLatin1Char('&') &&
+ original.at(currPos + 2) != QLatin1Char('&') &&
+ original.at(currPos + 3) == QLatin1Char(')')) {
+ /* remove mnemonics its format is "\s*(&X)" */
+ int n = 0;
+ while (finalDest > n && returnText.at(finalDest - n - 1).isSpace())
+ ++n;
+ finalDest -= n;
+ currPos += 4;
+ l -= 4;
+ continue;
+ }
+ returnText[finalDest] = original.at(currPos);
+ ++currPos;
+ ++finalDest;
+ --l;
+ }
+ returnText.truncate(finalDest);
+ return returnText;
+}
+
+QIOSMenu::QIOSMenu()
+ : QPlatformMenu()
+ , m_tag(0)
+ , m_enabled(true)
+ , m_visible(true)
+ , m_text(QString())
+ , m_menuType(DefaultMenu)
+ , m_effectiveMenuType(DefaultMenu)
+ , m_parentWindow(0)
+ , m_targetItem(0)
+ , m_menuController(0)
+ , m_pickerView(0)
+{
+}
+
+QIOSMenu::~QIOSMenu()
+{
+ dismiss();
+}
+
+void QIOSMenu::insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before)
+{
+ if (!before) {
+ m_menuItems.append(static_cast<QIOSMenuItem *>(menuItem));
+ } else {
+ int index = m_menuItems.indexOf(static_cast<QIOSMenuItem *>(before)) + 1;
+ m_menuItems.insert(index, static_cast<QIOSMenuItem *>(menuItem));
+ }
+}
+
+void QIOSMenu::removeMenuItem(QPlatformMenuItem *menuItem)
+{
+ m_menuItems.removeOne(static_cast<QIOSMenuItem *>(menuItem));
+}
+
+void QIOSMenu::setTag(quintptr tag)
+{
+ m_tag = tag;
+}
+
+quintptr QIOSMenu::tag() const
+{
+ return m_tag;
+}
+
+void QIOSMenu::setText(const QString &text)
+{
+ m_text = text;
+}
+
+void QIOSMenu::setEnabled(bool enabled)
+{
+ m_enabled = enabled;
+}
+
+void QIOSMenu::setVisible(bool visible)
+{
+ m_visible = visible;
+}
+
+void QIOSMenu::setMenuType(QPlatformMenu::MenuType type)
+{
+ m_menuType = type;
+}
+
+void QIOSMenu::handleItemSelected(QIOSMenuItem *menuItem)
+{
+ emit menuItem->activated();
+ dismiss();
+
+ if (QIOSMenu *menu = menuItem->m_menu) {
+ menu->setMenuType(m_effectiveMenuType);
+ menu->showPopup(m_parentWindow, m_targetRect, 0);
+ }
+}
+
+void QIOSMenu::showPopup(const QWindow *parentWindow, const QRect &targetRect, const QPlatformMenuItem *item)
+{
+ if (m_currentMenu == this || !m_visible || !m_enabled || !parentWindow)
+ return;
+
+ emit aboutToShow();
+
+ m_parentWindow = const_cast<QWindow *>(parentWindow);
+ m_targetRect = targetRect;
+ m_targetItem = static_cast<const QIOSMenuItem *>(item);
+
+ if (!m_parentWindow->isActive())
+ m_parentWindow->requestActivate();
+
+ if (m_currentMenu && m_currentMenu != this)
+ m_currentMenu->dismiss();
+
+ m_currentMenu = this;
+ m_effectiveMenuType = m_menuType;
+ connect(qGuiApp, &QGuiApplication::focusObjectChanged, this, &QIOSMenu::dismiss);
+
+ switch (m_effectiveMenuType) {
+ case EditMenu:
+ toggleShowUsingUIMenuController(true);
+ break;
+ default:
+ toggleShowUsingUIPickerView(true);
+ break;
+ }
+}
+
+void QIOSMenu::dismiss()
+{
+ if (m_currentMenu != this)
+ return;
+
+ emit aboutToHide();
+
+ disconnect(qGuiApp, &QGuiApplication::focusObjectChanged, this, &QIOSMenu::dismiss);
+
+ switch (m_effectiveMenuType) {
+ case EditMenu:
+ toggleShowUsingUIMenuController(false);
+ break;
+ default:
+ toggleShowUsingUIPickerView(false);
+ break;
+ }
+
+ m_currentMenu = 0;
+}
+
+void QIOSMenu::toggleShowUsingUIMenuController(bool show)
+{
+ if (show) {
+ Q_ASSERT(!m_menuController);
+ m_menuController = [[QUIMenuController alloc] initWithVisibleMenuItems:visibleMenuItems()];
+ repositionMenu();
+ connect(qGuiApp->inputMethod(), &QInputMethod::keyboardRectangleChanged, this, &QIOSMenu::repositionMenu);
+ } else {
+ disconnect(qGuiApp->inputMethod(), &QInputMethod::keyboardRectangleChanged, this, &QIOSMenu::repositionMenu);
+
+ Q_ASSERT(m_menuController);
+ [[UIMenuController sharedMenuController] setMenuVisible:NO animated:YES];
+ [m_menuController release];
+ m_menuController = 0;
+ }
+}
+
+void QIOSMenu::toggleShowUsingUIPickerView(bool show)
+{
+ static QObject *focusObjectWithPickerView = 0;
+
+ if (show) {
+ Q_ASSERT(!m_pickerView);
+ m_pickerView = [[QUIPickerView alloc] initWithVisibleMenuItems:visibleMenuItems() selectItem:m_targetItem];
+
+ Q_ASSERT(!focusObjectWithPickerView);
+ focusObjectWithPickerView = qApp->focusWindow()->focusObject();
+ focusObjectWithPickerView->installEventFilter(this);
+ qApp->inputMethod()->update(Qt::ImPlatformData);
+ } else {
+ Q_ASSERT(focusObjectWithPickerView);
+ focusObjectWithPickerView->removeEventFilter(this);
+ qApp->inputMethod()->update(Qt::ImPlatformData);
+ focusObjectWithPickerView = 0;
+
+ Q_ASSERT(m_pickerView);
+ [m_pickerView release];
+ m_pickerView = 0;
+ }
+}
+
+bool QIOSMenu::eventFilter(QObject *obj, QEvent *event)
+{
+ if (event->type() == QEvent::InputMethodQuery) {
+ QInputMethodQueryEvent *queryEvent = static_cast<QInputMethodQueryEvent *>(event);
+ if (queryEvent->queries() & Qt::ImPlatformData) {
+ // Let object fill inn default query results
+ obj->event(queryEvent);
+
+ QVariantMap imPlatformData = queryEvent->value(Qt::ImPlatformData).toMap();
+ imPlatformData.insert(kImePlatformDataInputView, QVariant::fromValue(static_cast<void *>(m_pickerView)));
+ imPlatformData.insert(kImePlatformDataInputAccessoryView, QVariant::fromValue(static_cast<void *>(m_pickerView.toolbar)));
+ queryEvent->setValue(Qt::ImPlatformData, imPlatformData);
+
+ return true;
+ }
+ }
+
+ return QObject::eventFilter(obj, event);
+}
+
+QIOSMenuItemList QIOSMenu::visibleMenuItems() const
+{
+ QIOSMenuItemList visibleMenuItems = m_menuItems;
+
+ for (int i = visibleMenuItems.count() - 1; i >= 0; --i) {
+ QIOSMenuItem *item = visibleMenuItems.at(i);
+ if (!item->m_enabled || !item->m_visible || item->m_separator)
+ visibleMenuItems.removeAt(i);
+ }
+
+ return visibleMenuItems;
+}
+
+void QIOSMenu::repositionMenu()
+{
+ switch (m_effectiveMenuType) {
+ case EditMenu: {
+ UIView *view = reinterpret_cast<UIView *>(m_parentWindow->winId());
+ [[UIMenuController sharedMenuController] setTargetRect:toCGRect(m_targetRect) inView:view];
+ [[UIMenuController sharedMenuController] setMenuVisible:YES animated:YES];
+ break; }
+ default:
+ break;
+ }
+}
+
+QPlatformMenuItem *QIOSMenu::menuItemAt(int position) const
+{
+ if (position < 0 || position >= m_menuItems.size())
+ return 0;
+ return m_menuItems.at(position);
+}
+
+QPlatformMenuItem *QIOSMenu::menuItemForTag(quintptr tag) const
+{
+ for (int i = 0; i < m_menuItems.size(); ++i) {
+ QPlatformMenuItem *item = m_menuItems.at(i);
+ if (item->tag() == tag)
+ return item;
+ }
+ return 0;
+}
diff --git a/src/plugins/platforms/ios/qiosplatformaccessibility.h b/src/plugins/platforms/ios/qiosplatformaccessibility.h
new file mode 100644
index 0000000000..4aa9dc3c7d
--- /dev/null
+++ b/src/plugins/platforms/ios/qiosplatformaccessibility.h
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QIOSPLATFORMACCESSIBILITY_H
+#define QIOSPLATFORMACCESSIBILITY_H
+
+#include <qpa/qplatformaccessibility.h>
+
+QT_BEGIN_NAMESPACE
+
+class QIOSPlatformAccessibility: public QPlatformAccessibility
+{
+public:
+ QIOSPlatformAccessibility();
+ ~QIOSPlatformAccessibility();
+
+ virtual void notifyAccessibilityUpdate(QAccessibleEvent *event);
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/platforms/ios/qiosplatformaccessibility.mm b/src/plugins/platforms/ios/qiosplatformaccessibility.mm
new file mode 100644
index 0000000000..ad8bd9bdf4
--- /dev/null
+++ b/src/plugins/platforms/ios/qiosplatformaccessibility.mm
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qiosplatformaccessibility.h"
+
+#include <QtGui/QtGui>
+#include "qioswindow.h"
+
+QIOSPlatformAccessibility::QIOSPlatformAccessibility()
+{}
+
+QIOSPlatformAccessibility::~QIOSPlatformAccessibility()
+{}
+
+
+void invalidateCache(QAccessibleInterface *iface)
+{
+ if (!iface || !iface->isValid()) {
+ qWarning() << "invalid accessible interface: " << iface;
+ return;
+ }
+
+ QWindow *win = 0;
+ QAccessibleInterface *parent = iface;
+ do {
+ win = parent->window();
+ parent = parent->parent();
+ } while (!win && parent);
+
+ if (win && win->handle()) {
+ QIOSWindow *window = static_cast<QIOSWindow*>(win->handle());
+ window->clearAccessibleCache();
+ }
+}
+
+
+void QIOSPlatformAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event)
+{
+ switch (event->type()) {
+ case QAccessible::ObjectCreated:
+ case QAccessible::ObjectShow:
+ case QAccessible::ObjectHide:
+ case QAccessible::ObjectDestroyed:
+ invalidateCache(event->accessibleInterface());
+ break;
+ default:
+ break;
+ }
+}
diff --git a/src/plugins/platforms/ios/qiosscreen.h b/src/plugins/platforms/ios/qiosscreen.h
index 173bd11719..7987ef82d5 100644
--- a/src/plugins/platforms/ios/qiosscreen.h
+++ b/src/plugins/platforms/ios/qiosscreen.h
@@ -1,40 +1,32 @@
/****************************************************************************
**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the plugins of the Qt Toolkit.
**
-** $QT_BEGIN_LICENSE:LGPL$
+** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
+** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3.0 as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 3.0 requirements will be
-** met: http://www.gnu.org/copyleft/gpl.html.
-**
-**
** $QT_END_LICENSE$
**
****************************************************************************/
@@ -55,11 +47,9 @@ class QIOSScreen : public QObject, public QPlatformScreen
Q_OBJECT
public:
- QIOSScreen(unsigned int screenIndex);
+ QIOSScreen(UIScreen *screen);
~QIOSScreen();
- enum ScreenIndex { MainScreen = 0 };
-
QRect geometry() const;
QRect availableGeometry() const;
int depth() const;
@@ -75,13 +65,10 @@ public:
UIScreen *uiScreen() const;
void updateProperties();
- void layoutWindows();
-
-public slots:
- void updateStatusBarVisibility();
private:
UIScreen *m_uiScreen;
+ UIWindow *m_uiWindow;
QRect m_geometry;
QRect m_availableGeometry;
int m_depth;
diff --git a/src/plugins/platforms/ios/qiosscreen.mm b/src/plugins/platforms/ios/qiosscreen.mm
index 5331d05ae9..d8a49eace6 100644
--- a/src/plugins/platforms/ios/qiosscreen.mm
+++ b/src/plugins/platforms/ios/qiosscreen.mm
@@ -40,6 +40,7 @@
****************************************************************************/
#include "qiosglobal.h"
+#include "qiosintegration.h"
#include "qiosscreen.h"
#include "qioswindow.h"
#include <qpa/qwindowsysteminterface.h>
@@ -48,6 +49,63 @@
#include <sys/sysctl.h>
+// -------------------------------------------------------------------------
+
+static QIOSScreen* qtPlatformScreenFor(UIScreen *uiScreen)
+{
+ foreach (QScreen *screen, QGuiApplication::screens()) {
+ QIOSScreen *platformScreen = static_cast<QIOSScreen *>(screen->handle());
+ if (platformScreen->uiScreen() == uiScreen)
+ return platformScreen;
+ }
+
+ return 0;
+}
+
+@interface QIOSScreenTracker : NSObject
+@end
+
+@implementation QIOSScreenTracker
+
++ (void)load
+{
+ NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
+ [center addObserver:self selector:@selector(screenConnected:)
+ name:UIScreenDidConnectNotification object:nil];
+ [center addObserver:self selector:@selector(screenDisconnected:)
+ name:UIScreenDidDisconnectNotification object:nil];
+ [center addObserver:self selector:@selector(screenModeChanged:)
+ name:UIScreenModeDidChangeNotification object:nil];
+}
+
++ (void)screenConnected:(NSNotification*)notification
+{
+ QIOSIntegration *integration = QIOSIntegration::instance();
+ Q_ASSERT_X(integration, Q_FUNC_INFO, "Screen connected before QIOSIntegration creation");
+
+ integration->addScreen(new QIOSScreen([notification object]));
+}
+
++ (void)screenDisconnected:(NSNotification*)notification
+{
+ QIOSScreen *screen = qtPlatformScreenFor([notification object]);
+ Q_ASSERT_X(screen, Q_FUNC_INFO, "Screen disconnected that we didn't know about");
+
+ delete screen;
+}
+
++ (void)screenModeChanged:(NSNotification*)notification
+{
+ QIOSScreen *screen = qtPlatformScreenFor([notification object]);
+ Q_ASSERT_X(screen, Q_FUNC_INFO, "Screen changed that we didn't know about");
+
+ screen->updateProperties();
+}
+
+@end
+
+// -------------------------------------------------------------------------
+
@interface QIOSOrientationListener : NSObject {
@public
QIOSScreen *m_screen;
@@ -99,6 +157,8 @@
@end
+// -------------------------------------------------------------------------
+
/*!
Returns the model identifier of the device.
@@ -118,30 +178,52 @@ static QString deviceModelIdentifier()
return QString::fromLatin1(value);
}
-QIOSScreen::QIOSScreen(unsigned int screenIndex)
+QIOSScreen::QIOSScreen(UIScreen *screen)
: QPlatformScreen()
- , m_uiScreen([[UIScreen screens] count] > screenIndex
- ? [[UIScreen screens] objectAtIndex:screenIndex]
- : [UIScreen mainScreen])
+ , m_uiScreen(screen)
+ , m_uiWindow(0)
, m_orientationListener(0)
{
- QString deviceIdentifier = deviceModelIdentifier();
-
- if (deviceIdentifier == QStringLiteral("iPhone2,1") /* iPhone 3GS */
- || deviceIdentifier == QStringLiteral("iPod3,1") /* iPod touch 3G */) {
- m_depth = 18;
+ if (screen == [UIScreen mainScreen]) {
+ QString deviceIdentifier = deviceModelIdentifier();
+
+ if (deviceIdentifier == QStringLiteral("iPhone2,1") /* iPhone 3GS */
+ || deviceIdentifier == QStringLiteral("iPod3,1") /* iPod touch 3G */) {
+ m_depth = 18;
+ } else {
+ m_depth = 24;
+ }
+
+ if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad
+ && !deviceIdentifier.contains(QRegularExpression("^iPad2,[567]$")) /* excluding iPad Mini */) {
+ m_unscaledDpi = 132;
+ } else {
+ m_unscaledDpi = 163; // Regular iPhone DPI
+ }
} else {
+ // External display, hard to say
m_depth = 24;
+ m_unscaledDpi = 96;
}
- if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad
- && !deviceIdentifier.contains(QRegularExpression("^iPad2,[567]$")) /* excluding iPad Mini */) {
- m_unscaledDpi = 132;
- } else {
- m_unscaledDpi = 163; // Regular iPhone DPI
+ for (UIWindow *existingWindow in [[UIApplication sharedApplication] windows]) {
+ if (existingWindow.screen == m_uiScreen) {
+ m_uiWindow = [m_uiWindow retain];
+ break;
+ }
}
- connect(qGuiApp, &QGuiApplication::focusWindowChanged, this, &QIOSScreen::updateStatusBarVisibility);
+ if (!m_uiWindow) {
+ // Create a window and associated view-controller that we can use
+ m_uiWindow = [[UIWindow alloc] initWithFrame:[m_uiScreen bounds]];
+ m_uiWindow.rootViewController = [[[QIOSViewController alloc] initWithQIOSScreen:this] autorelease];
+
+ // FIXME: Only do once windows are added to the screen, and for any screen
+ if (screen == [UIScreen mainScreen]) {
+ m_uiWindow.screen = m_uiScreen;
+ m_uiWindow.hidden = NO;
+ }
+ }
updateProperties();
}
@@ -149,111 +231,24 @@ QIOSScreen::QIOSScreen(unsigned int screenIndex)
QIOSScreen::~QIOSScreen()
{
[m_orientationListener release];
+ [m_uiWindow release];
}
void QIOSScreen::updateProperties()
{
- UIWindow *uiWindow = 0;
- for (uiWindow in [[UIApplication sharedApplication] windows]) {
- if (uiWindow.screen == m_uiScreen)
- break;
- }
+ QRect previousGeometry = m_geometry;
+ QRect previousAvailableGeometry = m_availableGeometry;
- bool inPortrait = UIInterfaceOrientationIsPortrait(uiWindow.rootViewController.interfaceOrientation);
- QRect geometry = inPortrait ? fromCGRect(m_uiScreen.bounds).toRect()
- : QRect(m_uiScreen.bounds.origin.x, m_uiScreen.bounds.origin.y,
- m_uiScreen.bounds.size.height, m_uiScreen.bounds.size.width);
+ UIView *rootView = m_uiWindow.rootViewController.view;
- if (geometry != m_geometry) {
- m_geometry = geometry;
+ m_geometry = fromCGRect([rootView convertRect:m_uiScreen.bounds fromView:m_uiWindow]).toRect();
+ m_availableGeometry = fromCGRect([rootView convertRect:m_uiScreen.applicationFrame fromView:m_uiWindow]).toRect();
+ if (m_geometry != previousGeometry || m_availableGeometry != previousAvailableGeometry) {
const qreal millimetersPerInch = 25.4;
m_physicalSize = QSizeF(m_geometry.size()) / m_unscaledDpi * millimetersPerInch;
- QWindowSystemInterface::handleScreenGeometryChange(screen(), m_geometry);
- }
-
- QRect availableGeometry = geometry;
-
- CGSize applicationFrameSize = m_uiScreen.applicationFrame.size;
- int statusBarHeight = geometry.height() - (inPortrait ? applicationFrameSize.height : applicationFrameSize.width);
-
- availableGeometry.adjust(0, statusBarHeight, 0, 0);
-
- if (availableGeometry != m_availableGeometry) {
- m_availableGeometry = availableGeometry;
- QWindowSystemInterface::handleScreenAvailableGeometryChange(screen(), m_availableGeometry);
- }
-
- if (screen())
- layoutWindows();
-}
-
-void QIOSScreen::updateStatusBarVisibility()
-{
- if (!isQtApplication())
- return;
-
- QWindow *focusWindow = QGuiApplication::focusWindow();
-
- // If we don't have a focus window we leave the status
- // bar as is, so that the user can activate a new window
- // with the same window state without the status bar jumping
- // back and forth.
- if (!focusWindow)
- return;
-
- UIView *view = reinterpret_cast<UIView *>(focusWindow->handle()->winId());
- QIOSViewController *viewController = static_cast<QIOSViewController *>(view.viewController);
-
- bool currentStatusBarVisibility = [UIApplication sharedApplication].statusBarHidden;
- if (viewController.prefersStatusBarHidden == currentStatusBarVisibility)
- return;
-
-#if QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_7_0)
- if (QSysInfo::MacintoshVersion >= QSysInfo::MV_IOS_7_0) {
- [viewController setNeedsStatusBarAppearanceUpdate];
- dispatch_async(dispatch_get_main_queue(), ^{
- updateProperties();
- });
- } else
-#endif
- {
- [[UIApplication sharedApplication]
- setStatusBarHidden:[viewController prefersStatusBarHidden]
- withAnimation:UIStatusBarAnimationNone];
-
- updateProperties();
- }
-}
-
-void QIOSScreen::layoutWindows()
-{
- QList<QWindow*> windows = QGuiApplication::topLevelWindows();
-
- const QRect oldGeometry = screen()->geometry();
- const QRect oldAvailableGeometry = screen()->availableGeometry();
- const QRect newGeometry = geometry();
- const QRect newAvailableGeometry = availableGeometry();
-
- for (int i = 0; i < windows.size(); ++i) {
- QWindow *window = windows.at(i);
-
- if (platformScreenForWindow(window) != this)
- continue;
-
- QIOSWindow *platformWindow = static_cast<QIOSWindow *>(window->handle());
- if (!platformWindow)
- continue;
-
- // FIXME: Handle more complex cases of no-state and/or child windows when rotating
-
- if (window->windowState() & Qt::WindowFullScreen
- || (window->windowState() & Qt::WindowNoState && window->geometry() == oldGeometry))
- platformWindow->applyGeometry(newGeometry);
- else if (window->windowState() & Qt::WindowMaximized
- || (window->windowState() & Qt::WindowNoState && window->geometry() == oldAvailableGeometry))
- platformWindow->applyGeometry(newAvailableGeometry);
+ QWindowSystemInterface::handleScreenGeometryChange(screen(), m_geometry, m_availableGeometry);
}
}
@@ -294,8 +289,15 @@ qreal QIOSScreen::devicePixelRatio() const
Qt::ScreenOrientation QIOSScreen::nativeOrientation() const
{
- // A UIScreen stays in the native orientation, regardless of rotation
- return m_uiScreen.bounds.size.width >= m_uiScreen.bounds.size.height ?
+ CGRect nativeBounds =
+#if QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_8_0)
+ QSysInfo::MacintoshVersion >= QSysInfo::MV_IOS_8_0 ? m_uiScreen.nativeBounds :
+#endif
+ m_uiScreen.bounds;
+
+ // All known iOS devices have a native orientation of portrait, but to
+ // be on the safe side we compare the width and height of the bounds.
+ return nativeBounds.size.width >= nativeBounds.size.height ?
Qt::LandscapeOrientation : Qt::PortraitOrientation;
}
diff --git a/src/plugins/platforms/ios/qiosservices.h b/src/plugins/platforms/ios/qiosservices.h
index aa39fbbed4..91b71f8b47 100644
--- a/src/plugins/platforms/ios/qiosservices.h
+++ b/src/plugins/platforms/ios/qiosservices.h
@@ -1,40 +1,32 @@
/****************************************************************************
**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the plugins of the Qt Toolkit.
**
-** $QT_BEGIN_LICENSE:LGPL$
+** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
+** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3.0 as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 3.0 requirements will be
-** met: http://www.gnu.org/copyleft/gpl.html.
-**
-**
** $QT_END_LICENSE$
**
****************************************************************************/
diff --git a/src/plugins/platforms/ios/qiostextresponder.h b/src/plugins/platforms/ios/qiostextresponder.h
new file mode 100644
index 0000000000..2923feba3b
--- /dev/null
+++ b/src/plugins/platforms/ios/qiostextresponder.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#import <UIKit/UIKit.h>
+
+#include <QtCore/qstring.h>
+
+class QIOSInputContext;
+
+@interface QIOSTextInputResponder : UIResponder <UITextInputTraits, UIKeyInput, UITextInput>
+{
+ @public
+ QString m_markedText;
+ BOOL m_inSendEventToFocusObject;
+
+ @private
+ QIOSInputContext *m_inputContext;
+}
+
+- (id)initWithInputContext:(QIOSInputContext *)context;
+- (void)notifyInputDelegate:(Qt::InputMethodQueries)updatedProperties;
+
+@property(readwrite, retain) UIView *inputView;
+@property(readwrite, retain) UIView *inputAccessoryView;
+
+// UITextInputTraits
+@property(nonatomic) UITextAutocapitalizationType autocapitalizationType;
+@property(nonatomic) UITextAutocorrectionType autocorrectionType;
+@property(nonatomic) UITextSpellCheckingType spellCheckingType;
+@property(nonatomic) BOOL enablesReturnKeyAutomatically;
+@property(nonatomic) UIKeyboardAppearance keyboardAppearance;
+@property(nonatomic) UIKeyboardType keyboardType;
+@property(nonatomic) UIReturnKeyType returnKeyType;
+@property(nonatomic, getter=isSecureTextEntry) BOOL secureTextEntry;
+
+// UITextInput
+@property(nonatomic, assign) id<UITextInputDelegate> inputDelegate;
+
+@end
diff --git a/src/plugins/platforms/ios/quiview_textinput.mm b/src/plugins/platforms/ios/qiostextresponder.mm
index e65ac1cc46..54362cde7a 100644
--- a/src/plugins/platforms/ios/quiview_textinput.mm
+++ b/src/plugins/platforms/ios/qiostextresponder.mm
@@ -39,36 +39,22 @@
**
****************************************************************************/
-#include <QtGui/qtextformat.h>
+#include "qiostextresponder.h"
-class StaticVariables
-{
-public:
- QInputMethodQueryEvent inputMethodQueryEvent;
- bool inUpdateKeyboardLayout;
- QTextCharFormat markedTextFormat;
+#include "qiosglobal.h"
+#include "qiosinputcontext.h"
+#include "quiview.h"
- StaticVariables()
- : inputMethodQueryEvent(Qt::ImQueryInput)
- , inUpdateKeyboardLayout(false)
- {
- // There seems to be no way to query how the preedit text
- // should be drawn. So we need to hard-code the color.
- QSysInfo::MacVersion iosVersion = QSysInfo::MacintoshVersion;
- if (iosVersion < QSysInfo::MV_IOS_7_0)
- markedTextFormat.setBackground(QColor(235, 239, 247));
- else
- markedTextFormat.setBackground(QColor(206, 221, 238));
- }
-};
+#include <QtCore/qscopedvaluerollback.h>
-Q_GLOBAL_STATIC(StaticVariables, staticVariables);
+#include <QtGui/qevent.h>
+#include <QtGui/qtextformat.h>
+#include <QtGui/private/qguiapplication_p.h>
+#include <QtGui/qpa/qplatformwindow.h>
// -------------------------------------------------------------------------
@interface QUITextPosition : UITextPosition
-{
-}
@property (nonatomic) NSUInteger index;
+ (QUITextPosition *)positionWithIndex:(NSUInteger)index;
@@ -89,8 +75,6 @@ Q_GLOBAL_STATIC(StaticVariables, staticVariables);
// -------------------------------------------------------------------------
@interface QUITextRange : UITextRange
-{
-}
@property (nonatomic) NSRange range;
+ (QUITextRange *)rangeWithNSRange:(NSRange)range;
@@ -130,51 +114,139 @@ Q_GLOBAL_STATIC(StaticVariables, staticVariables);
// -------------------------------------------------------------------------
-@implementation QUIView (TextInput)
+@interface WrapperView : UIView
+@end
+
+@implementation WrapperView
-- (BOOL)canBecomeFirstResponder
+-(id)initWithView:(UIView *)view
{
- return YES;
+ if (self = [self init]) {
+ [self addSubview:view];
+
+ self.autoresizingMask = view.autoresizingMask;
+
+ [self sizeToFit];
+ }
+
+ return self;
}
-- (BOOL)becomeFirstResponder
+- (void)layoutSubviews
{
- // Note: QIOSInputContext controls our first responder status based on
- // whether or not the keyboard should be open or closed.
- [self updateTextInputTraits];
- return [super becomeFirstResponder];
+ UIView* view = [self.subviews firstObject];
+ view.frame = self.bounds;
+
+ // FIXME: During orientation changes the size and position
+ // of the view is not respected by the host view, even if
+ // we call sizeToFit or setNeedsLayout on the superview.
}
-- (BOOL)resignFirstResponder
+- (CGSize)sizeThatFits:(CGSize)size
{
- // Resigning first responed status means that the virtual keyboard was closed, or
- // some other view became first responder. In either case we clear the focus object to
- // avoid blinking cursors in line edits etc:
- if (m_qioswindow)
- static_cast<QWindowPrivate *>(QObjectPrivate::get(m_qioswindow->window()))->clearFocusObject();
- return [super resignFirstResponder];
+ return [[self.subviews firstObject] sizeThatFits:size];
}
-+ (bool)inUpdateKeyboardLayout
+// By keeping the responder (QIOSTextInputResponder in this case)
+// retained, we ensure that all messages sent to the view during
+// its lifetime in a window hierarcy will be able to traverse the
+// responder chain.
+-(void)willMoveToWindow:(UIWindow *)window
{
- return staticVariables()->inUpdateKeyboardLayout;
+ if (window)
+ [[self nextResponder] retain];
+ else
+ [[self nextResponder] autorelease];
}
-- (void)updateKeyboardLayout
+@end
+
+// -------------------------------------------------------------------------
+
+@implementation QIOSTextInputResponder
+
+- (id)initWithInputContext:(QIOSInputContext *)inputContext
{
- if (![self isFirstResponder])
- return;
+ if (!(self = [self init]))
+ return self;
+
+ m_inSendEventToFocusObject = NO;
+ m_inputContext = inputContext;
+
+ Qt::InputMethodHints hints = Qt::InputMethodHints([self imValue:Qt::ImHints].toUInt());
+
+ self.returnKeyType = (hints & Qt::ImhMultiLine) ? UIReturnKeyDefault : UIReturnKeyDone;
+ self.secureTextEntry = BOOL(hints & Qt::ImhHiddenText);
+ self.autocorrectionType = (hints & Qt::ImhNoPredictiveText) ?
+ UITextAutocorrectionTypeNo : UITextAutocorrectionTypeDefault;
+ self.spellCheckingType = (hints & Qt::ImhNoPredictiveText) ?
+ UITextSpellCheckingTypeNo : UITextSpellCheckingTypeDefault;
+
+ if (hints & Qt::ImhUppercaseOnly)
+ self.autocapitalizationType = UITextAutocapitalizationTypeAllCharacters;
+ else if (hints & Qt::ImhNoAutoUppercase)
+ self.autocapitalizationType = UITextAutocapitalizationTypeNone;
+ else
+ self.autocapitalizationType = UITextAutocapitalizationTypeSentences;
- // There seems to be no API to inform that the keyboard layout needs to update.
- // As a work-around, we quickly resign first responder just to reassign it again.
- QScopedValueRollback<bool> rollback(staticVariables()->inUpdateKeyboardLayout);
- staticVariables()->inUpdateKeyboardLayout = true;
- [super resignFirstResponder];
- [self updateTextInputTraits];
- [super becomeFirstResponder];
+ if (hints & Qt::ImhUrlCharactersOnly)
+ self.keyboardType = UIKeyboardTypeURL;
+ else if (hints & Qt::ImhEmailCharactersOnly)
+ self.keyboardType = UIKeyboardTypeEmailAddress;
+ else if (hints & Qt::ImhDigitsOnly)
+ self.keyboardType = UIKeyboardTypeNumberPad;
+ else if (hints & Qt::ImhFormattedNumbersOnly)
+ self.keyboardType = UIKeyboardTypeDecimalPad;
+ else if (hints & Qt::ImhDialableCharactersOnly)
+ self.keyboardType = UIKeyboardTypeNumberPad;
+ else
+ self.keyboardType = UIKeyboardTypeDefault;
+
+ QVariantMap platformData = [self imValue:Qt::ImPlatformData].toMap();
+ if (UIView *inputView = static_cast<UIView *>(platformData.value(kImePlatformDataInputView).value<void *>()))
+ self.inputView = [[[WrapperView alloc] initWithView:inputView] autorelease];
+ if (UIView *accessoryView = static_cast<UIView *>(platformData.value(kImePlatformDataInputAccessoryView).value<void *>()))
+ self.inputAccessoryView = [[[WrapperView alloc] initWithView:accessoryView] autorelease];
+
+ return self;
+}
+
+- (void)dealloc
+{
+ self.inputView = 0;
+ self.inputAccessoryView = 0;
+ [super dealloc];
+}
+
+- (BOOL)isFirstResponder
+{
+ return YES;
+}
+
+- (UIResponder*)nextResponder
+{
+ return qApp->focusWindow() ?
+ reinterpret_cast<QUIView *>(qApp->focusWindow()->handle()->winId()) : 0;
}
-- (void)updateUITextInputDelegate:(NSNumber *)intQuery
+/*!
+ iOS uses [UIResponder(Internal) _requiresKeyboardWhenFirstResponder] to check if the
+ current responder should bring up the keyboard, which in turn checks if the responder
+ supports the UIKeyInput protocol. By dynamically reporting our protocol conformance
+ we can control the keyboard visibility depending on whether or not we have a focus
+ object with IME enabled.
+*/
+- (BOOL)conformsToProtocol:(Protocol *)protocol
+{
+ if (protocol == @protocol(UIKeyInput))
+ return m_inputContext->inputMethodAccepted();
+
+ return [super conformsToProtocol:protocol];
+}
+
+// -------------------------------------------------------------------------
+
+- (void)notifyInputDelegate:(Qt::InputMethodQueries)updatedProperties
{
// As documented, we should not report textWillChange/textDidChange unless the text
// was changed externally. That will cause spell checking etc to fail. But we don't
@@ -184,35 +256,17 @@ Q_GLOBAL_STATIC(StaticVariables, staticVariables);
if (m_inSendEventToFocusObject)
return;
- Qt::InputMethodQueries query = Qt::InputMethodQueries([intQuery intValue]);
- if (query & (Qt::ImCursorPosition | Qt::ImAnchorPosition)) {
- [self.inputDelegate selectionWillChange:id<UITextInput>(self)];
- [self.inputDelegate selectionDidChange:id<UITextInput>(self)];
+ if (updatedProperties & (Qt::ImCursorPosition | Qt::ImAnchorPosition)) {
+ [self.inputDelegate selectionWillChange:self];
+ [self.inputDelegate selectionDidChange:self];
}
- if (query & Qt::ImSurroundingText) {
- [self.inputDelegate textWillChange:id<UITextInput>(self)];
- [self.inputDelegate textDidChange:id<UITextInput>(self)];
+ if (updatedProperties & Qt::ImSurroundingText) {
+ [self.inputDelegate textWillChange:self];
+ [self.inputDelegate textDidChange:self];
}
}
-- (void)updateInputMethodWithQuery:(Qt::InputMethodQueries)query
-{
- Q_UNUSED(query);
-
- QObject *focusObject = QGuiApplication::focusObject();
- if (!focusObject)
- return;
-
- // Note that we ignore \a query, and instead update using Qt::ImQueryInput. This enables us to just
- // store the event without copying out the result from the event each time. Besides, we seem to be
- // called with Qt::ImQueryInput when only changing selection, and always if typing text. So there would
- // not be any performance gain by only updating \a query.
- staticVariables()->inputMethodQueryEvent = QInputMethodQueryEvent(Qt::ImQueryInput);
- QCoreApplication::sendEvent(focusObject, &staticVariables()->inputMethodQueryEvent);
- [self updateUITextInputDelegate:[NSNumber numberWithInt:int(query)]];
-}
-
- (void)sendEventToFocusObject:(QEvent &)e
{
QObject *focusObject = QGuiApplication::focusObject();
@@ -227,29 +281,9 @@ Q_GLOBAL_STATIC(StaticVariables, staticVariables);
QCoreApplication::sendEvent(focusObject, &e);
}
-- (void)reset
-{
- [self setMarkedText:@"" selectedRange:NSMakeRange(0, 0)];
- [self updateInputMethodWithQuery:Qt::ImQueryInput];
- // Guard agains recursive callbacks by posting calls to UITextInput
- [self performSelectorOnMainThread:@selector(updateKeyboardLayout) withObject:nil waitUntilDone:NO];
- [self performSelectorOnMainThread:@selector(updateUITextInputDelegate:)
- withObject:[NSNumber numberWithInt:int(Qt::ImQueryInput)]
- waitUntilDone:NO];
-}
-
-- (void)commit
-{
- [self unmarkText];
- // Guard agains recursive callbacks by posting calls to UITextInput
- [self performSelectorOnMainThread:@selector(updateUITextInputDelegate:)
- withObject:[NSNumber numberWithInt:int(Qt::ImSurroundingText)]
- waitUntilDone:NO];
-}
-
- (QVariant)imValue:(Qt::InputMethodQuery)query
{
- return staticVariables()->inputMethodQueryEvent.value(query);
+ return m_inputContext->imeState().currentState.value(query);
}
-(id<UITextInputTokenizer>)tokenizer
@@ -277,7 +311,8 @@ Q_GLOBAL_STATIC(StaticVariables, staticVariables);
[self sendEventToFocusObject:e];
}
-- (UITextRange *)selectedTextRange {
+- (UITextRange *)selectedTextRange
+{
int cursorPos = [self imValue:Qt::ImCursorPosition].toInt();
int anchorPos = [self imValue:Qt::ImAnchorPosition].toInt();
return [QUITextRange rangeWithNSRange:NSMakeRange(qMin(cursorPos, anchorPos), qAbs(anchorPos - cursorPos))];
@@ -296,8 +331,19 @@ Q_GLOBAL_STATIC(StaticVariables, staticVariables);
m_markedText = markedText ? QString::fromNSString(markedText) : QString();
+ static QTextCharFormat markedTextFormat;
+ if (markedTextFormat.isEmpty()) {
+ // There seems to be no way to query how the preedit text
+ // should be drawn. So we need to hard-code the color.
+ QSysInfo::MacVersion iosVersion = QSysInfo::MacintoshVersion;
+ if (iosVersion < QSysInfo::MV_IOS_7_0)
+ markedTextFormat.setBackground(QColor(235, 239, 247));
+ else
+ markedTextFormat.setBackground(QColor(206, 221, 238));
+ }
+
QList<QInputMethodEvent::Attribute> attrs;
- attrs << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, 0, markedText.length, staticVariables()->markedTextFormat);
+ attrs << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, 0, markedText.length, markedTextFormat);
QInputMethodEvent e(m_markedText, attrs);
[self sendEventToFocusObject:e];
}
@@ -325,7 +371,8 @@ Q_GLOBAL_STATIC(StaticVariables, staticVariables);
return NSOrderedSame;
}
-- (UITextRange *)markedTextRange {
+- (UITextRange *)markedTextRange
+{
return m_markedText.isEmpty() ? nil : [QUITextRange rangeWithNSRange:NSMakeRange(0, m_markedText.length())];
}
@@ -369,7 +416,8 @@ Q_GLOBAL_STATIC(StaticVariables, staticVariables);
// to be relative to the view this method returns.
// Since QInputMethod returns rects relative to the top level
// QWindow, that is also the view we need to return.
- QPlatformWindow *topLevel = m_qioswindow;
+ Q_ASSERT(qApp->focusWindow()->handle());
+ QPlatformWindow *topLevel = qApp->focusWindow()->handle();
while (QPlatformWindow *p = topLevel->parent())
topLevel = p;
return reinterpret_cast<UIView *>(topLevel->winId());
@@ -522,14 +570,15 @@ Q_GLOBAL_STATIC(StaticVariables, staticVariables);
return;
if ([text isEqualToString:@"\n"]) {
- if (self.returnKeyType == UIReturnKeyDone)
- qApp->inputMethod()->hide();
-
QKeyEvent press(QEvent::KeyPress, Qt::Key_Return, Qt::NoModifier);
QKeyEvent release(QEvent::KeyRelease, Qt::Key_Return, Qt::NoModifier);
[self sendEventToFocusObject:press];
[self sendEventToFocusObject:release];
+ Qt::InputMethodHints imeHints = static_cast<Qt::InputMethodHints>([self imValue:Qt::ImHints].toUInt());
+ if (!(imeHints & Qt::ImhMultiLine))
+ m_inputContext->hideVirtualKeyboard();
+
return;
}
@@ -549,47 +598,4 @@ Q_GLOBAL_STATIC(StaticVariables, staticVariables);
[self sendEventToFocusObject:release];
}
-- (void)updateTextInputTraits
-{
- // Ask the current focus object what kind of input it
- // expects, and configure the keyboard appropriately:
- QObject *focusObject = QGuiApplication::focusObject();
- if (!focusObject)
- return;
- QInputMethodQueryEvent queryEvent(Qt::ImEnabled | Qt::ImHints);
- if (!QCoreApplication::sendEvent(focusObject, &queryEvent))
- return;
- if (!queryEvent.value(Qt::ImEnabled).toBool())
- return;
-
- Qt::InputMethodHints hints = static_cast<Qt::InputMethodHints>(queryEvent.value(Qt::ImHints).toUInt());
-
- self.returnKeyType = (hints & Qt::ImhMultiLine) ? UIReturnKeyDefault : UIReturnKeyDone;
- self.secureTextEntry = BOOL(hints & Qt::ImhHiddenText);
- self.autocorrectionType = (hints & Qt::ImhNoPredictiveText) ?
- UITextAutocorrectionTypeNo : UITextAutocorrectionTypeDefault;
- self.spellCheckingType = (hints & Qt::ImhNoPredictiveText) ?
- UITextSpellCheckingTypeNo : UITextSpellCheckingTypeDefault;
-
- if (hints & Qt::ImhUppercaseOnly)
- self.autocapitalizationType = UITextAutocapitalizationTypeAllCharacters;
- else if (hints & Qt::ImhNoAutoUppercase)
- self.autocapitalizationType = UITextAutocapitalizationTypeNone;
- else
- self.autocapitalizationType = UITextAutocapitalizationTypeSentences;
-
- if (hints & Qt::ImhUrlCharactersOnly)
- self.keyboardType = UIKeyboardTypeURL;
- else if (hints & Qt::ImhEmailCharactersOnly)
- self.keyboardType = UIKeyboardTypeEmailAddress;
- else if (hints & Qt::ImhDigitsOnly)
- self.keyboardType = UIKeyboardTypeNumberPad;
- else if (hints & Qt::ImhFormattedNumbersOnly)
- self.keyboardType = UIKeyboardTypeDecimalPad;
- else if (hints & Qt::ImhDialableCharactersOnly)
- self.keyboardType = UIKeyboardTypeNumberPad;
- else
- self.keyboardType = UIKeyboardTypeDefault;
-}
-
@end
diff --git a/src/plugins/platforms/ios/qiostheme.h b/src/plugins/platforms/ios/qiostheme.h
index b03f65f556..16b330a030 100644
--- a/src/plugins/platforms/ios/qiostheme.h
+++ b/src/plugins/platforms/ios/qiostheme.h
@@ -1,40 +1,32 @@
/****************************************************************************
**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the plugins of the Qt Toolkit.
**
-** $QT_BEGIN_LICENSE:LGPL$
+** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
+** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3.0 as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 3.0 requirements will be
-** met: http://www.gnu.org/copyleft/gpl.html.
-**
-**
** $QT_END_LICENSE$
**
****************************************************************************/
@@ -55,6 +47,9 @@ public:
QVariant themeHint(ThemeHint hint) const;
+ QPlatformMenuItem* createPlatformMenuItem() const Q_DECL_OVERRIDE;
+ QPlatformMenu* createPlatformMenu() const Q_DECL_OVERRIDE;
+
const QFont *font(Font type = SystemFont) const;
static const char *name;
diff --git a/src/plugins/platforms/ios/qiostheme.mm b/src/plugins/platforms/ios/qiostheme.mm
index e51e97bd5a..cbeb157cf2 100644
--- a/src/plugins/platforms/ios/qiostheme.mm
+++ b/src/plugins/platforms/ios/qiostheme.mm
@@ -53,6 +53,8 @@
#include <UIKit/UIFont.h>
#include <UIKit/UIInterface.h>
+#include "qiosmenu.h"
+
QT_BEGIN_NAMESPACE
const char *QIOSTheme::name = "ios";
@@ -66,6 +68,16 @@ QIOSTheme::~QIOSTheme()
qDeleteAll(m_fonts);
}
+QPlatformMenuItem* QIOSTheme::createPlatformMenuItem() const
+{
+ return new QIOSMenuItem();
+}
+
+QPlatformMenu* QIOSTheme::createPlatformMenu() const
+{
+ return new QIOSMenu();
+}
+
QVariant QIOSTheme::themeHint(ThemeHint hint) const
{
switch (hint) {
diff --git a/src/plugins/platforms/ios/qiosviewcontroller.h b/src/plugins/platforms/ios/qiosviewcontroller.h
index a0017808d3..cbb28aec0d 100644
--- a/src/plugins/platforms/ios/qiosviewcontroller.h
+++ b/src/plugins/platforms/ios/qiosviewcontroller.h
@@ -1,47 +1,48 @@
/****************************************************************************
**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the plugins of the Qt Toolkit.
**
-** $QT_BEGIN_LICENSE:LGPL$
+** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
+** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3.0 as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 3.0 requirements will be
-** met: http://www.gnu.org/copyleft/gpl.html.
-**
-**
** $QT_END_LICENSE$
**
****************************************************************************/
#import <UIKit/UIKit.h>
-@interface QIOSViewController : UIViewController
-- (BOOL)prefersStatusBarHidden;
+class QIOSScreen;
+
+@interface QIOSViewController : UIViewController {
+ QIOSScreen *m_screen;
+}
+
+@property (nonatomic, assign) BOOL changingOrientation;
+@property (nonatomic, assign) BOOL prefersStatusBarHidden;
+
+- (id)initWithQIOSScreen:(QIOSScreen *)screen;
+- (void)updateProperties;
@end
diff --git a/src/plugins/platforms/ios/qiosviewcontroller.mm b/src/plugins/platforms/ios/qiosviewcontroller.mm
index 2fe679fc20..ca8d48bf6d 100644
--- a/src/plugins/platforms/ios/qiosviewcontroller.mm
+++ b/src/plugins/platforms/ios/qiosviewcontroller.mm
@@ -50,9 +50,179 @@
#include "qiosscreen.h"
#include "qiosglobal.h"
#include "qioswindow.h"
+#include "quiview.h"
+
+// -------------------------------------------------------------------------
+
+@interface QIOSDesktopManagerView : UIView
+@end
+
+@implementation QIOSDesktopManagerView
+
+- (void)layoutSubviews
+{
+ for (int i = int(self.subviews.count) - 1; i >= 0; --i) {
+ UIView *view = static_cast<UIView *>([self.subviews objectAtIndex:i]);
+ if (![view isKindOfClass:[QUIView class]])
+ continue;
+
+ [self layoutView: static_cast<QUIView *>(view)];
+ }
+}
+
+- (void)layoutView:(QUIView *)view
+{
+ QWindow *window = view.qwindow;
+ Q_ASSERT(window->handle());
+
+ // Re-apply window states to update geometry
+ if (window->windowState() & (Qt::WindowFullScreen | Qt::WindowMaximized))
+ window->handle()->setWindowState(window->windowState());
+}
+
+// Even if the root view controller has both wantsFullScreenLayout and
+// extendedLayoutIncludesOpaqueBars enabled, iOS will still push the root
+// view down 20 pixels (and shrink the view accordingly) when the in-call
+// statusbar is active (instead of updating the topLayoutGuide). Since
+// we treat the root view controller as our screen, we want to reflect
+// the in-call statusbar as a change in available geometry, not in screen
+// geometry. To simplify the screen geometry mapping code we reset the
+// view modifications that iOS does and take the statusbar height
+// explicitly into account in QIOSScreen::updateProperties().
+
+- (void)setFrame:(CGRect)newFrame
+{
+ [super setFrame:CGRectMake(0, 0, CGRectGetWidth(newFrame), CGRectGetHeight(self.window.bounds))];
+}
+
+- (void)setBounds:(CGRect)newBounds
+{
+ CGRect transformedWindowBounds = [self convertRect:self.window.bounds fromView:self.window];
+ [super setBounds:CGRectMake(0, 0, CGRectGetWidth(newBounds), CGRectGetHeight(transformedWindowBounds))];
+}
+
+- (void)setCenter:(CGPoint)newCenter
+{
+ Q_UNUSED(newCenter);
+ [super setCenter:self.window.center];
+}
+
+- (void)didMoveToWindow
+{
+ // The initial frame computed during startup may happen before the view has
+ // a window, meaning our calculations above will be wrong. We ensure that the
+ // frame is set correctly once we have a window to base our calulations on.
+ [self setFrame:self.window.bounds];
+}
+
+@end
+
+// -------------------------------------------------------------------------
@implementation QIOSViewController
+- (id)initWithQIOSScreen:(QIOSScreen *)screen
+{
+ if (self = [self init]) {
+ m_screen = screen;
+
+#if QT_IOS_DEPLOYMENT_TARGET_BELOW(__IPHONE_7_0)
+ QSysInfo::MacVersion iosVersion = QSysInfo::MacintoshVersion;
+
+ // We prefer to keep the root viewcontroller in fullscreen layout, so that
+ // we don't have to compensate for the viewcontroller position. This also
+ // gives us the same behavior on iOS 5/6 as on iOS 7, where full screen layout
+ // is the only way.
+ if (iosVersion < QSysInfo::MV_IOS_7_0)
+ self.wantsFullScreenLayout = YES;
+
+ // Use translucent statusbar by default on iOS6 iPhones (unless the user changed
+ // the default in the Info.plist), so that windows placed under the stausbar are
+ // still visible, just like on iOS7.
+ if (screen->uiScreen() == [UIScreen mainScreen]
+ && iosVersion >= QSysInfo::MV_IOS_6_0 && iosVersion < QSysInfo::MV_IOS_7_0
+ && [UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone
+ && [UIApplication sharedApplication].statusBarStyle == UIStatusBarStyleDefault)
+ [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleBlackTranslucent];
+#endif
+
+ self.changingOrientation = NO;
+
+ // Status bar may be initially hidden at startup through Info.plist
+ self.prefersStatusBarHidden = infoPlistValue(@"UIStatusBarHidden", false);
+
+ QObject::connect(qApp, &QGuiApplication::focusWindowChanged, [self]() {
+ [self updateProperties];
+ });
+ }
+
+ return self;
+}
+
+- (void)loadView
+{
+ self.view = [[[QIOSDesktopManagerView alloc] init] autorelease];
+}
+
+- (void)viewDidLoad
+{
+ [super viewDidLoad];
+
+ NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
+ [center addObserver:self selector:@selector(willChangeStatusBarFrame:)
+ name:UIApplicationWillChangeStatusBarFrameNotification
+ object:[UIApplication sharedApplication]];
+}
+
+- (void)viewDidUnload
+{
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:nil object:nil];
+ [super viewDidUnload];
+}
+
+// -------------------------------------------------------------------------
+
+- (void)updateProperties
+{
+ if (!isQtApplication())
+ return;
+
+ QWindow *focusWindow = QGuiApplication::focusWindow();
+
+ // If we don't have a focus window we leave the statusbar
+ // as is, so that the user can activate a new window with
+ // the same window state without the status bar jumping
+ // back and forth.
+ if (!focusWindow)
+ return;
+
+ // We only care about changes to focusWindow that involves our screen
+ if (!focusWindow->screen() || focusWindow->screen()->handle() != m_screen)
+ return;
+
+ // All decisions are based on the the top level window
+ focusWindow = qt_window_private(focusWindow)->topLevelWindow();
+
+ bool currentStatusBarVisibility = self.prefersStatusBarHidden;
+ self.prefersStatusBarHidden = focusWindow->windowState() == Qt::WindowFullScreen;
+ if (self.prefersStatusBarHidden != currentStatusBarVisibility) {
+#if QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_7_0)
+ if (QSysInfo::MacintoshVersion >= QSysInfo::MV_IOS_7_0) {
+ [self setNeedsStatusBarAppearanceUpdate];
+ } else
+#endif
+ {
+ [[UIApplication sharedApplication]
+ setStatusBarHidden:self.prefersStatusBarHidden
+ withAnimation:UIStatusBarAnimationNone];
+ }
+
+ [self.view setNeedsLayout];
+ }
+}
+
+// -------------------------------------------------------------------------
+
-(BOOL)shouldAutorotate
{
// Until a proper orientation and rotation API is in place, we always auto rotate.
@@ -77,16 +247,55 @@
}
#endif
-- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration
+- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)orientation duration:(NSTimeInterval)duration
{
+ Q_UNUSED(orientation);
Q_UNUSED(duration);
- Q_UNUSED(interfaceOrientation);
+ self.changingOrientation = YES;
+}
+
+- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)orientation
+{
+ Q_UNUSED(orientation);
+
+ self.changingOrientation = NO;
+}
+
+- (void)willChangeStatusBarFrame:(NSNotification*)notification
+{
+ Q_UNUSED(notification);
+
+ if (self.view.window.screen != [UIScreen mainScreen])
+ return;
+
+ // Orientation changes will already result in laying out subviews, so we don't
+ // need to do anything extra for frame changes during an orientation change.
+ // Technically we can receive another actual statusbar frame update during the
+ // orientation change that we should react to, but to simplify the logic we
+ // use a simple bool variable instead of a ignoreNextFrameChange approach.
+ if (self.changingOrientation)
+ return;
+
+ // UIKit doesn't have a delegate callback for statusbar changes that's run inside the
+ // animation block, like UIViewController's willAnimateRotationToInterfaceOrientation,
+ // nor does it expose a constant for the duration and easing of the animation. However,
+ // though poking at the various UIStatusBar methods, we can observe that the animation
+ // uses the default easing curve, and runs with a duration of 0.35 seconds.
+ static qreal kUIStatusBarAnimationDuration = 0.35;
+
+ [UIView animateWithDuration:kUIStatusBarAnimationDuration animations:^{
+ [self.view setNeedsLayout];
+ [self.view layoutIfNeeded];
+ }];
+}
+
+- (void)viewWillLayoutSubviews
+{
if (!QCoreApplication::instance())
- return; // FIXME: Store orientation for later (?)
+ return;
- QIOSScreen *qiosScreen = static_cast<QIOSScreen *>(QGuiApplication::primaryScreen()->handle());
- qiosScreen->updateProperties();
+ m_screen->updateProperties();
}
#if QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_7_0)
@@ -102,17 +311,5 @@
}
#endif
-- (BOOL)prefersStatusBarHidden
-{
- static bool hiddenFromPlist = infoPlistValue(@"UIStatusBarHidden", false);
- if (hiddenFromPlist)
- return YES;
- QWindow *focusWindow = QGuiApplication::focusWindow();
- if (!focusWindow)
- return [UIApplication sharedApplication].statusBarHidden;
-
- return qt_window_private(focusWindow)->topLevelWindow()->windowState() == Qt::WindowFullScreen;
-}
-
@end
diff --git a/src/plugins/platforms/ios/qioswindow.h b/src/plugins/platforms/ios/qioswindow.h
index 6b6892e6e4..5bbd76fb36 100644
--- a/src/plugins/platforms/ios/qioswindow.h
+++ b/src/plugins/platforms/ios/qioswindow.h
@@ -1,40 +1,32 @@
/****************************************************************************
**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the plugins of the Qt Toolkit.
**
-** $QT_BEGIN_LICENSE:LGPL$
+** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
+** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3.0 as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 3.0 requirements will be
-** met: http://www.gnu.org/copyleft/gpl.html.
-**
-**
** $QT_END_LICENSE$
**
****************************************************************************/
@@ -73,6 +65,7 @@ public:
void setParent(const QPlatformWindow *window);
void handleContentOrientationChange(Qt::ScreenOrientation orientation);
void setVisible(bool visible);
+ void setOpacity(qreal level) Q_DECL_OVERRIDE;
bool isExposed() const Q_DECL_OVERRIDE;
@@ -87,6 +80,8 @@ public:
WId winId() const { return WId(m_view); };
+ void clearAccessibleCache();
+
private:
void applicationStateChanged(Qt::ApplicationState state);
void applyGeometry(const QRect &rect);
diff --git a/src/plugins/platforms/ios/qioswindow.mm b/src/plugins/platforms/ios/qioswindow.mm
index 6f5c96cfc1..e29ff53876 100644
--- a/src/plugins/platforms/ios/qioswindow.mm
+++ b/src/plugins/platforms/ios/qioswindow.mm
@@ -39,323 +39,23 @@
**
****************************************************************************/
-#include "qiosglobal.h"
#include "qioswindow.h"
-#include "quiview.h"
+
+#include "qiosapplicationdelegate.h"
#include "qioscontext.h"
-#include "qiosinputcontext.h"
+#include "qiosglobal.h"
+#include "qiosintegration.h"
#include "qiosscreen.h"
-#include "qiosapplicationdelegate.h"
#include "qiosviewcontroller.h"
-#include "qiosintegration.h"
-#include <QtGui/private/qguiapplication_p.h>
+#include "quiview.h"
+
#include <QtGui/private/qwindow_p.h>
#include <qpa/qplatformintegration.h>
#import <QuartzCore/CAEAGLLayer.h>
-#include <QtGui/QKeyEvent>
-#include <qpa/qwindowsysteminterface.h>
-
#include <QtDebug>
-// Include category as an alternative to using -ObjC (Apple QA1490)
-#include "quiview_textinput.mm"
-
-@implementation QUIView
-
-@synthesize autocapitalizationType;
-@synthesize autocorrectionType;
-@synthesize enablesReturnKeyAutomatically;
-@synthesize keyboardAppearance;
-@synthesize keyboardType;
-@synthesize returnKeyType;
-@synthesize secureTextEntry;
-
-+ (Class)layerClass
-{
- return [CAEAGLLayer class];
-}
-
--(id)initWithQIOSWindow:(QIOSWindow *)window
-{
- if (self = [self initWithFrame:toCGRect(window->geometry())])
- m_qioswindow = window;
-
- return self;
-}
-
-- (id)initWithFrame:(CGRect)frame
-{
- if ((self = [super initWithFrame:frame])) {
- // Set up EAGL layer
- CAEAGLLayer *eaglLayer = static_cast<CAEAGLLayer *>(self.layer);
- eaglLayer.opaque = TRUE;
- eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
- [NSNumber numberWithBool:YES], kEAGLDrawablePropertyRetainedBacking,
- kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];
-
- if (isQtApplication())
- self.hidden = YES;
-
- self.multipleTouchEnabled = YES;
- m_inSendEventToFocusObject = NO;
- }
-
- return self;
-}
-
-- (void)willMoveToWindow:(UIWindow *)newWindow
-{
- // UIKIt will normally set the scale factor of a view to match the corresponding
- // screen scale factor, but views backed by CAEAGLLayers need to do this manually.
- self.contentScaleFactor = newWindow && newWindow.screen ?
- newWindow.screen.scale : [[UIScreen mainScreen] scale];
-
- // FIXME: Allow the scale factor to be customized through QSurfaceFormat.
-}
-
-- (void)didAddSubview:(UIView *)subview
-{
- if ([subview isKindOfClass:[QUIView class]])
- self.clipsToBounds = YES;
-}
-
-- (void)willRemoveSubview:(UIView *)subview
-{
- for (UIView *view in self.subviews) {
- if (view != subview && [view isKindOfClass:[QUIView class]])
- return;
- }
-
- self.clipsToBounds = NO;
-}
-
-- (void)setNeedsDisplay
-{
- [super setNeedsDisplay];
-
- // We didn't implement drawRect: so we have to manually
- // mark the layer as needing display.
- [self.layer setNeedsDisplay];
-}
-
-- (void)layoutSubviews
-{
- // This method is the de facto way to know that view has been resized,
- // or otherwise needs invalidation of its buffers. Note though that we
- // do not get this callback when the view just changes its position, so
- // the position of our QWindow (and platform window) will only get updated
- // when the size is also changed.
-
- if (!CGAffineTransformIsIdentity(self.transform))
- qWarning() << m_qioswindow->window()
- << "is backed by a UIView that has a transform set. This is not supported.";
-
- // The original geometry requested by setGeometry() might be different
- // from what we end up with after applying window constraints.
- QRect requestedGeometry = m_qioswindow->geometry();
-
- QRect actualGeometry;
- if (m_qioswindow->window()->isTopLevel()) {
- UIWindow *uiWindow = self.window;
- UIView *rootView = uiWindow.rootViewController.view;
- CGRect rootViewPositionInRelationToRootViewController =
- [rootView convertRect:uiWindow.bounds fromView:uiWindow];
-
- actualGeometry = fromCGRect(CGRectOffset([self.superview convertRect:self.frame toView:rootView],
- -rootViewPositionInRelationToRootViewController.origin.x,
- -rootViewPositionInRelationToRootViewController.origin.y
- + rootView.bounds.origin.y)).toRect();
- } else {
- actualGeometry = fromCGRect(self.frame).toRect();
- }
-
- // Persist the actual/new geometry so that QWindow::geometry() can
- // be queried on the resize event.
- m_qioswindow->QPlatformWindow::setGeometry(actualGeometry);
-
- QRect previousGeometry = requestedGeometry != actualGeometry ?
- requestedGeometry : qt_window_private(m_qioswindow->window())->geometry;
-
- QWindowSystemInterface::handleGeometryChange(m_qioswindow->window(), actualGeometry, previousGeometry);
- QWindowSystemInterface::flushWindowSystemEvents();
-
- if (actualGeometry.size() != previousGeometry.size()) {
- // Trigger expose event on resize
- [self setNeedsDisplay];
-
- // A new size means we also need to resize the FBO's corresponding buffers,
- // but we defer that to when the application calls makeCurrent.
- }
-}
-
-- (void)displayLayer:(CALayer *)layer
-{
- Q_UNUSED(layer);
- Q_ASSERT(layer == self.layer);
-
- [self sendUpdatedExposeEvent];
-}
-
-- (void)sendUpdatedExposeEvent
-{
- QRegion region;
-
- if (m_qioswindow->isExposed()) {
- QSize bounds = fromCGRect(self.layer.bounds).toRect().size();
-
- Q_ASSERT(m_qioswindow->geometry().size() == bounds);
- Q_ASSERT(self.hidden == !m_qioswindow->window()->isVisible());
-
- region = QRect(QPoint(), bounds);
- }
-
- QWindowSystemInterface::handleExposeEvent(m_qioswindow->window(), region);
- QWindowSystemInterface::flushWindowSystemEvents();
-}
-
-- (void)updateTouchList:(NSSet *)touches withState:(Qt::TouchPointState)state
-{
- // We deliver touch events in global coordinates. But global in this respect
- // means the same coordinate system that we use for describing the geometry
- // of the top level QWindow we're inside. And that would be the coordinate
- // system of the superview of the UIView that backs that window:
- QPlatformWindow *topLevel = m_qioswindow;
- while (QPlatformWindow *topLevelParent = topLevel->parent())
- topLevel = topLevelParent;
- UIView *rootView = reinterpret_cast<UIView *>(topLevel->winId()).superview;
- CGSize rootViewSize = rootView.frame.size;
-
- foreach (UITouch *uiTouch, m_activeTouches.keys()) {
- QWindowSystemInterface::TouchPoint &touchPoint = m_activeTouches[uiTouch];
- if (![touches containsObject:uiTouch]) {
- touchPoint.state = Qt::TouchPointStationary;
- } else {
- touchPoint.state = state;
- touchPoint.pressure = (state == Qt::TouchPointReleased) ? 0.0 : 1.0;
- QPoint touchPos = fromCGPoint([uiTouch locationInView:rootView]).toPoint();
- touchPoint.area = QRectF(touchPos, QSize(0, 0));
- touchPoint.normalPosition = QPointF(touchPos.x() / rootViewSize.width, touchPos.y() / rootViewSize.height);
- }
- }
-}
-
-- (void) sendTouchEventWithTimestamp:(ulong)timeStamp
-{
- // Send touch event synchronously
- QIOSIntegration *iosIntegration = static_cast<QIOSIntegration *>(QGuiApplicationPrivate::platformIntegration());
- QWindowSystemInterface::handleTouchEvent(m_qioswindow->window(), timeStamp, iosIntegration->touchDevice(), m_activeTouches.values());
- QWindowSystemInterface::flushWindowSystemEvents();
-}
-
-- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
-{
- // UIKit generates [Began -> Moved -> Ended] event sequences for
- // each touch point. Internally we keep a hashmap of active UITouch
- // points to QWindowSystemInterface::TouchPoints, and assigns each TouchPoint
- // an id for use by Qt.
- for (UITouch *touch in touches) {
- Q_ASSERT(!m_activeTouches.contains(touch));
- m_activeTouches[touch].id = m_nextTouchId++;
- }
-
- if (m_activeTouches.size() == 1) {
- QPlatformWindow *topLevel = m_qioswindow;
- while (QPlatformWindow *p = topLevel->parent())
- topLevel = p;
- if (topLevel->window() != QGuiApplication::focusWindow())
- topLevel->requestActivateWindow();
- }
-
- [self updateTouchList:touches withState:Qt::TouchPointPressed];
- [self sendTouchEventWithTimestamp:ulong(event.timestamp * 1000)];
-}
-
-- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
-{
- [self updateTouchList:touches withState:Qt::TouchPointMoved];
- [self sendTouchEventWithTimestamp:ulong(event.timestamp * 1000)];
-}
-
-- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
-{
- [self updateTouchList:touches withState:Qt::TouchPointReleased];
- [self sendTouchEventWithTimestamp:ulong(event.timestamp * 1000)];
-
- // Remove ended touch points from the active set:
- for (UITouch *touch in touches)
- m_activeTouches.remove(touch);
- if (m_activeTouches.isEmpty())
- m_nextTouchId = 0;
-}
-
-- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
-{
- if (m_activeTouches.isEmpty())
- return;
-
- // When four-finger swiping, we get a touchesCancelled callback
- // which includes all four touch points. The swipe gesture is
- // then active until all four touches have been released, and
- // we start getting touchesBegan events again.
-
- // When five-finger pinching, we also get a touchesCancelled
- // callback with all five touch points, but the pinch gesture
- // ends when the second to last finger is released from the
- // screen. The last finger will not emit any more touch
- // events, _but_, will contribute to starting another pinch
- // gesture. That second pinch gesture will _not_ trigger a
- // touchesCancelled event when starting, but as each finger
- // is released, and we may get touchesMoved events for the
- // remaining fingers. [event allTouches] also contains one
- // less touch point than it should, so this behavior is
- // likely a bug in the iOS system gesture recognizer, but we
- // have to take it into account when maintaining the Qt state.
- // We do this by assuming that there are no cases where a
- // sub-set of the active touch events are intentionally cancelled.
-
- if (touches && (static_cast<NSInteger>([touches count]) != m_activeTouches.count()))
- qWarning("Subset of active touches cancelled by UIKit");
-
- m_activeTouches.clear();
- m_nextTouchId = 0;
-
- NSTimeInterval timestamp = event ? event.timestamp : [[NSProcessInfo processInfo] systemUptime];
-
- // Send cancel touch event synchronously
- QIOSIntegration *iosIntegration = static_cast<QIOSIntegration *>(QGuiApplicationPrivate::platformIntegration());
- QWindowSystemInterface::handleTouchCancelEvent(m_qioswindow->window(), ulong(timestamp * 1000), iosIntegration->touchDevice());
- QWindowSystemInterface::flushWindowSystemEvents();
-}
-
-@end
-
-@implementation UIView (QIOS)
-
-- (QWindow *)qwindow
-{
- if ([self isKindOfClass:[QUIView class]]) {
- if (QIOSWindow *w = static_cast<QUIView *>(self)->m_qioswindow)
- return w->window();
- }
- return nil;
-}
-
-- (UIViewController *)viewController
-{
- id responder = self;
- while ((responder = [responder nextResponder])) {
- if ([responder isKindOfClass:UIViewController.class])
- return responder;
- }
- return nil;
-}
-
-@end
-
-QT_BEGIN_NAMESPACE
-
QIOSWindow::QIOSWindow(QWindow *window)
: QPlatformWindow(window)
, m_view([[QUIView alloc] initWithQIOSWindow:this])
@@ -374,6 +74,7 @@ QIOSWindow::QIOSWindow(QWindow *window)
screen()->availableGeometry().width(), screen()->availableGeometry().height());
setWindowState(window->windowState());
+ setOpacity(window->opacity());
}
QIOSWindow::~QIOSWindow()
@@ -385,6 +86,7 @@ QIOSWindow::~QIOSWindow()
// cancellation of all touch events.
[m_view touchesCancelled:0 withEvent:0];
+ clearAccessibleCache();
m_view->m_qioswindow = 0;
[m_view removeFromSuperview];
[m_view release];
@@ -417,7 +119,6 @@ void QIOSWindow::setVisible(bool visible)
if (visible) {
requestActivateWindow();
- static_cast<QIOSScreen *>(screen())->updateStatusBarVisibility();
} else {
// Activate top-most visible QWindow:
NSArray *subviews = m_view.viewController.view.subviews;
@@ -434,6 +135,11 @@ void QIOSWindow::setVisible(bool visible)
}
}
+void QIOSWindow::setOpacity(qreal level)
+{
+ m_view.alpha = qBound(0.0, level, 1.0);
+}
+
void QIOSWindow::setGeometry(const QRect &rect)
{
m_normalGeometry = rect;
@@ -471,24 +177,7 @@ void QIOSWindow::applyGeometry(const QRect &rect)
// The baseclass takes care of persisting this for us.
QPlatformWindow::setGeometry(rect);
- if (window()->isTopLevel()) {
- // The QWindow is in QScreen coordinates, which maps to a possibly rotated root-view-controller.
- // Since the root-view-controller might be translated in relation to the UIWindow, we need to
- // check specifically for that and compensate. Also check if the root view has been scrolled
- // as a result of the keyboard being open.
- UIWindow *uiWindow = m_view.window;
- UIView *rootView = uiWindow.rootViewController.view;
- CGRect rootViewPositionInRelationToRootViewController =
- [rootView convertRect:uiWindow.bounds fromView:uiWindow];
-
- m_view.frame = CGRectOffset([m_view.superview convertRect:toCGRect(rect) fromView:rootView],
- rootViewPositionInRelationToRootViewController.origin.x,
- rootViewPositionInRelationToRootViewController.origin.y
- + rootView.bounds.origin.y);
- } else {
- // Easy, in parent's coordinates
- m_view.frame = toCGRect(rect);
- }
+ m_view.frame = toCGRect(rect);
// iOS will automatically trigger -[layoutSubviews:] for resize,
// but not for move, so we force it just in case.
@@ -512,7 +201,7 @@ void QIOSWindow::setWindowState(Qt::WindowState state)
qt_window_private(window())->windowState = state;
if (window()->isTopLevel() && window()->isVisible() && window()->isActive())
- static_cast<QIOSScreen *>(screen())->updateStatusBarVisibility();
+ [m_view.qtViewController updateProperties];
switch (state) {
case Qt::WindowNoState:
@@ -557,12 +246,12 @@ void QIOSWindow::requestActivateWindow()
if (blockedByModal())
return;
+ Q_ASSERT(m_view.window);
[m_view.window makeKeyWindow];
+ [m_view becomeFirstResponder];
if (window()->isTopLevel())
raise();
-
- QWindowSystemInterface::handleWindowActivated(window());
}
void QIOSWindow::raiseOrLower(bool raise)
@@ -634,6 +323,11 @@ qreal QIOSWindow::devicePixelRatio() const
return m_view.contentScaleFactor;
}
+void QIOSWindow::clearAccessibleCache()
+{
+ [m_view clearAccessibleCache];
+}
+
#include "moc_qioswindow.cpp"
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/ios/quiaccessibilityelement.h b/src/plugins/platforms/ios/quiaccessibilityelement.h
new file mode 100644
index 0000000000..31ba5af870
--- /dev/null
+++ b/src/plugins/platforms/ios/quiaccessibilityelement.h
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QUIACCESSIBILITYELEMENT_H
+#define QUIACCESSIBILITYELEMENT_H
+
+#import <UIKit/UIKit.h>
+#import <QtGui/QtGui>
+
+@interface QMacAccessibilityElement : UIAccessibilityElement
+{}
+
+@property (readonly) QAccessible::Id axid;
+
+- (id) initWithId: (QAccessible::Id) anId withAccessibilityContainer: (id) view;
++ (QMacAccessibilityElement *) elementWithId: (QAccessible::Id) anId withAccessibilityContainer: (id) view;
+
+@end
+
+#endif
diff --git a/src/plugins/platforms/ios/quiaccessibilityelement.mm b/src/plugins/platforms/ios/quiaccessibilityelement.mm
new file mode 100644
index 0000000000..331c38460c
--- /dev/null
+++ b/src/plugins/platforms/ios/quiaccessibilityelement.mm
@@ -0,0 +1,193 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "quiaccessibilityelement.h"
+
+#include "private/qaccessiblecache_p.h"
+
+@implementation QMacAccessibilityElement
+
+- (id) initWithId: (QAccessible::Id) anId withAccessibilityContainer: (id) view
+{
+ Q_ASSERT((int)anId < 0);
+ self = [super initWithAccessibilityContainer: view];
+ if (self)
+ _axid = anId;
+
+ return self;
+}
+
++ (id) elementWithId: (QAccessible::Id) anId withAccessibilityContainer: (id) view
+{
+ Q_ASSERT(anId);
+ if (!anId)
+ return nil;
+
+ QAccessibleCache *cache = QAccessibleCache::instance();
+
+ QMacAccessibilityElement *element = cache->elementForId(anId);
+ if (!element) {
+ Q_ASSERT(QAccessible::accessibleInterface(anId));
+ element = [[self alloc] initWithId:anId withAccessibilityContainer: view];
+ cache->insertElement(anId, element);
+ }
+ return element;
+}
+
+- (void) invalidate
+{
+ [self release];
+}
+
+- (BOOL) isAccessibilityElement
+{
+ return YES;
+}
+
+- (NSString*) accessibilityLabel
+{
+ QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
+ if (!iface) {
+ qWarning() << "invalid accessible interface for: " << self.axid;
+ return @"";
+ }
+
+ return iface->text(QAccessible::Name).toNSString();
+}
+
+- (NSString*) accessibilityHint
+{
+ QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
+ if (!iface) {
+ qWarning() << "invalid accessible interface for: " << self.axid;
+ return @"";
+ }
+ return iface->text(QAccessible::Description).toNSString();
+}
+
+- (NSString*) accessibilityValue
+{
+ QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
+ if (!iface) {
+ qWarning() << "invalid accessible interface for: " << self.axid;
+ return @"";
+ }
+
+ QAccessible::State state = iface->state();
+
+ if (state.checkable)
+ return state.checked ? @"checked" : @"unchecked"; // FIXME: translation
+
+ QAccessibleValueInterface *val = iface->valueInterface();
+ if (val) {
+ return val->currentValue().toString().toNSString();
+ } else if (QAccessibleTextInterface *text = iface->textInterface()) {
+ // FIXME doesn't work?
+ return text->text(0, text->characterCount() - 1).toNSString();
+ }
+
+ return [super accessibilityHint];
+}
+
+- (CGRect) accessibilityFrame
+{
+ QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
+ if (!iface) {
+ qWarning() << "invalid accessible interface for: " << self.axid;
+ return CGRect();
+ }
+
+ QRect rect = iface->rect();
+ return CGRectMake(rect.x(), rect.y(), rect.width(), rect.height());
+}
+
+- (UIAccessibilityTraits) accessibilityTraits
+{
+ UIAccessibilityTraits traits = UIAccessibilityTraitNone;
+
+ QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
+ if (!iface) {
+ qWarning() << "invalid accessible interface for: " << self.axid;
+ return traits;
+ }
+ QAccessible::State state = iface->state();
+ if (state.disabled)
+ traits |= UIAccessibilityTraitNotEnabled;
+
+ if (iface->role() == QAccessible::Button)
+ traits |= UIAccessibilityTraitButton;
+
+ if (iface->valueInterface())
+ traits |= UIAccessibilityTraitAdjustable;
+
+ return traits;
+}
+
+- (BOOL) accessibilityActivate
+{
+ QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
+ if (QAccessibleActionInterface *action = iface->actionInterface()) {
+ if (action->actionNames().contains(QAccessibleActionInterface::pressAction())) {
+ action->doAction(QAccessibleActionInterface::pressAction());
+ return YES;
+ } else if (action->actionNames().contains(QAccessibleActionInterface::showMenuAction())) {
+ action->doAction(QAccessibleActionInterface::showMenuAction());
+ return YES;
+ }
+ }
+ return NO; // fall back to sending mouse clicks
+}
+
+- (void) accessibilityIncrement
+{
+ QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
+ if (QAccessibleActionInterface *action = iface->actionInterface())
+ action->doAction(QAccessibleActionInterface::increaseAction());
+}
+
+- (void) accessibilityDecrement
+{
+ QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid);
+ if (QAccessibleActionInterface *action = iface->actionInterface())
+ action->doAction(QAccessibleActionInterface::decreaseAction());
+}
+
+@end
diff --git a/src/plugins/platforms/ios/quiview.h b/src/plugins/platforms/ios/quiview.h
index 122e7c604b..fe71e5bed7 100644
--- a/src/plugins/platforms/ios/quiview.h
+++ b/src/plugins/platforms/ios/quiview.h
@@ -5,75 +5,64 @@
**
** This file is part of the plugins of the Qt Toolkit.
**
-** $QT_BEGIN_LICENSE:LGPL$
+** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
+** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3.0 as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 3.0 requirements will be
-** met: http://www.gnu.org/copyleft/gpl.html.
-**
-**
** $QT_END_LICENSE$
**
****************************************************************************/
#import <UIKit/UIKit.h>
-#include "qioswindow.h"
+
+#include <qhash.h>
+#include <qstring.h>
+
+#include <qpa/qwindowsysteminterface.h>
+
+class QIOSWindow;
+
+@class QIOSViewController;
@interface QUIView : UIView
{
-@public
- UITextAutocapitalizationType autocapitalizationType;
- UITextAutocorrectionType autocorrectionType;
- BOOL enablesReturnKeyAutomatically;
- UIKeyboardAppearance keyboardAppearance;
- UIKeyboardType keyboardType;
- UIReturnKeyType returnKeyType;
- BOOL secureTextEntry;
+ @public
QIOSWindow *m_qioswindow;
+ @private
QHash<UITouch *, QWindowSystemInterface::TouchPoint> m_activeTouches;
int m_nextTouchId;
- QString m_markedText;
- BOOL m_inSendEventToFocusObject;
-}
-@property(nonatomic, assign) id<UITextInputDelegate> inputDelegate;
-@property(nonatomic) UITextAutocapitalizationType autocapitalizationType;
-@property(nonatomic) UITextAutocorrectionType autocorrectionType;
-@property(nonatomic) UITextSpellCheckingType spellCheckingType;
-@property(nonatomic) BOOL enablesReturnKeyAutomatically;
-@property(nonatomic) UIKeyboardAppearance keyboardAppearance;
-@property(nonatomic) UIKeyboardType keyboardType;
-@property(nonatomic) UIReturnKeyType returnKeyType;
-@property(nonatomic, getter=isSecureTextEntry) BOOL secureTextEntry;
+ @private
+ NSMutableArray *m_accessibleElements;
+};
+
+- (id)initWithQIOSWindow:(QIOSWindow *)window;
+- (void)sendUpdatedExposeEvent;
+@end
+@interface QUIView (Accessibility)
+- (void)clearAccessibleCache;
@end
-@interface QUIView (TextInput) <UITextInput>
-- (void)updateInputMethodWithQuery:(Qt::InputMethodQueries)query;
-- (void)reset;
-- (void)commit;
-+ (bool)inUpdateKeyboardLayout;
+@interface QUIView (QtHelpers)
+- (QIOSViewController*)qtViewController;
@end
+
diff --git a/src/plugins/platforms/ios/quiview.mm b/src/plugins/platforms/ios/quiview.mm
new file mode 100644
index 0000000000..200b07b5fc
--- /dev/null
+++ b/src/plugins/platforms/ios/quiview.mm
@@ -0,0 +1,385 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "quiview.h"
+
+#include "qiosglobal.h"
+#include "qiosintegration.h"
+#include "qiosviewcontroller.h"
+#include "qioswindow.h"
+#include "qiosmenu.h"
+
+#include <QtGui/private/qguiapplication_p.h>
+#include <QtGui/private/qwindow_p.h>
+
+@implementation QUIView
+
++ (Class)layerClass
+{
+ return [CAEAGLLayer class];
+}
+
+-(id)initWithQIOSWindow:(QIOSWindow *)window
+{
+ if (self = [self initWithFrame:toCGRect(window->geometry())])
+ m_qioswindow = window;
+
+ m_accessibleElements = [[NSMutableArray alloc] init];
+ return self;
+}
+
+- (id)initWithFrame:(CGRect)frame
+{
+ if ((self = [super initWithFrame:frame])) {
+ // Set up EAGL layer
+ CAEAGLLayer *eaglLayer = static_cast<CAEAGLLayer *>(self.layer);
+ eaglLayer.opaque = TRUE;
+ eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSNumber numberWithBool:YES], kEAGLDrawablePropertyRetainedBacking,
+ kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];
+
+ if (isQtApplication())
+ self.hidden = YES;
+
+ self.multipleTouchEnabled = YES;
+ }
+
+ return self;
+}
+
+- (void)willMoveToWindow:(UIWindow *)newWindow
+{
+ // UIKIt will normally set the scale factor of a view to match the corresponding
+ // screen scale factor, but views backed by CAEAGLLayers need to do this manually.
+ self.contentScaleFactor = newWindow && newWindow.screen ?
+ newWindow.screen.scale : [[UIScreen mainScreen] scale];
+
+ // FIXME: Allow the scale factor to be customized through QSurfaceFormat.
+}
+
+- (void)didAddSubview:(UIView *)subview
+{
+ if ([subview isKindOfClass:[QUIView class]])
+ self.clipsToBounds = YES;
+}
+
+- (void)willRemoveSubview:(UIView *)subview
+{
+ for (UIView *view in self.subviews) {
+ if (view != subview && [view isKindOfClass:[QUIView class]])
+ return;
+ }
+
+ self.clipsToBounds = NO;
+}
+
+- (void)setNeedsDisplay
+{
+ [super setNeedsDisplay];
+
+ // We didn't implement drawRect: so we have to manually
+ // mark the layer as needing display.
+ [self.layer setNeedsDisplay];
+}
+
+- (void)layoutSubviews
+{
+ // This method is the de facto way to know that view has been resized,
+ // or otherwise needs invalidation of its buffers. Note though that we
+ // do not get this callback when the view just changes its position, so
+ // the position of our QWindow (and platform window) will only get updated
+ // when the size is also changed.
+
+ if (!CGAffineTransformIsIdentity(self.transform))
+ qWarning() << m_qioswindow->window()
+ << "is backed by a UIView that has a transform set. This is not supported.";
+
+ // The original geometry requested by setGeometry() might be different
+ // from what we end up with after applying window constraints.
+ QRect requestedGeometry = m_qioswindow->geometry();
+
+ QRect actualGeometry = fromCGRect(self.frame).toRect();
+
+ // Persist the actual/new geometry so that QWindow::geometry() can
+ // be queried on the resize event.
+ m_qioswindow->QPlatformWindow::setGeometry(actualGeometry);
+
+ QRect previousGeometry = requestedGeometry != actualGeometry ?
+ requestedGeometry : qt_window_private(m_qioswindow->window())->geometry;
+
+ QWindowSystemInterface::handleGeometryChange(m_qioswindow->window(), actualGeometry, previousGeometry);
+ QWindowSystemInterface::flushWindowSystemEvents();
+
+ if (actualGeometry.size() != previousGeometry.size()) {
+ // Trigger expose event on resize
+ [self setNeedsDisplay];
+
+ // A new size means we also need to resize the FBO's corresponding buffers,
+ // but we defer that to when the application calls makeCurrent.
+ }
+}
+
+- (void)displayLayer:(CALayer *)layer
+{
+ Q_UNUSED(layer);
+ Q_ASSERT(layer == self.layer);
+
+ [self sendUpdatedExposeEvent];
+}
+
+- (void)sendUpdatedExposeEvent
+{
+ QRegion region;
+
+ if (m_qioswindow->isExposed()) {
+ QSize bounds = fromCGRect(self.layer.bounds).toRect().size();
+
+ Q_ASSERT(m_qioswindow->geometry().size() == bounds);
+ Q_ASSERT(self.hidden == !m_qioswindow->window()->isVisible());
+
+ region = QRect(QPoint(), bounds);
+ }
+
+ QWindowSystemInterface::handleExposeEvent(m_qioswindow->window(), region);
+ QWindowSystemInterface::flushWindowSystemEvents();
+}
+
+// -------------------------------------------------------------------------
+
+- (BOOL)canBecomeFirstResponder
+{
+ return YES;
+}
+
+- (BOOL)becomeFirstResponder
+{
+ if ([super becomeFirstResponder]) {
+ QWindowSystemInterface::handleWindowActivated(m_qioswindow->window());
+ QWindowSystemInterface::flushWindowSystemEvents();
+
+ return YES;
+ }
+
+ return NO;
+}
+
+- (BOOL)resignFirstResponder
+{
+ if ([super resignFirstResponder]) {
+ // We don't want to send window deactivation in case we're in the process
+ // of activating another window. The handleWindowActivated of the activation
+ // will take care of both.
+ dispatch_async(dispatch_get_main_queue (), ^{
+ if (![[UIResponder currentFirstResponder] isKindOfClass:[QUIView class]]) {
+ QWindowSystemInterface::handleWindowActivated(0);
+ QWindowSystemInterface::flushWindowSystemEvents();
+ }
+ });
+
+ return YES;
+ }
+
+ return NO;
+}
+
+// -------------------------------------------------------------------------
+
+
+- (void)updateTouchList:(NSSet *)touches withState:(Qt::TouchPointState)state
+{
+ foreach (UITouch *uiTouch, m_activeTouches.keys()) {
+ QWindowSystemInterface::TouchPoint &touchPoint = m_activeTouches[uiTouch];
+ if (![touches containsObject:uiTouch]) {
+ touchPoint.state = Qt::TouchPointStationary;
+ } else {
+ touchPoint.state = state;
+
+ // Touch positions are expected to be in QScreen global coordinates, and
+ // as we already have the QWindow positioned at the right place, we can
+ // just map from the local view position to global coordinates.
+ QPoint localViewPosition = fromCGPoint([uiTouch locationInView:self]).toPoint();
+ QPoint globalScreenPosition = m_qioswindow->mapToGlobal(localViewPosition);
+
+ touchPoint.area = QRectF(globalScreenPosition, QSize(0, 0));
+
+ // FIXME: Do we really need to support QTouchDevice::NormalizedPosition?
+ QSize screenSize = m_qioswindow->screen()->geometry().size();
+ touchPoint.normalPosition = QPointF(globalScreenPosition.x() / screenSize.width(),
+ globalScreenPosition.y() / screenSize.height());
+
+ // We don't claim that our touch device supports QTouchDevice::Pressure,
+ // but fill in a meaningfull value in case clients use it anyways.
+ touchPoint.pressure = (state == Qt::TouchPointReleased) ? 0.0 : 1.0;
+ }
+ }
+}
+
+- (void) sendTouchEventWithTimestamp:(ulong)timeStamp
+{
+ // Send touch event synchronously
+ QIOSIntegration *iosIntegration = QIOSIntegration::instance();
+ QWindowSystemInterface::handleTouchEvent(m_qioswindow->window(), timeStamp, iosIntegration->touchDevice(), m_activeTouches.values());
+ QWindowSystemInterface::flushWindowSystemEvents();
+}
+
+- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
+{
+ // UIKit generates [Began -> Moved -> Ended] event sequences for
+ // each touch point. Internally we keep a hashmap of active UITouch
+ // points to QWindowSystemInterface::TouchPoints, and assigns each TouchPoint
+ // an id for use by Qt.
+ for (UITouch *touch in touches) {
+ Q_ASSERT(!m_activeTouches.contains(touch));
+ m_activeTouches[touch].id = m_nextTouchId++;
+ }
+
+ if (m_activeTouches.size() == 1) {
+ QPlatformWindow *topLevel = m_qioswindow;
+ while (QPlatformWindow *p = topLevel->parent())
+ topLevel = p;
+ if (topLevel->window() != QGuiApplication::focusWindow())
+ topLevel->requestActivateWindow();
+ }
+
+ [self updateTouchList:touches withState:Qt::TouchPointPressed];
+ [self sendTouchEventWithTimestamp:ulong(event.timestamp * 1000)];
+}
+
+- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
+{
+ [self updateTouchList:touches withState:Qt::TouchPointMoved];
+ [self sendTouchEventWithTimestamp:ulong(event.timestamp * 1000)];
+}
+
+- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
+{
+ [self updateTouchList:touches withState:Qt::TouchPointReleased];
+ [self sendTouchEventWithTimestamp:ulong(event.timestamp * 1000)];
+
+ // Remove ended touch points from the active set:
+ for (UITouch *touch in touches)
+ m_activeTouches.remove(touch);
+ if (m_activeTouches.isEmpty())
+ m_nextTouchId = 0;
+}
+
+- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
+{
+ if (m_activeTouches.isEmpty())
+ return;
+
+ // When four-finger swiping, we get a touchesCancelled callback
+ // which includes all four touch points. The swipe gesture is
+ // then active until all four touches have been released, and
+ // we start getting touchesBegan events again.
+
+ // When five-finger pinching, we also get a touchesCancelled
+ // callback with all five touch points, but the pinch gesture
+ // ends when the second to last finger is released from the
+ // screen. The last finger will not emit any more touch
+ // events, _but_, will contribute to starting another pinch
+ // gesture. That second pinch gesture will _not_ trigger a
+ // touchesCancelled event when starting, but as each finger
+ // is released, and we may get touchesMoved events for the
+ // remaining fingers. [event allTouches] also contains one
+ // less touch point than it should, so this behavior is
+ // likely a bug in the iOS system gesture recognizer, but we
+ // have to take it into account when maintaining the Qt state.
+ // We do this by assuming that there are no cases where a
+ // sub-set of the active touch events are intentionally cancelled.
+
+ if (touches && (static_cast<NSInteger>([touches count]) != m_activeTouches.count()))
+ qWarning("Subset of active touches cancelled by UIKit");
+
+ m_activeTouches.clear();
+ m_nextTouchId = 0;
+
+ NSTimeInterval timestamp = event ? event.timestamp : [[NSProcessInfo processInfo] systemUptime];
+
+ // Send cancel touch event synchronously
+ QIOSIntegration *iosIntegration = static_cast<QIOSIntegration *>(QGuiApplicationPrivate::platformIntegration());
+ QWindowSystemInterface::handleTouchCancelEvent(m_qioswindow->window(), ulong(timestamp * 1000), iosIntegration->touchDevice());
+ QWindowSystemInterface::flushWindowSystemEvents();
+}
+
+- (id)targetForAction:(SEL)action withSender:(id)sender
+{
+ // Check first if QIOSMenu should handle the action before continuing up the responder chain
+ id target = [QIOSMenu::menuActionTarget() targetForAction:action withSender:sender];
+ return target ? target : [super targetForAction:action withSender:sender];
+}
+
+@end
+
+@implementation UIView (QtHelpers)
+
+- (QWindow *)qwindow
+{
+ if ([self isKindOfClass:[QUIView class]]) {
+ if (QIOSWindow *w = static_cast<QUIView *>(self)->m_qioswindow)
+ return w->window();
+ }
+ return nil;
+}
+
+- (UIViewController *)viewController
+{
+ id responder = self;
+ while ((responder = [responder nextResponder])) {
+ if ([responder isKindOfClass:UIViewController.class])
+ return responder;
+ }
+ return nil;
+}
+
+- (QIOSViewController*)qtViewController
+{
+ UIViewController *vc = self.viewController;
+ if ([vc isKindOfClass:QIOSViewController.class])
+ return static_cast<QIOSViewController *>(vc);
+
+ return nil;
+}
+
+@end
+
+// Include category as an alternative to using -ObjC (Apple QA1490)
+#include "quiview_accessibility.mm"
diff --git a/src/plugins/platforms/ios/quiview_accessibility.mm b/src/plugins/platforms/ios/quiview_accessibility.mm
new file mode 100644
index 0000000000..6565e08302
--- /dev/null
+++ b/src/plugins/platforms/ios/quiview_accessibility.mm
@@ -0,0 +1,117 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qiosplatformaccessibility.h"
+#include "quiaccessibilityelement.h"
+
+#include <QtGui/private/qguiapplication_p.h>
+
+@implementation QUIView (Accessibility)
+
+- (void)createAccessibleElement:(QAccessibleInterface *)iface
+{
+ if (!iface || iface->state().invisible)
+ return;
+ QAccessible::Id accessibleId = QAccessible::uniqueId(iface);
+ UIAccessibilityElement *elem = [[QMacAccessibilityElement alloc] initWithId: accessibleId withAccessibilityContainer: self];
+ [m_accessibleElements addObject: elem];
+}
+
+- (void)createAccessibleContainer:(QAccessibleInterface *)iface
+{
+ if (!iface)
+ return;
+
+ if (iface->childCount() == 0) {
+ [self createAccessibleElement: iface];
+ } else {
+ for (int i = 0; i < iface->childCount(); ++i)
+ [self createAccessibleContainer: iface->child(i)];
+ }
+}
+
+- (void)initAccessibility
+{
+ static bool init = false;
+ if (!init)
+ QGuiApplicationPrivate::platformIntegration()->accessibility()->setActive(true);
+ init = true;
+
+ if ([m_accessibleElements count])
+ return;
+
+ QWindow *win = m_qioswindow->window();
+ QAccessibleInterface *iface = win->accessibleRoot();
+ if (iface)
+ [self createAccessibleContainer: iface];
+}
+
+- (void)clearAccessibleCache
+{
+ [m_accessibleElements removeAllObjects];
+ UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, @"");
+}
+
+// this is a container, returning yes here means the functions below will never be called
+- (BOOL)isAccessibilityElement
+{
+ return NO;
+}
+
+- (NSInteger)accessibilityElementCount
+{
+ [self initAccessibility];
+ return [m_accessibleElements count];
+}
+
+- (id)accessibilityElementAtIndex:(NSInteger)index
+{
+ [self initAccessibility];
+ return m_accessibleElements[index];
+}
+
+- (NSInteger)indexOfAccessibilityElement:(id)element
+{
+ [self initAccessibility];
+ return [m_accessibleElements indexOfObject:element];
+}
+
+@end