on xenial help sign in
Merge from trunk
f5ee184Michael Terry
debian/changelog+23
@@ -1,3 +1,26 @@
1+unity8 (8.12+16.04.20160429.2-0ubuntu1) xenial; urgency=medium
2+
3+ [ CI Train Bot ]
4+ * Resync trunk.
5+
6+ [ Daniel d'Andrada ]
7+ * Ensure shell transformations use bindings as OrientedShell can get
8+ resized
9+ * Make shell rotation animation work on multimonitor scenarios.
10+ * Refactor screenshotting code and make it work in multimonitor
11+ scenarios
12+
13+ [ Gerry Boland, Nick Dedekind ]
14+ * Indicators: set menu height binding while fully open/closed, fixes
15+ dynamic grid unit bug.
16+
17+ [ Michael Zanetti ]
18+ * Call the dash sizing hack when units.gu changes
19+ * Temporary means to override GUs on the internal screen
20+ * reduce some paint layers.
21+
22+ -- Michał Sawicz <michal.sawicz@canonical.com> Fri, 29 Apr 2016 20:07:37 +0000
23+
1unity8 (8.12+16.04.20160427.3-0ubuntu1) xenial; urgency=medium
24unity8 (8.12+16.04.20160427.3-0ubuntu1) xenial; urgency=medium
2
25
3 [ Albert Astals Cid ]
26 [ Albert Astals Cid ]
debian/unity8-private.install+1−1
@@ -8,7 +8,7 @@ usr/lib/*/unity8/qml/Greeter
8usr/lib/*/unity8/qml/IntegratedLightDM
8usr/lib/*/unity8/qml/IntegratedLightDM
9usr/lib/*/unity8/qml/Lights
9usr/lib/*/unity8/qml/Lights
10usr/lib/*/unity8/qml/Powerd
10usr/lib/*/unity8/qml/Powerd
11−usr/lib/*/unity8/qml/ScreenGrabber
11+usr/lib/*/unity8/qml/ScreenshotDirectory
12usr/lib/*/unity8/qml/SessionBroadcast
12usr/lib/*/unity8/qml/SessionBroadcast
13usr/lib/*/unity8/qml/Ubuntu
13usr/lib/*/unity8/qml/Ubuntu
14usr/lib/*/unity8/qml/Unity
14usr/lib/*/unity8/qml/Unity
plugins/CMakeLists.txt+1−1
@@ -19,8 +19,8 @@ add_subdirectory(IntegratedLightDM)
19add_subdirectory(Lights)
19add_subdirectory(Lights)
20add_subdirectory(Dash)
20add_subdirectory(Dash)
21add_subdirectory(Powerd)
21add_subdirectory(Powerd)
22+add_subdirectory(ScreenshotDirectory)
22add_subdirectory(SessionBroadcast)
23add_subdirectory(SessionBroadcast)
23−add_subdirectory(ScreenGrabber)
24add_subdirectory(Ubuntu)
24add_subdirectory(Ubuntu)
25add_subdirectory(UInput)
25add_subdirectory(UInput)
26add_subdirectory(Unity)
26add_subdirectory(Unity)
plugins/ScreenGrabber/CMakeLists.txtdeleted−13
@@ -1,13 +0,0 @@
1−include_directories(
2− ${CMAKE_CURRENT_BINARY_DIR}
3−)
4−
5−set(SCREENGRABBERSOURCES
6− screengrabber.cpp
7− plugin.cpp
8−)
9−
10−add_library(ScreenGrabber-qml MODULE ${SCREENGRABBERSOURCES})
11−qt5_use_modules(ScreenGrabber-qml Qml Gui Quick Concurrent)
12−
13−add_unity8_plugin(ScreenGrabber 0.1 ScreenGrabber TARGETS ScreenGrabber-qml)
plugins/ScreenGrabber/qmldirdeleted−3
@@ -1,3 +0,0 @@
1−module ScreenGrabber
2−plugin ScreenGrabber-qml
3−typeinfo ScreenGrabber.qmltypes
plugins/ScreenGrabber/screengrabber.cppdeleted−115
@@ -1,115 +0,0 @@
1−/*
2− * Copyright (C) 2014-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 "screengrabber.h"
18−
19−#include <QDir>
20−#include <QDateTime>
21−#include <QStandardPaths>
22−#include <QTemporaryDir>
23−#include <QtGui/QImage>
24−#include <QtGui/QGuiApplication>
25−#include <QtQuick/QQuickWindow>
26−
27−#include <QDebug>
28−
29−QString saveScreenshot(const QImage &screenshot, const QString &filename, const QString &format, int quality)
30−{
31− if (screenshot.save(filename, format.toLatin1().data(), quality)) {
32− return filename;
33− } else {
34− qWarning() << "ScreenGrabber: failed to save snapshot!";
35− }
36− return QString();
37−}
38−
39−ScreenGrabber::ScreenGrabber(QObject *parent)
40− : QObject(parent)
41−{
42− QObject::connect(&m_watcher,
43− &QFutureWatcher<QString>::finished,
44− this,
45− &ScreenGrabber::onScreenshotSaved);
46−
47− QDir screenshotsDir;
48− if (qEnvironmentVariableIsSet("UNITY_TESTING")) {
49− QTemporaryDir tDir;
50− tDir.setAutoRemove(false);
51− screenshotsDir = tDir.path();
52− } else {
53− screenshotsDir = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
54− }
55− screenshotsDir.mkpath(QStringLiteral("Screenshots"));
56− screenshotsDir.cd(QStringLiteral("Screenshots"));
57− if (screenshotsDir.exists()) {
58− fileNamePrefix = screenshotsDir.absolutePath();
59− fileNamePrefix.append("/screenshot");
60− } else {
61− qWarning() << "ScreenGrabber: failed to create directory at: " << screenshotsDir.absolutePath();
62− }
63−}
64−
65−void ScreenGrabber::captureAndSave(int angle)
66−{
67− if (fileNamePrefix.isEmpty())
68− {
69− qWarning() << "ScreenShotter: no directory to save screenshot";
70− return;
71− }
72−
73− const QWindowList windows = QGuiApplication::topLevelWindows();
74− if (windows.empty())
75− {
76− qWarning() << "ScreenShotter: no top level windows found!";
77− return;
78− }
79−
80− QQuickWindow *main_window = qobject_cast<QQuickWindow *>(windows[0]);
81− if (!main_window)
82− {
83− qWarning() << "ScreenShotter: can only take screenshots of QQuickWindows";
84− return;
85− }
86−
87− const QImage screenshot = main_window->grabWindow().transformed(QTransform().rotate(angle));
88− const QString filename = makeFileName();
89− qDebug() << "Saving screenshot to" << filename;
90− QFuture<QString> saveFuture(QtConcurrent::run(saveScreenshot, screenshot, filename, getFormat(), screenshotQuality));
91− m_watcher.setFuture(saveFuture);
92−}
93−
94−void ScreenGrabber::onScreenshotSaved()
95−{
96− const QString filename = m_watcher.future().result();
97− if (!filename.isEmpty()) {
98− Q_EMIT screenshotSaved(filename);
99− }
100−}
101−
102−QString ScreenGrabber::makeFileName() const
103−{
104− QString fileName(fileNamePrefix);
105− fileName.append(QDateTime::currentDateTime().toString(QStringLiteral("yyyyMMdd_hhmmsszzz")));
106− fileName.append(".");
107− fileName.append(getFormat());
108− return fileName;
109−}
110−
111−QString ScreenGrabber::getFormat() const
112−{
113− //TODO: This should be configurable (perhaps through gsettings?)
114− return QStringLiteral("png");
115−}
plugins/ScreenGrabber/screengrabber.hdeleted−49
@@ -1,49 +0,0 @@
1−/*
2− * Copyright (C) 2014-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 SNAPSHOTTER_H
18−#define SNAPSHOTTER_H
19−
20−#include <QObject>
21−#include <QString>
22−#include <QtConcurrent>
23−
24−class ScreenGrabber: public QObject
25−{
26− Q_OBJECT
27−
28−public:
29− explicit ScreenGrabber(QObject *parent = 0);
30− ~ScreenGrabber() = default;
31−
32−public Q_SLOTS:
33− void captureAndSave(int angle = 0);
34−
35−Q_SIGNALS:
36− void screenshotSaved(const QString &filename);
37−
38−private Q_SLOTS:
39− void onScreenshotSaved();
40−
41−private:
42− QString makeFileName() const;
43− QString getFormat() const;
44− QString fileNamePrefix;
45− int screenshotQuality = -1; // default quality for the format
46− QFutureWatcher<QString> m_watcher;
47−};
48−
49−#endif
plugins/ScreenshotDirectory/CMakeLists.txtnew file+9
@@ -0,0 +1,9 @@
1+set(PLUGIN_SOURCES
2+ ScreenshotDirectory.cpp
3+ plugin.cpp
4+)
5+
6+add_library(ScreenshotDirectory-qml MODULE ${PLUGIN_SOURCES})
7+qt5_use_modules(ScreenshotDirectory-qml Qml Gui Quick)
8+
9+add_unity8_plugin(ScreenshotDirectory 0.1 ScreenshotDirectory TARGETS ScreenshotDirectory-qml)
plugins/ScreenGrabber/ScreenGrabber.qmltypes → plugins/ScreenshotDirectory/ScreenGrabber.qmltypesrenamed

The content is the same.

plugins/ScreenshotDirectory/ScreenshotDirectory.cppnew file+64
@@ -0,0 +1,64 @@
1+/*
2+ * Copyright (C) 2014-2016 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 "ScreenshotDirectory.h"
18+
19+#include <QDir>
20+#include <QDateTime>
21+#include <QStandardPaths>
22+#include <QTemporaryDir>
23+
24+#include <QDebug>
25+
26+ScreenshotDirectory::ScreenshotDirectory(QObject *parent)
27+ : QObject(parent)
28+{
29+ QDir screenshotsDir;
30+ if (qEnvironmentVariableIsSet("UNITY_TESTING")) {
31+ QTemporaryDir tDir;
32+ tDir.setAutoRemove(false);
33+ screenshotsDir = tDir.path();
34+ } else {
35+ screenshotsDir = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
36+ }
37+ screenshotsDir.mkpath(QStringLiteral("Screenshots"));
38+ screenshotsDir.cd(QStringLiteral("Screenshots"));
39+ if (screenshotsDir.exists()) {
40+ m_fileNamePrefix = screenshotsDir.absolutePath();
41+ m_fileNamePrefix.append("/screenshot");
42+ } else {
43+ qWarning() << "ScreenshotDirectory: failed to create directory at:" << screenshotsDir.absolutePath();
44+ }
45+}
46+
47+QString ScreenshotDirectory::makeFileName() const
48+{
49+ if (m_fileNamePrefix.isEmpty()) {
50+ return QString();
51+ }
52+
53+ QString fileName(m_fileNamePrefix);
54+ fileName.append(QDateTime::currentDateTime().toString(QStringLiteral("yyyyMMdd_hhmmsszzz")));
55+ fileName.append(".");
56+ fileName.append(format());
57+ return fileName;
58+}
59+
60+QString ScreenshotDirectory::format() const
61+{
62+ //TODO: This should be configurable (perhaps through gsettings?)
63+ return QStringLiteral("png");
64+}
plugins/Utils/windowscreenshotprovider.h → plugins/ScreenshotDirectory/ScreenshotDirectory.hrenamed+17−9
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2015 Canonical, Ltd.
2+ * Copyright (C) 2014-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
@@ -14,18 +14,26 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
15 */
16
16
17−#ifndef WINDOW_SCREENSHOT_PROVIDER_H_
17+#ifndef SCREENSHOTDIRECTORY_H
18−#define WINDOW_SCREENSHOT_PROVIDER_H_
18+#define SCREENSHOTDIRECTORY_H
19
19
20−#include <QQuickImageProvider>
20+#include <QObject>
21+#include <QString>
21
22
22−class WindowScreenshotProvider : public QQuickImageProvider
23+class ScreenshotDirectory: public QObject
23{
24{
25+ Q_OBJECT
26+
24public:
27public:
25− WindowScreenshotProvider();
28+ explicit ScreenshotDirectory(QObject *parent = 0);
29+ ~ScreenshotDirectory() = default;
30+
31+public Q_SLOTS:
32+ QString makeFileName() const;
26
33
27− // id is ignored for now
34+private:
28− QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize) override;
35+ QString format() const;
36+ QString m_fileNamePrefix;
29};
37};
30
38
31−#endif // WINDOW_SCREENSHOT_PROVIDER_H_
39+#endif // SCREENSHOTDIRECTORY_H
plugins/ScreenGrabber/plugin.cpp → plugins/ScreenshotDirectory/plugin.cpprenamed+5−7
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014,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
@@ -12,17 +12,15 @@
12 *
12 *
13 * You should have received a copy of the GNU General Public License
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/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Authors: Alberto Aguirre <alberto.aguirre@canonical.com>
17 */
15 */
18
16
19#include "plugin.h"
17#include "plugin.h"
20−#include "screengrabber.h"
18+#include "ScreenshotDirectory.h"
21
19
22#include <QtQml/qqml.h>
20#include <QtQml/qqml.h>
23
21
24−void ScreenGrabberPlugin::registerTypes(const char *uri)
22+void ScreenshotDirectoryPlugin::registerTypes(const char *uri)
25{
23{
26− Q_ASSERT(uri == QLatin1String("ScreenGrabber"));
24+ Q_ASSERT(uri == QLatin1String("ScreenshotDirectory"));
27− qmlRegisterType<ScreenGrabber>(uri, 0, 1, "ScreenGrabber");
25+ qmlRegisterType<ScreenshotDirectory>(uri, 0, 1, "ScreenshotDirectory");
28}
26}
plugins/ScreenGrabber/plugin.h → plugins/ScreenshotDirectory/plugin.hrenamed+5−7
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014,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
@@ -12,16 +12,14 @@
12 *
12 *
13 * You should have received a copy of the GNU General Public License
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/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Authors: Alberto Aguirre <alberto.aguirre@canonical.com>
17 */
15 */
18
16
19−#ifndef SCREENGRABBER_PLUGIN_H
17+#ifndef SCREENSHOTDIRECTORY_PLUGIN_H
20−#define SCREENGRABBER_PLUGIN_H
18+#define SCREENSHOTDIRECTORY_PLUGIN_H
21
19
22#include <QtQml/QQmlExtensionPlugin>
20#include <QtQml/QQmlExtensionPlugin>
23
21
24−class ScreenGrabberPlugin : public QQmlExtensionPlugin
22+class ScreenshotDirectoryPlugin : public QQmlExtensionPlugin
25{
23{
26 Q_OBJECT
24 Q_OBJECT
27 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
25 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
@@ -30,4 +28,4 @@ public:
30 void registerTypes(const char *uri) override;
28 void registerTypes(const char *uri) override;
31};
29};
32
30
33−#endif // SCREENGRABBER_PLUGIN_H
31+#endif // SCREENSHOTDIRECTORY_PLUGIN_H
plugins/ScreenshotDirectory/qmldirnew file+2
@@ -0,0 +1,2 @@
1+module ScreenshotDirectory
2+plugin ScreenshotDirectory-qml
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−}
po/unity8.pot+4−4
@@ -8,7 +8,7 @@ msgid ""
8msgstr ""
8msgstr ""
9"Project-Id-Version: unity8\n"
9"Project-Id-Version: unity8\n"
10"Report-Msgid-Bugs-To: \n"
10"Report-Msgid-Bugs-To: \n"
11−"POT-Creation-Date: 2016-04-27 15:01+0000\n"
11+"POT-Creation-Date: 2016-04-29 20:07+0000\n"
12"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
12"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14"Language-Team: LANGUAGE <LL@li.org>\n"
14"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -186,15 +186,15 @@ msgstr ""
186msgid "Reset"
186msgid "Reset"
187msgstr ""
187msgstr ""
188
188
189−#: qml/Dash/GenericScopeView.qml:570 qml/Dash/GenericScopeView.qml:755
189+#: qml/Dash/GenericScopeView.qml:575 qml/Dash/GenericScopeView.qml:760
190msgid "Show less"
190msgid "Show less"
191msgstr ""
191msgstr ""
192
192
193−#: qml/Dash/GenericScopeView.qml:570
193+#: qml/Dash/GenericScopeView.qml:575
194msgid "Show all"
194msgid "Show all"
195msgstr ""
195msgstr ""
196
196
197−#: qml/Dash/GenericScopeView.qml:627
197+#: qml/Dash/GenericScopeView.qml:632
198msgctxt "Label: Hint for dash search line edit"
198msgctxt "Label: Hint for dash search line edit"
199msgid "Search"
199msgid "Search"
200msgstr ""
200msgstr ""
qml/Components/ScreenGrabber.qml → qml/Components/ItemGrabber.qmlrenamed+27−17
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2014-2015 Canonical, Ltd.
2+ * Copyright (C) 2014-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
@@ -15,36 +15,31 @@
15 */
15 */
16
16
17import QtQuick 2.4
17import QtQuick 2.4
18−import ScreenGrabber 0.1
18+import ScreenshotDirectory 0.1
19−import GlobalShortcut 1.0
20
19
20+/*
21+ Captures an image of the given item and saves it in a screenshots directory.
22+ It also displays a flash visual effect and camera shutter sound, as feedback
23+ to the user to hint that a screenshot was taken.
24+ */
21Rectangle {
25Rectangle {
22 id: root
26 id: root
23 visible: false
27 visible: false
24 color: "white"
28 color: "white"
25− anchors.fill: parent
26 opacity: 0.0
29 opacity: 0.0
27
30
28− // to be set from outside
31+ ScreenshotDirectory {
29− property int rotationAngle: 0
32+ id: screenshotDirectory
30−
31− ScreenGrabber {
32− id: screenGrabber
33 objectName: "screenGrabber"
33 objectName: "screenGrabber"
34 }
34 }
35
35
36− GlobalShortcut {
37− id: screenshotShortcut
38− shortcut: Qt.Key_Print
39− onTriggered: capture()
40− }
41−
42 NotificationAudio {
36 NotificationAudio {
43 id: shutterSound
37 id: shutterSound
44 source: "/system/media/audio/ui/camera_click.ogg"
38 source: "/system/media/audio/ui/camera_click.ogg"
45 }
39 }
46
40
47− function capture() {
41+ function capture(item) {
42+ d.target = item;
48 visible = true;
43 visible = true;
49 shutterSound.stop();
44 shutterSound.stop();
50 shutterSound.play();
45 shutterSound.play();
@@ -62,13 +57,28 @@ Rectangle {
62 }
57 }
63 }
58 }
64
59
60+ QtObject {
61+ id: d
62+ property Item target
63+ }
64+
65 NumberAnimation on opacity {
65 NumberAnimation on opacity {
66 id: fadeOut
66 id: fadeOut
67 from: 1.0
67 from: 1.0
68 to: 0.0
68 to: 0.0
69 onStopped: {
69 onStopped: {
70 if (visible) {
70 if (visible) {
71− screenGrabber.captureAndSave(root.rotationAngle);
71+ d.target.grabToImage(function(result)
72+ {
73+ var fileName = screenshotDirectory.makeFileName();
74+ if (fileName.length === 0) {
75+ console.warn("ItemGrabber: No fileName to save image to");
76+ } else {
77+ console.log("ItemGrabber: Saving image to " + fileName);
78+ result.saveToFile(fileName);
79+ }
80+ });
81+
72 visible = false;
82 visible = false;
73 }
83 }
74 }
84 }
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/Components/NotificationAudio.qml+7−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
@@ -23,10 +23,14 @@ Item {
23 readonly property var playbackState: priv.audio ? priv.audio.playbackState : 0
23 readonly property var playbackState: priv.audio ? priv.audio.playbackState : 0
24
24
25 function play() {
25 function play() {
26+ if (priv.audio) {
26− priv.audio.play();
27+ priv.audio.play();
28+ }
27 }
29 }
28 function stop() {
30 function stop() {
31+ if (priv.audio) {
29− priv.audio.stop();
32+ priv.audio.stop();
33+ }
30 }
34 }
31
35
32 QtObject {
36 QtObject {
qml/Components/VirtualTouchPad.qml+1−1
@@ -31,7 +31,7 @@ Item {
31 anchors.fill: parent
31 anchors.fill: parent
32
32
33 // FIXME: Once we have Qt DPR support, this should be Qt.styleHints.startDragDistance
33 // FIXME: Once we have Qt DPR support, this should be Qt.styleHints.startDragDistance
34− readonly property int clickThreshold: units.gu(1.5)
34+ readonly property int clickThreshold: internalGu * 1.5
35 property bool isClick: false
35 property bool isClick: false
36 property bool isDoubleClick: false
36 property bool isDoubleClick: false
37 property bool isDrag: false
37 property bool isDrag: false
qml/Dash/GenericScopeView.qml+5
@@ -360,6 +360,11 @@ FocusScope {
360 scopeView.enableHeightBehaviorOnNextCreation = item.enableHeightBehaviorOnNextCreation;
360 scopeView.enableHeightBehaviorOnNextCreation = item.enableHeightBehaviorOnNextCreation;
361 }
361 }
362 }
362 }
363+ // FIXME: directly connecting to onUnitsChanged cause a compile error:
364+ // Cannot assign to non-existent property "onUnitsChanged"
365+ // Until the units object is reworked to properly do all we need, let's go through a intermediate property
366+ property int pxpgu: units.gu(1);
367+ onPxpguChanged: clickScopeSizingHacks();
363
368
364 function clickScopeSizingHacks() {
369 function clickScopeSizingHacks() {
365 if (scope && scope.id === "clickscope" &&
370 if (scope && scope.id === "clickscope" &&
qml/DisabledScreenNotice.qml+4−4
@@ -96,19 +96,19 @@ Item {
96
96
97 Column {
97 Column {
98 anchors.centerIn: parent
98 anchors.centerIn: parent
99− width: parent.width - units.gu(8)
99+ width: parent.width - (internalGu * 8)
100− spacing: units.gu(4)
100+ spacing: internalGu * 4
101
101
102 Label {
102 Label {
103 id: text
103 id: text
104 text: i18n.tr("Your device is now connected to an external display. Use this screen as a touch pad to interact with the pointer.")
104 text: i18n.tr("Your device is now connected to an external display. Use this screen as a touch pad to interact with the pointer.")
105 color: "white"
105 color: "white"
106 width: parent.width
106 width: parent.width
107− fontSize: "large"
107+ font.pixelSize: 2.5 * internalGu
108 wrapMode: Text.Wrap
108 wrapMode: Text.Wrap
109 }
109 }
110 Icon {
110 Icon {
111− height: units.gu(8)
111+ height: internalGu * 8
112 width: height
112 width: height
113 name: "input-touchpad-symbolic"
113 name: "input-touchpad-symbolic"
114 color: "white"
114 color: "white"
qml/OrientedShell.qml+7−7
@@ -26,9 +26,8 @@ import "Rotation"
26// Workaround https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1473471
26// Workaround https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1473471
27import Ubuntu.Components 1.3
27import Ubuntu.Components 1.3
28
28
29−Rectangle {
29+Item {
30 id: root
30 id: root
31− color: "black"
32
31
33 implicitWidth: units.gu(40)
32 implicitWidth: units.gu(40)
34 implicitHeight: units.gu(71)
33 implicitHeight: units.gu(71)
@@ -241,7 +240,7 @@ Rectangle {
241 orientedShell: root
240 orientedShell: root
242 shell: shell
241 shell: shell
243 shellCover: shellCover
242 shellCover: shellCover
244− windowScreenshot: windowScreenshot
243+ shellSnapshot: shellSnapshot
245 }
244 }
246
245
247 Shell {
246 Shell {
@@ -291,8 +290,9 @@ Rectangle {
291 visible: false
290 visible: false
292 }
291 }
293
292
294− WindowScreenshot {
293+ ItemSnapshot {
295− id: windowScreenshot
294+ id: shellSnapshot
295+ target: shell
296 visible: false
296 visible: false
297 width: root.width
297 width: root.width
298 height: root.height
298 height: root.height
@@ -302,9 +302,9 @@ Rectangle {
302 property real transformOriginY
302 property real transformOriginY
303
303
304 transform: Rotation {
304 transform: Rotation {
305− origin.x: windowScreenshot.transformOriginX; origin.y: windowScreenshot.transformOriginY;
305+ origin.x: shellSnapshot.transformOriginX; origin.y: shellSnapshot.transformOriginY;
306 axis { x: 0; y: 0; z: 1 }
306 axis { x: 0; y: 0; z: 1 }
307− angle: windowScreenshot.transformRotationAngle
307+ angle: shellSnapshot.transformRotationAngle
308 }
308 }
309 }
309 }
310}
310}
qml/Panel/IndicatorsMenu.qml+20−10
@@ -43,18 +43,28 @@ Showable {
43
43
44 // TODO: Perhaps we need a animation standard for showing/hiding? Each showable seems to
44 // TODO: Perhaps we need a animation standard for showing/hiding? Each showable seems to
45 // use its own values. Need to ask design about this.
45 // use its own values. Need to ask design about this.
46+ showAnimation: SequentialAnimation {
46− showAnimation: StandardAnimation {
47+ StandardAnimation {
48+ target: root
47− property: "height"
49+ property: "height"
48− to: openedHeight
50+ to: openedHeight
49− duration: UbuntuAnimation.BriskDuration
51+ duration: UbuntuAnimation.BriskDuration
50− easing.type: Easing.OutCubic
52+ easing.type: Easing.OutCubic
53+ }
54+ // set binding in case units.gu changes while menu open, so height correctly adjusted to fit
55+ ScriptAction { script: root.height = Qt.binding( function(){ return root.openedHeight; } ) }
51 }
56 }
52
57
58+ hideAnimation: SequentialAnimation {
53− hideAnimation: StandardAnimation {
59+ StandardAnimation {
60+ target: root
54− property: "height"
61+ property: "height"
55− to: minimizedPanelHeight
62+ to: minimizedPanelHeight
56− duration: UbuntuAnimation.BriskDuration
63+ duration: UbuntuAnimation.BriskDuration
57− easing.type: Easing.OutCubic
64+ easing.type: Easing.OutCubic
65+ }
66+ // set binding in case units.gu changes while menu closed, so menu adjusts to fit
67+ ScriptAction { script: root.height = Qt.binding( function(){ return root.minimizedPanelHeight; } ) }
58 }
68 }
59
69
60 height: minimizedPanelHeight
70 height: minimizedPanelHeight
qml/Rotation/HalfLoopRotationAnimation.qml+2−1
@@ -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
@@ -42,5 +42,6 @@ SequentialAnimation {
42 from: root.fromAngle; to: root.toAngle
42 from: root.fromAngle; to: root.toAngle
43 duration: rotationDuration; easing.type: rotationEasing
43 duration: rotationDuration; easing.type: rotationEasing
44 }
44 }
45+ UpdateShellTransformations { info: root.info; shell: root.shell }
45 ScriptAction { script: { info.transitioning = false; } }
46 ScriptAction { script: { info.transitioning = false; } }
46}
47}
qml/Rotation/ImmediateRotationAction.qml+11−22
@@ -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
@@ -16,30 +16,19 @@
16
16
17import QtQuick 2.4
17import QtQuick 2.4
18
18
19−ScriptAction {
19+SequentialAnimation {
20+ id: root
20 property var info
21 property var info
21 property var shell
22 property var shell
22
23
23− script: {
24+ ScriptAction { script: {
24− info.transitioning = true;
25+ root.info.transitioning = true;
25− shell.orientationAngle = info.requestedOrientationAngle;
26+ } }
26− shell.transformRotationAngle = info.requestedOrientationAngle;
27
27
28− // Making bindings as orientedShell's dimensions might wiggle during startup.
28+ UpdateShellTransformations { info: root.info; shell: root.shell }
29− if (info.requestedOrientationAngle === 90 || info.requestedOrientationAngle === 270) {
30− shell.width = Qt.binding(function() { return orientedShell.height; });
31− shell.height = Qt.binding(function() { return orientedShell.width; });
32− } else {
33− shell.width = Qt.binding(function() { return orientedShell.width; });
34− shell.height = Qt.binding(function() { return orientedShell.height; });
35− }
36
29
37− shell.x = Qt.binding(function() { return (orientedShell.width - shell.width) / 2; });
30+ ScriptAction { script: {
38− shell.y = Qt.binding(function() { return (orientedShell.height - shell.height) / 2; });
39− shell.transformOriginX = Qt.binding(function() { return shell.width / 2; });
40− shell.transformOriginY = Qt.binding(function() { return shell.height / 2; });
41−
42− shell.updateFocusedAppOrientation();
31+ root.shell.updateFocusedAppOrientation();
43− info.transitioning = false;
32+ root.info.transitioning = false;
44− }
33+ } }
45}
34}
qml/Rotation/NinetyRotationAnimation.qml+14−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,25 @@ 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 }
81+ UpdateShellTransformations { info: root.info; shell: root.shell }
84 ScriptAction { script: {
82 ScriptAction { script: {
85− windowScreenshot.visible = false;
83+ shellSnapshot.visible = false;
86− windowScreenshot.discard();
84+ shellSnapshot.discard();
87 shellCover.visible = false;
85 shellCover.visible = false;
88 info.transitioning = false;
86 info.transitioning = false;
89 } }
87 } }
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: {
qml/Rotation/UpdateShellTransformations.qmlnew file+42
@@ -0,0 +1,42 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.4
18+
19+ScriptAction {
20+ property var info
21+ property var shell
22+
23+ script: {
24+ shell.orientationAngle = info.requestedOrientationAngle;
25+ shell.transformRotationAngle = info.requestedOrientationAngle;
26+
27+ // They must all be bindings as orientedShell's size can change
28+
29+ if (info.requestedOrientationAngle === 90 || info.requestedOrientationAngle === 270) {
30+ shell.width = Qt.binding(function() { return orientedShell.height; });
31+ shell.height = Qt.binding(function() { return orientedShell.width; });
32+ } else {
33+ shell.width = Qt.binding(function() { return orientedShell.width; });
34+ shell.height = Qt.binding(function() { return orientedShell.height; });
35+ }
36+
37+ shell.x = Qt.binding(function() { return (orientedShell.width - shell.width) / 2; });
38+ shell.y = Qt.binding(function() { return (orientedShell.height - shell.height) / 2; });
39+ shell.transformOriginX = Qt.binding(function() { return shell.width / 2; });
40+ shell.transformOriginY = Qt.binding(function() { return shell.height / 2; });
41+ }
42+}
qml/Shell.qml+11−5
@@ -193,7 +193,7 @@ Item {
193 onPowerKeyLongPressed: dialogs.showPowerDialog();
193 onPowerKeyLongPressed: dialogs.showPowerDialog();
194 onVolumeDownTriggered: volumeControl.volumeDown();
194 onVolumeDownTriggered: volumeControl.volumeDown();
195 onVolumeUpTriggered: volumeControl.volumeUp();
195 onVolumeUpTriggered: volumeControl.volumeUp();
196− onScreenshotTriggered: screenGrabber.capture();
196+ onScreenshotTriggered: itemGrabber.capture(shell);
197 }
197 }
198
198
199 GlobalShortcut {
199 GlobalShortcut {
@@ -705,16 +705,22 @@ Item {
705 onShowHome: showHome()
705 onShowHome: showHome()
706 }
706 }
707
707
708− ScreenGrabber {
708+ ItemGrabber {
709− id: screenGrabber
709+ id: itemGrabber
710− rotationAngle: -shell.orientationAngle
710+ anchors.fill: parent
711 z: dialogs.z + 10
711 z: dialogs.z + 10
712+ GlobalShortcut { shortcut: Qt.Key_Print; onTriggered: itemGrabber.capture(shell) }
713+ Connections {
714+ target: applicationsDisplayLoader.item
715+ ignoreUnknownSignals: true
716+ onItemSnapshotRequested: itemGrabber.capture(item)
717+ }
712 }
718 }
713
719
714 Cursor {
720 Cursor {
715 id: cursor
721 id: cursor
716 visible: shell.hasMouse
722 visible: shell.hasMouse
717− z: screenGrabber.z + 1
723+ z: itemGrabber.z + 1
718
724
719 onPushedLeftBoundary: {
725 onPushedLeftBoundary: {
720 if (buttons === Qt.NoButton) {
726 if (buttons === Qt.NoButton) {
qml/Stages/AbstractStage.qml+8−7
@@ -21,13 +21,6 @@ import GSettings 1.0
21FocusScope {
21FocusScope {
22 id: root
22 id: root
23
23
24− property alias color: backRect.color
25− Rectangle {
26− id: backRect
27− color: "#060606"
28− anchors.fill: parent
29− }
30−
31 // Controls to be set from outside
24 // Controls to be set from outside
32 property QtObject applicationManager
25 property QtObject applicationManager
33 property QtObject topLevelSurfaceList
26 property QtObject topLevelSurfaceList
@@ -49,6 +42,7 @@ FocusScope {
49 property bool suspended
42 property bool suspended
50 // A Stage should paint a wallpaper etc over its full size but not use the margins for window placement
43 // A Stage should paint a wallpaper etc over its full size but not use the margins for window placement
51 property int leftMargin: 0
44 property int leftMargin: 0
45+ property alias paintBackground: background.visible
52
46
53 // To be read from outside
47 // To be read from outside
54 property var mainApp: null
48 property var mainApp: null
@@ -60,6 +54,7 @@ FocusScope {
60 | Qt.InvertedLandscapeOrientation
54 | Qt.InvertedLandscapeOrientation
61
55
62 signal stageAboutToBeUnloaded
56 signal stageAboutToBeUnloaded
57+ signal itemSnapshotRequested(Item item)
63
58
64 // Shared code for use in stage implementations
59 // Shared code for use in stage implementations
65 GSettings {
60 GSettings {
@@ -76,4 +71,10 @@ FocusScope {
76 }
71 }
77 return false;
72 return false;
78 }
73 }
74+
75+ Rectangle {
76+ id: background
77+ color: "#060606"
78+ anchors.fill: parent
79+ }
79}
80}
qml/Stages/DesktopStage.qml+7
@@ -26,6 +26,7 @@ import GlobalShortcut 1.0
26AbstractStage {
26AbstractStage {
27 id: root
27 id: root
28 anchors.fill: parent
28 anchors.fill: parent
29+ paintBackground: false
29
30
30 // functions to be called from outside
31 // functions to be called from outside
31 function updateFocusedAppOrientation() { /* TODO */ }
32 function updateFocusedAppOrientation() { /* TODO */ }
@@ -94,6 +95,12 @@ AbstractStage {
94 active: priv.focusedAppDelegate !== null
95 active: priv.focusedAppDelegate !== null
95 }
96 }
96
97
98+ GlobalShortcut {
99+ shortcut: Qt.AltModifier|Qt.Key_Print
100+ onTriggered: root.itemSnapshotRequested(priv.focusedAppDelegate)
101+ active: priv.focusedAppDelegate !== null
102+ }
103+
97 Connections {
104 Connections {
98 target: root.topLevelSurfaceList
105 target: root.topLevelSurfaceList
99 onCountChanged: {
106 onCountChanged: {
qml/Stages/PhoneStage.qml+2
@@ -31,6 +31,8 @@ AbstractStage {
31 property real startScale: 1.1
31 property real startScale: 1.1
32 property real endScale: 0.7
32 property real endScale: 0.7
33
33
34+ paintBackground: spreadView.shiftedContentX !== 0
35+
34 onBeingResizedChanged: {
36 onBeingResizedChanged: {
35 if (beingResized) {
37 if (beingResized) {
36 // Brace yourselves for impact!
38 // Brace yourselves for impact!
qml/Stages/ShimStage.qml−1
@@ -21,7 +21,6 @@ AbstractStage {
21 id: shimStage
21 id: shimStage
22
22
23 anchors.fill: parent
23 anchors.fill: parent
24− color: UbuntuColors.lightAubergine
25
24
26 Text {
25 Text {
27 id: greeterModeText
26 id: greeterModeText
qml/Stages/TabletStage.qml+3
@@ -34,6 +34,8 @@ AbstractStage {
34 property int stageFocusedSurface: priv.focusedAppDelegate ? priv.focusedAppDelegate.stage : ApplicationInfoInterface.MainStage
34 property int stageFocusedSurface: priv.focusedAppDelegate ? priv.focusedAppDelegate.stage : ApplicationInfoInterface.MainStage
35 // </tutorial-hacks>
35 // </tutorial-hacks>
36
36
37+ paintBackground: spreadView.shiftedContentX !== 0
38+
37 // Functions to be called from outside
39 // Functions to be called from outside
38 function updateFocusedAppOrientation() {
40 function updateFocusedAppOrientation() {
39 var mainStageIndex = root.topLevelSurfaceList.indexForId(priv.mainStageItemId);
41 var mainStageIndex = root.topLevelSurfaceList.indexForId(priv.mainStageItemId);
@@ -746,6 +748,7 @@ AbstractStage {
746 surface: model.surface
748 surface: model.surface
747 closeable: !isDash
749 closeable: !isDash
748 highlightShown: root.altTabPressed && priv.highlightIndex == zIndex
750 highlightShown: root.altTabPressed && priv.highlightIndex == zIndex
751+ dropShadow: spreadView.active || priv.focusedAppDelegateIsDislocated
749
752
750 readonly property bool wantsMainStage: stage == ApplicationInfoInterface.MainStage
753 readonly property bool wantsMainStage: stage == ApplicationInfoInterface.MainStage
751
754
src/SecondaryWindow.cpp+9
@@ -19,9 +19,18 @@
19// local
19// local
20#include <paths.h>
20#include <paths.h>
21
21
22+#include <QQmlContext>
23+
22SecondaryWindow::SecondaryWindow(QQmlEngine *engine)
24SecondaryWindow::SecondaryWindow(QQmlEngine *engine)
23 : QQuickView(engine, nullptr)
25 : QQuickView(engine, nullptr)
24{
26{
27+ QByteArray pxpguEnv = qgetenv("GRID_UNIT_PX");
28+ bool ok;
29+ int pxpgu = pxpguEnv.toInt(&ok);
30+ if (!ok) {
31+ pxpgu = 8;
32+ }
33+ engine->rootContext()->setContextProperty("internalGu", pxpgu);
25 setResizeMode(QQuickView::SizeRootObjectToView);
34 setResizeMode(QQuickView::SizeRootObjectToView);
26 setColor("black");
35 setColor("black");
27 setTitle(QStringLiteral("Unity8 Shell - Secondary Screen"));
36 setTitle(QStringLiteral("Unity8 Shell - Secondary Screen"));
src/ShellApplication.cpp+6
@@ -168,6 +168,9 @@ void ShellApplication::onScreenAdded(QScreen * /*screen*/)
168 // Changing the QScreen where a QWindow is drawn makes it also lose focus (besides having
168 // Changing the QScreen where a QWindow is drawn makes it also lose focus (besides having
169 // its backing QPlatformWindow recreated). So lets refocus it.
169 // its backing QPlatformWindow recreated). So lets refocus it.
170 m_shellView->requestActivate();
170 m_shellView->requestActivate();
171+ // QWindow::destroy() is called when it changes between screens. We have to manually make it visible again
172+ // <dandrader> This bug is supposedly fixed in Qt 5.5.1, although I can still reproduce it there. :-/
173+ m_shellView->setVisible(true);
171
174
172 m_secondaryWindow = new SecondaryWindow(m_qmlEngine);
175 m_secondaryWindow = new SecondaryWindow(m_qmlEngine);
173 m_secondaryWindow->setScreen(screens().at(0));
176 m_secondaryWindow->setScreen(screens().at(0));
@@ -194,5 +197,8 @@ void ShellApplication::onScreenAboutToBeRemoved(QScreen *screen)
194 // Changing the QScreen where a QWindow is drawn makes it also lose focus (besides having
197 // Changing the QScreen where a QWindow is drawn makes it also lose focus (besides having
195 // its backing QPlatformWindow recreated). So lets refocus it.
198 // its backing QPlatformWindow recreated). So lets refocus it.
196 m_shellView->requestActivate();
199 m_shellView->requestActivate();
200+ // QWindow::destroy() is called when it changes between screens. We have to manually make it visible again
201+ // <dandrader> This bug is supposedly fixed in Qt 5.5.1, although I can still reproduce it there. :-/
202+ m_shellView->setVisible(true);
197 }
203 }
198}
204}
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
tests/plugins/CMakeLists.txt−1
@@ -3,7 +3,6 @@ add_subdirectory(GlobalShortcut)
3add_subdirectory(Greeter)
3add_subdirectory(Greeter)
4add_subdirectory(IntegratedLightDM)
4add_subdirectory(IntegratedLightDM)
5add_subdirectory(Dash)
5add_subdirectory(Dash)
6−add_subdirectory(ScreenGrabber)
7add_subdirectory(Ubuntu)
6add_subdirectory(Ubuntu)
8add_subdirectory(Unity)
7add_subdirectory(Unity)
9add_subdirectory(Utils)
8add_subdirectory(Utils)
tests/plugins/ScreenGrabber/CMakeLists.txtdeleted−14
@@ -1,14 +0,0 @@
1−include_directories(
2− ${CMAKE_SOURCE_DIR}/plugins/ScreenGrabber
3− ${CMAKE_CURRENT_BINARY_DIR}
4− )
5−
6−add_definitions(
7− -DCURRENT_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}"
8− )
9−add_definitions(-DBUILT_PLUGINS_DIR="${CMAKE_BINARY_DIR}/plugins")
10−
11−add_executable(ScreenGrabberTestExec ScreenGrabberTest.cpp ${CMAKE_SOURCE_DIR}/plugins/ScreenGrabber/screengrabber.cpp)
12−qt5_use_modules(ScreenGrabberTestExec Test Quick Concurrent)
13−target_link_libraries(ScreenGrabberTestExec ${Qt5Quick_LIBRARIES} ScreenGrabber-qml)
14−add_unity8_uitest(ScreenGrabber ScreenGrabberTestExec DEPENDS ScreenGrabber-qml)
tests/plugins/ScreenGrabber/ScreenGrabberTest.cppdeleted−77
@@ -1,77 +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 "screengrabber.h"
18−
19−#include <QtTest>
20−#include <QtQuick/QQuickView>
21−#include <QtQuick/QQuickItem>
22−#include <QQmlEngine>
23−
24−class ScreenGrabberTest: public QObject
25−{
26− Q_OBJECT
27−
28−private Q_SLOTS:
29−
30− void initTestCase()
31− {
32− m_view = new QQuickView();
33− m_view->engine()->addImportPath(BUILT_PLUGINS_DIR);
34− m_view->setSource(QUrl::fromLocalFile(CURRENT_SOURCE_DIR "/grabber.qml"));
35− m_grabber = dynamic_cast<ScreenGrabber*>(m_view->rootObject()->property("grabber").value<QObject*>());
36− QVERIFY(m_grabber);
37− m_view->show();
38− QTest::qWaitForWindowExposed(m_view);
39− }
40−
41− void cleanupTestCase()
42− {
43− delete m_view;
44− m_view = nullptr;
45− QTRY_COMPARE(QThreadPool::globalInstance()->activeThreadCount(), 0);
46− }
47−
48− void testGrabScreenshot()
49− {
50− QSignalSpy grabberSpy(m_grabber, &ScreenGrabber::screenshotSaved);
51− m_grabber->captureAndSave();
52− QTRY_VERIFY(grabberSpy.count() == 1);
53− const QVariantList args = grabberSpy.takeFirst();
54− QVERIFY(args.count() == 1); // verify we got a non-empty filename where the screenshot has been saved
55− QVERIFY(!args.first().toString().isEmpty());
56− }
57−
58− void testRotatedScreenshot()
59− {
60− QSignalSpy grabberSpy(m_grabber, &ScreenGrabber::screenshotSaved);
61− m_grabber->captureAndSave(90); // rotate by 90°
62− QTRY_VERIFY(grabberSpy.count() == 1);
63− const QVariantList args = grabberSpy.takeFirst();
64− const QString filename = args.first().toString();
65− QVERIFY(!filename.isEmpty());
66− QImage img(filename);
67− QVERIFY(img.height() > img.width()); // verify that the image got rotated by 90° (height > width)
68− }
69−
70−private:
71− QQuickView *m_view;
72− ScreenGrabber *m_grabber = nullptr;
73−};
74−
75−QTEST_MAIN(ScreenGrabberTest)
76−
77−#include "ScreenGrabberTest.moc"
tests/plugins/ScreenGrabber/grabber.qmldeleted−31
@@ -1,31 +0,0 @@
1−/*
2− * Copyright 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−import QtQuick 2.4
18−import ScreenGrabber 0.1
19−
20−Rectangle {
21− property var grabber: screenGrabber
22−
23− width: 101 // width intentionally bigger than height to test rotation
24− height: 100
25− color: "green"
26−
27− ScreenGrabber {
28− id: screenGrabber
29− objectName: "screenGrabber"
30− }
31−}