Merge lp:~mzanetti/unity/phablet-folding-launcher into lp:unity/phablet

Proposed by Michael Zanetti
Status: Merged
Approved by: Daniel d'Andrada
Approved revision: no longer in the source branch.
Merged at revision: 732
Proposed branch: lp:~mzanetti/unity/phablet-folding-launcher
Merge into: lp:unity/phablet
Diff against target: 1923 lines (+902/-677)
18 files modified
Launcher/Launcher.qml (+161/-337)
Launcher/LauncherDelegate.qml (+127/-0)
Launcher/LauncherPanel.qml (+82/-279)
Shell.qml (+13/-21)
plugins/Ubuntu/Gestures/DirectionalDragArea.cpp (+11/-4)
plugins/Ubuntu/Gestures/DirectionalDragArea.h (+3/-2)
plugins/Unity/CMakeLists.txt (+1/-0)
plugins/Unity/launchermodel.cpp (+153/-0)
plugins/Unity/launchermodel.h (+86/-0)
plugins/Unity/plugin.cpp (+3/-0)
tests/autopilot/qml_phone_shell/tests/helpers.py (+1/-1)
tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp (+3/-1)
tests/qmltests/CMakeLists.txt (+1/-1)
tests/qmltests/Launcher/tst_Launcher.qml (+17/-31)
tests/qmltests/plugins/Unity/CMakeLists.txt (+1/-0)
tests/qmltests/plugins/Unity/fake_launchermodel.cpp (+150/-0)
tests/qmltests/plugins/Unity/fake_launchermodel.h (+86/-0)
tests/qmltests/plugins/Unity/fake_unity_plugin.cpp (+3/-0)
To merge this branch: bzr merge lp:~mzanetti/unity/phablet-folding-launcher
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Daniel d'Andrada (community) Approve
Michał Sawicz Needs Information
Review via email: mp+166000@code.launchpad.net

Commit message

refactored launcher
- moved dummy data one level towards real implementation
- throw away all unused demo experiments from the launcher
- add folding effect
- preliminary (and disabled) drag'n'drop feature for icons

Description of the change

Ugly dummy data will of course be removed again when switching to real API.

preliminary drag and drop support in there is disabled as its not really working anyways. This will be fixed/completed in next steps.

This also fixes a signal in the DirectionalDragArea. As dragging is defined by (undecided || recognized) a switch to rejected should signal the ending of the drag.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Michał Sawicz (saviq) wrote :

My 2¢ here:
* feels like the long-drag-launcher-to-dash-apps has an unnecessary delay (behavior?) between the finger and the app edge
* when (un)folding, the icons change in distance from the adjacent, unfolded icon - that's not necessarily bad, just noting to make sure that's ok with design

review: Needs Information
Revision history for this message
Daniel d'Andrada (dandrader) wrote :

Good refactoring and clean up. Looks way better now!

=====================================================

Icons are not getting highlighted when pressed. The only way I could get an icon highlighted was by long-pressing it.

=====================================================

The branch alone works fine, but if I merge it with latest trunk it get the following wrong behavior:
I have an application on foreground. Right from the beginning of a left-edge drag the application window slides rightwards instead of only after the launcher is fully revealed.

======================================================

+ MouseArea {
+ id: closeMouseArea
+ enabled: root.state == "visible"
+ anchors.fill: panel
+ anchors.rightMargin: -units.gu(2)
+ drag {
+ axis: Drag.XAxis
+ maximumX: 0
+ target: panel
+ }
+
+ onReleased: {
+ if (panel.x < -panel.width/3) {
+ root.switchToNextState("")
+ } else {
+ root.switchToNextState("visible")
             }
         }
- }

- function hide() {
- launcher.state = ""
+ }
+ MouseArea {
+ anchors {
+ left: closeMouseArea.right
+ top: parent.top
+ right: parent.right
+ bottom: parent.bottom
+ }
+ enabled: root.state == "visible"
+ onPressed: {
+ root.state = ""
+ }
     }

I think second MouseArea should be the one named "closeMouseArea" (as it closes the launcher when clicked), not the first one.

============================================================

