on xenial help sign in
Merge unity8
commita0df9f68986bdf4464b3d8d1c0961d1353ee8af7 authorAlbert Astals Cid <albert.astals@canonical.com> dateMon 22 Jun 2015, 11:37 (11 years ago) parentac6bd3fc4c7e43
330 files changed+12483−3524
CMakeLists.txt+3 CODING+4 cmake/modules/QmlTest.cmake+10−8 data/unity8-dash.conf−5 data/unity8-dash.desktop.in+1 data/unity8.conf+1 debian/changelog+157 debian/control+6−5 debian/unity8.install+3 libs/UbuntuGestures/CMakeLists.txt+1 libs/UbuntuGestures/CandidateInactivityTimer.cpp+7−2 libs/UbuntuGestures/CandidateInactivityTimer.h+4−2 plugins/Ubuntu/Gestures/TimeSource.cpp → libs/UbuntuGestures/TimeSource.cpp plugins/Ubuntu/Gestures/TimeSource.h → libs/UbuntuGestures/TimeSource.h+14−4 libs/UbuntuGestures/Timer.cpp+54−11 libs/UbuntuGestures/Timer.h+15−3 libs/UbuntuGestures/TouchRegistry.cpp+28−26 libs/UbuntuGestures/TouchRegistry.h+26−10 plugins/AccountsService/plugin.h+1−1 plugins/Dash/CardCreator.js+44−15 plugins/Dash/plugin.h+1−1 plugins/Greeter/Unity/Launcher/CMakeLists.txt+1−1 plugins/Greeter/Unity/Launcher/launchermodelas.h+9−9 plugins/Greeter/Unity/Launcher/plugin.h+1−1 plugins/Greeter/Unity/Launcher/quicklistmodel.h+2−2 plugins/LightDM/UsersModel.cpp+1−1 plugins/LightDM/liblightdm/GreeterPrivate.cpp+29−5 plugins/LightDM/liblightdm/GreeterPrivate.h+1−1 plugins/LightDM/liblightdm/UsersModel.h+2−2 plugins/LightDM/plugin.h+1−1 plugins/Lights/plugin.h+1−1 plugins/Powerd/plugin.h+1−1 plugins/ScreenGrabber/plugin.h+1−1 plugins/SessionBroadcast/plugin.h+1−1 plugins/Ubuntu/DownloadDaemonListener/plugin.h+2−2 plugins/Ubuntu/Gestures/CMakeLists.txt+2−1 plugins/Ubuntu/Gestures/Damper.cpp+24 plugins/Ubuntu/Gestures/Damper.h+2 plugins/Ubuntu/Gestures/Direction.cpp+6−3 plugins/Ubuntu/Gestures/Direction.h+3−2 plugins/Ubuntu/Gestures/DirectionalDragArea.cpp+324−322 plugins/Ubuntu/Gestures/DirectionalDragArea.h+30−203 plugins/Ubuntu/Gestures/DirectionalDragArea_p.h+167 plugins/Ubuntu/Gestures/FloatingFlickable.cpp+150 plugins/Ubuntu/Gestures/FloatingFlickable.h+86 plugins/Ubuntu/Gestures/TouchDispatcher.cpp+88−53 plugins/Ubuntu/Gestures/TouchDispatcher.h+16−9 plugins/Ubuntu/Gestures/TouchGate.cpp+94−60 plugins/Ubuntu/Gestures/TouchGate.h+19−5 plugins/Ubuntu/Gestures/plugin.cpp+3−1 plugins/Ubuntu/Gestures/plugin.h+1−1 plugins/Ubuntu/Payments/plugin.h+1−1 plugins/Ubuntu/SystemImage/plugin.h+1−1 plugins/Unity/Connectivity/plugin.h+1−1 plugins/Unity/DashCommunicator/plugin.h+1−1 plugins/Unity/Indicators/indicatorsmodel.h+5−5 plugins/Unity/Indicators/menucontentactivator.cpp+4−4 plugins/Unity/Indicators/plugin.h+1−1 plugins/Unity/Launcher/CMakeLists.txt+1−1 plugins/Unity/Launcher/launchermodel.h+10−10 plugins/Unity/Launcher/plugin.h+1−1 plugins/Unity/Launcher/quicklistmodel.h+2−2 plugins/Unity/Session/plugin.h+1−1 plugins/Utils/CMakeLists.txt+3 plugins/Utils/ElapsedTimer.h+51 plugins/Utils/HomeKeyWatcher.cpp+116 plugins/Utils/HomeKeyWatcher.h+74 plugins/Utils/Timer.cpp+59 plugins/Utils/Timer.h+79 plugins/Utils/plugin.cpp+7−5 plugins/Utils/plugin.h+2−2 plugins/Utils/qlimitproxymodelqml.h+2−2 plugins/Utils/qvariantlistmodel.h+6−6 plugins/Utils/timeformatter.h+1−1 plugins/Utils/unitysortfilterproxymodelqml.h+2−2 plugins/Utils/windowkeysfilter.h+1−1 plugins/Utils/windowscreenshotprovider.cpp+59 plugins/Utils/windowscreenshotprovider.h+31 plugins/Wizard/plugin.h+1−1 po/am.po+4−4 po/ast.po+86−86 po/be.po+46−46 po/br.po+4−4 po/ca.po+25−25 po/cs.po+136−119 po/da.po+53−51 po/de.po+31−31 po/el.po+11−11 po/en_GB.po+3−3 po/eo.po+6−6 po/es.po+4−4 po/fi.po+4−4 po/fr.po+4−4 po/gd.po+4−4 po/hu.po+14−14 po/ia.po+684 po/it.po+4−4 po/nb.po+8−8 po/nl.po+4−4 po/nn.po+584 po/pa.po+3−3 po/pl.po+10−10 po/pt.po+7−7 po/pt_BR.po+9−9 po/sl.po+8−8 po/ur.po+584 po/zh_CN.po+22−20 qml/CMakeLists.txt+1 qml/Components/DragHandle.qml+18−33 qml/Components/EdgeDragArea.qml−46 qml/Components/EdgeDragEvaluator.qml+17−14 qml/Components/ResponsiveVerticalJournal.qml+7−2 qml/Components/WindowScreenshot.qml+35 qml/Dash/CardCarousel.qml−1 qml/Dash/CardGrid.qml−1 qml/Dash/CardHorizontalList.qml−1 qml/Dash/CardTool.qml+16−15 qml/Dash/CardVerticalJournal.qml−1 qml/Dash/Dash.qml+10−12 qml/Dash/DashApplication.qml+3−3 qml/Dash/GenericScopeView.qml+7−7 qml/Dash/Previews/PreviewRatingDisplay.qml+9−44 qml/Dash/Previews/PreviewRatingEdit.qml+80 qml/Dash/Previews/PreviewRatingInput.qml+3 qml/Dash/Previews/PreviewRatingSingleDisplay.qml+70 qml/Dash/Previews/PreviewWidgetFactory.qml+1 qml/Dash/ScopesList.qml+2 qml/Dash/ScopesListCategory.qml+5−1 qml/Dash/graphics/phone/screenshots/gmail-webapp.svg+343 qml/Dash/graphics/phone/screenshots/settings@12.png qml/Dash/graphics/phone/screenshots/ubuntu-weather-app.svg+201 qml/DeviceConfiguration.qml+81 qml/Greeter/CoverPage.qml+3−2 qml/Greeter/Greeter.qml+6−2 qml/Greeter/NarrowView.qml+1 qml/Greeter/ShimGreeter.qml+45 qml/Greeter/WideView.qml+1 qml/Launcher/Launcher.qml+7−15 qml/Notifications/ShapedIcon.qml+4−3 qml/OrientedShell.qml+185 qml/Panel/Indicators/IndicatorsLight.qml+8 qml/Panel/IndicatorsMenu.qml+12−2 qml/Panel/Panel.qml+9 qml/Rotation/HalfLoopRotationAnimation.qml+46 qml/Rotation/ImmediateRotationAction.qml+45 qml/Rotation/NinetyRotationAnimation.qml+90 qml/Rotation/RotationStates.qml+278 qml/Shell.qml+161−83 qml/Stages/ApplicationWindow.qml+53−18 qml/Stages/DecoratedWindow.qml+1 qml/Stages/DesktopStage.qml+43−14 qml/Stages/OrientationChangeAnimation.qml+241 qml/Stages/PhoneStage.qml+171−82 qml/Stages/SessionContainer.qml+2−8 qml/Stages/ShimStage.qml+44 qml/Stages/SpreadDelegate.qml+261−20 qml/Stages/SurfaceContainer.qml+2−2 qml/Stages/TabletStage.qml+177−50 qml/Stages/TransformedSpreadDelegate.qml+50−6 qml/Stages/TransformedTabletSpreadDelegate.qml+5−2 qml/Tutorial/TutorialBottom.qml+1−1 run.sh+1−1 runtests.sh+3 src/ApplicationArguments.cpp+22 src/ApplicationArguments.h+7−11 src/CMakeLists.txt+5−6 src/Dash/CMakeLists.txt+7−1 src/Dash/main.cpp+12−12 src/UnityCommandLineParser.cpp+5 src/UnityCommandLineParser.h+2 src/main.cpp+18−13 tests/autopilot/unity8/application_lifecycle/tests/test_application_lifecycle.py+14−4 tests/autopilot/unity8/fixture_setup.py+197−3 tests/autopilot/unity8/indicators/__init__.py+2−4 tests/autopilot/unity8/indicators/tests/__init__.py−2 tests/autopilot/unity8/indicators/tests/test_action_latency.py+4−2 tests/autopilot/unity8/indicators/tests/test_display_indicator.py+1−1 tests/autopilot/unity8/process_helpers.py+5−4 tests/autopilot/unity8/sensors.py+93 tests/autopilot/unity8/shell/__init__.py−24 tests/autopilot/unity8/shell/emulators/__init__.py−6 tests/autopilot/unity8/shell/emulators/dash.py+23−16 tests/autopilot/unity8/shell/emulators/greeter.py+2−4 tests/autopilot/unity8/shell/emulators/launcher.py+3−2 tests/autopilot/unity8/shell/emulators/main_window.py+14−2 tests/autopilot/unity8/shell/tests/__init__.py+32−111 tests/autopilot/unity8/shell/tests/test_emulators.py+6 tests/autopilot/unity8/shell/tests/test_rotation.py+113 tests/autopilot/unity8/shell/tests/test_upstart.py+5−4 tests/libs/UbuntuGestures/tst_TouchRegistry.cpp+139−74 tests/mocks/AccountsService/plugin.h+1−1 tests/mocks/CMakeLists.txt+1 tests/mocks/GSettings.1.0/fake_gsettings.h+2−2 tests/mocks/GSettings.1.0/plugin.cpp+6 tests/mocks/GSettings.1.0/plugin.h+1−1 tests/mocks/LightDM/liblightdm/UsersModel.h+2−2 tests/mocks/LightDM/plugin.h+1−1 tests/mocks/Lights/plugin.h+1−1 tests/mocks/MeeGo/QOfono/plugin.h+1−1 tests/mocks/Powerd/plugin.h+1−1 tests/mocks/QMenuModel/plugin.h+1−1 tests/mocks/QMenuModel/unitymenumodel.h+6−6 tests/mocks/QtMultimedia/plugin.h+1−1 tests/mocks/Ubuntu/Connectivity/plugin.h+2−2 tests/mocks/Ubuntu/DownloadDaemonListener/plugin.h+1−1 tests/mocks/Ubuntu/Payments/plugin.h+1−1 tests/mocks/Ubuntu/SystemImage/plugin.h+1−1 tests/mocks/Ubuntu/SystemSettings/SecurityPrivacy/plugin.h+1−1 tests/mocks/Ubuntu/Telephony/MockCallEntry.h+1−1 tests/mocks/Ubuntu/Telephony/plugin.h+2−2 tests/mocks/Unity/Application/ApplicationInfo.cpp+43−2 tests/mocks/Unity/Application/ApplicationInfo.h+9−1 tests/mocks/Unity/Application/ApplicationManager.cpp+40−3 tests/mocks/Unity/Application/CMakeLists.txt+1−2 tests/mocks/Unity/Application/MirSurfaceItem.cpp+10−27 tests/mocks/Unity/Application/MirSurfaceItem.h+29−29 tests/mocks/Unity/Application/MirSurfaceItem.qml+12−14 tests/mocks/Unity/Application/ObjectListModel.h+1−1 tests/mocks/Unity/Application/SurfaceManager.cpp+4−34 tests/mocks/Unity/Application/SurfaceManager.h+2−5 tests/mocks/Unity/Application/UbuntuKeyboardInfo.cpp+33−1 tests/mocks/Unity/Application/UbuntuKeyboardInfo.h+11−5 tests/mocks/Unity/Application/VirtualKeyboard.cpp+44 tests/mocks/Unity/Application/VirtualKeyboard.h+42 tests/mocks/Unity/Application/VirtualKeyboard.qml+7−1 tests/mocks/Unity/Application/plugin.h+2−2 tests/mocks/Unity/Connectivity/plugin.h+1−1 tests/mocks/Unity/DashCommunicator/plugin.h+1−1 tests/mocks/Unity/Indicators/fakeindicatorsmodel.h+5−5 tests/mocks/Unity/Indicators/fakeplugin.h+1−1 tests/mocks/Unity/Launcher/MockLauncherItem.h+2−2 tests/mocks/Unity/Launcher/MockLauncherModel.cpp+3−1 tests/mocks/Unity/Launcher/MockLauncherModel.h+10−10 tests/mocks/Unity/Launcher/MockQuickListModel.h+2−2 tests/mocks/Unity/Launcher/plugin.h+1−1 tests/mocks/Unity/Notifications/MockActionModel.h+3−3 tests/mocks/Unity/Notifications/MockNotificationModel.h+3−3 tests/mocks/Unity/Notifications/plugin.h+1−1 tests/mocks/Unity/fake_scopes.h+2−2 tests/mocks/Unity/fake_unity_plugin.h+1−1 tests/mocks/Utils/CMakeLists.txt+40 tests/mocks/Utils/HomeKeyWatcher.qml+21 tests/mocks/Utils/Style.js+37 tests/mocks/Utils/Utils.qmltypes+175 tests/mocks/Utils/constants.cpp+23 tests/mocks/Utils/constants.h+43 tests/mocks/Utils/plugin.cpp+76 tests/mocks/Utils/plugin.h+33 tests/mocks/Utils/qmldir+5 tests/mocks/Utils/windowstatestorage.cpp+48 tests/mocks/Utils/windowstatestorage.h+38 tests/mocks/Wizard/mockplugin.h+1−1 tests/mocks/indicator-service/mock-indicator-service.c+76−12 tests/plugins/Dash/cardcreator/1.res+24−10 tests/plugins/Dash/cardcreator/2.res+3−8 tests/plugins/Dash/cardcreator/3.res+34−20 tests/plugins/Dash/cardcreator/4.res+1−2 tests/plugins/Dash/cardcreator/5.res+35−21 tests/plugins/Dash/cardcreator/5.tst+1−1 tests/plugins/Dash/cardcreator/6.res+3−8 tests/plugins/Dash/cardcreator/6.tst+1−1 tests/plugins/Dash/cardcreator/7.res+3−8 tests/plugins/Dash/cardcreator/7.tst+1−1 tests/plugins/Dash/horizontaljournaltest.cpp+3−3 tests/plugins/Dash/horizontaljournaltry.cpp+1−1 tests/plugins/Dash/listviewwithpageheadersectionexternalmodeltest.cpp+3−3 tests/plugins/Dash/listviewwithpageheadertest.cpp+8−2 tests/plugins/Dash/organicgridtest.cpp+2−2 tests/plugins/Dash/organicgridtry.cpp+2−2 tests/plugins/Dash/verticaljournaltest.cpp+3−3 tests/plugins/Dash/verticaljournaltry.cpp+1−1 tests/plugins/Greeter/Unity/Launcher/CMakeLists.txt+1−1 tests/plugins/LightDM/CMakeLists.txt+35−16 tests/plugins/LightDM/pam.cpp+52 tests/plugins/Ubuntu/Gestures/CMakeLists.txt+5−2 tests/plugins/Ubuntu/Gestures/DownwardsLauncher.qml+5−19 tests/plugins/Ubuntu/Gestures/GestureTest.cpp+14−3 tests/plugins/Ubuntu/Gestures/GestureTest.h+4 tests/plugins/Ubuntu/Gestures/LeftwardsLauncher.qml+6−19 tests/plugins/Ubuntu/Gestures/RightwardsLauncher.qml+5−19 tests/plugins/Ubuntu/Gestures/TestItem.cpp+30 tests/plugins/Ubuntu/Gestures/TestItem.h+33 tests/plugins/Ubuntu/Gestures/UpwardsLauncher.qml+6−19 tests/plugins/Ubuntu/Gestures/tst_AxisVelocityCalculator.cpp+1−1 tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp+481−446 tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.qml+8−1 tests/plugins/Ubuntu/Gestures/tst_FloatingFlickable.cpp+122 tests/plugins/Ubuntu/Gestures/tst_FloatingFlickable.qml+80 tests/plugins/Ubuntu/Gestures/tst_PressedOutsideNotifier.cpp+1−1 tests/plugins/Ubuntu/Gestures/tst_TouchDispatcher.cpp+5−6 tests/plugins/Ubuntu/Gestures/tst_TouchGate.cpp+54−22 tests/plugins/Unity/Indicators/menucontentactivatortest.cpp+2−2 tests/plugins/Unity/Launcher/CMakeLists.txt−1 tests/plugins/Unity/Launcher/launchermodeltest.cpp+24−22 tests/plugins/Utils/CMakeLists.txt+1 tests/plugins/Utils/homekeywatchertest.cpp+323 tests/plugins/Utils/qlimitproxymodeltest.cpp+5−5 tests/plugins/Utils/unitysortfilterproxymodeltest.cpp+5−5 tests/qmltests/CMakeLists.txt+3−1 tests/qmltests/Components/CMakeLists.txt+19−3 tests/qmltests/Components/tst_DragHandle.cpp+15−21 tests/qmltests/Components/tst_DragHandle.qml+7 tests/qmltests/Components/tst_DragHandle/BidirectionalShowable.qml+2−2 tests/qmltests/Components/tst_DragHandle/HorizontalShowable.qml+4−4 tests/qmltests/Components/tst_DragHandle/VerticalShowable.qml+4−4 tests/qmltests/Components/tst_EdgeDragEvaluator.cpp+165 tests/qmltests/Components/tst_EdgeDragEvaluator.qml+27 tests/qmltests/Dash/Previews/tst_PreviewRatingEdit.qml+80 tests/qmltests/Dash/tst_Card.qml−1 tests/qmltests/Dash/tst_DashContent.qml+7−2 tests/qmltests/Dash/tst_GenericScopeView.qml+30−7 tests/qmltests/Panel/tst_IndicatorsMenu.qml−2 tests/qmltests/Panel/tst_Panel.qml−2 tests/qmltests/Stages/ApplicationCheckBox.qml+49−20 tests/qmltests/Stages/tst_ApplicationWindow.qml+14−23 tests/qmltests/Stages/tst_DesktopStage.qml+77−67 tests/qmltests/Stages/tst_PhoneStage.qml+13−66 tests/qmltests/Stages/tst_SessionContainer.qml+1−64 tests/qmltests/Stages/tst_SpreadDelegate.qml+172−37 tests/qmltests/Stages/tst_TabletStage.qml+51 tests/qmltests/Tutorial/tst_Tutorial.qml+6−1 tests/qmltests/tst_OrientedShell.qml+1151 tests/qmltests/tst_Shell.qml+166−117 tests/qmltests/tst_ShellWithPin.qml+9−11 tests/uqmlscene/main.cpp−10 tests/utils/modules/Unity/Test/MouseTouchEmulationCheckbox.qml+1 tests/utils/modules/Unity/Test/UnityTestCase.qml+58−11 tests/utils/modules/Unity/Test/plugin.h+1−1 tests/utils/modules/Unity/Test/testutil.cpp+8−20 tests/utils/modules/Unity/Test/testutil.h+1−1
CMakeLists.txt+3
@@ -56,6 +56,8 @@ find_package(Qt5DBus 5.2 REQUIRED)
5656find_package(Qt5Concurrent 5.2 REQUIRED)
5757find_package(Qt5Sql 5.2 REQUIRED)
5858
59+pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
60+
5961# Standard install paths
6062include(GNUInstallDirs)
6163
@@ -111,6 +113,7 @@ set(MOCK_INDICATOR_SERVICE_APP unity-mock-indicator-service)
111113include_directories(
112114 ${CMAKE_SOURCE_DIR}/include
113115 ${CMAKE_BINARY_DIR}/include
116+ ${APPLICATION_API_INCLUDE_DIRS}
114117)
115118
116119# add subdirectories to build
CODING+4
@@ -158,6 +158,10 @@ To run the tests for a single component/class under gdb (so you can catch SEGFAU
158158
159159$ make gdbtestComponentName
160160
161+To run a single test function of a single component/class:
162+
163+$ make testComponentName FUNCTION="TestCaseName::functionName"
164+
161165To run a test application that allow you to manually test or to simply try out
162166a given component:
163167
cmake/modules/QmlTest.cmake+10−8
@@ -155,10 +155,11 @@ macro(add_binary_qml_test CLASS_NAME LD_PATH DEPS ENVVAR)
155155 set(testCommand
156156 ${ENVVAR}
157157 LD_LIBRARY_PATH=${LD_PATH}
158− dbus-launch
159− ${CMAKE_CURRENT_BINARY_DIR}/${CLASS_NAME}TestExec
160− -o ${CMAKE_BINARY_DIR}/test${CLASS_NAME}.xml,xunitxml
161− -o -,txt)
158+ dbus-test-runner
159+ -m 300
160+ -t ${CMAKE_CURRENT_BINARY_DIR}/${CLASS_NAME}TestExec
161+ -p -o -p ${CMAKE_BINARY_DIR}/test${CLASS_NAME}.xml,xunitxml
162+ -p -o -p -,txt)
162163
163164 add_qmltest_target(test${CLASS_NAME} "${testCommand}" FALSE TRUE)
164165 add_dependencies(test${CLASS_NAME} ${CLASS_NAME}TestExec ${DEPS})
@@ -173,10 +174,11 @@ macro(add_binary_qml_test CLASS_NAME LD_PATH DEPS ENVVAR)
173174 ${ENVVAR}
174175 LD_LIBRARY_PATH=${LD_PATH}
175176 xvfb-run --server-args "-screen 0 1024x768x24" --auto-servernum
176− dbus-launch
177− ${CMAKE_CURRENT_BINARY_DIR}/${CLASS_NAME}TestExec
178− -o ${CMAKE_BINARY_DIR}/test${CLASS_NAME}.xml,xunitxml
179− -o -,txt)
177+ dbus-test-runner
178+ -m 300
179+ -t ${CMAKE_CURRENT_BINARY_DIR}/${CLASS_NAME}TestExec
180+ -p -o -p ${CMAKE_BINARY_DIR}/test${CLASS_NAME}.xml,xunitxml
181+ -p -o -p -,txt)
180182
181183 add_qmltest_target(xvfbtest${CLASS_NAME} "${xvfbtestCommand}" FALSE TRUE)
182184 add_dependencies(qmluitests xvfbtest${CLASS_NAME})
data/unity8-dash.conf−5
@@ -18,11 +18,6 @@ respawn
1818env APP_ID=unity8-dash
1919
2020pre-start script
21− if [ -z "$UNITY_SCOPES_LIST" ]; then
22− # FIXME: remove once we have this in dconf
23− initctl set-env UNITY_SCOPES_LIST="clickscope;musicaggregator;videoaggregator"
24− fi
25−
2621 initctl emit scope-ui-starting
2722end script
2823
data/unity8-dash.desktop.in+1
@@ -7,3 +7,4 @@ Terminal=false
77Icon=
88NoDisplay=true
99X-Ubuntu-Touch=true
10+X-Ubuntu-Supported-Orientations=primary
data/unity8.conf+1
@@ -54,4 +54,5 @@ post-stop script
5454 initctl set-env --global MIR_SOCKET=$UNITY_MIR_SOCKET
5555 initctl unset-env --global UNITY_MIR_SOCKET
5656 initctl unset-env --global MIR_SERVER_PROMPT_FILE
57+ initctl emit application-end
5758end script
debian/changelog+157
@@ -1,3 +1,160 @@
1+unity8 (8.10+15.10.20150616.1-0ubuntu1) wily; urgency=medium
2+
3+ [ Albert Astals Cid ]
4+ * Bind running to the proper object.visible
5+ * Do not use template in generated card creator code
6+ * Fix regression introduced by the concierge change
7+ * Take into account the extra margin on top when calculating the
8+ display margin
9+ * workaround oxide 1.7 issue (LP: #1459142)
10+
11+ [ CI Train Bot ]
12+ * New rebuild forced.
13+
14+ [ Federico Gimenez ]
15+ * On the autopilot helpers, swipe if the item to open from the dash is
16+ not visible. unity8-autopilot dependencies now require ubuntu-ui-
17+ toolkit-autopilot (>= 1.2.1485+15.04.20150417.1-0ubuntu1)
18+
19+ [ Florian Boucault ]
20+ * Notifications: shaped icon was stretching the icon instead of
21+ respecting its aspect ratio and filling the whole shape. (LP:
22+ #1450229)
23+
24+ [ Josh Arenson ]
25+ * Implement bare-bones shellMode=greeter and shellMode=shell
26+
27+ [ Leo Arias ]
28+ * On the autopilot helpers, swipe if the item to open from the dash is
29+ not visible. unity8-autopilot dependencies now require ubuntu-ui-
30+ toolkit-autopilot (>= 1.2.1485+15.04.20150417.1-0ubuntu1)
31+
32+ [ Leonardo Arias Fonseca ]
33+ * On the autopilot helpers, swipe if the item to open from the dash is
34+ not visible. unity8-autopilot dependencies now require ubuntu-ui-
35+ toolkit-autopilot (>= 1.2.1485+15.04.20150417.1-0ubuntu1)
36+
37+ [ Michael Zanetti ]
38+ * add info on how to run a single function in make test
39+
40+ [ Richard Huddie ]
41+ * Slow down the speed of autopilot helpers used to swipe to a
42+ different scope to improve the reliability. (LP: #1461059, #1457047)
43+
44+ -- CI Train Bot <ci-train-bot@canonical.com> Tue, 16 Jun 2015 15:12:59 +0000
45+
46+unity8 (8.10+15.10.20150612-0ubuntu2) wily; urgency=medium
47+
48+ * No-change rebuild against Qt 5.4.2.
49+
50+ -- Timo Jyrinki <timo-jyrinki@ubuntu.com> Mon, 15 Jun 2015 12:33:07 +0300
51+
52+unity8 (8.10+15.10.20150612-0ubuntu1) wily; urgency=medium
53+
54+ [ Michał Sawicz ]
55+ * Implement full-shell rotation (LP: #1210199)
56+
57+ [ CI Train Bot ]
58+ * New rebuild forced.
59+ * Resync trunk.
60+
61+ [ Daniel d'Andrada ]
62+ * Implemented autopilot-test and fake-sensors for shell-rotation.
63+
64+ [ Mirco Müller ]
65+ * Implemented autopilot-test and fake-sensors for shell-rotation.
66+
67+ -- CI Train Bot <ci-train-bot@canonical.com> Fri, 12 Jun 2015 16:07:47 +0000
68+
69+unity8 (8.02+15.10.20150603.1-0ubuntu1) wily; urgency=medium
70+
71+ [ CI Train Bot ]
72+ * New rebuild forced.
73+ * Resync trunk.
74+
75+ [ Renato Araujo Oliveira Filho ]
76+ * Set the device led color to green. (LP: #1450894)
77+
78+ -- CI Train Bot <ci-train-bot@canonical.com> Wed, 03 Jun 2015 21:42:35 +0000
79+
80+unity8 (8.02+15.10.20150518.1-0ubuntu1) wily; urgency=medium
81+
82+ [ Albert Astals Cid ]
83+ * Add overrides to override functions
84+ * Implement "rating-edit" preview widget (LP: #1318144)
85+ * Make the DashContent::test_mainNavigation test more stable (LP:
86+ #1450809)
87+ * Use art height as implicitHeight when the header is overlayed and
88+ there's no summary
89+
90+ [ CI Train Bot ]
91+ * New rebuild forced.
92+ * Resync trunk.
93+
94+ [ Daniel d'Andrada ]
95+ * Introducing FloatingFlickable
96+ * Make Ubuntu.Gestures components install TouchRegistry by themselves
97+
98+ [ Michael Terry ]
99+ * Fix a possible crash in our PAM threading code. (LP: #1425362) (LP:
100+ #1425362)
101+ * Fix the lockscreen becoming unresponsive after testing an app on the
102+ device from QtCreator. (LP: #1435364)
103+
104+ [ Nick Dedekind ]
105+ * Fixed desktop stage app focus.
106+ * Fixed issue in laggy indicator autpilot tests (LP: #1446846)
107+
108+ -- CI Train Bot <ci-train-bot@canonical.com> Mon, 18 May 2015 23:04:39 +0000
109+
110+unity8 (8.02+15.04.20150511-0ubuntu1) vivid; urgency=medium
111+
112+ [ Albert Astals Cid ]
113+ * Workarounds for concierge mode.
114+
115+ [ CI Train Bot ]
116+ * New rebuild forced.
117+ * Resync trunk.
118+
119+ -- CI Train Bot <ci-train-bot@canonical.com> Mon, 11 May 2015 08:29:58 +0000
120+
121+unity8 (8.02+15.04.20150505-0ubuntu1) vivid; urgency=medium
122+
123+ [ Albert Astals Cid ]
124+ * Make runtests fake a test error if make fails
125+ * Make the test more stable
126+ * Use dbus-test-runner instead of dbus-launch
127+
128+ [ Daniel d'Andrada ]
129+ * DirectionalDragArea: improvements & API grooming (LP: #1417920)
130+ * Fix EdgeDragEvaluator when a drag can happen both ways
131+ (Direction.Horizontal)
132+
133+ [ Josh Arenson ]
134+ * Remove panelHeight property as it is unused.
135+
136+ [ Leo Arias ]
137+ * Initial clean up of the autopilot tests set up. Removed the touch
138+ device from the test case. Moved the restart of unity to a fixture.
139+ Removed the unused DragMixin. Updated the setUpClass to use
140+ process_helpers. Removed the workaround for bug #1238417, already
141+ fixed. Use the toolkit helper to set the testability environment
142+ variable. Fixed the indicators base class that was restarting unity
143+ twice. (LP: #1238417, #1447206)
144+ * Use the base class from the toolkit in autopilot tests.
145+
146+ [ Michael Zanetti ]
147+ * emit application-stop when we're going down (LP: #1326513)
148+
149+ [ Michał Sawicz ]
150+ * UNITY_SCOPES_LIST is no more
151+
152+ [ handsome_feng<445865575@qq.com> ]
153+ * When click the favorite scope in Dash Manager , it just return to
154+ the corresponding scope page. (LP: #1447056)
155+
156+ -- CI Train Bot <ci-train-bot@canonical.com> Tue, 05 May 2015 14:47:24 +0000
157+
1158unity8 (8.02+15.04.20150422-0ubuntu1) vivid; urgency=medium
2159
3160 [ Albert Astals Cid ]
debian/control+6−5
@@ -12,6 +12,7 @@ Build-Depends: cmake,
1212# append :native to g++-4.9 so we don't try to run armhf g++
1313# on an x86 CPU for eaxmple, when cross-compiling.
1414 g++-4.9:native,
15+ libandroid-properties-dev,
1516 graphviz,
1617 gsettings-ubuntu-schemas (>= 0.0.2+14.10.20140815),
1718 libconnectivity-qt1-dev,
@@ -27,7 +28,7 @@ Build-Depends: cmake,
2728 libqmenumodel-dev (>= 0.2.9),
2829 libqt5xmlpatterns5-dev,
2930 libsystemsettings-dev,
30− libunity-api-dev (>= 7.96),
31+ libunity-api-dev (>= 7.97),
3132 libusermetricsoutput1-dev,
3233 libxcb1-dev,
3334 pkg-config,
@@ -94,7 +95,7 @@ Depends: gsettings-desktop-schemas,
9495 qml-module-qtquick-xmllistmodel,
9596 qml-module-qtsysteminfo,
9697 qtdeclarative5-gsettings1.0,
97− qtdeclarative5-qtmir-plugin (>= 0.4.4),
98+ qtdeclarative5-qtmir-plugin (>= 0.4.5),
9899 qtdeclarative5-ubuntu-telephony0.1,
99100 qtdeclarative5-ubuntu-web-plugin,
100101 ubuntu-system-settings,
@@ -124,7 +125,7 @@ Depends: qml-module-qtquick-layouts,
124125 qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 1.1.1239) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 1.1.1239),
125126 qtdeclarative5-unity-notifications-plugin (>= 0.1.2) | unity-notifications-impl,
126127 ubuntu-thumbnailer-impl-0,
127− unity-application-impl-4,
128+ unity-application-impl-6,
128129 unity-notifications-impl-3,
129130 unity-plugin-scopes | unity-scopes-impl,
130131 unity-scopes-impl-6,
@@ -150,7 +151,7 @@ Depends: autopilot-qt5 (>= 1.4),
150151 python3-fixtures,
151152 python3-gi,
152153 qttestability-autopilot (>= 1.4),
153− ubuntu-ui-toolkit-autopilot,
154+ ubuntu-ui-toolkit-autopilot (>= 1.2.1485+15.04.20150417.1-0ubuntu1),
154155 unity-scope-click,
155156 unity8 (= ${source:Version}),
156157 unity8-fake-env (= ${source:Version}),
@@ -168,7 +169,7 @@ Pre-Depends: ${misc:Pre-Depends},
168169Depends: ${misc:Depends},
169170 ${shlibs:Depends},
170171Provides: unity-application-impl,
171− unity-application-impl-4,
172+ unity-application-impl-6,
172173Replaces: unity8-autopilot (<< 8.02+15.04.20150422-0ubuntu1)
173174Description: Fake environment for running Unity 8 shell
174175 Provides fake implementations of some QML modules used by Unity 8 shell
debian/unity8.install+3
@@ -8,6 +8,9 @@ usr/share/applications/unity8.desktop
88usr/share/unity8/Greeter
99usr/share/unity8/Launcher
1010usr/share/unity8/Panel
11+usr/share/unity8/Rotation
12+usr/share/unity8/DeviceConfiguration.qml
13+usr/share/unity8/OrientedShell.qml
1114usr/share/unity8/Shell.qml
1215usr/share/unity8/Stages
1316usr/share/unity8/Tutorial
libs/UbuntuGestures/CMakeLists.txt+1
@@ -5,6 +5,7 @@ set(UbuntuGestures_SOURCES
55 CandidateInactivityTimer.cpp
66 DebugHelpers.cpp
77 Timer.cpp
8+ TimeSource.cpp
89 TouchOwnershipEvent.cpp
910 TouchRegistry.cpp
1011 UnownedTouchEvent.cpp
libs/UbuntuGestures/CandidateInactivityTimer.cpp+7−2
@@ -19,12 +19,12 @@
1919namespace UbuntuGestures {
2020
2121CandidateInactivityTimer::CandidateInactivityTimer(int touchId, QQuickItem *candidate,
22− AbstractTimerFactory &timerFactory, QObject *parent)
22+ AbstractTimer *timer, QObject *parent)
2323 : QObject(parent)
24+ , m_timer(timer)
2425 , m_touchId(touchId)
2526 , m_candidate(candidate)
2627{
27− m_timer = timerFactory.createTimer(this);
2828 connect(m_timer, &AbstractTimer::timeout,
2929 this, &CandidateInactivityTimer::onTimeout);
3030 m_timer->setInterval(durationMs);
@@ -32,6 +32,11 @@ CandidateInactivityTimer::CandidateInactivityTimer(int touchId, QQuickItem *cand
3232 m_timer->start();
3333}
3434
35+CandidateInactivityTimer::~CandidateInactivityTimer()
36+{
37+ delete m_timer;
38+}
39+
3540void CandidateInactivityTimer::onTimeout()
3641{
3742 qWarning("[TouchRegistry] Candidate for touch %d defaulted!", m_touchId);
libs/UbuntuGestures/CandidateInactivityTimer.h+4−2
@@ -29,10 +29,12 @@ class UBUNTUGESTURES_EXPORT CandidateInactivityTimer : public QObject {
2929 Q_OBJECT
3030public:
3131 CandidateInactivityTimer(int touchId, QQuickItem *candidate,
32− AbstractTimerFactory &timerFactory,
32+ AbstractTimer *timer,
3333 QObject *parent = nullptr);
3434
35− const int durationMs = 350;
35+ virtual ~CandidateInactivityTimer();
36+
37+ const int durationMs = 1000;
3638
3739Q_SIGNALS:
3840 void candidateDefaulted(int touchId, QQuickItem *candidate);
plugins/Ubuntu/Gestures/TimeSource.cpp → libs/UbuntuGestures/TimeSource.cpprenamed

The content is the same.

plugins/Ubuntu/Gestures/TimeSource.h → libs/UbuntuGestures/TimeSource.hrenamed+14−4
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2013 - Canonical Ltd.
2+ * Copyright (C) 2013,2015 Canonical Ltd.
33 *
44 * This program is free software: you can redistribute it and/or modify it
55 * under the terms of the GNU Lesser General Public License, as
@@ -21,14 +21,14 @@
2121#ifndef UBUNTUGESTURES_TIMESOURCE_H
2222#define UBUNTUGESTURES_TIMESOURCE_H
2323
24−#include "UbuntuGesturesQmlGlobal.h"
24+#include "UbuntuGesturesGlobal.h"
2525#include <QSharedPointer>
2626
2727namespace UbuntuGestures {
2828/*
2929 Interface for a time source.
3030 */
31−class UBUNTUGESTURESQML_EXPORT TimeSource {
31+class UBUNTUGESTURES_EXPORT TimeSource {
3232public:
3333 virtual ~TimeSource() {}
3434 /* Returns the current time in milliseconds since some reference time in the past. */
@@ -40,7 +40,7 @@ typedef QSharedPointer<TimeSource> SharedTimeSource;
4040 Implementation of a time source
4141 */
4242class RealTimeSourcePrivate;
43−class RealTimeSource : public TimeSource {
43+class UBUNTUGESTURES_EXPORT RealTimeSource : public TimeSource {
4444public:
4545 RealTimeSource();
4646 virtual ~RealTimeSource();
@@ -49,6 +49,16 @@ private:
4949 RealTimeSourcePrivate *d;
5050};
5151
52+/*
53+ A fake time source, useful for tests
54+ */
55+class FakeTimeSource : public TimeSource {
56+public:
57+ FakeTimeSource() { m_msecsSinceReference = 0; }
58+ qint64 msecsSinceReference() override { return m_msecsSinceReference; }
59+ qint64 m_msecsSinceReference;
60+};
61+
5262} // namespace UbuntuGestures
5363
5464#endif // UBUNTUGESTURES_TIMESOURCE_H
libs/UbuntuGestures/Timer.cpp+54−11
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 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
@@ -58,13 +58,36 @@ void Timer::setSingleShot(bool value)
5858
5959/////////////////////////////////// FakeTimer //////////////////////////////////
6060
61−FakeTimer::FakeTimer(QObject *parent)
61+FakeTimer::FakeTimer(const SharedTimeSource &timeSource, QObject *parent)
6262 : UbuntuGestures::AbstractTimer(parent)
6363 , m_interval(0)
6464 , m_singleShot(false)
65+ , m_timeSource(timeSource)
6566{
6667}
6768
69+void FakeTimer::update()
70+{
71+ if (!isRunning()) {
72+ return;
73+ }
74+
75+ if (m_nextTimeoutTime <= m_timeSource->msecsSinceReference()) {
76+ if (isSingleShot()) {
77+ stop();
78+ } else {
79+ m_nextTimeoutTime += interval();
80+ }
81+ Q_EMIT timeout();
82+ }
83+}
84+
85+void FakeTimer::start()
86+{
87+ AbstractTimer::start();
88+ m_nextTimeoutTime = m_timeSource->msecsSinceReference() + (qint64)interval();
89+}
90+
6891int FakeTimer::interval() const
6992{
7093 return m_interval;
@@ -87,23 +110,43 @@ void FakeTimer::setSingleShot(bool value)
87110
88111/////////////////////////////////// FakeTimerFactory //////////////////////////////////
89112
90−AbstractTimer *FakeTimerFactory::createTimer(QObject *parent)
113+FakeTimerFactory::FakeTimerFactory()
91114{
92− FakeTimer *fakeTimer = new FakeTimer(parent);
93−
94− timers.append(fakeTimer);
95−
96− return fakeTimer;
115+ m_timeSource.reset(new FakeTimeSource);
97116}
98117
99−void FakeTimerFactory::makeRunningTimersTimeout()
118+void FakeTimerFactory::updateTime(qint64 targetTime)
100119{
120+ qint64 minTimeoutTime = targetTime;
121+
101122 for (int i = 0; i < timers.count(); ++i) {
102123 FakeTimer *timer = timers[i].data();
103− if (timer && timer->isRunning()) {
104− timer->emitTimeout();
124+ if (timer && timer->isRunning() && timer->nextTimeoutTime() < minTimeoutTime) {
125+ minTimeoutTime = timer->nextTimeoutTime();
105126 }
106127 }
128+
129+ m_timeSource->m_msecsSinceReference = minTimeoutTime;
130+
131+ for (int i = 0; i < timers.count(); ++i) {
132+ FakeTimer *timer = timers[i].data();
133+ if (timer) {
134+ timer->update();
135+ }
136+ }
137+
138+ if (m_timeSource->msecsSinceReference() < targetTime) {
139+ updateTime(targetTime);
140+ }
141+}
142+
143+AbstractTimer *FakeTimerFactory::createTimer(QObject *parent)
144+{
145+ FakeTimer *fakeTimer = new FakeTimer(m_timeSource, parent);
146+
147+ timers.append(fakeTimer);
148+
149+ return fakeTimer;
107150}
108151
109152} // namespace UbuntuGestures
libs/UbuntuGestures/Timer.h+15−3
@@ -18,6 +18,7 @@
1818#define UBUNTUGESTURES_TIMER_H
1919
2020#include "UbuntuGesturesGlobal.h"
21+#include "TimeSource.h"
2122
2223#include <QObject>
2324#include <QPointer>
@@ -66,17 +67,21 @@ class UBUNTUGESTURES_EXPORT FakeTimer : public AbstractTimer
6667{
6768 Q_OBJECT
6869public:
69− FakeTimer(QObject *parent = nullptr);
70+ FakeTimer(const SharedTimeSource &timeSource, QObject *parent = nullptr);
7071
71− virtual void emitTimeout() { Q_EMIT timeout(); }
72+ void update();
73+ qint64 nextTimeoutTime() const { return m_nextTimeoutTime; }
7274
7375 int interval() const override;
7476 void setInterval(int msecs) override;
77+ void start() override;
7578 bool isSingleShot() const override;
7679 void setSingleShot(bool value) override;
7780private:
7881 int m_interval;
7982 bool m_singleShot;
83+ SharedTimeSource m_timeSource;
84+ qint64 m_nextTimeoutTime;
8085};
8186
8287class UBUNTUGESTURES_EXPORT AbstractTimerFactory
@@ -95,9 +100,16 @@ public:
95100class UBUNTUGESTURES_EXPORT FakeTimerFactory : public AbstractTimerFactory
96101{
97102public:
103+ FakeTimerFactory();
104+ virtual ~FakeTimerFactory() {}
105+
106+ void updateTime(qint64 msecsSinceReference);
107+ QSharedPointer<TimeSource> timeSource() { return m_timeSource; }
108+
98109 AbstractTimer *createTimer(QObject *parent = nullptr) override;
99− void makeRunningTimersTimeout();
100110 QList<QPointer<FakeTimer>> timers;
111+private:
112+ QSharedPointer<FakeTimeSource> m_timeSource;
101113};
102114
103115} // namespace UbuntuGestures
libs/UbuntuGestures/TouchRegistry.cpp+28−26
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 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
@@ -41,20 +41,10 @@ using namespace UbuntuGestures;
4141TouchRegistry *TouchRegistry::m_instance = nullptr;
4242
4343TouchRegistry::TouchRegistry(QObject *parent)
44− : TouchRegistry(parent, new TimerFactory)
45−{
46−}
47−
48−TouchRegistry::TouchRegistry(QObject *parent, AbstractTimerFactory *timerFactory)
4944 : QObject(parent)
5045 , m_inDispatchLoop(false)
51− , m_timerFactory(timerFactory)
46+ , m_timerFactory(new TimerFactory)
5247{
53− if (m_instance == nullptr) {
54− m_instance = this;
55− } else {
56− qFatal("Cannot have more than one instance of TouchRegistry. It must be a singleton.");
57− }
5848}
5949
6050TouchRegistry::~TouchRegistry()
@@ -64,6 +54,20 @@ TouchRegistry::~TouchRegistry()
6454 delete m_timerFactory;
6555}
6656
57+TouchRegistry *TouchRegistry::instance()
58+{
59+ if (m_instance == nullptr) {
60+ m_instance = new TouchRegistry;
61+ }
62+ return m_instance;
63+}
64+
65+void TouchRegistry::setTimerFactory(AbstractTimerFactory *timerFactory)
66+{
67+ delete m_timerFactory;
68+ m_timerFactory = timerFactory;
69+}
70+
6771void TouchRegistry::update(const QTouchEvent *event)
6872{
6973 #if TOUCHREGISTRY_DEBUG
@@ -95,10 +99,6 @@ void TouchRegistry::deliverTouchUpdatesToUndecidedCandidatesAndWatchers(const QT
9599 // for each point and there should not be many active points at any given moment.
96100 // But having three nested for-loops does scare.
97101
98− // TODO: Don't send it to the object that is already receiving the regular event
99− // because QQuickWindow is sending it to him (i.e., he's the touch owner from Qt's point of view)
100− // Problem is, we cannnot easily get this information.
101−
102102 const QList<QTouchEvent::TouchPoint> &updatedTouchPoints = event->touchPoints();
103103
104104 // Maps an item to the touches in this event he should be informed about.
@@ -118,7 +118,9 @@ void TouchRegistry::deliverTouchUpdatesToUndecidedCandidatesAndWatchers(const QT
118118 for (int i = 0; i < touchInfo->candidates.count(); ++i) {
119119 CandidateInfo &candidate = touchInfo->candidates[i];
120120 Q_ASSERT(!candidate.item.isNull());
121− touchIdsForItems[candidate.item.data()].append(touchInfo->id);
121+ if (candidate.state != CandidateInfo::InterimOwner) {
122+ touchIdsForItems[candidate.item.data()].append(touchInfo->id);
123+ }
122124 }
123125 }
124126
@@ -260,10 +262,10 @@ void TouchRegistry::addCandidateOwnerForTouch(int id, QQuickItem *candidate)
260262 // TODO: Check if candidate already exists
261263
262264 CandidateInfo candidateInfo;
263− candidateInfo.undecided = true;
265+ candidateInfo.state = CandidateInfo::Undecided;
264266 candidateInfo.item = candidate;
265267 candidateInfo.inactivityTimer = new CandidateInactivityTimer(id, candidate,
266− *m_timerFactory,
268+ m_timerFactory->createTimer(),
267269 this);
268270 connect(candidateInfo.inactivityTimer, &CandidateInactivityTimer::candidateDefaulted,
269271 this, &TouchRegistry::rejectCandidateOwnerForTouch);
@@ -301,8 +303,8 @@ void TouchRegistry::removeCandidateOwnerForTouch(int id, QQuickItem *candidate)
301303 for (int i = 0; i < touchInfo->candidates.count() && indexRemoved == -1; ++i) {
302304 CandidateInfo &candidateInfo = touchInfo->candidates[i];
303305 if (candidateInfo.item == candidate) {
304− Q_ASSERT(i > 0 || candidateInfo.undecided);
305− if (i == 0 && !candidateInfo.undecided) {
306+ Q_ASSERT(i > 0 || candidateInfo.state == CandidateInfo::Undecided);
307+ if (i == 0 && candidateInfo.state != CandidateInfo::Undecided) {
306308 qCritical("TouchRegistry: touch owner is being removed.");
307309 }
308310 delete candidateInfo.inactivityTimer;
@@ -340,7 +342,7 @@ void TouchRegistry::requestTouchOwnership(int id, QQuickItem *candidate)
340342 for (int i = 0; i < touchInfo->candidates.count(); ++i) {
341343 CandidateInfo &candidateInfo = touchInfo->candidates[i];
342344 if (candidateInfo.item == candidate) {
343− candidateInfo.undecided = false;
345+ candidateInfo.state = CandidateInfo::Requested;
344346 delete candidateInfo.inactivityTimer;
345347 candidateInfo.inactivityTimer = nullptr;
346348 candidateIndex = i;
@@ -351,7 +353,7 @@ void TouchRegistry::requestTouchOwnership(int id, QQuickItem *candidate)
351353 // add it as a candidate if not present yet
352354 if (candidateIndex < 0) {
353355 CandidateInfo candidateInfo;
354− candidateInfo.undecided = false;
356+ candidateInfo.state = CandidateInfo::InterimOwner;
355357 candidateInfo.item = candidate;
356358 candidateInfo.inactivityTimer = nullptr;
357359 touchInfo->candidates.append(candidateInfo);
@@ -407,8 +409,8 @@ void TouchRegistry::rejectCandidateOwnerForTouch(int id, QQuickItem *candidate)
407409 for (int i = 0; i < touchInfo->candidates.count() && rejectedCandidateIndex == -1; ++i) {
408410 CandidateInfo &candidateInfo = touchInfo->candidates[i];
409411 if (candidateInfo.item == candidate) {
410− Q_ASSERT(i > 0 || candidateInfo.undecided);
411− if (i == 0 && !candidateInfo.undecided) {
412+ Q_ASSERT(i > 0 || candidateInfo.state == CandidateInfo::Undecided);
413+ if (i == 0 && candidateInfo.state != CandidateInfo::Undecided) {
412414 qCritical() << "TouchRegistry: Can't reject item (" << (void*)candidate
413415 << ") as it already owns touch" << id;
414416 return;
@@ -467,7 +469,7 @@ void TouchRegistry::TouchInfo::init(int id)
467469
468470bool TouchRegistry::TouchInfo::isOwned() const
469471{
470− return !candidates.isEmpty() && !candidates.first().undecided;
472+ return !candidates.isEmpty() && candidates.first().state != CandidateInfo::Undecided;
471473}
472474
473475bool TouchRegistry::TouchInfo::ended() const
libs/UbuntuGestures/TouchRegistry.h+26−10
@@ -25,6 +25,7 @@
2525
2626#include "UbuntuGesturesGlobal.h"
2727#include "CandidateInactivityTimer.h"
28+#include "Timer.h"
2829#include "Pool.h"
2930
3031namespace UbuntuGestures {
@@ -79,9 +80,11 @@ namespace UbuntuGestures {
7980 If an item wants ownership over touches as soon as he receives the TouchBegin for them, his step 1
8081 would be instead:
8182 TouchRegistry::instance()->requestTouchOwnership(touchId, this);
82− return true;
83− He would then be notified once ownership has been granted to him, from which point onwards he could
84− safely assume other TouchRegistry users wouldn't snatch this touch away from him.
83+ touchEvent->accept();
84+ He won't get any UnownedTouchEvent for that touch as he is already the interim owner (ie, QQuickWindow
85+ will keep sending touch updates to him already). Eventually he will be notified once ownership has
86+ been granted to him (from TouchRegistry perspective), from which point onwards he could safely assume
87+ other TouchRegistry users wouldn't snatch this touch away from him.
8588
8689 Items oblivious to TouchRegistry will lose their touch points without warning, just like in plain Qt.
8790
@@ -91,15 +94,10 @@ class UBUNTUGESTURES_EXPORT TouchRegistry : public QObject
9194{
9295 Q_OBJECT
9396public:
94− TouchRegistry(QObject *parent = nullptr);
95− // Useful for tests, where you should feed a fake timer
96− TouchRegistry(QObject *parent, UbuntuGestures::AbstractTimerFactory *timerFactory);
97−
9897 virtual ~TouchRegistry();
9998
10099 // Returns a pointer to the application's TouchRegistry instance.
101− // If no instance has been allocated, null is returned.
102− static TouchRegistry *instance() { return m_instance; }
100+ static TouchRegistry *instance();
103101
104102 void update(const QTouchEvent *event);
105103
@@ -126,13 +124,31 @@ public:
126124 // but would nonetheless like to be kept up-to-date on its state.
127125 void addTouchWatcher(int touchId, QQuickItem *watcherItem);
128126
127+ // Useful for tests, where you should use fake timers
128+ void setTimerFactory(UbuntuGestures::AbstractTimerFactory *timerFactory);
129+
129130private Q_SLOTS:
130131 void rejectCandidateOwnerForTouch(int id, QQuickItem *candidate);
131132
132133private:
134+ // Only instance() can cronstruct one
135+ TouchRegistry(QObject *parent = nullptr);
136+
133137 class CandidateInfo {
134138 public:
135− bool undecided;
139+ enum {
140+ // A candidate owner that doesn't yet know for sure whether he wants the touch point
141+ // (gesture recognition is stilll going on)
142+ Undecided = 0,
143+ // A candidate owner that wants the touch but hasn't been granted it yet,
144+ // most likely because there's an undecided candidate with higher priority
145+ Requested = 1,
146+ // An item that is the interim owner of the touch, receiving QTouchEvents of it
147+ // from QQuickWindow. Ie, it's the actual touch owner from Qt's point of view.
148+ // It wants to keep its touch ownership but hasn't been granted it by TouchRegistry
149+ // yet because of undecided candidates higher up.
150+ InterimOwner = 2
151+ } state;
136152 // TODO: Prune candidates that become null and resolve ownership accordingly.
137153 QPointer<QQuickItem> item;
138154 QPointer<UbuntuGestures::CandidateInactivityTimer> inactivityTimer;
plugins/AccountsService/plugin.h+1−1
@@ -28,7 +28,7 @@ class AccountsServicePlugin : public QQmlExtensionPlugin
2828 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
2929
3030public:
31− void registerTypes(const char *uri);
31+ void registerTypes(const char *uri) override;
3232};
3333
3434#endif
plugins/Dash/CardCreator.js+44−15
@@ -16,6 +16,9 @@
1616
1717.pragma library
1818
19+// %1 is the template["card-background"] string
20+// %2 is the template["card-background"]["elements"][0]
21+// %3 is the template["card-background"]["elements"][1]
1922var kBackgroundLoaderCode = 'Loader {\n\
2023 id: backgroundLoader; \n\
2124 objectName: "backgroundLoader"; \n\
@@ -34,18 +37,14 @@ var kBackgroundLoaderCode = 'Loader {\n\
3437 objectName: "backgroundImage"; \n\
3538 source: { \n\
3639 if (cardData && typeof cardData["background"] === "string") return cardData["background"]; \n\
37− else if (template && typeof template["card-background"] === "string") return template["card-background"]; \n\
38− else return ""; \n\
40+ else return "%1"; \n\
3941 } \n\
4042 } \n\
4143 function getColor(index) { \n\
4244 if (cardData && typeof cardData["background"] === "object" \n\
4345 && (cardData["background"]["type"] === "color" || cardData["background"]["type"] === "gradient")) { \n\
4446 return cardData["background"]["elements"][index]; \n\
45− } else if (template && typeof template["card-background"] === "object" \n\
46− && (template["card-background"]["type"] === "color" || template["card-background"]["type"] === "gradient")) { \n\
47− return template["card-background"]["elements"][index]; \n\
48− } else return undefined; \n\
47+ } else return index === 0 ? %2 : %3; \n\
4948 } \n\
5049 } \n\
5150 }\n';
@@ -64,11 +63,28 @@ var kArtShapeHolderCode = 'Item { \n\
6463 active: cardData && cardData["art"] || false; \n\
6564 asynchronous: root.asynchronous; \n\
6665 visible: status == Loader.Ready; \n\
67− sourceComponent: UbuntuShape { \n\
66+ sourceComponent: Item { \n\
6867 id: artShape; \n\
6968 objectName: "artShape"; \n\
70− radius: "medium"; \n\
69+ property bool doShapeItem: components["art"]["conciergeMode"] !== true; \n\
7170 visible: image.status == Image.Ready; \n\
71+ readonly property alias image: artImage.image; \n\
72+ property alias borderSource: artShapeShape.borderSource; \n\
73+ ShaderEffectSource { \n\
74+ id: artShapeSource; \n\
75+ sourceItem: artImage; \n\
76+ anchors.centerIn: parent; \n\
77+ width: 1; \n\
78+ height: 1; \n\
79+ hideSource: doShapeItem; \n\
80+ } \n\
81+ Shape { \n\
82+ id: artShapeShape; \n\
83+ image: artShapeSource; \n\
84+ anchors.fill: parent; \n\
85+ visible: doShapeItem; \n\
86+ radius: "medium"; \n\
87+ } \n\
7288 readonly property real fixedArtShapeSizeAspect: (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) ? root.fixedArtShapeSize.width / root.fixedArtShapeSize.height : -1; \n\
7389 readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : components !== undefined ? components["art"]["aspect-ratio"] : 1; \n\
7490 Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; } \n\
@@ -87,11 +103,9 @@ var kArtShapeHolderCode = 'Item { \n\
87103 objectName: "artImage"; \n\
88104 source: cardData && cardData["art"] || ""; \n\
89105 asynchronous: root.asynchronous; \n\
90− visible: false; \n\
91106 width: %2; \n\
92107 height: %3; \n\
93108 } \n\
94− image: artImage.image; \n\
95109 } \n\
96110 } \n\
97111 }\n';
@@ -302,9 +316,11 @@ var kSummaryLabelCode = 'Label { \n\
302316
303317function cardString(template, components) {
304318 var code;
319+
320+ var templateInteractive = (template == null ? true : (template["non-interactive"] !== undefined ? !template["non-interactive"] : true)) ? "true" : "false";
321+
305322 code = 'AbstractButton { \n\
306323 id: root; \n\
307− property var template; \n\
308324 property var components; \n\
309325 property var cardData; \n\
310326 property var artShapeBorderSource: undefined; \n\
@@ -317,12 +333,12 @@ function cardString(template, components) {
317333 property bool asynchronous: true; \n\
318334 property bool showHeader: true; \n\
319335 implicitWidth: childrenRect.width; \n\
320− enabled: root.template == null ? true : (root.template["non-interactive"] !== undefined ? !root.template["non-interactive"] : true); \n\
321− \n';
336+ enabled: %1; \n\
337+ \n'.arg(templateInteractive);
322338
323339 var hasArt = components["art"] && components["art"]["field"] || false;
324340 var hasSummary = components["summary"] || false;
325− var artAndSummary = hasArt && hasSummary;
341+ var artAndSummary = hasArt && hasSummary && components["art"]["conciergeMode"] !== true;
326342 var isHorizontal = template["card-layout"] === "horizontal";
327343 var hasBackground = (!isHorizontal && (template["card-background"] || components["background"] || artAndSummary)) ||
328344 (hasSummary && (template["card-background"] || components["background"]));
@@ -335,7 +351,18 @@ function cardString(template, components) {
335351 var hasAttributes = hasTitle && components["attributes"]["field"] || false;
336352
337353 if (hasBackground) {
338− code += kBackgroundLoaderCode;
354+ var templateCardBackground = (template && typeof template["card-background"] === "string") ? template["card-background"] : "";
355+ var backgroundElements0;
356+ var backgroundElements1;
357+ if (template && typeof template["card-background"] === "object" && (template["card-background"]["type"] === "color" || template["card-background"]["type"] === "gradient")) {
358+ if (template["card-background"]["elements"][0] !== undefined) {
359+ backgroundElements0 = '"%1"'.arg(template["card-background"]["elements"][0]);
360+ }
361+ if (template["card-background"]["elements"][1] !== undefined) {
362+ backgroundElements1 = '"%1"'.arg(template["card-background"]["elements"][1]);
363+ }
364+ }
365+ code += kBackgroundLoaderCode.arg(templateCardBackground).arg(backgroundElements0).arg(backgroundElements1);
339366 }
340367
341368 if (hasArt) {
@@ -631,6 +658,8 @@ function cardString(template, components) {
631658 var implicitHeight = 'implicitHeight: ';
632659 if (hasSummary) {
633660 implicitHeight += 'summary.y + summary.height + units.gu(1);\n';
661+ } else if (headerAsOverlay) {
662+ implicitHeight += 'artShapeHolder.height;\n';
634663 } else if (hasHeaderRow) {
635664 implicitHeight += 'row.y + row.height + units.gu(1);\n';
636665 } else if (hasMascot) {
plugins/Dash/plugin.h+1−1
@@ -27,7 +27,7 @@ class DashPlugin : public QQmlExtensionPlugin
2727 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
2828
2929public:
30− void registerTypes(const char *uri);
30+ void registerTypes(const char *uri) override;
3131};
3232
3333#endif
plugins/Greeter/Unity/Launcher/CMakeLists.txt+1−1
@@ -1,5 +1,5 @@
11pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
2−pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=5)
2+pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
33pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
44
55add_definitions(-DSM_BUSNAME=systemBus)
plugins/Greeter/Unity/Launcher/launchermodelas.h+9−9
@@ -37,19 +37,19 @@ public:
3737 LauncherModel(QObject *parent = 0);
3838 ~LauncherModel();
3939
40− int rowCount(const QModelIndex &parent = QModelIndex()) const;
40+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
4141
42− QVariant data(const QModelIndex &index, int role) const;
42+ QVariant data(const QModelIndex &index, int role) const override;
4343
44− Q_INVOKABLE unity::shell::launcher::LauncherItemInterface* get(int index) const;
45− Q_INVOKABLE void move(int oldIndex, int newIndex);
46− Q_INVOKABLE void pin(const QString &appId, int index = -1);
47− Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex);
48− Q_INVOKABLE void setUser(const QString &username);
44+ Q_INVOKABLE unity::shell::launcher::LauncherItemInterface* get(int index) const override;
45+ Q_INVOKABLE void move(int oldIndex, int newIndex) override;
46+ Q_INVOKABLE void pin(const QString &appId, int index = -1) override;
47+ Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex) override;
48+ Q_INVOKABLE void setUser(const QString &username) override;
4949 Q_INVOKABLE QString getUrlForAppId(const QString &appId) const;
5050
51− unity::shell::application::ApplicationManagerInterface* applicationManager() const;
52− void setApplicationManager(unity::shell::application::ApplicationManagerInterface *appManager);
51+ unity::shell::application::ApplicationManagerInterface* applicationManager() const override;
52+ void setApplicationManager(unity::shell::application::ApplicationManagerInterface *appManager) override;
5353
5454 bool onlyPinned() const override;
5555 void setOnlyPinned(bool onlyPinned) override;
plugins/Greeter/Unity/Launcher/plugin.h+1−1
@@ -26,7 +26,7 @@ class UnityLauncherPlugin : public QQmlExtensionPlugin
2626 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
2727
2828public:
29− void registerTypes(const char *uri);
29+ void registerTypes(const char *uri) override;
3030};
3131
3232#endif
plugins/Greeter/Unity/Launcher/quicklistmodel.h+2−2
@@ -45,8 +45,8 @@ public:
4545
4646 QuickListEntry get(int index) const;
4747
48− int rowCount(const QModelIndex &parent = QModelIndex()) const;
49− QVariant data(const QModelIndex &index, int role) const;
48+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
49+ QVariant data(const QModelIndex &index, int role) const override;
5050
5151private:
5252 QList<QuickListEntry> m_list;
plugins/LightDM/UsersModel.cpp+1−1
@@ -32,7 +32,7 @@ class MangleModel : public QSortFilterProxyModel
3232public:
3333 explicit MangleModel(QObject* parent=0);
3434
35− virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
35+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
3636};
3737
3838MangleModel::MangleModel(QObject* parent)
plugins/LightDM/liblightdm/GreeterPrivate.cpp+29−5
@@ -56,7 +56,8 @@ public:
5656 this, SLOT(handleMessage(pam_handle *, QString, QLightDM::Greeter::MessageType)));
5757 // This next connect is how we pass ResponseFutures between threads
5858 connect(this, SIGNAL(showPrompt(pam_handle *, QString, QLightDM::Greeter::PromptType, QLightDM::GreeterImpl::ResponseFuture)),
59− this, SLOT(handlePrompt(pam_handle *, QString, QLightDM::Greeter::PromptType, QLightDM::GreeterImpl::ResponseFuture)));
59+ this, SLOT(handlePrompt(pam_handle *, QString, QLightDM::Greeter::PromptType, QLightDM::GreeterImpl::ResponseFuture)),
60+ Qt::BlockingQueuedConnection);
6061 }
6162
6263 ~GreeterImpl()
@@ -68,6 +69,13 @@ public:
6869 {
6970 // Clear out any existing PAM interactions first
7071 cancelPam();
72+ if (pamHandle != nullptr) {
73+ // While we were cancelling pam above, we processed Qt events.
74+ // Which may have allowed someone to call start() on us again.
75+ // In which case, we'll bail on our current start() call.
76+ // This isn't racy because it's all in the same thread.
77+ return;
78+ }
7179
7280 AppData *appData = new AppData();
7381 appData->impl = this;
@@ -79,7 +87,7 @@ public:
7987
8088 if (pam_start("lightdm", username.toUtf8(), &conversation, &pamHandle) == PAM_SUCCESS) {
8189 appData->handle = pamHandle;
82− futureWatcher.setFuture(QtConcurrent::run(authenticateWithPam, pamHandle));
90+ futureWatcher.setFuture(QtConcurrent::mapped(QList<pam_handle*>() << pamHandle, authenticateWithPam));
8391 } else {
8492 delete appData;
8593 greeterPrivate->authenticated = false;
@@ -88,7 +96,7 @@ public:
8896 }
8997 }
9098
91− static int authenticateWithPam(pam_handle* pamHandle)
99+ static int authenticateWithPam(pam_handle* const& pamHandle)
92100 {
93101 int pamStatus = pam_authenticate(pamHandle, 0);
94102 if (pamStatus == PAM_SUCCESS) {
@@ -242,11 +250,22 @@ private Q_SLOTS:
242250private:
243251 void cancelPam()
244252 {
245− // Unfortunately we can't simply cancel our QFuture because QtConcurrent::run doesn't support cancel
246253 if (pamHandle != nullptr) {
254+ QFuture<int> pamFuture = futureWatcher.future();
247255 pam_handle *handle = pamHandle;
248256 pamHandle = nullptr; // to disable normal finishPam() handling
249− while (respond(QString())); // clear our local queue of QFutures
257+ pamFuture.cancel();
258+
259+ // Note the empty loop, we just want to clear the futures queue.
260+ // Any further prompts from the pam thread will be immediately
261+ // responded to/dismissed in handlePrompt().
262+ while (respond(QString()));
263+
264+ // Now let signal/slot handling happen so the thread can finish
265+ while (!pamFuture.isFinished()) {
266+ QCoreApplication::processEvents();
267+ }
268+
250269 pam_end(handle, PAM_CONV_ERR);
251270 }
252271 }
@@ -266,6 +285,11 @@ GreeterPrivate::GreeterPrivate(Greeter* parent)
266285{
267286}
268287
288+GreeterPrivate::~GreeterPrivate()
289+{
290+ delete m_impl;
291+}
292+
269293void GreeterPrivate::handleAuthenticate()
270294{
271295 m_impl->start(authenticationUser);
plugins/LightDM/liblightdm/GreeterPrivate.h+1−1
@@ -30,7 +30,7 @@ class GreeterPrivate
3030{
3131public:
3232 explicit GreeterPrivate(Greeter* parent=0);
33− virtual ~GreeterPrivate() = default;
33+ virtual ~GreeterPrivate();
3434
3535 // These variables may not be used by all subclasses, that's no problem
3636 bool authenticated;
plugins/LightDM/liblightdm/UsersModel.h+2−2
@@ -53,8 +53,8 @@ public:
5353 BackgroundPathRole
5454 };
5555
56− int rowCount(const QModelIndex &parent) const;
57− QVariant data(const QModelIndex &index, int role) const;
56+ int rowCount(const QModelIndex &parent) const override;
57+ QVariant data(const QModelIndex &index, int role) const override;
5858
5959private:
6060 UsersModelPrivate * const d_ptr;
plugins/LightDM/plugin.h+1−1
@@ -29,7 +29,7 @@ class LightDMPlugin : public QQmlExtensionPlugin
2929 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
3030
3131public:
32− void registerTypes(const char *uri);
32+ void registerTypes(const char *uri) override;
3333};
3434
3535#endif
plugins/Lights/plugin.h+1−1
@@ -28,7 +28,7 @@ class LightsPlugin : public QQmlExtensionPlugin
2828 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
2929
3030public:
31− void registerTypes(const char *uri);
31+ void registerTypes(const char *uri) override;
3232};
3333
3434#endif
plugins/Powerd/plugin.h+1−1
@@ -29,7 +29,7 @@ class PowerdPlugin : public QQmlExtensionPlugin
2929 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
3030
3131public:
32− void registerTypes(const char *uri);
32+ void registerTypes(const char *uri) override;
3333};
3434
3535#endif
plugins/ScreenGrabber/plugin.h+1−1
@@ -27,7 +27,7 @@ class ScreenGrabberPlugin : public QQmlExtensionPlugin
2727 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
2828
2929public:
30− void registerTypes(const char *uri);
30+ void registerTypes(const char *uri) override;
3131};
3232
3333#endif // SCREENGRABBER_PLUGIN_H
plugins/SessionBroadcast/plugin.h+1−1
@@ -27,7 +27,7 @@ class SessionBroadcastPlugin : public QQmlExtensionPlugin
2727 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
2828
2929public:
30− void registerTypes(const char *uri);
30+ void registerTypes(const char *uri) override;
3131};
3232
3333#endif
plugins/Ubuntu/DownloadDaemonListener/plugin.h+2−2
@@ -31,7 +31,7 @@ class BackendPlugin : public QQmlExtensionPlugin
3131 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
3232
3333public:
34− void registerTypes(const char *uri);
35− void initializeEngine(QQmlEngine *engine, const char *uri);
34+ void registerTypes(const char *uri) override;
35+ void initializeEngine(QQmlEngine *engine, const char *uri) override;
3636};
3737#endif // BACKEND_PLUGIN_H
plugins/Ubuntu/Gestures/CMakeLists.txt+2−1
@@ -4,10 +4,11 @@ remove_definitions(-DQT_NO_KEYWORDS)
44set(UbuntuGesturesQml_SOURCES
55 plugin.cpp
66 AxisVelocityCalculator.cpp
7+ Damper.cpp
78 Direction.cpp
89 DirectionalDragArea.cpp
10+ FloatingFlickable.cpp
911 PressedOutsideNotifier.cpp
10− TimeSource.cpp
1112 TouchDispatcher.cpp
1213 TouchGate.cpp
1314)
plugins/Ubuntu/Gestures/Damper.cppnew file+24
@@ -0,0 +1,24 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "Damper.h"
18+#include <QDebug>
19+
20+QDebug operator<<(QDebug dbg, const DampedPointF &p)
21+{
22+ dbg.nospace() << "(" << p.x() << ", " << p.y() << ")";
23+ return dbg.space();
24+}
plugins/Ubuntu/Gestures/Damper.h+2
@@ -84,4 +84,6 @@ private:
8484 Damper<qreal> m_y;
8585};
8686
87+QDebug operator<<(QDebug dbg, const DampedPointF &p);
88+
8789#endif // UBUNTU_GESTURES_DAMPER_H
plugins/Ubuntu/Gestures/Direction.cpp+6−3
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013,2015 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
@@ -25,12 +25,15 @@ bool Direction::isHorizontal(Direction::Type type)
2525
2626bool Direction::isVertical(Direction::Type type)
2727{
28− return type == Direction::Upwards || type == Direction::Downwards;
28+ return type == Direction::Upwards
29+ || type == Direction::Downwards
30+ || type == Direction::Vertical;
2931}
3032
3133bool Direction::isPositive(Direction::Type type)
3234{
3335 return type == Rightwards
3436 || type == Downwards
35− || type == Horizontal;
37+ || type == Horizontal
38+ || type == Vertical;
3639}
plugins/Ubuntu/Gestures/Direction.h+3−2
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013,2015 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
@@ -34,7 +34,8 @@ public:
3434 Leftwards, // Along the negative direction of the X axis
3535 Downwards, // Along the positive direction of the Y axis
3636 Upwards, // Along the negative direction of the Y axis
37− Horizontal // Along the X axis, in any direction
37+ Horizontal, // Along the X axis, in any direction
38+ Vertical // Along the Y axis, in any direction
3839 };
3940
4041 Q_INVOKABLE static bool isHorizontal(Direction::Type type);
plugins/Ubuntu/Gestures/DirectionalDragArea.cpp+324−322
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2013-2014 Canonical, Ltd.
2+ * Copyright (C) 2013-2015 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
@@ -21,6 +21,7 @@
2121
2222#include <QQuickWindow>
2323#include <QtCore/qmath.h>
24+#include <QScreen>
2425#include <QDebug>
2526
2627#pragma GCC diagnostic push
@@ -33,6 +34,8 @@
3334#include "TouchRegistry.h"
3435#include "UnownedTouchEvent.h"
3536
37+#include "DirectionalDragArea_p.h"
38+
3639using namespace UbuntuGestures;
3740
3841#if DIRECTIONALDRAGAREA_DEBUG
@@ -40,11 +43,11 @@ using namespace UbuntuGestures;
4043#include "DebugHelpers.h"
4144
4245namespace {
43−const char *statusToString(DirectionalDragArea::Status status)
46+const char *statusToString(DirectionalDragAreaPrivate::Status status)
4447{
45− if (status == DirectionalDragArea::WaitingForTouch) {
48+ if (status == DirectionalDragAreaPrivate::WaitingForTouch) {
4649 return "WaitingForTouch";
47− } else if (status == DirectionalDragArea::Undecided) {
50+ } else if (status == DirectionalDragAreaPrivate::Undecided) {
4851 return "Undecided";
4952 } else {
5053 return "Recognized";
@@ -56,205 +59,168 @@ const char *statusToString(DirectionalDragArea::Status status)
5659#define ddaDebug(params) ((void)0)
5760#endif // DIRECTIONALDRAGAREA_DEBUG
5861
59−
6062DirectionalDragArea::DirectionalDragArea(QQuickItem *parent)
6163 : QQuickItem(parent)
62− , m_status(WaitingForTouch)
63− , m_sceneDistance(0)
64− , m_touchId(-1)
65− , m_direction(Direction::Rightwards)
66− , m_wideningAngle(0)
67− , m_wideningFactor(0)
68− , m_distanceThreshold(0)
69− , m_distanceThresholdSquared(0.)
70− , m_minSpeed(0)
71− , m_maxSilenceTime(200)
72− , m_silenceTime(0)
73− , m_compositionTime(60)
74− , m_numSamplesOnLastSpeedCheck(0)
75− , m_recognitionTimer(0)
76− , m_velocityCalculator(0)
77− , m_timeSource(new RealTimeSource)
78− , m_activeTouches(m_timeSource)
79−{
80− setRecognitionTimer(new Timer(this));
81− m_recognitionTimer->setInterval(60);
82− m_recognitionTimer->setSingleShot(false);
83−
84− m_velocityCalculator = new AxisVelocityCalculator(this);
85−
86− connect(this, &QQuickItem::enabledChanged, this, &DirectionalDragArea::giveUpIfDisabledOrInvisible);
87− connect(this, &QQuickItem::visibleChanged, this, &DirectionalDragArea::giveUpIfDisabledOrInvisible);
88−}
89−
90−Direction::Type DirectionalDragArea::direction() const
91−{
92− return m_direction;
93−}
94−
95−void DirectionalDragArea::setDirection(Direction::Type direction)
64+ , d(new DirectionalDragAreaPrivate(this))
9665{
97− if (direction != m_direction) {
98− m_direction = direction;
99− Q_EMIT directionChanged(m_direction);
100− }
101−}
66+ d->setRecognitionTimer(new Timer(this));
67+ d->recognitionTimer->setInterval(d->maxTime);
68+ d->recognitionTimer->setSingleShot(true);
10269
103−void DirectionalDragArea::setMaxDeviation(qreal value)
104−{
105− if (m_dampedScenePos.maxDelta() != value) {
106− m_dampedScenePos.setMaxDelta(value);
107− Q_EMIT maxDeviationChanged(value);
108− }
70+ connect(this, &QQuickItem::enabledChanged, d, &DirectionalDragAreaPrivate::giveUpIfDisabledOrInvisible);
71+ connect(this, &QQuickItem::visibleChanged, d, &DirectionalDragAreaPrivate::giveUpIfDisabledOrInvisible);
10972}
11073
111−qreal DirectionalDragArea::wideningAngle() const
112−{
113− return m_wideningAngle;
114−}
115−
116−void DirectionalDragArea::setWideningAngle(qreal angle)
117−{
118− if (angle == m_wideningAngle)
119− return;
120−
121− m_wideningAngle = angle;
122−
123− // wideningFactor = pow(cosine(angle), 2)
124− {
125− qreal angleRadians = angle * M_PI / 180.0;
126− m_wideningFactor = qCos(angleRadians);
127− m_wideningFactor = m_wideningFactor * m_wideningFactor;
128− }
129−
130− Q_EMIT wideningAngleChanged(angle);
131−}
132−
133−void DirectionalDragArea::setDistanceThreshold(qreal value)
74+Direction::Type DirectionalDragArea::direction() const
13475{
135− if (m_distanceThreshold != value) {
136− m_distanceThreshold = value;
137− m_distanceThresholdSquared = m_distanceThreshold * m_distanceThreshold;
138− Q_EMIT distanceThresholdChanged(value);
139− }
76+ return d->direction;
14077}
14178
142−void DirectionalDragArea::setMinSpeed(qreal value)
79+void DirectionalDragArea::setDirection(Direction::Type direction)
14380{
144− if (m_minSpeed != value) {
145− m_minSpeed = value;
146− Q_EMIT minSpeedChanged(value);
81+ if (direction != d->direction) {
82+ d->direction = direction;
83+ Q_EMIT directionChanged(d->direction);
14784 }
14885}
14986
150−void DirectionalDragArea::setMaxSilenceTime(int value)
87+void DirectionalDragAreaPrivate::setDistanceThreshold(qreal value)
15188{
152− if (m_maxSilenceTime != value) {
153− m_maxSilenceTime = value;
154− Q_EMIT maxSilenceTimeChanged(value);
89+ if (distanceThreshold != value) {
90+ distanceThreshold = value;
91+ distanceThresholdSquared = distanceThreshold * distanceThreshold;
15592 }
15693}
15794
158−void DirectionalDragArea::setCompositionTime(int value)
95+void DirectionalDragAreaPrivate::setMaxTime(int value)
15996{
160− if (m_compositionTime != value) {
161− m_compositionTime = value;
162− Q_EMIT compositionTimeChanged(value);
97+ if (maxTime != value) {
98+ maxTime = value;
99+ recognitionTimer->setInterval(maxTime);
163100 }
164101}
165102
166−void DirectionalDragArea::setRecognitionTimer(UbuntuGestures::AbstractTimer *timer)
103+void DirectionalDragAreaPrivate::setRecognitionTimer(UbuntuGestures::AbstractTimer *timer)
167104{
168105 int interval = 0;
169106 bool timerWasRunning = false;
170107 bool wasSingleShot = false;
171108
172109 // can be null when called from the constructor
173− if (m_recognitionTimer) {
174− interval = m_recognitionTimer->interval();
175− timerWasRunning = m_recognitionTimer->isRunning();
176− if (m_recognitionTimer->parent() == this) {
177− delete m_recognitionTimer;
110+ if (recognitionTimer) {
111+ interval = recognitionTimer->interval();
112+ timerWasRunning = recognitionTimer->isRunning();
113+ if (recognitionTimer->parent() == this) {
114+ delete recognitionTimer;
178115 }
179116 }
180117
181− m_recognitionTimer = timer;
118+ recognitionTimer = timer;
182119 timer->setInterval(interval);
183120 timer->setSingleShot(wasSingleShot);
184121 connect(timer, &UbuntuGestures::AbstractTimer::timeout,
185− this, &DirectionalDragArea::checkSpeed);
122+ this, &DirectionalDragAreaPrivate::rejectGesture);
186123 if (timerWasRunning) {
187− m_recognitionTimer->start();
124+ recognitionTimer->start();
188125 }
189126}
190127
191−void DirectionalDragArea::setTimeSource(const SharedTimeSource &timeSource)
128+void DirectionalDragAreaPrivate::setTimeSource(const SharedTimeSource &timeSource)
192129{
193− m_timeSource = timeSource;
194− m_velocityCalculator->setTimeSource(timeSource);
195− m_activeTouches.m_timeSource = timeSource;
130+ this->timeSource = timeSource;
131+ activeTouches.m_timeSource = timeSource;
196132}
197133
198134qreal DirectionalDragArea::distance() const
199135{
200− if (Direction::isHorizontal(m_direction)) {
201− return m_previousPos.x() - m_startPos.x();
136+ if (Direction::isHorizontal(d->direction)) {
137+ return d->publicPos.x() - d->startPos.x();
202138 } else {
203− return m_previousPos.y() - m_startPos.y();
139+ return d->publicPos.y() - d->startPos.y();
204140 }
205141}
206142
207−void DirectionalDragArea::updateSceneDistance()
143+void DirectionalDragAreaPrivate::updateSceneDistance()
208144{
209− QPointF totalMovement = m_previousScenePos - m_startScenePos;
210− m_sceneDistance = projectOntoDirectionVector(totalMovement);
145+ QPointF totalMovement = publicScenePos - startScenePos;
146+ sceneDistance = projectOntoDirectionVector(totalMovement);
211147}
212148
213149qreal DirectionalDragArea::sceneDistance() const
214150{
215− return m_sceneDistance;
151+ return d->sceneDistance;
216152}
217153
218154qreal DirectionalDragArea::touchX() const
219155{
220− return m_previousPos.x();
156+ return d->publicPos.x();
221157}
222158
223159qreal DirectionalDragArea::touchY() const
224160{
225− return m_previousPos.y();
161+ return d->publicPos.y();
226162}
227163
228164qreal DirectionalDragArea::touchSceneX() const
229165{
230− return m_previousScenePos.x();
166+ return d->publicScenePos.x();
231167}
232168
233169qreal DirectionalDragArea::touchSceneY() const
234170{
235− return m_previousScenePos.y();
171+ return d->publicScenePos.y();
172+}
173+
174+bool DirectionalDragArea::dragging() const
175+{
176+ return d->status == DirectionalDragAreaPrivate::Recognized;
177+}
178+
179+bool DirectionalDragArea::pressed() const
180+{
181+ return d->status != DirectionalDragAreaPrivate::WaitingForTouch;
182+}
183+
184+bool DirectionalDragArea::immediateRecognition() const
185+{
186+ return d->immediateRecognition;
187+}
188+
189+void DirectionalDragArea::setImmediateRecognition(bool enabled)
190+{
191+ if (d->immediateRecognition != enabled) {
192+ d->immediateRecognition = enabled;
193+ Q_EMIT immediateRecognitionChanged(enabled);
194+ }
195+}
196+
197+void DirectionalDragArea::removeTimeConstraints()
198+{
199+ d->setMaxTime(60 * 60 * 1000);
200+ d->compositionTime = 0;
201+ ddaDebug("removed time constraints");
236202}
237203
238204bool DirectionalDragArea::event(QEvent *event)
239205{
240206 if (event->type() == TouchOwnershipEvent::touchOwnershipEventType()) {
241− touchOwnershipEvent(static_cast<TouchOwnershipEvent *>(event));
207+ d->touchOwnershipEvent(static_cast<TouchOwnershipEvent *>(event));
242208 return true;
243209 } else if (event->type() == UnownedTouchEvent::unownedTouchEventType()) {
244− unownedTouchEvent(static_cast<UnownedTouchEvent *>(event));
210+ d->unownedTouchEvent(static_cast<UnownedTouchEvent *>(event));
245211 return true;
246212 } else {
247213 return QQuickItem::event(event);
248214 }
249215}
250216
251−void DirectionalDragArea::touchOwnershipEvent(TouchOwnershipEvent *event)
217+void DirectionalDragAreaPrivate::touchOwnershipEvent(TouchOwnershipEvent *event)
252218{
253219 if (event->gained()) {
254220 QVector<int> ids;
255221 ids.append(event->touchId());
256222 ddaDebug("grabbing touch");
257− grabTouchPoints(ids);
223+ q->grabTouchPoints(ids);
258224
259225 // Work around for Qt bug. If we grab a touch that is being used for mouse pointer
260226 // emulation it will cause the emulation logic to go nuts.
@@ -262,35 +228,35 @@ void DirectionalDragArea::touchOwnershipEvent(TouchOwnershipEvent *event)
262228 //
263229 // The fix for this bug has landed in Qt 5.4 (https://codereview.qt-project.org/96887)
264230 // TODO: Remove this workaround once we start using Qt 5.4
265− if (window()) {
266− QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(window());
267− if (windowPrivate->touchMouseId == event->touchId() && window()->mouseGrabberItem()) {
231+ if (q->window()) {
232+ QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(q->window());
233+ if (windowPrivate->touchMouseId == event->touchId() && q->window()->mouseGrabberItem()) {
268234 ddaDebug("removing mouse grabber");
269− window()->mouseGrabberItem()->ungrabMouse();
235+ q->window()->mouseGrabberItem()->ungrabMouse();
270236 }
271237 }
272238 } else {
273239 // We still wanna know when it ends for keeping the composition time window up-to-date
274− TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
240+ TouchRegistry::instance()->addTouchWatcher(touchId, q);
275241
276242 setStatus(WaitingForTouch);
277243 }
278244}
279245
280−void DirectionalDragArea::unownedTouchEvent(UnownedTouchEvent *unownedTouchEvent)
246+void DirectionalDragAreaPrivate::unownedTouchEvent(UnownedTouchEvent *unownedTouchEvent)
281247{
282248 QTouchEvent *event = unownedTouchEvent->touchEvent();
283249
284250 Q_ASSERT(!event->touchPointStates().testFlag(Qt::TouchPointPressed));
285251
286− ddaDebug("Unowned " << m_timeSource->msecsSinceReference() << " " << qPrintable(touchEventToString(event)));
252+ ddaDebug("Unowned " << timeSource->msecsSinceReference() << " " << qPrintable(touchEventToString(event)));
287253
288− switch (m_status) {
254+ switch (status) {
289255 case WaitingForTouch:
290256 // do nothing
291257 break;
292258 case Undecided:
293− Q_ASSERT(isEnabled() && isVisible());
259+ Q_ASSERT(q->isEnabled() && q->isVisible());
294260 unownedTouchEvent_undecided(unownedTouchEvent);
295261 break;
296262 default: // Recognized:
@@ -298,18 +264,18 @@ void DirectionalDragArea::unownedTouchEvent(UnownedTouchEvent *unownedTouchEvent
298264 break;
299265 }
300266
301− m_activeTouches.update(event);
267+ activeTouches.update(event);
302268}
303269
304−void DirectionalDragArea::unownedTouchEvent_undecided(UnownedTouchEvent *unownedTouchEvent)
270+void DirectionalDragAreaPrivate::unownedTouchEvent_undecided(UnownedTouchEvent *unownedTouchEvent)
305271{
306272 const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(unownedTouchEvent->touchEvent());
307273 if (!touchPoint) {
308− qCritical() << "DirectionalDragArea[status=Undecided]: touch " << m_touchId
274+ qCritical() << "DirectionalDragArea[status=Undecided]: touch " << touchId
309275 << "missing from UnownedTouchEvent without first reaching state Qt::TouchPointReleased. "
310276 "Considering it as released.";
311277
312− TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
278+ TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
313279 setStatus(WaitingForTouch);
314280 return;
315281 }
@@ -319,48 +285,42 @@ void DirectionalDragArea::unownedTouchEvent_undecided(UnownedTouchEvent *unowned
319285 if (touchPoint->state() == Qt::TouchPointReleased) {
320286 // touch has ended before recognition concluded
321287 ddaDebug("Touch has ended before recognition concluded");
322− TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
323− emitSignalIfTapped();
288+ TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
324289 setStatus(WaitingForTouch);
325290 return;
326291 }
327292
328− m_previousDampedScenePos.setX(m_dampedScenePos.x());
329− m_previousDampedScenePos.setY(m_dampedScenePos.y());
330− m_dampedScenePos.update(touchScenePos);
331− updateVelocityCalculator(touchScenePos);
332−
333− if (!pointInsideAllowedArea()) {
334− ddaDebug("Rejecting gesture because touch point is outside allowed area.");
335− TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
336− // We still wanna know when it ends for keeping the composition time window up-to-date
337− TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
338− setStatus(WaitingForTouch);
339− return;
340− }
293+ previousDampedScenePos.setX(dampedScenePos.x());
294+ previousDampedScenePos.setY(dampedScenePos.y());
295+ dampedScenePos.update(touchScenePos);
341296
342297 if (!movingInRightDirection()) {
343298 ddaDebug("Rejecting gesture because touch point is moving in the wrong direction.");
344− TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
299+ TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
345300 // We still wanna know when it ends for keeping the composition time window up-to-date
346− TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
301+ TouchRegistry::instance()->addTouchWatcher(touchId, q);
347302 setStatus(WaitingForTouch);
348303 return;
349304 }
350305
351− setPreviousPos(touchPoint->pos());
352− setPreviousScenePos(touchScenePos);
353−
354306 if (isWithinTouchCompositionWindow()) {
355307 // There's still time for some new touch to appear and ruin our party as it would be combined
356− // with our m_touchId one and therefore deny the possibility of a single-finger gesture.
308+ // with our touchId one and therefore deny the possibility of a single-finger gesture.
357309 ddaDebug("Sill within composition window. Let's wait more.");
358310 return;
359311 }
360312
361− if (movedFarEnough(touchScenePos)) {
362− TouchRegistry::instance()->requestTouchOwnership(m_touchId, this);
313+ if (movedFarEnoughAlongGestureAxis()) {
314+ TouchRegistry::instance()->requestTouchOwnership(touchId, q);
363315 setStatus(Recognized);
316+ setPublicPos(touchPoint->pos());
317+ setPublicScenePos(touchScenePos);
318+ } else if (isPastMaxDistance()) {
319+ ddaDebug("Rejecting gesture because it went farther than maxDistance without getting recognized.");
320+ TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
321+ // We still wanna know when it ends for keeping the composition time window up-to-date
322+ TouchRegistry::instance()->addTouchWatcher(touchId, q);
323+ setStatus(WaitingForTouch);
364324 } else {
365325 ddaDebug("Didn't move far enough yet. Let's wait more.");
366326 }
@@ -371,29 +331,29 @@ void DirectionalDragArea::touchEvent(QTouchEvent *event)
371331 // TODO: Consider when more than one touch starts in the same event (although it's not possible
372332 // with Mir's android-input). Have to track them all. Consider it a plus/bonus.
373333
374− ddaDebug(m_timeSource->msecsSinceReference() << " " << qPrintable(touchEventToString(event)));
334+ ddaDebug(d->timeSource->msecsSinceReference() << " " << qPrintable(touchEventToString(event)));
375335
376336 if (!isEnabled() || !isVisible()) {
377337 QQuickItem::touchEvent(event);
378338 return;
379339 }
380340
381− switch (m_status) {
382− case WaitingForTouch:
383− touchEvent_absent(event);
341+ switch (d->status) {
342+ case DirectionalDragAreaPrivate::WaitingForTouch:
343+ d->touchEvent_absent(event);
384344 break;
385− case Undecided:
386− touchEvent_undecided(event);
345+ case DirectionalDragAreaPrivate::Undecided:
346+ d->touchEvent_undecided(event);
387347 break;
388348 default: // Recognized:
389− touchEvent_recognized(event);
349+ d->touchEvent_recognized(event);
390350 break;
391351 }
392352
393− m_activeTouches.update(event);
353+ d->activeTouches.update(event);
394354}
395355
396−void DirectionalDragArea::touchEvent_absent(QTouchEvent *event)
356+void DirectionalDragAreaPrivate::touchEvent_absent(QTouchEvent *event)
397357{
398358 // TODO: accept/reject is for the whole event, not per touch id. See how that affects us.
399359
@@ -423,36 +383,40 @@ void DirectionalDragArea::touchEvent_absent(QTouchEvent *event)
423383 allGood = false;
424384 } else {
425385 // that's our candidate
426− m_touchId = touchPoint.id();
427386 newTouchPoint = &touchPoint;
428387 }
429388 }
430389 }
431390
391+ if (allGood) {
392+ allGood = sanityCheckRecognitionProperties();
393+ if (!allGood) {
394+ qWarning("DirectionalDragArea: recognition properties are wrongly set. Gesture recognition"
395+ " is impossible");
396+ }
397+ }
398+
432399 if (allGood) {
433400 Q_ASSERT(newTouchPoint);
434401
435− m_startPos = newTouchPoint->pos();
436− m_startScenePos = newTouchPoint->scenePos();
437− m_touchId = newTouchPoint->id();
438− m_dampedScenePos.reset(m_startScenePos);
439− m_velocityCalculator->setTrackedPosition(0.);
440− m_velocityCalculator->reset();
441− m_numSamplesOnLastSpeedCheck = 0;
442− m_silenceTime = 0;
443− setPreviousPos(m_startPos);
444− setPreviousScenePos(m_startScenePos);
402+ startPos = newTouchPoint->pos();
403+ startScenePos = newTouchPoint->scenePos();
404+ touchId = newTouchPoint->id();
405+ dampedScenePos.reset(startScenePos);
406+ setPublicPos(startPos);
407+
408+ setPublicScenePos(startScenePos);
445409 updateSceneDirectionVector();
446410
447411 if (recognitionIsDisabled()) {
448412 // Behave like a dumb TouchArea
449413 ddaDebug("Gesture recognition is disabled. Requesting touch ownership immediately.");
450− TouchRegistry::instance()->requestTouchOwnership(m_touchId, this);
414+ TouchRegistry::instance()->requestTouchOwnership(touchId, q);
451415 setStatus(Recognized);
452416 event->accept();
453417 } else {
454418 // just monitor the touch points for now.
455− TouchRegistry::instance()->addCandidateOwnerForTouch(m_touchId, this);
419+ TouchRegistry::instance()->addCandidateOwnerForTouch(touchId, q);
456420
457421 setStatus(Undecided);
458422 // Let the item below have it. We will monitor it and grab it later if a gesture
@@ -465,12 +429,11 @@ void DirectionalDragArea::touchEvent_absent(QTouchEvent *event)
465429 }
466430}
467431
468−void DirectionalDragArea::touchEvent_undecided(QTouchEvent *event)
432+void DirectionalDragAreaPrivate::touchEvent_undecided(QTouchEvent *event)
469433{
470− Q_ASSERT(event->type() == QEvent::TouchBegin);
471434 Q_ASSERT(fetchTargetTouchPoint(event) == nullptr);
472435
473− // We're not interested in new touch points. We already have our candidate (m_touchId).
436+ // We're not interested in new touch points. We already have our candidate (touchId).
474437 // But we do want to know when those new touches end for keeping the composition time
475438 // window up-to-date
476439 event->ignore();
@@ -480,63 +443,60 @@ void DirectionalDragArea::touchEvent_undecided(QTouchEvent *event)
480443 // multi-finger drags are not accepted
481444 ddaDebug("Multi-finger drags are not accepted");
482445
483− TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
446+ TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
484447 // We still wanna know when it ends for keeping the composition time window up-to-date
485− TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
448+ TouchRegistry::instance()->addTouchWatcher(touchId, q);
486449
487450 setStatus(WaitingForTouch);
488451 }
489452}
490453
491−void DirectionalDragArea::touchEvent_recognized(QTouchEvent *event)
454+void DirectionalDragAreaPrivate::touchEvent_recognized(QTouchEvent *event)
492455{
493456 const QTouchEvent::TouchPoint *touchPoint = fetchTargetTouchPoint(event);
494457
495458 if (!touchPoint) {
496− qCritical() << "DirectionalDragArea[status=Recognized]: touch " << m_touchId
459+ qCritical() << "DirectionalDragArea[status=Recognized]: touch " << touchId
497460 << "missing from QTouchEvent without first reaching state Qt::TouchPointReleased. "
498461 "Considering it as released.";
499462 setStatus(WaitingForTouch);
500463 } else {
501− setPreviousPos(touchPoint->pos());
502− setPreviousScenePos(touchPoint->scenePos());
464+ setPublicPos(touchPoint->pos());
465+ setPublicScenePos(touchPoint->scenePos());
503466
504467 if (touchPoint->state() == Qt::TouchPointReleased) {
505− emitSignalIfTapped();
506468 setStatus(WaitingForTouch);
507469 }
508470 }
509471}
510472
511−void DirectionalDragArea::watchPressedTouchPoints(const QList<QTouchEvent::TouchPoint> &touchPoints)
473+void DirectionalDragAreaPrivate::watchPressedTouchPoints(const QList<QTouchEvent::TouchPoint> &touchPoints)
512474{
513475 for (int i = 0; i < touchPoints.count(); ++i) {
514476 const QTouchEvent::TouchPoint &touchPoint = touchPoints.at(i);
515477 if (touchPoint.state() == Qt::TouchPointPressed) {
516− TouchRegistry::instance()->addTouchWatcher(touchPoint.id(), this);
478+ TouchRegistry::instance()->addTouchWatcher(touchPoint.id(), q);
517479 }
518480 }
519481}
520482
521−bool DirectionalDragArea::recognitionIsDisabled() const
483+bool DirectionalDragAreaPrivate::recognitionIsDisabled() const
522484{
523− return distanceThreshold() <= 0 && compositionTime() <= 0;
485+ return immediateRecognition || (distanceThreshold <= 0 && compositionTime <= 0);
524486}
525487
526−void DirectionalDragArea::emitSignalIfTapped()
488+bool DirectionalDragAreaPrivate::sanityCheckRecognitionProperties()
527489{
528− qint64 touchDuration = m_timeSource->msecsSinceReference() - m_activeTouches.touchStartTime(m_touchId);
529− if (touchDuration <= maxTapDuration()) {
530− Q_EMIT tapped();
531− }
490+ return recognitionIsDisabled()
491+ || (distanceThreshold < maxDistance && compositionTime < maxTime);
532492}
533493
534−const QTouchEvent::TouchPoint *DirectionalDragArea::fetchTargetTouchPoint(QTouchEvent *event)
494+const QTouchEvent::TouchPoint *DirectionalDragAreaPrivate::fetchTargetTouchPoint(QTouchEvent *event)
535495{
536496 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
537497 const QTouchEvent::TouchPoint *touchPoint = 0;
538498 for (int i = 0; i < touchPoints.size(); ++i) {
539− if (touchPoints.at(i).id() == m_touchId) {
499+ if (touchPoints.at(i).id() == touchId) {
540500 touchPoint = &touchPoints.at(i);
541501 break;
542502 }
@@ -544,39 +504,13 @@ const QTouchEvent::TouchPoint *DirectionalDragArea::fetchTargetTouchPoint(QTouch
544504 return touchPoint;
545505}
546506
547−bool DirectionalDragArea::pointInsideAllowedArea() const
507+bool DirectionalDragAreaPrivate::movingInRightDirection() const
548508{
549− // NB: Using squared values to avoid computing the square root to find
550− // the length totalMovement
551−
552− QPointF totalMovement(m_dampedScenePos.x() - m_startScenePos.x(),
553− m_dampedScenePos.y() - m_startScenePos.y());
554−
555− qreal squaredTotalMovSize = totalMovement.x() * totalMovement.x() +
556− totalMovement.y() * totalMovement.y();
557−
558− if (squaredTotalMovSize == 0.) {
559− // didn't move
560− return true;
561− }
562−
563− qreal projectedMovement = projectOntoDirectionVector(totalMovement);
564−
565−
566− qreal cosineAngleSquared = (projectedMovement * projectedMovement) / squaredTotalMovSize;
567−
568− // Same as:
569− // angle_between_movement_vector_and_gesture_direction_vector <= widening_angle
570− return cosineAngleSquared >= m_wideningFactor;
571−}
572−
573−bool DirectionalDragArea::movingInRightDirection() const
574−{
575− if (m_direction == Direction::Horizontal) {
509+ if (direction == Direction::Horizontal || direction == Direction::Vertical) {
576510 return true;
577511 } else {
578− QPointF movementVector(m_dampedScenePos.x() - m_previousDampedScenePos.x(),
579− m_dampedScenePos.y() - m_previousDampedScenePos.y());
512+ QPointF movementVector(dampedScenePos.x() - previousDampedScenePos.x(),
513+ dampedScenePos.y() - previousDampedScenePos.y());
580514
581515 qreal scalarProjection = projectOntoDirectionVector(movementVector);
582516
@@ -584,96 +518,95 @@ bool DirectionalDragArea::movingInRightDirection() const
584518 }
585519}
586520
587−bool DirectionalDragArea::movedFarEnough(const QPointF &point) const
521+bool DirectionalDragAreaPrivate::movedFarEnoughAlongGestureAxis() const
588522{
589− if (m_distanceThreshold <= 0.) {
523+ if (distanceThreshold <= 0.) {
590524 // distance threshold check is disabled
591525 return true;
592526 } else {
593− QPointF totalMovement(point.x() - m_startScenePos.x(),
594− point.y() - m_startScenePos.y());
527+ QPointF totalMovement(dampedScenePos.x() - startScenePos.x(),
528+ dampedScenePos.y() - startScenePos.y());
595529
596− qreal squaredTotalMovSize = totalMovement.x() * totalMovement.x() +
597− totalMovement.y() * totalMovement.y();
530+ qreal scalarProjection = projectOntoDirectionVector(totalMovement);
598531
599− return squaredTotalMovSize > m_distanceThresholdSquared;
532+ ddaDebug(" movedFarEnoughAlongGestureAxis: scalarProjection=" << scalarProjection
533+ << ", distanceThreshold=" << distanceThreshold);
534+
535+ if (direction == Direction::Horizontal || direction == Direction::Vertical) {
536+ return qAbs(scalarProjection) > distanceThreshold;
537+ } else {
538+ return scalarProjection > distanceThreshold;
539+ }
600540 }
601541}
602542
603−void DirectionalDragArea::checkSpeed()
543+bool DirectionalDragAreaPrivate::isPastMaxDistance() const
604544{
605− Q_ASSERT(m_status == Undecided);
545+ QPointF totalMovement(dampedScenePos.x() - startScenePos.x(),
546+ dampedScenePos.y() - startScenePos.y());
606547
607− if (m_velocityCalculator->numSamples() >= AxisVelocityCalculator::MIN_SAMPLES_NEEDED) {
608− qreal speed = qFabs(m_velocityCalculator->calculate());
609− qreal minSpeedMsecs = m_minSpeed / 1000.0;
548+ qreal squaredDistance = totalMovement.x()*totalMovement.x() + totalMovement.y()*totalMovement.y();
549+ return squaredDistance > maxDistance*maxDistance;
550+}
610551
611− if (speed < minSpeedMsecs) {
612− ddaDebug("Rejecting gesture because it's below minimum speed.");
613− TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
614− TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
615− setStatus(WaitingForTouch);
552+void DirectionalDragAreaPrivate::giveUpIfDisabledOrInvisible()
553+{
554+ if (!q->isEnabled() || !q->isVisible()) {
555+ if (status == Undecided) {
556+ TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
557+ // We still wanna know when it ends for keeping the composition time window up-to-date
558+ TouchRegistry::instance()->addTouchWatcher(touchId, q);
616559 }
617− }
618−
619− if (m_velocityCalculator->numSamples() == m_numSamplesOnLastSpeedCheck) {
620− m_silenceTime += m_recognitionTimer->interval();
621560
622− if (m_silenceTime > m_maxSilenceTime) {
623− ddaDebug("Rejecting gesture because its silence time has been exceeded.");
624− TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
625− TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
561+ if (status != WaitingForTouch) {
562+ ddaDebug("Resetting status because got disabled or made invisible");
626563 setStatus(WaitingForTouch);
627564 }
628− } else {
629− m_silenceTime = 0;
630565 }
631− m_numSamplesOnLastSpeedCheck = m_velocityCalculator->numSamples();
632566}
633567
634−void DirectionalDragArea::giveUpIfDisabledOrInvisible()
568+void DirectionalDragAreaPrivate::rejectGesture()
635569{
636− if (!isEnabled() || !isVisible()) {
637− if (m_status == Undecided) {
638− TouchRegistry::instance()->removeCandidateOwnerForTouch(m_touchId, this);
639− // We still wanna know when it ends for keeping the composition time window up-to-date
640− TouchRegistry::instance()->addTouchWatcher(m_touchId, this);
641− }
570+ if (status == Undecided) {
571+ ddaDebug("Rejecting gesture because it's taking too long to drag beyond the threshold.");
642572
643− if (m_status != WaitingForTouch) {
644− ddaDebug("Resetting status because got disabled or made invisible");
645− setStatus(WaitingForTouch);
646− }
573+ TouchRegistry::instance()->removeCandidateOwnerForTouch(touchId, q);
574+ // We still wanna know when it ends for keeping the composition time window up-to-date
575+ TouchRegistry::instance()->addTouchWatcher(touchId, q);
576+
577+ setStatus(WaitingForTouch);
647578 }
648579}
649580
650−void DirectionalDragArea::setStatus(DirectionalDragArea::Status newStatus)
581+void DirectionalDragAreaPrivate::setStatus(Status newStatus)
651582{
652− if (newStatus == m_status)
583+ if (newStatus == status)
653584 return;
654585
655− DirectionalDragArea::Status oldStatus = m_status;
586+ Status oldStatus = status;
656587
657588 if (oldStatus == Undecided) {
658− m_recognitionTimer->stop();
589+ recognitionTimer->stop();
659590 }
660591
661− m_status = newStatus;
662− Q_EMIT statusChanged(m_status);
592+ status = newStatus;
593+ Q_EMIT statusChanged(status);
663594
664595 ddaDebug(statusToString(oldStatus) << " -> " << statusToString(newStatus));
665596
666597 switch (newStatus) {
667598 case WaitingForTouch:
668− Q_EMIT draggingChanged(false);
599+ if (oldStatus == Recognized) {
600+ Q_EMIT q->draggingChanged(false);
601+ }
602+ Q_EMIT q->pressedChanged(false);
669603 break;
670604 case Undecided:
671− m_recognitionTimer->start();
672− Q_EMIT draggingChanged(true);
605+ recognitionTimer->start();
606+ Q_EMIT q->pressedChanged(true);
673607 break;
674608 case Recognized:
675− if (oldStatus == WaitingForTouch)
676− Q_EMIT draggingChanged(true);
609+ Q_EMIT q->draggingChanged(true);
677610 break;
678611 default:
679612 // no-op
@@ -681,77 +614,128 @@ void DirectionalDragArea::setStatus(DirectionalDragArea::Status newStatus)
681614 }
682615}
683616
684−void DirectionalDragArea::setPreviousPos(const QPointF &point)
617+void DirectionalDragAreaPrivate::setPublicPos(const QPointF &point)
685618{
686− bool xChanged = m_previousPos.x() != point.x();
687− bool yChanged = m_previousPos.y() != point.y();
619+ bool xChanged = publicPos.x() != point.x();
620+ bool yChanged = publicPos.y() != point.y();
688621
689− m_previousPos = point;
622+ // Public position should not get updated while the gesture is still being recognized
623+ // (ie, Undecided status).
624+ Q_ASSERT(status == WaitingForTouch || status == Recognized);
625+
626+ if (status == Recognized && !recognitionIsDisabled()) {
627+ // When the gesture finally gets recognized, the finger will likely be
628+ // reasonably far from the edge. If we made the contentX immediately
629+ // follow the finger position it would be visually unpleasant as it
630+ // would appear right next to the user's finger out of nowhere (ie,
631+ // it would jump). Instead, we make contentX go towards the user's
632+ // finger in several steps. ie., in an animated way.
633+ QPointF delta = point - publicPos;
634+ // the trick is not to go all the way (1.0) as it would cause a sudden jump
635+ publicPos.rx() += 0.4 * delta.x();
636+ publicPos.ry() += 0.4 * delta.y();
637+ } else {
638+ // no smoothing when initializing or if gesture recognition was immediate as there will
639+ // be no jump.
640+ publicPos = point;
641+ }
690642
691643 if (xChanged) {
692− Q_EMIT touchXChanged(point.x());
693− if (Direction::isHorizontal(m_direction))
694− Q_EMIT distanceChanged(distance());
644+ Q_EMIT q->touchXChanged(publicPos.x());
645+ if (Direction::isHorizontal(direction))
646+ Q_EMIT q->distanceChanged(q->distance());
695647 }
696648
697649 if (yChanged) {
698− Q_EMIT touchYChanged(point.y());
699− if (Direction::isVertical(m_direction))
700− Q_EMIT distanceChanged(distance());
650+ Q_EMIT q->touchYChanged(publicPos.y());
651+ if (Direction::isVertical(direction))
652+ Q_EMIT q->distanceChanged(q->distance());
701653 }
702654}
703655
704−void DirectionalDragArea::setPreviousScenePos(const QPointF &point)
656+void DirectionalDragAreaPrivate::setPublicScenePos(const QPointF &point)
705657{
706− bool xChanged = m_previousScenePos.x() != point.x();
707− bool yChanged = m_previousScenePos.y() != point.y();
658+ bool xChanged = publicScenePos.x() != point.x();
659+ bool yChanged = publicScenePos.y() != point.y();
708660
709661 if (!xChanged && !yChanged)
710662 return;
711663
712− qreal oldSceneDistance = sceneDistance();
713− m_previousScenePos = point;
664+ // Public position should not get updated while the gesture is still being recognized
665+ // (ie, Undecided status).
666+ Q_ASSERT(status == WaitingForTouch || status == Recognized);
667+
668+ qreal oldSceneDistance = sceneDistance;
669+
670+ if (status == Recognized && !recognitionIsDisabled()) {
671+ // When the gesture finally gets recognized, the finger will likely be
672+ // reasonably far from the edge. If we made the contentX immediately
673+ // follow the finger position it would be visually unpleasant as it
674+ // would appear right next to the user's finger out of nowhere (ie,
675+ // it would jump). Instead, we make contentX go towards the user's
676+ // finger in several steps. ie., in an animated way.
677+ QPointF delta = point - publicScenePos;
678+ // the trick is not to go all the way (1.0) as it would cause a sudden jump
679+ publicScenePos.rx() += 0.4 * delta.x();
680+ publicScenePos.ry() += 0.4 * delta.y();
681+ } else {
682+ // no smoothing when initializing or if gesture recognition was immediate as there will
683+ // be no jump.
684+ publicScenePos = point;
685+ }
686+
714687 updateSceneDistance();
715688
716− if (oldSceneDistance != sceneDistance()) {
717− Q_EMIT sceneDistanceChanged(sceneDistance());
689+ if (oldSceneDistance != sceneDistance) {
690+ Q_EMIT q->sceneDistanceChanged(sceneDistance);
718691 }
719692
720693 if (xChanged) {
721− Q_EMIT touchSceneXChanged(point.x());
694+ Q_EMIT q->touchSceneXChanged(publicScenePos.x());
722695 }
723696
724697 if (yChanged) {
725− Q_EMIT touchSceneYChanged(point.y());
698+ Q_EMIT q->touchSceneYChanged(publicScenePos.y());
726699 }
727700}
728701
729−void DirectionalDragArea::updateVelocityCalculator(const QPointF &scenePos)
702+bool DirectionalDragAreaPrivate::isWithinTouchCompositionWindow()
730703{
731− QPointF totalSceneMovement = scenePos - m_startScenePos;
704+ return
705+ compositionTime > 0 &&
706+ !activeTouches.isEmpty() &&
707+ timeSource->msecsSinceReference() <=
708+ activeTouches.mostRecentStartTime() + (qint64)compositionTime;
709+}
732710
733− qreal scalarProjection = projectOntoDirectionVector(totalSceneMovement);
711+void DirectionalDragArea::itemChange(ItemChange change, const ItemChangeData &value)
712+{
713+ if (change == QQuickItem::ItemSceneChange) {
714+ if (value.window != nullptr) {
715+ value.window->installEventFilter(TouchRegistry::instance());
734716
735− m_velocityCalculator->setTrackedPosition(scalarProjection);
717+ // TODO: Handle window->screen() changes (ie window changing screens)
718+ qreal pixelsPerMm = value.window->screen()->physicalDotsPerInch() / 25.4;
719+ d->setPixelsPerMm(pixelsPerMm);
720+ }
721+ }
736722}
737723
738−bool DirectionalDragArea::isWithinTouchCompositionWindow()
724+void DirectionalDragAreaPrivate::setPixelsPerMm(qreal pixelsPerMm)
739725{
740− return
741− compositionTime() > 0 &&
742− !m_activeTouches.isEmpty() &&
743− m_timeSource->msecsSinceReference() <=
744− m_activeTouches.mostRecentStartTime() + (qint64)compositionTime();
726+ dampedScenePos.setMaxDelta(1. * pixelsPerMm);
727+ setDistanceThreshold(4. * pixelsPerMm);
728+ maxDistance = 10. * pixelsPerMm;
745729}
746730
747731//************************** ActiveTouchesInfo **************************
748732
749−DirectionalDragArea::ActiveTouchesInfo::ActiveTouchesInfo(const SharedTimeSource &timeSource)
733+ActiveTouchesInfo::ActiveTouchesInfo(const SharedTimeSource &timeSource)
750734 : m_timeSource(timeSource)
751735{
752736}
753737
754−void DirectionalDragArea::ActiveTouchesInfo::update(QTouchEvent *event)
738+void ActiveTouchesInfo::update(QTouchEvent *event)
755739{
756740 if (!(event->touchPointStates() & (Qt::TouchPointPressed | Qt::TouchPointReleased))) {
757741 // nothing to update
@@ -773,7 +757,7 @@ void DirectionalDragArea::ActiveTouchesInfo::update(QTouchEvent *event)
773757}
774758
775759#if ACTIVETOUCHESINFO_DEBUG
776−QString DirectionalDragArea::ActiveTouchesInfo::toString()
760+QString ActiveTouchesInfo::toString()
777761{
778762 QString string = "(";
779763
@@ -791,7 +775,7 @@ QString DirectionalDragArea::ActiveTouchesInfo::toString()
791775}
792776#endif // ACTIVETOUCHESINFO_DEBUG
793777
794−void DirectionalDragArea::ActiveTouchesInfo::addTouchPoint(int touchId)
778+void ActiveTouchesInfo::addTouchPoint(int touchId)
795779{
796780 ActiveTouchInfo &activeTouchInfo = m_touchInfoPool.getEmptySlot();
797781 activeTouchInfo.id = touchId;
@@ -802,7 +786,7 @@ void DirectionalDragArea::ActiveTouchesInfo::addTouchPoint(int touchId)
802786 #endif
803787}
804788
805−qint64 DirectionalDragArea::ActiveTouchesInfo::touchStartTime(int touchId)
789+qint64 ActiveTouchesInfo::touchStartTime(int touchId)
806790{
807791 qint64 result = -1;
808792
@@ -819,7 +803,7 @@ qint64 DirectionalDragArea::ActiveTouchesInfo::touchStartTime(int touchId)
819803 return result;
820804}
821805
822−void DirectionalDragArea::ActiveTouchesInfo::removeTouchPoint(int touchId)
806+void ActiveTouchesInfo::removeTouchPoint(int touchId)
823807{
824808 m_touchInfoPool.forEach([&](Pool<ActiveTouchInfo>::Iterator &touchInfo) {
825809 if (touchId == touchInfo->id) {
@@ -835,7 +819,7 @@ void DirectionalDragArea::ActiveTouchesInfo::removeTouchPoint(int touchId)
835819 #endif
836820}
837821
838−qint64 DirectionalDragArea::ActiveTouchesInfo::mostRecentStartTime()
822+qint64 ActiveTouchesInfo::mostRecentStartTime()
839823{
840824 Q_ASSERT(!m_touchInfoPool.isEmpty());
841825
@@ -851,16 +835,17 @@ qint64 DirectionalDragArea::ActiveTouchesInfo::mostRecentStartTime()
851835 return highestStartTime;
852836}
853837
854−void DirectionalDragArea::updateSceneDirectionVector()
838+void DirectionalDragAreaPrivate::updateSceneDirectionVector()
855839{
856840 QPointF localOrigin(0., 0.);
857841 QPointF localDirection;
858− switch (m_direction) {
842+ switch (direction) {
859843 case Direction::Upwards:
860844 localDirection.rx() = 0.;
861845 localDirection.ry() = -1.;
862846 break;
863847 case Direction::Downwards:
848+ case Direction::Vertical:
864849 localDirection.rx() = 0.;
865850 localDirection.ry() = 1;
866851 break;
@@ -873,14 +858,31 @@ void DirectionalDragArea::updateSceneDirectionVector()
873858 localDirection.ry() = 0.;
874859 break;
875860 }
876− QPointF sceneOrigin = mapToScene(localOrigin);
877− QPointF sceneDirection = mapToScene(localDirection);
878− m_sceneDirectionVector = sceneDirection - sceneOrigin;
861+ QPointF sceneOrigin = q->mapToScene(localOrigin);
862+ QPointF sceneDirection = q->mapToScene(localDirection);
863+ sceneDirectionVector = sceneDirection - sceneOrigin;
864+}
865+
866+qreal DirectionalDragAreaPrivate::projectOntoDirectionVector(const QPointF &sceneVector) const
867+{
868+ // same as dot product as sceneDirectionVector is a unit vector
869+ return sceneVector.x() * sceneDirectionVector.x() +
870+ sceneVector.y() * sceneDirectionVector.y();
879871}
880872
881−qreal DirectionalDragArea::projectOntoDirectionVector(const QPointF &sceneVector) const
873+DirectionalDragAreaPrivate::DirectionalDragAreaPrivate(DirectionalDragArea *q)
874+ : q(q)
875+ , status(WaitingForTouch)
876+ , sceneDistance(0)
877+ , touchId(-1)
878+ , direction(Direction::Rightwards)
879+ , distanceThreshold(0)
880+ , distanceThresholdSquared(0.)
881+ , maxTime(400)
882+ , compositionTime(60)
883+ , immediateRecognition(false)
884+ , recognitionTimer(nullptr)
885+ , timeSource(new RealTimeSource)
886+ , activeTouches(timeSource)
882887{
883− // same as dot product as m_sceneDirectionVector is a unit vector
884− return sceneVector.x() * m_sceneDirectionVector.x() +
885− sceneVector.y() * m_sceneDirectionVector.y();
886888}
plugins/Ubuntu/Gestures/DirectionalDragArea.h+30−203
@@ -18,7 +18,6 @@
1818#define DIRECTIONAL_DRAG_AREA_H
1919
2020#include <QtQuick/QQuickItem>
21−#include "AxisVelocityCalculator.h"
2221#include "UbuntuGesturesQmlGlobal.h"
2322#include "Damper.h"
2423#include "Direction.h"
@@ -29,6 +28,7 @@
2928
3029class TouchOwnershipEvent;
3130class UnownedTouchEvent;
31+class DirectionalDragAreaPrivate;
3232
3333/*
3434 An area that detects axis-aligned single-finger drag gestures
@@ -61,95 +61,31 @@ class UBUNTUGESTURESQML_EXPORT DirectionalDragArea : public QQuickItem {
6161 Q_PROPERTY(qreal touchSceneX READ touchSceneX NOTIFY touchSceneXChanged)
6262 Q_PROPERTY(qreal touchSceneY READ touchSceneY NOTIFY touchSceneYChanged)
6363
64− // The current status of the directional drag gesture area.
65− Q_PROPERTY(Status status READ status NOTIFY statusChanged)
66−
6764 // Whether a drag gesture is taking place
68− // This will be true as long as status is Undecided or Recognized
69− // When a gesture gets rejected, dragging turns to false.
7065 Q_PROPERTY(bool dragging READ dragging NOTIFY draggingChanged)
7166
72− /////
73− // stuff that will be set in stone at some point
74−
75− // How far the touch point can move away from its expected position before
76− // it causes a rejection in the gesture recognition. This is to compensate
77− // for both noise in the touch input signal and for the natural irregularities
78− // in the finger movement.
79− // Proper value is likely device-specific.
80− Q_PROPERTY(qreal maxDeviation READ maxDeviation WRITE setMaxDeviation NOTIFY maxDeviationChanged)
81−
82− // Widening angle, in degrees
83− // It's roughly the maximum angle a touch point can make relative to the
84− // axis defined by the compoment's direction for it to be recognized as a
85− // directional drag.
86− Q_PROPERTY(qreal wideningAngle READ wideningAngle WRITE setWideningAngle
87− NOTIFY wideningAngleChanged)
88−
89− // How far a touch point has to move from its initial position in order for
90− // it to be recognized as a directional drag.
91− Q_PROPERTY(qreal distanceThreshold READ distanceThreshold WRITE setDistanceThreshold
92− NOTIFY distanceThresholdChanged)
93−
94− // Minimum speed a gesture needs to have in order to be recognized as a
95− // directional drag.
96− // In pixels per second
97− Q_PROPERTY(qreal minSpeed READ minSpeed WRITE setMinSpeed NOTIFY minSpeedChanged)
98−
99− // A gesture will be rejected if more than maxSilenceTime milliseconds has
100− // passed since we last got an input event from it (during Undecided state).
101− //
102− // Silence (i.e., lack of new input events) doesn't necessarily mean that the user's
103− // finger is still (zero drag speed). In some cases the finger might be moving but
104− // the driver's high noise filtering might cause those silence periods, specially
105− // in the moments succeeding a press (talking about Galaxy Nexus here).
106− Q_PROPERTY(int maxSilenceTime READ maxSilenceTime
107− WRITE setMaxSilenceTime
108− NOTIFY maxSilenceTimeChanged)
67+ // Whether the drag area is pressed.
68+ Q_PROPERTY(bool pressed READ pressed NOTIFY pressedChanged)
10969
70+ // Whether a gesture should be Recognized as soon a touch lands on the area.
71+ // With this property enabled it will work pretty much like a MultiPointTouchArea,
72+ // just with a different API.
11073 //
111− /////
112−
113− // Maximum time (in milliseconds) after the start of a given touch point where
114− // subsequent touch starts are grouped with the first one into an N-touches gesture
115− // (e.g. a two-fingers tap or drag).
116− Q_PROPERTY(int compositionTime READ compositionTime
117− WRITE setCompositionTime
118− NOTIFY compositionTimeChanged)
74+ // It's false by default. In most cases you will not want that enabled.
75+ Q_PROPERTY(bool immediateRecognition
76+ READ immediateRecognition
77+ WRITE setImmediateRecognition
78+ NOTIFY immediateRecognitionChanged)
11979
12080 Q_ENUMS(Direction)
121− Q_ENUMS(Status)
12281public:
12382 DirectionalDragArea(QQuickItem *parent = 0);
12483
12584 Direction::Type direction() const;
12685 void setDirection(Direction::Type);
12786
128− // Describes the state of the directional drag gesture.
129− enum Status {
130− // Waiting for a new touch point to land on this area. No gesture is being processed
131− // or tracked.
132− WaitingForTouch,
133−
134− // A touch point has landed on this area but it's not know yet whether it is
135− // performing a drag in the correct direction.
136− // If it's decided that the touch point is not performing a directional drag gesture,
137− // it will be rejected/ignored and status will return to WaitingForTouch.
138− Undecided, //Recognizing,
139−
140− // There's a touch point in this area and it performed a drag in the correct
141− // direction.
142− //
143− // Once recognized, the gesture state will move back to WaitingForTouch only once
144− // that touch point ends. The gesture will remain in the Recognized state even if
145− // the touch point starts moving in other directions or halts.
146− Recognized,
147− };
148− Status status() const { return m_status; }
149−
15087 qreal distance() const;
15188 qreal sceneDistance() const;
152− void updateSceneDistance();
15389
15490 qreal touchX() const;
15591 qreal touchY() const;
@@ -157,152 +93,43 @@ public:
15793 qreal touchSceneX() const;
15894 qreal touchSceneY() const;
15995
160− bool dragging() const { return (m_status == Undecided) || (m_status == Recognized); }
161−
162− qreal maxDeviation() const { return m_dampedScenePos.maxDelta(); }
163− void setMaxDeviation(qreal value);
164−
165− qreal wideningAngle() const;
166− void setWideningAngle(qreal value);
167−
168− qreal distanceThreshold() const { return m_distanceThreshold; }
169− void setDistanceThreshold(qreal value);
170−
171− qreal minSpeed() const { return m_minSpeed; }
172− void setMinSpeed(qreal value);
96+ bool dragging() const;
17397
174− int maxSilenceTime() const { return m_maxSilenceTime; }
175− void setMaxSilenceTime(int value);
98+ bool pressed() const;
17699
177− int compositionTime() const { return m_compositionTime; }
178− void setCompositionTime(int value);
179−
180− // Replaces the existing Timer with the given one.
181− //
182− // Useful for providing a fake timer when testing.
183− void setRecognitionTimer(UbuntuGestures::AbstractTimer *timer);
184−
185− // Useful for testing, where a fake time source can be supplied
186− void setTimeSource(const UbuntuGestures::SharedTimeSource &timeSource);
100+ bool immediateRecognition() const;
101+ void setImmediateRecognition(bool enabled);
187102
188103 bool event(QEvent *e) override;
189104
190− // Maximum time, in milliseconds, between a press and a release, for a touch
191− // sequence to be considered a tap.
192− int maxTapDuration() const { return 300; }
105+ /*
106+ In qmltests, sequences of touch events are sent all at once, unlike in "real life".
107+ Also qmltests might run really slowly, e.g. when run from inside virtual machines.
108+ Thus to remove a variable that qmltests cannot really control, namely time, this
109+ function removes all constraints that are sensible to elapsed time.
110+
111+ This effectively makes the DirectionalDragArea easier to fool.
112+ */
113+ Q_INVOKABLE void removeTimeConstraints();
193114
194115Q_SIGNALS:
195116 void directionChanged(Direction::Type direction);
196− void statusChanged(Status value);
197117 void draggingChanged(bool value);
118+ void pressedChanged(bool value);
198119 void distanceChanged(qreal value);
199120 void sceneDistanceChanged(qreal value);
200− void maxDeviationChanged(qreal value);
201− void wideningAngleChanged(qreal value);
202− void distanceThresholdChanged(qreal value);
203− void minSpeedChanged(qreal value);
204− void maxSilenceTimeChanged(int value);
205− void compositionTimeChanged(int value);
206121 void touchXChanged(qreal value);
207122 void touchYChanged(qreal value);
208123 void touchSceneXChanged(qreal value);
209124 void touchSceneYChanged(qreal value);
210−
211− // TODO: I would rather not have such signal as it has nothing to do with drag gestures.
212− // Remove when no longer used or move its implementation to the QML code that uses it
213− // See maxTapDuration()
214− void tapped();
125+ void immediateRecognitionChanged(bool value);
215126
216127protected:
217− virtual void touchEvent(QTouchEvent *event);
218−
219−private Q_SLOTS:
220− void checkSpeed();
221− void giveUpIfDisabledOrInvisible();
222−
223−private:
224− void touchEvent_absent(QTouchEvent *event);
225− void touchEvent_undecided(QTouchEvent *event);
226− void touchEvent_recognized(QTouchEvent *event);
227− bool pointInsideAllowedArea() const;
228− bool movingInRightDirection() const;
229− bool movedFarEnough(const QPointF &point) const;
230− const QTouchEvent::TouchPoint *fetchTargetTouchPoint(QTouchEvent *event);
231− void setStatus(Status newStatus);
232− void setPreviousPos(const QPointF &point);
233− void setPreviousScenePos(const QPointF &point);
234− void updateVelocityCalculator(const QPointF &point);
235− bool isWithinTouchCompositionWindow();
236− void updateSceneDirectionVector();
237− // returns the scalar projection between the given vector (in scene coordinates)
238− // and m_sceneDirectionVector
239− qreal projectOntoDirectionVector(const QPointF &sceneVector) const;
240− void touchOwnershipEvent(TouchOwnershipEvent *event);
241− void unownedTouchEvent(UnownedTouchEvent *event);
242− void unownedTouchEvent_undecided(UnownedTouchEvent *unownedTouchEvent);
243− void watchPressedTouchPoints(const QList<QTouchEvent::TouchPoint> &touchPoints);
244− bool recognitionIsDisabled() const;
245− void emitSignalIfTapped();
246−
247− Status m_status;
248−
249− QPointF m_startPos;
250− QPointF m_startScenePos;
251− QPointF m_previousPos;
252− QPointF m_previousScenePos;
253− qreal m_sceneDistance;
254− int m_touchId;
255−
256− // A movement damper is used in some of the gesture recognition calculations
257− // to get rid of noise or small oscillations in the touch position.
258− DampedPointF m_dampedScenePos;
259− QPointF m_previousDampedScenePos;
260−
261− // Unit vector in scene coordinates describing the direction of the gesture recognition
262− QPointF m_sceneDirectionVector;
263−
264− Direction::Type m_direction;
265− qreal m_wideningAngle; // in degrees
266− qreal m_wideningFactor; // it's pow(cosine(m_wideningAngle), 2)
267− qreal m_distanceThreshold;
268− qreal m_distanceThresholdSquared; // it's pow(m_distanceThreshold, 2)
269− qreal m_minSpeed;
270− int m_maxSilenceTime; // in milliseconds
271− int m_silenceTime; // in milliseconds
272− int m_compositionTime; // in milliseconds
273− int m_numSamplesOnLastSpeedCheck;
274− UbuntuGestures::AbstractTimer *m_recognitionTimer;
275− AxisVelocityCalculator *m_velocityCalculator;
276−
277− UbuntuGestures::SharedTimeSource m_timeSource;
278−
279− // Information about an active touch point
280− struct ActiveTouchInfo {
281− ActiveTouchInfo() : id(-1), startTime(-1) {}
282− bool isValid() const { return id != -1; }
283− void reset() { id = -1; }
284− int id;
285− qint64 startTime;
286− };
287− class ActiveTouchesInfo {
288− public:
289− ActiveTouchesInfo(const UbuntuGestures::SharedTimeSource &timeSource);
290− void update(QTouchEvent *event);
291− qint64 touchStartTime(int id);
292− bool isEmpty() const { return m_touchInfoPool.isEmpty(); }
293− qint64 mostRecentStartTime();
294− UbuntuGestures::SharedTimeSource m_timeSource;
295− private:
296− void addTouchPoint(int touchId);
297− void removeTouchPoint(int touchId);
298− #if ACTIVETOUCHESINFO_DEBUG
299− QString toString();
300− #endif
301−
302− Pool<ActiveTouchInfo> m_touchInfoPool;
303− } m_activeTouches;
128+ void touchEvent(QTouchEvent *event) override;
129+ void itemChange(ItemChange change, const ItemChangeData &value) override;
304130
305− friend class tst_DirectionalDragArea;
131+public: // so tests can access it
132+ DirectionalDragAreaPrivate *d;
306133};
307134
308135#endif // DIRECTIONAL_DRAG_AREA_H
plugins/Ubuntu/Gestures/DirectionalDragArea_p.hnew file+167
@@ -0,0 +1,167 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef DIRECTIONAL_DRAG_AREA_PRIV_H
18+#define DIRECTIONAL_DRAG_AREA_PRIV_H
19+
20+// Information about an active touch point
21+struct UBUNTUGESTURESQML_EXPORT ActiveTouchInfo {
22+ ActiveTouchInfo() : id(-1), startTime(-1) {}
23+ bool isValid() const { return id != -1; }
24+ void reset() { id = -1; }
25+ int id;
26+ qint64 startTime;
27+};
28+class UBUNTUGESTURESQML_EXPORT ActiveTouchesInfo {
29+public:
30+ ActiveTouchesInfo(const UbuntuGestures::SharedTimeSource &timeSource);
31+ void update(QTouchEvent *event);
32+ qint64 touchStartTime(int id);
33+ bool isEmpty() const { return m_touchInfoPool.isEmpty(); }
34+ qint64 mostRecentStartTime();
35+ UbuntuGestures::SharedTimeSource m_timeSource;
36+private:
37+ void addTouchPoint(int touchId);
38+ void removeTouchPoint(int touchId);
39+ #if ACTIVETOUCHESINFO_DEBUG
40+ QString toString();
41+ #endif
42+
43+ Pool<ActiveTouchInfo> m_touchInfoPool;
44+};
45+
46+class UBUNTUGESTURESQML_EXPORT DirectionalDragAreaPrivate : public QObject {
47+ Q_OBJECT
48+
49+ Q_ENUMS(Status)
50+public:
51+ DirectionalDragAreaPrivate(DirectionalDragArea *q);
52+
53+public Q_SLOTS:
54+ void giveUpIfDisabledOrInvisible();
55+ void rejectGesture();
56+
57+public:
58+ // Describes the state of the directional drag gesture.
59+ enum Status {
60+ // Waiting for a new touch point to land on this area. No gesture is being processed
61+ // or tracked.
62+ WaitingForTouch,
63+
64+ // A touch point has landed on this area but it's not know yet whether it is
65+ // performing a drag in the correct direction.
66+ // If it's decided that the touch point is not performing a directional drag gesture,
67+ // it will be rejected/ignored and status will return to WaitingForTouch.
68+ Undecided, //Recognizing,
69+
70+ // There's a touch point in this area and it performed a drag in the correct
71+ // direction.
72+ //
73+ // Once recognized, the gesture state will move back to WaitingForTouch only once
74+ // that touch point ends. The gesture will remain in the Recognized state even if
75+ // the touch point starts moving in other directions or halts.
76+ Recognized,
77+ };
78+
79+ void touchEvent_absent(QTouchEvent *event);
80+ void touchEvent_undecided(QTouchEvent *event);
81+ void touchEvent_recognized(QTouchEvent *event);
82+ bool movingInRightDirection() const;
83+ bool movedFarEnoughAlongGestureAxis() const;
84+ bool isPastMaxDistance() const;
85+ const QTouchEvent::TouchPoint *fetchTargetTouchPoint(QTouchEvent *event);
86+ void setStatus(Status newStatus);
87+ void setPublicPos(const QPointF &point);
88+ void setPublicScenePos(const QPointF &point);
89+ bool isWithinTouchCompositionWindow();
90+ void updateSceneDirectionVector();
91+ // returns the scalar projection between the given vector (in scene coordinates)
92+ // and m_sceneDirectionVector
93+ qreal projectOntoDirectionVector(const QPointF &sceneVector) const;
94+ void touchOwnershipEvent(TouchOwnershipEvent *event);
95+ void unownedTouchEvent(UnownedTouchEvent *event);
96+ void unownedTouchEvent_undecided(UnownedTouchEvent *unownedTouchEvent);
97+ void watchPressedTouchPoints(const QList<QTouchEvent::TouchPoint> &touchPoints);
98+ bool recognitionIsDisabled() const;
99+ bool sanityCheckRecognitionProperties();
100+ void updateSceneDistance();
101+ void setMaxTime(int value);
102+ void setDistanceThreshold(qreal value);
103+ void setPixelsPerMm(qreal pixelsPerMm);
104+ QString objectName() const { return q->objectName(); }
105+
106+ // Replaces the existing Timer with the given one.
107+ //
108+ // Useful for providing a fake timer when testing.
109+ void setRecognitionTimer(UbuntuGestures::AbstractTimer *timer);
110+
111+ // Useful for testing, where a fake time source can be supplied
112+ void setTimeSource(const UbuntuGestures::SharedTimeSource &timeSource);
113+
114+ DirectionalDragArea *q;
115+
116+ // The current status of the directional drag gesture area.
117+ Status status;
118+
119+ QPointF startPos;
120+ QPointF startScenePos;
121+ qreal sceneDistance;
122+ int touchId;
123+
124+ // The touch position exposed in the public API.
125+ // It only starts to move once the gesture gets recognized.
126+ QPointF publicPos;
127+ QPointF publicScenePos;
128+
129+ // A movement damper is used in some of the gesture recognition calculations
130+ // to get rid of noise or small oscillations in the touch position.
131+ DampedPointF dampedScenePos;
132+ QPointF previousDampedScenePos;
133+
134+ // Unit vector in scene coordinates describing the direction of the gesture recognition
135+ QPointF sceneDirectionVector;
136+
137+ Direction::Type direction;
138+
139+ // How far a touch point has to move from its initial position along the gesture axis in order
140+ // for it to be recognized as a directional drag.
141+ qreal distanceThreshold;
142+ qreal distanceThresholdSquared; // it's pow(distanceThreshold, 2)
143+
144+ // Maximum time (in milliseconds) the gesture can take to go beyond the distance threshold
145+ int maxTime;
146+
147+ // Maximum distance the gesture can go without crossing the axis-aligned distance threshold
148+ qreal maxDistance;
149+
150+ // Maximum time (in milliseconds) after the start of a given touch point where
151+ // subsequent touch starts are grouped with the first one into an N-touches gesture
152+ // (e.g. a two-fingers tap or drag).
153+ int compositionTime;
154+
155+ bool immediateRecognition;
156+
157+ UbuntuGestures::AbstractTimer *recognitionTimer;
158+
159+ UbuntuGestures::SharedTimeSource timeSource;
160+
161+ ActiveTouchesInfo activeTouches;
162+
163+Q_SIGNALS:
164+ void statusChanged(Status value);
165+};
166+
167+#endif // DIRECTIONAL_DRAG_AREA_PRIV_H
plugins/Ubuntu/Gestures/FloatingFlickable.cppnew file+150
@@ -0,0 +1,150 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "FloatingFlickable.h"
18+
19+#include <private/qquickflickable_p.h>
20+#include "DirectionalDragArea.h"
21+
22+#include <QDebug>
23+
24+FloatingFlickable::FloatingFlickable(QQuickItem *parent)
25+ : QQuickItem(parent)
26+ , m_mousePressed(false)
27+{
28+ m_dragArea = new DirectionalDragArea(this);
29+ m_dragArea->setWidth(width());
30+ m_dragArea->setHeight(height());
31+ m_dragArea->setDirection(Direction::Horizontal);
32+ connect(m_dragArea, &DirectionalDragArea::touchXChanged,
33+ this, &FloatingFlickable::onDragAreaTouchPosChanged);
34+ connect(m_dragArea, &DirectionalDragArea::touchYChanged,
35+ this, &FloatingFlickable::onDragAreaTouchPosChanged);
36+ connect(m_dragArea, &DirectionalDragArea::draggingChanged,
37+ this, &FloatingFlickable::onDragAreaDraggingChanged);
38+ connect(m_dragArea, &DirectionalDragArea::directionChanged, this, &FloatingFlickable::directionChanged);
39+
40+ m_flickable = new QQuickFlickable(this);
41+ m_flickable->setEnabled(false);
42+ m_flickable->setWidth(width());
43+ m_flickable->setHeight(height());
44+ connect(m_flickable, &QQuickFlickable::contentWidthChanged, this, &FloatingFlickable::contentWidthChanged);
45+ connect(m_flickable, &QQuickFlickable::contentHeightChanged, this, &FloatingFlickable::contentHeightChanged);
46+ connect(m_flickable, &QQuickFlickable::contentXChanged, this, &FloatingFlickable::contentXChanged);
47+ connect(m_flickable, &QQuickFlickable::contentYChanged, this, &FloatingFlickable::contentYChanged);
48+
49+ connect(this, &QQuickItem::widthChanged, this, &FloatingFlickable::updateChildrenWidth);
50+ connect(this, &QQuickItem::heightChanged, this, &FloatingFlickable::updateChildrenHeight);
51+}
52+
53+qreal FloatingFlickable::contentWidth() const
54+{
55+ return m_flickable->contentWidth();
56+}
57+
58+void FloatingFlickable::setContentWidth(qreal contentWidth)
59+{
60+ m_flickable->setContentWidth(contentWidth);
61+}
62+
63+qreal FloatingFlickable::contentHeight() const
64+{
65+ return m_flickable->contentHeight();
66+}
67+
68+void FloatingFlickable::setContentHeight(qreal contentHeight)
69+{
70+ m_flickable->setContentHeight(contentHeight);
71+}
72+
73+qreal FloatingFlickable::contentX() const
74+{
75+ return m_flickable->contentX();
76+}
77+
78+void FloatingFlickable::setContentX(qreal contentX)
79+{
80+ m_flickable->setContentX(contentX);
81+}
82+
83+qreal FloatingFlickable::contentY() const
84+{
85+ return m_flickable->contentY();
86+}
87+
88+void FloatingFlickable::setContentY(qreal contentY)
89+{
90+ m_flickable->setContentY(contentY);
91+}
92+
93+Direction::Type FloatingFlickable::direction() const
94+{
95+ return m_dragArea->direction();
96+}
97+
98+void FloatingFlickable::setDirection(Direction::Type direction)
99+{
100+ m_dragArea->setDirection(direction);
101+ if (Direction::isHorizontal(direction)) {
102+ m_flickable->setFlickableDirection(QQuickFlickable::HorizontalFlick);
103+ } else {
104+ m_flickable->setFlickableDirection(QQuickFlickable::VerticalFlick);
105+ }
106+}
107+
108+void FloatingFlickable::updateChildrenWidth()
109+{
110+ m_dragArea->setWidth(width());
111+ m_flickable->setWidth(width());
112+}
113+
114+void FloatingFlickable::updateChildrenHeight()
115+{
116+ m_dragArea->setHeight(height());
117+ m_flickable->setHeight(height());
118+}
119+
120+void FloatingFlickable::onDragAreaTouchPosChanged(qreal)
121+{
122+ if (m_mousePressed) {
123+ QMouseEvent mouseEvent(QEvent::MouseMove,
124+ QPointF(m_dragArea->touchX(),m_dragArea->touchY()),
125+ Qt::NoButton, Qt::LeftButton, Qt::NoModifier);
126+
127+ QCoreApplication::sendEvent(m_flickable, &mouseEvent);
128+
129+ }
130+}
131+
132+void FloatingFlickable::onDragAreaDraggingChanged(bool dragging)
133+{
134+ if (dragging && !m_mousePressed) {
135+ QMouseEvent mouseEvent(QEvent::MouseButtonPress,
136+ QPointF(m_dragArea->touchX(),m_dragArea->touchY()),
137+ Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
138+
139+ QCoreApplication::sendEvent(m_flickable, &mouseEvent);
140+ m_mousePressed = true;
141+
142+ } else if (!dragging && m_mousePressed) {
143+ QMouseEvent mouseEvent(QEvent::MouseButtonRelease,
144+ QPointF(m_dragArea->touchX(),m_dragArea->touchY()),
145+ Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
146+
147+ QCoreApplication::sendEvent(m_flickable, &mouseEvent);
148+ m_mousePressed = false;
149+ }
150+}
plugins/Ubuntu/Gestures/FloatingFlickable.hnew file+86
@@ -0,0 +1,86 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef FLOATING_FLICKABLE_H
18+#define FLOATING_FLICKABLE_H
19+
20+#include <QQuickItem>
21+#include "UbuntuGesturesQmlGlobal.h"
22+#include "Direction.h"
23+
24+class DirectionalDragArea;
25+class QQuickFlickable;
26+
27+/*
28+ A Flickable that can be put in front of the item to be flicked and
29+ still have the item-to-be-flicked receive input events that are not flicks.
30+
31+ Ie, it's a Flickable that, input-wise, is transparent to non-flick gestures.
32+
33+ With a regular Flickable you would have to make the item-to-be-flicked a child
34+ of Flicakble to achieve the same result. FloatingFlickable has no such requirement
35+ or limitation.
36+ */
37+class UBUNTUGESTURESQML_EXPORT FloatingFlickable : public QQuickItem {
38+ Q_OBJECT
39+
40+ Q_PROPERTY(qreal contentWidth READ contentWidth WRITE setContentWidth NOTIFY contentWidthChanged)
41+ Q_PROPERTY(qreal contentHeight READ contentHeight WRITE setContentHeight NOTIFY contentHeightChanged)
42+ Q_PROPERTY(qreal contentX READ contentX WRITE setContentX NOTIFY contentXChanged)
43+ Q_PROPERTY(qreal contentY READ contentY WRITE setContentY NOTIFY contentYChanged)
44+
45+ Q_PROPERTY(Direction::Type direction READ direction WRITE setDirection NOTIFY directionChanged)
46+
47+Q_SIGNALS:
48+ void contentWidthChanged();
49+ void contentHeightChanged();
50+ void contentXChanged();
51+ void contentYChanged();
52+ void directionChanged();
53+
54+public:
55+ FloatingFlickable(QQuickItem *parent = nullptr);
56+
57+ qreal contentWidth() const;
58+ void setContentWidth(qreal contentWidth);
59+
60+ qreal contentHeight() const;
61+ void setContentHeight(qreal contentHeight);
62+
63+ qreal contentX() const;
64+ void setContentX(qreal contentX);
65+
66+ qreal contentY() const;
67+ void setContentY(qreal contentY);
68+
69+ Direction::Type direction() const;
70+ void setDirection(Direction::Type);
71+
72+private Q_SLOTS:
73+ void updateChildrenWidth();
74+ void updateChildrenHeight();
75+ void onDragAreaTouchPosChanged(qreal);
76+ void onDragAreaDraggingChanged(bool value);
77+
78+private:
79+ DirectionalDragArea *m_dragArea;
80+ QQuickFlickable *m_flickable;
81+ bool m_mousePressed;
82+
83+ friend class tst_FloatingFlickable;
84+};
85+
86+#endif // FLOATING_FLICKABLE_H
plugins/Ubuntu/Gestures/TouchDispatcher.cpp+88−53
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 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
@@ -28,8 +28,11 @@
2828#define TOUCHDISPATCHER_DEBUG 0
2929
3030#if TOUCHDISPATCHER_DEBUG
31+#define ugDebug(params) qDebug().nospace() << "[TouchDispatcher(" << this << ")] " << params
3132#include <DebugHelpers.h>
32−#endif
33+#else // TOUCHDISPATCHER_DEBUG
34+#define ugDebug(params) ((void)0)
35+#endif // TOUCHDISPATCHER_DEBUG
3336
3437TouchDispatcher::TouchDispatcher()
3538 : m_status(NoActiveTouch)
@@ -44,13 +47,12 @@ void TouchDispatcher::setTargetItem(QQuickItem *target)
4447 m_targetItem = target;
4548 if (m_status != NoActiveTouch) {
4649 qWarning("[TouchDispatcher] Changing target item in the middle of a touch stream");
47− m_status = TargetRejectedTouches;
50+ setStatus(TargetRejectedTouches);
4851 }
4952 }
5053}
5154
52−void TouchDispatcher::dispatch(QEvent::Type eventType,
53− QTouchDevice *device,
55+void TouchDispatcher::dispatch(QTouchDevice *device,
5456 Qt::KeyboardModifiers modifiers,
5557 const QList<QTouchEvent::TouchPoint> &touchPoints,
5658 QWindow *window,
@@ -61,6 +63,8 @@ void TouchDispatcher::dispatch(QEvent::Type eventType,
6163 return;
6264 }
6365
66+ QEvent::Type eventType = resolveEventType(touchPoints);
67+
6468 if (eventType == QEvent::TouchBegin) {
6569 dispatchTouchBegin(device, modifiers, touchPoints, window, timestamp);
6670
@@ -72,15 +76,13 @@ void TouchDispatcher::dispatch(QEvent::Type eventType,
7276 dispatchAsMouse(device, modifiers, touchPoints, timestamp);
7377 } else {
7478 Q_ASSERT(m_status == TargetRejectedTouches);
75− #if TOUCHDISPATCHER_DEBUG
76− qDebug() << "[TouchDispatcher] Not dispatching touch event to" << m_targetItem.data()
77− << "because it already rejected the touch stream.";
78− #endif
79+ ugDebug("Not dispatching touch event to " << m_targetItem.data()
80+ << "because it already rejected the touch stream.");
7981 // Do nothing
8082 }
8183
8284 if (eventType == QEvent::TouchEnd) {
83− m_status = NoActiveTouch;
85+ setStatus(NoActiveTouch);
8486 m_touchMouseId = -1;
8587 }
8688
@@ -103,10 +105,7 @@ void TouchDispatcher::dispatchTouchBegin(
103105 QQuickItem *targetItem = m_targetItem.data();
104106
105107 if (!targetItem->isEnabled() || !targetItem->isVisible()) {
106− #if TOUCHDISPATCHER_DEBUG
107− qDebug() << "[TouchDispatcher] Cannot dispatch touch event to" << targetItem
108− << "because it's disabled or invisible.";
109− #endif
108+ ugDebug("Cannot dispatch touch event to " << targetItem << " because it's disabled or invisible.");
110109 return;
111110 }
112111
@@ -118,62 +117,46 @@ void TouchDispatcher::dispatchTouchBegin(
118117 createQTouchEvent(QEvent::TouchBegin, device, modifiers, targetTouchPoints, window, timestamp));
119118
120119
121− #if TOUCHDISPATCHER_DEBUG
122− qDebug() << "[TouchDispatcher] dispatching" << qPrintable(touchEventToString(touchEvent.data()))
123− << "to" << targetItem;
124− #endif
120+ ugDebug("dispatching " << qPrintable(touchEventToString(touchEvent.data()))
121+ << " to " << targetItem);
125122 QCoreApplication::sendEvent(targetItem, touchEvent.data());
126123
127124
128125 if (touchEvent->isAccepted()) {
129− #if TOUCHDISPATCHER_DEBUG
130− qDebug() << "[TouchDispatcher] Item accepted the touch event.";
131− #endif
132− m_status = DeliveringTouchEvents;
126+ ugDebug("Item accepted the touch event.");
127+ setStatus(DeliveringTouchEvents);
133128 } else if (targetItem->acceptedMouseButtons() & Qt::LeftButton) {
134− #if TOUCHDISPATCHER_DEBUG
135− qDebug() << "[TouchDispatcher] Item rejected the touch event. Trying a QMouseEvent";
136− #endif
129+ ugDebug("Item rejected the touch event. Trying a QMouseEvent");
137130 // NB: Arbitrarily chose the first touch point to emulate the mouse pointer
138131 QScopedPointer<QMouseEvent> mouseEvent(
139132 touchToMouseEvent(QEvent::MouseButtonPress, targetTouchPoints.at(0), timestamp,
140133 modifiers, false /* transformNeeded */));
141134 Q_ASSERT(targetTouchPoints.at(0).state() == Qt::TouchPointPressed);
142135
143− #if TOUCHDISPATCHER_DEBUG
144− qDebug() << "[TouchDispatcher] dispatching" << qPrintable(mouseEventToString(mouseEvent.data()))
145− << "to" << m_targetItem.data();
146− #endif
136+ ugDebug("dispatching " << qPrintable(mouseEventToString(mouseEvent.data()))
137+ << " to " << m_targetItem.data());
147138 QCoreApplication::sendEvent(targetItem, mouseEvent.data());
148139 if (mouseEvent->isAccepted()) {
149− #if TOUCHDISPATCHER_DEBUG
150− qDebug() << "[TouchDispatcher] Item accepted the QMouseEvent.";
151− #endif
152− m_status = DeliveringMouseEvents;
140+ ugDebug("Item accepted the QMouseEvent.");
141+ setStatus(DeliveringMouseEvents);
153142 m_touchMouseId = targetTouchPoints.at(0).id();
154143
155144 if (checkIfDoubleClicked(timestamp)) {
156145 QScopedPointer<QMouseEvent> doubleClickEvent(
157146 touchToMouseEvent(QEvent::MouseButtonDblClick, targetTouchPoints.at(0), timestamp,
158147 modifiers, false /* transformNeeded */));
159− #if TOUCHDISPATCHER_DEBUG
160− qDebug() << "[TouchDispatcher] dispatching" << qPrintable(mouseEventToString(doubleClickEvent.data()))
161− << "to" << m_targetItem.data();
162− #endif
148+ ugDebug("dispatching " << qPrintable(mouseEventToString(doubleClickEvent.data()))
149+ << " to " << m_targetItem.data());
163150 QCoreApplication::sendEvent(targetItem, doubleClickEvent.data());
164151 }
165152
166153 } else {
167− #if TOUCHDISPATCHER_DEBUG
168− qDebug() << "[TouchDispatcher] Item rejected the QMouseEvent.";
169− #endif
170− m_status = TargetRejectedTouches;
154+ ugDebug("Item rejected the QMouseEvent.");
155+ setStatus(TargetRejectedTouches);
171156 }
172157 } else {
173− #if TOUCHDISPATCHER_DEBUG
174− qDebug() << "[TouchDispatcher] Item rejected the touch event and does not accept mouse buttons.";
175− #endif
176− m_status = TargetRejectedTouches;
158+ ugDebug("Item rejected the touch event and does not accept mouse buttons.");
159+ setStatus(TargetRejectedTouches);
177160 }
178161}
179162
@@ -194,10 +177,8 @@ void TouchDispatcher::dispatchAsTouch(QEvent::Type eventType,
194177 createQTouchEvent(eventType, device, modifiers, targetTouchPoints, window, timestamp));
195178
196179
197− #if TOUCHDISPATCHER_DEBUG
198− qDebug() << "[TouchDispatcher] dispatching" << qPrintable(touchEventToString(eventForTargetItem.data()))
199− << "to" << targetItem;
200− #endif
180+ ugDebug("dispatching " << qPrintable(touchEventToString(eventForTargetItem.data()))
181+ << " to " << targetItem);
201182 QCoreApplication::sendEvent(targetItem, eventForTargetItem.data());
202183}
203184
@@ -254,10 +235,8 @@ void TouchDispatcher::dispatchAsMouse(
254235 QScopedPointer<QMouseEvent> mouseEvent(touchToMouseEvent(eventType, *touchMouse, timestamp, modifiers,
255236 true /* transformNeeded */));
256237
257− #if TOUCHDISPATCHER_DEBUG
258− qDebug() << "[TouchDispatcher] dispatching" << qPrintable(mouseEventToString(mouseEvent.data()))
259− << "to" << m_targetItem.data();
260− #endif
238+ ugDebug("dispatching " << qPrintable(mouseEventToString(mouseEvent.data()))
239+ << " to " << m_targetItem.data());
261240 QCoreApplication::sendEvent(m_targetItem.data(), mouseEvent.data());
262241 }
263242}
@@ -365,3 +344,59 @@ bool TouchDispatcher::checkIfDoubleClicked(ulong newPressEventTimestamp)
365344
366345 return doubleClicked;
367346}
347+
348+void TouchDispatcher::setStatus(Status status)
349+{
350+ if (status != m_status) {
351+ #if TOUCHDISPATCHER_DEBUG
352+ switch (status) {
353+ case NoActiveTouch:
354+ ugDebug("status = NoActiveTouch");
355+ break;
356+ case DeliveringTouchEvents:
357+ ugDebug("status = DeliveringTouchEvents");
358+ break;
359+ case DeliveringMouseEvents:
360+ ugDebug("status = DeliveringMouseEvents");
361+ break;
362+ case TargetRejectedTouches:
363+ ugDebug("status = TargetRejectedTouches");
364+ break;
365+ default:
366+ ugDebug("status = " << status);
367+ break;
368+ }
369+ #endif
370+ m_status = status;
371+ }
372+}
373+
374+void TouchDispatcher::reset()
375+{
376+ setStatus(NoActiveTouch);
377+ m_touchMouseId = -1;
378+ m_touchMousePressTimestamp =0;
379+}
380+
381+QEvent::Type TouchDispatcher::resolveEventType(const QList<QTouchEvent::TouchPoint> &touchPoints)
382+{
383+ QEvent::Type eventType;
384+
385+ Qt::TouchPointStates eventStates = 0;
386+ for (int i = 0; i < touchPoints.count(); i++)
387+ eventStates |= touchPoints[i].state();
388+
389+ switch (eventStates) {
390+ case Qt::TouchPointPressed:
391+ eventType = QEvent::TouchBegin;
392+ break;
393+ case Qt::TouchPointReleased:
394+ eventType = QEvent::TouchEnd;
395+ break;
396+ default:
397+ eventType = QEvent::TouchUpdate;
398+ break;
399+ }
400+
401+ return eventType;
402+}
plugins/Ubuntu/Gestures/TouchDispatcher.h+16−9
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 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
@@ -36,12 +36,20 @@ public:
3636 void setTargetItem(QQuickItem *target);
3737 QQuickItem *targetItem() { return m_targetItem; }
3838
39− void dispatch(QEvent::Type eventType,
40− QTouchDevice *device,
39+ void dispatch(QTouchDevice *device,
4140 Qt::KeyboardModifiers modifiers,
4241 const QList<QTouchEvent::TouchPoint> &touchPoints,
4342 QWindow *window,
4443 ulong timestamp);
44+
45+ void reset();
46+
47+ enum Status {
48+ NoActiveTouch,
49+ DeliveringTouchEvents,
50+ DeliveringMouseEvents,
51+ TargetRejectedTouches
52+ };
4553private:
4654 void dispatchTouchBegin(
4755 QTouchDevice *device,
@@ -73,14 +81,13 @@ private:
7381
7482 bool checkIfDoubleClicked(ulong newPressEventTimestamp);
7583
84+ void setStatus(Status status);
85+
86+ static QEvent::Type resolveEventType(const QList<QTouchEvent::TouchPoint> &touchPoints);
87+
7688 QPointer<QQuickItem> m_targetItem;
7789
78− enum {
79− NoActiveTouch,
80− DeliveringTouchEvents,
81− DeliveringMouseEvents,
82− TargetRejectedTouches
83− } m_status;
90+ Status m_status;
8491
8592 int m_touchMouseId;
8693 ulong m_touchMousePressTimestamp;
plugins/Ubuntu/Gestures/TouchGate.cpp+94−60
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 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
@@ -18,13 +18,24 @@
1818
1919#include <QCoreApplication>
2020#include <QDebug>
21+#include <QQuickWindow>
2122
2223#include <TouchOwnershipEvent.h>
2324#include <TouchRegistry.h>
2425
2526#if TOUCHGATE_DEBUG
27+#define ugDebug(params) qDebug().nospace() << "[TouchGate(" << (void*)this << ")] " << params
2628#include <DebugHelpers.h>
27−#endif
29+#else // TOUCHGATE_DEBUG
30+#define ugDebug(params) ((void)0)
31+#endif // TOUCHGATE_DEBUG
32+
33+TouchGate::TouchGate(QQuickItem *parent)
34+ : QQuickItem(parent)
35+{
36+ connect(this, &QQuickItem::enabledChanged,
37+ this, &TouchGate::onEnabledChanged);
38+}
2839
2940bool TouchGate::event(QEvent *e)
3041{
@@ -38,13 +49,12 @@ bool TouchGate::event(QEvent *e)
3849
3950void TouchGate::touchEvent(QTouchEvent *event)
4051{
41− #if TOUCHGATE_DEBUG
42− qDebug() << "[TouchGate] got touch event" << qPrintable(touchEventToString(event));
43− #endif
52+ ugDebug("got touch event" << qPrintable(touchEventToString(event)));
4453 event->accept();
4554
4655 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
47− bool goodToGo = true;
56+ QList<QTouchEvent::TouchPoint> validTouchPoints;
57+ bool ownsAllTouches = true;
4858 for (int i = 0; i < touchPoints.count(); ++i) {
4959 const QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
5060
@@ -55,31 +65,50 @@ void TouchGate::touchEvent(QTouchEvent *event)
5565 TouchRegistry::instance()->requestTouchOwnership(touchPoint.id(), this);
5666 }
5767
58− goodToGo &= m_touchInfoMap.contains(touchPoint.id())
59− && m_touchInfoMap[touchPoint.id()].ownership == OwnershipGranted;
68+ if (m_touchInfoMap.contains(touchPoint.id())) {
69+ validTouchPoints.append(touchPoint);
6070
61− if (touchPoint.state() == Qt::TouchPointReleased && m_touchInfoMap.contains(touchPoint.id())) {
62− m_touchInfoMap[touchPoint.id()].ended = true;
71+ ownsAllTouches &= m_touchInfoMap[touchPoint.id()].ownership == OwnershipGranted;
72+
73+ if (touchPoint.state() == Qt::TouchPointReleased) {
74+ m_touchInfoMap[touchPoint.id()].ended = true;
75+ }
6376 }
6477
6578 }
6679
67− if (goodToGo) {
80+ if (validTouchPoints.isEmpty()) {
81+ // nothing to do.
82+ return;
83+ }
84+
85+ if (ownsAllTouches) {
6886 if (m_storedEvents.isEmpty()) {
6987 // let it pass through
70− dispatchTouchEventToTarget(event);
88+ removeTouchInfoForEndedTouches(validTouchPoints);
89+ m_dispatcher.dispatch(event->device(), event->modifiers(), validTouchPoints,
90+ event->window(), event->timestamp());
7191 } else {
7292 // Retain the event to ensure TouchGate dispatches them in order.
7393 // Otherwise the current event would come before the stored ones, which are older.
74− #if TOUCHGATE_DEBUG
75− qDebug("[TouchGate] Storing event because thouches %s are still pending ownership.",
76− qPrintable(oldestPendingTouchIdsString()));
77− #endif
78− storeTouchEvent(event);
94+ ugDebug("Storing event because thouches " << qPrintable(oldestPendingTouchIdsString())
95+ << " are still pending ownership.");
96+ storeTouchEvent(event->device(), event->modifiers(), validTouchPoints,
97+ event->window(), event->timestamp());
7998 }
8099 } else {
81100 // Retain events that have unowned touches
82− storeTouchEvent(event);
101+ storeTouchEvent(event->device(), event->modifiers(), validTouchPoints,
102+ event->window(), event->timestamp());
103+ }
104+}
105+
106+void TouchGate::itemChange(ItemChange change, const ItemChangeData &value)
107+{
108+ if (change == QQuickItem::ItemSceneChange) {
109+ if (value.window != nullptr) {
110+ value.window->installEventFilter(TouchRegistry::instance());
111+ }
83112 }
84113}
85114
@@ -88,24 +117,23 @@ void TouchGate::touchOwnershipEvent(TouchOwnershipEvent *event)
88117 // TODO: Optimization: batch those actions as TouchOwnershipEvents
89118 // might come one right after the other.
90119
91− Q_ASSERT(m_touchInfoMap.contains(event->touchId()));
120+ if (m_touchInfoMap.contains(event->touchId())) {
121+ TouchInfo &touchInfo = m_touchInfoMap[event->touchId()];
92122
93− TouchInfo &touchInfo = m_touchInfoMap[event->touchId()];
123+ if (event->gained()) {
124+ ugDebug("Got ownership of touch " << event->touchId());
125+ touchInfo.ownership = OwnershipGranted;
126+ } else {
127+ ugDebug("Lost ownership of touch " << event->touchId());
128+ m_touchInfoMap.remove(event->touchId());
129+ removeTouchFromStoredEvents(event->touchId());
130+ }
94131
95− if (event->gained()) {
96− #if TOUCHGATE_DEBUG
97− qDebug() << "[TouchGate] Got ownership of touch " << event->touchId();
98− #endif
99− touchInfo.ownership = OwnershipGranted;
132+ dispatchFullyOwnedEvents();
100133 } else {
101− #if TOUCHGATE_DEBUG
102− qDebug() << "[TouchGate] Lost ownership of touch " << event->touchId();
103− #endif
104− m_touchInfoMap.remove(event->touchId());
105− removeTouchFromStoredEvents(event->touchId());
134+ // Ignore it. It probably happened because the TouchGate got disabled
135+ // between the time it requested ownership and the time it got it.
106136 }
107−
108− dispatchFullyOwnedEvents();
109137}
110138
111139bool TouchGate::isTouchPointOwned(int touchId) const
@@ -113,14 +141,15 @@ bool TouchGate::isTouchPointOwned(int touchId) const
113141 return m_touchInfoMap[touchId].ownership == OwnershipGranted;
114142}
115143
116−void TouchGate::storeTouchEvent(const QTouchEvent *event)
144+void TouchGate::storeTouchEvent(QTouchDevice *device,
145+ Qt::KeyboardModifiers modifiers,
146+ const QList<QTouchEvent::TouchPoint> &touchPoints,
147+ QWindow *window,
148+ ulong timestamp)
117149{
118− #if TOUCHGATE_DEBUG
119− qDebug() << "[TouchGate] Storing" << qPrintable(touchEventToString(event));
120− #endif
121−
122− TouchEvent clonedEvent(event);
123− m_storedEvents.append(std::move(clonedEvent));
150+ ugDebug("Storing" << touchPoints);
151+ TouchEvent event(device, modifiers, touchPoints, window, timestamp);
152+ m_storedEvents.append(std::move(event));
124153}
125154
126155void TouchGate::removeTouchFromStoredEvents(int touchId)
@@ -193,25 +222,13 @@ void TouchGate::setTargetItem(QQuickItem *item)
193222void TouchGate::dispatchTouchEventToTarget(const TouchEvent &event)
194223{
195224 removeTouchInfoForEndedTouches(event.touchPoints);
196− m_dispatcher.dispatch(event.eventType,
197− event.device,
225+ m_dispatcher.dispatch(event.device,
198226 event.modifiers,
199227 event.touchPoints,
200228 event.window,
201229 event.timestamp);
202230}
203231
204−void TouchGate::dispatchTouchEventToTarget(QTouchEvent* event)
205−{
206− removeTouchInfoForEndedTouches(event->touchPoints());
207− m_dispatcher.dispatch(event->type(),
208− event->device(),
209− event->modifiers(),
210− event->touchPoints(),
211− event->window(),
212− event->timestamp());
213−}
214−
215232void TouchGate::removeTouchInfoForEndedTouches(const QList<QTouchEvent::TouchPoint> &touchPoints)
216233{
217234 for (int i = 0; i < touchPoints.size(); ++i) {\
@@ -226,14 +243,31 @@ void TouchGate::removeTouchInfoForEndedTouches(const QList<QTouchEvent::TouchPoi
226243 }
227244}
228245
229−TouchGate::TouchEvent::TouchEvent(const QTouchEvent *event)
230− : eventType(event->type())
231− , device(event->device())
232− , modifiers(event->modifiers())
233− , touchPoints(event->touchPoints())
234− , target(qobject_cast<QQuickItem*>(event->target()))
235− , window(event->window())
236− , timestamp(event->timestamp())
246+void TouchGate::onEnabledChanged()
247+{
248+ ugDebug(" enabled = " << isEnabled());
249+ if (!isEnabled()) {
250+ reset();
251+ }
252+}
253+
254+void TouchGate::reset()
255+{
256+ m_storedEvents.clear();
257+ m_touchInfoMap.clear();
258+ m_dispatcher.reset();
259+}
260+
261+TouchGate::TouchEvent::TouchEvent(QTouchDevice *device,
262+ Qt::KeyboardModifiers modifiers,
263+ const QList<QTouchEvent::TouchPoint> &touchPoints,
264+ QWindow *window,
265+ ulong timestamp)
266+ : device(device)
267+ , modifiers(modifiers)
268+ , touchPoints(touchPoints)
269+ , window(window)
270+ , timestamp(timestamp)
237271{
238272}
239273
plugins/Ubuntu/Gestures/TouchGate.h+19−5
@@ -45,6 +45,8 @@ class UBUNTUGESTURESQML_EXPORT TouchGate : public QQuickItem {
4545 Q_PROPERTY(QQuickItem* targetItem READ targetItem WRITE setTargetItem NOTIFY targetItemChanged)
4646
4747public:
48+ TouchGate(QQuickItem *parent = nullptr);
49+
4850 bool event(QEvent *e) override;
4951
5052 QQuickItem *targetItem() { return m_dispatcher.targetItem(); }
@@ -55,31 +57,43 @@ Q_SIGNALS:
5557
5658protected:
5759 void touchEvent(QTouchEvent *event) override;
60+ void itemChange(ItemChange change, const ItemChangeData &value) override;
61+
62+private Q_SLOTS:
63+ void onEnabledChanged();
64+
5865private:
66+ void reset();
67+
5968 class TouchEvent {
6069 public:
61− TouchEvent(const QTouchEvent *event);
70+ TouchEvent(QTouchDevice *device,
71+ Qt::KeyboardModifiers modifiers,
72+ const QList<QTouchEvent::TouchPoint> &touchPoints,
73+ QWindow *window,
74+ ulong timestamp);
6275
6376 bool removeTouch(int touchId);
6477
65− QEvent::Type eventType;
6678 QTouchDevice *device;
6779 Qt::KeyboardModifiers modifiers;
6880 QList<QTouchEvent::TouchPoint> touchPoints;
69− QQuickItem *target;
7081 QWindow *window;
7182 ulong timestamp;
7283 };
7384
7485 void touchOwnershipEvent(TouchOwnershipEvent *event);
7586 bool isTouchPointOwned(int touchId) const;
76− void storeTouchEvent(const QTouchEvent *event);
87+ void storeTouchEvent(QTouchDevice *device,
88+ Qt::KeyboardModifiers modifiers,
89+ const QList<QTouchEvent::TouchPoint> &touchPoints,
90+ QWindow *window,
91+ ulong timestamp);
7792 void removeTouchFromStoredEvents(int touchId);
7893 void dispatchFullyOwnedEvents();
7994 bool eventIsFullyOwned(const TouchEvent &event) const;
8095
8196 void dispatchTouchEventToTarget(const TouchEvent &event);
82− void dispatchTouchEventToTarget(QTouchEvent* event);
8397
8498 void removeTouchInfoForEndedTouches(const QList<QTouchEvent::TouchPoint> &touchPoints);
8599
plugins/Ubuntu/Gestures/plugin.cpp+3−1
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013,2015 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
@@ -18,6 +18,7 @@
1818#include "AxisVelocityCalculator.h"
1919#include "Direction.h"
2020#include "DirectionalDragArea.h"
21+#include "FloatingFlickable.h"
2122#include "PressedOutsideNotifier.h"
2223#include "TouchGate.h"
2324
@@ -34,6 +35,7 @@ void UbuntuGesturesQmlPlugin::registerTypes(const char *uri)
3435 qmlRegisterSingletonType<Direction>(uri, 0, 1, "Direction", directionSingleton);
3536 qmlRegisterType<DirectionalDragArea>(uri, 0, 1, "DirectionalDragArea");
3637 qmlRegisterType<AxisVelocityCalculator>(uri, 0, 1, "AxisVelocityCalculator");
38+ qmlRegisterType<FloatingFlickable>(uri, 0, 1, "FloatingFlickable");
3739 qmlRegisterType<PressedOutsideNotifier>(uri, 0, 1, "PressedOutsideNotifier");
3840 qmlRegisterType<TouchGate>(uri, 0, 1, "TouchGate");
3941}
plugins/Ubuntu/Gestures/plugin.h+1−1
@@ -24,7 +24,7 @@ class UbuntuGesturesQmlPlugin : public QQmlExtensionPlugin
2424 Q_OBJECT
2525 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
2626public:
27− void registerTypes(const char *uri);
27+ void registerTypes(const char *uri) override;
2828};
2929
3030
plugins/Ubuntu/Payments/plugin.h+1−1
@@ -25,7 +25,7 @@ class BackendPlugin : public QQmlExtensionPlugin
2525 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
2626
2727public:
28− void registerTypes(const char *uri);
28+ void registerTypes(const char *uri) override;
2929};
3030
3131#endif // PAYMENTS_PLUGIN_H
plugins/Ubuntu/SystemImage/plugin.h+1−1
@@ -25,7 +25,7 @@ class BackendPlugin : public QQmlExtensionPlugin
2525 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
2626
2727public:
28− void registerTypes(const char *uri);
28+ void registerTypes(const char *uri) override;
2929};
3030
3131#endif // SYSTEMIMAGE_PLUGIN_H
plugins/Unity/Connectivity/plugin.h+1−1
@@ -25,7 +25,7 @@ class BackendPlugin : public QQmlExtensionPlugin
2525 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
2626
2727public:
28− void registerTypes(const char *uri);
28+ void registerTypes(const char *uri) override;
2929};
3030
3131#endif // CONNECTIVITY_PLUGIN_H
plugins/Unity/DashCommunicator/plugin.h+1−1
@@ -28,7 +28,7 @@ class DashCommunicatorPlugin : public QQmlExtensionPlugin
2828 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
2929
3030public:
31− void registerTypes(const char *uri);
31+ void registerTypes(const char *uri) override;
3232};
3333
3434#endif
plugins/Unity/Indicators/indicatorsmodel.h+5−5
@@ -50,11 +50,11 @@ public:
5050 void setProfile(const QString& profile);
5151
5252 /* QAbstractItemModel */
53− QHash<int, QByteArray> roleNames() const;
54− int columnCount(const QModelIndex &parent = QModelIndex()) const;
55− QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
56− QModelIndex parent (const QModelIndex &index) const;
57− int rowCount(const QModelIndex &parent = QModelIndex()) const;
53+ QHash<int, QByteArray> roleNames() const override;
54+ int columnCount(const QModelIndex &parent = QModelIndex()) const override;
55+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
56+ QModelIndex parent (const QModelIndex &index) const override;
57+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
5858
5959
6060Q_SIGNALS:
plugins/Unity/Indicators/menucontentactivator.cpp+4−4
@@ -28,10 +28,10 @@ public:
2828 connect(&m_timer, &QTimer::timeout,
2929 this, &UnityIndicators::AbstractTimer::timeout);
3030 }
31− virtual int interval() const { return m_timer.interval(); }
32− virtual void setInterval(int msecs) { m_timer.setInterval(msecs); }
33− virtual void start() { m_timer.start(); UnityIndicators::AbstractTimer::start(); }
34− virtual void stop() { m_timer.stop(); UnityIndicators::AbstractTimer::stop(); }
31+ int interval() const override { return m_timer.interval(); }
32+ void setInterval(int msecs) override { m_timer.setInterval(msecs); }
33+ void start() override { m_timer.start(); UnityIndicators::AbstractTimer::start(); }
34+ void stop() override { m_timer.stop(); UnityIndicators::AbstractTimer::stop(); }
3535private:
3636 QTimer m_timer;
3737};
plugins/Unity/Indicators/plugin.h+1−1
@@ -27,7 +27,7 @@ class IndicatorsPlugin : public QQmlExtensionPlugin
2727 Q_OBJECT
2828 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
2929public:
30− void registerTypes(const char *uri);
30+ void registerTypes(const char *uri) override;
3131};
3232
3333#endif // INDICATORS2_PLUGIN_H
plugins/Unity/Launcher/CMakeLists.txt+1−1
@@ -1,5 +1,5 @@
11pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
2−pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=5)
2+pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
33pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
44
55add_definitions(-DSM_BUSNAME=systemBus)
plugins/Unity/Launcher/launchermodel.h+10−10
@@ -41,19 +41,19 @@ public:
4141 LauncherModel(QObject *parent = 0);
4242 ~LauncherModel();
4343
44− int rowCount(const QModelIndex &parent = QModelIndex()) const;
44+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
4545
46− QVariant data(const QModelIndex &index, int role) const;
46+ QVariant data(const QModelIndex &index, int role) const override;
4747
48− Q_INVOKABLE unity::shell::launcher::LauncherItemInterface* get(int index) const;
49− Q_INVOKABLE void move(int oldIndex, int newIndex);
50− Q_INVOKABLE void pin(const QString &appId, int index = -1);
51− Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex);
52− Q_INVOKABLE void setUser(const QString &username);
48+ Q_INVOKABLE unity::shell::launcher::LauncherItemInterface* get(int index) const override;
49+ Q_INVOKABLE void move(int oldIndex, int newIndex) override;
50+ Q_INVOKABLE void pin(const QString &appId, int index = -1) override;
51+ Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex) override;
52+ Q_INVOKABLE void setUser(const QString &username) override;
5353 Q_INVOKABLE QString getUrlForAppId(const QString &appId) const;
5454
55− unity::shell::application::ApplicationManagerInterface* applicationManager() const;
56− void setApplicationManager(unity::shell::application::ApplicationManagerInterface *appManager);
55+ unity::shell::application::ApplicationManagerInterface* applicationManager() const override;
56+ void setApplicationManager(unity::shell::application::ApplicationManagerInterface *appManager) override;
5757
5858 bool onlyPinned() const override;
5959 void setOnlyPinned(bool onlyPinned) override;
@@ -61,7 +61,7 @@ public:
6161 int findApplication(const QString &appId);
6262
6363public Q_SLOTS:
64− void requestRemove(const QString &appId);
64+ void requestRemove(const QString &appId) override;
6565 Q_INVOKABLE void refresh();
6666
6767private:
plugins/Unity/Launcher/plugin.h+1−1
@@ -28,7 +28,7 @@ class UnityLauncherPlugin : public QQmlExtensionPlugin
2828 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
2929
3030public:
31− void registerTypes(const char *uri);
31+ void registerTypes(const char *uri) override;
3232};
3333
3434#endif
plugins/Unity/Launcher/quicklistmodel.h+2−2
@@ -48,8 +48,8 @@ public:
4848
4949 QuickListEntry get(int index) const;
5050
51− int rowCount(const QModelIndex &parent = QModelIndex()) const;
52− QVariant data(const QModelIndex &index, int role) const;
51+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
52+ QVariant data(const QModelIndex &index, int role) const override;
5353
5454private:
5555 QList<QuickListEntry> m_list;
plugins/Unity/Session/plugin.h+1−1
@@ -28,7 +28,7 @@ class SessionPlugin : public QQmlExtensionPlugin
2828 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
2929
3030public:
31− void registerTypes(const char *uri);
31+ void registerTypes(const char *uri) override;
3232};
3333
3434#endif
plugins/Utils/CMakeLists.txt+3
@@ -9,13 +9,16 @@ include_directories(
99
1010set(QMLPLUGIN_SRC
1111 constants.cpp
12+ HomeKeyWatcher.cpp
1213 inputwatcher.cpp
1314 qlimitproxymodelqml.cpp
1415 unitysortfilterproxymodelqml.cpp
1516 relativetimeformatter.cpp
1617 timeformatter.cpp
18+ Timer.cpp
1719 unitymenumodelpaths.cpp
1820 windowkeysfilter.cpp
21+ windowscreenshotprovider.cpp
1922 easingcurve.cpp
2023 windowstatestorage.cpp
2124 plugin.cpp
plugins/Utils/ElapsedTimer.hnew file+51
@@ -0,0 +1,51 @@
1+/*
2+ * Copyright (C) 2015 Canonical Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it
5+ * under the terms of the GNU Lesser General Public License, as
6+ * published by the Free Software Foundation; either version 2.1 or 3.0
7+ * of the License.
8+ *
9+ * This program is distributed in the hope that it will be useful, but
10+ * WITHOUT ANY WARRANTY; without even the implied warranties of
11+ * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
12+ * PURPOSE. See the applicable version of the GNU Lesser General Public
13+ * License for more details.
14+ *
15+ * You should have received a copy of both the GNU Lesser General Public
16+ * License along with this program. If not, see <http://www.gnu.org/licenses/>
17+ */
18+
19+#ifndef UNITYUTIL_ELAPSEDTIMER_H
20+#define UNITYUTIL_ELAPSEDTIMER_H
21+
22+#include <QElapsedTimer>
23+
24+namespace UnityUtil {
25+
26+/**
27+ Interface for ElapsedTimer implementations
28+ */
29+class AbstractElapsedTimer {
30+public:
31+ virtual ~AbstractElapsedTimer() {}
32+ virtual void start() = 0;
33+ virtual qint64 msecsSinceReference() const = 0;
34+ virtual qint64 elapsed() const = 0;
35+};
36+
37+/**
38+ A QElapsedTimer wrapper
39+ */
40+class ElapsedTimer : public AbstractElapsedTimer {
41+public:
42+ void start() override { m_timer.start(); }
43+ qint64 msecsSinceReference() const override { return m_timer.msecsSinceReference(); }
44+ qint64 elapsed() const override { return m_timer.elapsed(); }
45+private:
46+ QElapsedTimer m_timer;
47+};
48+
49+} // namespace UnityUtil
50+
51+#endif // UNITYUTIL_ELAPSEDTIMER_H
plugins/Utils/HomeKeyWatcher.cppnew file+116
@@ -0,0 +1,116 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "HomeKeyWatcher.h"
18+
19+#include <QQuickWindow>
20+
21+using namespace UnityUtil;
22+
23+HomeKeyWatcher::HomeKeyWatcher(QQuickItem *parent)
24+ : HomeKeyWatcher(new Timer, new ElapsedTimer, parent)
25+{
26+}
27+
28+HomeKeyWatcher::HomeKeyWatcher(UnityUtil::AbstractTimer *timer,
29+ UnityUtil::AbstractElapsedTimer *elapsedTimer,
30+ QQuickItem *parent)
31+ : QQuickItem(parent)
32+ , m_windowBeingTouched(false)
33+ , m_homeKeyPressed(false)
34+ , m_windowLastTouchedTimer(elapsedTimer)
35+ , m_activationTimer(timer)
36+{
37+ m_windowLastTouchedTimer->start();
38+
39+ connect(this, &QQuickItem::windowChanged,
40+ this, &HomeKeyWatcher::setupFilterOnWindow);
41+
42+ connect(m_activationTimer, &UnityUtil::AbstractTimer::timeout,
43+ this, &HomeKeyWatcher::emitActivatedIfNoTouchesAround);
44+ m_activationTimer->setInterval(msecsWithoutTouches);
45+}
46+
47+HomeKeyWatcher::~HomeKeyWatcher()
48+{
49+ delete m_windowLastTouchedTimer;
50+ delete m_activationTimer;
51+}
52+
53+bool HomeKeyWatcher::eventFilter(QObject *watched, QEvent *event)
54+{
55+ Q_ASSERT(!m_filteredWindow.isNull());
56+ Q_ASSERT(watched == static_cast<QObject*>(m_filteredWindow.data()));
57+ Q_UNUSED(watched);
58+
59+ update(event);
60+
61+ // We're only monitoring, never filtering out events
62+ return false;
63+}
64+
65+void HomeKeyWatcher::update(QEvent *event)
66+{
67+ if (event->type() == QEvent::KeyPress) {
68+ QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
69+
70+ m_homeKeyPressed = true;
71+
72+ if (keyEvent->key() == Qt::Key_Super_L && !keyEvent->isAutoRepeat()
73+ && !m_windowBeingTouched
74+ && m_windowLastTouchedTimer->elapsed() >= msecsWithoutTouches) {
75+ m_activationTimer->start();
76+ }
77+
78+ } else if (event->type() == QEvent::KeyRelease) {
79+ QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
80+
81+ if (keyEvent->key() == Qt::Key_Super_L) {
82+ m_homeKeyPressed = false;
83+ }
84+
85+ } else if (event->type() == QEvent::TouchBegin) {
86+
87+ m_activationTimer->stop();
88+ m_windowBeingTouched = true;
89+
90+ } else if (event->type() == QEvent::TouchEnd) {
91+
92+ m_windowBeingTouched = false;
93+ m_windowLastTouchedTimer->start();
94+ }
95+}
96+
97+void HomeKeyWatcher::setupFilterOnWindow(QQuickWindow *window)
98+{
99+ if (!m_filteredWindow.isNull()) {
100+ m_filteredWindow->removeEventFilter(this);
101+ m_filteredWindow.clear();
102+ }
103+
104+ if (window) {
105+ window->installEventFilter(this);
106+ m_filteredWindow = window;
107+ }
108+}
109+
110+void HomeKeyWatcher::emitActivatedIfNoTouchesAround()
111+{
112+ if (!m_homeKeyPressed && !m_windowBeingTouched &&
113+ (m_windowLastTouchedTimer->elapsed() > msecsWithoutTouches)) {
114+ Q_EMIT activated();
115+ }
116+}
plugins/Utils/HomeKeyWatcher.hnew file+74
@@ -0,0 +1,74 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef UNITY_HOMEKEYWATCHER_H
18+#define UNITY_HOMEKEYWATCHER_H
19+
20+#include <QQuickItem>
21+#include <QPointer>
22+
23+#include "Timer.h"
24+#include "ElapsedTimer.h"
25+
26+/*
27+ Signals when the home key seems to be have been intentionally tapped.
28+
29+ It only says the home key has been activated if it has been tapped in isolation,
30+ that is, without being accompanied by touches on the screen. Home key taps that
31+ happen along with (or immediately after, or immediately before) touches on the
32+ screen are considered to have happened unintentionally and are thus ignored.
33+
34+ Rationale being that it's easy to accidentally hit the home key while performing
35+ a swipe from a screen edge, for instance. That's particularly the case when the
36+ home key is a capacitive key.
37+ */
38+class HomeKeyWatcher : public QQuickItem
39+{
40+ Q_OBJECT
41+public:
42+
43+ HomeKeyWatcher(QQuickItem *parent = 0);
44+
45+ // for testing
46+ HomeKeyWatcher(UnityUtil::AbstractTimer *timer,
47+ UnityUtil::AbstractElapsedTimer *elapsedTimer,
48+ QQuickItem *parent = 0);
49+
50+ virtual ~HomeKeyWatcher();
51+
52+ bool eventFilter(QObject *watched, QEvent *event) override;
53+
54+ void update(QEvent *event);
55+
56+ const qint64 msecsWithoutTouches = 150;
57+
58+Q_SIGNALS:
59+ // Emitted when the home key has been intentionally tapped
60+ void activated();
61+
62+private Q_SLOTS:
63+ void setupFilterOnWindow(QQuickWindow *window);
64+ void emitActivatedIfNoTouchesAround();
65+
66+private:
67+ QPointer<QQuickWindow> m_filteredWindow;
68+ bool m_windowBeingTouched;
69+ bool m_homeKeyPressed;
70+ UnityUtil::AbstractElapsedTimer *m_windowLastTouchedTimer;
71+ UnityUtil::AbstractTimer *m_activationTimer;
72+};
73+
74+#endif // UNITY_HOMEKEYWATCHER_H
plugins/Utils/Timer.cppnew file+59
@@ -0,0 +1,59 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "Timer.h"
18+
19+namespace UnityUtil {
20+
21+Timer::Timer(QObject *parent) : AbstractTimer(parent)
22+{
23+ m_timer.setSingleShot(true);
24+ connect(&m_timer, &QTimer::timeout, this, &AbstractTimer::timeout);
25+}
26+
27+int Timer::interval() const
28+{
29+ return m_timer.interval();
30+}
31+
32+void Timer::setInterval(int msecs)
33+{
34+ m_timer.setInterval(msecs);
35+}
36+
37+void Timer::start()
38+{
39+ m_timer.start();
40+ AbstractTimer::start();
41+}
42+
43+void Timer::stop()
44+{
45+ m_timer.stop();
46+ AbstractTimer::stop();
47+}
48+
49+bool Timer::isSingleShot() const
50+{
51+ return m_timer.isSingleShot();
52+}
53+
54+void Timer::setSingleShot(bool value)
55+{
56+ m_timer.setSingleShot(value);
57+}
58+
59+} // namespace UnityUtil
plugins/Utils/Timer.hnew file+79
@@ -0,0 +1,79 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef UNITYUTIL_TIMER_H
18+#define UNITYUTIL_TIMER_H
19+
20+#include "ElapsedTimer.h"
21+
22+#include <QObject>
23+#include <QPointer>
24+#include <QTimer>
25+
26+namespace UnityUtil {
27+
28+/** Defines an interface for a Timer. Useful for tests. */
29+class AbstractTimer : public QObject
30+{
31+ Q_OBJECT
32+public:
33+ AbstractTimer(QObject *parent) : QObject(parent), m_isRunning(false) {}
34+ virtual int interval() const = 0;
35+ virtual void setInterval(int msecs) = 0;
36+ virtual void start() { m_isRunning = true; }
37+ virtual void stop() { m_isRunning = false; }
38+ bool isRunning() const { return m_isRunning; }
39+ virtual bool isSingleShot() const = 0;
40+ virtual void setSingleShot(bool value) = 0;
41+Q_SIGNALS:
42+ void timeout();
43+private:
44+ bool m_isRunning;
45+};
46+
47+/** A QTimer wrapper */
48+class Timer : public AbstractTimer
49+{
50+ Q_OBJECT
51+public:
52+ Timer(QObject *parent = nullptr);
53+
54+ int interval() const override;
55+ void setInterval(int msecs) override;
56+ void start() override;
57+ void stop() override;
58+ bool isSingleShot() const override;
59+ void setSingleShot(bool value) override;
60+private:
61+ QTimer m_timer;
62+};
63+
64+class AbstractTimerFactory
65+{
66+public:
67+ virtual ~AbstractTimerFactory() {}
68+ virtual AbstractTimer *create(QObject *parent = nullptr) = 0;
69+};
70+
71+class TimerFactory : public AbstractTimerFactory
72+{
73+public:
74+ AbstractTimer *create(QObject *parent = nullptr) override { return new Timer(parent); }
75+};
76+
77+} // namespace UnityUtil
78+
79+#endif // UNITYUTIL_TIMER_H
plugins/Utils/plugin.cpp+7−5
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2012 Canonical, Ltd.
2+ * Copyright (C) 2012-2015 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− * Author: Michał Sawicz <michal.sawicz@canonical.com>
1715 */
1816
1917// Qt
@@ -21,11 +19,12 @@
2119#include <QDBusConnection>
2220#include <QQmlContext>
2321#include <QtQuick/QQuickWindow>
24−#include <QDebug>
2522// self
2623#include "plugin.h"
2724
2825// local
26+#include "easingcurve.h"
27+#include "HomeKeyWatcher.h"
2928#include "inputwatcher.h"
3029#include "qlimitproxymodelqml.h"
3130#include "unitysortfilterproxymodelqml.h"
@@ -33,7 +32,7 @@
3332#include "timeformatter.h"
3433#include "unitymenumodelpaths.h"
3534#include "windowkeysfilter.h"
36−#include "easingcurve.h"
35+#include "windowscreenshotprovider.h"
3736#include "windowstatestorage.h"
3837#include "constants.h"
3938
@@ -54,6 +53,7 @@ static QObject *createConstants(QQmlEngine *engine, QJSEngine *scriptEngine)
5453void UtilsPlugin::registerTypes(const char *uri)
5554{
5655 Q_ASSERT(uri == QLatin1String("Utils"));
56+ qmlRegisterType<HomeKeyWatcher>(uri, 0, 1, "HomeKeyWatcher");
5757 qmlRegisterType<QAbstractItemModel>();
5858 qmlRegisterType<QLimitProxyModelQML>(uri, 0, 1, "LimitProxyModel");
5959 qmlRegisterType<UnitySortFilterProxyModelQML>(uri, 0, 1, "UnitySortFilterProxyModel");
@@ -71,4 +71,6 @@ void UtilsPlugin::registerTypes(const char *uri)
7171void UtilsPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
7272{
7373 QQmlExtensionPlugin::initializeEngine(engine, uri);
74+
75+ engine->addImageProvider(QLatin1String("window"), new WindowScreenshotProvider);
7476}
plugins/Utils/plugin.h+2−2
@@ -28,8 +28,8 @@ class UtilsPlugin : public QQmlExtensionPlugin
2828 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
2929
3030public:
31− void registerTypes(const char *uri);
32− void initializeEngine(QQmlEngine *engine, const char *uri);
31+ void registerTypes(const char *uri) override;
32+ void initializeEngine(QQmlEngine *engine, const char *uri) override;
3333};
3434
3535#endif
plugins/Utils/qlimitproxymodelqml.h+2−2
@@ -32,8 +32,8 @@ public:
3232
3333 /* getters */
3434 int limit() const;
35− int rowCount(const QModelIndex &parent = QModelIndex()) const;
36− QHash<int, QByteArray> roleNames() const;
35+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
36+ QHash<int, QByteArray> roleNames() const override;
3737
3838 /* setters */
3939 void setModel(QAbstractItemModel *model);
plugins/Utils/qvariantlistmodel.h+6−6
@@ -54,14 +54,14 @@ public:
5454
5555 ~QVariantListModel();
5656
57− int rowCount(const QModelIndex &parent = QModelIndex()) const;
58− QModelIndex sibling(int row, int column, const QModelIndex &idx) const;
57+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
58+ QModelIndex sibling(int row, int column, const QModelIndex &idx) const override;
5959
60− QVariant data(const QModelIndex &index, int role) const;
61− bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
60+ QVariant data(const QModelIndex &index, int role) const override;
61+ bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
6262
63− bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex());
64− bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex());
63+ bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
64+ bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
6565
6666 QVariantList variantList() const;
6767 void setVariantList(const QVariantList &list);
plugins/Utils/timeformatter.h+1−1
@@ -62,7 +62,7 @@ public:
6262 GDateTimeFormatter(QObject *parent = 0);
6363
6464protected:
65− virtual QString formatTime() const;
65+ QString formatTime() const override;
6666};
6767
6868#endif
plugins/Utils/unitysortfilterproxymodelqml.h+2−2
@@ -36,12 +36,12 @@ public:
3636 Q_INVOKABLE int count();
3737 Q_INVOKABLE int findFirst(int role, const QVariant& value) const;
3838 Q_INVOKABLE int mapRowToSource(int row);
39− virtual bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const;
39+ bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
4040
4141 /* getters */
4242 int totalCount() const;
4343 bool invertMatch() const;
44− QHash<int, QByteArray> roleNames() const;
44+ QHash<int, QByteArray> roleNames() const override;
4545
4646 /* setters */
4747 void setModel(QAbstractItemModel *model);
plugins/Utils/windowkeysfilter.h+1−1
@@ -39,7 +39,7 @@ class WindowKeysFilter : public QQuickItem
3939public:
4040 WindowKeysFilter(QQuickItem *parent = 0);
4141
42− bool eventFilter(QObject *watched, QEvent *event);
42+ bool eventFilter(QObject *watched, QEvent *event) override;
4343
4444private Q_SLOTS:
4545 void setupFilterOnWindow(QQuickWindow *window);
plugins/Utils/windowscreenshotprovider.cppnew file+59
@@ -0,0 +1,59 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "windowscreenshotprovider.h"
18+
19+#include <QGuiApplication>
20+#include <QQuickWindow>
21+
22+WindowScreenshotProvider::WindowScreenshotProvider()
23+ : QQuickImageProvider(QQmlImageProviderBase::Image, 0)
24+{
25+}
26+
27+// A very simple implementation where we assume that there's only one window and that it's a
28+// QQuickWindow. Thus the id parameter is irrelevant.
29+//
30+// Idea: Make the id contain the objectName of the QQuickWindow once we care about a multi-display
31+// compositor?
32+// Strictly speaking that could be the actual QWindow::winId(), but that's mostly a
33+// meaningless arbitrary number.
34+QImage WindowScreenshotProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
35+{
36+ Q_UNUSED(id);
37+ Q_UNUSED(requestedSize);
38+
39+ QWindowList windows = QGuiApplication::topLevelWindows();
40+
41+ if (windows.count() != 1) {
42+ size->rwidth() = 0;
43+ size->rheight() = 0;
44+ return QImage();
45+ }
46+
47+ QQuickWindow *quickWindow = qobject_cast<QQuickWindow *>(windows[0]);
48+
49+ if (!quickWindow) {
50+ size->rwidth() = 0;
51+ size->rheight() = 0;
52+ return QImage();
53+ }
54+
55+ QImage image = quickWindow->grabWindow();
56+ size->rwidth() = image.width();
57+ size->rheight() = image.height();
58+ return image;
59+}
plugins/Utils/windowscreenshotprovider.hnew file+31
@@ -0,0 +1,31 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef WINDOW_SCREENSHOT_PROVIDER_H_
18+#define WINDOW_SCREENSHOT_PROVIDER_H_
19+
20+#include <QQuickImageProvider>
21+
22+class WindowScreenshotProvider : public QQuickImageProvider
23+{
24+public:
25+ WindowScreenshotProvider();
26+
27+ // id is ignored for now
28+ QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize) override;
29+};
30+
31+#endif // WINDOW_SCREENSHOT_PROVIDER_H_
plugins/Wizard/plugin.h+1−1
@@ -26,7 +26,7 @@ class WizardPlugin : public QQmlExtensionPlugin
2626 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
2727
2828public:
29− void registerTypes(const char *uri);
29+ void registerTypes(const char *uri) override;
3030};
3131
3232#endif
po/am.po+4−4
@@ -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: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-03-19 23:16+0000\n"
11+"PO-Revision-Date: 2015-05-24 14:07+0000\n"
1212"Last-Translator: samson <Unknown>\n"
1313"Language-Team: Amharic <am@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=2; plural=n > 1;\n"
18−"X-Launchpad-Export-Date: 2015-04-23 06:47+0000\n"
19−"X-Generator: Launchpad (build 17430)\n"
18+"X-Launchpad-Export-Date: 2015-05-25 06:04+0000\n"
19+"X-Generator: Launchpad (build 17514)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -371,7 +371,7 @@ msgstr "እዚህ መታ ይድርጉ ለመቀጠል"
371371
372372#: qml/Tutorial/TutorialContent.qml:81
373373msgid "Click here to finish."
374−msgstr ""
374+msgstr "ለመጨረስ እዚህ ይጫኑ"
375375
376376#: qml/Tutorial/TutorialLeftFinish.qml:24
377377msgid "These are the shortcuts to favorite apps"
po/ast.po+86−86
@@ -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: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-03-02 18:12+0000\n"
12−"Last-Translator: ivarela <ivarela@ubuntu.com>\n"
11+"PO-Revision-Date: 2015-06-11 23:39+0000\n"
12+"Last-Translator: enolp <enolp@softastur.org>\n"
1313"Language-Team: Asturian <ast@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=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-04-23 06:48+0000\n"
19−"X-Generator: Launchpad (build 17430)\n"
18+"X-Launchpad-Export-Date: 2015-06-16 11:20+0000\n"
19+"X-Generator: Launchpad (build 17557)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -25,11 +25,11 @@ msgstr "Contraseña: "
2525#: plugins/Unity/Launcher/launcheritem.cpp:45
2626#: plugins/Unity/Launcher/launcheritem.cpp:100
2727msgid "Pin shortcut"
28−msgstr "Anclar"
28+msgstr "Anclar atayu"
2929
3030#: plugins/Unity/Launcher/launcheritem.cpp:100
3131msgid "Unpin shortcut"
32−msgstr "Desanclar"
32+msgstr "Desanclar atayu"
3333
3434#: qml/Components/DelayedLockscreen.qml:49
3535msgid "Device Locked"
@@ -37,7 +37,7 @@ msgstr "Preséu bloquiáu"
3737
3838#: qml/Components/DelayedLockscreen.qml:64
3939msgid "You have been locked out due to too many failed passphrase attempts."
40−msgstr "Quedasti bloquiáu polos escesivos intentos fallíos de frase de pasu."
40+msgstr "Quedasti bloquiáu polos escesivos intentos fallíos de fras de pasu."
4141
4242#: qml/Components/DelayedLockscreen.qml:65
4343msgid "You have been locked out due to too many failed passcode attempts."
@@ -48,27 +48,27 @@ msgstr ""
4848#, qt-format
4949msgid "Please wait %1 minute and then try again…"
5050msgid_plural "Please wait %1 minutes and then try again…"
51−msgstr[0] "Espera %1 minutu y vuelvi a intentalo…"
52−msgstr[1] "Espera %1 minutos y vuelvi a intentalo…"
51+msgstr[0] "Por favor, espera %1 minutu y vuelvi a intentalo…"
52+msgstr[1] "Por favor, espera %1 minutos y vuelvi a intentalo…"
5353
5454#: qml/Components/Dialogs.qml:69
5555msgctxt "Title: Lock/Log out dialog"
5656msgid "Log out"
57−msgstr ""
57+msgstr "Zarrar sesión"
5858
5959#: qml/Components/Dialogs.qml:70
6060msgid "Are you sure you want to log out?"
61−msgstr "De xuru que quies zarrar la sesión?"
61+msgstr "¿De xuru que quies zarrar la sesión?"
6262
6363#: qml/Components/Dialogs.qml:72
6464msgctxt "Button: Lock the system"
6565msgid "Lock"
66−msgstr ""
66+msgstr "Bloquiar"
6767
6868#: qml/Components/Dialogs.qml:79
6969msgctxt "Button: Log out from the system"
7070msgid "Log Out"
71−msgstr ""
71+msgstr "Zarrar sesión"
7272
7373#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
7474#: qml/Components/Dialogs.qml:173
@@ -78,30 +78,30 @@ msgstr "Encaboxar"
7878#: qml/Components/Dialogs.qml:98
7979msgctxt "Title: Reboot/Shut down dialog"
8080msgid "Shut down"
81−msgstr ""
81+msgstr "Apagáu"
8282
8383#: qml/Components/Dialogs.qml:99
8484msgid "Are you sure you want to shut down?"
85−msgstr "De xuru que quies apagar?"
85+msgstr "¿De xuru que quies apagar?"
8686
8787#: qml/Components/Dialogs.qml:101
8888msgctxt "Button: Reboot the system"
8989msgid "Reboot"
90−msgstr ""
90+msgstr "Reaniciar"
9191
9292#: qml/Components/Dialogs.qml:109
9393msgctxt "Button: Shut down the system"
9494msgid "Shut down"
95−msgstr ""
95+msgstr "Apagar"
9696
9797#: qml/Components/Dialogs.qml:129
9898msgctxt "Title: Reboot dialog"
9999msgid "Reboot"
100−msgstr ""
100+msgstr "Reaniciu"
101101
102102#: qml/Components/Dialogs.qml:130
103103msgid "Are you sure you want to reboot?"
104−msgstr "De xuru que quies reaniciar?"
104+msgstr "¿De xuru que quies reaniciar?"
105105
106106#: qml/Components/Dialogs.qml:132
107107msgid "No"
@@ -121,18 +121,18 @@ msgid ""
121121"Are you sure you would like\n"
122122"to power off?"
123123msgstr ""
124−"De xuru que quies\n"
124+"¿De xuru que te prestaría\n"
125125"apagar?"
126126
127127#: qml/Components/Dialogs.qml:155
128128msgctxt "Button: Power off the system"
129129msgid "Power off"
130−msgstr ""
130+msgstr "Apagar"
131131
132132#: qml/Components/Dialogs.qml:164
133133msgctxt "Button: Restart the system"
134134msgid "Restart"
135−msgstr ""
135+msgstr "Reaniciar"
136136
137137#: qml/Components/Lockscreen.qml:245
138138msgid "Return to Call"
@@ -157,22 +157,22 @@ msgstr "Ver too"
157157#: qml/Dash/GenericScopeView.qml:594
158158msgctxt "Label: Hint for dash search line edit"
159159msgid "Search"
160−msgstr ""
160+msgstr "Guetar"
161161
162162#: qml/Dash/PageHeader.qml:269
163163msgctxt "Button: Open the Ubuntu Store"
164164msgid "Store"
165−msgstr ""
165+msgstr "Tienda"
166166
167167#: qml/Dash/PageHeader.qml:276
168168msgctxt "Button: Start a search in the current dash scope"
169169msgid "Search"
170−msgstr ""
170+msgstr "Guetar"
171171
172172#: qml/Dash/PageHeader.qml:286
173173msgctxt "Button: Show the current dash scope settings"
174174msgid "Settings"
175−msgstr ""
175+msgstr "Axustes"
176176
177177#: qml/Dash/PageHeader.qml:293
178178msgid "Remove from Favorites"
@@ -204,19 +204,19 @@ msgstr "Unviar"
204204
205205#: qml/Dash/PullToRefreshScopeStyle.qml:55
206206msgid "Pull to refresh…"
207−msgstr "Tirar p'anovar..."
207+msgstr "Tira p'anovar..."
208208
209209#: qml/Dash/PullToRefreshScopeStyle.qml:60
210210msgid "Release to refresh…"
211−msgstr "Soltar p'anovar..."
211+msgstr "Sueltar p'anovar..."
212212
213213#: qml/Dash/ScopeSettings/ScopeSettingBoolean.qml:43
214214msgid "Enable location data"
215−msgstr "Activar datos de llocalización"
215+msgstr "Habilitar datos d'allugamientu"
216216
217217#: qml/Dash/ScopesList.qml:66
218218msgid "Manage"
219−msgstr "Alministrar"
219+msgstr "Xestionar"
220220
221221#: qml/Dash/ScopesList.qml:112
222222msgid "Home"
@@ -232,52 +232,52 @@ msgstr "Desbloquiar"
232232
233233#: qml/Greeter/LoginList.qml:236
234234msgid "Retry"
235−msgstr ""
235+msgstr "Reintentar"
236236
237237#: qml/Greeter/LoginList.qml:237
238238msgid "Tap to unlock"
239−msgstr ""
239+msgstr "Calca pa desbloquiar"
240240
241241#: qml/Greeter/NarrowView.qml:53 qml/Wizard/Pages/passwd-confirm.qml:53
242242msgid "Sorry, incorrect passphrase."
243−msgstr "Fras de pasu incorreuta"
243+msgstr "Perdón, fras de pasu incorreuta"
244244
245245#: qml/Greeter/NarrowView.qml:54 qml/Wizard/Pages/passwd-confirm.qml:54
246246msgid "Sorry, incorrect passcode."
247−msgstr "Códigu de pasu incorreutu"
247+msgstr "Perdón, códigu de pasu incorreutu."
248248
249249#: qml/Greeter/NarrowView.qml:55
250250msgid "This will be your last attempt."
251−msgstr "Esti ye'l caberu intentu."
251+msgstr "Esti sedra'l to caberu intentu"
252252
253253#: qml/Greeter/NarrowView.qml:57
254254msgid ""
255255"If passphrase is entered incorrectly, your phone will conduct a factory "
256256"reset and all personal data will be deleted."
257257msgstr ""
258−"Si s'inxerta la fras incorreutamente, el teléfonu va volver a la "
259−"configuración de fábrica y van perdese tolos datos personales."
258+"Si s'inxerta la fras de pasu incorreutamente, el to teléfonu va reafitase y "
259+"van desaniciase tolos datos personales."
260260
261261#: qml/Greeter/NarrowView.qml:58
262262msgid ""
263263"If passcode is entered incorrectly, your phone will conduct a factory reset "
264264"and all personal data will be deleted."
265265msgstr ""
266−"Si s'inxerta'l códigu incorreutamente, el teléfonu va volver a la "
267−"configuración de fábrica y van perdese tolos datos personales."
266+"Si s'inxerta'l códigu de pasu incorreutamente, el to teléfonu va reafitase y "
267+"van desaniciase tolos datos personales."
268268
269269#: qml/Greeter/NarrowView.qml:115
270270#, qt-format
271271msgid "Enter %1"
272−msgstr "Escribi %1"
272+msgstr "Introduz %1"
273273
274274#: qml/Greeter/NarrowView.qml:116 qml/Wizard/Pages/passwd-set.qml:60
275275msgid "Enter passphrase"
276−msgstr "Escribi la frase contraseña"
276+msgstr "Introduz la fras de pasu"
277277
278278#: qml/Greeter/NarrowView.qml:117
279279msgid "Enter passcode"
280−msgstr "Escribi'l códigu de pasu"
280+msgstr "Introduz el códigu de pasu"
281281
282282#: qml/Greeter/NarrowView.qml:118
283283#, qt-format
@@ -290,7 +290,7 @@ msgstr "Sentímoslo, contraseña incorreuta"
290290
291291#: qml/Greeter/NarrowView.qml:120
292292msgid "Please re-enter"
293−msgstr "Vuelvi a escribila"
293+msgstr "Por favor, reintroduzla"
294294
295295#: qml/Greeter/NarrowView.qml:121
296296msgid "Sorry, incorrect passcode"
@@ -302,7 +302,7 @@ msgstr "Amosar contraseña"
302302
303303#: qml/Panel/ActiveCallHint.qml:79
304304msgid "Tap to return to call..."
305−msgstr "Tocar pa tornar a la llamada..."
305+msgstr "Toca pa tornar a la llamada..."
306306
307307#: qml/Panel/ActiveCallHint.qml:92
308308msgid "Conference"
@@ -310,11 +310,11 @@ msgstr "Conferencia"
310310
311311#: qml/Panel/Indicators/MenuItemFactory.qml:639
312312msgid "Nothing is playing"
313−msgstr "Nun hai nada en reproducción"
313+msgstr "Nun ta reproduciéndose nada"
314314
315315#: qml/Panel/Indicators/MenuItemFactory.qml:787
316316msgid "In queue…"
317−msgstr "N'espera…"
317+msgstr "Na cola..."
318318
319319#: qml/Panel/Indicators/MenuItemFactory.qml:791
320320msgid "Downloading"
@@ -339,7 +339,7 @@ msgstr "Falló, toca pa reintentar"
339339#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
340340msgctxt "Label: Hint in message indicator line edit"
341341msgid "Reply"
342−msgstr ""
342+msgstr "Responder"
343343
344344#: qml/Panel/Indicators/ModemInfoItem.qml:105
345345msgid "Unlock SIM"
@@ -351,7 +351,7 @@ msgstr "Roaming"
351351
352352#: qml/Tutorial/TutorialBottomFinish.qml:24
353353msgid "This action does different things for different apps"
354−msgstr "Esta aición tiene efeutos distintos en cada aplicación"
354+msgstr "Esta aición fai coses distintes p'aplicaciones estremaes"
355355
356356#: qml/Tutorial/TutorialBottomFinish.qml:25
357357msgid "Tap here to finish."
@@ -363,7 +363,7 @@ msgstr "Abrir menús especiales"
363363
364364#: qml/Tutorial/TutorialBottom.qml:29
365365msgid "Swipe up from the bottom edge."
366−msgstr ""
366+msgstr "Esliza dende'l berbesu d'embaxo."
367367
368368#: qml/Tutorial/TutorialContent.qml:81 qml/Tutorial/TutorialLeftFinish.qml:25
369369#: qml/Tutorial/TutorialRight.qml:144
@@ -372,11 +372,11 @@ msgstr "Toca equí pa siguir."
372372
373373#: qml/Tutorial/TutorialContent.qml:81
374374msgid "Click here to finish."
375−msgstr ""
375+msgstr "Primi equí pa finar."
376376
377377#: qml/Tutorial/TutorialLeftFinish.qml:24
378378msgid "These are the shortcuts to favorite apps"
379−msgstr ""
379+msgstr "Estos son los atayos a les aplicaciones favorites"
380380
381381#: qml/Tutorial/TutorialLeft.qml:26
382382msgid "Open the launcher"
@@ -384,44 +384,44 @@ msgstr "Abrir el llanzador"
384384
385385#: qml/Tutorial/TutorialLeft.qml:27
386386msgid "Short swipe from the left edge."
387−msgstr ""
387+msgstr "Esliza un poco dende'l berbesu esquierdu."
388388
389389#: qml/Tutorial/TutorialPage.qml:178
390390msgid "You almost got it!"
391−msgstr ""
391+msgstr "¡Yá cuasi finamos!"
392392
393393#: qml/Tutorial/TutorialPage.qml:195
394394msgid "Try again."
395−msgstr ""
395+msgstr "Intentar de nueves"
396396
397397#: qml/Tutorial/TutorialRight.qml:31
398398msgid "To view open apps"
399−msgstr ""
399+msgstr "Pa ver aplicaciones abiertes"
400400
401401#: qml/Tutorial/TutorialRight.qml:32
402402msgid "Long swipe from the right edge."
403−msgstr ""
403+msgstr "Eslizamientu llargu dende'l berbesu drechu."
404404
405405#: qml/Tutorial/TutorialRight.qml:122
406406msgid "View all your running tasks."
407−msgstr ""
407+msgstr "Mira toles tos aplicaciones n'execución."
408408
409409#: qml/Wizard/Page.qml:89
410410msgctxt "Button: Go back one page in the Wizard"
411411msgid "Back"
412−msgstr ""
412+msgstr "Atrás"
413413
414414#: qml/Wizard/Pages/10-welcome.qml:27
415415msgid "Hi!"
416−msgstr "Hola!"
416+msgstr "¡Hola!"
417417
418418#: qml/Wizard/Pages/10-welcome.qml:44
419419msgid "Welcome to your Ubuntu phone."
420−msgstr "Dámoste l'acoyida a Ubuntu Phone"
420+msgstr "Bienllegáu/ada al to teléfonu d'Ubuntu"
421421
422422#: qml/Wizard/Pages/10-welcome.qml:52
423423msgid "Let’s get started."
424−msgstr "Entamamos."
424+msgstr "Entamemos."
425425
426426#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
427427#: qml/Wizard/Pages/40-wifi.qml:215 qml/Wizard/Pages/50-location.qml:131
@@ -432,7 +432,7 @@ msgstr "Siguir"
432432
433433#: qml/Wizard/Pages/20-sim.qml:25
434434msgid "Add a SIM card and restart your device"
435−msgstr "Amiesta una tarxeta SIM y reanicia'l preséu"
435+msgstr "Amiesta una tarxeta SIM y reanicia'l to preséu"
436436
437437#: qml/Wizard/Pages/20-sim.qml:59
438438msgid "Without it, you won’t be able to make calls or use text messaging."
@@ -440,49 +440,49 @@ msgstr "Ensin ella, nun vas poder facer llamaes nin unviar mensaxes."
440440
441441#: qml/Wizard/Pages/20-sim.qml:73 qml/Wizard/Pages/40-wifi.qml:215
442442msgid "Skip"
443−msgstr "Omitir"
443+msgstr "Saltar"
444444
445445#: qml/Wizard/Pages/30-passwd-type.qml:39
446446msgid "Lock security"
447−msgstr "Seguridá de bloquéu"
447+msgstr "Seguranza del bloquéu"
448448
449449#: qml/Wizard/Pages/30-passwd-type.qml:74
450450msgid "Please select how you’d like to unlock your phone."
451−msgstr "Seleiciona cómo te prestaría desbloquiar el teléfonu."
451+msgstr "Por favor, esbilla como te prestaría desbloquiar el teléfonu."
452452
453453#: qml/Wizard/Pages/30-passwd-type.qml:97
454454msgctxt "Label: Type of security method"
455455msgid "Swipe"
456−msgstr ""
456+msgstr "Eslizar"
457457
458458#: qml/Wizard/Pages/30-passwd-type.qml:98
459459msgctxt "Label: Description of security method"
460460msgid "No security"
461−msgstr ""
461+msgstr "Ensin seguranza"
462462
463463#: qml/Wizard/Pages/30-passwd-type.qml:100
464464msgctxt "Label: Type of security method"
465465msgid "Passcode"
466−msgstr ""
466+msgstr "Códigu de pasu"
467467
468468#: qml/Wizard/Pages/30-passwd-type.qml:101
469469msgctxt "Label: Description of security method"
470470msgid "4 digits only"
471−msgstr ""
471+msgstr "Namái 4 díxitos"
472472
473473#: qml/Wizard/Pages/30-passwd-type.qml:103
474474msgctxt "Label: Type of security method"
475475msgid "Passphrase"
476−msgstr ""
476+msgstr "Fras de pasu"
477477
478478#: qml/Wizard/Pages/30-passwd-type.qml:104
479479msgctxt "Label: Description of security method"
480480msgid "Numbers and letters"
481−msgstr ""
481+msgstr "Númberos y lletres"
482482
483483#: qml/Wizard/Pages/40-wifi.qml:29
484484msgid "Connect to Wi‑Fi"
485−msgstr "Coneutar a WiFi"
485+msgstr "Coneutase al WiFi"
486486
487487#: qml/Wizard/Pages/40-wifi.qml:168
488488msgid "Available networks…"
@@ -494,11 +494,11 @@ msgstr "Nun hai redes disponibles"
494494
495495#: qml/Wizard/Pages/50-location.qml:27
496496msgid "Location"
497−msgstr "Llocalización"
497+msgstr "Allugamientu"
498498
499499#: qml/Wizard/Pages/50-location.qml:62
500500msgid "Let the phone detect your location:"
501−msgstr "Permitir al teléfonu deteutar la llocalización:"
501+msgstr "Dexar al teléfonu deteutar l'allugamientu:"
502502
503503#: qml/Wizard/Pages/50-location.qml:69
504504msgid "Using GPS only (less accurate)"
@@ -507,7 +507,7 @@ msgstr "Namái usando GPS (menos precisión)"
507507#: qml/Wizard/Pages/50-location.qml:86
508508msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
509509msgstr ""
510−"Usando GPS, Wifi anónimes ya información de redes móviles (recomiéndase)"
510+"Usando GPS, Wi-fi anónimes ya información de redes móviles (aconseyao)"
511511
512512#. TRANSLATORS: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
513513#: qml/Wizard/Pages/50-location.qml:103
@@ -515,8 +515,8 @@ msgid ""
515515"By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
516516"conditions</a>."
517517msgstr ""
518−"Seleicionando esta opción, aceutes los <a href='#'>términos y "
519−"condiciones</a> de Nokia HERE."
518+"Esbillando esta opción aceutes los <a href='#'>términos y condiciones</a> de "
519+"Nokia HERE."
520520
521521#: qml/Wizard/Pages/50-location.qml:112
522522msgid "Not at all"
@@ -528,7 +528,7 @@ msgstr "Pues cambialo dempués n'<b>Axustes del sistema</b>."
528528
529529#: qml/Wizard/Pages/60-reporting.qml:24
530530msgid "Improving your experience"
531−msgstr "Ameyorar la esperiencia"
531+msgstr "Ameyorando la to esperiencia"
532532
533533#: qml/Wizard/Pages/60-reporting.qml:36
534534msgid ""
@@ -543,7 +543,7 @@ msgid ""
543543"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
544544"Privacy</b>"
545545msgstr ""
546−"Esto pue desactivase nos <b>Axustes del sistema</b> en <b>Seguranza y "
546+"Esto pue deshabilitase nos <b>Axustes del sistema</b> en <b>Seguranza y "
547547"privacidá</b>"
548548
549549#: qml/Wizard/Pages/80-finished.qml:24
@@ -552,11 +552,11 @@ msgstr "Too fecho"
552552
553553#: qml/Wizard/Pages/80-finished.qml:39
554554msgid "Nice work!"
555−msgstr "Bon trabayu!"
555+msgstr "¡Bon trabayu!"
556556
557557#: qml/Wizard/Pages/80-finished.qml:46
558558msgid "Your phone is now ready to use."
559−msgstr "El teléfonu yá ta preparáu pa usar."
559+msgstr "El teléfonu yá ta tresnáu pa usase."
560560
561561#: qml/Wizard/Pages/80-finished.qml:53
562562msgid "Finish"
@@ -568,24 +568,24 @@ msgstr "Términos y condiciones"
568568
569569#: qml/Wizard/Pages/passwd-confirm.qml:49
570570msgid "Confirm passphrase"
571−msgstr "Confirmar fras secreta"
571+msgstr "Confirmar frase de pasu"
572572
573573#: qml/Wizard/Pages/passwd-confirm.qml:50
574574msgid "Confirm passcode"
575−msgstr "Confirmar el códigu de pasu"
575+msgstr "Confirmar códigu de pasu"
576576
577577#: qml/Wizard/Pages/passwd-confirm.qml:53
578578#: qml/Wizard/Pages/passwd-confirm.qml:54
579579msgid "Please try again."
580−msgstr "Vuelvi a intentalo."
580+msgstr "Por favor, vuelvi a intentalo."
581581
582582#: qml/Wizard/Pages/passwd-set.qml:61
583583msgid "Choose your passcode"
584−msgstr "Escueyi'l códigu de pasu"
584+msgstr "Escueyi'l to códigu de pasu"
585585
586586#: qml/Wizard/Pages/passwd-set.qml:68
587587msgid "Passphrase must be 4 characters long"
588−msgstr "La fras de contraseña tien de ser de 4 caráuteres"
588+msgstr "La fras de pasu tien de ser 4 caráuteres de llargor"
589589
590590#: qml/Wizard/StackButton.qml:39
591591#, qt-format
po/be.po+46−46
@@ -8,16 +8,16 @@ msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-02-07 19:04+0000\n"
12−"Last-Translator: Mikhail_SaTuRn <mikhail_saturn@tut.by>\n"
11+"PO-Revision-Date: 2015-04-27 14:34+0000\n"
12+"Last-Translator: Arsień Šachalevič <semen.shah@gmail.com>\n"
1313"Language-Team: Belarusian <be@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%10==1 && n%100!=11 ? 0 : n%10>=2 && "
1818"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
19−"X-Launchpad-Export-Date: 2015-04-23 06:48+0000\n"
20−"X-Generator: Launchpad (build 17430)\n"
19+"X-Launchpad-Export-Date: 2015-04-28 05:33+0000\n"
20+"X-Generator: Launchpad (build 17453)\n"
2121
2222#: plugins/LightDM/Greeter.cpp:112
2323msgid "Password: "
@@ -26,11 +26,11 @@ msgstr "Пароль: "
2626#: plugins/Unity/Launcher/launcheritem.cpp:45
2727#: plugins/Unity/Launcher/launcheritem.cpp:100
2828msgid "Pin shortcut"
29−msgstr "Заблакаваць кнопку"
29+msgstr "Замацаваць кнопку"
3030
3131#: plugins/Unity/Launcher/launcheritem.cpp:100
3232msgid "Unpin shortcut"
33−msgstr "Разблакаваць кнопку"
33+msgstr "Адмацаваць кнопку"
3434
3535#: qml/Components/DelayedLockscreen.qml:49
3636msgid "Device Locked"
@@ -58,21 +58,21 @@ msgstr[2] "Пачакайце %1 хвілінаў і паўтарыце спро
5858#: qml/Components/Dialogs.qml:69
5959msgctxt "Title: Lock/Log out dialog"
6060msgid "Log out"
61−msgstr ""
61+msgstr "Выйсці"
6262
6363#: qml/Components/Dialogs.qml:70
6464msgid "Are you sure you want to log out?"
65−msgstr "Вы насамрэч хочаце выйсці?"
65+msgstr "Вы ўпэўнены, што хочаце выйсці?"
6666
6767#: qml/Components/Dialogs.qml:72
6868msgctxt "Button: Lock the system"
6969msgid "Lock"
70−msgstr ""
70+msgstr "Заблакаваць"
7171
7272#: qml/Components/Dialogs.qml:79
7373msgctxt "Button: Log out from the system"
7474msgid "Log Out"
75−msgstr ""
75+msgstr "Выйсці"
7676
7777#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
7878#: qml/Components/Dialogs.qml:173
@@ -82,7 +82,7 @@ msgstr "Скасаваць"
8282#: qml/Components/Dialogs.qml:98
8383msgctxt "Title: Reboot/Shut down dialog"
8484msgid "Shut down"
85−msgstr ""
85+msgstr "Выключыць"
8686
8787#: qml/Components/Dialogs.qml:99
8888msgid "Are you sure you want to shut down?"
@@ -91,17 +91,17 @@ msgstr "Вы ўпэўненыя, што жадаеце выключыць пры
9191#: qml/Components/Dialogs.qml:101
9292msgctxt "Button: Reboot the system"
9393msgid "Reboot"
94−msgstr ""
94+msgstr "Перазагрузіць"
9595
9696#: qml/Components/Dialogs.qml:109
9797msgctxt "Button: Shut down the system"
9898msgid "Shut down"
99−msgstr ""
99+msgstr "Выключыць"
100100
101101#: qml/Components/Dialogs.qml:129
102102msgctxt "Title: Reboot dialog"
103103msgid "Reboot"
104−msgstr ""
104+msgstr "Перазагрузіць"
105105
106106#: qml/Components/Dialogs.qml:130
107107msgid "Are you sure you want to reboot?"
@@ -118,7 +118,7 @@ msgstr "Так"
118118#: qml/Components/Dialogs.qml:152
119119msgctxt "Title: Power off/Restart dialog"
120120msgid "Power"
121−msgstr ""
121+msgstr "Сілкаванне"
122122
123123#: qml/Components/Dialogs.qml:153
124124msgid ""
@@ -131,12 +131,12 @@ msgstr ""
131131#: qml/Components/Dialogs.qml:155
132132msgctxt "Button: Power off the system"
133133msgid "Power off"
134−msgstr ""
134+msgstr "Выключыць"
135135
136136#: qml/Components/Dialogs.qml:164
137137msgctxt "Button: Restart the system"
138138msgid "Restart"
139−msgstr ""
139+msgstr "Перазапуск"
140140
141141#: qml/Components/Lockscreen.qml:245
142142msgid "Return to Call"
@@ -161,22 +161,22 @@ msgstr "Больш падрабязна"
161161#: qml/Dash/GenericScopeView.qml:594
162162msgctxt "Label: Hint for dash search line edit"
163163msgid "Search"
164−msgstr ""
164+msgstr "Пошук"
165165
166166#: qml/Dash/PageHeader.qml:269
167167msgctxt "Button: Open the Ubuntu Store"
168168msgid "Store"
169−msgstr ""
169+msgstr "Крама"
170170
171171#: qml/Dash/PageHeader.qml:276
172172msgctxt "Button: Start a search in the current dash scope"
173173msgid "Search"
174−msgstr ""
174+msgstr "Пошук"
175175
176176#: qml/Dash/PageHeader.qml:286
177177msgctxt "Button: Show the current dash scope settings"
178178msgid "Settings"
179−msgstr ""
179+msgstr "Налады"
180180
181181#: qml/Dash/PageHeader.qml:293
182182msgid "Remove from Favorites"
@@ -216,7 +216,7 @@ msgstr "Адпусціце для абнаўлення ..."
216216
217217#: qml/Dash/ScopeSettings/ScopeSettingBoolean.qml:43
218218msgid "Enable location data"
219−msgstr ""
219+msgstr "Уключыць дадзеныя аб месцазнаходжанні"
220220
221221#: qml/Dash/ScopesList.qml:66
222222msgid "Manage"
@@ -236,11 +236,11 @@ msgstr "Разблакаваць"
236236
237237#: qml/Greeter/LoginList.qml:236
238238msgid "Retry"
239−msgstr ""
239+msgstr "Паспрабаваць ізноў"
240240
241241#: qml/Greeter/LoginList.qml:237
242242msgid "Tap to unlock"
243−msgstr ""
243+msgstr "Націсніце, каб разблакаваць"
244244
245245#: qml/Greeter/NarrowView.qml:53 qml/Wizard/Pages/passwd-confirm.qml:53
246246msgid "Sorry, incorrect passphrase."
@@ -343,7 +343,7 @@ msgstr "Памылка, націсніце для паўтору"
343343#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
344344msgctxt "Label: Hint in message indicator line edit"
345345msgid "Reply"
346−msgstr ""
346+msgstr "Адказаць"
347347
348348#: qml/Panel/Indicators/ModemInfoItem.qml:105
349349msgid "Unlock SIM"
@@ -355,65 +355,65 @@ msgstr "Роўмінг"
355355
356356#: qml/Tutorial/TutorialBottomFinish.qml:24
357357msgid "This action does different things for different apps"
358−msgstr ""
358+msgstr "Гэта дзеянне мае розныя наступствы для розных праграм"
359359
360360#: qml/Tutorial/TutorialBottomFinish.qml:25
361361msgid "Tap here to finish."
362−msgstr ""
362+msgstr "Націсніце тут, каб завяршыць."
363363
364364#: qml/Tutorial/TutorialBottom.qml:28
365365msgid "Open special menus"
366−msgstr ""
366+msgstr "Адкрыць спецыяльныя меню"
367367
368368#: qml/Tutorial/TutorialBottom.qml:29
369369msgid "Swipe up from the bottom edge."
370−msgstr ""
370+msgstr "Правесці уверх ад ніжняга краю."
371371
372372#: qml/Tutorial/TutorialContent.qml:81 qml/Tutorial/TutorialLeftFinish.qml:25
373373#: qml/Tutorial/TutorialRight.qml:144
374374msgid "Tap here to continue."
375−msgstr ""
375+msgstr "Націсніце тут, каб працягнуць."
376376
377377#: qml/Tutorial/TutorialContent.qml:81
378378msgid "Click here to finish."
379−msgstr ""
379+msgstr "Націсніце тут для завяршэння."
380380
381381#: qml/Tutorial/TutorialLeftFinish.qml:24
382382msgid "These are the shortcuts to favorite apps"
383−msgstr ""
383+msgstr "Гэта скарачэнні для абраных праграм"
384384
385385#: qml/Tutorial/TutorialLeft.qml:26
386386msgid "Open the launcher"
387−msgstr ""
387+msgstr "Адкрыць панэль запуску"
388388
389389#: qml/Tutorial/TutorialLeft.qml:27
390390msgid "Short swipe from the left edge."
391−msgstr ""
391+msgstr "Кароткі рух з левага краю."
392392
393393#: qml/Tutorial/TutorialPage.qml:178
394394msgid "You almost got it!"
395−msgstr ""
395+msgstr "Амаль усё!"
396396
397397#: qml/Tutorial/TutorialPage.qml:195
398398msgid "Try again."
399−msgstr ""
399+msgstr "Паспрабуйце яшчэ."
400400
401401#: qml/Tutorial/TutorialRight.qml:31
402402msgid "To view open apps"
403−msgstr ""
403+msgstr "Паглядзець адкрытыя праграмы"
404404
405405#: qml/Tutorial/TutorialRight.qml:32
406406msgid "Long swipe from the right edge."
407−msgstr ""
407+msgstr "Доўгі рух ад правага краю."
408408
409409#: qml/Tutorial/TutorialRight.qml:122
410410msgid "View all your running tasks."
411−msgstr ""
411+msgstr "Паказаць усе запушчаныя задачы."
412412
413413#: qml/Wizard/Page.qml:89
414414msgctxt "Button: Go back one page in the Wizard"
415415msgid "Back"
416−msgstr ""
416+msgstr "Вярнуцца"
417417
418418#: qml/Wizard/Pages/10-welcome.qml:27
419419msgid "Hi!"
@@ -457,32 +457,32 @@ msgstr "Калі ласка, абярыце спосаб разблакаван
457457#: qml/Wizard/Pages/30-passwd-type.qml:97
458458msgctxt "Label: Type of security method"
459459msgid "Swipe"
460−msgstr ""
460+msgstr "Рух"
461461
462462#: qml/Wizard/Pages/30-passwd-type.qml:98
463463msgctxt "Label: Description of security method"
464464msgid "No security"
465−msgstr ""
465+msgstr "Без бяспекі"
466466
467467#: qml/Wizard/Pages/30-passwd-type.qml:100
468468msgctxt "Label: Type of security method"
469469msgid "Passcode"
470−msgstr ""
470+msgstr "Пароль"
471471
472472#: qml/Wizard/Pages/30-passwd-type.qml:101
473473msgctxt "Label: Description of security method"
474474msgid "4 digits only"
475−msgstr ""
475+msgstr "Толькі 4 лічбы"
476476
477477#: qml/Wizard/Pages/30-passwd-type.qml:103
478478msgctxt "Label: Type of security method"
479479msgid "Passphrase"
480−msgstr ""
480+msgstr "Пароль"
481481
482482#: qml/Wizard/Pages/30-passwd-type.qml:104
483483msgctxt "Label: Description of security method"
484484msgid "Numbers and letters"
485−msgstr ""
485+msgstr "Літары і лічбы"
486486
487487#: qml/Wizard/Pages/40-wifi.qml:29
488488msgid "Connect to Wi‑Fi"
po/br.po+4−4
@@ -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: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-03-25 07:36+0000\n"
11+"PO-Revision-Date: 2015-04-27 07:03+0000\n"
1212"Last-Translator: Fohanno Thierry <thierry.fohanno@ofis-bzh.org>\n"
1313"Language-Team: Breton <br@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=2; plural=n > 1;\n"
18−"X-Launchpad-Export-Date: 2015-04-23 06:48+0000\n"
19−"X-Generator: Launchpad (build 17430)\n"
18+"X-Launchpad-Export-Date: 2015-04-28 05:33+0000\n"
19+"X-Generator: Launchpad (build 17453)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -375,7 +375,7 @@ msgstr "Pouzañ amañ evit kenderc'hel"
375375
376376#: qml/Tutorial/TutorialContent.qml:81
377377msgid "Click here to finish."
378−msgstr ""
378+msgstr "Klikit amañ evit echuiñ."
379379
380380#: qml/Tutorial/TutorialLeftFinish.qml:24
381381msgid "These are the shortcuts to favorite apps"
po/ca.po+25−25
@@ -8,15 +8,15 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-03-07 11:45+0000\n"
11+"PO-Revision-Date: 2015-05-02 07:52+0000\n"
1212"Last-Translator: David Planella <david.planella@ubuntu.com>\n"
1313"Language-Team: Catalan <ca@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=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-04-23 06:48+0000\n"
19−"X-Generator: Launchpad (build 17430)\n"
18+"X-Launchpad-Export-Date: 2015-05-03 06:07+0000\n"
19+"X-Generator: Launchpad (build 17474)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -57,7 +57,7 @@ msgstr[1] "Espereu %1 minuts i torneu-ho a intentar..."
5757#: qml/Components/Dialogs.qml:69
5858msgctxt "Title: Lock/Log out dialog"
5959msgid "Log out"
60−msgstr ""
60+msgstr "Surt"
6161
6262#: qml/Components/Dialogs.qml:70
6363msgid "Are you sure you want to log out?"
@@ -66,12 +66,12 @@ msgstr "Segur que voleu sortir?"
6666#: qml/Components/Dialogs.qml:72
6767msgctxt "Button: Lock the system"
6868msgid "Lock"
69−msgstr ""
69+msgstr "Bloca"
7070
7171#: qml/Components/Dialogs.qml:79
7272msgctxt "Button: Log out from the system"
7373msgid "Log Out"
74−msgstr ""
74+msgstr "Surt"
7575
7676#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
7777#: qml/Components/Dialogs.qml:173
@@ -81,7 +81,7 @@ msgstr "Cancel·la"
8181#: qml/Components/Dialogs.qml:98
8282msgctxt "Title: Reboot/Shut down dialog"
8383msgid "Shut down"
84−msgstr ""
84+msgstr "Apaga"
8585
8686#: qml/Components/Dialogs.qml:99
8787msgid "Are you sure you want to shut down?"
@@ -90,17 +90,17 @@ msgstr "Segur que voleu apagar?"
9090#: qml/Components/Dialogs.qml:101
9191msgctxt "Button: Reboot the system"
9292msgid "Reboot"
93−msgstr ""
93+msgstr "Reinicia"
9494
9595#: qml/Components/Dialogs.qml:109
9696msgctxt "Button: Shut down the system"
9797msgid "Shut down"
98−msgstr ""
98+msgstr "Apaga"
9999
100100#: qml/Components/Dialogs.qml:129
101101msgctxt "Title: Reboot dialog"
102102msgid "Reboot"
103−msgstr ""
103+msgstr "Reinicia"
104104
105105#: qml/Components/Dialogs.qml:130
106106msgid "Are you sure you want to reboot?"
@@ -117,7 +117,7 @@ msgstr "Sí"
117117#: qml/Components/Dialogs.qml:152
118118msgctxt "Title: Power off/Restart dialog"
119119msgid "Power"
120−msgstr ""
120+msgstr "Energia"
121121
122122#: qml/Components/Dialogs.qml:153
123123msgid ""
@@ -130,12 +130,12 @@ msgstr ""
130130#: qml/Components/Dialogs.qml:155
131131msgctxt "Button: Power off the system"
132132msgid "Power off"
133−msgstr ""
133+msgstr "Apaga"
134134
135135#: qml/Components/Dialogs.qml:164
136136msgctxt "Button: Restart the system"
137137msgid "Restart"
138−msgstr ""
138+msgstr "Reinicia"
139139
140140#: qml/Components/Lockscreen.qml:245
141141msgid "Return to Call"
@@ -160,22 +160,22 @@ msgstr "Mostra-ho tot"
160160#: qml/Dash/GenericScopeView.qml:594
161161msgctxt "Label: Hint for dash search line edit"
162162msgid "Search"
163−msgstr ""
163+msgstr "Cerca"
164164
165165#: qml/Dash/PageHeader.qml:269
166166msgctxt "Button: Open the Ubuntu Store"
167167msgid "Store"
168−msgstr ""
168+msgstr "Botiga"
169169
170170#: qml/Dash/PageHeader.qml:276
171171msgctxt "Button: Start a search in the current dash scope"
172172msgid "Search"
173−msgstr ""
173+msgstr "Cerca"
174174
175175#: qml/Dash/PageHeader.qml:286
176176msgctxt "Button: Show the current dash scope settings"
177177msgid "Settings"
178−msgstr ""
178+msgstr "Paràmetres"
179179
180180#: qml/Dash/PageHeader.qml:293
181181msgid "Remove from Favorites"
@@ -344,7 +344,7 @@ msgstr "Ha fallat, feu un toc per tornar-ho a intentar"
344344#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
345345msgctxt "Label: Hint in message indicator line edit"
346346msgid "Reply"
347−msgstr ""
347+msgstr "Respon"
348348
349349#: qml/Panel/Indicators/ModemInfoItem.qml:105
350350msgid "Unlock SIM"
@@ -377,7 +377,7 @@ msgstr "Feu un toc per continuar."
377377
378378#: qml/Tutorial/TutorialContent.qml:81
379379msgid "Click here to finish."
380−msgstr ""
380+msgstr "Feu un toc aquí per finalitzar"
381381
382382#: qml/Tutorial/TutorialLeftFinish.qml:24
383383msgid "These are the shortcuts to favorite apps"
@@ -414,7 +414,7 @@ msgstr "Mostra totes les tasques en execució."
414414#: qml/Wizard/Page.qml:89
415415msgctxt "Button: Go back one page in the Wizard"
416416msgid "Back"
417−msgstr ""
417+msgstr "Enrere"
418418
419419#: qml/Wizard/Pages/10-welcome.qml:27
420420msgid "Hi!"
@@ -458,17 +458,17 @@ msgstr "Seleccioneu com voleu desblocar el telèfon."
458458#: qml/Wizard/Pages/30-passwd-type.qml:97
459459msgctxt "Label: Type of security method"
460460msgid "Swipe"
461−msgstr ""
461+msgstr "Lliscament amb el dit"
462462
463463#: qml/Wizard/Pages/30-passwd-type.qml:98
464464msgctxt "Label: Description of security method"
465465msgid "No security"
466−msgstr ""
466+msgstr "Sense seguretat"
467467
468468#: qml/Wizard/Pages/30-passwd-type.qml:100
469469msgctxt "Label: Type of security method"
470470msgid "Passcode"
471−msgstr ""
471+msgstr "Codi d'accés"
472472
473473#: qml/Wizard/Pages/30-passwd-type.qml:101
474474msgctxt "Label: Description of security method"
@@ -478,12 +478,12 @@ msgstr "Només 4 dígits"
478478#: qml/Wizard/Pages/30-passwd-type.qml:103
479479msgctxt "Label: Type of security method"
480480msgid "Passphrase"
481−msgstr ""
481+msgstr "Contrasenya"
482482
483483#: qml/Wizard/Pages/30-passwd-type.qml:104
484484msgctxt "Label: Description of security method"
485485msgid "Numbers and letters"
486−msgstr ""
486+msgstr "Números i lletres"
487487
488488#: qml/Wizard/Pages/40-wifi.qml:29
489489msgid "Connect to Wi‑Fi"
po/cs.po+136−119
@@ -8,132 +8,138 @@ msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2014-01-08 10:11+0000\n"
11+"PO-Revision-Date: 2015-05-11 20:42+0000\n"
1212"Last-Translator: Tadeáš Pařík <tadeas.parik@gmail.com>\n"
1313"Language-Team: Czech <cs@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) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
18−"X-Launchpad-Export-Date: 2015-04-23 06:48+0000\n"
19−"X-Generator: Launchpad (build 17430)\n"
18+"X-Launchpad-Export-Date: 2015-05-13 05:31+0000\n"
19+"X-Generator: Launchpad (build 17493)\n"
20+"Language: cs\n"
2021
2122#: plugins/LightDM/Greeter.cpp:112
2223msgid "Password: "
23−msgstr ""
24+msgstr "Heslo: "
2425
2526#: plugins/Unity/Launcher/launcheritem.cpp:45
2627#: plugins/Unity/Launcher/launcheritem.cpp:100
2728msgid "Pin shortcut"
28−msgstr ""
29+msgstr "Připíchnout zkratku"
2930
3031#: plugins/Unity/Launcher/launcheritem.cpp:100
3132msgid "Unpin shortcut"
32−msgstr ""
33+msgstr "Odpíchnout zkratku"
3334
3435#: qml/Components/DelayedLockscreen.qml:49
3536msgid "Device Locked"
36−msgstr ""
37+msgstr "Zařízení uzamčeno"
3738
3839#: qml/Components/DelayedLockscreen.qml:64
3940msgid "You have been locked out due to too many failed passphrase attempts."
4041msgstr ""
42+"Zařízení bylo uzamčeno z důvodu mnoha neúspěšných pokusů o přihlášení."
4143
4244#: qml/Components/DelayedLockscreen.qml:65
4345msgid "You have been locked out due to too many failed passcode attempts."
4446msgstr ""
47+"Zařízení bylo uzamčeno z důvodu mnoha neúspěšných pokusů o přihlášení."
4548
4649#: qml/Components/DelayedLockscreen.qml:74
4750#, qt-format
4851msgid "Please wait %1 minute and then try again…"
4952msgid_plural "Please wait %1 minutes and then try again…"
50−msgstr[0] ""
51−msgstr[1] ""
53+msgstr[0] "Počkejte, prosím, %1 minut a poté to zkuste znovu..."
54+msgstr[1] "Počkejte, prosím, %1 minuty a poté to zkuste znovu..."
55+msgstr[2] "Počkejte, prosím, %1 minut a poté to zkuste znovu..."
5256
5357#: qml/Components/Dialogs.qml:69
5458msgctxt "Title: Lock/Log out dialog"
5559msgid "Log out"
56−msgstr ""
60+msgstr "Odhlásit se"
5761
5862#: qml/Components/Dialogs.qml:70
5963msgid "Are you sure you want to log out?"
60−msgstr ""
64+msgstr "Jste si jistí, že se chcete odhlásit?"
6165
6266#: qml/Components/Dialogs.qml:72
6367msgctxt "Button: Lock the system"
6468msgid "Lock"
65−msgstr ""
69+msgstr "Uzamknout"
6670
6771#: qml/Components/Dialogs.qml:79
6872msgctxt "Button: Log out from the system"
6973msgid "Log Out"
70−msgstr ""
74+msgstr "Odhlásit"
7175
7276#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
7377#: qml/Components/Dialogs.qml:173
7478msgid "Cancel"
75−msgstr ""
79+msgstr "Zrušit"
7680
7781#: qml/Components/Dialogs.qml:98
7882msgctxt "Title: Reboot/Shut down dialog"
7983msgid "Shut down"
80−msgstr ""
84+msgstr "Vypnout"
8185
8286#: qml/Components/Dialogs.qml:99
8387msgid "Are you sure you want to shut down?"
84−msgstr ""
88+msgstr "Jste si jistí, že chcete zařízení vypnout?"
8589
8690#: qml/Components/Dialogs.qml:101
8791msgctxt "Button: Reboot the system"
8892msgid "Reboot"
89−msgstr ""
93+msgstr "Restartovat"
9094
9195#: qml/Components/Dialogs.qml:109
9296msgctxt "Button: Shut down the system"
9397msgid "Shut down"
94−msgstr ""
98+msgstr "Vypnout"
9599
96100#: qml/Components/Dialogs.qml:129
97101msgctxt "Title: Reboot dialog"
98102msgid "Reboot"
99−msgstr ""
103+msgstr "Restartovat"
100104
101105#: qml/Components/Dialogs.qml:130
102106msgid "Are you sure you want to reboot?"
103−msgstr ""
107+msgstr "Jste si jistí, že chcete zařízení restartovat?"
104108
105109#: qml/Components/Dialogs.qml:132
106110msgid "No"
107−msgstr ""
111+msgstr "Ne"
108112
109113#: qml/Components/Dialogs.qml:138
110114msgid "Yes"
111−msgstr ""
115+msgstr "Ano"
112116
113117#: qml/Components/Dialogs.qml:152
114118msgctxt "Title: Power off/Restart dialog"
115119msgid "Power"
116−msgstr ""
120+msgstr "Vypnutí"
117121
118122#: qml/Components/Dialogs.qml:153
119123msgid ""
120124"Are you sure you would like\n"
121125"to power off?"
122126msgstr ""
127+"Jste si jistí, že chcete\n"
128+"telefon vypnout?"
123129
124130#: qml/Components/Dialogs.qml:155
125131msgctxt "Button: Power off the system"
126132msgid "Power off"
127−msgstr ""
133+msgstr "Vypnutí"
128134
129135#: qml/Components/Dialogs.qml:164
130136msgctxt "Button: Restart the system"
131137msgid "Restart"
132−msgstr ""
138+msgstr "Reboot"
133139
134140#: qml/Components/Lockscreen.qml:245
135141msgid "Return to Call"
136−msgstr ""
142+msgstr "Vrátit se k hovoru"
137143
138144#: qml/Components/Lockscreen.qml:245
139145msgid "Emergency Call"
@@ -141,51 +147,51 @@ msgstr "Tísňové volání"
141147
142148#: qml/Components/Lockscreen.qml:277
143149msgid "OK"
144−msgstr ""
150+msgstr "OK"
145151
146152#: qml/Dash/GenericScopeView.qml:532 qml/Dash/GenericScopeView.qml:690
147153msgid "See less"
148−msgstr ""
154+msgstr "Zobrazit méně"
149155
150156#: qml/Dash/GenericScopeView.qml:532
151157msgid "See all"
152−msgstr ""
158+msgstr "Zobrazit vše"
153159
154160#: qml/Dash/GenericScopeView.qml:594
155161msgctxt "Label: Hint for dash search line edit"
156162msgid "Search"
157−msgstr ""
163+msgstr "Hledat"
158164
159165#: qml/Dash/PageHeader.qml:269
160166msgctxt "Button: Open the Ubuntu Store"
161167msgid "Store"
162−msgstr ""
168+msgstr "Obchod"
163169
164170#: qml/Dash/PageHeader.qml:276
165171msgctxt "Button: Start a search in the current dash scope"
166172msgid "Search"
167−msgstr ""
173+msgstr "Hledat"
168174
169175#: qml/Dash/PageHeader.qml:286
170176msgctxt "Button: Show the current dash scope settings"
171177msgid "Settings"
172−msgstr ""
178+msgstr "Nastavení"
173179
174180#: qml/Dash/PageHeader.qml:293
175181msgid "Remove from Favorites"
176−msgstr ""
182+msgstr "Odstranit z oblíbených"
177183
178184#: qml/Dash/PageHeader.qml:293
179185msgid "Add to Favorites"
180−msgstr ""
186+msgstr "Přidat do oblíbených"
181187
182188#: qml/Dash/Previews/PreviewActionCombo.qml:35
183189msgid "More..."
184−msgstr ""
190+msgstr "Více..."
185191
186192#: qml/Dash/Previews/PreviewActionCombo.qml:35
187193msgid "Less..."
188−msgstr ""
194+msgstr "Méně..."
189195
190196#: qml/Dash/Previews/PreviewRatingInput.qml:81
191197msgid "Rate this"
@@ -201,93 +207,97 @@ msgstr "Odeslat"
201207
202208#: qml/Dash/PullToRefreshScopeStyle.qml:55
203209msgid "Pull to refresh…"
204−msgstr ""
210+msgstr "Vytáhněte pro obnovení..."
205211
206212#: qml/Dash/PullToRefreshScopeStyle.qml:60
207213msgid "Release to refresh…"
208−msgstr ""
214+msgstr "Pusťte pro obnovení..."
209215
210216#: qml/Dash/ScopeSettings/ScopeSettingBoolean.qml:43
211217msgid "Enable location data"
212−msgstr ""
218+msgstr "Povolit údaje umístění"
213219
214220#: qml/Dash/ScopesList.qml:66
215221msgid "Manage"
216−msgstr ""
222+msgstr "Spravovat"
217223
218224#: qml/Dash/ScopesList.qml:112
219225msgid "Home"
220−msgstr ""
226+msgstr "Domů"
221227
222228#: qml/Dash/ScopesList.qml:113
223229msgid "Also installed"
224−msgstr ""
230+msgstr "Také nainstalováno"
225231
226232#: qml/Greeter/CoverPage.qml:107
227233msgid "Unlock"
228−msgstr ""
234+msgstr "Odemknout"
229235
230236#: qml/Greeter/LoginList.qml:236
231237msgid "Retry"
232−msgstr ""
238+msgstr "Znovu"
233239
234240#: qml/Greeter/LoginList.qml:237
235241msgid "Tap to unlock"
236−msgstr ""
242+msgstr "Klepnout pro odemčení"
237243
238244#: qml/Greeter/NarrowView.qml:53 qml/Wizard/Pages/passwd-confirm.qml:53
239245msgid "Sorry, incorrect passphrase."
240−msgstr ""
246+msgstr "Zadali jste špatné heslo."
241247
242248#: qml/Greeter/NarrowView.qml:54 qml/Wizard/Pages/passwd-confirm.qml:54
243249msgid "Sorry, incorrect passcode."
244−msgstr ""
250+msgstr "Zadali jste špatné heslo."
245251
246252#: qml/Greeter/NarrowView.qml:55
247253msgid "This will be your last attempt."
248−msgstr ""
254+msgstr "Toto je váš poslední pokus."
249255
250256#: qml/Greeter/NarrowView.qml:57
251257msgid ""
252258"If passphrase is entered incorrectly, your phone will conduct a factory "
253259"reset and all personal data will be deleted."
254260msgstr ""
261+"Pokud bude heslo zadáno nesprávně, bude telefon obnoven do továrního "
262+"nastavení a všechna osobní data budou smazána."
255263
256264#: qml/Greeter/NarrowView.qml:58
257265msgid ""
258266"If passcode is entered incorrectly, your phone will conduct a factory reset "
259267"and all personal data will be deleted."
260268msgstr ""
269+"Pokud bude heslo zadáno nesprávně, bude telefon obnoven do továrního "
270+"nastavení a všechna osobní data budou smazána."
261271
262272#: qml/Greeter/NarrowView.qml:115
263273#, qt-format
264274msgid "Enter %1"
265−msgstr ""
275+msgstr "Vložte %1"
266276
267277#: qml/Greeter/NarrowView.qml:116 qml/Wizard/Pages/passwd-set.qml:60
268278msgid "Enter passphrase"
269−msgstr ""
279+msgstr "Vložte heslo"
270280
271281#: qml/Greeter/NarrowView.qml:117
272282msgid "Enter passcode"
273−msgstr ""
283+msgstr "Vložte heslo"
274284
275285#: qml/Greeter/NarrowView.qml:118
276286#, qt-format
277287msgid "Sorry, incorrect %1"
278−msgstr ""
288+msgstr "Špatné %1"
279289
280290#: qml/Greeter/NarrowView.qml:119
281291msgid "Sorry, incorrect passphrase"
282−msgstr ""
292+msgstr "Špatné heslo"
283293
284294#: qml/Greeter/NarrowView.qml:120
285295msgid "Please re-enter"
286−msgstr ""
296+msgstr "Vložte znovu"
287297
288298#: qml/Greeter/NarrowView.qml:121
289299msgid "Sorry, incorrect passcode"
290−msgstr ""
300+msgstr "Špatné heslo"
291301
292302#: qml/Notifications/NotificationMenuItemFactory.qml:128
293303msgid "Show password"
@@ -295,211 +305,211 @@ msgstr "Zobrazit heslo"
295305
296306#: qml/Panel/ActiveCallHint.qml:79
297307msgid "Tap to return to call..."
298−msgstr ""
308+msgstr "Klepnutím se vrátíte k hovoru..."
299309
300310#: qml/Panel/ActiveCallHint.qml:92
301311msgid "Conference"
302−msgstr ""
312+msgstr "Konferenční hovor"
303313
304314#: qml/Panel/Indicators/MenuItemFactory.qml:639
305315msgid "Nothing is playing"
306−msgstr ""
316+msgstr "Nic se nepřehrává"
307317
308318#: qml/Panel/Indicators/MenuItemFactory.qml:787
309319msgid "In queue…"
310−msgstr ""
320+msgstr "Ve frontě..."
311321
312322#: qml/Panel/Indicators/MenuItemFactory.qml:791
313323msgid "Downloading"
314−msgstr ""
324+msgstr "Stahování"
315325
316326#: qml/Panel/Indicators/MenuItemFactory.qml:793
317327msgid "Paused, tap to resume"
318−msgstr ""
328+msgstr "Pauza, pro pokračování klepněte"
319329
320330#: qml/Panel/Indicators/MenuItemFactory.qml:795
321331msgid "Canceled"
322−msgstr ""
332+msgstr "Zrušeno"
323333
324334#: qml/Panel/Indicators/MenuItemFactory.qml:797
325335msgid "Finished"
326−msgstr ""
336+msgstr "Dokončeno"
327337
328338#: qml/Panel/Indicators/MenuItemFactory.qml:799
329339msgid "Failed, tap to retry"
330−msgstr ""
340+msgstr "Nezdařilo se, opakujte klepnutím"
331341
332342#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
333343msgctxt "Label: Hint in message indicator line edit"
334344msgid "Reply"
335−msgstr ""
345+msgstr "Odpovědět"
336346
337347#: qml/Panel/Indicators/ModemInfoItem.qml:105
338348msgid "Unlock SIM"
339−msgstr ""
349+msgstr "Odemknout SIM"
340350
341351#: qml/Panel/Indicators/RoamingIndication.qml:27
342352msgid "Roaming"
343−msgstr ""
353+msgstr "Roaming"
344354
345355#: qml/Tutorial/TutorialBottomFinish.qml:24
346356msgid "This action does different things for different apps"
347−msgstr ""
357+msgstr "Tato akce vyvolá různé reakce v rozdílných aplikacích"
348358
349359#: qml/Tutorial/TutorialBottomFinish.qml:25
350360msgid "Tap here to finish."
351−msgstr ""
361+msgstr "Klepněte zde pro dokončení."
352362
353363#: qml/Tutorial/TutorialBottom.qml:28
354364msgid "Open special menus"
355−msgstr ""
365+msgstr "Otevřít speciální menu"
356366
357367#: qml/Tutorial/TutorialBottom.qml:29
358368msgid "Swipe up from the bottom edge."
359−msgstr ""
369+msgstr "Potáhněte prstem z dola nahoru."
360370
361371#: qml/Tutorial/TutorialContent.qml:81 qml/Tutorial/TutorialLeftFinish.qml:25
362372#: qml/Tutorial/TutorialRight.qml:144
363373msgid "Tap here to continue."
364−msgstr ""
374+msgstr "Klepněte zde pro pokračování."
365375
366376#: qml/Tutorial/TutorialContent.qml:81
367377msgid "Click here to finish."
368−msgstr ""
378+msgstr "Klikněte zde pro dokončení."
369379
370380#: qml/Tutorial/TutorialLeftFinish.qml:24
371381msgid "These are the shortcuts to favorite apps"
372−msgstr ""
382+msgstr "Zde jsou odkazy na vaše oblíbené aplikace"
373383
374384#: qml/Tutorial/TutorialLeft.qml:26
375385msgid "Open the launcher"
376−msgstr ""
386+msgstr "Otevřete launcher"
377387
378388#: qml/Tutorial/TutorialLeft.qml:27
379389msgid "Short swipe from the left edge."
380−msgstr ""
390+msgstr "Potáhněte krátce z levého okraje."
381391
382392#: qml/Tutorial/TutorialPage.qml:178
383393msgid "You almost got it!"
384−msgstr ""
394+msgstr "Už to skoro máte!"
385395
386396#: qml/Tutorial/TutorialPage.qml:195
387397msgid "Try again."
388−msgstr ""
398+msgstr "Zkuste to znovu."
389399
390400#: qml/Tutorial/TutorialRight.qml:31
391401msgid "To view open apps"
392−msgstr ""
402+msgstr "K zobrazení otevřených aplikací"
393403
394404#: qml/Tutorial/TutorialRight.qml:32
395405msgid "Long swipe from the right edge."
396−msgstr ""
406+msgstr "Potáhněte dlouho z pravého okraje."
397407
398408#: qml/Tutorial/TutorialRight.qml:122
399409msgid "View all your running tasks."
400−msgstr ""
410+msgstr "Zobrazíte všechny běžící procesy."
401411
402412#: qml/Wizard/Page.qml:89
403413msgctxt "Button: Go back one page in the Wizard"
404414msgid "Back"
405−msgstr ""
415+msgstr "Zpět"
406416
407417#: qml/Wizard/Pages/10-welcome.qml:27
408418msgid "Hi!"
409−msgstr ""
419+msgstr "Ahoj!"
410420
411421#: qml/Wizard/Pages/10-welcome.qml:44
412422msgid "Welcome to your Ubuntu phone."
413−msgstr ""
423+msgstr "Vítejte ve vašem Ubuntu telefonu."
414424
415425#: qml/Wizard/Pages/10-welcome.qml:52
416426msgid "Let’s get started."
417−msgstr ""
427+msgstr "Začněme."
418428
419429#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
420430#: qml/Wizard/Pages/40-wifi.qml:215 qml/Wizard/Pages/50-location.qml:131
421431#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
422432#: qml/Wizard/Pages/passwd-set.qml:90
423433msgid "Continue"
424−msgstr ""
434+msgstr "Pokračovat"
425435
426436#: qml/Wizard/Pages/20-sim.qml:25
427437msgid "Add a SIM card and restart your device"
428−msgstr ""
438+msgstr "Vložte SIM kartu a restartujte své zařízení"
429439
430440#: qml/Wizard/Pages/20-sim.qml:59
431441msgid "Without it, you won’t be able to make calls or use text messaging."
432−msgstr ""
442+msgstr "Bez ni nebudete moci telefonovat nebo posílat textové zprávy."
433443
434444#: qml/Wizard/Pages/20-sim.qml:73 qml/Wizard/Pages/40-wifi.qml:215
435445msgid "Skip"
436−msgstr ""
446+msgstr "Přeskočit"
437447
438448#: qml/Wizard/Pages/30-passwd-type.qml:39
439449msgid "Lock security"
440−msgstr ""
450+msgstr "Bezpečnostní uzamčení"
441451
442452#: qml/Wizard/Pages/30-passwd-type.qml:74
443453msgid "Please select how you’d like to unlock your phone."
444−msgstr ""
454+msgstr "Zvolte, prosím, jak chcete svůj telefon odemykat."
445455
446456#: qml/Wizard/Pages/30-passwd-type.qml:97
447457msgctxt "Label: Type of security method"
448458msgid "Swipe"
449−msgstr ""
459+msgstr "Potažením prstu"
450460
451461#: qml/Wizard/Pages/30-passwd-type.qml:98
452462msgctxt "Label: Description of security method"
453463msgid "No security"
454−msgstr ""
464+msgstr "Bez zabezpečení"
455465
456466#: qml/Wizard/Pages/30-passwd-type.qml:100
457467msgctxt "Label: Type of security method"
458468msgid "Passcode"
459−msgstr ""
469+msgstr "Číselné heslo"
460470
461471#: qml/Wizard/Pages/30-passwd-type.qml:101
462472msgctxt "Label: Description of security method"
463473msgid "4 digits only"
464−msgstr ""
474+msgstr "Pouze 4 čísla"
465475
466476#: qml/Wizard/Pages/30-passwd-type.qml:103
467477msgctxt "Label: Type of security method"
468478msgid "Passphrase"
469−msgstr ""
479+msgstr "Heslo"
470480
471481#: qml/Wizard/Pages/30-passwd-type.qml:104
472482msgctxt "Label: Description of security method"
473483msgid "Numbers and letters"
474−msgstr ""
484+msgstr "Čísla a písmena"
475485
476486#: qml/Wizard/Pages/40-wifi.qml:29
477487msgid "Connect to Wi‑Fi"
478−msgstr ""
488+msgstr "Připojte se k Wi-Fi"
479489
480490#: qml/Wizard/Pages/40-wifi.qml:168
481491msgid "Available networks…"
482−msgstr ""
492+msgstr "Dostupné sítě..."
483493
484494#: qml/Wizard/Pages/40-wifi.qml:169
485495msgid "No available networks."
486−msgstr ""
496+msgstr "Žádná dostupná síť."
487497
488498#: qml/Wizard/Pages/50-location.qml:27
489499msgid "Location"
490−msgstr ""
500+msgstr "Umístění"
491501
492502#: qml/Wizard/Pages/50-location.qml:62
493503msgid "Let the phone detect your location:"
494−msgstr ""
504+msgstr "Povolte telefonu zjistit, kde se zrovna nacházíte:"
495505
496506#: qml/Wizard/Pages/50-location.qml:69
497507msgid "Using GPS only (less accurate)"
498−msgstr ""
508+msgstr "Používat pouze GPS (méně přesné)"
499509
500510#: qml/Wizard/Pages/50-location.qml:86
501511msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
502−msgstr ""
512+msgstr "Používat GPS, anonymní Wi-Fi a datovou síť (doporučeno)"
503513
504514#. TRANSLATORS: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
505515#: qml/Wizard/Pages/50-location.qml:103
@@ -507,81 +517,88 @@ msgid ""
507517"By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
508518"conditions</a>."
509519msgstr ""
520+"Zvolením této možnosti souhlasíte s <a href='#'>pravidly a podmínkami</a> "
521+"Nokia HERE."
510522
511523#: qml/Wizard/Pages/50-location.qml:112
512524msgid "Not at all"
513−msgstr ""
525+msgstr "V žádném případě"
514526
515527#: qml/Wizard/Pages/50-location.qml:124
516528msgid "You can change your mind later in <b>System Settings</b>."
517529msgstr ""
530+"Pokud změníte názor, můžete nastavení změnit v <b>Nastavení systému</b>."
518531
519532#: qml/Wizard/Pages/60-reporting.qml:24
520533msgid "Improving your experience"
521−msgstr ""
534+msgstr "Zlepšete své zkušenosti"
522535
523536#: qml/Wizard/Pages/60-reporting.qml:36
524537msgid ""
525538"Your phone is set up to automatically report errors to Canonical and its "
526539"partners, the makers of the operating system."
527540msgstr ""
541+"Váš telefon je nastaven tak, aby automaticky odesílal informace o chybách "
542+"společnosti Canonical a partnerům, kteří stvořili tento operační systém."
528543
529544#: qml/Wizard/Pages/60-reporting.qml:43
530545msgid ""
531546"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
532547"Privacy</b>"
533548msgstr ""
549+"To může být vypnuto v <b>Nastavení systému</b> v <b>Zabezpečení a "
550+"soukromí</b>"
534551
535552#: qml/Wizard/Pages/80-finished.qml:24
536553msgid "All done"
537−msgstr ""
554+msgstr "Vše je hotovo"
538555
539556#: qml/Wizard/Pages/80-finished.qml:39
540557msgid "Nice work!"
541−msgstr ""
558+msgstr "Skvělá práce!"
542559
543560#: qml/Wizard/Pages/80-finished.qml:46
544561msgid "Your phone is now ready to use."
545−msgstr ""
562+msgstr "Váš telefon je nyní připraven k použití."
546563
547564#: qml/Wizard/Pages/80-finished.qml:53
548565msgid "Finish"
549−msgstr ""
566+msgstr "Dokončit"
550567
551568#: qml/Wizard/Pages/here-terms.qml:27
552569msgid "Terms & Conditions"
553−msgstr ""
570+msgstr "Pravidla a podmínky"
554571
555572#: qml/Wizard/Pages/passwd-confirm.qml:49
556573msgid "Confirm passphrase"
557−msgstr ""
574+msgstr "Potvrdit heslo"
558575
559576#: qml/Wizard/Pages/passwd-confirm.qml:50
560577msgid "Confirm passcode"
561−msgstr ""
578+msgstr "Potvrdit číselné heslo"
562579
563580#: qml/Wizard/Pages/passwd-confirm.qml:53
564581#: qml/Wizard/Pages/passwd-confirm.qml:54
565582msgid "Please try again."
566−msgstr ""
583+msgstr "Zkuste to, prosím, znovu."
567584
568585#: qml/Wizard/Pages/passwd-set.qml:61
569586msgid "Choose your passcode"
570−msgstr ""
587+msgstr "Zvolte své heslo"
571588
572589#: qml/Wizard/Pages/passwd-set.qml:68
573590msgid "Passphrase must be 4 characters long"
574−msgstr ""
591+msgstr "Heslo musí být alespoň 4 znaky dlouhé"
575592
576593#: qml/Wizard/StackButton.qml:39
577594#, qt-format
578595msgid "〈 %1"
579−msgstr ""
596+msgstr "〈 %1"
580597
581598#: qml/Wizard/StackButton.qml:42
582599#, qt-format
583600msgid "%1 〉"
584−msgstr ""
601+msgstr "%1 〉"
585602
586603#~ msgid "Try swiping from the right edge to unlock the phone"
587604#~ msgstr "Pokuste se odemknout telefon potažením prstu z pravého okraje."
po/da.po+53−51
@@ -9,19 +9,19 @@ msgstr ""
99"Project-Id-Version: unity8\n"
1010"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1111"POT-Creation-Date: 2015-04-14 16:13+0200\n"
12−"PO-Revision-Date: 2015-04-17 10:46+0000\n"
13−"Last-Translator: Bluegrassin <anderslgade@gmail.com>\n"
12+"PO-Revision-Date: 2015-04-28 19:14+0000\n"
13+"Last-Translator: Thomas Lundsgaard Hansen <thomaslhansen@live.dk>\n"
1414"Language-Team: Danish <dansk@dansk-gruppen.dk>\n"
1515"MIME-Version: 1.0\n"
1616"Content-Type: text/plain; charset=UTF-8\n"
1717"Content-Transfer-Encoding: 8bit\n"
1818"Plural-Forms: nplurals=2; plural=n != 1;\n"
19−"X-Launchpad-Export-Date: 2015-04-23 06:48+0000\n"
20−"X-Generator: Launchpad (build 17430)\n"
19+"X-Launchpad-Export-Date: 2015-04-29 05:27+0000\n"
20+"X-Generator: Launchpad (build 17453)\n"
2121
2222#: plugins/LightDM/Greeter.cpp:112
2323msgid "Password: "
24−msgstr ""
24+msgstr "Adgangskode: "
2525
2626#: plugins/Unity/Launcher/launcheritem.cpp:45
2727#: plugins/Unity/Launcher/launcheritem.cpp:100
@@ -58,7 +58,7 @@ msgstr[1] "Vent venligst %1 minutter og prøv igen..."
5858#: qml/Components/Dialogs.qml:69
5959msgctxt "Title: Lock/Log out dialog"
6060msgid "Log out"
61−msgstr ""
61+msgstr "Log af"
6262
6363#: qml/Components/Dialogs.qml:70
6464msgid "Are you sure you want to log out?"
@@ -67,22 +67,22 @@ msgstr "Vil du logge ud?"
6767#: qml/Components/Dialogs.qml:72
6868msgctxt "Button: Lock the system"
6969msgid "Lock"
70−msgstr ""
70+msgstr "Lås"
7171
7272#: qml/Components/Dialogs.qml:79
7373msgctxt "Button: Log out from the system"
7474msgid "Log Out"
75−msgstr ""
75+msgstr "Log af"
7676
7777#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
7878#: qml/Components/Dialogs.qml:173
7979msgid "Cancel"
80−msgstr ""
80+msgstr "Annullér"
8181
8282#: qml/Components/Dialogs.qml:98
8383msgctxt "Title: Reboot/Shut down dialog"
8484msgid "Shut down"
85−msgstr ""
85+msgstr "Luk ned"
8686
8787#: qml/Components/Dialogs.qml:99
8888msgid "Are you sure you want to shut down?"
@@ -91,17 +91,17 @@ msgstr "Er du sikker på du vil lukke ned?"
9191#: qml/Components/Dialogs.qml:101
9292msgctxt "Button: Reboot the system"
9393msgid "Reboot"
94−msgstr ""
94+msgstr "Genstart"
9595
9696#: qml/Components/Dialogs.qml:109
9797msgctxt "Button: Shut down the system"
9898msgid "Shut down"
99−msgstr ""
99+msgstr "Luk ned"
100100
101101#: qml/Components/Dialogs.qml:129
102102msgctxt "Title: Reboot dialog"
103103msgid "Reboot"
104−msgstr ""
104+msgstr "Genstart"
105105
106106#: qml/Components/Dialogs.qml:130
107107msgid "Are you sure you want to reboot?"
@@ -109,11 +109,11 @@ msgstr "Er du sikker på du vil genstarte?"
109109
110110#: qml/Components/Dialogs.qml:132
111111msgid "No"
112−msgstr ""
112+msgstr "Nej"
113113
114114#: qml/Components/Dialogs.qml:138
115115msgid "Yes"
116−msgstr ""
116+msgstr "Ja"
117117
118118#: qml/Components/Dialogs.qml:152
119119msgctxt "Title: Power off/Restart dialog"
@@ -131,12 +131,12 @@ msgstr ""
131131#: qml/Components/Dialogs.qml:155
132132msgctxt "Button: Power off the system"
133133msgid "Power off"
134−msgstr ""
134+msgstr "Luk ned"
135135
136136#: qml/Components/Dialogs.qml:164
137137msgctxt "Button: Restart the system"
138138msgid "Restart"
139−msgstr ""
139+msgstr "Genstart"
140140
141141#: qml/Components/Lockscreen.qml:245
142142msgid "Return to Call"
@@ -161,30 +161,30 @@ msgstr "Se alle"
161161#: qml/Dash/GenericScopeView.qml:594
162162msgctxt "Label: Hint for dash search line edit"
163163msgid "Search"
164−msgstr ""
164+msgstr "Søg"
165165
166166#: qml/Dash/PageHeader.qml:269
167167msgctxt "Button: Open the Ubuntu Store"
168168msgid "Store"
169−msgstr ""
169+msgstr "Butik"
170170
171171#: qml/Dash/PageHeader.qml:276
172172msgctxt "Button: Start a search in the current dash scope"
173173msgid "Search"
174−msgstr ""
174+msgstr "Søg"
175175
176176#: qml/Dash/PageHeader.qml:286
177177msgctxt "Button: Show the current dash scope settings"
178178msgid "Settings"
179−msgstr ""
179+msgstr "Indstillinger"
180180
181181#: qml/Dash/PageHeader.qml:293
182182msgid "Remove from Favorites"
183−msgstr ""
183+msgstr "Fjern fra Favoritter"
184184
185185#: qml/Dash/PageHeader.qml:293
186186msgid "Add to Favorites"
187−msgstr ""
187+msgstr "Tilføj til Favoritter"
188188
189189#: qml/Dash/Previews/PreviewActionCombo.qml:35
190190msgid "More..."
@@ -220,11 +220,11 @@ msgstr "Aktivér lokationsdata"
220220
221221#: qml/Dash/ScopesList.qml:66
222222msgid "Manage"
223−msgstr ""
223+msgstr "Administrér"
224224
225225#: qml/Dash/ScopesList.qml:112
226226msgid "Home"
227−msgstr ""
227+msgstr "Hjem"
228228
229229#: qml/Dash/ScopesList.qml:113
230230msgid "Also installed"
@@ -232,15 +232,15 @@ msgstr "Også installeret"
232232
233233#: qml/Greeter/CoverPage.qml:107
234234msgid "Unlock"
235−msgstr ""
235+msgstr "Lås op"
236236
237237#: qml/Greeter/LoginList.qml:236
238238msgid "Retry"
239−msgstr ""
239+msgstr "Forsøg igen"
240240
241241#: qml/Greeter/LoginList.qml:237
242242msgid "Tap to unlock"
243−msgstr ""
243+msgstr "Tap for at låse op"
244244
245245#: qml/Greeter/NarrowView.qml:53 qml/Wizard/Pages/passwd-confirm.qml:53
246246msgid "Sorry, incorrect passphrase."
@@ -277,7 +277,7 @@ msgstr "Indtast %1"
277277
278278#: qml/Greeter/NarrowView.qml:116 qml/Wizard/Pages/passwd-set.qml:60
279279msgid "Enter passphrase"
280−msgstr ""
280+msgstr "Angiv adgangsfrase"
281281
282282#: qml/Greeter/NarrowView.qml:117
283283msgid "Enter passcode"
@@ -314,7 +314,7 @@ msgstr "Konference"
314314
315315#: qml/Panel/Indicators/MenuItemFactory.qml:639
316316msgid "Nothing is playing"
317−msgstr ""
317+msgstr "Intet afspilles"
318318
319319#: qml/Panel/Indicators/MenuItemFactory.qml:787
320320msgid "In queue…"
@@ -343,7 +343,7 @@ msgstr "Mislykkedes - tryk for at prøve igen"
343343#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
344344msgctxt "Label: Hint in message indicator line edit"
345345msgid "Reply"
346−msgstr ""
346+msgstr "Svar"
347347
348348#: qml/Panel/Indicators/ModemInfoItem.qml:105
349349msgid "Unlock SIM"
@@ -351,7 +351,7 @@ msgstr "Lås SIM-kort op"
351351
352352#: qml/Panel/Indicators/RoamingIndication.qml:27
353353msgid "Roaming"
354−msgstr ""
354+msgstr "Roaming"
355355
356356#: qml/Tutorial/TutorialBottomFinish.qml:24
357357msgid "This action does different things for different apps"
@@ -376,7 +376,7 @@ msgstr "Tryk her for at fortsætte."
376376
377377#: qml/Tutorial/TutorialContent.qml:81
378378msgid "Click here to finish."
379−msgstr ""
379+msgstr "Tryk her for at afslutte."
380380
381381#: qml/Tutorial/TutorialLeftFinish.qml:24
382382msgid "These are the shortcuts to favorite apps"
@@ -384,7 +384,7 @@ msgstr "Disse er genveje til favorit-apps"
384384
385385#: qml/Tutorial/TutorialLeft.qml:26
386386msgid "Open the launcher"
387−msgstr ""
387+msgstr "Åbn opstarteren"
388388
389389#: qml/Tutorial/TutorialLeft.qml:27
390390msgid "Short swipe from the left edge."
@@ -413,11 +413,11 @@ msgstr "Se alle dine kørende opgaver."
413413#: qml/Wizard/Page.qml:89
414414msgctxt "Button: Go back one page in the Wizard"
415415msgid "Back"
416−msgstr ""
416+msgstr "Tilbage"
417417
418418#: qml/Wizard/Pages/10-welcome.qml:27
419419msgid "Hi!"
420−msgstr ""
420+msgstr "Hej!"
421421
422422#: qml/Wizard/Pages/10-welcome.qml:44
423423msgid "Welcome to your Ubuntu phone."
@@ -432,7 +432,7 @@ msgstr "Lad os komme i gang."
432432#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
433433#: qml/Wizard/Pages/passwd-set.qml:90
434434msgid "Continue"
435−msgstr ""
435+msgstr "Fortsæt"
436436
437437#: qml/Wizard/Pages/20-sim.qml:25
438438msgid "Add a SIM card and restart your device"
@@ -441,14 +441,16 @@ msgstr "Indsæt et SIM-kort og genstart din enhed"
441441#: qml/Wizard/Pages/20-sim.qml:59
442442msgid "Without it, you won’t be able to make calls or use text messaging."
443443msgstr ""
444+"Uden denne vil du ikke være i stand til at foretage opkald eller sende "
445+"tekstbeskeder."
444446
445447#: qml/Wizard/Pages/20-sim.qml:73 qml/Wizard/Pages/40-wifi.qml:215
446448msgid "Skip"
447−msgstr ""
449+msgstr "Spring over"
448450
449451#: qml/Wizard/Pages/30-passwd-type.qml:39
450452msgid "Lock security"
451−msgstr ""
453+msgstr "Låsesikkerhed"
452454
453455#: qml/Wizard/Pages/30-passwd-type.qml:74
454456msgid "Please select how you’d like to unlock your phone."
@@ -457,32 +459,32 @@ msgstr "Vælg venligst hvordan du ønsker at låse din telefon op."
457459#: qml/Wizard/Pages/30-passwd-type.qml:97
458460msgctxt "Label: Type of security method"
459461msgid "Swipe"
460−msgstr ""
462+msgstr "Swipe"
461463
462464#: qml/Wizard/Pages/30-passwd-type.qml:98
463465msgctxt "Label: Description of security method"
464466msgid "No security"
465−msgstr ""
467+msgstr "Ingen sikkerhed"
466468
467469#: qml/Wizard/Pages/30-passwd-type.qml:100
468470msgctxt "Label: Type of security method"
469471msgid "Passcode"
470−msgstr ""
472+msgstr "Adgangskode"
471473
472474#: qml/Wizard/Pages/30-passwd-type.qml:101
473475msgctxt "Label: Description of security method"
474476msgid "4 digits only"
475−msgstr ""
477+msgstr "Kun 4 tal"
476478
477479#: qml/Wizard/Pages/30-passwd-type.qml:103
478480msgctxt "Label: Type of security method"
479481msgid "Passphrase"
480−msgstr ""
482+msgstr "Adgangsfrase"
481483
482484#: qml/Wizard/Pages/30-passwd-type.qml:104
483485msgctxt "Label: Description of security method"
484486msgid "Numbers and letters"
485−msgstr ""
487+msgstr "Tal og bogstaver"
486488
487489#: qml/Wizard/Pages/40-wifi.qml:29
488490msgid "Connect to Wi‑Fi"
@@ -498,7 +500,7 @@ msgstr "Ingen tilgængelige netværk."
498500
499501#: qml/Wizard/Pages/50-location.qml:27
500502msgid "Location"
501−msgstr ""
503+msgstr "Placering"
502504
503505#: qml/Wizard/Pages/50-location.qml:62
504506msgid "Let the phone detect your location:"
@@ -564,15 +566,15 @@ msgstr "Din telefon er nu klar til brug."
564566
565567#: qml/Wizard/Pages/80-finished.qml:53
566568msgid "Finish"
567−msgstr ""
569+msgstr "Afslut"
568570
569571#: qml/Wizard/Pages/here-terms.qml:27
570572msgid "Terms & Conditions"
571−msgstr ""
573+msgstr "Betingelser og vilkår"
572574
573575#: qml/Wizard/Pages/passwd-confirm.qml:49
574576msgid "Confirm passphrase"
575−msgstr ""
577+msgstr "Bekæft adgangsfrase"
576578
577579#: qml/Wizard/Pages/passwd-confirm.qml:50
578580msgid "Confirm passcode"
@@ -581,7 +583,7 @@ msgstr "Bekræft adgangskode"
581583#: qml/Wizard/Pages/passwd-confirm.qml:53
582584#: qml/Wizard/Pages/passwd-confirm.qml:54
583585msgid "Please try again."
584−msgstr ""
586+msgstr "Forsøg venligst igen."
585587
586588#: qml/Wizard/Pages/passwd-set.qml:61
587589msgid "Choose your passcode"
@@ -589,7 +591,7 @@ msgstr "Vælg din adgangskode"
589591
590592#: qml/Wizard/Pages/passwd-set.qml:68
591593msgid "Passphrase must be 4 characters long"
592−msgstr ""
594+msgstr "Adgangsfrasen skal være mindst 4 tegn"
593595
594596#: qml/Wizard/StackButton.qml:39
595597#, qt-format
po/de.po+31−31
@@ -8,15 +8,15 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-03-06 14:24+0000\n"
12−"Last-Translator: Niklas Wenzel <nikwen.developer@gmail.com>\n"
11+"PO-Revision-Date: 2015-06-17 14:05+0000\n"
12+"Last-Translator: JkB <Unknown>\n"
1313"Language-Team: German <de@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=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-04-23 06:48+0000\n"
19−"X-Generator: Launchpad (build 17430)\n"
18+"X-Launchpad-Export-Date: 2015-06-19 05:01+0000\n"
19+"X-Generator: Launchpad (build 17570)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -51,13 +51,13 @@ msgstr ""
5151#, qt-format
5252msgid "Please wait %1 minute and then try again…"
5353msgid_plural "Please wait %1 minutes and then try again…"
54−msgstr[0] "Bitte warten Sie %1 Minute und versuchen Sie es dann erneut..."
55−msgstr[1] "Bitte warten Sie %1 Minuten und versuchen Sie es dann erneut..."
54+msgstr[0] "Bitte %1 Minute warten und es dann erneut versuchen …"
55+msgstr[1] "Bitte %1 Minuten warten und es dann erneut versuchen …"
5656
5757#: qml/Components/Dialogs.qml:69
5858msgctxt "Title: Lock/Log out dialog"
5959msgid "Log out"
60−msgstr ""
60+msgstr "Abmelden"
6161
6262#: qml/Components/Dialogs.qml:70
6363msgid "Are you sure you want to log out?"
@@ -66,12 +66,12 @@ msgstr "Sind Sie sicher, dass Sie sich abmelden wollen?"
6666#: qml/Components/Dialogs.qml:72
6767msgctxt "Button: Lock the system"
6868msgid "Lock"
69−msgstr ""
69+msgstr "Sperren"
7070
7171#: qml/Components/Dialogs.qml:79
7272msgctxt "Button: Log out from the system"
7373msgid "Log Out"
74−msgstr ""
74+msgstr "Abmelden"
7575
7676#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
7777#: qml/Components/Dialogs.qml:173
@@ -81,7 +81,7 @@ msgstr "Abbrechen"
8181#: qml/Components/Dialogs.qml:98
8282msgctxt "Title: Reboot/Shut down dialog"
8383msgid "Shut down"
84−msgstr ""
84+msgstr "Ausschalten"
8585
8686#: qml/Components/Dialogs.qml:99
8787msgid "Are you sure you want to shut down?"
@@ -90,17 +90,17 @@ msgstr "Sind Sie sicher, dass Sie herunterfahren wollen?"
9090#: qml/Components/Dialogs.qml:101
9191msgctxt "Button: Reboot the system"
9292msgid "Reboot"
93−msgstr ""
93+msgstr "Neustarten"
9494
9595#: qml/Components/Dialogs.qml:109
9696msgctxt "Button: Shut down the system"
9797msgid "Shut down"
98−msgstr ""
98+msgstr "Ausschalten"
9999
100100#: qml/Components/Dialogs.qml:129
101101msgctxt "Title: Reboot dialog"
102102msgid "Reboot"
103−msgstr ""
103+msgstr "Neustarten"
104104
105105#: qml/Components/Dialogs.qml:130
106106msgid "Are you sure you want to reboot?"
@@ -117,7 +117,7 @@ msgstr "Ja"
117117#: qml/Components/Dialogs.qml:152
118118msgctxt "Title: Power off/Restart dialog"
119119msgid "Power"
120−msgstr ""
120+msgstr "Ausschalten"
121121
122122#: qml/Components/Dialogs.qml:153
123123msgid ""
@@ -130,12 +130,12 @@ msgstr ""
130130#: qml/Components/Dialogs.qml:155
131131msgctxt "Button: Power off the system"
132132msgid "Power off"
133−msgstr ""
133+msgstr "Ausschalten"
134134
135135#: qml/Components/Dialogs.qml:164
136136msgctxt "Button: Restart the system"
137137msgid "Restart"
138−msgstr ""
138+msgstr "Neustarten"
139139
140140#: qml/Components/Lockscreen.qml:245
141141msgid "Return to Call"
@@ -160,22 +160,22 @@ msgstr "Alle anzeigen"
160160#: qml/Dash/GenericScopeView.qml:594
161161msgctxt "Label: Hint for dash search line edit"
162162msgid "Search"
163−msgstr ""
163+msgstr "Suchen"
164164
165165#: qml/Dash/PageHeader.qml:269
166166msgctxt "Button: Open the Ubuntu Store"
167167msgid "Store"
168−msgstr ""
168+msgstr "Speichern"
169169
170170#: qml/Dash/PageHeader.qml:276
171171msgctxt "Button: Start a search in the current dash scope"
172172msgid "Search"
173−msgstr ""
173+msgstr "Suchen"
174174
175175#: qml/Dash/PageHeader.qml:286
176176msgctxt "Button: Show the current dash scope settings"
177177msgid "Settings"
178−msgstr ""
178+msgstr "Einstellungen"
179179
180180#: qml/Dash/PageHeader.qml:293
181181msgid "Remove from Favorites"
@@ -276,7 +276,7 @@ msgstr "%1 eingeben"
276276
277277#: qml/Greeter/NarrowView.qml:116 qml/Wizard/Pages/passwd-set.qml:60
278278msgid "Enter passphrase"
279−msgstr "Passwort eingeben"
279+msgstr "Passphrase eingeben"
280280
281281#: qml/Greeter/NarrowView.qml:117
282282msgid "Enter passcode"
@@ -305,7 +305,7 @@ msgstr "Passwort anzeigen"
305305
306306#: qml/Panel/ActiveCallHint.qml:79
307307msgid "Tap to return to call..."
308−msgstr "Tippen um zum Anruf zurückzugehen"
308+msgstr "Tippen, um zum Anruf zurückzugehen …"
309309
310310#: qml/Panel/ActiveCallHint.qml:92
311311msgid "Conference"
@@ -342,7 +342,7 @@ msgstr "Fehlgeschlagen, tippen Sie zum Wiederholen"
342342#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
343343msgctxt "Label: Hint in message indicator line edit"
344344msgid "Reply"
345−msgstr ""
345+msgstr "Antworten"
346346
347347#: qml/Panel/Indicators/ModemInfoItem.qml:105
348348msgid "Unlock SIM"
@@ -376,7 +376,7 @@ msgstr "Zum Fortfahren hier tippen."
376376
377377#: qml/Tutorial/TutorialContent.qml:81
378378msgid "Click here to finish."
379−msgstr ""
379+msgstr "Zum Abschließen bitte hier klicken."
380380
381381#: qml/Tutorial/TutorialLeftFinish.qml:24
382382msgid "These are the shortcuts to favorite apps"
@@ -413,7 +413,7 @@ msgstr "Sehen Sie alle Ihre laufenden Anwendungen."
413413#: qml/Wizard/Page.qml:89
414414msgctxt "Button: Go back one page in the Wizard"
415415msgid "Back"
416−msgstr ""
416+msgstr "Zurück"
417417
418418#: qml/Wizard/Pages/10-welcome.qml:27
419419msgid "Hi!"
@@ -457,32 +457,32 @@ msgstr "Bitte wählen Sie aus, wie Sie Ihr Telefon entsperren wollen."
457457#: qml/Wizard/Pages/30-passwd-type.qml:97
458458msgctxt "Label: Type of security method"
459459msgid "Swipe"
460−msgstr ""
460+msgstr "Wischen"
461461
462462#: qml/Wizard/Pages/30-passwd-type.qml:98
463463msgctxt "Label: Description of security method"
464464msgid "No security"
465−msgstr ""
465+msgstr "Keine Sicherheitssperre"
466466
467467#: qml/Wizard/Pages/30-passwd-type.qml:100
468468msgctxt "Label: Type of security method"
469469msgid "Passcode"
470−msgstr ""
470+msgstr "PIN"
471471
472472#: qml/Wizard/Pages/30-passwd-type.qml:101
473473msgctxt "Label: Description of security method"
474474msgid "4 digits only"
475−msgstr ""
475+msgstr "nur vier Ziffern"
476476
477477#: qml/Wizard/Pages/30-passwd-type.qml:103
478478msgctxt "Label: Type of security method"
479479msgid "Passphrase"
480−msgstr ""
480+msgstr "Kennwort"
481481
482482#: qml/Wizard/Pages/30-passwd-type.qml:104
483483msgctxt "Label: Description of security method"
484484msgid "Numbers and letters"
485−msgstr ""
485+msgstr "Ziffern und Buchstaben"
486486
487487#: qml/Wizard/Pages/40-wifi.qml:29
488488msgid "Connect to Wi‑Fi"
po/el.po+11−11
@@ -8,15 +8,15 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-04-22 09:03+0000\n"
11+"PO-Revision-Date: 2015-06-18 19:05+0000\n"
1212"Last-Translator: Simos Xenitellis <Unknown>\n"
1313"Language-Team: Greek <el@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=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-04-23 06:48+0000\n"
19−"X-Generator: Launchpad (build 17430)\n"
18+"X-Launchpad-Export-Date: 2015-06-19 05:01+0000\n"
19+"X-Generator: Launchpad (build 17570)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -117,7 +117,7 @@ msgstr "Ναι"
117117#: qml/Components/Dialogs.qml:152
118118msgctxt "Title: Power off/Restart dialog"
119119msgid "Power"
120−msgstr ""
120+msgstr "Τερματισμός"
121121
122122#: qml/Components/Dialogs.qml:153
123123msgid ""
@@ -165,17 +165,17 @@ msgstr "Αναζήτηση"
165165#: qml/Dash/PageHeader.qml:269
166166msgctxt "Button: Open the Ubuntu Store"
167167msgid "Store"
168−msgstr ""
168+msgstr "Κατάστημα"
169169
170170#: qml/Dash/PageHeader.qml:276
171171msgctxt "Button: Start a search in the current dash scope"
172172msgid "Search"
173−msgstr ""
173+msgstr "Αναζήτηση"
174174
175175#: qml/Dash/PageHeader.qml:286
176176msgctxt "Button: Show the current dash scope settings"
177177msgid "Settings"
178−msgstr ""
178+msgstr "Ρυθμίσεις"
179179
180180#: qml/Dash/PageHeader.qml:293
181181msgid "Remove from Favorites"
@@ -378,7 +378,7 @@ msgstr "Πατήστε εδώ για συνέχεια."
378378
379379#: qml/Tutorial/TutorialContent.qml:81
380380msgid "Click here to finish."
381−msgstr ""
381+msgstr "Πατήστε εδώ για τέλος."
382382
383383#: qml/Tutorial/TutorialLeftFinish.qml:24
384384msgid "These are the shortcuts to favorite apps"
@@ -510,12 +510,12 @@ msgstr "Επιτρέψετε το τηλέφωνο να εντοπίσει τη
510510
511511#: qml/Wizard/Pages/50-location.qml:69
512512msgid "Using GPS only (less accurate)"
513−msgstr "Χρήση μόνο GPS (λιγότερο ακριβή)"
513+msgstr "Χρήση μόνο GPS (λιγότερο ακριβής)"
514514
515515#: qml/Wizard/Pages/50-location.qml:86
516516msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
517517msgstr ""
518−"Χρήση GPS, ανωνυνοποιημένα δίκτυα Wi-Fi και πληροφορίες δικτύων κινητής "
518+"Χρήση GPS, ανωνυμοποιημένων δικτύων Wi-Fi και πληροφορίες δικτύων κινητής "
519519"τηλεφωνίας (συνιστάται)"
520520
521521#. TRANSLATORS: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
@@ -546,7 +546,7 @@ msgid ""
546546"partners, the makers of the operating system."
547547msgstr ""
548548"Το τηλέφωνό σας είναι ρυθμισμένο να στέλνει αυτόματα αναφορές σφαλμάτων στην "
549−"Canonical και τους συνεργάτες της, τους δημιουργούς τους λειτουργικού "
549+"Canonical και τους συνεργάτες της, τους δημιουργούς του λειτουργικού "
550550"συστήματος."
551551
552552#: qml/Wizard/Pages/60-reporting.qml:43
po/en_GB.po+3−3
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-01-22 12:14+0000\n"
11+"PO-Revision-Date: 2015-04-23 12:59+0000\n"
1212"Last-Translator: Andi Chandler <Unknown>\n"
1313"Language-Team: English (United Kingdom) <en_GB@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=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-04-23 06:48+0000\n"
18+"X-Launchpad-Export-Date: 2015-04-24 05:39+0000\n"
1919"X-Generator: Launchpad (build 17430)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
@@ -371,7 +371,7 @@ msgstr "Tap here to continue."
371371
372372#: qml/Tutorial/TutorialContent.qml:81
373373msgid "Click here to finish."
374−msgstr ""
374+msgstr "Click here to finish."
375375
376376#: qml/Tutorial/TutorialLeftFinish.qml:24
377377msgid "These are the shortcuts to favorite apps"
po/eo.po+6−6
@@ -8,15 +8,15 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2013-06-26 22:35+0000\n"
12−"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
11+"PO-Revision-Date: 2015-06-06 23:37+0000\n"
12+"Last-Translator: Eliovir <Unknown>\n"
1313"Language-Team: Esperanto <eo@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=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-04-23 06:48+0000\n"
19−"X-Generator: Launchpad (build 17430)\n"
18+"X-Launchpad-Export-Date: 2015-06-08 05:26+0000\n"
19+"X-Generator: Launchpad (build 17540)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -197,7 +197,7 @@ msgstr ""
197197
198198#: qml/Dash/Previews/PreviewRatingInput.qml:166
199199msgid "Send"
200−msgstr ""
200+msgstr "Sendi"
201201
202202#: qml/Dash/PullToRefreshScopeStyle.qml:55
203203msgid "Pull to refresh…"
@@ -291,7 +291,7 @@ msgstr ""
291291
292292#: qml/Notifications/NotificationMenuItemFactory.qml:128
293293msgid "Show password"
294−msgstr ""
294+msgstr "Montri pasvorton"
295295
296296#: qml/Panel/ActiveCallHint.qml:79
297297msgid "Tap to return to call..."
po/es.po+4−4
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-03-27 21:24+0000\n"
12−"Last-Translator: Adolfo Jayme <fitoschido@gmail.com>\n"
11+"PO-Revision-Date: 2015-04-24 17:45+0000\n"
12+"Last-Translator: Paco Molinero <paco@byasl.com>\n"
1313"Language-Team: Spanish <es@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=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-04-23 06:48+0000\n"
18+"X-Launchpad-Export-Date: 2015-04-25 05:38+0000\n"
1919"X-Generator: Launchpad (build 17430)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
@@ -375,7 +375,7 @@ msgstr "Toque aquí para continuar."
375375
376376#: qml/Tutorial/TutorialContent.qml:81
377377msgid "Click here to finish."
378−msgstr ""
378+msgstr "Pulse aquí para finalizar."
379379
380380#: qml/Tutorial/TutorialLeftFinish.qml:24
381381msgid "These are the shortcuts to favorite apps"
po/fi.po+4−4
@@ -8,15 +8,15 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-03-25 07:55+0000\n"
11+"PO-Revision-Date: 2015-05-19 09:14+0000\n"
1212"Last-Translator: Jiri Grönroos <Unknown>\n"
1313"Language-Team: Finnish <fi@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=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-04-23 06:48+0000\n"
19−"X-Generator: Launchpad (build 17430)\n"
18+"X-Launchpad-Export-Date: 2015-05-22 05:09+0000\n"
19+"X-Generator: Launchpad (build 17514)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -373,7 +373,7 @@ msgstr "Napauta tästä jatkaaksesi."
373373
374374#: qml/Tutorial/TutorialContent.qml:81
375375msgid "Click here to finish."
376−msgstr ""
376+msgstr "Viimeistele napsauttamalla tästä."
377377
378378#: qml/Tutorial/TutorialLeftFinish.qml:24
379379msgid "These are the shortcuts to favorite apps"
po/fr.po+4−4
@@ -8,15 +8,15 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-03-19 21:55+0000\n"
11+"PO-Revision-Date: 2015-05-10 15:54+0000\n"
1212"Last-Translator: Anne <anneonyme017@gmail.com>\n"
1313"Language-Team: French <fr@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=2; plural=n > 1;\n"
18−"X-Launchpad-Export-Date: 2015-04-23 06:48+0000\n"
19−"X-Generator: Launchpad (build 17430)\n"
18+"X-Launchpad-Export-Date: 2015-05-11 05:41+0000\n"
19+"X-Generator: Launchpad (build 17487)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -377,7 +377,7 @@ msgstr "Appuyez ici pour continuer."
377377
378378#: qml/Tutorial/TutorialContent.qml:81
379379msgid "Click here to finish."
380−msgstr ""
380+msgstr "Cliquez ici pour terminer."
381381
382382#: qml/Tutorial/TutorialLeftFinish.qml:24
383383msgid "These are the shortcuts to favorite apps"
po/gd.po+4−4
@@ -8,7 +8,7 @@ msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-03-27 09:50+0000\n"
11+"PO-Revision-Date: 2015-05-01 16:39+0000\n"
1212"Last-Translator: GunChleoc <Unknown>\n"
1313"Language-Team: Fòram na Gàidhlig\n"
1414"MIME-Version: 1.0\n"
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=4; plural=(n==1 || n==11) ? 0 : (n==2 || n==12) ? 1 : "
1818"(n > 2 && n < 20) ? 2 : 3;\n"
19−"X-Launchpad-Export-Date: 2015-04-23 06:48+0000\n"
20−"X-Generator: Launchpad (build 17430)\n"
19+"X-Launchpad-Export-Date: 2015-05-02 05:29+0000\n"
20+"X-Generator: Launchpad (build 17474)\n"
2121"Language: gd\n"
2222
2323#: plugins/LightDM/Greeter.cpp:112
@@ -169,7 +169,7 @@ msgstr "Lorg"
169169#: qml/Dash/PageHeader.qml:269
170170msgctxt "Button: Open the Ubuntu Store"
171171msgid "Store"
172−msgstr "A' bhùth"
172+msgstr "Am bùth"
173173
174174#: qml/Dash/PageHeader.qml:276
175175msgctxt "Button: Start a search in the current dash scope"
po/hu.po+14−14
@@ -8,15 +8,15 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-03-25 20:55+0000\n"
11+"PO-Revision-Date: 2015-06-19 09:54+0000\n"
1212"Last-Translator: Richard Somlói <ricsipontaz@gmail.com>\n"
1313"Language-Team: Hungarian <hu@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=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-04-23 06:48+0000\n"
19−"X-Generator: Launchpad (build 17430)\n"
18+"X-Launchpad-Export-Date: 2015-06-20 05:33+0000\n"
19+"X-Generator: Launchpad (build 17572)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -113,7 +113,7 @@ msgstr "Igen"
113113#: qml/Components/Dialogs.qml:152
114114msgctxt "Title: Power off/Restart dialog"
115115msgid "Power"
116−msgstr "Főkapcsoló"
116+msgstr "Menü"
117117
118118#: qml/Components/Dialogs.qml:153
119119msgid ""
@@ -239,11 +239,11 @@ msgstr "Koppintson a feloldáshoz"
239239
240240#: qml/Greeter/NarrowView.qml:53 qml/Wizard/Pages/passwd-confirm.qml:53
241241msgid "Sorry, incorrect passphrase."
242−msgstr "Sajnálom, helytelen jelmondat."
242+msgstr "Elnézést, helytelen jelmondat."
243243
244244#: qml/Greeter/NarrowView.qml:54 qml/Wizard/Pages/passwd-confirm.qml:54
245245msgid "Sorry, incorrect passcode."
246−msgstr "Sajnálom, helytelen jelkód."
246+msgstr "Elnézést, helytelen jelkód."
247247
248248#: qml/Greeter/NarrowView.qml:55
249249msgid "This will be your last attempt."
@@ -281,11 +281,11 @@ msgstr "Jelkód megadása"
281281#: qml/Greeter/NarrowView.qml:118
282282#, qt-format
283283msgid "Sorry, incorrect %1"
284−msgstr "Sajnálom, helytelen %1"
284+msgstr "Elnézést, helytelen %1"
285285
286286#: qml/Greeter/NarrowView.qml:119
287287msgid "Sorry, incorrect passphrase"
288−msgstr "Sajnálom, helytelen jelmondat"
288+msgstr "Elnézést, helytelen jelmondat"
289289
290290#: qml/Greeter/NarrowView.qml:120
291291msgid "Please re-enter"
@@ -293,7 +293,7 @@ msgstr "Próbálja újra"
293293
294294#: qml/Greeter/NarrowView.qml:121
295295msgid "Sorry, incorrect passcode"
296−msgstr "Sajnálom, helytelen jelkód"
296+msgstr "Elnézést, helytelen jelkód"
297297
298298#: qml/Notifications/NotificationMenuItemFactory.qml:128
299299msgid "Show password"
@@ -354,7 +354,7 @@ msgstr "Ez a művelet másként viselkedhet a különböző alkalmazásoknál"
354354
355355#: qml/Tutorial/TutorialBottomFinish.qml:25
356356msgid "Tap here to finish."
357−msgstr "Koppintson a befejezéshez."
357+msgstr "Koppintson ide a befejezéshez."
358358
359359#: qml/Tutorial/TutorialBottom.qml:28
360360msgid "Open special menus"
@@ -371,11 +371,11 @@ msgstr "Koppintson ide a folytatáshoz."
371371
372372#: qml/Tutorial/TutorialContent.qml:81
373373msgid "Click here to finish."
374−msgstr ""
374+msgstr "Kattintson ide a befejezéshez."
375375
376376#: qml/Tutorial/TutorialLeftFinish.qml:24
377377msgid "These are the shortcuts to favorite apps"
378−msgstr "Ezek a kedvenc alkalmazások gyorsít-indítói"
378+msgstr "Ezek a kedvenc alkalmazások gyorsindítói"
379379
380380#: qml/Tutorial/TutorialLeft.qml:26
381381msgid "Open the launcher"
@@ -395,7 +395,7 @@ msgstr "Próbálja újra."
395395
396396#: qml/Tutorial/TutorialRight.qml:31
397397msgid "To view open apps"
398−msgstr "Nyitott alkalmazások megtekintéséhez"
398+msgstr "A nyitott alkalmazások megtekintéséhez"
399399
400400#: qml/Tutorial/TutorialRight.qml:32
401401msgid "Long swipe from the right edge."
@@ -403,7 +403,7 @@ msgstr "Húzza az ujját hosszan a kijelző jobboldalától indulva."
403403
404404#: qml/Tutorial/TutorialRight.qml:122
405405msgid "View all your running tasks."
406−msgstr "Minden futó feladat megtekintése."
406+msgstr "Az összes futó alkalmazás megtekinthető."
407407
408408#: qml/Wizard/Page.qml:89
409409msgctxt "Button: Go back one page in the Wizard"
po/ia.ponew file+684
@@ -0,0 +1,684 @@
1+# Interlingua translation for unity8
2+# Copyright (c) 2015 Rosetta Contributors and Canonical Ltd 2015
3+# This file is distributed under the same license as the unity8 package.
4+# FIRST AUTHOR <EMAIL@ADDRESS>, 2015.
5+#
6+msgid ""
7+msgstr ""
8+"Project-Id-Version: unity8\n"
9+"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10+"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11+"PO-Revision-Date: 2015-06-08 18:58+0000\n"
12+"Last-Translator: karm <melo@carmu.com>\n"
13+"Language-Team: Interlingua <ia@li.org>\n"
14+"MIME-Version: 1.0\n"
15+"Content-Type: text/plain; charset=UTF-8\n"
16+"Content-Transfer-Encoding: 8bit\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18+"X-Launchpad-Export-Date: 2015-06-09 05:42+0000\n"
19+"X-Generator: Launchpad (build 17549)\n"
20+
21+#: plugins/LightDM/Greeter.cpp:112
22+msgid "Password: "
23+msgstr "Contrasigno: "
24+
25+#: plugins/Unity/Launcher/launcheritem.cpp:45
26+#: plugins/Unity/Launcher/launcheritem.cpp:100
27+msgid "Pin shortcut"
28+msgstr "Via-breve a pin"
29+
30+#: plugins/Unity/Launcher/launcheritem.cpp:100
31+msgid "Unpin shortcut"
32+msgstr "Via-breve a levar pin"
33+
34+#: qml/Components/DelayedLockscreen.qml:49
35+msgid "Device Locked"
36+msgstr "Dispositivo blocate"
37+
38+#: qml/Components/DelayedLockscreen.qml:64
39+msgid "You have been locked out due to too many failed passphrase attempts."
40+msgstr ""
41+"Tu ha essite blocate per multe tentativas de phrase-contrasigno fallite."
42+
43+#: qml/Components/DelayedLockscreen.qml:65
44+msgid "You have been locked out due to too many failed passcode attempts."
45+msgstr ""
46+"Tu ha essite blocate per multe tentativas de codice-contrasigno fallite."
47+
48+#: qml/Components/DelayedLockscreen.qml:74
49+#, qt-format
50+msgid "Please wait %1 minute and then try again…"
51+msgid_plural "Please wait %1 minutes and then try again…"
52+msgstr[0] "Per favor attende %1 minuta e postea tenta de nove"
53+msgstr[1] "Per favor attende %1 minutas e postea tentar de nove"
54+
55+#: qml/Components/Dialogs.qml:69
56+msgctxt "Title: Lock/Log out dialog"
57+msgid "Log out"
58+msgstr "Clauder de session"
59+
60+#: qml/Components/Dialogs.qml:70
61+msgid "Are you sure you want to log out?"
62+msgstr "Desira tu vermente Clauder le Session"
63+
64+#: qml/Components/Dialogs.qml:72
65+msgctxt "Button: Lock the system"
66+msgid "Lock"
67+msgstr "Blocar"
68+
69+#: qml/Components/Dialogs.qml:79
70+msgctxt "Button: Log out from the system"
71+msgid "Log Out"
72+msgstr "Claude session"
73+
74+#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
75+#: qml/Components/Dialogs.qml:173
76+msgid "Cancel"
77+msgstr "Cancellar"
78+
79+#: qml/Components/Dialogs.qml:98
80+msgctxt "Title: Reboot/Shut down dialog"
81+msgid "Shut down"
82+msgstr "Stoppar"
83+
84+#: qml/Components/Dialogs.qml:99
85+msgid "Are you sure you want to shut down?"
86+msgstr "Desira tu vermente Stoppar"
87+
88+#: qml/Components/Dialogs.qml:101
89+msgctxt "Button: Reboot the system"
90+msgid "Reboot"
91+msgstr "Reinitiar"
92+
93+#: qml/Components/Dialogs.qml:109
94+msgctxt "Button: Shut down the system"
95+msgid "Shut down"
96+msgstr "Stoppar"
97+
98+#: qml/Components/Dialogs.qml:129
99+msgctxt "Title: Reboot dialog"
100+msgid "Reboot"
101+msgstr "Reinitiar"
102+
103+#: qml/Components/Dialogs.qml:130
104+msgid "Are you sure you want to reboot?"
105+msgstr "Desira tu vermente reinitiar"
106+
107+#: qml/Components/Dialogs.qml:132
108+msgid "No"
109+msgstr "No"
110+
111+#: qml/Components/Dialogs.qml:138
112+msgid "Yes"
113+msgstr "Si"
114+
115+#: qml/Components/Dialogs.qml:152
116+msgctxt "Title: Power off/Restart dialog"
117+msgid "Power"
118+msgstr "Energia"
119+
120+#: qml/Components/Dialogs.qml:153
121+msgid ""
122+"Are you sure you would like\n"
123+"to power off?"
124+msgstr ""
125+"Desira tu vermente \n"
126+"stoppar le energia?"
127+
128+#: qml/Components/Dialogs.qml:155
129+msgctxt "Button: Power off the system"
130+msgid "Power off"
131+msgstr "Stoppar le energia"
132+
133+#: qml/Components/Dialogs.qml:164
134+msgctxt "Button: Restart the system"
135+msgid "Restart"
136+msgstr "Reinitiar"
137+
138+#: qml/Components/Lockscreen.qml:245
139+msgid "Return to Call"
140+msgstr "Retornar al appello"
141+
142+#: qml/Components/Lockscreen.qml:245
143+msgid "Emergency Call"
144+msgstr "Appello de emergentia"
145+
146+#: qml/Components/Lockscreen.qml:277
147+msgid "OK"
148+msgstr "OK"
149+
150+#: qml/Dash/GenericScopeView.qml:532 qml/Dash/GenericScopeView.qml:690
151+msgid "See less"
152+msgstr "Vider minus"
153+
154+#: qml/Dash/GenericScopeView.qml:532
155+msgid "See all"
156+msgstr "Vider toto"
157+
158+#: qml/Dash/GenericScopeView.qml:594
159+msgctxt "Label: Hint for dash search line edit"
160+msgid "Search"
161+msgstr "Cerca"
162+
163+#: qml/Dash/PageHeader.qml:269
164+msgctxt "Button: Open the Ubuntu Store"
165+msgid "Store"
166+msgstr "Salva"
167+
168+#: qml/Dash/PageHeader.qml:276
169+msgctxt "Button: Start a search in the current dash scope"
170+msgid "Search"
171+msgstr "Cerca"
172+
173+#: qml/Dash/PageHeader.qml:286
174+msgctxt "Button: Show the current dash scope settings"
175+msgid "Settings"
176+msgstr "Configurationes"
177+
178+#: qml/Dash/PageHeader.qml:293
179+msgid "Remove from Favorites"
180+msgstr "Remover ex le Favoritos"
181+
182+#: qml/Dash/PageHeader.qml:293
183+msgid "Add to Favorites"
184+msgstr "Adder al Favoritos"
185+
186+#: qml/Dash/Previews/PreviewActionCombo.qml:35
187+msgid "More..."
188+msgstr "Plus..."
189+
190+#: qml/Dash/Previews/PreviewActionCombo.qml:35
191+msgid "Less..."
192+msgstr "Minus..."
193+
194+#: qml/Dash/Previews/PreviewRatingInput.qml:81
195+msgid "Rate this"
196+msgstr "Evalutar isto"
197+
198+#: qml/Dash/Previews/PreviewRatingInput.qml:126
199+msgid "Add a review"
200+msgstr "Adder un revision"
201+
202+#: qml/Dash/Previews/PreviewRatingInput.qml:166
203+msgid "Send"
204+msgstr "Mandar"
205+
206+#: qml/Dash/PullToRefreshScopeStyle.qml:55
207+msgid "Pull to refresh…"
208+msgstr "Tirar pro renovar…"
209+
210+#: qml/Dash/PullToRefreshScopeStyle.qml:60
211+msgid "Release to refresh…"
212+msgstr "Relaxar pro renovar…"
213+
214+#: qml/Dash/ScopeSettings/ScopeSettingBoolean.qml:43
215+msgid "Enable location data"
216+msgstr "Habilita le datos del location"
217+
218+#: qml/Dash/ScopesList.qml:66
219+msgid "Manage"
220+msgstr "Tractar"
221+
222+#: qml/Dash/ScopesList.qml:112
223+msgid "Home"
224+msgstr "A domo"
225+
226+#: qml/Dash/ScopesList.qml:113
227+msgid "Also installed"
228+msgstr "Ja installate"
229+
230+#: qml/Greeter/CoverPage.qml:107
231+msgid "Unlock"
232+msgstr "Liberar"
233+
234+#: qml/Greeter/LoginList.qml:236
235+msgid "Retry"
236+msgstr "Re-provar"
237+
238+#: qml/Greeter/LoginList.qml:237
239+msgid "Tap to unlock"
240+msgstr "Pulsa pro liberar"
241+
242+#: qml/Greeter/NarrowView.qml:53 qml/Wizard/Pages/passwd-confirm.qml:53
243+msgid "Sorry, incorrect passphrase."
244+msgstr "Displacente, incorrecte phrase-contrasigno."
245+
246+#: qml/Greeter/NarrowView.qml:54 qml/Wizard/Pages/passwd-confirm.qml:54
247+msgid "Sorry, incorrect passcode."
248+msgstr "Displacente, incorrecte codice-contrasigno."
249+
250+#: qml/Greeter/NarrowView.qml:55
251+msgid "This will be your last attempt."
252+msgstr "Isto essera tu ultime tentativa."
253+
254+#: qml/Greeter/NarrowView.qml:57
255+msgid ""
256+"If passphrase is entered incorrectly, your phone will conduct a factory "
257+"reset and all personal data will be deleted."
258+msgstr ""
259+"Si le phrase-contrasigno es inserite incorrectemente, tu telephono conducera "
260+"un prefixation al fabrica e omne le datos personal essera delite."
261+
262+#: qml/Greeter/NarrowView.qml:58
263+msgid ""
264+"If passcode is entered incorrectly, your phone will conduct a factory reset "
265+"and all personal data will be deleted."
266+msgstr ""
267+"Si le codice-contrasigno es inserite incorrectemente, tu telephono conducera "
268+"un prefixation a fabrica e omne le datos personal essera delite."
269+
270+#: qml/Greeter/NarrowView.qml:115
271+#, qt-format
272+msgid "Enter %1"
273+msgstr "Inserer %1"
274+
275+#: qml/Greeter/NarrowView.qml:116 qml/Wizard/Pages/passwd-set.qml:60
276+msgid "Enter passphrase"
277+msgstr "Inserer le phrase-contrasigno"
278+
279+#: qml/Greeter/NarrowView.qml:117
280+msgid "Enter passcode"
281+msgstr "Inserer le codice-contrasigno"
282+
283+#: qml/Greeter/NarrowView.qml:118
284+#, qt-format
285+msgid "Sorry, incorrect %1"
286+msgstr "Displacente, incorrecte %1"
287+
288+#: qml/Greeter/NarrowView.qml:119
289+msgid "Sorry, incorrect passphrase"
290+msgstr "Displacente, phrase-contrasigno incorrecte"
291+
292+#: qml/Greeter/NarrowView.qml:120
293+msgid "Please re-enter"
294+msgstr "Per favor re-inserer"
295+
296+#: qml/Greeter/NarrowView.qml:121
297+msgid "Sorry, incorrect passcode"
298+msgstr "Displacente, codice-contrasigno incorrecte"
299+
300+#: qml/Notifications/NotificationMenuItemFactory.qml:128
301+msgid "Show password"
302+msgstr "Monstrar le contrasigno"
303+
304+#: qml/Panel/ActiveCallHint.qml:79
305+msgid "Tap to return to call..."
306+msgstr "Colpar pro retornar al appello..."
307+
308+#: qml/Panel/ActiveCallHint.qml:92
309+msgid "Conference"
310+msgstr "Conferentia"
311+
312+#: qml/Panel/Indicators/MenuItemFactory.qml:639
313+msgid "Nothing is playing"
314+msgstr "Nil es jocante"
315+
316+#: qml/Panel/Indicators/MenuItemFactory.qml:787
317+msgid "In queue…"
318+msgstr "In cauda…"
319+
320+#: qml/Panel/Indicators/MenuItemFactory.qml:791
321+msgid "Downloading"
322+msgstr "Discargante"
323+
324+#: qml/Panel/Indicators/MenuItemFactory.qml:793
325+msgid "Paused, tap to resume"
326+msgstr "In pausa, colpar pro reprender"
327+
328+#: qml/Panel/Indicators/MenuItemFactory.qml:795
329+msgid "Canceled"
330+msgstr "Cancellate"
331+
332+#: qml/Panel/Indicators/MenuItemFactory.qml:797
333+msgid "Finished"
334+msgstr "Finite"
335+
336+#: qml/Panel/Indicators/MenuItemFactory.qml:799
337+msgid "Failed, tap to retry"
338+msgstr "Fallite, colpar pro re-provar"
339+
340+#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
341+msgctxt "Label: Hint in message indicator line edit"
342+msgid "Reply"
343+msgstr "Responde"
344+
345+#: qml/Panel/Indicators/ModemInfoItem.qml:105
346+msgid "Unlock SIM"
347+msgstr "Liberar le SIM"
348+
349+#: qml/Panel/Indicators/RoamingIndication.qml:27
350+msgid "Roaming"
351+msgstr "Roaming"
352+
353+#: qml/Tutorial/TutorialBottomFinish.qml:24
354+msgid "This action does different things for different apps"
355+msgstr "Iste action ha effectos differente pro applicationes differente"
356+
357+#: qml/Tutorial/TutorialBottomFinish.qml:25
358+msgid "Tap here to finish."
359+msgstr "Pulsa ci pro finir"
360+
361+#: qml/Tutorial/TutorialBottom.qml:28
362+msgid "Open special menus"
363+msgstr "Aperi le menus special"
364+
365+#: qml/Tutorial/TutorialBottom.qml:29
366+msgid "Swipe up from the bottom edge."
367+msgstr "Stria in alto ex le bordo basse."
368+
369+#: qml/Tutorial/TutorialContent.qml:81 qml/Tutorial/TutorialLeftFinish.qml:25
370+#: qml/Tutorial/TutorialRight.qml:144
371+msgid "Tap here to continue."
372+msgstr "Pulsa ci pro continuar"
373+
374+#: qml/Tutorial/TutorialContent.qml:81
375+msgid "Click here to finish."
376+msgstr "Clicca ci pro finir"
377+
378+#: qml/Tutorial/TutorialLeftFinish.qml:24
379+msgid "These are the shortcuts to favorite apps"
380+msgstr "Iste es le vias breve al applicationes favorite"
381+
382+#: qml/Tutorial/TutorialLeft.qml:26
383+msgid "Open the launcher"
384+msgstr "Aperi le lanceator"
385+
386+#: qml/Tutorial/TutorialLeft.qml:27
387+msgid "Short swipe from the left edge."
388+msgstr "Stria curte ex le bordo sinistre."
389+
390+#: qml/Tutorial/TutorialPage.qml:178
391+msgid "You almost got it!"
392+msgstr "Tu ha quasi obtenite lo!"
393+
394+#: qml/Tutorial/TutorialPage.qml:195
395+msgid "Try again."
396+msgstr "Prova de novo"
397+
398+#: qml/Tutorial/TutorialRight.qml:31
399+msgid "To view open apps"
400+msgstr "Aperi le applicationes pro vider"
401+
402+#: qml/Tutorial/TutorialRight.qml:32
403+msgid "Long swipe from the right edge."
404+msgstr "Stria longemente ex le bordo dextre."
405+
406+#: qml/Tutorial/TutorialRight.qml:122
407+msgid "View all your running tasks."
408+msgstr "Vide tote tu tasks fluente"
409+
410+#: qml/Wizard/Page.qml:89
411+msgctxt "Button: Go back one page in the Wizard"
412+msgid "Back"
413+msgstr "Retro"
414+
415+#: qml/Wizard/Pages/10-welcome.qml:27
416+msgid "Hi!"
417+msgstr "Salute!"
418+
419+#: qml/Wizard/Pages/10-welcome.qml:44
420+msgid "Welcome to your Ubuntu phone."
421+msgstr "Benvenite a tu telephono Ubuntu."
422+
423+#: qml/Wizard/Pages/10-welcome.qml:52
424+msgid "Let’s get started."
425+msgstr "Que nos initia."
426+
427+#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
428+#: qml/Wizard/Pages/40-wifi.qml:215 qml/Wizard/Pages/50-location.qml:131
429+#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
430+#: qml/Wizard/Pages/passwd-set.qml:90
431+msgid "Continue"
432+msgstr "Continua"
433+
434+#: qml/Wizard/Pages/20-sim.qml:25
435+msgid "Add a SIM card and restart your device"
436+msgstr "Adde un carta SIM e reinitia tu dispositivo"
437+
438+#: qml/Wizard/Pages/20-sim.qml:59
439+msgid "Without it, you won’t be able to make calls or use text messaging."
440+msgstr ""
441+"Sin illo, tu non es habile a facer appellos o usar le messages de texto."
442+
443+#: qml/Wizard/Pages/20-sim.qml:73 qml/Wizard/Pages/40-wifi.qml:215
444+msgid "Skip"
445+msgstr "Salta"
446+
447+#: qml/Wizard/Pages/30-passwd-type.qml:39
448+msgid "Lock security"
449+msgstr "Bloco de securitate"
450+
451+#: qml/Wizard/Pages/30-passwd-type.qml:74
452+msgid "Please select how you’d like to unlock your phone."
453+msgstr "Per favor selige in qual maniera disblocar tu telephono."
454+
455+#: qml/Wizard/Pages/30-passwd-type.qml:97
456+msgctxt "Label: Type of security method"
457+msgid "Swipe"
458+msgstr "Striar"
459+
460+#: qml/Wizard/Pages/30-passwd-type.qml:98
461+msgctxt "Label: Description of security method"
462+msgid "No security"
463+msgstr "Nulle securitate"
464+
465+#: qml/Wizard/Pages/30-passwd-type.qml:100
466+msgctxt "Label: Type of security method"
467+msgid "Passcode"
468+msgstr "Codice-contrasigno"
469+
470+#: qml/Wizard/Pages/30-passwd-type.qml:101
471+msgctxt "Label: Description of security method"
472+msgid "4 digits only"
473+msgstr "4 cifras sol"
474+
475+#: qml/Wizard/Pages/30-passwd-type.qml:103
476+msgctxt "Label: Type of security method"
477+msgid "Passphrase"
478+msgstr "Phrase-contrasigno"
479+
480+#: qml/Wizard/Pages/30-passwd-type.qml:104
481+msgctxt "Label: Description of security method"
482+msgid "Numbers and letters"
483+msgstr "Numeros e litteras"
484+
485+#: qml/Wizard/Pages/40-wifi.qml:29
486+msgid "Connect to Wi‑Fi"
487+msgstr "Connecter al Wi‑Fi"
488+
489+#: qml/Wizard/Pages/40-wifi.qml:168
490+msgid "Available networks…"
491+msgstr "Retes disponibile..."
492+
493+#: qml/Wizard/Pages/40-wifi.qml:169
494+msgid "No available networks."
495+msgstr "Nulle rete disponibile."
496+
497+#: qml/Wizard/Pages/50-location.qml:27
498+msgid "Location"
499+msgstr "Location"
500+
501+#: qml/Wizard/Pages/50-location.qml:62
502+msgid "Let the phone detect your location:"
503+msgstr "Lassar le telephono disvelar tu position:"
504+
505+#: qml/Wizard/Pages/50-location.qml:69
506+msgid "Using GPS only (less accurate)"
507+msgstr "Usar sol le GPS (minus accurate)"
508+
509+#: qml/Wizard/Pages/50-location.qml:86
510+msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
511+msgstr ""
512+"Usar le GPS, le Wi-Fi in modo anonyme e le informationes del rete cellular "
513+"(recommendate)"
514+
515+#. TRANSLATORS: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
516+#: qml/Wizard/Pages/50-location.qml:103
517+msgid ""
518+"By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
519+"conditions</a>."
520+msgstr ""
521+"Per le selection de iste option tu esser de accordo con le Nokia HERE <a "
522+"href='#'>terminos e conditiones</a>."
523+
524+#: qml/Wizard/Pages/50-location.qml:112
525+msgid "Not at all"
526+msgstr "No, del toto"
527+
528+#: qml/Wizard/Pages/50-location.qml:124
529+msgid "You can change your mind later in <b>System Settings</b>."
530+msgstr ""
531+"Tu pote cambiar tu desiderata plus tarde in <b>Configurationes del "
532+"systema</b>."
533+
534+#: qml/Wizard/Pages/60-reporting.qml:24
535+msgid "Improving your experience"
536+msgstr "Affinamento de tu experientia"
537+
538+#: qml/Wizard/Pages/60-reporting.qml:36
539+msgid ""
540+"Your phone is set up to automatically report errors to Canonical and its "
541+"partners, the makers of the operating system."
542+msgstr ""
543+"Tu telephono es configurate a reportar automaticamente le errores a "
544+"Canonical e su socios, le fabricantes del systema operative."
545+
546+#: qml/Wizard/Pages/60-reporting.qml:43
547+msgid ""
548+"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
549+"Privacy</b>"
550+msgstr ""
551+"Isto pote esser impedite in <b>Preparationes del systema</b> sub "
552+"<b>Securitate &amp; Confidentialitate</b>"
553+
554+#: qml/Wizard/Pages/80-finished.qml:24
555+msgid "All done"
556+msgstr "Toto preste"
557+
558+#: qml/Wizard/Pages/80-finished.qml:39
559+msgid "Nice work!"
560+msgstr "Belle obra!"
561+
562+#: qml/Wizard/Pages/80-finished.qml:46
563+msgid "Your phone is now ready to use."
564+msgstr "Tu telephono es ora preste al uso."
565+
566+#: qml/Wizard/Pages/80-finished.qml:53
567+msgid "Finish"
568+msgstr "Fin"
569+
570+#: qml/Wizard/Pages/here-terms.qml:27
571+msgid "Terms & Conditions"
572+msgstr "Terminos & Conditiones"
573+
574+#: qml/Wizard/Pages/passwd-confirm.qml:49
575+msgid "Confirm passphrase"
576+msgstr "Confirma le phrase-contrasigno"
577+
578+#: qml/Wizard/Pages/passwd-confirm.qml:50
579+msgid "Confirm passcode"
580+msgstr "Confirma le codice-contrasigno"
581+
582+#: qml/Wizard/Pages/passwd-confirm.qml:53
583+#: qml/Wizard/Pages/passwd-confirm.qml:54
584+msgid "Please try again."
585+msgstr "Reproba per favor."
586+
587+#: qml/Wizard/Pages/passwd-set.qml:61
588+msgid "Choose your passcode"
589+msgstr "Selige tu codice-contrasigno"
590+
591+#: qml/Wizard/Pages/passwd-set.qml:68
592+msgid "Passphrase must be 4 characters long"
593+msgstr "Le phrase-contrasigno debe esser longe 4 characteres"
594+
595+#: qml/Wizard/StackButton.qml:39
596+#, qt-format
597+msgid "〈 %1"
598+msgstr "〈 %1"
599+
600+#: qml/Wizard/StackButton.qml:42
601+#, qt-format
602+msgid "%1 〉"
603+msgstr "%1 〉"
604+
605+#~ msgid "Log out"
606+#~ msgstr "Clauder le Session"
607+
608+#~ msgid "Reboot"
609+#~ msgstr "Reinitiar"
610+
611+#~ msgid "Shut down"
612+#~ msgstr "Stoppar"
613+
614+#~ msgid "Power"
615+#~ msgstr "Energia"
616+
617+#~ msgid "Restart"
618+#~ msgstr "Re-Initiar"
619+
620+#~ msgid "Power off"
621+#~ msgstr "Stoppar le energia"
622+
623+#~ msgid "Top edge"
624+#~ msgstr "Bordo superior"
625+
626+#~ msgid "Try swiping from the top edge to access the indicators"
627+#~ msgstr "Proba striar le bordo superior pro acceder le indicatores"
628+
629+#~ msgid "Close"
630+#~ msgstr "Clauder"
631+
632+#~ msgid "Left edge"
633+#~ msgstr "Bordo sinistre"
634+
635+#~ msgid "Try swiping from the right edge to unlock the phone"
636+#~ msgstr "Proba striar le bordo dextere pro liberar le telephono"
637+
638+#~ msgid "Skip intro"
639+#~ msgstr "Saltar le introduction"
640+
641+#~ msgid "Right edge"
642+#~ msgstr "Bordo dextere"
643+
644+#~ msgid "Swipe up again to close the settings screen"
645+#~ msgstr "Stria novemente pro clauder le pagina del preparationes"
646+
647+#~ msgid "Swipe from the left to reveal the launcher for quick access to apps"
648+#~ msgstr ""
649+#~ "Striar ab le sinistra pro revelar le lanceator pro un celere accesso al "
650+#~ "applicationes"
651+
652+#~ msgid "Well done"
653+#~ msgstr "Ben!"
654+
655+#~ msgid "Search"
656+#~ msgstr "Cercar"
657+
658+#~ msgid "Store"
659+#~ msgstr "Salvar"
660+
661+#~ msgid "Settings"
662+#~ msgstr "Preparationes"
663+
664+#~ msgid "Confirm"
665+#~ msgstr "Confirmar"
666+
667+#~ msgid "Type or say a command"
668+#~ msgstr "Scriber o dicer un commando"
669+
670+#~ msgid "Loading. Please Wait..."
671+#~ msgstr "Cargante. Attender per favor..."
672+
673+#~ msgid "Speak Now..."
674+#~ msgstr "Parlar Ora..."
675+
676+#~ msgid "Speaking..."
677+#~ msgstr "Parlante..."
678+
679+#~ msgid ""
680+#~ "You have now mastered the edge gestures and can start using the "
681+#~ "phone<br><br>Tap on the screen to start"
682+#~ msgstr ""
683+#~ "Tu es ora maestrate le gestos del bordo e pote initiar usar le "
684+#~ "telephono<br><br>colpar sur le schermo pro initiar"
po/it.po+4−4
@@ -8,15 +8,15 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-03-21 12:42+0000\n"
11+"PO-Revision-Date: 2015-06-17 09:56+0000\n"
1212"Last-Translator: Claudio Arseni <claudio.arseni@gmail.com>\n"
1313"Language-Team: Italian <it@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=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-04-23 06:48+0000\n"
19−"X-Generator: Launchpad (build 17430)\n"
18+"X-Launchpad-Export-Date: 2015-06-19 05:01+0000\n"
19+"X-Generator: Launchpad (build 17570)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -375,7 +375,7 @@ msgstr "Toccare qui per continuare."
375375
376376#: qml/Tutorial/TutorialContent.qml:81
377377msgid "Click here to finish."
378−msgstr ""
378+msgstr "Fare clic qui per terminare."
379379
380380#: qml/Tutorial/TutorialLeftFinish.qml:24
381381msgid "These are the shortcuts to favorite apps"
po/nb.po+8−8
@@ -8,15 +8,15 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-03-20 14:03+0000\n"
12−"Last-Translator: Åka Sikrom <Unknown>\n"
11+"PO-Revision-Date: 2015-06-03 02:43+0000\n"
12+"Last-Translator: Eirik Garatun <eikknud@gmail.com>\n"
1313"Language-Team: Norwegian Bokmal <nb@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=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-04-23 06:48+0000\n"
19−"X-Generator: Launchpad (build 17430)\n"
18+"X-Launchpad-Export-Date: 2015-06-05 05:58+0000\n"
19+"X-Generator: Launchpad (build 17540)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -163,17 +163,17 @@ msgstr "Søk"
163163#: qml/Dash/PageHeader.qml:269
164164msgctxt "Button: Open the Ubuntu Store"
165165msgid "Store"
166−msgstr ""
166+msgstr "Butikk"
167167
168168#: qml/Dash/PageHeader.qml:276
169169msgctxt "Button: Start a search in the current dash scope"
170170msgid "Search"
171−msgstr ""
171+msgstr "Søk"
172172
173173#: qml/Dash/PageHeader.qml:286
174174msgctxt "Button: Show the current dash scope settings"
175175msgid "Settings"
176−msgstr ""
176+msgstr "Innstillinger"
177177
178178#: qml/Dash/PageHeader.qml:293
179179msgid "Remove from Favorites"
@@ -373,7 +373,7 @@ msgstr "Trykk her for å fortsette."
373373
374374#: qml/Tutorial/TutorialContent.qml:81
375375msgid "Click here to finish."
376−msgstr ""
376+msgstr "Trykk her for å fullføre."
377377
378378#: qml/Tutorial/TutorialLeftFinish.qml:24
379379msgid "These are the shortcuts to favorite apps"
po/nl.po+4−4
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-03-28 21:07+0000\n"
12−"Last-Translator: Sergi <philipsen.sergi@gmail.com>\n"
11+"PO-Revision-Date: 2015-04-25 13:27+0000\n"
12+"Last-Translator: rob <linuxned@gmail.com>\n"
1313"Language-Team: Dutch <nl@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=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-04-23 06:48+0000\n"
18+"X-Launchpad-Export-Date: 2015-04-26 05:47+0000\n"
1919"X-Generator: Launchpad (build 17430)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
@@ -377,7 +377,7 @@ msgstr "Tik hier om door te gaan."
377377
378378#: qml/Tutorial/TutorialContent.qml:81
379379msgid "Click here to finish."
380−msgstr ""
380+msgstr "Klik hier om te stoppen."
381381
382382#: qml/Tutorial/TutorialLeftFinish.qml:24
383383msgid "These are the shortcuts to favorite apps"
po/nn.ponew file+584
@@ -0,0 +1,584 @@
1+# Norwegian Nynorsk translation for unity8
2+# Copyright (c) 2015 Rosetta Contributors and Canonical Ltd 2015
3+# This file is distributed under the same license as the unity8 package.
4+# FIRST AUTHOR <EMAIL@ADDRESS>, 2015.
5+#
6+msgid ""
7+msgstr ""
8+"Project-Id-Version: unity8\n"
9+"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10+"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11+"PO-Revision-Date: 2015-05-12 11:32+0000\n"
12+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13+"Language-Team: Norwegian Nynorsk <nn@li.org>\n"
14+"MIME-Version: 1.0\n"
15+"Content-Type: text/plain; charset=UTF-8\n"
16+"Content-Transfer-Encoding: 8bit\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18+"X-Launchpad-Export-Date: 2015-05-13 05:31+0000\n"
19+"X-Generator: Launchpad (build 17493)\n"
20+
21+#: plugins/LightDM/Greeter.cpp:112
22+msgid "Password: "
23+msgstr ""
24+
25+#: plugins/Unity/Launcher/launcheritem.cpp:45
26+#: plugins/Unity/Launcher/launcheritem.cpp:100
27+msgid "Pin shortcut"
28+msgstr ""
29+
30+#: plugins/Unity/Launcher/launcheritem.cpp:100
31+msgid "Unpin shortcut"
32+msgstr ""
33+
34+#: qml/Components/DelayedLockscreen.qml:49
35+msgid "Device Locked"
36+msgstr ""
37+
38+#: qml/Components/DelayedLockscreen.qml:64
39+msgid "You have been locked out due to too many failed passphrase attempts."
40+msgstr ""
41+
42+#: qml/Components/DelayedLockscreen.qml:65
43+msgid "You have been locked out due to too many failed passcode attempts."
44+msgstr ""
45+
46+#: qml/Components/DelayedLockscreen.qml:74
47+#, qt-format
48+msgid "Please wait %1 minute and then try again…"
49+msgid_plural "Please wait %1 minutes and then try again…"
50+msgstr[0] ""
51+msgstr[1] ""
52+
53+#: qml/Components/Dialogs.qml:69
54+msgctxt "Title: Lock/Log out dialog"
55+msgid "Log out"
56+msgstr ""
57+
58+#: qml/Components/Dialogs.qml:70
59+msgid "Are you sure you want to log out?"
60+msgstr ""
61+
62+#: qml/Components/Dialogs.qml:72
63+msgctxt "Button: Lock the system"
64+msgid "Lock"
65+msgstr ""
66+
67+#: qml/Components/Dialogs.qml:79
68+msgctxt "Button: Log out from the system"
69+msgid "Log Out"
70+msgstr ""
71+
72+#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
73+#: qml/Components/Dialogs.qml:173
74+msgid "Cancel"
75+msgstr ""
76+
77+#: qml/Components/Dialogs.qml:98
78+msgctxt "Title: Reboot/Shut down dialog"
79+msgid "Shut down"
80+msgstr ""
81+
82+#: qml/Components/Dialogs.qml:99
83+msgid "Are you sure you want to shut down?"
84+msgstr ""
85+
86+#: qml/Components/Dialogs.qml:101
87+msgctxt "Button: Reboot the system"
88+msgid "Reboot"
89+msgstr ""
90+
91+#: qml/Components/Dialogs.qml:109
92+msgctxt "Button: Shut down the system"
93+msgid "Shut down"
94+msgstr ""
95+
96+#: qml/Components/Dialogs.qml:129
97+msgctxt "Title: Reboot dialog"
98+msgid "Reboot"
99+msgstr ""
100+
101+#: qml/Components/Dialogs.qml:130
102+msgid "Are you sure you want to reboot?"
103+msgstr ""
104+
105+#: qml/Components/Dialogs.qml:132
106+msgid "No"
107+msgstr ""
108+
109+#: qml/Components/Dialogs.qml:138
110+msgid "Yes"
111+msgstr ""
112+
113+#: qml/Components/Dialogs.qml:152
114+msgctxt "Title: Power off/Restart dialog"
115+msgid "Power"
116+msgstr ""
117+
118+#: qml/Components/Dialogs.qml:153
119+msgid ""
120+"Are you sure you would like\n"
121+"to power off?"
122+msgstr ""
123+
124+#: qml/Components/Dialogs.qml:155
125+msgctxt "Button: Power off the system"
126+msgid "Power off"
127+msgstr ""
128+
129+#: qml/Components/Dialogs.qml:164
130+msgctxt "Button: Restart the system"
131+msgid "Restart"
132+msgstr ""
133+
134+#: qml/Components/Lockscreen.qml:245
135+msgid "Return to Call"
136+msgstr ""
137+
138+#: qml/Components/Lockscreen.qml:245
139+msgid "Emergency Call"
140+msgstr ""
141+
142+#: qml/Components/Lockscreen.qml:277
143+msgid "OK"
144+msgstr ""
145+
146+#: qml/Dash/GenericScopeView.qml:532 qml/Dash/GenericScopeView.qml:690
147+msgid "See less"
148+msgstr ""
149+
150+#: qml/Dash/GenericScopeView.qml:532
151+msgid "See all"
152+msgstr ""
153+
154+#: qml/Dash/GenericScopeView.qml:594
155+msgctxt "Label: Hint for dash search line edit"
156+msgid "Search"
157+msgstr ""
158+
159+#: qml/Dash/PageHeader.qml:269
160+msgctxt "Button: Open the Ubuntu Store"
161+msgid "Store"
162+msgstr ""
163+
164+#: qml/Dash/PageHeader.qml:276
165+msgctxt "Button: Start a search in the current dash scope"
166+msgid "Search"
167+msgstr ""
168+
169+#: qml/Dash/PageHeader.qml:286
170+msgctxt "Button: Show the current dash scope settings"
171+msgid "Settings"
172+msgstr ""
173+
174+#: qml/Dash/PageHeader.qml:293
175+msgid "Remove from Favorites"
176+msgstr ""
177+
178+#: qml/Dash/PageHeader.qml:293
179+msgid "Add to Favorites"
180+msgstr ""
181+
182+#: qml/Dash/Previews/PreviewActionCombo.qml:35
183+msgid "More..."
184+msgstr ""
185+
186+#: qml/Dash/Previews/PreviewActionCombo.qml:35
187+msgid "Less..."
188+msgstr ""
189+
190+#: qml/Dash/Previews/PreviewRatingInput.qml:81
191+msgid "Rate this"
192+msgstr ""
193+
194+#: qml/Dash/Previews/PreviewRatingInput.qml:126
195+msgid "Add a review"
196+msgstr ""
197+
198+#: qml/Dash/Previews/PreviewRatingInput.qml:166
199+msgid "Send"
200+msgstr ""
201+
202+#: qml/Dash/PullToRefreshScopeStyle.qml:55
203+msgid "Pull to refresh…"
204+msgstr ""
205+
206+#: qml/Dash/PullToRefreshScopeStyle.qml:60
207+msgid "Release to refresh…"
208+msgstr ""
209+
210+#: qml/Dash/ScopeSettings/ScopeSettingBoolean.qml:43
211+msgid "Enable location data"
212+msgstr ""
213+
214+#: qml/Dash/ScopesList.qml:66
215+msgid "Manage"
216+msgstr ""
217+
218+#: qml/Dash/ScopesList.qml:112
219+msgid "Home"
220+msgstr ""
221+
222+#: qml/Dash/ScopesList.qml:113
223+msgid "Also installed"
224+msgstr ""
225+
226+#: qml/Greeter/CoverPage.qml:107
227+msgid "Unlock"
228+msgstr ""
229+
230+#: qml/Greeter/LoginList.qml:236
231+msgid "Retry"
232+msgstr ""
233+
234+#: qml/Greeter/LoginList.qml:237
235+msgid "Tap to unlock"
236+msgstr ""
237+
238+#: qml/Greeter/NarrowView.qml:53 qml/Wizard/Pages/passwd-confirm.qml:53
239+msgid "Sorry, incorrect passphrase."
240+msgstr ""
241+
242+#: qml/Greeter/NarrowView.qml:54 qml/Wizard/Pages/passwd-confirm.qml:54
243+msgid "Sorry, incorrect passcode."
244+msgstr ""
245+
246+#: qml/Greeter/NarrowView.qml:55
247+msgid "This will be your last attempt."
248+msgstr ""
249+
250+#: qml/Greeter/NarrowView.qml:57
251+msgid ""
252+"If passphrase is entered incorrectly, your phone will conduct a factory "
253+"reset and all personal data will be deleted."
254+msgstr ""
255+
256+#: qml/Greeter/NarrowView.qml:58
257+msgid ""
258+"If passcode is entered incorrectly, your phone will conduct a factory reset "
259+"and all personal data will be deleted."
260+msgstr ""
261+
262+#: qml/Greeter/NarrowView.qml:115
263+#, qt-format
264+msgid "Enter %1"
265+msgstr ""
266+
267+#: qml/Greeter/NarrowView.qml:116 qml/Wizard/Pages/passwd-set.qml:60
268+msgid "Enter passphrase"
269+msgstr ""
270+
271+#: qml/Greeter/NarrowView.qml:117
272+msgid "Enter passcode"
273+msgstr ""
274+
275+#: qml/Greeter/NarrowView.qml:118
276+#, qt-format
277+msgid "Sorry, incorrect %1"
278+msgstr ""
279+
280+#: qml/Greeter/NarrowView.qml:119
281+msgid "Sorry, incorrect passphrase"
282+msgstr ""
283+
284+#: qml/Greeter/NarrowView.qml:120
285+msgid "Please re-enter"
286+msgstr ""
287+
288+#: qml/Greeter/NarrowView.qml:121
289+msgid "Sorry, incorrect passcode"
290+msgstr ""
291+
292+#: qml/Notifications/NotificationMenuItemFactory.qml:128
293+msgid "Show password"
294+msgstr ""
295+
296+#: qml/Panel/ActiveCallHint.qml:79
297+msgid "Tap to return to call..."
298+msgstr ""
299+
300+#: qml/Panel/ActiveCallHint.qml:92
301+msgid "Conference"
302+msgstr ""
303+
304+#: qml/Panel/Indicators/MenuItemFactory.qml:639
305+msgid "Nothing is playing"
306+msgstr ""
307+
308+#: qml/Panel/Indicators/MenuItemFactory.qml:787
309+msgid "In queue…"
310+msgstr ""
311+
312+#: qml/Panel/Indicators/MenuItemFactory.qml:791
313+msgid "Downloading"
314+msgstr ""
315+
316+#: qml/Panel/Indicators/MenuItemFactory.qml:793
317+msgid "Paused, tap to resume"
318+msgstr ""
319+
320+#: qml/Panel/Indicators/MenuItemFactory.qml:795
321+msgid "Canceled"
322+msgstr ""
323+
324+#: qml/Panel/Indicators/MenuItemFactory.qml:797
325+msgid "Finished"
326+msgstr ""
327+
328+#: qml/Panel/Indicators/MenuItemFactory.qml:799
329+msgid "Failed, tap to retry"
330+msgstr ""
331+
332+#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
333+msgctxt "Label: Hint in message indicator line edit"
334+msgid "Reply"
335+msgstr ""
336+
337+#: qml/Panel/Indicators/ModemInfoItem.qml:105
338+msgid "Unlock SIM"
339+msgstr ""
340+
341+#: qml/Panel/Indicators/RoamingIndication.qml:27
342+msgid "Roaming"
343+msgstr ""
344+
345+#: qml/Tutorial/TutorialBottomFinish.qml:24
346+msgid "This action does different things for different apps"
347+msgstr ""
348+
349+#: qml/Tutorial/TutorialBottomFinish.qml:25
350+msgid "Tap here to finish."
351+msgstr ""
352+
353+#: qml/Tutorial/TutorialBottom.qml:28
354+msgid "Open special menus"
355+msgstr ""
356+
357+#: qml/Tutorial/TutorialBottom.qml:29
358+msgid "Swipe up from the bottom edge."
359+msgstr ""
360+
361+#: qml/Tutorial/TutorialContent.qml:81 qml/Tutorial/TutorialLeftFinish.qml:25
362+#: qml/Tutorial/TutorialRight.qml:144
363+msgid "Tap here to continue."
364+msgstr ""
365+
366+#: qml/Tutorial/TutorialContent.qml:81
367+msgid "Click here to finish."
368+msgstr ""
369+
370+#: qml/Tutorial/TutorialLeftFinish.qml:24
371+msgid "These are the shortcuts to favorite apps"
372+msgstr ""
373+
374+#: qml/Tutorial/TutorialLeft.qml:26
375+msgid "Open the launcher"
376+msgstr ""
377+
378+#: qml/Tutorial/TutorialLeft.qml:27
379+msgid "Short swipe from the left edge."
380+msgstr ""
381+
382+#: qml/Tutorial/TutorialPage.qml:178
383+msgid "You almost got it!"
384+msgstr ""
385+
386+#: qml/Tutorial/TutorialPage.qml:195
387+msgid "Try again."
388+msgstr ""
389+
390+#: qml/Tutorial/TutorialRight.qml:31
391+msgid "To view open apps"
392+msgstr ""
393+
394+#: qml/Tutorial/TutorialRight.qml:32
395+msgid "Long swipe from the right edge."
396+msgstr ""
397+
398+#: qml/Tutorial/TutorialRight.qml:122
399+msgid "View all your running tasks."
400+msgstr ""
401+
402+#: qml/Wizard/Page.qml:89
403+msgctxt "Button: Go back one page in the Wizard"
404+msgid "Back"
405+msgstr ""
406+
407+#: qml/Wizard/Pages/10-welcome.qml:27
408+msgid "Hi!"
409+msgstr ""
410+
411+#: qml/Wizard/Pages/10-welcome.qml:44
412+msgid "Welcome to your Ubuntu phone."
413+msgstr ""
414+
415+#: qml/Wizard/Pages/10-welcome.qml:52
416+msgid "Let’s get started."
417+msgstr ""
418+
419+#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
420+#: qml/Wizard/Pages/40-wifi.qml:215 qml/Wizard/Pages/50-location.qml:131
421+#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
422+#: qml/Wizard/Pages/passwd-set.qml:90
423+msgid "Continue"
424+msgstr ""
425+
426+#: qml/Wizard/Pages/20-sim.qml:25
427+msgid "Add a SIM card and restart your device"
428+msgstr ""
429+
430+#: qml/Wizard/Pages/20-sim.qml:59
431+msgid "Without it, you won’t be able to make calls or use text messaging."
432+msgstr ""
433+
434+#: qml/Wizard/Pages/20-sim.qml:73 qml/Wizard/Pages/40-wifi.qml:215
435+msgid "Skip"
436+msgstr ""
437+
438+#: qml/Wizard/Pages/30-passwd-type.qml:39
439+msgid "Lock security"
440+msgstr ""
441+
442+#: qml/Wizard/Pages/30-passwd-type.qml:74
443+msgid "Please select how you’d like to unlock your phone."
444+msgstr ""
445+
446+#: qml/Wizard/Pages/30-passwd-type.qml:97
447+msgctxt "Label: Type of security method"
448+msgid "Swipe"
449+msgstr ""
450+
451+#: qml/Wizard/Pages/30-passwd-type.qml:98
452+msgctxt "Label: Description of security method"
453+msgid "No security"
454+msgstr ""
455+
456+#: qml/Wizard/Pages/30-passwd-type.qml:100
457+msgctxt "Label: Type of security method"
458+msgid "Passcode"
459+msgstr ""
460+
461+#: qml/Wizard/Pages/30-passwd-type.qml:101
462+msgctxt "Label: Description of security method"
463+msgid "4 digits only"
464+msgstr ""
465+
466+#: qml/Wizard/Pages/30-passwd-type.qml:103
467+msgctxt "Label: Type of security method"
468+msgid "Passphrase"
469+msgstr ""
470+
471+#: qml/Wizard/Pages/30-passwd-type.qml:104
472+msgctxt "Label: Description of security method"
473+msgid "Numbers and letters"
474+msgstr ""
475+
476+#: qml/Wizard/Pages/40-wifi.qml:29
477+msgid "Connect to Wi‑Fi"
478+msgstr ""
479+
480+#: qml/Wizard/Pages/40-wifi.qml:168
481+msgid "Available networks…"
482+msgstr ""
483+
484+#: qml/Wizard/Pages/40-wifi.qml:169
485+msgid "No available networks."
486+msgstr ""
487+
488+#: qml/Wizard/Pages/50-location.qml:27
489+msgid "Location"
490+msgstr ""
491+
492+#: qml/Wizard/Pages/50-location.qml:62
493+msgid "Let the phone detect your location:"
494+msgstr ""
495+
496+#: qml/Wizard/Pages/50-location.qml:69
497+msgid "Using GPS only (less accurate)"
498+msgstr ""
499+
500+#: qml/Wizard/Pages/50-location.qml:86
501+msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
502+msgstr ""
503+
504+#. TRANSLATORS: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
505+#: qml/Wizard/Pages/50-location.qml:103
506+msgid ""
507+"By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
508+"conditions</a>."
509+msgstr ""
510+
511+#: qml/Wizard/Pages/50-location.qml:112
512+msgid "Not at all"
513+msgstr ""
514+
515+#: qml/Wizard/Pages/50-location.qml:124
516+msgid "You can change your mind later in <b>System Settings</b>."
517+msgstr ""
518+
519+#: qml/Wizard/Pages/60-reporting.qml:24
520+msgid "Improving your experience"
521+msgstr ""
522+
523+#: qml/Wizard/Pages/60-reporting.qml:36
524+msgid ""
525+"Your phone is set up to automatically report errors to Canonical and its "
526+"partners, the makers of the operating system."
527+msgstr ""
528+
529+#: qml/Wizard/Pages/60-reporting.qml:43
530+msgid ""
531+"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
532+"Privacy</b>"
533+msgstr ""
534+
535+#: qml/Wizard/Pages/80-finished.qml:24
536+msgid "All done"
537+msgstr ""
538+
539+#: qml/Wizard/Pages/80-finished.qml:39
540+msgid "Nice work!"
541+msgstr ""
542+
543+#: qml/Wizard/Pages/80-finished.qml:46
544+msgid "Your phone is now ready to use."
545+msgstr ""
546+
547+#: qml/Wizard/Pages/80-finished.qml:53
548+msgid "Finish"
549+msgstr ""
550+
551+#: qml/Wizard/Pages/here-terms.qml:27
552+msgid "Terms & Conditions"
553+msgstr ""
554+
555+#: qml/Wizard/Pages/passwd-confirm.qml:49
556+msgid "Confirm passphrase"
557+msgstr ""
558+
559+#: qml/Wizard/Pages/passwd-confirm.qml:50
560+msgid "Confirm passcode"
561+msgstr ""
562+
563+#: qml/Wizard/Pages/passwd-confirm.qml:53
564+#: qml/Wizard/Pages/passwd-confirm.qml:54
565+msgid "Please try again."
566+msgstr ""
567+
568+#: qml/Wizard/Pages/passwd-set.qml:61
569+msgid "Choose your passcode"
570+msgstr ""
571+
572+#: qml/Wizard/Pages/passwd-set.qml:68
573+msgid "Passphrase must be 4 characters long"
574+msgstr ""
575+
576+#: qml/Wizard/StackButton.qml:39
577+#, qt-format
578+msgid "〈 %1"
579+msgstr ""
580+
581+#: qml/Wizard/StackButton.qml:42
582+#, qt-format
583+msgid "%1 〉"
584+msgstr ""
po/pa.po+3−3
@@ -8,14 +8,14 @@ msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-03-26 04:53+0000\n"
11+"PO-Revision-Date: 2015-04-24 04:29+0000\n"
1212"Last-Translator: Gursharnjit_Singh <ubuntuser13@gmail.com>\n"
1313"Language-Team: Punjabi <pa@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=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-04-23 06:48+0000\n"
18+"X-Launchpad-Export-Date: 2015-04-25 05:38+0000\n"
1919"X-Generator: Launchpad (build 17430)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
@@ -371,7 +371,7 @@ msgstr "ਜਾਰੀ ਰੱਖਣ ਲਈ ਇੱਥੇ ਛੂਹੋ।"
371371
372372#: qml/Tutorial/TutorialContent.qml:81
373373msgid "Click here to finish."
374−msgstr ""
374+msgstr "ਸਮਾਪਤੀ ਲਈ ਇੱਥੇ ਕਲਿੱਕ ਕਰੋ।"
375375
376376#: qml/Tutorial/TutorialLeftFinish.qml:24
377377msgid "These are the shortcuts to favorite apps"
po/pl.po+10−10
@@ -8,7 +8,7 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-04-04 13:00+0000\n"
11+"PO-Revision-Date: 2015-05-02 12:37+0000\n"
1212"Last-Translator: GTriderXC <gtriderxc@yahoo.com>\n"
1313"Language-Team: Polish <pl@li.org>\n"
1414"MIME-Version: 1.0\n"
@@ -16,8 +16,8 @@ msgstr ""
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
1818"|| n%100>=20) ? 1 : 2;\n"
19−"X-Launchpad-Export-Date: 2015-04-23 06:48+0000\n"
20−"X-Generator: Launchpad (build 17430)\n"
19+"X-Launchpad-Export-Date: 2015-05-03 06:07+0000\n"
20+"X-Generator: Launchpad (build 17474)\n"
2121
2222#: plugins/LightDM/Greeter.cpp:112
2323msgid "Password: "
@@ -123,7 +123,7 @@ msgid ""
123123"Are you sure you would like\n"
124124"to power off?"
125125msgstr ""
126−"Jesteś pewny że chcesz\n"
126+"Czy na pewno\n"
127127"wyłączyć?"
128128
129129#: qml/Components/Dialogs.qml:155
@@ -361,11 +361,11 @@ msgstr "Dotknij tutaj, by zakończyć."
361361
362362#: qml/Tutorial/TutorialBottom.qml:28
363363msgid "Open special menus"
364−msgstr ""
364+msgstr "Otwórz menu specjalne"
365365
366366#: qml/Tutorial/TutorialBottom.qml:29
367367msgid "Swipe up from the bottom edge."
368−msgstr ""
368+msgstr "Przeciągnij od dolnej krawędzi w górę ekranu"
369369
370370#: qml/Tutorial/TutorialContent.qml:81 qml/Tutorial/TutorialLeftFinish.qml:25
371371#: qml/Tutorial/TutorialRight.qml:144
@@ -374,7 +374,7 @@ msgstr "Dotknij tutaj, by kontynuować."
374374
375375#: qml/Tutorial/TutorialContent.qml:81
376376msgid "Click here to finish."
377−msgstr ""
377+msgstr "Kliknij tutaj, by zakończyć."
378378
379379#: qml/Tutorial/TutorialLeftFinish.qml:24
380380msgid "These are the shortcuts to favorite apps"
@@ -382,11 +382,11 @@ msgstr "To są skróty do ulubionych aplikacji"
382382
383383#: qml/Tutorial/TutorialLeft.qml:26
384384msgid "Open the launcher"
385−msgstr ""
385+msgstr "Otwórz launcher"
386386
387387#: qml/Tutorial/TutorialLeft.qml:27
388388msgid "Short swipe from the left edge."
389−msgstr ""
389+msgstr "Krótkie przeciągnięcie od lewej krawędzi."
390390
391391#: qml/Tutorial/TutorialPage.qml:178
392392msgid "You almost got it!"
@@ -402,7 +402,7 @@ msgstr "By zobaczyć otwarte aplikacje"
402402
403403#: qml/Tutorial/TutorialRight.qml:32
404404msgid "Long swipe from the right edge."
405−msgstr ""
405+msgstr "Długie przeciągnięcie od prawej krawędzi."
406406
407407#: qml/Tutorial/TutorialRight.qml:122
408408msgid "View all your running tasks."
po/pt.po+7−7
@@ -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: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-04-18 02:15+0000\n"
11+"PO-Revision-Date: 2015-05-19 03:42+0000\n"
1212"Last-Translator: António Miranda <Unknown>\n"
1313"Language-Team: Portuguese <pt@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=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-04-23 06:48+0000\n"
19−"X-Generator: Launchpad (build 17430)\n"
18+"X-Launchpad-Export-Date: 2015-05-22 05:09+0000\n"
19+"X-Generator: Launchpad (build 17514)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -262,7 +262,7 @@ msgid ""
262262"If passcode is entered incorrectly, your phone will conduct a factory reset "
263263"and all personal data will be deleted."
264264msgstr ""
265−"Se o código seja introduzido incorretamente, todos os dados pessoais do seu "
265+"Se o código não for introduzido corretamente, todos os dados pessoais do seu "
266266"telemóvel serão apagados."
267267
268268#: qml/Greeter/NarrowView.qml:115
@@ -276,7 +276,7 @@ msgstr "Indique a palavra-passe"
276276
277277#: qml/Greeter/NarrowView.qml:117
278278msgid "Enter passcode"
279−msgstr "Introduza código"
279+msgstr "Introduza o código"
280280
281281#: qml/Greeter/NarrowView.qml:118
282282#, qt-format
@@ -293,7 +293,7 @@ msgstr "Por favor, volte a introduzir"
293293
294294#: qml/Greeter/NarrowView.qml:121
295295msgid "Sorry, incorrect passcode"
296−msgstr "Código incorreta"
296+msgstr "Código incorreto"
297297
298298#: qml/Notifications/NotificationMenuItemFactory.qml:128
299299msgid "Show password"
@@ -371,7 +371,7 @@ msgstr "Toque aqui para continuar."
371371
372372#: qml/Tutorial/TutorialContent.qml:81
373373msgid "Click here to finish."
374−msgstr ""
374+msgstr "Clique aqui para finalizar."
375375
376376#: qml/Tutorial/TutorialLeftFinish.qml:24
377377msgid "These are the shortcuts to favorite apps"
po/pt_BR.po+9−9
@@ -8,15 +8,15 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-04-06 19:02+0000\n"
11+"PO-Revision-Date: 2015-05-04 21:50+0000\n"
1212"Last-Translator: Tiago Hillebrandt <tiagohillebrandt@gmail.com>\n"
1313"Language-Team: Brazilian Portuguese <pt_BR@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=2; plural=n > 1;\n"
18−"X-Launchpad-Export-Date: 2015-04-23 06:48+0000\n"
19−"X-Generator: Launchpad (build 17430)\n"
18+"X-Launchpad-Export-Date: 2015-05-05 05:47+0000\n"
19+"X-Generator: Launchpad (build 17474)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -122,8 +122,8 @@ msgid ""
122122"Are you sure you would like\n"
123123"to power off?"
124124msgstr ""
125−"Você tem certeza que gostaria \n"
126−"de desligar?"
125+"Você tem certeza que deseja\n"
126+"desligar?"
127127
128128#: qml/Components/Dialogs.qml:155
129129msgctxt "Button: Power off the system"
@@ -163,17 +163,17 @@ msgstr "Pesquisar"
163163#: qml/Dash/PageHeader.qml:269
164164msgctxt "Button: Open the Ubuntu Store"
165165msgid "Store"
166−msgstr ""
166+msgstr "Loja"
167167
168168#: qml/Dash/PageHeader.qml:276
169169msgctxt "Button: Start a search in the current dash scope"
170170msgid "Search"
171−msgstr ""
171+msgstr "Pesquisar"
172172
173173#: qml/Dash/PageHeader.qml:286
174174msgctxt "Button: Show the current dash scope settings"
175175msgid "Settings"
176−msgstr ""
176+msgstr "Configurações"
177177
178178#: qml/Dash/PageHeader.qml:293
179179msgid "Remove from Favorites"
@@ -373,7 +373,7 @@ msgstr "Toque aqui para continuar."
373373
374374#: qml/Tutorial/TutorialContent.qml:81
375375msgid "Click here to finish."
376−msgstr ""
376+msgstr "Clique aqui para finalizar."
377377
378378#: qml/Tutorial/TutorialLeftFinish.qml:24
379379msgid "These are the shortcuts to favorite apps"
po/sl.po+8−8
@@ -8,16 +8,16 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-03-23 07:24+0000\n"
12−"Last-Translator: Damir Jerovšek <Unknown>\n"
11+"PO-Revision-Date: 2015-06-05 06:39+0000\n"
12+"Last-Translator: Martin Srebotnjak <miles@filmsi.net>\n"
1313"Language-Team: Slovenian <sl@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=4; plural=(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || "
1818"n%100==4 ? 3 : 0);\n"
19−"X-Launchpad-Export-Date: 2015-04-23 06:48+0000\n"
20−"X-Generator: Launchpad (build 17430)\n"
19+"X-Launchpad-Export-Date: 2015-06-06 05:31+0000\n"
20+"X-Generator: Launchpad (build 17540)\n"
2121
2222#: plugins/LightDM/Greeter.cpp:112
2323msgid "Password: "
@@ -164,17 +164,17 @@ msgstr "Iskanje"
164164#: qml/Dash/PageHeader.qml:269
165165msgctxt "Button: Open the Ubuntu Store"
166166msgid "Store"
167−msgstr ""
167+msgstr "Trgovina"
168168
169169#: qml/Dash/PageHeader.qml:276
170170msgctxt "Button: Start a search in the current dash scope"
171171msgid "Search"
172−msgstr ""
172+msgstr "Iskanje"
173173
174174#: qml/Dash/PageHeader.qml:286
175175msgctxt "Button: Show the current dash scope settings"
176176msgid "Settings"
177−msgstr ""
177+msgstr "Nastavitve"
178178
179179#: qml/Dash/PageHeader.qml:293
180180msgid "Remove from Favorites"
@@ -374,7 +374,7 @@ msgstr "Tapnite tukaj za nadaljevanje."
374374
375375#: qml/Tutorial/TutorialContent.qml:81
376376msgid "Click here to finish."
377−msgstr ""
377+msgstr "Kliknite tu za zaključek."
378378
379379#: qml/Tutorial/TutorialLeftFinish.qml:24
380380msgid "These are the shortcuts to favorite apps"
po/ur.ponew file+584
@@ -0,0 +1,584 @@
1+# Urdu translation for unity8
2+# Copyright (c) 2015 Rosetta Contributors and Canonical Ltd 2015
3+# This file is distributed under the same license as the unity8 package.
4+# FIRST AUTHOR <EMAIL@ADDRESS>, 2015.
5+#
6+msgid ""
7+msgstr ""
8+"Project-Id-Version: unity8\n"
9+"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10+"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11+"PO-Revision-Date: 2015-05-05 03:12+0000\n"
12+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13+"Language-Team: Urdu <ur@li.org>\n"
14+"MIME-Version: 1.0\n"
15+"Content-Type: text/plain; charset=UTF-8\n"
16+"Content-Transfer-Encoding: 8bit\n"
17+"Plural-Forms: nplurals=2; plural=n != 1;\n"
18+"X-Launchpad-Export-Date: 2015-05-09 05:49+0000\n"
19+"X-Generator: Launchpad (build 17474)\n"
20+
21+#: plugins/LightDM/Greeter.cpp:112
22+msgid "Password: "
23+msgstr ""
24+
25+#: plugins/Unity/Launcher/launcheritem.cpp:45
26+#: plugins/Unity/Launcher/launcheritem.cpp:100
27+msgid "Pin shortcut"
28+msgstr ""
29+
30+#: plugins/Unity/Launcher/launcheritem.cpp:100
31+msgid "Unpin shortcut"
32+msgstr ""
33+
34+#: qml/Components/DelayedLockscreen.qml:49
35+msgid "Device Locked"
36+msgstr ""
37+
38+#: qml/Components/DelayedLockscreen.qml:64
39+msgid "You have been locked out due to too many failed passphrase attempts."
40+msgstr ""
41+
42+#: qml/Components/DelayedLockscreen.qml:65
43+msgid "You have been locked out due to too many failed passcode attempts."
44+msgstr ""
45+
46+#: qml/Components/DelayedLockscreen.qml:74
47+#, qt-format
48+msgid "Please wait %1 minute and then try again…"
49+msgid_plural "Please wait %1 minutes and then try again…"
50+msgstr[0] ""
51+msgstr[1] ""
52+
53+#: qml/Components/Dialogs.qml:69
54+msgctxt "Title: Lock/Log out dialog"
55+msgid "Log out"
56+msgstr ""
57+
58+#: qml/Components/Dialogs.qml:70
59+msgid "Are you sure you want to log out?"
60+msgstr ""
61+
62+#: qml/Components/Dialogs.qml:72
63+msgctxt "Button: Lock the system"
64+msgid "Lock"
65+msgstr ""
66+
67+#: qml/Components/Dialogs.qml:79
68+msgctxt "Button: Log out from the system"
69+msgid "Log Out"
70+msgstr ""
71+
72+#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
73+#: qml/Components/Dialogs.qml:173
74+msgid "Cancel"
75+msgstr ""
76+
77+#: qml/Components/Dialogs.qml:98
78+msgctxt "Title: Reboot/Shut down dialog"
79+msgid "Shut down"
80+msgstr ""
81+
82+#: qml/Components/Dialogs.qml:99
83+msgid "Are you sure you want to shut down?"
84+msgstr ""
85+
86+#: qml/Components/Dialogs.qml:101
87+msgctxt "Button: Reboot the system"
88+msgid "Reboot"
89+msgstr ""
90+
91+#: qml/Components/Dialogs.qml:109
92+msgctxt "Button: Shut down the system"
93+msgid "Shut down"
94+msgstr ""
95+
96+#: qml/Components/Dialogs.qml:129
97+msgctxt "Title: Reboot dialog"
98+msgid "Reboot"
99+msgstr ""
100+
101+#: qml/Components/Dialogs.qml:130
102+msgid "Are you sure you want to reboot?"
103+msgstr ""
104+
105+#: qml/Components/Dialogs.qml:132
106+msgid "No"
107+msgstr ""
108+
109+#: qml/Components/Dialogs.qml:138
110+msgid "Yes"
111+msgstr "ھاں"
112+
113+#: qml/Components/Dialogs.qml:152
114+msgctxt "Title: Power off/Restart dialog"
115+msgid "Power"
116+msgstr ""
117+
118+#: qml/Components/Dialogs.qml:153
119+msgid ""
120+"Are you sure you would like\n"
121+"to power off?"
122+msgstr ""
123+
124+#: qml/Components/Dialogs.qml:155
125+msgctxt "Button: Power off the system"
126+msgid "Power off"
127+msgstr ""
128+
129+#: qml/Components/Dialogs.qml:164
130+msgctxt "Button: Restart the system"
131+msgid "Restart"
132+msgstr ""
133+
134+#: qml/Components/Lockscreen.qml:245
135+msgid "Return to Call"
136+msgstr ""
137+
138+#: qml/Components/Lockscreen.qml:245
139+msgid "Emergency Call"
140+msgstr ""
141+
142+#: qml/Components/Lockscreen.qml:277
143+msgid "OK"
144+msgstr ""
145+
146+#: qml/Dash/GenericScopeView.qml:532 qml/Dash/GenericScopeView.qml:690
147+msgid "See less"
148+msgstr ""
149+
150+#: qml/Dash/GenericScopeView.qml:532
151+msgid "See all"
152+msgstr ""
153+
154+#: qml/Dash/GenericScopeView.qml:594
155+msgctxt "Label: Hint for dash search line edit"
156+msgid "Search"
157+msgstr ""
158+
159+#: qml/Dash/PageHeader.qml:269
160+msgctxt "Button: Open the Ubuntu Store"
161+msgid "Store"
162+msgstr ""
163+
164+#: qml/Dash/PageHeader.qml:276
165+msgctxt "Button: Start a search in the current dash scope"
166+msgid "Search"
167+msgstr ""
168+
169+#: qml/Dash/PageHeader.qml:286
170+msgctxt "Button: Show the current dash scope settings"
171+msgid "Settings"
172+msgstr ""
173+
174+#: qml/Dash/PageHeader.qml:293
175+msgid "Remove from Favorites"
176+msgstr ""
177+
178+#: qml/Dash/PageHeader.qml:293
179+msgid "Add to Favorites"
180+msgstr ""
181+
182+#: qml/Dash/Previews/PreviewActionCombo.qml:35
183+msgid "More..."
184+msgstr ""
185+
186+#: qml/Dash/Previews/PreviewActionCombo.qml:35
187+msgid "Less..."
188+msgstr ""
189+
190+#: qml/Dash/Previews/PreviewRatingInput.qml:81
191+msgid "Rate this"
192+msgstr ""
193+
194+#: qml/Dash/Previews/PreviewRatingInput.qml:126
195+msgid "Add a review"
196+msgstr ""
197+
198+#: qml/Dash/Previews/PreviewRatingInput.qml:166
199+msgid "Send"
200+msgstr ""
201+
202+#: qml/Dash/PullToRefreshScopeStyle.qml:55
203+msgid "Pull to refresh…"
204+msgstr ""
205+
206+#: qml/Dash/PullToRefreshScopeStyle.qml:60
207+msgid "Release to refresh…"
208+msgstr ""
209+
210+#: qml/Dash/ScopeSettings/ScopeSettingBoolean.qml:43
211+msgid "Enable location data"
212+msgstr ""
213+
214+#: qml/Dash/ScopesList.qml:66
215+msgid "Manage"
216+msgstr ""
217+
218+#: qml/Dash/ScopesList.qml:112
219+msgid "Home"
220+msgstr ""
221+
222+#: qml/Dash/ScopesList.qml:113
223+msgid "Also installed"
224+msgstr ""
225+
226+#: qml/Greeter/CoverPage.qml:107
227+msgid "Unlock"
228+msgstr ""
229+
230+#: qml/Greeter/LoginList.qml:236
231+msgid "Retry"
232+msgstr ""
233+
234+#: qml/Greeter/LoginList.qml:237
235+msgid "Tap to unlock"
236+msgstr ""
237+
238+#: qml/Greeter/NarrowView.qml:53 qml/Wizard/Pages/passwd-confirm.qml:53
239+msgid "Sorry, incorrect passphrase."
240+msgstr ""
241+
242+#: qml/Greeter/NarrowView.qml:54 qml/Wizard/Pages/passwd-confirm.qml:54
243+msgid "Sorry, incorrect passcode."
244+msgstr ""
245+
246+#: qml/Greeter/NarrowView.qml:55
247+msgid "This will be your last attempt."
248+msgstr ""
249+
250+#: qml/Greeter/NarrowView.qml:57
251+msgid ""
252+"If passphrase is entered incorrectly, your phone will conduct a factory "
253+"reset and all personal data will be deleted."
254+msgstr ""
255+
256+#: qml/Greeter/NarrowView.qml:58
257+msgid ""
258+"If passcode is entered incorrectly, your phone will conduct a factory reset "
259+"and all personal data will be deleted."
260+msgstr ""
261+
262+#: qml/Greeter/NarrowView.qml:115
263+#, qt-format
264+msgid "Enter %1"
265+msgstr ""
266+
267+#: qml/Greeter/NarrowView.qml:116 qml/Wizard/Pages/passwd-set.qml:60
268+msgid "Enter passphrase"
269+msgstr ""
270+
271+#: qml/Greeter/NarrowView.qml:117
272+msgid "Enter passcode"
273+msgstr ""
274+
275+#: qml/Greeter/NarrowView.qml:118
276+#, qt-format
277+msgid "Sorry, incorrect %1"
278+msgstr ""
279+
280+#: qml/Greeter/NarrowView.qml:119
281+msgid "Sorry, incorrect passphrase"
282+msgstr ""
283+
284+#: qml/Greeter/NarrowView.qml:120
285+msgid "Please re-enter"
286+msgstr ""
287+
288+#: qml/Greeter/NarrowView.qml:121
289+msgid "Sorry, incorrect passcode"
290+msgstr ""
291+
292+#: qml/Notifications/NotificationMenuItemFactory.qml:128
293+msgid "Show password"
294+msgstr ""
295+
296+#: qml/Panel/ActiveCallHint.qml:79
297+msgid "Tap to return to call..."
298+msgstr ""
299+
300+#: qml/Panel/ActiveCallHint.qml:92
301+msgid "Conference"
302+msgstr ""
303+
304+#: qml/Panel/Indicators/MenuItemFactory.qml:639
305+msgid "Nothing is playing"
306+msgstr ""
307+
308+#: qml/Panel/Indicators/MenuItemFactory.qml:787
309+msgid "In queue…"
310+msgstr ""
311+
312+#: qml/Panel/Indicators/MenuItemFactory.qml:791
313+msgid "Downloading"
314+msgstr ""
315+
316+#: qml/Panel/Indicators/MenuItemFactory.qml:793
317+msgid "Paused, tap to resume"
318+msgstr ""
319+
320+#: qml/Panel/Indicators/MenuItemFactory.qml:795
321+msgid "Canceled"
322+msgstr ""
323+
324+#: qml/Panel/Indicators/MenuItemFactory.qml:797
325+msgid "Finished"
326+msgstr ""
327+
328+#: qml/Panel/Indicators/MenuItemFactory.qml:799
329+msgid "Failed, tap to retry"
330+msgstr ""
331+
332+#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
333+msgctxt "Label: Hint in message indicator line edit"
334+msgid "Reply"
335+msgstr ""
336+
337+#: qml/Panel/Indicators/ModemInfoItem.qml:105
338+msgid "Unlock SIM"
339+msgstr ""
340+
341+#: qml/Panel/Indicators/RoamingIndication.qml:27
342+msgid "Roaming"
343+msgstr ""
344+
345+#: qml/Tutorial/TutorialBottomFinish.qml:24
346+msgid "This action does different things for different apps"
347+msgstr ""
348+
349+#: qml/Tutorial/TutorialBottomFinish.qml:25
350+msgid "Tap here to finish."
351+msgstr ""
352+
353+#: qml/Tutorial/TutorialBottom.qml:28
354+msgid "Open special menus"
355+msgstr ""
356+
357+#: qml/Tutorial/TutorialBottom.qml:29
358+msgid "Swipe up from the bottom edge."
359+msgstr ""
360+
361+#: qml/Tutorial/TutorialContent.qml:81 qml/Tutorial/TutorialLeftFinish.qml:25
362+#: qml/Tutorial/TutorialRight.qml:144
363+msgid "Tap here to continue."
364+msgstr ""
365+
366+#: qml/Tutorial/TutorialContent.qml:81
367+msgid "Click here to finish."
368+msgstr ""
369+
370+#: qml/Tutorial/TutorialLeftFinish.qml:24
371+msgid "These are the shortcuts to favorite apps"
372+msgstr ""
373+
374+#: qml/Tutorial/TutorialLeft.qml:26
375+msgid "Open the launcher"
376+msgstr ""
377+
378+#: qml/Tutorial/TutorialLeft.qml:27
379+msgid "Short swipe from the left edge."
380+msgstr ""
381+
382+#: qml/Tutorial/TutorialPage.qml:178
383+msgid "You almost got it!"
384+msgstr ""
385+
386+#: qml/Tutorial/TutorialPage.qml:195
387+msgid "Try again."
388+msgstr ""
389+
390+#: qml/Tutorial/TutorialRight.qml:31
391+msgid "To view open apps"
392+msgstr ""
393+
394+#: qml/Tutorial/TutorialRight.qml:32
395+msgid "Long swipe from the right edge."
396+msgstr ""
397+
398+#: qml/Tutorial/TutorialRight.qml:122
399+msgid "View all your running tasks."
400+msgstr ""
401+
402+#: qml/Wizard/Page.qml:89
403+msgctxt "Button: Go back one page in the Wizard"
404+msgid "Back"
405+msgstr ""
406+
407+#: qml/Wizard/Pages/10-welcome.qml:27
408+msgid "Hi!"
409+msgstr ""
410+
411+#: qml/Wizard/Pages/10-welcome.qml:44
412+msgid "Welcome to your Ubuntu phone."
413+msgstr ""
414+
415+#: qml/Wizard/Pages/10-welcome.qml:52
416+msgid "Let’s get started."
417+msgstr ""
418+
419+#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
420+#: qml/Wizard/Pages/40-wifi.qml:215 qml/Wizard/Pages/50-location.qml:131
421+#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
422+#: qml/Wizard/Pages/passwd-set.qml:90
423+msgid "Continue"
424+msgstr ""
425+
426+#: qml/Wizard/Pages/20-sim.qml:25
427+msgid "Add a SIM card and restart your device"
428+msgstr ""
429+
430+#: qml/Wizard/Pages/20-sim.qml:59
431+msgid "Without it, you won’t be able to make calls or use text messaging."
432+msgstr ""
433+
434+#: qml/Wizard/Pages/20-sim.qml:73 qml/Wizard/Pages/40-wifi.qml:215
435+msgid "Skip"
436+msgstr ""
437+
438+#: qml/Wizard/Pages/30-passwd-type.qml:39
439+msgid "Lock security"
440+msgstr ""
441+
442+#: qml/Wizard/Pages/30-passwd-type.qml:74
443+msgid "Please select how you’d like to unlock your phone."
444+msgstr ""
445+
446+#: qml/Wizard/Pages/30-passwd-type.qml:97
447+msgctxt "Label: Type of security method"
448+msgid "Swipe"
449+msgstr ""
450+
451+#: qml/Wizard/Pages/30-passwd-type.qml:98
452+msgctxt "Label: Description of security method"
453+msgid "No security"
454+msgstr ""
455+
456+#: qml/Wizard/Pages/30-passwd-type.qml:100
457+msgctxt "Label: Type of security method"
458+msgid "Passcode"
459+msgstr ""
460+
461+#: qml/Wizard/Pages/30-passwd-type.qml:101
462+msgctxt "Label: Description of security method"
463+msgid "4 digits only"
464+msgstr ""
465+
466+#: qml/Wizard/Pages/30-passwd-type.qml:103
467+msgctxt "Label: Type of security method"
468+msgid "Passphrase"
469+msgstr ""
470+
471+#: qml/Wizard/Pages/30-passwd-type.qml:104
472+msgctxt "Label: Description of security method"
473+msgid "Numbers and letters"
474+msgstr ""
475+
476+#: qml/Wizard/Pages/40-wifi.qml:29
477+msgid "Connect to Wi‑Fi"
478+msgstr ""
479+
480+#: qml/Wizard/Pages/40-wifi.qml:168
481+msgid "Available networks…"
482+msgstr ""
483+
484+#: qml/Wizard/Pages/40-wifi.qml:169
485+msgid "No available networks."
486+msgstr ""
487+
488+#: qml/Wizard/Pages/50-location.qml:27
489+msgid "Location"
490+msgstr ""
491+
492+#: qml/Wizard/Pages/50-location.qml:62
493+msgid "Let the phone detect your location:"
494+msgstr ""
495+
496+#: qml/Wizard/Pages/50-location.qml:69
497+msgid "Using GPS only (less accurate)"
498+msgstr ""
499+
500+#: qml/Wizard/Pages/50-location.qml:86
501+msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
502+msgstr ""
503+
504+#. TRANSLATORS: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
505+#: qml/Wizard/Pages/50-location.qml:103
506+msgid ""
507+"By selecting this option you agree to the Nokia HERE <a href='#'>terms and "
508+"conditions</a>."
509+msgstr ""
510+
511+#: qml/Wizard/Pages/50-location.qml:112
512+msgid "Not at all"
513+msgstr ""
514+
515+#: qml/Wizard/Pages/50-location.qml:124
516+msgid "You can change your mind later in <b>System Settings</b>."
517+msgstr ""
518+
519+#: qml/Wizard/Pages/60-reporting.qml:24
520+msgid "Improving your experience"
521+msgstr ""
522+
523+#: qml/Wizard/Pages/60-reporting.qml:36
524+msgid ""
525+"Your phone is set up to automatically report errors to Canonical and its "
526+"partners, the makers of the operating system."
527+msgstr ""
528+
529+#: qml/Wizard/Pages/60-reporting.qml:43
530+msgid ""
531+"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
532+"Privacy</b>"
533+msgstr ""
534+
535+#: qml/Wizard/Pages/80-finished.qml:24
536+msgid "All done"
537+msgstr ""
538+
539+#: qml/Wizard/Pages/80-finished.qml:39
540+msgid "Nice work!"
541+msgstr ""
542+
543+#: qml/Wizard/Pages/80-finished.qml:46
544+msgid "Your phone is now ready to use."
545+msgstr ""
546+
547+#: qml/Wizard/Pages/80-finished.qml:53
548+msgid "Finish"
549+msgstr ""
550+
551+#: qml/Wizard/Pages/here-terms.qml:27
552+msgid "Terms & Conditions"
553+msgstr ""
554+
555+#: qml/Wizard/Pages/passwd-confirm.qml:49
556+msgid "Confirm passphrase"
557+msgstr ""
558+
559+#: qml/Wizard/Pages/passwd-confirm.qml:50
560+msgid "Confirm passcode"
561+msgstr ""
562+
563+#: qml/Wizard/Pages/passwd-confirm.qml:53
564+#: qml/Wizard/Pages/passwd-confirm.qml:54
565+msgid "Please try again."
566+msgstr ""
567+
568+#: qml/Wizard/Pages/passwd-set.qml:61
569+msgid "Choose your passcode"
570+msgstr ""
571+
572+#: qml/Wizard/Pages/passwd-set.qml:68
573+msgid "Passphrase must be 4 characters long"
574+msgstr ""
575+
576+#: qml/Wizard/StackButton.qml:39
577+#, qt-format
578+msgid "〈 %1"
579+msgstr ""
580+
581+#: qml/Wizard/StackButton.qml:42
582+#, qt-format
583+msgid "%1 〉"
584+msgstr ""
po/zh_CN.po+22−20
@@ -8,15 +8,15 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2014-12-04 11:57+0000\n"
12−"Last-Translator: Mo <alimo199x@gmail.com>\n"
11+"PO-Revision-Date: 2015-04-28 20:51+0000\n"
12+"Last-Translator: Aron Xu <Unknown>\n"
1313"Language-Team: Chinese (Simplified) <zh_CN@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=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2015-04-23 06:48+0000\n"
19−"X-Generator: Launchpad (build 17430)\n"
18+"X-Launchpad-Export-Date: 2015-04-29 05:27+0000\n"
19+"X-Generator: Launchpad (build 17453)\n"
2020
2121#: plugins/LightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -53,7 +53,7 @@ msgstr[1] ""
5353#: qml/Components/Dialogs.qml:69
5454msgctxt "Title: Lock/Log out dialog"
5555msgid "Log out"
56−msgstr ""
56+msgstr "退出"
5757
5858#: qml/Components/Dialogs.qml:70
5959msgid "Are you sure you want to log out?"
@@ -62,12 +62,12 @@ msgstr ""
6262#: qml/Components/Dialogs.qml:72
6363msgctxt "Button: Lock the system"
6464msgid "Lock"
65−msgstr ""
65+msgstr "锁定"
6666
6767#: qml/Components/Dialogs.qml:79
6868msgctxt "Button: Log out from the system"
6969msgid "Log Out"
70−msgstr ""
70+msgstr "退出"
7171
7272#: qml/Components/Dialogs.qml:86 qml/Components/Dialogs.qml:117
7373#: qml/Components/Dialogs.qml:173
@@ -77,38 +77,38 @@ msgstr "取消"
7777#: qml/Components/Dialogs.qml:98
7878msgctxt "Title: Reboot/Shut down dialog"
7979msgid "Shut down"
80−msgstr ""
80+msgstr "关机"
8181
8282#: qml/Components/Dialogs.qml:99
8383msgid "Are you sure you want to shut down?"
84−msgstr ""
84+msgstr "您确定要关机?"
8585
8686#: qml/Components/Dialogs.qml:101
8787msgctxt "Button: Reboot the system"
8888msgid "Reboot"
89−msgstr ""
89+msgstr "重启"
9090
9191#: qml/Components/Dialogs.qml:109
9292msgctxt "Button: Shut down the system"
9393msgid "Shut down"
94−msgstr ""
94+msgstr "关机"
9595
9696#: qml/Components/Dialogs.qml:129
9797msgctxt "Title: Reboot dialog"
9898msgid "Reboot"
99−msgstr ""
99+msgstr "重启"
100100
101101#: qml/Components/Dialogs.qml:130
102102msgid "Are you sure you want to reboot?"
103−msgstr ""
103+msgstr "您确定要重启吗?"
104104
105105#: qml/Components/Dialogs.qml:132
106106msgid "No"
107−msgstr ""
107+msgstr "否"
108108
109109#: qml/Components/Dialogs.qml:138
110110msgid "Yes"
111−msgstr ""
111+msgstr "是"
112112
113113#: qml/Components/Dialogs.qml:152
114114msgctxt "Title: Power off/Restart dialog"
@@ -120,16 +120,18 @@ msgid ""
120120"Are you sure you would like\n"
121121"to power off?"
122122msgstr ""
123+"您确定要\n"
124+"关闭电源吗?"
123125
124126#: qml/Components/Dialogs.qml:155
125127msgctxt "Button: Power off the system"
126128msgid "Power off"
127−msgstr ""
129+msgstr "关机"
128130
129131#: qml/Components/Dialogs.qml:164
130132msgctxt "Button: Restart the system"
131133msgid "Restart"
132−msgstr ""
134+msgstr "重启"
133135
134136#: qml/Components/Lockscreen.qml:245
135137msgid "Return to Call"
@@ -154,7 +156,7 @@ msgstr "查看全部"
154156#: qml/Dash/GenericScopeView.qml:594
155157msgctxt "Label: Hint for dash search line edit"
156158msgid "Search"
157−msgstr ""
159+msgstr "搜索"
158160
159161#: qml/Dash/PageHeader.qml:269
160162msgctxt "Button: Open the Ubuntu Store"
@@ -164,12 +166,12 @@ msgstr ""
164166#: qml/Dash/PageHeader.qml:276
165167msgctxt "Button: Start a search in the current dash scope"
166168msgid "Search"
167−msgstr ""
169+msgstr "搜索"
168170
169171#: qml/Dash/PageHeader.qml:286
170172msgctxt "Button: Show the current dash scope settings"
171173msgid "Settings"
172−msgstr ""
174+msgstr "设置"
173175
174176#: qml/Dash/PageHeader.qml:293
175177msgid "Remove from Favorites"
qml/CMakeLists.txt+1
@@ -13,6 +13,7 @@ set(QML_DIRS
1313 Notifications
1414 Panel
1515 Stages
16+ Rotation
1617 Tutorial
1718 Wizard
1819 )
qml/Components/DragHandle.qml+18−33
@@ -43,16 +43,8 @@ import Ubuntu.Gestures 0.1
4343 }
4444
4545 */
46−EdgeDragArea {
46+DirectionalDragArea {
4747 id: dragArea
48− objectName: "dragHandle"
49−
50− // Disable gesture recognition by default when hinting is used as
51− // it conflicts with the hinting idea.
52− distanceThreshold: hintDisplacement > 0 ? 0 : defaultDistanceThreshold
53− maxSilenceTime: hintDisplacement > 0 ? 60*60*1000 : defaultMaxSilenceTime
54− maxDeviation: hintDisplacement > 0 ? 999999 : defaultMaxDeviation
55− compositionTime: hintDisplacement > 0 ? 0 : defaultCompositionTime
5648
5749 property bool stretch: false
5850
@@ -68,6 +60,9 @@ EdgeDragArea {
6860 }
6961
7062 property real hintDisplacement: 0
63+
64+ immediateRecognition: hintDisplacement > 0
65+
7166 property var overrideStartValue: undefined
7267 SmoothedAnimation {
7368 id: hintingAnimation
@@ -98,7 +93,6 @@ EdgeDragArea {
9893 // Private stuff
9994 QtObject {
10095 id: d
101− property var previousStatus: DirectionalDragArea.WaitingForTouch
10296 property real startValue
10397 property real minValue: {
10498 if (direction == Direction.Horizontal) {
@@ -183,7 +177,7 @@ EdgeDragArea {
183177 }
184178
185179 onDistanceChanged: {
186− if (status === DirectionalDragArea.Recognized) {
180+ if (dragging) {
187181 // don't go the whole distance in order to smooth out the movement
188182 var step = distance * 0.3;
189183
@@ -193,31 +187,22 @@ EdgeDragArea {
193187 }
194188 }
195189
196− onStatusChanged: {
197− if (status === DirectionalDragArea.WaitingForTouch) {
198− hintingAnimation.stop();
199− if (d.previousStatus === DirectionalDragArea.Recognized) {
200− d.onFinishedRecognizedGesture();
201− } else /* d.previousStatus === DirectionalDragArea.Undecided */ {
202− // Gesture was rejected.
203− d.rollbackDrag();
190+ onDraggingChanged: {
191+ if (dragging) {
192+ dragEvaluator.reset();
193+ if (overrideStartValue !== undefined) {
194+ d.startValue = overrideStartValue;
195+ } else {
196+ d.startValue = parent[d.targetProp];
204197 }
205− } else /* Undecided || Recognized */ {
206− if (d.previousStatus === DirectionalDragArea.WaitingForTouch) {
207− dragEvaluator.reset();
208− if (overrideStartValue !== undefined) {
209− d.startValue = overrideStartValue;
210− } else {
211− d.startValue = parent[d.targetProp];
212− }
213198
214− if (hintDisplacement > 0) {
215− hintingAnimation.targetValue = d.startValue;
216− hintingAnimation.start();
217− }
199+ if (hintDisplacement > 0) {
200+ hintingAnimation.targetValue = d.startValue;
201+ hintingAnimation.start();
218202 }
203+ } else {
204+ hintingAnimation.stop();
205+ d.onFinishedRecognizedGesture();
219206 }
220−
221− d.previousStatus = status;
222207 }
223208}
qml/Components/EdgeDragArea.qmldeleted−46
@@ -1,46 +0,0 @@
1−/*
2− * Copyright (C) 2013 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.0
18−import Ubuntu.Components 0.1
19−import Ubuntu.Gestures 0.1
20−
21−/*
22− A DirectionalDragArea wrapper that provides some well-chosen defaults
23− for its gesture recognition parameters.
24−*/
25−DirectionalDragArea {
26−
27− // TODO: Re-evaluate those or even the recognition heuristics itself once
28− // we have gesture cancelling/forwarding in place.
29− //
30− // The idea here is that it's better having lax rules than false negatives.
31− // False negatives are very frustrating to the user.
32− maxDeviation: defaultMaxDeviation
33− wideningAngle: defaultWideningAngle
34− distanceThreshold: defaultDistanceThreshold
35− minSpeed: defaultMinSpeed
36− maxSilenceTime: defaultMaxSilenceTime
37− compositionTime: defaultCompositionTime
38−
39− readonly property real defaultMaxDeviation: units.gu(3)
40− readonly property real defaultWideningAngle: 50
41− readonly property real defaultDistanceThreshold: units.gu(1.5)
42− // some people were getting false negatives with it enabled.
43− readonly property real defaultMinSpeed: units.gu(0)
44− readonly property int defaultMaxSilenceTime: 200
45− readonly property int defaultCompositionTime: 60
46−}
qml/Components/EdgeDragEvaluator.qml+17−14
@@ -61,18 +61,12 @@ AxisVelocityCalculator {
6161 }
6262
6363 velocity = calculate();
64+ minVelocity = __calculateMinimumVelocityForAutoCompletion();
6465
65− if (direction == Direction.Horizontal) {
66− minVelocity = __calculateMinimumVelocityForAutoCompletion(Math.abs(deltaPos));
67− return Math.abs(velocity) >= minVelocity;
66+ if (_dragDirectionIsPositive()) {
67+ return velocity >= minVelocity;
6868 } else {
69− minVelocity = __calculateMinimumVelocityForAutoCompletion(deltaPos);
70− if (Direction.isPositive(direction) && (velocity >= minVelocity)
71− || !Direction.isPositive(direction) && (velocity <= minVelocity)) {
72− return true;
73− } else {
74− return false;
75− }
69+ return velocity <= minVelocity;
7670 }
7771 }
7872
@@ -81,11 +75,20 @@ AxisVelocityCalculator {
8175 // speedThreshold in pixels per millisecond
8276 property real __speedThresholdMs: speedThreshold / 1000.0
8377
84− // Minimum velocity when a drag total distance is zero
85− property real __v0: Direction.isPositive(direction) ? __speedThresholdMs : - __speedThresholdMs
78+ function _dragDirectionIsPositive() {
79+ if (direction === Direction.Horizontal) {
80+ return (trackedPosition - __startPosition) > 0;
81+ } else {
82+ return Direction.isPositive(direction);
83+ }
84+ }
85+
86+ function __calculateMinimumVelocityForAutoCompletion() {
87+ // Minimum velocity when a drag total distance is zero
88+ var v0 = _dragDirectionIsPositive() ? __speedThresholdMs : - __speedThresholdMs;
89+ var deltaPos = trackedPosition - __startPosition;
8690
87− function __calculateMinimumVelocityForAutoCompletion(distance) {
88− return __v0 - ((__speedThresholdMs / dragThreshold) * distance);
91+ return v0 - ((__speedThresholdMs / dragThreshold) * deltaPos);
8992 }
9093
9194 function reset() {
qml/Components/ResponsiveVerticalJournal.qml+7−2
@@ -39,6 +39,8 @@ import Dash 0.1
3939 +-----+
4040*/
4141Item {
42+ id: root
43+
4244 property real minimumColumnSpacing: units.gu(1)
4345
4446 property alias columnWidth: verticalJournalView.columnWidth
@@ -46,8 +48,8 @@ Item {
4648 property alias model: verticalJournalView.model
4749 property alias delegate: verticalJournalView.delegate
4850 property alias cacheBuffer: verticalJournalView.cacheBuffer
49− property alias displayMarginBeginning: verticalJournalView.displayMarginBeginning
50− property alias displayMarginEnd: verticalJournalView.displayMarginEnd
51+ property real displayMarginBeginning: 0
52+ property real displayMarginEnd: 0
5153
5254 implicitHeight: verticalJournalView.implicitHeight + rowSpacing
5355 clip: height < implicitHeight
@@ -63,6 +65,9 @@ Item {
6365 bottomMargin: rowSpacing / 2
6466 }
6567
68+ displayMarginBeginning: -Math.max(-root.displayMarginBeginning - anchors.topMargin, 0)
69+ displayMarginEnd:-Math.max(-root.displayMarginEnd - anchors.topMargin, 0)
70+
6671 function px2gu(pixels) {
6772 return Math.floor(pixels / units.gu(1))
6873 }
qml/Components/WindowScreenshot.qmlnew file+35
@@ -0,0 +1,35 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.2
18+
19+Item {
20+ id: root
21+
22+ function take() {
23+ var timeNow = new Date().getTime();
24+ image.source = "image://window/" + timeNow;
25+ }
26+
27+ // Unload the image to free up memory
28+ function discard() {
29+ image.source = "";
30+ }
31+
32+ Image {
33+ id: image
34+ }
35+}
qml/Dash/CardCarousel.qml−1
@@ -61,7 +61,6 @@ DashRenderer {
6161 item.fixedHeaderHeight = Qt.binding(function() { return carousel.headerHeight; });
6262 item.height = Qt.binding(function() { return cardTool.cardHeight; });
6363 item.cardData = Qt.binding(function() { return model; });
64− item.template = Qt.binding(function() { return cardTool.template; });
6564 item.components = Qt.binding(function() { return cardTool.components; });
6665 item.fontScale = Qt.binding(function() { return carousel.fontScale; });
6766 item.showHeader = Qt.binding(function() { return loader.explicitlyScaled; });
qml/Dash/CardGrid.qml−1
@@ -66,7 +66,6 @@ DashRenderer {
6666 item.fixedHeaderHeight = Qt.binding(function() { return cardTool.headerHeight; });
6767 item.fixedArtShapeSize = Qt.binding(function() { return cardTool.artShapeSize; });
6868 item.cardData = Qt.binding(function() { return model; });
69− item.template = Qt.binding(function() { return cardTool.template; });
7069 item.components = Qt.binding(function() { return cardTool.components; });
7170 item.titleAlignment = Qt.binding(function() { return cardTool.titleAlignment; });
7271 item.scopeStyle = root.scopeStyle;
qml/Dash/CardHorizontalList.qml−1
@@ -53,7 +53,6 @@ DashRenderer {
5353 item.fixedArtShapeSize = Qt.binding(function() { return cardTool.artShapeSize; });
5454 item.fixedHeaderHeight = Qt.binding(function() { return cardTool.headerHeight; });
5555 item.cardData = Qt.binding(function() { return model; });
56− item.template = Qt.binding(function() { return cardTool.template; });
5756 item.components = Qt.binding(function() { return cardTool.components; });
5857 item.titleAlignment = Qt.binding(function() { return cardTool.titleAlignment; });
5958 item.scopeStyle = root.scopeStyle;
qml/Dash/CardTool.qml+16−15
@@ -198,8 +198,8 @@ Item {
198198
199199 Loader {
200200 id: cardLoader
201− property var fields: ["art", "mascot", "title", "subtitle", "summary", "attributes"]
202− property var maxData: {
201+ readonly property var fields: ["art", "mascot", "title", "subtitle", "summary", "attributes"]
202+ readonly property var maxData: {
203203 "art": Qt.resolvedUrl("graphics/pixel.png"),
204204 "mascot": Qt.resolvedUrl("graphics/pixel.png"),
205205 "title": "—\n—",
@@ -211,26 +211,27 @@ Item {
211211 onLoaded: {
212212 item.objectName = "cardToolCard";
213213 item.asynchronous = false;
214− item.template = Qt.binding(function() { return cardTool.template; });
215214 item.components = Qt.binding(function() { return cardTool.components; });
216215 item.width = Qt.binding(function() { return cardTool.cardWidth || item.implicitWidth; });
217216 item.height = Qt.binding(function() { return cardTool.cardHeight || item.implicitHeight; });
218217 }
219218 Connections {
220− target: cardLoader.item
221− onComponentsChanged: {
222− var data = {};
223− for (var k in cardLoader.fields) {
224− var component = cardLoader.item.components[cardLoader.fields[k]];
225− var key = cardLoader.fields[k];
226− if ((typeof component === "string" && component.length > 0) ||
227− (typeof component === "object" && component !== null
228− && typeof component["field"] === "string" && component["field"].length > 0)) {
229− data[key] = cardLoader.maxData[key];
230− }
219+ target: cardTool
220+ onTemplateChanged: cardLoader.updateCardData();
221+ onComponentsChanged: cardLoader.updateCardData();
222+ }
223+ function updateCardData() {
224+ var data = {};
225+ for (var k in fields) {
226+ var component = cardTool.components[fields[k]];
227+ var key = fields[k];
228+ if ((typeof component === "string" && component.length > 0) ||
229+ (typeof component === "object" && component !== null
230+ && typeof component["field"] === "string" && component["field"].length > 0)) {
231+ data[key] = maxData[key];
231232 }
232− cardLoader.item.cardData = data;
233233 }
234+ item.cardData = data;
234235 }
235236 }
236237}
qml/Dash/CardVerticalJournal.qml−1
@@ -72,7 +72,6 @@ DashRenderer {
7272 item.fixedArtShapeSize = Qt.binding(function() { return cardTool.artShapeSize; });
7373 item.fixedHeaderHeight = Qt.binding(function() { return cardTool.headerHeight; });
7474 item.cardData = Qt.binding(function() { return model; });
75− item.template = Qt.binding(function() { return cardTool.template; });
7675 item.components = Qt.binding(function() { return cardTool.components; });
7776 item.titleAlignment = Qt.binding(function() { return cardTool.titleAlignment; });
7877 item.scopeStyle = root.scopeStyle;
qml/Dash/Dash.qml+10−12
@@ -153,7 +153,7 @@ Showable {
153153 // (as expected) but would also cause the dash content flickable to move a bit, because
154154 // that flickable was getting the touch events while overviewDragHandle was still undecided
155155 // about whether that touch was indeed performing a directional drag gesture.
156− forceNonInteractive: overviewDragHandle.status != DirectionalDragArea.WaitingForTouch
156+ forceNonInteractive: overviewDragHandle.dragging
157157
158158 enabled: bottomEdgeController.progress == 0
159159 }
@@ -187,6 +187,11 @@ Showable {
187187 onRequestFavoriteMoveTo: {
188188 scopes.moveFavoriteTo(scopeId, index);
189189 }
190+ onRequestRestore: {
191+ bottomEdgeController.enableAnimation = true;
192+ bottomEdgeController.progress = 0;
193+ dash.setCurrentScope(scopeId, false, false);
194+ }
190195
191196 Binding {
192197 target: scopesList.scope
@@ -330,7 +335,7 @@ Showable {
330335 }
331336 }
332337
333− EdgeDragArea {
338+ DirectionalDragArea {
334339 id: overviewDragHandle
335340 objectName: "overviewDragHandle"
336341 z: 1
@@ -346,21 +351,14 @@ Showable {
346351 height: units.gu(2)
347352
348353 onSceneDistanceChanged: {
349− if (status == DirectionalDragArea.Recognized) {
354+ if (dragging) {
350355 bottomEdgeController.enableAnimation = false;
351356 bottomEdgeController.progress = Math.max(0, Math.min(1, sceneDistance / fullMovement));
352357 }
353358 }
354359
355− property int previousStatus: -1
356− property int currentStatus: DirectionalDragArea.WaitingForTouch
357−
358− onStatusChanged: {
359− previousStatus = currentStatus;
360− currentStatus = status;
361−
362− if (status == DirectionalDragArea.WaitingForTouch &&
363− previousStatus == DirectionalDragArea.Recognized) {
360+ onDraggingChanged: {
361+ if (!dragging) {
364362 bottomEdgeController.enableAnimation = true;
365363 bottomEdgeController.progress = (bottomEdgeController.progress > 0.2) ? 1 : 0;
366364 }
qml/Dash/DashApplication.qml+3−3
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014,2015 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
@@ -19,8 +19,8 @@ import Ubuntu.Components 1.0
1919import Ubuntu.Thumbnailer 0.1 // Register support for image://thumbnailer/ and image://albumart/
2020
2121MainView {
22− width: applicationArguments.hasGeometry() ? applicationArguments.width() : units.gu(40)
23− height: applicationArguments.hasGeometry() ? applicationArguments.height() : units.gu(68)
22+ implicitWidth: units.gu(40)
23+ implicitHeight: units.gu(68)
2424
2525 useDeprecatedToolbar: false
2626
qml/Dash/GenericScopeView.qml+7−7
@@ -67,8 +67,8 @@ FocusScope {
6767 subPageLoader.closeSubPage()
6868 }
6969
70− function itemClicked(index, result, item, itemModel, resultsModel, limitedCategoryItemCount) {
71− if (itemModel.uri.indexOf("scope://") === 0 || scope.id === "clickscope") {
70+ function itemClicked(index, result, item, itemModel, resultsModel, limitedCategoryItemCount, categoryId) {
71+ if (itemModel.uri.indexOf("scope://") === 0 || scope.id === "clickscope" || (scope.id === "videoaggregator" && categoryId === "myvideos-getstarted")) {
7272 // TODO Technically it is possible that calling activate() will make the scope emit
7373 // previewRequested so that we show a preview but there's no scope that does that yet
7474 // so it's not implemented
@@ -80,8 +80,8 @@ FocusScope {
8080 }
8181 }
8282
83− function itemPressedAndHeld(index, result, itemModel, resultsModel, limitedCategoryItemCount) {
84− if (itemModel.uri.indexOf("scope://") !== 0) {
83+ function itemPressedAndHeld(index, result, itemModel, resultsModel, limitedCategoryItemCount, categoryId) {
84+ if (itemModel.uri.indexOf("scope://") !== 0 && !(scope.id === "videoaggregator" && categoryId === "myvideos-getstarted")) {
8585 if (scope.preview(result)) {
8686 openPreview(index, resultsModel, limitedCategoryItemCount);
8787 }
@@ -346,11 +346,11 @@ FocusScope {
346346 Connections {
347347 target: rendererLoader.item
348348 onClicked: {
349− scopeView.itemClicked(index, result, item, itemModel, target.model, categoryItemCount());
349+ scopeView.itemClicked(index, result, item, itemModel, target.model, categoryItemCount(), baseItem.category);
350350 }
351351
352352 onPressAndHold: {
353− scopeView.itemPressedAndHeld(index, result, itemModel, target.model, categoryItemCount());
353+ scopeView.itemPressedAndHeld(index, result, itemModel, target.model, categoryItemCount(), baseItem.category);
354354 }
355355
356356 function categoryItemCount() {
@@ -425,7 +425,7 @@ FocusScope {
425425 // to the next, we set the visible range to the viewport so
426426 // items are not culled (invisible) but still use no cacheBuffer
427427 // (it will be set once the scope is the current one)
428− var displayMarginBeginning = baseItem.y;
428+ var displayMarginBeginning = baseItem.y + rendererLoader.anchors.topMargin;
429429 displayMarginBeginning = -Math.max(-displayMarginBeginning, 0);
430430 displayMarginBeginning = -Math.min(-displayMarginBeginning, baseItem.height);
431431 displayMarginBeginning = Math.round(displayMarginBeginning);
qml/Dash/Previews/PreviewRatingDisplay.qml+9−44
@@ -49,53 +49,18 @@ PreviewWidget {
4949 objectName: "reviewsRepeater"
5050 model: root.widgetData["reviews"]
5151
52− delegate: Column {
53− id: reviewItem
52+ delegate: PreviewRatingSingleDisplay {
5453 objectName: "reviewItem" + index
55− anchors { left: parent.left; right: parent.right;}
56− spacing: units.gu(1)
5754
58− Rating {
59− id: rating
60− objectName: "rating"
61− size: 5
62− value: modelData["rating"] || -1
63− visible: value >= 0
64− interactive: false
55+ anchors { left: parent.left; right: parent.right; }
6556
66− property var urlIconEmpty: widgetData["rating-icon-empty"]
67− property var urlIconFull: widgetData["rating-icon-full"]
68− property var urlIconHalf: widgetData["rating-icon-half"]
69− }
70−
71− Label {
72− id: authorLabel
73− objectName: "authorLabel"
74− anchors { left: parent.left; right: parent.right }
75− color: scopeStyle ? scopeStyle.foreground : Theme.palette.normal.baseText
76− opacity: .8
77− text: modelData["author"] || ""
78− visible: text !== ""
79− wrapMode: Text.Wrap
80− }
81−
82− Label {
83− id: reviewLabel
84− objectName: "reviewLabel"
85− anchors { left: parent.left; right: parent.right }
86− color: scopeStyle ? scopeStyle.foreground : Theme.palette.normal.baseText
87− opacity: .8
88− text: modelData["review"] || ""
89− visible: text !== ""
90− wrapMode: Text.Wrap
91− }
92−
93− Item {
94− id: spacing
95− anchors { left: parent.left; right: parent.right }
96− height: units.gu(2)
97− visible: rating.visible || authorLabel.visible || reviewLabel.visible
98− }
57+ rating: modelData["rating"] || -1
58+ author: modelData["author"] || ""
59+ review: modelData["review"] || ""
60+ urlIconEmpty: widgetData["rating-icon-empty"]
61+ urlIconFull: widgetData["rating-icon-full"]
62+ urlIconHalf: widgetData["rating-icon-half"]
63+ labelColor: scopeStyle ? scopeStyle.foreground : Theme.palette.normal.baseText
9964 }
10065 }
10166 }
qml/Dash/Previews/PreviewRatingEdit.qmlnew file+80
@@ -0,0 +1,80 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.4
18+import Ubuntu.Components 0.1
19+import "../../Components"
20+
21+/*! \brief Preview widget for editable rating.
22+
23+ Shows a single display rating that can be switched by the user to edit mode
24+ and then behaves as a rating display widget
25+
26+ The display part uses widgetData["author"], widgetData["review"] and widgetData["rating"].
27+
28+ The edit part uses the same fields as the "rating-input" widget
29+*/
30+
31+PreviewWidget {
32+ id: root
33+ implicitHeight: display.visible ? display.implicitHeight : input.implicitHeight
34+
35+ Button {
36+ id: editIcon
37+ objectName: "editButton"
38+
39+ iconName: "edit"
40+ width: height
41+ anchors.right: parent.right
42+ visible: display.visible
43+ onClicked: display.visible = false
44+ }
45+
46+ PreviewRatingSingleDisplay {
47+ id: display
48+ objectName: "display"
49+
50+ anchors.left: parent.left
51+ anchors.right: editIcon.left
52+
53+ rating: widgetData["rating"] || -1
54+ author: widgetData["author"] || ""
55+ review: widgetData["review"] || ""
56+ urlIconEmpty: widgetData["rating-icon-empty"]
57+ urlIconFull: widgetData["rating-icon-full"]
58+ urlIconHalf: widgetData["rating-icon-half"]
59+ labelColor: scopeStyle ? scopeStyle.foreground : Theme.palette.normal.baseText
60+ }
61+
62+ PreviewRatingInput {
63+ id: input
64+ objectName: "input"
65+
66+ visible: !display.visible
67+ width: parent.width
68+
69+ widgetId: root.widgetId
70+ widgetData: root.widgetData
71+ isCurrentPreview: root.isCurrentPreview
72+ scopeStyle: root.scopeStyle
73+
74+ ratingValue: widgetData["rating"]
75+ reviewText: widgetData["review"]
76+
77+ onTriggered: root.triggered(widgetId, actionId, data);
78+ }
79+
80+}
qml/Dash/Previews/PreviewRatingInput.qml+3
@@ -48,6 +48,9 @@ PreviewWidget {
4848 }
4949 }
5050
51+ property alias ratingValue: rating.value
52+ property alias reviewText: reviewTextArea.text
53+
5154 function submit() {
5255 // checks rating-input requirements
5356 if (((widgetData["required"] === "both" ||
qml/Dash/Previews/PreviewRatingSingleDisplay.qmlnew file+70
@@ -0,0 +1,70 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.4
18+import Ubuntu.Components 0.1
19+import "../../Components"
20+
21+Column {
22+ id: reviewItem
23+ spacing: units.gu(1)
24+
25+ property alias rating: ratingItem.value
26+ property alias author: authorLabel.text
27+ property alias review: reviewLabel.text
28+ property alias urlIconEmpty: ratingItem.urlIconEmpty
29+ property alias urlIconFull: ratingItem.urlIconFull
30+ property alias urlIconHalf: ratingItem.urlIconHalf
31+ property alias labelColor: authorLabel.color
32+
33+ Rating {
34+ id: ratingItem
35+ objectName: "rating"
36+ size: 5
37+ visible: value >= 0
38+ interactive: false
39+
40+ property var urlIconEmpty
41+ property var urlIconFull
42+ property var urlIconHalf
43+ }
44+
45+ Label {
46+ id: authorLabel
47+ objectName: "authorLabel"
48+ anchors { left: parent.left; right: parent.right }
49+ opacity: .8
50+ visible: text !== ""
51+ wrapMode: Text.Wrap
52+ }
53+
54+ Label {
55+ id: reviewLabel
56+ objectName: "reviewLabel"
57+ anchors { left: parent.left; right: parent.right }
58+ color: authorLabel.color
59+ opacity: .8
60+ visible: text !== ""
61+ wrapMode: Text.Wrap
62+ }
63+
64+ Item {
65+ id: spacing
66+ anchors { left: parent.left; right: parent.right }
67+ height: units.gu(2)
68+ visible: rating.visible || authorLabel.visible || reviewLabel.visible
69+ }
70+}
qml/Dash/Previews/PreviewWidgetFactory.qml+1
@@ -57,6 +57,7 @@ Loader {
5757 case "progress": return "PreviewProgress.qml";
5858 case "payments": return "PreviewPayments.qml";
5959 case "rating-input": return "PreviewRatingInput.qml";
60+ case "rating-edit": return "PreviewRatingEdit.qml";
6061 case "reviews": return "PreviewRatingDisplay.qml";
6162 case "table": return "PreviewTable.qml";
6263 case "text": return "PreviewTextSummary.qml";
qml/Dash/ScopesList.qml+2
@@ -31,6 +31,7 @@ Item {
3131 signal storeClicked()
3232 signal requestFavorite(string scopeId, bool favorite)
3333 signal requestFavoriteMoveTo(string scopeId, int index)
34+ signal requestRestore(string scopeId)
3435
3536 state: "browse"
3637
@@ -124,6 +125,7 @@ Item {
124125 onRequestEditMode: root.state = "edit";
125126 onRequestScopeMoveTo: root.requestFavoriteMoveTo(scopeId, index);
126127 onRequestActivate: root.scope.activate(result);
128+ onRequestRestore: root.requestRestore(scopeId);
127129 }
128130 }
129131 }
qml/Dash/ScopesListCategory.qml+5−1
@@ -35,6 +35,7 @@ Item {
3535 signal requestEditMode()
3636 signal requestScopeMoveTo(string scopeId, int index)
3737 signal requestActivate(var result)
38+ signal requestRestore(string scopeId)
3839
3940 implicitHeight: visible ? childrenRect.height : 0
4041
@@ -93,7 +94,10 @@ Item {
9394
9495 onClicked: {
9596 if (!editMode) {
96− root.requestActivate(result);
97+ if (root.isFavoritesFeed)
98+ root.requestRestore(model.scopeId);
99+ else
100+ root.requestActivate(result);
97101 }
98102 }
99103 onPressAndHold: {
qml/Dash/graphics/phone/screenshots/gmail-webapp.svgnew file+343
@@ -0,0 +1,343 @@
1+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2+<!-- Created with Inkscape (http://www.inkscape.org/) -->
3+
4+<svg
5+ xmlns:dc="http://purl.org/dc/elements/1.1/"
6+ xmlns:cc="http://creativecommons.org/ns#"
7+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
8+ xmlns:svg="http://www.w3.org/2000/svg"
9+ xmlns="http://www.w3.org/2000/svg"
10+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
11+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
12+ width="768"
13+ height="1280"
14+ id="svg2"
15+ version="1.1"
16+ inkscape:version="0.48.5 r10040"
17+ sodipodi:docname="gmail-webapp.svg">
18+ <defs
19+ id="defs4" />
20+ <sodipodi:namedview
21+ id="base"
22+ pagecolor="#ffffff"
23+ bordercolor="#666666"
24+ borderopacity="1.0"
25+ inkscape:pageopacity="0.0"
26+ inkscape:pageshadow="2"
27+ inkscape:zoom="0.49497475"
28+ inkscape:cx="117.33439"
29+ inkscape:cy="668.80479"
30+ inkscape:document-units="px"
31+ inkscape:current-layer="layer1"
32+ showgrid="false"
33+ inkscape:window-width="1920"
34+ inkscape:window-height="1056"
35+ inkscape:window-x="0"
36+ inkscape:window-y="24"
37+ inkscape:window-maximized="1" />
38+ <metadata
39+ id="metadata7">
40+ <rdf:RDF>
41+ <cc:Work
42+ rdf:about="">
43+ <dc:format>image/svg+xml</dc:format>
44+ <dc:type
45+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
46+ <dc:title></dc:title>
47+ </cc:Work>
48+ </rdf:RDF>
49+ </metadata>
50+ <g
51+ inkscape:label="Layer 1"
52+ inkscape:groupmode="layer"
53+ id="layer1"
54+ transform="translate(0,227.63782)">
55+ <rect
56+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.69999999000000002;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
57+ id="rect2985"
58+ width="769.73627"
59+ height="1276.8328"
60+ x="-2.0203052"
61+ y="3.1671834"
62+ transform="translate(0,-227.63782)" />
63+ <rect
64+ style="fill:#e6e6e6;fill-opacity:1;stroke:none;stroke-width:4;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
65+ id="rect3797"
66+ width="773.77686"
67+ height="129.29953"
68+ x="-6.0609155"
69+ y="-0.87342685"
70+ transform="translate(0,-227.63782)" />
71+ <text
72+ xml:space="preserve"
73+ style="font-size:72px;font-style:normal;font-weight:500;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu Medium;font-stretch:normal;font-variant:normal"
74+ x="20.203053"
75+ y="-139.61781"
76+ id="text3755"
77+ sodipodi:linespacing="125%"><tspan
78+ sodipodi:role="line"
79+ id="tspan3757"
80+ x="20.203053"
81+ y="-139.61781">GMail</tspan></text>
82+ <text
83+ xml:space="preserve"
84+ style="font-size:72px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
85+ x="14.142137"
86+ y="-28.501045"
87+ id="text3759"
88+ sodipodi:linespacing="125%"><tspan
89+ sodipodi:role="line"
90+ id="tspan3761"
91+ x="14.142137"
92+ y="-28.501045">Inbox</tspan></text>
93+ <path
94+ sodipodi:type="arc"
95+ style="fill:#8effff;fill-opacity:1;stroke:#000000;stroke-width:0.69999999;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
96+ id="path3765"
97+ sodipodi:cx="107.07617"
98+ sodipodi:cy="337.52768"
99+ sodipodi:rx="64.649765"
100+ sodipodi:ry="61.619305"
101+ d="m 171.72594,337.52768 a 64.649765,61.619305 0 1 1 -129.299533,0 64.649765,61.619305 0 1 1 129.299533,0 z"
102+ transform="translate(-28.284271,-251.88148)" />
103+ <path
104+ style="fill:none;stroke:#000000;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
105+ d="m 14.142136,237.52257 729.330134,0"
106+ id="path3769"
107+ inkscape:connector-curvature="0"
108+ transform="translate(0,-227.63782)" />
109+ <text
110+ xml:space="preserve"
111+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
112+ x="167.68533"
113+ y="78.575127"
114+ id="text3771"
115+ sodipodi:linespacing="125%"><tspan
116+ sodipodi:role="line"
117+ id="tspan3773"
118+ x="167.68533"
119+ y="78.575127">Lorem ipsum</tspan></text>
120+ <text
121+ xml:space="preserve"
122+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
123+ x="167.68532"
124+ y="131.10306"
125+ id="text3775"
126+ sodipodi:linespacing="125%"><tspan
127+ sodipodi:role="line"
128+ id="tspan3777"
129+ x="167.68532"
130+ y="131.10306"
131+ style="font-size:48px;fill:#b3b3b3">bla bla bla bla bla bla bla ...</tspan></text>
132+ <path
133+ style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
134+ d="m 13.13199,165.44825 729.33013,0"
135+ id="path3769-2"
136+ inkscape:connector-curvature="0" />
137+ <path
138+ sodipodi:type="arc"
139+ style="fill:#8eff58;fill-opacity:1;stroke:#000000;stroke-width:0.69999999000000002;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
140+ id="path3765-3"
141+ sodipodi:cx="107.07617"
142+ sodipodi:cy="337.52768"
143+ sodipodi:rx="64.649765"
144+ sodipodi:ry="61.619305"
145+ d="m 171.72594,337.52768 a 64.649765,61.619305 0 1 1 -129.299533,0 64.649765,61.619305 0 1 1 129.299533,0 z"
146+ transform="translate(-26.263969,-93.102383)" />
147+ <text
148+ xml:space="preserve"
149+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
150+ x="169.70564"
151+ y="237.35422"
152+ id="text3771-9"
153+ sodipodi:linespacing="125%"><tspan
154+ sodipodi:role="line"
155+ id="tspan3773-7"
156+ x="169.70564"
157+ y="237.35422">Lorem ipsum</tspan></text>
158+ <text
159+ xml:space="preserve"
160+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
161+ x="169.70561"
162+ y="289.88217"
163+ id="text3775-1"
164+ sodipodi:linespacing="125%"><tspan
165+ sodipodi:role="line"
166+ id="tspan3777-1"
167+ x="169.70561"
168+ y="289.88217"
169+ style="font-size:48px;fill:#b3b3b3">bla bla bla bla bla bla bla ...</tspan></text>
170+ <path
171+ style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
172+ d="m 15.152292,324.22735 729.330128,0"
173+ id="path3769-2-6"
174+ inkscape:connector-curvature="0" />
175+ <path
176+ sodipodi:type="arc"
177+ style="fill:#8effff;fill-opacity:1;stroke:#000000;stroke-width:0.69999999;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
178+ id="path3765-7"
179+ sodipodi:cx="107.07617"
180+ sodipodi:cy="337.52768"
181+ sodipodi:rx="64.649765"
182+ sodipodi:ry="61.619305"
183+ d="m 171.72594,337.52768 a 64.649765,61.619305 0 1 1 -129.299533,0 64.649765,61.619305 0 1 1 129.299533,0 z"
184+ transform="translate(-28.284274,64.481414)" />
185+ <text
186+ xml:space="preserve"
187+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
188+ x="167.68533"
189+ y="394.93799"
190+ id="text3771-92"
191+ sodipodi:linespacing="125%"><tspan
192+ sodipodi:role="line"
193+ id="tspan3773-0"
194+ x="167.68533"
195+ y="394.93799">Lorem ipsum</tspan></text>
196+ <text
197+ xml:space="preserve"
198+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
199+ x="167.68532"
200+ y="447.46594"
201+ id="text3775-9"
202+ sodipodi:linespacing="125%"><tspan
203+ sodipodi:role="line"
204+ id="tspan3777-8"
205+ x="167.68532"
206+ y="447.46594"
207+ style="font-size:48px;fill:#b3b3b3">bla bla bla bla bla bla bla ...</tspan></text>
208+ <path
209+ style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
210+ d="m 13.131986,481.81114 729.330124,0"
211+ id="path3769-2-3"
212+ inkscape:connector-curvature="0" />
213+ <path
214+ sodipodi:type="arc"
215+ style="fill:#8ea2ff;fill-opacity:1;stroke:#000000;stroke-width:0.69999999000000002;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
216+ id="path3765-2"
217+ sodipodi:cx="107.07617"
218+ sodipodi:cy="337.52768"
219+ sodipodi:rx="64.649765"
220+ sodipodi:ry="61.619305"
221+ d="m 171.72594,337.52768 a 64.649765,61.619305 0 1 1 -129.299533,0 64.649765,61.619305 0 1 1 129.299533,0 z"
222+ transform="translate(-26.263969,226.10582)" />
223+ <text
224+ xml:space="preserve"
225+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
226+ x="169.70564"
227+ y="556.56238"
228+ id="text3771-1"
229+ sodipodi:linespacing="125%"><tspan
230+ sodipodi:role="line"
231+ id="tspan3773-1"
232+ x="169.70564"
233+ y="556.56238">Lorem ipsum</tspan></text>
234+ <text
235+ xml:space="preserve"
236+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
237+ x="169.70563"
238+ y="609.09033"
239+ id="text3775-8"
240+ sodipodi:linespacing="125%"><tspan
241+ sodipodi:role="line"
242+ id="tspan3777-85"
243+ x="169.70563"
244+ y="609.09033"
245+ style="font-size:48px;fill:#b3b3b3">bla bla bla bla bla bla bla ...</tspan></text>
246+ <path
247+ style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
248+ d="m 15.152292,643.43555 729.330128,0"
249+ id="path3769-2-1"
250+ inkscape:connector-curvature="0" />
251+ <path
252+ sodipodi:type="arc"
253+ style="fill:#8effff;fill-opacity:1;stroke:#000000;stroke-width:0.69999999;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
254+ id="path3765-8"
255+ sodipodi:cx="107.07617"
256+ sodipodi:cy="337.52768"
257+ sodipodi:rx="64.649765"
258+ sodipodi:ry="61.619305"
259+ d="m 171.72594,337.52768 a 64.649765,61.619305 0 1 1 -129.299533,0 64.649765,61.619305 0 1 1 129.299533,0 z"
260+ transform="translate(-24.243664,391.77084)" />
261+ <text
262+ xml:space="preserve"
263+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
264+ x="171.72594"
265+ y="722.22742"
266+ id="text3771-7"
267+ sodipodi:linespacing="125%"><tspan
268+ sodipodi:role="line"
269+ id="tspan3773-6"
270+ x="171.72594"
271+ y="722.22742">Lorem ipsum</tspan></text>
272+ <text
273+ xml:space="preserve"
274+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
275+ x="171.72592"
276+ y="774.75537"
277+ id="text3775-5"
278+ sodipodi:linespacing="125%"><tspan
279+ sodipodi:role="line"
280+ id="tspan3777-2"
281+ x="171.72592"
282+ y="774.75537"
283+ style="font-size:48px;fill:#b3b3b3">bla bla bla bla bla bla bla ...</tspan></text>
284+ <path
285+ style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
286+ d="m 17.172597,809.10057 729.330133,0"
287+ id="path3769-2-13"
288+ inkscape:connector-curvature="0" />
289+ <path
290+ sodipodi:type="arc"
291+ style="fill:#e44738;fill-opacity:1;stroke:#000000;stroke-width:0.69999999000000002;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
292+ id="path3765-9"
293+ sodipodi:cx="107.07617"
294+ sodipodi:cy="337.52768"
295+ sodipodi:rx="64.649765"
296+ sodipodi:ry="61.619305"
297+ d="m 171.72594,337.52768 a 64.649765,61.619305 0 1 1 -129.299533,0 64.649765,61.619305 0 1 1 129.299533,0 z"
298+ transform="translate(-24.243664,557.43585)" />
299+ <text
300+ xml:space="preserve"
301+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
302+ x="171.72594"
303+ y="887.89246"
304+ id="text3771-2"
305+ sodipodi:linespacing="125%"><tspan
306+ sodipodi:role="line"
307+ id="tspan3773-8"
308+ x="171.72594"
309+ y="887.89246">Lorem ipsum</tspan></text>
310+ <text
311+ xml:space="preserve"
312+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
313+ x="171.72592"
314+ y="940.42041"
315+ id="text3775-3"
316+ sodipodi:linespacing="125%"><tspan
317+ sodipodi:role="line"
318+ id="tspan3777-7"
319+ x="171.72592"
320+ y="940.42041"
321+ style="font-size:48px;fill:#b3b3b3">bla bla bla bla bla bla bla ...</tspan></text>
322+ <path
323+ style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
324+ d="m 17.172597,974.76558 729.330123,0"
325+ id="path3769-2-9"
326+ inkscape:connector-curvature="0" />
327+ <path
328+ sodipodi:type="arc"
329+ style="fill:none;stroke:#000000;stroke-width:8;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
330+ id="path3929"
331+ sodipodi:cx="590.93921"
332+ sodipodi:cy="-119.06127"
333+ sodipodi:rx="33.335033"
334+ sodipodi:ry="33.335033"
335+ d="m 624.27424,-119.06127 a 33.335033,33.335033 0 1 1 -66.67006,0 33.335033,33.335033 0 1 1 66.67006,0 z"
336+ transform="matrix(0.85096826,0,0,0.85096826,175.43863,-68.81729)" />
337+ <path
338+ style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:3.40387297;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
339+ d="m 692.92249,-145.20602 20.63059,24.06901 12.03451,-13.75372 -20.63059,-17.19215"
340+ id="path3931"
341+ inkscape:connector-curvature="0" />
342+ </g>
343+</svg>
qml/Dash/graphics/phone/screenshots/settings@12.pngdeleted

Binary file not shown.

qml/Dash/graphics/phone/screenshots/ubuntu-weather-app.svgnew file+201
@@ -0,0 +1,201 @@
1+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2+<!-- Created with Inkscape (http://www.inkscape.org/) -->
3+
4+<svg
5+ xmlns:dc="http://purl.org/dc/elements/1.1/"
6+ xmlns:cc="http://creativecommons.org/ns#"
7+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
8+ xmlns:svg="http://www.w3.org/2000/svg"
9+ xmlns="http://www.w3.org/2000/svg"
10+ xmlns:xlink="http://www.w3.org/1999/xlink"
11+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
12+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
13+ width="829"
14+ height="480"
15+ id="svg2740"
16+ sodipodi:version="0.32"
17+ inkscape:version="0.48.5 r10040"
18+ version="1.0"
19+ sodipodi:docname="ubuntu-weather-app.svg"
20+ inkscape:output_extension="org.inkscape.output.svg.inkscape">
21+ <defs
22+ id="defs2742">
23+ <linearGradient
24+ id="linearGradient3824">
25+ <stop
26+ style="stop-color:#e6e6e6;stop-opacity:1;"
27+ offset="0"
28+ id="stop3826" />
29+ <stop
30+ style="stop-color:#23abff;stop-opacity:1;"
31+ offset="1"
32+ id="stop3828" />
33+ </linearGradient>
34+ <linearGradient
35+ inkscape:collect="always"
36+ xlink:href="#linearGradient3824"
37+ id="linearGradient3830"
38+ x1="348.55862"
39+ y1="343.23914"
40+ x2="348.55862"
41+ y2="-17.422215"
42+ gradientUnits="userSpaceOnUse" />
43+ </defs>
44+ <sodipodi:namedview
45+ id="base"
46+ pagecolor="#ffffff"
47+ bordercolor="#666666"
48+ borderopacity="1.0"
49+ gridtolerance="10000"
50+ guidetolerance="10"
51+ objecttolerance="10"
52+ inkscape:pageopacity="0.0"
53+ inkscape:pageshadow="2"
54+ inkscape:zoom="0.82625984"
55+ inkscape:cx="331.28234"
56+ inkscape:cy="125.54212"
57+ inkscape:document-units="px"
58+ inkscape:current-layer="layer1"
59+ showgrid="false"
60+ inkscape:window-width="1145"
61+ inkscape:window-height="847"
62+ inkscape:window-x="268"
63+ inkscape:window-y="63"
64+ inkscape:window-maximized="0" />
65+ <metadata
66+ id="metadata2745">
67+ <rdf:RDF>
68+ <cc:Work
69+ rdf:about="">
70+ <dc:format>image/svg+xml</dc:format>
71+ <dc:type
72+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
73+ </cc:Work>
74+ </rdf:RDF>
75+ </metadata>
76+ <g
77+ inkscape:label="Layer 1"
78+ inkscape:groupmode="layer"
79+ id="layer1"
80+ transform="translate(-321.13452,-104.68346)">
81+ <rect
82+ style="fill:url(#linearGradient3830);fill-opacity:1;stroke:#000000;stroke-width:0.69999999000000002;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
83+ id="rect2990"
84+ width="832.66785"
85+ height="480.47839"
86+ x="-1.210273"
87+ y="0.73188055"
88+ transform="translate(321.13452,104.68346)" />
89+ <path
90+ d="m 310.66382,132.06057 c -1.82703,2.0443 -22.21039,-18.38308 -24.89334,-17.37811 -2.68295,1.00497 -6.73783,33.64192 -9.57527,34.01165 -2.83744,0.36973 -7.71546,-33.08881 -10.48084,-33.88783 -2.76539,-0.79903 -18.7793,19.34152 -21.17964,17.82892 -2.40034,-1.51259 5.69972,-26.09808 4.36486,-28.67057 -1.33487,-2.57249 -31.86305,-3.78032 -32.49182,-6.6501 -0.62876,-2.86978 27.66855,-10.399376 27.90248,-13.241764 0.23394,-2.842387 -18.99828,-18.90215 -17.89698,-21.393798 1.10131,-2.491647 26.85456,8.177399 28.59429,5.99159 1.73972,-2.185809 -3.39182,-29.683489 -0.75169,-30.507098 2.64013,-0.823608 18.50306,18.485327 21.32153,18.392972 2.81847,-0.09235 11.30312,-24.287685 14.18258,-23.722317 2.87947,0.565368 1.44329,25.667631 3.67226,27.34365 2.22897,1.676019 24.27161,-6.28288 25.90675,-4.004945 1.63514,2.277936 -12.63923,24.445025 -11.77955,27.07291 0.85968,2.627885 27.01183,5.1355 26.65457,7.852474 -0.35726,2.716973 -32.69913,3.875594 -34.04684,6.507203 -1.3477,2.631613 12.32368,32.410863 10.49665,34.455163 z"
91+ id="path11949"
92+ inkscape:flatsided="false"
93+ inkscape:randomized="-0.092"
94+ inkscape:rounded="0.1"
95+ sodipodi:arg1="0.79570711"
96+ sodipodi:arg2="1.144773"
97+ sodipodi:cx="275.15002"
98+ sodipodi:cy="88.090233"
99+ sodipodi:r1="57.019234"
100+ sodipodi:r2="32.818508"
101+ sodipodi:sides="9"
102+ sodipodi:type="star"
103+ style="fill:#f5ff12;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;visibility:visible;display:inline;overflow:visible"
104+ transform="matrix(2.527571,0,0,2.527571,85.214779,60.619097)" />
105+ <path
106+ d="m 194.51692,77.283737 c 0,20.659371 -17.86424,37.407103 -39.90091,37.407103 -22.03666,0 -39.90091,-16.747732 -39.90091,-37.407103 0,-20.659371 17.86425,-37.4071 39.90091,-37.4071 22.03667,0 39.90091,16.747729 39.90091,37.4071 z"
107+ id="path11951"
108+ sodipodi:cx="154.61601"
109+ sodipodi:cy="77.283737"
110+ sodipodi:rx="39.900909"
111+ sodipodi:ry="37.407101"
112+ sodipodi:type="arc"
113+ style="fill:#f5ff12;fill-opacity:1;fill-rule:nonzero;stroke:#f5ff12;stroke-width:10.27388287;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:0.5;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
114+ transform="matrix(2.4568175,0,0,2.1990879,404.91256,120.05076)" />
115+ <path
116+ transform="matrix(3.2663208,0,0,3.4016021,515.92101,-254.59331)"
117+ style="fill:#6798e9;fill-opacity:1;fill-rule:nonzero;stroke:#6798e9;stroke-width:7.30059433;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0.5;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
118+ sodipodi:type="inkscape:offset"
119+ inkscape:radius="0"
120+ inkscape:original="M 110.53125 144.96875 C 100.76599 144.96875 92.384060 150.03292 88.281250 157.34375 C 86.642607 156.42892 84.835190 155.87500 82.906250 155.87500 C 77.073071 155.87500 72.219030 160.56753 70.718750 166.93750 C 68.157973 165.92533 65.180470 165.31250 62.000000 165.31250 C 52.458571 165.31250 44.718750 170.53296 44.718750 176.96875 C 44.718749 182.77816 51.066430 187.55402 59.312500 188.43750 C 58.942922 189.06456 58.656250 189.68551 58.656250 190.34375 C 58.656248 196.50568 75.538140 201.53125 96.343750 201.53125 C 117.14936 201.53125 134.03126 196.50568 134.03125 190.34375 C 134.03125 189.92916 133.77393 189.55916 133.62500 189.15625 C 134.53629 189.38287 135.43860 189.62500 136.43750 189.62500 C 142.00333 189.62501 146.53125 185.84318 146.53125 181.18750 C 146.53125 176.53182 142.00332 172.75000 136.43750 172.75000 C 135.75542 172.75000 135.17544 172.98560 134.53125 173.09375 C 135.09454 171.23218 135.46875 169.33268 135.46875 167.31250 C 135.46875 154.98864 124.31330 144.96875 110.53125 144.96875 z "
121+ id="path11953"
122+ d="m 110.53125,144.96875 c -9.76526,0 -18.14719,5.06417 -22.25,12.375 -1.638643,-0.91483 -3.44606,-1.46875 -5.375,-1.46875 -5.833179,0 -10.68722,4.69253 -12.1875,11.0625 -2.560777,-1.01217 -5.53828,-1.625 -8.71875,-1.625 -9.541429,0 -17.28125,5.22046 -17.28125,11.65625 -10e-7,5.80941 6.34768,10.58527 14.59375,11.46875 -0.369578,0.62706 -0.65625,1.24801 -0.65625,1.90625 -2e-6,6.16193 16.88189,11.1875 37.6875,11.1875 20.80561,0 37.68751,-5.02557 37.6875,-11.1875 0,-0.41459 -0.25732,-0.78459 -0.40625,-1.1875 0.91129,0.22662 1.8136,0.46875 2.8125,0.46875 5.56583,1e-5 10.09375,-3.78182 10.09375,-8.4375 0,-4.65568 -4.52793,-8.4375 -10.09375,-8.4375 -0.68208,0 -1.26206,0.2356 -1.90625,0.34375 0.56329,-1.86157 0.9375,-3.76107 0.9375,-5.78125 0,-12.32386 -11.15545,-22.34375 -24.9375,-22.34375 z" />
123+ <path
124+ inkscape:connector-curvature="0"
125+ style="fill:#386195;fill-opacity:1;fill-rule:nonzero;stroke:#386195;stroke-width:24.07697487;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0.5;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
126+ id="path11963"
127+ d="m 975.70833,305.75079 c -17.87875,0 -33.1897,9.10731 -40.70137,22.18913 -3.00012,-1.63696 -6.34124,-2.6392 -9.87286,-2.6392 -10.67967,0 -19.5639,8.39604 -22.31073,19.7943 -4.6884,-1.81117 -10.09942,-2.93251 -15.92242,-2.93251 -17.46892,0 -31.65122,9.35352 -31.65122,20.86955 0,10.39528 11.61748,18.9466 26.71485,20.52749 -0.67665,1.12204 -1.20995,2.24338 -1.20995,3.42121 0,11.02604 30.92124,19.98985 69.01327,19.98985 38.09195,0 69.0132,-8.96381 69.0132,-19.98985 0,-0.74183 -0.5017,-1.42952 -0.7743,-2.15046 1.6684,0.40556 3.3496,0.83088 5.1784,0.83088 10.1902,0 18.439,-6.7716 18.439,-15.10234 0,-8.33078 -8.2488,-15.05348 -18.439,-15.05348 -1.2488,0 -2.3535,0.39297 -3.5329,0.58647 1.0312,-3.33102 1.7422,-6.7466 1.7422,-10.36143 0,-22.05212 -20.4533,-39.97961 -45.68617,-39.97961 z" />
128+ <path
129+ inkscape:connector-curvature="0"
130+ style="fill:#6798e9;fill-opacity:1;fill-rule:nonzero;stroke:#6798e9;stroke-width:15.64964962;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:3.20000005;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
131+ sodipodi:nodetypes="cccc"
132+ id="path11965"
133+ d="m 717.80594,482.73655 c -32.27924,28.4432 5.62151,52.69793 20.03284,13.63938 l 11.08203,-36.22961 -31.11487,22.59023 z" />
134+ <path
135+ inkscape:connector-curvature="0"
136+ style="fill:#6798e9;fill-opacity:1;fill-rule:nonzero;stroke:#6798e9;stroke-width:15.64964962;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:3.20000005;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
137+ sodipodi:nodetypes="cccc"
138+ id="path11967"
139+ d="m 799.47,485.76116 c -32.27925,28.4432 5.62151,52.69792 20.03284,13.63937 l 11.08202,-36.2296 -31.11486,22.59023 z" />
140+ <text
141+ xml:space="preserve"
142+ style="font-size:32px;font-style:normal;font-weight:bold;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans Bold"
143+ x="68.985565"
144+ y="92.712631"
145+ id="text2992"
146+ sodipodi:linespacing="125%"
147+ transform="translate(321.13452,104.68346)"><tspan
148+ sodipodi:role="line"
149+ id="tspan2994"
150+ x="68.985565"
151+ y="92.712631" /></text>
152+ <text
153+ xml:space="preserve"
154+ style="font-size:10px;font-style:normal;font-weight:bold;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans Bold"
155+ x="-202.1156"
156+ y="155.64684"
157+ id="text2996"
158+ sodipodi:linespacing="125%"
159+ transform="translate(321.13452,104.68346)"><tspan
160+ sodipodi:role="line"
161+ id="tspan2998"
162+ x="-202.1156"
163+ y="155.64684" /></text>
164+ <text
165+ xml:space="preserve"
166+ style="font-size:24px;font-style:normal;font-weight:bold;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans Bold"
167+ x="342.91943"
168+ y="155.03653"
169+ id="text3000"
170+ sodipodi:linespacing="125%"><tspan
171+ sodipodi:role="line"
172+ id="tspan3002"
173+ x="342.91943"
174+ y="155.03653"
175+ style="font-size:32px;font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Ubuntu;-inkscape-font-specification:Ubuntu Medium">Wheather App</tspan></text>
176+ <text
177+ xml:space="preserve"
178+ style="font-size:24px;font-style:normal;font-weight:bold;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ff0000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans Bold"
179+ x="460.31592"
180+ y="358.3624"
181+ id="text3004"
182+ sodipodi:linespacing="125%"><tspan
183+ sodipodi:role="line"
184+ id="tspan3006"
185+ x="460.31592"
186+ y="358.3624"
187+ style="font-size:48px;fill:#ff0000;-inkscape-font-specification:Ubuntu;font-family:Ubuntu;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal">22C</tspan></text>
188+ <text
189+ xml:space="preserve"
190+ style="font-size:24px;font-style:normal;font-weight:bold;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#0000ff;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans Bold"
191+ x="460.19751"
192+ y="412.14325"
193+ id="text3004-0"
194+ sodipodi:linespacing="125%"><tspan
195+ sodipodi:role="line"
196+ id="tspan3006-4"
197+ x="460.19751"
198+ y="412.14325"
199+ style="font-size:48px;fill:#0000ff;-inkscape-font-specification:Ubuntu Medium;font-family:Ubuntu;font-weight:500;font-style:normal;font-stretch:normal;font-variant:normal">14C</tspan></text>
200+ </g>
201+</svg>
qml/DeviceConfiguration.qmlnew file+81
@@ -0,0 +1,81 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+
19+StateGroup {
20+ id: root
21+
22+ readonly property int useNativeOrientation: -1
23+
24+ property int primaryOrientation: useNativeOrientation
25+
26+ property int supportedOrientations: Qt.PortraitOrientation
27+ | Qt.InvertedPortraitOrientation
28+ | Qt.LandscapeOrientation
29+ | Qt.InvertedLandscapeOrientation
30+
31+ // Supported values so far:
32+ // "phone", "tablet" or "desktop"
33+ property string category: "phone"
34+
35+
36+ property alias name: root.state
37+
38+ states: [
39+ State {
40+ name: "mako"
41+ PropertyChanges {
42+ target: root
43+ supportedOrientations: Qt.PortraitOrientation
44+ | Qt.LandscapeOrientation
45+ | Qt.InvertedLandscapeOrientation
46+ }
47+ },
48+ State {
49+ name: "krillin"
50+ PropertyChanges {
51+ target: root
52+ supportedOrientations: Qt.PortraitOrientation
53+ | Qt.LandscapeOrientation
54+ | Qt.InvertedLandscapeOrientation
55+ }
56+ },
57+ State {
58+ name: "manta"
59+ PropertyChanges {
60+ target: root
61+ category: "tablet"
62+ }
63+ },
64+ State {
65+ name: "flo"
66+ PropertyChanges {
67+ target: root
68+ primaryOrientation: Qt.InvertedLandscapeOrientation
69+ category: "tablet"
70+ }
71+ },
72+ State {
73+ name: "desktop"
74+ PropertyChanges {
75+ target: root
76+ category: "desktop"
77+ }
78+ }
79+ ]
80+
81+}
qml/Greeter/CoverPage.qml+3−2
@@ -129,13 +129,14 @@ Showable {
129129
130130 DragHandle {
131131 id: dragHandle
132+ objectName: "coverPageDragHandle"
132133 anchors.fill: parent
133134 anchors.leftMargin: root.dragHandleLeftMargin
134135 enabled: root.draggable
135136 direction: Direction.Horizontal
136137
137− onDraggingChanged: {
138− if (dragging) {
138+ onPressedChanged: {
139+ if (pressed) {
139140 root.tease();
140141 showLabelAnimation.start();
141142 }
qml/Greeter/Greeter.qml+6−2
@@ -52,6 +52,8 @@ Showable {
5252 property int failedLoginsDelayAttempts: 7 // number of failed logins
5353 property real failedLoginsDelayMinutes: 5 // minutes of forced waiting
5454
55+ readonly property bool animating: loader.item ? loader.item.animating : false
56+
5557 signal tease()
5658 signal sessionStarted()
5759 signal emergencyCall()
@@ -141,8 +143,10 @@ Showable {
141143 enabled = false;
142144 if (LightDM.Greeter.startSessionSync()) {
143145 sessionStarted();
144− loader.item.notifyAuthenticationSucceeded();
145− } else {
146+ if (loader.item) {
147+ loader.item.notifyAuthenticationSucceeded();
148+ }
149+ } else if (loader.item) {
146150 loader.item.notifyAuthenticationFailed();
147151 }
148152 enabled = true;
qml/Greeter/NarrowView.qml+1
@@ -33,6 +33,7 @@ FocusScope {
3333 property alias infographicModel: coverPage.infographicModel
3434 readonly property bool fullyShown: coverPage.showProgress === 1 || lockscreen.shown
3535 readonly property bool required: coverPage.required || lockscreen.required
36+ readonly property bool animating: coverPage.showAnimation.running || coverPage.hideAnimation.running
3637
3738 signal selected(int index) // unused
3839 signal responded(string response)
qml/Greeter/ShimGreeter.qmlnew file+45
@@ -0,0 +1,45 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.3
18+
19+/*
20+ * This is a shim greeter that is only used to provide a shell,
21+ * running without a greeter, the requisite information
22+ *
23+ */
24+
25+/* FIXME: this shuld be fine as a QtObject, but bug lp:1447391
26+ * dictates wrapping as an item instead
27+ */
28+Item {
29+ id: shimGreeter
30+
31+ readonly property bool active: false
32+ readonly property bool hasLockedApp: lockedApp !== ""
33+ readonly property bool locked: false
34+ readonly property bool shown: false
35+ readonly property bool waiting: false
36+
37+ property string lockedApp: ""
38+
39+ // Since ShimGreeter is never active, these can just return
40+ property var forceShow: (function() { return; })
41+ property var notifyAboutToFocusApp: (function(appId) { return; })
42+ property var notifyAppFocused: (function(appId) { return; })
43+ property var notifyShowingDashFromDrag: (function(appId) { return false; })
44+
45+}
qml/Greeter/WideView.qml+1
@@ -32,6 +32,7 @@ FocusScope {
3232 property alias infographicModel: coverPage.infographicModel
3333 readonly property bool fullyShown: coverPage.showProgress === 1
3434 readonly property bool required: coverPage.required
35+ readonly property bool animating: coverPage.showAnimation.running || coverPage.hideAnimation.running
3536
3637 // so that it can be replaced in tests with a mock object
3738 property var inputMethod: Qt.inputMethod
qml/Launcher/Launcher.qml+7−15
@@ -210,7 +210,7 @@ Item {
210210 bottom: parent.bottom
211211 }
212212 x: -width
213− visible: root.x > 0 || x > -width || dragArea.status === DirectionalDragArea.Undecided
213+ visible: root.x > 0 || x > -width || dragArea.pressed
214214 model: LauncherModel
215215
216216 property bool animate: true
@@ -272,7 +272,7 @@ Item {
272272 }
273273 }
274274
275− EdgeDragArea {
275+ DirectionalDragArea {
276276 id: dragArea
277277 objectName: "launcherDragArea"
278278
@@ -283,20 +283,11 @@ Item {
283283 width: root.dragAreaWidth
284284 height: root.height
285285
286− onTouchXChanged: {
287− if (status !== DirectionalDragArea.Recognized || launcher.state == "visible")
286+ onDistanceChanged: {
287+ if (!dragging || launcher.state == "visible")
288288 return;
289289
290− // When the gesture finally gets recognized, the finger will likely be
291− // reasonably far from the edge. If we made the panel immediately
292− // follow the finger position it would be visually unpleasant as it
293− // would appear right next to the user's finger out of nowhere.
294− // Instead, we make the panel go towards the user's finger in several
295− // steps. ie., in an animated way.
296− var targetPanelX = Math.min(0, touchX - panel.width) - root.x
297− var delta = targetPanelX - panel.x
298− // the trick is not to go all the way (1.0) as it would cause a sudden jump
299− panel.x += 0.4 * delta
290+ panel.x = -panel.width + Math.min(Math.max(0, distance), panel.width);
300291 }
301292
302293 onDraggingChanged: {
@@ -306,7 +297,8 @@ Item {
306297 if (distance > minimizeDistance) {
307298 root.dash();
308299 }
309− } else {
300+ } else if (root.state === "") {
301+ // didn't drag far enough. rollback
310302 root.switchToNextState("")
311303 }
312304 }
qml/Notifications/ShapedIcon.qml+4−3
@@ -14,8 +14,8 @@
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1515 */
1616
17−import QtQuick 2.0
18−import Ubuntu.Components 0.1
17+import QtQuick 2.3
18+import Ubuntu.Components 1.2
1919
2020Item {
2121 property string fileSource
@@ -25,7 +25,8 @@ Item {
2525 objectName: "shapedIcon"
2626 anchors.fill: parent
2727 visible: shaped
28− image: realImage
28+ source: realImage
29+ sourceFillMode: UbuntuShape.PreserveAspectCrop
2930 }
3031
3132 Image {
qml/OrientedShell.qmlnew file+185
@@ -0,0 +1,185 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import QtQuick.Window 2.0
19+import Unity.Session 0.1
20+import GSettings 1.0
21+import "Components"
22+import "Rotation"
23+
24+Rectangle {
25+ id: root
26+ color: "black"
27+
28+ implicitWidth: units.gu(40)
29+ implicitHeight: units.gu(71)
30+
31+ // NB: native and primary orientations here don't map exactly to their QScreen counterparts
32+ readonly property int nativeOrientation: width > height ? Qt.LandscapeOrientation : Qt.PortraitOrientation
33+
34+ readonly property int primaryOrientation:
35+ deviceConfiguration.primaryOrientation == deviceConfiguration.useNativeOrientation
36+ ? nativeOrientation : deviceConfiguration.primaryOrientation
37+
38+ DeviceConfiguration {
39+ id: deviceConfiguration
40+ name: applicationArguments.deviceName
41+ }
42+
43+ // to be overwritten by tests
44+ property var usageModeSettings: GSettings { schema.id: "com.canonical.Unity8" }
45+ property int physicalOrientation: Screen.orientation
46+ property bool orientationLocked: OrientationLock.enabled
47+ property var orientationLock: OrientationLock
48+
49+ property int orientation
50+ onPhysicalOrientationChanged: {
51+ if (!orientationLocked) {
52+ orientation = physicalOrientation;
53+ }
54+ }
55+ onOrientationLockedChanged: {
56+ if (orientationLocked) {
57+ orientationLock.savedOrientation = physicalOrientation;
58+ } else {
59+ orientation = physicalOrientation;
60+ }
61+ }
62+ Component.onCompleted: {
63+ if (orientationLocked) {
64+ orientation = orientationLock.savedOrientation;
65+ }
66+ }
67+
68+ readonly property int supportedOrientations: shell.supportedOrientations
69+ & deviceConfiguration.supportedOrientations
70+ property int acceptedOrientationAngle: {
71+ if (orientation & supportedOrientations) {
72+ return Screen.angleBetween(nativeOrientation, orientation);
73+ } else if (shell.orientation & supportedOrientations) {
74+ // stay where we are
75+ return shell.orientationAngle;
76+ } else if (angleToOrientation(shell.mainAppWindowOrientationAngle) & supportedOrientations) {
77+ return shell.mainAppWindowOrientationAngle;
78+ } else {
79+ // rotate to some supported orientation as we can't stay where we currently are
80+ // TODO: Choose the closest to the current one
81+ if (supportedOrientations & Qt.PortraitOrientation) {
82+ return Screen.angleBetween(nativeOrientation, Qt.PortraitOrientation);
83+ } else if (supportedOrientations & Qt.LandcscapeOrientation) {
84+ return Screen.angleBetween(nativeOrientation, Qt.LandscapeOrientation);
85+ } else if (supportedOrientations & Qt.InvertedPortraitOrientation) {
86+ return Screen.angleBetween(nativeOrientation, Qt.InvertedPortraitOrientation);
87+ } else if (supportedOrientations & Qt.InvertedLandscapeOrientation) {
88+ return Screen.angleBetween(nativeOrientation, Qt.InvertedLandscapeOrientation);
89+ } else {
90+ // if all fails, fallback to primary orientation
91+ return Screen.angleBetween(nativeOrientation, primaryOrientation);
92+ }
93+ }
94+ }
95+
96+ function angleToOrientation(angle) {
97+ switch (angle) {
98+ case 0:
99+ return nativeOrientation;
100+ break;
101+ case 90:
102+ return nativeOrientation === Qt.PortraitOrientation ? Qt.InvertedLandscapeOrientation
103+ : Qt.PortraitOrientation;
104+ break;
105+ case 180:
106+ return nativeOrientation === Qt.PortraitOrientation ? Qt.InvertedPortraitOrientation
107+ : Qt.InvertedLandscapeOrientation;
108+ break;
109+ case 270:
110+ return nativeOrientation === Qt.PortraitOrientation ? Qt.LandscapeOrientation
111+ : Qt.InvertedPortraitOrientation;
112+ break;
113+ default:
114+ console.warn("angleToOrientation: Invalid orientation angle: " + angle);
115+ return primaryOrientation;
116+ }
117+ }
118+
119+ RotationStates {
120+ id: rotationStates
121+ objectName: "rotationStates"
122+ orientedShell: root
123+ shell: shell
124+ shellCover: shellCover
125+ windowScreenshot: windowScreenshot
126+ }
127+
128+ Shell {
129+ id: shell
130+ objectName: "shell"
131+ width: root.width
132+ height: root.height
133+ orientation: root.angleToOrientation(orientationAngle)
134+ primaryOrientation: root.primaryOrientation
135+ nativeOrientation: root.nativeOrientation
136+ nativeWidth: root.width
137+ nativeHeight: root.height
138+
139+ // TODO: Factor in the connected input devices (eg: physical keyboard, mouse, touchscreen),
140+ // what's the output device (eg: big TV, desktop monitor, phone display), etc.
141+ usageScenario: {
142+ if (root.usageModeSettings.usageMode === "Windowed") {
143+ return "desktop";
144+ } else if (root.usageModeSettings.usageMode === "Staged"
145+ && deviceConfiguration.category === "desktop") {
146+ return "tablet";
147+ } else {
148+ return deviceConfiguration.category;
149+ }
150+ }
151+
152+ property real transformRotationAngle
153+ property real transformOriginX
154+ property real transformOriginY
155+
156+ transform: Rotation {
157+ origin.x: shell.transformOriginX; origin.y: shell.transformOriginY; axis { x: 0; y: 0; z: 1 }
158+ angle: shell.transformRotationAngle
159+ }
160+ }
161+
162+ Rectangle {
163+ id: shellCover
164+ color: "black"
165+ anchors.fill: parent
166+ visible: false
167+ }
168+
169+ WindowScreenshot {
170+ id: windowScreenshot
171+ visible: false
172+ width: root.width
173+ height: root.height
174+
175+ property real transformRotationAngle
176+ property real transformOriginX
177+ property real transformOriginY
178+
179+ transform: Rotation {
180+ origin.x: windowScreenshot.transformOriginX; origin.y: windowScreenshot.transformOriginY;
181+ axis { x: 0; y: 0; z: 1 }
182+ angle: windowScreenshot.transformRotationAngle
183+ }
184+ }
185+}
qml/Panel/Indicators/IndicatorsLight.qml+8
@@ -26,6 +26,8 @@ import Unity.Indicators 0.1 as Indicators
2626QtObject {
2727 id: root
2828
29+ property color color: "darkgreen"
30+
2931 property var _actionGroup: QMenuModel.QDBusActionGroup {
3032 busType: 1
3133 busName: "com.canonical.indicator.messages"
@@ -50,4 +52,10 @@ QtObject {
5052 return (Powerd.status === Powerd.Off && _rootState.hasMessages) ? Lights.On : Lights.Off
5153 }
5254 }
55+
56+ property var _colorBinding: Binding {
57+ target: Lights
58+ property: "color"
59+ value: root.color
60+ }
5361}
qml/Panel/IndicatorsMenu.qml+12−2
@@ -186,9 +186,18 @@ Showable {
186186 enabled: !root.shown && root.available
187187 autoCompleteDragThreshold: maxTotalDragDistance / 2
188188 stretch: true
189− distanceThreshold: enableHint ? 0 : minimizedPanelHeight
190189
191− onTapped: showTapped(Qt.point(touchSceneX, touchSceneY));
190+ onPressedChanged: {
191+ if (pressed) {
192+ touchPressTime = new Date().getTime();
193+ } else {
194+ var touchReleaseTime = new Date().getTime();
195+ if (touchReleaseTime - touchPressTime <= 300) {
196+ root.showTapped(Qt.point(touchSceneX, touchSceneY));
197+ }
198+ }
199+ }
200+ property var touchPressTime
192201
193202 // using hint regulates minimum to hint displacement, but in fullscreen mode, we need to do it manually.
194203 overrideStartValue: enableHint ? minimizedPanelHeight : expandedPanelHeight + handle.height
@@ -204,6 +213,7 @@ Showable {
204213
205214 DragHandle {
206215 id: __hideDragHandle
216+ objectName: "hideDragHandle"
207217 anchors.fill: handle
208218 direction: Direction.Upwards
209219 enabled: root.shown && root.available
qml/Panel/Panel.qml+9
@@ -27,6 +27,9 @@ Item {
2727 property alias indicators: __indicators
2828 property alias callHint: __callHint
2929 property bool fullscreenMode: false
30+ property real indicatorAreaShowProgress: 1.0
31+
32+ opacity: fullscreenMode && indicators.fullyClosed ? 0.0 : 1.0
3033
3134 Rectangle {
3235 id: darkenedArea
@@ -58,6 +61,12 @@ Item {
5861 NumberAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing }
5962 }
6063
64+ transform: Translate {
65+ y: indicators.state === "initial"
66+ ? (1.0 - indicatorAreaShowProgress) * -d.indicatorHeight
67+ : 0
68+ }
69+
6170 BorderImage {
6271 id: dropShadow
6372 anchors {
qml/Rotation/HalfLoopRotationAnimation.qmlnew file+46
@@ -0,0 +1,46 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.3
18+
19+SequentialAnimation {
20+ id: root
21+
22+ // set from outside
23+ property int fromAngle
24+ property int toAngle
25+ property var info
26+ property var shell
27+
28+ readonly property bool flipShellDimensions: toAngle == 90 || toAngle == 270
29+
30+ ScriptAction { script: {
31+ info.transitioning = true;
32+ shell.orientationAngle = root.toAngle;
33+ shell.x = (orientedShell.width - shell.width) / 2
34+ shell.y = (orientedShell.height - shell.height) / 2;
35+ shell.transformOriginX = shell.width / 2;
36+ shell.transformOriginY = shell.height / 2;
37+ shell.updateFocusedAppOrientation();
38+ } }
39+ NumberAnimation {
40+ target: shell
41+ property: "transformRotationAngle"
42+ from: root.fromAngle; to: root.toAngle
43+ duration: rotationDuration; easing.type: rotationEasing
44+ }
45+ ScriptAction { script: { info.transitioning = false; } }
46+}
qml/Rotation/ImmediateRotationAction.qmlnew file+45
@@ -0,0 +1,45 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.3
18+
19+ScriptAction {
20+ property var info
21+ property var shell
22+
23+ script: {
24+ info.transitioning = true;
25+ shell.orientationAngle = info.requestedOrientationAngle;
26+ shell.transformRotationAngle = info.requestedOrientationAngle;
27+
28+ // Making bindings as orientedShell's dimensions might wiggle during startup.
29+ if (info.requestedOrientationAngle === 90 || info.requestedOrientationAngle === 270) {
30+ shell.width = Qt.binding(function() { return orientedShell.height; });
31+ shell.height = Qt.binding(function() { return orientedShell.width; });
32+ } else {
33+ shell.width = Qt.binding(function() { return orientedShell.width; });
34+ shell.height = Qt.binding(function() { return orientedShell.height; });
35+ }
36+
37+ shell.x = Qt.binding(function() { return (orientedShell.width - shell.width) / 2; });
38+ shell.y = Qt.binding(function() { return (orientedShell.height - shell.height) / 2; });
39+ shell.transformOriginX = Qt.binding(function() { return shell.width / 2; });
40+ shell.transformOriginY = Qt.binding(function() { return shell.height / 2; });
41+
42+ shell.updateFocusedAppOrientation();
43+ info.transitioning = false;
44+ }
45+}
qml/Rotation/NinetyRotationAnimation.qmlnew file+90
@@ -0,0 +1,90 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.3
18+
19+SequentialAnimation {
20+ id: root
21+
22+ property int fromAngle
23+ property int toAngle
24+ property var info
25+ property var shell
26+
27+ readonly property real fromY: fromAngle === 0 || fromAngle === 90 ? 0 : orientedShell.height - orientedShell.width;
28+ readonly property real toY: toAngle === 0 || toAngle === 90 ? 0 : orientedShell.height - orientedShell.width;
29+ readonly property bool flipShellDimensions: toAngle == 90 || toAngle == 270
30+
31+ ScriptAction { script: {
32+ info.transitioning = true;
33+ windowScreenshot.take();
34+ windowScreenshot.visible = true;
35+ shell.orientationAngle = root.toAngle;
36+ shell.x = 0;
37+ shell.width = flipShellDimensions ? orientedShell.height : orientedShell.width;
38+ shell.height = flipShellDimensions ? orientedShell.width : orientedShell.height;
39+ shell.transformOriginX = orientedShell.width / 2;
40+ shell.transformOriginY = orientedShell.width / 2;
41+ shell.updateFocusedAppOrientation();
42+ shellCover.visible = true;
43+
44+ windowScreenshot.transformOriginX = orientedShell.width / 2;
45+ if (fromAngle == 180 || fromAngle == 270) {
46+ windowScreenshot.transformOriginY = orientedShell.height - (orientedShell.width / 2);
47+ } else {
48+ windowScreenshot.transformOriginY = orientedShell.width / 2;
49+ }
50+ } }
51+ ParallelAnimation {
52+ NumberAnimation {
53+ target: shellCover; property: "opacity"; from: 1; to: 0;
54+ duration: rotationDuration; easing.type: rotationEasing
55+ }
56+ RotationAnimation {
57+ target: shell; property: "transformRotationAngle";
58+ from: root.fromAngle; to: root.toAngle
59+ direction: RotationAnimation.Shortest
60+ duration: rotationDuration; easing.type: rotationEasing
61+ }
62+ NumberAnimation {
63+ target: shell; property: "y"
64+ from: root.fromY; to: root.toY
65+ duration: rotationDuration; easing.type: rotationEasing
66+ }
67+
68+ NumberAnimation {
69+ target: windowScreenshot; property: "opacity"; from: 1; to: 0;
70+ duration: rotationDuration; easing.type: rotationEasing
71+ }
72+ RotationAnimation {
73+ target: windowScreenshot; property: "transformRotationAngle";
74+ from: 0; to: root.toAngle - root.fromAngle
75+ direction: RotationAnimation.Shortest
76+ duration: rotationDuration; easing.type: rotationEasing
77+ }
78+ NumberAnimation {
79+ target: windowScreenshot; property: "y"
80+ from: 0; to: root.toY - root.fromY
81+ duration: rotationDuration; easing.type: rotationEasing
82+ }
83+ }
84+ ScriptAction { script: {
85+ windowScreenshot.visible = false;
86+ windowScreenshot.discard();
87+ shellCover.visible = false;
88+ info.transitioning = false;
89+ } }
90+}
qml/Rotation/RotationStates.qmlnew file+278
@@ -0,0 +1,278 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import Ubuntu.Components 1.1
19+import Powerd 0.1
20+
21+// Why the state machine is done that way:
22+// We cannot use regular PropertyChanges{} inside the State elements as steps in the
23+// transition animations must take place in a well defined order.
24+// Which means that we also cannot jump to a new state in the middle of a transition
25+// as that would make hell brake loose.
26+StateGroup {
27+ id: root
28+
29+ // to be set from the outside
30+ property Item orientedShell
31+ property Item shell
32+ property Item shellCover
33+ property Item windowScreenshot
34+
35+ property int rotationDuration: 450
36+ property int rotationEasing: Easing.InOutCubic
37+ // Those values are good for debugging/development
38+ //property int rotationDuration: 3000
39+ //property int rotationEasing: Easing.Linear
40+
41+ state: "0"
42+ states: [
43+ State { name: "0" },
44+ State { name: "90" },
45+ State { name: "180" },
46+ State { name: "270" }
47+ ]
48+
49+ property QtObject d: QtObject {
50+ id: d
51+
52+ property bool startingUp: true
53+ property var finishStartUpTimer: Timer {
54+ interval: 500
55+ onTriggered: d.startingUp = false
56+ }
57+ Component.onCompleted: {
58+ finishStartUpTimer.start();
59+ }
60+
61+ property bool transitioning: false
62+ onTransitioningChanged: {
63+ d.tryUpdateState();
64+ }
65+
66+ readonly property int requestedOrientationAngle: root.orientedShell.acceptedOrientationAngle
67+
68+ // Avoiding a direct call to tryUpdateState() as the state change might trigger an immediate
69+ // change to Shell.orientationAngle which, in its turn, causes a reevaluation of
70+ // requestedOrientationAngle (ie., OrientedShell.acceptedOrientationAngle). A reentrant evaluation
71+ // of a binding is detected by QML as a binding loop and QML will deny the reevalutation, which
72+ // will leave us in a bogus state.
73+ //
74+ // To avoid this mess we update the state in the next event loop iteration, ensuring a clean
75+ // call stack.
76+ onRequestedOrientationAngleChanged: {
77+ stateUpdateTimer.start();
78+ }
79+ property Timer stateUpdateTimer: Timer {
80+ id: stateUpdateTimer
81+ interval: 1
82+ onTriggered: { d.tryUpdateState(); }
83+ }
84+
85+ function tryUpdateState() {
86+ if (d.transitioning || (!d.startingUp && !root.shell.orientationChangesEnabled)) {
87+ return;
88+ }
89+
90+ var requestedState = d.requestedOrientationAngle.toString();
91+ if (requestedState !== root.state) {
92+ d.resolveAnimationType();
93+ root.state = requestedState;
94+ }
95+ }
96+
97+ property Connections shellConnections: Connections {
98+ target: root.shell
99+ onOrientationChangesEnabledChanged: {
100+ d.tryUpdateState();
101+ }
102+ }
103+
104+ property var shellBeingResized: Binding {
105+ target: root.shell
106+ property: "beingResized"
107+ value: d.transitioning
108+ }
109+
110+ readonly property int fullAnimation: 0
111+ readonly property int indicatorsBarAnimation: 1
112+ readonly property int noAnimation: 2
113+
114+ property int animationType
115+
116+ // animationType update *must* take place *before* the state update.
117+ // If animationType and state were updated through bindings, as with normal qml code,
118+ // there would be no guarantee in the order of the binding updates, which could then
119+ // cause the wrong transitions to be chosen for the state changes.
120+ function resolveAnimationType() {
121+ if (d.startingUp) {
122+ // During start up, inital property values are still settling while we're still
123+ // to render the very first frame
124+ d.animationType = d.noAnimation;
125+ } else if (Powerd.status === Powerd.Off) {
126+ // There's no point in animating if the user can't see it (display is off).
127+ d.animationType = d.noAnimation;
128+ } else if (root.shell.showingGreeter) {
129+ // A rotating greeter looks weird.
130+ d.animationType = d.noAnimation;
131+ } else {
132+ if (!root.shell.mainApp) {
133+ // shouldn't happen but, anyway
134+ d.animationType = d.fullAnimation;
135+ return;
136+ }
137+
138+ if (root.shell.mainApp.rotatesWindowContents) {
139+ // The application will animate its own GUI, so we don't have to do anything ourselves.
140+ d.animationType = d.noAnimation;
141+ } else if (root.shell.mainAppWindowOrientationAngle == d.requestedOrientationAngle) {
142+ // The app window is already on its final orientation angle.
143+ // So we just animate the indicators bar
144+ // TODO: what if the app is fullscreen?
145+ d.animationType = d.indicatorsBarAnimation;
146+ } else {
147+ d.animationType = d.fullAnimation;
148+ }
149+ }
150+ }
151+
152+ // When an application switch takes place, d.requestedOrientationAngle and
153+ // root.shell.mainAppWindowOrientationAngle get updated separately, at different moments.
154+ // So, when one of those properties change, we shouldn't make a decision straight away
155+ // as the other might be stale and about to be changed. So let's give it a bit of time for
156+ // them to get properly updated.
157+ // This approach is indeed a bit hacky.
158+ property bool appWindowOrientationAngleNeedsUpdateUnstable:
159+ root.shell.orientationAngle === d.requestedOrientationAngle
160+ && root.shell.mainApp
161+ && root.shell.mainAppWindowOrientationAngle !== root.shell.orientationAngle
162+ && !d.transitioning
163+ onAppWindowOrientationAngleNeedsUpdateUnstableChanged: {
164+ stableTimer.restart();
165+ }
166+ property Timer stableTimer: Timer {
167+ interval: 200
168+ onTriggered: {
169+ if (d.appWindowOrientationAngleNeedsUpdateUnstable) {
170+ shell.updateFocusedAppOrientationAnimated();
171+ }
172+ }
173+ }
174+ }
175+
176+ transitions: [
177+ Transition {
178+ from: "90"; to: "0"
179+ enabled: d.animationType == d.fullAnimation
180+ NinetyRotationAnimation { fromAngle: 90; toAngle: 0
181+ info: d; shell: root.shell }
182+ },
183+ Transition {
184+ from: "0"; to: "90"
185+ enabled: d.animationType == d.fullAnimation
186+ NinetyRotationAnimation { fromAngle: 0; toAngle: 90
187+ info: d; shell: root.shell }
188+ },
189+ Transition {
190+ from: "0"; to: "270"
191+ enabled: d.animationType == d.fullAnimation
192+ NinetyRotationAnimation { fromAngle: 0; toAngle: 270
193+ info: d; shell: root.shell }
194+ },
195+ Transition {
196+ from: "270"; to: "0"
197+ enabled: d.animationType == d.fullAnimation
198+ NinetyRotationAnimation { fromAngle: 270; toAngle: 0
199+ info: d; shell: root.shell }
200+ },
201+ Transition {
202+ from: "90"; to: "180"
203+ enabled: d.animationType == d.fullAnimation
204+ NinetyRotationAnimation { fromAngle: 90; toAngle: 180
205+ info: d; shell: root.shell }
206+ },
207+ Transition {
208+ from: "180"; to: "90"
209+ enabled: d.animationType == d.fullAnimation
210+ NinetyRotationAnimation { fromAngle: 180; toAngle: 90
211+ info: d; shell: root.shell }
212+ },
213+ Transition {
214+ from: "180"; to: "270"
215+ enabled: d.animationType == d.fullAnimation
216+ NinetyRotationAnimation { fromAngle: 180; toAngle: 270
217+ info: d; shell: root.shell }
218+ },
219+ Transition {
220+ from: "270"; to: "180"
221+ enabled: d.animationType == d.fullAnimation
222+ NinetyRotationAnimation { fromAngle: 270; toAngle: 180
223+ info: d; shell: root.shell }
224+ },
225+ Transition {
226+ from: "0"; to: "180"
227+ enabled: d.animationType == d.fullAnimation
228+ HalfLoopRotationAnimation { fromAngle: 0; toAngle: 180
229+ info: d; shell: root.shell }
230+ },
231+ Transition {
232+ from: "180"; to: "0"
233+ enabled: d.animationType == d.fullAnimation
234+ HalfLoopRotationAnimation { fromAngle: 180; toAngle: 0
235+ info: d; shell: root.shell }
236+ },
237+ Transition {
238+ from: "90"; to: "270"
239+ enabled: d.animationType == d.fullAnimation
240+ HalfLoopRotationAnimation { fromAngle: 90; toAngle: 270
241+ info: d; shell: root.shell }
242+ },
243+ Transition {
244+ from: "270"; to: "90"
245+ enabled: d.animationType == d.fullAnimation
246+ HalfLoopRotationAnimation { fromAngle: 270; toAngle: 90
247+ info: d; shell: root.shell }
248+ },
249+ Transition {
250+ objectName: "immediateTransition"
251+ enabled: d.animationType == d.noAnimation
252+ ImmediateRotationAction { info: d; shell: root.shell }
253+ },
254+ Transition {
255+ enabled: d.animationType == d.indicatorsBarAnimation
256+ SequentialAnimation {
257+ ScriptAction { script: {
258+ d.transitioning = true;
259+ } }
260+ NumberAnimation {
261+ duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing
262+ target: root.shell; property: "indicatorAreaShowProgress"
263+ from: 1.0; to: 0.0
264+ }
265+ ImmediateRotationAction { info: d; shell: root.shell }
266+ NumberAnimation {
267+ duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing
268+ target: root.shell; property: "indicatorAreaShowProgress"
269+ from: 0.0; to: 1.0
270+ }
271+ ScriptAction { script: {
272+ d.transitioning = false;
273+ }}
274+ }
275+ }
276+ ]
277+
278+}
qml/Shell.qml+161−83
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013-2015 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
@@ -45,48 +45,80 @@ import Unity.Indicators 0.1 as Indicators
4545Item {
4646 id: shell
4747
48+ // to be set from outside
49+ property int orientationAngle: 0
50+ property int orientation
51+ property int primaryOrientation
52+ property int nativeOrientation
53+ property real nativeWidth
54+ property real nativeHeight
55+ property alias indicatorAreaShowProgress: panel.indicatorAreaShowProgress
56+ property bool beingResized
57+ property string usageScenario: "phone" // supported values: "phone", "tablet" or "desktop"
58+ function updateFocusedAppOrientation() {
59+ applicationsDisplayLoader.item.updateFocusedAppOrientation();
60+ }
61+ function updateFocusedAppOrientationAnimated() {
62+ applicationsDisplayLoader.item.updateFocusedAppOrientationAnimated();
63+ }
64+
65+ // to be read from outside
66+ readonly property int mainAppWindowOrientationAngle:
67+ applicationsDisplayLoader.item ? applicationsDisplayLoader.item.mainAppWindowOrientationAngle : 0
68+
69+ readonly property bool orientationChangesEnabled: panel.indicators.fullyClosed
70+ && (applicationsDisplayLoader.item && applicationsDisplayLoader.item.orientationChangesEnabled)
71+ && !greeter.animating
72+
73+ readonly property bool showingGreeter: greeter.shown
74+
75+ property bool startingUp: true
76+ Timer { id: finishStartUpTimer; interval: 500; onTriggered: startingUp = false }
77+
78+ property int supportedOrientations: {
79+ if (startingUp) {
80+ // Ensure we don't rotate during start up
81+ return Qt.PrimaryOrientation;
82+ } else if (greeter.shown) {
83+ return Qt.PrimaryOrientation;
84+ } else if (mainApp) {
85+ return mainApp.supportedOrientations;
86+ } else {
87+ // we just don't care
88+ return Qt.PortraitOrientation
89+ | Qt.LandscapeOrientation
90+ | Qt.InvertedPortraitOrientation
91+ | Qt.InvertedLandscapeOrientation;
92+ }
93+ }
94+
95+ // For autopilot consumption
96+ readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
97+
98+ // internal props from here onwards
99+ readonly property var mainApp:
100+ applicationsDisplayLoader.item ? applicationsDisplayLoader.item.mainApp : null
101+
48102 // Disable everything while greeter is waiting, so that the user can't swipe
49103 // the greeter or launcher until we know whether the session is locked.
50104 enabled: !greeter.waiting
51105
52− // this is only here to select the width / height of the window if not running fullscreen
53− property bool tablet: false
54− width: tablet ? units.gu(160) : applicationArguments.hasGeometry() ? applicationArguments.width() : units.gu(40)
55− height: tablet ? units.gu(100) : applicationArguments.hasGeometry() ? applicationArguments.height() : units.gu(71)
56−
57106 property real edgeSize: units.gu(2)
58107 property url defaultBackground: Qt.resolvedUrl(shell.width >= units.gu(60) ? "graphics/tablet_background.jpg" : "graphics/phone_background.jpg")
59108 property url background: asImageTester.status == Image.Ready ? asImageTester.source
60109 : gsImageTester.status == Image.Ready ? gsImageTester.source : defaultBackground
61− readonly property real panelHeight: panel.panelHeight
62−
63− property bool sideStageEnabled: shell.width >= units.gu(100)
64− readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
65−
66− property int orientation
67− readonly property int deviceOrientationAngle: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation)
68− onDeviceOrientationAngleChanged: {
69− if (!OrientationLock.enabled) {
70− orientation = Screen.orientation;
71− }
72− }
73− readonly property bool orientationLockEnabled: OrientationLock.enabled
74− onOrientationLockEnabledChanged: {
75− if (orientationLockEnabled) {
76− OrientationLock.savedOrientation = Screen.orientation;
77− } else {
78− orientation = Screen.orientation;
79− }
80− }
81110
82111 // This is _only_ used to expose the property to autopilot tests
83112 readonly property string testShellMode: shellMode
84113
114+ readonly property alias greeter: greeterLoader.item
115+
85116 function activateApplication(appId) {
86117 if (ApplicationManager.findApplication(appId)) {
87118 ApplicationManager.requestFocusApplication(appId);
88119 } else {
89− var execFlags = shell.sideStageEnabled ? ApplicationManager.NoFlag : ApplicationManager.ForceMainStage;
120+ var execFlags = shell.usageScenario === "phone" ? ApplicationManager.ForceMainStage
121+ : ApplicationManager.NoFlag;
90122 ApplicationManager.startApplication(appId, execFlags);
91123 }
92124 }
@@ -110,7 +142,6 @@ Item {
110142
111143 GSettings {
112144 id: backgroundSettings
113− objectName: "backgroundSettings"
114145 schema.id: "org.gnome.desktop.background"
115146 }
116147
@@ -124,11 +155,6 @@ Item {
124155 sourceSize.width: 0
125156 }
126157
127− GSettings {
128− id: usageModeSettings
129− schema.id: "com.canonical.Unity8"
130− }
131−
132158 Binding {
133159 target: LauncherModel
134160 property: "applicationManager"
@@ -140,9 +166,7 @@ Item {
140166 if (ApplicationManager.count > 0) {
141167 ApplicationManager.focusApplication(ApplicationManager.get(0).appId);
142168 }
143− if (orientationLockEnabled) {
144− orientation = OrientationLock.savedOrientation;
145− }
169+ finishStartUpTimer.start();
146170 }
147171
148172 VolumeControl {
@@ -180,6 +204,10 @@ Item {
180204 Keys.onReleased: physicalKeysMapper.onKeyReleased(event);
181205 }
182206
207+ HomeKeyWatcher {
208+ onActivated: { launcher.fadeOut(); shell.showHome(); }
209+ }
210+
183211 Item {
184212 id: stages
185213 objectName: "stages"
@@ -231,9 +259,21 @@ Item {
231259 // theoretical attack where user enters lockedApp mode, then makes
232260 // the screen larger (maybe connects to monitor) and tries to enter
233261 // tablet mode.
234− property bool tabletMode: shell.sideStageEnabled && !greeter.hasLockedApp
235− source: usageModeSettings.usageMode === "Windowed" ? "Stages/DesktopStage.qml"
236− : tabletMode ? "Stages/TabletStage.qml" : "Stages/PhoneStage.qml"
262+
263+ property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
264+ ? "phone"
265+ : shell.usageScenario
266+ source: {
267+ if(shellMode === "greeter") {
268+ return "Stages/ShimStage.qml"
269+ } else if (applicationsDisplayLoader.usageScenario === "phone") {
270+ return "Stages/PhoneStage.qml";
271+ } else if (applicationsDisplayLoader.usageScenario === "tablet") {
272+ return "Stages/TabletStage.qml";
273+ } else {
274+ return "Stages/DesktopStage.qml";
275+ }
276+ }
237277
238278 property bool interactive: tutorial.spreadEnabled
239279 && !greeter.shown
@@ -276,7 +316,12 @@ Item {
276316 }
277317 Binding {
278318 target: applicationsDisplayLoader.item
279− property: "orientation"
319+ property: "shellOrientationAngle"
320+ value: shell.orientationAngle
321+ }
322+ Binding {
323+ target: applicationsDisplayLoader.item
324+ property: "shellOrientation"
280325 value: shell.orientation
281326 }
282327 Binding {
@@ -284,6 +329,31 @@ Item {
284329 property: "background"
285330 value: shell.background
286331 }
332+ Binding {
333+ target: applicationsDisplayLoader.item
334+ property: "shellPrimaryOrientation"
335+ value: shell.primaryOrientation
336+ }
337+ Binding {
338+ target: applicationsDisplayLoader.item
339+ property: "nativeOrientation"
340+ value: shell.nativeOrientation
341+ }
342+ Binding {
343+ target: applicationsDisplayLoader.item
344+ property: "nativeWidth"
345+ value: shell.nativeWidth
346+ }
347+ Binding {
348+ target: applicationsDisplayLoader.item
349+ property: "nativeHeight"
350+ value: shell.nativeHeight
351+ }
352+ Binding {
353+ target: applicationsDisplayLoader.item
354+ property: "beingResized"
355+ value: shell.beingResized
356+ }
287357 }
288358
289359 Tutorial {
@@ -348,50 +418,59 @@ Item {
348418 }
349419 }
350420
351− Greeter {
352− id: greeter
353− objectName: "greeter"
354−
355− hides: [launcher, panel.indicators]
356− tabletMode: shell.sideStageEnabled
357− launcherOffset: launcher.progress
358− forcedUnlock: tutorial.running
359− background: shell.background
360−
421+ Loader {
422+ id: greeterLoader
361423 anchors.fill: parent
362424 anchors.topMargin: panel.panelHeight
425+ sourceComponent: shellMode != "shell" ? integratedGreeter :
426+ Qt.createComponent(Qt.resolvedUrl("Greeter/ShimGreeter.qml"));
427+ onLoaded: {
428+ item.objectName = "greeter"
429+ }
430+ }
363431
364− // avoid overlapping with Launcher's edge drag area
365− // FIXME: Fix TouchRegistry & friends and remove this workaround
366− // Issue involves launcher's DDA getting disabled on a long
367− // left-edge drag
368− dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
432+ Component {
433+ id: integratedGreeter
434+ Greeter {
369435
370− onSessionStarted: {
371− launcher.hide();
372− }
436+ hides: [launcher, panel.indicators]
437+ tabletMode: shell.sideStageEnabled
438+ launcherOffset: launcher.progress
439+ forcedUnlock: tutorial.running
440+ background: shell.background
373441
374− onTease: {
375− if (!tutorial.running) {
376− launcher.tease();
442+ // avoid overlapping with Launcher's edge drag area
443+ // FIXME: Fix TouchRegistry & friends and remove this workaround
444+ // Issue involves launcher's DDA getting disabled on a long
445+ // left-edge drag
446+ dragHandleLeftMargin: launcher.available ? launcher.dragAreaWidth + 1 : 0
447+
448+ onSessionStarted: {
449+ launcher.hide();
377450 }
378− }
379451
380− onEmergencyCall: startLockedApp("dialer-app")
452+ onTease: {
453+ if (!tutorial.running) {
454+ launcher.tease();
455+ }
456+ }
381457
382− Timer {
383− // See powerConnection for why this is useful
384− id: showGreeterDelayed
385− interval: 1
386− onTriggered: {
387− greeter.forceShow();
458+ onEmergencyCall: startLockedApp("dialer-app")
459+
460+ Binding {
461+ target: ApplicationManager
462+ property: "suspended"
463+ value: greeter.shown
388464 }
389465 }
466+ }
390467
391− Binding {
392− target: ApplicationManager
393− property: "suspended"
394− value: greeter.shown
468+ Timer {
469+ // See powerConnection for why this is useful
470+ id: showGreeterDelayed
471+ interval: 1
472+ onTriggered: {
473+ greeter.forceShow();
395474 }
396475 }
397476
@@ -480,20 +559,18 @@ Item {
480559 expandedPanelHeight: units.gu(7)
481560
482561 indicatorsModel: Indicators.IndicatorsModel {
483− // TODO: This should be sourced by device type (e.g. "desktop", "tablet", "phone"...)
484− profile: indicatorProfile
485− Component.onCompleted: load()
562+ // tablet and phone both use the same profile
563+ profile: shell.usageScenario === "desktop" ? "desktop" : "phone"
564+ Component.onCompleted: load();
486565 }
487566 }
567+
488568 callHint {
489569 greeterShown: greeter.shown
490570 }
491571
492− property bool topmostApplicationIsFullscreen:
493− ApplicationManager.focusedApplicationId &&
494− ApplicationManager.findApplication(ApplicationManager.focusedApplicationId).fullscreen
495−
496− fullscreenMode: (topmostApplicationIsFullscreen && !LightDM.Greeter.active && launcher.progress == 0)
572+ property bool mainAppIsFullscreen: shell.mainApp && shell.mainApp.fullscreen
573+ fullscreenMode: (mainAppIsFullscreen && !LightDM.Greeter.active && launcher.progress == 0)
497574 || greeter.hasLockedApp
498575 }
499576
@@ -511,7 +588,7 @@ Item {
511588 available: tutorial.launcherEnabled
512589 && (!greeter.locked || AccountsService.enableLauncherWhileLocked)
513590 && !greeter.hasLockedApp
514− inverted: usageModeSettings.usageMode === "Staged"
591+ inverted: shell.usageScenario !== "desktop"
515592 shadeBackground: !tutorial.running
516593
517594 onShowDashHome: showHome()
@@ -553,7 +630,7 @@ Item {
553630 Rectangle {
554631 id: modalNotificationBackground
555632
556− visible: notifications.useModal && (notifications.state == "narrow")
633+ visible: notifications.useModal
557634 color: "#000000"
558635 anchors.fill: parent
559636 opacity: 0.9
@@ -598,6 +675,7 @@ Item {
598675
599676 Dialogs {
600677 id: dialogs
678+ objectName: "dialogs"
601679 anchors.fill: parent
602680 z: overlay.z + 10
603681 onPowerOffClicked: {
qml/Stages/ApplicationWindow.qml+53−18
@@ -1,5 +1,5 @@
11/*
2− * Copyright 2014 Canonical Ltd.
2+ * Copyright 2014-2015 Canonical Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU Lesser General Public License as published by
@@ -25,10 +25,11 @@ FocusScope {
2525 // to be read from outside
2626 readonly property bool fullscreen: application ? application.fullscreen : false
2727 property alias interactive: sessionContainer.interactive
28+ property bool orientationChangesEnabled: d.supportsSurfaceResize ? d.surfaceOldEnoughToBeResized : true
2829
2930 // to be set from outside
3031 property QtObject application
31− property int orientation
32+ property int surfaceOrientationAngle
3233
3334 function removeScreenshot() {
3435 sessionGrabber.removeScreenshot();
@@ -68,6 +69,15 @@ FocusScope {
6869 // Remove this when possible
6970 property bool surfaceInitialized: false
7071
72+ property bool supportsSurfaceResize:
73+ application &&
74+ ((application.supportedOrientations & Qt.PortraitOrientation)
75+ || (application.supportedOrientations & Qt.InvertedPortraitOrientation))
76+ &&
77+ ((application.supportedOrientations & Qt.LandscapeOrientation)
78+ || (application.supportedOrientations & Qt.InvertedLandscapeOrientation))
79+
80+ property bool surfaceOldEnoughToBeResized: false
7181 }
7282
7383 Timer {
@@ -76,6 +86,12 @@ FocusScope {
7686 onTriggered: { if (sessionContainer.surface) {d.surfaceInitialized = true;} }
7787 }
7888
89+ Timer {
90+ id: surfaceIsOldTimer
91+ interval: 1000
92+ onTriggered: { if (stateGroup.state === "surface") { d.surfaceOldEnoughToBeResized = true; } }
93+ }
94+
7995 Loader {
8096 id: splashLoader
8197 objectName: "splashLoader"
@@ -137,7 +153,8 @@ FocusScope {
137153 // A fake application might not even have a session property.
138154 session: application && application.session ? application.session : null
139155 anchors.fill: parent
140− orientation: root.orientation
156+
157+ surfaceOrientationAngle: application && application.rotatesWindowContents ? root.surfaceOrientationAngle : 0
141158
142159 onSurfaceChanged: {
143160 if (sessionContainer.surface) {
@@ -203,15 +220,21 @@ FocusScope {
203220 UbuntuNumberAnimation { target: sessionContainer.surfaceContainer; property: "opacity";
204221 from: 0.0; to: 1.0
205222 duration: UbuntuAnimation.BriskDuration }
206− PropertyAction { target: splashLoader; property: "active"; value: false }
223+ ScriptAction { script: {
224+ splashLoader.active = false;
225+ surfaceIsOldTimer.start();
226+ } }
207227 }
208228 },
209229 Transition {
210230 from: "surface"; to: "splashScreen"
211231 SequentialAnimation {
212− PropertyAction { target: splashLoader; property: "active"; value: true }
213− PropertyAction { target: sessionContainer.surfaceContainer
214− property: "visible"; value: true }
232+ ScriptAction { script: {
233+ surfaceIsOldTimer.stop();
234+ d.surfaceOldEnoughToBeResized = false;
235+ splashLoader.active = true;
236+ sessionContainer.surfaceContainer.visible = true;
237+ } }
215238 UbuntuNumberAnimation { target: splashLoader; property: "opacity";
216239 from: 0.0; to: 1.0
217240 duration: UbuntuAnimation.BriskDuration }
@@ -222,14 +245,18 @@ FocusScope {
222245 Transition {
223246 from: "surface"; to: "screenshot"
224247 SequentialAnimation {
225− PropertyAction { target: screenshotImage
226− property: "visible"; value: true }
248+ ScriptAction { script: {
249+ surfaceIsOldTimer.stop();
250+ d.surfaceOldEnoughToBeResized = false;
251+ screenshotImage.visible = true;
252+ } }
227253 UbuntuNumberAnimation { target: screenshotImage; property: "opacity";
228254 from: 0.0; to: 1.0
229255 duration: UbuntuAnimation.BriskDuration }
230− PropertyAction { target: sessionContainer.surfaceContainer
231− property: "visible"; value: false }
232− ScriptAction { script: { if (sessionContainer.session) { sessionContainer.session.release(); } } }
256+ ScriptAction { script: {
257+ sessionContainer.surfaceContainer.visible = false;
258+ if (sessionContainer.session) { sessionContainer.session.release(); }
259+ } }
233260 }
234261 },
235262 Transition {
@@ -240,8 +267,11 @@ FocusScope {
240267 UbuntuNumberAnimation { target: screenshotImage; property: "opacity";
241268 from: 1.0; to: 0.0
242269 duration: UbuntuAnimation.BriskDuration }
243− PropertyAction { target: screenshotImage; property: "visible"; value: false }
244− PropertyAction { target: screenshotImage; property: "source"; value: "" }
270+ ScriptAction { script: {
271+ screenshotImage.visible = false;
272+ screenshotImage.source = "";
273+ surfaceIsOldTimer.start();
274+ } }
245275 }
246276 },
247277 Transition {
@@ -257,10 +287,12 @@ FocusScope {
257287 },
258288 Transition {
259289 from: "surface"; to: "void"
260− SequentialAnimation {
261− PropertyAction { target: sessionContainer.surfaceContainer; property: "visible"; value: false }
262− ScriptAction { script: { if (sessionContainer.session) { sessionContainer.session.release(); } } }
263− }
290+ ScriptAction { script: {
291+ surfaceIsOldTimer.stop();
292+ d.surfaceOldEnoughToBeResized = false;
293+ sessionContainer.surfaceContainer.visible = false;
294+ if (sessionContainer.session) { sessionContainer.session.release(); }
295+ } }
264296 },
265297 Transition {
266298 from: "void"; to: "surface"
@@ -270,6 +302,9 @@ FocusScope {
270302 UbuntuNumberAnimation { target: sessionContainer.surfaceContainer; property: "opacity";
271303 from: 0.0; to: 1.0
272304 duration: UbuntuAnimation.BriskDuration }
305+ ScriptAction { script: {
306+ surfaceIsOldTimer.start();
307+ } }
273308 }
274309 }
275310 ]
qml/Stages/DecoratedWindow.qml+1
@@ -42,6 +42,7 @@ FocusScope {
4242
4343 WindowDecoration {
4444 id: decoration
45+ objectName: application ? "appWindowDecoration_" + application.appId : "appWindowDecoration_null"
4546 anchors { left: parent.left; top: parent.top; right: parent.right }
4647 height: units.gu(3)
4748 title: model.name
qml/Stages/DesktopStage.qml+43−14
@@ -20,16 +20,36 @@ import QtQuick 2.3
2020import Ubuntu.Components 1.1
2121import Unity.Application 0.1
2222import "../Components/PanelState"
23−import Utils 0.1
2423
25−FocusScope {
24+Rectangle {
2625 id: root
2726
2827 anchors.fill: parent
2928
30− property alias background: wallpaper.source
29+ // Controls to be set from outside
30+ property int dragAreaWidth // just to comply with the interface shared between stages
31+ property real maximizedAppTopMargin
32+ property bool interactive
33+ property bool spreadEnabled // just to comply with the interface shared between stages
34+ property real inverseProgress: 0 // just to comply with the interface shared between stages
35+ property int shellOrientationAngle: 0
36+ property int shellOrientation
37+ property int shellPrimaryOrientation
38+ property int nativeOrientation
39+ property bool beingResized: false
40+
41+ // functions to be called from outside
42+ function updateFocusedAppOrientation() { /* TODO */ }
43+ function updateFocusedAppOrientationAnimated() { /* TODO */}
44+
45+ // To be read from outside
46+ readonly property var mainApp: ApplicationManager.focusedApplicationId
47+ ? ApplicationManager.findApplication(ApplicationManager.focusedApplicationId)
48+ : null
49+ property int mainAppWindowOrientationAngle: 0
50+ readonly property bool orientationChangesEnabled: false
3151
32− property var windowStateStorage: WindowStateStorage
52+ property alias background: wallpaper.source
3353
3454 CrossFadeImage {
3555 id: wallpaper
@@ -58,7 +78,16 @@ FocusScope {
5878 id: priv
5979
6080 readonly property string focusedAppId: ApplicationManager.focusedApplicationId
61− readonly property var focusedAppDelegate: focusedAppId ? appRepeater.itemAt(indexOf(focusedAppId)) : null
81+ readonly property var focusedAppDelegate: {
82+ var index = indexOf(focusedAppId);
83+ return index >= 0 && index < appRepeater.count ? appRepeater.itemAt(index) : null
84+ }
85+
86+ onFocusedAppDelegateChanged: {
87+ if (focusedAppDelegate) {
88+ focusedAppDelegate.focus = true;
89+ }
90+ }
6291
6392 function indexOf(appId) {
6493 for (var i = 0; i < ApplicationManager.count; i++) {
@@ -89,13 +118,19 @@ FocusScope {
89118 id: appRepeater
90119 model: ApplicationManager
91120
92− delegate: Item {
121+ delegate: FocusScope {
93122 id: appDelegate
94123 z: ApplicationManager.count - index
95124 y: units.gu(3)
96125 width: units.gu(60)
97126 height: units.gu(50)
98127
128+ onFocusChanged: {
129+ if (focus) {
130+ ApplicationManager.requestFocusApplication(model.appId);
131+ }
132+ }
133+
99134 readonly property int minWidth: units.gu(10)
100135 readonly property int minHeight: units.gu(10)
101136
@@ -119,14 +154,13 @@ FocusScope {
119154 ]
120155
121156 WindowMoveResizeArea {
122− windowStateStorage: root.windowStateStorage
123157 target: appDelegate
124158 minWidth: appDelegate.minWidth
125159 minHeight: appDelegate.minHeight
126160 resizeHandleWidth: units.gu(0.5)
127161 windowId: model.appId // FIXME: Change this to point to windowId once we have such a thing
128162
129− onPressed: decoratedWindow.focus = true;
163+ onPressed: appDelegate.focus = true;
130164 }
131165
132166 DecoratedWindow {
@@ -135,12 +169,7 @@ FocusScope {
135169 anchors.fill: parent
136170 application: ApplicationManager.get(index)
137171 active: ApplicationManager.focusedApplicationId === model.appId
138−
139− onFocusChanged: {
140− if (focus) {
141− ApplicationManager.requestFocusApplication(model.appId);
142− }
143− }
172+ focus: true
144173
145174 onClose: ApplicationManager.stopApplication(model.appId)
146175 onMaximize: appDelegate.state = (appDelegate.state == "maximized" ? "normal" : "maximized")
qml/Stages/OrientationChangeAnimation.qmlnew file+241
@@ -0,0 +1,241 @@
1+/*
2+ * Copyright 2015 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.3
18+
19+QtObject {
20+ id: root
21+
22+ // to be set from outside
23+ property Item spreadDelegate
24+ property Item background
25+ property Item window
26+ property Item screenshot
27+
28+ function start() {
29+ if (window.orientationAngle === 0) {
30+ if (spreadDelegate.shellOrientationAngle === 90) {
31+ chosenAnimation = simple90Animation;
32+ } else if (spreadDelegate.shellOrientationAngle === 180) {
33+ chosenAnimation = halfLoopAnimation;
34+ } else if (spreadDelegate.shellOrientationAngle === 270) {
35+ chosenAnimation = moving90Animation;
36+ } else {
37+ chosenAnimation = null;
38+ }
39+ } else if (window.orientationAngle === 90) {
40+ if (spreadDelegate.shellOrientationAngle === 0) {
41+ chosenAnimation = simple90Animation;
42+ } else if (spreadDelegate.shellOrientationAngle === 180) {
43+ chosenAnimation = moving90Animation;
44+ } else if (spreadDelegate.shellOrientationAngle === 270) {
45+ chosenAnimation = halfLoopAnimation;
46+ } else {
47+ chosenAnimation = null;
48+ }
49+ } else if (window.orientationAngle === 180) {
50+ if (spreadDelegate.shellOrientationAngle === 0) {
51+ chosenAnimation = halfLoopAnimation;
52+ } else if (spreadDelegate.shellOrientationAngle === 90) {
53+ chosenAnimation = moving90Animation;
54+ } else if (spreadDelegate.shellOrientationAngle === 270) {
55+ chosenAnimation = simple90Animation;
56+ } else {
57+ chosenAnimation = null;
58+ }
59+ } else if (window.orientationAngle === 270) {
60+ if (spreadDelegate.shellOrientationAngle === 0) {
61+ chosenAnimation = moving90Animation;
62+ } else if (spreadDelegate.shellOrientationAngle === 90) {
63+ chosenAnimation = halfLoopAnimation;
64+ } else if (spreadDelegate.shellOrientationAngle === 180) {
65+ chosenAnimation = simple90Animation;
66+ } else {
67+ chosenAnimation = null;
68+ }
69+ }
70+
71+ if (chosenAnimation)
72+ chosenAnimation.start();
73+ }
74+
75+ // to be read from outside
76+ property bool running: chosenAnimation ? chosenAnimation.running : false
77+
78+ property int duration: 450
79+ property int easingType: Easing.InOutCubic
80+
81+ property int shortestDimension: spreadDelegate.width < spreadDelegate.height
82+ ? spreadDelegate.width : spreadDelegate.height
83+ property int longestDimension: spreadDelegate.width > spreadDelegate.height
84+ ? spreadDelegate.width : spreadDelegate.height
85+ property string longestAxis: spreadDelegate.width > spreadDelegate.height ? "x" : "y"
86+
87+ property QtObject chosenAnimation
88+
89+ function setup90Animation() {
90+ background.visible = true;
91+
92+ screenshot.width = window.width;
93+ screenshot.height = window.height;
94+ screenshot.window.anchors.topMargin = window.window.anchors.topMargin;
95+ screenshot.take();
96+ screenshot.transformOriginX = root.shortestDimension / 2;
97+ screenshot.transformOriginY = root.shortestDimension / 2;
98+ screenshot.visible = true;
99+
100+ window.rotation = 0;
101+ window.width = spreadDelegate.width;
102+ window.height = spreadDelegate.height;
103+ window.transformOriginX = root.shortestDimension / 2;
104+ window.transformOriginY = root.shortestDimension / 2;
105+ }
106+
107+ function tearDown90Animation() {
108+ window.orientationAngle = spreadDelegate.shellOrientationAngle;
109+ screenshot.discard();
110+ screenshot.visible = false;
111+ background.visible = false;
112+ }
113+
114+ property QtObject simple90Animation: SequentialAnimation {
115+ id: simple90Animation
116+
117+ ScriptAction { script: setup90Animation() }
118+ ParallelAnimation {
119+ RotationAnimation {
120+ target: root.window
121+ duration: root.duration
122+ easing.type: root.easingType
123+ from: window.orientationAngle - spreadDelegate.shellOrientationAngle
124+ to: 0
125+ property: "transformRotationAngle"
126+ }
127+ RotationAnimation {
128+ target: root.screenshot
129+ duration: root.duration
130+ easing.type: root.easingType
131+ from: window.orientationAngle - spreadDelegate.shellOrientationAngle
132+ to: 0
133+ property: "transformRotationAngle"
134+ }
135+ NumberAnimation {
136+ target: root.screenshot
137+ duration: root.duration
138+ easing.type: root.easingType
139+ property: "opacity"
140+ from: 1.0
141+ to: 0.0
142+ }
143+ NumberAnimation {
144+ target: root.window
145+ duration: root.duration
146+ easing.type: root.easingType
147+ property: "opacity"
148+ from: 0.0
149+ to: 1.0
150+ }
151+ }
152+ ScriptAction { script: tearDown90Animation() }
153+ }
154+
155+ property QtObject moving90Animation: SequentialAnimation {
156+ id: moving90Animation
157+
158+ ScriptAction { script: setup90Animation() }
159+ ParallelAnimation {
160+ RotationAnimation {
161+ target: root.window
162+ duration: root.duration
163+ easing.type: root.easingType
164+ direction: RotationAnimation.Shortest
165+ from: window.orientationAngle - spreadDelegate.shellOrientationAngle
166+ to: 0
167+ property: "transformRotationAngle"
168+ }
169+ RotationAnimation {
170+ target: root.screenshot
171+ duration: root.duration
172+ easing.type: root.easingType
173+ direction: RotationAnimation.Shortest
174+ from: window.orientationAngle - spreadDelegate.shellOrientationAngle
175+ to: 0
176+ property: "transformRotationAngle"
177+ }
178+ NumberAnimation {
179+ target: root.screenshot
180+ duration: root.duration
181+ easing.type: root.easingType
182+ property: "opacity"
183+ from: 1.0
184+ to: 0.0
185+ }
186+ NumberAnimation {
187+ target: root.window
188+ duration: root.duration
189+ easing.type: root.easingType
190+ property: "opacity"
191+ from: 0.0
192+ to: 1.0
193+ }
194+ NumberAnimation {
195+ target: root.window
196+ duration: root.duration
197+ easing.type: root.easingType
198+ property: root.longestAxis
199+ from: root.longestDimension - root.shortestDimension
200+ to: 0
201+ }
202+ NumberAnimation {
203+ target: root.screenshot
204+ duration: root.duration
205+ easing.type: root.easingType
206+ property: root.longestAxis
207+ from: root.longestDimension - root.shortestDimension
208+ to: 0
209+ }
210+ }
211+ ScriptAction { script: tearDown90Animation() }
212+ }
213+
214+ property QtObject halfLoopAnimation: SequentialAnimation {
215+ id: halfLoopAnimation
216+
217+ ScriptAction { script: {
218+ background.visible = true;
219+
220+ window.rotation = 0;
221+ window.width = spreadDelegate.width;
222+ window.height = spreadDelegate.height;
223+ window.transformOriginX = window.width / 2
224+ window.transformOriginY = window.height / 2
225+ } }
226+ ParallelAnimation {
227+ RotationAnimation {
228+ target: root.window
229+ duration: root.duration
230+ easing.type: root.easingType
231+ from: window.orientationAngle - spreadDelegate.shellOrientationAngle
232+ to: 0
233+ property: "transformRotationAngle"
234+ }
235+ }
236+ ScriptAction { script: {
237+ window.orientationAngle = spreadDelegate.shellOrientationAngle;
238+ background.visible = false;
239+ } }
240+ }
241+}
qml/Stages/PhoneStage.qml+171−82
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 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
@@ -31,12 +31,64 @@ Rectangle {
3131 property bool interactive
3232 property bool spreadEnabled: true // If false, animations and right edge will be disabled
3333 property real inverseProgress: 0 // This is the progress for left edge drags, in pixels.
34− property int orientation: Qt.PortraitOrientation
3534 property QtObject applicationManager: ApplicationManager
3635 property bool focusFirstApp: true // If false, focused app will appear on right edge like other apps
3736 property bool altTabEnabled: true
3837 property real startScale: 1.1
3938 property real endScale: 0.7
39+ property int shellOrientationAngle: 0
40+ property int shellOrientation
41+ property int shellPrimaryOrientation
42+ property int nativeOrientation
43+ property real nativeWidth
44+ property real nativeHeight
45+ property bool beingResized: false
46+ onBeingResizedChanged: {
47+ if (beingResized) {
48+ // Brace yourselves for impact!
49+ spreadView.selectedIndex = -1;
50+ spreadView.phase = 0;
51+ spreadView.contentX = -spreadView.shift;
52+ }
53+ }
54+ function updateFocusedAppOrientation() {
55+ if (spreadRepeater.count > 0) {
56+ spreadRepeater.itemAt(0).matchShellOrientation();
57+ }
58+
59+ for (var i = 1; i < spreadRepeater.count; ++i) {
60+
61+ var spreadDelegate = spreadRepeater.itemAt(i);
62+
63+ var delta = spreadDelegate.appWindowOrientationAngle - root.shellOrientationAngle;
64+ if (delta < 0) { delta += 360; }
65+ delta = delta % 360;
66+
67+ var supportedOrientations = spreadDelegate.application.supportedOrientations;
68+ if (supportedOrientations === Qt.PrimaryOrientation) {
69+ supportedOrientations = spreadDelegate.shellPrimaryOrientation;
70+ }
71+
72+ if (delta === 180 && (supportedOrientations & spreadDelegate.shellOrientation)) {
73+ spreadDelegate.matchShellOrientation();
74+ }
75+ }
76+ }
77+ function updateFocusedAppOrientationAnimated() {
78+ if (spreadRepeater.count > 0) {
79+ spreadRepeater.itemAt(0).animateToShellOrientation();
80+ }
81+ }
82+
83+ // To be read from outside
84+ readonly property var mainApp: applicationManager.focusedApplicationId
85+ ? applicationManager.findApplication(applicationManager.focusedApplicationId)
86+ : null
87+ property int mainAppWindowOrientationAngle: 0
88+ readonly property bool orientationChangesEnabled: priv.focusedAppOrientationChangesEnabled
89+ && !priv.focusedAppDelegateIsDislocated
90+ && !(priv.focusedAppDelegate && priv.focusedAppDelegate.xBehavior.running)
91+ && spreadView.phase === 0
4092
4193 // How far left the stage has been dragged
4294 readonly property real dragProgress: spreadRepeater.count > 0 ? -spreadRepeater.itemAt(0).xTranslate : 0
@@ -54,12 +106,6 @@ Rectangle {
54106 spreadView.snapTo(priv.indexOf(appId));
55107 }
56108
57− onWidthChanged: {
58− spreadView.selectedIndex = -1;
59− spreadView.phase = 0;
60− spreadView.contentX = -spreadView.shift;
61− }
62−
63109 onInverseProgressChanged: {
64110 // This can't be a simple binding because that would be triggered after this handler
65111 // while we need it active before doing the anition left/right
@@ -73,6 +119,20 @@ Rectangle {
73119 priv.oldInverseProgress = inverseProgress;
74120 }
75121
122+ // <FIXME-contentX> See rationale in the next comment with this tag
123+ onWidthChanged: {
124+ if (!root.beingResized) {
125+ // we're being resized without a warning (ie, the corresponding property wasn't set
126+ root.beingResized = true;
127+ beingResizedTimer.start();
128+ }
129+ }
130+ Timer {
131+ id: beingResizedTimer
132+ interval: 100
133+ onTriggered: { root.beingResized = false; }
134+ }
135+
76136 Connections {
77137 target: applicationManager
78138
@@ -115,45 +175,57 @@ Rectangle {
115175 QtObject {
116176 id: priv
117177
178+ property string focusedAppId: root.applicationManager.focusedApplicationId
179+ property bool focusedAppOrientationChangesEnabled: false
118180 readonly property int firstSpreadIndex: root.focusFirstApp ? 1 : 0
119− property string focusedAppId: applicationManager.focusedApplicationId
120− property var focusedApplication: applicationManager.findApplication(focusedAppId)
121− property var focusedAppDelegate: null
181+ readonly property var focusedAppDelegate: {
182+ var index = indexOf(focusedAppId);
183+ return index >= 0 && index < spreadRepeater.count ? spreadRepeater.itemAt(index) : null
184+ }
122185
123186 property real oldInverseProgress: 0
124187 property bool animateX: false
125188
126− onFocusedAppIdChanged: focusedAppDelegate = spreadRepeater.itemAt(0);
127−
128189 onFocusedAppDelegateChanged: {
129190 if (focusedAppDelegate) {
130191 focusedAppDelegate.focus = true;
131192 }
132193 }
133194
195+ property bool focusedAppDelegateIsDislocated: focusedAppDelegate && focusedAppDelegate.x !== 0
196+
134197 function indexOf(appId) {
135− for (var i = 0; i < applicationManager.count; i++) {
136− if (applicationManager.get(i).appId == appId) {
198+ for (var i = 0; i < root.applicationManager.count; i++) {
199+ if (root.applicationManager.get(i).appId == appId) {
137200 return i;
138201 }
139202 }
140203 return -1;
141204 }
142205
206+ // Is more stable than "spreadView.shiftedContentX === 0" as it filters out noise caused by
207+ // Flickable.contentX changing due to resizes.
208+ property bool fullyShowingFocusedApp: true
209+ }
210+ Timer {
211+ id: fullyShowingFocusedAppUpdateTimer
212+ interval: 100
213+ onTriggered: {
214+ priv.fullyShowingFocusedApp = spreadView.shiftedContentX === 0;
215+ }
143216 }
144217
145218 Flickable {
146219 id: spreadView
147220 objectName: "spreadView"
148221 anchors.fill: parent
149− interactive: (spreadDragArea.status == DirectionalDragArea.Recognized || phase > 1)
150− && draggedDelegateCount === 0
222+ interactive: (spreadDragArea.dragging || phase > 1) && draggedDelegateCount === 0
151223 contentWidth: spreadRow.width - shift
152224 contentX: -shift
153225
154226 // This indicates when the spreadView is active. That means, all the animations
155227 // are activated and tiles need to line up for the spread.
156− readonly property bool active: shiftedContentX > 0 || spreadDragArea.status === DirectionalDragArea.Recognized || !root.focusFirstApp
228+ readonly property bool active: shiftedContentX > 0 || spreadDragArea.dragging || !root.focusFirstApp
157229
158230 // The flickable needs to fill the screen in order to get touch events all over.
159231 // However, we don't want to the user to be able to scroll back all the way. For
@@ -188,9 +260,25 @@ Rectangle {
188260 property int draggedDelegateCount: 0
189261 property int closingIndex: -1
190262
191− property bool focusChanging: false
263+ // <FIXME-contentX> Workaround Flickable's behavior of bringing contentX back between valid boundaries
264+ // when resized. The proper way to fix this is refactoring PhoneStage so that it doesn't
265+ // rely on having Flickable.contentX keeping an out-of-bounds value when it's set programatically
266+ // (as opposed to having contentX reaching an out-of-bounds value through dragging, which will trigger
267+ // the Flickable.boundsBehavior upon release).
268+ onContentXChanged: { forceItToRemainStillIfBeingResized(); }
269+ onShiftChanged: { forceItToRemainStillIfBeingResized(); }
270+ function forceItToRemainStillIfBeingResized() {
271+ if (root.beingResized && contentX != -spreadView.shift) {
272+ contentX = -spreadView.shift;
273+ }
274+ }
192275
193276 onShiftedContentXChanged: {
277+ if (root.beingResized) {
278+ // Flickabe.contentX wiggles during resizes. Don't react to it.
279+ return;
280+ }
281+
194282 switch (phase) {
195283 case 0:
196284 if (shiftedContentX > width * positionMarker2) {
@@ -205,6 +293,7 @@ Rectangle {
205293 }
206294 break;
207295 }
296+ fullyShowingFocusedAppUpdateTimer.restart();
208297 }
209298
210299 function snap() {
@@ -229,7 +318,7 @@ Rectangle {
229318 snapAnimation.start();
230319 return;
231320 }
232− if (applicationManager.count <= index) {
321+ if (root.applicationManager.count <= index) {
233322 // In case we're trying to snap to some non existing app, lets snap back to the first one
234323 index = 0;
235324 }
@@ -266,7 +355,7 @@ Rectangle {
266355 ScriptAction {
267356 script: {
268357 if (spreadView.selectedIndex >= 0) {
269− applicationManager.focusApplication(applicationManager.get(spreadView.selectedIndex).appId);
358+ root.applicationManager.focusApplication(root.applicationManager.get(spreadView.selectedIndex).appId);
270359
271360 spreadView.selectedIndex = -1;
272361 spreadView.phase = 0;
@@ -281,7 +370,7 @@ Rectangle {
281370 // This width controls how much the spread can be flicked left/right. It's composed of:
282371 // tileDistance * app count (with a minimum of 3 apps, in order to also allow moving 1 and 2 apps a bit)
283372 // + some constant value (still scales with the screen width) which looks good and somewhat fills the screen
284− width: Math.max(3, applicationManager.count) * spreadView.tileDistance + (spreadView.width - spreadView.tileDistance) * 1.5
373+ width: Math.max(3, root.applicationManager.count) * spreadView.tileDistance + (spreadView.width - spreadView.tileDistance) * 1.5
285374 height: parent.height
286375 Behavior on width {
287376 enabled: spreadView.closingIndex >= 0
@@ -304,7 +393,7 @@ Rectangle {
304393 Repeater {
305394 id: spreadRepeater
306395 objectName: "spreadRepeater"
307− model: applicationManager
396+ model: root.applicationManager
308397 delegate: TransformedSpreadDelegate {
309398 id: appDelegate
310399 objectName: "appDelegate" + index
@@ -319,11 +408,10 @@ Rectangle {
319408 selected: spreadView.selectedIndex == index
320409 otherSelected: spreadView.selectedIndex >= 0 && !selected
321410 interactive: !spreadView.interactive && spreadView.phase === 0
322− && spreadView.shiftedContentX === 0 && root.interactive && isFocused
411+ && priv.fullyShowingFocusedApp && root.interactive && isFocused
323412 swipeToCloseEnabled: spreadView.interactive && root.interactive && !snapAnimation.running
324413 maximizedAppTopMargin: root.maximizedAppTopMargin
325− dropShadow: spreadView.active ||
326− (priv.focusedAppDelegate && priv.focusedAppDelegate.x !== 0)
414+ dropShadow: spreadView.active || priv.focusedAppDelegateIsDislocated
327415 focusFirstApp: root.focusFirstApp
328416
329417 readonly property bool isDash: model.appId == "unity8-dash"
@@ -346,7 +434,7 @@ Rectangle {
346434 return spreadView.width + spreadIndex * spreadView.tileDistance;
347435 }
348436
349− application: applicationManager.get(index)
437+ application: root.applicationManager.get(index)
350438 closeable: !isDash
351439
352440 property real behavioredIndex: index
@@ -362,18 +450,16 @@ Rectangle {
362450 }
363451 }
364452
453+ property var xBehavior: xBehavior
365454 Behavior on x {
455+ id: xBehavior
366456 enabled: root.spreadEnabled &&
367457 !spreadView.active &&
368458 !snapAnimation.running &&
369− priv.animateX
459+ priv.animateX &&
460+ !root.beingResized
370461 UbuntuNumberAnimation {
371462 duration: UbuntuAnimation.BriskDuration
372− onRunningChanged: {
373− if (!running && root.inverseProgress == 0) {
374− spreadView.focusChanging = false;
375− }
376− }
377463 }
378464 }
379465
@@ -413,22 +499,21 @@ Rectangle {
413499 }
414500
415501 // Hiding tiles when their progress is negative or reached the maximum
416− visible: (progress >= 0 && progress < 1.7) ||
417− (isDash && priv.focusedAppDelegate.x !== 0)
502+ visible: (progress >= 0 && progress < 1.7)
503+ || (isDash && priv.focusedAppDelegateIsDislocated)
418504
419− Binding {
420− target: appDelegate
421− property: "orientation"
422− when: appDelegate.interactive
423− value: root.orientation
424− }
505+
506+ shellOrientationAngle: root.shellOrientationAngle
507+ shellOrientation: root.shellOrientation
508+ shellPrimaryOrientation: root.shellPrimaryOrientation
509+ nativeOrientation: root.nativeOrientation
425510
426511 onClicked: {
427512 if (root.altTabEnabled && spreadView.phase == 2) {
428− if (applicationManager.focusedApplicationId == applicationManager.get(index).appId) {
513+ if (root.applicationManager.focusedApplicationId == root.applicationManager.get(index).appId) {
429514 spreadView.snapTo(index);
430515 } else {
431− applicationManager.requestFocusApplication(applicationManager.get(index).appId);
516+ root.applicationManager.requestFocusApplication(root.applicationManager.get(index).appId);
432517 }
433518 }
434519 }
@@ -443,14 +528,27 @@ Rectangle {
443528
444529 onClosed: {
445530 spreadView.closingIndex = index;
446− applicationManager.stopApplication(applicationManager.get(index).appId);
531+ root.applicationManager.stopApplication(root.applicationManager.get(index).appId);
532+ }
533+
534+ Binding {
535+ target: root
536+ when: index == 0
537+ property: "mainAppWindowOrientationAngle"
538+ value: appWindowOrientationAngle
539+ }
540+ Binding {
541+ target: priv
542+ when: index == 0
543+ property: "focusedAppOrientationChangesEnabled"
544+ value: orientationChangesEnabled
447545 }
448546 }
449547 }
450548 }
451549 }
452550
453− EdgeDragArea {
551+ DirectionalDragArea {
454552 id: spreadDragArea
455553 objectName: "spreadDragArea"
456554 direction: Direction.Leftwards
@@ -462,59 +560,50 @@ Rectangle {
462560 property var gesturePoints: new Array()
463561
464562 onTouchXChanged: {
465− if (!dragging) {
466− // Initial touch. Let's reset the spreadView to the starting position.
467− spreadView.phase = 0;
468− spreadView.contentX = -spreadView.shift;
469− }
470− if (dragging && status == DirectionalDragArea.Recognized) {
563+ if (dragging) {
471564 // Gesture recognized. Let's move the spreadView with the finger
472565 var dragX = Math.min(touchX + width, width); // Prevent dragging rightwards
473566 dragX = -dragX + spreadDragArea.width - spreadView.shift;
474567 // Don't allow dragging further than the animation crossing with phase2's animation
475568 var maxMovement = spreadView.width * spreadView.positionMarker4 - spreadView.shift;
569+
476570 spreadView.contentX = Math.min(dragX, maxMovement);
571+ } else {
572+ // Initial touch. Let's reset the spreadView to the starting position.
573+ spreadView.phase = 0;
574+ spreadView.contentX = -spreadView.shift;
477575 }
478− gesturePoints.push(touchX);
479− }
480−
481− property int previousStatus: -1
482− property int currentStatus: DirectionalDragArea.WaitingForTouch
483576
484− onStatusChanged: {
485− previousStatus = currentStatus;
486− currentStatus = status;
577+ gesturePoints.push(touchX);
487578 }
488579
489580 onDraggingChanged: {
490581 if (dragging) {
491582 // A potential edge-drag gesture has started. Start recording it
492583 gesturePoints = [];
493− return;
494− }
584+ } else {
585+ // Ok. The user released. Find out if it was a one-way movement.
586+ var oneWayFlick = true;
587+ var smallestX = spreadDragArea.width;
588+ for (var i = 0; i < gesturePoints.length; i++) {
589+ if (gesturePoints[i] >= smallestX) {
590+ oneWayFlick = false;
591+ break;
592+ }
593+ smallestX = gesturePoints[i];
594+ }
595+ gesturePoints = [];
495596
496− // Ok. The user released. Find out if it was a one-way movement.
497− var oneWayFlick = true;
498− var smallestX = spreadDragArea.width;
499− for (var i = 0; i < gesturePoints.length; i++) {
500− if (gesturePoints[i] >= smallestX) {
501− oneWayFlick = false;
502− break;
597+ if (oneWayFlick && spreadView.shiftedContentX > units.gu(2) &&
598+ spreadView.shiftedContentX < spreadView.positionMarker1 * spreadView.width) {
599+ // If it was a short one-way movement, do the Alt+Tab switch
600+ // no matter if we didn't cross positionMarker1 yet.
601+ spreadView.snapTo(1);
602+ } else if (!dragging) {
603+ // otherwise snap to the closest snap position we can find
604+ // (might be back to start, to app 1 or to spread)
605+ spreadView.snap();
503606 }
504− smallestX = gesturePoints[i];
505− }
506− gesturePoints = [];
507−
508− if (previousStatus == DirectionalDragArea.Recognized &&
509− oneWayFlick && spreadView.shiftedContentX > units.gu(2) &&
510− spreadView.shiftedContentX < spreadView.positionMarker1 * spreadView.width) {
511− // If it was a short one-way movement, do the Alt+Tab switch
512− // no matter if we didn't cross positionMarker1 yet.
513− spreadView.snapTo(1);
514− } else if (!dragging) {
515− // otherwise snap to the closest snap position we can find
516− // (might be back to start, to app 1 or to spread)
517− spreadView.snap();
518607 }
519608 }
520609 }
qml/Stages/SessionContainer.qml+2−8
@@ -1,5 +1,5 @@
11/*
2− * Copyright 2014 Canonical Ltd.
2+ * Copyright 2014-2015 Canonical Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU Lesser General Public License as published by
@@ -24,14 +24,13 @@ FocusScope {
2424 readonly property var childSessions: session ? session.childSessions : null
2525 readonly property alias surface: _surfaceContainer.surface
2626 property alias interactive: _surfaceContainer.interactive
27− property int orientation
27+ property alias surfaceOrientationAngle: _surfaceContainer.surfaceOrientationAngle
2828
2929 readonly property alias surfaceContainer: _surfaceContainer
3030 SurfaceContainer {
3131 id: _surfaceContainer
3232 anchors.fill: parent
3333 surface: session ? session.surface : null
34− orientation: root.orientation
3534 }
3635
3736 Repeater {
@@ -72,11 +71,6 @@ FocusScope {
7271 target: item; when: item
7372 property: "height"; value: root.height
7473 }
75−
76− Binding {
77− target: item; when: item
78− property: "orientation"; value: root.orientation
79− }
8074 }
8175 }
8276
qml/Stages/ShimStage.qmlnew file+44
@@ -0,0 +1,44 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.3
18+import Ubuntu.Components 0.1
19+
20+Rectangle {
21+ id: shimStage
22+
23+ anchors.fill: parent
24+ color: UbuntuColors.lightAubergine
25+
26+ Text {
27+ id: greeterModeText
28+
29+ anchors.horizontalCenter: parent.horizontalCenter
30+ anchors.top: parent.top
31+ anchors.topMargin: units.gu(10)
32+ font.bold: true
33+ horizontalAlignment: Text.AlignHCenter
34+ text: "Shell is in \"greeter\" mode"
35+ }
36+
37+ Text {
38+ anchors.centerIn: parent
39+ color: UbuntuColors.orange
40+ font.pointSize: units.gu(8)
41+ horizontalAlignment: Text.AlignHCenter
42+ text: "Shim \nStage"
43+ }
44+}
qml/Stages/SpreadDelegate.qml+261−20
@@ -1,5 +1,5 @@
11/*
2− * Copyright 2014 Canonical Ltd.
2+ * Copyright 2014-2015 Canonical Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU Lesser General Public License as published by
@@ -18,6 +18,7 @@
1818 */
1919
2020import QtQuick 2.0
21+import QtQuick.Window 2.0
2122import Ubuntu.Components 1.1
2223import "../Components"
2324
@@ -28,6 +29,9 @@ FocusScope {
2829 readonly property bool dragged: dragArea.moving
2930 signal clicked()
3031 signal closed()
32+ readonly property alias appWindowOrientationAngle: appWindowWithShadow.orientationAngle
33+ readonly property alias appWindowRotation: appWindowWithShadow.rotation
34+ readonly property alias orientationChangesEnabled: appWindow.orientationChangesEnabled
3135
3236 // to be set from outside
3337 property bool interactive: true
@@ -36,10 +40,54 @@ FocusScope {
3640 property alias swipeToCloseEnabled: dragArea.enabled
3741 property bool closeable
3842 property alias application: appWindow.application
39− property int orientation
43+ property int shellOrientationAngle
44+ property int shellOrientation
45+ property int shellPrimaryOrientation
46+ property int nativeOrientation
47+
48+ function matchShellOrientation() {
49+ if (!root.application)
50+ return;
51+ appWindowWithShadow.orientationAngle = root.shellOrientationAngle;
52+ }
53+
54+ function animateToShellOrientation() {
55+ if (!root.application)
56+ return;
57+
58+ if (root.application.rotatesWindowContents) {
59+ appWindowWithShadow.orientationAngle = root.shellOrientationAngle;
60+ } else {
61+ orientationChangeAnimation.start();
62+ }
63+ }
64+
65+ OrientationChangeAnimation {
66+ id: orientationChangeAnimation
67+ objectName: "orientationChangeAnimation"
68+ spreadDelegate: root
69+ background: background
70+ window: appWindowWithShadow
71+ screenshot: appWindowScreenshotWithShadow
72+ }
73+
74+ QtObject {
75+ id: priv
76+ property bool startingUp: true
77+ }
78+
79+ Component.onCompleted: { finishStartUpTimer.start(); }
80+ Timer { id: finishStartUpTimer; interval: 400; onTriggered: priv.startingUp = false }
81+
82+ Rectangle {
83+ id: background
84+ color: "black"
85+ anchors.fill: parent
86+ visible: false
87+ }
4088
4189 Item {
42− objectName: "appWindowWithShadow"
90+ objectName: "displacedAppWindowWithShadow"
4391
4492 readonly property real limit: root.height / 4
4593
@@ -52,27 +100,220 @@ FocusScope {
52100 return k * (1 - Math.pow((k - 1) / k, distance))
53101 }
54102
55− BorderImage {
56− anchors {
57− fill: appWindow
58− margins: -units.gu(2)
103+ Item {
104+ id: appWindowWithShadow
105+ objectName: "appWindowWithShadow"
106+
107+ property int orientationAngle
108+
109+ property real transformRotationAngle: 0
110+ property real transformOriginX
111+ property real transformOriginY
112+
113+ property var window: appWindow
114+
115+ transform: Rotation {
116+ origin.x: appWindowWithShadow.transformOriginX
117+ origin.y: appWindowWithShadow.transformOriginY
118+ axis { x: 0; y: 0; z: 1 }
119+ angle: appWindowWithShadow.transformRotationAngle
120+ }
121+
122+ state: {
123+ if (priv.startingUp) {
124+ return "startingUp";
125+ } else if (root.application && root.application.rotatesWindowContents) {
126+ return "counterRotate";
127+ } else if (orientationChangeAnimation.running) {
128+ return "animatingRotation";
129+ } else {
130+ return "keepSceneRotation";
131+ }
59132 }
60− source: "graphics/dropshadow2gu.sci"
61− opacity: root.dropShadow ? .3 : 0
62− Behavior on opacity { UbuntuNumberAnimation {} }
63− }
64133
65− ApplicationWindow {
66− id: appWindow
67− objectName: application ? "appWindow_" + application.appId : "appWindow_null"
68− focus: true
69− anchors {
70− fill: parent
71− topMargin: appWindow.fullscreen ? 0 : maximizedAppTopMargin
134+ // Ensures the given angle is in the form (0,90,180,270)
135+ function normalizeAngle(angle) {
136+ while (angle < 0) {
137+ angle += 360;
138+ }
139+ return angle % 360;
72140 }
73141
74− interactive: root.interactive
75− orientation: root.orientation
142+ states: [
143+ // Sets the initial orientationAngle of the window, when it first slides into view
144+ // (with the splash screen likely being displayed). At that point we just try to
145+ // match shell's current orientation. We need a bit of time in this state as the
146+ // information we need to decide orientationAngle may take a few cycles to
147+ // be set.
148+ State {
149+ name: "startingUp"
150+ PropertyChanges {
151+ target: appWindowWithShadow
152+ restoreEntryValues: false
153+ orientationAngle: {
154+ if (!root.application || root.application.rotatesWindowContents) {
155+ return 0;
156+ }
157+ var supportedOrientations = root.application.supportedOrientations;
158+
159+ if (supportedOrientations === Qt.PrimaryOrientation) {
160+ supportedOrientations = root.shellPrimaryOrientation;
161+ }
162+
163+ // If it doesn't support shell's current orientation
164+ // then simply pick some arbitraty one that it does support
165+ var chosenOrientation = 0;
166+ if (supportedOrientations & root.shellOrientation) {
167+ chosenOrientation = root.shellOrientation;
168+ } else if (supportedOrientations & Qt.PortraitOrientation) {
169+ chosenOrientation = Qt.PortraitOrientation;
170+ } else if (supportedOrientations & Qt.LandscapeOrientation) {
171+ chosenOrientation = Qt.LandscapeOrientation;
172+ } else if (supportedOrientations & Qt.InvertedPortraitOrientation) {
173+ chosenOrientation = Qt.InvertedPortraitOrientation;
174+ } else if (supportedOrientations & Qt.InvertedLandscapeOrientation) {
175+ chosenOrientation = Qt.InvertedLandscapeOrientation;
176+ } else {
177+ chosenOrientation = root.shellPrimaryOrientation;
178+ }
179+
180+ return Screen.angleBetween(root.nativeOrientation, chosenOrientation);
181+ }
182+
183+ rotation: normalizeAngle(appWindowWithShadow.orientationAngle - root.shellOrientationAngle)
184+ width: {
185+ if (rotation == 0 || rotation == 180) {
186+ return root.width;
187+ } else {
188+ return root.height;
189+ }
190+ }
191+ height: {
192+ if (rotation == 0 || rotation == 180)
193+ return root.height;
194+ else
195+ return root.width;
196+ }
197+ }
198+ },
199+ // In this state we stick to our currently set orientationAngle, which may change only due
200+ // to calls made to matchShellOrientation() or animateToShellOrientation()
201+ State {
202+ id: keepSceneRotationState
203+ name: "keepSceneRotation"
204+
205+ StateChangeScript { script: {
206+ // break binding
207+ appWindowWithShadow.orientationAngle = appWindowWithShadow.orientationAngle;
208+ } }
209+ PropertyChanges {
210+ target: appWindowWithShadow
211+ restoreEntryValues: false
212+ rotation: normalizeAngle(appWindowWithShadow.orientationAngle - root.shellOrientationAngle)
213+ width: {
214+ if (rotation == 0 || rotation == 180) {
215+ return root.width;
216+ } else {
217+ return root.height;
218+ }
219+ }
220+ height: {
221+ if (rotation == 0 || rotation == 180)
222+ return root.height;
223+ else
224+ return root.width;
225+ }
226+ }
227+ },
228+ // In this state we counteract any shell rotation so that the window, in scene coordinates,
229+ // remains unrotated.
230+ State {
231+ name: "counterRotate"
232+ StateChangeScript { script: {
233+ // break binding
234+ appWindowWithShadow.orientationAngle = appWindowWithShadow.orientationAngle;
235+ } }
236+ PropertyChanges {
237+ target: appWindowWithShadow
238+ width: root.shellOrientationAngle == 0 || root.shellOrientationAngle == 180 ? root.width : root.height
239+ height: root.shellOrientationAngle == 0 || root.shellOrientationAngle == 180 ? root.height : root.width
240+ rotation: normalizeAngle(-root.shellOrientationAngle)
241+ }
242+ PropertyChanges {
243+ target: appWindow
244+ surfaceOrientationAngle: orientationAngle
245+ }
246+ },
247+ State {
248+ name: "animatingRotation"
249+ }
250+ ]
251+
252+ x: (parent.width - width) / 2
253+ y: (parent.height - height) / 2
254+
255+ BorderImage {
256+ anchors {
257+ fill: appWindow
258+ margins: -units.gu(2)
259+ }
260+ source: "graphics/dropshadow2gu.sci"
261+ opacity: root.dropShadow ? .3 : 0
262+ Behavior on opacity { UbuntuNumberAnimation {} }
263+ }
264+
265+ ApplicationWindow {
266+ id: appWindow
267+ objectName: application ? "appWindow_" + application.appId : "appWindow_null"
268+ focus: true
269+ anchors {
270+ fill: parent
271+ topMargin: appWindow.fullscreen || application.rotatesWindowContents
272+ ? 0 : maximizedAppTopMargin
273+ }
274+
275+ interactive: root.interactive
276+ }
277+ }
278+ }
279+
280+ Item {
281+ // mimics appWindowWithShadow. Do the positioning of screenshots of non-fullscreen
282+ // app windows
283+ id: appWindowScreenshotWithShadow
284+ visible: false
285+
286+ property real transformRotationAngle: 0
287+ property real transformOriginX
288+ property real transformOriginY
289+
290+ transform: Rotation {
291+ origin.x: appWindowScreenshotWithShadow.transformOriginX
292+ origin.y: appWindowScreenshotWithShadow.transformOriginY
293+ axis { x: 0; y: 0; z: 1 }
294+ angle: appWindowScreenshotWithShadow.transformRotationAngle
295+ }
296+
297+ property var window: appWindowScreenshot
298+
299+ function take() {
300+ // Format: "image://application/$APP_ID/$CURRENT_TIME_MS"
301+ // eg: "image://application/calculator-app/123456"
302+ var timeMs = new Date().getTime();
303+ appWindowScreenshot.source = "image://application/" + root.application.appId + "/" + timeMs;
304+ }
305+ function discard() {
306+ appWindowScreenshot.source = "";
307+ }
308+
309+ Image {
310+ id: appWindowScreenshot
311+ source: ""
312+
313+ anchors.fill: parent
314+
315+ sourceSize.width: width
316+ sourceSize.height: height
76317 }
77318 }
78319
qml/Stages/SurfaceContainer.qml+2−2
@@ -24,8 +24,8 @@ FocusScope {
2424 objectName: "surfaceContainer"
2525 property Item surface: null
2626 property bool hadSurface: false
27− property int orientation
2827 property bool interactive
28+ property int surfaceOrientationAngle: 0
2929
3030 onSurfaceChanged: {
3131 if (surface) {
@@ -39,10 +39,10 @@ FocusScope {
3939 }
4040 }
4141 Binding { target: surface; property: "anchors.fill"; value: root }
42− Binding { target: surface; property: "orientation"; value: root.orientation }
4342 Binding { target: surface; property: "z"; value: 1 }
4443 Binding { target: surface; property: "enabled"; value: root.interactive; when: surface }
4544 Binding { target: surface; property: "antialiasing"; value: !root.interactive; when: surface }
45+ Binding { target: surface; property: "orientationAngle"; value: root.surfaceOrientationAngle; when: surface }
4646
4747 InputWatcher {
4848 target: root.surface
qml/Stages/TabletStage.qml+177−50
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 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
@@ -28,13 +28,79 @@ Rectangle {
2828 color: "#111111"
2929
3030 // Controls to be set from outside
31− property bool shown: false
32− property bool moving: false
3331 property int dragAreaWidth
3432 property real maximizedAppTopMargin
3533 property bool interactive
34+ property alias beingResized: spreadView.beingResized
35+
36+ property bool spreadEnabled: true // If false, animations and right edge will be disabled
37+
3638 property real inverseProgress: 0 // This is the progress for left edge drags, in pixels.
37− property int orientation: Qt.PortraitOrientation
39+ property int shellOrientationAngle: 0
40+ property int shellOrientation
41+ property int shellPrimaryOrientation
42+ property int nativeOrientation
43+ property real nativeWidth
44+ property real nativeHeight
45+ function updateFocusedAppOrientation() {
46+ var mainStageAppIndex = priv.indexOf(priv.mainStageAppId);
47+ if (mainStageAppIndex >= 0 && mainStageAppIndex < spreadRepeater.count) {
48+ spreadRepeater.itemAt(mainStageAppIndex).matchShellOrientation();
49+ }
50+
51+ for (var i = 0; i < spreadRepeater.count; ++i) {
52+
53+ if (i === mainStageAppIndex) {
54+ continue;
55+ }
56+
57+ var spreadDelegate = spreadRepeater.itemAt(i);
58+
59+ var delta = spreadDelegate.appWindowOrientationAngle - root.shellOrientationAngle;
60+ if (delta < 0) { delta += 360; }
61+ delta = delta % 360;
62+
63+ var supportedOrientations = spreadDelegate.application.supportedOrientations;
64+ if (supportedOrientations === Qt.PrimaryOrientation) {
65+ supportedOrientations = spreadDelegate.shellPrimaryOrientation;
66+ }
67+
68+ if (delta === 180 && (supportedOrientations & spreadDelegate.shellOrientation)) {
69+ spreadDelegate.matchShellOrientation();
70+ }
71+ }
72+ }
73+ function updateFocusedAppOrientationAnimated() {
74+ var mainStageAppIndex = priv.indexOf(priv.mainStageAppId);
75+ if (mainStageAppIndex >= 0 && mainStageAppIndex < spreadRepeater.count) {
76+ spreadRepeater.itemAt(mainStageAppIndex).animateToShellOrientation();
77+ }
78+
79+ if (priv.sideStageAppId) {
80+ var sideStageAppIndex = priv.indexOf(priv.sideStageAppId);
81+ if (sideStageAppIndex >= 0 && sideStageAppIndex < spreadRepeater.count) {
82+ spreadRepeater.itemAt(sideStageAppIndex).matchShellOrientation();
83+ }
84+ }
85+ }
86+
87+ // To be read from outside
88+ property var mainApp: null
89+ property int mainAppWindowOrientationAngle: 0
90+ readonly property bool orientationChangesEnabled: priv.mainAppOrientationChangesEnabled
91+
92+ onWidthChanged: {
93+ spreadView.selectedIndex = -1;
94+ spreadView.phase = 0;
95+ spreadView.contentX = -spreadView.shift;
96+ }
97+
98+ onShellOrientationChanged: {
99+ if (shellOrientation == Qt.PortraitOrientation || shellOrientation == Qt.InvertedPortraitOrientation) {
100+ ApplicationManager.focusApplication(priv.mainStageAppId);
101+ priv.sideStageAppId = "";
102+ }
103+ }
38104
39105 onInverseProgressChanged: {
40106 // This can't be a simple binding because that would be triggered after this handler
@@ -53,7 +119,19 @@ Rectangle {
53119 id: priv
54120
55121 property string focusedAppId: ApplicationManager.focusedApplicationId
122+ readonly property var focusedAppDelegate: {
123+ var index = indexOf(focusedAppId);
124+ return index >= 0 && index < spreadRepeater.count ? spreadRepeater.itemAt(index) : null
125+ }
126+
56127 property string oldFocusedAppId: ""
128+ property bool mainAppOrientationChangesEnabled: false
129+
130+ property real landscapeHeight: root.nativeOrientation == Qt.LandscapeOrientation ?
131+ root.nativeHeight : root.nativeWidth
132+
133+ property bool shellIsLandscape: root.shellOrientation === Qt.LandscapeOrientation
134+ || root.shellOrientation === Qt.InvertedLandscapeOrientation
57135
58136 property string mainStageAppId
59137 property string sideStageAppId
@@ -71,24 +149,17 @@ Rectangle {
71149 priv.sideStageAppId = focusedAppId;
72150 } else {
73151 priv.mainStageAppId = focusedAppId;
152+ root.mainApp = focusedApp;
74153 }
75154 }
76155
77156 appId0 = ApplicationManager.count >= 1 ? ApplicationManager.get(0).appId : "";
78157 appId1 = ApplicationManager.count > 1 ? ApplicationManager.get(1).appId : "";
79−
80− // Update the QML focus accordingly
81− updateSpreadDelegateFocus();
82158 }
83159
84− function updateSpreadDelegateFocus() {
85− if (priv.focusedAppId) {
86− var focusedAppIndex = priv.indexOf(priv.focusedAppId);
87− if (focusedAppIndex !== -1) {
88− spreadRepeater.itemAt(focusedAppIndex).focus = true;
89− } else {
90− console.warn("TabletStage: Failed to find the SpreadDelegate for appID=" + priv.focusedAppId);
91− }
160+ onFocusedAppDelegateChanged: {
161+ if (focusedAppDelegate) {
162+ focusedAppDelegate.focus = true;
92163 }
93164 }
94165
@@ -169,9 +240,9 @@ Rectangle {
169240
170241 Flickable {
171242 id: spreadView
243+ objectName: "spreadView"
172244 anchors.fill: parent
173− interactive: (spreadDragArea.status == DirectionalDragArea.Recognized || phase > 1)
174− && draggedDelegateCount === 0
245+ interactive: (spreadDragArea.dragging || phase > 1) && draggedDelegateCount === 0
175246 contentWidth: spreadRow.width - shift
176247 contentX: -shift
177248
@@ -218,7 +289,25 @@ Rectangle {
218289 property int draggedDelegateCount: 0
219290 property int closingIndex: -1
220291
292+ // FIXME: Workaround Flickable's not keepping its contentX still when resized
293+ onContentXChanged: { forceItToRemainStillIfBeingResized(); }
294+ onShiftChanged: { forceItToRemainStillIfBeingResized(); }
295+ function forceItToRemainStillIfBeingResized() {
296+ if (root.beingResized && contentX != -shift) {
297+ contentX = -shift;
298+ }
299+ }
300+
221301 property bool animateX: true
302+ property bool beingResized: false
303+ onBeingResizedChanged: {
304+ if (beingResized) {
305+ // Brace yourselves for impact!
306+ selectedIndex = -1;
307+ phase = 0;
308+ contentX = -shift;
309+ }
310+ }
222311
223312 property bool sideStageDragging: sideStageDragHandle.dragging
224313 property real sideStageDragProgress: sideStageDragHandle.progress
@@ -257,7 +346,6 @@ Rectangle {
257346 case "overlay":
258347 return 1;
259348 }
260− print("Unhandled nextInStack case! This shouldn't happen any more when the Dash is an app!");
261349 return -1;
262350 }
263351 property int nextZInStack: indexToZIndex(nextInStack)
@@ -293,6 +381,10 @@ Rectangle {
293381 }
294382
295383 onShiftedContentXChanged: {
384+ if (root.beingResized) {
385+ // Flickabe.contentX wiggles during resizes. Don't react to it.
386+ return;
387+ }
296388 if (spreadView.phase == 0 && spreadView.shiftedContentX > spreadView.width * spreadView.positionMarker2) {
297389 spreadView.phase = 1;
298390 } else if (spreadView.phase == 1 && spreadView.shiftedContentX > spreadView.width * spreadView.positionMarker4) {
@@ -402,8 +494,8 @@ Rectangle {
402494 MouseArea {
403495 id: spreadRow
404496 x: spreadView.contentX
405− height: root.height
406497 width: spreadView.width + Math.max(spreadView.width, ApplicationManager.count * spreadView.tileDistance)
498+ height: root.height
407499
408500 onClicked: {
409501 spreadView.snapTo(0);
@@ -412,8 +504,9 @@ Rectangle {
412504 Rectangle {
413505 id: sideStageBackground
414506 color: "black"
415− anchors.fill: parent
416− anchors.leftMargin: spreadView.width - (1 - sideStageDragHandle.progress) * spreadView.sideStageWidth
507+ width: spreadView.sideStageWidth * (1 - sideStageDragHandle.progress)
508+ height: priv.landscapeHeight
509+ x: spreadView.width - width
417510 z: spreadView.indexToZIndex(priv.indexOf(priv.sideStageAppId))
418511 opacity: spreadView.phase == 0 ? 1 : 0
419512 Behavior on opacity { UbuntuNumberAnimation {} }
@@ -421,8 +514,10 @@ Rectangle {
421514
422515 Item {
423516 id: sideStageDragHandle
424− anchors { top: parent.top; bottom: parent.bottom; left: parent.left; leftMargin: spreadView.width - spreadView.sideStageWidth - width }
517+ anchors.right: sideStageBackground.left
518+ anchors.top: sideStageBackground.top
425519 width: units.gu(2)
520+ height: priv.landscapeHeight
426521 z: sideStageBackground.z
427522 opacity: spreadView.phase <= 0 && spreadView.sideStageVisible ? 1 : 0
428523 property real progress: 0
@@ -441,7 +536,6 @@ Rectangle {
441536
442537 Image {
443538 anchors.centerIn: parent
444− anchors.horizontalCenterOffset: parent.progress * spreadView.sideStageWidth - (width - parent.width) / 2
445539 width: sideStageDragHandleMouseArea.pressed ? parent.width * 2 : parent.width
446540 height: parent.height
447541 source: "graphics/sidestage_handle@20.png"
@@ -454,16 +548,19 @@ Rectangle {
454548 enabled: spreadView.shiftedContentX == 0
455549 property int startX
456550 property var gesturePoints: new Array()
551+ property real totalDiff
457552
458553 onPressed: {
459554 gesturePoints = [];
460555 startX = mouseX;
556+ totalDiff = 0.0;
461557 sideStageDragHandle.progress = 0;
462558 sideStageDragHandle.dragging = true;
463559 }
464560 onMouseXChanged: {
561+ totalDiff += mouseX - startX;
465562 if (priv.mainStageAppId) {
466− sideStageDragHandle.progress = Math.max(0, (-startX + mouseX) / spreadView.sideStageWidth);
563+ sideStageDragHandle.progress = Math.max(0, totalDiff / spreadView.sideStageWidth);
467564 }
468565 gesturePoints.push(mouseX);
469566 }
@@ -492,12 +589,27 @@ Rectangle {
492589
493590 Repeater {
494591 id: spreadRepeater
592+ objectName: "spreadRepeater"
495593 model: ApplicationManager
496594
497595 delegate: TransformedTabletSpreadDelegate {
498596 id: spreadTile
499− height: spreadView.height
500− width: model.stage == ApplicationInfoInterface.MainStage ? spreadView.width : spreadView.sideStageWidth
597+ objectName: model.appId ? "tabletSpreadDelegate_" + model.appId
598+ : "tabletSpreadDelegate_null";
599+ width: {
600+ if (wantsMainStage) {
601+ return spreadView.width;
602+ } else {
603+ return spreadView.sideStageWidth;
604+ }
605+ }
606+ height: {
607+ if (wantsMainStage) {
608+ return spreadView.height;
609+ } else {
610+ return priv.landscapeHeight;
611+ }
612+ }
501613 active: model.appId == priv.mainStageAppId || model.appId == priv.sideStageAppId
502614 zIndex: spreadView.indexToZIndex(index)
503615 selected: spreadView.selectedIndex == index
@@ -510,6 +622,8 @@ Rectangle {
510622 application: ApplicationManager.get(index)
511623 closeable: !isDash
512624
625+ readonly property bool wantsMainStage: model.stage == ApplicationInfoInterface.MainStage
626+
513627 readonly property bool isDash: model.appId == "unity8-dash"
514628
515629 // FIXME: A regular binding doesn't update any more after closing an app.
@@ -561,12 +675,11 @@ Rectangle {
561675 return progress;
562676 }
563677
564− Binding {
565− target: spreadTile
566− property: "orientation"
567− when: spreadTile.interactive
568− value: root.orientation
569− }
678+ shellOrientationAngle: wantsMainStage ? root.shellOrientationAngle : 0
679+ shellOrientation: wantsMainStage ? root.shellOrientation : Qt.PortraitOrientation
680+ shellPrimaryOrientation: wantsMainStage ? root.shellPrimaryOrientation : Qt.PortraitOrientation
681+ nativeOrientation: wantsMainStage ? root.nativeOrientation : Qt.PortraitOrientation
682+
570683
571684 onClicked: {
572685 if (spreadView.phase == 2) {
@@ -587,6 +700,19 @@ Rectangle {
587700 ApplicationManager.stopApplication(ApplicationManager.get(index).appId);
588701 }
589702
703+ Binding {
704+ target: root
705+ when: model.appId == priv.mainStageAppId
706+ property: "mainAppWindowOrientationAngle"
707+ value: appWindowOrientationAngle
708+ }
709+ Binding {
710+ target: priv
711+ when: model.appId == priv.mainStageAppId
712+ property: "mainAppOrientationChangesEnabled"
713+ value: orientationChangesEnabled
714+ }
715+
590716 EasingCurve {
591717 id: snappingCurve
592718 type: EasingCurve.Linear
@@ -598,11 +724,13 @@ Rectangle {
598724 }
599725 }
600726
601− EdgeDragArea {
727+ DirectionalDragArea {
602728 id: spreadDragArea
729+ objectName: "spreadDragArea"
603730 anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
604731 width: root.dragAreaWidth
605732 direction: Direction.Leftwards
733+ enabled: (spreadView.phase != 2 && root.spreadEnabled) || dragging
606734
607735 property var gesturePoints: new Array()
608736
@@ -624,26 +752,25 @@ Rectangle {
624752 if (dragging) {
625753 // Gesture recognized. Start recording this gesture
626754 gesturePoints = [];
627− return;
628− }
629−
630− // Ok. The user released. Find out if it was a one-way movement.
631− var oneWayFlick = priv.evaluateOneWayFlick(gesturePoints);
632− gesturePoints = [];
755+ } else {
756+ // Ok. The user released. Find out if it was a one-way movement.
757+ var oneWayFlick = priv.evaluateOneWayFlick(gesturePoints);
758+ gesturePoints = [];
633759
634− if (oneWayFlick && spreadView.shiftedContentX < spreadView.positionMarker1 * spreadView.width) {
635− // If it was a short one-way movement, do the Alt+Tab switch
636− // no matter if we didn't cross positionMarker1 yet.
637− spreadView.snapTo(spreadView.nextInStack);
638− } else if (!dragging) {
639− if (spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker1) {
640− spreadView.snap();
641− } else if (spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker2) {
760+ if (oneWayFlick && spreadView.shiftedContentX < spreadView.positionMarker1 * spreadView.width) {
761+ // If it was a short one-way movement, do the Alt+Tab switch
762+ // no matter if we didn't cross positionMarker1 yet.
642763 spreadView.snapTo(spreadView.nextInStack);
643764 } else {
644− // otherwise snap to the closest snap position we can find
645− // (might be back to start, to app 1 or to spread)
646− spreadView.snap();
765+ if (spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker1) {
766+ spreadView.snap();
767+ } else if (spreadView.shiftedContentX < spreadView.width * spreadView.positionMarker2) {
768+ spreadView.snapTo(spreadView.nextInStack);
769+ } else {
770+ // otherwise snap to the closest snap position we can find
771+ // (might be back to start, to app 1 or to spread)
772+ spreadView.snap();
773+ }
647774 }
648775 }
649776 }
qml/Stages/TransformedSpreadDelegate.qml+50−6
@@ -298,7 +298,6 @@ SpreadDelegate {
298298
299299 return easingCurve.value;
300300 }
301−
302301 }
303302
304303 transform: [
@@ -307,16 +306,61 @@ SpreadDelegate {
307306 axis { x: 0; y: 1; z: 0 }
308307 angle: priv.angle
309308 },
309+
310+ // The next two transformations are to ensure that fullscreen and rotated
311+ // windows all have the same size and position as an unrotated (0 degrees)
312+ // non-fullscreen window when they're stacked on top of each other on the
313+ // far left of the spread.
314+ Translate {
315+ y: !fullscreen && appWindowRotation === 180
316+ ? priv.topMarginProgress * maximizedAppTopMargin
317+ : 0
318+ },
319+ Scale {
320+ origin {
321+ x: appWindowRotation === 270 ? spreadView.width : 0
322+ y: spreadView.height
323+ }
324+
325+ xScale: {
326+ switch (appWindowRotation) {
327+ case 90:
328+ case 270:
329+ if (fullscreen) {
330+ return 1;
331+ } else {
332+ return 1 + priv.topMarginProgress * maximizedAppTopMargin / spreadView.width;
333+ }
334+ break;
335+ default:
336+ return 1;
337+ }
338+ }
339+
340+ yScale: {
341+ switch (appWindowRotation) {
342+ case 0:
343+ if (fullscreen) {
344+ return 1 - priv.topMarginProgress * maximizedAppTopMargin / spreadView.height;
345+ } else {
346+ return 1;
347+ }
348+ break;
349+ case 90:
350+ case 270:
351+ return 1 - priv.topMarginProgress * maximizedAppTopMargin / spreadView.height;
352+ break;
353+ default:
354+ return 1;
355+ }
356+ }
357+ },
358+
310359 Scale {
311360 origin { x: 0; y: spreadView.height / 2 }
312361 xScale: priv.scale
313362 yScale: xScale
314363 },
315− Scale {
316− origin { x: 0; y: (spreadView.height * priv.scale) + maximizedAppTopMargin * 3 }
317− xScale: 1
318− yScale: fullscreen ? 1 - priv.topMarginProgress * maximizedAppTopMargin / spreadView.height : 1
319− },
320364 Translate {
321365 x: priv.xTranslate
322366 }
qml/Stages/TransformedTabletSpreadDelegate.qml+5−2
@@ -50,7 +50,9 @@ SpreadDelegate {
5050 property int dragOffset: 0
5151
5252 dropShadow: spreadView.active ||
53− (active && model.stage == ApplicationInfoInterface.MainStage && priv.xTranslate != 0)
53+ (active
54+ && (model.stage == ApplicationInfoInterface.MainStage || !priv.shellIsLandscape)
55+ && priv.xTranslate != 0)
5456
5557 onSelectedChanged: {
5658 if (selected) {
@@ -139,7 +141,8 @@ SpreadDelegate {
139141 !snapAnimation.running &&
140142 model.appId !== "unity8-dash" &&
141143 !spreadView.sideStageDragging &&
142− spreadView.animateX
144+ spreadView.animateX &&
145+ !spreadView.beingResized
143146 UbuntuNumberAnimation {
144147 duration: UbuntuAnimation.FastDuration
145148 }
qml/Tutorial/TutorialBottom.qml+1−1
@@ -73,7 +73,7 @@ TutorialPage {
7373 ]
7474 }
7575
76− EdgeDragArea {
76+ DirectionalDragArea {
7777 id: dragArea
7878 direction: Direction.Upwards
7979 anchors {
run.sh+1−1
@@ -44,7 +44,7 @@ else
4444 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/builddir/plugins/LightDM/liblightdm
4545fi
4646
47−QML_PHONE_SHELL_ARGS=""
47+QML_PHONE_SHELL_ARGS="-windowgeometry=40gux68gu -devicename=mako"
4848if $MOUSE_TOUCH; then
4949 QML_PHONE_SHELL_ARGS="$QML_PHONE_SHELL_ARGS -mousetouch"
5050fi
runtests.sh+3
@@ -42,6 +42,9 @@ sleep 1
4242cd -
4343
4444make -k qmltests
45+if [ $? -ne 0 ]; then
46+ echo '<?xml version="1.0" encoding="UTF-8" ?><testsuite errors="0" failures="1" tests="1" name="makeExitStatusTest"><properties/><testcase result="fail" name="makeExitStatus"><failure message="Make test did not suceed" result="fail"/></testcase><system-err/></testsuite>' > testMakeExitStatus.xml
47+fi
4548
4649cd -
4750
src/ApplicationArguments.cppnew file+22
@@ -0,0 +1,22 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "ApplicationArguments.h"
18+
19+ApplicationArguments::ApplicationArguments(QObject *parent)
20+ : QObject(parent)
21+{
22+}
src/ApplicationArguments.h+7−11
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013,2015 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
@@ -13,7 +13,6 @@
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/>.
1515 *
16− * Authored by: Nick Dedekind <nick.dedekind@canonical.com>
1716 */
1817
1918
@@ -22,23 +21,20 @@
2221
2322#include <QObject>
2423#include <QSize>
25−#include <QStringList>
24+#include <QString>
2625
2726class ApplicationArguments : public QObject
2827{
2928 Q_OBJECT
29+ Q_PROPERTY(QString deviceName READ deviceName CONSTANT)
3030public:
31− // Not exposed to the app as setSize isn't invokable
32− void setSize(const QSize &size) {
33− m_size = size;
34− }
31+ ApplicationArguments(QObject *parent = nullptr);
3532
36− Q_INVOKABLE bool hasGeometry() const { return m_size.isValid(); }
37− Q_INVOKABLE int width() const { return m_size.width(); }
38− Q_INVOKABLE int height() const { return m_size.height(); }
33+ void setDeviceName(QString deviceName) { m_deviceName = deviceName; }
34+ QString deviceName() const { return m_deviceName; }
3935
4036private:
41− QSize m_size;
37+ QString m_deviceName;
4238};
4339
4440#endif // APPLICATION_ARGUMENTS_H
src/CMakeLists.txt+5−6
@@ -17,7 +17,7 @@ file(GLOB_RECURSE QML_FILES
1717)
1818
1919add_executable(${SHELL_APP}
20− ApplicationArguments.h
20+ ApplicationArguments.cpp
2121 main.cpp
2222 MouseTouchAdaptor.cpp
2323 CachingNetworkManagerFactory.cpp
@@ -26,13 +26,12 @@ add_executable(${SHELL_APP}
2626)
2727
2828qt5_use_modules(${SHELL_APP} Gui Qml Quick Test)
29−pkg_check_modules(XCB REQUIRED xcb)
3029
31−if (NOT "${XCB_INCLUDE_DIRS}" STREQUAL "")
32− set_target_properties(${SHELL_APP} PROPERTIES INCLUDE_DIRECTORIES ${XCB_INCLUDE_DIRS})
33−
34− target_link_libraries(${SHELL_APP} ${XCB_LDFLAGS})
30+pkg_check_modules(NEEDED_LIBS REQUIRED xcb libandroid-properties)
31+if (NOT "${NEEDED_LIBS_INCLUDE_DIRS}" STREQUAL "")
32+ set_target_properties(${SHELL_APP} PROPERTIES INCLUDE_DIRECTORIES ${NEEDED_LIBS_INCLUDE_DIRS})
3533endif()
34+target_link_libraries(${SHELL_APP} ${NEEDED_LIBS_LDFLAGS})
3635
3736target_link_libraries(${SHELL_APP} UbuntuGestures connectivity-qt1)
3837
src/Dash/CMakeLists.txt+7−1
@@ -1,6 +1,6 @@
11set(DASH_SRCS
22 main.cpp
3− ../ApplicationArguments.h
3+ ../ApplicationArguments.cpp
44 ../MouseTouchAdaptor.cpp
55 ../CachingNetworkManagerFactory.cpp
66)
@@ -9,6 +9,12 @@ add_executable(unity8-dash ${DASH_SRCS})
99
1010qt5_use_modules(unity8-dash Gui Qml Quick Test)
1111
12+pkg_check_modules(NEEDED_LIBS REQUIRED xcb libandroid-properties)
13+if (NOT "${NEEDED_LIBS_INCLUDE_DIRS}" STREQUAL "")
14+ set_target_properties(unity8-dash PROPERTIES INCLUDE_DIRECTORIES ${NEEDED_LIBS_INCLUDE_DIRS})
15+endif()
16+target_link_libraries(unity8-dash ${NEEDED_LIBS_LDFLAGS})
17+
1218# For it to find libUbuntuGestures.so, needed by Ubuntu.Gestures QML module.
1319set_target_properties(unity8-dash PROPERTIES INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${SHELL_PRIVATE_LIBDIR}")
1420
src/Dash/main.cpp+12−12
@@ -31,9 +31,6 @@
3131#include "../ApplicationArguments.h"
3232#include "../CachingNetworkManagerFactory.h"
3333
34−// Ubuntu Gestures
35−#include <TouchRegistry.h>
36−
3734int main(int argc, const char *argv[])
3835{
3936 QGuiApplication *application = new QGuiApplication(argc, (char**)argv);
@@ -61,12 +58,6 @@ int main(int argc, const char *argv[])
6158 parser.process(*application);
6259
6360 ApplicationArguments qmlArgs;
64− if (parser.isSet(windowGeometryOption) &&
65− parser.value(windowGeometryOption).split('x').size() == 2)
66− {
67− QStringList geom = parser.value(windowGeometryOption).split('x');
68− qmlArgs.setSize(QSize(geom.at(0).toInt(), geom.at(1).toInt()));
69− }
7061
7162 if (getenv("QT_LOAD_TESTABILITY")) {
7263 QLibrary testLib(QLatin1String("qttestability"));
@@ -88,12 +79,21 @@ int main(int argc, const char *argv[])
8879
8980 QQuickView* view = new QQuickView();
9081 view->setResizeMode(QQuickView::SizeRootObjectToView);
82+
83+ if (parser.isSet(windowGeometryOption) &&
84+ parser.value(windowGeometryOption).split('x').size() == 2)
85+ {
86+ QStringList geom = parser.value(windowGeometryOption).split('x');
87+ QSize windowSize(geom.at(0).toInt(), geom.at(1).toInt());
88+ if (windowSize.isValid()) {
89+ view->setWidth(windowSize.width());
90+ view->setHeight(windowSize.height());
91+ }
92+ }
93+
9194 view->setTitle("Scopes");
9295 view->rootContext()->setContextProperty("applicationArguments", &qmlArgs);
9396
94− TouchRegistry touchRegistry;
95− view->installEventFilter(&touchRegistry);
96−
9797 // You will need this if you want to interact with touch-only components using a mouse
9898 // Needed only when manually testing on a desktop.
9999 MouseTouchAdaptor *mouseTouchAdaptor = 0;
src/UnityCommandLineParser.cpp+5
@@ -49,6 +49,10 @@ UnityCommandLineParser::UnityCommandLineParser(const QCoreApplication &app)
4949 "DISCOURAGED: Please set QT_LOAD_TESTABILITY instead.\nLoad the testability driver");
5050 parser.addOption(testabilityOption);
5151
52+ QCommandLineOption devicenameOption(QStringList() << "devicename",
53+ "Specify the device name instead of letting Unity 8 find it out", "devicename", "");
54+ parser.addOption(devicenameOption);
55+
5256 QCommandLineOption modeOption("mode",
5357 "Whether to run greeter and/or shell [full-greeter, full-shell, greeter, shell]",
5458 "mode", "full-greeter");
@@ -73,6 +77,7 @@ UnityCommandLineParser::UnityCommandLineParser(const QCoreApplication &app)
7377 m_hasFrameless = parser.isSet(framelessOption);
7478 m_hasMouseToTouch = parser.isSet(mousetouchOption);
7579 m_hasFullscreen = parser.isSet(fullscreenOption);
80+ m_deviceName = parser.value(devicenameOption);
7681 resolveMode(parser, modeOption);
7782}
7883
src/UnityCommandLineParser.h+2
@@ -30,6 +30,7 @@ public:
3030 bool hasFrameless() const { return m_hasFrameless; }
3131 bool hasMouseToTouch() const { return m_hasMouseToTouch; }
3232 bool hasFullscreen() const { return m_hasFullscreen; }
33+ QString deviceName() const { return m_deviceName; }
3334 QString mode() const { return m_mode; }
3435private:
3536
@@ -44,6 +45,7 @@ private:
4445 bool m_hasFrameless;
4546 bool m_hasMouseToTouch;
4647 bool m_hasFullscreen;
48+ QString m_deviceName;
4749 QString m_mode;
4850};
4951
src/main.cpp+18−13
@@ -25,6 +25,9 @@
2525#include <csignal>
2626#include <libintl.h>
2727
28+// libandroid-properties
29+#include <hybris/properties/properties.h>
30+
2831// local
2932#include <paths.h>
3033#include "MouseTouchAdaptor.h"
@@ -32,9 +35,6 @@
3235#include "CachingNetworkManagerFactory.h"
3336#include "UnityCommandLineParser.h"
3437
35−// Ubuntu Gestures
36−#include <TouchRegistry.h>
37−
3838int main(int argc, const char *argv[])
3939{
4040 bool isMirServer = false;
@@ -50,13 +50,15 @@ int main(int argc, const char *argv[])
5050
5151 UnityCommandLineParser parser(*application);
5252
53− QString indicatorProfile = qgetenv("UNITY_INDICATOR_PROFILE");
54− if (indicatorProfile.isEmpty()) {
55− indicatorProfile = "phone";
56− }
57−
5853 ApplicationArguments qmlArgs;
59− qmlArgs.setSize(parser.windowGeometry());
54+
55+ if (!parser.deviceName().isEmpty()) {
56+ qmlArgs.setDeviceName(parser.deviceName());
57+ } else {
58+ char buffer[200];
59+ property_get("ro.product.device", buffer /* value */, "desktop" /* default_value*/);
60+ qmlArgs.setDeviceName(QString(buffer));
61+ }
6062
6163 // The testability driver is only loaded by QApplication but not by QGuiApplication.
6264 // However, QApplication depends on QWidget which would add some unneeded overhead => Let's load the testability driver on our own.
@@ -82,15 +84,18 @@ int main(int argc, const char *argv[])
8284 view->setResizeMode(QQuickView::SizeRootObjectToView);
8385 view->setColor("black");
8486 view->setTitle("Unity8 Shell");
87+
88+ if (parser.windowGeometry().isValid()) {
89+ view->setWidth(parser.windowGeometry().width());
90+ view->setHeight(parser.windowGeometry().height());
91+ }
92+
8593 view->engine()->setBaseUrl(QUrl::fromLocalFile(::qmlDirectory()));
8694 view->rootContext()->setContextProperty("applicationArguments", &qmlArgs);
87− view->rootContext()->setContextProperty("indicatorProfile", indicatorProfile);
8895 view->rootContext()->setContextProperty("shellMode", parser.mode());
8996 if (parser.hasFrameless()) {
9097 view->setFlags(Qt::FramelessWindowHint);
9198 }
92− TouchRegistry touchRegistry;
93− view->installEventFilter(&touchRegistry);
9499
95100 // You will need this if you want to interact with touch-only components using a mouse
96101 // Needed only when manually testing on a desktop.
@@ -99,7 +104,7 @@ int main(int argc, const char *argv[])
99104 mouseTouchAdaptor = MouseTouchAdaptor::instance();
100105 }
101106
102− QUrl source(::qmlDirectory()+"Shell.qml");
107+ QUrl source(::qmlDirectory()+"OrientedShell.qml");
103108 prependImportPaths(view->engine(), ::overrideImportPaths());
104109 if (!isMirServer) {
105110 prependImportPaths(view->engine(), ::nonMirImportPaths());
tests/autopilot/unity8/application_lifecycle/tests/test_application_lifecycle.py+14−4
@@ -21,6 +21,7 @@
2121
2222import logging
2323import os
24+import threading
2425
2526from autopilot.platform import model
2627
@@ -47,7 +48,7 @@ class ApplicationLifecycleTests(tests.ApplicationLifeCycleTestCase):
4748 end_y = start_y
4849
4950 logger.info("Swiping screen from the right edge")
50− self.touch.drag(start_x, start_y, end_x, end_y)
51+ self.main_window.pointing_device.drag(start_x, start_y, end_x, end_y)
5152
5253 def launch_fake_app(self):
5354 _, desktop_file_path = self.create_test_application()
@@ -90,10 +91,19 @@ class ApplicationLifecycleTests(tests.ApplicationLifeCycleTestCase):
9091 """Greeter should hide when an app is opened"""
9192 process_helpers.lock_unity()
9293
93− application_name = self.launch_fake_app()
94+ # FIXME - this is because the device greeter uses a password.
95+ # Need to be able to selectively enable mocks so that we can use the fake greeter.
96+ def unlock_thread_worker(greeter):
97+ greeter.wait_swiped_away()
98+ process_helpers.unlock_unity()
99+ greeter.created.wait_for(False)
100+
94101 greeter = self.main_window.get_greeter()
95− greeter.wait_swiped_away()
96− process_helpers.unlock_unity()
102+ unlock_thread = threading.Thread(target=unlock_thread_worker, args=(greeter,))
103+ unlock_thread.start()
104+ application_name = self.launch_fake_app()
105+ unlock_thread.join(10)
106+
97107 self.assert_current_focused_application(application_name)
98108
99109 def test_greeter_hides_on_app_focus(self):
tests/autopilot/unity8/fixture_setup.py+197−3
@@ -17,21 +17,215 @@
1717# along with this program. If not, see <http://www.gnu.org/licenses/>.
1818#
1919
20−import fixtures
20+import os
2121import subprocess
22+import threading
23+import fixtures
2224import logging
2325
26+import ubuntuuitoolkit
2427from autopilot import introspection
28+from autopilot.matchers import Eventually
29+from testtools.matchers import Equals
30+from ubuntuuitoolkit import fixture_setup
2531
2632from unity8 import (
2733 get_binary_path,
34+ sensors,
2835 process_helpers
2936)
37+
3038from unity8.shell import emulators
3139
40+from unity8.shell.emulators import (
41+ main_window as main_window_emulator
42+)
43+
44+from unity8 import (
45+ get_mocks_library_path,
46+ get_default_extra_mock_libraries,
47+ get_data_dirs
48+)
49+
50+logger = logging.getLogger(__name__)
51+
52+
53+class LaunchUnityWithFakeSensors(fixtures.Fixture):
54+
55+ """Fixture to launch Unity8 with an injectable sensors backend.
56+
57+ :ivar unity_proxy: The Autopilot proxy object for the Unity shell.
58+
59+ """
60+
61+ unity_proxy = None
62+ main_win = None
63+
64+ def setUp(self):
65+ """Restart Unity8 with testability and create sensors."""
66+ super().setUp()
67+ self.useFixture(
68+ fixture_setup.InitctlEnvironmentVariable(
69+ UBUNTU_PLATFORM_API_TEST_OVERRIDE='sensors'))
70+
71+ self.addCleanup(process_helpers.stop_job, 'unity8')
72+ restart_thread = threading.Thread(
73+ target=self._restart_unity_with_testability)
74+ restart_thread.start()
75+
76+ self._create_sensors()
77+
78+ restart_thread.join()
79+ self.fake_sensors = sensors.FakePlatformSensors()
80+
81+ def _get_lightdm_mock_path(self):
82+ lib_path = get_mocks_library_path()
83+ lightdm_mock_path = os.path.abspath(
84+ os.path.join(lib_path, "LightDM", "liblightdm")
85+ )
86+
87+ if not os.path.exists(lightdm_mock_path):
88+ raise RuntimeError(
89+ "LightDM mock does not exist at path {}.".
90+ format(lightdm_mock_path)
91+ )
92+ return lightdm_mock_path
93+
94+ def _get_qml_import_path_with_mock(self):
95+ """Return the QML2_IMPORT_PATH value with the mock path prepended."""
96+ qml_import_path = [get_mocks_library_path()]
97+ if os.getenv('QML2_IMPORT_PATH') is not None:
98+ qml_import_path.append(os.getenv('QML2_IMPORT_PATH'))
99+
100+ qml_import_path = ':'.join(qml_import_path)
101+ return qml_import_path
102+
103+ def _restart_unity_with_testability(self):
104+ _environment = {}
105+
106+ data_dirs = get_data_dirs(True)
107+ if data_dirs is not None:
108+ _environment['XDG_DATA_DIRS'] = data_dirs
109+
110+ _environment['QML2_IMPORT_PATH'] = (
111+ self._get_qml_import_path_with_mock()
112+ )
113+
114+ new_ld_library_path = [
115+ get_default_extra_mock_libraries(),
116+ self._get_lightdm_mock_path()
117+ ]
118+ if os.getenv('LD_LIBRARY_PATH') is not None:
119+ new_ld_library_path.append(os.getenv('LD_LIBRARY_PATH'))
120+ new_ld_library_path = ':'.join(new_ld_library_path)
121+ _environment['LD_LIBRARY_PATH'] = new_ld_library_path
122+
123+ # FIXME: we shouldn't be doing this
124+ # $MIR_SOCKET, fallback to $XDG_RUNTIME_DIR/mir_socket and
125+ # /tmp/mir_socket as last resort
126+ try:
127+ os.unlink(
128+ os.getenv('MIR_SOCKET',
129+ os.path.join(os.getenv('XDG_RUNTIME_DIR', "/tmp"),
130+ "mir_socket")))
131+ except OSError:
132+ pass
133+ try:
134+ os.unlink("/tmp/mir_socket")
135+ except OSError:
136+ pass
137+
138+ binary_arg = "BINARY=%s" % get_binary_path()
139+ env_args = ["%s=%s" % (k, v) for k, v in _environment.items()]
140+ args = [binary_arg] + env_args
141+ self.unity_proxy = process_helpers.restart_unity_with_testability(
142+ *args)
143+ self.main_win = self.unity_proxy.select_single(
144+ main_window_emulator.QQuickView)
145+
146+ def _create_sensors(self):
147+ # Wait for unity to start running.
148+ Eventually(Equals(True)).match(
149+ lambda: process_helpers.is_job_running('unity8'))
150+
151+ # Wait for the sensors fifo file to be created.
152+ fifo_path = '/tmp/sensor-fifo-{0}'.format(
153+ process_helpers.get_unity_pid())
154+ Eventually(Equals(True)).match(
155+ lambda: os.path.exists(fifo_path))
156+
157+ with open(fifo_path, 'w') as fifo:
158+ fifo.write('create accel 0 1000 0.1\n')
159+ fifo.write('create light 0 10 1\n')
160+ fifo.write('create proximity\n')
161+
32162logger = logging.getLogger(__name__)
33163
34164
165+class RestartUnityWithTestability(fixtures.Fixture):
166+
167+ """Fixture to launch Unity8 with testability.
168+
169+ :ivar unity_proxy: The Autopilot proxy object for the Unity shell.
170+
171+ """
172+
173+ unity_proxy = None
174+
175+ def __init__(self, binary_path, variables):
176+ """Initialize the fixture instance.
177+
178+ :param str binary_path: The path to the Dash app binary.
179+ :param cli_arguments: The arguments to pass when launching the
180+ :param variables: The variables to use when launching the app.
181+ :type variables: A dictionary.
182+
183+ """
184+ super().__init__()
185+ self.binary_path = binary_path
186+ self.variables = variables
187+
188+ def setUp(self):
189+ """Restart unity with testability when the fixture is used."""
190+ super().setUp()
191+ self.addCleanup(self.stop_unity)
192+ self.restart_unity()
193+
194+ def restart_unity(self):
195+ self.restart_unity_with_testability()
196+
197+ def restart_unity_with_testability(self):
198+ self._unlink_mir_socket()
199+
200+ binary_arg = 'BINARY={}'.format(self.binary_path)
201+ variable_args = [
202+ '{}={}'.format(key, value) for key, value in self.variables.items()
203+ ]
204+ all_args = [binary_arg] + variable_args
205+
206+ self.unity_proxy = process_helpers.restart_unity_with_testability(
207+ *all_args)
208+
209+ def _unlink_mir_socket(self):
210+ # FIXME: we shouldn't be doing this
211+ # $MIR_SOCKET, fallback to $XDG_RUNTIME_DIR/mir_socket and
212+ # /tmp/mir_socket as last resort
213+ try:
214+ os.unlink(
215+ os.getenv('MIR_SOCKET',
216+ os.path.join(os.getenv('XDG_RUNTIME_DIR', "/tmp"),
217+ "mir_socket")))
218+ except OSError:
219+ pass
220+ try:
221+ os.unlink("/tmp/mir_socket")
222+ except OSError:
223+ pass
224+
225+ def stop_unity(self):
226+ process_helpers.stop_job('unity8')
227+
228+
35229class LaunchDashApp(fixtures.Fixture):
36230
37231 """Fixture to launch the Dash app."""
@@ -65,7 +259,7 @@ class LaunchDashApp(fixtures.Fixture):
65259 pid = process_helpers.start_job('unity8-dash', *all_args)
66260 return introspection.get_proxy_object_for_existing_process(
67261 pid=pid,
68− emulator_base=emulators.UnityEmulatorBase,
262+ emulator_base=ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase
69263 )
70264
71265 def stop_application(self):
@@ -122,7 +316,7 @@ class LaunchMockIndicatorService(fixtures.Fixture):
122316 self.ensure_not_running = ensure_not_running
123317
124318 def setUp(self):
125− super(LaunchMockIndicatorService, self).setUp()
319+ super().setUp()
126320 if self.ensure_not_running:
127321 self.ensure_service_not_running()
128322 self.addCleanup(self.stop_service)
tests/autopilot/unity8/indicators/__init__.py+2−4
@@ -18,10 +18,8 @@
1818import ubuntuuitoolkit
1919from autopilot import introspection
2020
21−from unity8.shell import emulators
2221
23−
24−class IndicatorPage(emulators.UnityEmulatorBase):
22+class IndicatorPage(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
2523
2624 """Autopilot helper for the IndicatorPage component."""
2725
@@ -166,7 +164,7 @@ class TestIndicatorPage(IndicatorPage):
166164 return self.select_single(objectName='indicator.action.slider')
167165
168166
169−class Slider(emulators.UnityEmulatorBase):
167+class Slider(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
170168
171169 """Autopilot helper for the Slider component."""
172170
tests/autopilot/unity8/indicators/tests/__init__.py−2
@@ -40,5 +40,3 @@ class DeviceIndicatorTestCase(IndicatorTestCase):
4040 if platform.model() == 'Desktop':
4141 self.skipTest('Test cannot be run on the desktop.')
4242 super().setUp()
43− self.unity_proxy = self.launch_unity()
44− process_helpers.unlock_unity(self.unity_proxy)
tests/autopilot/unity8/indicators/tests/test_action_latency.py+4−2
@@ -70,10 +70,11 @@ class TestServerValueUpdate(TestIndicatorBaseTestCase):
7070 switch_menu = self.indicator_page.get_switch_menu()
7171
7272 switch.change_state()
73+ final_value = switch.checked
7374
7475 self.assertThat(
7576 switch_menu.serverChecked,
76− Eventually(Equals(switch.checked), timeout=20)
77+ Eventually(Equals(final_value), timeout=20)
7778 )
7879
7980 def test_slider_reaches_server_value(self):
@@ -82,6 +83,7 @@ class TestServerValueUpdate(TestIndicatorBaseTestCase):
8283
8384 old_value = slider.value
8485 slider.slide_left()
86+ final_value = slider.value
8587
8688 self.assertThat(
8789 slider_menu.serverValue,
@@ -90,7 +92,7 @@ class TestServerValueUpdate(TestIndicatorBaseTestCase):
9092
9193 self.assertThat(
9294 slider_menu.serverValue,
93− Eventually(Equals(slider.value), timeout=20)
95+ Eventually(Equals(final_value), timeout=20)
9496 )
9597
9698
tests/autopilot/unity8/indicators/tests/test_display_indicator.py+1−1
@@ -18,7 +18,7 @@
1818
1919from unity8 import (
2020 fixture_setup,
21− indicators,
21+ indicators
2222)
2323from unity8.indicators import tests
2424
tests/autopilot/unity8/process_helpers.py+5−4
@@ -21,6 +21,7 @@ import logging
2121import subprocess
2222import dbus
2323
24+import ubuntuuitoolkit
2425from autopilot.exceptions import ProcessSearchError
2526from autopilot.introspection import get_proxy_object_for_existing_process
2627
@@ -57,7 +58,7 @@ def unlock_unity(unity_proxy_obj=None):
5758 """
5859 if unity_proxy_obj is None:
5960 try:
60− pid = _get_unity_pid()
61+ pid = get_unity_pid()
6162 unity = _get_unity_proxy_object(pid)
6263 main_window = unity.select_single(main_window_emulator.QQuickView)
6364 except ProcessSearchError as e:
@@ -107,7 +108,7 @@ def lock_unity(unity_proxy_obj=None):
107108 """
108109 if unity_proxy_obj is None:
109110 try:
110− pid = _get_unity_pid()
111+ pid = get_unity_pid()
111112 unity = _get_unity_proxy_object(pid)
112113 main_window = unity.select_single(main_window_emulator.QQuickView)
113114 except ProcessSearchError as e:
@@ -261,7 +262,7 @@ def _get_unity_status():
261262 raise CannotAccessUnity(str(error))
262263
263264
264−def _get_unity_pid():
265+def get_unity_pid():
265266 try:
266267 return get_job_pid('unity8')
267268 except JobError as error:
@@ -271,5 +272,5 @@ def _get_unity_pid():
271272def _get_unity_proxy_object(pid):
272273 return get_proxy_object_for_existing_process(
273274 pid=pid,
274− emulator_base=emulators.UnityEmulatorBase,
275+ emulator_base=ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase
275276 )
tests/autopilot/unity8/sensors.pynew file+93
@@ -0,0 +1,93 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+#
3+# Unity Autopilot Test Suite
4+# Copyright (C) 2015 Canonical
5+#
6+# This program is free software: you can redistribute it and/or modify
7+# it under the terms of the GNU General Public License as published by
8+# the Free Software Foundation, either version 3 of the License, or
9+# (at your option) any later version.
10+#
11+# This program is distributed in the hope that it will be useful,
12+# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+# GNU General Public License for more details.
15+#
16+# You should have received a copy of the GNU General Public License
17+# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+#
19+
20+"""Control module for event injection to the fake/test platform sensors."""
21+
22+from unity8 import process_helpers
23+
24+
25+class FakePlatformSensors:
26+
27+ def __init__(self, pid=None):
28+ self.pid = pid or process_helpers.get_unity_pid()
29+
30+ def set_orientation(self, action):
31+ if action == 'top_up':
32+ with open("/tmp/sensor-fifo-{0}".format(self.pid), "w") as fifo:
33+ fifo.write("70 accel -10.050858 -0.598550 0.756568\n")
34+ fifo.write("70 accel -9.797073 -0.555455 1.019930\n")
35+ fifo.write("70 accel -10.141838 -0.770933 0.632069\n")
36+ fifo.write("70 accel -12.057199 -1.259350 1.690306\n")
37+ fifo.write("70 accel -19.282900 -3.926491 3.098097\n")
38+ fifo.write("70 accel -14.480132 -14.269443 1.216254\n")
39+ fifo.write("70 accel 16.419436 4.242526 -7.714118\n")
40+ fifo.write("70 accel 5.583278 8.279149 -1.848324\n")
41+ fifo.write("70 accel 1.422156 8.547300 0.416591\n")
42+ fifo.write("70 accel 4.357447 9.988609 -0.110133\n")
43+ fifo.write("70 accel 0.699107 9.840169 0.756568\n")
44+ fifo.write("70 accel 1.364695 9.844957 -0.287304\n")
45+ fifo.flush()
46+ elif action == 'top_down':
47+ with open("/tmp/sensor-fifo-{0}".format(self.pid), "w") as fifo:
48+ fifo.write("70 accel -10.050858 -0.598550 0.756568\n")
49+ fifo.write("70 accel 9.538500 -0.603339 1.292869\n")
50+ fifo.write("70 accel 9.485827 -0.636858 1.422156\n")
51+ fifo.write("70 accel 9.677363 -0.402226 1.374272\n")
52+ fifo.write("70 accel 9.303867 -0.507571 1.283292\n")
53+ fifo.write("70 accel 8.604761 -1.015141 1.436521\n")
54+ fifo.write("70 accel 7.580042 -2.001553 0.521936\n")
55+ fifo.write("70 accel 7.503428 -4.247314 0.502782\n")
56+ fifo.write("70 accel 7.067683 -7.240066 0.842759\n")
57+ fifo.write("70 accel 6.488286 -9.873688 -0.541090\n")
58+ fifo.write("70 accel 6.229713 -9.241618 -1.048660\n")
59+ fifo.write("70 accel 4.046201 -9.198523 -0.057461\n")
60+ fifo.write("70 accel 2.398990 -9.629479 0.957681\n")
61+ fifo.write("70 accel 1.632846 -9.361329 -0.311246\n")
62+ fifo.write("70 accel -0.181959 -9.696517 -0.301669\n")
63+ fifo.flush()
64+ elif action == 'left_up':
65+ with open("/tmp/sensor-fifo-{0}".format(self.pid), "w") as fifo:
66+ fifo.write("70 accel -10.050858 -0.598550 0.756568\n")
67+ fifo.write("70 accel 0.196325 9.878476 0.948104\n")
68+ fifo.write("70 accel 0.258574 9.955091 1.091756\n")
69+ fifo.write("70 accel 0.287304 10.041282 1.134852\n")
70+ fifo.write("70 accel 1.537078 10.553641 1.561020\n")
71+ fifo.write("70 accel 8.130709 10.093954 2.561796\n")
72+ fifo.write("70 accel -0.229843 5.348647 1.723825\n")
73+ fifo.write("70 accel -9.916783 0.488417 -3.418920\n")
74+ fifo.write("70 accel -13.417107 -0.416591 -2.360683\n")
75+ fifo.write("70 accel -13.872005 -2.049437 -0.574608\n")
76+ fifo.flush()
77+ elif action == 'right_up':
78+ with open("/tmp/sensor-fifo-{0}".format(self.pid), "w") as fifo:
79+ fifo.write("70 accel -10.050858 -0.598550 0.756568\n")
80+ fifo.write("70 accel -4.550858 -0.598550 0.856568\n")
81+ fifo.write("70 accel -0.799663 9.988609 1.197101\n")
82+ fifo.write("70 accel -0.861913 9.864111 1.066701\n")
83+ fifo.write("70 accel -0.861913 9.864111 0.866701\n")
84+ fifo.write("70 accel -1.776498 9.830592 1.273715\n")
85+ fifo.write("70 accel -2.376498 9.830592 1.273715\n")
86+ fifo.write("70 accel -13.158532 2.217031 1.091756\n")
87+ fifo.write("70 accel 5.056554 1.067814 0.799663\n")
88+ fifo.write("70 accel 5.056554 1.767814 0.799663\n")
89+ fifo.write("70 accel 14.882358 2.896984 1.221043\n")
90+ fifo.write("70 accel 9.466674 -0.363919 -1.029507\n")
91+ fifo.write("70 accel 11.253524 -0.186748 -0.311246\n")
92+ fifo.write("70 accel 12.253524 -0.186748 -0.311246\n")
93+ fifo.flush()
tests/autopilot/unity8/shell/__init__.py−24
@@ -19,7 +19,6 @@
1919
2020"""unity shell autopilot tests and emulators - sub level package."""
2121
22−from time import sleep
2322from functools import wraps
2423from gi.repository import Notify
2524
@@ -38,29 +37,6 @@ def disable_qml_mocking(fn):
3837 return wrapper
3938
4039
41−class DragMixin():
42− def _drag(self, x1, y1, x2, y2):
43− # XXX This ugly code is here just temporarily, waiting for drag
44− # improvements to land on autopilot so we don't have to access device
45− # private internal attributes. --elopio - 2014-02-12
46− cur_x = x1
47− cur_y = y1
48− dx = 1.0 * (x2 - x1) / 100
49− dy = 1.0 * (y2 - y1) / 100
50− for i in range(0, 100):
51− try:
52− self.touch._finger_move(int(cur_x), int(cur_y))
53− except AttributeError:
54− self.touch._device.finger_move(int(cur_x), int(cur_y))
55− sleep(0.002)
56− cur_x += dx
57− cur_y += dy
58− try:
59− self.touch._finger_move(int(x2), int(y2))
60− except AttributeError:
61− self.touch._device.finger_move(int(x2), int(y2))
62−
63−
6440def create_ephemeral_notification(
6541 summary='',
6642 body='',
tests/autopilot/unity8/shell/emulators/__init__.py−6
@@ -17,12 +17,6 @@
1717# along with this program. If not, see <http://www.gnu.org/licenses/>.
1818#
1919
20−import ubuntuuitoolkit
21−
22−
23−class UnityEmulatorBase(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
24− """A base class for all unity emulators."""
25−
2620
2721class UnityEmulatorException(Exception):
2822 """Exception raised when there is an error with the Unity emulators."""
tests/autopilot/unity8/shell/emulators/dash.py+23−16
@@ -18,13 +18,13 @@
1818#
1919
2020import logging
21−import ubuntuuitoolkit
22−
23−from unity8.shell import emulators
2421
22+import ubuntuuitoolkit
2523from autopilot import logging as autopilot_logging
2624from autopilot.introspection import dbus
2725
26+from unity8.shell import emulators
27+
2828
2929logger = logging.getLogger(__name__)
3030
@@ -40,7 +40,7 @@ class DashApp():
4040 self.dash = self.main_view.select_single(Dash)
4141
4242
43−class Dash(emulators.UnityEmulatorBase):
43+class Dash(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
4444 """An emulator that understands the Dash."""
4545
4646 def __init__(self, *args):
@@ -130,7 +130,7 @@ class Dash(emulators.UnityEmulatorBase):
130130 x, y, width, height = dash_content.globalRect
131131 # Make the drag range be a multiple of the drag "rate" value.
132132 # Workarounds https://bugs.launchpad.net/mir/+bug/1399690
133− rate = 10
133+ rate = 5
134134 divisions = 5
135135 jump = (width / divisions) // rate * rate
136136 start_x = x + jump
@@ -146,7 +146,7 @@ class Dash(emulators.UnityEmulatorBase):
146146 x, y, width, height = dash_content.globalRect
147147 # Make the drag range be a multiple of the drag "rate" value.
148148 # Workarounds https://bugs.launchpad.net/mir/+bug/1399690
149− rate = 10
149+ rate = 5
150150 divisions = 5
151151 jump = (width / divisions) // rate * rate
152152 start_x = x + jump * (divisions - 1)
@@ -185,14 +185,15 @@ class Dash(emulators.UnityEmulatorBase):
185185
186186
187187class ListViewWithPageHeader(ubuntuuitoolkit.QQuickFlickable):
188− pass
188+
189+ margin_to_swipe_from_bottom = ubuntuuitoolkit.units.gu(4)
189190
190191
191−class GenericScopeView(emulators.UnityEmulatorBase):
192+class GenericScopeView(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
192193 """Autopilot emulator for generic scopes."""
193194
194195 @autopilot_logging.log_action(logger.info)
195− def open_preview(self, category, app_name):
196+ def open_preview(self, category, app_name, press_duration=0.10):
196197 """Open the preview of an application.
197198
198199 :parameter category: The name of the category where the application is.
@@ -203,16 +204,19 @@ class GenericScopeView(emulators.UnityEmulatorBase):
203204 # FIXME some categories need a long press in order to see the preview.
204205 # Some categories do not show previews, like recent apps.
205206 # --elopio - 2014-1-14
206− self.click_scope_item(category, app_name)
207+ self.click_scope_item(category, app_name, press_duration)
207208 preview_list = self.wait_select_single(
208209 'QQuickLoader', objectName='subPageLoader')
209210 preview_list.subPageShown.wait_for(True)
210211 preview_list.x.wait_for(0)
212+ self.get_root_instance().select_single(
213+ objectName='processingIndicator').visible.wait_for(False)
211214 return preview_list.select_single(
212− Preview, objectName='preview{}'.format(preview_list.currentIndex))
215+ Preview, objectName='preview{}'.format(
216+ preview_list.initialIndex))
213217
214218 @autopilot_logging.log_action(logger.debug)
215− def click_scope_item(self, category, title):
219+ def click_scope_item(self, category, title, press_duration=0.10):
216220 """Click an item from the scope.
217221
218222 :parameter category: The name of the category where the item is.
@@ -220,9 +224,12 @@ class GenericScopeView(emulators.UnityEmulatorBase):
220224
221225 """
222226 category_element = self._get_category_element(category)
223− icon = category_element.wait_select_single('AbstractButton',
224− title=title)
225− self.pointing_device.click_object(icon)
227+ icon = category_element.wait_select_single(
228+ 'AbstractButton', title=title)
229+ list_view = self.select_single(
230+ ListViewWithPageHeader, objectName='categoryListView')
231+ list_view.swipe_child_into_view(icon)
232+ self.pointing_device.click_object(icon, press_duration=press_duration)
226233
227234 def _get_category_element(self, category):
228235 try:
@@ -255,5 +262,5 @@ class GenericScopeView(emulators.UnityEmulatorBase):
255262 return result
256263
257264
258−class Preview(emulators.UnityEmulatorBase):
265+class Preview(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
259266 """Autopilot custom proxy object for generic previews."""
tests/autopilot/unity8/shell/emulators/greeter.py+2−4
@@ -1,7 +1,7 @@
11# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
22#
33# Unity Autopilot Test Suite
4−# Copyright (C) 2012, 2013, 2014 Canonical
4+# Copyright (C) 2012, 2013, 2014, 2015 Canonical
55#
66# This program is free software: you can redistribute it and/or modify
77# it under the terms of the GNU General Public License as published by
@@ -18,12 +18,10 @@
1818#
1919
2020import ubuntuuitoolkit
21−
2221from autopilot.utilities import sleep
23−from unity8.shell.emulators import UnityEmulatorBase
2422
2523
26−class Greeter(UnityEmulatorBase):
24+class Greeter(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
2725 """An emulator that understands the greeter screen."""
2826
2927 def wait_swiped_away(self):
tests/autopilot/unity8/shell/emulators/launcher.py+3−2
@@ -1,7 +1,7 @@
11# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
22#
33# Unity Autopilot Test Suite
4−# Copyright (C) 2012, 2013, 2014 Canonical
4+# Copyright (C) 2012, 2013, 2014, 2015 Canonical
55#
66# This program is free software: you can redistribute it and/or modify
77# it under the terms of the GNU General Public License as published by
@@ -20,6 +20,7 @@
2020import logging
2121
2222import autopilot.logging
23+import ubuntuuitoolkit
2324
2425from unity8.shell import emulators
2526
@@ -27,7 +28,7 @@ from unity8.shell import emulators
2728logger = logging.getLogger(__name__)
2829
2930
30−class Launcher(emulators.UnityEmulatorBase):
31+class Launcher(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
3132
3233 """An emulator that understands the Launcher."""
3334
tests/autopilot/unity8/shell/emulators/main_window.py+14−2
@@ -19,17 +19,17 @@
1919
2020import logging
2121
22+import ubuntuuitoolkit
2223from autopilot import logging as autopilot_logging
2324from autopilot import input
2425
25−from unity8.shell import emulators
2626from unity8.shell.emulators.greeter import Greeter
2727from unity8.shell.emulators.launcher import Launcher
2828
2929logger = logging.getLogger(__name__)
3030
3131
32−class QQuickView(emulators.UnityEmulatorBase):
32+class QQuickView(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
3333 """An emulator class that makes it easy to interact with the shell"""
3434
3535 def get_greeter(self):
@@ -169,6 +169,18 @@ class QQuickView(emulators.UnityEmulatorBase):
169169 objectName='pinPadButton{}'.format(button_id)
170170 )
171171
172+ def get_shell_orientation_angle(self):
173+ return self._get_shell().orientationAngle
174+
175+ def get_shell_orientation(self):
176+ return self._get_shell().orientation
177+
178+ def get_shell_primary_orientation(self):
179+ return self._get_shell().primaryOrientation
180+
181+ def get_shell_native_orientation(self):
182+ return self._get_shell().nativeOrientation
183+
172184 @autopilot_logging.log_action(logger.info)
173185 def wait_for_notification(self):
174186 """Wait for a notification dialog to appear.
tests/autopilot/unity8/shell/tests/__init__.py+32−111
@@ -24,17 +24,17 @@ try:
2424except ImportError:
2525 Gio = None
2626
27+import logging
28+import os
29+
2730from autopilot import introspection
2831from autopilot.platform import model
2932from autopilot.testcase import AutopilotTestCase
3033from autopilot.matchers import Eventually
31−from autopilot.input import Touch
3234from autopilot.display import Display
33−import logging
34−import os.path
35−import subprocess
36−import sys
3735from testtools.matchers import Equals
36+
37+import ubuntuuitoolkit
3838from ubuntuuitoolkit import (
3939 fixture_setup as toolkit_fixtures,
4040 ubuntu_scenarios
@@ -51,7 +51,6 @@ from unity8 import (
5151 fixture_setup,
5252 process_helpers
5353)
54−from unity8.shell import emulators
5554from unity8.shell.emulators import (
5655 dash as dash_helpers,
5756 main_window as main_window_emulator,
@@ -93,36 +92,25 @@ class UnityTestCase(AutopilotTestCase):
9392 @classmethod
9493 def setUpClass(cls):
9594 try:
96− output = subprocess.check_output(
97− ["/sbin/initctl", "status", "unity8"],
98− stderr=subprocess.STDOUT,
99− universal_newlines=True,
100− )
101− except subprocess.CalledProcessError as e:
102− sys.stderr.write(
103− "Error: `initctl status unity8` failed, most probably the "
104− "unity8 session could not be found:\n\n"
105− "{0}\n"
106− "Please install unity8 or copy data/unity8.conf to "
107− "{1}\n".format(
108− e.output,
109− os.path.join(os.getenv("XDG_CONFIG_HOME",
110− os.path.join(os.getenv("HOME"),
111− ".config")
112− ),
113− "upstart")
114− )
115− )
116− sys.exit(1)
117−
118− if "start/" in output:
119− sys.stderr.write(
120− "Error: Unity is currently running, these tests require it to "
121− "be 'stopped'.\n"
122− "Please run this command before running these tests: \n"
123− "initctl stop unity8\n"
95+ is_unity_running = process_helpers.is_job_running('unity8')
96+ except process_helpers.JobError as e:
97+ xdg_config_home = os.getenv(
98+ 'XDG_CONFIG_HOME', os.path.join(os.getenv('HOME'), '.config'))
99+ upstart_config_path = os.path.join(xdg_config_home, 'upstart')
100+ logger.error(
101+ '`initctl status unity8` failed, most probably the '
102+ 'unity8 session could not be found:\n\n'
103+ '{0}\n'
104+ 'Please install unity8 or copy data/unity8.conf to '
105+ '{1}\n'.format(e.output, upstart_config_path)
124106 )
125− sys.exit(2)
107+ raise e
108+ else:
109+ assert not is_unity_running, (
110+ 'Unity is currently running, these tests require it to be '
111+ 'stopped.\n'
112+ 'Please run this command before running these tests: \n'
113+ 'initctl stop unity8\n')
126114
127115 def setUp(self):
128116 super().setUp()
@@ -134,13 +122,6 @@ class UnityTestCase(AutopilotTestCase):
134122 self._data_dirs_mock_enabled = True
135123 self._environment = {}
136124
137− # FIXME: This is a work around re: lp:1238417
138− if model() != "Desktop":
139− from autopilot.input import _uinput
140− _uinput._touch_device = _uinput.create_touch_device()
141− self.addCleanup(_uinput._touch_device.close)
142−
143− self.touch = Touch.create()
144125 self._setup_display_details()
145126
146127 def _setup_display_details(self):
@@ -196,40 +177,6 @@ class UnityTestCase(AutopilotTestCase):
196177 divisor = divisor * 2
197178 return divisor
198179
199− def _patch_environment(self, key, value):
200− """Wrapper for patching env for upstart environment."""
201− try:
202− current_value = subprocess.check_output(
203− ["/sbin/initctl", "get-env", "--global", key],
204− stderr=subprocess.STDOUT,
205− universal_newlines=True,
206− ).rstrip()
207− except subprocess.CalledProcessError:
208− current_value = None
209−
210− subprocess.call([
211− "/sbin/initctl",
212− "set-env",
213− "--global",
214− "%s=%s" % (key, value)
215− ], stderr=subprocess.STDOUT)
216− self.addCleanup(self._upstart_reset_env, key, current_value)
217−
218− def _upstart_reset_env(self, key, value):
219− logger.info("Resetting upstart env %s to %s", key, value)
220− if value is None:
221− subprocess.call(
222− ["/sbin/initctl", "unset-env", key],
223− stderr=subprocess.STDOUT,
224− )
225− else:
226− subprocess.call([
227− "/sbin/initctl",
228− "set-env",
229− "--global",
230− "%s=%s" % (key, value)
231− ], stderr=subprocess.STDOUT)
232−
233180 def launch_unity(self, mode="full-greeter", *args):
234181 """
235182 Launch the unity shell, return a proxy object for it.
@@ -257,21 +204,6 @@ class UnityTestCase(AutopilotTestCase):
257204 if self._data_dirs_mock_enabled:
258205 self._patch_data_dirs()
259206
260− # FIXME: we shouldn't be doing this
261− # $MIR_SOCKET, fallback to $XDG_RUNTIME_DIR/mir_socket and
262− # /tmp/mir_socket as last resort
263− try:
264− os.unlink(
265− os.getenv('MIR_SOCKET',
266− os.path.join(os.getenv('XDG_RUNTIME_DIR', "/tmp"),
267− "mir_socket")))
268− except OSError:
269− pass
270− try:
271− os.unlink("/tmp/mir_socket")
272− except OSError:
273− pass
274−
275207 unity8_cli_args_list = ["--mode={}".format(mode)]
276208 if len(args) != 0:
277209 unity8_cli_args_list += args
@@ -297,26 +229,15 @@ class UnityTestCase(AutopilotTestCase):
297229
298230 def _launch_unity_with_upstart(self, binary_path, args):
299231 logger.info("Starting unity")
300− self._patch_environment("QT_LOAD_TESTABILITY", 1)
301−
302− binary_arg = "BINARY=%s" % binary_path
303− extra_args = "ARGS=%s" % " ".join(args)
304− env_args = ["%s=%s" % (k, v) for k, v in self._environment.items()]
305− all_args = [binary_arg, extra_args] + env_args
232+ self.useFixture(toolkit_fixtures.InitctlEnvironmentVariable(
233+ global_=True, QT_LOAD_TESTABILITY=1))
306234
307− self.addCleanup(self._cleanup_launching_upstart_unity)
308−
309− return process_helpers.restart_unity_with_testability(*all_args)
310−
311− def _cleanup_launching_upstart_unity(self):
312− logger.info("Stopping unity")
313− try:
314− subprocess.check_output(
315− ["/sbin/initctl", "stop", "unity8"],
316− stderr=subprocess.STDOUT
317− )
318− except subprocess.CalledProcessError:
319− logger.warning("Appears unity was already stopped!")
235+ variables = self._environment
236+ variables['ARGS'] = " ".join(args)
237+ launch_unity_fixture = fixture_setup.RestartUnityWithTestability(
238+ binary_path, variables)
239+ self.useFixture(launch_unity_fixture)
240+ return launch_unity_fixture.unity_proxy
320241
321242 def _patch_data_dirs(self):
322243 data_dirs = get_data_dirs(self._data_dirs_mock_enabled)
@@ -369,7 +290,7 @@ class UnityTestCase(AutopilotTestCase):
369290 pid = process_helpers.get_job_pid('unity8-dash')
370291 dash_proxy = introspection.get_proxy_object_for_existing_process(
371292 pid=pid,
372− emulator_base=emulators.UnityEmulatorBase,
293+ emulator_base=ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase
373294 )
374295 dash_app = dash_helpers.DashApp(dash_proxy)
375296 return dash_app.dash
tests/autopilot/unity8/shell/tests/test_emulators.py+6
@@ -120,6 +120,12 @@ class GenericScopeViewEmulatorTestCase(tests.DashBaseTestCase):
120120 self.assertIsInstance(preview, dash_emulators.Preview)
121121 self.assertTrue(preview.isCurrent)
122122
123+ def test_open_preview_of_non_visible_item(self):
124+ """Open an item that requires swiping to make it visible."""
125+ preview = self.generic_scope.open_preview('2', 'Title.2.0')
126+ self.assertIsInstance(preview, dash_emulators.Preview)
127+ self.assertTrue(preview.isCurrent)
128+
123129
124130class DashAppsEmulatorTestCase(tests.DashBaseTestCase):
125131
tests/autopilot/unity8/shell/tests/test_rotation.pynew file+113
@@ -0,0 +1,113 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+#
3+# Unity Autopilot Test Suite
4+# Copyright (C) 2015 Canonical
5+#
6+# This program is free software: you can redistribute it and/or modify
7+# it under the terms of the GNU General Public License as published by
8+# the Free Software Foundation, either version 3 of the License, or
9+# (at your option) any later version.
10+#
11+# This program is distributed in the hope that it will be useful,
12+# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+# GNU General Public License for more details.
15+#
16+# You should have received a copy of the GNU General Public License
17+# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+#
19+
20+"""Tests for shell-rotation"""
21+
22+from autopilot.platform import model
23+from unity8 import (
24+ fixture_setup,
25+ process_helpers
26+)
27+from unity8.shell import tests
28+import logging
29+from testtools.matchers import Equals
30+from autopilot.matchers import Eventually
31+
32+logger = logging.getLogger(__name__)
33+
34+
35+class RotationBase(tests.UnityTestCase):
36+ """Base class for all shell-rotation tests that provides helper methods."""
37+
38+ def setUp(self):
39+ if model() == 'Desktop':
40+ self.skipTest('Test cannot be run on the desktop.')
41+ super().setUp()
42+ self._qml_mock_enabled = False
43+ self._data_dirs_mock_enabled = False
44+
45+ def _assert_change_of_orientation_and_angle(self):
46+ self.assertThat(self.shell_proxy.orientation,
47+ Eventually(Equals(self.orientation)))
48+ self.assertThat(self.shell_proxy.orientationAngle,
49+ Eventually(Equals(self.angle)))
50+
51+
52+class TestFakeSensor(RotationBase):
53+ scenarios = [('top up', {'action': 'top_up', 'orientation': 1}),
54+ ('right up', {'action': 'right_up', 'orientation': 8}),
55+ ('top down', {'action': 'top_down', 'orientation': 4}),
56+ ('left up', {'action': 'left_up', 'orientation': 2})]
57+
58+ def test_fake_sensor(self):
59+ unity_with_sensors = fixture_setup.LaunchUnityWithFakeSensors()
60+ self.useFixture(unity_with_sensors)
61+ process_helpers.unlock_unity(unity_with_sensors.unity_proxy)
62+ fake_sensors = unity_with_sensors.fake_sensors
63+ o_proxy = unity_with_sensors.main_win.select_single('OrientedShell')
64+
65+ fake_sensors.set_orientation(self.action)
66+ self.assertThat(o_proxy.physicalOrientation,
67+ Eventually(Equals(self.orientation), timeout=15))
68+
69+
70+class TestRotationWithApp(RotationBase):
71+ scenarios = [
72+ ('top up, angle 0',
73+ {'action': 'top_up', 'orientation': 1, 'angle': 0}),
74+ ('right up, angle 90',
75+ {'action': 'right_up', 'orientation': 8, 'angle': 90}),
76+ ('top down, angle 180',
77+ {'action': 'top_down', 'orientation': 4, 'angle': 180}),
78+ ('left up, angle 270',
79+ {'action': 'left_up', 'orientation': 2, 'angle': 270})]
80+
81+ def test_rotation_with_webbrowser_app(self):
82+ """Do an orientation-change and verify that an app and the shell
83+ adapted correctly"""
84+
85+ unity_with_sensors = fixture_setup.LaunchUnityWithFakeSensors()
86+ self.useFixture(unity_with_sensors)
87+ process_helpers.unlock_unity(unity_with_sensors.unity_proxy)
88+ fake_sensors = unity_with_sensors.fake_sensors
89+ o_proxy = unity_with_sensors.main_win.select_single('OrientedShell')
90+ self.shell_proxy = unity_with_sensors.main_win.select_single('Shell')
91+
92+ # launch an application
93+ self.launch_upstart_application('webbrowser-app')
94+ unity_with_sensors.main_win.show_dash_from_launcher()
95+ unity_with_sensors.main_win.launch_application('webbrowser-app')
96+
97+ # skip test early, if device doesn't support a certain orientation
98+ if not (self.shell_proxy.orientation & o_proxy.supportedOrientations):
99+ self.skipTest('unsupported orientation ' + self.action)
100+
101+ self.assertThat(
102+ unity_with_sensors.main_win.get_current_focused_app_id(),
103+ Eventually(Equals('webbrowser-app')))
104+
105+ # get default orientation and angle
106+ self.orientation = self.shell_proxy.orientation
107+ self.angle = self.shell_proxy.orientationAngle
108+
109+ # check if fake sensors affect orientation and angle
110+ fake_sensors.set_orientation(self.action)
111+ self.assertThat(o_proxy.physicalOrientation,
112+ Eventually(Equals(self.orientation), timeout=15))
113+ self._assert_change_of_orientation_and_angle()
tests/autopilot/unity8/shell/tests/test_upstart.py+5−4
@@ -1,7 +1,7 @@
11# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
22#
33# Unity Autopilot Test Suite
4−# Copyright (C) 2013, 2014 Canonical
4+# Copyright (C) 2013, 2014, 2015 Canonical
55#
66# This program is free software: you can redistribute it and/or modify
77# it under the terms of the GNU General Public License as published by
@@ -26,13 +26,13 @@ import subprocess
2626import time
2727
2828import fixtures
29−from testtools.matchers import Equals, MismatchError
29+import ubuntuuitoolkit
3030from autopilot.matchers import Eventually
3131from autopilot.introspection import get_proxy_object_for_existing_process
32+from testtools.matchers import Equals, MismatchError
3233from ubuntuuitoolkit import ubuntu_scenarios
3334
3435from unity8 import get_binary_path
35−from unity8.shell.emulators import UnityEmulatorBase
3636from unity8.shell.tests import UnityTestCase
3737
3838import logging
@@ -90,10 +90,11 @@ class UpstartIntegrationTests(UnityTestCase):
9090 self.addCleanup(ensure_stopped)
9191
9292 def _set_proxy(self):
93+ proxy_base = ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase
9394 super()._set_proxy(
9495 get_proxy_object_for_existing_process(
9596 pid=self.process.pid,
96− emulator_base=UnityEmulatorBase,))
97+ emulator_base=proxy_base))
9798
9899 def test_no_sigstop(self):
99100 self.useFixture(
tests/libs/UbuntuGestures/tst_TouchRegistry.cpp+139−74
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 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
@@ -53,8 +53,8 @@ private Q_SLOTS:
5353 void initTestCase() {} // will be called before the first test function is executed
5454 void cleanupTestCase() {} // will be called after the last test function was executed.
5555
56− void init() {} // called right before each and every test function is executed
57− void cleanup() {} // called right after each and every test function is executed
56+ void init(); // called right before each and every test function is executed
57+ void cleanup(); // called right after each and every test function is executed
5858
5959 void requestWithNoCandidates();
6060 void lateCandidateRequestGetsNothing();
@@ -66,11 +66,24 @@ private Q_SLOTS:
6666 void candidatesAndWatchers_2();
6767 void rejectingTouchfterItsEnd();
6868 void removeOldUndecidedCandidates();
69+ void interimOwnerWontGetUnownedTouchEvents();
70+
71+private:
72+ TouchRegistry *touchRegistry;
6973};
7074
75+void tst_TouchRegistry::init()
76+{
77+ touchRegistry = TouchRegistry::instance();
78+}
79+
80+void tst_TouchRegistry::cleanup()
81+{
82+ delete touchRegistry;
83+}
84+
7185void tst_TouchRegistry::requestWithNoCandidates()
7286{
73− TouchRegistry touchRegistry;
7487 DummyCandidate candidate;
7588
7689 {
@@ -82,17 +95,16 @@ void tst_TouchRegistry::requestWithNoCandidates()
8295 Qt::NoModifier,
8396 Qt::TouchPointPressed,
8497 touchPoints);
85− touchRegistry.update(&touchEvent);
98+ touchRegistry->update(&touchEvent);
8699 }
87100
88− touchRegistry.requestTouchOwnership(0, &candidate);
101+ touchRegistry->requestTouchOwnership(0, &candidate);
89102
90103 QVERIFY(candidate.ownedTouches.contains(0));
91104}
92105
93106void tst_TouchRegistry::lateCandidateRequestGetsNothing()
94107{
95− TouchRegistry touchRegistry;
96108 DummyCandidate earlyCandidate;
97109 earlyCandidate.setObjectName("early");
98110 DummyCandidate lateCandidate;
@@ -107,21 +119,21 @@ void tst_TouchRegistry::lateCandidateRequestGetsNothing()
107119 Qt::NoModifier,
108120 Qt::TouchPointPressed,
109121 touchPoints);
110− touchRegistry.update(&touchEvent);
122+ touchRegistry->update(&touchEvent);
111123 }
112124
113− touchRegistry.addCandidateOwnerForTouch(0, &earlyCandidate);
114− touchRegistry.addCandidateOwnerForTouch(0, &lateCandidate);
125+ touchRegistry->addCandidateOwnerForTouch(0, &earlyCandidate);
126+ touchRegistry->addCandidateOwnerForTouch(0, &lateCandidate);
115127
116128 QVERIFY(earlyCandidate.ownedTouches.isEmpty());
117129 QVERIFY(lateCandidate.ownedTouches.isEmpty());
118130
119− touchRegistry.requestTouchOwnership(0, &lateCandidate);
131+ touchRegistry->requestTouchOwnership(0, &lateCandidate);
120132
121133 QVERIFY(earlyCandidate.ownedTouches.isEmpty());
122134 QVERIFY(lateCandidate.ownedTouches.isEmpty());
123135
124− touchRegistry.requestTouchOwnership(0, &earlyCandidate);
136+ touchRegistry->requestTouchOwnership(0, &earlyCandidate);
125137
126138 QVERIFY(earlyCandidate.ownedTouches.contains(0));
127139 QVERIFY(lateCandidate.ownedTouches.isEmpty());
@@ -129,7 +141,6 @@ void tst_TouchRegistry::lateCandidateRequestGetsNothing()
129141
130142void tst_TouchRegistry::lateCandidateGestOwnershipOnceEarlyCandidateQuits()
131143{
132− TouchRegistry touchRegistry;
133144 DummyCandidate earlyCandidate;
134145 DummyCandidate lateCandidate;
135146
@@ -142,21 +153,21 @@ void tst_TouchRegistry::lateCandidateGestOwnershipOnceEarlyCandidateQuits()
142153 Qt::NoModifier,
143154 Qt::TouchPointPressed,
144155 touchPoints);
145− touchRegistry.update(&touchEvent);
156+ touchRegistry->update(&touchEvent);
146157 }
147158
148− touchRegistry.addCandidateOwnerForTouch(0, &earlyCandidate);
149− touchRegistry.addCandidateOwnerForTouch(0, &lateCandidate);
159+ touchRegistry->addCandidateOwnerForTouch(0, &earlyCandidate);
160+ touchRegistry->addCandidateOwnerForTouch(0, &lateCandidate);
150161
151162 QVERIFY(earlyCandidate.ownedTouches.isEmpty());
152163 QVERIFY(lateCandidate.ownedTouches.isEmpty());
153164
154− touchRegistry.requestTouchOwnership(0, &lateCandidate);
165+ touchRegistry->requestTouchOwnership(0, &lateCandidate);
155166
156167 QVERIFY(earlyCandidate.ownedTouches.isEmpty());
157168 QVERIFY(lateCandidate.ownedTouches.isEmpty());
158169
159− touchRegistry.removeCandidateOwnerForTouch(0, &earlyCandidate);
170+ touchRegistry->removeCandidateOwnerForTouch(0, &earlyCandidate);
160171
161172 QVERIFY(earlyCandidate.ownedTouches.isEmpty());
162173 QVERIFY(lateCandidate.ownedTouches.contains(0));
@@ -164,7 +175,6 @@ void tst_TouchRegistry::lateCandidateGestOwnershipOnceEarlyCandidateQuits()
164175
165176void tst_TouchRegistry::dispatchesTouchEventsToCandidates()
166177{
167− TouchRegistry touchRegistry;
168178 QQuickItem rootItem;
169179
170180 DummyCandidate candidate0;
@@ -189,10 +199,10 @@ void tst_TouchRegistry::dispatchesTouchEventsToCandidates()
189199 Qt::NoModifier,
190200 Qt::TouchPointPressed,
191201 touchPoints);
192− touchRegistry.update(&touchEvent);
202+ touchRegistry->update(&touchEvent);
193203 }
194204
195− touchRegistry.addCandidateOwnerForTouch(0, &candidate0);
205+ touchRegistry->addCandidateOwnerForTouch(0, &candidate0);
196206
197207 {
198208 QList<QTouchEvent::TouchPoint> touchPoints;
@@ -207,7 +217,7 @@ void tst_TouchRegistry::dispatchesTouchEventsToCandidates()
207217 Qt::NoModifier,
208218 Qt::TouchPointPressed | Qt::TouchPointMoved,
209219 touchPoints);
210− touchRegistry.update(&touchEvent);
220+ touchRegistry->update(&touchEvent);
211221 }
212222
213223 // candidate0 should have received an update on the touch he's interested on.
@@ -219,7 +229,7 @@ void tst_TouchRegistry::dispatchesTouchEventsToCandidates()
219229 QCOMPARE(candidate0.unownedTouchEvents[0].touchPoints[0].rect().x(), 11 - candidate0.x());
220230 QCOMPARE(candidate0.unownedTouchEvents[0].touchPoints[0].rect().y(), 11 - candidate0.y());
221231
222− touchRegistry.addCandidateOwnerForTouch(1, &candidate1);
232+ touchRegistry->addCandidateOwnerForTouch(1, &candidate1);
223233
224234 {
225235 QList<QTouchEvent::TouchPoint> touchPoints;
@@ -234,7 +244,7 @@ void tst_TouchRegistry::dispatchesTouchEventsToCandidates()
234244 Qt::NoModifier,
235245 Qt::TouchPointPressed | Qt::TouchPointMoved,
236246 touchPoints);
237− touchRegistry.update(&touchEvent);
247+ touchRegistry->update(&touchEvent);
238248 }
239249
240250 // candidate0 gets updates only for touch 0 and
@@ -251,7 +261,6 @@ void tst_TouchRegistry::dispatchesTouchEventsToCandidates()
251261
252262void tst_TouchRegistry::dispatchesTouchEventsToWatchers()
253263{
254− TouchRegistry touchRegistry;
255264 QQuickItem rootItem;
256265
257266 DummyCandidate watcher;
@@ -270,10 +279,10 @@ void tst_TouchRegistry::dispatchesTouchEventsToWatchers()
270279 Qt::NoModifier,
271280 Qt::TouchPointPressed,
272281 touchPoints);
273− touchRegistry.update(&touchEvent);
282+ touchRegistry->update(&touchEvent);
274283 }
275284
276− touchRegistry.addTouchWatcher(0, &watcher);
285+ touchRegistry->addTouchWatcher(0, &watcher);
277286
278287 {
279288 QList<QTouchEvent::TouchPoint> touchPoints;
@@ -288,7 +297,7 @@ void tst_TouchRegistry::dispatchesTouchEventsToWatchers()
288297 Qt::NoModifier,
289298 Qt::TouchPointPressed | Qt::TouchPointMoved,
290299 touchPoints);
291− touchRegistry.update(&touchEvent);
300+ touchRegistry->update(&touchEvent);
292301 }
293302
294303 // watcher should have received an update on the touch he's interested on.
@@ -313,7 +322,7 @@ void tst_TouchRegistry::dispatchesTouchEventsToWatchers()
313322 Qt::NoModifier,
314323 Qt::TouchPointPressed | Qt::TouchPointMoved,
315324 touchPoints);
316− touchRegistry.update(&touchEvent);
325+ touchRegistry->update(&touchEvent);
317326 }
318327
319328 // watcher gets updates only for touch 0
@@ -325,7 +334,6 @@ void tst_TouchRegistry::dispatchesTouchEventsToWatchers()
325334
326335void tst_TouchRegistry::keepDispatchingToWatchersAfterLastCandidateGivesUp()
327336{
328− TouchRegistry touchRegistry;
329337 DummyCandidate item;
330338
331339 {
@@ -337,10 +345,10 @@ void tst_TouchRegistry::keepDispatchingToWatchersAfterLastCandidateGivesUp()
337345 Qt::NoModifier,
338346 Qt::TouchPointPressed,
339347 touchPoints);
340− touchRegistry.update(&touchEvent);
348+ touchRegistry->update(&touchEvent);
341349 }
342350
343− touchRegistry.addCandidateOwnerForTouch(0, &item);
351+ touchRegistry->addCandidateOwnerForTouch(0, &item);
344352
345353 {
346354 QList<QTouchEvent::TouchPoint> touchPoints;
@@ -353,7 +361,7 @@ void tst_TouchRegistry::keepDispatchingToWatchersAfterLastCandidateGivesUp()
353361 Qt::NoModifier,
354362 Qt::TouchPointPressed | Qt::TouchPointMoved,
355363 touchPoints);
356− touchRegistry.update(&touchEvent);
364+ touchRegistry->update(&touchEvent);
357365 }
358366
359367 QCOMPARE(item.unownedTouchEvents.count(), 1);
@@ -361,7 +369,7 @@ void tst_TouchRegistry::keepDispatchingToWatchersAfterLastCandidateGivesUp()
361369 QCOMPARE(item.unownedTouchEvents[0].touchPoints[0].id(), 0);
362370 item.unownedTouchEvents.clear();
363371
364− touchRegistry.addTouchWatcher(1, &item);
372+ touchRegistry->addTouchWatcher(1, &item);
365373
366374 {
367375 QList<QTouchEvent::TouchPoint> touchPoints;
@@ -374,7 +382,7 @@ void tst_TouchRegistry::keepDispatchingToWatchersAfterLastCandidateGivesUp()
374382 Qt::NoModifier,
375383 Qt::TouchPointMoved,
376384 touchPoints);
377− touchRegistry.update(&touchEvent);
385+ touchRegistry->update(&touchEvent);
378386 }
379387
380388 QCOMPARE(item.unownedTouchEvents.count(), 1);
@@ -394,7 +402,7 @@ void tst_TouchRegistry::keepDispatchingToWatchersAfterLastCandidateGivesUp()
394402 Qt::NoModifier,
395403 Qt::TouchPointReleased | Qt::TouchPointMoved,
396404 touchPoints);
397− touchRegistry.update(&touchEvent);
405+ touchRegistry->update(&touchEvent);
398406 }
399407
400408 QCOMPARE(item.unownedTouchEvents.count(), 1);
@@ -403,7 +411,7 @@ void tst_TouchRegistry::keepDispatchingToWatchersAfterLastCandidateGivesUp()
403411 QVERIFY(item.unownedTouchEvents[0].containsTouchWithId(1));
404412 item.unownedTouchEvents.clear();
405413
406− touchRegistry.removeCandidateOwnerForTouch(0, &item);
414+ touchRegistry->removeCandidateOwnerForTouch(0, &item);
407415
408416 {
409417 QList<QTouchEvent::TouchPoint> touchPoints;
@@ -414,7 +422,7 @@ void tst_TouchRegistry::keepDispatchingToWatchersAfterLastCandidateGivesUp()
414422 Qt::NoModifier,
415423 Qt::TouchPointReleased,
416424 touchPoints);
417− touchRegistry.update(&touchEvent);
425+ touchRegistry->update(&touchEvent);
418426 }
419427
420428 QCOMPARE(item.unownedTouchEvents.count(), 1);
@@ -422,7 +430,7 @@ void tst_TouchRegistry::keepDispatchingToWatchersAfterLastCandidateGivesUp()
422430 QVERIFY(item.unownedTouchEvents[0].containsTouchWithId(1));
423431 item.unownedTouchEvents.clear();
424432
425− QVERIFY(touchRegistry.m_touchInfoPool.isEmpty());
433+ QVERIFY(touchRegistry->m_touchInfoPool.isEmpty());
426434}
427435
428436/*
@@ -431,7 +439,6 @@ void tst_TouchRegistry::keepDispatchingToWatchersAfterLastCandidateGivesUp()
431439 */
432440void tst_TouchRegistry::candidatesAndWatchers_1()
433441{
434− TouchRegistry touchRegistry;
435442 DummyCandidate item;
436443
437444 {
@@ -443,10 +450,10 @@ void tst_TouchRegistry::candidatesAndWatchers_1()
443450 Qt::NoModifier,
444451 Qt::TouchPointPressed,
445452 touchPoints);
446− touchRegistry.update(&touchEvent);
453+ touchRegistry->update(&touchEvent);
447454 }
448455
449− touchRegistry.addCandidateOwnerForTouch(0, &item);
456+ touchRegistry->addCandidateOwnerForTouch(0, &item);
450457
451458 {
452459 QList<QTouchEvent::TouchPoint> touchPoints;
@@ -459,14 +466,14 @@ void tst_TouchRegistry::candidatesAndWatchers_1()
459466 Qt::NoModifier,
460467 Qt::TouchPointPressed | Qt::TouchPointMoved,
461468 touchPoints);
462− touchRegistry.update(&touchEvent);
469+ touchRegistry->update(&touchEvent);
463470 }
464471
465− touchRegistry.addTouchWatcher(1, &item);
472+ touchRegistry->addTouchWatcher(1, &item);
466473
467− touchRegistry.removeCandidateOwnerForTouch(0, &item);
474+ touchRegistry->removeCandidateOwnerForTouch(0, &item);
468475
469− touchRegistry.addTouchWatcher(0, &item);
476+ touchRegistry->addTouchWatcher(0, &item);
470477
471478 {
472479 QList<QTouchEvent::TouchPoint> touchPoints;
@@ -479,7 +486,7 @@ void tst_TouchRegistry::candidatesAndWatchers_1()
479486 Qt::NoModifier,
480487 Qt::TouchPointReleased | Qt::TouchPointMoved,
481488 touchPoints);
482− touchRegistry.update(&touchEvent);
489+ touchRegistry->update(&touchEvent);
483490 }
484491
485492 {
@@ -491,10 +498,10 @@ void tst_TouchRegistry::candidatesAndWatchers_1()
491498 Qt::NoModifier,
492499 Qt::TouchPointReleased,
493500 touchPoints);
494− touchRegistry.update(&touchEvent);
501+ touchRegistry->update(&touchEvent);
495502 }
496503
497− QVERIFY(touchRegistry.m_touchInfoPool.isEmpty());
504+ QVERIFY(touchRegistry->m_touchInfoPool.isEmpty());
498505
499506 item.unownedTouchEvents.clear();
500507
@@ -507,7 +514,7 @@ void tst_TouchRegistry::candidatesAndWatchers_1()
507514 Qt::NoModifier,
508515 Qt::TouchPointPressed,
509516 touchPoints);
510− touchRegistry.update(&touchEvent);
517+ touchRegistry->update(&touchEvent);
511518 }
512519
513520 // Haven't made any subscription for that new touch 0.
@@ -520,8 +527,6 @@ void tst_TouchRegistry::candidatesAndWatchers_1()
520527 */
521528void tst_TouchRegistry::candidatesAndWatchers_2()
522529{
523− TouchRegistry touchRegistry;
524−
525530 DummyCandidate directionalDragArea;
526531 directionalDragArea.setObjectName("DirectionalDragArea");
527532
@@ -538,14 +543,14 @@ void tst_TouchRegistry::candidatesAndWatchers_2()
538543 Qt::NoModifier,
539544 Qt::TouchPointPressed,
540545 touchPoints);
541− touchRegistry.update(&touchEvent);
546+ touchRegistry->update(&touchEvent);
542547 }
543548
544549 // [TouchRegistry] addCandidateOwnerForTouch id 0 candidate DirectionalDragArea
545− touchRegistry.addCandidateOwnerForTouch(0, &directionalDragArea);
550+ touchRegistry->addCandidateOwnerForTouch(0, &directionalDragArea);
546551
547552 // [TouchRegistry] requestTouchOwnership id 0 candidate TouchGate
548− touchRegistry.requestTouchOwnership(0, &touchGate);
553+ touchRegistry->requestTouchOwnership(0, &touchGate);
549554
550555 //[TouchRegistry] got TouchUpdate (id:0, state:moved, scenePos:(147,1023)) (id:1, state:pressed, scenePos:(327,792))
551556 {
@@ -559,24 +564,24 @@ void tst_TouchRegistry::candidatesAndWatchers_2()
559564 Qt::NoModifier,
560565 Qt::TouchPointPressed | Qt::TouchPointMoved,
561566 touchPoints);
562− touchRegistry.update(&touchEvent);
567+ touchRegistry->update(&touchEvent);
563568 }
564569
565570 // [TouchRegistry] addTouchWatcher id 1 watcher DirectionalDragArea
566− touchRegistry.addTouchWatcher(1, &directionalDragArea);
571+ touchRegistry->addTouchWatcher(1, &directionalDragArea);
567572
568573 // [TouchRegistry] removeCandidateOwnerForTouch id 0 candidate DirectionalDragArea
569− touchRegistry.removeCandidateOwnerForTouch(0, &directionalDragArea);
574+ touchRegistry->removeCandidateOwnerForTouch(0, &directionalDragArea);
570575
571576 //[TouchRegistry] sending TouchWonershipEvent(id = 0 gained) to candidate TouchGate
572577 QCOMPARE(touchGate.ownedTouches.count(), 1);
573578 QVERIFY(touchGate.ownedTouches.contains(0));
574579
575580 // [TouchRegistry] addTouchWatcher id 0 watcher DirectionalDragArea
576− touchRegistry.addTouchWatcher(0, &directionalDragArea);
581+ touchRegistry->addTouchWatcher(0, &directionalDragArea);
577582
578583 // [TouchRegistry] requestTouchOwnership id 1 candidate TouchGate
579− touchRegistry.requestTouchOwnership(1, &touchGate);
584+ touchRegistry->requestTouchOwnership(1, &touchGate);
580585
581586 //[TouchRegistry] sending TouchWonershipEvent(id = 1 gained) to candidate TouchGate
582587 QCOMPARE(touchGate.ownedTouches.count(), 2);
@@ -597,7 +602,7 @@ void tst_TouchRegistry::candidatesAndWatchers_2()
597602 Qt::NoModifier,
598603 Qt::TouchPointMoved,
599604 touchPoints);
600− touchRegistry.update(&touchEvent);
605+ touchRegistry->update(&touchEvent);
601606 }
602607
603608 //vvvvv DDA Watchers are being ignored from now on vvvvvvv
@@ -623,7 +628,7 @@ void tst_TouchRegistry::candidatesAndWatchers_2()
623628 Qt::NoModifier,
624629 Qt::TouchPointMoved | Qt::TouchPointReleased,
625630 touchPoints);
626− touchRegistry.update(&touchEvent);
631+ touchRegistry->update(&touchEvent);
627632 }
628633
629634 QCOMPARE(directionalDragArea.unownedTouchEvents.count(), 1);
@@ -645,7 +650,7 @@ void tst_TouchRegistry::candidatesAndWatchers_2()
645650 Qt::NoModifier,
646651 Qt::TouchPointReleased,
647652 touchPoints);
648− touchRegistry.update(&touchEvent);
653+ touchRegistry->update(&touchEvent);
649654 }
650655
651656 QCOMPARE(directionalDragArea.unownedTouchEvents.count(), 1);
@@ -657,7 +662,6 @@ void tst_TouchRegistry::candidatesAndWatchers_2()
657662
658663void tst_TouchRegistry::rejectingTouchfterItsEnd()
659664{
660− TouchRegistry touchRegistry;
661665 DummyCandidate earlyCandidate;
662666 DummyCandidate lateCandidate;
663667
@@ -670,16 +674,16 @@ void tst_TouchRegistry::rejectingTouchfterItsEnd()
670674 Qt::NoModifier,
671675 Qt::TouchPointPressed,
672676 touchPoints);
673− touchRegistry.update(&touchEvent);
677+ touchRegistry->update(&touchEvent);
674678 }
675679
676− touchRegistry.addCandidateOwnerForTouch(0, &earlyCandidate);
677− touchRegistry.addCandidateOwnerForTouch(0, &lateCandidate);
680+ touchRegistry->addCandidateOwnerForTouch(0, &earlyCandidate);
681+ touchRegistry->addCandidateOwnerForTouch(0, &lateCandidate);
678682
679683 QVERIFY(earlyCandidate.ownedTouches.isEmpty());
680684 QVERIFY(lateCandidate.ownedTouches.isEmpty());
681685
682− touchRegistry.requestTouchOwnership(0, &lateCandidate);
686+ touchRegistry->requestTouchOwnership(0, &lateCandidate);
683687
684688 QVERIFY(earlyCandidate.ownedTouches.isEmpty());
685689 QVERIFY(lateCandidate.ownedTouches.isEmpty());
@@ -693,25 +697,25 @@ void tst_TouchRegistry::rejectingTouchfterItsEnd()
693697 Qt::NoModifier,
694698 Qt::TouchPointPressed,
695699 touchPoints);
696− touchRegistry.update(&touchEvent);
700+ touchRegistry->update(&touchEvent);
697701 }
698702
699703 QVERIFY(earlyCandidate.ownedTouches.isEmpty());
700704 QVERIFY(lateCandidate.ownedTouches.isEmpty());
701705
702− touchRegistry.removeCandidateOwnerForTouch(0, &earlyCandidate);
706+ touchRegistry->removeCandidateOwnerForTouch(0, &earlyCandidate);
703707
704708 QCOMPARE(lateCandidate.ownedTouches.count(), 1);
705709 QCOMPARE(lateCandidate.ownedTouches.contains(0), 1);
706710
707711 // Check that there's no trace left of touch 0 as we no longer need to keep tabs on it.
708− QVERIFY(!touchRegistry.findTouchInfo(0));
712+ QVERIFY(!touchRegistry->findTouchInfo(0));
709713}
710714
711715void tst_TouchRegistry::removeOldUndecidedCandidates()
712716{
713717 FakeTimerFactory *fakeTimerFactory = new FakeTimerFactory;
714− TouchRegistry *touchRegistry = new TouchRegistry(nullptr, fakeTimerFactory);
718+ touchRegistry->setTimerFactory(fakeTimerFactory);
715719
716720 DummyCandidate undecidedCandidate;
717721 undecidedCandidate.setObjectName("undecided");
@@ -742,14 +746,75 @@ void tst_TouchRegistry::removeOldUndecidedCandidates()
742746
743747 // Simulate that enough time has passed to cause the CandidateInactivityTimer to timeout,
744748 // making TouchRegistry consider that undecidedCantidate defaulted.
745− fakeTimerFactory->makeRunningTimersTimeout();
749+ fakeTimerFactory->updateTime(10000);
746750
747751 QVERIFY(undecidedCandidate.ownedTouches.isEmpty());
748752 QVERIFY(undecidedCandidate.lostTouches.contains(0));
749753 QVERIFY(candidateThatWantsTouch.ownedTouches.contains(0));
750754 QVERIFY(candidateThatWantsTouch.lostTouches.isEmpty());
755+}
751756
752− delete touchRegistry;
757+/*
758+ An item that calls requestTouchOwnership() without first having called addCandidateOwnerForTouch()
759+ is assumed to be the interim owner of the touch point, thus there's no point in sending
760+ UnownedTouchEvents to him as he already gets proper QTouchEvents from QQuickWindow because
761+ he didn't ignore the first QTouchEvent with that touch point.
762+ */
763+void tst_TouchRegistry::interimOwnerWontGetUnownedTouchEvents()
764+{
765+ FakeTimerFactory *fakeTimerFactory = new FakeTimerFactory;
766+ touchRegistry->setTimerFactory(fakeTimerFactory);
767+
768+ DummyCandidate undecidedCandidate;
769+ undecidedCandidate.setObjectName("undecided");
770+
771+ DummyCandidate interimOwner;
772+ interimOwner.setObjectName("interimOwner");
773+
774+ {
775+ QList<QTouchEvent::TouchPoint> touchPoints;
776+ touchPoints.append(QTouchEvent::TouchPoint(0));
777+ touchPoints[0].setState(Qt::TouchPointPressed);
778+ QTouchEvent touchEvent(QEvent::TouchBegin,
779+ 0 /* device */,
780+ Qt::NoModifier,
781+ Qt::TouchPointPressed,
782+ touchPoints);
783+ touchRegistry->update(&touchEvent);
784+ }
785+
786+ touchRegistry->addCandidateOwnerForTouch(0, &undecidedCandidate);
787+ touchRegistry->requestTouchOwnership(0, &interimOwner);
788+
789+ {
790+ QList<QTouchEvent::TouchPoint> touchPoints;
791+ touchPoints.append(QTouchEvent::TouchPoint(0));
792+ touchPoints[0].setState(Qt::TouchPointMoved);
793+ QTouchEvent touchEvent(QEvent::TouchUpdate,
794+ 0 /* device */,
795+ Qt::NoModifier,
796+ Qt::TouchPointMoved,
797+ touchPoints);
798+ touchRegistry->update(&touchEvent);
799+ }
800+
801+ QCOMPARE(undecidedCandidate.unownedTouchEvents.count(), 1);
802+ QCOMPARE(interimOwner.unownedTouchEvents.count(), 0);
803+
804+ {
805+ QList<QTouchEvent::TouchPoint> touchPoints;
806+ touchPoints.append(QTouchEvent::TouchPoint(0));
807+ touchPoints[0].setState(Qt::TouchPointMoved);
808+ QTouchEvent touchEvent(QEvent::TouchUpdate,
809+ 0 /* device */,
810+ Qt::NoModifier,
811+ Qt::TouchPointMoved,
812+ touchPoints);
813+ touchRegistry->update(&touchEvent);
814+ }
815+
816+ QCOMPARE(undecidedCandidate.unownedTouchEvents.count(), 2);
817+ QCOMPARE(interimOwner.unownedTouchEvents.count(), 0);
753818}
754819
755820////////////// TouchMemento //////////
tests/mocks/AccountsService/plugin.h+1−1
@@ -29,7 +29,7 @@ class AccountsServicePlugin : public QQmlExtensionPlugin
2929 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
3030
3131public:
32− void registerTypes(const char *uri);
32+ void registerTypes(const char *uri) override;
3333};
3434
3535#endif
tests/mocks/CMakeLists.txt+1
@@ -41,6 +41,7 @@ add_subdirectory(Ubuntu)
4141add_subdirectory(Unity)
4242add_subdirectory(QtMultimedia)
4343add_subdirectory(Wizard)
44+add_subdirectory(Utils)
4445
4546install(
4647 DIRECTORY data
tests/mocks/GSettings.1.0/fake_gsettings.h+2−2
@@ -78,10 +78,10 @@ public:
7878 ~GSettingsControllerQml();
7979
8080 QString pictureUri() const;
81− void setPictureUri(const QString &str);
81+ Q_INVOKABLE void setPictureUri(const QString &str);
8282
8383 QString usageMode() const;
84− void setUsageMode(const QString &usageMode);
84+ Q_INVOKABLE void setUsageMode(const QString &usageMode);
8585
8686Q_SIGNALS:
8787 void pictureUriChanged(const QString&);
tests/mocks/GSettings.1.0/plugin.cpp+6
@@ -19,8 +19,14 @@
1919
2020#include <QtQml/qqml.h>
2121
22+static QObject* controllerProvider(QQmlEngine* /* engine */, QJSEngine* /* scriptEngine */)
23+{
24+ return GSettingsControllerQml::instance();
25+}
26+
2227void FakeGSettingsQmlPlugin::registerTypes(const char *uri)
2328{
29+ qmlRegisterSingletonType<GSettingsControllerQml>(uri, 1, 0, "GSettingsController", controllerProvider);
2430 qmlRegisterType<GSettingsQml>(uri, 1, 0, "GSettings");
2531 qmlRegisterUncreatableType<GSettingsSchemaQml>(uri, 1, 0, "GSettingsSchema",
2632 "GSettingsSchema can only be used inside of a GSettings component");
tests/mocks/GSettings.1.0/plugin.h+1−1
@@ -25,7 +25,7 @@ class FakeGSettingsQmlPlugin : public QQmlExtensionPlugin
2525 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
2626
2727public:
28− void registerTypes(const char *uri);
28+ void registerTypes(const char *uri) override;
2929};
3030
3131#endif // PLUGIN_H
tests/mocks/LightDM/liblightdm/UsersModel.h+2−2
@@ -53,8 +53,8 @@ public:
5353 BackgroundPathRole
5454 };
5555
56− int rowCount(const QModelIndex &parent) const;
57− QVariant data(const QModelIndex &index, int role) const;
56+ int rowCount(const QModelIndex &parent) const override;
57+ QVariant data(const QModelIndex &index, int role) const override;
5858
5959 QString mockMode() const;
6060 void setMockMode(QString mockMode);
tests/mocks/LightDM/plugin.h+1−1
@@ -26,7 +26,7 @@ class LightDMPlugin : public QQmlExtensionPlugin
2626 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
2727
2828public:
29− void registerTypes(const char *uri);
29+ void registerTypes(const char *uri) override;
3030};
3131
3232#endif
tests/mocks/Lights/plugin.h+1−1
@@ -26,7 +26,7 @@ class PowerdPlugin : public QQmlExtensionPlugin
2626 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
2727
2828public:
29− void registerTypes(const char *uri);
29+ void registerTypes(const char *uri) override;
3030};
3131
3232#endif
tests/mocks/MeeGo/QOfono/plugin.h+1−1
@@ -25,7 +25,7 @@ class BackendPlugin : public QQmlExtensionPlugin
2525 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
2626
2727public:
28− void registerTypes(const char *uri);
28+ void registerTypes(const char *uri) override;
2929};
3030
3131#endif // MOCK_QOFONO_PLUGIN_H
tests/mocks/Powerd/plugin.h+1−1
@@ -28,7 +28,7 @@ class PowerdPlugin : public QQmlExtensionPlugin
2828 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
2929
3030public:
31− void registerTypes(const char *uri);
31+ void registerTypes(const char *uri) override;
3232};
3333
3434#endif
tests/mocks/QMenuModel/plugin.h+1−1
@@ -28,7 +28,7 @@ class QMenuModelPlugin : public QQmlExtensionPlugin
2828 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
2929
3030public:
31− void registerTypes(const char *uri);
31+ void registerTypes(const char *uri) override;
3232};
3333
3434#endif
tests/mocks/QMenuModel/unitymenumodel.h+6−6
@@ -61,12 +61,12 @@ public:
6161
6262 QString nameOwner() const;
6363
64− int rowCount(const QModelIndex &parent = QModelIndex()) const;
65− int columnCount(const QModelIndex &parent = QModelIndex()) const;
66− QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
67− QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const;
68− QModelIndex parent(const QModelIndex &index) const;
69− QHash<int, QByteArray> roleNames() const;
64+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
65+ int columnCount(const QModelIndex &parent = QModelIndex()) const override;
66+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
67+ QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const override;
68+ QModelIndex parent(const QModelIndex &index) const override;
69+ QHash<int, QByteArray> roleNames() const override;
7070
7171 Q_INVOKABLE QObject * submenu(int position, QQmlComponent* actionStateParser = nullptr);
7272 Q_INVOKABLE bool loadExtendedAttributes(int position, const QVariantMap &schema);
tests/mocks/QtMultimedia/plugin.h+1−1
@@ -28,7 +28,7 @@ class MockQtMultimediaPlugin : public QQmlExtensionPlugin
2828 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
2929
3030public:
31− void registerTypes(const char *uri);
31+ void registerTypes(const char *uri) override;
3232};
3333
3434#endif
tests/mocks/Ubuntu/Connectivity/plugin.h+2−2
@@ -28,8 +28,8 @@ class QmlConnectivityNetworkingPlugin : public QQmlExtensionPlugin {
2828 Q_INTERFACES(QQmlExtensionInterface)
2929
3030public:
31− void registerTypes(const char *uri);
32− void initializeEngine(QQmlEngine *engine, const char *uri);
31+ void registerTypes(const char *uri) override;
32+ void initializeEngine(QQmlEngine *engine, const char *uri) override;
3333};
3434
3535#endif // QML_CONNECTIVITY_PLUGIN_H
tests/mocks/Ubuntu/DownloadDaemonListener/plugin.h+1−1
@@ -24,7 +24,7 @@ class FakeUbuntuDownloadDaemonListenerQmlPlugin : public QQmlExtensionPlugin
2424 Q_OBJECT
2525 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
2626public:
27− void registerTypes(const char *uri);
27+ void registerTypes(const char *uri) override;
2828};
2929
3030#endif
tests/mocks/Ubuntu/Payments/plugin.h+1−1
@@ -25,7 +25,7 @@ class BackendPlugin : public QQmlExtensionPlugin
2525 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
2626
2727public:
28− void registerTypes(const char *uri);
28+ void registerTypes(const char *uri) override;
2929};
3030
3131#endif // PAYMENTS_PLUGIN_H
tests/mocks/Ubuntu/SystemImage/plugin.h+1−1
@@ -25,7 +25,7 @@ class BackendPlugin : public QQmlExtensionPlugin
2525 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
2626
2727public:
28− void registerTypes(const char *uri);
28+ void registerTypes(const char *uri) override;
2929};
3030
3131#endif // MOCK_SYSTEMIMAGE_PLUGIN_H
tests/mocks/Ubuntu/SystemSettings/SecurityPrivacy/plugin.h+1−1
@@ -25,7 +25,7 @@ class BackendPlugin : public QQmlExtensionPlugin
2525 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
2626
2727public:
28− void registerTypes(const char *uri);
28+ void registerTypes(const char *uri) override;
2929};
3030
3131#endif // MOCK_SECURITYPRIVACY_PLUGIN_H
tests/mocks/Ubuntu/Telephony/MockCallEntry.h+1−1
@@ -53,7 +53,7 @@ Q_SIGNALS:
5353 void elapsedTimerRunningChanged();
5454
5555protected:
56− void timerEvent(QTimerEvent * event);
56+ void timerEvent(QTimerEvent * event) override;
5757
5858private:
5959 QString m_phoneNumber;
tests/mocks/Ubuntu/Telephony/plugin.h+2−2
@@ -27,8 +27,8 @@ class FakeUbuntuTelephonyQmlPlugin : public QQmlExtensionPlugin
2727 Q_OBJECT
2828 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
2929public:
30− void initializeEngine(QQmlEngine *engine, const char *uri);
31− void registerTypes(const char *uri);
30+ void initializeEngine(QQmlEngine *engine, const char *uri) override;
31+ void registerTypes(const char *uri) override;
3232
3333private:
3434 QQmlContext *mRootContext;
tests/mocks/Unity/Application/ApplicationInfo.cpp+43−2
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2013-2014 Canonical, Ltd.
2+ * Copyright (C) 2013-2015 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
@@ -34,6 +34,11 @@ ApplicationInfo::ApplicationInfo(const QString &appId, QObject *parent)
3434 , m_focused(false)
3535 , m_fullscreen(false)
3636 , m_session(0)
37+ , m_supportedOrientations(Qt::PortraitOrientation |
38+ Qt::LandscapeOrientation |
39+ Qt::InvertedPortraitOrientation |
40+ Qt::InvertedLandscapeOrientation)
41+ , m_rotatesWindowContents(false)
3742 , m_manualSurfaceCreation(false)
3843{
3944}
@@ -45,6 +50,11 @@ ApplicationInfo::ApplicationInfo(QObject *parent)
4550 , m_focused(false)
4651 , m_fullscreen(false)
4752 , m_session(0)
53+ , m_supportedOrientations(Qt::PortraitOrientation |
54+ Qt::LandscapeOrientation |
55+ Qt::InvertedPortraitOrientation |
56+ Qt::InvertedLandscapeOrientation)
57+ , m_rotatesWindowContents(false)
4858 , m_manualSurfaceCreation(false)
4959{
5060}
@@ -99,9 +109,17 @@ void ApplicationInfo::setIconId(const QString &iconId)
99109
100110void ApplicationInfo::setScreenshotId(const QString &screenshotId)
101111{
102− QString screenshotFileName = QString("%1/Dash/graphics/phone/screenshots/%2@12.png")
112+ QString screenshotFileName;
113+
114+ if (screenshotId.endsWith(".svg")) {
115+ screenshotFileName = QString("%1/Dash/graphics/phone/screenshots/%2")
116+ .arg(qmlDirectory())
117+ .arg(screenshotId);
118+ } else {
119+ screenshotFileName = QString("%1/Dash/graphics/phone/screenshots/%2@12.png")
103120 .arg(qmlDirectory())
104121 .arg(screenshotId);
122+ }
105123
106124 if (screenshotFileName != m_screenshotFileName) {
107125 m_screenshotFileName = screenshotFileName;
@@ -145,6 +163,9 @@ void ApplicationInfo::setState(State value)
145163
146164 if (!m_manualSurfaceCreation && m_state == ApplicationInfo::Running) {
147165 QTimer::singleShot(500, this, SLOT(createSession()));
166+ } else if (m_state == ApplicationInfo::Stopped) {
167+ delete m_session;
168+ m_session = nullptr;
148169 }
149170 }
150171}
@@ -172,3 +193,23 @@ void ApplicationInfo::setManualSurfaceCreation(bool value)
172193 Q_EMIT manualSurfaceCreationChanged(value);
173194 }
174195}
196+
197+Qt::ScreenOrientations ApplicationInfo::supportedOrientations() const
198+{
199+ return m_supportedOrientations;
200+}
201+
202+void ApplicationInfo::setSupportedOrientations(Qt::ScreenOrientations orientations)
203+{
204+ m_supportedOrientations = orientations;
205+}
206+
207+bool ApplicationInfo::rotatesWindowContents() const
208+{
209+ return m_rotatesWindowContents;
210+}
211+
212+void ApplicationInfo::setRotatesWindowContents(bool value)
213+{
214+ m_rotatesWindowContents = value;
215+}
tests/mocks/Unity/Application/ApplicationInfo.h+9−1
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2013-2014 Canonical, Ltd.
2+ * Copyright (C) 2013-2015 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,12 @@ public:
8080 void setFullscreen(bool value);
8181 bool fullscreen() const { return m_fullscreen; }
8282
83+ Qt::ScreenOrientations supportedOrientations() const override;
84+ void setSupportedOrientations(Qt::ScreenOrientations orientations);
85+
86+ bool rotatesWindowContents() const override;
87+ void setRotatesWindowContents(bool value);
88+
8389 bool manualSurfaceCreation() const { return m_manualSurfaceCreation; }
8490 void setManualSurfaceCreation(bool value);
8591
@@ -108,6 +114,8 @@ private:
108114 bool m_focused;
109115 bool m_fullscreen;
110116 Session* m_session;
117+ Qt::ScreenOrientations m_supportedOrientations;
118+ bool m_rotatesWindowContents;
111119
112120 bool m_manualSurfaceCreation;
113121};
tests/mocks/Unity/Application/ApplicationManager.cpp+40−3
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2013-2014 Canonical, Ltd.
2+ * Copyright (C) 2013-2015 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
@@ -143,7 +143,7 @@ QModelIndex ApplicationManager::findIndex(ApplicationInfo* application)
143143}
144144
145145void ApplicationManager::add(ApplicationInfo *application) {
146− if (!application) {
146+ if (!application || m_runningApplications.contains(application)) {
147147 return;
148148 }
149149
@@ -348,6 +348,13 @@ void ApplicationManager::unfocusCurrentApplication()
348348
349349void ApplicationManager::buildListOfAvailableApplications()
350350{
351+ /*
352+ ATTENTION!
353+ Be careful when changing application properties here as some qmltests
354+ rely on them being the way it's specified here (e.g. that camera-app
355+ is fullscreen, that twitter-webapp can rotate in all directions, etc)
356+ */
357+
351358 ApplicationInfo *application;
352359
353360 application = new ApplicationInfo(this);
@@ -355,6 +362,7 @@ void ApplicationManager::buildListOfAvailableApplications()
355362 application->setName("Unity 8 Mock Dash");
356363 application->setScreenshotId("unity8-dash");
357364 application->setStage(ApplicationInfo::MainStage);
365+ application->setSupportedOrientations(Qt::PrimaryOrientation);
358366 m_availableApplications.append(application);
359367
360368 application = new ApplicationInfo(this);
@@ -363,6 +371,8 @@ void ApplicationManager::buildListOfAvailableApplications()
363371 application->setScreenshotId("dialer");
364372 application->setIconId("dialer-app");
365373 application->setStage(ApplicationInfo::SideStage);
374+ application->setSupportedOrientations(Qt::PortraitOrientation
375+ | Qt::InvertedPortraitOrientation);
366376 m_availableApplications.append(application);
367377
368378 application = new ApplicationInfo(this);
@@ -371,6 +381,11 @@ void ApplicationManager::buildListOfAvailableApplications()
371381 application->setScreenshotId("camera");
372382 application->setIconId("camera");
373383 application->setFullscreen(true);
384+ application->setSupportedOrientations(Qt::PortraitOrientation
385+ | Qt::LandscapeOrientation
386+ | Qt::InvertedPortraitOrientation
387+ | Qt::InvertedLandscapeOrientation);
388+ application->setRotatesWindowContents(true);
374389 m_availableApplications.append(application);
375390
376391 application = new ApplicationInfo(this);
@@ -416,13 +431,35 @@ void ApplicationManager::buildListOfAvailableApplications()
416431 application->setAppId("gmail-webapp");
417432 application->setName("GMail");
418433 application->setIconId("gmail");
434+ application->setScreenshotId("gmail-webapp.svg");
435+ application->setFullscreen(false);
436+ application->setStage(ApplicationInfo::MainStage);
437+ application->setSupportedOrientations(Qt::PortraitOrientation
438+ | Qt::LandscapeOrientation
439+ | Qt::InvertedPortraitOrientation
440+ | Qt::InvertedLandscapeOrientation);
441+ m_availableApplications.append(application);
442+
443+ application = new ApplicationInfo(this);
444+ application->setAppId("music-app");
445+ application->setName("Music");
446+ application->setIconId("soundcloud");
447+ application->setScreenshotId("music");
448+ application->setFullscreen(false);
449+ application->setStage(ApplicationInfo::MainStage);
450+ application->setSupportedOrientations(Qt::PortraitOrientation
451+ | Qt::LandscapeOrientation
452+ | Qt::InvertedPortraitOrientation
453+ | Qt::InvertedLandscapeOrientation);
419454 m_availableApplications.append(application);
420455
421456 application = new ApplicationInfo(this);
422457 application->setAppId("ubuntu-weather-app");
423458 application->setName("Weather");
424459 application->setIconId("weather");
425− application->setStage(ApplicationInfo::SideStage);
460+ application->setScreenshotId("ubuntu-weather-app.svg");
461+ application->setSupportedOrientations(Qt::LandscapeOrientation
462+ | Qt::InvertedLandscapeOrientation);
426463 m_availableApplications.append(application);
427464
428465 application = new ApplicationInfo(this);
tests/mocks/Unity/Application/CMakeLists.txt+1−2
@@ -1,5 +1,3 @@
1−pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=5)
2−
31set(FakeUnityApplicationQml_SOURCES
42 plugin.cpp
53 ApplicationInfo.cpp
@@ -14,6 +12,7 @@ set(FakeUnityApplicationQml_SOURCES
1412 SurfaceManager.cpp
1513 SessionModel.h
1614 UbuntuKeyboardInfo.cpp
15+ VirtualKeyboard.cpp
1716 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
1817 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
1918)
tests/mocks/Unity/Application/MirSurfaceItem.cpp+10−27
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 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
@@ -39,14 +39,12 @@ MirSurfaceItem::MirSurfaceItem(const QString& name,
3939 , m_type(type)
4040 , m_state(state)
4141 , m_live(true)
42− , m_orientation(Qt::PortraitOrientation)
43− , m_touchPressCount(0)
44− , m_touchReleaseCount(0)
42+ , m_orientationAngle(Angle0)
4543 , m_qmlItem(nullptr)
4644 , m_screenshotUrl(screenshot)
45+ , m_touchPressCount(0)
46+ , m_touchReleaseCount(0)
4747{
48− qDebug() << "MirSurfaceItem::MirSurfaceItem() " << this->name();
49−
5048 setAcceptedMouseButtons(Qt::LeftButton | Qt::MiddleButton | Qt::RightButton |
5149 Qt::ExtraButton1 | Qt::ExtraButton2 | Qt::ExtraButton3 | Qt::ExtraButton4 |
5250 Qt::ExtraButton5 | Qt::ExtraButton6 | Qt::ExtraButton7 | Qt::ExtraButton8 |
@@ -55,9 +53,6 @@ MirSurfaceItem::MirSurfaceItem(const QString& name,
5553
5654 QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
5755
58− connect(this, &QQuickItem::focusChanged,
59− this, &MirSurfaceItem::onFocusChanged);
60−
6156 // The assumptions I make here really should hold.
6257 QQuickView *quickView =
6358 qobject_cast<QQuickView*>(QGuiApplication::topLevelWindows()[0]);
@@ -117,17 +112,17 @@ void MirSurfaceItem::release()
117112 }
118113}
119114
120−void MirSurfaceItem::setOrientation(const Qt::ScreenOrientation orientation)
115+void MirSurfaceItem::setOrientationAngle(OrientationAngle angle)
121116{
122− if (m_orientation == orientation)
117+ if (m_orientationAngle == angle)
123118 return;
124119
125− m_orientation = orientation;
120+ m_orientationAngle = angle;
126121
127− QQmlProperty orientationProp(m_qmlItem, "orientation");
128− orientationProp.write(QVariant::fromValue(orientation));
122+ QQmlProperty orientationProp(m_qmlItem, "orientationAngle");
123+ orientationProp.write(QVariant::fromValue(m_orientationAngle));
129124
130− Q_EMIT orientationChanged();
125+ Q_EMIT orientationAngleChanged(m_orientationAngle);
131126}
132127
133128void MirSurfaceItem::setSession(Session* session)
@@ -152,15 +147,6 @@ void MirSurfaceItem::setLive(bool live)
152147 }
153148}
154149
155−void MirSurfaceItem::onFocusChanged()
156−{
157− if (!hasFocus()) {
158− // Causes a crash in tst_Shell.qml, inside the mock Unity.Application itself.
159− // Didn't have time to debug yet.
160− //Q_EMIT inputMethodDismissed();
161− }
162−}
163−
164150void MirSurfaceItem::setState(MirSurfaceItem::State newState)
165151{
166152 if (newState != m_state) {
@@ -197,9 +183,6 @@ void MirSurfaceItem::touchEvent(QTouchEvent * event)
197183 if (event->touchPointStates() & Qt::TouchPointPressed) {
198184 ++m_touchPressCount;
199185 Q_EMIT touchPressCountChanged(m_touchPressCount);
200− // Causes a crash in tst_Shell.qml, inside the mock Unity.Application itself.
201− // Didn't have time to debug yet.
202− // Q_EMIT inputMethodRequested();
203186 } else if (event->touchPointStates() & Qt::TouchPointReleased) {
204187 ++m_touchReleaseCount;
205188 Q_EMIT touchReleaseCountChanged(m_touchReleaseCount);
tests/mocks/Unity/Application/MirSurfaceItem.h+29−29
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 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
@@ -29,17 +29,18 @@ class MirSurfaceItem : public QQuickItem
2929 Q_OBJECT
3030 Q_ENUMS(Type)
3131 Q_ENUMS(State)
32+ Q_ENUMS(OrientationAngle)
3233
3334 Q_PROPERTY(Type type READ type NOTIFY typeChanged)
3435 Q_PROPERTY(State state READ state NOTIFY stateChanged)
3536 Q_PROPERTY(QString name READ name CONSTANT)
3637 Q_PROPERTY(bool live READ live NOTIFY liveChanged)
37− Q_PROPERTY(Qt::ScreenOrientation orientation READ orientation WRITE setOrientation NOTIFY orientationChanged DESIGNABLE false)
38−
39− Q_PROPERTY(int touchPressCount READ touchPressCount WRITE setTouchPressCount NOTIFY touchPressCountChanged
40− DESIGNABLE false)
41− Q_PROPERTY(int touchReleaseCount READ touchReleaseCount WRITE setTouchReleaseCount NOTIFY touchReleaseCountChanged
42− DESIGNABLE false)
38+ Q_PROPERTY(OrientationAngle orientationAngle READ orientationAngle WRITE setOrientationAngle
39+ NOTIFY orientationAngleChanged DESIGNABLE false)
40+ Q_PROPERTY(int touchPressCount READ touchPressCount WRITE setTouchPressCount
41+ NOTIFY touchPressCountChanged DESIGNABLE false)
42+ Q_PROPERTY(int touchReleaseCount READ touchReleaseCount WRITE setTouchReleaseCount
43+ NOTIFY touchReleaseCountChanged DESIGNABLE false)
4344
4445public:
4546 enum Type {
@@ -62,6 +63,13 @@ public:
6263 Fullscreen,
6364 };
6465
66+ enum OrientationAngle {
67+ Angle0 = 0,
68+ Angle90 = 90,
69+ Angle180 = 180,
70+ Angle270 = 270
71+ };
72+
6573 ~MirSurfaceItem();
6674
6775 //getters
@@ -70,9 +78,9 @@ public:
7078 State state() const { return m_state; }
7179 QString name() const { return m_name; }
7280 bool live() const { return m_live; }
73− Qt::ScreenOrientation orientation() const { return m_orientation; }
81+ OrientationAngle orientationAngle() const { return m_orientationAngle; }
7482
75− void setOrientation(const Qt::ScreenOrientation orientation);
83+ void setOrientationAngle(OrientationAngle angle);
7684
7785 void setSession(Session* item);
7886 void setScreenshot(const QUrl& screenshot);
@@ -91,29 +99,14 @@ Q_SIGNALS:
9199 void typeChanged(Type);
92100 void stateChanged(State);
93101 void liveChanged(bool live);
94− void orientationChanged();
102+ void orientationAngleChanged(OrientationAngle angle);
95103 void touchPressCountChanged(int count);
96104 void touchReleaseCountChanged(int count);
97105
98− void inputMethodRequested();
99− void inputMethodDismissed();
100−
101106 // internal mock use
102107 void deregister();
103108
104109protected:
105− void touchEvent(QTouchEvent * event) override;
106−
107− // becaue the default implementation ignores the events
108− void mousePressEvent(QMouseEvent *) override {}
109− void mouseMoveEvent(QMouseEvent *) override {}
110− void mouseReleaseEvent(QMouseEvent *) override {}
111−
112−private Q_SLOTS:
113− void onFocusChanged();
114− void onComponentStatusChanged(QQmlComponent::Status status);
115−
116−private:
117110 explicit MirSurfaceItem(const QString& name,
118111 Type type,
119112 State state,
@@ -121,6 +114,12 @@ private:
121114 const QString &qmlFilePath = QString(),
122115 QQuickItem *parent = 0);
123116
117+ void touchEvent(QTouchEvent * event) override;
118+
119+private Q_SLOTS:
120+ void onComponentStatusChanged(QQmlComponent::Status status);
121+
122+private:
124123 void createQmlContentItem();
125124 void printComponentErrors();
126125
@@ -129,19 +128,20 @@ private:
129128 const Type m_type;
130129 State m_state;
131130 bool m_live;
132− Qt::ScreenOrientation m_orientation;
133− int m_touchPressCount;
134− int m_touchReleaseCount;
131+ OrientationAngle m_orientationAngle;
135132
136133 QQmlComponent *m_qmlContentComponent;
137134 QQuickItem *m_qmlItem;
138135 QUrl m_screenshotUrl;
139136
137+ int m_touchPressCount;
138+ int m_touchReleaseCount;
139+
140140 friend class SurfaceManager;
141141};
142142
143143Q_DECLARE_METATYPE(MirSurfaceItem*)
144144Q_DECLARE_METATYPE(QList<MirSurfaceItem*>)
145−Q_DECLARE_METATYPE(Qt::ScreenOrientation)
145+Q_DECLARE_METATYPE(MirSurfaceItem::OrientationAngle)
146146
147147#endif // MIRSURFACEITEM_H
tests/mocks/Unity/Application/MirSurfaceItem.qml+12−14
@@ -1,5 +1,5 @@
11/*
2− * Copyright 2014 Canonical Ltd.
2+ * Copyright 2014-2015 Canonical Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU Lesser General Public License as published by
@@ -23,19 +23,11 @@ Rectangle {
2323 implicitWidth: units.gu(40)
2424 implicitHeight: units.gu(70)
2525
26− rotation: {
27− if (orientation == Qt.PortraitOrientation) return 0;
28− else if (orientation == Qt.LandscapeOrientation) return 90;
29− else if (orientation == Qt.InvertedPortraitOrientation) return 180;
30− else return 270;
31− }
32− x: parent ? (parent.width - width) / 2 : 0
33− y: parent ? (parent.height - height) / 2 : 0
34− width: parent ? (rotation == 0 || rotation == 180 ? parent.width : parent.height) : implicitWidth
35− height: parent ? (rotation == 0 || rotation == 180 ? parent.height : parent.width) : implicitHeight
26+ width: parent ? parent.width : implicitWidth
27+ height: parent ? parent.height : implicitHeight
3628
3729 property alias screenshotSource: screenshotImage.source
38− property int orientation: Qt.PortraitOrientation
30+ property int orientationAngle
3931
4032 Image {
4133 id: screenshotImage
@@ -50,6 +42,7 @@ Rectangle {
5042 fontSizeMode: Text.Fit
5143 minimumPixelSize: 10
5244 verticalAlignment: Text.AlignVCenter
45+ rotation: surfaceText.rotation
5346 x: surfaceText.x
5447 y: surfaceText.y
5548 width: surfaceText.width
@@ -59,12 +52,17 @@ Rectangle {
5952 }
6053 Text {
6154 id: surfaceText
62− anchors.fill: parent
63− text: "SURFACE"
55+ text: "SURFACE " + root.width + "," + root.height
6456 color: root.parent && root.parent.activeFocus ? "yellow" : "blue"
6557 font.bold: true
6658 fontSizeMode: Text.Fit
6759 minimumPixelSize: 10; font.pixelSize: 200
6860 verticalAlignment: Text.AlignVCenter
61+
62+ rotation: root.orientationAngle
63+ x: (parent.width - width) / 2
64+ y: (parent.height - height) / 2
65+ width: (rotation == 0 || rotation == 180 ? parent.width : parent.height)
66+ height:(rotation == 0 || rotation == 180 ? parent.height : parent.width)
6967 }
7068}
tests/mocks/Unity/Application/ObjectListModel.h+1−1
@@ -76,7 +76,7 @@ public:
7676 return QVariant();
7777 }
7878
79− QHash<int, QByteArray> roleNames() const
79+ QHash<int, QByteArray> roleNames() const override
8080 {
8181 QHash<int, QByteArray> roleNames;
8282 roleNames.insert(RoleModelData, "modelData");
tests/mocks/Unity/Application/SurfaceManager.cpp+4−34
@@ -16,8 +16,9 @@
1616
1717#include "SurfaceManager.h"
1818
19+#include "VirtualKeyboard.h"
20+
1921#include <paths.h>
20−#include "MirSurfaceItem.h"
2122
2223SurfaceManager *SurfaceManager::the_surface_manager = nullptr;
2324
@@ -40,10 +41,7 @@ MirSurfaceItem *SurfaceManager::createSurface(const QString& name,
4041 MirSurfaceItem::State state,
4142 const QUrl& screenshot)
4243{
43− MirSurfaceItem* surface = new MirSurfaceItem(name,
44− type,
45− state,
46− screenshot);
44+ MirSurfaceItem* surface = new MirSurfaceItem(name, type, state, screenshot);
4745 Q_EMIT surfaceCreated(surface);
4846 return surface;
4947}
@@ -57,40 +55,12 @@ void SurfaceManager::registerSurface(MirSurfaceItem *surface)
5755 surface->setLive(false);
5856 Q_EMIT surfaceDestroyed(surface);
5957 });
60− connect(surface, &MirSurfaceItem::inputMethodRequested,
61− this, &SurfaceManager::showInputMethod);
62− connect(surface, &MirSurfaceItem::inputMethodDismissed,
63− this, &SurfaceManager::hideInputMethod);
64−}
65−
66−void SurfaceManager::showInputMethod()
67−{
68− inputMethodSurface()->setState(MirSurfaceItem::Restored);
69−}
70−
71−void SurfaceManager::hideInputMethod()
72−{
73− if (m_virtualKeyboard) {
74− m_virtualKeyboard->setState(MirSurfaceItem::Minimized);
75− }
7658}
7759
7860MirSurfaceItem *SurfaceManager::inputMethodSurface()
7961{
8062 if (!m_virtualKeyboard) {
81−
82− QString screenshotPath = QString("file://%1/Dash/graphics/phone/screenshots/vkb_portrait.png")
83− .arg(qmlDirectory());
84−
85− QString qmlFilePath = QString("%1/Unity/Application/VirtualKeyboard.qml")
86− .arg(mockPluginsDir());
87−
88− m_virtualKeyboard = new MirSurfaceItem(
89− "input-method",
90− MirSurfaceItem::InputMethod,
91− MirSurfaceItem::Minimized,
92− screenshotPath,
93− qmlFilePath);
63+ m_virtualKeyboard = new VirtualKeyboard;
9464 Q_EMIT surfaceCreated(m_virtualKeyboard);
9565 }
9666 return m_virtualKeyboard;
tests/mocks/Unity/Application/SurfaceManager.h+2−5
@@ -20,6 +20,7 @@
2020#include <QObject>
2121
2222#include "MirSurfaceItem.h"
23+#include "VirtualKeyboard.h"
2324
2425class SurfaceManager : public QObject
2526{
@@ -45,13 +46,9 @@ Q_SIGNALS:
4546 void surfaceCreated(MirSurfaceItem *surface);
4647 void surfaceDestroyed(MirSurfaceItem *surface);
4748
48−private Q_SLOTS:
49− void showInputMethod();
50− void hideInputMethod();
51−
5249private:
5350 static SurfaceManager *the_surface_manager;
54− MirSurfaceItem *m_virtualKeyboard;
51+ VirtualKeyboard *m_virtualKeyboard;
5552};
5653
5754#endif // SURFACEMANAGER_H
tests/mocks/Unity/Application/UbuntuKeyboardInfo.cpp+33−1
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
33 *
44 * This program is free software: you can redistribute it and/or modify it under
55 * the terms of the GNU Lesser General Public License version 3, as published by
@@ -26,3 +26,35 @@ UbuntuKeyboardInfo::UbuntuKeyboardInfo(QObject *parent)
2626 m_height(200)
2727{
2828}
29+
30+void UbuntuKeyboardInfo::setX(qreal value)
31+{
32+ if (value != m_x) {
33+ m_x = value;
34+ Q_EMIT xChanged(m_x);
35+ }
36+}
37+
38+void UbuntuKeyboardInfo::setY(qreal value)
39+{
40+ if (value != m_y) {
41+ m_y = value;
42+ Q_EMIT yChanged(m_y);
43+ }
44+}
45+
46+void UbuntuKeyboardInfo::setWidth(qreal value)
47+{
48+ if (value != m_width) {
49+ m_width = value;
50+ Q_EMIT widthChanged(m_width);
51+ }
52+}
53+
54+void UbuntuKeyboardInfo::setHeight(qreal value)
55+{
56+ if (value != m_height) {
57+ m_height = value;
58+ Q_EMIT heightChanged(m_height);
59+ }
60+}
tests/mocks/Unity/Application/UbuntuKeyboardInfo.h+11−5
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
33 *
44 * This program is free software: you can redistribute it and/or modify it under
55 * the terms of the GNU Lesser General Public License version 3, as published by
@@ -22,10 +22,10 @@
2222
2323class UbuntuKeyboardInfo : public QObject {
2424 Q_OBJECT
25− Q_PROPERTY(qreal x READ x NOTIFY xChanged)
26− Q_PROPERTY(qreal y READ y NOTIFY yChanged)
27− Q_PROPERTY(qreal width READ width NOTIFY widthChanged)
28− Q_PROPERTY(qreal height READ height NOTIFY heightChanged)
25+ Q_PROPERTY(qreal x READ x WRITE setX NOTIFY xChanged)
26+ Q_PROPERTY(qreal y READ y WRITE setY NOTIFY yChanged)
27+ Q_PROPERTY(qreal width READ width WRITE setWidth NOTIFY widthChanged)
28+ Q_PROPERTY(qreal height READ height WRITE setHeight NOTIFY heightChanged)
2929public:
3030 UbuntuKeyboardInfo(QObject *parent = 0);
3131 virtual ~UbuntuKeyboardInfo() {}
@@ -34,6 +34,12 @@ public:
3434 qreal width() const { return m_width; }
3535 qreal height() const { return m_height; }
3636
37+ // Just in the fake implementation
38+ void setX(qreal value);
39+ void setY(qreal value);
40+ void setWidth(qreal value);
41+ void setHeight(qreal value);
42+
3743 static UbuntuKeyboardInfo *singleton() {
3844 if (!m_instance) {
3945 m_instance = new UbuntuKeyboardInfo;
tests/mocks/Unity/Application/VirtualKeyboard.cppnew file+44
@@ -0,0 +1,44 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "VirtualKeyboard.h"
18+
19+#include <paths.h>
20+
21+#include <QString>
22+
23+#include <QDebug>
24+
25+VirtualKeyboard::VirtualKeyboard(QQuickItem *parent)
26+ : MirSurfaceItem("input-method",
27+ MirSurfaceItem::InputMethod,
28+ MirSurfaceItem::Minimized,
29+ QString("file://%1/Dash/graphics/phone/screenshots/vkb_portrait.png")
30+ .arg(qmlDirectory()),
31+ QString("%1/Unity/Application/VirtualKeyboard.qml")
32+ .arg(mockPluginsDir()),
33+ parent)
34+{
35+}
36+
37+VirtualKeyboard::~VirtualKeyboard()
38+{
39+}
40+
41+void VirtualKeyboard::touchEvent(QTouchEvent *event)
42+{
43+ event->setAccepted(false);
44+}
tests/mocks/Unity/Application/VirtualKeyboard.hnew file+42
@@ -0,0 +1,42 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef VIRTUALKEYBOARD_H
18+#define VIRTUALKEYBOARD_H
19+
20+#include "MirSurfaceItem.h"
21+#include "MirSurfaceItemModel.h"
22+
23+#include <QQuickItem>
24+#include <QUrl>
25+
26+class Session;
27+
28+class VirtualKeyboard : public MirSurfaceItem
29+{
30+ Q_OBJECT
31+public:
32+ explicit VirtualKeyboard(QQuickItem *parent = 0);
33+ ~VirtualKeyboard();
34+
35+protected:
36+ void touchEvent(QTouchEvent * event) override;
37+};
38+
39+Q_DECLARE_METATYPE(VirtualKeyboard*)
40+Q_DECLARE_METATYPE(QList<VirtualKeyboard*>)
41+
42+#endif // VIRTUALKEYBOARD_H
tests/mocks/Unity/Application/VirtualKeyboard.qml+7−1
@@ -1,5 +1,5 @@
11/*
2− * Copyright 2014 Canonical Ltd.
2+ * Copyright 2014-2015 Canonical Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU Lesser General Public License as published by
@@ -15,6 +15,7 @@
1515 */
1616
1717import QtQuick 2.0
18+import Unity.Application 0.1
1819
1920Item {
2021 implicitWidth: units.gu(40)
@@ -37,4 +38,9 @@ Item {
3738 anchors.fill: parent
3839 }
3940 }
41+
42+ Binding { target: UbuntuKeyboardInfo; property: "x"; value: screenshotImage.x}
43+ Binding { target: UbuntuKeyboardInfo; property: "y"; value: screenshotImage.y}
44+ Binding { target: UbuntuKeyboardInfo; property: "width"; value: screenshotImage.width}
45+ Binding { target: UbuntuKeyboardInfo; property: "height"; value: screenshotImage.height}
4046}
tests/mocks/Unity/Application/plugin.h+2−2
@@ -24,8 +24,8 @@ class FakeUnityApplicationQmlPlugin : public QQmlExtensionPlugin
2424 Q_OBJECT
2525 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
2626public:
27− void registerTypes(const char *uri);
28− void initializeEngine(QQmlEngine *engine, const char *uri);
27+ void registerTypes(const char *uri) override;
28+ void initializeEngine(QQmlEngine *engine, const char *uri) override;
2929};
3030
3131#endif
tests/mocks/Unity/Connectivity/plugin.h+1−1
@@ -25,7 +25,7 @@ class BackendPlugin : public QQmlExtensionPlugin
2525 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
2626
2727public:
28− void registerTypes(const char *uri);
28+ void registerTypes(const char *uri) override;
2929};
3030
3131#endif // MOCK_CONNECTIVITY_PLUGIN_H
tests/mocks/Unity/DashCommunicator/plugin.h+1−1
@@ -28,7 +28,7 @@ class DashCommunicatorPlugin : public QQmlExtensionPlugin
2828 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
2929
3030public:
31− void registerTypes(const char *uri);
31+ void registerTypes(const char *uri) override;
3232};
3333
3434#endif
tests/mocks/Unity/Indicators/fakeindicatorsmodel.h+5−5
@@ -50,11 +50,11 @@ public:
5050 QVariant modelData() const { return m_modelData; }
5151
5252 /* QAbstractItemModel */
53− QHash<int, QByteArray> roleNames() const;
54− int columnCount(const QModelIndex &parent = QModelIndex()) const;
55− QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
56− QModelIndex parent (const QModelIndex &index) const;
57− int rowCount(const QModelIndex &parent = QModelIndex()) const;
53+ QHash<int, QByteArray> roleNames() const override;
54+ int columnCount(const QModelIndex &parent = QModelIndex()) const override;
55+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
56+ QModelIndex parent (const QModelIndex &index) const override;
57+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
5858
5959Q_SIGNALS:
6060 void countChanged();
tests/mocks/Unity/Indicators/fakeplugin.h+1−1
@@ -26,7 +26,7 @@ class IndicatorsFakePlugin : public QQmlExtensionPlugin
2626 Q_OBJECT
2727 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
2828public:
29− void registerTypes(const char *uri);
29+ void registerTypes(const char *uri) override;
3030};
3131
3232#endif // INDICATORS_FAKE_PLUGIN_H
tests/mocks/Unity/Launcher/MockLauncherItem.h+2−2
@@ -43,9 +43,9 @@ public:
4343 int progress() const override;
4444 int count() const override;
4545 bool countVisible() const override;
46− bool focused() const;
46+ bool focused() const override;
4747
48− unity::shell::launcher::QuickListModelInterface *quickList() const;
48+ unity::shell::launcher::QuickListModelInterface *quickList() const override;
4949
5050private:
5151 void setPinned(bool pinned);
tests/mocks/Unity/Launcher/MockLauncherModel.cpp+3−1
@@ -1,5 +1,5 @@
11/*
2− * Copyright 2013 Canonical Ltd.
2+ * Copyright 2013,2015 Canonical Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU Lesser General Public License as published by
@@ -35,6 +35,8 @@ MockLauncherModel::MockLauncherModel(QObject* parent): LauncherModelInterface(pa
3535 item->setProgress(50);
3636 item->setCountVisible(true);
3737 m_list.append(item);
38+ item = new MockLauncherItem("music-app", "/usr/share/applications/music-app.desktop", "Music", "soundcloud", this);
39+ m_list.append(item);
3840 item = new MockLauncherItem("facebook-webapp", "/usr/share/applications/facebook-webapp.desktop", "Facebook", "facebook", this);
3941 item->setProgress(150);
4042 m_list.append(item);
tests/mocks/Unity/Launcher/MockLauncherModel.h+10−10
@@ -34,20 +34,20 @@ public:
3434 MockLauncherModel(QObject* parent = 0);
3535 ~MockLauncherModel();
3636
37− int rowCount(const QModelIndex& parent) const;
37+ int rowCount(const QModelIndex& parent) const override;
3838
39− QVariant data(const QModelIndex& index, int role) const;
39+ QVariant data(const QModelIndex& index, int role) const override;
4040
41− Q_INVOKABLE unity::shell::launcher::LauncherItemInterface *get(int index) const;
42− Q_INVOKABLE void move(int oldIndex, int newIndex);
43− Q_INVOKABLE void pin(const QString &appId, int index = -1);
44− Q_INVOKABLE void requestRemove(const QString &appId);
45− Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex);
46− Q_INVOKABLE void setUser(const QString &username);
41+ Q_INVOKABLE unity::shell::launcher::LauncherItemInterface *get(int index) const override;
42+ Q_INVOKABLE void move(int oldIndex, int newIndex) override;
43+ Q_INVOKABLE void pin(const QString &appId, int index = -1) override;
44+ Q_INVOKABLE void requestRemove(const QString &appId) override;
45+ Q_INVOKABLE void quickListActionInvoked(const QString &appId, int actionIndex) override;
46+ Q_INVOKABLE void setUser(const QString &username) override;
4747 Q_INVOKABLE QString getUrlForAppId(const QString &appId) const;
4848
49− unity::shell::application::ApplicationManagerInterface *applicationManager() const;
50− void setApplicationManager(unity::shell::application::ApplicationManagerInterface *applicationManager);
49+ unity::shell::application::ApplicationManagerInterface *applicationManager() const override;
50+ void setApplicationManager(unity::shell::application::ApplicationManagerInterface *applicationManager) override;
5151
5252 bool onlyPinned() const override;
5353 void setOnlyPinned(bool onlyPinned) override;
tests/mocks/Unity/Launcher/MockQuickListModel.h+2−2
@@ -30,9 +30,9 @@ class MockQuickListModel: public QuickListModelInterface
3030public:
3131 MockQuickListModel(QObject *parent = 0);
3232
33− QVariant data(const QModelIndex &index, int role) const;
33+ QVariant data(const QModelIndex &index, int role) const override;
3434
35− int rowCount(const QModelIndex &parent = QModelIndex()) const;
35+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
3636};
3737
3838#endif // MOCKQUICKLISTMODEL_H
tests/mocks/Unity/Launcher/plugin.h+1−1
@@ -29,7 +29,7 @@ class TestLauncherPlugin : public QQmlExtensionPlugin
2929 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
3030
3131public:
32− void registerTypes(const char* uri);
32+ void registerTypes(const char* uri) override;
3333};
3434
3535#endif // TESTLAUNCHER_PLUGIN_H
tests/mocks/Unity/Notifications/MockActionModel.h+3−3
@@ -32,9 +32,9 @@ public:
3232 ActionModel(QObject *parent=nullptr);
3333 virtual ~ActionModel();
3434
35− virtual int rowCount(const QModelIndex &index) const;
36− virtual QVariant data(const QModelIndex &index, int role) const;
37− virtual QHash<int, QByteArray> roleNames() const;
35+ int rowCount(const QModelIndex &index) const override;
36+ QVariant data(const QModelIndex &index, int role) const override;
37+ QHash<int, QByteArray> roleNames() const override;
3838
3939 Q_ENUMS(ActionsRoles)
4040 enum ActionsRoles {
tests/mocks/Unity/Notifications/MockNotificationModel.h+3−3
@@ -35,9 +35,9 @@ public:
3535 MockNotificationModel(QObject *parent=nullptr);
3636 virtual ~MockNotificationModel();
3737
38− virtual int rowCount(const QModelIndex &parent) const;
39− virtual QVariant data(const QModelIndex &index, int role) const;
40− virtual QHash<int, QByteArray> roleNames() const;
38+ int rowCount(const QModelIndex &parent) const override;
39+ QVariant data(const QModelIndex &index, int role) const override;
40+ QHash<int, QByteArray> roleNames() const override;
4141
4242 Q_INVOKABLE void append(MockNotification* n);
4343 MockNotification* getNotification(int id) const;
tests/mocks/Unity/Notifications/plugin.h+1−1
@@ -28,7 +28,7 @@ class TestNotificationPlugin : public QQmlExtensionPlugin
2828 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
2929
3030public:
31− void registerTypes(const char* uri);
31+ void registerTypes(const char* uri) override;
3232};
3333
3434#endif // TESTNOTIFICATION_PLUGIN_H
tests/mocks/Unity/fake_scopes.h+2−2
@@ -50,8 +50,8 @@ public:
5050 Q_INVOKABLE void clearFavorites();
5151 Q_INVOKABLE void load();
5252
53− QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
54− QModelIndex parent ( const QModelIndex & index ) const;
53+ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
54+ QModelIndex parent ( const QModelIndex & index ) const override;
5555
5656 bool loaded() const override;
5757 int count() const override;
tests/mocks/Unity/fake_unity_plugin.h+1−1
@@ -28,7 +28,7 @@ class FakeUnityPlugin : public QQmlExtensionPlugin
2828 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
2929
3030public:
31− void registerTypes(const char *uri);
31+ void registerTypes(const char *uri) override;
3232};
3333
3434#endif
tests/mocks/Utils/CMakeLists.txtnew file+40
@@ -0,0 +1,40 @@
1+pkg_search_module(GIO REQUIRED gio-2.0)
2+
3+include_directories(
4+ ${CMAKE_CURRENT_SOURCE_DIR}
5+ ${CMAKE_CURRENT_BINARY_DIR}
6+ ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
7+ ${GIO_INCLUDE_DIRS}
8+ ${CMAKE_SOURCE_DIR}/plugins/Utils
9+)
10+
11+set(QMLPLUGIN_SRC
12+ ${CMAKE_SOURCE_DIR}/plugins/Utils/inputwatcher.cpp
13+ ${CMAKE_SOURCE_DIR}/plugins/Utils/qlimitproxymodelqml.cpp
14+ ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp
15+ ${CMAKE_SOURCE_DIR}/plugins/Utils/relativetimeformatter.cpp
16+ ${CMAKE_SOURCE_DIR}/plugins/Utils/timeformatter.cpp
17+ ${CMAKE_SOURCE_DIR}/plugins/Utils/unitymenumodelpaths.cpp
18+ ${CMAKE_SOURCE_DIR}/plugins/Utils/windowkeysfilter.cpp
19+ ${CMAKE_SOURCE_DIR}/plugins/Utils/windowscreenshotprovider.cpp
20+ ${CMAKE_SOURCE_DIR}/plugins/Utils/easingcurve.cpp
21+ constants.cpp
22+ plugin.cpp
23+ windowstatestorage.cpp
24+ )
25+
26+add_library(FakeUtils-qml SHARED
27+ ${QMLPLUGIN_SRC}
28+ )
29+
30+target_link_libraries(FakeUtils-qml ${GIO_LDFLAGS})
31+
32+# Because this is an internal support library, we want
33+# to expose all symbols in it. Consider changing this
34+# either to a static library or just using the
35+# files directly in targets.
36+set_target_properties(FakeUtils-qml PROPERTIES COMPILE_FLAGS -fvisibility=default)
37+
38+qt5_use_modules(FakeUtils-qml Qml Quick DBus Network Gui)
39+
40+add_unity8_mock(Utils 0.1 Utils TARGETS FakeUtils-qml)
tests/mocks/Utils/HomeKeyWatcher.qmlnew file+21
@@ -0,0 +1,21 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+
19+QtObject {
20+ signal activated()
21+}
tests/mocks/Utils/Style.jsnew file+37
@@ -0,0 +1,37 @@
1+/*
2+ * Copyright (C) 2014 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+.pragma library
18+
19+/*! \brief Calculate average luminance of the passed colors
20+
21+ \note If not fully opaque, luminance is dependant on blending.
22+ */
23+function luminance() {
24+ var sum = 0;
25+ // TODO this was originally
26+ // for (var k in arguments) {
27+ // but for some unkown reason was causing crashes in testDash/testDashContent
28+ // investigate when we have some time
29+ for (var k = 0; k < arguments.length; ++k) {
30+ // only way to convert string to color
31+ var c = Qt.lighter(arguments[k], 1.0);
32+
33+ sum += 0.2126 * c.r + 0.7152 * c.g + 0.0722 * c.b;
34+ }
35+
36+ return sum / arguments.length;
37+}
tests/mocks/Utils/Utils.qmltypesnew file+175
@@ -0,0 +1,175 @@
1+import QtQuick.tooling 1.1
2+
3+// This file describes the plugin-supplied types contained in the library.
4+// It is used for QML tooling purposes only.
5+//
6+// This file was auto-generated by:
7+// 'qmlplugindump -notrelocatable Utils 0.1 plugins'
8+
9+Module {
10+ Component {
11+ name: "EasingCurve"
12+ prototype: "QObject"
13+ exports: ["Utils/EasingCurve 0.1"]
14+ exportMetaObjectRevisions: [0]
15+ Property { name: "type"; type: "QEasingCurve::Type" }
16+ Property { name: "period"; type: "double" }
17+ Property { name: "progress"; type: "double" }
18+ Property { name: "value"; type: "double"; isReadonly: true }
19+ }
20+ Component {
21+ name: "QAbstractProxyModel"
22+ prototype: "QAbstractItemModel"
23+ Property { name: "sourceModel"; type: "QAbstractItemModel"; isPointer: true }
24+ }
25+ Component { name: "QIdentityProxyModel"; prototype: "QAbstractProxyModel" }
26+ Component {
27+ name: "QLimitProxyModelQML"
28+ prototype: "QIdentityProxyModel"
29+ exports: ["Utils/LimitProxyModel 0.1"]
30+ exportMetaObjectRevisions: [0]
31+ Property { name: "model"; type: "QAbstractItemModel"; isPointer: true }
32+ Property { name: "limit"; type: "int" }
33+ Property { name: "count"; type: "int"; isReadonly: true }
34+ Signal { name: "totalCountChanged" }
35+ }
36+ Component {
37+ name: "QSortFilterProxyModel"
38+ prototype: "QAbstractProxyModel"
39+ Property { name: "filterRegExp"; type: "QRegExp" }
40+ Property { name: "filterKeyColumn"; type: "int" }
41+ Property { name: "dynamicSortFilter"; type: "bool" }
42+ Property { name: "filterCaseSensitivity"; type: "Qt::CaseSensitivity" }
43+ Property { name: "sortCaseSensitivity"; type: "Qt::CaseSensitivity" }
44+ Property { name: "isSortLocaleAware"; type: "bool" }
45+ Property { name: "sortRole"; type: "int" }
46+ Property { name: "filterRole"; type: "int" }
47+ Method {
48+ name: "setFilterRegExp"
49+ Parameter { name: "pattern"; type: "string" }
50+ }
51+ Method {
52+ name: "setFilterWildcard"
53+ Parameter { name: "pattern"; type: "string" }
54+ }
55+ Method {
56+ name: "setFilterFixedString"
57+ Parameter { name: "pattern"; type: "string" }
58+ }
59+ Method { name: "clear" }
60+ Method { name: "invalidate" }
61+ }
62+ Component {
63+ name: "RelativeTimeFormatter"
64+ prototype: "TimeFormatter"
65+ exports: ["Utils/RelativeTimeFormatter 0.1"]
66+ exportMetaObjectRevisions: [0]
67+ }
68+ Component {
69+ name: "TimeFormatter"
70+ prototype: "QObject"
71+ exports: ["Utils/GDateTimeFormatter 0.1", "Utils/TimeFormatter 0.1"]
72+ exportMetaObjectRevisions: [0, 0]
73+ Property { name: "format"; type: "string" }
74+ Property { name: "timeString"; type: "string"; isReadonly: true }
75+ Property { name: "time"; type: "qlonglong" }
76+ Signal {
77+ name: "formatChanged"
78+ Parameter { name: "format"; type: "string" }
79+ }
80+ Signal {
81+ name: "timeStringChanged"
82+ Parameter { name: "timeString"; type: "string" }
83+ }
84+ Signal {
85+ name: "timeChanged"
86+ Parameter { name: "time"; type: "qlonglong" }
87+ }
88+ }
89+ Component {
90+ name: "UnityMenuModelPaths"
91+ prototype: "QObject"
92+ exports: ["Utils/UnityMenuModelPaths 0.1"]
93+ exportMetaObjectRevisions: [0]
94+ Property { name: "source"; type: "QVariant" }
95+ Property { name: "busName"; type: "QByteArray"; isReadonly: true }
96+ Property { name: "actions"; type: "QVariantMap"; isReadonly: true }
97+ Property { name: "menuObjectPath"; type: "QByteArray"; isReadonly: true }
98+ Property { name: "busNameHint"; type: "QByteArray" }
99+ Property { name: "actionsHint"; type: "QByteArray" }
100+ Property { name: "menuObjectPathHint"; type: "QByteArray" }
101+ }
102+ Component {
103+ name: "UnitySortFilterProxyModelQML"
104+ prototype: "QSortFilterProxyModel"
105+ exports: ["Utils/UnitySortFilterProxyModel 0.1"]
106+ exportMetaObjectRevisions: [0]
107+ Property { name: "model"; type: "QAbstractItemModel"; isPointer: true }
108+ Property { name: "totalCount"; type: "int"; isReadonly: true }
109+ Property { name: "count"; type: "int"; isReadonly: true }
110+ Property { name: "invertMatch"; type: "bool" }
111+ Signal {
112+ name: "invertMatchChanged"
113+ Parameter { type: "bool" }
114+ }
115+ Method {
116+ name: "get"
117+ type: "QVariantMap"
118+ Parameter { name: "row"; type: "int" }
119+ }
120+ Method {
121+ name: "data"
122+ type: "QVariant"
123+ Parameter { name: "row"; type: "int" }
124+ Parameter { name: "role"; type: "int" }
125+ }
126+ Method { name: "count"; type: "int" }
127+ Method {
128+ name: "findFirst"
129+ type: "int"
130+ Parameter { name: "role"; type: "int" }
131+ Parameter { name: "value"; type: "QVariant" }
132+ }
133+ Method {
134+ name: "mapRowToSource"
135+ type: "int"
136+ Parameter { name: "row"; type: "int" }
137+ }
138+ Method {
139+ name: "mapFromSource"
140+ type: "int"
141+ Parameter { name: "row"; type: "int" }
142+ }
143+ Method {
144+ name: "mapToSource"
145+ type: "int"
146+ Parameter { name: "row"; type: "int" }
147+ }
148+ }
149+ Component {
150+ name: "WindowKeysFilter"
151+ defaultProperty: "data"
152+ prototype: "QQuickItem"
153+ exports: ["Utils/WindowKeysFilter 0.1"]
154+ exportMetaObjectRevisions: [0]
155+ }
156+ Component {
157+ name: "WindowStateStorage"
158+ prototype: "QObject"
159+ exports: ["Utils/WindowStateStorage 0.1"]
160+ isCreatable: false
161+ isSingleton: true
162+ exportMetaObjectRevisions: [0]
163+ Method {
164+ name: "saveGeometry"
165+ Parameter { name: "windowId"; type: "string" }
166+ Parameter { name: "rect"; type: "QRect" }
167+ }
168+ Method {
169+ name: "getGeometry"
170+ type: "QRect"
171+ Parameter { name: "windowId"; type: "string" }
172+ Parameter { name: "defaultValue"; type: "QRect" }
173+ }
174+ }
175+}
tests/mocks/Utils/constants.cppnew file+23
@@ -0,0 +1,23 @@
1+/*
2+ * Copyright 2015 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+*/
16+
17+#include "constants.h"
18+
19+Constants::Constants(QObject *parent)
20+ : QObject(parent)
21+{
22+ m_indicatorValueTimeout = 5000;
23+}
tests/mocks/Utils/constants.hnew file+43
@@ -0,0 +1,43 @@
1+/*
2+ * Copyright 2015 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+*/
16+
17+#ifndef CONSTANTS_H
18+#define CONSTANTS_H
19+
20+#include <QObject>
21+
22+/**
23+ * @brief The Constants class
24+ *
25+ * This singleton class exposes contants to Qml
26+ *
27+ */
28+
29+class Constants: public QObject
30+{
31+ Q_OBJECT
32+ Q_PROPERTY(int indicatorValueTimeout READ indicatorValueTimeout CONSTANT)
33+
34+public:
35+ Constants(QObject *parent = 0);
36+
37+ int indicatorValueTimeout() const { return m_indicatorValueTimeout; }
38+
39+private:
40+ int m_indicatorValueTimeout;
41+};
42+
43+#endif
tests/mocks/Utils/plugin.cppnew file+76
@@ -0,0 +1,76 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+// Qt
18+#include <QtQml/qqml.h>
19+#include <QDBusConnection>
20+#include <QQmlContext>
21+#include <QtQuick/QQuickWindow>
22+#include <QDebug>
23+
24+// local
25+#include "plugin.h"
26+#include "windowstatestorage.h"
27+#include "constants.h"
28+
29+// plugin
30+#include <inputwatcher.h>
31+#include <qlimitproxymodelqml.h>
32+#include <unitysortfilterproxymodelqml.h>
33+#include <relativetimeformatter.h>
34+#include <timeformatter.h>
35+#include <unitymenumodelpaths.h>
36+#include <windowkeysfilter.h>
37+#include <windowscreenshotprovider.h>
38+#include <easingcurve.h>
39+
40+static QObject *createWindowStateStorage(QQmlEngine *engine, QJSEngine *scriptEngine)
41+{
42+ Q_UNUSED(engine)
43+ Q_UNUSED(scriptEngine)
44+ return new WindowStateStorage();
45+}
46+
47+static QObject *createConstants(QQmlEngine *engine, QJSEngine *scriptEngine)
48+{
49+ Q_UNUSED(engine)
50+ Q_UNUSED(scriptEngine)
51+ return new Constants();
52+}
53+
54+void FakeUtilsPlugin::registerTypes(const char *uri)
55+{
56+ Q_ASSERT(uri == QLatin1String("Utils"));;
57+ qmlRegisterType<QAbstractItemModel>();
58+ qmlRegisterType<QLimitProxyModelQML>(uri, 0, 1, "LimitProxyModel");
59+ qmlRegisterType<UnitySortFilterProxyModelQML>(uri, 0, 1, "UnitySortFilterProxyModel");
60+ qmlRegisterType<UnityMenuModelPaths>(uri, 0, 1, "UnityMenuModelPaths");
61+ qmlRegisterType<TimeFormatter>(uri, 0, 1, "TimeFormatter");
62+ qmlRegisterType<WindowKeysFilter>(uri, 0, 1, "WindowKeysFilter");
63+ qmlRegisterType<GDateTimeFormatter>(uri, 0, 1, "GDateTimeFormatter");
64+ qmlRegisterType<EasingCurve>(uri, 0, 1, "EasingCurve");
65+ qmlRegisterType<RelativeTimeFormatter>(uri, 0, 1, "RelativeTimeFormatter");
66+ qmlRegisterSingletonType<WindowStateStorage>(uri, 0, 1, "WindowStateStorage", createWindowStateStorage);
67+ qmlRegisterType<InputWatcher>(uri, 0, 1, "InputWatcher");
68+ qmlRegisterSingletonType<Constants>(uri, 0, 1, "Constants", createConstants);
69+}
70+
71+void FakeUtilsPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
72+{
73+ QQmlExtensionPlugin::initializeEngine(engine, uri);
74+
75+ engine->addImageProvider(QLatin1String("window"), new WindowScreenshotProvider);
76+}
tests/mocks/Utils/plugin.hnew file+33
@@ -0,0 +1,33 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef FAKE_UTILS_PLUGIN_H
18+#define FAKE_UTILS_PLUGIN_H
19+
20+#include <QtQml/QQmlEngine>
21+#include <QtQml/QQmlExtensionPlugin>
22+
23+class FakeUtilsPlugin : public QQmlExtensionPlugin
24+{
25+ Q_OBJECT
26+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
27+
28+public:
29+ void registerTypes(const char *uri);
30+ void initializeEngine(QQmlEngine *engine, const char *uri);
31+};
32+
33+#endif
tests/mocks/Utils/qmldirnew file+5
@@ -0,0 +1,5 @@
1+module Utils
2+plugin FakeUtils-qml
3+typeinfo Utils.qmltypes
4+Style 0.1 Style.js
5+HomeKeyWatcher 0.1 HomeKeyWatcher.qml
tests/mocks/Utils/windowstatestorage.cppnew file+48
@@ -0,0 +1,48 @@
1+/*
2+ * Copyright 2015 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "windowstatestorage.h"
18+
19+#include <QRect>
20+
21+WindowStateStorage::WindowStateStorage(QObject *parent):
22+ QObject(parent)
23+{
24+}
25+
26+void WindowStateStorage::setGeometry(const QVariantMap& geometry)
27+{
28+ if (geometry != m_geometry) {
29+ m_geometry = geometry;
30+ Q_EMIT geometryChanged(m_geometry);
31+ }
32+}
33+
34+QVariantMap WindowStateStorage::geometry() const
35+{
36+ return m_geometry;
37+}
38+
39+void WindowStateStorage::saveGeometry(const QString &windowId, const QRect &rect)
40+{
41+ m_geometry[windowId] = rect;
42+}
43+
44+QRect WindowStateStorage::getGeometry(const QString &windowId, const QRect &defaultValue)
45+{
46+ if (!m_geometry.contains(windowId)) return defaultValue;
47+ return m_geometry.value(windowId).toRect();
48+}
tests/mocks/Utils/windowstatestorage.hnew file+38
@@ -0,0 +1,38 @@
1+/*
2+ * Copyright 2015 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include <QObject>
18+#include <QVariantMap>
19+
20+class WindowStateStorage: public QObject
21+{
22+ Q_OBJECT
23+ Q_PROPERTY(QVariantMap geometry READ geometry WRITE setGeometry NOTIFY geometryChanged)
24+public:
25+ WindowStateStorage(QObject *parent = 0);
26+
27+ Q_INVOKABLE void saveGeometry(const QString &windowId, const QRect &rect);
28+ Q_INVOKABLE QRect getGeometry(const QString &windowId, const QRect &defaultValue);
29+
30+Q_SIGNALS:
31+ void geometryChanged(const QVariantMap& geometry);
32+
33+private:
34+ void setGeometry(const QVariantMap& geometry);
35+ QVariantMap geometry() const;
36+
37+ QVariantMap m_geometry;
38+};
tests/mocks/Wizard/mockplugin.h+1−1
@@ -26,7 +26,7 @@ class MockWizardPlugin : public QQmlExtensionPlugin
2626 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
2727
2828public:
29− void registerTypes(const char *uri);
29+ void registerTypes(const char *uri) override;
3030};
3131
3232#endif
tests/mocks/indicator-service/mock-indicator-service.c+76−12
@@ -27,8 +27,15 @@ typedef struct
2727 guint actions_export_id;
2828 guint menu_export_id;
2929 int action_delay;
30+ int change_interval;
3031} IndicatorTestService;
3132
33+typedef struct
34+{
35+ GSimpleAction *action;
36+ GVariant* value;
37+} Action;
38+
3239static void
3340bus_acquired (GDBusConnection *connection,
3441 const gchar *name,
@@ -96,10 +103,10 @@ actual_switch (gpointer user_data)
96103 gboolean state = g_variant_get_boolean (v);
97104 GVariant *new_state = g_variant_new_boolean (state == TRUE ? FALSE : TRUE);
98105
99− g_simple_action_set_state(G_SIMPLE_ACTION(action), new_state);
106+ g_simple_action_set_state(action, new_state);
100107
101108 g_variant_unref (v);
102− g_message ("switching");
109+ g_message ("switching %d", state);
103110 return FALSE;
104111}
105112
@@ -120,13 +127,12 @@ activate_switch (GSimpleAction *action,
120127static gboolean
121128actual_slide (gpointer user_data)
122129{
123− GSimpleAction *action = user_data;
124−
125− GVariant* new_state = g_object_get_qdata (G_OBJECT (action), slider_value_quark ());
130+ Action *slide_action = user_data;
126131
127− g_simple_action_set_state(G_SIMPLE_ACTION(action), new_state);
132+ g_simple_action_set_state(G_SIMPLE_ACTION(slide_action->action), slide_action->value);
133+ g_message ("sliding %f", g_variant_get_double(slide_action->value));
134+ free(slide_action);
128135
129− g_message ("switching");
130136 return FALSE;
131137}
132138
@@ -136,10 +142,40 @@ void change_slider (GSimpleAction *action,
136142{
137143 IndicatorTestService *indicator = user_data;
138144
139− g_object_set_qdata (G_OBJECT (action), slider_value_quark (), g_variant_ref(value));
145+ Action* slide_action = malloc(sizeof(Action));
146+ slide_action->action = action;
147+ slide_action->value = g_variant_ref(value);
140148
141− g_timeout_add(indicator->action_delay, actual_slide, action);
142− g_message ("slide delay %.03f", g_variant_get_double(value));
149+ g_timeout_add(indicator->action_delay, actual_slide, slide_action);
150+ g_message ("slide delay %f", g_variant_get_double(value));
151+}
152+
153+static gboolean
154+change_interval (gpointer user_data)
155+{
156+ g_message ("change interval");
157+
158+ IndicatorTestService *indicator = user_data;
159+
160+ GAction* action_switch = g_action_map_lookup_action(G_ACTION_MAP(indicator->actions), "action.switch");
161+ actual_switch(G_SIMPLE_ACTION(action_switch));
162+
163+ GAction* action_checkbox = g_action_map_lookup_action(G_ACTION_MAP(indicator->actions), "action.checkbox");
164+ actual_switch(G_SIMPLE_ACTION(action_checkbox));
165+
166+ GAction* action_accessPoint = g_action_map_lookup_action(G_ACTION_MAP(indicator->actions), "action.accessPoint");
167+ actual_switch(G_SIMPLE_ACTION(action_accessPoint));
168+
169+ GAction* action_slider = g_action_map_lookup_action(G_ACTION_MAP(indicator->actions), "action.slider");
170+ static double old_value = 0.25;
171+ double new_value = old_value == 0.25 ? 0.75 : 0.25;
172+ old_value = new_value;
173+ Action* slide_action = malloc(sizeof(Action));
174+ slide_action->action = G_SIMPLE_ACTION(action_slider);
175+ slide_action->value = g_variant_new_double(new_value);
176+ actual_slide(slide_action);
177+
178+ return TRUE;
143179}
144180
145181int
@@ -147,6 +183,7 @@ main (int argc, char **argv)
147183{
148184 IndicatorTestService indicator = { 0 };
149185 indicator.action_delay = -1;
186+ indicator.change_interval = -1;
150187 GMenuItem *item;
151188 GMenu *submenu;
152189 GActionEntry entries[] = {
@@ -191,6 +228,27 @@ main (int argc, char **argv)
191228 }
192229 break;
193230 }
231+
232+ case 'c':
233+ {
234+ arg += 2;
235+ if (!arg[0] && i < argc-1) {
236+ i++;
237+ int interval = -1;
238+
239+ if (sscanf(argv[i], "%d", &interval) == 1) {
240+ indicator.change_interval = interval;
241+ } else {
242+ printf("Invalid change interval value: %s\n", argv[i]);
243+ help = 1;
244+ }
245+ } else {
246+ printf("Invalid change interval value: %s\n", argv[i]);
247+ help = 1;
248+ }
249+ break;
250+ }
251+
194252 case 'h':
195253 help = 1;
196254 break;
@@ -201,8 +259,9 @@ main (int argc, char **argv)
201259
202260 if (help) {
203261 printf("Usage: %s [<options>]\n"
204− " -t DELAY Action activation delay\n"
205− " -h Show this help text\n"
262+ " -t DELAY Action activation delay\n"
263+ " -c CHANGE_INTERVAL Interval to change action values\n"
264+ " -h Show this help text\n"
206265 , argv[0]);
207266 return 0;
208267 }
@@ -249,6 +308,11 @@ main (int argc, char **argv)
249308 NULL);
250309
251310 loop = g_main_loop_new (NULL, FALSE);
311+
312+ if (indicator.change_interval != -1) {
313+ g_timeout_add(indicator.change_interval, change_interval, &indicator);
314+ }
315+
252316 g_main_loop_run (loop);
253317
254318 g_object_unref (submenu);
tests/plugins/Dash/cardcreator/1.res+24−10
@@ -1,6 +1,5 @@
11AbstractButton {
22 id: root;
3− property var template;
43 property var components;
54 property var cardData;
65 property var artShapeBorderSource: undefined;
@@ -13,7 +12,7 @@ AbstractButton {
1312 property bool asynchronous: true;
1413 property bool showHeader: true;
1514 implicitWidth: childrenRect.width;
16− enabled: root.template == null ? true : (root.template["non-interactive"] !== undefined ? !root.template["non-interactive"] : true);
15+ enabled: true;
1716
1817onArtShapeBorderSourceChanged: { if (artShapeBorderSource !== undefined && artShapeLoader.item) artShapeLoader.item.borderSource = artShapeBorderSource; }
1918readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
@@ -28,12 +27,29 @@ Item {
2827 active: cardData && cardData["art"] || false;
2928 asynchronous: root.asynchronous;
3029 visible: status == Loader.Ready;
31− sourceComponent: UbuntuShape {
32− id: artShape;
33− objectName: "artShape";
34− radius: "medium";
35− visible: image.status == Image.Ready;
36− readonly property real fixedArtShapeSizeAspect: (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) ? root.fixedArtShapeSize.width / root.fixedArtShapeSize.height : -1;
30+ sourceComponent: Item {
31+ id: artShape;
32+ objectName: "artShape";
33+ property bool doShapeItem: components["art"]["conciergeMode"] !== true;
34+ visible: image.status == Image.Ready;
35+ readonly property alias image: artImage.image;
36+ property alias borderSource: artShapeShape.borderSource;
37+ ShaderEffectSource {
38+ id: artShapeSource;
39+ sourceItem: artImage;
40+ anchors.centerIn: parent;
41+ width: 1;
42+ height: 1;
43+ hideSource: doShapeItem;
44+ }
45+ Shape {
46+ id: artShapeShape;
47+ image: artShapeSource;
48+ anchors.fill: parent;
49+ visible: doShapeItem;
50+ radius: "medium";
51+ }
52+ readonly property real fixedArtShapeSizeAspect: (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) ? root.fixedArtShapeSize.width / root.fixedArtShapeSize.height : -1;
3753 readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : components !== undefined ? components["art"]["aspect-ratio"] : 1;
3854 Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; }
3955 Connections { target: root; onFixedArtShapeSizeChanged: updateWidthHeightBindings(); }
@@ -51,11 +67,9 @@ Item {
5167 objectName: "artImage";
5268 source: cardData && cardData["art"] || "";
5369 asynchronous: root.asynchronous;
54− visible: false;
5570 width: root.width;
5671 height: width / artShape.aspect;
5772 }
58− image: artImage.image;
5973 }
6074 }
6175 }
tests/plugins/Dash/cardcreator/2.res+3−8
@@ -1,6 +1,5 @@
11AbstractButton {
22 id: root;
3− property var template;
43 property var components;
54 property var cardData;
65 property var artShapeBorderSource: undefined;
@@ -13,7 +12,7 @@ AbstractButton {
1312 property bool asynchronous: true;
1413 property bool showHeader: true;
1514 implicitWidth: childrenRect.width;
16− enabled: root.template == null ? true : (root.template["non-interactive"] !== undefined ? !root.template["non-interactive"] : true);
15+ enabled: true;
1716
1817Loader {
1918 id: backgroundLoader;
@@ -33,18 +32,14 @@ Loader {
3332 objectName: "backgroundImage";
3433 source: {
3534 if (cardData && typeof cardData["background"] === "string") return cardData["background"];
36− else if (template && typeof template["card-background"] === "string") return template["card-background"];
37− else return "";
35+ else return "";
3836 }
3937 }
4038 function getColor(index) {
4139 if (cardData && typeof cardData["background"] === "object"
4240 && (cardData["background"]["type"] === "color" || cardData["background"]["type"] === "gradient")) {
4341 return cardData["background"]["elements"][index];
44− } else if (template && typeof template["card-background"] === "object"
45− && (template["card-background"]["type"] === "color" || template["card-background"]["type"] === "gradient")) {
46− return template["card-background"]["elements"][index];
47− } else return undefined;
42+ } else return index === 0 ? "#E9E9E9" : undefined;
4843 }
4944 }
5045 }
tests/plugins/Dash/cardcreator/3.res+34−20
@@ -1,6 +1,5 @@
11AbstractButton {
22 id: root;
3− property var template;
43 property var components;
54 property var cardData;
65 property var artShapeBorderSource: undefined;
@@ -13,7 +12,7 @@ AbstractButton {
1312 property bool asynchronous: true;
1413 property bool showHeader: true;
1514 implicitWidth: childrenRect.width;
16− enabled: root.template == null ? true : (root.template["non-interactive"] !== undefined ? !root.template["non-interactive"] : true);
15+ enabled: true;
1716
1817onArtShapeBorderSourceChanged: { if (artShapeBorderSource !== undefined && artShapeLoader.item) artShapeLoader.item.borderSource = artShapeBorderSource; }
1918readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
@@ -28,34 +27,49 @@ Item {
2827 active: cardData && cardData["art"] || false;
2928 asynchronous: root.asynchronous;
3029 visible: status == Loader.Ready;
31− sourceComponent: UbuntuShape {
32− id: artShape;
33− objectName: "artShape";
34− radius: "medium";
35− visible: image.status == Image.Ready;
36− readonly property real fixedArtShapeSizeAspect: (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) ? root.fixedArtShapeSize.width / root.fixedArtShapeSize.height : -1;
37− readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : components !== undefined ? components["art"]["aspect-ratio"] : 1;
38− Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; }
39− Connections { target: root; onFixedArtShapeSizeChanged: updateWidthHeightBindings(); }
40− function updateWidthHeightBindings() {
41− if (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) {
42− width = root.fixedArtShapeSize.width;
43− height = root.fixedArtShapeSize.height;
44− } else {
30+ sourceComponent: Item {
31+ id: artShape;
32+ objectName: "artShape";
33+ property bool doShapeItem: components["art"]["conciergeMode"] !== true;
34+ visible: image.status == Image.Ready;
35+ readonly property alias image: artImage.image;
36+ property alias borderSource: artShapeShape.borderSource;
37+ ShaderEffectSource {
38+ id: artShapeSource;
39+ sourceItem: artImage;
40+ anchors.centerIn: parent;
41+ width: 1;
42+ height: 1;
43+ hideSource: doShapeItem;
44+ }
45+ Shape {
46+ id: artShapeShape;
47+ image: artShapeSource;
48+ anchors.fill: parent;
49+ visible: doShapeItem;
50+ radius: "medium";
51+ }
52+ readonly property real fixedArtShapeSizeAspect: (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) ? root.fixedArtShapeSize.width / root.fixedArtShapeSize.height : -1;
53+ readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : components !== undefined ? components["art"]["aspect-ratio"] : 1;
54+ Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; }
55+ Connections { target: root; onFixedArtShapeSizeChanged: updateWidthHeightBindings(); }
56+ function updateWidthHeightBindings() {
57+ if (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) {
58+ width = root.fixedArtShapeSize.width;
59+ height = root.fixedArtShapeSize.height;
60+ } else {
4561 width = Qt.binding(function() { return image.status !== Image.Ready ? 0 : image.width });
4662 height = Qt.binding(function() { return image.status !== Image.Ready ? 0 : image.height });
4763 }
48− }
64+ }
4965 CroppedImageMinimumSourceSize {
5066 id: artImage;
5167 objectName: "artImage";
52− source: cardData && cardData["art"] || "";
68+ source: cardData && cardData["art"] || "";
5369 asynchronous: root.asynchronous;
54− visible: false;
5570 width: root.width;
5671 height: width / artShape.aspect;
5772 }
58− image: artImage.image;
5973 }
6074 }
6175 }
tests/plugins/Dash/cardcreator/4.res+1−2
@@ -1,6 +1,5 @@
11AbstractButton {
22 id: root;
3− property var template;
43 property var components;
54 property var cardData;
65 property var artShapeBorderSource: undefined;
@@ -13,7 +12,7 @@ AbstractButton {
1312 property bool asynchronous: true;
1413 property bool showHeader: true;
1514 implicitWidth: childrenRect.width;
16− enabled: root.template == null ? true : (root.template["non-interactive"] !== undefined ? !root.template["non-interactive"] : true);
15+ enabled: true;
1716
1817readonly property size artShapeSize: Qt.size(-1, -1);
1918readonly property int headerHeight: row.height;
tests/plugins/Dash/cardcreator/5.res+35−21
@@ -1,6 +1,5 @@
11AbstractButton {
22 id: root;
3− property var template;
43 property var components;
54 property var cardData;
65 property var artShapeBorderSource: undefined;
@@ -13,7 +12,7 @@ AbstractButton {
1312 property bool asynchronous: true;
1413 property bool showHeader: true;
1514 implicitWidth: childrenRect.width;
16− enabled: root.template == null ? true : (root.template["non-interactive"] !== undefined ? !root.template["non-interactive"] : true);
15+ enabled: false;
1716
1817onArtShapeBorderSourceChanged: { if (artShapeBorderSource !== undefined && artShapeLoader.item) artShapeLoader.item.borderSource = artShapeBorderSource; }
1918readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
@@ -28,34 +27,49 @@ Item {
2827 active: cardData && cardData["art"] || false;
2928 asynchronous: root.asynchronous;
3029 visible: status == Loader.Ready;
31− sourceComponent: UbuntuShape {
32− id: artShape;
33− objectName: "artShape";
34− radius: "medium";
35− visible: image.status == Image.Ready;
36− readonly property real fixedArtShapeSizeAspect: (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) ? root.fixedArtShapeSize.width / root.fixedArtShapeSize.height : -1;
37− readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : components !== undefined ? components["art"]["aspect-ratio"] : 1;
38− Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; }
39− Connections { target: root; onFixedArtShapeSizeChanged: updateWidthHeightBindings(); }
40− function updateWidthHeightBindings() {
41− if (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) {
42− width = root.fixedArtShapeSize.width;
43− height = root.fixedArtShapeSize.height;
44− } else {
30+ sourceComponent: Item {
31+ id: artShape;
32+ objectName: "artShape";
33+ property bool doShapeItem: components["art"]["conciergeMode"] !== true;
34+ visible: image.status == Image.Ready;
35+ readonly property alias image: artImage.image;
36+ property alias borderSource: artShapeShape.borderSource;
37+ ShaderEffectSource {
38+ id: artShapeSource;
39+ sourceItem: artImage;
40+ anchors.centerIn: parent;
41+ width: 1;
42+ height: 1;
43+ hideSource: doShapeItem;
44+ }
45+ Shape {
46+ id: artShapeShape;
47+ image: artShapeSource;
48+ anchors.fill: parent;
49+ visible: doShapeItem;
50+ radius: "medium";
51+ }
52+ readonly property real fixedArtShapeSizeAspect: (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) ? root.fixedArtShapeSize.width / root.fixedArtShapeSize.height : -1;
53+ readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : components !== undefined ? components["art"]["aspect-ratio"] : 1;
54+ Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; }
55+ Connections { target: root; onFixedArtShapeSizeChanged: updateWidthHeightBindings(); }
56+ function updateWidthHeightBindings() {
57+ if (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) {
58+ width = root.fixedArtShapeSize.width;
59+ height = root.fixedArtShapeSize.height;
60+ } else {
4561 width = Qt.binding(function() { return image.status !== Image.Ready ? 0 : image.width });
4662 height = Qt.binding(function() { return image.status !== Image.Ready ? 0 : image.height });
4763 }
48− }
64+ }
4965 CroppedImageMinimumSourceSize {
5066 id: artImage;
5167 objectName: "artImage";
52− source: cardData && cardData["art"] || "";
68+ source: cardData && cardData["art"] || "";
5369 asynchronous: root.asynchronous;
54− visible: false;
5570 width: root.width;
5671 height: width / artShape.aspect;
5772 }
58− image: artImage.image;
5973 }
6074 }
6175 }
@@ -150,5 +164,5 @@ UbuntuShape {
150164 radius: "medium";
151165 borderSource: "radius_pressed.sci"
152166}
153−implicitHeight: subtitleLabel.y + subtitleLabel.height + units.gu(1);
167+implicitHeight: artShapeHolder.height;
154168}
tests/plugins/Dash/cardcreator/5.tst+1−1
@@ -1,3 +1,3 @@
1−template: {"card-layout":"vertical","card-size":"medium","category-layout":"carousel","collapsed-rows":2,"overlay":true}
1+template: {"non-interactive":true,"card-layout":"vertical","card-size":"medium","category-layout":"carousel","collapsed-rows":2,"overlay":true}
22components: {"art":{"aspect-ratio":1,"field":"art"},"subtitle":{"field":"artist"},"title":{"field":"title"},"attributes":{}}
33result: 5.res
tests/plugins/Dash/cardcreator/6.res+3−8
@@ -1,6 +1,5 @@
11AbstractButton {
22 id: root;
3− property var template;
43 property var components;
54 property var cardData;
65 property var artShapeBorderSource: undefined;
@@ -13,7 +12,7 @@ AbstractButton {
1312 property bool asynchronous: true;
1413 property bool showHeader: true;
1514 implicitWidth: childrenRect.width;
16− enabled: root.template == null ? true : (root.template["non-interactive"] !== undefined ? !root.template["non-interactive"] : true);
15+ enabled: true;
1716
1817Loader {
1918 id: backgroundLoader;
@@ -33,18 +32,14 @@ Loader {
3332 objectName: "backgroundImage";
3433 source: {
3534 if (cardData && typeof cardData["background"] === "string") return cardData["background"];
36− else if (template && typeof template["card-background"] === "string") return template["card-background"];
37− else return "";
35+ else return "http://assets.ubuntu.com/sites/ubuntu/latest/u/img/logos/logo-ubuntu-grey.png";
3836 }
3937 }
4038 function getColor(index) {
4139 if (cardData && typeof cardData["background"] === "object"
4240 && (cardData["background"]["type"] === "color" || cardData["background"]["type"] === "gradient")) {
4341 return cardData["background"]["elements"][index];
44− } else if (template && typeof template["card-background"] === "object"
45− && (template["card-background"]["type"] === "color" || template["card-background"]["type"] === "gradient")) {
46− return template["card-background"]["elements"][index];
47− } else return undefined;
42+ } else return index === 0 ? undefined : undefined;
4843 }
4944 }
5045 }
tests/plugins/Dash/cardcreator/6.tst+1−1
@@ -1,3 +1,3 @@
1−template: {"card-background":{"elements":["#E9E9E9"],"type":"color"},"card-layout":"vertical","card-size":"medium","category-layout":"grid","collapsed-rows":2}
1+template: {"card-background":"http://assets.ubuntu.com/sites/ubuntu/latest/u/img/logos/logo-ubuntu-grey.png","card-layout":"vertical","card-size":"medium","category-layout":"grid","collapsed-rows":2}
22components: {"art":{"aspect-ratio":1},"background":{"field":"background"},"subtitle":{"field":"author"},"title":{"field":"title"},"emblem":{"field":"source"},"attributes":{}}
33result: 6.res
tests/plugins/Dash/cardcreator/7.res+3−8
@@ -1,6 +1,5 @@
11AbstractButton {
22 id: root;
3− property var template;
43 property var components;
54 property var cardData;
65 property var artShapeBorderSource: undefined;
@@ -13,7 +12,7 @@ AbstractButton {
1312 property bool asynchronous: true;
1413 property bool showHeader: true;
1514 implicitWidth: childrenRect.width;
16− enabled: root.template == null ? true : (root.template["non-interactive"] !== undefined ? !root.template["non-interactive"] : true);
15+ enabled: true;
1716
1817Loader {
1918 id: backgroundLoader;
@@ -33,18 +32,14 @@ Loader {
3332 objectName: "backgroundImage";
3433 source: {
3534 if (cardData && typeof cardData["background"] === "string") return cardData["background"];
36− else if (template && typeof template["card-background"] === "string") return template["card-background"];
37− else return "";
35+ else return "";
3836 }
3937 }
4038 function getColor(index) {
4139 if (cardData && typeof cardData["background"] === "object"
4240 && (cardData["background"]["type"] === "color" || cardData["background"]["type"] === "gradient")) {
4341 return cardData["background"]["elements"][index];
44− } else if (template && typeof template["card-background"] === "object"
45− && (template["card-background"]["type"] === "color" || template["card-background"]["type"] === "gradient")) {
46− return template["card-background"]["elements"][index];
47− } else return undefined;
42+ } else return index === 0 ? "#E9E9E9" : "#E9AAE9";
4843 }
4944 }
5045 }
tests/plugins/Dash/cardcreator/7.tst+1−1
@@ -1,3 +1,3 @@
1−template: {"card-background":{"elements":["#E9E9E9"],"type":"color"},"card-layout":"vertical","card-size":"medium","category-layout":"grid","collapsed-rows":2}
1+template: {"card-background":{"elements":["#E9E9E9","#E9AAE9"],"type":"color"},"card-layout":"vertical","card-size":"medium","category-layout":"grid","collapsed-rows":2}
22components: {"art":{"aspect-ratio":1},"background":{"field":"background"},"mascot":{"field":"icon"},"subtitle":{"field":"author"},"title":{"field":"title"},"attributes":{"field":"attributes","max-count":2}}
33result: 7.res
tests/plugins/Dash/horizontaljournaltest.cpp+3−3
@@ -32,19 +32,19 @@
3232
3333class WidthModel : public QAbstractListModel {
3434public:
35− QHash<int, QByteArray> roleNames() const
35+ QHash<int, QByteArray> roleNames() const override
3636 {
3737 QHash<int, QByteArray> roles;
3838 roles.insert(Qt::DisplayRole, "modelWidth");
3939 return roles;
4040 }
4141
42− int rowCount(const QModelIndex & /*parent*/) const
42+ int rowCount(const QModelIndex & /*parent*/) const override
4343 {
4444 return m_list.count();
4545 }
4646
47− QVariant data(const QModelIndex &index, int /*role*/) const
47+ QVariant data(const QModelIndex &index, int /*role*/) const override
4848 {
4949 return m_list[index.row()];
5050 }
tests/plugins/Dash/horizontaljournaltry.cpp+1−1
@@ -29,7 +29,7 @@
2929class QWidthModela : public QStringListModel {
3030 Q_OBJECT
3131public:
32− QHash<int, QByteArray> roleNames() const
32+ QHash<int, QByteArray> roleNames() const override
3333 {
3434 QHash<int, QByteArray> roles;
3535 roles.insert(Qt::DisplayRole, "modelWidth");
tests/plugins/Dash/listviewwithpageheadersectionexternalmodeltest.cpp+3−3
@@ -37,7 +37,7 @@ public:
3737 list << "A" << "B" << "C" << "D" << "E" << "F" << "G" << "H";
3838 }
3939
40− int rowCount(const QModelIndex &parent) const
40+ int rowCount(const QModelIndex &parent) const override
4141 {
4242 if (parent.isValid())
4343 return 0;
@@ -45,14 +45,14 @@ public:
4545 return list.count();
4646 }
4747
48− QVariant data(const QModelIndex &index, int role) const
48+ QVariant data(const QModelIndex &index, int role) const override
4949 {
5050 if (role == Qt::SizeHintRole) return 50;
5151 else if (role == Qt::DisplayRole) return list[index.row()];
5252 return QVariant();
5353 }
5454
55− QHash<int, QByteArray> roleNames() const
55+ QHash<int, QByteArray> roleNames() const override
5656 {
5757 QHash<int, QByteArray> hash;
5858 hash.insert(Qt::DisplayRole, "type");
tests/plugins/Dash/listviewwithpageheadertest.cpp+8−2
@@ -973,9 +973,15 @@ private Q_SLOTS:
973973 QCOMPARE(lvwph->contentY(), 1258.);
974974 QCOMPARE(lvwph->m_headerItemShownHeight, 0.);
975975
976− changeContentY(-1700);
977−
976+ changeContentY(-800);
977+ QTRY_COMPARE(lvwph->m_visibleItems.count(), 5);
978+ changeContentY(-400);
979+ QTRY_COMPARE(lvwph->m_visibleItems.count(), 8);
980+ changeContentY(-200);
981+ QTRY_COMPARE(lvwph->m_visibleItems.count(), 10);
982+ changeContentY(-300);
978983 QTRY_COMPARE(lvwph->m_visibleItems.count(), 12);
984+
979985 QCOMPARE(lvwph->m_firstVisibleIndex, 0);
980986 verifyItem(0, -308., 75., true);
981987 verifyItem(1, -233., 75., true);
tests/plugins/Dash/organicgridtest.cpp+2−2
@@ -34,12 +34,12 @@ class DummyModel : public QAbstractListModel {
3434public:
3535 DummyModel() : m_count(0) {}
3636
37− int rowCount(const QModelIndex & /*parent*/) const
37+ int rowCount(const QModelIndex & /*parent*/) const override
3838 {
3939 return m_count;
4040 }
4141
42− QVariant data(const QModelIndex & /*index*/, int /*role*/) const
42+ QVariant data(const QModelIndex & /*index*/, int /*role*/) const override
4343 {
4444 return QVariant();
4545 }
tests/plugins/Dash/organicgridtry.cpp+2−2
@@ -31,12 +31,12 @@ class DummyModel : public QAbstractListModel {
3131public:
3232 DummyModel() : m_count(0) {}
3333
34− int rowCount(const QModelIndex & /*parent*/) const
34+ int rowCount(const QModelIndex & /*parent*/) const override
3535 {
3636 return m_count;
3737 }
3838
39− QVariant data(const QModelIndex & /*index*/, int /*role*/) const
39+ QVariant data(const QModelIndex & /*index*/, int /*role*/) const override
4040 {
4141 return QVariant();
4242 }
tests/plugins/Dash/verticaljournaltest.cpp+3−3
@@ -32,19 +32,19 @@
3232
3333class HeightModel : public QAbstractListModel {
3434public:
35− QHash<int, QByteArray> roleNames() const
35+ QHash<int, QByteArray> roleNames() const override
3636 {
3737 QHash<int, QByteArray> roles;
3838 roles.insert(Qt::DisplayRole, "modelHeight");
3939 return roles;
4040 }
4141
42− int rowCount(const QModelIndex & /*parent*/) const
42+ int rowCount(const QModelIndex & /*parent*/) const override
4343 {
4444 return m_list.count();
4545 }
4646
47− QVariant data(const QModelIndex &index, int /*role*/) const
47+ QVariant data(const QModelIndex &index, int /*role*/) const override
4848 {
4949 return m_list[index.row()];
5050 }
tests/plugins/Dash/verticaljournaltry.cpp+1−1
@@ -29,7 +29,7 @@
2929class QHeightModel : public QStringListModel {
3030 Q_OBJECT
3131public:
32− QHash<int, QByteArray> roleNames() const
32+ QHash<int, QByteArray> roleNames() const override
3333 {
3434 QHash<int, QByteArray> roles;
3535 roles.insert(Qt::DisplayRole, "modelHeight");
tests/plugins/Greeter/Unity/Launcher/CMakeLists.txt+1−1
@@ -1,5 +1,5 @@
11pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
2−pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=5)
2+pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
33
44include_directories(
55 ${CMAKE_CURRENT_SOURCE_DIR}
tests/plugins/LightDM/CMakeLists.txt+35−16
@@ -1,8 +1,38 @@
1+add_definitions(
2+ -DCURRENT_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}"
3+ )
4+include_directories(
5+ ${CMAKE_CURRENT_BINARY_DIR}
6+ )
7+
18add_executable(GreeterDBusTestExec
29 dbus.cpp
310 ${CMAKE_SOURCE_DIR}/plugins/LightDM/Greeter.cpp
411 )
512qt5_use_modules(GreeterDBusTestExec Core DBus Quick Test)
13+target_link_libraries(GreeterDBusTestExec
14+ -L${CMAKE_BINARY_DIR}/tests/mocks/LightDM/liblightdm
15+ -llightdm-qt5-2
16+ )
17+target_include_directories(GreeterDBusTestExec PUBLIC
18+ ${CMAKE_SOURCE_DIR}/plugins/LightDM
19+ ${CMAKE_SOURCE_DIR}/tests/mocks/LightDM
20+ )
21+add_binary_qml_test(GreeterDBus "${CMAKE_BINARY_DIR}/tests/mocks/LightDM/liblightdm" MockLightDM "QML2_IMPORT_PATH=${CMAKE_BINARY_DIR}/tests/mocks")
22+
23+add_executable(GreeterPamTestExec
24+ pam.cpp
25+ ${CMAKE_SOURCE_DIR}/plugins/LightDM/liblightdm/GreeterPrivate.cpp
26+ )
27+qt5_use_modules(GreeterPamTestExec Concurrent Core Test)
28+target_link_libraries(GreeterPamTestExec
29+ integratedLightDM
30+ )
31+target_include_directories(GreeterPamTestExec PUBLIC
32+ ${CMAKE_SOURCE_DIR}/plugins/LightDM/liblightdm
33+ )
34+add_qmltest_target(testGreeterPam "${CMAKE_CURRENT_BINARY_DIR}/GreeterPamTestExec" TRUE FALSE)
35+add_dependencies(testGreeterPam GreeterPamTestExec)
636
737add_executable(GreeterUsersModelTestExec
838 usersmodel.cpp
@@ -10,24 +40,13 @@ add_executable(GreeterUsersModelTestExec
1040 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp
1141 )
1242qt5_use_modules(GreeterUsersModelTestExec Core Test)
13−
14−include_directories(
15− ${CMAKE_CURRENT_BINARY_DIR}
43+target_link_libraries(GreeterUsersModelTestExec
44+ -L${CMAKE_BINARY_DIR}/tests/mocks/LightDM/liblightdm
45+ -llightdm-qt5-2
46+ )
47+target_include_directories(GreeterUsersModelTestExec PUBLIC
1648 ${CMAKE_SOURCE_DIR}/plugins/LightDM
1749 ${CMAKE_SOURCE_DIR}/plugins/Utils
1850 ${CMAKE_SOURCE_DIR}/tests/mocks/LightDM
1951 )
20−
21−target_link_libraries(GreeterDBusTestExec
22− MockLightDM
23− )
24−
25−target_link_libraries(GreeterUsersModelTestExec
26− MockLightDM
27− )
28−
29−add_definitions(-DCURRENT_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}")
30−
31−add_binary_qml_test(GreeterDBus "${CMAKE_BINARY_DIR}/tests/mocks/LightDM/liblightdm" MockLightDM "QML2_IMPORT_PATH=${CMAKE_BINARY_DIR}/tests/mocks")
32−
3352add_binary_qml_test(GreeterUsersModel "${CMAKE_BINARY_DIR}/tests/mocks/LightDM/liblightdm" MockLightDM "LIBLIGHTDM_MOCK_MODE=full")
tests/plugins/LightDM/pam.cppnew file+52
@@ -0,0 +1,52 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "GreeterPrivate.h"
18+
19+#include <QtTest>
20+
21+class GreeterPamTest : public QObject
22+{
23+ Q_OBJECT
24+
25+private Q_SLOTS:
26+
27+ void init()
28+ {
29+ m_greeterpriv = new QLightDM::GreeterPrivate();
30+ }
31+
32+ void cleanup()
33+ {
34+ delete m_greeterpriv;
35+ QTRY_COMPARE(QThreadPool::globalInstance()->activeThreadCount(), 0);
36+ }
37+
38+ void testRapidFireAuthentication()
39+ {
40+ m_greeterpriv->authenticationUser = qgetenv("USER");
41+ for (int i = 0; i < 100; i++) {
42+ m_greeterpriv->handleAuthenticate();
43+ }
44+ }
45+
46+private:
47+ QLightDM::GreeterPrivate *m_greeterpriv;
48+};
49+
50+QTEST_GUILESS_MAIN(GreeterPamTest)
51+
52+#include "pam.moc"
tests/plugins/Ubuntu/Gestures/CMakeLists.txt+5−2
@@ -19,14 +19,16 @@ add_custom_target(UbuntuGesturesTestQmlFiles ALL
1919)
2020
2121add_definitions(-DUBUNTU_GESTURES_PLUGIN_DIR="${CMAKE_BINARY_DIR}/plugins")
22+add_definitions(-DTESTS_UTILS_MODULES_DIR="${CMAKE_BINARY_DIR}/tests/utils/modules")
2223
2324macro(add_gesture_ui_test CLASSNAME)
24− add_executable(${CLASSNAME}TestExec tst_${CLASSNAME}.cpp GestureTest.cpp)
25+ add_executable(${CLASSNAME}TestExec tst_${CLASSNAME}.cpp GestureTest.cpp TestItem.cpp)
2526 qt5_use_modules(${CLASSNAME}TestExec Test Core Qml Gui Quick)
2627 target_link_libraries(${CLASSNAME}TestExec UbuntuGesturesQml UbuntuGestures)
2728
2829 add_binary_qml_test(${CLASSNAME} ${CMAKE_BINARY_DIR}/plugins/Ubuntu/Gestures UbuntuGesturesTestQmlFiles "")
29− add_manual_qml_test(. ${CLASSNAME} IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
30+ add_manual_qml_test(. ${CLASSNAME} IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins
31+ ${CMAKE_BINARY_DIR}/tests/utils/modules)
3032endmacro(add_gesture_ui_test)
3133
3234macro(add_gesture_test CLASSNAME)
@@ -44,6 +46,7 @@ macro(add_gesture_test CLASSNAME)
4446endmacro(add_gesture_test)
4547
4648add_gesture_ui_test(DirectionalDragArea)
49+add_gesture_ui_test(FloatingFlickable)
4750add_gesture_ui_test(PressedOutsideNotifier)
4851add_gesture_ui_test(TouchDispatcher)
4952add_gesture_ui_test(TouchGate)
tests/plugins/Ubuntu/Gestures/DownwardsLauncher.qml+5−19
@@ -37,8 +37,8 @@ Item {
3737
3838 Rectangle {
3939 id: dragAreaRect
40− color: "yellow"
41− opacity: 0.0
40+ opacity: dragArea.dragging ? 0.5 : 0.0
41+ color: "green"
4242 anchors.fill: dragArea
4343 }
4444
@@ -49,24 +49,10 @@ Item {
4949 height: units.gu(5)
5050
5151 direction: Direction.Downwards
52− maxDeviation: units.gu(2)
53− wideningAngle: 10
54− distanceThreshold: units.gu(4)
5552
56− onStatusChanged: {
57− switch (status) {
58− case DirectionalDragArea.WaitingForTouch:
59− dragAreaRect.opacity = 0.0
60− break;
61− case DirectionalDragArea.Undecided:
62− dragAreaRect.color = "yellow"
63− dragAreaRect.opacity = 0.3
64− launcher.y = Qt.binding(launcher.followDragArea)
65− break;
66− default: // DirectionalDragArea.Recognized:
67− dragAreaRect.color = "green"
68− dragAreaRect.opacity = 0.5
69− break;
53+ onDraggingChanged: {
54+ if (dragging) {
55+ launcher.y = Qt.binding(launcher.followDragArea)
7056 }
7157 }
7258
tests/plugins/Ubuntu/Gestures/GestureTest.cpp+14−3
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013,2015 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
@@ -21,8 +21,11 @@
2121#include <QQuickView>
2222#include <QtTest>
2323
24+#include <Timer.h>
2425#include <TouchRegistry.h>
2526
27+using namespace UbuntuGestures;
28+
2629GestureTest::GestureTest(const QString &qmlFilename)
2730 : QObject(), m_device(nullptr), m_view(nullptr), m_qmlFilename(qmlFilename)
2831{
@@ -40,14 +43,18 @@ void GestureTest::initTestCase()
4043void GestureTest::init()
4144{
4245 m_view = new QQuickView;
43− m_view->setResizeMode(QQuickView::SizeViewToRootObject);
46+ m_view->setResizeMode(QQuickView::SizeRootObjectToView);
4447 m_view->engine()->addImportPath(QStringLiteral(UBUNTU_GESTURES_PLUGIN_DIR));
48+ m_view->engine()->addImportPath(QStringLiteral(TESTS_UTILS_MODULES_DIR));
4549 m_view->setSource(QUrl::fromLocalFile(m_qmlFilename));
4650 m_view->show();
4751 QVERIFY(QTest::qWaitForWindowExposed(m_view));
4852 QVERIFY(m_view->rootObject() != 0);
4953
50− m_touchRegistry = new TouchRegistry;
54+ m_fakeTimerFactory = new FakeTimerFactory;
55+
56+ m_touchRegistry = TouchRegistry::instance();
57+ m_touchRegistry->setTimerFactory(m_fakeTimerFactory);
5158 m_view->installEventFilter(m_touchRegistry);
5259
5360 qApp->processEvents();
@@ -59,6 +66,10 @@ void GestureTest::cleanup()
5966 delete m_touchRegistry;
6067 m_touchRegistry = nullptr;
6168
69+ // TouchRegistry will take down the timer factory along with him
70+ // delete m_fakeTimerFactory;
71+ m_fakeTimerFactory = nullptr;
72+
6273 delete m_view;
6374 m_view = nullptr;
6475}
tests/plugins/Ubuntu/Gestures/GestureTest.h+4
@@ -23,6 +23,9 @@
2323class QQuickView;
2424class QTouchDevice;
2525
26+namespace UbuntuGestures {
27+ class FakeTimerFactory;
28+}
2629class TouchRegistry;
2730
2831// C++ std lib
@@ -81,6 +84,7 @@ protected:
8184 QTouchDevice *m_device;
8285 QQuickView *m_view;
8386 TouchRegistry *m_touchRegistry;
87+ UbuntuGestures::FakeTimerFactory *m_fakeTimerFactory;
8488 QString m_qmlFilename;
8589};
8690
tests/plugins/Ubuntu/Gestures/LeftwardsLauncher.qml+6−19
@@ -21,7 +21,7 @@ import Ubuntu.Components 0.1
2121Item {
2222 id: root
2323
24− function reset() { launcher.x = root.width }
24+ function reset() { launcher.x = Qt.binding(function(){return root.width;}); }
2525
2626 Rectangle {
2727 id: launcher
@@ -41,7 +41,8 @@ Item {
4141
4242 Rectangle {
4343 id: dragAreaRect
44− opacity: 0.0
44+ opacity: dragArea.dragging ? 0.5 : 0.0
45+ color: "green"
4546 anchors.fill: dragArea
4647 }
4748
@@ -52,24 +53,10 @@ Item {
5253 width: units.gu(5)
5354
5455 direction: Direction.Leftwards
55− maxDeviation: units.gu(2)
56− wideningAngle: 10
57− distanceThreshold: units.gu(4)
5856
59− onStatusChanged: {
60− switch (status) {
61− case DirectionalDragArea.WaitingForTouch:
62− dragAreaRect.opacity = 0.0
63− break;
64− case DirectionalDragArea.Undecided:
65− dragAreaRect.color = "yellow"
66− dragAreaRect.opacity = 0.3
67− launcher.x = Qt.binding(launcher.followDragArea)
68− break;
69− default: //case DirectionalDragArea.Recognized:
70− dragAreaRect.color = "green"
71− dragAreaRect.opacity = 0.5
72− break;
57+ onDraggingChanged: {
58+ if (dragging) {
59+ launcher.x = Qt.binding(launcher.followDragArea)
7360 }
7461 }
7562
tests/plugins/Ubuntu/Gestures/RightwardsLauncher.qml+5−19
@@ -38,7 +38,8 @@ Item {
3838
3939 Rectangle {
4040 id: dragAreaRect
41− opacity: 0.0
41+ opacity: dragArea.dragging ? 0.5 : 0.0
42+ color: "green"
4243 anchors.fill: dragArea
4344 }
4445
@@ -52,25 +53,10 @@ Item {
5253 width: units.gu(5)
5354
5455 direction: Direction.Rightwards
55− maxDeviation: units.gu(2)
56− wideningAngle: 10
57− distanceThreshold: units.gu(4)
58− minSpeed: 50
5956
60− onStatusChanged: {
61− switch (status) {
62− case DirectionalDragArea.WaitingForTouch:
63− dragAreaRect.opacity = 0.0
64− break;
65− case DirectionalDragArea.Undecided:
66− dragAreaRect.color = "yellow"
67− dragAreaRect.opacity = 0.3
68− launcher.x = Qt.binding(launcher.followDragArea)
69− break;
70− default: //case DirectionalDragArea.Recognized:
71− dragAreaRect.color = "green"
72− dragAreaRect.opacity = 0.5
73− break;
57+ onDraggingChanged: {
58+ if (dragging) {
59+ launcher.x = Qt.binding(launcher.followDragArea)
7460 }
7561 }
7662
tests/plugins/Ubuntu/Gestures/TestItem.cppnew file+30
@@ -0,0 +1,30 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "TestItem.h"
18+
19+#include <QTouchEvent>
20+
21+void TestItem::touchEvent(QTouchEvent *event)
22+{
23+ QTouchEvent *clonedEvent = new QTouchEvent(event->type(),
24+ event->device(),
25+ event->modifiers(),
26+ event->touchPointStates(),
27+ event->touchPoints());
28+
29+ touchEventsReceived.append(QSharedPointer<QTouchEvent>(clonedEvent));
30+}
tests/plugins/Ubuntu/Gestures/TestItem.hnew file+33
@@ -0,0 +1,33 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef UBUNTUGESTURES_TEST_ITEM_H
18+#define UBUNTUGESTURES_TEST_ITEM_H
19+
20+#include <QQuickItem>
21+
22+class TestItem : public QQuickItem
23+{
24+ Q_OBJECT
25+
26+public:
27+ QList<QSharedPointer<QTouchEvent>> touchEventsReceived;
28+
29+protected:
30+ void touchEvent(QTouchEvent *event) override;
31+};
32+
33+#endif // UBUNTUGESTURES_TEST_ITEM_H
tests/plugins/Ubuntu/Gestures/UpwardsLauncher.qml+6−19
@@ -21,7 +21,7 @@ import Ubuntu.Components 0.1
2121Item {
2222 id: root
2323
24− function reset() { launcher.y = root.height }
24+ function reset() { launcher.y = Qt.binding(function(){return root.height;}); }
2525
2626 Rectangle {
2727 id: launcher
@@ -41,7 +41,8 @@ Item {
4141
4242 Rectangle {
4343 id: dragAreaRect
44− opacity: 0.0
44+ opacity: dragArea.dragging ? 0.5 : 0.0
45+ color: "green"
4546 anchors.fill: dragArea
4647 }
4748
@@ -52,24 +53,10 @@ Item {
5253 height: units.gu(5)
5354
5455 direction: Direction.Upwards
55− maxDeviation: units.gu(2)
56− wideningAngle: 10
57− distanceThreshold: units.gu(4)
5856
59− onStatusChanged: {
60− switch (status) {
61− case DirectionalDragArea.WaitingForTouch:
62− dragAreaRect.opacity = 0.0
63− break;
64− case DirectionalDragArea.Undecided:
65− dragAreaRect.color = "yellow"
66− dragAreaRect.opacity = 0.3
67− launcher.y = Qt.binding(launcher.followDragArea)
68− break;
69− default: //case DirectionalDragArea.Recognized:
70− dragAreaRect.color = "green"
71− dragAreaRect.opacity = 0.5
72− break;
57+ onDraggingChanged: {
58+ if (dragging) {
59+ launcher.y = Qt.binding(launcher.followDragArea)
7360 }
7461 }
7562
tests/plugins/Ubuntu/Gestures/tst_AxisVelocityCalculator.cpp+1−1
@@ -24,7 +24,7 @@ class FakeTimeSource : public UbuntuGestures::TimeSource {
2424public:
2525 FakeTimeSource() : m_value(0) {}
2626
27− virtual qint64 msecsSinceReference() {
27+ qint64 msecsSinceReference() override {
2828 return m_value;
2929 }
3030
tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp+481−446
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2013-2014 Canonical, Ltd.
2+ * Copyright (C) 2013-2015 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
@@ -20,51 +20,38 @@
2020#include <QtQml/QQmlEngine>
2121#include <QPointer>
2222#include <private/qquickmousearea_p.h>
23−#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
24− #include <private/qquickwindow_p.h>
25−#endif
23+#include <private/qquickwindow_p.h>
2624
2725
2826#include <DirectionalDragArea.h>
27+#include <DirectionalDragArea_p.h>
2928#include <TouchRegistry.h>
3029
3130#include "GestureTest.h"
3231
3332using namespace UbuntuGestures;
3433
35−
36−class ComplexFakeTimer : public FakeTimer
37−{
34+// Because QSignalSpy(directionalDragArea, SIGNAL(DirectionalDragArea::Status)) simply
35+// doesn't work
36+class StatusSpy : public QObject {
3837 Q_OBJECT
3938public:
40− ComplexFakeTimer(const SharedTimeSource &timeSource, QObject *parent = 0)
41− : FakeTimer(parent),
42− m_timeSource(timeSource)
43− {}
44−
45− void start() override {
46− AbstractTimer::start();
47− m_nextTimeoutTime = m_timeSource->msecsSinceReference() + (qint64)interval();
39+ StatusSpy(DirectionalDragArea *edgeDragArea) {
40+ m_recognized = false;
41+ connect(edgeDragArea->d, &DirectionalDragAreaPrivate::statusChanged,
42+ this, &StatusSpy::onStatusChanged);
4843 }
49−
50− void emitTimeout() {
51− m_nextTimeoutTime += interval();
52− Q_EMIT timeout();
44+ bool recognized() {
45+ return m_recognized;
5346 }
5447
55− qint64 nextTimeoutTime() const { return m_nextTimeoutTime; }
48+private Q_SLOTS:
49+ void onStatusChanged(DirectionalDragAreaPrivate::Status status) {
50+ m_recognized |= status == DirectionalDragAreaPrivate::Recognized;
51+ }
5652
5753private:
58− SharedTimeSource m_timeSource;
59− qint64 m_nextTimeoutTime;
60−};
61−
62−class FakeTimeSource : public UbuntuGestures::TimeSource
63−{
64−public:
65− FakeTimeSource() { m_msecsSinceReference = 0; }
66− virtual qint64 msecsSinceReference() {return m_msecsSinceReference;}
67− qint64 m_msecsSinceReference;
54+ bool m_recognized;
6855};
6956
7057/*
@@ -97,16 +84,10 @@ class tst_DirectionalDragArea: public GestureTest
9784public:
9885 tst_DirectionalDragArea();
9986private Q_SLOTS:
100− void init(); // called right before each and every test function is executed
101− void cleanup(); // called right after each and every test function is executed
87+ void init() override; // called right before each and every test function is executed
10288
103− void edgeDrag();
104− void edgeDrag_data();
10589 void dragWithShortDirectionChange();
106− void minSpeed();
107− void minSpeed_data();
10890 void recognitionTimerUsage();
109− void maxSilenceTime();
11091 void sceneXAndX();
11192 void sceneYAndY();
11293 void twoFingerTap();
@@ -122,14 +103,24 @@ private Q_SLOTS:
122103 void immediateRecognitionWhenConstraintsDisabled();
123104 void withdrawTouchOwnershipCandidacyIfDisabledDuringRecognition();
124105 void withdrawTouchOwnershipCandidacyIfDisabledDuringRecognition_data();
125− void tappedSignal();
126− void tappedSignal_data();
127106 void gettingTouchOwnershipMakesMouseAreaBehindGetCanceled();
107+ void interleavedTouches();
108+ void makoRightEdgeDrag();
109+ void makoRightEdgeDrag_verticalDownwards();
110+ void makoLeftEdgeDrag_slowStart();
111+ void makoLeftEdgeDrag_movesSlightlyBackwardsOnStart();
128112
129113private:
114+ // QTest::touchEvent takes QPoint instead of QPointF and I don't want to
115+ // lose precision due to rounding.
116+ // Besides, those helper functions lead to more compact code.
117+ void sendTouchPress(qint64 timestamp, int id, QPointF pos);
118+ void sendTouchUpdate(qint64 timestamp, int id, QPointF pos);
119+ void sendTouchRelease(qint64 timestamp, int id, QPointF pos);
120+ void sendTouch(qint64 timestamp, int id, QPointF pos,
121+ Qt::TouchPointState pointState, QEvent::Type eventType);
122+
130123 void passTime(qint64 timeSpanMs);
131− ComplexFakeTimer *fakeTimer;
132− QSharedPointer<FakeTimeSource> fakeTimeSource;
133124};
134125
135126tst_DirectionalDragArea::tst_DirectionalDragArea()
@@ -148,185 +139,57 @@ void tst_DirectionalDragArea::init()
148139 m_view->resize(m_view->rootObject()->width(), m_view->rootObject()->height());
149140 QTRY_COMPARE(m_view->width(), (int)m_view->rootObject()->width());
150141 QTRY_COMPARE(m_view->height(), (int)m_view->rootObject()->height());
151−
152− fakeTimeSource.reset(new FakeTimeSource);
153− fakeTimer = new ComplexFakeTimer(fakeTimeSource);
154−}
155−
156−void tst_DirectionalDragArea::cleanup()
157−{
158− delete fakeTimer;
159− fakeTimer = 0;
160−
161− fakeTimeSource.reset();
162−
163− GestureTest::cleanup();
164−}
165−
166−void tst_DirectionalDragArea::passTime(qint64 timeSpanMs)
167−{
168− qint64 finalTime = fakeTimeSource->m_msecsSinceReference + timeSpanMs;
169−
170− if (fakeTimer->isRunning() && finalTime >= fakeTimer->nextTimeoutTime()) {
171− fakeTimeSource->m_msecsSinceReference = fakeTimer->nextTimeoutTime();
172− fakeTimer->emitTimeout();
173−
174− qint64 timeSpanRemainder = finalTime - fakeTimeSource->m_msecsSinceReference;
175− if (timeSpanRemainder > 0) {
176− passTime(timeSpanRemainder);
177− }
178− } else {
179− fakeTimeSource->m_msecsSinceReference = finalTime;
180− }
181142}
182143
183−namespace {
184−QPointF calculateInitialTouchPos(DirectionalDragArea *edgeDragArea)
144+void tst_DirectionalDragArea::sendTouchPress(qint64 timestamp, int id, QPointF pos)
185145{
186− QPointF localCenter(edgeDragArea->width() / 2., edgeDragArea->height() / 2.);
187− return edgeDragArea->mapToScene(localCenter);
146+ sendTouch(timestamp, id, pos, Qt::TouchPointPressed, QEvent::TouchBegin);
188147}
189148
190−QPointF calculateDirectionVector(DirectionalDragArea *edgeDragArea,
191− qreal wideningAngleMultiplier)
149+void tst_DirectionalDragArea::sendTouchUpdate(qint64 timestamp, int id, QPointF pos)
192150{
193− qreal angleRadians = edgeDragArea->wideningAngle() * wideningAngleMultiplier
194− * M_PI / 180.0;
195−
196− qreal angleCos = qCos(angleRadians);
197− qreal angleSin = qSin(angleRadians);
198−
199− switch (edgeDragArea->direction()) {
200− case Direction::Upwards:
201− return QPointF(angleSin, -angleCos);
202− case Direction::Downwards:
203− return QPointF(angleSin, angleCos);
204− case Direction::Leftwards:
205− return QPointF(-angleCos, angleSin);
206− default: // Direction::Rightwards:
207− return QPointF(angleCos, angleSin);
208− }
151+ sendTouch(timestamp, id, pos, Qt::TouchPointMoved, QEvent::TouchUpdate);
209152}
210153
211−QPointF createTouchDeviation(DirectionalDragArea *edgeDragArea)
154+void tst_DirectionalDragArea::sendTouchRelease(qint64 timestamp, int id, QPointF pos)
212155{
213− qreal deviation = edgeDragArea->maxDeviation() * 0.8;
214−
215− if (Direction::isHorizontal(edgeDragArea->direction())) {
216− return QPointF(0, deviation);
217− } else {
218− return QPointF(deviation, 0);
219− }
220−}
156+ sendTouch(timestamp, id, pos, Qt::TouchPointReleased, QEvent::TouchEnd);
221157}
222158
223−void tst_DirectionalDragArea::edgeDrag()
159+void tst_DirectionalDragArea::sendTouch(qint64 timestamp, int id, QPointF pos,
160+ Qt::TouchPointState pointState, QEvent::Type eventType)
224161{
225− QFETCH(QString, dragAreaObjectName);
226− QFETCH(qreal, wideningAngleMultiplier);
227− QFETCH(qreal, dragDistanceFactor);
228− QFETCH(bool, expectGestureRecognition);
229−
230− DirectionalDragArea *edgeDragArea =
231− m_view->rootObject()->findChild<DirectionalDragArea*>(dragAreaObjectName);
232− QVERIFY(edgeDragArea != 0);
233− edgeDragArea->setRecognitionTimer(fakeTimer);
234− edgeDragArea->setTimeSource(fakeTimeSource);
235−
236− QSignalSpy draggingSpy(edgeDragArea, SIGNAL(draggingChanged(bool)));
237−
238− QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
239− QPointF touchPoint = initialTouchPos;
240−
241− qreal desiredDragDistance = edgeDragArea->distanceThreshold()*dragDistanceFactor;
242− QPointF dragDirectionVector = calculateDirectionVector(edgeDragArea,
243− wideningAngleMultiplier);
244− qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
245− QPointF touchMovement = dragDirectionVector * movementStepDistance;
246− int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
247− int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
248−
249− QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
250−
251− QCOMPARE(draggingSpy.count(), 1);
252− QCOMPARE(edgeDragArea->dragging(), true);
253−
254− if (wideningAngleMultiplier > 0) {
255− // go close to the border of the valid area for this touch point
256− // in order to make it easier to leave it by dragging at an angle
257− // slightly bigger than the widening angle
258− touchPoint += createTouchDeviation(edgeDragArea);
259− QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
260− }
162+ m_fakeTimerFactory->updateTime(timestamp);
261163
262− for (int i = 0; i < totalMovementSteps; ++i) {
263− touchPoint += touchMovement;
264− passTime(movementTimeStepMs);
265− QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
266− }
164+ QTouchEvent::TouchPoint point;
267165
268− if (expectGestureRecognition)
269− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
166+ point.setState(pointState);
167+ point.setId(id);
168+ point.setScenePos(pos);
169+ point.setPos(pos);
270170
271− if (edgeDragArea->status() == DirectionalDragArea::WaitingForTouch) {
272− QCOMPARE(edgeDragArea->dragging(), false);
273− QCOMPARE(draggingSpy.count(), 2);
274− }
171+ QList<QTouchEvent::TouchPoint> points;
172+ points << point;
275173
276− QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
174+ QTouchEvent touchEvent(eventType, m_device, Qt::NoModifier, Qt::TouchPointPressed, points);
175+ QCoreApplication::sendEvent(m_view, &touchEvent);
277176
278− QCOMPARE(draggingSpy.count(), 2);
279− QCOMPARE(edgeDragArea->dragging(), false);
177+ QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(m_view);
178+ windowPrivate->flushDelayedTouchEvent();
280179}
281180
282−void tst_DirectionalDragArea::edgeDrag_data()
181+void tst_DirectionalDragArea::passTime(qint64 timeSpanMs)
283182{
284− QTest::addColumn<QString>("dragAreaObjectName");
285− QTest::addColumn<qreal>("wideningAngleMultiplier");
286− QTest::addColumn<qreal>("dragDistanceFactor");
287− QTest::addColumn<bool>("expectGestureRecognition");
288−
289− QTest::newRow("rightwards, tiny drag")
290− << "hpDragArea" << 0.0 << 0.2 << false;
291−
292− QTest::newRow("rightwards, straight drag")
293− << "hpDragArea" << 0.0 << 3.0 << true;
294−
295− QTest::newRow("rightwards, diagonal drag")
296− << "hpDragArea" << 0.9 << 3.0 << true;
297−
298− QTest::newRow("rightwards, overly diagonal drag")
299− << "hpDragArea" << 2.0 << 3.0 << false;
300−
301− QTest::newRow("leftwards, tiny drag")
302− << "hnDragArea" << 0.0 << 0.2 << false;
303−
304− QTest::newRow("leftwards, straight drag")
305− << "hnDragArea" << 0.0 << 3.0 << true;
306−
307− QTest::newRow("leftwards, diagonal drag")
308− << "hnDragArea" << 0.9 << 3.0 << true;
309−
310− QTest::newRow("downwards, tiny drag")
311− << "vpDragArea" << 0.0 << 0.2 << false;
312−
313− QTest::newRow("downwards, straight drag")
314− << "vpDragArea" << 0.0 << 3.0 << true;
315−
316− QTest::newRow("downwards, diagonal drag")
317− << "vpDragArea" << 0.9 << 3.0 << true;
318−
319− QTest::newRow("upwards, tiny drag")
320− << "vnDragArea" << 0.0 << 0.2 << false;
321−
322− QTest::newRow("upwards, straight drag")
323− << "vnDragArea" << 0.0 << 3.0 << true;
324−
325− QTest::newRow("upwards, diagonal drag")
326− << "vnDragArea" << 0.9 << 3.0 << true;
183+ qint64 finalTime = m_fakeTimerFactory->timeSource()->msecsSinceReference() + timeSpanMs;
184+ m_fakeTimerFactory->updateTime(finalTime);
185+}
327186
328− QTest::newRow("upwards, overly diagonal drag")
329− << "vnDragArea" << 2.0 << 3.0 << false;
187+namespace {
188+QPointF calculateInitialTouchPos(DirectionalDragArea *edgeDragArea)
189+{
190+ QPointF localCenter(edgeDragArea->width() / 2., edgeDragArea->height() / 2.);
191+ return edgeDragArea->mapToScene(localCenter);
192+}
330193}
331194
332195/*
@@ -339,17 +202,17 @@ void tst_DirectionalDragArea::dragWithShortDirectionChange()
339202 DirectionalDragArea *edgeDragArea =
340203 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
341204 QVERIFY(edgeDragArea != 0);
342− edgeDragArea->setRecognitionTimer(fakeTimer);
343− edgeDragArea->setTimeSource(fakeTimeSource);
205+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
206+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
344207
345208 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
346209 QPointF touchPoint = initialTouchPos;
347210
348− qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0;
211+ qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2.0;
349212 QPointF dragDirectionVector(1.0, 0.0);
350− qreal touchStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
351− // make sure we are above minimum speed
352− int touchStepTimeMs = (touchStepDistance / (edgeDragArea->minSpeed() * 5.0f)) * 1000.0f;
213+ qreal touchStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
214+ // make sure we are above maximum time
215+ int touchStepTimeMs = edgeDragArea->d->maxTime / 20. ;
353216 QPointF touchMovement = dragDirectionVector * touchStepDistance;
354217
355218 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
@@ -373,65 +236,13 @@ void tst_DirectionalDragArea::dragWithShortDirectionChange()
373236 passTime(touchStepTimeMs);
374237 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
375238 } while ((touchPoint - initialTouchPos).manhattanLength() < desiredDragDistance
376− || fakeTimeSource->m_msecsSinceReference < (edgeDragArea->compositionTime() * 1.5f));
239+ || m_fakeTimerFactory->timeSource()->msecsSinceReference() < (edgeDragArea->d->compositionTime * 1.5f));
377240
378− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
241+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
379242
380243 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
381244}
382245
383−/*
384− Checks that a gesture will be rejected if it's slower than minSpeed while
385− status is Undecided.
386− */
387−void tst_DirectionalDragArea::minSpeed()
388−{
389− QFETCH(qreal, minSpeed);
390− QFETCH(qreal, speed);
391− QFETCH(int, expectedStatusAfterSpeedCheck);
392−
393− DirectionalDragArea *edgeDragArea =
394− m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
395− QVERIFY(edgeDragArea != 0);
396− edgeDragArea->setRecognitionTimer(fakeTimer);
397− edgeDragArea->setTimeSource(fakeTimeSource);
398−
399− // A really long, unattainable, number. We don't want it getting recognized before
400− // the speed checks we want have been performed
401− edgeDragArea->setDistanceThreshold(500000);
402−
403− edgeDragArea->setMinSpeed(minSpeed);
404−
405− QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
406− QPointF touchPoint = initialTouchPos;
407−
408− QPointF dragDirectionVector(1.0, 0.0);
409− qint64 timeStepMsecs = 10;
410− qreal distanceStep = (speed / 1000.0f) * timeStepMsecs;
411− QPointF touchMovement = dragDirectionVector * distanceStep;
412−
413− QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
414−
415− // Move for a while to ensure our speed check is performed a couple of times
416− for (int i=0; i < 20; ++i) {
417− touchPoint += touchMovement;
418− passTime(timeStepMsecs);
419− QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
420− }
421−
422− QCOMPARE((int)edgeDragArea->status(), expectedStatusAfterSpeedCheck);
423−}
424−
425−void tst_DirectionalDragArea::minSpeed_data()
426−{
427− QTest::addColumn<qreal>("minSpeed");
428− QTest::addColumn<qreal>("speed");
429− QTest::addColumn<int>("expectedStatusAfterSpeedCheck");
430−
431− QTest::newRow("slower than minSpeed") << 100.0 << 50.0 << (int)DirectionalDragArea::WaitingForTouch;
432− QTest::newRow("faster than minSpeed") << 100.0 << 150.0 << (int)DirectionalDragArea::Undecided;
433−}
434−
435246/*
436247 Checks that the recognition timer is started and stopped appropriately.
437248 I.e., check that it's running only while gesture recognition is taking place
@@ -442,11 +253,9 @@ void tst_DirectionalDragArea::recognitionTimerUsage()
442253 DirectionalDragArea *edgeDragArea =
443254 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
444255 QVERIFY(edgeDragArea != 0);
445− edgeDragArea->setRecognitionTimer(fakeTimer);
446− edgeDragArea->setTimeSource(fakeTimeSource);
447−
448− // don't let it interfere with our test
449− edgeDragArea->setMinSpeed(0.0);
256+ AbstractTimer *fakeTimer = m_fakeTimerFactory->createTimer();
257+ edgeDragArea->d->setRecognitionTimer(fakeTimer);
258+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
450259
451260 int timeStepMs = 5; // some arbitrary small value.
452261
@@ -454,62 +263,31 @@ void tst_DirectionalDragArea::recognitionTimerUsage()
454263 QPointF touchPoint = initialTouchPos;
455264
456265 QPointF dragDirectionVector(1.0, 0.0);
457− QPointF touchMovement = dragDirectionVector * (edgeDragArea->distanceThreshold() * 0.2f);
266+ QPointF touchMovement = dragDirectionVector * (edgeDragArea->d->distanceThreshold * 0.2f);
458267
459− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
268+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
460269 QVERIFY(!fakeTimer->isRunning());
461270
462271 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
463272
464− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
273+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
465274 QVERIFY(fakeTimer->isRunning());
466275
467276 // Move beyond distance threshold and composition time to ensure recognition
468− while (fakeTimeSource->m_msecsSinceReference <= edgeDragArea->compositionTime() ||
469− (touchPoint - initialTouchPos).manhattanLength() <= edgeDragArea->distanceThreshold()) {
277+ while (m_fakeTimerFactory->timeSource()->msecsSinceReference() <= edgeDragArea->d->compositionTime ||
278+ (touchPoint - initialTouchPos).manhattanLength() <= edgeDragArea->d->distanceThreshold) {
470279
471− QCOMPARE(edgeDragArea->status() == DirectionalDragArea::Undecided, fakeTimer->isRunning());
280+ QCOMPARE(edgeDragArea->d->status == DirectionalDragAreaPrivate::Undecided, fakeTimer->isRunning());
472281
473282 touchPoint += touchMovement;
474283 passTime(timeStepMs);
475284 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
476285 }
477286
478− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
287+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
479288 QVERIFY(!fakeTimer->isRunning());
480289}
481290
482−/*
483− A gesture should be rejected if too much time has passed without any new input
484− events from it.
485− */
486−void tst_DirectionalDragArea::maxSilenceTime()
487−{
488− DirectionalDragArea *edgeDragArea =
489− m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
490− QVERIFY(edgeDragArea != 0);
491− edgeDragArea->setRecognitionTimer(fakeTimer);
492− edgeDragArea->setTimeSource(fakeTimeSource);
493−
494− // Make sure this property is not disabled
495− edgeDragArea->setMaxSilenceTime(100);
496−
497− QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
498− QPointF touchPoint = initialTouchPos;
499−
500− QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
501−
502− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
503− QVERIFY(fakeTimer->isRunning());
504−
505− // Force timer to timeout until after maxSilenceTime has been reached
506− while (fakeTimeSource->m_msecsSinceReference < edgeDragArea->maxSilenceTime()) {
507− passTime(fakeTimer->interval());
508− }
509−
510− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
511−}
512−
513291/*
514292 Checks that it informs the X coordinate of the touch point in local and scene coordinates
515293 correctly.
@@ -519,18 +297,19 @@ void tst_DirectionalDragArea::sceneXAndX()
519297 DirectionalDragArea *edgeDragArea =
520298 m_view->rootObject()->findChild<DirectionalDragArea*>("hnDragArea");
521299 QVERIFY(edgeDragArea != 0);
522− edgeDragArea->setRecognitionTimer(fakeTimer);
523− edgeDragArea->setTimeSource(fakeTimeSource);
300+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
301+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
302+ edgeDragArea->setImmediateRecognition(true);
524303
525304 QPointF touchScenePos(m_view->width() - (edgeDragArea->width()/2.0f), m_view->height()/2.0f);
526305
527− QTest::touchEvent(m_view, m_device).press(0, touchScenePos.toPoint());
306+ sendTouchPress(0 /* timestamp */, 0 /* id */, touchScenePos);
528307
529308 QSignalSpy touchXSpy(edgeDragArea, SIGNAL(touchXChanged(qreal)));
530309 QSignalSpy touchSceneXSpy(edgeDragArea, SIGNAL(touchSceneXChanged(qreal)));
531310
532311 touchScenePos.rx() = m_view->width() / 2;
533− QTest::touchEvent(m_view, m_device).move(0, touchScenePos.toPoint());
312+ sendTouchUpdate(50 /* timestamp */, 0 /* id */, touchScenePos);
534313
535314 QCOMPARE(touchXSpy.count(), 1);
536315 QCOMPARE(touchSceneXSpy.count(), 1);
@@ -547,18 +326,19 @@ void tst_DirectionalDragArea::sceneYAndY()
547326 DirectionalDragArea *edgeDragArea =
548327 m_view->rootObject()->findChild<DirectionalDragArea*>("vnDragArea");
549328 QVERIFY(edgeDragArea != 0);
550− edgeDragArea->setRecognitionTimer(fakeTimer);
551− edgeDragArea->setTimeSource(fakeTimeSource);
329+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
330+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
331+ edgeDragArea->setImmediateRecognition(true);
552332
553333 QPointF touchScenePos(m_view->width()/2.0f, m_view->height() - (edgeDragArea->height()/2.0f));
554334
555− QTest::touchEvent(m_view, m_device).press(0, touchScenePos.toPoint());
335+ sendTouchPress(0 /* timestamp */, 0 /* id */, touchScenePos);
556336
557337 QSignalSpy touchYSpy(edgeDragArea, SIGNAL(touchYChanged(qreal)));
558338 QSignalSpy touchSceneYSpy(edgeDragArea, SIGNAL(touchSceneYChanged(qreal)));
559339
560340 touchScenePos.ry() = m_view->height() / 2;
561− QTest::touchEvent(m_view, m_device).move(0, touchScenePos.toPoint());
341+ sendTouchUpdate(50 /* timestamp */, 0 /* id */, touchScenePos);
562342
563343 QCOMPARE(touchYSpy.count(), 1);
564344 QCOMPARE(touchSceneYSpy.count(), 1);
@@ -577,8 +357,8 @@ void tst_DirectionalDragArea::twoFingerTap()
577357 DirectionalDragArea *edgeDragArea =
578358 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
579359 QVERIFY(edgeDragArea != 0);
580− edgeDragArea->setRecognitionTimer(fakeTimer);
581− edgeDragArea->setTimeSource(fakeTimeSource);
360+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
361+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
582362
583363 // Make touches evenly spaced along the edgeDragArea
584364 QPoint touchAPos(edgeDragArea->width()/2.0f, m_view->height()*0.33f);
@@ -592,7 +372,7 @@ void tst_DirectionalDragArea::twoFingerTap()
592372 QTest::touchEvent(m_view, m_device)
593373 .press(0, touchAPos);
594374
595− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
375+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
596376
597377 passTime(timeStepMsecs);
598378 QTest::touchEvent(m_view, m_device)
@@ -601,20 +381,20 @@ void tst_DirectionalDragArea::twoFingerTap()
601381
602382 // A second touch point appeared during recognition, reject immediately as this
603383 // can't be a single-touch gesture anymore.
604− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
384+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
605385
606386 passTime(timeStepMsecs);
607387 QTest::touchEvent(m_view, m_device)
608388 .release(0, touchAPos)
609389 .move(1, touchBPos);
610390
611− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
391+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
612392
613393 passTime(timeStepMsecs);
614394 QTest::touchEvent(m_view, m_device)
615395 .release(1, touchBPos);
616396
617− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
397+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
618398
619399 // Perform the second two-finger tap
620400
@@ -622,27 +402,27 @@ void tst_DirectionalDragArea::twoFingerTap()
622402 QTest::touchEvent(m_view, m_device)
623403 .press(0, touchAPos);
624404
625− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
405+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
626406
627407 passTime(timeStepMsecs);
628408 QTest::touchEvent(m_view, m_device)
629409 .move(0, touchAPos)
630410 .press(1, touchBPos);
631411
632− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
412+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
633413
634414 passTime(timeStepMsecs);
635415 QTest::touchEvent(m_view, m_device)
636416 .release(0, touchAPos)
637417 .move(1, touchBPos);
638418
639− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
419+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
640420
641421 passTime(timeStepMsecs);
642422 QTest::touchEvent(m_view, m_device)
643423 .release(1, touchBPos);
644424
645− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
425+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
646426}
647427
648428/*
@@ -659,25 +439,25 @@ void tst_DirectionalDragArea::movingDDA()
659439 DirectionalDragArea *edgeDragArea =
660440 rightwardsLauncher->findChild<DirectionalDragArea*>("hpDragArea");
661441 Q_ASSERT(edgeDragArea != 0);
662− edgeDragArea->setRecognitionTimer(fakeTimer);
663− edgeDragArea->setTimeSource(fakeTimeSource);
442+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
443+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
664444
665445 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
666446 QPointF touchPoint = initialTouchPos;
667447
668− qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
448+ qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2.0f;
669449 QPointF dragDirectionVector(1.0f, 0.0f);
670450
671− qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
451+ qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
672452 QPointF touchMovement = dragDirectionVector * movementStepDistance;
673453 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
674− int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
454+ int movementTimeStepMs = (edgeDragArea->d->compositionTime * 1.5f) / totalMovementSteps;
675455
676456 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
677457
678458 // Move it far ahead along the direction of the gesture
679459 // rightwardsLauncher is a parent of our DirectionalDragArea. So moving it will move our DDA
680− rightwardsLauncher->setX(rightwardsLauncher->x() + edgeDragArea->distanceThreshold() * 5.0f);
460+ rightwardsLauncher->setX(rightwardsLauncher->x() + edgeDragArea->d->distanceThreshold * 5.0f);
681461
682462 for (int i = 0; i < totalMovementSteps; ++i) {
683463 touchPoint += touchMovement;
@@ -685,7 +465,7 @@ void tst_DirectionalDragArea::movingDDA()
685465 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
686466 }
687467
688− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
468+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
689469
690470 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
691471}
@@ -699,8 +479,8 @@ void tst_DirectionalDragArea::ignoreOldFinger()
699479 DirectionalDragArea *edgeDragArea =
700480 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
701481 Q_ASSERT(edgeDragArea != 0);
702− edgeDragArea->setRecognitionTimer(fakeTimer);
703− edgeDragArea->setTimeSource(fakeTimeSource);
482+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
483+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
704484
705485 // Make touches evenly spaced along the edgeDragArea
706486 QPoint touch0Pos(edgeDragArea->width()/2.0f, m_view->height()*0.33f);
@@ -708,26 +488,26 @@ void tst_DirectionalDragArea::ignoreOldFinger()
708488
709489 QTest::touchEvent(m_view, m_device).press(0, touch0Pos);
710490
711− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
491+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
712492
713493 // leave it lying around for some time
714− passTime(qMax(edgeDragArea->maxSilenceTime(), edgeDragArea->compositionTime()) * 10);
494+ passTime(edgeDragArea->d->maxTime * 10);
715495
716− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
496+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
717497
718− qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
498+ qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2.0f;
719499 QPointF dragDirectionVector(1.0f, 0.0f);
720500
721− qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
501+ qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
722502 QPointF touchMovement = dragDirectionVector * movementStepDistance;
723503 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
724− int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
504+ int movementTimeStepMs = (edgeDragArea->d->compositionTime * 1.5f) / totalMovementSteps;
725505
726506 QTest::touchEvent(m_view, m_device)
727507 .move(0, touch0Pos)
728508 .press(1, touch1Pos);
729509
730− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
510+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
731511
732512 for (int i = 0; i < totalMovementSteps; ++i) {
733513 touch1Pos += touchMovement.toPoint();
@@ -737,13 +517,13 @@ void tst_DirectionalDragArea::ignoreOldFinger()
737517 .move(1, touch1Pos);
738518 }
739519
740− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
520+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
741521
742522 QTest::touchEvent(m_view, m_device)
743523 .move(0, touch0Pos)
744524 .release(1, touch1Pos);
745525
746− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
526+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
747527}
748528
749529/*
@@ -762,19 +542,19 @@ void tst_DirectionalDragArea::rotated()
762542 DirectionalDragArea *edgeDragArea =
763543 rightwardsLauncher->findChild<DirectionalDragArea*>("hpDragArea");
764544 Q_ASSERT(edgeDragArea != 0);
765− edgeDragArea->setRecognitionTimer(fakeTimer);
766− edgeDragArea->setTimeSource(fakeTimeSource);
545+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
546+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
767547
768548 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
769549 QPointF touchPoint = initialTouchPos;
770550
771− qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
551+ qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2.0f;
772552 QPointF dragDirectionVector(0.0f, 1.0f);
773553
774− qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
554+ qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
775555 QPointF touchMovement = dragDirectionVector * movementStepDistance;
776556 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
777− int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
557+ int movementTimeStepMs = (edgeDragArea->d->compositionTime * 1.5f) / totalMovementSteps;
778558
779559 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
780560
@@ -784,7 +564,7 @@ void tst_DirectionalDragArea::rotated()
784564 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
785565 }
786566
787− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
567+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
788568
789569 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
790570}
@@ -802,31 +582,30 @@ void tst_DirectionalDragArea::sceneDistance()
802582 DirectionalDragArea *edgeDragArea =
803583 rightwardsLauncher->findChild<DirectionalDragArea*>("hpDragArea");
804584 Q_ASSERT(edgeDragArea != 0);
805− edgeDragArea->setRecognitionTimer(fakeTimer);
806− edgeDragArea->setTimeSource(fakeTimeSource);
585+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
586+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
587+
588+ // to disable the position smoothing so that we can more easily check sceneDistance values
589+ edgeDragArea->setImmediateRecognition(true);
807590
808591 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
809592 QPointF touchPoint = initialTouchPos;
810593
811− qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
594+ qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2.0f;
812595
813− qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
596+ qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
814597 QPointF touchMovement = dragDirectionVector * movementStepDistance;
815598 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
816− int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
599+ int movementTimeStepMs = (edgeDragArea->d->compositionTime * 1.5f) / totalMovementSteps;
817600
818− QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
601+ qint64 timestamp = 0;
602+
603+ sendTouchPress(timestamp, 0, touchPoint);
819604
820−#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
821− QQuickWindowPrivate *wp = QQuickWindowPrivate::get(m_view);
822−#endif
823605 for (int i = 0; i < totalMovementSteps; ++i) {
824606 touchPoint += touchMovement;
825− passTime(movementTimeStepMs);
826− QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
827−#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
828− wp->flushDelayedTouchEvent();
829−#endif
607+ timestamp += movementTimeStepMs;
608+ sendTouchUpdate(timestamp, 0, touchPoint);
830609 }
831610
832611 qreal actualDragDistance = ((qreal)totalMovementSteps) * movementStepDistance;
@@ -836,7 +615,8 @@ void tst_DirectionalDragArea::sceneDistance()
836615 // NB: qFuzzyCompare(), used internally by QCOMPARE(), is broken.
837616 QVERIFY(qAbs(edgeDragArea->sceneDistance() - actualDragDistance) < 0.001);
838617
839− QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
618+ timestamp += movementTimeStepMs;
619+ sendTouchRelease(timestamp, 0, touchPoint);
840620}
841621
842622void tst_DirectionalDragArea::sceneDistance_data()
@@ -860,18 +640,18 @@ void tst_DirectionalDragArea::disabledWhileDragging()
860640 DirectionalDragArea *edgeDragArea =
861641 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
862642 Q_ASSERT(edgeDragArea != 0);
863− edgeDragArea->setRecognitionTimer(fakeTimer);
864− edgeDragArea->setTimeSource(fakeTimeSource);
643+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
644+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
865645
866646 QPointF touchPoint = calculateInitialTouchPos(edgeDragArea);
867647
868− qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2.0f;
648+ qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2.0f;
869649 QPointF dragDirectionVector(1., 0.); // horizontal positive
870650
871− qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
651+ qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
872652 QPointF touchMovement = dragDirectionVector * movementStepDistance;
873653 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
874− int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
654+ int movementTimeStepMs = (edgeDragArea->d->compositionTime * 1.5f) / totalMovementSteps;
875655
876656 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
877657
@@ -881,13 +661,13 @@ void tst_DirectionalDragArea::disabledWhileDragging()
881661 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
882662 }
883663
884− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
664+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
885665 QCOMPARE(edgeDragArea->dragging(), true);
886666
887667 // disable the dragArea while it's being dragged.
888668 edgeDragArea->setEnabled(false);
889669
890− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
670+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
891671 QCOMPARE(edgeDragArea->dragging(), false);
892672
893673 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
@@ -898,15 +678,14 @@ void tst_DirectionalDragArea::oneFingerDownFollowedByLateSecondFingerDown()
898678 DirectionalDragArea *edgeDragArea =
899679 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
900680 Q_ASSERT(edgeDragArea != 0);
901− edgeDragArea->setRecognitionTimer(fakeTimer);
902− edgeDragArea->setTimeSource(fakeTimeSource);
681+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
682+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
903683
904684 // Disable some constraints we're not interested in
905− edgeDragArea->setMaxSilenceTime(60 * 1000);
906− edgeDragArea->setMinSpeed(0);
685+ edgeDragArea->d->setMaxTime(60 * 1000);
907686
908687 // And ensure others have the values we want
909− edgeDragArea->setCompositionTime(60);
688+ edgeDragArea->d->compositionTime = 60;
910689
911690 // Put an item right behind edgeDragArea to receive the touches ignored by it
912691 DummyItem *dummyItem = new DummyItem;
@@ -923,10 +702,10 @@ void tst_DirectionalDragArea::oneFingerDownFollowedByLateSecondFingerDown()
923702
924703 QTest::touchEvent(m_view, m_device).press(0, touch0Pos);
925704
926− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
705+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
927706
928707 // We are now going to be way beyond compositionTime
929− passTime(edgeDragArea->compositionTime()*3);
708+ passTime(edgeDragArea->d->compositionTime*3);
930709
931710 QTest::touchEvent(m_view, m_device)
932711 .move(0, touch0Pos)
@@ -948,7 +727,7 @@ void tst_DirectionalDragArea::oneFingerDownFollowedByLateSecondFingerDown()
948727 .move(0, touch0Pos)
949728 .move(1, touch1Pos);
950729
951− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
730+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
952731
953732 passTime(5);
954733
@@ -956,7 +735,7 @@ void tst_DirectionalDragArea::oneFingerDownFollowedByLateSecondFingerDown()
956735 .release(0, touch0Pos)
957736 .move(1, touch1Pos);
958737
959− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
738+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
960739
961740 passTime(5);
962741
@@ -964,7 +743,7 @@ void tst_DirectionalDragArea::oneFingerDownFollowedByLateSecondFingerDown()
964743 .release(1, touch1Pos);
965744
966745 // Shouldn't be keepping info about touches that no longer exist or interest us
967− QVERIFY(edgeDragArea->m_activeTouches.isEmpty());
746+ QVERIFY(edgeDragArea->d->activeTouches.isEmpty());
968747
969748 delete dummyItem;
970749}
@@ -974,12 +753,11 @@ void tst_DirectionalDragArea::givesUpWhenLosesTouch()
974753 DirectionalDragArea *edgeDragArea =
975754 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
976755 Q_ASSERT(edgeDragArea != 0);
977− edgeDragArea->setRecognitionTimer(fakeTimer);
978− edgeDragArea->setTimeSource(fakeTimeSource);
756+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
757+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
979758
980759 // Disable some constraints we're not interested in
981− edgeDragArea->setMaxSilenceTime(60 * 1000);
982− edgeDragArea->setMinSpeed(0);
760+ edgeDragArea->d->setMaxTime(60 * 1000);
983761
984762 // Put an item right in front of edgeDragArea
985763 DummyItem *dummyItem = new DummyItem(edgeDragArea->parentItem());
@@ -998,11 +776,11 @@ void tst_DirectionalDragArea::givesUpWhenLosesTouch()
998776
999777 QTest::touchEvent(m_view, m_device).press(0, touchPos);
1000778
1001− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
779+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
1002780
1003781 m_touchRegistry->requestTouchOwnership(0, dummyItem);
1004782
1005− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
783+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
1006784
1007785 dummyItem->grabTouchPoints({0});
1008786 dummyItem->touchEventHandler = [&](QTouchEvent *event) { event->accept(); };
@@ -1010,9 +788,9 @@ void tst_DirectionalDragArea::givesUpWhenLosesTouch()
1010788 passTime(5);
1011789 QTest::touchEvent(m_view, m_device).release(0, touchPos);
1012790
1013− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
791+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
1014792
1015− QVERIFY(edgeDragArea->m_activeTouches.isEmpty());
793+ QVERIFY(edgeDragArea->d->activeTouches.isEmpty());
1016794}
1017795
1018796void tst_DirectionalDragArea::threeFingerDrag()
@@ -1020,15 +798,14 @@ void tst_DirectionalDragArea::threeFingerDrag()
1020798 DirectionalDragArea *edgeDragArea =
1021799 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
1022800 Q_ASSERT(edgeDragArea != 0);
1023− edgeDragArea->setRecognitionTimer(fakeTimer);
1024− edgeDragArea->setTimeSource(fakeTimeSource);
801+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
802+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1025803
1026804 // Disable some constraints we're not interested in
1027− edgeDragArea->setMaxSilenceTime(60 * 1000);
1028− edgeDragArea->setMinSpeed(0);
805+ edgeDragArea->d->setMaxTime(60 * 1000);
1029806
1030807 // And ensure others have the values we want
1031− edgeDragArea->setCompositionTime(60);
808+ edgeDragArea->d->compositionTime = 60;
1032809
1033810 // Make touches evenly spaced along the edgeDragArea
1034811 QPoint touch0Pos(edgeDragArea->width()/2.0f, m_view->height()*0.25f);
@@ -1038,14 +815,14 @@ void tst_DirectionalDragArea::threeFingerDrag()
1038815 QTest::touchEvent(m_view, m_device)
1039816 .press(0, touch0Pos);
1040817
1041− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
818+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
1042819
1043820 passTime(5);
1044821 QTest::touchEvent(m_view, m_device)
1045822 .move(0, touch0Pos)
1046823 .press(1, touch1Pos);
1047824
1048− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
825+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
1049826
1050827 passTime(5);
1051828 QTest::touchEvent(m_view, m_device)
@@ -1053,7 +830,7 @@ void tst_DirectionalDragArea::threeFingerDrag()
1053830 .move(1, touch1Pos)
1054831 .press(2, touch2Pos);
1055832
1056− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
833+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
1057834
1058835 passTime(10);
1059836 QTest::touchEvent(m_view, m_device)
@@ -1077,7 +854,7 @@ void tst_DirectionalDragArea::threeFingerDrag()
1077854 .release(0, touch0Pos);
1078855
1079856 // Shouldn't be keepping info about touches that no longer exist or interest us
1080− QVERIFY(edgeDragArea->m_activeTouches.isEmpty());
857+ QVERIFY(edgeDragArea->d->activeTouches.isEmpty());
1081858}
1082859
1083860/*
@@ -1090,12 +867,12 @@ void tst_DirectionalDragArea::immediateRecognitionWhenConstraintsDisabled()
1090867 DirectionalDragArea *edgeDragArea =
1091868 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
1092869 Q_ASSERT(edgeDragArea != 0);
1093− edgeDragArea->setRecognitionTimer(fakeTimer);
1094− edgeDragArea->setTimeSource(fakeTimeSource);
870+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
871+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1095872
1096873 // Disable the minimum amount of constraints to ensure immediate recognition
1097− edgeDragArea->setDistanceThreshold(0);
1098− edgeDragArea->setCompositionTime(0);
874+ edgeDragArea->d->setDistanceThreshold(0);
875+ edgeDragArea->d->compositionTime = 0;
1099876
1100877 // Put an item right behind edgeDragArea to receive the touches ignored by it
1101878 DummyItem *dummyItem = new DummyItem;
@@ -1111,7 +888,7 @@ void tst_DirectionalDragArea::immediateRecognitionWhenConstraintsDisabled()
1111888 QTest::touchEvent(m_view, m_device).press(0, touch0Pos);
1112889
1113890 // check for immediate recognition
1114− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
891+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
1115892
1116893 // and therefore it should have immediately grabbed the touch point,
1117894 // not letting it leak to items behind him.
@@ -1123,19 +900,19 @@ void tst_DirectionalDragArea::withdrawTouchOwnershipCandidacyIfDisabledDuringRec
1123900 DirectionalDragArea *edgeDragArea =
1124901 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
1125902 Q_ASSERT(edgeDragArea != 0);
1126− edgeDragArea->setRecognitionTimer(fakeTimer);
1127− edgeDragArea->setTimeSource(fakeTimeSource);
903+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
904+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1128905
1129906 QPointF touchPoint = calculateInitialTouchPos(edgeDragArea);
1130907
1131908 // Move less than the minimum needed for the drag gesture recognition
1132− qreal desiredDragDistance = edgeDragArea->distanceThreshold()*0.5f;
909+ qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 0.5f;
1133910 QPointF dragDirectionVector(1., 0.); // horizontal positive
1134911
1135− qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
912+ qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
1136913 QPointF touchMovement = dragDirectionVector * movementStepDistance;
1137914 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
1138− int movementTimeStepMs = (edgeDragArea->compositionTime() * 0.8f) / totalMovementSteps;
915+ int movementTimeStepMs = (edgeDragArea->d->compositionTime * 0.8f) / totalMovementSteps;
1139916
1140917 QTest::touchEvent(m_view, m_device).press(0, touchPoint.toPoint());
1141918
@@ -1145,14 +922,14 @@ void tst_DirectionalDragArea::withdrawTouchOwnershipCandidacyIfDisabledDuringRec
1145922 QTest::touchEvent(m_view, m_device).move(0, touchPoint.toPoint());
1146923 }
1147924
1148− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
925+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
1149926
1150927 // edgeDragArea should be an undecided candidate
1151928 {
1152929 auto touchInfo = m_touchRegistry->findTouchInfo(0);
1153930 QCOMPARE(touchInfo->candidates.size(), 1);
1154931 QCOMPARE(touchInfo->candidates.at(0).item.data(), edgeDragArea);
1155− QCOMPARE(touchInfo->candidates.at(0).undecided, true);
932+ QCOMPARE(touchInfo->candidates.at(0).state, TouchRegistry::CandidateInfo::Undecided);
1156933 }
1157934
1158935 // disable the dragArea while it's still recognizing a possible drag gesture.
@@ -1169,7 +946,7 @@ void tst_DirectionalDragArea::withdrawTouchOwnershipCandidacyIfDisabledDuringRec
1169946 QCOMPARE(touchInfo->candidates.size(), 0);
1170947 }
1171948
1172− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
949+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
1173950
1174951 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
1175952}
@@ -1182,47 +959,13 @@ void tst_DirectionalDragArea::withdrawTouchOwnershipCandidacyIfDisabledDuringRec
1182959 QTest::newRow("invisible") << false;
1183960}
1184961
1185−void tst_DirectionalDragArea::tappedSignal()
1186−{
1187− DirectionalDragArea *edgeDragArea =
1188− m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
1189− QVERIFY(edgeDragArea != 0);
1190− edgeDragArea->setRecognitionTimer(fakeTimer);
1191− edgeDragArea->setTimeSource(fakeTimeSource);
1192−
1193− QFETCH(bool, immediateGestureRecognition);
1194− if (immediateGestureRecognition) {
1195− // Disable the minimum amount of constraints to ensure immediate recognition
1196− edgeDragArea->setDistanceThreshold(0);
1197− edgeDragArea->setCompositionTime(0);
1198− }
1199−
1200− QPoint touch0Pos(edgeDragArea->width()/2.0f, m_view->height()/2.0f);
1201−
1202− QSignalSpy tappedSpy(edgeDragArea, SIGNAL(tapped()));
1203−
1204− QTest::touchEvent(m_view, m_device).press(0, touch0Pos);
1205− passTime(edgeDragArea->maxTapDuration() / 2);
1206− QTest::touchEvent(m_view, m_device).release(0, touch0Pos);
1207−
1208− QCOMPARE(tappedSpy.count(), 1);
1209−}
1210−
1211−void tst_DirectionalDragArea::tappedSignal_data()
1212−{
1213− QTest::addColumn<bool>("immediateGestureRecognition");
1214−
1215− QTest::newRow("immediate gesture recognition") << true;
1216− QTest::newRow("default gesture recognition") << false;
1217−}
1218−
1219962void tst_DirectionalDragArea::gettingTouchOwnershipMakesMouseAreaBehindGetCanceled()
1220963{
1221964 DirectionalDragArea *edgeDragArea =
1222965 m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
1223966 QVERIFY(edgeDragArea != nullptr);
1224− edgeDragArea->setRecognitionTimer(fakeTimer);
1225− edgeDragArea->setTimeSource(fakeTimeSource);
967+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
968+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1226969
1227970 QQuickMouseArea *mouseArea =
1228971 m_view->rootObject()->findChild<QQuickMouseArea*>("mouseArea");
@@ -1233,12 +976,12 @@ void tst_DirectionalDragArea::gettingTouchOwnershipMakesMouseAreaBehindGetCancel
1233976 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea);
1234977 QPointF touchPoint = initialTouchPos;
1235978
1236− qreal desiredDragDistance = edgeDragArea->distanceThreshold()*2;
979+ qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2;
1237980 QPointF dragDirectionVector(1.0f, 0.0f); // rightwards
1238− qreal movementStepDistance = edgeDragArea->distanceThreshold() * 0.1f;
981+ qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
1239982 QPointF touchMovement = dragDirectionVector * movementStepDistance;
1240983 int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
1241− int movementTimeStepMs = (edgeDragArea->compositionTime() * 1.5f) / totalMovementSteps;
984+ int movementTimeStepMs = (edgeDragArea->d->compositionTime * 1.5f) / totalMovementSteps;
1242985
1243986 QCOMPARE(mouseArea->pressed(), false);
1244987
@@ -1259,13 +1002,305 @@ void tst_DirectionalDragArea::gettingTouchOwnershipMakesMouseAreaBehindGetCancel
12591002 // As the DirectionalDragArea recognizes the gesture, it grabs the touch from the MouseArea,
12601003 // which should make the MouseArea get a cancelation event, which will then cause it to
12611004 // reset its state (going back to "unpressed"/"released").
1262− QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
1005+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
12631006 QCOMPARE(mouseArea->pressed(), false);
12641007 QCOMPARE(mouseAreaSpy.canceledCount, 1);
12651008
12661009 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
12671010}
12681011
1012+void tst_DirectionalDragArea::interleavedTouches()
1013+{
1014+ DirectionalDragArea *edgeDragArea =
1015+ m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
1016+ QVERIFY(edgeDragArea != 0);
1017+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
1018+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1019+
1020+ QPointF touch0 = edgeDragArea->mapToScene(
1021+ QPointF(edgeDragArea->width()*0.5, edgeDragArea->height()*0.3));
1022+
1023+ qreal desiredDragDistance = edgeDragArea->d->distanceThreshold * 2;
1024+ QPointF dragDirectionVector(1.0f, 0.0f); // rightwards
1025+ qreal movementStepDistance = edgeDragArea->d->distanceThreshold * 0.1f;
1026+ QPointF touchMovement = dragDirectionVector * movementStepDistance;
1027+ int totalMovementSteps = qCeil(desiredDragDistance / movementStepDistance);
1028+ int movementTimeStepMs = (edgeDragArea->d->maxTime * 0.4f) / totalMovementSteps;
1029+
1030+ QTest::touchEvent(m_view, m_device).press(0, touch0.toPoint());
1031+ for (int i = 0; i < totalMovementSteps; ++i) {
1032+ touch0 += touchMovement;
1033+ passTime(movementTimeStepMs);
1034+ QTest::touchEvent(m_view, m_device).move(0, touch0.toPoint());
1035+ }
1036+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
1037+
1038+ QPointF touch1 = edgeDragArea->mapToScene(
1039+ QPointF(edgeDragArea->width()*0.5, edgeDragArea->height()*0.6));
1040+
1041+ QTest::touchEvent(m_view, m_device)
1042+ .move(0, touch0.toPoint())
1043+ .press(1, touch1.toPoint());
1044+
1045+ touch1 += touchMovement;
1046+ passTime(movementTimeStepMs);
1047+ QTest::touchEvent(m_view, m_device)
1048+ .move(0, touch0.toPoint())
1049+ .move(1, touch1.toPoint());
1050+
1051+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Recognized);
1052+
1053+ QTest::touchEvent(m_view, m_device)
1054+ .release(0, touch0.toPoint())
1055+ .move(1, touch1.toPoint());
1056+
1057+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
1058+
1059+ touch1 += touchMovement;
1060+ passTime(movementTimeStepMs);
1061+ QTest::touchEvent(m_view, m_device)
1062+ .move(1, touch1.toPoint());
1063+
1064+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
1065+
1066+ QPointF touch2 = edgeDragArea->mapToScene(
1067+ QPointF(edgeDragArea->width()*0.5, edgeDragArea->height()*0.9));
1068+
1069+ passTime(edgeDragArea->d->compositionTime + movementTimeStepMs);
1070+ QTest::touchEvent(m_view, m_device)
1071+ .move(1, touch1.toPoint())
1072+ .press(2, touch2.toPoint());
1073+
1074+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::Undecided);
1075+ QCOMPARE(edgeDragArea->d->touchId, 2);
1076+
1077+ touch2 += touchMovement;
1078+ passTime(movementTimeStepMs);
1079+ QTest::touchEvent(m_view, m_device)
1080+ .move(1, touch1.toPoint())
1081+ .move(2, touch2.toPoint());
1082+
1083+ touch1 += touchMovement;
1084+ passTime(movementTimeStepMs);
1085+ QTest::touchEvent(m_view, m_device)
1086+ .move(1, touch1.toPoint())
1087+ .move(2, touch2.toPoint());
1088+
1089+ passTime(movementTimeStepMs);
1090+ QTest::touchEvent(m_view, m_device)
1091+ .release(1, touch1.toPoint())
1092+ .move(2, touch2.toPoint());
1093+
1094+ passTime(movementTimeStepMs);
1095+ QTest::touchEvent(m_view, m_device)
1096+ .release(2, touch2.toPoint());
1097+
1098+ QCOMPARE((int)edgeDragArea->d->status, (int)DirectionalDragAreaPrivate::WaitingForTouch);
1099+}
1100+
1101+/*
1102+ A valid right-edge drag performed on mako
1103+ */
1104+void tst_DirectionalDragArea::makoRightEdgeDrag()
1105+{
1106+ m_view->resize(768, 1280);
1107+ QTest::qWait(300);
1108+
1109+ DirectionalDragArea *edgeDragArea =
1110+ m_view->rootObject()->findChild<DirectionalDragArea*>("hnDragArea");
1111+ QVERIFY(edgeDragArea != nullptr);
1112+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
1113+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1114+
1115+ StatusSpy *statusSpy = new StatusSpy(edgeDragArea);
1116+
1117+ edgeDragArea->d->setPixelsPerMm(320.0 /*mako ppi*/ * 0.03937 /* inches per mm*/);
1118+
1119+ sendTouchPress(319744, 0, QPointF(767.001, 719.719));
1120+ sendTouchUpdate(319765, 0, QPointF(765.744,729.973));
1121+ sendTouchUpdate(319784, 0, QPointF(740.879,752.182));
1122+ sendTouchUpdate(319803, 0, QPointF(689.698,795.795));
1123+ sendTouchUpdate(319826, 0, QPointF(616.978,856.212));
1124+ sendTouchUpdate(319845, 0, QPointF(558.769,906.157));
1125+ sendTouchUpdate(319859, 0, QPointF(513.219,945.266));
1126+ sendTouchUpdate(319878, 0, QPointF(481.31,975.496));
1127+ sendTouchUpdate(319902, 0, QPointF(460.016,997.439));
1128+ sendTouchUpdate(319920, 0, QPointF(449.761,1008.6));
1129+ sendTouchUpdate(319929, 0, QPointF(445.891,1012.42));
1130+ sendTouchUpdate(319947, 0, QPointF(444.884,1013.93));
1131+ sendTouchUpdate(319965, 0, QPointF(444.461,1014.35));
1132+ sendTouchUpdate(320057, 0, QPointF(444.71,1013.56));
1133+ sendTouchUpdate(320138, 0, QPointF(445.434,1013.6));
1134+ sendTouchUpdate(320154, 0, QPointF(446.338,1012.98));
1135+ sendTouchUpdate(320171, 0, QPointF(447.232,1012.08));
1136+ sendTouchRelease(320171, 0, QPointF(447.232,1012.08));
1137+
1138+ QCOMPARE(statusSpy->recognized(), true);
1139+
1140+ delete statusSpy;
1141+}
1142+
1143+/*
1144+ A vertical, downwards swipe performed on mako near its right edge.
1145+
1146+ The DirectionalDragArea on the right edge must not recognize this
1147+ gesture.
1148+ */
1149+void tst_DirectionalDragArea::makoRightEdgeDrag_verticalDownwards()
1150+{
1151+ m_view->resize(768, 1280);
1152+ QTest::qWait(300);
1153+
1154+ DirectionalDragArea *edgeDragArea =
1155+ m_view->rootObject()->findChild<DirectionalDragArea*>("hnDragArea");
1156+ QVERIFY(edgeDragArea != nullptr);
1157+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
1158+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1159+
1160+ edgeDragArea->d->setPixelsPerMm(320.0 /*mako ppi*/ * 0.03937 /* inches per mm*/);
1161+
1162+ StatusSpy *statusSpy = new StatusSpy(edgeDragArea);
1163+
1164+ sendTouchPress(12012445, 26, QPointF(767.001,461.82));
1165+ sendTouchUpdate(12012472, 26, QPointF(767.001,462.569));
1166+ sendTouchUpdate(12012528, 26, QPointF(767.001,463.334));
1167+ sendTouchUpdate(12012546, 26, QPointF(767.001,466.856));
1168+ sendTouchUpdate(12012571, 26, QPointF(767.001,473.291));
1169+ sendTouchUpdate(12012587, 26, QPointF(767.001,487.31));
1170+ sendTouchUpdate(12012604, 26, QPointF(765.364,507.521));
1171+ sendTouchUpdate(12012618, 26, QPointF(765.364,507.521));
1172+ sendTouchUpdate(12012627, 26, QPointF(762.642,534.317));
1173+ sendTouchUpdate(12012655, 26, QPointF(760.846,573.406));
1174+ sendTouchUpdate(12012667, 26, QPointF(759.838,625.295));
1175+ sendTouchUpdate(12012675, 26, QPointF(758.875,703.207));
1176+ sendTouchUpdate(12012696, 26, QPointF(761.52,777.015));
1177+ sendTouchUpdate(12012713, 26, QPointF(765.659,835.591));
1178+ sendTouchUpdate(12012731, 26, QPointF(766.778,883.206));
1179+ sendTouchUpdate(12012748, 26, QPointF(767.001,922.937));
1180+ sendTouchUpdate(12012779, 26, QPointF(767.001,967.558));
1181+ sendTouchUpdate(12012798, 26, QPointF(767.001,1006.12));
1182+ sendTouchUpdate(12012809, 26, QPointF(767.001,1033.1));
1183+ sendTouchRelease(12012810, 26, QPointF(767.001,1033.1));
1184+
1185+ QCOMPARE(statusSpy->recognized(), false);
1186+
1187+ delete statusSpy;
1188+}
1189+
1190+/*
1191+ A valid left-edge drag performed on mako. This one starts a bit slow than speeds up
1192+ */
1193+void tst_DirectionalDragArea::makoLeftEdgeDrag_slowStart()
1194+{
1195+ m_view->resize(768, 1280);
1196+ QTest::qWait(300);
1197+
1198+ DirectionalDragArea *edgeDragArea =
1199+ m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
1200+ QVERIFY(edgeDragArea != nullptr);
1201+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
1202+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1203+
1204+ edgeDragArea->d->setPixelsPerMm(320.0 /*mako ppi*/ * 0.03937 /* inches per mm*/);
1205+
1206+ StatusSpy *statusSpy = new StatusSpy(edgeDragArea);
1207+
1208+ sendTouchPress(4002267, 77, QPointF(0,885.154));
1209+ sendTouchUpdate(4002275, 77, QPointF(0,886.214));
1210+ sendTouchUpdate(4002311, 77, QPointF(1.09568,887.75));
1211+ sendTouchUpdate(4002329, 77, QPointF(3.53647,890.191));
1212+ sendTouchUpdate(4002347, 77, QPointF(7.87434,892.879));
1213+ sendTouchUpdate(4002366, 77, QPointF(12.3036,895.075));
1214+ sendTouchUpdate(4002384, 77, QPointF(15.8885,896.849));
1215+ sendTouchUpdate(4002406, 77, QPointF(18.4504,897.88));
1216+ sendTouchUpdate(4002420, 77, QPointF(20.2429,898.149));
1217+ sendTouchUpdate(4002439, 77, QPointF(20.9945,898.149));
1218+ sendTouchUpdate(4002457, 77, QPointF(21.8819,898.149));
1219+ sendTouchUpdate(4002480, 77, QPointF(22.7454,897.389));
1220+ sendTouchUpdate(4002493, 77, QPointF(23.5456,896.589));
1221+ sendTouchUpdate(4002511, 77, QPointF(24.5435,895.031));
1222+ sendTouchUpdate(4002529, 77, QPointF(25.4271,892.32));
1223+ sendTouchUpdate(4002548, 77, QPointF(26.3145,889.658));
1224+ sendTouchUpdate(4002566, 77, QPointF(27.2004,886.999));
1225+ sendTouchUpdate(4002584, 77, QPointF(28.035,885.048));
1226+ sendTouchUpdate(4002603, 77, QPointF(29.9684,883.167));
1227+ sendTouchUpdate(4002620, 77, QPointF(33.3591,881.403));
1228+ sendTouchUpdate(4002639, 77, QPointF(44.1017,879.642));
1229+ sendTouchUpdate(4002657, 77, QPointF(64.828,878.502));
1230+ sendTouchUpdate(4002675, 77, QPointF(87.9486,878.157));
1231+ sendTouchUpdate(4002693, 77, QPointF(112.96,877.742));
1232+ sendTouchUpdate(4002711, 77, QPointF(138.903,877.157));
1233+ sendTouchUpdate(4002729, 77, QPointF(163.204,877.157));
1234+ sendTouchUpdate(4002747, 77, QPointF(182.127,877.157));
1235+ sendTouchUpdate(4002765, 77, QPointF(194.478,877.657));
1236+ sendTouchUpdate(4002785, 77, QPointF(201.474,878.508));
1237+ sendTouchUpdate(4002803, 77, QPointF(204.855,879.401));
1238+ sendTouchUpdate(4002822, 77, QPointF(206.616,880.281));
1239+ sendTouchUpdate(4002839, 77, QPointF(207.115,880.906));
1240+ sendTouchUpdate(4002894, 77, QPointF(206.865,881.184));
1241+ sendTouchUpdate(4002912, 77, QPointF(206.865,882.143));
1242+ sendTouchUpdate(4002930, 77, QPointF(206.865,883.106));
1243+ sendTouchUpdate(4002949, 77, QPointF(206.526,883.994));
1244+ sendTouchUpdate(4002967, 77, QPointF(205.866,884.88));
1245+ sendTouchUpdate(4002985, 77, QPointF(205.866,885.766));
1246+ sendTouchUpdate(4003005, 77, QPointF(205.866,886.654));
1247+ sendTouchUpdate(4003021, 77, QPointF(205.366,887.537));
1248+ sendTouchUpdate(4003039, 77, QPointF(204.592,888.428));
1249+ sendTouchUpdate(4003050, 77, QPointF(204.367,888.653));
1250+ sendTouchRelease(4003050, 77, QPointF(204.367,888.653));
1251+
1252+ QCOMPARE(statusSpy->recognized(), true);
1253+
1254+ delete statusSpy;
1255+}
1256+
1257+void tst_DirectionalDragArea::makoLeftEdgeDrag_movesSlightlyBackwardsOnStart()
1258+{
1259+ m_view->resize(768, 1280);
1260+ QTest::qWait(300);
1261+
1262+ DirectionalDragArea *edgeDragArea =
1263+ m_view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
1264+ QVERIFY(edgeDragArea != nullptr);
1265+ edgeDragArea->d->setRecognitionTimer(m_fakeTimerFactory->createTimer(edgeDragArea));
1266+ edgeDragArea->d->setTimeSource(m_fakeTimerFactory->timeSource());
1267+
1268+ edgeDragArea->d->setPixelsPerMm(320.0 /*mako ppi*/ * 0.03937 /* inches per mm*/);
1269+
1270+ StatusSpy *statusSpy = new StatusSpy(edgeDragArea);
1271+
1272+ sendTouchPress(41097, 24, QPointF(13.9909,827.177));
1273+ sendTouchUpdate(41120, 24, QPointF(19.2375,825.677));
1274+ sendTouchUpdate(41138, 24, QPointF(18.4057,826.177));
1275+ sendTouchUpdate(41161, 24, QPointF(20.1067,825.867));
1276+ sendTouchUpdate(41177, 24, QPointF(21.8869,824.977));
1277+ sendTouchUpdate(41193, 24, QPointF(24.7603,823.494));
1278+ sendTouchUpdate(41211, 24, QPointF(28.3889,821.725));
1279+ sendTouchUpdate(41229, 24, QPointF(32.2909,819.955));
1280+ sendTouchUpdate(41247, 24, QPointF(38.2251,817.431));
1281+ sendTouchUpdate(41266, 24, QPointF(52.4182,814.223));
1282+ sendTouchUpdate(41284, 24, QPointF(85.8465,809.483));
1283+ sendTouchUpdate(41302, 24, QPointF(126.091,802.741));
1284+ sendTouchUpdate(41320, 24, QPointF(153.171,797.977));
1285+ sendTouchUpdate(41338, 24, QPointF(170.565,795.077));
1286+ sendTouchUpdate(41356, 24, QPointF(178.685,794.101));
1287+ sendTouchUpdate(41375, 24, QPointF(183.706,793.225));
1288+ sendTouchUpdate(41393, 24, QPointF(186.112,793.19));
1289+ sendTouchUpdate(41411, 24, QPointF(187.634,793.19));
1290+ sendTouchUpdate(41429, 24, QPointF(188.505,793.19));
1291+ sendTouchUpdate(41532, 24, QPointF(187.816,793.19));
1292+ sendTouchUpdate(41538, 24, QPointF(186.902,793.19));
1293+ sendTouchUpdate(41557, 24, QPointF(186.01,793.19));
1294+ sendTouchUpdate(41575, 24, QPointF(185.125,793.444));
1295+ sendTouchUpdate(41593, 24, QPointF(184.229,793.69));
1296+ sendTouchUpdate(41605, 24, QPointF(183.88,793.69));
1297+ sendTouchRelease(41607, 24, QPointF(183.88,793.69));
1298+
1299+ QCOMPARE(statusSpy->recognized(), true);
1300+
1301+ delete statusSpy;
1302+}
1303+
12691304QTEST_MAIN(tst_DirectionalDragArea)
12701305
12711306#include "tst_DirectionalDragArea.moc"
tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.qml+8−1
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013,2015 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
@@ -16,12 +16,19 @@
1616
1717import QtQuick 2.0
1818import Ubuntu.Components 0.1
19+import Unity.Test 0.1
1920
2021Rectangle {
2122 width: units.gu(60)
2223 height: units.gu(60)
2324 color: "white"
2425
26+ Binding {
27+ target: MouseTouchAdaptor
28+ property: "enabled"
29+ value: true
30+ }
31+
2532 MouseArea {
2633 id: mouseArea
2734 objectName: "mouseArea"
tests/plugins/Ubuntu/Gestures/tst_FloatingFlickable.cppnew file+122
@@ -0,0 +1,122 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include <QtTest>
18+#include <QQuickView>
19+
20+// Ubuntu.Gestures plugin
21+#include <DirectionalDragArea.h>
22+#include <FloatingFlickable.h>
23+
24+#include "GestureTest.h"
25+#include "TestItem.h"
26+
27+using namespace UbuntuGestures;
28+
29+class tst_FloatingFlickable: public GestureTest
30+{
31+ Q_OBJECT
32+public:
33+ tst_FloatingFlickable();
34+private Q_SLOTS:
35+ void tapGoesThrough();
36+ void flickChangesContentX();
37+ void flickChangesContentY();
38+};
39+
40+tst_FloatingFlickable::tst_FloatingFlickable()
41+ : GestureTest(QStringLiteral("tst_FloatingFlickable.qml"))
42+{
43+}
44+
45+void tst_FloatingFlickable::tapGoesThrough()
46+{
47+ FloatingFlickable *floatingFlickable =
48+ m_view->rootObject()->findChild<FloatingFlickable*>("floatingFlickable");
49+ QVERIFY(floatingFlickable != nullptr);
50+
51+ TestItem *testItem = new TestItem;
52+ testItem->setWidth(floatingFlickable->width());
53+ testItem->setHeight(floatingFlickable->height());
54+ testItem->setParentItem(m_view->rootObject());
55+ testItem->setZ(1.0);
56+
57+ floatingFlickable->setZ(2.0);
58+
59+ QTest::touchEvent(m_view, m_device)
60+ .press(0, QPoint(floatingFlickable->width()/2, floatingFlickable->height()/2));
61+
62+ QCOMPARE(testItem->touchEventsReceived.count(), 1);
63+}
64+
65+void tst_FloatingFlickable::flickChangesContentX()
66+{
67+ FloatingFlickable *floatingFlickable =
68+ m_view->rootObject()->findChild<FloatingFlickable*>("floatingFlickable");
69+ QVERIFY(floatingFlickable != nullptr);
70+
71+ floatingFlickable->m_dragArea->removeTimeConstraints();
72+
73+ qreal startContentX = floatingFlickable->contentX();
74+
75+ QPoint startPos(floatingFlickable->width() - 5, 10);
76+ int stepCount = 20;
77+ int step = (startPos.x() - 5) / stepCount;
78+
79+ QTest::touchEvent(m_view, m_device).press(0, startPos);
80+ for (int i = 0; i < stepCount; ++i) {
81+ QTest::qWait(10);
82+ QPoint touchPos = startPos - QPoint(step*i, 0);
83+ QTest::touchEvent(m_view, m_device).move(0, touchPos);
84+ }
85+
86+ QTest::qWait(10);
87+ QTest::touchEvent(m_view, m_device).release(0, QPoint(5, 10));
88+
89+ QVERIFY(floatingFlickable->contentX() > (startContentX + floatingFlickable->width()/2));
90+}
91+
92+void tst_FloatingFlickable::flickChangesContentY()
93+{
94+ FloatingFlickable *floatingFlickable =
95+ m_view->rootObject()->findChild<FloatingFlickable*>("floatingFlickable");
96+ QVERIFY(floatingFlickable != nullptr);
97+
98+ floatingFlickable->setDirection(Direction::Vertical);
99+ floatingFlickable->m_dragArea->removeTimeConstraints();
100+
101+ qreal startContentY = floatingFlickable->contentY();
102+
103+ QPoint startPos(10, floatingFlickable->height() - 5);
104+ int stepCount = 20;
105+ int step = (startPos.y() - 5) / stepCount;
106+
107+ QTest::touchEvent(m_view, m_device).press(0, startPos);
108+ for (int i = 0; i < stepCount; ++i) {
109+ QTest::qWait(10);
110+ QPoint touchPos = startPos - QPoint(0, step*i);
111+ QTest::touchEvent(m_view, m_device).move(0, touchPos);
112+ }
113+
114+ QTest::qWait(10);
115+ QTest::touchEvent(m_view, m_device).release(0, QPoint(10, 5));
116+
117+ QVERIFY(floatingFlickable->contentY() > (startContentY + floatingFlickable->height()/2));
118+}
119+
120+QTEST_MAIN(tst_FloatingFlickable)
121+
122+#include "tst_FloatingFlickable.moc"
tests/plugins/Ubuntu/Gestures/tst_FloatingFlickable.qmlnew file+80
@@ -0,0 +1,80 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import Ubuntu.Components 0.1
19+import Ubuntu.Gestures 0.1
20+import Unity.Test 0.1
21+
22+Rectangle {
23+ id: root
24+ width: units.gu(60)
25+ height: units.gu(60)
26+ color: "white"
27+
28+ Binding {
29+ target: MouseTouchAdaptor
30+ property: "enabled"
31+ value: true
32+ }
33+
34+ Grid {
35+ id: colorGrid
36+ readonly property bool horizontal: floatingFlickable.direction === Direction.Horizontal
37+ x: horizontal ? -floatingFlickable.contentX : 0
38+ y: horizontal ? 0 : -floatingFlickable.contentY
39+ rows: horizontal ? 1 : 100
40+ columns: horizontal ? 100 : 1
41+ Repeater {
42+ model: 100
43+ Rectangle {
44+ width: colorGrid.horizontal ? units.gu(12) : root.width
45+ height: colorGrid.horizontal ? root.height : units.gu(12)
46+ color: mouseArea.pressed ? "red"
47+ : Qt.rgba(Math.random(), Math.random(), Math.random(), 1)
48+ MouseArea {
49+ id: mouseArea
50+ anchors.fill: parent
51+ }
52+ }
53+ }
54+ }
55+
56+ FloatingFlickable {
57+ id: floatingFlickable
58+ objectName: "floatingFlickable"
59+ anchors.fill: parent
60+ contentWidth: colorGrid.width
61+ contentHeight: colorGrid.height
62+ }
63+
64+ Button {
65+ anchors.bottom: parent.bottom
66+ anchors.right: parent.right
67+ anchors.margins: units.gu(1)
68+
69+ text: floatingFlickable.direction === Direction.Horizontal ? "Horizontal" : "Vertical"
70+ activeFocusOnPress: false
71+
72+ onClicked: {
73+ if (floatingFlickable.direction === Direction.Horizontal) {
74+ floatingFlickable.direction = Direction.Vertical;
75+ } else {
76+ floatingFlickable.direction = Direction.Horizontal;
77+ }
78+ }
79+ }
80+}
tests/plugins/Ubuntu/Gestures/tst_PressedOutsideNotifier.cpp+1−1
@@ -28,7 +28,7 @@ class tst_PressedOutsideNotifier: public GestureTest
2828public:
2929 tst_PressedOutsideNotifier();
3030private Q_SLOTS:
31− void init(); // called right before each and every test function is executed
31+ void init() override; // called right before each and every test function is executed
3232
3333 void touchOutsideAreaTriggersSignal();
3434 void touchInsideAreaHasNoEffect();
tests/plugins/Ubuntu/Gestures/tst_TouchDispatcher.cpp+5−6
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 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
@@ -81,8 +81,7 @@ void tst_TouchDispatcher::sendMouseEventIfTouchIgnored()
8181 event->accept();
8282 };
8383
84− touchDispatcher.dispatch(QEvent::TouchBegin, m_device, Qt::NoModifier, touchPoints, m_view,
85− 12345 /* timestamp */);
84+ touchDispatcher.dispatch(m_device, Qt::NoModifier, touchPoints, m_view, 12345 /* timestamp */);
8685
8786 QVERIFY(gotTouchEvent);
8887 QCOMPARE(gotMousePressEvent, shouldGetMousePress);
@@ -128,18 +127,18 @@ void tst_TouchDispatcher::mouseDoubleClick()
128127 }
129128 ulong timestamp = 12345;
130129
131− touchDispatcher.dispatch(QEvent::TouchBegin, m_device, Qt::NoModifier, touchPoints, m_view, timestamp);
130+ touchDispatcher.dispatch(m_device, Qt::NoModifier, touchPoints, m_view, timestamp);
132131
133132 touchPoints[0].setState(Qt::TouchPointReleased);
134133 timestamp += doubleClickInterval / 10;
135134
136− touchDispatcher.dispatch(QEvent::TouchEnd, m_device, Qt::NoModifier, touchPoints, m_view, timestamp);
135+ touchDispatcher.dispatch(m_device, Qt::NoModifier, touchPoints, m_view, timestamp);
137136
138137 touchPoints[0].setId(1);
139138 touchPoints[0].setState(Qt::TouchPointPressed);
140139 timestamp += timeBetweenClicks;
141140
142− touchDispatcher.dispatch(QEvent::TouchBegin, m_device, Qt::NoModifier, touchPoints, m_view, timestamp);
141+ touchDispatcher.dispatch(m_device, Qt::NoModifier, touchPoints, m_view, timestamp);
143142
144143 QCOMPARE(gotDoubleClickEvent, shouldSendDoubleClick);
145144}
tests/plugins/Ubuntu/Gestures/tst_TouchGate.cpp+54−22
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 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
@@ -29,16 +29,7 @@
2929#include <TouchGate.h>
3030#include <TouchRegistry.h>
3131
32−class TestItem : public QQuickItem
33−{
34− Q_OBJECT
35−
36−public:
37− QList<QSharedPointer<QTouchEvent>> touchEventsReceived;
38−
39−protected:
40− void touchEvent(QTouchEvent *event) override;
41−};
32+#include "TestItem.h"
4233
4334class CandidateItem : public QQuickItem
4435{
@@ -63,6 +54,7 @@ private Q_SLOTS:
6354 void init(); // called right before each and every test function is executed
6455 void cleanup(); // called right after each and every test function is executed
6556
57+private:
6658 void onlyCandidate_passThrough();
6759 void holdsEventsUntilGainsOwnership();
6860 void holdsEventsUntilGainsOwnership_data();
@@ -70,6 +62,9 @@ private Q_SLOTS:
7062 void dontDispacthToDisabledOrInvisibleTarget();
7163 void dontDispacthToDisabledOrInvisibleTarget_data();
7264
65+private Q_SLOTS:
66+ void disabledWhileHoldingTouch();
67+
7368private:
7469 QQuickView *createView();
7570 TouchRegistry *touchRegistry;
@@ -100,10 +95,9 @@ void tst_TouchGate::cleanupTestCase()
10095
10196void tst_TouchGate::init()
10297{
103− touchRegistry = new TouchRegistry;
98+ touchRegistry = TouchRegistry::instance();
10499
105100 view = createView();
106− view->installEventFilter(touchRegistry);
107101 view->setSource(QUrl::fromLocalFile("touchGateExample.qml"));
108102 view->show();
109103 QVERIFY(QTest::qWaitForWindowExposed(view));
@@ -326,17 +320,55 @@ void tst_TouchGate::dontDispacthToDisabledOrInvisibleTarget_data()
326320 QTest::newRow("disabled invisible") << false << false;
327321}
328322
329−///////////// TestItem /////////////////////////////////////////////////////////////
330−
331−void TestItem::touchEvent(QTouchEvent *event)
323+void tst_TouchGate::disabledWhileHoldingTouch()
332324{
333− QTouchEvent *clonedEvent = new QTouchEvent(event->type(),
334− event->device(),
335− event->modifiers(),
336− event->touchPointStates(),
337− event->touchPoints());
325+ TouchGate *touchGate = view->rootObject()->findChild<TouchGate*>("touchGate");
326+ Q_ASSERT(touchGate);
327+
328+ TestItem *testItem = new TestItem;
329+ testItem->setWidth(touchGate->width());
330+ testItem->setHeight(touchGate->height());
331+ testItem->setParentItem(view->rootObject());
332+ testItem->setZ(0.0);
333+
334+ touchGate->setZ(1.0);
335+ touchGate->setTargetItem(testItem);
338336
339− touchEventsReceived.append(QSharedPointer<QTouchEvent>(clonedEvent));
337+ QTest::touchEvent(view, device)
338+ .press(0, QPoint(100,100));
339+
340+ QCOMPARE(testItem->touchEventsReceived.count(), 1);
341+ testItem->touchEventsReceived.clear();
342+
343+ touchGate->setEnabled(false);
344+
345+ QTest::touchEvent(view, device)
346+ .move(0, QPoint(101,101));
347+
348+ // Nothing new came as TouchGate didn't even get it because it's disabled
349+ QCOMPARE(testItem->touchEventsReceived.count(), 0);
350+
351+ touchGate->setEnabled(true);
352+
353+ QTest::touchEvent(view, device)
354+ .move(0, QPoint(102,102));
355+
356+ QTest::touchEvent(view, device)
357+ .release(0, QPoint(102,102));
358+
359+ // Nothing new came because TouchGate has already discarded touch 0
360+ QCOMPARE(testItem->touchEventsReceived.count(), 0);
361+
362+ QTest::touchEvent(view, device)
363+ .press(1, QPoint(200,200));
364+
365+ QCOMPARE(testItem->touchEventsReceived.count(), 1);
366+ {
367+ // it got only the new touch point.
368+ QSharedPointer<QTouchEvent> touchEvent = testItem->touchEventsReceived[0];
369+ QCOMPARE(touchEvent->touchPoints().count(), 1);
370+ QCOMPARE(touchEvent->touchPoints()[0].id(), 1);
371+ }
340372}
341373
342374///////////// CandidateItem /////////////////////////////////////////////////////////////
tests/plugins/Unity/Indicators/menucontentactivatortest.cpp+2−2
@@ -30,8 +30,8 @@ public:
3030 : UnityIndicators::AbstractTimer(parent)
3131 {}
3232
33− virtual int interval() const { return m_duration; }
34− virtual void setInterval(int msecs) { m_duration = msecs; }
33+ int interval() const override { return m_duration; }
34+ void setInterval(int msecs) override { m_duration = msecs; }
3535
3636 void emitTimeout() {
3737 if (isRunning()) {
tests/plugins/Unity/Launcher/CMakeLists.txt−1
@@ -1,6 +1,5 @@
11pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
22pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=6)
3−pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=5)
43
54include_directories(
65 ${CMAKE_CURRENT_SOURCE_DIR}
tests/plugins/Unity/Launcher/launchermodeltest.cpp+24−22

This diff is too big to show here.

tests/plugins/Utils/CMakeLists.txt+1

This diff is too big to show here.

tests/plugins/Utils/homekeywatchertest.cppnew file+323

This diff is too big to show here.

tests/plugins/Utils/qlimitproxymodeltest.cpp+5−5

This diff is too big to show here.

tests/plugins/Utils/unitysortfilterproxymodeltest.cpp+5−5

This diff is too big to show here.

tests/qmltests/CMakeLists.txt+3−1

This diff is too big to show here.

tests/qmltests/Components/CMakeLists.txt+19−3

This diff is too big to show here.

tests/qmltests/Components/tst_DragHandle.cpp+15−21

This diff is too big to show here.

tests/qmltests/Components/tst_DragHandle.qml+7

This diff is too big to show here.

tests/qmltests/Components/tst_DragHandle/BidirectionalShowable.qml+2−2

This diff is too big to show here.

tests/qmltests/Components/tst_DragHandle/HorizontalShowable.qml+4−4

This diff is too big to show here.

tests/qmltests/Components/tst_DragHandle/VerticalShowable.qml+4−4

This diff is too big to show here.

tests/qmltests/Components/tst_EdgeDragEvaluator.cppnew file+165

This diff is too big to show here.

tests/qmltests/Components/tst_EdgeDragEvaluator.qmlnew file+27

This diff is too big to show here.

tests/qmltests/Dash/Previews/tst_PreviewRatingEdit.qmlnew file+80

This diff is too big to show here.

tests/qmltests/Dash/tst_Card.qml−1

This diff is too big to show here.

tests/qmltests/Dash/tst_DashContent.qml+7−2

This diff is too big to show here.

tests/qmltests/Dash/tst_GenericScopeView.qml+30−7

This diff is too big to show here.

tests/qmltests/Panel/tst_IndicatorsMenu.qml−2

This diff is too big to show here.

tests/qmltests/Panel/tst_Panel.qml−2

This diff is too big to show here.

tests/qmltests/Stages/ApplicationCheckBox.qml+49−20

This diff is too big to show here.

tests/qmltests/Stages/tst_ApplicationWindow.qml+14−23

This diff is too big to show here.

tests/qmltests/Stages/tst_DesktopStage.qml+77−67

This diff is too big to show here.

tests/qmltests/Stages/tst_PhoneStage.qml+13−66

This diff is too big to show here.

tests/qmltests/Stages/tst_SessionContainer.qml+1−64

This diff is too big to show here.

tests/qmltests/Stages/tst_SpreadDelegate.qml+172−37

This diff is too big to show here.

tests/qmltests/Stages/tst_TabletStage.qml+51

This diff is too big to show here.

tests/qmltests/Tutorial/tst_Tutorial.qml+6−1

This diff is too big to show here.

tests/qmltests/tst_OrientedShell.qmlnew file+1151

This diff is too big to show here.

tests/qmltests/tst_Shell.qml+166−117

This diff is too big to show here.

tests/qmltests/tst_ShellWithPin.qml+9−11

This diff is too big to show here.

tests/uqmlscene/main.cpp−10

This diff is too big to show here.

tests/utils/modules/Unity/Test/MouseTouchEmulationCheckbox.qml+1

This diff is too big to show here.

tests/utils/modules/Unity/Test/UnityTestCase.qml+58−11

This diff is too big to show here.

tests/utils/modules/Unity/Test/plugin.h+1−1

This diff is too big to show here.

tests/utils/modules/Unity/Test/testutil.cpp+8−20

This diff is too big to show here.

tests/utils/modules/Unity/Test/testutil.h+1−1

This diff is too big to show here.