on xenial help sign in
initial rework of the applicationswitching - require for being able to properly work on the right edge implementations
ab506beMichael Zanetti
Components/EdgeDemo.qml+1
@@ -31,6 +31,7 @@ Item {
31 property bool dashEnabled: true
31 property bool dashEnabled: true
32 property bool panelEnabled: true
32 property bool panelEnabled: true
33 property bool panelContentEnabled: true
33 property bool panelContentEnabled: true
34+ property bool running: !launcherEnabled || !dashEnabled || !panelEnabled || !panelContentEnabled
34
35
35 property bool paused: false
36 property bool paused: false
36
37
Components/FilterGrid.qml+1−1
@@ -67,7 +67,7 @@ Item {
67 property alias highlightIndex: iconTileGrid.highlightIndex
67 property alias highlightIndex: iconTileGrid.highlightIndex
68 readonly property alias currentItem: iconTileGrid.currentItem
68 readonly property alias currentItem: iconTileGrid.currentItem
69
69
70− implicitHeight: !filterAnimation.running ? childrenRect.height : height
70+ height: !filterAnimation.running ? childrenRect.height : height
71 clip: filterAnimation.running
71 clip: filterAnimation.running
72
72
73 NumberAnimation {
73 NumberAnimation {
Components/ResponsiveFlowView.qml+3−10
@@ -27,9 +27,8 @@ Item {
27 property alias verticalSpacing: flow.verticalSpacing
27 property alias verticalSpacing: flow.verticalSpacing
28 property alias horizontalSpacing: flow.horizontalSpacing
28 property alias horizontalSpacing: flow.horizontalSpacing
29 property int referenceDelegateWidth
29 property int referenceDelegateWidth
30− property alias firstModel: repeater1.model
30+ property alias model: repeater.model
31− property alias secondModel: repeater2.model
32− property alias delegate: repeater1.delegate
31+ property alias delegate: repeater.delegate
33 readonly property int cellWidth: referenceDelegateWidth + horizontalSpacing
32 readonly property int cellWidth: referenceDelegateWidth + horizontalSpacing
34 readonly property int cellHeight: referenceDelegateWidth + verticalSpacing
33 readonly property int cellHeight: referenceDelegateWidth + verticalSpacing
35 property alias move: flow.move
34 property alias move: flow.move
@@ -69,13 +68,7 @@ Item {
69 property int margin: allocatableVerticalSpace - columns * horizontalSpacing
68 property int margin: allocatableVerticalSpace - columns * horizontalSpacing
70
69
71 Repeater {
70 Repeater {
72− id: repeater1
71+ id: repeater
73− model: (root.model) ? root.model[0] : null
74− }
75− Repeater {
76− id: repeater2
77− model: (root.model) ? root.model[1] : null
78− delegate: repeater1.delegate
79 }
72 }
80 }
73 }
81}
74}
Dash/Apps/RunningApplicationTile.qml+36−19
@@ -23,7 +23,6 @@ import "../../Components/ListItems"
23AbstractButton {
23AbstractButton {
24 id: root
24 id: root
25 property var application
25 property var application
26− property bool __sideStageEnabled: shell.applicationManager.sideStageEnabled
27
26
28 signal requestedApplicationActivation(var application)
27 signal requestedApplicationActivation(var application)
29 signal requestedApplicationTermination(var application)
28 signal requestedApplicationTermination(var application)
@@ -51,34 +50,52 @@ AbstractButton {
51 }
50 }
52 }
51 }
53
52
54− function updateScreenshotFromCache() {
53+ Component.onCompleted: print("---------------created apptile", application, application.source)
55− applicationImage.updateFromCache();
56− }
57
54
58− // FIXME: should use UbuntuShape from SDK
59 UbuntuShapeForItem {
55 UbuntuShapeForItem {
60 id: shapedApplicationImage
56 id: shapedApplicationImage
61− anchors {
62− top: parent.top
63− horizontalCenter: parent.horizontalCenter
57+ anchors { top: parent.top; horizontalCenter: parent.horizontalCenter }
64− }
65
58
66 // FIXME: width and height should be defined according to the
59 // FIXME: width and height should be defined according to the
67 // application window's aspect ratio.
60 // application window's aspect ratio.
68− width: (application.stage === ApplicationInfo.MainStage && __sideStageEnabled) ?
69− units.gu(22) : units.gu(11)
70− height: (__sideStageEnabled) ? units.gu(22) : units.gu(19)
61+ height: units.gu(19)
62+ width: applicationImage.width
71 radius: "medium"
63 radius: "medium"
72− image: applicationImage
73− }
74
64
75− ApplicationImage {
65+ image: Image {
76− id: applicationImage
66+ id: applicationImage
77− source: ApplicationManager.findApplication((application) ? application.appId : "")
67+ source: application.screenshot
78− width: shapedApplicationImage.width
68+ // height : width = ss.height : ss.width
79− height: shapedApplicationImage.height
69+ height: shapedApplicationImage.height
70+ width: height * sourceSize.width / sourceSize.height
71+ }
72+
80 }
73 }
81
74
75+// // FIXME: should use UbuntuShape from SDK
76+// UbuntuShapeForItem {
77+// id: shapedApplicationImage
78+// anchors {
79+// top: parent.top
80+// horizontalCenter: parent.horizontalCenter
81+// }
82+
83+// // FIXME: width and height should be defined according to the
84+// // application window's aspect ratio.
85+// width: (application.stage === ApplicationInfo.MainStage && __sideStageEnabled) ?
86+// units.gu(22) : units.gu(11)
87+// height: (__sideStageEnabled) ? units.gu(22) : units.gu(19)
88+// radius: "medium"
89+// image: applicationImage
90+// }
91+
92+// ApplicationImage {
93+// id: applicationImage
94+// source: ApplicationManager.findApplication((application) ? application.appId : "")
95+// width: shapedApplicationImage.width
96+// height: shapedApplicationImage.height
97+// }
98+
82 UbuntuShape {
99 UbuntuShape {
83 id: borderPressed
100 id: borderPressed
84
101
Dash/Apps/RunningApplicationsGrid.qml+5−10
@@ -18,6 +18,7 @@ import QtQuick 2.0
18import "../../Components"
18import "../../Components"
19
19
20import Ubuntu.Gestures 0.1
20import Ubuntu.Gestures 0.1
21+import Unity.Application 0.1
21
22
22ResponsiveFlowView {
23ResponsiveFlowView {
23 id: root
24 id: root
@@ -25,7 +26,7 @@ ResponsiveFlowView {
25
26
26 signal updateScreenshots
27 signal updateScreenshots
27 property alias enableHeightBehavior: heightBehaviour.enabled
28 property alias enableHeightBehavior: heightBehaviour.enabled
28− property bool enableHeightBehaviorOnNextCreation: firstModel.count + secondModel.count == 0
29+ property bool enableHeightBehaviorOnNextCreation: model.count === 0
29
30
30 Connections {
31 Connections {
31 target: shell
32 target: shell
@@ -40,13 +41,7 @@ ResponsiveFlowView {
40 }
41 }
41
42
42 Connections {
43 Connections {
43− target: root.firstModel
44+ target: root.model
44− onCountChanged: {
45− heightBehaviour.enabled = true;
46− }
47− }
48− Connections {
49− target: root.secondModel
50 onCountChanged: {
45 onCountChanged: {
51 heightBehaviour.enabled = true;
46 heightBehaviour.enabled = true;
52 }
47 }
@@ -85,10 +80,10 @@ ResponsiveFlowView {
85 root.terminationModeEnabled = true
80 root.terminationModeEnabled = true
86 }
81 }
87 onRequestedApplicationTermination: {
82 onRequestedApplicationTermination: {
88− shell.applicationManager.stopApplication(model.appId)
83+ ApplicationManager.stopApplication(model.appId)
89 }
84 }
90 onRequestedApplicationActivation: {
85 onRequestedApplicationActivation: {
91− shell.activateApplication(model.appId)
86+ ApplicationManager.focusApplication(model.appId)
92 }
87 }
93
88
94 terminationModeEnabled: root.terminationModeEnabled
89 terminationModeEnabled: root.terminationModeEnabled
Dash/DashApps.qml+4−10
@@ -17,6 +17,7 @@
17import QtQuick 2.0
17import QtQuick 2.0
18import Ubuntu.Components 0.1
18import Ubuntu.Components 0.1
19import Utils 0.1
19import Utils 0.1
20+import Unity.Application 0.1
20import "../Components"
21import "../Components"
21import "../Components/ListItems"
22import "../Components/ListItems"
22import "Apps"
23import "Apps"
@@ -25,10 +26,6 @@ GenericScopeView {
25 id: scopeView
26 id: scopeView
26 objectName: "DashApps"
27 objectName: "DashApps"
27
28
28− // FIXME: a way to aggregate these models would be ideal
29− property var mainStageApplicationsModel: shell.applicationManager.mainStageApplications
30− property var sideStageApplicationModel: shell.applicationManager.sideStageApplications
31−
32 //FIXME an alias to itself shouldn't be required. More than likely a Qt bug. Try removing with newer Qt.
29 //FIXME an alias to itself shouldn't be required. More than likely a Qt bug. Try removing with newer Qt.
33 property alias searchHistory: scopeView.searchHistory
30 property alias searchHistory: scopeView.searchHistory
34
31
@@ -41,19 +38,16 @@ GenericScopeView {
41 SortFilterProxyModel {
38 SortFilterProxyModel {
42 id: runningApplicationsModel
39 id: runningApplicationsModel
43
40
44− property var firstModel: mainStageApplicationsModel
45− property var secondModel: sideStageApplicationModel
46 property bool canEnableTerminationMode: scopeView.isCurrent
41 property bool canEnableTerminationMode: scopeView.isCurrent
47
42
48− model: dummyVisibilityModifier
43+ model: ApplicationManager
49 filterRole: 0
44 filterRole: 0
50− filterRegExp: invertMatch ? ((mainStageApplicationsModel.count === 0 &&
51− sideStageApplicationModel.count === 0) ? RegExp("running-apps") : RegExp("")) : RegExp("disabled")
45+ filterRegExp: invertMatch ? (ApplicationManager.count === 0 ? RegExp("running-apps") : RegExp("")) : RegExp("disabled")
52 invertMatch: scopeView.scope.searchQuery.length == 0
46 invertMatch: scopeView.scope.searchQuery.length == 0
53 }
47 }
54
48
55 onScopeChanged: {
49 onScopeChanged: {
56 scopeView.scope.categories.overrideResults("recent", runningApplicationsModel);
50 scopeView.scope.categories.overrideResults("recent", runningApplicationsModel);
57− enableHeightBehaviorOnNextCreation = (mainStageApplicationsModel.count + sideStageApplicationModel.count == 0)
51+ enableHeightBehaviorOnNextCreation = ApplicationManager.count === 0
58 }
52 }
59}
53}
Dash/DashFilterGrid.qml+6−2
@@ -37,7 +37,7 @@ DashRenderer {
37 expandable: filterGrid.expandable
37 expandable: filterGrid.expandable
38 delegateCreationBegin: filterGrid.delegateCreationBegin
38 delegateCreationBegin: filterGrid.delegateCreationBegin
39 delegateCreationEnd: filterGrid.delegateCreationEnd
39 delegateCreationEnd: filterGrid.delegateCreationEnd
40− implicitHeight: filterGrid.implicitHeight
40+ height: filterGrid.height
41 margins: filterGrid.margins
41 margins: filterGrid.margins
42 uncollapsedHeight: filterGrid.uncollapsedHeight
42 uncollapsedHeight: filterGrid.uncollapsedHeight
43 verticalSpacing: filterGrid.verticalSpacing
43 verticalSpacing: filterGrid.verticalSpacing
@@ -48,7 +48,7 @@ DashRenderer {
48
48
49 FilterGrid {
49 FilterGrid {
50 id: filterGrid
50 id: filterGrid
51− anchors.fill: parent
51+ width: dashFilterGrid.width
52 minimumHorizontalSpacing: units.gu(0.5)
52 minimumHorizontalSpacing: units.gu(0.5)
53 delegateWidth: units.gu(11)
53 delegateWidth: units.gu(11)
54 delegateHeight: units.gu(9.5)
54 delegateHeight: units.gu(9.5)
@@ -56,5 +56,9 @@ DashRenderer {
56 model: dashFilterGrid.model
56 model: dashFilterGrid.model
57 filter: dashFilterGrid.filter
57 filter: dashFilterGrid.filter
58 highlightIndex: dashFilterGrid.highlightIndex
58 highlightIndex: dashFilterGrid.highlightIndex
59+
60+ onFilterChanged: {
61+ dashFilterGrid.filter = filter
62+ }
59 }
63 }
60}
64}
Dash/Generic/GenericCarousel.qml+1
@@ -28,6 +28,7 @@ DashCarousel {
28
28
29 BaseCarouselDelegate {
29 BaseCarouselDelegate {
30 id: item
30 id: item
31+ objectName: "carouselDelegate"
31
32
32 UbuntuShape {
33 UbuntuShape {
33 anchors.fill: parent
34 anchors.fill: parent
Dash/GenericScopeView.qml+3−2
@@ -126,6 +126,7 @@ FocusScope {
126
126
127 delegate: ListItems.Base {
127 delegate: ListItems.Base {
128 id: baseItem
128 id: baseItem
129+ objectName: "dashCategory" + category
129 highlightWhenPressed: false
130 highlightWhenPressed: false
130
131
131 readonly property bool expandable: rendererLoader.item ? rendererLoader.item.expandable : false
132 readonly property bool expandable: rendererLoader.item ? rendererLoader.item.expandable : false
@@ -149,8 +150,7 @@ FocusScope {
149 }
150 }
150 if (source.toString().indexOf("Apps/RunningApplicationsGrid.qml") != -1) {
151 if (source.toString().indexOf("Apps/RunningApplicationsGrid.qml") != -1) {
151 // TODO: the running apps grid doesn't support standard scope results model yet
152 // TODO: the running apps grid doesn't support standard scope results model yet
152− item.firstModel = Qt.binding(function() { return results.firstModel })
153+ item.model = Qt.binding(function() { return results.model })
153− item.secondModel = Qt.binding(function() { return results.secondModel })
154 item.canEnableTerminationMode = Qt.binding(function() { return scopeView.isCurrent })
154 item.canEnableTerminationMode = Qt.binding(function() { return scopeView.isCurrent })
155 } else {
155 } else {
156 item.model = Qt.binding(function() { return results })
156 item.model = Qt.binding(function() { return results })
@@ -271,6 +271,7 @@ FocusScope {
271
271
272 sectionProperty: "name"
272 sectionProperty: "name"
273 sectionDelegate: ListItems.Header {
273 sectionDelegate: ListItems.Header {
274+ objectName: "dashSectionHeader" + delegate.category
274 property var delegate: categoryView.item(delegateIndex)
275 property var delegate: categoryView.item(delegateIndex)
275 width: categoryView.width
276 width: categoryView.width
276 text: section
277 text: section
Dash/graphics/phone/screenshots/angrybirds@12.pngdeleted

Binary file not shown.

Greeter/Greeter.qml+1−1
@@ -51,7 +51,7 @@ Showable {
51 signal unlocked(int uid)
51 signal unlocked(int uid)
52
52
53 onRightTeaserPressedChanged: {
53 onRightTeaserPressedChanged: {
54− if (rightTeaserPressed && !locked && x == 0) {
54+ if (rightTeaserPressed && (!locked || narrowMode) && x == 0) {
55 teasingTimer.start();
55 teasingTimer.start();
56 }
56 }
57 }
57 }
Greeter/LoginList.qml+11
@@ -289,5 +289,16 @@ Item {
289 }
289 }
290 passwordInput.text = "";
290 passwordInput.text = "";
291 }
291 }
292+
293+ onRequestAuthenticationUser: {
294+ // Find index for requested user, if it exists
295+ for (var i = 0; i < userList.model.count; i++) {
296+ if (user == userList.model.data(i, LightDM.UserRoles.NameRole)) {
297+ moveTimer.start();
298+ userList.currentIndex = i;
299+ return;
300+ }
301+ }
302+ }
292 }
303 }
293}
304}
Hud/SliderLabel.qml+4−2
@@ -36,10 +36,12 @@ Item {
36 label.text = sliderData["label"]
36 label.text = sliderData["label"]
37 slider.minimumValue = sliderData["min"]
37 slider.minimumValue = sliderData["min"]
38 slider.maximumValue = sliderData["max"]
38 slider.maximumValue = sliderData["max"]
39+ if("live" in sliderData)
39− slider.live = sliderData["live"]
40+ slider.live = sliderData["live"]
40 // SDK Slider does not support step yet
41 // SDK Slider does not support step yet
41// slider.step = sliderData["step"]
42// slider.step = sliderData["step"]
43+ if("value" in sliderData)
42− slider.value = sliderData["value"]
44+ slider.value = sliderData["value"]
43 action = sliderData["action"]
45 action = sliderData["action"]
44 }
46 }
45
47
Shell.qml+67−242
@@ -49,20 +49,22 @@ FocusScope {
49 readonly property real panelHeight: panel.panelHeight
49 readonly property real panelHeight: panel.panelHeight
50
50
51 property bool dashShown: dash.shown
51 property bool dashShown: dash.shown
52− property bool stageScreenshotsReady: {
52+
53− if (sideStage.shown) {
53+ function activateApplication(appId) {
54− if (mainStage.applications.count > 0) {
54+ if (ApplicationManager.findApplication(appId)) {
55− return mainStage.usingScreenshots && sideStage.usingScreenshots;
55+ print("Shell.qml: activating app", appId);
56− } else {
56+ ApplicationManager.activateApplication(appId);
57− return sideStage.usingScreenshots;
58− }
59 } else {
57 } else {
60− return mainStage.usingScreenshots;
58+ print("starting app", appId);
59+
60+ // FIXME:
61+// var execFlags = sideStageEnabled ? ApplicationManager.NoFlag : ApplicationManager.ForceMainStage;
62+ var execFlags = ApplicationManager.ForceMainStage;
63+
64+ ApplicationManager.startApplication(appId, execFlags);
61 }
65 }
62 }
66 }
63
67
64− property var applicationManager: ApplicationManagerWrapper {}
65−
66 Binding {
68 Binding {
67 target: LauncherModel
69 target: LauncherModel
68 property: "applicationManager"
70 property: "applicationManager"
@@ -71,49 +73,6 @@ FocusScope {
71
73
72 Component.onCompleted: {
74 Component.onCompleted: {
73 Theme.name = "Ubuntu.Components.Themes.SuruGradient"
75 Theme.name = "Ubuntu.Components.Themes.SuruGradient"
74−
75− applicationManager.sideStageEnabled = Qt.binding(function() { return sideStage.enabled })
76−
77− // FIXME: if application focused before shell starts, shell draws on top of it only.
78− // We should detect already running applications on shell start and bring them to the front.
79− applicationManager.unfocusCurrentApplication();
80− }
81−
82− readonly property bool applicationFocused: !!applicationManager.mainStageFocusedApplication
83− || !!applicationManager.sideStageFocusedApplication
84− // Used for autopilot testing.
85− readonly property string currentFocusedAppId: ApplicationManager.focusedApplicationId
86−
87− readonly property bool fullscreenMode: {
88− if (greeter.shown || lockscreen.shown) {
89− return false;
90− } else if (mainStage.usingScreenshots) { // Window Manager animating so want to re-evaluate fullscreen mode
91− return mainStage.switchingFromFullscreenToFullscreen;
92− } else if (applicationManager.mainStageFocusedApplication) {
93− return applicationManager.mainStageFocusedApplication.fullscreen;
94− } else {
95− return false;
96− }
97− }
98−
99− function activateApplication(appId, argument) {
100− if (applicationManager) {
101− // For newly started applications, as it takes them time to draw their first frame
102− // we add a delay before we hide the animation screenshots to compensate.
103− var addDelay = !applicationManager.getApplicationFromDesktopFile(appId);
104−
105− var application;
106− application = applicationManager.activateApplication(appId, argument);
107− if (application == null) {
108− return;
109− }
110− if (application.stage == ApplicationInfo.MainStage || !sideStage.enabled) {
111− mainStage.activateApplication(appId, addDelay);
112− } else {
113− sideStage.activateApplication(appId, addDelay);
114− }
115− stages.show();
116− }
117 }
76 }
118
77
119 GSettings {
78 GSettings {
@@ -161,19 +120,18 @@ FocusScope {
161 // Whether the underlay is fully covered by opaque UI elements.
120 // Whether the underlay is fully covered by opaque UI elements.
162 property bool fullyCovered: panel.indicators.fullyOpened && shell.width <= panel.indicatorsMenuWidth
121 property bool fullyCovered: panel.indicators.fullyOpened && shell.width <= panel.indicatorsMenuWidth
163
122
164− readonly property bool applicationRunning: ((mainStage.applications && mainStage.applications.count > 0)
123+ readonly property bool applicationRunning: ApplicationManager.focusedApplicationId.length > 0
165− || (sideStage.applications && sideStage.applications.count > 0))
166
124
167 // Whether the user should see the topmost application surface (if there's one at all).
125 // Whether the user should see the topmost application surface (if there's one at all).
168− readonly property bool applicationSurfaceShouldBeSeen: applicationRunning && !stages.fullyHidden
126+ readonly property bool applicationSurfaceShouldBeSeen: applicationRunning && !stages.painting
169− && !mainStage.usingScreenshots // but want sideStage animating over app surface
170−
171−
172
127
173 // NB! Application surfaces are stacked behing the shell one. So they can only be seen by the user
128 // NB! Application surfaces are stacked behing the shell one. So they can only be seen by the user
174 // through the translucent parts of the shell surface.
129 // through the translucent parts of the shell surface.
175 visible: !fullyCovered && !applicationSurfaceShouldBeSeen
130 visible: !fullyCovered && !applicationSurfaceShouldBeSeen
176
131
132+ onVisibleChanged: print("hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", visible, fullyCovered, applicationSurfaceShouldBeSeen,
133+ applicationRunning, stages.fullyHidden, stages.usingScreenshots)
134+
177 CrossFadeImage {
135 CrossFadeImage {
178 id: backgroundImage
136 id: backgroundImage
179 objectName: "backgroundImage"
137 objectName: "backgroundImage"
@@ -197,12 +155,6 @@ FocusScope {
197 hides: [stages, launcher, panel.indicators]
155 hides: [stages, launcher, panel.indicators]
198 shown: disappearingAnimationProgress !== 1.0
156 shown: disappearingAnimationProgress !== 1.0
199 enabled: disappearingAnimationProgress === 0.0 && edgeDemo.dashEnabled
157 enabled: disappearingAnimationProgress === 0.0 && edgeDemo.dashEnabled
200− // FIXME: unfocus all applications when going back to the dash
201− onEnabledChanged: {
202− if (enabled) {
203− shell.applicationManager.unfocusCurrentApplication()
204− }
205− }
206
158
207 anchors {
159 anchors {
208 fill: parent
160 fill: parent
@@ -233,8 +185,7 @@ FocusScope {
233 x: launcher.progress
185 x: launcher.progress
234 Behavior on x {SmoothedAnimation{velocity: 600}}
186 Behavior on x {SmoothedAnimation{velocity: 600}}
235
187
236− property real showProgress:
237− MathUtils.clamp(1 - (x + stages.x) / shell.width, 0, 1)
188+ property real showProgress: MathUtils.clamp(1 - (x + stages.x) / shell.width, 0, 1)
238
189
239 Showable {
190 Showable {
240 id: stages
191 id: stages
@@ -244,6 +195,9 @@ FocusScope {
244
195
245 property bool fullyShown: shown && x == 0 && parent.x == 0
196 property bool fullyShown: shown && x == 0 && parent.x == 0
246 property bool fullyHidden: !shown && x == width
197 property bool fullyHidden: !shown && x == width
198+
199+ property bool painting: applicationsDisplayLoader.item ? applicationsDisplayLoader.item.painting : false
200+
247 available: !greeter.shown
201 available: !greeter.shown
248 hides: [panel.indicators]
202 hides: [panel.indicators]
249 shown: false
203 shown: false
@@ -254,145 +208,50 @@ FocusScope {
254 width: parent.width
208 width: parent.width
255 height: parent.height
209 height: parent.height
256
210
257− // close the stages when no focused application remains
211+ onShownChanged: {
258− Connections {
212+ print("stages shown", shown)
259− target: shell.applicationManager
213+ if (!shown) {
260− onMainStageFocusedApplicationChanged: stages.closeIfNoApplications()
214+ ApplicationManager.unfocusCurrentApplication();
261− onSideStageFocusedApplicationChanged: stages.closeIfNoApplications()
215+ }
262− ignoreUnknownSignals: true
263 }
216 }
264
217
265− function closeIfNoApplications() {
218+ onFullyShownChanged: {
266− if (!shell.applicationManager.mainStageFocusedApplication
219+ print("kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk", fullyShown)
267− && !shell.applicationManager.sideStageFocusedApplication
268− && shell.applicationManager.mainStageApplications.count == 0
269− && shell.applicationManager.sideStageApplications.count == 0) {
270− stages.hide();
271− }
272 }
220 }
273
221
274− // show the stages when an application gets the focus
275 Connections {
222 Connections {
276− target: shell.applicationManager
223+ target: ApplicationManager
277− onMainStageFocusedApplicationChanged: {
224+ onFocusedApplicationIdChanged: {
278− if (shell.applicationManager.mainStageFocusedApplication) {
225+ if (ApplicationManager.focusedApplicationId.length > 0) {
279− mainStage.show();
226+ print("should show stages")
280 stages.show();
227 stages.show();
281 }
228 }
282 }
229 }
283− onSideStageFocusedApplicationChanged: {
284− if (shell.applicationManager.sideStageFocusedApplication) {
285− sideStage.show();
286− stages.show();
287− }
288− }
289− ignoreUnknownSignals: true
290− }
291
230
292− Stage {
231+ onApplicationAdded: {
293− id: mainStage
294−
295− anchors.fill: parent
296− fullyShown: stages.fullyShown
297− fullyHidden: stages.fullyHidden
298− shouldUseScreenshots: !fullyShown
299− rightEdgeEnabled: !sideStage.enabled
300−
301− applicationManager: shell.applicationManager
302− rightEdgeDraggingAreaWidth: shell.edgeSize
303− normalApplicationY: shell.panelHeight
304−
305− shown: true
306− function show() {
307 stages.show();
232 stages.show();
308 }
233 }
309− function hide() {
310− }
311−
312− // FIXME: workaround the fact that focusing a main stage application
313− // raises its surface on top of all other surfaces including the ones
314− // that belong to side stage applications.
315− onFocusedApplicationChanged: {
316− if (focusedApplication && sideStage.focusedApplication && sideStage.fullyShown) {
317− shell.applicationManager.focusApplication(sideStage.focusedApplication);
318− }
319− }
320 }
234 }
321
235
322− SideStage {
236+ Loader {
323− id: sideStage
237+ id: applicationsDisplayLoader
238+ anchors.fill: parent
239+
240+// source: "Stages/StageWithSideStage.qml"
241+ source: "Stages/PhoneStage.qml"
324
242
325− applicationManager: shell.applicationManager
243+// Rectangle { anchors.fill: parent; color: "blue"; opacity: .5 }
326− rightEdgeDraggingAreaWidth: shell.edgeSize
327− normalApplicationY: shell.panelHeight
328
244
329− onShownChanged: {
245+ Binding {
330− if (!shown && mainStage.applications.count == 0) {
246+ target: applicationsDisplayLoader.item
331− stages.hide();
247+ property: "moving"
332− }
248+ value: !stages.fullyShown
333 }
249 }
334− // FIXME: when hiding the side stage, refocus the main stage
250+ Binding {
335− // application so that it goes in front of the side stage
251+ target: applicationsDisplayLoader.item
336− // application and hides it
252+ property: "shown"
337− onFullyShownChanged: {
253+ value: stages.shown
338− if (!fullyShown && stages.fullyShown && sideStage.focusedApplication != null) {
339− shell.applicationManager.focusApplication(mainStage.focusedApplication);
340− }
341 }
254 }
342−
343− enabled: shell.width >= units.gu(60)
344− visible: enabled
345− fullyShown: stages.fullyShown && shown
346− && sideStage[sideStageRevealer.boundProperty] == sideStageRevealer.openedValue
347− shouldUseScreenshots: !fullyShown || mainStage.usingScreenshots || sideStageRevealer.pressed
348−
349− available: !greeter.shown && !lockscreen.shown && enabled
350− hides: [launcher, panel.indicators]
351− shown: false
352− showAnimation: StandardAnimation { property: "x"; duration: 350; to: sideStageRevealer.openedValue; easing.type: Easing.OutQuint }
353− hideAnimation: StandardAnimation { property: "x"; duration: 350; to: sideStageRevealer.closedValue; easing.type: Easing.OutQuint }
354−
355− width: units.gu(40)
356− height: stages.height
357− handleExpanded: sideStageRevealer.pressed
358− }
359−
360− Revealer {
361− id: sideStageRevealer
362−
363− enabled: mainStage.applications.count > 0 && sideStage.applications.count > 0
364− && sideStage.available
365− direction: Qt.RightToLeft
366− openedValue: parent.width - sideStage.width
367− hintDisplacement: units.gu(3)
368− /* The size of the sidestage handle needs to be bigger than the
369− typical size used for edge detection otherwise it is really
370− hard to grab.
371− */
372− handleSize: sideStage.shown ? units.gu(4) : shell.edgeSize
373− closedValue: parent.width + sideStage.handleSizeCollapsed
374− target: sideStage
375− x: parent.width - width
376− width: sideStage.width + handleSize * 0.7
377− height: sideStage.height
378− orientation: Qt.Horizontal
379− }
380−
381− DragHandle {
382− id: stagesDragHandle
383−
384− anchors.top: parent.top
385− anchors.bottom: parent.bottom
386− anchors.right: parent.left
387−
388− width: shell.edgeSize
389− direction: Direction.Leftwards
390− enabled: greeter.showProgress == 0 && edgeDemo.dashEnabled
391− property bool haveApps: mainStage.applications.count > 0 || sideStage.applications.count > 0
392−
393− maxTotalDragDistance: haveApps ? parent.width : parent.width * 0.7
394− // Make autocompletion impossible when !haveApps
395− edgeDragEvaluator.minDragDistance: haveApps ? maxTotalDragDistance * 0.1 : Number.MAX_VALUE
396 }
255 }
397 }
256 }
398 }
257 }
@@ -472,23 +331,6 @@ FocusScope {
472 property var previousMainApp: null
331 property var previousMainApp: null
473 property var previousSideApp: null
332 property var previousSideApp: null
474
333
475− function removeApplicationFocus() {
476− greeter.previousMainApp = applicationManager.mainStageFocusedApplication;
477− greeter.previousSideApp = applicationManager.sideStageFocusedApplication;
478− applicationManager.unfocusCurrentApplication();
479− }
480−
481− function restoreApplicationFocus() {
482− if (greeter.previousMainApp) {
483− applicationManager.focusApplication(greeter.previousMainApp);
484− greeter.previousMainApp = null;
485− }
486− if (greeter.previousSideApp) {
487− applicationManager.focusApplication(greeter.previousSideApp);
488− greeter.previousSideApp = null;
489− }
490− }
491−
492 onShownChanged: {
334 onShownChanged: {
493 if (shown) {
335 if (shown) {
494 lockscreen.reset();
336 lockscreen.reset();
@@ -499,9 +341,6 @@ FocusScope {
499 greeter.selected(0);
341 greeter.selected(0);
500 }
342 }
501 greeter.forceActiveFocus();
343 greeter.forceActiveFocus();
502− removeApplicationFocus();
503− } else {
504− restoreApplicationFocus();
505 }
344 }
506 }
345 }
507
346
@@ -518,34 +357,11 @@ FocusScope {
518 launcher.tease();
357 launcher.tease();
519 }
358 }
520 }
359 }
521−
522− Connections {
523− target: applicationManager
524− ignoreUnknownSignals: true
525− // If any app is focused when greeter is open, it's due to a user action
526− // like a snap decision (say, an incoming call).
527− // TODO: these should be protected to only unlock for certain applications / certain usecases
528− // potentially only in connection with a notification.
529− onMainStageFocusedApplicationChanged: {
530− if (greeter.shown && applicationManager.mainStageFocusedApplication) {
531− greeter.previousMainApp = null // make way for new focused app
532− greeter.previousSideApp = null
533− greeter.hide()
534− }
535− }
536− onSideStageFocusedApplicationChanged: {
537− if (greeter.shown && applicationManager.sideStageFocusedApplication) {
538− greeter.previousMainApp = null // make way for new focused app
539− greeter.previousSideApp = null
540− greeter.hide()
541− }
542− }
543− }
544 }
360 }
545
361
546 InputFilterArea {
362 InputFilterArea {
547 anchors.fill: parent
363 anchors.fill: parent
548− blockInput: !applicationFocused || greeter.shown || lockscreen.shown || launcher.shown
364+ blockInput: ApplicationManager.focusedApplicationId.length === 0 || greeter.shown || lockscreen.shown || launcher.shown
549 || panel.indicators.shown || hud.shown
365 || panel.indicators.shown || hud.shown
550 }
366 }
551
367
@@ -590,7 +406,9 @@ FocusScope {
590 available: edgeDemo.panelEnabled
406 available: edgeDemo.panelEnabled
591 contentEnabled: edgeDemo.panelContentEnabled
407 contentEnabled: edgeDemo.panelContentEnabled
592 }
408 }
593− fullscreenMode: shell.fullscreenMode
409+ property string focusedAppId: ApplicationManager.focusedApplicationId
410+ property var focusedApplication: ApplicationManager.findApplication(focusedAppId)
411+ fullscreenMode: focusedApplication && focusedApplication.fullscreen
594 searchVisible: !greeter.shown && !lockscreen.shown && dash.shown
412 searchVisible: !greeter.shown && !lockscreen.shown && dash.shown
595
413
596 InputFilterArea {
414 InputFilterArea {
@@ -616,9 +434,8 @@ FocusScope {
616 hideAnimation: StandardAnimation { property: "y"; duration: hud.showableAnimationDuration; to: hudRevealer.closedValue; easing.type: Easing.Linear }
434 hideAnimation: StandardAnimation { property: "y"; duration: hud.showableAnimationDuration; to: hudRevealer.closedValue; easing.type: Easing.Linear }
617
435
618 Connections {
436 Connections {
619− target: shell.applicationManager
437+ target: ApplicationManager
620− onMainStageFocusedApplicationChanged: hud.hide()
438+ onFocusedApplicationIdChanged: hud.hide()
621− onSideStageFocusedApplicationChanged: hud.hide()
622 }
439 }
623 }
440 }
624
441
@@ -643,7 +460,7 @@ FocusScope {
643 theHud: hud
460 theHud: hud
644 anchors.fill: parent
461 anchors.fill: parent
645 enabled: hud.available
462 enabled: hud.available
646− applicationIsOnForeground: applicationFocused
463+ applicationIsOnForeground: ApplicationManager.focusedApplicationId
647 }
464 }
648
465
649 InputFilterArea {
466 InputFilterArea {
@@ -667,7 +484,12 @@ FocusScope {
667 dragAreaWidth: shell.edgeSize
484 dragAreaWidth: shell.edgeSize
668 available: (!greeter.shown || greeter.narrowMode) && edgeDemo.launcherEnabled
485 available: (!greeter.shown || greeter.narrowMode) && edgeDemo.launcherEnabled
669
486
670− onDashItemSelected: showHome()
487+ onDashItemSelected: {
488+ if (edgeDemo.running)
489+ return;
490+
491+ showHome()
492+ }
671 onDash: {
493 onDash: {
672 if (stages.shown) {
494 if (stages.shown) {
673 stages.hide();
495 stages.hide();
@@ -676,6 +498,9 @@ FocusScope {
676 }
498 }
677 onDashSwipeChanged: if (dashSwipe && stages.shown) dash.setCurrentScope("applications.scope", false, true)
499 onDashSwipeChanged: if (dashSwipe && stages.shown) dash.setCurrentScope("applications.scope", false, true)
678 onLauncherApplicationSelected:{
500 onLauncherApplicationSelected:{
501+ if (edgeDemo.running)
502+ return;
503+
679 greeter.hide()
504 greeter.hide()
680 shell.activateApplication(appId)
505 shell.activateApplication(appId)
681 }
506 }
@@ -761,14 +586,14 @@ FocusScope {
761 anchors.bottom: parent.bottom
586 anchors.bottom: parent.bottom
762 anchors.left: parent.left
587 anchors.left: parent.left
763 anchors.right: parent.right
588 anchors.right: parent.right
764− height: shell.applicationManager ? shell.applicationManager.keyboardHeight : 0
589+ height: ApplicationManager.keyboardVisible ? ApplicationManager.keyboardHeight : 0
765
590
766− enabled: shell.applicationManager && shell.applicationManager.keyboardVisible
591+ enabled: ApplicationManager.keyboardVisible
767 }
592 }
768
593
769 Label {
594 Label {
770 anchors.centerIn: parent
595 anchors.centerIn: parent
771− visible: applicationManager.fake
596+ visible: ApplicationManager.fake ? ApplicationManager.fake : false
772 text: "EARLY ALPHA\nNOT READY FOR USE"
597 text: "EARLY ALPHA\nNOT READY FOR USE"
773 color: "lightgrey"
598 color: "lightgrey"
774 opacity: 0.2
599 opacity: 0.2
Stages/PhoneStage.qmlnew file+188
@@ -0,0 +1,188 @@
1+import QtQuick 2.0
2+import Ubuntu.Components 0.1
3+import Unity.Application 0.1
4+import "../Components"
5+
6+/*
7+
8+*/
9+
10+Item {
11+ id: root
12+
13+ // Controls to be set from outside
14+ property bool shown: false
15+ property bool moving: false
16+
17+ // State information propagated to the outside
18+ readonly property bool painting: mainScreenshotImage.visible || fadeInScreenshotImage.visible
19+
20+ onMovingChanged: {
21+ if (moving) {
22+ priv.requestNewScreenshot();
23+ } else {
24+ mainScreenshotImage.visible = false;
25+ }
26+ }
27+
28+ Connections {
29+ target: ApplicationManager
30+
31+ onFocusRequested: {
32+ print("******focus requested*********", appId, "currently focused:", priv.focusedAppId, priv.focusedScreenshot)
33+ mainScreenshotImage.src = priv.focusedScreenshot;
34+ mainScreenshotImage.visible = true;
35+ mainScreenshotImage.anchors.leftMargin = 0;
36+ priv.grantFocus(appId);
37+ }
38+
39+ onFocusedApplicationIdChanged: {
40+ print("*** ApplicationManager.focusedAppId changed appId:", priv.focusedAppId, "shown:", root.shown)
41+
42+ if (ApplicationManager.focusedApplicationId.length > 0) {
43+ priv.applicationStarting = false;
44+ priv.secondApplicationStarting = false;
45+
46+ // There is a new focused one
47+ if (root.shown){
48+ print("starting applicationSwitchingAnimation");
49+ applicationSwitchingAnimation.start();
50+ }
51+ }
52+ }
53+
54+ onApplicationAdded: {
55+ if (!priv.focusedApplication) {
56+ print("*** applicationStarting")
57+ priv.applicationStarting = true;
58+ } else {
59+ print("*** SECONDapplicationStarting")
60+ priv.secondApplicationStarting = true;
61+ }
62+ }
63+ }
64+
65+ QtObject {
66+ id: priv
67+
68+ property string focusedAppId: ApplicationManager.focusedApplicationId
69+ property var focusedApplication: ApplicationManager.findApplication(focusedAppId)
70+ property url focusedScreenshot: focusedApplication ? focusedApplication.screenshot : ""
71+
72+ property bool waitingForScreenshot: false
73+
74+ property bool applicationStarting: false
75+ property bool secondApplicationStarting: false
76+
77+ onFocusedScreenshotChanged: {
78+ waitingForScreenshot = false;
79+ if (root.moving) {
80+ print("*** moving app", ApplicationManager.focusedApplicationId, "setting screenshot to", ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).screenshot)
81+ mainScreenshotImage.src = ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).screenshot;
82+ mainScreenshotImage.visible = true;
83+ }
84+ }
85+
86+ function requestNewScreenshot() {
87+ print("requesting new screenshot for", ApplicationManager.focusedApplicationId);
88+ waitingForScreenshot = true;
89+ ApplicationManager.updateScreenshot(ApplicationManager.focusedApplicationId);
90+ }
91+
92+ function grantFocus(appId) {
93+ grantFocusTimer.appId = appId;
94+ grantFocusTimer.start();
95+ }
96+
97+ }
98+
99+ Timer {
100+ id: grantFocusTimer
101+ interval: 1
102+ repeat: false
103+ property string appId
104+ onTriggered: {
105+ print("*** Calling AppManager.focusApplication(", appId, "). Image is visible:", mainScreenshotImage.visible)
106+ ApplicationManager.focusApplication(appId);
107+ }
108+ }
109+
110+ SequentialAnimation {
111+ id: applicationSwitchingAnimation
112+ // setup
113+ PropertyAction { target: mainScreenshotImage; property: "anchors.leftMargin"; value: 0 }
114+ PropertyAction { target: fadeInScreenshotImage; property: "source"; value: priv.focusedScreenshot }
115+ PropertyAction { target: fadeInScreenshotImage; property: "visible"; value: true }
116+ PropertyAction { target: fadeInScreenshotImage; property: "opacity"; value: 0 }
117+ PropertyAction { target: fadeInScreenshotImage; property: "scale"; value: .8 }
118+
119+ // The actual animation
120+ ParallelAnimation {
121+ UbuntuNumberAnimation { target: mainScreenshotImage; property: "anchors.leftMargin"; to: root.width; duration: UbuntuAnimation.SlowDuration }
122+ UbuntuNumberAnimation { target: fadeInScreenshotImage; property: "opacity"; to: 1; duration: UbuntuAnimation.SlowDuration }
123+ UbuntuNumberAnimation { target: fadeInScreenshotImage; property: "scale"; to: 1; duration: UbuntuAnimation.SlowDuration }
124+ }
125+
126+ // restore stuff
127+ PropertyAction { target: fadeInScreenshotImage; property: "visible"; value: false }
128+ PropertyAction { target: mainScreenshotImage; property: "visible"; value: false }
129+ }
130+
131+ // FIXME: Drop this and make the imageprovider show a splashscreen instead
132+ Rectangle {
133+ id: appSplash
134+ anchors.fill: parent
135+ color: "white"
136+ visible: priv.secondApplicationStarting
137+ }
138+ Image {
139+ id: fadeInScreenshotImage
140+ anchors { left: parent.left; bottom: parent.bottom }
141+ width: parent.width
142+ scale: .7
143+ visible: false
144+// Rectangle { anchors.fill: mainScreenshotImage; color: "green"; opacity: .5 }
145+ }
146+
147+ Rectangle {
148+ id: appSplash2
149+ anchors.fill: parent
150+ color: "red"
151+ visible: priv.applicationStarting
152+ }
153+ Image {
154+ id: mainScreenshotImage
155+ anchors { left: parent.left; bottom: parent.bottom }
156+ width: parent.width
157+
158+ property string src
159+ source: src
160+ visible: false
161+
162+ onVisibleChanged: {
163+ print("------------------------ image visible", visible)
164+ }
165+
166+ onSrcChanged: {
167+ print("*** Main image source changed", src, "visible:", visible)
168+ }
169+
170+ onStatusChanged: {
171+ print("*** Main image status changed", status, source)
172+ }
173+ }
174+// Rectangle { anchors.fill: mainScreenshotImage; color: "blue"; opacity: .5 }
175+
176+ Image {
177+ anchors { top: parent.top; right: parent.right }
178+ height: parent.height / 4
179+ width: parent.width / 4
180+ source: ApplicationManager.findApplication(priv.focusedAppId).screenshot
181+ }
182+ Image {
183+ anchors { bottom: parent.bottom; right: parent.right }
184+ height: parent.height / 4
185+ width: parent.width / 4
186+ source: mainScreenshotImage.src
187+ }
188+}
debian/changelog+62
@@ -1,3 +1,65 @@
1+unity8 (7.84+14.04.20131206.1-0ubuntu1) trusty; urgency=low
2+
3+ [ Michal Hruby ]
4+ * Ensure DashContent's ListView's currentItem is set if the model is
5+ not empty.
6+
7+ [ Michał Sawicz ]
8+ * Don't generate build-deps .deb twice and allow overriding
9+ QML2_IMPORT_PATH in ./run.
10+
11+ [ Albert Astals ]
12+ * Remove unused Applications/ folder .
13+ * Implement an 'interface' for Dash Renderers.
14+ * Use deelistmodel's conversion method Instead of a copy of the code .
15+ * Remove icons we don't use .
16+ * Do not start apps or go to dash on demo If you are pulling the
17+ launcher out while in demo mode it doesn't make sense to let you
18+ start applications. Besides it locks you out because it starts the
19+ app and the demo is still not finished so you can't really use any
20+ of the edges to escape Bug #1233696. (LP: #1233696)
21+ * Add a test for the carousel showing the preview when being clicked .
22+ * Fix collapsing of categories not working and the
23+ expansion/collapsing animation +test.
24+
25+ [ Timo Jyrinki ]
26+ * Depend on either Qt 5.2 or libqt5v8-5-private-dev.
27+ * qtdeclarative5-private-dev 5.0.2-6ubuntu5 now depends directly on Qt
28+ V8 private headers. The remaining "qtdeclarative5-private-dev"
29+ dependency is enough now both when compiling against 5.0.2 or 5.2.
30+
31+ [ Pete Woods ]
32+ * Handle optional parameterized action properties. (LP: #1256258)
33+
34+ [ Andrea Cimitan ]
35+ * Implement an 'interface' for Dash Renderers.
36+
37+ [ Michael Zanetti ]
38+ * Added music preview.
39+ * rename some parameters from desktopFile to appId as scopes are now
40+ changed to give us the appId.
41+ * unhardcode launcher's search paths for .desktop files.
42+ * Enable teasing of the phone greeter even though we have a
43+ lockscreen.
44+ * check if variant is valid to avoid asserting in debug mode when the
45+ connection to AccountsService doesn't work for some reason .
46+
47+ [ Michael Terry ]
48+ * Add the DBus greeter API from the desktop greeter into the unity8
49+ greeter.
50+
51+ [ Daniel d'Andrada ]
52+ * Dash: disable close mode when you click outside app thumbnails To
53+ leave the termination mode you can now just mouse/touch press
54+ anywhere outside the running applications' thumbnails The other way,
55+ which still works, is long-pressing a thumbnail once more. (LP:
56+ #1193414)
57+
58+ [ Ubuntu daily release ]
59+ * Automatic snapshot from revision 579
60+
61+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Fri, 06 Dec 2013 13:15:57 +0000
62+
1unity8 (7.84+14.04.20131128.2-0ubuntu1) trusty; urgency=low
63unity8 (7.84+14.04.20131128.2-0ubuntu1) trusty; urgency=low
2
64
3 [ Michal Hruby ]
65 [ Michal Hruby ]
debian/control−1
@@ -16,7 +16,6 @@ Build-Depends: cmake,
16 libnih-dev,
16 libnih-dev,
17 libpulse-dev,
17 libpulse-dev,
18 libqmenumodel-dev (>= 0.2.7),
18 libqmenumodel-dev (>= 0.2.7),
19− libqt5v8-5-private-dev,
20 libunity-api-dev (>= 7.80.4),
19 libunity-api-dev (>= 7.80.4),
21 libunity-mir-dev,
20 libunity-mir-dev,
22 libunity-scopes-json-def-phone,
21 libunity-scopes-json-def-phone,
graphics/applicationIcons/angrybirds@18.pngdeleted

Binary file not shown.

graphics/applicationIcons/media-player@18.pngdeleted

Binary file not shown.

graphics/applicationIcons/music@18.pngdeleted

Binary file not shown.

graphics/applicationIcons/ski_safari@18.pngdeleted

Binary file not shown.

graphics/applicationIcons/skype@18.pngdeleted

Binary file not shown.

plugins/HudClient/hudclient.cpp+1−16
@@ -243,28 +243,13 @@ void HudClient::modelReady(bool needDisconnect)
243 }
243 }
244}
244}
245
245
246−static QVariant QVariantFromGVariant(GVariant *value)
247−{
248− // Only handle the cases we care for now
249− switch (g_variant_classify(value)) {
250− case G_VARIANT_CLASS_BOOLEAN:
251− return QVariant((bool) g_variant_get_boolean(value));
252− case G_VARIANT_CLASS_DOUBLE:
253− return QVariant(g_variant_get_double(value));
254− case G_VARIANT_CLASS_STRING:
255− return QVariant(QString::fromUtf8(g_variant_get_string(value, NULL)));
256− default:
257− return QVariant();
258− }
259−}
260−
261static void addAttribute(QVariantMap &properties, GMenuModel *menuModel, int item, const char *attribute) {
246static void addAttribute(QVariantMap &properties, GMenuModel *menuModel, int item, const char *attribute) {
262 GVariant *v = g_menu_model_get_item_attribute_value(menuModel, item, attribute, NULL);
247 GVariant *v = g_menu_model_get_item_attribute_value(menuModel, item, attribute, NULL);
263
248
264 if (v == NULL)
249 if (v == NULL)
265 return;
250 return;
266
251
267− properties.insert(attribute, QVariantFromGVariant(v));
252+ properties.insert(attribute, DeeListModel::VariantForData(v));
268 g_variant_unref(v);
253 g_variant_unref(v);
269}
254}
270
255
plugins/LightDM/CMakeLists.txt+2−1
@@ -17,6 +17,7 @@ include_directories(
17
17
18set(QMLPLUGIN_SRC
18set(QMLPLUGIN_SRC
19 ../Utils/qsortfilterproxymodelqml.cpp # FIXME evaluate a more generic approach for using other plugins
19 ../Utils/qsortfilterproxymodelqml.cpp # FIXME evaluate a more generic approach for using other plugins
20+ DBusGreeterList.cpp
20 Greeter.cpp
21 Greeter.cpp
21 plugin.cpp
22 plugin.cpp
22 UsersModel.cpp
23 UsersModel.cpp
@@ -33,7 +34,7 @@ target_link_libraries(LightDM-qml
33 ${LIBUSERMETRICSOUTPUT_LDFLAGS}
34 ${LIBUSERMETRICSOUTPUT_LDFLAGS}
34 )
35 )
35
36
36−qt5_use_modules(LightDM-qml Gui Qml)
37+qt5_use_modules(LightDM-qml DBus Gui Qml)
37
38
38# export the qmldir qmltypes and plugin files
39# export the qmldir qmltypes and plugin files
39export_qmlfiles(LightDM LightDM)
40export_qmlfiles(LightDM LightDM)
plugins/LightDM/DBusGreeterList.cppnew file+77
@@ -0,0 +1,77 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "DBusGreeterList.h"
18+#include "Greeter.h"
19+
20+#include <QDBusMessage>
21+#include <QStringList>
22+
23+DBusGreeterList::DBusGreeterList(Greeter *greeter, const QDBusConnection &connection, const QString &path)
24+ : QObject(greeter),
25+ m_greeter(greeter),
26+ m_connection(connection),
27+ m_path(path)
28+{
29+ connect(m_greeter, SIGNAL(authenticationUserChanged(const QString &)), this, SLOT(authenticationUserChangedHandler(const QString &)));
30+ connect(m_greeter, SIGNAL(promptlessChanged()), this, SLOT(promptlessChangedHandler()));
31+}
32+
33+QString DBusGreeterList::GetActiveEntry() const
34+{
35+ return m_greeter->authenticationUser();
36+}
37+
38+void DBusGreeterList::SetActiveEntry(const QString &entry)
39+{
40+ Q_EMIT m_greeter->requestAuthenticationUser(entry);
41+}
42+
43+bool DBusGreeterList::entryIsLocked() const
44+{
45+ return !m_greeter->promptless();
46+}
47+
48+void DBusGreeterList::authenticationUserChangedHandler(const QString &user)
49+{
50+ notifyPropertyChanged("ActiveEntry", user);
51+ Q_EMIT EntrySelected(user);
52+}
53+
54+void DBusGreeterList::promptlessChangedHandler()
55+{
56+ notifyPropertyChanged("EntryIsLocked", entryIsLocked());
57+ Q_EMIT entryIsLockedChanged();
58+}
59+
60+// Manually emit a PropertiesChanged signal over DBus, because QtDBus
61+// doesn't do it for us on Q_PROPERTIES, oddly enough.
62+void DBusGreeterList::notifyPropertyChanged(const QString& propertyName, const QVariant &value)
63+{
64+ QDBusMessage message;
65+ QVariantMap changedProps;
66+
67+ changedProps.insert(propertyName, value);
68+
69+ message = QDBusMessage::createSignal(m_path,
70+ "org.freedesktop.DBus.Properties",
71+ "PropertiesChanged");
72+ message << "com.canonical.UnityGreeter.List";
73+ message << changedProps;
74+ message << QStringList();
75+
76+ m_connection.send(message);
77+}
plugins/LightDM/DBusGreeterList.hnew file+61
@@ -0,0 +1,61 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef UNITY_DBUSGREETERLIST_H
18+#define UNITY_DBUSGREETERLIST_H
19+
20+#include <QDBusConnection>
21+#include <QObject>
22+
23+class Greeter;
24+class QDBusInterface;
25+
26+/** This is an internal class used to talk with the indicators.
27+ */
28+
29+class DBusGreeterList : public QObject
30+{
31+ Q_OBJECT
32+ Q_CLASSINFO("D-Bus Interface", "com.canonical.UnityGreeter.List")
33+
34+ Q_PROPERTY(QString ActiveEntry READ GetActiveEntry WRITE SetActiveEntry NOTIFY EntrySelected) // since 14.04
35+ Q_PROPERTY(bool EntryIsLocked READ entryIsLocked NOTIFY entryIsLockedChanged) // since 14.04
36+
37+public:
38+ explicit DBusGreeterList(Greeter *greeter, const QDBusConnection &connection, const QString &path);
39+
40+ Q_SCRIPTABLE void SetActiveEntry(const QString &entry); // since 13.04
41+ Q_SCRIPTABLE QString GetActiveEntry() const; // since 13.10
42+
43+ bool entryIsLocked() const;
44+
45+Q_SIGNALS:
46+ Q_SCRIPTABLE void EntrySelected(const QString &entry); // since 13.10
47+
48+ void entryIsLockedChanged();
49+
50+private Q_SLOTS:
51+ void authenticationUserChangedHandler(const QString &user);
52+ void promptlessChangedHandler();
53+ void notifyPropertyChanged(const QString &propertyName, const QVariant &value);
54+
55+private:
56+ Greeter *m_greeter;
57+ QDBusConnection m_connection;
58+ QString m_path;
59+};
60+
61+#endif
plugins/LightDM/Greeter.cpp+1
@@ -88,6 +88,7 @@ void Greeter::authenticate(const QString &username)
88 }
88 }
89
89
90 d->m_greeter->authenticate(username);
90 d->m_greeter->authenticate(username);
91+ Q_EMIT authenticationUserChanged(username);
91}
92}
92
93
93void Greeter::respond(const QString &response)
94void Greeter::respond(const QString &response)
plugins/LightDM/Greeter.h+6−1
@@ -35,7 +35,7 @@ class Greeter : public QObject
35 Q_OBJECT
35 Q_OBJECT
36
36
37 Q_PROPERTY(bool authenticated READ isAuthenticated)
37 Q_PROPERTY(bool authenticated READ isAuthenticated)
38− Q_PROPERTY(QString authenticationUser READ authenticationUser)
38+ Q_PROPERTY(QString authenticationUser READ authenticationUser NOTIFY authenticationUserChanged)
39 Q_PROPERTY(bool promptless READ promptless NOTIFY promptlessChanged)
39 Q_PROPERTY(bool promptless READ promptless NOTIFY promptlessChanged)
40
40
41public:
41public:
@@ -54,8 +54,13 @@ Q_SIGNALS:
54 void showMessage(const QString &text, bool isError);
54 void showMessage(const QString &text, bool isError);
55 void showPrompt(const QString &text, bool isSecret);
55 void showPrompt(const QString &text, bool isSecret);
56 void authenticationComplete();
56 void authenticationComplete();
57+ void authenticationUserChanged(const QString &user);
57 void promptlessChanged();
58 void promptlessChanged();
58
59
60+ // This signal is emitted by external agents like indicators, and the UI
61+ // should switch to this user if possible.
62+ void requestAuthenticationUser(const QString &user);
63+
59private:
64private:
60 GreeterPrivate * const d_ptr;
65 GreeterPrivate * const d_ptr;
61
66
plugins/LightDM/plugin.cpp+14−2
@@ -18,20 +18,32 @@
18 */
18 */
19
19
20#include "plugin.h"
20#include "plugin.h"
21+#include "DBusGreeterList.h"
21#include "Greeter.h"
22#include "Greeter.h"
22#include "UsersModel.h"
23#include "UsersModel.h"
23#include <libusermetricsoutput/ColorTheme.h>
24#include <libusermetricsoutput/ColorTheme.h>
24#include <libusermetricsoutput/UserMetrics.h>
25#include <libusermetricsoutput/UserMetrics.h>
25#include <QLightDM/UsersModel>
26#include <QLightDM/UsersModel>
26
27
27−#include <QtCore/QAbstractItemModel>
28+#include <QAbstractItemModel>
29+#include <QDBusConnection>
28#include <QtQml/qqml.h>
30#include <QtQml/qqml.h>
29
31
32+static const char* GREETER_LIST_DBUS_PATH = "/list";
33+static const char* GREETER_DBUS_SERVICE = "com.canonical.UnityGreeter";
34+
30static QObject *greeter_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
35static QObject *greeter_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
31{
36{
32 Q_UNUSED(engine)
37 Q_UNUSED(engine)
33 Q_UNUSED(scriptEngine)
38 Q_UNUSED(scriptEngine)
39+
40+ Greeter *greeter = new Greeter();
41+ QDBusConnection connection = QDBusConnection::sessionBus();
42+ DBusGreeterList *list = new DBusGreeterList(greeter, connection, GREETER_LIST_DBUS_PATH);
43+ connection.registerObject(GREETER_LIST_DBUS_PATH, list, QDBusConnection::ExportScriptableContents);
44+ connection.registerService(GREETER_DBUS_SERVICE);
45+
34− return new Greeter();
46+ return greeter;
35}
47}
36
48
37static QObject *users_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
49static QObject *users_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
plugins/Unity/Launcher/backend/launcherbackend.cpp+4−2
@@ -183,8 +183,10 @@ void LauncherBackend::syncFromAccounts()
183
183
184 if (m_accounts && !m_user.isEmpty()) {
184 if (m_accounts && !m_user.isEmpty()) {
185 QVariant variant = m_accounts->getUserProperty(m_user, "com.canonical.unity.AccountsService", "launcher-items");
185 QVariant variant = m_accounts->getUserProperty(m_user, "com.canonical.unity.AccountsService", "launcher-items");
186+ if (variant.isValid() && variant.canConvert<QDBusArgument>()) {
186− apps = qdbus_cast<QList<QVariantMap>>(variant.value<QDBusArgument>());
187+ apps = qdbus_cast<QList<QVariantMap>>(variant.value<QDBusArgument>());
187− defaults = isDefaultsItem(apps);
188+ defaults = isDefaultsItem(apps);
189+ }
188 }
190 }
189
191
190 if (m_accounts && defaults) { // Checking accounts as it'll be null when !useStorage
192 if (m_accounts && defaults) { // Checking accounts as it'll be null when !useStorage
po/ar.po+5−5
@@ -8,7 +8,7 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2013-11-29 09:21+0100\n"
10"POT-Creation-Date: 2013-11-29 09:21+0100\n"
11−"PO-Revision-Date: 2013-11-15 12:29+0000\n"
11+"PO-Revision-Date: 2013-12-05 06:55+0000\n"
12"Last-Translator: Ibrahim Saed <ibraheem5000@gmail.com>\n"
12"Last-Translator: Ibrahim Saed <ibraheem5000@gmail.com>\n"
13"Language-Team: Arabic <ar@li.org>\n"
13"Language-Team: Arabic <ar@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
@@ -16,8 +16,8 @@ msgstr ""
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n % 100 >= "
17"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n % 100 >= "
18"3 && n % 100 <= 10 ? 3 : n % 100 >= 11 && n % 100 <= 99 ? 4 : 5;\n"
18"3 && n % 100 <= 10 ? 3 : n % 100 >= 11 && n % 100 <= 99 ? 4 : 5;\n"
19−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
19+"X-Launchpad-Export-Date: 2013-12-06 06:30+0000\n"
20−"X-Generator: Launchpad (build 16856)\n"
20+"X-Generator: Launchpad (build 16863)\n"
21
21
22#: Components/EdgeDemoOverlay.qml:151
22#: Components/EdgeDemoOverlay.qml:151
23msgid "Skip intro"
23msgid "Skip intro"
@@ -154,11 +154,11 @@ msgstr "ابحث"
154#: plugins/Unity/Launcher/launcheritem.cpp:43
154#: plugins/Unity/Launcher/launcheritem.cpp:43
155#: plugins/Unity/Launcher/launcheritem.cpp:73
155#: plugins/Unity/Launcher/launcheritem.cpp:73
156msgid "Lock to Launcher"
156msgid "Lock to Launcher"
157−msgstr ""
157+msgstr "ثبّت في الشريط"
158
158
159#: plugins/Unity/Launcher/launcheritem.cpp:73
159#: plugins/Unity/Launcher/launcheritem.cpp:73
160msgid "Unlock from Launcher"
160msgid "Unlock from Launcher"
161−msgstr ""
161+msgstr "فُك التثبيت من الشريط"
162
162
163#: Shell.qml:439
163#: Shell.qml:439
164#, qt-format
164#, qt-format
po/gl.po+6−6
@@ -8,15 +8,15 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2013-11-29 09:21+0100\n"
10"POT-Creation-Date: 2013-11-29 09:21+0100\n"
11−"PO-Revision-Date: 2013-10-03 18:49+0000\n"
11+"PO-Revision-Date: 2013-12-06 17:15+0000\n"
12"Last-Translator: Marcos Lans <Unknown>\n"
12"Last-Translator: Marcos Lans <Unknown>\n"
13"Language-Team: Galician <gl@li.org>\n"
13"Language-Team: Galician <gl@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2013-11-30 05:33+0000\n"
18+"X-Launchpad-Export-Date: 2013-12-07 06:35+0000\n"
19−"X-Generator: Launchpad (build 16856)\n"
19+"X-Generator: Launchpad (build 16869)\n"
20
20
21#: Components/EdgeDemoOverlay.qml:151
21#: Components/EdgeDemoOverlay.qml:151
22msgid "Skip intro"
22msgid "Skip intro"
@@ -139,7 +139,7 @@ msgstr "Falando…"
139
139
140#: Notifications/NotificationMenuItemFactory.qml:89
140#: Notifications/NotificationMenuItemFactory.qml:89
141msgid "Show password"
141msgid "Show password"
142−msgstr ""
142+msgstr "Mostrar contrasinal"
143
143
144#: Panel/SearchIndicator.qml:27
144#: Panel/SearchIndicator.qml:27
145msgid "Search"
145msgid "Search"
@@ -148,11 +148,11 @@ msgstr "Buscar"
148#: plugins/Unity/Launcher/launcheritem.cpp:43
148#: plugins/Unity/Launcher/launcheritem.cpp:43
149#: plugins/Unity/Launcher/launcheritem.cpp:73
149#: plugins/Unity/Launcher/launcheritem.cpp:73
150msgid "Lock to Launcher"
150msgid "Lock to Launcher"
151−msgstr ""
151+msgstr "Manter no Iniciador"
152
152
153#: plugins/Unity/Launcher/launcheritem.cpp:73
153#: plugins/Unity/Launcher/launcheritem.cpp:73
154msgid "Unlock from Launcher"
154msgid "Unlock from Launcher"
155−msgstr ""
155+msgstr "Non manter no Iniciador"
156
156
157#: Shell.qml:439
157#: Shell.qml:439
158#, qt-format
158#, qt-format
tests/mocks/LightDM/CMakeLists.txt+2−1
@@ -17,6 +17,7 @@ include_directories(
17)
17)
18
18
19set(QMLPLUGIN_SRC
19set(QMLPLUGIN_SRC
20+ ${CMAKE_SOURCE_DIR}/plugins/LightDM/DBusGreeterList.cpp
20 ${CMAKE_SOURCE_DIR}/plugins/LightDM/Greeter.cpp
21 ${CMAKE_SOURCE_DIR}/plugins/LightDM/Greeter.cpp
21 ${CMAKE_SOURCE_DIR}/plugins/LightDM/plugin.cpp
22 ${CMAKE_SOURCE_DIR}/plugins/LightDM/plugin.cpp
22 ${CMAKE_SOURCE_DIR}/plugins/LightDM/UsersModel.cpp
23 ${CMAKE_SOURCE_DIR}/plugins/LightDM/UsersModel.cpp
@@ -39,7 +40,7 @@ target_link_libraries(MockLightDM-qml
39 -lusermetricsoutput
40 -lusermetricsoutput
40 )
41 )
41
42
42−qt5_use_modules(MockLightDM-qml Gui Qml)
43+qt5_use_modules(MockLightDM-qml DBus Gui Qml)
43
44
44# copy qmldir file into build directory for shadow builds
45# copy qmldir file into build directory for shadow builds
45file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/qmldir"
46file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/qmldir"
tests/mocks/Unity/Application/ApplicationInfo.h+1
@@ -74,6 +74,7 @@ class ApplicationInfo : public ApplicationInfoInterface {
74 IMPLEMENT_PROPERTY(fullscreen, Fullscreen, bool)
74 IMPLEMENT_PROPERTY(fullscreen, Fullscreen, bool)
75 IMPLEMENT_PROPERTY(imageQml, ImageQml, QString)
75 IMPLEMENT_PROPERTY(imageQml, ImageQml, QString)
76 IMPLEMENT_PROPERTY(windowQml, WindowQml, QString)
76 IMPLEMENT_PROPERTY(windowQml, WindowQml, QString)
77+ IMPLEMENT_PROPERTY(screenshot, Screenshot, QUrl)
77
78
78 #undef IMPLEMENT_PROPERTY
79 #undef IMPLEMENT_PROPERTY
79
80
tests/mocks/Unity/Application/ApplicationManager.cpp+29
@@ -23,6 +23,9 @@
23#include <QQuickItem>
23#include <QQuickItem>
24#include <QQuickView>
24#include <QQuickView>
25#include <QQmlComponent>
25#include <QQmlComponent>
26+#include <QTimer>
27+#include <QDateTime>
28+#include <QDebug>
26
29
27ApplicationManager::ApplicationManager(QObject *parent)
30ApplicationManager::ApplicationManager(QObject *parent)
28 : ApplicationManagerInterface(parent)
31 : ApplicationManagerInterface(parent)
@@ -170,6 +173,8 @@ ApplicationInfo* ApplicationManager::startApplication(const QString &appId,
170 }
173 }
171 add(application);
174 add(application);
172
175
176+ QMetaObject::invokeMethod(this, "focusApplication", Qt::QueuedConnection, Q_ARG(QString, appId));
177+
173 return application;
178 return application;
174}
179}
175
180
@@ -181,9 +186,31 @@ bool ApplicationManager::stopApplication(const QString &appId)
181
186
182 remove(application);
187 remove(application);
183 Q_EMIT focusedApplicationIdChanged();
188 Q_EMIT focusedApplicationIdChanged();
189+ qDebug() << Q_FUNC_INFO << "emitting focusedAppChanged" << focusedApplicationId();
184 return true;
190 return true;
185}
191}
186
192
193+void ApplicationManager::updateScreenshot(const QString &appId)
194+{
195+ int idx = -1;
196+ ApplicationInfo *application = nullptr;
197+ for (int i = 0; i < m_availableApplications.count(); ++i) {
198+ application = m_availableApplications.at(i);
199+ if (application->appId() == appId) {
200+ idx = i;
201+ break;
202+ }
203+ }
204+
205+ if (idx == -1) {
206+ return;
207+ }
208+
209+ application->setScreenshot(QString("image://application/%1/%2").arg(appId).arg(QDateTime::currentMSecsSinceEpoch()));
210+ QModelIndex appIndex = index(idx);
211+ Q_EMIT dataChanged(appIndex, appIndex, QVector<int>() << RoleScreenshot);
212+}
213+
187QString ApplicationManager::focusedApplicationId() const {
214QString ApplicationManager::focusedApplicationId() const {
188 for (ApplicationInfo *app : m_runningApplications) {
215 for (ApplicationInfo *app : m_runningApplications) {
189 if (app->focused()) {
216 if (app->focused()) {
@@ -237,6 +264,7 @@ bool ApplicationManager::focusApplication(const QString &appId)
237
264
238 // move app to top of stack
265 // move app to top of stack
239 move(m_runningApplications.indexOf(application), 0);
266 move(m_runningApplications.indexOf(application), 0);
267+ qDebug() << Q_FUNC_INFO << "emitting focusedAppChanged" << focusedApplicationId();
240 Q_EMIT focusedApplicationIdChanged();
268 Q_EMIT focusedApplicationIdChanged();
241 return true;
269 return true;
242}
270}
@@ -249,6 +277,7 @@ void ApplicationManager::unfocusCurrentApplication()
249 app->setFocused(false);
277 app->setFocused(false);
250 }
278 }
251 }
279 }
280+ qDebug() << Q_FUNC_INFO << "emitting focusedAppChanged" << focusedApplicationId();
252 Q_EMIT focusedApplicationIdChanged();
281 Q_EMIT focusedApplicationIdChanged();
253}
282}
254
283
tests/mocks/Unity/Application/ApplicationManager.h+1
@@ -92,6 +92,7 @@ class ApplicationManager : public ApplicationManagerInterface {
92 Q_INVOKABLE ApplicationInfo *startApplication(const QString &appId, const QStringList &arguments = QStringList()) override;
92 Q_INVOKABLE ApplicationInfo *startApplication(const QString &appId, const QStringList &arguments = QStringList()) override;
93 Q_INVOKABLE ApplicationInfo *startApplication(const QString &appId, ExecFlags flags, const QStringList &arguments = QStringList());
93 Q_INVOKABLE ApplicationInfo *startApplication(const QString &appId, ExecFlags flags, const QStringList &arguments = QStringList());
94 Q_INVOKABLE bool stopApplication(const QString &appId) override;
94 Q_INVOKABLE bool stopApplication(const QString &appId) override;
95+ Q_INVOKABLE void updateScreenshot(const QString &appId) override;
95
96
96 QString focusedApplicationId() const override;
97 QString focusedApplicationId() const override;
97
98
tests/mocks/Unity/fake_scope.cpp+1−1
@@ -204,7 +204,7 @@ DeeModel* create_categories_model(unsigned category_count) {
204 std::to_string(i).c_str(),
204 std::to_string(i).c_str(),
205 ("Category "+std::to_string(i)).c_str(),
205 ("Category "+std::to_string(i)).c_str(),
206 "gtk-apply",
206 "gtk-apply",
207− "grid",
207+ i % 2 == 0 ? "grid" : "carousel",
208 hints);
208 hints);
209 }
209 }
210 return category_model;
210 return category_model;
tests/plugins/CMakeLists.txt+1
@@ -1,4 +1,5 @@
1add_subdirectory(AccountsService)
1add_subdirectory(AccountsService)
2+add_subdirectory(LightDM)
2add_subdirectory(ListViewWithPageHeader)
3add_subdirectory(ListViewWithPageHeader)
3add_subdirectory(Ubuntu)
4add_subdirectory(Ubuntu)
4add_subdirectory(Unity)
5add_subdirectory(Unity)
tests/plugins/LightDM/CMakeLists.txtnew file+24
@@ -0,0 +1,24 @@
1+add_executable(test-lightdm-dbus
2+ dbus.cpp
3+ ${CMAKE_SOURCE_DIR}/plugins/LightDM/Greeter.cpp
4+ )
5+qt5_use_modules(test-lightdm-dbus Core DBus Quick Test)
6+
7+include_directories(
8+ ${CMAKE_CURRENT_BINARY_DIR}
9+ ${CMAKE_SOURCE_DIR}/plugins/LightDM
10+ ${CMAKE_SOURCE_DIR}/tests/mocks/LightDM
11+ ${LIBLIGHTDM_INCLUDE_DIRS}
12+ )
13+
14+target_link_libraries(test-lightdm-dbus
15+ -L${CMAKE_BINARY_DIR}/tests/mocks/LightDM/full
16+ -llightdm-qt5-2
17+ )
18+
19+add_definitions(-DCURRENT_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}")
20+
21+add_custom_target(testLightDMDBus dbus-launch env QML2_IMPORT_PATH=${CMAKE_BINARY_DIR}/tests/mocks LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/full ${CMAKE_CURRENT_BINARY_DIR}/test-lightdm-dbus)
22+add_dependencies(testLightDMDBus test-lightdm-dbus)
23+
24+add_dependencies(qmluitests testLightDMDBus)
tests/plugins/LightDM/dbus.cppnew file+166
@@ -0,0 +1,166 @@
1+/*
2+ * Copyright (C) 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "Greeter.h"
18+
19+#include <QDBusInterface>
20+#include <QDBusReply>
21+#include <QSignalSpy>
22+#include <QQuickItem>
23+#include <QQuickView>
24+#include <QtTestGui>
25+
26+class GreeterDBusTest : public QObject
27+{
28+ Q_OBJECT
29+
30+Q_SIGNALS:
31+ void PropertiesChangedRelay(const QString &interface, const QVariantMap &changed, const QStringList &invalidated);
32+
33+private Q_SLOTS:
34+
35+ void initTestCase()
36+ {
37+ // Qt doesn't like us connecting to PropertiesChanged using normal
38+ // SIGNAL method, because QtDBus doesn't know about PropertiesChanged.
39+ // So we connect the hard way for the benefit of any tests that want
40+ // to watch.
41+ QDBusConnection::sessionBus().connect(
42+ "com.canonical.UnityGreeter",
43+ "/list",
44+ "org.freedesktop.DBus.Properties",
45+ "PropertiesChanged",
46+ this,
47+ SIGNAL(PropertiesChangedRelay(const QString&, const QVariantMap&, const QStringList&)));
48+ }
49+
50+ void init()
51+ {
52+ view = new QQuickView();
53+ view->setSource(QUrl::fromLocalFile(CURRENT_SOURCE_DIR "/greeter.qml"));
54+ greeter = dynamic_cast<Greeter*>(view->rootObject()->property("greeter").value<QObject*>());
55+ QVERIFY(greeter);
56+ QVERIFY(greeter->authenticationUser() == "");
57+ view->show();
58+ QTest::qWaitForWindowExposed(view);
59+
60+ dbusList = new QDBusInterface("com.canonical.UnityGreeter",
61+ "/list",
62+ "com.canonical.UnityGreeter.List",
63+ QDBusConnection::sessionBus(), view);
64+ QVERIFY(dbusList->isValid());
65+ }
66+
67+ void cleanup()
68+ {
69+ delete view;
70+ }
71+
72+ void testGetActiveEntry()
73+ {
74+ greeter->authenticate("has-password");
75+
76+ QDBusReply<QString> reply = dbusList->call("GetActiveEntry");
77+ QVERIFY(reply.isValid());
78+ QVERIFY(reply.value() == "has-password");
79+ }
80+
81+ void testSetActiveEntry()
82+ {
83+ QSignalSpy spy(greeter, SIGNAL(requestAuthenticationUser(QString)));
84+ QDBusReply<void> reply = dbusList->call("SetActiveEntry", "has-password");
85+ QVERIFY(reply.isValid());
86+ spy.wait();
87+
88+ QCOMPARE(spy.count(), 1);
89+ QList<QVariant> arguments = spy.takeFirst();
90+ QVERIFY(arguments.at(0).toString() == "has-password");
91+ }
92+
93+ void testEntrySelectedSignal()
94+ {
95+ QSignalSpy spy(dbusList, SIGNAL(EntrySelected(QString)));
96+ greeter->authenticate("has-password");
97+ spy.wait();
98+
99+ QCOMPARE(spy.count(), 1);
100+ QList<QVariant> arguments = spy.takeFirst();
101+ QVERIFY(arguments.at(0).toString() == "has-password");
102+ }
103+
104+ void testActiveEntryGet()
105+ {
106+ greeter->authenticate("has-password");
107+ QVERIFY(dbusList->property("ActiveEntry").toString() == "has-password");
108+ }
109+
110+ void testActiveEntrySet()
111+ {
112+ QSignalSpy spy(greeter, SIGNAL(requestAuthenticationUser(QString)));
113+ QVERIFY(dbusList->setProperty("ActiveEntry", "has-password"));
114+ spy.wait();
115+
116+ QCOMPARE(spy.count(), 1);
117+ QList<QVariant> arguments = spy.takeFirst();
118+ QVERIFY(arguments.at(0).toString() == "has-password");
119+ }
120+
121+ void testActiveEntryChanged()
122+ {
123+ QSignalSpy spy(this, SIGNAL(PropertiesChangedRelay(QString, QVariantMap, QStringList)));
124+ greeter->authenticate("has-password");
125+ spy.wait();
126+
127+ QCOMPARE(spy.count(), 1);
128+ QList<QVariant> arguments = spy.takeFirst();
129+ QVERIFY(arguments.at(0).toString() == "com.canonical.UnityGreeter.List");
130+ QVERIFY(arguments.at(1).toMap().contains("ActiveEntry"));
131+ QVERIFY(arguments.at(1).toMap()["ActiveEntry"] == "has-password");
132+ }
133+
134+ void testEntryIsLockedGet()
135+ {
136+ QVERIFY(dbusList->property("EntryIsLocked").toBool());
137+
138+ greeter->authenticate("no-password");
139+ QVERIFY(!dbusList->property("EntryIsLocked").toBool());
140+
141+ greeter->authenticate("has-password");
142+ QVERIFY(dbusList->property("EntryIsLocked").toBool());
143+ }
144+
145+ void testEntryIsLockedChanged()
146+ {
147+ QSignalSpy spy(this, SIGNAL(PropertiesChangedRelay(QString, QVariantMap, QStringList)));
148+ greeter->authenticate("no-password");
149+ spy.wait();
150+
151+ QCOMPARE(spy.count(), 2); // once for locked, once for user; first will be locked mode
152+ QList<QVariant> arguments = spy.takeFirst();
153+ QVERIFY(arguments.at(0).toString() == "com.canonical.UnityGreeter.List");
154+ QVERIFY(arguments.at(1).toMap().contains("EntryIsLocked"));
155+ QVERIFY(arguments.at(1).toMap()["EntryIsLocked"] == false);
156+ }
157+
158+private:
159+ QQuickView *view;
160+ Greeter *greeter;
161+ QDBusInterface *dbusList;
162+};
163+
164+QTEST_MAIN(GreeterDBusTest)
165+
166+#include "dbus.moc"
tests/plugins/LightDM/greeter.qmlnew file+22
@@ -0,0 +1,22 @@
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+
17+import QtQuick 2.0
18+import LightDM 0.1 as LightDM
19+
20+Item {
21+ property var greeter: LightDM.Greeter
22+}
tests/plugins/Unity/Launcher/launchermodeltest.cpp+2
@@ -39,6 +39,7 @@ public:
39 ApplicationInfoInterface::Stage stage() const { return ApplicationInfoInterface::MainStage; }
39 ApplicationInfoInterface::Stage stage() const { return ApplicationInfoInterface::MainStage; }
40 ApplicationInfoInterface::State state() const { return ApplicationInfoInterface::Running; }
40 ApplicationInfoInterface::State state() const { return ApplicationInfoInterface::Running; }
41 bool focused() const { return m_focused; }
41 bool focused() const { return m_focused; }
42+ QUrl screenshot() const { return QUrl(); }
42
43
43 // Methods used for mocking (not in the interface)
44 // Methods used for mocking (not in the interface)
44 void setFocused(bool focused) { m_focused = focused; Q_EMIT focusedChanged(focused); }
45 void setFocused(bool focused) { m_focused = focused; Q_EMIT focusedChanged(focused); }
@@ -92,6 +93,7 @@ public:
92 m_list.takeAt(index)->deleteLater();
93 m_list.takeAt(index)->deleteLater();
93 endRemoveRows();
94 endRemoveRows();
94 }
95 }
96+ void updateScreenshot(const QString &appId) { Q_UNUSED(appId); }
95
97
96private:
98private:
97 QList<MockApp*> m_list;
99 QList<MockApp*> m_list;
tests/qmltests/Dash/tst_GenericScopeView.qml+54
@@ -19,6 +19,7 @@ import QtTest 1.0
19import Unity 0.1
19import Unity 0.1
20import ".."
20import ".."
21import "../../../Dash"
21import "../../../Dash"
22+import "../../../Components"
22import Ubuntu.Components 0.1
23import Ubuntu.Components 0.1
23import Unity.Test 0.1 as UT
24import Unity.Test 0.1 as UT
24
25
@@ -43,6 +44,7 @@ Item {
43 GenericScopeView {
44 GenericScopeView {
44 id: genericScopeView
45 id: genericScopeView
45 anchors.fill: parent
46 anchors.fill: parent
47+ searchHistory: SearchHistoryModel {}
46
48
47 UT.UnityTestCase {
49 UT.UnityTestCase {
48 name: "GenericScopeView"
50 name: "GenericScopeView"
@@ -222,6 +224,58 @@ Item {
222 genericScopeView.scope = scopes.get(0)
224 genericScopeView.scope = scopes.get(0)
223 tryCompare(genericScopeView.scope, "searchQuery", "")
225 tryCompare(genericScopeView.scope, "searchQuery", "")
224 }
226 }
227+
228+ function test_filter_expand_collapse() {
229+ // wait for the item to be there
230+ tryCompareFunction(function() { return findChild(genericScopeView, "dashSectionHeader0") != undefined; }, true);
231+
232+ var header = findChild(genericScopeView, "dashSectionHeader0")
233+ var category = findChild(genericScopeView, "dashCategory0")
234+
235+ waitForRendering(header);
236+ verify(category.expandable);
237+ verify(category.filtered);
238+
239+ var initialHeight = category.height;
240+ var middleHeight;
241+ mouseClick(header, header.width / 2, header.height / 2);
242+ tryCompareFunction(function() { middleHeight = category.height; return category.height > initialHeight; }, true);
243+ tryCompare(category, "filtered", false);
244+ verify(category.height > middleHeight);
245+
246+ mouseClick(header, header.width / 2, header.height / 2);
247+ verify(category.expandable);
248+ tryCompare(category, "filtered", true);
249+ }
250+
251+ function test_showPreviewCarousel() {
252+ tryCompareFunction(function() { return findChild(genericScopeView, "carouselDelegate") != undefined; }, true);
253+ var tile = findChild(genericScopeView, "carouselDelegate");
254+ mouseClick(tile, tile.width / 2, tile.height / 2);
255+ var openEffect = findChild(genericScopeView, "openEffect");
256+ tryCompare(openEffect, "gap", 1);
257+
258+ // check for it opening successfully
259+ var previewListView = findChild(genericScopeView, "previewListView");
260+ var currentPreviewItem = findChild(genericScopeView, "previewLoader0");
261+ tryCompareFunction(function() {
262+ var parts = currentPreviewItem.source.toString().split("/");
263+ var name = parts[parts.length - 1];
264+ return name == "DashPreviewPlaceholder.qml";
265+ },
266+ true);
267+ tryCompareFunction(function() {
268+ var parts = currentPreviewItem.source.toString().split("/");
269+ var name = parts[parts.length - 1];
270+ return name == "GenericPreview.qml";
271+ },
272+ true);
273+ tryCompare(currentPreviewItem, "progress", 1);
274+ tryCompare(previewListView, "open", true);
275+
276+ closePreview();
277+ tryCompare(previewListView, "open", false);
278+ }
225 }
279 }
226 }
280 }
227}
281}
tests/qmltests/Greeter/tst_Phone.qml+1−1
@@ -103,7 +103,7 @@ Item {
103 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));
104 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
105
105
106− if (!data.locked && !data.moved) {
106+ if (!data.moved) {
107 // 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
108 // because animation duration and teaseTimer are the same duration and
108 // because animation duration and teaseTimer are the same duration and
109 // might cause slight offsets
109 // might cause slight offsets
tests/qmltests/Greeter/tst_Tablet.qml+11
@@ -324,5 +324,16 @@ Item {
324 tryCompare(greeter, "rightTeaserPressed", false)
324 tryCompare(greeter, "rightTeaserPressed", false)
325 }
325 }
326
326
327+ function test_dbus_set_active_entry() {
328+ select_user("no-password") // to guarantee a selected signal
329+ selectionSpy.clear()
330+ LightDM.Greeter.requestAuthenticationUser("has-password")
331+
332+ selectionSpy.wait()
333+ tryCompare(selectionSpy, "count", 1)
334+
335+ var userlist = findChild(greeter, "userList")
336+ compare(greeter.model.data(userlist.currentIndex, LightDM.UserRoles.NameRole), "has-password")
337+ }
327 }
338 }
328}
339}