on xenial help sign in
merged trunk
1bff468Andrea Cimitan
Components/Carousel.qml+28−10
@@ -45,18 +45,24 @@ Item {
45 property int drawBuffer: width / pathItemCount // an "ok" value - but values used from the listView cause loops
45 property int drawBuffer: width / pathItemCount // an "ok" value - but values used from the listView cause loops
46 /// The selected item can be shown in a different size controlled by selectedItemScaleFactor
46 /// The selected item can be shown in a different size controlled by selectedItemScaleFactor
47 property real selectedItemScaleFactor: 1.1
47 property real selectedItemScaleFactor: 1.1
48+ /// The index of the item that should be highlighted
49+ property alias highlightIndex: listView.highlightIndex
50+ /// exposes the delegate of the currentItem
51+ readonly property alias currentItem: listView.currentItem
52+ /// exposes the distance to the next row (only one row in carousel, so it's the topMargins)
53+ readonly property alias verticalSpacing: listView.verticalMargin
48
54
49 /// Emitted when the user clicked on an item
55 /// Emitted when the user clicked on an item
50 /// @param index is the index of the clicked item
56 /// @param index is the index of the clicked item
51− /// @param delegateItem is the clicked component/delegate itself
57+ /// @param model is the model of all the items in the carousel
52 /// @param itemY is y of the clicked delegate
58 /// @param itemY is y of the clicked delegate
53− signal clicked(int index, var delegateItem, real itemY)
59+ signal clicked(int index, var model, real itemY)
54
60
55 /// Emitted when the user pressed and held on an item
61 /// Emitted when the user pressed and held on an item
56 /// @param index is the index of the held item
62 /// @param index is the index of the held item
57− /// @param delegateItem is the held component/delegate itself
63+ /// @param model is the model of all the items in the carousel
58 /// @param itemY is y of the held delegate
64 /// @param itemY is y of the held delegate
59− signal pressAndHold(int index, var delegateItem, real itemY)
65+ signal pressAndHold(int index, var model, real itemY)
60
66
61 implicitHeight: listView.tileHeight * selectedItemScaleFactor
67 implicitHeight: listView.tileHeight * selectedItemScaleFactor
62
68
@@ -75,6 +81,7 @@ Item {
75 property real newContentX: disabledNewContentX
81 property real newContentX: disabledNewContentX
76 property real pathItemCount: referenceWidth / referenceTileWidth
82 property real pathItemCount: referenceWidth / referenceTileWidth
77 property real tileAspectRatio: 1
83 property real tileAspectRatio: 1
84+ property int highlightIndex: -1
78
85
79 /* The positioning and scaling of the items in the carousel is based on the variable
86 /* The positioning and scaling of the items in the carousel is based on the variable
80 'continuousIndex', a continuous real variable between [0, 'carousel.model.count'],
87 'continuousIndex', a continuous real variable between [0, 'carousel.model.count'],
@@ -136,13 +143,14 @@ Item {
136 height: listView.tileHeight
143 height: listView.tileHeight
137 }
144 }
138
145
139− boundsBehavior: Flickable.StopAtBounds
146+ boundsBehavior: Flickable.DragOverBounds
140 cacheBuffer: carousel.cacheBuffer
147 cacheBuffer: carousel.cacheBuffer
141 flickDeceleration: Math.max(1500 * Math.pow(realWidth / referenceWidth, 1.5), 1500) // 1500 is platform default
148 flickDeceleration: Math.max(1500 * Math.pow(realWidth / referenceWidth, 1.5), 1500) // 1500 is platform default
142 maximumFlickVelocity: Math.max(2500 * Math.pow(realWidth / referenceWidth, 1.5), 2500) // 2500 is platform default
149 maximumFlickVelocity: Math.max(2500 * Math.pow(realWidth / referenceWidth, 1.5), 2500) // 2500 is platform default
143 orientation: ListView.Horizontal
150 orientation: ListView.Horizontal
144
151
145 function itemClicked(index, delegateItem) {
152 function itemClicked(index, delegateItem) {
153+ listView.currentIndex = index
146 var x = CarouselJS.getXFromContinuousIndex(index,
154 var x = CarouselJS.getXFromContinuousIndex(index,
147 realWidth,
155 realWidth,
148 realContentWidth,
156 realContentWidth,
@@ -151,11 +159,11 @@ Item {
151 gapToEndPhase,
159 gapToEndPhase,
152 carousel.drawBuffer)
160 carousel.drawBuffer)
153
161
154− if (Math.abs(x - contentX) < 1) {
162+ if (Math.abs(x - contentX) < 1 && delegateItem !== undefined) {
155 /* We're clicking the selected item and
163 /* We're clicking the selected item and
156 we're in the neighbourhood of radius 1 pixel from it.
164 we're in the neighbourhood of radius 1 pixel from it.
157 Let's emit the clicked signal. */
165 Let's emit the clicked signal. */
158− carousel.clicked(index, delegateItem, delegateItem.y)
166+ carousel.clicked(index, listView.model, delegateItem.y)
159 return
167 return
160 }
168 }
161
169
@@ -175,11 +183,11 @@ Item {
175 gapToEndPhase,
183 gapToEndPhase,
176 carousel.drawBuffer);
184 carousel.drawBuffer);
177
185
178− if (Math.abs(x - contentX) < 1) {
186+ if (Math.abs(x - contentX) < 1 && delegateItem !== undefined) {
179 /* We're pressAndHold the selected item and
187 /* We're pressAndHold the selected item and
180 we're in the neighbourhood of radius 1 pixel from it.
188 we're in the neighbourhood of radius 1 pixel from it.
181 Let's emit the pressAndHold signal. */
189 Let's emit the pressAndHold signal. */
182− carousel.pressAndHold(index, delegateItem, delegateItem.y);
190+ carousel.pressAndHold(index, listView.model, delegateItem.y);
183 return;
191 return;
184 }
192 }
185
193
@@ -190,6 +198,12 @@ Item {
190 newContentXAnimation.start();
198 newContentXAnimation.start();
191 }
199 }
192
200
201+ onHighlightIndexChanged: {
202+ if (highlightIndex != -1) {
203+ itemClicked(highlightIndex)
204+ }
205+ }
206+
193 onMovementStarted: {
207 onMovementStarted: {
194 stepAnimation.stop()
208 stepAnimation.stop()
195 newContentXAnimation.stop()
209 newContentXAnimation.stop()
@@ -273,14 +287,18 @@ Item {
273 itemTranslationScale,
287 itemTranslationScale,
274 listView.maximumItemTranslation)
288 listView.maximumItemTranslation)
275
289
290+ readonly property real xTransform: listView.viewTranslation + translationX * listView.scaleFactor
291+ readonly property real center: x - listView.contentX + xTransform - drawBuffer + (width/2)
292+
276 width: listView.tileWidth
293 width: listView.tileWidth
277 height: listView.tileHeight
294 height: listView.tileHeight
278 scale: itemScale * explicitScaleFactor
295 scale: itemScale * explicitScaleFactor
279 sourceComponent: itemComponent
296 sourceComponent: itemComponent
280 z: cachedTiles - Math.abs(index - listView.selectedIndex)
297 z: cachedTiles - Math.abs(index - listView.selectedIndex)
298+ opacity: highlightIndex == -1 ? 1 : (highlightIndex == index ? 0.6 : 0.2)
281
299
282 transform: Translate {
300 transform: Translate {
283− x: listView.viewTranslation + translationX * listView.scaleFactor
301+ x: xTransform
284 }
302 }
285
303
286 Behavior on explicitScaleFactor {
304 Behavior on explicitScaleFactor {
Components/FilterGrid.qml+3
@@ -54,6 +54,7 @@ Item {
54 property alias delegateWidth: iconTileGrid.delegateWidth
54 property alias delegateWidth: iconTileGrid.delegateWidth
55 property alias delegateHeight: iconTileGrid.delegateHeight
55 property alias delegateHeight: iconTileGrid.delegateHeight
56 property alias verticalSpacing: iconTileGrid.verticalSpacing
56 property alias verticalSpacing: iconTileGrid.verticalSpacing
57+ readonly property alias margins: iconTileGrid.margins
57 property alias delegate: iconTileGrid.delegate
58 property alias delegate: iconTileGrid.delegate
58 property alias cellWidth: iconTileGrid.cellWidth
59 property alias cellWidth: iconTileGrid.cellWidth
59 property alias cellHeight: iconTileGrid.cellHeight
60 property alias cellHeight: iconTileGrid.cellHeight
@@ -62,6 +63,8 @@ Item {
62 readonly property alias flicking: iconTileGrid.flicking
63 readonly property alias flicking: iconTileGrid.flicking
63 readonly property alias moving: iconTileGrid.moving
64 readonly property alias moving: iconTileGrid.moving
64 readonly property alias pressDelay: iconTileGrid.pressDelay
65 readonly property alias pressDelay: iconTileGrid.pressDelay
66+ property alias highlightIndex: iconTileGrid.highlightIndex
67+ readonly property alias currentItem: iconTileGrid.currentItem
65
68
66 height: !filterAnimation.running ? childrenRect.height : height
69 height: !filterAnimation.running ? childrenRect.height : height
67 clip: filterAnimation.running
70 clip: filterAnimation.running
Components/LazyImage.qml+2−2
@@ -100,10 +100,10 @@ Item {
100 verticalAlignment: Image.AlignVCenter
100 verticalAlignment: Image.AlignVCenter
101 sourceSize.width: root.scaleTo == "width" ? root.width
101 sourceSize.width: root.scaleTo == "width" ? root.width
102 : root.scaleTo == "fit" && root.width < root.height ? root.width
102 : root.scaleTo == "fit" && root.width < root.height ? root.width
103− : undefined
103+ : 0
104 sourceSize.height: root.scaleTo == "height" ? root.height
104 sourceSize.height: root.scaleTo == "height" ? root.height
105 : root.scale == "fit" && root.height < root.width ? root.height
105 : root.scale == "fit" && root.height < root.width ? root.height
106− : undefined
106+ : 0
107 }
107 }
108 }
108 }
109
109
Components/PageHeader.qml+4
@@ -48,6 +48,10 @@ Item {
48 searchField.focus = false;
48 searchField.focus = false;
49 }
49 }
50
50
51+ function unfocus() {
52+ searchField.focus = false;
53+ }
54+
51 Connections {
55 Connections {
52 target: greeter
56 target: greeter
53 onShownChanged: if (shown) resetSearch()
57 onShownChanged: if (shown) resetSearch()
Components/ResponsiveGridView.qml+10
@@ -26,6 +26,7 @@ Item {
26 property int maximumNumberOfColumns: 6
26 property int maximumNumberOfColumns: 6
27 readonly property int columns: gridView.columns
27 readonly property int columns: gridView.columns
28 property alias verticalSpacing: gridView.verticalSpacing
28 property alias verticalSpacing: gridView.verticalSpacing
29+ readonly property alias margins: gridView.margin
29 property int delegateWidth
30 property int delegateWidth
30 property int delegateHeight
31 property int delegateHeight
31 property alias model: gridView.model
32 property alias model: gridView.model
@@ -41,6 +42,8 @@ Item {
41 readonly property alias pressDelay: gridView.pressDelay
42 readonly property alias pressDelay: gridView.pressDelay
42 property alias delegateCreationBegin: gridView.delegateCreationBegin
43 property alias delegateCreationBegin: gridView.delegateCreationBegin
43 property alias delegateCreationEnd: gridView.delegateCreationEnd
44 property alias delegateCreationEnd: gridView.delegateCreationEnd
45+ property alias highlightIndex: gridView.highlightIndex
46+ readonly property alias currentItem: gridView.currentItem
44
47
45 function contentHeightForRows(rows) {
48 function contentHeightForRows(rows) {
46 return rows * cellHeight + verticalSpacing
49 return rows * cellHeight + verticalSpacing
@@ -77,8 +80,15 @@ Item {
77 property real horizontalSpacing: spacingForColumns(columns)
80 property real horizontalSpacing: spacingForColumns(columns)
78 property real verticalSpacing: horizontalSpacing
81 property real verticalSpacing: horizontalSpacing
79 property int margin: allocatableHorizontalSpace - columns * horizontalSpacing
82 property int margin: allocatableHorizontalSpace - columns * horizontalSpacing
83+ property int highlightIndex: -1
80
84
81 cellWidth: delegateWidth + horizontalSpacing
85 cellWidth: delegateWidth + horizontalSpacing
82 cellHeight: delegateHeight + verticalSpacing
86 cellHeight: delegateHeight + verticalSpacing
87+
88+ onHighlightIndexChanged: {
89+ if (highlightIndex != -1) {
90+ currentIndex = highlightIndex
91+ }
92+ }
83 }
93 }
84}
94}
Components/Tile.qml+9
@@ -26,5 +26,14 @@ AbstractButton {
26 property int imageHeight
26 property int imageHeight
27 property int maximumLineCount: 1
27 property int maximumLineCount: 1
28
28
29+ readonly property int center: (index % GridView.view.columns * width) + (width / 2)
30+
31+ opacity: GridView.view.highlightIndex === -1 ? 1 :
32+ GridView.view.highlightIndex === index ? 0.6 : 0.2
33+
34+ Behavior on opacity {
35+ UbuntuNumberAnimation {}
36+ }
37+
29 style: TileStyle {}
38 style: TileStyle {}
30}
39}
Components/UbuntuShapeForItem.qml+1
@@ -31,6 +31,7 @@ Item {
31
31
32 ShaderEffectSource {
32 ShaderEffectSource {
33 id: source
33 id: source
34+ anchors.centerIn: parent // Placed under shape, so it's hidden
34 width: 1
35 width: 1
35 height: 1
36 height: 1
36 hideSource: true
37 hideSource: true
Dash/Apps/AppPreview.qml+9
@@ -44,6 +44,15 @@ GenericPreview {
44 model: previewData.infoMap["more-screenshots"] != null ? previewData.infoMap["more-screenshots"].value : [previewData.image]
44 model: previewData.infoMap["more-screenshots"] != null ? previewData.infoMap["more-screenshots"].value : [previewData.image]
45 cacheBuffer: width * 3
45 cacheBuffer: width * 3
46
46
47+ // FIXME: Because of ListViews inside ListViews inside Flickables inside ListViews (and some more)
48+ // we finally reached the point where this ListView doesn't correctly get swipe input any more but
49+ // instead the parent ListView is the one that is swiped. This MouseArea sort of creates a blocking
50+ // layer to make sure this ListView can be swiped, regardless of what's behind it.
51+ MouseArea {
52+ anchors.fill: parent
53+ enabled: parent.contentWidth > parent.width
54+ }
55+
47 LazyImage {
56 LazyImage {
48 objectName: "placeholderScreenshot"
57 objectName: "placeholderScreenshot"
49 anchors {
58 anchors {
Dash/Apps/ApplicationsFilterGrid.qml+14−5
@@ -19,7 +19,7 @@ import "../../Components"
19import "../../Applications"
19import "../../Applications"
20
20
21FilterGrid {
21FilterGrid {
22− id: filterGrid
22+ id: filtergrid
23
23
24 filter: false
24 filter: false
25 minimumHorizontalSpacing: units.gu(0.5)
25 minimumHorizontalSpacing: units.gu(0.5)
@@ -28,9 +28,11 @@ FilterGrid {
28 delegateHeight: units.gu(9.5)
28 delegateHeight: units.gu(9.5)
29 verticalSpacing: units.gu(2)
29 verticalSpacing: units.gu(2)
30
30
31− signal clicked(int index, variant data)
31+ signal clicked(int index, var model, real itemY)
32+ signal pressAndHold(int index, var model, real itemY)
32
33
33 delegate: Tile {
34 delegate: Tile {
35+ id: tile
34 objectName: "delegate" + index
36 objectName: "delegate" + index
35 Application {
37 Application {
36 id: application
38 id: application
@@ -44,12 +46,19 @@ FilterGrid {
44
46
45 property string icon: model.icon ? model.icon : "../../graphics/applicationIcons/" + application.icon + ".png" // FIXME: this is temporary
47 property string icon: model.icon ? model.icon : "../../graphics/applicationIcons/" + application.icon + ".png" // FIXME: this is temporary
46
48
47− width: filterGrid.cellWidth
49+ width: filtergrid.cellWidth
48− height: filterGrid.cellHeight
50+ height: filtergrid.cellHeight
49 text: model.title ? model.title : application.name // FIXME: this is temporary
51 text: model.title ? model.title : application.name // FIXME: this is temporary
50 imageWidth: units.gu(8)
52 imageWidth: units.gu(8)
51 imageHeight: units.gu(7.5)
53 imageHeight: units.gu(7.5)
52 source: icon
54 source: icon
53− onClicked: filterGrid.clicked(index, application.desktopFile);
55+
56+ onClicked: {
57+ filtergrid.clicked(index, filtergrid.model, tile.y)
58+ }
59+
60+ onPressAndHold: {
61+ filtergrid.pressAndHold(index, filtergrid.model, tile.y)
62+ }
54 }
63 }
55}
64}
Dash/Apps/DashPluginFilterGrid.qml+2−4
@@ -37,13 +37,11 @@ GenericFilterGrid {
37 }
37 }
38
38
39 onClicked: {
39 onClicked: {
40− var data = { model: model }
41− filtergrid.clicked(index, data, tile.y)
40+ filtergrid.clicked(index, filtergrid.model, tile.y)
42 }
41 }
43
42
44 onPressAndHold: {
43 onPressAndHold: {
45− var data = { model: model }
46− filtergrid.pressAndHold(index, data, tile.y)
44+ filtergrid.pressAndHold(index, filtergrid.model, tile.y)
47 }
45 }
48 }
46 }
49}
47}
Dash/DashBar.qml+2−2
@@ -35,7 +35,7 @@ Item {
35
35
36 function startNavigation() {
36 function startNavigation() {
37 timeout.stop()
37 timeout.stop()
38− panel.opened = true
38+ panel.open()
39 }
39 }
40
40
41 function stopNavigation() {
41 function stopNavigation() {
@@ -43,7 +43,7 @@ Item {
43 }
43 }
44
44
45 function finishNavigation() {
45 function finishNavigation() {
46− panel.opened = false
46+ panel.close()
47 }
47 }
48
48
49 Timer {
49 Timer {
Dash/DashContent.qml+2−2
@@ -116,8 +116,8 @@ Item {
116 source: scopeMapper.map(scope.id)
116 source: scopeMapper.map(scope.id)
117 objectName: scope.id + " loader"
117 objectName: scope.id + " loader"
118
118
119− readonly property bool previewShown: item.previewShown
119+ readonly property bool previewShown: item ? item.previewShown : false
120− readonly property bool moving: item.moving
120+ readonly property bool moving: item ? item.moving : false
121
121
122 // these are needed for autopilot tests
122 // these are needed for autopilot tests
123 readonly property string scopeId: scope.id
123 readonly property string scopeId: scope.id
Dash/DashPreview.qml+1−3
@@ -17,7 +17,7 @@
17import QtQuick 2.0
17import QtQuick 2.0
18import Ubuntu.Components 0.1
18import Ubuntu.Components 0.1
19
19
20−Rectangle {
20+Item {
21 id: root
21 id: root
22
22
23 property int keyboardSize: Qt.inputMethod.visible ? Qt.inputMethod.keyboardRectangle.height : 0
23 property int keyboardSize: Qt.inputMethod.visible ? Qt.inputMethod.keyboardRectangle.height : 0
@@ -36,7 +36,6 @@ Rectangle {
36 readonly property int columnWidth: narrowMode ? contentRow.width : (contentRow.width / 3) - contentRow.spacing
36 readonly property int columnWidth: narrowMode ? contentRow.width : (contentRow.width / 3) - contentRow.spacing
37 readonly property int contentSpacing: units.gu(3)
37 readonly property int contentSpacing: units.gu(3)
38
38
39− color: Qt.rgba(0, 0, 0, .3)
40 clip: true
39 clip: true
41
40
42 function ensureVisible(item) {
41 function ensureVisible(item) {
@@ -61,7 +60,6 @@ Rectangle {
61 anchors.fill: parent
60 anchors.fill: parent
62 }
61 }
63
62
64−
65 Row {
63 Row {
66 id: contentRow
64 id: contentRow
67 anchors {
65 anchors {
Launcher/QuicklistForegroundStyle.qml → Dash/DashPreviewPlaceholder.qmlrenamed+7−5
@@ -14,10 +14,12 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
15 */
16
16
17+import QtQuick 2.0
17−import Ubuntu.Components.Themes.Ambiance 0.1
18+import Ubuntu.Components 0.1
18
19
19−PopoverForegroundStyle {
20+DashPreview {
20− bubbleColor: "#221e1c"
21+ ActivityIndicator {
21− bubbleOpacity: 0.9
22+ anchors.centerIn: parent
22− arrowSource: "graphics/quicklist_tooltip.png"
23+ running: true
24+ }
23}
25}
Dash/Generic/GenericFilterGrid.qml+4−6
@@ -28,8 +28,8 @@ FilterGrid {
28 property int iconWidth: units.gu(8)
28 property int iconWidth: units.gu(8)
29 property int iconHeight: units.gu(7.5)
29 property int iconHeight: units.gu(7.5)
30
30
31− signal clicked(int index, var delegateItem, real itemY)
31+ signal clicked(int index, var model, real itemY)
32− signal pressAndHold(int index, var delegateItem, real itemY)
32+ signal pressAndHold(int index, var model, real itemY)
33
33
34 delegate: Tile {
34 delegate: Tile {
35 id: tile
35 id: tile
@@ -42,13 +42,11 @@ FilterGrid {
42 source: model.icon
42 source: model.icon
43
43
44 onClicked: {
44 onClicked: {
45− var data = { model: model }
46− filtergrid.clicked(index, data, tile.y)
45+ filtergrid.clicked(index, filtergrid.model, tile.y)
47 }
46 }
48
47
49 onPressAndHold: {
48 onPressAndHold: {
50− var data = { model: model }
51− filtergrid.pressAndHold(index, data, tile.y)
49+ filtergrid.pressAndHold(index, filtergrid.model, tile.y)
52 }
50 }
53 }
51 }
54}
52}
Dash/Generic/WeatherFilterGrid.qml+2−4
@@ -37,13 +37,11 @@ GenericFilterGrid {
37 style: FlatTileStyle {}
37 style: FlatTileStyle {}
38
38
39 onClicked: {
39 onClicked: {
40− var data = { model: model }
41− filtergrid.clicked(index, data, tile.y)
40+ filtergrid.clicked(index, filtergrid.model, tile.y)
42 }
41 }
43
42
44 onPressAndHold: {
43 onPressAndHold: {
45− var data = { model: model }
46− filtergrid.pressAndHold(index, data, tile.y)
44+ filtergrid.pressAndHold(index, filtergrid.model, tile.y)
47 }
45 }
48 }
46 }
49}
47}
Dash/GenericScopeView.qml+220−59
@@ -21,14 +21,14 @@ import "../Components/ListItems" as ListItems
21
21
22ScopeView {
22ScopeView {
23 id: scopeView
23 id: scopeView
24− readonly property alias previewShown: previewLoader.onScreen
24+ readonly property alias previewShown: previewListView.onScreen
25 property bool enableHeightBehaviorOnNextCreation: false
25 property bool enableHeightBehaviorOnNextCreation: false
26
26
27 moving: categoryView.moving
27 moving: categoryView.moving
28
28
29 onIsCurrentChanged: {
29 onIsCurrentChanged: {
30 pageHeader.resetSearch();
30 pageHeader.resetSearch();
31− previewLoader.open = false;
31+ previewListView.open = false;
32 }
32 }
33
33
34 onMovementStarted: categoryView.showHeader()
34 onMovementStarted: categoryView.showHeader()
@@ -57,9 +57,8 @@ ScopeView {
57
57
58 Connections {
58 Connections {
59 target: scopeView.scope
59 target: scopeView.scope
60− onShowDash: previewLoader.open = false;
60+ onShowDash: previewListView.open = false;
61− onHideDash: previewLoader.open = false;
61+ onHideDash: previewListView.open = false;
62− onActivated: previewLoader.closePreviewSpinner();
63 }
62 }
64
63
65 ScopeListView {
64 ScopeListView {
@@ -67,12 +66,18 @@ ScopeView {
67 objectName: "categoryListView"
66 objectName: "categoryListView"
68 anchors.fill: parent
67 anchors.fill: parent
69 model: scopeView.categories
68 model: scopeView.categories
70− forceNoClip: previewLoader.onScreen
69+ forceNoClip: previewListView.onScreen
71
70
72 onAtYEndChanged: if (atYEnd) endReached()
71 onAtYEndChanged: if (atYEnd) endReached()
73 onMovingChanged: if (moving && atYEnd) endReached()
72 onMovingChanged: if (moving && atYEnd) endReached()
74
73
75 property string expandedCategoryId: ""
74 property string expandedCategoryId: ""
75+ signal correctExpandedCategory();
76+
77+ Behavior on contentY {
78+ enabled: previewListView.open
79+ UbuntuNumberAnimation {}
80+ }
76
81
77 delegate: ListItems.Base {
82 delegate: ListItems.Base {
78 id: baseItem
83 id: baseItem
@@ -124,33 +129,50 @@ ScopeView {
124 Connections {
129 Connections {
125 target: rendererLoader.item
130 target: rendererLoader.item
126 onClicked: {
131 onClicked: {
132+ // Prepare the preview in case activate() triggers a preview only
127 effect.positionPx = mapToItem(categoryView, 0, itemY).y
133 effect.positionPx = mapToItem(categoryView, 0, itemY).y
134+ previewListView.categoryId = categoryId
135+ previewListView.categoryDelegate = rendererLoader.item
136+ previewListView.model = model;
137+ previewListView.init = true;
138+ previewListView.currentIndex = index;
139+
140+ var item = model.get(index);
141+
142+ if ((scopeView.scope.id == "applications.scope" && categoryId == "installed")
143+ || (scopeView.scope.id == "home.scope" && categoryId == "applications.scope")) {
128− scopeView.scope.activate(delegateItem.model.uri,
144+ scopeView.scope.activate(item.uri, item.icon, item.category, 0, item.mimetype, item.title,
129− delegateItem.model.icon,
145+ item.comment, item.dndUri, item.metadata)
130− delegateItem.model.category,
146+ } else {
131− 0,
147+ previewListView.open = true
132− delegateItem.model.mimetype,
148+
133− delegateItem.model.title,
149+ scopeView.scope.preview( item.uri, item.icon, item.category, 0, item.mimetype, item.title,
134− delegateItem.model.comment,
150+ item.comment, item.dndUri, item.metadata)
135− delegateItem.model.dndUri,
151+ }
136− delegateItem.model.metadata)
137 }
152 }
138 onPressAndHold: {
153 onPressAndHold: {
139 effect.positionPx = mapToItem(categoryView, 0, itemY).y
154 effect.positionPx = mapToItem(categoryView, 0, itemY).y
155+ previewListView.categoryId = categoryId
156+ previewListView.categoryDelegate = rendererLoader.item
157+ previewListView.model = model;
158+ previewListView.init = true;
159+ previewListView.currentIndex = index;
160+ previewListView.open = true
161+
162+ var item = model.get(index)
140− scopeView.scope.preview( delegateItem.model.uri,
163+ scopeView.scope.preview( item.uri, item.icon, item.category, 0, item.mimetype, item.title,
141− delegateItem.model.icon,
164+ item.comment, item.dndUri, item.metadata)
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)
149 }
165 }
150 }
166 }
151 Connections {
167 Connections {
152 target: categoryView
168 target: categoryView
153 onExpandedCategoryIdChanged: {
169 onExpandedCategoryIdChanged: {
170+ collapseAllButExpandedCategory();
171+ }
172+ onCorrectExpandedCategory: {
173+ collapseAllButExpandedCategory();
174+ }
175+ function collapseAllButExpandedCategory() {
154 var item = rendererLoader.item;
176 var item = rendererLoader.item;
155 if (item.expandable) {
177 if (item.expandable) {
156 var shouldFilter = categoryId != categoryView.expandedCategoryId;
178 var shouldFilter = categoryId != categoryView.expandedCategoryId;
@@ -158,13 +180,15 @@ ScopeView {
158 // If the filter animation will be seen start it, otherwise, just flip the switch
180 // If the filter animation will be seen start it, otherwise, just flip the switch
159 var shrinkingVisible = shouldFilter && y + item.collapsedHeight < categoryView.height;
181 var shrinkingVisible = shouldFilter && y + item.collapsedHeight < categoryView.height;
160 var growingVisible = !shouldFilter && y + height < categoryView.height;
182 var growingVisible = !shouldFilter && y + height < categoryView.height;
183+ if (!previewListView.open || !shouldFilter) {
161− if (shrinkingVisible || growingVisible) {
184+ if (shrinkingVisible || growingVisible) {
162− item.startFilterAnimation(shouldFilter)
185+ item.startFilterAnimation(shouldFilter)
163− } else {
186+ } else {
164− item.filter = shouldFilter;
187+ item.filter = shouldFilter;
165− }
188+ }
166− if (!shouldFilter) {
189+ if (!shouldFilter && !previewListView.open) {
167− categoryView.maximizeVisibleArea(index, item.uncollapsedHeight);
190+ categoryView.maximizeVisibleArea(index, item.uncollapsedHeight);
191+ }
168 }
192 }
169 }
193 }
170 }
194 }
@@ -270,6 +294,7 @@ ScopeView {
270
294
271 OpenEffect {
295 OpenEffect {
272 id: effect
296 id: effect
297+ objectName: "openEffect"
273 anchors {
298 anchors {
274 fill: parent
299 fill: parent
275 bottomMargin: -bottomOverflow
300 bottomMargin: -bottomOverflow
@@ -282,29 +307,44 @@ ScopeView {
282 topOpacity: (1 - gap * 1.2)
307 topOpacity: (1 - gap * 1.2)
283 bottomGapPx: positionPx + gap * (targetBottomGapPx - positionPx)
308 bottomGapPx: positionPx + gap * (targetBottomGapPx - positionPx)
284 bottomOverflow: units.gu(20)
309 bottomOverflow: units.gu(20)
285− bottomOpacity: 1 - (gap * 0.8)
310+ live: !expansionAnimation.running
286
311
287 property int targetBottomGapPx: height - units.gu(8) - bottomOverflow
312 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
289
314
290 Behavior on gap {
315 Behavior on gap {
291 NumberAnimation {
316 NumberAnimation {
317+ id: expansionAnimation
292 duration: 200
318 duration: 200
293 easing.type: Easing.InOutQuad
319 easing.type: Easing.InOutQuad
294 onRunningChanged: {
320 onRunningChanged: {
295− if (!previewLoader.open && !running) {
321+ if (!previewListView.open && !running) {
296− previewLoader.onScreen = false
322+ previewListView.onScreen = false
297 }
323 }
298 }
324 }
299 }
325 }
300 }
326 }
327+ Behavior on positionPx {
328+ enabled: previewListView.open
329+ UbuntuNumberAnimation {}
330+ }
301 }
331 }
302
332
303 Connections {
333 Connections {
304 target: scopeView.scope
334 target: scopeView.scope
305 onPreviewReady: {
335 onPreviewReady: {
336+ if (previewListView.init) {
337+ // Preview was triggered because of a click on the item. Need to expand now.
338+ if (!previewListView.open) {
339+ previewListView.open = true
340+ }
341+
342+ var index = previewListView.currentIndex
343+ previewListView.currentIndex = -1
344+ previewListView.currentIndex = index
345+ previewListView.init = false
346+ }
306− previewLoader.previewData = preview
347+ previewListView.currentItem.previewData = preview
307− previewLoader.open = true
308 }
348 }
309 }
349 }
310
350
@@ -312,18 +352,9 @@ ScopeView {
312 id: previewDelegateMapper
352 id: previewDelegateMapper
313 }
353 }
314
354
315− Connections {
355+ ListView {
316− ignoreUnknownSignals: true
356+ id: previewListView
317− target: previewLoader.valid ? previewLoader.item : null
357+ objectName: "previewListView"
318− onClose: {
319− previewLoader.open = false
320− }
321− }
322−
323− Loader {
324− objectName: "previewLoader"
325− id: previewLoader
326− property var previewData
327 height: effect.bottomGapPx - effect.topGapPx
358 height: effect.bottomGapPx - effect.topGapPx
328 anchors {
359 anchors {
329 top: parent.top
360 top: parent.top
@@ -331,38 +362,168 @@ ScopeView {
331 left: parent.left
362 left: parent.left
332 right: parent.right
363 right: parent.right
333 }
364 }
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+ }
335
413
336 property bool open: false
414 property bool open: false
337 property bool onScreen: false
415 property bool onScreen: false
338− property bool valid: item !== null
339
416
340 onOpenChanged: {
417 onOpenChanged: {
341 if (open) {
418 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;
343 }
431 }
344 }
432 }
345
433
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+
346− onLoaded: {
473+ onLoaded: {
474+ if (previewListView.onScreen && previewData !== undefined) {
347− item.previewData = Qt.binding(function() { return previewLoader.previewData })
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+ }
348 }
492 }
493+ }
494+
495+ Image {
496+ objectName: "pointerArrow"
497+ anchors {
498+ top: previewListView.bottom
499+ left: parent.left
500+ leftMargin: previewListView.categoryDelegate !== undefined && previewListView.categoryDelegate.currentItem ?
501+ previewListView.categoryDelegate.currentItem.center + (-width + margins) / 2 : 0
349
502
350− function closePreviewSpinner() {
503+ Behavior on leftMargin {
351− if(item) {
504+ SmoothedAnimation {
352− item.showProcessingAction = false;
505+ duration: UbuntuAnimation.FastDuration
506+ }
353 }
507 }
354 }
508 }
509+ height: units.gu(1)
510+ width: units.gu(2)
511+ property int margins: previewListView.categoryDelegate ? previewListView.categoryDelegate.margins : 0
512+ opacity: previewListView.open ? .5 : 0
513+
514+ source: "graphics/tooltip_arrow.png"
355 }
515 }
356
516
357 // TODO: Move as InverseMouseArea to DashPreview
517 // TODO: Move as InverseMouseArea to DashPreview
358 MouseArea {
518 MouseArea {
519+ objectName: "closePreviewMouseArea"
359− enabled: previewLoader.onScreen
520+ enabled: previewListView.onScreen
360 anchors {
521 anchors {
361 fill: parent
522 fill: parent
362 topMargin: effect.bottomGapPx
523 topMargin: effect.bottomGapPx
363 }
524 }
364 onClicked: {
525 onClicked: {
365− previewLoader.open = false;
526+ previewListView.open = false;
366 }
527 }
367 }
528 }
368}
529}
Dash/Music/AlbumTile.qmldeleted−116
@@ -1,116 +0,0 @@
1−/*
2− * Copyright (C) 2013 Canonical, Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−import QtQuick 2.0
18−import Ubuntu.Components 0.1
19−
20−AbstractButton {
21− id: root
22− property url source
23− property alias album: albumLabel.text
24− property alias artist: artistLabel.text
25− property alias iconWidth: icon.width
26− property alias iconHeight: icon.height
27−
28− UbuntuShape {
29− id: icon
30− anchors {
31− top: parent.top
32− horizontalCenter: parent.horizontalCenter
33− }
34− radius: "medium"
35− image: Image {
36− source: root.source
37− sourceSize { width: icon.width; height: icon.height }
38− asynchronous: true
39− cache: false
40− }
41− }
42−
43− UbuntuShape {
44− id: borderPressed
45−
46− anchors.fill: icon
47− radius: "medium"
48− borderSource: "radius_pressed.sci"
49− opacity: root.pressed ? 1.0 : 0.0
50− Behavior on opacity { NumberAnimation { duration: 200; easing.type: Easing.OutQuint } }
51− }
52−
53− Item {
54− id: artistContainer
55− anchors {
56− left: parent.left
57− right: parent.right
58− top: icon.bottom
59− }
60− height: units.gu(2)
61−
62− Label {
63− id: artistLabel
64− anchors {
65− baseline: parent.bottom
66− left: parent.left
67− right: parent.right
68− leftMargin: units.gu(1)
69− rightMargin: units.gu(1)
70− }
71−
72− color: Theme.palette.selected.backgroundText
73− opacity: 0.9
74− style: Text.Raised
75− styleColor: "black"
76− fontSize: "small"
77− elide: Text.ElideMiddle
78− horizontalAlignment: Text.AlignHCenter
79− }
80− }
81−
82− Item {
83− anchors {
84− left: parent.left
85− right: parent.right
86− top: artistContainer.bottom
87− }
88− height: units.gu(1.5)
89−
90− Label {
91− id: albumLabel
92− anchors {
93− baseline: parent.bottom
94− left: parent.left
95− right: parent.right
96− leftMargin: units.gu(1)
97− rightMargin: units.gu(1)
98− }
99−
100− color: Theme.palette.selected.backgroundText
101− opacity: 0.9
102− style: Text.Raised
103− styleColor: "black"
104− fontSize: "small"
105− font.weight: Font.Light
106− elide: Text.ElideMiddle
107− horizontalAlignment: Text.AlignHCenter
108− }
109− }
110−
111− GridView.onRemove: SequentialAnimation {
112− PropertyAction { target: root; property: "GridView.delayRemove"; value: true }
113− NumberAnimation { target: root; property: "opacity"; from: 1; to: 0; duration: 200; easing.type: Easing.InOutQuad }
114− PropertyAction { target: root; property: "GridView.delayRemove"; value: false }
115− }
116−}
Dash/Music/MusicFilterGrid.qml+2−4
@@ -44,13 +44,11 @@ GenericFilterGrid {
44 }
44 }
45
45
46 onClicked: {
46 onClicked: {
47− var data = { model: model }
48− filtergrid.clicked(index, data, tile.y)
47+ filtergrid.clicked(index, filtergrid.model, tile.y)
49 }
48 }
50
49
51 onPressAndHold: {
50 onPressAndHold: {
52− var data = { model: model }
53− filtergrid.pressAndHold(index, data, tile.y)
51+ filtergrid.pressAndHold(index, filtergrid.model, tile.y)
54 }
52 }
55 }
53 }
56}
54}
Dash/SearchableResultModel.qml+4
@@ -25,6 +25,10 @@ SortFilterProxyModel {
25
25
26 filterRole: CategoryResults.RoleTitle
26 filterRole: CategoryResults.RoleTitle
27
27
28+ function get(index) {
29+ return model.get(mapToSource(index))
30+ }
31+
28 onSearchQueryChanged: {
32 onSearchQueryChanged: {
29 if (searchQuery.length == 0) {
33 if (searchQuery.length == 0) {
30 filterRegExp = RegExp("");
34 filterRegExp = RegExp("");
Dash/Video/VideoFilterGrid.qml+2−4
@@ -42,13 +42,11 @@ GenericFilterGrid {
42 style: VideoTileStyle {}
42 style: VideoTileStyle {}
43
43
44 onClicked: {
44 onClicked: {
45− var data = { model: model }
46− filtergrid.clicked(index, data, tile.y)
45+ filtergrid.clicked(index, filtergrid.model, tile.y)
47 }
46 }
48
47
49 onPressAndHold: {
48 onPressAndHold: {
50− var data = { model: model }
51− filtergrid.pressAndHold(index, data, tile.y)
49+ filtergrid.pressAndHold(index, filtergrid.model, tile.y)
52 }
50 }
53 }
51 }
54}
52}
Dash/graphics/tooltip_arrow@27.pngnew file

Binary file not shown.

Greeter/Greeter.qml+1−1
@@ -52,7 +52,7 @@ Showable {
52 signal unlocked(int uid)
52 signal unlocked(int uid)
53
53
54 onRightTeaserPressedChanged: {
54 onRightTeaserPressedChanged: {
55− if (rightTeaserPressed && !locked) {
55+ if (rightTeaserPressed && !locked && x == 0) {
56 teasingTimer.start();
56 teasingTimer.start();
57 }
57 }
58 }
58 }
Launcher/Launcher.qml+1−1
@@ -28,7 +28,7 @@ Item {
28
28
29 property int panelWidth: units.gu(8)
29 property int panelWidth: units.gu(8)
30 property int dragAreaWidth: units.gu(1)
30 property int dragAreaWidth: units.gu(1)
31− property int minimizeDistance: panelWidth * 2.5
31+ property int minimizeDistance: units.gu(26)
32 property real progress: dragArea.dragging && dragArea.touchX > panelWidth ?
32 property real progress: dragArea.dragging && dragArea.touchX > panelWidth ?
33 (width * (dragArea.touchX-panelWidth) / (width - panelWidth)) : 0
33 (width * (dragArea.touchX-panelWidth) / (width - panelWidth)) : 0
34
34
Launcher/LauncherPanel.qml+87−46
@@ -21,6 +21,7 @@ import Unity 0.1
21import Unity.Launcher 0.1
21import Unity.Launcher 0.1
22import Ubuntu.Components.Popups 0.1
22import Ubuntu.Components.Popups 0.1
23import "../Components/ListItems"
23import "../Components/ListItems"
24+import "../Components/"
24
25
25Item {
26Item {
26 id: root
27 id: root
@@ -31,7 +32,7 @@ Item {
31 property bool inverted: true
32 property bool inverted: true
32 property bool dragging: false
33 property bool dragging: false
33 property bool moving: launcherListView.moving || launcherListView.flicking
34 property bool moving: launcherListView.moving || launcherListView.flicking
34− property bool preventHiding: moving || dndArea.draggedIndex >= 0 || dndArea.quickListPopover !== null || dndArea.pressed
35+ property bool preventHiding: moving || dndArea.draggedIndex >= 0 || quickList.state === "open" || dndArea.pressed
35 property int highlightIndex: -1
36 property int highlightIndex: -1
36
37
37 signal applicationSelected(string appId)
38 signal applicationSelected(string appId)
@@ -278,7 +279,6 @@ Item {
278 property bool postDragging: false
279 property bool postDragging: false
279 property int startX
280 property int startX
280 property int startY
281 property int startY
281− property var quickListPopover: null
282
282
283 onPressed: {
283 onPressed: {
284 selectedItem = launcherListView.itemAt(mouseX, mouseY + launcherListView.realContentY)
284 selectedItem = launcherListView.itemAt(mouseX, mouseY + launcherListView.realContentY)
@@ -357,11 +357,10 @@ Item {
357 draggedIndex = Math.floor((mouseY + launcherListView.realContentY) / launcherListView.realItemHeight);
357 draggedIndex = Math.floor((mouseY + launcherListView.realContentY) / launcherListView.realItemHeight);
358
358
359 // Opening QuickList
359 // Opening QuickList
360− var quickListModel = launcherListView.model.get(draggedIndex).quickList
360+ quickList.item = selectedItem;
361− var quickListAppId = launcherListView.model.get(draggedIndex).appId
361+ quickList.model = launcherListView.model.get(draggedIndex).quickList;
362−
362+ quickList.appId = launcherListView.model.get(draggedIndex).appId;
363− quickListPopover = PopupUtils.open(popoverComponent, selectedItem,
363+ quickList.state = "open";
364− {model: quickListModel, appId: quickListAppId})
365
364
366 launcherListView.interactive = false
365 launcherListView.interactive = false
367
366
@@ -387,7 +386,7 @@ Item {
387 var distance = Math.max(Math.abs(mouseX - startX), Math.abs(mouseY - startY))
386 var distance = Math.max(Math.abs(mouseX - startX), Math.abs(mouseY - startY))
388 if (!preDragging && distance > units.gu(1.5)) {
387 if (!preDragging && distance > units.gu(1.5)) {
389 preDragging = true;
388 preDragging = true;
390− PopupUtils.close(quickListPopover)
389+ quickList.state = "";
391 }
390 }
392 if (distance > launcherListView.itemHeight) {
391 if (distance > launcherListView.itemHeight) {
393 selectedItem.dragging = true
392 selectedItem.dragging = true
@@ -482,53 +481,95 @@ Item {
482 }
481 }
483 }
482 }
484
483
485− Component {
484+ UbuntuShapeForItem {
486− id: popoverComponent
485+ id: quickListShape
486+ objectName: "quickListShape"
487+ anchors.fill: quickList
488+ opacity: quickList.state === "open" ? 0.8 : 0
489+ visible: opacity > 0
490+ rotation: root.rotation
491+
492+ Behavior on opacity {
493+ UbuntuNumberAnimation {}
494+ }
495+
496+ image: quickList
497+
498+ Image {
499+ anchors {
500+ right: parent.left
501+ rightMargin: -units.dp(4)
502+ verticalCenter: parent.verticalCenter
503+ verticalCenterOffset: -quickList.offset
504+ }
505+ height: units.gu(1)
506+ width: units.gu(2)
507+ source: "graphics/quicklist_tooltip.png"
508+ rotation: 90
509+ }
510+
511+ InverseMouseArea {
512+ anchors.fill: parent
513+ enabled: quickList.state == "open"
514+ onClicked: {
515+ quickList.state = ""
516+ }
517+ }
518+
519+ }
520+
521+ Rectangle {
522+ id: quickList
523+ objectName: "quickList"
524+ color: "#221e1c"
525+ width: units.gu(30)
526+ height: quickListColumn.height
527+ anchors {
528+ left: root.inverted ? undefined : parent.right
529+ right: root.inverted ? parent.left : undefined
530+ margins: units.gu(1)
487
531
488− Popover {
532+ }
489− id: popover
533+ y: itemCenter - (height / 2) + offset
490− property var model
534+ rotation: root.rotation
491− property string appId
492− contentWidth: quickListColumn.width
493− foregroundStyle: QuicklistForegroundStyle {}
494
535
495− // FIXME: There's a bug in the Popover positioning that it covers the item in case it is rotated.
536+ property var model
496− // https://bugs.launchpad.net/ubuntu-ui-toolkit/+bug/1204470
537+ property string appId
497− // For now, let's move the Popover around with callerMargin.
538+ property var item
498− // Remove popupMargin once the bug is fixed.
499− property int popupMargin: root.inverted ? launcherListView.itemHeight : 0;
500
539
501− callerMargin: units.gu(1) + popupMargin
540+ // internal
541+ property int itemCenter: item ? root.mapFromItem(quickList.item).y + (item.height / 2) : units.gu(1)
542+ property int offset: itemCenter + (height/2) + units.gu(1) > parent.height ? -itemCenter - (height/2) - units.gu(1) + parent.height :
543+ itemCenter - (height/2) < units.gu(1) ? (height/2) - itemCenter + units.gu(1) : 0
502
544
503− Column {
545+ Column {
504− id: quickListColumn
546+ id: quickListColumn
505− width: units.gu(30)
547+ width: parent.width
506− height: childrenRect.height
548+ height: childrenRect.height
507
549
508− Repeater {
550+ Repeater {
509− id: popoverRepeater
551+ id: popoverRepeater
510− model: popover.model
552+ model: quickList.model
511
553
512− ListItems.Standard {
554+ ListItems.Standard {
513− objectName: "quickListEntry" + index
555+ objectName: "quickListEntry" + index
514− text: (model.clickable ? "" : "<b>") + model.label + (model.clickable ? "" : "</b>")
556+ text: (model.clickable ? "" : "<b>") + model.label + (model.clickable ? "" : "</b>")
515− highlightWhenPressed: model.clickable
557+ highlightWhenPressed: model.clickable
516
558
517− // FIXME: This is a workaround for the theme not being context sensitive. I.e. the
559+ // FIXME: This is a workaround for the theme not being context sensitive. I.e. the
518− // ListItems don't know that they are sitting in a themed Popover where the color
560+ // ListItems don't know that they are sitting in a themed Popover where the color
519− // needs to be inverted.
561+ // needs to be inverted.
520− __foregroundColor: Theme.palette.selected.backgroundText
562+ __foregroundColor: Theme.palette.selected.backgroundText
521
563
522− onClicked: {
564+ onClicked: {
523− if (!model.clickable) {
565+ if (!model.clickable) {
524− return;
566+ return;
525− }
526− PopupUtils.close(popover);
527− // Unsetting model to prevent showing changing entries during fading out
528− // that may happen because of triggering an action.
529− LauncherModel.quickListActionInvoked(appId, index);
530− popoverRepeater.model = undefined;
531 }
567 }
568+ quickList.state = "";
569+ // Unsetting model to prevent showing changing entries during fading out
570+ // that may happen because of triggering an action.
571+ LauncherModel.quickListActionInvoked(quickList.appId, index);
572+ quickList.model = undefined;
532 }
573 }
533 }
574 }
534 }
575 }
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+
1unity8 (7.83+13.10.20131016.2-0ubuntu1) saucy; urgency=low
37unity8 (7.83+13.10.20131016.2-0ubuntu1) saucy; urgency=low
2
38
3 [ Loïc Minier ]
39 [ Loïc Minier ]
main.cpp+8−8
@@ -78,14 +78,6 @@ void resolveIconTheme() {
78
78
79int startShell(int argc, const char** argv, void* server)
79int startShell(int argc, const char** argv, void* server)
80{
80{
81− /* Workaround Qt platform integration plugin not advertising itself
82− as having the following capabilities:
83− - QPlatformIntegration::ThreadedOpenGL
84− - QPlatformIntegration::BufferQueueingOpenGL
85− */
86− setenv("QML_FORCE_THREADED_RENDERER", "1", 1);
87− setenv("QML_FIXED_ANIMATION_STEP", "1", 1);
88−
89 const bool isUbuntuMirServer = qgetenv("QT_QPA_PLATFORM") == "ubuntumirserver";
81 const bool isUbuntuMirServer = qgetenv("QT_QPA_PLATFORM") == "ubuntumirserver";
90
82
91 QGuiApplication::setApplicationName("Unity 8");
83 QGuiApplication::setApplicationName("Unity 8");
@@ -186,6 +178,14 @@ int startShell(int argc, const char** argv, void* server)
186
178
187int main(int argc, const char *argv[])
179int main(int argc, const char *argv[])
188{
180{
181+ /* Workaround Qt platform integration plugin not advertising itself
182+ as having the following capabilities:
183+ - QPlatformIntegration::ThreadedOpenGL
184+ - QPlatformIntegration::BufferQueueingOpenGL
185+ */
186+ setenv("QML_FORCE_THREADED_RENDERER", "1", 1);
187+ setenv("QML_FIXED_ANIMATION_STEP", "1", 1);
188+
189 // For ubuntumirserver/ubuntumirclient
189 // For ubuntumirserver/ubuntumirclient
190 if (qgetenv("QT_QPA_PLATFORM").startsWith("ubuntumir")) {
190 if (qgetenv("QT_QPA_PLATFORM").startsWith("ubuntumir")) {
191 setenv("QT_QPA_PLATFORM", "ubuntumirserver", 1);
191 setenv("QT_QPA_PLATFORM", "ubuntumirserver", 1);
plugins/Unity/Indicators/CMakeLists.txt+1
@@ -36,6 +36,7 @@ set(IndicatorsQML_SOURCES
36 unitymenumodelcache.cpp
36 unitymenumodelcache.cpp
37 unitymenumodelstack.cpp
37 unitymenumodelstack.cpp
38 visibleindicatorsmodel.cpp
38 visibleindicatorsmodel.cpp
39+ timeformatter.cpp
39)
40)
40add_definitions(-DUNITYINDICATORS_LIBRARY)
41add_definitions(-DUNITYINDICATORS_LIBRARY)
41
42
plugins/Unity/Indicators/Messaging/qml/MessageMenuItemFactory.qml+3−4
@@ -22,7 +22,6 @@ import QtQuick 2.0
22import Ubuntu.Components 0.1
22import Ubuntu.Components 0.1
23import Unity.Indicators 0.1 as Indicators
23import Unity.Indicators 0.1 as Indicators
24import QMenuModel 0.1 as QMenuModel
24import QMenuModel 0.1 as QMenuModel
25−import "utils.js" as Utils
26
25
27Indicators.BaseMenuItem {
26Indicators.BaseMenuItem {
28 id: menuItem
27 id: menuItem
@@ -65,7 +64,7 @@ Indicators.BaseMenuItem {
65 SimpleTextMessage {
64 SimpleTextMessage {
66 // text
65 // text
67 title: menu && menu.label ? menu.label : ""
66 title: menu && menu.label ? menu.label : ""
68− time: menu ? Utils.formatDate(menu.ext.xCanonicalTime) : ""
67+ time: menu ? menu.ext.xCanonicalTime : 0
69 message: menu && menu.ext.xCanonicalText ? menu.ext.xCanonicalText : ""
68 message: menu && menu.ext.xCanonicalText ? menu.ext.xCanonicalText : ""
70 // icons
69 // icons
71 avatar: menu && menu.ext.icon !== undefined ? menu.ext.icon : "qrc:/indicators/artwork/messaging/default_contact.png"
70 avatar: menu && menu.ext.icon !== undefined ? menu.ext.icon : "qrc:/indicators/artwork/messaging/default_contact.png"
@@ -90,7 +89,7 @@ Indicators.BaseMenuItem {
90
89
91 // text
90 // text
92 title: menu && menu.label ? menu.label : ""
91 title: menu && menu.label ? menu.label : ""
93− time: menu ? Utils.formatDate(menu.ext.xCanonicalTime) : ""
92+ time: menu ? menu.ext.xCanonicalTime : 0
94 message: menu && menu.ext.xCanonicalText ? menu.ext.xCanonicalText : ""
93 message: menu && menu.ext.xCanonicalText ? menu.ext.xCanonicalText : ""
95 replyButtonText: actionsDescription && actionsDescription[0].label ? actionsDescription[0].label : "Send"
94 replyButtonText: actionsDescription && actionsDescription[0].label ? actionsDescription[0].label : "Send"
96 // icons
95 // icons
@@ -126,7 +125,7 @@ Indicators.BaseMenuItem {
126
125
127 // text
126 // text
128 title: menu && menu.label ? menu.label : ""
127 title: menu && menu.label ? menu.label : ""
129− time: menu ? Utils.formatDate(menu.ext.xCanonicalTime) : ""
128+ time: menu ? menu.ext.xCanonicalTime : ""
130 message: menu && menu.ext.xCanonicalText ? menu.ext.xCanonicalText : ""
129 message: menu && menu.ext.xCanonicalText ? menu.ext.xCanonicalText : ""
131 actionButtonText: actionsDescription && actionsDescription[0].label ? actionsDescription[0].label : "Call back"
130 actionButtonText: actionsDescription && actionsDescription[0].label ? actionsDescription[0].label : "Call back"
132 replyButtonText: actionsDescription && actionsDescription[1].label ? actionsDescription[1].label : "Send"
131 replyButtonText: actionsDescription && actionsDescription[1].label ? actionsDescription[1].label : "Send"
plugins/Unity/Indicators/Messaging/qml/SimpleTextMessage.qml+9−2
@@ -20,19 +20,20 @@
20
20
21import QtQuick 2.0
21import QtQuick 2.0
22import Ubuntu.Components 0.1
22import Ubuntu.Components 0.1
23+import Unity.Indicators 0.1
23
24
24HeroMessage {
25HeroMessage {
25 id: __heroMessage
26 id: __heroMessage
26
27
27 property string title: ""
28 property string title: ""
28− property string time: ""
29+ property var time
29 property string message: ""
30 property string message: ""
30
31
31 property alias footer: footerLoader.sourceComponent
32 property alias footer: footerLoader.sourceComponent
32
33
33 expandedHeight: fullMessage.y + fullMessage.height + units.gu(2)
34 expandedHeight: fullMessage.y + fullMessage.height + units.gu(2)
34 heroMessageHeader.titleText.text: title
35 heroMessageHeader.titleText.text: title
35− heroMessageHeader.subtitleText.text: time
36+ heroMessageHeader.subtitleText.text: timeFormatter.timeString
36 heroMessageHeader.bodyText.text: message
37 heroMessageHeader.bodyText.text: message
37
38
38 Item {
39 Item {
@@ -49,6 +50,12 @@ HeroMessage {
49 opacity: 0.0
50 opacity: 0.0
50 enabled: false
51 enabled: false
51
52
53+ TimeFormatter {
54+ id: timeFormatter
55+ time: __heroMessage.time
56+ format: "hh:mm - MMM dd"
57+ }
58+
52 Label {
59 Label {
53 id: bodyText
60 id: bodyText
54 anchors {
61 anchors {
plugins/Unity/Indicators/Messaging/qml/SnapDecision.qml+9−3
@@ -21,13 +21,13 @@
21import QtQuick 2.0
21import QtQuick 2.0
22import Ubuntu.Components 0.1
22import Ubuntu.Components 0.1
23import Unity.Indicators 0.1 as Indicators
23import Unity.Indicators 0.1 as Indicators
24−import "utils.js" as Utils
24+import Unity.Indicators 0.1
25
25
26HeroMessage {
26HeroMessage {
27 id: snapDecision
27 id: snapDecision
28
28
29 property string title: ""
29 property string title: ""
30− property string time: ""
30+ property var time
31 property string message: ""
31 property string message: ""
32
32
33 property bool activateEnabled: true
33 property bool activateEnabled: true
@@ -41,7 +41,7 @@ HeroMessage {
41 heroMessageHeader.titleText.text: title
41 heroMessageHeader.titleText.text: title
42 heroMessageHeader.subtitleText.text: message
42 heroMessageHeader.subtitleText.text: message
43 heroMessageHeader.subtitleText.color: "#e8e1d0"
43 heroMessageHeader.subtitleText.color: "#e8e1d0"
44− heroMessageHeader.bodyText.text: time
44+ heroMessageHeader.bodyText.text: timeFormatter.timeString
45 heroMessageHeader.bodyText.color: "#8f8f88"
45 heroMessageHeader.bodyText.color: "#8f8f88"
46
46
47 signal activate
47 signal activate
@@ -59,6 +59,12 @@ HeroMessage {
59 height: units.gu(4)
59 height: units.gu(4)
60 opacity: 0.0
60 opacity: 0.0
61
61
62+ TimeFormatter {
63+ id: timeFormatter
64+ time: snapDecision.time
65+ format: "hh:mm - MMM dd"
66+ }
67+
62 Button {
68 Button {
63 text: "Message"
69 text: "Message"
64 color: "#bababa"
70 color: "#bababa"
plugins/Unity/Indicators/Messaging/qml/utils.jsdeleted−27
@@ -1,27 +0,0 @@
1−/*
2− * Copyright 2013 Canonical Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU Lesser General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU Lesser General Public License for more details.
12− *
13− * You should have received a copy of the GNU Lesser General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Authors:
17− * Renato Araujo Oliveira Filho <renato@canonical.com>
18− * Olivier Tilloy <olivier.tilloy@canonical.com>
19− */
20−
21−.pragma library
22−
23−function formatDate(timestamp) {
24− // timestamp is in microseconds since epoch
25− var date = new Date(timestamp / 1000);
26− return Qt.formatDateTime(date, "hh:mm - MMM dd");
27−}
plugins/Unity/Indicators/plugin.cpp+2
@@ -33,6 +33,7 @@
33#include "unitymenumodelcache.h"
33#include "unitymenumodelcache.h"
34#include "unitymenumodelstack.h"
34#include "unitymenumodelstack.h"
35#include "visibleindicatorsmodel.h"
35#include "visibleindicatorsmodel.h"
36+#include "timeformatter.h"
36
37
37static QObject* menuModelCacheSingleton(QQmlEngine* engine, QJSEngine* scriptEngine) {
38static QObject* menuModelCacheSingleton(QQmlEngine* engine, QJSEngine* scriptEngine) {
38 Q_UNUSED(engine);
39 Q_UNUSED(engine);
@@ -51,6 +52,7 @@ void Indicators2Plugin::registerTypes(const char *uri)
51 qmlRegisterType<RootActionState>(uri, 0, 1, "RootActionState");
52 qmlRegisterType<RootActionState>(uri, 0, 1, "RootActionState");
52 qmlRegisterType<ModelPrinter>(uri, 0, 1, "ModelPrinter");
53 qmlRegisterType<ModelPrinter>(uri, 0, 1, "ModelPrinter");
53 qmlRegisterType<VisibleIndicatorsModel>(uri, 0, 1, "VisibleIndicatorsModel");
54 qmlRegisterType<VisibleIndicatorsModel>(uri, 0, 1, "VisibleIndicatorsModel");
55+ qmlRegisterType<TimeFormatter>(uri, 0, 1, "TimeFormatter");
54
56
55 qmlRegisterSingletonType<UnityMenuModelCache>(uri, 0, 1, "UnityMenuModelCache", menuModelCacheSingleton);
57 qmlRegisterSingletonType<UnityMenuModelCache>(uri, 0, 1, "UnityMenuModelCache", menuModelCacheSingleton);
56
58
plugins/Unity/Indicators/qml/IndicatorPage.qml+27−6
@@ -34,11 +34,36 @@ IndicatorBase {
34 Indicators.UnityMenuModelStack {
34 Indicators.UnityMenuModelStack {
35 id: menuStack
35 id: menuStack
36 head: contentActive ? main.menuModel : null
36 head: contentActive ? main.menuModel : null
37+
38+ property var rootMenu: null
39+
40+ onTailChanged: {
41+ if (!tail) {
42+ rootMenu = null;
43+ } else if (rootMenu != tail) {
44+ if (tail.get(0, "type") === rootMenuType) {
45+ rootMenu = menuStack.tail.submenu(0);
46+ push(rootMenu, 0);
47+ } else {
48+ rootMenu = null;
49+ }
50+ }
51+ }
52+ }
53+
54+ Connections {
55+ target: menuStack.tail
56+ onRowsInserted: {
57+ if (menuStack.rootMenu !== menuStack.tail && menuStack.tail.get(0, "type") === rootMenuType) {
58+ menuStack.rootMenu = menuStack.tail.submenu(0);
59+ menuStack.push(menuStack.rootMenu, 0);
60+ }
61+ }
37 }
62 }
38
63
39 ListView {
64 ListView {
40 id: mainMenu
65 id: mainMenu
41− model: menuStack.tail ? menuStack.tail : null
66+ model: menuStack.rootMenu
42
67
43 anchors {
68 anchors {
44 fill: parent
69 fill: parent
@@ -102,7 +127,7 @@ IndicatorBase {
102
127
103 Loader {
128 Loader {
104 id: loader
129 id: loader
105− asynchronous: true
130+ asynchronous: false
106
131
107 property int modelIndex: index
132 property int modelIndex: index
108
133
@@ -114,10 +139,6 @@ IndicatorBase {
114 sourceComponent: factory.load(model)
139 sourceComponent: factory.load(model)
115
140
116 onLoaded: {
141 onLoaded: {
117− if (model.type === rootMenuType) {
118− menuStack.push(mainMenu.model.submenu(index));
119− }
120−
121 if (item.hasOwnProperty("menuSelected")) {
142 if (item.hasOwnProperty("menuSelected")) {
122 item.menuSelected = Qt.binding(function() { return mainMenu.selectedIndex == index; });
143 item.menuSelected = Qt.binding(function() { return mainMenu.selectedIndex == index; });
123 item.selectMenu.connect(function() { mainMenu.selectedIndex = index; });
144 item.selectMenu.connect(function() { mainMenu.selectedIndex = index; });
plugins/Unity/Indicators/qml/SliderMenuItem.qml+3−2
@@ -51,12 +51,12 @@ FramedMenuItem {
51
51
52 control: Item {
52 control: Item {
53 id: row
53 id: row
54− width: sliderMenu.text ? units.gu(20) : menuItem.width - (2 * menuItem.__contentsMargins)
54+ width: menuItem.text ? units.gu(20) : menuItem.width - (2 * menuItem.__contentsMargins)
55 height: slider.height
55 height: slider.height
56
56
57 Image {
57 Image {
58 id: leftImage
58 id: leftImage
59− visible: source != ""
59+ visible: status === Image.Ready
60 anchors.left: row.left
60 anchors.left: row.left
61 anchors.verticalCenter: row.verticalCenter
61 anchors.verticalCenter: row.verticalCenter
62 height: units.gu(4)
62 height: units.gu(4)
@@ -96,6 +96,7 @@ FramedMenuItem {
96
96
97 Image {
97 Image {
98 id: rightImage
98 id: rightImage
99+ visible: status === Image.Ready
99 anchors.right: row.right
100 anchors.right: row.right
100 anchors.verticalCenter: row.verticalCenter
101 anchors.verticalCenter: row.verticalCenter
101 height: units.gu(4)
102 height: units.gu(4)
plugins/Unity/Indicators/timeformatter.cppnew file+172
@@ -0,0 +1,172 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Author: Lars Uebernickel <lars.uebernickel@canonical.com>
17+ */
18+
19+#include "timeformatter.h"
20+
21+#include <gio/gio.h>
22+#include <QDateTime>
23+
24+struct TimeFormatterPrivate
25+{
26+ TimeFormatter *formatter;
27+
28+ QString format;
29+ QString timeString;
30+ qint64 time;
31+
32+ GDBusConnection *system_bus;
33+ guint subscription_id;
34+ GCancellable *cancellable;
35+};
36+
37+static void
38+timedate1_properties_changed (GDBusConnection *connection,
39+ const gchar *sender_name,
40+ const gchar *object_path,
41+ const gchar *interface_name,
42+ const gchar *signal_name,
43+ GVariant *parameters,
44+ gpointer user_data)
45+{
46+ Q_UNUSED(connection);
47+ Q_UNUSED(sender_name);
48+ Q_UNUSED(object_path);
49+ Q_UNUSED(interface_name);
50+ Q_UNUSED(signal_name);
51+
52+ TimeFormatterPrivate *priv = (TimeFormatterPrivate *)user_data;
53+ GVariant *changed;
54+ GVariantIter *iter;
55+ const gchar *name;
56+
57+ if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sa{sv}as)")))
58+ return;
59+
60+ g_variant_get (parameters, "(s@a{sv}as)", NULL, &changed, &iter);
61+
62+ if (g_variant_lookup (changed, "Timezone", "s", NULL)) {
63+ priv->formatter->update();
64+ }
65+ else {
66+ while (g_variant_iter_next (iter, "&s", &name)) {
67+ if (g_str_equal (name, "Timezone")) {
68+ priv->formatter->update();
69+ break;
70+ }
71+ }
72+ }
73+
74+ g_variant_unref (changed);
75+ g_variant_iter_free (iter);
76+}
77+
78+static void
79+got_bus(GObject *object, GAsyncResult *result, gpointer user_data)
80+{
81+ Q_UNUSED(object);
82+
83+ TimeFormatterPrivate *priv = (TimeFormatterPrivate *)user_data;
84+ GError *error = NULL;
85+
86+ priv->system_bus = g_bus_get_finish (result, &error);
87+ if (priv->system_bus == NULL) {
88+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
89+ qWarning("TimeFormatter: cannot connect to the system bus: %s", error->message);
90+ g_error_free (error);
91+ return;
92+ }
93+
94+ /* Listen to the PropertiesChanged on the org.freedesktop.timedate1
95+ * interface from any sender. In practice, this signal will only be sent
96+ * from timedated (we can trust other processes on the system bus to behave
97+ * nicely). That way, we don't have to watch timedated's well-known name
98+ * and keep the process alive.
99+ */
100+ priv->subscription_id = g_dbus_connection_signal_subscribe (priv->system_bus,
101+ NULL, /* sender */
102+ "org.freedesktop.DBus.Properties",
103+ "PropertiesChanged",
104+ NULL,
105+ "org.freedesktop.timedate1",
106+ G_DBUS_SIGNAL_FLAGS_NONE,
107+ timedate1_properties_changed,
108+ priv, NULL);
109+}
110+
111+TimeFormatter::TimeFormatter(QObject *parent): QObject(parent)
112+{
113+ priv = new TimeFormatterPrivate;
114+ priv->formatter = this;
115+ priv->time = 0;
116+ priv->format = "yyyy-MM-dd hh:mm";
117+ priv->system_bus = NULL;
118+ priv->subscription_id = 0;
119+ priv->cancellable = g_cancellable_new ();
120+
121+ g_bus_get (G_BUS_TYPE_SYSTEM, priv->cancellable, got_bus, priv);
122+}
123+
124+TimeFormatter::~TimeFormatter()
125+{
126+ if (priv->system_bus) {
127+ g_dbus_connection_signal_unsubscribe (priv->system_bus, priv->subscription_id);
128+ g_object_unref (priv->system_bus);
129+ }
130+
131+ g_cancellable_cancel (priv->cancellable);
132+ g_object_unref (priv->cancellable);
133+}
134+
135+QString TimeFormatter::format() const
136+{
137+ return priv->format;
138+}
139+
140+QString TimeFormatter::timeString() const
141+{
142+ return priv->timeString;
143+}
144+
145+qint64 TimeFormatter::time() const
146+{
147+ return priv->time;
148+}
149+
150+void TimeFormatter::setFormat(const QString &format)
151+{
152+ if (priv->format != format) {
153+ priv->format = format;
154+ Q_EMIT formatChanged(priv->format);
155+ update();
156+ }
157+}
158+
159+void TimeFormatter::setTime(qint64 time)
160+{
161+ if (priv->time != time) {
162+ priv->time = time;
163+ Q_EMIT timeChanged(priv->time);
164+ update();
165+ }
166+}
167+
168+void TimeFormatter::update()
169+{
170+ priv->timeString = QDateTime::fromMSecsSinceEpoch(priv->time / 1000).toString(priv->format);
171+ Q_EMIT timeStringChanged(priv->timeString);
172+}
plugins/Unity/Indicators/timeformatter.hnew file+54
@@ -0,0 +1,54 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Author: Lars Uebernickel <lars.uebernickel@canonical.com>
17+ */
18+
19+#ifndef TIME_FORMATTER_H
20+#define TIME_FORMATTER_H
21+
22+#include "unityindicatorsglobal.h"
23+#include <QObject>
24+
25+class UNITYINDICATORS_EXPORT TimeFormatter : public QObject
26+{
27+ Q_OBJECT
28+ Q_PROPERTY(QString format READ format WRITE setFormat NOTIFY formatChanged)
29+ Q_PROPERTY(QString timeString READ timeString NOTIFY timeStringChanged)
30+ Q_PROPERTY(qint64 time READ time WRITE setTime NOTIFY timeChanged)
31+
32+public:
33+ TimeFormatter(QObject *parent = 0);
34+ ~TimeFormatter();
35+
36+ QString format() const;
37+ QString timeString() const;
38+ qint64 time() const;
39+
40+ void setFormat(const QString &format);
41+ void setTime(qint64 time);
42+
43+ void update();
44+
45+Q_SIGNALS:
46+ void formatChanged(const QString &format);
47+ void timeStringChanged(const QString &timeString);
48+ void timeChanged(qint64 time);
49+
50+private:
51+ struct TimeFormatterPrivate *priv;
52+};
53+
54+#endif
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
2CODE_DIR=shell
2CODE_DIR=shell
3USER=phablet
3USER=phablet
4USER_ID=32011
4USER_ID=32011
@@ -17,6 +17,8 @@ SUDO="echo $PASSWORD | sudo -S"
17NUM_JOBS='$(( `grep -c ^processor /proc/cpuinfo` + 1 ))'
17NUM_JOBS='$(( `grep -c ^processor /proc/cpuinfo` + 1 ))'
18FLIPPED=false
18FLIPPED=false
19CHROOT_PREFIX="/data/ubuntu"
19CHROOT_PREFIX="/data/ubuntu"
20+SSH_WAS_STARTED=0
21+SSH_STARTED=0
20
22
21usage() {
23usage() {
22 echo "usage: run_on_device [OPTIONS]\n"
24 echo "usage: run_on_device [OPTIONS]\n"
@@ -32,7 +34,15 @@ usage() {
32 exit 1
34 exit 1
33}
35}
34
36
37+start_ssh() {
38+ if [ $SSH_STARTED -eq 0 ]; then
39+ exec_with_adb initctl start ssh
40+ SSH_STARTED=1
41+ fi
42+}
43+
35exec_with_ssh() {
44exec_with_ssh() {
45+ start_ssh
36 ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -t $USER@$TARGET_IP -p $TARGET_SSH_PORT sudo -u $USER -i bash -ic \"$@\"
46 ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -t $USER@$TARGET_IP -p $TARGET_SSH_PORT sudo -u $USER -i bash -ic \"$@\"
37}
47}
38
48
@@ -52,7 +62,7 @@ adb_root() {
52install_ssh_key() {
62install_ssh_key() {
53 ssh-keygen -R $TARGET_IP
63 ssh-keygen -R $TARGET_IP
54 HOME_DIR="${CHROOT_PREFIX}/home/phablet"
64 HOME_DIR="${CHROOT_PREFIX}/home/phablet"
55− adb push ~/.ssh/phone_rsa.pub $HOME_DIR/.ssh/authorized_keys
65+ adb push ~/.ssh/id_rsa.pub $HOME_DIR/.ssh/authorized_keys
56 adb shell chown $USER_ID:$USER_ID $HOME_DIR/.ssh
66 adb shell chown $USER_ID:$USER_ID $HOME_DIR/.ssh
57 adb shell chown $USER_ID:$USER_ID $HOME_DIR/.ssh/authorized_keys
67 adb shell chown $USER_ID:$USER_ID $HOME_DIR/.ssh/authorized_keys
58 adb shell chmod 700 $HOME_DIR/.ssh
68 adb shell chmod 700 $HOME_DIR/.ssh
@@ -65,8 +75,6 @@ setup_adb_forwarding() {
65}
75}
66
76
67install_dependencies() {
77install_dependencies() {
68− exec_with_adb rm /etc/init/ssh.override
69− exec_with_adb service ssh start
70 exec_with_adb apt-get update
78 exec_with_adb apt-get update
71 exec_with_ssh $SUDO apt-get -y install build-essential rsync bzr ccache gdb ninja-build devscripts equivs
79 exec_with_ssh $SUDO apt-get -y install build-essential rsync bzr ccache gdb ninja-build devscripts equivs
72}
80}
@@ -74,6 +82,7 @@ install_dependencies() {
74sync_code() {
82sync_code() {
75 [ -e .bzr ] && bzr export --uncommitted --format=dir /tmp/$CODE_DIR
83 [ -e .bzr ] && bzr export --uncommitted --format=dir /tmp/$CODE_DIR
76 [ -e .git ] && git checkout-index -a -f --prefix=/tmp/$CODE_DIR/
84 [ -e .git ] && git checkout-index -a -f --prefix=/tmp/$CODE_DIR/
85+ start_ssh
77 rsync -crlOzv --delete --exclude builddir -e "ssh -p $TARGET_SSH_PORT -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no" /tmp/$CODE_DIR/ $USER@$TARGET_IP:$CODE_DIR/
86 rsync -crlOzv --delete --exclude builddir -e "ssh -p $TARGET_SSH_PORT -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no" /tmp/$CODE_DIR/ $USER@$TARGET_IP:$CODE_DIR/
78 rm -rf /tmp/$CODE_DIR
87 rm -rf /tmp/$CODE_DIR
79}
88}
@@ -96,7 +105,9 @@ run() {
96 fi
105 fi
97
106
98 exec_with_ssh "stop unity8"
107 exec_with_ssh "stop unity8"
108+ exec_with_ssh "start maliit-server"
99 exec_with_ssh "cd $CODE_DIR/ && ./run $ARGS -- $RUN_OPTIONS"
109 exec_with_ssh "cd $CODE_DIR/ && ./run $ARGS -- $RUN_OPTIONS"
110+ exec_with_ssh "stop maliit-server"
100 exec_with_ssh "start unity8"
111 exec_with_ssh "start unity8"
101}
112}
102
113
@@ -130,6 +141,12 @@ if [ "`adb shell 'grep -q Ubuntu /etc/lsb-release 2> /dev/null && echo -n FLIPPE
130 fi
141 fi
131fi
142fi
132
143
144+status_output=$(exec_with_adb initctl status ssh)
145+if [[ $status_output == "ssh start/running, process "* ]]; then
146+ SSH_WAS_STARTED=1
147+fi
148+SSH_STARTED=$SSH_WAS_STARTED
149+
133if $SETUP; then
150if $SETUP; then
134 echo "Setting up environment for building shell.."
151 echo "Setting up environment for building shell.."
135 install_ssh_key
152 install_ssh_key
@@ -144,3 +161,6 @@ else
144 run
161 run
145fi
162fi
146
163
164+if [ $SSH_WAS_STARTED -eq 0 ]; then
165+ exec_with_adb initctl stop ssh
166+fi
tests/autopilot/unity8/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 @@
16
16
17#include "fake_preview.h"
17#include "fake_preview.h"
18
18
19+Preview::Preview(QObject *parent): QObject(parent)
20+{
21+}
22+
19QString Preview::rendererName() const
23QString Preview::rendererName() const
20{
24{
21− return "foo";
25+ return "generic-preview";
22}
26}
23
27
24QString Preview::title() const
28QString Preview::title() const
@@ -61,3 +65,7 @@ void Preview::execute(const QString& actionId, const QHash<QString, QVariant>& h
61 Q_UNUSED(actionId);
65 Q_UNUSED(actionId);
62 Q_UNUSED(hints);
66 Q_UNUSED(hints);
63}
67}
68+
69+void Preview::cancelAction()
70+{
71+}
tests/mocks/Unity/fake_preview.h+2
@@ -51,6 +51,8 @@ public:
51
51
52 Q_INVOKABLE void execute(const QString& actionId, const QHash<QString, QVariant>& hints);
52 Q_INVOKABLE void execute(const QString& actionId, const QHash<QString, QVariant>& hints);
53
53
54+ Q_INVOKABLE void cancelAction();
55+
54Q_SIGNALS:
56Q_SIGNALS:
55 void previewChanged();
57 void previewChanged();
56};
58};
tests/mocks/Unity/fake_scope.cpp+25−4
@@ -14,13 +14,11 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
15 */
16
16
17−// Self
18#include "fake_scope.h"
17#include "fake_scope.h"
19−
20−#include <dee.h>
18+#include "fake_preview.h"
21#include "paths.h"
19#include "paths.h"
22
20
23−#include "fake_preview.h"
21+#include <dee.h>
24
22
25static DeeModel* create_categories_model(unsigned category_count);
23static DeeModel* create_categories_model(unsigned category_count);
26static DeeModel* create_results_model(unsigned category_count, unsigned result_count);
24static DeeModel* create_results_model(unsigned category_count, unsigned result_count);
@@ -39,6 +37,13 @@ Scope::Scope(QObject* parent)
39 m_categories->setResultModel(results_model);
37 m_categories->setResultModel(results_model);
40 m_categories->setModel(create_categories_model(4));
38 m_categories->setModel(create_categories_model(4));
41 m_results->setModel(results_model);
39 m_results->setModel(results_model);
40+
41+ m_timer.setInterval(1000);
42+ m_timer.setSingleShot(true);
43+ connect(&m_timer, &QTimer::timeout, [this]() {
44+ Preview *p = new Preview(this);
45+ Q_EMIT previewReady(p);
46+ });
42}
47}
43
48
44Scope::Scope(QString const& id, QString const& name, bool visible, QObject* parent)
49Scope::Scope(QString const& id, QString const& name, bool visible, QObject* parent)
@@ -55,6 +60,13 @@ Scope::Scope(QString const& id, QString const& name, bool visible, QObject* pare
55 m_categories->setResultModel(results_model);
60 m_categories->setResultModel(results_model);
56 m_categories->setModel(create_categories_model(4));
61 m_categories->setModel(create_categories_model(4));
57 m_results->setModel(results_model);
62 m_results->setModel(results_model);
63+
64+ m_timer.setInterval(1000);
65+ m_timer.setSingleShot(true);
66+ connect(&m_timer, &QTimer::timeout, [this]() {
67+ Preview *p = new Preview(this);
68+ Q_EMIT previewReady(p);
69+ });
58}
70}
59
71
60QString Scope::id() const {
72QString Scope::id() const {
@@ -168,6 +180,8 @@ void Scope::activate(const QVariant &uri, const QVariant &icon_hint, const QVari
168 Q_UNUSED(comment);
180 Q_UNUSED(comment);
169 Q_UNUSED(dnd_uri);
181 Q_UNUSED(dnd_uri);
170 Q_UNUSED(metadata);
182 Q_UNUSED(metadata);
183+
184+ m_timer.start();
171}
185}
172
186
173void Scope::preview(const QVariant &uri, const QVariant &icon_hint, const QVariant &category,
187void Scope::preview(const QVariant &uri, const QVariant &icon_hint, const QVariant &category,
@@ -183,6 +197,8 @@ void Scope::preview(const QVariant &uri, const QVariant &icon_hint, const QVaria
183 Q_UNUSED(comment);
197 Q_UNUSED(comment);
184 Q_UNUSED(dnd_uri);
198 Q_UNUSED(dnd_uri);
185 Q_UNUSED(metadata);
199 Q_UNUSED(metadata);
200+
201+ m_timer.start();
186}
202}
187
203
188static const gchar * categories_model_schema[] = {
204static const gchar * categories_model_schema[] = {
@@ -248,3 +264,8 @@ DeeModel* create_results_model(unsigned category_count, unsigned result_count) {
248 }
264 }
249 return results_model;
265 return results_model;
250}
266}
267+
268+void Scope::cancelActivation()
269+{
270+ m_timer.stop();
271+}
tests/mocks/Unity/fake_scope.h+7−2
@@ -17,10 +17,11 @@
17#ifndef FAKE_SCOPE_H
17#ifndef FAKE_SCOPE_H
18#define FAKE_SCOPE_H
18#define FAKE_SCOPE_H
19
19
20−// Qt
21−#include <QObject>
22#include "fake_categories.h"
20#include "fake_categories.h"
23
21
22+#include <QObject>
23+#include <QDebug>
24+
24class Preview;
25class Preview;
25
26
26class Scope : public QObject
27class Scope : public QObject
@@ -78,6 +79,8 @@ public:
78 const QVariant &result_type, const QVariant &mimetype, const QVariant &title,
79 const QVariant &result_type, const QVariant &mimetype, const QVariant &title,
79 const QVariant &comment, const QVariant &dnd_uri, const QVariant &metadata);
80 const QVariant &comment, const QVariant &dnd_uri, const QVariant &metadata);
80
81
82+ Q_INVOKABLE void cancelActivation();
83+
81Q_SIGNALS:
84Q_SIGNALS:
82 void idChanged(const QString&);
85 void idChanged(const QString&);
83 void nameChanged(const QString&);
86 void nameChanged(const QString&);
@@ -115,6 +118,8 @@ protected:
115
118
116 Categories* m_categories;
119 Categories* m_categories;
117 DeeListModel* m_results;
120 DeeListModel* m_results;
121+
122+ QTimer m_timer;
118};
123};
119
124
120#endif // FAKE_SCOPE_H
125#endif // FAKE_SCOPE_H
tests/mocks/Unity/fake_unity_plugin.cpp+3
@@ -22,6 +22,7 @@
22// local
22// local
23#include "fake_scopes.h"
23#include "fake_scopes.h"
24#include "fake_categories.h"
24#include "fake_categories.h"
25+#include "fake_preview.h"
25#include "categoryresults.h"
26#include "categoryresults.h"
26
27
27// External
28// External
@@ -37,8 +38,10 @@ void FakeUnityPlugin::registerTypes(const char *uri)
37
38
38 Q_ASSERT(uri == QLatin1String("Unity"));
39 Q_ASSERT(uri == QLatin1String("Unity"));
39
40
41+ qmlRegisterUncreatableType<Preview>(uri, 0, 1, "Preview", "Can't create Preview object in QML.");
40 qmlRegisterType<Scopes>(uri, 0, 1, "Scopes");
42 qmlRegisterType<Scopes>(uri, 0, 1, "Scopes");
41 qmlRegisterType<Scope>(uri, 0, 1, "Scope");
43 qmlRegisterType<Scope>(uri, 0, 1, "Scope");
42 qmlRegisterType<Categories>(uri, 0, 1, "Categories");
44 qmlRegisterType<Categories>(uri, 0, 1, "Categories");
43 qmlRegisterUncreatableType<CategoryResults>(uri, 0, 1, "CategoryResults", "Can't create");
45 qmlRegisterUncreatableType<CategoryResults>(uri, 0, 1, "CategoryResults", "Can't create");
46+ qmlRegisterType<DeeListModel>(uri, 0, 1, "DeeListModel");
44}
47}
tests/plugins/Unity/Indicators/CMakeLists.txt+1
@@ -30,4 +30,5 @@ run_tests(
30 indicatorsmodeltest
30 indicatorsmodeltest
31 menucontentactivatortest
31 menucontentactivatortest
32 rootactionstatetest
32 rootactionstatetest
33+ timeformattertest
33 )
34 )
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
23import Unity.Test 0.1 as UT
23import Unity.Test 0.1 as UT
24
24
25Item {
25Item {
26+ id: shell
26 width: units.gu(120)
27 width: units.gu(120)
27 height: units.gu(80)
28 height: units.gu(80)
28
29
29 Scopes {
30 Scopes {
30 id: scopes
31 id: scopes
32+
33+ onLoadedChanged: {
34+ genericScopeView.scope = scopes.get(0)
35+ }
36+ }
37+
38+ property Item applicationManager: Item {
39+ signal sideStageFocusedApplicationChanged()
40+ signal mainStageFocusedApplicationChanged()
31 }
41 }
32
42
33 GenericScopeView {
43 GenericScopeView {
@@ -38,44 +48,144 @@ Item {
38 name: "GenericScopeView"
48 name: "GenericScopeView"
39 when: scopes.loaded
49 when: scopes.loaded
40
50
41− function init() {
42− genericScopeView.scope = scopes.get(0)
43− }
44−
45 function test_isCurrent() {
51 function test_isCurrent() {
46 var pageHeader = findChild(genericScopeView, "pageHeader");
52 var pageHeader = findChild(genericScopeView, "pageHeader");
47− var previewLoader = findChild(genericScopeView, "previewLoader");
53+ var previewListView = findChild(genericScopeView, "previewListView");
48 genericScopeView.isCurrent = true
54 genericScopeView.isCurrent = true
49 pageHeader.searchQuery = "test"
55 pageHeader.searchQuery = "test"
50− previewLoader.open = true
56+ previewListView.open = true
51 genericScopeView.isCurrent = false
57 genericScopeView.isCurrent = false
52 tryCompare(pageHeader, "searchQuery", "")
58 tryCompare(pageHeader, "searchQuery", "")
53 tryCompare(genericScopeView, "previewShown", false);
59 tryCompare(genericScopeView, "previewShown", false);
54 }
60 }
55
61
56 function test_showDash() {
62 function test_showDash() {
57− var previewLoader = findChild(genericScopeView, "previewLoader");
63+ var previewListView = findChild(genericScopeView, "previewListView");
58− previewLoader.open = true;
64+ previewListView.open = true;
59 scopes.get(0).showDash();
65 scopes.get(0).showDash();
60 tryCompare(genericScopeView, "previewShown", false);
66 tryCompare(genericScopeView, "previewShown", false);
61 }
67 }
62
68
63 function test_hideDash() {
69 function test_hideDash() {
64− var previewLoader = findChild(genericScopeView, "previewLoader");
70+ var previewListView = findChild(genericScopeView, "previewListView");
65− previewLoader.open = true;
71+ previewListView.open = true;
66 scopes.get(0).hideDash();
72 scopes.get(0).hideDash();
67 tryCompare(genericScopeView, "previewShown", false);
73 tryCompare(genericScopeView, "previewShown", false);
68 }
74 }
69
75
76+ function openPreview() {
77+ var tile = findChild(genericScopeView, "delegate0");
78+ mouseClick(tile, tile.width / 2, tile.height / 2);
79+ var openEffect = findChild(genericScopeView, "openEffect");
80+ tryCompare(openEffect, "gap", 1);
81+ }
82+
83+ function checkArrowPosition(index) {
84+ var tile = findChild(genericScopeView, "delegate" + index);
85+ var tileCenter = tile.x + tile.width/2;
86+ var pointerArrow = findChild(genericScopeView, "pointerArrow");
87+ var pointerArrowCenter = pointerArrow.x + pointerArrow.width/2;
88+ compare(pointerArrowCenter, tileCenter, "Pointer did not move to tile");
89+ }
90+
91+ function closePreview() {
92+ var closePreviewMouseArea = findChild(genericScopeView, "closePreviewMouseArea");
93+ mouseClick(closePreviewMouseArea, closePreviewMouseArea.width / 2, closePreviewMouseArea.height / 2);
94+
95+ var previewListView = findChild(genericScopeView, "previewListView");
96+ tryCompare(previewListView, "open", false);
97+ var openEffect = findChild(genericScopeView, "openEffect");
98+ tryCompare(openEffect, "gap", 0);
99+
100+ var categoryListView = findChild(genericScopeView, "categoryListView");
101+ categoryListView.flick(0, units.gu(200));
102+ tryCompare(categoryListView, "flicking", false);
103+ }
104+
105+ function test_previewOpenClose() {
106+ var previewListView = findChild(genericScopeView, "previewListView");
107+ tryCompare(previewListView, "open", false);
108+
109+ openPreview();
110+
111+ // check for it opening successfully
112+ var currentPreviewItem = findChild(genericScopeView, "previewLoader0");
113+ tryCompareFunction(function() {
114+ var parts = currentPreviewItem.source.toString().split("/");
115+ var name = parts[parts.length - 1];
116+ return name == "DashPreviewPlaceholder.qml";
117+ },
118+ true);
119+ tryCompareFunction(function() {
120+ var parts = currentPreviewItem.source.toString().split("/");
121+ var name = parts[parts.length - 1];
122+ return name == "GenericPreview.qml";
123+ },
124+ true);
125+ tryCompare(currentPreviewItem, "progress", 1);
126+ tryCompare(previewListView, "open", true);
127+
128+ closePreview();
129+ tryCompare(previewListView, "open", false);
130+ }
131+
132+ function test_previewCycleOne() {
133+ var previewListView = findChild(genericScopeView, "previewListView");
134+ tryCompare(previewListView, "open", false);
135+
136+ openPreview();
137+
138+ // wait for it to be loaded
139+ var currentPreviewItem = findChild(genericScopeView, "previewLoader0");
140+ tryCompareFunction(function() {
141+ var parts = currentPreviewItem.source.toString().split("/");
142+ var name = parts[parts.length - 1];
143+ return name == "GenericPreview.qml";
144+ },
145+ true);
146+ tryCompare(currentPreviewItem, "progress", 1);
147+ waitForRendering(currentPreviewItem);
148+
149+ checkArrowPosition(0);
150+
151+ // flick to the next previews
152+
153+ for (var i = 1; i < previewListView.count; ++i) {
154+
155+ mouseFlick(previewListView, previewListView.width - units.gu(1),
156+ previewListView.height / 2,
157+ units.gu(2),
158+ previewListView.height / 2);
159+
160+ // wait for it to be loaded
161+ var nextPreviewItem = findChild(genericScopeView, "previewLoader" + i);
162+ tryCompareFunction(function() {
163+ var parts = nextPreviewItem.source.toString().split("/");
164+ var name = parts[parts.length - 1];
165+ return name == "GenericPreview.qml";
166+ },
167+ true);
168+ tryCompare(nextPreviewItem, "progress", 1);
169+ waitForRendering(nextPreviewItem);
170+
171+ checkArrowPosition(i);
172+ }
173+
174+ closePreview();
175+ }
176+
70 function test_show_spinner() {
177 function test_show_spinner() {
178+ openPreview();
71− var previewLoader = findChild(genericScopeView, "previewLoader");
179+ var previewListView = findChild(genericScopeView, "previewListView");
72− previewLoader.open = true;
180+ var previewLoader = findChild(genericScopeView, "previewLoader0");
73− previewLoader.source = "../../../Dash/Generic/GenericPreview.qml";
181+
74 previewLoader.item.showProcessingAction = true;
182 previewLoader.item.showProcessingAction = true;
75 var waitingForAction = findChild(genericScopeView, "waitingForActionMouseArea");
183 var waitingForAction = findChild(genericScopeView, "waitingForActionMouseArea");
76 tryCompare(waitingForAction, "enabled", true);
184 tryCompare(waitingForAction, "enabled", true);
77 previewLoader.closePreviewSpinner();
185 previewLoader.closePreviewSpinner();
78 tryCompare(waitingForAction, "enabled", false);
186 tryCompare(waitingForAction, "enabled", false);
187+
188+ closePreview();
79 }
189 }
80 }
190 }
81 }
191 }
tests/qmltests/Greeter/tst_Phone.qml+18−5
@@ -83,8 +83,9 @@ Item {
83
83
84 function test_teaseLockedUnlocked_data() {
84 function test_teaseLockedUnlocked_data() {
85 return [
85 return [
86− {tag: "unlocked", locked: false},
86+ {tag: "unlocked", locked: false, moved: false},
87− {tag: "locked", locked: true}
87+ {tag: "locked", locked: true, moved: false},
88+ {tag: "moved", locked: false, moved: true}
88 ];
89 ];
89 }
90 }
90
91
@@ -93,10 +94,16 @@ Item {
93 tryCompare(greeter, "x", 0);
94 tryCompare(greeter, "x", 0);
94 greeter.locked = data.locked;
95 greeter.locked = data.locked;
95
96
97+ // simulate greeter being in the middle of a swipe
98+ if (data.moved) {
99+ greeter.x = -units.gu(4);
100+ tryCompare(greeter, "x", -units.gu(4));
101+ }
102+
96 mouseClick(greeter, greeter.width - units.gu(5), greeter.height - units.gu(1));
103 mouseClick(greeter, greeter.width - units.gu(5), greeter.height - units.gu(1));
97 greeter.minX = 0; // This is needed because the transition actually makes x jump once before animating
104 greeter.minX = 0; // This is needed because the transition actually makes x jump once before animating
98
105
99− if (!data.locked) {
106+ if (!data.locked && !data.moved) {
100 // Check if it has been moved over by 2 GUs. Give it a 2 pixel grace area
107 // Check if it has been moved over by 2 GUs. Give it a 2 pixel grace area
101 // because animation duration and teaseTimer are the same duration and
108 // because animation duration and teaseTimer are the same duration and
102 // might cause slight offsets
109 // might cause slight offsets
@@ -104,10 +111,16 @@ Item {
104 } else {
111 } else {
105 // waiting 100ms to make sure nothing moves
112 // waiting 100ms to make sure nothing moves
106 wait(100);
113 wait(100);
107− compare(greeter.minX, 0, "Greeter moved even tho its locked");
114+ compare(greeter.minX, 0, "Greeter teasing not disabled even though it's locked or moving.");
108 }
115 }
116+
117+ // Restore position in case we moved it for the test
118+ if (data.moved) {
119+ greeter.x = 0;
120+ }
121+
109 // Wait until we're back to 0
122 // Wait until we're back to 0
110− tryCompareFunction(function() { return greeter.x;}, 0);
123+ tryCompareFunction(function() { return greeter.x;}, 0);
111 }
124 }
112
125
113 function test_statsWelcomeScreen() {
126 function test_statsWelcomeScreen() {
tests/qmltests/Launcher/tst_Launcher.qml+47−11
@@ -363,25 +363,60 @@ Item {
363 var draggedItem = findChild(launcher, "launcherDelegate5")
363 var draggedItem = findChild(launcher, "launcherDelegate5")
364 var item0 = findChild(launcher, "launcherDelegate0")
364 var item0 = findChild(launcher, "launcherDelegate0")
365 var fakeDragItem = findChild(launcher, "fakeDragItem")
365 var fakeDragItem = findChild(launcher, "fakeDragItem")
366− var dndArea = findChild(launcher, "dndArea")
366+ var quickListShape = findChild(launcher, "quickListShape")
367
367
368 // Initial state
368 // Initial state
369− compare(dndArea.quickListPopover, null)
369+ compare(quickListShape.visible, false)
370
370
371 // Doing longpress
371 // Doing longpress
372 mousePress(draggedItem, draggedItem.width / 2, draggedItem.height / 2)
372 mousePress(draggedItem, draggedItem.width / 2, draggedItem.height / 2)
373 tryCompare(fakeDragItem, "visible", true) // Wait longpress happening
373 tryCompare(fakeDragItem, "visible", true) // Wait longpress happening
374− verify(dndArea.quickListPopover != null)
374+ tryCompare(quickListShape, "visible", true)
375
375
376 // Dragging a bit (> 1.5 gu)
376 // Dragging a bit (> 1.5 gu)
377 mouseMove(draggedItem, -units.gu(2), draggedItem.height / 2)
377 mouseMove(draggedItem, -units.gu(2), draggedItem.height / 2)
378
378
379 // QuickList needs to be closed when a drag operation starts
379 // QuickList needs to be closed when a drag operation starts
380− tryCompare(dndArea, "quickListPopover", null)
380+ tryCompare(quickListShape, "visible", false)
381
381
382 mouseRelease(draggedItem);
382 mouseRelease(draggedItem);
383 }
383 }
384
384
385+ function test_quicklist_positioning_data() {
386+ return [
387+ {tag: "top", flickTo: "top", itemIndex: 0},
388+ {tag: "bottom", flickTo: "bottom", itemIndex: 9}
389+ ];
390+ }
391+
392+ function test_quicklist_positioning(data) {
393+ revealer.dragLauncherIntoView();
394+ var quickList = findChild(launcher, "quickList")
395+ var draggedItem = findChild(launcher, "launcherDelegate" + data.itemIndex)
396+ var quickListShape = findChild(launcher, "quickListShape")
397+
398+ // Position launcher to where we need it
399+ var listView = findChild(launcher, "launcherListView");
400+ listView.flick(0, units.gu(200) * (data.flickTo === "top" ? 1 : -1));
401+ tryCompare(listView, "flicking", false);
402+
403+ // Doing longpress
404+ mousePress(draggedItem, draggedItem.width / 2, draggedItem.height / 2)
405+ tryCompare(quickListShape, "opacity", 0.8)
406+ mouseRelease(draggedItem);
407+
408+ verify(quickList.y >= units.gu(1))
409+ verify(quickList.y + quickList.height + units.gu(1) <= launcher.height)
410+
411+ // Click somewhere in the empty space to dismiss the quicklist
412+ mouseClick(launcher, launcher.width - units.gu(1), units.gu(1));
413+ tryCompare(quickListShape, "visible", false)
414+
415+ // Click somewhere in the empty space to dismiss the launcher
416+ mouseClick(launcher, launcher.width - units.gu(1), units.gu(1));
417+ revealer.waitUntilLauncherDisappears()
418+ }
419+
385 function test_quicklist_click_data() {
420 function test_quicklist_click_data() {
386 return [
421 return [
387 {tag: "non-clickable", index: 1, clickable: false },
422 {tag: "non-clickable", index: 1, clickable: false },
@@ -392,19 +427,20 @@ Item {
392 function test_quicklist_click(data) {
427 function test_quicklist_click(data) {
393 revealer.dragLauncherIntoView();
428 revealer.dragLauncherIntoView();
394 var clickedItem = findChild(launcher, "launcherDelegate5")
429 var clickedItem = findChild(launcher, "launcherDelegate5")
395− var dndArea = findChild(launcher, "dndArea")
430+ var quickList = findChild(launcher, "quickList")
431+ var quickListShape = findChild(launcher, "quickListShape")
396
432
397 // Initial state
433 // Initial state
398− compare(dndArea.quickListPopover, null)
434+ tryCompare(quickListShape, "visible", false)
399
435
400 // Doing longpress
436 // Doing longpress
401 mousePress(clickedItem, clickedItem.width / 2, clickedItem.height / 2)
437 mousePress(clickedItem, clickedItem.width / 2, clickedItem.height / 2)
402 tryCompare(clickedItem, "itemOpacity", 0) // Wait for longpress to happen
438 tryCompare(clickedItem, "itemOpacity", 0) // Wait for longpress to happen
403− verify(dndArea.quickListPopover != null)
439+ verify(quickListShape.visible, "QuickList must be visible")
404
440
405 mouseRelease(clickedItem);
441 mouseRelease(clickedItem);
406
442
407− var quickListEntry = findChild(dndArea.quickListPopover, "quickListEntry" + data.index)
443+ var quickListEntry = findChild(quickList, "quickListEntry" + data.index)
408
444
409 signalSpy.clear();
445 signalSpy.clear();
410 signalSpy.signalName = "quickListTriggered"
446 signalSpy.signalName = "quickListTriggered"
@@ -413,7 +449,7 @@ Item {
413
449
414 if (data.clickable) {
450 if (data.clickable) {
415 // QuickList needs to be closed when some clickable item is clicked
451 // QuickList needs to be closed when some clickable item is clicked
416− tryCompare(dndArea, "quickListPopover", null)
452+ tryCompare(quickListShape, "visible", false)
417
453
418 compare(signalSpy.count, 1, "Quicklist signal wasn't triggered")
454 compare(signalSpy.count, 1, "Quicklist signal wasn't triggered")
419 compare(signalSpy.signalArguments[0][0], LauncherModel.get(5).appId)
455 compare(signalSpy.signalArguments[0][0], LauncherModel.get(5).appId)
@@ -422,13 +458,13 @@ Item {
422 } else {
458 } else {
423
459
424 // QuickList must not be closed when a non-clickable item is clicked
460 // QuickList must not be closed when a non-clickable item is clicked
425− verify(dndArea.quickListPopover != null)
461+ verify(quickListShape.visible, "QuickList must be visible")
426
462
427 compare(signalSpy.count, 0, "Quicklist signal must NOT be triggered when clicking a non-clickable item")
463 compare(signalSpy.count, 0, "Quicklist signal must NOT be triggered when clicking a non-clickable item")
428
464
429 // Click somewhere in the empty space to dismiss the quicklist
465 // Click somewhere in the empty space to dismiss the quicklist
430 mouseClick(launcher, launcher.width - units.gu(1), units.gu(1));
466 mouseClick(launcher, launcher.width - units.gu(1), units.gu(1));
431− tryCompare(dndArea, "quickListPopover", null)
467+ tryCompare(quickListShape, "visible", false)
432 }
468 }
433 }
469 }
434 }
470 }
tests/qmltests/tst_Shell.qml+14−9
@@ -111,14 +111,14 @@ Item {
111 waitUntilApplicationWindowIsFullyVisible();
111 waitUntilApplicationWindowIsFullyVisible();
112
112
113 // Minimize the application we just launched
113 // Minimize the application we just launched
114− swipeFromLeftEdge();
114+ swipeFromLeftEdge(units.gu(27));
115
115
116 waitForUIToSettle();
116 waitForUIToSettle();
117
117
118 checkRightEdgeDragWithMinimizedApp();
118 checkRightEdgeDragWithMinimizedApp();
119
119
120 // Minimize that application once again
120 // Minimize that application once again
121− swipeFromLeftEdge();
121+ swipeFromLeftEdge(units.gu(27));
122
122
123 // Right edge behavior should now be the same as before that app,
123 // Right edge behavior should now be the same as before that app,
124 // was launched. Manually cleanup beforehand to get to initial
124 // was launched. Manually cleanup beforehand to get to initial
@@ -130,8 +130,10 @@ Item {
130
130
131 function test_leftEdgeDrag_data() {
131 function test_leftEdgeDrag_data() {
132 return [
132 return [
133− {tag: "without launcher", revealLauncher: false},
133+ {tag: "without launcher", revealLauncher: false, swipeLength: units.gu(27), appHides: true},
134− {tag: "with launcher", revealLauncher: true},
134+ {tag: "with launcher", revealLauncher: true, swipeLength: units.gu(27), appHides: true},
135+ {tag: "small swipe", revealLauncher: false, swipeLength: units.gu(25), appHides: false},
136+ {tag: "long swipe", revealLauncher: false, swipeLength: units.gu(27), appHides: true}
135 ];
137 ];
136 }
138 }
137
139
@@ -144,8 +146,11 @@ Item {
144 dragLauncherIntoView();
146 dragLauncherIntoView();
145 }
147 }
146
148
147− swipeFromLeftEdge();
149+ swipeFromLeftEdge(data.swipeLength);
150+ if (data.appHides)
148− waitUntilApplicationWindowIsFullyHidden();
151+ waitUntilApplicationWindowIsFullyHidden();
152+ else
153+ waitUntilApplicationWindowIsFullyVisible();
149 }
154 }
150
155
151 function test_suspend() {
156 function test_suspend() {
@@ -297,7 +302,7 @@ Item {
297 verify(itemIsOnScreen(dashApps));
302 verify(itemIsOnScreen(dashApps));
298
303
299 // Minimize the application we just launched
304 // Minimize the application we just launched
300− swipeFromLeftEdge();
305+ swipeFromLeftEdge(units.gu(27));
301
306
302 // Wait for the whole UI to settle down
307 // Wait for the whole UI to settle down
303 waitUntilApplicationWindowIsFullyHidden();
308 waitUntilApplicationWindowIsFullyHidden();
@@ -380,10 +385,10 @@ Item {
380 }
385 }
381 }
386 }
382
387
383− function swipeFromLeftEdge() {
388+ function swipeFromLeftEdge(swipeLength) {
384 var touchStartX = 2;
389 var touchStartX = 2;
385 var touchStartY = shell.height / 2;
390 var touchStartY = shell.height / 2;
386− touchFlick(shell, touchStartX, touchStartY, shell.width * 0.75, touchStartY);
391+ touchFlick(shell, touchStartX, touchStartY, swipeLength, touchStartY);
387 }
392 }
388
393
389 function swipeLeftFromCenter() {
394 function swipeLeftFromCenter() {