on xenial help sign in
merge prereq
commitd323b7b959309f43cafac11bb2f89917bf375c3d authorMichael Zanetti <michael.zanetti@canonical.com> dateMon 13 Mar 2017, 15:52 (10 years ago) parent016093cff6e4b5
71 files changed+1349−303
build.sh+16 data/com.canonical.Unity8.gschema.xml+11−1 data/unity8-greeter-wrapper+7−4 data/unity8.conf+19−35 debian/changelog+56 plugins/Utils/URLDispatcher.cpp+5−2 plugins/Utils/WindowInputMonitor.cpp+3 plugins/WindowManager/TopLevelWindowModel.cpp+1 po/unity8.pot+7−3 po/update-unity-pot+17 qml/ApplicationMenus/MenuBar.qml+49−24 qml/ApplicationMenus/MenuItem.qml+6−6 qml/ApplicationMenus/MenuPopup.qml+75−73 qml/Components/KeyboardShortcutsOverlay.qml+1−1 qml/Components/WindowControlButtons.qml−21 qml/Greeter/GreeterPrompt.qml+29−10 qml/Launcher/Launcher.qml+3 qml/Launcher/LauncherPanel.qml+3−1 qml/Launcher/MoreAppsHeader.qml+16 qml/Launcher/graphics/home.svg+243 qml/Launcher/graphics/home@18.png qml/OrientedShell.qml+8−6 qml/Panel/Panel.qml+6−11 qml/Panel/PanelMenuPage.qml+1−1 qml/Shell.qml+4−2 qml/Stage/ApplicationWindow.qml+1 qml/Stage/ChildWindow.qml+34−1 qml/Stage/DecoratedWindow.qml+7 qml/Stage/MoveHandler.qml+13 qml/Stage/Stage.qml+23−11 qml/Stage/StageMaths.qml+16 qml/Stage/StagedFullscreenPolicy.qml+5−11 qml/Stage/WindowDecoration.qml+12−14 qml/Stage/WindowInfoItem.qml+16 qml/Stage/WindowResizeArea.qml+12 qml/Stage/WindowStateSaver.qml+4−3 qml/Stage/WindowedFullscreenPolicy.qml+4−12 src/ShellView.cpp+22 tests/CMakeLists.txt+1 tests/copyright/check_copyright.sh+33−4 tests/mocks/Biometryd/MockOperation.h−1 tests/mocks/Unity/Application/ApplicationManager.cpp+1 tests/mocks/Unity/Indicators/fakeindicatorsmodeldata.js+16 tests/mocks/liblightdm/MockController.cpp+2−1 tests/plugins/Utils/WindowInputMonitorTest.cpp+31−1 tests/qmltests/ApplicationMenuDataLoader.qml+16 tests/qmltests/ApplicationMenus/tst_MenuBar.qml+3 tests/qmltests/Greeter/tst_WideView.qml+4 tests/qmltests/Launcher/tst_Drawer.qml+17 tests/qmltests/Panel/tst_Panel.qml+6−3 tests/qmltests/Stage/tst_DesktopStage.qml+47−3 tests/qmltests/Tutorial/tst_Tutorial.qml+1 tests/qmltests/tst_OrientedShell.qml+17−3 tests/qmltests/tst_Shell.qml+76−22 tests/qmltests/tst_ShellWithPin.qml+1 tests/scripts/alert-launcher-icon.sh+16 tests/scripts/get-progress.sh+16 tests/scripts/list-launcher-icons.sh+16 tests/scripts/set-count-visible.sh+16 tests/scripts/set-count.sh+16 tests/scripts/set-progress.sh+16 tools/CMakeLists.txt+2−12 tools/menutool/CMakeLists.txt+5 tools/menutool/README+11 tools/menutool/menutool.cpp+62 tools/menutool/menutool.qml+118 tools/scopetool/CMakeLists.txt+12 tools/registry-tracker.cpp → tools/scopetool/registry-tracker.cpp tools/registry-tracker.h → tools/scopetool/registry-tracker.h tools/scopetool.cpp → tools/scopetool/scopetool.cpp tools/unlock-device+16
build.sh+16
@@ -1,5 +1,21 @@
11#!/bin/sh
22
3+#
4+# Copyright (C) 2017 Canonical Ltd
5+#
6+# This program is free software: you can redistribute it and/or modify
7+# it under the terms of the GNU General Public License version 3 as
8+# published by the Free Software Foundation.
9+#
10+# This program is distributed in the hope that it will be useful,
11+# but WITHOUT ANY WARRANTY; without even the implied warranty of
12+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+# GNU General Public License for more details.
14+#
15+# You should have received a copy of the GNU General Public License
16+# along with this program. If not, see <http://www.gnu.org/licenses/>.
17+#
18+
319SETUP=false
420CLEAN=false
521NUM_JOBS=$(( `grep -c ^processor /proc/cpuinfo` + 1 ))
data/com.canonical.Unity8.gschema.xml+11−1
@@ -33,7 +33,7 @@
3333 <description>How big (in grid units) the edge-drag sensitive area should be.</description>
3434 </key>
3535 <key type="b" name="autohide-launcher">
36− <default>true</default>
36+ <default>false</default>
3737 <summary>Autohide the launcher</summary>
3838 <description>This will only be applied in windowed mode. In staged mode, the launcher will always hide.</description>
3939 </key>
@@ -58,6 +58,16 @@
5858 <summary>The uri to the app store</summary>
5959 <description>This will be used whenever the user triggers an action to open the app store.</description>
6060 </key>
61+ <key type="b" name="always-show-osk">
62+ <default>false</default>
63+ <summary>Force showing/hiding the OSK</summary>
64+ <description>Toggle the visibility of the On Screen Keyboard unconditionally</description>
65+ </key>
66+ <key type="b" name="osk-switch-visible">
67+ <default>false</default>
68+ <summary>Whether the OSK switch should be visible</summary>
69+ <description>Toggle the visibility of the OSK switch</description>
70+ </key>
6171 </schema>
6272
6373 <schema path="/com/canonical/unity8/greeter/" id="com.canonical.Unity8.Greeter" gettext-domain="unity8">
data/unity8-greeter-wrapper+7−4
@@ -49,9 +49,12 @@ if which ubuntu-touch-session >/dev/null; then
4949 TOUCH_WRAPPER=ubuntu-touch-session
5050fi
5151
52−# Pretend that we're in an ubuntu-touch session, so that indicator-network
53−# will start.
54−export DESKTOP_SESSION=ubuntu-touch
52+# Advertise as a unity8 session, so that indicator-network will start.
53+export XDG_SESSION_DESKTOP=unity8
54+export DESKTOP_SESSION=$XDG_SESSION_DESKTOP
55+
56+set_greeter_var QT_IM_MODULE maliitphablet
57+set_greeter_var QT_QPA_PLATFORM ubuntumirclient
5558
5659# We disable ofono using pulse. It causes problems with racing with the user's
5760# pulse. We need to come up with a better long-term fix for this, because we
@@ -100,7 +103,7 @@ if [ "$(id -u phablet 2>/dev/null)" = "32011" ]; then
100103fi
101104
102105# And finally actually start the greeter
103−exec env QT_QPA_PLATFORM=ubuntumirclient MIR_SERVER_FILE=$SUB_SOCKET $TOUCH_WRAPPER $@ &
106+exec env MIR_SERVER_FILE=$SUB_SOCKET $TOUCH_WRAPPER $@ &
104107CMD_PID=$!
105108wait $CMD_PID
106109CMD_PID=
data/unity8.conf+19−35
@@ -20,28 +20,32 @@ env QML_NO_TOUCH_COMPRESSION=1
2020respawn
2121expect stop
2222
23+# FIXME Create an autopilot test that makes sure start+stop and restart
24+# work fine at least 2 times
25+
2326pre-start script
2427 # Tell unity-mir to raise SIGSTOP after we start
2528 initctl set-env UNITY_MIR_EMITS_SIGSTOP=1
2629
27− if [ -n "$MIR_SOCKET" ]; then
28− # FIXME Create an autopilot test that makes sure start+stop and restart
29− # work fine at least 2 times
30− # Save original value of MIR_SOCKET in case we are stopped,
31− # as we modify the variable for future jobs, including ourself.
32− UNITY_MIR_SOCKET=$(initctl get-env --global MIR_SOCKET)
33− initctl set-env --global UNITY_MIR_SOCKET=$UNITY_MIR_SOCKET
30+ # Save original value of MIR_SOCKET so that we can restore it when stopped,
31+ # as we modify the variable for future jobs, including ourself.
32+ initctl set-env UNITY_MIR_SOCKET=$MIR_SOCKET
3433
35− # Point unity8 at unity-system-compositor
34+ # Decide where we are going to put our own socket file
35+ if [ -n "$SNAP" ]; then
36+ # In snappy, we want to hard code our path because our apparmor profile
37+ # only lets us put the socket in one place. And consumers expect it
38+ # there.
39+ MIR_SERVER_FILE=/run/user/$(id -u)/mir_socket
40+ else
3641 MIR_SERVER_FILE=$XDG_RUNTIME_DIR/mir_socket
37− initctl set-env MIR_SERVER_FILE=$MIR_SERVER_FILE
38− initctl set-env MIR_SERVER_HOST_SOCKET=$UNITY_MIR_SOCKET
39−
40− # Point all future jobs in this session to our Mir socket instead of
41− # unity-system-compositor's socket.
42− initctl set-env --global MIR_SOCKET=$MIR_SERVER_FILE
43− gdbus call --session --dest org.freedesktop.DBus --object-path /org/freedesktop/DBus --method org.freedesktop.DBus.UpdateActivationEnvironment "@a{ss} {'MIR_SOCKET': '$MIR_SERVER_FILE'}"
4442 fi
43+ initctl set-env MIR_SERVER_FILE=$MIR_SERVER_FILE
44+
45+ # Point all future jobs in this session to our Mir socket instead of
46+ # unity-system-compositor's socket.
47+ initctl set-env --global MIR_SOCKET=$MIR_SERVER_FILE
48+ gdbus call --session --dest org.freedesktop.DBus --object-path /org/freedesktop/DBus --method org.freedesktop.DBus.UpdateActivationEnvironment "@a{ss} {'MIR_SOCKET': '$MIR_SERVER_FILE'}"
4549
4650 # Remove the normal and trusted sockets if still there
4751 if [ -S "$MIR_SERVER_FILE" ]; then
@@ -54,12 +58,6 @@ pre-start script
5458
5559 initctl set-env --global MIR_SERVER_PROMPT_FILE=1
5660
57− #
58− # Mir's input resampling is deprecated and going away soon. Turn it off
59− # so we don't get a surprise change in behaviour when that happens.
60− #
61− initctl set-env --global MIR_CLIENT_INPUT_RATE=0
62−
6361 if [ "$XDG_SESSION_DESKTOP" = "ubuntu-touch" ]; then
6462 # On Ubuntu Touch, we currently use a trick where we auto-login the
6563 # user without a LightDM greeter at all. In this case, we want to
@@ -74,25 +72,11 @@ end script
7472exec ${BINARY:-unity8} --mode=$MODE $ARGS
7573
7674post-start script
77− # If no MIR_SOCKET was defined on launch, we try to set one here
78− if [ -z "$(initctl get-env --global UNITY_MIR_SOCKET)" ]; then
79− if [ -S "$XDG_RUNTIME_DIR/mir_socket" ]; then
80− mir_socket=$XDG_RUNTIME_DIR/mir_socket
81− elif [ -S "/tmp/mir_socket" ]; then
82− mir_socket=/tmp/mir_socket
83− fi
84−
85− if [ -n "$mir_socket" ]; then
86− initctl set-env --global MIR_SOCKET="$mir_socket"
87− fi
88− fi
89−
9075 initctl emit --no-wait indicator-services-start
9176end script
9277
9378post-stop script
9479 initctl set-env --global MIR_SOCKET=$UNITY_MIR_SOCKET
95− initctl unset-env --global UNITY_MIR_SOCKET
9680 initctl unset-env --global MIR_SERVER_PROMPT_FILE
9781 initctl emit application-end
9882end script
debian/changelog+56
@@ -1,3 +1,59 @@
1+unity8 (8.15+17.04.20170308-0ubuntu1) zesty; urgency=medium
2+
3+ [ Albert Astals Cid ]
4+ * Let's follow the keyboard order
5+ * Enable the license check (LP: #1590044)
6+ * Show a barebones error message if loading the QML doesn't work
7+ * Make sure cursor is back to normal when closing the window (LP:
8+ #1666158)
9+ * Fix small warning when sensitive is undefined
10+ * Remove unused member variable
11+ * Initialize m_msecsSinceReference
12+ * Add warning if registerService fails
13+ * Move menus Component {} out of the repeater
14+ * Add tool to browse menus of a running app
15+
16+ [ Daniel d'Andrada ]
17+ * Drag child windows with Alt+LeftMouseButton (LP: #1664947)
18+
19+ [ Gerry Boland ]
20+ * TopLevelWindowModel: connect to the OSK surface to know when it goes
21+ away so we can clean up after it. (LP: #1670681)
22+
23+ [ Josh Arenson ]
24+ * Initialize sessionMode to single when mock is reset (LP: #1667463)
25+
26+ [ Lukáš Tinkl ]
27+ * Make the menu string "Back" translatable
28+ * Let the OSK be driven by a switch in indicator-keyboard (LP:
29+ #1521518)
30+ * Disable the tutorial when there's no touchscreen (LP: #1661557)
31+ * Shell chrome fixes (LP: #1658117, #1665723, #1665724, #1669010)
32+ * Cancel the drag as soon as we're entering the spread (LP: #1668642)
33+ * Fix being unable to use window control buttons or menus with touch
34+ (LP: #1667604, #1668387)
35+
36+ [ Michael Terry ]
37+ * Set QT_IM_MODULE in the greeter so that the OSK can come up. (LP:
38+ #1670383)
39+ * Simplify our startup handling of Mir environment variables and set
40+ the right socket path in snappy.
41+
42+ [ Michael Zanetti ]
43+ * Cancel the home key activation if something else is pressed along
44+ with Meta (LP: #1670678)
45+ * close the drawer when something is launched from the launcher (LP:
46+ #1660367)
47+ * use a svg instead of a png for the Launcher's BFB (LP: #1668057)
48+ * change the default setting for the launcher autohiding (LP:
49+ #1670655)
50+
51+ [ Nick Dedekind ]
52+ * Split up Shell::test_spreadDisabled to ensure initial state for each
53+ test.
54+
55+ -- Michał Sawicz <michal.sawicz@canonical.com> Wed, 08 Mar 2017 09:56:46 +0000
56+
157unity8 (8.15+17.04.20170221-0ubuntu1) zesty; urgency=medium
258
359 * Handle ubuntu-app-launch API/ABI break
plugins/Utils/URLDispatcher.cpp+5−2
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2016 Canonical, Ltd.
2+ * Copyright (C) 2016-2017 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
@@ -17,6 +17,7 @@
1717#include "URLDispatcher.h"
1818
1919#include <QDBusConnection>
20+#include <QDebug>
2021
2122class URLDispatcherInterface : public QObject
2223{
@@ -63,7 +64,9 @@ void URLDispatcher::setActive(bool value)
6364 connection.registerObject(QStringLiteral("/com/canonical/URLDispatcher"),
6465 dispatcher,
6566 QDBusConnection::ExportScriptableContents);
66− connection.registerService(QStringLiteral("com.canonical.URLDispatcher"));
67+ if (!connection.registerService(QStringLiteral("com.canonical.URLDispatcher"))) {
68+ qWarning() << "Unable to register DBus service com.canonical.URLDispatcher";
69+ }
6770 m_dispatcher = dispatcher;
6871 } else {
6972 connection.unregisterService(QStringLiteral("com.canonical.URLDispatcher"));
plugins/Utils/WindowInputMonitor.cpp+3
@@ -73,6 +73,9 @@ void WindowInputMonitor::update(QEvent *event)
7373 && m_windowLastTouchedTimer->elapsed() >= msecsWithoutTouches) {
7474 m_pressedHomeKey = keyEvent->key();
7575 m_activationTimer->start();
76+ } else if (m_pressedHomeKey != 0 && !m_homeKeys.contains(keyEvent->key())) {
77+ // something else came in... cancel activation
78+ m_activationTimer->stop();
7679 }
7780
7881 } else if (event->type() == QEvent::KeyRelease) {
plugins/WindowManager/TopLevelWindowModel.cpp+1
@@ -341,6 +341,7 @@ void TopLevelWindowModel::onSurfaceCreated(unityapi::MirSurfaceInterface *surfac
341341 });
342342 } else {
343343 if (surface->type() == Mir::InputMethodType) {
344+ connectSurface(surface);
344345 setInputMethodWindow(createWindow(surface));
345346 } else {
346347 auto *application = m_applicationManager->findApplicationWithSurface(surface);
po/unity8.pot+7−3
@@ -8,7 +8,7 @@ msgid ""
88msgstr ""
99"Project-Id-Version: unity8\n"
1010"Report-Msgid-Bugs-To: \n"
11−"POT-Creation-Date: 2017-02-21 13:23+0000\n"
11+"POT-Creation-Date: 2017-03-08 09:56+0000\n"
1212"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
1313"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1414"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -196,7 +196,7 @@ msgid "Switches applications via the launcher."
196196msgstr ""
197197
198198#: qml/Components/KeyboardShortcutsOverlay.qml:164
199−msgid "Super + 0 to 9"
199+msgid "Super + 1 to 0"
200200msgstr ""
201201
202202#: qml/Components/KeyboardShortcutsOverlay.qml:169
@@ -526,7 +526,7 @@ msgctxt "Apps sorted alphabetically"
526526msgid "A-Z"
527527msgstr ""
528528
529−#: qml/Launcher/MoreAppsHeader.qml:39
529+#: qml/Launcher/MoreAppsHeader.qml:55
530530msgid "More apps in the store"
531531msgstr ""
532532
@@ -617,6 +617,10 @@ msgctxt "Button: Call back on phone"
617617msgid "Call back"
618618msgstr ""
619619
620+#: qml/Panel/PanelMenuPage.qml:92
621+msgid "Back"
622+msgstr ""
623+
620624#: qml/Stage/SideStage.qml:76
621625msgid "Drag using 3 fingers any application from one window to the other"
622626msgstr ""
po/update-unity-pot+17
@@ -1,4 +1,21 @@
11#!/bin/sh
2+
3+#
4+# Copyright (C) 2017 Canonical Ltd
5+#
6+# This program is free software: you can redistribute it and/or modify
7+# it under the terms of the GNU General Public License version 3 as
8+# published by the Free Software Foundation.
9+#
10+# This program is distributed in the hope that it will be useful,
11+# but WITHOUT ANY WARRANTY; without even the implied warranty of
12+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+# GNU General Public License for more details.
14+#
15+# You should have received a copy of the GNU General Public License
16+# along with this program. If not, see <http://www.gnu.org/licenses/>.
17+#
18+
219set -e
320cd $(dirname $0)
421PO_DIR=$PWD
qml/ApplicationMenus/MenuBar.qml+49−24
@@ -28,11 +28,19 @@ Item {
2828 property alias unityMenuModel: rowRepeater.model
2929 property bool enableKeyFilter: false
3030 property real overflowWidth: width
31+ property bool windowMoving: false
3132
3233 // read from outside
3334 readonly property bool valid: rowRepeater.count > 0
3435 readonly property bool showRequested: d.longAltPressed || d.currentItem != null
3536
37+ // MoveHandler API for DecoratedWindow
38+ signal pressed(var mouse)
39+ signal pressedChangedEx(bool pressed, var pressedButtons, real mouseX, real mouseY)
40+ signal positionChanged(var mouse)
41+ signal released(var mouse)
42+ signal doubleClicked(var mouse)
43+
3644 implicitWidth: row.width
3745 height: parent.height
3846
@@ -77,7 +85,7 @@ Item {
7785 anchors.fill: parent
7886 enabled: d.currentItem != null
7987 hoverEnabled: enabled && d.currentItem && d.currentItem.__popup != null
80− onPressed: d.dismissAll()
88+ onPressed: { mouse.accepted = false; d.dismissAll(); }
8189 }
8290
8391 Row {
@@ -96,6 +104,11 @@ Item {
96104 onModelReset: d.firstInvisibleIndex = undefined
97105 }
98106
107+ Component {
108+ id: menuComponent
109+ MenuPopup { }
110+ }
111+
99112 Repeater {
100113 id: rowRepeater
101114
@@ -114,12 +127,19 @@ Item {
114127
115128 implicitWidth: column.implicitWidth
116129 implicitHeight: row.height
117− enabled: model.sensitive && shouldDisplay
130+ enabled: (model.sensitive === true) && shouldDisplay
118131 opacity: shouldDisplay ? 1 : 0
119132
120133 function show() {
121134 if (!__popup) {
122− __popup = menuComponent.createObject(root, { objectName: visualItem.objectName + "-menu" });
135+ __popup = menuComponent.createObject(root,
136+ {
137+ objectName: visualItem.objectName + "-menu",
138+ desiredX: Qt.binding(function() { return visualItem.x - units.gu(1); }),
139+ desiredY: Qt.binding(function() { return root.height; }),
140+ unityMenuModel: Qt.binding(function() { return root.unityMenuModel.submenu(visualItem.__ownIndex); })
141+ });
142+ __popup.reset();
123143 __popup.childActivated.connect(dismiss);
124144 // force the current item to be the newly popped up menu
125145 } else {
@@ -162,17 +182,6 @@ Item {
162182 onDismissAll: visualItem.dismiss()
163183 }
164184
165− Component {
166− id: menuComponent
167− MenuPopup {
168− desiredX: visualItem.x - units.gu(1)
169− desiredY: parent.height
170− unityMenuModel: root.unityMenuModel.submenu(visualItem.__ownIndex)
171−
172− Component.onCompleted: reset();
173− }
174− }
175−
176185 RowLayout {
177186 id: column
178187 spacing: units.gu(1)
@@ -217,24 +226,40 @@ Item {
217226 } // Row
218227
219228 MouseArea {
220− anchors.fill: row
229+ anchors.fill: parent
221230 hoverEnabled: d.currentItem
222231
232+ property bool moved: false
233+
223234 onEntered: {
224235 if (d.currentItem) {
225236 updateCurrentItemFromPosition(Qt.point(mouseX, mouseY))
226237 }
227238 }
228− onPositionChanged: {
229− if (d.currentItem) {
230− updateCurrentItemFromPosition(Qt.point(mouse.x, mouse.y))
239+
240+ onClicked: {
241+ if (!moved) {
242+ var prevItem = d.currentItem;
243+ updateCurrentItemFromPosition(Qt.point(mouseX, mouseY));
244+ if (prevItem && d.currentItem == prevItem) {
245+ prevItem.hide();
246+ }
231247 }
248+ moved = false;
232249 }
233− onClicked: {
234− var prevItem = d.currentItem;
235− updateCurrentItemFromPosition(Qt.point(mouse.x, mouse.y))
236− if (prevItem && d.currentItem == prevItem) {
237− prevItem.hide();
250+
251+ // for the MoveHandler
252+ onPressed: root.pressed(mouse)
253+ onPressedChanged: root.pressedChangedEx(pressed, pressedButtons, mouseX, mouseY)
254+ onReleased: root.released(mouse)
255+ onDoubleClicked: root.doubleClicked(mouse)
256+
257+ Mouse.ignoreSynthesizedEvents: true
258+ Mouse.onPositionChanged: {
259+ root.positionChanged(mouse);
260+ moved = root.windowMoving;
261+ if (d.currentItem) {
262+ updateCurrentItemFromPosition(Qt.point(mouse.x, mouse.y))
238263 }
239264 }
240265
@@ -259,7 +284,7 @@ Item {
259284 hoverEnabled: d.currentItem
260285 onEntered: d.currentItem = this
261286 onPositionChanged: d.currentItem = this
262− onClicked: d.currentItem = this
287+ onPressed: d.currentItem = this
263288
264289 property Item __popup: null;
265290 readonly property bool popupVisible: __popup && __popup.visible
qml/ApplicationMenus/MenuItem.qml+6−6
@@ -34,7 +34,7 @@ ActionItem {
3434 val += units.gu(1) + title.contentWidth;
3535 if (hasSubmenu) {
3636 val += units.gu(1) + chevronIcon.width;
37− } else if (shortcut != undefined) {
37+ } else if (menuData && menuData.shortcut != undefined) {
3838 val += units.gu(3) + shortcutLabel.contentWidth;
3939 }
4040 return val + units.gu(1);
@@ -49,9 +49,9 @@ ActionItem {
4949 enabled: root.enabled
5050
5151 // FIXME - SDK Action:text modifies menu text with html underline for mnemonic
52− text: menuData.label.replace("_", "&").replace("<u>", "&").replace("</u>", "")
53− checkable: menuData.isCheck || menuData.isRadio
54− checked: menuData.isToggled
52+ text: menuData ? menuData.label.replace("_", "&").replace("<u>", "&").replace("</u>", "") : ""
53+ checkable: menuData && (menuData.isCheck || menuData.isRadio)
54+ checked: menuData && menuData.isToggled
5555 }
5656
5757 width: {
@@ -127,8 +127,8 @@ ActionItem {
127127 color: enabled ? theme.palette.normal.overlaySecondaryText :
128128 theme.palette.disabled.overlaySecondaryText
129129
130− visible: menuData.shortcut != undefined && !root.hasSubmenu && QuickUtils.keyboardAttached
131− text: menuData.shortcut ? menuData.shortcut : ""
130+ visible: menuData && menuData.shortcut != undefined && !root.hasSubmenu && QuickUtils.keyboardAttached
131+ text: menuData && menuData.shortcut ? menuData.shortcut : ""
132132 }
133133 }
134134
qml/ApplicationMenus/MenuPopup.qml+75−73
@@ -276,6 +276,79 @@ UbuntuShape {
276276 }
277277 }
278278
279+ Component {
280+ id: separatorComponent
281+ ListItems.ThinDivider {
282+ // Parent will be loader
283+ objectName: parent.objectName + "-separator"
284+ implicitHeight: units.dp(2)
285+ }
286+ }
287+
288+ Component {
289+ id: menuItemComponent
290+ MenuItem {
291+ // Parent will be loader
292+ id: menuItem
293+ menuData: parent.__menuData
294+ objectName: parent.objectName + "-actionItem"
295+
296+ width: MathUtils.clamp(implicitWidth, d.__minimumWidth, d.__maximumWidth)
297+
298+ action.onTriggered: {
299+ submenuHoverTimer.stop();
300+
301+ d.currentItem = parent;
302+
303+ if (hasSubmenu) {
304+ if (!popup) {
305+ var model = root.unityMenuModel.submenu(__ownIndex);
306+ popup = submenuComponent.createObject(focusScope, {
307+ objectName: parent.objectName + "-",
308+ unityMenuModel: model,
309+ substractWidth: true,
310+ desiredX: Qt.binding(function() { return root.width }),
311+ desiredY: Qt.binding(function() {
312+ var dummy = listView.contentY; // force a recalc on contentY change.
313+ return mapToItem(container, 0, y).y;
314+ })
315+ });
316+ popup.retreat.connect(function() {
317+ popup.destroy();
318+ popup = null;
319+ menuItem.forceActiveFocus();
320+ });
321+ popup.childActivated.connect(function() {
322+ popup.destroy();
323+ popup = null;
324+ root.childActivated();
325+ });
326+ } else if (popup) {
327+ popup.visible = true;
328+ }
329+ } else {
330+ root.unityMenuModel.activate(__ownIndex);
331+ root.childActivated();
332+ }
333+ }
334+
335+ Connections {
336+ target: d
337+ onCurrentIndexChanged: {
338+ if (popup && d.currentIndex != __ownIndex) {
339+ popup.visible = false;
340+ }
341+ }
342+ onDismissAll: {
343+ if (popup) {
344+ popup.destroy();
345+ popup = null;
346+ }
347+ }
348+ }
349+ }
350+ }
351+
279352 ColumnLayout {
280353 id: menuColumn
281354 spacing: 0
@@ -289,6 +362,8 @@ UbuntuShape {
289362 id: loader
290363 objectName: root.objectName + "-item" + __ownIndex
291364
365+ property Item popup: null
366+ property var __menuData: model
292367 property int __ownIndex: index
293368 property bool __isSeparator: model.isSeparator
294369
@@ -301,80 +376,7 @@ UbuntuShape {
301376 return menuItemComponent;
302377 }
303378
304− property Item popup: null
305−
306379 Layout.fillWidth: true
307−
308− Component {
309− id: menuItemComponent
310− MenuItem {
311− id: menuItem
312− menuData: model
313− objectName: loader.objectName + "-actionItem"
314−
315− width: MathUtils.clamp(implicitWidth, d.__minimumWidth, d.__maximumWidth)
316−
317− action.onTriggered: {
318− submenuHoverTimer.stop();
319−
320− d.currentItem = loader;
321−
322− if (hasSubmenu) {
323− if (!popup) {
324− var model = root.unityMenuModel.submenu(__ownIndex);
325− popup = submenuComponent.createObject(focusScope, {
326− objectName: loader.objectName + "-",
327− unityMenuModel: model,
328− substractWidth: true,
329− desiredX: Qt.binding(function() { return root.width }),
330− desiredY: Qt.binding(function() {
331− var dummy = listView.contentY; // force a recalc on contentY change.
332− return mapToItem(container, 0, y).y;
333− })
334− });
335− popup.retreat.connect(function() {
336− popup.destroy();
337− popup = null;
338− menuItem.forceActiveFocus();
339− });
340− popup.childActivated.connect(function() {
341− popup.destroy();
342− popup = null;
343− root.childActivated();
344− });
345− } else if (popup) {
346− popup.visible = true;
347− }
348− } else {
349− root.unityMenuModel.activate(__ownIndex);
350− root.childActivated();
351− }
352− }
353−
354− Connections {
355− target: d
356− onCurrentIndexChanged: {
357− if (popup && d.currentIndex != __ownIndex) {
358− popup.visible = false;
359− }
360− }
361− onDismissAll: {
362− if (popup) {
363− popup.destroy();
364− popup = null;
365− }
366− }
367− }
368− }
369− }
370−
371− Component {
372− id: separatorComponent
373− ListItems.ThinDivider {
374− objectName: loader.objectName + "-separator"
375− implicitHeight: units.dp(2)
376− }
377− }
378380 }
379381
380382 }
qml/Components/KeyboardShortcutsOverlay.qml+1−1
@@ -161,7 +161,7 @@ Rectangle {
161161 }
162162
163163 Label {
164− text: i18n.tr("Super + 0 to 9")
164+ text: i18n.tr("Super + 1 to 0")
165165 fontSize: "small"
166166 font.weight: Font.Medium
167167 }
qml/Components/WindowControlButtons.qml−21
@@ -47,13 +47,6 @@ Row {
4747 onClicked: root.closeClicked()
4848 visible: root.closeButtonShown
4949
50− // We dont want touch events to fall through to parent,
51− // otherwise the containsMouse will not work.
52− MouseArea {
53− anchors.fill: parent
54− propagateComposedEvents: true
55− }
56−
5750 Rectangle {
5851 anchors.fill: parent
5952 anchors.margins: windowIsMaximized ? units.dp(3) : 0
@@ -78,13 +71,6 @@ Row {
7871 onClicked: root.minimizeClicked()
7972 visible: root.minimizeButtonVisible
8073
81− // We dont want touch events to fall through to parent,
82− // otherwise the containsMouse will not work.
83− MouseArea {
84− anchors.fill: parent
85− propagateComposedEvents: true
86− }
87−
8874 Rectangle {
8975 anchors.fill: parent
9076 anchors.margins: windowIsMaximized ? units.dp(3) : 0
@@ -118,13 +104,6 @@ Row {
118104 }
119105 }
120106
121− // We dont want touch events to fall through to parent,
122− // otherwise the containsMouse will not work.
123− MouseArea {
124− anchors.fill: parent
125− propagateComposedEvents: true
126− }
127−
128107 Rectangle {
129108 anchors.fill: parent
130109 anchors.margins: windowIsMaximized ? units.dp(3) : 0
qml/Greeter/GreeterPrompt.qml+29−10
@@ -16,6 +16,7 @@
1616
1717import QtQuick 2.4
1818import Ubuntu.Components 1.3
19+import GSettings 1.0
1920import "../Components"
2021
2122FocusScope {
@@ -43,6 +44,11 @@ FocusScope {
4344 }
4445 }
4546
47+ GSettings {
48+ id: unity8Settings
49+ schema.id: "com.canonical.Unity8"
50+ }
51+
4652 StyledItem {
4753 id: d
4854
@@ -154,14 +160,27 @@ FocusScope {
154160 }
155161
156162 secondaryItem: [
157− Icon {
158− id: capsIcon
159− name: "keyboard-caps-enabled"
160− height: units.gu(3)
161− width: units.gu(3)
162− color: d.textColor
163− visible: root.isSecret && false // TODO: detect when caps lock is on
164− readonly property real visibleWidth: visible ? width + passwordInput.frameSpacing : 0
163+ Row {
164+ id: extraIcons
165+ spacing: passwordInput.frameSpacing
166+ Icon {
167+ name: "keyboard-caps-enabled"
168+ height: units.gu(3)
169+ width: units.gu(3)
170+ color: d.textColor
171+ visible: root.isSecret && false // TODO: detect when caps lock is on
172+ }
173+ Icon {
174+ name: "input-keyboard-symbolic"
175+ height: units.gu(3)
176+ width: units.gu(3)
177+ color: d.textColor
178+ visible: !unity8Settings.alwaysShowOsk
179+ MouseArea {
180+ anchors.fill: parent
181+ onClicked: unity8Settings.alwaysShowOsk = true
182+ }
183+ }
165184 }
166185 ]
167186
@@ -199,7 +218,7 @@ FocusScope {
199218 right: parent.right
200219 verticalCenter: parent.verticalCenter
201220 leftMargin: units.gu(1.5)
202− rightMargin: anchors.leftMargin + capsIcon.visibleWidth
221+ rightMargin: anchors.leftMargin + extraIcons.width
203222 }
204223 text: root.text
205224 visible: passwordInput.text == "" && !passwordInput.inputMethodComposing
@@ -221,7 +240,7 @@ FocusScope {
221240 anchors.left: parent.left
222241 anchors.right: parent.right
223242 anchors.leftMargin: passwordInput.frameSpacing * 2
224− anchors.rightMargin: passwordInput.frameSpacing * 2 + capsIcon.visibleWidth
243+ anchors.rightMargin: passwordInput.frameSpacing * 2 + extraIcons.width
225244 color: d.drawColor
226245 text: passwordInput.displayText
227246 visible: root.isPrompt && !root.interactive
qml/Launcher/Launcher.qml+3
@@ -114,6 +114,9 @@ FocusScope {
114114
115115 function hide(flags) {
116116 if ((flags & ignoreHideIfMouseOverLauncher) && Utils.Functions.itemUnderMouse(panel)) {
117+ if (state == "drawer") {
118+ switchToNextState("visibleTemporary");
119+ }
117120 return;
118121 }
119122 if (root.lockedVisible) {
qml/Launcher/LauncherPanel.qml+3−1
@@ -92,8 +92,10 @@ Rectangle {
9292 objectName: "dashItem"
9393 width: parent.width * .6
9494 height: width
95+ sourceSize.width: width
96+ sourceSize.height: height
9597 anchors.centerIn: parent
96− source: "graphics/home.png"
98+ source: "graphics/home.svg"
9799 rotation: root.rotation
98100 }
99101 AbstractButton {
qml/Launcher/MoreAppsHeader.qml+16
@@ -1,3 +1,19 @@
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+
117import QtQuick 2.4
218import Ubuntu.Components 1.3
319
qml/Launcher/graphics/home.svgnew file+243
@@ -0,0 +1,243 @@
1+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2+<!-- Created with Inkscape (http://www.inkscape.org/) -->
3+
4+<svg
5+ xmlns:dc="http://purl.org/dc/elements/1.1/"
6+ xmlns:cc="http://creativecommons.org/ns#"
7+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
8+ xmlns:svg="http://www.w3.org/2000/svg"
9+ xmlns="http://www.w3.org/2000/svg"
10+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
11+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
12+ viewBox="0 0 90 89.999999"
13+ style="enable-background:new"
14+ inkscape:export-ydpi="180"
15+ inkscape:export-xdpi="180"
16+ inkscape:export-filename="app_shape02.png"
17+ sodipodi:docname="home@18.svg"
18+ inkscape:version="0.92+devel unknown"
19+ version="1.1"
20+ id="svg3336"
21+ height="90"
22+ width="90">
23+ <sodipodi:namedview
24+ inkscape:snap-global="false"
25+ fit-margin-bottom="0"
26+ fit-margin-right="0"
27+ fit-margin-left="0"
28+ fit-margin-top="0"
29+ inkscape:snap-grids="true"
30+ inkscape:guide-bbox="true"
31+ showguides="false"
32+ inkscape:snap-center="true"
33+ inkscape:snap-object-midpoints="true"
34+ inkscape:snap-midpoints="true"
35+ inkscape:snap-smooth-nodes="true"
36+ inkscape:snap-intersection-paths="true"
37+ inkscape:object-nodes="true"
38+ inkscape:object-paths="true"
39+ inkscape:snap-bbox-midpoints="true"
40+ inkscape:snap-bbox-edge-midpoints="true"
41+ inkscape:bbox-nodes="true"
42+ inkscape:bbox-paths="true"
43+ inkscape:snap-bbox="true"
44+ showborder="true"
45+ inkscape:showpageshadow="false"
46+ inkscape:snap-page="true"
47+ inkscape:window-maximized="1"
48+ inkscape:window-y="24"
49+ inkscape:window-x="0"
50+ inkscape:window-height="1029"
51+ inkscape:window-width="1920"
52+ showgrid="false"
53+ inkscape:current-layer="layer1"
54+ inkscape:document-units="px"
55+ inkscape:cy="-8.7116629"
56+ inkscape:cx="2.9876656"
57+ inkscape:zoom="1.5625"
58+ inkscape:pageshadow="2"
59+ inkscape:pageopacity="0"
60+ borderopacity="1.0"
61+ bordercolor="#666666"
62+ pagecolor="#4f4f4f"
63+ id="base"
64+ inkscape:document-rotation="0">
65+ <inkscape:grid
66+ type="xygrid"
67+ id="grid811"
68+ originx="-211"
69+ originy="-195.00023"
70+ visible="true"
71+ enabled="true" />
72+ <sodipodi:guide
73+ orientation="1,0"
74+ position="-107,-35.000232"
75+ id="guide819"
76+ inkscape:locked="false" />
77+ <sodipodi:guide
78+ orientation="1,0"
79+ position="-91,-51.000232"
80+ id="guide821"
81+ inkscape:locked="false" />
82+ <sodipodi:guide
83+ orientation="1,0"
84+ position="181,-67.000232"
85+ id="guide823"
86+ inkscape:locked="false" />
87+ <sodipodi:guide
88+ orientation="1,0"
89+ position="197,-67.000232"
90+ id="guide825"
91+ inkscape:locked="false" />
92+ <sodipodi:guide
93+ orientation="0,1"
94+ position="117,196.99977"
95+ id="guide827"
96+ inkscape:locked="false" />
97+ <sodipodi:guide
98+ orientation="0,1"
99+ position="125,180.99977"
100+ id="guide829"
101+ inkscape:locked="false" />
102+ <sodipodi:guide
103+ orientation="0,1"
104+ position="-35,-91.000232"
105+ id="guide831"
106+ inkscape:locked="false" />
107+ <sodipodi:guide
108+ orientation="0,1"
109+ position="5.0000002,-107.00023"
110+ id="guide833"
111+ inkscape:locked="false" />
112+ <inkscape:grid
113+ type="axonomgrid"
114+ id="grid835"
115+ units="px"
116+ empspacing="60"
117+ gridanglex="42.9"
118+ gridanglez="42.9"
119+ spacingy="3.522px"
120+ enabled="false"
121+ originx="-211"
122+ originy="-194.00023" />
123+ <sodipodi:guide
124+ orientation="0,1"
125+ position="133,164.99977"
126+ id="guide4126"
127+ inkscape:locked="false" />
128+ <sodipodi:guide
129+ orientation="0,1"
130+ position="-51,-75.000232"
131+ id="guide4128"
132+ inkscape:locked="false" />
133+ <sodipodi:guide
134+ orientation="1,0"
135+ position="-75,-115.00023"
136+ id="guide4130"
137+ inkscape:locked="false" />
138+ <sodipodi:guide
139+ orientation="1,0"
140+ position="165,-75.000232"
141+ id="guide4134"
142+ inkscape:locked="false" />
143+ <sodipodi:guide
144+ orientation="-0.70710678,0.70710678"
145+ position="333,332.99977"
146+ id="guide4136"
147+ inkscape:locked="false" />
148+ <sodipodi:guide
149+ orientation="1,0"
150+ position="45,-243.00023"
151+ id="guide4138"
152+ inkscape:locked="false" />
153+ <sodipodi:guide
154+ orientation="0,1"
155+ position="333,44.999767"
156+ id="guide4140"
157+ inkscape:locked="false" />
158+ <sodipodi:guide
159+ orientation="0.70710678,0.70710678"
160+ position="333,-243.00023"
161+ id="guide4142"
162+ inkscape:locked="false" />
163+ <sodipodi:guide
164+ orientation="0,1"
165+ position="37,-195.00023"
166+ id="guide4152"
167+ inkscape:locked="false" />
168+ <sodipodi:guide
169+ orientation="0,1"
170+ position="125,284.99977"
171+ id="guide4154"
172+ inkscape:locked="false" />
173+ <sodipodi:guide
174+ position="133,-115.00023"
175+ orientation="1,0"
176+ id="guide4361"
177+ inkscape:locked="false" />
178+ <sodipodi:guide
179+ position="365,-43.000232"
180+ orientation="0,1"
181+ id="guide4363"
182+ inkscape:locked="false" />
183+ <sodipodi:guide
184+ position="-43,-19.000232"
185+ orientation="1,0"
186+ id="guide4229"
187+ inkscape:locked="false" />
188+ <sodipodi:guide
189+ position="101,132.99977"
190+ orientation="0,1"
191+ id="guide4233"
192+ inkscape:locked="false" />
193+ <sodipodi:guide
194+ position="-30.52,-307.00023"
195+ orientation="0,1"
196+ id="guide4235"
197+ inkscape:locked="false" />
198+ </sodipodi:namedview>
199+ <defs
200+ id="defs3338">
201+ <clipPath
202+ clipPathUnits="userSpaceOnUse"
203+ id="clipPath4755">
204+ <path
205+ style="color:#000000;opacity:0.24899998;fill:#5e2c73;stroke-width:5"
206+ d="M 360,50 594,284 V 215.89844 C 594,70.73665 573.2426,50 427.94531,50 Z"
207+ id="path4757"
208+ inkscape:connector-curvature="0" />
209+ </clipPath>
210+ <clipPath
211+ clipPathUnits="userSpaceOnUse"
212+ id="clipPath4767">
213+ <path
214+ style="color:#000000;fill:#f6f6f6;stroke-width:5"
215+ d="M 543.8125,454.1875 312.18945,685.81055 367.94531,681 C 512.70468,668.50896 526.55393,654.72881 539,510.10156 Z"
216+ id="path4769"
217+ inkscape:connector-curvature="0" />
218+ </clipPath>
219+ </defs>
220+ <metadata
221+ id="metadata3341">
222+ <rdf:RDF>
223+ <cc:Work
224+ rdf:about="">
225+ <dc:format>image/svg+xml</dc:format>
226+ <dc:type
227+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
228+ <dc:title></dc:title>
229+ </cc:Work>
230+ </rdf:RDF>
231+ </metadata>
232+ <g
233+ inkscape:groupmode="layer"
234+ id="layer1"
235+ inkscape:label="Pictogram"
236+ transform="translate(-243,-242.99977)">
237+ <path
238+ style="color:#000000;fill:#ffffff;stroke-width:8;stroke-linecap:square"
239+ d="m 288,243.00023 a 44.999951,44.999951 0 0 0 -45,45 44.999951,44.999951 0 0 0 45,44.99954 44.999951,44.999951 0 0 0 45,-44.99954 44.999951,44.999951 0 0 0 -45,-45 z m 14.57434,13.77039 c 1.02065,-0.0231 2.06021,0.22446 3.0092,0.77195 2.76095,1.59476 3.70514,5.12339 2.11064,7.88408 -1.59271,2.76068 -5.12109,3.70488 -7.88178,2.11064 -2.75891,-1.59271 -3.70494,-5.12125 -2.11248,-7.88041 1.04672,-1.8117 2.9259,-2.84213 4.87442,-2.88626 z m -14.5748,6.15598 c 2.32096,0 4.56584,0.32221 6.69895,0.91089 0.37619,2.31944 1.75264,4.4561 3.94752,5.7227 2.19107,1.26304 4.72599,1.38994 6.92062,0.56079 4.26774,4.19709 7.04521,9.90478 7.44987,16.26267 l -8.22457,0.12112 C 304.03344,277.89278 296.80747,271.1402 288,271.1402 c -2.53726,0 -4.9441,0.5641 -7.10161,1.56813 l -4.01059,-7.18662 c 3.34938,-1.65779 7.12182,-2.59511 11.11174,-2.59511 z m -13.90979,4.21169 4.21992,7.06779 c -4.3351,3.05147 -7.16833,8.09073 -7.16833,13.79461 0,5.70414 2.83323,10.74309 7.16833,13.79278 l -4.21992,7.06916 c -5.0519,-3.37556 -8.80916,-8.53411 -10.36858,-14.57708 1.82199,-1.48672 2.98818,-3.74984 2.98818,-6.28532 0,-2.53701 -1.16619,-4.80043 -2.98818,-6.28715 1.56145,-6.04118 5.31871,-11.19923 10.36858,-14.57479 z m -15.48569,15.08943 c 3.18747,0 5.7716,2.58397 5.7716,5.77297 0,3.18746 -2.58413,5.77114 -5.7716,5.77114 -3.18924,0 -5.77296,-2.58368 -5.77296,-5.77114 0,-3.189 2.58372,-5.77297 5.77296,-5.77297 z m 46.18604,7.2666 8.2264,0.12112 c -0.40465,6.3579 -3.18213,12.06558 -7.44987,16.26267 -2.19462,-0.82915 -4.72803,-0.70428 -6.92062,0.5608 -2.19438,1.26888 -3.57087,3.40326 -3.94706,5.72269 -2.13286,0.59047 -4.378,0.91318 -6.69895,0.91318 -3.99018,0 -7.76236,-0.93758 -11.11175,-2.5974 l 4.0106,-7.18662 c 2.15726,1.00378 4.56389,1.56859 7.10115,1.56859 8.80772,0 16.03339,-6.75074 16.7901,-15.36503 z m -1.96713,18.19415 c 1.94837,0.044 3.82782,1.0739 4.87304,2.88443 1.59449,2.76095 0.64796,6.28907 -2.11247,7.88179 -2.76069,1.5945 -6.28862,0.64822 -7.88133,-2.11247 -1.59475,-2.75917 -0.64821,-6.2873 2.11247,-7.88179 0.94829,-0.5475 1.98772,-0.795 3.00829,-0.77196 z"
240+ id="circle4206"
241+ inkscape:connector-curvature="0" />
242+ </g>
243+</svg>
qml/Launcher/graphics/home@18.pngdeleted

Binary file not shown.

qml/OrientedShell.qml+8−6
@@ -197,6 +197,12 @@ Item {
197197 value: !shell.oskEnabled || shell.usageScenario == "desktop"
198198 }
199199
200+ Binding {
201+ target: unity8Settings
202+ property: "oskSwitchVisible"
203+ value: shell.hasKeyboard
204+ }
205+
200206 readonly property int supportedOrientations: shell.supportedOrientations
201207 & (deviceConfiguration.supportedOrientations == deviceConfiguration.useNativeOrientation
202208 ? orientations.native_
@@ -268,12 +274,8 @@ Item {
268274 mode: applicationArguments.mode
269275 hasMouse: pointerInputDevices > 0
270276 hasKeyboard: keyboardsModel.count > 0
271− // TODO: Factor in if the current screen is a touch screen and if the user wants to
272− // have multiple keyboards around. For now we only enable one keyboard at a time
273− // thus hiding it here if there is a physical one around or if we have a second
274− // screen (the virtual touchpad & osk on the phone) attached.
275− oskEnabled: (keyboardsModel.count === 0 && screens.count === 1) ||
276− forceOSKEnabled
277+ hasTouchscreen: touchScreensModel.count > 0
278+ oskEnabled: unity8Settings.alwaysShowOsk || !hasKeyboard || forceOSKEnabled
277279
278280 usageScenario: {
279281 if (unity8Settings.usageMode === "Windowed") {
qml/Panel/Panel.qml+6−11
@@ -23,9 +23,6 @@ import Utils 0.1
2323import Unity.ApplicationMenu 0.1
2424
2525import QtQuick.Window 2.2
26−// for indicator-keyboard
27−import AccountsService 0.1
28−import Unity.InputInfo 0.1
2926
3027import "../ApplicationMenus"
3128import "../Components"
@@ -47,6 +44,7 @@ Item {
4744 property bool fullscreenMode: false
4845 property real panelAreaShowProgress: 1.0
4946 property bool greeterShown: false
47+ property bool hasKeyboard: false
5048
5149 property string mode: "staged"
5250
@@ -231,6 +229,8 @@ Item {
231229 target: __indicators
232230 onShownChanged: bar.dismiss();
233231 }
232+
233+ onDoubleClicked: PanelState.restoreClicked()
234234 }
235235 }
236236 }
@@ -337,12 +337,12 @@ Item {
337337 objectName: identifier+"-panelItem"
338338
339339 property int ownIndex: index
340− property bool overflow: parent.width - x > __indicators.overFlowWidth
341− property bool hidden: !expanded && (overflow || !indicatorVisible || hideSessionIndicator || hideKeyboardIndicator)
340+ readonly property bool overflow: parent.width - x > __indicators.overFlowWidth
341+ readonly property bool hidden: !expanded && (overflow || !indicatorVisible || hideSessionIndicator || hideKeyboardIndicator)
342342 // HACK for indicator-session
343343 readonly property bool hideSessionIndicator: identifier == "indicator-session" && Math.min(Screen.width, Screen.height) <= units.gu(60)
344344 // HACK for indicator-keyboard
345− readonly property bool hideKeyboardIndicator: identifier == "indicator-keyboard" && (AccountsService.keymaps.length < 2 || keyboardsModel.count == 0)
345+ readonly property bool hideKeyboardIndicator: identifier == "indicator-keyboard" && !hasKeyboard
346346
347347 height: parent.height
348348 expanded: indicators.expanded
@@ -396,11 +396,6 @@ Item {
396396 }
397397 }
398398
399− InputDeviceModel {
400− id: keyboardsModel
401− deviceFilter: InputInfo.Keyboard
402− }
403−
404399 IndicatorsLight {
405400 id: indicatorLights
406401 }
qml/Panel/PanelMenuPage.qml+1−1
@@ -89,7 +89,7 @@ PageStack {
8989 leadingActionBar.actions: [
9090 Action {
9191 iconName: "back"
92− text: "Back"
92+ text: i18n.tr("Back")
9393 onTriggered: {
9494 root.pop();
9595 }
qml/Shell.qml+4−2
@@ -69,6 +69,7 @@ StyledItem {
6969 }
7070 property bool hasMouse: false
7171 property bool hasKeyboard: false
72+ property bool hasTouchscreen: false
7273
7374 // to be read from outside
7475 readonly property int mainAppWindowOrientationAngle: stage.mainAppWindowOrientationAngle
@@ -533,6 +534,7 @@ StyledItem {
533534 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0 && !stage.spreadShown)
534535 || greeter.hasLockedApp
535536 greeterShown: greeter && greeter.shown
537+ hasKeyboard: shell.hasKeyboard
536538 }
537539
538540 Launcher {
@@ -627,8 +629,8 @@ StyledItem {
627629 objectName: "tutorial"
628630 anchors.fill: parent
629631
630− paused: callManager.hasCalls || !greeter || greeter.active ||
631− wizard.active
632+ paused: callManager.hasCalls || !greeter || greeter.active || wizard.active
633+ || !hasTouchscreen // TODO #1661557 something better for no touchscreen
632634 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
633635 inputMethod.visible ||
634636 (launcher.shown && !launcher.lockedVisible) ||
qml/Stage/ApplicationWindow.qml+1
@@ -28,6 +28,7 @@ FocusScope {
2828 property bool orientationChangesEnabled: d.supportsSurfaceResize ? d.surfaceOldEnoughToBeResized : true
2929 readonly property string title: surface && surface.name !== "" ? surface.name : d.name
3030 readonly property QtObject focusedSurface: d.focusedSurface.surface
31+ readonly property alias surfaceInitialized: d.surfaceInitialized
3132
3233 // to be set from outside
3334 property QtObject surface
qml/Stage/ChildWindow.qml+34−1
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2016 Canonical, Ltd.
2+ * Copyright (C) 2016-2017 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
@@ -143,6 +143,39 @@ Item {
143143 consumesInput: true
144144 }
145145
146+ Loader {
147+ active: d.moveable
148+ anchors.fill: parent
149+ sourceComponent: Component {
150+ MouseArea {
151+ acceptedButtons: Qt.LeftButton
152+ property bool dragging: false
153+ cursorShape: undefined // don't interfere with the cursor shape set by the underlying MirSurfaceItem
154+ onPressed: {
155+ if (mouse.button == Qt.LeftButton && mouse.modifiers == Qt.AltModifier) {
156+ d.moveHandler.handlePressedChanged(true, Qt.LeftButton, mouse.x, mouse.y);
157+ dragging = true;
158+ mouse.accepted = true;
159+ } else {
160+ mouse.accepted = false;
161+ }
162+ }
163+ onPositionChanged: {
164+ if (dragging) {
165+ d.moveHandler.handlePositionChanged(mouse);
166+ }
167+ }
168+ onReleased: {
169+ if (dragging) {
170+ d.moveHandler.handlePressedChanged(false, Qt.LeftButton);
171+ d.moveHandler.handleReleased();
172+ dragging = false;
173+ }
174+ }
175+ }
176+ }
177+ }
178+
146179 Loader {
147180 id: touchOverlayLoader
148181 active: d.resizeable || d.moveable
qml/Stage/DecoratedWindow.qml+7
@@ -80,6 +80,10 @@ FocusScope {
8080 signal decorationPressed()
8181 signal decorationReleased()
8282
83+ function cancelDrag() {
84+ moveHandler.cancelDrag();
85+ }
86+
8387 QtObject {
8488 id: d
8589 property int requestedDecorationHeight: root.hasDecoration ? decoration.height : 0
@@ -204,12 +208,14 @@ FocusScope {
204208 height: units.gu(3)
205209
206210 title: applicationWindow.title
211+ windowMoving: moveHandler.moving && !altDragHandler.dragging
207212
208213 opacity: root.hasDecoration ? Math.min(1, root.showDecoration) : 0
209214 Behavior on opacity { UbuntuNumberAnimation { } }
210215
211216 onPressed: root.decorationPressed();
212217 onPressedChanged: moveHandler.handlePressedChanged(pressed, pressedButtons, mouseX, mouseY)
218+ onPressedChangedEx: moveHandler.handlePressedChanged(pressed, pressedButtons, mouseX, mouseY)
213219 onPositionChanged: moveHandler.handlePositionChanged(mouse)
214220 onReleased: {
215221 root.decorationReleased();
@@ -255,6 +261,7 @@ FocusScope {
255261 }
256262
257263 MouseArea {
264+ id: altDragHandler
258265 anchors.fill: applicationWindow
259266 acceptedButtons: Qt.LeftButton
260267 property bool dragging: false
qml/Stage/MoveHandler.qml+13
@@ -29,6 +29,7 @@ QtObject {
2929 property real boundsTopMargin: 0
3030
3131 readonly property bool dragging: priv.dragging
32+ readonly property bool moving: priv.moving
3233
3334 signal fakeMaximizeAnimationRequested(real amount)
3435 signal fakeMaximizeLeftAnimationRequested(real amount)
@@ -43,6 +44,7 @@ QtObject {
4344 property real distanceX
4445 property real distanceY
4546 property bool dragging
47+ property bool moving
4648
4749 readonly property int triggerArea: units.gu(8)
4850 property bool nearLeftEdge: target.maximizedLeft
@@ -110,6 +112,7 @@ QtObject {
110112
111113 function handlePositionChanged(mouse, sensingPoints) {
112114 if (priv.dragging) {
115+ priv.moving = true;
113116 priv.mouseDownTimer.stop();
114117 Mir.cursorName = "grabbing";
115118
@@ -180,6 +183,7 @@ QtObject {
180183 }
181184
182185 function handleReleased(touchMode) {
186+ priv.moving = false;
183187 if (touchMode) {
184188 priv.progress = 0;
185189 priv.resetEdges();
@@ -190,4 +194,13 @@ QtObject {
190194 target.restoredY = target.y;
191195 }
192196 }
197+
198+ function cancelDrag() {
199+ priv.dragging = false;
200+ root.stopFakeAnimation();
201+ priv.mouseDownTimer.stop();
202+ Mir.cursorName = "";
203+ priv.progress = 0;
204+ priv.resetEdges();
205+ }
193206}
qml/Stage/Stage.qml+23−11
@@ -470,7 +470,7 @@ FocusScope {
470470 extend: "stagedRightEdge"
471471 PropertyChanges {
472472 target: sideStage
473− opacity: priv.sideStageDelegate.x === sideStage.x ? 1 : 0
473+ opacity: priv.sideStageDelegate && priv.sideStageDelegate.x === sideStage.x ? 1 : 0
474474 visible: true
475475 }
476476 },
@@ -723,6 +723,7 @@ FocusScope {
723723 // miral doesn't know about our window decorations. So we have to deduct them
724724 value: Qt.point(appDelegate.requestedX + appDelegate.clientAreaItem.x,
725725 appDelegate.requestedY + appDelegate.clientAreaItem.y)
726+ when: root.mode == "windowed"
726727 }
727728
728729 // In those are for windowed mode. Those values basically store the window's properties
@@ -768,7 +769,6 @@ FocusScope {
768769 Math.max(0, priv.virtualKeyboardHeight - (appContainer.height - (appDelegate.requestedY + appDelegate.height))))
769770 when: root.oskEnabled && appDelegate.focus && (appDelegate.state == "normal" || appDelegate.state == "restored")
770771 && root.inputMethodRect.height > 0
771−
772772 }
773773
774774 Behavior on x { id: xBehavior; enabled: priv.closingIndex >= 0; UbuntuNumberAnimation { onRunningChanged: if (!running) priv.closingIndex = -1} }
@@ -933,7 +933,8 @@ FocusScope {
933933 } else if (model.window.state === Mir.MaximizedBottomRightState) {
934934 appDelegate.maximizeBottomRight();
935935 } else if (model.window.state === Mir.RestoredState) {
936− if (appDelegate.fullscreen && appDelegate.prevWindowState != WindowStateStorage.WindowStateRestored) {
936+ if (appDelegate.fullscreen && appDelegate.prevWindowState != WindowStateStorage.WindowStateRestored
937+ && appDelegate.prevWindowState != WindowStateStorage.WindowStateNormal) {
937938 model.window.requestState(WindowStateStorage.toMirState(appDelegate.prevWindowState));
938939 } else {
939940 appDelegate.restore();
@@ -945,6 +946,21 @@ FocusScope {
945946 }
946947 }
947948
949+ readonly property bool windowReady: clientAreaItem.surfaceInitialized
950+ onWindowReadyChanged: {
951+ if (windowReady) {
952+ var loadedMirState = WindowStateStorage.toMirState(windowStateSaver.loadedState);
953+ // need to apply the shell chrome policy on top the saved window state
954+ var policy;
955+ if (root.mode == "windowed") {
956+ policy = windowedFullscreenPolicy;
957+ } else {
958+ policy = stagedFullscreenPolicy
959+ }
960+ window.requestState(policy.applyPolicy(loadedMirState, surface.shellChrome));
961+ }
962+ }
963+
948964 Component.onCompleted: {
949965 if (application && application.rotatesWindowContents) {
950966 decoratedWindow.surfaceOrientationAngle = shellOrientationAngle;
@@ -957,7 +973,6 @@ FocusScope {
957973 windowedY = priv.focusedAppDelegate ? priv.focusedAppDelegate.windowedY + units.gu(3) : normalZ * units.gu(3)
958974 // Now load any saved state. This needs to happen *after* the cascading!
959975 windowStateSaver.load();
960− model.window.requestState(WindowStateStorage.toMirState(windowState));
961976
962977 updateQmlFocusFromMirSurfaceFocus();
963978
@@ -1173,6 +1188,7 @@ FocusScope {
11731188 states: [
11741189 State {
11751190 name: "spread"; when: root.state == "spread"
1191+ StateChangeScript { script: { decoratedWindow.cancelDrag(); } }
11761192 PropertyChanges {
11771193 target: decoratedWindow;
11781194 showDecoration: false;
@@ -1199,6 +1215,7 @@ FocusScope {
11991215 }
12001216 PropertyChanges { target: dragArea; enabled: true }
12011217 PropertyChanges { target: windowInfoItem; opacity: spreadMaths.tileInfoOpacity; visible: spreadMaths.itemVisible }
1218+ PropertyChanges { target: touchControls; enabled: false }
12021219 },
12031220 State {
12041221 name: "stagedRightEdge"
@@ -1441,7 +1458,7 @@ FocusScope {
14411458 ]
14421459 transitions: [
14431460 Transition {
1444− from: "staged,stagedWithSideStage"; to: "normal"
1461+ from: "staged,stagedWithSideStage"
14451462 enabled: appDelegate.animationsEnabled
14461463 PropertyAction { target: appDelegate; properties: "visuallyMinimized,visuallyMaximized" }
14471464 UbuntuNumberAnimation { target: appDelegate; properties: "x,y,requestedX,requestedY,opacity,requestedWidth,requestedHeight,scale"; duration: priv.animationDuration }
@@ -1462,8 +1479,7 @@ FocusScope {
14621479 },
14631480 Transition {
14641481 from: "normal,staged"; to: "stagedWithSideStage"
1465− UbuntuNumberAnimation { target: appDelegate; properties: "x,y"; duration: priv.animationDuration }
1466− UbuntuNumberAnimation { target: appDelegate; properties: "requestedWidth,requestedHeight"; duration: priv.animationDuration }
1482+ UbuntuNumberAnimation { target: appDelegate; properties: "x,y,requestedWidth,requestedHeight"; duration: priv.animationDuration }
14671483 },
14681484 Transition {
14691485 to: "windowedRightEdge"
@@ -1503,7 +1519,6 @@ FocusScope {
15031519 from: ",normal,restored,maximized,maximizedLeft,maximizedRight,maximizedTopLeft,maximizedTopRight,maximizedBottomLeft,maximizedBottomRight,maximizedHorizontally,maximizedVertically,fullscreen"
15041520 to: "minimized"
15051521 SequentialAnimation {
1506− ScriptAction { script: print("transitioning:", appDelegate.x, appDelegate.y, appDelegate.scale) }
15071522 ScriptAction { script: { fakeRectangle.stop(); } }
15081523 PropertyAction { target: appDelegate; property: "visuallyMaximized" }
15091524 UbuntuNumberAnimation { target: appDelegate; properties: "x,y,scale,opacity"; duration: priv.animationDuration }
@@ -1514,7 +1529,6 @@ FocusScope {
15141529 from: "minimized"
15151530 to: ",normal,restored,maximized,maximizedLeft,maximizedRight,maximizedTopLeft,maximizedTopRight,maximizedBottomLeft,maximizedBottomRight,maximizedHorizontally,maximizedVertically,fullscreen"
15161531 SequentialAnimation {
1517− ScriptAction { script: print("transitioning:", appDelegate.x, appDelegate.y, appDelegate.scale) }
15181532 PropertyAction { target: appDelegate; property: "visuallyMinimized,z" }
15191533 ParallelAnimation {
15201534 UbuntuNumberAnimation { target: appDelegate; properties: "x"; from: -appDelegate.width / 2; duration: priv.animationDuration }
@@ -1661,8 +1675,6 @@ FocusScope {
16611675
16621676 WindowedFullscreenPolicy {
16631677 id: windowedFullscreenPolicy
1664− active: root.mode == "windowed"
1665− surface: model.window.surface
16661678 }
16671679 StagedFullscreenPolicy {
16681680 id: stagedFullscreenPolicy
qml/Stage/StageMaths.qml+16
@@ -1,3 +1,19 @@
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+
117import QtQuick 2.4
218import Unity.Application 0.1
319import Ubuntu.Components 1.3
qml/Stage/StagedFullscreenPolicy.qml+5−11
@@ -28,14 +28,14 @@ import Unity.Application 0.1
2828QtObject {
2929 property bool active: true
3030
31− property var surface: null
32− onSurfaceChanged: {
33− if (!active || !surface) return;
34− if (surface.shellChrome === Mir.LowChrome) {
35− surface.requestState(Mir.FullscreenState);
31+ function applyPolicy(surfaceState, surfaceChrome) {
32+ if (surfaceChrome === Mir.LowChrome) {
33+ return Mir.FullscreenState;
3634 }
35+ return surfaceState;
3736 }
3837
38+ property var surface: null
3939 property var _connections: Connections {
4040 target: surface
4141 onShellChromeChanged: {
@@ -46,11 +46,5 @@ QtObject {
4646 surface.requestState(Mir.RestoredState);
4747 }
4848 }
49− onStateChanged: {
50− if (!active) return;
51− if (surface.state === Mir.RestoredState && surface.shellChrome === Mir.LowChrome) {
52− surface.requestState(Mir.FullscreenState);
53− }
54− }
5549 }
5650}
qml/Stage/WindowDecoration.qml+12−14
@@ -33,23 +33,21 @@ MouseArea {
3333 property alias overlayShown: buttons.overlayShown
3434 property var menu: undefined
3535 property bool enableMenus: true
36+ property bool windowMoving: false
3637
3738 readonly property real buttonsWidth: buttons.width + row.spacing
3839
3940 acceptedButtons: Qt.AllButtons // prevent leaking unhandled mouse events
4041 hoverEnabled: true
4142
42− drag.target: Item {}
43− drag.filterChildren: true
44− drag.threshold: 0
45−
4643 signal closeClicked()
4744 signal minimizeClicked()
4845 signal maximizeClicked()
4946 signal maximizeHorizontallyClicked()
5047 signal maximizeVerticallyClicked()
5148
52− onClicked: mouse.accepted = true // propogated event
49+ signal pressedChangedEx(bool pressed, var pressedButtons, real mouseX, real mouseY)
50+
5351 onDoubleClicked: {
5452 if (mouse.button == Qt.LeftButton) {
5553 root.maximizeClicked();
@@ -69,13 +67,6 @@ MouseArea {
6967 (menuBar.showRequested || root.containsMouse)
7068 }
7169
72− // We dont want touch events to fall through to parent as it expect some child MouseArea to have them
73− // If not some MouseArea in the menu bar, it will be this one.
74− MouseArea {
75− anchors.fill: parent
76− propagateComposedEvents: true
77− }
78−
7970 Rectangle {
8071 id: background
8172 anchors.fill: parent
@@ -136,7 +127,7 @@ MouseArea {
136127 fontSize: "medium"
137128 font.weight: root.active ? Font.Light : Font.Medium
138129 elide: Text.ElideRight
139− opacity: overlayShown || priv.shouldShowMenus ? 0 : 1
130+ opacity: overlayShown || menuBarLoader.visible ? 0 : 1
140131 visible: opacity != 0
141132 Behavior on opacity { UbuntuNumberAnimation {} }
142133 }
@@ -154,9 +145,16 @@ MouseArea {
154145 height: menuBarLoader.height
155146 enableKeyFilter: valid && root.active && root.enableMenus
156147 unityMenuModel: root.menu
148+ windowMoving: root.windowMoving
149+
150+ onPressed: root.onPressed(mouse)
151+ onPressedChangedEx: root.pressedChangedEx(pressed, pressedButtons, mouseX, mouseY)
152+ onPositionChanged: root.onPositionChanged(mouse)
153+ onReleased: root.onReleased(mouse)
154+ onDoubleClicked: root.onDoubleClicked(mouse)
157155 }
158156
159− opacity: !overlayShown && priv.shouldShowMenus ? 1 : 0
157+ opacity: (!overlayShown && priv.shouldShowMenus) || (active && priv.menuBar.valid && root.windowMoving) ? 1 : 0
160158 visible: opacity == 1
161159 Behavior on opacity { UbuntuNumberAnimation {} }
162160 }
qml/Stage/WindowInfoItem.qml+16
@@ -1,3 +1,19 @@
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+
117import QtQuick 2.4
218import Ubuntu.Components 1.3
319
qml/Stage/WindowResizeArea.qml+12
@@ -152,6 +152,18 @@ MouseArea {
152152 onCursorNameChanged: {
153153 Mir.cursorName = cursorName;
154154 }
155+ Component.onDestruction: {
156+ // TODO Qt 5.8 has fixed the problem with containsMouse
157+ // not being updated when the MouseArea that had containsMouse
158+ // is hidden/removed. When we start using Qt 5.8 we should
159+ // try to fix this scenario
160+ // two windows side by side
161+ // cursor in the resize left area of the right one
162+ // close window by Alt+F4
163+ // cursor should change to resize right of the left one
164+ // currently changes to ""
165+ Mir.cursorName = "";
166+ }
155167
156168 function updateBorders() {
157169 leftBorder = mouseX <= borderThickness;
qml/Stage/WindowStateSaver.qml+4−3
@@ -28,6 +28,8 @@ QtObject {
2828 property int leftMargin: 0
2929 property int minimumY: 0
3030
31+ property int loadedState
32+
3133 function load() {
3234 var defaultWidth = units.gu(60);
3335 var defaultHeight = units.gu(50);
@@ -41,14 +43,13 @@ QtObject {
4143 (target.fullscreen ? 0 : root.leftMargin)); });
4244 target.windowedY = Qt.binding(function() { return Math.max(Math.min(windowGeometry.y, screenHeight - target.windowedHeight), minimumY); });
4345
44− var windowState = WindowStateStorage.getState(target.appId, WindowStateStorage.WindowStateNormal)
45− target.restore(false /* animated */, windowState);
46−
4746 target.updateNormalGeometry();
4847
4948 // initialize the x/y to restore to
5049 target.restoredX = target.normalX;
5150 target.restoredY = target.normalY;
51+
52+ loadedState = WindowStateStorage.getState(target.appId, WindowStateStorage.WindowStateNormal);
5253 }
5354
5455 function save() {
qml/Stage/WindowedFullscreenPolicy.qml+4−12
@@ -22,18 +22,10 @@ import Unity.Application 0.1
2222// On first surface load; if the surface is set to low chrome & fullscreen, the
2323// state of the window is returned to restored.
2424QtObject {
25− property bool active: true
26− property QtObject surface: null
27−
28− property bool _firstTimeSurface: true
29−
30− onSurfaceChanged: {
31− if (!active || !surface) return;
32− if (!_firstTimeSurface) return;
33− _firstTimeSurface = false;
34−
35− if (surface.state === Mir.FullscreenState && surface.shellChrome === Mir.LowChrome) {
36− surface.requestState(Mir.RestoredState);
25+ function applyPolicy(surfaceState, surfaceChrome) {
26+ if (surfaceState === Mir.FullscreenState && surfaceChrome === Mir.LowChrome) {
27+ return Mir.RestoredState;
3728 }
29+ return surfaceState;
3830 }
3931}
src/ShellView.cpp+22
@@ -19,6 +19,9 @@
1919// Qt
2020#include <QQmlContext>
2121#include <QQuickItem>
22+#include <QtQuick/private/qquickitem_p.h>
23+#include <QtQuick/private/qquickrectangle_p.h>
24+#include <QtQuick/private/qquicktext_p.h>
2225
2326// local
2427#include <paths.h>
@@ -32,6 +35,25 @@ ShellView::ShellView(QQmlEngine *engine, QObject *qmlArgs)
3235
3336 rootContext()->setContextProperty(QStringLiteral("applicationArguments"), qmlArgs);
3437
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+
3557 QUrl source(::qmlDirectory() + "/OrientedShell.qml");
3658 setSource(source);
3759
tests/CMakeLists.txt+1
@@ -143,3 +143,4 @@ add_subdirectory(plugins)
143143add_subdirectory(qmltests)
144144add_subdirectory(whitespace)
145145add_subdirectory(imports)
146+add_subdirectory(copyright)
tests/copyright/check_copyright.sh+33−4
@@ -1,7 +1,7 @@
11#!/bin/sh
22
33#
4−# Copyright (C) 2013 Canonical Ltd
4+# Copyright (C) 2017 Canonical Ltd
55#
66# This program is free software: you can redistribute it and/or modify
77# it under the terms of the GNU General Public License version 3 as
@@ -15,8 +15,6 @@
1515# You should have received a copy of the GNU General Public License
1616# along with this program. If not, see <http://www.gnu.org/licenses/>.
1717#
18−# Authored by: Michi Henning <michi.henning@canonical.com>
19−#
2018
2119#
2220# Check that, somewhere in the first 30 lines of each file, the string "Copyright" (case independent) appears.
@@ -33,7 +31,38 @@ usage()
3331[ $# -lt 1 ] && usage
3432[ $# -gt 2 ] && usage
3533
36−ignore_pat="\\.sci$"
34+# image files
35+ignore_pat="\\.sci$|\\.svg$|\\.png$|\\.jpg$"
36+# git/bzr files
37+ignore_pat="$ignore_pat|/\\.bzr/|\\.bzrignore$|\\.gitignore$|/\\.bazaar/Makefile$|/\\.bzr-builddeb/default\\.conf$"
38+# info files
39+ignore_pat="$ignore_pat|/README$|/CODING$|/LGPL_EXCEPTION\\.txt$"
40+# cmake files
41+ignore_pat="$ignore_pat|/CMakeLists\\.txt$|\\.cmake$"
42+# card creator test files
43+ignore_pat="$ignore_pat|/cardcreator/.*\\.res$|/cardcreator/.*\\.res\\.cardcreator$|/cardcreator/.*\\.tst$"
44+# project files
45+ignore_pat="$ignore_pat|/\\.project$|/\\.pydevproject$|/\\.settings/|/\\.crossbuilder/"
46+# test desktop files
47+ignore_pat="$ignore_pat|/tests/.*/.*\\.desktop$"
48+# xml files
49+ignore_pat="$ignore_pat|\\.xml$"
50+# /data/ files
51+ignore_pat="$ignore_pat|/data/.*\\.conf$|/data/.*\\.in|/data/.*\\.url-dispatcher$|/data/.*\\.pkla$"
52+# polkit files
53+ignore_pat="$ignore_pat|\\.pkla$"
54+# /debian/ files
55+ignore_pat="$ignore_pat|/debian/"
56+# qmldir files
57+ignore_pat="$ignore_pat|/qmldir$"
58+# Doxygen files
59+ignore_pat="$ignore_pat|/Doxyfile.in$"
60+# Wizard license files
61+ignore_pat="$ignore_pat|/Wizard/licenses/.*\\.html$"
62+# qrc files
63+ignore_pat="$ignore_pat|\\.qrc$"
64+# the doc devices.conf file
65+ignore_pat="$ignore_pat|/doc/devices\\.conf$"
3766
3867#
3968# We don't use the -i option of licensecheck to add ignore_dir to the pattern because Jenkins creates directories
tests/mocks/Biometryd/MockOperation.h−1
@@ -40,7 +40,6 @@ Q_SIGNALS:
4040
4141private:
4242 QObject *m_observer;
43− bool m_running;
4443};
4544
4645Q_DECLARE_METATYPE(MockOperation*)
tests/mocks/Unity/Application/ApplicationManager.cpp+1
@@ -337,6 +337,7 @@ void ApplicationManager::buildListOfAvailableApplications()
337337 application->setScreenshotId("camera");
338338 application->setIconId("camera");
339339 application->setFullscreen(true);
340+ application->setShellChrome(Mir::LowChrome);
340341 application->setSupportedOrientations(Qt::PortraitOrientation
341342 | Qt::LandscapeOrientation
342343 | Qt::InvertedPortraitOrientation
tests/mocks/Unity/Indicators/fakeindicatorsmodeldata.js+16
@@ -1,3 +1,19 @@
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+
117var usageScenario = (typeof shell != 'undefined') ? shell.usageScenario : "desktop";
218
319var fakeMenuData = {
tests/mocks/liblightdm/MockController.cpp+2−1
@@ -25,6 +25,7 @@ MockController::MockController(QObject *parent)
2525 : QObject(parent)
2626 , m_selectGuestHint(false)
2727 , m_hasGuestAccountHint(false)
28+ , m_sessionMode("single")
2829 , m_fullSessions(
2930 {
3031 {"ubuntu", "Ubuntu"},
@@ -68,7 +69,7 @@ void MockController::reset()
6869 setSelectGuestHint(false);
6970 setHasGuestAccountHint(false);
7071 setUserMode(userMode);
71− setSessionMode("full");
72+ setSessionMode("single");
7273 setNumSessions(numFullSessions());
7374 setShowManualLoginHint(false);
7475 setHideUsersHint(false);
tests/plugins/Utils/WindowInputMonitorTest.cpp+31−1
@@ -32,7 +32,7 @@ public:
3232 qint64 elapsed() const override { return m_valid ? msecsSinceEpoch - m_msecsSinceReference : qrand(); }
3333
3434private:
35− qint64 m_msecsSinceReference;
35+ qint64 m_msecsSinceReference{0};
3636 bool m_valid{false};
3737};
3838qint64 FakeElapsedTimer::msecsSinceEpoch = 0;
@@ -89,6 +89,8 @@ private Q_SLOTS:
8989 void tapWhileTouching();
9090 void multipleHomeKeys();
9191
92+ void keyComboInvolvingHome();
93+
9294 void repeatedSuperPress();
9395
9496private:
@@ -197,6 +199,34 @@ void WindowInputMonitorTest::touchTapTouch()
197199 QCOMPARE(activatedSpy.count(), expectedActivatedCount);
198200}
199201
202+void WindowInputMonitorTest::keyComboInvolvingHome()
203+{
204+ WindowInputMonitor homeKeyWatcher(m_fakeTimerFactory->create(this), new FakeElapsedTimer);
205+ QSignalSpy activatedSpy(&homeKeyWatcher, &WindowInputMonitor::homeKeyActivated);
206+ QVERIFY(activatedSpy.isValid());
207+
208+ passTime(1000);
209+ {
210+ QKeyEvent keyEvent(QEvent::KeyPress, Qt::Key_Super_L, Qt::NoModifier);
211+ homeKeyWatcher.update(&keyEvent);
212+ }
213+ {
214+ QKeyEvent keyEvent(QEvent::KeyPress, Qt::Key_A, Qt::NoModifier);
215+ homeKeyWatcher.update(&keyEvent);
216+ }
217+ {
218+ QKeyEvent keyEvent(QEvent::KeyRelease, Qt::Key_A, Qt::NoModifier);
219+ homeKeyWatcher.update(&keyEvent);
220+ }
221+ {
222+ QKeyEvent keyEvent(QEvent::KeyRelease, Qt::Key_Super_L, Qt::NoModifier);
223+ homeKeyWatcher.update(&keyEvent);
224+ }
225+ passTime(1000);
226+
227+ QCOMPARE(activatedSpy.count(), 0);
228+}
229+
200230void WindowInputMonitorTest::tapWhileTouching()
201231{
202232 WindowInputMonitor homeKeyWatcher(m_fakeTimerFactory->create(this), new FakeElapsedTimer);
tests/qmltests/ApplicationMenuDataLoader.qml+16
@@ -1,3 +1,19 @@
1+/*
2+ * Copyright 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+
117import QtQuick 2.4
218import Ubuntu.Components 1.3
319import Unity.Application 0.1
tests/qmltests/ApplicationMenus/tst_MenuBar.qml+3
@@ -85,6 +85,7 @@ Item {
8585 menuBar.dismiss();
8686 menuBackend.modelData = appMenuData.generateTestData(5,5,2,3, "menu")
8787 activatedSpy.clear();
88+ waitForRendering(menuBar);
8889 }
8990
9091 function test_mouseNavigation() {
@@ -97,6 +98,7 @@ Item {
9798 var menuItem2 = findChild(menuBar, "menuBar-item2"); verify(menuItem2);
9899
99100 menuItem0.show();
101+ mouseMove(menuItem0, menuItem0.width/2, menuItem0.height/2);
100102 compare(priv.currentItem, menuItem0, "CurrentItem should be set to item 0");
101103 compare(priv.currentItem.popupVisible, true, "Popup should be visible");
102104
@@ -178,6 +180,7 @@ Item {
178180
179181 keyPress(data.tag, Qt.AltModifier, 100);
180182 tryCompare(priv, "currentItem", menuItem);
183+ keyRelease(data.tag, Qt.AltModifier, 100);
181184 }
182185
183186 function test_menuActivateClosesMenu() {
tests/qmltests/Greeter/tst_WideView.qml+4
@@ -355,6 +355,7 @@ StyledItem {
355355 loader.active = true;
356356 tryCompare(loader, "status", Loader.Ready);
357357 removeTimeConstraintsFromSwipeAreas(loader.item);
358+ LightDMController.sessionMode = "single";
358359 }
359360
360361 function getIndexOf(name) {
@@ -418,6 +419,7 @@ StyledItem {
418419 }
419420
420421 function test_sessionIconsAreValid() {
422+ LightDMController.sessionMode = "full";
421423 selectUser("has-password");
422424
423425 // Test the login list icon is valid
@@ -439,6 +441,7 @@ StyledItem {
439441 // Ensure the default session is selected (Ubuntu)
440442 cleanup();
441443
444+ LightDMController.sessionMode = "full";
442445 selectUser("has-password");
443446
444447 var sessionChooserButton = findChild(view, "sessionChooserButton");
@@ -484,6 +487,7 @@ StyledItem {
484487 }
485488
486489 function test_sessionIconShownWithMultipleSessions() {
490+ LightDMController.sessionMode = "full";
487491 selectUser("has-password");
488492
489493 var sessionChooserButton = findChild(view, "sessionChooserButton");
tests/qmltests/Launcher/tst_Drawer.qml+17
@@ -312,6 +312,23 @@ StyledItem {
312312 compare(launcher.lastSelectedApplication, "calendar-app");
313313 }
314314
315+ function test_focusAppFromLauncherWhileDrawerIsOpen() {
316+ // FIXME: Skipping this test because of a bug in Qt. Please enable
317+ // once https://codereview.qt-project.org/#/c/184942/ is accepted
318+ skip();
319+
320+ launcher.openDrawer(true);
321+ waitForRendering(launcher);
322+ waitUntilTransitionsEnd(launcher);
323+
324+ var appIcon = findChild(launcher, "launcherDelegate4")
325+
326+ mouseMove(launcher, units.gu(4), launcher.height / 2)
327+ mouseClick(appIcon, appIcon.width / 2, appIcon.height / 2);
328+
329+ tryCompare(launcher, "state", "visibleTemporary");
330+ }
331+
315332 function test_focusMovesCorrectlyBetweenLauncherAndDrawer() {
316333 var panel = findChild(launcher, "launcherPanel");
317334 var drawer = findChild(launcher, "drawer");
tests/qmltests/Panel/tst_Panel.qml+6−3
@@ -272,6 +272,7 @@ PanelTest {
272272 }
273273
274274 function cleanup() {
275+ panel.hasKeyboard = false;
275276 panel.indicators.hide();
276277 panel.applicationMenus.hide();
277278 waitForAllAnimationToComplete("initial");
@@ -713,8 +714,8 @@ PanelTest {
713714 { tag: "No keyboard, no keymap", keyboard: false, keymaps: [], hidden: true },
714715 { tag: "No keyboard, one keymap", keyboard: false, keymaps: ["us"], hidden: true },
715716 { tag: "No keyboard, 2 keymaps", keyboard: false, keymaps: ["us", "cs"], hidden: true },
716− { tag: "Keyboard, no keymap", keyboard: true, keymaps: [], hidden: true },
717− { tag: "Keyboard, one keymap", keyboard: true, keymaps: ["us"], hidden: true },
717+ { tag: "Keyboard, no keymap", keyboard: true, keymaps: [], hidden: false },
718+ { tag: "Keyboard, one keymap", keyboard: true, keymaps: ["us"], hidden: false },
718719 { tag: "Keyboard, 2 keymaps", keyboard: true, keymaps: ["us", "cs"], hidden: false }
719720 ];
720721 }
@@ -722,6 +723,7 @@ PanelTest {
722723 function test_hidingKeyboardIndicator(data) {
723724 var item = findChild(panel, "indicator-keyboard-panelItem");
724725 AccountsService.keymaps = data.keymaps;
726+ panel.hasKeyboard = data.keyboard;
725727 if (data.keyboard) {
726728 MockInputDeviceBackend.addMockDevice("/indicator_kbd0", InputInfo.Keyboard);
727729 } else {
@@ -734,11 +736,12 @@ PanelTest {
734736 function test_visibleIndicators_data() {
735737 return [
736738 { visible: [true, false, true, false, true, true, false, true] },
737− { visible: [false, false, false, false, false, false, true, false] }
739+ { visible: [true, false, false, false, false, false, true, false] }
738740 ];
739741 }
740742
741743 function test_visibleIndicators(data) {
744+ panel.hasKeyboard = true;
742745 for (var i = 0; i < data.visible.length; i++) {
743746 var visible = data.visible[i];
744747 root.setIndicatorVisible(i, visible);
tests/qmltests/Stage/tst_DesktopStage.qml+47−3
@@ -321,7 +321,7 @@ Item {
321321 var maximizeButton = findChild(appDelegate, "maximizeWindowButton");
322322 verify(maximizeButton);
323323 mouseClick(maximizeButton);
324− tryCompare(appDelegate, "visuallyMaximized", true);
324+ tryCompare(appDelegate, "maximized", true);
325325 }
326326
327327 function test_tappingOnDecorationFocusesApplication(data) {
@@ -637,7 +637,7 @@ Item {
637637 var dialerAppDelegate = startApplication("dialer-app");
638638
639639 // verify the drop shadow becomes visible
640− verify(PanelState.dropShadow == true);
640+ tryCompareFunction(function() { return PanelState.dropShadow; }, true);
641641
642642 // close the maximized app
643643 ApplicationManager.stopApplication("facebook-webapp");
@@ -771,6 +771,32 @@ Item {
771771 tryCompareFunction(function(){return posBefore == posAfter;}, data.button !== Qt.LeftButton ? true : false);
772772 }
773773
774+ function test_spreadDisablesWindowDrag() {
775+ var appDelegate = startApplication("dialer-app");
776+ verify(appDelegate);
777+ var decoration = findChild(appDelegate, "appWindowDecoration");
778+ verify(decoration);
779+
780+ // grab the decoration
781+ mousePress(decoration);
782+
783+ // enter the spread
784+ keyPress(Qt.Key_W, Qt.MetaModifier)
785+ tryCompare(stage, "state", "spread");
786+
787+ // try to drag the window
788+ mouseMove(decoration, 10, 10, 200);
789+
790+ // verify it's not moving even before we release the decoration drag
791+ tryCompare(appDelegate, "dragging", false);
792+
793+ // cleanup
794+ mouseRelease(decoration);
795+ keyRelease(Qt.Key_W, Qt.MetaModifier);
796+ stage.closeSpread();
797+ tryCompare(stage, "state", "windowed");
798+ }
799+
774800 // regression test for https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1627281
775801 function test_doubleTapToMaximizeWindow() {
776802 var dialerAppDelegate = startApplication("dialer-app");
@@ -785,7 +811,7 @@ Item {
785811 waitUntilTransitionsEnd(dialerAppDelegate);
786812 waitUntilTransitionsEnd(stage);
787813
788− tryCompare(dialerAppDelegate, "state", "maximized");
814+ tryCompare(dialerAppDelegate, "maximized", true);
789815 }
790816
791817 function test_saveRestoreSize() {
@@ -1013,5 +1039,23 @@ Item {
10131039 expectFail("", "Hovering the window controls should be ignored when the menu is open");
10141040 tryCompare(closeButton, "containsMouse", true);
10151041 }
1042+
1043+ function test_windowControlsTouchInteractionWithMenu() {
1044+ var appDelegate = startApplication("gmail-webapp");
1045+
1046+ var wd = findChild(appDelegate, "appWindowDecoration");
1047+ var maxButton = findChild(wd, "maximizeWindowButton");
1048+ var menuBarLoader = findChild(wd, "menuBarLoader");
1049+ var menuNav = findInvisibleChild(menuBarLoader, "d");
1050+
1051+ // make the menubar active and visible, select first item
1052+ menuBarLoader.active = true;
1053+ menuNav.select(0);
1054+ tryCompare(menuBarLoader.item, "visible", true);
1055+
1056+ // verify the maximized button can still be tapped
1057+ tap(maxButton);
1058+ tryCompare(appDelegate, "state", "maximized");
1059+ }
10161060 }
10171061}
tests/qmltests/Tutorial/tst_Tutorial.qml+1
@@ -149,6 +149,7 @@ Rectangle {
149149 native_: shellLoader.nativeOrientation
150150 primary: shellLoader.primaryOrientation
151151 }
152+ hasTouchscreen: true
152153
153154 Component.onDestruction: {
154155 shellLoader.itemDestroyed = true;
tests/qmltests/tst_OrientedShell.qml+17−3
@@ -695,9 +695,11 @@ Rectangle {
695695 // false -> true -> false
696696 compare(transitionSpy.count, 2);
697697
698− // It should retain native dimensions regardless of its rotation/orientation
699− compare(cameraSurface.width, orientedShell.width);
700− compare(cameraSurface.height, orientedShell.height);
698+ if (!data.windowed) { // subject to shell-chrome policies
699+ // It should retain native dimensions regardless of its rotation/orientation
700+ compare(cameraSurface.width, orientedShell.width);
701+ compare(cameraSurface.height, orientedShell.height);
702+ }
701703
702704 // Surface focus shouldn't have been touched because of the rotation
703705 compare(focusChangedSpy.count, 0);
@@ -1641,5 +1643,17 @@ Rectangle {
16411643 tryCompare(dialogLoader, "item", null);
16421644 compare(window.activeFocusItem, surfaceItem);
16431645 }
1646+
1647+ function test_tutorialDisabledWithNoTouchscreen() {
1648+ loadShell("desktop");
1649+ usageModeSelector.selectWindowed();
1650+
1651+ MockInputDeviceBackend.addMockDevice("/touchscreen", InputInfo.TouchScreen);
1652+ var tutorial = findChild(shell, "tutorial");
1653+ tryCompare(tutorial, "paused", false);
1654+
1655+ MockInputDeviceBackend.removeDevice("/touchscreen");
1656+ tryCompare(tutorial, "paused", true);
1657+ }
16441658 }
16451659}
tests/qmltests/tst_Shell.qml+76−22
@@ -137,6 +137,7 @@ Rectangle {
137137 primary: shellLoader.primaryOrientation
138138 }
139139 mode: shellLoader.mode
140+ hasTouchscreen: true
140141 Component.onCompleted: {
141142 ApplicationManager.startApplication("unity8-dash");
142143 }
@@ -334,7 +335,7 @@ Rectangle {
334335
335336 Row {
336337 CheckBox {
337− id: fullscreeAppCheck
338+ id: fullscreenAppCheck
338339 activeFocusOnPress: false
339340 activeFocusOnTab: false
340341
@@ -348,13 +349,10 @@ Rectangle {
348349 }
349350
350351 Binding {
351− target: fullscreeAppCheck
352+ target: fullscreenAppCheck
352353 when: topLevelSurfaceList && topLevelSurfaceList.focusedWindow
353354 property: "checked"
354− value: {
355− if (!topLevelSurfaceList || !topLevelSurfaceList.focusedWindow) return false;
356− return topLevelSurfaceList.focusedWindow.state === Mir.FullscreenState
357− }
355+ value: topLevelSurfaceList.focusedWindow.state === Mir.FullscreenState
358356 }
359357 }
360358 Label {
@@ -365,6 +363,8 @@ Rectangle {
365363 Row {
366364 CheckBox {
367365 id: chromeAppCheck
366+ activeFocusOnPress: false
367+ activeFocusOnTab: false
368368
369369 onTriggered: {
370370 if (!topLevelSurfaceList.focusedWindow || !topLevelSurfaceList.focusedWindow.surface) return;
@@ -380,10 +380,8 @@ Rectangle {
380380 target: chromeAppCheck
381381 when: topLevelSurfaceList && topLevelSurfaceList.focusedWindow !== null && topLevelSurfaceList.focusedWindow.surface !== null
382382 property: "checked"
383− value: {
384− if (!topLevelSurfaceList || !topLevelSurfaceList.focusedWindow || !topLevelSurfaceList.focusedWindow.surface) return false;
385− topLevelSurfaceList.focusedWindow.surface.shellChrome === Mir.LowChrome
386− }
383+ value: topLevelSurfaceList.focusedWindow.surface &&
384+ topLevelSurfaceList.focusedWindow.surface.shellChrome === Mir.LowChrome
387385 }
388386 }
389387 Label {
@@ -2294,7 +2292,6 @@ Rectangle {
22942292 return [
22952293 { tag: "phone" },
22962294 { tag: "tablet" },
2297− { tag: "desktop" },
22982295 ]
22992296 }
23002297
@@ -2674,8 +2671,8 @@ Rectangle {
26742671 }
26752672
26762673 function test_cursorHidingWithFullscreenApp() {
2677− loadShell("desktop");
2678− shell.usageScenario = "desktop";
2674+ loadShell("phone");
2675+ shell.usageScenario = "phone";
26792676 waitForRendering(shell);
26802677 swipeAwayGreeter();
26812678
@@ -2737,14 +2734,14 @@ Rectangle {
27372734 GSettingsController.setEnableIndicatorMenu(true);
27382735 }
27392736
2740− function test_spreadDisabled_data() {
2737+ function test_spreadDisabled_WithSwipe_data() {
27412738 return [
27422739 { tag: "enabled", spreadEnabled: true },
27432740 { tag: "disabled", spreadEnabled: false }
27442741 ];
27452742 }
27462743
2747− function test_spreadDisabled(data) {
2744+ function test_spreadDisabled_WithSwipe(data) {
27482745 loadShell("phone");
27492746 swipeAwayGreeter();
27502747 var stage = findChild(shell, "stage");
@@ -2753,9 +2750,20 @@ Rectangle {
27532750 // Try swiping
27542751 touchFlick(shell, shell.width - 2, shell.height / 2, units.gu(1), shell.height / 2);
27552752 tryCompare(stage, "state", data.spreadEnabled ? "spread" : "staged");
2753+ }
2754+
2755+ function test_spreadDisabled_WithEdgePush_data() {
2756+ return [
2757+ { tag: "enabled", spreadEnabled: true },
2758+ { tag: "disabled", spreadEnabled: false }
2759+ ];
2760+ }
27562761
2757− stage.closeSpread();
2758− tryCompare(stage, "state", "staged");
2762+ function test_spreadDisabled_WithEdgePush(data) {
2763+ loadShell("phone");
2764+ swipeAwayGreeter();
2765+ var stage = findChild(shell, "stage");
2766+ stage.spreadEnabled = data.spreadEnabled;
27592767
27602768 // Try by edge push
27612769 var cursor = findChild(shell, "cursor");
@@ -2765,21 +2773,43 @@ Rectangle {
27652773 }
27662774 mouseMove(stage, stage.width - units.gu(5), units.gu(10));
27672775 tryCompare(stage, "state", data.spreadEnabled ? "spread" : "staged");
2776+ }
27682777
2769− stage.closeSpread();
2770− tryCompare(stage, "state", "staged");
2778+ function test_spreadDisabled_WithAltTab_data() {
2779+ return [
2780+ { tag: "enabled", spreadEnabled: true },
2781+ { tag: "disabled", spreadEnabled: false }
2782+ ];
2783+ }
2784+
2785+ function test_spreadDisabled_WithAltTab(data) {
2786+ loadShell("phone");
2787+ swipeAwayGreeter();
2788+ var stage = findChild(shell, "stage");
2789+ stage.spreadEnabled = data.spreadEnabled;
27712790
27722791 // Try by alt+tab
27732792 keyPress(Qt.Key_Alt);
27742793 keyClick(Qt.Key_Tab);
27752794 tryCompare(stage, "state", data.spreadEnabled ? "spread" : "staged");
27762795 keyRelease(Qt.Key_Alt);
2796+ }
27772797
2778− stage.closeSpread();
2779− tryCompare(stage, "state", "staged");
2798+ function test_spreadDisabled_WithSuperW_data() {
2799+ return [
2800+ { tag: "enabled", spreadEnabled: true },
2801+ { tag: "disabled", spreadEnabled: false }
2802+ ];
2803+ }
2804+
2805+ function test_spreadDisabled_WithSuperW(data) {
2806+ loadShell("phone");
2807+ swipeAwayGreeter();
2808+ var stage = findChild(shell, "stage");
2809+ stage.spreadEnabled = data.spreadEnabled;
27802810
27812811 // Try by Super+W
2782− keyPress(Qt.Key_W, Qt.MetaModifier)
2812+ keyPress(Qt.Key_W, Qt.MetaModifier, 200);
27832813 tryCompare(stage, "state", data.spreadEnabled ? "spread" : "staged");
27842814 keyRelease(Qt.Key_W, Qt.MetaModifier)
27852815 }
@@ -2910,5 +2940,29 @@ Rectangle {
29102940 tryCompare(topLevelSurfaceList.focusedWindow, "surface", appDelegate.surface);
29112941 tryCompare(topLevelSurfaceList.applicationAt(0), "appId", "dialer-app");
29122942 }
2943+
2944+ function test_doubleClickPanelRestoresWindow() {
2945+ loadShell("desktop");
2946+ shell.usageScenario = "desktop";
2947+ waitForRendering(shell);
2948+ swipeAwayGreeter();
2949+
2950+ // start dialer
2951+ var appDelegate = startApplication("dialer-app")
2952+ verify(appDelegate);
2953+ tryCompare(appDelegate, "state", "normal");
2954+
2955+ // maximize dialer
2956+ var decoration = findChild(appDelegate, "appWindowDecoration");
2957+ verify(decoration);
2958+ mouseDoubleClickSequence(decoration);
2959+ tryCompare(appDelegate, "state", "maximized");
2960+
2961+ // double click the panel
2962+ var panel = findChild(shell, "panel");
2963+ verify(panel);
2964+ mouseDoubleClickSequence(panel, panel.width/2, PanelState.panelHeight/2, Qt.LeftButton, Qt.NoModifier, 300);
2965+ tryCompare(appDelegate, "state", "restored");
2966+ }
29132967 }
29142968}
tests/qmltests/tst_ShellWithPin.qml+1
@@ -69,6 +69,7 @@ Item {
6969 property bool itemDestroyed: false
7070 sourceComponent: Component {
7171 Shell {
72+ hasTouchscreen: true
7273 Component.onDestruction: {
7374 shellLoader.itemDestroyed = true
7475 }
tests/scripts/alert-launcher-icon.sh+16
@@ -1,3 +1,19 @@
11#!/bin/sh
22
3+#
4+# Copyright (C) 2017 Canonical Ltd
5+#
6+# This program is free software: you can redistribute it and/or modify
7+# it under the terms of the GNU General Public License version 3 as
8+# published by the Free Software Foundation.
9+#
10+# This program is distributed in the hope that it will be useful,
11+# but WITHOUT ANY WARRANTY; without even the implied warranty of
12+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+# GNU General Public License for more details.
14+#
15+# You should have received a copy of the GNU General Public License
16+# along with this program. If not, see <http://www.gnu.org/licenses/>.
17+#
18+
319qdbus com.canonical.Unity.Launcher /com/canonical/Unity/Launcher/$1 com.canonical.Unity.Launcher.Item.Alert
tests/scripts/get-progress.sh+16
@@ -1,3 +1,19 @@
11#!/bin/sh
22
3+#
4+# Copyright (C) 2017 Canonical Ltd
5+#
6+# This program is free software: you can redistribute it and/or modify
7+# it under the terms of the GNU General Public License version 3 as
8+# published by the Free Software Foundation.
9+#
10+# This program is distributed in the hope that it will be useful,
11+# but WITHOUT ANY WARRANTY; without even the implied warranty of
12+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+# GNU General Public License for more details.
14+#
15+# You should have received a copy of the GNU General Public License
16+# along with this program. If not, see <http://www.gnu.org/licenses/>.
17+#
18+
319qdbus com.canonical.Unity.Launcher /com/canonical/Unity/Launcher/$1 org.freedesktop.DBus.Properties.Get com.canonical.Unity.Launcher.Item progress
tests/scripts/list-launcher-icons.sh+16
@@ -1,3 +1,19 @@
11#!/bin/sh
22
3+#
4+# Copyright (C) 2017 Canonical Ltd
5+#
6+# This program is free software: you can redistribute it and/or modify
7+# it under the terms of the GNU General Public License version 3 as
8+# published by the Free Software Foundation.
9+#
10+# This program is distributed in the hope that it will be useful,
11+# but WITHOUT ANY WARRANTY; without even the implied warranty of
12+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+# GNU General Public License for more details.
14+#
15+# You should have received a copy of the GNU General Public License
16+# along with this program. If not, see <http://www.gnu.org/licenses/>.
17+#
18+
319qdbus com.canonical.Unity | grep Launcher | cut -f6 -d/
tests/scripts/set-count-visible.sh+16
@@ -1,3 +1,19 @@
11#!/bin/sh
22
3+#
4+# Copyright (C) 2017 Canonical Ltd
5+#
6+# This program is free software: you can redistribute it and/or modify
7+# it under the terms of the GNU General Public License version 3 as
8+# published by the Free Software Foundation.
9+#
10+# This program is distributed in the hope that it will be useful,
11+# but WITHOUT ANY WARRANTY; without even the implied warranty of
12+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+# GNU General Public License for more details.
14+#
15+# You should have received a copy of the GNU General Public License
16+# along with this program. If not, see <http://www.gnu.org/licenses/>.
17+#
18+
319qdbus com.canonical.Unity.Launcher /com/canonical/Unity/Launcher/$1 org.freedesktop.DBus.Properties.Set com.canonical.Unity.Launcher.Item countVisible $2
tests/scripts/set-count.sh+16
@@ -1,3 +1,19 @@
11#!/bin/sh
22
3+#
4+# Copyright (C) 2017 Canonical Ltd
5+#
6+# This program is free software: you can redistribute it and/or modify
7+# it under the terms of the GNU General Public License version 3 as
8+# published by the Free Software Foundation.
9+#
10+# This program is distributed in the hope that it will be useful,
11+# but WITHOUT ANY WARRANTY; without even the implied warranty of
12+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+# GNU General Public License for more details.
14+#
15+# You should have received a copy of the GNU General Public License
16+# along with this program. If not, see <http://www.gnu.org/licenses/>.
17+#
18+
319qdbus com.canonical.Unity.Launcher /com/canonical/Unity/Launcher/$1 org.freedesktop.DBus.Properties.Set com.canonical.Unity.Launcher.Item count $2
tests/scripts/set-progress.sh+16
@@ -1,3 +1,19 @@
11#!/bin/sh
22
3+#
4+# Copyright (C) 2017 Canonical Ltd
5+#
6+# This program is free software: you can redistribute it and/or modify
7+# it under the terms of the GNU General Public License version 3 as
8+# published by the Free Software Foundation.
9+#
10+# This program is distributed in the hope that it will be useful,
11+# but WITHOUT ANY WARRANTY; without even the implied warranty of
12+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+# GNU General Public License for more details.
14+#
15+# You should have received a copy of the GNU General Public License
16+# along with this program. If not, see <http://www.gnu.org/licenses/>.
17+#
18+
319qdbus com.canonical.Unity.Launcher /com/canonical/Unity/Launcher/$1 org.freedesktop.DBus.Properties.Set com.canonical.Unity.Launcher.Item progress $2
tools/CMakeLists.txt+2−12
@@ -1,14 +1,4 @@
1−add_executable(${SCOPE_TOOL}
2− scopetool.cpp
3− registry-tracker.cpp
4− ../src/UnixSignalHandler.cpp
5− )
6−
7−qt5_use_modules(${SCOPE_TOOL} Qml Quick)
8−
9−# install binaries
10−install(TARGETS ${SCOPE_TOOL}
11− RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
12− )
1+add_subdirectory(menutool)
2+add_subdirectory(scopetool)
133
144install(FILES unlock-device DESTINATION ${SHELL_APP_DIR})
tools/menutool/CMakeLists.txtnew file+5
@@ -0,0 +1,5 @@
1+add_executable(menutool
2+ menutool.cpp
3+ )
4+
5+qt5_use_modules(menutool Qml Quick)
tools/menutool/READMEnew file+11
@@ -0,0 +1,11 @@
1+This tool can be used to browse the menus of another application
2+
3+How to use:
4+ * Start application that exports menus
5+ e.g. QT_QPA_PLATFORMTHEME=ubuntuappmenu kate
6+ * Figure out which is the dbus address
7+ * This is a bit tricky, but using qdbusviewer to see the addresses
8+ that appear when starting the app and looking for one that
9+ contains com/Ubuntu/Menu/0 should be enough
10+ * Start tool passing the dbus address
11+ builddir/menutool/menutool :1.293
tools/menutool/menutool.cppnew file+62
@@ -0,0 +1,62 @@
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+// Qt
18+#include <QDebug>
19+#include <QCommandLineParser>
20+#include <QGuiApplication>
21+#include <QQmlContext>
22+#include <QQmlEngine>
23+#include <QQuickView>
24+
25+// local
26+#include <paths.h>
27+
28+int main(int argc, char *argv[])
29+{
30+ QGuiApplication::setApplicationName("Menu Test Tool");
31+ QGuiApplication *application = new QGuiApplication(argc, argv);
32+
33+ QCommandLineParser parser;
34+ parser.parse(application->arguments());
35+ const QStringList args = parser.positionalArguments();
36+
37+ if (args.count() != 1) {
38+ qWarning() << "You need to pass the dbus address";
39+ return 1;
40+ }
41+
42+ QQuickView* view = new QQuickView();
43+ view->setResizeMode(QQuickView::SizeRootObjectToView);
44+ view->setTitle(QGuiApplication::applicationName());
45+ view->engine()->setBaseUrl(QUrl::fromLocalFile(::qmlDirectory()));
46+ view->rootContext()->setContextProperty("contextBusName", args[0]);
47+
48+ QUrl source(::sourceDirectory() + "/tools/menutool/menutool.qml");
49+ prependImportPaths(view->engine(), ::overrideImportPaths());
50+ prependImportPaths(view->engine(), ::nonMirImportPaths());
51+
52+ view->setSource(source);
53+
54+ view->show();
55+
56+ int result = application->exec();
57+
58+ delete view;
59+ delete application;
60+
61+ return result;
62+}
tools/menutool/menutool.qmlnew file+118
@@ -0,0 +1,118 @@
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 Ubuntu.Components 1.3
19+
20+import "../../qml/ApplicationMenus"
21+import "../../qml/Panel"
22+import "../../qml/Components/PanelState"
23+
24+import QMenuModel 0.1
25+import Unity.Indicators 0.1 as Indicators
26+
27+Item {
28+ width: units.gu(180)
29+ height: units.gu(120)
30+
31+ UnityMenuModel {
32+ id: menuModel
33+ busName: contextBusName
34+ menuObjectPath: "/com/ubuntu/Menu/0"
35+ actions: { "unity": "/com/ubuntu/Menu/0" }
36+ }
37+
38+ readonly property bool hasMenus: repeater.count > 0
39+ Repeater {
40+ id: repeater
41+ model: menuModel
42+ delegate: Item {}
43+ }
44+
45+ Panel {
46+ id: panel
47+
48+ height: parent.height
49+ width: parent.width / 2
50+ minimizedPanelHeight: units.gu(6)
51+ visible: hasMenus
52+
53+ mode: "windowed"
54+
55+ applicationMenus {
56+ model: menuModel
57+ }
58+
59+ Rectangle {
60+ width: 50
61+ height: 50
62+ anchors.centerIn: parent
63+ color: "gray"
64+ rotation: 45
65+ Timer {
66+ interval: 20
67+ running: true
68+ repeat: true
69+ onTriggered: parent.rotation = parent.rotation+1
70+ }
71+ }
72+
73+ Rectangle {
74+ color: "green"
75+ anchors.bottom: parent.bottom
76+ anchors.right: parent.right
77+ Label {
78+ id: label
79+ anchors.centerIn: parent
80+ text: "Click here to open touch menu manually"
81+ }
82+ width: label.width + units.gu(2)
83+ height: label.height + units.gu(2)
84+ MouseArea {
85+ anchors.fill: parent
86+ onClicked: panel.applicationMenus.show();
87+ }
88+ visible: !panel.applicationMenus.shown
89+ }
90+ }
91+
92+ Rectangle {
93+ color: "blue"
94+ height: parent.height
95+ width: parent.width / 2
96+ x: width
97+ visible: hasMenus
98+
99+ MenuBar {
100+ id: menuBar
101+ height: units.gu(3)
102+ width: parent.width
103+ enableKeyFilter: true
104+ unityMenuModel: menuModel
105+ }
106+ }
107+
108+ Component.onCompleted: {
109+ theme.name = "Ubuntu.Components.Themes.SuruDark";
110+ PanelState.title = "Drag here to open touch menu";
111+ }
112+
113+ Text {
114+ anchors.centerIn: parent
115+ text: "The dbus address you gave has no menus. Make sure you read the README file."
116+ visible: !hasMenus
117+ }
118+}
tools/scopetool/CMakeLists.txtnew file+12
@@ -0,0 +1,12 @@
1+add_executable(${SCOPE_TOOL}
2+ scopetool.cpp
3+ registry-tracker.cpp
4+ ../../src/UnixSignalHandler.cpp
5+ )
6+
7+qt5_use_modules(${SCOPE_TOOL} Qml Quick)
8+
9+# install binaries
10+install(TARGETS ${SCOPE_TOOL}
11+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
12+ )
tools/registry-tracker.cpp → tools/scopetool/registry-tracker.cpprenamed

The content is the same.

tools/registry-tracker.h → tools/scopetool/registry-tracker.hrenamed

The content is the same.

tools/scopetool.cpp → tools/scopetool/scopetool.cpprenamed

The content is the same.

tools/unlock-device+16
@@ -1,6 +1,22 @@
11#!/bin/bash
22# -*- Mode: sh; indent-tabs-mode: nil; tab-width: 4 -*-
33
4+#
5+# Copyright (C) 2017 Canonical Ltd
6+#
7+# This program is free software: you can redistribute it and/or modify
8+# it under the terms of the GNU General Public License version 3 as
9+# published by the Free Software Foundation.
10+#
11+# This program is distributed in the hope that it will be useful,
12+# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+# GNU General Public License for more details.
15+#
16+# You should have received a copy of the GNU General Public License
17+# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+#
19+
420# Our goal here is to get an Ubuntu Touch device into a testable unlocked state.
521# This will include a reboot.
622