+ if (distance > root.width / 2){

Coding style: spacing between ')' and '{'

============================================================

- default: // Rejected
+ case Rejected:
+ Q_EMIT draggingChanged(false);
+ break;
+ default:
             // no-op
             break;

Since the default case now deals specifically with invalid states, we could make it issue a qFatal().

============================================================

You can remove this line from Launcher.qml:

import "../Applications/applications.js" as ApplicationsModel

And this one from tst_Launcher.qml:

import "../../../Applications/applications.js" as ApplicationsModel

============================================================

In Shell.qml:

"""
+ InputFilterArea {
+ blockInput: launcher.shown
+ anchors {
+ top: parent.top
+ bottom: parent.bottom
+ left: parent.left
+ }
+ width: launcher.shown ? launcher.width : launcher.panelWidth
+ }

"""

Why not make width just:

+ width: launcher.width

Afterall it only works when launcher.shown is true anyway.

============================================================

tst_Launcher fails to run due to missing symbols, related to "import Unity 0.1".

review: Needs Fixing
Revision history for this message
Michael Zanetti (mzanetti) wrote :

> My 2¢ here:
> * feels like the long-drag-launcher-to-dash-apps has an unnecessary delay
> (behavior?) between the finger and the app edge

Hmm... might be this:
Behavior on x {SmoothedAnimation{velocity: 600}}

However, I did not touch this and the current launcher has the same. If it would be just some NumberAnimation I would disable it while dragging, but as it is a SmoothedAnimation with some velocity it seems kinda intentional. I'll have a chat with Vesa about it.

> * when (un)folding, the icons change in distance from the adjacent, unfolded
> icon - that's not necessarily bad, just noting to make sure that's ok with
> design

Hmm... this seems to be pretty much the same distance as it is on the desktop. However, next steps in the design work is to figure how many folded items should be visible. Depending on how the others will fade out, this most likely will still change by design.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Michael Zanetti (mzanetti) wrote :
Download full text (3.4 KiB)

> Icons are not getting highlighted when pressed. The only way I could get an
> icon highlighted was by long-pressing it.

fixed.

> =====================================================
>
> The branch alone works fine, but if I merge it with latest trunk it get the
> following wrong behavior:
> I have an application on foreground. Right from the beginning of a left-edge
> drag the application window slides rightwards instead of only after the
> launcher is fully revealed.

Hmm... Can't reproduce. After merging it still seems to be the same for me.

> ======================================================
>
> + MouseArea {
> + id: closeMouseArea
> + enabled: root.state == "visible"
> + anchors.fill: panel
> + anchors.rightMargin: -units.gu(2)
> + drag {
> + axis: Drag.XAxis
> + maximumX: 0
> + target: panel
> + }
> +
> + onReleased: {
> + if (panel.x < -panel.width/3) {
> + root.switchToNextState("")
> + } else {
> + root.switchToNextState("visible")
> }
> }
> - }
>
> - function hide() {
> - launcher.state = ""
> + }
> + MouseArea {
> + anchors {
> + left: closeMouseArea.right
> + top: parent.top
> + right: parent.right
> + bottom: parent.bottom
> + }
> + enabled: root.state == "visible"
> + onPressed: {
> + root.state = ""
> + }
> }
>
> I think second MouseArea should be the one named "closeMouseArea" (as it
> closes the launcher when clicked), not the first one.

Fixed.

> ============================================================
>
> + if (distance > root.width / 2){
>
> Coding style: spacing between ')' and '{'

Fixed.

> ============================================================
>
> - default: // Rejected
> + case Rejected:
> + Q_EMIT draggingChanged(false);
> + break;
> + default:
> // no-op
> break;
>
> Since the default case now deals specifically with invalid states, we could
> make it issue a qFatal().

How I read it, the default case is still valid for the Recognized state.

> ============================================================
>
> You can remove this line from Launcher.qml:
>
> import "../Applications/applications.js" as ApplicationsModel
>
> And this one from tst_Launcher.qml:
>
> import "../../../Applications/applications.js" as ApplicationsModel

fixed.

> ============================================================
>
> In Shell.qml:
>
> """
> + InputFilterArea {
> + blockInput: launcher.shown
> + anchors {
> + top: parent.top
> + bottom: parent.bottom
> + left: parent.left
> + }
> + width: launcher.shown ? launcher.width : launcher.panelWidth
> + }
>
> """
>
> Why not make width just:
>
> + width: launcher.width
>
> Afterall it only works when launcher.shown is true anyway.

Fixed.

> ==================================================...

Read more...

Revision history for this message
Daniel d'Andrada (dandrader) wrote :

> >
> > The branch alone works fine, but if I merge it with latest trunk it get the
> > following wrong behavior:
> > I have an application on foreground. Right from the beginning of a left-edge
> > drag the application window slides rightwards instead of only after the
> > launcher is fully revealed.
>
> Hmm... Can't reproduce. After merging it still seems to be the same for me.

Well, it's also working fine for me now. Let's forget about it then. :)

> >
> > - default: // Rejected
> > + case Rejected:
> > + Q_EMIT draggingChanged(false);
> > + break;
> > + default:
> > // no-op
> > break;
> >
> > Since the default case now deals specifically with invalid states, we could
> > make it issue a qFatal().
>
> How I read it, the default case is still valid for the Recognized state.

Right!

Revision history for this message
Daniel d'Andrada (dandrader) wrote :

One last detail I just spotted:

plugins/Unity/launchermodel.cpp:56:5: warning: unused parameter ‘parent’ [-Wunused-parameter]

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Michael Zanetti (mzanetti) wrote :

> plugins/Unity/launchermodel.cpp:56:5: warning: unused parameter ‘parent’
> [-Wunused-parameter]

fixed

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Daniel d'Andrada (dandrader) wrote :

All good.

Do you think it makes sense to fix those warnings that are output when tst_Launcher.qml is run?

review: Approve
Revision history for this message
Michael Zanetti (mzanetti) wrote :

> Do you think it makes sense to fix those warnings that are output when
> tst_Launcher.qml is run?

Done.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Launcher/Launcher.qml'
2--- Launcher/Launcher.qml 2013-05-27 15:12:07 +0000
3+++ Launcher/Launcher.qml 2013-06-04 12:32:22 +0000
4@@ -18,401 +18,225 @@
5 import "../Components"
6 import Ubuntu.Components 0.1
7 import Ubuntu.Gestures 0.1
8-import "../Applications/applications.js" as ApplicationsModel
9+import Unity 0.1
10
11 Item {
12- id: launcher
13-
14- property var favourites: ["/usr/share/applications/phone-app.desktop",
15- "/usr/share/applications/camera-app.desktop",
16- "/usr/share/applications/gallery-app.desktop",
17- "/usr/share/applications/facebook-webapp.desktop",
18- "/usr/share/applications/webbrowser-app.desktop",
19- "/usr/share/applications/twitter-webapp.desktop",
20- "/usr/share/applications/gmail-webapp.desktop",
21- "/usr/share/applications/ubuntu-weather-app.desktop",
22- "/usr/share/applications/notes-app.desktop",
23- "/usr/share/applications/ubuntu-calendar-app.desktop"]
24- property ListModel model: ListModel {}
25- property ListModel applicationsRunning: ListModel {}
26- property int lastFavouriteIndex: -1
27+ id: root
28
29 property bool available: true // can be used to disable all interactions
30- property bool applicationFocused: false
31-
32- property bool shown: launcher.state != ""
33- property int progress
34- progress: {
35- if (launcher.state == "spreadMoving")
36- return dragArea.distance
37- else if (launcher.state == "spread")
38- return launcher.width
39- else
40- return 0
41- }
42-
43- // Thresholds and dimensions
44- property int shortcutsHintWidth: units.gu(3)
45- property int shortcutsWidth: units.gu(6)
46- property int shortcutsThreshold: units.dp(2)
47- property int shortcutsCloseThreshold: units.gu(1)
48- property int spreadHintThreshold: shortcutsWidth
49- property int spreadOutThreshold: units.gu(18)
50-
51- // For how long (in milliseconds) the launcher will stay up without being
52- // interacted with, before it finally hides itself.
53- property int inactivityTimeout: 8000
54-
55- property string iconPath: "."
56-
57- property int launcherProgressValue
58- launcherProgressValue: {
59- if (launcher.state == ""
60- || launcher.state == "hint"
61- || launcher.state == "moving"
62- || launcher.state == "shortcuts") {
63- return 0
64- } else if (launcher.state == "spreadHint") {
65- return units.gu(17)
66- } else if (launcher.state == "spreadMoving") {
67- return units.gu(22)
68- } else if (launcher.state == "spread") {
69- return units.gu(28)
70- }
71- }
72
73 property bool teasing: false
74-
75- signal launcherApplicationSelected(string name)
76- signal dashItemSelected(int itemIndex)
77-
78+ property int panelWidth: units.gu(8.5)
79+ property int dragAreaWidth: units.gu(1)
80+ property real progress: dragArea.dragging && dragArea.touchX > panel.width ? dragArea.touchX : 0
81+
82+ readonly property bool shown: panel.x > -panel.width
83+
84+ // emitted when an application is selected
85+ signal launcherApplicationSelected(string desktopFile)
86+
87+ // emitted when the apps dash should be shown because of a swipe gesture
88 signal dash()
89
90+ // emitted when the dash icon in the launcher has been tapped
91+ signal dashItemSelected()
92+
93+ onStateChanged: {
94+ if (state == "") {
95+ dismissTimer.stop()
96+ } else {
97+ dismissTimer.restart()
98+ }
99+ }
100+
101+ function hide() {
102+ state = ""
103+ }
104+
105+ function switchToNextState(state) {
106+ animateTimer.nextState = state
107+ animateTimer.start();
108+ }
109+
110 onTeasingChanged: {
111 if (teasing) {
112- hintTimer.restart();
113+ teaseTimer.start();
114 }
115 }
116
117 Timer {
118- id: hintTimer
119+ id: teaseTimer
120 interval: 200
121 }
122
123- // internal properties hidden from public api
124- QtObject {
125- id: priv
126-
127- property bool shortcutAnimationOngoing
128- property string previousStateHelp
129- property string previousState: ""
130- property string teasing: launcher.teasing || hintTimer.running
131- }
132-
133- onStateChanged: {
134- priv.previousState = priv.previousStateHelp
135- priv.previousStateHelp = state
136- if (launcher.state != "shorcuts") {
137- dismissTimer.stop()
138- }
139-
140- if (launcher.state == "spread") {
141- dragArea.visible = false
142- hideTimer.restart()
143- dragArea.visible = true
144- }
145- //console.log("Launcher state: " + state)
146- }
147-
148- Component.onCompleted: {
149- __populateWithFavouriteApplications();
150- }
151-
152- function __populateWithFavouriteApplications() {
153- for (var i=0; i<launcher.favourites.length; i++) {
154- var desktopFile = launcher.favourites[i];
155- var application = ApplicationsModel.__availableApplications[desktopFile];
156- if (application) {
157- application["desktopFile"] = desktopFile;
158- launcher.model.append(application);
159- }
160- }
161- }
162-
163- function hide() {
164- launcher.state = ""
165+ Timer {
166+ id: dismissTimer
167+ interval: 5000
168+ onTriggered: {
169+ if (!panel.moving) {
170+ root.state = ""
171+ } else {
172+ dismissTimer.restart()
173+ }
174+ }
175+ }
176+
177+ // Because the animation on x is disabled while dragging
178+ // switching state directly in the drag handlers would not animate
179+ // the completion of the hide/reveal gesture. Lets update the state
180+ // machine and switch to the final state in the next event loop run
181+ Timer {
182+ id: animateTimer
183+ interval: 1
184+ property string nextState: ""
185+ onTriggered: {
186+ // switching to an intermediate state here to make sure all the
187+ // values are restored, even if we were already in the target state
188+ root.state = "tmp"
189+ root.state = nextState
190+ }
191+ }
192+
193+ LauncherModel {
194+ id: launcherModel
195+ }
196+
197+ MouseArea {
198+ id: launcherDragArea
199+ enabled: root.state == "visible"
200+ anchors.fill: panel
201+ anchors.rightMargin: -units.gu(2)
202+ drag {
203+ axis: Drag.XAxis
204+ maximumX: 0
205+ target: panel
206+ }
207+
208+ onReleased: {
209+ if (panel.x < -panel.width/3) {
210+ root.switchToNextState("")
211+ } else {
212+ root.switchToNextState("visible")
213+ }
214+ }
215+
216+ }
217+ MouseArea {
218+ id: closeMouseArea
219+ anchors {
220+ left: launcherDragArea.right
221+ top: parent.top
222+ right: parent.right
223+ bottom: parent.bottom
224+ }
225+ enabled: root.state == "visible"
226+ onPressed: {
227+ root.state = ""
228+ }
229 }
230
231 Rectangle {
232 id: backgroundShade
233 anchors.fill: parent
234 color: "black"
235- opacity: {
236- if (launcher.state == "" || launcher.state == "hint" || launcher.state == "moving") {
237- return 0
238- } else return 0.4
239- }
240- visible: opacity != 0
241+ opacity: root.state == "visible" ? 0.4 : 0
242
243 Behavior on opacity { NumberAnimation { duration: 200; easing.type: Easing.OutQuad} }
244-
245- MouseArea {
246- anchors.fill: parent
247- onClicked: {
248- launcher.state = ""
249- }
250- visible: launcher.state != "spread"
251- }
252 }
253
254- LauncherShortcuts {
255- id: shortcuts
256- objectName: "shortcuts"
257- width: shortcutsWidth
258+ LauncherPanel {
259+ id: panel
260+ objectName: "launcherPanel"
261+ width: root.panelWidth
262 anchors {
263 top: parent.top
264 bottom: parent.bottom
265 }
266 x: -width
267- model: launcher.model
268+ model: launcherModel
269+
270+ property bool animate: true
271+
272 onApplicationSelected: {
273- launcher.state = ""
274+ root.state = ""
275 launcherApplicationSelected(desktopFile)
276 }
277 onDashItemSelected: {
278- launcher.state = ""
279- launcher.dashItemSelected(index)
280- }
281- iconPath: launcher.iconPath
282-
283- Behavior on x { NumberAnimation {duration: launcher.state == "hint" ? 70 : launcher.state == "moving" ? 0 : 300; easing.type: Easing.OutCubic } }
284+ root.state = ""
285+ root.dashItemSelected(index)
286+ }
287+
288+ onMovingChanged: {
289+ if (dismissTimer.running) {
290+ dismissTimer.restart();
291+ }
292+ }
293+
294+ Behavior on x {
295+ NumberAnimation {
296+ // Disabling animation when dragging
297+ duration: dragArea.dragging || launcherDragArea.drag.active ? 0 : 300;
298+ easing.type: Easing.OutCubic
299+ }
300+ }
301 }
302
303 DirectionalDragArea {
304 id: dragArea
305- enabled: launcher.state != "shortcuts" || dragging
306
307 direction: DirectionalDragArea.Rightwards
308
309 // values to be tweaked and later removed from here and set in stone as defaults
310 // once we are confident it's all good.
311 maxDeviation: units.gu(1.5)
312- wideningAngle: 10
313+ wideningAngle: 30
314 distanceThreshold: units.gu(4)
315
316- visible: launcher.available
317- width: {
318- if (launcher.state == "") {
319- return shortcutsThreshold
320- } else {
321- return shortcutsWidth
322+ enabled: root.available && root.state !== "visible"
323+ width: root.dragAreaWidth
324+ height: root.height
325+
326+ onTouchXChanged: {
327+ if (dragging) {
328+ panel.x = Math.min(0, touchX - panel.width);
329 }
330 }
331- height: launcher.height
332
333 onDraggingChanged: {
334- if (dragging)
335- onDragStarted()
336- else
337- onDragEnded()
338- }
339-
340- function onDragStarted() {
341- if (launcher.state == "") launcher.state = "hint"
342- dismissTimer.stop()
343- }
344-
345- onDistanceChanged: {
346- if (launcher.state == "shortcuts" && distance < shortcutsCloseThreshold) {
347- launcher.state = ""
348- } else if (launcher.state == "hint" && distance > 0 ) {
349- launcher.state = "moving"
350- } else if (distance > shortcutsThreshold && distance < spreadHintThreshold) {
351- launcher.state = "shortcuts"
352- } else if (distance >= spreadHintThreshold && distance < shortcutsWidth) {
353- if (launcher.applicationFocused){
354- launcher.state = "spreadHint"
355- }
356- } else if (distance >= shortcutsWidth && distance < spreadOutThreshold) {
357- if (launcher.applicationFocused) {
358- launcher.state = "spreadMoving"
359+ if (!dragging) {
360+ if (distance > root.width / 2) {
361+ root.dash()
362+ root.switchToNextState("")
363+ } else if (distance > panel.width/2) {
364+ root.switchToNextState("visible")
365 } else {
366- dismissTimer.restart()
367- }
368- } else if (distance >= spreadOutThreshold && launcher.applicationFocused) {
369- launcher.state = "spread"
370- }
371- }
372-
373- function onDragEnded() {
374- var currentApplicationIndex
375- if (launcher.state == "shortcuts" && !priv.shortcutAnimationOngoing) {
376- // launch app
377- currentApplicationIndex = shortcuts.selectCurrentApplication()
378- if (currentApplicationIndex === 0) {
379- launcher.dashItemSelected(0)
380- launcher.state = ""
381- } else if (currentApplicationIndex === -1) {
382- launcher.state = "shortcuts"
383- }
384- }
385-
386- if (launcher.state == "hint" || launcher.state == "moving") {
387- launcher.state = ""
388- } else if (launcher.state == "spreadMoving" || launcher.state == "spreadHint") {
389- launcher.state = "shortcuts"
390- }
391-
392- shortcuts.stopScroll()
393-
394- if (launcher.state == "shortcuts") {
395- dismissTimer.restart()
396- }
397-
398- }
399- }
400-
401- Timer {
402- id: dismissTimer
403- interval: launcher.inactivityTimeout;
404- running: false;
405- repeat: false
406- onTriggered: {
407- launcher.state = ""
408- }
409- }
410-
411- Timer {
412- id: hideTimer
413- interval: 50;
414- running: false;
415- repeat: false
416- onTriggered: {
417- launcher.state = ""
418+ root.switchToNextState("")
419+ }
420+ }
421 }
422 }
423
424 states: [
425 State {
426- name: "hint"
427- when: priv.teasing
428- PropertyChanges {
429- target: shortcuts
430- x: -shortcuts.width + launcher.shortcutsHintWidth
431- }
432- },
433- State {
434- name: "moving"
435- PropertyChanges {
436- target: shortcuts
437- x: -shortcuts.width + dragArea.distance + launcher.shortcutsHintWidth
438- }
439- },
440- State {
441- name: "shortcuts"
442- PropertyChanges {
443- target: shortcuts
444- x: 0
445- }
446- },
447- State {
448- name: "spreadHint"
449- extend: "shortcuts"
450- },
451- State {
452- name: "spreadMoving"
453- PropertyChanges {
454- target: launcher
455- launcherProgressValue: dragArea.distance
456- }
457- PropertyChanges {
458- target: shortcuts
459- x: 0
460- }
461- },
462- State {
463- name: "spread"
464- extend: "shortcuts"
465- }
466-
467- ]
468-
469- transitions: [
470- Transition {
471- to: ""
472- SequentialAnimation {
473- PropertyAnimation {
474- target: shortcuts
475- properties: "x"
476- duration: 400
477- easing.type: Easing.OutCubic
478- }
479- ScriptAction {
480- script: {
481- shortcuts.resetScrollPosition()
482- }
483- }
484- }
485- },
486- Transition {
487- to: "hint"
488- PropertyAnimation {
489- target: shortcuts
490- properties: "x"
491- duration: 400
492- easing.type: Easing.OutCubic
493- }
494- },
495- Transition {
496- to: "shortcuts"
497- SequentialAnimation {
498- ScriptAction {
499- script: {
500- if (priv.previousState == "moving") {
501- priv.shortcutAnimationOngoing = true
502- }
503- }
504- }
505- ParallelAnimation {
506- SequentialAnimation {
507- PropertyAnimation {
508- target: shortcuts
509- properties: "x"
510- duration: 300
511- easing.type: Easing.OutCubic
512- }
513- ScriptAction {
514- script: {
515- priv.shortcutAnimationOngoing = false
516- }
517- }
518- }
519- }
520- }
521- },
522- Transition {
523- to: "spreadHint"
524- PropertyAnimation {
525- target: shortcuts
526- properties: "x"
527- duration: 300
528- easing.type: Easing.OutCubic
529- }
530- },
531- Transition {
532- to: "spreadMoving"
533- ScriptAction {
534- script: {
535- priv.shortcutAnimationOngoing = false
536- }
537- }
538- },
539- Transition {
540- to: "spread"
541- ScriptAction {
542- script: {
543- launcher.dash()
544- }
545+ name: "" // hidden state. Must be the default state ("") because "when:" falls back to this.
546+ PropertyChanges {
547+ target: panel
548+ x: -root.panelWidth
549+ }
550+ },
551+ State {
552+ name: "visible"
553+ PropertyChanges {
554+ target: panel
555+ x: 0
556+ }
557+ },
558+ State {
559+ name: "teasing"
560+ when: root.teasing || teaseTimer.running
561+ PropertyChanges {
562+ target: panel
563+ x: -root.panelWidth + units.gu(2)
564 }
565 }
566 ]
567
568=== added file 'Launcher/LauncherDelegate.qml'
569--- Launcher/LauncherDelegate.qml 1970-01-01 00:00:00 +0000
570+++ Launcher/LauncherDelegate.qml 2013-06-04 12:32:22 +0000
571@@ -0,0 +1,127 @@
572+/*
573+ * Copyright (C) 2013 Canonical, Ltd.
574+ *
575+ * This program is free software; you can redistribute it and/or modify
576+ * it under the terms of the GNU General Public License as published by
577+ * the Free Software Foundation; version 3.
578+ *
579+ * This program is distributed in the hope that it will be useful,
580+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
581+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
582+ * GNU General Public License for more details.
583+ *
584+ * You should have received a copy of the GNU General Public License
585+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
586+ */
587+
588+import QtQuick 2.0
589+import Ubuntu.Components 0.1
590+
591+Item {
592+ id: root
593+
594+ property string iconName
595+
596+ property real angle: 0
597+ property bool highlighted: false
598+ property real offset: 0
599+
600+ property real maxAngle: 0
601+ property bool inverted: false
602+
603+ readonly property int effectiveHeight: Math.cos(angle * Math.PI / 180) * height
604+ readonly property real foldedHeight: Math.cos(maxAngle * Math.PI / 180) * height
605+
606+ property int itemsBeforeThis: 0
607+ property int itemsAfterThis: 0
608+
609+ property bool dragging:false
610+
611+ signal clicked()
612+ signal longtap()
613+ signal released()
614+
615+ UbuntuShape {
616+ id: iconItem
617+ color: Qt.rgba(0, 0, 1, 0.5)
618+ width: parent.width
619+ height: parent.height
620+ anchors.centerIn: parent
621+ anchors.verticalCenterOffset: root.offset + (height - root.effectiveHeight)/2 * (angle < 0 ? -1 : 1)
622+ rotation: root.inverted ? 180 : 0
623+ radius: "medium"
624+
625+ transform: Rotation {
626+ axis { x: 1; y: 0; z: 0 }
627+ origin { x: iconItem.width / 2; y: iconItem.height / 2; z: 0 }
628+ angle: root.angle
629+ }
630+
631+ image: Image {
632+ source: "../graphics/applicationIcons/" + root.iconName + ".png"
633+ }
634+
635+ MouseArea {
636+ id: mouseArea
637+ anchors.fill: parent
638+ onClicked: root.clicked()
639+ onCanceled: root.released()
640+ preventStealing: false
641+
642+ onPressAndHold: {
643+ root.state = "moving"
644+ }
645+ onReleased: {
646+ root.state = "docked"
647+ }
648+ }
649+
650+ BorderImage {
651+ id: overlayHighlight
652+ anchors.centerIn: iconItem
653+ rotation: inverted ? 180 : 0
654+ source: root.highlighted || mouseArea.pressed ? "graphics/selected.sci" : "graphics/non-selected.sci"
655+ width: iconItem.width + units.gu(1.5)
656+ height: width
657+ }
658+ }
659+
660+ states: [
661+ State {
662+ name: "docked"
663+ PropertyChanges {
664+ target: root
665+ offset: if (launcherFlickable.contentY > itemsBeforeThis * (foldedHeight + launcherColumn.spacing*2)) {
666+ return launcherFlickable.contentY - (index * (foldedHeight + launcherColumn.spacing*2));
667+ } else if (y + height - launcherFlickable.contentY > launcherFlickable.height - (itemsAfterThis*(foldedHeight - launcherColumn.spacing))) {
668+ return launcherFlickable.height - (y+height) + launcherFlickable.contentY - (itemsAfterThis*(foldedHeight - launcherColumn.spacing));
669+ } else {
670+ return 0;
671+ }
672+ angle: -Math.min(Math.max(offset * maxAngle / foldedHeight, -maxAngle), maxAngle)
673+
674+ }
675+ },
676+
677+ State {
678+ name: "moving"
679+ PropertyChanges {
680+ target: launcherDelegate;
681+ offset: 0
682+ angle: 0
683+ }
684+ PropertyChanges {
685+ target: root
686+ highlighted: true
687+ dragging: true
688+ }
689+ PropertyChanges {
690+ target: mouseArea
691+ preventStealing: true
692+ drag.target: root
693+ }
694+ }
695+
696+ ]
697+
698+}
699
700=== renamed file 'Launcher/LauncherShortcuts.qml' => 'Launcher/LauncherPanel.qml'
701--- Launcher/LauncherShortcuts.qml 2013-05-27 15:13:11 +0000
702+++ Launcher/LauncherPanel.qml 2013-06-04 12:32:22 +0000
703@@ -16,301 +16,104 @@
704
705 import QtQuick 2.0
706 import Ubuntu.Components 0.1
707+import Unity 0.1
708
709 Item {
710- id: shortcuts
711-
712- property ListModel model
713- property ListModel shortcutsModel: model
714- property ListModel openModel: ListModel{}
715- property ListModel semiFoldedModel: ListModel{}
716- property ListModel foldedModel: ListModel{}
717-
718- property bool enabled: true
719- property string iconPath: "."
720-
721- property bool animating: openShortcuts.animating || semiFoldedShortcut.animating || foldedShortcuts.animating // read only
722- property int currentIndex: -1
723-
724- property int scrollValue: 0
725- property bool scrolling: scrollTimer.running
726-
727- property int itemWidth: units.gu(9)
728- property int itemHeight: units.gu(7.5)
729- property int iconWidth: units.gu(7)
730- property int iconHeight: units.gu(6.5)
731-
732- property int iconCount: shortcutsModel.count
733-
734- property int foldedSize: itemWidth - foldedShortcuts.initialFoldedOffset
735-
736- property int openItemsCount
737- property int foldedItemsCount: iconCount - openItemsCount
738- property int fillItemHeight: height - openItemsCount * itemHeight - foldedItemsCount * foldedSize
739- property bool open: false
740+ id: root
741+
742+ rotation: inverted ? 180 : 0
743+
744+ property var model
745 property bool inverted: true
746- property int labelPosition
747- labelPosition: {
748- if (!inverted) {
749- return currentIndex*shortcuts.itemHeight + shortcuts.itemHeight/2 + shortcutsContainer.targetY
750- } else {
751- return shortcuts.height - (currentIndex*shortcuts.itemHeight + shortcuts.itemHeight/2 + shortcutsContainer.targetY)
752- }
753- }
754-
755- rotation: inverted ? 180 : 0
756-
757- QtObject {
758- id: priv
759-
760- property int modelCount: shortcuts.model ? shortcuts.model.count : 0
761- }
762+ property bool dragging: false
763+ property bool moving: launcherFlickable.moving
764+ property int dragPosition: 0
765+ property int highlightIndex: -1
766
767 signal applicationSelected(string desktopFile)
768 signal dashItemSelected(int index)
769
770- Component.onCompleted: init()
771-
772- function resetScrollPosition() {
773- flickable.contentY = 0
774- }
775-
776- function init() {
777- clearModels()
778- addDashIcon()
779- calculateOpenIcons()
780- createModels()
781- }
782-
783- function clearModels() {
784- openModel.clear()
785- semiFoldedModel.clear()
786- foldedModel.clear()
787- }
788-
789- function addDashIcon() {
790- shortcutsModel.insert(0, {"desktopFile": "dash.desktop",
791- "name": "Home",
792- "icon": "dash",
793- "exec": ""
794- });
795- }
796-
797- function calculateOpenIcons() {
798- var foldedSize = (shortcuts.itemHeight - foldedShortcuts.initialFoldedOffset)
799- var maxopenItemsCount = Math.floor(shortcuts.height / shortcuts.itemHeight)
800- var leftOverIcons = shortcuts.iconCount - maxopenItemsCount
801- var leftOverSpace
802- if (leftOverIcons > 0) {
803- leftOverSpace = shortcuts.height - shortcuts.itemHeight * maxopenItemsCount
804- leftOverIcons = leftOverIcons - Math.floor(leftOverSpace / foldedSize)
805- while (leftOverIcons > 0) {
806- maxopenItemsCount--
807- leftOverIcons = shortcuts.iconCount - maxopenItemsCount
808- if (leftOverIcons > 0) {
809- leftOverSpace = shortcuts.height - shortcuts.itemHeight * maxopenItemsCount
810- leftOverIcons = leftOverIcons - Math.floor(leftOverSpace / foldedSize)
811- }
812- }
813- }
814- shortcuts.openItemsCount = Math.min(maxopenItemsCount, shortcuts.iconCount)
815- shortcuts.visible = true
816- }
817-
818- function createModels() {
819- for (var i = 0; i < shortcutsModel.count; i++) {
820- if (i < shortcuts.openItemsCount) {
821- shortcuts.openModel.append(shortcutsModel.get(i))
822- } else if (i == shortcuts.openItemsCount) {
823- shortcuts.semiFoldedModel.append(shortcutsModel.get(i))
824- } else {
825- shortcuts.foldedModel.append(shortcutsModel.get(i))
826- }
827- }
828- }
829-
830- function scroll() {
831- if (!scrolling && shortcutsContainer.height > shortcuts.height) {
832- var scrollingAtStart = shortcutsContainer.targetY == 0
833- var scrollingAtEnd = shortcutsContainer.targetY == shortcuts.height - shortcutsContainer.height
834- var scrollingInTheMiddle = shortcutsContainer.targetY > (shortcuts.height - shortcutsContainer.height) && shortcutsContainer.targetY < 0
835- var scrollValue = inverted ? -shortcuts.scrollValue : shortcuts.scrollValue
836-
837- shortcutsContainer.animateY = false
838-
839- if (scrollingInTheMiddle || (scrollingAtEnd && scrollValue > 0) || (scrollingAtStart && scrollValue < 0)) {
840- scrollTimer.start()
841- }
842- }
843- }
844-
845- function stopScroll() {
846- scrollTimer.stop()
847- shortcutsContainer.animateY = true
848- }
849-
850- function selectCurrentApplication() {
851- if (currentIndex < shortcuts.openItemsCount || foldedShortcuts.foldedAngle == 0) {
852- return currentIndex
853- } else {
854- return -1
855- }
856+ onDragPositionChanged: {
857+ var effectiveDragPosition = root.inverted ? launcherFlickable.height - dragPosition : dragPosition - mainColumn.anchors.margins
858+
859+ var hiddenContentHeight = launcherFlickable.contentHeight - launcherFlickable.height
860+ // Shortening scrollable height because the first/last item needs to be fully expanded before reaching the top/bottom
861+ var scrollableHeight = launcherFlickable.height - (launcherFlickable.itemSize + launcherColumn.spacing) *2
862+ // As we shortened the scrollableHeight, lets move everything down by the itemSize
863+ var shortenedEffectiveDragPosition = effectiveDragPosition - launcherFlickable.itemSize - launcherColumn.spacing
864+ var newContentY = shortenedEffectiveDragPosition * hiddenContentHeight / scrollableHeight
865+
866+ // limit top/bottom to prevent overshooting
867+ launcherFlickable.contentY = Math.min(hiddenContentHeight, Math.max(0, newContentY));
868+
869+ // Now calculate the current index:
870+ // > the current mouse position + the hidden/scolled content on top is the mouse position in the averall view
871+ // > adjust that removing all the margins
872+ // > divide by itemSize to get index
873+ highlightIndex = (effectiveDragPosition + launcherFlickable.contentY - mainColumn.anchors.margins*3 - launcherColumn.spacing/2) / (launcherFlickable.itemSize + launcherColumn.spacing)
874 }
875
876 BorderImage {
877 id: background
878 source: "graphics/launcher_bg.sci"
879- width: parent.width
880- x: 0
881- rotation: inverted ? 180 : 0
882- height: parent.height
883+ anchors.fill: parent
884 }
885
886- Component {
887- id: shortcutsItemDelegate
888- Item {
889- id: delegateItem
890- property int ownIndex: parent ? index + parent.initialIndex : -1
891- property bool isCurrentItem: ownIndex == shortcuts.currentIndex
892- property bool ribbonModeHighlight: itemMouseArea.pressed
893-
894- z: -index
895- anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined
896- width: shortcuts.itemWidth
897- height: shortcuts.itemHeight
898-
899- Item {
900- width: shortcuts.iconWidth
901- height: shortcuts.iconHeight
902- anchors.centerIn: parent
903- transform: Rotation { origin.x: width/2; origin.y: height; axis { x: 1; y: 0; z: 0 } angle: parent ? parent.foldedAngle : 0 }
904- smooth: true
905- UbuntuShape {
906- id: icon
907- objectName: "delegate " + model.desktopFile
908- anchors.centerIn: parent
909- rotation: inverted ? 180 : 0
910- width: shortcuts.iconWidth
911- height: width * units.gu(7.5) / units.gu(8)
912- radius: "medium"
913- borderSource: ""
914- image: Image {
915- id: iconImage
916- source: '../' + shortcuts.iconPath + "/" + model.icon + '.png'
917- sourceSize { width: icon.width; height: icon.height }
918+ Column {
919+ id: mainColumn
920+ anchors.fill: parent
921+ anchors.margins: units.gu(1)
922+ spacing: units.gu(1)
923+
924+ LauncherDelegate {
925+ id: dashItem
926+ objectName: "dashItem"
927+ width: launcherFlickable.itemSize
928+ height: launcherFlickable.itemSize
929+ anchors.horizontalCenter: parent.horizontalCenter
930+ iconName: "dash"
931+ onClicked: root.dashItemSelected(0)
932+ }
933+ Flickable {
934+ id: launcherFlickable
935+ anchors.left: parent.left
936+ anchors.right: parent.right
937+ height: parent.height - dashItem.height - parent.spacing
938+ contentHeight: launcherColumn.height
939+
940+ property int itemSize: width
941+
942+ Column {
943+ id: launcherColumn
944+ width: parent.width
945+ spacing: units.gu(1)
946+ anchors.horizontalCenter: parent.horizontalCenter
947+
948+ Repeater {
949+ id: iconRepeater
950+ model: root.model
951+
952+ LauncherDelegate {
953+ id: launcherDelegate
954+ objectName: "launcherDelegate" + index
955+ width: launcherFlickable.itemSize
956+ height: launcherFlickable.itemSize
957+ iconName: model.icon
958+ inverted: root.inverted
959+ highlighted: root.dragging && index === root.highlightIndex
960+ z: -Math.abs(offset)
961+ state: "docked"
962+
963+ maxAngle: 60
964+
965+ itemsBeforeThis: index
966+ itemsAfterThis: iconRepeater.count - (index+1)
967+
968+ onClicked: {
969+ root.applicationSelected(launcherModel.get(index).desktopFile);
970+ }
971 }
972 }
973-
974- BorderImage {
975- id: overlayHighlight
976- anchors.centerIn: icon
977- rotation: inverted ? 180 : 0
978- source: isCurrentItem && (ribbonModeHighlight) ? "graphics/selected.sci" : "graphics/non-selected.sci"
979- width: icon.width + units.gu(1.5)
980- height: width * units.gu(7.5) / units.gu(8)
981- }
982- }
983- MouseArea {
984- id: itemMouseArea
985- anchors.fill: parent
986- onPressed: shortcuts.currentIndex = ownIndex
987- onClicked: {
988- if (model.desktopFile === "dash.desktop") shortcuts.dashItemSelected(ownIndex)
989- else shortcuts.applicationSelected(model.desktopFile)
990- }
991- }
992- }
993- }
994-
995- Flickable {
996- id: flickable
997- width: parent.width
998- height: parent.height
999- contentWidth: width
1000- contentHeight: shortcutsContainer.height
1001- Item {
1002- id: shortcutsContainer
1003-
1004- property bool animateY: true
1005- property real targetY
1006-
1007- x: ( parent.width - background.width) / 2
1008- y: targetY
1009- width: shortcuts.width
1010- height: openShortcuts.height + semiFoldedShortcut.height + foldedShortcuts.height
1011-
1012- Behavior on y { NumberAnimation{duration: shortcutsContainer.animateY ? 200 : 0; } }
1013-
1014- ShortcutsContainer {
1015- id: openShortcuts
1016- objectName: "openShortcuts"
1017-
1018- width: shortcuts.width
1019- height: childrenRect.height
1020- z: 3
1021- model: shortcuts.openModel
1022- delegate: shortcutsItemDelegate
1023- initialIndex: 0
1024- }
1025-
1026- ShortcutsContainer {
1027- id: semiFoldedShortcut
1028- objectName: "semiFoldedShortcut"
1029-
1030- foldedAngle: 0 // FIXME: folding disabled // shortcuts.open ? 0 : 90 * (height - initialFoldedOffset)/height - 80 // todo: better algorithm here
1031- initialFoldedOffset: -2 * shortcuts.itemHeight / 3 + shortcuts.fillItemHeight
1032- foldedOffset: 0 // FIXME: folding disabled //shortcuts.open ? 0 : initialFoldedOffset
1033- visible: shortcuts.iconCount > shortcuts.openItemsCount
1034- width: shortcuts.itemWidth
1035- height: childrenRect.height
1036- anchors.top: openShortcuts.bottom
1037- z: 2
1038- anchors.horizontalCenter: parent.horizontalCenter
1039- anchors.topMargin: foldedOffset
1040- model: shortcuts.semiFoldedModel
1041- delegate: shortcutsItemDelegate
1042- initialIndex: shortcuts.openModel.count
1043- }
1044-
1045- ShortcutsContainer {
1046- id: foldedShortcuts
1047- objectName: "foldedShortcuts"
1048-
1049- foldedAngle: 0 // FIXME: folding disabled //shortcuts.open ? 0 : 68
1050- initialFoldedOffset: 2 * shortcuts.itemHeight / 3
1051- foldedOffset: 0 // FIXME: folding disabled // shortcuts.open ? 0 : initialFoldedOffset
1052- width: shortcuts.width
1053- height: childrenRect.height
1054- spacing: -foldedOffset
1055- anchors.top: semiFoldedShortcut.bottom
1056- anchors.topMargin: -foldedOffset
1057- z: 1
1058- model: shortcuts.foldedModel
1059- delegate: shortcutsItemDelegate
1060- initialIndex: shortcuts.openModel.count + shortcuts.semiFoldedModel.count
1061- }
1062- }
1063- }
1064-
1065- Timer {
1066- id: scrollTimer
1067-
1068- repeat: true
1069- running: false
1070- interval: 16
1071- onTriggered:{
1072- if (shortcutsContainer.height > shortcuts.height) {
1073- var scrollValue = inverted ? -shortcuts.scrollValue : shortcuts.scrollValue
1074- var newValue = shortcutsContainer.targetY + scrollValue
1075-
1076- if (newValue > 0) {
1077- newValue = 0
1078- stop()
1079- } else if (newValue < shortcuts.height - shortcutsContainer.height) {
1080- newValue = shortcuts.height - shortcutsContainer.height
1081- stop()
1082- }
1083- shortcutsContainer.targetY = newValue
1084 }
1085 }
1086 }
1087
1088=== modified file 'Shell.qml'
1089--- Shell.qml 2013-06-03 08:29:01 +0000
1090+++ Shell.qml 2013-06-04 12:32:22 +0000
1091@@ -479,16 +479,23 @@
1092 enabled: !panel.indicators.shown
1093 }
1094
1095+ InputFilterArea {
1096+ blockInput: launcher.shown
1097+ anchors {
1098+ top: parent.top
1099+ bottom: parent.bottom
1100+ left: parent.left
1101+ }
1102+ width: launcher.width
1103+ }
1104+
1105 Launcher {
1106 id: launcher
1107
1108 anchors.top: parent.top
1109 anchors.bottom: parent.bottom
1110 width: parent.width
1111- applicationFocused: stages.shown
1112- shortcutsWidth: units.gu(9)
1113- shortcutsThreshold: shell.edgeSize
1114- iconPath: "graphics/applicationIcons"
1115+ dragAreaWidth: shell.edgeSize
1116 available: !greeter.locked
1117 teasing: available && greeter.leftTeaserPressed
1118 onDashItemSelected: {
1119@@ -502,17 +509,12 @@
1120 stages.hide();
1121 }
1122 onDash: {
1123- greeter.hide()
1124+ dash.setCurrentLens("applications.lens", true, false)
1125 stages.hide();
1126 }
1127 onLauncherApplicationSelected:{
1128 greeter.hide()
1129- shell.activateApplication(name)
1130- }
1131- onStateChanged: {
1132- if (state == "spreadMoving") {
1133- dash.setCurrentLens("applications.lens", false, true)
1134- }
1135+ shell.activateApplication(desktopFile)
1136 }
1137 onShownChanged: {
1138 if (shown) {
1139@@ -521,16 +523,6 @@
1140 }
1141 }
1142 }
1143-
1144- InputFilterArea {
1145- blockInput: launcher.shown
1146- anchors {
1147- top: parent.top
1148- bottom: parent.bottom
1149- left: parent.left
1150- }
1151- width: launcher.shortcutsWidth
1152- }
1153 }
1154
1155 focus: true
1156
1157=== modified file 'plugins/Ubuntu/Gestures/DirectionalDragArea.cpp'
1158--- plugins/Ubuntu/Gestures/DirectionalDragArea.cpp 2013-05-20 11:12:09 +0000
1159+++ plugins/Ubuntu/Gestures/DirectionalDragArea.cpp 2013-06-04 12:32:22 +0000
1160@@ -243,17 +243,24 @@
1161 if (newStatus == m_status)
1162 return;
1163
1164+ DirectionalDragArea::Status oldStatus = m_status;
1165+
1166 m_status = newStatus;
1167 Q_EMIT statusChanged(m_status);
1168
1169 switch (newStatus) {
1170 case WaitingForTouch:
1171+ if (oldStatus != DirectionalDragArea::Rejected) {
1172+ Q_EMIT draggingChanged(false);
1173+ }
1174+ break;
1175+ case Undecided:
1176+ Q_EMIT draggingChanged(true);
1177+ break;
1178+ case Rejected:
1179 Q_EMIT draggingChanged(false);
1180 break;
1181- case Undecided:
1182- Q_EMIT draggingChanged(true);
1183- break;
1184- default: // Rejected
1185+ default:
1186 // no-op
1187 break;
1188 }
1189
1190=== modified file 'plugins/Ubuntu/Gestures/DirectionalDragArea.h'
1191--- plugins/Ubuntu/Gestures/DirectionalDragArea.h 2013-05-20 13:12:51 +0000
1192+++ plugins/Ubuntu/Gestures/DirectionalDragArea.h 2013-06-04 12:32:22 +0000
1193@@ -47,8 +47,9 @@
1194 // The current status of the directional drag gesture area.
1195 Q_PROPERTY(Status status READ status NOTIFY statusChanged)
1196
1197- // Whether a drag gesture is taking place (regardless of whether it's a correct
1198- // single-finger directional drag or not)
1199+ // Whether a drag gesture is taking place
1200+ // This will be true as long as status is Undecided or Recognized
1201+ // When a gesture gets rejected, dragging turns to false.
1202 Q_PROPERTY(bool dragging READ dragging NOTIFY draggingChanged)
1203
1204 /////
1205
1206=== modified file 'plugins/Unity/CMakeLists.txt'
1207--- plugins/Unity/CMakeLists.txt 2013-05-08 09:51:55 +0000
1208+++ plugins/Unity/CMakeLists.txt 2013-06-04 12:32:22 +0000
1209@@ -20,6 +20,7 @@
1210 peoplepreviewdata.cpp
1211 plugin.cpp
1212 bottombarvisibilitycommunicatorshell.cpp
1213+ launchermodel.cpp
1214 )
1215
1216 add_library(Unity-qml MODULE
1217
1218=== added file 'plugins/Unity/launchermodel.cpp'
1219--- plugins/Unity/launchermodel.cpp 1970-01-01 00:00:00 +0000
1220+++ plugins/Unity/launchermodel.cpp 2013-06-04 12:32:22 +0000
1221@@ -0,0 +1,153 @@
1222+/*
1223+ * Copyright (C) 2011 Canonical, Ltd.
1224+ *
1225+ * Authors:
1226+ * Michael Zanetti <michael.zanetti@canonical.com>
1227+ *
1228+ * This program is free software; you can redistribute it and/or modify
1229+ * it under the terms of the GNU General Public License as published by
1230+ * the Free Software Foundation; version 3.
1231+ *
1232+ * This program is distributed in the hope that it will be useful,
1233+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1234+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1235+ * GNU General Public License for more details.
1236+ *
1237+ * You should have received a copy of the GNU General Public License
1238+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1239+ */
1240+
1241+#include "launchermodel.h"
1242+
1243+LauncherModel::LauncherModel(QObject *parent): QAbstractListModel(parent)
1244+{
1245+ // FIXME: Dummy data... Aggregate real data from backends
1246+
1247+ // Fake favorites
1248+ LauncherItem *item = new LauncherItem("/usr/share/applications/phone-app.desktop", "Phone", "phone-app");
1249+ m_list.append(item);
1250+ item = new LauncherItem("/usr/share/applications/camera-app.desktop", "Camera", "camera");
1251+ m_list.append(item);
1252+ item = new LauncherItem("/usr/share/applications/gallery-app.desktop", "Gallery", "gallery");
1253+ m_list.append(item);
1254+ item = new LauncherItem("/usr/share/applications/facebook-webapp.desktop", "Facebook", "facebook");
1255+ m_list.append(item);
1256+ item = new LauncherItem("/usr/share/applications/webbrowser-app.desktop", "Browser", "browser");
1257+ m_list.append(item);
1258+ item = new LauncherItem("/usr/share/applications/twitter-webapp.desktop", "Twitter", "twitter");
1259+ m_list.append(item);
1260+ item = new LauncherItem("/usr/share/applications/gmail-webapp.desktop", "GMail", "gmail");
1261+ m_list.append(item);
1262+ item = new LauncherItem("/usr/share/applications/ubuntu-weather-app.desktop", "Weather", "weather");
1263+ m_list.append(item);
1264+ item = new LauncherItem("/usr/share/applications/notes-app.desktop", "Notepad", "notepad");
1265+ m_list.append(item);
1266+ item = new LauncherItem("/usr/share/applications/ubuntu-calendar-app.desktop","Calendar", "calendar");
1267+ m_list.append(item);
1268+}
1269+
1270+LauncherModel::~LauncherModel()
1271+{
1272+ while (!m_list.empty()) {
1273+ m_list.takeFirst()->deleteLater();
1274+ }
1275+}
1276+
1277+int LauncherModel::rowCount(const QModelIndex &parent) const
1278+{
1279+ Q_UNUSED(parent)
1280+ return m_list.count();
1281+}
1282+
1283+QVariant LauncherModel::data(const QModelIndex &index, int role) const
1284+{
1285+ LauncherItem *item = m_list.at(index.row());
1286+ switch(role) {
1287+ case RoleName:
1288+ return item->name();
1289+ case RoleIcon:
1290+ return item->icon();
1291+ case RoleFavorite:
1292+ return item->favorite();
1293+ }
1294+
1295+ return QVariant();
1296+}
1297+
1298+LauncherItem *LauncherModel::get(int index) const
1299+{
1300+ if (index < 0 || index >= m_list.count()) {
1301+ return 0;
1302+ }
1303+ return m_list.at(index);
1304+}
1305+
1306+void LauncherModel::move(int oldIndex, int newIndex)
1307+{
1308+ beginMoveRows(QModelIndex(), oldIndex, oldIndex, QModelIndex(), newIndex);
1309+ m_list.move(oldIndex, newIndex);
1310+ endMoveRows();
1311+}
1312+
1313+QHash<int, QByteArray> LauncherModel::roleNames() const
1314+{
1315+ QHash<int, QByteArray> roles;
1316+ roles.insert(RoleDesktopFile, "desktopFile");
1317+ roles.insert(RoleName, "name");
1318+ roles.insert(RoleIcon, "icon");
1319+ roles.insert(RoleFavorite, "favorite");
1320+ roles.insert(RoleRunning, "runnng");
1321+ return roles;
1322+}
1323+
1324+
1325+LauncherItem::LauncherItem(const QString &desktopFile, const QString &name, const QString &icon, QObject *parent):
1326+ QObject(parent),
1327+ m_desktopFile(desktopFile),
1328+ m_name(name),
1329+ m_icon(icon),
1330+ m_favorite(false)
1331+{
1332+
1333+}
1334+
1335+QString LauncherItem::desktopFile() const
1336+{
1337+ return m_desktopFile;
1338+}
1339+
1340+QString LauncherItem::name() const
1341+{
1342+ return m_name;
1343+}
1344+
1345+QString LauncherItem::icon() const
1346+{
1347+ return m_icon;
1348+}
1349+
1350+bool LauncherItem::favorite() const
1351+{
1352+ return m_favorite;
1353+}
1354+
1355+void LauncherItem::setFavorite(bool favorite)
1356+{
1357+ if (m_favorite != favorite) {
1358+ m_favorite = favorite;
1359+ Q_EMIT favoriteChanged(m_favorite);
1360+ }
1361+}
1362+
1363+bool LauncherItem::running() const
1364+{
1365+ return m_running;
1366+}
1367+
1368+void LauncherItem::setRunning(bool running)
1369+{
1370+ if (m_running != running) {
1371+ m_running = running;
1372+ Q_EMIT runningChanged(running);
1373+ }
1374+}
1375
1376=== added file 'plugins/Unity/launchermodel.h'
1377--- plugins/Unity/launchermodel.h 1970-01-01 00:00:00 +0000
1378+++ plugins/Unity/launchermodel.h 2013-06-04 12:32:22 +0000
1379@@ -0,0 +1,86 @@
1380+/*
1381+ * Copyright (C) 2011 Canonical, Ltd.
1382+ *
1383+ * Authors:
1384+ * Michael Zanetti <michael.zanetti@canonical.com>
1385+ *
1386+ * This program is free software; you can redistribute it and/or modify
1387+ * it under the terms of the GNU General Public License as published by
1388+ * the Free Software Foundation; version 3.
1389+ *
1390+ * This program is distributed in the hope that it will be useful,
1391+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1392+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1393+ * GNU General Public License for more details.
1394+ *
1395+ * You should have received a copy of the GNU General Public License
1396+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1397+ */
1398+
1399+#include <QAbstractListModel>
1400+
1401+class LauncherItem;
1402+
1403+class LauncherModel: public QAbstractListModel
1404+{
1405+ Q_OBJECT
1406+
1407+public:
1408+ enum Roles {
1409+ RoleDesktopFile = Qt::UserRole,
1410+ RoleName,
1411+ RoleIcon,
1412+ RoleFavorite,
1413+ RoleRunning
1414+ };
1415+
1416+ LauncherModel(QObject *parent = 0);
1417+ ~LauncherModel();
1418+
1419+ int rowCount(const QModelIndex &parent) const;
1420+
1421+ QVariant data(const QModelIndex &index, int role) const;
1422+
1423+ Q_INVOKABLE LauncherItem* get(int index) const;
1424+ Q_INVOKABLE void move(int oldIndex, int newIndex);
1425+
1426+ QHash<int, QByteArray> roleNames() const;
1427+
1428+private:
1429+ QList<LauncherItem*> m_list;
1430+};
1431+
1432+class LauncherItem: public QObject
1433+{
1434+ Q_OBJECT
1435+ Q_PROPERTY(QString desktopFile READ desktopFile CONSTANT)
1436+ Q_PROPERTY(QString name READ name CONSTANT)
1437+ Q_PROPERTY(QString icon READ icon CONSTANT)
1438+ Q_PROPERTY(bool favorite READ favorite WRITE setFavorite NOTIFY favoriteChanged)
1439+ Q_PROPERTY(bool running READ running WRITE setRunning NOTIFY runningChanged)
1440+
1441+public:
1442+ LauncherItem(const QString &desktopFile, const QString &name, const QString &icon, QObject *parent = 0);
1443+
1444+ QString desktopFile() const;
1445+
1446+ QString name() const;
1447+ QString icon() const;
1448+
1449+ bool favorite() const;
1450+ void setFavorite(bool favorite);
1451+
1452+ bool running() const;
1453+ void setRunning(bool running);
1454+
1455+Q_SIGNALS:
1456+ void favoriteChanged(bool favorite);
1457+ void runningChanged(bool running);
1458+
1459+private:
1460+ QString m_desktopFile;
1461+ QString m_name;
1462+ QString m_icon;
1463+ bool m_favorite;
1464+ bool m_running;
1465+};
1466
1467=== modified file 'plugins/Unity/plugin.cpp'
1468--- plugins/Unity/plugin.cpp 2013-04-05 22:03:12 +0000
1469+++ plugins/Unity/plugin.cpp 2013-06-04 12:32:22 +0000
1470@@ -30,6 +30,7 @@
1471 #include "categoryfilter.h"
1472 #include "peoplepreviewdata.h"
1473 #include "bottombarvisibilitycommunicatorshell.h"
1474+#include "launchermodel.h"
1475
1476 // libqtdee
1477 #include "deelistmodel.h"
1478@@ -49,6 +50,8 @@
1479 qmlRegisterType<PeoplePhoneData>(uri, 0, 1, "PeoplePhoneData");
1480 qmlRegisterType<PeopleAddressData>(uri, 0, 1, "PeopleAddressData");
1481 qmlRegisterType<PeopleIMData>(uri, 0, 1, "PeopleIMData");
1482+ qmlRegisterType<LauncherModel>(uri, 0, 1, "LauncherModel");
1483+ qmlRegisterUncreatableType<LauncherItem>(uri, 0, 1, "LauncherItem", "Can't create new Launcher Items in QML. Get them from the LauncherModel.");
1484 qmlRegisterUncreatableType<BottomBarVisibilityCommunicatorShell>(uri, 0, 1, "BottomBarVisibilityCommunicatorShell", "Can't create BottomBarVisibilityCommunicatorShell");
1485 }
1486
1487
1488=== modified file 'tests/autopilot/qml_phone_shell/tests/helpers.py'
1489--- tests/autopilot/qml_phone_shell/tests/helpers.py 2013-05-30 20:17:20 +0000
1490+++ tests/autopilot/qml_phone_shell/tests/helpers.py 2013-06-04 12:32:22 +0000
1491@@ -154,7 +154,7 @@
1492 view = self.main_window.get_qml_view()
1493 start_x = view.x + 1
1494 start_y = view.y + view.height / 2
1495- stop_x = start_x + launcher.shortcutsWidth + 1
1496+ stop_x = start_x + launcher.panelWidth + 1
1497 stop_y = start_y
1498 self.touch.drag(start_x, start_y, stop_x, stop_y)
1499 self.assertThat(launcher.shown, Eventually(Equals(True)))
1500
1501=== modified file 'tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp'
1502--- tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp 2013-05-20 13:12:51 +0000
1503+++ tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp 2013-06-04 12:32:22 +0000
1504@@ -162,8 +162,10 @@
1505 if (expectedGestureRecognition)
1506 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
1507
1508- if (edgeDragArea->status() == DirectionalDragArea::Rejected)
1509+ if (edgeDragArea->status() == DirectionalDragArea::Rejected) {
1510 QCOMPARE(edgeDragArea->dragging(), false);
1511+ QCOMPARE(draggingSpy.count(), 2);
1512+ }
1513
1514 QTest::touchEvent(view, device).release(0, touchPoint.toPoint());
1515
1516
1517=== modified file 'tests/qmltests/CMakeLists.txt'
1518--- tests/qmltests/CMakeLists.txt 2013-06-03 20:18:44 +0000
1519+++ tests/qmltests/CMakeLists.txt 2013-06-04 12:32:22 +0000
1520@@ -53,7 +53,7 @@
1521 ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/full")
1522 add_qml_test(Hud Hud)
1523 add_qml_test(Hud Result)
1524-add_qml_test(Launcher Launcher IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/plugins)
1525+add_qml_test(Launcher Launcher IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
1526 add_qml_test(Notifications Notifications)
1527 add_qml_test(Panel IndicatorRow)
1528 add_qml_test(Panel Indicators)
1529
1530=== modified file 'tests/qmltests/Launcher/tst_Launcher.qml'
1531--- tests/qmltests/Launcher/tst_Launcher.qml 2013-05-21 11:32:49 +0000
1532+++ tests/qmltests/Launcher/tst_Launcher.qml 2013-06-04 12:32:22 +0000
1533@@ -19,7 +19,6 @@
1534 import Unity.Test 0.1 as UT
1535 import ".."
1536 import "../../../Launcher"
1537-import "../../../Applications/applications.js" as ApplicationsModel
1538
1539 /* Nothing is shown at first. If you drag from left edge you will bring up the
1540 launcher. */
1541@@ -32,20 +31,12 @@
1542 x: 0
1543 y: 0
1544 width: units.gu(40)
1545- height: units.gu(71)
1546-
1547- favourites: ["/usr/share/applications/phone-app.desktop",
1548- "/usr/share/applications/camera-app.desktop",
1549- "/usr/share/applications/gallery-app.desktop"]
1550-
1551- shortcutsWidth: units.gu(9)
1552- shortcutsThreshold: units.gu(2)
1553- iconPath: "graphics/applicationIcons"
1554+ height: units.gu(81)
1555
1556 property string latestApplicationSelected
1557
1558 onLauncherApplicationSelected: {
1559- latestApplicationSelected = name
1560+ latestApplicationSelected = desktopFile
1561 }
1562
1563 property int dashItemSelected_count: 0
1564@@ -62,13 +53,11 @@
1565 // Drag from the left edge of the screen rightwards and check that the launcher
1566 // appears (as if being dragged by the finger/pointer)
1567 function test_dragLeftEdgeToRevealLauncherAndTapCenterToDismiss() {
1568- // "shortcuts" is the vertical stripe of icons the user see.
1569- // i.e., the launcher graphical element itself.
1570- var shortcuts = findChild(launcher, "shortcuts")
1571- verify(shortcuts != undefined)
1572+ var panel = findChild(launcher, "launcherPanel")
1573+ verify(panel != undefined)
1574
1575 // it starts out hidden just left of the left launcher edge
1576- compare(shortcuts.x, -shortcuts.width)
1577+ compare(panel.x, -panel.width)
1578
1579 dragLauncherIntoView()
1580
1581@@ -76,7 +65,7 @@
1582 mouseClick(launcher, launcher.width/2, launcher.height/2)
1583
1584 // should eventually get fully retracted (hidden)
1585- tryCompare(shortcuts, "x", -shortcuts.width, 1000)
1586+ tryCompare(panel, "x", -launcher.panelWidth, 1000)
1587 }
1588
1589 /* If I click on the icon of an application on the launcher
1590@@ -88,11 +77,10 @@
1591
1592 dragLauncherIntoView()
1593
1594- var phoneIcon = findChild(launcher,
1595- "delegate /usr/share/applications/phone-app.desktop")
1596- verify(phoneIcon != undefined)
1597+ var appIcon = findChild(launcher, "launcherDelegate0")
1598+ verify(appIcon != undefined)
1599
1600- mouseClick(phoneIcon, phoneIcon.width/2, phoneIcon.height/2)
1601+ mouseClick(appIcon, appIcon.width/2, appIcon.height/2)
1602
1603 tryCompare(launcher, "latestApplicationSelected",
1604 "/usr/share/applications/phone-app.desktop")
1605@@ -108,8 +96,7 @@
1606
1607 dragLauncherIntoView()
1608
1609- var dashIcon = findChild(launcher,
1610- "delegate dash.desktop")
1611+ var dashIcon = findChild(launcher, "dashItem")
1612 verify(dashIcon != undefined)
1613
1614 mouseClick(dashIcon, dashIcon.width/2, dashIcon.height/2)
1615@@ -121,24 +108,23 @@
1616 }
1617
1618 function dragLauncherIntoView() {
1619- var startX = launcher.shortcutsThreshold/2
1620+ var startX = launcher.dragAreaWidth/2
1621 var startY = launcher.height/2
1622 touchFlick(launcher,
1623 startX, startY,
1624 startX+units.gu(8), startY)
1625
1626- // "shortcuts" is the vertical stripe of icons the user see.
1627- // i.e., the launcher graphical element itself.
1628- var shortcuts = findChild(launcher, "shortcuts")
1629- verify(shortcuts != undefined)
1630+ var panel = findChild(launcher, "launcherPanel")
1631+ verify(panel != undefined)
1632
1633 // wait until it gets fully extended
1634- tryCompare(shortcuts, "x", 0)
1635+ tryCompare(panel, "x", 0)
1636+ tryCompare(launcher, "state", "visible")
1637 }
1638
1639 function waitUntilLauncherDisappears() {
1640- var shortcuts = findChild(launcher, "shortcuts")
1641- tryCompare(shortcuts, "x", -shortcuts.width, 1000)
1642+ var panel = findChild(launcher, "launcherPanel")
1643+ tryCompare(panel, "x", -panel.width, 1000)
1644 }
1645 }
1646 }
1647
1648=== modified file 'tests/qmltests/plugins/Unity/CMakeLists.txt'
1649--- tests/qmltests/plugins/Unity/CMakeLists.txt 2013-05-02 16:38:00 +0000
1650+++ tests/qmltests/plugins/Unity/CMakeLists.txt 2013-06-04 12:32:22 +0000
1651@@ -25,6 +25,7 @@
1652 fake_lens.cpp
1653 fake_lenses.cpp
1654 fake_unity_plugin.cpp
1655+ fake_launchermodel.cpp
1656 )
1657
1658 add_library(FakeUnityQml MODULE ${UnityQML_SOURCES})
1659
1660=== added file 'tests/qmltests/plugins/Unity/fake_launchermodel.cpp'
1661--- tests/qmltests/plugins/Unity/fake_launchermodel.cpp 1970-01-01 00:00:00 +0000
1662+++ tests/qmltests/plugins/Unity/fake_launchermodel.cpp 2013-06-04 12:32:22 +0000
1663@@ -0,0 +1,150 @@
1664+/*
1665+ * Copyright (C) 2011 Canonical, Ltd.
1666+ *
1667+ * Authors:
1668+ * Michael Zanetti <michael.zanetti@canonical.com>
1669+ *
1670+ * This program is free software; you can redistribute it and/or modify
1671+ * it under the terms of the GNU General Public License as published by
1672+ * the Free Software Foundation; version 3.
1673+ *
1674+ * This program is distributed in the hope that it will be useful,
1675+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1676+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1677+ * GNU General Public License for more details.
1678+ *
1679+ * You should have received a copy of the GNU General Public License
1680+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1681+ */
1682+
1683+#include "launchermodel.h"
1684+
1685+LauncherModel::LauncherModel(QObject *parent): QAbstractListModel(parent)
1686+{
1687+ LauncherItem *item = new LauncherItem("/usr/share/applications/phone-app.desktop", "Phone", "phone-app");
1688+ m_list.append(item);
1689+ item = new LauncherItem("/usr/share/applications/camera-app.desktop", "Camera", "camera");
1690+ m_list.append(item);
1691+ item = new LauncherItem("/usr/share/applications/gallery-app.desktop", "Gallery", "gallery");
1692+ m_list.append(item);
1693+ item = new LauncherItem("/usr/share/applications/facebook-webapp.desktop", "Facebook", "facebook");
1694+ m_list.append(item);
1695+ item = new LauncherItem("/usr/share/applications/webbrowser-app.desktop", "Browser", "browser");
1696+ m_list.append(item);
1697+ item = new LauncherItem("/usr/share/applications/twitter-webapp.desktop", "Twitter", "twitter");
1698+ m_list.append(item);
1699+ item = new LauncherItem("/usr/share/applications/gmail-webapp.desktop", "GMail", "gmail");
1700+ m_list.append(item);
1701+ item = new LauncherItem("/usr/share/applications/ubuntu-weather-app.desktop", "Weather", "weather");
1702+ m_list.append(item);
1703+ item = new LauncherItem("/usr/share/applications/notes-app.desktop", "Notepad", "notepad");
1704+ m_list.append(item);
1705+ item = new LauncherItem("/usr/share/applications/ubuntu-calendar-app.desktop","Calendar", "calendar");
1706+ m_list.append(item);
1707+}
1708+
1709+LauncherModel::~LauncherModel()
1710+{
1711+ while (!m_list.empty()) {
1712+ m_list.takeFirst()->deleteLater();
1713+ }
1714+}
1715+
1716+int LauncherModel::rowCount(const QModelIndex &parent) const
1717+{
1718+ Q_UNUSED(parent)
1719+ return m_list.count();
1720+}
1721+
1722+QVariant LauncherModel::data(const QModelIndex &index, int role) const
1723+{
1724+ LauncherItem *item = m_list.at(index.row());
1725+ switch(role) {
1726+ case RoleName:
1727+ return item->name();
1728+ case RoleIcon:
1729+ return item->icon();
1730+ case RoleFavorite:
1731+ return item->favorite();
1732+ }
1733+
1734+ return QVariant();
1735+}
1736+
1737+LauncherItem *LauncherModel::get(int index) const
1738+{
1739+ if (index < 0 || index >= m_list.count()) {
1740+ return 0;
1741+ }
1742+ return m_list.at(index);
1743+}
1744+
1745+void LauncherModel::move(int oldIndex, int newIndex)
1746+{
1747+ beginMoveRows(QModelIndex(), oldIndex, oldIndex, QModelIndex(), newIndex);
1748+ m_list.move(oldIndex, newIndex);
1749+ endMoveRows();
1750+}
1751+
1752+QHash<int, QByteArray> LauncherModel::roleNames() const
1753+{
1754+ QHash<int, QByteArray> roles;
1755+ roles.insert(RoleDesktopFile, "desktopFile");
1756+ roles.insert(RoleName, "name");
1757+ roles.insert(RoleIcon, "icon");
1758+ roles.insert(RoleFavorite, "favorite");
1759+ roles.insert(RoleRunning, "runnng");
1760+ return roles;
1761+}
1762+
1763+
1764+LauncherItem::LauncherItem(const QString &desktopFile, const QString &name, const QString &icon, QObject *parent):
1765+ QObject(parent),
1766+ m_desktopFile(desktopFile),
1767+ m_name(name),
1768+ m_icon(icon),
1769+ m_favorite(false)
1770+{
1771+
1772+}
1773+
1774+QString LauncherItem::desktopFile() const
1775+{
1776+ return m_desktopFile;
1777+}
1778+
1779+QString LauncherItem::name() const
1780+{
1781+ return m_name;
1782+}
1783+
1784+QString LauncherItem::icon() const
1785+{
1786+ return m_icon;
1787+}
1788+
1789+bool LauncherItem::favorite() const
1790+{
1791+ return m_favorite;
1792+}
1793+
1794+void LauncherItem::setFavorite(bool favorite)
1795+{
1796+ if (m_favorite != favorite) {
1797+ m_favorite = favorite;
1798+ Q_EMIT favoriteChanged(m_favorite);
1799+ }
1800+}
1801+
1802+bool LauncherItem::running() const
1803+{
1804+ return m_running;
1805+}
1806+
1807+void LauncherItem::setRunning(bool running)
1808+{
1809+ if (m_running != running) {
1810+ m_running = running;
1811+ Q_EMIT runningChanged(running);
1812+ }
1813+}
1814
1815=== added file 'tests/qmltests/plugins/Unity/fake_launchermodel.h'
1816--- tests/qmltests/plugins/Unity/fake_launchermodel.h 1970-01-01 00:00:00 +0000
1817+++ tests/qmltests/plugins/Unity/fake_launchermodel.h 2013-06-04 12:32:22 +0000
1818@@ -0,0 +1,86 @@
1819+/*
1820+ * Copyright (C) 2011 Canonical, Ltd.
1821+ *
1822+ * Authors:
1823+ * Michael Zanetti <michael.zanetti@canonical.com>
1824+ *
1825+ * This program is free software; you can redistribute it and/or modify
1826+ * it under the terms of the GNU General Public License as published by
1827+ * the Free Software Foundation; version 3.
1828+ *
1829+ * This program is distributed in the hope that it will be useful,
1830+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1831+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1832+ * GNU General Public License for more details.
1833+ *
1834+ * You should have received a copy of the GNU General Public License
1835+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1836+ */
1837+
1838+#include <QAbstractListModel>
1839+
1840+class LauncherItem;
1841+
1842+class LauncherModel: public QAbstractListModel
1843+{
1844+ Q_OBJECT
1845+
1846+public:
1847+ enum Roles {
1848+ RoleDesktopFile = Qt::UserRole,
1849+ RoleName,
1850+ RoleIcon,
1851+ RoleFavorite,
1852+ RoleRunning
1853+ };
1854+
1855+ LauncherModel(QObject *parent = 0);
1856+ ~LauncherModel();
1857+
1858+ int rowCount(const QModelIndex &parent) const;
1859+
1860+ QVariant data(const QModelIndex &index, int role) const;
1861+
1862+ Q_INVOKABLE LauncherItem* get(int index) const;
1863+ Q_INVOKABLE void move(int oldIndex, int newIndex);
1864+
1865+ QHash<int, QByteArray> roleNames() const;
1866+
1867+private:
1868+ QList<LauncherItem*> m_list;
1869+};
1870+
1871+class LauncherItem: public QObject
1872+{
1873+ Q_OBJECT
1874+ Q_PROPERTY(QString desktopFile READ desktopFile CONSTANT)
1875+ Q_PROPERTY(QString name READ name CONSTANT)
1876+ Q_PROPERTY(QString icon READ icon CONSTANT)
1877+ Q_PROPERTY(bool favorite READ favorite WRITE setFavorite NOTIFY favoriteChanged)
1878+ Q_PROPERTY(bool running READ running WRITE setRunning NOTIFY runningChanged)
1879+
1880+public:
1881+ LauncherItem(const QString &desktopFile, const QString &name, const QString &icon, QObject *parent = 0);
1882+
1883+ QString desktopFile() const;
1884+
1885+ QString name() const;
1886+ QString icon() const;
1887+
1888+ bool favorite() const;
1889+ void setFavorite(bool favorite);
1890+
1891+ bool running() const;
1892+ void setRunning(bool running);
1893+
1894+Q_SIGNALS:
1895+ void favoriteChanged(bool favorite);
1896+ void runningChanged(bool running);
1897+
1898+private:
1899+ QString m_desktopFile;
1900+ QString m_name;
1901+ QString m_icon;
1902+ bool m_favorite;
1903+ bool m_running;
1904+};
1905
1906=== modified file 'tests/qmltests/plugins/Unity/fake_unity_plugin.cpp'
1907--- tests/qmltests/plugins/Unity/fake_unity_plugin.cpp 2013-05-02 23:05:09 +0000
1908+++ tests/qmltests/plugins/Unity/fake_unity_plugin.cpp 2013-06-04 12:32:22 +0000
1909@@ -23,6 +23,7 @@
1910 #include "fake_lenses.h"
1911 #include "categories.h"
1912 #include "categoryfilter.h"
1913+#include "fake_launchermodel.h"
1914
1915 // External
1916 #include <glib-object.h>
1917@@ -40,4 +41,6 @@
1918 qmlRegisterType<Lens>(uri, 0, 1, "Lens");
1919 qmlRegisterType<Categories>(uri, 0, 1, "Categories");
1920 qmlRegisterType<CategoryFilter>(uri, 0, 1, "CategoryFilter");
1921+ qmlRegisterType<LauncherModel>(uri, 0, 1, "LauncherModel");
1922+ qmlRegisterUncreatableType<LauncherItem>(uri, 0, 1, "LauncherItem", "Can't create");
1923 }

Subscribers

People subscribed via source and target branches