on 87d6679 help sign in
69 files changed+1235−613
since splitsnapshots
unifiedsplit
debian/control+1−1 debian/unity8.install+5 plugins/Cursor/CMakeLists.txt+1 plugins/Cursor/InputDispatcherFilter.cpp+175 plugins/Cursor/InputDispatcherFilter.h+62 plugins/Cursor/MousePointer.cpp+63−78 plugins/Cursor/MousePointer.h+5−4 plugins/GlobalShortcut/globalshortcut.cpp−15 plugins/GlobalShortcut/globalshortcut.h−4 plugins/GlobalShortcut/globalshortcutregistry.cpp+23−15 plugins/GlobalShortcut/globalshortcutregistry.h+2−2 plugins/UInput/plugin.cpp+7−1 qml/ApplicationMenus/ApplicationMenuItemFactory.qml+4−4 qml/ApplicationMenus/MenuBar.qml+5−1 qml/ApplicationMenus/MenuItem.qml+2 qml/ApplicationMenus/MenuPopup.qml+4−1 qml/CMakeLists.txt+1−1 qml/Components/PanelState/PanelState.qml−1 qml/Components/PanelState/qmldir+1−1 qml/Components/VirtualTouchPad.qml+13−19 qml/DisabledScreenNotice.qml+2−1 qml/ErrorApplication.qml+67 qml/Greeter/Greeter.qml+21−3 qml/Greeter/SecondaryGreeter.qml+75 qml/OrientedShell.qml+6−8 qml/Panel/Panel.qml+17−15 qml/Shell.qml+29−11 qml/ShellApplication.qml+52 src/SecondaryWindow.h → qml/ShellNotifier.qml+12−12 qml/ShellScreen.qml+66 qml/Stage/DecoratedWindow.qml+2−1 qml/Stage/FakeMaximizeDelegate.qml+12−11 qml/Stage/Stage.qml+27−24 qml/qmldir+1 src/ApplicationArguments.cpp+11−2 src/ApplicationArguments.h+17−15 src/CMakeLists.txt−2 src/SecondaryWindow.cpp−40 src/ShellApplication.cpp+27−102 src/ShellApplication.h+1−13 src/ShellView.cpp−85 src/ShellView.h−34 src/UnityCommandLineParser.h+1−1 tests/mocks/Cursor/Cursor.qml+49−2 tests/mocks/GSettings.1.0/plugin.cpp+12 tests/mocks/GSettings.1.0/plugin.h+1 tests/mocks/UInput/plugin.cpp+6−1 tests/mocks/Unity/Application/SurfaceManager.cpp+1−1 tests/mocks/Unity/InputInfo/mockcontroller.cpp+10 tests/mocks/Unity/InputInfo/mockcontroller.h+3−1 tests/mocks/Unity/InputInfo/plugin.cpp+1−2 tests/mocks/Unity/Screens/CMakeLists.txt+2−1 tests/mocks/Unity/Screens/plugin.cpp+14−1 tests/mocks/Unity/Screens/screens.cpp+52−9 tests/mocks/Unity/Screens/screens.h+84−4 tests/mocks/Unity/Screens/screenwindow.cpp+35 tests/mocks/Unity/Screens/screenwindow.h+42 tests/qmltests/ApplicationMenus/tst_MenuBar.qml+2 tests/qmltests/ApplicationMenus/tst_MenuPopup.qml+2 tests/qmltests/Components/tst_VirtualTouchPad.qml+2−2 tests/qmltests/Panel/tst_Panel.qml+18−14 tests/qmltests/Stage/tst_DecoratedWindow.qml+5−1 tests/qmltests/Stage/tst_DesktopStage.qml+8−3 tests/qmltests/Stage/tst_PhoneStage.qml+2 tests/qmltests/Stage/tst_WindowResizeArea.qml+1−8 tests/qmltests/tst_OrientedShell.qml+24−5 tests/qmltests/tst_Shell.qml+30−17 tests/qmltests/tst_ShellWithPin.qml+7−13 tests/uqmlscene/main.cpp+2
debian/control+1−1
@@ -38,7 +38,7 @@ Build-Depends: cmake,
3838 libubuntugestures5-private-dev (>= 1.3.2030),
3939 libudev-dev,
4040 libudm-common-dev,
41− libunity-api-dev (>= 8.6),
41+ libunity-api-dev (>= 8.7),
4242 libusermetricsoutput1-dev,
4343# Need those X11 libs touch emulation from mouse events in manual QML tests on a X11 desktop
4444 libx11-dev[!arm64 !armhf],
debian/unity8.install+5
@@ -16,7 +16,12 @@ usr/share/unity8/Rotation
1616usr/share/unity8/DeviceConfiguration.qml
1717usr/share/unity8/OrientedShell.qml
1818usr/share/unity8/DisabledScreenNotice.qml
19+usr/share/unity8/ErrorApplication.qml
20+usr/share/unity8/qmldir
1921usr/share/unity8/Shell.qml
22+usr/share/unity8/ShellApplication.qml
23+usr/share/unity8/ShellNotifier.qml
24+usr/share/unity8/ShellScreen.qml
2025usr/share/unity8/Stage
2126usr/share/unity8/Tutorial
2227usr/share/unity8/Wizard
plugins/Cursor/CMakeLists.txt+1
@@ -15,6 +15,7 @@ set(QMLPLUGIN_SRC
1515 CursorImageInfo.cpp
1616 CursorImageProvider.cpp
1717 MousePointer.cpp
18+ InputDispatcherFilter.cpp
1819 # We need to run moc on this header
1920 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirMousePointerInterface.h
2021 )
plugins/Cursor/InputDispatcherFilter.cppnew file+175
@@ -0,0 +1,175 @@
1+/*
2+ * Copyright (C) 2016 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 "InputDispatcherFilter.h"
18+#include "MousePointer.h"
19+
20+#include <QEvent>
21+#include <QGuiApplication>
22+#include <QQuickWindow>
23+#include <QScreen>
24+#include <QtMath>
25+#include <qpa/qplatformnativeinterface.h>
26+#include <qpa/qplatformscreen.h>
27+
28+InputDispatcherFilter *InputDispatcherFilter::instance()
29+{
30+ static InputDispatcherFilter filter;
31+ return &filter;
32+}
33+
34+InputDispatcherFilter::InputDispatcherFilter(QObject *parent)
35+ : QObject(parent)
36+ , m_pushing(false)
37+{
38+ QPlatformNativeInterface *ni = QGuiApplication::platformNativeInterface();
39+ m_inputDispatcher = static_cast<QObject*>(ni->nativeResourceForIntegration("InputDispatcher"));
40+ if (m_inputDispatcher) {
41+ m_inputDispatcher->installEventFilter(this);
42+ }
43+}
44+
45+void InputDispatcherFilter::registerPointer(MousePointer *pointer)
46+{
47+ // allow first registered pointer to be visible.
48+ m_pointers.insert(pointer);
49+}
50+
51+void InputDispatcherFilter::unregisterPointer(MousePointer *pointer)
52+{
53+ m_pointers.remove(pointer);
54+}
55+
56+bool InputDispatcherFilter::eventFilter(QObject *o, QEvent *e)
57+{
58+ if (o != m_inputDispatcher) return false;
59+
60+ switch (e->type()) {
61+ case QEvent::MouseMove:
62+ case QEvent::MouseButtonPress:
63+ case QEvent::MouseButtonRelease:
64+ {
65+ // if we don't have any pointers, filter all mouse events.
66+ auto pointer = currentPointer();
67+ if (!pointer) return true;
68+
69+ QMouseEvent* me = static_cast<QMouseEvent*>(e);
70+
71+ // Local position gives relative change of mouse pointer.
72+ QPointF movement = me->localPos();
73+
74+ // Adjust the position
75+ QPointF oldPos = pointer->window()->geometry().topLeft() + pointer->position();
76+ QPointF newPos = adjustedPositionForMovement(oldPos, movement);
77+
78+ QScreen* currentScreen = screenAt(newPos);
79+ if (currentScreen) {
80+ QRect screenRect = currentScreen->geometry();
81+ qreal newX = (oldPos + movement).x();
82+ qreal newY = (oldPos + movement).y();
83+
84+ if (newX <= screenRect.left() && newY < screenRect.top() + pointer->topBoundaryOffset()) { // top left corner
85+ const auto distance = qSqrt(qPow(newX, 2) + qPow(newY- screenRect.top() - pointer->topBoundaryOffset(), 2));
86+ Q_EMIT pushedTopLeftCorner(currentScreen, qAbs(distance), me->buttons());
87+ m_pushing = true;
88+ } else if (newX >= screenRect.right()-1 && newY < screenRect.top() + pointer->topBoundaryOffset()) { // top right corner
89+ const auto distance = qSqrt(qPow(newX-screenRect.right(), 2) + qPow(newY - screenRect.top() - pointer->topBoundaryOffset(), 2));
90+ Q_EMIT pushedTopRightCorner(currentScreen, qAbs(distance), me->buttons());
91+ m_pushing = true;
92+ } else if (newX < 0 && newY >= screenRect.bottom()-1) { // bottom left corner
93+ const auto distance = qSqrt(qPow(newX, 2) + qPow(newY-screenRect.bottom(), 2));
94+ Q_EMIT pushedBottomLeftCorner(currentScreen, qAbs(distance), me->buttons());
95+ m_pushing = true;
96+ } else if (newX >= screenRect.right()-1 && newY >= screenRect.bottom()-1) { // bottom right corner
97+ const auto distance = qSqrt(qPow(newX-screenRect.right(), 2) + qPow(newY-screenRect.bottom(), 2));
98+ Q_EMIT pushedBottomRightCorner(currentScreen, qAbs(distance), me->buttons());
99+ m_pushing = true;
100+ } else if (newX < screenRect.left()) { // left edge
101+ Q_EMIT pushedLeftBoundary(currentScreen, qAbs(newX), me->buttons());
102+ m_pushing = true;
103+ } else if (newX >= screenRect.right()) { // right edge
104+ Q_EMIT pushedRightBoundary(currentScreen, newX - (screenRect.right() - 1), me->buttons());
105+ m_pushing = true;
106+ } else if (newY < screenRect.top() + pointer->topBoundaryOffset()) { // top edge
107+ Q_EMIT pushedTopBoundary(currentScreen, qAbs(newY - screenRect.top() - pointer->topBoundaryOffset()), me->buttons());
108+ m_pushing = true;
109+ } else if (Q_LIKELY(newX > 0 && newX < screenRect.right()-1 && newY > 0 && newY < screenRect.bottom()-1)) { // normal pos, not pushing
110+ if (m_pushing) {
111+ Q_EMIT pushStopped(currentScreen);
112+ m_pushing = false;
113+ }
114+ }
115+ }
116+
117+ // Send the event
118+ QMouseEvent eCopy(me->type(), me->localPos(), newPos, me->button(), me->buttons(), me->modifiers());
119+ eCopy.setTimestamp(me->timestamp());
120+ o->event(&eCopy);
121+ return true;
122+ }
123+ default:
124+ break;
125+ }
126+ return false;
127+}
128+
129+QPointF InputDispatcherFilter::adjustedPositionForMovement(const QPointF &pt, const QPointF &movement) const
130+{
131+ QPointF adjusted = pt + movement;
132+
133+ auto screen = screenAt(adjusted); // first check if our move was to a screen with an enabled pointer.
134+ if (screen) {
135+ return adjusted;
136+ } else if ((screen = screenAt(pt))) { // then check if our old position was valid
137+ QRectF screenBounds = screen->geometry();
138+ // bound the new position to the old screen geometry
139+ adjusted.rx() = qMax(screenBounds.left(), qMin(adjusted.x(), screenBounds.right()-1));
140+ adjusted.ry() = qMax(screenBounds.top(), qMin(adjusted.y(), screenBounds.bottom()-1));
141+ } else {
142+ auto screens = QGuiApplication::screens();
143+
144+ // center of first screen with a pointer.
145+ Q_FOREACH(QScreen* screen, screens) {
146+ Q_FOREACH(MousePointer* pointer, m_pointers) {
147+ if (pointer->isEnabled() && pointer->screen() == screen) {
148+ return screen->geometry().center();
149+ }
150+ }
151+ }
152+ }
153+ return adjusted;
154+}
155+
156+QScreen *InputDispatcherFilter::screenAt(const QPointF &pt) const
157+{
158+ Q_FOREACH(MousePointer* pointer, m_pointers) {
159+ if (!pointer->isEnabled()) continue;
160+
161+ QScreen* screen = pointer->screen();
162+ if (screen && screen->geometry().contains(pt.toPoint()))
163+ return screen;
164+ }
165+ return nullptr;
166+}
167+
168+MousePointer *InputDispatcherFilter::currentPointer() const
169+{
170+ Q_FOREACH(MousePointer* pointer, m_pointers) {
171+ if (!pointer->isEnabled()) continue;
172+ return pointer;
173+ }
174+ return nullptr;
175+}
plugins/Cursor/InputDispatcherFilter.hnew file+62
@@ -0,0 +1,62 @@
1+/*
2+ * Copyright (C) 2016 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 INPUTDISPATCHERFILTER_H
18+#define INPUTDISPATCHERFILTER_H
19+
20+#include <QObject>
21+#include <QPointF>
22+#include <QSet>
23+
24+class MousePointer;
25+class QScreen;
26+
27+class InputDispatcherFilter : public QObject
28+{
29+ Q_OBJECT
30+public:
31+ static InputDispatcherFilter *instance();
32+
33+ void registerPointer(MousePointer* pointer);
34+ void unregisterPointer(MousePointer* pointer);
35+
36+Q_SIGNALS:
37+ void pushedLeftBoundary(QScreen* screen, qreal amount, Qt::MouseButtons buttons);
38+ void pushedRightBoundary(QScreen* screen, qreal amount, Qt::MouseButtons buttons);
39+ void pushedTopBoundary(QScreen* screen, qreal amount, Qt::MouseButtons buttons);
40+ void pushedTopLeftCorner(QScreen* screen, qreal amount, Qt::MouseButtons buttons);
41+ void pushedTopRightCorner(QScreen* screen, qreal amount, Qt::MouseButtons buttons);
42+ void pushedBottomLeftCorner(QScreen* screen, qreal amount, Qt::MouseButtons buttons);
43+ void pushedBottomRightCorner(QScreen* screen, qreal amount, Qt::MouseButtons buttons);
44+ void pushStopped(QScreen* screen);
45+
46+protected:
47+ InputDispatcherFilter(QObject* parent = nullptr);
48+
49+ bool eventFilter(QObject *o, QEvent *e) override;
50+
51+ QPointF adjustedPositionForMovement(const QPointF& pt, const QPointF& movement) const;
52+ QScreen* screenAt(const QPointF& pt) const;
53+
54+ MousePointer* currentPointer() const;
55+
56+private:
57+ QObject* m_inputDispatcher;
58+ QSet<MousePointer*> m_pointers;
59+ bool m_pushing;
60+};
61+
62+#endif // INPUTDISPATCHERFILTER_H
plugins/Cursor/MousePointer.cpp+63−78
@@ -16,84 +16,74 @@
1616
1717#include "MousePointer.h"
1818#include "CursorImageProvider.h"
19−
20−// Unity API
21−#include <unity/shell/application/MirPlatformCursor.h>
19+#include "InputDispatcherFilter.h"
2220
2321#include <QQuickWindow>
24−#include <QGuiApplication>
25−#include <QtMath>
2622
27−#include <qpa/qwindowsysteminterface.h>
23+// Unity API
24+#include <unity/shell/application/MirPlatformCursor.h>
2825
2926MousePointer::MousePointer(QQuickItem *parent)
3027 : MirMousePointerInterface(parent)
3128 , m_cursorName(QStringLiteral("left_ptr"))
3229 , m_themeName(QStringLiteral("default"))
3330{
34−}
35−
36−void MousePointer::handleMouseEvent(ulong timestamp, QPointF movement, Qt::MouseButtons buttons,
37− Qt::KeyboardModifiers modifiers)
38−{
39− if (!parentItem()) {
40− return;
41− }
42−
43− if (!movement.isNull()) {
44− Q_EMIT mouseMoved();
45− }
46−
47− m_accumulatedMovement += movement;
48− // don't apply the fractional part
49− QPointF appliedMovement(int(m_accumulatedMovement.x()), int(m_accumulatedMovement.y()));
50− m_accumulatedMovement -= appliedMovement;
51−
52− qreal newX = x() + appliedMovement.x();
53− qreal newY = y() + appliedMovement.y();
54− const qreal sceneWidth = parentItem()->width();
55− const qreal sceneHeight = parentItem()->height();
56−
57− if (newX <= 0 && newY < m_topBoundaryOffset) { // top left corner
58− const auto distance = qSqrt(qPow(newX, 2) + qPow(newY-m_topBoundaryOffset, 2));
59− Q_EMIT pushedTopLeftCorner(qAbs(distance), buttons);
60− m_pushing = true;
61− } else if (newX >= sceneWidth-1 && newY < m_topBoundaryOffset) { // top right corner
62− const auto distance = qSqrt(qPow(newX-sceneWidth, 2) + qPow(newY-m_topBoundaryOffset, 2));
63− Q_EMIT pushedTopRightCorner(qAbs(distance), buttons);
64− m_pushing = true;
65− } else if (newX < 0 && newY >= sceneHeight-1) { // bottom left corner
66− const auto distance = qSqrt(qPow(newX, 2) + qPow(newY-sceneHeight, 2));
67− Q_EMIT pushedBottomLeftCorner(qAbs(distance), buttons);
68− m_pushing = true;
69− } else if (newX >= sceneWidth-1 && newY >= sceneHeight-1) { // bottom right corner
70− const auto distance = qSqrt(qPow(newX-sceneWidth, 2) + qPow(newY-sceneHeight, 2));
71− Q_EMIT pushedBottomRightCorner(qAbs(distance), buttons);
72− m_pushing = true;
73− } else if (newX < 0) { // left edge
74− Q_EMIT pushedLeftBoundary(qAbs(newX), buttons);
75− m_pushing = true;
76− } else if (newX >= sceneWidth) { // right edge
77− Q_EMIT pushedRightBoundary(newX - (sceneWidth - 1), buttons);
78− m_pushing = true;
79− } else if (newY < m_topBoundaryOffset) { // top edge
80− Q_EMIT pushedTopBoundary(qAbs(newY - m_topBoundaryOffset), buttons);
81− m_pushing = true;
82− } else if (Q_LIKELY(newX > 0 && newX < sceneWidth-1 && newY > 0 && newY < sceneHeight-1)) { // normal pos, not pushing
83− if (m_pushing) {
31+ connect(InputDispatcherFilter::instance(), &InputDispatcherFilter::pushedLeftBoundary,
32+ this, [this](QScreen* screen, qreal amount, Qt::MouseButtons buttons) {
33+ if (window() && window()->screen() == screen) {
34+ Q_EMIT pushedLeftBoundary(amount, buttons);
35+ }
36+ });
37+ connect(InputDispatcherFilter::instance(), &InputDispatcherFilter::pushedRightBoundary,
38+ this, [this](QScreen* screen, qreal amount, Qt::MouseButtons buttons) {
39+ if (window() && window()->screen() == screen) {
40+ Q_EMIT pushedRightBoundary(amount, buttons);
41+ }
42+ });
43+ connect(InputDispatcherFilter::instance(), &InputDispatcherFilter::pushedTopBoundary,
44+ this, [this](QScreen* screen, qreal amount, Qt::MouseButtons buttons) {
45+ if (window() && window()->screen() == screen) {
46+ Q_EMIT pushedTopBoundary(amount, buttons);
47+ }
48+ });
49+ connect(InputDispatcherFilter::instance(), &InputDispatcherFilter::pushedTopLeftCorner,
50+ this, [this](QScreen* screen, qreal amount, Qt::MouseButtons buttons) {
51+ if (window() && window()->screen() == screen) {
52+ Q_EMIT pushedTopLeftCorner(amount, buttons);
53+ }
54+ });
55+ connect(InputDispatcherFilter::instance(), &InputDispatcherFilter::pushedTopRightCorner,
56+ this, [this](QScreen* screen, qreal amount, Qt::MouseButtons buttons) {
57+ if (window() && window()->screen() == screen) {
58+ Q_EMIT pushedTopRightCorner(amount, buttons);
59+ }
60+ });
61+ connect(InputDispatcherFilter::instance(), &InputDispatcherFilter::pushedBottomLeftCorner,
62+ this, [this](QScreen* screen, qreal amount, Qt::MouseButtons buttons) {
63+ if (window() && window()->screen() == screen) {
64+ Q_EMIT pushedBottomLeftCorner(amount, buttons);
65+ }
66+ });
67+ connect(InputDispatcherFilter::instance(), &InputDispatcherFilter::pushedBottomRightCorner,
68+ this, [this](QScreen* screen, qreal amount, Qt::MouseButtons buttons) {
69+ if (window() && window()->screen() == screen) {
70+ Q_EMIT pushedBottomRightCorner(amount, buttons);
71+ }
72+ });
73+ connect(InputDispatcherFilter::instance(), &InputDispatcherFilter::pushStopped,
74+ this, [this](QScreen* screen) {
75+ if (window() && window()->screen() == screen) {
8476 Q_EMIT pushStopped();
85− m_pushing = false;
8677 }
87− }
78+ });
8879
89− applyItemConfinement(newX, newY);
90−
91− setX(qBound(0.0, newX, sceneWidth - 1));
92− setY(qBound(0.0, newY, sceneHeight - 1));
80+ InputDispatcherFilter::instance()->registerPointer(this);
81+}
9382
94− QPointF scenePosition = mapToItem(nullptr, QPointF(0, 0));
95− QWindowSystemInterface::handleMouseEvent(window(), timestamp, scenePosition /*local*/, scenePosition /*global*/,
96− buttons, modifiers);
83+MousePointer::~MousePointer()
84+{
85+ registerScreen(nullptr);
86+ InputDispatcherFilter::instance()->unregisterPointer(this);
9787}
9888
9989void MousePointer::applyItemConfinement(qreal &newX, qreal &newY)
@@ -121,17 +111,6 @@ void MousePointer::applyItemConfinement(qreal &newX, qreal &newY)
121111 }
122112}
123113
124−void MousePointer::handleWheelEvent(ulong timestamp, QPoint angleDelta, Qt::KeyboardModifiers modifiers)
125−{
126− if (!parentItem()) {
127− return;
128− }
129−
130− QPointF scenePosition = mapToItem(nullptr, QPointF(0, 0));
131− QWindowSystemInterface::handleWheelEvent(window(), timestamp, scenePosition /* local */, scenePosition /* global */,
132− QPoint() /* pixelDelta */, angleDelta, modifiers, Qt::ScrollUpdate);
133−}
134−
135114int MousePointer::topBoundaryOffset() const
136115{
137116 return m_topBoundaryOffset;
@@ -182,7 +161,7 @@ void MousePointer::registerScreen(QScreen *screen)
182161 if (m_registeredScreen) {
183162 auto previousCursor = dynamic_cast<MirPlatformCursor*>(m_registeredScreen->handle()->cursor());
184163 if (previousCursor) {
185− previousCursor->setMousePointer(nullptr);
164+ previousCursor->unregisterMousePointer(this);
186165 } else {
187166 qCritical("QPlatformCursor is not a MirPlatformCursor! Cursor module only works in a Mir server.");
188167 }
@@ -193,7 +172,7 @@ void MousePointer::registerScreen(QScreen *screen)
193172 if (m_registeredScreen) {
194173 auto cursor = dynamic_cast<MirPlatformCursor*>(m_registeredScreen->handle()->cursor());
195174 if (cursor) {
196− cursor->setMousePointer(this);
175+ cursor->registerMousePointer(this);
197176 } else {
198177 qCritical("QPlaformCursor is not a MirPlatformCursor! Cursor module only works in Mir.");
199178 }
@@ -216,6 +195,12 @@ void MousePointer::setThemeName(const QString &themeName)
216195 }
217196}
218197
198+void MousePointer::moveTo(const QPoint &position)
199+{
200+ setPosition(position);
201+ Q_EMIT mouseMoved();
202+}
203+
219204void MousePointer::setCustomCursor(const QCursor &customCursor)
220205{
221206 CursorImageProvider::instance()->setCustomCursor(customCursor);
plugins/Cursor/MousePointer.h+5−4
@@ -31,6 +31,7 @@ class MousePointer : public MirMousePointerInterface {
3131 Q_PROPERTY(int topBoundaryOffset READ topBoundaryOffset WRITE setTopBoundaryOffset NOTIFY topBoundaryOffsetChanged)
3232public:
3333 MousePointer(QQuickItem *parent = nullptr);
34+ ~MousePointer();
3435
3536 void setCursorName(const QString &qtCursorName) override;
3637 QString cursorName() const override { return m_cursorName; }
@@ -38,6 +39,8 @@ public:
3839 void setThemeName(const QString &themeName) override;
3940 QString themeName() const override { return m_themeName; }
4041
42+ void moveTo(const QPoint& position) override;
43+
4144 void setCustomCursor(const QCursor &) override;
4245
4346 QQuickItem* confiningItem() const;
@@ -46,10 +49,7 @@ public:
4649 int topBoundaryOffset() const;
4750 void setTopBoundaryOffset(int topBoundaryOffset);
4851
49−public Q_SLOTS:
50− void handleMouseEvent(ulong timestamp, QPointF movement, Qt::MouseButtons buttons,
51− Qt::KeyboardModifiers modifiers) override;
52− void handleWheelEvent(ulong timestamp, QPoint angleDelta, Qt::KeyboardModifiers modifiers) override;
52+ QScreen* screen() const { return m_registeredScreen; }
5353
5454Q_SIGNALS:
5555 void pushedLeftBoundary(qreal amount, Qt::MouseButtons buttons);
@@ -79,6 +79,7 @@ private:
7979 QPointer<QScreen> m_registeredScreen;
8080 QString m_cursorName;
8181 QString m_themeName;
82+ bool m_active;
8283
8384 // Accumulated, unapplied, mouse movement.
8485 QPointF m_accumulatedMovement;
plugins/GlobalShortcut/globalshortcut.cpp−15
@@ -56,11 +56,6 @@ void GlobalShortcut::setActive(bool active)
5656 Q_EMIT activeChanged(active);
5757}
5858
59−void GlobalShortcut::componentComplete()
60−{
61− connect(this, &QQuickItem::windowChanged, this, &GlobalShortcut::setupFilterOnWindow);
62−}
63−
6459void GlobalShortcut::keyPressEvent(QKeyEvent * event)
6560{
6661 if (!m_active) return;
@@ -76,13 +71,3 @@ void GlobalShortcut::keyReleaseEvent(QKeyEvent * event)
7671 event->accept();
7772 Q_EMIT released(m_shortcut.toString());
7873}
79−
80−void GlobalShortcut::setupFilterOnWindow(QQuickWindow *window)
81−{
82− if (!window) {
83−// qWarning() << Q_FUNC_INFO << "Failed to setup filter on window";
84− return;
85− }
86−
87− registry->setupFilterOnWindow((qulonglong) window->winId());
88−}
plugins/GlobalShortcut/globalshortcut.h−4
@@ -53,7 +53,6 @@ public:
5353 void setActive(bool active);
5454
5555protected:
56− void componentComplete() override;
5756 void keyPressEvent(QKeyEvent * event) override;
5857 void keyReleaseEvent(QKeyEvent * event) override;
5958
@@ -69,9 +68,6 @@ Q_SIGNALS:
6968 void released(const QString &shortcut);
7069 void activeChanged(bool active);
7170
72−private Q_SLOTS:
73− void setupFilterOnWindow(QQuickWindow* window);
74−
7571private:
7672 QVariant m_shortcut;
7773 bool m_active = true;
plugins/GlobalShortcut/globalshortcutregistry.cpp+23−15
@@ -21,11 +21,25 @@
2121
2222#include "globalshortcutregistry.h"
2323
24−static qulonglong s_windowId = 0;
24+namespace {
25+QWindow* windowForShortcut(GlobalShortcut *sc) {
26+ QObject* parent= sc;
27+ while(parent) {
28+ if (auto item = qobject_cast<QQuickItem*>(parent)) {
29+ auto window = item->window();
30+ if (window) return window;
31+ }
32+ parent = parent->parent();
33+ }
34+ return nullptr;
35+}
36+} // namespace
2537
2638GlobalShortcutRegistry::GlobalShortcutRegistry(QObject *parent)
2739 : QObject(parent)
2840{
41+ connect(qGuiApp, &QGuiApplication::focusWindowChanged, this, &GlobalShortcutRegistry::setupFilterOnWindow);
42+ setupFilterOnWindow(qGuiApp->focusWindow());
2943}
3044
3145GlobalShortcutList GlobalShortcutRegistry::shortcuts() const
@@ -91,7 +105,10 @@ bool GlobalShortcutRegistry::eventFilter(QObject *obj, QEvent *event)
91105 const auto shortcuts = m_shortcuts.value(seq);
92106 Q_FOREACH(const auto &shortcut, shortcuts) {
93107 if (shortcut) {
94− qApp->sendEvent(shortcut, &eCopy);
108+ auto window = windowForShortcut(shortcut);
109+ if (!window || window == obj) { // accept shortcut if it's not attached to a window or it's window is active.
110+ qApp->sendEvent(shortcut, &eCopy);
111+ }
95112 }
96113 }
97114 }
@@ -102,24 +119,15 @@ bool GlobalShortcutRegistry::eventFilter(QObject *obj, QEvent *event)
102119 return QObject::eventFilter(obj, event);
103120}
104121
105−void GlobalShortcutRegistry::setupFilterOnWindow(qulonglong wid)
122+void GlobalShortcutRegistry::setupFilterOnWindow(QWindow* window)
106123{
107− if (wid == s_windowId) {
108− return;
109− }
110−
111124 if (m_filteredWindow) {
112125 m_filteredWindow->removeEventFilter(this);
113126 m_filteredWindow.clear();
114− s_windowId = 0;
115127 }
116128
117− Q_FOREACH(QWindow *window, qApp->allWindows()) {
118− if (window && window->winId() == wid) {
119− m_filteredWindow = window;
120− window->installEventFilter(this);
121− s_windowId = wid;
122− break;
123− }
129+ if (window) {
130+ m_filteredWindow = window;
131+ window->installEventFilter(this);
124132 }
125133}
plugins/GlobalShortcut/globalshortcutregistry.h+2−2
@@ -52,9 +52,9 @@ public:
5252 void addShortcut(const QVariant &seq, GlobalShortcut * sc);
5353
5454 /**
55− * Sets up key events filtering on window @p wid
55+ * Sets up key events filtering on window @p window
5656 */
57− void setupFilterOnWindow(qulonglong wid);
57+ void setupFilterOnWindow(QWindow* window);
5858
5959protected:
6060 bool eventFilter(QObject *obj, QEvent *event) override;
plugins/UInput/plugin.cpp+7−1
@@ -19,8 +19,14 @@
1919
2020#include <QtQml/qqml.h>
2121
22+
23+QObject* uinputSingleton(QQmlEngine*, QJSEngine*)
24+{
25+ return new UInput;
26+}
27+
2228void UInputPlugin::registerTypes(const char *uri)
2329{
2430 Q_ASSERT(uri == QLatin1String("UInput"));
25− qmlRegisterType<UInput>(uri, 0, 1, "UInput");
31+ qmlRegisterSingletonType<UInput>(uri, 0, 1, "UInput", uinputSingleton);
2632}
qml/ApplicationMenus/ApplicationMenuItemFactory.qml+4−4
@@ -45,7 +45,7 @@ Object {
4545
4646 Action {
4747 id: action
48− text: menuData.label.replace("_", "&")
48+ text: menuData && menuData.label.replace("_", "&") || ""
4949 }
5050
5151 ListItemLayout {
@@ -59,8 +59,8 @@ Object {
5959 }
6060
6161 Label {
62− text: menuData.shortcut
63− visible: menuData.shortcut && QuickUtils.keyboardAttached
62+ text: menuData && menuData.shortcut || ""
63+ visible: menuData && menuData.shortcut && QuickUtils.keyboardAttached
6464 SlotsLayout.position: SlotsLayout.Trailing
6565 color: enabled ? theme.palette.normal.backgroundSecondaryText :
6666 theme.palette.disabled.backgroundSecondaryText
@@ -86,7 +86,7 @@ Object {
8686
8787 Action {
8888 id: action
89− text: menuData.label.replace("_", "&")
89+ text: menuData && menuData.label.replace("_", "&") || ""
9090 }
9191
9292 ListItemLayout {
qml/ApplicationMenus/MenuBar.qml+5−1
@@ -19,6 +19,7 @@ import QtQuick.Layouts 1.1
1919import Utils 0.1
2020import Ubuntu.Components 1.3
2121import GlobalShortcut 1.0
22+import "../Components/PanelState"
2223
2324Item {
2425 id: root
@@ -29,6 +30,7 @@ Item {
2930 property bool enableKeyFilter: false
3031 property real overflowWidth: width
3132 property bool windowMoving: false
33+ property PanelState panelState
3234
3335 // read from outside
3436 readonly property bool valid: rowRepeater.count > 0
@@ -106,7 +108,9 @@ Item {
106108
107109 Component {
108110 id: menuComponent
109− MenuPopup { }
111+ MenuPopup {
112+ panelState: root.panelState
113+ }
110114 }
111115
112116 Repeater {
qml/ApplicationMenus/MenuItem.qml+2
@@ -17,6 +17,7 @@
1717import QtQuick 2.4
1818import QtQuick.Layouts 1.1
1919import Ubuntu.Components 1.3
20+import "../Components/PanelState"
2021
2122ActionItem {
2223 id: root
@@ -24,6 +25,7 @@ ActionItem {
2425 implicitWidth: requiredWidth
2526
2627 property var menuData: undefined
28+ property PanelState panelState
2729
2830 readonly property real requiredWidth: {
2931 var val = 0;
qml/ApplicationMenus/MenuPopup.qml+4−1
@@ -64,6 +64,7 @@ UbuntuShape {
6464 }
6565
6666 property alias unityMenuModel: repeater.model
67+ property PanelState panelState
6768
6869 function show() {
6970 visible = true;
@@ -103,7 +104,7 @@ UbuntuShape {
103104 property real __minimumWidth: units.gu(20)
104105 property real __maximumWidth: ApplicationMenusLimits.screenWidth * 0.7
105106 property real __minimumHeight: units.gu(2)
106− property real __maximumHeight: ApplicationMenusLimits.screenHeight - PanelState.panelHeight
107+ property real __maximumHeight: ApplicationMenusLimits.screenHeight - panelState.panelHeight
107108
108109 signal dismissAll()
109110
@@ -293,6 +294,7 @@ UbuntuShape {
293294 // Parent will be loader
294295 id: menuItem
295296 menuData: parent.__menuData
297+ panelState: root.panelState
296298 objectName: parent.objectName + "-actionItem"
297299
298300 width: MathUtils.clamp(implicitWidth, d.__minimumWidth, d.__maximumWidth)
@@ -483,6 +485,7 @@ UbuntuShape {
483485
484486 onLoaded: {
485487 item.unityMenuModel = Qt.binding(function() { return submenuLoader.unityMenuModel; });
488+ item.panelState = Qt.binding(function() { return root.panelState; });
486489 item.objectName = Qt.binding(function() { return submenuLoader.objectName + "menu"; });
487490 item.desiredX = Qt.binding(function() { return submenuLoader.desiredX; });
488491 item.desiredY = Qt.binding(function() { return submenuLoader.desiredY; });
qml/CMakeLists.txt+1−1
@@ -1,4 +1,4 @@
1−file(GLOB QML_JS_FILES *.qml *.js)
1+file(GLOB QML_JS_FILES *.qml *.js qmldir)
22
33install(FILES ${QML_JS_FILES}
44 DESTINATION ${SHELL_APP_DIR}
qml/Components/PanelState/PanelState.qml−1
@@ -14,7 +14,6 @@
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1515 */
1616
17−pragma Singleton
1817import QtQuick 2.4
1918
2019QtObject {
qml/Components/PanelState/qmldir+1−1
@@ -1 +1 @@
1−singleton PanelState 1.0 PanelState.qml
1+PanelState 1.0 PanelState.qml
qml/Components/VirtualTouchPad.qml+13−19
@@ -24,16 +24,14 @@ import "../Components"
2424
2525Item {
2626 id: root
27− property var uinput: UInput {
28− Component.onCompleted: createMouse();
29− Component.onDestruction: removeMouse();
30− }
3127
3228 Component.onCompleted: {
29+ UInput.createMouse();
3330 if (!settings.touchpadTutorialHasRun) {
3431 root.runTutorial()
3532 }
3633 }
34+ Component.onDestruction: UInput.removeMouse()
3735
3836 function runTutorial() {
3937 // If the tutorial animation is started too early, e.g. in Component.onCompleted,
@@ -85,7 +83,7 @@ Item {
8583 if (((point1.pressed && !point2.pressed) || (!point1.pressed && point2.pressed))
8684 && clickTimer.running) {
8785 clickTimer.stop();
88− uinput.pressMouse(UInput.ButtonLeft)
86+ UInput.pressMouse(UInput.ButtonLeft)
8987 isDoubleClick = true;
9088 }
9189 isClick = true;
@@ -104,7 +102,7 @@ Item {
104102
105103 onReleased: {
106104 if (isDoubleClick || isDrag) {
107− uinput.releaseMouse(UInput.ButtonLeft)
105+ UInput.releaseMouse(UInput.ButtonLeft)
108106 isDoubleClick = false;
109107 }
110108 if (isClick) {
@@ -120,8 +118,8 @@ Item {
120118 interval: 200
121119 property int button: UInput.ButtonLeft
122120 onTriggered: {
123− uinput.pressMouse(button);
124− uinput.releaseMouse(button);
121+ UInput.pressMouse(button);
122+ UInput.releaseMouse(button);
125123 }
126124 function scheduleClick(button) {
127125 clickTimer.button = button;
@@ -138,7 +136,7 @@ Item {
138136 isDrag = true;
139137 }
140138
141− uinput.moveMouse(tp.x - tp.previousX, tp.y - tp.previousY);
139+ UInput.moveMouse(tp.x - tp.previousX, tp.y - tp.previousY);
142140 }
143141
144142 function scroll(touchPoints) {
@@ -166,7 +164,7 @@ Item {
166164 dh /= 2;
167165 dv /= 2;
168166
169− uinput.scrollMouse(dh, dv);
167+ UInput.scrollMouse(dh, dv);
170168 }
171169
172170 touchPoints: [
@@ -189,8 +187,8 @@ Item {
189187 objectName: "leftButton"
190188 Layout.fillWidth: true
191189 Layout.fillHeight: true
192− onPressed: uinput.pressMouse(UInput.ButtonLeft);
193− onReleased: uinput.releaseMouse(UInput.ButtonLeft);
190+ onPressed: UInput.pressMouse(UInput.ButtonLeft);
191+ onReleased: UInput.releaseMouse(UInput.ButtonLeft);
194192 property bool highlight: false
195193 UbuntuShape {
196194 anchors.fill: parent
@@ -204,8 +202,8 @@ Item {
204202 objectName: "rightButton"
205203 Layout.fillWidth: true
206204 Layout.fillHeight: true
207− onPressed: uinput.pressMouse(UInput.ButtonRight);
208− onReleased: uinput.releaseMouse(UInput.ButtonRight);
205+ onPressed: UInput.pressMouse(UInput.ButtonRight);
206+ onReleased: UInput.releaseMouse(UInput.ButtonRight);
209207 property bool highlight: false
210208 UbuntuShape {
211209 anchors.fill: parent
@@ -239,14 +237,10 @@ Item {
239237 }
240238 }
241239
242− Screens {
243− id: screens
244− }
245−
246240 InputMethod {
247241 id: inputMethod
248242 // Don't resize when there is only one screen to avoid resize clashing with the InputMethod in the Shell.
249− enabled: screens.count > 1 && settings.oskEnabled && !tutorial.running
243+ enabled: Screens.count > 1 && settings.oskEnabled && !tutorial.running
250244 objectName: "inputMethod"
251245 anchors.fill: parent
252246 }
qml/DisabledScreenNotice.qml+2−1
@@ -28,8 +28,9 @@ Item {
2828 property var screen: Screen
2929 property var orientationLock: OrientationLock
3030
31+ property alias deviceConfiguration: _deviceConfiguration
3132 DeviceConfiguration {
32− id: deviceConfiguration
33+ id: _deviceConfiguration
3334 name: applicationArguments.deviceName
3435 }
3536
qml/ErrorApplication.qmlnew file+67
@@ -0,0 +1,67 @@
1+/*
2+* Copyright (C) 2017 Canonical, Ltd.
3+*
4+* This program is free software; you can redistribute it and/or modify
5+* it under the terms of the GNU General Public License as published by
6+* the Free Software Foundation; version 3.
7+*
8+* This program is distributed in the hope that it will be useful,
9+* but WITHOUT ANY WARRANTY; without even the implied warranty of
10+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+* GNU General Public License for more details.
12+*
13+* You should have received a copy of the GNU General Public License
14+* along with this program. If not, see <http://www.gnu.org/licenses/>.
15+*/
16+
17+import QtQuick 2.4
18+import QtQuick.Window 2.2
19+import Ubuntu.Components 1.3
20+import Unity.Screens 0.1
21+
22+Instantiator {
23+ id: root
24+ model: Screens
25+
26+ ScreenWindow {
27+ id: window
28+ objectName: "screen"+index
29+ screen: model.screen
30+ visibility: applicationArguments.hasFullscreen ? Window.FullScreen : Window.Windowed
31+ flags: applicationArguments.hasFrameless ? Qt.FramelessWindowHint : 0
32+
33+ Binding {
34+ when: applicationArguments.hasGeometry
35+ target: window
36+ property: "width"
37+ value: applicationArguments.windowGeometry.width
38+ }
39+ Binding {
40+ when: applicationArguments.hasGeometry
41+ target: window
42+ property: "height"
43+ value: applicationArguments.windowGeometry.height
44+ }
45+
46+ Loader {
47+ width: window.width
48+ height: window.height
49+
50+ Rectangle {
51+ color: "white"
52+ Column {
53+ spacing: units.gu(1)
54+
55+ Label {
56+ text: "Unity encountered an unrecoverable error while loading:"
57+ fontSize: "large"
58+ }
59+
60+ Label {
61+ text: errorString
62+ }
63+ }
64+ }
65+ }
66+ }
67+}
qml/Greeter/Greeter.qml+21−3
@@ -25,6 +25,7 @@ import Unity.Launcher 0.1
2525import Unity.Session 0.1
2626
2727import "." 0.1
28+import ".." 0.1
2829import "../Components"
2930
3031Showable {
@@ -227,7 +228,7 @@ Showable {
227228 if (forcedUnlock && shown) {
228229 hideView();
229230 if (hideNow) {
230− root.hideNow(); // skip hide animation
231+ ShellNotifier.greeter.hide(true); // skip hide animation
231232 }
232233 }
233234 }
@@ -375,7 +376,7 @@ Showable {
375376 onEmergencyCall: root.emergencyCall()
376377 onRequiredChanged: {
377378 if (!loader.item.required) {
378− root.hide();
379+ ShellNotifier.greeter.hide(false);
379380 }
380381 }
381382 }
@@ -498,12 +499,29 @@ Showable {
498499 onRequestAuthenticationUser: d.selectUser(d.getUserIndex(user))
499500 }
500501
502+ Connections {
503+ target: ShellNotifier.greeter
504+ onHide: {
505+ if (now) {
506+ root.hideNow(); // skip hide animation
507+ } else {
508+ root.hide();
509+ }
510+ }
511+ }
512+
513+ Binding {
514+ target: ShellNotifier.greeter
515+ property: "shown"
516+ value: root.shown
517+ }
518+
501519 Connections {
502520 target: DBusUnitySessionService
503521 onLockRequested: root.forceShow()
504522 onUnlocked: {
505523 root.forcedUnlock = true;
506− root.hideNow();
524+ ShellNotifier.greeter.hide(true);
507525 }
508526 }
509527
qml/Greeter/SecondaryGreeter.qmlnew file+75
@@ -0,0 +1,75 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.4
18+import Ubuntu.Components 1.3
19+
20+import "../Components"
21+import ".." 0.1
22+
23+Showable {
24+ id: root
25+
26+ readonly property bool active: required || hasLockedApp
27+
28+ readonly property bool hasLockedApp: lockedApp !== ""
29+ readonly property bool locked: false
30+ readonly property bool waiting: false
31+ readonly property bool fullyShown: shown
32+
33+ property string lockedApp: ""
34+
35+ function forceShow() { show(); }
36+ property var notifyAppFocusRequested: (function(appId) { return; })
37+ property var notifyUserRequestedApp: (function(appId) { return; })
38+ property var notifyShowingDashFromDrag: (function(appId) { return false; })
39+
40+ showAnimation: StandardAnimation { property: "opacity"; to: 1 }
41+ hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
42+
43+ shown: ShellNotifier.greeter.shown
44+ Component.onCompleted: opacity = shown ? 1 : 0
45+ visible: opacity != 0
46+
47+ Rectangle {
48+ anchors.fill: parent
49+ color: UbuntuColors.purple
50+ }
51+
52+ MouseArea {
53+ anchors.fill: parent
54+ acceptedButtons: Qt.AllButtons
55+ onWheel: wheel.accepted = true
56+ }
57+
58+ Connections {
59+ target: ShellNotifier.greeter
60+ onHide: {
61+ if (now) {
62+ root.hideNow(); // skip hide animation
63+ } else {
64+ root.hide();
65+ }
66+ }
67+ onShownChanged: {
68+ if (ShellNotifier.greeter.shown) {
69+ root.show();
70+ } else {
71+ root.hide();
72+ }
73+ }
74+ }
75+}
qml/OrientedShell.qml+6−8
@@ -32,15 +32,17 @@ Item {
3232 implicitWidth: units.gu(40)
3333 implicitHeight: units.gu(71)
3434
35+ property alias deviceConfiguration: _deviceConfiguration
36+ property alias orientations: d.orientations
37+ property alias surfaceManager: shell.surfaceManager
38+
3539 onWidthChanged: calculateUsageMode();
3640
3741 DeviceConfiguration {
38− id: deviceConfiguration
42+ id: _deviceConfiguration
3943 name: applicationArguments.deviceName
4044 }
4145
42− property alias orientations: d.orientations
43−
4446 Item {
4547 id: d
4648
@@ -157,10 +159,6 @@ Item {
157159 return false;
158160 }
159161
160− Screens {
161− id: screens
162− }
163−
164162 property int orientation
165163 onPhysicalOrientationChanged: {
166164 if (!orientationLocked) {
@@ -264,7 +262,7 @@ Item {
264262
265263 Shell {
266264 id: shell
267− objectName: "shell"
265+ objectName: "shell-"+screenWindow.objectName
268266 width: root.width
269267 height: root.height
270268 orientation: root.angleToOrientation(orientationAngle)
qml/Panel/Panel.qml+17−15
@@ -48,6 +48,7 @@ Item {
4848 property bool hasKeyboard: false
4949
5050 property string mode: "staged"
51+ property PanelState panelState
5152
5253 MouseArea {
5354 id: backMouseEater
@@ -64,14 +65,14 @@ Item {
6465 }
6566
6667 Binding {
67− target: PanelState
68+ target: panelState
6869 property: "panelHeight"
6970 value: minimizedPanelHeight
7071 }
7172
7273 RegisteredApplicationMenuModel {
7374 id: registeredMenuModel
74− persistentSurfaceId: PanelState.focusedPersistentSurfaceId
75+ persistentSurfaceId: panelState.focusedPersistentSurfaceId
7576 }
7677
7778 QtObject {
@@ -82,11 +83,11 @@ Item {
8283 !indicators.shown &&
8384 (decorationMouseArea.containsMouse || menuBarLoader.menusRequested)
8485
85− property bool showWindowDecorationControls: (revealControls && PanelState.decorationsVisible) ||
86− PanelState.decorationsAlwaysVisible
86+ property bool showWindowDecorationControls: (revealControls && panelState.decorationsVisible) ||
87+ panelState.decorationsAlwaysVisible
8788
8889 property bool showPointerMenu: revealControls &&
89− (PanelState.decorationsVisible || mode == "staged")
90+ (panelState.decorationsVisible || mode == "staged")
9091
9192 property bool enablePointerMenu: revealControls &&
9293 applicationMenus.available &&
@@ -138,7 +139,7 @@ Item {
138139 fill: panelAreaBackground
139140 bottomMargin: -units.gu(1)
140141 }
141− visible: PanelState.dropShadow
142+ visible: panelState.dropShadow
142143 source: "graphics/rectangular_dropshadow.sci"
143144 }
144145
@@ -195,12 +196,12 @@ Item {
195196 visible: opacity != 0
196197 Behavior on opacity { UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration } }
197198
198− active: PanelState.decorationsVisible || PanelState.decorationsAlwaysVisible
199+ active: panelState.decorationsVisible || panelState.decorationsAlwaysVisible
199200 windowIsMaximized: true
200− onCloseClicked: PanelState.closeClicked()
201− onMinimizeClicked: PanelState.minimizeClicked()
202− onMaximizeClicked: PanelState.restoreClicked()
203− closeButtonShown: PanelState.closeButtonShown
201+ onCloseClicked: panelState.closeClicked()
202+ onMinimizeClicked: panelState.minimizeClicked()
203+ onMaximizeClicked: panelState.restoreClicked()
204+ closeButtonShown: panelState.closeButtonShown
204205 }
205206
206207 Loader {
@@ -220,11 +221,12 @@ Item {
220221 sourceComponent: MenuBar {
221222 id: bar
222223 objectName: "menuBar"
223− anchors.left: parent.left
224+ anchors.left: menuBarLoader.left
224225 anchors.margins: units.gu(1)
225226 height: menuBarLoader.height
226− enableKeyFilter: valid && PanelState.decorationsVisible
227+ enableKeyFilter: valid && panelState.decorationsVisible
227228 unityMenuModel: __applicationMenus.model
229+ panelState: root.panelState
228230
229231 Connections {
230232 target: __applicationMenus
@@ -236,7 +238,7 @@ Item {
236238 onShownChanged: bar.dismiss();
237239 }
238240
239− onDoubleClicked: PanelState.restoreClicked()
241+ onDoubleClicked: panelState.restoreClicked()
240242 onPressed: mouse.accepted = false // let the parent mouse area handle this, so it can both unsnap window and show menu
241243 }
242244 }
@@ -343,7 +345,7 @@ Item {
343345 opacity: __applicationMenus.visible && !__applicationMenus.expanded ? 1 : 0
344346 visible: opacity != 0
345347 Behavior on opacity { NumberAnimation { duration: UbuntuAnimation.SnapDuration } }
346− text: PanelState.title
348+ text: panelState.title
347349 }
348350
349351 PanelMenu {
qml/Shell.qml+29−11
@@ -1,4 +1,4 @@
1−/*
1+/*
22 * Copyright (C) 2013-2016 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
@@ -38,6 +38,7 @@ import "Notifications"
3838import "Stage"
3939import "Tutorial"
4040import "Wizard"
41+import "Components/PanelState"
4142import Unity.Notifications 1.0 as NotificationBackend
4243import Unity.Session 0.1
4344import Unity.DashCommunicator 0.1
@@ -52,6 +53,7 @@ StyledItem {
5253 theme.name: "Ubuntu.Components.Themes.SuruDark"
5354
5455 // to be set from outside
56+ property alias surfaceManager: topLevelSurfaceList.surfaceManager
5557 property int orientationAngle: 0
5658 property int orientation
5759 property Orientations orientations
@@ -260,21 +262,21 @@ StyledItem {
260262 schema.id: "com.canonical.Unity8"
261263 }
262264
265+ PanelState {
266+ id: panelState
267+ objectName: "panelState"
268+ }
269+
263270 Item {
264271 id: stages
265272 objectName: "stages"
266273 width: parent.width
267274 height: parent.height
268275
269− SurfaceManager {
270− id: surfaceMan
271− objectName: "surfaceManager"
272− }
273276 TopLevelWindowModel {
274277 id: topLevelSurfaceList
275278 objectName: "topLevelSurfaceList"
276279 applicationManager: ApplicationManager // it's a singleton
277− surfaceManager: surfaceMan
278280 }
279281
280282 Stage {
@@ -316,6 +318,7 @@ StyledItem {
316318 altTabPressed: physicalKeysMapper.altTabPressed
317319 oskEnabled: shell.oskEnabled
318320 spreadEnabled: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
321+ panelState: panelState
319322
320323 onSpreadShownChanged: {
321324 panel.indicators.hide();
@@ -370,8 +373,13 @@ StyledItem {
370373 objectName: "greeterLoader"
371374 anchors.fill: parent
372375 anchors.topMargin: panel.panelHeight
373− sourceComponent: shell.mode != "shell" ? integratedGreeter :
374− Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
376+ sourceComponent: {
377+ if (shell.mode != "shell") {
378+ if (screenWindow.primary) return integratedGreeter;
379+ return secondaryGreeter;
380+ }
381+ return Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
382+ }
375383 onLoaded: {
376384 item.objectName = "greeter"
377385 }
@@ -421,6 +429,13 @@ StyledItem {
421429 }
422430 }
423431
432+ Component {
433+ id: secondaryGreeter
434+ SecondaryGreeter {
435+ hides: [launcher, panel.indicators]
436+ }
437+ }
438+
424439 Timer {
425440 // See powerConnection for why this is useful
426441 id: showGreeterDelayed
@@ -538,6 +553,7 @@ StyledItem {
538553 || greeter.hasLockedApp
539554 greeterShown: greeter && greeter.shown
540555 hasKeyboard: shell.hasKeyboard
556+ panelState: panelState
541557 }
542558
543559 Launcher {
@@ -786,11 +802,11 @@ StyledItem {
786802 Cursor {
787803 id: cursor
788804 objectName: "cursor"
789− visible: shell.hasMouse
805+
790806 z: itemGrabber.z + 1
791807 topBoundaryOffset: panel.panelHeight
792−
793− confiningItem: stage.itemConfiningMouseCursor
808+ enabled: shell.hasMouse && screenWindow.active
809+ visible: enabled
794810
795811 property bool mouseNeverMoved: true
796812 Binding {
@@ -802,6 +818,8 @@ StyledItem {
802818 when: cursor.mouseNeverMoved && cursor.visible
803819 }
804820
821+ confiningItem: stage.itemConfiningMouseCursor
822+
805823 height: units.gu(3)
806824
807825 readonly property var previewRectangle: stage.previewRectangle.target &&
qml/ShellApplication.qmlnew file+52
@@ -0,0 +1,52 @@
1+/*
2+* Copyright (C) 2016 Canonical, Ltd.
3+*
4+* This program is free software; you can redistribute it and/or modify
5+* it under the terms of the GNU General Public License as published by
6+* the Free Software Foundation; version 3.
7+*
8+* This program is distributed in the hope that it will be useful,
9+* but WITHOUT ANY WARRANTY; without even the implied warranty of
10+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+* GNU General Public License for more details.
12+*
13+* You should have received a copy of the GNU General Public License
14+* along with this program. If not, see <http://www.gnu.org/licenses/>.
15+*/
16+
17+import QtQuick 2.4
18+import QtQuick.Window 2.2
19+import Unity.Screens 0.1
20+import Unity.Application 0.1
21+
22+Instantiator {
23+ id: root
24+ model: Screens
25+
26+ property QtObject surfaceMan: SurfaceManager {}
27+
28+ ShellScreen {
29+ id: window
30+ objectName: "screen"+index
31+ screen: model.screen
32+ visibility: applicationArguments.hasFullscreen ? Window.FullScreen : Window.Windowed
33+ flags: applicationArguments.hasFrameless ? Qt.FramelessWindowHint : 0
34+ surfaceManager: surfaceMan
35+
36+ Binding {
37+ when: applicationArguments.hasGeometry
38+ target: window
39+ property: "width"
40+ value: applicationArguments.windowGeometry.width
41+ }
42+ Binding {
43+ when: applicationArguments.hasGeometry
44+ target: window
45+ property: "height"
46+ value: applicationArguments.windowGeometry.height
47+ }
48+
49+ Component.onCompleted: screen.active = primary
50+ primary: index == 0
51+ }
52+}
src/SecondaryWindow.h → qml/ShellNotifier.qmlrenamed+12−12
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2015 Canonical, Ltd.
2+ * Copyright (C) 2016 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -14,17 +14,17 @@
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1515 */
1616
17−#ifndef UNITY_SECONDARY_WINDOW_H
18−#define UNITY_SECONDARY_WINDOW_H
19−
20−#include <QQuickView>
17+/*
18+ Shared signals & properties on multi-window desktop
19+ */
2120
22−class SecondaryWindow : public QQuickView
23−{
24− Q_OBJECT
21+pragma Singleton
22+import QtQuick 2.4
2523
26−public:
27− SecondaryWindow(QQmlEngine *engine);
28−};
24+QtObject {
25+ property var greeter: QtObject {
26+ signal hide(bool now)
2927
30−#endif // UNITY_SECONDARY_WINDOW_H
28+ property bool shown: true
29+ }
30+}
qml/ShellScreen.qmlnew file+66
@@ -0,0 +1,66 @@
1+/*
2+* Copyright (C) 2016 Canonical, Ltd.
3+*
4+* This program is free software; you can redistribute it and/or modify
5+* it under the terms of the GNU General Public License as published by
6+* the Free Software Foundation; version 3.
7+*
8+* This program is distributed in the hope that it will be useful,
9+* but WITHOUT ANY WARRANTY; without even the implied warranty of
10+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+* GNU General Public License for more details.
12+*
13+* You should have received a copy of the GNU General Public License
14+* along with this program. If not, see <http://www.gnu.org/licenses/>.
15+*/
16+
17+import QtQuick 2.4
18+import Ubuntu.Components 1.3
19+import Unity.Screens 0.1
20+import Cursor 1.1
21+import "Components"
22+
23+ScreenWindow {
24+ id: screenWindow
25+
26+ color: "black"
27+ title: "Unity8 Shell"
28+ property bool primary: false
29+ property QtObject surfaceManager: null
30+
31+ DeviceConfiguration {
32+ id: deviceConfiguration
33+ name: applicationArguments.deviceName
34+ }
35+
36+ Loader {
37+ id: loader
38+ width: screenWindow.width
39+ height: screenWindow.height
40+
41+ sourceComponent: {
42+ if (Screens.count > 1 && primary && deviceConfiguration.category !== "desktop") {
43+ return disabledScreenComponent;
44+ }
45+ return shellComponent;
46+ }
47+ }
48+
49+ Component {
50+ id: shellComponent
51+ OrientedShell {
52+ implicitWidth: screenWindow.width
53+ implicitHeight: screenWindow.height
54+ surfaceManager: screenWindow.surfaceManager
55+
56+ deviceConfiguration {
57+ name: Screens.count > 1 ? "desktop" : applicationArguments.deviceName
58+ }
59+ }
60+ }
61+
62+ Component {
63+ id: disabledScreenComponent
64+ DisabledScreenNotice {}
65+ }
66+}
qml/Stage/DecoratedWindow.qml+2−1
@@ -38,6 +38,7 @@ FocusScope {
3838 property alias maximizeButtonShown: decoration.maximizeButtonShown
3939 property alias interactive: applicationWindow.interactive
4040 readonly property alias orientationChangesEnabled: applicationWindow.orientationChangesEnabled
41+ property PanelState panelState
4142
4243 // Changing this will actually add/remove a decoration, meaning, requestedHeight will take the decoration into account.
4344 property bool hasDecoration: true
@@ -234,7 +235,7 @@ FocusScope {
234235 enableMenus: {
235236 return active &&
236237 surface &&
237− (PanelState.focusedPersistentSurfaceId === surface.persistentId && !PanelState.decorationsVisible)
238+ (panelState.focusedPersistentSurfaceId === surface.persistentId && !panelState.decorationsVisible)
238239 }
239240 menu: sharedAppModel.model
240241
qml/Stage/FakeMaximizeDelegate.qml+12−11
@@ -35,6 +35,7 @@ Rectangle {
3535 property int leftMargin
3636 property real appContainerWidth
3737 property real appContainerHeight
38+ property PanelState panelState
3839
3940 readonly property real hintThreshold: 0.1
4041
@@ -178,7 +179,7 @@ Rectangle {
178179 ParallelAnimation {
179180 id: fakeMaximizeAnimation
180181 UbuntuNumberAnimation { target: fakeRectangle; properties: "x"; duration: UbuntuAnimation.BriskDuration; to: leftMargin }
181− UbuntuNumberAnimation { target: fakeRectangle; properties: "y"; duration: UbuntuAnimation.BriskDuration; to: PanelState.panelHeight }
182+ UbuntuNumberAnimation { target: fakeRectangle; properties: "y"; duration: UbuntuAnimation.BriskDuration; to: panelState.panelHeight }
182183 UbuntuNumberAnimation { target: fakeRectangle; properties: "width"; duration: UbuntuAnimation.BriskDuration; to: appContainerWidth - leftMargin }
183184 UbuntuNumberAnimation { target: fakeRectangle; properties: "height"; duration: UbuntuAnimation.BriskDuration; to: appContainerHeight }
184185 }
@@ -186,39 +187,39 @@ Rectangle {
186187 ParallelAnimation {
187188 id: fakeMaximizeLeftAnimation
188189 UbuntuNumberAnimation { target: fakeRectangle; properties: "x"; duration: UbuntuAnimation.BriskDuration; to: leftMargin }
189− UbuntuNumberAnimation { target: fakeRectangle; properties: "y"; duration: UbuntuAnimation.BriskDuration; to: PanelState.panelHeight }
190+ UbuntuNumberAnimation { target: fakeRectangle; properties: "y"; duration: UbuntuAnimation.BriskDuration; to: panelState.panelHeight }
190191 UbuntuNumberAnimation { target: fakeRectangle; properties: "width"; duration: UbuntuAnimation.BriskDuration; to: (appContainerWidth - leftMargin)/2 }
191− UbuntuNumberAnimation { target: fakeRectangle; properties: "height"; duration: UbuntuAnimation.BriskDuration; to: appContainerHeight - PanelState.panelHeight }
192+ UbuntuNumberAnimation { target: fakeRectangle; properties: "height"; duration: UbuntuAnimation.BriskDuration; to: appContainerHeight - panelState.panelHeight }
192193 }
193194
194195 ParallelAnimation {
195196 id: fakeMaximizeRightAnimation
196197 UbuntuNumberAnimation { target: fakeRectangle; properties: "x"; duration: UbuntuAnimation.BriskDuration; to: (appContainerWidth + leftMargin)/2 }
197− UbuntuNumberAnimation { target: fakeRectangle; properties: "y"; duration: UbuntuAnimation.BriskDuration; to: PanelState.panelHeight }
198+ UbuntuNumberAnimation { target: fakeRectangle; properties: "y"; duration: UbuntuAnimation.BriskDuration; to: panelState.panelHeight }
198199 UbuntuNumberAnimation { target: fakeRectangle; properties: "width"; duration: UbuntuAnimation.BriskDuration; to: (appContainerWidth - leftMargin)/2 }
199− UbuntuNumberAnimation { target: fakeRectangle; properties: "height"; duration: UbuntuAnimation.BriskDuration; to: appContainerHeight - PanelState.panelHeight }
200+ UbuntuNumberAnimation { target: fakeRectangle; properties: "height"; duration: UbuntuAnimation.BriskDuration; to: appContainerHeight - panelState.panelHeight }
200201 }
201202
202203 ParallelAnimation {
203204 id: fakeMaximizeTopLeftAnimation
204205 UbuntuNumberAnimation { target: fakeRectangle; properties: "x"; duration: UbuntuAnimation.BriskDuration; to: leftMargin }
205− UbuntuNumberAnimation { target: fakeRectangle; properties: "y"; duration: UbuntuAnimation.BriskDuration; to: PanelState.panelHeight }
206+ UbuntuNumberAnimation { target: fakeRectangle; properties: "y"; duration: UbuntuAnimation.BriskDuration; to: panelState.panelHeight }
206207 UbuntuNumberAnimation { target: fakeRectangle; properties: "width"; duration: UbuntuAnimation.BriskDuration; to: (appContainerWidth - leftMargin)/2 }
207− UbuntuNumberAnimation { target: fakeRectangle; properties: "height"; duration: UbuntuAnimation.BriskDuration; to: (appContainerHeight - PanelState.panelHeight)/2 }
208+ UbuntuNumberAnimation { target: fakeRectangle; properties: "height"; duration: UbuntuAnimation.BriskDuration; to: (appContainerHeight - panelState.panelHeight)/2 }
208209 }
209210
210211 ParallelAnimation {
211212 id: fakeMaximizeTopRightAnimation
212213 UbuntuNumberAnimation { target: fakeRectangle; properties: "x"; duration: UbuntuAnimation.BriskDuration; to: (appContainerWidth + leftMargin)/2 }
213− UbuntuNumberAnimation { target: fakeRectangle; properties: "y"; duration: UbuntuAnimation.BriskDuration; to: PanelState.panelHeight }
214+ UbuntuNumberAnimation { target: fakeRectangle; properties: "y"; duration: UbuntuAnimation.BriskDuration; to: panelState.panelHeight }
214215 UbuntuNumberAnimation { target: fakeRectangle; properties: "width"; duration: UbuntuAnimation.BriskDuration; to: (appContainerWidth - leftMargin)/2 }
215− UbuntuNumberAnimation { target: fakeRectangle; properties: "height"; duration: UbuntuAnimation.BriskDuration; to: (appContainerHeight - PanelState.panelHeight)/2 }
216+ UbuntuNumberAnimation { target: fakeRectangle; properties: "height"; duration: UbuntuAnimation.BriskDuration; to: (appContainerHeight - panelState.panelHeight)/2 }
216217 }
217218
218219 ParallelAnimation {
219220 id: fakeMaximizeBottomLeftAnimation
220221 UbuntuNumberAnimation { target: fakeRectangle; properties: "x"; duration: UbuntuAnimation.BriskDuration; to: leftMargin }
221− UbuntuNumberAnimation { target: fakeRectangle; properties: "y"; duration: UbuntuAnimation.BriskDuration; to: (appContainerHeight + PanelState.panelHeight)/2 }
222+ UbuntuNumberAnimation { target: fakeRectangle; properties: "y"; duration: UbuntuAnimation.BriskDuration; to: (appContainerHeight + panelState.panelHeight)/2 }
222223 UbuntuNumberAnimation { target: fakeRectangle; properties: "width"; duration: UbuntuAnimation.BriskDuration; to: (appContainerWidth - leftMargin)/2 }
223224 UbuntuNumberAnimation { target: fakeRectangle; properties: "height"; duration: UbuntuAnimation.BriskDuration; to: appContainerHeight/2 }
224225 }
@@ -226,7 +227,7 @@ Rectangle {
226227 ParallelAnimation {
227228 id: fakeMaximizeBottomRightAnimation
228229 UbuntuNumberAnimation { target: fakeRectangle; properties: "x"; duration: UbuntuAnimation.BriskDuration; to: (appContainerWidth + leftMargin)/2 }
229− UbuntuNumberAnimation { target: fakeRectangle; properties: "y"; duration: UbuntuAnimation.BriskDuration; to: (appContainerHeight + PanelState.panelHeight)/2 }
230+ UbuntuNumberAnimation { target: fakeRectangle; properties: "y"; duration: UbuntuAnimation.BriskDuration; to: (appContainerHeight + panelState.panelHeight)/2 }
230231 UbuntuNumberAnimation { target: fakeRectangle; properties: "width"; duration: UbuntuAnimation.BriskDuration; to: (appContainerWidth - leftMargin)/2 }
231232 UbuntuNumberAnimation { target: fakeRectangle; properties: "height"; duration: UbuntuAnimation.BriskDuration; to: appContainerHeight/2 }
232233 }
qml/Stage/Stage.qml+27−24
@@ -47,6 +47,7 @@ FocusScope {
4747 property bool oskEnabled: false
4848 property rect inputMethodRect
4949 property real rightEdgePushProgress: 0
50+ property PanelState panelState
5051
5152 // Configuration
5253 property string mode: "staged"
@@ -352,20 +353,20 @@ FocusScope {
352353 Component.onCompleted: priv.updateMainAndSideStageIndexes();
353354
354355 Connections {
355− target: PanelState
356+ target: panelState
356357 onCloseClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.close(); } }
357358 onMinimizeClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.requestMinimize(); } }
358359 onRestoreClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.requestRestore(); } }
359360 }
360361
361362 Binding {
362− target: PanelState
363+ target: panelState
363364 property: "decorationsVisible"
364365 value: priv.focusedAppDelegate !== null && priv.focusedAppDelegate.maximized // FIXME for Locally integrated menus
365366 }
366367
367368 Binding {
368− target: PanelState
369+ target: panelState
369370 property: "title"
370371 value: {
371372 if (priv.focusedAppDelegate !== null) {
@@ -380,7 +381,7 @@ FocusScope {
380381 }
381382
382383 Binding {
383− target: PanelState
384+ target: panelState
384385 property: "focusedPersistentSurfaceId"
385386 value: {
386387 if (priv.focusedAppDelegate !== null) {
@@ -394,21 +395,21 @@ FocusScope {
394395 }
395396
396397 Binding {
397− target: PanelState
398+ target: panelState
398399 property: "dropShadow"
399400 value: priv.focusedAppDelegate && !priv.focusedAppDelegate.maximized && priv.foregroundMaximizedAppDelegate !== null && mode == "windowed"
400401 }
401402
402403 Binding {
403− target: PanelState
404+ target: panelState
404405 property: "closeButtonShown"
405406 value: priv.focusedAppDelegate && priv.focusedAppDelegate.maximized && !priv.focusedAppDelegate.isDash
406407 }
407408
408409 Component.onDestruction: {
409− PanelState.title = "";
410− PanelState.decorationsVisible = false;
411− PanelState.dropShadow = false;
410+ panelState.title = "";
411+ panelState.decorationsVisible = false;
412+ panelState.dropShadow = false;
412413 }
413414
414415 Instantiator {
@@ -678,7 +679,7 @@ FocusScope {
678679 Item {
679680 id: boundariesForWindowPlacement
680681 anchors.fill: parent
681− anchors.topMargin: PanelState.panelHeight
682+ anchors.topMargin: panelState.panelHeight
682683 visible: false
683684 }
684685
@@ -779,7 +780,7 @@ FocusScope {
779780 target: appDelegate
780781 property: "y"
781782 value: appDelegate.requestedY -
782− Math.min(appDelegate.requestedY - PanelState.panelHeight,
783+ Math.min(appDelegate.requestedY - panelState.panelHeight,
783784 Math.max(0, priv.virtualKeyboardHeight - (appContainer.height - (appDelegate.requestedY + appDelegate.height))))
784785 when: root.oskEnabled && appDelegate.focus && (appDelegate.state == "normal" || appDelegate.state == "restored")
785786 && root.inputMethodRect.height > 0
@@ -1177,7 +1178,7 @@ FocusScope {
11771178 progress: 0
11781179 targetHeight: spreadItem.stackHeight
11791180 targetX: spreadMaths.targetX
1180− startY: appDelegate.fullscreen ? 0 : PanelState.panelHeight
1181+ startY: appDelegate.fullscreen ? 0 : panelState.panelHeight
11811182 targetY: spreadMaths.targetY
11821183 targetAngle: spreadMaths.targetAngle
11831184 targetScale: spreadMaths.targetScale
@@ -1298,9 +1299,9 @@ FocusScope {
12981299 PropertyChanges {
12991300 target: appDelegate
13001301 x: stageMaths.itemX
1301− y: appDelegate.fullscreen ? 0 : PanelState.panelHeight
1302+ y: appDelegate.fullscreen ? 0 : panelState.panelHeight
13021303 requestedWidth: appContainer.width
1303− requestedHeight: appDelegate.fullscreen ? appContainer.height : appContainer.height - PanelState.panelHeight
1304+ requestedHeight: appDelegate.fullscreen ? appContainer.height : appContainer.height - panelState.panelHeight
13041305 visuallyMaximized: true
13051306 visible: appDelegate.x < root.width
13061307 }
@@ -1330,10 +1331,10 @@ FocusScope {
13301331 PropertyChanges {
13311332 target: appDelegate
13321333 x: stageMaths.itemX
1333− y: appDelegate.fullscreen ? 0 : PanelState.panelHeight
1334+ y: appDelegate.fullscreen ? 0 : panelState.panelHeight
13341335 z: stageMaths.itemZ
13351336 requestedWidth: stageMaths.itemWidth
1336− requestedHeight: appDelegate.fullscreen ? appContainer.height : appContainer.height - PanelState.panelHeight
1337+ requestedHeight: appDelegate.fullscreen ? appContainer.height : appContainer.height - panelState.panelHeight
13371338 visuallyMaximized: true
13381339 visible: appDelegate.x < root.width
13391340 }
@@ -1402,9 +1403,9 @@ FocusScope {
14021403 PropertyChanges {
14031404 target: appDelegate
14041405 windowedX: root.leftMargin
1405− windowedY: PanelState.panelHeight
1406+ windowedY: panelState.panelHeight
14061407 windowedWidth: (appContainer.width - root.leftMargin)/2
1407− windowedHeight: appContainer.height - PanelState.panelHeight
1408+ windowedHeight: appContainer.height - panelState.panelHeight
14081409 }
14091410 },
14101411 State {
@@ -1421,9 +1422,9 @@ FocusScope {
14211422 PropertyChanges {
14221423 target: appDelegate
14231424 windowedX: root.leftMargin
1424− windowedY: PanelState.panelHeight
1425+ windowedY: panelState.panelHeight
14251426 windowedWidth: (appContainer.width - root.leftMargin)/2
1426− windowedHeight: (appContainer.height - PanelState.panelHeight)/2
1427+ windowedHeight: (appContainer.height - panelState.panelHeight)/2
14271428 }
14281429 },
14291430 State {
@@ -1440,7 +1441,7 @@ FocusScope {
14401441 PropertyChanges {
14411442 target: appDelegate
14421443 windowedX: root.leftMargin
1443− windowedY: (appContainer.height + PanelState.panelHeight)/2
1444+ windowedY: (appContainer.height + panelState.panelHeight)/2
14441445 windowedWidth: (appContainer.width - root.leftMargin)/2
14451446 windowedHeight: appContainer.height/2
14461447 }
@@ -1462,8 +1463,8 @@ FocusScope {
14621463 State {
14631464 name: "maximizedVertically"; when: appDelegate.maximizedVertically && !appDelegate.minimized
14641465 extend: "normal"
1465− PropertyChanges { target: appDelegate; windowedX: windowedX; windowedY: PanelState.panelHeight;
1466− windowedWidth: windowedWidth; windowedHeight: appContainer.height - PanelState.panelHeight }
1466+ PropertyChanges { target: appDelegate; windowedX: windowedX; windowedY: panelState.panelHeight;
1467+ windowedWidth: windowedWidth; windowedHeight: appContainer.height - panelState.panelHeight }
14671468 },
14681469 State {
14691470 name: "minimized"; when: appDelegate.minimized
@@ -1576,7 +1577,7 @@ FocusScope {
15761577 ]
15771578
15781579 Binding {
1579− target: PanelState
1580+ target: panelState
15801581 property: "decorationsAlwaysVisible"
15811582 value: appDelegate && appDelegate.maximized && touchControls.overlayShown
15821583 }
@@ -1621,6 +1622,7 @@ FocusScope {
16211622 highlightSize: windowInfoItem.iconMargin / 2
16221623 boundsItem: boundariesForWindowPlacement
16231624 altDragEnabled: root.mode == "windowed"
1625+ panelState: root.panelState
16241626
16251627 requestedWidth: appDelegate.requestedWidth
16261628 requestedHeight: appDelegate.requestedHeight
@@ -1820,6 +1822,7 @@ FocusScope {
18201822 leftMargin: root.leftMargin
18211823 appContainerWidth: appContainer.width
18221824 appContainerHeight: appContainer.height
1825+ panelState: root.panelState
18231826 }
18241827
18251828 MouseArea {
qml/qmldirnew file+1
@@ -0,0 +1 @@
1+singleton ShellNotifier 0.1 ShellNotifier.qml
src/ApplicationArguments.cpp+11−2
@@ -16,7 +16,16 @@
1616
1717#include "ApplicationArguments.h"
1818
19−ApplicationArguments::ApplicationArguments(QObject *parent)
20− : QObject(parent)
19+ApplicationArguments::ApplicationArguments(QCoreApplication *app)
20+ : QObject(app)
21+ , UnityCommandLineParser(*app)
2122{
2223}
24+
25+void ApplicationArguments::setDeviceName(const QString &deviceName)
26+{
27+ if (deviceName != m_deviceName) {
28+ m_deviceName = deviceName;
29+ Q_EMIT deviceNameChanged(m_deviceName);
30+ }
31+}
src/ApplicationArguments.h+17−15
@@ -23,31 +23,33 @@
2323#include <QSize>
2424#include <QString>
2525
26−class ApplicationArguments : public QObject
26+#include "UnityCommandLineParser.h"
27+
28+class ApplicationArguments : public QObject,
29+ public UnityCommandLineParser
2730{
2831 Q_OBJECT
2932 Q_PROPERTY(QString deviceName READ deviceName NOTIFY deviceNameChanged)
3033 Q_PROPERTY(QString mode READ mode CONSTANT)
34+
35+ Q_PROPERTY(bool hasGeometry READ hasGeometry CONSTANT)
36+ Q_PROPERTY(QSize windowGeometry READ windowGeometry CONSTANT)
37+ Q_PROPERTY(bool hasTestability READ hasTestability CONSTANT)
38+ Q_PROPERTY(bool hasFrameless READ hasFrameless CONSTANT)
39+ Q_PROPERTY(bool hasFullscreen READ hasFullscreen CONSTANT)
40+#ifdef UNITY8_ENABLE_TOUCH_EMULATION
41+ Q_PROPERTY(bool hasMouseToTouch READ hasMouseToTouch CONSTANT)
42+#endif
43+
3144public:
32− ApplicationArguments(QObject *parent = nullptr);
45+ ApplicationArguments(QCoreApplication *app);
3346
34− void setDeviceName(const QString &deviceName) {
35− if (deviceName != m_deviceName) {
36− m_deviceName = deviceName;
37− Q_EMIT deviceNameChanged(m_deviceName);
38− }
39− }
40− QString deviceName() const { return m_deviceName; }
47+ void setDeviceName(const QString &deviceName);
4148
42− void setMode(const QString &mode) { m_mode = mode; }
43− QString mode() const { return m_mode; }
49+ bool hasGeometry() const { return m_windowGeometry.isValid(); }
4450
4551Q_SIGNALS:
4652 void deviceNameChanged(const QString&);
47−
48−private:
49− QString m_deviceName;
50− QString m_mode;
5153};
5254
5355#endif // APPLICATION_ARGUMENTS_H
src/CMakeLists.txt−2
@@ -29,9 +29,7 @@ set(SOURCE_FILES
2929 ApplicationArguments.cpp
3030 main.cpp
3131 CachingNetworkManagerFactory.cpp
32− SecondaryWindow.cpp
3332 ShellApplication.cpp
34− ShellView.cpp
3533 UnityCommandLineParser.cpp
3634 UnixSignalHandler.cpp
3735 DebuggingController.cpp
src/SecondaryWindow.cppdeleted−40
@@ -1,40 +0,0 @@
1−/*
2− * Copyright (C) 2015 Canonical, Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−#include "SecondaryWindow.h"
18−
19−// local
20−#include <paths.h>
21−
22−#include <QQmlContext>
23−
24−SecondaryWindow::SecondaryWindow(QQmlEngine *engine)
25− : QQuickView(engine, nullptr)
26−{
27− QByteArray pxpguEnv = qgetenv("GRID_UNIT_PX");
28− bool ok;
29− int pxpgu = pxpguEnv.toInt(&ok);
30− if (!ok) {
31− pxpgu = 8;
32− }
33− engine->rootContext()->setContextProperty(QStringLiteral("internalGu"), pxpgu);
34− setResizeMode(QQuickView::SizeRootObjectToView);
35− setColor("black");
36− setTitle(QStringLiteral("Unity8 Shell - Secondary Screen"));
37−
38− QUrl source(::qmlDirectory() + "/DisabledScreenNotice.qml");
39− setSource(source);
40−}
src/ShellApplication.cpp+27−102
@@ -20,6 +20,8 @@
2020#include <QLibrary>
2121#include <QProcess>
2222#include <QScreen>
23+#include <QQmlContext>
24+#include <QQmlComponent>
2325
2426#include <QGSettings>
2527
@@ -36,30 +38,22 @@
3638
3739ShellApplication::ShellApplication(int & argc, char ** argv, bool isMirServer)
3840 : QGuiApplication(argc, argv)
41+ , m_qmlArgs(this)
3942{
4043 setApplicationName(QStringLiteral("unity8"));
4144 setOrganizationName(QStringLiteral("Canonical"));
4245
43− connect(this, &QGuiApplication::screenAdded, this, &ShellApplication::onScreenAdded);
44−
4546 setupQmlEngine(isMirServer);
4647
47− UnityCommandLineParser parser(*this);
48−
49− if (!parser.deviceName().isEmpty()) {
50− m_deviceName = parser.deviceName();
51− } else {
48+ if (m_qmlArgs.deviceName().isEmpty()) {
5249 char buffer[200];
5350 property_get("ro.product.device", buffer /* value */, "desktop" /* default_value*/);
54− m_deviceName = QString(buffer);
51+ m_qmlArgs.setDeviceName(QString(buffer));
5552 }
56− m_qmlArgs.setDeviceName(m_deviceName);
57−
58− m_qmlArgs.setMode(parser.mode());
5953
6054 // The testability driver is only loaded by QApplication but not by QGuiApplication.
6155 // However, QApplication depends on QWidget which would add some unneeded overhead => Let's load the testability driver on our own.
62− if (parser.hasTestability() || getenv("QT_LOAD_TESTABILITY")) {
56+ if (m_qmlArgs.hasTestability() || getenv("QT_LOAD_TESTABILITY")) {
6357 QLibrary testLib(QStringLiteral("qttestability"));
6458 if (testLib.load()) {
6559 typedef void (*TasInitialize)(void);
@@ -80,57 +74,41 @@ ShellApplication::ShellApplication(int & argc, char ** argv, bool isMirServer)
8074 QScopedPointer<QGSettings> gSettings(new QGSettings("com.canonical.Unity8"));
8175 gSettings->reset(QStringLiteral("alwaysShowOsk"));
8276
83− m_shellView = new ShellView(m_qmlEngine, &m_qmlArgs);
8477
85− if (parser.windowGeometry().isValid()) {
86− m_shellView->setWidth(parser.windowGeometry().width());
87− m_shellView->setHeight(parser.windowGeometry().height());
78+ QByteArray pxpguEnv = qgetenv("GRID_UNIT_PX");
79+ bool ok;
80+ int pxpgu = pxpguEnv.toInt(&ok);
81+ if (!ok) {
82+ pxpgu = 8;
8883 }
89−
90− if (parser.hasFrameless()) {
91− m_shellView->setFlags(Qt::FramelessWindowHint);
84+ m_qmlEngine->rootContext()->setContextProperty("internalGu", pxpgu);
85+ m_qmlEngine->rootContext()->setContextProperty(QStringLiteral("applicationArguments"), &m_qmlArgs);
86+ m_qmlEngine->rootContext()->setContextProperty("DebuggingController", new DebuggingController(this));
87+
88+ auto component(new QQmlComponent(m_qmlEngine,
89+ QUrl::fromLocalFile(::qmlDirectory() + "/ShellApplication.qml")));
90+ component->create();
91+ if (component->status() == QQmlComponent::Error) {
92+ m_qmlEngine->rootContext()->setContextProperty(QStringLiteral("errorString"), component->errorString());
93+ auto errorComponent(new QQmlComponent(m_qmlEngine,
94+ QUrl::fromLocalFile(::qmlDirectory() + "/ErrorApplication.qml")));
95+ errorComponent->create();
96+ qDebug() << errorComponent->errorString();
97+ return;
9298 }
9399
94−
95100 #ifdef UNITY8_ENABLE_TOUCH_EMULATION
96101 // You will need this if you want to interact with touch-only components using a mouse
97102 // Needed only when manually testing on a desktop.
98− if (parser.hasMouseToTouch()) {
103+ if (m_qmlArgs.hasMouseToTouch()) {
99104 m_mouseTouchAdaptor = MouseTouchAdaptor::instance();
100105 }
101106 #endif
102107
103− new DebuggingController(this);
104−
105− // Some hard-coded policy for now.
106− // NB: We don't support more than two screens at the moment
107− //
108− // TODO: Support an arbitrary number of screens and different policies
109− // (eg cloned desktop, several desktops, etc)
110− if (isMirServer && screens().count() == 2) {
111− m_shellView->setScreen(screens().at(1));
112− m_qmlArgs.setDeviceName(QStringLiteral("desktop"));
113−
114− m_secondaryWindow = new SecondaryWindow(m_qmlEngine);
115− m_secondaryWindow->setScreen(screens().at(0));
116− // QWindow::showFullScreen() also calls QWindow::requestActivate() and we don't want that!
117− m_secondaryWindow->setWindowState(Qt::WindowFullScreen);
118− m_secondaryWindow->setVisible(true);
119− }
120−
121− if (parser.mode().compare("greeter") == 0) {
122− QSize primaryScreenSize = this->primaryScreen()->size();
123− m_shellView->setHeight(primaryScreenSize.height());
124− m_shellView->setWidth(primaryScreenSize.width());
125− m_shellView->show();
126− m_shellView->requestActivate();
108+ if (m_qmlArgs.mode().compare("greeter") == 0) {
127109 if (!QProcess::startDetached("initctl emit --no-wait unity8-greeter-started")) {
128110 qDebug() << "Unable to send unity8-greeter-started event to Upstart";
129111 }
130− } else if (isMirServer || parser.hasFullscreen()) {
131− m_shellView->showFullScreen();
132− } else {
133− m_shellView->show();
134112 }
135113}
136114
@@ -141,14 +119,6 @@ ShellApplication::~ShellApplication()
141119
142120void ShellApplication::destroyResources()
143121{
144− // Deletion order is important. Don't use QScopedPointers and the like
145− // Otherwise the process will hang on shutdown (bug somewhere I guess).
146− delete m_shellView;
147− m_shellView = nullptr;
148−
149− delete m_secondaryWindow;
150− m_secondaryWindow = nullptr;
151−
152122 #ifdef UNITY8_ENABLE_TOUCH_EMULATION
153123 delete m_mouseTouchAdaptor;
154124 m_mouseTouchAdaptor = nullptr;
@@ -174,48 +144,3 @@ void ShellApplication::setupQmlEngine(bool isMirServer)
174144
175145 QObject::connect(m_qmlEngine, &QQmlEngine::quit, this, &QGuiApplication::quit);
176146}
177−
178−void ShellApplication::onScreenAdded(QScreen * /*screen*/)
179−{
180− // TODO: Support an arbitrary number of screens and different policies
181− // (eg cloned desktop, several desktops, etc)
182− if (screens().count() == 2) {
183− m_shellView->setScreen(screens().at(1));
184− m_qmlArgs.setDeviceName(QStringLiteral("desktop"));
185− // Changing the QScreen where a QWindow is drawn makes it also lose focus (besides having
186− // its backing QPlatformWindow recreated). So lets refocus it.
187− m_shellView->requestActivate();
188− // QWindow::destroy() is called when it changes between screens. We have to manually make it visible again
189− // <dandrader> This bug is supposedly fixed in Qt 5.5.1, although I can still reproduce it there. :-/
190− m_shellView->setVisible(true);
191−
192− m_secondaryWindow = new SecondaryWindow(m_qmlEngine);
193− m_secondaryWindow->setScreen(screens().at(0));
194−
195− // QWindow::showFullScreen() also calls QWindow::requestActivate() and we don't want that!
196− m_secondaryWindow->setWindowState(Qt::WindowFullScreen);
197− m_secondaryWindow->setVisible(true);
198− }
199−}
200−
201−void ShellApplication::onScreenAboutToBeRemoved(QScreen *screen)
202−{
203− // TODO: Support an arbitrary number of screens and different policies
204− // (eg cloned desktop, several desktops, etc)
205− if (screen == m_shellView->screen()) {
206− const QList<QScreen *> allScreens = screens();
207− Q_ASSERT(allScreens.count() > 1);
208− Q_ASSERT(allScreens.at(0) != screen);
209− Q_ASSERT(m_secondaryWindow);
210− delete m_secondaryWindow;
211− m_secondaryWindow = nullptr;
212− m_shellView->setScreen(allScreens.first());
213− m_qmlArgs.setDeviceName(m_deviceName);
214− // Changing the QScreen where a QWindow is drawn makes it also lose focus (besides having
215− // its backing QPlatformWindow recreated). So lets refocus it.
216− m_shellView->requestActivate();
217− // QWindow::destroy() is called when it changes between screens. We have to manually make it visible again
218− // <dandrader> This bug is supposedly fixed in Qt 5.5.1, although I can still reproduce it there. :-/
219− m_shellView->setVisible(true);
220− }
221−}
src/ShellApplication.h+1−13
@@ -18,7 +18,7 @@
1818#define SHELLAPPLICATION_H
1919
2020#include <QGuiApplication>
21−#include <QQmlEngine>
21+#include <QQmlApplicationEngine>
2222#include <QQuickView>
2323#include <QScopedPointer>
2424
@@ -28,9 +28,6 @@
2828#include "MouseTouchAdaptor.h"
2929#endif
3030
31−#include "SecondaryWindow.h"
32−#include "ShellView.h"
33−
3431class ShellApplication : public QGuiApplication
3532{
3633 Q_OBJECT
@@ -39,19 +36,10 @@ public:
3936 virtual ~ShellApplication();
4037
4138 void destroyResources();
42−public Q_SLOTS:
43− // called by qtmir
44− void onScreenAboutToBeRemoved(QScreen *screen);
45−
46−private Q_SLOTS:
47− void onScreenAdded(QScreen*);
4839
4940private:
5041 void setupQmlEngine(bool isMirServer);
51− QString m_deviceName;
5242 ApplicationArguments m_qmlArgs;
53− ShellView *m_shellView{nullptr};
54− SecondaryWindow *m_secondaryWindow{nullptr};
5543
5644 #ifdef UNITY8_ENABLE_TOUCH_EMULATION
5745 MouseTouchAdaptor *m_mouseTouchAdaptor{nullptr};
src/ShellView.cppdeleted−85
@@ -1,85 +0,0 @@
1−/*
2− * Copyright (C) 2015 Canonical, Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−#include "ShellView.h"
18−
19−// Qt
20−#include <QQmlContext>
21−#include <QQuickItem>
22−#include <QtQuick/private/qquickitem_p.h>
23−#include <QtQuick/private/qquickrectangle_p.h>
24−#include <QtQuick/private/qquicktext_p.h>
25−
26−// local
27−#include <paths.h>
28−
29−ShellView::ShellView(QQmlEngine *engine, QObject *qmlArgs)
30− : QQuickView(engine, nullptr)
31−{
32− setResizeMode(QQuickView::SizeRootObjectToView);
33− setColor("black");
34− setTitle(QStringLiteral("Unity8"));
35−
36− rootContext()->setContextProperty(QStringLiteral("applicationArguments"), qmlArgs);
37−
38− connect(this, &QQuickView::statusChanged, this, [this] {
39− if (status() == QQuickView::Error) {
40− QQuickRectangle *rect = new QQuickRectangle(contentItem());
41− rect->setColor(Qt::white);
42− QQuickItemPrivate::get(rect)->anchors()->setFill(contentItem());
43−
44− QString errorsString;
45− for(const QQmlError &e: errors()) {
46− errorsString += e.toString() + "\n";
47− }
48− QQuickText *text = new QQuickText(rect);
49− text->setColor(Qt::black);
50− text->setWrapMode(QQuickText::Wrap);
51− text->setText(QString("There was an error loading Unity8:\n%1").arg(errorsString));
52− QQuickItemPrivate::get(text)->anchors()->setFill(rect);
53− }
54− }
55− );
56−
57− QUrl source(::qmlDirectory() + "/OrientedShell.qml");
58− setSource(source);
59−
60− connect(this, &QWindow::widthChanged, this, &ShellView::onWidthChanged);
61− connect(this, &QWindow::heightChanged, this, &ShellView::onHeightChanged);
62−}
63−
64−void ShellView::onWidthChanged(int w)
65−{
66− // For good measure in case SizeRootObjectToView doesn't fulfill its promise.
67− //
68− // There's at least one situation that's know to leave the root object with an outdated size.
69− // (really looks like Qt bug)
70− // Happens when starting unity8 with an external monitor already connected.
71− // The QResizeEvent we get still has the size of the first screen and since the resize move is triggered
72− // from the resize event handler, the root item doesn't get resized.
73− // TODO: Confirm the Qt bug and submit a patch upstream
74− if (rootObject()) {
75− rootObject()->setWidth(w);
76− }
77−}
78−
79−void ShellView::onHeightChanged(int h)
80−{
81− // See comment in ShellView::onWidthChanged()
82− if (rootObject()) {
83− rootObject()->setHeight(h);
84− }
85−}
src/ShellView.hdeleted−34
@@ -1,34 +0,0 @@
1−/*
2− * Copyright (C) 2015 Canonical, Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−#ifndef UNITY_SHELL_VIEW_H
18−#define UNITY_SHELL_VIEW_H
19−
20−#include <QQuickView>
21−
22−class ShellView : public QQuickView
23−{
24− Q_OBJECT
25−
26−public:
27− ShellView(QQmlEngine *engine, QObject *qmlArgs);
28−
29−private Q_SLOTS:
30− void onWidthChanged(int);
31− void onHeightChanged(int);
32−};
33−
34−#endif // UNITY_SHELL_VIEW_H
src/UnityCommandLineParser.h+1−1
@@ -36,8 +36,8 @@ public:
3636 bool hasFullscreen() const { return m_hasFullscreen; }
3737 QString deviceName() const { return m_deviceName; }
3838 QString mode() const { return m_mode; }
39−private:
4039
40+protected:
4141 int parsePixelsValue(const QString &str);
4242 static float getenvFloat(const char* name, float defaultValue);
4343 void resolveMode(QCommandLineParser &parser, QCommandLineOption &modeOption);
tests/mocks/Cursor/Cursor.qml+49−2
@@ -15,8 +15,11 @@
1515 */
1616
1717import QtQuick 2.4
18+import UInput 0.1
19+
20+Canvas {
21+ id: root
1822
19−Item {
2023 property int topBoundaryOffset // effectively panel height
2124 property Item confiningItem
2225
@@ -30,5 +33,49 @@ Item {
3033 signal pushStopped()
3134 signal mouseMoved()
3235
33− onMouseMoved: opacity = 1;
36+ width: units.gu(2)
37+ height: units.gu(2)
38+ antialiasing: true
39+
40+ onPaint: {
41+ var ctx = getContext("2d");
42+ ctx.save();
43+ ctx.clearRect(0,0,width, height);
44+ ctx.strokeStyle = "#000000";
45+ ctx.lineWidth = 1
46+ ctx.fillStyle = "#ffffff";
47+ ctx.globalAlpha = 1.0;
48+ ctx.lineJoin = "round";
49+ ctx.beginPath();
50+
51+ // put rectangle in the middle
52+ // draw the rectangle
53+ ctx.moveTo(width/2,height);
54+ ctx.lineTo(width, height/2);
55+ ctx.lineTo(0,0);
56+
57+ ctx.closePath();
58+ ctx.fill();
59+ ctx.stroke();
60+ ctx.restore();
61+ }
62+
63+ Connections {
64+ target: UInput
65+ onMouseMoved: {
66+ var newX = root.x;
67+ newX += dx;
68+ if (newX < 0) newX = 0;
69+ else if (newX >= parent.width) newX = parent.width-1;
70+
71+ var newY = root.y;
72+ newY += dy;
73+ if (newY < 0) newY = 0;
74+ else if (newY >= parent.height) newY = parent.height-1;
75+
76+ root.x = newX;
77+ root.y = newY;
78+ root.mouseMoved();
79+ }
80+ }
3481}
tests/mocks/GSettings.1.0/plugin.cpp+12
@@ -18,6 +18,8 @@
1818#include "fake_gsettings.h"
1919
2020#include <QtQml/qqml.h>
21+#include <QQmlEngine>
22+#include <QQmlContext>
2123
2224static QObject* controllerProvider(QQmlEngine* /* engine */, QJSEngine* /* scriptEngine */)
2325{
@@ -31,3 +33,13 @@ void FakeGSettingsQmlPlugin::registerTypes(const char *uri)
3133 qmlRegisterUncreatableType<GSettingsSchemaQml>(uri, 1, 0, "GSettingsSchema",
3234 "GSettingsSchema can only be used inside of a GSettings component");
3335}
36+
37+void FakeGSettingsQmlPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
38+{
39+ QQmlExtensionPlugin::initializeEngine(engine, uri);
40+
41+ QString usageType = qgetenv("UNITY_MOCK_DESKTOP");
42+ if (!usageType.isEmpty()) {
43+ GSettingsControllerQml::instance()->setUsageMode("Windowed");
44+ }
45+}
tests/mocks/GSettings.1.0/plugin.h+1
@@ -26,6 +26,7 @@ class FakeGSettingsQmlPlugin : public QQmlExtensionPlugin
2626
2727public:
2828 void registerTypes(const char *uri) override;
29+ void initializeEngine(QQmlEngine *engine, const char *uri) override;
2930};
3031
3132#endif // PLUGIN_H
tests/mocks/UInput/plugin.cpp+6−1
@@ -19,8 +19,13 @@
1919
2020#include <QtQml/qqml.h>
2121
22+QObject* uinputSingleton(QQmlEngine*, QJSEngine*)
23+{
24+ return new MockUInput;
25+}
26+
2227void MockUInputPlugin::registerTypes(const char *uri)
2328{
2429 Q_ASSERT(uri == QLatin1String("UInput"));
25− qmlRegisterType<MockUInput>(uri, 0, 1, "UInput");
30+ qmlRegisterSingletonType<MockUInput>(uri, 0, 1, "UInput", uinputSingleton);
2631}
tests/mocks/Unity/Application/SurfaceManager.cpp+1−1
@@ -21,7 +21,7 @@
2121
2222#include <paths.h>
2323
24−#define SURFACEMANAGER_DEBUG 0
24+#define SURFACEMANAGER_DEBUG 1
2525
2626#if SURFACEMANAGER_DEBUG
2727#define DEBUG_MSG(params) qDebug().nospace() << "SurfaceManager[" << (void*)this << "]::" << __func__ << params
tests/mocks/Unity/InputInfo/mockcontroller.cpp+10
@@ -18,10 +18,20 @@
1818
1919#include "qinputdeviceinfo_mock_p.h"
2020
21+#include <QQuickView>
22+#include <QQmlComponent>
23+#include <QQuickItem>
24+#include <paths.h>
25+
2126MockController::MockController(QObject *parent):
2227 QObject(parent)
2328{
29+}
2430
31+MockController *MockController::instance()
32+{
33+ static MockController* controller = new MockController();
34+ return controller;
2535}
2636
2737QInputDevice *MockController::addMockDevice(const QString &devicePath, QInputDevice::InputType type)
tests/mocks/Unity/InputInfo/mockcontroller.h+3−1
@@ -24,9 +24,11 @@ class MockController: public QObject
2424{
2525 Q_OBJECT
2626public:
27− MockController(QObject *parent = 0);
27+ MockController(QObject *parent = nullptr);
2828 ~MockController() = default;
2929
30+ static MockController *instance();
31+
3032 Q_INVOKABLE QInputDevice* addMockDevice(const QString &devicePath, QInputDevice::InputType type);
3133 Q_INVOKABLE void removeDevice(const QString &devicePath);
3234};
tests/mocks/Unity/InputInfo/plugin.cpp+1−2
@@ -26,9 +26,8 @@
2626
2727static QObject *backendProvider(QQmlEngine *engine, QJSEngine *scriptEngine)
2828{
29− Q_UNUSED(engine)
3029 Q_UNUSED(scriptEngine)
31− return new MockController(engine);
30+ return MockController::instance();
3231}
3332
3433void InputInfoPlugin::registerTypes(const char *uri)
tests/mocks/Unity/Screens/CMakeLists.txt+2−1
@@ -5,10 +5,11 @@ include_directories(
55set(MockScreens_SOURCES
66 plugin.cpp
77 screens.cpp
8+ screenwindow.cpp
89)
910
1011add_library(MockScreensPlugin MODULE ${MockScreens_SOURCES})
1112
12−qt5_use_modules(MockScreensPlugin Gui Qml)
13+qt5_use_modules(MockScreensPlugin Gui Qml Quick)
1314
1415add_unity8_mock(Unity.Screens 0.1 Unity/Screens PREFIX mocks TARGETS MockScreensPlugin)
tests/mocks/Unity/Screens/plugin.cpp+14−1
@@ -16,14 +16,27 @@
1616
1717#include "plugin.h"
1818#include "screens.h"
19+#include "screenwindow.h"
1920
2021#include <QScreen>
2122
23+namespace {
24+QObject* screensSingleton(QQmlEngine* engine, QJSEngine* scriptEngine) {
25+ Q_UNUSED(engine);
26+ Q_UNUSED(scriptEngine);
27+ return new Screens();
28+}
29+}
30+
2231void UnityScreensPlugin::registerTypes(const char* uri)
2332{
2433 Q_ASSERT(QLatin1String(uri) == QLatin1String("Unity.Screens"));
2534
2635 qRegisterMetaType<QScreen*>("QScreen*");
36+ qRegisterMetaType<ScreenMode*>("ScreenMode*");
37+ qmlRegisterUncreatableType<ScreenMode>(uri, 0, 1, "ScreenMode", "ScreenMode is not creatable.");
2738
28− qmlRegisterType<Screens>(uri, 0, 1, "Screens");
39+ qmlRegisterSingletonType<Screens>(uri, 0, 1, "Screens", screensSingleton);
40+ qmlRegisterType<ScreenWindow>(uri, 0, 1, "ScreenWindow");
41+ qmlRegisterRevision<QWindow,1>(uri, 0, 1);
2942}
tests/mocks/Unity/Screens/screens.cpp+52−9
@@ -18,16 +18,30 @@
1818
1919// Qt
2020#include <QGuiApplication>
21−#include <QScreen>
2221#include <QDebug>
2322
24−Q_DECLARE_METATYPE(QScreen*)
25−
2623Screens::Screens(QObject *parent) :
2724 QAbstractListModel(parent)
2825{
29− // start with one screen attached
30− m_screenList.append(new Screen());
26+ bool ok = false;
27+ int screenCount = qEnvironmentVariableIntValue("UNITY_MOCK_SCREEN_COUNT", &ok);
28+ if (!ok) screenCount = 1;
29+ QPoint lastPoint(0,0);
30+ for (int i = 0; i < screenCount; ++i) {
31+ auto screen = new Screen();
32+ screen->m_active = i == 0;
33+ screen->m_name = QString("Monitor %1").arg(i);
34+ screen->m_position = QPoint(lastPoint.x(), lastPoint.y());
35+ screen->m_sizes.append(new ScreenMode(50, QSize(640,480)));
36+ screen->m_sizes.append(new ScreenMode(60, QSize(1280,1024)));
37+ screen->m_sizes.append(new ScreenMode(60, QSize(1440,900)));
38+ screen->m_sizes.append(new ScreenMode(60, QSize(1920,1080)));
39+ screen->m_currentModeIndex = 3;
40+ screen->m_physicalSize = QSize(300,200);
41+ m_screenList.append(screen);
42+
43+ lastPoint.rx() += screen->m_sizes[screen->m_currentModeIndex]->size.width();
44+ }
3145}
3246
3347Screens::~Screens() noexcept
@@ -40,7 +54,6 @@ QHash<int, QByteArray> Screens::roleNames() const
4054{
4155 QHash<int, QByteArray> roles;
4256 roles[ScreenRole] = "screen";
43− roles[OutputTypeRole] = "outputType";
4457 return roles;
4558}
4659
@@ -52,9 +65,7 @@ QVariant Screens::data(const QModelIndex &index, int role) const
5265
5366 switch(role) {
5467 case ScreenRole:
55− return QVariant::fromValue(m_screenList.at(index.row())->qScreen);
56− case OutputTypeRole:
57− return m_screenList.at(index.row())->outputTypes;
68+ return QVariant::fromValue(m_screenList.at(index.row()));
5869 }
5970
6071 return QVariant();
@@ -69,3 +80,35 @@ int Screens::count() const
6980{
7081 return m_screenList.size();
7182}
83+
84+void Screens::activateScreen(int)
85+{
86+ qWarning("Not Implemented");
87+}
88+
89+Screen::Screen(QObject* parent)
90+ : QObject(parent)
91+{
92+}
93+
94+Screen::~Screen()
95+{
96+ qDeleteAll(m_sizes);
97+ m_sizes.clear();
98+}
99+
100+QQmlListProperty<ScreenMode> Screen::availableModes()
101+{
102+ return QQmlListProperty<ScreenMode>(this, m_sizes);
103+}
104+
105+Screen *Screen::beginConfiguration()
106+{
107+ qWarning("Not Implemented");
108+ return nullptr;
109+}
110+
111+void Screen::applyConfiguration()
112+{
113+ qWarning("Not Implemented");
114+}
tests/mocks/Unity/Screens/screens.h+84−4
@@ -19,6 +19,7 @@
1919
2020#include <QAbstractListModel>
2121#include <QScreen>
22+#include <QQmlListProperty>
2223
2324class Screen;
2425
@@ -31,7 +32,13 @@ class Screens : public QAbstractListModel
3132public:
3233 enum ItemRoles {
3334 ScreenRole = Qt::UserRole + 1,
34− OutputTypeRole
35+ OutputTypeRole,
36+ EnabledRole,
37+ NameRole,
38+ ScaleRole,
39+ FormFactorRole,
40+ GeometryRole,
41+ SizesRole
3542 };
3643
3744 enum OutputTypes {
@@ -53,6 +60,15 @@ public:
5360 };
5461 Q_ENUM(OutputTypes)
5562
63+ enum FormFactor {
64+ FormFactorUnknown,
65+ FormFactorPhone,
66+ FormFactorTablet,
67+ FormFactorMonitor,
68+ FormFactorTV,
69+ FormFactorProjector,
70+ };
71+
5672 explicit Screens(QObject *parent = 0);
5773 virtual ~Screens() noexcept;
5874
@@ -63,6 +79,9 @@ public:
6379
6480 int count() const;
6581
82+public Q_SLOTS:
83+ void activateScreen(int index);
84+
6685Q_SIGNALS:
6786 void countChanged();
6887 void screenAdded(QScreen *screen);
@@ -72,11 +91,72 @@ private:
7291 QList<Screen *> m_screenList;
7392};
7493
75−class Screen
94+class ScreenMode : public QObject
7695{
96+ Q_OBJECT
97+ Q_PROPERTY(qreal refreshRate MEMBER refreshRate CONSTANT)
98+ Q_PROPERTY(QSize size MEMBER size CONSTANT)
7799public:
78− Screens::OutputTypes outputTypes = Screens::Unknown;
79− QScreen *qScreen = nullptr;
100+ ScreenMode() {}
101+ ScreenMode(qreal refreshRate, QSize size):refreshRate(refreshRate),size(size) {}
102+ ScreenMode(const ScreenMode& other)
103+ : QObject(nullptr),
104+ refreshRate{other.refreshRate},size{other.size}
105+ {}
106+
107+ qreal refreshRate;
108+ QSize size;
80109};
81110
111+class Screen : public QObject
112+{
113+ Q_OBJECT
114+
115+ Q_PROPERTY(bool active MEMBER m_active NOTIFY activeChanged)
116+
117+ Q_PROPERTY(bool used MEMBER m_used NOTIFY usedChanged)
118+ Q_PROPERTY(QString name MEMBER m_name NOTIFY nameChanged)
119+ Q_PROPERTY(Screens::OutputTypes outputType MEMBER m_outputType NOTIFY outputTypeChanged)
120+ Q_PROPERTY(float scale MEMBER m_scale NOTIFY scaleChanged)
121+ Q_PROPERTY(Screens::FormFactor formFactor MEMBER m_formFactor NOTIFY formFactorChanged)
122+ Q_PROPERTY(QPoint position MEMBER m_position NOTIFY positionChanged)
123+ Q_PROPERTY(uint currentModeIndex MEMBER m_currentModeIndex NOTIFY currentModeIndexChanged)
124+ Q_PROPERTY(QQmlListProperty<ScreenMode> availableModes READ availableModes NOTIFY availableModesChanged)
125+ Q_PROPERTY(QSizeF physicalSize MEMBER m_physicalSize NOTIFY physicalSizeChanged)
126+public:
127+ Screen(QObject* parent = 0);
128+ ~Screen();
129+
130+ QQmlListProperty<ScreenMode> availableModes();
131+
132+ Q_INVOKABLE Screen* beginConfiguration();
133+ Q_INVOKABLE void applyConfiguration();
134+
135+Q_SIGNALS:
136+ void activeChanged();
137+ void usedChanged();
138+ void nameChanged();
139+ void outputTypeChanged();
140+ void scaleChanged();
141+ void formFactorChanged();
142+ void positionChanged();
143+ void currentModeIndexChanged();
144+ void availableModesChanged();
145+ void physicalSizeChanged();
146+
147+public:
148+ bool m_active{false};
149+ bool m_used{true};
150+ QString m_name;
151+ Screens::OutputTypes m_outputType{Screens::Unknown};
152+ float m_scale{1.0};
153+ Screens::FormFactor m_formFactor{Screens::FormFactorMonitor};
154+ QPoint m_position;
155+ uint m_currentModeIndex{0};
156+ QList<ScreenMode*> m_sizes;
157+ QSizeF m_physicalSize;
158+};
159+
160+Q_DECLARE_METATYPE(ScreenMode)
161+
82162#endif // SCREENS_H
tests/mocks/Unity/Screens/screenwindow.cppnew file+35
@@ -0,0 +1,35 @@
1+/*
2+ * Copyright (C) 2016 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 "screenwindow.h"
18+
19+ScreenWindow::ScreenWindow(QWindow *parent)
20+ : QQuickWindow(parent)
21+{
22+}
23+
24+Screen *ScreenWindow::screenWrapper() const
25+{
26+ return m_screen.data();
27+}
28+
29+void ScreenWindow::setScreenWrapper(Screen *screen)
30+{
31+ if (m_screen != screen) {
32+ m_screen = screen;
33+ Q_EMIT screenWrapperChanged();
34+ }
35+}
tests/mocks/Unity/Screens/screenwindow.hnew file+42
@@ -0,0 +1,42 @@
1+/*
2+ * Copyright (C) 2016 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 SCREENWINDOW_H
18+#define SCREENWINDOW_H
19+
20+#include <QQuickWindow>
21+#include <QPointer>
22+
23+#include "screens.h"
24+
25+class ScreenWindow : public QQuickWindow
26+{
27+ Q_OBJECT
28+ Q_PROPERTY(Screen *screen READ screenWrapper WRITE setScreenWrapper NOTIFY screenWrapperChanged)
29+public:
30+ ScreenWindow(QWindow *parent = 0);
31+
32+ Screen *screenWrapper() const;
33+ void setScreenWrapper(Screen *screen);
34+
35+Q_SIGNALS:
36+ void screenWrapperChanged();
37+
38+private:
39+ QPointer<Screen> m_screen;
40+};
41+
42+#endif // SCREENWINDOW_H
tests/qmltests/ApplicationMenus/tst_MenuBar.qml+2
@@ -24,6 +24,7 @@ import Unity.Test 0.1
2424import Utils 0.1
2525
2626import "../../../qml/ApplicationMenus"
27+import "../../../qml/Components/PanelState"
2728import ".."
2829
2930Item {
@@ -67,6 +68,7 @@ Item {
6768 id: menuBackend
6869 modelData: appMenuData.generateTestData(10,5,2,3)
6970 }
71+ panelState: PanelState {}
7072 }
7173 }
7274
tests/qmltests/ApplicationMenus/tst_MenuPopup.qml+2
@@ -24,6 +24,7 @@ import Unity.Test 0.1
2424import Utils 0.1
2525
2626import "../../../qml/ApplicationMenus"
27+import "../../../qml/Components/PanelState"
2728import ".."
2829
2930Item {
@@ -59,6 +60,7 @@ Item {
5960 }}
6061 ]
6162 }
63+ panelState: PanelState {}
6264 }
6365 }
6466
tests/qmltests/Components/tst_VirtualTouchPad.qml+2−2
@@ -39,11 +39,11 @@ Rectangle {
3939
4040 SignalSpy {
4141 id: mouseEventSpy1
42− target: touchScreenPad.uinput
42+ target: UInput
4343 }
4444 SignalSpy {
4545 id: mouseEventSpy2
46− target: touchScreenPad.uinput
46+ target: UInput
4747 }
4848
4949 UnityTestCase {
tests/qmltests/Panel/tst_Panel.qml+18−14
@@ -42,6 +42,8 @@ PanelTest {
4242 value: keyboardAttached.checked
4343 }
4444
45+ readonly property alias panelState: panel.panelState
46+
4547 SurfaceManager { id: sMgr }
4648 ApplicationMenuDataLoader {
4749 id: appMenuData
@@ -99,6 +101,8 @@ PanelTest {
99101 model: root.indicatorsModel
100102 hides: [ panel.applicationMenus ]
101103 }
104+
105+ panelState: PanelState {}
102106 }
103107 }
104108 }
@@ -153,7 +157,7 @@ PanelTest {
153157 Layout.fillWidth: true
154158 CheckBox {
155159 id: windowControlsCB
156− onClicked: PanelState.decorationsVisible = checked
160+ onClicked: panelState.decorationsVisible = checked
157161 }
158162 Label {
159163 text: "Show window decorations"
@@ -164,7 +168,7 @@ PanelTest {
164168 RowLayout {
165169 Layout.fillWidth: true
166170 CheckBox {
167− onClicked: PanelState.title = checked ? "Fake window title" : ""
171+ onClicked: panelState.title = checked ? "Fake window title" : ""
168172 }
169173 Label {
170174 text: "Show fake window title"
@@ -250,7 +254,7 @@ PanelTest {
250254
251255 SignalSpy {
252256 id: windowControlButtonsSpy
253− target: PanelState
257+ target: panelState
254258 signalName: "closeClicked"
255259 }
256260
@@ -260,8 +264,8 @@ PanelTest {
260264 panel.fullscreenMode = false;
261265 callManager.foregroundCall = null;
262266
263− PanelState.title = "";
264− PanelState.decorationsVisible = false;
267+ panelState.title = "";
268+ panelState.decorationsVisible = false;
265269
266270 // Wait for the indicators to get into position.
267271 // (switches between normal and fullscreen modes are animated)
@@ -456,7 +460,7 @@ PanelTest {
456460 }
457461
458462 function test_hint(data) {
459− PanelState.title = "Fake Title"
463+ panelState.title = "Fake Title"
460464 panel.fullscreenMode = data.fullscreen;
461465 callManager.foregroundCall = data.call;
462466
@@ -498,7 +502,7 @@ PanelTest {
498502 // menus, first by running the hint animation, then after dragging down will
499503 // expose more of the panel. Releasing the touch will complete the show.
500504 function test_drag_applicationMenu_down_shows_menu(data) {
501− PanelState.title = "Fake Title";
505+ panelState.title = "Fake Title";
502506 panel.fullscreenMode = data.fullscreen;
503507 callManager.foregroundCall = data.call;
504508
@@ -589,7 +593,7 @@ PanelTest {
589593 }
590594
591595 function test_darkenedAreaEatsAllApplicationMenuEvents() {
592− PanelState.title = "Fake Title"
596+ panelState.title = "Fake Title"
593597
594598 // The center of the area not covered by the indicators menu
595599 // Ie, the visible darkened area behind the menu
@@ -710,7 +714,7 @@ PanelTest {
710714 var windowControlArea = findChild(panel, "windowControlArea");
711715 verify(windowControlArea, "Window control area should have been created in windowed mode")
712716
713− PanelState.decorationsVisible = true;
717+ panelState.decorationsVisible = true;
714718 // click in very topleft corner and verify the close button got clicked too
715719 mouseMove(panel, 0, 0);
716720 mouseClick(panel, 0, 0, undefined /*button*/, undefined /*modifiers*/, 100 /*short delay*/);
@@ -773,7 +777,7 @@ PanelTest {
773777 }
774778
775779 function test_stagedApplicationMenuBarShowOnMouseHover() {
776− PanelState.title = "Fake Title";
780+ panelState.title = "Fake Title";
777781 panel.mode = "staged";
778782 mouseEmulation.checked = false;
779783
@@ -791,7 +795,7 @@ PanelTest {
791795 }
792796
793797 function test_windowedApplicationMenuShowOnMouseHoverWhenDecorationsShown() {
794− PanelState.title = "Fake Title";
798+ panelState.title = "Fake Title";
795799 panel.mode = "windowed";
796800 mouseEmulation.checked = false;
797801
@@ -807,7 +811,7 @@ PanelTest {
807811 tryCompare(appTitle, "visible", true, undefined, "App title should still be visible on mouse hover when panel decorations are not visible");
808812 tryCompare(appMenuBar, "visible", false, undefined, "App menu bar should be visible on mouse hover when panel decorations are not visible");
809813
810− PanelState.decorationsVisible = true;
814+ panelState.decorationsVisible = true;
811815
812816 tryCompare(appTitle, "visible", false, undefined, "App title should still be visible on mouse hover when panel decorations are visible");
813817 tryCompare(appMenuBar, "visible", true, undefined, "App menu bar should be visible on mouse hover when panel decorations not visible");
@@ -855,7 +859,7 @@ PanelTest {
855859
856860 var indicatorsBar = findChild(panel.applicationMenus, "indicatorsBar");
857861
858− PanelState.title = "Fake Title"
862+ panelState.title = "Fake Title"
859863 pullDownApplicationsMenu(0 /*xPos*/);
860864 compare(aboutToShowCalledSpy.count, 1);
861865
@@ -888,7 +892,7 @@ PanelTest {
888892
889893 var indicatorsBar = findChild(panel.applicationMenus, "indicatorsBar");
890894
891− PanelState.title = "Fake Title"
895+ panelState.title = "Fake Title"
892896 pullDownApplicationsMenu(0 /*xPos*/);
893897
894898 tryCompare(indicatorsBar, "currentItemIndex", 0);
tests/qmltests/Stage/tst_DecoratedWindow.qml+5−1
@@ -52,6 +52,10 @@ Rectangle {
5252 surfaceManager: sMgr
5353 }
5454
55+ PanelState {
56+ id: panelState
57+ }
58+
5559 Item {
5660 id: fakeShell
5761
@@ -99,7 +103,7 @@ Rectangle {
99103 surface: fakeApplication && fakeApplication.surfaceList.count > 0 ? fakeApplication.surfaceList.get(0) : null
100104
101105 Binding {
102− target: PanelState
106+ target: panelState
103107 property: "focusedPersistentSurfaceId"
104108 value: decoratedWindow.surface ? decoratedWindow.surface.persistentId : "x"
105109 }
tests/qmltests/Stage/tst_DesktopStage.qml+8−3
@@ -44,6 +44,10 @@ Item {
4444 value: false
4545 }
4646
47+ PanelState {
48+ id: panelState
49+ }
50+
4751 Component.onCompleted: {
4852 ApplicationMenusLimits.screenWidth = Qt.binding( function() { return stageLoader.width; } );
4953 ApplicationMenusLimits.screenHeight = Qt.binding( function() { return stageLoader.height; } );
@@ -102,6 +106,7 @@ Item {
102106 topLevelSurfaceList: topSurfaceList
103107 interactive: true
104108 mode: "windowed"
109+ panelState: panelState
105110 }
106111 }
107112 }
@@ -631,19 +636,19 @@ Item {
631636 maximizeDelegate(facebookAppDelegate);
632637
633638 // verify the drop shadow is still not visible
634− verify(PanelState.dropShadow == false);
639+ verify(panelState.dropShadow == false);
635640
636641 // start a foreground app, not maximized
637642 var dialerAppDelegate = startApplication("dialer-app");
638643
639644 // verify the drop shadow becomes visible
640− tryCompareFunction(function() { return PanelState.dropShadow; }, true);
645+ tryCompareFunction(function() { return panelState.dropShadow; }, true);
641646
642647 // close the maximized app
643648 ApplicationManager.stopApplication("facebook-webapp");
644649
645650 // verify the drop shadow is gone
646− tryCompare(PanelState, "dropShadow", false);
651+ tryCompare(panelState, "dropShadow", false);
647652 }
648653
649654 function test_threeFingerTapShowsWindowControls_data() {
tests/qmltests/Stage/tst_PhoneStage.qml+2
@@ -19,6 +19,7 @@ import QtTest 1.0
1919import Unity.Test 0.1 as UT
2020import ".."
2121import "../../../qml/Components"
22+import "../../../qml/Components/PanelState"
2223import "../../../qml/Stage"
2324import Ubuntu.Components 1.3
2425import Unity.Application 0.1
@@ -55,6 +56,7 @@ Item {
5556 Component.onCompleted: {
5657 ApplicationManager.startApplication("unity8-dash");
5758 }
59+ panelState: PanelState {}
5860 }
5961
6062 Flickable {
tests/qmltests/Stage/tst_WindowResizeArea.qml+1−8
@@ -19,7 +19,6 @@ import QtQuick.Layouts 1.1
1919import QtTest 1.0
2020import Unity.Test 0.1
2121import ".."
22−import "../../../qml/Components/PanelState"
2322import "../../../qml/Stage"
2423import Ubuntu.Components 1.3
2524import Ubuntu.Components.ListItems 1.3 as ListItem
@@ -31,12 +30,6 @@ Item {
3130 height: units.gu(60)
3231 width: units.gu(85)
3332
34− Binding {
35− target: PanelState
36− property: "panelHeight"
37− value: units.gu(3)
38− }
39−
4033 Component {
4134 id: fakeWindowComponent
4235
@@ -362,7 +355,7 @@ Item {
362355
363356 // Make sure it's again where we left it in normal state before destroying
364357 compare(fakeWindow.requestedX >= 0, true)
365− compare(fakeWindow.requestedY >= PanelState.panelHeight, true)
358+ compare(fakeWindow.requestedY >= 0, true)
366359 compare(fakeWindow.requestedX + fakeWindow.width <= root.width, true)
367360 compare(fakeWindow.requestedY + fakeWindow.height <= root.height, true)
368361
tests/qmltests/tst_OrientedShell.qml+24−5
@@ -30,7 +30,6 @@ import Utils 0.1
3030
3131import "../../qml"
3232import "../../qml/Components"
33−import "../../qml/Components/PanelState"
3433import "Stage"
3534
3635Rectangle {
@@ -82,6 +81,12 @@ Rectangle {
8281 deviceFilter: InputInfo.Keyboard
8382 }
8483
84+ QtObject {
85+ id: _screenWindow
86+ property bool primary: true
87+ }
88+ property alias screenWindow: _screenWindow
89+
8590 property int physicalOrientation0
8691 property int physicalOrientation90
8792 property int physicalOrientation180
@@ -174,6 +179,11 @@ Rectangle {
174179 Component.onDestruction: {
175180 orientedShellLoader.itemDestroyed = true;
176181 }
182+
183+ SurfaceManager {
184+ id: surfaceMan
185+ }
186+ surfaceManager: surfaceMan
177187 }
178188 }
179189 }
@@ -446,6 +456,15 @@ Rectangle {
446456 property Item orientedShell: orientedShellLoader.status === Loader.Ready ? orientedShellLoader.item : null
447457 property Item shell
448458 property QtObject topLevelSurfaceList
459+ property var panelState: undefined
460+
461+ onOrientedShellChanged: {
462+ if (orientedShell) {
463+ panelState = findInvisibleChild(orientedShell, "panelState");
464+ } else {
465+ panelState = undefined;
466+ }
467+ }
449468
450469 SignalSpy { id: signalSpy }
451470 SignalSpy { id: signalSpy2 }
@@ -1512,13 +1531,13 @@ Rectangle {
15121531 print("exptectedAngle", expectedAngle, point.x, point.y)
15131532 switch (expectedAngle) {
15141533 case 0:
1515− return point.x === 0 && point.y === PanelState.panelHeight;
1534+ return point.x === 0 && point.y === panelState.panelHeight;
15161535 case 90:
1517− return point.x === orientedShell.width - PanelState.panelHeight && point.y === 0;
1536+ return point.x === orientedShell.width - panelState.panelHeight && point.y === 0;
15181537 case 180:
1519− return point.x === orientedShell.width && point.y === orientedShell.height - PanelState.panelHeight;
1538+ return point.x === orientedShell.width && point.y === orientedShell.height - panelState.panelHeight;
15201539 default: // 270
1521− return point.x === PanelState.panelHeight && point.y === orientedShell.height;
1540+ return point.x === panelState.panelHeight && point.y === orientedShell.height;
15221541 }
15231542 }
15241543
tests/qmltests/tst_Shell.qml+30−17
@@ -38,7 +38,6 @@ import Unity.Indicators 0.1 as Indicators
3838
3939import "../../qml"
4040import "../../qml/Components"
41−import "../../qml/Components/PanelState"
4241import "Stage"
4342
4443Rectangle {
@@ -61,14 +60,23 @@ Rectangle {
6160 onShellChanged: {
6261 if (shell) {
6362 topLevelSurfaceList = testCase.findInvisibleChild(shell, "topLevelSurfaceList");
64− appMenuData.surfaceManager = testCase.findInvisibleChild(shell, "surfaceManager");
63+ appMenuData.surfaceManager = shell.surfaceManager;
64+ panelState = testCase.findInvisibleChild(shell, "panelState");
6565 } else {
6666 topLevelSurfaceList = null;
6767 appMenuData.surfaceManager = null;
68+ panelState = null;
6869 }
6970 }
7071
7172 property var topLevelSurfaceList: null
73+ property var panelState: null
74+
75+ QtObject {
76+ id: _screenWindow
77+ property bool primary: true
78+ }
79+ property alias screenWindow: _screenWindow
7280
7381 Item {
7482 id: shellContainer
@@ -144,6 +152,11 @@ Rectangle {
144152 Component.onDestruction: {
145153 shellLoader.itemDestroyed = true;
146154 }
155+
156+ SurfaceManager {
157+ id: surfaceMan
158+ }
159+ surfaceManager: surfaceMan
147160 }
148161 }
149162 }
@@ -678,7 +691,7 @@ Rectangle {
678691 }
679692
680693 function ensureInputMethodSurface() {
681− var surfaceManager = findInvisibleChild(shell, "surfaceManager");
694+ var surfaceManager = shell.surfaceManager;
682695 verify(surfaceManager);
683696 surfaceManager.createInputMethodSurface();
684697
@@ -1866,21 +1879,21 @@ Rectangle {
18661879 var maximizeButton = findChild(appDelegate, "maximizeWindowButton");
18671880
18681881 tryCompare(appDelegate, "state", "normal");
1869− tryCompare(PanelState, "decorationsVisible", false)
1882+ tryCompare(panelState, "decorationsVisible", false)
18701883
18711884 mouseClick(maximizeButton, maximizeButton.width / 2, maximizeButton.height / 2);
18721885 tryCompare(appDelegate, "state", "maximized");
1873− tryCompare(PanelState, "decorationsVisible", true)
1886+ tryCompare(panelState, "decorationsVisible", true)
18741887
18751888 ApplicationManager.stopApplication(application.appId);
1876− tryCompare(PanelState, "decorationsVisible", false)
1889+ tryCompare(panelState, "decorationsVisible", false)
18771890
18781891 // wait until all zombie surfaces are gone. As MirSurfaceItems hold references over them.
18791892 // They won't be gone until those surface items are destroyed.
18801893 tryCompareFunction(function() { return application.surfaceList.count }, 0);
18811894
18821895 ApplicationManager.startApplication(application.appId);
1883− tryCompare(PanelState, "decorationsVisible", true)
1896+ tryCompare(panelState, "decorationsVisible", true)
18841897 }
18851898
18861899 function test_newAppHasValidGeometry() {
@@ -1937,7 +1950,7 @@ Rectangle {
19371950 mousePress(appDelegate, appDelegate.width / 2, units.gu(1))
19381951 mouseMove(appDelegate, appDelegate.width / 2, -units.gu(100))
19391952
1940− compare(appDelegate.y >= PanelState.panelHeight, true);
1953+ compare(appDelegate.y >= panelState.panelHeight, true);
19411954 }
19421955
19431956 function test_cantResizeWindowUnderPanel() {
@@ -1965,7 +1978,7 @@ Rectangle {
19651978 mouseMove(decoration, decoration.width/2, -units.gu(100));
19661979
19671980 // verify we don't go past the panel
1968− compare(appDelegate.y >= PanelState.panelHeight, true);
1981+ compare(appDelegate.y >= panelState.panelHeight, true);
19691982 }
19701983
19711984 function test_restoreWindowStateFixesIfUnderPanel() {
@@ -1989,7 +2002,7 @@ Rectangle {
19892002 tryCompareFunction(function () { return topLevelSurfaceList.applicationAt(0).appId; }, application.appId);
19902003
19912004 appDelegate = appRepeater.itemAt(0);
1992− compare(appDelegate.y >= PanelState.panelHeight, true);
2005+ compare(appDelegate.y >= panelState.panelHeight, true);
19932006 }
19942007
19952008 function test_lifecyclePolicyForNonTouchApp_data() {
@@ -2652,9 +2665,9 @@ Rectangle {
26522665
26532666 function test_oskDisplacesWindow_data() {
26542667 return [
2655− {tag: "no need to displace", windowHeight: units.gu(10), windowY: units.gu(5), targetDisplacement: units.gu(5), oskEnabled: true},
2656− {tag: "displace to top", windowHeight: units.gu(50), windowY: units.gu(10), targetDisplacement: PanelState.panelHeight, oskEnabled: true},
2657− {tag: "osk not on this screen", windowHeight: units.gu(40), windowY: units.gu(10), targetDisplacement: units.gu(10), oskEnabled: false},
2668+ {tag: "no need to displace", windowHeight: units.gu(10), windowY: units.gu(5), targetDisplacement: function() { return units.gu(5); }, oskEnabled: true},
2669+ {tag: "displace to top", windowHeight: units.gu(50), windowY: units.gu(10), targetDisplacement: function() { return panelState.panelHeight; }, oskEnabled: true},
2670+ {tag: "osk not on this screen", windowHeight: units.gu(40), windowY: units.gu(10), targetDisplacement: function() { return units.gu(10); }, oskEnabled: false},
26582671 ]
26592672 }
26602673
@@ -2676,11 +2689,11 @@ Rectangle {
26762689 dashAppDelegate.windowedY = data.windowY;
26772690 topLevelSurfaceList.inputMethodSurface.setInputBounds(Qt.rect(0, 0, 0, 0));
26782691 var initialY = dashAppDelegate.y;
2679− print("intial", initialY, "panel", PanelState.panelHeight);
2680− verify(initialY > PanelState.panelHeight);
2692+ print("intial", initialY, "panel", panelState.panelHeight);
2693+ verify(initialY > panelState.panelHeight);
26812694
26822695 topLevelSurfaceList.inputMethodSurface.setInputBounds(Qt.rect(0, root.height / 2, root.width, root.height / 2));
2683− tryCompare(dashAppDelegate, "y", data.targetDisplacement);
2696+ tryCompare(dashAppDelegate, "y", data.targetDisplacement());
26842697
26852698 topLevelSurfaceList.inputMethodSurface.setInputBounds(Qt.rect(0, 0, 0, 0));
26862699 tryCompare(dashAppDelegate, "y", initialY);
@@ -3113,7 +3126,7 @@ Rectangle {
31133126 // double click the panel
31143127 var panel = findChild(shell, "panel");
31153128 verify(panel);
3116− mouseDoubleClickSequence(panel, panel.width/2, PanelState.panelHeight/2, Qt.LeftButton, Qt.NoModifier, 300);
3129+ mouseDoubleClickSequence(panel, panel.width/2, panelState.panelHeight/2, Qt.LeftButton, Qt.NoModifier, 300);
31173130 tryCompare(appDelegate, "state", "restored");
31183131 }
31193132
tests/qmltests/tst_ShellWithPin.qml+7−13
@@ -41,20 +41,10 @@ Item {
4141 }
4242
4343 QtObject {
44− id: applicationArguments
45−
46− function hasGeometry() {
47− return false;
48− }
49−
50− function width() {
51− return 0;
52− }
53−
54− function height() {
55− return 0;
56− }
44+ id: _screenWindow
45+ property bool primary: true
5746 }
47+ property alias screenWindow: _screenWindow
5848
5949 Row {
6050 id: contentRow
@@ -73,6 +63,10 @@ Item {
7363 Component.onDestruction: {
7464 shellLoader.itemDestroyed = true
7565 }
66+ SurfaceManager {
67+ id: surfaceMan
68+ }
69+ surfaceManager: surfaceMan
7670 }
7771 }
7872 }
tests/uqmlscene/main.cpp+2
@@ -479,6 +479,8 @@ int main(int argc, char ** argv)
479479 // TODO: as soon as the engine construction completes, the debug service is
480480 // listening for connections. But actually we aren't ready to debug anything.
481481 QQmlEngine engine;
482+ engine.rootContext()->setContextProperty("DebuggingController", new DebuggingController(&app));
483+
482484 QQmlComponent *component = new QQmlComponent(&engine);
483485 for (int i = 0; i < imports.size(); ++i)
484486 engine.addImportPath(imports.at(i));