Merge lp:~dandrader/unity8/multiMonitorScreenshot into lp:unity8

Proposed by Daniel d'Andrada on 2016-04-26
Status: Merged
Approved by: Lukáš Tinkl on 2016-04-29
Approved revision: 2343
Merged at revision: 2376
Proposed branch: lp:~dandrader/unity8/multiMonitorScreenshot
Merge into: lp:unity8
Diff against target: 693 lines (+105/-282)
19 files modified
debian/unity8-private.install (+1/-1)
plugins/CMakeLists.txt (+1/-1)
plugins/ScreenGrabber/ScreenGrabber.qmltypes (+0/-17)
plugins/ScreenshotDirectory/CMakeLists.txt (+5/-9)
plugins/ScreenshotDirectory/ScreenshotDirectory.cpp (+18/-69)
plugins/ScreenshotDirectory/ScreenshotDirectory.h (+10/-20)
plugins/ScreenshotDirectory/plugin.cpp (+5/-7)
plugins/ScreenshotDirectory/plugin.h (+5/-7)
plugins/ScreenshotDirectory/qmldir (+2/-3)
qml/Components/ItemGrabber.qml (+27/-17)
qml/Components/NotificationAudio.qml (+7/-3)
qml/Shell.qml (+11/-5)
qml/Stages/AbstractStage.qml (+1/-0)
qml/Stages/DesktopStage.qml (+6/-0)
src/ShellApplication.cpp (+6/-0)
tests/plugins/CMakeLists.txt (+0/-1)
tests/plugins/ScreenGrabber/CMakeLists.txt (+0/-14)
tests/plugins/ScreenGrabber/ScreenGrabberTest.cpp (+0/-77)
tests/plugins/ScreenGrabber/grabber.qml (+0/-31)
To merge this branch: bzr merge lp:~dandrader/unity8/multiMonitorScreenshot
Reviewer Review Type Date Requested Status
Lukáš Tinkl (community) 2016-04-26 Approve on 2016-04-29
Unity8 CI Bot continuous-integration Needs Fixing on 2016-04-27
Review via email: mp+292961@code.launchpad.net

Commit Message

Refactor screenshotting code and make it work in multimonitor scenarios

+ Workaround bug in QWindow regrading "visible" property when moving a window between screens.
+ Implement snapshotting the focused window with Alt+PrtScn

Description of the Change

* Are there any related MPs required for this MP to build/function as expected? Please list.
No

* Did you perform an exploratory manual test run of your code change and any related functionality?
Yes.
Tested on a laptop connected to an external monitor. Can't test on a phone connected to a monitor as my Slimport dongle seens to be busted.

* If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?
Not applicable.

* If you changed the UI, has there been a design review?
Not applicable.

To post a comment you must log in.
Unity8 CI Bot (unity8-ci-bot) wrote :

FAILED: Continuous integration, rev:2344
https://unity8-jenkins.ubuntu.com/job/lp-unity8-ci/1071/
Executed test runs:
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=vivid+overlay,testname=qmluitests.sh/619
    FAILURE: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=xenial,testname=qmluitests.sh/619/console
    FAILURE: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=phone-armhf,release=vivid+overlay,testname=autopilot.sh/619/console
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-0-fetch/1440
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/1406
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/1406
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=vivid+overlay/1406
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=vivid+overlay/1406/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/1406
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/1406/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=vivid+overlay/1406
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=vivid+overlay/1406/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/1406
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/1406/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=vivid+overlay/1406
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=vivid+overlay/1406/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/1406
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/1406/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://unity8-jenkins.ubuntu.com/job/lp-unity8-ci/1071/rebuild

review: Needs Fixing (continuous-integration)
Gerry Boland (gerboland) wrote :

+++ qml/Components/ItemGrabber.qml
+ root.parent.grabToImage
Breaking encapsulation?

Lukáš Tinkl (lukas-kde) wrote :

Since you removed the previous plugin's CPP test, could you add a new one that checks whether a screenshot is taken when you press

a) both Volume keys
b) the dedicated PrtScr key

Thanks, otherwise the code looks alright to me (minus the issue pointed out by Gerry above)

Daniel d'Andrada (dandrader) wrote :

On 27/04/2016 04:54, Gerry Boland wrote:
> +++ qml/Components/ItemGrabber.qml
> + root.parent.grabToImage
> Breaking encapsulation?

I didn't put a "property Item target" parameter because ItemGrabber not
only grabs an image and saves it, it also shows an effect (the white
flash) over its parent it even has an "anchors.fill: parent" so it's
meant to capture its parent.

I don't mind much adding a target parameter and have Shell do the
anchoring but just feels like a flexibility that doesn't make sense (no
use case).

Documented this component to make intent and design clear.

Lukáš Tinkl (lukas-kde) wrote :

Tested this with an external screen, works great. Still would like to see some test :)

review: Approve
Daniel d'Andrada (dandrader) wrote :

On 27/04/2016 05:30, Lukáš Tinkl wrote:
> Since you removed the previous plugin's CPP test, could you add a new one that checks whether a screenshot is taken when you press
>
> a) both Volume keys
> b) the dedicated PrtScr key
>

