on xenial help sign in
Merge
commit0cded640d9aba0328c4a95cd41ce07c167145664 authorAlbert Astals Cid <albert.astals@canonical.com> dateWed 29 Jun 2016, 14:22 (10 years ago) parentdbee994a189f76
262 files changed+3628−740
build.sh+2−2 data/unity8-dash.png data/unity8.conf+13 debian/changelog+63 debian/control+7−6 plugins/Cursor/CMakeLists.txt+4 plugins/Cursor/Cursor.qml+26−11 plugins/Cursor/MousePointer.cpp+5−5 plugins/Dash/CMakeLists.txt+4 plugins/Dash/abstractdashview.h+1−3 plugins/Dash/graphics/music_progress_bg.png plugins/Dash/horizontaljournal.cpp+1−4 plugins/Dash/listviewwithpageheader.cpp+8−4 plugins/Dash/organicgrid.cpp−3 plugins/Dash/verticaljournal.cpp−3 plugins/Lights/CMakeLists.txt+1 plugins/Lights/android-hardware.h−4 plugins/Ubuntu/DownloadDaemonListener/CMakeLists.txt−1 plugins/Ubuntu/Gestures/CMakeLists.txt+5−1 plugins/Ubuntu/Gestures/TouchDispatcher.cpp−4 plugins/Unity/Indicators/CMakeLists.txt+4 plugins/Unity/Session/CMakeLists.txt+2 plugins/Unity/Session/dbusunitysessionservice.cpp+50−18 plugins/Unity/Session/dbusunitysessionservice.h+7 plugins/Utils/CMakeLists.txt+5 plugins/Utils/globalfunctions.cpp+1−4 plugins/Utils/windowstatestorage.cpp+2−2 plugins/Utils/windowstatestorage.h+1−1 plugins/WindowManager/CMakeLists.txt+1 po/sk.po+31−31 po/unity8.pot+34−20 qml/Components/LazyImage.qml+3−1 qml/Components/ListItems/graphics/ListItemDividerHorizontal@18.png qml/Components/MediaServices/MediaServices.qml+386 qml/Components/MediaServices/MediaServicesControls.qml+192 qml/Components/MediaServices/MediaServicesHeader.qml+71 qml/Components/MediaServices/VideoPlayer.qml+155 qml/Components/MediaServices/VideoPlayerControls.qml+158 qml/Dash/Previews/PreviewSharing.qml → qml/Components/SharingPicker.qml+2−14 qml/Components/WindowControlButtons.qml+2 qml/Components/graphics/close@20.png qml/Components/graphics/icon_star_empty@20.png qml/Components/graphics/icon_star_full@20.png qml/Components/graphics/icon_star_half@20.png qml/Components/graphics/icon_star_off@20.png qml/Components/graphics/icon_star_on@20.png qml/Components/graphics/non-selected@18.png qml/Dash/CardTool.qml+2−1 qml/Dash/DashApplication.qml+16−5 qml/Dash/Previews/Preview.qml+16 qml/Dash/Previews/PreviewImageGallery.qml+2 qml/Dash/Previews/PreviewInlineVideo.qml+65 qml/Dash/Previews/PreviewMediaToolbar.qml+25−5 qml/Dash/Previews/PreviewRatingEdit.qml+4−1 qml/Dash/Previews/PreviewWidget.qml+3 qml/Dash/Previews/PreviewWidgetFactory.qml+11−1 qml/Dash/Previews/PreviewZoomableImage.qml+1 qml/Dash/ScopesList.qml+1 qml/Dash/graphics/dash_divider_top_darkgrad@27.png qml/Dash/graphics/dash_divider_top_lightgrad@27.png qml/Dash/graphics/header_handlearrow2@27.png qml/Dash/graphics/header_handlearrow@27.png qml/Dash/graphics/icon_clear@20.png qml/Dash/graphics/icon_search_active@20.png qml/Dash/graphics/icon_search_inactive@20.png qml/Dash/graphics/navigation_shadow@18.png qml/Dash/graphics/navigation_shadow_light@18.png qml/Dash/graphics/overview_hint@27.png qml/Dash/graphics/pagination_dot_off@30.png qml/Dash/graphics/pagination_dot_on@30.png qml/Dash/graphics/paper.png qml/Dash/graphics/pixel.png qml/Dash/graphics/play_button@18.png qml/Dash/graphics/play_button_active@18.png qml/Dash/graphics/play_button_active_big@20.png qml/Dash/graphics/play_button_big@20.png qml/Dash/graphics/tabbarchevron@27.png qml/Dash/graphics/tablet/icon_close_preview@20.png qml/Dash/graphics/tooltip_arrow@27.png qml/Greeter/Greeter.qml+4−1 qml/Greeter/graphics/dot_empty.png qml/Greeter/graphics/dot_filled.png qml/Greeter/graphics/dot_pointer.png qml/Greeter/graphics/icon_arrow.png qml/Greeter/graphics/infographic_circle_back.png qml/Launcher/LauncherPanel.qml+3−1 qml/Launcher/graphics/divider-line.png qml/Launcher/graphics/focused_app_arrow@30.png qml/Launcher/graphics/home@18.png qml/Launcher/graphics/quicklist_tooltip@30.png qml/Panel/Indicators/client/IndicatorsClient.qml+7−1 qml/Panel/Indicators/client/IndicatorsList.qml+7 qml/Panel/IndicatorsMenu.qml+3−1 qml/Panel/graphics/rectangular_dropshadow@30.png qml/Rotation/HalfLoopRotationAnimation.qml+1−1 qml/Rotation/ImmediateRotationAction.qml+1−1 qml/Rotation/NinetyRotationAnimation.qml+1−1 qml/Rotation/UpdateShellTransformations.qml+4−4 qml/Stages/DecoratedWindow.qml+5−4 qml/Stages/DesktopSpread.qml+2 qml/Stages/DesktopStage.qml+19−2 qml/Stages/TabletStage.qml+17−9 qml/Stages/WindowControlsOverlay.qml+2−2 qml/Stages/WindowDecoration.qml+13−6 qml/Stages/WindowResizeArea.qml+11−11 qml/Stages/graphics/PageHeaderBaseDividerBottom@18.png qml/Stages/graphics/PageHeaderBaseDividerLight@18.png qml/Stages/graphics/dropshadow2gu@30.png qml/Stages/graphics/sidestage_handle@20.png qml/Tutorial/graphics/background1.png qml/Tutorial/graphics/background2.png qml/Wizard/Pages/data/Desktop_header_bkg.png qml/Wizard/Pages/data/Desktop_splash_screen_bkg.png qml/Wizard/Pages/data/Phone_header_bkg.png qml/Wizard/Pages/data/Phone_splash_screen_bkg.png qml/Wizard/Pages/data/Tick@30.png qml/Wizard/Pages/data/timezonemap/map.png qml/Wizard/Pages/data/timezonemap/pin.png qml/Wizard/Pages/data/timezonemap/timezone_-1.png qml/Wizard/Pages/data/timezonemap/timezone_-10.png qml/Wizard/Pages/data/timezonemap/timezone_-11.png qml/Wizard/Pages/data/timezonemap/timezone_-2.png qml/Wizard/Pages/data/timezonemap/timezone_-3.5.png qml/Wizard/Pages/data/timezonemap/timezone_-3.png qml/Wizard/Pages/data/timezonemap/timezone_-4.5.png qml/Wizard/Pages/data/timezonemap/timezone_-4.png qml/Wizard/Pages/data/timezonemap/timezone_-5.png qml/Wizard/Pages/data/timezonemap/timezone_-6.png qml/Wizard/Pages/data/timezonemap/timezone_-7.png qml/Wizard/Pages/data/timezonemap/timezone_-8.png qml/Wizard/Pages/data/timezonemap/timezone_-9.5.png qml/Wizard/Pages/data/timezonemap/timezone_-9.png qml/Wizard/Pages/data/timezonemap/timezone_0.png qml/Wizard/Pages/data/timezonemap/timezone_1.png qml/Wizard/Pages/data/timezonemap/timezone_10.5.png qml/Wizard/Pages/data/timezonemap/timezone_10.png qml/Wizard/Pages/data/timezonemap/timezone_11.png qml/Wizard/Pages/data/timezonemap/timezone_12.75.png qml/Wizard/Pages/data/timezonemap/timezone_12.png qml/Wizard/Pages/data/timezonemap/timezone_13.png qml/Wizard/Pages/data/timezonemap/timezone_2.png qml/Wizard/Pages/data/timezonemap/timezone_3.5.png qml/Wizard/Pages/data/timezonemap/timezone_3.png qml/Wizard/Pages/data/timezonemap/timezone_4.5.png qml/Wizard/Pages/data/timezonemap/timezone_4.png qml/Wizard/Pages/data/timezonemap/timezone_5.5.png qml/Wizard/Pages/data/timezonemap/timezone_5.75.png qml/Wizard/Pages/data/timezonemap/timezone_5.png qml/Wizard/Pages/data/timezonemap/timezone_6.5.png qml/Wizard/Pages/data/timezonemap/timezone_6.png qml/Wizard/Pages/data/timezonemap/timezone_7.png qml/Wizard/Pages/data/timezonemap/timezone_8.5.png qml/Wizard/Pages/data/timezonemap/timezone_8.png qml/Wizard/Pages/data/timezonemap/timezone_9.5.png qml/Wizard/Pages/data/timezonemap/timezone_9.png qml/Wizard/Pages/passcode-desktop.qml+27−10 qml/Wizard/PasswordMeter.qml+3−1 qml/graphics/dropshadow_left@20.png qml/graphics/dropshadow_right@20.png src/CMakeLists.txt+1 src/Dash/main.cpp+22−21 tests/CMakeLists.txt+6 tests/data/unity/backgrounds/blue.png tests/data/unity/backgrounds/red.png tests/graphics/applicationIcons/browser@18.png tests/graphics/applicationIcons/calendar@18.png tests/graphics/applicationIcons/camera@18.png tests/graphics/applicationIcons/contacts-app@18.png tests/graphics/applicationIcons/dash.png tests/graphics/applicationIcons/dialer-app@18.png tests/graphics/applicationIcons/evernote@18.png tests/graphics/applicationIcons/facebook@18.png tests/graphics/applicationIcons/gallery@18.png tests/graphics/applicationIcons/gmail@18.png tests/graphics/applicationIcons/libreoffice@18.png tests/graphics/applicationIcons/map@18.png tests/graphics/applicationIcons/messages-app@18.png tests/graphics/applicationIcons/notepad@18.png tests/graphics/applicationIcons/pinterest@18.png tests/graphics/applicationIcons/soundcloud@18.png tests/graphics/applicationIcons/system-settings@18.png tests/graphics/applicationIcons/twitter@18.png tests/graphics/applicationIcons/weather@18.png tests/graphics/applicationIcons/wikipedia@18.png tests/graphics/applicationIcons/youtube@18.png tests/graphics/avatars/amanda@12.png tests/graphics/avatars/anna_olsson@12.png tests/graphics/avatars/funky@12.png tests/graphics/clock@18.png tests/mocks/QtMultimedia/CMakeLists.txt+8−2 tests/mocks/QtMultimedia/VideoSurface.qml+111 tests/mocks/QtMultimedia/audio.cpp−132 tests/mocks/QtMultimedia/audio.h−103 tests/mocks/QtMultimedia/mediaplayer.cpp+318 tests/mocks/QtMultimedia/mediaplayer.h+223 tests/mocks/QtMultimedia/plugin.cpp+7−3 tests/mocks/QtMultimedia/videooutput.cpp+127 tests/mocks/QtMultimedia/videooutput.h+52 tests/mocks/Unity/Application/ApplicationManager.cpp+6−1 tests/mocks/Unity/Application/UbuntuKeyboardInfo.cpp+1−1 tests/mocks/Unity/Application/resources/screenshots/browser@12.png tests/mocks/Unity/Application/resources/screenshots/camera@12.png tests/mocks/Unity/Application/resources/screenshots/dialer@12.png tests/mocks/Unity/Application/resources/screenshots/facebook@12.png tests/mocks/Unity/Application/resources/screenshots/gallery@12.png tests/mocks/Unity/Application/resources/screenshots/libreoffice@12.png tests/mocks/Unity/Application/resources/screenshots/map@12.png tests/mocks/Unity/Application/resources/screenshots/music@12.png tests/mocks/Unity/Application/resources/screenshots/twitter@12.png tests/mocks/Unity/Application/resources/screenshots/unity8-dash@12.png tests/mocks/Unity/Application/resources/screenshots/vkb_portrait.png tests/mocks/Unity/Application/resources/vkb_portrait.png tests/mocks/Unity/fake_scope.cpp+6−1 tests/mocks/Unity/fake_scope.h+1 tests/mocks/Utils/CMakeLists.txt+5−1 tests/mocks/Utils/windowstatestorage.cpp+2−2 tests/mocks/Utils/windowstatestorage.h+1−1 tests/plugins/AccountsService/CMakeLists.txt−5 tests/plugins/Dash/CMakeLists.txt+4 tests/plugins/Dash/horizontaljournaltest.cpp−3 tests/plugins/Dash/listviewwithpageheadersectionexternalmodeltest.cpp−3 tests/plugins/Dash/listviewwithpageheadersectiontest.cpp−3 tests/plugins/Dash/listviewwithpageheadertest.cpp−3 tests/plugins/Dash/organicgridtest.cpp−3 tests/plugins/Dash/verticaljournaltest.cpp−3 tests/plugins/Ubuntu/Gestures/CMakeLists.txt+3−2 tests/plugins/Unity/Session/CMakeLists.txt+29−1 tests/plugins/Unity/Session/LightDMServer.cpp+29 tests/plugins/Unity/Session/LightDMServer.h+38 tests/plugins/Unity/Session/LogindServer.cpp+33 tests/plugins/Unity/Session/LogindServer.h+43 tests/plugins/Unity/Session/interfaces.xml+17 tests/plugins/Unity/Session/server.cpp+50 tests/plugins/Unity/Session/sessionbackendtest.cpp+15−1 tests/qmltests/CMakeLists.txt+3−1 tests/qmltests/Components/CMakeLists.txt+5−1 tests/qmltests/Components/tst_LazyImage/portrait.png tests/qmltests/Components/tst_LazyImage/square.png tests/qmltests/Components/tst_LazyImage/wide.png tests/qmltests/Components/tst_MediaServices.qml+230 tests/qmltests/Dash/Previews/tst_PreviewSharing.qml → tests/qmltests/Components/tst_SharingPicker.qml+8−8 tests/qmltests/Dash/Previews/tst_PreviewInlineVideo.qml+114 tests/qmltests/Dash/Previews/tst_PreviewMediaToolbar.qml+3−3 tests/qmltests/Dash/Previews/tst_PreviewRatingDisplayCreationRanges.qml+51−15 tests/qmltests/Dash/Previews/tst_PreviewRatingEdit.qml+3 tests/qmltests/Dash/Previews/tst_PreviewWidgetFactory.qml+17−16 tests/qmltests/Dash/artwork/avatar@12.png tests/qmltests/Dash/artwork/background.png tests/qmltests/Dash/artwork/checkers.png tests/qmltests/Dash/artwork/emblem.png tests/qmltests/Dash/artwork/music-player-design.png tests/qmltests/Dash/tst_Dash.qml+56−71 tests/qmltests/Dash/tst_DashContent.qml+7−1 tests/qmltests/Dash/tst_GenericScopeView.qml+7 tests/qmltests/Launcher/tst_Launcher.qml+23−3 tests/qmltests/Panel/tst_IndicatorsMenu.qml+29−1 tests/qmltests/Stages/tst_DesktopStage.qml+135 tests/qmltests/Stages/tst_TabletStage.qml+162−35 tests/qmltests/Stages/tst_WindowResizeArea.qml+36−34 tests/qmltests/UnityLogo.png tests/qmltests/tst_OrientedShell.qml+53−44 tests/utils/modules/Unity/Test/CMakeLists.txt+5−1
build.sh+2−2
@@ -46,11 +46,11 @@ install_dependencies() {
4646 qmenumodel-qml \
4747 qml-module-qtquick-layouts \
4848 qml-module-qtquick-xmllistmodel \
49+ qml-module-ubuntu-components \
50+ qml-module-ubuntu-thumbnailer0.1 \
4951 qtdeclarative5-gsettings1.0 \
50− qtdeclarative5-ubuntu-ui-toolkit-plugin \
5152 qtdeclarative5-ubuntu-settings-components \
5253 qtdeclarative5-ubuntu-telephony0.1 \
53− qtdeclarative5-ubuntu-thumbnailer0.1 \
5454 qtdeclarative5-unity-notifications-plugin \
5555 qml-module-ubuntu-connectivity \
5656 ubuntu-mobile-icons \
data/unity8-dash.png

Binary file not shown.

data/unity8.conf+13
@@ -65,6 +65,19 @@ end script
6565exec ${BINARY:-unity8} --mode=$MODE $ARGS
6666
6767post-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+
6881 initctl emit --no-wait indicator-services-start
6982end script
7083
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+
164unity8 (8.12+16.10.20160620.3-0ubuntu1) yakkety; urgency=medium
265
366 [ Michael Terry, Michał Sawicz ]
debian/control+7−6
@@ -44,11 +44,14 @@ Build-Depends: cmake,
4444 python3-all:any,
4545 python3-setuptools,
4646 qml-module-qt-labs-folderlistmodel,
47+ qml-module-qtmultimedia (>= 5.4.1-1ubuntu19~overlay2),
4748 qml-module-qtquick-layouts,
4849 qml-module-qtquick-xmllistmodel,
4950 qml-module-qtquick2,
5051 qml-module-qtsysteminfo,
5152 qml-module-qttest,
53+ qml-module-ubuntu-components (>= 1.3.1845) | qml-module-ubuntu-components-gles (>= 1.3.1845),
54+ qml-module-ubuntu-web,
5255 qt5-default,
5356 qtbase5-dev (>= 5.4),
5457 qtbase5-dev-tools,
@@ -56,11 +59,8 @@ Build-Depends: cmake,
5659 qtdeclarative5-dev (>= 5.4),
5760 qtdeclarative5-dev-tools,
5861 qtdeclarative5-private-dev (>= 5.4),
59− qtdeclarative5-qtmultimedia-plugin (>= 5.4.1-1ubuntu19~overlay2),
6062 qtdeclarative5-ubuntu-content1,
6163 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,
6464 ttf-ubuntu-font-family,
6565Standards-Version: 3.9.4
6666Homepage: http://launchpad.net/unity
@@ -75,7 +75,7 @@ X-Ubuntu-Use-Langpack: yes
7575Package: indicators-client
7676Architecture: amd64 armhf i386
7777Depends: 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),
7979 unity8 (= ${binary:Version}),
8080 ${misc:Depends},
8181 ${shlibs:Depends},
@@ -107,6 +107,7 @@ Depends: dmz-cursor-theme,
107107 qml-module-qt-labs-folderlistmodel,
108108 qml-module-qtquick-xmllistmodel,
109109 qml-module-qtsysteminfo,
110+ qml-module-ubuntu-web,
110111 qtdeclarative5-qtmir-plugin (>= 0.4.8),
111112 qtdeclarative5-ubuntu-telephony0.1,
112113 qtdeclarative5-ubuntu-web-plugin,
@@ -132,9 +133,9 @@ Description: Unity 8 shell
132133Package: unity8-common
133134Architecture: all
134135Depends: 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,
135138 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),
138139 qtdeclarative5-unity-notifications-plugin (>= 0.1.2) | unity-notifications-impl,
139140 ubuntu-thumbnailer-impl-0,
140141 unity-application-impl-17,
plugins/Cursor/CMakeLists.txt+4
@@ -3,6 +3,10 @@ add_subdirectory(3rd_party)
33include_directories(
44 ${CMAKE_CURRENT_SOURCE_DIR}
55 ${CMAKE_CURRENT_SOURCE_DIR}/3rd_party/xcursor
6+)
7+
8+include_directories(
9+ SYSTEM
610 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
711)
812
plugins/Cursor/Cursor.qml+26−11
@@ -16,6 +16,7 @@
1616
1717import QtQuick 2.4
1818import Cursor 1.1
19+import Powerd 0.1
1920
2021MousePointer {
2122 id: mousePointer
@@ -26,19 +27,33 @@ MousePointer {
2627 cursorName: mousePointer.cursorName
2728 }
2829
29− AnimatedSprite {
30− x: -imageInfo.hotspot.x
31− y: -imageInfo.hotspot.y
32− source: "image://cursor/" + mousePointer.themeName + "/" + mousePointer.cursorName
30+ Loader {
31+ active: mousePointer.visible && imageInfo.frameCount > 1
32+ sourceComponent: AnimatedSprite {
33+ x: -imageInfo.hotspot.x
34+ y: -imageInfo.hotspot.y
35+ source: "image://cursor/" + mousePointer.themeName + "/" + mousePointer.cursorName
3336
34− interpolate: false
37+ interpolate: false
3538
36− width: imageInfo.frameWidth
37− height: imageInfo.frameHeight
39+ width: imageInfo.frameWidth
40+ height: imageInfo.frameHeight
3841
39− frameCount: imageInfo.frameCount
40− frameDuration: imageInfo.frameDuration
41− frameWidth: imageInfo.frameWidth
42− frameHeight: imageInfo.frameHeight
42+ frameCount: imageInfo.frameCount
43+ frameDuration: imageInfo.frameDuration
44+ frameWidth: imageInfo.frameWidth
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+ }
4358 }
4459}
plugins/Cursor/MousePointer.cpp+5−5
@@ -52,17 +52,17 @@ void MousePointer::handleMouseEvent(ulong timestamp, QPointF movement, Qt::Mouse
5252 if (newX < 0) {
5353 Q_EMIT pushedLeftBoundary(qAbs(newX), buttons);
5454 newX = 0;
55− } else if (newX > parentItem()->width()) {
56− Q_EMIT pushedRightBoundary(newX - parentItem()->width(), buttons);
57− newX = parentItem()->width();
55+ } else if (newX >= parentItem()->width()) {
56+ Q_EMIT pushedRightBoundary(newX - (parentItem()->width() - 1), buttons);
57+ newX = parentItem()->width() - 1;
5858 }
5959 setX(newX);
6060
6161 qreal newY = y() + appliedMovement.y();
6262 if (newY < 0) {
6363 newY = 0;
64− } else if (newY > parentItem()->height()) {
65− newY = parentItem()->height();
64+ } else if (newY >= parentItem()->height()) {
65+ newY = parentItem()->height() - 1;
6666 }
6767 setY(newY);
6868
plugins/Dash/CMakeLists.txt+4
@@ -10,6 +10,10 @@ endif()
1010include_directories(
1111 ${CMAKE_CURRENT_SOURCE_DIR}
1212 ${CMAKE_CURRENT_BINARY_DIR}
13+)
14+
15+include_directories(
16+ SYSTEM
1317 ${Qt5Qml_PRIVATE_INCLUDE_DIRS}
1418 ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
1519 ${Qt5V8_PRIVATE_INCLUDE_DIR}
plugins/Dash/abstractdashview.h+1−3
@@ -21,11 +21,9 @@
2121
2222class QAbstractItemModel;
2323class QQmlComponent;
24−#pragma GCC diagnostic push
25−#pragma GCC diagnostic ignored "-pedantic"
24+
2625#include <private/qqmldelegatemodel_p.h>
2726#include <qqmlinfo.h>
28−#pragma GCC diagnostic pop
2927
3028class AbstractDashView : public QQuickItem
3129{
plugins/Dash/graphics/music_progress_bg.png

Binary file not shown.

plugins/Dash/horizontaljournal.cpp+1−4
@@ -26,12 +26,9 @@
2626#include "horizontaljournal.h"
2727
2828#include <qqmlengine.h>
29−#pragma GCC diagnostic push
30−#pragma GCC diagnostic ignored "-pedantic"
31−#include <private/qqmldelegatemodel_p.h>
3229#include <qqmlinfo.h>
30+#include <private/qqmldelegatemodel_p.h>
3331#include <private/qquickitem_p.h>
34−#pragma GCC diagnostic pop
3532
3633HorizontalJournal::HorizontalJournal()
3734 : m_firstVisibleIndex(-1)
plugins/Dash/listviewwithpageheader.cpp+8−4
@@ -95,14 +95,11 @@
9595#include <QDebug>
9696#include <qqmlinfo.h>
9797#include <qqmlengine.h>
98−#pragma GCC diagnostic push
99−#pragma GCC diagnostic ignored "-pedantic"
10098#include <private/qqmlcontext_p.h>
10199#include <private/qqmldelegatemodel_p.h>
102100#include <private/qqmlglobal_p.h>
103101#include <private/qquickitem_p.h>
104102#include <private/qquickanimation_p.h>
105−#pragma GCC diagnostic pop
106103// #include <private/qquickrectangle_p.h>
107104
108105qreal ListViewWithPageHeader::ListItem::height() const
@@ -256,6 +253,7 @@ void ListViewWithPageHeader::setHeader(QQuickItem *headerItem)
256253 oldHeaderHeight = m_headerItem->height();
257254 oldHeaderY = m_headerItem->y();
258255 m_headerItem->setParentItem(nullptr);
256+ QQuickItemPrivate::get(m_headerItem)->removeItemChangeListener(this, QQuickItemPrivate::ImplicitHeight);
259257 }
260258 m_headerItem = headerItem;
261259 if (m_headerItem) {
@@ -702,7 +700,9 @@ void ListViewWithPageHeader::reallyReleaseItem(ListItem *listItem)
702700 if (flags & QQmlDelegateModel::Destroyed) {
703701 item->setParentItem(nullptr);
704702 }
705− listItem->sectionItem()->deleteLater();
703+ if (listItem->sectionItem()) {
704+ listItem->sectionItem()->deleteLater();
705+ }
706706 delete listItem;
707707}
708708
@@ -1334,6 +1334,10 @@ void ListViewWithPageHeader::layout()
13341334 }
13351335 }
13361336 }
1337+ if (m_headerItem) {
1338+ const bool cullHeader = m_headerItem->y() + m_headerItem->height() < contentY();
1339+ QQuickItemPrivate::get(m_headerItem)->setCulled(cullHeader);
1340+ }
13371341 m_inLayout = false;
13381342}
13391343
plugins/Dash/organicgrid.cpp−3
@@ -16,10 +16,7 @@
1616
1717#include "organicgrid.h"
1818
19−#pragma GCC diagnostic push
20−#pragma GCC diagnostic ignored "-pedantic"
2119#include <private/qquickitem_p.h>
22−#pragma GCC diagnostic pop
2320
2421OrganicGrid::OrganicGrid()
2522 : m_firstVisibleIndex(-1)
plugins/Dash/verticaljournal.cpp−3
@@ -27,10 +27,7 @@
2727 */
2828#include "verticaljournal.h"
2929
30−#pragma GCC diagnostic push
31−#pragma GCC diagnostic ignored "-pedantic"
3230#include <private/qquickitem_p.h>
33−#pragma GCC diagnostic pop
3431
3532VerticalJournal::VerticalJournal()
3633 : m_columnWidth(0)
plugins/Lights/CMakeLists.txt+1
@@ -2,6 +2,7 @@ pkg_check_modules(ANDROID_HEADERS REQUIRED android-headers)
22pkg_check_modules(ANDROID_HARDWARE REQUIRED libhardware)
33
44include_directories(
5+ SYSTEM
56 ${ANDROID_HEADERS_INCLUDE_DIRS}
67)
78
plugins/Lights/android-hardware.h−4
@@ -15,9 +15,5 @@
1515 *
1616 */
1717
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−
2218#include <hardware/hardware.h>
2319#include <hardware/lights.h>
plugins/Ubuntu/DownloadDaemonListener/CMakeLists.txt−1
@@ -1,7 +1,6 @@
11include_directories(
22 ${CMAKE_CURRENT_SOURCE_DIR}
33 ${CMAKE_CURRENT_BINARY_DIR}
4− ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
54)
65
76set(DOWNLOADDAEMONLISTENER_SOURCES
plugins/Ubuntu/Gestures/CMakeLists.txt+5−1
@@ -24,7 +24,10 @@ qt5_use_modules(UbuntuGesturesQml Core Quick)
2424# So that Foo.cpp can #include "Foo.moc"
2525include_directories(${CMAKE_CURRENT_BINARY_DIR})
2626
27−include_directories(${UBUNTUGESTURES_INCLUDE_DIRS})
27+include_directories(
28+ SYSTEM
29+ ${UBUNTUGESTURES_INCLUDE_DIRS}
30+)
2831
2932# There's no cmake var for v8 include path :-/ so create one
3033LIST(GET Qt5Core_INCLUDE_DIRS 0 QtCoreDir0)
@@ -32,6 +35,7 @@ SET(Qt5V8_PRIVATE_INCLUDE_DIR ${QtCoreDir0}/QtV8/${Qt5Core_VERSION_STRING}/QtV8)
3235
3336# DANGER! DANGER! Using Qt's private API!
3437include_directories(
38+ SYSTEM
3539 ${Qt5Qml_PRIVATE_INCLUDE_DIRS}
3640 ${Qt5Quick_INCLUDE_DIRS}
3741 ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
plugins/Ubuntu/Gestures/TouchDispatcher.cpp−4
@@ -19,11 +19,7 @@
1919#include <QGuiApplication>
2020#include <QScopedPointer>
2121#include <QStyleHints>
22−
23−#pragma GCC diagnostic push
24−#pragma GCC diagnostic ignored "-pedantic"
2522#include <private/qquickitem_p.h>
26−#pragma GCC diagnostic pop
2723
2824#define TOUCHDISPATCHER_DEBUG 0
2925
plugins/Unity/Indicators/CMakeLists.txt+4
@@ -6,6 +6,10 @@ find_package(Qt5Quick REQUIRED)
66include_directories(
77 ${CMAKE_CURRENT_SOURCE_DIR}
88 ${CMAKE_CURRENT_BINARY_DIR}
9+)
10+
11+include_directories(
12+ SYSTEM
913 ${GLIB_INCLUDE_DIRS}
1014 ${GIO_INCLUDE_DIRS}
1115 ${QMENUMODEL_INCLUDE_DIRS}
plugins/Unity/Session/CMakeLists.txt+2
@@ -6,6 +6,8 @@ include_directories(
66 ${libunity8-private_SOURCE_DIR}
77)
88
9+add_definitions(-DSM_BUSNAME=systemBus)
10+
911set(QMLSESSIONPLUGIN_SRC
1012 plugin.cpp
1113 dbusunitysessionservice.cpp
plugins/Unity/Session/dbusunitysessionservice.cpp+50−18
@@ -64,19 +64,19 @@ public:
6464 QStringLiteral("GetSessionByPID"));
6565 msg << (quint32) getpid();
6666
67− QDBusReply<QDBusObjectPath> reply = QDBusConnection::systemBus().call(msg);
67+ QDBusReply<QDBusObjectPath> reply = QDBusConnection::SM_BUSNAME().call(msg);
6868 if (reply.isValid()) {
6969 logindSessionPath = reply.value().path();
7070
7171 // start watching the Active property
72− QDBusConnection::systemBus().connect(LOGIN1_SERVICE, logindSessionPath, QStringLiteral("org.freedesktop.DBus.Properties"), QStringLiteral("PropertiesChanged"),
73− this, SLOT(onPropertiesChanged(QString,QVariantMap,QStringList)));
72+ QDBusConnection::SM_BUSNAME().connect(LOGIN1_SERVICE, logindSessionPath, QStringLiteral("org.freedesktop.DBus.Properties"), QStringLiteral("PropertiesChanged"),
73+ this, SLOT(onPropertiesChanged(QString,QVariantMap,QStringList)));
7474
7575 setupSystemdInhibition();
7676
7777 // re-enable the inhibition upon resume from sleep
78− QDBusConnection::systemBus().connect(LOGIN1_SERVICE, LOGIN1_PATH, LOGIN1_IFACE, QStringLiteral("PrepareForSleep"),
79− this, SLOT(onResuming(bool)));
78+ QDBusConnection::SM_BUSNAME().connect(LOGIN1_SERVICE, LOGIN1_PATH, LOGIN1_IFACE, QStringLiteral("PrepareForSleep"),
79+ this, SLOT(onResuming(bool)));
8080 } else {
8181 qWarning() << "Failed to get logind session path" << reply.error().message();
8282 }
@@ -96,7 +96,7 @@ public:
9696 msg << "Unity8 handles power events"; // why
9797 msg << "block"; // mode
9898
99− QDBusPendingCall pendingCall = QDBusConnection::systemBus().asyncCall(msg);
99+ QDBusPendingCall pendingCall = QDBusConnection::SM_BUSNAME().asyncCall(msg);
100100 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall, this);
101101 connect(watcher, &QDBusPendingCallWatcher::finished,
102102 this, [this](QDBusPendingCallWatcher* watcher) {
@@ -114,7 +114,7 @@ public:
114114 bool checkLogin1Call(const QString &method) const
115115 {
116116 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);
118118 return reply.isValid() && (reply == QStringLiteral("yes") || reply == QStringLiteral("challenge"));
119119 }
120120
@@ -125,7 +125,7 @@ public:
125125 LOGIN1_IFACE,
126126 method);
127127 msg.setArguments(args);
128− QDBusConnection::systemBus().asyncCall(msg);
128+ QDBusConnection::SM_BUSNAME().asyncCall(msg);
129129 }
130130
131131 void setActive(bool active)
@@ -157,7 +157,7 @@ public:
157157 msg << LOGIN1_SESSION_IFACE;
158158 msg << ACTIVE_KEY;
159159
160− QDBusPendingCall pendingCall = QDBusConnection::systemBus().asyncCall(msg);
160+ QDBusPendingCall pendingCall = QDBusConnection::SM_BUSNAME().asyncCall(msg);
161161 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall, this);
162162 connect(watcher, &QDBusPendingCallWatcher::finished,
163163 this, [this](QDBusPendingCallWatcher* watcher) {
@@ -191,7 +191,7 @@ public:
191191 msg << LOGIN1_SESSION_IFACE;
192192 msg << IDLE_SINCE_KEY;
193193
194− QDBusReply<QVariant> reply = QDBusConnection::systemBus().call(msg);
194+ QDBusReply<QVariant> reply = QDBusConnection::SM_BUSNAME().call(msg);
195195 if (reply.isValid()) {
196196 return reply.value().value<quint64>();
197197 } else {
@@ -208,7 +208,7 @@ public:
208208 LOGIN1_SESSION_IFACE,
209209 QStringLiteral("SetIdleHint"));
210210 msg << idle;
211− QDBusConnection::systemBus().asyncCall(msg);
211+ QDBusConnection::SM_BUSNAME().asyncCall(msg);
212212 }
213213
214214private Q_SLOTS:
@@ -243,11 +243,11 @@ DBusUnitySessionService::DBusUnitySessionService()
243243 : UnityDBusObject(QStringLiteral("/com/canonical/Unity/Session"), QStringLiteral("com.canonical.Unity"))
244244{
245245 if (!d->logindSessionPath.isEmpty()) {
246− // connect our Lock() slot to the logind's session Lock() signal
247− QDBusConnection::systemBus().connect(LOGIN1_SERVICE, d->logindSessionPath, LOGIN1_SESSION_IFACE, QStringLiteral("Lock"), this, SLOT(Lock()));
246+ // connect our PromptLock() slot to the logind's session Lock() signal
247+ QDBusConnection::SM_BUSNAME().connect(LOGIN1_SERVICE, d->logindSessionPath, LOGIN1_SESSION_IFACE, QStringLiteral("Lock"), this, SLOT(PromptLock()));
248248 // ... and our Unlocked() signal to the logind's session Unlock() signal
249249 // (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()));
251251 connect(d, &DBusUnitySessionServicePrivate::prepareForSleep, this, &DBusUnitySessionService::PromptLock);
252252 } else {
253253 qWarning() << "Failed to connect to logind's session Lock/Unlock signals";
@@ -332,15 +332,41 @@ QString DBusUnitySessionService::HostName() const
332332
333333void DBusUnitySessionService::PromptLock()
334334{
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.
335338 Q_EMIT LockRequested();
336339 Q_EMIT lockRequested();
337340}
338341
339342void DBusUnitySessionService::Lock()
340343{
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.
342364 PromptLock();
365+ switchToGreeter();
366+}
343367
368+void DBusUnitySessionService::switchToGreeter()
369+{
344370 // lock the session using the org.freedesktop.DisplayManager system DBUS service
345371 const QString sessionPath = QString::fromLocal8Bit(qgetenv("XDG_SESSION_PATH"));
346372 QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.DisplayManager"),
@@ -348,7 +374,7 @@ void DBusUnitySessionService::Lock()
348374 QStringLiteral("org.freedesktop.DisplayManager.Session"),
349375 QStringLiteral("Lock"));
350376
351− QDBusPendingCall pendingCall = QDBusConnection::systemBus().asyncCall(msg);
377+ QDBusPendingCall pendingCall = QDBusConnection::SM_BUSNAME().asyncCall(msg);
352378 QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall, this);
353379 connect(watcher, &QDBusPendingCallWatcher::finished,
354380 this, [this](QDBusPendingCallWatcher* watcher) {
@@ -365,6 +391,12 @@ void DBusUnitySessionService::Lock()
365391 });
366392}
367393
394+void DBusUnitySessionService::doUnlock()
395+{
396+ Q_EMIT Unlocked();
397+ Q_EMIT unlocked();
398+}
399+
368400bool DBusUnitySessionService::IsLocked() const
369401{
370402 return !d->isSessionActive;
@@ -486,7 +518,7 @@ void DBusGnomeScreensaverWrapper::SetActive(bool lock)
486518
487519void DBusGnomeScreensaverWrapper::Lock()
488520{
489− performAsyncUnityCall(QStringLiteral("Lock"));
521+ performAsyncUnityCall(QStringLiteral("PromptLock"));
490522}
491523
492524quint32 DBusGnomeScreensaverWrapper::GetActiveTime() const
@@ -523,7 +555,7 @@ bool DBusScreensaverWrapper::SetActive(bool lock)
523555
524556void DBusScreensaverWrapper::Lock()
525557{
526− performAsyncUnityCall(QStringLiteral("Lock"));
558+ performAsyncUnityCall(QStringLiteral("PromptLock"));
527559}
528560
529561quint32 DBusScreensaverWrapper::GetActiveTime() const
plugins/Unity/Session/dbusunitysessionservice.h+7
@@ -106,6 +106,7 @@ Q_SIGNALS:
106106 * Emitted after the session has been unlocked.
107107 */
108108 Q_SCRIPTABLE void Unlocked();
109+ void unlocked();
109110
110111public Q_SLOTS:
111112 /**
@@ -258,6 +259,12 @@ public Q_SLOTS:
258259 * @return whether the session is currently locked
259260 */
260261 Q_SCRIPTABLE bool IsLocked() const;
262+
263+private Q_SLOTS:
264+ void doUnlock();
265+
266+private:
267+ void switchToGreeter();
261268};
262269
263270class DBusGnomeSessionManagerWrapper : public UnityDBusObject
plugins/Utils/CMakeLists.txt+5
@@ -1,6 +1,10 @@
11include_directories(
22 ${CMAKE_CURRENT_SOURCE_DIR}
33 ${CMAKE_CURRENT_BINARY_DIR}
4+)
5+
6+include_directories(
7+ SYSTEM
48 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
59 ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
610 ${GIO_INCLUDE_DIRS}
@@ -9,6 +13,7 @@ include_directories(
913set(QMLPLUGIN_SRC
1014 activefocuslogger.cpp
1115 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
16+ ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
1217 applicationsfiltermodel.cpp
1318 constants.cpp
1419 WindowInputMonitor.cpp
plugins/Utils/globalfunctions.cpp+1−4
@@ -16,11 +16,8 @@
1616
1717#include "globalfunctions.h"
1818
19−#pragma GCC diagnostic push
20−#pragma GCC diagnostic ignored "-pedantic"
21−#include <private/qquickitem_p.h>
22−#pragma GCC diagnostic pop
2319#include <QQmlEngine>
20+#include <private/qquickitem_p.h>
2421
2522GlobalFunctions::GlobalFunctions(QObject *parent)
2623 : QObject(parent)
plugins/Utils/windowstatestorage.cpp+2−2
@@ -95,7 +95,7 @@ void WindowStateStorage::saveStage(const QString &appId, int stage)
9595 saveValue(queryString);
9696}
9797
98−int WindowStateStorage::getStage(const QString &appId) const
98+int WindowStateStorage::getStage(const QString &appId, int defaultValue) const
9999{
100100 const QString queryString = QStringLiteral("SELECT * FROM stage WHERE appId = '%1';")
101101 .arg(sanitiseString(appId));
@@ -103,7 +103,7 @@ int WindowStateStorage::getStage(const QString &appId) const
103103 QSqlQuery query = getValue(queryString);
104104
105105 if (!query.first()) {
106− return unity::shell::application::ApplicationInfoInterface::MainStage;
106+ return defaultValue;
107107 }
108108 return query.value("stage").toInt();
109109}
plugins/Utils/windowstatestorage.h+1−1
@@ -49,7 +49,7 @@ public:
4949 Q_INVOKABLE QRect getGeometry(const QString &windowId, const QRect defaultValue) const;
5050
5151 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;
5353
5454private:
5555 void initdb();
plugins/WindowManager/CMakeLists.txt+1
@@ -4,6 +4,7 @@ set(WINDOWMANAGER_SRC
44 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
55 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/Mir.h
66 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceInterface.h
7+ ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceListInterface.h
78 )
89
910add_library(windowmanager-qml SHARED ${WINDOWMANAGER_SRC})
po/sk.po+31−31
@@ -8,15 +8,15 @@ msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"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"
1212"Last-Translator: P_E_T_O <Unknown>\n"
1313"Language-Team: Slovak <sk@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"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"
19−"X-Generator: Launchpad (build 18111)\n"
18+"X-Launchpad-Export-Date: 2016-06-28 05:41+0000\n"
19+"X-Generator: Launchpad (build 18115)\n"
2020"Language: sk\n"
2121
2222#: plugins/IntegratedLightDM/Greeter.cpp:112
@@ -127,59 +127,59 @@ msgstr "Reštartovať"
127127
128128#: qml/Components/KeyboardShortcutsOverlay.qml:41
129129msgid "Keyboard Shortcuts"
130−msgstr ""
130+msgstr "Klávesové skratky"
131131
132132#: qml/Components/KeyboardShortcutsOverlay.qml:55
133133msgid "Unity 8"
134−msgstr ""
134+msgstr "Unity 8"
135135
136136#: qml/Components/KeyboardShortcutsOverlay.qml:62
137137msgid "PrtScr"
138−msgstr ""
138+msgstr "PrtScr"
139139
140140#: qml/Components/KeyboardShortcutsOverlay.qml:67
141141msgid "Takes a screenshot."
142−msgstr ""
142+msgstr "Vytvorí snímku obrazovky."
143143
144144#: qml/Components/KeyboardShortcutsOverlay.qml:75
145145msgid "Alt + PrtScr"
146−msgstr ""
146+msgstr "Alt + PrtScr"
147147
148148#: qml/Components/KeyboardShortcutsOverlay.qml:80
149149msgid "Takes a screenshot of a window."
150−msgstr ""
150+msgstr "Vytvorí snímku okna."
151151
152152#: qml/Components/KeyboardShortcutsOverlay.qml:88
153153msgid "Super + Space"
154−msgstr ""
154+msgstr "Super + Space"
155155
156156#: qml/Components/KeyboardShortcutsOverlay.qml:93
157157msgid "Switches to next keyboard layout."
158−msgstr ""
158+msgstr "Prepne na ďalšie rozloženie klávesnice."
159159
160160#: qml/Components/KeyboardShortcutsOverlay.qml:101
161161msgid "Super + Shift + Space"
162−msgstr ""
162+msgstr "Super + Shift + Space"
163163
164164#: qml/Components/KeyboardShortcutsOverlay.qml:106
165165msgid "Switches to previous keyboard layout."
166−msgstr ""
166+msgstr "Prepne na predošlé rozloženie klávesnice."
167167
168168#: qml/Components/KeyboardShortcutsOverlay.qml:118
169169msgid "Launcher"
170−msgstr ""
170+msgstr "Launcher"
171171
172172#: qml/Components/KeyboardShortcutsOverlay.qml:125
173173msgid "Super (Hold)"
174−msgstr ""
174+msgstr "Super (Držať)"
175175
176176#: qml/Components/KeyboardShortcutsOverlay.qml:130
177177msgid "Opens the launcher, displays shortcuts."
178−msgstr ""
178+msgstr "Otvorí launcher, zobrazí skratky."
179179
180180#: qml/Components/KeyboardShortcutsOverlay.qml:138
181181msgid "Alt + F1"
182−msgstr ""
182+msgstr "Alt + F1"
183183
184184#: qml/Components/KeyboardShortcutsOverlay.qml:143
185185msgid "Opens launcher keyboard navigation mode."
@@ -187,55 +187,55 @@ msgstr ""
187187
188188#: qml/Components/KeyboardShortcutsOverlay.qml:151
189189msgid "Super + Tab"
190−msgstr ""
190+msgstr "Super + Tab"
191191
192192#: qml/Components/KeyboardShortcutsOverlay.qml:156
193193msgid "Switches applications via the launcher."
194−msgstr ""
194+msgstr "Prepínanie aplikácií pomocou launchera."
195195
196196#: qml/Components/KeyboardShortcutsOverlay.qml:164
197197msgid "Super + 0 to 9"
198−msgstr ""
198+msgstr "Super + 0 do 9"
199199
200200#: qml/Components/KeyboardShortcutsOverlay.qml:169
201201msgid "Same as clicking on a launcher icon."
202−msgstr ""
202+msgstr "Rovnaké ako kliknutie na ikonu v launchery."
203203
204204#: qml/Components/KeyboardShortcutsOverlay.qml:181
205205msgid "Scopes"
206−msgstr ""
206+msgstr "Scopes"
207207
208208#: qml/Components/KeyboardShortcutsOverlay.qml:188
209209msgid "Super (Tap)"
210−msgstr ""
210+msgstr "Super (Klik)"
211211
212212#: qml/Components/KeyboardShortcutsOverlay.qml:193
213213msgid "Opens the Scopes home."
214−msgstr ""
214+msgstr "Otvorí Scopes."
215215
216216#: qml/Components/KeyboardShortcutsOverlay.qml:209
217217msgid "Switching"
218−msgstr ""
218+msgstr "Prepínanie"
219219
220220#: qml/Components/KeyboardShortcutsOverlay.qml:216
221221msgid "Alt + Tab"
222−msgstr ""
222+msgstr "Alt + Tab"
223223
224224#: qml/Components/KeyboardShortcutsOverlay.qml:221
225225msgid "Switches between applications."
226−msgstr ""
226+msgstr "Prepínanie medzi aplikáciami."
227227
228228#: qml/Components/KeyboardShortcutsOverlay.qml:229
229229msgid "Super + W"
230−msgstr ""
230+msgstr "Super + W"
231231
232232#: qml/Components/KeyboardShortcutsOverlay.qml:234
233233msgid "Opens the desktop spread."
234−msgstr ""
234+msgstr "Otvorí rozloženie obrazoviek."
235235
236236#: qml/Components/KeyboardShortcutsOverlay.qml:242
237237msgid "Cursor Left or Right"
238−msgstr ""
238+msgstr "Ľavý alebo pravý kurzorový kláves"
239239
240240#: qml/Components/KeyboardShortcutsOverlay.qml:247
241241msgid "Moves the focus."
@@ -243,7 +243,7 @@ msgstr ""
243243
244244#: qml/Components/KeyboardShortcutsOverlay.qml:259
245245msgid "Windows"
246−msgstr ""
246+msgstr "Okná"
247247
248248#: qml/Components/KeyboardShortcutsOverlay.qml:266
249249msgid "Ctrl + Super + D"
po/unity8.pot+34−20
@@ -8,7 +8,7 @@ msgid ""
88msgstr ""
99"Project-Id-Version: unity8\n"
1010"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"
1212"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
1313"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1414"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -289,6 +289,16 @@ msgstr ""
289289msgid "OK"
290290msgstr ""
291291
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+
292302#: qml/Components/ModeSwitchWarningDialog.qml:32
293303msgid "Apps may have unsaved data:"
294304msgstr ""
@@ -315,6 +325,10 @@ msgstr ""
315325msgid "Close all"
316326msgstr ""
317327
328+#: qml/Components/SharingPicker.qml:54
329+msgid "Preview Share Item"
330+msgstr ""
331+
318332#: qml/Dash/DashPageHeader.qml:333
319333msgctxt "Button: Open the Ubuntu Store"
320334msgid "Store"
@@ -388,10 +402,6 @@ msgstr ""
388402msgid "Add a review"
389403msgstr ""
390404
391−#: qml/Dash/Previews/PreviewSharing.qml:66
392−msgid "Preview Share Item"
393−msgstr ""
394−
395405#: qml/Dash/PullToRefreshScopeStyle.qml:55
396406msgid "Pull to refresh…"
397407msgstr ""
@@ -408,11 +418,11 @@ msgstr ""
408418msgid "Manage"
409419msgstr ""
410420
411−#: qml/Dash/ScopesList.qml:101
421+#: qml/Dash/ScopesList.qml:102
412422msgid "Home"
413423msgstr ""
414424
415−#: qml/Dash/ScopesList.qml:102
425+#: qml/Dash/ScopesList.qml:103
416426msgid "Also installed"
417427msgstr ""
418428
@@ -426,7 +436,7 @@ msgstr ""
426436msgid "Unlock"
427437msgstr ""
428438
429−#: qml/Greeter/Greeter.qml:536
439+#: qml/Greeter/Greeter.qml:539
430440msgid "Try again"
431441msgstr ""
432442
@@ -617,7 +627,7 @@ msgstr ""
617627#: qml/Wizard/Pages/40-location.qml:271 qml/Wizard/Pages/50-timezone.qml:270
618628#: qml/Wizard/Pages/60-account.qml:65 qml/Wizard/Pages/70-passwd-type.qml:144
619629#: qml/Wizard/Pages/75-report-check.qml:84
620−#: qml/Wizard/Pages/passcode-desktop.qml:124
630+#: qml/Wizard/Pages/passcode-desktop.qml:141
621631#: qml/Wizard/Pages/password-set.qml:141
622632msgid "Next"
623633msgstr ""
@@ -769,7 +779,7 @@ msgid "Service Terms"
769779msgstr ""
770780
771781#: qml/Wizard/Pages/passcode-confirm.qml:43
772−#: qml/Wizard/Pages/passcode-desktop.qml:80
782+#: qml/Wizard/Pages/passcode-desktop.qml:96
773783msgid "Confirm passcode"
774784msgstr ""
775785
@@ -786,24 +796,24 @@ msgstr ""
786796msgid "Lock Screen Passcode"
787797msgstr ""
788798
789−#: qml/Wizard/Pages/passcode-desktop.qml:56
799+#: qml/Wizard/Pages/passcode-desktop.qml:60
790800msgid "Enter 4 numbers to setup your passcode"
791801msgstr ""
792802
793−#: qml/Wizard/Pages/passcode-desktop.qml:65
803+#: qml/Wizard/Pages/passcode-desktop.qml:76
794804#: qml/Wizard/Pages/passcode-set.qml:54
795805msgid "Choose passcode"
796806msgstr ""
797807
798−#: qml/Wizard/Pages/passcode-desktop.qml:105
808+#: qml/Wizard/Pages/passcode-desktop.qml:126
799809msgid "Passcode too short"
800810msgstr ""
801811
802−#: qml/Wizard/Pages/passcode-desktop.qml:107
812+#: qml/Wizard/Pages/passcode-desktop.qml:128
803813msgid "Passcodes match"
804814msgstr ""
805815
806−#: qml/Wizard/Pages/passcode-desktop.qml:109
816+#: qml/Wizard/Pages/passcode-desktop.qml:130
807817msgid "Passcodes do not match"
808818msgstr ""
809819
@@ -852,25 +862,29 @@ msgid "To proceed with no SIM tap Skip."
852862msgstr ""
853863
854864#: qml/Wizard/PasswordMeter.qml:87
855−msgid "Passwords match"
865+msgid "Password too short"
856866msgstr ""
857867
858868#: qml/Wizard/PasswordMeter.qml:89
869+msgid "Passwords match"
870+msgstr ""
871+
872+#: qml/Wizard/PasswordMeter.qml:91
859873msgid "Passwords do not match"
860874msgstr ""
861875
862−#: qml/Wizard/PasswordMeter.qml:93
876+#: qml/Wizard/PasswordMeter.qml:95
863877msgid "Strong password"
864878msgstr ""
865879
866−#: qml/Wizard/PasswordMeter.qml:95
880+#: qml/Wizard/PasswordMeter.qml:97
867881msgid "Fair password"
868882msgstr ""
869883
870−#: qml/Wizard/PasswordMeter.qml:97
884+#: qml/Wizard/PasswordMeter.qml:99
871885msgid "Weak password"
872886msgstr ""
873887
874−#: qml/Wizard/PasswordMeter.qml:99
888+#: qml/Wizard/PasswordMeter.qml:101
875889msgid "Very weak password"
876890msgstr ""
qml/Components/LazyImage.qml+3−1
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013-2016 Canonical, Ltd.
33 *
44 * Authors:
55 * Michał Sawicz <michal.sawicz@canonical.com>
@@ -29,6 +29,7 @@ Item {
2929
3030 property real initialWidth: scaleTo == "width" || scaleTo == "fit" ? width : units.gu(10)
3131 property real initialHeight: scaleTo == "height" || scaleTo == "fit" ? height : units.gu(10)
32+ property real lastScaledDimension: scaleTo == "height" || scaleTo == "fit" ? width : height
3233
3334 property alias sourceSize: image.sourceSize
3435 property alias asynchronous: image.asynchronous
@@ -168,6 +169,7 @@ Item {
168169 easing.type: Easing.Linear; duration: UbuntuAnimation.SnapDuration
169170 }
170171 }
172+ ScriptAction { script: { lastScaledDimension = scaleTo == "height" || scaleTo == "fit" ? root.width : root.height } }
171173 }
172174 },
173175
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
2020
2121Item {
2222 id: root
23− implicitHeight: button.height
24− implicitWidth: button.width
2523
2624 property var shareData
2725 property alias active: peerPicker.active
@@ -30,18 +28,8 @@ Item {
3028 readonly property var url: shareData ? shareData["uri"] : ""
3129 readonly property Item rootItem: QuickUtils.rootItem(root)
3230
33− AbstractButton {
34− id: button
35− height: units.gu(4)
36− width: units.gu(4)
37− onClicked: 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− }
31+ function showPeerPicker() {
32+ peerPicker.visible = true;
4533 }
4634
4735 function createExportedItems(url) {
qml/Components/WindowControlButtons.qml+2
@@ -29,6 +29,7 @@ Row {
2929 property bool active: false
3030 property bool windowIsMaximized: false
3131 property bool closeButtonShown: true
32+ property bool maximizeButtonShown: true
3233 property bool overlayShown
3334
3435 signal closeClicked()
@@ -88,6 +89,7 @@ Row {
8889 hoverEnabled: true
8990 height: parent.height
9091 width: height
92+ visible: root.maximizeButtonShown
9193 acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
9294 onClicked: {
9395 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 @@
1515 */
1616
1717import QtQuick 2.4
18+import Ubuntu.Components 1.3
1819import Dash 0.1
1920
2021/*!
@@ -141,7 +142,7 @@ Item {
141142 type:real \brief Height of the card's header.
142143 */
143144 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)
145146
146147 QtObject {
147148 id: carouselTool
qml/Dash/DashApplication.qml+16−5
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2014,2015 Canonical, Ltd.
2+ * Copyright (C) 2014-2016 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -15,14 +15,25 @@
1515 */
1616
1717import QtQuick 2.4
18+import QtQuick.Window 2.2
1819import Ubuntu.Components 1.3
1920import Ubuntu.Thumbnailer 0.1 // Register support for image://thumbnailer/ and image://albumart/
2021
21−MainView {
22− implicitWidth: units.gu(40)
23− implicitHeight: units.gu(68)
22+Window {
23+ visible: true
24+ title: "Scopes" // Intentionally not translated
2425
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 {
2633 anchors.fill: parent
34+
35+ Dash {
36+ anchors.fill: parent
37+ }
2738 }
2839}
qml/Dash/Previews/Preview.qml+16
@@ -37,12 +37,16 @@ Item {
3737 //! \brief The ScopeStyle component.
3838 property var scopeStyle: null
3939
40+ //! Should the orientation be locked
41+ property int orientationLockCount: 0
42+
4043 clip: true
4144
4245 Binding {
4346 target: previewModel
4447 property: "widgetColumnCount"
4548 value: row.columns
49+ when: root.orientationLockCount === 0
4650 }
4751
4852 MouseArea {
@@ -110,6 +114,18 @@ Item {
110114 column.forceLayout();
111115 column.positionViewAtIndex(index, ListView.Contain)
112116 }
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+ }
113129 }
114130 }
115131 }
qml/Dash/Previews/PreviewImageGallery.qml+2
@@ -27,6 +27,8 @@ PreviewWidget {
2727 id: root
2828 implicitHeight: units.gu(22)
2929
30+ orientationLock: overlay.visible
31+
3032 property Item rootItem: QuickUtils.rootItem(root)
3133
3234 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
1919import "../../Components"
2020
2121Rectangle {
22− property alias shareData: sharingWidget.shareData
22+ property alias shareData: sharingPicker.shareData
2323
2424 implicitHeight: units.gu(6)
2525 color: Qt.rgba(0, 0, 0, 0.8)
26− visible: sharingWidget.active
26+ visible: sharingPicker.active
2727
28− PreviewSharing {
29− id: sharingWidget
30− objectName: "sharingWidget"
28+ Action {
29+ id: sharingAction
30+ iconName: "share"
31+ onTriggered: sharingPicker.showPeerPicker()
32+ }
33+
34+ AbstractButton {
35+ id: button
3136 anchors {
3237 left: parent.left
3338 verticalCenter: parent.verticalCenter
3439 leftMargin: units.gu(1)
3540 }
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"
3656 }
3757}
qml/Dash/Previews/PreviewRatingEdit.qml+4−1
@@ -73,7 +73,10 @@ PreviewWidget {
7373 ratingValue: widgetData["rating"]
7474 reviewText: widgetData["review"]
7575
76− onTriggered: root.triggered(widgetId, actionId, data);
76+ onTriggered: {
77+ root.triggered(widgetId, actionId, data);
78+ display.visible = true;
79+ }
7780 onMakeSureVisible: root.makeSureVisible(item);
7881 }
7982
qml/Dash/Previews/PreviewWidget.qml+3
@@ -31,6 +31,9 @@ Item {
3131 //! Should the widget show in expanded mode (For those that support it)
3232 property bool expanded: true
3333
34+ //! Should the orientation be locked
35+ property bool orientationLock: false
36+
3437 //! Set margins width.
3538 property real widgetMargins: units.gu(1)
3639
qml/Dash/Previews/PreviewWidgetFactory.qml+11−1
@@ -37,6 +37,9 @@ Loader {
3737 //! Should the widget show in expanded mode (For those that support it)
3838 property bool expanded: widgetType !== "expandable" || widgetData["expanded"] === true
3939
40+ //! Should the orientation be locked
41+ readonly property bool orientationLock: status === Loader.Ready ? item.orientationLock : false
42+
4043 //! Set margins width.
4144 property real widgetMargins: status === Loader.Ready ? item.widgetMargins : units.gu(1)
4245
@@ -70,7 +73,14 @@ Loader {
7073 case "reviews": return "PreviewRatingDisplay.qml";
7174 case "table": return "PreviewTable.qml";
7275 case "text": return "PreviewTextSummary.qml";
73− case "video": return "PreviewVideoPlayback.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) {
80+ return "PreviewVideoPlayback.qml";
81+ }
82+ return "PreviewInlineVideo.qml";
83+ }
7484 default: return "";
7585 }
7686 }
qml/Dash/Previews/PreviewZoomableImage.qml+1
@@ -31,6 +31,7 @@ PreviewWidget {
3131 implicitHeight: lazyImage.height
3232
3333 widgetMargins: -units.gu(1)
34+ orientationLock: overlay.visible
3435
3536 property Item rootItem: QuickUtils.rootItem(root)
3637
qml/Dash/ScopesList.qml+1
@@ -72,6 +72,7 @@ Item {
7272 }
7373
7474 Flickable {
75+ objectName: "scopesListFlickable"
7576 anchors {
7677 top: header.bottom
7778 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 {
478478 Connections {
479479 target: DBusUnitySessionService
480480 onLockRequested: root.forceShow()
481− onUnlocked: root.forcedUnlock = true
481+ onUnlocked: {
482+ root.forcedUnlock = true;
483+ root.hideNow();
484+ }
482485 }
483486
484487 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 @@
11/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013-2016 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -69,7 +69,9 @@ Rectangle {
6969 MouseArea {
7070 id: mouseEventEater
7171 anchors.fill: parent
72+ acceptedButtons: Qt.AllButtons
7273 hoverEnabled: true
74+ onWheel: wheel.accepted = true;
7375 }
7476
7577 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 @@
1919
2020import QtQuick 2.4
2121import Ubuntu.Components 1.3
22−import Ubuntu.Components.ListItems 1.3 as ListItem
2322
2423Rectangle {
2524 color: theme.palette.normal.background
@@ -37,6 +36,13 @@ Rectangle {
3736 function reset() {
3837 clear();
3938 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+
4046 var page = component.createObject(pages, {"profile": indicatorProfile});
4147 push(page);
4248 }
qml/Panel/Indicators/client/IndicatorsList.qml+7
@@ -58,6 +58,13 @@ Page {
5858
5959 onClicked: {
6060 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+
6168 page.pageStack.push(new_page.createObject(pages), {"indicatorProperties" : model.indicatorProperties });
6269 }
6370 }
qml/Panel/IndicatorsMenu.qml+3−1
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2016 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -80,6 +80,8 @@ Showable {
8080 MouseArea {
8181 anchors.fill: parent
8282 hoverEnabled: true
83+ acceptedButtons: Qt.AllButtons
84+ onWheel: wheel.accepted = true;
8385 }
8486
8587 MenuContent {
qml/Panel/graphics/rectangular_dropshadow@30.png

Binary file not shown.

qml/Rotation/HalfLoopRotationAnimation.qml+1−1
@@ -42,6 +42,6 @@ SequentialAnimation {
4242 from: root.fromAngle; to: root.toAngle
4343 duration: rotationDuration; easing.type: rotationEasing
4444 }
45− UpdateShellTransformations { info: root.info; shell: root.shell }
45+ UpdateShellTransformations { shell: root.shell; rotationAngle: root.toAngle }
4646 ScriptAction { script: { info.transitioning = false; } }
4747}
qml/Rotation/ImmediateRotationAction.qml+1−1
@@ -25,7 +25,7 @@ SequentialAnimation {
2525 root.info.transitioning = true;
2626 } }
2727
28− UpdateShellTransformations { info: root.info; shell: root.shell }
28+ UpdateShellTransformations { shell: root.shell; rotationAngle: root.info.requestedOrientationAngle }
2929
3030 ScriptAction { script: {
3131 root.shell.updateFocusedAppOrientation();
qml/Rotation/NinetyRotationAnimation.qml+1−1
@@ -78,7 +78,7 @@ SequentialAnimation {
7878 duration: rotationDuration; easing.type: rotationEasing
7979 }
8080 }
81− UpdateShellTransformations { info: root.info; shell: root.shell }
81+ UpdateShellTransformations { shell: root.shell; rotationAngle: root.toAngle }
8282 ScriptAction { script: {
8383 shellSnapshot.visible = false;
8484 shellSnapshot.discard();
qml/Rotation/UpdateShellTransformations.qml+4−4
@@ -17,16 +17,16 @@
1717import QtQuick 2.4
1818
1919ScriptAction {
20− property var info
2120 property var shell
21+ property int rotationAngle
2222
2323 script: {
24− shell.orientationAngle = info.requestedOrientationAngle;
25− shell.transformRotationAngle = info.requestedOrientationAngle;
24+ shell.orientationAngle = rotationAngle;
25+ shell.transformRotationAngle = rotationAngle;
2626
2727 // They must all be bindings as orientedShell's size can change
2828
29− if (info.requestedOrientationAngle === 90 || info.requestedOrientationAngle === 270) {
29+ if (rotationAngle === 90 || rotationAngle === 270) {
3030 shell.width = Qt.binding(function() { return orientedShell.height; });
3131 shell.height = Qt.binding(function() { return orientedShell.width; });
3232 } else {
qml/Stages/DecoratedWindow.qml+5−4
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2014-2015 Canonical, Ltd.
2+ * Copyright (C) 2014-2016 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -12,8 +12,6 @@
1212 *
1313 * You should have received a copy of the GNU General Public License
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Authors: Michael Zanetti <michael.zanetti@canonical.com>
1715 */
1816
1917import QtQuick 2.4
@@ -31,6 +29,7 @@ FocusScope {
3129 property alias active: decoration.active
3230 readonly property alias title: applicationWindow.title
3331 property alias fullscreen: applicationWindow.fullscreen
32+ property alias maximizeButtonShown: decoration.maximizeButtonShown
3433
3534 readonly property bool decorationShown: !fullscreen
3635 property bool highlightShown: false
@@ -38,6 +37,7 @@ FocusScope {
3837
3938 property real requestedWidth
4039 property real requestedHeight
40+
4141 property alias surfaceOrientationAngle: applicationWindow.surfaceOrientationAngle
4242 readonly property real visibleDecorationHeight: root.decorationShown ? decoration.height : 0
4343 readonly property bool counterRotate: surfaceOrientationAngle != 0 && surfaceOrientationAngle != 180
@@ -45,7 +45,8 @@ FocusScope {
4545 readonly property int minimumWidth: !counterRotate ? applicationWindow.minimumWidth : applicationWindow.minimumHeight
4646 readonly property int minimumHeight: visibleDecorationHeight + (!counterRotate ? applicationWindow.minimumHeight : applicationWindow.minimumWidth)
4747 readonly property int maximumWidth: !counterRotate ? applicationWindow.maximumWidth : applicationWindow.maximumHeight
48− readonly property int maximumHeight: visibleDecorationHeight + (!counterRotate ? applicationWindow.maximumHeight : applicationWindow.maximumWidth)
48+ readonly property int maximumHeight: (root.decorationShown && applicationWindow.maximumHeight > 0 ? decoration.height : 0)
49+ + (!counterRotate ? applicationWindow.maximumHeight : applicationWindow.maximumWidth)
4950 readonly property int widthIncrement: !counterRotate ? applicationWindow.widthIncrement : applicationWindow.heightIncrement
5051 readonly property int heightIncrement: !counterRotate ? applicationWindow.heightIncrement : applicationWindow.widthIncrement
5152
qml/Stages/DesktopSpread.qml+2
@@ -132,6 +132,8 @@ FocusScope {
132132 anchors.fill: parent
133133 visible: spreadBackground.visible
134134 enabled: visible
135+ acceptedButtons: Qt.AllButtons
136+ onWheel: wheel.accepted = true;
135137 }
136138
137139 Item {
qml/Stages/DesktopStage.qml+19−2
@@ -263,8 +263,23 @@ AbstractStage {
263263 }
264264 }
265265 z: normalZ
266− x: 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)
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.
268+ property real requestedX: priv.focusedAppDelegate ? priv.focusedAppDelegate.x + units.gu(3) : (normalZ - 1) * 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+ }
268283
269284 width: decoratedWindow.width
270285 height: decoratedWindow.height
@@ -654,6 +669,8 @@ AbstractStage {
654669 surface: model.surface
655670 active: appDelegate.focus
656671 focus: true
672+ maximizeButtonShown: (maximumWidth == 0 || maximumWidth >= appContainer.width) &&
673+ (maximumHeight == 0 || maximumHeight >= appContainer.height)
657674 overlayShown: touchControls.overlayShown
658675
659676 requestedWidth: appDelegate.requestedWidth
qml/Stages/TabletStage.qml+17−9
@@ -101,7 +101,7 @@ AbstractStage {
101101 orientations |= Qt.LandscapeOrientation | Qt.InvertedLandscapeOrientation;
102102 if (priv.sideStageItemId && !spreadView.surfaceDragging) {
103103 // 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
105105 orientations |= Qt.PortraitOrientation|Qt.InvertedPortraitOrientation;
106106 }
107107 return orientations;
@@ -624,7 +624,7 @@ AbstractStage {
624624 enabled: priv.sideStageEnabled
625625
626626 onDropped: {
627− drop.source.spreadDelegate.stage = ApplicationInfoInterface.MainStage;
627+ drop.source.spreadDelegate.saveStage(ApplicationInfoInterface.MainStage);
628628 drop.source.spreadDelegate.focus = true;
629629 }
630630 keys: "SideStage"
@@ -679,7 +679,7 @@ AbstractStage {
679679 }
680680 onDropped: {
681681 if (drop.keys == "MainStage") {
682− drop.source.spreadDelegate.stage = ApplicationInfoInterface.SideStage;
682+ drop.source.spreadDelegate.saveStage(ApplicationInfoInterface.SideStage);
683683 drop.source.spreadDelegate.focus = true;
684684 }
685685 }
@@ -810,6 +810,11 @@ AbstractStage {
810810 }
811811 }
812812
813+ function saveStage(newStage) {
814+ stage = newStage;
815+ WindowStateStorage.saveStage(application.appId, newStage);
816+ }
817+
813818 // FIXME: A regular binding doesn't update any more after closing an app.
814819 // Using a Binding for now.
815820 Binding {
@@ -843,15 +848,17 @@ AbstractStage {
843848 refreshStage();
844849 _constructing = false;
845850 }
846− Component.onDestruction: {
847− WindowStateStorage.saveStage(application.appId, stage);
848− }
849851
850852 function refreshStage() {
851853 var newStage = ApplicationInfoInterface.MainStage;
852− if (priv.sideStageEnabled) {
853− if (application && application.supportedOrientations & (Qt.PortraitOrientation|Qt.InvertedPortraitOrientation)) {
854− newStage = WindowStateStorage.getStage(application.appId);
854+ if (priv.sideStageEnabled) { // we're in lanscape rotation.
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+ }
861+ newStage = WindowStateStorage.getStage(application.appId, defaultStage);
855862 }
856863 }
857864
@@ -1085,6 +1092,7 @@ AbstractStage {
10851092 // only accept opposite stage.
10861093 Drag.keys: {
10871094 if (!surface) return "Disabled";
1095+ if (spreadDelegate.isDash) return "Disabled";
10881096
10891097 if (spreadDelegate.stage === ApplicationInfo.MainStage) {
10901098 if (spreadDelegate.application.supportedOrientations
qml/Stages/WindowControlsOverlay.qml+2−2
@@ -94,8 +94,8 @@ Item {
9494 function handlePositionChanged(mouseX, mouseY) {
9595 if (priv.dragging) {
9696 var pos = mapToItem(root.target.parent, mouseX, mouseY);
97− root.target.x = Math.round(pos.x - priv.distanceX);
98− root.target.y = Math.round(Math.max(pos.y - priv.distanceY, PanelState.panelHeight));
97+ root.target.requestedX = Math.round(pos.x - priv.distanceX);
98+ root.target.requestedY = Math.round(Math.max(pos.y - priv.distanceY, PanelState.panelHeight));
9999 }
100100 }
101101 }
qml/Stages/WindowDecoration.qml+13−6
@@ -26,18 +26,22 @@ MouseArea {
2626
2727 property Item target
2828 property alias title: titleLabel.text
29+ property alias maximizeButtonShown: buttons.maximizeButtonShown
2930 property bool active: false
31+ acceptedButtons: Qt.AllButtons // prevent leaking unhandled mouse events
3032 property alias overlayShown: buttons.overlayShown
3133
32− hoverEnabled: true
33−
3434 signal closeClicked()
3535 signal minimizeClicked()
3636 signal maximizeClicked()
3737 signal maximizeHorizontallyClicked()
3838 signal maximizeVerticallyClicked()
3939
40− onDoubleClicked: root.maximizeClicked()
40+ onDoubleClicked: {
41+ if (maximizeButtonShown && mouse.button == Qt.LeftButton) {
42+ root.maximizeClicked();
43+ }
44+ }
4145
4246 QtObject {
4347 id: priv
@@ -47,7 +51,7 @@ MouseArea {
4751 }
4852
4953 onPressedChanged: {
50− if (pressed) {
54+ if (pressed && pressedButtons == Qt.LeftButton) {
5155 var pos = mapToItem(root.target, mouseX, mouseY);
5256 priv.distanceX = pos.x;
5357 priv.distanceY = pos.y;
@@ -65,11 +69,14 @@ MouseArea {
6569 // Use integer coordinate values to ensure that target is left in a pixel-aligned
6670 // position. Mouse movement could have subpixel precision, yielding a fractional
6771 // mouse position.
68− root.target.x = Math.round(pos.x - priv.distanceX);
69− root.target.y = Math.round(Math.max(pos.y - priv.distanceY, PanelState.panelHeight));
72+ root.target.requestedX = Math.round(pos.x - priv.distanceX);
73+ root.target.requestedY = Math.round(Math.max(pos.y - priv.distanceY, PanelState.panelHeight));
7074 }
7175 }
7276
77+ // do not let unhandled wheel event pass thru the decoration
78+ onWheel: wheel.accepted = true;
79+
7380 Rectangle {
7481 anchors.fill: parent
7582 anchors.bottomMargin: -radius
qml/Stages/WindowResizeArea.qml+11−11
@@ -55,8 +55,8 @@ MouseArea {
5555
5656 function updateNormalGeometry() {
5757 if (root.target.state == "normal") {
58− normalX = root.target.x
59− normalY = root.target.y
58+ normalX = root.target.requestedX
59+ normalY = root.target.requestedY
6060 normalWidth = root.target.width
6161 normalHeight = root.target.height
6262 }
@@ -73,14 +73,14 @@ MouseArea {
7373
7474 function loadWindowState() {
7575 var windowGeometry = windowStateStorage.getGeometry(root.windowId,
76− Qt.rect(target.x, target.y, defaultWidth, defaultHeight));
76+ Qt.rect(target.requestedX, target.requestedY, defaultWidth, defaultHeight));
7777
7878 target.requestedWidth = Qt.binding(function() { return Math.min(Math.max(windowGeometry.width, d.minimumWidth), screenWidth - root.leftMargin); });
7979 target.requestedHeight = Qt.binding(function() { return Math.min(Math.max(windowGeometry.height, d.minimumHeight),
8080 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),
8282 (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); });
8484
8585 var windowState = windowStateStorage.getState(root.windowId, WindowStateStorage.WindowStateNormal)
8686 switch (windowState) {
@@ -258,8 +258,8 @@ MouseArea {
258258 var pos = mapToItem(root.target.parent, mouseX, mouseY);
259259 d.startMousePosX = pos.x;
260260 d.startMousePosY = pos.y;
261− d.startX = target.x;
262− d.startY = target.y;
261+ d.startX = target.requestedX;
262+ d.startY = target.requestedY;
263263 d.startWidth = target.width;
264264 d.startHeight = target.height;
265265 d.currentWidth = target.width;
@@ -296,7 +296,7 @@ MouseArea {
296296
297297 if (d.leftBorder) {
298298 var newTargetX = d.startX + deltaX;
299− var rightBorderX = target.x + target.width;
299+ var rightBorderX = target.requestedX + target.width;
300300 if (rightBorderX > newTargetX + d.minimumWidth) {
301301 if (rightBorderX < newTargetX + d.maximumWidth) {
302302 target.requestedWidth = rightBorderX - newTargetX;
@@ -322,7 +322,7 @@ MouseArea {
322322
323323 if (d.topBorder) {
324324 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;
326326 if (bottomBorderY > newTargetY + d.minimumHeight) {
327327 if (bottomBorderY < newTargetY + d.maximumHeight) {
328328 target.requestedHeight = bottomBorderY - newTargetY;
@@ -351,13 +351,13 @@ MouseArea {
351351 target: root.target
352352 onWidthChanged: {
353353 if (d.moveLeftBorder) {
354− target.x += d.currentWidth - target.width;
354+ target.requestedX += d.currentWidth - target.width;
355355 }
356356 d.currentWidth = target.width;
357357 }
358358 onHeightChanged: {
359359 if (d.moveTopBorder) {
360− target.y += d.currentHeight - target.height;
360+ target.requestedY += d.currentHeight - target.height;
361361 }
362362 d.currentHeight = target.height;
363363 }
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 {
3333 readonly property alias password: passwordField.text
3434 readonly property alias password2: password2Field.text
3535
36− GridLayout {
36+ Flickable {
3737 id: column
38− columns: 1
39− rows: 3
38+ clip: true
39+ flickableDirection: Flickable.VerticalFlick
4040 anchors.fill: content
41− anchors.leftMargin: leftMargin
42− anchors.rightMargin: rightMargin
41+ anchors.leftMargin: parent.leftMargin
42+ anchors.rightMargin: parent.rightMargin
4343 anchors.topMargin: customMargin
44− rowSpacing: units.gu(3)
44+
45+ bottomMargin: Qt.inputMethod.keyboardRectangle.height - height - customMargin
46+
47+ Behavior on contentY { UbuntuNumberAnimation {} }
4548
4649 Label {
4750 id: infoLabel
@@ -49,6 +52,7 @@ LocalComponents.Page {
4952 anchors {
5053 left: parent.left
5154 right: parent.right
55+ top: parent.top
5256 }
5357 wrapMode: Text.Wrap
5458 font.weight: Font.Light
@@ -57,6 +61,13 @@ LocalComponents.Page {
5761 }
5862
5963 GridLayout {
64+ anchors {
65+ left: parent.left
66+ right: parent.right
67+ top: infoLabel.bottom
68+ topMargin: units.gu(3)
69+ }
70+
6071 columns: 2
6172 columnSpacing: units.gu(2)
6273 rowSpacing: units.gu(2)
@@ -74,6 +85,11 @@ LocalComponents.Page {
7485 validator: RegExpValidator { regExp: /^\d{4}$/ }
7586 maximumLength: 4
7687 onAccepted: password2Field.forceActiveFocus()
88+ onActiveFocusChanged: {
89+ if (activeFocus) {
90+ column.contentY = y
91+ }
92+ }
7793 }
7894
7995 Label {
@@ -88,6 +104,11 @@ LocalComponents.Page {
88104 inputMethodHints: Qt.ImhDigitsOnly
89105 validator: RegExpValidator { regExp: /^\d{4}$/ }
90106 maximumLength: 4
107+ onActiveFocusChanged: {
108+ if (activeFocus) {
109+ column.contentY = y
110+ }
111+ }
91112 }
92113
93114 Label {
@@ -112,10 +133,6 @@ LocalComponents.Page {
112133 }
113134 }
114135 }
115−
116− Item { // spacer
117− Layout.fillHeight: true
118− }
119136 }
120137
121138 Component {
qml/Wizard/PasswordMeter.qml+3−1
@@ -83,7 +83,9 @@ Item {
8383 wrapMode: Text.Wrap
8484 text: {
8585 if (matching !== undefined) {
86− if (matching)
86+ if (password.length < 8)
87+ return i18n.tr("Password too short");
88+ else if (matching)
8789 return i18n.tr("Passwords match");
8890 else if (!matching)
8991 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 @@
11pkg_check_modules(CONNECTIVITY REQUIRED connectivity-qt1)
22
33include_directories(
4+ SYSTEM
45 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
56 ${CONNECTIVITY_INCLUDE_DIRS}
67)
src/Dash/main.cpp+22−21
@@ -25,6 +25,7 @@
2525#include <QCommandLineParser>
2626#include <QLibrary>
2727#include <libintl.h>
28+#include <QQmlApplicationEngine>
2829
2930#include <paths.h>
3031#include "../qmldebuggerutils.h"
@@ -83,40 +84,40 @@ int main(int argc, const char *argv[])
8384 bindtextdomain("unity8", translationDirectory().toUtf8().data());
8485 textdomain("unity8");
8586
86− QQuickView* view = new QQuickView();
87− view->setResizeMode(QQuickView::SizeRootObjectToView);
87+ #ifdef UNITY8_ENABLE_TOUCH_EMULATION
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
8895
96+ QQmlApplicationEngine *engine = new QQmlApplicationEngine(application);
97+
98+ int initialWidth = -1;
99+ int initialHeight = -1;
89100 if (parser.isSet(windowGeometryOption) &&
90101 parser.value(windowGeometryOption).split('x').size() == 2)
91102 {
92103 QStringList geom = parser.value(windowGeometryOption).split('x');
93104 QSize windowSize(geom.at(0).toInt(), geom.at(1).toInt());
94105 if (windowSize.isValid()) {
95− view->setWidth(windowSize.width());
96− view->setHeight(windowSize.height());
106+ initialWidth = windowSize.width();
107+ initialHeight = windowSize.height();
97108 }
98109 }
99−
100− view->setTitle(QStringLiteral("Scopes"));
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+ engine->rootContext()->setContextProperty("initialWidth", initialWidth);
111+ engine->rootContext()->setContextProperty("initialHeight", initialHeight);
110112
111113 QUrl source(::qmlDirectory() + "/Dash/DashApplication.qml");
112− prependImportPaths(view->engine(), ::overrideImportPaths());
113− appendImportPaths(view->engine(), ::fallbackImportPaths());
114+ prependImportPaths(engine, ::overrideImportPaths());
115+ appendImportPaths(engine, ::fallbackImportPaths());
114116
115117 CachingNetworkManagerFactory *managerFactory = new CachingNetworkManagerFactory();
116− view->engine()->setNetworkAccessManagerFactory(managerFactory);
118+ engine->setNetworkAccessManagerFactory(managerFactory);
117119
118− view->setSource(source);
119− view->show();
120+ engine->load(source);
120121
121122 UnixSignalHandler signalHandler([]{
122123 QGuiApplication::exit(0);
@@ -125,7 +126,7 @@ int main(int argc, const char *argv[])
125126
126127 int result = application->exec();
127128
128− delete view;
129+ delete engine;
129130
130131 #ifdef UNITY8_ENABLE_TOUCH_EMULATION
131132 delete mouseTouchAdaptor;
tests/CMakeLists.txt+6
@@ -126,6 +126,12 @@ function(add_unity8_qmlbenchmark PATH COMPONENT_NAME ITERATIONS)
126126 add_unity8_qmltest(${PATH} ${COMPONENT_NAME} ITERATIONS ${ITERATIONS} ${ARGN})
127127endfunction()
128128
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()
129135
130136# Actual test definitions
131137add_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+
16add_library(QtMultimedia-qml MODULE
27 plugin.cpp
3− audio.cpp
8+ mediaplayer.cpp
9+ videooutput.cpp
410 declarativeplaylist.cpp
511 )
612
7−qt5_use_modules(QtMultimedia-qml Qml)
13+qt5_use_modules(QtMultimedia-qml Qml Quick)
814
915add_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 @@
1717 */
1818
1919#include "plugin.h"
20−#include "audio.h"
20+#include "mediaplayer.h"
2121#include "declarativeplaylist.h"
22+#include "videooutput.h"
2223
2324#include <QtQml/qqml.h>
2425
2526void MockQtMultimediaPlugin::registerTypes(const char *uri)
2627{
2728 Q_ASSERT(uri == QLatin1String("QtMultimedia"));
28− qmlRegisterType<Audio>(uri, 5, 0, "Audio");
29− qmlRegisterType<Audio>(uri, 5, 0, "MediaPlayer");
29+ qmlRegisterType<MediaPlayer>(uri, 5, 0, "Audio");
30+ qmlRegisterType<MediaPlayer>(uri, 5, 0, "MediaPlayer");
31+ qmlRegisterType<VideoOutput>(uri, 5, 0, "VideoOutput");
32+
33+ qmlRegisterType<MediaDataSource>(uri, 5, 0, "MediaDataSource");
3034 qmlRegisterType<DeclarativePlaylist>(uri, 5, 6, "Playlist");
3135}
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()
302302 application->setAppId("unity8-dash");
303303 application->setName("Unity 8 Mock Dash");
304304 application->setScreenshotId("unity8-dash");
305− application->setSupportedOrientations(Qt::PrimaryOrientation);
306305 m_availableApplications.append(application);
307306
308307 application = new ApplicationInfo(this);
@@ -459,6 +458,12 @@ void ApplicationManager::buildListOfAvailableApplications()
459458 application->setScreenshotId("libreoffice");
460459 application->setIsTouchApp(false);
461460 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);
462467}
463468
464469
tests/mocks/Unity/Application/UbuntuKeyboardInfo.cpp+1−1
@@ -21,7 +21,7 @@ UbuntuKeyboardInfo::UbuntuKeyboardInfo(QObject *parent)
2121 m_x(0),
2222 m_y(0),
2323 m_width(200),
24− m_height(200)
24+ m_height(0)
2525{
2626}
2727
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
3939 , m_searching(false)
4040 , m_favorite(favorite)
4141 , m_isActive(false)
42+ , m_hasBeenActive(false)
4243 , m_hasNavigation(true)
4344 , m_hasPrimaryFilter(true)
4445 , m_currentNavigationId("root")
@@ -108,7 +109,7 @@ bool Scope::favorite() const
108109
109110unity::shell::scopes::CategoriesInterface* Scope::categories() const
110111{
111− return m_categories;
112+ return m_hasBeenActive ? m_categories : nullptr;
112113}
113114
114115unity::shell::scopes::SettingsModelInterface* Scope::settings() const
@@ -153,6 +154,10 @@ void Scope::setActive(const bool active)
153154 m_isActive = active;
154155 Q_EMIT isActiveChanged();
155156 }
157+ if (active && !m_hasBeenActive) {
158+ m_hasBeenActive = true;
159+ Q_EMIT categoriesChanged();
160+ }
156161}
157162
158163void Scope::setFavorite(const bool favorite)
tests/mocks/Unity/fake_scope.h+1
@@ -109,6 +109,7 @@ protected:
109109 bool m_searching;
110110 bool m_favorite;
111111 bool m_isActive;
112+ bool m_hasBeenActive;
112113 bool m_hasNavigation;
113114 bool m_hasPrimaryFilter;
114115 QString m_currentNavigationId;
tests/mocks/Utils/CMakeLists.txt+5−1
@@ -1,10 +1,14 @@
11include_directories(
22 ${CMAKE_CURRENT_SOURCE_DIR}
33 ${CMAKE_CURRENT_BINARY_DIR}
4+ ${CMAKE_SOURCE_DIR}/plugins/Utils
5+)
6+
7+include_directories(
8+ SYSTEM
49 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
510 ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
611 ${GIO_INCLUDE_DIRS}
7− ${CMAKE_SOURCE_DIR}/plugins/Utils
812)
913
1014set(QMLPLUGIN_SRC
tests/mocks/Utils/windowstatestorage.cpp+2−2
@@ -53,9 +53,9 @@ void WindowStateStorage::saveStage(const QString &appId, int stage)
5353 Q_EMIT stageSaved(appId, stage);
5454}
5555
56−int WindowStateStorage::getStage(const QString &appId) const
56+int WindowStateStorage::getStage(const QString &appId, int defaultValue) const
5757{
58− return m_stage.value(appId, unity::shell::application::ApplicationInfoInterface::MainStage);
58+ return m_stage.value(appId, defaultValue);
5959}
6060
6161void WindowStateStorage::clear()
tests/mocks/Utils/windowstatestorage.h+1−1
@@ -48,7 +48,7 @@ public:
4848 Q_INVOKABLE QRect getGeometry(const QString &windowId, const QRect &defaultValue);
4949
5050 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;
5252
5353 // Only in the mock, to easily restore a fresh state
5454 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()
61macro(make_dbus_class NAME INTERFACE)
72 if(${CMAKE_CURRENT_SOURCE_DIR}/interfaces.xml IS_NEWER_THAN ${CMAKE_CURRENT_BINARY_DIR}/${NAME}Adaptor.h)
83 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()
1212
1313
1414include_directories(
15+ SYSTEM
1516 ${Qt5Qml_PRIVATE_INCLUDE_DIRS}
1617 ${Qt5Quick_INCLUDE_DIRS}
1718 ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
1819 ${Qt5V8_PRIVATE_INCLUDE_DIR}
20+ )
21+
22+include_directories(
1923 ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/Dash
2024 ${CMAKE_CURRENT_BINARY_DIR}
2125 )
tests/plugins/Dash/horizontaljournaltest.cpp−3
@@ -23,10 +23,7 @@
2323#include <QStringListModel>
2424#include <QQmlContext>
2525#include <QQmlEngine>
26−#pragma GCC diagnostic push
27−#pragma GCC diagnostic ignored "-pedantic"
2826#include <private/qquickitem_p.h>
29−#pragma GCC diagnostic pop
3027
3128#include "horizontaljournal.h"
3229
tests/plugins/Dash/listviewwithpageheadersectionexternalmodeltest.cpp−3
@@ -22,12 +22,9 @@
2222#include <QSignalSpy>
2323#include <QSortFilterProxyModel>
2424#include <QtTestGui>
25−#pragma GCC diagnostic push
26−#pragma GCC diagnostic ignored "-pedantic"
2725#include <private/qqmllistmodel_p.h>
2826#include <private/qquickanimation_p.h>
2927#include <private/qquickitem_p.h>
30−#pragma GCC diagnostic pop
3128
3229class StringListModel : public QAbstractListModel
3330{
tests/plugins/Dash/listviewwithpageheadersectiontest.cpp−3
@@ -21,12 +21,9 @@
2121#include <QQuickView>
2222#include <QSignalSpy>
2323#include <QtTestGui>
24−#pragma GCC diagnostic push
25−#pragma GCC diagnostic ignored "-pedantic"
2624#include <private/qqmllistmodel_p.h>
2725#include <private/qquickanimation_p.h>
2826#include <private/qquickitem_p.h>
29−#pragma GCC diagnostic pop
3027
3128class ListViewWithPageHeaderTestSection : public QObject
3229{
tests/plugins/Dash/listviewwithpageheadertest.cpp−3
@@ -21,12 +21,9 @@
2121#include <QQuickView>
2222#include <QSignalSpy>
2323#include <QtTestGui>
24−#pragma GCC diagnostic push
25−#pragma GCC diagnostic ignored "-pedantic"
2624#include <private/qqmllistmodel_p.h>
2725#include <private/qquickanimation_p.h>
2826#include <private/qquickitem_p.h>
29−#pragma GCC diagnostic pop
3027
3128#include <limits>
3229
tests/plugins/Dash/organicgridtest.cpp−3
@@ -23,10 +23,7 @@
2323#include <QStringListModel>
2424#include <QQmlContext>
2525#include <QQmlEngine>
26−#pragma GCC diagnostic push
27−#pragma GCC diagnostic ignored "-pedantic"
2826#include <private/qquickitem_p.h>
29−#pragma GCC diagnostic pop
3027
3128#include "organicgrid.h"
3229
tests/plugins/Dash/verticaljournaltest.cpp−3
@@ -23,10 +23,7 @@
2323#include <QStringListModel>
2424#include <QQmlContext>
2525#include <QQmlEngine>
26−#pragma GCC diagnostic push
27−#pragma GCC diagnostic ignored "-pedantic"
2826#include <private/qquickitem_p.h>
29−#pragma GCC diagnostic pop
3027
3128#include "verticaljournal.h"
3229
tests/plugins/Ubuntu/Gestures/CMakeLists.txt+3−2
@@ -4,14 +4,15 @@ pkg_check_modules(UBUNTUGESTURES REQUIRED UbuntuGestures)
44
55# DANGER! DANGER! Using Qt's private API!
66include_directories(
7+ SYSTEM
78 ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
9+ ${UBUNTUGESTURES_INCLUDE_DIRS}
10+ ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
811)
912
1013include_directories(
1114 ${CMAKE_SOURCE_DIR}/plugins/Ubuntu/Gestures
12− ${UBUNTUGESTURES_INCLUDE_DIRS}
1315 ${CMAKE_CURRENT_BINARY_DIR}
14− ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
1516 )
1617
1718export_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+
111include_directories(
212 ${CMAKE_CURRENT_BINARY_DIR}
313 ${CMAKE_SOURCE_DIR}/plugins/Unity/Session
@@ -9,6 +19,17 @@ add_definitions(-DSM_BUSNAME=sessionBus)
919add_definitions(-DSRCDIR="${CMAKE_CURRENT_SOURCE_DIR}")
1020add_definitions(-DSESSION_TESTING)
1121
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+
1233### SessionBackendTest
1334add_executable(sessionbackendtestExec
1435 sessionbackendtest.cpp
@@ -21,6 +42,13 @@ target_link_libraries(sessionbackendtestExec
2142qt5_use_modules(sessionbackendtestExec Test Core Qml DBus)
2243
2344add_unity8_unittest(SessionBackend dbus-test-runner
45+ ENVIRONMENT XDG_SESSION_PATH=/session
2446 ARG_PREFIX "--parameter"
25− ARGS --task $<TARGET_FILE:sessionbackendtestExec>
47+ ARGS
48+ --task $<TARGET_FILE:LightDMServer>
49+ --task-name server
50+ --ignore-return
51+ --task $<TARGET_FILE:sessionbackendtestExec>
52+ --task-name client
53+ --wait-for org.freedesktop.DisplayManager
2654)
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:
6161 QTest::newRow("Reboot") << "RequestReboot" << "RebootRequested(bool)";
6262 QTest::newRow("Shutdown") << "RequestShutdown" << "ShutdownRequested(bool)";
6363 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()
6466 }
6567
6668 void testUnitySessionLogoutRequested() {
@@ -76,7 +78,7 @@ private Q_SLOTS:
7678 QDBusReply<void> reply = dbusUnitySession->call(method);
7779 QCOMPARE(reply.isValid(), true);
7880
79− QCOMPARE(spy.count(), 1);
81+ QTRY_COMPARE(spy.count(), 1);
8082 }
8183
8284 void testGnomeSessionWrapper_data() {
@@ -122,6 +124,18 @@ private Q_SLOTS:
122124 QCOMPARE(spy.count(), 1);
123125 }
124126
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+
125139 void testUserName() {
126140 DBusUnitySessionService dbusUnitySessionService;
127141 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)
1212add_unity8_qmltest(Components DraggingArea)
1313add_unity8_qmltest(Components LazyImage)
1414add_unity8_qmltest(Components Lockscreen)
15+add_unity8_qmltest(Components MediaServices)
1516add_unity8_qmltest(Components ModeSwitchWarningDialog)
1617add_unity8_qmltest(Components PhysicalKeysMapper)
1718add_unity8_qmltest(Components Rating)
1819add_unity8_qmltest(Components ResponsiveGridView)
1920add_unity8_qmltest(Components ResponsiveVerticalJournal)
21+add_unity8_qmltest(Components SharingPicker)
2022add_unity8_qmltest(Components Showable)
2123add_unity8_qmltest(Components VirtualTouchPad)
2224add_unity8_qmltest(Components WallpaperResolver)
@@ -40,6 +42,7 @@ add_unity8_qmltest(Dash/Previews PreviewExpandable)
4042add_unity8_qmltest(Dash/Previews PreviewHeader)
4143add_unity8_qmltest(Dash/Previews PreviewIconActions)
4244add_unity8_qmltest(Dash/Previews PreviewImageGallery)
45+add_unity8_qmltest(Dash/Previews PreviewInlineVideo)
4346add_unity8_qmltest(Dash/Previews PreviewMediaToolbar)
4447add_unity8_qmltest(Dash/Previews PreviewPayments)
4548add_unity8_qmltest(Dash/Previews PreviewProgress)
@@ -47,7 +50,6 @@ add_unity8_qmltest(Dash/Previews PreviewRatingDisplay)
4750add_unity8_qmltest(Dash/Previews PreviewRatingDisplayCreationRanges)
4851add_unity8_qmltest(Dash/Previews PreviewRatingEdit)
4952add_unity8_qmltest(Dash/Previews PreviewRatingInput)
50−add_unity8_qmltest(Dash/Previews PreviewSharing)
5153add_unity8_qmltest(Dash/Previews PreviewTable)
5254add_unity8_qmltest(Dash/Previews PreviewTextSummary)
5355add_unity8_qmltest(Dash/Previews PreviewVideoPlayback)
tests/qmltests/Components/CMakeLists.txt+5−1
@@ -6,8 +6,12 @@ pkg_check_modules(UBUNTUGESTURES REQUIRED UbuntuGestures)
66
77include_directories(
88 ${CMAKE_SOURCE_DIR}/plugins/Ubuntu/Gestures
9− ${UBUNTUGESTURES_INCLUDE_DIRS}
109 ${CMAKE_CURRENT_BINARY_DIR}
10+ )
11+
12+include_directories(
13+ SYSTEM
14+ ${UBUNTUGESTURES_INCLUDE_DIRS}
1115 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
1216 ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
1317 ${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 @@
1717import QtQuick 2.4
1818import QtTest 1.0
1919import Ubuntu.Components 1.3
20−import "../../../../qml/Dash/Previews"
20+import "../../../qml/Components"
2121import Unity.Test 0.1 as UT
2222
2323Rectangle {
@@ -40,33 +40,33 @@ Rectangle {
4040 "content-type": "text"
4141 }
4242
43− PreviewSharing {
44− id: previewSharing
43+ SharingPicker {
44+ id: sharingPicker
4545 anchors { left: parent.left; bottom: parent.bottom; }
4646 shareData: root.shareData
4747 }
4848
4949 UT.UnityTestCase {
50− name: "PreviewSharingTest"
50+ name: "SharingPickerTest"
5151 when: windowShown
5252
53− property Item peerPicker: findChild(previewSharing.rootItem, "peerPicker")
53+ property Item peerPicker: findChild(sharingPicker.rootItem, "peerPicker")
5454
5555 function cleanup() {
5656 peerPicker.visible = false;
5757 }
5858
5959 function test_open_picker() {
60− mouseClick(previewSharing);
60+ sharingPicker.showPeerPicker();
6161 compare(peerPicker.visible, true);
6262 }
6363
6464 function test_createExportedItems() {
65− var exportedItems = previewSharing.createExportedItems(shareData["uri"]);
65+ var exportedItems = sharingPicker.createExportedItems(shareData["uri"]);
6666 for (var i = 0; i < exportedItems.length; i++) {
6767 compare(exportedItems[i].url, shareData["uri"][i]);
6868 }
69− exportedItems = previewSharing.createExportedItems(shareDataString["uri"]);
69+ exportedItems = sharingPicker.createExportedItems(shareDataString["uri"]);
7070 compare(exportedItems[0].url, shareDataString["uri"]);
7171 }
7272 }
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 {
5151 name: "PreviewMediaToolbarTest"
5252 when: windowShown
5353
54− property Item sharingWidget: findChild(previewMediaToolbar, "sharingWidget")
54+ property Item sharingPicker: findChild(previewMediaToolbar, "sharingPicker")
5555
5656 function cleanup() {
5757 previewMediaToolbar.shareData = root.shareData;
@@ -60,10 +60,10 @@ Rectangle {
6060 function test_visible() {
6161 previewMediaToolbar.shareData = shareDataNoUri;
6262 compare(previewMediaToolbar.visible, false);
63− compare(sharingWidget.visible, false);
63+ compare(sharingPicker.visible, false);
6464 previewMediaToolbar.shareData = shareData;
6565 compare(previewMediaToolbar.visible, true);
66− compare(sharingWidget.visible, true);
66+ compare(sharingPicker.visible, true);
6767 }
6868 }
6969}
tests/qmltests/Dash/Previews/tst_PreviewRatingDisplayCreationRanges.qml+51−15
@@ -1,5 +1,5 @@
11/*
2− * Copyright 2014,2015 Canonical Ltd.
2+ * Copyright 2014-2016 Canonical Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -42,6 +42,8 @@ Flickable {
4242 parentFlickable: root
4343 }
4444
45+ property int createdDelegates: 0
46+
4547 Component.onCompleted: {
4648 for (var i = 0; i < 500; ++i) {
4749 widgetData500.reviews.push({ author: "Some dude", review: "Very cool app" + i });
@@ -49,37 +51,68 @@ Flickable {
4951 factory.widgetData = widgetData500;
5052 }
5153
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+
5268 UT.UnityTestCase {
53− name: "PreviewRatingDisplayTest"
69+ name: "PreviewRatingDisplayCreationRangesTest"
5470 when: windowShown
5571
56− function test_creation_speed() {
57− factory.parentFlickable = null;
72+ function initTestCase() {
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
5880 factory.widgetData = reviewsEmpty;
5981 waitForRendering(root);
6082
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
6288 factory.widgetData = widgetData500;
6389 waitForRendering(root);
64− var end = new Date().getTime();
65− var time500 = end - start;
6690
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
6799 factory.widgetData = reviewsEmpty;
68100 waitForRendering(root);
69101
102+ // set parentFlickable to enable the creation ranges code
70103 factory.parentFlickable = root;
71− start = new Date().getTime();
104+ root.createdDelegates = 0;
105+
106+ // Set the 500 reviews data
72107 factory.widgetData = widgetData500;
73108 waitForRendering(root);
74− end = new Date().getTime();
75− var timeRanges500 = end - start;
76109
77− // Measurements show it's usually like 20 times faster
78− // but we set the range at 8 times fast to make sure it's not an unstable test
79− verify(timeRanges500 * 8 < time500);
80− }
110+ // Check we have only created a few delegates
111+ // For some reason xenial and yaketti we get 16 and on vivid 15 so
112+ // settle for <= 20
113+ expectFailContinue("", "Should not create more than 20 delegates when using ranges");
114+ tryCompareFunction(function() { return root.createdDelegates > 20 }, true);
81115
82− function test_check_indexes() {
83116 // Check that item 499 isn't there on startup but if we scroll down
84117 // it will be there
85118 var reviewItem499 = findChild(factory, "reviewItem499");
@@ -89,6 +122,9 @@ Flickable {
89122
90123 var reviewItem499 = findChild(factory, "reviewItem499");
91124 verify(reviewItem499 !== null);
125+
126+ // Check we have created 500 delegates
127+ compare(root.createdDelegates, 500);
92128 }
93129 }
94130}
tests/qmltests/Dash/Previews/tst_PreviewRatingEdit.qml+3
@@ -56,7 +56,9 @@ Rectangle {
5656 verify(!reviewTextArea.visible)
5757
5858 var editButton = findChild(previewRatingEdit, "editButton");
59+ var display = findChild(previewRatingEdit, "display");
5960 mouseClick(editButton);
61+ tryCompare(display, "visible", false);
6062 verify(!authorLabel.visible)
6163 verify(reviewTextArea.visible)
6264
@@ -72,6 +74,7 @@ Rectangle {
7274 var submitButton = findChild(previewRatingEdit, "submitButton")
7375 mouseClick(submitButton);
7476
77+ tryCompare(display, "visible", true);
7578 compare(spy.count, 1);
7679 var args = spy.signalArguments[0];
7780 compare(args[0], previewRatingEdit.widgetId);
tests/qmltests/Dash/Previews/tst_PreviewWidgetFactory.qml+17−16
@@ -75,26 +75,27 @@ Rectangle {
7575
7676 function test_mapping_data() {
7777 return [
78− { tag: "Actions", type: "actions", source: "PreviewActions.qml", expanded: true },
79− { tag: "Audio", type: "audio", source: "PreviewAudioPlayback.qml", expanded: true },
80− { tag: "Comment", type: "comment", source: "PreviewComment.qml", expanded: true },
81− { tag: "Comment Input", type: "comment-input", source: "PreviewCommentInput.qml", expanded: true },
82− { tag: "Expandable", type: "expandable", source: "PreviewExpandable.qml", expanded: false },
83− { tag: "Gallery", type: "gallery", source: "PreviewImageGallery.qml", expanded: true },
84− { tag: "Header", type: "header", source: "PreviewHeader.qml", expanded: true },
85− { tag: "Image", type: "image", source: "PreviewZoomableImage.qml", expanded: true },
86− { tag: "Progress", type: "progress", source: "PreviewProgress.qml", expanded: true },
87− { tag: "Rating Input", type: "rating-input", source: "PreviewRatingInput.qml", expanded: true },
88− { tag: "Rating Display", type: "reviews", source: "PreviewRatingDisplay.qml", expanded: true },
89− { tag: "Table", type: "table", source: "PreviewTable.qml", expanded: true },
90− { tag: "Text", type: "text", source: "PreviewTextSummary.qml", expanded: true },
91− { tag: "Video", type: "video", source: "PreviewVideoPlayback.qml", expanded: true },
78+ { tag: "Actions", data: { type: "actions" }, source: "PreviewActions.qml", expanded: true },
79+ { tag: "Audio", data: { type: "audio" }, source: "PreviewAudioPlayback.qml", expanded: true },
80+ { tag: "Comment", data: { type: "comment" }, source: "PreviewComment.qml", expanded: true },
81+ { tag: "Comment Input", data: { type: "comment-input" }, source: "PreviewCommentInput.qml", expanded: true },
82+ { tag: "Expandable", data: { type: "expandable" }, source: "PreviewExpandable.qml", expanded: false },
83+ { tag: "Gallery", data: { type: "gallery" }, source: "PreviewImageGallery.qml", expanded: true },
84+ { tag: "Header", data: { type: "header" }, source: "PreviewHeader.qml", expanded: true },
85+ { tag: "Image", data: { type: "image" }, source: "PreviewZoomableImage.qml", expanded: true },
86+ { tag: "Progress", data: { type: "progress" }, source: "PreviewProgress.qml", expanded: true },
87+ { tag: "Rating Input", data: { type: "rating-input" }, source: "PreviewRatingInput.qml", expanded: true },
88+ { tag: "Rating Display", data: { type: "reviews" }, source: "PreviewRatingDisplay.qml", expanded: true },
89+ { tag: "Table", data: { type: "table" }, source: "PreviewTable.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 },
92+ { tag: "Video", data: { type: "video", source: "https://demo" }, source: "PreviewVideoPlayback.qml", expanded: true },
9293 ];
9394 }
9495
9596 function test_mapping(data) {
96− factory.widgetData = { type: data.type };
97− factory.widgetType = data.type;
97+ factory.widgetData = data.data;
98+ factory.widgetType = data.data.type;
9899
99100 verify((String(factory.source)).indexOf(data.source) != -1);
100101 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 {
112112 mouseClick(tile);
113113 }
114114
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+
115128 function test_longNavigationFilterList() {
116129 // Select the scope with long navigation
117130 dash.setCurrentScope("LongPrimaryNavigation")
@@ -121,6 +134,7 @@ Item {
121134 var dashContentList = findChild(dashContent, "dashContentList")
122135 var searchButton = findChild(dashContentList.currentItem, "search_button")
123136 var extraPanel = findChild(dashContentList.currentItem, "peExtraPanel")
137+ waitForRendering(extraPanel);
124138 tryCompare(extraPanel, "visible", false)
125139
126140 // Open the primaryNavigationFilter
@@ -179,22 +193,16 @@ Item {
179193 }
180194
181195 function test_manage_dash_clickscope_unfavoritable() {
182− // Show the 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);
196+ open_manage_dash();
191197
192198 // Click scope star area is not visible (i.e. can't be unfavorited)
199+ var favScopesListCategoryList = findChild(findChild(dash, "scopesListCategoryfavorites"), "scopesListCategoryInnerList");
193200 var clickScope = findChild(favScopesListCategoryList, "delegateclickscope");
194201 var starArea = findChild(clickScope, "starArea");
195202 compare(starArea.visible, false);
196203
197204 // Go back
205+ var bottomEdgeController = findInvisibleChild(dash, "bottomEdgeController");
198206 var scopesList = findChild(dash, "scopesList");
199207 var scopesListPageHeader = findChild(scopesList, "pageHeader");
200208 var backButton = findChild(scopesListPageHeader, "innerPageHeader").leadingActionBar;
@@ -203,20 +211,14 @@ Item {
203211 }
204212
205213 function test_manage_dash_select_same_favorite() {
206− // Show the 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);
214+ open_manage_dash();
215215
216216 // Click in first item
217+ var favScopesListCategoryList = findChild(findChild(dash, "scopesListCategoryfavorites"), "scopesListCategoryInnerList");
217218 mouseClick(favScopesListCategoryList.currentItem);
218219
219220 // Make sure animation went back
221+ var bottomEdgeController = findInvisibleChild(dash, "bottomEdgeController");
220222 tryCompare(bottomEdgeController, "progress", 0);
221223
222224 var dashContentList = findChild(dash, "dashContentList");
@@ -224,31 +226,25 @@ Item {
224226 }
225227
226228 function test_manage_dash_select_different_favorite() {
227− // Show the 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);
229+ open_manage_dash();
236230
237231 // Click in second item
232+ var favScopesListCategoryList = findChild(findChild(dash, "scopesListCategoryfavorites"), "scopesListCategoryInnerList");
238233 favScopesListCategoryList.currentIndex = 1;
239234 mouseClick(favScopesListCategoryList.currentItem);
240235
241236 // Make sure animation went back
237+ var bottomEdgeController = findInvisibleChild(dash, "bottomEdgeController");
242238 tryCompare(bottomEdgeController, "progress", 0);
243239 var dashContentList = findChild(dash, "dashContentList");
244240 compare(dashContentList.currentIndex, 1);
245241 }
246242
247243 function test_manage_dash_select_non_favorite() {
248− // Show the manage dash
249− touchFlick(dash, dash.width / 2, dash.height - 1, dash.width / 2, units.gu(2));
250− var bottomEdgeController = findInvisibleChild(dash, "bottomEdgeController");
251− tryCompare(bottomEdgeController, "progress", 1);
244+ open_manage_dash();
245+
246+ // Ensure non favorites are visible
247+ flickToYEnd(findChild(dash,"scopesListFlickable"));
252248
253249 // Make sure stuff is loaded
254250 var nonfavScopesListCategory = findChild(dash, "scopesListCategoryother");
@@ -268,6 +264,7 @@ Item {
268264 tryCompare(dashTempScopeItem, "visible", true);
269265
270266 // Check the manage dash is gone
267+ var bottomEdgeController = findInvisibleChild(dash, "bottomEdgeController");
271268 tryCompare(bottomEdgeController, "progress", 0);
272269
273270 // Go back
@@ -296,6 +293,7 @@ Item {
296293 tryCompare(bottomEdgeController, "progress", 1);
297294
298295 // Go back
296+ var bottomEdgeController = findInvisibleChild(dash, "bottomEdgeController");
299297 var scopesList = findChild(dash, "scopesList");
300298 var scopesListPageHeader = findChild(scopesList, "pageHeader");
301299 var backButton = findChild(scopesListPageHeader, "innerPageHeader").leadingActionBar;
@@ -358,10 +356,7 @@ Item {
358356 }
359357
360358 function test_manage_dash_store_no_favorites() {
361− // Show the 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);
359+ open_manage_dash();
365360
366361 // clear the favorite scopes
367362 scopes.clearFavorites();
@@ -379,6 +374,7 @@ Item {
379374
380375 spy.wait();
381376 compare(spy.signalArguments[0][0], "scope://com.canonical.scopes.clickstore");
377+ var bottomEdgeController = findInvisibleChild(dash, "bottomEdgeController");
382378 tryCompare(bottomEdgeController, "progress", 0);
383379 }
384380
@@ -387,17 +383,10 @@ Item {
387383 compare(dashContentList.currentIndex, 0);
388384 compare(dashContentList.currentItem.scopeId, "MockScope1");
389385
390− // Show the 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);
386+ open_manage_dash();
399387
400388 // Enter edit mode
389+ var favScopesListCategoryList = findChild(findChild(dash, "scopesListCategoryfavorites"), "scopesListCategoryInnerList");
401390 var scopesList = findChild(dash, "scopesList");
402391 var clickScope = findChild(favScopesListCategoryList, "delegateclickscope");
403392 mousePress(clickScope);
@@ -408,6 +397,7 @@ Item {
408397 touchFlick(starArea, 0, 0, 0, -units.gu(10));
409398
410399 // Exit edit mode and go back
400+ var bottomEdgeController = findInvisibleChild(dash, "bottomEdgeController");
411401 var scopesList = findChild(dash, "scopesList");
412402 var scopesListPageHeader = findChild(scopesList, "pageHeader");
413403 var backButton = findChild(scopesListPageHeader, "innerPageHeader").leadingActionBar;
@@ -429,17 +419,10 @@ Item {
429419 compare(dashContentList.currentIndex, 0);
430420 compare(dashContentList.currentItem.scopeId, "MockScope1");
431421
432− // Show the 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);
422+ open_manage_dash();
441423
442424 // Enter edit mode
425+ var favScopesListCategoryList = findChild(findChild(dash, "scopesListCategoryfavorites"), "scopesListCategoryInnerList");
443426 var scopesList = findChild(dash, "scopesList");
444427 var clickScope = findChild(favScopesListCategoryList, "delegateclickscope");
445428 mousePress(clickScope);
@@ -459,6 +442,8 @@ Item {
459442 mouseClick(backButton);
460443
461444 // Click on third scope
445+ var favScopesListCategoryList = findChild(findChild(dash, "scopesListCategoryfavorites"), "scopesListCategoryInnerList");
446+ var bottomEdgeController = findInvisibleChild(dash, "bottomEdgeController");
462447 var mockScope5 = findChild(favScopesListCategoryList, "delegateMockScope5");
463448 waitForRendering(mockScope5)
464449 mouseClick(mockScope5);
@@ -472,14 +457,12 @@ Item {
472457 compare(dashContentList.currentIndex, 0);
473458 compare(dashContentList.currentItem.scopeId, "MockScope1");
474459
475− // Show the 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);
460+ open_manage_dash();
479461
480462 var dashCommunicatorService = findInvisibleChild(dash, "dashCommunicatorService");
481463 dashCommunicatorService.mockSetCurrentScope(1, true, false);
482464
465+ var bottomEdgeController = findInvisibleChild(dash, "bottomEdgeController");
483466 tryCompare(bottomEdgeController, "progress", 0);
484467 tryCompare(dashContentList, "currentIndex", 1)
485468 }
@@ -507,10 +490,10 @@ Item {
507490 }
508491
509492 function test_close_temp_scope_preview_opening_scope() {
510− // Show the manage dash
511− touchFlick(dash, dash.width / 2, dash.height - 1, dash.width / 2, units.gu(2));
512− var bottomEdgeController = findInvisibleChild(dash, "bottomEdgeController");
513− tryCompare(bottomEdgeController, "progress", 1);
493+ open_manage_dash();
494+
495+ // Ensure non favorite scopes are visible
496+ flickToYEnd(findChild(dash, "scopesListFlickable"));
514497
515498 // Make sure stuff is loaded
516499 var nonfavScopesListCategory = findChild(dash, "scopesListCategoryother");
@@ -530,6 +513,7 @@ Item {
530513 tryCompare(dashTempScopeItem, "visible", true);
531514
532515 // Check the manage dash is gone
516+ var bottomEdgeController = findInvisibleChild(dash, "bottomEdgeController");
533517 tryCompare(bottomEdgeController, "progress", 0);
534518
535519 // Open preview
@@ -580,12 +564,10 @@ Item {
580564 UriHandler.opened("scopes://clickscope");
581565 tryCompare(dashContentList, "currentIndex", 1);
582566
583− // Show the 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);
567+ open_manage_dash();
587568
588569 // UriHandler changes to a scope and closes the manage scopes
570+ var bottomEdgeController = findInvisibleChild(dash, "bottomEdgeController");
589571 UriHandler.opened("scopes://MockScope1");
590572 tryCompare(dashContentList, "currentIndex", 0);
591573 tryCompare(bottomEdgeController, "progress", 0);
@@ -603,11 +585,11 @@ Item {
603585 tryCompare(dashContent, "subPageShown", false);
604586
605587 // Go to a temp scope
606− touchFlick(dash, dash.width / 2, dash.height - 1, dash.width / 2, units.gu(2));
607− var bottomEdgeController = findInvisibleChild(dash, "bottomEdgeController");
608− tryCompare(bottomEdgeController, "progress", 1);
588+ open_manage_dash();
589+
609590 var nonfavScopesListCategory = findChild(dash, "scopesListCategoryother");
610591 var nonfavScopesListCategoryList = findChild(nonfavScopesListCategory, "scopesListCategoryInnerList");
592+ flickToYEnd(findChild(dash, "scopesListFlickable"));
611593 tryCompare(nonfavScopesListCategoryList, "currentIndex", 0);
612594 mouseClick(nonfavScopesListCategoryList.currentItem);
613595 var dashTempScopeItem = findChild(dash, "dashTempScopeItem");
@@ -666,12 +648,15 @@ Item {
666648 function test_tempScopeItemXOnResize()
667649 {
668650 // Go to a temp scope
669− touchFlick(dash, dash.width / 2, dash.height - 1, dash.width / 2, units.gu(2));
670− var bottomEdgeController = findInvisibleChild(dash, "bottomEdgeController");
671− tryCompare(bottomEdgeController, "progress", 1);
651+ open_manage_dash();
652+
672653 var nonfavScopesListCategory = findChild(dash, "scopesListCategoryother");
673654 var nonfavScopesListCategoryList = findChild(nonfavScopesListCategory, "scopesListCategoryInnerList");
655+ var scopesListFlickable = findChild(dash, "scopesListFlickable");
674656 tryCompare(nonfavScopesListCategoryList, "currentIndex", 0);
657+
658+ // Ensure the non-favorites, located at the bottom, are visible
659+ flickToYEnd(scopesListFlickable);
675660 mouseClick(nonfavScopesListCategoryList.currentItem);
676661 var dashTempScopeItem = findChild(dash, "dashTempScopeItem");
677662 tryCompare(dashTempScopeItem, "x", 0);
tests/qmltests/Dash/tst_DashContent.qml+7−1
@@ -70,7 +70,7 @@ Item {
7070 function loadScopes() {
7171 scopeLoadedSpy.clear();
7272 scopesModel.load();
73− tryCompare(scopeLoadedSpy, "count", 6, 15000);
73+ tryCompare(scopeLoadedSpy, "count", 8, 15000);
7474 tryCompare(scopesModel, "loaded", true);
7575 tryCompareFunction(function() {
7676 var mockScope1Loader = findChild(shell, "scopeLoader0");
@@ -610,6 +610,12 @@ Item {
610610 }
611611
612612 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+
613619 // Our cards are of type AbstractButton as defined in CardCreator.js
614620 // This gives also other things that are not cards but for our purpose it
615621 // does not matter
tests/qmltests/Dash/tst_GenericScopeView.qml+7
@@ -76,6 +76,7 @@ Item {
7676 tryCompare(scopes, "loaded", true);
7777
7878 genericScopeView.scope = scopes.getScope(2);
79+ genericScopeView.isCurrent = true;
7980 shell.width = units.gu(120);
8081 genericScopeView.categoryView.positionAtBeginning();
8182 waitForRendering(genericScopeView.categoryView);
@@ -116,6 +117,7 @@ Item {
116117 }
117118
118119 function test_isActive() {
120+ genericScopeView.isCurrent = false
119121 tryCompare(genericScopeView.scope, "isActive", false)
120122 genericScopeView.isCurrent = true
121123 tryCompare(genericScopeView.scope, "isActive", true)
@@ -140,6 +142,7 @@ Item {
140142 }
141143
142144 function test_searchQuery() {
145+ genericScopeView.isCurrent = false
143146 genericScopeView.scope = scopes.getScope(0);
144147 genericScopeView.scope.searchQuery = "test";
145148 genericScopeView.scope = scopes.getScope(1);
@@ -151,6 +154,7 @@ Item {
151154 }
152155
153156 function test_changeScope() {
157+ genericScopeView.isCurrent = false;
154158 genericScopeView.scope.searchQuery = "test"
155159 var originalScopeId = genericScopeView.scope.id;
156160 genericScopeView.scope = scopes.getScope(originalScopeId + 1)
@@ -458,6 +462,7 @@ Item {
458462 function test_seeAllTwoCategoriesScenario1() {
459463 mockScope.setId("mockScope");
460464 mockScope.setName("Mock Scope");
465+ mockScope.isActive = true;
461466 mockScope.categories.setCount(2);
462467 mockScope.categories.resultModel(0).setResultCount(50);
463468 mockScope.categories.resultModel(1).setResultCount(25);
@@ -501,6 +506,7 @@ Item {
501506 function test_seeAllTwoCategoriesScenario2() {
502507 mockScope.setId("mockScope");
503508 mockScope.setName("Mock Scope");
509+ mockScope.isActive = true;
504510 mockScope.categories.setCount(2);
505511 mockScope.categories.resultModel(0).setResultCount(25);
506512 mockScope.categories.resultModel(1).setResultCount(50);
@@ -629,6 +635,7 @@ Item {
629635 var category0 = findChild(categoryListView, "dashCategory0");
630636 waitForRendering(category0);
631637
638+ tryCompareFunction(function() { return category0.item != null; }, true);
632639 var cardGrid = category0.item;
633640 compare(cardGrid.cardTool.template["non-interactive"], true);
634641
tests/qmltests/Launcher/tst_Launcher.qml+23−3
@@ -1,5 +1,5 @@
11/*
2− * Copyright 2013-2014 Canonical Ltd.
2+ * Copyright 2013-2016 Canonical Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -210,6 +210,11 @@ Rectangle {
210210 target: LauncherModel
211211 }
212212
213+ SignalSpy {
214+ id: clickThroughSpy
215+ target: clickThroughTester
216+ }
217+
213218 Item {
214219 id: fakeDismissTimer
215220 property bool running: false
@@ -231,6 +236,8 @@ Rectangle {
231236
232237 property Item launcher: launcherLoader.status === Loader.Ready ? launcherLoader.item : null
233238 function cleanup() {
239+ signalSpy.clear();
240+ clickThroughSpy.clear();
234241 launcherLoader.active = false;
235242 // Loader.status might be Loader.Null and Loader.item might be null but the Loader
236243 // item might still be alive. So if we set Loader.active back to true
@@ -925,7 +932,6 @@ Rectangle {
925932
926933 var quickListEntry = findChild(quickList, "quickListEntry" + data.index)
927934
928− signalSpy.clear();
929935 signalSpy.signalName = "quickListTriggered"
930936
931937 mouseClick(quickListEntry)
@@ -1218,7 +1224,6 @@ Rectangle {
12181224 launcher.openForKeyboardNavigation();
12191225 waitForRendering(launcher);
12201226
1221− signalSpy.clear();
12221227 signalSpy.signalName = "quickListTriggered"
12231228
12241229 keyClick(Qt.Key_Down); // Down to launcher item 0
@@ -1311,5 +1316,20 @@ Rectangle {
13111316 compare(surfacePipRepeater.model, Math.min(3, LauncherModel.get(i).surfaceCount))
13121317 }
13131318 }
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+ }
13141334 }
13151335}
tests/qmltests/Panel/tst_IndicatorsMenu.qml+29−1
@@ -1,5 +1,5 @@
11/*
2− * Copyright 2013-2014 Canonical Ltd.
2+ * Copyright 2013-2016 Canonical Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -27,6 +27,16 @@ IndicatorTest {
2727 width: units.gu(80)
2828 height: units.gu(71)
2929
30+ SignalSpy {
31+ id: clickThroughSpy
32+ target: clickThroughTester
33+ }
34+
35+ MouseArea {
36+ id: clickThroughTester
37+ anchors.fill: root
38+ }
39+
3040 RowLayout {
3141 anchors.fill: parent
3242 anchors.margins: units.gu(1)
@@ -112,6 +122,10 @@ IndicatorTest {
112122 indicatorsMenu.verticalVelocityThreshold = 0.5
113123 }
114124
125+ function cleanup() {
126+ clickThroughSpy.clear();
127+ }
128+
115129 function get_indicator_item(index) {
116130 var indicatorItem = findChild(indicatorsMenu, root.originalModelData[index]["identifier"] + "-panelItem");
117131 verify(indicatorItem !== null);
@@ -257,5 +271,19 @@ IndicatorTest {
257271
258272 touchRelease(indicatorsMenu, nextItemMappedPositionX, indicatorsMenu.openedHeight / 3);
259273 }
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+ }
260288 }
261289}
tests/qmltests/Stages/tst_DesktopStage.qml+135
@@ -89,6 +89,13 @@ Item {
8989 topLevelSurfaceList: topSurfaceList
9090 }
9191 }
92+
93+ MouseArea {
94+ id: clickThroughTester
95+ anchors.fill: desktopStageLoader.item
96+ acceptedButtons: Qt.AllButtons
97+ hoverEnabled: true
98+ }
9299 }
93100
94101 Rectangle {
@@ -140,6 +147,11 @@ Item {
140147 }
141148 }
142149
150+ SignalSpy {
151+ id: mouseEaterSpy
152+ target: clickThroughTester
153+ }
154+
143155 UnityTestCase {
144156 id: testCase
145157 name: "DesktopStage"
@@ -179,6 +191,7 @@ Item {
179191 desktopStageLoader.active = true;
180192 tryCompare(desktopStageLoader, "status", Loader.Ready);
181193
194+ mouseEaterSpy.clear();
182195 }
183196
184197 function waitUntilAppSurfaceShowsUp(surfaceId) {
@@ -504,6 +517,33 @@ Item {
504517 tryCompare(MirFocusController, "focusedSurface", null); // verify still no surface is focused
505518 }
506519
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+
507547 function test_minimizeApplicationHidesSurface() {
508548 compare(topSurfaceList.applicationAt(0).appId, "unity8-dash");
509549 var dashSurface = topSurfaceList.surfaceAt(0);
@@ -677,5 +717,100 @@ Item {
677717 var closeButton = findChild(dashAppDelegate, "closeWindowButton");
678718 tryCompare(closeButton, "visible", false);
679719 }
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+ }
680815 }
681816}
tests/qmltests/Stages/tst_TabletStage.qml+162−35
@@ -242,6 +242,48 @@ Rectangle {
242242 appWindow.width * 0.1, -appWindow.height / 2);
243243 }
244244
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+
245287 function test_tappingSwitchesFocusBetweenStages() {
246288 WindowStateStorage.saveStage(dialerCheckBox.appId, ApplicationInfoInterface.SideStage)
247289
@@ -493,14 +535,36 @@ Rectangle {
493535 function() { return sideStage.shown; });
494536 }
495537
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() {
497561 return [
498562 { tag: "MainStage", stage: ApplicationInfoInterface.MainStage, mainStageAppId: "webbrowser-app", sideStageAppId: ""},
499563 { tag: "SideStage", stage: ApplicationInfoInterface.SideStage, mainStageAppId: "unity8-dash", sideStageAppId: "webbrowser-app" },
500564 ];
501565 }
502566
503− function test_applicationLoadsInCorrectStage(data) {
567+ function test_applicationLoadsInSavedStage(data) {
504568 WindowStateStorage.saveStage(webbrowserCheckBox.appId, data.stage)
505569
506570 var stagesPriv = findInvisibleChild(tabletStage, "stagesPriv");
@@ -519,13 +583,13 @@ Rectangle {
519583
520584 function test_applicationSavesLastStage_data() {
521585 return [
522− { tag: "MainStage", stage: ApplicationInfoInterface.MainStage},
523− { tag: "SideStage", stage: ApplicationInfoInterface.SideStage},
586+ { tag: "MainStage", fromStage: ApplicationInfoInterface.MainStage, toStage: ApplicationInfoInterface.SideStage },
587+ { tag: "SideStage", fromStage: ApplicationInfoInterface.SideStage, toStage: ApplicationInfoInterface.MainStage },
524588 ];
525589 }
526590
527591 function test_applicationSavesLastStage(data) {
528− WindowStateStorage.saveStage(webbrowserCheckBox.appId, data.stage);
592+ WindowStateStorage.saveStage(webbrowserCheckBox.appId, data.fromStage);
529593 stageSaver.clear();
530594
531595 var stagesPriv = findInvisibleChild(tabletStage, "stagesPriv");
@@ -538,11 +602,15 @@ Rectangle {
538602 webbrowserCheckBox.checked = true;
539603 waitUntilAppSurfaceShowsUp(webbrowserSurfaceId);
540604
541− webbrowserCheckBox.checked = false;
605+ if (data.toStage === ApplicationInfoInterface.SideStage) {
606+ dragToSideStage(webbrowserSurfaceId);
607+ } else {
608+ dragToMainStage(webbrowserSurfaceId);
609+ }
542610
543611 tryCompare(stageSaver, "count", 1);
544612 compare(stageSaver.signalArguments[0][0], "webbrowser-app")
545− compare(stageSaver.signalArguments[0][1], data.stage)
613+ compare(stageSaver.signalArguments[0][1], data.toStage)
546614 }
547615
548616 function test_loadSideStageByDraggingFromMainStage() {
@@ -555,21 +623,10 @@ Rectangle {
555623 verify(appDelegate);
556624 compare(appDelegate.stage, ApplicationInfoInterface.MainStage);
557625
558− var pos = tabletStage.width - sideStage.width - (tabletStage.width - sideStage.width) / 2;
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− });
626+ dragToSideStage(webbrowserSurfaceId);
572627
628+ var spreadView = findChild(tabletStageLoader, "spreadView")
629+ tryCompare(spreadView, "surfaceDragging", false);
573630 tryCompare(appDelegate, "stage", ApplicationInfoInterface.SideStage);
574631 }
575632
@@ -584,20 +641,10 @@ Rectangle {
584641 verify(appDelegate);
585642 compare(appDelegate.stage, ApplicationInfoInterface.SideStage);
586643
587− var pos = tabletStage.width - sideStage.width / 2;
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− });
644+ dragToMainStage(webbrowserSurfaceId);
600645
646+ var spreadView = findChild(tabletStageLoader, "spreadView")
647+ tryCompare(spreadView, "surfaceDragging", false);
601648 tryCompare(appDelegate, "stage", ApplicationInfoInterface.MainStage);
602649 }
603650
@@ -737,6 +784,22 @@ Rectangle {
737784
738785 tryCompare(appDelegate.surface, "activeFocus", true);
739786
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+
740803 var pos = tabletStage.width - sideStage.width - (tabletStage.width - sideStage.width) / 2;
741804 var end_pos = tabletStage.width - sideStage.width / 2;
742805
@@ -752,7 +815,71 @@ Rectangle {
752815 pos >= end_pos;
753816 });
754817
755− tryCompare(appDelegate.surface, "activeFocus", true);
818+ var spreadView = findChild(tabletStageLoader, "spreadView")
819+ tryCompare(spreadView, "surfaceDragging", false);
820+ tryCompare(appDelegate, "stage", ApplicationInfoInterface.MainStage);
756821 }
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+
757884 }
758885}
tests/qmltests/Stages/tst_WindowResizeArea.qml+36−34
@@ -44,8 +44,10 @@ Item {
4444 id: fakeWindow
4545 property alias resizeAreaMinWidth: windowResizeArea.minWidth
4646 property alias resizeAreaMinHeight: windowResizeArea.minHeight
47− x: units.gu(20)
48− y: units.gu(20)
47+ x: requestedX
48+ y: requestedY
49+ property real requestedX: units.gu(20)
50+ property real requestedY: units.gu(20)
4951 width: requestedWidth
5052 height: requestedHeight
5153 property real requestedWidth
@@ -226,8 +228,8 @@ Item {
226228 property var fakeWindow: windowLoader.item
227229
228230 function init() {
229− fakeWindow.x = units.gu(20)
230− fakeWindow.y = units.gu(20)
231+ fakeWindow.requestedX = units.gu(20)
232+ fakeWindow.requestedY = units.gu(20)
231233 fakeWindow.requestedWidth = units.gu(20)
232234 fakeWindow.requestedHeight = units.gu(20)
233235 fakeWindow.resizeAreaMinWidth = units.gu(15);
@@ -254,8 +256,8 @@ Item {
254256 }
255257
256258 function test_resizeWindowRightBottom(data) {
257− var initialWindowX = fakeWindow.x;
258− var initialWindowY = fakeWindow.y;
259+ var initialWindowX = fakeWindow.requestedX;
260+ var initialWindowY = fakeWindow.requestedY;
259261 var initialWindowWidth = fakeWindow.width
260262 var initialWindowHeight = fakeWindow.height
261263
@@ -266,8 +268,8 @@ Item {
266268 tryCompare(fakeWindow, "width", Math.max(initialWindowWidth + data.dx, fakeWindow.resizeAreaMinWidth));
267269 tryCompare(fakeWindow, "height", Math.max(initialWindowHeight + data.dy, fakeWindow.resizeAreaMinHeight));
268270
269− compare(fakeWindow.x, initialWindowX);
270− compare(fakeWindow.y, initialWindowY);
271+ compare(fakeWindow.requestedX, initialWindowX);
272+ compare(fakeWindow.requestedY, initialWindowY);
271273 }
272274
273275 function test_resizeWindowLeftTop_data() {
@@ -280,8 +282,8 @@ Item {
280282 }
281283
282284 function test_resizeWindowLeftTop(data) {
283− var initialWindowX = fakeWindow.x;
284− var initialWindowY = fakeWindow.y;
285+ var initialWindowX = fakeWindow.requestedX;
286+ var initialWindowY = fakeWindow.requestedY;
285287 var initialWindowWidth = fakeWindow.width
286288 var initialWindowHeight = fakeWindow.height
287289
@@ -294,13 +296,13 @@ Item {
294296
295297 var maxMoveX = initialWindowWidth - fakeWindow.resizeAreaMinWidth;
296298 var maxMoveY = initialWindowHeight - fakeWindow.resizeAreaMinHeight;
297− compare(fakeWindow.x, Math.min(initialWindowX + data.dx, initialWindowX + maxMoveX));
298− compare(fakeWindow.y, Math.min(initialWindowY + data.dy, initialWindowY + maxMoveY));
299+ compare(fakeWindow.requestedX, Math.min(initialWindowX + data.dx, initialWindowX + maxMoveX));
300+ compare(fakeWindow.requestedY, Math.min(initialWindowY + data.dy, initialWindowY + maxMoveY));
299301 }
300302
301303 function test_saveRestoreSize() {
302− var initialWindowX = fakeWindow.x;
303− var initialWindowY = fakeWindow.y;
304+ var initialWindowX = fakeWindow.requestedX;
305+ var initialWindowY = fakeWindow.requestedY;
304306 var initialWindowWidth = fakeWindow.width
305307 var initialWindowHeight = fakeWindow.height
306308
@@ -332,8 +334,8 @@ Item {
332334 }
333335
334336 function test_resizeSmallerAndLarger(data) {
335− var initialWindowX = fakeWindow.x;
336− var initialWindowY = fakeWindow.y;
337+ var initialWindowX = fakeWindow.requestedX;
338+ var initialWindowY = fakeWindow.requestedY;
337339 var initialWindowWidth = fakeWindow.width
338340 var initialWindowHeight = fakeWindow.height
339341
@@ -348,18 +350,18 @@ Item {
348350 }
349351
350352 function test_saveRestoreMaximized() {
351− var initialWindowX = fakeWindow.x;
352− var initialWindowY = fakeWindow.y;
353+ var initialWindowX = fakeWindow.requestedX;
354+ var initialWindowY = fakeWindow.requestedY;
353355
354356 var moveDelta = units.gu(5);
355357
356− fakeWindow.x = initialWindowX + moveDelta
357− fakeWindow.y = initialWindowY + moveDelta
358+ fakeWindow.requestedX = initialWindowX + moveDelta
359+ fakeWindow.requestedY = initialWindowY + moveDelta
358360
359361 // Now change the state to maximized. The window should not keep updating the stored values
360362 fakeWindow.maximize()
361− fakeWindow.x = 31415 // 0 is too risky to pass the test even when broken
362− fakeWindow.y = 31415
363+ fakeWindow.requestedX = 31415 // 0 is too risky to pass the test even when broken
364+ fakeWindow.requestedY = 31415
363365
364366 // This will destroy the window and recreate it
365367 windowLoader.active = false;
@@ -389,8 +391,8 @@ Item {
389391 }
390392
391393 function test_restoreMovesIntoBounds(data) {
392− fakeWindow.x = data.x;
393− fakeWindow.y = data.y;
394+ fakeWindow.requestedX = data.x;
395+ fakeWindow.requestedY = data.y;
394396 fakeWindow.width = data.w;
395397 fakeWindow.height = data.h;
396398 waitForRendering(root);
@@ -402,10 +404,10 @@ Item {
402404 waitForRendering(root)
403405
404406 // Make sure it's again where we left it in normal state before destroying
405− compare(fakeWindow.x >= 0, true)
406− compare(fakeWindow.y >= PanelState.panelHeight, true)
407− compare(fakeWindow.x + fakeWindow.width <= root.width, true)
408− compare(fakeWindow.y + fakeWindow.height <= root.height, true)
407+ compare(fakeWindow.requestedX >= 0, true)
408+ compare(fakeWindow.requestedY >= PanelState.panelHeight, true)
409+ compare(fakeWindow.requestedX + fakeWindow.width <= root.width, true)
410+ compare(fakeWindow.requestedY + fakeWindow.height <= root.height, true)
409411
410412 waitForRendering(root)
411413 }
@@ -417,8 +419,8 @@ Item {
417419 and height) increment value.
418420 */
419421 function test_sizeIncrement() {
420− var initialWindowX = fakeWindow.x;
421− var initialWindowY = fakeWindow.y;
422+ var initialWindowX = fakeWindow.requestedX;
423+ var initialWindowY = fakeWindow.requestedY;
422424 var initialWindowWidth = fakeWindow.width
423425 var initialWindowHeight = fakeWindow.height
424426
@@ -448,8 +450,8 @@ Item {
448450 Tests that when dragging a window border you cannot make it bigger than its maximum size
449451 */
450452 function test_maximumSize() {
451− fakeWindow.x = units.gu(1);
452− fakeWindow.y = units.gu(1);
453+ fakeWindow.requestedX = units.gu(1);
454+ fakeWindow.requestedY = units.gu(1);
453455 fakeWindow.resizeAreaMinWidth = 1; // so it does not interfere with anything
454456 fakeWindow.resizeAreaMinHeight = 1; // so it does not interfere with anything
455457 fakeWindow.requestedWidth = units.gu(10);
@@ -478,8 +480,8 @@ Item {
478480 Tests that when dragging a window border you cannot make it smaller than its minimum size
479481 */
480482 function test_minimumSize() {
481− fakeWindow.x = units.gu(1);
482− fakeWindow.y = units.gu(1);
483+ fakeWindow.requestedX = units.gu(1);
484+ fakeWindow.requestedY = units.gu(1);
483485 fakeWindow.resizeAreaMinWidth = 1; // so it does not interfere with anything
484486 fakeWindow.resizeAreaMinHeight = 1; // so it does not interfere with anything
485487 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 {
503503 function test_appSupportingOnlyPrimaryOrientationMakesPhoneShellStayPut() {
504504 loadShell("mako");
505505
506− var spreadRepeater = findChild(shell, "spreadRepeater");
507− verify(spreadRepeater);
508−
509− // unity8-dash supports only primary orientation and should be already running
510− compare(spreadRepeater.count, 1);
511− var dashDelegate = spreadRepeater.itemAt(0);
512− var dashApp = dashDelegate.application;
513− verify(dashApp);
514− compare(dashApp.appId, "unity8-dash");
515− var dashAppWindow = findChild(dashDelegate, "appWindow");
516− verify(dashAppWindow);
517− compare(dashDelegate.focus, true);
518− compare(dashApp.rotatesWindowContents, false);
519− compare(dashApp.supportedOrientations, Qt.PrimaryOrientation);
520− compare(dashApp.stage, ApplicationInfoInterface.MainStage);
521−
522− tryCompareFunction(function(){return dashDelegate.surface != null;}, true);
523− verify(checkAppSurfaceOrientation(dashAppWindow, dashApp, root.primaryOrientationAngle));
506+ var primarySurfaceId = topLevelSurfaceList.nextId;
507+ var primaryApp = ApplicationManager.startApplication("primary-oriented-app");
508+ verify(primaryApp);
509+ waitUntilAppWindowIsFullyLoaded(primarySurfaceId);
510+
511+ var primaryAppWindow = findAppWindowForSurfaceId(primarySurfaceId);
512+ verify(primaryAppWindow)
513+ var primaryDelegate = findChild(shell, "spreadDelegate_" + primarySurfaceId);
514+
515+ compare(primaryDelegate.focus, true);
516+ compare(primaryApp.rotatesWindowContents, false);
517+ compare(primaryApp.supportedOrientations, Qt.PrimaryOrientation);
518+ compare(primaryApp.stage, ApplicationInfoInterface.MainStage);
519+
520+ tryCompareFunction(function(){return primaryDelegate.surface != null;}, true);
521+ verify(checkAppSurfaceOrientation(primaryAppWindow, primaryApp, root.primaryOrientationAngle));
524522
525523 compare(shell.transformRotationAngle, root.primaryOrientationAngle);
526524 rotateTo(90);
527525
528− verify(checkAppSurfaceOrientation(dashAppWindow, dashApp, root.primaryOrientationAngle));
526+ verify(checkAppSurfaceOrientation(primaryAppWindow, primaryApp, root.primaryOrientationAngle));
529527 compare(shell.transformRotationAngle, root.primaryOrientationAngle);
530528
531529 rotateTo(180);
532530
533− verify(checkAppSurfaceOrientation(dashAppWindow, dashApp, root.primaryOrientationAngle));
531+ verify(checkAppSurfaceOrientation(primaryAppWindow, primaryApp, root.primaryOrientationAngle));
534532 compare(shell.transformRotationAngle, root.primaryOrientationAngle);
535533
536534 rotateTo(270);
537535
538− verify(checkAppSurfaceOrientation(dashAppWindow, dashApp, root.primaryOrientationAngle));
536+ verify(checkAppSurfaceOrientation(primaryAppWindow, primaryApp, root.primaryOrientationAngle));
539537 compare(shell.transformRotationAngle, root.primaryOrientationAngle);
540538 }
541539
@@ -548,37 +546,39 @@ Rectangle {
548546 function test_appSupportingOnlyPrimaryOrientationWillOnlyRotateInLandscape(data) {
549547 loadShell(data.deviceName);
550548
551− compare(topLevelSurfaceList.applicationAt(0).appId, "unity8-dash");
552− var dashSurfaceId = topLevelSurfaceList.idAt(0);
553− var dashAppWindow = findAppWindowForSurfaceId(dashSurfaceId);
549+ var primarySurfaceId = topLevelSurfaceList.nextId;
550+ var primaryApp = ApplicationManager.startApplication("primary-oriented-app");
551+ verify(primaryApp);
552+ waitUntilAppWindowIsFullyLoaded(primarySurfaceId);
554553
555− // unity8-dash supports only primary orientation and should be already running
554+ var primaryAppWindow = findAppWindowForSurfaceId(primarySurfaceId);
555+ verify(primaryAppWindow)
556556
557− compare(ApplicationManager.focusedApplicationId, "unity8-dash");
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);
557+ // primary-oriented-app supports only primary orientation
563558
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);
565563
566− tryCompareFunction(function(){return checkAppSurfaceOrientation(dashAppWindow, dashApp, root.primaryOrientationAngle)}, true);
564+ tryCompareFunction(function(){return primaryApp.surfaceList.count > 0;}, true);
565+
566+ tryCompareFunction(function(){return checkAppSurfaceOrientation(primaryAppWindow, primaryApp, root.primaryOrientationAngle)}, true);
567567 compare(shell.transformRotationAngle, root.primaryOrientationAngle);
568568
569569 rotateTo(90);
570570
571− tryCompareFunction(function(){return checkAppSurfaceOrientation(dashAppWindow, dashApp, root.primaryOrientationAngle)}, true);
571+ tryCompareFunction(function(){return checkAppSurfaceOrientation(primaryAppWindow, primaryApp, root.primaryOrientationAngle)}, true);
572572 compare(shell.transformRotationAngle, root.primaryOrientationAngle);
573573
574574 rotateTo(180);
575575
576− tryCompareFunction(function(){return checkAppSurfaceOrientation(dashAppWindow, dashApp, root.primaryOrientationAngle + 180)}, true);
576+ tryCompareFunction(function(){return checkAppSurfaceOrientation(primaryAppWindow, primaryApp, root.primaryOrientationAngle + 180)}, true);
577577 compare(shell.transformRotationAngle, root.primaryOrientationAngle + 180);
578578
579579 rotateTo(270);
580580
581− tryCompareFunction(function(){return checkAppSurfaceOrientation(dashAppWindow, dashApp, root.primaryOrientationAngle + 180)}, true);
581+ tryCompareFunction(function(){return checkAppSurfaceOrientation(primaryAppWindow, primaryApp, root.primaryOrientationAngle + 180)}, true);
582582 compare(shell.transformRotationAngle, root.primaryOrientationAngle + 180);
583583 }
584584
@@ -816,12 +816,13 @@ Rectangle {
816816 }
817817
818818 /*
819+ - launch an app that only supports the primary orientation
819820 - launch an app that supports all orientations, such as twitter-webapp
820821 - wait a bit until that app is considered to have finished initializing and is thus
821822 ready to get resized/rotated
822− - switch back to dash
823+ - switch back to previous app (only supporting primary
823824 - 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
825826 doesn't support such orientation
826827 - do a long right-edge drag to show the apps spread
827828 - tap on twitter-webapp
@@ -838,6 +839,13 @@ Rectangle {
838839 */
839840 function test_greeterStaysAwayAfterRotation() {
840841 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+
841849 var twitterSurfaceId = topLevelSurfaceList.nextId;
842850 var twitterApp = ApplicationManager.startApplication("twitter-webapp");
843851 verify(twitterApp);
@@ -850,12 +858,11 @@ Rectangle {
850858 waitUntilAppWindowIsFullyLoaded(twitterSurfaceId);
851859 waitUntilAppWindowCanRotate(twitterSurfaceId);
852860
853− // go back to unity8-dash
861+ // go back to primary-oriented-app
854862 performEdgeSwipeToSwitchToPreviousApp();
855863
856864 rotateTo(90);
857865 wait(1); // spin the event loop to let all bindings do their thing
858− // should not rotat as unity8-dash doesn't support it
859866 tryCompare(shell, "transformRotationAngle", 0);
860867
861868 performEdgeSwipeToShowAppSpread();
@@ -1257,7 +1264,7 @@ Rectangle {
12571264 Regression test for https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1476757
12581265
12591266 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)
12611268 2- launch or switch to some other application
12621269 3- right-edge swipe to show the apps spread
12631270 4- swipe up to close the current app (the one from step 2)
@@ -1275,8 +1282,10 @@ Rectangle {
12751282 function test_lockPhoneAfterClosingAppInSpreadThenUnlockAndRotate() {
12761283 loadShell("mako");
12771284
1278− compare(topLevelSurfaceList.applicationAt(0).appId, "unity8-dash");
1279− var dashSurfaceId = topLevelSurfaceList.idAt(0);
1285+ var primarySurfaceId = topLevelSurfaceList.nextId;
1286+ var primaryApp = ApplicationManager.startApplication("primary-oriented-app");
1287+ verify(primaryApp);
1288+ waitUntilAppWindowIsFullyLoaded(primarySurfaceId);
12801289
12811290 var gmailSurfaceId = topLevelSurfaceList.nextId;
12821291 var gmailApp = ApplicationManager.startApplication("gmail-webapp");
@@ -1298,7 +1307,7 @@ Rectangle {
12981307
12991308 swipeAwayGreeter();
13001309
1301− verify(isAppSurfaceFocused(dashSurfaceId))
1310+ verify(isAppSurfaceFocused(primarySurfaceId))
13021311
13031312 signalSpy.clear();
13041313 signalSpy.target = shell;
tests/utils/modules/Unity/Test/CMakeLists.txt+5−1
@@ -5,10 +5,14 @@ add_definitions(-DQT_NO_KEYWORDS)
55pkg_check_modules(UBUNTUGESTURES REQUIRED UbuntuGestures)
66
77include_directories(
8+ ${CMAKE_SOURCE_DIR}/src
9+)
10+
11+include_directories(
12+ SYSTEM
813 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
914 ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
1015 ${UBUNTUGESTURES_INCLUDE_DIRS}
11− ${CMAKE_SOURCE_DIR}/src
1216)
1317
1418set(UnityTestQML_SOURCES