on xenial help sign in
merged with trunk
d3b225cNick Dedekind
Bottombar/Bottombar.qml+3−1
@@ -107,7 +107,7 @@ Item {
107 height: distanceThreshold
107 height: distanceThreshold
108 anchors.bottom: parent.bottom
108 anchors.bottom: parent.bottom
109
109
110− distanceThreshold: units.gu(8)
110+ distanceThreshold: units.gu(1)
111 enabled: !theHud.shown && bottombar.enabled && applicationIsOnForeground
111 enabled: !theHud.shown && bottombar.enabled && applicationIsOnForeground
112 direction: Direction.Upwards
112 direction: Direction.Upwards
113
113
@@ -119,6 +119,8 @@ Item {
119 if (previousStatus == DirectionalDragArea.Recognized) {
119 if (previousStatus == DirectionalDragArea.Recognized) {
120 if (hudButton.mouseOver) {
120 if (hudButton.mouseOver) {
121 hudButton.clicked()
121 hudButton.clicked()
122+ } else {
123+ bottombar.state = "hidden"
122 }
124 }
123 }
125 }
124 } else if (status === DirectionalDragArea.Undecided) {
126 } else if (status === DirectionalDragArea.Undecided) {
Components/EdgeDemo.qml+2−1
@@ -218,8 +218,9 @@ Item {
218 d.finalEdgeDemo = d.overlay.createObject(demo.underlay, {
218 d.finalEdgeDemo = d.overlay.createObject(demo.underlay, {
219 "edge": "none",
219 "edge": "none",
220 "title": i18n.tr("Well done"),
220 "title": i18n.tr("Well done"),
221− "text": i18n.tr("You have now mastered the edge gestures and can start using the phone"),
221+ "text": i18n.tr("You have now mastered the edge gestures and can start using the phone<br><br>Tap anywhere to start"),
222 "anchors.fill": demo.dash,
222 "anchors.fill": demo.dash,
223+ "showSkip": false,
223 });
224 });
224 }
225 }
225 if (d.finalEdgeDemo) {
226 if (d.finalEdgeDemo) {
Components/EdgeDemoOverlay.qml+5
@@ -43,6 +43,11 @@ Showable {
43 */
43 */
44 property alias skipText: skipLabel.text
44 property alias skipText: skipLabel.text
45
45
46+ /*
47+ * This is the visible status of the skip button.
48+ */
49+ property alias showSkip: skipLabel.visible
50+
46 /*
51 /*
47 * Whether this demo is running currently.
52 * Whether this demo is running currently.
48 */
53 */
Components/IconUtil.jsdeleted−42
@@ -1,42 +0,0 @@
1−/*
2− * Copyright (C) 2013 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−.pragma library
18−
19−// FIXME: a quick hack to get icons out of gicons and fallback to
20−// base icon for annotated icons. Doesn't correctly handle all icons.
21−// Proper global solution needed.
22−function from_gicon(name) {
23− var icon_name = name;
24− var annotated_re = /^. UnityProtocolAnnotatedIcon/;
25− if (annotated_re.test(name)) {
26− var base_icon_re = /'base-icon':.+?'(.+?)'/;
27− var base_icon = name.toString().match(base_icon_re);
28− icon_name = base_icon[1];
29− } else {
30− var themed_re = /^. GThemedIcon\s*([^\s]+)\s*/;
31− var themed = name.match(themed_re);
32− if (themed) {
33− return "image://gicon/" + themed[1];
34− }
35− }
36− var name_re = /^[a-z0-9\._-]+$/;
37− if (name_re.test(icon_name)) {
38− return "image://gicon/" + icon_name;
39− } else {
40− return icon_name;
41− }
42−}
Dash/Apps/AppPreview.qml+1−2
@@ -20,7 +20,6 @@ import Ubuntu.Components.ListItems 0.1 as ListItem
20import Ubuntu.DownloadDaemonListener 0.1
20import Ubuntu.DownloadDaemonListener 0.1
21import ".."
21import ".."
22import "../../Components"
22import "../../Components"
23−import "../../Components/IconUtil.js" as IconUtil
24
23
25DashPreview {
24DashPreview {
26 id: root
25 id: root
@@ -137,7 +136,7 @@ DashPreview {
137 anchors { left: parent.left; right: parent.right }
136 anchors { left: parent.left; right: parent.right }
138
137
139 appName: root.previewData.title
138 appName: root.previewData.title
140− icon: IconUtil.from_gicon(root.previewData.appIcon)
139+ icon: root.previewData.appIcon
141 rating: Math.round(root.previewData.rating * 10)
140 rating: Math.round(root.previewData.rating * 10)
142 reviews: root.previewData.numRatings
141 reviews: root.previewData.numRatings
143 rated: root.previewData.infoMap["rated"] ? root.previewData.infoMap["rated"].value : 0
142 rated: root.previewData.infoMap["rated"] ? root.previewData.infoMap["rated"].value : 0
Dash/Apps/ApplicationsFilterGrid.qml+1−2
@@ -17,7 +17,6 @@
17import QtQuick 2.0
17import QtQuick 2.0
18import "../../Components"
18import "../../Components"
19import "../../Applications"
19import "../../Applications"
20−import "../../Components/IconUtil.js" as IconUtil
21
20
22FilterGrid {
21FilterGrid {
23 id: filterGrid
22 id: filterGrid
@@ -50,7 +49,7 @@ FilterGrid {
50 text: model.title ? model.title : application.name // FIXME: this is temporary
49 text: model.title ? model.title : application.name // FIXME: this is temporary
51 imageWidth: units.gu(8)
50 imageWidth: units.gu(8)
52 imageHeight: units.gu(7.5)
51 imageHeight: units.gu(7.5)
53− source: IconUtil.from_gicon(icon)
52+ source: icon
54 onClicked: filterGrid.clicked(index, application.desktopFile);
53 onClicked: filterGrid.clicked(index, application.desktopFile);
55 }
54 }
56}
55}
Dash/DashBar.qml+2−3
@@ -83,9 +83,8 @@ Item {
83 }
83 }
84
84
85 delegate:
85 delegate:
86− Item {
86+ // FIXME: Use Panel's integrated trigger() once that works again.
87− signal clicked()
87+ MouseArea {
88−
89 width: itemSize
88 width: itemSize
90 height: dashBar.height
89 height: dashBar.height
91 anchors.top: parent.top
90 anchors.top: parent.top
Dash/Generic/GenericCarousel.qml+1−2
@@ -17,7 +17,6 @@
17import QtQuick 2.0
17import QtQuick 2.0
18import Ubuntu.Components 0.1
18import Ubuntu.Components 0.1
19import "../../Components"
19import "../../Components"
20−import "../../Components/IconUtil.js" as IconUtil
21
20
22Carousel {
21Carousel {
23 id: genericCarousel
22 id: genericCarousel
@@ -42,7 +41,7 @@ Carousel {
42 image: Image {
41 image: Image {
43 asynchronous: true
42 asynchronous: true
44 sourceSize { width: item.width; height: item.height }
43 sourceSize { width: item.width; height: item.height }
45− source: model ? IconUtil.from_gicon(model.icon) : ""
44+ source: model ? model.icon : ""
46 }
45 }
47 }
46 }
48
47
Dash/Generic/GenericFilterGrid.qml+1−2
@@ -16,7 +16,6 @@
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18import "../../Components"
18import "../../Components"
19−import "../../Components/IconUtil.js" as IconUtil
20
19
21FilterGrid {
20FilterGrid {
22 id: filtergrid
21 id: filtergrid
@@ -41,7 +40,7 @@ FilterGrid {
41 imageWidth: filtergrid.iconWidth
40 imageWidth: filtergrid.iconWidth
42 imageHeight: filtergrid.iconHeight
41 imageHeight: filtergrid.iconHeight
43
42
44− source: IconUtil.from_gicon(model.icon)
43+ source: model.icon
45
44
46 fillMode: Image.PreserveAspectCrop
45 fillMode: Image.PreserveAspectCrop
47
46
Dash/Generic/GenericPreview.qml+1−2
@@ -18,7 +18,6 @@ import QtQuick 2.0
18import Ubuntu.Components 0.1
18import Ubuntu.Components 0.1
19import "../../Components"
19import "../../Components"
20import "../../Components/ListItems" as ListItems
20import "../../Components/ListItems" as ListItems
21−import "../../Components/IconUtil.js" as IconUtil
22import ".."
21import ".."
23
22
24DashPreview {
23DashPreview {
@@ -27,7 +26,7 @@ DashPreview {
27 title: previewData.title
26 title: previewData.title
28 previewWidthRatio: 0.6
27 previewWidthRatio: 0.6
29
28
30− property url url: IconUtil.from_gicon(previewData.image)
29+ property url url: previewData.image
31
30
32 header: UbuntuShape {
31 header: UbuntuShape {
33 id: urlLoader
32 id: urlLoader
Dash/GenericScopeView.qml+4−5
@@ -18,7 +18,6 @@ import QtQuick 2.0
18import Ubuntu.Components 0.1
18import Ubuntu.Components 0.1
19import "../Components"
19import "../Components"
20import "../Components/ListItems" as ListItems
20import "../Components/ListItems" as ListItems
21−import "../Components/IconUtil.js" as IconUtil
22
21
23ScopeView {
22ScopeView {
24 id: scopeView
23 id: scopeView
@@ -74,12 +73,12 @@ ScopeView {
74 onLoaded: {
73 onLoaded: {
75 if (source.toString().indexOf("Apps/RunningApplicationsGrid.qml") != -1) {
74 if (source.toString().indexOf("Apps/RunningApplicationsGrid.qml") != -1) {
76 // TODO: the running apps grid doesn't support standard scope results model yet
75 // TODO: the running apps grid doesn't support standard scope results model yet
77− item.firstModel = results.firstModel
76+ item.firstModel = Qt.binding(function() { return results.firstModel })
78− item.secondModel = results.secondModel
77+ item.secondModel = Qt.binding(function() { return results.secondModel })
79 } else {
78 } else {
80− item.model = results
79+ item.model = Qt.binding(function() { return results })
81 }
80 }
82− item.objectName = categoryId
81+ item.objectName = Qt.binding(function() { return categoryId })
83 if (item.expandable) {
82 if (item.expandable) {
84 var shouldFilter = index != categoryView.expandedIndex;
83 var shouldFilter = index != categoryView.expandedIndex;
85 if (shouldFilter != item.filter) {
84 if (shouldFilter != item.filter) {
Greeter/Infographics.qml+1−2
@@ -286,9 +286,8 @@ Item {
286 objectName: "backgroundCircle"
286 objectName: "backgroundCircle"
287
287
288 anchors.fill: parent
288 anchors.fill: parent
289− opacity: 0.9
290
289
291− source: "graphics/infographic_circle.png"
290+ source: "graphics/infographic_circle_back.png"
292 }
291 }
293
292
294 Timer {
293 Timer {
Greeter/graphics/infographic_circle.pngdeleted

Binary file not shown.

Greeter/graphics/infographic_circle_back.pngnew file

Binary file not shown.

Launcher/LauncherDelegate.qml+20−9
@@ -24,6 +24,7 @@ Item {
24 property int count: -1
24 property int count: -1
25 property int progress: -1
25 property int progress: -1
26 property bool highlighted: false
26 property bool highlighted: false
27+ property bool itemFocused: false
27 property real maxAngle: 0
28 property real maxAngle: 0
28 property bool inverted: false
29 property bool inverted: false
29
30
@@ -43,17 +44,18 @@ Item {
43 onCountChanged: shaderEffectSource.scheduleUpdate();
44 onCountChanged: shaderEffectSource.scheduleUpdate();
44 onProgressChanged: shaderEffectSource.scheduleUpdate();
45 onProgressChanged: shaderEffectSource.scheduleUpdate();
45 onHighlightedChanged: shaderEffectSource.scheduleUpdate();
46 onHighlightedChanged: shaderEffectSource.scheduleUpdate();
47+ onItemFocusedChanged: shaderEffectSource.scheduleUpdate();
46
48
47 Item {
49 Item {
48 id: iconItem
50 id: iconItem
49− width: parent.itemWidth
51+ width: parent.itemWidth + units.gu(1)
50− height: parent.itemHeight
52+ height: parent.itemHeight + units.gu(1)
51 anchors.centerIn: parent
53 anchors.centerIn: parent
52
54
53 UbuntuShape {
55 UbuntuShape {
54 id: iconShape
56 id: iconShape
55 anchors.fill: parent
57 anchors.fill: parent
56− anchors.margins: units.gu(0.5)
58+ anchors.margins: units.gu(1)
57 radius: "medium"
59 radius: "medium"
58
60
59 image: Image {
61 image: Image {
@@ -78,7 +80,7 @@ Item {
78 anchors {
80 anchors {
79 right: parent.right
81 right: parent.right
80 top: parent.top
82 top: parent.top
81− margins: -units.dp(1)
83+ margins: units.dp(3)
82 }
84 }
83 width: Math.min(root.itemWidth, Math.max(units.gu(3), countLabel.implicitWidth + units.gu(2.5)))
85 width: Math.min(root.itemWidth, Math.max(units.gu(3), countLabel.implicitWidth + units.gu(2.5)))
84 height: units.gu(3)
86 height: units.gu(3)
@@ -105,9 +107,9 @@ Item {
105 left: iconItem.left
107 left: iconItem.left
106 right: iconItem.right
108 right: iconItem.right
107 bottom: iconItem.bottom
109 bottom: iconItem.bottom
108− leftMargin: units.gu(0.5)
110+ leftMargin: units.gu(1)
109− rightMargin: units.gu(0.5)
111+ rightMargin: units.gu(1)
110− bottomMargin: units.gu(0.5)
112+ bottomMargin: units.gu(1)
111 }
113 }
112 height: units.gu(1.5)
114 height: units.gu(1.5)
113 visible: root.progress > -1
115 visible: root.progress > -1
@@ -136,14 +138,23 @@ Item {
136 }
138 }
137 }
139 }
138 }
140 }
141+ Image {
142+ objectName: "focusedHighlight"
143+ anchors {
144+ right: parent.right
145+ verticalCenter: parent.verticalCenter
146+ }
147+ visible: root.itemFocused
148+ source: "graphics/focused_app_arrow.png"
149+ }
139 }
150 }
140
151
141 ShaderEffect {
152 ShaderEffect {
142 id: transformEffect
153 id: transformEffect
143 anchors.centerIn: parent
154 anchors.centerIn: parent
144 anchors.verticalCenterOffset: root.offset
155 anchors.verticalCenterOffset: root.offset
145− width: parent.itemWidth
156+ width: iconItem.width
146− height: parent.itemHeight
157+ height: iconItem.height
147 property real itemOpacity: root.itemOpacity
158 property real itemOpacity: root.itemOpacity
148 property real brightness: Math.max(-1, root.brightness)
159 property real brightness: Math.max(-1, root.brightness)
149 property real angle: root.angle
160 property real angle: root.angle
Launcher/LauncherPanel.qml+9−2
@@ -50,8 +50,6 @@ Item {
50 id: mainColumn
50 id: mainColumn
51 anchors {
51 anchors {
52 fill: parent
52 fill: parent
53− leftMargin: units.gu(0.5)
54− rightMargin: units.gu(0.5)
55 }
53 }
56
54
57 MouseArea {
55 MouseArea {
@@ -94,6 +92,8 @@ Item {
94 fill: parent
92 fill: parent
95 topMargin: -extensionSize + units.gu(0.5)
93 topMargin: -extensionSize + units.gu(0.5)
96 bottomMargin: -extensionSize + units.gu(1)
94 bottomMargin: -extensionSize + units.gu(1)
95+ leftMargin: units.gu(0.5)
96+ rightMargin: units.gu(0.5)
97 }
97 }
98 topMargin: extensionSize
98 topMargin: extensionSize
99 bottomMargin: extensionSize
99 bottomMargin: extensionSize
@@ -156,6 +156,7 @@ Item {
156 iconName: model.icon
156 iconName: model.icon
157 count: model.count
157 count: model.count
158 progress: model.progress
158 progress: model.progress
159+ itemFocused: model.focused
159 inverted: root.inverted
160 inverted: root.inverted
160 z: -Math.abs(offset)
161 z: -Math.abs(offset)
161 maxAngle: 55
162 maxAngle: 55
@@ -512,6 +513,12 @@ Item {
512 objectName: "quickListEntry" + index
513 objectName: "quickListEntry" + index
513 text: (model.clickable ? "" : "<b>") + model.label + (model.clickable ? "" : "</b>")
514 text: (model.clickable ? "" : "<b>") + model.label + (model.clickable ? "" : "</b>")
514 highlightWhenPressed: model.clickable
515 highlightWhenPressed: model.clickable
516+
517+ // FIXME: This is a workaround for the theme not being context sensitive. I.e. the
518+ // ListItems don't know that they are sitting in a themed Popover where the color
519+ // needs to be inverted.
520+ __foregroundColor: Theme.palette.selected.backgroundText
521+
515 onClicked: {
522 onClicked: {
516 if (!model.clickable) {
523 if (!model.clickable) {
517 return;
524 return;
Launcher/graphics/focused_app_arrow@30.pngnew file

Binary file not shown.

Panel/IndicatorItem.qml+2−3
@@ -26,13 +26,12 @@ Item {
26 property bool highlighted: false
26 property bool highlighted: false
27 property bool dimmed: false
27 property bool dimmed: false
28 property var indicatorProperties: undefined
28 property var indicatorProperties: undefined
29+ property bool indicatorVisible: loader.item ? loader.item.enabled : false
29
30
30 opacity: dimmed ? 0.4 : 1
31 opacity: dimmed ? 0.4 : 1
31 Behavior on opacity { StandardAnimation {} }
32 Behavior on opacity { StandardAnimation {} }
32
33
33− // only visible when non-empty
34− visible: loader.item != undefined && loader.status == Loader.Ready ? loader.item.enabled : false
35− width: visible ? loader.item.width : 0
34+ width: loader.item ? loader.item.width : 0
36
35
37 Loader {
36 Loader {
38 id: loader
37 id: loader
Panel/IndicatorRow.qml+19−1
@@ -16,6 +16,7 @@
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18import Ubuntu.Components 0.1
18import Ubuntu.Components 0.1
19+import Unity.Indicators 0.1 as Indicators
19import "../Components"
20import "../Components"
20
21
21Item {
22Item {
@@ -25,6 +26,7 @@ Item {
25 readonly property int currentItemIndex: currentItem ? currentItem.ownIndex : -1
26 readonly property int currentItemIndex: currentItem ? currentItem.ownIndex : -1
26 property alias row: row
27 property alias row: row
27 property QtObject indicatorsModel: null
28 property QtObject indicatorsModel: null
29+ property var visibleIndicators: defined
28
30
29 width: units.gu(40)
31 width: units.gu(40)
30 height: units.gu(3)
32 height: units.gu(3)
@@ -55,7 +57,8 @@ Item {
55 Item {
57 Item {
56 id: itemWrapper
58 id: itemWrapper
57 height: indicatorRow.height
59 height: indicatorRow.height
58− width: childrenRect.width
60+ width: indicatorItem.width
61+ visible: indicatorItem.indicatorVisible
59
62
60 property int ownIndex: index
63 property int ownIndex: index
61 property alias highlighted: indicatorItem.highlighted
64 property alias highlighted: indicatorItem.highlighted
@@ -70,6 +73,21 @@ Item {
70
73
71 widgetSource: model.widgetSource
74 widgetSource: model.widgetSource
72 indicatorProperties : model.indicatorProperties
75 indicatorProperties : model.indicatorProperties
76+
77+ Component.onCompleted: {
78+ if (visibleIndicators == undefined) {
79+ visibleIndicators = {}
80+ }
81+ indicatorRow.visibleIndicators[model.identifier] = indicatorVisible;
82+ indicatorRow.visibleIndicatorsChanged();
83+ }
84+ onIndicatorVisibleChanged: {
85+ if (visibleIndicators == undefined) {
86+ visibleIndicators = {}
87+ }
88+ indicatorRow.visibleIndicators[model.identifier] = indicatorVisible;
89+ indicatorRow.visibleIndicatorsChanged();
90+ }
73 }
91 }
74
92
75 opacity: {
93 opacity: {
Panel/Indicators.qml+17−2
@@ -180,11 +180,19 @@ Showable {
180 clip: !indicators.fullyOpened
180 clip: !indicators.fullyOpened
181 activeHeader: indicators.state == "hint" || indicators.state == "reveal"
181 activeHeader: indicators.state == "hint" || indicators.state == "reveal"
182 enabled: contentEnabled
182 enabled: contentEnabled
183+ visibleIndicators: indicatorRow.visibleIndicators
183
184
184 Connections {
185 Connections {
186+ property bool enableIndexChangeSignal: true
187+
185− target: indicatorRow
188+ target: enableIndexChangeSignal ? indicatorRow : null
186 onCurrentItemIndexChanged: {
189 onCurrentItemIndexChanged: {
190+ var oldActive = enableIndexChangeSignal;
191+ enableIndexChangeSignal = false;
192+
187 menuContent.setCurrentMenuIndex(indicatorRow.currentItemIndex);
193 menuContent.setCurrentMenuIndex(indicatorRow.currentItemIndex);
194+
195+ enableIndexChangeSignal = oldActive;
188 }
196 }
189 }
197 }
190
198
@@ -256,9 +264,16 @@ Showable {
256 state: indicators.state
264 state: indicators.state
257
265
258 Connections {
266 Connections {
267+ property bool enableIndexChangeSignal: true
268+
259− target: menuContent
269+ target: enableIndexChangeSignal ? menuContent : null
260 onCurrentMenuIndexChanged: {
270 onCurrentMenuIndexChanged: {
271+ var oldActive = enableIndexChangeSignal;
272+ enableIndexChangeSignal = false;
273+
261 indicatorRow.setCurrentItem(menuContent.currentMenuIndex);
274 indicatorRow.setCurrentItem(menuContent.currentMenuIndex);
275+
276+ enableIndexChangeSignal = oldActive;
262 }
277 }
263 }
278 }
264 }
279 }
Panel/Indicators/DatetimeIndicatorWidget.qml+4−2
@@ -25,6 +25,7 @@ Indicators.IndicatorWidget {
25 id: indicatorWidget
25 id: indicatorWidget
26
26
27 width: timeLabel.width + units.gu(1)
27 width: timeLabel.width + units.gu(1)
28+ rootMenuType: "com.canonical.indicator.root.time"
28
29
29 property alias label: timeLabel.text
30 property alias label: timeLabel.text
30
31
@@ -52,14 +53,15 @@ Indicators.IndicatorWidget {
52
53
53 onActionStateChanged: {
54 onActionStateChanged: {
54 if (action == undefined || !action.valid) {
55 if (action == undefined || !action.valid) {
56+ enabled = false;
55 return;
57 return;
56 }
58 }
57
59
58 if (action.state == undefined) {
60 if (action.state == undefined) {
59− visible = false;
61+ enabled = false;
60 return;
62 return;
61 }
63 }
62
64
63− indicatorWidget.visible = action.state[3];
65+ enabled = action.state[3];
64 }
66 }
65}
67}
Panel/Indicators/DefaultIndicatorWidget.qml+2−1
@@ -24,7 +24,7 @@ import Unity.IndicatorsLegacy 0.1 as Indicators
24Indicators.IndicatorWidget {
24Indicators.IndicatorWidget {
25 id: indicatorWidget
25 id: indicatorWidget
26
26
27− width: itemRow.width + units.gu(1)
27+ width: itemRow.width + units.gu(0.5)
28
28
29 property alias label: itemLabel.text
29 property alias label: itemLabel.text
30 property alias iconSource: itemImage.source
30 property alias iconSource: itemImage.source
@@ -63,6 +63,7 @@ Indicators.IndicatorWidget {
63
63
64 onActionStateChanged: {
64 onActionStateChanged: {
65 if (action == undefined || !action.valid) {
65 if (action == undefined || !action.valid) {
66+ enabled = false;
66 return;
67 return;
67 }
68 }
68
69
Panel/Indicators/DefaultIndicatorWidget2.qml+43−16
@@ -24,10 +24,11 @@ import Unity.Indicators 0.1 as Indicators
24Indicators.IndicatorWidget {
24Indicators.IndicatorWidget {
25 id: indicatorWidget
25 id: indicatorWidget
26
26
27− width: itemRow.width + units.gu(1)
27+ width: itemRow.width + units.gu(0.5)
28
28
29− property alias label: itemLabel.text
29+ property alias leftLabel: itemLeftLabel.text
30− property alias iconSource: itemImage.source
30+ property alias rightLabel: itemRightLabel.text
31+ property var icons: undefined
31
32
32 Row {
33 Row {
33 id: itemRow
34 id: itemRow
@@ -39,19 +40,43 @@ Indicators.IndicatorWidget {
39 }
40 }
40 spacing: units.gu(0.5)
41 spacing: units.gu(0.5)
41
42
42− // FIXME : Should us Ubuntu.Icon . results in low res images
43+ Label {
43− Image {
44+ id: itemLeftLabel
44− id: itemImage
45− objectName: "itemImage"
45+ objectName: "leftLabel"
46− visible: source != ""
46+ color: Theme.palette.selected.backgroundText
47− height: indicatorWidget.iconSize
47+ opacity: 0.8
48− width: indicatorWidget.iconSize
48+ font.family: "Ubuntu"
49+ fontSize: "medium"
49 anchors.verticalCenter: parent.verticalCenter
50 anchors.verticalCenter: parent.verticalCenter
51+ visible: text != ""
52+ }
53+
54+ Row {
55+ width: childrenRect.width
56+ anchors {
57+ top: parent.top
58+ bottom: parent.bottom
59+ }
60+ spacing: units.gu(0.5)
61+
62+ Repeater {
63+ model: indicatorWidget.icons
64+
65+ Image {
66+ id: itemImage
67+ objectName: "itemImage"
68+ visible: source != ""
69+ source: modelData
70+ height: indicatorWidget.iconSize
71+ width: indicatorWidget.iconSize
72+ anchors.verticalCenter: parent.verticalCenter
73+ }
74+ }
50 }
75 }
51
76
52 Label {
77 Label {
53− id: itemLabel
78+ id: itemRightLabel
54− objectName: "itemLabel"
79+ objectName: "rightLabel"
55 color: Theme.palette.selected.backgroundText
80 color: Theme.palette.selected.backgroundText
56 opacity: 0.8
81 opacity: 0.8
57 font.family: "Ubuntu"
82 font.family: "Ubuntu"
@@ -63,14 +88,16 @@ Indicators.IndicatorWidget {
63
88
64 onActionStateChanged: {
89 onActionStateChanged: {
65 if (actionState == undefined) {
90 if (actionState == undefined) {
66− label = "";
91+ leftLabel = "";
67− iconSource = "";
92+ rightLabel = "";
93+ icons = undefined;
68 enabled = false;
94 enabled = false;
69 return;
95 return;
70 }
96 }
71
97
72− label = actionState.label;
98+ leftLabel = actionState.leftLabel ? actionState.leftLabel : "";
99+ rightLabel = actionState.rightLabel ? actionState.rightLabel : "";
73− iconSource = actionState.icon;
100+ icons = actionState.icons;
74 enabled = actionState.visible;
101 enabled = actionState.visible;
75 }
102 }
76}
103}
Panel/Indicators/NetworkIndicatorWidget.qml+4−1
@@ -24,7 +24,7 @@ import Unity.IndicatorsLegacy 0.1 as Indicators
24Indicators.IndicatorWidget {
24Indicators.IndicatorWidget {
25 id: indicatorWidget
25 id: indicatorWidget
26
26
27− width: networkIcon.width + units.gu(1)
27+ width: networkIcon.width + units.gu(0.5)
28
28
29 property int signalStrength: 0
29 property int signalStrength: 0
30 property int connectionState: Indicators.NetworkConnection.Initial
30 property int connectionState: Indicators.NetworkConnection.Initial
@@ -43,14 +43,17 @@ Indicators.IndicatorWidget {
43
43
44 onActionStateChanged: {
44 onActionStateChanged: {
45 if (action == undefined || !action.valid) {
45 if (action == undefined || !action.valid) {
46+ enabled = false;
46 return;
47 return;
47 }
48 }
48
49
49 if (action.state == undefined) {
50 if (action.state == undefined) {
51+ enabled = false;
50 connectionState = 0;
52 connectionState = 0;
51 return;
53 return;
52 }
54 }
53
55
56+ enabled = true;
54 connectionState = action.state[Indicators.NetworkActionState.Connection];
57 connectionState = action.state[Indicators.NetworkActionState.Connection];
55 if (connectionState == Indicators.NetworkConnection.Activated) {
58 if (connectionState == Indicators.NetworkConnection.Activated) {
56 signalStrength = action.state[Indicators.NetworkActionState.SignalStrength];
59 signalStrength = action.state[Indicators.NetworkActionState.SignalStrength];
Panel/Indicators/SoundIndicatorWidget.qml+1
@@ -22,4 +22,5 @@ import QtQuick 2.0
22DefaultIndicatorWidget {
22DefaultIndicatorWidget {
23 // default the icon so we have something to show when we don't have the bus.
23 // default the icon so we have something to show when we don't have the bus.
24 iconSource: "image://gicon/audio-volume-high"
24 iconSource: "image://gicon/audio-volume-high"
25+ enabled: true
25}
26}
Panel/MenuContent.qml+25−7
@@ -17,6 +17,7 @@
17import QtQuick 2.0
17import QtQuick 2.0
18import Ubuntu.Components 0.1
18import Ubuntu.Components 0.1
19import Unity.Indicators 0.1 as Indicators
19import Unity.Indicators 0.1 as Indicators
20+import Utils 0.1
20import "../Components"
21import "../Components"
21
22
22// FIXME : We dont want to use MainView.
23// FIXME : We dont want to use MainView.
@@ -27,20 +28,23 @@ MainView {
27
28
28 property QtObject indicatorsModel: null
29 property QtObject indicatorsModel: null
29 property bool __contentActive: false
30 property bool __contentActive: false
30− readonly property int currentMenuIndex : tabs.selectedTabIndex
31+ readonly property int currentMenuIndex : filteredIndicators.mapToSource(tabs.selectedTabIndex)
31 backgroundColor: "#221e1c" // FIXME not in palette yet
32 backgroundColor: "#221e1c" // FIXME not in palette yet
32 property int contentReleaseInterval: 20000
33 property int contentReleaseInterval: 20000
33 property bool activeHeader: false
34 property bool activeHeader: false
35+ property alias visibleIndicators: visibleIndicatorsModel.visible
34
36
35 width: units.gu(40)
37 width: units.gu(40)
36 height: units.gu(42)
38 height: units.gu(42)
37
39
38 function setCurrentMenuIndex(index) {
40 function setCurrentMenuIndex(index) {
39− if (currentMenuIndex !== index) {
41+ var filteredIndex = filteredIndicators.mapFromSource(index)
40− if (currentMenuIndex == -1) {
42+
43+ if (tabs.selectedTabIndex !== filteredIndex) {
44+ if (tabs.selectedTabIndex == -1) {
41 tabs.tabBar.animate = false;
45 tabs.tabBar.animate = false;
42 }
46 }
43− tabs.selectedTabIndex = index;
47+ tabs.selectedTabIndex = filteredIndex;
44 tabs.tabBar.animate = true;
48 tabs.tabBar.animate = true;
45 }
49 }
46 }
50 }
@@ -61,6 +65,20 @@ MainView {
61 tabs.tabBar.alwaysSelectionMode = activeHeader;
65 tabs.tabBar.alwaysSelectionMode = activeHeader;
62 }
66 }
63
67
68+ SortFilterProxyModel {
69+ id: filteredIndicators
70+ model: visibleIndicatorsModel
71+ dynamicSortFilter: true
72+
73+ filterRole: Indicators.IndicatorsModelRole.IsVisible
74+ filterRegExp: RegExp("^true$")
75+ }
76+
77+ Indicators.VisibleIndicatorsModel {
78+ id: visibleIndicatorsModel
79+ model: indicatorsModel
80+ }
81+
64 Tabs {
82 Tabs {
65 id: tabs
83 id: tabs
66 objectName: "tabs"
84 objectName: "tabs"
@@ -69,8 +87,8 @@ MainView {
69
87
70 Repeater {
88 Repeater {
71 id: repeater
89 id: repeater
72− model: indicatorsModel
90+ model: filteredIndicators
73− objectName: "menus"
91+ objectName: "tabsRepeater"
74
92
75 // FIXME: This is needed because tabs dont handle repeaters well.
93 // FIXME: This is needed because tabs dont handle repeaters well.
76 // Due to the child ordering happening after child insertion.
94 // Due to the child ordering happening after child insertion.
@@ -114,7 +132,7 @@ MainView {
114 item[pName] = indicatorProperties[pName]
132 item[pName] = indicatorProperties[pName]
115 }
133 }
116 }
134 }
117− if (contentActive && menus.visible) {
135+ if (contentActive && tabs.visible) {
118 item.start()
136 item.start()
119 }
137 }
120 }
138 }
Shell.qml+9
@@ -95,6 +95,15 @@ FocusScope {
95 greeter.hide();
95 greeter.hide();
96 shell.activateApplication(desktopFile);
96 shell.activateApplication(desktopFile);
97 }
97 }
98+
99+ onMainStageFocusedApplicationChanged: {
100+ var app = applicationManager.mainStageFocusedApplication
101+ if (app != null) {
102+ LauncherModel.applicationFocused(app.desktopFile);
103+ } else {
104+ LauncherModel.applicationFocused("");
105+ }
106+ }
98 }
107 }
99
108
100 function activateApplication(desktopFile, argument) {
109 function activateApplication(desktopFile, argument) {
build+9
@@ -57,6 +57,15 @@ mk_build_deps() {
57 [ ! -f unity8-build-deps*deb -o $CODE_DIR/debian/control -nt unity8-build-deps*deb ] && mk-build-deps --install --root-cmd sudo $CODE_DIR/debian/control
57 [ ! -f unity8-build-deps*deb -o $CODE_DIR/debian/control -nt unity8-build-deps*deb ] && mk-build-deps --install --root-cmd sudo $CODE_DIR/debian/control
58}
58}
59
59
60+if [ -f "/usr/bin/ccache" ] ; then
61+ if [ "x$CC" = "x" ]; then
62+ export CC='ccache gcc'
63+ fi
64+ if [ "x$CXX" = "x" ] ; then
65+ export CXX='ccache g++'
66+ fi
67+fi
68+
60if [ -f "/usr/bin/ninja" ] ; then
69if [ -f "/usr/bin/ninja" ] ; then
61 GENERATOR="-G Ninja"
70 GENERATOR="-G Ninja"
62 # Ninja does not need -j, it parallelizes automatically.
71 # Ninja does not need -j, it parallelizes automatically.
debian/changelog+71
@@ -1,3 +1,74 @@
1+unity8 (7.81.3+13.10.20130905.2-0ubuntu1) saucy; urgency=low
2+
3+ [ Michael Zanetti ]
4+ * Integrate Launcher with AppManager.
5+
6+ [ Nick Dedekind ]
7+ * Removed FIXME from slider int->double conversion.
8+
9+ [ Ubuntu daily release ]
10+ * Automatic snapshot from revision 291
11+
12+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Thu, 05 Sep 2013 10:48:02 +0000
13+
14+unity8 (7.81.3+13.10.20130904.1-0ubuntu1) saucy; urgency=low
15+
16+ [ Nicolas d'Offay ]
17+ * Switched infographic background at design's request.
18+
19+ [ Ubuntu daily release ]
20+ * Automatic snapshot from revision 287
21+
22+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 04 Sep 2013 07:34:57 +0000
23+
24+unity8 (7.81.3+13.10.20130904-0ubuntu1) saucy; urgency=low
25+
26+ [ mhall119 ]
27+ * Add a little bit of text to the last step of the tour telling the
28+ user how to end it and get to their phone.
29+
30+ [ Jussi Pakkanen ]
31+ * Use CCache if it is installed.
32+
33+ [ Nick Dedekind ]
34+ * Multiple icon/label support for indicators.
35+
36+ [ Albert Astals ]
37+ * Remove unneeded role.
38+
39+ [ Lars Uebernickel ]
40+ * Fall back to "ubuntu-mobile" icon theme if $UBUNTU_ICON_THEME is
41+ unset.
42+
43+ [ Michael Zanetti ]
44+ * Use MouseAreas in DashBar to enable clicking again.
45+ * load launcher default config from existing dconf key.
46+
47+ [ Ubuntu daily release ]
48+ * Automatic snapshot from revision 285
49+
50+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 04 Sep 2013 03:02:57 +0000
51+
52+unity8 (7.81.3+13.10.20130903.1-0ubuntu1) saucy; urgency=low
53+
54+ [ Michael Zanetti ]
55+ * workaround quicklist text color.
56+
57+ [ Ubuntu daily release ]
58+ * Automatic snapshot from revision 277
59+
60+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 03 Sep 2013 06:09:36 +0000
61+
62+unity8 (7.81.3+13.10.20130830-0ubuntu1) saucy; urgency=low
63+
64+ [ Pawel Stolowski ]
65+ * Implement a virtual 'All' filter option.
66+
67+ [ Ubuntu daily release ]
68+ * Automatic snapshot from revision 275
69+
70+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Fri, 30 Aug 2013 12:44:40 +0000
71+
1unity8 (7.81.3+13.10.20130829.2-0ubuntu1) saucy; urgency=low
72unity8 (7.81.3+13.10.20130829.2-0ubuntu1) saucy; urgency=low
2
73
3 [ Michael Terry ]
74 [ Michael Terry ]
debian/control+1
@@ -9,6 +9,7 @@ Build-Depends: cmake,
9 libgl1-mesa-dev[!armhf] | libgl-dev[!armhf],
9 libgl1-mesa-dev[!armhf] | libgl-dev[!armhf],
10 libgl1-mesa-dri,
10 libgl1-mesa-dri,
11 libgles2-mesa-dev[armhf],
11 libgles2-mesa-dev[armhf],
12+ libgsettings-qt-dev,
12 libhud-client2-dev,
13 libhud-client2-dev,
13 libjson-perl,
14 libjson-perl,
14 libnih-dbus-dev,
15 libnih-dbus-dev,
main.cpp+3−2
@@ -65,9 +65,10 @@ void appendImportPaths(QQmlEngine *engine, const QStringList &paths)
65
65
66void resolveIconTheme() {
66void resolveIconTheme() {
67 const char *ubuntuIconTheme = getenv("UBUNTU_ICON_THEME");
67 const char *ubuntuIconTheme = getenv("UBUNTU_ICON_THEME");
68− if (ubuntuIconTheme != NULL) {
68+ if (ubuntuIconTheme == NULL) {
69− QIcon::setThemeName(ubuntuIconTheme);
69+ ubuntuIconTheme = "ubuntu-mobile";
70 }
70 }
71+ QIcon::setThemeName(ubuntuIconTheme);
71}
72}
72} // namespace {
73} // namespace {
73
74
plugins/Unity/CMakeLists.txt+1
@@ -47,6 +47,7 @@ set(QMLPLUGIN_SRC
47 signalslist.cpp
47 signalslist.cpp
48 socialpreview.cpp
48 socialpreview.cpp
49 socialpreviewcomment.cpp
49 socialpreviewcomment.cpp
50+ iconutils.cpp
50 variantutils.cpp
51 variantutils.cpp
51)
52)
52
53
plugins/Unity/Indicators/CMakeLists.txt+9
@@ -35,11 +35,20 @@ set(IndicatorsQML_SOURCES
35 rootactionstate.cpp
35 rootactionstate.cpp
36 unitymenumodelcache.cpp
36 unitymenumodelcache.cpp
37 unitymenumodelstack.cpp
37 unitymenumodelstack.cpp
38+ visibleindicatorsmodel.cpp
38)
39)
39add_definitions(-DUNITYINDICATORS_LIBRARY)
40add_definitions(-DUNITYINDICATORS_LIBRARY)
40
41
42+set(IndicatorsQML_RESOURCES
43+ resources/indicators.qrc
44+)
45+
46+qt5_add_resources(IndicatorsQML_RESOURCES_RCC
47+ ${IndicatorsQML_RESOURCES}
48+)
41
49
42add_library(IndicatorsQml SHARED
50add_library(IndicatorsQml SHARED
51+ ${IndicatorsQML_RESOURCES_RCC}
43 ${IndicatorsQML_SOURCES}
52 ${IndicatorsQML_SOURCES}
44)
53)
45
54
plugins/Unity/Indicators/Network/qml/AccessPoint.qml+71−26
@@ -22,13 +22,15 @@ import Ubuntu.Components 0.1
22import Ubuntu.Components.ListItems 0.1 as ListItem
22import Ubuntu.Components.ListItems 0.1 as ListItem
23import Unity.Indicators 0.1 as Indicators
23import Unity.Indicators 0.1 as Indicators
24
24
25−Indicators.FramedMenuItem {
25+Indicators.BaseMenuItem {
26− id: accessPoint
26+ id: menuItem
27+ implicitHeight: units.gu(5.5)
27
28
28 property bool checked: false
29 property bool checked: false
29 property bool secure: false
30 property bool secure: false
30 property bool adHoc: false
31 property bool adHoc: false
31 property int signalStrength: 0
32 property int signalStrength: 0
33+ property alias text: label.text
32
34
33 signal activate()
35 signal activate()
34
36
@@ -37,36 +39,79 @@ Indicators.FramedMenuItem {
37 checkBoxActive.checked = checked;
39 checkBoxActive.checked = checked;
38 }
40 }
39
41
40− icon: {
42+ onClicked: {
41− var imageName = "nm-signal-100"
43+ checkBoxActive.clicked();
42−
44+ }
43− if (adHoc) {
45+
44− imageName = "nm-adhoc";
46+ CheckBox {
45− } else if (signalStrength == 0) {
47+ id: checkBoxActive
46− imageName = "nm-signal-00";
48+ height: units.gu(3)
47− } else if (signalStrength <= 25) {
49+ width: units.gu(3)
48− imageName = "nm-signal-25";
50+
49− } else if (signalStrength <= 50) {
51+ anchors {
50− imageName = "nm-signal-50";
52+ left: parent.left
51− } else if (signalStrength <= 75) {
53+ leftMargin: menuItem.__contentsMargins
52− imageName = "nm-signal-75";
54+ verticalCenter: parent.verticalCenter
53 }
55 }
54
56
55− if (secure) {
57+ onClicked: {
56− imageName += "-secure";
58+ menuItem.activate();
57 }
59 }
58− return "image://gicon/" + imageName;
59 }
60 }
60
61
61− iconFrame: false
62+ Image {
62− control: CheckBox {
63+ id: iconSignal
63− id: checkBoxActive
64− height: units.gu(4)
65− width: units.gu(4)
66− anchors.centerIn: parent
67
64
68− onClicked: {
65+ width: height
69− accessPoint.activate();
66+ height: Math.min(units.gu(5), parent.height - units.gu(1))
67+ anchors {
68+ left: checkBoxActive.right
69+ leftMargin: units.gu(1)
70+ verticalCenter: parent.verticalCenter
71+ }
72+
73+ source: {
74+ var imageName = "nm-signal-100"
75+
76+ if (adHoc) {
77+ imageName = "nm-adhoc";
78+ } else if (signalStrength == 0) {
79+ imageName = "nm-signal-00";
80+ } else if (signalStrength <= 25) {
81+ imageName = "nm-signal-25";
82+ } else if (signalStrength <= 50) {
83+ imageName = "nm-signal-50";
84+ } else if (signalStrength <= 75) {
85+ imageName = "nm-signal-75";
86+ }
87+ return "image://theme/" + imageName;
88+ }
89+ }
90+
91+ Label {
92+ id: label
93+ anchors {
94+ left: iconSignal.right
95+ leftMargin: units.gu(1)
96+ verticalCenter: parent.verticalCenter
97+ right: iconSecure.visible ? iconSecure.left : parent.right
98+ rightMargin: menuItem.__contentsMargins
99+ }
100+ elide: Text.ElideRight
101+ opacity: label.enabled ? 1.0 : 0.5
102+ }
103+
104+ Image {
105+ id: iconSecure
106+ visible: secure
107+ source: "qrc:/indicators/artwork/network/secure.svg"
108+
109+ width: height
110+ height: Math.min(units.gu(4), parent.height - units.gu(1))
111+ anchors {
112+ right: parent.right
113+ rightMargin: units.gu(1)
114+ verticalCenter: parent.verticalCenter
70 }
115 }
71 }
116 }
72}
117}
plugins/Unity/Indicators/indicator.cpp−2
@@ -51,7 +51,6 @@ void Indicator::init(const QString& busName, const QSettings& settings)
51 }
51 }
52 }
52 }
53
53
54−
55 QVariantMap properties;
54 QVariantMap properties;
56 properties.clear();
55 properties.clear();
57 properties.insert("busType", 1);
56 properties.insert("busType", 1);
@@ -87,7 +86,6 @@ void Indicator::setPosition(int position)
87 }
86 }
88}
87}
89
88
90−
91QVariant Indicator::indicatorProperties() const
89QVariant Indicator::indicatorProperties() const
92{
90{
93 return m_properties;
91 return m_properties;
plugins/Unity/Indicators/indicator.h−4
@@ -29,10 +29,6 @@
29class UNITYINDICATORS_EXPORT Indicator : public QObject
29class UNITYINDICATORS_EXPORT Indicator : public QObject
30{
30{
31 Q_OBJECT
31 Q_OBJECT
32− Q_PROPERTY(QString identifier READ identifier NOTIFY identifierChanged)
33− Q_PROPERTY(int position READ position NOTIFY positionChanged)
34− Q_PROPERTY(QVariant indicatorProperties READ indicatorProperties NOTIFY indicatorPropertiesChanged)
35−
36public:
32public:
37 typedef QSharedPointer<Indicator> Ptr;
33 typedef QSharedPointer<Indicator> Ptr;
38
34
plugins/Unity/Indicators/indicators.h+1−1
@@ -79,7 +79,7 @@ public:
79 WidgetSource,
79 WidgetSource,
80 PageSource,
80 PageSource,
81 IndicatorProperties,
81 IndicatorProperties,
82− IsValid
82+ IsVisible
83 };
83 };
84
84
85 IndicatorsModelRole(QObject*parent=0):QObject(parent) {}
85 IndicatorsModelRole(QObject*parent=0):QObject(parent) {}
plugins/Unity/Indicators/indicatorsmodel.cpp−3
@@ -206,7 +206,6 @@ QHash<int, QByteArray> IndicatorsModel::roleNames() const
206 roles[IndicatorsModelRole::WidgetSource] = "widgetSource";
206 roles[IndicatorsModelRole::WidgetSource] = "widgetSource";
207 roles[IndicatorsModelRole::PageSource] = "pageSource";
207 roles[IndicatorsModelRole::PageSource] = "pageSource";
208 roles[IndicatorsModelRole::IndicatorProperties] = "indicatorProperties";
208 roles[IndicatorsModelRole::IndicatorProperties] = "indicatorProperties";
209− roles[IndicatorsModelRole::IsValid] = "isValid";
210 }
209 }
211 return roles;
210 return roles;
212}
211}
@@ -269,8 +268,6 @@ QVariant IndicatorsModel::data(const QModelIndex &index, int role) const
269 return QVariant(indicator->indicatorProperties());
268 return QVariant(indicator->indicatorProperties());
270 }
269 }
271 break;
270 break;
272− case IndicatorsModelRole::IsValid:
273− return (indicator ? true : false);
274 case IndicatorsModelRole::Title:
271 case IndicatorsModelRole::Title:
275 case IndicatorsModelRole::Description:
272 case IndicatorsModelRole::Description:
276 case IndicatorsModelRole::WidgetSource:
273 case IndicatorsModelRole::WidgetSource:
plugins/Unity/Indicators/indicatorsmodel.h+2−1
@@ -44,7 +44,7 @@ public:
44 Q_INVOKABLE void load();
44 Q_INVOKABLE void load();
45 Q_INVOKABLE void unload();
45 Q_INVOKABLE void unload();
46
46
47− Q_INVOKABLE QVariant data(int row, int role = Qt::DisplayRole) const;
47+ Q_INVOKABLE QVariant data(int row, int role) const;
48
48
49 /* QAbstractItemModel */
49 /* QAbstractItemModel */
50 QHash<int, QByteArray> roleNames() const;
50 QHash<int, QByteArray> roleNames() const;
@@ -56,6 +56,7 @@ public:
56 void setIndicatorData(const QVariant& data);
56 void setIndicatorData(const QVariant& data);
57 QVariant indicatorData() const;
57 QVariant indicatorData() const;
58
58
59+
59Q_SIGNALS:
60Q_SIGNALS:
60 void countChanged();
61 void countChanged();
61 void indicatorDataChanged(const QVariant& data);
62 void indicatorDataChanged(const QVariant& data);
plugins/Unity/Indicators/plugin.cpp+2
@@ -32,6 +32,7 @@
32#include "rootactionstate.h"
32#include "rootactionstate.h"
33#include "unitymenumodelcache.h"
33#include "unitymenumodelcache.h"
34#include "unitymenumodelstack.h"
34#include "unitymenumodelstack.h"
35+#include "visibleindicatorsmodel.h"
35
36
36static QObject* menuModelCacheSingleton(QQmlEngine* engine, QJSEngine* scriptEngine) {
37static QObject* menuModelCacheSingleton(QQmlEngine* engine, QJSEngine* scriptEngine) {
37 Q_UNUSED(engine);
38 Q_UNUSED(engine);
@@ -47,6 +48,7 @@ void Indicators2Plugin::registerTypes(const char *uri)
47 qmlRegisterType<UnityMenuModelStack>(uri, 0, 1, "UnityMenuModelStack");
48 qmlRegisterType<UnityMenuModelStack>(uri, 0, 1, "UnityMenuModelStack");
48 qmlRegisterType<RootActionState>(uri, 0, 1, "RootActionState");
49 qmlRegisterType<RootActionState>(uri, 0, 1, "RootActionState");
49 qmlRegisterType<ModelPrinter>(uri, 0, 1, "ModelPrinter");
50 qmlRegisterType<ModelPrinter>(uri, 0, 1, "ModelPrinter");
51+ qmlRegisterType<VisibleIndicatorsModel>(uri, 0, 1, "VisibleIndicatorsModel");
50
52
51 qmlRegisterSingletonType<UnityMenuModelCache>(uri, 0, 1, "UnityMenuModelCache", menuModelCacheSingleton);
53 qmlRegisterSingletonType<UnityMenuModelCache>(uri, 0, 1, "UnityMenuModelCache", menuModelCacheSingleton);
52
54
plugins/Unity/Indicators/qml/IndicatorBase.qml−5
@@ -24,8 +24,6 @@ import Unity.Indicators 0.1 as Indicators
24Item {
24Item {
25 id: indicatorItem
25 id: indicatorItem
26
26
27− // FIXME : should be disabled until bus available when we have desktop indicators
28− // for now, disable when we dont habe the correct profile.
29 enabled: menuObjectPaths.hasOwnProperty(device)
27 enabled: menuObjectPaths.hasOwnProperty(device)
30
28
31 //const
29 //const
@@ -39,9 +37,6 @@ Item {
39
37
40 property string deviceMenuObjectPath: menuObjectPaths.hasOwnProperty(device) ? menuObjectPaths[device] : ""
38 property string deviceMenuObjectPath: menuObjectPaths.hasOwnProperty(device) ? menuObjectPaths[device] : ""
41
39
42− signal actionGroupUpdated()
43− signal modelUpdated()
44−
45 property alias menuModel: cachedModel.model
40 property alias menuModel: cachedModel.model
46
41
47 CachedUnityMenuModel {
42 CachedUnityMenuModel {
plugins/Unity/Indicators/qml/IndicatorWidget.qml+1
@@ -24,6 +24,7 @@ IndicatorBase {
24 id: indicatorWidget
24 id: indicatorWidget
25
25
26 active: true
26 active: true
27+ enabled: false
27 property int iconSize: height - units.gu(0.5)
28 property int iconSize: height - units.gu(0.5)
28
29
29 property var actionState: Indicators.RootActionState {
30 property var actionState: Indicators.RootActionState {
plugins/Unity/Indicators/qml/MenuItemFactory.qml+3−18
@@ -79,23 +79,8 @@ Item {
79 'max-icon': 'icon'});
79 'max-icon': 'icon'});
80 }
80 }
81
81
82− // FIXME: The interval should be [0.0 - 1.0]. Unfortunately, when
83− // reaching the boundaries (0.0 or 1.0), the value is converted
84− // to an integer when automatically wrapped in a variant when
85− // passed to QStateAction::updateState(…). The server chokes on
86− // those values, complaining that they’re not of the right type…
87 onChangeState: {
82 onChangeState: {
88− if (value == Math.round(value)) {
89− if (value >= maximumValue) {
90− model.changeState(modelIndex, maximumValue - 0.000001);
91− } else if (value <= minimumValue) {
92− model.changeState(modelIndex, minimumValue + 0.000001);
83+ model.changeState(modelIndex, value);
93− } else {
94− model.changeState(modelIndex, value * 1.000001);
95− }
96− } else {
97− model.changeState(modelIndex, value);
98− }
99 }
84 }
100 }
85 }
101 }
86 }
@@ -184,7 +169,7 @@ Item {
184 id: accessPoint;
169 id: accessPoint;
185 ICNetwork.AccessPoint {
170 ICNetwork.AccessPoint {
186 property QtObject menu: null
171 property QtObject menu: null
187− property var strenthAction: QMenuModel.UnityMenuAction {
172+ property var strengthAction: QMenuModel.UnityMenuAction {
188 model: menuFactory.model ? menuFactory.model : null
173 model: menuFactory.model ? menuFactory.model : null
189 index: modelIndex
174 index: modelIndex
190 name: menu ? menu.ext.xCanonicalWifiApStrengthAction : ""
175 name: menu ? menu.ext.xCanonicalWifiApStrengthAction : ""
@@ -194,7 +179,7 @@ Item {
194 secure: menu ? menu.ext.xCanonicalWifiApIsSecure : false
179 secure: menu ? menu.ext.xCanonicalWifiApIsSecure : false
195 adHoc: menu ? menu.ext.xCanonicalWifiApIsAdhoc : false
180 adHoc: menu ? menu.ext.xCanonicalWifiApIsAdhoc : false
196 checked: menu ? menu.isToggled : false
181 checked: menu ? menu.isToggled : false
197− signalStrength: strenthAction.valid ? strenthAction.state : 0
182+ signalStrength: strengthAction.valid ? strengthAction.state : 0
198 enabled: menu ? menu.sensitive : false
183 enabled: menu ? menu.sensitive : false
199
184
200 Component.onCompleted: {
185 Component.onCompleted: {
plugins/Unity/Indicators/resources/artwork/network/secure.svgnew file+71
@@ -0,0 +1,71 @@
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+ width="64"
13+ height="64"
14+ id="svg2986"
15+ version="1.1"
16+ inkscape:version="0.48.4 r9939"
17+ sodipodi:docname="secure.svg">
18+ <defs
19+ id="defs2988" />
20+ <sodipodi:namedview
21+ id="base"
22+ pagecolor="#ffffff"
23+ bordercolor="#666666"
24+ borderopacity="1.0"
25+ inkscape:pageopacity="0.0"
26+ inkscape:pageshadow="2"
27+ inkscape:zoom="7.9180415"
28+ inkscape:cx="7.5048745"
29+ inkscape:cy="29.401628"
30+ inkscape:current-layer="layer1"
31+ showgrid="true"
32+ inkscape:grid-bbox="true"
33+ inkscape:document-units="px"
34+ inkscape:snap-grids="false"
35+ inkscape:window-width="1549"
36+ inkscape:window-height="876"
37+ inkscape:window-x="51"
38+ inkscape:window-y="24"
39+ inkscape:window-maximized="1">
40+ <inkscape:grid
41+ type="xygrid"
42+ id="grid3012"
43+ empspacing="5"
44+ visible="true"
45+ enabled="true"
46+ snapvisiblegridlinesonly="true" />
47+ </sodipodi:namedview>
48+ <metadata
49+ id="metadata2991">
50+ <rdf:RDF>
51+ <cc:Work
52+ rdf:about="">
53+ <dc:format>image/svg+xml</dc:format>
54+ <dc:type
55+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
56+ <dc:title />
57+ </cc:Work>
58+ </rdf:RDF>
59+ </metadata>
60+ <g
61+ id="layer1"
62+ inkscape:label="Layer 1"
63+ inkscape:groupmode="layer"
64+ transform="translate(0,32)">
65+ <path
66+ inkscape:connector-curvature="0"
67+ d="M 29.999999,-18 C 25.568001,-18 22,-14.432 22,-10 v 6 h -2 c -1.107999,0 -2,0.892 -2,2 v 18 c 0,1.108 0.892001,2 2,2 h 24 c 1.108,0 2,-0.892 2,-2 V -2 c 0,-1.108 -0.892,-2 -2,-2 h -2 v -6 c 0,-4.432 -3.568001,-8 -8.000001,-8 h -4 z m 0,4 h 4 c 2.216,0 4,1.784 4,4 v 6 H 26.000001 v -6 c 0,-2.216 1.784,-4 3.999998,-4 z"
68+ style="fill:#dfdbd2;fill-rule:evenodd"
69+ id="path8" />
70+ </g>
71+</svg>
plugins/Unity/Indicators/resources/indicators.qrcnew file+5
@@ -0,0 +1,5 @@
1+<RCC>
2+ <qresource prefix="/indicators">
3+ <file>artwork/network/secure.svg</file>
4+ </qresource>
5+</RCC>
plugins/Unity/Indicators/rootactionstate.cpp+44−14
@@ -108,25 +108,32 @@ bool RootActionState::isValid() const
108 return m_menu && m_menu->rowCount() > 0;
108 return m_menu && m_menu->rowCount() > 0;
109}
109}
110
110
111−QString RootActionState::label() const
111+QString RootActionState::leftLabel() const
112{
112{
113 if (!isValid()) return QString();
113 if (!isValid()) return QString();
114
114
115− return m_cachedState.value("label", QVariant::fromValue(QString())).toString();
115+ return m_cachedState.value("pre-label", QVariant::fromValue(QString())).toString();
116}
116}
117
117
118−QString RootActionState::icon() const
118+QString RootActionState::rightLabel() const
119{
119{
120 if (!isValid()) return QString();
120 if (!isValid()) return QString();
121
121
122− return m_cachedState.value("icon", QVariant::fromValue(QString())).toString();
122+ return m_cachedState.value("label", QVariant::fromValue(QString())).toString();
123+}
124+
125+QStringList RootActionState::icons() const
126+{
127+ if (!isValid()) return QStringList();
128+
129+ return m_cachedState.value("icons", QVariant::fromValue(QStringList())).toStringList();
123}
130}
124
131
125QString RootActionState::accessibleName() const
132QString RootActionState::accessibleName() const
126{
133{
127 if (!isValid()) return QString();
134 if (!isValid()) return QString();
128
135
129− return m_cachedState.value("accessible-name", QVariant::fromValue(QString())).toString();
136+ return m_cachedState.value("accessible-desc", QVariant::fromValue(QString())).toString();
130}
137}
131
138
132bool RootActionState::isVisible() const
139bool RootActionState::isVisible() const
@@ -198,16 +205,39 @@ QVariant RootActionState::toQVariant(GVariant* state) const
198 while (g_variant_iter_loop (&iter, "{sv}", &key, &vvalue))
205 while (g_variant_iter_loop (&iter, "{sv}", &key, &vvalue))
199 {
206 {
200 QString str = QString::fromUtf8(key);
207 QString str = QString::fromUtf8(key);
201− if (str == "icon") {
208+ if (str == "icon" && !qmap.contains("icons")) {
209+ QStringList icons;
210+
202 // FIXME - should be sending a url.
211 // FIXME - should be sending a url.
203− GIcon *icon = g_icon_deserialize (vvalue);
212+ GIcon *gicon = g_icon_deserialize (vvalue);
204− if (icon) {
213+ if (gicon) {
205− qmap.insert(str, iconUri(icon));
214+ icons << iconUri(gicon);
206− g_object_unref (icon);
215+ g_object_unref (gicon);
207 }
216 }
208− else {
209− qmap.insert(str, QVariant(""));
217+ qmap.insert("icons", icons);
218+
219+ } else if (str == "icons") {
220+
221+ QStringList icons;
222+
223+ if (g_variant_type_is_array(g_variant_get_type(vvalue))) {
224+
225+
226+ for (int i = 0, iMax = g_variant_n_children(vvalue); i < iMax; i++) {
227+ GVariant *child = g_variant_get_child_value(vvalue, i);
228+
229+ // FIXME - should be sending a url.
230+ GIcon *gicon = g_icon_deserialize (child);
231+ if (gicon) {
232+ icons << iconUri(gicon);
233+ g_object_unref (gicon);
234+ }
235+ g_variant_unref(child);
236+ }
210 }
237 }
238+ // will overwrite icon.
239+ qmap.insert("icons", icons);
240+
211 } else {
241 } else {
212 qmap.insert(str, ActionStateParser::toQVariant(vvalue));
242 qmap.insert(str, ActionStateParser::toQVariant(vvalue));
213 }
243 }
@@ -230,12 +260,12 @@ QVariant RootActionState::toQVariant(GVariant* state) const
230 &visible);
260 &visible);
231
261
232 qmap["label"] = label ? QString::fromUtf8(label) : "";
262 qmap["label"] = label ? QString::fromUtf8(label) : "";
233− qmap["accessible-name"] = accessible_name ? QString::fromUtf8(accessible_name) : "";
263+ qmap["accessible-desc"] = accessible_name ? QString::fromUtf8(accessible_name) : "";
234 qmap["visible"] = visible;
264 qmap["visible"] = visible;
235
265
236 gicon = g_icon_new_for_string (icon, NULL);
266 gicon = g_icon_new_for_string (icon, NULL);
237 if (gicon) {
267 if (gicon) {
238− qmap["icon"] = iconUri(gicon);
268+ qmap["icons"] = QStringList() << iconUri(gicon);
239 g_object_unref (gicon);
269 g_object_unref (gicon);
240 }
270 }
241
271
plugins/Unity/Indicators/rootactionstate.h+9−6
@@ -32,8 +32,9 @@ class UNITYINDICATORS_EXPORT RootActionState : public ActionStateParser
32 Q_PROPERTY(UnityMenuModel* menu READ menu WRITE setMenu NOTIFY menuChanged)
32 Q_PROPERTY(UnityMenuModel* menu READ menu WRITE setMenu NOTIFY menuChanged)
33
33
34 Q_PROPERTY(bool valid READ isValid NOTIFY validChanged)
34 Q_PROPERTY(bool valid READ isValid NOTIFY validChanged)
35− Q_PROPERTY(QString label READ label NOTIFY labelChanged)
35+ Q_PROPERTY(QString leftLabel READ leftLabel NOTIFY leftLabelChanged)
36− Q_PROPERTY(QString icon READ icon NOTIFY iconChanged)
36+ Q_PROPERTY(QString rightLabel READ rightLabel NOTIFY rightLabelChanged)
37+ Q_PROPERTY(QStringList icons READ icons NOTIFY iconsChanged)
37 Q_PROPERTY(QString accessibleName READ accessibleName NOTIFY accessibleNameChanged)
38 Q_PROPERTY(QString accessibleName READ accessibleName NOTIFY accessibleNameChanged)
38 Q_PROPERTY(bool visible READ isVisible NOTIFY visibleChanged)
39 Q_PROPERTY(bool visible READ isVisible NOTIFY visibleChanged)
39public:
40public:
@@ -47,8 +48,9 @@ public:
47 void setIndex(int index);
48 void setIndex(int index);
48
49
49 bool isValid() const;
50 bool isValid() const;
50− QString label() const;
51+ QString leftLabel() const;
51− QString icon() const;
52+ QString rightLabel() const;
53+ QStringList icons() const;
52 QString accessibleName() const;
54 QString accessibleName() const;
53 bool isVisible() const;
55 bool isVisible() const;
54
56
@@ -62,8 +64,9 @@ Q_SIGNALS:
62 void indexChanged();
64 void indexChanged();
63
65
64 void validChanged();
66 void validChanged();
65− void labelChanged();
67+ void leftLabelChanged();
66− void iconChanged();
68+ void rightLabelChanged();
69+ void iconsChanged();
67 void accessibleNameChanged();
70 void accessibleNameChanged();
68 void visibleChanged();
71 void visibleChanged();
69
72
plugins/Unity/Indicators/visibleindicatorsmodel.cppnew file+94
@@ -0,0 +1,94 @@
1+/*
2+ * Copyright (C) 2012, 2013 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+// self
18+#include "visibleindicatorsmodel.h"
19+#include "indicators.h"
20+
21+VisibleIndicatorsModel::VisibleIndicatorsModel(QObject *parent)
22+ : QIdentityProxyModel(parent),
23+ m_inserting(false)
24+{
25+ QObject::connect(this, SIGNAL(rowsAboutToBeInserted(QModelIndex, int, int)), this, SLOT(onBeginRowInserted(QModelIndex, int, int)));
26+ QObject::connect(this, SIGNAL(rowsInserted(QModelIndex, int, int)), this, SLOT(onRowInserted(QModelIndex, int, int)));
27+}
28+
29+QHash<int, QByteArray> VisibleIndicatorsModel::roleNames() const
30+{
31+ static QHash<int, QByteArray> roles;
32+ if (roles.isEmpty())
33+ {
34+ roles[IndicatorsModelRole::Identifier] = "identifier";
35+ roles[IndicatorsModelRole::Position] = "position";
36+ roles[IndicatorsModelRole::Title] = "title";
37+ roles[IndicatorsModelRole::Description] = "description";
38+ roles[IndicatorsModelRole::WidgetSource] = "widgetSource";
39+ roles[IndicatorsModelRole::PageSource] = "pageSource";
40+ roles[IndicatorsModelRole::IndicatorProperties] = "indicatorProperties";
41+ roles[IndicatorsModelRole::IsVisible] = "isVisible";
42+ }
43+ return roles;
44+}
45+
46+void VisibleIndicatorsModel::setSourceModel(QAbstractItemModel *model)
47+{
48+ if (sourceModel() != model) {
49+ QIdentityProxyModel::setSourceModel(model);
50+ Q_EMIT modelChanged();
51+ }
52+}
53+
54+QVariantMap VisibleIndicatorsModel::visible() const
55+{
56+ return m_visible;
57+}
58+
59+void VisibleIndicatorsModel::onBeginRowInserted(const QModelIndex&, int, int)
60+{
61+ m_inserting = true;
62+}
63+
64+void VisibleIndicatorsModel::onRowInserted(const QModelIndex&, int, int)
65+{
66+ m_inserting = false;
67+}
68+
69+void VisibleIndicatorsModel::setVisible(const QVariantMap& visible)
70+{
71+ if (m_visible != visible) {
72+ m_visible = visible;
73+ Q_EMIT visibleChanged();
74+
75+ // need to tell the view that the visible data has changed.
76+ if (!m_inserting && rowCount() > 0) {
77+ Q_EMIT dataChanged(index(0, 0), index(rowCount() - 1, 0), QVector<int>() << IndicatorsModelRole::IsVisible);
78+ }
79+ }
80+}
81+
82+QVariant VisibleIndicatorsModel::data(const QModelIndex &index, int role) const
83+{
84+ if (role != IndicatorsModelRole::IsVisible) {
85+ return QIdentityProxyModel::data(index, role);
86+ }
87+
88+ if (!index.isValid()) {
89+ return false;
90+ }
91+
92+ QString ident = QIdentityProxyModel::data(index, IndicatorsModelRole::Identifier).toString();
93+ return m_visible.value(ident, false).toBool();
94+}
plugins/Unity/Indicators/visibleindicatorsmodel.hnew file+56
@@ -0,0 +1,56 @@
1+/*
2+ * Copyright (C) 2012, 2013 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+ * Authors:
17+ * Nick Dedekind <nick.dedekind@canonical.com>
18+ */
19+
20+#ifndef VISIBLEINDICATORSMODEL_H
21+#define VISIBLEINDICATORSMODEL_H
22+
23+#include <QIdentityProxyModel>
24+
25+class VisibleIndicatorsModel : public QIdentityProxyModel
26+{
27+ Q_OBJECT
28+
29+ Q_PROPERTY(QAbstractItemModel* model READ sourceModel WRITE setSourceModel NOTIFY modelChanged)
30+ Q_PROPERTY(QVariantMap visible READ visible WRITE setVisible NOTIFY visibleChanged)
31+
32+public:
33+ explicit VisibleIndicatorsModel(QObject *parent = 0);
34+
35+ virtual void setSourceModel(QAbstractItemModel *model);
36+ virtual QHash<int, QByteArray> roleNames() const;
37+ virtual QVariant data(const QModelIndex &index, int role) const;
38+
39+ QVariantMap visible() const;
40+ void setVisible(const QVariantMap& visible);
41+
42+public Q_SLOTS:
43+ void onBeginRowInserted(const QModelIndex&, int start, int end);
44+ void onRowInserted(const QModelIndex&, int start, int end);
45+
46+Q_SIGNALS:
47+ void totalCountChanged();
48+ void modelChanged();
49+ void visibleChanged();
50+
51+private:
52+ QVariantMap m_visible;
53+ bool m_inserting;
54+};
55+
56+#endif // VISIBLEINDICATORSMODEL_H
plugins/Unity/IndicatorsLegacy/qml/IndicatorWidget.qml+2−2
@@ -23,7 +23,7 @@ import Unity.IndicatorsLegacy 0.1 as Indicators
23IndicatorBase {
23IndicatorBase {
24 id: indicatorWidget
24 id: indicatorWidget
25
25
26−// enabled: false // FIXME : should be disabled until bus available when we have desktop indicators
26+ enabled: false
27 property var action: undefined
27 property var action: undefined
28 property bool started: (busType != 0) && (busName != "") && (deviceMenuObjectPath != "")
28 property bool started: (busType != 0) && (busName != "") && (deviceMenuObjectPath != "")
29 property int iconSize: height - units.gu(0.5)
29 property int iconSize: height - units.gu(0.5)
@@ -40,7 +40,6 @@ IndicatorBase {
40 actionGroup.stop();
40 actionGroup.stop();
41 }
41 }
42 }
42 }
43− onActionChanged: actionStateChanged()
44 onActionGroupUpdated: update_state()
43 onActionGroupUpdated: update_state()
45 onModelUpdated: update_state()
44 onModelUpdated: update_state()
46
45
@@ -57,6 +56,7 @@ IndicatorBase {
57
56
58 if (extra.canonical_type === rootMenuType) {
57 if (extra.canonical_type === rootMenuType) {
59 action = actionGroup.action(proxyModel.data(0, Indicators.FlatMenuProxyModelRole.Action));
58 action = actionGroup.action(proxyModel.data(0, Indicators.FlatMenuProxyModelRole.Action));
59+ actionStateChanged();
60 }
60 }
61 }
61 }
62
62
plugins/Unity/Launcher/CMakeLists.txt+4
@@ -1,11 +1,13 @@
1include(FindPkgConfig)
1include(FindPkgConfig)
2pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=2)
2pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=2)
3+pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
3
4
4add_definitions(-DSM_BUSNAME=systemBus)
5add_definitions(-DSM_BUSNAME=systemBus)
5
6
6include_directories(
7include_directories(
7 ${CMAKE_CURRENT_SOURCE_DIR}
8 ${CMAKE_CURRENT_SOURCE_DIR}
8 ${CMAKE_SOURCE_DIR}/plugins/AccountsService
9 ${CMAKE_SOURCE_DIR}/plugins/AccountsService
10+ ${GSETTINGS_QT_INCLUDE_DIRS}
9)
11)
10
12
11set(QMLLAUNCHERPLUGIN_SRC
13set(QMLLAUNCHERPLUGIN_SRC
@@ -25,6 +27,8 @@ add_library(UnityLauncher-qml MODULE
25 ${QMLLAUNCHERPLUGIN_SRC}
27 ${QMLLAUNCHERPLUGIN_SRC}
26 )
28 )
27
29
30+target_link_libraries(UnityLauncher-qml ${GSETTINGS_QT_LDFLAGS})
31+
28qt5_use_modules(UnityLauncher-qml DBus Qml)
32qt5_use_modules(UnityLauncher-qml DBus Qml)
29
33
30# export the qmldir and qmltypes files
34# export the qmldir and qmltypes files
plugins/Unity/Launcher/backend/launcherbackend.cpp+186−98
@@ -24,6 +24,19 @@
24#include <QDir>
24#include <QDir>
25#include <QDBusArgument>
25#include <QDBusArgument>
26#include <QFileInfo>
26#include <QFileInfo>
27+#include <QGSettings>
28+#include <QDebug>
29+
30+class LauncherBackendItem
31+{
32+public:
33+ LauncherBackendItem(): pinned(false) {}
34+
35+ QString desktopFile;
36+ QString displayName;
37+ QString icon;
38+ bool pinned;
39+};
27
40
28LauncherBackend::LauncherBackend(bool useStorage, QObject *parent):
41LauncherBackend::LauncherBackend(bool useStorage, QObject *parent):
29 QObject(parent),
42 QObject(parent),
@@ -32,95 +45,122 @@ LauncherBackend::LauncherBackend(bool useStorage, QObject *parent):
32 if (useStorage) {
45 if (useStorage) {
33 m_accounts = new AccountsService(this);
46 m_accounts = new AccountsService(this);
34 }
47 }
35− setUser(qgetenv("USER"));
48+ m_user = qgetenv("USER");
49+ syncFromAccounts();
36}
50}
37
51
38LauncherBackend::~LauncherBackend()
52LauncherBackend::~LauncherBackend()
39{
53{
40− clearItems();
54+ m_storedApps.clear();
41−}
42
55
43−void LauncherBackend::clearItems()
44−{
45− for (LauncherBackendItem &app: m_storedApps) {
56+ Q_FOREACH(LauncherBackendItem *item, m_itemCache) {
46− delete app.settings;
57+ delete item;
47 }
58 }
48− m_storedApps.clear();
59+ m_itemCache.clear();
49}
60}
50
61
51QStringList LauncherBackend::storedApplications() const
62QStringList LauncherBackend::storedApplications() const
52{
63{
53− auto files = QStringList();
54− for (const LauncherBackendItem &app: m_storedApps) {
55− files << app.settings->fileName();
56− }
57− return files;
64+ return m_storedApps;
58}
65}
59
66
60void LauncherBackend::setStoredApplications(const QStringList &appIds)
67void LauncherBackend::setStoredApplications(const QStringList &appIds)
61{
68{
62− // Record all existing pinned apps, so we can notice them in new list
69+ if (appIds.count() < m_storedApps.count()) {
63− auto pinnedItems = QStringList();
70+ Q_FOREACH(const QString &appId, m_storedApps) {
64− for (LauncherBackendItem &app: m_storedApps) {
71+ if (!appIds.contains(appId)) {
65− if (app.pinned) {
72+ delete m_itemCache.take(appId);
66− pinnedItems.append(app.settings->fileName());
73+ }
67 }
74 }
68 }
75 }
69−
76+ m_storedApps = appIds;
70− clearItems();
71−
72− for (const QString &appId: appIds) {
77+ Q_FOREACH(const QString &appId, appIds) {
73− loadApp(makeAppDetails(appId, pinnedItems.contains(desktopFile(appId))));
78+ if (!m_itemCache.contains(appId)) {
79+ QString df = findDesktopFile(appId);
80+ if (!df.isEmpty()) {
81+ LauncherBackendItem *item = parseDesktopFile(df);
82+ m_itemCache.insert(appId, item);
83+ } else {
84+ // Cannot find any data for that app... ignoring it.
85+ qWarning() << "cannot find desktop file for" << appId << ". discarding app.";
86+ m_storedApps.removeAll(appId);
87+ }
88+ }
74 }
89 }
75−
76 syncToAccounts();
90 syncToAccounts();
77}
91}
78
92
79QString LauncherBackend::desktopFile(const QString &appId) const
93QString LauncherBackend::desktopFile(const QString &appId) const
80{
94{
81− QFileInfo fileInfo(QDir("/usr/share/applications"), appId);
95+ LauncherBackendItem *item = m_itemCache.value(appId);
82− return fileInfo.absoluteFilePath();
96+ if (item) {
97+ return item->desktopFile;
98+ }
99+
100+ return findDesktopFile(appId);
83}
101}
84
102
85QString LauncherBackend::displayName(const QString &appId) const
103QString LauncherBackend::displayName(const QString &appId) const
86{
104{
87− auto desktopFile = parseDesktopFile(appId);
105+ LauncherBackendItem *item = m_itemCache.value(appId);
88− auto displayName = desktopFile->value("Desktop Entry/Name").toString();
106+ if (item) {
89− delete desktopFile;
107+ return item->displayName;
108+ }
109+
110+ QString df = findDesktopFile(appId);
111+ if (!df.isEmpty()) {
112+ LauncherBackendItem *item = parseDesktopFile(df);
113+ m_itemCache.insert(appId, item);
114+ return item->displayName;
115+ }
116+
90− return displayName;
117+ return QString();
91}
118}
92
119
93QString LauncherBackend::icon(const QString &appId) const
120QString LauncherBackend::icon(const QString &appId) const
94{
121{
95− auto desktopFile = parseDesktopFile(appId);
122+ QString iconName;
96− auto iconName = desktopFile->value("Desktop Entry/Icon").toString();
123+ LauncherBackendItem *item = m_itemCache.value(appId);
97− delete desktopFile;
124+ if (item) {
98−
125+ iconName = item->icon;
126+ } else {
127+ QString df = findDesktopFile(appId);
99− if (!iconName.isEmpty()) {
128+ if (!df.isEmpty()) {
100− QFileInfo iconFileInfo(iconName);
129+ LauncherBackendItem *item = parseDesktopFile(df);
101− if (iconFileInfo.isRelative()) {
130+ m_itemCache.insert(appId, item);
102− iconName = "image://gicon/" + iconName;
131+ iconName = item->icon;
103 }
132 }
104 }
133 }
105
134
135+ QFileInfo info(iconName);
136+ if (info.exists()) {
137+ return info.absoluteFilePath();
138+ }
106− return iconName;
139+ return "image://theme/" + iconName;
107}
140}
108
141
109bool LauncherBackend::isPinned(const QString &appId) const
142bool LauncherBackend::isPinned(const QString &appId) const
110{
143{
111− auto index = findItem(appId);
144+ LauncherBackendItem *item = m_itemCache.value(appId);
112− if (index < 0) {
145+ if (item) {
113− return false;
146+ return item->pinned;
114− } else {
115− return m_storedApps[index].pinned;
116 }
147 }
148+ return false;
117}
149}
118
150
119void LauncherBackend::setPinned(const QString &appId, bool pinned)
151void LauncherBackend::setPinned(const QString &appId, bool pinned)
120{
152{
121− auto index = findItem(appId);
153+ if (!m_storedApps.contains(appId)) {
122− if (index >= 0 && !m_storedApps[index].pinned) {
154+ return;
155+ }
156+
157+ LauncherBackendItem *item = m_itemCache.value(appId);
158+ if (!item) {
159+ item = new LauncherBackendItem();
160+ m_itemCache.insert(appId, item);
161+ }
162+ if (item->pinned != pinned) {
123− m_storedApps[index].pinned = pinned;
163+ item->pinned = pinned;
124 syncToAccounts();
164 syncToAccounts();
125 }
165 }
126}
166}
@@ -153,8 +193,10 @@ int LauncherBackend::count(const QString &appId) const
153
193
154void LauncherBackend::setUser(const QString &username)
194void LauncherBackend::setUser(const QString &username)
155{
195{
196+ if (qgetenv("USER") == "lightdm" && m_user != username) {
156− m_user = username;
197+ m_user = username;
157− syncFromAccounts();
198+ syncFromAccounts();
199+ }
158}
200}
159
201
160void LauncherBackend::triggerQuickListAction(const QString &appId, const QString &quickListId)
202void LauncherBackend::triggerQuickListAction(const QString &appId, const QString &quickListId)
@@ -169,75 +211,132 @@ void LauncherBackend::syncFromAccounts()
169 QList<QVariantMap> apps;
211 QList<QVariantMap> apps;
170 bool defaults = true;
212 bool defaults = true;
171
213
172− clearItems();
214+ m_storedApps.clear();
173
215
174− if (m_user != "" && m_accounts != nullptr) {
216+ if (m_accounts && !m_user.isEmpty()) {
175− auto variant = m_accounts->getUserProperty(m_user, "launcher-items");
217+ QVariant variant = m_accounts->getUserProperty(m_user, "launcher-items");
176 apps = qdbus_cast<QList<QVariantMap>>(variant.value<QDBusArgument>());
218 apps = qdbus_cast<QList<QVariantMap>>(variant.value<QDBusArgument>());
177 defaults = isDefaultsItem(apps);
219 defaults = isDefaultsItem(apps);
178 }
220 }
179
221
180− // TODO: load default pinned ones from default config, instead of hardcoding here...
222+ if (m_accounts && defaults) { // Checking accounts as it'll be null when !useStorage
181− if (defaults) {
223+ QGSettings gSettings("com.canonical.Unity.Launcher", "/com/canonical/unity/launcher/");
182− apps <<
224+ Q_FOREACH(const QString &entry, gSettings.get("favorites").toStringList()) {
183− makeAppDetails("dialer-app.desktop", true) <<
225+ if (entry.startsWith("application://")) {
184− makeAppDetails("messaging-app.desktop", true) <<
226+ QString appId = entry;
185− makeAppDetails("address-book-app.desktop", true) <<
227+ // Transform "application://foobar.desktop" to "foobar"
186− makeAppDetails("camera-app.desktop", true) <<
228+ appId.remove("application://");
187− makeAppDetails("gallery-app.desktop", true) <<
229+ if (appId.endsWith(".desktop")) {
188− makeAppDetails("facebook-webapp.desktop", true) <<
230+ appId.chop(8);
189− makeAppDetails("webbrowser-app.desktop", true) <<
231+ }
190− makeAppDetails("twitter-webapp.desktop", true) <<
232+ QString df = findDesktopFile(appId);
191− makeAppDetails("gmail-webapp.desktop", true) <<
233+
192− makeAppDetails("ubuntu-weather-app.desktop", true) <<
234+ if (!df.isEmpty()) {
193− makeAppDetails("notes-app.desktop", true) <<
235+ m_storedApps << appId;
194− makeAppDetails("calendar-app.desktop", true);
236+
195− }
237+ if (!m_itemCache.contains(appId)) {
196−
238+ m_itemCache.insert(appId, parseDesktopFile(df));
239+ }
240+ m_itemCache.value(appId)->pinned = true;
241+ }
242+ }
243+ }
244+ } else {
197− for (const QVariant &app: apps) {
245+ for (const QVariant &app: apps) {
198− loadApp(app.toMap());
246+ loadFromVariant(app.toMap());
247+ }
199 }
248 }
200}
249}
201
250
202void LauncherBackend::syncToAccounts()
251void LauncherBackend::syncToAccounts()
203{
252{
204− if (m_user != "" && m_accounts != nullptr) {
253+ if (m_accounts && !m_user.isEmpty()) {
205 QList<QVariantMap> items;
254 QList<QVariantMap> items;
206
255
207− for (LauncherBackendItem &app: m_storedApps) {
256+ Q_FOREACH(const QString &appId, m_storedApps) {
208− items << makeAppDetails(app.settings->fileName(), app.pinned);
257+ items << itemToVariant(appId);
209 }
258 }
210
259
211 m_accounts->setUserProperty(m_user, "launcher-items", QVariant::fromValue(items));
260 m_accounts->setUserProperty(m_user, "launcher-items", QVariant::fromValue(items));
212 }
261 }
213}
262}
214
263
215−QSettings *LauncherBackend::parseDesktopFile(const QString &appId) const
264+QString LauncherBackend::findDesktopFile(const QString &appId) const
216{
265{
217− auto fullAppId = desktopFile(appId);
266+ int dashPos = -1;
218− return new QSettings(fullAppId, QSettings::IniFormat);
267+ QString helper = appId;
268+
269+ QStringList searchDirs;
270+ searchDirs << QDir::homePath() + "/.local/share/applications";
271+ searchDirs << "/usr/share/applications";
272+
273+#ifdef LAUNCHER_TESTING
274+ searchDirs << "";
275+#endif
276+
277+ do {
278+ if (dashPos != -1) {
279+ helper = helper.replace(dashPos, 1, '/');
280+ }
281+
282+ Q_FOREACH(const QString &searchDir, searchDirs) {
283+ QFileInfo fileInfo(QDir(searchDir), helper + ".desktop");
284+ if (fileInfo.exists()) {
285+ return fileInfo.absoluteFilePath();
286+ }
287+ }
288+
289+ dashPos = helper.indexOf("-");
290+ } while (dashPos != -1);
291+
292+ return QString();
219}
293}
220
294
221−void LauncherBackend::loadApp(const QVariantMap &details)
295+LauncherBackendItem* LauncherBackend::parseDesktopFile(const QString &desktopFile) const
222{
296{
223− auto appId = details.value("id").toString();
297+ QSettings settings(desktopFile, QSettings::IniFormat);
224− auto isPinned = details.value("is-pinned").toBool();
225
298
226− if (appId.isEmpty()) {
299+
300+ LauncherBackendItem* item = new LauncherBackendItem();
301+ item->desktopFile = desktopFile;
302+ item->displayName = settings.value("Desktop Entry/Name").toString();
303+ item->icon = settings.value("Desktop Entry/Icon").toString();
304+ item->pinned = false;
305+ return item;
306+}
307+
308+void LauncherBackend::loadFromVariant(const QVariantMap &details)
309+{
310+ if (!details.contains("id")) {
227 return;
311 return;
228 }
312 }
313+ QString appId = details.value("id").toString();
314+
315+ LauncherBackendItem *item = m_itemCache.value(appId);
316+ if (item) {
317+ delete item;
318+ }
319+
320+ item = new LauncherBackendItem();
229
321
230− LauncherBackendItem item;
322+ item->desktopFile = details.value("desktopFile").toString();
231− item.settings = parseDesktopFile(appId);
323+ item->displayName = details.value("name").toString();
232− item.pinned = isPinned;
324+ item->icon = details.value("icon").toString();
325+ item->pinned = details.value("is-pinned").toBool();
326+
327+ m_itemCache.insert(appId, item);
233− m_storedApps.append(item);
328+ m_storedApps.append(appId);
234}
329}
235
330
236−QVariantMap LauncherBackend::makeAppDetails(const QString &appId, bool pinned) const
331+QVariantMap LauncherBackend::itemToVariant(const QString &appId) const
237{
332{
333+ LauncherBackendItem *item = m_itemCache.value(appId);
238 QVariantMap details;
334 QVariantMap details;
239 details.insert("id", appId);
335 details.insert("id", appId);
336+ details.insert("name", item->displayName);
337+ details.insert("icon", item->icon);
338+ details.insert("desktopFile", item->desktopFile);
240− details.insert("is-pinned", pinned);
339+ details.insert("is-pinned", item->pinned);
241 return details;
340 return details;
242}
341}
243
342
@@ -248,14 +347,3 @@ bool LauncherBackend::isDefaultsItem(const QList<QVariantMap> &apps) const
248 // list of one item with the 'defaults' field set to true.
347 // list of one item with the 'defaults' field set to true.
249 return (apps.size() == 1 && apps[0].value("defaults").toBool());
348 return (apps.size() == 1 && apps[0].value("defaults").toBool());
250}
349}
251−
252−int LauncherBackend::findItem(const QString &appId) const
253−{
254− auto fullAppId = desktopFile(appId);
255− for (int i = 0; i < m_storedApps.size(); ++i) {
256− if (m_storedApps[i].settings->fileName() == fullAppId) {
257− return i;
258− }
259− }
260− return -1;
261−}
plugins/Unity/Launcher/backend/launcherbackend.h+10−12
@@ -31,6 +31,8 @@ class AccountsService;
31 * @brief An interface that provides all the data needed by the launcher.
31 * @brief An interface that provides all the data needed by the launcher.
32 */
32 */
33
33
34+class LauncherBackendItem;
35+
34class LauncherBackend : public QObject
36class LauncherBackend : public QObject
35{
37{
36 Q_OBJECT
38 Q_OBJECT
@@ -141,23 +143,19 @@ Q_SIGNALS:
141 void countChanged(const QString &appId, int count);
143 void countChanged(const QString &appId, int count);
142
144
143private:
145private:
146+ QString findDesktopFile(const QString &appId) const;
144− QSettings *parseDesktopFile(const QString &appId) const;
147+ LauncherBackendItem* parseDesktopFile(const QString &desktopFile) const;
148+
145− QVariantMap makeAppDetails(const QString &appId, bool pinned) const;
149+ QVariantMap itemToVariant(const QString &appId) const;
146− void loadApp(const QVariantMap &details);
150+ void loadFromVariant(const QVariantMap &details);
147− int findItem(const QString &appId) const;
151+
148 bool isDefaultsItem(const QList<QVariantMap> &apps) const;
152 bool isDefaultsItem(const QList<QVariantMap> &apps) const;
149 void syncFromAccounts();
153 void syncFromAccounts();
150 void syncToAccounts();
154 void syncToAccounts();
151− void clearItems();
152
155
156+ QList<QString> m_storedApps;
153− class LauncherBackendItem
157+ mutable QHash<QString, LauncherBackendItem*> m_itemCache;
154− {
155− public:
156− QSettings *settings;
157− bool pinned;
158− };
159
158
160− QList<LauncherBackendItem> m_storedApps;
161 AccountsService *m_accounts;
159 AccountsService *m_accounts;
162 QString m_user;
160 QString m_user;
163};
161};
plugins/Unity/Launcher/launcheritem.cpp+14
@@ -31,6 +31,7 @@ LauncherItem::LauncherItem(const QString &appId, const QString &desktopFile, con
31 m_recent(false),
31 m_recent(false),
32 m_progress(-1),
32 m_progress(-1),
33 m_count(0),
33 m_count(0),
34+ m_focused(false),
34 m_quickList(new QuickListModel(this))
35 m_quickList(new QuickListModel(this))
35{
36{
36 QuickListEntry nameAction;
37 QuickListEntry nameAction;
@@ -131,6 +132,19 @@ void LauncherItem::setCount(int count)
131 }
132 }
132}
133}
133
134
135+bool LauncherItem::focused() const
136+{
137+ return m_focused;
138+}
139+
140+void LauncherItem::setFocused(bool focused)
141+{
142+ if (m_focused != focused) {
143+ m_focused = focused;
144+ Q_EMIT focusedChanged(focused);
145+ }
146+}
147+
134unity::shell::launcher::QuickListModelInterface *LauncherItem::quickList() const
148unity::shell::launcher::QuickListModelInterface *LauncherItem::quickList() const
135{
149{
136 return m_quickList;
150 return m_quickList;
plugins/Unity/Launcher/launcheritem.h+4
@@ -54,6 +54,9 @@ public:
54 int count() const;
54 int count() const;
55 void setCount(int count);
55 void setCount(int count);
56
56
57+ bool focused() const;
58+ void setFocused(bool focused);
59+
57 unity::shell::launcher::QuickListModelInterface *quickList() const;
60 unity::shell::launcher::QuickListModelInterface *quickList() const;
58
61
59Q_SIGNALS:
62Q_SIGNALS:
@@ -70,6 +73,7 @@ private:
70 bool m_recent;
73 bool m_recent;
71 int m_progress;
74 int m_progress;
72 int m_count;
75 int m_count;
76+ bool m_focused;
73 QuickListModel *m_quickList;
77 QuickListModel *m_quickList;
74};
78};
75
79
plugins/Unity/Launcher/launchermodel.cpp+59
@@ -74,6 +74,8 @@ QVariant LauncherModel::data(const QModelIndex &index, int role) const
74 return item->count();
74 return item->count();
75 case RoleProgress:
75 case RoleProgress:
76 return item->progress();
76 return item->progress();
77+ case RoleFocused:
78+ return item->focused();
77 }
79 }
78
80
79 return QVariant();
81 return QVariant();
@@ -191,6 +193,63 @@ void LauncherModel::setUser(const QString &username)
191 m_backend->setUser(username);
193 m_backend->setUser(username);
192}
194}
193
195
196+void LauncherModel::applicationFocused(const QString &appId)
197+{
198+ // Unfocus all apps
199+ for(int i = 0; i < m_list.count(); ++i) {
200+ LauncherItem *item = m_list.at(i);
201+ if (item->focused()) {
202+ item->setFocused(false);
203+ Q_EMIT dataChanged(this->index(i), this->index(i), QVector<int>() << RoleFocused);
204+ }
205+ }
206+
207+ if (appId.isEmpty()) {
208+ return;
209+ }
210+
211+ // FIXME: drop this once we get real appIds from the AppManager
212+ QString helper = appId.split('/').last();
213+ if (helper.endsWith(".desktop")) {
214+ helper.chop(8);
215+ }
216+
217+ int index = findApplication(helper);
218+ if (index >= 0) {
219+ m_list.at(index)->setFocused(true);
220+ Q_EMIT dataChanged(this->index(index), this->index(index), QVector<int>() << RoleFocused);
221+ } else {
222+ // Add app to recent apps
223+ QString desktopFile = m_backend->desktopFile(helper);
224+ QString appName = m_backend->displayName(helper);
225+ QString icon = m_backend->icon(helper);
226+
227+ LauncherItem *item = new LauncherItem(helper, desktopFile, appName, icon);
228+ item->setRecent(true);
229+ item->setFocused(true);
230+ beginInsertRows(QModelIndex(), m_list.count(), m_list.count());
231+ m_list.append(item);
232+ endInsertRows();
233+
234+ // Clean up old recent apps
235+ QList<int> recentAppIndices;
236+ for (int i = 0; i < m_list.count(); ++i) {
237+ if (m_list.at(i)->recent()) {
238+ recentAppIndices << i;
239+ }
240+ }
241+ int run = 0;
242+ while (recentAppIndices.count() > 5) {
243+ beginRemoveRows(QModelIndex(), recentAppIndices.first() - run, recentAppIndices.first() - run);
244+ m_list.takeAt(recentAppIndices.first() - run)->deleteLater();
245+ endRemoveRows();
246+ recentAppIndices.takeFirst();
247+ ++run;
248+ }
249+ storeAppList();
250+ }
251+}
252+
194void LauncherModel::storeAppList()
253void LauncherModel::storeAppList()
195{
254{
196 QStringList appIds;
255 QStringList appIds;
plugins/Unity/Launcher/launchermodel.h+1
@@ -49,6 +49,7 @@ public:
49 Q_INVOKABLE void requestRemove(const QString &appId);
49 Q_INVOKABLE void requestRemove(const QString &appId);
50 Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex);
50 Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex);
51 Q_INVOKABLE void setUser(const QString &username);
51 Q_INVOKABLE void setUser(const QString &username);
52+ Q_INVOKABLE void applicationFocused(const QString &appId);
52
53
53private:
54private:
54 void storeAppList();
55 void storeAppList();
plugins/Unity/applicationpreview.cpp+5−1
@@ -19,6 +19,7 @@
19
19
20// local
20// local
21#include "applicationpreview.h"
21#include "applicationpreview.h"
22+#include "iconutils.h"
22
23
23#include <QDebug>
24#include <QDebug>
24
25
@@ -61,7 +62,10 @@ QString ApplicationPreview::license() const
61QString ApplicationPreview::appIcon() const
62QString ApplicationPreview::appIcon() const
62{
63{
63 if (m_unityAppPreview) {
64 if (m_unityAppPreview) {
64− return QString::fromUtf8(g_icon_to_string(m_unityAppPreview->app_icon()));
65+ auto giconString = g_icon_to_string(m_unityAppPreview->app_icon());
66+ QString result(gIconToDeclarativeImageProviderString(QString::fromUtf8(giconString)));
67+ g_free(giconString);
68+ return result;
65 } else {
69 } else {
66 qWarning() << "Preview not set";
70 qWarning() << "Preview not set";
67 }
71 }
plugins/Unity/categories.cpp−3
@@ -43,7 +43,6 @@ Categories::Categories(QObject* parent)
43 m_roles[Categories::RoleHints] = "hints";
43 m_roles[Categories::RoleHints] = "hints";
44 m_roles[Categories::RoleResults] = "results";
44 m_roles[Categories::RoleResults] = "results";
45 m_roles[Categories::RoleCount] = "count";
45 m_roles[Categories::RoleCount] = "count";
46− m_roles[Categories::RoleCategoryIndex] = "categoryIndex";
47
46
48 // TODO This should not be needed but accumulatting the count changes
47 // TODO This should not be needed but accumulatting the count changes
49 // makes the visualization more stable and also makes crashes on fast
48 // makes the visualization more stable and also makes crashes on fast
@@ -222,8 +221,6 @@ Categories::data(const QModelIndex& index, int role) const
222 return QVariant::fromValue(m_overriddenCategories[id]->rowCount());
221 return QVariant::fromValue(m_overriddenCategories[id]->rowCount());
223 }
222 }
224 return QVariant::fromValue(getResults(index.row())->rowCount());
223 return QVariant::fromValue(getResults(index.row())->rowCount());
225− case RoleCategoryIndex:
226− return QVariant::fromValue(index.row());
227 default:
224 default:
228 return QVariant();
225 return QVariant();
229 }
226 }
plugins/Unity/categories.h+1−2
@@ -50,8 +50,7 @@ public:
50 RoleProgressSource,
50 RoleProgressSource,
51 RoleHints,
51 RoleHints,
52 RoleResults,
52 RoleResults,
53− RoleCount,
53+ RoleCount
54− RoleCategoryIndex
55 };
54 };
56
55
57 Q_INVOKABLE void overrideResults(const QString& categoryId, QAbstractItemModel* model);
56 Q_INVOKABLE void overrideResults(const QString& categoryId, QAbstractItemModel* model);
plugins/Unity/categoryresults.cpp+5−2
@@ -19,6 +19,7 @@
19
19
20// self
20// self
21#include "categoryresults.h"
21#include "categoryresults.h"
22+#include "iconutils.h"
22
23
23// TODO: use something from libunity once it's public
24// TODO: use something from libunity once it's public
24enum ResultsColumn {
25enum ResultsColumn {
@@ -76,8 +77,10 @@ CategoryResults::data(const QModelIndex& index, int role) const
76 switch (role) {
77 switch (role) {
77 case RoleUri:
78 case RoleUri:
78 return DeeListModel::data(index, ResultsColumn::URI);
79 return DeeListModel::data(index, ResultsColumn::URI);
79− case RoleIconHint:
80+ case RoleIconHint: {
80− return DeeListModel::data(index, ResultsColumn::ICON_HINT);
81+ QString giconString(DeeListModel::data(index, ResultsColumn::ICON_HINT).toString());
82+ return QVariant::fromValue(gIconToDeclarativeImageProviderString(giconString));
83+ }
81 case RoleCategory:
84 case RoleCategory:
82 return DeeListModel::data(index, ResultsColumn::CATEGORY);
85 return DeeListModel::data(index, ResultsColumn::CATEGORY);
83 case RoleMimetype:
86 case RoleMimetype:
plugins/Unity/categoryresults.h+1−1
@@ -24,7 +24,7 @@
24// dee-qt
24// dee-qt
25#include "deelistmodel.h"
25#include "deelistmodel.h"
26
26
27−class CategoryResults : public DeeListModel
27+class Q_DECL_EXPORT CategoryResults : public DeeListModel
28{
28{
29 Q_OBJECT
29 Q_OBJECT
30
30
plugins/Unity/iconutils.cppnew file+91
@@ -0,0 +1,91 @@
1+/*
2+ * Copyright (C) 2013 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+ * Authors:
17+ * Michal Hruby <michal.hruby@canonical.com>
18+ */
19+
20+#include <QStringList>
21+#include <QUrl>
22+
23+#include <gio/gio.h>
24+#include "iconutils.h"
25+
26+#define BASE_THEME_ICON_URI "image://theme/"
27+
28+QString gIconToDeclarativeImageProviderString(QString const &giconString)
29+{
30+ if (giconString.isEmpty()) return giconString;
31+
32+ if (giconString.startsWith('/') || giconString.startsWith(QLatin1String("http")) ||
33+ giconString.startsWith(QLatin1String("file:"))) {
34+ return giconString;
35+ }
36+
37+ if (!giconString.startsWith(QLatin1String(". "))) {
38+ // must be a themed icon
39+ QString themedIcon(BASE_THEME_ICON_URI);
40+ themedIcon.append(giconString);
41+ return themedIcon;
42+ }
43+
44+ // special case annotated icon
45+ if (giconString.startsWith(QLatin1String(". UnityProtocolAnnotatedIcon "))) {
46+ QString annotatedIcon;
47+ QString serializedData(QUrl::fromPercentEncoding(giconString.mid(29).toUtf8()));
48+ GVariant *variant = g_variant_parse(G_VARIANT_TYPE_VARDICT, serializedData.toUtf8().constData(), NULL, NULL, NULL);
49+ gchar *baseUri;
50+ if (variant != NULL && g_variant_lookup(variant, "base-icon", "&s", &baseUri)) {
51+ annotatedIcon = gIconToDeclarativeImageProviderString(QString(baseUri));
52+ // FIXME: enclose in image://annotated/... once unity supports that
53+ }
54+ if (variant != NULL) g_variant_unref(variant);
55+
56+ return annotatedIcon;
57+ }
58+
59+ // handle real gicon strings
60+ QString result;
61+ GError *error = NULL;
62+ GIcon *icon = g_icon_new_for_string(giconString.toLocal8Bit().constData(), &error);
63+ if (error != NULL || icon == NULL) {
64+ qWarning("Unable to deserialize icon: %s", giconString.toLocal8Bit().constData());
65+ g_clear_error(&error);
66+ return result;
67+ }
68+
69+ if (G_IS_THEMED_ICON(icon)) {
70+ QString themedIcon(BASE_THEME_ICON_URI);
71+ QStringList list;
72+ const char* const *iconNames = g_themed_icon_get_names(G_THEMED_ICON(icon));
73+ if (iconNames != NULL) {
74+ for (const char * const *iter = iconNames; *iter != NULL; ++iter) {
75+ list << QLatin1String(*iter);
76+ }
77+ }
78+ themedIcon.append(list.join(QString(",")));
79+ result = themedIcon;
80+ } else if (G_IS_FILE_ICON(icon)) {
81+ GFile *file = g_file_icon_get_file(G_FILE_ICON(icon));
82+ gchar *uri = g_file_get_uri(file);
83+ QString iconUri(uri);
84+ g_free(uri);
85+ result = iconUri;
86+ }
87+
88+ g_object_unref(icon);
89+
90+ return result;
91+}
plugins/Unity/iconutils.hnew file+27
@@ -0,0 +1,27 @@
1+/*
2+ * Copyright (C) 2013 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+ * Authors:
17+ * Michal Hruby <michal.hruby@canonical.com>
18+ */
19+
20+#ifndef ICONUTILS_H
21+#define ICONUTILS_H
22+
23+#include <QString>
24+
25+QString gIconToDeclarativeImageProviderString(QString const &giconString);
26+
27+#endif
plugins/Unity/preview.cpp+5−1
@@ -26,6 +26,7 @@
26#include "moviepreview.h"
26#include "moviepreview.h"
27#include "musicpreview.h"
27#include "musicpreview.h"
28#include "socialpreview.h"
28#include "socialpreview.h"
29+#include "iconutils.h"
29#include "variantutils.h"
30#include "variantutils.h"
30
31
31// Qt
32// Qt
@@ -102,7 +103,10 @@ QVariantMap Preview::infoHintsHash() const
102QString Preview::image() const
103QString Preview::image() const
103{
104{
104 if (m_unityPreview) {
105 if (m_unityPreview) {
105− return QString::fromUtf8(g_icon_to_string(m_unityPreview->image()));
106+ auto giconString = g_icon_to_string(m_unityPreview->image());
107+ QString result(gIconToDeclarativeImageProviderString(QString::fromUtf8(giconString)));
108+ g_free(giconString);
109+ return result;
106 } else {
110 } else {
107 qWarning() << "Preview not set";
111 qWarning() << "Preview not set";
108 }
112 }
plugins/Unity/socialpreview.cpp+5−1
@@ -19,6 +19,7 @@
19
19
20#include "socialpreview.h"
20#include "socialpreview.h"
21#include "socialpreviewcomment.h"
21#include "socialpreviewcomment.h"
22+#include "iconutils.h"
22
23
23#include <QDebug>
24#include <QDebug>
24
25
@@ -51,7 +52,10 @@ QString SocialPreview::content() const
51QString SocialPreview::avatar() const
52QString SocialPreview::avatar() const
52{
53{
53 if (m_unitySocialPreview) {
54 if (m_unitySocialPreview) {
54− return QString::fromUtf8(g_icon_to_string(m_unitySocialPreview->avatar()));
55+ auto giconString = g_icon_to_string(m_unitySocialPreview->avatar());
56+ QString result(gIconToDeclarativeImageProviderString(QString::fromUtf8(giconString)));
57+ g_free(giconString);
58+ return result;
55 } else {
59 } else {
56 qWarning() << "Preview not set";
60 qWarning() << "Preview not set";
57 }
61 }
plugins/Utils/qsortfilterproxymodelqml.cpp+20
@@ -121,3 +121,23 @@ QSortFilterProxyModelQML::findFirst(int role, const QVariant& value) const
121 return -1;
121 return -1;
122 }
122 }
123}
123}
124+
125+int
126+QSortFilterProxyModelQML::mapFromSource(int row)
127+{
128+ if (sourceModel() != NULL) {
129+ return QSortFilterProxyModel::mapFromSource(sourceModel()->index(row, 0)).row();
130+ } else {
131+ return -1;
132+ }
133+}
134+
135+int
136+QSortFilterProxyModelQML::mapToSource(int row)
137+{
138+ if (sourceModel() != NULL) {
139+ return QSortFilterProxyModel::mapToSource(index(row, 0)).row();
140+ } else {
141+ return -1;
142+ }
143+}
plugins/Utils/qsortfilterproxymodelqml.h+3
@@ -45,6 +45,9 @@ public:
45 void setModel(QAbstractItemModel *model);
45 void setModel(QAbstractItemModel *model);
46 void setInvertMatch(bool invertMatch);
46 void setInvertMatch(bool invertMatch);
47
47
48+ Q_INVOKABLE int mapFromSource(int row);
49+ Q_INVOKABLE int mapToSource(int row);
50+
48Q_SIGNALS:
51Q_SIGNALS:
49 void totalCountChanged();
52 void totalCountChanged();
50 void countChanged();
53 void countChanged();
po/es.po+24−22
@@ -8,15 +8,15 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2013-08-27 13:36-0400\n"
10"POT-Creation-Date: 2013-08-27 13:36-0400\n"
11−"PO-Revision-Date: 2013-07-08 07:25+0000\n"
11+"PO-Revision-Date: 2013-09-04 22:12+0000\n"
12"Last-Translator: Adolfo Jayme Barrientos <fitoschido@gmail.com>\n"
12"Last-Translator: Adolfo Jayme Barrientos <fitoschido@gmail.com>\n"
13"Language-Team: Spanish <es@li.org>\n"
13"Language-Team: Spanish <es@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-08-30 05:37+0000\n"
18+"X-Launchpad-Export-Date: 2013-09-05 05:35+0000\n"
19−"X-Generator: Launchpad (build 16750)\n"
19+"X-Generator: Launchpad (build 16758)\n"
20
20
21#: Applications/applications.js:23
21#: Applications/applications.js:23
22msgid "Phone"
22msgid "Phone"
@@ -88,47 +88,49 @@ msgstr "YouTube"
88
88
89#: Components/EdgeDemoOverlay.qml:145
89#: Components/EdgeDemoOverlay.qml:145
90msgid "Skip intro"
90msgid "Skip intro"
91−msgstr ""
91+msgstr "Omitir la introducción"
92
92
93#: Components/EdgeDemo.qml:98
93#: Components/EdgeDemo.qml:98
94msgid "Right edge"
94msgid "Right edge"
95−msgstr ""
95+msgstr "Borde derecho"
96
96
97#: Components/EdgeDemo.qml:99
97#: Components/EdgeDemo.qml:99
98msgid "Try swiping from the right edge to unlock the phone"
98msgid "Try swiping from the right edge to unlock the phone"
99−msgstr ""
99+msgstr "Deslice el dedo desde el borde derecho para desbloquear el teléfono"
100
100
101#: Components/EdgeDemo.qml:135
101#: Components/EdgeDemo.qml:135
102msgid "Top edge"
102msgid "Top edge"
103−msgstr ""
103+msgstr "Borde superior"
104
104
105#: Components/EdgeDemo.qml:136
105#: Components/EdgeDemo.qml:136
106msgid "Try swiping from the top edge to access the indicators"
106msgid "Try swiping from the top edge to access the indicators"
107−msgstr ""
107+msgstr "Deslice el dedo desde el borde superior para acceder a los menús"
108
108
109#: Components/EdgeDemo.qml:161
109#: Components/EdgeDemo.qml:161
110msgid "Close"
110msgid "Close"
111−msgstr ""
111+msgstr "Cerrar"
112
112
113#: Components/EdgeDemo.qml:162
113#: Components/EdgeDemo.qml:162
114msgid "Swipe up again to close the settings screen"
114msgid "Swipe up again to close the settings screen"
115−msgstr ""
115+msgstr "Deslice hacia arriba para cerrar la pantalla de configuración"
116
116
117#: Components/EdgeDemo.qml:189
117#: Components/EdgeDemo.qml:189
118msgid "Left edge"
118msgid "Left edge"
119−msgstr ""
119+msgstr "Borde izquierdo"
120
120
121#: Components/EdgeDemo.qml:190
121#: Components/EdgeDemo.qml:190
122msgid "Swipe from the left to reveal the launcher for quick access to apps"
122msgid "Swipe from the left to reveal the launcher for quick access to apps"
123msgstr ""
123msgstr ""
124+"Deslice el dedo desde la izquierda para mostrar el lanzador y acceder "
125+"rápidamente a las aplicaciones"
124
126
125#: Components/EdgeDemo.qml:217
127#: Components/EdgeDemo.qml:217
126msgid "Well done"
128msgid "Well done"
127−msgstr ""
129+msgstr "Bien hecho"
128
130
129#: Components/EdgeDemo.qml:218
131#: Components/EdgeDemo.qml:218
130msgid "You have now mastered the edge gestures and can start using the phone"
132msgid "You have now mastered the edge gestures and can start using the phone"
131−msgstr ""
133+msgstr "Ahora domina el uso de gestos de los bordes. Puede usar el teléfono"
132
134
133#: Components/PageHeader.qml:298
135#: Components/PageHeader.qml:298
134msgid "Recent searches"
136msgid "Recent searches"
@@ -138,35 +140,35 @@ msgstr "Búsquedas recientes"
138#: Dash/Apps/AppInfo.qml:73
140#: Dash/Apps/AppInfo.qml:73
139#, qt-format
141#, qt-format
140msgid "(%1)"
142msgid "(%1)"
141−msgstr ""
143+msgstr "(%1)"
142
144
143#. TRANSLATORS: Number of persons who wrote reviews for this app
145#. TRANSLATORS: Number of persons who wrote reviews for this app
144#: Dash/Apps/AppInfo.qml:85
146#: Dash/Apps/AppInfo.qml:85
145#, qt-format
147#, qt-format
146msgid "%1 review"
148msgid "%1 review"
147msgid_plural "%1 reviews"
149msgid_plural "%1 reviews"
148−msgstr[0] ""
150+msgstr[0] "%1 reseña"
149−msgstr[1] ""
151+msgstr[1] "%1 reseñas"
150
152
151#: Dash/Apps/AppPreview.qml:175
153#: Dash/Apps/AppPreview.qml:175
152msgid "Rate this"
154msgid "Rate this"
153−msgstr ""
155+msgstr "Valorar esto"
154
156
155#: Dash/Apps/AppReviews.qml:72
157#: Dash/Apps/AppReviews.qml:72
156msgid "Add a review"
158msgid "Add a review"
157−msgstr ""
159+msgstr "Añadir una reseña"
158
160
159#: Dash/Apps/AppReviews.qml:84
161#: Dash/Apps/AppReviews.qml:84
160msgid "Review"
162msgid "Review"
161−msgstr ""
163+msgstr "Reseñar"
162
164
163#: Dash/Apps/AppReviews.qml:118
165#: Dash/Apps/AppReviews.qml:118
164msgid "Send"
166msgid "Send"
165−msgstr ""
167+msgstr "Enviar"
166
168
167#: Dash/Apps/AppReviews.qml:136
169#: Dash/Apps/AppReviews.qml:136
168msgid "Comments:"
170msgid "Comments:"
169−msgstr ""
171+msgstr "Comentarios:"
170
172
171#: Dash/DashMusic.qml:27 Dash/DashVideos.qml:28
173#: Dash/DashMusic.qml:27 Dash/DashVideos.qml:28
172msgid "Featured"
174msgid "Featured"
@@ -210,7 +212,7 @@ msgstr "Autor:"
210
212
211#: Greeter/Lockscreen.qml:134
213#: Greeter/Lockscreen.qml:134
212msgid "Emergency Call"
214msgid "Emergency Call"
213−msgstr ""
215+msgstr "Llamada de emergencia"
214
216
215#: Greeter/PassphraseLockscreen.qml:59
217#: Greeter/PassphraseLockscreen.qml:59
216#, qt-format
218#, qt-format
tests/autopilot/unity8/shell/tests/test_hud.py+7−2
@@ -34,6 +34,8 @@ class TestHud(UnityTestCase):
34
34
35 def test_show_hud_button_appears(self):
35 def test_show_hud_button_appears(self):
36 """Swiping up while an app is active must show the 'show hud' button.
36 """Swiping up while an app is active must show the 'show hud' button.
37+ The button must disappear not opening the HUD when releasing the
38+ mouse again somewhere on the screen except on the button itself.
37
39
38 """
40 """
39 self.launch_unity()
41 self.launch_unity()
@@ -49,9 +51,12 @@ class TestHud(UnityTestCase):
49 hud_show_button
51 hud_show_button
50 )
52 )
51 self.touch.press(swipe_coords.start_x, swipe_coords.start_y)
53 self.touch.press(swipe_coords.start_x, swipe_coords.start_y)
52− self.addCleanup(self.touch.release)
54+ self.addCleanup(self._maybe_release_finger)
53− self.touch._finger_move(swipe_coords.end_x, swipe_coords.end_y)
55+ self.touch._finger_move(swipe_coords.end_x, swipe_coords.end_y + hud_show_button.height);
54 self.assertThat(hud_show_button.opacity, Eventually(Equals(1.0)))
56 self.assertThat(hud_show_button.opacity, Eventually(Equals(1.0)))
57+ self.touch.release();
58+ self.assertThat(hud_show_button.opacity, Eventually(Equals(0.0)))
59+ self.assertThat(hud.shown, Equals(False))
55
60
56 def test_show_hud_appears(self):
61 def test_show_hud_appears(self):
57 """Releasing the touch on the 'show hud' button must display the hud.
62 """Releasing the touch on the 'show hud' button must display the hud.
tests/mocks/LightDM/demo/GreeterPrivate.cpp+4−2
@@ -55,11 +55,13 @@ void GreeterPrivate::handleRespond(const QString &response)
55 QSettings settings(QDir::homePath() + "/.unity8-greeter-demo", QSettings::NativeFormat);
55 QSettings settings(QDir::homePath() + "/.unity8-greeter-demo", QSettings::NativeFormat);
56 QVariant password = settings.value("password", "none");
56 QVariant password = settings.value("password", "none");
57
57
58+ QString passwordValue;
58 if (password == "pin") {
59 if (password == "pin") {
59− authenticated = (response == "1234");
60+ passwordValue = settings.value("passwordValue", "1234").toString();
60 } else {
61 } else {
61− authenticated = (response == "password");
62+ passwordValue = settings.value("passwordValue", "password").toString();
62 }
63 }
64+ authenticated = (response == passwordValue);
63 Q_EMIT q->authenticationComplete();
65 Q_EMIT q->authenticationComplete();
64}
66}
65
67
tests/mocks/Unity/CMakeLists.txt+3
@@ -17,6 +17,9 @@ add_definitions(-DQT_NO_KEYWORDS)
17
17
18set(UnityQML_SOURCES
18set(UnityQML_SOURCES
19 ${CMAKE_SOURCE_DIR}/plugins/Unity/categoryresults.cpp
19 ${CMAKE_SOURCE_DIR}/plugins/Unity/categoryresults.cpp
20+ ${CMAKE_SOURCE_DIR}/plugins/Unity/iconutils.cpp
21+ fake_applications_scope.cpp
22+ fake_preview.cpp
20 fake_scope.cpp
23 fake_scope.cpp
21 fake_scopes.cpp
24 fake_scopes.cpp
22 fake_categories.cpp
25 fake_categories.cpp
tests/mocks/Unity/Indicators/CMakeLists.txt+1
@@ -13,6 +13,7 @@ set(IndicatorsFakeQml_SOURCES
13 fakeplugin.cpp
13 fakeplugin.cpp
14 ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/indicators.h
14 ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/indicators.h
15 ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/menucontentactivator.cpp
15 ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/menucontentactivator.cpp
16+ ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/visibleindicatorsmodel.cpp
16)
17)
17
18
18set(IndicatorsFakeQml_RESOURCES
19set(IndicatorsFakeQml_RESOURCES
tests/mocks/Unity/Indicators/IndicatorsModel.qml+47−76
@@ -23,82 +23,53 @@ ListModel {
23
23
24 function load() {}
24 function load() {}
25
25
26− ListElement {
26+ // Need to do this dynamically.
27+ // Apparently ListModels dont order roles visually when using ListElement
28+ // which throws off the filter for non-visible indicators.
29+ Component.onCompleted: {
27− identifier: "indicator-fake1";
30+ append({ "identifier": "indicator-fake1",
28− priority: 0
31+ "priority": 0,
29− title: "Menu1";
32+ "title": "Menu1",
30− description: "";
33+ "description": "",
31− widgetSource: "qrc:/tests/indciators/qml/fake_menu_widget1.qml";
34+ "widgetSource": "qrc:/tests/indciators/qml/fake_menu_widget1.qml",
32− pageSource: "qrc:/tests/indciators/qml/fake_menu_page1.qml";
35+ "pageSource": "qrc:/tests/indciators/qml/fake_menu_page1.qml",
33− indicatorProperties: "";
34− isValid: "";
35− }
36− ListElement {
37− identifier: "indicator-fake2";
38− priority: 1
39− title: "Menu2";
40− description: "";
41− widgetSource: "qrc:/tests/indciators/qml/fake_menu_widget2.qml";
42− pageSource: "qrc:/tests/indciators/qml/fake_menu_page2.qml";
43− indicatorProperties: "";
36+ "indicatorProperties": ""
44− isValid: "";
45− }
46− ListElement {
47− identifier: "indicator-fake3";
48− priority: 2
49− title: "Menu3";
50− description: "";
51− widgetSource: "qrc:/tests/indciators/qml/fake_menu_widget3.qml";
52− pageSource: "qrc:/tests/indciators/qml/fake_menu_page3.qml";
53− indicatorProperties: "";
54− isValid: "";
55− }
37+ });
56− ListElement {
57− identifier: "indicator-fake4";
58− priority: 3
59− title: "Menu4";
60− description: "";
61− widgetSource: "qrc:/tests/indciators/qml/fake_menu_widget4.qml";
62− pageSource: "qrc:/tests/indciators/qml/fake_menu_page4.qml";
63− indicatorProperties: "";
64− isValid: "";
65− }
66− ListElement {
67− identifier: "indicator-fake5";
68− priority: 4
69− title: "Menu5";
70− description: "";
71− widgetSource: "qrc:/tests/indciators/qml/fake_menu_widget5.qml";
72− pageSource: "qrc:/tests/indciators/qml/fake_menu_page5.qml";
73− indicatorProperties: "";
74− isValid: "";
75− }
76
38
77− function data(row, role) {
39+ append({ "identifier": "indicator-fake2",
78− if (role == Indicators.IndicatorsModelRole.Identifier) {
40+ "priority": 1,
79− return get(row).identifier;
41+ "title": "Menu2",
80− }
42+ "description": "",
81− else if (role == Indicators.IndicatorsModelRole.Priority) {
43+ "widgetSource": "qrc:/tests/indciators/qml/fake_menu_widget2.qml",
82− return get(row).priority
44+ "pageSource": "qrc:/tests/indciators/qml/fake_menu_page2.qml",
83− }
45+ "indicatorProperties": ""
84− else if (role == Indicators.IndicatorsModelRole.Title) {
85− return get(row).title
86− }
46+ });
87− else if (role == Indicators.IndicatorsModelRole.Description) {
47+
88− return get(row).description
48+ append({ "identifier": "indicator-fake3",
89− }
49+ "priority": 2,
90− else if (role == Indicators.IndicatorsModelRole.WidgetSource) {
50+ "title": "Menu3",
91− return get(row).widgetSource
51+ "description": "",
92− }
52+ "widgetSource": "qrc:/tests/indciators/qml/fake_menu_widget3.qml",
93− else if (role == Indicators.IndicatorsModelRole.PageSource) {
53+ "pageSource": "qrc:/tests/indciators/qml/fake_menu_page3.qml",
94− return get(row).pageSource
54+ "indicatorProperties": ""
95− }
96− else if (role == Indicators.IndicatorsModelRole.IndicatorProperties) {
97− return get(row).indicatorProperties
98− }
99− else if (role == Indicators.IndicatorsModelRole.IsValid) {
100− return get(row).isValid
101− }
102− }
55+ });
103
56
57+ append({ "identifier": "indicator-fake4",
58+ "priority": 3,
59+ "title": "Menu4",
60+ "description": "",
61+ "widgetSource": "qrc:/tests/indciators/qml/fake_menu_widget4.qml",
62+ "pageSource": "qrc:/tests/indciators/qml/fake_menu_page4.qml",
63+ "indicatorProperties": ""
64+ });
65+
66+ append({ "identifier": "indicator-fake5",
67+ "priority": 4,
68+ "title": "Menu5",
69+ "description": "",
70+ "widgetSource": "qrc:/tests/indciators/qml/fake_menu_widget5.qml",
71+ "pageSource": "qrc:/tests/indciators/qml/fake_menu_page5.qml",
72+ "indicatorProperties": ""
73+ });
74+ }
104}
75}
tests/mocks/Unity/Indicators/fakeplugin.cpp+2
@@ -25,12 +25,14 @@
25// local
25// local
26#include "indicators.h"
26#include "indicators.h"
27#include "menucontentactivator.h"
27#include "menucontentactivator.h"
28+#include "visibleindicatorsmodel.h"
28
29
29void IndicatorsFakePlugin::registerTypes(const char * uri)
30void IndicatorsFakePlugin::registerTypes(const char * uri)
30{
31{
31 Q_INIT_RESOURCE(indicators_fake);
32 Q_INIT_RESOURCE(indicators_fake);
32
33
33 qmlRegisterType<MenuContentActivator>(uri, 0, 1, "MenuContentActivator");
34 qmlRegisterType<MenuContentActivator>(uri, 0, 1, "MenuContentActivator");
35+ qmlRegisterType<VisibleIndicatorsModel>(uri, 0, 1, "VisibleIndicatorsModel");
34
36
35 qmlRegisterUncreatableType<MenuContentState>(uri, 0, 1, "MenuContentState", "Can't create MenuContentState class");
37 qmlRegisterUncreatableType<MenuContentState>(uri, 0, 1, "MenuContentState", "Can't create MenuContentState class");
36 qmlRegisterUncreatableType<ActionState>(uri, 0, 1, "ActionState", "Can't create ActionState class");
38 qmlRegisterUncreatableType<ActionState>(uri, 0, 1, "ActionState", "Can't create ActionState class");
tests/mocks/Unity/Indicators/qml/fake_menu_widget2.qml+2
@@ -20,4 +20,6 @@ Rectangle {
20 width: 40
20 width: 40
21 height: 40
21 height: 40
22 color : "yellow"
22 color : "yellow"
23+
24+ enabled: false
23}
25}
tests/mocks/Unity/Launcher/MockLauncherItem.cpp+16
@@ -33,6 +33,7 @@ MockLauncherItem::MockLauncherItem(const QString &appId, const QString& desktopF
33 m_recent(false),
33 m_recent(false),
34 m_progress(-1),
34 m_progress(-1),
35 m_count(0),
35 m_count(0),
36+ m_focused(false),
36 m_quickList(new MockQuickListModel(this))
37 m_quickList(new MockQuickListModel(this))
37{
38{
38
39
@@ -128,6 +129,21 @@ void MockLauncherItem::setCount(int count)
128 }
129 }
129}
130}
130
131
132+bool MockLauncherItem::focused() const
133+{
134+ return m_focused;
135+}
136+
137+void MockLauncherItem::setFocused(bool focused)
138+{
139+ if (m_focused != focused)
140+ {
141+ m_focused = focused;
142+ Q_EMIT focusedChanged(focused);
143+ }
144+}
145+
146+
131unity::shell::launcher::QuickListModelInterface *MockLauncherItem::quickList() const
147unity::shell::launcher::QuickListModelInterface *MockLauncherItem::quickList() const
132{
148{
133 return m_quickList;
149 return m_quickList;
tests/mocks/Unity/Launcher/MockLauncherItem.h+4
@@ -52,6 +52,9 @@ public:
52 int count() const;
52 int count() const;
53 void setCount(int count);
53 void setCount(int count);
54
54
55+ bool focused() const;
56+ void setFocused(bool focused);
57+
55 unity::shell::launcher::QuickListModelInterface *quickList() const;
58 unity::shell::launcher::QuickListModelInterface *quickList() const;
56
59
57private:
60private:
@@ -64,6 +67,7 @@ private:
64 bool m_recent;
67 bool m_recent;
65 int m_progress;
68 int m_progress;
66 int m_count;
69 int m_count;
70+ bool m_focused;
67 MockQuickListModel *m_quickList;
71 MockQuickListModel *m_quickList;
68};
72};
69
73
tests/mocks/Unity/Launcher/MockLauncherModel.cpp+9
@@ -25,6 +25,7 @@ MockLauncherModel::MockLauncherModel(QObject* parent): LauncherModelInterface(pa
25 MockLauncherItem *item = new MockLauncherItem("phone-app", "/usr/share/applications/phone-app.desktop", "Phone", "phone-app", this);
25 MockLauncherItem *item = new MockLauncherItem("phone-app", "/usr/share/applications/phone-app.desktop", "Phone", "phone-app", this);
26 item->setProgress(0);
26 item->setProgress(0);
27 m_list.append(item);
27 m_list.append(item);
28+ item->setFocused(true);
28 item = new MockLauncherItem("camera-app", "/usr/share/applications/camera-app.desktop", "Camera", "camera", this);
29 item = new MockLauncherItem("camera-app", "/usr/share/applications/camera-app.desktop", "Camera", "camera", this);
29 item->setProgress(10);
30 item->setProgress(10);
30 m_list.append(item);
31 m_list.append(item);
@@ -49,6 +50,7 @@ MockLauncherModel::MockLauncherModel(QObject* parent): LauncherModelInterface(pa
49 item = new MockLauncherItem("notes-app", "/usr/share/applications/notes-app.desktop", "Notepad", "notepad", this);
50 item = new MockLauncherItem("notes-app", "/usr/share/applications/notes-app.desktop", "Notepad", "notepad", this);
50 item->setProgress(50);
51 item->setProgress(50);
51 item->setCount(5);
52 item->setCount(5);
53+ item->setFocused(true);
52 m_list.append(item);
54 m_list.append(item);
53 item = new MockLauncherItem("calendar-app", "/usr/share/applications/calendar-app.desktop","Calendar", "calendar", this);
55 item = new MockLauncherItem("calendar-app", "/usr/share/applications/calendar-app.desktop","Calendar", "calendar", this);
54 m_list.append(item);
56 m_list.append(item);
@@ -85,6 +87,8 @@ QVariant MockLauncherModel::data(const QModelIndex& index, int role) const
85 return item->progress();
87 return item->progress();
86 case RoleCount:
88 case RoleCount:
87 return item->count();
89 return item->count();
90+ case RoleFocused:
91+ return item->focused();
88 }
92 }
89
93
90 return QVariant();
94 return QVariant();
@@ -180,3 +184,8 @@ void MockLauncherModel::setUser(const QString &username)
180 Q_UNUSED(username)
184 Q_UNUSED(username)
181 // TODO: implement this...
185 // TODO: implement this...
182}
186}
187+
188+void MockLauncherModel::applicationFocused(const QString &appId)
189+{
190+ Q_UNUSED(appId)
191+}
tests/mocks/Unity/Launcher/MockLauncherModel.h+1
@@ -44,6 +44,7 @@ public:
44 Q_INVOKABLE void requestRemove(const QString &appId);
44 Q_INVOKABLE void requestRemove(const QString &appId);
45 Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex);
45 Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex);
46 Q_INVOKABLE void setUser(const QString &username);
46 Q_INVOKABLE void setUser(const QString &username);
47+ Q_INVOKABLE void applicationFocused(const QString &appId);
47
48
48Q_SIGNALS:
49Q_SIGNALS:
49 void quickListTriggered(const QString &appId, int index);
50 void quickListTriggered(const QString &appId, int index);
tests/mocks/Unity/fake_applications_scope.cppnew file+68
@@ -0,0 +1,68 @@
1+/*
2+ * Copyright (C) 2013 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 "fake_applications_scope.h"
18+
19+static const gchar * categories_model_schema[] = {
20+ "s", //ID
21+ "s", // DISPLAY_NAME
22+ "s", // ICON_HINT
23+ "s", // RENDERER_NAME
24+ "a{sv}" // HINTS
25+};
26+
27+ApplicationsScope::ApplicationsScope(bool visible, QObject* parent)
28+ : Scope(parent)
29+{
30+ m_id = "applications.scope";
31+ m_name = "Applications";
32+ m_visible = visible;
33+ m_categories->setModel(createCategoriesModel());
34+}
35+
36+DeeModel* ApplicationsScope::createCategoriesModel()
37+{
38+ DeeModel* category_model = dee_sequence_model_new();
39+ dee_model_set_schema_full(category_model, categories_model_schema, G_N_ELEMENTS(categories_model_schema));
40+ GVariant* hints = g_variant_new_array(g_variant_type_element(G_VARIANT_TYPE_VARDICT), NULL, 0);
41+
42+ GVariant* children[1];
43+ children[0] = g_variant_new_dict_entry(g_variant_new_string("content-type"),
44+ g_variant_new_variant(g_variant_new_string("apps")));
45+ GVariant* recentHints = g_variant_new_array(g_variant_type_element(G_VARIANT_TYPE_VARDICT), children, 1);
46+ dee_model_append(category_model,
47+ "recent",
48+ "Recent",
49+ "gtk-apply",
50+ "special",
51+ recentHints);
52+
53+ dee_model_append(category_model,
54+ "installed",
55+ "Installed",
56+ "gtk-apply",
57+ "grid",
58+ hints);
59+
60+ dee_model_append(category_model,
61+ "suggested",
62+ "Suggested",
63+ "gtk-apply",
64+ "grid",
65+ hints);
66+
67+ return category_model;
68+}
tests/mocks/Unity/fake_applications_scope.hnew file+33
@@ -0,0 +1,33 @@
1+/*
2+ * Copyright (C) 2013 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 FAKE_APPLICATIONS_SCOPE_H
18+#define FAKE_APPLICATIONS_SCOPE_H
19+
20+#include "fake_scope.h"
21+
22+class ApplicationsScope : public Scope
23+{
24+ Q_OBJECT
25+
26+public:
27+ ApplicationsScope(bool visible, QObject* parent = 0);
28+
29+private:
30+ DeeModel* createCategoriesModel();
31+};
32+
33+#endif // FAKE_APPLICATIONS_SCOPE_H
tests/mocks/Unity/fake_categories.cpp+84−12
@@ -24,11 +24,20 @@
24
24
25#define CATEGORY_COLUMN 2
25#define CATEGORY_COLUMN 2
26
26
27+// TODO: use something from libunity once it's public
28+enum CategoryColumn {
29+ ID,
30+ DISPLAY_NAME,
31+ ICON_HINT,
32+ RENDERER_NAME,
33+ HINTS
34+};
35+
27Categories::Categories(QObject* parent)
36Categories::Categories(QObject* parent)
28 : DeeListModel(parent)
37 : DeeListModel(parent)
29{
38{
30 // FIXME: need to clean up unused filters on countChanged
39 // FIXME: need to clean up unused filters on countChanged
31− m_roles[Categories::RoleId] = "id";
40+ m_roles[Categories::RoleCategoryId] = "categoryId";
32 m_roles[Categories::RoleName] = "name";
41 m_roles[Categories::RoleName] = "name";
33 m_roles[Categories::RoleIcon] = "icon";
42 m_roles[Categories::RoleIcon] = "icon";
34 m_roles[Categories::RoleRenderer] = "renderer";
43 m_roles[Categories::RoleRenderer] = "renderer";
@@ -36,7 +45,6 @@ Categories::Categories(QObject* parent)
36 m_roles[Categories::RoleHints] = "hints";
45 m_roles[Categories::RoleHints] = "hints";
37 m_roles[Categories::RoleResults] = "results";
46 m_roles[Categories::RoleResults] = "results";
38 m_roles[Categories::RoleCount] = "count";
47 m_roles[Categories::RoleCount] = "count";
39− m_roles[Categories::RoleCategoryIndex] = "categoryIndex";
40
48
41 // TODO This should not be needed but accumulatting the count changes
49 // TODO This should not be needed but accumulatting the count changes
42 // makes the visualization more stable and also makes crashes on fast
50 // makes the visualization more stable and also makes crashes on fast
@@ -74,6 +82,25 @@ Categories::onCountChanged()
74 }
82 }
75}
83}
76
84
85+void Categories::onRowCountChanged()
86+{
87+ QAbstractItemModel* model = qobject_cast<QAbstractItemModel*>(sender());
88+ // find the corresponding category index
89+ for (auto iter = m_overriddenCategories.begin(); iter != m_overriddenCategories.end(); ++iter) {
90+ if (iter.value() == model) {
91+ for (int i = 0; i < rowCount(); i++) {
92+ auto id = data(index(i), RoleCategoryId).toString();
93+ if (id != iter.key()) continue;
94+ QVector<int> roles;
95+ roles.append(RoleCount);
96+ QModelIndex changedIndex = index(i);
97+ Q_EMIT dataChanged(changedIndex, changedIndex, roles);
98+ break;
99+ }
100+ }
101+ }
102+}
103+
77void
104void
78Categories::onEmitCountChanged()
105Categories::onEmitCountChanged()
79{
106{
@@ -93,6 +120,41 @@ Categories::roleNames() const
93 return m_roles;
120 return m_roles;
94}
121}
95
122
123+void Categories::onOverrideModelDestroyed()
124+{
125+ QObject* model = sender();
126+ auto iter = m_overriddenCategories.begin();
127+ while (iter != m_overriddenCategories.end()) {
128+ if (iter.value() == model) {
129+ iter = m_overriddenCategories.erase(iter);
130+ continue;
131+ }
132+ ++iter;
133+ }
134+}
135+
136+void Categories::overrideResults(const QString& categoryId, QAbstractItemModel* model)
137+{
138+ m_overriddenCategories[categoryId] = model;
139+ // watch the model
140+ connect(model, &QObject::destroyed, this, &Categories::onOverrideModelDestroyed);
141+ connect(model, &QAbstractItemModel::rowsInserted, this, &Categories::onRowCountChanged);
142+ connect(model, &QAbstractItemModel::rowsRemoved, this, &Categories::onRowCountChanged);
143+ connect(model, &QAbstractItemModel::modelReset, this, &Categories::onRowCountChanged);
144+
145+ // emit the dataChanged signal if the category is already in the model
146+ for (int i = 0; i < rowCount(); i++) {
147+ auto id = data(index(i), RoleCategoryId).toString();
148+ if (id != categoryId) continue;
149+ QVector<int> roles;
150+ roles.append(RoleCount);
151+ roles.append(RoleResults);
152+ QModelIndex changedIndex = index(i);
153+ Q_EMIT dataChanged(changedIndex, changedIndex, roles);
154+ break;
155+ }
156+}
157+
96void Categories::setResultModel(DeeModel* model)
158void Categories::setResultModel(DeeModel* model)
97{
159{
98 // FIXME: should ref it
160 // FIXME: should ref it
@@ -154,27 +216,37 @@ Categories::data(const QModelIndex& index, int role) const
154 }
216 }
155
217
156 switch (role) {
218 switch (role) {
157− case RoleId:
219+ case RoleCategoryId:
158− return DeeListModel::data(index, 0); //ID
220+ return DeeListModel::data(index, CategoryColumn::ID);
159 case RoleName:
221 case RoleName:
160− return DeeListModel::data(index, 1); //DISPLAY_NAME
222+ return DeeListModel::data(index, CategoryColumn::DISPLAY_NAME);
161 case RoleIcon:
223 case RoleIcon:
162− return DeeListModel::data(index, 2); //ICON_HINT
224+ return DeeListModel::data(index, CategoryColumn::ICON_HINT);
163 case RoleRenderer:
225 case RoleRenderer:
164− return DeeListModel::data(index, 3); //RENDERER_NAME
226+ return DeeListModel::data(index, CategoryColumn::RENDERER_NAME);
165 case RoleContentType:
227 case RoleContentType:
166 {
228 {
167− auto hints = DeeListModel::data(index, 4).toHash();
229+ auto hints = DeeListModel::data(index, CategoryColumn::HINTS).toHash();
168− return hints.contains("content-type") ? hints["content-type"] : QVariant(QString("default"));
230+ return hints.contains("content-type") ? hints["content-type"] : QVariant(QString("default"));
169 }
231 }
170 case RoleHints:
232 case RoleHints:
171− return DeeListModel::data(index, 4); //HINTS
233+ return DeeListModel::data(index, CategoryColumn::HINTS);
172 case RoleResults:
234 case RoleResults:
235+ if (m_overriddenCategories.size() > 0)
236+ {
237+ auto id = DeeListModel::data(index, CategoryColumn::ID).toString();
238+ if (m_overriddenCategories.find(id) != m_overriddenCategories.end())
239+ return QVariant::fromValue(m_overriddenCategories[id]);
240+ }
173 return QVariant::fromValue(getFilter(index.row()));
241 return QVariant::fromValue(getFilter(index.row()));
174 case RoleCount:
242 case RoleCount:
243+ if (m_overriddenCategories.size() > 0)
244+ {
245+ auto id = DeeListModel::data(index, CategoryColumn::ID).toString();
246+ if (m_overriddenCategories.find(id) != m_overriddenCategories.end())
247+ return QVariant::fromValue(m_overriddenCategories[id]->rowCount());
248+ }
175 return QVariant::fromValue(getFilter(index.row())->rowCount());
249 return QVariant::fromValue(getFilter(index.row())->rowCount());
176− case RoleCategoryIndex:
177− return QVariant::fromValue(index.row());
178 default:
250 default:
179 return QVariant();
251 return QVariant();
180 }
252 }
tests/mocks/Unity/fake_categories.h+7−3
@@ -34,17 +34,18 @@ class Categories : public DeeListModel
34public:
34public:
35 Categories(QObject* parent = 0);
35 Categories(QObject* parent = 0);
36 enum Roles {
36 enum Roles {
37− RoleId,
37+ RoleCategoryId,
38 RoleName,
38 RoleName,
39 RoleIcon,
39 RoleIcon,
40 RoleRenderer,
40 RoleRenderer,
41 RoleContentType,
41 RoleContentType,
42 RoleHints,
42 RoleHints,
43 RoleResults,
43 RoleResults,
44− RoleCount,
44+ RoleCount
45− RoleCategoryIndex
46 };
45 };
47
46
47+ Q_INVOKABLE void overrideResults(const QString& categoryId, QAbstractItemModel* model);
48+
48 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
49 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
49
50
50 QHash<int, QByteArray> roleNames() const;
51 QHash<int, QByteArray> roleNames() const;
@@ -54,7 +55,9 @@ public:
54
55
55private Q_SLOTS:
56private Q_SLOTS:
56 void onCountChanged();
57 void onCountChanged();
58+ void onRowCountChanged();
57 void onEmitCountChanged();
59 void onEmitCountChanged();
60+ void onOverrideModelDestroyed();
58
61
59private:
62private:
60 DeeModel* getResultsForCategory(unsigned index) const;
63 DeeModel* getResultsForCategory(unsigned index) const;
@@ -64,6 +67,7 @@ private:
64 QTimer m_timer;
67 QTimer m_timer;
65 QSet<DeeListModel*> m_timerFilters;
68 QSet<DeeListModel*> m_timerFilters;
66 QHash<int, QByteArray> m_roles;
69 QHash<int, QByteArray> m_roles;
70+ QMap<QString, QAbstractItemModel*> m_overriddenCategories;
67 mutable QMap<int, DeeListModel*> m_filters;
71 mutable QMap<int, DeeListModel*> m_filters;
68};
72};
69
73
tests/mocks/Unity/fake_preview.cppnew file+66
@@ -0,0 +1,66 @@
1+/*
2+ * Copyright (C) 2013 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 "fake_preview.h"
18+
19+QString Preview::rendererName() const
20+{
21+ return "foo";
22+}
23+
24+QString Preview::title() const
25+{
26+ return "Title";
27+}
28+
29+QString Preview::subtitle() const
30+{
31+ return "Subtitle";
32+}
33+
34+QString Preview::description() const
35+{
36+ return "Description";
37+}
38+
39+QVariant Preview::actions()
40+{
41+ return QVariant();
42+}
43+
44+QVariant Preview::infoHints()
45+{
46+ return QVariant();
47+}
48+
49+QVariantMap Preview::infoHintsHash() const
50+{
51+ return QVariantMap();
52+}
53+
54+QString Preview::image() const
55+{
56+ return "";
57+}
58+
59+QString Preview::imageSourceUri() const
60+{
61+ return "";
62+}
63+
64+void Preview::execute(const QString& actionId, const QHash<QString, QVariant>& hints)
65+{
66+}
tests/mocks/Unity/fake_preview.hnew file+62
@@ -0,0 +1,62 @@
1+/*
2+ * Copyright (C) 2013 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 FAKE_PREVIEW_H
18+#define FAKE_PREVIEW_H
19+
20+// Qt
21+#include <QObject>
22+#include <QString>
23+#include <QMetaType>
24+#include <QList>
25+#include <QVariantMap>
26+
27+class Q_DECL_EXPORT Preview : public QObject
28+{
29+ Q_OBJECT
30+
31+ Q_PROPERTY(QString rendererName READ rendererName NOTIFY previewChanged)
32+ Q_PROPERTY(QString title READ title NOTIFY previewChanged)
33+ Q_PROPERTY(QString subtitle READ subtitle NOTIFY previewChanged)
34+ Q_PROPERTY(QString description READ description NOTIFY previewChanged)
35+ Q_PROPERTY(QVariant actions READ actions NOTIFY previewChanged)
36+ Q_PROPERTY(QVariant infoHints READ infoHints NOTIFY previewChanged)
37+ Q_PROPERTY(QVariantMap infoMap READ infoHintsHash NOTIFY previewChanged)
38+ Q_PROPERTY(QString image READ image NOTIFY previewChanged)
39+ Q_PROPERTY(QString imageSourceUri READ imageSourceUri NOTIFY previewChanged)
40+
41+public:
42+ explicit Preview(QObject *parent = 0);
43+
44+ QString rendererName() const;
45+ QString title() const;
46+ QString subtitle() const;
47+ QString description() const;
48+ QVariant actions();
49+ QVariant infoHints();
50+ QVariantMap infoHintsHash() const;
51+ QString image() const;
52+ QString imageSourceUri() const;
53+
54+ Q_INVOKABLE void execute(const QString& actionId, const QHash<QString, QVariant>& hints);
55+
56+Q_SIGNALS:
57+ void previewChanged();
58+};
59+
60+Q_DECLARE_METATYPE(Preview *)
61+
62+#endif // FAKE_PREVIEW_H
tests/mocks/Unity/fake_scope.cpp+33−9
@@ -20,6 +20,8 @@
20#include <dee.h>
20#include <dee.h>
21#include "paths.h"
21#include "paths.h"
22
22
23+#include "fake_preview.h"
24+
23static DeeModel* create_categories_model(unsigned category_count);
25static DeeModel* create_categories_model(unsigned category_count);
24static DeeModel* create_results_model(unsigned category_count, unsigned result_count);
26static DeeModel* create_results_model(unsigned category_count, unsigned result_count);
25
27
@@ -127,6 +129,36 @@ void Scope::setNoResultsHint(const QString& str) {
127 }
129 }
128}
130}
129
131
132+void Scope::activate(const QVariant &uri, const QVariant &icon_hint, const QVariant &category,
133+ const QVariant &result_type, const QVariant &mimetype, const QVariant &title,
134+ const QVariant &comment, const QVariant &dnd_uri, const QVariant &metadata)
135+{
136+ Q_UNUSED(uri);
137+ Q_UNUSED(icon_hint);
138+ Q_UNUSED(category);
139+ Q_UNUSED(result_type);
140+ Q_UNUSED(mimetype);
141+ Q_UNUSED(title);
142+ Q_UNUSED(comment);
143+ Q_UNUSED(dnd_uri);
144+ Q_UNUSED(metadata);
145+}
146+
147+void Scope::preview(const QVariant &uri, const QVariant &icon_hint, const QVariant &category,
148+ const QVariant &result_type, const QVariant &mimetype, const QVariant &title,
149+ const QVariant &comment, const QVariant &dnd_uri, const QVariant &metadata)
150+{
151+ Q_UNUSED(uri);
152+ Q_UNUSED(icon_hint);
153+ Q_UNUSED(category);
154+ Q_UNUSED(result_type);
155+ Q_UNUSED(mimetype);
156+ Q_UNUSED(title);
157+ Q_UNUSED(comment);
158+ Q_UNUSED(dnd_uri);
159+ Q_UNUSED(metadata);
160+}
161+
130static const gchar * categories_model_schema[] = {
162static const gchar * categories_model_schema[] = {
131 "s", //ID
163 "s", //ID
132 "s", // DISPLAY_NAME
164 "s", // DISPLAY_NAME
@@ -168,14 +200,6 @@ static const gchar * results_model_schema[] = {
168 "a{sv}" // METADATA
200 "a{sv}" // METADATA
169};
201};
170
202
171−static const gchar * icons[] = {
172− "Applications.png",
173− "Home.png",
174− "Music.png",
175− "People.png",
176− "Videos.png",
177−};
178−
179DeeModel* create_results_model(unsigned category_count, unsigned result_count) {
203DeeModel* create_results_model(unsigned category_count, unsigned result_count) {
180 DeeModel* results_model = dee_sequence_model_new();
204 DeeModel* results_model = dee_sequence_model_new();
181 dee_model_set_schema_full(results_model, results_model_schema, G_N_ELEMENTS(results_model_schema));
205 dee_model_set_schema_full(results_model, results_model_schema, G_N_ELEMENTS(results_model_schema));
@@ -187,7 +211,7 @@ DeeModel* create_results_model(unsigned category_count, unsigned result_count) {
187
211
188 dee_model_append(results_model,
212 dee_model_append(results_model,
189 ("uri://result."+std::to_string(i)).c_str(),
213 ("uri://result."+std::to_string(i)).c_str(),
190− (shellAppDirectory() + "Dash/graphics/scopeIcons/" + (icons[i%G_N_ELEMENTS(icons)])).toLatin1().data(),
214+ (shellAppDirectory() + "graphics/applicationIcons/dash.png").toLatin1().data(),
191 category,
215 category,
192 0,
216 0,
193 "application/x-desktop",
217 "application/x-desktop",
tests/mocks/Unity/fake_scope.h+15−1
@@ -21,6 +21,8 @@
21#include <QObject>
21#include <QObject>
22#include "fake_categories.h"
22#include "fake_categories.h"
23
23
24+class Preview;
25+
24class Scope : public QObject
26class Scope : public QObject
25{
27{
26 Q_OBJECT
28 Q_OBJECT
@@ -63,6 +65,13 @@ public:
63 void setNoResultsHint(const QString& hint);
65 void setNoResultsHint(const QString& hint);
64 void setFormFactor(const QString& form_factor);
66 void setFormFactor(const QString& form_factor);
65
67
68+ Q_INVOKABLE void activate(const QVariant &uri, const QVariant &icon_hint, const QVariant &category,
69+ const QVariant &result_type, const QVariant &mimetype, const QVariant &title,
70+ const QVariant &comment, const QVariant &dnd_uri, const QVariant &metadata);
71+ Q_INVOKABLE void preview(const QVariant &uri, const QVariant &icon_hint, const QVariant &category,
72+ const QVariant &result_type, const QVariant &mimetype, const QVariant &title,
73+ const QVariant &comment, const QVariant &dnd_uri, const QVariant &metadata);
74+
66Q_SIGNALS:
75Q_SIGNALS:
67 void idChanged(const QString&);
76 void idChanged(const QString&);
68 void nameChanged(const QString&);
77 void nameChanged(const QString&);
@@ -78,7 +87,12 @@ Q_SIGNALS:
78 void noResultsHintChanged();
87 void noResultsHintChanged();
79 void formFactorChanged();
88 void formFactorChanged();
80
89
81−private:
90+ // signals triggered by activate(..) or preview(..) requests.
91+ void previewReady(Preview *preview);
92+ void activateApplication(const QString &desktop);
93+
94+protected:
95+
82 QString m_id;
96 QString m_id;
83 QString m_iconHint;
97 QString m_iconHint;
84 QString m_description;
98 QString m_description;
tests/mocks/Unity/fake_scopes.cpp+4−1
@@ -19,6 +19,9 @@
19// Self
19// Self
20#include "fake_scopes.h"
20#include "fake_scopes.h"
21
21
22+// Local
23+#include "fake_applications_scope.h"
24+
22// TODO: Implement remaining pieces, like Categories (i.e. LensView now gives warnings)
25// TODO: Implement remaining pieces, like Categories (i.e. LensView now gives warnings)
23
26
24// Qt
27// Qt
@@ -53,7 +56,7 @@ void Scopes::updateScopes()
53 addScope(new Scope("MockScope1", "People", true, this));
56 addScope(new Scope("MockScope1", "People", true, this));
54 addScope(new Scope("MockScope2", "Music", false, this));
57 addScope(new Scope("MockScope2", "Music", false, this));
55 addScope(new Scope("home.scope", "Home", true, this));
58 addScope(new Scope("home.scope", "Home", true, this));
56− addScope(new Scope("applications.scope", "Applications", true, this));
59+ addScope(new ApplicationsScope(true, this));
57 addScope(new Scope("MockScope5", "Videos", true, this));
60 addScope(new Scope("MockScope5", "Videos", true, this));
58
61
59 if (!m_loaded) {
62 if (!m_loaded) {
tests/plugins/Unity/CMakeLists.txt+4
@@ -5,6 +5,7 @@ pkg_check_modules(UNITYCORE REQUIRED unity-core-6.0)
5pkg_check_modules(LIBUNITYPROTO REQUIRED unity-protocol-private)
5pkg_check_modules(LIBUNITYPROTO REQUIRED unity-protocol-private)
6pkg_check_modules(LIBUNITY REQUIRED unity)
6pkg_check_modules(LIBUNITY REQUIRED unity)
7pkg_check_modules(LIBSIGCPP REQUIRED sigc++-2.0)
7pkg_check_modules(LIBSIGCPP REQUIRED sigc++-2.0)
8+pkg_check_modules(QTDEE REQUIRED libdee-qt5>=3.2)
8
9
9include_directories(
10include_directories(
10 ${Unity-qml_SOURCE_DIR}
11 ${Unity-qml_SOURCE_DIR}
@@ -14,6 +15,7 @@ include_directories(
14 ${LIBUNITY_INCLUDE_DIRS}
15 ${LIBUNITY_INCLUDE_DIRS}
15 ${LIBSIGCPP_INCLUDE_DIRS}
16 ${LIBSIGCPP_INCLUDE_DIRS}
16 ${UNITYCORE_INCLUDE_DIRS}
17 ${UNITYCORE_INCLUDE_DIRS}
18+ ${QTDEE_INCLUDE_DIRS}
17 )
19 )
18
20
19macro(run_tests)
21macro(run_tests)
@@ -36,6 +38,7 @@ macro(run_tests)
36 ${LIBUNITY_LDFLAGS}
38 ${LIBUNITY_LDFLAGS}
37 ${LIBUNITYPROTO_LDFLAGS}
39 ${LIBUNITYPROTO_LDFLAGS}
38 ${LIBSIGCPP_LDFLAGS}
40 ${LIBSIGCPP_LDFLAGS}
41+ ${QTDEE_LDFLAGS}
39 )
42 )
40
43
41 set(_test_list "${_test_list};${_test}")
44 set(_test_list "${_test_list};${_test}")
@@ -46,4 +49,5 @@ run_tests(
46 signalslisttest
49 signalslisttest
47 previewbindingstest
50 previewbindingstest
48 filtersbindingstest
51 filtersbindingstest
52+ resultstest
49 )
53 )
tests/plugins/Unity/Launcher/CMakeLists.txt+40−3
@@ -1,22 +1,59 @@
1+pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
2+pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=2)
3+
1include_directories(
4include_directories(
2 ${CMAKE_CURRENT_BINARY_DIR}
5 ${CMAKE_CURRENT_BINARY_DIR}
3 ${CMAKE_SOURCE_DIR}/plugins/AccountsService
6 ${CMAKE_SOURCE_DIR}/plugins/AccountsService
4 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher
7 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher
5 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/backend
8 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/backend
9+ ${GSETTINGS_QT_INCLUDE_DIRS}
6 )
10 )
7
11
8add_definitions(-DSM_BUSNAME=sessionBus)
12add_definitions(-DSM_BUSNAME=sessionBus)
9add_definitions(-DSRCDIR="${CMAKE_CURRENT_SOURCE_DIR}")
13add_definitions(-DSRCDIR="${CMAKE_CURRENT_SOURCE_DIR}")
14+add_definitions(-DLAUNCHER_TESTING)
10
15
16+### LauncherBackendTest
11−set(testCommand ${CMAKE_CURRENT_BINARY_DIR}/launcherbackendtestExec
17+set(testBackendCommand ${CMAKE_CURRENT_BINARY_DIR}/launcherbackendtestExec
12 -o ${CMAKE_BINARY_DIR}/launcherbackendtest.xml,xunitxml
18 -o ${CMAKE_BINARY_DIR}/launcherbackendtest.xml,xunitxml
13 -o -,txt)
19 -o -,txt)
14−add_test(NAME launcherbackendtest COMMAND ${testCommand})
20+add_test(NAME launcherbackendtest COMMAND ${testBackendCommand})
15−add_custom_target(launcherbackendtest ${testCommand})
21+add_custom_target(launcherbackendtest ${testBackendCommand})
16add_executable(launcherbackendtestExec
22add_executable(launcherbackendtestExec
17 launcherbackendtest.cpp
23 launcherbackendtest.cpp
18 ${CMAKE_SOURCE_DIR}/plugins/AccountsService/AccountsService.cpp
24 ${CMAKE_SOURCE_DIR}/plugins/AccountsService/AccountsService.cpp
19 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/backend/launcherbackend.cpp
25 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/backend/launcherbackend.cpp
20 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/common/quicklistentry.cpp
26 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/common/quicklistentry.cpp
21 )
27 )
28+target_link_libraries(launcherbackendtestExec ${GSETTINGS_QT_LDFLAGS})
22qt5_use_modules(launcherbackendtestExec Test Core DBus)
29qt5_use_modules(launcherbackendtestExec Test Core DBus)
30+
31+### LauncherModelTest
32+set(testModelCommand ${CMAKE_CURRENT_BINARY_DIR}/launchermodeltestExec
33+ -o ${CMAKE_BINARY_DIR}/launchermodeltest.xml,xunitxml
34+ -o -,txt)
35+add_test(NAME launchermodeltest COMMAND ${testModelCommand})
36+add_custom_target(launchermodeltest ${testModelCommand})
37+add_executable(launchermodeltestExec
38+ launchermodeltest.cpp
39+ ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/launchermodel.cpp
40+ ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/launcheritem.cpp
41+ ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/quicklistmodel.cpp
42+ ${CMAKE_SOURCE_DIR}/plugins/AccountsService/AccountsService.cpp
43+ ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/backend/launcherbackend.cpp
44+ ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/common/quicklistentry.cpp
45+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherItemInterface.h
46+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/LauncherModelInterface.h
47+ ${LAUNCHER_API_INCLUDEDIR}/unity/shell/launcher/QuickListModelInterface.h
48+ )
49+target_link_libraries(launchermodeltestExec ${GSETTINGS_QT_LDFLAGS} )
50+qt5_use_modules(launchermodeltestExec Test Core DBus)
51+
52+# copy .desktop files into build directory for shadow builds
53+file(GLOB DESKTOP_FILES *.desktop)
54+
55+foreach(DESKTOP_FILE ${DESKTOP_FILES})
56+ file(COPY "${DESKTOP_FILE}"
57+ DESTINATION ${CMAKE_CURRENT_BINARY_DIR}
58+ )
59+endforeach()
tests/plugins/Unity/Launcher/launcherbackendtest.cpp+14−48
@@ -20,7 +20,6 @@
20#include "launcherbackend.h"
20#include "launcherbackend.h"
21
21
22#include <QtTest>
22#include <QtTest>
23−#include <QDebug>
24
23
25class LauncherBackendTest : public QObject
24class LauncherBackendTest : public QObject
26{
25{
@@ -31,62 +30,29 @@ private Q_SLOTS:
31 {
30 {
32 LauncherBackend backend(false);
31 LauncherBackend backend(false);
33
32
34− backend.setStoredApplications(QStringList() << "relative.path" << "/full/path");
33+ backend.setStoredApplications(QStringList() << "rel-icon" << "abs-icon" << "invalid");
35− QCOMPARE(backend.storedApplications(), QStringList() << "/usr/share/applications/relative.path" << "/full/path");
34+ QCOMPARE(backend.storedApplications(), QStringList() << "rel-icon" << "abs-icon");
36−
37− QCOMPARE(backend.desktopFile("one.desktop"), QString("/usr/share/applications/one.desktop"));
38− QCOMPARE(backend.desktopFile("two"), QString("/usr/share/applications/two"));
39− QCOMPARE(backend.desktopFile("/full"), QString("/full"));
40−
41− QCOMPARE(backend.desktopFile("/usr/share/applications/one.desktop"), QString("/usr/share/applications/one.desktop"));
42− }
43−
44− void testDesktopReading()
45− {
46− LauncherBackend backend(false);
47−
48− QCOMPARE(backend.displayName(SRCDIR "/rel-icon.desktop"), QString("Relative Icon"));
49− QCOMPARE(backend.icon(SRCDIR "/rel-icon.desktop"), QString("image://gicon/rel-icon"));
50−
51− QCOMPARE(backend.displayName(SRCDIR "/abs-icon.desktop"), QString("Absolute Icon"));
52− QCOMPARE(backend.icon(SRCDIR "/abs-icon.desktop"), QString("/path/to/icon.png"));
53−
54− QCOMPARE(backend.displayName(SRCDIR "/no-icon.desktop"), QString("No Icon"));
55− QCOMPARE(backend.icon(SRCDIR "/no-icon.desktop"), QString(""));
56−
57− QCOMPARE(backend.displayName(SRCDIR "/no-name.desktop"), QString(""));
58− QCOMPARE(backend.icon(SRCDIR "/no-name.desktop"), QString("image://gicon/no-name"));
59−
60− QCOMPARE(backend.displayName(SRCDIR "/no-exist.desktop"), QString(""));
61− QCOMPARE(backend.icon(SRCDIR "/no-exist.desktop"), QString(""));
62 }
35 }
63
36
64 void testPinning()
37 void testPinning()
65 {
38 {
66 LauncherBackend backend(false);
39 LauncherBackend backend(false);
67
40
68− // Confirm that default entries are all pinned
69− auto defaultApps = backend.storedApplications();
70− QVERIFY(defaultApps.size() > 0);
71− for (auto app: defaultApps) {
72− QCOMPARE(backend.isPinned(app), true);
73− }
74−
75− backend.setStoredApplications(QStringList() << "one" << "two");
41+ backend.setStoredApplications(QStringList() << "rel-icon" << "abs-icon");
76− QCOMPARE(backend.isPinned("one"), false);
42+ QCOMPARE(backend.isPinned("rel-icon"), false);
77− QCOMPARE(backend.isPinned("two"), false);
43+ QCOMPARE(backend.isPinned("abs-icon"), false);
78
44
79− backend.setPinned("two", true);
45+ backend.setPinned("rel-icon", true);
80− QCOMPARE(backend.isPinned("two"), true);
46+ QCOMPARE(backend.isPinned("rel-icon"), true);
81
47
82− backend.setStoredApplications(QStringList() << "one" << "two" << "three");
48+ backend.setStoredApplications(QStringList() << "rel-icon" << "abs-icon" << "no-name");
83− QCOMPARE(backend.isPinned("two"), true);
49+ QCOMPARE(backend.isPinned("rel-icon"), true);
84− QCOMPARE(backend.isPinned("three"), false);
50+ QCOMPARE(backend.isPinned("no-name"), false);
85
51
86− backend.setPinned("three", true);
52+ backend.setPinned("no-name", true);
87− backend.setStoredApplications(QStringList() << "one" << "two");
53+ backend.setStoredApplications(QStringList() << "rel-icon" << "abs-icon");
88− QCOMPARE(backend.isPinned("two"), true);
54+ QCOMPARE(backend.isPinned("rel-icon"), true);
89− QCOMPARE(backend.isPinned("three"), false); // doesn't exist anymore!
55+ QCOMPARE(backend.isPinned("no-name"), false); // doesn't exist anymore!
90 }
56 }
91};
57};
92
58
tests/plugins/Unity/Launcher/launchermodeltest.cppnew file+146
@@ -0,0 +1,146 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors:
17+ * Michael Zanetti <michael.zanetti@canonical.com>
18+ */
19+
20+// unity-api
21+#include <unity/shell/launcher/LauncherModelInterface.h>
22+
23+#include "launcheritem.h"
24+#include "launchermodel.h"
25+
26+#include <QtTest>
27+
28+class LauncherModelTest : public QObject
29+{
30+ Q_OBJECT
31+
32+private:
33+ LauncherModel *launcherModel;
34+
35+private Q_SLOTS:
36+
37+ void initTestCase() {
38+ launcherModel = new LauncherModel(this);
39+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 0);
40+ }
41+
42+ void init() {
43+ qDebug() << "init";
44+ launcherModel->applicationFocused("abs-icon");
45+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
46+
47+ launcherModel->applicationFocused("no-icon.desktop");
48+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
49+
50+ launcherModel->applicationFocused(QString());
51+ }
52+
53+ void cleanup() {
54+ while (launcherModel->rowCount(QModelIndex()) > 0) {
55+ launcherModel->requestRemove(launcherModel->get(0)->appId());
56+ }
57+ }
58+
59+ void testMove() {
60+ QCOMPARE(launcherModel->get(0)->pinned(), false);
61+ QCOMPARE(launcherModel->get(1)->pinned(), false);
62+
63+ LauncherItemInterface *item0BeforeMove = launcherModel->get(0);
64+ LauncherItemInterface *item1BeforeMove = launcherModel->get(1);
65+ launcherModel->move(1, 0);
66+
67+ QCOMPARE(item0BeforeMove, launcherModel->get(1));
68+ QCOMPARE(item1BeforeMove, launcherModel->get(0));
69+
70+ // moved item must be pinned now
71+ QCOMPARE(item0BeforeMove->pinned(), false);
72+ QCOMPARE(item1BeforeMove->pinned(), true);
73+ }
74+
75+ void testPinning() {
76+ QCOMPARE(launcherModel->get(0)->pinned(), false);
77+ QCOMPARE(launcherModel->get(1)->pinned(), false);
78+ launcherModel->pin(launcherModel->get(0)->appId());
79+ QCOMPARE(launcherModel->get(0)->pinned(), true);
80+ QCOMPARE(launcherModel->get(1)->pinned(), false);
81+ }
82+
83+ void testRemove() {
84+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
85+ launcherModel->requestRemove(launcherModel->get(0)->appId());
86+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
87+ }
88+
89+ void testQuickListPinningRemoving() {
90+ // we start with 2 unpinned items
91+ QCOMPARE(launcherModel->get(0)->pinned(), false);
92+ QCOMPARE(launcherModel->get(1)->pinned(), false);
93+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
94+
95+ // find the Pin item in the quicklist
96+ QuickListModel *model = qobject_cast<QuickListModel*>(launcherModel->get(0)->quickList());
97+ int pinActionIndex = -1;
98+ for (int i = 0; i < model->rowCount(QModelIndex()); ++i) {
99+ if (model->get(i).actionId() == "pin_item") {
100+ pinActionIndex = i;
101+ break;
102+ }
103+ }
104+ QVERIFY(pinActionIndex >= 0);
105+
106+ // trigger pin item quicklist action => Item must be pinned now.
107+ launcherModel->quickListActionInvoked(launcherModel->get(0)->appId(), pinActionIndex);
108+ QCOMPARE(launcherModel->get(0)->pinned(), true);
109+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
110+
111+ // quicklist needs to transform to remove item. trigger it and check it item goes away
112+ launcherModel->quickListActionInvoked(launcherModel->get(0)->appId(), pinActionIndex);
113+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
114+ }
115+
116+ void testApplicationFocused() {
117+ // all apps unfocused at beginning...
118+ QCOMPARE(launcherModel->get(0)->focused(), false);
119+ QCOMPARE(launcherModel->get(1)->focused(), false);
120+
121+ launcherModel->applicationFocused("abs-icon");
122+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
123+ QCOMPARE(launcherModel->get(0)->focused(), true);
124+ QCOMPARE(launcherModel->get(1)->focused(), false);
125+
126+ launcherModel->applicationFocused("no-icon");
127+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
128+ QCOMPARE(launcherModel->get(0)->focused(), false);
129+ QCOMPARE(launcherModel->get(1)->focused(), true);
130+
131+ launcherModel->applicationFocused("rel-icon");
132+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 3);
133+ QCOMPARE(launcherModel->get(0)->focused(), false);
134+ QCOMPARE(launcherModel->get(1)->focused(), false);
135+ QCOMPARE(launcherModel->get(2)->focused(), true);
136+
137+ launcherModel->applicationFocused(QString());
138+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 3);
139+ QCOMPARE(launcherModel->get(0)->focused(), false);
140+ QCOMPARE(launcherModel->get(1)->focused(), false);
141+ QCOMPARE(launcherModel->get(2)->focused(), false);
142+ }
143+};
144+
145+QTEST_GUILESS_MAIN(LauncherModelTest)
146+#include "launchermodeltest.moc"
tests/plugins/Unity/resultstest.cppnew file+104
@@ -0,0 +1,104 @@
1+/*
2+ * Copyright (C) 2013 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+ * Authors:
17+ * Michal Hruby <michal.hruby@canonical.com>
18+ */
19+
20+#include <QTest>
21+#include <dee.h>
22+
23+#include "resultstest.h"
24+#include "categoryresults.h"
25+
26+static DeeModel* createBackendModel()
27+{
28+ auto model = dee_sequence_model_new();
29+ dee_model_set_schema(model, "s", "s", "u", "u", "s", "s", "s", "s", "a{sv}", NULL);
30+
31+ return model;
32+}
33+
34+void ResultsTest::testAllColumns()
35+{
36+ auto deeModel = createBackendModel();
37+ GVariantBuilder builder;
38+ g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
39+ g_variant_builder_add(&builder, "{sv}", "metadata-field", g_variant_new_string("foo"));
40+ dee_model_append (deeModel,
41+ "test:uri",
42+ "themed-icon",
43+ 4,
44+ 0,
45+ "application/octet-stream",
46+ "Test",
47+ "Comment",
48+ "test:dnd-uri",
49+ g_variant_builder_end(&builder));
50+
51+ CategoryResults* results = new CategoryResults(this);
52+ results->setModel(deeModel);
53+
54+ auto index = results->index(0, 0); // there's just one result
55+ QCOMPARE(index.data(CategoryResults::Roles::RoleUri).toString(), QString("test:uri"));
56+ QCOMPARE(index.data(CategoryResults::Roles::RoleIconHint).toString(), QString("image://theme/themed-icon"));
57+ QCOMPARE(index.data(CategoryResults::Roles::RoleCategory).toInt(), 4);
58+ QCOMPARE(index.data(CategoryResults::Roles::RoleMimetype).toString(), QString("application/octet-stream"));
59+ QCOMPARE(index.data(CategoryResults::Roles::RoleTitle).toString(), QString("Test"));
60+ QCOMPARE(index.data(CategoryResults::Roles::RoleComment).toString(), QString("Comment"));
61+ QCOMPARE(index.data(CategoryResults::Roles::RoleDndUri).toString(), QString("test:dnd-uri"));
62+ auto metadata = index.data(CategoryResults::Roles::RoleMetadata).toHash();
63+ QCOMPARE(metadata["metadata-field"].toString(), QString("foo"));
64+}
65+
66+void ResultsTest::testIconColumn_data()
67+{
68+ QTest::addColumn<QString>("giconString");
69+ QTest::addColumn<QString>("result");
70+
71+ QTest::newRow("absolute path") << "/usr/share/icons/example.png" << "/usr/share/icons/example.png";
72+ QTest::newRow("file uri") << "file:///usr/share/icons/example.png" << "file:///usr/share/icons/example.png";
73+ QTest::newRow("http uri") << "http://images.ubuntu.com/example.jpg" << "http://images.ubuntu.com/example.jpg";
74+ QTest::newRow("themed icon") << "accessories-other" << "image://theme/accessories-other";
75+ QTest::newRow("fileicon") << ". GFileIcon http://example.org/resource.gif" << "http://example.org/resource.gif";
76+ QTest::newRow("themedicon") << ". GThemedIcon accessories-other accessories generic" << "image://theme/accessories-other,accessories,generic";
77+ QTest::newRow("annotatedicon") << ". UnityProtocolAnnotatedIcon %7B'base-icon':%20%3C'.%20GThemedIcon%20accessories-other%20accessories%20generic'%3E%7D" << "image://theme/accessories-other,accessories,generic";
78+}
79+
80+void ResultsTest::testIconColumn()
81+{
82+ QFETCH(QString, giconString);
83+ QFETCH(QString, result);
84+ auto deeModel = createBackendModel();
85+ dee_model_append (deeModel,
86+ "test:uri",
87+ giconString.toLocal8Bit().constData(),
88+ 0,
89+ 0,
90+ "application/octet-stream",
91+ "Test",
92+ "",
93+ "test:dnd-uri",
94+ g_variant_new_array(g_variant_type_element(G_VARIANT_TYPE_VARDICT), NULL, 0));
95+
96+ CategoryResults* results = new CategoryResults(this);
97+ results->setModel(deeModel);
98+
99+ auto index = results->index(0, 0); // there's just one result
100+ auto transformedIcon = index.data(CategoryResults::Roles::RoleIconHint).toString();
101+ QCOMPARE(transformedIcon, result);
102+}
103+
104+QTEST_MAIN(ResultsTest)
tests/plugins/Unity/resultstest.hnew file+35
@@ -0,0 +1,35 @@
1+/*
2+ * Copyright (C) 2013 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+ * Authors:
17+ * Michal Hruby <michal.hruby@canonical.com>
18+ */
19+
20+#ifndef RESULTSTEST_H
21+#define RESULTSTEST_H
22+
23+#include <QObject>
24+
25+class ResultsTest : public QObject
26+{
27+ Q_OBJECT
28+
29+ private Q_SLOTS:
30+ void testAllColumns();
31+ void testIconColumn_data();
32+ void testIconColumn();
33+};
34+
35+#endif
tests/qmltests/CMakeLists.txt+1−1
@@ -66,7 +66,7 @@ add_qml_test(Launcher Launcher IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltes
66add_qml_test(Notifications Notifications IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_SOURCE_DIR}/tests/mocks)
66add_qml_test(Notifications Notifications IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_SOURCE_DIR}/tests/mocks)
67add_qml_test(Panel IndicatorRow)
67add_qml_test(Panel IndicatorRow)
68add_qml_test(Panel Indicators IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
68add_qml_test(Panel Indicators IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
69−add_qml_test(Panel MenuContent)
69+add_qml_test(Panel MenuContent IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
70add_qml_test(Panel Panel IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
70add_qml_test(Panel Panel IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
71add_qml_test(Panel SearchIndicator)
71add_qml_test(Panel SearchIndicator)
72add_qml_test(SideStage SideStage IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS}
72add_qml_test(SideStage SideStage IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS}
tests/qmltests/Launcher/tst_Launcher.qml+8
@@ -198,6 +198,14 @@ Item {
198 compare(findChild(delegate, "progressOverlay").visible, LauncherModel.get(i).progress >= 0)
198 compare(findChild(delegate, "progressOverlay").visible, LauncherModel.get(i).progress >= 0)
199 }
199 }
200 }
200 }
201+
202+ function test_focusedHighlight() {
203+ var launcherListView = findChild(launcher, "launcherListView");
204+ for (var i = 0; i < launcherListView.count; ++i) {
205+ var delegate = findChild(launcherListView, "launcherDelegate" + i)
206+ compare(findChild(delegate, "focusedHighlight").visible, LauncherModel.get(i).focused)
207+ }
208+ }
201 }
209 }
202
210
203 UT.UnityTestCase {
211 UT.UnityTestCase {
tests/qmltests/Panel/tst_IndicatorItem.qml+2−2
@@ -53,9 +53,9 @@ Rectangle {
53 }
53 }
54
54
55 function test_empty() {
55 function test_empty() {
56− compare(indicatorItem.visible, false, "IndicatorItem should not be visible.");
56+ compare(indicatorItem.indicatorVisible, false, "IndicatorItem should not be visible.");
57 indicatorItem.widgetSource = "qrc:/tests/indciators/qml/fake_menu_widget1.qml";
57 indicatorItem.widgetSource = "qrc:/tests/indciators/qml/fake_menu_widget1.qml";
58− tryCompare(indicatorItem, "visible", true);
58+ tryCompare(indicatorItem, "indicatorVisible", true);
59 }
59 }
60 }
60 }
61}
61}
tests/qmltests/Panel/tst_Indicators.qml+30−11
@@ -100,26 +100,29 @@ Item {
100 {
100 {
101 // tests changing the lateral position of the revealer activates the correct indicator items.
101 // tests changing the lateral position of the revealer activates the correct indicator items.
102
102
103− var indicator_row = findChild(indicators, "indicatorRow")
103+ var indicatorRow = findChild(indicators, "indicatorRow")
104− var row_repeater = findChild(indicators, "rowRepeater")
104+ var rowRepeater = findChild(indicators, "rowRepeater")
105
105
106− for (var i = 0; i < row_repeater.count; i++) {
106+ for (var i = 0; i < rowRepeater.count; i++) {
107− var indicator_item = row_repeater.itemAt(i);
107+ var indicatorItem = rowRepeater.itemAt(i);
108
108
109+ if (!indicatorItem.visible)
110+ continue;
111+
109− var indicator_position = indicators.mapFromItem(indicator_item,
112+ var indicatorPosition = indicators.mapFromItem(indicatorItem,
110− indicator_item.width/2, indicator_item.height/2)
113+ indicatorItem.width/2, indicatorItem.height/2)
111
114
112 touchFlick(indicators,
115 touchFlick(indicators,
113− indicator_position.x, indicator_position.y,
116+ indicatorPosition.x, indicatorPosition.y,
114− indicator_position.x, indicators.openedHeight * 0.4,
117+ indicatorPosition.x, indicators.openedHeight * 0.4,
115 true /* beginTouch */, false /* endTouch */)
118 true /* beginTouch */, false /* endTouch */)
116
119
117− compare(indicator_row.currentItem, indicator_item,
120+ compare(indicatorRow.currentItem, indicatorItem,
118 "Incorrect item activated at position " + i);
121 "Incorrect item activated at position " + i);
119
122
120 touchFlick(indicators,
123 touchFlick(indicators,
121− indicator_position.x, indicators.openedHeight * 0.4,
124+ indicatorPosition.x, indicators.openedHeight * 0.4,
122− indicator_position.x, indicator_position.y,
125+ indicatorPosition.x, indicatorPosition.y,
123 false /* beginTouch */, true /* endTouch */)
126 false /* beginTouch */, true /* endTouch */)
124
127
125 // wait until fully closed
128 // wait until fully closed
@@ -158,5 +161,21 @@ Item {
158 compare(indicators.fullyOpened, true);
161 compare(indicators.fullyOpened, true);
159 }
162 }
160
163
164+ function test_row_visible_menuContent_visible_data() { return [
165+ {tag: "visible", index: 0, name: "indicator-fake1", visible: true },
166+ {tag: "invisible", index: 1, name: "indicator-fake2", visible: false }]
167+ }
168+
169+ function test_row_visible_menuContent_visible(data) {
170+ var indicatorTabs = findChild(indicators, "tabs");
171+ var rowRepeater = findChild(indicators, "rowRepeater");
172+
173+ var indicatorItem = rowRepeater.itemAt(data.index);
174+ tryCompareFunction(function() { return indicatorItem.width > 0}, true);
175+ tryCompare(indicatorItem, "visible", data.visible);
176+
177+ var indicatorTab = findChild(indicatorTabs, data.name)
178+ tryCompareFunction(function() { return indicatorTab !== undefined }, data.visible);
179+ }
161 }
180 }
162}
181}
tests/qmltests/Panel/tst_MenuContent.qml+9−2
@@ -48,8 +48,15 @@ Item {
48 id: menuContent
48 id: menuContent
49 indicatorsModel: indicatorsModel
49 indicatorsModel: indicatorsModel
50 contentReleaseInterval: 50
50 contentReleaseInterval: 50
51−
52 height: parent.height - 50
51 height: parent.height - 50
52+
53+ visibleIndicators: {
54+ 'indicator-fake1': true,
55+ 'indicator-fake2': true,
56+ 'indicator-fake3': true,
57+ 'indicator-fake4': true,
58+ 'indicator-fake5': true
59+ }
53 }
60 }
54
61
55 Rectangle {
62 Rectangle {
@@ -134,7 +141,7 @@ Item {
134 var menuIndex = i%menuCount;
141 var menuIndex = i%menuCount;
135
142
136 activate_content(menuIndex);
143 activate_content(menuIndex);
137− testTabObjectName = indicatorsModel.data(menuIndex, Indicators.IndicatorsModelRole.Identifier);
144+ testTabObjectName = indicatorsModel.get(menuIndex).identifier;
138 compare(tabs.selectedTabIndex, menuIndex, "Current tab index does not match selected tab index");
145 compare(tabs.selectedTabIndex, menuIndex, "Current tab index does not match selected tab index");
139 tryCompareFunction(selected_tab_equals_test_tab, true);
146 tryCompareFunction(selected_tab_equals_test_tab, true);
140 }
147 }
tests/qmltests/Panel/tst_Panel.qml+3
@@ -159,6 +159,9 @@ Item {
159 var indicatorItem = get_indicator_item(i);
159 var indicatorItem = get_indicator_item(i);
160 verify(indicatorItem != undefined);
160 verify(indicatorItem != undefined);
161
161
162+ if (!indicatorItem.visible)
163+ continue;
164+
162 var indicatorItemCoord = get_indicator_item_position(i);
165 var indicatorItemCoord = get_indicator_item_position(i);
163
166
164 touchPress(panel,
167 touchPress(panel,