You're asking for tests different from the ones I removed.

This is what I removed:

- testGrabScreenshot(): tests that a screenshot is saved to disk.

The screenshot-taking part is 100% done by Qt. I don't see any sense in
testing that. What's left is checking if
ScreenshotDirectory::makeFileName() (which is the remaining C++ code
from the old ScreenGrabber) generates valid file names.
I see little value in such test as it's in the same abstraction level as
the code it's testing, which also just a couple of lines long.

- testRotatedScreenshot(): tests that a screenshot taken when Shell is
rotated by OrientedShell is still upright.

Doesn't make sense as there's no code for that anymore since screenshots
are now being taken of the Shell component (at Item level) instead of
the QQuickWindow (at window level).

The new tests you're asking for are merely testing signal-slot connections:

a)
"""
onScreenshotTriggered: itemGrabber.capture();
"""

b)
"""
onTriggered: capture()
"""

In my opinition they would make for pretty useless tests. Useful tests
check emerging behavior or code interactions that are not obvious by
just looking at the code. They save the developer for unwittingly
breaking code or behavior. They shouldn't be a one-to-one mapping of the
code being tested.

I don't see anything test-worthy in ItemGrabber or ScreenshotDirectory.
There's little code there and it's all pretty simple and isolated.

Unless you mean high-level functional tests that press the volume keys
and check if a file lands on the correct dir, which lies out of scope of
this change (it exercises *way* more than the code I'm changing). And,
besides, I have my issues with high-level functional tests, but that's a
whole different topic.

Lukáš Tinkl (lukas-kde) wrote :

It wouldn't be much work:

QStandardPaths::setTestModeEnabled(true);
QString filename = ScreenshotDirectory::makeFileName();
QFileSystemWatcher::addPath(filename);

then check output of:
QFileSystemWatcher::fileChanged(filename);

Unity8 CI Bot (unity8-ci-bot) wrote :

FAILED: Continuous integration, rev:2345
https://unity8-jenkins.ubuntu.com/job/lp-unity8-ci/1076/
Executed test runs:
    UNSTABLE: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=vivid+overlay,testname=qmluitests.sh/624
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=xenial+overlay,testname=qmluitests.sh/624
    FAILURE: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=phone-armhf,release=vivid+overlay,testname=autopilot.sh/624/console
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-0-fetch/1445
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/1411
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/1411
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=vivid+overlay/1411
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=vivid+overlay/1411/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/1411
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/1411/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=vivid+overlay/1411
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=vivid+overlay/1411/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/1411
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/1411/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=vivid+overlay/1411
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=vivid+overlay/1411/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/1411
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/1411/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://unity8-jenkins.ubuntu.com/job/lp-unity8-ci/1076/rebuild

review: Needs Fixing (continuous-integration)
Nick Dedekind (nick-dedekind) wrote :

> On 27/04/2016 04:54, Gerry Boland wrote:
> > +++ qml/Components/ItemGrabber.qml
> > + root.parent.grabToImage
> > Breaking encapsulation?
>
> I didn't put a "property Item target" parameter because ItemGrabber not
> only grabs an image and saves it, it also shows an effect (the white
> flash) over its parent it even has an "anchors.fill: parent" so it's
> meant to capture its parent.
>
> I don't mind much adding a target parameter and have Shell do the
> anchoring but just feels like a flexibility that doesn't make sense (no
> use case).
>
> Documented this component to make intent and design clear.

I believe there is a use case where we would screenshot the focused surface on desktop.

Nick Dedekind (nick-dedekind) wrote :

> > On 27/04/2016 04:54, Gerry Boland wrote:
> > > +++ qml/Components/ItemGrabber.qml
> > > + root.parent.grabToImage
> > > Breaking encapsulation?
> >
> > I didn't put a "property Item target" parameter because ItemGrabber not
> > only grabs an image and saves it, it also shows an effect (the white
> > flash) over its parent it even has an "anchors.fill: parent" so it's
> > meant to capture its parent.
> >
> > I don't mind much adding a target parameter and have Shell do the
> > anchoring but just feels like a flexibility that doesn't make sense (no
> > use case).
> >
> > Documented this component to make intent and design clear.
>
> I believe there is a use case where we would screenshot the focused surface on
> desktop.

Alt-PrtSc
I don't think we a ItemGrabber on every surface...

2342. By Daniel d'Andrada on 2016-04-28

Refactor screenshotting code and make it work in multimonitor scenarios

+ Implement snapshotting the focused window with Alt+PrtScn
+ Workaround bug in QWindow regarding visibility when moving between screens

Daniel d'Andrada (dandrader) wrote :

> > > On 27/04/2016 04:54, Gerry Boland wrote:
> > > > +++ qml/Components/ItemGrabber.qml
> > > > + root.parent.grabToImage
> > > > Breaking encapsulation?
> > >
> > > I didn't put a "property Item target" parameter because ItemGrabber not
> > > only grabs an image and saves it, it also shows an effect (the white
> > > flash) over its parent it even has an "anchors.fill: parent" so it's
> > > meant to capture its parent.
> > >
> > > I don't mind much adding a target parameter and have Shell do the
> > > anchoring but just feels like a flexibility that doesn't make sense (no
> > > use case).
> > >
> > > Documented this component to make intent and design clear.
> >
> > I believe there is a use case where we would screenshot the focused surface
> on
> > desktop.
>
> Alt-PrtSc
> I don't think we a ItemGrabber on every surface...

