Merge lp:~mzanetti/unity8/appmanager-rework into lp:unity8
- appmanager-rework
- Merge into trunk
Status: | Rejected |
---|---|
Rejected by: | Michael Zanetti |
Proposed branch: | lp:~mzanetti/unity8/appmanager-rework |
Merge into: | lp:unity8 |
Diff against target: |
1949 lines (+987/-314) 22 files modified
plugins/Utils/CMakeLists.txt (+2/-1) plugins/Utils/easingcurve.cpp (+49/-0) plugins/Utils/easingcurve.h (+42/-0) plugins/Utils/plugin.cpp (+2/-0) qml/Components/ResponsiveFlowView.qml (+3/-10) qml/Dash/Apps/RunningApplicationTile.qml (+13/-24) qml/Dash/Apps/RunningApplicationsGrid.qml (+5/-20) qml/Dash/DashApps.qml (+4/-10) qml/Dash/GenericScopeView.qml (+1/-2) qml/Shell.qml (+85/-243) qml/Stages/PhoneStage.qml (+574/-0) run_on_device (+1/-0) tests/mocks/Unity/Application/ApplicationInfo.h (+3/-0) tests/mocks/Unity/Application/ApplicationManager.cpp (+56/-1) tests/mocks/Unity/Application/ApplicationManager.h (+2/-0) tests/mocks/Unity/Application/ApplicationScreenshotProvider.cpp (+69/-0) tests/mocks/Unity/Application/ApplicationScreenshotProvider.h (+34/-0) tests/mocks/Unity/Application/CMakeLists.txt (+1/-0) tests/mocks/Unity/Application/plugin.cpp (+18/-3) tests/mocks/Unity/Application/plugin.h (+1/-0) tests/plugins/Unity/Launcher/launchermodeltest.cpp (+3/-0) tests/qmltests/tst_Shell.qml (+19/-0) |
To merge this branch: | bzr merge lp:~mzanetti/unity8/appmanager-rework |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Michael Zanetti (community) | Disapprove | ||
Review via email: mp+199815@code.launchpad.net |
Commit message
rework ApplicationManager handling
Description of the change
This completely reworks how the Shell talks to the ApplicationManager as a preparation for the upcoming Right Edge work.
It drops ApplicationMana
Also it cuts ties between the RunningApplicat
Last but not least it drops the stages code as it is now and starts fresh with a PhoneStage for the upcoming Right Edge work. In the future a TabledStage and DesktopStage should be easily addable.
- 575. By Michael Zanetti
-
initial try on the right edge animation
- 576. By Michael Zanetti
-
make it interactive
- 577. By Michael Zanetti
-
added a bit of scaling. not matching spec yet
- 578. By Michael Zanetti
-
make it actually switch apps
- 579. By Michael Zanetti
-
improve mouse interaction with CoverFlip
- 580. By Michael Zanetti
-
update screenshots before starting right edge animation
- 581. By Michael Zanetti
-
merge trunk
- 582. By Michael Zanetti
-
reworked animation
- 583. By Michael Zanetti
-
improved the whole animation a lot
- 584. By Michael Zanetti
-
fix glitch with focusing the right app when moving backwards, after having already passed progressmarker1
- 585. By Michael Zanetti
-
make it look nicer if there are only 3 running apps
- 586. By Michael Zanetti
-
make flicking AND clicking work
- 587. By Michael Zanetti
-
cleanup
- 588. By Michael Zanetti
-
fix selected animation
- 589. By Michael Zanetti
-
drop unused file
- 590. By Michael Zanetti
-
merge trunk
- 591. By Michael Zanetti
-
add a point of no return
- 592. By Michael Zanetti
-
some work on bringing the fake application plugin up to date with the new api
- 593. By Michael Zanetti
-
added missing files
- 594. By Michael Zanetti
-
more progress on the fake application manager
- 595. By Michael Zanetti
-
fix hiding the stage when the app is killed
- 596. By Michael Zanetti
-
added a dropshadow
- 597. By Michael Zanetti
-
slow down scrolling a bit and fix the flickables contentWidth to roughly reflect the app's positions
- 598. By Michael Zanetti
-
shrink app tiles a little to make 3 of them fit into one row again
Unmerged revisions
Preview Diff
1 | === modified file 'plugins/Utils/CMakeLists.txt' |
2 | --- plugins/Utils/CMakeLists.txt 2013-12-10 14:22:43 +0000 |
3 | +++ plugins/Utils/CMakeLists.txt 2014-01-22 10:43:18 +0000 |
4 | @@ -18,6 +18,7 @@ |
5 | qsortfilterproxymodelqml.cpp |
6 | timeformatter.cpp |
7 | unitymenumodelpaths.cpp |
8 | + easingcurve.cpp |
9 | plugin.cpp |
10 | ) |
11 | |
12 | @@ -33,7 +34,7 @@ |
13 | # files directly in targets. |
14 | set_target_properties(Utils-qml PROPERTIES COMPILE_FLAGS -fvisibility=default) |
15 | |
16 | -qt5_use_modules(Utils-qml Qml Quick DBus Network XmlPatterns) |
17 | +qt5_use_modules(Utils-qml Qml Quick DBus Network XmlPatterns Gui) |
18 | |
19 | # export the qmldir qmltypes and plugin files |
20 | export_qmlfiles(Utils Utils) |
21 | |
22 | === added file 'plugins/Utils/easingcurve.cpp' |
23 | --- plugins/Utils/easingcurve.cpp 1970-01-01 00:00:00 +0000 |
24 | +++ plugins/Utils/easingcurve.cpp 2014-01-22 10:43:18 +0000 |
25 | @@ -0,0 +1,49 @@ |
26 | +#include "easingcurve.h" |
27 | + |
28 | + |
29 | +EasingCurve::EasingCurve(QObject *parent): |
30 | + QObject(parent) |
31 | +{ |
32 | + |
33 | +} |
34 | + |
35 | +QEasingCurve::Type EasingCurve::type() const |
36 | +{ |
37 | + return m_easingCurve.type(); |
38 | +} |
39 | + |
40 | +void EasingCurve::setType(const QEasingCurve::Type &type) |
41 | +{ |
42 | + m_easingCurve.setType(type); |
43 | + Q_EMIT typeChanged(); |
44 | +} |
45 | + |
46 | +qreal EasingCurve::period() const |
47 | +{ |
48 | + return m_easingCurve.period(); |
49 | +} |
50 | + |
51 | +void EasingCurve::setPeriod(qreal period) |
52 | +{ |
53 | + m_easingCurve.setPeriod(period); |
54 | + Q_EMIT periodChanged(); |
55 | +} |
56 | + |
57 | +qreal EasingCurve::progress() const |
58 | +{ |
59 | + return m_progress; |
60 | +} |
61 | + |
62 | +void EasingCurve::setProgress(qreal progress) |
63 | +{ |
64 | + if (m_progress != progress) { |
65 | + m_progress = progress; |
66 | + m_value = m_easingCurve.valueForProgress(m_progress); |
67 | + Q_EMIT progressChanged(); |
68 | + } |
69 | +} |
70 | + |
71 | +qreal EasingCurve::value() const |
72 | +{ |
73 | + return m_value; |
74 | +} |
75 | |
76 | === added file 'plugins/Utils/easingcurve.h' |
77 | --- plugins/Utils/easingcurve.h 1970-01-01 00:00:00 +0000 |
78 | +++ plugins/Utils/easingcurve.h 2014-01-22 10:43:18 +0000 |
79 | @@ -0,0 +1,42 @@ |
80 | +#ifndef EASINGCURVE_H |
81 | +#define EASINGCURVE_H |
82 | + |
83 | +#include <QObject> |
84 | +#include <QEasingCurve> |
85 | + |
86 | +class EasingCurve: public QObject |
87 | +{ |
88 | + Q_OBJECT |
89 | + Q_ENUMS(QEasingCurve::Type) |
90 | + Q_PROPERTY(QEasingCurve::Type type READ type WRITE setType NOTIFY typeChanged) |
91 | + Q_PROPERTY(qreal period READ period WRITE setPeriod NOTIFY periodChanged) |
92 | + Q_PROPERTY(qreal progress READ progress WRITE setProgress NOTIFY progressChanged) |
93 | + Q_PROPERTY(qreal value READ value NOTIFY progressChanged) |
94 | + |
95 | +public: |
96 | + EasingCurve(QObject *parent = 0); |
97 | + |
98 | + QEasingCurve::Type type() const; |
99 | + void setType(const QEasingCurve::Type &type); |
100 | + |
101 | + qreal period() const; |
102 | + void setPeriod(qreal period); |
103 | + |
104 | + qreal progress() const; |
105 | + void setProgress(qreal progress); |
106 | + |
107 | + qreal value() const; |
108 | + |
109 | +Q_SIGNALS: |
110 | + void typeChanged(); |
111 | + void periodChanged(); |
112 | + void progressChanged(); |
113 | + |
114 | +private: |
115 | + QEasingCurve m_easingCurve; |
116 | + qreal m_progress; |
117 | + qreal m_value; |
118 | +}; |
119 | + |
120 | +#endif |
121 | + |
122 | |
123 | === modified file 'plugins/Utils/plugin.cpp' |
124 | --- plugins/Utils/plugin.cpp 2013-12-10 14:22:43 +0000 |
125 | +++ plugins/Utils/plugin.cpp 2014-01-22 10:43:18 +0000 |
126 | @@ -32,6 +32,7 @@ |
127 | #include "qsortfilterproxymodelqml.h" |
128 | #include "timeformatter.h" |
129 | #include "unitymenumodelpaths.h" |
130 | +#include "easingcurve.h" |
131 | |
132 | static const char* BOTTOM_BAR_VISIBILITY_COMMUNICATOR_DBUS_PATH = "/BottomBarVisibilityCommunicator"; |
133 | static const char* DBUS_SERVICE = "com.canonical.Shell.BottomBarVisibilityCommunicator"; |
134 | @@ -46,6 +47,7 @@ |
135 | qmlRegisterType<TimeFormatter>(uri, 0, 1, "TimeFormatter"); |
136 | qmlRegisterType<GDateTimeFormatter>(uri, 0, 1, "GDateTimeFormatter"); |
137 | qmlRegisterUncreatableType<BottomBarVisibilityCommunicatorShell>(uri, 0, 1, "BottomBarVisibilityCommunicatorShell", "Can't create BottomBarVisibilityCommunicatorShell"); |
138 | + qmlRegisterType<EasingCurve>(uri, 0, 1, "EasingCurve"); |
139 | } |
140 | |
141 | void UtilsPlugin::initializeEngine(QQmlEngine *engine, const char *uri) |
142 | |
143 | === modified file 'qml/Components/ResponsiveFlowView.qml' |
144 | --- qml/Components/ResponsiveFlowView.qml 2013-06-05 22:03:08 +0000 |
145 | +++ qml/Components/ResponsiveFlowView.qml 2014-01-22 10:43:18 +0000 |
146 | @@ -27,9 +27,8 @@ |
147 | property alias verticalSpacing: flow.verticalSpacing |
148 | property alias horizontalSpacing: flow.horizontalSpacing |
149 | property int referenceDelegateWidth |
150 | - property alias firstModel: repeater1.model |
151 | - property alias secondModel: repeater2.model |
152 | - property alias delegate: repeater1.delegate |
153 | + property alias model: repeater.model |
154 | + property alias delegate: repeater.delegate |
155 | readonly property int cellWidth: referenceDelegateWidth + horizontalSpacing |
156 | readonly property int cellHeight: referenceDelegateWidth + verticalSpacing |
157 | property alias move: flow.move |
158 | @@ -69,13 +68,7 @@ |
159 | property int margin: allocatableVerticalSpace - columns * horizontalSpacing |
160 | |
161 | Repeater { |
162 | - id: repeater1 |
163 | - model: (root.model) ? root.model[0] : null |
164 | - } |
165 | - Repeater { |
166 | - id: repeater2 |
167 | - model: (root.model) ? root.model[1] : null |
168 | - delegate: repeater1.delegate |
169 | + id: repeater |
170 | } |
171 | } |
172 | } |
173 | |
174 | === modified file 'qml/Dash/Apps/RunningApplicationTile.qml' |
175 | --- qml/Dash/Apps/RunningApplicationTile.qml 2013-10-28 14:12:24 +0000 |
176 | +++ qml/Dash/Apps/RunningApplicationTile.qml 2014-01-22 10:43:18 +0000 |
177 | @@ -23,7 +23,6 @@ |
178 | AbstractButton { |
179 | id: root |
180 | property var application |
181 | - property bool __sideStageEnabled: shell.applicationManager.sideStageEnabled |
182 | |
183 | signal requestedApplicationActivation(var application) |
184 | signal requestedApplicationTermination(var application) |
185 | @@ -51,32 +50,22 @@ |
186 | } |
187 | } |
188 | |
189 | - function updateScreenshotFromCache() { |
190 | - applicationImage.updateFromCache(); |
191 | - } |
192 | - |
193 | - // FIXME: should use UbuntuShape from SDK |
194 | - UbuntuShapeForItem { |
195 | + UbuntuShape { |
196 | id: shapedApplicationImage |
197 | - anchors { |
198 | - top: parent.top |
199 | - horizontalCenter: parent.horizontalCenter |
200 | + anchors { top: parent.top; horizontalCenter: parent.horizontalCenter } |
201 | + |
202 | + height: units.gu(17) |
203 | + width: applicationImage.width |
204 | + radius: "medium" |
205 | + |
206 | + image: Image { |
207 | + id: applicationImage |
208 | + source: application.screenshot |
209 | + // height : width = ss.height : ss.width |
210 | + height: shapedApplicationImage.height |
211 | + width: height * sourceSize.width / sourceSize.height |
212 | } |
213 | |
214 | - // FIXME: width and height should be defined according to the |
215 | - // application window's aspect ratio. |
216 | - width: (application.stage === ApplicationInfo.MainStage && __sideStageEnabled) ? |
217 | - units.gu(22) : units.gu(11) |
218 | - height: (__sideStageEnabled) ? units.gu(22) : units.gu(19) |
219 | - radius: "medium" |
220 | - image: applicationImage |
221 | - } |
222 | - |
223 | - ApplicationImage { |
224 | - id: applicationImage |
225 | - source: ApplicationManager.findApplication((application) ? application.appId : "") |
226 | - width: shapedApplicationImage.width |
227 | - height: shapedApplicationImage.height |
228 | } |
229 | |
230 | UbuntuShape { |
231 | |
232 | === modified file 'qml/Dash/Apps/RunningApplicationsGrid.qml' |
233 | --- qml/Dash/Apps/RunningApplicationsGrid.qml 2013-11-29 08:57:13 +0000 |
234 | +++ qml/Dash/Apps/RunningApplicationsGrid.qml 2014-01-22 10:43:18 +0000 |
235 | @@ -18,6 +18,7 @@ |
236 | import "../../Components" |
237 | |
238 | import Ubuntu.Gestures 0.1 |
239 | +import Unity.Application 0.1 |
240 | |
241 | ResponsiveFlowView { |
242 | id: root |
243 | @@ -25,13 +26,7 @@ |
244 | |
245 | signal updateScreenshots |
246 | property alias enableHeightBehavior: heightBehaviour.enabled |
247 | - property bool enableHeightBehaviorOnNextCreation: firstModel.count + secondModel.count == 0 |
248 | - |
249 | - Connections { |
250 | - target: shell |
251 | - onDashShownChanged: if (shell.dashShown && shell.stageScreenshotsReady) updateScreenshots(); |
252 | - onStageScreenshotsReadyChanged: if (shell.dashShown && shell.stageScreenshotsReady) updateScreenshots(); |
253 | - } |
254 | + property bool enableHeightBehaviorOnNextCreation: model.count === 0 |
255 | |
256 | Behavior on height { |
257 | id: heightBehaviour |
258 | @@ -40,13 +35,7 @@ |
259 | } |
260 | |
261 | Connections { |
262 | - target: root.firstModel |
263 | - onCountChanged: { |
264 | - heightBehaviour.enabled = true; |
265 | - } |
266 | - } |
267 | - Connections { |
268 | - target: root.secondModel |
269 | + target: root.model |
270 | onCountChanged: { |
271 | heightBehaviour.enabled = true; |
272 | } |
273 | @@ -85,17 +74,13 @@ |
274 | root.terminationModeEnabled = true |
275 | } |
276 | onRequestedApplicationTermination: { |
277 | - shell.applicationManager.stopApplication(model.appId) |
278 | + ApplicationManager.stopApplication(model.appId) |
279 | } |
280 | onRequestedApplicationActivation: { |
281 | - shell.activateApplication(model.appId) |
282 | + ApplicationManager.focusApplication(model.appId) |
283 | } |
284 | |
285 | terminationModeEnabled: root.terminationModeEnabled |
286 | - |
287 | - Component.onCompleted: { |
288 | - root.updateScreenshots.connect(updateScreenshotFromCache); |
289 | - } |
290 | } |
291 | } |
292 | |
293 | |
294 | === modified file 'qml/Dash/DashApps.qml' |
295 | --- qml/Dash/DashApps.qml 2013-12-02 13:23:28 +0000 |
296 | +++ qml/Dash/DashApps.qml 2014-01-22 10:43:18 +0000 |
297 | @@ -17,6 +17,7 @@ |
298 | import QtQuick 2.0 |
299 | import Ubuntu.Components 0.1 |
300 | import Utils 0.1 |
301 | +import Unity.Application 0.1 |
302 | import "../Components" |
303 | import "../Components/ListItems" |
304 | import "Apps" |
305 | @@ -25,10 +26,6 @@ |
306 | id: scopeView |
307 | objectName: "DashApps" |
308 | |
309 | - // FIXME: a way to aggregate these models would be ideal |
310 | - property var mainStageApplicationsModel: shell.applicationManager.mainStageApplications |
311 | - property var sideStageApplicationModel: shell.applicationManager.sideStageApplications |
312 | - |
313 | ListModel { |
314 | id: dummyVisibilityModifier |
315 | |
316 | @@ -38,19 +35,16 @@ |
317 | SortFilterProxyModel { |
318 | id: runningApplicationsModel |
319 | |
320 | - property var firstModel: mainStageApplicationsModel |
321 | - property var secondModel: sideStageApplicationModel |
322 | property bool canEnableTerminationMode: scopeView.isCurrent |
323 | |
324 | - model: dummyVisibilityModifier |
325 | + model: ApplicationManager |
326 | filterRole: 0 |
327 | - filterRegExp: invertMatch ? ((mainStageApplicationsModel.count === 0 && |
328 | - sideStageApplicationModel.count === 0) ? RegExp("running-apps") : RegExp("")) : RegExp("disabled") |
329 | + filterRegExp: invertMatch ? (ApplicationManager.count === 0 ? RegExp("running-apps") : RegExp("")) : RegExp("disabled") |
330 | invertMatch: scopeView.scope.searchQuery.length == 0 |
331 | } |
332 | |
333 | onScopeChanged: { |
334 | scopeView.scope.categories.overrideResults("recent", runningApplicationsModel); |
335 | - enableHeightBehaviorOnNextCreation = (mainStageApplicationsModel.count + sideStageApplicationModel.count == 0) |
336 | + enableHeightBehaviorOnNextCreation = ApplicationManager.count === 0 |
337 | } |
338 | } |
339 | |
340 | === modified file 'qml/Dash/GenericScopeView.qml' |
341 | --- qml/Dash/GenericScopeView.qml 2014-01-07 17:06:01 +0000 |
342 | +++ qml/Dash/GenericScopeView.qml 2014-01-22 10:43:18 +0000 |
343 | @@ -159,8 +159,7 @@ |
344 | } |
345 | if (source.toString().indexOf("Apps/RunningApplicationsGrid.qml") != -1) { |
346 | // TODO: the running apps grid doesn't support standard scope results model yet |
347 | - item.firstModel = Qt.binding(function() { return results.firstModel }) |
348 | - item.secondModel = Qt.binding(function() { return results.secondModel }) |
349 | + item.model = Qt.binding(function() { return results.model }) |
350 | item.canEnableTerminationMode = Qt.binding(function() { return scopeView.isCurrent }) |
351 | } else { |
352 | item.model = Qt.binding(function() { return results }) |
353 | |
354 | === modified file 'qml/Shell.qml' |
355 | --- qml/Shell.qml 2014-01-02 08:21:56 +0000 |
356 | +++ qml/Shell.qml 2014-01-22 10:43:18 +0000 |
357 | @@ -49,20 +49,22 @@ |
358 | readonly property real panelHeight: panel.panelHeight |
359 | |
360 | property bool dashShown: dash.shown |
361 | - property bool stageScreenshotsReady: { |
362 | - if (sideStage.shown) { |
363 | - if (mainStage.applications.count > 0) { |
364 | - return mainStage.usingScreenshots && sideStage.usingScreenshots; |
365 | - } else { |
366 | - return sideStage.usingScreenshots; |
367 | - } |
368 | + |
369 | + function activateApplication(appId) { |
370 | + if (ApplicationManager.findApplication(appId)) { |
371 | + print("Shell.qml: activating app", appId); |
372 | + ApplicationManager.activateApplication(appId); |
373 | } else { |
374 | - return mainStage.usingScreenshots; |
375 | + print("starting app", appId); |
376 | + |
377 | + // FIXME: |
378 | +// var execFlags = sideStageEnabled ? ApplicationManager.NoFlag : ApplicationManager.ForceMainStage; |
379 | + var execFlags = ApplicationManager.ForceMainStage; |
380 | + |
381 | + ApplicationManager.startApplication(appId, execFlags); |
382 | } |
383 | } |
384 | |
385 | - property var applicationManager: ApplicationManagerWrapper {} |
386 | - |
387 | Binding { |
388 | target: LauncherModel |
389 | property: "applicationManager" |
390 | @@ -71,49 +73,6 @@ |
391 | |
392 | Component.onCompleted: { |
393 | Theme.name = "Ubuntu.Components.Themes.SuruGradient" |
394 | - |
395 | - applicationManager.sideStageEnabled = Qt.binding(function() { return sideStage.enabled }) |
396 | - |
397 | - // FIXME: if application focused before shell starts, shell draws on top of it only. |
398 | - // We should detect already running applications on shell start and bring them to the front. |
399 | - applicationManager.unfocusCurrentApplication(); |
400 | - } |
401 | - |
402 | - readonly property bool applicationFocused: !!applicationManager.mainStageFocusedApplication |
403 | - || !!applicationManager.sideStageFocusedApplication |
404 | - // Used for autopilot testing. |
405 | - readonly property string currentFocusedAppId: ApplicationManager.focusedApplicationId |
406 | - |
407 | - readonly property bool fullscreenMode: { |
408 | - if (greeter.shown || lockscreen.shown) { |
409 | - return false; |
410 | - } else if (mainStage.usingScreenshots) { // Window Manager animating so want to re-evaluate fullscreen mode |
411 | - return mainStage.switchingFromFullscreenToFullscreen; |
412 | - } else if (applicationManager.mainStageFocusedApplication) { |
413 | - return applicationManager.mainStageFocusedApplication.fullscreen; |
414 | - } else { |
415 | - return false; |
416 | - } |
417 | - } |
418 | - |
419 | - function activateApplication(appId, argument) { |
420 | - if (applicationManager) { |
421 | - // For newly started applications, as it takes them time to draw their first frame |
422 | - // we add a delay before we hide the animation screenshots to compensate. |
423 | - var addDelay = !applicationManager.getApplicationFromDesktopFile(appId); |
424 | - |
425 | - var application; |
426 | - application = applicationManager.activateApplication(appId, argument); |
427 | - if (application == null) { |
428 | - return; |
429 | - } |
430 | - if (application.stage == ApplicationInfo.MainStage || !sideStage.enabled) { |
431 | - mainStage.activateApplication(appId, addDelay); |
432 | - } else { |
433 | - sideStage.activateApplication(appId, addDelay); |
434 | - } |
435 | - stages.show(); |
436 | - } |
437 | } |
438 | |
439 | GSettings { |
440 | @@ -161,19 +120,19 @@ |
441 | // Whether the underlay is fully covered by opaque UI elements. |
442 | property bool fullyCovered: panel.indicators.fullyOpened && shell.width <= panel.indicatorsMenuWidth |
443 | |
444 | - readonly property bool applicationRunning: ((mainStage.applications && mainStage.applications.count > 0) |
445 | - || (sideStage.applications && sideStage.applications.count > 0)) |
446 | + readonly property bool applicationRunning: ApplicationManager.focusedApplicationId.length > 0 |
447 | + onApplicationRunningChanged: print("***************+ application running", applicationRunning) |
448 | |
449 | // Whether the user should see the topmost application surface (if there's one at all). |
450 | - readonly property bool applicationSurfaceShouldBeSeen: applicationRunning && !stages.fullyHidden |
451 | - && !mainStage.usingScreenshots // but want sideStage animating over app surface |
452 | - |
453 | - |
454 | - |
455 | - // NB! Application surfaces are stacked behing the shell one. So they can only be seen by the user |
456 | + readonly property bool applicationSurfaceShouldBeSeen: applicationRunning && !stages.painting |
457 | + |
458 | + // NB! Application surfaces are stacked behind the shell one. So they can only be seen by the user |
459 | // through the translucent parts of the shell surface. |
460 | visible: !fullyCovered && !applicationSurfaceShouldBeSeen |
461 | |
462 | + onVisibleChanged: print("hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", visible, fullyCovered, applicationSurfaceShouldBeSeen, |
463 | + applicationRunning, stages.fullyHidden, stages.painting) |
464 | + |
465 | CrossFadeImage { |
466 | id: backgroundImage |
467 | objectName: "backgroundImage" |
468 | @@ -197,12 +156,6 @@ |
469 | hides: [stages, launcher, panel.indicators] |
470 | shown: disappearingAnimationProgress !== 1.0 |
471 | enabled: disappearingAnimationProgress === 0.0 && edgeDemo.dashEnabled |
472 | - // FIXME: unfocus all applications when going back to the dash |
473 | - onEnabledChanged: { |
474 | - if (enabled) { |
475 | - shell.applicationManager.unfocusCurrentApplication() |
476 | - } |
477 | - } |
478 | |
479 | anchors { |
480 | fill: parent |
481 | @@ -233,8 +186,7 @@ |
482 | x: launcher.progress |
483 | Behavior on x {SmoothedAnimation{velocity: 600}} |
484 | |
485 | - property real showProgress: |
486 | - MathUtils.clamp(1 - (x + stages.x) / shell.width, 0, 1) |
487 | + property real showProgress: MathUtils.clamp(1 - (x + stages.x) / shell.width, 0, 1) |
488 | |
489 | Showable { |
490 | id: stages |
491 | @@ -244,6 +196,9 @@ |
492 | |
493 | property bool fullyShown: shown && x == 0 && parent.x == 0 |
494 | property bool fullyHidden: !shown && x == width |
495 | + |
496 | + property bool painting: applicationsDisplayLoader.item ? applicationsDisplayLoader.item.painting : false |
497 | + |
498 | available: !greeter.shown |
499 | hides: [panel.indicators] |
500 | shown: false |
501 | @@ -254,128 +209,57 @@ |
502 | width: parent.width |
503 | height: parent.height |
504 | |
505 | - // close the stages when no focused application remains |
506 | - Connections { |
507 | - target: shell.applicationManager |
508 | - onMainStageFocusedApplicationChanged: stages.closeIfNoApplications() |
509 | - onSideStageFocusedApplicationChanged: stages.closeIfNoApplications() |
510 | - ignoreUnknownSignals: true |
511 | - } |
512 | - |
513 | - function closeIfNoApplications() { |
514 | - if (!shell.applicationManager.mainStageFocusedApplication |
515 | - && !shell.applicationManager.sideStageFocusedApplication |
516 | - && shell.applicationManager.mainStageApplications.count == 0 |
517 | - && shell.applicationManager.sideStageApplications.count == 0) { |
518 | - stages.hide(); |
519 | - } |
520 | - } |
521 | - |
522 | - // show the stages when an application gets the focus |
523 | - Connections { |
524 | - target: shell.applicationManager |
525 | - onMainStageFocusedApplicationChanged: { |
526 | - if (shell.applicationManager.mainStageFocusedApplication) { |
527 | - mainStage.show(); |
528 | - stages.show(); |
529 | - } |
530 | - } |
531 | - onSideStageFocusedApplicationChanged: { |
532 | - if (shell.applicationManager.sideStageFocusedApplication) { |
533 | - sideStage.show(); |
534 | - stages.show(); |
535 | - } |
536 | - } |
537 | - ignoreUnknownSignals: true |
538 | - } |
539 | - |
540 | - Stage { |
541 | - id: mainStage |
542 | - |
543 | + property string lastFocusedAppId |
544 | + onShownChanged: { |
545 | + print("stages shown", shown) |
546 | + if (shown) { |
547 | + if (!ApplicationManager.focusedApplicationId && lastFocusedAppId) { |
548 | + ApplicationManager.focusApplication(lastFocusedAppId); |
549 | + } |
550 | + } else { |
551 | + lastFocusedAppId = ApplicationManager.focusedApplicationId; |
552 | + ApplicationManager.unfocusCurrentApplication(); |
553 | + } |
554 | + } |
555 | + |
556 | + Connections { |
557 | + target: ApplicationManager |
558 | + onFocusedApplicationIdChanged: { |
559 | + if (ApplicationManager.focusedApplicationId.length > 0) { |
560 | + print("should show stages") |
561 | + stages.show(); |
562 | + } else { |
563 | + stages.hide(); |
564 | + } |
565 | + } |
566 | + |
567 | + onApplicationAdded: { |
568 | + stages.show(); |
569 | + } |
570 | + } |
571 | + |
572 | + Loader { |
573 | + id: applicationsDisplayLoader |
574 | anchors.fill: parent |
575 | - fullyShown: stages.fullyShown |
576 | - fullyHidden: stages.fullyHidden |
577 | - shouldUseScreenshots: !fullyShown |
578 | - rightEdgeEnabled: !sideStage.enabled |
579 | - |
580 | - applicationManager: shell.applicationManager |
581 | - rightEdgeDraggingAreaWidth: shell.edgeSize |
582 | - normalApplicationY: shell.panelHeight |
583 | - |
584 | - shown: true |
585 | - function show() { |
586 | - stages.show(); |
587 | - } |
588 | - function hide() { |
589 | - } |
590 | - |
591 | - // FIXME: workaround the fact that focusing a main stage application |
592 | - // raises its surface on top of all other surfaces including the ones |
593 | - // that belong to side stage applications. |
594 | - onFocusedApplicationChanged: { |
595 | - if (focusedApplication && sideStage.focusedApplication && sideStage.fullyShown) { |
596 | - shell.applicationManager.focusApplication(sideStage.focusedApplication); |
597 | - } |
598 | - } |
599 | - } |
600 | - |
601 | - SideStage { |
602 | - id: sideStage |
603 | - |
604 | - applicationManager: shell.applicationManager |
605 | - rightEdgeDraggingAreaWidth: shell.edgeSize |
606 | - normalApplicationY: shell.panelHeight |
607 | - |
608 | - onShownChanged: { |
609 | - if (!shown && mainStage.applications.count == 0) { |
610 | - stages.hide(); |
611 | - } |
612 | - } |
613 | - // FIXME: when hiding the side stage, refocus the main stage |
614 | - // application so that it goes in front of the side stage |
615 | - // application and hides it |
616 | - onFullyShownChanged: { |
617 | - if (!fullyShown && stages.fullyShown && sideStage.focusedApplication != null) { |
618 | - shell.applicationManager.focusApplication(mainStage.focusedApplication); |
619 | - } |
620 | - } |
621 | - |
622 | - enabled: shell.width >= units.gu(60) |
623 | - visible: enabled |
624 | - fullyShown: stages.fullyShown && shown |
625 | - && sideStage[sideStageRevealer.boundProperty] == sideStageRevealer.openedValue |
626 | - shouldUseScreenshots: !fullyShown || mainStage.usingScreenshots || sideStageRevealer.pressed |
627 | - |
628 | - available: !greeter.shown && !lockscreen.shown && enabled |
629 | - hides: [launcher, panel.indicators] |
630 | - shown: false |
631 | - showAnimation: StandardAnimation { property: "x"; duration: 350; to: sideStageRevealer.openedValue; easing.type: Easing.OutQuint } |
632 | - hideAnimation: StandardAnimation { property: "x"; duration: 350; to: sideStageRevealer.closedValue; easing.type: Easing.OutQuint } |
633 | - |
634 | - width: units.gu(40) |
635 | - height: stages.height |
636 | - handleExpanded: sideStageRevealer.pressed |
637 | - } |
638 | - |
639 | - Revealer { |
640 | - id: sideStageRevealer |
641 | - |
642 | - enabled: mainStage.applications.count > 0 && sideStage.applications.count > 0 |
643 | - && sideStage.available |
644 | - direction: Qt.RightToLeft |
645 | - openedValue: parent.width - sideStage.width |
646 | - hintDisplacement: units.gu(3) |
647 | - /* The size of the sidestage handle needs to be bigger than the |
648 | - typical size used for edge detection otherwise it is really |
649 | - hard to grab. |
650 | - */ |
651 | - handleSize: sideStage.shown ? units.gu(4) : shell.edgeSize |
652 | - closedValue: parent.width + sideStage.handleSizeCollapsed |
653 | - target: sideStage |
654 | - x: parent.width - width |
655 | - width: sideStage.width + handleSize * 0.7 |
656 | - height: sideStage.height |
657 | - orientation: Qt.Horizontal |
658 | + |
659 | +// source: "Stages/StageWithSideStage.qml" |
660 | + source: "Stages/PhoneStage.qml" |
661 | + |
662 | + Binding { |
663 | + target: applicationsDisplayLoader.item |
664 | + property: "moving" |
665 | + value: !stages.fullyShown |
666 | + } |
667 | + Binding { |
668 | + target: applicationsDisplayLoader.item |
669 | + property: "shown" |
670 | + value: stages.shown |
671 | + } |
672 | + Binding { |
673 | + target: applicationsDisplayLoader.item |
674 | + property: "dragAreaWidth" |
675 | + value: shell.edgeSize |
676 | + } |
677 | } |
678 | |
679 | DragHandle { |
680 | @@ -388,7 +272,7 @@ |
681 | width: shell.edgeSize |
682 | direction: Direction.Leftwards |
683 | enabled: greeter.showProgress == 0 && edgeDemo.dashEnabled |
684 | - property bool haveApps: mainStage.applications.count > 0 || sideStage.applications.count > 0 |
685 | + property bool haveApps: ApplicationManager.count > 0 |
686 | |
687 | maxTotalDragDistance: haveApps ? parent.width : parent.width * 0.7 |
688 | // Make autocompletion impossible when !haveApps |
689 | @@ -473,23 +357,6 @@ |
690 | property var previousMainApp: null |
691 | property var previousSideApp: null |
692 | |
693 | - function removeApplicationFocus() { |
694 | - greeter.previousMainApp = applicationManager.mainStageFocusedApplication; |
695 | - greeter.previousSideApp = applicationManager.sideStageFocusedApplication; |
696 | - applicationManager.unfocusCurrentApplication(); |
697 | - } |
698 | - |
699 | - function restoreApplicationFocus() { |
700 | - if (greeter.previousMainApp) { |
701 | - applicationManager.focusApplication(greeter.previousMainApp); |
702 | - greeter.previousMainApp = null; |
703 | - } |
704 | - if (greeter.previousSideApp) { |
705 | - applicationManager.focusApplication(greeter.previousSideApp); |
706 | - greeter.previousSideApp = null; |
707 | - } |
708 | - } |
709 | - |
710 | onShownChanged: { |
711 | if (shown) { |
712 | lockscreen.reset(); |
713 | @@ -500,9 +367,6 @@ |
714 | greeter.selected(0); |
715 | } |
716 | greeter.forceActiveFocus(); |
717 | - removeApplicationFocus(); |
718 | - } else { |
719 | - restoreApplicationFocus(); |
720 | } |
721 | } |
722 | |
723 | @@ -519,34 +383,11 @@ |
724 | launcher.tease(); |
725 | } |
726 | } |
727 | - |
728 | - Connections { |
729 | - target: applicationManager |
730 | - ignoreUnknownSignals: true |
731 | - // If any app is focused when greeter is open, it's due to a user action |
732 | - // like a snap decision (say, an incoming call). |
733 | - // TODO: these should be protected to only unlock for certain applications / certain usecases |
734 | - // potentially only in connection with a notification. |
735 | - onMainStageFocusedApplicationChanged: { |
736 | - if (greeter.shown && applicationManager.mainStageFocusedApplication) { |
737 | - greeter.previousMainApp = null // make way for new focused app |
738 | - greeter.previousSideApp = null |
739 | - greeter.hide() |
740 | - } |
741 | - } |
742 | - onSideStageFocusedApplicationChanged: { |
743 | - if (greeter.shown && applicationManager.sideStageFocusedApplication) { |
744 | - greeter.previousMainApp = null // make way for new focused app |
745 | - greeter.previousSideApp = null |
746 | - greeter.hide() |
747 | - } |
748 | - } |
749 | - } |
750 | } |
751 | |
752 | InputFilterArea { |
753 | anchors.fill: parent |
754 | - blockInput: !applicationFocused || greeter.shown || lockscreen.shown || launcher.shown |
755 | + blockInput: ApplicationManager.focusedApplicationId.length === 0 || greeter.shown || lockscreen.shown || launcher.shown |
756 | || panel.indicators.shown || hud.shown |
757 | } |
758 | |
759 | @@ -595,7 +436,9 @@ |
760 | available: edgeDemo.panelEnabled |
761 | contentEnabled: edgeDemo.panelContentEnabled |
762 | } |
763 | - fullscreenMode: shell.fullscreenMode |
764 | + property string focusedAppId: ApplicationManager.focusedApplicationId |
765 | + property var focusedApplication: ApplicationManager.findApplication(focusedAppId) |
766 | + fullscreenMode: focusedApplication && focusedApplication.fullscreen && !greeter.shown && !lockscreen.shown |
767 | searchVisible: !greeter.shown && !lockscreen.shown && dash.shown |
768 | |
769 | InputFilterArea { |
770 | @@ -621,9 +464,8 @@ |
771 | hideAnimation: StandardAnimation { property: "y"; duration: hud.showableAnimationDuration; to: hudRevealer.closedValue; easing.type: Easing.Linear } |
772 | |
773 | Connections { |
774 | - target: shell.applicationManager |
775 | - onMainStageFocusedApplicationChanged: hud.hide() |
776 | - onSideStageFocusedApplicationChanged: hud.hide() |
777 | + target: ApplicationManager |
778 | + onFocusedApplicationIdChanged: hud.hide() |
779 | } |
780 | } |
781 | |
782 | @@ -648,7 +490,7 @@ |
783 | theHud: hud |
784 | anchors.fill: parent |
785 | enabled: hud.available |
786 | - applicationIsOnForeground: applicationFocused |
787 | + applicationIsOnForeground: ApplicationManager.focusedApplicationId |
788 | } |
789 | |
790 | InputFilterArea { |
791 | @@ -774,14 +616,14 @@ |
792 | anchors.bottom: parent.bottom |
793 | anchors.left: parent.left |
794 | anchors.right: parent.right |
795 | - height: shell.applicationManager ? shell.applicationManager.keyboardHeight : 0 |
796 | + height: ApplicationManager.keyboardVisible ? ApplicationManager.keyboardHeight : 0 |
797 | |
798 | - enabled: shell.applicationManager && shell.applicationManager.keyboardVisible |
799 | + enabled: ApplicationManager.keyboardVisible |
800 | } |
801 | |
802 | Label { |
803 | anchors.centerIn: parent |
804 | - visible: applicationManager.fake |
805 | + visible: ApplicationManager.fake ? ApplicationManager.fake : false |
806 | text: "EARLY ALPHA\nNOT READY FOR USE" |
807 | color: "lightgrey" |
808 | opacity: 0.2 |
809 | |
810 | === added directory 'qml/Stages' |
811 | === added file 'qml/Stages/PhoneStage.qml' |
812 | --- qml/Stages/PhoneStage.qml 1970-01-01 00:00:00 +0000 |
813 | +++ qml/Stages/PhoneStage.qml 2014-01-22 10:43:18 +0000 |
814 | @@ -0,0 +1,574 @@ |
815 | +import QtQuick 2.0 |
816 | +import Ubuntu.Components 0.1 |
817 | +import Ubuntu.Gestures 0.1 |
818 | +import Unity.Application 0.1 |
819 | +import Utils 0.1 |
820 | +import "../Components" |
821 | + |
822 | +/* |
823 | + |
824 | +*/ |
825 | + |
826 | +Item { |
827 | + id: root |
828 | + |
829 | + // Controls to be set from outside |
830 | + property bool shown: false |
831 | + property bool moving: false |
832 | + property int dragAreaWidth |
833 | + |
834 | + // State information propagated to the outside |
835 | + readonly property bool painting: mainScreenshotImage.visible || fadeInScreenshotImage.visible || appSplash.visible |
836 | + onPaintingChanged: print("**********************+ painting changed", painting) |
837 | + |
838 | + onMovingChanged: { |
839 | + if (moving) { |
840 | + priv.requestNewScreenshot(); |
841 | + } else { |
842 | + mainScreenshotImage.visible = false; |
843 | + } |
844 | + } |
845 | + |
846 | + Connections { |
847 | + target: ApplicationManager |
848 | + |
849 | + onFocusRequested: { |
850 | + priv.switchToApp(appId); |
851 | + } |
852 | + |
853 | + onFocusedApplicationIdChanged: { |
854 | + if (ApplicationManager.focusedApplicationId.length > 0) { |
855 | + if (priv.secondApplicationStarting || priv.applicationStarting) { |
856 | + appSplashTimer.start(); |
857 | + } else { |
858 | + mainScreenshotImage.src = ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).screenshot |
859 | + } |
860 | + } |
861 | + } |
862 | + |
863 | + onApplicationAdded: { |
864 | + if (!priv.focusedApplication) { |
865 | + mainScreenshotImage.src = ""; |
866 | + mainScreenshotImage.visible = false; |
867 | + priv.applicationStarting = true; |
868 | + } else { |
869 | + mainScreenshotImage.src = "foobar"; |
870 | + priv.newFocusedAppId = appId; |
871 | + priv.secondApplicationStarting = true; |
872 | + priv.requestNewScreenshot(); |
873 | + } |
874 | + } |
875 | + } |
876 | + |
877 | + QtObject { |
878 | + id: priv |
879 | + |
880 | + property string focusedAppId: ApplicationManager.focusedApplicationId |
881 | + property var focusedApplication: ApplicationManager.findApplication(focusedAppId) |
882 | + property url focusedScreenshot: focusedApplication ? focusedApplication.screenshot : "" |
883 | + |
884 | + property bool waitingForScreenshot: false |
885 | + |
886 | + property bool applicationStarting: false |
887 | + property bool secondApplicationStarting: false |
888 | + |
889 | + property string newFocusedAppId |
890 | + |
891 | + onFocusedScreenshotChanged: { |
892 | + if (root.moving && priv.waitingForScreenshot) { |
893 | + mainScreenshotImage.anchors.leftMargin = 0; |
894 | + mainScreenshotImage.src = ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).screenshot; |
895 | + mainScreenshotImage.visible = true; |
896 | + } else if (priv.secondApplicationStarting && priv.waitingForScreenshot) { |
897 | + applicationSwitchingAnimation.start(); |
898 | + } |
899 | + waitingForScreenshot = false; |
900 | + } |
901 | + |
902 | + function requestNewScreenshot() { |
903 | + waitingForScreenshot = true; |
904 | + ApplicationManager.updateScreenshot(ApplicationManager.focusedApplicationId); |
905 | + } |
906 | + |
907 | + function switchToApp(appId) { |
908 | + priv.newFocusedAppId = appId; |
909 | + applicationSwitchingAnimation.start(); |
910 | + grantFocusTimer.start(); |
911 | + } |
912 | + |
913 | + } |
914 | + |
915 | + // FIXME: the signal connection seems to get lost with the fake application manager. |
916 | + // Check with Qt 5.2, see if we can remove this Connections object |
917 | + Connections { |
918 | + target: priv.focusedApplication |
919 | + onScreenshotChanged: priv.focusedScreenshot = priv.focusedApplication.screenshot |
920 | + } |
921 | + |
922 | + Timer { |
923 | + id: grantFocusTimer |
924 | + // Delay the actual switch to be covered by the animation for sure. |
925 | + // 1) If we switch before starting the animation, the Mir event loop paints before the Qt event loop => flickering |
926 | + // 2) If we do the switch after the animation, the panel wouldn't fade in early enough. |
927 | + interval: UbuntuAnimation.SlowDuration / 4 |
928 | + repeat: false |
929 | + onTriggered: { |
930 | + ApplicationManager.focusApplication(priv.newFocusedAppId); |
931 | + } |
932 | + } |
933 | + |
934 | + Timer { |
935 | + id: appSplashTimer |
936 | + // This is to show the splash screen a bit longer. |
937 | + // Mir signals us that the newly started app has gotten focus before it paints something on the screen |
938 | + // This would result in the old app surface becoming visible for a bit. |
939 | + // FIXME: change appManager to only change the focusedApplicationId when the surface is ready to be shown. |
940 | + interval: 1500 |
941 | + repeat: false |
942 | + onTriggered: { |
943 | + priv.applicationStarting = false; |
944 | + priv.secondApplicationStarting = false; |
945 | + } |
946 | + } |
947 | + |
948 | + SequentialAnimation { |
949 | + id: applicationSwitchingAnimation |
950 | + // setup |
951 | + PropertyAction { target: mainScreenshotImage; property: "anchors.leftMargin"; value: 0 } |
952 | + // PropertyAction seems to fail when secondApplicationStarting and we didn't have another screenshot before |
953 | + ScriptAction { script: mainScreenshotImage.src = priv.focusedScreenshot } |
954 | + PropertyAction { target: mainScreenshotImage; property: "visible"; value: true } |
955 | + PropertyAction { target: fadeInScreenshotImage; property: "source"; value: ApplicationManager.findApplication(priv.newFocusedAppId).screenshot } |
956 | + PropertyAction { target: fadeInScreenshotImage; property: "visible"; value: true } |
957 | + PropertyAction { target: fadeInScreenshotImage; property: "opacity"; value: 0 } |
958 | + PropertyAction { target: fadeInScreenshotImage; property: "scale"; value: .8 } |
959 | + |
960 | + |
961 | + // The actual animation |
962 | + ParallelAnimation { |
963 | + UbuntuNumberAnimation { target: mainScreenshotImage; property: "anchors.leftMargin"; to: root.width; duration: UbuntuAnimation.SlowDuration } |
964 | + UbuntuNumberAnimation { target: fadeInScreenshotImage; property: "opacity"; to: 1; duration: UbuntuAnimation.SlowDuration } |
965 | + UbuntuNumberAnimation { target: fadeInScreenshotImage; property: "scale"; to: 1; duration: UbuntuAnimation.SlowDuration } |
966 | + } |
967 | + |
968 | + // restore stuff |
969 | + PropertyAction { target: fadeInScreenshotImage; property: "visible"; value: false } |
970 | + PropertyAction { target: mainScreenshotImage; property: "visible"; value: false } |
971 | + } |
972 | + |
973 | + // FIXME: Drop this and make the imageprovider show a splashscreen instead |
974 | + Rectangle { |
975 | + id: appSplash2 |
976 | + anchors.fill: parent |
977 | + color: "white" |
978 | + visible: priv.secondApplicationStarting |
979 | + } |
980 | + Image { |
981 | + id: fadeInScreenshotImage |
982 | + anchors { left: parent.left; bottom: parent.bottom } |
983 | + width: parent.width |
984 | + scale: .7 |
985 | + visible: false |
986 | + } |
987 | + |
988 | + Rectangle { |
989 | + id: appSplash |
990 | + anchors.fill: parent |
991 | + color: "white" |
992 | + visible: priv.applicationStarting |
993 | + } |
994 | + Image { |
995 | + id: mainScreenshotImage |
996 | + anchors { left: parent.left; bottom: parent.bottom } |
997 | + width: parent.width |
998 | + |
999 | + property string src |
1000 | + source: src |
1001 | + visible: false |
1002 | + } |
1003 | + |
1004 | + EdgeDragArea { |
1005 | + id: coverFlipDragArea |
1006 | + direction: Direction.Leftwards |
1007 | + |
1008 | + //enabled: root.available |
1009 | + anchors { top: parent.top; right: parent.right; bottom: parent.bottom } |
1010 | + width: root.dragAreaWidth |
1011 | + |
1012 | + onTouchXChanged: { |
1013 | + if (!dragging && !priv.waitingForScreenshot) { |
1014 | + priv.requestNewScreenshot(); |
1015 | + } |
1016 | + if (dragging && !priv.waitingForScreenshot) { |
1017 | + coverFlickable.contentX = -touchX |
1018 | + } |
1019 | + } |
1020 | + |
1021 | + onDraggingChanged: { |
1022 | + if (!dragging) { |
1023 | + coverFlip.snap(); |
1024 | + } |
1025 | + } |
1026 | + } |
1027 | + |
1028 | + Rectangle { |
1029 | + id: coverFlipBackground |
1030 | + anchors.fill: parent |
1031 | + color: "black" |
1032 | + visible: coverFlip.visible |
1033 | + } |
1034 | + |
1035 | + InputFilterArea { |
1036 | + anchors.fill: root |
1037 | + blockInput: coverFlip.visible |
1038 | + } |
1039 | + |
1040 | + Flickable { |
1041 | + id: coverFlickable |
1042 | + anchors.fill: root |
1043 | + contentHeight: height |
1044 | + contentWidth: width * 2 + ((ApplicationManager.count - 2) * root.width * coverFlip.tileDistance * 1.5) |
1045 | + flickableDirection: Qt.Horizontal |
1046 | + enabled: coverFlip.visible |
1047 | + |
1048 | + property bool passedFirstStage: false |
1049 | + |
1050 | + onContentXChanged: { |
1051 | + if (coverFlickable.passedFirstStage && contentX < width * coverFlip.progressMarker1) { |
1052 | + contentX = width * coverFlip.progressMarker1; |
1053 | + return; |
1054 | + } |
1055 | + var progress = contentX / width |
1056 | + if (progress > coverFlip.progressMarker1) { |
1057 | + coverFlickable.passedFirstStage = true; |
1058 | + } |
1059 | + coverFlip.progress = progress; |
1060 | + } |
1061 | + |
1062 | + Row { |
1063 | + id: coverFlip |
1064 | + height: parent.height |
1065 | + // The MouseAreas on the AppImages need to be children of the flickable in order to make focus stealing |
1066 | + // for flicking vs. clicking work correctly. |
1067 | + // However, for the animation calculations to become easier we don't want the row to move so we don't |
1068 | + // always have to take contentX into account. So lets just compensate the flickable's movement here. |
1069 | + x: coverFlickable.contentX |
1070 | + visible: progress > 0 |
1071 | + |
1072 | + property real progress: 0 |
1073 | + property real startAngle: 45 |
1074 | + property int endAngle: 10 |
1075 | + |
1076 | + property real maxScale: 1.4 |
1077 | + property real minScale: .6 |
1078 | + |
1079 | + // Markers: relative screen position from left to right |
1080 | + // marks the line where first application is finished moving in from the right |
1081 | + property real progressMarker1: 0.5 |
1082 | + |
1083 | + property real tileDistance: 0.1 |
1084 | + |
1085 | + property bool animatingBack: false |
1086 | + |
1087 | + property real tileWidth: root.width |
1088 | + |
1089 | + property real oldProgress: 0 |
1090 | + onProgressChanged: { |
1091 | + if (coverFlipDragArea.dragging) { |
1092 | + if (oldProgress < coverFlip.progressMarker1 && progress >= coverFlip.progressMarker1) { |
1093 | + ApplicationManager.move(0, 1) |
1094 | + } else if (oldProgress >= coverFlip.progressMarker1 && progress < coverFlip.progressMarker1) { |
1095 | + ApplicationManager.move(0, 1) |
1096 | + } |
1097 | + } |
1098 | + oldProgress = progress; |
1099 | + } |
1100 | + |
1101 | + function snap() { |
1102 | + if (coverFlip.progress < 0.25) { |
1103 | + snapAnimation.targetContentX = 0 |
1104 | + snapAnimation.targetAppId = ApplicationManager.get(0).appId; |
1105 | + } else if (coverFlip.progress < coverFlip.progressMarker1) { |
1106 | + snapAnimation.targetContentX = root.width * coverFlip.progressMarker1 |
1107 | + snapAnimation.targetAppId = ApplicationManager.get(1).appId; |
1108 | + } else if (coverFlip.progress < 0.6) { |
1109 | + snapAnimation.targetContentX = root.width * coverFlip.progressMarker1 |
1110 | + snapAnimation.targetAppId = ApplicationManager.get(0).appId; |
1111 | + } else { |
1112 | + if (ApplicationManager.count == 3) { |
1113 | + snapAnimation.targetContentX = root.width * 1.1; |
1114 | + } else { |
1115 | + snapAnimation.targetContentX = root.width * 1.25; |
1116 | + } |
1117 | + snapAnimation.targetAppId = ""; |
1118 | + } |
1119 | + snapAnimation.start(); |
1120 | + } |
1121 | + |
1122 | + function selectItem(index) { |
1123 | + tileRepeater.itemAt(index).select(); |
1124 | + } |
1125 | + |
1126 | + SequentialAnimation { |
1127 | + id: snapAnimation |
1128 | + property int targetContentX: 0 |
1129 | + property string targetAppId |
1130 | + |
1131 | + UbuntuNumberAnimation { |
1132 | + target: coverFlickable |
1133 | + properties: "contentX" |
1134 | + to: snapAnimation.targetContentX |
1135 | + duration: UbuntuAnimation.SnapDuration |
1136 | + } |
1137 | + ScriptAction { |
1138 | + script: { |
1139 | + if (snapAnimation.targetAppId) { |
1140 | + coverFlickable.passedFirstStage = false; |
1141 | + ApplicationManager.focusApplication(snapAnimation.targetAppId); |
1142 | + } |
1143 | + if (snapAnimation.targetContentX == root.width * coverFlip.progressMarker1) { |
1144 | + coverFlickable.contentX = 0; |
1145 | + } |
1146 | + } |
1147 | + } |
1148 | + } |
1149 | + |
1150 | + Repeater { |
1151 | + id: tileRepeater |
1152 | + model: ApplicationManager |
1153 | + |
1154 | + Item { |
1155 | + id: appItem |
1156 | + height: parent.height |
1157 | + width: coverFlip.tileWidth |
1158 | + |
1159 | + // This is the main progress, of the gesture, the same for every tile |
1160 | + property real progress: coverFlip.progress |
1161 | + // The progress, translated for the second stage of the animation, after the first app switch has happened |
1162 | + // Additionally it speeds it up a bit, depending on the distance of the tile |
1163 | + property real translatedProgress: appItem.progress - coverFlip.progressMarker1 - (coverFlip.tileDistance * (index-1)) |
1164 | + |
1165 | + // Is this tile selected by a click? |
1166 | + property bool isSelected: false |
1167 | + // We need to remember some values when app is selected to be able to animate it to the foreground |
1168 | + property real selectedXTranslation: 0 |
1169 | + property real selectedTranslatedProgress: 0 |
1170 | + property real selectedProgress: 0 |
1171 | + property real selectedAngle: 0 |
1172 | + property real selectedXScale: 0 |
1173 | + |
1174 | + function select() { |
1175 | + appItem.selectedXTranslation = appItem.xTranslation; |
1176 | + appItem.selectedAngle = appItem.angle; |
1177 | + appItem.selectedXScale = appItem.xScale; |
1178 | + appItem.selectedTranslatedProgress = appItem.translatedProgress - coverFlip.progressMarker1; |
1179 | + appItem.selectedProgress = appItem.progress - coverFlip.progressMarker1; |
1180 | + appItem.isSelected = true; |
1181 | + switchToAppAnimation.targetContentX = coverFlip.progressMarker1 * root.width |
1182 | + switchToAppAnimation.start(); |
1183 | + } |
1184 | + |
1185 | + property int xTranslation: { |
1186 | + var xTranslate = 0; |
1187 | + var minXTranslate = -index * root.width + index * units.dp(3); |
1188 | + switch (index) { |
1189 | + case 0: |
1190 | + if (appItem.progress < coverFlip.progressMarker1) { |
1191 | + var progress = appItem.progress |
1192 | + var progressDiff = coverFlip.progressMarker1 |
1193 | + var translateDiff = -root.width * 0.25 |
1194 | + // progress : progressDiff = translate : translateDiff |
1195 | + xTranslate = progress * translateDiff / progressDiff |
1196 | + } |
1197 | + break; |
1198 | + case 1: |
1199 | + if (appItem.progress < coverFlip.progressMarker1) { |
1200 | + var progress = appItem.progress; |
1201 | + var progressDiff = coverFlip.progressMarker1; |
1202 | + var translateDiff = -root.width; |
1203 | + // progress : progressDiff = translate : translateDiff |
1204 | + xTranslate = progress * translateDiff / progressDiff; |
1205 | + break; |
1206 | + } |
1207 | + // Intentionally no break here... |
1208 | + default: |
1209 | + if (appItem.progress > coverFlip.progressMarker1) { |
1210 | + xTranslate = xTranslateEasing.value * xTranslateEasing.period; |
1211 | + if (appItem.isSelected) { |
1212 | + var translateDiff = root.width * index + appItem.selectedXTranslation |
1213 | + var progressDiff = appItem.selectedProgress |
1214 | + var progress = progressDiff - (appItem.progress - coverFlip.progressMarker1); |
1215 | + // progress : progressDiff = translate : translateDiff |
1216 | + var newTranslate = progress * translateDiff / progressDiff; |
1217 | + |
1218 | + xTranslate = appItem.selectedXTranslation - newTranslate; |
1219 | + } |
1220 | + break; |
1221 | + } |
1222 | + } |
1223 | + return xTranslate; |
1224 | + } |
1225 | + |
1226 | + property real angle: { |
1227 | + var newAngle = 0; |
1228 | + switch (index) { |
1229 | + case 0: |
1230 | + if (appItem.progress < coverFlip.progressMarker1) { |
1231 | + var progress = appItem.progress; |
1232 | + var angleDiff = coverFlip.endAngle; |
1233 | + var progressDiff = coverFlip.progressMarker1; |
1234 | + // progress : progressDiff = angle : angleDiff |
1235 | + newAngle = progress * angleDiff / progressDiff; |
1236 | + } else { |
1237 | + var progress = appItem.progress - coverFlip.progressMarker1; |
1238 | + var angleDiff = coverFlip.endAngle; |
1239 | + var progressDiff = 1 - coverFlip.progressMarker1; |
1240 | + // progress : progressDiff = angle : angleDiff |
1241 | + newAngle = progress * angleDiff / progressDiff; |
1242 | + newAngle = Math.min(coverFlip.endAngle, newAngle); |
1243 | + } |
1244 | + break; |
1245 | + case 1: |
1246 | + if (appItem.progress < coverFlip.progressMarker1) { |
1247 | + var progress = coverFlip.progress; |
1248 | + var angleDiff = coverFlip.startAngle; |
1249 | + var progressDiff = coverFlip.progressMarker1; |
1250 | + // progress : progressDiff = angle : angleDiff |
1251 | + var angle = progress * angleDiff / progressDiff; |
1252 | + newAngle = coverFlip.startAngle - angle; |
1253 | + break; |
1254 | + } |
1255 | + // Intentionally no break here... |
1256 | + default: |
1257 | + newAngle = coverFlip.startAngle - (angleEasing.value * angleEasing.period); |
1258 | + // make sure we stop at the left screen edge |
1259 | + newAngle = Math.max(newAngle, coverFlip.endAngle); |
1260 | + |
1261 | + if (appItem.isSelected) { |
1262 | +// var selectedAngleTranslate = selectedAngleEasing.value * selectedAngleEasing.period |
1263 | + var angleDiff = appItem.selectedAngle |
1264 | + var progressDiff = appItem.selectedProgress |
1265 | + var progress = progressDiff - (appItem.progress - coverFlip.progressMarker1); |
1266 | + // progress : progressDiff = angle : angleDiff |
1267 | + var selectedAngleTranslate = progress * angleDiff / progressDiff; |
1268 | + |
1269 | + newAngle = appItem.selectedAngle - selectedAngleTranslate; |
1270 | + } |
1271 | + } |
1272 | + return newAngle; |
1273 | + } |
1274 | + |
1275 | + property real xScale: { |
1276 | + var scale = 1; |
1277 | + |
1278 | + switch (index) { |
1279 | + case 0: |
1280 | + if (appItem.progress > coverFlip.progressMarker1) { |
1281 | + var scaleDiff = coverFlip.maxScale - 1; |
1282 | + var progressDiff = 1.5 - coverFlip.progressMarker1; |
1283 | + // progress : progressDiff = scale : scaleDiff |
1284 | + scale = 1 - (appItem.progress - coverFlip.progressMarker1) * scaleDiff / progressDiff; |
1285 | + } |
1286 | + break; |
1287 | + case 1: |
1288 | + if (appItem.progress < coverFlip.progressMarker1) { |
1289 | + var scaleDiff = coverFlip.maxScale - 1 |
1290 | + var progressDiff = coverFlip.progressMarker1 |
1291 | + // progress : progressDiff = scale : scaleDiff |
1292 | + scale = coverFlip.maxScale - (appItem.progress * scaleDiff / progressDiff); |
1293 | + break; |
1294 | + } |
1295 | + // Intentionally no break |
1296 | + default: |
1297 | + scale = coverFlip.maxScale - scaleEasing.value * scaleEasing.period; |
1298 | + if (appItem.isSelected) { |
1299 | + var scaleDiff = -(1 - appItem.selectedXScale) |
1300 | + var progressDiff = appItem.selectedProgress |
1301 | + var progress = progressDiff - (appItem.progress - coverFlip.progressMarker1); |
1302 | + // progress : progressDiff = angle : angleDiff |
1303 | + var selectedScaleTranslate = progress * scaleDiff / progressDiff; |
1304 | + |
1305 | + scale = appItem.selectedXScale - selectedScaleTranslate; |
1306 | + } |
1307 | + } |
1308 | + return Math.min(coverFlip.maxScale, Math.max(coverFlip.minScale, scale)); |
1309 | + } |
1310 | + |
1311 | + EasingCurve { |
1312 | + id: xTranslateEasing |
1313 | + type: EasingCurve.OutQuad |
1314 | + period: index * -width |
1315 | + progress: appItem.translatedProgress |
1316 | + } |
1317 | + EasingCurve { |
1318 | + id: angleEasing |
1319 | + type: EasingCurve.InQuad |
1320 | + period: coverFlip.startAngle - coverFlip.endAngle |
1321 | + progress: appItem.translatedProgress |
1322 | + } |
1323 | + EasingCurve { |
1324 | + id: scaleEasing |
1325 | + type: EasingCurve.Linear |
1326 | + period: coverFlip.maxScale - coverFlip.minScale |
1327 | + progress: appItem.translatedProgress |
1328 | + } |
1329 | + |
1330 | + transform: [ |
1331 | + Rotation { |
1332 | + origin { x: 0; y: coverFlip.height / 2 } |
1333 | + axis { x: 0; y: 1; z: 0 } |
1334 | + angle: appItem.angle |
1335 | + }, |
1336 | + Translate { |
1337 | + x: appItem.xTranslation |
1338 | + }, |
1339 | + Scale { |
1340 | + origin { x: appItem.xTranslation; y: root.height / 2 - (root.height - appImage.height)} |
1341 | + xScale: appItem.xScale |
1342 | + yScale: xScale |
1343 | + } |
1344 | + ] |
1345 | + |
1346 | + Image { |
1347 | + id: dropShadow |
1348 | + anchors.fill: appImage |
1349 | + anchors.margins: -units.gu(2) |
1350 | + source: "graphics/dropshadow.png" |
1351 | + } |
1352 | + Image { |
1353 | + id: appImage |
1354 | + anchors { left: parent.left; bottom: parent.bottom } |
1355 | + width: root.width |
1356 | + source: ApplicationManager.get(index).screenshot |
1357 | + scale: 1 |
1358 | + } |
1359 | + MouseArea { |
1360 | + anchors.fill: parent |
1361 | + onClicked: { |
1362 | + appItem.select() |
1363 | + } |
1364 | + } |
1365 | + |
1366 | + SequentialAnimation { |
1367 | + id: switchToAppAnimation |
1368 | + property int targetContentX |
1369 | + UbuntuNumberAnimation { |
1370 | + target: coverFlickable; |
1371 | + property: "contentX"; |
1372 | + to: switchToAppAnimation.targetContentX; |
1373 | + duration: UbuntuAnimation.SnapDuration |
1374 | + } |
1375 | + ScriptAction { |
1376 | + script: { |
1377 | + ApplicationManager.focusApplication(ApplicationManager.get(index).appId); |
1378 | + appItem.isSelected = false; |
1379 | + coverFlip.progress = 0; |
1380 | + coverFlickable.passedFirstStage = false; |
1381 | + } |
1382 | + } |
1383 | + } |
1384 | + } |
1385 | + } |
1386 | + } |
1387 | + } |
1388 | +} |
1389 | |
1390 | === added directory 'qml/Stages/graphics' |
1391 | === added file 'qml/Stages/graphics/dropshadow.png' |
1392 | Binary files qml/Stages/graphics/dropshadow.png 1970-01-01 00:00:00 +0000 and qml/Stages/graphics/dropshadow.png 2014-01-22 10:43:18 +0000 differ |
1393 | === modified file 'run_on_device' |
1394 | --- run_on_device 2013-11-27 11:47:14 +0000 |
1395 | +++ run_on_device 2014-01-22 10:43:18 +0000 |
1396 | @@ -103,6 +103,7 @@ |
1397 | ARGS="$ARGS -k" |
1398 | fi |
1399 | |
1400 | + exec_with_ssh "killall -9 dialer-app; killall -9 gallery-app; killall -9 messaging-app; killall -9 address-book-app" |
1401 | exec_with_ssh "stop unity8" |
1402 | exec_with_ssh "start maliit-server" |
1403 | exec_with_ssh "cd $CODE_DIR/ && ./run $ARGS -- $RUN_OPTIONS" |
1404 | |
1405 | === modified file 'tests/mocks/Unity/Application/ApplicationInfo.h' |
1406 | --- tests/mocks/Unity/Application/ApplicationInfo.h 2013-10-11 11:37:04 +0000 |
1407 | +++ tests/mocks/Unity/Application/ApplicationInfo.h 2014-01-22 10:43:18 +0000 |
1408 | @@ -19,6 +19,7 @@ |
1409 | |
1410 | #include <QObject> |
1411 | #include <QQmlComponent> |
1412 | +#include <QDebug> |
1413 | |
1414 | class QQuickItem; |
1415 | |
1416 | @@ -56,6 +57,7 @@ |
1417 | { \ |
1418 | if (m_##name != value) { \ |
1419 | m_##name = value; \ |
1420 | + qDebug() << "emitting changed" << this << Q_FUNC_INFO; \ |
1421 | Q_EMIT name##Changed(); \ |
1422 | } \ |
1423 | } \ |
1424 | @@ -74,6 +76,7 @@ |
1425 | IMPLEMENT_PROPERTY(fullscreen, Fullscreen, bool) |
1426 | IMPLEMENT_PROPERTY(imageQml, ImageQml, QString) |
1427 | IMPLEMENT_PROPERTY(windowQml, WindowQml, QString) |
1428 | + IMPLEMENT_PROPERTY(screenshot, Screenshot, QUrl) |
1429 | |
1430 | #undef IMPLEMENT_PROPERTY |
1431 | |
1432 | |
1433 | === modified file 'tests/mocks/Unity/Application/ApplicationManager.cpp' |
1434 | --- tests/mocks/Unity/Application/ApplicationManager.cpp 2013-12-17 16:04:47 +0000 |
1435 | +++ tests/mocks/Unity/Application/ApplicationManager.cpp 2014-01-22 10:43:18 +0000 |
1436 | @@ -24,6 +24,9 @@ |
1437 | #include <QQuickItem> |
1438 | #include <QQuickView> |
1439 | #include <QQmlComponent> |
1440 | +#include <QTimer> |
1441 | +#include <QDateTime> |
1442 | +#include <QDebug> |
1443 | |
1444 | ApplicationManager::ApplicationManager(QObject *parent) |
1445 | : ApplicationManagerInterface(parent) |
1446 | @@ -63,6 +66,8 @@ |
1447 | return app->state(); |
1448 | case RoleFocused: |
1449 | return app->focused(); |
1450 | + case RoleScreenshot: |
1451 | + return app->screenshot(); |
1452 | default: |
1453 | return QVariant(); |
1454 | } |
1455 | @@ -75,7 +80,9 @@ |
1456 | } |
1457 | |
1458 | ApplicationInfo *ApplicationManager::findApplication(const QString &appId) const { |
1459 | + qDebug() << "findapp called"; |
1460 | for (ApplicationInfo *app : m_runningApplications) { |
1461 | + qDebug() << "searching app"<< app->appId(); |
1462 | if (app->appId() == appId) { |
1463 | return app; |
1464 | } |
1465 | @@ -84,8 +91,10 @@ |
1466 | } |
1467 | |
1468 | void ApplicationManager::add(ApplicationInfo *application) { |
1469 | - if (!application) |
1470 | + qDebug() << "add() " << application->appId(); |
1471 | + if (!application) { |
1472 | return; |
1473 | + } |
1474 | |
1475 | beginInsertRows(QModelIndex(), m_runningApplications.size(), m_runningApplications.size()); |
1476 | m_runningApplications.append(application); |
1477 | @@ -171,6 +180,8 @@ |
1478 | } |
1479 | add(application); |
1480 | |
1481 | + QMetaObject::invokeMethod(this, "focusApplication", Qt::QueuedConnection, Q_ARG(QString, appId)); |
1482 | + |
1483 | return application; |
1484 | } |
1485 | |
1486 | @@ -182,9 +193,33 @@ |
1487 | |
1488 | remove(application); |
1489 | Q_EMIT focusedApplicationIdChanged(); |
1490 | + qDebug() << Q_FUNC_INFO << "emitting focusedAppChanged" << focusedApplicationId(); |
1491 | return true; |
1492 | } |
1493 | |
1494 | +void ApplicationManager::updateScreenshot(const QString &appId) |
1495 | +{ |
1496 | + int idx = -1; |
1497 | + ApplicationInfo *application = nullptr; |
1498 | + for (int i = 0; i < m_availableApplications.count(); ++i) { |
1499 | + application = m_availableApplications.at(i); |
1500 | + if (application->appId() == appId) { |
1501 | + idx = i; |
1502 | + break; |
1503 | + } |
1504 | + } |
1505 | + |
1506 | + if (idx == -1) { |
1507 | + return; |
1508 | + } |
1509 | + |
1510 | + qDebug() << "setting new screenshot" << application->screenshot() << application; |
1511 | + application->setScreenshot(QString("image://application/%1/%2").arg(appId).arg(QDateTime::currentMSecsSinceEpoch())); |
1512 | + qDebug() << "set new screenshot" << application->screenshot(); |
1513 | + QModelIndex appIndex = index(idx); |
1514 | + Q_EMIT dataChanged(appIndex, appIndex, QVector<int>() << RoleScreenshot); |
1515 | +} |
1516 | + |
1517 | QString ApplicationManager::focusedApplicationId() const { |
1518 | for (ApplicationInfo *app : m_runningApplications) { |
1519 | if (app->focused()) { |
1520 | @@ -238,10 +273,16 @@ |
1521 | |
1522 | // move app to top of stack |
1523 | move(m_runningApplications.indexOf(application), 0); |
1524 | + qDebug() << Q_FUNC_INFO << "emitting focusedAppChanged" << focusedApplicationId(); |
1525 | Q_EMIT focusedApplicationIdChanged(); |
1526 | return true; |
1527 | } |
1528 | |
1529 | +void ApplicationManager::activateApplication(const QString &appId) |
1530 | +{ |
1531 | + QMetaObject::invokeMethod(this, "focusRequested", Qt::QueuedConnection, Q_ARG(QString, appId)); |
1532 | +} |
1533 | + |
1534 | void ApplicationManager::unfocusCurrentApplication() |
1535 | { |
1536 | for (ApplicationInfo *app : m_runningApplications) { |
1537 | @@ -250,6 +291,7 @@ |
1538 | app->setFocused(false); |
1539 | } |
1540 | } |
1541 | + qDebug() << Q_FUNC_INFO << "emitting focusedAppChanged" << focusedApplicationId(); |
1542 | Q_EMIT focusedApplicationIdChanged(); |
1543 | } |
1544 | |
1545 | @@ -299,6 +341,7 @@ |
1546 | application->setName("Phone"); |
1547 | application->setIcon(QUrl("phone")); |
1548 | application->setStage(ApplicationInfo::SideStage); |
1549 | + application->setScreenshot(QString("image://application/%1/123456789").arg(application->appId())); |
1550 | generateQmlStrings(application); |
1551 | m_availableApplications.append(application); |
1552 | |
1553 | @@ -307,6 +350,7 @@ |
1554 | application->setName("Camera"); |
1555 | application->setIcon(QUrl("camera")); |
1556 | application->setFullscreen(true); |
1557 | + application->setScreenshot(QUrl("image://application/phone/123456789")); |
1558 | generateQmlStrings(application); |
1559 | m_availableApplications.append(application); |
1560 | |
1561 | @@ -314,6 +358,7 @@ |
1562 | application->setAppId("gallery-app"); |
1563 | application->setName("Gallery"); |
1564 | application->setIcon(QUrl("gallery")); |
1565 | + application->setScreenshot(QUrl("image://application/phone/123456789")); |
1566 | generateQmlStrings(application); |
1567 | m_availableApplications.append(application); |
1568 | |
1569 | @@ -322,6 +367,7 @@ |
1570 | application->setName("Facebook"); |
1571 | application->setIcon(QUrl("facebook")); |
1572 | application->setStage(ApplicationInfo::SideStage); |
1573 | + application->setScreenshot(QUrl("image://application/phone/123456789")); |
1574 | generateQmlStrings(application); |
1575 | m_availableApplications.append(application); |
1576 | |
1577 | @@ -329,6 +375,7 @@ |
1578 | application->setAppId("webbrowser-app"); |
1579 | application->setName("Browser"); |
1580 | application->setIcon(QUrl("browser")); |
1581 | + application->setScreenshot(QUrl("image://application/phone/123456789")); |
1582 | generateQmlStrings(application); |
1583 | m_availableApplications.append(application); |
1584 | |
1585 | @@ -337,6 +384,7 @@ |
1586 | application->setName("Twitter"); |
1587 | application->setIcon(QUrl("twitter")); |
1588 | application->setStage(ApplicationInfo::SideStage); |
1589 | + application->setScreenshot(QUrl("image://application/phone/123456789")); |
1590 | generateQmlStrings(application); |
1591 | m_availableApplications.append(application); |
1592 | |
1593 | @@ -344,6 +392,7 @@ |
1594 | application->setAppId("gmail-webapp"); |
1595 | application->setName("GMail"); |
1596 | application->setIcon(QUrl("gmail")); |
1597 | + application->setScreenshot(QUrl("image://application/phone/123456789")); |
1598 | m_availableApplications.append(application); |
1599 | |
1600 | application = new ApplicationInfo(this); |
1601 | @@ -351,6 +400,7 @@ |
1602 | application->setName("Weather"); |
1603 | application->setIcon(QUrl("weather")); |
1604 | application->setStage(ApplicationInfo::SideStage); |
1605 | + application->setScreenshot(QUrl("image://application/phone/123456789")); |
1606 | generateQmlStrings(application); |
1607 | m_availableApplications.append(application); |
1608 | |
1609 | @@ -359,6 +409,7 @@ |
1610 | application->setName("Notepad"); |
1611 | application->setIcon(QUrl("notepad")); |
1612 | application->setStage(ApplicationInfo::SideStage); |
1613 | + application->setScreenshot(QUrl("image://application/phone/123456789")); |
1614 | m_availableApplications.append(application); |
1615 | |
1616 | application = new ApplicationInfo(this); |
1617 | @@ -366,6 +417,7 @@ |
1618 | application->setName("Calendar"); |
1619 | application->setIcon(QUrl("calendar")); |
1620 | application->setStage(ApplicationInfo::SideStage); |
1621 | + application->setScreenshot(QUrl("image://application/phone/123456789")); |
1622 | m_availableApplications.append(application); |
1623 | |
1624 | application = new ApplicationInfo(this); |
1625 | @@ -373,18 +425,21 @@ |
1626 | application->setName("Media Player"); |
1627 | application->setIcon(QUrl("mediaplayer-app")); |
1628 | application->setFullscreen(true); |
1629 | + application->setScreenshot(QUrl("image://application/phone/123456789")); |
1630 | m_availableApplications.append(application); |
1631 | |
1632 | application = new ApplicationInfo(this); |
1633 | application->setAppId("evernote"); |
1634 | application->setName("Evernote"); |
1635 | application->setIcon(QUrl("evernote")); |
1636 | + application->setScreenshot(QUrl("image://application/phone/123456789")); |
1637 | m_availableApplications.append(application); |
1638 | |
1639 | application = new ApplicationInfo(this); |
1640 | application->setAppId("map"); |
1641 | application->setName("Map"); |
1642 | application->setIcon(QUrl("map")); |
1643 | + application->setScreenshot(QUrl("image://application/phone/123456789")); |
1644 | generateQmlStrings(application); |
1645 | m_availableApplications.append(application); |
1646 | |
1647 | |
1648 | === modified file 'tests/mocks/Unity/Application/ApplicationManager.h' |
1649 | --- tests/mocks/Unity/Application/ApplicationManager.h 2013-10-11 11:37:04 +0000 |
1650 | +++ tests/mocks/Unity/Application/ApplicationManager.h 2014-01-22 10:43:18 +0000 |
1651 | @@ -88,10 +88,12 @@ |
1652 | |
1653 | // Application control methods |
1654 | Q_INVOKABLE bool focusApplication(const QString &appId) override; |
1655 | + Q_INVOKABLE void activateApplication(const QString &appId) override; |
1656 | Q_INVOKABLE void unfocusCurrentApplication() override; |
1657 | Q_INVOKABLE ApplicationInfo *startApplication(const QString &appId, const QStringList &arguments = QStringList()) override; |
1658 | Q_INVOKABLE ApplicationInfo *startApplication(const QString &appId, ExecFlags flags, const QStringList &arguments = QStringList()); |
1659 | Q_INVOKABLE bool stopApplication(const QString &appId) override; |
1660 | + Q_INVOKABLE void updateScreenshot(const QString &appId) override; |
1661 | |
1662 | QString focusedApplicationId() const override; |
1663 | |
1664 | |
1665 | === added file 'tests/mocks/Unity/Application/ApplicationScreenshotProvider.cpp' |
1666 | --- tests/mocks/Unity/Application/ApplicationScreenshotProvider.cpp 1970-01-01 00:00:00 +0000 |
1667 | +++ tests/mocks/Unity/Application/ApplicationScreenshotProvider.cpp 2014-01-22 10:43:18 +0000 |
1668 | @@ -0,0 +1,69 @@ |
1669 | +/* |
1670 | + * Copyright (C) 2013 Canonical, Ltd. |
1671 | + * |
1672 | + * This program is free software: you can redistribute it and/or modify it under |
1673 | + * the terms of the GNU Lesser General Public License version 3, as published by |
1674 | + * the Free Software Foundation. |
1675 | + * |
1676 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
1677 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
1678 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
1679 | + * Lesser General Public License for more details. |
1680 | + * |
1681 | + * You should have received a copy of the GNU Lesser General Public License |
1682 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1683 | + */ |
1684 | + |
1685 | +#include "ApplicationScreenshotProvider.h" |
1686 | +#include "ApplicationManager.h" |
1687 | +#include "ApplicationInfo.h" |
1688 | + |
1689 | +#include "paths.h" |
1690 | + |
1691 | +#include <QDebug> |
1692 | +#include <QGuiApplication> |
1693 | +#include <QWindow> |
1694 | +#include <QQuickWindow> |
1695 | + |
1696 | +ApplicationScreenshotProvider::ApplicationScreenshotProvider(ApplicationManager *appManager) |
1697 | + : QQuickImageProvider(QQuickImageProvider::Image) |
1698 | + , m_appManager(appManager) |
1699 | +{ |
1700 | +} |
1701 | + |
1702 | +QImage ApplicationScreenshotProvider::requestImage(const QString &imageId, QSize * size, |
1703 | + const QSize &requestedSize) |
1704 | +{ |
1705 | + // We ignore requestedSize here intentionally to avoid keeping scaled copies around |
1706 | + Q_UNUSED(requestedSize) |
1707 | + |
1708 | + QString appId = imageId.split('/').first(); |
1709 | + |
1710 | + ApplicationInfo* app = static_cast<ApplicationInfo*>(m_appManager->findApplication(appId)); |
1711 | + if (app == NULL) { |
1712 | + qDebug() << "ApplicationScreenshotProvider - app not found:" << appId; |
1713 | + return QImage(); |
1714 | + } |
1715 | + |
1716 | + QString filePath = QString("%1/Dash/graphics/phone/screenshots/%2@12.png").arg(qmlDirectory()).arg(app->icon().toString()); |
1717 | + |
1718 | + QImage image; |
1719 | + if (!image.load(filePath)) { |
1720 | + qDebug() << "failed loading app image" << filePath; |
1721 | + } |
1722 | + |
1723 | + QGuiApplication *unity = qobject_cast<QGuiApplication*>(qApp); |
1724 | + |
1725 | + Q_FOREACH (QWindow *win, unity->allWindows()) { |
1726 | + QQuickWindow *quickWin = qobject_cast<QQuickWindow*>(win); |
1727 | + if (quickWin) { |
1728 | + image = image.scaledToWidth(quickWin->width()); |
1729 | + } |
1730 | + } |
1731 | + |
1732 | + size->setWidth(image.width()); |
1733 | + size->setHeight(image.height()); |
1734 | + qDebug() << "got image of size" << size->width() << size->height() << requestedSize; |
1735 | + |
1736 | + return image; |
1737 | +} |
1738 | |
1739 | === added file 'tests/mocks/Unity/Application/ApplicationScreenshotProvider.h' |
1740 | --- tests/mocks/Unity/Application/ApplicationScreenshotProvider.h 1970-01-01 00:00:00 +0000 |
1741 | +++ tests/mocks/Unity/Application/ApplicationScreenshotProvider.h 2014-01-22 10:43:18 +0000 |
1742 | @@ -0,0 +1,34 @@ |
1743 | +/* |
1744 | + * Copyright (C) 2013 Canonical, Ltd. |
1745 | + * |
1746 | + * This program is free software: you can redistribute it and/or modify it under |
1747 | + * the terms of the GNU Lesser General Public License version 3, as published by |
1748 | + * the Free Software Foundation. |
1749 | + * |
1750 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
1751 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
1752 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
1753 | + * Lesser General Public License for more details. |
1754 | + * |
1755 | + * You should have received a copy of the GNU Lesser General Public License |
1756 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1757 | + */ |
1758 | + |
1759 | +#ifndef APPLICATIONSCREENSHOTPROVIDER_H |
1760 | +#define APPLICATIONSCREENSHOTPROVIDER_H |
1761 | + |
1762 | +#include <QQuickImageProvider> |
1763 | + |
1764 | +class ApplicationManager; |
1765 | +class ApplicationScreenshotProvider : public QQuickImageProvider |
1766 | +{ |
1767 | +public: |
1768 | + explicit ApplicationScreenshotProvider(ApplicationManager *appManager); |
1769 | + |
1770 | + QImage requestImage(const QString &appId, QSize *size, const QSize &requestedSize) override; |
1771 | + |
1772 | +private: |
1773 | + ApplicationManager* m_appManager; |
1774 | +}; |
1775 | + |
1776 | +#endif // APPLICATIONSCREENSHOTPROVIDER_H |
1777 | |
1778 | === modified file 'tests/mocks/Unity/Application/CMakeLists.txt' |
1779 | --- tests/mocks/Unity/Application/CMakeLists.txt 2013-12-13 01:02:53 +0000 |
1780 | +++ tests/mocks/Unity/Application/CMakeLists.txt 2014-01-22 10:43:18 +0000 |
1781 | @@ -5,6 +5,7 @@ |
1782 | ApplicationInfo.cpp |
1783 | ApplicationImage.cpp |
1784 | ApplicationManager.cpp |
1785 | + ApplicationScreenshotProvider.cpp |
1786 | ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h |
1787 | ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h |
1788 | ) |
1789 | |
1790 | === modified file 'tests/mocks/Unity/Application/plugin.cpp' |
1791 | --- tests/mocks/Unity/Application/plugin.cpp 2013-09-11 15:33:02 +0000 |
1792 | +++ tests/mocks/Unity/Application/plugin.cpp 2014-01-22 10:43:18 +0000 |
1793 | @@ -18,13 +18,20 @@ |
1794 | #include "ApplicationInfo.h" |
1795 | #include "ApplicationImage.h" |
1796 | #include "ApplicationManager.h" |
1797 | +#include "ApplicationScreenshotProvider.h" |
1798 | |
1799 | #include <qqml.h> |
1800 | +#include <QQmlEngine> |
1801 | + |
1802 | +ApplicationManager *s_appManager = 0; |
1803 | |
1804 | static QObject* applicationManagerSingleton(QQmlEngine* engine, QJSEngine* scriptEngine) { |
1805 | - Q_UNUSED(engine); |
1806 | - Q_UNUSED(scriptEngine); |
1807 | - return new ApplicationManager(); |
1808 | + Q_UNUSED(engine); |
1809 | + Q_UNUSED(scriptEngine); |
1810 | + if (!s_appManager) { |
1811 | + s_appManager = new ApplicationManager(); |
1812 | + } |
1813 | + return s_appManager; |
1814 | } |
1815 | |
1816 | void FakeUnityApplicationQmlPlugin::registerTypes(const char *uri) |
1817 | @@ -37,3 +44,11 @@ |
1818 | |
1819 | qmlRegisterType<ApplicationImage>(uri, 0, 1, "ApplicationImage"); |
1820 | } |
1821 | + |
1822 | +void FakeUnityApplicationQmlPlugin::initializeEngine(QQmlEngine *engine, const char *uri) |
1823 | +{ |
1824 | + QQmlExtensionPlugin::initializeEngine(engine, uri); |
1825 | + |
1826 | + ApplicationManager* appManager = static_cast<ApplicationManager*>(applicationManagerSingleton(engine, NULL)); |
1827 | + engine->addImageProvider(QLatin1String("application"), new ApplicationScreenshotProvider(appManager)); |
1828 | +} |
1829 | |
1830 | === modified file 'tests/mocks/Unity/Application/plugin.h' |
1831 | --- tests/mocks/Unity/Application/plugin.h 2013-09-04 13:42:27 +0000 |
1832 | +++ tests/mocks/Unity/Application/plugin.h 2014-01-22 10:43:18 +0000 |
1833 | @@ -25,6 +25,7 @@ |
1834 | Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface") |
1835 | public: |
1836 | void registerTypes(const char *uri); |
1837 | + void initializeEngine(QQmlEngine *engine, const char *uri); |
1838 | }; |
1839 | |
1840 | #endif |
1841 | |
1842 | === modified file 'tests/plugins/Unity/Launcher/launchermodeltest.cpp' |
1843 | --- tests/plugins/Unity/Launcher/launchermodeltest.cpp 2013-09-16 17:59:13 +0000 |
1844 | +++ tests/plugins/Unity/Launcher/launchermodeltest.cpp 2014-01-22 10:43:18 +0000 |
1845 | @@ -39,6 +39,7 @@ |
1846 | ApplicationInfoInterface::Stage stage() const { return ApplicationInfoInterface::MainStage; } |
1847 | ApplicationInfoInterface::State state() const { return ApplicationInfoInterface::Running; } |
1848 | bool focused() const { return m_focused; } |
1849 | + QUrl screenshot() const { return QUrl(); } |
1850 | |
1851 | // Methods used for mocking (not in the interface) |
1852 | void setFocused(bool focused) { m_focused = focused; Q_EMIT focusedChanged(focused); } |
1853 | @@ -92,6 +93,8 @@ |
1854 | m_list.takeAt(index)->deleteLater(); |
1855 | endRemoveRows(); |
1856 | } |
1857 | + void updateScreenshot(const QString &appId) { Q_UNUSED(appId); } |
1858 | + void activateApplication(const QString &appId) { Q_UNUSED(appId); } |
1859 | |
1860 | private: |
1861 | QList<MockApp*> m_list; |
1862 | |
1863 | === modified file 'tests/qmltests/tst_Shell.qml' |
1864 | --- tests/qmltests/tst_Shell.qml 2014-01-16 12:37:57 +0000 |
1865 | +++ tests/qmltests/tst_Shell.qml 2014-01-22 10:43:18 +0000 |
1866 | @@ -117,6 +117,7 @@ |
1867 | while (apps.count > 0) { |
1868 | ApplicationManager.stopApplication(apps.get(0).appId); |
1869 | } |
1870 | + compare(ApplicationManager.count, 0) |
1871 | } |
1872 | |
1873 | /* |
1874 | @@ -182,28 +183,34 @@ |
1875 | function test_suspend() { |
1876 | var greeter = findChild(shell, "greeter"); |
1877 | |
1878 | + print("step1") |
1879 | // Launch an app from the launcher |
1880 | dragLauncherIntoView(); |
1881 | tapOnAppIconInLauncher(); |
1882 | waitUntilApplicationWindowIsFullyVisible(); |
1883 | |
1884 | + print("step2") |
1885 | var mainApp = ApplicationManager.focusedApplicationId; |
1886 | verify(mainApp != ""); |
1887 | |
1888 | + print("step3") |
1889 | // Try to suspend while proximity is engaged... |
1890 | Powerd.displayPowerStateChange(Powerd.Off, Powerd.UseProximity); |
1891 | tryCompare(greeter, "showProgress", 0); |
1892 | |
1893 | + print("step4") |
1894 | // Now really suspend |
1895 | Powerd.displayPowerStateChange(Powerd.Off, 0); |
1896 | tryCompare(greeter, "showProgress", 1); |
1897 | tryCompare(ApplicationManager, "focusedApplicationId", ""); |
1898 | |
1899 | + print("step5") |
1900 | // And wake up |
1901 | Powerd.displayPowerStateChange(Powerd.On, 0); |
1902 | tryCompare(ApplicationManager, "focusedApplicationId", ""); |
1903 | tryCompare(greeter, "showProgress", 1); |
1904 | |
1905 | + print("step6") |
1906 | // Swipe away greeter to focus app |
1907 | swipeAwayGreeter(); |
1908 | tryCompare(ApplicationManager, "focusedApplicationId", mainApp); |
1909 | @@ -503,6 +510,8 @@ |
1910 | |
1911 | function test_DashShown(data) { |
1912 | |
1913 | + print("step1") |
1914 | + // greeter: false, |
1915 | if (data.greeter) { |
1916 | // Swipe the greeter in |
1917 | var greeter = findChild(shell, "greeter"); |
1918 | @@ -510,21 +519,31 @@ |
1919 | tryCompare(greeter, "showProgress", 1); |
1920 | } |
1921 | |
1922 | + print("step2") |
1923 | + //app: false, |
1924 | if (data.app) { |
1925 | dragLauncherIntoView(); |
1926 | tapOnAppIconInLauncher(); |
1927 | } |
1928 | |
1929 | + print("step3") |
1930 | + // launcher: true, |
1931 | if (data.launcher) { |
1932 | dragLauncherIntoView(); |
1933 | } |
1934 | |
1935 | + print("step4") |
1936 | + // indicators: false, |
1937 | if (data.indicators) { |
1938 | showIndicators(); |
1939 | } |
1940 | |
1941 | + print("step5") |
1942 | + //expectedShown: true}, |
1943 | var dash = findChild(shell, "dash"); |
1944 | + print("step6", dash, dash.shown, data.expectedShown) |
1945 | tryCompare(dash, "shown", data.expectedShown); |
1946 | + print("step7") |
1947 | } |
1948 | |
1949 | function test_searchIndicatorHidesOnAppFocus() { |
superseeded by the right-edge-2 branch