summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTor Arne Vestbø <tor.arne.vestbo@digia.com>2014-11-25 15:08:04 +0100
committerTor Arne Vestbø <tor.arne.vestbo@digia.com>2014-12-02 10:56:47 +0100
commitc59c8ddf1f98938441925e01d9931c3c1eb3f5cc (patch)
tree54e37b980eee49c652760709f56f2e7aa3af4f9c
parent1018502722ee70dd077304e35ff4600853143571 (diff)
iOS: Refactor QIOSKeyboardListener implementation and usage
QIOSKeyboardListener takes care of both maintaining the virtual keyboard state, and acting as a gesture recognizer for the hide keyboard gesture. We make this explicit though a union in QIOSInputContext, so that we can access each 'mode' separately. This improved code readability and allows later refactoring of the state and gesture into separate classes without changing the call sites. Change-Id: Icc60f4a542983cda7ca0fd6622963d32d1e90db9 Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@theqtcompany.com>
-rw-r--r--src/plugins/platforms/ios/qiosinputcontext.h6
-rw-r--r--src/plugins/platforms/ios/qiosinputcontext.mm137
2 files changed, 83 insertions, 60 deletions
diff --git a/src/plugins/platforms/ios/qiosinputcontext.h b/src/plugins/platforms/ios/qiosinputcontext.h
index b4ff695f1a..174c44751c 100644
--- a/src/plugins/platforms/ios/qiosinputcontext.h
+++ b/src/plugins/platforms/ios/qiosinputcontext.h
@@ -48,6 +48,7 @@ QT_BEGIN_NAMESPACE
@class QIOSKeyboardListener;
@class QIOSTextInputResponder;
+@protocol KeyboardState;
struct ImeState
{
@@ -90,7 +91,10 @@ public:
static QIOSInputContext *instance();
private:
- QIOSKeyboardListener *m_keyboardListener;
+ union {
+ QIOSKeyboardListener *m_keyboardHideGesture;
+ id <KeyboardState> m_keyboardState;
+ };
QIOSTextInputResponder *m_textResponder;
ImeState m_imeState;
};
diff --git a/src/plugins/platforms/ios/qiosinputcontext.mm b/src/plugins/platforms/ios/qiosinputcontext.mm
index e417e9a1fb..72437fea6e 100644
--- a/src/plugins/platforms/ios/qiosinputcontext.mm
+++ b/src/plugins/platforms/ios/qiosinputcontext.mm
@@ -63,30 +63,40 @@ static QUIView *focusView()
// -------------------------------------------------------------------------
-@interface QIOSKeyboardListener : UIGestureRecognizer <UIGestureRecognizerDelegate> {
-@public
- QIOSInputContext *m_context;
- BOOL m_keyboardVisible;
- BOOL m_keyboardVisibleAndDocked;
- QRectF m_keyboardRect;
- CGRect m_keyboardEndRect;
- NSTimeInterval m_duration;
- UIViewAnimationCurve m_curve;
+@protocol KeyboardState
+- (void)updateKeyboardRect;
+@property(nonatomic) BOOL keyboardVisible;
+@property(nonatomic) BOOL keyboardVisibleAndDocked;
+@property(nonatomic, assign) QRectF keyboardRect;
+@property(nonatomic) CGRect keyboardEndRect;
+@property(nonatomic) NSTimeInterval animationDuration;
+@property(nonatomic) UIViewAnimationCurve animationCurve;
+@end
+
+// -------------------------------------------------------------------------
+
+@interface QIOSKeyboardListener : UIGestureRecognizer <KeyboardState, UIGestureRecognizerDelegate> {
+ @public
UIViewController *m_viewController;
+ @private
+ QIOSInputContext *m_context;
}
@end
@implementation QIOSKeyboardListener
+// KeyboardState protocol
+@synthesize keyboardVisible;
+@synthesize keyboardVisibleAndDocked;
+@synthesize keyboardRect;
+@synthesize keyboardEndRect;
+@synthesize animationDuration;
+@synthesize animationCurve;
+
- (id)initWithQIOSInputContext:(QIOSInputContext *)context
{
- self = [super initWithTarget:self action:@selector(gestureStateChanged:)];
- if (self) {
+ if (self = [super initWithTarget:self action:@selector(gestureStateChanged:)]) {
m_context = context;
- m_keyboardVisible = NO;
- m_keyboardVisibleAndDocked = NO;
- m_duration = 0;
- m_curve = UIViewAnimationCurveEaseOut;
m_viewController = 0;
if (isQtApplication()) {
@@ -107,6 +117,11 @@ static QUIView *focusView()
[m_viewController.view.window addGestureRecognizer:self];
}
+ self.keyboardVisible = NO;
+ self.keyboardVisibleAndDocked = NO;
+ self.animationDuration = 0;
+ self.animationCurve = UIViewAnimationCurveEaseOut;
+
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(keyboardWillShow:)
@@ -120,10 +135,11 @@ static QUIView *focusView()
selector:@selector(keyboardDidChangeFrame:)
name:@"UIKeyboardDidChangeFrameNotification" object:nil];
}
+
return self;
}
-- (void) dealloc
+- (void)dealloc
{
[m_viewController.view.window removeGestureRecognizer:self];
[m_viewController release];
@@ -137,29 +153,31 @@ static QUIView *focusView()
[[NSNotificationCenter defaultCenter]
removeObserver:self
name:@"UIKeyboardDidChangeFrameNotification" object:nil];
+
[super dealloc];
}
-- (void) keyboardDidChangeFrame:(NSNotification *)notification
+- (void)keyboardDidChangeFrame:(NSNotification *)notification
{
Q_UNUSED(notification);
- [self handleKeyboardRectChanged];
+
+ [self updateKeyboardRect];
// If the keyboard was visible and docked from before, this is just a geometry
// change (normally caused by an orientation change). In that case, update scroll:
- if (m_keyboardVisibleAndDocked)
+ if (self.keyboardVisibleAndDocked)
m_context->scrollToCursor();
}
-- (void) keyboardWillShow:(NSNotification *)notification
+- (void)keyboardWillShow:(NSNotification *)notification
{
// Note that UIKeyboardWillShowNotification is only sendt when the keyboard is docked.
- m_keyboardVisibleAndDocked = YES;
- m_keyboardEndRect = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
+ self.keyboardVisibleAndDocked = YES;
+ self.keyboardEndRect = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
- if (!m_duration) {
- m_duration = [[notification.userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
- m_curve = UIViewAnimationCurve([[notification.userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] integerValue]);
+ if (!self.animationDuration) {
+ self.animationDuration = [[notification.userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
+ self.animationCurve = UIViewAnimationCurve([[notification.userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] integerValue]);
}
UIResponder *firstResponder = [UIResponder currentFirstResponder];
@@ -172,11 +190,11 @@ static QUIView *focusView()
m_context->scrollToCursor();
}
-- (void) keyboardWillHide:(NSNotification *)notification
+- (void)keyboardWillHide:(NSNotification *)notification
{
// Note that UIKeyboardWillHideNotification is also sendt when the keyboard is undocked.
- m_keyboardVisibleAndDocked = NO;
- m_keyboardEndRect = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
+ self.keyboardVisibleAndDocked = NO;
+ self.keyboardEndRect = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
if (self.state != UIGestureRecognizerStateBegan) {
// Only disable the gesture if the hiding of the keyboard was not caused by it.
// Otherwise we need to await the final touchEnd callback for doing some clean-up.
@@ -185,27 +203,27 @@ static QUIView *focusView()
m_context->scroll(0);
}
-- (void) handleKeyboardRectChanged
+- (void)updateKeyboardRect
{
// 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]);
+ QRectF convertedRect = fromCGRect([view convertRect:self.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)
+ if (!self.keyboardVisibleAndDocked)
convertedRect.setHeight(0);
- if (convertedRect != m_keyboardRect) {
- m_keyboardRect = convertedRect;
+ if (convertedRect != self.keyboardRect) {
+ self.keyboardRect = convertedRect;
m_context->emitKeyboardRectChanged();
}
- BOOL visible = CGRectIntersectsRect(m_keyboardEndRect, [UIScreen mainScreen].bounds);
- if (m_keyboardVisible != visible) {
- m_keyboardVisible = visible;
+ BOOL visible = CGRectIntersectsRect(self.keyboardEndRect, [UIScreen mainScreen].bounds);
+ if (self.keyboardVisible != visible) {
+ self.keyboardVisible = visible;
m_context->emitInputPanelVisibleChanged();
}
}
@@ -216,7 +234,7 @@ static QUIView *focusView()
{
[super touchesBegan:touches withEvent:event];
- Q_ASSERT(m_keyboardVisibleAndDocked);
+ Q_ASSERT(self.keyboardVisibleAndDocked);
if ([touches count] != 1)
self.state = UIGestureRecognizerStateFailed;
@@ -230,7 +248,7 @@ static QUIView *focusView()
return;
CGPoint touchPoint = [[touches anyObject] locationInView:m_viewController.view.window];
- if (CGRectContainsPoint(m_keyboardEndRect, touchPoint))
+ if (CGRectContainsPoint(self.keyboardEndRect, touchPoint))
self.state = UIGestureRecognizerStateBegan;
}
@@ -280,7 +298,7 @@ static QUIView *focusView()
{
[super reset];
- if (!m_keyboardVisibleAndDocked) {
+ if (!self.keyboardVisibleAndDocked) {
qImDebug() << "keyboard was hidden, disabling hide-keyboard gesture";
self.enabled = NO;
} else {
@@ -328,7 +346,7 @@ QIOSInputContext *QIOSInputContext::instance()
QIOSInputContext::QIOSInputContext()
: QPlatformInputContext()
- , m_keyboardListener([[QIOSKeyboardListener alloc] initWithQIOSInputContext:this])
+ , m_keyboardHideGesture([[QIOSKeyboardListener alloc] initWithQIOSInputContext:this])
, m_textResponder(0)
{
if (isQtApplication())
@@ -338,15 +356,10 @@ QIOSInputContext::QIOSInputContext()
QIOSInputContext::~QIOSInputContext()
{
- [m_keyboardListener release];
+ [m_keyboardHideGesture release];
[m_textResponder release];
}
-QRectF QIOSInputContext::keyboardRect() const
-{
- return m_keyboardListener->m_keyboardRect;
-}
-
void QIOSInputContext::showInputPanel()
{
// No-op, keyboard controlled fully by platform based on focus
@@ -377,12 +390,17 @@ void QIOSInputContext::clearCurrentFocusObject()
bool QIOSInputContext::isInputPanelVisible() const
{
- return m_keyboardListener->m_keyboardVisible;
+ return m_keyboardState.keyboardVisible;
+}
+
+QRectF QIOSInputContext::keyboardRect() const
+{
+ return m_keyboardState.keyboardRect;
}
void QIOSInputContext::cursorRectangleChanged()
{
- if (!m_keyboardListener->m_keyboardVisibleAndDocked || !qApp->focusObject())
+ if (!m_keyboardState.keyboardVisibleAndDocked || !qApp->focusObject())
return;
// Check if the cursor has changed position inside the input item. Since
@@ -402,14 +420,14 @@ void QIOSInputContext::scrollToCursor()
if (!isQtApplication())
return;
- if (m_keyboardListener.state == UIGestureRecognizerStatePossible && m_keyboardListener.numberOfTouches == 1) {
+ if (m_keyboardHideGesture.state == UIGestureRecognizerStatePossible && m_keyboardHideGesture.numberOfTouches == 1) {
// Don't scroll to the cursor if the user is touching the screen and possibly
// trying to trigger the hide-keyboard gesture.
qImDebug() << "preventing scrolling to cursor as we're still waiting for a possible gesture";
return;
}
- UIView *view = m_keyboardListener->m_viewController.view;
+ UIView *view = m_keyboardHideGesture->m_viewController.view;
if (view.window != focusView().window)
return;
@@ -417,7 +435,7 @@ void QIOSInputContext::scrollToCursor()
QRectF translatedCursorPos = qApp->inputMethod()->cursorRectangle();
translatedCursorPos.translate(focusView().qwindow->geometry().topLeft());
- qreal keyboardY = [view convertRect:m_keyboardListener->m_keyboardEndRect fromView:nil].origin.y;
+ qreal keyboardY = [view convertRect:m_keyboardState.keyboardEndRect fromView:nil].origin.y;
int statusBarY = qGuiApp->primaryScreen()->availableGeometry().y();
scroll((translatedCursorPos.bottomLeft().y() < keyboardY - margin) ? 0
@@ -426,15 +444,15 @@ void QIOSInputContext::scrollToCursor()
void QIOSInputContext::scroll(int y)
{
- UIView *rootView = m_keyboardListener->m_viewController.view;
+ UIView *rootView = m_keyboardHideGesture->m_viewController.view;
CATransform3D translationTransform = CATransform3DMakeTranslation(0.0, -y, 0.0);
if (CATransform3DEqualToTransform(translationTransform, rootView.layer.sublayerTransform))
return;
QPointer<QIOSInputContext> self = this;
- [UIView animateWithDuration:m_keyboardListener->m_duration delay:0
- options:(m_keyboardListener->m_curve << 16) | UIViewAnimationOptionBeginFromCurrentState
+ [UIView animateWithDuration:m_keyboardState.animationDuration delay:0
+ options:(m_keyboardState.animationCurve << 16) | UIViewAnimationOptionBeginFromCurrentState
animations:^{
// The sublayerTransform property of CALayer is not implicitly animated for a
// layer-backed view, even inside a UIView animation block, so we need to set up
@@ -464,7 +482,7 @@ void QIOSInputContext::scroll(int y)
}
completion:^(BOOL){
if (self)
- [m_keyboardListener handleKeyboardRectChanged];
+ [m_keyboardState updateKeyboardRect];
}
];
}
@@ -477,7 +495,7 @@ void QIOSInputContext::setFocusObject(QObject *focusObject)
qImDebug() << "new focus object =" << focusObject;
- if (m_keyboardListener.state == UIGestureRecognizerStateChanged) {
+ if (m_keyboardHideGesture.state == UIGestureRecognizerStateChanged) {
// A new focus object may be set as part of delivering touch events to
// application during the hide-keyboard gesture, but we don't want that
// to result in a new object getting focus and bringing the keyboard up
@@ -489,7 +507,7 @@ void QIOSInputContext::setFocusObject(QObject *focusObject)
reset();
- if (m_keyboardListener->m_keyboardVisibleAndDocked)
+ if (m_keyboardState.keyboardVisibleAndDocked)
scrollToCursor();
}
@@ -501,8 +519,9 @@ void QIOSInputContext::focusWindowChanged(QWindow *focusWindow)
reset();
- [m_keyboardListener handleKeyboardRectChanged];
- if (m_keyboardListener->m_keyboardVisibleAndDocked)
+ [m_keyboardState updateKeyboardRect];
+
+ if (m_keyboardState.keyboardVisibleAndDocked)
scrollToCursor();
}