on xenial help sign in
Merged with trunk
c619dc8Nick Dedekind
Components/Carousel.qml+33−14
@@ -45,18 +45,24 @@ Item {
45 property int drawBuffer: width / pathItemCount // an "ok" value - but values used from the listView cause loops
45 property int drawBuffer: width / pathItemCount // an "ok" value - but values used from the listView cause loops
46 /// The selected item can be shown in a different size controlled by selectedItemScaleFactor
46 /// The selected item can be shown in a different size controlled by selectedItemScaleFactor
47 property real selectedItemScaleFactor: 1.1
47 property real selectedItemScaleFactor: 1.1
48+ /// The index of the item that should be highlighted
49+ property alias highlightIndex: listView.highlightIndex
50+ /// exposes the delegate of the currentItem
51+ readonly property alias currentItem: listView.currentItem
52+ /// exposes the distance to the next row (only one row in carousel, so it's the topMargins)
53+ readonly property alias verticalSpacing: listView.verticalMargin
48
54
49 /// Emitted when the user clicked on an item
55 /// Emitted when the user clicked on an item
50 /// @param index is the index of the clicked item
56 /// @param index is the index of the clicked item
51− /// @param delegateItem is the clicked component/delegate itself
57+ /// @param model is the model of all the items in the carousel
52 /// @param itemY is y of the clicked delegate
58 /// @param itemY is y of the clicked delegate
53− signal clicked(int index, var delegateItem, real itemY)
59+ signal clicked(int index, var model, real itemY)
54
60
55 /// Emitted when the user pressed and held on an item
61 /// Emitted when the user pressed and held on an item
56 /// @param index is the index of the held item
62 /// @param index is the index of the held item
57− /// @param delegateItem is the held component/delegate itself
63+ /// @param model is the model of all the items in the carousel
58 /// @param itemY is y of the held delegate
64 /// @param itemY is y of the held delegate
59− signal pressAndHold(int index, var delegateItem, real itemY)
65+ signal pressAndHold(int index, var model, real itemY)
60
66
61 implicitHeight: listView.tileHeight * selectedItemScaleFactor
67 implicitHeight: listView.tileHeight * selectedItemScaleFactor
62
68
@@ -72,9 +78,10 @@ Item {
72 id: listView
78 id: listView
73
79
74 property real minimumTileWidth: 0
80 property real minimumTileWidth: 0
75− property real newContentX: -1
81+ property real newContentX: disabledNewContentX
76 property real pathItemCount: referenceWidth / referenceTileWidth
82 property real pathItemCount: referenceWidth / referenceTileWidth
77 property real tileAspectRatio: 1
83 property real tileAspectRatio: 1
84+ property int highlightIndex: -1
78
85
79 /* The positioning and scaling of the items in the carousel is based on the variable
86 /* The positioning and scaling of the items in the carousel is based on the variable
80 'continuousIndex', a continuous real variable between [0, 'carousel.model.count'],
87 'continuousIndex', a continuous real variable between [0, 'carousel.model.count'],
@@ -100,6 +107,7 @@ Item {
100 readonly property real kMiddleIndex: (realWidth / 2) / tileWidth - 0.5
107 readonly property real kMiddleIndex: (realWidth / 2) / tileWidth - 0.5
101 readonly property real kXBeginningEnd: 1 / tileWidth + kMiddleIndex / gapToMiddlePhase
108 readonly property real kXBeginningEnd: 1 / tileWidth + kMiddleIndex / gapToMiddlePhase
102 readonly property real maximumItemTranslation: (listView.tileWidth * 3) / listView.scaleFactor
109 readonly property real maximumItemTranslation: (listView.tileWidth * 3) / listView.scaleFactor
110+ readonly property real disabledNewContentX: -carousel.drawBuffer - 1
103 readonly property real realContentWidth: contentWidth - 2 * carousel.drawBuffer
111 readonly property real realContentWidth: contentWidth - 2 * carousel.drawBuffer
104 readonly property real realContentX: contentX + carousel.drawBuffer
112 readonly property real realContentX: contentX + carousel.drawBuffer
105 readonly property real realPathItemCount: Math.min(realWidth / tileWidth, pathItemCount)
113 readonly property real realPathItemCount: Math.min(realWidth / tileWidth, pathItemCount)
@@ -135,13 +143,14 @@ Item {
135 height: listView.tileHeight
143 height: listView.tileHeight
136 }
144 }
137
145
138− boundsBehavior: Flickable.StopAtBounds
146+ boundsBehavior: Flickable.DragOverBounds
139 cacheBuffer: carousel.cacheBuffer
147 cacheBuffer: carousel.cacheBuffer
140 flickDeceleration: Math.max(1500 * Math.pow(realWidth / referenceWidth, 1.5), 1500) // 1500 is platform default
148 flickDeceleration: Math.max(1500 * Math.pow(realWidth / referenceWidth, 1.5), 1500) // 1500 is platform default
141 maximumFlickVelocity: Math.max(2500 * Math.pow(realWidth / referenceWidth, 1.5), 2500) // 2500 is platform default
149 maximumFlickVelocity: Math.max(2500 * Math.pow(realWidth / referenceWidth, 1.5), 2500) // 2500 is platform default
142 orientation: ListView.Horizontal
150 orientation: ListView.Horizontal
143
151
144 function itemClicked(index, delegateItem) {
152 function itemClicked(index, delegateItem) {
153+ listView.currentIndex = index
145 var x = CarouselJS.getXFromContinuousIndex(index,
154 var x = CarouselJS.getXFromContinuousIndex(index,
146 realWidth,
155 realWidth,
147 realContentWidth,
156 realContentWidth,
@@ -150,11 +159,11 @@ Item {
150 gapToEndPhase,
159 gapToEndPhase,
151 carousel.drawBuffer)
160 carousel.drawBuffer)
152
161
153− if (Math.abs(x - contentX) < 1) {
162+ if (Math.abs(x - contentX) < 1 && delegateItem !== undefined) {
154 /* We're clicking the selected item and
163 /* We're clicking the selected item and
155 we're in the neighbourhood of radius 1 pixel from it.
164 we're in the neighbourhood of radius 1 pixel from it.
156 Let's emit the clicked signal. */
165 Let's emit the clicked signal. */
157− carousel.clicked(index, delegateItem, delegateItem.y)
166+ carousel.clicked(index, listView.model, delegateItem.y)
158 return
167 return
159 }
168 }
160
169
@@ -174,11 +183,11 @@ Item {
174 gapToEndPhase,
183 gapToEndPhase,
175 carousel.drawBuffer);
184 carousel.drawBuffer);
176
185
177− if (Math.abs(x - contentX) < 1) {
186+ if (Math.abs(x - contentX) < 1 && delegateItem !== undefined) {
178 /* We're pressAndHold the selected item and
187 /* We're pressAndHold the selected item and
179 we're in the neighbourhood of radius 1 pixel from it.
188 we're in the neighbourhood of radius 1 pixel from it.
180 Let's emit the pressAndHold signal. */
189 Let's emit the pressAndHold signal. */
181− carousel.pressAndHold(index, delegateItem, delegateItem.y);
190+ carousel.pressAndHold(index, listView.model, delegateItem.y);
182 return;
191 return;
183 }
192 }
184
193
@@ -189,10 +198,16 @@ Item {
189 newContentXAnimation.start();
198 newContentXAnimation.start();
190 }
199 }
191
200
201+ onHighlightIndexChanged: {
202+ if (highlightIndex != -1) {
203+ itemClicked(highlightIndex)
204+ }
205+ }
206+
192 onMovementStarted: {
207 onMovementStarted: {
193 stepAnimation.stop()
208 stepAnimation.stop()
194 newContentXAnimation.stop()
209 newContentXAnimation.stop()
195− newContentX = -1
210+ newContentX = disabledNewContentX
196 }
211 }
197 onMovementEnded: {
212 onMovementEnded: {
198 if (realContentX > 0 && realContentX < realContentWidth - realWidth)
213 if (realContentX > 0 && realContentX < realContentWidth - realWidth)
@@ -228,7 +243,7 @@ Item {
228 easing.type: Easing.InOutQuad
243 easing.type: Easing.InOutQuad
229 }
244 }
230 ScriptAction {
245 ScriptAction {
231− script: listView.newContentX = -1
246+ script: listView.newContentX = listView.disabledNewContentX
232 }
247 }
233 }
248 }
234
249
@@ -253,7 +268,7 @@ Item {
253 readonly property bool explicitScale: (!listView.moving ||
268 readonly property bool explicitScale: (!listView.moving ||
254 listView.realContentX <= 0 ||
269 listView.realContentX <= 0 ||
255 listView.realContentX >= listView.realContentWidth - listView.realWidth) &&
270 listView.realContentX >= listView.realContentWidth - listView.realWidth) &&
256− listView.newContentX < 0 &&
271+ listView.newContentX === listView.disabledNewContentX &&
257 index === listView.selectedIndex
272 index === listView.selectedIndex
258 readonly property real cachedTiles: listView.realPathItemCount + carousel.drawBuffer / listView.tileWidth
273 readonly property real cachedTiles: listView.realPathItemCount + carousel.drawBuffer / listView.tileWidth
259 readonly property real distance: listView.continuousIndex - index
274 readonly property real distance: listView.continuousIndex - index
@@ -272,14 +287,18 @@ Item {
272 itemTranslationScale,
287 itemTranslationScale,
273 listView.maximumItemTranslation)
288 listView.maximumItemTranslation)
274
289
290+ readonly property real xTransform: listView.viewTranslation + translationX * listView.scaleFactor
291+ readonly property real center: x - listView.contentX + xTransform - drawBuffer + (width/2)
292+
275 width: listView.tileWidth
293 width: listView.tileWidth
276 height: listView.tileHeight
294 height: listView.tileHeight
277 scale: itemScale * explicitScaleFactor
295 scale: itemScale * explicitScaleFactor
278 sourceComponent: itemComponent
296 sourceComponent: itemComponent
279 z: cachedTiles - Math.abs(index - listView.selectedIndex)
297 z: cachedTiles - Math.abs(index - listView.selectedIndex)
298+ opacity: highlightIndex == -1 ? 1 : (highlightIndex == index ? 0.6 : 0.2)
280
299
281 transform: Translate {
300 transform: Translate {
282− x: listView.viewTranslation + translationX * listView.scaleFactor
301+ x: xTransform
283 }
302 }
284
303
285 Behavior on explicitScaleFactor {
304 Behavior on explicitScaleFactor {
Components/FilterGrid.qml+3
@@ -54,6 +54,7 @@ Item {
54 property alias delegateWidth: iconTileGrid.delegateWidth
54 property alias delegateWidth: iconTileGrid.delegateWidth
55 property alias delegateHeight: iconTileGrid.delegateHeight
55 property alias delegateHeight: iconTileGrid.delegateHeight
56 property alias verticalSpacing: iconTileGrid.verticalSpacing
56 property alias verticalSpacing: iconTileGrid.verticalSpacing
57+ readonly property alias margins: iconTileGrid.margins
57 property alias delegate: iconTileGrid.delegate
58 property alias delegate: iconTileGrid.delegate
58 property alias cellWidth: iconTileGrid.cellWidth
59 property alias cellWidth: iconTileGrid.cellWidth
59 property alias cellHeight: iconTileGrid.cellHeight
60 property alias cellHeight: iconTileGrid.cellHeight
@@ -62,6 +63,8 @@ Item {
62 readonly property alias flicking: iconTileGrid.flicking
63 readonly property alias flicking: iconTileGrid.flicking
63 readonly property alias moving: iconTileGrid.moving
64 readonly property alias moving: iconTileGrid.moving
64 readonly property alias pressDelay: iconTileGrid.pressDelay
65 readonly property alias pressDelay: iconTileGrid.pressDelay
66+ property alias highlightIndex: iconTileGrid.highlightIndex
67+ readonly property alias currentItem: iconTileGrid.currentItem
65
68
66 height: !filterAnimation.running ? childrenRect.height : height
69 height: !filterAnimation.running ? childrenRect.height : height
67 clip: filterAnimation.running
70 clip: filterAnimation.running
Components/LazyImage.qml+2−2
@@ -100,10 +100,10 @@ Item {
100 verticalAlignment: Image.AlignVCenter
100 verticalAlignment: Image.AlignVCenter
101 sourceSize.width: root.scaleTo == "width" ? root.width
101 sourceSize.width: root.scaleTo == "width" ? root.width
102 : root.scaleTo == "fit" && root.width < root.height ? root.width
102 : root.scaleTo == "fit" && root.width < root.height ? root.width
103− : undefined
103+ : 0
104 sourceSize.height: root.scaleTo == "height" ? root.height
104 sourceSize.height: root.scaleTo == "height" ? root.height
105 : root.scale == "fit" && root.height < root.width ? root.height
105 : root.scale == "fit" && root.height < root.width ? root.height
106− : undefined
106+ : 0
107 }
107 }
108 }
108 }
109
109
Components/PageHeader.qml+4
@@ -48,6 +48,10 @@ Item {
48 searchField.focus = false;
48 searchField.focus = false;
49 }
49 }
50
50
51+ function unfocus() {
52+ searchField.focus = false;
53+ }
54+
51 Connections {
55 Connections {
52 target: greeter
56 target: greeter
53 onShownChanged: if (shown) resetSearch()
57 onShownChanged: if (shown) resetSearch()
Components/ResponsiveGridView.qml+10
@@ -26,6 +26,7 @@ Item {
26 property int maximumNumberOfColumns: 6
26 property int maximumNumberOfColumns: 6
27 readonly property int columns: gridView.columns
27 readonly property int columns: gridView.columns
28 property alias verticalSpacing: gridView.verticalSpacing
28 property alias verticalSpacing: gridView.verticalSpacing
29+ readonly property alias margins: gridView.margin
29 property int delegateWidth
30 property int delegateWidth
30 property int delegateHeight
31 property int delegateHeight
31 property alias model: gridView.model
32 property alias model: gridView.model
@@ -41,6 +42,8 @@ Item {
41 readonly property alias pressDelay: gridView.pressDelay
42 readonly property alias pressDelay: gridView.pressDelay
42 property alias delegateCreationBegin: gridView.delegateCreationBegin
43 property alias delegateCreationBegin: gridView.delegateCreationBegin
43 property alias delegateCreationEnd: gridView.delegateCreationEnd
44 property alias delegateCreationEnd: gridView.delegateCreationEnd
45+ property alias highlightIndex: gridView.highlightIndex
46+ readonly property alias currentItem: gridView.currentItem
44
47
45 function contentHeightForRows(rows) {
48 function contentHeightForRows(rows) {
46 return rows * cellHeight + verticalSpacing
49 return rows * cellHeight + verticalSpacing
@@ -77,8 +80,15 @@ Item {
77 property real horizontalSpacing: spacingForColumns(columns)
80 property real horizontalSpacing: spacingForColumns(columns)
78 property real verticalSpacing: horizontalSpacing
81 property real verticalSpacing: horizontalSpacing
79 property int margin: allocatableHorizontalSpace - columns * horizontalSpacing
82 property int margin: allocatableHorizontalSpace - columns * horizontalSpacing
83+ property int highlightIndex: -1
80
84
81 cellWidth: delegateWidth + horizontalSpacing
85 cellWidth: delegateWidth + horizontalSpacing
82 cellHeight: delegateHeight + verticalSpacing
86 cellHeight: delegateHeight + verticalSpacing
87+
88+ onHighlightIndexChanged: {
89+ if (highlightIndex != -1) {
90+ currentIndex = highlightIndex
91+ }
92+ }
83 }
93 }
84}
94}
Components/Tile.qml+9
@@ -26,5 +26,14 @@ AbstractButton {
26 property int imageHeight
26 property int imageHeight
27 property int maximumLineCount: 1
27 property int maximumLineCount: 1
28
28
29+ readonly property int center: (index % GridView.view.columns * width) + (width / 2)
30+
31+ opacity: GridView.view.highlightIndex === -1 ? 1 :
32+ GridView.view.highlightIndex === index ? 0.6 : 0.2
33+
34+ Behavior on opacity {
35+ UbuntuNumberAnimation {}
36+ }
37+
29 style: TileStyle {}
38 style: TileStyle {}
30}
39}
Components/Time.js+9−9
@@ -16,15 +16,15 @@
16
16
17.pragma library
17.pragma library
18
18
19−var factors = {
19+var factors = [
20− "second": [ 1000, "a second ago", "in a second", "%1 seconds ago", "in %1 seconds" ],
20+ [ 1000, "a second ago", "in a second", "%1 seconds ago", "in %1 seconds" ],
21− "minute": [ 60, "a minute ago", "in a minute", "%1 minutes ago", "in %1 minutes" ],
21+ [ 60, "a minute ago", "in a minute", "%1 minutes ago", "in %1 minutes" ],
22− "hour": [ 60, "an hour ago", "in an hour", "%1 hours ago", "in %1 hours" ],
22+ [ 60, "an hour ago", "in an hour", "%1 hours ago", "in %1 hours" ],
23− "day": [ 24, "yesterday", "tomorrow", "%1 days ago", "in %1 days" ],
23+ [ 24, "yesterday", "tomorrow", "%1 days ago", "in %1 days" ],
24− "week": [ 7, "a week ago", "in a week", "about %1 weeks ago", "in about %1 weeks" ],
24+ [ 7, "a week ago", "in a week", "about %1 weeks ago", "in about %1 weeks" ],
25− "month": [ 4.35, "a month ago", "in a month", "%1 months ago", "in %1 months" ], // == 365.25 days / 12 months / 7 days
25+ [ 4.35, "a month ago", "in a month", "%1 months ago", "in %1 months" ], // == 365.25 days / 12 months / 7 days
26− "year": [ 12, "a year ago", "in a year", "%1 years ago", "in %1 years" ]
26+ [ 12, "a year ago", "in a year", "%1 years ago", "in %1 years" ]
27−}
27+]
28
28
29function readableFromNow(date, now) {
29function readableFromNow(date, now) {
30 var then = new Date(date);
30 var then = new Date(date);
Components/UbuntuShapeForItem.qml+1
@@ -31,6 +31,7 @@ Item {
31
31
32 ShaderEffectSource {
32 ShaderEffectSource {
33 id: source
33 id: source
34+ anchors.centerIn: parent // Placed under shape, so it's hidden
34 width: 1
35 width: 1
35 height: 1
36 height: 1
36 hideSource: true
37 hideSource: true
Dash/Apps/AppPreview.qml+9
@@ -44,6 +44,15 @@ GenericPreview {
44 model: previewData.infoMap["more-screenshots"] != null ? previewData.infoMap["more-screenshots"].value : [previewData.image]
44 model: previewData.infoMap["more-screenshots"] != null ? previewData.infoMap["more-screenshots"].value : [previewData.image]
45 cacheBuffer: width * 3
45 cacheBuffer: width * 3
46
46
47+ // FIXME: Because of ListViews inside ListViews inside Flickables inside ListViews (and some more)
48+ // we finally reached the point where this ListView doesn't correctly get swipe input any more but
49+ // instead the parent ListView is the one that is swiped. This MouseArea sort of creates a blocking
50+ // layer to make sure this ListView can be swiped, regardless of what's behind it.
51+ MouseArea {
52+ anchors.fill: parent
53+ enabled: parent.contentWidth > parent.width
54+ }
55+
47 LazyImage {
56 LazyImage {
48 objectName: "placeholderScreenshot"
57 objectName: "placeholderScreenshot"
49 anchors {
58 anchors {
Dash/Apps/ApplicationsFilterGrid.qml+14−5
@@ -19,7 +19,7 @@ import "../../Components"
19import "../../Applications"
19import "../../Applications"
20
20
21FilterGrid {
21FilterGrid {
22− id: filterGrid
22+ id: filtergrid
23
23
24 filter: false
24 filter: false
25 minimumHorizontalSpacing: units.gu(0.5)
25 minimumHorizontalSpacing: units.gu(0.5)
@@ -28,9 +28,11 @@ FilterGrid {
28 delegateHeight: units.gu(9.5)
28 delegateHeight: units.gu(9.5)
29 verticalSpacing: units.gu(2)
29 verticalSpacing: units.gu(2)
30
30
31− signal clicked(int index, variant data)
31+ signal clicked(int index, var model, real itemY)
32+ signal pressAndHold(int index, var model, real itemY)
32
33
33 delegate: Tile {
34 delegate: Tile {
35+ id: tile
34 objectName: "delegate" + index
36 objectName: "delegate" + index
35 Application {
37 Application {
36 id: application
38 id: application
@@ -44,12 +46,19 @@ FilterGrid {
44
46
45 property string icon: model.icon ? model.icon : "../../graphics/applicationIcons/" + application.icon + ".png" // FIXME: this is temporary
47 property string icon: model.icon ? model.icon : "../../graphics/applicationIcons/" + application.icon + ".png" // FIXME: this is temporary
46
48
47− width: filterGrid.cellWidth
49+ width: filtergrid.cellWidth
48− height: filterGrid.cellHeight
50+ height: filtergrid.cellHeight
49 text: model.title ? model.title : application.name // FIXME: this is temporary
51 text: model.title ? model.title : application.name // FIXME: this is temporary
50 imageWidth: units.gu(8)
52 imageWidth: units.gu(8)
51 imageHeight: units.gu(7.5)
53 imageHeight: units.gu(7.5)
52 source: icon
54 source: icon
53− onClicked: filterGrid.clicked(index, application.desktopFile);
55+
56+ onClicked: {
57+ filtergrid.clicked(index, filtergrid.model, tile.y)
58+ }
59+
60+ onPressAndHold: {
61+ filtergrid.pressAndHold(index, filtergrid.model, tile.y)
62+ }
54 }
63 }
55}
64}
Dash/Apps/DashPluginFilterGrid.qml+2−4
@@ -37,13 +37,11 @@ GenericFilterGrid {
37 }
37 }
38
38
39 onClicked: {
39 onClicked: {
40− var data = { model: model }
41− filtergrid.clicked(index, data, tile.y)
40+ filtergrid.clicked(index, filtergrid.model, tile.y)
42 }
41 }
43
42
44 onPressAndHold: {
43 onPressAndHold: {
45− var data = { model: model }
46− filtergrid.pressAndHold(index, data, tile.y)
44+ filtergrid.pressAndHold(index, filtergrid.model, tile.y)
47 }
45 }
48 }
46 }
49}
47}
Dash/DashBar.qml+2−2
@@ -35,7 +35,7 @@ Item {
35
35
36 function startNavigation() {
36 function startNavigation() {
37 timeout.stop()
37 timeout.stop()
38− panel.opened = true
38+ panel.open()
39 }
39 }
40
40
41 function stopNavigation() {
41 function stopNavigation() {
@@ -43,7 +43,7 @@ Item {
43 }
43 }
44
44
45 function finishNavigation() {
45 function finishNavigation() {
46− panel.opened = false
46+ panel.close()
47 }
47 }
48
48
49 Timer {
49 Timer {
Dash/DashContent.qml+2−2
@@ -116,8 +116,8 @@ Item {
116 source: scopeMapper.map(scope.id)
116 source: scopeMapper.map(scope.id)
117 objectName: scope.id + " loader"
117 objectName: scope.id + " loader"
118
118
119− readonly property bool previewShown: item.previewShown
119+ readonly property bool previewShown: item ? item.previewShown : false
120− readonly property bool moving: item.moving
120+ readonly property bool moving: item ? item.moving : false
121
121
122 // these are needed for autopilot tests
122 // these are needed for autopilot tests
123 readonly property string scopeId: scope.id
123 readonly property string scopeId: scope.id
Dash/DashPreview.qml+9−3
@@ -17,11 +17,12 @@
17import QtQuick 2.0
17import QtQuick 2.0
18import Ubuntu.Components 0.1
18import Ubuntu.Components 0.1
19
19
20−Rectangle {
20+Item {
21 id: root
21 id: root
22
22
23 property int keyboardSize: Qt.inputMethod.visible ? Qt.inputMethod.keyboardRectangle.height : 0
23 property int keyboardSize: Qt.inputMethod.visible ? Qt.inputMethod.keyboardRectangle.height : 0
24 property var previewData
24 property var previewData
25+ property alias showProcessingAction: waitingForActionMouseArea.enabled
25
26
26 property real previewWidthRatio: 0.5
27 property real previewWidthRatio: 0.5
27
28
@@ -35,7 +36,6 @@ Rectangle {
35 readonly property int columnWidth: narrowMode ? contentRow.width : (contentRow.width / 3) - contentRow.spacing
36 readonly property int columnWidth: narrowMode ? contentRow.width : (contentRow.width / 3) - contentRow.spacing
36 readonly property int contentSpacing: units.gu(3)
37 readonly property int contentSpacing: units.gu(3)
37
38
38− color: Qt.rgba(0, 0, 0, .3)
39 clip: true
39 clip: true
40
40
41 function ensureVisible(item) {
41 function ensureVisible(item) {
@@ -60,7 +60,6 @@ Rectangle {
60 anchors.fill: parent
60 anchors.fill: parent
61 }
61 }
62
62
63−
64 Row {
63 Row {
65 id: contentRow
64 id: contentRow
66 anchors {
65 anchors {
@@ -196,4 +195,11 @@ Rectangle {
196 }
195 }
197 sourceComponent: root.header
196 sourceComponent: root.header
198 }
197 }
198+
199+ MouseArea {
200+ id: waitingForActionMouseArea
201+ objectName: "waitingForActionMouseArea"
202+ anchors.fill: parent
203+ enabled: false
204+ }
199}
205}
Launcher/QuicklistForegroundStyle.qml → Dash/DashPreviewPlaceholder.qmlrenamed+7−5
@@ -14,10 +14,12 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
15 */
16
16
17+import QtQuick 2.0
17−import Ubuntu.Components.Themes.Ambiance 0.1
18+import Ubuntu.Components 0.1
18
19
19−PopoverForegroundStyle {
20+DashPreview {
20− bubbleColor: "#221e1c"
21+ ActivityIndicator {
21− bubbleOpacity: 0.9
22+ anchors.centerIn: parent
22− arrowSource: "graphics/quicklist_tooltip.png"
23+ running: true
24+ }
23}
25}
Dash/Generic/GenericFilterGrid.qml+4−6
@@ -28,8 +28,8 @@ FilterGrid {
28 property int iconWidth: units.gu(8)
28 property int iconWidth: units.gu(8)
29 property int iconHeight: units.gu(7.5)
29 property int iconHeight: units.gu(7.5)
30
30
31− signal clicked(int index, var delegateItem, real itemY)
31+ signal clicked(int index, var model, real itemY)
32− signal pressAndHold(int index, var delegateItem, real itemY)
32+ signal pressAndHold(int index, var model, real itemY)
33
33
34 delegate: Tile {
34 delegate: Tile {
35 id: tile
35 id: tile
@@ -42,13 +42,11 @@ FilterGrid {
42 source: model.icon
42 source: model.icon
43
43
44 onClicked: {
44 onClicked: {
45− var data = { model: model }
46− filtergrid.clicked(index, data, tile.y)
45+ filtergrid.clicked(index, filtergrid.model, tile.y)
47 }
46 }
48
47
49 onPressAndHold: {
48 onPressAndHold: {
50− var data = { model: model }
51− filtergrid.pressAndHold(index, data, tile.y)
49+ filtergrid.pressAndHold(index, filtergrid.model, tile.y)
52 }
50 }
53 }
51 }
54}
52}
Dash/Generic/GenericPreview.qml+4−1
@@ -71,7 +71,10 @@ DashPreview {
71 text: modelData.displayName
71 text: modelData.displayName
72 iconSource: modelData.iconHint
72 iconSource: modelData.iconHint
73 iconPosition: "right"
73 iconPosition: "right"
74+ onClicked: {
74− onClicked: previewData.execute(modelData.id, { })
75+ previewData.execute(modelData.id, { })
76+ genericPreview.showProcessingAction = true;
77+ }
75 }
78 }
76 }
79 }
77 }
80 }
Dash/Generic/WeatherFilterGrid.qml+2−4
@@ -37,13 +37,11 @@ GenericFilterGrid {
37 style: FlatTileStyle {}
37 style: FlatTileStyle {}
38
38
39 onClicked: {
39 onClicked: {
40− var data = { model: model }
41− filtergrid.clicked(index, data, tile.y)
40+ filtergrid.clicked(index, filtergrid.model, tile.y)
42 }
41 }
43
42
44 onPressAndHold: {
43 onPressAndHold: {
45− var data = { model: model }
46− filtergrid.pressAndHold(index, data, tile.y)
44+ filtergrid.pressAndHold(index, filtergrid.model, tile.y)
47 }
45 }
48 }
46 }
49}
47}
Dash/GenericScopeView.qml+223−55
@@ -21,14 +21,14 @@ import "../Components/ListItems" as ListItems
21
21
22ScopeView {
22ScopeView {
23 id: scopeView
23 id: scopeView
24− readonly property alias previewShown: previewLoader.onScreen
24+ readonly property alias previewShown: previewListView.onScreen
25 property bool enableHeightBehaviorOnNextCreation: false
25 property bool enableHeightBehaviorOnNextCreation: false
26
26
27 moving: categoryView.moving
27 moving: categoryView.moving
28
28
29 onIsCurrentChanged: {
29 onIsCurrentChanged: {
30 pageHeader.resetSearch();
30 pageHeader.resetSearch();
31− previewLoader.open = false;
31+ previewListView.open = false;
32 }
32 }
33
33
34 onMovementStarted: categoryView.showHeader()
34 onMovementStarted: categoryView.showHeader()
@@ -57,8 +57,8 @@ ScopeView {
57
57
58 Connections {
58 Connections {
59 target: scopeView.scope
59 target: scopeView.scope
60− onShowDash: previewLoader.open = false;
60+ onShowDash: previewListView.open = false;
61− onHideDash: previewLoader.open = false;
61+ onHideDash: previewListView.open = false;
62 }
62 }
63
63
64 ScopeListView {
64 ScopeListView {
@@ -66,12 +66,18 @@ ScopeView {
66 objectName: "categoryListView"
66 objectName: "categoryListView"
67 anchors.fill: parent
67 anchors.fill: parent
68 model: scopeView.categories
68 model: scopeView.categories
69− forceNoClip: previewLoader.onScreen
69+ forceNoClip: previewListView.onScreen
70
70
71 onAtYEndChanged: if (atYEnd) endReached()
71 onAtYEndChanged: if (atYEnd) endReached()
72 onMovingChanged: if (moving && atYEnd) endReached()
72 onMovingChanged: if (moving && atYEnd) endReached()
73
73
74 property string expandedCategoryId: ""
74 property string expandedCategoryId: ""
75+ signal correctExpandedCategory();
76+
77+ Behavior on contentY {
78+ enabled: previewListView.open
79+ UbuntuNumberAnimation {}
80+ }
75
81
76 delegate: ListItems.Base {
82 delegate: ListItems.Base {
77 id: baseItem
83 id: baseItem
@@ -123,33 +129,50 @@ ScopeView {
123 Connections {
129 Connections {
124 target: rendererLoader.item
130 target: rendererLoader.item
125 onClicked: {
131 onClicked: {
132+ // Prepare the preview in case activate() triggers a preview only
126 effect.positionPx = mapToItem(categoryView, 0, itemY).y
133 effect.positionPx = mapToItem(categoryView, 0, itemY).y
134+ previewListView.categoryId = categoryId
135+ previewListView.categoryDelegate = rendererLoader.item
136+ previewListView.model = model;
137+ previewListView.init = true;
138+ previewListView.currentIndex = index;
139+
140+ var item = model.get(index);
141+
142+ if ((scopeView.scope.id == "applications.scope" && categoryId == "installed")
143+ || (scopeView.scope.id == "home.scope" && categoryId == "applications.scope")) {
127− scopeView.scope.activate(delegateItem.model.uri,
144+ scopeView.scope.activate(item.uri, item.icon, item.category, 0, item.mimetype, item.title,
128− delegateItem.model.icon,
145+ item.comment, item.dndUri, item.metadata)
129− delegateItem.model.category,
146+ } else {
130− 0,
147+ previewListView.open = true
131− delegateItem.model.mimetype,
148+
132− delegateItem.model.title,
149+ scopeView.scope.preview( item.uri, item.icon, item.category, 0, item.mimetype, item.title,
133− delegateItem.model.comment,
150+ item.comment, item.dndUri, item.metadata)
134− delegateItem.model.dndUri,
151+ }
135− delegateItem.model.metadata)
136 }
152 }
137 onPressAndHold: {
153 onPressAndHold: {
138 effect.positionPx = mapToItem(categoryView, 0, itemY).y
154 effect.positionPx = mapToItem(categoryView, 0, itemY).y
155+ previewListView.categoryId = categoryId
156+ previewListView.categoryDelegate = rendererLoader.item
157+ previewListView.model = model;
158+ previewListView.init = true;
159+ previewListView.currentIndex = index;
160+ previewListView.open = true
161+
162+ var item = model.get(index)
139− scopeView.scope.preview( delegateItem.model.uri,
163+ scopeView.scope.preview( item.uri, item.icon, item.category, 0, item.mimetype, item.title,
140− delegateItem.model.icon,
164+ item.comment, item.dndUri, item.metadata)
141− delegateItem.model.category,
142− 0,
143− delegateItem.model.mimetype,
144− delegateItem.model.title,
145− delegateItem.model.comment,
146− delegateItem.model.dndUri,
147− delegateItem.model.metadata)
148 }
165 }
149 }
166 }
150 Connections {
167 Connections {
151 target: categoryView
168 target: categoryView
152 onExpandedCategoryIdChanged: {
169 onExpandedCategoryIdChanged: {
170+ collapseAllButExpandedCategory();
171+ }
172+ onCorrectExpandedCategory: {
173+ collapseAllButExpandedCategory();
174+ }
175+ function collapseAllButExpandedCategory() {
153 var item = rendererLoader.item;
176 var item = rendererLoader.item;
154 if (item.expandable) {
177 if (item.expandable) {
155 var shouldFilter = categoryId != categoryView.expandedCategoryId;
178 var shouldFilter = categoryId != categoryView.expandedCategoryId;
@@ -157,13 +180,15 @@ ScopeView {
157 // If the filter animation will be seen start it, otherwise, just flip the switch
180 // If the filter animation will be seen start it, otherwise, just flip the switch
158 var shrinkingVisible = shouldFilter && y + item.collapsedHeight < categoryView.height;
181 var shrinkingVisible = shouldFilter && y + item.collapsedHeight < categoryView.height;
159 var growingVisible = !shouldFilter && y + height < categoryView.height;
182 var growingVisible = !shouldFilter && y + height < categoryView.height;
183+ if (!previewListView.open || !shouldFilter) {
160− if (shrinkingVisible || growingVisible) {
184+ if (shrinkingVisible || growingVisible) {
161− item.startFilterAnimation(shouldFilter)
185+ item.startFilterAnimation(shouldFilter)
162− } else {
186+ } else {
163− item.filter = shouldFilter;
187+ item.filter = shouldFilter;
164− }
188+ }
165− if (!shouldFilter) {
189+ if (!shouldFilter && !previewListView.open) {
166− categoryView.maximizeVisibleArea(index, item.uncollapsedHeight);
190+ categoryView.maximizeVisibleArea(index, item.uncollapsedHeight);
191+ }
167 }
192 }
168 }
193 }
169 }
194 }
@@ -269,6 +294,7 @@ ScopeView {
269
294
270 OpenEffect {
295 OpenEffect {
271 id: effect
296 id: effect
297+ objectName: "openEffect"
272 anchors {
298 anchors {
273 fill: parent
299 fill: parent
274 bottomMargin: -bottomOverflow
300 bottomMargin: -bottomOverflow
@@ -281,29 +307,44 @@ ScopeView {
281 topOpacity: (1 - gap * 1.2)
307 topOpacity: (1 - gap * 1.2)
282 bottomGapPx: positionPx + gap * (targetBottomGapPx - positionPx)
308 bottomGapPx: positionPx + gap * (targetBottomGapPx - positionPx)
283 bottomOverflow: units.gu(20)
309 bottomOverflow: units.gu(20)
284− bottomOpacity: 1 - (gap * 0.8)
310+ live: !expansionAnimation.running
285
311
286 property int targetBottomGapPx: height - units.gu(8) - bottomOverflow
312 property int targetBottomGapPx: height - units.gu(8) - bottomOverflow
287− property real gap: previewLoader.open ? 1.0 : 0.0
313+ property real gap: previewListView.open ? 1.0 : 0.0
288
314
289 Behavior on gap {
315 Behavior on gap {
290 NumberAnimation {
316 NumberAnimation {
317+ id: expansionAnimation
291 duration: 200
318 duration: 200
292 easing.type: Easing.InOutQuad
319 easing.type: Easing.InOutQuad
293 onRunningChanged: {
320 onRunningChanged: {
294− if (!previewLoader.open && !running) {
321+ if (!previewListView.open && !running) {
295− previewLoader.onScreen = false
322+ previewListView.onScreen = false
296 }
323 }
297 }
324 }
298 }
325 }
299 }
326 }
327+ Behavior on positionPx {
328+ enabled: previewListView.open
329+ UbuntuNumberAnimation {}
330+ }
300 }
331 }
301
332
302 Connections {
333 Connections {
303 target: scopeView.scope
334 target: scopeView.scope
304 onPreviewReady: {
335 onPreviewReady: {
336+ if (previewListView.init) {
337+ // Preview was triggered because of a click on the item. Need to expand now.
338+ if (!previewListView.open) {
339+ previewListView.open = true
340+ }
341+
342+ var index = previewListView.currentIndex
343+ previewListView.currentIndex = -1
344+ previewListView.currentIndex = index
345+ previewListView.init = false
346+ }
305− previewLoader.previewData = preview
347+ previewListView.currentItem.previewData = preview
306− previewLoader.open = true
307 }
348 }
308 }
349 }
309
350
@@ -311,18 +352,9 @@ ScopeView {
311 id: previewDelegateMapper
352 id: previewDelegateMapper
312 }
353 }
313
354
314− Connections {
355+ ListView {
315− ignoreUnknownSignals: true
356+ id: previewListView
316− target: previewLoader.valid ? previewLoader.item : null
357+ objectName: "previewListView"
317− onClose: {
318− previewLoader.open = false
319− }
320− }
321−
322− Loader {
323− objectName: "previewLoader"
324− id: previewLoader
325− property var previewData
326 height: effect.bottomGapPx - effect.topGapPx
358 height: effect.bottomGapPx - effect.topGapPx
327 anchors {
359 anchors {
328 top: parent.top
360 top: parent.top
@@ -330,32 +362,168 @@ ScopeView {
330 left: parent.left
362 left: parent.left
331 right: parent.right
363 right: parent.right
332 }
364 }
333− source: onScreen ? previewDelegateMapper.map(previewLoader.previewData.rendererName) : ""
365+ orientation: ListView.Horizontal
366+ highlightRangeMode: ListView.StrictlyEnforceRange
367+ snapMode: ListView.SnapOneItem
368+ boundsBehavior: Flickable.DragAndOvershootBounds
369+ highlightMoveDuration: 250
370+ flickDeceleration: units.gu(625)
371+ maximumFlickVelocity: width * 5
372+ cacheBuffer: 0
373+
374+ // To be set before opening the preview
375+ property string categoryId: ""
376+ property var categoryDelegate
377+
378+ // because the ListView is built asynchronous, setting the
379+ // currentIndex directly won't work. We need to refresh it
380+ // when the first preview is ready to be displayed.
381+ property bool init: true
382+
383+ onCurrentIndexChanged: {
384+ var row = Math.floor(currentIndex / categoryDelegate.columns);
385+ if (categoryDelegate.collapsedRowCount <= row) {
386+ categoryView.expandedCategoryId = categoryId
387+ }
388+
389+ if (open) {
390+ categoryDelegate.highlightIndex = currentIndex
391+ }
392+
393+ if (!init && model !== undefined) {
394+ var item = model.get(currentIndex)
395+ scopeView.scope.preview( item.uri, item.icon, item.category, 0, item.mimetype, item.title, item.comment, item.dndUri, item.metadata)
396+ }
397+
398+ var itemY = categoryView.contentItem.mapFromItem(categoryDelegate.currentItem).y;
399+ var newContentY = itemY - effect.positionPx - categoryDelegate.verticalSpacing;
400+ var effectAdjust = effect.positionPx;
401+ if (newContentY < 0) {
402+ effectAdjust += newContentY;
403+ newContentY = 0;
404+ }
405+ if (newContentY > Math.max(0, categoryView.contentHeight - categoryView.height)) {
406+ effectAdjust += -(categoryView.contentHeight - categoryView.height) + newContentY
407+ newContentY = categoryView.contentHeight - categoryView.height;
408+ }
409+
410+ effect.positionPx = effectAdjust;
411+ categoryView.contentY = newContentY - categoryView.originY;
412+ }
334
413
335 property bool open: false
414 property bool open: false
336 property bool onScreen: false
415 property bool onScreen: false
337− property bool valid: item !== null
338
416
339 onOpenChanged: {
417 onOpenChanged: {
340 if (open) {
418 if (open) {
341− onScreen = true
419+ onScreen = true;
420+ categoryDelegate.highlightIndex = currentIndex;
421+ pageHeader.unfocus();
422+ } else {
423+ // Cancel any pending preview requests or actions
424+ if (previewListView.currentItem.previewData !== undefined) {
425+ previewListView.currentItem.previewData.cancelAction();
426+ }
427+ scopeView.scope.cancelActivation();
428+ model = undefined;
429+ categoryView.correctExpandedCategory();
430+ categoryDelegate.highlightIndex = -1;
342 }
431 }
343 }
432 }
344
433
345− onLoaded: {
434+ Rectangle {
346− item.previewData = Qt.binding(function() { return previewLoader.previewData })
435+ anchors.fill: parent
436+ color: Qt.rgba(0, 0, 0, .3)
437+ z: -1
347 }
438 }
439+
440+ delegate: Loader {
441+ id: previewLoader
442+ objectName: "previewLoader" + index
443+ height: previewListView.height
444+ width: previewListView.width
445+ asynchronous: true
446+ source: previewListView.onScreen ?
447+ (previewData !== undefined ? previewDelegateMapper.map(previewData.rendererName) : "DashPreviewPlaceholder.qml") : ""
448+
449+ onPreviewDataChanged: {
450+ if (previewData !== undefined && source.toString().indexOf("DashPreviewPlaceholder.qml") != -1) {
451+ previewLoader.opacity = 0;
452+ }
453+ }
454+
455+ onSourceChanged: {
456+ if (previewData !== undefined) {
457+ fadeIn.start()
458+ }
459+ }
460+
461+ PropertyAnimation {
462+ id: fadeIn
463+ target: previewLoader
464+ property: "opacity"
465+ from: 0.0
466+ to: 1.0
467+ duration: UbuntuAnimation.BriskDuration
468+ }
469+
470+ property var previewData
471+ property bool valid: item !== null
472+
473+ onLoaded: {
474+ if (previewListView.onScreen && previewData !== undefined) {
475+ item.previewData = Qt.binding(function() { return previewData })
476+ }
477+ }
478+
479+ Connections {
480+ ignoreUnknownSignals: true
481+ target: item
482+ onClose: {
483+ previewListView.open = false
484+ }
485+ }
486+
487+ function closePreviewSpinner() {
488+ if(item) {
489+ item.showProcessingAction = false;
490+ }
491+ }
492+ }
493+ }
494+
495+ Image {
496+ objectName: "pointerArrow"
497+ anchors {
498+ top: previewListView.bottom
499+ left: parent.left
500+ leftMargin: previewListView.categoryDelegate !== undefined && previewListView.categoryDelegate.currentItem ?
501+ previewListView.categoryDelegate.currentItem.center + (-width + margins) / 2 : 0
502+
503+ Behavior on leftMargin {
504+ SmoothedAnimation {
505+ duration: UbuntuAnimation.FastDuration
506+ }
507+ }
508+ }
509+ height: units.gu(1)
510+ width: units.gu(2)
511+ property int margins: previewListView.categoryDelegate ? previewListView.categoryDelegate.margins : 0
512+ opacity: previewListView.open ? .5 : 0
513+
514+ source: "graphics/tooltip_arrow.png"
348 }
515 }
349
516
350 // TODO: Move as InverseMouseArea to DashPreview
517 // TODO: Move as InverseMouseArea to DashPreview
351 MouseArea {
518 MouseArea {
519+ objectName: "closePreviewMouseArea"
352− enabled: previewLoader.onScreen
520+ enabled: previewListView.onScreen
353 anchors {
521 anchors {
354 fill: parent
522 fill: parent
355 topMargin: effect.bottomGapPx
523 topMargin: effect.bottomGapPx
356 }
524 }
357 onClicked: {
525 onClicked: {
358− previewLoader.open = false;
526+ previewListView.open = false;
359 }
527 }
360 }
528 }
361}
529}
Dash/Music/AlbumTile.qmldeleted−116
@@ -1,116 +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−import QtQuick 2.0
18−import Ubuntu.Components 0.1
19−
20−AbstractButton {
21− id: root
22− property url source
23− property alias album: albumLabel.text
24− property alias artist: artistLabel.text
25− property alias iconWidth: icon.width
26− property alias iconHeight: icon.height
27−
28− UbuntuShape {
29− id: icon
30− anchors {
31− top: parent.top
32− horizontalCenter: parent.horizontalCenter
33− }
34− radius: "medium"
35− image: Image {
36− source: root.source
37− sourceSize { width: icon.width; height: icon.height }
38− asynchronous: true
39− cache: false
40− }
41− }
42−
43− UbuntuShape {
44− id: borderPressed
45−
46− anchors.fill: icon
47− radius: "medium"
48− borderSource: "radius_pressed.sci"
49− opacity: root.pressed ? 1.0 : 0.0
50− Behavior on opacity { NumberAnimation { duration: 200; easing.type: Easing.OutQuint } }
51− }
52−
53− Item {
54− id: artistContainer
55− anchors {
56− left: parent.left
57− right: parent.right
58− top: icon.bottom
59− }
60− height: units.gu(2)
61−
62− Label {
63− id: artistLabel
64− anchors {
65− baseline: parent.bottom
66− left: parent.left
67− right: parent.right
68− leftMargin: units.gu(1)
69− rightMargin: units.gu(1)
70− }
71−
72− color: Theme.palette.selected.backgroundText
73− opacity: 0.9
74− style: Text.Raised
75− styleColor: "black"
76− fontSize: "small"
77− elide: Text.ElideMiddle
78− horizontalAlignment: Text.AlignHCenter
79− }
80− }
81−
82− Item {
83− anchors {
84− left: parent.left
85− right: parent.right
86− top: artistContainer.bottom
87− }
88− height: units.gu(1.5)
89−
90− Label {
91− id: albumLabel
92− anchors {
93− baseline: parent.bottom
94− left: parent.left
95− right: parent.right
96− leftMargin: units.gu(1)
97− rightMargin: units.gu(1)
98− }
99−
100− color: Theme.palette.selected.backgroundText
101− opacity: 0.9
102− style: Text.Raised
103− styleColor: "black"
104− fontSize: "small"
105− font.weight: Font.Light
106− elide: Text.ElideMiddle
107− horizontalAlignment: Text.AlignHCenter
108− }
109− }
110−
111− GridView.onRemove: SequentialAnimation {
112− PropertyAction { target: root; property: "GridView.delayRemove"; value: true }
113− NumberAnimation { target: root; property: "opacity"; from: 1; to: 0; duration: 200; easing.type: Easing.InOutQuad }
114− PropertyAction { target: root; property: "GridView.delayRemove"; value: false }
115− }
116−}
Dash/Music/MusicFilterGrid.qml+2−4
@@ -44,13 +44,11 @@ GenericFilterGrid {
44 }
44 }
45
45
46 onClicked: {
46 onClicked: {
47− var data = { model: model }
48− filtergrid.clicked(index, data, tile.y)
47+ filtergrid.clicked(index, filtergrid.model, tile.y)
49 }
48 }
50
49
51 onPressAndHold: {
50 onPressAndHold: {
52− var data = { model: model }
53− filtergrid.pressAndHold(index, data, tile.y)
51+ filtergrid.pressAndHold(index, filtergrid.model, tile.y)
54 }
52 }
55 }
53 }
56}
54}
Dash/SearchableResultModel.qml+4
@@ -25,6 +25,10 @@ SortFilterProxyModel {
25
25
26 filterRole: CategoryResults.RoleTitle
26 filterRole: CategoryResults.RoleTitle
27
27
28+ function get(index) {
29+ return model.get(mapToSource(index))
30+ }
31+
28 onSearchQueryChanged: {
32 onSearchQueryChanged: {
29 if (searchQuery.length == 0) {
33 if (searchQuery.length == 0) {
30 filterRegExp = RegExp("");
34 filterRegExp = RegExp("");
Dash/Video/VideoFilterGrid.qml+2−4
@@ -42,13 +42,11 @@ GenericFilterGrid {
42 style: VideoTileStyle {}
42 style: VideoTileStyle {}
43
43
44 onClicked: {
44 onClicked: {
45− var data = { model: model }
46− filtergrid.clicked(index, data, tile.y)
45+ filtergrid.clicked(index, filtergrid.model, tile.y)
47 }
46 }
48
47
49 onPressAndHold: {
48 onPressAndHold: {
50− var data = { model: model }
51− filtergrid.pressAndHold(index, data, tile.y)
49+ filtergrid.pressAndHold(index, filtergrid.model, tile.y)
52 }
50 }
53 }
51 }
54}
52}
Dash/graphics/tooltip_arrow@27.pngnew file

Binary file not shown.

Greeter/Greeter.qml+1−1
@@ -52,7 +52,7 @@ Showable {
52 signal unlocked(int uid)
52 signal unlocked(int uid)
53
53
54 onRightTeaserPressedChanged: {
54 onRightTeaserPressedChanged: {
55− if (rightTeaserPressed && !locked) {
55+ if (rightTeaserPressed && !locked && x == 0) {
56 teasingTimer.start();
56 teasingTimer.start();
57 }
57 }
58 }
58 }
Launcher/Launcher.qml+1−1
@@ -28,7 +28,7 @@ Item {
28
28
29 property int panelWidth: units.gu(8)
29 property int panelWidth: units.gu(8)
30 property int dragAreaWidth: units.gu(1)
30 property int dragAreaWidth: units.gu(1)
31− property int minimizeDistance: panelWidth * 2.5
31+ property int minimizeDistance: units.gu(26)
32 property real progress: dragArea.dragging && dragArea.touchX > panelWidth ?
32 property real progress: dragArea.dragging && dragArea.touchX > panelWidth ?
33 (width * (dragArea.touchX-panelWidth) / (width - panelWidth)) : 0
33 (width * (dragArea.touchX-panelWidth) / (width - panelWidth)) : 0
34
34
Launcher/LauncherPanel.qml+87−46
@@ -21,6 +21,7 @@ import Unity 0.1
21import Unity.Launcher 0.1
21import Unity.Launcher 0.1
22import Ubuntu.Components.Popups 0.1
22import Ubuntu.Components.Popups 0.1
23import "../Components/ListItems"
23import "../Components/ListItems"
24+import "../Components/"
24
25
25Item {
26Item {
26 id: root
27 id: root
@@ -31,7 +32,7 @@ Item {
31 property bool inverted: true
32 property bool inverted: true
32 property bool dragging: false
33 property bool dragging: false
33 property bool moving: launcherListView.moving || launcherListView.flicking
34 property bool moving: launcherListView.moving || launcherListView.flicking
34− property bool preventHiding: moving || dndArea.draggedIndex >= 0 || dndArea.quickListPopover !== null || dndArea.pressed
35+ property bool preventHiding: moving || dndArea.draggedIndex >= 0 || quickList.state === "open" || dndArea.pressed
35 property int highlightIndex: -1
36 property int highlightIndex: -1
36
37
37 signal applicationSelected(string appId)
38 signal applicationSelected(string appId)
@@ -278,7 +279,6 @@ Item {
278 property bool postDragging: false
279 property bool postDragging: false
279 property int startX
280 property int startX
280 property int startY
281 property int startY
281− property var quickListPopover: null
282
282
283 onPressed: {
283 onPressed: {
284 selectedItem = launcherListView.itemAt(mouseX, mouseY + launcherListView.realContentY)
284 selectedItem = launcherListView.itemAt(mouseX, mouseY + launcherListView.realContentY)
@@ -357,11 +357,10 @@ Item {
357 draggedIndex = Math.floor((mouseY + launcherListView.realContentY) / launcherListView.realItemHeight);
357 draggedIndex = Math.floor((mouseY + launcherListView.realContentY) / launcherListView.realItemHeight);
358
358
359 // Opening QuickList
359 // Opening QuickList
360− var quickListModel = launcherListView.model.get(draggedIndex).quickList
360+ quickList.item = selectedItem;
361− var quickListAppId = launcherListView.model.get(draggedIndex).appId
361+ quickList.model = launcherListView.model.get(draggedIndex).quickList;
362−
362+ quickList.appId = launcherListView.model.get(draggedIndex).appId;
363− quickListPopover = PopupUtils.open(popoverComponent, selectedItem,
363+ quickList.state = "open";
364− {model: quickListModel, appId: quickListAppId})
365
364
366 launcherListView.interactive = false
365 launcherListView.interactive = false
367
366
@@ -387,7 +386,7 @@ Item {
387 var distance = Math.max(Math.abs(mouseX - startX), Math.abs(mouseY - startY))
386 var distance = Math.max(Math.abs(mouseX - startX), Math.abs(mouseY - startY))
388 if (!preDragging && distance > units.gu(1.5)) {
387 if (!preDragging && distance > units.gu(1.5)) {
389 preDragging = true;
388 preDragging = true;
390− PopupUtils.close(quickListPopover)
389+ quickList.state = "";
391 }
390 }
392 if (distance > launcherListView.itemHeight) {
391 if (distance > launcherListView.itemHeight) {
393 selectedItem.dragging = true
392 selectedItem.dragging = true
@@ -482,53 +481,95 @@ Item {
482 }
481 }
483 }
482 }
484
483
485− Component {
484+ UbuntuShapeForItem {
486− id: popoverComponent
485+ id: quickListShape
486+ objectName: "quickListShape"
487+ anchors.fill: quickList
488+ opacity: quickList.state === "open" ? 0.8 : 0
489+ visible: opacity > 0
490+ rotation: root.rotation
491+
492+ Behavior on opacity {
493+ UbuntuNumberAnimation {}
494+ }
495+
496+ image: quickList
497+
498+ Image {
499+ anchors {
500+ right: parent.left
501+ rightMargin: -units.dp(4)
502+ verticalCenter: parent.verticalCenter
503+ verticalCenterOffset: -quickList.offset
504+ }
505+ height: units.gu(1)
506+ width: units.gu(2)
507+ source: "graphics/quicklist_tooltip.png"
508+ rotation: 90
509+ }
510+
511+ InverseMouseArea {
512+ anchors.fill: parent
513+ enabled: quickList.state == "open"
514+ onClicked: {
515+ quickList.state = ""
516+ }
517+ }
518+
519+ }
520+
521+ Rectangle {
522+ id: quickList
523+ objectName: "quickList"
524+ color: "#221e1c"
525+ width: units.gu(30)
526+ height: quickListColumn.height
527+ anchors {
528+ left: root.inverted ? undefined : parent.right
529+ right: root.inverted ? parent.left : undefined
530+ margins: units.gu(1)
487
531
488− Popover {
532+ }
489− id: popover
533+ y: itemCenter - (height / 2) + offset
490− property var model
534+ rotation: root.rotation
491− property string appId
492− contentWidth: quickListColumn.width
493− foregroundStyle: QuicklistForegroundStyle {}
494
535
495− // FIXME: There's a bug in the Popover positioning that it covers the item in case it is rotated.
536+ property var model
496− // https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1204470
537+ property string appId
497− // For now, let's move the Popover around with callerMargin.
538+ property var item
498− // Remove popupMargin once the bug is fixed.
499− property int popupMargin: root.inverted ? launcherListView.itemHeight : 0;
500
539
501− callerMargin: units.gu(1) + popupMargin
540+ // internal
541+ property int itemCenter: item ? root.mapFromItem(quickList.item).y + (item.height / 2) : units.gu(1)
542+ property int offset: itemCenter + (height/2) + units.gu(1) > parent.height ? -itemCenter - (height/2) - units.gu(1) + parent.height :
543+ itemCenter - (height/2) < units.gu(1) ? (height/2) - itemCenter + units.gu(1) : 0
502
544
503− Column {
545+ Column {
504− id: quickListColumn
546+ id: quickListColumn
505− width: units.gu(30)
547+ width: parent.width
506− height: childrenRect.height
548+ height: childrenRect.height
507
549
508− Repeater {
550+ Repeater {
509− id: popoverRepeater
551+ id: popoverRepeater
510− model: popover.model
552+ model: quickList.model
511
553
512− ListItems.Standard {
554+ ListItems.Standard {
513− objectName: "quickListEntry" + index
555+ objectName: "quickListEntry" + index
514− text: (model.clickable ? "" : "<b>") + model.label + (model.clickable ? "" : "</b>")
556+ text: (model.clickable ? "" : "<b>") + model.label + (model.clickable ? "" : "</b>")
515− highlightWhenPressed: model.clickable
557+ highlightWhenPressed: model.clickable
516
558
517− // FIXME: This is a workaround for the theme not being context sensitive. I.e. the
559+ // 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
560+ // ListItems don't know that they are sitting in a themed Popover where the color
519− // needs to be inverted.
561+ // needs to be inverted.
520− __foregroundColor: Theme.palette.selected.backgroundText
562+ __foregroundColor: Theme.palette.selected.backgroundText
521
563
522− onClicked: {
564+ onClicked: {
523− if (!model.clickable) {
565+ if (!model.clickable) {
524− return;
566+ return;
525− }
526− PopupUtils.close(popover);
527− // Unsetting model to prevent showing changing entries during fading out
528− // that may happen because of triggering an action.
529− LauncherModel.quickListActionInvoked(appId, index);
530− popoverRepeater.model = undefined;
531 }
567 }
568+ quickList.state = "";
569+ // Unsetting model to prevent showing changing entries during fading out
570+ // that may happen because of triggering an action.
571+ LauncherModel.quickListActionInvoked(quickList.appId, index);
572+ quickList.model = undefined;
532 }
573 }
533 }
574 }
534 }
575 }
Notifications/Notification.qml+2−1
@@ -34,12 +34,13 @@ UbuntuShape {
34 property var notification
34 property var notification
35
35
36 objectName: "background"
36 objectName: "background"
37− implicitHeight: contentColumn.height + contentColumn.spacing * 2
37+ implicitHeight: type != Notification.PlaceHolder ? contentColumn.height + contentColumn.spacing * 2 : 0
38 color: Qt.rgba(0.132, 0.117, 0.109, 0.97)
38 color: Qt.rgba(0.132, 0.117, 0.109, 0.97)
39 opacity: 0
39 opacity: 0
40 radius: "medium"
40 radius: "medium"
41
41
42 clip: true
42 clip: true
43+ visible: type != Notification.PlaceHolder
43
44
44 UnityMenuModelPaths {
45 UnityMenuModelPaths {
45 id: paths
46 id: paths
data/unity8-setcap.confdeleted−32
@@ -1,32 +0,0 @@
1−# unity8-setcap - ugly hacks to arrange for /usr/bin/unity8 to have CAP_SYS_RESOURCE
2−
3−# XXX replace me with some root-helper to gain CAP_SYS_RESOURCE XXX
4−
5−author "Loïc Minier <loic.minier@ubuntu.com>"
6−description "Ugly hacks to arrange for /usr/bin/unity8 to have CAP_SYS_RESOURCE"
7−
8−# start when first boot-hooks event is emitted and before lightdm (lightdm
9−# starts ubuntu-touch-session which starts unity8); note that /run is
10−# guaranteeds to be be there because lightdm starts on filesystem
11−start on boot-hooks and starting lightdm
12−
13−# NOT a task as otherwise this would block restarting lightdm
14−
15−env RUNDIR=/run/unity8-setcap
16−
17−# work needs to be done in pre-start as this really is a job with nothing to
18−# start
19−pre-start script
20− if [ ! -e "$RUNDIR" ]; then
21− mkdir "$RUNDIR"
22− # /run is noexec, hence mounting another tmpfs exec
23− # NB: unity8 is 35K; 512K should be enough for everyone
24− mount -o rw,nosuid,nodev,exec,relatime,mode=755,size=512k -t tmpfs tmpfs "$RUNDIR"
25− cp -a /usr/bin/unity8 "$RUNDIR"
26− setcap CAP_SYS_RESOURCE=+ep "$RUNDIR/unity8"
27− # bind-mount this back as unity8 checks dirname(argv[0]'s) == /usr to
28− # decide whether it's installed or not
29− mount --bind "$RUNDIR/unity8" /usr/bin/unity8
30− fi
31−end script
32−
debian/changelog+127
@@ -1,3 +1,130 @@
1+unity8 (7.83+14.04.20131105.1-0ubuntu1) trusty; urgency=low
2+
3+ [ Michał Sawicz ]
4+ * Implement switching between previews by swiping. (LP: #1220651,
5+ #1088572)
6+
7+ [ Nick Dedekind ]
8+ * Faster loading of indicator menus. (LP: #1226650)
9+
10+ [ Albert Astals ]
11+ * Make sure we have ssh started when we need it .
12+ * Fix a few warnings in DashContent.qml on shutdown
13+ Dash/DashContent.qml:119: TypeError: Cannot read property
14+ 'previewShown' of null Dash/DashContent.qml:120: TypeError: Cannot
15+ read property 'moving' of null .
16+ * Don't use deprecated Panel methods.
17+
18+ [ Christopher Lee ]
19+ * Adds an easy to consume function that attempts to unlock the unity
20+ greeter. (LP: #1240261)
21+
22+ [ Lars Uebernickel ]
23+ * Indicators: add TimeFormatter and use it in the messaging menu This
24+ component can be used to turn a timestamp and a format string into a
25+ string with the corresponding time and format. The string will
26+ change whenever the timezone changes. (LP: #1236413)
27+
28+ [ Andrea Cimitan ]
29+ * Allow drag over bounds, but not overshoot. (LP: #1204300)
30+ * Threshold to activate the left edge swipe to reveal dash is now 26
31+ GU. (LP: #1236286)
32+
33+ [ Michael Zanetti ]
34+ * replace the launcher quicklist's Popover with an own quicklist
35+ implementation The Popover probably won't ever support what the
36+ launcher needs.
37+ * Implement switching between previews by swiping. (LP: #1220651,
38+ #1088572)
39+ * don't trigger the greeter teasing during a movement of the greeter.
40+
41+ [ Ubuntu daily release ]
42+ * Automatic snapshot from revision 495
43+
44+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 05 Nov 2013 12:22:04 +0000
45+
46+unity8 (7.83+14.04.20131031-0ubuntu1) trusty; urgency=low
47+
48+ [ Michał Sawicz ]
49+ * Use setenv as early as possible to avoid setenv and getenv clashing
50+ in multi-threaded situations. (LP: #1240866)
51+
52+ [ Ubuntu daily release ]
53+ * Automatic snapshot from revision 482
54+
55+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Thu, 31 Oct 2013 20:10:56 +0000
56+
57+unity8 (7.83+14.04.20131028.1-0ubuntu1) trusty; urgency=low
58+
59+ * New rebuild forced
60+ * Automatic snapshot from revision 480
61+
62+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 28 Oct 2013 22:42:07 +0000
63+
64+unity8 (7.83+14.04.20131028-0ubuntu1) trusty; urgency=low
65+
66+ [ Andrea Cimitan ]
67+ * Fix 1195349 by counting drawbuffer on the newContentX logic of the
68+ carousel When we changed carousel from repeater to listview, we
69+ added drawbuffer. this breaks the logic of newContentX, which was
70+ considered disabled and set to -1. The correct disabled value now
71+ has to take into account the drawbuffer. (LP: #1195349)
72+
73+ [ Christopher Lee ]
74+ * Checks both then env and upstart env for the currently set
75+ XDG_DATA_DIRS so they can be set correctly for the test.
76+
77+ [ Ubuntu daily release ]
78+ * Automatic snapshot from revision 479
79+
80+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 28 Oct 2013 03:08:59 +0000
81+
82+unity8 (7.83+13.10.20131016.2-0ubuntu1) saucy; urgency=low
83+
84+ [ Loïc Minier ]
85+ * Merge ~lool/unity8/drop-setcap-conf. Drop unity8-setcap.conf as this
86+ breaks desktop installs (no boot-hook event is emitted, boot
87+ stalls); add maintscript snippet to rm_conffile on upgrades; this
88+ boot-hook is now shipped under a different name in lxc-android-
89+ config.
90+
91+ [ Michał Sawicz ]
92+ * Ship a camera-app.desktop file and use a custom XDG_DATA_DIRS for
93+ testing.
94+
95+ [ Ubuntu daily release ]
96+ * Automatic snapshot from revision 472
97+
98+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 16 Oct 2013 23:15:48 +0000
99+
100+unity8 (7.83+13.10.20131016.1-0ubuntu1) saucy; urgency=low
101+
102+ [ Diego Sarmentero ]
103+ * Disable Preview interaction until the scope responds.
104+
105+ [ Albert Astals ]
106+ * Hide placeholder notification.
107+
108+ [ Ubuntu daily release ]
109+ * Automatic snapshot from revision 469
110+
111+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 16 Oct 2013 17:29:36 +0000
112+
113+unity8 (7.83+13.10.20131016-0ubuntu1) saucy; urgency=low
114+
115+ [ Loïc Minier ]
116+ * Add upstart job to copy unity8 to a new tmpfs, setcap it, and bind-
117+ mount it back; this is an ugly hack to set CAP_SYS_RESOURCE until we
118+ have a root-helper for it.
119+
120+ [ Michal Hruby ]
121+ * Enable definition of scope back references in overridden results.
122+
123+ [ Ubuntu daily release ]
124+ * Automatic snapshot from revision 466
125+
126+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 16 Oct 2013 11:29:52 +0000
127+
1unity8 (7.83+13.10.20131015.4-0ubuntu1) saucy; urgency=low
128unity8 (7.83+13.10.20131015.4-0ubuntu1) saucy; urgency=low
2
129
3 [ Michał Sawicz ]
130 [ Michał Sawicz ]
debian/control+1−1
@@ -91,7 +91,7 @@ Description: Unity 8 shell
91Package: unity8-autopilot
91Package: unity8-autopilot
92Architecture: all
92Architecture: all
93Depends: gir1.2-glib-2.0,
93Depends: gir1.2-glib-2.0,
94− libautopilot-qt,
94+ libautopilot-qt (>= 1.4),
95 libqt5test5,
95 libqt5test5,
96 libqt5widgets5,
96 libqt5widgets5,
97 python-evdev,
97 python-evdev,
debian/unity8-fake-env.install+1
@@ -1 +1,2 @@
1usr/lib/*/unity8/qml/mocks
1usr/lib/*/unity8/qml/mocks
2+usr/share/unity8/mocks
debian/unity8.install−1
@@ -14,4 +14,3 @@ usr/share/unity8/Shell.qml
14usr/share/unity8/SideStage
14usr/share/unity8/SideStage
15usr/share/unity8/graphics
15usr/share/unity8/graphics
16data/unity8.conf usr/share/upstart/sessions/
16data/unity8.conf usr/share/upstart/sessions/
17−data/unity8-setcap.conf etc/init/boot-hooks/
debian/unity8.maintscriptnew file+1
@@ -0,0 +1 @@
1+rm_conffile /etc/init/boot-hooks/unity8-setcap.conf
main.cpp+8−8
@@ -78,14 +78,6 @@ void resolveIconTheme() {
78
78
79int startShell(int argc, const char** argv, void* server)
79int startShell(int argc, const char** argv, void* server)
80{
80{
81− /* Workaround Qt platform integration plugin not advertising itself
82− as having the following capabilities:
83− - QPlatformIntegration::ThreadedOpenGL
84− - QPlatformIntegration::BufferQueueingOpenGL
85− */
86− setenv("QML_FORCE_THREADED_RENDERER", "1", 1);
87− setenv("QML_FIXED_ANIMATION_STEP", "1", 1);
88−
89 const bool isUbuntuMirServer = qgetenv("QT_QPA_PLATFORM") == "ubuntumirserver";
81 const bool isUbuntuMirServer = qgetenv("QT_QPA_PLATFORM") == "ubuntumirserver";
90
82
91 QGuiApplication::setApplicationName("Unity 8");
83 QGuiApplication::setApplicationName("Unity 8");
@@ -186,6 +178,14 @@ int startShell(int argc, const char** argv, void* server)
186
178
187int main(int argc, const char *argv[])
179int main(int argc, const char *argv[])
188{
180{
181+ /* Workaround Qt platform integration plugin not advertising itself
182+ as having the following capabilities:
183+ - QPlatformIntegration::ThreadedOpenGL
184+ - QPlatformIntegration::BufferQueueingOpenGL
185+ */
186+ setenv("QML_FORCE_THREADED_RENDERER", "1", 1);
187+ setenv("QML_FIXED_ANIMATION_STEP", "1", 1);
188+
189 // For ubuntumirserver/ubuntumirclient
189 // For ubuntumirserver/ubuntumirclient
190 if (qgetenv("QT_QPA_PLATFORM").startsWith("ubuntumir")) {
190 if (qgetenv("QT_QPA_PLATFORM").startsWith("ubuntumir")) {
191 setenv("QT_QPA_PLATFORM", "ubuntumirserver", 1);
191 setenv("QT_QPA_PLATFORM", "ubuntumirserver", 1);
plugins/Unity/Indicators/CMakeLists.txt+1
@@ -36,6 +36,7 @@ set(IndicatorsQML_SOURCES
36 unitymenumodelcache.cpp
36 unitymenumodelcache.cpp
37 unitymenumodelstack.cpp
37 unitymenumodelstack.cpp
38 visibleindicatorsmodel.cpp
38 visibleindicatorsmodel.cpp
39+ timeformatter.cpp
39)
40)
40add_definitions(-DUNITYINDICATORS_LIBRARY)
41add_definitions(-DUNITYINDICATORS_LIBRARY)
41
42
plugins/Unity/Indicators/Messaging/qml/MessageMenuItemFactory.qml+3−4
@@ -22,7 +22,6 @@ import QtQuick 2.0
22import Ubuntu.Components 0.1
22import Ubuntu.Components 0.1
23import Unity.Indicators 0.1 as Indicators
23import Unity.Indicators 0.1 as Indicators
24import QMenuModel 0.1 as QMenuModel
24import QMenuModel 0.1 as QMenuModel
25−import "utils.js" as Utils
26
25
27Indicators.BaseMenuItem {
26Indicators.BaseMenuItem {
28 id: menuItem
27 id: menuItem
@@ -65,7 +64,7 @@ Indicators.BaseMenuItem {
65 SimpleTextMessage {
64 SimpleTextMessage {
66 // text
65 // text
67 title: menu && menu.label ? menu.label : ""
66 title: menu && menu.label ? menu.label : ""
68− time: menu ? Utils.formatDate(menu.ext.xCanonicalTime) : ""
67+ time: menu ? menu.ext.xCanonicalTime : 0
69 message: menu && menu.ext.xCanonicalText ? menu.ext.xCanonicalText : ""
68 message: menu && menu.ext.xCanonicalText ? menu.ext.xCanonicalText : ""
70 // icons
69 // icons
71 avatar: menu && menu.ext.icon !== undefined ? menu.ext.icon : "qrc:/indicators/artwork/messaging/default_contact.png"
70 avatar: menu && menu.ext.icon !== undefined ? menu.ext.icon : "qrc:/indicators/artwork/messaging/default_contact.png"
@@ -90,7 +89,7 @@ Indicators.BaseMenuItem {
90
89
91 // text
90 // text
92 title: menu && menu.label ? menu.label : ""
91 title: menu && menu.label ? menu.label : ""
93− time: menu ? Utils.formatDate(menu.ext.xCanonicalTime) : ""
92+ time: menu ? menu.ext.xCanonicalTime : 0
94 message: menu && menu.ext.xCanonicalText ? menu.ext.xCanonicalText : ""
93 message: menu && menu.ext.xCanonicalText ? menu.ext.xCanonicalText : ""
95 replyButtonText: actionsDescription && actionsDescription[0].label ? actionsDescription[0].label : "Send"
94 replyButtonText: actionsDescription && actionsDescription[0].label ? actionsDescription[0].label : "Send"
96 // icons
95 // icons
@@ -126,7 +125,7 @@ Indicators.BaseMenuItem {
126
125
127 // text
126 // text
128 title: menu && menu.label ? menu.label : ""
127 title: menu && menu.label ? menu.label : ""
129− time: menu ? Utils.formatDate(menu.ext.xCanonicalTime) : ""
128+ time: menu ? menu.ext.xCanonicalTime : ""
130 message: menu && menu.ext.xCanonicalText ? menu.ext.xCanonicalText : ""
129 message: menu && menu.ext.xCanonicalText ? menu.ext.xCanonicalText : ""
131 actionButtonText: actionsDescription && actionsDescription[0].label ? actionsDescription[0].label : "Call back"
130 actionButtonText: actionsDescription && actionsDescription[0].label ? actionsDescription[0].label : "Call back"
132 replyButtonText: actionsDescription && actionsDescription[1].label ? actionsDescription[1].label : "Send"
131 replyButtonText: actionsDescription && actionsDescription[1].label ? actionsDescription[1].label : "Send"
plugins/Unity/Indicators/Messaging/qml/SimpleTextMessage.qml+9−2
@@ -20,19 +20,20 @@
20
20
21import QtQuick 2.0
21import QtQuick 2.0
22import Ubuntu.Components 0.1
22import Ubuntu.Components 0.1
23+import Unity.Indicators 0.1
23
24
24HeroMessage {
25HeroMessage {
25 id: __heroMessage
26 id: __heroMessage
26
27
27 property string title: ""
28 property string title: ""
28− property string time: ""
29+ property var time
29 property string message: ""
30 property string message: ""
30
31
31 property alias footer: footerLoader.sourceComponent
32 property alias footer: footerLoader.sourceComponent
32
33
33 expandedHeight: fullMessage.y + fullMessage.height + units.gu(2)
34 expandedHeight: fullMessage.y + fullMessage.height + units.gu(2)
34 heroMessageHeader.titleText.text: title
35 heroMessageHeader.titleText.text: title
35− heroMessageHeader.subtitleText.text: time
36+ heroMessageHeader.subtitleText.text: timeFormatter.timeString
36 heroMessageHeader.bodyText.text: message
37 heroMessageHeader.bodyText.text: message
37
38
38 Item {
39 Item {
@@ -49,6 +50,12 @@ HeroMessage {
49 opacity: 0.0
50 opacity: 0.0
50 enabled: false
51 enabled: false
51
52
53+ TimeFormatter {
54+ id: timeFormatter
55+ time: __heroMessage.time
56+ format: "hh:mm - MMM dd"
57+ }
58+
52 Label {
59 Label {
53 id: bodyText
60 id: bodyText
54 anchors {
61 anchors {
plugins/Unity/Indicators/Messaging/qml/SnapDecision.qml+9−3
@@ -21,13 +21,13 @@
21import QtQuick 2.0
21import QtQuick 2.0
22import Ubuntu.Components 0.1
22import Ubuntu.Components 0.1
23import Unity.Indicators 0.1 as Indicators
23import Unity.Indicators 0.1 as Indicators
24−import "utils.js" as Utils
24+import Unity.Indicators 0.1
25
25
26HeroMessage {
26HeroMessage {
27 id: snapDecision
27 id: snapDecision
28
28
29 property string title: ""
29 property string title: ""
30− property string time: ""
30+ property var time
31 property string message: ""
31 property string message: ""
32
32
33 property bool activateEnabled: true
33 property bool activateEnabled: true
@@ -41,7 +41,7 @@ HeroMessage {
41 heroMessageHeader.titleText.text: title
41 heroMessageHeader.titleText.text: title
42 heroMessageHeader.subtitleText.text: message
42 heroMessageHeader.subtitleText.text: message
43 heroMessageHeader.subtitleText.color: "#e8e1d0"
43 heroMessageHeader.subtitleText.color: "#e8e1d0"
44− heroMessageHeader.bodyText.text: time
44+ heroMessageHeader.bodyText.text: timeFormatter.timeString
45 heroMessageHeader.bodyText.color: "#8f8f88"
45 heroMessageHeader.bodyText.color: "#8f8f88"
46
46
47 signal activate
47 signal activate
@@ -59,6 +59,12 @@ HeroMessage {
59 height: units.gu(4)
59 height: units.gu(4)
60 opacity: 0.0
60 opacity: 0.0
61
61
62+ TimeFormatter {
63+ id: timeFormatter
64+ time: snapDecision.time
65+ format: "hh:mm - MMM dd"
66+ }
67+
62 Button {
68 Button {
63 text: "Message"
69 text: "Message"
64 color: "#bababa"
70 color: "#bababa"
plugins/Unity/Indicators/Messaging/qml/utils.jsdeleted−27
@@ -1,27 +0,0 @@
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− * Renato Araujo Oliveira Filho <renato@canonical.com>
18− * Olivier Tilloy <olivier.tilloy@canonical.com>
19− */
20−
21−.pragma library
22−
23−function formatDate(timestamp) {
24− // timestamp is in microseconds since epoch
25− var date = new Date(timestamp / 1000);
26− return Qt.formatDateTime(date, "hh:mm - MMM dd");
27−}
plugins/Unity/Indicators/plugin.cpp+2
@@ -33,6 +33,7 @@
33#include "unitymenumodelcache.h"
33#include "unitymenumodelcache.h"
34#include "unitymenumodelstack.h"
34#include "unitymenumodelstack.h"
35#include "visibleindicatorsmodel.h"
35#include "visibleindicatorsmodel.h"
36+#include "timeformatter.h"
36
37
37static QObject* menuModelCacheSingleton(QQmlEngine* engine, QJSEngine* scriptEngine) {
38static QObject* menuModelCacheSingleton(QQmlEngine* engine, QJSEngine* scriptEngine) {
38 Q_UNUSED(engine);
39 Q_UNUSED(engine);
@@ -51,6 +52,7 @@ void Indicators2Plugin::registerTypes(const char *uri)
51 qmlRegisterType<RootActionState>(uri, 0, 1, "RootActionState");
52 qmlRegisterType<RootActionState>(uri, 0, 1, "RootActionState");
52 qmlRegisterType<ModelPrinter>(uri, 0, 1, "ModelPrinter");
53 qmlRegisterType<ModelPrinter>(uri, 0, 1, "ModelPrinter");
53 qmlRegisterType<VisibleIndicatorsModel>(uri, 0, 1, "VisibleIndicatorsModel");
54 qmlRegisterType<VisibleIndicatorsModel>(uri, 0, 1, "VisibleIndicatorsModel");
55+ qmlRegisterType<TimeFormatter>(uri, 0, 1, "TimeFormatter");
54
56
55 qmlRegisterSingletonType<UnityMenuModelCache>(uri, 0, 1, "UnityMenuModelCache", menuModelCacheSingleton);
57 qmlRegisterSingletonType<UnityMenuModelCache>(uri, 0, 1, "UnityMenuModelCache", menuModelCacheSingleton);
56
58
plugins/Unity/Indicators/qml/IndicatorPage.qml+27−6
@@ -34,11 +34,36 @@ IndicatorBase {
34 Indicators.UnityMenuModelStack {
34 Indicators.UnityMenuModelStack {
35 id: menuStack
35 id: menuStack
36 head: contentActive ? main.menuModel : null
36 head: contentActive ? main.menuModel : null
37+
38+ property var rootMenu: null
39+
40+ onTailChanged: {
41+ if (!tail) {
42+ rootMenu = null;
43+ } else if (rootMenu != tail) {
44+ if (tail.get(0, "type") === rootMenuType) {
45+ rootMenu = menuStack.tail.submenu(0);
46+ push(rootMenu, 0);
47+ } else {
48+ rootMenu = null;
49+ }
50+ }
51+ }
52+ }
53+
54+ Connections {
55+ target: menuStack.tail
56+ onRowsInserted: {
57+ if (menuStack.rootMenu !== menuStack.tail && menuStack.tail.get(0, "type") === rootMenuType) {
58+ menuStack.rootMenu = menuStack.tail.submenu(0);
59+ menuStack.push(menuStack.rootMenu, 0);
60+ }
61+ }
37 }
62 }
38
63
39 ListView {
64 ListView {
40 id: mainMenu
65 id: mainMenu
41− model: menuStack.tail ? menuStack.tail : null
66+ model: menuStack.rootMenu
42
67
43 anchors {
68 anchors {
44 fill: parent
69 fill: parent
@@ -102,7 +127,7 @@ IndicatorBase {
102
127
103 Loader {
128 Loader {
104 id: loader
129 id: loader
105− asynchronous: true
130+ asynchronous: false
106
131
107 property int modelIndex: index
132 property int modelIndex: index
108
133
@@ -114,10 +139,6 @@ IndicatorBase {
114 sourceComponent: factory.load(model)
139 sourceComponent: factory.load(model)
115
140
116 onLoaded: {
141 onLoaded: {
117− if (model.type === rootMenuType) {
118− menuStack.push(mainMenu.model.submenu(index), index);
119− }
120−
121 if (item.hasOwnProperty("menuSelected")) {
142 if (item.hasOwnProperty("menuSelected")) {
122 item.menuSelected = Qt.binding(function() { return mainMenu.selectedIndex == index; });
143 item.menuSelected = Qt.binding(function() { return mainMenu.selectedIndex == index; });
123 item.selectMenu.connect(function() { mainMenu.selectedIndex = index; });
144 item.selectMenu.connect(function() { mainMenu.selectedIndex = index; });
plugins/Unity/Indicators/qml/SliderMenuItem.qml+3−2
@@ -51,12 +51,12 @@ FramedMenuItem {
51
51
52 control: Item {
52 control: Item {
53 id: row
53 id: row
54− width: sliderMenu.text ? units.gu(20) : menuItem.width - (2 * menuItem.__contentsMargins)
54+ width: menuItem.text ? units.gu(20) : menuItem.width - (2 * menuItem.__contentsMargins)
55 height: slider.height
55 height: slider.height
56
56
57 Image {
57 Image {
58 id: leftImage
58 id: leftImage
59− visible: source != ""
59+ visible: status === Image.Ready
60 anchors.left: row.left
60 anchors.left: row.left
61 anchors.verticalCenter: row.verticalCenter
61 anchors.verticalCenter: row.verticalCenter
62 height: units.gu(4)
62 height: units.gu(4)
@@ -96,6 +96,7 @@ FramedMenuItem {
96
96
97 Image {
97 Image {
98 id: rightImage
98 id: rightImage
99+ visible: status === Image.Ready
99 anchors.right: row.right
100 anchors.right: row.right
100 anchors.verticalCenter: row.verticalCenter
101 anchors.verticalCenter: row.verticalCenter
101 height: units.gu(4)
102 height: units.gu(4)
plugins/Unity/Indicators/timeformatter.cppnew file+172
@@ -0,0 +1,172 @@
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 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+ * Author: Lars Uebernickel <lars.uebernickel@canonical.com>
17+ */
18+
19+#include "timeformatter.h"
20+
21+#include <gio/gio.h>
22+#include <QDateTime>
23+
24+struct TimeFormatterPrivate
25+{
26+ TimeFormatter *formatter;
27+
28+ QString format;
29+ QString timeString;
30+ qint64 time;
31+
32+ GDBusConnection *system_bus;
33+ guint subscription_id;
34+ GCancellable *cancellable;
35+};
36+
37+static void
38+timedate1_properties_changed (GDBusConnection *connection,
39+ const gchar *sender_name,
40+ const gchar *object_path,
41+ const gchar *interface_name,
42+ const gchar *signal_name,
43+ GVariant *parameters,
44+ gpointer user_data)
45+{
46+ Q_UNUSED(connection);
47+ Q_UNUSED(sender_name);
48+ Q_UNUSED(object_path);
49+ Q_UNUSED(interface_name);
50+ Q_UNUSED(signal_name);
51+
52+ TimeFormatterPrivate *priv = (TimeFormatterPrivate *)user_data;
53+ GVariant *changed;
54+ GVariantIter *iter;
55+ const gchar *name;
56+
57+ if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sa{sv}as)")))
58+ return;
59+
60+ g_variant_get (parameters, "(s@a{sv}as)", NULL, &changed, &iter);
61+
62+ if (g_variant_lookup (changed, "Timezone", "s", NULL)) {
63+ priv->formatter->update();
64+ }
65+ else {
66+ while (g_variant_iter_next (iter, "&s", &name)) {
67+ if (g_str_equal (name, "Timezone")) {
68+ priv->formatter->update();
69+ break;
70+ }
71+ }
72+ }
73+
74+ g_variant_unref (changed);
75+ g_variant_iter_free (iter);
76+}
77+
78+static void
79+got_bus(GObject *object, GAsyncResult *result, gpointer user_data)
80+{
81+ Q_UNUSED(object);
82+
83+ TimeFormatterPrivate *priv = (TimeFormatterPrivate *)user_data;
84+ GError *error = NULL;
85+
86+ priv->system_bus = g_bus_get_finish (result, &error);
87+ if (priv->system_bus == NULL) {
88+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
89+ qWarning("TimeFormatter: cannot connect to the system bus: %s", error->message);
90+ g_error_free (error);
91+ return;
92+ }
93+
94+ /* Listen to the PropertiesChanged on the org.freedesktop.timedate1
95+ * interface from any sender. In practice, this signal will only be sent
96+ * from timedated (we can trust other processes on the system bus to behave
97+ * nicely). That way, we don't have to watch timedated's well-known name
98+ * and keep the process alive.
99+ */
100+ priv->subscription_id = g_dbus_connection_signal_subscribe (priv->system_bus,
101+ NULL, /* sender */
102+ "org.freedesktop.DBus.Properties",
103+ "PropertiesChanged",
104+ NULL,
105+ "org.freedesktop.timedate1",
106+ G_DBUS_SIGNAL_FLAGS_NONE,
107+ timedate1_properties_changed,
108+ priv, NULL);
109+}
110+
111+TimeFormatter::TimeFormatter(QObject *parent): QObject(parent)
112+{
113+ priv = new TimeFormatterPrivate;
114+ priv->formatter = this;
115+ priv->time = 0;
116+ priv->format = "yyyy-MM-dd hh:mm";
117+ priv->system_bus = NULL;
118+ priv->subscription_id = 0;
119+ priv->cancellable = g_cancellable_new ();
120+
121+ g_bus_get (G_BUS_TYPE_SYSTEM, priv->cancellable, got_bus, priv);
122+}
123+
124+TimeFormatter::~TimeFormatter()
125+{
126+ if (priv->system_bus) {
127+ g_dbus_connection_signal_unsubscribe (priv->system_bus, priv->subscription_id);
128+ g_object_unref (priv->system_bus);
129+ }
130+
131+ g_cancellable_cancel (priv->cancellable);
132+ g_object_unref (priv->cancellable);
133+}
134+
135+QString TimeFormatter::format() const
136+{
137+ return priv->format;
138+}
139+
140+QString TimeFormatter::timeString() const
141+{
142+ return priv->timeString;
143+}
144+
145+qint64 TimeFormatter::time() const
146+{
147+ return priv->time;
148+}
149+
150+void TimeFormatter::setFormat(const QString &format)
151+{
152+ if (priv->format != format) {
153+ priv->format = format;
154+ Q_EMIT formatChanged(priv->format);
155+ update();
156+ }
157+}
158+
159+void TimeFormatter::setTime(qint64 time)
160+{
161+ if (priv->time != time) {
162+ priv->time = time;
163+ Q_EMIT timeChanged(priv->time);
164+ update();
165+ }
166+}
167+
168+void TimeFormatter::update()
169+{
170+ priv->timeString = QDateTime::fromMSecsSinceEpoch(priv->time / 1000).toString(priv->format);
171+ Q_EMIT timeStringChanged(priv->timeString);
172+}
plugins/Unity/Indicators/timeformatter.hnew file+54
@@ -0,0 +1,54 @@
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 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+ * Author: Lars Uebernickel <lars.uebernickel@canonical.com>
17+ */
18+
19+#ifndef TIME_FORMATTER_H
20+#define TIME_FORMATTER_H
21+
22+#include "unityindicatorsglobal.h"
23+#include <QObject>
24+
25+class UNITYINDICATORS_EXPORT TimeFormatter : public QObject
26+{
27+ Q_OBJECT
28+ Q_PROPERTY(QString format READ format WRITE setFormat NOTIFY formatChanged)
29+ Q_PROPERTY(QString timeString READ timeString NOTIFY timeStringChanged)
30+ Q_PROPERTY(qint64 time READ time WRITE setTime NOTIFY timeChanged)
31+
32+public:
33+ TimeFormatter(QObject *parent = 0);
34+ ~TimeFormatter();
35+
36+ QString format() const;
37+ QString timeString() const;
38+ qint64 time() const;
39+
40+ void setFormat(const QString &format);
41+ void setTime(qint64 time);
42+
43+ void update();
44+
45+Q_SIGNALS:
46+ void formatChanged(const QString &format);
47+ void timeStringChanged(const QString &timeString);
48+ void timeChanged(qint64 time);
49+
50+private:
51+ struct TimeFormatterPrivate *priv;
52+};
53+
54+#endif
plugins/Unity/scope.cpp+2
@@ -228,6 +228,7 @@ void Scope::cancelActivation()
228
228
229void Scope::onActivated(unity::dash::LocalResult const& result, unity::dash::ScopeHandledType type, unity::glib::HintsMap const& hints)
229void Scope::onActivated(unity::dash::LocalResult const& result, unity::dash::ScopeHandledType type, unity::glib::HintsMap const& hints)
230{
230{
231+ Q_EMIT activated();
231 // note: we will not get called on SHOW_PREVIEW, instead UnityCore will signal preview_ready.
232 // note: we will not get called on SHOW_PREVIEW, instead UnityCore will signal preview_ready.
232 switch (type)
233 switch (type)
233 {
234 {
@@ -261,6 +262,7 @@ void Scope::onActivated(unity::dash::LocalResult const& result, unity::dash::Sco
261
262
262void Scope::onPreviewReady(unity::dash::LocalResult const& /* result */, unity::dash::Preview::Ptr const& preview)
263void Scope::onPreviewReady(unity::dash::LocalResult const& /* result */, unity::dash::Preview::Ptr const& preview)
263{
264{
265+ Q_EMIT activated();
264 auto prv = Preview::newFromUnityPreview(preview);
266 auto prv = Preview::newFromUnityPreview(preview);
265 // is this the best solution? QML may need to keep more than one preview instance around, so we can't own it.
267 // is this the best solution? QML may need to keep more than one preview instance around, so we can't own it.
266 // passing it by value is not possible.
268 // passing it by value is not possible.
plugins/Unity/scope.h+1
@@ -117,6 +117,7 @@ Q_SIGNALS:
117 void showDash();
117 void showDash();
118 void hideDash();
118 void hideDash();
119 void gotoUri(const QString &uri);
119 void gotoUri(const QString &uri);
120+ void activated();
120
121
121 void activateApplication(const QString &desktop);
122 void activateApplication(const QString &desktop);
122
123
po/ar.po+33−44
@@ -8,7 +8,7 @@ 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-10-11 12:12+0200\n"
10"POT-Creation-Date: 2013-10-11 12:12+0200\n"
11−"PO-Revision-Date: 2013-06-26 23:26+0000\n"
11+"PO-Revision-Date: 2013-10-20 19:41+0000\n"
12"Last-Translator: Ibrahim Saed <ibraheem5000@gmail.com>\n"
12"Last-Translator: Ibrahim Saed <ibraheem5000@gmail.com>\n"
13"Language-Team: Arabic <ar@li.org>\n"
13"Language-Team: Arabic <ar@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
@@ -16,8 +16,8 @@ msgstr ""
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n % 100 >= "
17"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n % 100 >= "
18"3 && n % 100 <= 10 ? 3 : n % 100 >= 11 && n % 100 <= 99 ? 4 : 5;\n"
18"3 && n % 100 <= 10 ? 3 : n % 100 >= 11 && n % 100 <= 99 ? 4 : 5;\n"
19−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
19+"X-Launchpad-Export-Date: 2013-10-21 05:04+0000\n"
20−"X-Generator: Launchpad (build 16799)\n"
20+"X-Generator: Launchpad (build 16807)\n"
21
21
22#: Applications/applications.js:23
22#: Applications/applications.js:23
23msgid "Phone"
23msgid "Phone"
@@ -65,7 +65,7 @@ msgstr "مشغّل الوسائط"
65
65
66#: Applications/applications.js:86
66#: Applications/applications.js:86
67msgid "Evernote"
67msgid "Evernote"
68−msgstr ""
68+msgstr "Evernote"
69
69
70#: Applications/applications.js:91
70#: Applications/applications.js:91
71msgid "Map"
71msgid "Map"
@@ -73,11 +73,11 @@ msgstr "الخريطة"
73
73
74#: Applications/applications.js:96
74#: Applications/applications.js:96
75msgid "Pinterest"
75msgid "Pinterest"
76−msgstr ""
76+msgstr "Pinterest"
77
77
78#: Applications/applications.js:101
78#: Applications/applications.js:101
79msgid "SoundCloud"
79msgid "SoundCloud"
80−msgstr ""
80+msgstr "SoundCloud"
81
81
82#: Applications/applications.js:106
82#: Applications/applications.js:106
83msgid "Wikipedia"
83msgid "Wikipedia"
@@ -89,49 +89,52 @@ msgstr "يوتيوب"
89
89
90#: Components/EdgeDemoOverlay.qml:149
90#: Components/EdgeDemoOverlay.qml:149
91msgid "Skip intro"
91msgid "Skip intro"
92−msgstr ""
92+msgstr "تخطي المقدمة"
93
93
94#: Components/EdgeDemo.qml:112
94#: Components/EdgeDemo.qml:112
95msgid "Right edge"
95msgid "Right edge"
96−msgstr ""
96+msgstr "الحافة اليمنى"
97
97
98#: Components/EdgeDemo.qml:113
98#: Components/EdgeDemo.qml:113
99msgid "Try swiping from the right edge to unlock the phone"
99msgid "Try swiping from the right edge to unlock the phone"
100−msgstr ""
100+msgstr "اسحب اصبعك من الحافة اليمنى لفتح قفل الهاتف"
101
101
102#: Components/EdgeDemo.qml:144
102#: Components/EdgeDemo.qml:144
103msgid "Top edge"
103msgid "Top edge"
104−msgstr ""
104+msgstr "الحافة العلوية"
105
105
106#: Components/EdgeDemo.qml:145
106#: Components/EdgeDemo.qml:145
107msgid "Try swiping from the top edge to access the indicators"
107msgid "Try swiping from the top edge to access the indicators"
108−msgstr ""
108+msgstr "اسحب اصبعك من الحافة العلوية للوصول إلى المؤشرات"
109
109
110#: Components/EdgeDemo.qml:170
110#: Components/EdgeDemo.qml:170
111msgid "Close"
111msgid "Close"
112−msgstr ""
112+msgstr "أغلق"
113
113
114#: Components/EdgeDemo.qml:171
114#: Components/EdgeDemo.qml:171
115msgid "Swipe up again to close the settings screen"
115msgid "Swipe up again to close the settings screen"
116−msgstr ""
116+msgstr "اسحب اصعبك لأعلى مرة أخرى لغلق شاشة الإعدادات"
117
117
118#: Components/EdgeDemo.qml:198
118#: Components/EdgeDemo.qml:198
119msgid "Left edge"
119msgid "Left edge"
120−msgstr ""
120+msgstr "الحافة اليسرى"
121
121
122#: Components/EdgeDemo.qml:199
122#: Components/EdgeDemo.qml:199
123msgid "Swipe from the left to reveal the launcher for quick access to apps"
123msgid "Swipe from the left to reveal the launcher for quick access to apps"
124msgstr ""
124msgstr ""
125+"اسحب أصبعك من الحافة اليسرى لإظهار شريط يونتي للوصول السريع للتطبيقات"
125
126
126#: Components/EdgeDemo.qml:226
127#: Components/EdgeDemo.qml:226
127msgid "Well done"
128msgid "Well done"
128−msgstr ""
129+msgstr "أحسنت!"
129
130
130#: Components/EdgeDemo.qml:227
131#: Components/EdgeDemo.qml:227
131msgid ""
132msgid ""
132"You have now mastered the edge gestures and can start using the "
133"You have now mastered the edge gestures and can start using the "
133"phone<br><br>Tap anywhere to start"
134"phone<br><br>Tap anywhere to start"
134msgstr ""
135msgstr ""
136+"لقد أتقنت الآن إيماءات الحواف ويمكنك البدء باستخدام الهاتف<br><br>انقر في أي "
137+"مكان للبدء"
135
138
136#: Components/PageHeader.qml:341
139#: Components/PageHeader.qml:341
137msgid "Recent searches"
140msgid "Recent searches"
@@ -142,7 +145,7 @@ msgstr "عمليات البحث الأخيرة"
142#: Dash/Apps/AppInfo.qml:73 Dash/Previews/Header.qml:93
145#: Dash/Apps/AppInfo.qml:73 Dash/Previews/Header.qml:93
143#, qt-format
146#, qt-format
144msgid "(%1)"
147msgid "(%1)"
145−msgstr ""
148+msgstr "(%1)"
146
149
147#. TRANSLATORS: Number of persons who wrote reviews for this app
150#. TRANSLATORS: Number of persons who wrote reviews for this app
148#. TRANSLATORS: Number of persons who wrote reviews for this app/video/whatever
151#. TRANSLATORS: Number of persons who wrote reviews for this app/video/whatever
@@ -150,32 +153,36 @@ msgstr ""
150#, qt-format
153#, qt-format
151msgid "%1 review"
154msgid "%1 review"
152msgid_plural "%1 reviews"
155msgid_plural "%1 reviews"
153−msgstr[0] ""
156+msgstr[0] "لا تعليقات (%1)"
157+msgstr[1] "تعليق واحد (%1)"
158+msgstr[2] "تعليقان (%1)"
154−msgstr[1] ""
159+msgstr[3] "%1 تعليقات"
160+msgstr[4] "%1 تعليقا"
161+msgstr[5] "%1 تعليق"
155
162
156#: Dash/Apps/AppPreview.qml:126
163#: Dash/Apps/AppPreview.qml:126
157msgid "Rate this"
164msgid "Rate this"
158−msgstr ""
165+msgstr "قيّم ذلك"
159
166
160#: Dash/Previews/Reviews.qml:74
167#: Dash/Previews/Reviews.qml:74
161msgid "Add a review"
168msgid "Add a review"
162−msgstr ""
169+msgstr "أضف تعليق"
163
170
164#: Dash/Previews/Reviews.qml:86
171#: Dash/Previews/Reviews.qml:86
165msgid "Review"
172msgid "Review"
166−msgstr ""
173+msgstr "التعليق"
167
174
168#: Dash/Previews/Reviews.qml:121
175#: Dash/Previews/Reviews.qml:121
169msgid "Send"
176msgid "Send"
170−msgstr ""
177+msgstr "أرسل"
171
178
172#: Dash/Previews/Reviews.qml:139
179#: Dash/Previews/Reviews.qml:139
173msgid "Comments:"
180msgid "Comments:"
174−msgstr ""
181+msgstr "التعليقات:"
175
182
176#: Greeter/Lockscreen.qml:142
183#: Greeter/Lockscreen.qml:142
177msgid "Emergency Call"
184msgid "Emergency Call"
178−msgstr ""
185+msgstr "مكالمة طوارئ"
179
186
180#: Greeter/PassphraseLockscreen.qml:59
187#: Greeter/PassphraseLockscreen.qml:59
181#, qt-format
188#, qt-format
@@ -216,27 +223,9 @@ msgstr "عذراً، لا يوجد ما يطابق بحثك."
216
223
217#: plugins/Unity/showallfilteroption.cpp:41
224#: plugins/Unity/showallfilteroption.cpp:41
218msgid "All"
225msgid "All"
219−msgstr ""
226+msgstr "الكل"
220
227
221#: Shell.qml:438
228#: Shell.qml:438
222#, qt-format
229#, qt-format
223msgid "Please enter %1"
230msgid "Please enter %1"
224−msgstr ""
231+msgstr "من فضلك أدخِل %1"
225−
226−#~ msgid "Featured"
227−#~ msgstr "المميز"
228−
229−#~ msgid "Recent"
230−#~ msgstr "الحديث"
231−
232−#~ msgid "New Releases"
233−#~ msgstr "الإصدارات الجديدة"
234−
235−#~ msgid "Music"
236−#~ msgstr "موسيقى"
237−
238−#~ msgid "Popular Online"
239−#~ msgstr "الشائع على الإنترنت"
240−
241−#~ msgid "Videos"
242−#~ msgstr "فيديوهات"
po/de.po+11−28
@@ -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-10-11 12:12+0200\n"
10"POT-Creation-Date: 2013-10-11 12:12+0200\n"
11−"PO-Revision-Date: 2013-10-06 10:57+0000\n"
11+"PO-Revision-Date: 2013-10-18 12:26+0000\n"
12−"Last-Translator: SaGrLand <sg@samuel-greef.de>\n"
12+"Last-Translator: Phillip Sz <Unknown>\n"
13"Language-Team: German <de@li.org>\n"
13"Language-Team: German <de@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-10-13 05:43+0000\n"
18+"X-Launchpad-Export-Date: 2013-10-19 05:30+0000\n"
19−"X-Generator: Launchpad (build 16799)\n"
19+"X-Generator: Launchpad (build 16807)\n"
20
20
21#: Applications/applications.js:23
21#: Applications/applications.js:23
22msgid "Phone"
22msgid "Phone"
@@ -96,7 +96,7 @@ msgstr "Rechter Rand"
96
96
97#: Components/EdgeDemo.qml:113
97#: Components/EdgeDemo.qml:113
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 "Vom rechten Rand her wischen, um das Gerät zu entsperren"
100
100
101#: Components/EdgeDemo.qml:144
101#: Components/EdgeDemo.qml:144
102msgid "Top edge"
102msgid "Top edge"
@@ -104,7 +104,7 @@ msgstr "Oberer Rand"
104
104
105#: Components/EdgeDemo.qml:145
105#: Components/EdgeDemo.qml:145
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 "Vom oberen Rand her wischen, um die Statusmeldungen anzuzeigen"
108
108
109#: Components/EdgeDemo.qml:170
109#: Components/EdgeDemo.qml:170
110msgid "Close"
110msgid "Close"
@@ -112,7 +112,7 @@ msgstr "Schließen"
112
112
113#: Components/EdgeDemo.qml:171
113#: Components/EdgeDemo.qml:171
114msgid "Swipe up again to close the settings screen"
114msgid "Swipe up again to close the settings screen"
115−msgstr ""
115+msgstr "Nochmals nach oben wischen, um das Einstellungsmenü zu schließen"
116
116
117#: Components/EdgeDemo.qml:198
117#: Components/EdgeDemo.qml:198
118msgid "Left edge"
118msgid "Left edge"
@@ -121,6 +121,8 @@ msgstr "Linker Rand"
121#: Components/EdgeDemo.qml:199
121#: Components/EdgeDemo.qml:199
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+"Von links wischen, um die Seitenleiste für den Anwendungs-Schnellzugriff "
125+"aufzurufen"
124
126
125#: Components/EdgeDemo.qml:226
127#: Components/EdgeDemo.qml:226
126msgid "Well done"
128msgid "Well done"
@@ -131,6 +133,8 @@ msgid ""
131"You have now mastered the edge gestures and can start using the "
133"You have now mastered the edge gestures and can start using the "
132"phone<br><br>Tap anywhere to start"
134"phone<br><br>Tap anywhere to start"
133msgstr ""
135msgstr ""
136+"Sie haben nun die Einführung abgeschlossen und können Ihr Gerät nun "
137+"verwenden<br><br>Antippen um zu starten"
134
138
135#: Components/PageHeader.qml:341
139#: Components/PageHeader.qml:341
136msgid "Recent searches"
140msgid "Recent searches"
@@ -221,24 +225,3 @@ msgstr "Alle"
221#, qt-format
225#, qt-format
222msgid "Please enter %1"
226msgid "Please enter %1"
223msgstr "Bitte geben Sie %1 ein"
227msgstr "Bitte geben Sie %1 ein"
224−
225−#~ msgid "Featured"
226−#~ msgstr "Empfehlungen"
227−
228−#~ msgid "Recent"
229−#~ msgstr "Zuletzt verwendet"
230−
231−#~ msgid "New Releases"
232−#~ msgstr "Neue Veröffentlichungen"
233−
234−#~ msgid "Top Charting"
235−#~ msgstr "Top Tabellenplatz"
236−
237−#~ msgid "Music"
238−#~ msgstr "Musik"
239−
240−#~ msgid "Popular Online"
241−#~ msgstr "Online Beliebt"
242−
243−#~ msgid "Videos"
244−#~ msgstr "Videos"
po/fa.ponew file+223
@@ -0,0 +1,223 @@
1+# Persian translation for unity8
2+# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013
3+# This file is distributed under the same license as the unity8 package.
4+# FIRST AUTHOR <EMAIL@ADDRESS>, 2013.
5+#
6+msgid ""
7+msgstr ""
8+"Project-Id-Version: unity8\n"
9+"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10+"POT-Creation-Date: 2013-10-11 12:12+0200\n"
11+"PO-Revision-Date: 2013-10-31 19:02+0000\n"
12+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13+"Language-Team: Persian <fa@li.org>\n"
14+"MIME-Version: 1.0\n"
15+"Content-Type: text/plain; charset=UTF-8\n"
16+"Content-Transfer-Encoding: 8bit\n"
17+"Plural-Forms: nplurals=1; plural=0;\n"
18+"X-Launchpad-Export-Date: 2013-11-01 06:44+0000\n"
19+"X-Generator: Launchpad (build 16820)\n"
20+
21+#: Applications/applications.js:23
22+msgid "Phone"
23+msgstr ""
24+
25+#: Applications/applications.js:29
26+msgid "Camera"
27+msgstr ""
28+
29+#: Applications/applications.js:35
30+msgid "Gallery"
31+msgstr ""
32+
33+#: Applications/applications.js:40
34+msgid "Facebook"
35+msgstr ""
36+
37+#: Applications/applications.js:46
38+msgid "Browser"
39+msgstr ""
40+
41+#: Applications/applications.js:51
42+msgid "Twitter"
43+msgstr ""
44+
45+#: Applications/applications.js:57
46+msgid "GMail"
47+msgstr ""
48+
49+#: Applications/applications.js:62
50+msgid "Weather"
51+msgstr ""
52+
53+#: Applications/applications.js:68
54+msgid "Notepad"
55+msgstr ""
56+
57+#: Applications/applications.js:74
58+msgid "Calendar"
59+msgstr ""
60+
61+#: Applications/applications.js:80
62+msgid "Media Player"
63+msgstr ""
64+
65+#: Applications/applications.js:86
66+msgid "Evernote"
67+msgstr ""
68+
69+#: Applications/applications.js:91
70+msgid "Map"
71+msgstr ""
72+
73+#: Applications/applications.js:96
74+msgid "Pinterest"
75+msgstr ""
76+
77+#: Applications/applications.js:101
78+msgid "SoundCloud"
79+msgstr ""
80+
81+#: Applications/applications.js:106
82+msgid "Wikipedia"
83+msgstr ""
84+
85+#: Applications/applications.js:111
86+msgid "YouTube"
87+msgstr ""
88+
89+#: Components/EdgeDemoOverlay.qml:149
90+msgid "Skip intro"
91+msgstr ""
92+
93+#: Components/EdgeDemo.qml:112
94+msgid "Right edge"
95+msgstr ""
96+
97+#: Components/EdgeDemo.qml:113
98+msgid "Try swiping from the right edge to unlock the phone"
99+msgstr ""
100+
101+#: Components/EdgeDemo.qml:144
102+msgid "Top edge"
103+msgstr ""
104+
105+#: Components/EdgeDemo.qml:145
106+msgid "Try swiping from the top edge to access the indicators"
107+msgstr ""
108+
109+#: Components/EdgeDemo.qml:170
110+msgid "Close"
111+msgstr ""
112+
113+#: Components/EdgeDemo.qml:171
114+msgid "Swipe up again to close the settings screen"
115+msgstr ""
116+
117+#: Components/EdgeDemo.qml:198
118+msgid "Left edge"
119+msgstr ""
120+
121+#: Components/EdgeDemo.qml:199
122+msgid "Swipe from the left to reveal the launcher for quick access to apps"
123+msgstr ""
124+
125+#: Components/EdgeDemo.qml:226
126+msgid "Well done"
127+msgstr ""
128+
129+#: Components/EdgeDemo.qml:227
130+msgid ""
131+"You have now mastered the edge gestures and can start using the "
132+"phone<br><br>Tap anywhere to start"
133+msgstr ""
134+
135+#: Components/PageHeader.qml:341
136+msgid "Recent searches"
137+msgstr ""
138+
139+#. TRANSLATORS: Number of persons who rated this app
140+#. TRANSLATORS: Number of persons who rated this app/video/whatever
141+#: Dash/Apps/AppInfo.qml:73 Dash/Previews/Header.qml:93
142+#, qt-format
143+msgid "(%1)"
144+msgstr ""
145+
146+#. TRANSLATORS: Number of persons who wrote reviews for this app
147+#. TRANSLATORS: Number of persons who wrote reviews for this app/video/whatever
148+#: Dash/Apps/AppInfo.qml:85 Dash/Previews/Header.qml:105
149+#, qt-format
150+msgid "%1 review"
151+msgid_plural "%1 reviews"
152+msgstr[0] ""
153+msgstr[1] ""
154+
155+#: Dash/Apps/AppPreview.qml:126
156+msgid "Rate this"
157+msgstr ""
158+
159+#: Dash/Previews/Reviews.qml:74
160+msgid "Add a review"
161+msgstr ""
162+
163+#: Dash/Previews/Reviews.qml:86
164+msgid "Review"
165+msgstr ""
166+
167+#: Dash/Previews/Reviews.qml:121
168+msgid "Send"
169+msgstr ""
170+
171+#: Dash/Previews/Reviews.qml:139
172+msgid "Comments:"
173+msgstr ""
174+
175+#: Greeter/Lockscreen.qml:142
176+msgid "Emergency Call"
177+msgstr ""
178+
179+#: Greeter/PassphraseLockscreen.qml:59
180+#, qt-format
181+msgid "Hello %1"
182+msgstr ""
183+
184+#: Greeter/PassphraseLockscreen.qml:59
185+msgid "Hello"
186+msgstr ""
187+
188+#: Hud/HudParametrizedActionsPage.qml:132
189+msgid "Confirm"
190+msgstr ""
191+
192+#: Hud/Hud.qml:61 Hud/Hud.qml:99 Hud/Hud.qml:372
193+msgid "Type or say a command"
194+msgstr ""
195+
196+#: Hud/Hud.qml:84
197+msgid "Loading. Please Wait..."
198+msgstr ""
199+
200+#: Hud/Hud.qml:89
201+msgid "Speak Now..."
202+msgstr ""
203+
204+#: Hud/Hud.qml:94
205+msgid "Speaking..."
206+msgstr ""
207+
208+#: Panel/SearchIndicator.qml:27
209+msgid "Search"
210+msgstr ""
211+
212+#: plugins/Unity/scope.cpp:398
213+msgid "Sorry, there is nothing that matches your search."
214+msgstr ""
215+
216+#: plugins/Unity/showallfilteroption.cpp:41
217+msgid "All"
218+msgstr ""
219+
220+#: Shell.qml:438
221+#, qt-format
222+msgid "Please enter %1"
223+msgstr ""
po/ta.ponew file+229
@@ -0,0 +1,229 @@
1+# Tamil translation for unity8
2+# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013
3+# This file is distributed under the same license as the unity8 package.
4+# FIRST AUTHOR <EMAIL@ADDRESS>, 2013.
5+# துளசி <Thangamani Arun<thangam.arunx@gmail.com>>, 2013.
6+msgid ""
7+msgstr ""
8+"Project-Id-Version: unity8\n"
9+"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10+"POT-Creation-Date: 2013-10-11 12:12+0200\n"
11+"PO-Revision-Date: 2013-10-24 11:36+0000\n"
12+"Last-Translator: Thangamani Arun - தங்கமணி அருண் <thangam.arunx@gmail.com>\n"
13+"Language-Team: Ubuntu Tamil Team/Free Tamil Computing/Thamizha\n"
14+"MIME-Version: 1.0\n"
15+"Content-Type: text/plain; charset=UTF-8\n"
16+"Content-Transfer-Encoding: 8bit\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18+"X-Launchpad-Export-Date: 2013-10-25 06:05+0000\n"
19+"X-Generator: Launchpad (build 16810)\n"
20+"Language: ta\n"
21+
22+#: Applications/applications.js:23
23+msgid "Phone"
24+msgstr "தொலைபேசி"
25+
26+#: Applications/applications.js:29
27+msgid "Camera"
28+msgstr "புகைப்பட கருவி"
29+
30+#: Applications/applications.js:35
31+msgid "Gallery"
32+msgstr "படத்தொகுப்பு"
33+
34+#: Applications/applications.js:40
35+msgid "Facebook"
36+msgstr "முகநுல்"
37+
38+#: Applications/applications.js:46
39+msgid "Browser"
40+msgstr "உலாவி"
41+
42+#: Applications/applications.js:51
43+msgid "Twitter"
44+msgstr "ட்விட்டர்"
45+
46+#: Applications/applications.js:57
47+msgid "GMail"
48+msgstr "ஜிமெயில்"
49+
50+#: Applications/applications.js:62
51+msgid "Weather"
52+msgstr "வானிலை"
53+
54+#: Applications/applications.js:68
55+msgid "Notepad"
56+msgstr "குறிப்புபலகை"
57+
58+#: Applications/applications.js:74
59+msgid "Calendar"
60+msgstr "நாள்காட்டி"
61+
62+#: Applications/applications.js:80
63+msgid "Media Player"
64+msgstr "ஊடக இயக்கி"
65+
66+#: Applications/applications.js:86
67+msgid "Evernote"
68+msgstr "எப்போதும்குறிப்பெடு"
69+
70+#: Applications/applications.js:91
71+msgid "Map"
72+msgstr "வரைபடம்"
73+
74+#: Applications/applications.js:96
75+msgid "Pinterest"
76+msgstr "விசைத்தட்டு"
77+
78+#: Applications/applications.js:101
79+msgid "SoundCloud"
80+msgstr "ஒலி கிளவுட்"
81+
82+#: Applications/applications.js:106
83+msgid "Wikipedia"
84+msgstr "விக்கிப்பீடியா"
85+
86+#: Applications/applications.js:111
87+msgid "YouTube"
88+msgstr "யூட்யுப்"
89+
90+#: Components/EdgeDemoOverlay.qml:149
91+msgid "Skip intro"
92+msgstr "அறிமுகத்தை தவிர்"
93+
94+#: Components/EdgeDemo.qml:112
95+msgid "Right edge"
96+msgstr "வலது விளிம்பு"
97+
98+#: Components/EdgeDemo.qml:113
99+msgid "Try swiping from the right edge to unlock the phone"
100+msgstr "தொலைபேசியை திறக்க வலது முனையிலிருந்து தேய்த்து முயற்சி செய்"
101+
102+#: Components/EdgeDemo.qml:144
103+msgid "Top edge"
104+msgstr "மேல் விளிம்பு"
105+
106+#: Components/EdgeDemo.qml:145
107+msgid "Try swiping from the top edge to access the indicators"
108+msgstr "காட்டிகளை அனுக மேல் விளிம்பிலிருந்து தேய்த்து முயற்சி செய்"
109+
110+#: Components/EdgeDemo.qml:170
111+msgid "Close"
112+msgstr "மூடு"
113+
114+#: Components/EdgeDemo.qml:171
115+msgid "Swipe up again to close the settings screen"
116+msgstr "அமைவுகளின் திரையை மூட மறுபடியும் மேலே தேய்க்கவும்"
117+
118+#: Components/EdgeDemo.qml:198
119+msgid "Left edge"
120+msgstr "இடது விளிம்பு"
121+
122+#: Components/EdgeDemo.qml:199
123+msgid "Swipe from the left to reveal the launcher for quick access to apps"
124+msgstr ""
125+"பயன்பாடுகளின் விரைவான அணுகலை பெறத்தேவையான தொடக்கியை இடமிருந்து தேய்த்தால் "
126+"வெளிப்படும்"
127+
128+#: Components/EdgeDemo.qml:226
129+msgid "Well done"
130+msgstr "நன்றாக செய்தீர்கள்"
131+
132+#: Components/EdgeDemo.qml:227
133+msgid ""
134+"You have now mastered the edge gestures and can start using the "
135+"phone<br><br>Tap anywhere to start"
136+msgstr ""
137+"நீங்கள் இப்போது விளிம்பு சைகைகளில் கைதேர்ந்தவர் மற்றும் தொலைபேசி "
138+"பயன்படுத்த தொடங்கவும் <br> <br>தட்டுவதன் மூலம் எங்க வேண்டுமானாலும் "
139+"தொடங்கலாம்."
140+
141+#: Components/PageHeader.qml:341
142+msgid "Recent searches"
143+msgstr "கடையாக தேடப்பட்டவைகள்"
144+
145+#. TRANSLATORS: Number of persons who rated this app
146+#. TRANSLATORS: Number of persons who rated this app/video/whatever
147+#: Dash/Apps/AppInfo.qml:73 Dash/Previews/Header.qml:93
148+#, qt-format
149+msgid "(%1)"
150+msgstr "(%1)"
151+
152+#. TRANSLATORS: Number of persons who wrote reviews for this app
153+#. TRANSLATORS: Number of persons who wrote reviews for this app/video/whatever
154+#: Dash/Apps/AppInfo.qml:85 Dash/Previews/Header.qml:105
155+#, qt-format
156+msgid "%1 review"
157+msgid_plural "%1 reviews"
158+msgstr[0] "%1 பரிசீலனை"
159+msgstr[1] "%1 பரிசீலனைகள்"
160+
161+#: Dash/Apps/AppPreview.qml:126
162+msgid "Rate this"
163+msgstr "இதை மதிப்பிடு"
164+
165+#: Dash/Previews/Reviews.qml:74
166+msgid "Add a review"
167+msgstr "ஒரு பரிசீலனையை சேர்"
168+
169+#: Dash/Previews/Reviews.qml:86
170+msgid "Review"
171+msgstr "மறுஆய்வு"
172+
173+#: Dash/Previews/Reviews.qml:121
174+msgid "Send"
175+msgstr "அனுப்பு"
176+
177+#: Dash/Previews/Reviews.qml:139
178+msgid "Comments:"
179+msgstr "கருத்துகள்:"
180+
181+#: Greeter/Lockscreen.qml:142
182+msgid "Emergency Call"
183+msgstr "அவசர அழைப்பு"
184+
185+#: Greeter/PassphraseLockscreen.qml:59
186+#, qt-format
187+msgid "Hello %1"
188+msgstr "வணக்கம் %1"
189+
190+#: Greeter/PassphraseLockscreen.qml:59
191+msgid "Hello"
192+msgstr "வணக்கம்"
193+
194+#: Hud/HudParametrizedActionsPage.qml:132
195+msgid "Confirm"
196+msgstr "உறுதிப்படுத்து"
197+
198+#: Hud/Hud.qml:61 Hud/Hud.qml:99 Hud/Hud.qml:372
199+msgid "Type or say a command"
200+msgstr "ஒரு கட்டளையை உச்சரி அல்லது தட்டச்சுசெய்"
201+
202+#: Hud/Hud.qml:84
203+msgid "Loading. Please Wait..."
204+msgstr "ஏற்றுகிறது. தயைசெய்து காத்திருக்கவும்..."
205+
206+#: Hud/Hud.qml:89
207+msgid "Speak Now..."
208+msgstr "இப்பொது பேசு..."
209+
210+#: Hud/Hud.qml:94
211+msgid "Speaking..."
212+msgstr "பேசுகிறது..."
213+
214+#: Panel/SearchIndicator.qml:27
215+msgid "Search"
216+msgstr "தெடு"
217+
218+#: plugins/Unity/scope.cpp:398
219+msgid "Sorry, there is nothing that matches your search."
220+msgstr "மன்னிக்கவும், உங்களுடைய தேடலுக்கு தகுந்த பொருத்தம் கிடைக்கவில்லை."
221+
222+#: plugins/Unity/showallfilteroption.cpp:41
223+msgid "All"
224+msgstr "அனைத்தும்"
225+
226+#: Shell.qml:438
227+#, qt-format
228+msgid "Please enter %1"
229+msgstr "தயவுச்செய்து %1 உள்ளிடு"
po/uk.po+6−25
@@ -8,7 +8,7 @@ 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-10-11 12:12+0200\n"
10"POT-Creation-Date: 2013-10-11 12:12+0200\n"
11−"PO-Revision-Date: 2013-09-28 07:54+0000\n"
11+"PO-Revision-Date: 2013-10-19 16:44+0000\n"
12"Last-Translator: Yuri Chornoivan <yurchor@gmail.com>\n"
12"Last-Translator: Yuri Chornoivan <yurchor@gmail.com>\n"
13"Language-Team: Ukrainian <uk@li.org>\n"
13"Language-Team: Ukrainian <uk@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
@@ -16,8 +16,8 @@ msgstr ""
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
17"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
18"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
18"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
19+"X-Launchpad-Export-Date: 2013-10-20 05:20+0000\n"
20−"X-Generator: Launchpad (build 16799)\n"
20+"X-Generator: Launchpad (build 16807)\n"
21
21
22#: Applications/applications.js:23
22#: Applications/applications.js:23
23msgid "Phone"
23msgid "Phone"
@@ -135,6 +135,8 @@ msgid ""
135"You have now mastered the edge gestures and can start using the "
135"You have now mastered the edge gestures and can start using the "
136"phone<br><br>Tap anywhere to start"
136"phone<br><br>Tap anywhere to start"
137msgstr ""
137msgstr ""
138+"Тепер жести налаштовано, ви можете почати користуватися "
139+"телефоном.<br><br>Натисніть у довільному місці екрана."
138
140
139#: Components/PageHeader.qml:341
141#: Components/PageHeader.qml:341
140msgid "Recent searches"
142msgid "Recent searches"
@@ -220,30 +222,9 @@ msgstr "Вибачте, для вказаних вами ключових слі
220
222
221#: plugins/Unity/showallfilteroption.cpp:41
223#: plugins/Unity/showallfilteroption.cpp:41
222msgid "All"
224msgid "All"
223−msgstr ""
225+msgstr "Усе"
224
226
225#: Shell.qml:438
227#: Shell.qml:438
226#, qt-format
228#, qt-format
227msgid "Please enter %1"
229msgid "Please enter %1"
228msgstr "Будь ласка, введіть %1"
230msgstr "Будь ласка, введіть %1"
229−
230−#~ msgid "Featured"
231−#~ msgstr "Рекомендовані"
232−
233−#~ msgid "Recent"
234−#~ msgstr "Нещодавні"
235−
236−#~ msgid "New Releases"
237−#~ msgstr "Нові випуски"
238−
239−#~ msgid "Top Charting"
240−#~ msgstr "З високими оцінками"
241−
242−#~ msgid "Music"
243−#~ msgstr "Музика"
244−
245−#~ msgid "Popular Online"
246−#~ msgstr "Популярне у мережі"
247−
248−#~ msgid "Videos"
249−#~ msgstr "Відео"
run_on_device+24−4
@@ -1,4 +1,4 @@
1−#!/bin/sh
1+#!/bin/bash
2CODE_DIR=shell
2CODE_DIR=shell
3USER=phablet
3USER=phablet
4USER_ID=32011
4USER_ID=32011
@@ -17,6 +17,8 @@ SUDO="echo $PASSWORD | sudo -S"
17NUM_JOBS='$(( `grep -c ^processor /proc/cpuinfo` + 1 ))'
17NUM_JOBS='$(( `grep -c ^processor /proc/cpuinfo` + 1 ))'
18FLIPPED=false
18FLIPPED=false
19CHROOT_PREFIX="/data/ubuntu"
19CHROOT_PREFIX="/data/ubuntu"
20+SSH_WAS_STARTED=0
21+SSH_STARTED=0
20
22
21usage() {
23usage() {
22 echo "usage: run_on_device [OPTIONS]\n"
24 echo "usage: run_on_device [OPTIONS]\n"
@@ -32,7 +34,15 @@ usage() {
32 exit 1
34 exit 1
33}
35}
34
36
37+start_ssh() {
38+ if [ $SSH_STARTED -eq 0 ]; then
39+ exec_with_adb initctl start ssh
40+ SSH_STARTED=1
41+ fi
42+}
43+
35exec_with_ssh() {
44exec_with_ssh() {
45+ start_ssh
36 ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -t $USER@$TARGET_IP -p $TARGET_SSH_PORT sudo -u $USER -i bash -ic \"$@\"
46 ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -t $USER@$TARGET_IP -p $TARGET_SSH_PORT sudo -u $USER -i bash -ic \"$@\"
37}
47}
38
48
@@ -52,7 +62,7 @@ adb_root() {
52install_ssh_key() {
62install_ssh_key() {
53 ssh-keygen -R $TARGET_IP
63 ssh-keygen -R $TARGET_IP
54 HOME_DIR="${CHROOT_PREFIX}/home/phablet"
64 HOME_DIR="${CHROOT_PREFIX}/home/phablet"
55− adb push ~/.ssh/phone_rsa.pub $HOME_DIR/.ssh/authorized_keys
65+ adb push ~/.ssh/id_rsa.pub $HOME_DIR/.ssh/authorized_keys
56 adb shell chown $USER_ID:$USER_ID $HOME_DIR/.ssh
66 adb shell chown $USER_ID:$USER_ID $HOME_DIR/.ssh
57 adb shell chown $USER_ID:$USER_ID $HOME_DIR/.ssh/authorized_keys
67 adb shell chown $USER_ID:$USER_ID $HOME_DIR/.ssh/authorized_keys
58 adb shell chmod 700 $HOME_DIR/.ssh
68 adb shell chmod 700 $HOME_DIR/.ssh
@@ -65,8 +75,6 @@ setup_adb_forwarding() {
65}
75}
66
76
67install_dependencies() {
77install_dependencies() {
68− exec_with_adb rm /etc/init/ssh.override
69− exec_with_adb service ssh start
70 exec_with_adb apt-get update
78 exec_with_adb apt-get update
71 exec_with_ssh $SUDO apt-get -y install build-essential rsync bzr ccache gdb ninja-build devscripts equivs
79 exec_with_ssh $SUDO apt-get -y install build-essential rsync bzr ccache gdb ninja-build devscripts equivs
72}
80}
@@ -74,6 +82,7 @@ install_dependencies() {
74sync_code() {
82sync_code() {
75 [ -e .bzr ] && bzr export --uncommitted --format=dir /tmp/$CODE_DIR
83 [ -e .bzr ] && bzr export --uncommitted --format=dir /tmp/$CODE_DIR
76 [ -e .git ] && git checkout-index -a -f --prefix=/tmp/$CODE_DIR/
84 [ -e .git ] && git checkout-index -a -f --prefix=/tmp/$CODE_DIR/
85+ start_ssh
77 rsync -crlOzv --delete --exclude builddir -e "ssh -p $TARGET_SSH_PORT -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no" /tmp/$CODE_DIR/ $USER@$TARGET_IP:$CODE_DIR/
86 rsync -crlOzv --delete --exclude builddir -e "ssh -p $TARGET_SSH_PORT -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no" /tmp/$CODE_DIR/ $USER@$TARGET_IP:$CODE_DIR/
78 rm -rf /tmp/$CODE_DIR
87 rm -rf /tmp/$CODE_DIR
79}
88}
@@ -96,7 +105,9 @@ run() {
96 fi
105 fi
97
106
98 exec_with_ssh "stop unity8"
107 exec_with_ssh "stop unity8"
108+ exec_with_ssh "start maliit-server"
99 exec_with_ssh "cd $CODE_DIR/ && ./run $ARGS -- $RUN_OPTIONS"
109 exec_with_ssh "cd $CODE_DIR/ && ./run $ARGS -- $RUN_OPTIONS"
110+ exec_with_ssh "stop maliit-server"
100 exec_with_ssh "start unity8"
111 exec_with_ssh "start unity8"
101}
112}
102
113
@@ -130,6 +141,12 @@ if [ "`adb shell 'grep -q Ubuntu /etc/lsb-release 2> /dev/null && echo -n FLIPPE
130 fi
141 fi
131fi
142fi
132
143
144+status_output=$(exec_with_adb initctl status ssh)
145+if [[ $status_output == "ssh start/running, process "* ]]; then
146+ SSH_WAS_STARTED=1
147+fi
148+SSH_STARTED=$SSH_WAS_STARTED
149+
133if $SETUP; then
150if $SETUP; then
134 echo "Setting up environment for building shell.."
151 echo "Setting up environment for building shell.."
135 install_ssh_key
152 install_ssh_key
@@ -144,3 +161,6 @@ else
144 run
161 run
145fi
162fi
146
163
164+if [ $SSH_WAS_STARTED -eq 0 ]; then
165+ exec_with_adb initctl stop ssh
166+fi
tests/autopilot/unity8/__init__.py+46−1
@@ -52,7 +52,11 @@ def get_mocks_library_path():
52 if running_installed_tests():
52 if running_installed_tests():
53 mock_path = "qml/mocks/"
53 mock_path = "qml/mocks/"
54 else:
54 else:
55+ mock_path = os.path.join(
56+ "../lib/",
57+ sysconfig.get_config_var('MULTIARCH'),
55− mock_path = "../lib/x86_64-linux-gnu/unity8/qml/mocks/"
58+ "unity8/qml/mocks/"
59+ )
56 lib_path = get_lib_path()
60 lib_path = get_lib_path()
57 ld_library_path = os.path.abspath(
61 ld_library_path = os.path.abspath(
58 os.path.join(
62 os.path.join(
@@ -84,6 +88,47 @@ def get_binary_path(binary="unity8"):
84 return binary_path
88 return binary_path
85
89
86
90
91+def get_data_dirs():
92+ """Prepend a mock data path to XDG_DATA_DIRS."""
93+ if running_installed_tests():
94+ data_path = "/usr/share/unity8/mocks/data"
95+ else:
96+ data_path = "../../mocks/data"
97+ full_data_path = os.path.abspath(
98+ os.path.join(
99+ os.path.dirname(__file__),
100+ data_path
101+ )
102+ )
103+
104+ if os.path.exists(full_data_path):
105+ xdg_path = _get_xdg_env_path()
106+ if xdg_path is not None:
107+ return "{0}:{1}".format(full_data_path, xdg_path)
108+ else:
109+ return full_data_path
110+ else:
111+ return None
112+
113+
114+def _get_xdg_env_path():
115+ path = os.getenv("XDG_DATA_DIRS")
116+ if path is None:
117+ path = _get_xdg_upstart_env()
118+ return path
119+
120+
121+def _get_xdg_upstart_env():
122+ try:
123+ return subprocess.check_output([
124+ "/sbin/initctl",
125+ "get-env",
126+ "--global",
127+ "XDG_DATA_DIRS"
128+ ]).rstrip()
129+ except subprocess.CalledProcessError:
130+ return None
131+
87def get_grid_size():
132def get_grid_size():
88 grid_size = os.getenv('GRID_UNIT_PX')
133 grid_size = os.getenv('GRID_UNIT_PX')
89 if grid_size is None:
134 if grid_size is None:
tests/autopilot/unity8/process_helpers.pynew file+116
@@ -0,0 +1,116 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+#
3+# Unity Autopilot Utilities
4+# Copyright (C) 2013 Canonical
5+#
6+# This program is free software: you can redistribute it and/or modify
7+# it under the terms of the GNU General Public License as published by
8+# the Free Software Foundation, either version 3 of the License, or
9+# (at your option) any later version.
10+#
11+# This program is distributed in the hope that it will be useful,
12+# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+# GNU General Public License for more details.
15+#
16+# You should have received a copy of the GNU General Public License
17+# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+#
19+
20+from autopilot.introspection import (
21+ get_proxy_object_for_existing_process,
22+ ProcessSearchError,
23+)
24+import subprocess
25+from unity8.shell.emulators import UnityEmulatorBase
26+from unity8.shell.emulators.main_window import MainWindow
27+
28+
29+class CannotAccessUnity(Exception):
30+ pass
31+
32+
33+def unlock_unity():
34+ """Helper function that attempts to unlock the unity greeter.
35+
36+ :raises RuntimeError: if the greeter attempts and fails to be unlocked.
37+
38+ :raises RuntimeWarning: when the greeter cannot be found because it is
39+ already unlocked.
40+ :raises CannotAccessUnity: if unity is not introspectable or cannot be
41+ found on dbus.
42+ :raises CannotAccessUnity: if unity's upstart status is not "start" or the
43+ upstart job cannot be found at all.
44+
45+ """
46+ pid = _get_unity_pid()
47+ try:
48+ unity = get_proxy_object_for_existing_process(
49+ pid=pid,
50+ emulator_base=UnityEmulatorBase,
51+ )
52+ main_window = MainWindow(unity)
53+
54+ greeter = main_window.get_greeter()
55+ if greeter is None:
56+ raise RuntimeWarning("Greeter appears to be already unlocked.")
57+ greeter.swipe()
58+ except ProcessSearchError as e:
59+ raise CannotAccessUnity(
60+ "Cannot introspect unity, make sure that it has been started "
61+ "with testability. Perhaps use the function "
62+ "'restart_unity_with_testability' this module provides."
63+ "(%s)"
64+ % str(e)
65+ )
66+
67+
68+def restart_unity_with_testability():
69+ """Restarts (or just starts) unity8 with the testability driver loaded
70+
71+ :raises subprocess.CalledProcessError: if unable to stop or start the
72+ unity8 upstart job.
73+
74+ """
75+ status = _get_unity_status()
76+ if "start/" in status:
77+ try:
78+ print("Stopping unity.")
79+ subprocess.check_call([
80+ 'initctl',
81+ 'stop',
82+ 'unity8',
83+ ])
84+ except subprocess.CalledProcessError as e:
85+ e.args += ("Failed to stop running instance of unity8", )
86+ raise
87+
88+ try:
89+ print("Starting unity with testability.")
90+ subprocess.check_call([
91+ 'initctl',
92+ 'start',
93+ 'unity8',
94+ 'QT_LOAD_TESTABILITY=1',
95+ ])
96+ except subprocess.CalledProcessError as e:
97+ e.args += ("Failed to start unity8 with testability ", )
98+ raise
99+
100+
101+def _get_unity_status():
102+ try:
103+ return subprocess.check_output([
104+ 'initctl',
105+ 'status',
106+ 'unity8'
107+ ])
108+ except subprocess.CalledProcessError as e:
109+ raise CannotAccessUnity("Unable to get unity's status: %s" % str(e))
110+
111+
112+def _get_unity_pid():
113+ status = _get_unity_status()
114+ if not "start/" in status:
115+ raise CannotAccessUnity("Unity is not in the running state.")
116+ return int(status.split()[-1])
tests/autopilot/unity8/shell/emulators/create_interactive_notification.pymode 100755 → 100644

Only the mode changed.

tests/autopilot/unity8/shell/emulators/dash.py+7−17
@@ -18,42 +18,32 @@
18#
18#
19
19
20from unity8.shell.emulators import UnityEmulatorBase
20from unity8.shell.emulators import UnityEmulatorBase
21−from time import sleep
21+
22
22
23class Dash(UnityEmulatorBase):
23class Dash(UnityEmulatorBase):
24
24
25 """An emulator that understands the Dash."""
25 """An emulator that understands the Dash."""
26
26
27 def get_home_applications_grid(self):
27 def get_home_applications_grid(self):
28− for i in range(1,10):
29− get_grid = self.get_scope('home').select_single(
28+ get_grid = self.get_scope('home').wait_select_single(
30− "GenericFilterGrid",
29+ "GenericFilterGrid",
31− objectName="applications.scope"
30+ objectName="applications.scope"
32− )
31+ )
33− if get_grid != None:
34− break
35− sleep(1)
36 return get_grid
32 return get_grid
37
33
38 def get_application_icon(self, text):
34 def get_application_icon(self, text):
39 """Returns a 'Tile' icon that has the text 'text' from the application
35 """Returns a 'Tile' icon that has the text 'text' from the application
40 grid.
36 grid.
41
37
42− Will return None if the icon isn't found.
43−
44 :param text: String containing the text of the icon to search for.
38 :param text: String containing the text of the icon to search for.
45
39
46 """
40 """
47 app_grid = self.get_home_applications_grid()
41 app_grid = self.get_home_applications_grid()
48− for i in range(1,10):
49− resp_grid = app_grid.select_single('ResponsiveGridView')
42+ resp_grid = app_grid.wait_select_single('ResponsiveGridView')
50− if resp_grid != None:
51− break
52− sleep(1)
53 return resp_grid.select_single('Tile', text=text)
43 return resp_grid.select_single('Tile', text=text)
54
44
55 def get_scope(self, scope_name='home'):
45 def get_scope(self, scope_name='home'):
56− dash_content = self.select_single(
46+ dash_content = self.wait_select_single(
57 'QQuickListView',
47 'QQuickListView',
58 objectName='dashContentList'
48 objectName='dashContentList'
59 )
49 )
tests/autopilot/unity8/shell/emulators/hud.py+12−4
@@ -26,6 +26,7 @@ from autopilot.input import Touch
26
26
27SwipeCoords = namedtuple('SwipeCoords', 'start_x end_x start_y end_y')
27SwipeCoords = namedtuple('SwipeCoords', 'start_x end_x start_y end_y')
28
28
29+
29class Hud(UnityEmulatorBase, DragMixin):
30class Hud(UnityEmulatorBase, DragMixin):
30
31
31 """An emulator that understands the Hud."""
32 """An emulator that understands the Hud."""
@@ -34,13 +35,18 @@ class Hud(UnityEmulatorBase, DragMixin):
34 """Swipes open the Hud."""
35 """Swipes open the Hud."""
35 self.touch = Touch.create()
36 self.touch = Touch.create()
36
37
37− window = self.get_root_instance().select_single('QQuickView')
38+ window = self.get_root_instance().wait_select_single('QQuickView')
38− hud_show_button = window.select_single("HudButton")
39+ hud_show_button = window.wait_select_single("HudButton")
39
40
40 swipe_coords = self.get_button_swipe_coords(window, hud_show_button)
41 swipe_coords = self.get_button_swipe_coords(window, hud_show_button)
41
42
42 self.touch.press(swipe_coords.start_x, swipe_coords.start_y)
43 self.touch.press(swipe_coords.start_x, swipe_coords.start_y)
43− self._drag(swipe_coords.start_x, swipe_coords.start_y, swipe_coords.start_x, swipe_coords.end_y)
44+ self._drag(
45+ swipe_coords.start_x,
46+ swipe_coords.start_y,
47+ swipe_coords.start_x,
48+ swipe_coords.end_y
49+ )
44 try:
50 try:
45 hud_show_button.opacity.wait_for(1.0)
51 hud_show_button.opacity.wait_for(1.0)
46 self.touch.release()
52 self.touch.release()
@@ -74,6 +80,8 @@ class Hud(UnityEmulatorBase, DragMixin):
74 start_x = int(main_view.x + (main_view.width / 2))
80 start_x = int(main_view.x + (main_view.width / 2))
75 end_x = start_x
81 end_x = start_x
76 start_y = main_view.y + (main_view.height - 3)
82 start_y = main_view.y + (main_view.height - 3)
83+ end_y = main_view.y + int(
77− end_y = main_view.y + int(hud_show_button.y + (hud_show_button.height/2))
84+ hud_show_button.y + (hud_show_button.height/2)
85+ )
78
86
79 return SwipeCoords(start_x, end_x, start_y, end_y)
87 return SwipeCoords(start_x, end_x, start_y, end_y)
tests/autopilot/unity8/shell/emulators/main_window.py+1−1
@@ -38,7 +38,7 @@ class MainWindow(object):
38 return self.app.select_single(Greeter)
38 return self.app.select_single(Greeter)
39
39
40 def get_greeter_content_loader(self):
40 def get_greeter_content_loader(self):
41− return self.app.select_single(
41+ return self.app.wait_select_single(
42 "QQuickLoader",
42 "QQuickLoader",
43 objectName="greeterContentLoader"
43 objectName="greeterContentLoader"
44 )
44 )
tests/autopilot/unity8/shell/tests/__init__.py+66−27
@@ -34,13 +34,14 @@ import logging
34import os.path
34import os.path
35import subprocess
35import subprocess
36import sys
36import sys
37−from testtools.matchers import Equals, NotEquals
37+from testtools.matchers import Equals
38
38
39from unity8 import (
39from unity8 import (
40 get_lib_path,
40 get_lib_path,
41 get_binary_path,
41 get_binary_path,
42 get_mocks_library_path,
42 get_mocks_library_path,
43 get_default_extra_mock_libraries,
43 get_default_extra_mock_libraries,
44+ get_data_dirs
44)
45)
45from unity8.shell.emulators import UnityEmulatorBase
46from unity8.shell.emulators import UnityEmulatorBase
46from unity8.shell.emulators.dash import Dash
47from unity8.shell.emulators.dash import Dash
@@ -52,7 +53,7 @@ logger = logging.getLogger(__name__)
52UNITYSHELL_GSETTINGS_SCHEMA = "org.compiz.unityshell"
53UNITYSHELL_GSETTINGS_SCHEMA = "org.compiz.unityshell"
53UNITYSHELL_GSETTINGS_PATH = "/org/compiz/profiles/unity/plugins/unityshell/"
54UNITYSHELL_GSETTINGS_PATH = "/org/compiz/profiles/unity/plugins/unityshell/"
54UNITYSHELL_LAUNCHER_KEY = "launcher-hide-mode"
55UNITYSHELL_LAUNCHER_KEY = "launcher-hide-mode"
55−UNITYSHELL_LAUNCHER_MODE = 1 # launcher hidden
56+UNITYSHELL_LAUNCHER_MODE = 1 # launcher hidden
56
57
57
58
58def _get_device_emulation_scenarios(devices='All'):
59def _get_device_emulation_scenarios(devices='All'):
@@ -92,17 +93,25 @@ class UnityTestCase(AutopilotTestCase):
92 ], stderr=subprocess.STDOUT)
93 ], stderr=subprocess.STDOUT)
93 except subprocess.CalledProcessError, e:
94 except subprocess.CalledProcessError, e:
94 sys.stderr.write(
95 sys.stderr.write(
95− "Error: `initctl status unity8` failed, most probably the unity8 session could not be found:\n\n"
96+ "Error: `initctl status unity8` failed, most probably the "
97+ "unity8 session could not be found:\n\n"
96 "{0}\n"
98 "{0}\n"
97− "Please install unity8 or copy data/unity8.conf to {1}\n".format(
99+ "Please install unity8 or copy data/unity8.conf to "
100+ "{1}\n".format(
98 e.output,
101 e.output,
99− os.path.join(os.getenv("XDG_CONFIG_HOME", os.path.join(os.getenv("HOME"), ".config")), "upstart")
102+ os.path.join(os.getenv("XDG_CONFIG_HOME",
103+ os.path.join(os.getenv("HOME"),
104+ ".config")
105+ ),
106+ "upstart")
100− ))
107+ )
108+ )
101 sys.exit(1)
109 sys.exit(1)
102
110
103 if "start/" in output:
111 if "start/" in output:
104 sys.stderr.write(
112 sys.stderr.write(
105− "Error: Unity is currently running, these tests require it to be 'stopped'.\n"
113+ "Error: Unity is currently running, these tests require it to "
114+ "be 'stopped'.\n"
106 "Please run this command before running these tests: \n"
115 "Please run this command before running these tests: \n"
107 "initctl stop unity8\n"
116 "initctl stop unity8\n"
108 )
117 )
@@ -110,11 +119,22 @@ class UnityTestCase(AutopilotTestCase):
110
119
111 def setUp(self):
120 def setUp(self):
112 super(UnityTestCase, self).setUp()
121 super(UnityTestCase, self).setUp()
122+ if (Gio is not None and
123+ UNITYSHELL_GSETTINGS_SCHEMA in
113− if Gio is not None and UNITYSHELL_GSETTINGS_SCHEMA in Gio.Settings.list_relocatable_schemas():
124+ Gio.Settings.list_relocatable_schemas()):
125+
114 # Hide Unity launcher
126 # Hide Unity launcher
115− self._unityshell_schema = Gio.Settings.new_with_path(UNITYSHELL_GSETTINGS_SCHEMA, UNITYSHELL_GSETTINGS_PATH)
127+ self._unityshell_schema = Gio.Settings.new_with_path(
128+ UNITYSHELL_GSETTINGS_SCHEMA,
129+ UNITYSHELL_GSETTINGS_PATH,
130+ )
116− self._launcher_hide_mode = self._unityshell_schema.get_int(UNITYSHELL_LAUNCHER_KEY)
131+ self._launcher_hide_mode = self._unityshell_schema.get_int(
132+ UNITYSHELL_LAUNCHER_KEY,
133+ )
117− self._unityshell_schema.set_int(UNITYSHELL_LAUNCHER_KEY, UNITYSHELL_LAUNCHER_MODE)
134+ self._unityshell_schema.set_int(
135+ UNITYSHELL_LAUNCHER_KEY,
136+ UNITYSHELL_LAUNCHER_MODE,
137+ )
118 self.addCleanup(self._reset_launcher)
138 self.addCleanup(self._reset_launcher)
119
139
120 self._proxy = None
140 self._proxy = None
@@ -122,6 +142,8 @@ class UnityTestCase(AutopilotTestCase):
122 self._qml_mock_enabled = True
142 self._qml_mock_enabled = True
123 self._environment = {}
143 self._environment = {}
124
144
145+ self._patch_data_dirs()
146+
125 #### FIXME: This is a work around re: lp:1238417 ####
147 #### FIXME: This is a work around re: lp:1238417 ####
126 if model() != "Desktop":
148 if model() != "Desktop":
127 from autopilot.input import _uinput
149 from autopilot.input import _uinput
@@ -133,7 +155,10 @@ class UnityTestCase(AutopilotTestCase):
133
155
134 def _reset_launcher(self):
156 def _reset_launcher(self):
135 """Reset Unity launcher hide mode"""
157 """Reset Unity launcher hide mode"""
136− self._unityshell_schema.set_int(UNITYSHELL_LAUNCHER_KEY, self._launcher_hide_mode)
158+ self._unityshell_schema.set_int(
159+ UNITYSHELL_LAUNCHER_KEY,
160+ self._launcher_hide_mode,
161+ )
137
162
138 def _setup_display_details(self):
163 def _setup_display_details(self):
139 scale_divisor = self._determine_geometry()
164 scale_divisor = self._determine_geometry()
@@ -214,7 +239,10 @@ class UnityTestCase(AutopilotTestCase):
214 def _upstart_reset_env(self, key, value):
239 def _upstart_reset_env(self, key, value):
215 logger.info("Resetting upstart env %s to %s", key, value)
240 logger.info("Resetting upstart env %s to %s", key, value)
216 if value is None:
241 if value is None:
242+ subprocess.call(
217− subprocess.call(["/sbin/initctl", "unset-env", key], stderr=subprocess.STDOUT)
243+ ["/sbin/initctl", "unset-env", key],
244+ stderr=subprocess.STDOUT,
245+ )
218 else:
246 else:
219 subprocess.call([
247 subprocess.call([
220 "/sbin/initctl",
248 "/sbin/initctl",
@@ -241,9 +269,13 @@ class UnityTestCase(AutopilotTestCase):
241 self._setup_extra_mock_environment_patch()
269 self._setup_extra_mock_environment_patch()
242
270
243 # FIXME: we shouldn't be doing this
271 # FIXME: we shouldn't be doing this
244− # $MIR_SOCKET, fallback to $XDG_RUNTIME_DIR/mir_socket and /tmp/mir_socket as last resort
272+ # $MIR_SOCKET, fallback to $XDG_RUNTIME_DIR/mir_socket and
273+ # /tmp/mir_socket as last resort
245 try:
274 try:
275+ os.unlink(
276+ os.getenv('MIR_SOCKET',
246− os.unlink(os.getenv('MIR_SOCKET', os.path.join(os.getenv('XDG_RUNTIME_DIR', "/tmp"), "mir_socket")))
277+ os.path.join(os.getenv('XDG_RUNTIME_DIR', "/tmp"),
278+ "mir_socket")))
247 except OSError:
279 except OSError:
248 pass
280 pass
249 try:
281 try:
@@ -253,7 +285,7 @@ class UnityTestCase(AutopilotTestCase):
253
285
254 app_proxy = self._launch_unity_with_upstart(
286 app_proxy = self._launch_unity_with_upstart(
255 binary_path,
287 binary_path,
256− self.unity_geometry_args
288+ self.unity_geometry_args,
257 )
289 )
258
290
259 self._set_proxy(app_proxy)
291 self._set_proxy(app_proxy)
@@ -272,14 +304,16 @@ class UnityTestCase(AutopilotTestCase):
272 binary_arg = "BINARY=%s" % binary_path
304 binary_arg = "BINARY=%s" % binary_path
273 extra_args = "ARGS=%s" % " ".join(args)
305 extra_args = "ARGS=%s" % " ".join(args)
274
306
275− output = subprocess.check_output([
307+ output = subprocess.check_output(
308+ [
276− "/sbin/initctl",
309+ "/sbin/initctl",
277− "start",
310+ "start",
278− "unity8",
311+ "unity8",
279− binary_arg,
312+ binary_arg,
280− extra_args
313+ extra_args,
281− ] + ["%s=%s" % (k,v) for k,v in self._environment.iteritems()],
314+ ] + ["%s=%s" % (k, v) for k, v in self._environment.iteritems()],
282− stderr=subprocess.STDOUT)
315+ stderr=subprocess.STDOUT,
316+ )
283
317
284 self.addCleanup(self._cleanup_launching_upstart_unity)
318 self.addCleanup(self._cleanup_launching_upstart_unity)
285
319
@@ -293,10 +327,16 @@ class UnityTestCase(AutopilotTestCase):
293 def _cleanup_launching_upstart_unity(self):
327 def _cleanup_launching_upstart_unity(self):
294 logger.info("Stopping unity")
328 logger.info("Stopping unity")
295 try:
329 try:
330+ subprocess.check_output(
296− subprocess.check_output(["/sbin/initctl", "stop", "unity8"], stderr=subprocess.STDOUT)
331+ ["/sbin/initctl", "stop", "unity8"], stderr=subprocess.STDOUT)
297 except subprocess.CalledProcessError:
332 except subprocess.CalledProcessError:
298 logger.warning("Appears unity was already stopped!")
333 logger.warning("Appears unity was already stopped!")
299
334
335+ def _patch_data_dirs(self):
336+ data_dirs = get_data_dirs()
337+ if data_dirs is not None:
338+ self._environment['XDG_DATA_DIRS'] = data_dirs
339+
300 def patch_lightdm_mock(self, mock_type='single'):
340 def patch_lightdm_mock(self, mock_type='single'):
301 self._lightdm_mock_type = mock_type
341 self._lightdm_mock_type = mock_type
302 logger.info("Setting up LightDM mock type '%s'", mock_type)
342 logger.info("Setting up LightDM mock type '%s'", mock_type)
@@ -358,8 +398,7 @@ class UnityTestCase(AutopilotTestCase):
358 self.assertThat(home_scope.isCurrent, Eventually(Equals(True)))
398 self.assertThat(home_scope.isCurrent, Eventually(Equals(True)))
359
399
360 def get_dash(self):
400 def get_dash(self):
361− dash = self._proxy.select_single(Dash)
401+ dash = self._proxy.wait_select_single(Dash)
362− self.assertThat(dash, NotEquals(None))
363 return dash
402 return dash
364
403
365 @property
404 @property
tests/autopilot/unity8/shell/tests/test_notifications.py+51−43
@@ -45,7 +45,7 @@ class NotificationsBase(UnityTestCase):
45 """Given an icons file name returns the full path (either system or
45 """Given an icons file name returns the full path (either system or
46 source tree.
46 source tree.
47
47
48− Consider the graphics directory as root so for example (runnign tests
48+ Consider the graphics directory as root so for example (running tests
49 from installed unity8-autopilot package):
49 from installed unity8-autopilot package):
50 >>> self.get_icon_path('clock.png')
50 >>> self.get_icon_path('clock.png')
51 /usr/share/unity8/graphics/clock.png
51 /usr/share/unity8/graphics/clock.png
@@ -145,8 +145,8 @@ class InteractiveNotificationBase(NotificationsBase):
145 hints,
145 hints,
146 )
146 )
147
147
148− get_notification = lambda: notify_list.select_single('Notification')
148+ get_notification = lambda: notify_list.wait_select_single(
149− self.assertThat(get_notification, Eventually(NotEquals(None)))
149+ 'Notification', objectName='notification1')
150 notification = get_notification()
150 notification = get_notification()
151
151
152 self.touch.tap_object(
152 self.touch.tap_object(
@@ -156,7 +156,8 @@ class InteractiveNotificationBase(NotificationsBase):
156 self.assert_notification_action_id_was_called('action_id')
156 self.assert_notification_action_id_was_called('action_id')
157
157
158 def test_sd_incoming_call(self):
158 def test_sd_incoming_call(self):
159− """Rejecting a call should make notification expand and offer more options."""
159+ """Rejecting a call should make notification expand and
160+ offer more options."""
160 self.launch_unity()
161 self.launch_unity()
161 greeter = self.main_window.get_greeter()
162 greeter = self.main_window.get_greeter()
162 greeter.swipe()
163 greeter.swipe()
@@ -191,8 +192,8 @@ class InteractiveNotificationBase(NotificationsBase):
191 hints
192 hints
192 )
193 )
193
194
194− get_notification = lambda: notify_list.select_single('Notification')
195+ get_notification = lambda: notify_list.wait_select_single(
195− self.assertThat(get_notification, Eventually(NotEquals(None)))
196+ 'Notification', objectName='notification1')
196 notification = get_notification()
197 notification = get_notification()
197 self._assert_notification(notification, None, None, True, True, 1.0)
198 self._assert_notification(notification, None, None, True, True, 1.0)
198 initial_height = notification.height
199 initial_height = notification.height
@@ -200,8 +201,10 @@ class InteractiveNotificationBase(NotificationsBase):
200 self.assertThat(
201 self.assertThat(
201 notification.height,
202 notification.height,
202 Eventually(Equals(initial_height +
203 Eventually(Equals(initial_height +
204+ 3 * notification.select_single(
203− 3 * notification.select_single(objectName="buttonColumn").spacing +
205+ objectName="buttonColumn").spacing +
204− 3 * notification.select_single(objectName="button4").height)))
206+ 3 * notification.select_single(
207+ objectName="button4").height)))
205 self.touch.tap_object(notification.select_single(objectName="button4"))
208 self.touch.tap_object(notification.select_single(objectName="button4"))
206 self.assert_notification_action_id_was_called("action_decline_4")
209 self.assert_notification_action_id_was_called("action_decline_4")
207
210
@@ -360,13 +363,20 @@ class EphemeralNotificationsTests(NotificationsBase):
360
363
361 notification.show()
364 notification.show()
362
365
363− get_notification = lambda: notify_list.select_single('Notification')
366+ notification = lambda: notify_list.wait_select_single(
364− self.assertThat(get_notification, Eventually(NotEquals(None)))
367+ 'Notification', objectName='notification1')
365− notification = get_notification()
366− self._assert_notification(notification, summary, body, True, True, 1.0)
368+ self._assert_notification(
369+ notification(),
370+ summary,
371+ body,
372+ True,
373+ True,
374+ 1.0,
375+ )
367
376
368 def test_icon_summary(self):
377 def test_icon_summary(self):
369− """Notification must display the expected summary and secondary icon."""
378+ """Notification must display the expected summary and secondary
379+ icon."""
370 self.launch_unity()
380 self.launch_unity()
371 greeter = self.main_window.get_greeter()
381 greeter = self.main_window.get_greeter()
372 greeter.swipe()
382 greeter.swipe()
@@ -391,11 +401,11 @@ class EphemeralNotificationsTests(NotificationsBase):
391
401
392 notification.show()
402 notification.show()
393
403
394− get_notification = lambda: notify_list.select_single('Notification')
404+ notification = lambda: notify_list.wait_select_single(
395− self.assertThat(get_notification, Eventually(NotEquals(None)))
405+ 'Notification', objectName='notification1')
396− notification = get_notification()
406+
397 self._assert_notification(
407 self._assert_notification(
398− notification,
408+ notification(),
399 summary,
409 summary,
400 None,
410 None,
401 False,
411 False,
@@ -404,7 +414,8 @@ class EphemeralNotificationsTests(NotificationsBase):
404 )
414 )
405
415
406 def test_urgency_order(self):
416 def test_urgency_order(self):
407− """Notifications must be displayed in order according to their urgency."""
417+ """Notifications must be displayed in order according to their
418+ urgency."""
408 self.launch_unity()
419 self.launch_unity()
409 greeter = self.main_window.get_greeter()
420 greeter = self.main_window.get_greeter()
410 greeter.swipe()
421 greeter.swipe()
@@ -448,11 +459,10 @@ class EphemeralNotificationsTests(NotificationsBase):
448 )
459 )
449 notification_critical.show()
460 notification_critical.show()
450
461
451− get_notification = lambda: notify_list.select_single(
462+ get_notification = lambda: notify_list.wait_select_single(
452 'Notification',
463 'Notification',
453 summary=summary_critical
464 summary=summary_critical
454 )
465 )
455− self.assertThat(get_notification, Eventually(NotEquals(None)))
456
466
457 notification = get_notification()
467 notification = get_notification()
458 self._assert_notification(
468 self._assert_notification(
@@ -464,11 +474,10 @@ class EphemeralNotificationsTests(NotificationsBase):
464 1.0
474 1.0
465 )
475 )
466
476
467− get_normal_notification = lambda: notify_list.select_single(
477+ get_normal_notification = lambda: notify_list.wait_select_single(
468 'Notification',
478 'Notification',
469 summary=summary_normal
479 summary=summary_normal
470 )
480 )
471− self.assertThat(get_normal_notification, Eventually(NotEquals(None)))
472 notification = get_normal_notification()
481 notification = get_normal_notification()
473 self._assert_notification(
482 self._assert_notification(
474 notification,
483 notification,
@@ -479,11 +488,10 @@ class EphemeralNotificationsTests(NotificationsBase):
479 1.0
488 1.0
480 )
489 )
481
490
482− get_low_notification = lambda: notify_list.select_single(
491+ get_low_notification = lambda: notify_list.wait_select_single(
483 'Notification',
492 'Notification',
484 summary=summary_low
493 summary=summary_low
485 )
494 )
486− self.assertThat(get_low_notification, Eventually(NotEquals(None)))
487 notification = get_low_notification()
495 notification = get_low_notification()
488 self._assert_notification(
496 self._assert_notification(
489 notification,
497 notification,
@@ -508,9 +516,8 @@ class EphemeralNotificationsTests(NotificationsBase):
508 notification = self._create_ephemeral_notification(summary, body)
516 notification = self._create_ephemeral_notification(summary, body)
509 notification.show()
517 notification.show()
510
518
511− get_notification = lambda: notify_list.select_single('Notification')
519+ notification = notify_list.wait_select_single(
512− self.assertThat(get_notification, Eventually(NotEquals(None)))
520+ 'Notification', objectName='notification1')
513− notification = get_notification()
514 self._assert_notification(
521 self._assert_notification(
515 notification,
522 notification,
516 summary,
523 summary,
@@ -533,14 +540,14 @@ class EphemeralNotificationsTests(NotificationsBase):
533 notification = self._create_ephemeral_notification(summary)
540 notification = self._create_ephemeral_notification(summary)
534 notification.show()
541 notification.show()
535
542
536− get_notification = lambda: notify_list.select_single('Notification')
543+ notification = notify_list.wait_select_single(
537− self.assertThat(get_notification, Eventually(NotEquals(None)))
544+ 'Notification', objectName='notification1')
538− notification = get_notification()
539
545
540 self._assert_notification(notification, summary, '', False, False, 1.0)
546 self._assert_notification(notification, summary, '', False, False, 1.0)
541
547
542 def test_update_notification_same_layout(self):
548 def test_update_notification_same_layout(self):
543− """Notification must allow updating its contents while being displayed."""
549+ """Notification must allow updating its contents while being
550+ displayed."""
544 self.launch_unity()
551 self.launch_unity()
545 greeter = self.main_window.get_greeter()
552 greeter = self.main_window.get_greeter()
546 greeter.swipe()
553 greeter.swipe()
@@ -558,8 +565,8 @@ class EphemeralNotificationsTests(NotificationsBase):
558 )
565 )
559 notification.show()
566 notification.show()
560
567
561− get_notification = lambda: notify_list.select_single('Notification', summary=summary)
568+ get_notification = lambda: notify_list.wait_select_single(
562− self.assertThat(get_notification, Eventually(NotEquals(None)))
569+ 'Notification', summary=summary)
563 self._assert_notification(
570 self._assert_notification(
564 get_notification(),
571 get_notification(),
565 summary,
572 summary,
@@ -575,11 +582,11 @@ class EphemeralNotificationsTests(NotificationsBase):
575 icon_path = self._get_icon_path('avatars/amanda.png')
582 icon_path = self._get_icon_path('avatars/amanda.png')
576 notification.update(summary, body, icon_path)
583 notification.update(summary, body, icon_path)
577 notification.show()
584 notification.show()
578− self.assertThat(get_notification, Eventually(NotEquals(None)))
579 self._assert_notification(get_notification(), summary, body)
585 self._assert_notification(get_notification(), summary, body)
580
586
581 def test_update_notification_layout_change(self):
587 def test_update_notification_layout_change(self):
582− """Notification must allow updating its contents and layout while being displayed."""
588+ """Notification must allow updating its contents and layout while
589+ being displayed."""
583 self.launch_unity()
590 self.launch_unity()
584 greeter = self.main_window.get_greeter()
591 greeter = self.main_window.get_greeter()
585 greeter.swipe()
592 greeter.swipe()
@@ -603,8 +610,8 @@ class EphemeralNotificationsTests(NotificationsBase):
603 )
610 )
604 notification.show()
611 notification.show()
605
612
606− get_notification = lambda: notify_list.select_single('Notification')
613+ get_notification = lambda: notify_list.wait_select_single(
607− self.assertThat(get_notification, Eventually(NotEquals(None)))
614+ 'Notification', objectName='notification1')
608 self._assert_notification(
615 self._assert_notification(
609 get_notification(),
616 get_notification(),
610 summary,
617 summary,
@@ -645,8 +652,8 @@ class EphemeralNotificationsTests(NotificationsBase):
645
652
646 notification.show()
653 notification.show()
647
654
648− get_notification = lambda: notify_list.select_single('Notification')
655+ get_notification = lambda: notify_list.wait_select_single(
649− self.assertThat(get_notification, Eventually(NotEquals(None)))
656+ 'Notification', objectName='notification1')
650 notification = get_notification()
657 notification = get_notification()
651 self._assert_notification(
658 self._assert_notification(
652 notification,
659 notification,
@@ -662,7 +669,8 @@ class EphemeralNotificationsTests(NotificationsBase):
662 'Did you watch the air-race in Oshkosh last week?',
669 'Did you watch the air-race in Oshkosh last week?',
663 'Phil owned the place like no one before him!',
670 'Phil owned the place like no one before him!',
664 'Did really everything in the race work according to regulations?',
671 'Did really everything in the race work according to regulations?',
665− 'Somehow I think to remember Burt Williams did cut corners and was not punished for this.',
672+ 'Somehow I think to remember Burt Williams did cut corners and '
673+ 'was not punished for this.',
666 'Hopefully the referees will watch the videos of the race.',
674 'Hopefully the referees will watch the videos of the race.',
667 'Burt could get fined with US$ 50000 for that rule-violation :)'
675 'Burt could get fined with US$ 50000 for that rule-violation :)'
668 ]
676 ]
@@ -677,10 +685,10 @@ class EphemeralNotificationsTests(NotificationsBase):
677 hints=[('x-canonical-append', 'true')]
685 hints=[('x-canonical-append', 'true')]
678 )
686 )
679 notification.show()
687 notification.show()
680− get_notification = lambda: notify_list.select_single(
688+ get_notification = lambda: notify_list.wait_select_single(
681− 'Notification'
689+ 'Notification',
690+ objectName='notification1'
682 )
691 )
683− self.assertThat(get_notification, Eventually(NotEquals(None)))
684 notification = get_notification()
692 notification = get_notification()
685 self._assert_notification(
693 self._assert_notification(
686 notification,
694 notification,
tests/mocks/CMakeLists.txt+5
@@ -7,3 +7,8 @@ add_subdirectory(Powerd)
7add_subdirectory(QMenuModel)
7add_subdirectory(QMenuModel)
8add_subdirectory(Ubuntu)
8add_subdirectory(Ubuntu)
9add_subdirectory(Unity)
9add_subdirectory(Unity)
10+
11+install(
12+ DIRECTORY data
13+ DESTINATION ${SHELL_APP_DIR}/mocks
14+)
tests/mocks/Unity/fake_preview.cpp+9−1
@@ -16,9 +16,13 @@
16
16
17#include "fake_preview.h"
17#include "fake_preview.h"
18
18
19+Preview::Preview(QObject *parent): QObject(parent)
20+{
21+}
22+
19QString Preview::rendererName() const
23QString Preview::rendererName() const
20{
24{
21− return "foo";
25+ return "generic-preview";
22}
26}
23
27
24QString Preview::title() const
28QString Preview::title() const
@@ -61,3 +65,7 @@ void Preview::execute(const QString& actionId, const QHash<QString, QVariant>& h
61 Q_UNUSED(actionId);
65 Q_UNUSED(actionId);
62 Q_UNUSED(hints);
66 Q_UNUSED(hints);
63}
67}
68+
69+void Preview::cancelAction()
70+{
71+}
tests/mocks/Unity/fake_preview.h+2
@@ -51,6 +51,8 @@ public:
51
51
52 Q_INVOKABLE void execute(const QString& actionId, const QHash<QString, QVariant>& hints);
52 Q_INVOKABLE void execute(const QString& actionId, const QHash<QString, QVariant>& hints);
53
53
54+ Q_INVOKABLE void cancelAction();
55+
54Q_SIGNALS:
56Q_SIGNALS:
55 void previewChanged();
57 void previewChanged();
56};
58};
tests/mocks/Unity/fake_scope.cpp+25−4
@@ -14,13 +14,11 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
15 */
16
16
17−// Self
18#include "fake_scope.h"
17#include "fake_scope.h"
19−
20−#include <dee.h>
18+#include "fake_preview.h"
21#include "paths.h"
19#include "paths.h"
22
20
23−#include "fake_preview.h"
21+#include <dee.h>
24
22
25static DeeModel* create_categories_model(unsigned category_count);
23static DeeModel* create_categories_model(unsigned category_count);
26static DeeModel* create_results_model(unsigned category_count, unsigned result_count);
24static DeeModel* create_results_model(unsigned category_count, unsigned result_count);
@@ -39,6 +37,13 @@ Scope::Scope(QObject* parent)
39 m_categories->setResultModel(results_model);
37 m_categories->setResultModel(results_model);
40 m_categories->setModel(create_categories_model(4));
38 m_categories->setModel(create_categories_model(4));
41 m_results->setModel(results_model);
39 m_results->setModel(results_model);
40+
41+ m_timer.setInterval(1000);
42+ m_timer.setSingleShot(true);
43+ connect(&m_timer, &QTimer::timeout, [this]() {
44+ Preview *p = new Preview(this);
45+ Q_EMIT previewReady(p);
46+ });
42}
47}
43
48
44Scope::Scope(QString const& id, QString const& name, bool visible, QObject* parent)
49Scope::Scope(QString const& id, QString const& name, bool visible, QObject* parent)
@@ -55,6 +60,13 @@ Scope::Scope(QString const& id, QString const& name, bool visible, QObject* pare
55 m_categories->setResultModel(results_model);
60 m_categories->setResultModel(results_model);
56 m_categories->setModel(create_categories_model(4));
61 m_categories->setModel(create_categories_model(4));
57 m_results->setModel(results_model);
62 m_results->setModel(results_model);
63+
64+ m_timer.setInterval(1000);
65+ m_timer.setSingleShot(true);
66+ connect(&m_timer, &QTimer::timeout, [this]() {
67+ Preview *p = new Preview(this);
68+ Q_EMIT previewReady(p);
69+ });
58}
70}
59
71
60QString Scope::id() const {
72QString Scope::id() const {
@@ -168,6 +180,8 @@ void Scope::activate(const QVariant &uri, const QVariant &icon_hint, const QVari
168 Q_UNUSED(comment);
180 Q_UNUSED(comment);
169 Q_UNUSED(dnd_uri);
181 Q_UNUSED(dnd_uri);
170 Q_UNUSED(metadata);
182 Q_UNUSED(metadata);
183+
184+ m_timer.start();
171}
185}
172
186
173void Scope::preview(const QVariant &uri, const QVariant &icon_hint, const QVariant &category,
187void Scope::preview(const QVariant &uri, const QVariant &icon_hint, const QVariant &category,
@@ -183,6 +197,8 @@ void Scope::preview(const QVariant &uri, const QVariant &icon_hint, const QVaria
183 Q_UNUSED(comment);
197 Q_UNUSED(comment);
184 Q_UNUSED(dnd_uri);
198 Q_UNUSED(dnd_uri);
185 Q_UNUSED(metadata);
199 Q_UNUSED(metadata);
200+
201+ m_timer.start();
186}
202}
187
203
188static const gchar * categories_model_schema[] = {
204static const gchar * categories_model_schema[] = {
@@ -248,3 +264,8 @@ DeeModel* create_results_model(unsigned category_count, unsigned result_count) {
248 }
264 }
249 return results_model;
265 return results_model;
250}
266}
267+
268+void Scope::cancelActivation()
269+{
270+ m_timer.stop();
271+}
tests/mocks/Unity/fake_scope.h+7−2
@@ -17,10 +17,11 @@
17#ifndef FAKE_SCOPE_H
17#ifndef FAKE_SCOPE_H
18#define FAKE_SCOPE_H
18#define FAKE_SCOPE_H
19
19
20−// Qt
21−#include <QObject>
22#include "fake_categories.h"
20#include "fake_categories.h"
23
21
22+#include <QObject>
23+#include <QDebug>
24+
24class Preview;
25class Preview;
25
26
26class Scope : public QObject
27class Scope : public QObject
@@ -78,6 +79,8 @@ public:
78 const QVariant &result_type, const QVariant &mimetype, const QVariant &title,
79 const QVariant &result_type, const QVariant &mimetype, const QVariant &title,
79 const QVariant &comment, const QVariant &dnd_uri, const QVariant &metadata);
80 const QVariant &comment, const QVariant &dnd_uri, const QVariant &metadata);
80
81
82+ Q_INVOKABLE void cancelActivation();
83+
81Q_SIGNALS:
84Q_SIGNALS:
82 void idChanged(const QString&);
85 void idChanged(const QString&);
83 void nameChanged(const QString&);
86 void nameChanged(const QString&);
@@ -115,6 +118,8 @@ protected:
115
118
116 Categories* m_categories;
119 Categories* m_categories;
117 DeeListModel* m_results;
120 DeeListModel* m_results;
121+
122+ QTimer m_timer;
118};
123};
119
124
120#endif // FAKE_SCOPE_H
125#endif // FAKE_SCOPE_H
tests/mocks/Unity/fake_unity_plugin.cpp+3
@@ -22,6 +22,7 @@
22// local
22// local
23#include "fake_scopes.h"
23#include "fake_scopes.h"
24#include "fake_categories.h"
24#include "fake_categories.h"
25+#include "fake_preview.h"
25#include "categoryresults.h"
26#include "categoryresults.h"
26
27
27// External
28// External
@@ -37,8 +38,10 @@ void FakeUnityPlugin::registerTypes(const char *uri)
37
38
38 Q_ASSERT(uri == QLatin1String("Unity"));
39 Q_ASSERT(uri == QLatin1String("Unity"));
39
40
41+ qmlRegisterUncreatableType<Preview>(uri, 0, 1, "Preview", "Can't create Preview object in QML.");
40 qmlRegisterType<Scopes>(uri, 0, 1, "Scopes");
42 qmlRegisterType<Scopes>(uri, 0, 1, "Scopes");
41 qmlRegisterType<Scope>(uri, 0, 1, "Scope");
43 qmlRegisterType<Scope>(uri, 0, 1, "Scope");
42 qmlRegisterType<Categories>(uri, 0, 1, "Categories");
44 qmlRegisterType<Categories>(uri, 0, 1, "Categories");
43 qmlRegisterUncreatableType<CategoryResults>(uri, 0, 1, "CategoryResults", "Can't create");
45 qmlRegisterUncreatableType<CategoryResults>(uri, 0, 1, "CategoryResults", "Can't create");
46+ qmlRegisterType<DeeListModel>(uri, 0, 1, "DeeListModel");
44}
47}
tests/mocks/data/applications/camera-app.desktopnew file

No changes to the content.

tests/plugins/Unity/Indicators/CMakeLists.txt+1
@@ -40,3 +40,4 @@ indicator_test(indicatorsmodeltest ADDITIONAL_LIBS IndicatorsQml)
40indicator_test(menucontentactivatortest ADDITIONAL_CPPS ${INDICATORS_DIR}/menucontentactivator.cpp)
40indicator_test(menucontentactivatortest ADDITIONAL_CPPS ${INDICATORS_DIR}/menucontentactivator.cpp)
41indicator_test(unitymenumodelstacktest ADDITIONAL_CPPS ${TEST_DIR}/mocks/QMenuModel/unitymenumodel.cpp ${INDICATORS_DIR}/unitymenumodelstack.cpp)
41indicator_test(unitymenumodelstacktest ADDITIONAL_CPPS ${TEST_DIR}/mocks/QMenuModel/unitymenumodel.cpp ${INDICATORS_DIR}/unitymenumodelstack.cpp)
42indicator_test(rootactionstatetest ADDITIONAL_LIBS IndicatorsQml)
42indicator_test(rootactionstatetest ADDITIONAL_LIBS IndicatorsQml)
43+indicator_test(timeformattertest ADDITIONAL_LIBS IndicatorsQml)
tests/plugins/Unity/Indicators/timeformattertest.cppnew file+55
@@ -0,0 +1,55 @@
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+ * Author: Lars Uebernickel <lars.uebernickel@canonical.com>
17+ */
18+
19+#include "timeformatter.h"
20+
21+#include <QtTest>
22+#include <QDebug>
23+
24+class TimeFormatterTest : public QObject
25+{
26+ Q_OBJECT
27+private Q_SLOTS:
28+
29+ void initTestCase()
30+ {
31+ setenv("UNITY_TEST_ENV", "1", 1);
32+ }
33+
34+ void cleanupTestCase()
35+ {
36+ unsetenv("UNITY_TEST_ENV");
37+ }
38+
39+ void testFormat()
40+ {
41+ const QString format = "hh:mm dd.MM.yy";
42+
43+ QDateTime time = QDateTime::currentDateTime();
44+
45+ TimeFormatter formatter;
46+ formatter.setTime(time.toMSecsSinceEpoch() * 1000);
47+ formatter.setFormat(format);
48+
49+ QCOMPARE(formatter.timeString(), time.toString(format));
50+ }
51+};
52+
53+QTEST_GUILESS_MAIN(TimeFormatterTest)
54+
55+#include "timeformattertest.moc"
tests/qmltests/Dash/Apps/tst_AppPreview.qml+3−2
@@ -137,11 +137,12 @@ Item {
137 for(var i = 0; i < buttons.count; i++) {
137 for(var i = 0; i < buttons.count; i++) {
138 var button = findChild(appPreview, "button" + i);
138 var button = findChild(appPreview, "button" + i);
139 mouseClick(button, 1, 1);
139 mouseClick(button, 1, 1);
140+ appPreview.showProcessingAction = false;
140 }
141 }
141
142
142 var actions = data.actions;
143 var actions = data.actions;
143− for(var i = 0; i < actions.length; i++) {
144+ for(var j = 0; j < actions.length; j++) {
144− compare(root.calls[i][0], actions[i].id, "Id of action not found.");
145+ compare(root.calls[j][0], actions[j].id, "Id of action not found.");
145 }
146 }
146 }
147 }
147
148
tests/qmltests/Dash/Apps/tst_RunningApplicationsGrid.qml+1
@@ -226,6 +226,7 @@ Item {
226 verify(fakeRunningAppsModel.contains("calendar"))
226 verify(fakeRunningAppsModel.contains("calendar"))
227
227
228 mouseClick(calendarTileCloseButton, calendarTileCloseButton.width/2, calendarTileCloseButton.height/2)
228 mouseClick(calendarTileCloseButton, calendarTileCloseButton.width/2, calendarTileCloseButton.height/2)
229+ wait(0) // spin event loop to start any pending animation
229
230
230 verify(!fakeRunningAppsModel.contains("calendar"))
231 verify(!fakeRunningAppsModel.contains("calendar"))
231
232
tests/qmltests/Dash/Movie/tst_MoviePreview.qml+1
@@ -74,6 +74,7 @@ Item {
74 for(var i = 0; i < buttons.count; i++) {
74 for(var i = 0; i < buttons.count; i++) {
75 var button = findChild(moviePreview, "button" + i);
75 var button = findChild(moviePreview, "button" + i);
76 mouseClick(button, 1, 1);
76 mouseClick(button, 1, 1);
77+ moviePreview.showProcessingAction = false;
77 }
78 }
78
79
79 var actions = get_actions_data();
80 var actions = get_actions_data();
tests/qmltests/Dash/tst_GenericPreview.qml+1
@@ -76,6 +76,7 @@ Item {
76 for(var i = 0; i < buttons.count; i++) {
76 for(var i = 0; i < buttons.count; i++) {
77 var button = findChild(genericPreview, "button" + i);
77 var button = findChild(genericPreview, "button" + i);
78 mouseClick(button, 1, 1);
78 mouseClick(button, 1, 1);
79+ genericPreview.showProcessingAction = false;
79 }
80 }
80
81
81 var actions = get_actions_data();
82 var actions = get_actions_data();
tests/qmltests/Dash/tst_GenericScopeView.qml+131−10
@@ -23,11 +23,21 @@ import Ubuntu.Components 0.1
23import Unity.Test 0.1 as UT
23import Unity.Test 0.1 as UT
24
24
25Item {
25Item {
26+ id: shell
26 width: units.gu(120)
27 width: units.gu(120)
27 height: units.gu(80)
28 height: units.gu(80)
28
29
29 Scopes {
30 Scopes {
30 id: scopes
31 id: scopes
32+
33+ onLoadedChanged: {
34+ genericScopeView.scope = scopes.get(0)
35+ }
36+ }
37+
38+ property Item applicationManager: Item {
39+ signal sideStageFocusedApplicationChanged()
40+ signal mainStageFocusedApplicationChanged()
31 }
41 }
32
42
33 GenericScopeView {
43 GenericScopeView {
@@ -38,34 +48,145 @@ Item {
38 name: "GenericScopeView"
48 name: "GenericScopeView"
39 when: scopes.loaded
49 when: scopes.loaded
40
50
41− function init() {
42− genericScopeView.scope = scopes.get(0)
43− }
44−
45 function test_isCurrent() {
51 function test_isCurrent() {
46 var pageHeader = findChild(genericScopeView, "pageHeader");
52 var pageHeader = findChild(genericScopeView, "pageHeader");
47− var previewLoader = findChild(genericScopeView, "previewLoader");
53+ var previewListView = findChild(genericScopeView, "previewListView");
48 genericScopeView.isCurrent = true
54 genericScopeView.isCurrent = true
49 pageHeader.searchQuery = "test"
55 pageHeader.searchQuery = "test"
50− previewLoader.open = true
56+ previewListView.open = true
51 genericScopeView.isCurrent = false
57 genericScopeView.isCurrent = false
52 tryCompare(pageHeader, "searchQuery", "")
58 tryCompare(pageHeader, "searchQuery", "")
53 tryCompare(genericScopeView, "previewShown", false);
59 tryCompare(genericScopeView, "previewShown", false);
54 }
60 }
55
61
56 function test_showDash() {
62 function test_showDash() {
57− var previewLoader = findChild(genericScopeView, "previewLoader");
63+ var previewListView = findChild(genericScopeView, "previewListView");
58− previewLoader.open = true;
64+ previewListView.open = true;
59 scopes.get(0).showDash();
65 scopes.get(0).showDash();
60 tryCompare(genericScopeView, "previewShown", false);
66 tryCompare(genericScopeView, "previewShown", false);
61 }
67 }
62
68
63 function test_hideDash() {
69 function test_hideDash() {
64− var previewLoader = findChild(genericScopeView, "previewLoader");
70+ var previewListView = findChild(genericScopeView, "previewListView");
65− previewLoader.open = true;
71+ previewListView.open = true;
66 scopes.get(0).hideDash();
72 scopes.get(0).hideDash();
67 tryCompare(genericScopeView, "previewShown", false);
73 tryCompare(genericScopeView, "previewShown", false);
68 }
74 }
75+
76+ function openPreview() {
77+ var tile = findChild(genericScopeView, "delegate0");
78+ mouseClick(tile, tile.width / 2, tile.height / 2);
79+ var openEffect = findChild(genericScopeView, "openEffect");
80+ tryCompare(openEffect, "gap", 1);
81+ }
82+
83+ function checkArrowPosition(index) {
84+ var tile = findChild(genericScopeView, "delegate" + index);
85+ var tileCenter = tile.x + tile.width/2;
86+ var pointerArrow = findChild(genericScopeView, "pointerArrow");
87+ var pointerArrowCenter = pointerArrow.x + pointerArrow.width/2;
88+ compare(pointerArrowCenter, tileCenter, "Pointer did not move to tile");
89+ }
90+
91+ function closePreview() {
92+ var closePreviewMouseArea = findChild(genericScopeView, "closePreviewMouseArea");
93+ mouseClick(closePreviewMouseArea, closePreviewMouseArea.width / 2, closePreviewMouseArea.height / 2);
94+
95+ var previewListView = findChild(genericScopeView, "previewListView");
96+ tryCompare(previewListView, "open", false);
97+ var openEffect = findChild(genericScopeView, "openEffect");
98+ tryCompare(openEffect, "gap", 0);
99+
100+ var categoryListView = findChild(genericScopeView, "categoryListView");
101+ categoryListView.flick(0, units.gu(200));
102+ tryCompare(categoryListView, "flicking", false);
103+ }
104+
105+ function test_previewOpenClose() {
106+ var previewListView = findChild(genericScopeView, "previewListView");
107+ tryCompare(previewListView, "open", false);
108+
109+ openPreview();
110+
111+ // check for it opening successfully
112+ var currentPreviewItem = findChild(genericScopeView, "previewLoader0");
113+ tryCompareFunction(function() {
114+ var parts = currentPreviewItem.source.toString().split("/");
115+ var name = parts[parts.length - 1];
116+ return name == "DashPreviewPlaceholder.qml";
117+ },
118+ true);
119+ tryCompareFunction(function() {
120+ var parts = currentPreviewItem.source.toString().split("/");
121+ var name = parts[parts.length - 1];
122+ return name == "GenericPreview.qml";
123+ },
124+ true);
125+ tryCompare(currentPreviewItem, "progress", 1);
126+ tryCompare(previewListView, "open", true);
127+
128+ closePreview();
129+ tryCompare(previewListView, "open", false);
130+ }
131+
132+ function test_previewCycleOne() {
133+ var previewListView = findChild(genericScopeView, "previewListView");
134+ tryCompare(previewListView, "open", false);
135+
136+ openPreview();
137+
138+ // wait for it to be loaded
139+ var currentPreviewItem = findChild(genericScopeView, "previewLoader0");
140+ tryCompareFunction(function() {
141+ var parts = currentPreviewItem.source.toString().split("/");
142+ var name = parts[parts.length - 1];
143+ return name == "GenericPreview.qml";
144+ },
145+ true);
146+ tryCompare(currentPreviewItem, "progress", 1);
147+ waitForRendering(currentPreviewItem);
148+
149+ checkArrowPosition(0);
150+
151+ // flick to the next previews
152+
153+ for (var i = 1; i < previewListView.count; ++i) {
154+
155+ mouseFlick(previewListView, previewListView.width - units.gu(1),
156+ previewListView.height / 2,
157+ units.gu(2),
158+ previewListView.height / 2);
159+
160+ // wait for it to be loaded
161+ var nextPreviewItem = findChild(genericScopeView, "previewLoader" + i);
162+ tryCompareFunction(function() {
163+ var parts = nextPreviewItem.source.toString().split("/");
164+ var name = parts[parts.length - 1];
165+ return name == "GenericPreview.qml";
166+ },
167+ true);
168+ tryCompare(nextPreviewItem, "progress", 1);
169+ waitForRendering(nextPreviewItem);
170+
171+ checkArrowPosition(i);
172+ }
173+
174+ closePreview();
175+ }
176+
177+ function test_show_spinner() {
178+ openPreview();
179+ var previewListView = findChild(genericScopeView, "previewListView");
180+ var previewLoader = findChild(genericScopeView, "previewLoader0");
181+
182+ previewLoader.item.showProcessingAction = true;
183+ var waitingForAction = findChild(genericScopeView, "waitingForActionMouseArea");
184+ tryCompare(waitingForAction, "enabled", true);
185+ previewLoader.closePreviewSpinner();
186+ tryCompare(waitingForAction, "enabled", false);
187+
188+ closePreview();
189+ }
69 }
190 }
70 }
191 }
71}
192}
tests/qmltests/Greeter/tst_Phone.qml+18−5
@@ -83,8 +83,9 @@ Item {
83
83
84 function test_teaseLockedUnlocked_data() {
84 function test_teaseLockedUnlocked_data() {
85 return [
85 return [
86− {tag: "unlocked", locked: false},
86+ {tag: "unlocked", locked: false, moved: false},
87− {tag: "locked", locked: true}
87+ {tag: "locked", locked: true, moved: false},
88+ {tag: "moved", locked: false, moved: true}
88 ];
89 ];
89 }
90 }
90
91
@@ -93,10 +94,16 @@ Item {
93 tryCompare(greeter, "x", 0);
94 tryCompare(greeter, "x", 0);
94 greeter.locked = data.locked;
95 greeter.locked = data.locked;
95
96
97+ // simulate greeter being in the middle of a swipe
98+ if (data.moved) {
99+ greeter.x = -units.gu(4);
100+ tryCompare(greeter, "x", -units.gu(4));
101+ }
102+
96 mouseClick(greeter, greeter.width - units.gu(5), greeter.height - units.gu(1));
103 mouseClick(greeter, greeter.width - units.gu(5), greeter.height - units.gu(1));
97 greeter.minX = 0; // This is needed because the transition actually makes x jump once before animating
104 greeter.minX = 0; // This is needed because the transition actually makes x jump once before animating
98
105
99− if (!data.locked) {
106+ if (!data.locked && !data.moved) {
100 // Check if it has been moved over by 2 GUs. Give it a 2 pixel grace area
107 // Check if it has been moved over by 2 GUs. Give it a 2 pixel grace area
101 // because animation duration and teaseTimer are the same duration and
108 // because animation duration and teaseTimer are the same duration and
102 // might cause slight offsets
109 // might cause slight offsets
@@ -104,10 +111,16 @@ Item {
104 } else {
111 } else {
105 // waiting 100ms to make sure nothing moves
112 // waiting 100ms to make sure nothing moves
106 wait(100);
113 wait(100);
107− compare(greeter.minX, 0, "Greeter moved even tho its locked");
114+ compare(greeter.minX, 0, "Greeter teasing not disabled even though it's locked or moving.");
108 }
115 }
116+
117+ // Restore position in case we moved it for the test
118+ if (data.moved) {
119+ greeter.x = 0;
120+ }
121+
109 // Wait until we're back to 0
122 // Wait until we're back to 0
110− tryCompareFunction(function() { return greeter.x;}, 0);
123+ tryCompareFunction(function() { return greeter.x;}, 0);
111 }
124 }
112
125
113 function test_statsWelcomeScreen() {
126 function test_statsWelcomeScreen() {
tests/qmltests/Launcher/tst_Launcher.qml+47−11
@@ -363,25 +363,60 @@ Item {
363 var draggedItem = findChild(launcher, "launcherDelegate5")
363 var draggedItem = findChild(launcher, "launcherDelegate5")
364 var item0 = findChild(launcher, "launcherDelegate0")
364 var item0 = findChild(launcher, "launcherDelegate0")
365 var fakeDragItem = findChild(launcher, "fakeDragItem")
365 var fakeDragItem = findChild(launcher, "fakeDragItem")
366− var dndArea = findChild(launcher, "dndArea")
366+ var quickListShape = findChild(launcher, "quickListShape")
367
367
368 // Initial state
368 // Initial state
369− compare(dndArea.quickListPopover, null)
369+ compare(quickListShape.visible, false)
370
370
371 // Doing longpress
371 // Doing longpress
372 mousePress(draggedItem, draggedItem.width / 2, draggedItem.height / 2)
372 mousePress(draggedItem, draggedItem.width / 2, draggedItem.height / 2)
373 tryCompare(fakeDragItem, "visible", true) // Wait longpress happening
373 tryCompare(fakeDragItem, "visible", true) // Wait longpress happening
374− verify(dndArea.quickListPopover != null)
374+ tryCompare(quickListShape, "visible", true)
375
375
376 // Dragging a bit (> 1.5 gu)
376 // Dragging a bit (> 1.5 gu)
377 mouseMove(draggedItem, -units.gu(2), draggedItem.height / 2)
377 mouseMove(draggedItem, -units.gu(2), draggedItem.height / 2)
378
378
379 // QuickList needs to be closed when a drag operation starts
379 // QuickList needs to be closed when a drag operation starts
380− tryCompare(dndArea, "quickListPopover", null)
380+ tryCompare(quickListShape, "visible", false)
381
381
382 mouseRelease(draggedItem);
382 mouseRelease(draggedItem);
383 }
383 }
384
384
385+ function test_quicklist_positioning_data() {
386+ return [
387+ {tag: "top", flickTo: "top", itemIndex: 0},
388+ {tag: "bottom", flickTo: "bottom", itemIndex: 9}
389+ ];
390+ }
391+
392+ function test_quicklist_positioning(data) {
393+ revealer.dragLauncherIntoView();
394+ var quickList = findChild(launcher, "quickList")
395+ var draggedItem = findChild(launcher, "launcherDelegate" + data.itemIndex)
396+ var quickListShape = findChild(launcher, "quickListShape")
397+
398+ // Position launcher to where we need it
399+ var listView = findChild(launcher, "launcherListView");
400+ listView.flick(0, units.gu(200) * (data.flickTo === "top" ? 1 : -1));
401+ tryCompare(listView, "flicking", false);
402+
403+ // Doing longpress
404+ mousePress(draggedItem, draggedItem.width / 2, draggedItem.height / 2)
405+ tryCompare(quickListShape, "opacity", 0.8)
406+ mouseRelease(draggedItem);
407+
408+ verify(quickList.y >= units.gu(1))
409+ verify(quickList.y + quickList.height + units.gu(1) <= launcher.height)
410+
411+ // Click somewhere in the empty space to dismiss the quicklist
412+ mouseClick(launcher, launcher.width - units.gu(1), units.gu(1));
413+ tryCompare(quickListShape, "visible", false)
414+
415+ // Click somewhere in the empty space to dismiss the launcher
416+ mouseClick(launcher, launcher.width - units.gu(1), units.gu(1));
417+ revealer.waitUntilLauncherDisappears()
418+ }
419+
385 function test_quicklist_click_data() {
420 function test_quicklist_click_data() {
386 return [
421 return [
387 {tag: "non-clickable", index: 1, clickable: false },
422 {tag: "non-clickable", index: 1, clickable: false },
@@ -392,19 +427,20 @@ Item {
392 function test_quicklist_click(data) {
427 function test_quicklist_click(data) {
393 revealer.dragLauncherIntoView();
428 revealer.dragLauncherIntoView();
394 var clickedItem = findChild(launcher, "launcherDelegate5")
429 var clickedItem = findChild(launcher, "launcherDelegate5")
395− var dndArea = findChild(launcher, "dndArea")
430+ var quickList = findChild(launcher, "quickList")
431+ var quickListShape = findChild(launcher, "quickListShape")
396
432
397 // Initial state
433 // Initial state
398− compare(dndArea.quickListPopover, null)
434+ tryCompare(quickListShape, "visible", false)
399
435
400 // Doing longpress
436 // Doing longpress
401 mousePress(clickedItem, clickedItem.width / 2, clickedItem.height / 2)
437 mousePress(clickedItem, clickedItem.width / 2, clickedItem.height / 2)
402 tryCompare(clickedItem, "itemOpacity", 0) // Wait for longpress to happen
438 tryCompare(clickedItem, "itemOpacity", 0) // Wait for longpress to happen
403− verify(dndArea.quickListPopover != null)
439+ verify(quickListShape.visible, "QuickList must be visible")
404
440
405 mouseRelease(clickedItem);
441 mouseRelease(clickedItem);
406
442
407− var quickListEntry = findChild(dndArea.quickListPopover, "quickListEntry" + data.index)
443+ var quickListEntry = findChild(quickList, "quickListEntry" + data.index)
408
444
409 signalSpy.clear();
445 signalSpy.clear();
410 signalSpy.signalName = "quickListTriggered"
446 signalSpy.signalName = "quickListTriggered"
@@ -413,7 +449,7 @@ Item {
413
449
414 if (data.clickable) {
450 if (data.clickable) {
415 // QuickList needs to be closed when some clickable item is clicked
451 // QuickList needs to be closed when some clickable item is clicked
416− tryCompare(dndArea, "quickListPopover", null)
452+ tryCompare(quickListShape, "visible", false)
417
453
418 compare(signalSpy.count, 1, "Quicklist signal wasn't triggered")
454 compare(signalSpy.count, 1, "Quicklist signal wasn't triggered")
419 compare(signalSpy.signalArguments[0][0], LauncherModel.get(5).appId)
455 compare(signalSpy.signalArguments[0][0], LauncherModel.get(5).appId)
@@ -422,13 +458,13 @@ Item {
422 } else {
458 } else {
423
459
424 // QuickList must not be closed when a non-clickable item is clicked
460 // QuickList must not be closed when a non-clickable item is clicked
425− verify(dndArea.quickListPopover != null)
461+ verify(quickListShape.visible, "QuickList must be visible")
426
462
427 compare(signalSpy.count, 0, "Quicklist signal must NOT be triggered when clicking a non-clickable item")
463 compare(signalSpy.count, 0, "Quicklist signal must NOT be triggered when clicking a non-clickable item")
428
464
429 // Click somewhere in the empty space to dismiss the quicklist
465 // Click somewhere in the empty space to dismiss the quicklist
430 mouseClick(launcher, launcher.width - units.gu(1), units.gu(1));
466 mouseClick(launcher, launcher.width - units.gu(1), units.gu(1));
431− tryCompare(dndArea, "quickListPopover", null)
467+ tryCompare(quickListShape, "visible", false)
432 }
468 }
433 }
469 }
434 }
470 }
tests/qmltests/tst_Shell.qml+37−9
@@ -111,14 +111,14 @@ Item {
111 waitUntilApplicationWindowIsFullyVisible();
111 waitUntilApplicationWindowIsFullyVisible();
112
112
113 // Minimize the application we just launched
113 // Minimize the application we just launched
114− swipeFromLeftEdge();
114+ swipeFromLeftEdge(units.gu(27));
115
115
116 waitForUIToSettle();
116 waitForUIToSettle();
117
117
118 checkRightEdgeDragWithMinimizedApp();
118 checkRightEdgeDragWithMinimizedApp();
119
119
120 // Minimize that application once again
120 // Minimize that application once again
121− swipeFromLeftEdge();
121+ swipeFromLeftEdge(units.gu(27));
122
122
123 // Right edge behavior should now be the same as before that app,
123 // Right edge behavior should now be the same as before that app,
124 // was launched. Manually cleanup beforehand to get to initial
124 // was launched. Manually cleanup beforehand to get to initial
@@ -130,8 +130,10 @@ Item {
130
130
131 function test_leftEdgeDrag_data() {
131 function test_leftEdgeDrag_data() {
132 return [
132 return [
133− {tag: "without launcher", revealLauncher: false},
133+ {tag: "without launcher", revealLauncher: false, swipeLength: units.gu(27), appHides: true},
134− {tag: "with launcher", revealLauncher: true},
134+ {tag: "with launcher", revealLauncher: true, swipeLength: units.gu(27), appHides: true},
135+ {tag: "small swipe", revealLauncher: false, swipeLength: units.gu(25), appHides: false},
136+ {tag: "long swipe", revealLauncher: false, swipeLength: units.gu(27), appHides: true}
135 ];
137 ];
136 }
138 }
137
139
@@ -144,8 +146,11 @@ Item {
144 dragLauncherIntoView();
146 dragLauncherIntoView();
145 }
147 }
146
148
147− swipeFromLeftEdge();
149+ swipeFromLeftEdge(data.swipeLength);
150+ if (data.appHides)
148− waitUntilApplicationWindowIsFullyHidden();
151+ waitUntilApplicationWindowIsFullyHidden();
152+ else
153+ waitUntilApplicationWindowIsFullyVisible();
149 }
154 }
150
155
151 function test_suspend() {
156 function test_suspend() {
@@ -293,7 +298,7 @@ Item {
293 waitUntilApplicationWindowIsFullyVisible();
298 waitUntilApplicationWindowIsFullyVisible();
294
299
295 // Minimize the application we just launched
300 // Minimize the application we just launched
296− swipeFromLeftEdge();
301+ swipeFromLeftEdge(units.gu(27));
297
302
298 // Wait for the whole UI to settle down
303 // Wait for the whole UI to settle down
299 waitUntilApplicationWindowIsFullyHidden();
304 waitUntilApplicationWindowIsFullyHidden();
@@ -376,10 +381,10 @@ Item {
376 }
381 }
377 }
382 }
378
383
379− function swipeFromLeftEdge() {
384+ function swipeFromLeftEdge(swipeLength) {
380 var touchStartX = 2;
385 var touchStartX = 2;
381 var touchStartY = shell.height / 2;
386 var touchStartY = shell.height / 2;
382− touchFlick(shell, touchStartX, touchStartY, shell.width * 0.75, touchStartY);
387+ touchFlick(shell, touchStartX, touchStartY, swipeLength, touchStartY);
383 }
388 }
384
389
385 function swipeLeftFromCenter() {
390 function swipeLeftFromCenter() {
@@ -483,5 +488,28 @@ Item {
483 var dash = findChild(shell, "dash");
488 var dash = findChild(shell, "dash");
484 tryCompare(dash, "shown", data.expectedShown);
489 tryCompare(dash, "shown", data.expectedShown);
485 }
490 }
491+
492+ function test_searchIndicatorHidesOnAppFocus() {
493+ var searchIndicator = findChild(shell, "container")
494+ tryCompare(searchIndicator, "opacity", 1)
495+ dragLauncherIntoView();
496+
497+ // Launch an app from the launcher
498+ tapOnAppIconInLauncher();
499+ waitUntilApplicationWindowIsFullyVisible();
500+
501+ tryCompare(searchIndicator, "opacity", 0);
502+ }
503+
504+ function test_searchIndicatorHidesOnGreeterShown() {
505+ var searchIndicator = findChild(shell, "container")
506+ var greeter = findChild(shell, "greeter");
507+
508+ tryCompare(searchIndicator, "opacity", 1)
509+
510+ greeter.show()
511+ tryCompare(greeter, "shown", true)
512+ tryCompare(searchIndicator, "opacity", 0)
513+ }
486 }
514 }
487}
515}