on xenial help sign in
merge prereq
commitd73aca020abc068b116f262bbcfab8ce833dd724 authorMichael Zanetti <michael.zanetti@canonical.com> dateMon 13 Mar 2017, 11:52 (10 years ago) parentb4c4b677367193
19 files changed+635−247
plugins/WindowManager/CMakeLists.txt+2−1 plugins/WindowManager/Screen.cpp+163−125 plugins/WindowManager/Screen.h+85−36 plugins/WindowManager/ScreenAttached.cpp+149 plugins/WindowManager/ScreenAttached.h+56 plugins/WindowManager/ScreenWindow.cpp+1−4 plugins/WindowManager/ScreenWindow.h+1−1 plugins/WindowManager/Screens.cpp+13−5 plugins/WindowManager/Screens.h+8−5 plugins/WindowManager/WindowManagerPlugin.cpp+9−4 plugins/WindowManager/WorkspaceModel.cpp+33−4 plugins/WindowManager/WorkspaceModel.h+5−2 tests/mocks/WindowManager/CMakeLists.txt+3−1 tests/mocks/WindowManager/ScreenWindow.cpp → tests/mocks/WindowManager/MockScreenWindow.cpp+8−22 tests/mocks/WindowManager/ScreenWindow.h → tests/mocks/WindowManager/MockScreenWindow.h+7−26 tests/mocks/WindowManager/MockScreens.cpp+69−1 tests/mocks/WindowManager/MockScreens.h+9−1 tests/mocks/WindowManager/WindowManagerPlugin.cpp+13−9 tests/qmltests/tst_ShellApplication.qml+1
plugins/WindowManager/CMakeLists.txt+2−1
@@ -12,8 +12,9 @@ set(WINDOWMANAGER_SRC
1212 TopLevelWindowModel.cpp
1313 Window.cpp
1414 WindowManagerPlugin.cpp
15− Screens.cpp
1615 Screen.cpp
16+ ScreenAttached.cpp
17+ Screens.cpp
1718 ScreenWindow.cpp
1819 Workspace.cpp
1920 WorkspaceManager.cpp
plugins/WindowManager/Screen.cpp+163−125
@@ -15,125 +15,138 @@
1515 */
1616
1717#include "Screen.h"
18+#include "Screens.h"
1819#include "WorkspaceManager.h"
1920#include "Workspace.h"
2021
21−#include <QQmlEngine>
22+ScreenInterface::ScreenInterface(QObject *parent)
23+ : QObject(parent)
24+{
25+}
2226
23−Screen::Screen(qtmir::Screen* screen)
24− : m_wrapped(screen)
25− , m_workspaces(new WorkspaceModel)
27+void ScreenInterface::connectToScreen(qtmir::Screen *screen)
2628{
27− QQmlEngine::setObjectOwnership(m_workspaces.data(), QQmlEngine::CppOwnership);
29+ m_wrapped = screen;
30+ connect(screen, &qtmir::Screen::usedChanged, this, &ScreenInterface::usedChanged);
31+ connect(screen, &qtmir::Screen::nameChanged, this, &ScreenInterface::nameChanged);
32+ connect(screen, &qtmir::Screen::outputTypeChanged, this, &ScreenInterface::outputTypeChanged);
33+ connect(screen, &qtmir::Screen::scaleChanged, this, &ScreenInterface::scaleChanged);
34+ connect(screen, &qtmir::Screen::formFactorChanged, this, &ScreenInterface::formFactorChanged);
35+ connect(screen, &qtmir::Screen::physicalSizeChanged, this, &ScreenInterface::physicalSizeChanged);
36+ connect(screen, &qtmir::Screen::positionChanged, this, &ScreenInterface::positionChanged);
37+ connect(screen, &qtmir::Screen::activeChanged, this, &ScreenInterface::activeChanged);
38+ connect(screen, &qtmir::Screen::currentModeIndexChanged, this, &ScreenInterface::currentModeIndexChanged);
39+ connect(screen, &qtmir::Screen::availableModesChanged, this, &ScreenInterface::availableModesChanged);
40+}
2841
29− connect(m_wrapped, &qtmir::Screen::usedChanged, this, &Screen::usedChanged);
30− connect(m_wrapped, &qtmir::Screen::nameChanged, this, &Screen::nameChanged);
31− connect(m_wrapped, &qtmir::Screen::outputTypeChanged, this, &Screen::outputTypeChanged);
32− connect(m_wrapped, &qtmir::Screen::scaleChanged, this, &Screen::scaleChanged);
33− connect(m_wrapped, &qtmir::Screen::formFactorChanged, this, &Screen::formFactorChanged);
34− connect(m_wrapped, &qtmir::Screen::physicalSizeChanged, this, &Screen::physicalSizeChanged);
35− connect(m_wrapped, &qtmir::Screen::positionChanged, this, &Screen::positionChanged);
36− connect(m_wrapped, &qtmir::Screen::activeChanged, this, &Screen::activeChanged);
37− connect(m_wrapped, &qtmir::Screen::currentModeIndexChanged, this, &Screen::currentModeIndexChanged);
38− connect(m_wrapped, &qtmir::Screen::availableModesChanged, this, &Screen::availableModesChanged);
39− connect(m_wrapped, &qtmir::Screen::outputTypeChanged, this, &Screen::outputTypeNameChanged);
42+qtmir::OutputId ScreenInterface::outputId() const
43+{
44+ if (!m_wrapped) return qtmir::OutputId(-1);
45+ return m_wrapped->outputId();
46+}
4047
41− // Connect the active workspace to activate the screen.
42− connect(m_workspaces.data(), &WorkspaceModel::workspaceAdded, this, [this](Workspace* workspace) {
43− connect(workspace, &Workspace::activeChanged, this, [this, workspace](bool active) {
44− if (active) {
45− setCurrentWorkspace(workspace);
46− activate();
47− }
48− });
49− if (workspace->isActive()) {
50− activate();
51− setCurrentWorkspace(workspace);
52− }
53− if (!m_currentWorspace) {
54− setCurrentWorkspace(workspace);
55− }
56− });
57− connect(m_workspaces.data(), &WorkspaceModel::workspaceRemoved, this, [this](Workspace* workspace) {
58− disconnect(workspace, &Workspace::activeChanged, this, 0);
59− if (workspace == m_currentWorspace) {
60− resetCurrentWorkspace();
61− }
62− });
63− connect(this, &Screen::activeChanged, this, [this](bool active) {
64− if (active && m_currentWorspace) {
65− m_currentWorspace->activate();
66− }
67− });
48+bool ScreenInterface::used() const
49+{
50+ if (!m_wrapped) return false;
51+ return m_wrapped->used();
52+}
6853
69− WorkspaceManager::instance()->createWorkspace()->assign(m_workspaces.data());
70− WorkspaceManager::instance()->createWorkspace()->assign(m_workspaces.data());
54+QString ScreenInterface::name() const
55+{
56+ if (!m_wrapped) return QString();
57+ return m_wrapped->name();
7158}
7259
73−Screen::Screen(const Screen &other)
74− : qtmir::Screen(nullptr)
75− , m_wrapped(other.m_wrapped)
76− , m_workspaces(new WorkspaceModelProxy(other.m_workspaces.data()))
60+float ScreenInterface::scale() const
7761{
78− QQmlEngine::setObjectOwnership(m_workspaces.data(), QQmlEngine::CppOwnership);
62+ if (!m_wrapped) return 1.0;
63+ return m_wrapped->scale();
64+}
7965
80− connect(m_wrapped, &qtmir::Screen::usedChanged, this, &Screen::usedChanged);
81− connect(m_wrapped, &qtmir::Screen::nameChanged, this, &Screen::nameChanged);
82− connect(m_wrapped, &qtmir::Screen::outputTypeChanged, this, &Screen::outputTypeChanged);
83− connect(m_wrapped, &qtmir::Screen::scaleChanged, this, &Screen::scaleChanged);
84− connect(m_wrapped, &qtmir::Screen::formFactorChanged, this, &Screen::formFactorChanged);
85− connect(m_wrapped, &qtmir::Screen::physicalSizeChanged, this, &Screen::physicalSizeChanged);
86− connect(m_wrapped, &qtmir::Screen::positionChanged, this, &Screen::positionChanged);
87− connect(m_wrapped, &qtmir::Screen::activeChanged, this, &Screen::activeChanged);
88− connect(m_wrapped, &qtmir::Screen::currentModeIndexChanged, this, &Screen::currentModeIndexChanged);
89− connect(m_wrapped, &qtmir::Screen::availableModesChanged, this, &Screen::availableModesChanged);
66+QSizeF ScreenInterface::physicalSize() const
67+{
68+ if (!m_wrapped) return QSizeF();
69+ return m_wrapped->physicalSize();
9070}
9171
92−void Screen::resetCurrentWorkspace()
72+qtmir::FormFactor ScreenInterface::formFactor() const
9373{
94− auto newCurrent = m_workspaces->rowCount() > 0 ? m_workspaces->get(0) : nullptr;
95− if (m_currentWorspace != newCurrent) {
96− m_currentWorspace = newCurrent;
97− Q_EMIT currentWorkspaceChanged(newCurrent);
98− }
74+ if (!m_wrapped) return qtmir::FormFactorUnknown;
75+ return m_wrapped->formFactor();
9976}
10077
101−qtmir::OutputId Screen::outputId() const
78+qtmir::OutputTypes ScreenInterface::outputType() const
10279{
103− return m_wrapped->outputId();
80+ if (!m_wrapped) return qtmir::Unknown;
81+ return m_wrapped->outputType();
10482}
10583
106−bool Screen::used() const
84+MirPowerMode ScreenInterface::powerMode() const
10785{
108− return m_wrapped->used();
86+ if (!m_wrapped) return mir_power_mode_on;
87+ return m_wrapped->powerMode();
10988}
11089
111−QString Screen::name() const
90+Qt::ScreenOrientation ScreenInterface::orientation() const
11291{
113− return m_wrapped->name();
92+ if (!m_wrapped) return Qt::PrimaryOrientation;
93+ return m_wrapped->orientation();
11494}
11595
116−float Screen::scale() const
96+QPoint ScreenInterface::position() const
11797{
118− return m_wrapped->scale();
98+ if (!m_wrapped) return QPoint();
99+ return m_wrapped->position();
119100}
120101
121−QSizeF Screen::physicalSize() const
102+QQmlListProperty<qtmir::ScreenMode> ScreenInterface::availableModes()
122103{
123− return m_wrapped->physicalSize();
104+ if (!m_wrapped) return QQmlListProperty<qtmir::ScreenMode>();
105+ return m_wrapped->availableModes();
124106}
125107
126−qtmir::FormFactor Screen::formFactor() const
108+uint ScreenInterface::currentModeIndex() const
127109{
128− return m_wrapped->formFactor();
110+ if (!m_wrapped) return -1;
111+ return m_wrapped->currentModeIndex();
129112}
130113
131−qtmir::OutputTypes Screen::outputType() const
114+bool ScreenInterface::isActive() const
132115{
133− return m_wrapped->outputType();
116+ if (!m_wrapped) return false;
117+ return m_wrapped->isActive();
118+}
119+
120+void ScreenInterface::activate()
121+{
122+ setActive(true);
123+}
124+
125+void ScreenInterface::setActive(bool active)
126+{
127+ if (!m_wrapped) return;
128+ m_wrapped->setActive(active);
129+}
130+
131+QScreen *ScreenInterface::qscreen() const
132+{
133+ if (!m_wrapped) return nullptr;
134+ return m_wrapped->qscreen();
135+}
136+
137+qtmir::ScreenConfiguration *ScreenInterface::beginConfiguration() const
138+{
139+ if (!m_wrapped) return nullptr;
140+ return m_wrapped->beginConfiguration();
141+}
142+
143+bool ScreenInterface::applyConfiguration(qtmir::ScreenConfiguration *configuration)
144+{
145+ if (!m_wrapped) return false;
146+ return m_wrapped->applyConfiguration(configuration);
134147}
135148
136−QString Screen::outputTypeName() const
149+QString ScreenInterface::outputTypeName() const
137150{
138151 switch (m_wrapped->outputType()) {
139152 case qtmir::Unknown:
@@ -162,47 +175,61 @@ QString Screen::outputTypeName() const
162175 case qtmir::TV:
163176 return tr("TV");
164177 }
178+ return QString();
165179}
166180
167−MirPowerMode Screen::powerMode() const
168−{
169− return m_wrapped->powerMode();
170−}
171−
172−Qt::ScreenOrientation Screen::orientation() const
181+void ScreenInterface::sync(ScreenInterface *proxy)
173182{
174− return m_wrapped->orientation();
175−}
176−
177−QPoint Screen::position() const
178−{
179− return m_wrapped->position();
183+ if (!proxy) return;
184+ workspaces()->sync(proxy->workspaces());
180185}
181186
182−QQmlListProperty<qtmir::ScreenMode> Screen::availableModes()
187+Screen::Screen(qtmir::Screen* wrapped)
188+ : m_workspaces(new WorkspaceModel)
183189{
184− return m_wrapped->availableModes();
185−}
190+ connectToScreen(wrapped);
186191
187−uint Screen::currentModeIndex() const
188−{
189− return m_wrapped->currentModeIndex();
190−}
192+ // Connect the active workspace to activate the screen.
193+ connect(m_workspaces.data(), &WorkspaceModel::workspaceInserted, this, [this](int, Workspace* workspace) {
194+ connect(workspace, &Workspace::activeChanged, this, [this, workspace](bool active) {
195+ if (active) {
196+ setCurrentWorkspace(workspace);
197+ activate();
198+ }
199+ });
200+ if (workspace->isActive()) {
201+ activate();
202+ setCurrentWorkspace(workspace);
203+ }
204+ if (!m_currentWorspace) {
205+ setCurrentWorkspace(workspace);
206+ }
207+ });
208+ connect(m_workspaces.data(), &WorkspaceModel::workspaceRemoved, this, [this](Workspace* workspace) {
209+ disconnect(workspace, &Workspace::activeChanged, this, 0);
210+ if (workspace == m_currentWorspace) {
211+ resetCurrentWorkspace();
212+ }
213+ });
214+ connect(this, &Screen::activeChanged, this, [this](bool active) {
215+ if (active && m_currentWorspace) {
216+ m_currentWorspace->activate();
217+ }
218+ });
191219
192−bool Screen::isActive() const
193−{
194− return m_wrapped->isActive();
220+ WorkspaceManager::instance()->createWorkspace()->assign(m_workspaces.data());
221+ WorkspaceManager::instance()->createWorkspace()->assign(m_workspaces.data());
195222}
196223
197−qtmir::ScreenConfiguration *Screen::beginConfiguration() const
224+void Screen::resetCurrentWorkspace()
198225{
199− return m_wrapped->beginConfiguration();
226+ auto newCurrent = m_workspaces->rowCount() > 0 ? m_workspaces->get(0) : nullptr;
227+ if (m_currentWorspace != newCurrent) {
228+ m_currentWorspace = newCurrent;
229+ Q_EMIT currentWorkspaceChanged(newCurrent);
230+ }
200231}
201232
202−bool Screen::applyConfiguration(qtmir::ScreenConfiguration *configuration)
203−{
204− return m_wrapped->applyConfiguration(configuration);
205−}
206233
207234WorkspaceModel *Screen::workspaces() const
208235{
@@ -222,32 +249,43 @@ void Screen::setCurrentWorkspace(Workspace *workspace)
222249 }
223250}
224251
225−void Screen::sync(Screen *proxy)
226−{
227− if (!proxy) return;
228−
229− workspaces()->sync(proxy->workspaces());
230−}
231−
232−void Screen::activate()
252+ScreenProxy::ScreenProxy(ScreenInterface *const screen)
253+ : m_workspaces(new WorkspaceModelProxy(screen->workspaces()))
254+ , m_original(screen)
233255{
234− setActive(true);
256+ connectToScreen(screen->wrapped());
257+
258+ auto updateCurrentWorkspaceFn = [this](Workspace* realWorkspace) {
259+ Q_FOREACH(Workspace* workspace, m_workspaces->list()) {
260+ auto p = qobject_cast<WorkspaceProxy*>(workspace);
261+ if (p && p->proxyObject() == realWorkspace) {
262+ if (m_currentWorspace != p) {
263+ m_currentWorspace = p;
264+ Q_EMIT currentWorkspaceChanged(p);
265+ }
266+ }
267+ }
268+ };
269+ connect(screen, &ScreenInterface::currentWorkspaceChanged, this, updateCurrentWorkspaceFn);
270+ updateCurrentWorkspaceFn(screen->currentWorkspace());
235271}
236272
237−void Screen::setActive(bool active)
273+WorkspaceModel *ScreenProxy::workspaces() const
238274{
239− m_wrapped->setActive(active);
275+ return m_workspaces.data();
240276}
241277
242−QScreen *Screen::qscreen() const
278+Workspace *ScreenProxy::currentWorkspace() const
243279{
244− return m_wrapped->qscreen();
280+ return m_currentWorspace.data();
245281}
246282
247−ScreenProxy::ScreenProxy(ScreenProxy::Screen *const screen)
248− : Screen(*screen)
249− , m_original(screen)
283+void ScreenProxy::setCurrentWorkspace(Workspace *workspace)
250284{
285+ auto p = qobject_cast<WorkspaceProxy*>(workspace);
286+ if (p) {
287+ m_original->setCurrentWorkspace(p->proxyObject());
288+ }
251289}
252290
253291void ScreenProxy::addWorkspace()
plugins/WindowManager/Screen.h+85−36
@@ -8,77 +8,126 @@
88#include "WorkspaceModel.h"
99
1010class ScreenProxy;
11+class ScreenAttached;
1112
12−class Screen : public qtmir::Screen
13+class ScreenInterface: public QObject
1314{
1415 Q_OBJECT
16+
17+ Q_PROPERTY(bool active READ isActive WRITE setActive NOTIFY activeChanged)
18+
19+ Q_PROPERTY(bool used READ used NOTIFY usedChanged)
20+ Q_PROPERTY(QString name READ name NOTIFY nameChanged)
21+ Q_PROPERTY(qtmir::OutputTypes outputType READ outputType NOTIFY outputTypeChanged)
22+ Q_PROPERTY(float scale READ scale NOTIFY scaleChanged)
23+ Q_PROPERTY(qtmir::FormFactor formFactor READ formFactor NOTIFY formFactorChanged)
24+ Q_PROPERTY(MirPowerMode powerMode READ powerMode NOTIFY powerModeChanged)
25+ Q_PROPERTY(Qt::ScreenOrientation orientation READ orientation NOTIFY orientationChanged)
26+ Q_PROPERTY(QPoint position READ position NOTIFY positionChanged)
27+ Q_PROPERTY(uint currentModeIndex READ currentModeIndex NOTIFY currentModeIndexChanged)
28+ Q_PROPERTY(QQmlListProperty<qtmir::ScreenMode> availableModes READ availableModes NOTIFY availableModesChanged)
29+ Q_PROPERTY(QSizeF physicalSize READ physicalSize NOTIFY physicalSizeChanged)
30+ Q_PROPERTY(QString outputTypeName READ outputTypeName NOTIFY outputTypeChanged)
31+
1532 Q_PROPERTY(WorkspaceModel* workspaces READ workspaces CONSTANT)
1633 Q_PROPERTY(Workspace* currentWorkspace READ currentWorkspace WRITE setCurrentWorkspace NOTIFY currentWorkspaceChanged)
17− Q_PROPERTY(QString outputTypeName READ outputTypeName NOTIFY outputTypeNameChanged)
18−
1934public:
20− explicit Screen(qtmir::Screen*const wrapped);
21−
22− qtmir::OutputId outputId() const override;
23− bool used() const override;
24− QString name() const override;
25− float scale() const override;
26− QSizeF physicalSize() const override;
27− qtmir::FormFactor formFactor() const override;
28− qtmir::OutputTypes outputType() const override;
35+ // From qtmir::Screen
36+ qtmir::OutputId outputId() const;
37+ bool used() const;
38+ QString name() const;
39+ float scale() const;
40+ QSizeF physicalSize() const;
41+ qtmir::FormFactor formFactor() const;
42+ qtmir::OutputTypes outputType() const;
43+ MirPowerMode powerMode() const;
44+ Qt::ScreenOrientation orientation() const;
45+ QPoint position() const;
46+ QQmlListProperty<qtmir::ScreenMode> availableModes();
47+ uint currentModeIndex() const;
48+ bool isActive() const;
49+ void setActive(bool active);
50+ QScreen* qscreen() const;
51+ qtmir::ScreenConfiguration *beginConfiguration() const;
52+ bool applyConfiguration(qtmir::ScreenConfiguration *configuration);
2953 QString outputTypeName() const;
30− MirPowerMode powerMode() const override;
31− Qt::ScreenOrientation orientation() const override;
32− QPoint position() const override;
33− QQmlListProperty<qtmir::ScreenMode> availableModes() override;
34− uint currentModeIndex() const override;
35− bool isActive() const override;
36− void setActive(bool active) override;
3754
38− QScreen* qscreen() const override;
55+ virtual WorkspaceModel* workspaces() const = 0;
56+ virtual Workspace *currentWorkspace() const = 0;
57+ virtual void setCurrentWorkspace(Workspace* workspace) = 0;
3958
40− qtmir::ScreenConfiguration *beginConfiguration() const override;
41− bool applyConfiguration(qtmir::ScreenConfiguration *configuration) override;
59+ void sync(ScreenInterface* proxy);
4260
4361 qtmir::Screen* wrapped() const { return m_wrapped; }
4462
45− WorkspaceModel* workspaces() const;
46−
47− Workspace *currentWorkspace() const;
48− void setCurrentWorkspace(Workspace* workspace);
49−
50− void sync(Screen* proxy);
51−
5263public Q_SLOTS:
5364 void activate();
5465
5566Q_SIGNALS:
67+ void usedChanged();
68+ void nameChanged();
69+ void outputTypeChanged();
70+ void scaleChanged();
71+ void formFactorChanged();
72+ void powerModeChanged();
73+ void orientationChanged();
74+ void positionChanged();
75+ void currentModeIndexChanged();
76+ void physicalSizeChanged();
77+ void availableModesChanged();
78+ void activeChanged(bool active);
5679 void currentWorkspaceChanged(Workspace*);
57− void outputTypeNameChanged();
5880
5981protected:
60− Screen(Screen const& other);
82+ ScreenInterface(QObject* parent = 0);
83+
84+ void connectToScreen(qtmir::Screen* screen);
85+
86+protected:
87+ QPointer<qtmir::Screen> m_wrapped;
88+};
6189
90+
91+class Screen : public ScreenInterface
92+{
93+ Q_OBJECT
94+public:
95+ explicit Screen(qtmir::Screen*const wrapped);
96+
97+ // From qtmir::Screen
98+ WorkspaceModel* workspaces() const override;
99+ Workspace *currentWorkspace() const override;
100+ void setCurrentWorkspace(Workspace* workspace) override;
101+
102+ static ScreenAttached *qmlAttachedProperties(QObject *owner);
103+
104+protected:
62105 void resetCurrentWorkspace();
63106
64− qtmir::Screen*const m_wrapped;
65107 const QScopedPointer<WorkspaceModel> m_workspaces;
66108 QPointer<Workspace> m_currentWorspace;
67109};
68110
69−class ScreenProxy : public Screen
111+class ScreenProxy : public ScreenInterface
70112{
71113 Q_OBJECT
72114public:
73− explicit ScreenProxy(Screen*const screen);
115+ explicit ScreenProxy(ScreenInterface*const screen);
74116
75− Screen* proxyObject() const { return m_original.data(); }
117+ // From qtmir::Screen
118+ WorkspaceModel* workspaces() const override;
119+ Workspace *currentWorkspace() const override;
120+ void setCurrentWorkspace(Workspace* workspace) override;
121+
122+ ScreenInterface* proxyObject() const { return m_original.data(); }
76123
77124public Q_SLOTS:
78125 void addWorkspace();
79126
80127private:
81− const QPointer<Screen> m_original;
128+ const QScopedPointer<WorkspaceModel> m_workspaces;
129+ const QPointer<ScreenInterface> m_original;
130+ QPointer<Workspace> m_currentWorspace;
82131};
83132
84133#endif // SCREEN_H
plugins/WindowManager/ScreenAttached.cppnew file+149
@@ -0,0 +1,149 @@
1+/*
2+ * Copyright (C) 2017 Canonical, Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it under
5+ * the terms of the GNU Lesser General Public License version 3, as published by
6+ * the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but WITHOUT
9+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11+ * Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "ScreenAttached.h"
18+#include "ScreenWindow.h"
19+#include "Screens.h"
20+
21+#include <QQuickItem>
22+#include <QScreen>
23+
24+ScreenAttached::ScreenAttached(QObject *owner)
25+ : ScreenInterface(owner)
26+ , m_window(nullptr)
27+{
28+ if (auto item = qobject_cast<QQuickItem*>(owner)) {
29+ connect(item, &QQuickItem::windowChanged, this, &ScreenAttached::windowChanged);
30+ windowChanged(item->window());
31+ } else if (auto window = qobject_cast<QQuickWindow*>(owner)) {
32+ windowChanged(window);
33+ }
34+}
35+
36+WorkspaceModel *ScreenAttached::workspaces() const
37+{
38+ if (!m_screen) return nullptr;
39+ return m_screen->workspaces();
40+}
41+
42+Workspace *ScreenAttached::currentWorkspace() const
43+{
44+ if (!m_screen) return nullptr;
45+ return m_screen->currentWorkspace();
46+}
47+
48+void ScreenAttached::setCurrentWorkspace(Workspace *workspace)
49+{
50+ if (!m_screen) return;
51+ return m_screen->setCurrentWorkspace(workspace);
52+}
53+
54+void ScreenAttached::windowChanged(QQuickWindow *window)
55+{
56+ if (m_window) {
57+ disconnect(m_window, &QWindow::screenChanged, this, &ScreenAttached::screenChanged);
58+ }
59+
60+ m_window = window;
61+ auto screenWindow = qobject_cast<ScreenWindow*>(window);
62+
63+ if (screenWindow) {
64+ screenChanged2(screenWindow->screenWrapper());
65+ connect(screenWindow, &ScreenWindow::screenWrapperChanged, this, &ScreenAttached::screenChanged2);
66+ } else {
67+ screenChanged(window ? window->screen() : NULL);
68+ if (window) {
69+ connect(window, &QWindow::screenChanged, this, &ScreenAttached::screenChanged);
70+ }
71+ }
72+}
73+
74+void ScreenAttached::screenChanged(QScreen *qscreen)
75+{
76+ // Find a screen that matches.
77+ // Should only get here in mocks if we don't have a ScreenWindow
78+ ScreenInterface* screen{nullptr};
79+ Q_FOREACH(auto s, Screens::self()->list()) {
80+ if (s->qscreen() == qscreen) {
81+ screen = s;
82+ }
83+ }
84+ screenChanged2(screen);
85+}
86+
87+void ScreenAttached::screenChanged2(ScreenInterface* screen)
88+{
89+ if (screen == m_screen) return;
90+
91+ ScreenInterface* oldScreen = m_screen;
92+ m_screen = screen;
93+
94+ if (oldScreen)
95+ oldScreen->disconnect(this);
96+
97+ if (!screen)
98+ return; //Don't bother emitting signals, because the new values are garbage anyways
99+
100+ if (!oldScreen || screen->isActive() != oldScreen->isActive())
101+ Q_EMIT activeChanged(screen->isActive());
102+ if (!oldScreen || screen->used() != oldScreen->used())
103+ Q_EMIT usedChanged();
104+ if (!oldScreen || screen->name() != oldScreen->name())
105+ Q_EMIT nameChanged();
106+ if (!oldScreen || screen->outputType() != oldScreen->outputType())
107+ Q_EMIT outputTypeChanged();
108+ if (!oldScreen || screen->scale() != oldScreen->scale())
109+ Q_EMIT scaleChanged();
110+ if (!oldScreen || screen->formFactor() != oldScreen->formFactor())
111+ Q_EMIT formFactorChanged();
112+ if (!oldScreen || screen->powerMode() != oldScreen->powerMode())
113+ Q_EMIT powerModeChanged();
114+ if (!oldScreen || screen->orientation() != oldScreen->orientation())
115+ Q_EMIT orientationChanged();
116+ if (!oldScreen || screen->position() != oldScreen->position())
117+ Q_EMIT positionChanged();
118+ if (!oldScreen || screen->currentModeIndex() != oldScreen->currentModeIndex())
119+ Q_EMIT currentModeIndexChanged();
120+ if (!oldScreen || screen->physicalSize() != oldScreen->physicalSize())
121+ Q_EMIT physicalSizeChanged();
122+
123+ if (oldScreen) {
124+ QVector<qtmir::ScreenMode*> oldModes;
125+ auto oldModesQmlList = oldScreen->availableModes();
126+ for (int i = 0; i < oldModesQmlList.count(&oldModesQmlList); i++) {
127+ oldModes << oldModesQmlList.at(&oldModesQmlList, i);
128+ }
129+
130+ QVector<qtmir::ScreenMode*> newModes;
131+ auto newModesQmlList = screen->availableModes();
132+ for (int i = 0; i < newModesQmlList.count(&newModesQmlList); i++) {
133+ newModes << newModesQmlList.at(&newModesQmlList, i);
134+ }
135+
136+ if (newModes != newModes) {
137+ Q_EMIT availableModesChanged();
138+ }
139+ } else {
140+ Q_EMIT availableModesChanged();
141+ }
142+
143+ connectToScreen(screen->wrapped());
144+}
145+
146+ScreenAttached *WMScreen::qmlAttachedProperties(QObject *owner)
147+{
148+ return new ScreenAttached(owner);
149+}
plugins/WindowManager/ScreenAttached.hnew file+56
@@ -0,0 +1,56 @@
1+/*
2+ * Copyright (C) 2017 Canonical, Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it under
5+ * the terms of the GNU Lesser General Public License version 3, as published by
6+ * the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but WITHOUT
9+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11+ * Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef SCREENATTACHED_H
18+#define SCREENATTACHED_H
19+
20+#include "Screen.h"
21+
22+#include <QQmlEngine>
23+#include <QtQml>
24+
25+class QQuickWindow;
26+
27+class ScreenAttached : public ScreenInterface
28+{
29+ Q_OBJECT
30+public:
31+ ScreenAttached(QObject* owner);
32+
33+ WorkspaceModel* workspaces() const override;
34+ Workspace *currentWorkspace() const override;
35+ void setCurrentWorkspace(Workspace* workspace) override;
36+
37+private Q_SLOTS:
38+ void windowChanged(QQuickWindow*);
39+ void screenChanged(QScreen*);
40+ void screenChanged2(ScreenInterface* screen);
41+
42+private:
43+ QPointer<ScreenInterface> m_screen;
44+ QQuickWindow* m_window;
45+};
46+
47+class WMScreen : public QObject
48+{
49+ Q_OBJECT
50+public:
51+ static ScreenAttached *qmlAttachedProperties(QObject *owner);
52+};
53+
54+QML_DECLARE_TYPEINFO(WMScreen, QML_HAS_ATTACHED_PROPERTIES)
55+
56+#endif // SCREENATTACHED_H
plugins/WindowManager/ScreenWindow.cpp+1−4
@@ -24,9 +24,6 @@
2424ScreenWindow::ScreenWindow(QQuickWindow *parent)
2525 : QQuickWindow(parent)
2626{
27− if (qGuiApp->platformName() != QLatin1String("mirserver")) {
28− qCritical("Not using 'mirserver' QPA plugin. Using ScreenWindow may produce unknown results.");
29− }
3027}
3128
3229ScreenWindow::~ScreenWindow()
@@ -42,7 +39,7 @@ void ScreenWindow::setScreenWrapper(Screen *screen)
4239{
4340 if (m_screen != screen) {
4441 m_screen = screen;
45− Q_EMIT screenWrapperChanged();
42+ Q_EMIT screenWrapperChanged(screen);
4643 }
4744 QQuickWindow::setScreen(screen->qscreen());
4845}
plugins/WindowManager/ScreenWindow.h+1−1
@@ -40,7 +40,7 @@ public:
4040 void setScreenWrapper(Screen *screen);
4141
4242Q_SIGNALS:
43− void screenWrapperChanged();
43+ void screenWrapperChanged(Screen* screen);
4444
4545private:
4646 QPointer<Screen> m_screen;
plugins/WindowManager/Screens.cpp+13−5
@@ -27,9 +27,12 @@
2727#include <QScreen>
2828#include <QWindow>
2929
30+Screens* Screens::m_self{nullptr};
31+
3032Screens::Screens(const QSharedPointer<qtmir::Screens>& model)
3133 : m_wrapped(model)
3234{
35+ m_self = this;
3336 connect(m_wrapped.data(), &qtmir::Screens::screenAdded, this, &Screens::onScreenAdded);
3437 connect(m_wrapped.data(), &qtmir::Screens::screenRemoved, this, &Screens::onScreenRemoved);
3538 connect(m_wrapped.data(), &qtmir::Screens::activeScreenChanged, this, &Screens::activeScreenChanged);
@@ -92,6 +95,11 @@ QVariant Screens::activeScreen() const
9295 return QVariant();
9396}
9497
98+Screens *Screens::self()
99+{
100+ return Screens::m_self;
101+}
102+
95103ScreensProxy *Screens::createProxy()
96104{
97105 return new ScreensProxy(this);
@@ -138,7 +146,7 @@ void Screens::onScreenAdded(qtmir::Screen *added)
138146void Screens::onScreenRemoved(qtmir::Screen *removed)
139147{
140148 int index = 0;
141− QMutableVectorIterator<Screen*> iter(m_screens);
149+ QMutableVectorIterator<ScreenInterface*> iter(m_screens);
142150 while(iter.hasNext()) {
143151 auto screenWrapper = iter.next();
144152 if (screenWrapper->wrapped() == removed) {
@@ -161,7 +169,7 @@ ScreensProxy::ScreensProxy(Screens * const screens)
161169 : Screens(*screens)
162170 , m_original(screens)
163171{
164− connect(screens, &Screens::screenAdded, this, [this](Screen *added) {
172+ connect(screens, &Screens::screenAdded, this, [this](ScreenInterface *added) {
165173 Q_FOREACH(auto screen, m_screens) {
166174 auto proxy = static_cast<ScreenProxy*>(screen);
167175 if (proxy->proxyObject() == added) return;
@@ -176,9 +184,9 @@ ScreensProxy::ScreensProxy(Screens * const screens)
176184 Q_EMIT countChanged();
177185 });
178186
179− connect(screens, &Screens::screenRemoved, this, [this](Screen *removed) {
187+ connect(screens, &Screens::screenRemoved, this, [this](ScreenInterface *removed) {
180188 int index = 0;
181− QMutableVectorIterator<Screen*> iter(m_screens);
189+ QMutableVectorIterator<ScreenInterface*> iter(m_screens);
182190 while(iter.hasNext()) {
183191 auto proxy = static_cast<ScreenProxy*>(iter.next());
184192 if (proxy->proxyObject() == removed) {
@@ -197,7 +205,7 @@ ScreensProxy::ScreensProxy(Screens * const screens)
197205 }
198206 });
199207
200− Q_FOREACH(Screen* screen, screens->list()) {
208+ Q_FOREACH(ScreenInterface* screen, screens->list()) {
201209 auto screenWrapper(new ScreenProxy(screen));
202210 QQmlEngine::setObjectOwnership(screenWrapper, QQmlEngine::CppOwnership);
203211 m_screens.push_back(new ScreenProxy(screen));
plugins/WindowManager/Screens.h+8−5
@@ -27,7 +27,7 @@ class Screen;
2727class Screens;
2828}
2929
30−class Screen;
30+class ScreenInterface;
3131class ScreensProxy;
3232
3333class Screens : public QAbstractListModel
@@ -55,7 +55,9 @@ public:
5555 int count() const;
5656 QVariant activeScreen() const;
5757
58− const QVector<Screen*>& list() const { return m_screens; }
58+ const QVector<ScreenInterface*>& list() const { return m_screens; }
59+
60+ static Screens *self();
5961
6062public Q_SLOTS:
6163 void activateScreen(const QVariant& index);
@@ -64,8 +66,8 @@ Q_SIGNALS:
6466 void countChanged();
6567 void activeScreenChanged();
6668
67− void screenAdded(Screen* screen);
68− void screenRemoved(Screen* screen);
69+ void screenAdded(ScreenInterface* screen);
70+ void screenRemoved(ScreenInterface* screen);
6971
7072private Q_SLOTS:
7173 void onScreenAdded(qtmir::Screen *screen);
@@ -74,8 +76,9 @@ private Q_SLOTS:
7476protected:
7577 Screens(const Screens& other);
7678
77− QVector<Screen*> m_screens;
79+ QVector<ScreenInterface*> m_screens;
7880 QSharedPointer<qtmir::Screens> m_wrapped;
81+ static Screens* m_self;
7982};
8083
8184class ScreensProxy : public Screens
plugins/WindowManager/WindowManagerPlugin.cpp+9−4
@@ -16,8 +16,9 @@
1616
1717#include "WindowManagerPlugin.h"
1818
19−#include "Screens.h"
2019#include "Screen.h"
20+#include "ScreenAttached.h"
21+#include "Screens.h"
2122#include "ScreenWindow.h"
2223#include "TopLevelWindowModel.h"
2324#include "Window.h"
@@ -28,6 +29,8 @@
2829#include <QtQml>
2930#include <qtmir/qtmir.h>
3031
32+static const QString notInstantiatable = QStringLiteral("Not instantiatable");
33+
3134static QObject *workspace_manager(QQmlEngine *engine, QJSEngine *scriptEngine)
3235{
3336 Q_UNUSED(engine)
@@ -44,10 +47,10 @@ void WindowManagerPlugin::registerTypes(const char *uri)
4447{
4548 qmlRegisterType<TopLevelWindowModel>(uri, 1, 0, "TopLevelWindowModel");
4649 qmlRegisterSingletonType<WorkspaceManager>(uri, 1, 0, "WorkspaceManager", workspace_manager);
47− qmlRegisterUncreatableType<WorkspaceModel>(uri, 1, 0, "WorkspaceModel", "Not a creatable type");
50+ qmlRegisterUncreatableType<WorkspaceModel>(uri, 1, 0, "WorkspaceModel", notInstantiatable);
4851 qmlRegisterSingletonType<Screens>(uri, 1, 0, "Screens", screensSingleton);
49− qmlRegisterUncreatableType<qtmir::ScreenMode>(uri, 1, 0, "ScreenMode", "ScreenMode is not creatable.");
50− qmlRegisterUncreatableType<Workspace>(uri, 1, 0, "Workspace", "Workspace is not creatable.");
52+ qmlRegisterUncreatableType<qtmir::ScreenMode>(uri, 1, 0, "ScreenMode", notInstantiatable);
53+ qmlRegisterUncreatableType<Workspace>(uri, 1, 0, "Workspace", notInstantiatable);
5154
5255 qRegisterMetaType<Screen*>("Screen*");
5356 qRegisterMetaType<ScreensProxy*>("ScreensProxy*");
@@ -58,4 +61,6 @@ void WindowManagerPlugin::registerTypes(const char *uri)
5861
5962 qmlRegisterType<ScreenWindow>(uri, 1, 0, "ScreenWindow");
6063 qmlRegisterRevision<QWindow,1>(uri, 1, 0);
64+
65+ qmlRegisterUncreatableType<WMScreen>(uri, 1, 0, "WMScreen", notInstantiatable);
6166}
plugins/WindowManager/WorkspaceModel.cpp+33−4
@@ -43,7 +43,7 @@ void WorkspaceModel::insert(int index, Workspace *workspace)
4343
4444 endInsertRows();
4545
46− Q_EMIT workspaceAdded(workspace);
46+ Q_EMIT workspaceInserted(index, workspace);
4747 Q_EMIT countChanged();
4848}
4949
@@ -78,7 +78,8 @@ void WorkspaceModel::move(int from, int to)
7878 m_workspaces.move(from, to);
7979#endif
8080 endMoveRows();
81− Q_EMIT countChanged();
81+
82+ Q_EMIT workspaceMoved(from, to);
8283 }
8384}
8485
@@ -114,6 +115,7 @@ QVariant WorkspaceModel::data(const QModelIndex &index, int role) const
114115void WorkspaceModel::sync(WorkspaceModel *proxy)
115116{
116117 if (!proxy) return;
118+ m_syncing = proxy;
117119 const auto& proxyList = proxy->list();
118120
119121 // check for removals
@@ -123,7 +125,7 @@ void WorkspaceModel::sync(WorkspaceModel *proxy)
123125
124126 bool found = false;
125127 Q_FOREACH(auto p, proxyList) {
126− auto workspaceProxy = static_cast<WorkspaceProxy*>(p);
128+ auto workspaceProxy = qobject_cast<WorkspaceProxy*>(p);
127129 if (workspaceProxy->proxyObject() == workspace) {
128130 found = true;
129131 break;
@@ -140,7 +142,7 @@ void WorkspaceModel::sync(WorkspaceModel *proxy)
140142 // existing
141143 QSet<Workspace*> newWorkspaces;
142144 for (int i = 0; i < proxyList.count(); i++) {
143− auto workspaceProxy = static_cast<WorkspaceProxy*>(proxyList[i]);
145+ auto workspaceProxy = qobject_cast<WorkspaceProxy*>(proxyList[i]);
144146 auto workspace = workspaceProxy->proxyObject();
145147
146148 int oldIndex = this->indexOf(workspace);
@@ -166,8 +168,13 @@ void WorkspaceModel::sync(WorkspaceModel *proxy)
166168
167169 WorkspaceManager::instance()->setActiveWorkspace(newActiveWorkspace);
168170 }
171+ m_syncing = nullptr;
169172}
170173
174+bool WorkspaceModel::isSyncingWith(WorkspaceModel *m)
175+{
176+ return m_syncing == m;
177+}
171178
172179WorkspaceModelProxy::WorkspaceModelProxy(WorkspaceModel * const model)
173180 : m_original(model)
@@ -177,6 +184,28 @@ WorkspaceModelProxy::WorkspaceModelProxy(WorkspaceModel * const model)
177184 QQmlEngine::setObjectOwnership(proxy, QQmlEngine::CppOwnership);
178185 proxy->assign(this);
179186 }
187+ connect(m_original, &WorkspaceModel::workspaceInserted, this, [this](int index, Workspace* inserted) {
188+ if (m_original->isSyncingWith(this)) return;
189+
190+ (new WorkspaceProxy(inserted))->assign(this, index);
191+ });
192+ connect(m_original, &WorkspaceModel::workspaceRemoved, this, [this](Workspace* removed) {
193+ if (m_original->isSyncingWith(this)) return;
194+
195+ for (int i = 0; i < rowCount(); i++) {
196+ auto workspaceProxy = qobject_cast<WorkspaceProxy*>(get(i));
197+ auto w = workspaceProxy->proxyObject();
198+ if (w == removed) {
199+ remove(workspaceProxy);
200+ break;
201+ }
202+ }
203+ });
204+ connect(m_original, &WorkspaceModel::workspaceMoved, this, [this](int from, int to) {
205+ if (m_original->isSyncingWith(this)) return;
206+
207+ move(from, to);
208+ });
180209}
181210
182211WorkspaceModelProxy::~WorkspaceModelProxy()
plugins/WindowManager/WorkspaceModel.h+5−2
@@ -61,15 +61,18 @@ public:
6161 const QVector<Workspace*>& list() const { return m_workspaces; }
6262
6363 void sync(WorkspaceModel* proxy);
64+ bool isSyncingWith(WorkspaceModel*);
6465
6566Q_SIGNALS:
6667 void countChanged();
6768
68− void workspaceAdded(Workspace *workspace);
69+ void workspaceInserted(int index, Workspace *workspace);
6970 void workspaceRemoved(Workspace *workspace);
71+ void workspaceMoved(int from, int to);
7072
7173protected:
7274 QVector<Workspace*> m_workspaces;
75+ WorkspaceModel* m_syncing;
7376};
7477
7578class WorkspaceModelProxy : public WorkspaceModel
@@ -81,7 +84,7 @@ public:
8184
8285 Q_INVOKABLE void move(int from, int to) override;
8386
84−private:
87+protected:
8588 const QPointer<WorkspaceModel> m_original;
8689};
8790
tests/mocks/WindowManager/CMakeLists.txt+3−1
@@ -21,14 +21,16 @@ qt5_use_modules(mockwindowmanagmentpolicy Core)
2121
2222set(WINDOWMANAGER_SRC
2323 ${CMAKE_SOURCE_DIR}/plugins/WindowManager/Screen.cpp
24+ ${CMAKE_SOURCE_DIR}/plugins/WindowManager/ScreenAttached.cpp
2425 ${CMAKE_SOURCE_DIR}/plugins/WindowManager/Screens.cpp
26+ ${CMAKE_SOURCE_DIR}/plugins/WindowManager/ScreenWindow.cpp
2527 ${CMAKE_SOURCE_DIR}/plugins/WindowManager/TopLevelWindowModel.cpp
2628 ${CMAKE_SOURCE_DIR}/plugins/WindowManager/Window.cpp
2729 ${CMAKE_SOURCE_DIR}/plugins/WindowManager/WorkspaceManager.cpp
2830 ${CMAKE_SOURCE_DIR}/plugins/WindowManager/WorkspaceModel.cpp
2931 ${CMAKE_SOURCE_DIR}/plugins/WindowManager/Workspace.cpp
3032 MockScreens.cpp
31− ScreenWindow.cpp
33+ MockScreenWindow.cpp
3234 WindowManagerPlugin.cpp
3335 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
3436 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/Mir.h
tests/mocks/WindowManager/ScreenWindow.cpp → tests/mocks/WindowManager/MockScreenWindow.cpprenamed+8−22
@@ -14,35 +14,21 @@
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1515 */
1616
17−#include "ScreenWindow.h"
18−#include "Screen.h"
17+#include "MockScreenWindow.h"
18+#include "MockScreens.h"
1919
2020// Qt
2121#include <QGuiApplication>
2222#include <QDebug>
2323
24−ScreenWindow::ScreenWindow(QQuickWindow *parent)
25− : QQuickWindow(parent)
24+MockScreenWindow::MockScreenWindow(QQuickWindow *parent)
25+ : ScreenWindow(parent)
2626{
27− if (qGuiApp->platformName() != QLatin1String("mirserver")) {
28− qCritical("Not using 'mirserver' QPA plugin. Using ScreenWindow may produce unknown results.");
29− }
27+ connect(this, &ScreenWindow::screenWrapperChanged, this, [this]() {
28+ MockScreens::instance()->connectWindow(this);
29+ });
3030}
3131
32−ScreenWindow::~ScreenWindow()
32+MockScreenWindow::~MockScreenWindow()
3333{
3434}
35−
36−Screen *ScreenWindow::screenWrapper() const
37−{
38− return m_screen.data();
39−}
40−
41−void ScreenWindow::setScreenWrapper(Screen *screen)
42−{
43− if (m_screen != screen) {
44− m_screen = screen;
45− Q_EMIT screenWrapperChanged();
46− }
47− QQuickWindow::setScreen(screen->qscreen());
48−}
tests/mocks/WindowManager/ScreenWindow.h → tests/mocks/WindowManager/MockScreenWindow.hrenamed+7−26
@@ -14,36 +14,17 @@
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1515 */
1616
17−#ifndef UNITY_SCREENWINDOW_H
18−#define UNITY_SCREENWINDOW_H
17+#ifndef MOCK_SCREENWINDOW_H
18+#define MOCK_SCREENWINDOW_H
1919
20−#include <QQuickWindow>
21−#include <QPointer>
20+#include "ScreenWindow.h"
2221
23−#include "Screen.h"
24−
25−class ScreenAdapter;
26−
27−/*
28− * ScreenWindow - wrapper of QQuickWindow to enable QML to specify destination screen.
29−**/
30−class ScreenWindow : public QQuickWindow
22+class MockScreenWindow : public ScreenWindow
3123{
3224 Q_OBJECT
33− Q_PROPERTY(Screen *screen READ screenWrapper WRITE setScreenWrapper NOTIFY screenWrapperChanged)
34− Q_PROPERTY(int winId READ winId CONSTANT)
3525public:
36− explicit ScreenWindow(QQuickWindow *parent = 0);
37− ~ScreenWindow();
38−
39− Screen *screenWrapper() const;
40− void setScreenWrapper(Screen *screen);
41−
42−Q_SIGNALS:
43− void screenWrapperChanged();
44−
45−private:
46− QPointer<Screen> m_screen;
26+ explicit MockScreenWindow(QQuickWindow *parent = 0);
27+ ~MockScreenWindow();
4728};
4829
49−#endif // UNITY_SCREENWINDOW_H
30+#endif // MOCK_SCREENWINDOW_H
tests/mocks/WindowManager/MockScreens.cpp+69−1
@@ -15,12 +15,19 @@
1515 */
1616
1717#include "MockScreens.h"
18+#include "ScreenWindow.h"
1819
1920// qtmirserver
2021#include <qtmir/screen.h>
2122
23+#include <QGuiApplication>
24+#include <QWindow>
25+#include <QWeakPointer>
26+
2227namespace {
2328
29+QWeakPointer<MockScreens> m_screens;
30+
2431class MockScreen : public qtmir::Screen
2532{
2633 Q_OBJECT
@@ -34,6 +41,37 @@ public:
3441 m_sizes.append(new qtmir::ScreenMode(60, QSize(1920,1080)));
3542 m_physicalSize = QSize(800,568);
3643 }
44+ ~MockScreen() {
45+ qDeleteAll(m_sizes);
46+ m_sizes.clear();
47+ }
48+
49+ void connectToWindow(QWindow* w)
50+ {
51+ if (m_connectedWindow == w) return;
52+
53+ if (m_connectedWindow) {
54+ disconnect(m_connectedWindow.data());
55+ m_sizes.takeFirst()->deleteLater();
56+ }
57+
58+ m_connectedWindow = w;
59+
60+ if (w) {
61+ connect(w, &ScreenWindow::heightChanged, this, [this](int height) {
62+ m_sizes.first()->size.rheight() = height;
63+ Q_EMIT availableModesChanged();
64+
65+ });
66+ connect(w, &ScreenWindow::widthChanged, this, [this](int width) {
67+ m_sizes.first()->size.rwidth() = width;
68+ Q_EMIT availableModesChanged();
69+ });
70+
71+ m_sizes.push_front(new qtmir::ScreenMode(50, w->size()));
72+ Q_EMIT availableModesChanged();
73+ }
74+ }
3775
3876 qtmir::OutputId outputId() const override { return m_id; }
3977 bool used() const override { return m_used; }
@@ -60,7 +98,12 @@ public:
6098 }
6199 }
62100
63− QScreen* qscreen() const override { return nullptr; }
101+ QScreen* qscreen() const override {
102+ if (qGuiApp->topLevelWindows().count() > 0) {
103+ return qGuiApp->topLevelWindows()[0]->screen();
104+ }
105+ return qGuiApp->primaryScreen();
106+ }
64107
65108 qtmir::ScreenConfiguration *beginConfiguration() const override {
66109 auto config = new qtmir::ScreenConfiguration;
@@ -102,6 +145,8 @@ public:
102145 uint m_currentModeIndex{0};
103146 QList<qtmir::ScreenMode*> m_sizes;
104147 QSizeF m_physicalSize;
148+
149+ QPointer<QWindow> m_connectedWindow;
105150};
106151}
107152
@@ -127,6 +172,7 @@ MockScreens::MockScreens()
127172MockScreens::~MockScreens()
128173{
129174 qDeleteAll(m_mocks);
175+ m_mocks.clear();
130176}
131177
132178QVector<qtmir::Screen *> MockScreens::screens() const
@@ -142,4 +188,26 @@ qtmir::Screen *MockScreens::activeScreen() const
142188 return nullptr;
143189}
144190
191+QSharedPointer<MockScreens> MockScreens::instance()
192+{
193+ if (!m_screens) {
194+ QSharedPointer<MockScreens> screens(new MockScreens());
195+ m_screens = screens.toWeakRef();
196+ return screens;
197+ } else {
198+ return m_screens.lock();
199+ }
200+}
201+
202+void MockScreens::connectWindow(ScreenWindow *w)
203+{
204+ Screen* screen = w->screenWrapper();
205+ if (!screen) return;
206+
207+ auto mockScreen = qobject_cast<MockScreen*>(screen->wrapped());
208+ if (mockScreen) {
209+ mockScreen->connectToWindow(w);
210+ }
211+}
212+
145213#include "MockScreens.moc"
tests/mocks/WindowManager/MockScreens.h+9−1
@@ -22,12 +22,14 @@
2222
2323#include <qtmir/screens.h>
2424
25−class Screen;
2625namespace qtmir
2726{
2827class Screens;
2928}
3029
30+class Screen;
31+class ScreenWindow;
32+
3133class MockScreens : public qtmir::Screens
3234{
3335 Q_OBJECT
@@ -39,8 +41,14 @@ public:
3941
4042 qtmir::Screen *activeScreen() const override;
4143
44+ static QSharedPointer<MockScreens> instance();
45+
46+public Q_SLOTS:
47+ void connectWindow(ScreenWindow *w);
48+
4249private:
4350 QVector<qtmir::Screen*> m_mocks;
4451};
4552
53+
4654#endif // MOCK_SCREENS_H
tests/mocks/WindowManager/WindowManagerPlugin.cpp+13−9
@@ -16,10 +16,11 @@
1616
1717#include "WindowManagerPlugin.h"
1818
19−#include "Screens.h"
20−#include "Screen.h"
2119#include "MockScreens.h"
22−#include "ScreenWindow.h"
20+#include "MockScreenWindow.h"
21+#include "Screen.h"
22+#include "ScreenAttached.h"
23+#include "Screens.h"
2324#include "TopLevelWindowModel.h"
2425#include "Window.h"
2526#include "WorkspaceManager.h"
@@ -29,6 +30,8 @@
2930
3031#include <QtQml>
3132
33+static const QString notInstantiatable = QStringLiteral("Not instantiatable");
34+
3235static QObject *workspace_manager(QQmlEngine *engine, QJSEngine *scriptEngine)
3336{
3437 Q_UNUSED(engine)
@@ -38,18 +41,17 @@ static QObject *workspace_manager(QQmlEngine *engine, QJSEngine *scriptEngine)
3841QObject* screensSingleton(QQmlEngine* engine, QJSEngine* scriptEngine) {
3942 Q_UNUSED(engine);
4043 Q_UNUSED(scriptEngine);
41− QSharedPointer<qtmir::Screens> mockScreens(new MockScreens());
42− return new Screens(mockScreens);
44+ return new Screens(MockScreens::instance());
4345}
4446
4547void WindowManagerPlugin::registerTypes(const char *uri)
4648{
4749 qmlRegisterType<TopLevelWindowModel>(uri, 1, 0, "TopLevelWindowModel");
4850 qmlRegisterSingletonType<WorkspaceManager>(uri, 1, 0, "WorkspaceManager", workspace_manager);
49− qmlRegisterUncreatableType<WorkspaceModel>(uri, 1, 0, "WorkspaceModel", "Not a creatable type");
51+ qmlRegisterUncreatableType<WorkspaceModel>(uri, 1, 0, "WorkspaceModel", notInstantiatable);
5052 qmlRegisterSingletonType<Screens>(uri, 1, 0, "Screens", screensSingleton);
51− qmlRegisterUncreatableType<qtmir::ScreenMode>(uri, 1, 0, "ScreenMode", "ScreenMode is not creatable.");
52− qmlRegisterUncreatableType<Workspace>(uri, 1, 0, "Workspace", "Workspace is not creatable.");
53+ qmlRegisterUncreatableType<qtmir::ScreenMode>(uri, 1, 0, "ScreenMode", notInstantiatable);
54+ qmlRegisterUncreatableType<Workspace>(uri, 1, 0, "Workspace", notInstantiatable);
5355
5456 qRegisterMetaType<Screen*>("Screen*");
5557 qRegisterMetaType<ScreensProxy*>("ScreensProxy*");
@@ -58,8 +60,10 @@ void WindowManagerPlugin::registerTypes(const char *uri)
5860 qRegisterMetaType<Window*>("Window*");
5961 qRegisterMetaType<QAbstractListModel*>("QAbstractListModel*");
6062
61− qmlRegisterType<ScreenWindow>(uri, 1, 0, "ScreenWindow");
63+ qmlRegisterType<MockScreenWindow>(uri, 1, 0, "ScreenWindow");
6264 qmlRegisterRevision<QWindow,1>(uri, 1, 0);
65+
66+ qmlRegisterUncreatableType<WMScreen>(uri, 1, 0, "WMScreen", notInstantiatable);
6367}
6468
6569void WindowManagerPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
tests/qmltests/tst_ShellApplication.qml+1
@@ -22,6 +22,7 @@ import Unity.Application 0.1
2222import Unity.Test 0.1
2323
2424import "../../qml"
25+import "../../qml/Components"
2526import "Stage"
2627
2728Rectangle {