on xenial help sign in
merged trunk
commit1bff4684362c845c6a7caa227ef73ea637d07fcb authorAndrea Cimitan <andrea.cimitan@gmail.com> dateTue 5 Nov 2013, 15:43 (13 years ago) parente3f4c1071e8dd4
52 files changed+1637−375
Components/Carousel.qml+28−10 Components/FilterGrid.qml+3 Components/LazyImage.qml+2−2 Components/PageHeader.qml+4 Components/ResponsiveGridView.qml+10 Components/Tile.qml+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+1−3 Launcher/QuicklistForegroundStyle.qml → Dash/DashPreviewPlaceholder.qml+7−5 Dash/Generic/GenericFilterGrid.qml+4−6 Dash/Generic/WeatherFilterGrid.qml+2−4 Dash/GenericScopeView.qml+220−59 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 debian/changelog+36 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 po/fa.po+223 po/ta.po+229 run_on_device+24−4 tests/autopilot/unity8/process_helpers.py+116 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/plugins/Unity/Indicators/CMakeLists.txt+1 tests/plugins/Unity/Indicators/timeformattertest.cpp+55 tests/qmltests/Dash/tst_GenericScopeView.qml+123−13 tests/qmltests/Greeter/tst_Phone.qml+18−5 tests/qmltests/Launcher/tst_Launcher.qml+47−11 tests/qmltests/tst_Shell.qml+14−9
Components/Carousel.qml+28−10
@@ -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
@@ -75,6 +81,7 @@ Item {
7581 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'],
@@ -136,13 +143,14 @@ Item {
136143 height: listView.tileHeight
137144 }
138145
139− boundsBehavior: Flickable.StopAtBounds
146+ boundsBehavior: Flickable.DragOverBounds
140147 cacheBuffer: carousel.cacheBuffer
141148 flickDeceleration: Math.max(1500 * Math.pow(realWidth / referenceWidth, 1.5), 1500) // 1500 is platform default
142149 maximumFlickVelocity: Math.max(2500 * Math.pow(realWidth / referenceWidth, 1.5), 2500) // 2500 is platform default
143150 orientation: ListView.Horizontal
144151
145152 function itemClicked(index, delegateItem) {
153+ listView.currentIndex = index
146154 var x = CarouselJS.getXFromContinuousIndex(index,
147155 realWidth,
148156 realContentWidth,
@@ -151,11 +159,11 @@ Item {
151159 gapToEndPhase,
152160 carousel.drawBuffer)
153161
154− if (Math.abs(x - contentX) < 1) {
162+ if (Math.abs(x - contentX) < 1 && delegateItem !== undefined) {
155163 /* We're clicking the selected item and
156164 we're in the neighbourhood of radius 1 pixel from it.
157165 Let's emit the clicked signal. */
158− carousel.clicked(index, delegateItem, delegateItem.y)
166+ carousel.clicked(index, listView.model, delegateItem.y)
159167 return
160168 }
161169
@@ -175,11 +183,11 @@ Item {
175183 gapToEndPhase,
176184 carousel.drawBuffer);
177185
178− if (Math.abs(x - contentX) < 1) {
186+ if (Math.abs(x - contentX) < 1 && delegateItem !== undefined) {
179187 /* We're pressAndHold the selected item and
180188 we're in the neighbourhood of radius 1 pixel from it.
181189 Let's emit the pressAndHold signal. */
182− carousel.pressAndHold(index, delegateItem, delegateItem.y);
190+ carousel.pressAndHold(index, listView.model, delegateItem.y);
183191 return;
184192 }
185193
@@ -190,6 +198,12 @@ Item {
190198 newContentXAnimation.start();
191199 }
192200
201+ onHighlightIndexChanged: {
202+ if (highlightIndex != -1) {
203+ itemClicked(highlightIndex)
204+ }
205+ }
206+
193207 onMovementStarted: {
194208 stepAnimation.stop()
195209 newContentXAnimation.stop()
@@ -273,14 +287,18 @@ Item {
273287 itemTranslationScale,
274288 listView.maximumItemTranslation)
275289
290+ readonly property real xTransform: listView.viewTranslation + translationX * listView.scaleFactor
291+ readonly property real center: x - listView.contentX + xTransform - drawBuffer + (width/2)
292+
276293 width: listView.tileWidth
277294 height: listView.tileHeight
278295 scale: itemScale * explicitScaleFactor
279296 sourceComponent: itemComponent
280297 z: cachedTiles - Math.abs(index - listView.selectedIndex)
298+ opacity: highlightIndex == -1 ? 1 : (highlightIndex == index ? 0.6 : 0.2)
281299
282300 transform: Translate {
283− x: listView.viewTranslation + translationX * listView.scaleFactor
301+ x: xTransform
284302 }
285303
286304 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/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+1−3
@@ -17,7 +17,7 @@
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
@@ -36,7 +36,6 @@ Rectangle {
3636 readonly property int columnWidth: narrowMode ? contentRow.width : (contentRow.width / 3) - contentRow.spacing
3737 readonly property int contentSpacing: units.gu(3)
3838
39− color: Qt.rgba(0, 0, 0, .3)
4039 clip: true
4140
4241 function ensureVisible(item) {
@@ -61,7 +60,6 @@ Rectangle {
6160 anchors.fill: parent
6261 }
6362
64−
6563 Row {
6664 id: contentRow
6765 anchors {
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/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+220−59
@@ -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,9 +57,8 @@ ScopeView {
5757
5858 Connections {
5959 target: scopeView.scope
60− onShowDash: previewLoader.open = false;
61− onHideDash: previewLoader.open = false;
62− onActivated: previewLoader.closePreviewSpinner();
60+ onShowDash: previewListView.open = false;
61+ onHideDash: previewListView.open = false;
6362 }
6463
6564 ScopeListView {
@@ -67,12 +66,18 @@ ScopeView {
6766 objectName: "categoryListView"
6867 anchors.fill: parent
6968 model: scopeView.categories
70− forceNoClip: previewLoader.onScreen
69+ forceNoClip: previewListView.onScreen
7170
7271 onAtYEndChanged: if (atYEnd) endReached()
7372 onMovingChanged: if (moving && atYEnd) endReached()
7473
7574 property string expandedCategoryId: ""
75+ signal correctExpandedCategory();
76+
77+ Behavior on contentY {
78+ enabled: previewListView.open
79+ UbuntuNumberAnimation {}
80+ }
7681
7782 delegate: ListItems.Base {
7883 id: baseItem
@@ -124,33 +129,50 @@ ScopeView {
124129 Connections {
125130 target: rendererLoader.item
126131 onClicked: {
132+ // Prepare the preview in case activate() triggers a preview only
127133 effect.positionPx = mapToItem(categoryView, 0, itemY).y
128− scopeView.scope.activate(delegateItem.model.uri,
129− delegateItem.model.icon,
130− delegateItem.model.category,
131− 0,
132− delegateItem.model.mimetype,
133− delegateItem.model.title,
134− delegateItem.model.comment,
135− delegateItem.model.dndUri,
136− 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+ }
137152 }
138153 onPressAndHold: {
139154 effect.positionPx = mapToItem(categoryView, 0, itemY).y
140− scopeView.scope.preview( delegateItem.model.uri,
141− delegateItem.model.icon,
142− delegateItem.model.category,
143− 0,
144− delegateItem.model.mimetype,
145− delegateItem.model.title,
146− delegateItem.model.comment,
147− delegateItem.model.dndUri,
148− 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)
149165 }
150166 }
151167 Connections {
152168 target: categoryView
153169 onExpandedCategoryIdChanged: {
170+ collapseAllButExpandedCategory();
171+ }
172+ onCorrectExpandedCategory: {
173+ collapseAllButExpandedCategory();
174+ }
175+ function collapseAllButExpandedCategory() {
154176 var item = rendererLoader.item;
155177 if (item.expandable) {
156178 var shouldFilter = categoryId != categoryView.expandedCategoryId;
@@ -158,13 +180,15 @@ ScopeView {
158180 // If the filter animation will be seen start it, otherwise, just flip the switch
159181 var shrinkingVisible = shouldFilter && y + item.collapsedHeight < categoryView.height;
160182 var growingVisible = !shouldFilter && y + height < categoryView.height;
161− if (shrinkingVisible || growingVisible) {
162− item.startFilterAnimation(shouldFilter)
163− } else {
164− item.filter = shouldFilter;
165− }
166− if (!shouldFilter) {
167− 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+ }
168192 }
169193 }
170194 }
@@ -270,6 +294,7 @@ ScopeView {
270294
271295 OpenEffect {
272296 id: effect
297+ objectName: "openEffect"
273298 anchors {
274299 fill: parent
275300 bottomMargin: -bottomOverflow
@@ -282,29 +307,44 @@ ScopeView {
282307 topOpacity: (1 - gap * 1.2)
283308 bottomGapPx: positionPx + gap * (targetBottomGapPx - positionPx)
284309 bottomOverflow: units.gu(20)
285− bottomOpacity: 1 - (gap * 0.8)
310+ live: !expansionAnimation.running
286311
287312 property int targetBottomGapPx: height - units.gu(8) - bottomOverflow
288− property real gap: previewLoader.open ? 1.0 : 0.0
313+ property real gap: previewListView.open ? 1.0 : 0.0
289314
290315 Behavior on gap {
291316 NumberAnimation {
317+ id: expansionAnimation
292318 duration: 200
293319 easing.type: Easing.InOutQuad
294320 onRunningChanged: {
295− if (!previewLoader.open && !running) {
296− previewLoader.onScreen = false
321+ if (!previewListView.open && !running) {
322+ previewListView.onScreen = false
297323 }
298324 }
299325 }
300326 }
327+ Behavior on positionPx {
328+ enabled: previewListView.open
329+ UbuntuNumberAnimation {}
330+ }
301331 }
302332
303333 Connections {
304334 target: scopeView.scope
305335 onPreviewReady: {
306− previewLoader.previewData = preview
307− 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
308348 }
309349 }
310350
@@ -312,18 +352,9 @@ ScopeView {
312352 id: previewDelegateMapper
313353 }
314354
315− Connections {
316− ignoreUnknownSignals: true
317− target: previewLoader.valid ? previewLoader.item : null
318− onClose: {
319− previewLoader.open = false
320− }
321− }
322−
323− Loader {
324− objectName: "previewLoader"
325− id: previewLoader
326− property var previewData
355+ ListView {
356+ id: previewListView
357+ objectName: "previewListView"
327358 height: effect.bottomGapPx - effect.topGapPx
328359 anchors {
329360 top: parent.top
@@ -331,38 +362,168 @@ ScopeView {
331362 left: parent.left
332363 right: parent.right
333364 }
334− 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+ }
335413
336414 property bool open: false
337415 property bool onScreen: false
338− property bool valid: item !== null
339416
340417 onOpenChanged: {
341418 if (open) {
342− 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;
343431 }
344432 }
345433
346− onLoaded: {
347− 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
438+ }
439+
440+ delegate: Loader {
441+ id: previewLoader
442+ objectName: "previewLoader" + index
443+ height: previewListView.height
444+ width: previewListView.width
445+ asynchronous: true
446+ source: previewListView.onScreen ?
447+ (previewData !== undefined ? previewDelegateMapper.map(previewData.rendererName) : "DashPreviewPlaceholder.qml") : ""
448+
449+ onPreviewDataChanged: {
450+ if (previewData !== undefined && source.toString().indexOf("DashPreviewPlaceholder.qml") != -1) {
451+ previewLoader.opacity = 0;
452+ }
453+ }
454+
455+ onSourceChanged: {
456+ if (previewData !== undefined) {
457+ fadeIn.start()
458+ }
459+ }
460+
461+ PropertyAnimation {
462+ id: fadeIn
463+ target: previewLoader
464+ property: "opacity"
465+ from: 0.0
466+ to: 1.0
467+ duration: UbuntuAnimation.BriskDuration
468+ }
469+
470+ property var previewData
471+ property bool valid: item !== null
472+
473+ onLoaded: {
474+ if (previewListView.onScreen && previewData !== undefined) {
475+ item.previewData = Qt.binding(function() { return previewData })
476+ }
477+ }
478+
479+ Connections {
480+ ignoreUnknownSignals: true
481+ target: item
482+ onClose: {
483+ previewListView.open = false
484+ }
485+ }
486+
487+ function closePreviewSpinner() {
488+ if(item) {
489+ item.showProcessingAction = false;
490+ }
491+ }
348492 }
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
349502
350− function closePreviewSpinner() {
351− if(item) {
352− item.showProcessingAction = false;
503+ Behavior on leftMargin {
504+ SmoothedAnimation {
505+ duration: UbuntuAnimation.FastDuration
506+ }
353507 }
354508 }
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"
355515 }
356516
357517 // TODO: Move as InverseMouseArea to DashPreview
358518 MouseArea {
359− enabled: previewLoader.onScreen
519+ objectName: "closePreviewMouseArea"
520+ enabled: previewListView.onScreen
360521 anchors {
361522 fill: parent
362523 topMargin: effect.bottomGapPx
363524 }
364525 onClicked: {
365− previewLoader.open = false;
526+ previewListView.open = false;
366527 }
367528 }
368529}
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 }
debian/changelog+36
@@ -1,3 +1,39 @@
1+unity8 (7.83+14.04.20131031-0ubuntu1) trusty; urgency=low
2+
3+ [ Michał Sawicz ]
4+ * Use setenv as early as possible to avoid setenv and getenv clashing
5+ in multi-threaded situations. (LP: #1240866)
6+
7+ [ Ubuntu daily release ]
8+ * Automatic snapshot from revision 482
9+
10+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Thu, 31 Oct 2013 20:10:56 +0000
11+
12+unity8 (7.83+14.04.20131028.1-0ubuntu1) trusty; urgency=low
13+
14+ * New rebuild forced
15+ * Automatic snapshot from revision 480
16+
17+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 28 Oct 2013 22:42:07 +0000
18+
19+unity8 (7.83+14.04.20131028-0ubuntu1) trusty; urgency=low
20+
21+ [ Andrea Cimitan ]
22+ * Fix 1195349 by counting drawbuffer on the newContentX logic of the
23+ carousel When we changed carousel from repeater to listview, we
24+ added drawbuffer. this breaks the logic of newContentX, which was
25+ considered disabled and set to -1. The correct disabled value now
26+ has to take into account the drawbuffer. (LP: #1195349)
27+
28+ [ Christopher Lee ]
29+ * Checks both then env and upstart env for the currently set
30+ XDG_DATA_DIRS so they can be set correctly for the test.
31+
32+ [ Ubuntu daily release ]
33+ * Automatic snapshot from revision 479
34+
35+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 28 Oct 2013 03:08:59 +0000
36+
137unity8 (7.83+13.10.20131016.2-0ubuntu1) saucy; urgency=low
238
339 [ Loïc Minier ]
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));
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
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 உள்ளிடு"
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/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/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/plugins/Unity/Indicators/CMakeLists.txt+1
@@ -30,4 +30,5 @@ run_tests(
3030 indicatorsmodeltest
3131 menucontentactivatortest
3232 rootactionstatetest
33+ timeformattertest
3334 )
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/tst_GenericScopeView.qml+123−13
@@ -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,44 +48,144 @@ 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 }
6975
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+
70177 function test_show_spinner() {
71− var previewLoader = findChild(genericScopeView, "previewLoader");
72− previewLoader.open = true;
73− previewLoader.source = "../../../Dash/Generic/GenericPreview.qml";
178+ openPreview();
179+ var previewListView = findChild(genericScopeView, "previewListView");
180+ var previewLoader = findChild(genericScopeView, "previewLoader0");
181+
74182 previewLoader.item.showProcessingAction = true;
75183 var waitingForAction = findChild(genericScopeView, "waitingForActionMouseArea");
76184 tryCompare(waitingForAction, "enabled", true);
77185 previewLoader.closePreviewSpinner();
78186 tryCompare(waitingForAction, "enabled", false);
187+
188+ closePreview();
79189 }
80190 }
81191 }
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+14−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() {
@@ -297,7 +302,7 @@ Item {
297302 verify(itemIsOnScreen(dashApps));
298303
299304 // Minimize the application we just launched
300− swipeFromLeftEdge();
305+ swipeFromLeftEdge(units.gu(27));
301306
302307 // Wait for the whole UI to settle down
303308 waitUntilApplicationWindowIsFullyHidden();
@@ -380,10 +385,10 @@ Item {
380385 }
381386 }
382387
383− function swipeFromLeftEdge() {
388+ function swipeFromLeftEdge(swipeLength) {
384389 var touchStartX = 2;
385390 var touchStartY = shell.height / 2;
386− touchFlick(shell, touchStartX, touchStartY, shell.width * 0.75, touchStartY);
391+ touchFlick(shell, touchStartX, touchStartY, swipeLength, touchStartY);
387392 }
388393
389394 function swipeLeftFromCenter() {