on xenial help sign in
Merge
0cded64Albert Astals Cid
build.sh+2−2
@@ -46,11 +46,11 @@ install_dependencies() {
46 qmenumodel-qml \
46 qmenumodel-qml \
47 qml-module-qtquick-layouts \
47 qml-module-qtquick-layouts \
48 qml-module-qtquick-xmllistmodel \
48 qml-module-qtquick-xmllistmodel \
49+ qml-module-ubuntu-components \
50+ qml-module-ubuntu-thumbnailer0.1 \
49 qtdeclarative5-gsettings1.0 \
51 qtdeclarative5-gsettings1.0 \
50− qtdeclarative5-ubuntu-ui-toolkit-plugin \
51 qtdeclarative5-ubuntu-settings-components \
52 qtdeclarative5-ubuntu-settings-components \
52 qtdeclarative5-ubuntu-telephony0.1 \
53 qtdeclarative5-ubuntu-telephony0.1 \
53− qtdeclarative5-ubuntu-thumbnailer0.1 \
54 qtdeclarative5-unity-notifications-plugin \
54 qtdeclarative5-unity-notifications-plugin \
55 qml-module-ubuntu-connectivity \
55 qml-module-ubuntu-connectivity \
56 ubuntu-mobile-icons \
56 ubuntu-mobile-icons \
data/unity8-dash.png

Binary file not shown.

data/unity8.conf+13
@@ -65,6 +65,19 @@ end script
65exec ${BINARY:-unity8} --mode=$MODE $ARGS
65exec ${BINARY:-unity8} --mode=$MODE $ARGS
66
66
67post-start script
67post-start script
68+ # If no MIR_SOCKET was defined on launch, we try to set one here
69+ if [ -z "$(initctl get-env --global UNITY_MIR_SOCKET)" ]; then
70+ if [ -S "$XDG_RUNTIME_DIR/mir_socket" ]; then
71+ mir_socket=$XDG_RUNTIME_DIR/mir_socket
72+ elif [ -S "/tmp/mir_socket" ]; then
73+ mir_socket=/tmp/mir_socket
74+ fi
75+
76+ if [ -n "$mir_socket" ]; then
77+ initctl set-env --global MIR_SOCKET="$mir_socket"
78+ fi
79+ fi
80+
68 initctl emit --no-wait indicator-services-start
81 initctl emit --no-wait indicator-services-start
69end script
82end script
70
83
debian/changelog+63
@@ -1,3 +1,66 @@
1+unity8 (8.12+16.10.20160627.5-0ubuntu1) yakkety; urgency=medium
2+
3+ [ Albert Astals Cid ]
4+ * Stop watching the old header item height once it's not our header
5+ anymore
6+ * LVWPH: cull the header item when not on view to save some painting
7+ * Adapt test to new mock behaviour
8+ * Don't let the mouse go outside the parentItem (LP: #1525350)
9+ * Use non deprecated package names
10+ * Mark system includes as such
11+ * Fix initialization order warning
12+ * Do not rely on deleteLater not crashing on null pointers
13+ * Add more headers of classes we use for automoc to be run
14+ * CardTool: Fix the fallback value to be a size and not an integer
15+ * CardTool: Include Ubuntu Components since we're using units.gu
16+ * Rework tst_PreviewRatingDisplayCreationRanges
17+
18+ [ Daniel d'Andrada ]
19+ * Cursor: Let AnimatedSprite run only on animated cursors and while
20+ the display is on (LP: #1588873, #1594358)
21+ * Fix missing screenshot when orientation changes while shell rotation
22+ ongoing (LP: #1587796)
23+
24+ [ Josh Arenson ]
25+ * Make PreviewRatingSingleDisplay visible again after editing a
26+ review. (LP: #1587097)
27+ * Correct errors in all png images to prevent libpng warnings.
28+
29+ [ Lukáš Tinkl ]
30+ * Fix some smaller issues with OOBE wizard password pages (LP:
31+ #1590467)
32+ * Prevent mouse wheel events from going through shell elements (LP:
33+ #1536268)
34+ * Do not let mouse and wheel events pass thru the titlebar (LP:
35+ #1588424)
36+ * Hide the maximize window button based on size restrictions (LP:
37+ #1541982)
38+
39+ [ Marco Trevisan (Treviño) ]
40+ * Unity8.conf: ensure MIR_SOCKET is set for future customers (such as
41+ the dash)
42+ * ClientIndicator: be more informative about loading failures for
43+ dynamically created objects
44+
45+ [ Michael Terry ]
46+ * Stop unity8 from getting in a "switch-to-greeter" loop when locking
47+ on a desktop. (LP: #1593718)
48+
49+ [ Michael Zanetti ]
50+ * set minimum window sizes on dash
51+ * displace application windows when the OSK would cover them
52+
53+ [ Nick Dedekind ]
54+ * Force dash to always stay in the main stage. (lp:#1592404) (LP:
55+ #1592404)
56+ * Save the last surface stage on stage drop. (LP: #1579003)
57+ * Default portrait only apps to the side stage. (LP: #1573153)
58+
59+ [ Albert Astals Cid, Andrea Cimitan, Michał Sawicz, Nick Dedekind ]
60+ * Added live video playback in dash previews
61+
62+ -- Michael Zanetti <michael.zanetti@canonical.com> Mon, 27 Jun 2016 18:48:59 +0000
63+
1unity8 (8.12+16.10.20160620.3-0ubuntu1) yakkety; urgency=medium
64unity8 (8.12+16.10.20160620.3-0ubuntu1) yakkety; urgency=medium
2
65
3 [ Michael Terry, Michał Sawicz ]
66 [ Michael Terry, Michał Sawicz ]
debian/control+7−6
@@ -44,11 +44,14 @@ Build-Depends: cmake,
44 python3-all:any,
44 python3-all:any,
45 python3-setuptools,
45 python3-setuptools,
46 qml-module-qt-labs-folderlistmodel,
46 qml-module-qt-labs-folderlistmodel,
47+ qml-module-qtmultimedia (>= 5.4.1-1ubuntu19~overlay2),
47 qml-module-qtquick-layouts,
48 qml-module-qtquick-layouts,
48 qml-module-qtquick-xmllistmodel,
49 qml-module-qtquick-xmllistmodel,
49 qml-module-qtquick2,
50 qml-module-qtquick2,
50 qml-module-qtsysteminfo,
51 qml-module-qtsysteminfo,
51 qml-module-qttest,
52 qml-module-qttest,
53+ qml-module-ubuntu-components (>= 1.3.1845) | qml-module-ubuntu-components-gles (>= 1.3.1845),
54+ qml-module-ubuntu-web,
52 qt5-default,
55 qt5-default,
53 qtbase5-dev (>= 5.4),
56 qtbase5-dev (>= 5.4),
54 qtbase5-dev-tools,
57 qtbase5-dev-tools,
@@ -56,11 +59,8 @@ Build-Depends: cmake,
56 qtdeclarative5-dev (>= 5.4),
59 qtdeclarative5-dev (>= 5.4),
57 qtdeclarative5-dev-tools,
60 qtdeclarative5-dev-tools,
58 qtdeclarative5-private-dev (>= 5.4),
61 qtdeclarative5-private-dev (>= 5.4),
59− qtdeclarative5-qtmultimedia-plugin (>= 5.4.1-1ubuntu19~overlay2),
60 qtdeclarative5-ubuntu-content1,
62 qtdeclarative5-ubuntu-content1,
61 qtdeclarative5-ubuntu-settings-components (>= 0.7),
63 qtdeclarative5-ubuntu-settings-components (>= 0.7),
62− qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 1.3.1845) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 1.3.1845),
63− qtdeclarative5-ubuntu-web-plugin,
64 ttf-ubuntu-font-family,
64 ttf-ubuntu-font-family,
65Standards-Version: 3.9.4
65Standards-Version: 3.9.4
66Homepage: http://launchpad.net/unity
66Homepage: http://launchpad.net/unity
@@ -75,7 +75,7 @@ X-Ubuntu-Use-Langpack: yes
75Package: indicators-client
75Package: indicators-client
76Architecture: amd64 armhf i386
76Architecture: amd64 armhf i386
77Depends: qmenumodel-qml (>= 0.2.9),
77Depends: qmenumodel-qml (>= 0.2.9),
78− qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 1.3.1845) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 1.3.1845),
78+ qml-module-ubuntu-components (>= 1.3.1845) | qml-module-ubuntu-components-gles (>= 1.3.1845),
79 unity8 (= ${binary:Version}),
79 unity8 (= ${binary:Version}),
80 ${misc:Depends},
80 ${misc:Depends},
81 ${shlibs:Depends},
81 ${shlibs:Depends},
@@ -107,6 +107,7 @@ Depends: dmz-cursor-theme,
107 qml-module-qt-labs-folderlistmodel,
107 qml-module-qt-labs-folderlistmodel,
108 qml-module-qtquick-xmllistmodel,
108 qml-module-qtquick-xmllistmodel,
109 qml-module-qtsysteminfo,
109 qml-module-qtsysteminfo,
110+ qml-module-ubuntu-web,
110 qtdeclarative5-qtmir-plugin (>= 0.4.8),
111 qtdeclarative5-qtmir-plugin (>= 0.4.8),
111 qtdeclarative5-ubuntu-telephony0.1,
112 qtdeclarative5-ubuntu-telephony0.1,
112 qtdeclarative5-ubuntu-web-plugin,
113 qtdeclarative5-ubuntu-web-plugin,
@@ -132,9 +133,9 @@ Description: Unity 8 shell
132Package: unity8-common
133Package: unity8-common
133Architecture: all
134Architecture: all
134Depends: qml-module-qtquick-layouts,
135Depends: qml-module-qtquick-layouts,
136+ qml-module-ubuntu-components (>= 1.3.1845) | qml-module-ubuntu-components-gles (>= 1.3.1845),
137+ qml-module-ubuntu-thumbnailer0.1 | ubuntu-thumbnailer-impl,
135 qtdeclarative5-ubuntu-settings-components (>= 0.7),
138 qtdeclarative5-ubuntu-settings-components (>= 0.7),
136− qtdeclarative5-ubuntu-thumbnailer0.1 | ubuntu-thumbnailer-impl,
137− qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 1.3.1845) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 1.3.1845),
138 qtdeclarative5-unity-notifications-plugin (>= 0.1.2) | unity-notifications-impl,
139 qtdeclarative5-unity-notifications-plugin (>= 0.1.2) | unity-notifications-impl,
139 ubuntu-thumbnailer-impl-0,
140 ubuntu-thumbnailer-impl-0,
140 unity-application-impl-17,
141 unity-application-impl-17,
plugins/Cursor/CMakeLists.txt+4
@@ -3,6 +3,10 @@ add_subdirectory(3rd_party)
3include_directories(
3include_directories(
4 ${CMAKE_CURRENT_SOURCE_DIR}
4 ${CMAKE_CURRENT_SOURCE_DIR}
5 ${CMAKE_CURRENT_SOURCE_DIR}/3rd_party/xcursor
5 ${CMAKE_CURRENT_SOURCE_DIR}/3rd_party/xcursor
6+)
7+
8+include_directories(
9+ SYSTEM
6 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
10 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
7)
11)
8
12
plugins/Cursor/Cursor.qml+26−11
@@ -16,6 +16,7 @@
16
16
17import QtQuick 2.4
17import QtQuick 2.4
18import Cursor 1.1
18import Cursor 1.1
19+import Powerd 0.1
19
20
20MousePointer {
21MousePointer {
21 id: mousePointer
22 id: mousePointer
@@ -26,19 +27,33 @@ MousePointer {
26 cursorName: mousePointer.cursorName
27 cursorName: mousePointer.cursorName
27 }
28 }
28
29
30+ Loader {
31+ active: mousePointer.visible && imageInfo.frameCount > 1
29− AnimatedSprite {
32+ sourceComponent: AnimatedSprite {
30− x: -imageInfo.hotspot.x
33+ x: -imageInfo.hotspot.x
31− y: -imageInfo.hotspot.y
34+ y: -imageInfo.hotspot.y
32− source: "image://cursor/" + mousePointer.themeName + "/" + mousePointer.cursorName
35+ source: "image://cursor/" + mousePointer.themeName + "/" + mousePointer.cursorName
33
36
34− interpolate: false
37+ interpolate: false
35
38
36− width: imageInfo.frameWidth
39+ width: imageInfo.frameWidth
37− height: imageInfo.frameHeight
40+ height: imageInfo.frameHeight
38
41
39− frameCount: imageInfo.frameCount
42+ frameCount: imageInfo.frameCount
40− frameDuration: imageInfo.frameDuration
43+ frameDuration: imageInfo.frameDuration
41− frameWidth: imageInfo.frameWidth
44+ frameWidth: imageInfo.frameWidth
42− frameHeight: imageInfo.frameHeight
45+ frameHeight: imageInfo.frameHeight
46+
47+ running: Powerd.status === Powerd.On
48+ }
49+ }
50+
51+ Loader {
52+ active: mousePointer.visible && imageInfo.frameCount === 1
53+ sourceComponent: Image {
54+ x: -imageInfo.hotspot.x
55+ y: -imageInfo.hotspot.y
56+ source: "image://cursor/" + mousePointer.themeName + "/" + mousePointer.cursorName
57+ }
43 }
58 }
44}
59}
plugins/Cursor/MousePointer.cpp+5−5
@@ -52,17 +52,17 @@ void MousePointer::handleMouseEvent(ulong timestamp, QPointF movement, Qt::Mouse
52 if (newX < 0) {
52 if (newX < 0) {
53 Q_EMIT pushedLeftBoundary(qAbs(newX), buttons);
53 Q_EMIT pushedLeftBoundary(qAbs(newX), buttons);
54 newX = 0;
54 newX = 0;
55− } else if (newX > parentItem()->width()) {
55+ } else if (newX >= parentItem()->width()) {
56− Q_EMIT pushedRightBoundary(newX - parentItem()->width(), buttons);
56+ Q_EMIT pushedRightBoundary(newX - (parentItem()->width() - 1), buttons);
57− newX = parentItem()->width();
57+ newX = parentItem()->width() - 1;
58 }
58 }
59 setX(newX);
59 setX(newX);
60
60
61 qreal newY = y() + appliedMovement.y();
61 qreal newY = y() + appliedMovement.y();
62 if (newY < 0) {
62 if (newY < 0) {
63 newY = 0;
63 newY = 0;
64− } else if (newY > parentItem()->height()) {
64+ } else if (newY >= parentItem()->height()) {
65− newY = parentItem()->height();
65+ newY = parentItem()->height() - 1;
66 }
66 }
67 setY(newY);
67 setY(newY);
68
68
plugins/Dash/CMakeLists.txt+4
@@ -10,6 +10,10 @@ endif()
10include_directories(
10include_directories(
11 ${CMAKE_CURRENT_SOURCE_DIR}
11 ${CMAKE_CURRENT_SOURCE_DIR}
12 ${CMAKE_CURRENT_BINARY_DIR}
12 ${CMAKE_CURRENT_BINARY_DIR}
13+)
14+
15+include_directories(
16+ SYSTEM
13 ${Qt5Qml_PRIVATE_INCLUDE_DIRS}
17 ${Qt5Qml_PRIVATE_INCLUDE_DIRS}
14 ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
18 ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
15 ${Qt5V8_PRIVATE_INCLUDE_DIR}
19 ${Qt5V8_PRIVATE_INCLUDE_DIR}
plugins/Dash/abstractdashview.h+1−3
@@ -21,11 +21,9 @@
21
21
22class QAbstractItemModel;
22class QAbstractItemModel;
23class QQmlComponent;
23class QQmlComponent;
24−#pragma GCC diagnostic push
24+
25−#pragma GCC diagnostic ignored "-pedantic"
26#include <private/qqmldelegatemodel_p.h>
25#include <private/qqmldelegatemodel_p.h>
27#include <qqmlinfo.h>
26#include <qqmlinfo.h>
28−#pragma GCC diagnostic pop
29
27
30class AbstractDashView : public QQuickItem
28class AbstractDashView : public QQuickItem
31{
29{
plugins/Dash/graphics/music_progress_bg.png

Binary file not shown.

plugins/Dash/horizontaljournal.cpp+1−4
@@ -26,12 +26,9 @@
26#include "horizontaljournal.h"
26#include "horizontaljournal.h"
27
27
28#include <qqmlengine.h>
28#include <qqmlengine.h>
29−#pragma GCC diagnostic push
30−#pragma GCC diagnostic ignored "-pedantic"
31−#include <private/qqmldelegatemodel_p.h>
32#include <qqmlinfo.h>
29#include <qqmlinfo.h>
30+#include <private/qqmldelegatemodel_p.h>
33#include <private/qquickitem_p.h>
31#include <private/qquickitem_p.h>
34−#pragma GCC diagnostic pop
35
32
36HorizontalJournal::HorizontalJournal()
33HorizontalJournal::HorizontalJournal()
37 : m_firstVisibleIndex(-1)
34 : m_firstVisibleIndex(-1)
plugins/Dash/listviewwithpageheader.cpp+8−4
@@ -95,14 +95,11 @@
95#include <QDebug>
95#include <QDebug>
96#include <qqmlinfo.h>
96#include <qqmlinfo.h>
97#include <qqmlengine.h>
97#include <qqmlengine.h>
98−#pragma GCC diagnostic push
99−#pragma GCC diagnostic ignored "-pedantic"
100#include <private/qqmlcontext_p.h>
98#include <private/qqmlcontext_p.h>
101#include <private/qqmldelegatemodel_p.h>
99#include <private/qqmldelegatemodel_p.h>
102#include <private/qqmlglobal_p.h>
100#include <private/qqmlglobal_p.h>
103#include <private/qquickitem_p.h>
101#include <private/qquickitem_p.h>
104#include <private/qquickanimation_p.h>
102#include <private/qquickanimation_p.h>
105−#pragma GCC diagnostic pop
106// #include <private/qquickrectangle_p.h>
103// #include <private/qquickrectangle_p.h>
107
104
108qreal ListViewWithPageHeader::ListItem::height() const
105qreal ListViewWithPageHeader::ListItem::height() const
@@ -256,6 +253,7 @@ void ListViewWithPageHeader::setHeader(QQuickItem *headerItem)
256 oldHeaderHeight = m_headerItem->height();
253 oldHeaderHeight = m_headerItem->height();
257 oldHeaderY = m_headerItem->y();
254 oldHeaderY = m_headerItem->y();
258 m_headerItem->setParentItem(nullptr);
255 m_headerItem->setParentItem(nullptr);
256+ QQuickItemPrivate::get(m_headerItem)->removeItemChangeListener(this, QQuickItemPrivate::ImplicitHeight);
259 }
257 }
260 m_headerItem = headerItem;
258 m_headerItem = headerItem;
261 if (m_headerItem) {
259 if (m_headerItem) {
@@ -702,7 +700,9 @@ void ListViewWithPageHeader::reallyReleaseItem(ListItem *listItem)
702 if (flags & QQmlDelegateModel::Destroyed) {
700 if (flags & QQmlDelegateModel::Destroyed) {
703 item->setParentItem(nullptr);
701 item->setParentItem(nullptr);
704 }
702 }
703+ if (listItem->sectionItem()) {
705− listItem->sectionItem()->deleteLater();
704+ listItem->sectionItem()->deleteLater();
705+ }
706 delete listItem;
706 delete listItem;
707}
707}
708
708
@@ -1334,6 +1334,10 @@ void ListViewWithPageHeader::layout()
1334 }
1334 }
1335 }
1335 }
1336 }
1336 }
1337+ if (m_headerItem) {
1338+ const bool cullHeader = m_headerItem->y() + m_headerItem->height() < contentY();
1339+ QQuickItemPrivate::get(m_headerItem)->setCulled(cullHeader);
1340+ }
1337 m_inLayout = false;
1341 m_inLayout = false;
1338}
1342}
1339
1343
plugins/Dash/organicgrid.cpp−3
@@ -16,10 +16,7 @@
16
16
17#include "organicgrid.h"
17#include "organicgrid.h"
18
18
19−#pragma GCC diagnostic push
20−#pragma GCC diagnostic ignored "-pedantic"
21#include <private/qquickitem_p.h>
19#include <private/qquickitem_p.h>
22−#pragma GCC diagnostic pop
23
20
24OrganicGrid::OrganicGrid()
21OrganicGrid::OrganicGrid()
25 : m_firstVisibleIndex(-1)
22 : m_firstVisibleIndex(-1)
plugins/Dash/verticaljournal.cpp−3
@@ -27,10 +27,7 @@
27 */
27 */
28#include "verticaljournal.h"
28#include "verticaljournal.h"
29
29
30−#pragma GCC diagnostic push
31−#pragma GCC diagnostic ignored "-pedantic"
32#include <private/qquickitem_p.h>
30#include <private/qquickitem_p.h>
33−#pragma GCC diagnostic pop
34
31
35VerticalJournal::VerticalJournal()
32VerticalJournal::VerticalJournal()
36 : m_columnWidth(0)
33 : m_columnWidth(0)
plugins/Lights/CMakeLists.txt+1
@@ -2,6 +2,7 @@ pkg_check_modules(ANDROID_HEADERS REQUIRED android-headers)
2pkg_check_modules(ANDROID_HARDWARE REQUIRED libhardware)
2pkg_check_modules(ANDROID_HARDWARE REQUIRED libhardware)
3
3
4include_directories(
4include_directories(
5+ SYSTEM
5 ${ANDROID_HEADERS_INCLUDE_DIRS}
6 ${ANDROID_HEADERS_INCLUDE_DIRS}
6)
7)
7
8
plugins/Lights/android-hardware.h−4
@@ -15,9 +15,5 @@
15 *
15 *
16 */
16 */
17
17
18−// The purpose of this header wrapper is just to avoid having warnings being reported on
19−// code from external header files, as we can't do anything about them.
20−#pragma GCC system_header
21−
22#include <hardware/hardware.h>
18#include <hardware/hardware.h>
23#include <hardware/lights.h>
19#include <hardware/lights.h>
plugins/Ubuntu/DownloadDaemonListener/CMakeLists.txt−1
@@ -1,7 +1,6 @@
1include_directories(
1include_directories(
2 ${CMAKE_CURRENT_SOURCE_DIR}
2 ${CMAKE_CURRENT_SOURCE_DIR}
3 ${CMAKE_CURRENT_BINARY_DIR}
3 ${CMAKE_CURRENT_BINARY_DIR}
4− ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
5)
4)
6
5
7set(DOWNLOADDAEMONLISTENER_SOURCES
6set(DOWNLOADDAEMONLISTENER_SOURCES
plugins/Ubuntu/Gestures/CMakeLists.txt+5−1
@@ -24,7 +24,10 @@ qt5_use_modules(UbuntuGesturesQml Core Quick)
24# So that Foo.cpp can #include "Foo.moc"
24# So that Foo.cpp can #include "Foo.moc"
25include_directories(${CMAKE_CURRENT_BINARY_DIR})
25include_directories(${CMAKE_CURRENT_BINARY_DIR})
26
26
27+include_directories(
28+ SYSTEM
27−include_directories(${UBUNTUGESTURES_INCLUDE_DIRS})
29+ ${UBUNTUGESTURES_INCLUDE_DIRS}
30+)
28
31
29# There's no cmake var for v8 include path :-/ so create one
32# There's no cmake var for v8 include path :-/ so create one
30LIST(GET Qt5Core_INCLUDE_DIRS 0 QtCoreDir0)
33LIST(GET Qt5Core_INCLUDE_DIRS 0 QtCoreDir0)
@@ -32,6 +35,7 @@ SET(Qt5V8_PRIVATE_INCLUDE_DIR ${QtCoreDir0}/QtV8/${Qt5Core_VERSION_STRING}/QtV8)
32
35
33# DANGER! DANGER! Using Qt's private API!
36# DANGER! DANGER! Using Qt's private API!
34include_directories(
37include_directories(
38+ SYSTEM
35 ${Qt5Qml_PRIVATE_INCLUDE_DIRS}
39 ${Qt5Qml_PRIVATE_INCLUDE_DIRS}
36 ${Qt5Quick_INCLUDE_DIRS}
40 ${Qt5Quick_INCLUDE_DIRS}
37 ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
41 ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
plugins/Ubuntu/Gestures/TouchDispatcher.cpp−4
@@ -19,11 +19,7 @@
19#include <QGuiApplication>
19#include <QGuiApplication>
20#include <QScopedPointer>
20#include <QScopedPointer>
21#include <QStyleHints>
21#include <QStyleHints>
22−
23−#pragma GCC diagnostic push
24−#pragma GCC diagnostic ignored "-pedantic"
25#include <private/qquickitem_p.h>
22#include <private/qquickitem_p.h>
26−#pragma GCC diagnostic pop
27
23
28#define TOUCHDISPATCHER_DEBUG 0
24#define TOUCHDISPATCHER_DEBUG 0
29
25
plugins/Unity/Indicators/CMakeLists.txt+4
@@ -6,6 +6,10 @@ find_package(Qt5Quick REQUIRED)
6include_directories(
6include_directories(
7 ${CMAKE_CURRENT_SOURCE_DIR}
7 ${CMAKE_CURRENT_SOURCE_DIR}
8 ${CMAKE_CURRENT_BINARY_DIR}
8 ${CMAKE_CURRENT_BINARY_DIR}
9+)
10+
11+include_directories(
12+ SYSTEM
9 ${GLIB_INCLUDE_DIRS}
13 ${GLIB_INCLUDE_DIRS}
10 ${GIO_INCLUDE_DIRS}
14 ${GIO_INCLUDE_DIRS}
11 ${QMENUMODEL_INCLUDE_DIRS}
15 ${QMENUMODEL_INCLUDE_DIRS}
plugins/Unity/Session/CMakeLists.txt+2
@@ -6,6 +6,8 @@ include_directories(
6 ${libunity8-private_SOURCE_DIR}
6 ${libunity8-private_SOURCE_DIR}
7)
7)
8
8
9+add_definitions(-DSM_BUSNAME=systemBus)
10+
9set(QMLSESSIONPLUGIN_SRC
11set(QMLSESSIONPLUGIN_SRC
10 plugin.cpp
12 plugin.cpp
11 dbusunitysessionservice.cpp
13 dbusunitysessionservice.cpp
plugins/Unity/Session/dbusunitysessionservice.cpp+50−18
@@ -64,19 +64,19 @@ public:
64 QStringLiteral("GetSessionByPID"));
64 QStringLiteral("GetSessionByPID"));
65 msg << (quint32) getpid();
65 msg << (quint32) getpid();
66
66
67− QDBusReply<QDBusObjectPath> reply = QDBusConnection::systemBus().call(msg);
67+ QDBusReply<QDBusObjectPath> reply = QDBusConnection::SM_BUSNAME().call(msg);
68 if (reply.isValid()) {
68 if (reply.isValid()) {
69 logindSessionPath = reply.value().path();
69 logindSessionPath = reply.value().path();
70
70
71 // start watching the Active property
71 // start watching the Active property
72− QDBusConnection::systemBus().connect(LOGIN1_SERVICE, logindSessionPath, QStringLiteral("org.freedesktop.DBus.Properties"), QStringLiteral("PropertiesChanged"),
72+ QDBusConnection::SM_BUSNAME().connect(LOGIN1_SERVICE, logindSessionPath, QStringLiteral("org.freedesktop.DBus.Properties"), QStringLiteral("PropertiesChanged"),
73− this, SLOT(onPropertiesChanged(QString,QVariantMap,QStringList)));
73+ this, SLOT(onPropertiesChanged(QString,QVariantMap,QStringList)));
74
74
75 setupSystemdInhibition();
75 setupSystemdInhibition();
76
76
77 // re-enable the inhibition upon resume from sleep
77 // re-enable the inhibition upon resume from sleep
78− QDBusConnection::systemBus().connect(LOGIN1_SERVICE, LOGIN1_PATH, LOGIN1_IFACE, QStringLiteral("PrepareForSleep"),
78+ QDBusConnection::SM_BUSNAME().connect(LOGIN1_SERVICE, LOGIN1_PATH, LOGIN1_IFACE, QStringLiteral("PrepareForSleep"),
79− this, SLOT(onResuming(bool)));
79+ this, SLOT(onResuming(bool)));
80 } else {
80 } else {
81 qWarning() << "Failed to get logind session path" << reply.error().message();
81 qWarning() << "Failed to get logind session path" << reply.error().message();
82 }
82 }
@@ -96,7 +96,7 @@ public:
96 msg << "Unity8 handles power events"; // why
96 msg << "Unity8 handles power events"; // why
97 msg << "block"; // mode
97 msg << "block"; // mode
98
98
99− QDBusPendingCall pendingCall = QDBusConnection::systemBus().asyncCall(msg);
99+ QDBusPendingCall pendingCall = QDBusConnection::SM_BUSNAME().asyncCall(msg);
100 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall, this);
100 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall, this);
101 connect(watcher, &QDBusPendingCallWatcher::finished,
101 connect(watcher, &QDBusPendingCallWatcher::finished,
102 this, [this](QDBusPendingCallWatcher* watcher) {
102 this, [this](QDBusPendingCallWatcher* watcher) {
@@ -114,7 +114,7 @@ public:
114 bool checkLogin1Call(const QString &method) const
114 bool checkLogin1Call(const QString &method) const
115 {
115 {
116 QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE, LOGIN1_PATH, LOGIN1_IFACE, method);
116 QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE, LOGIN1_PATH, LOGIN1_IFACE, method);
117− QDBusReply<QString> reply = QDBusConnection::systemBus().call(msg);
117+ QDBusReply<QString> reply = QDBusConnection::SM_BUSNAME().call(msg);
118 return reply.isValid() && (reply == QStringLiteral("yes") || reply == QStringLiteral("challenge"));
118 return reply.isValid() && (reply == QStringLiteral("yes") || reply == QStringLiteral("challenge"));
119 }
119 }
120
120
@@ -125,7 +125,7 @@ public:
125 LOGIN1_IFACE,
125 LOGIN1_IFACE,
126 method);
126 method);
127 msg.setArguments(args);
127 msg.setArguments(args);
128− QDBusConnection::systemBus().asyncCall(msg);
128+ QDBusConnection::SM_BUSNAME().asyncCall(msg);
129 }
129 }
130
130
131 void setActive(bool active)
131 void setActive(bool active)
@@ -157,7 +157,7 @@ public:
157 msg << LOGIN1_SESSION_IFACE;
157 msg << LOGIN1_SESSION_IFACE;
158 msg << ACTIVE_KEY;
158 msg << ACTIVE_KEY;
159
159
160− QDBusPendingCall pendingCall = QDBusConnection::systemBus().asyncCall(msg);
160+ QDBusPendingCall pendingCall = QDBusConnection::SM_BUSNAME().asyncCall(msg);
161 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall, this);
161 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall, this);
162 connect(watcher, &QDBusPendingCallWatcher::finished,
162 connect(watcher, &QDBusPendingCallWatcher::finished,
163 this, [this](QDBusPendingCallWatcher* watcher) {
163 this, [this](QDBusPendingCallWatcher* watcher) {
@@ -191,7 +191,7 @@ public:
191 msg << LOGIN1_SESSION_IFACE;
191 msg << LOGIN1_SESSION_IFACE;
192 msg << IDLE_SINCE_KEY;
192 msg << IDLE_SINCE_KEY;
193
193
194− QDBusReply<QVariant> reply = QDBusConnection::systemBus().call(msg);
194+ QDBusReply<QVariant> reply = QDBusConnection::SM_BUSNAME().call(msg);
195 if (reply.isValid()) {
195 if (reply.isValid()) {
196 return reply.value().value<quint64>();
196 return reply.value().value<quint64>();
197 } else {
197 } else {
@@ -208,7 +208,7 @@ public:
208 LOGIN1_SESSION_IFACE,
208 LOGIN1_SESSION_IFACE,
209 QStringLiteral("SetIdleHint"));
209 QStringLiteral("SetIdleHint"));
210 msg << idle;
210 msg << idle;
211− QDBusConnection::systemBus().asyncCall(msg);
211+ QDBusConnection::SM_BUSNAME().asyncCall(msg);
212 }
212 }
213
213
214private Q_SLOTS:
214private Q_SLOTS:
@@ -243,11 +243,11 @@ DBusUnitySessionService::DBusUnitySessionService()
243 : UnityDBusObject(QStringLiteral("/com/canonical/Unity/Session"), QStringLiteral("com.canonical.Unity"))
243 : UnityDBusObject(QStringLiteral("/com/canonical/Unity/Session"), QStringLiteral("com.canonical.Unity"))
244{
244{
245 if (!d->logindSessionPath.isEmpty()) {
245 if (!d->logindSessionPath.isEmpty()) {
246− // connect our Lock() slot to the logind's session Lock() signal
246+ // connect our PromptLock() slot to the logind's session Lock() signal
247− QDBusConnection::systemBus().connect(LOGIN1_SERVICE, d->logindSessionPath, LOGIN1_SESSION_IFACE, QStringLiteral("Lock"), this, SLOT(Lock()));
247+ QDBusConnection::SM_BUSNAME().connect(LOGIN1_SERVICE, d->logindSessionPath, LOGIN1_SESSION_IFACE, QStringLiteral("Lock"), this, SLOT(PromptLock()));
248 // ... and our Unlocked() signal to the logind's session Unlock() signal
248 // ... and our Unlocked() signal to the logind's session Unlock() signal
249 // (lightdm handles the unlocking by calling logind's Unlock method which in turn emits this signal we connect to)
249 // (lightdm handles the unlocking by calling logind's Unlock method which in turn emits this signal we connect to)
250− QDBusConnection::systemBus().connect(LOGIN1_SERVICE, d->logindSessionPath, LOGIN1_SESSION_IFACE, QStringLiteral("Unlock"), this, SIGNAL(Unlocked()));
250+ QDBusConnection::SM_BUSNAME().connect(LOGIN1_SERVICE, d->logindSessionPath, LOGIN1_SESSION_IFACE, QStringLiteral("Unlock"), this, SLOT(doUnlock()));
251 connect(d, &DBusUnitySessionServicePrivate::prepareForSleep, this, &DBusUnitySessionService::PromptLock);
251 connect(d, &DBusUnitySessionServicePrivate::prepareForSleep, this, &DBusUnitySessionService::PromptLock);
252 } else {
252 } else {
253 qWarning() << "Failed to connect to logind's session Lock/Unlock signals";
253 qWarning() << "Failed to connect to logind's session Lock/Unlock signals";
@@ -332,15 +332,41 @@ QString DBusUnitySessionService::HostName() const
332
332
333void DBusUnitySessionService::PromptLock()
333void DBusUnitySessionService::PromptLock()
334{
334{
335+ // Prompt as in quick. No locking animation needed. Usually used by
336+ // indicator-session in combination with a switch to greeter or other
337+ // user session.
335 Q_EMIT LockRequested();
338 Q_EMIT LockRequested();
336 Q_EMIT lockRequested();
339 Q_EMIT lockRequested();
337}
340}
338
341
339void DBusUnitySessionService::Lock()
342void DBusUnitySessionService::Lock()
340{
343{
341− // signal u8 to show the lockscreen/greeter
344+ // Normal lock (with animation, as compared to PromptLock above). Usually
345+ // used by indicator-session to lock the session in place.
346+ //
347+ // FIXME: We also -- as a bit of a hack around indicator-session not fully
348+ // supporting a phone profile -- switch to greeter here. The unity7 flow is
349+ // that the user chooses "Lock/Switch" from the indicator, and then can go
350+ // to greeter by selecting "Switch" again from the indicator, which is now
351+ // exposed by the desktop_lockscreen profile. But since in unity8, we try
352+ // to expose most things all the time, we don't use the separate lockscreen
353+ // profile. Instead, we just go directly to the greeter the first time
354+ // a user presses "Lock/Switch". This isn't what this DBus call is
355+ // supposed to do, but we can live with it for now.
356+ //
357+ // Here's a bug about indicator-session growing a converged Touch profile:
358+ // https://launchpad.net/bugs/1557716
359+ //
360+ // We only do this here in the animated-lock call because that's the only
361+ // time the indicator locks without also asking the display manager to
362+ // switch sessions on us. And since we are switching screens, we also
363+ // don't bother respecting the animate request, simply doing a PromptLock.
342 PromptLock();
364 PromptLock();
365+ switchToGreeter();
366+}
343
367
368+void DBusUnitySessionService::switchToGreeter()
369+{
344 // lock the session using the org.freedesktop.DisplayManager system DBUS service
370 // lock the session using the org.freedesktop.DisplayManager system DBUS service
345 const QString sessionPath = QString::fromLocal8Bit(qgetenv("XDG_SESSION_PATH"));
371 const QString sessionPath = QString::fromLocal8Bit(qgetenv("XDG_SESSION_PATH"));
346 QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.DisplayManager"),
372 QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.DisplayManager"),
@@ -348,7 +374,7 @@ void DBusUnitySessionService::Lock()
348 QStringLiteral("org.freedesktop.DisplayManager.Session"),
374 QStringLiteral("org.freedesktop.DisplayManager.Session"),
349 QStringLiteral("Lock"));
375 QStringLiteral("Lock"));
350
376
351− QDBusPendingCall pendingCall = QDBusConnection::systemBus().asyncCall(msg);
377+ QDBusPendingCall pendingCall = QDBusConnection::SM_BUSNAME().asyncCall(msg);
352 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall, this);
378 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall, this);
353 connect(watcher, &QDBusPendingCallWatcher::finished,
379 connect(watcher, &QDBusPendingCallWatcher::finished,
354 this, [this](QDBusPendingCallWatcher* watcher) {
380 this, [this](QDBusPendingCallWatcher* watcher) {
@@ -365,6 +391,12 @@ void DBusUnitySessionService::Lock()
365 });
391 });
366}
392}
367
393
394+void DBusUnitySessionService::doUnlock()
395+{
396+ Q_EMIT Unlocked();
397+ Q_EMIT unlocked();
398+}
399+
368bool DBusUnitySessionService::IsLocked() const
400bool DBusUnitySessionService::IsLocked() const
369{
401{
370 return !d->isSessionActive;
402 return !d->isSessionActive;
@@ -486,7 +518,7 @@ void DBusGnomeScreensaverWrapper::SetActive(bool lock)
486
518
487void DBusGnomeScreensaverWrapper::Lock()
519void DBusGnomeScreensaverWrapper::Lock()
488{
520{
489− performAsyncUnityCall(QStringLiteral("Lock"));
521+ performAsyncUnityCall(QStringLiteral("PromptLock"));
490}
522}
491
523
492quint32 DBusGnomeScreensaverWrapper::GetActiveTime() const
524quint32 DBusGnomeScreensaverWrapper::GetActiveTime() const
@@ -523,7 +555,7 @@ bool DBusScreensaverWrapper::SetActive(bool lock)
523
555
524void DBusScreensaverWrapper::Lock()
556void DBusScreensaverWrapper::Lock()
525{
557{
526− performAsyncUnityCall(QStringLiteral("Lock"));
558+ performAsyncUnityCall(QStringLiteral("PromptLock"));
527}
559}
528
560
529quint32 DBusScreensaverWrapper::GetActiveTime() const
561quint32 DBusScreensaverWrapper::GetActiveTime() const
plugins/Unity/Session/dbusunitysessionservice.h+7
@@ -106,6 +106,7 @@ Q_SIGNALS:
106 * Emitted after the session has been unlocked.
106 * Emitted after the session has been unlocked.
107 */
107 */
108 Q_SCRIPTABLE void Unlocked();
108 Q_SCRIPTABLE void Unlocked();
109+ void unlocked();
109
110
110public Q_SLOTS:
111public Q_SLOTS:
111 /**
112 /**
@@ -258,6 +259,12 @@ public Q_SLOTS:
258 * @return whether the session is currently locked
259 * @return whether the session is currently locked
259 */
260 */
260 Q_SCRIPTABLE bool IsLocked() const;
261 Q_SCRIPTABLE bool IsLocked() const;
262+
263+private Q_SLOTS:
264+ void doUnlock();
265+
266+private:
267+ void switchToGreeter();
261};
268};
262
269
263class DBusGnomeSessionManagerWrapper : public UnityDBusObject
270class DBusGnomeSessionManagerWrapper : public UnityDBusObject
plugins/Utils/CMakeLists.txt+5
@@ -1,6 +1,10 @@
1include_directories(
1include_directories(
2 ${CMAKE_CURRENT_SOURCE_DIR}
2 ${CMAKE_CURRENT_SOURCE_DIR}
3 ${CMAKE_CURRENT_BINARY_DIR}
3 ${CMAKE_CURRENT_BINARY_DIR}
4+)
5+
6+include_directories(
7+ SYSTEM
4 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
8 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
5 ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
9 ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
6 ${GIO_INCLUDE_DIRS}
10 ${GIO_INCLUDE_DIRS}
@@ -9,6 +13,7 @@ include_directories(
9set(QMLPLUGIN_SRC
13set(QMLPLUGIN_SRC
10 activefocuslogger.cpp
14 activefocuslogger.cpp
11 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
15 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
16+ ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
12 applicationsfiltermodel.cpp
17 applicationsfiltermodel.cpp
13 constants.cpp
18 constants.cpp
14 WindowInputMonitor.cpp
19 WindowInputMonitor.cpp
plugins/Utils/globalfunctions.cpp+1−4
@@ -16,11 +16,8 @@
16
16
17#include "globalfunctions.h"
17#include "globalfunctions.h"
18
18
19−#pragma GCC diagnostic push
20−#pragma GCC diagnostic ignored "-pedantic"
21−#include <private/qquickitem_p.h>
22−#pragma GCC diagnostic pop
23#include <QQmlEngine>
19#include <QQmlEngine>
20+#include <private/qquickitem_p.h>
24
21
25GlobalFunctions::GlobalFunctions(QObject *parent)
22GlobalFunctions::GlobalFunctions(QObject *parent)
26 : QObject(parent)
23 : QObject(parent)
plugins/Utils/windowstatestorage.cpp+2−2
@@ -95,7 +95,7 @@ void WindowStateStorage::saveStage(const QString &appId, int stage)
95 saveValue(queryString);
95 saveValue(queryString);
96}
96}
97
97
98−int WindowStateStorage::getStage(const QString &appId) const
98+int WindowStateStorage::getStage(const QString &appId, int defaultValue) const
99{
99{
100 const QString queryString = QStringLiteral("SELECT * FROM stage WHERE appId = '%1';")
100 const QString queryString = QStringLiteral("SELECT * FROM stage WHERE appId = '%1';")
101 .arg(sanitiseString(appId));
101 .arg(sanitiseString(appId));
@@ -103,7 +103,7 @@ int WindowStateStorage::getStage(const QString &appId) const
103 QSqlQuery query = getValue(queryString);
103 QSqlQuery query = getValue(queryString);
104
104
105 if (!query.first()) {
105 if (!query.first()) {
106− return unity::shell::application::ApplicationInfoInterface::MainStage;
106+ return defaultValue;
107 }
107 }
108 return query.value("stage").toInt();
108 return query.value("stage").toInt();
109}
109}
plugins/Utils/windowstatestorage.h+1−1
@@ -49,7 +49,7 @@ public:
49 Q_INVOKABLE QRect getGeometry(const QString &windowId, const QRect defaultValue) const;
49 Q_INVOKABLE QRect getGeometry(const QString &windowId, const QRect defaultValue) const;
50
50
51 Q_INVOKABLE void saveStage(const QString &appId, int stage);
51 Q_INVOKABLE void saveStage(const QString &appId, int stage);
52− Q_INVOKABLE int getStage(const QString &appId) const;
52+ Q_INVOKABLE int getStage(const QString &appId, int defaultValue) const;
53
53
54private:
54private:
55 void initdb();
55 void initdb();
plugins/WindowManager/CMakeLists.txt+1
@@ -4,6 +4,7 @@ set(WINDOWMANAGER_SRC
4 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
4 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
5 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/Mir.h
5 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/Mir.h
6 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceInterface.h
6 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceInterface.h
7+ ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceListInterface.h
7 )
8 )
8
9
9add_library(windowmanager-qml SHARED ${WINDOWMANAGER_SRC})
10add_library(windowmanager-qml SHARED ${WINDOWMANAGER_SRC})
po/sk.po+31−31
@@ -8,15 +8,15 @@ msgstr ""
8"Project-Id-Version: unity8\n"
8"Project-Id-Version: unity8\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2016-06-20 20:25+0000\n"
10"POT-Creation-Date: 2016-06-20 20:25+0000\n"
11−"PO-Revision-Date: 2016-05-02 10:43+0000\n"
11+"PO-Revision-Date: 2016-06-27 07:37+0000\n"
12"Last-Translator: P_E_T_O <Unknown>\n"
12"Last-Translator: P_E_T_O <Unknown>\n"
13"Language-Team: Slovak <sk@li.org>\n"
13"Language-Team: Slovak <sk@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=3; plural=(n==1) ? 1 : (n>=2 && n<=4) ? 2 : 0;\n"
17"Plural-Forms: nplurals=3; plural=(n==1) ? 1 : (n>=2 && n<=4) ? 2 : 0;\n"
18−"X-Launchpad-Export-Date: 2016-06-26 06:01+0000\n"
18+"X-Launchpad-Export-Date: 2016-06-28 05:41+0000\n"
19−"X-Generator: Launchpad (build 18111)\n"
19+"X-Generator: Launchpad (build 18115)\n"
20"Language: sk\n"
20"Language: sk\n"
21
21
22#: plugins/IntegratedLightDM/Greeter.cpp:112
22#: plugins/IntegratedLightDM/Greeter.cpp:112
@@ -127,59 +127,59 @@ msgstr "Reštartovať"
127
127
128#: qml/Components/KeyboardShortcutsOverlay.qml:41
128#: qml/Components/KeyboardShortcutsOverlay.qml:41
129msgid "Keyboard Shortcuts"
129msgid "Keyboard Shortcuts"
130−msgstr ""
130+msgstr "Klávesové skratky"
131
131
132#: qml/Components/KeyboardShortcutsOverlay.qml:55
132#: qml/Components/KeyboardShortcutsOverlay.qml:55
133msgid "Unity 8"
133msgid "Unity 8"
134−msgstr ""
134+msgstr "Unity 8"
135
135
136#: qml/Components/KeyboardShortcutsOverlay.qml:62
136#: qml/Components/KeyboardShortcutsOverlay.qml:62
137msgid "PrtScr"
137msgid "PrtScr"
138−msgstr ""
138+msgstr "PrtScr"
139
139
140#: qml/Components/KeyboardShortcutsOverlay.qml:67
140#: qml/Components/KeyboardShortcutsOverlay.qml:67
141msgid "Takes a screenshot."
141msgid "Takes a screenshot."
142−msgstr ""
142+msgstr "Vytvorí snímku obrazovky."
143
143
144#: qml/Components/KeyboardShortcutsOverlay.qml:75
144#: qml/Components/KeyboardShortcutsOverlay.qml:75
145msgid "Alt + PrtScr"
145msgid "Alt + PrtScr"
146−msgstr ""
146+msgstr "Alt + PrtScr"
147
147
148#: qml/Components/KeyboardShortcutsOverlay.qml:80
148#: qml/Components/KeyboardShortcutsOverlay.qml:80
149msgid "Takes a screenshot of a window."
149msgid "Takes a screenshot of a window."
150−msgstr ""
150+msgstr "Vytvorí snímku okna."
151
151
152#: qml/Components/KeyboardShortcutsOverlay.qml:88
152#: qml/Components/KeyboardShortcutsOverlay.qml:88
153msgid "Super + Space"
153msgid "Super + Space"
154−msgstr ""
154+msgstr "Super + Space"
155
155
156#: qml/Components/KeyboardShortcutsOverlay.qml:93
156#: qml/Components/KeyboardShortcutsOverlay.qml:93
157msgid "Switches to next keyboard layout."
157msgid "Switches to next keyboard layout."
158−msgstr ""
158+msgstr "Prepne na ďalšie rozloženie klávesnice."
159
159
160#: qml/Components/KeyboardShortcutsOverlay.qml:101
160#: qml/Components/KeyboardShortcutsOverlay.qml:101
161msgid "Super + Shift + Space"
161msgid "Super + Shift + Space"
162−msgstr ""
162+msgstr "Super + Shift + Space"
163
163
164#: qml/Components/KeyboardShortcutsOverlay.qml:106
164#: qml/Components/KeyboardShortcutsOverlay.qml:106
165msgid "Switches to previous keyboard layout."
165msgid "Switches to previous keyboard layout."
166−msgstr ""
166+msgstr "Prepne na predošlé rozloženie klávesnice."
167
167
168#: qml/Components/KeyboardShortcutsOverlay.qml:118
168#: qml/Components/KeyboardShortcutsOverlay.qml:118
169msgid "Launcher"
169msgid "Launcher"
170−msgstr ""
170+msgstr "Launcher"
171
171
172#: qml/Components/KeyboardShortcutsOverlay.qml:125
172#: qml/Components/KeyboardShortcutsOverlay.qml:125
173msgid "Super (Hold)"
173msgid "Super (Hold)"
174−msgstr ""
174+msgstr "Super (Držať)"
175
175
176#: qml/Components/KeyboardShortcutsOverlay.qml:130
176#: qml/Components/KeyboardShortcutsOverlay.qml:130
177msgid "Opens the launcher, displays shortcuts."
177msgid "Opens the launcher, displays shortcuts."
178−msgstr ""
178+msgstr "Otvorí launcher, zobrazí skratky."
179
179
180#: qml/Components/KeyboardShortcutsOverlay.qml:138
180#: qml/Components/KeyboardShortcutsOverlay.qml:138
181msgid "Alt + F1"
181msgid "Alt + F1"
182−msgstr ""
182+msgstr "Alt + F1"
183
183
184#: qml/Components/KeyboardShortcutsOverlay.qml:143
184#: qml/Components/KeyboardShortcutsOverlay.qml:143
185msgid "Opens launcher keyboard navigation mode."
185msgid "Opens launcher keyboard navigation mode."
@@ -187,55 +187,55 @@ msgstr ""
187
187
188#: qml/Components/KeyboardShortcutsOverlay.qml:151
188#: qml/Components/KeyboardShortcutsOverlay.qml:151
189msgid "Super + Tab"
189msgid "Super + Tab"
190−msgstr ""
190+msgstr "Super + Tab"
191
191
192#: qml/Components/KeyboardShortcutsOverlay.qml:156
192#: qml/Components/KeyboardShortcutsOverlay.qml:156
193msgid "Switches applications via the launcher."
193msgid "Switches applications via the launcher."
194−msgstr ""
194+msgstr "Prepínanie aplikácií pomocou launchera."
195
195
196#: qml/Components/KeyboardShortcutsOverlay.qml:164
196#: qml/Components/KeyboardShortcutsOverlay.qml:164
197msgid "Super + 0 to 9"
197msgid "Super + 0 to 9"
198−msgstr ""
198+msgstr "Super + 0 do 9"
199
199
200#: qml/Components/KeyboardShortcutsOverlay.qml:169
200#: qml/Components/KeyboardShortcutsOverlay.qml:169
201msgid "Same as clicking on a launcher icon."
201msgid "Same as clicking on a launcher icon."
202−msgstr ""
202+msgstr "Rovnaké ako kliknutie na ikonu v launchery."
203
203
204#: qml/Components/KeyboardShortcutsOverlay.qml:181
204#: qml/Components/KeyboardShortcutsOverlay.qml:181
205msgid "Scopes"
205msgid "Scopes"
206−msgstr ""
206+msgstr "Scopes"
207
207
208#: qml/Components/KeyboardShortcutsOverlay.qml:188
208#: qml/Components/KeyboardShortcutsOverlay.qml:188
209msgid "Super (Tap)"
209msgid "Super (Tap)"
210−msgstr ""
210+msgstr "Super (Klik)"
211
211
212#: qml/Components/KeyboardShortcutsOverlay.qml:193
212#: qml/Components/KeyboardShortcutsOverlay.qml:193
213msgid "Opens the Scopes home."
213msgid "Opens the Scopes home."
214−msgstr ""
214+msgstr "Otvorí Scopes."
215
215
216#: qml/Components/KeyboardShortcutsOverlay.qml:209
216#: qml/Components/KeyboardShortcutsOverlay.qml:209
217msgid "Switching"
217msgid "Switching"
218−msgstr ""
218+msgstr "Prepínanie"
219
219
220#: qml/Components/KeyboardShortcutsOverlay.qml:216
220#: qml/Components/KeyboardShortcutsOverlay.qml:216
221msgid "Alt + Tab"
221msgid "Alt + Tab"
222−msgstr ""
222+msgstr "Alt + Tab"
223
223
224#: qml/Components/KeyboardShortcutsOverlay.qml:221
224#: qml/Components/KeyboardShortcutsOverlay.qml:221
225msgid "Switches between applications."
225msgid "Switches between applications."
226−msgstr ""
226+msgstr "Prepínanie medzi aplikáciami."
227
227
228#: qml/Components/KeyboardShortcutsOverlay.qml:229
228#: qml/Components/KeyboardShortcutsOverlay.qml:229
229msgid "Super + W"
229msgid "Super + W"
230−msgstr ""
230+msgstr "Super + W"
231
231
232#: qml/Components/KeyboardShortcutsOverlay.qml:234
232#: qml/Components/KeyboardShortcutsOverlay.qml:234
233msgid "Opens the desktop spread."
233msgid "Opens the desktop spread."
234−msgstr ""
234+msgstr "Otvorí rozloženie obrazoviek."
235
235
236#: qml/Components/KeyboardShortcutsOverlay.qml:242
236#: qml/Components/KeyboardShortcutsOverlay.qml:242
237msgid "Cursor Left or Right"
237msgid "Cursor Left or Right"
238−msgstr ""
238+msgstr "Ľavý alebo pravý kurzorový kláves"
239
239
240#: qml/Components/KeyboardShortcutsOverlay.qml:247
240#: qml/Components/KeyboardShortcutsOverlay.qml:247
241msgid "Moves the focus."
241msgid "Moves the focus."
@@ -243,7 +243,7 @@ msgstr ""
243
243
244#: qml/Components/KeyboardShortcutsOverlay.qml:259
244#: qml/Components/KeyboardShortcutsOverlay.qml:259
245msgid "Windows"
245msgid "Windows"
246−msgstr ""
246+msgstr "Okná"
247
247
248#: qml/Components/KeyboardShortcutsOverlay.qml:266
248#: qml/Components/KeyboardShortcutsOverlay.qml:266
249msgid "Ctrl + Super + D"
249msgid "Ctrl + Super + D"
po/unity8.pot+34−20
@@ -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-06-20 20:25+0000\n"
11+"POT-Creation-Date: 2016-06-27 18:49+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"
@@ -289,6 +289,16 @@ msgstr ""
289msgid "OK"
289msgid "OK"
290msgstr ""
290msgstr ""
291
291
292+#: qml/Components/MediaServices/VideoPlayerControls.qml:44
293+#, qt-format
294+msgid "%1:%2:%3"
295+msgstr ""
296+
297+#: qml/Components/MediaServices/VideoPlayerControls.qml:49
298+#, qt-format
299+msgid "%1:%2"
300+msgstr ""
301+
292#: qml/Components/ModeSwitchWarningDialog.qml:32
302#: qml/Components/ModeSwitchWarningDialog.qml:32
293msgid "Apps may have unsaved data:"
303msgid "Apps may have unsaved data:"
294msgstr ""
304msgstr ""
@@ -315,6 +325,10 @@ msgstr ""
315msgid "Close all"
325msgid "Close all"
316msgstr ""
326msgstr ""
317
327
328+#: qml/Components/SharingPicker.qml:54
329+msgid "Preview Share Item"
330+msgstr ""
331+
318#: qml/Dash/DashPageHeader.qml:333
332#: qml/Dash/DashPageHeader.qml:333
319msgctxt "Button: Open the Ubuntu Store"
333msgctxt "Button: Open the Ubuntu Store"
320msgid "Store"
334msgid "Store"
@@ -388,10 +402,6 @@ msgstr ""
388msgid "Add a review"
402msgid "Add a review"
389msgstr ""
403msgstr ""
390
404
391−#: qml/Dash/Previews/PreviewSharing.qml:66
392−msgid "Preview Share Item"
393−msgstr ""
394−
395#: qml/Dash/PullToRefreshScopeStyle.qml:55
405#: qml/Dash/PullToRefreshScopeStyle.qml:55
396msgid "Pull to refresh…"
406msgid "Pull to refresh…"
397msgstr ""
407msgstr ""
@@ -408,11 +418,11 @@ msgstr ""
408msgid "Manage"
418msgid "Manage"
409msgstr ""
419msgstr ""
410
420
411−#: qml/Dash/ScopesList.qml:101
421+#: qml/Dash/ScopesList.qml:102
412msgid "Home"
422msgid "Home"
413msgstr ""
423msgstr ""
414
424
415−#: qml/Dash/ScopesList.qml:102
425+#: qml/Dash/ScopesList.qml:103
416msgid "Also installed"
426msgid "Also installed"
417msgstr ""
427msgstr ""
418
428
@@ -426,7 +436,7 @@ msgstr ""
426msgid "Unlock"
436msgid "Unlock"
427msgstr ""
437msgstr ""
428
438
429−#: qml/Greeter/Greeter.qml:536
439+#: qml/Greeter/Greeter.qml:539
430msgid "Try again"
440msgid "Try again"
431msgstr ""
441msgstr ""
432
442
@@ -617,7 +627,7 @@ msgstr ""
617#: qml/Wizard/Pages/40-location.qml:271 qml/Wizard/Pages/50-timezone.qml:270
627#: qml/Wizard/Pages/40-location.qml:271 qml/Wizard/Pages/50-timezone.qml:270
618#: qml/Wizard/Pages/60-account.qml:65 qml/Wizard/Pages/70-passwd-type.qml:144
628#: qml/Wizard/Pages/60-account.qml:65 qml/Wizard/Pages/70-passwd-type.qml:144
619#: qml/Wizard/Pages/75-report-check.qml:84
629#: qml/Wizard/Pages/75-report-check.qml:84
620−#: qml/Wizard/Pages/passcode-desktop.qml:124
630+#: qml/Wizard/Pages/passcode-desktop.qml:141
621#: qml/Wizard/Pages/password-set.qml:141
631#: qml/Wizard/Pages/password-set.qml:141
622msgid "Next"
632msgid "Next"
623msgstr ""
633msgstr ""
@@ -769,7 +779,7 @@ msgid "Service Terms"
769msgstr ""
779msgstr ""
770
780
771#: qml/Wizard/Pages/passcode-confirm.qml:43
781#: qml/Wizard/Pages/passcode-confirm.qml:43
772−#: qml/Wizard/Pages/passcode-desktop.qml:80
782+#: qml/Wizard/Pages/passcode-desktop.qml:96
773msgid "Confirm passcode"
783msgid "Confirm passcode"
774msgstr ""
784msgstr ""
775
785
@@ -786,24 +796,24 @@ msgstr ""
786msgid "Lock Screen Passcode"
796msgid "Lock Screen Passcode"
787msgstr ""
797msgstr ""
788
798
789−#: qml/Wizard/Pages/passcode-desktop.qml:56
799+#: qml/Wizard/Pages/passcode-desktop.qml:60
790msgid "Enter 4 numbers to setup your passcode"
800msgid "Enter 4 numbers to setup your passcode"
791msgstr ""
801msgstr ""
792
802
793−#: qml/Wizard/Pages/passcode-desktop.qml:65
803+#: qml/Wizard/Pages/passcode-desktop.qml:76
794#: qml/Wizard/Pages/passcode-set.qml:54
804#: qml/Wizard/Pages/passcode-set.qml:54
795msgid "Choose passcode"
805msgid "Choose passcode"
796msgstr ""
806msgstr ""
797
807
798−#: qml/Wizard/Pages/passcode-desktop.qml:105
808+#: qml/Wizard/Pages/passcode-desktop.qml:126
799msgid "Passcode too short"
809msgid "Passcode too short"
800msgstr ""
810msgstr ""
801
811
802−#: qml/Wizard/Pages/passcode-desktop.qml:107
812+#: qml/Wizard/Pages/passcode-desktop.qml:128
803msgid "Passcodes match"
813msgid "Passcodes match"
804msgstr ""
814msgstr ""
805
815
806−#: qml/Wizard/Pages/passcode-desktop.qml:109
816+#: qml/Wizard/Pages/passcode-desktop.qml:130
807msgid "Passcodes do not match"
817msgid "Passcodes do not match"
808msgstr ""
818msgstr ""
809
819
@@ -852,25 +862,29 @@ msgid "To proceed with no SIM tap Skip."
852msgstr ""
862msgstr ""
853
863
854#: qml/Wizard/PasswordMeter.qml:87
864#: qml/Wizard/PasswordMeter.qml:87
855−msgid "Passwords match"
865+msgid "Password too short"
856msgstr ""
866msgstr ""
857
867
858#: qml/Wizard/PasswordMeter.qml:89
868#: qml/Wizard/PasswordMeter.qml:89
869+msgid "Passwords match"
870+msgstr ""
871+
872+#: qml/Wizard/PasswordMeter.qml:91
859msgid "Passwords do not match"
873msgid "Passwords do not match"
860msgstr ""
874msgstr ""
861
875
862−#: qml/Wizard/PasswordMeter.qml:93
876+#: qml/Wizard/PasswordMeter.qml:95
863msgid "Strong password"
877msgid "Strong password"
864msgstr ""
878msgstr ""
865
879
866−#: qml/Wizard/PasswordMeter.qml:95
880+#: qml/Wizard/PasswordMeter.qml:97
867msgid "Fair password"
881msgid "Fair password"
868msgstr ""
882msgstr ""
869
883
870−#: qml/Wizard/PasswordMeter.qml:97
884+#: qml/Wizard/PasswordMeter.qml:99
871msgid "Weak password"
885msgid "Weak password"
872msgstr ""
886msgstr ""
873
887
874−#: qml/Wizard/PasswordMeter.qml:99
888+#: qml/Wizard/PasswordMeter.qml:101
875msgid "Very weak password"
889msgid "Very weak password"
876msgstr ""
890msgstr ""
qml/Components/LazyImage.qml+3−1
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013-2016 Canonical, Ltd.
3 *
3 *
4 * Authors:
4 * Authors:
5 * Michał Sawicz <michal.sawicz@canonical.com>
5 * Michał Sawicz <michal.sawicz@canonical.com>
@@ -29,6 +29,7 @@ Item {
29
29
30 property real initialWidth: scaleTo == "width" || scaleTo == "fit" ? width : units.gu(10)
30 property real initialWidth: scaleTo == "width" || scaleTo == "fit" ? width : units.gu(10)
31 property real initialHeight: scaleTo == "height" || scaleTo == "fit" ? height : units.gu(10)
31 property real initialHeight: scaleTo == "height" || scaleTo == "fit" ? height : units.gu(10)
32+ property real lastScaledDimension: scaleTo == "height" || scaleTo == "fit" ? width : height
32
33
33 property alias sourceSize: image.sourceSize
34 property alias sourceSize: image.sourceSize
34 property alias asynchronous: image.asynchronous
35 property alias asynchronous: image.asynchronous
@@ -168,6 +169,7 @@ Item {
168 easing.type: Easing.Linear; duration: UbuntuAnimation.SnapDuration
169 easing.type: Easing.Linear; duration: UbuntuAnimation.SnapDuration
169 }
170 }
170 }
171 }
172+ ScriptAction { script: { lastScaledDimension = scaleTo == "height" || scaleTo == "fit" ? root.width : root.height } }
171 }
173 }
172 },
174 },
173
175
qml/Components/ListItems/graphics/ListItemDividerHorizontal@18.png

Binary file not shown.

qml/Components/MediaServices/MediaServices.qmlnew file+386
@@ -0,0 +1,386 @@
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+import QtMultimedia 5.0
19+import Ubuntu.Components 1.3
20+
21+FocusScope {
22+ id: root
23+ property var sourceData
24+ property string context: ""
25+ property list<Action> actions
26+ property Item rootItem: QuickUtils.rootItem(root)
27+ property var maximumEmbeddedHeight
28+
29+ property alias header: headerContent.item
30+ property alias content: contentLoader.item
31+ property alias footer: footerContent.item
32+ property alias fullscreen: priv.fullscreen
33+
34+ signal close();
35+
36+ onFullscreenChanged: {
37+ if (!fullscreen) rotationAction.checked = false;
38+ }
39+
40+ readonly property color backgroundColor: "#1B1B1B"
41+ readonly property color iconColor: "#F3F3E7"
42+
43+ Rectangle {
44+ anchors.fill: parent
45+ color: root.backgroundColor
46+ opacity: 0.85
47+ }
48+
49+ OrientationHelper {
50+ id: orientationHelper
51+ automaticOrientation: fullscreen
52+
53+ StateGroup {
54+ id: orientationState
55+
56+ states: [
57+ State {
58+ name: "portrait"
59+ when: !rotationAction.checked
60+ },
61+ State {
62+ name: "landscape"
63+ when: rotationAction.checked
64+ }
65+ ]
66+
67+ transitions: [
68+ Transition {
69+ to: "landscape"
70+ SequentialAnimation {
71+ PropertyAction { target: orientationHelper; property: "automaticOrientation"; value: false }
72+ PropertyAction { target: orientationHelper; property: "orientationAngle"; value: 90 }
73+ PropertyAction { target: orientationHelper; property: "automaticOrientation"; value: false }
74+ }
75+ },
76+ Transition {
77+ to: "portrait"
78+ SequentialAnimation {
79+ PropertyAction { target: orientationHelper; property: "automaticOrientation"; value: false }
80+ PropertyAction { target: orientationHelper; property: "orientationAngle"; value: 0 }
81+ PropertyAction { target: orientationHelper; property: "automaticOrientation"; value: fullscreen }
82+ }
83+ }
84+ ]
85+ }
86+
87+ Loader {
88+ id: contentLoader
89+
90+ sourceComponent: {
91+ switch (context) {
92+ case "video":
93+ return videoComponent;
94+ }
95+ return undefined;
96+ }
97+
98+ Component {
99+ id: videoComponent
100+ VideoPlayer {
101+ id: player
102+ objectName: "videoPlayer"
103+
104+ width: orientationHelper.width
105+ height: orientationHelper.height
106+ maximumEmbeddedHeight: root.maximumEmbeddedHeight
107+ fixedHeight: fullscreen
108+ orientation: orientationState.state === "landscape" ? Qt.LandscapeOrientation : Qt.PortraitOrientation
109+
110+ playButtonBackgroundColor: root.backgroundColor
111+ playButtonIconColor: root.iconColor
112+
113+ screenshot: {
114+ var screenshotData = root.sourceData["screenshot"];
115+ if (screenshotData) return screenshotData;
116+
117+ var source = root.sourceData["source"];
118+ if (source) {
119+ if (source.toString().indexOf("file://") === 0) {
120+ return "image://thumbnailer/" + source.toString().substr(7);
121+ }
122+ }
123+ return "";
124+ }
125+
126+ mediaPlayer: footer ? footer.mediaPlayer : null
127+
128+ onClicked: {
129+ if (mediaPlayer.availability !== MediaPlayer.Available) return;
130+
131+ if (mediaPlayer.playbackState === MediaPlayer.StoppedState) {
132+ mediaPlayer.play();
133+ } else if (controlHideTimer.running) {
134+ if (mediaPlayer.playbackState === MediaPlayer.PlayingState) {
135+ mediaPlayer.pause();
136+ } else {
137+ mediaPlayer.play();
138+ }
139+ } else {
140+ controlHideTimer.restart();
141+ }
142+ }
143+ onPositionChanged: controlHideTimer.restart();
144+ }
145+ }
146+ }
147+ }
148+
149+ Loader {
150+ id: headerContent
151+ anchors {
152+ top: parent.top
153+ left: parent.left
154+ right: parent.right
155+ topMargin: -headerContent.height
156+ }
157+ height: units.gu(6)
158+ visible: false
159+
160+ // eater
161+ MouseArea {
162+ anchors.fill: parent
163+ }
164+
165+ Rectangle {
166+ anchors.fill: parent
167+ color: root.backgroundColor
168+ opacity: 0.85
169+ visible: headerContent.status === Loader.Ready
170+ }
171+
172+ active: root.fullscreen
173+ sourceComponent: headerComponent
174+
175+ Component {
176+ id: headerComponent
177+
178+ MediaServicesHeader {
179+ iconColor: root.iconColor
180+ onGoPrevious: {
181+ rotationAction.checked = false;
182+ root.close();
183+ }
184+ }
185+ }
186+
187+ // If we interact with the bar, reset the hide timer.
188+ MouseArea {
189+ anchors.fill: parent
190+ onPressed: {
191+ mouse.accepted = false
192+ if (controlHideTimer.running) controlHideTimer.restart()
193+ }
194+ }
195+ }
196+
197+ Loader {
198+ id: footerContent
199+ anchors {
200+ left: parent.left
201+ right: parent.right
202+ bottom: parent.bottom
203+ bottomMargin: -footerContent.height
204+ }
205+ height: units.gu(7)
206+ visible: false
207+
208+ sourceComponent: {
209+ switch (context) {
210+ case "video":
211+ return videoControlsComponent;
212+ }
213+ return undefined;
214+ }
215+ // eater
216+ MouseArea {
217+ anchors.fill: parent
218+ }
219+
220+ Rectangle {
221+ anchors.fill: parent
222+ color: root.backgroundColor
223+ opacity: 0.85
224+ visible: footerContent.status === Loader.Ready
225+ }
226+
227+ Component {
228+ id: videoControlsComponent
229+ VideoPlayerControls {
230+ id: controls
231+ objectName: "videoControls"
232+
233+ viewAction: rotationAction.enabled ? rotationAction : fullscreenAction
234+ userActions: root.actions
235+ iconColor: root.iconColor
236+ backgroundColor: root.backgroundColor
237+
238+ mediaPlayer.source: {
239+ if (!root.sourceData) return "";
240+
241+ var source = root.sourceData["source"];
242+ if (source.toString().indexOf("video://") === 0) {
243+ return source.toString().substr(6);
244+ }
245+ return source;
246+ }
247+ mediaPlayer.onPlaybackStateChanged: {
248+ controlHideTimer.restart();
249+ }
250+
251+ Binding {
252+ target: priv
253+ property: "forceControlsShown"
254+
255+ value: (fullscreen && mediaPlayer.playbackState === MediaPlayer.StoppedState) ||
256+ mediaPlayer.playbackState === MediaPlayer.PausedState ||
257+ interacting
258+ }
259+
260+ onInteractingChanged: {
261+ controlHideTimer.restart();
262+ }
263+
264+ Binding {
265+ target: header
266+ property: "title"
267+ value: controls.mediaPlayer.metaData.title !== undefined ?
268+ controls.mediaPlayer.metaData.title :
269+ controls.mediaPlayer.source.toString().replace(/^.*[\\\/]/, '')
270+ when: header != null
271+ }
272+ }
273+ }
274+
275+ // If we interact with the bar, reset the hide timer.
276+ MouseArea {
277+ z: 1
278+ anchors.fill: parent
279+ onPressed: {
280+ mouse.accepted = false
281+ if (controlHideTimer.running) controlHideTimer.restart()
282+ }
283+ }
284+ }
285+
286+ StateGroup {
287+ states: [
288+ State {
289+ name: "controlsShown"
290+ when: priv.forceControlsShown || priv.controlTimerActive
291+ PropertyChanges {
292+ target: footerContent
293+ anchors.bottomMargin: 0
294+ visible: true
295+ }
296+ PropertyChanges {
297+ target: headerContent
298+ anchors.topMargin: 0
299+ visible: true
300+ }
301+ }
302+ ]
303+
304+ transitions: [
305+ Transition {
306+ to: "controlsShown"
307+ SequentialAnimation {
308+ PropertyAction { target: root; property: "clip"; value: true }
309+ PropertyAction { property: "visible" }
310+ NumberAnimation {
311+ properties: "anchors.bottomMargin,anchors.topMargin"
312+ duration: UbuntuAnimation.FastDuration
313+ }
314+ PropertyAction { target: root; property: "clip"; value: false }
315+ }
316+ },
317+ Transition {
318+ from: "controlsShown"
319+ SequentialAnimation {
320+ PropertyAction { target: root; property: "clip"; value: true }
321+ NumberAnimation {
322+ properties: "anchors.bottomMargin,anchors.topMargin"
323+ duration: UbuntuAnimation.FastDuration
324+ }
325+ PropertyAction { property: "visible" }
326+ PropertyAction { target: root; property: "clip"; value: false }
327+ }
328+ }
329+ ]
330+ }
331+
332+ StateGroup {
333+ states: [
334+ State {
335+ name: "minimized"
336+ when: !priv.fullscreen
337+ PropertyChanges { target: root; implicitHeight: content ? content.implicitHeight : 0; }
338+ },
339+ State {
340+ name: "fullscreen"
341+ when: priv.fullscreen
342+ ParentChange { target: root; parent: rootItem; x: 0; y: 0; width: parent.width; }
343+ PropertyChanges { target: root; implicitHeight: root.parent ? root.parent.height : 0; }
344+ PropertyChanges { target: fullscreenAction; iconName: "view-restore"; }
345+ }
346+ ]
347+
348+ transitions: Transition {
349+ ParentAnimation {
350+ UbuntuNumberAnimation { properties: "x,y,width,implicitHeight"; duration: UbuntuAnimation.FastDuration }
351+ }
352+ }
353+ }
354+
355+ QtObject {
356+ id: priv
357+
358+ property bool fullscreen: false
359+ property alias controlTimerActive: controlHideTimer.running
360+ property bool forceControlsShown: false
361+ }
362+
363+ Timer {
364+ id: controlHideTimer
365+ objectName: "controlHideTimer"
366+ interval: 4000
367+ running: false
368+ }
369+
370+ Action {
371+ id: fullscreenAction
372+ enabled: rotationAction.enabled === false
373+ iconName: "view-fullscreen"
374+
375+ onTriggered: priv.fullscreen = !priv.fullscreen
376+ }
377+
378+ Action {
379+ id: rotationAction
380+ enabled: root.fullscreen === true
381+ iconName: "view-rotate"
382+
383+ property bool checked: false
384+ onTriggered: checked = !checked
385+ }
386+}
qml/Components/MediaServices/MediaServicesControls.qmlnew file+192
@@ -0,0 +1,192 @@
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+import QtQuick.Layouts 1.1
19+import QtMultimedia 5.0
20+import Ubuntu.Components 1.3
21+import Ubuntu.Components.ListItems 1.3 as ListItem
22+
23+Item {
24+ id: root
25+
26+ property alias component: loader.sourceComponent
27+
28+ signal actionClicked(string action)
29+ signal viewModeClicked
30+
31+ property list<Action> userActions
32+ property Action viewAction
33+
34+ property color iconColor
35+ property color backgroundColor
36+
37+ RowLayout {
38+ anchors {
39+ left: parent.left
40+ right: parent.right
41+ verticalCenter: parent.verticalCenter
42+ margins: units.gu(2)
43+ }
44+ spacing: units.gu(2)
45+
46+ AbstractButton {
47+ id: actionButton
48+ Layout.preferredWidth: units.gu(3)
49+ Layout.preferredHeight: units.gu(3)
50+ Layout.alignment: Qt.AlignVCenter
51+ enabled: action && action.enabled
52+
53+ Action {
54+ id: popupAction
55+ iconName: "navigation-menu"
56+ onTriggered: userActionsPopup.createObject(root, { "anchors.bottom": root.top })
57+ }
58+
59+ action: {
60+ switch (userActions.length) {
61+ case 0:
62+ return null;
63+ case 1:
64+ return userActions[0];
65+ default:
66+ return popupAction;
67+ }
68+ }
69+
70+ Icon {
71+ anchors.fill: parent
72+ visible: actionButton.action && actionButton.action.iconSource !== "" || false
73+ source: actionButton.action ? actionButton.action.iconSource : ""
74+ color: root.iconColor
75+ opacity: actionButton.action && actionButton.action.enabled ? 1.0 : 0.5
76+ }
77+ }
78+
79+ Loader {
80+ id: loader
81+ Layout.fillWidth: true
82+ Layout.preferredHeight: units.gu(3)
83+ }
84+
85+ AbstractButton {
86+ objectName: "viewActionButton"
87+ Layout.preferredWidth: units.gu(3)
88+ Layout.preferredHeight: units.gu(3)
89+ Layout.alignment: Qt.AlignVCenter
90+ enabled: viewAction.enabled
91+ action: viewAction
92+
93+ Icon {
94+ anchors.fill: parent
95+ visible: viewAction.iconSource !== ""
96+ source: viewAction.iconSource
97+ color: root.iconColor
98+ opacity: viewAction.enabled ? 1.0 : 0.5
99+ }
100+ }
101+ }
102+
103+ Component {
104+ id: userActionsPopup
105+
106+ Rectangle {
107+ id: popup
108+ color: root.backgroundColor
109+ width: userActionsColumn.width
110+ height: userActionsColumn.height
111+
112+ InverseMouseArea {
113+ id: eventGrabber
114+ acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.RightButton
115+ anchors.fill: popup
116+ propagateComposedEvents: false
117+ onWheel: wheel.accepted = true
118+
119+ onPressed: popup.destroy()
120+ }
121+
122+ Column {
123+ id: userActionsColumn
124+ spacing: units.gu(1)
125+ width: units.gu(31)
126+
127+ Repeater {
128+ id: actionRepeater
129+ model: userActions
130+ AbstractButton {
131+ action: modelData
132+
133+ onClicked: popup.destroy()
134+
135+ implicitHeight: units.gu(4) + bottomDividerLine.height
136+ width: parent ? parent.width : units.gu(31)
137+
138+ Rectangle {
139+ visible: parent.pressed
140+ anchors {
141+ left: parent.left
142+ right: parent.right
143+ top: parent.top
144+ }
145+ height: parent.height - bottomDividerLine.height
146+ opacity: 0.5
147+ }
148+
149+ Icon {
150+ id: actionIcon
151+ visible: "" !== action.iconSource
152+ source: action.iconSource
153+ color: root.iconColor
154+ anchors {
155+ verticalCenter: parent.verticalCenter
156+ verticalCenterOffset: units.dp(-1)
157+ left: parent.left
158+ leftMargin: units.gu(2)
159+ }
160+ width: units.gu(2)
161+ height: units.gu(2)
162+ opacity: action.enabled ? 1.0 : 0.5
163+ }
164+
165+ Label {
166+ anchors {
167+ verticalCenter: parent.verticalCenter
168+ verticalCenterOffset: units.dp(-1)
169+ left: actionIcon.visible ? actionIcon.right : parent.left
170+ leftMargin: units.gu(2)
171+ right: parent.right
172+ }
173+ // In the tabs overflow panel there are no icons, and the font-size
174+ // is medium as opposed to the small font-size in the actions overflow panel.
175+ fontSize: actionIcon.visible ? "small" : "medium"
176+ elide: Text.ElideRight
177+ text: action.text
178+ color: root.iconColor
179+ opacity: action.enabled ? 1.0 : 0.5
180+ }
181+
182+ ListItem.ThinDivider {
183+ id: bottomDividerLine
184+ anchors.bottom: parent.bottom
185+ visible: index !== actionRepeater.count - 1
186+ }
187+ }
188+ }
189+ }
190+ }
191+ }
192+}
qml/Components/MediaServices/MediaServicesHeader.qmlnew file+71
@@ -0,0 +1,71 @@
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+import QtQuick.Layouts 1.1
19+import Ubuntu.Components 1.3
20+
21+Item {
22+ id: root
23+
24+ property alias title: titleLabel.text
25+ property alias component: loader.sourceComponent
26+ property alias iconColor: titleLabel.color
27+
28+ signal goPrevious
29+
30+ RowLayout {
31+ id: row
32+ anchors {
33+ left: parent.left
34+ right: parent.right
35+ verticalCenter: parent.verticalCenter
36+ margins: units.gu(2)
37+ }
38+ spacing: units.gu(2)
39+
40+ AbstractButton {
41+ id: navigationButton
42+ objectName: "navigationButton"
43+ Layout.preferredWidth: units.gu(3)
44+ Layout.preferredHeight: units.gu(3)
45+ Layout.alignment: Qt.AlignVCenter
46+
47+ Icon {
48+ anchors.fill: parent
49+ name: "go-previous"
50+ color: titleLabel.color
51+ }
52+
53+ onTriggered: {
54+ root.goPrevious();
55+ }
56+ }
57+
58+ Label {
59+ id: titleLabel
60+ Layout.fillWidth: true
61+ Layout.alignment: Qt.AlignVCenter
62+ elide: Text.ElideRight
63+ }
64+
65+ Loader {
66+ id: loader
67+ Layout.alignment: Qt.AlignVCenter
68+ Layout.preferredHeight: units.gu(3)
69+ }
70+ }
71+}
qml/Components/MediaServices/VideoPlayer.qmlnew file+155
@@ -0,0 +1,155 @@
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+import QtMultimedia 5.0
19+import Ubuntu.Components 1.3
20+import Ubuntu.Thumbnailer 0.1
21+import "../../Components"
22+
23+Item {
24+ id: root
25+
26+ property alias screenshot: image.source
27+ property alias mediaPlayer: videoOutput.source
28+ property int orientation: Qt.PortraitOrientation
29+ property bool fixedHeight: false
30+ property var maximumEmbeddedHeight
31+
32+ property alias playButtonBackgroundColor: playButton.color
33+ property alias playButtonIconColor: playButtonIcon.color
34+
35+ implicitHeight: {
36+ if (parent && orientation === Qt.LandscapeOrientation) {
37+ return parent.height;
38+ }
39+ return content.height;
40+ }
41+
42+ signal clicked
43+ signal positionChanged
44+
45+ Item {
46+ id: content
47+ anchors {
48+ left: parent.left
49+ right: parent.right
50+ verticalCenter: parent.verticalCenter
51+ }
52+ height: {
53+ if (root.orientation === Qt.LandscapeOrientation || fixedHeight) {
54+ return root.height;
55+ }
56+ var proposedHeight = videoOutput.height;
57+ if (maximumEmbeddedHeight !== undefined && maximumEmbeddedHeight < proposedHeight) {
58+ return maximumEmbeddedHeight;
59+ }
60+ return proposedHeight;
61+ }
62+ clip: image.height > videoOutput.height
63+
64+ LazyImage {
65+ id: image
66+ objectName: "screenshot"
67+ anchors {
68+ left: parent.left
69+ right: parent.right
70+ verticalCenter: parent.verticalCenter
71+ }
72+ scaleTo: "width"
73+ lastScaledDimension: playButton.height + units.gu(2)
74+ initialHeight: lastScaledDimension
75+ useUbuntuShape: false
76+
77+ visible: !mediaPlayer || mediaPlayer.playbackState === MediaPlayer.StoppedState
78+ }
79+
80+ VideoOutput {
81+ id: videoOutput
82+ anchors.centerIn: parent
83+
84+ width: root.width
85+ height: {
86+ if (fixedHeight) {
87+ return root.height;
88+ }
89+ var proposedHeight = mediaPlayer && mediaPlayer.metaData.resolution !== undefined ?
90+ (mediaPlayer.metaData.resolution.height / mediaPlayer.metaData.resolution.width) * width :
91+ image.height;
92+ if (maximumEmbeddedHeight !== undefined && maximumEmbeddedHeight < proposedHeight) {
93+ return maximumEmbeddedHeight;
94+ }
95+ return proposedHeight;
96+ }
97+
98+ source: mediaPlayer
99+ visible: mediaPlayer && mediaPlayer.playbackState !== MediaPlayer.StoppedState || false
100+
101+ Connections {
102+ target: mediaPlayer
103+ onError: {
104+ if (error !== MediaPlayer.NoError) {
105+ errorTimer.restart();
106+ }
107+ }
108+ }
109+ }
110+ }
111+
112+ Rectangle {
113+ id: playButton
114+ readonly property bool bigButton: parent.width > units.gu(40)
115+ anchors.centerIn: content
116+ width: bigButton ? units.gu(10) : units.gu(8)
117+ height: width
118+ visible: mediaPlayer && mediaPlayer.playbackState !== MediaPlayer.PlayingState || false
119+ opacity: 0.85
120+ radius: width/2
121+
122+ Behavior on width { UbuntuNumberAnimation {} }
123+
124+ Icon {
125+ id: playButtonIcon
126+ anchors.fill: parent
127+ anchors.margins: units.gu(1)
128+ name: errorTimer.running ? "dialog-warning-symbolic" : "media-playback-start"
129+ }
130+ }
131+
132+ ActivityIndicator {
133+ anchors.centerIn: content
134+ running: {
135+ if (!mediaPlayer) return false;
136+ return mediaPlayer.status === MediaPlayer.Stalled ||
137+ (mediaPlayer.playbackState === MediaPlayer.PlayingState && mediaPlayer.status === MediaPlayer.Loading);
138+ }
139+ }
140+
141+ MouseArea {
142+ id: contentMouseArea
143+ anchors.fill: content
144+ enabled: !errorTimer.running
145+ hoverEnabled: mediaPlayer && mediaPlayer.playbackState !== MediaPlayer.StoppedState || false
146+
147+ onClicked: root.clicked()
148+ onPositionChanged: root.positionChanged()
149+ }
150+
151+ Timer {
152+ id: errorTimer
153+ interval: 2000
154+ }
155+}
qml/Components/MediaServices/VideoPlayerControls.qmlnew file+158
@@ -0,0 +1,158 @@
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+import QtQuick.Layouts 1.1
19+import QtMultimedia 5.0
20+import Ubuntu.Components 1.3
21+
22+MediaServicesControls {
23+ id: root
24+ readonly property alias mediaPlayer: _mediaPlayer
25+ property bool interacting: false
26+
27+ QtObject {
28+ id: priv
29+
30+ function formatProgress(time) {
31+ time = Math.floor(time / 1000);
32+
33+ var secs = time % 60;
34+ time = Math.floor(time / 60);
35+ var min = time % 60;
36+ var hour = Math.floor(time / 60);
37+
38+ if (secs < 10) secs = "0%1".arg(secs);
39+ if (min < 10) min = "0%1".arg(min);
40+ if (hour > 0) {
41+ // TRANSLATORS: this refers to a duration/remaining time of the video in hours, minutes and seconds,
42+ // of which you can change the order.
43+ // %1 refers to hours, %2 refers to minutes and %3 refers to seconds.
44+ return i18n.tr("%1:%2:%3").arg(hour).arg(min).arg(secs);
45+ } else {
46+ // TRANSLATORS: this refers to a duration/remaining time of the video in minutes and seconds,
47+ // of which you can change the order.
48+ // %1 refers to minutes and %2 refers to seconds.
49+ return i18n.tr("%1:%2").arg(min).arg(secs);
50+ }
51+ }
52+ }
53+
54+ component: Item {
55+ Connections {
56+ target: mediaPlayer
57+ onPositionChanged: {
58+ if (slider.valueGuard) return;
59+
60+ slider.valueGuard = true;
61+ slider.value = mediaPlayer.position;
62+ slider.valueGuard = false;
63+ if (!slider.pressed) {
64+ positionLabel.text = priv.formatProgress(mediaPlayer.position);
65+ }
66+ }
67+ }
68+
69+ Binding {
70+ target: root
71+ property: "interacting"
72+ value: slider.pressed
73+ }
74+
75+ Label {
76+ id: positionLabel
77+ anchors {
78+ left: parent.left
79+ bottom: parent.bottom
80+ bottomMargin: -units.dp(3)
81+ }
82+ verticalAlignment: Text.AlignBottom
83+ fontSize: "x-small"
84+ color: root.iconColor
85+
86+ text: priv.formatProgress(mediaPlayer.position)
87+ }
88+
89+ Slider {
90+ id: slider
91+ property bool valueGuard: false
92+
93+ anchors {
94+ left: parent.left
95+ right: parent.right
96+ }
97+ height: units.gu(2)
98+ live: true
99+ enabled: mediaPlayer.seekable && mediaPlayer.duration > 0
100+ minimumValue: 0
101+ maximumValue: mediaPlayer.duration > 0 ? mediaPlayer.duration : 1
102+ value: mediaPlayer.position
103+
104+ onStyleInstanceChanged: {
105+ if (__styleInstance) __styleInstance.backgroundColor = root.iconColor;
106+ }
107+
108+ onValueChanged: {
109+ if (!pressed || slider.valueGuard) return;
110+
111+ slider.valueGuard = true;
112+ mediaPlayer.seek(value);
113+ slider.valueGuard = false;
114+ }
115+
116+ property bool wasPlaying: mediaPlayer.playbackState === MediaPlayer.PlayingState
117+ onPressedChanged: {
118+ if (pressed) {
119+ wasPlaying = mediaPlayer.playbackState === MediaPlayer.PlayingState
120+ mediaPlayer.pause();
121+ } else {
122+ positionLabel.text = priv.formatProgress(mediaPlayer.position);
123+ if (wasPlaying) {
124+ mediaPlayer.play();
125+ }
126+ }
127+ }
128+
129+ function formatValue(value) {
130+ return priv.formatProgress(value);
131+ }
132+ }
133+
134+ Label {
135+ anchors {
136+ right: parent.right
137+ bottom: parent.bottom
138+ bottomMargin: -units.dp(3)
139+ }
140+ verticalAlignment: Text.AlignBottom
141+ fontSize: "x-small"
142+ color: root.iconColor
143+
144+ text: priv.formatProgress(mediaPlayer.duration)
145+ }
146+ }
147+
148+ MediaPlayer {
149+ id: _mediaPlayer
150+ objectName: "mediaPlayer"
151+
152+ onError: {
153+ if (error !== MediaPlayer.NoError) {
154+ stop();
155+ }
156+ }
157+ }
158+}
qml/Dash/Previews/PreviewSharing.qml → qml/Components/SharingPicker.qmlrenamed+2−14
@@ -20,8 +20,6 @@ import Ubuntu.Content 1.1
20
20
21Item {
21Item {
22 id: root
22 id: root
23− implicitHeight: button.height
24− implicitWidth: button.width
25
23
26 property var shareData
24 property var shareData
27 property alias active: peerPicker.active
25 property alias active: peerPicker.active
@@ -30,18 +28,8 @@ Item {
30 readonly property var url: shareData ? shareData["uri"] : ""
28 readonly property var url: shareData ? shareData["uri"] : ""
31 readonly property Item rootItem: QuickUtils.rootItem(root)
29 readonly property Item rootItem: QuickUtils.rootItem(root)
32
30
33− AbstractButton {
31+ function showPeerPicker() {
34− id: button
35− height: units.gu(4)
36− width: units.gu(4)
37− onClicked: peerPicker.visible = true
32+ peerPicker.visible = true;
38−
39− Icon {
40− anchors.centerIn: parent
41− height: units.gu(3)
42− width: units.gu(3)
43− source: "image://theme/share"
44− }
45 }
33 }
46
34
47 function createExportedItems(url) {
35 function createExportedItems(url) {
qml/Components/WindowControlButtons.qml+2
@@ -29,6 +29,7 @@ Row {
29 property bool active: false
29 property bool active: false
30 property bool windowIsMaximized: false
30 property bool windowIsMaximized: false
31 property bool closeButtonShown: true
31 property bool closeButtonShown: true
32+ property bool maximizeButtonShown: true
32 property bool overlayShown
33 property bool overlayShown
33
34
34 signal closeClicked()
35 signal closeClicked()
@@ -88,6 +89,7 @@ Row {
88 hoverEnabled: true
89 hoverEnabled: true
89 height: parent.height
90 height: parent.height
90 width: height
91 width: height
92+ visible: root.maximizeButtonShown
91 acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
93 acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
92 onClicked: {
94 onClicked: {
93 if (mouse.button == Qt.LeftButton) {
95 if (mouse.button == Qt.LeftButton) {
qml/Components/graphics/close@20.png

Binary file not shown.

qml/Components/graphics/icon_star_empty@20.png

Binary file not shown.

qml/Components/graphics/icon_star_full@20.png

Binary file not shown.

qml/Components/graphics/icon_star_half@20.png

Binary file not shown.

qml/Components/graphics/icon_star_off@20.png

Binary file not shown.

qml/Components/graphics/icon_star_on@20.png

Binary file not shown.

qml/Components/graphics/non-selected@18.png

Binary file not shown.

qml/Dash/CardTool.qml+2−1
@@ -15,6 +15,7 @@
15 */
15 */
16
16
17import QtQuick 2.4
17import QtQuick 2.4
18+import Ubuntu.Components 1.3
18import Dash 0.1
19import Dash 0.1
19
20
20/*!
21/*!
@@ -141,7 +142,7 @@ Item {
141 type:real \brief Height of the card's header.
142 type:real \brief Height of the card's header.
142 */
143 */
143 readonly property int headerHeight: cardLoader.item ? cardLoader.item.headerHeight : 0
144 readonly property int headerHeight: cardLoader.item ? cardLoader.item.headerHeight : 0
144− property size artShapeSize: cardLoader.item ? cardLoader.item.artShapeSize : 0
145+ property size artShapeSize: cardLoader.item ? cardLoader.item.artShapeSize : Qt.size(0, 0)
145
146
146 QtObject {
147 QtObject {
147 id: carouselTool
148 id: carouselTool
qml/Dash/DashApplication.qml+16−5
@@ -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,14 +15,25 @@
15 */
15 */
16
16
17import QtQuick 2.4
17import QtQuick 2.4
18+import QtQuick.Window 2.2
18import Ubuntu.Components 1.3
19import Ubuntu.Components 1.3
19import Ubuntu.Thumbnailer 0.1 // Register support for image://thumbnailer/ and image://albumart/
20import Ubuntu.Thumbnailer 0.1 // Register support for image://thumbnailer/ and image://albumart/
20
21
21−MainView {
22+Window {
22− implicitWidth: units.gu(40)
23+ visible: true
23− implicitHeight: units.gu(68)
24+ title: "Scopes" // Intentionally not translated
24
25
25− Dash {
26+ width: initialWidth > 0 ? initialWidth : units.gu(40)
27+ height: initialHeight > 0 ? initialHeight : units.gu(68)
28+
29+ minimumWidth: units.gu(40)
30+ minimumHeight: units.gu(40)
31+
32+ MainView {
26 anchors.fill: parent
33 anchors.fill: parent
34+
35+ Dash {
36+ anchors.fill: parent
37+ }
27 }
38 }
28}
39}
qml/Dash/Previews/Preview.qml+16
@@ -37,12 +37,16 @@ Item {
37 //! \brief The ScopeStyle component.
37 //! \brief The ScopeStyle component.
38 property var scopeStyle: null
38 property var scopeStyle: null
39
39
40+ //! Should the orientation be locked
41+ property int orientationLockCount: 0
42+
40 clip: true
43 clip: true
41
44
42 Binding {
45 Binding {
43 target: previewModel
46 target: previewModel
44 property: "widgetColumnCount"
47 property: "widgetColumnCount"
45 value: row.columns
48 value: row.columns
49+ when: root.orientationLockCount === 0
46 }
50 }
47
51
48 MouseArea {
52 MouseArea {
@@ -110,6 +114,18 @@ Item {
110 column.forceLayout();
114 column.forceLayout();
111 column.positionViewAtIndex(index, ListView.Contain)
115 column.positionViewAtIndex(index, ListView.Contain)
112 }
116 }
117+
118+ onOrientationLockChanged: {
119+ if (orientationLock)
120+ root.orientationLockCount++;
121+ else
122+ root.orientationLockCount = Math.max(0, root.orientationLockCount--);
123+ }
124+
125+ Component.onDestruction: {
126+ if (orientationLock)
127+ root.orientationLockCount = Math.max(0, root.orientationLockCount--);
128+ }
113 }
129 }
114 }
130 }
115 }
131 }
qml/Dash/Previews/PreviewImageGallery.qml+2
@@ -27,6 +27,8 @@ PreviewWidget {
27 id: root
27 id: root
28 implicitHeight: units.gu(22)
28 implicitHeight: units.gu(22)
29
29
30+ orientationLock: overlay.visible
31+
30 property Item rootItem: QuickUtils.rootItem(root)
32 property Item rootItem: QuickUtils.rootItem(root)
31
33
32 ListView {
34 ListView {
qml/Dash/Previews/PreviewInlineVideo.qmlnew file+65
@@ -0,0 +1,65 @@
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+import QtMultimedia 5.0
19+import Ubuntu.Components 1.3
20+import Ubuntu.Thumbnailer 0.1
21+import "../../Components"
22+import "../../Components/MediaServices"
23+
24+/*! \brief Preview widget for video.
25+
26+ This widget shows video contained in widgetData["source"],
27+ with a placeholder screenshot specified by widgetData["screenshot"].
28+ */
29+
30+PreviewWidget {
31+ id: root
32+ implicitWidth: units.gu(35)
33+ implicitHeight: services.height
34+
35+ widgetMargins: -units.gu(1)
36+ orientationLock: services.fullscreen
37+
38+ property alias rootItem: services.rootItem
39+
40+ MediaServices {
41+ id: services
42+ width: parent.width
43+
44+ actions: sharingAction
45+ context: "video"
46+ sourceData: widgetData
47+ fullscreen: false
48+ maximumEmbeddedHeight: rootItem.height / 2
49+
50+ onClose: fullscreen = false
51+
52+ Action {
53+ id: sharingAction
54+ iconName: "share"
55+ visible: sharingPicker.active
56+ onTriggered: sharingPicker.showPeerPicker()
57+ }
58+ }
59+
60+ SharingPicker {
61+ id: sharingPicker
62+ objectName: "sharingPicker"
63+ shareData: widgetData["share-data"]
64+ }
65+}
qml/Dash/Previews/PreviewMediaToolbar.qml+25−5
@@ -19,19 +19,39 @@ import Ubuntu.Components 1.3
19import "../../Components"
19import "../../Components"
20
20
21Rectangle {
21Rectangle {
22− property alias shareData: sharingWidget.shareData
22+ property alias shareData: sharingPicker.shareData
23
23
24 implicitHeight: units.gu(6)
24 implicitHeight: units.gu(6)
25 color: Qt.rgba(0, 0, 0, 0.8)
25 color: Qt.rgba(0, 0, 0, 0.8)
26− visible: sharingWidget.active
26+ visible: sharingPicker.active
27
27
28− PreviewSharing {
28+ Action {
29− id: sharingWidget
29+ id: sharingAction
30− objectName: "sharingWidget"
30+ iconName: "share"
31+ onTriggered: sharingPicker.showPeerPicker()
32+ }
33+
34+ AbstractButton {
35+ id: button
31 anchors {
36 anchors {
32 left: parent.left
37 left: parent.left
33 verticalCenter: parent.verticalCenter
38 verticalCenter: parent.verticalCenter
34 leftMargin: units.gu(1)
39 leftMargin: units.gu(1)
35 }
40 }
41+ height: units.gu(4)
42+ width: units.gu(4)
43+ action: sharingAction
44+
45+ Icon {
46+ anchors.centerIn: parent
47+ height: units.gu(3)
48+ width: units.gu(3)
49+ source: button.action.iconSource
50+ }
51+ }
52+
53+ SharingPicker {
54+ id: sharingPicker
55+ objectName: "sharingPicker"
36 }
56 }
37}
57}
qml/Dash/Previews/PreviewRatingEdit.qml+4−1
@@ -73,7 +73,10 @@ PreviewWidget {
73 ratingValue: widgetData["rating"]
73 ratingValue: widgetData["rating"]
74 reviewText: widgetData["review"]
74 reviewText: widgetData["review"]
75
75
76+ onTriggered: {
76− onTriggered: root.triggered(widgetId, actionId, data);
77+ root.triggered(widgetId, actionId, data);
78+ display.visible = true;
79+ }
77 onMakeSureVisible: root.makeSureVisible(item);
80 onMakeSureVisible: root.makeSureVisible(item);
78 }
81 }
79
82
qml/Dash/Previews/PreviewWidget.qml+3
@@ -31,6 +31,9 @@ Item {
31 //! Should the widget show in expanded mode (For those that support it)
31 //! Should the widget show in expanded mode (For those that support it)
32 property bool expanded: true
32 property bool expanded: true
33
33
34+ //! Should the orientation be locked
35+ property bool orientationLock: false
36+
34 //! Set margins width.
37 //! Set margins width.
35 property real widgetMargins: units.gu(1)
38 property real widgetMargins: units.gu(1)
36
39
qml/Dash/Previews/PreviewWidgetFactory.qml+11−1
@@ -37,6 +37,9 @@ Loader {
37 //! Should the widget show in expanded mode (For those that support it)
37 //! Should the widget show in expanded mode (For those that support it)
38 property bool expanded: widgetType !== "expandable" || widgetData["expanded"] === true
38 property bool expanded: widgetType !== "expandable" || widgetData["expanded"] === true
39
39
40+ //! Should the orientation be locked
41+ readonly property bool orientationLock: status === Loader.Ready ? item.orientationLock : false
42+
40 //! Set margins width.
43 //! Set margins width.
41 property real widgetMargins: status === Loader.Ready ? item.widgetMargins : units.gu(1)
44 property real widgetMargins: status === Loader.Ready ? item.widgetMargins : units.gu(1)
42
45
@@ -70,7 +73,14 @@ Loader {
70 case "reviews": return "PreviewRatingDisplay.qml";
73 case "reviews": return "PreviewRatingDisplay.qml";
71 case "table": return "PreviewTable.qml";
74 case "table": return "PreviewTable.qml";
72 case "text": return "PreviewTextSummary.qml";
75 case "text": return "PreviewTextSummary.qml";
76+ case "video": {
77+ if (!widgetData) return "";
78+ var source = widgetData.hasOwnProperty("source") ? widgetData["source"].toString() : "";
79+ if (source.match("^https{0,1}\:") !== null) {
73− case "video": return "PreviewVideoPlayback.qml";
80+ return "PreviewVideoPlayback.qml";
81+ }
82+ return "PreviewInlineVideo.qml";
83+ }
74 default: return "";
84 default: return "";
75 }
85 }
76 }
86 }
qml/Dash/Previews/PreviewZoomableImage.qml+1
@@ -31,6 +31,7 @@ PreviewWidget {
31 implicitHeight: lazyImage.height
31 implicitHeight: lazyImage.height
32
32
33 widgetMargins: -units.gu(1)
33 widgetMargins: -units.gu(1)
34+ orientationLock: overlay.visible
34
35
35 property Item rootItem: QuickUtils.rootItem(root)
36 property Item rootItem: QuickUtils.rootItem(root)
36
37
qml/Dash/ScopesList.qml+1
@@ -72,6 +72,7 @@ Item {
72 }
72 }
73
73
74 Flickable {
74 Flickable {
75+ objectName: "scopesListFlickable"
75 anchors {
76 anchors {
76 top: header.bottom
77 top: header.bottom
77 bottom: parent.bottom
78 bottom: parent.bottom
qml/Dash/graphics/dash_divider_top_darkgrad@27.png

Binary file not shown.

qml/Dash/graphics/dash_divider_top_lightgrad@27.png

Binary file not shown.

qml/Dash/graphics/header_handlearrow2@27.png

Binary file not shown.

qml/Dash/graphics/header_handlearrow@27.png

Binary file not shown.

qml/Dash/graphics/icon_clear@20.png

Binary file not shown.

qml/Dash/graphics/icon_search_active@20.png

Binary file not shown.

qml/Dash/graphics/icon_search_inactive@20.png

Binary file not shown.

qml/Dash/graphics/navigation_shadow@18.png

Binary file not shown.

qml/Dash/graphics/navigation_shadow_light@18.png

Binary file not shown.

qml/Dash/graphics/overview_hint@27.png

Binary file not shown.

qml/Dash/graphics/pagination_dot_off@30.png

Binary file not shown.

qml/Dash/graphics/pagination_dot_on@30.png

Binary file not shown.

qml/Dash/graphics/paper.png

Binary file not shown.

qml/Dash/graphics/pixel.png

Binary file not shown.

qml/Dash/graphics/play_button@18.png

Binary file not shown.

qml/Dash/graphics/play_button_active@18.png

Binary file not shown.

qml/Dash/graphics/play_button_active_big@20.png

Binary file not shown.

qml/Dash/graphics/play_button_big@20.png

Binary file not shown.

qml/Dash/graphics/tabbarchevron@27.png

Binary file not shown.

qml/Dash/graphics/tablet/icon_close_preview@20.png

Binary file not shown.

qml/Dash/graphics/tooltip_arrow@27.png

Binary file not shown.

qml/Greeter/Greeter.qml+4−1
@@ -478,7 +478,10 @@ Showable {
478 Connections {
478 Connections {
479 target: DBusUnitySessionService
479 target: DBusUnitySessionService
480 onLockRequested: root.forceShow()
480 onLockRequested: root.forceShow()
481+ onUnlocked: {
481− onUnlocked: root.forcedUnlock = true
482+ root.forcedUnlock = true;
483+ root.hideNow();
484+ }
482 }
485 }
483
486
484 Binding {
487 Binding {
qml/Greeter/graphics/dot_empty.png

Binary file not shown.

qml/Greeter/graphics/dot_filled.png

Binary file not shown.

qml/Greeter/graphics/dot_pointer.png

Binary file not shown.

qml/Greeter/graphics/icon_arrow.png

Binary file not shown.

qml/Greeter/graphics/infographic_circle_back.png

Binary file not shown.

qml/Launcher/LauncherPanel.qml+3−1
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013-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
@@ -69,7 +69,9 @@ Rectangle {
69 MouseArea {
69 MouseArea {
70 id: mouseEventEater
70 id: mouseEventEater
71 anchors.fill: parent
71 anchors.fill: parent
72+ acceptedButtons: Qt.AllButtons
72 hoverEnabled: true
73 hoverEnabled: true
74+ onWheel: wheel.accepted = true;
73 }
75 }
74
76
75 Column {
77 Column {
qml/Launcher/graphics/divider-line.png

Binary file not shown.

qml/Launcher/graphics/focused_app_arrow@30.png

Binary file not shown.

qml/Launcher/graphics/home@18.png

Binary file not shown.

qml/Launcher/graphics/quicklist_tooltip@30.png

Binary file not shown.

qml/Panel/Indicators/client/IndicatorsClient.qml+7−1
@@ -19,7 +19,6 @@
19
19
20import QtQuick 2.4
20import QtQuick 2.4
21import Ubuntu.Components 1.3
21import Ubuntu.Components 1.3
22−import Ubuntu.Components.ListItems 1.3 as ListItem
23
22
24Rectangle {
23Rectangle {
25 color: theme.palette.normal.background
24 color: theme.palette.normal.background
@@ -37,6 +36,13 @@ Rectangle {
37 function reset() {
36 function reset() {
38 clear();
37 clear();
39 var component = Qt.createComponent("IndicatorsList.qml");
38 var component = Qt.createComponent("IndicatorsList.qml");
39+ if (component.status !== Component.Ready) {
40+ if (component.status === Component.Error)
41+ console.error("Error: " + component.errorString());
42+
43+ return;
44+ }
45+
40 var page = component.createObject(pages, {"profile": indicatorProfile});
46 var page = component.createObject(pages, {"profile": indicatorProfile});
41 push(page);
47 push(page);
42 }
48 }
qml/Panel/Indicators/client/IndicatorsList.qml+7
@@ -58,6 +58,13 @@ Page {
58
58
59 onClicked: {
59 onClicked: {
60 var new_page = Qt.createComponent("IndicatorRepresentation.qml");
60 var new_page = Qt.createComponent("IndicatorRepresentation.qml");
61+ if (new_page.status !== Component.Ready) {
62+ if (new_page.status === Component.Error)
63+ console.error("Error: " + new_page.errorString());
64+
65+ return;
66+ }
67+
61 page.pageStack.push(new_page.createObject(pages), {"indicatorProperties" : model.indicatorProperties });
68 page.pageStack.push(new_page.createObject(pages), {"indicatorProperties" : model.indicatorProperties });
62 }
69 }
63 }
70 }
qml/Panel/IndicatorsMenu.qml+3−1
@@ -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
@@ -80,6 +80,8 @@ Showable {
80 MouseArea {
80 MouseArea {
81 anchors.fill: parent
81 anchors.fill: parent
82 hoverEnabled: true
82 hoverEnabled: true
83+ acceptedButtons: Qt.AllButtons
84+ onWheel: wheel.accepted = true;
83 }
85 }
84
86
85 MenuContent {
87 MenuContent {
qml/Panel/graphics/rectangular_dropshadow@30.png

Binary file not shown.

qml/Rotation/HalfLoopRotationAnimation.qml+1−1
@@ -42,6 +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+ UpdateShellTransformations { shell: root.shell; rotationAngle: root.toAngle }
46 ScriptAction { script: { info.transitioning = false; } }
46 ScriptAction { script: { info.transitioning = false; } }
47}
47}
qml/Rotation/ImmediateRotationAction.qml+1−1
@@ -25,7 +25,7 @@ SequentialAnimation {
25 root.info.transitioning = true;
25 root.info.transitioning = true;
26 } }
26 } }
27
27
28− UpdateShellTransformations { info: root.info; shell: root.shell }
28+ UpdateShellTransformations { shell: root.shell; rotationAngle: root.info.requestedOrientationAngle }
29
29
30 ScriptAction { script: {
30 ScriptAction { script: {
31 root.shell.updateFocusedAppOrientation();
31 root.shell.updateFocusedAppOrientation();
qml/Rotation/NinetyRotationAnimation.qml+1−1
@@ -78,7 +78,7 @@ SequentialAnimation {
78 duration: rotationDuration; easing.type: rotationEasing
78 duration: rotationDuration; easing.type: rotationEasing
79 }
79 }
80 }
80 }
81− UpdateShellTransformations { info: root.info; shell: root.shell }
81+ UpdateShellTransformations { shell: root.shell; rotationAngle: root.toAngle }
82 ScriptAction { script: {
82 ScriptAction { script: {
83 shellSnapshot.visible = false;
83 shellSnapshot.visible = false;
84 shellSnapshot.discard();
84 shellSnapshot.discard();
qml/Rotation/UpdateShellTransformations.qml+4−4
@@ -17,16 +17,16 @@
17import QtQuick 2.4
17import QtQuick 2.4
18
18
19ScriptAction {
19ScriptAction {
20− property var info
21 property var shell
20 property var shell
21+ property int rotationAngle
22
22
23 script: {
23 script: {
24− shell.orientationAngle = info.requestedOrientationAngle;
24+ shell.orientationAngle = rotationAngle;
25− shell.transformRotationAngle = info.requestedOrientationAngle;
25+ shell.transformRotationAngle = rotationAngle;
26
26
27 // They must all be bindings as orientedShell's size can change
27 // They must all be bindings as orientedShell's size can change
28
28
29− if (info.requestedOrientationAngle === 90 || info.requestedOrientationAngle === 270) {
29+ if (rotationAngle === 90 || rotationAngle === 270) {
30 shell.width = Qt.binding(function() { return orientedShell.height; });
30 shell.width = Qt.binding(function() { return orientedShell.height; });
31 shell.height = Qt.binding(function() { return orientedShell.width; });
31 shell.height = Qt.binding(function() { return orientedShell.width; });
32 } else {
32 } else {
qml/Stages/DecoratedWindow.qml+5−4
@@ -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
@@ -12,8 +12,6 @@
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: Michael Zanetti <michael.zanetti@canonical.com>
17 */
15 */
18
16
19import QtQuick 2.4
17import QtQuick 2.4
@@ -31,6 +29,7 @@ FocusScope {
31 property alias active: decoration.active
29 property alias active: decoration.active
32 readonly property alias title: applicationWindow.title
30 readonly property alias title: applicationWindow.title
33 property alias fullscreen: applicationWindow.fullscreen
31 property alias fullscreen: applicationWindow.fullscreen
32+ property alias maximizeButtonShown: decoration.maximizeButtonShown
34
33
35 readonly property bool decorationShown: !fullscreen
34 readonly property bool decorationShown: !fullscreen
36 property bool highlightShown: false
35 property bool highlightShown: false
@@ -38,6 +37,7 @@ FocusScope {
38
37
39 property real requestedWidth
38 property real requestedWidth
40 property real requestedHeight
39 property real requestedHeight
40+
41 property alias surfaceOrientationAngle: applicationWindow.surfaceOrientationAngle
41 property alias surfaceOrientationAngle: applicationWindow.surfaceOrientationAngle
42 readonly property real visibleDecorationHeight: root.decorationShown ? decoration.height : 0
42 readonly property real visibleDecorationHeight: root.decorationShown ? decoration.height : 0
43 readonly property bool counterRotate: surfaceOrientationAngle != 0 && surfaceOrientationAngle != 180
43 readonly property bool counterRotate: surfaceOrientationAngle != 0 && surfaceOrientationAngle != 180
@@ -45,7 +45,8 @@ FocusScope {
45 readonly property int minimumWidth: !counterRotate ? applicationWindow.minimumWidth : applicationWindow.minimumHeight
45 readonly property int minimumWidth: !counterRotate ? applicationWindow.minimumWidth : applicationWindow.minimumHeight
46 readonly property int minimumHeight: visibleDecorationHeight + (!counterRotate ? applicationWindow.minimumHeight : applicationWindow.minimumWidth)
46 readonly property int minimumHeight: visibleDecorationHeight + (!counterRotate ? applicationWindow.minimumHeight : applicationWindow.minimumWidth)
47 readonly property int maximumWidth: !counterRotate ? applicationWindow.maximumWidth : applicationWindow.maximumHeight
47 readonly property int maximumWidth: !counterRotate ? applicationWindow.maximumWidth : applicationWindow.maximumHeight
48+ readonly property int maximumHeight: (root.decorationShown && applicationWindow.maximumHeight > 0 ? decoration.height : 0)
48− readonly property int maximumHeight: visibleDecorationHeight + (!counterRotate ? applicationWindow.maximumHeight : applicationWindow.maximumWidth)
49+ + (!counterRotate ? applicationWindow.maximumHeight : applicationWindow.maximumWidth)
49 readonly property int widthIncrement: !counterRotate ? applicationWindow.widthIncrement : applicationWindow.heightIncrement
50 readonly property int widthIncrement: !counterRotate ? applicationWindow.widthIncrement : applicationWindow.heightIncrement
50 readonly property int heightIncrement: !counterRotate ? applicationWindow.heightIncrement : applicationWindow.widthIncrement
51 readonly property int heightIncrement: !counterRotate ? applicationWindow.heightIncrement : applicationWindow.widthIncrement
51
52
qml/Stages/DesktopSpread.qml+2
@@ -132,6 +132,8 @@ FocusScope {
132 anchors.fill: parent
132 anchors.fill: parent
133 visible: spreadBackground.visible
133 visible: spreadBackground.visible
134 enabled: visible
134 enabled: visible
135+ acceptedButtons: Qt.AllButtons
136+ onWheel: wheel.accepted = true;
135 }
137 }
136
138
137 Item {
139 Item {
qml/Stages/DesktopStage.qml+19−2
@@ -263,8 +263,23 @@ AbstractStage {
263 }
263 }
264 }
264 }
265 z: normalZ
265 z: normalZ
266+ x: requestedX // may be overridden in some states. Do not directly write to this.
267+ y: requestedY // may be overridden in some states. Do not directly write to this.
266− x: priv.focusedAppDelegate ? priv.focusedAppDelegate.x + units.gu(3) : (normalZ - 1) * units.gu(3)
268+ property real requestedX: priv.focusedAppDelegate ? priv.focusedAppDelegate.x + units.gu(3) : (normalZ - 1) * units.gu(3)
267− y: priv.focusedAppDelegate ? priv.focusedAppDelegate.y + units.gu(3) : normalZ * units.gu(3)
269+ property real requestedY: priv.focusedAppDelegate ? priv.focusedAppDelegate.y + units.gu(3) : normalZ * units.gu(3)
270+
271+ Binding {
272+ target: appDelegate
273+ property: "y"
274+ value: appDelegate.requestedY -
275+ Math.min(appDelegate.requestedY - PanelState.panelHeight,
276+ Math.max(0, UbuntuKeyboardInfo.height - (appContainer.height - (appDelegate.requestedY + appDelegate.height))))
277+ when: appDelegate.focus && appDelegate.state == "normal"
278+ && SurfaceManager.inputMethodSurface
279+ && SurfaceManager.inputMethodSurface.state != Mir.HiddenState
280+ && SurfaceManager.inputMethodSurface.state != Mir.MinimizedState
281+
282+ }
268
283
269 width: decoratedWindow.width
284 width: decoratedWindow.width
270 height: decoratedWindow.height
285 height: decoratedWindow.height
@@ -654,6 +669,8 @@ AbstractStage {
654 surface: model.surface
669 surface: model.surface
655 active: appDelegate.focus
670 active: appDelegate.focus
656 focus: true
671 focus: true
672+ maximizeButtonShown: (maximumWidth == 0 || maximumWidth >= appContainer.width) &&
673+ (maximumHeight == 0 || maximumHeight >= appContainer.height)
657 overlayShown: touchControls.overlayShown
674 overlayShown: touchControls.overlayShown
658
675
659 requestedWidth: appDelegate.requestedWidth
676 requestedWidth: appDelegate.requestedWidth
qml/Stages/TabletStage.qml+17−9
@@ -101,7 +101,7 @@ AbstractStage {
101 orientations |= Qt.LandscapeOrientation | Qt.InvertedLandscapeOrientation;
101 orientations |= Qt.LandscapeOrientation | Qt.InvertedLandscapeOrientation;
102 if (priv.sideStageItemId && !spreadView.surfaceDragging) {
102 if (priv.sideStageItemId && !spreadView.surfaceDragging) {
103 // If we have a sidestage app, support Portrait orientation
103 // If we have a sidestage app, support Portrait orientation
104− // so that it will switch the sidestage app to mainstage on rotate
104+ // so that it will switch the sidestage app to mainstage on rotate to portrait
105 orientations |= Qt.PortraitOrientation|Qt.InvertedPortraitOrientation;
105 orientations |= Qt.PortraitOrientation|Qt.InvertedPortraitOrientation;
106 }
106 }
107 return orientations;
107 return orientations;
@@ -624,7 +624,7 @@ AbstractStage {
624 enabled: priv.sideStageEnabled
624 enabled: priv.sideStageEnabled
625
625
626 onDropped: {
626 onDropped: {
627− drop.source.spreadDelegate.stage = ApplicationInfoInterface.MainStage;
627+ drop.source.spreadDelegate.saveStage(ApplicationInfoInterface.MainStage);
628 drop.source.spreadDelegate.focus = true;
628 drop.source.spreadDelegate.focus = true;
629 }
629 }
630 keys: "SideStage"
630 keys: "SideStage"
@@ -679,7 +679,7 @@ AbstractStage {
679 }
679 }
680 onDropped: {
680 onDropped: {
681 if (drop.keys == "MainStage") {
681 if (drop.keys == "MainStage") {
682− drop.source.spreadDelegate.stage = ApplicationInfoInterface.SideStage;
682+ drop.source.spreadDelegate.saveStage(ApplicationInfoInterface.SideStage);
683 drop.source.spreadDelegate.focus = true;
683 drop.source.spreadDelegate.focus = true;
684 }
684 }
685 }
685 }
@@ -810,6 +810,11 @@ AbstractStage {
810 }
810 }
811 }
811 }
812
812
813+ function saveStage(newStage) {
814+ stage = newStage;
815+ WindowStateStorage.saveStage(application.appId, newStage);
816+ }
817+
813 // FIXME: A regular binding doesn't update any more after closing an app.
818 // FIXME: A regular binding doesn't update any more after closing an app.
814 // Using a Binding for now.
819 // Using a Binding for now.
815 Binding {
820 Binding {
@@ -843,15 +848,17 @@ AbstractStage {
843 refreshStage();
848 refreshStage();
844 _constructing = false;
849 _constructing = false;
845 }
850 }
846− Component.onDestruction: {
847− WindowStateStorage.saveStage(application.appId, stage);
848− }
849
851
850 function refreshStage() {
852 function refreshStage() {
851 var newStage = ApplicationInfoInterface.MainStage;
853 var newStage = ApplicationInfoInterface.MainStage;
852− if (priv.sideStageEnabled) {
854+ if (priv.sideStageEnabled) { // we're in lanscape rotation.
853− if (application && application.supportedOrientations & (Qt.PortraitOrientation|Qt.InvertedPortraitOrientation)) {
855+ if (!isDash && application && application.supportedOrientations & (Qt.PortraitOrientation|Qt.InvertedPortraitOrientation)) {
856+ var defaultStage = ApplicationInfoInterface.SideStage; // if application supports portrait, it defaults to sidestage.
857+ if (application.supportedOrientations & (Qt.LandscapeOrientation|Qt.InvertedLandscapeOrientation)) {
858+ // if it supports lanscape, it defaults to mainstage.
859+ defaultStage = ApplicationInfoInterface.MainStage;
860+ }
854− newStage = WindowStateStorage.getStage(application.appId);
861+ newStage = WindowStateStorage.getStage(application.appId, defaultStage);
855 }
862 }
856 }
863 }
857
864
@@ -1085,6 +1092,7 @@ AbstractStage {
1085 // only accept opposite stage.
1092 // only accept opposite stage.
1086 Drag.keys: {
1093 Drag.keys: {
1087 if (!surface) return "Disabled";
1094 if (!surface) return "Disabled";
1095+ if (spreadDelegate.isDash) return "Disabled";
1088
1096
1089 if (spreadDelegate.stage === ApplicationInfo.MainStage) {
1097 if (spreadDelegate.stage === ApplicationInfo.MainStage) {
1090 if (spreadDelegate.application.supportedOrientations
1098 if (spreadDelegate.application.supportedOrientations
qml/Stages/WindowControlsOverlay.qml+2−2
@@ -94,8 +94,8 @@ Item {
94 function handlePositionChanged(mouseX, mouseY) {
94 function handlePositionChanged(mouseX, mouseY) {
95 if (priv.dragging) {
95 if (priv.dragging) {
96 var pos = mapToItem(root.target.parent, mouseX, mouseY);
96 var pos = mapToItem(root.target.parent, mouseX, mouseY);
97− root.target.x = Math.round(pos.x - priv.distanceX);
97+ root.target.requestedX = Math.round(pos.x - priv.distanceX);
98− root.target.y = Math.round(Math.max(pos.y - priv.distanceY, PanelState.panelHeight));
98+ root.target.requestedY = Math.round(Math.max(pos.y - priv.distanceY, PanelState.panelHeight));
99 }
99 }
100 }
100 }
101 }
101 }
qml/Stages/WindowDecoration.qml+13−6
@@ -26,18 +26,22 @@ MouseArea {
26
26
27 property Item target
27 property Item target
28 property alias title: titleLabel.text
28 property alias title: titleLabel.text
29+ property alias maximizeButtonShown: buttons.maximizeButtonShown
29 property bool active: false
30 property bool active: false
31+ acceptedButtons: Qt.AllButtons // prevent leaking unhandled mouse events
30 property alias overlayShown: buttons.overlayShown
32 property alias overlayShown: buttons.overlayShown
31
33
32− hoverEnabled: true
33−
34 signal closeClicked()
34 signal closeClicked()
35 signal minimizeClicked()
35 signal minimizeClicked()
36 signal maximizeClicked()
36 signal maximizeClicked()
37 signal maximizeHorizontallyClicked()
37 signal maximizeHorizontallyClicked()
38 signal maximizeVerticallyClicked()
38 signal maximizeVerticallyClicked()
39
39
40+ onDoubleClicked: {
41+ if (maximizeButtonShown && mouse.button == Qt.LeftButton) {
40− onDoubleClicked: root.maximizeClicked()
42+ root.maximizeClicked();
43+ }
44+ }
41
45
42 QtObject {
46 QtObject {
43 id: priv
47 id: priv
@@ -47,7 +51,7 @@ MouseArea {
47 }
51 }
48
52
49 onPressedChanged: {
53 onPressedChanged: {
50− if (pressed) {
54+ if (pressed && pressedButtons == Qt.LeftButton) {
51 var pos = mapToItem(root.target, mouseX, mouseY);
55 var pos = mapToItem(root.target, mouseX, mouseY);
52 priv.distanceX = pos.x;
56 priv.distanceX = pos.x;
53 priv.distanceY = pos.y;
57 priv.distanceY = pos.y;
@@ -65,11 +69,14 @@ MouseArea {
65 // Use integer coordinate values to ensure that target is left in a pixel-aligned
69 // Use integer coordinate values to ensure that target is left in a pixel-aligned
66 // position. Mouse movement could have subpixel precision, yielding a fractional
70 // position. Mouse movement could have subpixel precision, yielding a fractional
67 // mouse position.
71 // mouse position.
68− root.target.x = Math.round(pos.x - priv.distanceX);
72+ root.target.requestedX = Math.round(pos.x - priv.distanceX);
69− root.target.y = Math.round(Math.max(pos.y - priv.distanceY, PanelState.panelHeight));
73+ root.target.requestedY = Math.round(Math.max(pos.y - priv.distanceY, PanelState.panelHeight));
70 }
74 }
71 }
75 }
72
76
77+ // do not let unhandled wheel event pass thru the decoration
78+ onWheel: wheel.accepted = true;
79+
73 Rectangle {
80 Rectangle {
74 anchors.fill: parent
81 anchors.fill: parent
75 anchors.bottomMargin: -radius
82 anchors.bottomMargin: -radius
qml/Stages/WindowResizeArea.qml+11−11
@@ -55,8 +55,8 @@ MouseArea {
55
55
56 function updateNormalGeometry() {
56 function updateNormalGeometry() {
57 if (root.target.state == "normal") {
57 if (root.target.state == "normal") {
58− normalX = root.target.x
58+ normalX = root.target.requestedX
59− normalY = root.target.y
59+ normalY = root.target.requestedY
60 normalWidth = root.target.width
60 normalWidth = root.target.width
61 normalHeight = root.target.height
61 normalHeight = root.target.height
62 }
62 }
@@ -73,14 +73,14 @@ MouseArea {
73
73
74 function loadWindowState() {
74 function loadWindowState() {
75 var windowGeometry = windowStateStorage.getGeometry(root.windowId,
75 var windowGeometry = windowStateStorage.getGeometry(root.windowId,
76− Qt.rect(target.x, target.y, defaultWidth, defaultHeight));
76+ Qt.rect(target.requestedX, target.requestedY, defaultWidth, defaultHeight));
77
77
78 target.requestedWidth = Qt.binding(function() { return Math.min(Math.max(windowGeometry.width, d.minimumWidth), screenWidth - root.leftMargin); });
78 target.requestedWidth = Qt.binding(function() { return Math.min(Math.max(windowGeometry.width, d.minimumWidth), screenWidth - root.leftMargin); });
79 target.requestedHeight = Qt.binding(function() { return Math.min(Math.max(windowGeometry.height, d.minimumHeight),
79 target.requestedHeight = Qt.binding(function() { return Math.min(Math.max(windowGeometry.height, d.minimumHeight),
80 root.screenHeight - (target.fullscreen ? 0 : PanelState.panelHeight)); });
80 root.screenHeight - (target.fullscreen ? 0 : PanelState.panelHeight)); });
81− target.x = Qt.binding(function() { return Math.max(Math.min(windowGeometry.x, root.screenWidth - root.leftMargin - target.requestedWidth),
81+ target.requestedX = Qt.binding(function() { return Math.max(Math.min(windowGeometry.x, root.screenWidth - root.leftMargin - target.requestedWidth),
82 (target.fullscreen ? 0 : root.leftMargin)); });
82 (target.fullscreen ? 0 : root.leftMargin)); });
83− target.y = Qt.binding(function() { return Math.max(Math.min(windowGeometry.y, root.screenHeight - target.requestedHeight), PanelState.panelHeight); });
83+ target.requestedY = Qt.binding(function() { return Math.max(Math.min(windowGeometry.y, root.screenHeight - target.requestedHeight), PanelState.panelHeight); });
84
84
85 var windowState = windowStateStorage.getState(root.windowId, WindowStateStorage.WindowStateNormal)
85 var windowState = windowStateStorage.getState(root.windowId, WindowStateStorage.WindowStateNormal)
86 switch (windowState) {
86 switch (windowState) {
@@ -258,8 +258,8 @@ MouseArea {
258 var pos = mapToItem(root.target.parent, mouseX, mouseY);
258 var pos = mapToItem(root.target.parent, mouseX, mouseY);
259 d.startMousePosX = pos.x;
259 d.startMousePosX = pos.x;
260 d.startMousePosY = pos.y;
260 d.startMousePosY = pos.y;
261− d.startX = target.x;
261+ d.startX = target.requestedX;
262− d.startY = target.y;
262+ d.startY = target.requestedY;
263 d.startWidth = target.width;
263 d.startWidth = target.width;
264 d.startHeight = target.height;
264 d.startHeight = target.height;
265 d.currentWidth = target.width;
265 d.currentWidth = target.width;
@@ -296,7 +296,7 @@ MouseArea {
296
296
297 if (d.leftBorder) {
297 if (d.leftBorder) {
298 var newTargetX = d.startX + deltaX;
298 var newTargetX = d.startX + deltaX;
299− var rightBorderX = target.x + target.width;
299+ var rightBorderX = target.requestedX + target.width;
300 if (rightBorderX > newTargetX + d.minimumWidth) {
300 if (rightBorderX > newTargetX + d.minimumWidth) {
301 if (rightBorderX < newTargetX + d.maximumWidth) {
301 if (rightBorderX < newTargetX + d.maximumWidth) {
302 target.requestedWidth = rightBorderX - newTargetX;
302 target.requestedWidth = rightBorderX - newTargetX;
@@ -322,7 +322,7 @@ MouseArea {
322
322
323 if (d.topBorder) {
323 if (d.topBorder) {
324 var newTargetY = Math.max(d.startY + deltaY, PanelState.panelHeight); // disallow resizing up past Panel
324 var newTargetY = Math.max(d.startY + deltaY, PanelState.panelHeight); // disallow resizing up past Panel
325− var bottomBorderY = target.y + target.height;
325+ var bottomBorderY = target.requestedY + target.height;
326 if (bottomBorderY > newTargetY + d.minimumHeight) {
326 if (bottomBorderY > newTargetY + d.minimumHeight) {
327 if (bottomBorderY < newTargetY + d.maximumHeight) {
327 if (bottomBorderY < newTargetY + d.maximumHeight) {
328 target.requestedHeight = bottomBorderY - newTargetY;
328 target.requestedHeight = bottomBorderY - newTargetY;
@@ -351,13 +351,13 @@ MouseArea {
351 target: root.target
351 target: root.target
352 onWidthChanged: {
352 onWidthChanged: {
353 if (d.moveLeftBorder) {
353 if (d.moveLeftBorder) {
354− target.x += d.currentWidth - target.width;
354+ target.requestedX += d.currentWidth - target.width;
355 }
355 }
356 d.currentWidth = target.width;
356 d.currentWidth = target.width;
357 }
357 }
358 onHeightChanged: {
358 onHeightChanged: {
359 if (d.moveTopBorder) {
359 if (d.moveTopBorder) {
360− target.y += d.currentHeight - target.height;
360+ target.requestedY += d.currentHeight - target.height;
361 }
361 }
362 d.currentHeight = target.height;
362 d.currentHeight = target.height;
363 }
363 }
qml/Stages/graphics/PageHeaderBaseDividerBottom@18.png

Binary file not shown.

qml/Stages/graphics/PageHeaderBaseDividerLight@18.png

Binary file not shown.

qml/Stages/graphics/dropshadow2gu@30.png

Binary file not shown.

qml/Stages/graphics/sidestage_handle@20.png

Binary file not shown.

qml/Tutorial/graphics/background1.png

Binary file not shown.

qml/Tutorial/graphics/background2.png

Binary file not shown.

qml/Wizard/Pages/data/Desktop_header_bkg.png

Binary file not shown.

qml/Wizard/Pages/data/Desktop_splash_screen_bkg.png

Binary file not shown.

qml/Wizard/Pages/data/Phone_header_bkg.png

Binary file not shown.

qml/Wizard/Pages/data/Phone_splash_screen_bkg.png

Binary file not shown.

qml/Wizard/Pages/data/Tick@30.png

Binary file not shown.

qml/Wizard/Pages/data/timezonemap/map.png

Binary file not shown.

qml/Wizard/Pages/data/timezonemap/pin.png

Binary file not shown.

qml/Wizard/Pages/data/timezonemap/timezone_-1.png

Binary file not shown.

qml/Wizard/Pages/data/timezonemap/timezone_-10.png

Binary file not shown.

qml/Wizard/Pages/data/timezonemap/timezone_-11.png

Binary file not shown.

qml/Wizard/Pages/data/timezonemap/timezone_-2.png

Binary file not shown.

qml/Wizard/Pages/data/timezonemap/timezone_-3.5.png

Binary file not shown.

qml/Wizard/Pages/data/timezonemap/timezone_-3.png

Binary file not shown.

qml/Wizard/Pages/data/timezonemap/timezone_-4.5.png

Binary file not shown.

qml/Wizard/Pages/data/timezonemap/timezone_-4.png

Binary file not shown.

qml/Wizard/Pages/data/timezonemap/timezone_-5.png

Binary file not shown.

qml/Wizard/Pages/data/timezonemap/timezone_-6.png

Binary file not shown.

qml/Wizard/Pages/data/timezonemap/timezone_-7.png

Binary file not shown.

qml/Wizard/Pages/data/timezonemap/timezone_-8.png

Binary file not shown.

qml/Wizard/Pages/data/timezonemap/timezone_-9.5.png

Binary file not shown.

qml/Wizard/Pages/data/timezonemap/timezone_-9.png

Binary file not shown.

qml/Wizard/Pages/data/timezonemap/timezone_0.png

Binary file not shown.

qml/Wizard/Pages/data/timezonemap/timezone_1.png

Binary file not shown.

qml/Wizard/Pages/data/timezonemap/timezone_10.5.png

Binary file not shown.

qml/Wizard/Pages/data/timezonemap/timezone_10.png

Binary file not shown.

qml/Wizard/Pages/data/timezonemap/timezone_11.png

Binary file not shown.

qml/Wizard/Pages/data/timezonemap/timezone_12.75.png

Binary file not shown.

qml/Wizard/Pages/data/timezonemap/timezone_12.png

Binary file not shown.

qml/Wizard/Pages/data/timezonemap/timezone_13.png

Binary file not shown.

qml/Wizard/Pages/data/timezonemap/timezone_2.png

Binary file not shown.

qml/Wizard/Pages/data/timezonemap/timezone_3.5.png

Binary file not shown.

qml/Wizard/Pages/data/timezonemap/timezone_3.png

Binary file not shown.

qml/Wizard/Pages/data/timezonemap/timezone_4.5.png

Binary file not shown.

qml/Wizard/Pages/data/timezonemap/timezone_4.png

Binary file not shown.

qml/Wizard/Pages/data/timezonemap/timezone_5.5.png

Binary file not shown.

qml/Wizard/Pages/data/timezonemap/timezone_5.75.png

Binary file not shown.

qml/Wizard/Pages/data/timezonemap/timezone_5.png

Binary file not shown.

qml/Wizard/Pages/data/timezonemap/timezone_6.5.png

Binary file not shown.

qml/Wizard/Pages/data/timezonemap/timezone_6.png

Binary file not shown.

qml/Wizard/Pages/data/timezonemap/timezone_7.png

Binary file not shown.

qml/Wizard/Pages/data/timezonemap/timezone_8.5.png

Binary file not shown.

qml/Wizard/Pages/data/timezonemap/timezone_8.png

Binary file not shown.

qml/Wizard/Pages/data/timezonemap/timezone_9.5.png

Binary file not shown.

qml/Wizard/Pages/data/timezonemap/timezone_9.png

Binary file not shown.

qml/Wizard/Pages/passcode-desktop.qml+27−10
@@ -33,15 +33,18 @@ LocalComponents.Page {
33 readonly property alias password: passwordField.text
33 readonly property alias password: passwordField.text
34 readonly property alias password2: password2Field.text
34 readonly property alias password2: password2Field.text
35
35
36− GridLayout {
36+ Flickable {
37 id: column
37 id: column
38− columns: 1
38+ clip: true
39− rows: 3
39+ flickableDirection: Flickable.VerticalFlick
40 anchors.fill: content
40 anchors.fill: content
41− anchors.leftMargin: leftMargin
41+ anchors.leftMargin: parent.leftMargin
42− anchors.rightMargin: rightMargin
42+ anchors.rightMargin: parent.rightMargin
43 anchors.topMargin: customMargin
43 anchors.topMargin: customMargin
44− rowSpacing: units.gu(3)
44+
45+ bottomMargin: Qt.inputMethod.keyboardRectangle.height - height - customMargin
46+
47+ Behavior on contentY { UbuntuNumberAnimation {} }
45
48
46 Label {
49 Label {
47 id: infoLabel
50 id: infoLabel
@@ -49,6 +52,7 @@ LocalComponents.Page {
49 anchors {
52 anchors {
50 left: parent.left
53 left: parent.left
51 right: parent.right
54 right: parent.right
55+ top: parent.top
52 }
56 }
53 wrapMode: Text.Wrap
57 wrapMode: Text.Wrap
54 font.weight: Font.Light
58 font.weight: Font.Light
@@ -57,6 +61,13 @@ LocalComponents.Page {
57 }
61 }
58
62
59 GridLayout {
63 GridLayout {
64+ anchors {
65+ left: parent.left
66+ right: parent.right
67+ top: infoLabel.bottom
68+ topMargin: units.gu(3)
69+ }
70+
60 columns: 2
71 columns: 2
61 columnSpacing: units.gu(2)
72 columnSpacing: units.gu(2)
62 rowSpacing: units.gu(2)
73 rowSpacing: units.gu(2)
@@ -74,6 +85,11 @@ LocalComponents.Page {
74 validator: RegExpValidator { regExp: /^\d{4}$/ }
85 validator: RegExpValidator { regExp: /^\d{4}$/ }
75 maximumLength: 4
86 maximumLength: 4
76 onAccepted: password2Field.forceActiveFocus()
87 onAccepted: password2Field.forceActiveFocus()
88+ onActiveFocusChanged: {
89+ if (activeFocus) {
90+ column.contentY = y
91+ }
92+ }
77 }
93 }
78
94
79 Label {
95 Label {
@@ -88,6 +104,11 @@ LocalComponents.Page {
88 inputMethodHints: Qt.ImhDigitsOnly
104 inputMethodHints: Qt.ImhDigitsOnly
89 validator: RegExpValidator { regExp: /^\d{4}$/ }
105 validator: RegExpValidator { regExp: /^\d{4}$/ }
90 maximumLength: 4
106 maximumLength: 4
107+ onActiveFocusChanged: {
108+ if (activeFocus) {
109+ column.contentY = y
110+ }
111+ }
91 }
112 }
92
113
93 Label {
114 Label {
@@ -112,10 +133,6 @@ LocalComponents.Page {
112 }
133 }
113 }
134 }
114 }
135 }
115−
116− Item { // spacer
117− Layout.fillHeight: true
118− }
119 }
136 }
120
137
121 Component {
138 Component {
qml/Wizard/PasswordMeter.qml+3−1
@@ -83,7 +83,9 @@ Item {
83 wrapMode: Text.Wrap
83 wrapMode: Text.Wrap
84 text: {
84 text: {
85 if (matching !== undefined) {
85 if (matching !== undefined) {
86+ if (password.length < 8)
87+ return i18n.tr("Password too short");
86− if (matching)
88+ else if (matching)
87 return i18n.tr("Passwords match");
89 return i18n.tr("Passwords match");
88 else if (!matching)
90 else if (!matching)
89 return i18n.tr("Passwords do not match");
91 return i18n.tr("Passwords do not match");
qml/graphics/dropshadow_left@20.png

Binary file not shown.

qml/graphics/dropshadow_right@20.png

Binary file not shown.

src/CMakeLists.txt+1
@@ -1,6 +1,7 @@
1pkg_check_modules(CONNECTIVITY REQUIRED connectivity-qt1)
1pkg_check_modules(CONNECTIVITY REQUIRED connectivity-qt1)
2
2
3include_directories(
3include_directories(
4+ SYSTEM
4 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
5 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
5 ${CONNECTIVITY_INCLUDE_DIRS}
6 ${CONNECTIVITY_INCLUDE_DIRS}
6)
7)
src/Dash/main.cpp+22−21
@@ -25,6 +25,7 @@
25#include <QCommandLineParser>
25#include <QCommandLineParser>
26#include <QLibrary>
26#include <QLibrary>
27#include <libintl.h>
27#include <libintl.h>
28+#include <QQmlApplicationEngine>
28
29
29#include <paths.h>
30#include <paths.h>
30#include "../qmldebuggerutils.h"
31#include "../qmldebuggerutils.h"
@@ -83,40 +84,40 @@ int main(int argc, const char *argv[])
83 bindtextdomain("unity8", translationDirectory().toUtf8().data());
84 bindtextdomain("unity8", translationDirectory().toUtf8().data());
84 textdomain("unity8");
85 textdomain("unity8");
85
86
86− QQuickView* view = new QQuickView();
87+ #ifdef UNITY8_ENABLE_TOUCH_EMULATION
87− view->setResizeMode(QQuickView::SizeRootObjectToView);
88+ // You will need this if you want to interact with touch-only components using a mouse
89+ // Needed only when manually testing on a desktop.
90+ MouseTouchAdaptor *mouseTouchAdaptor = 0;
91+ if (parser.isSet(mousetouchOption)) {
92+ mouseTouchAdaptor = MouseTouchAdaptor::instance();
93+ }
94+ #endif
88
95
96+ QQmlApplicationEngine *engine = new QQmlApplicationEngine(application);
97+
98+ int initialWidth = -1;
99+ int initialHeight = -1;
89 if (parser.isSet(windowGeometryOption) &&
100 if (parser.isSet(windowGeometryOption) &&
90 parser.value(windowGeometryOption).split('x').size() == 2)
101 parser.value(windowGeometryOption).split('x').size() == 2)
91 {
102 {
92 QStringList geom = parser.value(windowGeometryOption).split('x');
103 QStringList geom = parser.value(windowGeometryOption).split('x');
93 QSize windowSize(geom.at(0).toInt(), geom.at(1).toInt());
104 QSize windowSize(geom.at(0).toInt(), geom.at(1).toInt());
94 if (windowSize.isValid()) {
105 if (windowSize.isValid()) {
95− view->setWidth(windowSize.width());
106+ initialWidth = windowSize.width();
96− view->setHeight(windowSize.height());
107+ initialHeight = windowSize.height();
97 }
108 }
98 }
109 }
99−
110+ engine->rootContext()->setContextProperty("initialWidth", initialWidth);
100− view->setTitle(QStringLiteral("Scopes"));
111+ engine->rootContext()->setContextProperty("initialHeight", initialHeight);
101−
102− #ifdef UNITY8_ENABLE_TOUCH_EMULATION
103− // You will need this if you want to interact with touch-only components using a mouse
104− // Needed only when manually testing on a desktop.
105− MouseTouchAdaptor *mouseTouchAdaptor = 0;
106− if (parser.isSet(mousetouchOption)) {
107− mouseTouchAdaptor = MouseTouchAdaptor::instance();
108− }
109− #endif
110
112
111 QUrl source(::qmlDirectory() + "/Dash/DashApplication.qml");
113 QUrl source(::qmlDirectory() + "/Dash/DashApplication.qml");
112− prependImportPaths(view->engine(), ::overrideImportPaths());
114+ prependImportPaths(engine, ::overrideImportPaths());
113− appendImportPaths(view->engine(), ::fallbackImportPaths());
115+ appendImportPaths(engine, ::fallbackImportPaths());
114
116
115 CachingNetworkManagerFactory *managerFactory = new CachingNetworkManagerFactory();
117 CachingNetworkManagerFactory *managerFactory = new CachingNetworkManagerFactory();
116− view->engine()->setNetworkAccessManagerFactory(managerFactory);
118+ engine->setNetworkAccessManagerFactory(managerFactory);
117
119
118− view->setSource(source);
120+ engine->load(source);
119− view->show();
120
121
121 UnixSignalHandler signalHandler([]{
122 UnixSignalHandler signalHandler([]{
122 QGuiApplication::exit(0);
123 QGuiApplication::exit(0);
@@ -125,7 +126,7 @@ int main(int argc, const char *argv[])
125
126
126 int result = application->exec();
127 int result = application->exec();
127
128
128− delete view;
129+ delete engine;
129
130
130 #ifdef UNITY8_ENABLE_TOUCH_EMULATION
131 #ifdef UNITY8_ENABLE_TOUCH_EMULATION
131 delete mouseTouchAdaptor;
132 delete mouseTouchAdaptor;
tests/CMakeLists.txt+6
@@ -126,6 +126,12 @@ function(add_unity8_qmlbenchmark PATH COMPONENT_NAME ITERATIONS)
126 add_unity8_qmltest(${PATH} ${COMPONENT_NAME} ITERATIONS ${ITERATIONS} ${ARGN})
126 add_unity8_qmltest(${PATH} ${COMPONENT_NAME} ITERATIONS ${ITERATIONS} ${ARGN})
127endfunction()
127endfunction()
128
128
129+# used to make dbus adaptor classes from xml to mock system services
130+if(DEFINED ENV{DEB_BUILD_MULTIARCH})
131+ set(QDBUSXML2CPP_EXECUTABLE "/usr/lib/$ENV{DEB_BUILD_MULTIARCH}/qt5/bin/qdbusxml2cpp")
132+else()
133+ FIND_PROGRAM(QDBUSXML2CPP_EXECUTABLE qdbusxml2cpp)
134+endif()
129
135
130# Actual test definitions
136# Actual test definitions
131add_subdirectory(plugins)
137add_subdirectory(plugins)
tests/data/unity/backgrounds/blue.png

Binary file not shown.

tests/data/unity/backgrounds/red.png

Binary file not shown.

tests/graphics/applicationIcons/browser@18.png

Binary file not shown.

tests/graphics/applicationIcons/calendar@18.png

Binary file not shown.

tests/graphics/applicationIcons/camera@18.png

Binary file not shown.

tests/graphics/applicationIcons/contacts-app@18.png

Binary file not shown.

tests/graphics/applicationIcons/dash.png

Binary file not shown.

tests/graphics/applicationIcons/dialer-app@18.png

Binary file not shown.

tests/graphics/applicationIcons/evernote@18.png

Binary file not shown.

tests/graphics/applicationIcons/facebook@18.png

Binary file not shown.

tests/graphics/applicationIcons/gallery@18.png

Binary file not shown.

tests/graphics/applicationIcons/gmail@18.png

Binary file not shown.

tests/graphics/applicationIcons/libreoffice@18.png

Binary file not shown.

tests/graphics/applicationIcons/map@18.png

Binary file not shown.

tests/graphics/applicationIcons/messages-app@18.png

Binary file not shown.

tests/graphics/applicationIcons/notepad@18.png

Binary file not shown.

tests/graphics/applicationIcons/pinterest@18.png

Binary file not shown.

tests/graphics/applicationIcons/soundcloud@18.png

Binary file not shown.

tests/graphics/applicationIcons/system-settings@18.png

Binary file not shown.

tests/graphics/applicationIcons/twitter@18.png

Binary file not shown.

tests/graphics/applicationIcons/weather@18.png

Binary file not shown.

tests/graphics/applicationIcons/wikipedia@18.png

Binary file not shown.

tests/graphics/applicationIcons/youtube@18.png

Binary file not shown.

tests/graphics/avatars/amanda@12.png

Binary file not shown.

tests/graphics/avatars/anna_olsson@12.png

Binary file not shown.

tests/graphics/avatars/funky@12.png

Binary file not shown.

tests/graphics/clock@18.png

Binary file not shown.

tests/mocks/QtMultimedia/CMakeLists.txt+8−2
@@ -1,9 +1,15 @@
1+include_directories(
2+ ${CMAKE_CURRENT_SOURCE_DIR}
3+ ${CMAKE_CURRENT_BINARY_DIR}
4+)
5+
1add_library(QtMultimedia-qml MODULE
6add_library(QtMultimedia-qml MODULE
2 plugin.cpp
7 plugin.cpp
3− audio.cpp
8+ mediaplayer.cpp
9+ videooutput.cpp
4 declarativeplaylist.cpp
10 declarativeplaylist.cpp
5 )
11 )
6
12
7−qt5_use_modules(QtMultimedia-qml Qml)
13+qt5_use_modules(QtMultimedia-qml Qml Quick)
8
14
9add_unity8_mock(QtMultimedia 5.0 QtMultimedia TARGETS QtMultimedia-qml)
15add_unity8_mock(QtMultimedia 5.0 QtMultimedia TARGETS QtMultimedia-qml)
tests/mocks/QtMultimedia/VideoSurface.qmlnew file+111
@@ -0,0 +1,111 @@
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+import Ubuntu.Components 1.3
19+import QtMultimedia 5.0
20+
21+Rectangle {
22+ id: root
23+ property int position: 0
24+ property int playbackState: 0
25+ property var resolution: { "width": 16, "height": 9 }
26+
27+ anchors.centerIn: parent
28+ color: "black"
29+
30+ // scale to the parent window.
31+ implicitWidth: parent ? aspectScale(Qt.size(resolution.width, resolution.height), Qt.size(parent.width, parent.height)).width : 160
32+ implicitHeight: parent ? aspectScale(Qt.size(resolution.width, resolution.height), Qt.size(parent.width, parent.height)).height : 90
33+
34+ function aspectScale(fromSize, toSize) {
35+ var rw = toSize.height * fromSize.width / fromSize.height;
36+ var useHeight = (rw <= toSize.width);
37+
38+ if (useHeight) {
39+ return Qt.size(rw, toSize.height);
40+ } else {
41+ return Qt.size(toSize.width,
42+ toSize.width * (fromSize.height / fromSize.width));
43+ }
44+ }
45+
46+ GridView {
47+ anchors.fill: parent
48+ model: (parent.width / units.gu(5)) * ((parent.height / units.gu(5) + 1))
49+ clip: true
50+
51+ cellHeight: units.gu(5)
52+ cellWidth: units.gu(5)
53+
54+ delegate: Item {
55+ width: units.gu(5)
56+ height: units.gu(5)
57+
58+ Rectangle {
59+ id: rect
60+ color: "red"
61+ anchors.fill: parent
62+ anchors.margins: units.gu(1)
63+
64+ Connections {
65+ target: root
66+ onPositionChanged: {
67+ rect.rotation = position/100
68+ }
69+ }
70+ }
71+ }
72+ }
73+
74+ Column {
75+ anchors {
76+ left: parent.left;
77+ right: parent.right
78+ verticalCenter: parent.verticalCenter
79+ }
80+ height: units.gu(10)
81+
82+ Label {
83+ anchors.horizontalCenter: parent.horizontalCenter
84+ text: {
85+ var pos = (position/1000).toFixed(0)
86+
87+ var m = Math.floor(pos/60);
88+ var ss = pos % 60;
89+ if (ss >= 10) {
90+ return m + ":" + ss;
91+ } else {
92+ return m + ":0" + ss;
93+ }
94+ }
95+ fontSize: "x-large"
96+ }
97+
98+ Label {
99+ anchors.horizontalCenter: parent.horizontalCenter
100+ text: getPlaybackState(playbackState)
101+ fontSize: "x-large"
102+ }
103+ }
104+
105+ function getPlaybackState(playbackState) {
106+ if (playbackState === MediaPlayer.PlayingState) return "Playing";
107+ else if (playbackState === MediaPlayer.PausedState) return "Paused";
108+ else if (playbackState === MediaPlayer.StoppedState) return "Stopped";
109+ return "";
110+ }
111+}
tests/mocks/QtMultimedia/audio.cppdeleted−132
@@ -1,132 +0,0 @@
1−/*
2− * Copyright (C) 2013-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− * Author: Michael Zanetti <michael.zanetti@canonical.com>
17− */
18−
19−#include "audio.h"
20−
21−Audio::Audio(QObject* parent):
22− QObject(parent),
23− m_playbackState(StoppedState),
24− m_playlist(nullptr)
25−{
26− qsrand(time(nullptr));
27− m_timer.setInterval(1000);
28− connect(&m_timer, &QTimer::timeout, this, &Audio::timerEvent);
29−
30− m_position = 0;
31− Q_EMIT positionChanged(m_position);
32−
33− m_duration = (qrand() % 20000) + 10000;
34− Q_EMIT durationChanged(m_duration);
35−}
36−
37−QUrl Audio::source() const
38−{
39− return m_source;
40−}
41−
42−void Audio::setSource(const QUrl &source)
43−{
44− if (m_source != source) {
45− m_source = source;
46− Q_EMIT sourceChanged(source);
47− }
48−}
49−
50−Audio::PlaybackState Audio::playbackState() const
51−{
52− return m_playbackState;
53−}
54−
55−int Audio::position() const
56−{
57− return m_position;
58−}
59−
60−int Audio::duration() const
61−{
62− return m_duration;
63−}
64−
65−QString Audio::errorString() const
66−{
67− return QString();
68−}
69−
70−void Audio::pause()
71−{
72− if (m_playbackState == PlayingState) {
73− m_playbackState = PausedState;
74− Q_EMIT playbackStateChanged(m_playbackState);
75− m_timer.stop();
76− }
77−}
78−
79−void Audio::play()
80−{
81− if (m_playbackState != PlayingState) {
82− m_playbackState = PlayingState;
83− Q_EMIT playbackStateChanged(m_playbackState);
84−
85− m_timer.start();
86− }
87−}
88−
89−void Audio::stop()
90−{
91− if (m_playbackState != StoppedState) {
92− m_playbackState = StoppedState;
93− Q_EMIT playbackStateChanged(m_playbackState);
94− m_timer.stop();
95− m_position = 0;
96− Q_EMIT positionChanged(m_position);
97− }
98−}
99−
100−void Audio::timerEvent()
101−{
102− if (m_position + 1000 < m_duration) {
103− m_position += 1000;
104− Q_EMIT positionChanged(m_position);
105− } else {
106− stop();
107− }
108−}
109−
110−Audio::AudioRole Audio::audioRole() const
111−{
112− return Audio::MusicRole;
113−}
114−
115−void Audio::setAudioRole(Audio::AudioRole audioRole)
116−{
117− Q_UNUSED(audioRole);
118−}
119−
120−DeclarativePlaylist *Audio::playlist() const
121−{
122− return m_playlist;
123−}
124−
125−void Audio::setPlaylist(DeclarativePlaylist *playlist)
126−{
127− if (playlist == m_playlist)
128− return;
129−
130− m_playlist = playlist;
131− Q_EMIT playlistChanged();
132−}
tests/mocks/QtMultimedia/audio.hdeleted−103
@@ -1,103 +0,0 @@
1−/*
2− * Copyright (C) 2012-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−
18−#ifndef MOCK_AUDIO_H
19−#define MOCK_AUDIO_H
20−
21−#include <QObject>
22−#include <QUrl>
23−#include <QTimer>
24−
25−class DeclarativePlaylist;
26−
27−class Audio: public QObject
28−{
29− Q_OBJECT
30− Q_ENUMS(PlaybackState)
31− Q_ENUMS(AudioRole)
32− Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
33− Q_PROPERTY(PlaybackState playbackState READ playbackState NOTIFY playbackStateChanged)
34− Q_PROPERTY(int position READ position NOTIFY positionChanged)
35− Q_PROPERTY(int duration READ duration NOTIFY durationChanged)
36− Q_PROPERTY(QString errorString READ errorString NOTIFY errorStringChanged)
37− Q_PROPERTY(AudioRole audioRole READ audioRole WRITE setAudioRole)
38− Q_PROPERTY(DeclarativePlaylist *playlist READ playlist WRITE setPlaylist NOTIFY playlistChanged)
39−public:
40− enum PlaybackState {
41− PlayingState,
42− PausedState,
43− StoppedState
44− };
45−
46− enum AudioRole {
47− UnknownRole,
48− MusicRole,
49− VideoRole,
50− VoiceCommunicationRole,
51− AlarmRole,
52− NotificationRole,
53− RingtoneRole,
54− AccessibilityRole,
55− SonificationRole,
56− GameRole
57− };
58−
59− explicit Audio(QObject *parent = nullptr);
60−
61− QUrl source() const;
62− void setSource(const QUrl &source);
63−
64− DeclarativePlaylist *playlist() const;
65− void setPlaylist(DeclarativePlaylist *playlist);
66−
67− PlaybackState playbackState() const;
68−
69− int position() const;
70−
71− int duration() const;
72−
73− QString errorString() const;
74−
75− AudioRole audioRole() const;
76− void setAudioRole(AudioRole audioRole);
77−
78−public Q_SLOTS:
79− void pause();
80− void play();
81− void stop();
82−
83−Q_SIGNALS:
84− void playlistChanged();
85− void sourceChanged(const QUrl &source);
86− void playbackStateChanged(PlaybackState playbackState);
87− void positionChanged(int position);
88− void durationChanged(int duration);
89− void errorStringChanged(const QString &errorString);
90−
91−private Q_SLOTS:
92− void timerEvent();
93−
94−private:
95− QUrl m_source;
96− PlaybackState m_playbackState;
97− QTimer m_timer;
98− int m_position;
99− int m_duration;
100− DeclarativePlaylist *m_playlist;
101−};
102−
103−#endif
tests/mocks/QtMultimedia/mediaplayer.cppnew file+318
@@ -0,0 +1,318 @@
1+/*
2+ * Copyright (C) 2013-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+ * Author: Michael Zanetti <michael.zanetti@canonical.com>
17+ */
18+
19+#include "mediaplayer.h"
20+
21+#include <QDebug>
22+#include <QSize>
23+
24+class MetaDataObject: public QObject
25+{
26+ Q_OBJECT
27+ Q_PROPERTY(QVariant title READ title NOTIFY metaDataChanged)
28+ Q_PROPERTY(QVariant resolution READ resolution NOTIFY metaDataChanged)
29+public:
30+ MetaDataObject(MediaPlayer* parent = nullptr)
31+ : QObject(parent)
32+ , m_source(parent)
33+ {
34+ }
35+
36+ QVariant title() const { return getProperty("title"); }
37+ QVariant resolution() const { return getProperty("resolution", QSize(640, 640)); }
38+
39+ QVariant getProperty(const QString& property, QVariant defaultValue = QVariant()) const {
40+ MediaDataSource* sourceData = MediaPlayerDataController::instance()->dataForSource(m_source->source());
41+ QVariant metaData = sourceData->metaData();
42+ return metaData.toMap().value(property, defaultValue);
43+ }
44+
45+Q_SIGNALS:
46+ void metaDataChanged();
47+
48+private:
49+ MediaPlayer* m_source;
50+};
51+
52+MediaPlayer::MediaPlayer(QObject* parent)
53+ : QObject(parent)
54+ , m_playbackState(StoppedState)
55+ , m_status(NoMedia)
56+ , m_metaData(new MetaDataObject(this))
57+ , m_playlist(nullptr)
58+{
59+ qsrand(time(nullptr));
60+ m_timer.setInterval(100);
61+ connect(&m_timer, &QTimer::timeout, this, &MediaPlayer::timerEvent);
62+
63+ connect(MediaPlayerDataController::instance(), &MediaPlayerDataController::sourceAboutToBeRemoved,
64+ this, [this](const QUrl& source) {
65+ MediaDataSource* dataSource = MediaPlayerDataController::instance()->dataForSource(source);
66+ if (!dataSource) return;
67+
68+ disconnect(dataSource, &MediaDataSource::durationChanged, this, &MediaPlayer::durationChanged);
69+ disconnect(dataSource, &MediaDataSource::seekableChanged, this, &MediaPlayer::seekableChanged);
70+ disconnect(dataSource, &MediaDataSource::availabilityChanged, this, &MediaPlayer::availabilityChanged);
71+ disconnect(dataSource, &MediaDataSource::metaDataChanged, m_metaData, &MetaDataObject::metaDataChanged);
72+ });
73+
74+ connect(MediaPlayerDataController::instance(), &MediaPlayerDataController::sourceAdded,
75+ this, [this](const QUrl& source) {
76+ MediaDataSource* dataSource = MediaPlayerDataController::instance()->dataForSource(source);
77+ if (!dataSource) return;
78+
79+ connect(dataSource, &MediaDataSource::durationChanged, this, &MediaPlayer::durationChanged);
80+ connect(dataSource, &MediaDataSource::seekableChanged,this, &MediaPlayer::seekableChanged);
81+ connect(dataSource, &MediaDataSource::availabilityChanged,this, &MediaPlayer::availabilityChanged);
82+ connect(dataSource, &MediaDataSource::metaDataChanged, m_metaData, &MetaDataObject::metaDataChanged);
83+ });
84+}
85+
86+QUrl MediaPlayer::source() const
87+{
88+ return m_source;
89+}
90+
91+void MediaPlayer::setSource(const QUrl &source)
92+{
93+ if (m_source != source) {
94+ m_source = source;
95+ Q_EMIT sourceChanged(source);
96+ Q_EMIT durationChanged(duration());
97+ Q_EMIT seekableChanged(isSeekable());
98+ Q_EMIT availabilityChanged(availability());
99+
100+ m_position = 0;
101+ Q_EMIT positionChanged(m_position);
102+ Q_EMIT m_metaData->metaDataChanged();
103+
104+ m_status = availability()==Available ? Loaded : InvalidMedia;
105+ Q_EMIT statusChanged();
106+ }
107+}
108+
109+DeclarativePlaylist *MediaPlayer::playlist() const
110+{
111+ return m_playlist;
112+}
113+
114+void MediaPlayer::setPlaylist(DeclarativePlaylist *playlist)
115+{
116+ if (playlist == m_playlist)
117+ return;
118+
119+ m_playlist = playlist;
120+ Q_EMIT playlistChanged();
121+}
122+
123+MediaPlayer::PlaybackState MediaPlayer::playbackState() const
124+{
125+ return m_playbackState;
126+}
127+
128+int MediaPlayer::position() const
129+{
130+ return m_position;
131+}
132+
133+int MediaPlayer::duration() const
134+{
135+ MediaDataSource* dataSource = MediaPlayerDataController::instance()->dataForSource(source());
136+ if (dataSource) return dataSource->duration();
137+ return 0;
138+}
139+
140+MediaPlayer::Error MediaPlayer::error() const
141+{
142+ return NoError;
143+}
144+
145+QString MediaPlayer::errorString() const
146+{
147+ return QString();
148+}
149+
150+void MediaPlayer::pause()
151+{
152+ if (m_playbackState == PlayingState) {
153+ m_playbackState = PausedState;
154+ Q_EMIT playbackStateChanged(m_playbackState);
155+ m_timer.stop();
156+ }
157+}
158+
159+void MediaPlayer::play()
160+{
161+ if (m_playbackState != PlayingState) {
162+ m_playbackState = PlayingState;
163+ Q_EMIT playbackStateChanged(m_playbackState);
164+
165+ m_timer.start();
166+ }
167+}
168+
169+void MediaPlayer::stop()
170+{
171+ if (m_playbackState != StoppedState) {
172+ m_playbackState = StoppedState;
173+ Q_EMIT playbackStateChanged(m_playbackState);
174+ m_timer.stop();
175+ m_position = 0;
176+ Q_EMIT positionChanged(m_position);
177+ }
178+}
179+
180+void MediaPlayer::seek(int position)
181+{
182+ if (status() != Loaded) return;
183+ int newPosition = qMin(qMax(0, position), duration());
184+ if (newPosition != m_position) {
185+ m_position = newPosition;
186+ Q_EMIT positionChanged(m_position);
187+ }
188+}
189+
190+void MediaPlayer::timerEvent()
191+{
192+ if (m_position + m_timer.interval() < duration()) {
193+ m_position += m_timer.interval();
194+ Q_EMIT positionChanged(m_position);
195+ } else {
196+ pause();
197+ }
198+}
199+
200+MediaPlayer::AudioRole MediaPlayer::audioRole() const
201+{
202+ return MediaPlayer::MusicRole;
203+}
204+
205+void MediaPlayer::setAudioRole(MediaPlayer::AudioRole audioRole)
206+{
207+ Q_UNUSED(audioRole);
208+}
209+
210+bool MediaPlayer::isSeekable() const
211+{
212+ MediaDataSource* dataSource = MediaPlayerDataController::instance()->dataForSource(source());
213+ if (dataSource) return dataSource->isSeekable();
214+ return true;
215+}
216+
217+MediaPlayer::Availability MediaPlayer::availability() const
218+{
219+ MediaDataSource* dataSource = MediaPlayerDataController::instance()->dataForSource(source());
220+ if (dataSource) return dataSource->availability();
221+ return Available;
222+}
223+
224+QObject *MediaPlayer::metaData() const
225+{
226+ return m_metaData;
227+}
228+
229+
230+MediaDataSource::MediaDataSource(QObject *parent)
231+ : QObject(parent)
232+ , m_seekable(true)
233+ , m_duration((qrand() % 20000) + 10000)
234+ , m_availability(MediaPlayer::Available)
235+{
236+}
237+
238+MediaDataSource::~MediaDataSource()
239+{
240+ if (!m_source.isEmpty()) MediaPlayerDataController::instance()->unregisterDataSource(this);
241+}
242+
243+void MediaDataSource::setSource(const QUrl &source)
244+{
245+ if (m_source != source) {
246+ if (!m_source.isEmpty()) MediaPlayerDataController::instance()->unregisterDataSource(this);
247+
248+ m_source = source;
249+
250+ if (!m_source.isEmpty()) MediaPlayerDataController::instance()->registerDataSource(this);
251+ Q_EMIT sourceChanged();
252+ }
253+}
254+
255+void MediaDataSource::setSeekable(bool seekable)
256+{
257+ if (m_seekable != seekable) {
258+ m_seekable = seekable;
259+ Q_EMIT seekableChanged(m_seekable);
260+ }
261+}
262+
263+void MediaDataSource::setDuration(int duration)
264+{
265+ if (m_duration != duration) {
266+ m_duration = duration;
267+ Q_EMIT durationChanged(m_duration);
268+ }
269+}
270+
271+void MediaDataSource::setAvailability(MediaPlayer::Availability availability)
272+{
273+ if (m_availability != availability) {
274+ m_availability = availability;
275+ Q_EMIT availabilityChanged(m_availability);
276+ }
277+}
278+
279+void MediaDataSource::setMetaData(const QVariant& metaData)
280+{
281+ if (m_metaData != metaData) {
282+ m_metaData = metaData;
283+ Q_EMIT metaDataChanged();
284+ }
285+}
286+
287+MediaPlayerDataController *MediaPlayerDataController::instance()
288+{
289+ static MediaPlayerDataController* instance = nullptr;
290+ if (!instance) { instance = new MediaPlayerDataController(); }
291+ return instance;
292+}
293+
294+void MediaPlayerDataController::registerDataSource(MediaDataSource *dataSource)
295+{
296+ m_dataSources[dataSource->source()] = dataSource;
297+ Q_EMIT sourceAdded(dataSource->source());
298+}
299+
300+void MediaPlayerDataController::unregisterDataSource(MediaDataSource *dataSource)
301+{
302+ QList<QUrl> keys = m_dataSources.keys(dataSource);
303+ Q_FOREACH(const QUrl& key, keys) {
304+ Q_EMIT sourceAboutToBeRemoved(key);
305+ m_dataSources.remove(key);
306+ }
307+}
308+
309+MediaDataSource *MediaPlayerDataController::dataForSource(const QUrl &source)
310+{
311+ if (!m_dataSources.contains(source)) {
312+ static MediaDataSource defaultSource;
313+ return &defaultSource;
314+ }
315+ return m_dataSources[source];
316+}
317+
318+#include "mediaplayer.moc"
tests/mocks/QtMultimedia/mediaplayer.hnew file+223
@@ -0,0 +1,223 @@
1+/*
2+ * Copyright (C) 2012-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+
18+#ifndef MOCK_MEDIAPLAYER_H
19+#define MOCK_MEDIAPLAYER_H
20+
21+#include <QObject>
22+#include <QUrl>
23+#include <QTimer>
24+#include <QHash>
25+#include <QVariant>
26+
27+class MetaDataObject;
28+class DeclarativePlaylist;
29+
30+class MediaPlayer: public QObject
31+{
32+ Q_OBJECT
33+
34+ Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
35+ Q_PROPERTY(PlaybackState playbackState READ playbackState NOTIFY playbackStateChanged)
36+ Q_PROPERTY(int position READ position NOTIFY positionChanged)
37+ Q_PROPERTY(int duration READ duration NOTIFY durationChanged)
38+ Q_PROPERTY(Error error READ error NOTIFY errorChanged)
39+ Q_PROPERTY(QString errorString READ errorString NOTIFY errorStringChanged)
40+ Q_PROPERTY(AudioRole audioRole READ audioRole WRITE setAudioRole)
41+
42+ Q_PROPERTY(bool seekable READ isSeekable NOTIFY seekableChanged)
43+ Q_PROPERTY(Availability availability READ availability NOTIFY availabilityChanged)
44+ Q_PROPERTY(Status status READ status NOTIFY statusChanged)
45+ Q_PROPERTY(QObject *metaData READ metaData CONSTANT)
46+ Q_PROPERTY(DeclarativePlaylist *playlist READ playlist WRITE setPlaylist NOTIFY playlistChanged)
47+
48+ Q_ENUMS(PlaybackState)
49+ Q_ENUMS(AudioRole)
50+ Q_ENUMS(Availability)
51+ Q_ENUMS(Status)
52+ Q_ENUMS(Error)
53+public:
54+ enum PlaybackState {
55+ PlayingState,
56+ PausedState,
57+ StoppedState
58+ };
59+
60+ enum AudioRole {
61+ UnknownRole,
62+ MusicRole,
63+ VideoRole,
64+ VoiceCommunicationRole,
65+ AlarmRole,
66+ NotificationRole,
67+ RingtoneRole,
68+ AccessibilityRole,
69+ SonificationRole,
70+ GameRole
71+ };
72+
73+ enum Availability {
74+ Available,
75+ Busy,
76+ Unavailable,
77+ ResourceMissing
78+ };
79+
80+ enum Status {
81+ UnknownStatus,
82+ NoMedia,
83+ Loading,
84+ Loaded,
85+ Stalled,
86+ Buffering,
87+ Buffered,
88+ EndOfMedia,
89+ InvalidMedia
90+ };
91+
92+ enum Error {
93+ NoError,
94+ ResourceError,
95+ FormatError,
96+ NetworkError,
97+ AccessDeniedError,
98+ ServiceMissingError
99+ };
100+
101+ explicit MediaPlayer(QObject *parent = nullptr);
102+
103+ QUrl source() const;
104+ void setSource(const QUrl &source);
105+
106+ DeclarativePlaylist *playlist() const;
107+ void setPlaylist(DeclarativePlaylist *playlist);
108+
109+ PlaybackState playbackState() const;
110+
111+ int position() const;
112+
113+ int duration() const;
114+
115+ Error error() const;
116+ QString errorString() const;
117+
118+ AudioRole audioRole() const;
119+ void setAudioRole(AudioRole audioRole);
120+
121+ bool isSeekable() const;
122+ MediaPlayer::Availability availability() const;
123+ Status status() const { return m_status; }
124+ QObject *metaData() const;
125+
126+public Q_SLOTS:
127+ void pause();
128+ void play();
129+ void stop();
130+ void seek(int position);
131+
132+Q_SIGNALS:
133+ void playlistChanged();
134+ void sourceChanged(const QUrl &source);
135+ void playbackStateChanged(PlaybackState playbackState);
136+ void positionChanged(int position);
137+ void durationChanged(int duration);
138+ void seekableChanged(bool seekable);
139+ void errorChanged(Error error);
140+ void errorStringChanged(const QString &errorString);
141+ void availabilityChanged(Availability availability);
142+ void statusChanged();
143+
144+ void error(Error error, const QString &errorString);
145+
146+private Q_SLOTS:
147+ void timerEvent();
148+
149+private:
150+ QUrl m_source;
151+ PlaybackState m_playbackState;
152+ QTimer m_timer;
153+ int m_position;
154+ Status m_status;
155+ MetaDataObject *m_metaData;
156+ DeclarativePlaylist *m_playlist;
157+};
158+
159+class MediaDataSource : public QObject
160+{
161+ Q_OBJECT
162+ Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
163+ Q_PROPERTY(bool seekable READ isSeekable WRITE setSeekable NOTIFY seekableChanged)
164+ Q_PROPERTY(int duration READ duration WRITE setDuration NOTIFY durationChanged)
165+ Q_PROPERTY(MediaPlayer::Availability availability READ availability WRITE setAvailability NOTIFY availabilityChanged)
166+ Q_PROPERTY(QVariant metaData READ metaData WRITE setMetaData NOTIFY metaDataChanged)
167+
168+public:
169+ MediaDataSource(QObject* parent = 0);
170+ ~MediaDataSource();
171+
172+ QUrl source() const { return m_source; }
173+ void setSource(const QUrl& source);
174+
175+ bool isSeekable() const { return m_seekable; }
176+ void setSeekable(bool seekable);
177+
178+ int duration() const { return m_duration; }
179+ void setDuration(int duration);
180+
181+ MediaPlayer::Availability availability() const { return m_availability; }
182+ void setAvailability(MediaPlayer::Availability availability);
183+
184+ QVariant metaData() const { return m_metaData; }
185+ void setMetaData(const QVariant& metaData);
186+
187+Q_SIGNALS:
188+ void sourceChanged();
189+ void seekableChanged(bool seekable);
190+ void durationChanged(int duration);
191+ void availabilityChanged(MediaPlayer::Availability availability);
192+ void metaDataChanged();
193+
194+private:
195+ QUrl m_source;
196+ bool m_seekable;
197+ int m_duration;
198+ MediaPlayer::Availability m_availability;
199+ QVariant m_metaData;
200+};
201+
202+class MediaPlayerDataController : public QObject
203+{
204+ Q_OBJECT
205+public:
206+ MediaPlayerDataController() = default;
207+
208+ static MediaPlayerDataController *instance();
209+
210+ void registerDataSource(MediaDataSource* dataSource);
211+ void unregisterDataSource(MediaDataSource* dataSource);
212+
213+ MediaDataSource* dataForSource(const QUrl& source);
214+
215+Q_SIGNALS:
216+ void sourceAdded(const QUrl& source);
217+ void sourceAboutToBeRemoved(const QUrl& source);
218+
219+public:
220+ QHash<QUrl, MediaDataSource*> m_dataSources;
221+};
222+
223+#endif // MOCK_MEDIAPLAYER_H
tests/mocks/QtMultimedia/plugin.cpp+7−3
@@ -17,15 +17,19 @@
17 */
17 */
18
18
19#include "plugin.h"
19#include "plugin.h"
20−#include "audio.h"
20+#include "mediaplayer.h"
21#include "declarativeplaylist.h"
21#include "declarativeplaylist.h"
22+#include "videooutput.h"
22
23
23#include <QtQml/qqml.h>
24#include <QtQml/qqml.h>
24
25
25void MockQtMultimediaPlugin::registerTypes(const char *uri)
26void MockQtMultimediaPlugin::registerTypes(const char *uri)
26{
27{
27 Q_ASSERT(uri == QLatin1String("QtMultimedia"));
28 Q_ASSERT(uri == QLatin1String("QtMultimedia"));
28− qmlRegisterType<Audio>(uri, 5, 0, "Audio");
29+ qmlRegisterType<MediaPlayer>(uri, 5, 0, "Audio");
29− qmlRegisterType<Audio>(uri, 5, 0, "MediaPlayer");
30+ qmlRegisterType<MediaPlayer>(uri, 5, 0, "MediaPlayer");
31+ qmlRegisterType<VideoOutput>(uri, 5, 0, "VideoOutput");
32+
33+ qmlRegisterType<MediaDataSource>(uri, 5, 0, "MediaDataSource");
30 qmlRegisterType<DeclarativePlaylist>(uri, 5, 6, "Playlist");
34 qmlRegisterType<DeclarativePlaylist>(uri, 5, 6, "Playlist");
31}
35}
tests/mocks/QtMultimedia/videooutput.cppnew file+127
@@ -0,0 +1,127 @@
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+#include "videooutput.h"
18+#include "mediaplayer.h"
19+
20+#include <paths.h>
21+
22+#include <QQuickView>
23+#include <QQmlComponent>
24+#include <QGuiApplication>
25+#include <QQmlProperty>
26+#include <QQmlEngine>
27+
28+VideoOutput::VideoOutput(QQuickItem *parent)
29+ : QQuickItem(parent)
30+ , m_source(nullptr)
31+ , m_qmlContentComponent(nullptr)
32+ , m_qmlItem(nullptr)
33+{
34+}
35+
36+void VideoOutput::setSource(QObject *source)
37+{
38+ if (m_source != source) {
39+ if (m_source) disconnect(m_source, 0, this, 0);
40+
41+ m_source = source;
42+ Q_EMIT sourceChanged();
43+
44+ MediaPlayer* mediaPlayer = qobject_cast<MediaPlayer*>(source);
45+ if (mediaPlayer) {
46+ connect(mediaPlayer, &MediaPlayer::positionChanged, this, &VideoOutput::updateProperties);
47+ connect(mediaPlayer, &MediaPlayer::playbackStateChanged, this, &VideoOutput::updateProperties);
48+ connect(mediaPlayer->metaData(), SIGNAL(metaDataChanged()), this, SLOT(updateProperties()));
49+ }
50+ }
51+}
52+
53+void VideoOutput::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
54+{
55+ if (change == QQuickItem::ItemSceneChange && !m_qmlContentComponent) {
56+ QWindowList list = QGuiApplication::topLevelWindows();
57+ if (list.isEmpty()) return;
58+
59+ // The assumptions I make here really should hold.
60+ QQuickView *quickView =
61+ qobject_cast<QQuickView*>(list[0]);
62+
63+ m_qmlContentComponent = new QQmlComponent(quickView->engine(),
64+ QString("%1/QtMultimedia/VideoSurface.qml").arg(mockPluginsDir()));
65+
66+ switch (m_qmlContentComponent->status()) {
67+ case QQmlComponent::Ready:
68+ createQmlContentItem();
69+ break;
70+ case QQmlComponent::Loading:
71+ connect(m_qmlContentComponent, &QQmlComponent::statusChanged,
72+ this, &VideoOutput::onComponentStatusChanged);
73+ break;
74+ case QQmlComponent::Error:
75+ printComponentErrors();
76+ qFatal("VideoOutput: failed to create content component.");
77+ break;
78+ default:
79+ qFatal("VideoOutput: Unhandled component status");
80+ }
81+ }
82+ QQuickItem::itemChange(change, value);
83+}
84+
85+void VideoOutput::printComponentErrors()
86+{
87+ QList<QQmlError> errors = m_qmlContentComponent->errors();
88+ for (int i = 0; i < errors.count(); ++i) {
89+ qDebug() << errors[i];
90+ }
91+}
92+
93+void VideoOutput::onComponentStatusChanged(QQmlComponent::Status status)
94+{
95+ if (status == QQmlComponent::Ready) {
96+ createQmlContentItem();
97+ }
98+}
99+
100+void VideoOutput::createQmlContentItem()
101+{
102+ m_qmlItem = qobject_cast<QQuickItem*>(m_qmlContentComponent->create());
103+ m_qmlItem->setParentItem(this);
104+
105+ updateProperties();
106+}
107+
108+void VideoOutput:: updateProperties()
109+{
110+ if (!m_qmlItem) return;
111+
112+ MediaPlayer* mediaPlayer = qobject_cast<MediaPlayer*>(m_source);
113+ if (!mediaPlayer) return;
114+
115+ QQmlProperty playbackStateProperty(m_qmlItem, "playbackState");
116+ playbackStateProperty.write(QVariant::fromValue<int>(mediaPlayer->playbackState()));
117+
118+ QQmlProperty positionProperty(m_qmlItem, "position");
119+ positionProperty.write(QVariant::fromValue(mediaPlayer->position()));
120+
121+ QQmlProperty propResSource(mediaPlayer->metaData(), "resolution");
122+ QQmlProperty propResDest(m_qmlItem, "resolution");
123+
124+ if (propResSource.isValid() && propResDest.isValid()) {
125+ propResDest.write(propResSource.read());
126+ }
127+}
tests/mocks/QtMultimedia/videooutput.hnew file+52
@@ -0,0 +1,52 @@
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+#ifndef VIDEOOUTPUT_H
18+#define VIDEOOUTPUT_H
19+
20+#include <QQuickItem>
21+#include <QPointer>
22+class QQmlComponent;
23+
24+class VideoOutput : public QQuickItem
25+{
26+ Q_OBJECT
27+ Q_PROPERTY(QObject* source READ source WRITE setSource NOTIFY sourceChanged)
28+public:
29+ explicit VideoOutput(QQuickItem *parent = 0);
30+
31+ QObject *source() const { return m_source.data(); }
32+ void setSource(QObject *source);
33+
34+ void itemChange(ItemChange change, const ItemChangeData & value);
35+
36+Q_SIGNALS:
37+ void sourceChanged();
38+
39+protected Q_SLOTS:
40+ void onComponentStatusChanged(QQmlComponent::Status status);
41+ void updateProperties();
42+
43+private:
44+ void createQmlContentItem();
45+ void printComponentErrors();
46+
47+ QPointer<QObject> m_source;
48+ QQmlComponent* m_qmlContentComponent;
49+ QQuickItem* m_qmlItem;
50+};
51+
52+#endif // VIDEOOUTPUT_H
tests/mocks/Unity/Application/ApplicationManager.cpp+6−1
@@ -302,7 +302,6 @@ void ApplicationManager::buildListOfAvailableApplications()
302 application->setAppId("unity8-dash");
302 application->setAppId("unity8-dash");
303 application->setName("Unity 8 Mock Dash");
303 application->setName("Unity 8 Mock Dash");
304 application->setScreenshotId("unity8-dash");
304 application->setScreenshotId("unity8-dash");
305− application->setSupportedOrientations(Qt::PrimaryOrientation);
306 m_availableApplications.append(application);
305 m_availableApplications.append(application);
307
306
308 application = new ApplicationInfo(this);
307 application = new ApplicationInfo(this);
@@ -459,6 +458,12 @@ void ApplicationManager::buildListOfAvailableApplications()
459 application->setScreenshotId("libreoffice");
458 application->setScreenshotId("libreoffice");
460 application->setIsTouchApp(false);
459 application->setIsTouchApp(false);
461 m_availableApplications.append(application);
460 m_availableApplications.append(application);
461+
462+ application = new ApplicationInfo(this);
463+ application->setAppId("primary-oriented-app");
464+ application->setName("Primary Oriented");
465+ application->setSupportedOrientations(Qt::PrimaryOrientation);
466+ m_availableApplications.append(application);
462}
467}
463
468
464
469
tests/mocks/Unity/Application/UbuntuKeyboardInfo.cpp+1−1
@@ -21,7 +21,7 @@ UbuntuKeyboardInfo::UbuntuKeyboardInfo(QObject *parent)
21 m_x(0),
21 m_x(0),
22 m_y(0),
22 m_y(0),
23 m_width(200),
23 m_width(200),
24− m_height(200)
24+ m_height(0)
25{
25{
26}
26}
27
27
tests/mocks/Unity/Application/resources/screenshots/browser@12.png

Binary file not shown.

tests/mocks/Unity/Application/resources/screenshots/camera@12.png

Binary file not shown.

tests/mocks/Unity/Application/resources/screenshots/dialer@12.png

Binary file not shown.

tests/mocks/Unity/Application/resources/screenshots/facebook@12.png

Binary file not shown.

tests/mocks/Unity/Application/resources/screenshots/gallery@12.png

Binary file not shown.

tests/mocks/Unity/Application/resources/screenshots/libreoffice@12.png

Binary file not shown.

tests/mocks/Unity/Application/resources/screenshots/map@12.png

Binary file not shown.

tests/mocks/Unity/Application/resources/screenshots/music@12.png

Binary file not shown.

tests/mocks/Unity/Application/resources/screenshots/twitter@12.png

Binary file not shown.

tests/mocks/Unity/Application/resources/screenshots/unity8-dash@12.png

Binary file not shown.

tests/mocks/Unity/Application/resources/screenshots/vkb_portrait.png

Binary file not shown.

tests/mocks/Unity/Application/resources/vkb_portrait.png

Binary file not shown.

tests/mocks/Unity/fake_scope.cpp+6−1
@@ -39,6 +39,7 @@ Scope::Scope(QString const& id, QString const& name, bool favorite, Scopes* pare
39 , m_searching(false)
39 , m_searching(false)
40 , m_favorite(favorite)
40 , m_favorite(favorite)
41 , m_isActive(false)
41 , m_isActive(false)
42+ , m_hasBeenActive(false)
42 , m_hasNavigation(true)
43 , m_hasNavigation(true)
43 , m_hasPrimaryFilter(true)
44 , m_hasPrimaryFilter(true)
44 , m_currentNavigationId("root")
45 , m_currentNavigationId("root")
@@ -108,7 +109,7 @@ bool Scope::favorite() const
108
109
109unity::shell::scopes::CategoriesInterface* Scope::categories() const
110unity::shell::scopes::CategoriesInterface* Scope::categories() const
110{
111{
111− return m_categories;
112+ return m_hasBeenActive ? m_categories : nullptr;
112}
113}
113
114
114unity::shell::scopes::SettingsModelInterface* Scope::settings() const
115unity::shell::scopes::SettingsModelInterface* Scope::settings() const
@@ -153,6 +154,10 @@ void Scope::setActive(const bool active)
153 m_isActive = active;
154 m_isActive = active;
154 Q_EMIT isActiveChanged();
155 Q_EMIT isActiveChanged();
155 }
156 }
157+ if (active && !m_hasBeenActive) {
158+ m_hasBeenActive = true;
159+ Q_EMIT categoriesChanged();
160+ }
156}
161}
157
162
158void Scope::setFavorite(const bool favorite)
163void Scope::setFavorite(const bool favorite)
tests/mocks/Unity/fake_scope.h+1
@@ -109,6 +109,7 @@ protected:
109 bool m_searching;
109 bool m_searching;
110 bool m_favorite;
110 bool m_favorite;
111 bool m_isActive;
111 bool m_isActive;
112+ bool m_hasBeenActive;
112 bool m_hasNavigation;
113 bool m_hasNavigation;
113 bool m_hasPrimaryFilter;
114 bool m_hasPrimaryFilter;
114 QString m_currentNavigationId;
115 QString m_currentNavigationId;
tests/mocks/Utils/CMakeLists.txt+5−1
@@ -1,10 +1,14 @@
1include_directories(
1include_directories(
2 ${CMAKE_CURRENT_SOURCE_DIR}
2 ${CMAKE_CURRENT_SOURCE_DIR}
3 ${CMAKE_CURRENT_BINARY_DIR}
3 ${CMAKE_CURRENT_BINARY_DIR}
4+ ${CMAKE_SOURCE_DIR}/plugins/Utils
5+)
6+
7+include_directories(
8+ SYSTEM
4 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
9 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
5 ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
10 ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
6 ${GIO_INCLUDE_DIRS}
11 ${GIO_INCLUDE_DIRS}
7− ${CMAKE_SOURCE_DIR}/plugins/Utils
8)
12)
9
13
10set(QMLPLUGIN_SRC
14set(QMLPLUGIN_SRC
tests/mocks/Utils/windowstatestorage.cpp+2−2
@@ -53,9 +53,9 @@ void WindowStateStorage::saveStage(const QString &appId, int stage)
53 Q_EMIT stageSaved(appId, stage);
53 Q_EMIT stageSaved(appId, stage);
54}
54}
55
55
56−int WindowStateStorage::getStage(const QString &appId) const
56+int WindowStateStorage::getStage(const QString &appId, int defaultValue) const
57{
57{
58− return m_stage.value(appId, unity::shell::application::ApplicationInfoInterface::MainStage);
58+ return m_stage.value(appId, defaultValue);
59}
59}
60
60
61void WindowStateStorage::clear()
61void WindowStateStorage::clear()
tests/mocks/Utils/windowstatestorage.h+1−1
@@ -48,7 +48,7 @@ public:
48 Q_INVOKABLE QRect getGeometry(const QString &windowId, const QRect &defaultValue);
48 Q_INVOKABLE QRect getGeometry(const QString &windowId, const QRect &defaultValue);
49
49
50 Q_INVOKABLE void saveStage(const QString &appId, int stage);
50 Q_INVOKABLE void saveStage(const QString &appId, int stage);
51− Q_INVOKABLE int getStage(const QString &appId) const;
51+ Q_INVOKABLE int getStage(const QString &appId, int defaultValue) const;
52
52
53 // Only in the mock, to easily restore a fresh state
53 // Only in the mock, to easily restore a fresh state
54 Q_INVOKABLE void clear();
54 Q_INVOKABLE void clear();
tests/plugins/AccountsService/CMakeLists.txt−5
@@ -1,8 +1,3 @@
1−if(DEFINED ENV{DEB_BUILD_MULTIARCH})
2− set(QDBUSXML2CPP_EXECUTABLE "/usr/lib/$ENV{DEB_BUILD_MULTIARCH}/qt5/bin/qdbusxml2cpp")
3−else()
4− FIND_PROGRAM(QDBUSXML2CPP_EXECUTABLE qdbusxml2cpp)
5−endif()
6macro(make_dbus_class NAME INTERFACE)
1macro(make_dbus_class NAME INTERFACE)
7 if(${CMAKE_CURRENT_SOURCE_DIR}/interfaces.xml IS_NEWER_THAN ${CMAKE_CURRENT_BINARY_DIR}/${NAME}Adaptor.h)
2 if(${CMAKE_CURRENT_SOURCE_DIR}/interfaces.xml IS_NEWER_THAN ${CMAKE_CURRENT_BINARY_DIR}/${NAME}Adaptor.h)
8 execute_process(COMMAND ${QDBUSXML2CPP_EXECUTABLE} -c ${NAME}Adaptor -i types.h -a ${CMAKE_CURRENT_BINARY_DIR}/${NAME}Adaptor ${CMAKE_CURRENT_SOURCE_DIR}/interfaces.xml ${INTERFACE})
3 execute_process(COMMAND ${QDBUSXML2CPP_EXECUTABLE} -c ${NAME}Adaptor -i types.h -a ${CMAKE_CURRENT_BINARY_DIR}/${NAME}Adaptor ${CMAKE_CURRENT_SOURCE_DIR}/interfaces.xml ${INTERFACE})
tests/plugins/Dash/CMakeLists.txt+4
@@ -12,10 +12,14 @@ endif()
12
12
13
13
14include_directories(
14include_directories(
15+ SYSTEM
15 ${Qt5Qml_PRIVATE_INCLUDE_DIRS}
16 ${Qt5Qml_PRIVATE_INCLUDE_DIRS}
16 ${Qt5Quick_INCLUDE_DIRS}
17 ${Qt5Quick_INCLUDE_DIRS}
17 ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
18 ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
18 ${Qt5V8_PRIVATE_INCLUDE_DIR}
19 ${Qt5V8_PRIVATE_INCLUDE_DIR}
20+ )
21+
22+include_directories(
19 ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/Dash
23 ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/Dash
20 ${CMAKE_CURRENT_BINARY_DIR}
24 ${CMAKE_CURRENT_BINARY_DIR}
21 )
25 )
tests/plugins/Dash/horizontaljournaltest.cpp−3
@@ -23,10 +23,7 @@
23#include <QStringListModel>
23#include <QStringListModel>
24#include <QQmlContext>
24#include <QQmlContext>
25#include <QQmlEngine>
25#include <QQmlEngine>
26−#pragma GCC diagnostic push
27−#pragma GCC diagnostic ignored "-pedantic"
28#include <private/qquickitem_p.h>
26#include <private/qquickitem_p.h>
29−#pragma GCC diagnostic pop
30
27
31#include "horizontaljournal.h"
28#include "horizontaljournal.h"
32
29
tests/plugins/Dash/listviewwithpageheadersectionexternalmodeltest.cpp−3
@@ -22,12 +22,9 @@
22#include <QSignalSpy>
22#include <QSignalSpy>
23#include <QSortFilterProxyModel>
23#include <QSortFilterProxyModel>
24#include <QtTestGui>
24#include <QtTestGui>
25−#pragma GCC diagnostic push
26−#pragma GCC diagnostic ignored "-pedantic"
27#include <private/qqmllistmodel_p.h>
25#include <private/qqmllistmodel_p.h>
28#include <private/qquickanimation_p.h>
26#include <private/qquickanimation_p.h>
29#include <private/qquickitem_p.h>
27#include <private/qquickitem_p.h>
30−#pragma GCC diagnostic pop
31
28
32class StringListModel : public QAbstractListModel
29class StringListModel : public QAbstractListModel
33{
30{
tests/plugins/Dash/listviewwithpageheadersectiontest.cpp−3
@@ -21,12 +21,9 @@
21#include <QQuickView>
21#include <QQuickView>
22#include <QSignalSpy>
22#include <QSignalSpy>
23#include <QtTestGui>
23#include <QtTestGui>
24−#pragma GCC diagnostic push
25−#pragma GCC diagnostic ignored "-pedantic"
26#include <private/qqmllistmodel_p.h>
24#include <private/qqmllistmodel_p.h>
27#include <private/qquickanimation_p.h>
25#include <private/qquickanimation_p.h>
28#include <private/qquickitem_p.h>
26#include <private/qquickitem_p.h>
29−#pragma GCC diagnostic pop
30
27
31class ListViewWithPageHeaderTestSection : public QObject
28class ListViewWithPageHeaderTestSection : public QObject
32{
29{
tests/plugins/Dash/listviewwithpageheadertest.cpp−3
@@ -21,12 +21,9 @@
21#include <QQuickView>
21#include <QQuickView>
22#include <QSignalSpy>
22#include <QSignalSpy>
23#include <QtTestGui>
23#include <QtTestGui>
24−#pragma GCC diagnostic push
25−#pragma GCC diagnostic ignored "-pedantic"
26#include <private/qqmllistmodel_p.h>
24#include <private/qqmllistmodel_p.h>
27#include <private/qquickanimation_p.h>
25#include <private/qquickanimation_p.h>
28#include <private/qquickitem_p.h>
26#include <private/qquickitem_p.h>
29−#pragma GCC diagnostic pop
30
27
31#include <limits>
28#include <limits>
32
29
tests/plugins/Dash/organicgridtest.cpp−3
@@ -23,10 +23,7 @@
23#include <QStringListModel>
23#include <QStringListModel>
24#include <QQmlContext>
24#include <QQmlContext>
25#include <QQmlEngine>
25#include <QQmlEngine>
26−#pragma GCC diagnostic push
27−#pragma GCC diagnostic ignored "-pedantic"
28#include <private/qquickitem_p.h>
26#include <private/qquickitem_p.h>
29−#pragma GCC diagnostic pop
30
27
31#include "organicgrid.h"
28#include "organicgrid.h"
32
29
tests/plugins/Dash/verticaljournaltest.cpp−3
@@ -23,10 +23,7 @@
23#include <QStringListModel>
23#include <QStringListModel>
24#include <QQmlContext>
24#include <QQmlContext>
25#include <QQmlEngine>
25#include <QQmlEngine>
26−#pragma GCC diagnostic push
27−#pragma GCC diagnostic ignored "-pedantic"
28#include <private/qquickitem_p.h>
26#include <private/qquickitem_p.h>
29−#pragma GCC diagnostic pop
30
27
31#include "verticaljournal.h"
28#include "verticaljournal.h"
32
29
tests/plugins/Ubuntu/Gestures/CMakeLists.txt+3−2
@@ -4,14 +4,15 @@ pkg_check_modules(UBUNTUGESTURES REQUIRED UbuntuGestures)
4
4
5# DANGER! DANGER! Using Qt's private API!
5# DANGER! DANGER! Using Qt's private API!
6include_directories(
6include_directories(
7+ SYSTEM
7 ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
8 ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
9+ ${UBUNTUGESTURES_INCLUDE_DIRS}
10+ ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
8)
11)
9
12
10include_directories(
13include_directories(
11 ${CMAKE_SOURCE_DIR}/plugins/Ubuntu/Gestures
14 ${CMAKE_SOURCE_DIR}/plugins/Ubuntu/Gestures
12− ${UBUNTUGESTURES_INCLUDE_DIRS}
13 ${CMAKE_CURRENT_BINARY_DIR}
15 ${CMAKE_CURRENT_BINARY_DIR}
14− ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
15 )
16 )
16
17
17export_qmlfiles(UbuntuGesturesTest .)
18export_qmlfiles(UbuntuGesturesTest .)
tests/plugins/Unity/Session/CMakeLists.txt+29−1
@@ -1,3 +1,13 @@
1+macro(make_dbus_class NAME INTERFACE)
2+ if(${CMAKE_CURRENT_SOURCE_DIR}/interfaces.xml IS_NEWER_THAN ${CMAKE_CURRENT_BINARY_DIR}/${NAME}Adaptor.h)
3+ execute_process(COMMAND ${QDBUSXML2CPP_EXECUTABLE} -c ${NAME}Adaptor -a ${CMAKE_CURRENT_BINARY_DIR}/${NAME}Adaptor ${CMAKE_CURRENT_SOURCE_DIR}/interfaces.xml ${INTERFACE})
4+ endif()
5+endmacro(make_dbus_class)
6+
7+make_dbus_class(LightDMSession org.freedesktop.DisplayManager.Session)
8+make_dbus_class(LogindManager org.freedesktop.login1.Manager)
9+make_dbus_class(LogindSession org.freedesktop.login1.Session)
10+
1include_directories(
11include_directories(
2 ${CMAKE_CURRENT_BINARY_DIR}
12 ${CMAKE_CURRENT_BINARY_DIR}
3 ${CMAKE_SOURCE_DIR}/plugins/Unity/Session
13 ${CMAKE_SOURCE_DIR}/plugins/Unity/Session
@@ -9,6 +19,17 @@ add_definitions(-DSM_BUSNAME=sessionBus)
9add_definitions(-DSRCDIR="${CMAKE_CURRENT_SOURCE_DIR}")
19add_definitions(-DSRCDIR="${CMAKE_CURRENT_SOURCE_DIR}")
10add_definitions(-DSESSION_TESTING)
20add_definitions(-DSESSION_TESTING)
11
21
22+### LightDMServer
23+add_executable(LightDMServer
24+ ${CMAKE_CURRENT_BINARY_DIR}/LightDMSessionAdaptor.cpp
25+ ${CMAKE_CURRENT_BINARY_DIR}/LogindManagerAdaptor.cpp
26+ ${CMAKE_CURRENT_BINARY_DIR}/LogindSessionAdaptor.cpp
27+ LightDMServer.cpp
28+ LogindServer.cpp
29+ server.cpp
30+)
31+qt5_use_modules(LightDMServer Core DBus)
32+
12### SessionBackendTest
33### SessionBackendTest
13add_executable(sessionbackendtestExec
34add_executable(sessionbackendtestExec
14 sessionbackendtest.cpp
35 sessionbackendtest.cpp
@@ -21,6 +42,13 @@ target_link_libraries(sessionbackendtestExec
21qt5_use_modules(sessionbackendtestExec Test Core Qml DBus)
42qt5_use_modules(sessionbackendtestExec Test Core Qml DBus)
22
43
23add_unity8_unittest(SessionBackend dbus-test-runner
44add_unity8_unittest(SessionBackend dbus-test-runner
45+ ENVIRONMENT XDG_SESSION_PATH=/session
24 ARG_PREFIX "--parameter"
46 ARG_PREFIX "--parameter"
47+ ARGS
48+ --task $<TARGET_FILE:LightDMServer>
49+ --task-name server
50+ --ignore-return
25− ARGS --task $<TARGET_FILE:sessionbackendtestExec>
51+ --task $<TARGET_FILE:sessionbackendtestExec>
52+ --task-name client
53+ --wait-for org.freedesktop.DisplayManager
26)
54)
tests/plugins/Unity/Session/LightDMServer.cppnew file+29
@@ -0,0 +1,29 @@
1+/*
2+ * Copyright 2016 Canonical Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it
5+ * under the terms of the GNU General Public License version 3, as published
6+ * by the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but
9+ * WITHOUT ANY WARRANTY; without even the implied warranties of
10+ * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
11+ * PURPOSE. See the GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * version 3 along with this program. If not, see
15+ * <http://www.gnu.org/licenses/>
16+ */
17+
18+#include "LightDMServer.h"
19+
20+LightDMServer::LightDMServer(QObject *logind, QObject *parent)
21+ : QObject(parent)
22+ , m_logind(logind)
23+{
24+}
25+
26+void LightDMServer::Lock()
27+{
28+ QMetaObject::invokeMethod(m_logind, "Lock");
29+}
tests/plugins/Unity/Session/LightDMServer.hnew file+38
@@ -0,0 +1,38 @@
1+/*
2+ * Copyright 2016 Canonical Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it
5+ * under the terms of the GNU General Public License version 3, as published
6+ * by the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but
9+ * WITHOUT ANY WARRANTY; without even the implied warranties of
10+ * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
11+ * PURPOSE. See the GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * version 3 along with this program. If not, see
15+ * <http://www.gnu.org/licenses/>
16+ */
17+
18+#ifndef UNITY_LIGHTDMSERVER_H
19+#define UNITY_LIGHTDMSERVER_H
20+
21+#include <QDBusContext>
22+#include <QObject>
23+
24+class LightDMServer: public QObject, protected QDBusContext
25+{
26+ Q_OBJECT
27+
28+public:
29+ explicit LightDMServer(QObject *logind, QObject *parent = nullptr);
30+
31+public Q_SLOTS:
32+ void Lock();
33+
34+private:
35+ QObject *m_logind;
36+};
37+
38+#endif
tests/plugins/Unity/Session/LogindServer.cppnew file+33
@@ -0,0 +1,33 @@
1+/*
2+ * Copyright 2016 Canonical Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it
5+ * under the terms of the GNU General Public License version 3, as published
6+ * by the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but
9+ * WITHOUT ANY WARRANTY; without even the implied warranties of
10+ * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
11+ * PURPOSE. See the GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * version 3 along with this program. If not, see
15+ * <http://www.gnu.org/licenses/>
16+ */
17+
18+#include "LogindServer.h"
19+
20+LogindServer::LogindServer(QObject *parent)
21+ : QObject(parent)
22+{
23+}
24+
25+QDBusObjectPath LogindServer::GetSessionByPID(quint32)
26+{
27+ return QDBusObjectPath("/logindsession");
28+}
29+
30+void LogindServer::MockEmitUnlock()
31+{
32+ Q_EMIT Unlock();
33+}
tests/plugins/Unity/Session/LogindServer.hnew file+43
@@ -0,0 +1,43 @@
1+/*
2+ * Copyright 2016 Canonical Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it
5+ * under the terms of the GNU General Public License version 3, as published
6+ * by the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but
9+ * WITHOUT ANY WARRANTY; without even the implied warranties of
10+ * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
11+ * PURPOSE. See the GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * version 3 along with this program. If not, see
15+ * <http://www.gnu.org/licenses/>
16+ */
17+
18+#ifndef UNITY_LOGINDSERVER_H
19+#define UNITY_LOGINDSERVER_H
20+
21+#include <QDBusContext>
22+#include <QDBusObjectPath>
23+#include <QObject>
24+
25+class LogindServer: public QObject, protected QDBusContext
26+{
27+ Q_OBJECT
28+
29+public:
30+ explicit LogindServer(QObject *parent = nullptr);
31+
32+public Q_SLOTS:
33+ QDBusObjectPath GetSessionByPID(quint32 pid);
34+
35+ void MockEmitUnlock(); // only in mock
36+
37+Q_SIGNALS:
38+ void Lock();
39+ void Unlock();
40+ void PrepareForSleep();
41+};
42+
43+#endif
tests/plugins/Unity/Session/interfaces.xmlnew file+17
@@ -0,0 +1,17 @@
1+<node>
2+ <interface name="org.freedesktop.DisplayManager.Session">
3+ <method name="Lock" />
4+ </interface>
5+ <interface name="org.freedesktop.login1.Manager">
6+ <method name="GetSessionByPID">
7+ <arg name="pid" type="u" direction="in" />
8+ <arg name="session" type="o" direction="out" />
9+ </method>
10+ <signal name="PrepareForSleep" />
11+ </interface>
12+ <interface name="org.freedesktop.login1.Session">
13+ <method name="MockEmitUnlock" />
14+ <signal name="Lock" />
15+ <signal name="Unlock" />
16+ </interface>
17+</node>
tests/plugins/Unity/Session/server.cppnew file+50
@@ -0,0 +1,50 @@
1+/*
2+ * Copyright 2016 Canonical Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it
5+ * under the terms of the GNU General Public License version 3, as published
6+ * by the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but
9+ * WITHOUT ANY WARRANTY; without even the implied warranties of
10+ * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
11+ * PURPOSE. See the GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * version 3 along with this program. If not, see
15+ * <http://www.gnu.org/licenses/>
16+ */
17+
18+#include "LightDMServer.h"
19+#include "LightDMSessionAdaptor.h"
20+#include "LogindManagerAdaptor.h"
21+#include "LogindServer.h"
22+#include "LogindSessionAdaptor.h"
23+#include <QCoreApplication>
24+
25+int main(int argc, char *argv[])
26+{
27+ QCoreApplication a(argc, argv);
28+
29+ auto logind = new LogindServer(&a);
30+ new LogindManagerAdaptor(logind);
31+ new LogindSessionAdaptor(logind);
32+
33+ auto lightdm = new LightDMServer(logind, &a);
34+ new LightDMSessionAdaptor(lightdm);
35+
36+ // We use the session bus for testing. The real plugin uses the system bus
37+ auto connection = QDBusConnection::sessionBus();
38+ if (!connection.registerObject("/org/freedesktop/login1", logind))
39+ return 1;
40+ if (!connection.registerObject("/logindsession", logind))
41+ return 1;
42+ if (!connection.registerService("org.freedesktop.login1"))
43+ return 1;
44+ if (!connection.registerObject("/session", lightdm))
45+ return 1;
46+ if (!connection.registerService("org.freedesktop.DisplayManager"))
47+ return 1;
48+
49+ return a.exec();
50+}
tests/plugins/Unity/Session/sessionbackendtest.cpp+15−1
@@ -61,6 +61,8 @@ private Q_SLOTS:
61 QTest::newRow("Reboot") << "RequestReboot" << "RebootRequested(bool)";
61 QTest::newRow("Reboot") << "RequestReboot" << "RebootRequested(bool)";
62 QTest::newRow("Shutdown") << "RequestShutdown" << "ShutdownRequested(bool)";
62 QTest::newRow("Shutdown") << "RequestShutdown" << "ShutdownRequested(bool)";
63 QTest::newRow("PromptLock") << "PromptLock" << "LockRequested()";
63 QTest::newRow("PromptLock") << "PromptLock" << "LockRequested()";
64+ QTest::newRow("Lock") << "Lock" << "LockRequested()";
65+ QTest::newRow("LockLightDM") << "Lock" << "Locked()"; // happens when we lock LightDM, only for Lock()
64 }
66 }
65
67
66 void testUnitySessionLogoutRequested() {
68 void testUnitySessionLogoutRequested() {
@@ -76,7 +78,7 @@ private Q_SLOTS:
76 QDBusReply<void> reply = dbusUnitySession->call(method);
78 QDBusReply<void> reply = dbusUnitySession->call(method);
77 QCOMPARE(reply.isValid(), true);
79 QCOMPARE(reply.isValid(), true);
78
80
79− QCOMPARE(spy.count(), 1);
81+ QTRY_COMPARE(spy.count(), 1);
80 }
82 }
81
83
82 void testGnomeSessionWrapper_data() {
84 void testGnomeSessionWrapper_data() {
@@ -122,6 +124,18 @@ private Q_SLOTS:
122 QCOMPARE(spy.count(), 1);
124 QCOMPARE(spy.count(), 1);
123 }
125 }
124
126
127+ void testUnlockFromLogind() {
128+ DBusUnitySessionService dbusUnitySessionService;
129+ QCoreApplication::processEvents(); // to let the service register on DBus
130+
131+ QDBusInterface iface("org.freedesktop.login1", "/logindsession", "org.freedesktop.login1.Session");
132+ QVERIFY(iface.isValid());
133+
134+ QSignalSpy spy(&dbusUnitySessionService, SIGNAL(Unlocked()));
135+ QCOMPARE(iface.call("MockEmitUnlock").errorMessage(), QString());
136+ QTRY_COMPARE(spy.count(), 1);
137+ }
138+
125 void testUserName() {
139 void testUserName() {
126 DBusUnitySessionService dbusUnitySessionService;
140 DBusUnitySessionService dbusUnitySessionService;
127 QCoreApplication::processEvents(); // to let the service register on DBus
141 QCoreApplication::processEvents(); // to let the service register on DBus
tests/qmltests/CMakeLists.txt+3−1
@@ -12,11 +12,13 @@ add_unity8_qmltest(Components Dialogs LIGHTDM)
12add_unity8_qmltest(Components DraggingArea)
12add_unity8_qmltest(Components DraggingArea)
13add_unity8_qmltest(Components LazyImage)
13add_unity8_qmltest(Components LazyImage)
14add_unity8_qmltest(Components Lockscreen)
14add_unity8_qmltest(Components Lockscreen)
15+add_unity8_qmltest(Components MediaServices)
15add_unity8_qmltest(Components ModeSwitchWarningDialog)
16add_unity8_qmltest(Components ModeSwitchWarningDialog)
16add_unity8_qmltest(Components PhysicalKeysMapper)
17add_unity8_qmltest(Components PhysicalKeysMapper)
17add_unity8_qmltest(Components Rating)
18add_unity8_qmltest(Components Rating)
18add_unity8_qmltest(Components ResponsiveGridView)
19add_unity8_qmltest(Components ResponsiveGridView)
19add_unity8_qmltest(Components ResponsiveVerticalJournal)
20add_unity8_qmltest(Components ResponsiveVerticalJournal)
21+add_unity8_qmltest(Components SharingPicker)
20add_unity8_qmltest(Components Showable)
22add_unity8_qmltest(Components Showable)
21add_unity8_qmltest(Components VirtualTouchPad)
23add_unity8_qmltest(Components VirtualTouchPad)
22add_unity8_qmltest(Components WallpaperResolver)
24add_unity8_qmltest(Components WallpaperResolver)
@@ -40,6 +42,7 @@ add_unity8_qmltest(Dash/Previews PreviewExpandable)
40add_unity8_qmltest(Dash/Previews PreviewHeader)
42add_unity8_qmltest(Dash/Previews PreviewHeader)
41add_unity8_qmltest(Dash/Previews PreviewIconActions)
43add_unity8_qmltest(Dash/Previews PreviewIconActions)
42add_unity8_qmltest(Dash/Previews PreviewImageGallery)
44add_unity8_qmltest(Dash/Previews PreviewImageGallery)
45+add_unity8_qmltest(Dash/Previews PreviewInlineVideo)
43add_unity8_qmltest(Dash/Previews PreviewMediaToolbar)
46add_unity8_qmltest(Dash/Previews PreviewMediaToolbar)
44add_unity8_qmltest(Dash/Previews PreviewPayments)
47add_unity8_qmltest(Dash/Previews PreviewPayments)
45add_unity8_qmltest(Dash/Previews PreviewProgress)
48add_unity8_qmltest(Dash/Previews PreviewProgress)
@@ -47,7 +50,6 @@ add_unity8_qmltest(Dash/Previews PreviewRatingDisplay)
47add_unity8_qmltest(Dash/Previews PreviewRatingDisplayCreationRanges)
50add_unity8_qmltest(Dash/Previews PreviewRatingDisplayCreationRanges)
48add_unity8_qmltest(Dash/Previews PreviewRatingEdit)
51add_unity8_qmltest(Dash/Previews PreviewRatingEdit)
49add_unity8_qmltest(Dash/Previews PreviewRatingInput)
52add_unity8_qmltest(Dash/Previews PreviewRatingInput)
50−add_unity8_qmltest(Dash/Previews PreviewSharing)
51add_unity8_qmltest(Dash/Previews PreviewTable)
53add_unity8_qmltest(Dash/Previews PreviewTable)
52add_unity8_qmltest(Dash/Previews PreviewTextSummary)
54add_unity8_qmltest(Dash/Previews PreviewTextSummary)
53add_unity8_qmltest(Dash/Previews PreviewVideoPlayback)
55add_unity8_qmltest(Dash/Previews PreviewVideoPlayback)
tests/qmltests/Components/CMakeLists.txt+5−1
@@ -6,8 +6,12 @@ pkg_check_modules(UBUNTUGESTURES REQUIRED UbuntuGestures)
6
6
7include_directories(
7include_directories(
8 ${CMAKE_SOURCE_DIR}/plugins/Ubuntu/Gestures
8 ${CMAKE_SOURCE_DIR}/plugins/Ubuntu/Gestures
9− ${UBUNTUGESTURES_INCLUDE_DIRS}
10 ${CMAKE_CURRENT_BINARY_DIR}
9 ${CMAKE_CURRENT_BINARY_DIR}
10+ )
11+
12+include_directories(
13+ SYSTEM
14+ ${UBUNTUGESTURES_INCLUDE_DIRS}
11 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
15 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
12 ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
16 ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
13 ${UBUNTUGESTURES_INCLUDEDIR}/UbuntuGestures/${UBUNTUGESTURES_VERSION}
17 ${UBUNTUGESTURES_INCLUDEDIR}/UbuntuGestures/${UBUNTUGESTURES_VERSION}
tests/qmltests/Components/tst_LazyImage/portrait.png

Binary file not shown.

tests/qmltests/Components/tst_LazyImage/square.png

Binary file not shown.

tests/qmltests/Components/tst_LazyImage/wide.png

Binary file not shown.

tests/qmltests/Components/tst_MediaServices.qmlnew file+230
@@ -0,0 +1,230 @@
1+/*
2+ * Copyright 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+import QtTest 1.0
19+import "../../../qml/Components/MediaServices"
20+import Unity.Test 0.1 as UT
21+import Ubuntu.Components 1.3
22+import QtMultimedia 5.0
23+
24+Rectangle {
25+ id: root
26+ width: units.gu(70)
27+ height: units.gu(80)
28+ color: "lightgrey"
29+
30+ property var sourceData: {
31+ // TODO do we want a video or make this fake
32+ "source": "file:///home/nick/Videos/test-mpeg.ogv",
33+ "screenshot": Qt.resolvedUrl("../Dash/artwork/avatar.png")
34+ }
35+
36+ property var sourceData2: {
37+ // TODO do we want a video or make this fake
38+ "source": "file:///home/nick/Videos/test-mpeg2.ogv",
39+ "screenshot": Qt.resolvedUrl("../Dash/artwork/checkers.png")
40+ }
41+
42+ MediaDataSource {
43+ source: root.sourceData["source"]
44+ duration: 60000
45+ metaData: {
46+ "title" : "TEST MPEG",
47+ "resolution" : { "width": 160, "height": 90 }
48+ }
49+ }
50+
51+ MediaDataSource {
52+ source: root.sourceData2["source"]
53+ duration: 30000
54+ metaData: {
55+ "title" : "TEST MPEG",
56+ "resolution" : { "width": 90, "height": 240 }
57+ }
58+ }
59+
60+ Item {
61+ anchors.fill: parent
62+
63+ Rectangle {
64+ anchors {
65+ top: parent.top
66+ bottom: parent.bottom
67+ left: parent.left
68+ right: controls.left
69+ }
70+ color: "darkblue"
71+
72+ MediaServices {
73+ id: services
74+ fullscreen: false
75+ width: parent.width
76+ sourceData: root.sourceData
77+ context: "video"
78+
79+ rootItem: parent
80+ maximumEmbeddedHeight: undefined
81+ }
82+ }
83+
84+ Rectangle {
85+ id: controls
86+ color: "darkgrey"
87+ anchors {
88+ top: parent.top
89+ bottom: parent.bottom
90+ right: parent.right
91+ }
92+ width: units.gu(30)
93+
94+ Column {
95+ anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
96+
97+ Button {
98+ text: "Fullscreen"
99+ onClicked: services.fullscreen = !services.fullscreen
100+ }
101+ UT.MouseTouchEmulationCheckbox {}
102+ }
103+ }
104+ }
105+
106+ SignalSpy {
107+ id: serviceCloseSpy
108+ target: services
109+ signalName: "close"
110+ }
111+
112+ UT.UnityTestCase {
113+ name: "VideoMediaServices"
114+ when: windowShown
115+
116+ property int oldControlTimerInterval: 0;
117+
118+ function init() {
119+ services.sourceData = root.sourceData
120+ services.context = "video";
121+
122+ var controlHideTimer = findInvisibleChild(services, "controlHideTimer");
123+ verify(controlHideTimer !== null);
124+ oldControlTimerInterval = controlHideTimer.interval;
125+ }
126+
127+ function cleanup() {
128+ serviceCloseSpy.clear();
129+ services.context = "";
130+ services.fullscreen = false;
131+ services.maximumEmbeddedHeight = undefined;
132+ tryCompareFunction(function() { return services.header ? services.header.visible : false }, false);
133+ tryCompareFunction(function() { return services.footer ? services.footer.visible : false }, false);
134+
135+ var controlHideTimer = findInvisibleChild(services, "controlHideTimer");
136+ controlHideTimer.interval = oldControlTimerInterval;
137+ }
138+
139+ function test_videoWidget() {
140+ var videoPlayer = findChild(services, "videoPlayer");
141+ verify(videoPlayer !== null);
142+
143+ var videoControls = findChild(services, "videoControls");
144+ verify(videoControls !== null);
145+ }
146+
147+ function test_tapVideoPlayerToPlayAndPause() {
148+ var videoPlayer = findChild(services, "videoPlayer");
149+ verify(videoPlayer !== null)
150+ tap(videoPlayer, videoPlayer.width/2, videoPlayer.height/2);
151+
152+ var mediaPlayer = findInvisibleChild(services, "mediaPlayer");
153+ verify(mediaPlayer !== null);
154+ compare(mediaPlayer.playbackState, MediaPlayer.PlayingState, "Media player should be playing");
155+
156+ tap(videoPlayer, videoPlayer.width/2, videoPlayer.height/2);
157+ compare(mediaPlayer.playbackState, MediaPlayer.PausedState, "Media player should be playing");
158+ }
159+
160+ function test_fullscreenSwitch() {
161+ var mediaPlayer = findInvisibleChild(services, "mediaPlayer");
162+ verify(mediaPlayer !== null);
163+ mediaPlayer.play();
164+ wait(UbuntuAnimation.BriskDuration); // animation
165+
166+ var button = findChild(services, "viewActionButton");
167+ verify(button !== null);
168+ tap(button);
169+
170+ compare(services.fullscreen, true, "Should have switched to fullscreen mode.");
171+ }
172+
173+ function test_HeaderVisibleOnlyWhenFullscreen() {
174+ services.fullscreen = false;
175+ compare(services.header, null, "Header should be null when not fullscreen");
176+ services.fullscreen = true;
177+ tryCompareFunction(function() { return services.header !== null }, true);
178+ }
179+
180+ function test_ControlsShowAndHideWhenPlayed() {
181+ services.fullscreen = true;
182+ var controlHideTimer = findInvisibleChild(services, "controlHideTimer");
183+ controlHideTimer.interval = 100;
184+
185+ var mediaPlayer = findInvisibleChild(services, "mediaPlayer");
186+ mediaPlayer.play();
187+
188+ tryCompare(services.header, "visible", true);
189+ tryCompare(services.footer, "visible", true);
190+
191+ tryCompare(services.header, "visible", false);
192+ tryCompare(services.footer, "visible", false);
193+ }
194+
195+ function test_ControlsDontTimeOutWhenPaused() {
196+ services.fullscreen = true;
197+ var controlHideTimer = findInvisibleChild(services, "controlHideTimer");
198+ controlHideTimer.interval = 100;
199+
200+ var mediaPlayer = findInvisibleChild(services, "mediaPlayer");
201+ mediaPlayer.play();
202+ mediaPlayer.pause();
203+ wait(300);
204+
205+ compare(services.header.visible, true, "Header should still be visible");
206+ compare(services.footer.visible, true, "Footer should still be visible");
207+ }
208+
209+ function test_close() {
210+ services.fullscreen = true;
211+ var mediaPlayer = findInvisibleChild(services, "mediaPlayer");
212+ mediaPlayer.play();
213+ mediaPlayer.pause();
214+ wait(UbuntuAnimation.BriskDuration); // animation
215+
216+ var navigationButton = findChild(services, "navigationButton");
217+ verify(navigationButton !== null);
218+
219+ tap(navigationButton);
220+ compare(serviceCloseSpy.count, 1, "close was not called");
221+ }
222+
223+ function test_maximumVideoSize() {
224+ services.maximumEmbeddedHeight = root.height / 2
225+ services.sourceData = root.sourceData2
226+ verify(services.content !== null);
227+ tryCompare(services.content, "height", root.height/2);
228+ }
229+ }
230+}
tests/qmltests/Dash/Previews/tst_PreviewSharing.qml → tests/qmltests/Components/tst_SharingPicker.qmlrenamed+8−8
@@ -17,7 +17,7 @@
17import QtQuick 2.4
17import QtQuick 2.4
18import QtTest 1.0
18import QtTest 1.0
19import Ubuntu.Components 1.3
19import Ubuntu.Components 1.3
20−import "../../../../qml/Dash/Previews"
20+import "../../../qml/Components"
21import Unity.Test 0.1 as UT
21import Unity.Test 0.1 as UT
22
22
23Rectangle {
23Rectangle {
@@ -40,33 +40,33 @@ Rectangle {
40 "content-type": "text"
40 "content-type": "text"
41 }
41 }
42
42
43− PreviewSharing {
43+ SharingPicker {
44− id: previewSharing
44+ id: sharingPicker
45 anchors { left: parent.left; bottom: parent.bottom; }
45 anchors { left: parent.left; bottom: parent.bottom; }
46 shareData: root.shareData
46 shareData: root.shareData
47 }
47 }
48
48
49 UT.UnityTestCase {
49 UT.UnityTestCase {
50− name: "PreviewSharingTest"
50+ name: "SharingPickerTest"
51 when: windowShown
51 when: windowShown
52
52
53− property Item peerPicker: findChild(previewSharing.rootItem, "peerPicker")
53+ property Item peerPicker: findChild(sharingPicker.rootItem, "peerPicker")
54
54
55 function cleanup() {
55 function cleanup() {
56 peerPicker.visible = false;
56 peerPicker.visible = false;
57 }
57 }
58
58
59 function test_open_picker() {
59 function test_open_picker() {
60− mouseClick(previewSharing);
60+ sharingPicker.showPeerPicker();
61 compare(peerPicker.visible, true);
61 compare(peerPicker.visible, true);
62 }
62 }
63
63
64 function test_createExportedItems() {
64 function test_createExportedItems() {
65− var exportedItems = previewSharing.createExportedItems(shareData["uri"]);
65+ var exportedItems = sharingPicker.createExportedItems(shareData["uri"]);
66 for (var i = 0; i < exportedItems.length; i++) {
66 for (var i = 0; i < exportedItems.length; i++) {
67 compare(exportedItems[i].url, shareData["uri"][i]);
67 compare(exportedItems[i].url, shareData["uri"][i]);
68 }
68 }
69− exportedItems = previewSharing.createExportedItems(shareDataString["uri"]);
69+ exportedItems = sharingPicker.createExportedItems(shareDataString["uri"]);
70 compare(exportedItems[0].url, shareDataString["uri"]);
70 compare(exportedItems[0].url, shareDataString["uri"]);
71 }
71 }
72 }
72 }
tests/qmltests/Dash/Previews/tst_PreviewInlineVideo.qmlnew file+114
@@ -0,0 +1,114 @@
1+/*
2+ * Copyright 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+import QtTest 1.0
19+import "../../../../qml/Dash/Previews"
20+import Unity.Test 0.1 as UT
21+import Ubuntu.Components 1.3
22+import QtMultimedia 5.0
23+
24+Rectangle {
25+ width: units.gu(70)
26+ height: units.gu(80)
27+ color: "lightgrey"
28+
29+ property var widgetData0: {
30+ "source": "file:///test-video1",
31+ "screenshot": Qt.resolvedUrl("../artwork/avatar.png"),
32+ "share-data": {
33+ "uri": [
34+ "file:///this/is/an/url",
35+ "file:///this/is/an/url/2",
36+ "file:///this/is/an/url/3"
37+ ],
38+ "content-type": "text"
39+ }
40+ }
41+
42+ property var widgetData1: {
43+ "source": "file:///test-video2",
44+ "screenshot": "file:///test-video2-screenshot"
45+ }
46+
47+ MediaDataSource {
48+ source: "file:///test-video1"
49+ duration: 6000000
50+ metaData: {
51+ "title" : "TEST MPEG",
52+ "resolution" : { "width": 1920, "height": 1080 }
53+ }
54+ }
55+
56+ Item {
57+ anchors.fill: parent
58+
59+ Item {
60+ id: inner
61+ anchors {
62+ top: parent.top
63+ bottom: parent.bottom
64+ left: parent.left
65+ right: controls.left
66+ }
67+
68+ Item {
69+ anchors.fill: parent
70+ anchors.margins: units.gu(2)
71+
72+ PreviewInlineVideo {
73+ id: videoPlayback
74+ width: parent.width
75+ widgetData: widgetData0
76+
77+ rootItem: inner
78+ }
79+ }
80+
81+ }
82+
83+ Rectangle {
84+ id: controls
85+ color: "darkgrey"
86+ anchors {
87+ top: parent.top
88+ bottom: parent.bottom
89+ right: parent.right
90+ }
91+ width: units.gu(30)
92+
93+ Column {
94+ anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
95+
96+ UT.MouseTouchEmulationCheckbox {}
97+ }
98+ }
99+ }
100+
101+ UT.UnityTestCase {
102+ name: "PreviewInlineVideoTest"
103+ when: windowShown
104+
105+ function test_loadScreenshot() {
106+ var screenshot = findChild(videoPlayback, "screenshot");
107+ verify(screenshot !== null);
108+
109+ videoPlayback.widgetData = widgetData1;
110+ var screenshotSource = screenshot.source
111+ compare(screenshotSource.toString(), "file:///test-video2-screenshot");
112+ }
113+ }
114+}
tests/qmltests/Dash/Previews/tst_PreviewMediaToolbar.qml+3−3
@@ -51,7 +51,7 @@ Rectangle {
51 name: "PreviewMediaToolbarTest"
51 name: "PreviewMediaToolbarTest"
52 when: windowShown
52 when: windowShown
53
53
54− property Item sharingWidget: findChild(previewMediaToolbar, "sharingWidget")
54+ property Item sharingPicker: findChild(previewMediaToolbar, "sharingPicker")
55
55
56 function cleanup() {
56 function cleanup() {
57 previewMediaToolbar.shareData = root.shareData;
57 previewMediaToolbar.shareData = root.shareData;
@@ -60,10 +60,10 @@ Rectangle {
60 function test_visible() {
60 function test_visible() {
61 previewMediaToolbar.shareData = shareDataNoUri;
61 previewMediaToolbar.shareData = shareDataNoUri;
62 compare(previewMediaToolbar.visible, false);
62 compare(previewMediaToolbar.visible, false);
63− compare(sharingWidget.visible, false);
63+ compare(sharingPicker.visible, false);
64 previewMediaToolbar.shareData = shareData;
64 previewMediaToolbar.shareData = shareData;
65 compare(previewMediaToolbar.visible, true);
65 compare(previewMediaToolbar.visible, true);
66− compare(sharingWidget.visible, true);
66+ compare(sharingPicker.visible, true);
67 }
67 }
68 }
68 }
69}
69}
tests/qmltests/Dash/Previews/tst_PreviewRatingDisplayCreationRanges.qml+51−15
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2014,2015 Canonical Ltd.
2+ * Copyright 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
@@ -42,6 +42,8 @@ Flickable {
42 parentFlickable: root
42 parentFlickable: root
43 }
43 }
44
44
45+ property int createdDelegates: 0
46+
45 Component.onCompleted: {
47 Component.onCompleted: {
46 for (var i = 0; i < 500; ++i) {
48 for (var i = 0; i < 500; ++i) {
47 widgetData500.reviews.push({ author: "Some dude", review: "Very cool app" + i });
49 widgetData500.reviews.push({ author: "Some dude", review: "Very cool app" + i });
@@ -49,37 +51,68 @@ Flickable {
49 factory.widgetData = widgetData500;
51 factory.widgetData = widgetData500;
50 }
52 }
51
53
54+ Component {
55+ id: countingPreviewRatingSingleDisplayComponent
56+ PreviewRatingSingleDisplay {
57+ objectName: "reviewItem" + index
58+
59+ anchors { left: parent.left; right: parent.right; }
60+
61+ rating: modelData["rating"] || -1
62+ author: modelData["author"] || ""
63+ review: modelData["review"] || ""
64+ Component.onCompleted: root.createdDelegates++;
65+ }
66+ }
67+
52 UT.UnityTestCase {
68 UT.UnityTestCase {
53− name: "PreviewRatingDisplayTest"
69+ name: "PreviewRatingDisplayCreationRangesTest"
54 when: windowShown
70 when: windowShown
55
71
56− function test_creation_speed() {
72+ function initTestCase() {
57− factory.parentFlickable = null;
73+ var lists = findChildsByType(factory, "QQuickListView");
74+ compare(lists.length, 1)
75+ lists[0].delegate = countingPreviewRatingSingleDisplayComponent;
76+ }
77+
78+ function test_check_delegate_creation_range() {
79+ // Clear the reviews
58 factory.widgetData = reviewsEmpty;
80 factory.widgetData = reviewsEmpty;
59 waitForRendering(root);
81 waitForRendering(root);
60
82
61− var start = new Date().getTime();
83+ // Unset parentFlickable this disables the creation range code
84+ factory.parentFlickable = null;
85+ root.createdDelegates = 0;
86+
87+ // Set the 500 reviews data
62 factory.widgetData = widgetData500;
88 factory.widgetData = widgetData500;
63 waitForRendering(root);
89 waitForRendering(root);
64− var end = new Date().getTime();
65− var time500 = end - start;
66
90
91+ // Check we have created 500 delegates
92+ compare(root.createdDelegates, 500);
93+
94+ // Check review 499 has been created
95+ var reviewItem499 = findChild(factory, "reviewItem499");
96+ verify(reviewItem499 !== null);
97+
98+ // Clear the reviews
67 factory.widgetData = reviewsEmpty;
99 factory.widgetData = reviewsEmpty;
68 waitForRendering(root);
100 waitForRendering(root);
69
101
102+ // set parentFlickable to enable the creation ranges code
70 factory.parentFlickable = root;
103 factory.parentFlickable = root;
71− start = new Date().getTime();
104+ root.createdDelegates = 0;
105+
106+ // Set the 500 reviews data
72 factory.widgetData = widgetData500;
107 factory.widgetData = widgetData500;
73 waitForRendering(root);
108 waitForRendering(root);
74− end = new Date().getTime();
75− var timeRanges500 = end - start;
76
109
77− // Measurements show it's usually like 20 times faster
110+ // Check we have only created a few delegates
78− // but we set the range at 8 times fast to make sure it's not an unstable test
111+ // For some reason xenial and yaketti we get 16 and on vivid 15 so
79− verify(timeRanges500 * 8 < time500);
112+ // settle for <= 20
80− }
113+ expectFailContinue("", "Should not create more than 20 delegates when using ranges");
114+ tryCompareFunction(function() { return root.createdDelegates > 20 }, true);
81
115
82− function test_check_indexes() {
83 // Check that item 499 isn't there on startup but if we scroll down
116 // Check that item 499 isn't there on startup but if we scroll down
84 // it will be there
117 // it will be there
85 var reviewItem499 = findChild(factory, "reviewItem499");
118 var reviewItem499 = findChild(factory, "reviewItem499");
@@ -89,6 +122,9 @@ Flickable {
89
122
90 var reviewItem499 = findChild(factory, "reviewItem499");
123 var reviewItem499 = findChild(factory, "reviewItem499");
91 verify(reviewItem499 !== null);
124 verify(reviewItem499 !== null);
125+
126+ // Check we have created 500 delegates
127+ compare(root.createdDelegates, 500);
92 }
128 }
93 }
129 }
94}
130}
tests/qmltests/Dash/Previews/tst_PreviewRatingEdit.qml+3
@@ -56,7 +56,9 @@ Rectangle {
56 verify(!reviewTextArea.visible)
56 verify(!reviewTextArea.visible)
57
57
58 var editButton = findChild(previewRatingEdit, "editButton");
58 var editButton = findChild(previewRatingEdit, "editButton");
59+ var display = findChild(previewRatingEdit, "display");
59 mouseClick(editButton);
60 mouseClick(editButton);
61+ tryCompare(display, "visible", false);
60 verify(!authorLabel.visible)
62 verify(!authorLabel.visible)
61 verify(reviewTextArea.visible)
63 verify(reviewTextArea.visible)
62
64
@@ -72,6 +74,7 @@ Rectangle {
72 var submitButton = findChild(previewRatingEdit, "submitButton")
74 var submitButton = findChild(previewRatingEdit, "submitButton")
73 mouseClick(submitButton);
75 mouseClick(submitButton);
74
76
77+ tryCompare(display, "visible", true);
75 compare(spy.count, 1);
78 compare(spy.count, 1);
76 var args = spy.signalArguments[0];
79 var args = spy.signalArguments[0];
77 compare(args[0], previewRatingEdit.widgetId);
80 compare(args[0], previewRatingEdit.widgetId);
tests/qmltests/Dash/Previews/tst_PreviewWidgetFactory.qml+17−16
@@ -75,26 +75,27 @@ Rectangle {
75
75
76 function test_mapping_data() {
76 function test_mapping_data() {
77 return [
77 return [
78− { tag: "Actions", type: "actions", source: "PreviewActions.qml", expanded: true },
78+ { tag: "Actions", data: { type: "actions" }, source: "PreviewActions.qml", expanded: true },
79− { tag: "Audio", type: "audio", source: "PreviewAudioPlayback.qml", expanded: true },
79+ { tag: "Audio", data: { type: "audio" }, source: "PreviewAudioPlayback.qml", expanded: true },
80− { tag: "Comment", type: "comment", source: "PreviewComment.qml", expanded: true },
80+ { tag: "Comment", data: { type: "comment" }, source: "PreviewComment.qml", expanded: true },
81− { tag: "Comment Input", type: "comment-input", source: "PreviewCommentInput.qml", expanded: true },
81+ { tag: "Comment Input", data: { type: "comment-input" }, source: "PreviewCommentInput.qml", expanded: true },
82− { tag: "Expandable", type: "expandable", source: "PreviewExpandable.qml", expanded: false },
82+ { tag: "Expandable", data: { type: "expandable" }, source: "PreviewExpandable.qml", expanded: false },
83− { tag: "Gallery", type: "gallery", source: "PreviewImageGallery.qml", expanded: true },
83+ { tag: "Gallery", data: { type: "gallery" }, source: "PreviewImageGallery.qml", expanded: true },
84− { tag: "Header", type: "header", source: "PreviewHeader.qml", expanded: true },
84+ { tag: "Header", data: { type: "header" }, source: "PreviewHeader.qml", expanded: true },
85− { tag: "Image", type: "image", source: "PreviewZoomableImage.qml", expanded: true },
85+ { tag: "Image", data: { type: "image" }, source: "PreviewZoomableImage.qml", expanded: true },
86− { tag: "Progress", type: "progress", source: "PreviewProgress.qml", expanded: true },
86+ { tag: "Progress", data: { type: "progress" }, source: "PreviewProgress.qml", expanded: true },
87− { tag: "Rating Input", type: "rating-input", source: "PreviewRatingInput.qml", expanded: true },
87+ { tag: "Rating Input", data: { type: "rating-input" }, source: "PreviewRatingInput.qml", expanded: true },
88− { tag: "Rating Display", type: "reviews", source: "PreviewRatingDisplay.qml", expanded: true },
88+ { tag: "Rating Display", data: { type: "reviews" }, source: "PreviewRatingDisplay.qml", expanded: true },
89− { tag: "Table", type: "table", source: "PreviewTable.qml", expanded: true },
89+ { tag: "Table", data: { type: "table" }, source: "PreviewTable.qml", expanded: true },
90− { tag: "Text", type: "text", source: "PreviewTextSummary.qml", expanded: true },
90+ { tag: "Text", data: { type: "text" }, source: "PreviewTextSummary.qml", expanded: true },
91+ { tag: "Video Inline", data: { type: "video", source: "httpDemo" }, source: "PreviewInlineVideo.qml", expanded: true },
91− { tag: "Video", type: "video", source: "PreviewVideoPlayback.qml", expanded: true },
92+ { tag: "Video", data: { type: "video", source: "https://demo" }, source: "PreviewVideoPlayback.qml", expanded: true },
92 ];
93 ];
93 }
94 }
94
95
95 function test_mapping(data) {
96 function test_mapping(data) {
96− factory.widgetData = { type: data.type };
97+ factory.widgetData = data.data;
97− factory.widgetType = data.type;
98+ factory.widgetType = data.data.type;
98
99
99 verify((String(factory.source)).indexOf(data.source) != -1);
100 verify((String(factory.source)).indexOf(data.source) != -1);
100 compare(factory.item.expanded, data.expanded);
101 compare(factory.item.expanded, data.expanded);
tests/qmltests/Dash/artwork/avatar@12.png

Binary file not shown.

tests/qmltests/Dash/artwork/background.png

Binary file not shown.

tests/qmltests/Dash/artwork/checkers.png

Binary file not shown.

tests/qmltests/Dash/artwork/emblem.png

Binary file not shown.

tests/qmltests/Dash/artwork/music-player-design.png

Binary file not shown.

tests/qmltests/Dash/tst_Dash.qml+56−71
@@ -112,6 +112,19 @@ Item {
112 mouseClick(tile);
112 mouseClick(tile);
113 }
113 }
114
114
115+ function open_manage_dash() {
116+ // Show the manage dash
117+ touchFlick(dash, dash.width / 2, dash.height - 1, dash.width / 2, units.gu(2));
118+ var bottomEdgeController = findInvisibleChild(dash, "bottomEdgeController");
119+ tryCompare(bottomEdgeController, "progress", 1);
120+
121+ // Make sure stuff is loaded
122+ var favScopesListCategory = findChild(dash, "scopesListCategoryfavorites");
123+ var favScopesListCategoryList = findChild(favScopesListCategory, "scopesListCategoryInnerList");
124+ waitForRendering(favScopesListCategoryList);
125+ tryCompare(favScopesListCategoryList, "currentIndex", 0);
126+ }
127+
115 function test_longNavigationFilterList() {
128 function test_longNavigationFilterList() {
116 // Select the scope with long navigation
129 // Select the scope with long navigation
117 dash.setCurrentScope("LongPrimaryNavigation")
130 dash.setCurrentScope("LongPrimaryNavigation")
@@ -121,6 +134,7 @@ Item {
121 var dashContentList = findChild(dashContent, "dashContentList")
134 var dashContentList = findChild(dashContent, "dashContentList")
122 var searchButton = findChild(dashContentList.currentItem, "search_button")
135 var searchButton = findChild(dashContentList.currentItem, "search_button")
123 var extraPanel = findChild(dashContentList.currentItem, "peExtraPanel")
136 var extraPanel = findChild(dashContentList.currentItem, "peExtraPanel")
137+ waitForRendering(extraPanel);
124 tryCompare(extraPanel, "visible", false)
138 tryCompare(extraPanel, "visible", false)
125
139
126 // Open the primaryNavigationFilter
140 // Open the primaryNavigationFilter
@@ -179,22 +193,16 @@ Item {
179 }
193 }
180
194
181 function test_manage_dash_clickscope_unfavoritable() {
195 function test_manage_dash_clickscope_unfavoritable() {
182− // Show the manage dash
196+ open_manage_dash();
183− touchFlick(dash, dash.width / 2, dash.height - 1, dash.width / 2, units.gu(2));
184− var bottomEdgeController = findInvisibleChild(dash, "bottomEdgeController");
185− tryCompare(bottomEdgeController, "progress", 1);
186−
187− // Make sure stuff is loaded
188− var favScopesListCategory = findChild(dash, "scopesListCategoryfavorites");
189− var favScopesListCategoryList = findChild(favScopesListCategory, "scopesListCategoryInnerList");
190− tryCompare(favScopesListCategoryList, "currentIndex", 0);
191
197
192 // Click scope star area is not visible (i.e. can't be unfavorited)
198 // Click scope star area is not visible (i.e. can't be unfavorited)
199+ var favScopesListCategoryList = findChild(findChild(dash, "scopesListCategoryfavorites"), "scopesListCategoryInnerList");
193 var clickScope = findChild(favScopesListCategoryList, "delegateclickscope");
200 var clickScope = findChild(favScopesListCategoryList, "delegateclickscope");
194 var starArea = findChild(clickScope, "starArea");
201 var starArea = findChild(clickScope, "starArea");
195 compare(starArea.visible, false);
202 compare(starArea.visible, false);
196
203
197 // Go back
204 // Go back
205+ var bottomEdgeController = findInvisibleChild(dash, "bottomEdgeController");
198 var scopesList = findChild(dash, "scopesList");
206 var scopesList = findChild(dash, "scopesList");
199 var scopesListPageHeader = findChild(scopesList, "pageHeader");
207 var scopesListPageHeader = findChild(scopesList, "pageHeader");
200 var backButton = findChild(scopesListPageHeader, "innerPageHeader").leadingActionBar;
208 var backButton = findChild(scopesListPageHeader, "innerPageHeader").leadingActionBar;
@@ -203,20 +211,14 @@ Item {
203 }
211 }
204
212
205 function test_manage_dash_select_same_favorite() {
213 function test_manage_dash_select_same_favorite() {
206− // Show the manage dash
214+ open_manage_dash();
207− touchFlick(dash, dash.width / 2, dash.height - 1, dash.width / 2, units.gu(2));
208− var bottomEdgeController = findInvisibleChild(dash, "bottomEdgeController");
209− tryCompare(bottomEdgeController, "progress", 1);
210−
211− // Make sure stuff is loaded
212− var favScopesListCategory = findChild(dash, "scopesListCategoryfavorites");
213− var favScopesListCategoryList = findChild(favScopesListCategory, "scopesListCategoryInnerList");
214− tryCompare(favScopesListCategoryList, "currentIndex", 0);
215
215
216 // Click in first item
216 // Click in first item
217+ var favScopesListCategoryList = findChild(findChild(dash, "scopesListCategoryfavorites"), "scopesListCategoryInnerList");
217 mouseClick(favScopesListCategoryList.currentItem);
218 mouseClick(favScopesListCategoryList.currentItem);
218
219
219 // Make sure animation went back
220 // Make sure animation went back
221+ var bottomEdgeController = findInvisibleChild(dash, "bottomEdgeController");
220 tryCompare(bottomEdgeController, "progress", 0);
222 tryCompare(bottomEdgeController, "progress", 0);
221
223
222 var dashContentList = findChild(dash, "dashContentList");
224 var dashContentList = findChild(dash, "dashContentList");
@@ -224,31 +226,25 @@ Item {
224 }
226 }
225
227
226 function test_manage_dash_select_different_favorite() {
228 function test_manage_dash_select_different_favorite() {
227− // Show the manage dash
229+ open_manage_dash();
228− touchFlick(dash, dash.width / 2, dash.height - 1, dash.width / 2, units.gu(2));
229− var bottomEdgeController = findInvisibleChild(dash, "bottomEdgeController");
230− tryCompare(bottomEdgeController, "progress", 1);
231−
232− // Make sure stuff is loaded
233− var favScopesListCategory = findChild(dash, "scopesListCategoryfavorites");
234− var favScopesListCategoryList = findChild(favScopesListCategory, "scopesListCategoryInnerList");
235− tryCompare(favScopesListCategoryList, "currentIndex", 0);
236
230
237 // Click in second item
231 // Click in second item
232+ var favScopesListCategoryList = findChild(findChild(dash, "scopesListCategoryfavorites"), "scopesListCategoryInnerList");
238 favScopesListCategoryList.currentIndex = 1;
233 favScopesListCategoryList.currentIndex = 1;
239 mouseClick(favScopesListCategoryList.currentItem);
234 mouseClick(favScopesListCategoryList.currentItem);
240
235
241 // Make sure animation went back
236 // Make sure animation went back
237+ var bottomEdgeController = findInvisibleChild(dash, "bottomEdgeController");
242 tryCompare(bottomEdgeController, "progress", 0);
238 tryCompare(bottomEdgeController, "progress", 0);
243 var dashContentList = findChild(dash, "dashContentList");
239 var dashContentList = findChild(dash, "dashContentList");
244 compare(dashContentList.currentIndex, 1);
240 compare(dashContentList.currentIndex, 1);
245 }
241 }
246
242
247 function test_manage_dash_select_non_favorite() {
243 function test_manage_dash_select_non_favorite() {
248− // Show the manage dash
244+ open_manage_dash();
249− touchFlick(dash, dash.width / 2, dash.height - 1, dash.width / 2, units.gu(2));
245+
250− var bottomEdgeController = findInvisibleChild(dash, "bottomEdgeController");
246+ // Ensure non favorites are visible
251− tryCompare(bottomEdgeController, "progress", 1);
247+ flickToYEnd(findChild(dash,"scopesListFlickable"));
252
248
253 // Make sure stuff is loaded
249 // Make sure stuff is loaded
254 var nonfavScopesListCategory = findChild(dash, "scopesListCategoryother");
250 var nonfavScopesListCategory = findChild(dash, "scopesListCategoryother");
@@ -268,6 +264,7 @@ Item {
268 tryCompare(dashTempScopeItem, "visible", true);
264 tryCompare(dashTempScopeItem, "visible", true);
269
265
270 // Check the manage dash is gone
266 // Check the manage dash is gone
267+ var bottomEdgeController = findInvisibleChild(dash, "bottomEdgeController");
271 tryCompare(bottomEdgeController, "progress", 0);
268 tryCompare(bottomEdgeController, "progress", 0);
272
269
273 // Go back
270 // Go back
@@ -296,6 +293,7 @@ Item {
296 tryCompare(bottomEdgeController, "progress", 1);
293 tryCompare(bottomEdgeController, "progress", 1);
297
294
298 // Go back
295 // Go back
296+ var bottomEdgeController = findInvisibleChild(dash, "bottomEdgeController");
299 var scopesList = findChild(dash, "scopesList");
297 var scopesList = findChild(dash, "scopesList");
300 var scopesListPageHeader = findChild(scopesList, "pageHeader");
298 var scopesListPageHeader = findChild(scopesList, "pageHeader");
301 var backButton = findChild(scopesListPageHeader, "innerPageHeader").leadingActionBar;
299 var backButton = findChild(scopesListPageHeader, "innerPageHeader").leadingActionBar;
@@ -358,10 +356,7 @@ Item {
358 }
356 }
359
357
360 function test_manage_dash_store_no_favorites() {
358 function test_manage_dash_store_no_favorites() {
361− // Show the manage dash
359+ open_manage_dash();
362− touchFlick(dash, dash.width / 2, dash.height - 1, dash.width / 2, units.gu(2));
363− var bottomEdgeController = findInvisibleChild(dash, "bottomEdgeController");
364− tryCompare(bottomEdgeController, "progress", 1);
365
360
366 // clear the favorite scopes
361 // clear the favorite scopes
367 scopes.clearFavorites();
362 scopes.clearFavorites();
@@ -379,6 +374,7 @@ Item {
379
374
380 spy.wait();
375 spy.wait();
381 compare(spy.signalArguments[0][0], "scope://com.canonical.scopes.clickstore");
376 compare(spy.signalArguments[0][0], "scope://com.canonical.scopes.clickstore");
377+ var bottomEdgeController = findInvisibleChild(dash, "bottomEdgeController");
382 tryCompare(bottomEdgeController, "progress", 0);
378 tryCompare(bottomEdgeController, "progress", 0);
383 }
379 }
384
380
@@ -387,17 +383,10 @@ Item {
387 compare(dashContentList.currentIndex, 0);
383 compare(dashContentList.currentIndex, 0);
388 compare(dashContentList.currentItem.scopeId, "MockScope1");
384 compare(dashContentList.currentItem.scopeId, "MockScope1");
389
385
390− // Show the manage dash
386+ open_manage_dash();
391− touchFlick(dash, dash.width / 2, dash.height - 1, dash.width / 2, units.gu(2));
392− var bottomEdgeController = findInvisibleChild(dash, "bottomEdgeController");
393− tryCompare(bottomEdgeController, "progress", 1);
394−
395− // Make sure stuff is loaded
396− var favScopesListCategory = findChild(dash, "scopesListCategoryfavorites");
397− var favScopesListCategoryList = findChild(favScopesListCategory, "scopesListCategoryInnerList");
398− tryCompare(favScopesListCategoryList, "currentIndex", 0);
399
387
400 // Enter edit mode
388 // Enter edit mode
389+ var favScopesListCategoryList = findChild(findChild(dash, "scopesListCategoryfavorites"), "scopesListCategoryInnerList");
401 var scopesList = findChild(dash, "scopesList");
390 var scopesList = findChild(dash, "scopesList");
402 var clickScope = findChild(favScopesListCategoryList, "delegateclickscope");
391 var clickScope = findChild(favScopesListCategoryList, "delegateclickscope");
403 mousePress(clickScope);
392 mousePress(clickScope);
@@ -408,6 +397,7 @@ Item {
408 touchFlick(starArea, 0, 0, 0, -units.gu(10));
397 touchFlick(starArea, 0, 0, 0, -units.gu(10));
409
398
410 // Exit edit mode and go back
399 // Exit edit mode and go back
400+ var bottomEdgeController = findInvisibleChild(dash, "bottomEdgeController");
411 var scopesList = findChild(dash, "scopesList");
401 var scopesList = findChild(dash, "scopesList");
412 var scopesListPageHeader = findChild(scopesList, "pageHeader");
402 var scopesListPageHeader = findChild(scopesList, "pageHeader");
413 var backButton = findChild(scopesListPageHeader, "innerPageHeader").leadingActionBar;
403 var backButton = findChild(scopesListPageHeader, "innerPageHeader").leadingActionBar;
@@ -429,17 +419,10 @@ Item {
429 compare(dashContentList.currentIndex, 0);
419 compare(dashContentList.currentIndex, 0);
430 compare(dashContentList.currentItem.scopeId, "MockScope1");
420 compare(dashContentList.currentItem.scopeId, "MockScope1");
431
421
432− // Show the manage dash
422+ open_manage_dash();
433− touchFlick(dash, dash.width / 2, dash.height - 1, dash.width / 2, units.gu(2));
434− var bottomEdgeController = findInvisibleChild(dash, "bottomEdgeController");
435− tryCompare(bottomEdgeController, "progress", 1);
436−
437− // Make sure stuff is loaded
438− var favScopesListCategory = findChild(dash, "scopesListCategoryfavorites");
439− var favScopesListCategoryList = findChild(favScopesListCategory, "scopesListCategoryInnerList");
440− tryCompare(favScopesListCategoryList, "currentIndex", 0);
441
423
442 // Enter edit mode
424 // Enter edit mode
425+ var favScopesListCategoryList = findChild(findChild(dash, "scopesListCategoryfavorites"), "scopesListCategoryInnerList");
443 var scopesList = findChild(dash, "scopesList");
426 var scopesList = findChild(dash, "scopesList");
444 var clickScope = findChild(favScopesListCategoryList, "delegateclickscope");
427 var clickScope = findChild(favScopesListCategoryList, "delegateclickscope");
445 mousePress(clickScope);
428 mousePress(clickScope);
@@ -459,6 +442,8 @@ Item {
459 mouseClick(backButton);
442 mouseClick(backButton);
460
443
461 // Click on third scope
444 // Click on third scope
445+ var favScopesListCategoryList = findChild(findChild(dash, "scopesListCategoryfavorites"), "scopesListCategoryInnerList");
446+ var bottomEdgeController = findInvisibleChild(dash, "bottomEdgeController");
462 var mockScope5 = findChild(favScopesListCategoryList, "delegateMockScope5");
447 var mockScope5 = findChild(favScopesListCategoryList, "delegateMockScope5");
463 waitForRendering(mockScope5)
448 waitForRendering(mockScope5)
464 mouseClick(mockScope5);
449 mouseClick(mockScope5);
@@ -472,14 +457,12 @@ Item {
472 compare(dashContentList.currentIndex, 0);
457 compare(dashContentList.currentIndex, 0);
473 compare(dashContentList.currentItem.scopeId, "MockScope1");
458 compare(dashContentList.currentItem.scopeId, "MockScope1");
474
459
475− // Show the manage dash
460+ open_manage_dash();
476− touchFlick(dash, dash.width / 2, dash.height - 1, dash.width / 2, units.gu(2));
477− var bottomEdgeController = findInvisibleChild(dash, "bottomEdgeController");
478− tryCompare(bottomEdgeController, "progress", 1);
479
461
480 var dashCommunicatorService = findInvisibleChild(dash, "dashCommunicatorService");
462 var dashCommunicatorService = findInvisibleChild(dash, "dashCommunicatorService");
481 dashCommunicatorService.mockSetCurrentScope(1, true, false);
463 dashCommunicatorService.mockSetCurrentScope(1, true, false);
482
464
465+ var bottomEdgeController = findInvisibleChild(dash, "bottomEdgeController");
483 tryCompare(bottomEdgeController, "progress", 0);
466 tryCompare(bottomEdgeController, "progress", 0);
484 tryCompare(dashContentList, "currentIndex", 1)
467 tryCompare(dashContentList, "currentIndex", 1)
485 }
468 }
@@ -507,10 +490,10 @@ Item {
507 }
490 }
508
491
509 function test_close_temp_scope_preview_opening_scope() {
492 function test_close_temp_scope_preview_opening_scope() {
510− // Show the manage dash
493+ open_manage_dash();
511− touchFlick(dash, dash.width / 2, dash.height - 1, dash.width / 2, units.gu(2));
494+
512− var bottomEdgeController = findInvisibleChild(dash, "bottomEdgeController");
495+ // Ensure non favorite scopes are visible
513− tryCompare(bottomEdgeController, "progress", 1);
496+ flickToYEnd(findChild(dash, "scopesListFlickable"));
514
497
515 // Make sure stuff is loaded
498 // Make sure stuff is loaded
516 var nonfavScopesListCategory = findChild(dash, "scopesListCategoryother");
499 var nonfavScopesListCategory = findChild(dash, "scopesListCategoryother");
@@ -530,6 +513,7 @@ Item {
530 tryCompare(dashTempScopeItem, "visible", true);
513 tryCompare(dashTempScopeItem, "visible", true);
531
514
532 // Check the manage dash is gone
515 // Check the manage dash is gone
516+ var bottomEdgeController = findInvisibleChild(dash, "bottomEdgeController");
533 tryCompare(bottomEdgeController, "progress", 0);
517 tryCompare(bottomEdgeController, "progress", 0);
534
518
535 // Open preview
519 // Open preview
@@ -580,12 +564,10 @@ Item {
580 UriHandler.opened("scopes://clickscope");
564 UriHandler.opened("scopes://clickscope");
581 tryCompare(dashContentList, "currentIndex", 1);
565 tryCompare(dashContentList, "currentIndex", 1);
582
566
583− // Show the manage dash
567+ open_manage_dash();
584− touchFlick(dash, dash.width / 2, dash.height - 1, dash.width / 2, units.gu(2));
585− var bottomEdgeController = findInvisibleChild(dash, "bottomEdgeController");
586− tryCompare(bottomEdgeController, "progress", 1);
587
568
588 // UriHandler changes to a scope and closes the manage scopes
569 // UriHandler changes to a scope and closes the manage scopes
570+ var bottomEdgeController = findInvisibleChild(dash, "bottomEdgeController");
589 UriHandler.opened("scopes://MockScope1");
571 UriHandler.opened("scopes://MockScope1");
590 tryCompare(dashContentList, "currentIndex", 0);
572 tryCompare(dashContentList, "currentIndex", 0);
591 tryCompare(bottomEdgeController, "progress", 0);
573 tryCompare(bottomEdgeController, "progress", 0);
@@ -603,11 +585,11 @@ Item {
603 tryCompare(dashContent, "subPageShown", false);
585 tryCompare(dashContent, "subPageShown", false);
604
586
605 // Go to a temp scope
587 // Go to a temp scope
606− touchFlick(dash, dash.width / 2, dash.height - 1, dash.width / 2, units.gu(2));
588+ open_manage_dash();
607− var bottomEdgeController = findInvisibleChild(dash, "bottomEdgeController");
589+
608− tryCompare(bottomEdgeController, "progress", 1);
609 var nonfavScopesListCategory = findChild(dash, "scopesListCategoryother");
590 var nonfavScopesListCategory = findChild(dash, "scopesListCategoryother");
610 var nonfavScopesListCategoryList = findChild(nonfavScopesListCategory, "scopesListCategoryInnerList");
591 var nonfavScopesListCategoryList = findChild(nonfavScopesListCategory, "scopesListCategoryInnerList");
592+ flickToYEnd(findChild(dash, "scopesListFlickable"));
611 tryCompare(nonfavScopesListCategoryList, "currentIndex", 0);
593 tryCompare(nonfavScopesListCategoryList, "currentIndex", 0);
612 mouseClick(nonfavScopesListCategoryList.currentItem);
594 mouseClick(nonfavScopesListCategoryList.currentItem);
613 var dashTempScopeItem = findChild(dash, "dashTempScopeItem");
595 var dashTempScopeItem = findChild(dash, "dashTempScopeItem");
@@ -666,12 +648,15 @@ Item {
666 function test_tempScopeItemXOnResize()
648 function test_tempScopeItemXOnResize()
667 {
649 {
668 // Go to a temp scope
650 // Go to a temp scope
669− touchFlick(dash, dash.width / 2, dash.height - 1, dash.width / 2, units.gu(2));
651+ open_manage_dash();
670− var bottomEdgeController = findInvisibleChild(dash, "bottomEdgeController");
652+
671− tryCompare(bottomEdgeController, "progress", 1);
672 var nonfavScopesListCategory = findChild(dash, "scopesListCategoryother");
653 var nonfavScopesListCategory = findChild(dash, "scopesListCategoryother");
673 var nonfavScopesListCategoryList = findChild(nonfavScopesListCategory, "scopesListCategoryInnerList");
654 var nonfavScopesListCategoryList = findChild(nonfavScopesListCategory, "scopesListCategoryInnerList");
655+ var scopesListFlickable = findChild(dash, "scopesListFlickable");
674 tryCompare(nonfavScopesListCategoryList, "currentIndex", 0);
656 tryCompare(nonfavScopesListCategoryList, "currentIndex", 0);
657+
658+ // Ensure the non-favorites, located at the bottom, are visible
659+ flickToYEnd(scopesListFlickable);
675 mouseClick(nonfavScopesListCategoryList.currentItem);
660 mouseClick(nonfavScopesListCategoryList.currentItem);
676 var dashTempScopeItem = findChild(dash, "dashTempScopeItem");
661 var dashTempScopeItem = findChild(dash, "dashTempScopeItem");
677 tryCompare(dashTempScopeItem, "x", 0);
662 tryCompare(dashTempScopeItem, "x", 0);
tests/qmltests/Dash/tst_DashContent.qml+7−1
@@ -70,7 +70,7 @@ Item {
70 function loadScopes() {
70 function loadScopes() {
71 scopeLoadedSpy.clear();
71 scopeLoadedSpy.clear();
72 scopesModel.load();
72 scopesModel.load();
73− tryCompare(scopeLoadedSpy, "count", 6, 15000);
73+ tryCompare(scopeLoadedSpy, "count", 8, 15000);
74 tryCompare(scopesModel, "loaded", true);
74 tryCompare(scopesModel, "loaded", true);
75 tryCompareFunction(function() {
75 tryCompareFunction(function() {
76 var mockScope1Loader = findChild(shell, "scopeLoader0");
76 var mockScope1Loader = findChild(shell, "scopeLoader0");
@@ -610,6 +610,12 @@ Item {
610 }
610 }
611
611
612 function test_noDelegateCreationDestructionOnMove() {
612 function test_noDelegateCreationDestructionOnMove() {
613+ // Go to scope 1 and back so that items are created
614+ // and so this test makes sense
615+ dashContent.setCurrentScopeAtIndex(1);
616+ waitForRendering(dashContent);
617+ dashContent.setCurrentScopeAtIndex(0);
618+
613 // Our cards are of type AbstractButton as defined in CardCreator.js
619 // Our cards are of type AbstractButton as defined in CardCreator.js
614 // This gives also other things that are not cards but for our purpose it
620 // This gives also other things that are not cards but for our purpose it
615 // does not matter
621 // does not matter
tests/qmltests/Dash/tst_GenericScopeView.qml+7
@@ -76,6 +76,7 @@ Item {
76 tryCompare(scopes, "loaded", true);
76 tryCompare(scopes, "loaded", true);
77
77
78 genericScopeView.scope = scopes.getScope(2);
78 genericScopeView.scope = scopes.getScope(2);
79+ genericScopeView.isCurrent = true;
79 shell.width = units.gu(120);
80 shell.width = units.gu(120);
80 genericScopeView.categoryView.positionAtBeginning();
81 genericScopeView.categoryView.positionAtBeginning();
81 waitForRendering(genericScopeView.categoryView);
82 waitForRendering(genericScopeView.categoryView);
@@ -116,6 +117,7 @@ Item {
116 }
117 }
117
118
118 function test_isActive() {
119 function test_isActive() {
120+ genericScopeView.isCurrent = false
119 tryCompare(genericScopeView.scope, "isActive", false)
121 tryCompare(genericScopeView.scope, "isActive", false)
120 genericScopeView.isCurrent = true
122 genericScopeView.isCurrent = true
121 tryCompare(genericScopeView.scope, "isActive", true)
123 tryCompare(genericScopeView.scope, "isActive", true)
@@ -140,6 +142,7 @@ Item {
140 }
142 }
141
143
142 function test_searchQuery() {
144 function test_searchQuery() {
145+ genericScopeView.isCurrent = false
143 genericScopeView.scope = scopes.getScope(0);
146 genericScopeView.scope = scopes.getScope(0);
144 genericScopeView.scope.searchQuery = "test";
147 genericScopeView.scope.searchQuery = "test";
145 genericScopeView.scope = scopes.getScope(1);
148 genericScopeView.scope = scopes.getScope(1);
@@ -151,6 +154,7 @@ Item {
151 }
154 }
152
155
153 function test_changeScope() {
156 function test_changeScope() {
157+ genericScopeView.isCurrent = false;
154 genericScopeView.scope.searchQuery = "test"
158 genericScopeView.scope.searchQuery = "test"
155 var originalScopeId = genericScopeView.scope.id;
159 var originalScopeId = genericScopeView.scope.id;
156 genericScopeView.scope = scopes.getScope(originalScopeId + 1)
160 genericScopeView.scope = scopes.getScope(originalScopeId + 1)
@@ -458,6 +462,7 @@ Item {
458 function test_seeAllTwoCategoriesScenario1() {
462 function test_seeAllTwoCategoriesScenario1() {
459 mockScope.setId("mockScope");
463 mockScope.setId("mockScope");
460 mockScope.setName("Mock Scope");
464 mockScope.setName("Mock Scope");
465+ mockScope.isActive = true;
461 mockScope.categories.setCount(2);
466 mockScope.categories.setCount(2);
462 mockScope.categories.resultModel(0).setResultCount(50);
467 mockScope.categories.resultModel(0).setResultCount(50);
463 mockScope.categories.resultModel(1).setResultCount(25);
468 mockScope.categories.resultModel(1).setResultCount(25);
@@ -501,6 +506,7 @@ Item {
501 function test_seeAllTwoCategoriesScenario2() {
506 function test_seeAllTwoCategoriesScenario2() {
502 mockScope.setId("mockScope");
507 mockScope.setId("mockScope");
503 mockScope.setName("Mock Scope");
508 mockScope.setName("Mock Scope");
509+ mockScope.isActive = true;
504 mockScope.categories.setCount(2);
510 mockScope.categories.setCount(2);
505 mockScope.categories.resultModel(0).setResultCount(25);
511 mockScope.categories.resultModel(0).setResultCount(25);
506 mockScope.categories.resultModel(1).setResultCount(50);
512 mockScope.categories.resultModel(1).setResultCount(50);
@@ -629,6 +635,7 @@ Item {
629 var category0 = findChild(categoryListView, "dashCategory0");
635 var category0 = findChild(categoryListView, "dashCategory0");
630 waitForRendering(category0);
636 waitForRendering(category0);
631
637
638+ tryCompareFunction(function() { return category0.item != null; }, true);
632 var cardGrid = category0.item;
639 var cardGrid = category0.item;
633 compare(cardGrid.cardTool.template["non-interactive"], true);
640 compare(cardGrid.cardTool.template["non-interactive"], true);
634
641
tests/qmltests/Launcher/tst_Launcher.qml+23−3
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2013-2014 Canonical Ltd.
2+ * Copyright 2013-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
@@ -210,6 +210,11 @@ Rectangle {
210 target: LauncherModel
210 target: LauncherModel
211 }
211 }
212
212
213+ SignalSpy {
214+ id: clickThroughSpy
215+ target: clickThroughTester
216+ }
217+
213 Item {
218 Item {
214 id: fakeDismissTimer
219 id: fakeDismissTimer
215 property bool running: false
220 property bool running: false
@@ -231,6 +236,8 @@ Rectangle {
231
236
232 property Item launcher: launcherLoader.status === Loader.Ready ? launcherLoader.item : null
237 property Item launcher: launcherLoader.status === Loader.Ready ? launcherLoader.item : null
233 function cleanup() {
238 function cleanup() {
239+ signalSpy.clear();
240+ clickThroughSpy.clear();
234 launcherLoader.active = false;
241 launcherLoader.active = false;
235 // Loader.status might be Loader.Null and Loader.item might be null but the Loader
242 // Loader.status might be Loader.Null and Loader.item might be null but the Loader
236 // item might still be alive. So if we set Loader.active back to true
243 // item might still be alive. So if we set Loader.active back to true
@@ -925,7 +932,6 @@ Rectangle {
925
932
926 var quickListEntry = findChild(quickList, "quickListEntry" + data.index)
933 var quickListEntry = findChild(quickList, "quickListEntry" + data.index)
927
934
928− signalSpy.clear();
929 signalSpy.signalName = "quickListTriggered"
935 signalSpy.signalName = "quickListTriggered"
930
936
931 mouseClick(quickListEntry)
937 mouseClick(quickListEntry)
@@ -1218,7 +1224,6 @@ Rectangle {
1218 launcher.openForKeyboardNavigation();
1224 launcher.openForKeyboardNavigation();
1219 waitForRendering(launcher);
1225 waitForRendering(launcher);
1220
1226
1221− signalSpy.clear();
1222 signalSpy.signalName = "quickListTriggered"
1227 signalSpy.signalName = "quickListTriggered"
1223
1228
1224 keyClick(Qt.Key_Down); // Down to launcher item 0
1229 keyClick(Qt.Key_Down); // Down to launcher item 0
@@ -1311,5 +1316,20 @@ Rectangle {
1311 compare(surfacePipRepeater.model, Math.min(3, LauncherModel.get(i).surfaceCount))
1316 compare(surfacePipRepeater.model, Math.min(3, LauncherModel.get(i).surfaceCount))
1312 }
1317 }
1313 }
1318 }
1319+
1320+ function test_preventMouseEventsThru() {
1321+ dragLauncherIntoView();
1322+ var launcherPanel = findChild(launcher, "launcherPanel");
1323+ tryCompare(launcherPanel, "visible", true);
1324+
1325+ clickThroughSpy.signalName = "wheel";
1326+ mouseWheel(launcherPanel, launcherPanel.width/2, launcherPanel.height/2, 10, 10);
1327+ tryCompare(clickThroughSpy, "count", 0);
1328+
1329+ clickThroughSpy.clear();
1330+ clickThroughSpy.signalName = "clicked";
1331+ mouseWheel(launcherPanel, launcherPanel.width/2, launcherPanel.height/2, Qt.RightButton);
1332+ tryCompare(clickThroughSpy, "count", 0);
1333+ }
1314 }
1334 }
1315}
1335}
tests/qmltests/Panel/tst_IndicatorsMenu.qml+29−1
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2013-2014 Canonical Ltd.
2+ * Copyright 2013-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
@@ -27,6 +27,16 @@ IndicatorTest {
27 width: units.gu(80)
27 width: units.gu(80)
28 height: units.gu(71)
28 height: units.gu(71)
29
29
30+ SignalSpy {
31+ id: clickThroughSpy
32+ target: clickThroughTester
33+ }
34+
35+ MouseArea {
36+ id: clickThroughTester
37+ anchors.fill: root
38+ }
39+
30 RowLayout {
40 RowLayout {
31 anchors.fill: parent
41 anchors.fill: parent
32 anchors.margins: units.gu(1)
42 anchors.margins: units.gu(1)
@@ -112,6 +122,10 @@ IndicatorTest {
112 indicatorsMenu.verticalVelocityThreshold = 0.5
122 indicatorsMenu.verticalVelocityThreshold = 0.5
113 }
123 }
114
124
125+ function cleanup() {
126+ clickThroughSpy.clear();
127+ }
128+
115 function get_indicator_item(index) {
129 function get_indicator_item(index) {
116 var indicatorItem = findChild(indicatorsMenu, root.originalModelData[index]["identifier"] + "-panelItem");
130 var indicatorItem = findChild(indicatorsMenu, root.originalModelData[index]["identifier"] + "-panelItem");
117 verify(indicatorItem !== null);
131 verify(indicatorItem !== null);
@@ -257,5 +271,19 @@ IndicatorTest {
257
271
258 touchRelease(indicatorsMenu, nextItemMappedPositionX, indicatorsMenu.openedHeight / 3);
272 touchRelease(indicatorsMenu, nextItemMappedPositionX, indicatorsMenu.openedHeight / 3);
259 }
273 }
274+
275+ function test_preventMouseEventsThru() {
276+ indicatorsMenu.show();
277+ tryCompare(indicatorsMenu, "fullyOpened", true);
278+
279+ clickThroughSpy.signalName = "wheel";
280+ mouseWheel(indicatorsMenu, indicatorsMenu.width/2, indicatorsMenu.height/2, 10, 10);
281+ tryCompare(clickThroughSpy, "count", 0);
282+
283+ clickThroughSpy.clear();
284+ clickThroughSpy.signalName = "clicked";
285+ mouseClick(indicatorsMenu, indicatorsMenu.width/2, indicatorsMenu.height/2, Qt.RightButton);
286+ tryCompare(clickThroughSpy, "count", 0);
287+ }
260 }
288 }
261}
289}
tests/qmltests/Stages/tst_DesktopStage.qml+135
@@ -89,6 +89,13 @@ Item {
89 topLevelSurfaceList: topSurfaceList
89 topLevelSurfaceList: topSurfaceList
90 }
90 }
91 }
91 }
92+
93+ MouseArea {
94+ id: clickThroughTester
95+ anchors.fill: desktopStageLoader.item
96+ acceptedButtons: Qt.AllButtons
97+ hoverEnabled: true
98+ }
92 }
99 }
93
100
94 Rectangle {
101 Rectangle {
@@ -140,6 +147,11 @@ Item {
140 }
147 }
141 }
148 }
142
149
150+ SignalSpy {
151+ id: mouseEaterSpy
152+ target: clickThroughTester
153+ }
154+
143 UnityTestCase {
155 UnityTestCase {
144 id: testCase
156 id: testCase
145 name: "DesktopStage"
157 name: "DesktopStage"
@@ -179,6 +191,7 @@ Item {
179 desktopStageLoader.active = true;
191 desktopStageLoader.active = true;
180 tryCompare(desktopStageLoader, "status", Loader.Ready);
192 tryCompare(desktopStageLoader, "status", Loader.Ready);
181
193
194+ mouseEaterSpy.clear();
182 }
195 }
183
196
184 function waitUntilAppSurfaceShowsUp(surfaceId) {
197 function waitUntilAppSurfaceShowsUp(surfaceId) {
@@ -504,6 +517,33 @@ Item {
504 tryCompare(MirFocusController, "focusedSurface", null); // verify still no surface is focused
517 tryCompare(MirFocusController, "focusedSurface", null); // verify still no surface is focused
505 }
518 }
506
519
520+ function test_oskDisplacesWindow_data() {
521+ return [
522+ {tag: "no need to displace", windowHeight: units.gu(10), windowY: units.gu(5), targetDisplacement: units.gu(5)},
523+ {tag: "displace to top", windowHeight: units.gu(50), windowY: units.gu(10), targetDisplacement: PanelState.panelHeight},
524+ {tag: "displace a bit", windowHeight: units.gu(40), windowY: units.gu(10), targetDisplacement: (root.height / 2) - units.gu(40)},
525+ ]
526+ }
527+
528+ function test_oskDisplacesWindow(data) {
529+ var dashAppDelegate = startApplication("unity8-dash");
530+ var oldOSKState = SurfaceManager.inputMethodSurface.state;
531+ SurfaceManager.inputMethodSurface.state = Mir.RestoredState;
532+ verify(dashAppDelegate);
533+ dashAppDelegate.requestedHeight = data.windowHeight;
534+ dashAppDelegate.requestedY = data.windowY;
535+ UbuntuKeyboardInfo.height = 0;
536+ var initialY = dashAppDelegate.y;
537+ verify(initialY > PanelState.panelHeight);
538+
539+ UbuntuKeyboardInfo.height = root.height / 2;
540+ tryCompare(dashAppDelegate, "y", data.targetDisplacement);
541+
542+ UbuntuKeyboardInfo.height = 0;
543+ tryCompare(dashAppDelegate, "y", initialY);
544+ SurfaceManager.inputMethodSurface.state = oldOSKState;
545+ }
546+
507 function test_minimizeApplicationHidesSurface() {
547 function test_minimizeApplicationHidesSurface() {
508 compare(topSurfaceList.applicationAt(0).appId, "unity8-dash");
548 compare(topSurfaceList.applicationAt(0).appId, "unity8-dash");
509 var dashSurface = topSurfaceList.surfaceAt(0);
549 var dashSurface = topSurfaceList.surfaceAt(0);
@@ -677,5 +717,100 @@ Item {
677 var closeButton = findChild(dashAppDelegate, "closeWindowButton");
717 var closeButton = findChild(dashAppDelegate, "closeWindowButton");
678 tryCompare(closeButton, "visible", false);
718 tryCompare(closeButton, "visible", false);
679 }
719 }
720+
721+ function test_hideMaximizeButtonWhenSizeConstrained() {
722+ var dialerDelegate = startApplication("dialer-app");
723+
724+ var dialerMaximizeButton = findChild(dialerDelegate, "maximizeWindowButton");
725+ tryCompare(dialerMaximizeButton, "visible", true);
726+
727+ // add size restrictions, smaller than our stage
728+ dialerDelegate.surface.setMaximumWidth(40);
729+ dialerDelegate.surface.setMaximumHeight(30);
730+ tryCompare(dialerMaximizeButton, "visible", false);
731+
732+ // try double clicking the decoration, shouldn't maximize it
733+ var sizeBefore = Qt.size(dialerDelegate.width, dialerDelegate.height);
734+ var deco = findChild(dialerDelegate, "appWindowDecoration");
735+ verify(deco);
736+ tryCompare(deco, "maximizeButtonShown", false);
737+ mouseDoubleClick(deco);
738+ var sizeAfter = Qt.size(dialerDelegate.width, dialerDelegate.height);
739+ tryCompareFunction(function(){ return sizeBefore; }, sizeAfter);
740+
741+ // remove restrictions, the maximize button should again be visible
742+ dialerDelegate.surface.setMaximumWidth(0);
743+ dialerDelegate.surface.setMaximumHeight(0);
744+ tryCompare(dialerMaximizeButton, "visible", true);
745+ }
746+
747+ function test_canMoveWindowWithLeftMouseButtonOnly_data() {
748+ return [
749+ {tag: "left mouse button", button: Qt.LeftButton },
750+ {tag: "right mouse button", button: Qt.RightButton },
751+ {tag: "middle mouse button", button: Qt.MiddleButton }
752+ ]
753+ }
754+
755+ function test_canMoveWindowWithLeftMouseButtonOnly(data) {
756+ var appDelegate = startApplication("dialer-app");
757+ verify(appDelegate);
758+
759+ var posBefore = Qt.point(appDelegate.x, appDelegate.y);
760+
761+ mousePress(appDelegate, appDelegate.width / 2, units.gu(1), data.button);
762+ mouseMove(appDelegate, appDelegate.width / 2, -units.gu(100), undefined /* delay */, data.button);
763+
764+ var posAfter = Qt.point(appDelegate.x, appDelegate.y);
765+
766+ tryCompareFunction(function(){return posBefore == posAfter;}, data.button !== Qt.LeftButton ? true : false);
767+ }
768+
769+ function test_preventMouseEventsThruDesktopSpread() {
770+ var spread = findChild(desktopStage, "spread");
771+ verify(spread);
772+ spread.show();
773+ tryCompareFunction( function(){ return spread.ready }, true );
774+
775+ mouseEaterSpy.signalName = "wheel";
776+ mouseWheel(spread, spread.width/2, spread.height/2, 10, 10);
777+ tryCompare(mouseEaterSpy, "count", 0);
778+
779+ mouseEaterSpy.clear();
780+ mouseEaterSpy.signalName = "clicked";
781+ mouseClick(spread, spread.width/2, spread.height/2, Qt.RightButton);
782+ tryCompare(mouseEaterSpy, "count", 0);
783+
784+ spread.cancel();
785+ }
786+
787+ function test_eatWindowDecorationMouseEvents_data() {
788+ return [
789+ {tag: "left mouse click", signalName: "clicked", button: Qt.LeftButton },
790+ {tag: "right mouse click", signalName: "clicked", button: Qt.RightButton },
791+ {tag: "middle mouse click", signalName: "clicked", button: Qt.MiddleButton },
792+ {tag: "mouse wheel", signalName: "wheel", button: Qt.MiddleButton },
793+ {tag: "double click (RMB)", signalName: "doubleClicked", button: Qt.RightButton },
794+ ]
795+ }
796+
797+ function test_eatWindowDecorationMouseEvents(data) {
798+ var dialerAppDelegate = startApplication("dialer-app");
799+ verify(dialerAppDelegate);
800+ var decoration = findChild(dialerAppDelegate, "appWindowDecoration");
801+ verify(decoration);
802+
803+ mouseEaterSpy.signalName = data.signalName;
804+ if (data.signalName === "wheel") {
805+ mouseWheel(decoration, decoration.width/2, decoration.height/2, 20, 20);
806+ } else if (data.signalName === "clicked") {
807+ mouseClick(decoration, decoration.width/2, decoration.height/2, data.button);
808+ } else {
809+ mouseDoubleClick(decoration, decoration.width/2, decoration.height/2, data.button);
810+ tryCompare(dialerAppDelegate, "maximized", false);
811+ }
812+
813+ tryCompare(mouseEaterSpy, "count", 0);
814+ }
680 }
815 }
681}
816}
tests/qmltests/Stages/tst_TabletStage.qml+162−35
@@ -242,6 +242,48 @@ Rectangle {
242 appWindow.width * 0.1, -appWindow.height / 2);
242 appWindow.width * 0.1, -appWindow.height / 2);
243 }
243 }
244
244
245+ function dragToSideStage(surfaceId) {
246+ sideStage.showNow();
247+ var targetAppDelegate = findChild(tabletStage, "spreadDelegate_" + surfaceId);
248+
249+ var pos = tabletStage.width - sideStage.width - (tabletStage.width - sideStage.width) / 2;
250+ var end_pos = tabletStage.width - sideStage.width / 2;
251+
252+ multiTouchDragUntil([0,1,2],
253+ tabletStage,
254+ pos,
255+ tabletStage.height / 2,
256+ units.gu(3),
257+ 0,
258+ function() {
259+ pos += units.gu(3);
260+ return sideStage.shown && !sideStage.showAnimation.running &&
261+ pos >= end_pos;
262+ });
263+ tryCompare(targetAppDelegate, "stage", ApplicationInfoInterface.SideStage);
264+ }
265+
266+ function dragToMainStage(surfaceId) {
267+ sideStage.showNow();
268+ var targetAppDelegate = findChild(tabletStage, "spreadDelegate_" + surfaceId);
269+ verify(targetAppDelegate);
270+
271+ var pos = tabletStage.width - sideStage.width / 2;
272+ var end_pos = tabletStage.width - sideStage.width - (tabletStage.width - sideStage.width) / 2;
273+
274+ multiTouchDragUntil([0,1,2],
275+ tabletStage,
276+ pos,
277+ tabletStage.height / 2,
278+ -units.gu(3),
279+ 0,
280+ function() {
281+ pos -= units.gu(3);
282+ return pos <= end_pos;
283+ });
284+ tryCompare(targetAppDelegate, "stage", ApplicationInfoInterface.MainStage);
285+ }
286+
245 function test_tappingSwitchesFocusBetweenStages() {
287 function test_tappingSwitchesFocusBetweenStages() {
246 WindowStateStorage.saveStage(dialerCheckBox.appId, ApplicationInfoInterface.SideStage)
288 WindowStateStorage.saveStage(dialerCheckBox.appId, ApplicationInfoInterface.SideStage)
247
289
@@ -493,14 +535,36 @@ Rectangle {
493 function() { return sideStage.shown; });
535 function() { return sideStage.shown; });
494 }
536 }
495
537
496− function test_applicationLoadsInCorrectStage_data() {
538+ function test_applicationLoadsInDefaultStage_data() {
539+ return [
540+ { tag: "MainStage", appId: "webbrowser-app", mainStageAppId: "webbrowser-app", sideStageAppId: "" },
541+ { tag: "SideStage", appId: "dialer-app", mainStageAppId: "unity8-dash", sideStageAppId: "dialer-app" },
542+ ];
543+ }
544+
545+ function test_applicationLoadsInDefaultStage(data) {
546+ var stagesPriv = findInvisibleChild(tabletStage, "stagesPriv");
547+ verify(stagesPriv);
548+
549+ tryCompare(stagesPriv, "mainStageAppId", "unity8-dash");
550+ tryCompare(stagesPriv, "sideStageAppId", "");
551+
552+ var appSurfaceId = topSurfaceList.nextId;
553+ var app = ApplicationManager.startApplication(data.appId);
554+ waitUntilAppSurfaceShowsUp(appSurfaceId);
555+
556+ tryCompare(stagesPriv, "mainStageAppId", data.mainStageAppId);
557+ tryCompare(stagesPriv, "sideStageAppId", data.sideStageAppId);
558+ }
559+
560+ function test_applicationLoadsInSavedStage_data() {
497 return [
561 return [
498 { tag: "MainStage", stage: ApplicationInfoInterface.MainStage, mainStageAppId: "webbrowser-app", sideStageAppId: ""},
562 { tag: "MainStage", stage: ApplicationInfoInterface.MainStage, mainStageAppId: "webbrowser-app", sideStageAppId: ""},
499 { tag: "SideStage", stage: ApplicationInfoInterface.SideStage, mainStageAppId: "unity8-dash", sideStageAppId: "webbrowser-app" },
563 { tag: "SideStage", stage: ApplicationInfoInterface.SideStage, mainStageAppId: "unity8-dash", sideStageAppId: "webbrowser-app" },
500 ];
564 ];
501 }
565 }
502
566
503− function test_applicationLoadsInCorrectStage(data) {
567+ function test_applicationLoadsInSavedStage(data) {
504 WindowStateStorage.saveStage(webbrowserCheckBox.appId, data.stage)
568 WindowStateStorage.saveStage(webbrowserCheckBox.appId, data.stage)
505
569
506 var stagesPriv = findInvisibleChild(tabletStage, "stagesPriv");
570 var stagesPriv = findInvisibleChild(tabletStage, "stagesPriv");
@@ -519,13 +583,13 @@ Rectangle {
519
583
520 function test_applicationSavesLastStage_data() {
584 function test_applicationSavesLastStage_data() {
521 return [
585 return [
522− { tag: "MainStage", stage: ApplicationInfoInterface.MainStage},
586+ { tag: "MainStage", fromStage: ApplicationInfoInterface.MainStage, toStage: ApplicationInfoInterface.SideStage },
523− { tag: "SideStage", stage: ApplicationInfoInterface.SideStage},
587+ { tag: "SideStage", fromStage: ApplicationInfoInterface.SideStage, toStage: ApplicationInfoInterface.MainStage },
524 ];
588 ];
525 }
589 }
526
590
527 function test_applicationSavesLastStage(data) {
591 function test_applicationSavesLastStage(data) {
528− WindowStateStorage.saveStage(webbrowserCheckBox.appId, data.stage);
592+ WindowStateStorage.saveStage(webbrowserCheckBox.appId, data.fromStage);
529 stageSaver.clear();
593 stageSaver.clear();
530
594
531 var stagesPriv = findInvisibleChild(tabletStage, "stagesPriv");
595 var stagesPriv = findInvisibleChild(tabletStage, "stagesPriv");
@@ -538,11 +602,15 @@ Rectangle {
538 webbrowserCheckBox.checked = true;
602 webbrowserCheckBox.checked = true;
539 waitUntilAppSurfaceShowsUp(webbrowserSurfaceId);
603 waitUntilAppSurfaceShowsUp(webbrowserSurfaceId);
540
604
541− webbrowserCheckBox.checked = false;
605+ if (data.toStage === ApplicationInfoInterface.SideStage) {
606+ dragToSideStage(webbrowserSurfaceId);
607+ } else {
608+ dragToMainStage(webbrowserSurfaceId);
609+ }
542
610
543 tryCompare(stageSaver, "count", 1);
611 tryCompare(stageSaver, "count", 1);
544 compare(stageSaver.signalArguments[0][0], "webbrowser-app")
612 compare(stageSaver.signalArguments[0][0], "webbrowser-app")
545− compare(stageSaver.signalArguments[0][1], data.stage)
613+ compare(stageSaver.signalArguments[0][1], data.toStage)
546 }
614 }
547
615
548 function test_loadSideStageByDraggingFromMainStage() {
616 function test_loadSideStageByDraggingFromMainStage() {
@@ -555,21 +623,10 @@ Rectangle {
555 verify(appDelegate);
623 verify(appDelegate);
556 compare(appDelegate.stage, ApplicationInfoInterface.MainStage);
624 compare(appDelegate.stage, ApplicationInfoInterface.MainStage);
557
625
558− var pos = tabletStage.width - sideStage.width - (tabletStage.width - sideStage.width) / 2;
626+ dragToSideStage(webbrowserSurfaceId);
559− var end_pos = tabletStage.width - sideStage.width / 2;
560−
561− multiTouchDragUntil([0,1,2],
562− tabletStage,
563− pos,
564− tabletStage.height / 2,
565− units.gu(3),
566− 0,
567− function() {
568− pos += units.gu(3);
569− return sideStage.shown && !sideStage.showAnimation.running &&
570− pos >= end_pos;
571− });
572
627
628+ var spreadView = findChild(tabletStageLoader, "spreadView")
629+ tryCompare(spreadView, "surfaceDragging", false);
573 tryCompare(appDelegate, "stage", ApplicationInfoInterface.SideStage);
630 tryCompare(appDelegate, "stage", ApplicationInfoInterface.SideStage);
574 }
631 }
575
632
@@ -584,20 +641,10 @@ Rectangle {
584 verify(appDelegate);
641 verify(appDelegate);
585 compare(appDelegate.stage, ApplicationInfoInterface.SideStage);
642 compare(appDelegate.stage, ApplicationInfoInterface.SideStage);
586
643
587− var pos = tabletStage.width - sideStage.width / 2;
644+ dragToMainStage(webbrowserSurfaceId);
588− var end_pos = tabletStage.width - sideStage.width - (tabletStage.width - sideStage.width) / 2;
589−
590− multiTouchDragUntil([0,1,2],
591− tabletStage,
592− pos,
593− tabletStage.height / 2,
594− -units.gu(3),
595− 0,
596− function() {
597− pos -= units.gu(3);
598− return pos <= end_pos;
599− });
600
645
646+ var spreadView = findChild(tabletStageLoader, "spreadView")
647+ tryCompare(spreadView, "surfaceDragging", false);
601 tryCompare(appDelegate, "stage", ApplicationInfoInterface.MainStage);
648 tryCompare(appDelegate, "stage", ApplicationInfoInterface.MainStage);
602 }
649 }
603
650
@@ -737,6 +784,22 @@ Rectangle {
737
784
738 tryCompare(appDelegate.surface, "activeFocus", true);
785 tryCompare(appDelegate.surface, "activeFocus", true);
739
786
787+ dragToSideStage(webbrowserSurfaceId);
788+
789+ var spreadView = findChild(tabletStageLoader, "spreadView")
790+ tryCompare(spreadView, "surfaceDragging", false);
791+ tryCompare(appDelegate.surface, "activeFocus", true);
792+ }
793+
794+ function test_dashDoesNotDragToSidestage() {
795+ sideStage.showNow();
796+ compare(topSurfaceList.applicationAt(0).appId, "unity8-dash");
797+ var dashSurfaceId = topSurfaceList.idAt(0);
798+
799+ var appDelegate = findChild(tabletStage, "spreadDelegate_" + dashSurfaceId);
800+ verify(appDelegate);
801+ compare(appDelegate.stage, ApplicationInfoInterface.MainStage);
802+
740 var pos = tabletStage.width - sideStage.width - (tabletStage.width - sideStage.width) / 2;
803 var pos = tabletStage.width - sideStage.width - (tabletStage.width - sideStage.width) / 2;
741 var end_pos = tabletStage.width - sideStage.width / 2;
804 var end_pos = tabletStage.width - sideStage.width / 2;
742
805
@@ -752,7 +815,71 @@ Rectangle {
752 pos >= end_pos;
815 pos >= end_pos;
753 });
816 });
754
817
818+ var spreadView = findChild(tabletStageLoader, "spreadView")
819+ tryCompare(spreadView, "surfaceDragging", false);
755− tryCompare(appDelegate.surface, "activeFocus", true);
820+ tryCompare(appDelegate, "stage", ApplicationInfoInterface.MainStage);
756 }
821 }
822+
823+ function test_switchStageOnRotation() {
824+ WindowStateStorage.saveStage(webbrowserCheckBox.appId, ApplicationInfoInterface.SideStage)
825+ var webbrowserSurfaceId = topSurfaceList.nextId;
826+ webbrowserCheckBox.checked = true;
827+ waitUntilAppSurfaceShowsUp(webbrowserSurfaceId);
828+
829+ var appDelegate = findChild(tabletStage, "spreadDelegate_" + webbrowserSurfaceId);
830+ verify(appDelegate);
831+ compare(appDelegate.stage, ApplicationInfoInterface.SideStage);
832+
833+ tabletStage.shellOrientation = Qt.PortraitOrientation;
834+ tryCompare(appDelegate, "stage", ApplicationInfoInterface.MainStage);
835+ }
836+
837+ function test_restoreOriginalStageOnRotation() {
838+ var webbrowserSurfaceId = topSurfaceList.nextId;
839+ webbrowserCheckBox.checked = true;
840+ waitUntilAppSurfaceShowsUp(webbrowserSurfaceId);
841+
842+ var appDelegate = findChild(tabletStage, "spreadDelegate_" + webbrowserSurfaceId);
843+ verify(appDelegate);
844+
845+ dragToSideStage(webbrowserSurfaceId);
846+
847+ // will be in sidestage now
848+ tabletStage.shellOrientation = Qt.PortraitOrientation;
849+ tryCompare(appDelegate, "stage", ApplicationInfoInterface.MainStage);
850+
851+ tabletStage.shellOrientation = Qt.LandscapeOrientation;
852+ tryCompare(appDelegate, "stage", ApplicationInfoInterface.SideStage);
853+ }
854+
855+ function test_restoreSavedStageOnCloseReopen() {
856+ var webbrowserSurfaceId = topSurfaceList.nextId;
857+ webbrowserCheckBox.checked = true;
858+ waitUntilAppSurfaceShowsUp(webbrowserSurfaceId);
859+
860+ var appDelegate = findChild(tabletStage, "spreadDelegate_" + webbrowserSurfaceId);
861+ verify(appDelegate);
862+
863+ dragToSideStage(webbrowserSurfaceId);
864+ // will be in sidestage now
865+ tabletStage.shellOrientation = Qt.PortraitOrientation;
866+ tryCompare(appDelegate, "stage", ApplicationInfoInterface.MainStage);
867+
868+ webbrowserCheckBox.checked = false;
869+ tryCompare(ApplicationManager, "count", 1);
870+
871+ // back to landscape
872+ tabletStage.shellOrientation = Qt.LandscapeOrientation;
873+
874+ webbrowserSurfaceId = topSurfaceList.nextId;
875+ webbrowserCheckBox.checked = true;
876+ waitUntilAppSurfaceShowsUp(webbrowserSurfaceId);
877+
878+ appDelegate = findChild(tabletStage, "spreadDelegate_" + webbrowserSurfaceId);
879+ verify(appDelegate);
880+ tryCompare(appDelegate, "stage", ApplicationInfoInterface.SideStage);
881+ }
882+
883+
757 }
884 }
758}
885}
tests/qmltests/Stages/tst_WindowResizeArea.qml+36−34
@@ -44,8 +44,10 @@ Item {
44 id: fakeWindow
44 id: fakeWindow
45 property alias resizeAreaMinWidth: windowResizeArea.minWidth
45 property alias resizeAreaMinWidth: windowResizeArea.minWidth
46 property alias resizeAreaMinHeight: windowResizeArea.minHeight
46 property alias resizeAreaMinHeight: windowResizeArea.minHeight
47− x: units.gu(20)
47+ x: requestedX
48+ y: requestedY
49+ property real requestedX: units.gu(20)
48− y: units.gu(20)
50+ property real requestedY: units.gu(20)
49 width: requestedWidth
51 width: requestedWidth
50 height: requestedHeight
52 height: requestedHeight
51 property real requestedWidth
53 property real requestedWidth
@@ -226,8 +228,8 @@ Item {
226 property var fakeWindow: windowLoader.item
228 property var fakeWindow: windowLoader.item
227
229
228 function init() {
230 function init() {
229− fakeWindow.x = units.gu(20)
231+ fakeWindow.requestedX = units.gu(20)
230− fakeWindow.y = units.gu(20)
232+ fakeWindow.requestedY = units.gu(20)
231 fakeWindow.requestedWidth = units.gu(20)
233 fakeWindow.requestedWidth = units.gu(20)
232 fakeWindow.requestedHeight = units.gu(20)
234 fakeWindow.requestedHeight = units.gu(20)
233 fakeWindow.resizeAreaMinWidth = units.gu(15);
235 fakeWindow.resizeAreaMinWidth = units.gu(15);
@@ -254,8 +256,8 @@ Item {
254 }
256 }
255
257
256 function test_resizeWindowRightBottom(data) {
258 function test_resizeWindowRightBottom(data) {
257− var initialWindowX = fakeWindow.x;
259+ var initialWindowX = fakeWindow.requestedX;
258− var initialWindowY = fakeWindow.y;
260+ var initialWindowY = fakeWindow.requestedY;
259 var initialWindowWidth = fakeWindow.width
261 var initialWindowWidth = fakeWindow.width
260 var initialWindowHeight = fakeWindow.height
262 var initialWindowHeight = fakeWindow.height
261
263
@@ -266,8 +268,8 @@ Item {
266 tryCompare(fakeWindow, "width", Math.max(initialWindowWidth + data.dx, fakeWindow.resizeAreaMinWidth));
268 tryCompare(fakeWindow, "width", Math.max(initialWindowWidth + data.dx, fakeWindow.resizeAreaMinWidth));
267 tryCompare(fakeWindow, "height", Math.max(initialWindowHeight + data.dy, fakeWindow.resizeAreaMinHeight));
269 tryCompare(fakeWindow, "height", Math.max(initialWindowHeight + data.dy, fakeWindow.resizeAreaMinHeight));
268
270
269− compare(fakeWindow.x, initialWindowX);
271+ compare(fakeWindow.requestedX, initialWindowX);
270− compare(fakeWindow.y, initialWindowY);
272+ compare(fakeWindow.requestedY, initialWindowY);
271 }
273 }
272
274
273 function test_resizeWindowLeftTop_data() {
275 function test_resizeWindowLeftTop_data() {
@@ -280,8 +282,8 @@ Item {
280 }
282 }
281
283
282 function test_resizeWindowLeftTop(data) {
284 function test_resizeWindowLeftTop(data) {
283− var initialWindowX = fakeWindow.x;
285+ var initialWindowX = fakeWindow.requestedX;
284− var initialWindowY = fakeWindow.y;
286+ var initialWindowY = fakeWindow.requestedY;
285 var initialWindowWidth = fakeWindow.width
287 var initialWindowWidth = fakeWindow.width
286 var initialWindowHeight = fakeWindow.height
288 var initialWindowHeight = fakeWindow.height
287
289
@@ -294,13 +296,13 @@ Item {
294
296
295 var maxMoveX = initialWindowWidth - fakeWindow.resizeAreaMinWidth;
297 var maxMoveX = initialWindowWidth - fakeWindow.resizeAreaMinWidth;
296 var maxMoveY = initialWindowHeight - fakeWindow.resizeAreaMinHeight;
298 var maxMoveY = initialWindowHeight - fakeWindow.resizeAreaMinHeight;
297− compare(fakeWindow.x, Math.min(initialWindowX + data.dx, initialWindowX + maxMoveX));
299+ compare(fakeWindow.requestedX, Math.min(initialWindowX + data.dx, initialWindowX + maxMoveX));
298− compare(fakeWindow.y, Math.min(initialWindowY + data.dy, initialWindowY + maxMoveY));
300+ compare(fakeWindow.requestedY, Math.min(initialWindowY + data.dy, initialWindowY + maxMoveY));
299 }
301 }
300
302
301 function test_saveRestoreSize() {
303 function test_saveRestoreSize() {
302− var initialWindowX = fakeWindow.x;
304+ var initialWindowX = fakeWindow.requestedX;
303− var initialWindowY = fakeWindow.y;
305+ var initialWindowY = fakeWindow.requestedY;
304 var initialWindowWidth = fakeWindow.width
306 var initialWindowWidth = fakeWindow.width
305 var initialWindowHeight = fakeWindow.height
307 var initialWindowHeight = fakeWindow.height
306
308
@@ -332,8 +334,8 @@ Item {
332 }
334 }
333
335
334 function test_resizeSmallerAndLarger(data) {
336 function test_resizeSmallerAndLarger(data) {
335− var initialWindowX = fakeWindow.x;
337+ var initialWindowX = fakeWindow.requestedX;
336− var initialWindowY = fakeWindow.y;
338+ var initialWindowY = fakeWindow.requestedY;
337 var initialWindowWidth = fakeWindow.width
339 var initialWindowWidth = fakeWindow.width
338 var initialWindowHeight = fakeWindow.height
340 var initialWindowHeight = fakeWindow.height
339
341
@@ -348,18 +350,18 @@ Item {
348 }
350 }
349
351
350 function test_saveRestoreMaximized() {
352 function test_saveRestoreMaximized() {
351− var initialWindowX = fakeWindow.x;
353+ var initialWindowX = fakeWindow.requestedX;
352− var initialWindowY = fakeWindow.y;
354+ var initialWindowY = fakeWindow.requestedY;
353
355
354 var moveDelta = units.gu(5);
356 var moveDelta = units.gu(5);
355
357
356− fakeWindow.x = initialWindowX + moveDelta
358+ fakeWindow.requestedX = initialWindowX + moveDelta
357− fakeWindow.y = initialWindowY + moveDelta
359+ fakeWindow.requestedY = initialWindowY + moveDelta
358
360
359 // Now change the state to maximized. The window should not keep updating the stored values
361 // Now change the state to maximized. The window should not keep updating the stored values
360 fakeWindow.maximize()
362 fakeWindow.maximize()
361− fakeWindow.x = 31415 // 0 is too risky to pass the test even when broken
363+ fakeWindow.requestedX = 31415 // 0 is too risky to pass the test even when broken
362− fakeWindow.y = 31415
364+ fakeWindow.requestedY = 31415
363
365
364 // This will destroy the window and recreate it
366 // This will destroy the window and recreate it
365 windowLoader.active = false;
367 windowLoader.active = false;
@@ -389,8 +391,8 @@ Item {
389 }
391 }
390
392
391 function test_restoreMovesIntoBounds(data) {
393 function test_restoreMovesIntoBounds(data) {
392− fakeWindow.x = data.x;
394+ fakeWindow.requestedX = data.x;
393− fakeWindow.y = data.y;
395+ fakeWindow.requestedY = data.y;
394 fakeWindow.width = data.w;
396 fakeWindow.width = data.w;
395 fakeWindow.height = data.h;
397 fakeWindow.height = data.h;
396 waitForRendering(root);
398 waitForRendering(root);
@@ -402,10 +404,10 @@ Item {
402 waitForRendering(root)
404 waitForRendering(root)
403
405
404 // Make sure it's again where we left it in normal state before destroying
406 // Make sure it's again where we left it in normal state before destroying
405− compare(fakeWindow.x >= 0, true)
407+ compare(fakeWindow.requestedX >= 0, true)
406− compare(fakeWindow.y >= PanelState.panelHeight, true)
408+ compare(fakeWindow.requestedY >= PanelState.panelHeight, true)
407− compare(fakeWindow.x + fakeWindow.width <= root.width, true)
409+ compare(fakeWindow.requestedX + fakeWindow.width <= root.width, true)
408− compare(fakeWindow.y + fakeWindow.height <= root.height, true)
410+ compare(fakeWindow.requestedY + fakeWindow.height <= root.height, true)
409
411
410 waitForRendering(root)
412 waitForRendering(root)
411 }
413 }
@@ -417,8 +419,8 @@ Item {
417 and height) increment value.
419 and height) increment value.
418 */
420 */
419 function test_sizeIncrement() {
421 function test_sizeIncrement() {
420− var initialWindowX = fakeWindow.x;
422+ var initialWindowX = fakeWindow.requestedX;
421− var initialWindowY = fakeWindow.y;
423+ var initialWindowY = fakeWindow.requestedY;
422 var initialWindowWidth = fakeWindow.width
424 var initialWindowWidth = fakeWindow.width
423 var initialWindowHeight = fakeWindow.height
425 var initialWindowHeight = fakeWindow.height
424
426
@@ -448,8 +450,8 @@ Item {
448 Tests that when dragging a window border you cannot make it bigger than its maximum size
450 Tests that when dragging a window border you cannot make it bigger than its maximum size
449 */
451 */
450 function test_maximumSize() {
452 function test_maximumSize() {
451− fakeWindow.x = units.gu(1);
453+ fakeWindow.requestedX = units.gu(1);
452− fakeWindow.y = units.gu(1);
454+ fakeWindow.requestedY = units.gu(1);
453 fakeWindow.resizeAreaMinWidth = 1; // so it does not interfere with anything
455 fakeWindow.resizeAreaMinWidth = 1; // so it does not interfere with anything
454 fakeWindow.resizeAreaMinHeight = 1; // so it does not interfere with anything
456 fakeWindow.resizeAreaMinHeight = 1; // so it does not interfere with anything
455 fakeWindow.requestedWidth = units.gu(10);
457 fakeWindow.requestedWidth = units.gu(10);
@@ -478,8 +480,8 @@ Item {
478 Tests that when dragging a window border you cannot make it smaller than its minimum size
480 Tests that when dragging a window border you cannot make it smaller than its minimum size
479 */
481 */
480 function test_minimumSize() {
482 function test_minimumSize() {
481− fakeWindow.x = units.gu(1);
483+ fakeWindow.requestedX = units.gu(1);
482− fakeWindow.y = units.gu(1);
484+ fakeWindow.requestedY = units.gu(1);
483 fakeWindow.resizeAreaMinWidth = 1; // so it does not interfere with anything
485 fakeWindow.resizeAreaMinWidth = 1; // so it does not interfere with anything
484 fakeWindow.resizeAreaMinHeight = 1; // so it does not interfere with anything
486 fakeWindow.resizeAreaMinHeight = 1; // so it does not interfere with anything
485 fakeWindow.requestedWidth = units.gu(20);
487 fakeWindow.requestedWidth = units.gu(20);
tests/qmltests/UnityLogo.png

Binary file not shown.

tests/qmltests/tst_OrientedShell.qml+53−44
@@ -503,39 +503,37 @@ Rectangle {
503 function test_appSupportingOnlyPrimaryOrientationMakesPhoneShellStayPut() {
503 function test_appSupportingOnlyPrimaryOrientationMakesPhoneShellStayPut() {
504 loadShell("mako");
504 loadShell("mako");
505
505
506− var spreadRepeater = findChild(shell, "spreadRepeater");
506+ var primarySurfaceId = topLevelSurfaceList.nextId;
507− verify(spreadRepeater);
507+ var primaryApp = ApplicationManager.startApplication("primary-oriented-app");
508−
508+ verify(primaryApp);
509− // unity8-dash supports only primary orientation and should be already running
509+ waitUntilAppWindowIsFullyLoaded(primarySurfaceId);
510− compare(spreadRepeater.count, 1);
510+
511− var dashDelegate = spreadRepeater.itemAt(0);
511+ var primaryAppWindow = findAppWindowForSurfaceId(primarySurfaceId);
512− var dashApp = dashDelegate.application;
512+ verify(primaryAppWindow)
513− verify(dashApp);
513+ var primaryDelegate = findChild(shell, "spreadDelegate_" + primarySurfaceId);
514− compare(dashApp.appId, "unity8-dash");
514+
515− var dashAppWindow = findChild(dashDelegate, "appWindow");
516− verify(dashAppWindow);
517− compare(dashDelegate.focus, true);
515+ compare(primaryDelegate.focus, true);
518− compare(dashApp.rotatesWindowContents, false);
516+ compare(primaryApp.rotatesWindowContents, false);
519− compare(dashApp.supportedOrientations, Qt.PrimaryOrientation);
517+ compare(primaryApp.supportedOrientations, Qt.PrimaryOrientation);
520− compare(dashApp.stage, ApplicationInfoInterface.MainStage);
518+ compare(primaryApp.stage, ApplicationInfoInterface.MainStage);
521−
519+
522− tryCompareFunction(function(){return dashDelegate.surface != null;}, true);
520+ tryCompareFunction(function(){return primaryDelegate.surface != null;}, true);
523− verify(checkAppSurfaceOrientation(dashAppWindow, dashApp, root.primaryOrientationAngle));
521+ verify(checkAppSurfaceOrientation(primaryAppWindow, primaryApp, root.primaryOrientationAngle));
524
522
525 compare(shell.transformRotationAngle, root.primaryOrientationAngle);
523 compare(shell.transformRotationAngle, root.primaryOrientationAngle);
526 rotateTo(90);
524 rotateTo(90);
527
525
528− verify(checkAppSurfaceOrientation(dashAppWindow, dashApp, root.primaryOrientationAngle));
526+ verify(checkAppSurfaceOrientation(primaryAppWindow, primaryApp, root.primaryOrientationAngle));
529 compare(shell.transformRotationAngle, root.primaryOrientationAngle);
527 compare(shell.transformRotationAngle, root.primaryOrientationAngle);
530
528
531 rotateTo(180);
529 rotateTo(180);
532
530
533− verify(checkAppSurfaceOrientation(dashAppWindow, dashApp, root.primaryOrientationAngle));
531+ verify(checkAppSurfaceOrientation(primaryAppWindow, primaryApp, root.primaryOrientationAngle));
534 compare(shell.transformRotationAngle, root.primaryOrientationAngle);
532 compare(shell.transformRotationAngle, root.primaryOrientationAngle);
535
533
536 rotateTo(270);
534 rotateTo(270);
537
535
538− verify(checkAppSurfaceOrientation(dashAppWindow, dashApp, root.primaryOrientationAngle));
536+ verify(checkAppSurfaceOrientation(primaryAppWindow, primaryApp, root.primaryOrientationAngle));
539 compare(shell.transformRotationAngle, root.primaryOrientationAngle);
537 compare(shell.transformRotationAngle, root.primaryOrientationAngle);
540 }
538 }
541
539
@@ -548,37 +546,39 @@ Rectangle {
548 function test_appSupportingOnlyPrimaryOrientationWillOnlyRotateInLandscape(data) {
546 function test_appSupportingOnlyPrimaryOrientationWillOnlyRotateInLandscape(data) {
549 loadShell(data.deviceName);
547 loadShell(data.deviceName);
550
548
551− compare(topLevelSurfaceList.applicationAt(0).appId, "unity8-dash");
549+ var primarySurfaceId = topLevelSurfaceList.nextId;
552− var dashSurfaceId = topLevelSurfaceList.idAt(0);
550+ var primaryApp = ApplicationManager.startApplication("primary-oriented-app");
553− var dashAppWindow = findAppWindowForSurfaceId(dashSurfaceId);
551+ verify(primaryApp);
552+ waitUntilAppWindowIsFullyLoaded(primarySurfaceId);
554
553
555− // unity8-dash supports only primary orientation and should be already running
554+ var primaryAppWindow = findAppWindowForSurfaceId(primarySurfaceId);
555+ verify(primaryAppWindow)
556
556
557− compare(ApplicationManager.focusedApplicationId, "unity8-dash");
557+ // primary-oriented-app supports only primary orientation
558− var dashApp = dashAppWindow.application;
559− verify(dashApp);
560− compare(dashApp.rotatesWindowContents, false);
561− compare(dashApp.supportedOrientations, Qt.PrimaryOrientation);
562− compare(dashApp.stage, ApplicationInfoInterface.MainStage);
563
558
564− tryCompareFunction(function(){return dashApp.surfaceList.count > 0;}, true);
559+ compare(ApplicationManager.focusedApplicationId, "primary-oriented-app");
560+ compare(primaryApp.rotatesWindowContents, false);
561+ compare(primaryApp.supportedOrientations, Qt.PrimaryOrientation);
562+ compare(primaryApp.stage, ApplicationInfoInterface.MainStage);
565
563
564+ tryCompareFunction(function(){return primaryApp.surfaceList.count > 0;}, true);
565+
566− tryCompareFunction(function(){return checkAppSurfaceOrientation(dashAppWindow, dashApp, root.primaryOrientationAngle)}, true);
566+ tryCompareFunction(function(){return checkAppSurfaceOrientation(primaryAppWindow, primaryApp, root.primaryOrientationAngle)}, true);
567 compare(shell.transformRotationAngle, root.primaryOrientationAngle);
567 compare(shell.transformRotationAngle, root.primaryOrientationAngle);
568
568
569 rotateTo(90);
569 rotateTo(90);
570
570
571− tryCompareFunction(function(){return checkAppSurfaceOrientation(dashAppWindow, dashApp, root.primaryOrientationAngle)}, true);
571+ tryCompareFunction(function(){return checkAppSurfaceOrientation(primaryAppWindow, primaryApp, root.primaryOrientationAngle)}, true);
572 compare(shell.transformRotationAngle, root.primaryOrientationAngle);
572 compare(shell.transformRotationAngle, root.primaryOrientationAngle);
573
573
574 rotateTo(180);
574 rotateTo(180);
575
575
576− tryCompareFunction(function(){return checkAppSurfaceOrientation(dashAppWindow, dashApp, root.primaryOrientationAngle + 180)}, true);
576+ tryCompareFunction(function(){return checkAppSurfaceOrientation(primaryAppWindow, primaryApp, root.primaryOrientationAngle + 180)}, true);
577 compare(shell.transformRotationAngle, root.primaryOrientationAngle + 180);
577 compare(shell.transformRotationAngle, root.primaryOrientationAngle + 180);
578
578
579 rotateTo(270);
579 rotateTo(270);
580
580
581− tryCompareFunction(function(){return checkAppSurfaceOrientation(dashAppWindow, dashApp, root.primaryOrientationAngle + 180)}, true);
581+ tryCompareFunction(function(){return checkAppSurfaceOrientation(primaryAppWindow, primaryApp, root.primaryOrientationAngle + 180)}, true);
582 compare(shell.transformRotationAngle, root.primaryOrientationAngle + 180);
582 compare(shell.transformRotationAngle, root.primaryOrientationAngle + 180);
583 }
583 }
584
584
@@ -816,12 +816,13 @@ Rectangle {
816 }
816 }
817
817
818 /*
818 /*
819+ - launch an app that only supports the primary orientation
819 - launch an app that supports all orientations, such as twitter-webapp
820 - launch an app that supports all orientations, such as twitter-webapp
820 - wait a bit until that app is considered to have finished initializing and is thus
821 - wait a bit until that app is considered to have finished initializing and is thus
821 ready to get resized/rotated
822 ready to get resized/rotated
822− - switch back to dash
823+ - switch back to previous app (only supporting primary
823 - rotate device to 90 degrees
824 - rotate device to 90 degrees
824− - Physical orientation is 90 but Shell orientation is kept at 0 because unity8-dash
825+ - Physical orientation is 90 but Shell orientation is kept at 0 because the app
825 doesn't support such orientation
826 doesn't support such orientation
826 - do a long right-edge drag to show the apps spread
827 - do a long right-edge drag to show the apps spread
827 - tap on twitter-webapp
828 - tap on twitter-webapp
@@ -838,6 +839,13 @@ Rectangle {
838 */
839 */
839 function test_greeterStaysAwayAfterRotation() {
840 function test_greeterStaysAwayAfterRotation() {
840 loadShell("mako");
841 loadShell("mako");
842+
843+ // Load an app which only supports primary
844+ var primarySurfaceId = topLevelSurfaceList.nextId;
845+ var primaryApp = ApplicationManager.startApplication("primary-oriented-app");
846+ verify(primaryApp);
847+ waitUntilAppWindowIsFullyLoaded(primarySurfaceId);
848+
841 var twitterSurfaceId = topLevelSurfaceList.nextId;
849 var twitterSurfaceId = topLevelSurfaceList.nextId;
842 var twitterApp = ApplicationManager.startApplication("twitter-webapp");
850 var twitterApp = ApplicationManager.startApplication("twitter-webapp");
843 verify(twitterApp);
851 verify(twitterApp);
@@ -850,12 +858,11 @@ Rectangle {
850 waitUntilAppWindowIsFullyLoaded(twitterSurfaceId);
858 waitUntilAppWindowIsFullyLoaded(twitterSurfaceId);
851 waitUntilAppWindowCanRotate(twitterSurfaceId);
859 waitUntilAppWindowCanRotate(twitterSurfaceId);
852
860
853− // go back to unity8-dash
861+ // go back to primary-oriented-app
854 performEdgeSwipeToSwitchToPreviousApp();
862 performEdgeSwipeToSwitchToPreviousApp();
855
863
856 rotateTo(90);
864 rotateTo(90);
857 wait(1); // spin the event loop to let all bindings do their thing
865 wait(1); // spin the event loop to let all bindings do their thing
858− // should not rotat as unity8-dash doesn't support it
859 tryCompare(shell, "transformRotationAngle", 0);
866 tryCompare(shell, "transformRotationAngle", 0);
860
867
861 performEdgeSwipeToShowAppSpread();
868 performEdgeSwipeToShowAppSpread();
@@ -1257,7 +1264,7 @@ Rectangle {
1257 Regression test for https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1476757
1264 Regression test for https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1476757
1258
1265
1259 Steps:
1266 Steps:
1260− 1- have a portrait-only app in foreground (eg unity8-dash)
1267+ 1- have a portrait-only app in foreground (eg primary-oriented-app)
1261 2- launch or switch to some other application
1268 2- launch or switch to some other application
1262 3- right-edge swipe to show the apps spread
1269 3- right-edge swipe to show the apps spread
1263 4- swipe up to close the current app (the one from step 2)
1270 4- swipe up to close the current app (the one from step 2)
@@ -1275,8 +1282,10 @@ Rectangle {
1275 function test_lockPhoneAfterClosingAppInSpreadThenUnlockAndRotate() {
1282 function test_lockPhoneAfterClosingAppInSpreadThenUnlockAndRotate() {
1276 loadShell("mako");
1283 loadShell("mako");
1277
1284
1278− compare(topLevelSurfaceList.applicationAt(0).appId, "unity8-dash");
1285+ var primarySurfaceId = topLevelSurfaceList.nextId;
1279− var dashSurfaceId = topLevelSurfaceList.idAt(0);
1286+ var primaryApp = ApplicationManager.startApplication("primary-oriented-app");
1287+ verify(primaryApp);
1288+ waitUntilAppWindowIsFullyLoaded(primarySurfaceId);
1280
1289
1281 var gmailSurfaceId = topLevelSurfaceList.nextId;
1290 var gmailSurfaceId = topLevelSurfaceList.nextId;
1282 var gmailApp = ApplicationManager.startApplication("gmail-webapp");
1291 var gmailApp = ApplicationManager.startApplication("gmail-webapp");
@@ -1298,7 +1307,7 @@ Rectangle {
1298
1307
1299 swipeAwayGreeter();
1308 swipeAwayGreeter();
1300
1309
1301− verify(isAppSurfaceFocused(dashSurfaceId))
1310+ verify(isAppSurfaceFocused(primarySurfaceId))
1302
1311
1303 signalSpy.clear();
1312 signalSpy.clear();
1304 signalSpy.target = shell;
1313 signalSpy.target = shell;
tests/utils/modules/Unity/Test/CMakeLists.txt+5−1
@@ -5,10 +5,14 @@ add_definitions(-DQT_NO_KEYWORDS)
5pkg_check_modules(UBUNTUGESTURES REQUIRED UbuntuGestures)
5pkg_check_modules(UBUNTUGESTURES REQUIRED UbuntuGestures)
6
6
7include_directories(
7include_directories(
8+ ${CMAKE_SOURCE_DIR}/src
9+)
10+
11+include_directories(
12+ SYSTEM
8 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
13 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
9 ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
14 ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
10 ${UBUNTUGESTURES_INCLUDE_DIRS}
15 ${UBUNTUGESTURES_INCLUDE_DIRS}
11− ${CMAKE_SOURCE_DIR}/src
12)
16)
13
17
14set(UnityTestQML_SOURCES
18set(UnityTestQML_SOURCES