Right. Implemented Alt+PrtSc support. Thanks!

Daniel d'Andrada (dandrader) wrote :

> > > On 27/04/2016 04:54, Gerry Boland wrote:
> > > > +++ qml/Components/ItemGrabber.qml
> > > > + root.parent.grabToImage
> > > > Breaking encapsulation?
> > >
> > > I didn't put a "property Item target" parameter because ItemGrabber not
> > > only grabs an image and saves it, it also shows an effect (the white
> > > flash) over its parent it even has an "anchors.fill: parent" so it's
> > > meant to capture its parent.
> > >
> > > I don't mind much adding a target parameter and have Shell do the
> > > anchoring but just feels like a flexibility that doesn't make sense (no
> > > use case).
> > >
> > > Documented this component to make intent and design clear.
> >
> > I believe there is a use case where we would screenshot the focused surface
> on
> > desktop.
>
> Alt-PrtSc
> I don't think we a ItemGrabber on every surface...

Right. Implemented Alt+PrtSc support. Thanks!

2343. By Daniel d'Andrada on 2016-04-29

s/Ctrl/Alt

Lukáš Tinkl (lukas-kde) wrote :

All good, tested on mako (both internal and external screen), no issues spotted.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/unity8-private.install'
2--- debian/unity8-private.install 2016-04-04 13:38:56 +0000
3+++ debian/unity8-private.install 2016-04-29 12:42:40 +0000
4@@ -8,7 +8,7 @@
5 usr/lib/*/unity8/qml/IntegratedLightDM
6 usr/lib/*/unity8/qml/Lights
7 usr/lib/*/unity8/qml/Powerd
8-usr/lib/*/unity8/qml/ScreenGrabber
9+usr/lib/*/unity8/qml/ScreenshotDirectory
10 usr/lib/*/unity8/qml/SessionBroadcast
11 usr/lib/*/unity8/qml/Ubuntu
12 usr/lib/*/unity8/qml/Unity
13
14=== modified file 'plugins/CMakeLists.txt'
15--- plugins/CMakeLists.txt 2016-04-04 13:37:49 +0000
16+++ plugins/CMakeLists.txt 2016-04-29 12:42:40 +0000
17@@ -19,8 +19,8 @@
18 add_subdirectory(Lights)
19 add_subdirectory(Dash)
20 add_subdirectory(Powerd)
21+add_subdirectory(ScreenshotDirectory)
22 add_subdirectory(SessionBroadcast)
23-add_subdirectory(ScreenGrabber)
24 add_subdirectory(Ubuntu)
25 add_subdirectory(UInput)
26 add_subdirectory(Unity)
27
28=== removed file 'plugins/ScreenGrabber/ScreenGrabber.qmltypes'
29--- plugins/ScreenGrabber/ScreenGrabber.qmltypes 2015-02-13 09:01:16 +0000
30+++ plugins/ScreenGrabber/ScreenGrabber.qmltypes 1970-01-01 00:00:00 +0000
31@@ -1,17 +0,0 @@
32-import QtQuick.tooling 1.1
33-
34-// This file describes the plugin-supplied types contained in the library.
35-// It is used for QML tooling purposes only.
36-//
37-// This file was auto-generated by:
38-// 'qmlplugindump -notrelocatable ScreenGrabber 0.1 plugins'
39-
40-Module {
41- Component {
42- name: "ScreenGrabber"
43- prototype: "QObject"
44- exports: ["ScreenGrabber/ScreenGrabber 0.1"]
45- exportMetaObjectRevisions: [0]
46- Method { name: "captureAndSave" }
47- }
48-}
49
50=== renamed directory 'plugins/ScreenGrabber' => 'plugins/ScreenshotDirectory'
51=== modified file 'plugins/ScreenshotDirectory/CMakeLists.txt'
52--- plugins/ScreenGrabber/CMakeLists.txt 2014-09-27 02:29:36 +0000
53+++ plugins/ScreenshotDirectory/CMakeLists.txt 2016-04-29 12:42:40 +0000
54@@ -1,13 +1,9 @@
55-include_directories(
56- ${CMAKE_CURRENT_BINARY_DIR}
57-)
58-
59-set(SCREENGRABBERSOURCES
60- screengrabber.cpp
61+set(PLUGIN_SOURCES
62+ ScreenshotDirectory.cpp
63 plugin.cpp
64 )
65
66-add_library(ScreenGrabber-qml MODULE ${SCREENGRABBERSOURCES})
67-qt5_use_modules(ScreenGrabber-qml Qml Gui Quick Concurrent)
68+add_library(ScreenshotDirectory-qml MODULE ${PLUGIN_SOURCES})
69+qt5_use_modules(ScreenshotDirectory-qml Qml Gui Quick)
70
71-add_unity8_plugin(ScreenGrabber 0.1 ScreenGrabber TARGETS ScreenGrabber-qml)
72+add_unity8_plugin(ScreenshotDirectory 0.1 ScreenshotDirectory TARGETS ScreenshotDirectory-qml)
73
74=== renamed file 'plugins/ScreenGrabber/screengrabber.cpp' => 'plugins/ScreenshotDirectory/ScreenshotDirectory.cpp'
75--- plugins/ScreenGrabber/screengrabber.cpp 2015-11-30 13:27:20 +0000
76+++ plugins/ScreenshotDirectory/ScreenshotDirectory.cpp 2016-04-29 12:42:40 +0000
77@@ -1,5 +1,5 @@
78 /*
79- * Copyright (C) 2014-2015 Canonical, Ltd.
80+ * Copyright (C) 2014-2016 Canonical, Ltd.
81 *
82 * This program is free software; you can redistribute it and/or modify
83 * it under the terms of the GNU General Public License as published by
84@@ -14,36 +14,18 @@
85 * along with this program. If not, see <http://www.gnu.org/licenses/>.
86 */
87
88-#include "screengrabber.h"
89+#include "ScreenshotDirectory.h"
90
91 #include <QDir>
92 #include <QDateTime>
93 #include <QStandardPaths>
94 #include <QTemporaryDir>
95-#include <QtGui/QImage>
96-#include <QtGui/QGuiApplication>
97-#include <QtQuick/QQuickWindow>
98
99 #include <QDebug>
100
101-QString saveScreenshot(const QImage &screenshot, const QString &filename, const QString &format, int quality)
102-{
103- if (screenshot.save(filename, format.toLatin1().data(), quality)) {
104- return filename;
105- } else {
106- qWarning() << "ScreenGrabber: failed to save snapshot!";
107- }
108- return QString();
109-}
110-
111-ScreenGrabber::ScreenGrabber(QObject *parent)
112+ScreenshotDirectory::ScreenshotDirectory(QObject *parent)
113 : QObject(parent)
114 {
115- QObject::connect(&m_watcher,
116- &QFutureWatcher<QString>::finished,
117- this,
118- &ScreenGrabber::onScreenshotSaved);
119-
120 QDir screenshotsDir;
121 if (qEnvironmentVariableIsSet("UNITY_TESTING")) {
122 QTemporaryDir tDir;
123@@ -55,60 +37,27 @@
124 screenshotsDir.mkpath(QStringLiteral("Screenshots"));
125 screenshotsDir.cd(QStringLiteral("Screenshots"));
126 if (screenshotsDir.exists()) {
127- fileNamePrefix = screenshotsDir.absolutePath();
128- fileNamePrefix.append("/screenshot");
129+ m_fileNamePrefix = screenshotsDir.absolutePath();
130+ m_fileNamePrefix.append("/screenshot");
131 } else {
132- qWarning() << "ScreenGrabber: failed to create directory at: " << screenshotsDir.absolutePath();
133- }
134-}
135-
136-void ScreenGrabber::captureAndSave(int angle)
137-{
138- if (fileNamePrefix.isEmpty())
139- {
140- qWarning() << "ScreenShotter: no directory to save screenshot";
141- return;
142- }
143-
144- const QWindowList windows = QGuiApplication::topLevelWindows();
145- if (windows.empty())
146- {
147- qWarning() << "ScreenShotter: no top level windows found!";
148- return;
149- }
150-
151- QQuickWindow *main_window = qobject_cast<QQuickWindow *>(windows[0]);
152- if (!main_window)
153- {
154- qWarning() << "ScreenShotter: can only take screenshots of QQuickWindows";
155- return;
156- }
157-
158- const QImage screenshot = main_window->grabWindow().transformed(QTransform().rotate(angle));
159- const QString filename = makeFileName();
160- qDebug() << "Saving screenshot to" << filename;
161- QFuture<QString> saveFuture(QtConcurrent::run(saveScreenshot, screenshot, filename, getFormat(), screenshotQuality));
162- m_watcher.setFuture(saveFuture);
163-}
164-
165-void ScreenGrabber::onScreenshotSaved()
166-{
167- const QString filename = m_watcher.future().result();
168- if (!filename.isEmpty()) {
169- Q_EMIT screenshotSaved(filename);
170- }
171-}
172-
173-QString ScreenGrabber::makeFileName() const
174-{
175- QString fileName(fileNamePrefix);
176+ qWarning() << "ScreenshotDirectory: failed to create directory at:" << screenshotsDir.absolutePath();
177+ }
178+}
179+
180+QString ScreenshotDirectory::makeFileName() const
181+{
182+ if (m_fileNamePrefix.isEmpty()) {
183+ return QString();
184+ }
185+
186+ QString fileName(m_fileNamePrefix);
187 fileName.append(QDateTime::currentDateTime().toString(QStringLiteral("yyyyMMdd_hhmmsszzz")));
188 fileName.append(".");
189- fileName.append(getFormat());
190+ fileName.append(format());
191 return fileName;
192 }
193
194-QString ScreenGrabber::getFormat() const
195+QString ScreenshotDirectory::format() const
196 {
197 //TODO: This should be configurable (perhaps through gsettings?)
198 return QStringLiteral("png");
199
200=== renamed file 'plugins/ScreenGrabber/screengrabber.h' => 'plugins/ScreenshotDirectory/ScreenshotDirectory.h'
201--- plugins/ScreenGrabber/screengrabber.h 2015-11-18 18:38:36 +0000
202+++ plugins/ScreenshotDirectory/ScreenshotDirectory.h 2016-04-29 12:42:40 +0000
203@@ -1,5 +1,5 @@
204 /*
205- * Copyright (C) 2014-2015 Canonical, Ltd.
206+ * Copyright (C) 2014-2016 Canonical, Ltd.
207 *
208 * This program is free software; you can redistribute it and/or modify
209 * it under the terms of the GNU General Public License as published by
210@@ -14,36 +14,26 @@
211 * along with this program. If not, see <http://www.gnu.org/licenses/>.
212 */
213
214-#ifndef SNAPSHOTTER_H
215-#define SNAPSHOTTER_H
216+#ifndef SCREENSHOTDIRECTORY_H
217+#define SCREENSHOTDIRECTORY_H
218
219 #include <QObject>
220 #include <QString>
221-#include <QtConcurrent>
222
223-class ScreenGrabber: public QObject
224+class ScreenshotDirectory: public QObject
225 {
226 Q_OBJECT
227
228 public:
229- explicit ScreenGrabber(QObject *parent = 0);
230- ~ScreenGrabber() = default;
231+ explicit ScreenshotDirectory(QObject *parent = 0);
232+ ~ScreenshotDirectory() = default;
233
234 public Q_SLOTS:
235- void captureAndSave(int angle = 0);
236-
237-Q_SIGNALS:
238- void screenshotSaved(const QString &filename);
239-
240-private Q_SLOTS:
241- void onScreenshotSaved();
242+ QString makeFileName() const;
243
244 private:
245- QString makeFileName() const;
246- QString getFormat() const;
247- QString fileNamePrefix;
248- int screenshotQuality = -1; // default quality for the format
249- QFutureWatcher<QString> m_watcher;
250+ QString format() const;
251+ QString m_fileNamePrefix;
252 };
253
254-#endif
255+#endif // SCREENSHOTDIRECTORY_H
256
257=== modified file 'plugins/ScreenshotDirectory/plugin.cpp'
258--- plugins/ScreenGrabber/plugin.cpp 2014-09-27 02:16:11 +0000
259+++ plugins/ScreenshotDirectory/plugin.cpp 2016-04-29 12:42:40 +0000
260@@ -1,5 +1,5 @@
261 /*
262- * Copyright (C) 2014 Canonical, Ltd.
263+ * Copyright (C) 2014,2016 Canonical, Ltd.
264 *
265 * This program is free software; you can redistribute it and/or modify
266 * it under the terms of the GNU General Public License as published by
267@@ -12,17 +12,15 @@
268 *
269 * You should have received a copy of the GNU General Public License
270 * along with this program. If not, see <http://www.gnu.org/licenses/>.
271- *
272- * Authors: Alberto Aguirre <alberto.aguirre@canonical.com>
273 */
274
275 #include "plugin.h"
276-#include "screengrabber.h"
277+#include "ScreenshotDirectory.h"
278
279 #include <QtQml/qqml.h>
280
281-void ScreenGrabberPlugin::registerTypes(const char *uri)
282+void ScreenshotDirectoryPlugin::registerTypes(const char *uri)
283 {
284- Q_ASSERT(uri == QLatin1String("ScreenGrabber"));
285- qmlRegisterType<ScreenGrabber>(uri, 0, 1, "ScreenGrabber");
286+ Q_ASSERT(uri == QLatin1String("ScreenshotDirectory"));
287+ qmlRegisterType<ScreenshotDirectory>(uri, 0, 1, "ScreenshotDirectory");
288 }
289
290=== modified file 'plugins/ScreenshotDirectory/plugin.h'
291--- plugins/ScreenGrabber/plugin.h 2015-04-30 09:31:51 +0000
292+++ plugins/ScreenshotDirectory/plugin.h 2016-04-29 12:42:40 +0000
293@@ -1,5 +1,5 @@
294 /*
295- * Copyright (C) 2014 Canonical, Ltd.
296+ * Copyright (C) 2014,2016 Canonical, Ltd.
297 *
298 * This program is free software; you can redistribute it and/or modify
299 * it under the terms of the GNU General Public License as published by
300@@ -12,16 +12,14 @@
301 *
302 * You should have received a copy of the GNU General Public License
303 * along with this program. If not, see <http://www.gnu.org/licenses/>.
304- *
305- * Authors: Alberto Aguirre <alberto.aguirre@canonical.com>
306 */
307
308-#ifndef SCREENGRABBER_PLUGIN_H
309-#define SCREENGRABBER_PLUGIN_H
310+#ifndef SCREENSHOTDIRECTORY_PLUGIN_H
311+#define SCREENSHOTDIRECTORY_PLUGIN_H
312
313 #include <QtQml/QQmlExtensionPlugin>
314
315-class ScreenGrabberPlugin : public QQmlExtensionPlugin
316+class ScreenshotDirectoryPlugin : public QQmlExtensionPlugin
317 {
318 Q_OBJECT
319 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
320@@ -30,4 +28,4 @@
321 void registerTypes(const char *uri) override;
322 };
323
324-#endif // SCREENGRABBER_PLUGIN_H
325+#endif // SCREENSHOTDIRECTORY_PLUGIN_H
326
327=== modified file 'plugins/ScreenshotDirectory/qmldir'
328--- plugins/ScreenGrabber/qmldir 2014-09-27 02:16:11 +0000
329+++ plugins/ScreenshotDirectory/qmldir 2016-04-29 12:42:40 +0000
330@@ -1,3 +1,2 @@
331-module ScreenGrabber
332-plugin ScreenGrabber-qml
333-typeinfo ScreenGrabber.qmltypes
334+module ScreenshotDirectory
335+plugin ScreenshotDirectory-qml
336
337=== renamed file 'qml/Components/ScreenGrabber.qml' => 'qml/Components/ItemGrabber.qml'
338--- qml/Components/ScreenGrabber.qml 2015-10-26 15:54:13 +0000
339+++ qml/Components/ItemGrabber.qml 2016-04-29 12:42:40 +0000
340@@ -1,5 +1,5 @@
341 /*
342- * Copyright (C) 2014-2015 Canonical, Ltd.
343+ * Copyright (C) 2014-2016 Canonical, Ltd.
344 *
345 * This program is free software; you can redistribute it and/or modify
346 * it under the terms of the GNU General Public License as published by
347@@ -15,36 +15,31 @@
348 */
349
350 import QtQuick 2.4
351-import ScreenGrabber 0.1
352-import GlobalShortcut 1.0
353+import ScreenshotDirectory 0.1
354
355+/*
356+ Captures an image of the given item and saves it in a screenshots directory.
357+ It also displays a flash visual effect and camera shutter sound, as feedback
358+ to the user to hint that a screenshot was taken.
359+ */
360 Rectangle {
361 id: root
362 visible: false
363 color: "white"
364- anchors.fill: parent
365 opacity: 0.0
366
367- // to be set from outside
368- property int rotationAngle: 0
369-
370- ScreenGrabber {
371- id: screenGrabber
372+ ScreenshotDirectory {
373+ id: screenshotDirectory
374 objectName: "screenGrabber"
375 }
376
377- GlobalShortcut {
378- id: screenshotShortcut
379- shortcut: Qt.Key_Print
380- onTriggered: capture()
381- }
382-
383 NotificationAudio {
384 id: shutterSound
385 source: "/system/media/audio/ui/camera_click.ogg"
386 }
387
388- function capture() {
389+ function capture(item) {
390+ d.target = item;
391 visible = true;
392 shutterSound.stop();
393 shutterSound.play();
394@@ -62,13 +57,28 @@
395 }
396 }
397
398+ QtObject {
399+ id: d
400+ property Item target
401+ }
402+
403 NumberAnimation on opacity {
404 id: fadeOut
405 from: 1.0
406 to: 0.0
407 onStopped: {
408 if (visible) {
409- screenGrabber.captureAndSave(root.rotationAngle);
410+ d.target.grabToImage(function(result)
411+ {
412+ var fileName = screenshotDirectory.makeFileName();
413+ if (fileName.length === 0) {
414+ console.warn("ItemGrabber: No fileName to save image to");
415+ } else {
416+ console.log("ItemGrabber: Saving image to " + fileName);
417+ result.saveToFile(fileName);
418+ }
419+ });
420+
421 visible = false;
422 }
423 }
424
425=== modified file 'qml/Components/NotificationAudio.qml'
426--- qml/Components/NotificationAudio.qml 2015-10-29 14:19:34 +0000
427+++ qml/Components/NotificationAudio.qml 2016-04-29 12:42:40 +0000
428@@ -1,5 +1,5 @@
429 /*
430- * Copyright (C) 2015 Canonical, Ltd.
431+ * Copyright (C) 2015-2016 Canonical, Ltd.
432 *
433 * This program is free software; you can redistribute it and/or modify
434 * it under the terms of the GNU General Public License as published by
435@@ -22,10 +22,14 @@
436 readonly property var playbackState: priv.audio ? priv.audio.playbackState : 0
437
438 function play() {
439- priv.audio.play();
440+ if (priv.audio) {
441+ priv.audio.play();
442+ }
443 }
444 function stop() {
445- priv.audio.stop();
446+ if (priv.audio) {
447+ priv.audio.stop();
448+ }
449 }
450
451 QtObject {
452
453=== modified file 'qml/Shell.qml'
454--- qml/Shell.qml 2016-04-21 13:47:29 +0000
455+++ qml/Shell.qml 2016-04-29 12:42:40 +0000
456@@ -193,7 +193,7 @@
457 onPowerKeyLongPressed: dialogs.showPowerDialog();
458 onVolumeDownTriggered: volumeControl.volumeDown();
459 onVolumeUpTriggered: volumeControl.volumeUp();
460- onScreenshotTriggered: screenGrabber.capture();
461+ onScreenshotTriggered: itemGrabber.capture(shell);
462 }
463
464 GlobalShortcut {
465@@ -706,16 +706,22 @@
466 onShowHome: showHome()
467 }
468
469- ScreenGrabber {
470- id: screenGrabber
471- rotationAngle: -shell.orientationAngle
472+ ItemGrabber {
473+ id: itemGrabber
474+ anchors.fill: parent
475 z: dialogs.z + 10
476+ GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
477+ Connections {
478+ target: applicationsDisplayLoader.item
479+ ignoreUnknownSignals: true
480+ onItemSnapshotRequested: itemGrabber.capture(item)
481+ }
482 }
483
484 Cursor {
485 id: cursor
486 visible: shell.hasMouse
487- z: screenGrabber.z + 1
488+ z: itemGrabber.z + 1
489
490 onPushedLeftBoundary: {
491 if (buttons === Qt.NoButton) {
492
493=== modified file 'qml/Stages/AbstractStage.qml'
494--- qml/Stages/AbstractStage.qml 2016-04-04 13:37:49 +0000
495+++ qml/Stages/AbstractStage.qml 2016-04-29 12:42:40 +0000
496@@ -60,6 +60,7 @@
497 | Qt.InvertedLandscapeOrientation
498
499 signal stageAboutToBeUnloaded
500+ signal itemSnapshotRequested(Item item)
501
502 // Shared code for use in stage implementations
503 GSettings {
504
505=== modified file 'qml/Stages/DesktopStage.qml'
506--- qml/Stages/DesktopStage.qml 2016-04-13 14:13:36 +0000
507+++ qml/Stages/DesktopStage.qml 2016-04-29 12:42:40 +0000
508@@ -94,6 +94,12 @@
509 active: priv.focusedAppDelegate !== null
510 }
511
512+ GlobalShortcut {
513+ shortcut: Qt.AltModifier|Qt.Key_Print
514+ onTriggered: root.itemSnapshotRequested(priv.focusedAppDelegate)
515+ active: priv.focusedAppDelegate !== null
516+ }
517+
518 Connections {
519 target: root.topLevelSurfaceList
520 onCountChanged: {
521
522=== modified file 'src/ShellApplication.cpp'
523--- src/ShellApplication.cpp 2015-12-16 13:58:39 +0000
524+++ src/ShellApplication.cpp 2016-04-29 12:42:40 +0000
525@@ -168,6 +168,9 @@
526 // Changing the QScreen where a QWindow is drawn makes it also lose focus (besides having
527 // its backing QPlatformWindow recreated). So lets refocus it.
528 m_shellView->requestActivate();
529+ // QWindow::destroy() is called when it changes between screens. We have to manually make it visible again
530+ // <dandrader> This bug is supposedly fixed in Qt 5.5.1, although I can still reproduce it there. :-/
531+ m_shellView->setVisible(true);
532
533 m_secondaryWindow = new SecondaryWindow(m_qmlEngine);
534 m_secondaryWindow->setScreen(screens().at(0));
535@@ -194,5 +197,8 @@
536 // Changing the QScreen where a QWindow is drawn makes it also lose focus (besides having
537 // its backing QPlatformWindow recreated). So lets refocus it.
538 m_shellView->requestActivate();
539+ // QWindow::destroy() is called when it changes between screens. We have to manually make it visible again
540+ // <dandrader> This bug is supposedly fixed in Qt 5.5.1, although I can still reproduce it there. :-/
541+ m_shellView->setVisible(true);
542 }
543 }
544
545=== modified file 'tests/plugins/CMakeLists.txt'
546--- tests/plugins/CMakeLists.txt 2015-09-02 09:30:32 +0000
547+++ tests/plugins/CMakeLists.txt 2016-04-29 12:42:40 +0000
548@@ -3,7 +3,6 @@
549 add_subdirectory(Greeter)
550 add_subdirectory(IntegratedLightDM)
551 add_subdirectory(Dash)
552-add_subdirectory(ScreenGrabber)
553 add_subdirectory(Ubuntu)
554 add_subdirectory(Unity)
555 add_subdirectory(Utils)
556
557=== removed directory 'tests/plugins/ScreenGrabber'
558=== removed file 'tests/plugins/ScreenGrabber/CMakeLists.txt'
559--- tests/plugins/ScreenGrabber/CMakeLists.txt 2015-11-24 12:25:30 +0000
560+++ tests/plugins/ScreenGrabber/CMakeLists.txt 1970-01-01 00:00:00 +0000
561@@ -1,14 +0,0 @@
562-include_directories(
563- ${CMAKE_SOURCE_DIR}/plugins/ScreenGrabber
564- ${CMAKE_CURRENT_BINARY_DIR}
565- )
566-
567-add_definitions(
568- -DCURRENT_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}"
569- )
570-add_definitions(-DBUILT_PLUGINS_DIR="${CMAKE_BINARY_DIR}/plugins")
571-
572-add_executable(ScreenGrabberTestExec ScreenGrabberTest.cpp ${CMAKE_SOURCE_DIR}/plugins/ScreenGrabber/screengrabber.cpp)
573-qt5_use_modules(ScreenGrabberTestExec Test Quick Concurrent)
574-target_link_libraries(ScreenGrabberTestExec ${Qt5Quick_LIBRARIES} ScreenGrabber-qml)
575-add_unity8_uitest(ScreenGrabber ScreenGrabberTestExec DEPENDS ScreenGrabber-qml)
576
577=== removed file 'tests/plugins/ScreenGrabber/ScreenGrabberTest.cpp'
578--- tests/plugins/ScreenGrabber/ScreenGrabberTest.cpp 2015-11-18 18:38:36 +0000
579+++ tests/plugins/ScreenGrabber/ScreenGrabberTest.cpp 1970-01-01 00:00:00 +0000
580@@ -1,77 +0,0 @@
581-/*
582- * Copyright (C) 2015 Canonical, Ltd.
583- *
584- * This program is free software; you can redistribute it and/or modify
585- * it under the terms of the GNU General Public License as published by
586- * the Free Software Foundation; version 3.
587- *
588- * This program is distributed in the hope that it will be useful,
589- * but WITHOUT ANY WARRANTY; without even the implied warranty of
590- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
591- * GNU General Public License for more details.
592- *
593- * You should have received a copy of the GNU General Public License
594- * along with this program. If not, see <http://www.gnu.org/licenses/>.
595- */
596-
597-#include "screengrabber.h"
598-
599-#include <QtTest>
600-#include <QtQuick/QQuickView>
601-#include <QtQuick/QQuickItem>
602-#include <QQmlEngine>
603-
604-class ScreenGrabberTest: public QObject
605-{
606- Q_OBJECT
607-
608-private Q_SLOTS:
609-
610- void initTestCase()
611- {
612- m_view = new QQuickView();
613- m_view->engine()->addImportPath(BUILT_PLUGINS_DIR);
614- m_view->setSource(QUrl::fromLocalFile(CURRENT_SOURCE_DIR "/grabber.qml"));
615- m_grabber = dynamic_cast<ScreenGrabber*>(m_view->rootObject()->property("grabber").value<QObject*>());
616- QVERIFY(m_grabber);
617- m_view->show();
618- QTest::qWaitForWindowExposed(m_view);
619- }
620-
621- void cleanupTestCase()
622- {
623- delete m_view;
624- m_view = nullptr;
625- QTRY_COMPARE(QThreadPool::globalInstance()->activeThreadCount(), 0);
626- }
627-
628- void testGrabScreenshot()
629- {
630- QSignalSpy grabberSpy(m_grabber, &ScreenGrabber::screenshotSaved);
631- m_grabber->captureAndSave();
632- QTRY_VERIFY(grabberSpy.count() == 1);
633- const QVariantList args = grabberSpy.takeFirst();
634- QVERIFY(args.count() == 1); // verify we got a non-empty filename where the screenshot has been saved
635- QVERIFY(!args.first().toString().isEmpty());
636- }
637-
638- void testRotatedScreenshot()
639- {
640- QSignalSpy grabberSpy(m_grabber, &ScreenGrabber::screenshotSaved);
641- m_grabber->captureAndSave(90); // rotate by 90°
642- QTRY_VERIFY(grabberSpy.count() == 1);
643- const QVariantList args = grabberSpy.takeFirst();
644- const QString filename = args.first().toString();
645- QVERIFY(!filename.isEmpty());
646- QImage img(filename);
647- QVERIFY(img.height() > img.width()); // verify that the image got rotated by 90° (height > width)
648- }
649-
650-private:
651- QQuickView *m_view;
652- ScreenGrabber *m_grabber = nullptr;
653-};
654-
655-QTEST_MAIN(ScreenGrabberTest)
656-
657-#include "ScreenGrabberTest.moc"
658
659=== removed file 'tests/plugins/ScreenGrabber/grabber.qml'
660--- tests/plugins/ScreenGrabber/grabber.qml 2015-10-12 12:55:34 +0000
661+++ tests/plugins/ScreenGrabber/grabber.qml 1970-01-01 00:00:00 +0000
662@@ -1,31 +0,0 @@
663-/*
664- * Copyright 2015 Canonical Ltd.
665- *
666- * This program is free software; you can redistribute it and/or modify
667- * it under the terms of the GNU General Public License as published by
668- * the Free Software Foundation; version 3.
669- *
670- * This program is distributed in the hope that it will be useful,
671- * but WITHOUT ANY WARRANTY; without even the implied warranty of
672- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
673- * GNU General Public License for more details.
674- *
675- * You should have received a copy of the GNU General Public License
676- * along with this program. If not, see <http://www.gnu.org/licenses/>.
677- */
678-
679-import QtQuick 2.4
680-import ScreenGrabber 0.1
681-
682-Rectangle {
683- property var grabber: screenGrabber
684-
685- width: 101 // width intentionally bigger than height to test rotation
686- height: 100
687- color: "green"
688-
689- ScreenGrabber {
690- id: screenGrabber
691- objectName: "screenGrabber"
692- }
693-}

Subscribers

People subscribed via source and target branches