on xenial help sign in
Merged with trunk
commitc619dc80c9c22d6184ecf2a048b60b7dd94f8c47 authorNick Dedekind <nicholas.dedekind@gmail.com> dateWed 6 Nov 2013, 10:26 (13 years ago) parent247b259d0bfd16
78 files changed+2040−609
Components/Carousel.qml+33−14 Components/FilterGrid.qml+3 Components/LazyImage.qml+2−2 Components/PageHeader.qml+4 Components/ResponsiveGridView.qml+10 Components/Tile.qml+9 Components/Time.js+9−9 Components/UbuntuShapeForItem.qml+1 Dash/Apps/AppPreview.qml+9 Dash/Apps/ApplicationsFilterGrid.qml+14−5 Dash/Apps/DashPluginFilterGrid.qml+2−4 Dash/DashBar.qml+2−2 Dash/DashContent.qml+2−2 Dash/DashPreview.qml+9−3 Launcher/QuicklistForegroundStyle.qml → Dash/DashPreviewPlaceholder.qml+7−5 Dash/Generic/GenericFilterGrid.qml+4−6 Dash/Generic/GenericPreview.qml+4−1 Dash/Generic/WeatherFilterGrid.qml+2−4 Dash/GenericScopeView.qml+223−55 Dash/Music/AlbumTile.qml−116 Dash/Music/MusicFilterGrid.qml+2−4 Dash/SearchableResultModel.qml+4 Dash/Video/VideoFilterGrid.qml+2−4 Dash/graphics/tooltip_arrow@27.png Greeter/Greeter.qml+1−1 Launcher/Launcher.qml+1−1 Launcher/LauncherPanel.qml+87−46 Notifications/Notification.qml+2−1 data/unity8-setcap.conf−32 debian/changelog+127 debian/control+1−1 debian/unity8-fake-env.install+1 debian/unity8.install−1 debian/unity8.maintscript+1 main.cpp+8−8 plugins/Unity/Indicators/CMakeLists.txt+1 plugins/Unity/Indicators/Messaging/qml/MessageMenuItemFactory.qml+3−4 plugins/Unity/Indicators/Messaging/qml/SimpleTextMessage.qml+9−2 plugins/Unity/Indicators/Messaging/qml/SnapDecision.qml+9−3 plugins/Unity/Indicators/Messaging/qml/utils.js−27 plugins/Unity/Indicators/plugin.cpp+2 plugins/Unity/Indicators/qml/IndicatorPage.qml+27−6 plugins/Unity/Indicators/qml/SliderMenuItem.qml+3−2 plugins/Unity/Indicators/timeformatter.cpp+172 plugins/Unity/Indicators/timeformatter.h+54 plugins/Unity/scope.cpp+2 plugins/Unity/scope.h+1 po/ar.po+33−44 po/de.po+11−28 po/fa.po+223 po/ta.po+229 po/uk.po+6−25 run_on_device+24−4 tests/autopilot/unity8/__init__.py+46−1 tests/autopilot/unity8/process_helpers.py+116 tests/autopilot/unity8/shell/emulators/create_interactive_notification.py tests/autopilot/unity8/shell/emulators/dash.py+7−17 tests/autopilot/unity8/shell/emulators/hud.py+12−4 tests/autopilot/unity8/shell/emulators/main_window.py+1−1 tests/autopilot/unity8/shell/tests/__init__.py+66−27 tests/autopilot/unity8/shell/tests/test_notifications.py+51−43 tests/mocks/CMakeLists.txt+5 tests/mocks/Unity/fake_preview.cpp+9−1 tests/mocks/Unity/fake_preview.h+2 tests/mocks/Unity/fake_scope.cpp+25−4 tests/mocks/Unity/fake_scope.h+7−2 tests/mocks/Unity/fake_unity_plugin.cpp+3 tests/mocks/data/applications/camera-app.desktop tests/plugins/Unity/Indicators/CMakeLists.txt+1 tests/plugins/Unity/Indicators/timeformattertest.cpp+55 tests/qmltests/Dash/Apps/tst_AppPreview.qml+3−2 tests/qmltests/Dash/Apps/tst_RunningApplicationsGrid.qml+1 tests/qmltests/Dash/Movie/tst_MoviePreview.qml+1 tests/qmltests/Dash/tst_GenericPreview.qml+1 tests/qmltests/Dash/tst_GenericScopeView.qml+131−10 tests/qmltests/Greeter/tst_Phone.qml+18−5 tests/qmltests/Launcher/tst_Launcher.qml+47−11 tests/qmltests/tst_Shell.qml+37−9
Components/Carousel.qml+33−14
@@ -45,18 +45,24 @@ Item {
4545 property int drawBuffer: width / pathItemCount // an "ok" value - but values used from the listView cause loops
4646 /// The selected item can be shown in a different size controlled by selectedItemScaleFactor
4747 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
4854
4955 /// Emitted when the user clicked on an item
5056 /// @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
5258 /// @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)
5460
5561 /// Emitted when the user pressed and held on an item
5662 /// @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
5864 /// @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)
6066
6167 implicitHeight: listView.tileHeight * selectedItemScaleFactor
6268
@@ -72,9 +78,10 @@ Item {
7278 id: listView
7379
7480 property real minimumTileWidth: 0
75− property real newContentX: -1
81+ property real newContentX: disabledNewContentX
7682 property real pathItemCount: referenceWidth / referenceTileWidth
7783 property real tileAspectRatio: 1
84+ property int highlightIndex: -1
7885
7986 /* The positioning and scaling of the items in the carousel is based on the variable
8087 'continuousIndex', a continuous real variable between [0, 'carousel.model.count'],
@@ -100,6 +107,7 @@ Item {
100107 readonly property real kMiddleIndex: (realWidth / 2) / tileWidth - 0.5
101108 readonly property real kXBeginningEnd: 1 / tileWidth + kMiddleIndex / gapToMiddlePhase
102109 readonly property real maximumItemTranslation: (listView.tileWidth * 3) / listView.scaleFactor
110+ readonly property real disabledNewContentX: -carousel.drawBuffer - 1
103111 readonly property real realContentWidth: contentWidth - 2 * carousel.drawBuffer
104112 readonly property real realContentX: contentX + carousel.drawBuffer
105113 readonly property real realPathItemCount: Math.min(realWidth / tileWidth, pathItemCount)
@@ -135,13 +143,14 @@ Item {
135143 height: listView.tileHeight
136144 }
137145
138− boundsBehavior: Flickable.StopAtBounds
146+ boundsBehavior: Flickable.DragOverBounds
139147 cacheBuffer: carousel.cacheBuffer
140148 flickDeceleration: Math.max(1500 * Math.pow(realWidth / referenceWidth, 1.5), 1500) // 1500 is platform default
141149 maximumFlickVelocity: Math.max(2500 * Math.pow(realWidth / referenceWidth, 1.5), 2500) // 2500 is platform default
142150 orientation: ListView.Horizontal
143151
144152 function itemClicked(index, delegateItem) {
153+ listView.currentIndex = index
145154 var x = CarouselJS.getXFromContinuousIndex(index,
146155 realWidth,
147156 realContentWidth,
@@ -150,11 +159,11 @@ Item {
150159 gapToEndPhase,
151160 carousel.drawBuffer)
152161
153− if (Math.abs(x - contentX) < 1) {
162+ if (Math.abs(x - contentX) < 1 && delegateItem !== undefined) {
154163 /* We're clicking the selected item and
155164 we're in the neighbourhood of radius 1 pixel from it.
156165 Let's emit the clicked signal. */
157− carousel.clicked(index, delegateItem, delegateItem.y)
166+ carousel.clicked(index, listView.model, delegateItem.y)
158167 return
159168 }
160169
@@ -174,11 +183,11 @@ Item {
174183 gapToEndPhase,
175184 carousel.drawBuffer);
176185
177− if (Math.abs(x - contentX) < 1) {
186+ if (Math.abs(x - contentX) < 1 && delegateItem !== undefined) {
178187 /* We're pressAndHold the selected item and
179188 we're in the neighbourhood of radius 1 pixel from it.
180189 Let's emit the pressAndHold signal. */
181− carousel.pressAndHold(index, delegateItem, delegateItem.y);
190+ carousel.pressAndHold(index, listView.model, delegateItem.y);
182191 return;
183192 }
184193
@@ -189,10 +198,16 @@ Item {
189198 newContentXAnimation.start();
190199 }
191200
201+ onHighlightIndexChanged: {
202+ if (highlightIndex != -1) {
203+ itemClicked(highlightIndex)
204+ }
205+ }
206+
192207 onMovementStarted: {
193208 stepAnimation.stop()
194209 newContentXAnimation.stop()
195− newContentX = -1
210+ newContentX = disabledNewContentX
196211 }
197212 onMovementEnded: {
198213 if (realContentX > 0 && realContentX < realContentWidth - realWidth)
@@ -228,7 +243,7 @@ Item {
228243 easing.type: Easing.InOutQuad
229244 }
230245 ScriptAction {
231− script: listView.newContentX = -1
246+ script: listView.newContentX = listView.disabledNewContentX
232247 }
233248 }
234249
@@ -253,7 +268,7 @@ Item {
253268 readonly property bool explicitScale: (!listView.moving ||
254269 listView.realContentX <= 0 ||
255270 listView.realContentX >= listView.realContentWidth - listView.realWidth) &&
256− listView.newContentX < 0 &&
271+ listView.newContentX === listView.disabledNewContentX &&
257272 index === listView.selectedIndex
258273 readonly property real cachedTiles: listView.realPathItemCount + carousel.drawBuffer / listView.tileWidth
259274 readonly property real distance: listView.continuousIndex - index
@@ -272,14 +287,18 @@ Item {
272287 itemTranslationScale,
273288 listView.maximumItemTranslation)
274289
290+ readonly property real xTransform: listView.viewTranslation + translationX * listView.scaleFactor
291+ readonly property real center: x - listView.contentX + xTransform - drawBuffer + (width/2)
292+
275293 width: listView.tileWidth
276294 height: listView.tileHeight
277295 scale: itemScale * explicitScaleFactor
278296 sourceComponent: itemComponent
279297 z: cachedTiles - Math.abs(index - listView.selectedIndex)
298+ opacity: highlightIndex == -1 ? 1 : (highlightIndex == index ? 0.6 : 0.2)
280299
281300 transform: Translate {
282− x: listView.viewTranslation + translationX * listView.scaleFactor
301+ x: xTransform
283302 }
284303
285304 Behavior on explicitScaleFactor {
Components/FilterGrid.qml+3
@@ -54,6 +54,7 @@ Item {
5454 property alias delegateWidth: iconTileGrid.delegateWidth
5555 property alias delegateHeight: iconTileGrid.delegateHeight
5656 property alias verticalSpacing: iconTileGrid.verticalSpacing
57+ readonly property alias margins: iconTileGrid.margins
5758 property alias delegate: iconTileGrid.delegate
5859 property alias cellWidth: iconTileGrid.cellWidth
5960 property alias cellHeight: iconTileGrid.cellHeight
@@ -62,6 +63,8 @@ Item {
6263 readonly property alias flicking: iconTileGrid.flicking
6364 readonly property alias moving: iconTileGrid.moving
6465 readonly property alias pressDelay: iconTileGrid.pressDelay
66+ property alias highlightIndex: iconTileGrid.highlightIndex
67+ readonly property alias currentItem: iconTileGrid.currentItem
6568
6669 height: !filterAnimation.running ? childrenRect.height : height
6770 clip: filterAnimation.running
Components/LazyImage.qml+2−2
@@ -100,10 +100,10 @@ Item {
100100 verticalAlignment: Image.AlignVCenter
101101 sourceSize.width: root.scaleTo == "width" ? root.width
102102 : root.scaleTo == "fit" && root.width < root.height ? root.width
103− : undefined
103+ : 0
104104 sourceSize.height: root.scaleTo == "height" ? root.height
105105 : root.scale == "fit" && root.height < root.width ? root.height
106− : undefined
106+ : 0
107107 }
108108 }
109109
Components/PageHeader.qml+4
@@ -48,6 +48,10 @@ Item {
4848 searchField.focus = false;
4949 }
5050
51+ function unfocus() {
52+ searchField.focus = false;
53+ }
54+
5155 Connections {
5256 target: greeter
5357 onShownChanged: if (shown) resetSearch()
Components/ResponsiveGridView.qml+10
@@ -26,6 +26,7 @@ Item {
2626 property int maximumNumberOfColumns: 6
2727 readonly property int columns: gridView.columns
2828 property alias verticalSpacing: gridView.verticalSpacing
29+ readonly property alias margins: gridView.margin
2930 property int delegateWidth
3031 property int delegateHeight
3132 property alias model: gridView.model
@@ -41,6 +42,8 @@ Item {
4142 readonly property alias pressDelay: gridView.pressDelay
4243 property alias delegateCreationBegin: gridView.delegateCreationBegin
4344 property alias delegateCreationEnd: gridView.delegateCreationEnd
45+ property alias highlightIndex: gridView.highlightIndex
46+ readonly property alias currentItem: gridView.currentItem
4447
4548 function contentHeightForRows(rows) {
4649 return rows * cellHeight + verticalSpacing
@@ -77,8 +80,15 @@ Item {
7780 property real horizontalSpacing: spacingForColumns(columns)
7881 property real verticalSpacing: horizontalSpacing
7982 property int margin: allocatableHorizontalSpace - columns * horizontalSpacing
83+ property int highlightIndex: -1
8084
8185 cellWidth: delegateWidth + horizontalSpacing
8286 cellHeight: delegateHeight + verticalSpacing
87+
88+ onHighlightIndexChanged: {
89+ if (highlightIndex != -1) {
90+ currentIndex = highlightIndex
91+ }
92+ }
8393 }
8494}
Components/Tile.qml+9
@@ -26,5 +26,14 @@ AbstractButton {
2626 property int imageHeight
2727 property int maximumLineCount: 1
2828
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+
2938 style: TileStyle {}
3039}
Components/Time.js+9−9
@@ -16,15 +16,15 @@
1616
1717.pragma library
1818
19−var factors = {
20− "second": [ 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" ],
22− "hour": [ 60, "an hour ago", "in an hour", "%1 hours ago", "in %1 hours" ],
23− "day": [ 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" ],
25− "month": [ 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" ]
27−}
19+var factors = [
20+ [ 1000, "a second ago", "in a second", "%1 seconds ago", "in %1 seconds" ],
21+ [ 60, "a minute ago", "in a minute", "%1 minutes ago", "in %1 minutes" ],
22+ [ 60, "an hour ago", "in an hour", "%1 hours ago", "in %1 hours" ],
23+ [ 24, "yesterday", "tomorrow", "%1 days ago", "in %1 days" ],
24+ [ 7, "a week ago", "in a week", "about %1 weeks ago", "in about %1 weeks" ],
25+ [ 4.35, "a month ago", "in a month", "%1 months ago", "in %1 months" ], // == 365.25 days / 12 months / 7 days
26+ [ 12, "a year ago", "in a year", "%1 years ago", "in %1 years" ]
27+]
2828
2929function readableFromNow(date, now) {
3030 var then = new Date(date);
Components/UbuntuShapeForItem.qml+1
@@ -31,6 +31,7 @@ Item {
3131
3232 ShaderEffectSource {
3333 id: source
34+ anchors.centerIn: parent // Placed under shape, so it's hidden
3435 width: 1
3536 height: 1
3637 hideSource: true
Dash/Apps/AppPreview.qml+9
@@ -44,6 +44,15 @@ GenericPreview {
4444 model: previewData.infoMap["more-screenshots"] != null ? previewData.infoMap["more-screenshots"].value : [previewData.image]
4545 cacheBuffer: width * 3
4646
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+
4756 LazyImage {
4857 objectName: "placeholderScreenshot"
4958 anchors {
Dash/Apps/ApplicationsFilterGrid.qml+14−5
@@ -19,7 +19,7 @@ import "../../Components"
1919import "../../Applications"
2020
2121FilterGrid {
22− id: filterGrid
22+ id: filtergrid
2323
2424 filter: false
2525 minimumHorizontalSpacing: units.gu(0.5)
@@ -28,9 +28,11 @@ FilterGrid {
2828 delegateHeight: units.gu(9.5)
2929 verticalSpacing: units.gu(2)
3030
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)
3233
3334 delegate: Tile {
35+ id: tile
3436 objectName: "delegate" + index
3537 Application {
3638 id: application
@@ -44,12 +46,19 @@ FilterGrid {
4446
4547 property string icon: model.icon ? model.icon : "../../graphics/applicationIcons/" + application.icon + ".png" // FIXME: this is temporary
4648
47− width: filterGrid.cellWidth
48− height: filterGrid.cellHeight
49+ width: filtergrid.cellWidth
50+ height: filtergrid.cellHeight
4951 text: model.title ? model.title : application.name // FIXME: this is temporary
5052 imageWidth: units.gu(8)
5153 imageHeight: units.gu(7.5)
5254 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+ }
5463 }
5564}
Dash/Apps/DashPluginFilterGrid.qml+2−4
@@ -37,13 +37,11 @@ GenericFilterGrid {
3737 }
3838
3939 onClicked: {
40− var data = { model: model }
41− filtergrid.clicked(index, data, tile.y)
40+ filtergrid.clicked(index, filtergrid.model, tile.y)
4241 }
4342
4443 onPressAndHold: {
45− var data = { model: model }
46− filtergrid.pressAndHold(index, data, tile.y)
44+ filtergrid.pressAndHold(index, filtergrid.model, tile.y)
4745 }
4846 }
4947}
Dash/DashBar.qml+2−2
@@ -35,7 +35,7 @@ Item {
3535
3636 function startNavigation() {
3737 timeout.stop()
38− panel.opened = true
38+ panel.open()
3939 }
4040
4141 function stopNavigation() {
@@ -43,7 +43,7 @@ Item {
4343 }
4444
4545 function finishNavigation() {
46− panel.opened = false
46+ panel.close()
4747 }
4848
4949 Timer {
Dash/DashContent.qml+2−2
@@ -116,8 +116,8 @@ Item {
116116 source: scopeMapper.map(scope.id)
117117 objectName: scope.id + " loader"
118118
119− readonly property bool previewShown: item.previewShown
120− readonly property bool moving: item.moving
119+ readonly property bool previewShown: item ? item.previewShown : false
120+ readonly property bool moving: item ? item.moving : false
121121
122122 // these are needed for autopilot tests
123123 readonly property string scopeId: scope.id
Dash/DashPreview.qml+9−3
@@ -17,11 +17,12 @@
1717import QtQuick 2.0
1818import Ubuntu.Components 0.1
1919
20−Rectangle {
20+Item {
2121 id: root
2222
2323 property int keyboardSize: Qt.inputMethod.visible ? Qt.inputMethod.keyboardRectangle.height : 0
2424 property var previewData
25+ property alias showProcessingAction: waitingForActionMouseArea.enabled
2526
2627 property real previewWidthRatio: 0.5
2728
@@ -35,7 +36,6 @@ Rectangle {
3536 readonly property int columnWidth: narrowMode ? contentRow.width : (contentRow.width / 3) - contentRow.spacing
3637 readonly property int contentSpacing: units.gu(3)
3738
38− color: Qt.rgba(0, 0, 0, .3)
3939 clip: true
4040
4141 function ensureVisible(item) {
@@ -60,7 +60,6 @@ Rectangle {
6060 anchors.fill: parent
6161 }
6262
63−
6463 Row {
6564 id: contentRow
6665 anchors {
@@ -196,4 +195,11 @@ Rectangle {
196195 }
197196 sourceComponent: root.header
198197 }
198+
199+ MouseArea {
200+ id: waitingForActionMouseArea
201+ objectName: "waitingForActionMouseArea"
202+ anchors.fill: parent
203+ enabled: false
204+ }
199205}
Launcher/QuicklistForegroundStyle.qml → Dash/DashPreviewPlaceholder.qmlrenamed+7−5
@@ -14,10 +14,12 @@
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1515 */
1616
17−import Ubuntu.Components.Themes.Ambiance 0.1
17+import QtQuick 2.0
18+import Ubuntu.Components 0.1
1819
19−PopoverForegroundStyle {
20− bubbleColor: "#221e1c"
21− bubbleOpacity: 0.9
22− arrowSource: "graphics/quicklist_tooltip.png"
20+DashPreview {
21+ ActivityIndicator {
22+ anchors.centerIn: parent
23+ running: true
24+ }
2325}
Dash/Generic/GenericFilterGrid.qml+4−6
@@ -28,8 +28,8 @@ FilterGrid {
2828 property int iconWidth: units.gu(8)
2929 property int iconHeight: units.gu(7.5)
3030
31− signal clicked(int index, var delegateItem, real itemY)
32− signal pressAndHold(int index, var delegateItem, real itemY)
31+ signal clicked(int index, var model, real itemY)
32+ signal pressAndHold(int index, var model, real itemY)
3333
3434 delegate: Tile {
3535 id: tile
@@ -42,13 +42,11 @@ FilterGrid {
4242 source: model.icon
4343
4444 onClicked: {
45− var data = { model: model }
46− filtergrid.clicked(index, data, tile.y)
45+ filtergrid.clicked(index, filtergrid.model, tile.y)
4746 }
4847
4948 onPressAndHold: {
50− var data = { model: model }
51− filtergrid.pressAndHold(index, data, tile.y)
49+ filtergrid.pressAndHold(index, filtergrid.model, tile.y)
5250 }
5351 }
5452}
Dash/Generic/GenericPreview.qml+4−1
@@ -71,7 +71,10 @@ DashPreview {
7171 text: modelData.displayName
7272 iconSource: modelData.iconHint
7373 iconPosition: "right"
74− onClicked: previewData.execute(modelData.id, { })
74+ onClicked: {
75+ previewData.execute(modelData.id, { })
76+ genericPreview.showProcessingAction = true;
77+ }
7578 }
7679 }
7780 }
Dash/Generic/WeatherFilterGrid.qml+2−4
@@ -37,13 +37,11 @@ GenericFilterGrid {
3737 style: FlatTileStyle {}
3838
3939 onClicked: {
40− var data = { model: model }
41− filtergrid.clicked(index, data, tile.y)
40+ filtergrid.clicked(index, filtergrid.model, tile.y)
4241 }
4342
4443 onPressAndHold: {
45− var data = { model: model }
46− filtergrid.pressAndHold(index, data, tile.y)
44+ filtergrid.pressAndHold(index, filtergrid.model, tile.y)
4745 }
4846 }
4947}
Dash/GenericScopeView.qml+223−55
@@ -21,14 +21,14 @@ import "../Components/ListItems" as ListItems
2121
2222ScopeView {
2323 id: scopeView
24− readonly property alias previewShown: previewLoader.onScreen
24+ readonly property alias previewShown: previewListView.onScreen
2525 property bool enableHeightBehaviorOnNextCreation: false
2626
2727 moving: categoryView.moving
2828
2929 onIsCurrentChanged: {
3030 pageHeader.resetSearch();
31− previewLoader.open = false;
31+ previewListView.open = false;
3232 }
3333
3434 onMovementStarted: categoryView.showHeader()
@@ -57,8 +57,8 @@ ScopeView {
5757
5858 Connections {
5959 target: scopeView.scope
60− onShowDash: previewLoader.open = false;
61− onHideDash: previewLoader.open = false;
60+ onShowDash: previewListView.open = false;
61+ onHideDash: previewListView.open = false;
6262 }
6363
6464 ScopeListView {
@@ -66,12 +66,18 @@ ScopeView {
6666 objectName: "categoryListView"
6767 anchors.fill: parent
6868 model: scopeView.categories
69− forceNoClip: previewLoader.onScreen
69+ forceNoClip: previewListView.onScreen
7070
7171 onAtYEndChanged: if (atYEnd) endReached()
7272 onMovingChanged: if (moving && atYEnd) endReached()
7373
7474 property string expandedCategoryId: ""
75+ signal correctExpandedCategory();
76+
77+ Behavior on contentY {
78+ enabled: previewListView.open
79+ UbuntuNumberAnimation {}
80+ }
7581
7682 delegate: ListItems.Base {
7783 id: baseItem
@@ -123,33 +129,50 @@ ScopeView {
123129 Connections {
124130 target: rendererLoader.item
125131 onClicked: {
132+ // Prepare the preview in case activate() triggers a preview only
126133 effect.positionPx = mapToItem(categoryView, 0, itemY).y
127− scopeView.scope.activate(delegateItem.model.uri,
128− delegateItem.model.icon,
129− delegateItem.model.category,
130− 0,
131− delegateItem.model.mimetype,
132− delegateItem.model.title,
133− delegateItem.model.comment,
134− delegateItem.model.dndUri,
135− delegateItem.model.metadata)
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")) {
144+ scopeView.scope.activate(item.uri, item.icon, item.category, 0, item.mimetype, item.title,
145+ item.comment, item.dndUri, item.metadata)
146+ } else {
147+ previewListView.open = true
148+
149+ scopeView.scope.preview( item.uri, item.icon, item.category, 0, item.mimetype, item.title,
150+ item.comment, item.dndUri, item.metadata)
151+ }
136152 }
137153 onPressAndHold: {
138154 effect.positionPx = mapToItem(categoryView, 0, itemY).y
139− scopeView.scope.preview( delegateItem.model.uri,
140− delegateItem.model.icon,
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)
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)
163+ scopeView.scope.preview( item.uri, item.icon, item.category, 0, item.mimetype, item.title,
164+ item.comment, item.dndUri, item.metadata)
148165 }
149166 }
150167 Connections {
151168 target: categoryView
152169 onExpandedCategoryIdChanged: {
170+ collapseAllButExpandedCategory();
171+ }
172+ onCorrectExpandedCategory: {
173+ collapseAllButExpandedCategory();
174+ }
175+ function collapseAllButExpandedCategory() {
153176 var item = rendererLoader.item;
154177 if (item.expandable) {
155178 var shouldFilter = categoryId != categoryView.expandedCategoryId;
@@ -157,13 +180,15 @@ ScopeView {
157180 // If the filter animation will be seen start it, otherwise, just flip the switch
158181 var shrinkingVisible = shouldFilter && y + item.collapsedHeight < categoryView.height;
159182 var growingVisible = !shouldFilter && y + height < categoryView.height;
160− if (shrinkingVisible || growingVisible) {
161− item.startFilterAnimation(shouldFilter)
162− } else {
163− item.filter = shouldFilter;
164− }
165− if (!shouldFilter) {
166− categoryView.maximizeVisibleArea(index, item.uncollapsedHeight);
183+ if (!previewListView.open || !shouldFilter) {
184+ if (shrinkingVisible || growingVisible) {
185+ item.startFilterAnimation(shouldFilter)
186+ } else {
187+ item.filter = shouldFilter;
188+ }
189+ if (!shouldFilter && !previewListView.open) {
190+ categoryView.maximizeVisibleArea(index, item.uncollapsedHeight);
191+ }
167192 }
168193 }
169194 }
@@ -269,6 +294,7 @@ ScopeView {
269294
270295 OpenEffect {
271296 id: effect
297+ objectName: "openEffect"
272298 anchors {
273299 fill: parent
274300 bottomMargin: -bottomOverflow
@@ -281,29 +307,44 @@ ScopeView {
281307 topOpacity: (1 - gap * 1.2)
282308 bottomGapPx: positionPx + gap * (targetBottomGapPx - positionPx)
283309 bottomOverflow: units.gu(20)
284− bottomOpacity: 1 - (gap * 0.8)
310+ live: !expansionAnimation.running
285311
286312 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
288314
289315 Behavior on gap {
290316 NumberAnimation {
317+ id: expansionAnimation
291318 duration: 200
292319 easing.type: Easing.InOutQuad
293320 onRunningChanged: {
294− if (!previewLoader.open && !running) {
295− previewLoader.onScreen = false
321+ if (!previewListView.open && !running) {
322+ previewListView.onScreen = false
296323 }
297324 }
298325 }
299326 }
327+ Behavior on positionPx {
328+ enabled: previewListView.open
329+ UbuntuNumberAnimation {}
330+ }
300331 }
301332
302333 Connections {
303334 target: scopeView.scope
304335 onPreviewReady: {
305− previewLoader.previewData = preview
306− previewLoader.open = true
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+ }
347+ previewListView.currentItem.previewData = preview
307348 }
308349 }
309350
@@ -311,18 +352,9 @@ ScopeView {
311352 id: previewDelegateMapper
312353 }
313354
314− Connections {
315− ignoreUnknownSignals: true
316− target: previewLoader.valid ? previewLoader.item : null
317− onClose: {
318− previewLoader.open = false
319− }
320− }
321−
322− Loader {
323− objectName: "previewLoader"
324− id: previewLoader
325− property var previewData
355+ ListView {
356+ id: previewListView
357+ objectName: "previewListView"
326358 height: effect.bottomGapPx - effect.topGapPx
327359 anchors {
328360 top: parent.top
@@ -330,32 +362,168 @@ ScopeView {
330362 left: parent.left
331363 right: parent.right
332364 }
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+ }
334413
335414 property bool open: false
336415 property bool onScreen: false
337− property bool valid: item !== null
338416
339417 onOpenChanged: {
340418 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;
342431 }
343432 }
344433
345− onLoaded: {
346− item.previewData = Qt.binding(function() { return previewLoader.previewData })
434+ Rectangle {
435+ anchors.fill: parent
436+ color: Qt.rgba(0, 0, 0, .3)
437+ z: -1
347438 }
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"
348515 }
349516
350517 // TODO: Move as InverseMouseArea to DashPreview
351518 MouseArea {
352− enabled: previewLoader.onScreen
519+ objectName: "closePreviewMouseArea"
520+ enabled: previewListView.onScreen
353521 anchors {
354522 fill: parent
355523 topMargin: effect.bottomGapPx
356524 }
357525 onClicked: {
358− previewLoader.open = false;
526+ previewListView.open = false;
359527 }
360528 }
361529}
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 {
4444 }
4545
4646 onClicked: {
47− var data = { model: model }
48− filtergrid.clicked(index, data, tile.y)
47+ filtergrid.clicked(index, filtergrid.model, tile.y)
4948 }
5049
5150 onPressAndHold: {
52− var data = { model: model }
53− filtergrid.pressAndHold(index, data, tile.y)
51+ filtergrid.pressAndHold(index, filtergrid.model, tile.y)
5452 }
5553 }
5654}
Dash/SearchableResultModel.qml+4
@@ -25,6 +25,10 @@ SortFilterProxyModel {
2525
2626 filterRole: CategoryResults.RoleTitle
2727
28+ function get(index) {
29+ return model.get(mapToSource(index))
30+ }
31+
2832 onSearchQueryChanged: {
2933 if (searchQuery.length == 0) {
3034 filterRegExp = RegExp("");
Dash/Video/VideoFilterGrid.qml+2−4
@@ -42,13 +42,11 @@ GenericFilterGrid {
4242 style: VideoTileStyle {}
4343
4444 onClicked: {
45− var data = { model: model }
46− filtergrid.clicked(index, data, tile.y)
45+ filtergrid.clicked(index, filtergrid.model, tile.y)
4746 }
4847
4948 onPressAndHold: {
50− var data = { model: model }
51− filtergrid.pressAndHold(index, data, tile.y)
49+ filtergrid.pressAndHold(index, filtergrid.model, tile.y)
5250 }
5351 }
5452}
Dash/graphics/tooltip_arrow@27.pngnew file

Binary file not shown.

Greeter/Greeter.qml+1−1
@@ -52,7 +52,7 @@ Showable {
5252 signal unlocked(int uid)
5353
5454 onRightTeaserPressedChanged: {
55− if (rightTeaserPressed && !locked) {
55+ if (rightTeaserPressed && !locked && x == 0) {
5656 teasingTimer.start();
5757 }
5858 }
Launcher/Launcher.qml+1−1
@@ -28,7 +28,7 @@ Item {
2828
2929 property int panelWidth: units.gu(8)
3030 property int dragAreaWidth: units.gu(1)
31− property int minimizeDistance: panelWidth * 2.5
31+ property int minimizeDistance: units.gu(26)
3232 property real progress: dragArea.dragging && dragArea.touchX > panelWidth ?
3333 (width * (dragArea.touchX-panelWidth) / (width - panelWidth)) : 0
3434
Launcher/LauncherPanel.qml+87−46
@@ -21,6 +21,7 @@ import Unity 0.1
2121import Unity.Launcher 0.1
2222import Ubuntu.Components.Popups 0.1
2323import "../Components/ListItems"
24+import "../Components/"
2425
2526Item {
2627 id: root
@@ -31,7 +32,7 @@ Item {
3132 property bool inverted: true
3233 property bool dragging: false
3334 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
3536 property int highlightIndex: -1
3637
3738 signal applicationSelected(string appId)
@@ -278,7 +279,6 @@ Item {
278279 property bool postDragging: false
279280 property int startX
280281 property int startY
281− property var quickListPopover: null
282282
283283 onPressed: {
284284 selectedItem = launcherListView.itemAt(mouseX, mouseY + launcherListView.realContentY)
@@ -357,11 +357,10 @@ Item {
357357 draggedIndex = Math.floor((mouseY + launcherListView.realContentY) / launcherListView.realItemHeight);
358358
359359 // Opening QuickList
360− var quickListModel = launcherListView.model.get(draggedIndex).quickList
361− var quickListAppId = launcherListView.model.get(draggedIndex).appId
362−
363− quickListPopover = PopupUtils.open(popoverComponent, selectedItem,
364− {model: quickListModel, appId: quickListAppId})
360+ quickList.item = selectedItem;
361+ quickList.model = launcherListView.model.get(draggedIndex).quickList;
362+ quickList.appId = launcherListView.model.get(draggedIndex).appId;
363+ quickList.state = "open";
365364
366365 launcherListView.interactive = false
367366
@@ -387,7 +386,7 @@ Item {
387386 var distance = Math.max(Math.abs(mouseX - startX), Math.abs(mouseY - startY))
388387 if (!preDragging && distance > units.gu(1.5)) {
389388 preDragging = true;
390− PopupUtils.close(quickListPopover)
389+ quickList.state = "";
391390 }
392391 if (distance > launcherListView.itemHeight) {
393392 selectedItem.dragging = true
@@ -482,53 +481,95 @@ Item {
482481 }
483482 }
484483
485− Component {
486− id: popoverComponent
484+ UbuntuShapeForItem {
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)
487531
488− Popover {
489− id: popover
490− property var model
491− property string appId
492− contentWidth: quickListColumn.width
493− foregroundStyle: QuicklistForegroundStyle {}
532+ }
533+ y: itemCenter - (height / 2) + offset
534+ rotation: root.rotation
494535
495− // FIXME: There's a bug in the Popover positioning that it covers the item in case it is rotated.
496− // https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1204470
497− // For now, let's move the Popover around with callerMargin.
498− // Remove popupMargin once the bug is fixed.
499− property int popupMargin: root.inverted ? launcherListView.itemHeight : 0;
536+ property var model
537+ property string appId
538+ property var item
500539
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
502544
503− Column {
504− id: quickListColumn
505− width: units.gu(30)
506− height: childrenRect.height
545+ Column {
546+ id: quickListColumn
547+ width: parent.width
548+ height: childrenRect.height
507549
508− Repeater {
509− id: popoverRepeater
510− model: popover.model
550+ Repeater {
551+ id: popoverRepeater
552+ model: quickList.model
511553
512− ListItems.Standard {
513− objectName: "quickListEntry" + index
514− text: (model.clickable ? "" : "<b>") + model.label + (model.clickable ? "" : "</b>")
515− highlightWhenPressed: model.clickable
554+ ListItems.Standard {
555+ objectName: "quickListEntry" + index
556+ text: (model.clickable ? "" : "<b>") + model.label + (model.clickable ? "" : "</b>")
557+ highlightWhenPressed: model.clickable
516558
517− // FIXME: This is a workaround for the theme not being context sensitive. I.e. the
518− // ListItems don't know that they are sitting in a themed Popover where the color
519− // needs to be inverted.
520− __foregroundColor: Theme.palette.selected.backgroundText
559+ // FIXME: This is a workaround for the theme not being context sensitive. I.e. the
560+ // ListItems don't know that they are sitting in a themed Popover where the color
561+ // needs to be inverted.
562+ __foregroundColor: Theme.palette.selected.backgroundText
521563
522− onClicked: {
523− if (!model.clickable) {
524− 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;
564+ onClicked: {
565+ if (!model.clickable) {
566+ return;
531567 }
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;
532573 }
533574 }
534575 }
Notifications/Notification.qml+2−1
@@ -34,12 +34,13 @@ UbuntuShape {
3434 property var notification
3535
3636 objectName: "background"
37− implicitHeight: contentColumn.height + contentColumn.spacing * 2
37+ implicitHeight: type != Notification.PlaceHolder ? contentColumn.height + contentColumn.spacing * 2 : 0
3838 color: Qt.rgba(0.132, 0.117, 0.109, 0.97)
3939 opacity: 0
4040 radius: "medium"
4141
4242 clip: true
43+ visible: type != Notification.PlaceHolder
4344
4445 UnityMenuModelPaths {
4546 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+
1128unity8 (7.83+13.10.20131015.4-0ubuntu1) saucy; urgency=low
2129
3130 [ Michał Sawicz ]
debian/control+1−1
@@ -91,7 +91,7 @@ Description: Unity 8 shell
9191Package: unity8-autopilot
9292Architecture: all
9393Depends: gir1.2-glib-2.0,
94− libautopilot-qt,
94+ libautopilot-qt (>= 1.4),
9595 libqt5test5,
9696 libqt5widgets5,
9797 python-evdev,
debian/unity8-fake-env.install+1
@@ -1 +1,2 @@
11usr/lib/*/unity8/qml/mocks
2+usr/share/unity8/mocks
debian/unity8.install−1
@@ -14,4 +14,3 @@ usr/share/unity8/Shell.qml
1414usr/share/unity8/SideStage
1515usr/share/unity8/graphics
1616data/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() {
7878
7979int startShell(int argc, const char** argv, void* server)
8080{
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−
8981 const bool isUbuntuMirServer = qgetenv("QT_QPA_PLATFORM") == "ubuntumirserver";
9082
9183 QGuiApplication::setApplicationName("Unity 8");
@@ -186,6 +178,14 @@ int startShell(int argc, const char** argv, void* server)
186178
187179int main(int argc, const char *argv[])
188180{
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+
189189 // For ubuntumirserver/ubuntumirclient
190190 if (qgetenv("QT_QPA_PLATFORM").startsWith("ubuntumir")) {
191191 setenv("QT_QPA_PLATFORM", "ubuntumirserver", 1);
plugins/Unity/Indicators/CMakeLists.txt+1
@@ -36,6 +36,7 @@ set(IndicatorsQML_SOURCES
3636 unitymenumodelcache.cpp
3737 unitymenumodelstack.cpp
3838 visibleindicatorsmodel.cpp
39+ timeformatter.cpp
3940)
4041add_definitions(-DUNITYINDICATORS_LIBRARY)
4142
plugins/Unity/Indicators/Messaging/qml/MessageMenuItemFactory.qml+3−4
@@ -22,7 +22,6 @@ import QtQuick 2.0
2222import Ubuntu.Components 0.1
2323import Unity.Indicators 0.1 as Indicators
2424import QMenuModel 0.1 as QMenuModel
25−import "utils.js" as Utils
2625
2726Indicators.BaseMenuItem {
2827 id: menuItem
@@ -65,7 +64,7 @@ Indicators.BaseMenuItem {
6564 SimpleTextMessage {
6665 // text
6766 title: menu && menu.label ? menu.label : ""
68− time: menu ? Utils.formatDate(menu.ext.xCanonicalTime) : ""
67+ time: menu ? menu.ext.xCanonicalTime : 0
6968 message: menu && menu.ext.xCanonicalText ? menu.ext.xCanonicalText : ""
7069 // icons
7170 avatar: menu && menu.ext.icon !== undefined ? menu.ext.icon : "qrc:/indicators/artwork/messaging/default_contact.png"
@@ -90,7 +89,7 @@ Indicators.BaseMenuItem {
9089
9190 // text
9291 title: menu && menu.label ? menu.label : ""
93− time: menu ? Utils.formatDate(menu.ext.xCanonicalTime) : ""
92+ time: menu ? menu.ext.xCanonicalTime : 0
9493 message: menu && menu.ext.xCanonicalText ? menu.ext.xCanonicalText : ""
9594 replyButtonText: actionsDescription && actionsDescription[0].label ? actionsDescription[0].label : "Send"
9695 // icons
@@ -126,7 +125,7 @@ Indicators.BaseMenuItem {
126125
127126 // text
128127 title: menu && menu.label ? menu.label : ""
129− time: menu ? Utils.formatDate(menu.ext.xCanonicalTime) : ""
128+ time: menu ? menu.ext.xCanonicalTime : ""
130129 message: menu && menu.ext.xCanonicalText ? menu.ext.xCanonicalText : ""
131130 actionButtonText: actionsDescription && actionsDescription[0].label ? actionsDescription[0].label : "Call back"
132131 replyButtonText: actionsDescription && actionsDescription[1].label ? actionsDescription[1].label : "Send"
plugins/Unity/Indicators/Messaging/qml/SimpleTextMessage.qml+9−2
@@ -20,19 +20,20 @@
2020
2121import QtQuick 2.0
2222import Ubuntu.Components 0.1
23+import Unity.Indicators 0.1
2324
2425HeroMessage {
2526 id: __heroMessage
2627
2728 property string title: ""
28− property string time: ""
29+ property var time
2930 property string message: ""
3031
3132 property alias footer: footerLoader.sourceComponent
3233
3334 expandedHeight: fullMessage.y + fullMessage.height + units.gu(2)
3435 heroMessageHeader.titleText.text: title
35− heroMessageHeader.subtitleText.text: time
36+ heroMessageHeader.subtitleText.text: timeFormatter.timeString
3637 heroMessageHeader.bodyText.text: message
3738
3839 Item {
@@ -49,6 +50,12 @@ HeroMessage {
4950 opacity: 0.0
5051 enabled: false
5152
53+ TimeFormatter {
54+ id: timeFormatter
55+ time: __heroMessage.time
56+ format: "hh:mm - MMM dd"
57+ }
58+
5259 Label {
5360 id: bodyText
5461 anchors {
plugins/Unity/Indicators/Messaging/qml/SnapDecision.qml+9−3
@@ -21,13 +21,13 @@
2121import QtQuick 2.0
2222import Ubuntu.Components 0.1
2323import Unity.Indicators 0.1 as Indicators
24−import "utils.js" as Utils
24+import Unity.Indicators 0.1
2525
2626HeroMessage {
2727 id: snapDecision
2828
2929 property string title: ""
30− property string time: ""
30+ property var time
3131 property string message: ""
3232
3333 property bool activateEnabled: true
@@ -41,7 +41,7 @@ HeroMessage {
4141 heroMessageHeader.titleText.text: title
4242 heroMessageHeader.subtitleText.text: message
4343 heroMessageHeader.subtitleText.color: "#e8e1d0"
44− heroMessageHeader.bodyText.text: time
44+ heroMessageHeader.bodyText.text: timeFormatter.timeString
4545 heroMessageHeader.bodyText.color: "#8f8f88"
4646
4747 signal activate
@@ -59,6 +59,12 @@ HeroMessage {
5959 height: units.gu(4)
6060 opacity: 0.0
6161
62+ TimeFormatter {
63+ id: timeFormatter
64+ time: snapDecision.time
65+ format: "hh:mm - MMM dd"
66+ }
67+
6268 Button {
6369 text: "Message"
6470 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 @@
3333#include "unitymenumodelcache.h"
3434#include "unitymenumodelstack.h"
3535#include "visibleindicatorsmodel.h"
36+#include "timeformatter.h"
3637
3738static QObject* menuModelCacheSingleton(QQmlEngine* engine, QJSEngine* scriptEngine) {
3839 Q_UNUSED(engine);
@@ -51,6 +52,7 @@ void Indicators2Plugin::registerTypes(const char *uri)
5152 qmlRegisterType<RootActionState>(uri, 0, 1, "RootActionState");
5253 qmlRegisterType<ModelPrinter>(uri, 0, 1, "ModelPrinter");
5354 qmlRegisterType<VisibleIndicatorsModel>(uri, 0, 1, "VisibleIndicatorsModel");
55+ qmlRegisterType<TimeFormatter>(uri, 0, 1, "TimeFormatter");
5456
5557 qmlRegisterSingletonType<UnityMenuModelCache>(uri, 0, 1, "UnityMenuModelCache", menuModelCacheSingleton);
5658
plugins/Unity/Indicators/qml/IndicatorPage.qml+27−6
@@ -34,11 +34,36 @@ IndicatorBase {
3434 Indicators.UnityMenuModelStack {
3535 id: menuStack
3636 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+ }
3762 }
3863
3964 ListView {
4065 id: mainMenu
41− model: menuStack.tail ? menuStack.tail : null
66+ model: menuStack.rootMenu
4267
4368 anchors {
4469 fill: parent
@@ -102,7 +127,7 @@ IndicatorBase {
102127
103128 Loader {
104129 id: loader
105− asynchronous: true
130+ asynchronous: false
106131
107132 property int modelIndex: index
108133
@@ -114,10 +139,6 @@ IndicatorBase {
114139 sourceComponent: factory.load(model)
115140
116141 onLoaded: {
117− if (model.type === rootMenuType) {
118− menuStack.push(mainMenu.model.submenu(index), index);
119− }
120−
121142 if (item.hasOwnProperty("menuSelected")) {
122143 item.menuSelected = Qt.binding(function() { return mainMenu.selectedIndex == index; });
123144 item.selectMenu.connect(function() { mainMenu.selectedIndex = index; });
plugins/Unity/Indicators/qml/SliderMenuItem.qml+3−2
@@ -51,12 +51,12 @@ FramedMenuItem {
5151
5252 control: Item {
5353 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)
5555 height: slider.height
5656
5757 Image {
5858 id: leftImage
59− visible: source != ""
59+ visible: status === Image.Ready
6060 anchors.left: row.left
6161 anchors.verticalCenter: row.verticalCenter
6262 height: units.gu(4)
@@ -96,6 +96,7 @@ FramedMenuItem {
9696
9797 Image {
9898 id: rightImage
99+ visible: status === Image.Ready
99100 anchors.right: row.right
100101 anchors.verticalCenter: row.verticalCenter
101102 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()
228228
229229void Scope::onActivated(unity::dash::LocalResult const& result, unity::dash::ScopeHandledType type, unity::glib::HintsMap const& hints)
230230{
231+ Q_EMIT activated();
231232 // note: we will not get called on SHOW_PREVIEW, instead UnityCore will signal preview_ready.
232233 switch (type)
233234 {
@@ -261,6 +262,7 @@ void Scope::onActivated(unity::dash::LocalResult const& result, unity::dash::Sco
261262
262263void Scope::onPreviewReady(unity::dash::LocalResult const& /* result */, unity::dash::Preview::Ptr const& preview)
263264{
265+ Q_EMIT activated();
264266 auto prv = Preview::newFromUnityPreview(preview);
265267 // is this the best solution? QML may need to keep more than one preview instance around, so we can't own it.
266268 // passing it by value is not possible.
plugins/Unity/scope.h+1
@@ -117,6 +117,7 @@ Q_SIGNALS:
117117 void showDash();
118118 void hideDash();
119119 void gotoUri(const QString &uri);
120+ void activated();
120121
121122 void activateApplication(const QString &desktop);
122123
po/ar.po+33−44
@@ -8,7 +8,7 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"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"
1212"Last-Translator: Ibrahim Saed <ibraheem5000@gmail.com>\n"
1313"Language-Team: Arabic <ar@li.org>\n"
1414"MIME-Version: 1.0\n"
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n % 100 >= "
1818"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"
20−"X-Generator: Launchpad (build 16799)\n"
19+"X-Launchpad-Export-Date: 2013-10-21 05:04+0000\n"
20+"X-Generator: Launchpad (build 16807)\n"
2121
2222#: Applications/applications.js:23
2323msgid "Phone"
@@ -65,7 +65,7 @@ msgstr "مشغّل الوسائط"
6565
6666#: Applications/applications.js:86
6767msgid "Evernote"
68−msgstr ""
68+msgstr "Evernote"
6969
7070#: Applications/applications.js:91
7171msgid "Map"
@@ -73,11 +73,11 @@ msgstr "الخريطة"
7373
7474#: Applications/applications.js:96
7575msgid "Pinterest"
76−msgstr ""
76+msgstr "Pinterest"
7777
7878#: Applications/applications.js:101
7979msgid "SoundCloud"
80−msgstr ""
80+msgstr "SoundCloud"
8181
8282#: Applications/applications.js:106
8383msgid "Wikipedia"
@@ -89,49 +89,52 @@ msgstr "يوتيوب"
8989
9090#: Components/EdgeDemoOverlay.qml:149
9191msgid "Skip intro"
92−msgstr ""
92+msgstr "تخطي المقدمة"
9393
9494#: Components/EdgeDemo.qml:112
9595msgid "Right edge"
96−msgstr ""
96+msgstr "الحافة اليمنى"
9797
9898#: Components/EdgeDemo.qml:113
9999msgid "Try swiping from the right edge to unlock the phone"
100−msgstr ""
100+msgstr "اسحب اصبعك من الحافة اليمنى لفتح قفل الهاتف"
101101
102102#: Components/EdgeDemo.qml:144
103103msgid "Top edge"
104−msgstr ""
104+msgstr "الحافة العلوية"
105105
106106#: Components/EdgeDemo.qml:145
107107msgid "Try swiping from the top edge to access the indicators"
108−msgstr ""
108+msgstr "اسحب اصبعك من الحافة العلوية للوصول إلى المؤشرات"
109109
110110#: Components/EdgeDemo.qml:170
111111msgid "Close"
112−msgstr ""
112+msgstr "أغلق"
113113
114114#: Components/EdgeDemo.qml:171
115115msgid "Swipe up again to close the settings screen"
116−msgstr ""
116+msgstr "اسحب اصعبك لأعلى مرة أخرى لغلق شاشة الإعدادات"
117117
118118#: Components/EdgeDemo.qml:198
119119msgid "Left edge"
120−msgstr ""
120+msgstr "الحافة اليسرى"
121121
122122#: Components/EdgeDemo.qml:199
123123msgid "Swipe from the left to reveal the launcher for quick access to apps"
124124msgstr ""
125+"اسحب أصبعك من الحافة اليسرى لإظهار شريط يونتي للوصول السريع للتطبيقات"
125126
126127#: Components/EdgeDemo.qml:226
127128msgid "Well done"
128−msgstr ""
129+msgstr "أحسنت!"
129130
130131#: Components/EdgeDemo.qml:227
131132msgid ""
132133"You have now mastered the edge gestures and can start using the "
133134"phone<br><br>Tap anywhere to start"
134135msgstr ""
136+"لقد أتقنت الآن إيماءات الحواف ويمكنك البدء باستخدام الهاتف<br><br>انقر في أي "
137+"مكان للبدء"
135138
136139#: Components/PageHeader.qml:341
137140msgid "Recent searches"
@@ -142,7 +145,7 @@ msgstr "عمليات البحث الأخيرة"
142145#: Dash/Apps/AppInfo.qml:73 Dash/Previews/Header.qml:93
143146#, qt-format
144147msgid "(%1)"
145−msgstr ""
148+msgstr "(%1)"
146149
147150#. TRANSLATORS: Number of persons who wrote reviews for this app
148151#. TRANSLATORS: Number of persons who wrote reviews for this app/video/whatever
@@ -150,32 +153,36 @@ msgstr ""
150153#, qt-format
151154msgid "%1 review"
152155msgid_plural "%1 reviews"
153−msgstr[0] ""
154−msgstr[1] ""
156+msgstr[0] "لا تعليقات (%1)"
157+msgstr[1] "تعليق واحد (%1)"
158+msgstr[2] "تعليقان (%1)"
159+msgstr[3] "%1 تعليقات"
160+msgstr[4] "%1 تعليقا"
161+msgstr[5] "%1 تعليق"
155162
156163#: Dash/Apps/AppPreview.qml:126
157164msgid "Rate this"
158−msgstr ""
165+msgstr "قيّم ذلك"
159166
160167#: Dash/Previews/Reviews.qml:74
161168msgid "Add a review"
162−msgstr ""
169+msgstr "أضف تعليق"
163170
164171#: Dash/Previews/Reviews.qml:86
165172msgid "Review"
166−msgstr ""
173+msgstr "التعليق"
167174
168175#: Dash/Previews/Reviews.qml:121
169176msgid "Send"
170−msgstr ""
177+msgstr "أرسل"
171178
172179#: Dash/Previews/Reviews.qml:139
173180msgid "Comments:"
174−msgstr ""
181+msgstr "التعليقات:"
175182
176183#: Greeter/Lockscreen.qml:142
177184msgid "Emergency Call"
178−msgstr ""
185+msgstr "مكالمة طوارئ"
179186
180187#: Greeter/PassphraseLockscreen.qml:59
181188#, qt-format
@@ -216,27 +223,9 @@ msgstr "عذراً، لا يوجد ما يطابق بحثك."
216223
217224#: plugins/Unity/showallfilteroption.cpp:41
218225msgid "All"
219−msgstr ""
226+msgstr "الكل"
220227
221228#: Shell.qml:438
222229#, qt-format
223230msgid "Please enter %1"
224−msgstr ""
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 "فيديوهات"
231+msgstr "من فضلك أدخِل %1"
po/de.po+11−28
@@ -8,15 +8,15 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2013-10-11 12:12+0200\n"
11−"PO-Revision-Date: 2013-10-06 10:57+0000\n"
12−"Last-Translator: SaGrLand <sg@samuel-greef.de>\n"
11+"PO-Revision-Date: 2013-10-18 12:26+0000\n"
12+"Last-Translator: Phillip Sz <Unknown>\n"
1313"Language-Team: German <de@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
19−"X-Generator: Launchpad (build 16799)\n"
18+"X-Launchpad-Export-Date: 2013-10-19 05:30+0000\n"
19+"X-Generator: Launchpad (build 16807)\n"
2020
2121#: Applications/applications.js:23
2222msgid "Phone"
@@ -96,7 +96,7 @@ msgstr "Rechter Rand"
9696
9797#: Components/EdgeDemo.qml:113
9898msgid "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"
100100
101101#: Components/EdgeDemo.qml:144
102102msgid "Top edge"
@@ -104,7 +104,7 @@ msgstr "Oberer Rand"
104104
105105#: Components/EdgeDemo.qml:145
106106msgid "Try swiping from the top edge to access the indicators"
107−msgstr ""
107+msgstr "Vom oberen Rand her wischen, um die Statusmeldungen anzuzeigen"
108108
109109#: Components/EdgeDemo.qml:170
110110msgid "Close"
@@ -112,7 +112,7 @@ msgstr "Schließen"
112112
113113#: Components/EdgeDemo.qml:171
114114msgid "Swipe up again to close the settings screen"
115−msgstr ""
115+msgstr "Nochmals nach oben wischen, um das Einstellungsmenü zu schließen"
116116
117117#: Components/EdgeDemo.qml:198
118118msgid "Left edge"
@@ -121,6 +121,8 @@ msgstr "Linker Rand"
121121#: Components/EdgeDemo.qml:199
122122msgid "Swipe from the left to reveal the launcher for quick access to apps"
123123msgstr ""
124+"Von links wischen, um die Seitenleiste für den Anwendungs-Schnellzugriff "
125+"aufzurufen"
124126
125127#: Components/EdgeDemo.qml:226
126128msgid "Well done"
@@ -131,6 +133,8 @@ msgid ""
131133"You have now mastered the edge gestures and can start using the "
132134"phone<br><br>Tap anywhere to start"
133135msgstr ""
136+"Sie haben nun die Einführung abgeschlossen und können Ihr Gerät nun "
137+"verwenden<br><br>Antippen um zu starten"
134138
135139#: Components/PageHeader.qml:341
136140msgid "Recent searches"
@@ -221,24 +225,3 @@ msgstr "Alle"
221225#, qt-format
222226msgid "Please enter %1"
223227msgstr "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 ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"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"
1212"Last-Translator: Yuri Chornoivan <yurchor@gmail.com>\n"
1313"Language-Team: Ukrainian <uk@li.org>\n"
1414"MIME-Version: 1.0\n"
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
1818"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2013-10-13 05:43+0000\n"
20−"X-Generator: Launchpad (build 16799)\n"
19+"X-Launchpad-Export-Date: 2013-10-20 05:20+0000\n"
20+"X-Generator: Launchpad (build 16807)\n"
2121
2222#: Applications/applications.js:23
2323msgid "Phone"
@@ -135,6 +135,8 @@ msgid ""
135135"You have now mastered the edge gestures and can start using the "
136136"phone<br><br>Tap anywhere to start"
137137msgstr ""
138+"Тепер жести налаштовано, ви можете почати користуватися "
139+"телефоном.<br><br>Натисніть у довільному місці екрана."
138140
139141#: Components/PageHeader.qml:341
140142msgid "Recent searches"
@@ -220,30 +222,9 @@ msgstr "Вибачте, для вказаних вами ключових слі
220222
221223#: plugins/Unity/showallfilteroption.cpp:41
222224msgid "All"
223−msgstr ""
225+msgstr "Усе"
224226
225227#: Shell.qml:438
226228#, qt-format
227229msgid "Please enter %1"
228230msgstr "Будь ласка, введіть %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
22CODE_DIR=shell
33USER=phablet
44USER_ID=32011
@@ -17,6 +17,8 @@ SUDO="echo $PASSWORD | sudo -S"
1717NUM_JOBS='$(( `grep -c ^processor /proc/cpuinfo` + 1 ))'
1818FLIPPED=false
1919CHROOT_PREFIX="/data/ubuntu"
20+SSH_WAS_STARTED=0
21+SSH_STARTED=0
2022
2123usage() {
2224 echo "usage: run_on_device [OPTIONS]\n"
@@ -32,7 +34,15 @@ usage() {
3234 exit 1
3335}
3436
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+
3544exec_with_ssh() {
45+ start_ssh
3646 ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -t $USER@$TARGET_IP -p $TARGET_SSH_PORT sudo -u $USER -i bash -ic \"$@\"
3747}
3848
@@ -52,7 +62,7 @@ adb_root() {
5262install_ssh_key() {
5363 ssh-keygen -R $TARGET_IP
5464 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
5666 adb shell chown $USER_ID:$USER_ID $HOME_DIR/.ssh
5767 adb shell chown $USER_ID:$USER_ID $HOME_DIR/.ssh/authorized_keys
5868 adb shell chmod 700 $HOME_DIR/.ssh
@@ -65,8 +75,6 @@ setup_adb_forwarding() {
6575}
6676
6777install_dependencies() {
68− exec_with_adb rm /etc/init/ssh.override
69− exec_with_adb service ssh start
7078 exec_with_adb apt-get update
7179 exec_with_ssh $SUDO apt-get -y install build-essential rsync bzr ccache gdb ninja-build devscripts equivs
7280}
@@ -74,6 +82,7 @@ install_dependencies() {
7482sync_code() {
7583 [ -e .bzr ] && bzr export --uncommitted --format=dir /tmp/$CODE_DIR
7684 [ -e .git ] && git checkout-index -a -f --prefix=/tmp/$CODE_DIR/
85+ start_ssh
7786 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/
7887 rm -rf /tmp/$CODE_DIR
7988}
@@ -96,7 +105,9 @@ run() {
96105 fi
97106
98107 exec_with_ssh "stop unity8"
108+ exec_with_ssh "start maliit-server"
99109 exec_with_ssh "cd $CODE_DIR/ && ./run $ARGS -- $RUN_OPTIONS"
110+ exec_with_ssh "stop maliit-server"
100111 exec_with_ssh "start unity8"
101112}
102113
@@ -130,6 +141,12 @@ if [ "`adb shell 'grep -q Ubuntu /etc/lsb-release 2> /dev/null && echo -n FLIPPE
130141 fi
131142fi
132143
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+
133150if $SETUP; then
134151 echo "Setting up environment for building shell.."
135152 install_ssh_key
@@ -144,3 +161,6 @@ else
144161 run
145162fi
146163
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():
5252 if running_installed_tests():
5353 mock_path = "qml/mocks/"
5454 else:
55− mock_path = "../lib/x86_64-linux-gnu/unity8/qml/mocks/"
55+ mock_path = os.path.join(
56+ "../lib/",
57+ sysconfig.get_config_var('MULTIARCH'),
58+ "unity8/qml/mocks/"
59+ )
5660 lib_path = get_lib_path()
5761 ld_library_path = os.path.abspath(
5862 os.path.join(
@@ -84,6 +88,47 @@ def get_binary_path(binary="unity8"):
8488 return binary_path
8589
8690
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+
87132def get_grid_size():
88133 grid_size = os.getenv('GRID_UNIT_PX')
89134 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 @@
1818#
1919
2020from unity8.shell.emulators import UnityEmulatorBase
21−from time import sleep
21+
2222
2323class Dash(UnityEmulatorBase):
2424
2525 """An emulator that understands the Dash."""
2626
2727 def get_home_applications_grid(self):
28− for i in range(1,10):
29− get_grid = self.get_scope('home').select_single(
30− "GenericFilterGrid",
31− objectName="applications.scope"
32− )
33− if get_grid != None:
34− break
35− sleep(1)
28+ get_grid = self.get_scope('home').wait_select_single(
29+ "GenericFilterGrid",
30+ objectName="applications.scope"
31+ )
3632 return get_grid
3733
3834 def get_application_icon(self, text):
3935 """Returns a 'Tile' icon that has the text 'text' from the application
4036 grid.
4137
42− Will return None if the icon isn't found.
43−
4438 :param text: String containing the text of the icon to search for.
4539
4640 """
4741 app_grid = self.get_home_applications_grid()
48− for i in range(1,10):
49− resp_grid = app_grid.select_single('ResponsiveGridView')
50− if resp_grid != None:
51− break
52− sleep(1)
42+ resp_grid = app_grid.wait_select_single('ResponsiveGridView')
5343 return resp_grid.select_single('Tile', text=text)
5444
5545 def get_scope(self, scope_name='home'):
56− dash_content = self.select_single(
46+ dash_content = self.wait_select_single(
5747 'QQuickListView',
5848 objectName='dashContentList'
5949 )
tests/autopilot/unity8/shell/emulators/hud.py+12−4
@@ -26,6 +26,7 @@ from autopilot.input import Touch
2626
2727SwipeCoords = namedtuple('SwipeCoords', 'start_x end_x start_y end_y')
2828
29+
2930class Hud(UnityEmulatorBase, DragMixin):
3031
3132 """An emulator that understands the Hud."""
@@ -34,13 +35,18 @@ class Hud(UnityEmulatorBase, DragMixin):
3435 """Swipes open the Hud."""
3536 self.touch = Touch.create()
3637
37− window = self.get_root_instance().select_single('QQuickView')
38− hud_show_button = window.select_single("HudButton")
38+ window = self.get_root_instance().wait_select_single('QQuickView')
39+ hud_show_button = window.wait_select_single("HudButton")
3940
4041 swipe_coords = self.get_button_swipe_coords(window, hud_show_button)
4142
4243 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+ )
4450 try:
4551 hud_show_button.opacity.wait_for(1.0)
4652 self.touch.release()
@@ -74,6 +80,8 @@ class Hud(UnityEmulatorBase, DragMixin):
7480 start_x = int(main_view.x + (main_view.width / 2))
7581 end_x = start_x
7682 start_y = main_view.y + (main_view.height - 3)
77− end_y = main_view.y + int(hud_show_button.y + (hud_show_button.height/2))
83+ end_y = main_view.y + int(
84+ hud_show_button.y + (hud_show_button.height/2)
85+ )
7886
7987 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):
3838 return self.app.select_single(Greeter)
3939
4040 def get_greeter_content_loader(self):
41− return self.app.select_single(
41+ return self.app.wait_select_single(
4242 "QQuickLoader",
4343 objectName="greeterContentLoader"
4444 )
tests/autopilot/unity8/shell/tests/__init__.py+66−27
@@ -34,13 +34,14 @@ import logging
3434import os.path
3535import subprocess
3636import sys
37−from testtools.matchers import Equals, NotEquals
37+from testtools.matchers import Equals
3838
3939from unity8 import (
4040 get_lib_path,
4141 get_binary_path,
4242 get_mocks_library_path,
4343 get_default_extra_mock_libraries,
44+ get_data_dirs
4445)
4546from unity8.shell.emulators import UnityEmulatorBase
4647from unity8.shell.emulators.dash import Dash
@@ -52,7 +53,7 @@ logger = logging.getLogger(__name__)
5253UNITYSHELL_GSETTINGS_SCHEMA = "org.compiz.unityshell"
5354UNITYSHELL_GSETTINGS_PATH = "/org/compiz/profiles/unity/plugins/unityshell/"
5455UNITYSHELL_LAUNCHER_KEY = "launcher-hide-mode"
55−UNITYSHELL_LAUNCHER_MODE = 1 # launcher hidden
56+UNITYSHELL_LAUNCHER_MODE = 1 # launcher hidden
5657
5758
5859def _get_device_emulation_scenarios(devices='All'):
@@ -92,17 +93,25 @@ class UnityTestCase(AutopilotTestCase):
9293 ], stderr=subprocess.STDOUT)
9394 except subprocess.CalledProcessError, e:
9495 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"
9698 "{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(
98101 e.output,
99− os.path.join(os.getenv("XDG_CONFIG_HOME", os.path.join(os.getenv("HOME"), ".config")), "upstart")
100− ))
102+ os.path.join(os.getenv("XDG_CONFIG_HOME",
103+ os.path.join(os.getenv("HOME"),
104+ ".config")
105+ ),
106+ "upstart")
107+ )
108+ )
101109 sys.exit(1)
102110
103111 if "start/" in output:
104112 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"
106115 "Please run this command before running these tests: \n"
107116 "initctl stop unity8\n"
108117 )
@@ -110,11 +119,22 @@ class UnityTestCase(AutopilotTestCase):
110119
111120 def setUp(self):
112121 super(UnityTestCase, self).setUp()
113− if Gio is not None and UNITYSHELL_GSETTINGS_SCHEMA in Gio.Settings.list_relocatable_schemas():
122+ if (Gio is not None and
123+ UNITYSHELL_GSETTINGS_SCHEMA in
124+ Gio.Settings.list_relocatable_schemas()):
125+
114126 # Hide Unity launcher
115− self._unityshell_schema = Gio.Settings.new_with_path(UNITYSHELL_GSETTINGS_SCHEMA, UNITYSHELL_GSETTINGS_PATH)
116− self._launcher_hide_mode = self._unityshell_schema.get_int(UNITYSHELL_LAUNCHER_KEY)
117− self._unityshell_schema.set_int(UNITYSHELL_LAUNCHER_KEY, UNITYSHELL_LAUNCHER_MODE)
127+ self._unityshell_schema = Gio.Settings.new_with_path(
128+ UNITYSHELL_GSETTINGS_SCHEMA,
129+ UNITYSHELL_GSETTINGS_PATH,
130+ )
131+ self._launcher_hide_mode = self._unityshell_schema.get_int(
132+ UNITYSHELL_LAUNCHER_KEY,
133+ )
134+ self._unityshell_schema.set_int(
135+ UNITYSHELL_LAUNCHER_KEY,
136+ UNITYSHELL_LAUNCHER_MODE,
137+ )
118138 self.addCleanup(self._reset_launcher)
119139
120140 self._proxy = None
@@ -122,6 +142,8 @@ class UnityTestCase(AutopilotTestCase):
122142 self._qml_mock_enabled = True
123143 self._environment = {}
124144
145+ self._patch_data_dirs()
146+
125147 #### FIXME: This is a work around re: lp:1238417 ####
126148 if model() != "Desktop":
127149 from autopilot.input import _uinput
@@ -133,7 +155,10 @@ class UnityTestCase(AutopilotTestCase):
133155
134156 def _reset_launcher(self):
135157 """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+ )
137162
138163 def _setup_display_details(self):
139164 scale_divisor = self._determine_geometry()
@@ -214,7 +239,10 @@ class UnityTestCase(AutopilotTestCase):
214239 def _upstart_reset_env(self, key, value):
215240 logger.info("Resetting upstart env %s to %s", key, value)
216241 if value is None:
217− subprocess.call(["/sbin/initctl", "unset-env", key], stderr=subprocess.STDOUT)
242+ subprocess.call(
243+ ["/sbin/initctl", "unset-env", key],
244+ stderr=subprocess.STDOUT,
245+ )
218246 else:
219247 subprocess.call([
220248 "/sbin/initctl",
@@ -241,9 +269,13 @@ class UnityTestCase(AutopilotTestCase):
241269 self._setup_extra_mock_environment_patch()
242270
243271 # 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
245274 try:
246− os.unlink(os.getenv('MIR_SOCKET', os.path.join(os.getenv('XDG_RUNTIME_DIR', "/tmp"), "mir_socket")))
275+ os.unlink(
276+ os.getenv('MIR_SOCKET',
277+ os.path.join(os.getenv('XDG_RUNTIME_DIR', "/tmp"),
278+ "mir_socket")))
247279 except OSError:
248280 pass
249281 try:
@@ -253,7 +285,7 @@ class UnityTestCase(AutopilotTestCase):
253285
254286 app_proxy = self._launch_unity_with_upstart(
255287 binary_path,
256− self.unity_geometry_args
288+ self.unity_geometry_args,
257289 )
258290
259291 self._set_proxy(app_proxy)
@@ -272,14 +304,16 @@ class UnityTestCase(AutopilotTestCase):
272304 binary_arg = "BINARY=%s" % binary_path
273305 extra_args = "ARGS=%s" % " ".join(args)
274306
275− output = subprocess.check_output([
276− "/sbin/initctl",
277− "start",
278− "unity8",
279− binary_arg,
280− extra_args
281− ] + ["%s=%s" % (k,v) for k,v in self._environment.iteritems()],
282− stderr=subprocess.STDOUT)
307+ output = subprocess.check_output(
308+ [
309+ "/sbin/initctl",
310+ "start",
311+ "unity8",
312+ binary_arg,
313+ extra_args,
314+ ] + ["%s=%s" % (k, v) for k, v in self._environment.iteritems()],
315+ stderr=subprocess.STDOUT,
316+ )
283317
284318 self.addCleanup(self._cleanup_launching_upstart_unity)
285319
@@ -293,10 +327,16 @@ class UnityTestCase(AutopilotTestCase):
293327 def _cleanup_launching_upstart_unity(self):
294328 logger.info("Stopping unity")
295329 try:
296− subprocess.check_output(["/sbin/initctl", "stop", "unity8"], stderr=subprocess.STDOUT)
330+ subprocess.check_output(
331+ ["/sbin/initctl", "stop", "unity8"], stderr=subprocess.STDOUT)
297332 except subprocess.CalledProcessError:
298333 logger.warning("Appears unity was already stopped!")
299334
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+
300340 def patch_lightdm_mock(self, mock_type='single'):
301341 self._lightdm_mock_type = mock_type
302342 logger.info("Setting up LightDM mock type '%s'", mock_type)
@@ -358,8 +398,7 @@ class UnityTestCase(AutopilotTestCase):
358398 self.assertThat(home_scope.isCurrent, Eventually(Equals(True)))
359399
360400 def get_dash(self):
361− dash = self._proxy.select_single(Dash)
362− self.assertThat(dash, NotEquals(None))
401+ dash = self._proxy.wait_select_single(Dash)
363402 return dash
364403
365404 @property
tests/autopilot/unity8/shell/tests/test_notifications.py+51−43
@@ -45,7 +45,7 @@ class NotificationsBase(UnityTestCase):
4545 """Given an icons file name returns the full path (either system or
4646 source tree.
4747
48− Consider the graphics directory as root so for example (runnign tests
48+ Consider the graphics directory as root so for example (running tests
4949 from installed unity8-autopilot package):
5050 >>> self.get_icon_path('clock.png')
5151 /usr/share/unity8/graphics/clock.png
@@ -145,8 +145,8 @@ class InteractiveNotificationBase(NotificationsBase):
145145 hints,
146146 )
147147
148− get_notification = lambda: notify_list.select_single('Notification')
149− self.assertThat(get_notification, Eventually(NotEquals(None)))
148+ get_notification = lambda: notify_list.wait_select_single(
149+ 'Notification', objectName='notification1')
150150 notification = get_notification()
151151
152152 self.touch.tap_object(
@@ -156,7 +156,8 @@ class InteractiveNotificationBase(NotificationsBase):
156156 self.assert_notification_action_id_was_called('action_id')
157157
158158 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."""
160161 self.launch_unity()
161162 greeter = self.main_window.get_greeter()
162163 greeter.swipe()
@@ -191,8 +192,8 @@ class InteractiveNotificationBase(NotificationsBase):
191192 hints
192193 )
193194
194− get_notification = lambda: notify_list.select_single('Notification')
195− self.assertThat(get_notification, Eventually(NotEquals(None)))
195+ get_notification = lambda: notify_list.wait_select_single(
196+ 'Notification', objectName='notification1')
196197 notification = get_notification()
197198 self._assert_notification(notification, None, None, True, True, 1.0)
198199 initial_height = notification.height
@@ -200,8 +201,10 @@ class InteractiveNotificationBase(NotificationsBase):
200201 self.assertThat(
201202 notification.height,
202203 Eventually(Equals(initial_height +
203− 3 * notification.select_single(objectName="buttonColumn").spacing +
204− 3 * notification.select_single(objectName="button4").height)))
204+ 3 * notification.select_single(
205+ objectName="buttonColumn").spacing +
206+ 3 * notification.select_single(
207+ objectName="button4").height)))
205208 self.touch.tap_object(notification.select_single(objectName="button4"))
206209 self.assert_notification_action_id_was_called("action_decline_4")
207210
@@ -360,13 +363,20 @@ class EphemeralNotificationsTests(NotificationsBase):
360363
361364 notification.show()
362365
363− get_notification = lambda: notify_list.select_single('Notification')
364− self.assertThat(get_notification, Eventually(NotEquals(None)))
365− notification = get_notification()
366− self._assert_notification(notification, summary, body, True, True, 1.0)
366+ notification = lambda: notify_list.wait_select_single(
367+ 'Notification', objectName='notification1')
368+ self._assert_notification(
369+ notification(),
370+ summary,
371+ body,
372+ True,
373+ True,
374+ 1.0,
375+ )
367376
368377 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."""
370380 self.launch_unity()
371381 greeter = self.main_window.get_greeter()
372382 greeter.swipe()
@@ -391,11 +401,11 @@ class EphemeralNotificationsTests(NotificationsBase):
391401
392402 notification.show()
393403
394− get_notification = lambda: notify_list.select_single('Notification')
395− self.assertThat(get_notification, Eventually(NotEquals(None)))
396− notification = get_notification()
404+ notification = lambda: notify_list.wait_select_single(
405+ 'Notification', objectName='notification1')
406+
397407 self._assert_notification(
398− notification,
408+ notification(),
399409 summary,
400410 None,
401411 False,
@@ -404,7 +414,8 @@ class EphemeralNotificationsTests(NotificationsBase):
404414 )
405415
406416 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."""
408419 self.launch_unity()
409420 greeter = self.main_window.get_greeter()
410421 greeter.swipe()
@@ -448,11 +459,10 @@ class EphemeralNotificationsTests(NotificationsBase):
448459 )
449460 notification_critical.show()
450461
451− get_notification = lambda: notify_list.select_single(
462+ get_notification = lambda: notify_list.wait_select_single(
452463 'Notification',
453464 summary=summary_critical
454465 )
455− self.assertThat(get_notification, Eventually(NotEquals(None)))
456466
457467 notification = get_notification()
458468 self._assert_notification(
@@ -464,11 +474,10 @@ class EphemeralNotificationsTests(NotificationsBase):
464474 1.0
465475 )
466476
467− get_normal_notification = lambda: notify_list.select_single(
477+ get_normal_notification = lambda: notify_list.wait_select_single(
468478 'Notification',
469479 summary=summary_normal
470480 )
471− self.assertThat(get_normal_notification, Eventually(NotEquals(None)))
472481 notification = get_normal_notification()
473482 self._assert_notification(
474483 notification,
@@ -479,11 +488,10 @@ class EphemeralNotificationsTests(NotificationsBase):
479488 1.0
480489 )
481490
482− get_low_notification = lambda: notify_list.select_single(
491+ get_low_notification = lambda: notify_list.wait_select_single(
483492 'Notification',
484493 summary=summary_low
485494 )
486− self.assertThat(get_low_notification, Eventually(NotEquals(None)))
487495 notification = get_low_notification()
488496 self._assert_notification(
489497 notification,
@@ -508,9 +516,8 @@ class EphemeralNotificationsTests(NotificationsBase):
508516 notification = self._create_ephemeral_notification(summary, body)
509517 notification.show()
510518
511− get_notification = lambda: notify_list.select_single('Notification')
512− self.assertThat(get_notification, Eventually(NotEquals(None)))
513− notification = get_notification()
519+ notification = notify_list.wait_select_single(
520+ 'Notification', objectName='notification1')
514521 self._assert_notification(
515522 notification,
516523 summary,
@@ -533,14 +540,14 @@ class EphemeralNotificationsTests(NotificationsBase):
533540 notification = self._create_ephemeral_notification(summary)
534541 notification.show()
535542
536− get_notification = lambda: notify_list.select_single('Notification')
537− self.assertThat(get_notification, Eventually(NotEquals(None)))
538− notification = get_notification()
543+ notification = notify_list.wait_select_single(
544+ 'Notification', objectName='notification1')
539545
540546 self._assert_notification(notification, summary, '', False, False, 1.0)
541547
542548 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."""
544551 self.launch_unity()
545552 greeter = self.main_window.get_greeter()
546553 greeter.swipe()
@@ -558,8 +565,8 @@ class EphemeralNotificationsTests(NotificationsBase):
558565 )
559566 notification.show()
560567
561− get_notification = lambda: notify_list.select_single('Notification', summary=summary)
562− self.assertThat(get_notification, Eventually(NotEquals(None)))
568+ get_notification = lambda: notify_list.wait_select_single(
569+ 'Notification', summary=summary)
563570 self._assert_notification(
564571 get_notification(),
565572 summary,
@@ -575,11 +582,11 @@ class EphemeralNotificationsTests(NotificationsBase):
575582 icon_path = self._get_icon_path('avatars/amanda.png')
576583 notification.update(summary, body, icon_path)
577584 notification.show()
578− self.assertThat(get_notification, Eventually(NotEquals(None)))
579585 self._assert_notification(get_notification(), summary, body)
580586
581587 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."""
583590 self.launch_unity()
584591 greeter = self.main_window.get_greeter()
585592 greeter.swipe()
@@ -603,8 +610,8 @@ class EphemeralNotificationsTests(NotificationsBase):
603610 )
604611 notification.show()
605612
606− get_notification = lambda: notify_list.select_single('Notification')
607− self.assertThat(get_notification, Eventually(NotEquals(None)))
613+ get_notification = lambda: notify_list.wait_select_single(
614+ 'Notification', objectName='notification1')
608615 self._assert_notification(
609616 get_notification(),
610617 summary,
@@ -645,8 +652,8 @@ class EphemeralNotificationsTests(NotificationsBase):
645652
646653 notification.show()
647654
648− get_notification = lambda: notify_list.select_single('Notification')
649− self.assertThat(get_notification, Eventually(NotEquals(None)))
655+ get_notification = lambda: notify_list.wait_select_single(
656+ 'Notification', objectName='notification1')
650657 notification = get_notification()
651658 self._assert_notification(
652659 notification,
@@ -662,7 +669,8 @@ class EphemeralNotificationsTests(NotificationsBase):
662669 'Did you watch the air-race in Oshkosh last week?',
663670 'Phil owned the place like no one before him!',
664671 '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.',
666674 'Hopefully the referees will watch the videos of the race.',
667675 'Burt could get fined with US$ 50000 for that rule-violation :)'
668676 ]
@@ -677,10 +685,10 @@ class EphemeralNotificationsTests(NotificationsBase):
677685 hints=[('x-canonical-append', 'true')]
678686 )
679687 notification.show()
680− get_notification = lambda: notify_list.select_single(
681− 'Notification'
688+ get_notification = lambda: notify_list.wait_select_single(
689+ 'Notification',
690+ objectName='notification1'
682691 )
683− self.assertThat(get_notification, Eventually(NotEquals(None)))
684692 notification = get_notification()
685693 self._assert_notification(
686694 notification,
tests/mocks/CMakeLists.txt+5
@@ -7,3 +7,8 @@ add_subdirectory(Powerd)
77add_subdirectory(QMenuModel)
88add_subdirectory(Ubuntu)
99add_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 @@
1616
1717#include "fake_preview.h"
1818
19+Preview::Preview(QObject *parent): QObject(parent)
20+{
21+}
22+
1923QString Preview::rendererName() const
2024{
21− return "foo";
25+ return "generic-preview";
2226}
2327
2428QString Preview::title() const
@@ -61,3 +65,7 @@ void Preview::execute(const QString& actionId, const QHash<QString, QVariant>& h
6165 Q_UNUSED(actionId);
6266 Q_UNUSED(hints);
6367}
68+
69+void Preview::cancelAction()
70+{
71+}
tests/mocks/Unity/fake_preview.h+2
@@ -51,6 +51,8 @@ public:
5151
5252 Q_INVOKABLE void execute(const QString& actionId, const QHash<QString, QVariant>& hints);
5353
54+ Q_INVOKABLE void cancelAction();
55+
5456Q_SIGNALS:
5557 void previewChanged();
5658};
tests/mocks/Unity/fake_scope.cpp+25−4
@@ -14,13 +14,11 @@
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1515 */
1616
17−// Self
1817#include "fake_scope.h"
19−
20−#include <dee.h>
18+#include "fake_preview.h"
2119#include "paths.h"
2220
23−#include "fake_preview.h"
21+#include <dee.h>
2422
2523static DeeModel* create_categories_model(unsigned category_count);
2624static DeeModel* create_results_model(unsigned category_count, unsigned result_count);
@@ -39,6 +37,13 @@ Scope::Scope(QObject* parent)
3937 m_categories->setResultModel(results_model);
4038 m_categories->setModel(create_categories_model(4));
4139 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+ });
4247}
4348
4449Scope::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
5560 m_categories->setResultModel(results_model);
5661 m_categories->setModel(create_categories_model(4));
5762 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+ });
5870}
5971
6072QString Scope::id() const {
@@ -168,6 +180,8 @@ void Scope::activate(const QVariant &uri, const QVariant &icon_hint, const QVari
168180 Q_UNUSED(comment);
169181 Q_UNUSED(dnd_uri);
170182 Q_UNUSED(metadata);
183+
184+ m_timer.start();
171185}
172186
173187void 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
183197 Q_UNUSED(comment);
184198 Q_UNUSED(dnd_uri);
185199 Q_UNUSED(metadata);
200+
201+ m_timer.start();
186202}
187203
188204static const gchar * categories_model_schema[] = {
@@ -248,3 +264,8 @@ DeeModel* create_results_model(unsigned category_count, unsigned result_count) {
248264 }
249265 return results_model;
250266}
267+
268+void Scope::cancelActivation()
269+{
270+ m_timer.stop();
271+}
tests/mocks/Unity/fake_scope.h+7−2
@@ -17,10 +17,11 @@
1717#ifndef FAKE_SCOPE_H
1818#define FAKE_SCOPE_H
1919
20−// Qt
21−#include <QObject>
2220#include "fake_categories.h"
2321
22+#include <QObject>
23+#include <QDebug>
24+
2425class Preview;
2526
2627class Scope : public QObject
@@ -78,6 +79,8 @@ public:
7879 const QVariant &result_type, const QVariant &mimetype, const QVariant &title,
7980 const QVariant &comment, const QVariant &dnd_uri, const QVariant &metadata);
8081
82+ Q_INVOKABLE void cancelActivation();
83+
8184Q_SIGNALS:
8285 void idChanged(const QString&);
8386 void nameChanged(const QString&);
@@ -115,6 +118,8 @@ protected:
115118
116119 Categories* m_categories;
117120 DeeListModel* m_results;
121+
122+ QTimer m_timer;
118123};
119124
120125#endif // FAKE_SCOPE_H
tests/mocks/Unity/fake_unity_plugin.cpp+3
@@ -22,6 +22,7 @@
2222// local
2323#include "fake_scopes.h"
2424#include "fake_categories.h"
25+#include "fake_preview.h"
2526#include "categoryresults.h"
2627
2728// External
@@ -37,8 +38,10 @@ void FakeUnityPlugin::registerTypes(const char *uri)
3738
3839 Q_ASSERT(uri == QLatin1String("Unity"));
3940
41+ qmlRegisterUncreatableType<Preview>(uri, 0, 1, "Preview", "Can't create Preview object in QML.");
4042 qmlRegisterType<Scopes>(uri, 0, 1, "Scopes");
4143 qmlRegisterType<Scope>(uri, 0, 1, "Scope");
4244 qmlRegisterType<Categories>(uri, 0, 1, "Categories");
4345 qmlRegisterUncreatableType<CategoryResults>(uri, 0, 1, "CategoryResults", "Can't create");
46+ qmlRegisterType<DeeListModel>(uri, 0, 1, "DeeListModel");
4447}
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)
4040indicator_test(menucontentactivatortest ADDITIONAL_CPPS ${INDICATORS_DIR}/menucontentactivator.cpp)
4141indicator_test(unitymenumodelstacktest ADDITIONAL_CPPS ${TEST_DIR}/mocks/QMenuModel/unitymenumodel.cpp ${INDICATORS_DIR}/unitymenumodelstack.cpp)
4242indicator_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 {
137137 for(var i = 0; i < buttons.count; i++) {
138138 var button = findChild(appPreview, "button" + i);
139139 mouseClick(button, 1, 1);
140+ appPreview.showProcessingAction = false;
140141 }
141142
142143 var actions = data.actions;
143− for(var i = 0; i < actions.length; i++) {
144− compare(root.calls[i][0], actions[i].id, "Id of action not found.");
144+ for(var j = 0; j < actions.length; j++) {
145+ compare(root.calls[j][0], actions[j].id, "Id of action not found.");
145146 }
146147 }
147148
tests/qmltests/Dash/Apps/tst_RunningApplicationsGrid.qml+1
@@ -226,6 +226,7 @@ Item {
226226 verify(fakeRunningAppsModel.contains("calendar"))
227227
228228 mouseClick(calendarTileCloseButton, calendarTileCloseButton.width/2, calendarTileCloseButton.height/2)
229+ wait(0) // spin event loop to start any pending animation
229230
230231 verify(!fakeRunningAppsModel.contains("calendar"))
231232
tests/qmltests/Dash/Movie/tst_MoviePreview.qml+1
@@ -74,6 +74,7 @@ Item {
7474 for(var i = 0; i < buttons.count; i++) {
7575 var button = findChild(moviePreview, "button" + i);
7676 mouseClick(button, 1, 1);
77+ moviePreview.showProcessingAction = false;
7778 }
7879
7980 var actions = get_actions_data();
tests/qmltests/Dash/tst_GenericPreview.qml+1
@@ -76,6 +76,7 @@ Item {
7676 for(var i = 0; i < buttons.count; i++) {
7777 var button = findChild(genericPreview, "button" + i);
7878 mouseClick(button, 1, 1);
79+ genericPreview.showProcessingAction = false;
7980 }
8081
8182 var actions = get_actions_data();
tests/qmltests/Dash/tst_GenericScopeView.qml+131−10
@@ -23,11 +23,21 @@ import Ubuntu.Components 0.1
2323import Unity.Test 0.1 as UT
2424
2525Item {
26+ id: shell
2627 width: units.gu(120)
2728 height: units.gu(80)
2829
2930 Scopes {
3031 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()
3141 }
3242
3343 GenericScopeView {
@@ -38,34 +48,145 @@ Item {
3848 name: "GenericScopeView"
3949 when: scopes.loaded
4050
41− function init() {
42− genericScopeView.scope = scopes.get(0)
43− }
44−
4551 function test_isCurrent() {
4652 var pageHeader = findChild(genericScopeView, "pageHeader");
47− var previewLoader = findChild(genericScopeView, "previewLoader");
53+ var previewListView = findChild(genericScopeView, "previewListView");
4854 genericScopeView.isCurrent = true
4955 pageHeader.searchQuery = "test"
50− previewLoader.open = true
56+ previewListView.open = true
5157 genericScopeView.isCurrent = false
5258 tryCompare(pageHeader, "searchQuery", "")
5359 tryCompare(genericScopeView, "previewShown", false);
5460 }
5561
5662 function test_showDash() {
57− var previewLoader = findChild(genericScopeView, "previewLoader");
58− previewLoader.open = true;
63+ var previewListView = findChild(genericScopeView, "previewListView");
64+ previewListView.open = true;
5965 scopes.get(0).showDash();
6066 tryCompare(genericScopeView, "previewShown", false);
6167 }
6268
6369 function test_hideDash() {
64− var previewLoader = findChild(genericScopeView, "previewLoader");
65− previewLoader.open = true;
70+ var previewListView = findChild(genericScopeView, "previewListView");
71+ previewListView.open = true;
6672 scopes.get(0).hideDash();
6773 tryCompare(genericScopeView, "previewShown", false);
6874 }
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+ }
69190 }
70191 }
71192}
tests/qmltests/Greeter/tst_Phone.qml+18−5
@@ -83,8 +83,9 @@ Item {
8383
8484 function test_teaseLockedUnlocked_data() {
8585 return [
86− {tag: "unlocked", locked: false},
87− {tag: "locked", locked: true}
86+ {tag: "unlocked", locked: false, moved: false},
87+ {tag: "locked", locked: true, moved: false},
88+ {tag: "moved", locked: false, moved: true}
8889 ];
8990 }
9091
@@ -93,10 +94,16 @@ Item {
9394 tryCompare(greeter, "x", 0);
9495 greeter.locked = data.locked;
9596
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+
96103 mouseClick(greeter, greeter.width - units.gu(5), greeter.height - units.gu(1));
97104 greeter.minX = 0; // This is needed because the transition actually makes x jump once before animating
98105
99− if (!data.locked) {
106+ if (!data.locked && !data.moved) {
100107 // Check if it has been moved over by 2 GUs. Give it a 2 pixel grace area
101108 // because animation duration and teaseTimer are the same duration and
102109 // might cause slight offsets
@@ -104,10 +111,16 @@ Item {
104111 } else {
105112 // waiting 100ms to make sure nothing moves
106113 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.");
108115 }
116+
117+ // Restore position in case we moved it for the test
118+ if (data.moved) {
119+ greeter.x = 0;
120+ }
121+
109122 // Wait until we're back to 0
110− tryCompareFunction(function() { return greeter.x;}, 0);
123+ tryCompareFunction(function() { return greeter.x;}, 0);
111124 }
112125
113126 function test_statsWelcomeScreen() {
tests/qmltests/Launcher/tst_Launcher.qml+47−11
@@ -363,25 +363,60 @@ Item {
363363 var draggedItem = findChild(launcher, "launcherDelegate5")
364364 var item0 = findChild(launcher, "launcherDelegate0")
365365 var fakeDragItem = findChild(launcher, "fakeDragItem")
366− var dndArea = findChild(launcher, "dndArea")
366+ var quickListShape = findChild(launcher, "quickListShape")
367367
368368 // Initial state
369− compare(dndArea.quickListPopover, null)
369+ compare(quickListShape.visible, false)
370370
371371 // Doing longpress
372372 mousePress(draggedItem, draggedItem.width / 2, draggedItem.height / 2)
373373 tryCompare(fakeDragItem, "visible", true) // Wait longpress happening
374− verify(dndArea.quickListPopover != null)
374+ tryCompare(quickListShape, "visible", true)
375375
376376 // Dragging a bit (> 1.5 gu)
377377 mouseMove(draggedItem, -units.gu(2), draggedItem.height / 2)
378378
379379 // QuickList needs to be closed when a drag operation starts
380− tryCompare(dndArea, "quickListPopover", null)
380+ tryCompare(quickListShape, "visible", false)
381381
382382 mouseRelease(draggedItem);
383383 }
384384
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+
385420 function test_quicklist_click_data() {
386421 return [
387422 {tag: "non-clickable", index: 1, clickable: false },
@@ -392,19 +427,20 @@ Item {
392427 function test_quicklist_click(data) {
393428 revealer.dragLauncherIntoView();
394429 var clickedItem = findChild(launcher, "launcherDelegate5")
395− var dndArea = findChild(launcher, "dndArea")
430+ var quickList = findChild(launcher, "quickList")
431+ var quickListShape = findChild(launcher, "quickListShape")
396432
397433 // Initial state
398− compare(dndArea.quickListPopover, null)
434+ tryCompare(quickListShape, "visible", false)
399435
400436 // Doing longpress
401437 mousePress(clickedItem, clickedItem.width / 2, clickedItem.height / 2)
402438 tryCompare(clickedItem, "itemOpacity", 0) // Wait for longpress to happen
403− verify(dndArea.quickListPopover != null)
439+ verify(quickListShape.visible, "QuickList must be visible")
404440
405441 mouseRelease(clickedItem);
406442
407− var quickListEntry = findChild(dndArea.quickListPopover, "quickListEntry" + data.index)
443+ var quickListEntry = findChild(quickList, "quickListEntry" + data.index)
408444
409445 signalSpy.clear();
410446 signalSpy.signalName = "quickListTriggered"
@@ -413,7 +449,7 @@ Item {
413449
414450 if (data.clickable) {
415451 // QuickList needs to be closed when some clickable item is clicked
416− tryCompare(dndArea, "quickListPopover", null)
452+ tryCompare(quickListShape, "visible", false)
417453
418454 compare(signalSpy.count, 1, "Quicklist signal wasn't triggered")
419455 compare(signalSpy.signalArguments[0][0], LauncherModel.get(5).appId)
@@ -422,13 +458,13 @@ Item {
422458 } else {
423459
424460 // 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")
426462
427463 compare(signalSpy.count, 0, "Quicklist signal must NOT be triggered when clicking a non-clickable item")
428464
429465 // Click somewhere in the empty space to dismiss the quicklist
430466 mouseClick(launcher, launcher.width - units.gu(1), units.gu(1));
431− tryCompare(dndArea, "quickListPopover", null)
467+ tryCompare(quickListShape, "visible", false)
432468 }
433469 }
434470 }
tests/qmltests/tst_Shell.qml+37−9
@@ -111,14 +111,14 @@ Item {
111111 waitUntilApplicationWindowIsFullyVisible();
112112
113113 // Minimize the application we just launched
114− swipeFromLeftEdge();
114+ swipeFromLeftEdge(units.gu(27));
115115
116116 waitForUIToSettle();
117117
118118 checkRightEdgeDragWithMinimizedApp();
119119
120120 // Minimize that application once again
121− swipeFromLeftEdge();
121+ swipeFromLeftEdge(units.gu(27));
122122
123123 // Right edge behavior should now be the same as before that app,
124124 // was launched. Manually cleanup beforehand to get to initial
@@ -130,8 +130,10 @@ Item {
130130
131131 function test_leftEdgeDrag_data() {
132132 return [
133− {tag: "without launcher", revealLauncher: false},
134− {tag: "with launcher", revealLauncher: true},
133+ {tag: "without launcher", revealLauncher: false, swipeLength: units.gu(27), appHides: 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}
135137 ];
136138 }
137139
@@ -144,8 +146,11 @@ Item {
144146 dragLauncherIntoView();
145147 }
146148
147− swipeFromLeftEdge();
148− waitUntilApplicationWindowIsFullyHidden();
149+ swipeFromLeftEdge(data.swipeLength);
150+ if (data.appHides)
151+ waitUntilApplicationWindowIsFullyHidden();
152+ else
153+ waitUntilApplicationWindowIsFullyVisible();
149154 }
150155
151156 function test_suspend() {
@@ -293,7 +298,7 @@ Item {
293298 waitUntilApplicationWindowIsFullyVisible();
294299
295300 // Minimize the application we just launched
296− swipeFromLeftEdge();
301+ swipeFromLeftEdge(units.gu(27));
297302
298303 // Wait for the whole UI to settle down
299304 waitUntilApplicationWindowIsFullyHidden();
@@ -376,10 +381,10 @@ Item {
376381 }
377382 }
378383
379− function swipeFromLeftEdge() {
384+ function swipeFromLeftEdge(swipeLength) {
380385 var touchStartX = 2;
381386 var touchStartY = shell.height / 2;
382− touchFlick(shell, touchStartX, touchStartY, shell.width * 0.75, touchStartY);
387+ touchFlick(shell, touchStartX, touchStartY, swipeLength, touchStartY);
383388 }
384389
385390 function swipeLeftFromCenter() {
@@ -483,5 +488,28 @@ Item {
483488 var dash = findChild(shell, "dash");
484489 tryCompare(dash, "shown", data.expectedShown);
485490 }
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+ }
486514 }
487515}