on 795acac help sign in
39 files changed+694−203
since splitsnapshots
unifiedsplit
plugins/Cursor/CMakeLists.txt+1 plugins/Cursor/InputDispatcherFilter.cpp+143 plugins/Cursor/InputDispatcherFilter.h+57 plugins/Cursor/MousePointer.cpp+40−73 plugins/Cursor/MousePointer.h+4 plugins/UInput/plugin.cpp+7−1 qml/Components/PanelState/PanelState.qml−1 qml/Components/PanelState/qmldir+1−1 qml/Components/VirtualTouchPad.qml+13−14 qml/Greeter/Greeter.qml+21−3 qml/Greeter/SecondaryGreeter.qml+75 qml/Panel/Panel.qml+11−10 qml/Shell.qml+23−15 qml/ShellNotifier.qml+31 qml/Stage/DecoratedWindow.qml+4 qml/Stage/FakeMaximizeDelegate.qml+12−11 qml/Stage/MoveHandler.qml+8−7 qml/Stage/Stage.qml+26−24 qml/Stage/WindowControlsOverlay.qml+2 qml/qmldir+1 tests/mocks/Cursor/Cursor.qml+50−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/InputInfo/mockcontroller.cpp+6 tests/mocks/Unity/InputInfo/mockcontroller.h+2 tests/mocks/Unity/InputInfo/plugin.cpp+11−1 tests/mocks/Unity/InputInfo/plugin.h+1 tests/mocks/Unity/Screens/CMakeLists.txt+2−1 tests/mocks/Unity/Screens/plugin.cpp+3 tests/mocks/Unity/Screens/screens.cpp+6−2 tests/mocks/Unity/Screens/screenwindow.cpp+32 tests/mocks/Unity/Screens/screenwindow.h+36 tests/qmltests/Components/tst_VirtualTouchPad.qml+2−2 tests/qmltests/Panel/tst_Panel.qml+9−8 tests/qmltests/Stage/tst_DesktopStage.qml+8−3 tests/qmltests/Stage/tst_WindowResizeArea.qml+1−8 tests/qmltests/tst_OrientedShell.qml+13−5 tests/qmltests/tst_Shell.qml+13−10
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+143
@@ -0,0 +1,143 @@
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 <QScreen>
23+#include <qpa/qplatformnativeinterface.h>
24+#include <qpa/qplatformscreen.h>
25+
26+InputDispatcherFilter *InputDispatcherFilter::instance()
27+{
28+ static InputDispatcherFilter filter;
29+ return &filter;
30+}
31+
32+InputDispatcherFilter::InputDispatcherFilter(QObject *parent)
33+ : QObject(parent)
34+{
35+ QPlatformNativeInterface *ni = QGuiApplication::platformNativeInterface();
36+ m_inputDispatcher = static_cast<QObject*>(ni->nativeResourceForIntegration("InputDispatcher"));
37+ if (m_inputDispatcher) {
38+ m_inputDispatcher->installEventFilter(this);
39+ }
40+}
41+
42+void InputDispatcherFilter::registerPointer(MousePointer *pointer)
43+{
44+ // allow first registered pointer to be visible.
45+ pointer->setVisible(m_pointers.count() == 0);
46+
47+ m_pointers.insert(pointer);
48+ connect(pointer, &MousePointer::mouseMoved, this, [this, pointer]() {
49+ Q_FOREACH(auto p, m_pointers) {
50+ p->setVisible(p == pointer);
51+ }
52+ });
53+}
54+
55+void InputDispatcherFilter::unregisterPointer(MousePointer *pointer)
56+{
57+ m_pointers.remove(pointer);
58+ disconnect(pointer, &MousePointer::mouseMoved, this, 0);
59+}
60+
61+void InputDispatcherFilter::setPosition(const QPointF &pos)
62+{
63+ mousePosition = pos;
64+}
65+
66+bool InputDispatcherFilter::eventFilter(QObject *o, QEvent *e)
67+{
68+ if (o != m_inputDispatcher) return false;
69+
70+ switch (e->type()) {
71+ case QEvent::MouseMove:
72+ case QEvent::MouseButtonPress:
73+ case QEvent::MouseButtonRelease:
74+ {
75+ QMouseEvent* me = static_cast<QMouseEvent*>(e);
76+
77+ // Local position gives relative change of mouse pointer.
78+ QPointF localPos = me->localPos();
79+ QPointF globalPos = me->screenPos();
80+
81+ // Adjust the position
82+ QPointF oldPos(mousePosition.isNull() ? globalPos : mousePosition);
83+ QPointF newPos = adjustedPositionForMovement(oldPos, localPos);
84+
85+ QScreen* currentScreen = screenAt(newPos);
86+ if (currentScreen) {
87+ QRect screenRect = currentScreen->geometry();
88+ qreal unadjustedX = (oldPos + localPos).x();
89+ if (unadjustedX < screenRect.left()) {
90+ Q_EMIT pushedLeftBoundary(currentScreen, qAbs(unadjustedX - screenRect.left()), me->buttons());
91+ } else if (unadjustedX > screenRect.right()) {
92+ Q_EMIT pushedRightBoundary(currentScreen, qAbs(unadjustedX - screenRect.right()), me->buttons());
93+ }
94+ }
95+
96+ // Send the event
97+ QMouseEvent eCopy(me->type(), me->localPos(), newPos, me->button(), me->buttons(), me->modifiers());
98+ eCopy.setTimestamp(me->timestamp());
99+ o->event(&eCopy);
100+ return true;
101+ }
102+ default:
103+ break;
104+ }
105+ return false;
106+}
107+
108+QPointF InputDispatcherFilter::adjustedPositionForMovement(const QPointF &pt, const QPointF &movement) const
109+{
110+ QPointF adjusted = pt + movement;
111+
112+ auto screen = screenAt(adjusted); // first check if our move was to a valid screen.
113+ if (screen) {
114+ return adjusted;
115+ } else if ((screen = screenAt(pt))) { // then check if our old position was valid
116+ QRectF screenBounds = screen->geometry();
117+ // bound the new position to the old screen geometry
118+ adjusted.rx() = qMax(screenBounds.left(), qMin(adjusted.x(), screenBounds.right()-1));
119+ adjusted.ry() = qMax(screenBounds.top(), qMin(adjusted.y(), screenBounds.bottom()-1));
120+ } else {
121+ auto screens = QGuiApplication::screens();
122+
123+ // center of first screen with a pointer.
124+ Q_FOREACH(QScreen* screen, screens) {
125+ Q_FOREACH(MousePointer* pointer, m_pointers) {
126+ if (pointer->screen() == screen) {
127+ return screen->geometry().center();
128+ }
129+ }
130+ }
131+ }
132+ return adjusted;
133+}
134+
135+QScreen *InputDispatcherFilter::screenAt(const QPointF &pt) const
136+{
137+ Q_FOREACH(MousePointer* pointer, m_pointers) {
138+ QScreen* screen = pointer->screen();
139+ if (screen && screen->geometry().contains(pt.toPoint()))
140+ return screen;
141+ }
142+ return nullptr;
143+}
plugins/Cursor/InputDispatcherFilter.hnew file+57
@@ -0,0 +1,57 @@
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+
35+ void unregisterPointer(MousePointer* pointer);
36+
37+ void setPosition(const QPointF &pos);
38+
39+Q_SIGNALS:
40+ void pushedLeftBoundary(QScreen* screen, qreal amount, Qt::MouseButtons buttons);
41+ void pushedRightBoundary(QScreen* screen, qreal amount, Qt::MouseButtons buttons);
42+
43+protected:
44+ InputDispatcherFilter(QObject* parent = nullptr);
45+
46+ bool eventFilter(QObject *o, QEvent *e) override;
47+
48+ QPointF adjustedPositionForMovement(const QPointF& pt, const QPointF& movement) const;
49+ QScreen* screenAt(const QPointF& pt) const;
50+
51+private:
52+ QObject* m_inputDispatcher;
53+ QSet<MousePointer*> m_pointers;
54+ QPointF mousePosition;
55+};
56+
57+#endif // INPUTDISPATCHERFILTER_H
plugins/Cursor/MousePointer.cpp+40−73
@@ -16,84 +16,57 @@
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−}
31+ InputDispatcherFilter::instance()->registerPointer(this);
3532
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()) {
33+ auto mouseMovedFunc = [this]() {
34+ if (!isEnabled() || !window()) return;
35+ QPointF globalPosition = mapToItem(nullptr, QPointF(0, 0));
36+ InputDispatcherFilter::instance()->setPosition(globalPosition);
4437 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) {
84− Q_EMIT pushStopped();
85− m_pushing = false;
38+ };
39+ connect(this, &QQuickItem::xChanged, this, mouseMovedFunc);
40+ connect(this, &QQuickItem::yChanged, this, mouseMovedFunc);
41+
42+ connect(this, &QQuickItem::enabledChanged, this, [this]() {
43+ if (!isEnabled()) setVisible(false);
44+ });
45+
46+ connect(InputDispatcherFilter::instance(), &InputDispatcherFilter::pushedLeftBoundary,
47+ this, [this](QScreen* screen, qreal amount, Qt::MouseButtons buttons) {
48+ if (window() && window()->screen() == screen) {
49+ Q_EMIT pushedLeftBoundary(amount, buttons);
8650 }
87− }
51+ });
8852
89− applyItemConfinement(newX, newY);
53+ connect(InputDispatcherFilter::instance(), &InputDispatcherFilter::pushedRightBoundary,
54+ this, [this](QScreen* screen, qreal amount, Qt::MouseButtons buttons) {
55+ if (window() && window()->screen() == screen) {
56+ Q_EMIT pushedRightBoundary(amount, buttons);
57+ }
58+ });
59+}
9060
91− setX(qBound(0.0, newX, sceneWidth - 1));
92− setY(qBound(0.0, newY, sceneHeight - 1));
61+MousePointer::~MousePointer()
62+{
63+ registerScreen(nullptr);
64+ InputDispatcherFilter::instance()->unregisterPointer(this);
65+}
9366
94− QPointF scenePosition = mapToItem(nullptr, QPointF(0, 0));
95− QWindowSystemInterface::handleMouseEvent(window(), timestamp, scenePosition /*local*/, scenePosition /*global*/,
96− buttons, modifiers);
67+void MousePointer::handleMouseEvent(ulong /*timestamp*/, QPointF /*movement*/, Qt::MouseButtons /*buttons*/,
68+ Qt::KeyboardModifiers /*modifiers*/)
69+{
9770}
9871
9972void MousePointer::applyItemConfinement(qreal &newX, qreal &newY)
@@ -121,15 +94,8 @@ void MousePointer::applyItemConfinement(qreal &newX, qreal &newY)
12194 }
12295}
12396
124−void MousePointer::handleWheelEvent(ulong timestamp, QPoint angleDelta, Qt::KeyboardModifiers modifiers)
97+void MousePointer::handleWheelEvent(ulong /*timestamp*/, QPoint /*angleDelta*/, Qt::KeyboardModifiers /*modifiers*/)
12598{
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);
13399}
134100
135101int MousePointer::topBoundaryOffset() const
@@ -182,7 +148,7 @@ void MousePointer::registerScreen(QScreen *screen)
182148 if (m_registeredScreen) {
183149 auto previousCursor = dynamic_cast<MirPlatformCursor*>(m_registeredScreen->handle()->cursor());
184150 if (previousCursor) {
185− previousCursor->setMousePointer(nullptr);
151+ previousCursor->unregisterMousePointer(this);
186152 } else {
187153 qCritical("QPlatformCursor is not a MirPlatformCursor! Cursor module only works in a Mir server.");
188154 }
@@ -193,7 +159,7 @@ void MousePointer::registerScreen(QScreen *screen)
193159 if (m_registeredScreen) {
194160 auto cursor = dynamic_cast<MirPlatformCursor*>(m_registeredScreen->handle()->cursor());
195161 if (cursor) {
196− cursor->setMousePointer(this);
162+ cursor->registerMousePointer(this);
197163 } else {
198164 qCritical("QPlaformCursor is not a MirPlatformCursor! Cursor module only works in Mir.");
199165 }
@@ -233,3 +199,4 @@ void MousePointer::setConfiningItem(QQuickItem *item)
233199 Q_EMIT confiningItemChanged();
234200 }
235201}
202+
plugins/Cursor/MousePointer.h+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; }
@@ -46,6 +47,8 @@ public:
4647 int topBoundaryOffset() const;
4748 void setTopBoundaryOffset(int topBoundaryOffset);
4849
50+ QScreen* screen() const { return m_registeredScreen; }
51+
4952public Q_SLOTS:
5053 void handleMouseEvent(ulong timestamp, QPointF movement, Qt::MouseButtons buttons,
5154 Qt::KeyboardModifiers modifiers) override;
@@ -90,3 +93,4 @@ private:
9093};
9194
9295#endif // MOUSEPOINTER_H
96+
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/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−14
@@ -24,10 +24,9 @@ import "../Components"
2424
2525Item {
2626 id: root
27− property var uinput: UInput {
28− Component.onCompleted: createMouse();
29− Component.onDestruction: removeMouse();
30− }
27+
28+ Component.onCompleted: UInput.createMouse()
29+ Component.onDestruction: UInput.removeMouse()
3130
3231 Component.onCompleted: {
3332 if (!settings.touchpadTutorialHasRun) {
@@ -85,7 +84,7 @@ Item {
8584 if (((point1.pressed && !point2.pressed) || (!point1.pressed && point2.pressed))
8685 && clickTimer.running) {
8786 clickTimer.stop();
88− uinput.pressMouse(UInput.ButtonLeft)
87+ UInput.pressMouse(UInput.ButtonLeft)
8988 isDoubleClick = true;
9089 }
9190 isClick = true;
@@ -104,7 +103,7 @@ Item {
104103
105104 onReleased: {
106105 if (isDoubleClick || isDrag) {
107− uinput.releaseMouse(UInput.ButtonLeft)
106+ UInput.releaseMouse(UInput.ButtonLeft)
108107 isDoubleClick = false;
109108 }
110109 if (isClick) {
@@ -120,8 +119,8 @@ Item {
120119 interval: 200
121120 property int button: UInput.ButtonLeft
122121 onTriggered: {
123− uinput.pressMouse(button);
124− uinput.releaseMouse(button);
122+ UInput.pressMouse(button);
123+ UInput.releaseMouse(button);
125124 }
126125 function scheduleClick(button) {
127126 clickTimer.button = button;
@@ -138,7 +137,7 @@ Item {
138137 isDrag = true;
139138 }
140139
141− uinput.moveMouse(tp.x - tp.previousX, tp.y - tp.previousY);
140+ UInput.moveMouse(tp.x - tp.previousX, tp.y - tp.previousY);
142141 }
143142
144143 function scroll(touchPoints) {
@@ -166,7 +165,7 @@ Item {
166165 dh /= 2;
167166 dv /= 2;
168167
169− uinput.scrollMouse(dh, dv);
168+ UInput.scrollMouse(dh, dv);
170169 }
171170
172171 touchPoints: [
@@ -189,8 +188,8 @@ Item {
189188 objectName: "leftButton"
190189 Layout.fillWidth: true
191190 Layout.fillHeight: true
192− onPressed: uinput.pressMouse(UInput.ButtonLeft);
193− onReleased: uinput.releaseMouse(UInput.ButtonLeft);
191+ onPressed: UInput.pressMouse(UInput.ButtonLeft);
192+ onReleased: UInput.releaseMouse(UInput.ButtonLeft);
194193 property bool highlight: false
195194 UbuntuShape {
196195 anchors.fill: parent
@@ -204,8 +203,8 @@ Item {
204203 objectName: "rightButton"
205204 Layout.fillWidth: true
206205 Layout.fillHeight: true
207− onPressed: uinput.pressMouse(UInput.ButtonRight);
208− onReleased: uinput.releaseMouse(UInput.ButtonRight);
206+ onPressed: UInput.pressMouse(UInput.ButtonRight);
207+ onReleased: UInput.releaseMouse(UInput.ButtonRight);
209208 property bool highlight: false
210209 UbuntuShape {
211210 anchors.fill: parent
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 {
@@ -234,7 +235,7 @@ Showable {
234235 if (forcedUnlock && shown) {
235236 hideView();
236237 if (hideNow) {
237− root.hideNow(); // skip hide animation
238+ ShellNotifier.greeter.hide(true); // skip hide animation
238239 }
239240 }
240241 }
@@ -397,7 +398,7 @@ Showable {
397398 onEmergencyCall: root.emergencyCall()
398399 onRequiredChanged: {
399400 if (!loader.item.required) {
400− root.hide();
401+ ShellNotifier.greeter.hide(false);
401402 }
402403 }
403404 }
@@ -529,12 +530,29 @@ Showable {
529530 onRequestAuthenticationUser: d.selectUser(d.getUserIndex(user), true)
530531 }
531532
533+ Connections {
534+ target: ShellNotifier.greeter
535+ onHide: {
536+ if (now) {
537+ root.hideNow(); // skip hide animation
538+ } else {
539+ root.hide();
540+ }
541+ }
542+ }
543+
544+ Binding {
545+ target: ShellNotifier.greeter
546+ property: "shown"
547+ value: root.shown
548+ }
549+
532550 Connections {
533551 target: DBusUnitySessionService
534552 onLockRequested: root.forceShow()
535553 onUnlocked: {
536554 root.forcedUnlock = true;
537− root.hideNow();
555+ ShellNotifier.greeter.hide(true);
538556 }
539557 }
540558
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/Panel/Panel.qml+11−10
@@ -29,6 +29,7 @@ Item {
2929 property bool fullscreenMode: false
3030 property real indicatorAreaShowProgress: 1.0
3131 property bool locked: false
32+ property PanelState panelState
3233
3334 MouseArea {
3435 anchors.fill: parent
@@ -40,7 +41,7 @@ Item {
4041 }
4142
4243 Binding {
43− target: PanelState
44+ target: panelState
4445 property: "panelHeight"
4546 value: indicators.minimizedPanelHeight
4647 }
@@ -74,7 +75,7 @@ Item {
7475 fill: indicatorAreaBackground
7576 bottomMargin: -units.gu(1)
7677 }
77− visible: PanelState.dropShadow && !callHint.visible
78+ visible: panelState.dropShadow && !callHint.visible
7879 source: "graphics/rectangular_dropshadow.sci"
7980 }
8081
@@ -120,14 +121,14 @@ Item {
120121 }
121122 height: indicators.minimizedPanelHeight
122123
123− visible: ((PanelState.buttonsVisible && parent.containsMouse) || PanelState.buttonsAlwaysVisible)
124+ visible: ((panelState.buttonsVisible && parent.containsMouse) || panelState.buttonsAlwaysVisible)
124125 && !root.locked && !callHint.visible
125− active: PanelState.buttonsVisible || PanelState.buttonsAlwaysVisible
126+ active: panelState.buttonsVisible || panelState.buttonsAlwaysVisible
126127 windowIsMaximized: true
127− onCloseClicked: PanelState.closeClicked()
128− onMinimizeClicked: PanelState.minimizeClicked()
129− onMaximizeClicked: PanelState.restoreClicked()
130− closeButtonShown: PanelState.closeButtonShown
128+ onCloseClicked: panelState.closeClicked()
129+ onMinimizeClicked: panelState.minimizeClicked()
130+ onMaximizeClicked: panelState.restoreClicked()
131+ closeButtonShown: panelState.closeButtonShown
131132 }
132133 }
133134
@@ -181,8 +182,8 @@ Item {
181182 visible: opacity != 0
182183 verticalAlignment: Text.AlignVCenter
183184 fontSize: "medium"
184− font.weight: PanelState.buttonsVisible ? Font.Light : Font.Medium
185− text: PanelState.title
185+ font.weight: panelState.buttonsVisible ? Font.Light : Font.Medium
186+ text: panelState.title
186187 elide: Text.ElideRight
187188 maximumLineCount: 1
188189 Behavior on opacity { UbuntuNumberAnimation {} }
qml/Shell.qml+23−15
@@ -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
@@ -249,7 +250,6 @@ StyledItem {
249250 var mappedCoords = mapFromItem(null, pos.x, pos.y);
250251 cursor.x = mappedCoords.x;
251252 cursor.y = mappedCoords.y;
252− cursor.mouseNeverMoved = false;
253253 }
254254 }
255255
@@ -258,6 +258,11 @@ StyledItem {
258258 schema.id: "com.canonical.Unity8"
259259 }
260260
261+ PanelState {
262+ id: panelState
263+ objectName: "panelState"
264+ }
265+
261266 Item {
262267 id: stages
263268 objectName: "stages"
@@ -313,6 +318,7 @@ StyledItem {
313318 altTabPressed: physicalKeysMapper.altTabPressed
314319 oskEnabled: shell.oskEnabled
315320 spreadEnabled: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
321+ panelState: panelState
316322 }
317323 }
318324
@@ -333,8 +339,13 @@ StyledItem {
333339 objectName: "greeterLoader"
334340 anchors.fill: parent
335341 anchors.topMargin: panel.panelHeight
336− sourceComponent: shell.mode != "shell" ? integratedGreeter :
337− Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
342+ sourceComponent: {
343+ if (shell.mode != "shell") {
344+ if (screenWindow.primary) return integratedGreeter;
345+ return secondaryGreeter;
346+ }
347+ return Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
348+ }
338349 onLoaded: {
339350 item.objectName = "greeter"
340351 }
@@ -374,6 +385,13 @@ StyledItem {
374385 }
375386 }
376387
388+ Component {
389+ id: secondaryGreeter
390+ SecondaryGreeter {
391+ hides: [launcher, panel.indicators]
392+ }
393+ }
394+
377395 Timer {
378396 // See powerConnection for why this is useful
379397 id: showGreeterDelayed
@@ -479,6 +497,7 @@ StyledItem {
479497 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0)
480498 || greeter.hasLockedApp
481499 locked: greeter && greeter.active
500+ panelState: panelState
482501 }
483502
484503 Launcher {
@@ -689,23 +708,13 @@ StyledItem {
689708
690709 Cursor {
691710 id: cursor
692− objectName: "cursor"
693711 visible: shell.hasMouse
694712 z: itemGrabber.z + 1
713+ opacity: 0
695714 topBoundaryOffset: panel.panelHeight
696715
697716 confiningItem: stage.itemConfiningMouseCursor
698717
699− property bool mouseNeverMoved: true
700− Binding {
701− target: cursor; property: "x"; value: shell.width / 2
702− when: cursor.mouseNeverMoved && cursor.visible
703− }
704− Binding {
705− target: cursor; property: "y"; value: shell.height / 2
706− when: cursor.mouseNeverMoved && cursor.visible
707− }
708−
709718 height: units.gu(3)
710719
711720 readonly property var previewRectangle: stage.previewRectangle.target &&
@@ -760,7 +769,6 @@ StyledItem {
760769 }
761770
762771 onMouseMoved: {
763− mouseNeverMoved = false;
764772 cursor.opacity = 1;
765773 }
766774
qml/ShellNotifier.qmlnew file+31
@@ -0,0 +1,31 @@
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+/*
18+ Shared signals & properties on multi-window desktop
19+ */
20+
21+pragma Singleton
22+import QtQuick 2.4
23+
24+QtObject {
25+ property var greeter: QtObject {
26+ signal show()
27+ signal hide(bool now)
28+
29+ property bool shown: false
30+ }
31+}
qml/Stage/DecoratedWindow.qml+4
@@ -18,6 +18,7 @@ import QtQuick 2.4
1818import Ubuntu.Components 1.3
1919import Unity.Application 0.1
2020import "Spread/MathUtils.js" as MathUtils
21+import "../Components/PanelState"
2122
2223FocusScope {
2324 id: root
@@ -70,6 +71,8 @@ FocusScope {
7071
7172 readonly property Item clientAreaItem: applicationWindow
7273
74+ property PanelState panelState
75+
7376 signal closeClicked()
7477 signal maximizeClicked()
7578 signal maximizeHorizontallyClicked()
@@ -179,6 +182,7 @@ FocusScope {
179182 objectName: "moveHandler"
180183 target: root.parent
181184 buttonsWidth: decoration.buttonsWidth
185+ panelState: root.panelState
182186 }
183187
184188 ApplicationWindow {
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/MoveHandler.qml+8−7
@@ -28,6 +28,7 @@ QtObject {
2828 property int stageWidth
2929 property int stageHeight
3030 property real buttonsWidth: 0
31+ property PanelState panelState
3132
3233 readonly property bool dragging: priv.dragging
3334
@@ -122,18 +123,18 @@ QtObject {
122123 // position. Mouse movement could have subpixel precision, yielding a fractional
123124 // mouse position.
124125 target.windowedX = Math.round(pos.x - priv.distanceX);
125− target.windowedY = Math.round(Math.max(pos.y - priv.distanceY, PanelState.panelHeight));
126+ target.windowedY = Math.round(Math.max(pos.y - priv.distanceY, panelState.panelHeight));
126127
127128 if (sensingPoints) { // edge/corner detection when dragging via the touch overlay
128− if (sensingPoints.topLeft.x < priv.triggerArea && sensingPoints.topLeft.y < PanelState.panelHeight + priv.triggerArea
129+ if (sensingPoints.topLeft.x < priv.triggerArea && sensingPoints.topLeft.y < panelState.panelHeight + priv.triggerArea
129130 && target.canBeCornerMaximized) { // top left
130− priv.progress = priv.progressInCorner(0, PanelState.panelHeight, sensingPoints.topLeft.x, sensingPoints.topLeft.y);
131+ priv.progress = priv.progressInCorner(0, panelState.panelHeight, sensingPoints.topLeft.x, sensingPoints.topLeft.y);
131132 priv.resetEdges();
132133 priv.nearTopLeftCorner = true;
133134 root.fakeMaximizeTopLeftAnimationRequested(priv.progress);
134− } else if (sensingPoints.topRight.x > stageWidth - priv.triggerArea && sensingPoints.topRight.y < PanelState.panelHeight + priv.triggerArea
135+ } else if (sensingPoints.topRight.x > stageWidth - priv.triggerArea && sensingPoints.topRight.y < panelState.panelHeight + priv.triggerArea
135136 && target.canBeCornerMaximized) { // top right
136− priv.progress = priv.progressInCorner(stageWidth, PanelState.panelHeight, sensingPoints.topRight.x, sensingPoints.topRight.y);
137+ priv.progress = priv.progressInCorner(stageWidth, panelState.panelHeight, sensingPoints.topRight.x, sensingPoints.topRight.y);
137138 priv.resetEdges();
138139 priv.nearTopRightCorner = true;
139140 root.fakeMaximizeTopRightAnimationRequested(priv.progress);
@@ -159,8 +160,8 @@ QtObject {
159160 priv.resetEdges();
160161 priv.nearRightEdge = true;
161162 root.fakeMaximizeRightAnimationRequested(priv.progress);
162− } else if (sensingPoints.top.y < PanelState.panelHeight + priv.triggerArea && target.canBeMaximized) { // top
163− priv.progress = MathUtils.clampAndProject(sensingPoints.top.y, PanelState.panelHeight + priv.triggerArea, 0, 0, 1);
163+ } else if (sensingPoints.top.y < panelState.panelHeight + priv.triggerArea && target.canBeMaximized) { // top
164+ priv.progress = MathUtils.clampAndProject(sensingPoints.top.y, panelState.panelHeight + priv.triggerArea, 0, 0, 1);
164165 priv.resetEdges();
165166 priv.nearTopEdge = true;
166167 root.fakeMaximizeAnimationRequested(priv.progress);
qml/Stage/Stage.qml+26−24
@@ -46,6 +46,7 @@ FocusScope {
4646 property int leftMargin: 0
4747 property bool oskEnabled: false
4848 property rect inputMethodRect
49+ property PanelState panelState
4950
5051 // Configuration
5152 property string mode: "staged"
@@ -348,20 +349,20 @@ FocusScope {
348349 Component.onCompleted: priv.updateMainAndSideStageIndexes();
349350
350351 Connections {
351− target: PanelState
352+ target: panelState
352353 onCloseClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.close(); } }
353354 onMinimizeClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.requestMinimize(); } }
354355 onRestoreClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.requestRestore(); } }
355356 }
356357
357358 Binding {
358− target: PanelState
359+ target: panelState
359360 property: "buttonsVisible"
360361 value: priv.focusedAppDelegate !== null && priv.focusedAppDelegate.maximized // FIXME for Locally integrated menus
361362 }
362363
363364 Binding {
364− target: PanelState
365+ target: panelState
365366 property: "title"
366367 value: {
367368 if (priv.focusedAppDelegate !== null) {
@@ -376,21 +377,21 @@ FocusScope {
376377 }
377378
378379 Binding {
379− target: PanelState
380+ target: panelState
380381 property: "dropShadow"
381382 value: priv.focusedAppDelegate && !priv.focusedAppDelegate.maximized && priv.foregroundMaximizedAppDelegate !== null && mode == "windowed"
382383 }
383384
384385 Binding {
385− target: PanelState
386+ target: panelState
386387 property: "closeButtonShown"
387388 value: priv.focusedAppDelegate && priv.focusedAppDelegate.maximized && !priv.focusedAppDelegate.isDash
388389 }
389390
390391 Component.onDestruction: {
391− PanelState.title = "";
392− PanelState.buttonsVisible = false;
393− PanelState.dropShadow = false;
392+ panelState.title = "";
393+ panelState.buttonsVisible = false;
394+ panelState.dropShadow = false;
394395 }
395396
396397 Instantiator {
@@ -739,7 +740,7 @@ FocusScope {
739740 target: appDelegate
740741 property: "y"
741742 value: appDelegate.requestedY -
742− Math.min(appDelegate.requestedY - PanelState.panelHeight,
743+ Math.min(appDelegate.requestedY - panelState.panelHeight,
743744 Math.max(0, priv.virtualKeyboardHeight - (appContainer.height - (appDelegate.requestedY + appDelegate.height))))
744745 when: root.oskEnabled && appDelegate.focus && (appDelegate.state == "normal" || appDelegate.state == "restored")
745746 && root.inputMethodRect.height > 0
@@ -876,7 +877,7 @@ FocusScope {
876877 screenWidth: appContainer.width
877878 screenHeight: appContainer.height
878879 leftMargin: root.leftMargin
879− minimumY: PanelState.panelHeight
880+ minimumY: panelState.panelHeight
880881 }
881882
882883 Connections {
@@ -1116,7 +1117,7 @@ FocusScope {
11161117 progress: 0
11171118 targetHeight: spreadItem.stackHeight
11181119 targetX: spreadMaths.targetX
1119− startY: appDelegate.fullscreen ? 0 : PanelState.panelHeight
1120+ startY: appDelegate.fullscreen ? 0 : panelState.panelHeight
11201121 targetY: spreadMaths.targetY
11211122 targetAngle: spreadMaths.targetAngle
11221123 targetScale: spreadMaths.targetScale
@@ -1235,9 +1236,9 @@ FocusScope {
12351236 PropertyChanges {
12361237 target: appDelegate
12371238 x: stageMaths.itemX
1238− y: appDelegate.fullscreen ? 0 : PanelState.panelHeight
1239+ y: appDelegate.fullscreen ? 0 : panelState.panelHeight
12391240 requestedWidth: appContainer.width
1240− requestedHeight: appDelegate.fullscreen ? appContainer.height : appContainer.height - PanelState.panelHeight
1241+ requestedHeight: appDelegate.fullscreen ? appContainer.height : appContainer.height - panelState.panelHeight
12411242 visuallyMaximized: true
12421243 visible: appDelegate.x < root.width
12431244 }
@@ -1263,10 +1264,10 @@ FocusScope {
12631264 PropertyChanges {
12641265 target: appDelegate
12651266 x: stageMaths.itemX
1266− y: appDelegate.fullscreen ? 0 : PanelState.panelHeight
1267+ y: appDelegate.fullscreen ? 0 : panelState.panelHeight
12671268 z: stageMaths.itemZ
12681269 requestedWidth: stageMaths.itemWidth
1269− requestedHeight: appDelegate.fullscreen ? appContainer.height : appContainer.height - PanelState.panelHeight
1270+ requestedHeight: appDelegate.fullscreen ? appContainer.height : appContainer.height - panelState.panelHeight
12701271 visuallyMaximized: true
12711272 visible: appDelegate.x < root.width
12721273 }
@@ -1331,9 +1332,9 @@ FocusScope {
13311332 PropertyChanges {
13321333 target: appDelegate
13331334 windowedX: root.leftMargin
1334− windowedY: PanelState.panelHeight
1335+ windowedY: panelState.panelHeight
13351336 windowedWidth: (appContainer.width - root.leftMargin)/2
1336− windowedHeight: appContainer.height - PanelState.panelHeight
1337+ windowedHeight: appContainer.height - panelState.panelHeight
13371338 }
13381339 },
13391340 State {
@@ -1350,9 +1351,9 @@ FocusScope {
13501351 PropertyChanges {
13511352 target: appDelegate
13521353 windowedX: root.leftMargin
1353− windowedY: PanelState.panelHeight
1354+ windowedY: panelState.panelHeight
13541355 windowedWidth: (appContainer.width - root.leftMargin)/2
1355− windowedHeight: (appContainer.height - PanelState.panelHeight)/2
1356+ windowedHeight: (appContainer.height - panelState.panelHeight)/2
13561357 }
13571358 },
13581359 State {
@@ -1369,7 +1370,7 @@ FocusScope {
13691370 PropertyChanges {
13701371 target: appDelegate
13711372 windowedX: root.leftMargin
1372− windowedY: (appContainer.height + PanelState.panelHeight)/2
1373+ windowedY: (appContainer.height + panelState.panelHeight)/2
13731374 windowedWidth: (appContainer.width - root.leftMargin)/2
13741375 windowedHeight: appContainer.height/2
13751376 }
@@ -1391,8 +1392,8 @@ FocusScope {
13911392 State {
13921393 name: "maximizedVertically"; when: appDelegate.maximizedVertically && !appDelegate.minimized
13931394 extend: "normal"
1394− PropertyChanges { target: appDelegate; windowedX: windowedX; windowedY: PanelState.panelHeight;
1395− windowedWidth: windowedWidth; windowedHeight: appContainer.height - PanelState.panelHeight }
1395+ PropertyChanges { target: appDelegate; windowedX: windowedX; windowedY: panelState.panelHeight;
1396+ windowedWidth: windowedWidth; windowedHeight: appContainer.height - panelState.panelHeight }
13961397 },
13971398 State {
13981399 name: "minimized"; when: appDelegate.minimized
@@ -1491,7 +1492,7 @@ FocusScope {
14911492 ]
14921493
14931494 Binding {
1494− target: PanelState
1495+ target: panelState
14951496 property: "buttonsAlwaysVisible"
14961497 value: appDelegate && appDelegate.maximized && touchControls.overlayShown
14971498 }
@@ -1506,7 +1507,7 @@ FocusScope {
15061507 anchors.margins: touchControls.overlayShown ? borderThickness/2 : -borderThickness
15071508
15081509 target: appDelegate
1509− minimumY: PanelState.panelHeight // disallow resizing up past Panel
1510+ minimumY: panelState.panelHeight // disallow resizing up past Panel
15101511 minWidth: units.gu(10)
15111512 minHeight: units.gu(10)
15121513 borderThickness: units.gu(2)
@@ -1699,6 +1700,7 @@ FocusScope {
16991700 leftMargin: root.leftMargin
17001701 appContainerWidth: appContainer.width
17011702 appContainerHeight: appContainer.height
1703+ panelState: root.panelState
17021704 }
17031705
17041706 EdgeBarrier {
qml/Stage/WindowControlsOverlay.qml+2
@@ -27,6 +27,7 @@ Item {
2727
2828 // to be set from outside
2929 property Item target // appDelegate
30+ property PanelState panelState
3031 property alias stageWidth: moveHandler.stageWidth
3132 property alias stageHeight: moveHandler.stageHeight
3233
@@ -161,6 +162,7 @@ Item {
161162 id: moveHandler
162163 objectName: "moveHandler"
163164 target: root.target
165+ panelState: root.panelState
164166
165167 onFakeMaximizeAnimationRequested: root.fakeMaximizeAnimationRequested(amount)
166168 onFakeMaximizeLeftAnimationRequested: root.fakeMaximizeLeftAnimationRequested(amount)
qml/qmldirnew file+1
@@ -0,0 +1 @@
1+singleton ShellNotifier 0.1 ShellNotifier.qml
tests/mocks/Cursor/Cursor.qml+50−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,50 @@ 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}
82+
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/InputInfo/mockcontroller.cpp+6
@@ -24,6 +24,12 @@ MockController::MockController(QObject *parent):
2424
2525}
2626
27+MockController *MockController::instance()
28+{
29+ static MockController* controller = new MockController;
30+ return controller;
31+}
32+
2733QInputDevice *MockController::addMockDevice(const QString &devicePath, QInputDevice::InputType type)
2834{
2935 return QInputDeviceManagerPrivate::instance()->addMockDevice(devicePath, type);
tests/mocks/Unity/InputInfo/mockcontroller.h+2
@@ -27,6 +27,8 @@ public:
2727 MockController(QObject *parent = 0);
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+11−1
@@ -28,7 +28,7 @@ static QObject *backendProvider(QQmlEngine *engine, QJSEngine *scriptEngine)
2828{
2929 Q_UNUSED(engine)
3030 Q_UNUSED(scriptEngine)
31− return new MockController(engine);
31+ return MockController::instance();
3232}
3333
3434void InputInfoPlugin::registerTypes(const char *uri)
@@ -44,3 +44,13 @@ void InputInfoPlugin::registerTypes(const char *uri)
4444 // MockController
4545 qmlRegisterSingletonType<MockController>(uri, major, minor, "MockInputDeviceBackend", backendProvider);
4646}
47+
48+void InputInfoPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
49+{
50+ QQmlExtensionPlugin::initializeEngine(engine, uri);
51+
52+ if (qEnvironmentVariableIsSet("UNITY_MOCK_DESKTOP")) {
53+ MockController::instance()->addMockDevice("/mouse0", QInputDevice::Mouse);
54+ MockController::instance()->addMockDevice("/kbd0", QInputDevice::Keyboard);
55+ }
56+}
tests/mocks/Unity/InputInfo/plugin.h+1
@@ -25,6 +25,7 @@ class InputInfoPlugin : public QQmlExtensionPlugin
2525 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
2626public:
2727 void registerTypes(const char *uri) override;
28+ void initializeEngine(QQmlEngine *engine, const char *uri);
2829};
2930
3031#endif // INPUTINFO_PLUGIN_H
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+3
@@ -16,6 +16,7 @@
1616
1717#include "plugin.h"
1818#include "screens.h"
19+#include "screenwindow.h"
1920
2021#include <QScreen>
2122
@@ -26,4 +27,6 @@ void UnityScreensPlugin::registerTypes(const char* uri)
2627 qRegisterMetaType<QScreen*>("QScreen*");
2728
2829 qmlRegisterType<Screens>(uri, 0, 1, "Screens");
30+ qmlRegisterType<ScreenWindow>(uri, 0, 1, "ScreenWindow");
31+ qmlRegisterRevision<QWindow,1>(uri, 0, 1);
2932}
tests/mocks/Unity/Screens/screens.cpp+6−2
@@ -26,8 +26,12 @@ Q_DECLARE_METATYPE(QScreen*)
2626Screens::Screens(QObject *parent) :
2727 QAbstractListModel(parent)
2828{
29− // start with one screen attached
30− m_screenList.append(new Screen());
29+ bool ok = false;
30+ int screenCount = qEnvironmentVariableIntValue("UNITY_MOCK_SCREEN_COUNT", &ok);
31+ if (!ok) screenCount = 1;
32+ for (int i = 0; i < screenCount; ++i) {
33+ m_screenList.append(new Screen());
34+ }
3135}
3236
3337Screens::~Screens()
tests/mocks/Unity/Screens/screenwindow.cppnew file+32
@@ -0,0 +1,32 @@
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+QScreen *ScreenWindow::screen() const
25+{
26+ return QQuickWindow::screen();
27+}
28+
29+void ScreenWindow::setScreen(QScreen *screen)
30+{
31+ QQuickWindow::setScreen(screen);
32+}
tests/mocks/Unity/Screens/screenwindow.hnew file+36
@@ -0,0 +1,36 @@
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+
22+class ScreenWindow : public QQuickWindow
23+{
24+ Q_OBJECT
25+ Q_PROPERTY(QScreen *screen READ screen WRITE setScreen NOTIFY screenChanged)
26+public:
27+ ScreenWindow(QWindow *parent = 0);
28+
29+ QScreen *screen() const;
30+ void setScreen(QScreen *screen);
31+
32+Q_SIGNALS:
33+ void screenChanged(QScreen *screen);
34+};
35+
36+#endif // SCREENWINDOW_H
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+9−8
@@ -23,7 +23,6 @@ import Unity.Application 0.1
2323import Unity.Indicators 0.1 as Indicators
2424import Ubuntu.Telephony 0.1 as Telephony
2525import "../../../qml/Panel"
26−import "../../../qml/Components/PanelState"
2726
2827IndicatorTest {
2928 id: root
@@ -33,6 +32,8 @@ IndicatorTest {
3332
3433 Component.onCompleted: theme.name = "Ubuntu.Components.Themes.SuruDark"
3534
35+ readonly property alias panelState: panel.panelState
36+
3637 SurfaceManager {}
3738
3839 Binding {
@@ -109,7 +110,7 @@ IndicatorTest {
109110 Layout.fillWidth: true
110111 CheckBox {
111112 id: windowControlsCB
112− onClicked: PanelState.buttonsVisible = checked
113+ onClicked: panelState.buttonsVisible = checked
113114 }
114115 Label {
115116 text: "Show window controls"
@@ -121,9 +122,9 @@ IndicatorTest {
121122 CheckBox {
122123 onClicked: {
123124 if (checked)
124− PanelState.title = "Fake window title"
125+ panelState.title = "Fake window title"
125126 else
126− PanelState.title = ""
127+ panelState.title = ""
127128 }
128129 }
129130 Label {
@@ -186,7 +187,7 @@ IndicatorTest {
186187
187188 SignalSpy {
188189 id: windowControlButtonsSpy
189− target: PanelState
190+ target: panelState
190191 signalName: "closeClicked"
191192 }
192193
@@ -510,13 +511,13 @@ IndicatorTest {
510511
511512 // https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1611959
512513 function test_windowControlButtonsFittsLaw() {
513− var buttonsVisible = PanelState.buttonsVisible;
514− PanelState.buttonsVisible = true;
514+ var buttonsVisible = panelState.buttonsVisible;
515+ panelState.buttonsVisible = true;
515516 // click in very topleft corner and verify the close button got clicked too
516517 mouseMove(panel, 0, 0);
517518 mouseClick(panel, 0, 0, undefined /*button*/, undefined /*modifiers*/, 100 /*short delay*/);
518519 compare(windowControlButtonsSpy.count, 1);
519− PanelState.buttonsVisible = buttonsVisible;
520+ panelState.buttonsVisible = buttonsVisible;
520521 }
521522 }
522523}
tests/qmltests/Stage/tst_DesktopStage.qml+8−3
@@ -41,6 +41,10 @@ Item {
4141 value: false
4242 }
4343
44+ PanelState {
45+ id: panelState
46+ }
47+
4448 Component.onCompleted: {
4549 theme.name = "Ubuntu.Components.Themes.SuruDark";
4650 resetGeometry();
@@ -92,6 +96,7 @@ Item {
9296 topLevelSurfaceList: topSurfaceList
9397 interactive: true
9498 mode: "windowed"
99+ panelState: panelState
95100 }
96101 }
97102
@@ -624,19 +629,19 @@ Item {
624629 maximizeDelegate(facebookAppDelegate);
625630
626631 // verify the drop shadow is still not visible
627− verify(PanelState.dropShadow == false);
632+ verify(panelState.dropShadow == false);
628633
629634 // start a foreground app, not maximized
630635 var dialerAppDelegate = startApplication("dialer-app");
631636
632637 // verify the drop shadow becomes visible
633− verify(PanelState.dropShadow == true);
638+ verify(panelState.dropShadow == true);
634639
635640 // close the maximized app
636641 ApplicationManager.stopApplication("facebook-webapp");
637642
638643 // verify the drop shadow is gone
639− tryCompare(PanelState, "dropShadow", false);
644+ tryCompare(panelState, "dropShadow", false);
640645 }
641646
642647 function test_threeFingerTapShowsWindowControls_data() {
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
@@ -352,7 +345,7 @@ Item {
352345
353346 // Make sure it's again where we left it in normal state before destroying
354347 compare(fakeWindow.requestedX >= 0, true)
355− compare(fakeWindow.requestedY >= PanelState.panelHeight, true)
348+ compare(fakeWindow.requestedY >= 0, true)
356349 compare(fakeWindow.requestedX + fakeWindow.width <= root.width, true)
357350 compare(fakeWindow.requestedY + fakeWindow.height <= root.height, true)
358351
tests/qmltests/tst_OrientedShell.qml+13−5
@@ -29,7 +29,6 @@ import Utils 0.1
2929
3030import "../../qml"
3131import "../../qml/Components"
32−import "../../qml/Components/PanelState"
3332import "Stage"
3433
3534Rectangle {
@@ -439,6 +438,15 @@ Rectangle {
439438 property Item orientedShell: orientedShellLoader.status === Loader.Ready ? orientedShellLoader.item : null
440439 property Item shell
441440 property QtObject topLevelSurfaceList
441+ property var panelState: undefined
442+
443+ onOrientedShellChanged: {
444+ if (orientedShell) {
445+ panelState = findInvisibleChild(shell, "panelState");
446+ } else {
447+ panelState = undefined;
448+ }
449+ }
442450
443451 SignalSpy { id: signalSpy }
444452 SignalSpy { id: signalSpy2 }
@@ -1507,13 +1515,13 @@ Rectangle {
15071515 print("exptectedAngle", expectedAngle, point.x, point.y)
15081516 switch (expectedAngle) {
15091517 case 0:
1510− return point.x === 0 && point.y === PanelState.panelHeight;
1518+ return point.x === 0 && point.y === panelState.panelHeight;
15111519 case 90:
1512− return point.x === orientedShell.width - PanelState.panelHeight && point.y === 0;
1520+ return point.x === orientedShell.width - panelState.panelHeight && point.y === 0;
15131521 case 180:
1514− return point.x === orientedShell.width && point.y === orientedShell.height - PanelState.panelHeight;
1522+ return point.x === orientedShell.width && point.y === orientedShell.height - panelState.panelHeight;
15151523 default: // 270
1516− return point.x === PanelState.panelHeight && point.y === orientedShell.height;
1524+ return point.x === panelState.panelHeight && point.y === orientedShell.height;
15171525 }
15181526 }
15191527
tests/qmltests/tst_Shell.qml+13−10
@@ -55,12 +55,15 @@ Rectangle {
5555 onShellChanged: {
5656 if (shell) {
5757 topLevelSurfaceList = testCase.findInvisibleChild(shell, "topLevelSurfaceList");
58+ panelState = testCase.findInvisibleChild(shell, "panelState");
5859 } else {
5960 topLevelSurfaceList = null;
61+ panelState = undefined;
6062 }
6163 }
6264
6365 property var topLevelSurfaceList: null
66+ property var panelState: undefined
6467
6568 Item {
6669 id: shellContainer
@@ -1878,21 +1881,21 @@ Rectangle {
18781881 var maximizeButton = findChild(appDelegate, "maximizeWindowButton");
18791882
18801883 tryCompare(appDelegate, "state", "normal");
1881− tryCompare(PanelState, "buttonsVisible", false)
1884+ tryCompare(panelState, "buttonsVisible", false)
18821885
18831886 mouseClick(maximizeButton, maximizeButton.width / 2, maximizeButton.height / 2);
18841887 tryCompare(appDelegate, "state", "maximized");
1885− tryCompare(PanelState, "buttonsVisible", true)
1888+ tryCompare(panelState, "buttonsVisible", true)
18861889
18871890 ApplicationManager.stopApplication(application.appId);
1888− tryCompare(PanelState, "buttonsVisible", false)
1891+ tryCompare(panelState, "buttonsVisible", false)
18891892
18901893 // wait until all zombie surfaces are gone. As MirSurfaceItems hold references over them.
18911894 // They won't be gone until those surface items are destroyed.
18921895 tryCompareFunction(function() { return application.surfaceList.count }, 0);
18931896
18941897 ApplicationManager.startApplication(application.appId);
1895− tryCompare(PanelState, "buttonsVisible", true)
1898+ tryCompare(panelState, "buttonsVisible", true)
18961899 }
18971900
18981901 function test_newAppHasValidGeometry() {
@@ -1948,7 +1951,7 @@ Rectangle {
19481951 mousePress(appDelegate, appDelegate.width / 2, units.gu(1))
19491952 mouseMove(appDelegate, appDelegate.width / 2, -units.gu(100))
19501953
1951− compare(appDelegate.y >= PanelState.panelHeight, true);
1954+ compare(appDelegate.y >= panelState.panelHeight, true);
19521955 }
19531956
19541957 function test_cantResizeWindowUnderPanel() {
@@ -1976,7 +1979,7 @@ Rectangle {
19761979 mouseMove(decoration, decoration.width/2, -units.gu(100));
19771980
19781981 // verify we don't go past the panel
1979− compare(appDelegate.y >= PanelState.panelHeight, true);
1982+ compare(appDelegate.y >= panelState.panelHeight, true);
19801983 }
19811984
19821985 function test_restoreWindowStateFixesIfUnderPanel() {
@@ -2000,7 +2003,7 @@ Rectangle {
20002003 tryCompareFunction(function () { return topLevelSurfaceList.applicationAt(0).appId; }, application.appId);
20012004
20022005 appDelegate = appRepeater.itemAt(0);
2003− compare(appDelegate.y >= PanelState.panelHeight, true);
2006+ compare(appDelegate.y >= panelState.panelHeight, true);
20042007 }
20052008
20062009 function test_lifecyclePolicyForNonTouchApp_data() {
@@ -2601,7 +2604,7 @@ Rectangle {
26012604 function test_oskDisplacesWindow_data() {
26022605 return [
26032606 {tag: "no need to displace", windowHeight: units.gu(10), windowY: units.gu(5), targetDisplacement: units.gu(5), oskEnabled: true},
2604− {tag: "displace to top", windowHeight: units.gu(50), windowY: units.gu(10), targetDisplacement: PanelState.panelHeight, oskEnabled: true},
2607+ {tag: "displace to top", windowHeight: units.gu(50), windowY: units.gu(10), targetDisplacement: panelState.panelHeight, oskEnabled: true},
26052608 {tag: "osk not on this screen", windowHeight: units.gu(40), windowY: units.gu(10), targetDisplacement: units.gu(10), oskEnabled: false},
26062609 ]
26072610 }
@@ -2624,8 +2627,8 @@ Rectangle {
26242627 dashAppDelegate.windowedY = data.windowY;
26252628 topLevelSurfaceList.inputMethodSurface.setInputBounds(Qt.rect(0, 0, 0, 0));
26262629 var initialY = dashAppDelegate.y;
2627− print("intial", initialY, "panel", PanelState.panelHeight);
2628− verify(initialY > PanelState.panelHeight);
2630+ print("intial", initialY, "panel", panelState.panelHeight);
2631+ verify(initialY > panelState.panelHeight);
26292632
26302633 topLevelSurfaceList.inputMethodSurface.setInputBounds(Qt.rect(0, root.height / 2, root.width, root.height / 2));
26312634 tryCompare(dashAppDelegate, "y", data.targetDisplacement);