Merge lp:~dandrader/ubuntu-keyboard/osk_rotation_lp1236489 into lp:ubuntu-keyboard

Proposed by Daniel d'Andrada
Status: Merged
Approved by: Gerry Boland
Approved revision: 79
Merged at revision: 81
Proposed branch: lp:~dandrader/ubuntu-keyboard/osk_rotation_lp1236489
Merge into: lp:ubuntu-keyboard
Diff against target: 479 lines (+273/-12)
8 files modified
qml/Keyboard.qml (+9/-0)
qml/KeyboardContainer.qml (+1/-0)
src/plugin/inputmethod.cpp (+2/-0)
src/plugin/plugin.pro (+2/-0)
src/plugin/scenerectwatcher.cpp (+91/-0)
src/plugin/scenerectwatcher.h (+54/-0)
src/plugin/ubuntuapplicationapiwrapper.cpp (+87/-11)
src/plugin/ubuntuapplicationapiwrapper.h (+27/-1)
To merge this branch: bzr merge lp:~dandrader/ubuntu-keyboard/osk_rotation_lp1236489
Reviewer Review Type Date Requested Status
Gerry Boland (community) Approve
PS Jenkins bot continuous-integration Approve
Review via email: mp+190946@code.launchpad.net

Commit message

ubuntu-keyboard-info socket now reports the keyboard's scene geometry

That's enough to cover keyboard rotations and animations.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Gerry Boland (gerboland) wrote :

Using a Timer to poll for geometry changes is not a nice solution IMO. Yes you have a point that it's harder to monitor geometry changes with respect to the scene coordinates, but for this specific use-case, is not the parent effectively the root of the scene anyway?

If yes, then listening for geometryChanged signal gives us all the information we need to send shell.

If you absolutely need to monitor for parent geometry changes, you might be interested in
http://bazaar.launchpad.net/~mir-team/unity-mir/trunk/view/head:/src/modules/Unity/Application/inputarea.cpp

review: Needs Fixing
Revision history for this message
Daniel d'Andrada (dandrader) wrote :

> is not the parent effectively the root of the scene anyway?

No, it's not. The current hierarchy is this:

fullscreenItem -> OrientationHelper -> canvas -> swipeArea -> keyboardSurface

>
> If you absolutely need to monitor for parent geometry changes, you might be
> interested in
> http://bazaar.launchpad.net/~mir-team/unity-
> mir/trunk/view/head:/src/modules/Unity/Application/inputarea.cpp

Thanks.
There's a reason for not having an API for notifying changes in scene coordinates: It's expensive and therefore not advisable. Qt has it in its QGraphicsScene API but it comes with a warning notice.

78. By Daniel d'Andrada

Replace timer with SceneRectWatcher

Revision history for this message
Daniel d'Andrada (dandrader) wrote :

Updated

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Gerry Boland (gerboland) wrote :

Ok, I'm happy the polling has gone away. Using timer to prevent flood of signals is good idea.

One bug: the height reported by this is too high, as it includes the area above the OSK where the key-down-popup is drawn. As a result, in the Contacts app, when creating a new contact, you cannot click the buttons on top of the OSK.

review: Needs Fixing
79. By Daniel d'Andrada

Inform a bigger area while the extended keys pop-up is being shown

Revision history for this message
Daniel d'Andrada (dandrader) wrote :

> One bug: the height reported by this is too high, as it includes the area
> above the OSK where the key-down-popup is drawn. As a result, in the Contacts
> app, when creating a new contact, you cannot click the buttons on top of the
> OSK.

Fixed.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Gerry Boland (gerboland) wrote :

