on xenial help sign in
Make shell rotation animation work on multimonitor scenarios
fa119b3Daniel d'Andrada
plugins/Utils/CMakeLists.txt−1
@@ -18,7 +18,6 @@ set(QMLPLUGIN_SRC
18 Timer.cpp
18 Timer.cpp
19 unitymenumodelpaths.cpp
19 unitymenumodelpaths.cpp
20 windowinputfilter.cpp
20 windowinputfilter.cpp
21− windowscreenshotprovider.cpp
22 easingcurve.cpp
21 easingcurve.cpp
23 windowstatestorage.cpp
22 windowstatestorage.cpp
24 timezoneFormatter.cpp
23 timezoneFormatter.cpp
plugins/Utils/plugin.cpp−8
@@ -31,7 +31,6 @@
31#include "unitysortfilterproxymodelqml.h"
31#include "unitysortfilterproxymodelqml.h"
32#include "unitymenumodelpaths.h"
32#include "unitymenumodelpaths.h"
33#include "windowinputfilter.h"
33#include "windowinputfilter.h"
34−#include "windowscreenshotprovider.h"
35#include "windowstatestorage.h"
34#include "windowstatestorage.h"
36#include "constants.h"
35#include "constants.h"
37#include "timezoneFormatter.h"
36#include "timezoneFormatter.h"
@@ -82,10 +81,3 @@ void UtilsPlugin::registerTypes(const char *uri)
82 qmlRegisterType<DeviceConfigParser>(uri, 0, 1, "DeviceConfigParser");
81 qmlRegisterType<DeviceConfigParser>(uri, 0, 1, "DeviceConfigParser");
83 qmlRegisterSingletonType<GlobalFunctions>(uri, 0, 1, "Functions", createGlobalFunctions);
82 qmlRegisterSingletonType<GlobalFunctions>(uri, 0, 1, "Functions", createGlobalFunctions);
84}
83}
85−
86−void UtilsPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
87−{
88− QQmlExtensionPlugin::initializeEngine(engine, uri);
89−
90− engine->addImageProvider(QStringLiteral("window"), new WindowScreenshotProvider);
91−}
plugins/Utils/plugin.h−1
@@ -29,7 +29,6 @@ class UtilsPlugin : public QQmlExtensionPlugin
29
29
30public:
30public:
31 void registerTypes(const char *uri) override;
31 void registerTypes(const char *uri) override;
32− void initializeEngine(QQmlEngine *engine, const char *uri) override;
33};
32};
34
33
35#endif
34#endif
plugins/Utils/windowscreenshotprovider.cppdeleted−59
@@ -1,59 +0,0 @@
1−/*
2− * Copyright (C) 2015 Canonical, Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−#include "windowscreenshotprovider.h"
18−
19−#include <QGuiApplication>
20−#include <QQuickWindow>
21−
22−WindowScreenshotProvider::WindowScreenshotProvider()
23− : QQuickImageProvider(QQmlImageProviderBase::Image, 0)
24−{
25−}
26−
27−// A very simple implementation where we assume that there's only one window and that it's a
28−// QQuickWindow. Thus the id parameter is irrelevant.
29−//
30−// Idea: Make the id contain the objectName of the QQuickWindow once we care about a multi-display
31−// compositor?
32−// Strictly speaking that could be the actual QWindow::winId(), but that's mostly a
33−// meaningless arbitrary number.
34−QImage WindowScreenshotProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
35−{
36− Q_UNUSED(id);
37− Q_UNUSED(requestedSize);
38−
39− QWindowList windows = QGuiApplication::topLevelWindows();
40−
41− if (windows.count() != 1) {
42− size->rwidth() = 0;
43− size->rheight() = 0;
44− return QImage();
45− }
46−
47− QQuickWindow *quickWindow = qobject_cast<QQuickWindow *>(windows[0]);
48−
49− if (!quickWindow) {
50− size->rwidth() = 0;
51− size->rheight() = 0;
52− return QImage();
53− }
54−
55− QImage image = quickWindow->grabWindow();
56− size->rwidth() = image.width();
57− size->rheight() = image.height();
58− return image;
59−}
plugins/Utils/windowscreenshotprovider.hdeleted−31
@@ -1,31 +0,0 @@
1−/*
2− * Copyright (C) 2015 Canonical, Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−#ifndef WINDOW_SCREENSHOT_PROVIDER_H_
18−#define WINDOW_SCREENSHOT_PROVIDER_H_
19−
20−#include <QQuickImageProvider>
21−
22−class WindowScreenshotProvider : public QQuickImageProvider
23−{
24−public:
25− WindowScreenshotProvider();
26−
27− // id is ignored for now
28− QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize) override;
29−};
30−
31−#endif // WINDOW_SCREENSHOT_PROVIDER_H_
qml/Components/WindowScreenshot.qml → qml/Components/ItemSnapshot.qmlrenamed+13−3
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2015 Canonical, Ltd.
2+ * Copyright (C) 2015-2016 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -19,9 +19,19 @@ import QtQuick 2.4
19Item {
19Item {
20 id: root
20 id: root
21
21
22+ property Item target
23+
24+ property bool ready: image.status === Image.Ready
25+
22 function take() {
26 function take() {
23− var timeNow = new Date().getTime();
27+ if (!target) {
28+ console.warn("ItemSnapshot.take(): no target set");
29+ return;
30+ }
31+
32+ target.grabToImage(function(result) {
24− image.source = "image://window/" + timeNow;
33+ image.source = result.url;
34+ });
25 }
35 }
26
36
27 // Unload the image to free up memory
37 // Unload the image to free up memory
qml/OrientedShell.qml+6−5
@@ -241,7 +241,7 @@ Rectangle {
241 orientedShell: root
241 orientedShell: root
242 shell: shell
242 shell: shell
243 shellCover: shellCover
243 shellCover: shellCover
244− windowScreenshot: windowScreenshot
244+ shellSnapshot: shellSnapshot
245 }
245 }
246
246
247 Shell {
247 Shell {
@@ -291,8 +291,9 @@ Rectangle {
291 visible: false
291 visible: false
292 }
292 }
293
293
294− WindowScreenshot {
294+ ItemSnapshot {
295− id: windowScreenshot
295+ id: shellSnapshot
296+ target: shell
296 visible: false
297 visible: false
297 width: root.width
298 width: root.width
298 height: root.height
299 height: root.height
@@ -302,9 +303,9 @@ Rectangle {
302 property real transformOriginY
303 property real transformOriginY
303
304
304 transform: Rotation {
305 transform: Rotation {
305− origin.x: windowScreenshot.transformOriginX; origin.y: windowScreenshot.transformOriginY;
306+ origin.x: shellSnapshot.transformOriginX; origin.y: shellSnapshot.transformOriginY;
306 axis { x: 0; y: 0; z: 1 }
307 axis { x: 0; y: 0; z: 1 }
307− angle: windowScreenshot.transformRotationAngle
308+ angle: shellSnapshot.transformRotationAngle
308 }
309 }
309 }
310 }
310}
311}
qml/Rotation/NinetyRotationAnimation.qml+13−16
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2015 Canonical, Ltd.
2+ * Copyright (C) 2015-2016 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -30,8 +30,7 @@ SequentialAnimation {
30
30
31 ScriptAction { script: {
31 ScriptAction { script: {
32 info.transitioning = true;
32 info.transitioning = true;
33− windowScreenshot.take();
33+
34− windowScreenshot.visible = true;
35 shell.orientationAngle = root.toAngle;
34 shell.orientationAngle = root.toAngle;
36 shell.x = 0;
35 shell.x = 0;
37 shell.width = flipShellDimensions ? orientedShell.height : orientedShell.width;
36 shell.width = flipShellDimensions ? orientedShell.height : orientedShell.width;
@@ -41,12 +40,10 @@ SequentialAnimation {
41 shell.updateFocusedAppOrientation();
40 shell.updateFocusedAppOrientation();
42 shellCover.visible = true;
41 shellCover.visible = true;
43
42
44− windowScreenshot.transformOriginX = orientedShell.width / 2;
43+ shellSnapshot.transformOriginX = shell.transformOriginX;
45− if (fromAngle == 180 || fromAngle == 270) {
44+ shellSnapshot.transformOriginY = shell.transformOriginY;
46− windowScreenshot.transformOriginY = orientedShell.height - (orientedShell.width / 2);
45+ shellSnapshot.transformRotationAngle = shell.transformRotationAngle;
47− } else {
46+ shellSnapshot.visible = true;
48− windowScreenshot.transformOriginY = orientedShell.width / 2;
49− }
50 } }
47 } }
51 ParallelAnimation {
48 ParallelAnimation {
52 NumberAnimation {
49 NumberAnimation {
@@ -66,24 +63,24 @@ SequentialAnimation {
66 }
63 }
67
64
68 NumberAnimation {
65 NumberAnimation {
69− target: windowScreenshot; property: "opacity"; from: 1; to: 0;
66+ target: shellSnapshot; property: "opacity"; from: 1; to: 0;
70 duration: rotationDuration; easing.type: rotationEasing
67 duration: rotationDuration; easing.type: rotationEasing
71 }
68 }
72 RotationAnimation {
69 RotationAnimation {
73− target: windowScreenshot; property: "transformRotationAngle";
70+ target: shellSnapshot; property: "transformRotationAngle";
74− from: 0; to: root.toAngle - root.fromAngle
71+ from: root.fromAngle; to: root.toAngle
75 direction: RotationAnimation.Shortest
72 direction: RotationAnimation.Shortest
76 duration: rotationDuration; easing.type: rotationEasing
73 duration: rotationDuration; easing.type: rotationEasing
77 }
74 }
78 NumberAnimation {
75 NumberAnimation {
79− target: windowScreenshot; property: "y"
76+ target: shellSnapshot; property: "y"
80− from: 0; to: root.toY - root.fromY
77+ from: root.fromY; to: root.toY
81 duration: rotationDuration; easing.type: rotationEasing
78 duration: rotationDuration; easing.type: rotationEasing
82 }
79 }
83 }
80 }
84 ScriptAction { script: {
81 ScriptAction { script: {
85− windowScreenshot.visible = false;
82+ shellSnapshot.visible = false;
86− windowScreenshot.discard();
83+ shellSnapshot.discard();
87 shellCover.visible = false;
84 shellCover.visible = false;
88 info.transitioning = false;
85 info.transitioning = false;
89 } }
86 } }
qml/Rotation/RotationStates.qml+25−5
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2015 Canonical, Ltd.
2+ * Copyright (C) 2015,2016 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -30,7 +30,7 @@ StateGroup {
30 property Item orientedShell
30 property Item orientedShell
31 property Item shell
31 property Item shell
32 property Item shellCover
32 property Item shellCover
33− property Item windowScreenshot
33+ property Item shellSnapshot
34
34
35 property int rotationDuration: 450
35 property int rotationDuration: 450
36 property int rotationEasing: Easing.InOutCubic
36 property int rotationEasing: Easing.InOutCubic
@@ -88,12 +88,32 @@ StateGroup {
88 }
88 }
89
89
90 var requestedState = d.requestedOrientationAngle.toString();
90 var requestedState = d.requestedOrientationAngle.toString();
91− if (requestedState !== root.state) {
91+ if (requestedState === root.state) {
92+ return;
93+ }
94+
92− d.resolveAnimationType();
95+ d.resolveAnimationType();
93− root.state = requestedState;
96+
97+ var angleDiff = Math.abs(root.shell.orientationAngle - d.requestedOrientationAngle);
98+ var isNinetyRotationAnimation = angleDiff == 90 || angleDiff == 270;
99+ var needsShellSnapshot = d.animationType == d.fullAnimation && isNinetyRotationAnimation;
100+
101+ if (needsShellSnapshot && !shellSnapshotReady) {
102+ root.shellSnapshot.take();
103+ // try again once we have a shell snapshot ready for use. Snapshot taking is async.
104+ return;
105+ }
106+
107+ if (!needsShellSnapshot && shellSnapshotReady) {
108+ root.shellSnapshot.discard();
94 }
109 }
110+
111+ root.state = requestedState;
95 }
112 }
96
113
114+ property bool shellSnapshotReady: root.shellSnapshot && root.shellSnapshot.ready
115+ onShellSnapshotReadyChanged: tryUpdateState();
116+
97 property Connections shellConnections: Connections {
117 property Connections shellConnections: Connections {
98 target: root.orientedShell
118 target: root.orientedShell
99 onOrientationChangesEnabledChanged: {
119 onOrientationChangesEnabledChanged: {
tests/mocks/Utils/CMakeLists.txt−1
@@ -13,7 +13,6 @@ set(QMLPLUGIN_SRC
13 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp
13 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp
14 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitymenumodelpaths.cpp
14 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitymenumodelpaths.cpp
15 ${CMAKE_SOURCE_DIR}/plugins/Utils/windowinputfilter.cpp
15 ${CMAKE_SOURCE_DIR}/plugins/Utils/windowinputfilter.cpp
16− ${CMAKE_SOURCE_DIR}/plugins/Utils/windowscreenshotprovider.cpp
17 ${CMAKE_SOURCE_DIR}/plugins/Utils/easingcurve.cpp
16 ${CMAKE_SOURCE_DIR}/plugins/Utils/easingcurve.cpp
18 ${CMAKE_SOURCE_DIR}/plugins/Utils/inputwatcher.cpp
17 ${CMAKE_SOURCE_DIR}/plugins/Utils/inputwatcher.cpp
19 ${CMAKE_SOURCE_DIR}/plugins/Utils/timezoneFormatter.cpp
18 ${CMAKE_SOURCE_DIR}/plugins/Utils/timezoneFormatter.cpp
tests/mocks/Utils/plugin.cpp−8
@@ -33,7 +33,6 @@
33#include <unitysortfilterproxymodelqml.h>
33#include <unitysortfilterproxymodelqml.h>
34#include <unitymenumodelpaths.h>
34#include <unitymenumodelpaths.h>
35#include <windowinputfilter.h>
35#include <windowinputfilter.h>
36−#include <windowscreenshotprovider.h>
37#include <easingcurve.h>
36#include <easingcurve.h>
38#include <timezoneFormatter.h>
37#include <timezoneFormatter.h>
39#include <applicationsfiltermodel.h>
38#include <applicationsfiltermodel.h>
@@ -82,10 +81,3 @@ void FakeUtilsPlugin::registerTypes(const char *uri)
82 qmlRegisterType<DeviceConfigParser>(uri, 0, 1, "DeviceConfigParser");
81 qmlRegisterType<DeviceConfigParser>(uri, 0, 1, "DeviceConfigParser");
83 qmlRegisterSingletonType<GlobalFunctions>(uri, 0, 1, "Functions", createGlobalFunctions);
82 qmlRegisterSingletonType<GlobalFunctions>(uri, 0, 1, "Functions", createGlobalFunctions);
84}
83}
85−
86−void FakeUtilsPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
87−{
88− QQmlExtensionPlugin::initializeEngine(engine, uri);
89−
90− engine->addImageProvider(QLatin1String("window"), new WindowScreenshotProvider);
91−}
tests/mocks/Utils/plugin.h−1
@@ -27,7 +27,6 @@ class FakeUtilsPlugin : public QQmlExtensionPlugin
27
27
28public:
28public:
29 void registerTypes(const char *uri);
29 void registerTypes(const char *uri);
30− void initializeEngine(QQmlEngine *engine, const char *uri);
31};
30};
32
31
33#endif
32#endif