on xenial help sign in
Fixed saving current stage and loss of surface focus when switching stages
9f9405bMichał Sawicz
qml/Stages/SurfaceContainer.qml+3
@@ -34,6 +34,9 @@ FocusScope {
34 property int surfaceOrientationAngle: 0
34 property int surfaceOrientationAngle: 0
35 property bool resizeSurface: true
35 property bool resizeSurface: true
36 property bool isPromptSurface: false
36 property bool isPromptSurface: false
37+ // FIME - dont export, use interactive property. Need to fix qtmir to handle consumesInputChanged
38+ // to update surface activeFocus. See mock MirSurfaceItem.
39+ property alias consumesInput: surfaceItem.consumesInput
37
40
38 onSurfaceChanged: {
41 onSurfaceChanged: {
39 // Not a binding because animations might remove the surface from the surfaceItem
42 // Not a binding because animations might remove the surface from the surfaceItem
qml/Stages/TabletStage.qml+4−3
@@ -752,7 +752,7 @@ AbstractStage {
752
752
753 readonly property bool wantsMainStage: stage == ApplicationInfoInterface.MainStage
753 readonly property bool wantsMainStage: stage == ApplicationInfoInterface.MainStage
754
754
755− readonly property bool isDash: model.application.appId == "unity8-dash"
755+ readonly property bool isDash: application.appId == "unity8-dash"
756
756
757 onFocusChanged: {
757 onFocusChanged: {
758 if (focus && !spreadRepeater.startingUp) {
758 if (focus && !spreadRepeater.startingUp) {
@@ -768,7 +768,7 @@ AbstractStage {
768 onFocusRequested: spreadTile.focus = true;
768 onFocusRequested: spreadTile.focus = true;
769 }
769 }
770 Connections {
770 Connections {
771− target: model.application
771+ target: spreadTile.application
772 onFocusRequested: {
772 onFocusRequested: {
773 if (!model.surface) {
773 if (!model.surface) {
774 // when an app has no surfaces, we assume there's only one entry representing it:
774 // when an app has no surfaces, we assume there's only one entry representing it:
@@ -844,7 +844,7 @@ AbstractStage {
844 _constructing = false;
844 _constructing = false;
845 }
845 }
846 Component.onDestruction: {
846 Component.onDestruction: {
847− WindowStateStorage.saveStage(model.application.appId, stage);
847+ WindowStateStorage.saveStage(application.appId, stage);
848 }
848 }
849
849
850 function refreshStage() {
850 function refreshStage() {
@@ -1073,6 +1073,7 @@ AbstractStage {
1073
1073
1074 surface: spreadDelegate ? spreadDelegate.surface : null
1074 surface: spreadDelegate ? spreadDelegate.surface : null
1075
1075
1076+ consumesInput: false
1076 interactive: false
1077 interactive: false
1077 resizeSurface: false
1078 resizeSurface: false
1078 focus: false
1079 focus: false
tests/mocks/Unity/Application/MirSurfaceItem.cpp+16−7
@@ -56,7 +56,11 @@ MirSurfaceItem::MirSurfaceItem(QQuickItem *parent)
56 Qt::ExtraButton9 | Qt::ExtraButton10 | Qt::ExtraButton11 |
56 Qt::ExtraButton9 | Qt::ExtraButton10 | Qt::ExtraButton11 |
57 Qt::ExtraButton12 | Qt::ExtraButton13);
57 Qt::ExtraButton12 | Qt::ExtraButton13);
58
58
59+ connect(this, &QQuickItem::activeFocusChanged, this, &MirSurfaceItem::updateMirSurfaceActiveFocus);
59 connect(this, &QQuickItem::visibleChanged, this, &MirSurfaceItem::updateMirSurfaceVisibility);
60 connect(this, &QQuickItem::visibleChanged, this, &MirSurfaceItem::updateMirSurfaceVisibility);
61+ connect(this, &MirSurfaceItem::consumesInputChanged, this, [this]() {
62+ updateMirSurfaceActiveFocus(hasActiveFocus());
63+ });
60}
64}
61
65
62MirSurfaceItem::~MirSurfaceItem()
66MirSurfaceItem::~MirSurfaceItem()
@@ -234,6 +238,10 @@ void MirSurfaceItem::setSurface(MirSurfaceInterface* surface)
234 delete m_qmlContentComponent;
238 delete m_qmlContentComponent;
235 m_qmlContentComponent = nullptr;
239 m_qmlContentComponent = nullptr;
236
240
241+ if (hasActiveFocus() && m_consumesInput && m_qmlSurface->live()) {
242+ m_qmlSurface->setActiveFocus(false);
243+ }
244+
237 disconnect(m_qmlSurface, nullptr, this, nullptr);
245 disconnect(m_qmlSurface, nullptr, this, nullptr);
238 m_qmlSurface->unregisterView((qintptr)this);
246 m_qmlSurface->unregisterView((qintptr)this);
239 }
247 }
@@ -243,8 +251,6 @@ void MirSurfaceItem::setSurface(MirSurfaceInterface* surface)
243 if (m_qmlSurface) {
251 if (m_qmlSurface) {
244 m_qmlSurface->registerView((qintptr)this);
252 m_qmlSurface->registerView((qintptr)this);
245
253
246− m_qmlSurface->setActiveFocus(hasActiveFocus());
247−
248 updateSurfaceSize();
254 updateSurfaceSize();
249 updateMirSurfaceVisibility();
255 updateMirSurfaceVisibility();
250
256
@@ -277,17 +283,20 @@ void MirSurfaceItem::setSurface(MirSurfaceInterface* surface)
277 default:
283 default:
278 qFatal("MirSurfaceItem: Unhandled component status");
284 qFatal("MirSurfaceItem: Unhandled component status");
279 }
285 }
286+
287+ if (m_consumesInput) {
288+ m_qmlSurface->setActiveFocus(hasActiveFocus());
289+ }
280 }
290 }
281
291
282 Q_EMIT surfaceChanged(m_qmlSurface);
292 Q_EMIT surfaceChanged(m_qmlSurface);
283}
293}
284
294
285−void MirSurfaceItem::itemChange(ItemChange change, const ItemChangeData & value)
295+
296+void MirSurfaceItem::updateMirSurfaceActiveFocus(bool focused)
286{
297{
287− if (change == QQuickItem::ItemActiveFocusHasChanged) {
298+ if (m_qmlSurface && m_consumesInput && m_qmlSurface->live()) {
288− if (m_qmlSurface) {
289− m_qmlSurface->setActiveFocus(value.boolValue);
299+ m_qmlSurface->setActiveFocus(focused);
290− }
291 }
300 }
292}
301}
293
302
tests/mocks/Unity/Application/MirSurfaceItem.h+1−1
@@ -98,12 +98,12 @@ protected:
98 void mousePressEvent(QMouseEvent * event) override;
98 void mousePressEvent(QMouseEvent * event) override;
99 void mouseMoveEvent(QMouseEvent * event) override;
99 void mouseMoveEvent(QMouseEvent * event) override;
100 void mouseReleaseEvent(QMouseEvent * event) override;
100 void mouseReleaseEvent(QMouseEvent * event) override;
101− void itemChange(ItemChange change, const ItemChangeData & value) override;
102
101
103private Q_SLOTS:
102private Q_SLOTS:
104 void onComponentStatusChanged(QQmlComponent::Status status);
103 void onComponentStatusChanged(QQmlComponent::Status status);
105 void updateScreenshot(QUrl screenshot);
104 void updateScreenshot(QUrl screenshot);
106 void updateMirSurfaceVisibility();
105 void updateMirSurfaceVisibility();
106+ void updateMirSurfaceActiveFocus(bool focused);
107
107
108private:
108private:
109 void createQmlContentItem();
109 void createQmlContentItem();
tests/mocks/Utils/windowstatestorage.cpp+1
@@ -50,6 +50,7 @@ QRect WindowStateStorage::getGeometry(const QString &windowId, const QRect &defa
50void WindowStateStorage::saveStage(const QString &appId, int stage)
50void WindowStateStorage::saveStage(const QString &appId, int stage)
51{
51{
52 m_stage[appId] = stage;
52 m_stage[appId] = stage;
53+ Q_EMIT stageSaved(appId, stage);
53}
54}
54
55
55int WindowStateStorage::getStage(const QString &appId) const
56int WindowStateStorage::getStage(const QString &appId) const
tests/mocks/Utils/windowstatestorage.h+3
@@ -45,6 +45,9 @@ public:
45Q_SIGNALS:
45Q_SIGNALS:
46 void geometryChanged(const QVariantMap& geometry);
46 void geometryChanged(const QVariantMap& geometry);
47
47
48+ // For testing.
49+ void stageSaved(const QString& appId, int stage);
50+
48private:
51private:
49 void setGeometry(const QVariantMap& geometry);
52 void setGeometry(const QVariantMap& geometry);
50 QVariantMap geometry() const;
53 QVariantMap geometry() const;
tests/qmltests/Stages/tst_TabletStage.qml+66−2
@@ -129,6 +129,12 @@ Rectangle {
129 }
129 }
130 }
130 }
131
131
132+ SignalSpy {
133+ id: stageSaver
134+ target: WindowStateStorage
135+ signalName: "stageSaved"
136+ }
137+
132 UnityTestCase {
138 UnityTestCase {
133 id: testCase
139 id: testCase
134 name: "TabletStage"
140 name: "TabletStage"
@@ -138,6 +144,7 @@ Rectangle {
138 property Item sideStage: tabletStage ? findChild(tabletStage, "sideStage") : null
144 property Item sideStage: tabletStage ? findChild(tabletStage, "sideStage") : null
139
145
140 function init() {
146 function init() {
147+ stageSaver.clear();
141 tabletStageLoader.active = true;
148 tabletStageLoader.active = true;
142 tryCompare(tabletStageLoader, "status", Loader.Ready);
149 tryCompare(tabletStageLoader, "status", Loader.Ready);
143
150
@@ -510,7 +517,35 @@ Rectangle {
510 tryCompare(stagesPriv, "sideStageAppId", data.sideStageAppId);
517 tryCompare(stagesPriv, "sideStageAppId", data.sideStageAppId);
511 }
518 }
512
519
513− function test_loadSideStageByDragginFromMainStage() {
520+ function test_applicationSavesLastStage_data() {
521+ return [
522+ { tag: "MainStage", stage: ApplicationInfoInterface.MainStage},
523+ { tag: "SideStage", stage: ApplicationInfoInterface.SideStage},
524+ ];
525+ }
526+
527+ function test_applicationSavesLastStage(data) {
528+ WindowStateStorage.saveStage(webbrowserCheckBox.appId, data.stage);
529+ stageSaver.clear();
530+
531+ var stagesPriv = findInvisibleChild(tabletStage, "stagesPriv");
532+ verify(stagesPriv);
533+
534+ tryCompare(stagesPriv, "mainStageAppId", "unity8-dash");
535+ tryCompare(stagesPriv, "sideStageAppId", "");
536+
537+ var webbrowserSurfaceId = topSurfaceList.nextId;
538+ webbrowserCheckBox.checked = true;
539+ waitUntilAppSurfaceShowsUp(webbrowserSurfaceId);
540+
541+ webbrowserCheckBox.checked = false;
542+
543+ tryCompare(stageSaver, "count", 1);
544+ compare(stageSaver.signalArguments[0][0], "webbrowser-app")
545+ compare(stageSaver.signalArguments[0][1], data.stage)
546+ }
547+
548+ function test_loadSideStageByDraggingFromMainStage() {
514 sideStage.showNow();
549 sideStage.showNow();
515 var webbrowserSurfaceId = topSurfaceList.nextId;
550 var webbrowserSurfaceId = topSurfaceList.nextId;
516 webbrowserCheckBox.checked = true;
551 webbrowserCheckBox.checked = true;
@@ -538,7 +573,7 @@ Rectangle {
538 tryCompare(appDelegate, "stage", ApplicationInfoInterface.SideStage);
573 tryCompare(appDelegate, "stage", ApplicationInfoInterface.SideStage);
539 }
574 }
540
575
541− function test_unloadSideStageByDragginFromStageStage() {
576+ function test_unloadSideStageByDraggingFromStageStage() {
542 sideStage.showNow();
577 sideStage.showNow();
543 WindowStateStorage.saveStage(webbrowserCheckBox.appId, ApplicationInfoInterface.SideStage)
578 WindowStateStorage.saveStage(webbrowserCheckBox.appId, ApplicationInfoInterface.SideStage)
544 var webbrowserSurfaceId = topSurfaceList.nextId;
579 var webbrowserSurfaceId = topSurfaceList.nextId;
@@ -690,5 +725,34 @@ Rectangle {
690 // only unity8-dash surface is left
725 // only unity8-dash surface is left
691 compare(topSurfaceList.count, 1);
726 compare(topSurfaceList.count, 1);
692 }
727 }
728+
729+ function test_draggingSurfaceKeepsSurfaceFocus() {
730+ var webbrowserSurfaceId = topSurfaceList.nextId;
731+ webbrowserCheckBox.checked = true;
732+ waitUntilAppSurfaceShowsUp(webbrowserSurfaceId);
733+
734+ var appDelegate = findChild(tabletStage, "spreadDelegate_" + webbrowserSurfaceId);
735+ verify(appDelegate);
736+ compare(appDelegate.stage, ApplicationInfoInterface.MainStage);
737+
738+ tryCompare(appDelegate.surface, "activeFocus", true);
739+
740+ var pos = tabletStage.width - sideStage.width - (tabletStage.width - sideStage.width) / 2;
741+ var end_pos = tabletStage.width - sideStage.width / 2;
742+
743+ multiTouchDragUntil([0,1,2],
744+ tabletStage,
745+ pos,
746+ tabletStage.height / 2,
747+ units.gu(3),
748+ 0,
749+ function() {
750+ pos += units.gu(3);
751+ return sideStage.shown && !sideStage.showAnimation.running &&
752+ pos >= end_pos;
753+ });
754+
755+ tryCompare(appDelegate.surface, "activeFocus", true);
756+ }
693 }
757 }
694}
758}