Works great! Code clean and functional, approving!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'qml/Keyboard.qml'
2--- qml/Keyboard.qml 2013-10-14 08:02:30 +0000
3+++ qml/Keyboard.qml 2013-10-16 13:17:37 +0000
4@@ -29,6 +29,13 @@
5 *
6 */
7
8+
9+//////////////////////
10+// !!!IMPORTANT!!! Heavy modifications here might break the assumptions made by
11+// UbuntuApplicationApiWrapper code (e.g. object names and what the items do/contain).
12+// Update the code there accordingly, if needed. That code should no longer be needed
13+// once the final architecture is in place.
14+
15 import QtQuick 2.0
16 import "constants.js" as Const
17 import "keys/"
18@@ -112,6 +119,7 @@
19
20 Item {
21 id: keyboardSurface
22+ objectName: "keyboardSurface"
23
24 x:0
25 y:0
26@@ -132,6 +140,7 @@
27
28 Item {
29 id: keyboardComp
30+ objectName: "keyboardComp"
31
32 height: canvas.keypadHeight - wordRibbon.height
33 width: parent.width
34
35=== modified file 'qml/KeyboardContainer.qml'
36--- qml/KeyboardContainer.qml 2013-10-10 09:27:28 +0000
37+++ qml/KeyboardContainer.qml 2013-10-16 13:17:37 +0000
38@@ -73,6 +73,7 @@
39 }
40 ExtendedKeysSelector {
41 id: extendedKeysSelector
42+ objectName: "extendedKeysSelector"
43 anchors.fill: parent
44 }
45
46
47=== modified file 'src/plugin/inputmethod.cpp'
48--- src/plugin/inputmethod.cpp 2013-09-30 12:06:45 +0000
49+++ src/plugin/inputmethod.cpp 2013-10-16 13:17:37 +0000
50@@ -448,6 +448,8 @@
51 {
52 d->qmlRootItem = d->view->rootObject()->findChild<QQuickItem*>("ubuntuKeyboard");
53 QObject::connect(d->qmlRootItem, SIGNAL(stateChanged(QString)), this, SLOT(onQMLStateChanged(QString)));
54+
55+ d->applicationApiWrapper->setRootObject(d->view->rootObject());
56 }
57 break;
58 default:
59
60=== modified file 'src/plugin/plugin.pro'
61--- src/plugin/plugin.pro 2013-09-26 10:55:56 +0000
62+++ src/plugin/plugin.pro 2013-10-16 13:17:37 +0000
63@@ -29,6 +29,7 @@
64 inputmethod_p.h \
65 editor.h \
66 keyboadsettings.h \
67+ scenerectwatcher.h \
68 updatenotifier.h \
69 ubuntuapplicationapiwrapper.h
70
71@@ -37,6 +38,7 @@
72 inputmethod.cpp \
73 editor.cpp \
74 keyboadsettings.cpp \
75+ scenerectwatcher.cpp \
76 updatenotifier.cpp \
77 ubuntuapplicationapiwrapper.cpp
78
79
80=== added file 'src/plugin/scenerectwatcher.cpp'
81--- src/plugin/scenerectwatcher.cpp 1970-01-01 00:00:00 +0000
82+++ src/plugin/scenerectwatcher.cpp 2013-10-16 13:17:37 +0000
83@@ -0,0 +1,91 @@
84+/*
85+ * Copyright 2013 Canonical Ltd.
86+ *
87+ * This program is free software; you can redistribute it and/or modify
88+ * it under the terms of the GNU Lesser General Public License as published by
89+ * the Free Software Foundation; version 3.
90+ *
91+ * This program is distributed in the hope that it will be useful,
92+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
93+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
94+ * GNU Lesser General Public License for more details.
95+ *
96+ * You should have received a copy of the GNU Lesser General Public License
97+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
98+ */
99+
100+#include <scenerectwatcher.h>
101+
102+SceneRectWatcher::SceneRectWatcher(QObject *parent)
103+ : QObject(parent)
104+{
105+ m_sceneRectChangedTimer.setInterval(200);
106+ m_sceneRectChangedTimer.setSingleShot(true);
107+ connect(&m_sceneRectChangedTimer, &QTimer::timeout,
108+ this, &SceneRectWatcher::sceneRectChanged);
109+}
110+
111+void SceneRectWatcher::setItem(QQuickItem *target)
112+{
113+ if (m_target == target)
114+ return;
115+
116+ m_sceneRectChangedTimer.stop();
117+ m_target = target;
118+ listenToAscendantsChanges();
119+}
120+
121+void SceneRectWatcher::listenToAscendantsChanges()
122+{
123+ disconnectFromAscendantsChanges();
124+
125+ if (!m_target)
126+ return;
127+
128+ QQuickItem* item = m_target.data();
129+
130+ m_connections.append(connect(item, &QQuickItem::widthChanged,
131+ this, &SceneRectWatcher::notifySceneRectChange));
132+ m_connections.append(connect(item, &QQuickItem::heightChanged,
133+ this, &SceneRectWatcher::notifySceneRectChange));
134+
135+ while (item != NULL) {
136+ m_connections.append(connect(item, &QQuickItem::parentChanged,
137+ this, &SceneRectWatcher::onAscendantChanged));
138+ m_connections.append(connect(item, &QQuickItem::xChanged,
139+ this, &SceneRectWatcher::notifySceneRectChange));
140+ m_connections.append(connect(item, &QQuickItem::yChanged,
141+ this, &SceneRectWatcher::notifySceneRectChange));
142+ m_connections.append(connect(item, &QQuickItem::transformOriginChanged,
143+ this, &SceneRectWatcher::notifySceneRectChange));
144+ m_connections.append(connect(item, &QQuickItem::rotationChanged,
145+ this, &SceneRectWatcher::notifySceneRectChange));
146+ m_connections.append(connect(item, &QQuickItem::scaleChanged,
147+ this, &SceneRectWatcher::notifySceneRectChange));
148+ item = item->parentItem();
149+ }
150+}
151+
152+void SceneRectWatcher::disconnectFromAscendantsChanges()
153+{
154+ // disconnect all previously connected signals
155+ Q_FOREACH (QMetaObject::Connection connection, m_connections) {
156+ disconnect(connection);
157+ }
158+ m_connections.clear();
159+}
160+
161+void SceneRectWatcher::onAscendantChanged()
162+{
163+ // Simple approach: Just rebuild connections from scratch.
164+ listenToAscendantsChanges();
165+
166+ notifySceneRectChange();
167+}
168+
169+void SceneRectWatcher::notifySceneRectChange()
170+{
171+ if (!m_sceneRectChangedTimer.isActive()) {
172+ m_sceneRectChangedTimer.start();
173+ }
174+}
175
176=== added file 'src/plugin/scenerectwatcher.h'
177--- src/plugin/scenerectwatcher.h 1970-01-01 00:00:00 +0000
178+++ src/plugin/scenerectwatcher.h 2013-10-16 13:17:37 +0000
179@@ -0,0 +1,54 @@
180+/*
181+ * Copyright 2013 Canonical Ltd.
182+ *
183+ * This program is free software; you can redistribute it and/or modify
184+ * it under the terms of the GNU Lesser General Public License as published by
185+ * the Free Software Foundation; version 3.
186+ *
187+ * This program is distributed in the hope that it will be useful,
188+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
189+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
190+ * GNU Lesser General Public License for more details.
191+ *
192+ * You should have received a copy of the GNU Lesser General Public License
193+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
194+ */
195+
196+#ifndef SCENERECTWATCHER_H
197+#define SCENERECTWATCHER_H
198+
199+#include <QObject>
200+
201+#include <QQuickItem>
202+#include <QList>
203+#include <QPointer>
204+#include <QTimer>
205+
206+class SceneRectWatcher : public QObject {
207+ Q_OBJECT
208+public:
209+ SceneRectWatcher(QObject *parent = 0);
210+ void setItem(QQuickItem *target);
211+
212+Q_SIGNALS:
213+ void sceneRectChanged();
214+
215+private Q_SLOTS:
216+ void onAscendantChanged();
217+ void notifySceneRectChange();
218+
219+private:
220+ void listenToAscendantsChanges();
221+ void disconnectFromAscendantsChanges();
222+
223+ QList<QMetaObject::Connection> m_connections;
224+ QPointer<QQuickItem> m_target;
225+
226+ // We send sceneRectChanged() with a delay avoid flooding sceneRectChanged()
227+ // listeners during animations and also because it's common that more than
228+ // one property changes during the same event loop iteration (e.g. ancestor.x()
229+ // and ancestor.y())
230+ QTimer m_sceneRectChangedTimer;
231+};
232+
233+#endif // SCENERECTWATCHER_H
234
235=== modified file 'src/plugin/ubuntuapplicationapiwrapper.cpp'
236--- src/plugin/ubuntuapplicationapiwrapper.cpp 2013-10-11 15:34:27 +0000
237+++ src/plugin/ubuntuapplicationapiwrapper.cpp 2013-10-16 13:17:37 +0000
238@@ -25,10 +25,10 @@
239 #define HAVE_UBUNTU_PLATFORM_API
240 #endif
241
242+#include <QByteArray>
243 #include <QDir>
244 #include <QFile>
245 #include <QtGlobal>
246-#include <QByteArray>
247
248 namespace {
249 const char gServerName[] = "ubuntu-keyboard-info";
250@@ -42,9 +42,14 @@
251 m_runningOnMir = true;
252 }
253
254+ m_sharedInfo.reset();
255+
256 if (m_runningOnMir) {
257 startLocalServer();
258 }
259+
260+ connect(&m_sceneRectWatcher, &SceneRectWatcher::sceneRectChanged,
261+ this, &UbuntuApplicationApiWrapper::updateSharedInfo);
262 }
263
264 void UbuntuApplicationApiWrapper::startLocalServer()
265@@ -86,7 +91,9 @@
266 Q_UNUSED(height)
267 #endif
268
269- sendInfoToClientConnection(width, height);
270+ m_sceneRectWatcher.setItem(m_keyboardComp);
271+ startWatchingExtendedKeysSelector();
272+ updateSharedInfo();
273 }
274
275 void UbuntuApplicationApiWrapper::reportOSKInvisible()
276@@ -96,6 +103,9 @@
277 ubuntu_ui_report_osk_invisible();
278 }
279 #endif
280+
281+ m_sceneRectWatcher.setItem(0);
282+ stopWatchingExtendedKeysSelector();
283 }
284
285 int UbuntuApplicationApiWrapper::oskWindowRole() const
286@@ -107,7 +117,7 @@
287 #endif
288 }
289
290-void UbuntuApplicationApiWrapper::sendInfoToClientConnection(int width, int height)
291+void UbuntuApplicationApiWrapper::sendInfoToClientConnection()
292 {
293 if (!m_clientConnection
294 || m_clientConnection->state() != QLocalSocket::ConnectedState) {
295@@ -115,18 +125,14 @@
296 return;
297 }
298
299- struct SharedInfo sharedInfo;
300- sharedInfo.keyboardWidth = width;
301- sharedInfo.keyboardHeight = height;
302-
303- if (sharedInfo == m_lastInfoShared) {
304+ if (m_sharedInfo == m_lastInfoShared) {
305 // do not flood our listener with redundant info. This also means that
306 // were are getting called unnecessarily
307 return;
308 }
309
310 const qint64 sharedInfoSize = sizeof(struct SharedInfo);
311- qint64 bytesWritten = m_clientConnection->write(reinterpret_cast<char *>(&sharedInfo),
312+ qint64 bytesWritten = m_clientConnection->write(reinterpret_cast<char *>(&m_sharedInfo),
313 sharedInfoSize);
314
315 if (bytesWritten < 0) {
316@@ -138,7 +144,7 @@
317 "but only" << bytesWritten << "went through";
318 }
319
320- m_lastInfoShared = sharedInfo;
321+ m_lastInfoShared = m_sharedInfo;
322 }
323
324 void UbuntuApplicationApiWrapper::onNewConnection()
325@@ -175,16 +181,86 @@
326 }
327 }
328
329+void UbuntuApplicationApiWrapper::updateSharedInfo()
330+{
331+ if (m_keyboardComp.isNull() || m_keyboardSurface.isNull()
332+ || m_extendedKeysSelector.isNull()) {
333+ return;
334+ }
335+
336+ QRectF keyboardSceneRect;
337+
338+ if (m_extendedKeysSelector->isEnabled() && m_extendedKeysSelector->isVisible()) {
339+ // The pop-up could be above the keyboard, so we need a bigger area that's guaranteed
340+ // to contain both the keyboard and that extended keys selector pop-up, which is the
341+ // keyboardSurface.
342+ keyboardSceneRect = m_keyboardSurface->mapRectToScene(QRectF(0, 0,
343+ m_keyboardSurface->width(),
344+ m_keyboardSurface->height()));
345+ } else {
346+ // Regular case, just the keyboard area.
347+ keyboardSceneRect = m_keyboardComp->mapRectToScene(QRectF(0, 0,
348+ m_keyboardComp->width(),
349+ m_keyboardComp->height()));
350+ }
351+
352+ m_sharedInfo.keyboardX = keyboardSceneRect.x();
353+ m_sharedInfo.keyboardY = keyboardSceneRect.y();
354+ m_sharedInfo.keyboardWidth = keyboardSceneRect.width();
355+ m_sharedInfo.keyboardHeight = keyboardSceneRect.height();
356+
357+ sendInfoToClientConnection();
358+}
359+
360+void UbuntuApplicationApiWrapper::setRootObject(QQuickItem *rootObject)
361+{
362+ // Getting items from qml/Keyboard.qml
363+
364+ m_keyboardSurface = rootObject->findChild<QQuickItem*>("keyboardSurface");
365+ if (m_keyboardSurface.isNull()) {
366+ qFatal("UbuntuApplicationApiWrapper: couldn't find \"keyboardSurface\" QML item");
367+ }
368+
369+ m_keyboardComp = rootObject->findChild<QQuickItem*>("keyboardComp");
370+ if (m_keyboardComp.isNull()) {
371+ qFatal("UbuntuApplicationApiWrapper: couldn't find \"keyboardComp\" QML item");
372+ }
373+
374+ // In qml/KeyboardContainer.qml, a child of qml/Keyboard.qml
375+ m_extendedKeysSelector = rootObject->findChild<QQuickItem*>("extendedKeysSelector");
376+ if (m_extendedKeysSelector.isNull()) {
377+ qFatal("UbuntuApplicationApiWrapper: couldn't find \"extendedKeysSelector\" QML item");
378+ }
379+}
380+
381+void UbuntuApplicationApiWrapper::startWatchingExtendedKeysSelector()
382+{
383+ connect(m_extendedKeysSelector.data(), &QQuickItem::enabledChanged,
384+ this, &UbuntuApplicationApiWrapper::updateSharedInfo);
385+ connect(m_extendedKeysSelector.data(), &QQuickItem::visibleChanged,
386+ this, &UbuntuApplicationApiWrapper::updateSharedInfo);
387+}
388+
389+void UbuntuApplicationApiWrapper::stopWatchingExtendedKeysSelector()
390+{
391+ disconnect(m_extendedKeysSelector.data(), 0, this, 0);
392+}
393+
394+
395 // ------------------------------- SharedInfo ----------------------------
396
397 bool UbuntuApplicationApiWrapper::SharedInfo::operator ==(const struct SharedInfo &other)
398 {
399- return keyboardWidth == other.keyboardWidth
400+ return keyboardX == other.keyboardX
401+ && keyboardY == other.keyboardY
402+ && keyboardWidth == other.keyboardWidth
403 && keyboardHeight == other.keyboardHeight;
404 }
405
406 void UbuntuApplicationApiWrapper::SharedInfo::reset()
407 {
408+ keyboardX = -1;
409+ keyboardY = -1;
410 keyboardWidth = 0;
411 keyboardHeight = 0;
412 }
413
414=== modified file 'src/plugin/ubuntuapplicationapiwrapper.h'
415--- src/plugin/ubuntuapplicationapiwrapper.h 2013-10-11 15:34:27 +0000
416+++ src/plugin/ubuntuapplicationapiwrapper.h 2013-10-16 13:17:37 +0000
417@@ -20,6 +20,10 @@
418 #include <QObject>
419 #include <QLocalServer>
420 #include <QLocalSocket>
421+#include <QQuickItem>
422+#include <QPointer>
423+
424+#include "scenerectwatcher.h"
425
426 /*
427 * Class: UbuntuApplicationApiWrapper
428@@ -41,28 +45,50 @@
429 void reportOSKVisible(const int, const int, const int, const int);
430 void reportOSKInvisible();
431
432+ void setRootObject(QQuickItem *rootObject);
433 private Q_SLOTS:
434 void onNewConnection();
435 void onClientDisconnected();
436+ void updateSharedInfo();
437
438 private:
439 // NB! Must match the definition in unity-mir. Not worth creating a shared header
440 // just for that.
441 struct SharedInfo {
442+ qint32 keyboardX;
443+ qint32 keyboardY;
444 qint32 keyboardWidth;
445 qint32 keyboardHeight;
446
447 bool operator ==(const struct SharedInfo &other);
448 void reset();
449 };
450- void sendInfoToClientConnection(int width, int height);
451+ void sendInfoToClientConnection();
452 void startLocalServer();
453 QString buildSocketFilePath() const;
454+ void startWatchingExtendedKeysSelector();
455+ void stopWatchingExtendedKeysSelector();
456
457 bool m_runningOnMir;
458 QLocalServer m_localServer;
459 QLocalSocket *m_clientConnection;
460+ struct SharedInfo m_sharedInfo;
461 struct SharedInfo m_lastInfoShared;
462+
463+ /////
464+ // Items from qml/Keyboard.qml
465+
466+ // Just the keyboard area
467+ QPointer<QQuickItem> m_keyboardComp;
468+
469+ // Keyboard + space for the word ribbon on top
470+ QPointer<QQuickItem> m_keyboardSurface;
471+
472+ // The baloon that pops-up when you long press a character key to get
473+ // its accented versions etc.
474+ QPointer<QQuickItem> m_extendedKeysSelector;
475+
476+ SceneRectWatcher m_sceneRectWatcher;
477 };
478
479 #endif // UBUNTUAPPLICATIONAPIWRAPPER_H

Subscribers

People subscribed via source and target branches