on xenial help sign in
merge in 2202 as prereq
commitf49d7d0e3311b561c9bbacb58b85dcf8538ac5f7 authorMichael Zanetti <michael.zanetti@canonical.com> dateWed 30 Nov 2016, 11:32 (10 years ago) parent34937079f75d6d
52 files changed+738−69
data/com.canonical.Unity8.gschema.xml+5 debian/changelog+46 debian/control+2−1 plugins/Dash/CardCreator.js+1−1 plugins/Unity/Indicators/indicatorsmanager.cpp+1−4 plugins/Unity/Launcher/launcheritem.cpp+1 plugins/Unity/Launcher/quicklistentry.cpp+11 plugins/Unity/Launcher/quicklistentry.h+4 plugins/Unity/Launcher/quicklistmodel.cpp+2 po/unity8.pot+10−10 qml/Components/BrightnessControl.qml+54 qml/Components/LazyImage.qml+2 qml/Dash/DashPageHeader.qml+1−1 qml/Dash/GenericScopeView.qml+4−2 qml/Dash/PageHeaderExtraPanel.qml+10−11 qml/Dash/Previews/PreviewProgress.qml+1−1 qml/Dash/PullToRefreshScopeStyle.qml+1−1 qml/Dash/ScopesListCategoryItem.qml+1 qml/Dash/graphics/navigation_shadow@18.png qml/Dash/graphics/navigation_shadow_light@18.png qml/Dash/graphics/rectangular_dropshadow@30.png qml/Dash/graphics/rectangular_dropshadow@30.sci+5 qml/Launcher/LauncherDelegate.qml+1 qml/Launcher/LauncherPanel.qml+80−3 qml/Launcher/Tooltip.qml+72 qml/Shell.qml+3−3 qml/Stage/DecoratedWindow.qml+1−1 qml/Stage/MoveHandler.qml+8 qml/Stage/Stage.qml+23−5 qml/Stage/WindowDecoration.qml+4−5 tests/autopilot/unity8/dash.py+3−3 tests/autopilot/unity8/greeter/__init__.py+1−1 tests/autopilot/unity8/shell/__init__.py+2 tests/autopilot/unity8/shell/tests/__init__.py+4−1 tests/autopilot/unity8/shell/tests/test_helpers.py+2−1 tests/autopilot/unity8/shell/tests/test_notifications.py+9−9 tests/mocks/GSettings.1.0/fake_gsettings.cpp+33 tests/mocks/GSettings.1.0/fake_gsettings.h+9 tests/mocks/Unity/Application/CMakeLists.txt+1 tests/mocks/Unity/Application/MirMock.cpp+49 tests/mocks/Unity/Application/MirMock.h+39 tests/mocks/Unity/Application/plugin.cpp+11−2 tests/mocks/Unity/Launcher/MockQuickListModel.cpp+2 tests/mocks/Unity/Screens/CMakeLists.txt+1−1 tests/mocks/Unity/fake_categories.cpp+4 tests/plugins/Dash/cardcreator/4.res+1−1 tests/plugins/Dash/cardcreator/4.res.cardcreator+1−1 tests/qmltests/Dash/tst_GenericScopeView.qml+26 tests/qmltests/Launcher/tst_Launcher.qml+89 tests/qmltests/Panel/tst_IndicatorsMenu.qml+17 tests/qmltests/Stage/tst_DesktopStage.qml+16 tests/qmltests/tst_Shell.qml+64
data/com.canonical.Unity8.gschema.xml+5
@@ -48,6 +48,11 @@
4848 <summary>Enable or disable the launcher</summary>
4949 <description>Toggle the availability of the launcher</description>
5050 </key>
51+ <key type="b" name="enable-indicator-menu">
52+ <default>true</default>
53+ <summary>Enable or disable the indicator pull down menus</summary>
54+ <description>Toggle the availability of the indicator pull down menus</description>
55+ </key>
5156 </schema>
5257
5358 <schema path="/com/canonical/unity8/greeter/" id="com.canonical.Unity8.Greeter" gettext-domain="unity8">
debian/changelog+46
@@ -1,3 +1,49 @@
1+unity8 (8.15+17.04.20161129-0ubuntu1) zesty; urgency=medium
2+
3+ [ Albert Astals Cid ]
4+ * Fix PreviewProgress implicitHeight (LP: #1641943)
5+ * Adapt to dummy notification being gone
6+ * Fix autopilot test_lock_screen tests
7+ * Autopilot: Add more applications to the list for wider screens
8+ * Fix autopilot DashHelperTestCase.test_search
9+ * Also install the Screens mock
10+ * Give default value to gu-px size
11+
12+ [ Andrea Azzarone ]
13+ * Set Mir.cursorName to "grabbing" on first mouse press on a window
14+ decoration. Don't wait for press+motion. (LP: #1618078)
15+ * Do not show a divider between each quicklist entry but just between
16+ each section, similar to how unity7 does. (LP: #1637478)
17+
18+ [ Andrea Cimitan ]
19+ * Fix white text (LP: #1644468)
20+ * Make MascotLoader in CardCreator flat
21+ * Make the remaining UbuntuShape flat
22+ * added dropshadow from panel indicators to dash page header extra
23+ panel, so there is right shadow when the extra panel is narrow
24+ * Look up for expandable template dash category flag
25+
26+ [ Brian Douglass ]
27+ * Added a setting to enable/disable the indicator dropdown menu.
28+
29+ [ Daniel d'Andrada ]
30+ * There's no need for WindowDecoration to access the appDelegate
31+
32+ [ Lukáš Tinkl ]
33+ * Enable brightness (laptop backlight) handling on desktop/laptop PCs
34+ (LP: #1595947)
35+
36+ [ Michael Zanetti ]
37+ * disable spread interaction while locked (LP: #1641578)
38+
39+ [ Andrea Azzarone, Michał Sawicz ]
40+ * Implement launcher tooltips.
41+
42+ [ Olivier Tilloy ]
43+ * Remove dependency on transitional package. (LP: #1583079)
44+
45+ -- Michał Sawicz <michal.sawicz@canonical.com> Tue, 29 Nov 2016 09:43:33 +0000
46+
147unity8 (8.15+17.04.20161116.1-0ubuntu1) zesty; urgency=medium
248
349 [ Marco Trevisan (Treviño) ]
debian/control+2−1
@@ -50,6 +50,7 @@ Build-Depends: cmake,
5050 python3-setuptools,
5151 qml-module-qt-labs-folderlistmodel,
5252 qml-module-qt-labs-settings,
53+ qml-module-qtqml-statemachine,
5354 qml-module-qtmultimedia (>= 5.4.1-1ubuntu19~overlay2),
5455 qml-module-qtquick-layouts,
5556 qml-module-qtquick-xmllistmodel,
@@ -123,12 +124,12 @@ Depends: dmz-cursor-theme,
123124 qml-module-biometryd,
124125 qml-module-qt-labs-folderlistmodel,
125126 qml-module-qt-labs-settings,
127+ qml-module-qtqml-statemachine,
126128 qml-module-qtquick-xmllistmodel,
127129 qml-module-qtsysteminfo,
128130 qml-module-ubuntu-web,
129131 qtdeclarative5-qtmir-plugin (>= 0.4.8),
130132 qtdeclarative5-ubuntu-telephony0.1,
131− qtdeclarative5-ubuntu-web-plugin,
132133 ubuntu-system-settings (>= 0.4),
133134 unity-launcher-impl-10,
134135 unity8-common (= ${source:Version}),
plugins/Dash/CardCreator.js+1−1
@@ -269,7 +269,7 @@ var kMascotShapeLoaderCode = 'Loader { \n\
269269 visible: showHeader && active && status === Loader.Ready; \n\
270270 width: units.gu(6); \n\
271271 height: units.gu(5.625); \n\
272− sourceComponent: UbuntuShape { image: mascotImage } \n\
272+ sourceComponent: UbuntuShape { aspect: UbuntuShape.Flat; image: mascotImage } \n\
273273 anchors { %1 } \n\
274274 }\n';
275275
plugins/Unity/Indicators/indicatorsmanager.cpp+1−4
@@ -282,13 +282,10 @@ Indicator::Ptr IndicatorsManager::indicator(const QString& indicator_name)
282282 // convergence:
283283 // 1) enable session indicator
284284 // 2) enable keyboard indicator
285− // 3) on a PC, switch the battery/power indicator to desktop mode,
286− // can't control brightness for now and phone-on-desktop broken (FIXME)
287285 //
288286 // The rest of the indicators respect their default profile (which is "phone", even on desktop PCs)
289287 if ((new_indicator->identifier() == QStringLiteral("indicator-session"))
290− || new_indicator->identifier() == QStringLiteral("indicator-keyboard")
291− || (new_indicator->identifier() == QStringLiteral("indicator-power") && m_platform.isPC())) {
288+ || new_indicator->identifier() == QStringLiteral("indicator-keyboard")) {
292289 new_indicator->setProfile(QString(m_profile).replace(QStringLiteral("phone"), QStringLiteral("desktop")));
293290 } else {
294291 new_indicator->setProfile(m_profile);
plugins/Unity/Launcher/launcheritem.cpp+1
@@ -42,6 +42,7 @@ LauncherItem::LauncherItem(const QString &appId, const QString &name, const QStr
4242 QuickListEntry nameAction;
4343 nameAction.setActionId(QStringLiteral("launch_item"));
4444 nameAction.setText(m_name);
45+ nameAction.setHasSeparator(true);
4546 m_quickList->appendAction(nameAction);
4647
4748 QuickListEntry pinningAction;
plugins/Unity/Launcher/quicklistentry.cpp+11
@@ -19,6 +19,7 @@
1919#include "quicklistentry.h"
2020
2121QuickListEntry::QuickListEntry()
22+ : m_hasSeparator(false)
2223{
2324}
2425
@@ -57,6 +58,16 @@ bool QuickListEntry::clickable() const
5758 return !m_actionId.isEmpty();
5859}
5960
61+void QuickListEntry::setHasSeparator(bool hasSeparator)
62+{
63+ m_hasSeparator = hasSeparator;
64+}
65+
66+bool QuickListEntry::hasSeparator() const
67+{
68+ return m_hasSeparator;
69+}
70+
6071bool QuickListEntry::operator==(const QuickListEntry &other)
6172{
6273 return !other.actionId().isEmpty() && other.actionId() == m_actionId;
plugins/Unity/Launcher/quicklistentry.h+4
@@ -37,12 +37,16 @@ public:
3737
3838 bool clickable() const;
3939
40+ void setHasSeparator(bool hasSeparator);
41+ bool hasSeparator() const;
42+
4043 bool operator==(const QuickListEntry & other);
4144
4245private:
4346 QString m_actionId;
4447 QString m_text;
4548 QString m_icon;
49+ bool m_hasSeparator;
4650};
4751
4852#endif // QUICKLISTENTRY
plugins/Unity/Launcher/quicklistmodel.cpp+2
@@ -79,6 +79,8 @@ QVariant QuickListModel::data(const QModelIndex &index, int role) const
7979 return m_list.at(index.row()).icon();
8080 case RoleClickable:
8181 return m_list.at(index.row()).clickable();
82+ case RoleHasSeparator:
83+ return m_list.at(index.row()).hasSeparator();
8284 }
8385 return QVariant();
8486}
po/unity8.pot+10−10
@@ -8,7 +8,7 @@ msgid ""
88msgstr ""
99"Project-Id-Version: unity8\n"
1010"Report-Msgid-Bugs-To: \n"
11−"POT-Creation-Date: 2016-11-16 10:44+0000\n"
11+"POT-Creation-Date: 2016-11-29 09:43+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"
@@ -22,16 +22,16 @@ msgstr ""
2222msgid "Password: "
2323msgstr ""
2424
25−#: plugins/Unity/Launcher/launcheritem.cpp:49
26−#: plugins/Unity/Launcher/launcheritem.cpp:108
25+#: plugins/Unity/Launcher/launcheritem.cpp:50
26+#: plugins/Unity/Launcher/launcheritem.cpp:109
2727msgid "Pin shortcut"
2828msgstr ""
2929
30−#: plugins/Unity/Launcher/launcheritem.cpp:54
30+#: plugins/Unity/Launcher/launcheritem.cpp:55
3131msgid "Quit"
3232msgstr ""
3333
34−#: plugins/Unity/Launcher/launcheritem.cpp:108
34+#: plugins/Unity/Launcher/launcheritem.cpp:109
3535msgid "Unpin shortcut"
3636msgstr ""
3737
@@ -342,24 +342,24 @@ msgstr ""
342342msgid "Reset"
343343msgstr ""
344344
345−#: qml/Dash/GenericScopeView.qml:545 qml/Dash/GenericScopeView.qml:716
345+#: qml/Dash/GenericScopeView.qml:547 qml/Dash/GenericScopeView.qml:718
346346msgid "Show less"
347347msgstr ""
348348
349−#: qml/Dash/GenericScopeView.qml:545
349+#: qml/Dash/GenericScopeView.qml:547
350350msgid "Show all"
351351msgstr ""
352352
353−#: qml/Dash/GenericScopeView.qml:576
353+#: qml/Dash/GenericScopeView.qml:578
354354msgctxt "Label: Hint for dash search line edit"
355355msgid "Search"
356356msgstr ""
357357
358−#: qml/Dash/PageHeaderExtraPanel.qml:57
358+#: qml/Dash/PageHeaderExtraPanel.qml:67
359359msgid "Recent Searches"
360360msgstr ""
361361
362−#: qml/Dash/PageHeaderExtraPanel.qml:68
362+#: qml/Dash/PageHeaderExtraPanel.qml:78
363363msgid "Clear All"
364364msgstr ""
365365
qml/Components/BrightnessControl.qmlnew file+54
@@ -0,0 +1,54 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.4
18+import Ubuntu.Components 1.3
19+import QMenuModel 0.1
20+import GlobalShortcut 1.0
21+
22+QtObject {
23+ objectName: "brightnessControl"
24+
25+ property GlobalShortcut brightnessUp: GlobalShortcut {
26+ shortcut: Qt.Key_MonBrightnessUp
27+ onTriggered: stepUp();
28+ }
29+
30+ property GlobalShortcut brightnessDown: GlobalShortcut {
31+ shortcut: Qt.Key_MonBrightnessDown
32+ onTriggered: stepDown();
33+ }
34+
35+ function stepUp() {
36+ actionGroup.brightness.updateState(MathUtils.clamp(actionGroup.brightness.state + 0.1, 0.01, 1.0));
37+ }
38+
39+ function stepDown() {
40+ actionGroup.brightness.updateState(MathUtils.clamp(actionGroup.brightness.state - 0.1, 0.01, 1.0));
41+ }
42+
43+ property QDBusActionGroup actionGroup: QDBusActionGroup {
44+ busType: DBus.SessionBus
45+ busName: "com.canonical.indicator.power"
46+ objectPath: "/com/canonical/indicator/power"
47+
48+ property variant brightness: action("brightness")
49+
50+ Component.onCompleted: {
51+ actionGroup.start();
52+ }
53+ }
54+}
qml/Components/LazyImage.qml+2
@@ -57,6 +57,7 @@ Item {
5757 active: useUbuntuShape
5858 visible: opacity != 0
5959 sourceComponent: UbuntuShape {
60+ aspect: UbuntuShape.Flat
6061 backgroundColor: "#22FFFFFF"
6162 }
6263
@@ -92,6 +93,7 @@ Item {
9293 visible: opacity != 0
9394 sourceComponent: UbuntuShapeOverlay {
9495 property bool pressed: false
96+ aspect: UbuntuShape.Flat
9597 overlayColor: Qt.rgba(0, 0, 0, pressed ? 0.1 : 0)
9698 overlayRect: Qt.rect(0.0, 0.0, 1.0, 1.0)
9799 }
qml/Dash/DashPageHeader.qml+1−1
@@ -26,7 +26,6 @@ Item {
2626 objectName: "pageHeader"
2727 implicitHeight: headerContainer.height + signatureLineHeight
2828 readonly property real signatureLineHeight: showSignatureLine ? units.gu(2.5) : headerBottomLine.height
29− readonly property real headerDividerLuminance: Style.luminance(bottomBorder.color)
3029
3130 property int activeFiltersCount: 0
3231 property bool scopeHasFilters: false
@@ -185,6 +184,7 @@ Item {
185184
186185 Column {
187186 id: headersColumn
187+ objectName: "headersColumn"
188188 anchors {
189189 left: parent.left
190190 right: parent.right
qml/Dash/GenericScopeView.qml+4−2
@@ -243,8 +243,10 @@ FocusScope {
243243
244244 property Item seeAllButton: seeAll
245245
246+ readonly property bool forceNotExpandable: cardTool.template && cardTool.template["expandable"] === false
246247 readonly property bool expandable: {
247248 if (categoryView.model.count === 1) return false;
249+ if (forceNotExpandable) return false;
248250 if (cardTool.template && cardTool.template["collapsed-rows"] === 0) return false;
249251 if (item && item.expandedHeight > item.collapsedHeight) return true;
250252 return false;
@@ -317,7 +319,7 @@ FocusScope {
317319 }
318320 }
319321
320− readonly property bool expanded: baseItem.expanded || !baseItem.expandable
322+ readonly property bool expanded: !baseItem.forceNotExpandable && (baseItem.expanded || !baseItem.expandable)
321323 height: expanded ? item.expandedHeight : item.collapsedHeight
322324
323325 source: {
@@ -657,7 +659,7 @@ FocusScope {
657659 target: categoryView
658660
659661 readonly property real contentY: categoryView.contentY - categoryView.originY
660− readonly property real headerDividerLuminance: categoryView.pageHeader.headerDividerLuminance
662+ readonly property color pullLabelColor: scopeStyle ? scopeStyle.foreground : theme.palette.normal.baseText
661663 y: -contentY - units.gu(5)
662664
663665 onRefresh: {
qml/Dash/PageHeaderExtraPanel.qml+10−11
@@ -43,6 +43,16 @@ Item {
4343 dashNavigation.resetNavigation();
4444 }
4545
46+ BorderImage {
47+ anchors {
48+ fill: parent
49+ leftMargin: -units.gu(1)
50+ rightMargin: -units.gu(1)
51+ bottomMargin: -units.gu(1)
52+ }
53+ source: "graphics/rectangular_dropshadow.sci"
54+ }
55+
4656 Rectangle {
4757 color: "white"
4858 anchors.fill: parent
@@ -188,15 +198,4 @@ Item {
188198 onSingleSelectionFilterSelected: extraPanelOptionSelected()
189199 }
190200 }
191−
192− // This is outside the item
193− Image {
194− anchors {
195− top: parent.bottom
196− left: parent.left
197− right: parent.right
198− }
199− fillMode: Image.Stretch
200− source: "graphics/navigation_shadow.png"
201− }
202201}
qml/Dash/Previews/PreviewProgress.qml+1−1
@@ -28,7 +28,7 @@ import Ubuntu.DownloadDaemonListener 0.1
2828PreviewWidget {
2929 id: root
3030
31− implicitHeight: progressBar.implicitHeight
31+ implicitHeight: progressBar.height
3232 implicitWidth: progressBar.implicitWidth
3333
3434 ProgressBar {
qml/Dash/PullToRefreshScopeStyle.qml+1−1
@@ -48,7 +48,7 @@ PullToRefreshStyle {
4848 anchors.horizontalCenter: parent.horizontalCenter
4949 horizontalAlignment: Text.AlignHCenter
5050 verticalAlignment: Text.AlignVCenter
51− color: styledItem.headerDividerLuminance < 0.3 ? UbuntuColors.porcelain : UbuntuColors.jet
51+ color: styledItem.pullLabelColor
5252 states: [
5353 State {
5454 name: "pulling"
qml/Dash/ScopesListCategoryItem.qml+1
@@ -49,6 +49,7 @@ AbstractButton {
4949 width: units.gu(5)
5050 height: units.gu(5)
5151 visible: !hideChildren
52+ aspect: UbuntuShape.Flat
5253 sourceFillMode: UbuntuShape.PreserveAspectCrop
5354 source: Image {
5455 id: shapeImage
qml/Dash/graphics/navigation_shadow@18.pngdeleted

Binary file not shown.

qml/Dash/graphics/navigation_shadow_light@18.pngdeleted

Binary file not shown.

qml/Dash/graphics/rectangular_dropshadow@30.pngnew file

Binary file not shown.

qml/Dash/graphics/rectangular_dropshadow@30.scinew file+5
@@ -0,0 +1,5 @@
1+border.left: 30
2+border.top: 30
3+border.bottom: 30
4+border.right: 30
5+source: rectangular_dropshadow@30.png
qml/Launcher/LauncherDelegate.qml+1
@@ -22,6 +22,7 @@ Item {
2222
2323 property int itemIndex: 0
2424 property string iconName
25+ property string name
2526 property int count: 0
2627 property bool countVisible: false
2728 property int progress: -1
qml/Launcher/LauncherPanel.qml+80−3
@@ -15,6 +15,7 @@
1515 */
1616
1717import QtQuick 2.4
18+import QtQml.StateMachine 1.0 as DSM
1819import Ubuntu.Components 1.3
1920import Unity.Launcher 0.1
2021import Ubuntu.Components.Popups 1.3
@@ -31,7 +32,7 @@ Rectangle {
3132 property bool dragging: false
3233 property bool moving: launcherListView.moving || launcherListView.flicking
3334 property bool preventHiding: moving || dndArea.draggedIndex >= 0 || quickList.state === "open" || dndArea.pressed
34− || mouseEventEater.containsMouse || dashItem.hovered
35+ || dndArea.containsMouse || dashItem.hovered
3536 property int highlightIndex: -2
3637 property bool shortcutHintsShown: false
3738
@@ -40,7 +41,7 @@ Rectangle {
4041 signal kbdNavigationCancelled()
4142
4243 onXChanged: {
43− if (quickList.state == "open") {
44+ if (quickList.state === "open") {
4445 quickList.state = ""
4546 }
4647 }
@@ -69,7 +70,6 @@ Rectangle {
6970 id: mouseEventEater
7071 anchors.fill: parent
7172 acceptedButtons: Qt.AllButtons
72− hoverEnabled: true
7373 onWheel: wheel.accepted = true;
7474 }
7575
@@ -225,6 +225,7 @@ Rectangle {
225225 // the right app when running autopilot tests for
226226 // multiple apps.
227227 readonly property string appId: model.appId
228+ name: model.name
228229 itemIndex: index
229230 itemHeight: launcherListView.itemHeight
230231 itemWidth: launcherListView.itemWidth
@@ -399,6 +400,7 @@ Rectangle {
399400 id: dndArea
400401 objectName: "dndArea"
401402 acceptedButtons: Qt.LeftButton | Qt.RightButton
403+ hoverEnabled: true
402404 anchors {
403405 fill: parent
404406 topMargin: launcherListView.topMargin
@@ -795,6 +797,7 @@ Rectangle {
795797 highlightColor: !model.clickable ? quickList.color : undefined // make disabled items visually unclickable
796798 divider.colorFrom: UbuntuColors.inkstone
797799 divider.colorTo: UbuntuColors.inkstone
800+ divider.visible: model.hasSeparator
798801
799802 Label {
800803 id: label
@@ -828,4 +831,78 @@ Rectangle {
828831 }
829832 }
830833 }
834+
835+ Tooltip {
836+ id: tooltipShape
837+ objectName: "tooltipShape"
838+
839+ visible: tooltipShownState.active
840+ rotation: root.rotation
841+ y: itemCenter - (height / 2)
842+
843+ anchors {
844+ left: root.inverted ? undefined : parent.right
845+ right: root.inverted ? parent.left : undefined
846+ margins: units.gu(1)
847+ }
848+
849+ readonly property var hoveredItem: dndArea.containsMouse ? launcherListView.itemAt(dndArea.mouseX, dndArea.mouseY + launcherListView.realContentY) : null
850+ readonly property int itemCenter: !hoveredItem ? 0 : root.mapFromItem(hoveredItem, 0, 0).y + (hoveredItem.height / 2) + hoveredItem.offset
851+
852+ text: !hoveredItem ? "" : hoveredItem.name
853+ }
854+
855+ DSM.StateMachine {
856+ id: tooltipStateMachine
857+ initialState: tooltipHiddenState
858+ running: true
859+
860+ DSM.State {
861+ id: tooltipHiddenState
862+
863+ DSM.SignalTransition {
864+ targetState: tooltipShownState
865+ signal: tooltipShape.hoveredItemChanged
866+ // !dndArea.pressed allows us to filter out touch input events
867+ guard: tooltipShape.hoveredItem !== null && !dndArea.pressed && !root.moving
868+ }
869+ }
870+
871+ DSM.State {
872+ id: tooltipShownState
873+
874+ DSM.SignalTransition {
875+ targetState: tooltipHiddenState
876+ signal: tooltipShape.hoveredItemChanged
877+ guard: tooltipShape.hoveredItem === null
878+ }
879+
880+ DSM.SignalTransition {
881+ targetState: tooltipDismissedState
882+ signal: dndArea.onPressed
883+ }
884+
885+ DSM.SignalTransition {
886+ targetState: tooltipDismissedState
887+ signal: quickList.stateChanged
888+ guard: quickList.state === "open"
889+ }
890+ }
891+
892+ DSM.State {
893+ id: tooltipDismissedState
894+
895+ DSM.SignalTransition {
896+ targetState: tooltipHiddenState
897+ signal: dndArea.positionChanged
898+ guard: quickList.state != "open" && !dndArea.pressed && !dndArea.moving
899+ }
900+
901+ DSM.SignalTransition {
902+ targetState: tooltipHiddenState
903+ signal: dndArea.exited
904+ guard: quickList.state != "open"
905+ }
906+ }
907+ }
831908}
qml/Launcher/Tooltip.qmlnew file+72
@@ -0,0 +1,72 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.4
18+import Ubuntu.Components 1.3
19+
20+UbuntuShape {
21+ id: root
22+
23+ // This property holds the delay (milliseconds) after which the tool tip is shown.
24+ // A tooltip with a negative delay is shown immediately. The default value is 500 ms.
25+ property int delay: 500
26+
27+ // This property holds the text shown on the tool tip.
28+ property alias text: label.text
29+
30+ aspect: UbuntuShape.Flat
31+ color: theme.palette.normal.background
32+ width: label.implicitWidth + units.gu(4)
33+ height: label.implicitHeight + units.gu(2)
34+ opacity: 0
35+
36+ Image {
37+ anchors {
38+ right: parent.left
39+ rightMargin: -units.dp(4)
40+ verticalCenter: parent.verticalCenter
41+ }
42+ source: "graphics/quicklist_tooltip.png"
43+ rotation: 90
44+ }
45+
46+ Label {
47+ id: label
48+ anchors.centerIn: parent
49+ color: theme.palette.normal.backgroundText
50+ }
51+
52+ states: [
53+ State {
54+ name: "visible"
55+ when: root.visible
56+ PropertyChanges {
57+ target: root
58+ opacity: 0.95
59+ }
60+ }
61+ ]
62+
63+ transitions: [
64+ Transition {
65+ from: ""; to: "visible"
66+ SequentialAnimation {
67+ PauseAnimation { duration: root.delay }
68+ UbuntuNumberAnimation { target: root; property: "opacity"; duration: UbuntuAnimation.BriskDuration }
69+ }
70+ }
71+ ]
72+}
qml/Shell.qml+3−3
@@ -307,8 +307,6 @@ StyledItem {
307307 suspended: greeter.shown
308308 altTabPressed: physicalKeysMapper.altTabPressed
309309 oskEnabled: shell.oskEnabled
310−
311− // TODO: This is not implemented yet in the new stage...
312310 spreadEnabled: tutorial.spreadEnabled && (!greeter || (!greeter.hasLockedApp && !greeter.shown))
313311 }
314312 }
@@ -447,6 +445,7 @@ StyledItem {
447445 && ((!greeter || !greeter.locked) || AccountsService.enableIndicatorsWhileLocked)
448446 && (!greeter || !greeter.hasLockedApp)
449447 && !shell.waitingOnGreeter
448+ && settings.enableIndicatorMenu
450449 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
451450
452451 minimizedPanelHeight: units.gu(3)
@@ -762,8 +761,9 @@ StyledItem {
762761 Behavior on opacity { UbuntuNumberAnimation {} }
763762 }
764763
765− // non-visual object
764+ // non-visual objects
766765 KeymapSwitcher {}
766+ BrightnessControl {}
767767
768768 Rectangle {
769769 id: shutdownFadeOutRectangle
qml/Stage/DecoratedWindow.qml+1−1
@@ -149,7 +149,7 @@ FocusScope {
149149
150150 WindowDecoration {
151151 id: decoration
152− target: root.parent || null
152+ closeButtonVisible: root.application.appId !== "unity8-dash"
153153 objectName: "appWindowDecoration"
154154 anchors { left: parent.left; top: parent.top; right: parent.right }
155155 height: units.gu(3)
qml/Stage/MoveHandler.qml+8
@@ -54,6 +54,11 @@ QtObject {
5454 property bool nearBottomLeftCorner: target.maximizedBottomLeft
5555 property bool nearBottomRightCorner: target.maximizedBottomRight
5656
57+ property Timer mouseDownTimer: Timer {
58+ interval: 175
59+ onTriggered: Mir.cursorName = "grabbing"
60+ }
61+
5762 function resetEdges() {
5863 nearLeftEdge = false;
5964 nearRightEdge = false;
@@ -93,14 +98,17 @@ QtObject {
9398 }
9499
95100 priv.dragging = true;
101+ priv.mouseDownTimer.start();
96102 } else {
97103 priv.dragging = false;
104+ priv.mouseDownTimer.stop();
98105 Mir.cursorName = "";
99106 }
100107 }
101108
102109 function handlePositionChanged(mouse, sensingPoints) {
103110 if (priv.dragging) {
111+ priv.mouseDownTimer.stop();
104112 Mir.cursorName = "grabbing";
105113
106114 // restore from maximized when dragging away from edges/corners; guard against inadvertent changes when going into maximized state
qml/Stage/Stage.qml+23−5
@@ -90,7 +90,9 @@ FocusScope {
9090
9191 onAltTabPressedChanged: {
9292 if (altTabPressed) {
93− altTabDelayTimer.start();
93+ if (root.spreadEnabled) {
94+ altTabDelayTimer.start();
95+ }
9496 } else {
9597 // Alt Tab has been released, did we already go to spread?
9698 if (priv.goneToSpread) {
@@ -125,7 +127,9 @@ FocusScope {
125127 function updateFocusedAppOrientation() { /* TODO */ }
126128 function updateFocusedAppOrientationAnimated() { /* TODO */}
127129 function pushRightEdge(amount) {
128− edgeBarrier.push(amount);
130+ if (root.spreadEnabled) {
131+ edgeBarrier.push(amount);
132+ }
129133 }
130134
131135 function closeSpread() {
@@ -166,6 +170,7 @@ FocusScope {
166170 GlobalShortcut {
167171 id: showSpreadShortcut
168172 shortcut: Qt.MetaModifier|Qt.Key_W
173+ active: root.spreadEnabled
169174 onTriggered: priv.goneToSpread = true
170175 }
171176
@@ -1526,9 +1531,21 @@ FocusScope {
15261531 onRequestedHeightChanged: oldRequestedHeight = requestedHeight
15271532
15281533 onCloseClicked: { appDelegate.close(); }
1529− onMaximizeClicked: appDelegate.anyMaximized ? appDelegate.restoreFromMaximized() : appDelegate.maximize();
1530− onMaximizeHorizontallyClicked: appDelegate.maximizedHorizontally ? appDelegate.restoreFromMaximized() : appDelegate.maximizeHorizontally()
1531− onMaximizeVerticallyClicked: appDelegate.maximizedVertically ? appDelegate.restoreFromMaximized() : appDelegate.maximizeVertically()
1534+ onMaximizeClicked: {
1535+ if (appDelegate.canBeMaximized) {
1536+ appDelegate.anyMaximized ? appDelegate.restoreFromMaximized() : appDelegate.maximize();
1537+ }
1538+ }
1539+ onMaximizeHorizontallyClicked: {
1540+ if (appDelegate.canBeMaximizedHorizontally) {
1541+ appDelegate.maximizedHorizontally ? appDelegate.restoreFromMaximized() : appDelegate.maximizeHorizontally()
1542+ }
1543+ }
1544+ onMaximizeVerticallyClicked: {
1545+ if (appDelegate.canBeMaximizedVertically) {
1546+ appDelegate.maximizedVertically ? appDelegate.restoreFromMaximized() : appDelegate.maximizeVertically()
1547+ }
1548+ }
15321549 onMinimizeClicked: appDelegate.minimize()
15331550 onDecorationPressed: { appDelegate.focus = true; }
15341551 onDecorationReleased: fakeRectangle.commit();
@@ -1785,6 +1802,7 @@ FocusScope {
17851802 direction: Direction.Leftwards
17861803 anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
17871804 width: root.dragAreaWidth
1805+ enabled: root.spreadEnabled
17881806
17891807 property var gesturePoints: []
17901808 property bool cancelled: false
qml/Stage/WindowDecoration.qml+4−5
@@ -22,7 +22,7 @@ MouseArea {
2222 id: root
2323 clip: true
2424
25− property Item target // appDelegate
25+ property alias closeButtonVisible: buttons.closeButtonShown
2626 property alias title: titleLabel.text
2727 property alias maximizeButtonShown: buttons.maximizeButtonShown
2828 property bool active: false
@@ -40,7 +40,7 @@ MouseArea {
4040 signal maximizeVerticallyClicked()
4141
4242 onDoubleClicked: {
43− if (target.canBeMaximized && mouse.button == Qt.LeftButton) {
43+ if (mouse.button == Qt.LeftButton) {
4444 root.maximizeClicked();
4545 }
4646 }
@@ -77,9 +77,8 @@ MouseArea {
7777 onCloseClicked: root.closeClicked();
7878 onMinimizeClicked: root.minimizeClicked();
7979 onMaximizeClicked: root.maximizeClicked();
80− onMaximizeHorizontallyClicked: if (root.target.canBeMaximizedHorizontally) root.maximizeHorizontallyClicked();
81− onMaximizeVerticallyClicked: if (root.target.canBeMaximizedVertically) root.maximizeVerticallyClicked();
82− closeButtonShown: root.target.appId !== "unity8-dash"
80+ onMaximizeHorizontallyClicked: root.maximizeHorizontallyClicked();
81+ onMaximizeVerticallyClicked: root.maximizeVerticallyClicked();
8382 }
8483
8584 Label {
tests/autopilot/unity8/dash.py+3−3
@@ -163,9 +163,9 @@ class Dash(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
163163 search_button.globalRect.x + search_button.width / 2,
164164 search_button.globalRect.y + search_button.height / 2)
165165 self.pointing_device.click()
166− headerContainer = current_header.select_single(
167− objectName="headerContainer")
168− headerContainer.contentY.wait_for(0)
166+ headersColumn = current_header.select_single(
167+ objectName="headersColumn")
168+ headersColumn.y.wait_for(0)
169169 keyboard.type(query)
170170 self.select_single(
171171 objectName="processingIndicator").visible.wait_for(False)
tests/autopilot/unity8/greeter/__init__.py+1−1
@@ -94,4 +94,4 @@ class Greeter(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
9494
9595 def get_prompt(self):
9696 return self.select_single(
97− ubuntuuitoolkit.TextField, objectName='passwordInput')
97+ ubuntuuitoolkit.TextField, objectName='promptField')
tests/autopilot/unity8/shell/__init__.py+2
@@ -25,6 +25,8 @@ from functools import wraps
2525import ubuntuuitoolkit
2626from autopilot import logging as autopilot_logging
2727from autopilot import input
28+import gi
29+gi.require_version('Notify', '0.7')
2830from gi.repository import Notify
2931
3032from unity8 import (
tests/autopilot/unity8/shell/tests/__init__.py+4−1
@@ -156,7 +156,10 @@ class UnityTestCase(AutopilotTestCase):
156156 def _setup_grid_size(self, scale_divisor):
157157 """Use the grid size that may be supplied or use the default."""
158158 if getattr(self, 'grid_unit_px', 0) == 0:
159− self.grid_size = int(os.getenv('GRID_UNIT_PX'))
159+ if os.getenv('GRID_UNIT_PX') == None:
160+ self.grid_size = 8
161+ else:
162+ self.grid_size = int(os.getenv('GRID_UNIT_PX'))
160163 else:
161164 self.grid_size = int(self.grid_unit_px / scale_divisor)
162165 self._environment["GRID_UNIT_PX"] = str(self.grid_size)
tests/autopilot/unity8/shell/tests/test_helpers.py+2−1
@@ -129,7 +129,8 @@ class DashAppsHelperTestCase(tests.DashBaseTestCase):
129129 available_applications = [
130130 'Title.2.0', 'Title.2.1', 'Title.2.2', 'Title.2.3', 'Title.2.4',
131131 'Title.2.5', 'Title.2.6', 'Title.2.7', 'Title.2.8', 'Title.2.9',
132− 'Title.2.10', 'Title.2.11', 'Title.2.12']
132+ 'Title.2.10', 'Title.2.11', 'Title.2.12', 'Title.2.13', 'Title.2.14',
133+ 'Title.2.15', 'Title.2.16', 'Title.2.17', 'Title.2.18']
133134
134135 def setUp(self):
135136 # Set up the fake scopes before launching unity.
tests/autopilot/unity8/shell/tests/test_notifications.py+9−9
@@ -143,7 +143,7 @@ class InteractiveNotificationBase(NotificationsBase):
143143 )
144144
145145 get_notification = lambda: notify_list.wait_select_single(
146− 'Notification', objectName='notification1')
146+ 'Notification', objectName='notification0')
147147 notification = get_notification()
148148
149149 notification.pointing_device.click_object(
@@ -185,7 +185,7 @@ class InteractiveNotificationBase(NotificationsBase):
185185 # verify and interact with the triggered snap-decision notification
186186 notify_list = self._get_notifications_list()
187187 get_notification = lambda: notify_list.wait_select_single(
188− 'Notification', objectName='notification1')
188+ 'Notification', objectName='notification0')
189189 notification = get_notification()
190190 self._assert_notification(
191191 notification, summary, body, False, False, 1.0)
@@ -234,7 +234,7 @@ class InteractiveNotificationBase(NotificationsBase):
234234 # verify and interact with the triggered snap-decision notification
235235 notify_list = self._get_notifications_list()
236236 get_notification = lambda: notify_list.wait_select_single(
237− 'Notification', objectName='notification1')
237+ 'Notification', objectName='notification0')
238238 notification = get_notification()
239239 self._assert_notification(
240240 notification, summary, body, True, False, 1.0)
@@ -282,7 +282,7 @@ class InteractiveNotificationBase(NotificationsBase):
282282 # verify and interact with the triggered snap-decision notification
283283 notify_list = self._get_notifications_list()
284284 get_notification = lambda: notify_list.wait_select_single(
285− 'Notification', objectName='notification1')
285+ 'Notification', objectName='notification0')
286286 notification = get_notification()
287287 self._assert_notification(
288288 notification, summary, body, True, False, 1.0)
@@ -445,7 +445,7 @@ class EphemeralNotificationsTests(NotificationsBase):
445445 notification.show()
446446
447447 notification = lambda: notify_list.wait_select_single(
448− 'Notification', objectName='notification1')
448+ 'Notification', objectName='notification0')
449449 self._assert_notification(
450450 notification(),
451451 summary,
@@ -478,7 +478,7 @@ class EphemeralNotificationsTests(NotificationsBase):
478478 notification.show()
479479
480480 notification = lambda: notify_list.wait_select_single(
481− 'Notification', objectName='notification1')
481+ 'Notification', objectName='notification0')
482482 self._assert_notification(
483483 notification(),
484484 summary,
@@ -590,7 +590,7 @@ class EphemeralNotificationsTests(NotificationsBase):
590590 notification.show()
591591
592592 notification = notify_list.wait_select_single(
593− 'Notification', objectName='notification1')
593+ 'Notification', objectName='notification0')
594594
595595 self._assert_notification(
596596 notification,
@@ -614,7 +614,7 @@ class EphemeralNotificationsTests(NotificationsBase):
614614 notification.show()
615615
616616 notification = notify_list.wait_select_single(
617− 'Notification', objectName='notification1')
617+ 'Notification', objectName='notification0')
618618
619619 self._assert_notification(notification, summary, '', False, False, 1.0)
620620
@@ -683,7 +683,7 @@ class EphemeralNotificationsTests(NotificationsBase):
683683 notification.show()
684684
685685 get_notification = lambda: notify_list.wait_select_single(
686− 'Notification', objectName='notification1')
686+ 'Notification', objectName='notification0')
687687
688688 self._assert_notification(
689689 get_notification(),
tests/mocks/GSettings.1.0/fake_gsettings.cpp+33
@@ -28,6 +28,7 @@ GSettingsControllerQml::GSettingsControllerQml()
2828 , m_launcherWidth(8)
2929 , m_edgeDragWidth(2)
3030 , m_enableLauncher(true)
31+ , m_enableIndicatorMenu(true)
3132{
3233}
3334
@@ -159,6 +160,19 @@ bool GSettingsControllerQml::enableLauncher() const
159160 return m_enableLauncher;
160161}
161162
163+bool GSettingsControllerQml::enableIndicatorMenu() const
164+{
165+ return m_enableIndicatorMenu;
166+}
167+
168+void GSettingsControllerQml::setEnableIndicatorMenu(bool enableIndicatorMenu)
169+{
170+ if (m_enableIndicatorMenu != enableIndicatorMenu) {
171+ m_enableIndicatorMenu = enableIndicatorMenu;
172+ Q_EMIT enableIndicatorMenuChanged(enableIndicatorMenu);
173+ }
174+}
175+
162176GSettingsSchemaQml::GSettingsSchemaQml(QObject *parent): QObject(parent) {
163177}
164178
@@ -225,6 +239,8 @@ void GSettingsQml::componentComplete()
225239 this, &GSettingsQml::edgeDragWidthChanged);
226240 connect(GSettingsControllerQml::instance(), &GSettingsControllerQml::enableLauncherChanged,
227241 this, &GSettingsQml::enableLauncherChanged);
242+ connect(GSettingsControllerQml::instance(), &GSettingsControllerQml::enableIndicatorMenuChanged,
243+ this, &GSettingsQml::enableIndicatorMenuChanged);
228244
229245 Q_EMIT disableHeightChanged();
230246 Q_EMIT pictureUriChanged();
@@ -235,6 +251,7 @@ void GSettingsQml::componentComplete()
235251 Q_EMIT launcherWidthChanged();
236252 Q_EMIT edgeDragWidthChanged();
237253 Q_EMIT enableLauncherChanged();
254+ Q_EMIT enableIndicatorMenuChanged();
238255}
239256
240257GSettingsSchemaQml * GSettingsQml::schema() const {
@@ -350,6 +367,15 @@ QVariant GSettingsQml::enableLauncher() const
350367 }
351368}
352369
370+QVariant GSettingsQml::enableIndicatorMenu() const
371+{
372+ if (m_valid && m_schema->id() == "com.canonical.Unity8") {
373+ return GSettingsControllerQml::instance()->enableIndicatorMenu();
374+ } else {
375+ return QVariant();
376+ }
377+}
378+
353379void GSettingsQml::setLifecycleExemptAppids(const QVariant &appIds)
354380{
355381 if (m_valid && m_schema->id() == "com.canonical.qtmir") {
@@ -384,3 +410,10 @@ void GSettingsQml::setEnableLauncher(const QVariant &enableLauncher)
384410 GSettingsControllerQml::instance()->setEnableLauncher(enableLauncher.toBool());
385411 }
386412}
413+
414+void GSettingsQml::setEnableIndicatorMenu(const QVariant &enableIndicatorMenu)
415+{
416+ if (m_valid && m_schema->id() == "com.canonical.Unity8") {
417+ GSettingsControllerQml::instance()->setEnableIndicatorMenu(enableIndicatorMenu.toBool());
418+ }
419+}
tests/mocks/GSettings.1.0/fake_gsettings.h+9
@@ -58,6 +58,7 @@ class GSettingsQml: public QObject, public QQmlParserStatus
5858 Q_PROPERTY(QVariant launcherWidth READ launcherWidth WRITE setLauncherWidth NOTIFY launcherWidthChanged)
5959 Q_PROPERTY(QVariant edgeDragWidth READ edgeDragWidth WRITE setEdgeDragWidth NOTIFY edgeDragWidthChanged)
6060 Q_PROPERTY(QVariant enableLauncher READ enableLauncher WRITE setEnableLauncher NOTIFY enableLauncherChanged)
61+ Q_PROPERTY(QVariant enableIndicatorMenu READ enableIndicatorMenu WRITE setEnableIndicatorMenu NOTIFY enableIndicatorMenuChanged)
6162
6263public:
6364 GSettingsQml(QObject *parent = nullptr);
@@ -75,6 +76,7 @@ public:
7576 QVariant launcherWidth() const;
7677 QVariant edgeDragWidth() const;
7778 QVariant enableLauncher() const;
79+ QVariant enableIndicatorMenu() const;
7880
7981 void setDisableHeight(const QVariant &val);
8082 void setPictureUri(const QVariant &str);
@@ -85,6 +87,7 @@ public:
8587 void setLauncherWidth(const QVariant &launcherWidth);
8688 void setEdgeDragWidth(const QVariant &edgeDragWidth);
8789 void setEnableLauncher(const QVariant &enableLauncher);
90+ void setEnableIndicatorMenu(const QVariant &enableIndicatorMenu);
8891
8992Q_SIGNALS:
9093 void disableHeightChanged();
@@ -97,6 +100,7 @@ Q_SIGNALS:
97100 void launcherWidthChanged();
98101 void edgeDragWidthChanged();
99102 void enableLauncherChanged();
103+ void enableIndicatorMenuChanged();
100104
101105private:
102106 GSettingsSchemaQml* m_schema;
@@ -140,6 +144,9 @@ public:
140144 bool enableLauncher() const;
141145 Q_INVOKABLE void setEnableLauncher(bool enableLauncher);
142146
147+ bool enableIndicatorMenu() const;
148+ Q_INVOKABLE void setEnableIndicatorMenu(bool enableIndicatorMenu);
149+
143150Q_SIGNALS:
144151 void disableHeightChanged();
145152 void pictureUriChanged(const QString&);
@@ -150,6 +157,7 @@ Q_SIGNALS:
150157 void launcherWidthChanged(int launcherWidth);
151158 void edgeDragWidthChanged(uint edgeDragWidth);
152159 void enableLauncherChanged(bool enableLauncher);
160+ void enableIndicatorMenuChanged(bool enableIndicatorMenu);
153161
154162private:
155163 GSettingsControllerQml();
@@ -163,6 +171,7 @@ private:
163171 int m_launcherWidth;
164172 uint m_edgeDragWidth;
165173 bool m_enableLauncher;
174+ bool m_enableIndicatorMenu;
166175
167176 static GSettingsControllerQml* s_controllerInstance;
168177 QList<GSettingsQml *> m_registeredGSettings;
tests/mocks/Unity/Application/CMakeLists.txt+1
@@ -5,6 +5,7 @@ set(FakeUnityApplicationQml_SOURCES
55 MirSurface.cpp
66 MirSurfaceItem.cpp
77 MirSurfaceListModel.cpp
8+ MirMock.cpp
89 ObjectListModel.h
910 SurfaceManager.cpp
1011 VirtualKeyboard.cpp
tests/mocks/Unity/Application/MirMock.cppnew file+49
@@ -0,0 +1,49 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "MirMock.h"
18+
19+MirMock *MirMock::the_mir = nullptr;
20+
21+MirMock *MirMock::instance()
22+{
23+ return the_mir;
24+}
25+
26+MirMock::MirMock()
27+{
28+ Q_ASSERT(the_mir == nullptr);
29+ the_mir = this;
30+}
31+
32+MirMock::~MirMock()
33+{
34+ Q_ASSERT(the_mir == this);
35+ the_mir = nullptr;
36+}
37+
38+void MirMock::setCursorName(const QString &cursorName)
39+{
40+ if (cursorName != m_cursorName) {
41+ m_cursorName = cursorName;
42+ Q_EMIT cursorNameChanged(m_cursorName);
43+ }
44+}
45+
46+QString MirMock::cursorName() const
47+{
48+ return m_cursorName;
49+}
tests/mocks/Unity/Application/MirMock.hnew file+39
@@ -0,0 +1,39 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef MIR_MOCK_H
18+#define MIR_MOCK_H
19+
20+#include <unity/shell/application/Mir.h>
21+
22+class MirMock : public ::Mir
23+{
24+ Q_OBJECT
25+public:
26+ explicit MirMock();
27+ virtual ~MirMock();
28+
29+ static MirMock *instance();
30+
31+ void setCursorName(const QString &cursorName) override;
32+ QString cursorName() const override;
33+
34+private:
35+ static MirMock *the_mir;
36+ QString m_cursorName;
37+};
38+
39+#endif
tests/mocks/Unity/Application/plugin.cpp+11−2
@@ -17,6 +17,7 @@
1717#include "plugin.h"
1818#include "ApplicationInfo.h"
1919#include "ApplicationManager.h"
20+#include "MirMock.h"
2021#include "MirSurfaceItem.h"
2122#include "SurfaceManager.h"
2223
@@ -38,6 +39,9 @@ void createUnityApplicationSharedSingletons()
3839 if (!SurfaceManager::instance()) {
3940 new SurfaceManager;
4041 }
42+ if (!MirMock::instance()) {
43+ new MirMock;
44+ }
4145}
4246
4347QObject* applicationManagerSingleton(QQmlEngine*, QJSEngine*)
@@ -46,6 +50,12 @@ QObject* applicationManagerSingleton(QQmlEngine*, QJSEngine*)
4650 return new ApplicationManager;
4751}
4852
53+QObject* mirSingleton(QQmlEngine*, QJSEngine*)
54+{
55+ createUnityApplicationSharedSingletons();
56+ return MirMock::instance();
57+}
58+
4959QObject* surfaceManagerSingleton(QQmlEngine*, QJSEngine*)
5060{
5161 createUnityApplicationSharedSingletons();
@@ -78,9 +88,8 @@ void FakeUnityApplicationQmlPlugin::registerTypes(const char *uri)
7888 qmlRegisterType<ApplicationInfo>(uri, 0, 1, "ApplicationInfo");
7989
8090 qmlRegisterSingletonType<ApplicationManager>(uri, 0, 1, "ApplicationManager", applicationManagerSingleton);
91+ qmlRegisterSingletonType<MirMock>(uri, 0, 1, "Mir", mirSingleton);
8192 qmlRegisterSingletonType<SurfaceManager>(uri, 0, 1, "SurfaceManager", surfaceManagerSingleton);
82−
83− qmlRegisterUncreatableType<Mir>(uri, 0, 1, "Mir", "Mir provides enum values, it can't be instantiated");
8493}
8594
8695void FakeUnityApplicationQmlPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
tests/mocks/Unity/Launcher/MockQuickListModel.cpp+2
@@ -37,6 +37,8 @@ QVariant MockQuickListModel::data(const QModelIndex &index, int role) const
3737 return QLatin1String("copy.png");
3838 case RoleClickable:
3939 return index.row() == 1 ? false : true;
40+ case RoleHasSeparator:
41+ return index.row() <= 1 ? true : false;
4042 }
4143 return QVariant();
4244}
tests/mocks/Unity/Screens/CMakeLists.txt+1−1
@@ -11,4 +11,4 @@ add_library(MockScreensPlugin MODULE ${MockScreens_SOURCES})
1111
1212qt5_use_modules(MockScreensPlugin Gui Qml)
1313
14−add_unity8_mock(Unity.Screens 0.1 Unity/Screens TARGETS MockScreensPlugin)
14+add_unity8_mock(Unity.Screens 0.1 Unity/Screens PREFIX mocks TARGETS MockScreensPlugin)
tests/mocks/Unity/fake_categories.cpp+4
@@ -69,6 +69,10 @@ Categories::data(const QModelIndex& index, int role) const
6969 } else {
7070 map["category-layout"] = "carousel";
7171 }
72+ if (index.row() == 16) {
73+ map["expandable"] = false;
74+ map["collapsed-rows"] = 1;
75+ }
7276 if (index.row() == 18) {
7377 map["category-layout"] = "horizontal-list";
7478 }
tests/plugins/Dash/cardcreator/4.res+1−1
@@ -34,7 +34,7 @@ Loader {
3434 visible: showHeader && active && status === Loader.Ready;
3535 width: units.gu(6);
3636 height: units.gu(5.625);
37− sourceComponent: UbuntuShape { image: mascotImage }
37+ sourceComponent: UbuntuShape { aspect: UbuntuShape.Flat; image: mascotImage }
3838 anchors { verticalCenter: parent.verticalCenter; }
3939 }
4040
tests/plugins/Dash/cardcreator/4.res.cardcreator+1−1
@@ -32,7 +32,7 @@ Loader {
3232 visible: showHeader && active && status === Loader.Ready;
3333 width: units.gu(6);
3434 height: units.gu(5.625);
35− sourceComponent: UbuntuShape { image: mascotImage }
35+ sourceComponent: UbuntuShape { aspect: UbuntuShape.Flat; image: mascotImage }
3636 anchors { verticalCenter: parent.verticalCenter; }
3737 }
3838
tests/qmltests/Dash/tst_GenericScopeView.qml+26
@@ -168,6 +168,32 @@ Item {
168168 tryCompare(genericScopeView.scope, "searchQuery", "test")
169169 }
170170
171+ function test_forceNotExpandable() {
172+ // wait for the item to be there
173+ tryCompareFunction(function() { return findChild(genericScopeView, "dashSectionHeader14", 0 /* timeout */) != null; }, true);
174+
175+ var category14 = findChild(genericScopeView, "dashCategory14")
176+ var seeAll14 = findChild(category14, "seeAll")
177+
178+ compare(category14.forceNotExpandable, false);
179+ waitForRendering(seeAll14);
180+ verify(category14.expandable);
181+ verify(!category14.expanded);
182+ compare(seeAll14.visible, true);
183+
184+ // wait for the item to be there
185+ tryCompareFunction(function() { return findChild(genericScopeView, "dashSectionHeader16", 0 /* timeout */) != null; }, true);
186+
187+ var category16 = findChild(genericScopeView, "dashCategory16")
188+ var seeAll16 = findChild(category16, "seeAll")
189+
190+ compare(category16.forceNotExpandable, true);
191+ waitForRendering(seeAll16);
192+ verify(!category16.expandable);
193+ verify(!category16.expanded);
194+ compare(seeAll16.visible, false);
195+ }
196+
171197 function test_expand_collapse() {
172198 tryCompareFunction(function() { return findChild(genericScopeView, "dashSectionHeader0", 0 /* timeout */) != null; }, true);
173199
tests/qmltests/Launcher/tst_Launcher.qml+89
@@ -1369,6 +1369,95 @@ Rectangle {
13691369 tryCompare(clickThroughSpy, "count", 0);
13701370 }
13711371
1372+ function test_tooltip() {
1373+ launcher.lockedVisible = true;
1374+ revealByEdgePush();
1375+
1376+ var item = findChild(launcher, "launcherDelegate0");
1377+ var tooltipShape = findChild(launcher, "tooltipShape");
1378+
1379+ // Initial state
1380+ tryCompare(tooltipShape, "visible", false);
1381+ tryCompare(tooltipShape, "opacity", .0);
1382+
1383+ // Move the mouse on the launcher icon
1384+ mouseMove(item, item.width / 2, item.height / 2, 10);
1385+ mouseMove(item, item.width / 2 + 1, item.height / 2, 10);
1386+ tryCompare(tooltipShape, "visible", true);
1387+ tryCompare(tooltipShape, "opacity", .95);
1388+
1389+ // Move the mouse away
1390+ mouseMove(root, root.width, root.height / 2, 10);
1391+ tryCompare(tooltipShape, "visible", false);
1392+ tryCompare(tooltipShape, "opacity", .0);
1393+ }
1394+
1395+ function test_quicklist_dismisses_tooltip() {
1396+ launcher.lockedVisible = true;
1397+ revealByEdgePush();
1398+
1399+ var item = findChild(launcher, "launcherDelegate0");
1400+ var tooltipShape = findChild(launcher, "tooltipShape");
1401+ var quickListShape = findChild(launcher, "quickListShape");
1402+
1403+ // Initial state
1404+ tryCompare(tooltipShape, "visible", false);
1405+ tryCompare(tooltipShape, "opacity", .0);
1406+
1407+ // Move the mouse on the launcher icon
1408+ mouseMove(item, item.width / 2, item.height / 2, 10);
1409+ mouseMove(item, item.width / 2 + 1, item.height / 2, 10);
1410+ tryCompare(tooltipShape, "visible", true);
1411+ tryCompare(tooltipShape, "opacity", .95);
1412+
1413+ // Right click to show the quicklist
1414+ mouseClick(item, item.width / 2, item.height / 2, Qt.RightButton);
1415+ tryCompare(quickListShape, "visible", true);
1416+ tryCompare(tooltipShape, "visible", false);
1417+ tryCompare(tooltipShape, "opacity", .0);
1418+
1419+ // Left click hides the quicklist, tooltip is still dismissed
1420+ mouseClick(item, item.width / 2, item.height / 2, Qt.LefftButton);
1421+ tryCompare(quickListShape, "visible", false);
1422+ tryCompare(tooltipShape, "visible", false);
1423+ tryCompare(tooltipShape, "opacity", .0);
1424+
1425+ // Mouse motion should should show tooltip again
1426+ mouseMove(item, item.width / 2, item.height / 2, 10);
1427+ mouseMove(item, item.width / 2 + 1, item.height / 2, 10);
1428+ tryCompare(tooltipShape, "visible", true);
1429+ tryCompare(tooltipShape, "opacity", .95);
1430+ }
1431+
1432+ function test_click_dismisses_tooltip() {
1433+ launcher.lockedVisible = true;
1434+ revealByEdgePush();
1435+
1436+ var item = findChild(launcher, "launcherDelegate0");
1437+ var tooltipShape = findChild(launcher, "tooltipShape");
1438+
1439+ // Initial state
1440+ tryCompare(tooltipShape, "visible", false);
1441+ tryCompare(tooltipShape, "opacity", .0);
1442+
1443+ // Move the mouse on the launcher icon
1444+ mouseMove(item, item.width / 2, item.height / 2, 10);
1445+ mouseMove(item, item.width / 2 + 1, item.height / 2, 10);
1446+ tryCompare(tooltipShape, "visible", true);
1447+ tryCompare(tooltipShape, "opacity", .95);
1448+
1449+ // Left click should dismiss the tooltip
1450+ mouseClick(item, item.width / 2 + 1, item.height / 2, Qt.LeftButton);
1451+ tryCompare(tooltipShape, "visible", false);
1452+ tryCompare(tooltipShape, "opacity", .0);
1453+
1454+ // Mouse motion should should show tooltip again
1455+ mouseMove(item, item.width / 2, item.height / 2, 10);
1456+ mouseMove(item, item.width / 2 + 1, item.height / 2, 10);
1457+ tryCompare(tooltipShape, "visible", true);
1458+ tryCompare(tooltipShape, "opacity", .95);
1459+ }
1460+
13721461 function test_launcherEnabledSetting() {
13731462 launcher.available = true;
13741463
tests/qmltests/Panel/tst_IndicatorsMenu.qml+17
@@ -286,5 +286,22 @@ IndicatorTest {
286286 mouseClick(indicatorsMenu, indicatorsMenu.width/2, indicatorsMenu.height/2, Qt.RightButton);
287287 tryCompare(clickThroughSpy, "count", 0);
288288 }
289+
290+ function test_enableMenuSetting_data() {
291+ return [
292+ {tag: "menu enabled", enabled: true},
293+ {tag: "menu disabled", enabled: false}
294+ ]
295+ }
296+
297+ function test_enableMenuSetting(data) {
298+ indicatorsMenu.available = data.enabled;
299+
300+ indicatorsMenu.show();
301+ tryCompare(indicatorsMenu, data.enabled ? "fullyOpened" : "fullyClosed", true);
302+
303+ indicatorsMenu.available = true;
304+ indicatorsMenu.hide();
305+ }
289306 }
290307}
tests/qmltests/Stage/tst_DesktopStage.qml+16
@@ -789,5 +789,21 @@ Item {
789789
790790 tryCompare(dialerAppDelegate, "state", "maximized");
791791 }
792+
793+ function test_grabbingCursorOnDecorationPress() {
794+ var appDelegate = startApplication("dialer-app");
795+ verify(appDelegate);
796+ var decoration = findChild(appDelegate, "appWindowDecoration");
797+ verify(decoration);
798+
799+ mousePress(decoration, decoration.width/2, decoration.height/2, Qt.LeftButton);
800+ tryCompare(Mir, "cursorName", "grabbing");
801+
802+ mouseMove(decoration, decoration.width/2 + 1, decoration.height/2 + 1);
803+ tryCompare(Mir, "cursorName", "grabbing");
804+
805+ mouseRelease(decoration);
806+ tryCompare(Mir, "cursorName", "");
807+ }
792808 }
793809}
tests/qmltests/tst_Shell.qml+64
@@ -2638,5 +2638,69 @@ Rectangle {
26382638
26392639 GSettingsController.setEnableLauncher(true);
26402640 }
2641+
2642+ function test_indicatorMenuEnabledSetting_data() {
2643+ return [
2644+ {tag: "indicator menu enabled", enabled: true},
2645+ {tag: "indicator menu disabled", enabled: false}
2646+ ]
2647+ }
2648+
2649+ function test_indicatorMenuEnabledSetting(data) {
2650+ loadShell("phone");
2651+
2652+ GSettingsController.setEnableIndicatorMenu(data.enabled);
2653+
2654+ var panel = findChild(shell, "panel");
2655+ compare(panel.indicators.available, data.enabled);
2656+
2657+ GSettingsController.setEnableIndicatorMenu(true);
2658+ }
2659+
2660+ function test_spreadDisabled_data() {
2661+ return [
2662+ { tag: "enabled", spreadEnabled: true },
2663+ { tag: "disabled", spreadEnabled: false }
2664+ ];
2665+ }
2666+
2667+ function test_spreadDisabled(data) {
2668+ loadShell("phone");
2669+ swipeAwayGreeter();
2670+ var stage = findChild(shell, "stage");
2671+ stage.spreadEnabled = data.spreadEnabled;
2672+
2673+ // Try swiping
2674+ touchFlick(shell, shell.width - 2, shell.height / 2, units.gu(1), shell.height / 2);
2675+ tryCompare(stage, "state", data.spreadEnabled ? "spread" : "staged");
2676+
2677+ stage.closeSpread();
2678+ tryCompare(stage, "state", "staged");
2679+
2680+ // Try by edge push
2681+ mouseMove(stage, stage.width - 1, units.gu(10));
2682+ for (var i = 0; i < units.gu(10); i++) {
2683+ stage.pushRightEdge(1);
2684+ }
2685+ mouseMove(stage, stage.width - units.gu(5), units.gu(10));
2686+ tryCompare(stage, "state", data.spreadEnabled ? "spread" : "staged");
2687+
2688+ stage.closeSpread();
2689+ tryCompare(stage, "state", "staged");
2690+
2691+ // Try by alt+tab
2692+ keyPress(Qt.Key_Alt);
2693+ keyClick(Qt.Key_Tab);
2694+ tryCompare(stage, "state", data.spreadEnabled ? "spread" : "staged");
2695+ keyRelease(Qt.Key_Alt);
2696+
2697+ stage.closeSpread();
2698+ tryCompare(stage, "state", "staged");
2699+
2700+ // Try by Super+W
2701+ keyPress(Qt.Key_W, Qt.MetaModifier)
2702+ tryCompare(stage, "state", data.spreadEnabled ? "spread" : "staged");
2703+ keyRelease(Qt.Key_W, Qt.MetaModifier)
2704+ }
26412705 }
26422706}